Merge branch 'stack/agent-profiles-1-seam' into stack/agent-profiles-3-wire

Rewires this layer onto the standing-mount model:

- serviceForAgent roots its search at the agent's standing mount (parent
  scope key → live mount fiber) — the composition no longer lives under the
  agent's own fiber, and two agents on one preset now address ONE instance,
  which the sharing test asserts instead of distinctness.
- viewFor/historyPage take a registry view SCOPE. A live agent is that scope;
  a cold read uses the recorded preset's standing key via standingKeyFor —
  composing plugins but starting no agent, session, or turn. A header without
  a preset (a pre-roster log) renders through the DEFAULT preset's standing
  layer; an unusable preset degrades the read to generic cards, never fails
  it. This turns produced-files and chat-scroll green structurally, with the
  token counts untouched (no resume, so the projections fold stays detached).
- The detached projections baseline now includes every standing unit's key at
  its empty fold (todos: null): the standing mount registers units
  deterministically, which is what makes the client's "omitted key =
  capability absence → clear" rule safe again. seeded-history's contract test
  asserts the new shape.
- The standard preset's realm preamble no longer claims a shared label pools
  instances — provide() throws on the second registration under one realm
  symbol; labels join REALMS.
This commit is contained in:
Yichen Jiang
2026-08-08 18:41:07 +08:00
24 changed files with 566 additions and 117 deletions
+17 -6
View File
@@ -18,7 +18,7 @@ import { pathToFileURL } from 'node:url'
import { Context, type Fiber } from 'cordis'
import { Include } from '@cordisjs/plugin-include'
import type { EntryTree } from '@cordisjs/plugin-loader'
import { scopeOf } from '@deepseek-ai/dsh-scope'
import { scopeOf, scopeParentOf, type ScopeKey } from '@deepseek-ai/dsh-scope'
import { PresetMountError, type AgentPreset } from './types.ts'
/** What one mounted subtree publishes about itself for the audit to read. */
@@ -77,6 +77,8 @@ export interface PresetMount {
readonly presetId: string
/** The mounted subtree's fiber. */
readonly fiber: Fiber
/** The standing scope key agents are parented to (undefined only in torn-down records). */
readonly key: ScopeKey | undefined
}
const mounts = new Set<PresetMount>()
@@ -189,14 +191,22 @@ export function serviceForAgent<K extends string & keyof Context>(
agent: { ctx: Context },
name: K,
): Context[K] | undefined {
const root = agent.ctx.fiber
// The agent's own key is parented to its preset's standing key; the mount
// is no longer under the agent's fiber, so the search roots at the standing
// mount instead of walking up from the agent.
const agentKey = scopeOf(agent.ctx)
if (agentKey === undefined) return undefined
const standingKey = scopeParentOf(agentKey)
if (standingKey === undefined) return undefined
const mount = livePresetMounts().find(candidate => candidate.key === standingKey)
if (mount === undefined) return undefined
const store = ctx.reflect.store
for (const key of Object.getOwnPropertySymbols(store)) {
const impl = store[key]
/* v8 ignore next -- cordis deletes a store slot on disposal rather than clearing it */
if (impl === undefined) continue
if (impl.name !== name) continue
if (withinFiber(impl.fiber, root)) return impl.value as Context[K]
if (withinFiber(impl.fiber, mount.fiber)) return impl.value as Context[K]
}
return undefined
}
@@ -248,8 +258,9 @@ export async function mountPreset(agentCtx: Context, preset: AgentPreset): Promi
)
}
const config: Include.Config = { path: pathToFileURL(preset.path).href }
// Before the record this mount is about to add: every session takes this
// path, so it is what keeps the set bounded on a host that never reads it.
// Before the record this mount is about to add: standing mounts are one per
// preset and live until whole-tree teardown, so pruning here only sweeps
// records of torn-down runtimes (tests; an HMR reload of the roster).
pruneDisposedMounts()
const handle = agentCtx.plugin(PresetTree, config)
try {
@@ -269,7 +280,7 @@ export async function mountPreset(agentCtx: Context, preset: AgentPreset): Promi
+ 'a preset service must sit behind an `isolate` realm or move to the host composition',
)
}
mounts.add({ presetId: preset.id, fiber })
mounts.add({ presetId: preset.id, fiber, key: scopeOf(agentCtx) })
} catch (error) {
try {
await handle.dispose()