Skip to content

feat(runtime): MCP progress projector and warm-runtime proofs (#96) - #175

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
wave4/96-mcp-projector
Sep 1, 2026
Merged

feat(runtime): MCP progress projector and warm-runtime proofs (#96)#175
ScriptedAlchemy merged 1 commit into
mainfrom
wave4/96-mcp-projector

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Wave 4 Lane D / #96 stage 4: standards-compatible MCP projector over the Effect render-event stream, plus warm-runtime fail-closed proofs.

  • projectMcpRenderStream emits notifications/progress only when the caller supplied a progress token; maps monotonically increasing numeric progress + optional total + short message (cap 200); buffers shell/replace internally and never encodes partial content in progress messages; returns one CallToolResult with supported blocks and object-valued structured content.
  • Image/audio/resource blocks are capability-gated: declared text fallback or typed McpProjectionError — no silent drops. Agent.Context projects to text (MCP has no additional-context block).
  • createWarmFlightHost + generated-entry worker: one process-lifetime provider across consecutive tools/calls; artifact-epoch mismatch, worker restart, and a missing runtime fail closed with typed AgentRuntimeError (MCP surfaces those as isError results, never fabricated success).
  • Generated Flight workers pipe stdout/stderr so worker death cannot corrupt the parent's MCP stdio channel.
  • Tasks-augmented flow (CreateTaskResult + tasks/get + tasks/result + tasks/cancel) is not in this stage.

Projector contract (what emits when)

Condition Emission
No _meta.progressToken No notifications/progress; final CallToolResult only
Token + monotonic completed notifications/progress with progress, optional total, shortened message
Non-increasing / rewind progress Dropped
shell / replace Buffered internally; never copied into progress messages
complete One CallToolResult; progress nodes omitted from content; object document.valuestructuredContent
Non-object value structuredContent omitted (not wrapped)
Gated image/audio/resource Text fallback or McpProjectionError (unsupported-rich-content)
Client abort / notifications/cancelled Renderer AbortSignal (SDK already aborts context.mcpReq.signal)

Warm-runtime proofs

  • Consecutive generated warmth calls: hits 1 then 2, same instanceId and pid.
  • Artifact-epoch mismatch: typed AgentRuntimeError (artifact-epoch-mismatch), host not executed.
  • Worker process.exit(1) then next warmth: isError / typed restart-or-unavailable, never hits: 2.
  • Missing runtime: typed runtime-unavailable.

Test plan

  • pnpm lint (0 errors)
  • pnpm typecheck
  • Runtime: projector + warm-runtime + dispatcher + effect-boundary (47 passed)
  • packages/agent-bundle/tests/entry-shell.test.ts (11 passed)
  • generated-route-server.test.ts integration (5 passed, retry:2)
  • Audiobook example tests (35 passed)
  • pnpm eval:spot (passed)

Stage 5/6 still needs

Helped / hurt

Born on the Effect render-event stream: emit notifications/progress only
when the caller supplied a token, return one CallToolResult, and fail
closed on epoch mismatch, restart, and a missing runtime.
@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6c8078d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@agent-bundle/runtime Minor
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ScriptedAlchemy
ScriptedAlchemy merged commit ed44cd5 into main Sep 1, 2026
4 of 9 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-01T19:05:29.389570Z 6c8078d PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6c8078d36a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

' worker.on(\'message\', (message) => {',
' const request = pending.get(message.id);',
' if (request === undefined) return;',
" if (message.type === 'progress') { void request.progress?.report(message.update); return; }",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Await worker progress forwarding

When a generated tool reports more than the session's buffered progress limit (10,000 by default) before returning, request.progress.report() rejects with event-count-exceeded, but this discarded promise becomes an unhandled rejection in the MCP server process. Under the supported Node versions, that can terminate the entire stdio server instead of failing only the offending tool call; forwarding needs to be awaited/serialized (with worker acknowledgement for backpressure) or its rejection must be routed into the pending request.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Fixed in #210 (merged as 1c36813): the generated shell no longer voids progress forwarding — rejections (and sync throws) from entry.progress.report route into the request failure path via entry.fail, so a progress-cap rejection fails the request instead of becoming an unhandled worker rejection. Regression test in entry-shell.test.ts runs the generated factory with a rejecting reporter and asserts the request fails with no unhandled rejection.

ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
…endered workers, canonical result output (#210)

* fix(cli): worker-name collisions, negative positionals, fail-closed rendered workers, canonical result output

Post-merge review findings from #186, #175, and #178: reserve generated
*-flight.mjs worker destinations during entry planning so a conventional
script colliding with a rendered worker is a build-time AB5000 diagnostic
instead of a silent overwrite; treat a lone -<digit> token as a positional
when the next positional expects a number (keeping -- escape and unknown
single-dash rejection intact); fail all pending rendered requests closed
when the worker exits with any code, including 0; route rejected progress
reports into the request failure path instead of voiding them; serialize
plain CLI results with the canonical stableJson writer (undefined emits
null); and validate the exitCode result mapping before writing stdout so
an invalid mapping cannot emit a success line first. The inlined cli-entry
runtime now ignores its owning source root during builds because it pulls
sibling core imports.

* fix(build): anchor the runtime ignored root to the module's marker ancestor

Review follow-up on #210: runtimeIgnoredRoot matched any /dist/ segment
in the path, so a checkout living under a dist directory resolved the
ignored root to the wrong ancestor and silently ignored the whole
checkout. The root is now the parent of the nearest src or dist ancestor
of the runtime module itself, and an unmarked path is a loud error.
@ScriptedAlchemy
ScriptedAlchemy deleted the wave4/96-mcp-projector branch September 3, 2026 05:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant