Skip to content

feat(agent): OpenUI bindings — library/fragment builders, toUIOutput, getUiStream() (DEV-773) - #92

Open
LukasParke wants to merge 11 commits into
mainfrom
lukeparke/dev-773-typescript-agent-openui-module-libraryfragment-builders
Open

feat(agent): OpenUI bindings — library/fragment builders, toUIOutput, getUiStream() (DEV-773)#92
LukasParke wants to merge 11 commits into
mainfrom
lukeparke/dev-773-typescript-agent-openui-module-libraryfragment-builders

Conversation

@LukasParke

@LukasParke LukasParke commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

The Agent-SDK half of the OpenUI backport from Noetic (DEV-773, part of the DEV-765 umbrella; spec: DEV-764 RFC).

Deliberately thin per the spec: the API owns parsing/prompting/validation; the SDK ships builders, the tool render surface, and stream access.

New src/lib/openui/ module

  • defineComponent / createLibrary — component vocabulary from Zod prop schemas; prop declaration order is normative (positional args in OpenUI Lang map by order)
  • fragment(library) + uiRef/uiState/uiBuiltin — typed constructors for tool-authored UI; literal props validated at construction time
  • openui(library) — produces the wire-shaped {id:'openui', library, dialect} plugin preference (Zod → JSON Schema); plugins already type-flows through CallModelInput, so no callModel signature change
  • translateUiEvent + UI stream event model

tool() render surface

  • Optional toUIOutput sibling of toModelOutput on regular/generator/HITL tools
  • Successful executions broadcast a tool.ui_fragment stream event (render-only — never sent to the model; throwing toUIOutput degrades to no-fragment)

ModelResult.getUiStream()

  • Streams UI events across all turns: tool-authored fragments + the API's response.openui.statement/fragment/document wire events
  • Wire events not yet in the SDK's stream-event union arrive via its forward-compat Unknown catch-all; translation reads the raw payload, so the stream works before and after the SDK regen (DEV-772)
  • Implements both the no-tools fast path (with hooks-session finalization) and the multi-turn broadcaster path

Deferred (documented in the ticket)

  • uiSubmitted()/uiInteracted()/uiToAssistant() stop predicates — need interaction events that only exist once Phase-3 surface state (DEV-774) lands
  • Items-stream surfacing of UI events; fragments on the auto-approve/pending-state paths

Test plan

  • 28 new tests (openui.test.ts, openui-stream.test.ts): serialization, library ordering/validation, fragment builder, plugin wire shape, event translation (incl. Unknown encoding), getUiStream fast path, broadcastUiFragment success/skip/throw paths
  • Full suite: 774/774 passing (62 files), typecheck + biome clean

🤖 Generated with Claude Code


Open in Devin Review

LukasParke and others added 2 commits July 31, 2026 15:53
…r (DEV-773)

The SDK half of the OpenUI backport from Noetic (DEV-765). Adds
packages/agent/src/lib/openui/: defineComponent/createLibrary (Zod props
with normative declaration order), the typed fragment() builder with
uiRef/uiState/uiBuiltin, OpenUI Lang expression serialization, and the
openui(library) helper producing the wire-shaped plugin preference
(Zod -> JSON Schema).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…Stream() (DEV-773)

Tools can now author OpenUI render fragments: an optional toUIOutput
sibling of toModelOutput on every executable tool shape. Fragments are
broadcast as tool.ui_fragment stream events after successful execution
(render-only, never sent to the model; a throwing toUIOutput degrades to
no-fragment).

getUiStream() on ModelResult surfaces UI events across all turns:
tool-authored fragments plus the API's response.openui.* wire events
(statement/fragment/document) from the openui plugin. Wire events not
yet in the SDK's stream-event union arrive via its forward-compat
Unknown catch-all, so translation reads the raw payload — the stream
works both before and after the SDK regen (DEV-772).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

Open in Devin Review

Comment thread packages/agent/src/index.ts
Comment thread packages/agent/src/lib/model-result.ts
Comment thread packages/agent/src/lib/openui/document.ts Outdated
Comment on lines +38 to +43
export const OPENUI_BUILTIN_COMPONENTS = [
'Action',
'Query',
'Mutation',
'ToolView',
] as const;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Declared builtin components are unreachable from the fragment builder

OPENUI_BUILTIN_COMPONENTS (Action, Query, Mutation, ToolView) is documented as implicitly accepted by every library, but fragment() only compiles constructors for components present in library.components (packages/agent/src/lib/openui/fragment.ts:111-113), and createLibrary never seeds the builtins. A tool author therefore cannot construct e.g. ToolView(...) via the typed builder; the only escape hatch, uiBuiltin, prepends @ and would emit @ToolView(...), which is builtin-function syntax rather than a component call. The constant is currently unused anywhere in the package — worth confirming whether the builder is meant to expose these.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accurate. OPENUI_BUILTIN_COMPONENTS (Action, Query, Mutation, ToolView) is documented as implicitly accepted by every library, but fragment() only compiles constructors for the components a library declares, so ui.Query(...) is not reachable from the typed builder — a caller has to hand-assemble that expression.

Not fixing it here: the builtins are data/action bindings rather than DOM, and the playground renderer returns null for all four, so nothing regresses today. Making them first-class on the builder is a real API addition (they take different argument shapes than a component`s positional props) and belongs with the client event-ingestion work rather than this PR. Leaving open so it is visible rather than silently resolved.

Comment on lines +68 to +76
function unwrapEvent(event: unknown): Record<string, unknown> | null {
if (!isRecord(event)) {
return null;
}
if (event['isUnknown'] === true && isRecord(event['raw'])) {
return event['raw'];
}
return event;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Unknown-event unwrapping relies on an unverified SDK encoding

unwrapEvent assumes the SDK's forward-compat catch-all materializes as { type: 'UNKNOWN', raw: <original>, isUnknown: true }. @openrouter/sdk is not vendored in this checkout, and no other module in the package inspects isUnknown/raw (the existing forward-compat handling in packages/agent/src/lib/stream-type-guards.ts:139-160 only switches on type). If the generated shape differs (e.g. the raw payload is spread onto the event, or the field is named differently), every response.openui.* wire event would silently translate to null and the UI stream would appear empty pre-regen — exactly the case this code exists to handle. Worth confirming against the installed SDK's Unknown<"type"> runtime encoding.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair challenge, and I cannot close it from this checkout either — @openrouter/sdk is a published dependency here, so the { type: "UNKNOWN", raw, isUnknown: true } catch-all shape is an assumption about the SDK`s forward-compat encoding rather than something this PR verifies.

What I can say: unwrapEvent fails safe. If the encoding differs, translateUiEvent returns null and getUiStream() yields nothing for pre-regen wire events — the documented degraded path — rather than throwing or emitting a malformed event. So the blast radius of a wrong guess is "native mode stays silent until the regen lands", which is already its expected state.

Leaving open: the right fix is a test against the real SDK types once the regen adds the OpenUI events, at which point the catch-all branch can be deleted entirely rather than verified.

New private package @openrouter/openui-playground: a local webapp for
testing, benching, and evaluating OpenUI generative-UI support.

- Progressive renderer over the demo component library (Stack/Card/
  Heading/Text/Stat/Badge/Table/Input/Select/Button/Progress) — UI
  materializes statement-by-statement mid-stream
- Two modes with identical event shapes: emulate (local library prompt +
  reference streaming parser over the text stream — works today) and
  native (openui() plugin + getUiStream() — flips on when DEV-771/772
  land), so the paths can be A/B'd from the history table
- Bench stats per run: TTFB, first-statement latency, total time,
  statement/diagnostic counts, token usage, cost; session history for
  comparing models and prompts
- Reference incremental OpenUI Lang parser (the same logic DEV-770
  ports into openrouter-web) with 11 conformance tests
- Plain node:http + static client; no build step

Verified end-to-end against live models: single-card and 12-statement
dashboard prompts parse clean (0 diagnostics) and render progressively.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 new potential issues.

Open in Devin Review

Comment thread packages/openui-playground/src/lang/prompt.ts Outdated
Comment on lines +55 to +58
}
if (def.props !== undefined) {
component.props = convertZodToJsonSchema(def.props);
}

