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

View File

@@ -236,14 +236,13 @@ describe('web e2e: seeded history renders through cold resume', () => {
// The seed carries a session/title event: the title unit is host-plane, so
// it folds the detached log and serves the value with nothing composed.
expect(typeof projections?.values.title).toBe('string')
// `todos` is NOT here, and that is the contract rather than a gap. Its unit
// is registered by `tool-todo` inside an agent's preset, so a detached
// session yields it from exactly one place: a durable checkpoint written
// while the session was live. This seed was written straight to persistence
// and never ran, so it recorded none — and the answer no longer depends on
// whether some UNRELATED session happens to be composed right now, which is
// the whole reason the checkpoint row carries its own view.
expect(projections?.values).not.toHaveProperty('todos')
// `todos` IS here, as its empty fold (null). Its unit is registered by
// `tool-todo` inside the default preset's STANDING mount, which the read
// itself ensures — deterministically, not because some unrelated session
// happens to be composed. A present-but-null key is what keeps the
// client's "omitted key = capability absent → clear the row" rule from
// wiping preset-owned projections on cold reads.
expect(projections?.values).toHaveProperty('todos', null)
})
it.skipIf(MODE === 'record')('lists the seeded session cold and renders its history from the log', async () => {