@namzu/sdk 0.1.8 → 0.3.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 +69 -2
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +5 -2
- 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 +21 -5
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/bridge/a2a/index.d.ts +1 -1
- package/dist/bridge/a2a/index.d.ts.map +1 -1
- package/dist/bridge/a2a/index.js +1 -1
- package/dist/bridge/a2a/index.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/message.d.ts +0 -2
- package/dist/bridge/a2a/message.d.ts.map +1 -1
- package/dist/bridge/a2a/message.js +0 -26
- package/dist/bridge/a2a/message.js.map +1 -1
- package/dist/bridge/a2a/task.d.ts +5 -4
- package/dist/bridge/a2a/task.d.ts.map +1 -1
- package/dist/bridge/a2a/task.js +4 -4
- 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 +14 -27
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/ids.d.ts +1 -1
- package/dist/contracts/ids.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +3 -3
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/contracts/schemas.d.ts +1 -31
- package/dist/contracts/schemas.d.ts.map +1 -1
- package/dist/contracts/schemas.js +1 -7
- package/dist/contracts/schemas.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 +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- 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 +316 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -0
- package/dist/manager/agent/lifecycle.d.ts +67 -3
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +375 -14
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/index.d.ts +2 -0
- package/dist/manager/index.d.ts.map +1 -1
- package/dist/manager/index.js +1 -0
- package/dist/manager/index.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +10 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +20 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/manager/thread/__tests__/lifecycle.test.d.ts +2 -0
- package/dist/manager/thread/__tests__/lifecycle.test.d.ts.map +1 -0
- package/dist/manager/thread/__tests__/lifecycle.test.js +216 -0
- package/dist/manager/thread/__tests__/lifecycle.test.js.map +1 -0
- package/dist/manager/thread/lifecycle.d.ts +105 -0
- package/dist/manager/thread/lifecycle.d.ts.map +1 -0
- package/dist/manager/thread/lifecycle.js +186 -0
- package/dist/manager/thread/lifecycle.js.map +1 -0
- package/dist/rag/retriever.js +2 -2
- 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 +85 -0
- package/dist/runtime/query/__tests__/context.test.js.map +1 -0
- package/dist/runtime/query/context-cache.d.ts +3 -3
- package/dist/runtime/query/context-cache.d.ts.map +1 -1
- package/dist/runtime/query/context-cache.js +2 -2
- package/dist/runtime/query/context-cache.js.map +1 -1
- package/dist/runtime/query/context.d.ts +45 -1
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +50 -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 +22 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +11 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/session/__tests__/integration/_fixtures.d.ts +122 -0
- package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -0
- package/dist/session/__tests__/integration/_fixtures.js +215 -0
- package/dist/session/__tests__/integration/_fixtures.js.map +1 -0
- package/dist/session/__tests__/integration/archive-gate.test.d.ts +15 -0
- package/dist/session/__tests__/integration/archive-gate.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/archive-gate.test.js +214 -0
- package/dist/session/__tests__/integration/archive-gate.test.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 +123 -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 +238 -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 +330 -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 +182 -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 +156 -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 +179 -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 +158 -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 +242 -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 +187 -0
- package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -0
- package/dist/session/__tests__/integration/spawn-rollback.test.d.ts +26 -0
- package/dist/session/__tests__/integration/spawn-rollback.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/spawn-rollback.test.js +236 -0
- package/dist/session/__tests__/integration/spawn-rollback.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 +201 -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 +189 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -0
- package/dist/session/errors.d.ts +139 -0
- package/dist/session/errors.d.ts.map +1 -0
- package/dist/session/errors.js +107 -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 +261 -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 +103 -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 +239 -0
- package/dist/session/handoff/__tests__/single.test.js.map +1 -0
- package/dist/session/handoff/assignment.d.ts +71 -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 +54 -0
- package/dist/session/handoff/broadcast.d.ts.map +1 -0
- package/dist/session/handoff/broadcast.js +311 -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 +69 -0
- package/dist/session/handoff/single.d.ts.map +1 -0
- package/dist/session/handoff/single.js +229 -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 +69 -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 +9 -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 +71 -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/hierarchy/thread.d.ts +54 -0
- package/dist/session/hierarchy/thread.d.ts.map +1 -0
- package/dist/session/hierarchy/thread.js +2 -0
- package/dist/session/hierarchy/thread.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 +93 -0
- package/dist/session/migration/id-prefix.d.ts.map +1 -0
- package/dist/session/migration/id-prefix.js +111 -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 +253 -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 +270 -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/index.d.ts +0 -2
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +0 -1
- package/dist/store/index.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 +267 -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 +258 -0
- package/dist/store/session/__tests__/memory.test.js.map +1 -0
- package/dist/store/session/disk.d.ts +86 -0
- package/dist/store/session/disk.d.ts.map +1 -0
- package/dist/store/session/disk.js +818 -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 +10 -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 +49 -0
- package/dist/store/session/memory.d.ts.map +1 -0
- package/dist/store/session/memory.js +335 -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/store/thread/disk.d.ts +41 -0
- package/dist/store/thread/disk.d.ts.map +1 -0
- package/dist/store/thread/disk.js +229 -0
- package/dist/store/thread/disk.js.map +1 -0
- package/dist/store/thread/index.d.ts +4 -0
- package/dist/store/thread/index.d.ts.map +1 -0
- package/dist/store/thread/index.js +6 -0
- package/dist/store/thread/index.js.map +1 -0
- package/dist/store/thread/memory.d.ts +23 -0
- package/dist/store/thread/memory.d.ts.map +1 -0
- package/dist/store/thread/memory.js +90 -0
- package/dist/store/thread/memory.js.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +1 -1
- package/dist/types/agent/base.d.ts +24 -1
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/factory.d.ts +8 -2
- package/dist/types/agent/factory.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +57 -2
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/ids/index.d.ts +22 -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/rag/retrieval.d.ts +4 -3
- package/dist/types/rag/retrieval.d.ts.map +1 -1
- package/dist/types/run/config.d.ts +12 -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 +12 -2
- 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 +9 -0
- package/dist/types/session/ids.d.ts.map +1 -0
- package/dist/types/session/ids.js +9 -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 +210 -0
- package/dist/types/session/store.d.ts.map +1 -0
- package/dist/types/session/store.js +9 -0
- package/dist/types/session/store.js.map +1 -0
- package/dist/types/thread/index.d.ts +2 -0
- package/dist/types/thread/index.d.ts.map +1 -0
- package/dist/types/thread/index.js +5 -0
- package/dist/types/thread/index.js.map +1 -0
- package/dist/types/thread/store.d.ts +86 -0
- package/dist/types/thread/store.d.ts.map +1 -0
- package/dist/types/thread/store.js +22 -0
- package/dist/types/thread/store.js.map +1 -0
- package/dist/utils/id.d.ts +8 -2
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +22 -4
- package/dist/utils/id.js.map +1 -1
- package/package.json +6 -11
- package/src/agents/ReactiveAgent.ts +7 -2
- package/src/agents/RouterAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +29 -6
- package/src/bridge/a2a/index.ts +0 -1
- package/src/bridge/a2a/mapper.ts +7 -0
- package/src/bridge/a2a/message.ts +0 -32
- package/src/bridge/a2a/task.ts +9 -8
- package/src/bridge/sse/mapper.ts +8 -1
- package/src/constants/a2a/index.ts +2 -2
- package/src/contracts/api.ts +14 -30
- package/src/contracts/ids.ts +1 -1
- package/src/contracts/index.ts +3 -7
- package/src/contracts/schemas.ts +1 -8
- package/src/gateway/local.ts +6 -0
- package/src/index.ts +14 -4
- package/src/manager/agent/__tests__/lifecycle.test.ts +473 -0
- package/src/manager/agent/lifecycle.ts +515 -21
- package/src/manager/index.ts +3 -0
- package/src/manager/run/persistence.ts +26 -1
- package/src/manager/thread/__tests__/lifecycle.test.ts +286 -0
- package/src/manager/thread/lifecycle.ts +217 -0
- package/src/rag/retriever.ts +2 -2
- package/src/run/reporter.ts +28 -0
- package/src/runtime/query/__tests__/context.test.ts +102 -0
- package/src/runtime/query/context-cache.ts +4 -4
- package/src/runtime/query/context.ts +98 -9
- package/src/runtime/query/events.ts +8 -0
- package/src/runtime/query/index.ts +38 -1
- package/src/session/__tests__/integration/_fixtures.ts +310 -0
- package/src/session/__tests__/integration/archive-gate.test.ts +288 -0
- package/src/session/__tests__/integration/capacity-caps.test.ts +171 -0
- package/src/session/__tests__/integration/e2e-spawn.test.ts +296 -0
- package/src/session/__tests__/integration/event-stream-ordering.test.ts +410 -0
- package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +271 -0
- package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +214 -0
- package/src/session/__tests__/integration/handoff-single-e2e.test.ts +251 -0
- package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +240 -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 +325 -0
- package/src/session/__tests__/integration/retention-archive.test.ts +233 -0
- package/src/session/__tests__/integration/spawn-rollback.test.ts +313 -0
- package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +239 -0
- package/src/session/__tests__/integration/tenant-isolation.test.ts +292 -0
- package/src/session/errors.ts +159 -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 +378 -0
- package/src/session/handoff/__tests__/capacity.test.ts +129 -0
- package/src/session/handoff/__tests__/single.test.ts +333 -0
- package/src/session/handoff/assignment.ts +74 -0
- package/src/session/handoff/broadcast.ts +406 -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 +310 -0
- package/src/session/handoff/version.ts +59 -0
- package/src/session/hierarchy/__tests__/session.test.ts +100 -0
- package/src/session/hierarchy/actor.ts +17 -0
- package/src/session/hierarchy/index.ts +18 -0
- package/src/session/hierarchy/lineage.ts +15 -0
- package/src/session/hierarchy/project.ts +41 -0
- package/src/session/hierarchy/session.ts +109 -0
- package/src/session/hierarchy/sub-session.ts +92 -0
- package/src/session/hierarchy/tenant.ts +13 -0
- package/src/session/hierarchy/thread.ts +55 -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 +141 -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 +318 -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 +343 -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/index.ts +0 -3
- package/src/store/session/__tests__/disk.test.ts +397 -0
- package/src/store/session/__tests__/memory.test.ts +402 -0
- package/src/store/session/disk.ts +976 -0
- package/src/store/session/index.ts +13 -0
- package/src/store/session/linkage.ts +80 -0
- package/src/store/session/memory.ts +412 -0
- package/src/store/session/messages.ts +21 -0
- package/src/store/thread/disk.ts +261 -0
- package/src/store/thread/index.ts +7 -0
- package/src/store/thread/memory.ts +104 -0
- package/src/types/agent/base.ts +27 -1
- package/src/types/agent/factory.ts +8 -3
- package/src/types/agent/task.ts +66 -2
- package/src/types/ids/index.ts +34 -3
- package/src/types/invocation/__tests__/state.test.ts +37 -29
- package/src/types/invocation/index.ts +26 -10
- package/src/types/rag/retrieval.ts +4 -3
- package/src/types/run/config.ts +13 -1
- package/src/types/run/events.ts +36 -1
- package/src/types/run/index.ts +8 -0
- package/src/types/run/metadata.ts +12 -2
- package/src/types/run/status.ts +33 -0
- package/src/types/session/ids.ts +23 -0
- package/src/types/session/index.ts +27 -0
- package/src/types/session/store.ts +252 -0
- package/src/types/thread/index.ts +5 -0
- package/src/types/thread/store.ts +92 -0
- package/src/utils/id.ts +34 -4
- package/dist/store/conversation/memory.d.ts +0 -21
- package/dist/store/conversation/memory.d.ts.map +0 -1
- package/dist/store/conversation/memory.js +0 -86
- package/dist/store/conversation/memory.js.map +0 -1
- package/dist/types/conversation/index.d.ts +0 -7
- package/dist/types/conversation/index.d.ts.map +0 -1
- package/dist/types/conversation/index.js +0 -2
- package/dist/types/conversation/index.js.map +0 -1
- package/src/store/conversation/memory.ts +0 -121
- package/src/types/conversation/index.ts +0 -8
|
@@ -0,0 +1,976 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DiskSessionStore — filesystem-backed implementation of
|
|
3
|
+
* {@link SessionStore}.
|
|
4
|
+
*
|
|
5
|
+
* Every mutation is write-tmp-rename (Convention #8). Directory layout
|
|
6
|
+
* matches session-hierarchy.md §7 / §13.4:
|
|
7
|
+
*
|
|
8
|
+
* {rootDir}/projects/{projectId}/
|
|
9
|
+
* project.json
|
|
10
|
+
* sessions/{sessionId}/
|
|
11
|
+
* session.json
|
|
12
|
+
* messages.jsonl
|
|
13
|
+
* subsessions/{subSessionId}/
|
|
14
|
+
* subsession.json
|
|
15
|
+
*
|
|
16
|
+
* Tenant scoping is enforced through the JSON payload (`tenantId` field on
|
|
17
|
+
* every record) rather than the path layout; cross-tenant reads reject with
|
|
18
|
+
* {@link TenantIsolationError} (Convention #17, session-hierarchy.md §12.2).
|
|
19
|
+
*
|
|
20
|
+
* Constructor takes `rootDir`; migration to the canonical `.namzu/projects/`
|
|
21
|
+
* path lives in Phase 7 of the overall roadmap.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
appendFile,
|
|
26
|
+
mkdir,
|
|
27
|
+
readFile,
|
|
28
|
+
readdir,
|
|
29
|
+
rename,
|
|
30
|
+
rm,
|
|
31
|
+
unlink,
|
|
32
|
+
writeFile,
|
|
33
|
+
} from 'node:fs/promises'
|
|
34
|
+
import { join } from 'node:path'
|
|
35
|
+
import { TenantIsolationError } from '../../session/errors.js'
|
|
36
|
+
import type { Project } from '../../session/hierarchy/project.js'
|
|
37
|
+
import type { Session } from '../../session/hierarchy/session.js'
|
|
38
|
+
import type { SubSession } from '../../session/hierarchy/sub-session.js'
|
|
39
|
+
import type { DeliverableRef } from '../../session/summary/deliverable.js'
|
|
40
|
+
import { SessionAlreadySummarizedError } from '../../session/summary/ref.js'
|
|
41
|
+
import type {
|
|
42
|
+
SessionSummaryKeyDecision,
|
|
43
|
+
SessionSummaryOutcome,
|
|
44
|
+
SessionSummaryRef,
|
|
45
|
+
} from '../../session/summary/ref.js'
|
|
46
|
+
import type { MessageId, SessionId, TenantId } from '../../types/ids/index.js'
|
|
47
|
+
import type { Message } from '../../types/message/index.js'
|
|
48
|
+
import type { ProjectId, SubSessionId, SummaryId, ThreadId } from '../../types/session/ids.js'
|
|
49
|
+
import type {
|
|
50
|
+
CreateProjectParams,
|
|
51
|
+
CreateSessionParams,
|
|
52
|
+
CreateSubSessionParams,
|
|
53
|
+
SessionStore,
|
|
54
|
+
SessionView,
|
|
55
|
+
} from '../../types/session/store.js'
|
|
56
|
+
import {
|
|
57
|
+
generateMessageId,
|
|
58
|
+
generateProjectId,
|
|
59
|
+
generateSessionId,
|
|
60
|
+
generateSubSessionId,
|
|
61
|
+
} from '../../utils/id.js'
|
|
62
|
+
import { getAncestry, getChildren, orderChildren } from './linkage.js'
|
|
63
|
+
import type { LinkageView } from './linkage.js'
|
|
64
|
+
import type { SessionMessage } from './messages.js'
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Config for {@link DiskSessionStore}. `rootDir` is absolute; all files live
|
|
68
|
+
* under it per the layout documented in the module header.
|
|
69
|
+
*/
|
|
70
|
+
export interface DiskSessionStoreConfig {
|
|
71
|
+
rootDir: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
interface PersistedProject {
|
|
75
|
+
id: ProjectId
|
|
76
|
+
tenantId: TenantId
|
|
77
|
+
name: string
|
|
78
|
+
config: Project['config']
|
|
79
|
+
createdAt: string
|
|
80
|
+
updatedAt: string
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface PersistedSession {
|
|
84
|
+
id: SessionId
|
|
85
|
+
threadId: ThreadId
|
|
86
|
+
projectId: ProjectId
|
|
87
|
+
tenantId: TenantId
|
|
88
|
+
status: Session['status']
|
|
89
|
+
currentActor: Session['currentActor']
|
|
90
|
+
previousActors: Session['previousActors']
|
|
91
|
+
workspaceId: Session['workspaceId']
|
|
92
|
+
ownerVersion: number
|
|
93
|
+
createdAt: string
|
|
94
|
+
updatedAt: string
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface PersistedSubSession {
|
|
98
|
+
id: SubSessionId
|
|
99
|
+
parentSessionId: SessionId
|
|
100
|
+
childSessionId: SessionId
|
|
101
|
+
tenantId: TenantId
|
|
102
|
+
kind: SubSession['kind']
|
|
103
|
+
status: SubSession['status']
|
|
104
|
+
spawnedBy: SubSession['spawnedBy']
|
|
105
|
+
spawnedAt: string
|
|
106
|
+
failureMode: SubSession['failureMode']
|
|
107
|
+
completionMode: SubSession['completionMode']
|
|
108
|
+
workspaceId: SubSession['workspaceId']
|
|
109
|
+
broadcastGroupId?: string
|
|
110
|
+
summaryRef?: SubSession['summaryRef']
|
|
111
|
+
archiveRef?: SubSession['archiveRef']
|
|
112
|
+
archivedAt?: string
|
|
113
|
+
updatedAt: string
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
interface PersistedMessageLine {
|
|
117
|
+
id: MessageId
|
|
118
|
+
sessionId: SessionId
|
|
119
|
+
tenantId: TenantId
|
|
120
|
+
message: Message
|
|
121
|
+
at: string
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface PersistedSummary {
|
|
125
|
+
id: SummaryId
|
|
126
|
+
sessionRef: SessionId
|
|
127
|
+
tenantId: TenantId
|
|
128
|
+
outcome: SessionSummaryOutcome
|
|
129
|
+
deliverables: readonly DeliverableRef[]
|
|
130
|
+
agentSummary: string
|
|
131
|
+
keyDecisions: ReadonlyArray<{ at: string; summary: string }>
|
|
132
|
+
at: string
|
|
133
|
+
materializedBy: 'kernel'
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Non-terminal statuses from which {@link DiskSessionStore.recordSummary}
|
|
138
|
+
* flips the owning session to `'idle'` as part of the atomic materialize +
|
|
139
|
+
* transition contract (session-hierarchy.md §8.1).
|
|
140
|
+
*/
|
|
141
|
+
const SUMMARY_TERMINAL_FLIP_STATUSES: ReadonlySet<Session['status']> = new Set([
|
|
142
|
+
'active',
|
|
143
|
+
'locked',
|
|
144
|
+
'awaiting_merge',
|
|
145
|
+
])
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Index of projectId → its directory path. Built lazily on lookup via
|
|
149
|
+
* {@link DiskSessionStore.resolveProjectDir}; populated by create / getProject.
|
|
150
|
+
*/
|
|
151
|
+
interface ProjectIndexEntry {
|
|
152
|
+
projectId: ProjectId
|
|
153
|
+
path: string
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Index of sessionId → (projectId, path). Populated lazily similarly.
|
|
158
|
+
*/
|
|
159
|
+
interface SessionIndexEntry {
|
|
160
|
+
sessionId: SessionId
|
|
161
|
+
projectId: ProjectId
|
|
162
|
+
path: string
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export class DiskSessionStore implements SessionStore {
|
|
166
|
+
private readonly rootDir: string
|
|
167
|
+
private readonly projectIndex = new Map<ProjectId, ProjectIndexEntry>()
|
|
168
|
+
private readonly sessionIndex = new Map<SessionId, SessionIndexEntry>()
|
|
169
|
+
private readonly subSessionIndex = new Map<
|
|
170
|
+
SubSessionId,
|
|
171
|
+
{ subSessionId: SubSessionId; sessionId: SessionId; projectId: ProjectId; path: string }
|
|
172
|
+
>()
|
|
173
|
+
|
|
174
|
+
constructor(config: DiskSessionStoreConfig) {
|
|
175
|
+
this.rootDir = config.rootDir
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Project CRUD ------------------------------------------------------------
|
|
179
|
+
|
|
180
|
+
async createProject(params: CreateProjectParams, tenantId: TenantId): Promise<Project> {
|
|
181
|
+
if (params.tenantId !== tenantId) {
|
|
182
|
+
throw new TenantIsolationError({
|
|
183
|
+
requested: tenantId,
|
|
184
|
+
resource: `project(name=${params.name})`,
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
const now = new Date()
|
|
188
|
+
const project: Project = {
|
|
189
|
+
id: generateProjectId(),
|
|
190
|
+
tenantId,
|
|
191
|
+
name: params.name,
|
|
192
|
+
config: {
|
|
193
|
+
maxDelegationDepth: 4,
|
|
194
|
+
maxDelegationWidth: 8,
|
|
195
|
+
maxInterventionDepth: 10,
|
|
196
|
+
},
|
|
197
|
+
createdAt: now,
|
|
198
|
+
updatedAt: now,
|
|
199
|
+
}
|
|
200
|
+
const dir = join(this.rootDir, 'projects', project.id)
|
|
201
|
+
await mkdir(dir, { recursive: true })
|
|
202
|
+
await atomicWriteJson(join(dir, 'project.json'), serializeProject(project))
|
|
203
|
+
this.projectIndex.set(project.id, { projectId: project.id, path: dir })
|
|
204
|
+
return project
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async getProject(projectId: ProjectId, tenantId: TenantId): Promise<Project | null> {
|
|
208
|
+
const dir = this.projectDir(projectId)
|
|
209
|
+
const raw = await readJson<PersistedProject>(join(dir, 'project.json'))
|
|
210
|
+
if (!raw) return null
|
|
211
|
+
this.assertTenant(raw.tenantId, tenantId, `project(${projectId})`)
|
|
212
|
+
return deserializeProject(raw)
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Session CRUD ------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
async createSession(params: CreateSessionParams, tenantId: TenantId): Promise<Session> {
|
|
218
|
+
const project = await this.getProject(params.projectId, tenantId)
|
|
219
|
+
if (!project) {
|
|
220
|
+
throw new Error(`Project ${params.projectId} not found`)
|
|
221
|
+
}
|
|
222
|
+
const now = new Date()
|
|
223
|
+
const session: Session = {
|
|
224
|
+
id: generateSessionId(),
|
|
225
|
+
threadId: params.threadId,
|
|
226
|
+
projectId: params.projectId,
|
|
227
|
+
tenantId,
|
|
228
|
+
status: 'idle',
|
|
229
|
+
currentActor: params.currentActor,
|
|
230
|
+
previousActors: [],
|
|
231
|
+
workspaceId: null,
|
|
232
|
+
ownerVersion: 0,
|
|
233
|
+
createdAt: now,
|
|
234
|
+
updatedAt: now,
|
|
235
|
+
}
|
|
236
|
+
const dir = join(this.projectDir(params.projectId), 'sessions', session.id)
|
|
237
|
+
await mkdir(dir, { recursive: true })
|
|
238
|
+
await atomicWriteJson(join(dir, 'session.json'), serializeSession(session))
|
|
239
|
+
this.sessionIndex.set(session.id, {
|
|
240
|
+
sessionId: session.id,
|
|
241
|
+
projectId: params.projectId,
|
|
242
|
+
path: dir,
|
|
243
|
+
})
|
|
244
|
+
return session
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async getSession(sessionId: SessionId, tenantId: TenantId): Promise<Session | null> {
|
|
248
|
+
const located = await this.locateSession(sessionId)
|
|
249
|
+
if (!located) return null
|
|
250
|
+
const raw = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
251
|
+
if (!raw) return null
|
|
252
|
+
this.assertTenant(raw.tenantId, tenantId, `session(${sessionId})`)
|
|
253
|
+
return deserializeSession(raw)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async listSessions(threadId: ThreadId, tenantId: TenantId): Promise<readonly Session[]> {
|
|
257
|
+
// Walk projects/*/sessions/* and filter on the persisted record. Sessions
|
|
258
|
+
// don't live under a thread-scoped path in the current layout — the
|
|
259
|
+
// denormalized `threadId` on every session.json is the authority. Matches
|
|
260
|
+
// DiskThreadStore.listThreads in scan semantics.
|
|
261
|
+
//
|
|
262
|
+
// Cost: O(all sessions across all projects in the root) per call. The
|
|
263
|
+
// MVP disk store prioritizes simplicity over index freshness, matching
|
|
264
|
+
// `buildLinkageView` / `locateSession` which use the same pattern. A
|
|
265
|
+
// production driver would maintain a threadId → sessionIds secondary
|
|
266
|
+
// index populated on createSession / deleteSession. Acceptable for
|
|
267
|
+
// ThreadManager archive/delete today because those operations are
|
|
268
|
+
// admin-initiated and infrequent.
|
|
269
|
+
const projectsDir = join(this.rootDir, 'projects')
|
|
270
|
+
let projectDirs: string[]
|
|
271
|
+
try {
|
|
272
|
+
projectDirs = await readdir(projectsDir)
|
|
273
|
+
} catch (err) {
|
|
274
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
275
|
+
if (code === 'ENOENT') return []
|
|
276
|
+
throw err
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const results: Session[] = []
|
|
280
|
+
for (const rawProject of projectDirs) {
|
|
281
|
+
if (!rawProject.startsWith('prj_')) continue
|
|
282
|
+
const sessionsRoot = join(projectsDir, rawProject, 'sessions')
|
|
283
|
+
let sessionDirs: string[]
|
|
284
|
+
try {
|
|
285
|
+
sessionDirs = await readdir(sessionsRoot)
|
|
286
|
+
} catch {
|
|
287
|
+
continue
|
|
288
|
+
}
|
|
289
|
+
for (const rawSessionId of sessionDirs) {
|
|
290
|
+
if (!rawSessionId.startsWith('ses_')) continue
|
|
291
|
+
const path = join(sessionsRoot, rawSessionId)
|
|
292
|
+
const raw = await readJson<PersistedSession>(join(path, 'session.json'))
|
|
293
|
+
if (!raw) continue
|
|
294
|
+
if (raw.tenantId !== tenantId) continue
|
|
295
|
+
if (raw.threadId !== threadId) continue
|
|
296
|
+
results.push(deserializeSession(raw))
|
|
297
|
+
this.sessionIndex.set(raw.id, {
|
|
298
|
+
sessionId: raw.id,
|
|
299
|
+
projectId: rawProject as ProjectId,
|
|
300
|
+
path,
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
results.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
|
|
305
|
+
return results
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
async updateSession(session: Session, tenantId: TenantId): Promise<void> {
|
|
309
|
+
const located = await this.locateSession(session.id)
|
|
310
|
+
if (!located) {
|
|
311
|
+
throw new Error(`Session ${session.id} not found`)
|
|
312
|
+
}
|
|
313
|
+
if (session.tenantId !== tenantId) {
|
|
314
|
+
throw new TenantIsolationError({
|
|
315
|
+
requested: tenantId,
|
|
316
|
+
resource: `session(${session.id}) payload`,
|
|
317
|
+
})
|
|
318
|
+
}
|
|
319
|
+
const existing = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
320
|
+
if (existing) {
|
|
321
|
+
this.assertTenant(existing.tenantId, tenantId, `session(${session.id})`)
|
|
322
|
+
}
|
|
323
|
+
const updated: Session = { ...session, updatedAt: new Date() }
|
|
324
|
+
await atomicWriteJson(join(located.path, 'session.json'), serializeSession(updated))
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
async deleteSession(sessionId: SessionId, tenantId: TenantId): Promise<void> {
|
|
328
|
+
const located = await this.locateSession(sessionId)
|
|
329
|
+
if (!located) return // Idempotent: missing = no-op.
|
|
330
|
+
const existing = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
331
|
+
if (!existing) return
|
|
332
|
+
this.assertTenant(existing.tenantId, tenantId, `session(${sessionId})`)
|
|
333
|
+
|
|
334
|
+
// Policy: reject if sub-sessions are attached. Callers must delete
|
|
335
|
+
// children first — Convention #5 deny-by-default; no implicit cascade.
|
|
336
|
+
// We check BOTH directions (this session as parent, or as child) to
|
|
337
|
+
// match the in-memory semantics.
|
|
338
|
+
const subsDir = join(located.path, 'subsessions')
|
|
339
|
+
let subEntries: string[] = []
|
|
340
|
+
try {
|
|
341
|
+
subEntries = await readdir(subsDir)
|
|
342
|
+
} catch (err) {
|
|
343
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
344
|
+
if (code !== 'ENOENT') throw err
|
|
345
|
+
}
|
|
346
|
+
if (subEntries.some((e) => e.startsWith('sub_'))) {
|
|
347
|
+
throw new Error(
|
|
348
|
+
`Session ${sessionId} has attached sub-sessions; delete them before deleting the session`,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Also scan tree for sub-session records that reference this session as
|
|
353
|
+
// `childSessionId`. We need to walk siblings; acceptable cost for the
|
|
354
|
+
// MVP disk store since the broadcast rollback path always pairs a
|
|
355
|
+
// deleteSubSession + deleteSession call on the child (no orphans at
|
|
356
|
+
// steady state).
|
|
357
|
+
const projectsDir = join(this.rootDir, 'projects')
|
|
358
|
+
let projectDirs: string[]
|
|
359
|
+
try {
|
|
360
|
+
projectDirs = await readdir(projectsDir)
|
|
361
|
+
} catch (err) {
|
|
362
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
363
|
+
if (code === 'ENOENT') projectDirs = []
|
|
364
|
+
else throw err
|
|
365
|
+
}
|
|
366
|
+
for (const rawProject of projectDirs) {
|
|
367
|
+
if (!rawProject.startsWith('prj_')) continue
|
|
368
|
+
const sessionsRoot = join(projectsDir, rawProject, 'sessions')
|
|
369
|
+
let siblingSessions: string[] = []
|
|
370
|
+
try {
|
|
371
|
+
siblingSessions = await readdir(sessionsRoot)
|
|
372
|
+
} catch {
|
|
373
|
+
continue
|
|
374
|
+
}
|
|
375
|
+
for (const rawSib of siblingSessions) {
|
|
376
|
+
if (!rawSib.startsWith('ses_')) continue
|
|
377
|
+
const sibSubsDir = join(sessionsRoot, rawSib, 'subsessions')
|
|
378
|
+
let sibSubs: string[] = []
|
|
379
|
+
try {
|
|
380
|
+
sibSubs = await readdir(sibSubsDir)
|
|
381
|
+
} catch {
|
|
382
|
+
continue
|
|
383
|
+
}
|
|
384
|
+
for (const rawSub of sibSubs) {
|
|
385
|
+
if (!rawSub.startsWith('sub_')) continue
|
|
386
|
+
const subRaw = await readJson<PersistedSubSession>(
|
|
387
|
+
join(sibSubsDir, rawSub, 'subsession.json'),
|
|
388
|
+
)
|
|
389
|
+
if (!subRaw) continue
|
|
390
|
+
if (subRaw.childSessionId === sessionId || subRaw.parentSessionId === sessionId) {
|
|
391
|
+
throw new Error(
|
|
392
|
+
`Session ${sessionId} has attached sub-sessions; delete them before deleting the session`,
|
|
393
|
+
)
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Recursive removal — `fs.rm` with `recursive: true` is the atomic
|
|
400
|
+
// primitive for bulk delete. No write-tmp-rename applies here (we're
|
|
401
|
+
// destroying state, not creating it).
|
|
402
|
+
await rm(located.path, { recursive: true, force: true })
|
|
403
|
+
this.sessionIndex.delete(sessionId)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// SubSession CRUD ---------------------------------------------------------
|
|
407
|
+
|
|
408
|
+
async createSubSession(params: CreateSubSessionParams, tenantId: TenantId): Promise<SubSession> {
|
|
409
|
+
const parent = await this.getSession(params.parentSessionId, tenantId)
|
|
410
|
+
if (!parent) throw new Error(`Parent session ${params.parentSessionId} not found`)
|
|
411
|
+
|
|
412
|
+
const child = await this.getSession(params.childSessionId, tenantId)
|
|
413
|
+
if (!child) throw new Error(`Child session ${params.childSessionId} not found`)
|
|
414
|
+
|
|
415
|
+
const parentLoc = this.sessionIndex.get(params.parentSessionId)
|
|
416
|
+
if (!parentLoc) throw new Error(`Parent session ${params.parentSessionId} missing from index`)
|
|
417
|
+
|
|
418
|
+
const now = new Date()
|
|
419
|
+
const subSession: SubSession = {
|
|
420
|
+
id: generateSubSessionId(),
|
|
421
|
+
parentSessionId: params.parentSessionId,
|
|
422
|
+
childSessionId: params.childSessionId,
|
|
423
|
+
kind: params.kind,
|
|
424
|
+
status: 'pending',
|
|
425
|
+
spawnedBy: params.spawnedBy,
|
|
426
|
+
spawnedAt: now,
|
|
427
|
+
failureMode: params.failureMode ?? 'delegate',
|
|
428
|
+
completionMode: params.completionMode ?? 'summary_ref',
|
|
429
|
+
workspaceId: null,
|
|
430
|
+
updatedAt: now,
|
|
431
|
+
}
|
|
432
|
+
const dir = join(parentLoc.path, 'subsessions', subSession.id)
|
|
433
|
+
await mkdir(dir, { recursive: true })
|
|
434
|
+
await atomicWriteJson(join(dir, 'subsession.json'), serializeSubSession(subSession, tenantId))
|
|
435
|
+
this.subSessionIndex.set(subSession.id, {
|
|
436
|
+
subSessionId: subSession.id,
|
|
437
|
+
sessionId: params.parentSessionId,
|
|
438
|
+
projectId: parentLoc.projectId,
|
|
439
|
+
path: dir,
|
|
440
|
+
})
|
|
441
|
+
return subSession
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async getSubSession(subSessionId: SubSessionId, tenantId: TenantId): Promise<SubSession | null> {
|
|
445
|
+
const located = await this.locateSubSession(subSessionId)
|
|
446
|
+
if (!located) return null
|
|
447
|
+
const raw = await readJson<PersistedSubSession>(join(located.path, 'subsession.json'))
|
|
448
|
+
if (!raw) return null
|
|
449
|
+
this.assertTenant(raw.tenantId, tenantId, `sub-session(${subSessionId})`)
|
|
450
|
+
return deserializeSubSession(raw)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async updateSubSession(subSession: SubSession, tenantId: TenantId): Promise<void> {
|
|
454
|
+
const located = await this.locateSubSession(subSession.id)
|
|
455
|
+
if (!located) {
|
|
456
|
+
throw new Error(`SubSession ${subSession.id} not found`)
|
|
457
|
+
}
|
|
458
|
+
const existing = await readJson<PersistedSubSession>(join(located.path, 'subsession.json'))
|
|
459
|
+
if (existing) {
|
|
460
|
+
this.assertTenant(existing.tenantId, tenantId, `sub-session(${subSession.id})`)
|
|
461
|
+
}
|
|
462
|
+
const updated: SubSession = { ...subSession, updatedAt: new Date() }
|
|
463
|
+
await atomicWriteJson(
|
|
464
|
+
join(located.path, 'subsession.json'),
|
|
465
|
+
serializeSubSession(updated, tenantId),
|
|
466
|
+
)
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
async deleteSubSession(subSessionId: SubSessionId, tenantId: TenantId): Promise<void> {
|
|
470
|
+
const located = await this.locateSubSession(subSessionId)
|
|
471
|
+
if (!located) return // Idempotent: missing = no-op.
|
|
472
|
+
const existing = await readJson<PersistedSubSession>(join(located.path, 'subsession.json'))
|
|
473
|
+
if (!existing) {
|
|
474
|
+
// Record vanished between locate + read — treat as already deleted.
|
|
475
|
+
this.subSessionIndex.delete(subSessionId)
|
|
476
|
+
return
|
|
477
|
+
}
|
|
478
|
+
this.assertTenant(existing.tenantId, tenantId, `sub-session(${subSessionId})`)
|
|
479
|
+
|
|
480
|
+
await rm(located.path, { recursive: true, force: true })
|
|
481
|
+
this.subSessionIndex.delete(subSessionId)
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Messages ----------------------------------------------------------------
|
|
485
|
+
|
|
486
|
+
async appendMessage(
|
|
487
|
+
sessionId: SessionId,
|
|
488
|
+
message: Message,
|
|
489
|
+
tenantId: TenantId,
|
|
490
|
+
): Promise<MessageId> {
|
|
491
|
+
const located = await this.locateSession(sessionId)
|
|
492
|
+
if (!located) throw new Error(`Session ${sessionId} not found`)
|
|
493
|
+
|
|
494
|
+
const session = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
495
|
+
if (!session) throw new Error(`Session ${sessionId} not found on disk`)
|
|
496
|
+
this.assertTenant(session.tenantId, tenantId, `session(${sessionId})`)
|
|
497
|
+
|
|
498
|
+
const id = generateMessageId()
|
|
499
|
+
const entry: PersistedMessageLine = {
|
|
500
|
+
id,
|
|
501
|
+
sessionId,
|
|
502
|
+
tenantId,
|
|
503
|
+
message,
|
|
504
|
+
at: new Date().toISOString(),
|
|
505
|
+
}
|
|
506
|
+
await appendFile(join(located.path, 'messages.jsonl'), `${JSON.stringify(entry)}\n`, 'utf-8')
|
|
507
|
+
return id
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async loadMessages(sessionId: SessionId, tenantId: TenantId): Promise<readonly Message[]> {
|
|
511
|
+
const rows = await this.loadSessionMessages(sessionId, tenantId)
|
|
512
|
+
return rows.map((r) => r.message)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
async loadSessionMessages(
|
|
516
|
+
sessionId: SessionId,
|
|
517
|
+
tenantId: TenantId,
|
|
518
|
+
): Promise<readonly SessionMessage[]> {
|
|
519
|
+
const located = await this.locateSession(sessionId)
|
|
520
|
+
if (!located) return []
|
|
521
|
+
|
|
522
|
+
const session = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
523
|
+
if (!session) return []
|
|
524
|
+
this.assertTenant(session.tenantId, tenantId, `session(${sessionId})`)
|
|
525
|
+
|
|
526
|
+
const path = join(located.path, 'messages.jsonl')
|
|
527
|
+
let raw: string
|
|
528
|
+
try {
|
|
529
|
+
raw = await readFile(path, 'utf-8')
|
|
530
|
+
} catch (err) {
|
|
531
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
532
|
+
if (code === 'ENOENT') return []
|
|
533
|
+
throw err
|
|
534
|
+
}
|
|
535
|
+
const lines = raw.split('\n').filter((l) => l.length > 0)
|
|
536
|
+
return lines.map((line) => {
|
|
537
|
+
const persisted = JSON.parse(line) as PersistedMessageLine
|
|
538
|
+
return {
|
|
539
|
+
id: persisted.id,
|
|
540
|
+
sessionId: persisted.sessionId,
|
|
541
|
+
tenantId: persisted.tenantId,
|
|
542
|
+
message: persisted.message,
|
|
543
|
+
at: new Date(persisted.at),
|
|
544
|
+
}
|
|
545
|
+
})
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// Linkage -----------------------------------------------------------------
|
|
549
|
+
|
|
550
|
+
async getChildren(sessionId: SessionId, tenantId: TenantId): Promise<readonly SubSession[]> {
|
|
551
|
+
const session = await this.getSession(sessionId, tenantId)
|
|
552
|
+
if (!session) return []
|
|
553
|
+
const view = await this.buildLinkageView(tenantId)
|
|
554
|
+
return orderChildren(getChildren(view, sessionId))
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
async getAncestry(sessionId: SessionId, tenantId: TenantId): Promise<readonly SessionId[]> {
|
|
558
|
+
const session = await this.getSession(sessionId, tenantId)
|
|
559
|
+
if (!session) return []
|
|
560
|
+
const view = await this.buildLinkageView(tenantId)
|
|
561
|
+
return getAncestry(view, sessionId)
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async drill(sessionId: SessionId, tenantId: TenantId): Promise<SessionView | null> {
|
|
565
|
+
const session = await this.getSession(sessionId, tenantId)
|
|
566
|
+
if (!session) return null
|
|
567
|
+
const view = await this.buildLinkageView(tenantId)
|
|
568
|
+
return {
|
|
569
|
+
session,
|
|
570
|
+
children: orderChildren(getChildren(view, sessionId)),
|
|
571
|
+
ancestry: getAncestry(view, sessionId),
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
// Summary (§4.7 / §8.1) ---------------------------------------------------
|
|
576
|
+
|
|
577
|
+
/**
|
|
578
|
+
* Atomic materialize-with-terminal-transition (§8.1). Two write-tmp-renames:
|
|
579
|
+
*
|
|
580
|
+
* 1. Persist `summary.json` under the session directory.
|
|
581
|
+
* 2. Flip `session.json#status` to `'idle'` if it's in a non-terminal
|
|
582
|
+
* state (`'active' | 'locked' | 'awaiting_merge'`).
|
|
583
|
+
*
|
|
584
|
+
* Each rename is atomic individually. A crash between step 1 and step 2
|
|
585
|
+
* leaves summary present + session still non-terminal — recovery replays
|
|
586
|
+
* the flip via {@link SessionSummaryMaterializer.recover}. Idempotent when
|
|
587
|
+
* the same summary is re-presented (recovery path); rejects a *different*
|
|
588
|
+
* summary for the same session as {@link SessionAlreadySummarizedError}.
|
|
589
|
+
*/
|
|
590
|
+
async recordSummary(
|
|
591
|
+
summary: SessionSummaryRef & { materializedBy: 'kernel' },
|
|
592
|
+
tenantId: TenantId,
|
|
593
|
+
): Promise<void> {
|
|
594
|
+
if (summary.tenantId !== tenantId) {
|
|
595
|
+
throw new TenantIsolationError({
|
|
596
|
+
requested: tenantId,
|
|
597
|
+
resource: `summary(${summary.id}) payload`,
|
|
598
|
+
})
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const located = await this.locateSession(summary.sessionRef)
|
|
602
|
+
if (!located) {
|
|
603
|
+
throw new Error(`Session ${summary.sessionRef} not found`)
|
|
604
|
+
}
|
|
605
|
+
const sessionRaw = await readJson<PersistedSession>(join(located.path, 'session.json'))
|
|
606
|
+
if (!sessionRaw) {
|
|
607
|
+
throw new Error(`Session ${summary.sessionRef} not found on disk`)
|
|
608
|
+
}
|
|
609
|
+
this.assertTenant(sessionRaw.tenantId, tenantId, `session(${summary.sessionRef})`)
|
|
610
|
+
|
|
611
|
+
const summaryPath = join(located.path, 'summary.json')
|
|
612
|
+
const existingRaw = await readJson<PersistedSummary>(summaryPath)
|
|
613
|
+
if (existingRaw) {
|
|
614
|
+
this.assertTenant(existingRaw.tenantId, tenantId, `summary(${existingRaw.id})`)
|
|
615
|
+
if (existingRaw.id !== summary.id) {
|
|
616
|
+
throw new SessionAlreadySummarizedError({
|
|
617
|
+
sessionId: summary.sessionRef,
|
|
618
|
+
existingSummaryId: existingRaw.id,
|
|
619
|
+
})
|
|
620
|
+
}
|
|
621
|
+
// Same summary id — recovery replay. No duplicate write; fall through
|
|
622
|
+
// to the status flip so crash-between-writes is recovered.
|
|
623
|
+
} else {
|
|
624
|
+
// Step 1: persist summary.
|
|
625
|
+
await atomicWriteJson(summaryPath, serializeSummary(summary))
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Step 2: flip session status atomically if still non-terminal.
|
|
629
|
+
if (SUMMARY_TERMINAL_FLIP_STATUSES.has(sessionRaw.status)) {
|
|
630
|
+
const flipped: PersistedSession = {
|
|
631
|
+
...sessionRaw,
|
|
632
|
+
status: 'idle',
|
|
633
|
+
updatedAt: new Date().toISOString(),
|
|
634
|
+
}
|
|
635
|
+
await atomicWriteJson(join(located.path, 'session.json'), flipped)
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
async getSummary(sessionId: SessionId, tenantId: TenantId): Promise<SessionSummaryRef | null> {
|
|
640
|
+
const located = await this.locateSession(sessionId)
|
|
641
|
+
if (!located) return null
|
|
642
|
+
const raw = await readJson<PersistedSummary>(join(located.path, 'summary.json'))
|
|
643
|
+
if (!raw) return null
|
|
644
|
+
this.assertTenant(raw.tenantId, tenantId, `summary(${raw.id})`)
|
|
645
|
+
return deserializeSummary(raw)
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// Helpers -----------------------------------------------------------------
|
|
649
|
+
|
|
650
|
+
private assertTenant(actual: TenantId, requested: TenantId, resource: string): void {
|
|
651
|
+
if (actual !== requested) {
|
|
652
|
+
throw new TenantIsolationError({ requested, resource })
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private projectDir(projectId: ProjectId): string {
|
|
657
|
+
const cached = this.projectIndex.get(projectId)
|
|
658
|
+
if (cached) return cached.path
|
|
659
|
+
const path = join(this.rootDir, 'projects', projectId)
|
|
660
|
+
this.projectIndex.set(projectId, { projectId, path })
|
|
661
|
+
return path
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private async locateSession(sessionId: SessionId): Promise<SessionIndexEntry | null> {
|
|
665
|
+
const cached = this.sessionIndex.get(sessionId)
|
|
666
|
+
if (cached) return cached
|
|
667
|
+
|
|
668
|
+
const projectsDir = join(this.rootDir, 'projects')
|
|
669
|
+
let projectDirs: string[]
|
|
670
|
+
try {
|
|
671
|
+
projectDirs = await readdir(projectsDir)
|
|
672
|
+
} catch (err) {
|
|
673
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
674
|
+
if (code === 'ENOENT') return null
|
|
675
|
+
throw err
|
|
676
|
+
}
|
|
677
|
+
for (const rawId of projectDirs) {
|
|
678
|
+
if (!rawId.startsWith('prj_')) continue
|
|
679
|
+
const projectId = rawId as ProjectId
|
|
680
|
+
const sessionsRoot = join(projectsDir, projectId, 'sessions')
|
|
681
|
+
let sessionDirs: string[]
|
|
682
|
+
try {
|
|
683
|
+
sessionDirs = await readdir(sessionsRoot)
|
|
684
|
+
} catch {
|
|
685
|
+
continue
|
|
686
|
+
}
|
|
687
|
+
for (const rawSessionId of sessionDirs) {
|
|
688
|
+
if (!rawSessionId.startsWith('ses_')) continue
|
|
689
|
+
if (rawSessionId === sessionId) {
|
|
690
|
+
const entry: SessionIndexEntry = {
|
|
691
|
+
sessionId,
|
|
692
|
+
projectId,
|
|
693
|
+
path: join(sessionsRoot, rawSessionId),
|
|
694
|
+
}
|
|
695
|
+
this.sessionIndex.set(sessionId, entry)
|
|
696
|
+
return entry
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return null
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
private async locateSubSession(subSessionId: SubSessionId): Promise<{
|
|
704
|
+
subSessionId: SubSessionId
|
|
705
|
+
sessionId: SessionId
|
|
706
|
+
projectId: ProjectId
|
|
707
|
+
path: string
|
|
708
|
+
} | null> {
|
|
709
|
+
const cached = this.subSessionIndex.get(subSessionId)
|
|
710
|
+
if (cached) return cached
|
|
711
|
+
|
|
712
|
+
const projectsDir = join(this.rootDir, 'projects')
|
|
713
|
+
let projectDirs: string[]
|
|
714
|
+
try {
|
|
715
|
+
projectDirs = await readdir(projectsDir)
|
|
716
|
+
} catch (err) {
|
|
717
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
718
|
+
if (code === 'ENOENT') return null
|
|
719
|
+
throw err
|
|
720
|
+
}
|
|
721
|
+
for (const rawProject of projectDirs) {
|
|
722
|
+
if (!rawProject.startsWith('prj_')) continue
|
|
723
|
+
const projectId = rawProject as ProjectId
|
|
724
|
+
const sessionsRoot = join(projectsDir, projectId, 'sessions')
|
|
725
|
+
let sessionDirs: string[]
|
|
726
|
+
try {
|
|
727
|
+
sessionDirs = await readdir(sessionsRoot)
|
|
728
|
+
} catch {
|
|
729
|
+
continue
|
|
730
|
+
}
|
|
731
|
+
for (const rawSession of sessionDirs) {
|
|
732
|
+
if (!rawSession.startsWith('ses_')) continue
|
|
733
|
+
const sessionId = rawSession as SessionId
|
|
734
|
+
const subsDir = join(sessionsRoot, sessionId, 'subsessions')
|
|
735
|
+
let subDirs: string[]
|
|
736
|
+
try {
|
|
737
|
+
subDirs = await readdir(subsDir)
|
|
738
|
+
} catch {
|
|
739
|
+
continue
|
|
740
|
+
}
|
|
741
|
+
for (const rawSub of subDirs) {
|
|
742
|
+
if (rawSub === subSessionId) {
|
|
743
|
+
const entry = {
|
|
744
|
+
subSessionId,
|
|
745
|
+
sessionId,
|
|
746
|
+
projectId,
|
|
747
|
+
path: join(subsDir, rawSub),
|
|
748
|
+
}
|
|
749
|
+
this.subSessionIndex.set(subSessionId, entry)
|
|
750
|
+
return entry
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return null
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
private async buildLinkageView(tenantId: TenantId): Promise<LinkageView> {
|
|
759
|
+
// Walk the full projects → sessions → subsessions tree once per call.
|
|
760
|
+
// Acceptable for an MVP disk store; a production impl would cache.
|
|
761
|
+
const allSubs: SubSession[] = []
|
|
762
|
+
const projectsDir = join(this.rootDir, 'projects')
|
|
763
|
+
let projectDirs: string[]
|
|
764
|
+
try {
|
|
765
|
+
projectDirs = await readdir(projectsDir)
|
|
766
|
+
} catch (err) {
|
|
767
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
768
|
+
if (code === 'ENOENT') return emptyLinkageView()
|
|
769
|
+
throw err
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
for (const rawProject of projectDirs) {
|
|
773
|
+
if (!rawProject.startsWith('prj_')) continue
|
|
774
|
+
const sessionsRoot = join(projectsDir, rawProject, 'sessions')
|
|
775
|
+
let sessionDirs: string[]
|
|
776
|
+
try {
|
|
777
|
+
sessionDirs = await readdir(sessionsRoot)
|
|
778
|
+
} catch {
|
|
779
|
+
continue
|
|
780
|
+
}
|
|
781
|
+
for (const rawSession of sessionDirs) {
|
|
782
|
+
if (!rawSession.startsWith('ses_')) continue
|
|
783
|
+
const subsRoot = join(sessionsRoot, rawSession, 'subsessions')
|
|
784
|
+
let subDirs: string[]
|
|
785
|
+
try {
|
|
786
|
+
subDirs = await readdir(subsRoot)
|
|
787
|
+
} catch {
|
|
788
|
+
continue
|
|
789
|
+
}
|
|
790
|
+
for (const rawSub of subDirs) {
|
|
791
|
+
const raw = await readJson<PersistedSubSession>(join(subsRoot, rawSub, 'subsession.json'))
|
|
792
|
+
if (!raw) continue
|
|
793
|
+
if (raw.tenantId !== tenantId) continue
|
|
794
|
+
allSubs.push(deserializeSubSession(raw))
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
return {
|
|
800
|
+
findChildSubSessions: (parentSessionId) =>
|
|
801
|
+
allSubs.filter((s) => s.parentSessionId === parentSessionId),
|
|
802
|
+
findParentSubSession: (childSessionId) =>
|
|
803
|
+
allSubs.find((s) => s.childSessionId === childSessionId) ?? null,
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
function emptyLinkageView(): LinkageView {
|
|
809
|
+
return {
|
|
810
|
+
findChildSubSessions: () => [],
|
|
811
|
+
findParentSubSession: () => null,
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
// Serialization helpers -----------------------------------------------------
|
|
816
|
+
|
|
817
|
+
function serializeProject(p: Project): PersistedProject {
|
|
818
|
+
return {
|
|
819
|
+
id: p.id,
|
|
820
|
+
tenantId: p.tenantId,
|
|
821
|
+
name: p.name,
|
|
822
|
+
config: p.config,
|
|
823
|
+
createdAt: p.createdAt.toISOString(),
|
|
824
|
+
updatedAt: p.updatedAt.toISOString(),
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
function deserializeProject(p: PersistedProject): Project {
|
|
829
|
+
return {
|
|
830
|
+
id: p.id,
|
|
831
|
+
tenantId: p.tenantId,
|
|
832
|
+
name: p.name,
|
|
833
|
+
config: p.config,
|
|
834
|
+
createdAt: new Date(p.createdAt),
|
|
835
|
+
updatedAt: new Date(p.updatedAt),
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function serializeSession(s: Session): PersistedSession {
|
|
840
|
+
return {
|
|
841
|
+
id: s.id,
|
|
842
|
+
threadId: s.threadId,
|
|
843
|
+
projectId: s.projectId,
|
|
844
|
+
tenantId: s.tenantId,
|
|
845
|
+
status: s.status,
|
|
846
|
+
currentActor: s.currentActor,
|
|
847
|
+
previousActors: s.previousActors,
|
|
848
|
+
workspaceId: s.workspaceId,
|
|
849
|
+
ownerVersion: s.ownerVersion,
|
|
850
|
+
createdAt: s.createdAt.toISOString(),
|
|
851
|
+
updatedAt: s.updatedAt.toISOString(),
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
function deserializeSession(s: PersistedSession): Session {
|
|
856
|
+
return {
|
|
857
|
+
id: s.id,
|
|
858
|
+
threadId: s.threadId,
|
|
859
|
+
projectId: s.projectId,
|
|
860
|
+
tenantId: s.tenantId,
|
|
861
|
+
status: s.status,
|
|
862
|
+
currentActor: s.currentActor,
|
|
863
|
+
previousActors: s.previousActors,
|
|
864
|
+
workspaceId: s.workspaceId,
|
|
865
|
+
ownerVersion: s.ownerVersion,
|
|
866
|
+
createdAt: new Date(s.createdAt),
|
|
867
|
+
updatedAt: new Date(s.updatedAt),
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function serializeSubSession(s: SubSession, tenantId: TenantId): PersistedSubSession {
|
|
872
|
+
return {
|
|
873
|
+
id: s.id,
|
|
874
|
+
parentSessionId: s.parentSessionId,
|
|
875
|
+
childSessionId: s.childSessionId,
|
|
876
|
+
tenantId,
|
|
877
|
+
kind: s.kind,
|
|
878
|
+
status: s.status,
|
|
879
|
+
spawnedBy: s.spawnedBy,
|
|
880
|
+
spawnedAt: s.spawnedAt.toISOString(),
|
|
881
|
+
failureMode: s.failureMode,
|
|
882
|
+
completionMode: s.completionMode,
|
|
883
|
+
workspaceId: s.workspaceId,
|
|
884
|
+
...(s.broadcastGroupId !== undefined && { broadcastGroupId: s.broadcastGroupId }),
|
|
885
|
+
...(s.summaryRef !== undefined && { summaryRef: s.summaryRef }),
|
|
886
|
+
...(s.archiveRef !== undefined && { archiveRef: s.archiveRef }),
|
|
887
|
+
...(s.archivedAt !== undefined && { archivedAt: s.archivedAt.toISOString() }),
|
|
888
|
+
updatedAt: s.updatedAt.toISOString(),
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
function deserializeSubSession(s: PersistedSubSession): SubSession {
|
|
893
|
+
return {
|
|
894
|
+
id: s.id,
|
|
895
|
+
parentSessionId: s.parentSessionId,
|
|
896
|
+
childSessionId: s.childSessionId,
|
|
897
|
+
kind: s.kind,
|
|
898
|
+
status: s.status,
|
|
899
|
+
spawnedBy: s.spawnedBy,
|
|
900
|
+
spawnedAt: new Date(s.spawnedAt),
|
|
901
|
+
failureMode: s.failureMode,
|
|
902
|
+
completionMode: s.completionMode,
|
|
903
|
+
workspaceId: s.workspaceId,
|
|
904
|
+
...(s.broadcastGroupId !== undefined && { broadcastGroupId: s.broadcastGroupId }),
|
|
905
|
+
...(s.summaryRef !== undefined && { summaryRef: s.summaryRef }),
|
|
906
|
+
...(s.archiveRef !== undefined && { archiveRef: s.archiveRef }),
|
|
907
|
+
...(s.archivedAt !== undefined && { archivedAt: new Date(s.archivedAt) }),
|
|
908
|
+
updatedAt: new Date(s.updatedAt),
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
function serializeSummary(s: SessionSummaryRef): PersistedSummary {
|
|
913
|
+
return {
|
|
914
|
+
id: s.id,
|
|
915
|
+
sessionRef: s.sessionRef,
|
|
916
|
+
tenantId: s.tenantId,
|
|
917
|
+
outcome: s.outcome,
|
|
918
|
+
deliverables: s.deliverables,
|
|
919
|
+
agentSummary: s.agentSummary,
|
|
920
|
+
keyDecisions: s.keyDecisions.map((k) => ({
|
|
921
|
+
at: k.at.toISOString(),
|
|
922
|
+
summary: k.summary,
|
|
923
|
+
})),
|
|
924
|
+
at: s.at.toISOString(),
|
|
925
|
+
materializedBy: 'kernel',
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
function deserializeSummary(s: PersistedSummary): SessionSummaryRef {
|
|
930
|
+
const decisions: SessionSummaryKeyDecision[] = s.keyDecisions.map((k) => ({
|
|
931
|
+
at: new Date(k.at),
|
|
932
|
+
summary: k.summary,
|
|
933
|
+
}))
|
|
934
|
+
return {
|
|
935
|
+
id: s.id,
|
|
936
|
+
sessionRef: s.sessionRef,
|
|
937
|
+
tenantId: s.tenantId,
|
|
938
|
+
outcome: s.outcome,
|
|
939
|
+
deliverables: s.deliverables,
|
|
940
|
+
agentSummary: s.agentSummary,
|
|
941
|
+
keyDecisions: decisions,
|
|
942
|
+
at: new Date(s.at),
|
|
943
|
+
materializedBy: 'kernel',
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// FS helpers -----------------------------------------------------------------
|
|
948
|
+
|
|
949
|
+
async function readJson<T>(path: string): Promise<T | null> {
|
|
950
|
+
try {
|
|
951
|
+
const raw = await readFile(path, 'utf-8')
|
|
952
|
+
return JSON.parse(raw) as T
|
|
953
|
+
} catch (err) {
|
|
954
|
+
const code = (err as NodeJS.ErrnoException).code
|
|
955
|
+
if (code === 'ENOENT') return null
|
|
956
|
+
throw err
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
async function atomicWriteJson(filePath: string, value: unknown): Promise<void> {
|
|
961
|
+
const tempPath = `${filePath}.tmp`
|
|
962
|
+
try {
|
|
963
|
+
await writeFile(tempPath, JSON.stringify(value, null, 2), 'utf-8')
|
|
964
|
+
await rename(tempPath, filePath)
|
|
965
|
+
} catch (err) {
|
|
966
|
+
await unlink(tempPath).catch(() => undefined)
|
|
967
|
+
throw err
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
// Note: messages are append-only `messages.jsonl` (not write-tmp-rename).
|
|
972
|
+
// Append is the write-safety primitive for log-structured files; each
|
|
973
|
+
// line is a whole record. This matches pattern doc §13.4 persistence
|
|
974
|
+
// (`messages.json[l]` as append-only event log).
|
|
975
|
+
|
|
976
|
+
export type { SessionMessage } from './messages.js'
|