@devin-ai-integration devin-ai-integration Bot Jul 31, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Plugin wire schema is generated in output mode, unlike the playground prompt (input mode)

openui() converts prop schemas via convertZodToJsonSchema, which calls z4.toJSONSchema(schema, { target: 'draft-7' }) with the default io: 'output' (packages/agent/src/lib/tool-executor.ts:93-104), while the playground's prompt generator deliberately uses io: 'input' (packages/openui-playground/src/lang/prompt.ts:13-15). For props declared with .default(...), output mode marks the property as required, so the API would see a prop as mandatory that the SDK's own componentProps reports as optional (packages/agent/src/lib/openui/library.ts:88-92). Worth confirming which io mode the API expects before DEV-772 lands, since positional-prop mapping and requiredness are consumed server-side.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed empirically, and leaving open for the API-side answer you asked for. The divergence is real — z4.toJSONSchema on z4.object({ tone: z4.string().default('info'), label: z4.string() }):

output required: ["tone","label"]
input  required: ["label"]

So in the wire schema a prop with .default is marked required, while componentProps reports it optional and the playground prompt (input mode) agrees with componentProps. Two surfaces describing the same component disagree.

On the merits io: 'input' looks right: these are props a MODEL supplies, so the schema should describe what may be sent, not what comes back. But I have not changed it, for two reasons. convertZodToJsonSchema in tool-executor.ts is shared with the tool-definition path, so switching its mode would silently alter every tool's wire schema — that needs to be a parameter, not a global flip. And the API does not accept this plugin id yet (DEV-771/772), so nothing can validate which mode it actually wants; guessing now risks locking in the wrong one before the endpoint exists.

Your framing was "worth confirming which mode the API expects before DEV-772" — that is exactly right, and it is a question for whoever owns the endpoint. Flagging it in the PR summary too.

Comment thread packages/openui-playground/public/app.js
Comment thread packages/openui-playground/src/server.ts
perry-the-pr-reviewer[bot]

This comment was marked as resolved.

serializeExpr emitted object keys raw, so a key with spaces, quotes,
punctuation, or a leading digit produced source the parser rejects. Keys
come from arbitrary tool-authored objects via toExpr, so they cannot be
assumed to be identifiers. The grammar already accepts a quoted key
(parseObject branches on '"'), so quoting the rest round-trips.

String(NaN)/String(Infinity) also emitted bare identifiers, which parse
back as refs to undefined names. JSON resolves the same hole as null; do
that rather than emit source that cannot round-trip.

fix(playground): describe enum props by their values, not "string"

describeSchema returned on json.type before checking json.enum, but an
enum serializes as {type: 'string', enum: [...]} — so every enum prop was
described to the model as a plain string and it never saw which values
are legal for Badge.tone, Stack.direction, Button.variant.

docs(changeset): add the required minor changeset for the OpenUI exports

~30 new exports plus ModelResult.getUiStream and the toUIOutput tool
option had no changeset, which the public-api-examples skill requires.
The example is compile-checked against the real signatures.
The gate's no_god_files rule is fan-out > 15, not file size:
model-result.ts sat at exactly 15 outbound edges and this PR's
./openui/ui-stream.js import made it 16. Verified by removing that one
import — the violation disappears. Re-exported UiStreamEvent and
translateUiEvent from stream-transformers.js, which model-result.ts
already depends on and which owns every other wire-event translation the
loop performs, so no new edge is added.

Complex functions were 9 -> 13, all four new here. Each is split along a
seam it already had:

