feat(lineage): expose the live agent tree (siblings, children, roots) on request.lineage (#457) - #544
Merged
Merged
Conversation
🦋 Changeset detectedLatest commit: 1778694 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 4, 2026
commit: |
ScriptedAlchemy
force-pushed
the
feat/457-lineage-tree
branch
from
September 4, 2026 07:55
87d39a9 to
12c3f7b
Compare
ScriptedAlchemy
added a commit
that referenced
this pull request
Sep 4, 2026
ScriptedAlchemy
force-pushed
the
feat/457-lineage-tree
branch
from
September 4, 2026 08:06
12c3f7b to
1d22eec
Compare
ScriptedAlchemy
force-pushed
the
feat/457-lineage-tree
branch
from
September 4, 2026 08:45
1d22eec to
7332e37
Compare
ScriptedAlchemy
enabled auto-merge (squash)
September 4, 2026 08:45
# Conflicts: # examples/worktree-proximity/README.md # examples/worktree-proximity/src/event-support.ts # examples/worktree-proximity/src/events/session/start.tsx # examples/worktree-proximity/src/events/tool/before.tsx # examples/worktree-proximity/src/mcp/coordinator/tools/status.tsx # examples/worktree-proximity/tests/route-unit/routes.test.ts # packages/agent-bundle/tests/worktree-proximity-journeys.test.ts # website/docs/en/guide/authoring/mcp.mdx # website/docs/zh/guide/authoring/mcp.mdx
# Conflicts: # examples/worktree-proximity/src/event-support.ts # examples/worktree-proximity/src/events/agent/start.tsx # examples/worktree-proximity/src/events/session/start.tsx # examples/worktree-proximity/src/events/stop.tsx # examples/worktree-proximity/src/events/tool/after.tsx # examples/worktree-proximity/src/events/tool/before.tsx # examples/worktree-proximity/tests/route-unit/routes.test.ts
This was referenced Sep 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #457.
Why
Since #421/#444,
(await agent()).lineageanswers "who is my parent, what is my root, am I a subagent" for the current request, while the warm runtime's lineage registry holds the whole tree and exposes it only throughsnapshot()(dumps, the Workbench). A plugin that coordinates agent threads (examples/worktree-proximity) therefore hand-rolled an actor tree —kind: root|child,parentSessionId,status: active|stopped— fed fromsession/start,agent/start, andstop, duplicating what the registry already knew.Design
AgentLineage.tree?: AgentLineageTree(@agent-bundle/runtime) —{ siblings, children, roots }ofAgentLineagePeer({ conversation, depth, parent?, startedAt, subagent?, resolution }). Computed at resolve time fromLineageState.nodesby the same registry that placed the request, on every path that produces a placed lineage:observe()for event routes,resolveToolCall()for MCP calls correlated through a hook window or a Codex_metawhose thread the registry saw start. It rides the existingObserved<AgentLineage>axis, so unavailability semantics and thecontext.lineageharness seam are unchanged, and it is plain frozen data (structuredClone-safe), so the Flight worker receives it exactly as the entry process resolved it.siblingsis every other live conversation under the caller's root, any depth, oldest first — the root itself included for a subagent — so a coordinator sees the whole live tree it belongs to;childrenthe direct live children;rootsthe other live depth-0 conversations, on Cursor only those seen in the sameworkspace_roots(the rule that already scopes child binding). A Cursor child whose conversation has not spoken yet is listed under itssubagent_id.resolutionis judged exactly as it would be on a request that node made itself (nodeResolution, extracted fromresolve()). Stopped nodes are never listed. No tree when the registry did not place the request: a standalone hook (resolveStandaloneLineage), or a Codex_metanaming a thread the registry never saw start, still resolves its own chain and carries notree. Nothing derives an actor or a user (Actor principal for generated event scopes: typed unavailable reasons and the one derivable host field (parked from #233/#223) #391);request.lineagestays the only identity-adjacent surface (feat(lineage): request.lineage is the only identity-adjacent surface; scoped, self-correcting Cursor child binding #444).AGENT_REQUEST_STORE_VERSION(the handle shape is unchanged;treeis an optional field of an existing value), the Workbench replay provenance (a deterministic replay has no registry and never produces a tree), the registry's mutations and journal.examples/worktree-proximityDeleted the actor bookkeeping the issue names:
ActorSchema(kind,parentSessionId,status, the per-actorprovenance.id/parentSessionId), theactorObservedandactorStoppedevents, and thesession:<root>pseudo-id. The state definition is nowworktree-proximity/intent—bindings(actor → worktree, the one fact the registry cannot know),activities,refusals— withactorBound,actorReleased,intentRecorded,edgeRefused.session/startandagent/startbind conversations under the idsrequest.lineage.treelists them by; the edge itself is the registry's.tool/beforetakes liveness fromlineage.tree.siblings(liveConversations()): an intent held by a host-identified actor the tree no longer lists under our root is stale and warns nobody; a derivedworktree:<root>actor is never filtered that way; a lineage without a tree presumes nothing. Newsrc/events/agent/stop.tsxroute (so the registry marks children stopped and the route releases their binding and intent). The coordinatorstatustool reportsagents— the tree the runtime resolved for its call (agentTree()), honestlyunavailablefor a client no hook window names — besidebindings,activities,refusals. Theagent-topologyprovider stays an honest stub until #459 (providers still receive nolineage). README rewritten accordingly.examples/host-test:dump --conversationnow matches the request's own chain (conversation/parent/root/subagent.id) instead of a JSON substring, so a sibling listed intreedoes not make a record match.Tests
rsc-runtime/tests/lineage-registry.test.ts— newlineage tree (#457)block: Claude 2.1.259 orchestration replay assertingsiblings/children/rootsat 15 rows (parallel + nested spawns, stops, the resume), peerresolution/startedAt/subagentagainst the registry's own facts and against the peer's own requests; Codex 0.147.0 (hook and_metapaths, tree absent for a cold thread); Cursor 3.18.25 (pending child under itssubagent_id, bound child, nested child, stops); synthetic two-root Claude and two-workspace Cursor cases forroots; frozen +structuredCloneround trip. TwotoEqualassertions inlineage-codex-rollout.test.tsgained the tree.examples/worktree-proximity/tests/route-unit/routes.test.ts— journeys 1–8 pass on the new state shape; new: the child sees a sibling's intent while the tree lists it and stops once the tree drops it (the acceptance test),statusrenders the injected tree,agent/stopreleases a child;tests/proximity.test.tscovers the liveness filter and the derived-actor exemption.packages/agent-bundle/tests/worktree-proximity-journeys.test.ts(cross-process) — children are now spawned Claude-style (AgentPreToolUse → SubagentStart → PostToolUse naming the child) andstatusis called inside the root's hook window: the root sees both childrenconfirmedin the registry-fed tree, still after a server restart (the lineage journal is durable beside the intent state), and onlyagent-aafteragent-b's SubagentStop, which also releases its binding and intent. Exact-revision equality across the restart still holds.Docs
docs/entry-conventions.md(lineage section: shape and semantics oftree),website/docs/{en,zh}/guide/authoring/mcp.mdx(request-context lineage paragraph). Changeset:.changeset/457-lineage-tree.md(@agent-bundle/runtimepatch).Verification
pnpm typecheck,pnpm lint,pnpm test:unit(3257 passed),pnpm test:route-unit(58),worktree-proximity-journeys.test.ts(integration pool),examples/worktree-proximitycheck(validate, typecheck, unit, route-unit),examples/host-testtypecheck + route-unit,pnpm docs:site:build.Coordination
Rebased onto
mainatdacb5b0ff(#536): the proximity route-unit suite was rewritten on top of #525'smountTestState, so this PR's new tests use that harness API too. #532 (request.plugin) touches the same generated shells but not the lineage path and rebases cleanly either way. #459 builds on this PR'streeand is a separate PR; #458 (#539), #460 (#541), #532 (request.plugin), and #466 (#545,canonical.payloadon event routes) landed onmainwhile this PR was open and were merged in (6e5936018,177869456): the proximity example now addresses notices throughnoticeRecipientFor(binding, worktreeRoot)— a binding whose provenance isderivedgetsrecipient.workspace.root, every other bindingrecipient.conversation(its actor id is the lineage conversation, so #539'ssession:prefix stripping is gone with the prefix);statusreports #460'snoticescounts besideagents/bindings/activities; and every event route, including the newagent/stop, readscanonical.payload(agentId/sessionId/toolName/toolInput) instead of the native envelope.Review status
73bdb9e(PR opened): Codex review completed 2026-09-04T07:20Z with no findings (no review threads).25f6f8ae9(pins thetreeon the twomcp-lineageprojection assertions that failed the first CI run) and the rebase ontodacb5b0ff(head1d22eec96, test file re-applied onmountTestState, no source changes). No PR comments are posted from this side, so no@codex reviewwas requested; the connector does not re-review on its own after a rebase.6e5936018and177869456(merges ofmainat6c2f8da0aand55026f076): conflict resolution only inexamples/worktree-proximity/**,packages/agent-bundle/tests/worktree-proximity-journeys.test.ts, and the twomcp.mdxpages (both sides kept: thetreeparagraph followed by Expose a terminal/IO capability to routes and rendered scripts (TTY-ness, color, columns) instead of each plugin re-detecting it #511's terminal section); nopackages/rsc-runtimesource changes. Unreviewed; verified locally withpnpm typecheck,pnpm lint,examples/worktree-proximitycheck(6 unit + 16 route-unit),examples/host-testtypecheck + route-unit (7),worktree-proximity-journeys.test.ts(integration pool), andpnpm docs:site:build.pull_requestruns for the pushes betweenf2894fc7cand the rebase (close/reopen and a fresh SHA did not help while other PRs' pushes did trigger); the rebased push triggered normally.