@namzu/sdk 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -5
- package/README.md +14 -9
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +5 -3
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +3 -0
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +18 -5
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +6 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/a2a/task.d.ts +2 -2
- package/dist/bridge/a2a/task.d.ts.map +1 -1
- package/dist/bridge/a2a/task.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +6 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/constants/a2a/index.d.ts +2 -2
- package/dist/constants/a2a/index.d.ts.map +1 -1
- package/dist/constants/a2a/index.js.map +1 -1
- package/dist/contracts/api.d.ts +22 -3
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +6 -0
- package/dist/gateway/local.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/manager/agent/__tests__/lifecycle.test.d.ts +2 -0
- package/dist/manager/agent/__tests__/lifecycle.test.d.ts.map +1 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js +302 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -0
- package/dist/manager/agent/lifecycle.d.ts +58 -3
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +311 -12
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +8 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +15 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +25 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/context.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/context.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/context.test.js +84 -0
- package/dist/runtime/query/__tests__/context.test.js.map +1 -0
- package/dist/runtime/query/context.d.ts +55 -2
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +48 -8
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/events.d.ts.map +1 -1
- package/dist/runtime/query/events.js +8 -0
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/index.d.ts +25 -2
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +11 -1
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/session/__tests__/integration/_fixtures.d.ts +115 -0
- package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -0
- package/dist/session/__tests__/integration/_fixtures.js +198 -0
- package/dist/session/__tests__/integration/_fixtures.js.map +1 -0
- package/dist/session/__tests__/integration/capacity-caps.test.d.ts +13 -0
- package/dist/session/__tests__/integration/capacity-caps.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/capacity-caps.test.js +116 -0
- package/dist/session/__tests__/integration/capacity-caps.test.js.map +1 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.d.ts +18 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.js +226 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.js.map +1 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts +15 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.js +323 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts +12 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js +170 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts +18 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js +146 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts +15 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js +163 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts +12 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js +157 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js.map +1 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.d.ts +11 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.js +140 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.js.map +1 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts +13 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.js +84 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.js.map +1 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts +14 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js +241 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js.map +1 -0
- package/dist/session/__tests__/integration/retention-archive.test.d.ts +12 -0
- package/dist/session/__tests__/integration/retention-archive.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/retention-archive.test.js +186 -0
- package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts +18 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.js +200 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.d.ts +14 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.js +180 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -0
- package/dist/session/errors.d.ts +60 -0
- package/dist/session/errors.d.ts.map +1 -0
- package/dist/session/errors.js +50 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/events/index.d.ts +4 -0
- package/dist/session/events/index.d.ts.map +1 -0
- package/dist/session/events/index.js +8 -0
- package/dist/session/events/index.js.map +1 -0
- package/dist/session/events/schema-version.d.ts +13 -0
- package/dist/session/events/schema-version.d.ts.map +1 -0
- package/dist/session/events/schema-version.js +12 -0
- package/dist/session/events/schema-version.js.map +1 -0
- package/dist/session/events/types.d.ts +64 -0
- package/dist/session/events/types.d.ts.map +1 -0
- package/dist/session/events/types.js +2 -0
- package/dist/session/events/types.js.map +1 -0
- package/dist/session/handoff/__tests__/broadcast.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/broadcast.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/broadcast.test.js +243 -0
- package/dist/session/handoff/__tests__/broadcast.test.js.map +1 -0
- package/dist/session/handoff/__tests__/capacity.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/capacity.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/capacity.test.js +100 -0
- package/dist/session/handoff/__tests__/capacity.test.js.map +1 -0
- package/dist/session/handoff/__tests__/single.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/single.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/single.test.js +230 -0
- package/dist/session/handoff/__tests__/single.test.js.map +1 -0
- package/dist/session/handoff/assignment.d.ts +59 -0
- package/dist/session/handoff/assignment.d.ts.map +1 -0
- package/dist/session/handoff/assignment.js +11 -0
- package/dist/session/handoff/assignment.js.map +1 -0
- package/dist/session/handoff/broadcast.d.ts +47 -0
- package/dist/session/handoff/broadcast.d.ts.map +1 -0
- package/dist/session/handoff/broadcast.js +296 -0
- package/dist/session/handoff/broadcast.js.map +1 -0
- package/dist/session/handoff/capacity.d.ts +66 -0
- package/dist/session/handoff/capacity.d.ts.map +1 -0
- package/dist/session/handoff/capacity.js +60 -0
- package/dist/session/handoff/capacity.js.map +1 -0
- package/dist/session/handoff/events.d.ts +66 -0
- package/dist/session/handoff/events.d.ts.map +1 -0
- package/dist/session/handoff/events.js +13 -0
- package/dist/session/handoff/events.js.map +1 -0
- package/dist/session/handoff/index.d.ts +12 -0
- package/dist/session/handoff/index.d.ts.map +1 -0
- package/dist/session/handoff/index.js +9 -0
- package/dist/session/handoff/index.js.map +1 -0
- package/dist/session/handoff/single.d.ts +62 -0
- package/dist/session/handoff/single.d.ts.map +1 -0
- package/dist/session/handoff/single.js +217 -0
- package/dist/session/handoff/single.js.map +1 -0
- package/dist/session/handoff/version.d.ts +52 -0
- package/dist/session/handoff/version.d.ts.map +1 -0
- package/dist/session/handoff/version.js +36 -0
- package/dist/session/handoff/version.js.map +1 -0
- package/dist/session/hierarchy/__tests__/session.test.d.ts +2 -0
- package/dist/session/hierarchy/__tests__/session.test.d.ts.map +1 -0
- package/dist/session/hierarchy/__tests__/session.test.js +67 -0
- package/dist/session/hierarchy/__tests__/session.test.js.map +1 -0
- package/dist/session/hierarchy/actor.d.ts +26 -0
- package/dist/session/hierarchy/actor.d.ts.map +1 -0
- package/dist/session/hierarchy/actor.js +2 -0
- package/dist/session/hierarchy/actor.js.map +1 -0
- package/dist/session/hierarchy/index.d.ts +8 -0
- package/dist/session/hierarchy/index.d.ts.map +1 -0
- package/dist/session/hierarchy/index.js +4 -0
- package/dist/session/hierarchy/index.js.map +1 -0
- package/dist/session/hierarchy/lineage.d.ts +15 -0
- package/dist/session/hierarchy/lineage.d.ts.map +1 -0
- package/dist/session/hierarchy/lineage.js +2 -0
- package/dist/session/hierarchy/lineage.js.map +1 -0
- package/dist/session/hierarchy/project.d.ts +40 -0
- package/dist/session/hierarchy/project.d.ts.map +1 -0
- package/dist/session/hierarchy/project.js +2 -0
- package/dist/session/hierarchy/project.js.map +1 -0
- package/dist/session/hierarchy/session.d.ts +59 -0
- package/dist/session/hierarchy/session.d.ts.map +1 -0
- package/dist/session/hierarchy/session.js +51 -0
- package/dist/session/hierarchy/session.js.map +1 -0
- package/dist/session/hierarchy/sub-session.d.ts +76 -0
- package/dist/session/hierarchy/sub-session.d.ts.map +1 -0
- package/dist/session/hierarchy/sub-session.js +2 -0
- package/dist/session/hierarchy/sub-session.js.map +1 -0
- package/dist/session/hierarchy/tenant.d.ts +13 -0
- package/dist/session/hierarchy/tenant.d.ts.map +1 -0
- package/dist/session/hierarchy/tenant.js +2 -0
- package/dist/session/hierarchy/tenant.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +15 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.d.ts +2 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.d.ts.map +1 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.js +179 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.js.map +1 -0
- package/dist/session/intervention/index.d.ts +3 -0
- package/dist/session/intervention/index.d.ts.map +1 -0
- package/dist/session/intervention/index.js +8 -0
- package/dist/session/intervention/index.js.map +1 -0
- package/dist/session/intervention/prev-artifact.d.ts +103 -0
- package/dist/session/intervention/prev-artifact.d.ts.map +1 -0
- package/dist/session/intervention/prev-artifact.js +112 -0
- package/dist/session/intervention/prev-artifact.js.map +1 -0
- package/dist/session/migration/__tests__/filesystem.test.d.ts +2 -0
- package/dist/session/migration/__tests__/filesystem.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/filesystem.test.js +188 -0
- package/dist/session/migration/__tests__/filesystem.test.js.map +1 -0
- package/dist/session/migration/__tests__/id-prefix.test.d.ts +2 -0
- package/dist/session/migration/__tests__/id-prefix.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/id-prefix.test.js +83 -0
- package/dist/session/migration/__tests__/id-prefix.test.js.map +1 -0
- package/dist/session/migration/__tests__/marker.test.d.ts +2 -0
- package/dist/session/migration/__tests__/marker.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/marker.test.js +75 -0
- package/dist/session/migration/__tests__/marker.test.js.map +1 -0
- package/dist/session/migration/errors.d.ts +26 -0
- package/dist/session/migration/errors.d.ts.map +1 -0
- package/dist/session/migration/errors.js +22 -0
- package/dist/session/migration/errors.js.map +1 -0
- package/dist/session/migration/filesystem.d.ts +94 -0
- package/dist/session/migration/filesystem.d.ts.map +1 -0
- package/dist/session/migration/filesystem.js +319 -0
- package/dist/session/migration/filesystem.js.map +1 -0
- package/dist/session/migration/id-prefix.d.ts +98 -0
- package/dist/session/migration/id-prefix.d.ts.map +1 -0
- package/dist/session/migration/id-prefix.js +116 -0
- package/dist/session/migration/id-prefix.js.map +1 -0
- package/dist/session/migration/index.d.ts +8 -0
- package/dist/session/migration/index.d.ts.map +1 -0
- package/dist/session/migration/index.js +8 -0
- package/dist/session/migration/index.js.map +1 -0
- package/dist/session/migration/marker.d.ts +57 -0
- package/dist/session/migration/marker.d.ts.map +1 -0
- package/dist/session/migration/marker.js +111 -0
- package/dist/session/migration/marker.js.map +1 -0
- package/dist/session/retention/__tests__/archive.test.d.ts +2 -0
- package/dist/session/retention/__tests__/archive.test.d.ts.map +1 -0
- package/dist/session/retention/__tests__/archive.test.js +252 -0
- package/dist/session/retention/__tests__/archive.test.js.map +1 -0
- package/dist/session/retention/__tests__/disk-backend.test.d.ts +2 -0
- package/dist/session/retention/__tests__/disk-backend.test.d.ts.map +1 -0
- package/dist/session/retention/__tests__/disk-backend.test.js +154 -0
- package/dist/session/retention/__tests__/disk-backend.test.js.map +1 -0
- package/dist/session/retention/archive-backend-ref.d.ts +18 -0
- package/dist/session/retention/archive-backend-ref.d.ts.map +1 -0
- package/dist/session/retention/archive-backend-ref.js +2 -0
- package/dist/session/retention/archive-backend-ref.js.map +1 -0
- package/dist/session/retention/archive.d.ts +130 -0
- package/dist/session/retention/archive.d.ts.map +1 -0
- package/dist/session/retention/archive.js +203 -0
- package/dist/session/retention/archive.js.map +1 -0
- package/dist/session/retention/backend.d.ts +101 -0
- package/dist/session/retention/backend.d.ts.map +1 -0
- package/dist/session/retention/backend.js +15 -0
- package/dist/session/retention/backend.js.map +1 -0
- package/dist/session/retention/disk-backend.d.ts +59 -0
- package/dist/session/retention/disk-backend.d.ts.map +1 -0
- package/dist/session/retention/disk-backend.js +236 -0
- package/dist/session/retention/disk-backend.js.map +1 -0
- package/dist/session/retention/index.d.ts +9 -0
- package/dist/session/retention/index.d.ts.map +1 -0
- package/dist/session/retention/index.js +6 -0
- package/dist/session/retention/index.js.map +1 -0
- package/dist/session/retention/policy.d.ts +49 -0
- package/dist/session/retention/policy.d.ts.map +1 -0
- package/dist/session/retention/policy.js +21 -0
- package/dist/session/retention/policy.js.map +1 -0
- package/dist/session/summary/__tests__/materialize.test.d.ts +2 -0
- package/dist/session/summary/__tests__/materialize.test.d.ts.map +1 -0
- package/dist/session/summary/__tests__/materialize.test.js +269 -0
- package/dist/session/summary/__tests__/materialize.test.js.map +1 -0
- package/dist/session/summary/deliverable.d.ts +74 -0
- package/dist/session/summary/deliverable.d.ts.map +1 -0
- package/dist/session/summary/deliverable.js +20 -0
- package/dist/session/summary/deliverable.js.map +1 -0
- package/dist/session/summary/index.d.ts +6 -0
- package/dist/session/summary/index.d.ts.map +1 -0
- package/dist/session/summary/index.js +9 -0
- package/dist/session/summary/index.js.map +1 -0
- package/dist/session/summary/materialize.d.ts +82 -0
- package/dist/session/summary/materialize.d.ts.map +1 -0
- package/dist/session/summary/materialize.js +117 -0
- package/dist/session/summary/materialize.js.map +1 -0
- package/dist/session/summary/ref.d.ts +91 -0
- package/dist/session/summary/ref.d.ts.map +1 -0
- package/dist/session/summary/ref.js +51 -0
- package/dist/session/summary/ref.js.map +1 -0
- package/dist/session/workspace/__tests__/git-worktree.test.d.ts +2 -0
- package/dist/session/workspace/__tests__/git-worktree.test.d.ts.map +1 -0
- package/dist/session/workspace/__tests__/git-worktree.test.js +244 -0
- package/dist/session/workspace/__tests__/git-worktree.test.js.map +1 -0
- package/dist/session/workspace/__tests__/path-builder.test.d.ts +2 -0
- package/dist/session/workspace/__tests__/path-builder.test.d.ts.map +1 -0
- package/dist/session/workspace/__tests__/path-builder.test.js +37 -0
- package/dist/session/workspace/__tests__/path-builder.test.js.map +1 -0
- package/dist/session/workspace/driver.d.ts +55 -0
- package/dist/session/workspace/driver.d.ts.map +1 -0
- package/dist/session/workspace/driver.js +12 -0
- package/dist/session/workspace/driver.js.map +1 -0
- package/dist/session/workspace/git-worktree.d.ts +65 -0
- package/dist/session/workspace/git-worktree.d.ts.map +1 -0
- package/dist/session/workspace/git-worktree.js +156 -0
- package/dist/session/workspace/git-worktree.js.map +1 -0
- package/dist/session/workspace/index.d.ts +8 -0
- package/dist/session/workspace/index.d.ts.map +1 -0
- package/dist/session/workspace/index.js +7 -0
- package/dist/session/workspace/index.js.map +1 -0
- package/dist/session/workspace/path-builder.d.ts +50 -0
- package/dist/session/workspace/path-builder.d.ts.map +1 -0
- package/dist/session/workspace/path-builder.js +50 -0
- package/dist/session/workspace/path-builder.js.map +1 -0
- package/dist/session/workspace/ref.d.ts +46 -0
- package/dist/session/workspace/ref.d.ts.map +1 -0
- package/dist/session/workspace/ref.js +11 -0
- package/dist/session/workspace/ref.js.map +1 -0
- package/dist/session/workspace/registry.d.ts +26 -0
- package/dist/session/workspace/registry.d.ts.map +1 -0
- package/dist/session/workspace/registry.js +35 -0
- package/dist/session/workspace/registry.js.map +1 -0
- package/dist/store/conversation/memory.d.ts +22 -0
- package/dist/store/conversation/memory.d.ts.map +1 -1
- package/dist/store/conversation/memory.js +22 -0
- package/dist/store/conversation/memory.js.map +1 -1
- package/dist/store/session/__tests__/disk.test.d.ts +2 -0
- package/dist/store/session/__tests__/disk.test.d.ts.map +1 -0
- package/dist/store/session/__tests__/disk.test.js +240 -0
- package/dist/store/session/__tests__/disk.test.js.map +1 -0
- package/dist/store/session/__tests__/memory.test.d.ts +2 -0
- package/dist/store/session/__tests__/memory.test.d.ts.map +1 -0
- package/dist/store/session/__tests__/memory.test.js +217 -0
- package/dist/store/session/__tests__/memory.test.js.map +1 -0
- package/dist/store/session/disk.d.ts +85 -0
- package/dist/store/session/disk.d.ts.map +1 -0
- package/dist/store/session/disk.js +757 -0
- package/dist/store/session/disk.js.map +1 -0
- package/dist/store/session/index.d.ts +7 -0
- package/dist/store/session/index.d.ts.map +1 -0
- package/dist/store/session/index.js +11 -0
- package/dist/store/session/index.js.map +1 -0
- package/dist/store/session/linkage.d.ts +38 -0
- package/dist/store/session/linkage.d.ts.map +1 -0
- package/dist/store/session/linkage.js +64 -0
- package/dist/store/session/linkage.js.map +1 -0
- package/dist/store/session/memory.d.ts +48 -0
- package/dist/store/session/memory.d.ts.map +1 -0
- package/dist/store/session/memory.js +322 -0
- package/dist/store/session/memory.js.map +1 -0
- package/dist/store/session/messages.d.ts +20 -0
- package/dist/store/session/messages.d.ts.map +1 -0
- package/dist/store/session/messages.js +12 -0
- package/dist/store/session/messages.js.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +1 -1
- package/dist/types/agent/base.d.ts +28 -1
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +50 -2
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/conversation/index.d.ts +7 -0
- package/dist/types/conversation/index.d.ts.map +1 -1
- package/dist/types/ids/index.d.ts +26 -3
- package/dist/types/ids/index.d.ts.map +1 -1
- package/dist/types/ids/index.js +8 -1
- package/dist/types/ids/index.js.map +1 -1
- package/dist/types/invocation/__tests__/state.test.js +36 -29
- package/dist/types/invocation/__tests__/state.test.js.map +1 -1
- package/dist/types/invocation/index.d.ts +20 -4
- package/dist/types/invocation/index.d.ts.map +1 -1
- package/dist/types/invocation/index.js +10 -7
- package/dist/types/invocation/index.js.map +1 -1
- package/dist/types/run/config.d.ts +11 -1
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +26 -1
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +8 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/metadata.d.ts +24 -1
- package/dist/types/run/metadata.d.ts.map +1 -1
- package/dist/types/run/status.d.ts +26 -0
- package/dist/types/run/status.d.ts.map +1 -0
- package/dist/types/run/status.js +2 -0
- package/dist/types/run/status.js.map +1 -0
- package/dist/types/session/ids.d.ts +18 -0
- package/dist/types/session/ids.d.ts.map +1 -0
- package/dist/types/session/ids.js +12 -0
- package/dist/types/session/ids.js.map +1 -0
- package/dist/types/session/index.d.ts +3 -0
- package/dist/types/session/index.d.ts.map +1 -0
- package/dist/types/session/index.js +5 -0
- package/dist/types/session/index.js.map +1 -0
- package/dist/types/session/store.d.ts +188 -0
- package/dist/types/session/store.d.ts.map +1 -0
- package/dist/types/session/store.js +14 -0
- package/dist/types/session/store.js.map +1 -0
- package/dist/utils/id.d.ts +18 -1
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +42 -4
- package/dist/utils/id.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/ReactiveAgent.ts +7 -3
- package/src/agents/RouterAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +26 -6
- package/src/bridge/a2a/mapper.ts +7 -0
- package/src/bridge/a2a/task.ts +2 -2
- package/src/bridge/sse/mapper.ts +8 -1
- package/src/constants/a2a/index.ts +2 -2
- package/src/contracts/api.ts +23 -3
- package/src/contracts/index.ts +2 -0
- package/src/gateway/local.ts +6 -0
- package/src/index.ts +14 -0
- package/src/manager/agent/__tests__/lifecycle.test.ts +452 -0
- package/src/manager/agent/lifecycle.ts +434 -19
- package/src/manager/run/persistence.ts +20 -1
- package/src/run/reporter.ts +28 -0
- package/src/runtime/query/__tests__/context.test.ts +101 -0
- package/src/runtime/query/context.ts +106 -10
- package/src/runtime/query/events.ts +8 -0
- package/src/runtime/query/index.ts +41 -3
- package/src/session/__tests__/integration/_fixtures.ts +282 -0
- package/src/session/__tests__/integration/capacity-caps.test.ts +164 -0
- package/src/session/__tests__/integration/e2e-spawn.test.ts +278 -0
- package/src/session/__tests__/integration/event-stream-ordering.test.ts +403 -0
- package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +245 -0
- package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +179 -0
- package/src/session/__tests__/integration/handoff-single-e2e.test.ts +220 -0
- package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +237 -0
- package/src/session/__tests__/integration/migration-filesystem.test.ts +209 -0
- package/src/session/__tests__/integration/migration-id-prefix.test.ts +101 -0
- package/src/session/__tests__/integration/prev-artifact-dag.test.ts +318 -0
- package/src/session/__tests__/integration/retention-archive.test.ts +231 -0
- package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +237 -0
- package/src/session/__tests__/integration/tenant-isolation.test.ts +282 -0
- package/src/session/errors.ts +70 -0
- package/src/session/events/index.ts +16 -0
- package/src/session/events/schema-version.ts +13 -0
- package/src/session/events/types.ts +71 -0
- package/src/session/handoff/__tests__/broadcast.test.ts +350 -0
- package/src/session/handoff/__tests__/capacity.test.ts +123 -0
- package/src/session/handoff/__tests__/single.test.ts +316 -0
- package/src/session/handoff/assignment.ts +62 -0
- package/src/session/handoff/broadcast.ts +381 -0
- package/src/session/handoff/capacity.ts +121 -0
- package/src/session/handoff/events.ts +72 -0
- package/src/session/handoff/index.ts +29 -0
- package/src/session/handoff/single.ts +288 -0
- package/src/session/handoff/version.ts +59 -0
- package/src/session/hierarchy/__tests__/session.test.ts +92 -0
- package/src/session/hierarchy/actor.ts +17 -0
- package/src/session/hierarchy/index.ts +17 -0
- package/src/session/hierarchy/lineage.ts +15 -0
- package/src/session/hierarchy/project.ts +41 -0
- package/src/session/hierarchy/session.ts +97 -0
- package/src/session/hierarchy/sub-session.ts +92 -0
- package/src/session/hierarchy/tenant.ts +13 -0
- package/src/session/index.ts +15 -0
- package/src/session/intervention/__tests__/prev-artifact.test.ts +234 -0
- package/src/session/intervention/index.ts +16 -0
- package/src/session/intervention/prev-artifact.ts +180 -0
- package/src/session/migration/__tests__/filesystem.test.ts +263 -0
- package/src/session/migration/__tests__/id-prefix.test.ts +101 -0
- package/src/session/migration/__tests__/marker.test.ts +84 -0
- package/src/session/migration/errors.ts +23 -0
- package/src/session/migration/filesystem.ts +401 -0
- package/src/session/migration/id-prefix.ts +146 -0
- package/src/session/migration/index.ts +38 -0
- package/src/session/migration/marker.ts +131 -0
- package/src/session/retention/__tests__/archive.test.ts +316 -0
- package/src/session/retention/__tests__/disk-backend.test.ts +180 -0
- package/src/session/retention/archive-backend-ref.ts +17 -0
- package/src/session/retention/archive.ts +281 -0
- package/src/session/retention/backend.ts +107 -0
- package/src/session/retention/disk-backend.ts +304 -0
- package/src/session/retention/index.ts +16 -0
- package/src/session/retention/policy.ts +53 -0
- package/src/session/summary/__tests__/materialize.test.ts +341 -0
- package/src/session/summary/deliverable.ts +84 -0
- package/src/session/summary/index.ts +31 -0
- package/src/session/summary/materialize.ts +169 -0
- package/src/session/summary/ref.ts +104 -0
- package/src/session/workspace/__tests__/git-worktree.test.ts +258 -0
- package/src/session/workspace/__tests__/path-builder.test.ts +51 -0
- package/src/session/workspace/driver.ts +60 -0
- package/src/session/workspace/git-worktree.ts +209 -0
- package/src/session/workspace/index.ts +25 -0
- package/src/session/workspace/path-builder.ts +71 -0
- package/src/session/workspace/ref.ts +50 -0
- package/src/session/workspace/registry.ts +42 -0
- package/src/store/conversation/memory.ts +23 -0
- package/src/store/session/__tests__/disk.test.ts +346 -0
- package/src/store/session/__tests__/memory.test.ts +327 -0
- package/src/store/session/disk.ts +920 -0
- package/src/store/session/index.ts +14 -0
- package/src/store/session/linkage.ts +80 -0
- package/src/store/session/memory.ts +400 -0
- package/src/store/session/messages.ts +21 -0
- package/src/types/agent/base.ts +31 -1
- package/src/types/agent/task.ts +58 -2
- package/src/types/conversation/index.ts +7 -0
- package/src/types/ids/index.ts +41 -3
- package/src/types/invocation/__tests__/state.test.ts +37 -29
- package/src/types/invocation/index.ts +26 -10
- package/src/types/run/config.ts +12 -1
- package/src/types/run/events.ts +36 -1
- package/src/types/run/index.ts +8 -0
- package/src/types/run/metadata.ts +24 -1
- package/src/types/run/status.ts +33 -0
- package/src/types/session/ids.ts +34 -0
- package/src/types/session/index.ts +28 -0
- package/src/types/session/store.ts +229 -0
- package/src/utils/id.ts +55 -4
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration — `active → locked` illegal-transition rejection matrix per
|
|
3
|
+
* pattern doc §5.1.
|
|
4
|
+
*
|
|
5
|
+
* A handoff request against a session whose current Run is `running`,
|
|
6
|
+
* `awaiting_hitl`, `awaiting_hitl_resolution`, or `awaiting_subsession`
|
|
7
|
+
* rejects with typed {@link HandoffLockRejected}. The reason enum maps:
|
|
8
|
+
* - running → 'active_run'
|
|
9
|
+
* - awaiting_hitl → 'pending_hitl'
|
|
10
|
+
* - awaiting_hitl_resol. → 'pending_hitl'
|
|
11
|
+
* - awaiting_subsession → 'pending_subsession'
|
|
12
|
+
*
|
|
13
|
+
* This integration test wires the `RunStatusResolver` seam into a full
|
|
14
|
+
* single-handoff flow so the rejection triggers from the run-fan-in check
|
|
15
|
+
* (§5.1), not just from a status precondition.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { describe, expect, it } from 'vitest'
|
|
19
|
+
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
20
|
+
import { generateHandoffId } from '../../../utils/id.js'
|
|
21
|
+
import type { HandoffAssignment } from '../../handoff/assignment.js'
|
|
22
|
+
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
23
|
+
import type { HandoffEventSink } from '../../handoff/events.js'
|
|
24
|
+
import {
|
|
25
|
+
type RunStatusResolver,
|
|
26
|
+
type SingleHandoffDeps,
|
|
27
|
+
executeSingleHandoff,
|
|
28
|
+
} from '../../handoff/single.js'
|
|
29
|
+
import { HandoffLockRejected } from '../../handoff/version.js'
|
|
30
|
+
import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
|
|
31
|
+
import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
|
|
32
|
+
import { DEFAULT_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
|
|
33
|
+
|
|
34
|
+
function buildDeps(store: InMemorySessionStore, runStatus?: RunStatusResolver): SingleHandoffDeps {
|
|
35
|
+
const driver = new GitWorktreeDriver({
|
|
36
|
+
repoRoot: '/repo',
|
|
37
|
+
logger: stubLogger(),
|
|
38
|
+
execFile: async () => okExec(),
|
|
39
|
+
})
|
|
40
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
41
|
+
workspaceRegistry.register(driver)
|
|
42
|
+
|
|
43
|
+
const events: HandoffEventSink = {}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
store,
|
|
47
|
+
workspaceRegistry,
|
|
48
|
+
capacity: new DefaultCapacityValidator(store),
|
|
49
|
+
events,
|
|
50
|
+
...(runStatus !== undefined && { runStatus }),
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function seedIdleSession(store: InMemorySessionStore) {
|
|
55
|
+
const project = await store.createProject(
|
|
56
|
+
{ tenantId: DEFAULT_TENANT, name: 'illegal' },
|
|
57
|
+
DEFAULT_TENANT,
|
|
58
|
+
)
|
|
59
|
+
const session = await store.createSession(
|
|
60
|
+
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
61
|
+
DEFAULT_TENANT,
|
|
62
|
+
)
|
|
63
|
+
return { project, session }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildAssignment(
|
|
67
|
+
sourceSessionId: Awaited<ReturnType<InMemorySessionStore['createSession']>>['id'],
|
|
68
|
+
projectId: Awaited<ReturnType<InMemorySessionStore['createProject']>>['id'],
|
|
69
|
+
): HandoffAssignment {
|
|
70
|
+
return {
|
|
71
|
+
id: generateHandoffId(),
|
|
72
|
+
mode: 'single',
|
|
73
|
+
sourceSessionId,
|
|
74
|
+
tenantId: DEFAULT_TENANT,
|
|
75
|
+
projectId,
|
|
76
|
+
sourceActor: userActor('usr_source'),
|
|
77
|
+
recipientActor: userActor('usr_target'),
|
|
78
|
+
expectedOwnerVersion: 0,
|
|
79
|
+
createdAt: new Date('2026-04-17'),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe('Integration — illegal handoff transitions (§5.1)', () => {
|
|
84
|
+
it('running Run → HandoffLockRejected { reason: active_run }', async () => {
|
|
85
|
+
const store = new InMemorySessionStore()
|
|
86
|
+
const { project, session } = await seedIdleSession(store)
|
|
87
|
+
const deps = buildDeps(store, {
|
|
88
|
+
async blockingRun() {
|
|
89
|
+
return { reason: 'active_run' }
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
const assignment = buildAssignment(session.id, project.id)
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
|
|
96
|
+
expect.fail('expected HandoffLockRejected')
|
|
97
|
+
} catch (err) {
|
|
98
|
+
expect(err).toBeInstanceOf(HandoffLockRejected)
|
|
99
|
+
expect((err as HandoffLockRejected).details.reason).toBe('active_run')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Source never locked — still idle with original version.
|
|
103
|
+
const reloaded = await store.getSession(session.id, DEFAULT_TENANT)
|
|
104
|
+
expect(reloaded?.status).toBe('idle')
|
|
105
|
+
expect(reloaded?.ownerVersion).toBe(0)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('awaiting_hitl → HandoffLockRejected { reason: pending_hitl }', async () => {
|
|
109
|
+
const store = new InMemorySessionStore()
|
|
110
|
+
const { project, session } = await seedIdleSession(store)
|
|
111
|
+
const deps = buildDeps(store, {
|
|
112
|
+
async blockingRun() {
|
|
113
|
+
return { reason: 'pending_hitl' }
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT)
|
|
119
|
+
expect.fail('expected HandoffLockRejected')
|
|
120
|
+
} catch (err) {
|
|
121
|
+
expect(err).toBeInstanceOf(HandoffLockRejected)
|
|
122
|
+
expect((err as HandoffLockRejected).details.reason).toBe('pending_hitl')
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('awaiting_hitl_resolution collapses into pending_hitl reason (timeout variant)', async () => {
|
|
127
|
+
// §5.2 treats `awaiting_hitl_resolution` as the same non-terminal class
|
|
128
|
+
// that forces the source into `awaiting_hitl` — the resolver surfaces
|
|
129
|
+
// `pending_hitl` for both. This keeps the lock-rejection enum
|
|
130
|
+
// conservative (no new reason variant for a sub-state).
|
|
131
|
+
const store = new InMemorySessionStore()
|
|
132
|
+
const { project, session } = await seedIdleSession(store)
|
|
133
|
+
const deps = buildDeps(store, {
|
|
134
|
+
async blockingRun() {
|
|
135
|
+
return { reason: 'pending_hitl' }
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
await expect(
|
|
140
|
+
executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT),
|
|
141
|
+
).rejects.toBeInstanceOf(HandoffLockRejected)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('awaiting_subsession → HandoffLockRejected { reason: pending_subsession }', async () => {
|
|
145
|
+
const store = new InMemorySessionStore()
|
|
146
|
+
const { project, session } = await seedIdleSession(store)
|
|
147
|
+
const deps = buildDeps(store, {
|
|
148
|
+
async blockingRun() {
|
|
149
|
+
return { reason: 'pending_subsession' }
|
|
150
|
+
},
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
try {
|
|
154
|
+
await executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT)
|
|
155
|
+
expect.fail('expected HandoffLockRejected')
|
|
156
|
+
} catch (err) {
|
|
157
|
+
expect(err).toBeInstanceOf(HandoffLockRejected)
|
|
158
|
+
expect((err as HandoffLockRejected).details.reason).toBe('pending_subsession')
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('session status precondition: active-status session rejects before resolver fires', async () => {
|
|
163
|
+
// When the session itself is already non-idle (e.g. `active`), the lock
|
|
164
|
+
// rejection fires from the status check — no RunStatusResolver invoked.
|
|
165
|
+
const store = new InMemorySessionStore()
|
|
166
|
+
const { project, session } = await seedIdleSession(store)
|
|
167
|
+
await store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
|
|
168
|
+
|
|
169
|
+
const deps = buildDeps(store, {
|
|
170
|
+
async blockingRun() {
|
|
171
|
+
return null // resolver would allow, but status guard trips first
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
await expect(
|
|
176
|
+
executeSingleHandoff(deps, buildAssignment(session.id, project.id), DEFAULT_TENANT),
|
|
177
|
+
).rejects.toBeInstanceOf(HandoffLockRejected)
|
|
178
|
+
})
|
|
179
|
+
})
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration — single-recipient handoff wired through the full stack.
|
|
3
|
+
*
|
|
4
|
+
* Orthogonal to `handoff/__tests__/single.test.ts` (unit-level): this
|
|
5
|
+
* exercises the real `InMemorySessionStore` + real `GitWorktreeDriver` (with
|
|
6
|
+
* stubbed `execFile`) + real {@link DefaultCapacityValidator} so the commit
|
|
7
|
+
* ordering + workspace provisioning side-effects are verifiable from the
|
|
8
|
+
* outside.
|
|
9
|
+
*
|
|
10
|
+
* Covers roadmap §5 invariants: §4.8 (handoff has no accepted field — commit
|
|
11
|
+
* is atomic), §6.1 (single-recipient flow: lock → commit), §5.1 (source
|
|
12
|
+
* transitions back to idle with bumped ownerVersion + appended previousActors).
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
16
|
+
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
17
|
+
import type { TenantId } from '../../../types/ids/index.js'
|
|
18
|
+
import { generateHandoffId } from '../../../utils/id.js'
|
|
19
|
+
import { TenantIsolationError } from '../../errors.js'
|
|
20
|
+
import type { HandoffAssignment } from '../../handoff/assignment.js'
|
|
21
|
+
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
22
|
+
import type { HandoffEventSink } from '../../handoff/events.js'
|
|
23
|
+
import { type SingleHandoffDeps, executeSingleHandoff } from '../../handoff/single.js'
|
|
24
|
+
import type { Session } from '../../hierarchy/session.js'
|
|
25
|
+
import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
|
|
26
|
+
import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
|
|
27
|
+
import { DEFAULT_TENANT, OTHER_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
|
|
28
|
+
|
|
29
|
+
function buildHandoffDeps(store: InMemorySessionStore): {
|
|
30
|
+
deps: SingleHandoffDeps
|
|
31
|
+
updateCalls: Array<{ status?: string; ownerVersion?: number }>
|
|
32
|
+
} {
|
|
33
|
+
const driver = new GitWorktreeDriver({
|
|
34
|
+
repoRoot: '/repo',
|
|
35
|
+
logger: stubLogger(),
|
|
36
|
+
execFile: async () => okExec(),
|
|
37
|
+
})
|
|
38
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
39
|
+
workspaceRegistry.register(driver)
|
|
40
|
+
|
|
41
|
+
const sink: HandoffEventSink = {
|
|
42
|
+
onLocked: vi.fn(),
|
|
43
|
+
onUnlocked: vi.fn(),
|
|
44
|
+
onCommitted: vi.fn(),
|
|
45
|
+
onBroadcastRollback: vi.fn(),
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const updateCalls: Array<{ status?: string; ownerVersion?: number }> = []
|
|
49
|
+
const originalUpdate = store.updateSession.bind(store)
|
|
50
|
+
store.updateSession = async (session: Session, tenantId) => {
|
|
51
|
+
updateCalls.push({ status: session.status, ownerVersion: session.ownerVersion })
|
|
52
|
+
return originalUpdate(session, tenantId)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
deps: {
|
|
57
|
+
store,
|
|
58
|
+
workspaceRegistry,
|
|
59
|
+
capacity: new DefaultCapacityValidator(store),
|
|
60
|
+
events: sink,
|
|
61
|
+
},
|
|
62
|
+
updateCalls,
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
describe('Integration — single-recipient handoff E2E', () => {
|
|
67
|
+
it('idle → locked → commit: source previousActors grew + ownerVersion bumped atomically', async () => {
|
|
68
|
+
const store = new InMemorySessionStore()
|
|
69
|
+
const project = await store.createProject(
|
|
70
|
+
{ tenantId: DEFAULT_TENANT, name: 'ho' },
|
|
71
|
+
DEFAULT_TENANT,
|
|
72
|
+
)
|
|
73
|
+
const sourceActor = userActor('usr_source')
|
|
74
|
+
const recipientActor = userActor('usr_target')
|
|
75
|
+
const session = await store.createSession(
|
|
76
|
+
{ projectId: project.id, currentActor: sourceActor },
|
|
77
|
+
DEFAULT_TENANT,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
const { deps, updateCalls } = buildHandoffDeps(store)
|
|
81
|
+
const assignment: HandoffAssignment = {
|
|
82
|
+
id: generateHandoffId(),
|
|
83
|
+
mode: 'single',
|
|
84
|
+
sourceSessionId: session.id,
|
|
85
|
+
tenantId: DEFAULT_TENANT,
|
|
86
|
+
projectId: project.id,
|
|
87
|
+
sourceActor,
|
|
88
|
+
recipientActor,
|
|
89
|
+
expectedOwnerVersion: 0,
|
|
90
|
+
createdAt: new Date('2026-04-17'),
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
|
|
94
|
+
|
|
95
|
+
expect(outcome.committedOwnerVersion).toBe(1)
|
|
96
|
+
|
|
97
|
+
const reloaded = await store.getSession(session.id, DEFAULT_TENANT)
|
|
98
|
+
expect(reloaded?.status).toBe('idle')
|
|
99
|
+
expect(reloaded?.ownerVersion).toBe(1)
|
|
100
|
+
expect(reloaded?.currentActor).toEqual(recipientActor)
|
|
101
|
+
expect(reloaded?.previousActors).toHaveLength(1)
|
|
102
|
+
expect(reloaded?.previousActors[0]).toEqual(sourceActor)
|
|
103
|
+
|
|
104
|
+
// Wired assertion: updateSession was invoked at least with the lock
|
|
105
|
+
// transition + the final commit. That sequence is what makes the
|
|
106
|
+
// handoff "atomic at the store layer".
|
|
107
|
+
// Expect at minimum locked (v0) followed by committed (v1).
|
|
108
|
+
expect(updateCalls).toEqual(
|
|
109
|
+
expect.arrayContaining([
|
|
110
|
+
{ status: 'locked', ownerVersion: 0 },
|
|
111
|
+
{ status: 'idle', ownerVersion: 1 },
|
|
112
|
+
]),
|
|
113
|
+
)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
it('cross-tenant assignment rejects at entry (TenantIsolationError)', async () => {
|
|
117
|
+
const store = new InMemorySessionStore()
|
|
118
|
+
const project = await store.createProject(
|
|
119
|
+
{ tenantId: DEFAULT_TENANT, name: 'ct' },
|
|
120
|
+
DEFAULT_TENANT,
|
|
121
|
+
)
|
|
122
|
+
const session = await store.createSession(
|
|
123
|
+
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
124
|
+
DEFAULT_TENANT,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
const { deps } = buildHandoffDeps(store)
|
|
128
|
+
const assignment: HandoffAssignment = {
|
|
129
|
+
id: generateHandoffId(),
|
|
130
|
+
mode: 'single',
|
|
131
|
+
sourceSessionId: session.id,
|
|
132
|
+
tenantId: OTHER_TENANT,
|
|
133
|
+
projectId: project.id,
|
|
134
|
+
sourceActor: userActor('usr_source', OTHER_TENANT),
|
|
135
|
+
recipientActor: userActor('usr_target', OTHER_TENANT),
|
|
136
|
+
expectedOwnerVersion: 0,
|
|
137
|
+
createdAt: new Date('2026-04-17'),
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
await expect(executeSingleHandoff(deps, assignment, OTHER_TENANT)).rejects.toBeInstanceOf(
|
|
141
|
+
TenantIsolationError,
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('source-owned workspace provisioned for recipient', async () => {
|
|
146
|
+
const store = new InMemorySessionStore()
|
|
147
|
+
const project = await store.createProject(
|
|
148
|
+
{ tenantId: DEFAULT_TENANT, name: 'wsp' },
|
|
149
|
+
DEFAULT_TENANT,
|
|
150
|
+
)
|
|
151
|
+
const source = await store.createSession(
|
|
152
|
+
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
153
|
+
DEFAULT_TENANT,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
const { deps } = buildHandoffDeps(store)
|
|
157
|
+
const assignment: HandoffAssignment = {
|
|
158
|
+
id: generateHandoffId(),
|
|
159
|
+
mode: 'single',
|
|
160
|
+
sourceSessionId: source.id,
|
|
161
|
+
tenantId: DEFAULT_TENANT,
|
|
162
|
+
projectId: project.id,
|
|
163
|
+
sourceActor: userActor('usr_source'),
|
|
164
|
+
recipientActor: userActor('usr_target'),
|
|
165
|
+
expectedOwnerVersion: 0,
|
|
166
|
+
createdAt: new Date('2026-04-17'),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
|
|
170
|
+
|
|
171
|
+
expect(outcome.workspaceId.startsWith('wsp_')).toBe(true)
|
|
172
|
+
expect(outcome.newSessionId.startsWith('ses_')).toBe(true)
|
|
173
|
+
|
|
174
|
+
// Recipient session exists under the same tenant/project.
|
|
175
|
+
const recipient = await store.getSession(outcome.newSessionId, DEFAULT_TENANT)
|
|
176
|
+
expect(recipient).not.toBeNull()
|
|
177
|
+
expect(recipient?.projectId).toBe(project.id)
|
|
178
|
+
expect(recipient?.tenantId).toBe(DEFAULT_TENANT)
|
|
179
|
+
expect(recipient?.currentActor).toEqual(userActor('usr_target'))
|
|
180
|
+
|
|
181
|
+
// A sub-session edge links the source to the recipient.
|
|
182
|
+
const children = await store.getChildren(source.id, DEFAULT_TENANT)
|
|
183
|
+
expect(children).toHaveLength(1)
|
|
184
|
+
expect(children[0]?.kind).toBe('user_handoff')
|
|
185
|
+
expect(children[0]?.childSessionId).toBe(outcome.newSessionId)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
// Tenant-denormalization-on-record assertion — every persisted entity
|
|
189
|
+
// carries the tenantId explicitly, which matters for §12 isolation.
|
|
190
|
+
it('denormalized tenantId stamped on Session + SubSession records', async () => {
|
|
191
|
+
const _tenantType: TenantId = DEFAULT_TENANT
|
|
192
|
+
const store = new InMemorySessionStore()
|
|
193
|
+
const project = await store.createProject(
|
|
194
|
+
{ tenantId: DEFAULT_TENANT, name: 'denorm' },
|
|
195
|
+
DEFAULT_TENANT,
|
|
196
|
+
)
|
|
197
|
+
const source = await store.createSession(
|
|
198
|
+
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
199
|
+
DEFAULT_TENANT,
|
|
200
|
+
)
|
|
201
|
+
const { deps } = buildHandoffDeps(store)
|
|
202
|
+
|
|
203
|
+
const assignment: HandoffAssignment = {
|
|
204
|
+
id: generateHandoffId(),
|
|
205
|
+
mode: 'single',
|
|
206
|
+
sourceSessionId: source.id,
|
|
207
|
+
tenantId: DEFAULT_TENANT,
|
|
208
|
+
projectId: project.id,
|
|
209
|
+
sourceActor: userActor('usr_source'),
|
|
210
|
+
recipientActor: userActor('usr_target'),
|
|
211
|
+
expectedOwnerVersion: 0,
|
|
212
|
+
createdAt: new Date(),
|
|
213
|
+
}
|
|
214
|
+
const outcome = await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
|
|
215
|
+
|
|
216
|
+
// Recipient's Session stores tenantId explicitly (not inferred).
|
|
217
|
+
const recipient = await store.getSession(outcome.newSessionId, DEFAULT_TENANT)
|
|
218
|
+
expect(recipient?.tenantId).toBe(_tenantType)
|
|
219
|
+
})
|
|
220
|
+
})
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration — Tenant → Project → Session → SubSession → Run hierarchy
|
|
3
|
+
* lifecycle against a real {@link InMemorySessionStore}.
|
|
4
|
+
*
|
|
5
|
+
* Covers roadmap §5 invariants §4 (branded IDs), §4.3 (currentActor
|
|
6
|
+
* immutability), §4.4 (sub-session status fan-in to drill), plus the
|
|
7
|
+
* `drill()` navigation primitive (§14.3). Orthogonal to `e2e-spawn.test.ts`
|
|
8
|
+
* (which exercises the full AgentManager spawn path); this file asserts the
|
|
9
|
+
* raw store contract under direct construction.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { describe, expect, it } from 'vitest'
|
|
13
|
+
import { TenantIsolationError } from '../../errors.js'
|
|
14
|
+
import { DEFAULT_TENANT, agentActor, buildHarness, userActor } from './_fixtures.js'
|
|
15
|
+
|
|
16
|
+
describe('Integration — hierarchy lifecycle', () => {
|
|
17
|
+
it('creates Tenant → Project → Session → SubSession with properly branded IDs', async () => {
|
|
18
|
+
const { store } = buildHarness()
|
|
19
|
+
const tenant = DEFAULT_TENANT
|
|
20
|
+
|
|
21
|
+
const project = await store.createProject({ tenantId: tenant, name: 'p1' }, tenant)
|
|
22
|
+
expect(project.id.startsWith('prj_')).toBe(true)
|
|
23
|
+
expect(project.tenantId.startsWith('tnt_')).toBe(true)
|
|
24
|
+
|
|
25
|
+
const session = await store.createSession(
|
|
26
|
+
{ projectId: project.id, currentActor: userActor('usr_a') },
|
|
27
|
+
tenant,
|
|
28
|
+
)
|
|
29
|
+
expect(session.id.startsWith('ses_')).toBe(true)
|
|
30
|
+
expect(session.projectId).toBe(project.id)
|
|
31
|
+
expect(session.tenantId).toBe(tenant)
|
|
32
|
+
expect(session.status).toBe('idle')
|
|
33
|
+
expect(session.ownerVersion).toBe(0)
|
|
34
|
+
expect(session.previousActors).toEqual([])
|
|
35
|
+
|
|
36
|
+
const childSession = await store.createSession(
|
|
37
|
+
{ projectId: project.id, currentActor: agentActor('agt_worker') },
|
|
38
|
+
tenant,
|
|
39
|
+
)
|
|
40
|
+
const subSession = await store.createSubSession(
|
|
41
|
+
{
|
|
42
|
+
parentSessionId: session.id,
|
|
43
|
+
childSessionId: childSession.id,
|
|
44
|
+
kind: 'agent_spawn',
|
|
45
|
+
spawnedBy: userActor('usr_a'),
|
|
46
|
+
},
|
|
47
|
+
tenant,
|
|
48
|
+
)
|
|
49
|
+
expect(subSession.id.startsWith('sub_')).toBe(true)
|
|
50
|
+
expect(subSession.parentSessionId).toBe(session.id)
|
|
51
|
+
expect(subSession.childSessionId).toBe(childSession.id)
|
|
52
|
+
expect(subSession.kind).toBe('agent_spawn')
|
|
53
|
+
expect(subSession.status).toBe('pending')
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('drill(parentSessionId) returns a SessionView with children[] and ancestry[]', async () => {
|
|
57
|
+
const { store } = buildHarness()
|
|
58
|
+
const tenant = DEFAULT_TENANT
|
|
59
|
+
|
|
60
|
+
const project = await store.createProject({ tenantId: tenant, name: 'drill' }, tenant)
|
|
61
|
+
const parent = await store.createSession(
|
|
62
|
+
{ projectId: project.id, currentActor: userActor('usr_root') },
|
|
63
|
+
tenant,
|
|
64
|
+
)
|
|
65
|
+
const childA = await store.createSession(
|
|
66
|
+
{ projectId: project.id, currentActor: agentActor('agt_a') },
|
|
67
|
+
tenant,
|
|
68
|
+
)
|
|
69
|
+
const childB = await store.createSession(
|
|
70
|
+
{ projectId: project.id, currentActor: agentActor('agt_b') },
|
|
71
|
+
tenant,
|
|
72
|
+
)
|
|
73
|
+
await store.createSubSession(
|
|
74
|
+
{
|
|
75
|
+
parentSessionId: parent.id,
|
|
76
|
+
childSessionId: childA.id,
|
|
77
|
+
kind: 'agent_spawn',
|
|
78
|
+
spawnedBy: userActor('usr_root'),
|
|
79
|
+
},
|
|
80
|
+
tenant,
|
|
81
|
+
)
|
|
82
|
+
await store.createSubSession(
|
|
83
|
+
{
|
|
84
|
+
parentSessionId: parent.id,
|
|
85
|
+
childSessionId: childB.id,
|
|
86
|
+
kind: 'agent_spawn',
|
|
87
|
+
spawnedBy: userActor('usr_root'),
|
|
88
|
+
},
|
|
89
|
+
tenant,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
const parentView = await store.drill(parent.id, tenant)
|
|
93
|
+
expect(parentView).not.toBeNull()
|
|
94
|
+
expect(parentView?.session.id).toBe(parent.id)
|
|
95
|
+
expect(parentView?.children).toHaveLength(2)
|
|
96
|
+
expect(parentView?.ancestry).toEqual([parent.id])
|
|
97
|
+
|
|
98
|
+
const childAView = await store.drill(childA.id, tenant)
|
|
99
|
+
expect(childAView).not.toBeNull()
|
|
100
|
+
expect(childAView?.ancestry).toEqual([parent.id, childA.id])
|
|
101
|
+
expect(childAView?.children).toHaveLength(0)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('drill returns null for unknown session (deny-by-default)', async () => {
|
|
105
|
+
const { store } = buildHarness()
|
|
106
|
+
const view = await store.drill(
|
|
107
|
+
'ses_missing' as Parameters<typeof store.drill>[0],
|
|
108
|
+
DEFAULT_TENANT,
|
|
109
|
+
)
|
|
110
|
+
expect(view).toBeNull()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('§4.3 currentActor immutable previousActors — append-only on handoff', async () => {
|
|
114
|
+
const { store } = buildHarness()
|
|
115
|
+
const tenant = DEFAULT_TENANT
|
|
116
|
+
|
|
117
|
+
const project = await store.createProject({ tenantId: tenant, name: 'actors' }, tenant)
|
|
118
|
+
const userA = userActor('usr_a')
|
|
119
|
+
const userB = userActor('usr_b')
|
|
120
|
+
const userC = userActor('usr_c')
|
|
121
|
+
|
|
122
|
+
const session = await store.createSession(
|
|
123
|
+
{ projectId: project.id, currentActor: userA },
|
|
124
|
+
tenant,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// Simulate two successive handoff commits — each pushes the old actor
|
|
128
|
+
// onto previousActors and increments ownerVersion.
|
|
129
|
+
const firstHandoff = {
|
|
130
|
+
...session,
|
|
131
|
+
currentActor: userB,
|
|
132
|
+
previousActors: [userA],
|
|
133
|
+
ownerVersion: 1,
|
|
134
|
+
}
|
|
135
|
+
await store.updateSession(firstHandoff, tenant)
|
|
136
|
+
|
|
137
|
+
const secondHandoff = {
|
|
138
|
+
...firstHandoff,
|
|
139
|
+
currentActor: userC,
|
|
140
|
+
previousActors: [...firstHandoff.previousActors, userB],
|
|
141
|
+
ownerVersion: 2,
|
|
142
|
+
}
|
|
143
|
+
await store.updateSession(secondHandoff, tenant)
|
|
144
|
+
|
|
145
|
+
const reloaded = await store.getSession(session.id, tenant)
|
|
146
|
+
expect(reloaded?.currentActor).toEqual(userC)
|
|
147
|
+
expect(reloaded?.previousActors).toEqual([userA, userB])
|
|
148
|
+
expect(reloaded?.ownerVersion).toBe(2)
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
it('cycle guard via AncestryCycleError: ancestry walk detects corrupted parent linkage', async () => {
|
|
152
|
+
const { store } = buildHarness()
|
|
153
|
+
const tenant = DEFAULT_TENANT
|
|
154
|
+
|
|
155
|
+
const project = await store.createProject({ tenantId: tenant, name: 'cycle' }, tenant)
|
|
156
|
+
const sA = await store.createSession(
|
|
157
|
+
{ projectId: project.id, currentActor: userActor('usr_a') },
|
|
158
|
+
tenant,
|
|
159
|
+
)
|
|
160
|
+
const sB = await store.createSession(
|
|
161
|
+
{ projectId: project.id, currentActor: userActor('usr_b') },
|
|
162
|
+
tenant,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
// Valid edge sA → sB.
|
|
166
|
+
await store.createSubSession(
|
|
167
|
+
{
|
|
168
|
+
parentSessionId: sA.id,
|
|
169
|
+
childSessionId: sB.id,
|
|
170
|
+
kind: 'agent_spawn',
|
|
171
|
+
spawnedBy: userActor('usr_a'),
|
|
172
|
+
},
|
|
173
|
+
tenant,
|
|
174
|
+
)
|
|
175
|
+
// Corrupting edge sB → sA closes the cycle. The store layer does not
|
|
176
|
+
// pre-check parent direction (the pattern doc §4.5 discusses
|
|
177
|
+
// intervention DAG cycles; ancestry cycles are a store-corruption
|
|
178
|
+
// detection path per session/errors.ts#AncestryCycleError).
|
|
179
|
+
await store.createSubSession(
|
|
180
|
+
{
|
|
181
|
+
parentSessionId: sB.id,
|
|
182
|
+
childSessionId: sA.id,
|
|
183
|
+
kind: 'agent_spawn',
|
|
184
|
+
spawnedBy: userActor('usr_b'),
|
|
185
|
+
},
|
|
186
|
+
tenant,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
await expect(store.getAncestry(sB.id, tenant)).rejects.toThrow(/cycle/i)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('SubSession pending → active → idle lifecycle', async () => {
|
|
193
|
+
const { store } = buildHarness()
|
|
194
|
+
const tenant = DEFAULT_TENANT
|
|
195
|
+
|
|
196
|
+
const project = await store.createProject({ tenantId: tenant, name: 'lifecycle' }, tenant)
|
|
197
|
+
const parent = await store.createSession(
|
|
198
|
+
{ projectId: project.id, currentActor: userActor('usr_a') },
|
|
199
|
+
tenant,
|
|
200
|
+
)
|
|
201
|
+
const child = await store.createSession(
|
|
202
|
+
{ projectId: project.id, currentActor: agentActor('agt_a') },
|
|
203
|
+
tenant,
|
|
204
|
+
)
|
|
205
|
+
const sub = await store.createSubSession(
|
|
206
|
+
{
|
|
207
|
+
parentSessionId: parent.id,
|
|
208
|
+
childSessionId: child.id,
|
|
209
|
+
kind: 'agent_spawn',
|
|
210
|
+
spawnedBy: userActor('usr_a'),
|
|
211
|
+
},
|
|
212
|
+
tenant,
|
|
213
|
+
)
|
|
214
|
+
expect(sub.status).toBe('pending')
|
|
215
|
+
|
|
216
|
+
// pending → active.
|
|
217
|
+
await store.updateSubSession({ ...sub, status: 'active' }, tenant)
|
|
218
|
+
const active = await store.getSubSession(sub.id, tenant)
|
|
219
|
+
expect(active?.status).toBe('active')
|
|
220
|
+
|
|
221
|
+
// active → idle (§5.3: no 'closed' state — sub-sessions terminate on idle).
|
|
222
|
+
await store.updateSubSession({ ...sub, status: 'idle' }, tenant)
|
|
223
|
+
const idle = await store.getSubSession(sub.id, tenant)
|
|
224
|
+
expect(idle?.status).toBe('idle')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('cross-tenant hierarchy access rejects via TenantIsolationError', async () => {
|
|
228
|
+
const { store } = buildHarness()
|
|
229
|
+
const projectA = await store.createProject(
|
|
230
|
+
{ tenantId: DEFAULT_TENANT, name: 'a' },
|
|
231
|
+
DEFAULT_TENANT,
|
|
232
|
+
)
|
|
233
|
+
await expect(
|
|
234
|
+
store.getProject(projectA.id, 'tnt_other' as typeof DEFAULT_TENANT),
|
|
235
|
+
).rejects.toBeInstanceOf(TenantIsolationError)
|
|
236
|
+
})
|
|
237
|
+
})
|