- translateUiEvent: one function per wire event type (cc=18 -> under)
- scanStatements: string-literal and bracket-depth state machines extracted
- generate: the native path's per-variant event mapping extracted
- renderCall: form controls and Table extracted to renderControl/renderTable

Verified with sentrux 0.5.7, the version CI pins: God files 0 -> 0,
complex functions back to 9 (the 9 remaining are all pre-existing on main
and untouched), gate reports 'No degradation detected'. Behavior unchanged
— full suite green, typecheck and lint clean.
The table of what each stream emits is the reference consumers use to
pick one; getUiStream was absent.
…escript-agent-openui-module-libraryfragment-builders
@cortex-github-agent

cortex-github-agent Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

cortex review — 6898806

Security · ⚠️ Experience (DX · UX · A11y) — incomplete · ⚠️ Performance — incomplete

Experience (DX · UX · A11y)

⚠️ Review incomplete for this category (failed — Invalid final response: empty or invalid output) — findings may be missing.

Performance

⚠️ Review incomplete for this category (failed — Invalid final response: empty or invalid output) — findings may be missing.

✔ 4 resolved since last push
  • uiRef / uiState / uiBuiltin names are serialized into OpenUI Lang source with no identifier validation. The serializer deliberately hardens every other value channel — strings go through JSON.stringify, non-identifier object keys are quoted, non-finite numbers become null (packages/agent/src/lib/openui/document.ts:64-113) — but ref/state/builtin names are emitted verbatim (case 'ref': return expr.name). A toUIOutput implementation that derives a ref from its input (which is model-controlled tool arguments) lets a malicious/injected model break out of the literal quoting and inject arbitrary expressions into the fragment, e.g. uiRef('x), @Run(dangerous_mutation'), which downstream renderers parse as extra action steps in what the client treats as trusted tool-authored UI. Validate names against /^[A-Za-z_][A-Za-z0-9_]*$/ at construction time. (packages/agent/src/lib/openui/fragment.ts:69)
  • Model-controlled parser diagnostics are interpolated into innerHTML unescaped → DOM XSS. d.source is passed through escapeHtml, but d.message beside it is not, and diagnostic messages embed raw model text: ExprParser.parseComplete throws trailing content after expression: '${this.src.slice(this.pos)}' (packages/openui-playground/src/lang/parser.ts) with the remainder of the model's line, and unexpected character '${ch}' / expected identifier at '...' do the same. A model output line such as a = Text("x") <img src=x onerror=...> becomes a diagnostic whose message contains the tag, which is then written into the document via innerHTML. Any prompt-injected or malicious model response therefore executes script in the playground origin — the same origin that can POST /api/generate with the server's API key. Same class at packages/openui-playground/public/app.js:447, where the user-supplied h.model string is interpolated into the history table's innerHTML. Fix: run every interpolated field through escapeHtml (or build the nodes with textContent). (packages/openui-playground/public/app.js:572)
  • API-key-backed generation endpoint listens on all interfaces with no auth or origin check. server.listen(PORT, …) defaults to 0.0.0.0, so anyone on the same network (café/office LAN, container host) can POST /api/generate with an arbitrary prompt/system/model and spend the developer's OPENROUTER_API_KEY (packages/openui-playground/src/server.ts:29-36); there is no rate limit or body-size cap on the path either (readBody, server.ts:58-65). Bind explicitly to 127.0.0.1 for a local-only tool. (packages/openui-playground/src/server.ts:190)
  • Static-file guard uses a bare string prefix check, so sibling directories escape the public root. normalize(join(PUBLIC_DIR, rel)) followed by file.startsWith(PUBLIC_DIR) accepts .../openui-playground/public-anything/...: a request for /../public.bak/secret resolves outside public/ yet still passes the prefix test and is served. Compare against PUBLIC_DIR + path.sep (or use path.relative and reject results starting with ..). (packages/openui-playground/src/server.ts:94)

Automatic first-pass review · updated in place on every push

cortex-github-agent[bot]

This comment was marked as resolved.

@LukasParke LukasParke added the cortex-keep-updated cortex keeps this PR up to date with its base branch label Aug 3, 2026
Two findings from cortex's review pass.

XSS: the diagnostics panel escaped `source` but interpolated `message` and
`line` raw into innerHTML. Every field there is model-controlled —
`ParseFailure.message` is built from the offending source line, and in
native mode diagnostics arrive verbatim off the wire — so a model could
inject markup by emitting a crafted statement. All three fields are now
escaped.

A11y: rendered Input/Select carried no accessible name, so a screen reader
announced an unlabelled field. Both signatures already have a `name` prop
that was going unused for labelling; it now sets aria-label (and the real
`name` attribute), falling back to the placeholder for Input.

@cortex-github-agent cortex-github-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cortex panel verdict: comment — details in the consolidated review comment.

- escape diagnostic messages and history model names before innerHTML (XSS)
- bind playground server to 127.0.0.1 (API-key-backed endpoint)
- require trailing separator in static-file public-root prefix check
- validate uiRef/uiState/uiBuiltin names as identifiers at construction
- aria-label form controls from their name prop; progressbar ARIA + text %
- aria-live status/diagnostics regions; error frames no longer overwritten
  by the green 'done' status
- rename toUIOutput -> toUiOutput (match Ui casing convention pre-release)
- warn (tool name + call id) when toUiOutput throws instead of catch {}
- collect toUiOutput broadcasts and await as one batch off the follow-up
  critical path
- sticky regexes + charCode skipWs in the playground parser (was O(n^2))
- memoize openui(library) wire shape per library (WeakMap)
- drop the playground's no-op build script / outDir
…yfragment-builders' of https://github.com/OpenRouterTeam/typescript-agent into lukeparke/dev-773-typescript-agent-openui-module-libraryfragment-builders-2

# Conflicts:
#	packages/openui-playground/public/app.js

@cortex-github-agent cortex-github-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cortex panel verdict: approve — details in the consolidated review comment.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: --- context after this hunk ---). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent cortex-github-agent Bot added the cortex-merge-conflict cortex could not auto-merge; manual update needed label Aug 4, 2026
@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/tool.ts (resolver produced a line not present on any side: Assistant). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: --- context after this hunk ---). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side: C: Analyze the semantics of both sides and combine them.). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: --- context after this hunk ---). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side:
). Manual update needed; label cortex-merge-conflict added.

3 similar comments
@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side:
). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side:
). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side:
). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/tool.ts (resolver produced a line not present on any side: --- context after this hunk ---). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: --- context after this hunk ---). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side:
). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: File: packages/agent/src/index.ts). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: Human: resolve this hunk). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: File: packages/agent/src/index.ts). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/model-result.ts (resolver produced a line not present on any side: 答案需要合并两侧:OURS 添加了 broadcastUiFragment 方法,THEIRS 添加了 handleAsyncInvocation 等一系列方法). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/lib/tool.ts (resolver produced a line not present on any side: Assistant... need to interleave alphabetically: ToUiOutputFunction, then Unified). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: ToolTaskHandle,). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: Ours and theirs are independent additions; interleave alphabetically: ToolTaskHa). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: ToolTaskHandle,). Manual update needed; label cortex-merge-conflict added.

3 similar comments
@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: ToolTaskHandle,). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: ToolTaskHandle,). Manual update needed; label cortex-merge-conflict added.

@cortex-github-agent

Copy link
Copy Markdown
Contributor

⚠️ cortex keep-fresh: could not auto-merge packages/agent/src/index.ts (resolver produced a line not present on any side: ToolTaskHandle,). Manual update needed; label cortex-merge-conflict added.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cortex-keep-updated cortex keeps this PR up to date with its base branch cortex-merge-conflict cortex could not auto-merge; manual update needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant