Skip to content

feat(routes): generate typed provider declarations and augment the runtime (#95) - #382

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
feat/95-provider-typegen
Sep 3, 2026
Merged

feat(routes): generate typed provider declarations and augment the runtime (#95)#382
ScriptedAlchemy merged 1 commit into
mainfrom
feat/95-provider-typegen

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Closes the last open #95 acceptance item — "a project-defined provider adds a typed context property without compiler modification" — and the delivery step "generates the augmented context type".

  • @agent-bundle/runtime: AgentProviderValues becomes an augmentable interface (readonly [key: string]: unknown plus the optional framework-owned processLifetime, exported as AgentProcessLifetime). Runtime behavior is unchanged.
  • agent-bundle typegen: .agent-bundle/routes.d.ts now declares AgentBundleProviders, ProviderKey, and ProviderValue<Key> from each conventional src/providers/* factory's awaited return type (camel-cased keys, in the exact order generated scopes execute them) and augments @agent-bundle/runtime so (await agent()).providers.<key> is typed with no cast. Provider-free graphs emit no augmentation (never references a module the project has no reason to depend on); a graph with providers but no executable routes keeps the file instead of deleting it.
  • Examples: worktree-proximity and audiobook-curator providers adopt the public AgentProviderContext type from agent-bundle instead of hand-rolled local interfaces (the worktree provider now narrows the discriminated invocation union).
  • Docs: docs/framework-mode.md gains a "Request context and providers" section (same context on every surface, provider contract, AB4940–AB4942, typegen + tsconfig include, test fixture seam); docs/diagnostics.md records the provider declarations beside the route typegen contract. Changeset: @agent-bundle/runtime minor, agent-bundle minor.

Evidence

  • packages/agent-bundle/tests/route-graph.test.ts — new "generates provider declarations and the runtime augmentation in execution order" (deterministic output, key-ordered imports, exact augmentation block) and the existing typegen test now pins that provider-free graphs emit no provider surface. 33/33.
  • packages/agent-bundle/tests/provider-typegen.test.ts (new, integration pool) — real inspect() publishes the declarations for a two-provider project, then a TypeScript program against the built @agent-bundle/runtime declarations proves ProviderKey, ProviderValue<'library'>, and (await agent()).providers.library/.buildNumber/.processLifetime?.hits type-check with no casts, undeclared keys stay unknown, and assigning providers.library to number is rejected ("Type 'LibraryContext' is not assignable to type 'number'"). 1/1.
  • After rebase onto ccb9cd18a: pnpm typecheck green, pnpm lint 0/0, pnpm test:projection 63/63, pnpm test:route-unit 35/35, targeted integration (provider-typegen, cli-routes-build, worktree-proximity-journeys, generated-route-server, state-packaging) all green.
  • pnpm test:unit: 2665 passed, 2 failed — mcp-probe-service teardown timing and native-claude-contract 5s timeout; both reproduce identically on pristine origin/main under load average >120 (stash A/B), unrelated.

Test plan

  • typegen unit + program-level integration proof
  • typecheck, lint, projection, route-unit
  • CI

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 324819d

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 Minor

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 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-03T05:41:18.183806Z e9e923c 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.

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@382
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@382
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@382

commit: e9e923c

@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: e9e923caf2

ℹ️ 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".

`import type * as provider${String(index)} from ${JSON.stringify(declarationModulePath(provider.provenance.relativePath))};`;

const providerMember = (provider: CompiledProvider, index: number): string =>
` readonly ${JSON.stringify(providerKeyFromName(provider.name))}: ProviderValueOf<typeof provider${String(index)}.default>;`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not require providers in contexts where they can be omitted

When the generated declaration is included, this required member merges globally into AgentProviderValues, but AgentRequestInit.providers remains optional and runAgentRequest converts an omission to {} (packages/rsc-runtime/src/agent-request.ts:184,415). Consequently, a custom invocation or renderRoute test can omit provider fixtures while (await agent()).providers.library is still typed as present, leading to an unchecked undefined dereference at runtime. Make generated keys optional for such contexts, or distinguish generated scopes from contexts that do not install providers.

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.

Agreed — addressed in follow-up #409 (2d1cd86). Rather than weakening handler types, the contexts that do not run providers now carry the obligation: once the generated augmentation adds required keys to AgentProviderValues, AgentRequestInit.providers (AgentRequestProvidersInit), the harness options argument (HarnessOptionsArguments) and context (RenderRouteContextInit) become required in that program. provider-typegen.test.ts proves omitting providers, a declared key, or the options argument is a compile error while a complete custom scope typechecks; provider-free projects and generated scopes are unchanged.

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 on main by #409 (62138ef, the #95/#100 follow-up lane): harness and test contexts that do not run providers now require the provider fixtures the augmentation declares, and docs/framework-mode.md/docs/entry-conventions.md document the contract; #408 had only clarified the docs and dropped that change in favour of #409 during rebase.

…ntime (#95)

AgentProviderValues becomes an augmentable interface (string index of
unknown plus the optional framework-owned processLifetime), and the
generated .agent-bundle/routes.d.ts declares AgentBundleProviders,
ProviderKey, and ProviderValue<Key> from each conventional provider
factory's awaited return type in execution order, augmenting
@agent-bundle/runtime so (await agent()).providers.<key> is typed without
a compiler change per provider. Examples adopt the public
AgentProviderContext type; framework-mode documents request context and
providers.
@ScriptedAlchemy
ScriptedAlchemy force-pushed the feat/95-provider-typegen branch from e9e923c to 324819d Compare September 3, 2026 06:07
@ScriptedAlchemy
ScriptedAlchemy merged commit dee724f into main Sep 3, 2026
4 checks passed
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
* fix(runtime,test,inspect): require provider fixtures where providers do not run; project only capability contract fields (#95, #100 review follow-ups)

* chore(changeset): follow the changeset summary convention (#409)

* chore(changeset): classify the required provider fixtures as a pre-1.0 minor (#409)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)
ScriptedAlchemy added a commit that referenced this pull request Sep 3, 2026
/#379/#385/#390) (#408)

* fix: address late review threads on merged PRs #368/#373/#374/#377/#378/#379/#385/#390

- dev: lease the adopted epoch in EpochAdoptionPolicy until replaced or closed;
  select the contract-matrix target from the server's own target list; apply the
  session timeout per matrix request; observe lifecycle progress through the
  session trace via a new ContractMatrixClient.observeProgress seam (#385)
- playground: wait for a hard-link catalog publisher to release its staging link
  before adopting the sidecar; return to discovery when the publication is
  rolled back (#377)
- build: run the Agent Plugins byte lane over portable/ during ordinary artifact
  validation; reject every forbidden control character in header values (#373)
- events/hooks: Codex PostToolUse accepts any present JSON tool_response (#378)
- api: project only contract fields of adapter capability rows in inspect (#390)
- tests/support: digest the real Claude home in the live session guard; isolate
  USERPROFILE alongside HOME (#374)
- docs: Claude plugin-root cwd exception, parked-pin trigger independence,
  provider typing contract, portable validation moments (#368/#379/#382/#373)

* fix(dev): lease before publishing contract status; align fixtures with the portable byte lane

- EpochAdoptionPolicy acquires the epoch lease before publishing a passed
  dev.contract.status and announces adoption synchronously with it, so a
  status reader never sees "passed" for an epoch that is not yet adopted
- hooks.test: Codex PostToolUse accepts a string tool_response and rejects a
  missing one; Claude keeps the object check
- mcp-session-service/public-api-packed fixtures: Agent Plugins forbids
  placeholders in headers and non-bare/non-./ commands, and ordinary artifact
  validation now enforces the standard, so the fixtures carry a literal header
  and a bare `node` command

* chore(changeset): drop the Codex tool_response bullet already released by #404

* fix(playground,test): withdraw a failed catalog publication before releasing its staging link; bind custom observeProgress

- #persistSnapshot rolls the sidecar back while the staging link still exists
  when the post-link directory fsync fails, so a concurrent reader keeps seeing
  an in-progress publication until the path is withdrawn instead of adopting a
  briefly singly linked file
- contractProgressObserver invokes a client's observeProgress method with the
  client as receiver

* chore(changeset): one-paragraph summary ending with the PR reference

* fix(playground): recover a catalog staging link abandoned by an exited publisher

After the settle deadline, a matching .stage-<pid>-* link whose publisher pid
no longer exists is an abandoned publication of an already fsynced sidecar:
withdraw the orphan and adopt the sidecar instead of rejecting the epoch
forever. A live publisher's staging link is still never yanked. Adds the
@internal catalogStagingSettleDeadlineMs seam for deterministic tests.

* fix(playground): fsync the catalog directory after withdrawing an abandoned staging link

* fix(dev): forward request _meta (progress token) through McpSession and the dev matrix client

Lifecycle fixtures pass their generated progressToken as params._meta; the
session adapter and McpSession.callTool dropped it, so generated routes never
enabled sendProgress and every progress-gated lifecycle fixture failed the dev
matrix. McpSessionToolCallOptions and McpClient.callTool now carry _meta.

* fix(dev,playground): restart the adoption drain after a handoff race; keep the staging link when a sidecar rollback fails

- EpochAdoptionPolicy reschedules its drain from the completion handler when a
  candidate arrived between the loop's last empty check and #processing being
  cleared, and settled() waits through restarts
- #persistSnapshot releases the staging link after a failed publication only
  once the owned sidecar is confirmed withdrawn, so a rollback failure never
  leaves a singly linked sidecar for readers to adopt

* ci: retrigger checks for the rebased head

* chore: drop the portable byte-lane changes superseded by #406; keep the _meta assertions

* fix(playground): restore the staging guard when a recovery fsync fails

* fix(playground): accept a concurrently restored staging guard (EEXIST aliasing the sidecar) during recovery

* fix(dev): recheck supersession after the adoption lease settles

* fix(playground): keep a fresh pid-owned guard when recovery can neither re-link nor withdraw the sidecar

* fix(playground): fsync every compensating recovery guard before trusting it
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