@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,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionSummaryRef — the structured pointer a parent session sees when a
|
|
3
|
+
* sub-session completes. Kernel-owned terminalization primitive: the only
|
|
4
|
+
* producer is {@link SessionSummaryMaterializer.materialize} (see
|
|
5
|
+
* `./materialize.ts`). See session-hierarchy.md §4.7 (shape) and §8.1
|
|
6
|
+
* (emission invariant).
|
|
7
|
+
*
|
|
8
|
+
* The `materializedBy: 'kernel'` literal field is load-bearing — combined
|
|
9
|
+
* with the opaque {@link SummaryId} brand (mintable only by
|
|
10
|
+
* `generateSummaryId`), it enforces at the type level that no agent or tool
|
|
11
|
+
* can construct a valid `SessionSummaryRef`. `SessionStore.recordSummary`
|
|
12
|
+
* only accepts `SessionSummaryRef & { materializedBy: 'kernel' }`, so even a
|
|
13
|
+
* payload shaped correctly would fail the structural check.
|
|
14
|
+
*
|
|
15
|
+
* This is Convention #0 load-bearing: there is no "tool" path to emit a
|
|
16
|
+
* summary; the parent agent sees only what the kernel seals.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { SessionId, TenantId } from '../../types/ids/index.js'
|
|
20
|
+
import type { SummaryId } from '../../types/session/ids.js'
|
|
21
|
+
import type { DeliverableRef } from './deliverable.js'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Max characters allowed for {@link SessionSummaryRef.agentSummary}. See
|
|
25
|
+
* session-hierarchy.md §4.7 — the agent's narration is bounded to keep
|
|
26
|
+
* parent prompts token-finite regardless of the child's transcript length.
|
|
27
|
+
*/
|
|
28
|
+
export const AGENT_SUMMARY_MAX_CHARS = 4000
|
|
29
|
+
|
|
30
|
+
export type SessionSummaryOutcomeStatus = 'succeeded' | 'partial' | 'failed'
|
|
31
|
+
|
|
32
|
+
export interface SessionSummaryOutcome {
|
|
33
|
+
readonly status: SessionSummaryOutcomeStatus
|
|
34
|
+
/** Short human-readable one-liner. Optional per pattern doc §4.7. */
|
|
35
|
+
readonly verdict?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface SessionSummaryKeyDecision {
|
|
39
|
+
readonly at: Date
|
|
40
|
+
readonly summary: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Structured completion pointer emitted by the kernel when a sub-session
|
|
45
|
+
* terminalizes. See session-hierarchy.md §4.7 and §8.1.
|
|
46
|
+
*
|
|
47
|
+
* Readonly across the board — the record is immutable once sealed.
|
|
48
|
+
*/
|
|
49
|
+
export interface SessionSummaryRef {
|
|
50
|
+
readonly id: SummaryId
|
|
51
|
+
readonly sessionRef: SessionId
|
|
52
|
+
readonly tenantId: TenantId
|
|
53
|
+
readonly outcome: SessionSummaryOutcome
|
|
54
|
+
readonly deliverables: readonly DeliverableRef[]
|
|
55
|
+
/** Agent's own narration; bounded to {@link AGENT_SUMMARY_MAX_CHARS}. */
|
|
56
|
+
readonly agentSummary: string
|
|
57
|
+
readonly keyDecisions: readonly SessionSummaryKeyDecision[]
|
|
58
|
+
/** When the summary was materialized. */
|
|
59
|
+
readonly at: Date
|
|
60
|
+
/**
|
|
61
|
+
* Literal `'kernel'` — enforces kernel-only emission at the type level.
|
|
62
|
+
* See the module header for the invariant rationale.
|
|
63
|
+
*/
|
|
64
|
+
readonly materializedBy: 'kernel'
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Raised by {@link SessionSummaryMaterializer.materialize} when the provided
|
|
69
|
+
* `agentSummary` exceeds {@link AGENT_SUMMARY_MAX_CHARS}. Convention #5
|
|
70
|
+
* deny-by-default — the kernel does not truncate silently.
|
|
71
|
+
*/
|
|
72
|
+
export class AgentSummaryTooLongError extends Error {
|
|
73
|
+
readonly details: {
|
|
74
|
+
readonly actual: number
|
|
75
|
+
readonly max: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
constructor(details: { actual: number; max: number }) {
|
|
79
|
+
super(`agentSummary ${details.actual} chars exceeds max ${details.max}`)
|
|
80
|
+
this.name = 'AgentSummaryTooLongError'
|
|
81
|
+
this.details = details
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Raised when {@link SessionSummaryMaterializer.materialize} is invoked
|
|
87
|
+
* against a session that already has a persisted summary. Re-materialization
|
|
88
|
+
* would duplicate history — callers wanting to append must open a new
|
|
89
|
+
* intervention sub-session (see session-hierarchy.md §4.5).
|
|
90
|
+
*/
|
|
91
|
+
export class SessionAlreadySummarizedError extends Error {
|
|
92
|
+
readonly details: {
|
|
93
|
+
readonly sessionId: SessionId
|
|
94
|
+
readonly existingSummaryId: SummaryId
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
constructor(details: { sessionId: SessionId; existingSummaryId: SummaryId }) {
|
|
98
|
+
super(
|
|
99
|
+
`Session ${details.sessionId} already has summary ${details.existingSummaryId}; re-materialization rejected`,
|
|
100
|
+
)
|
|
101
|
+
this.name = 'SessionAlreadySummarizedError'
|
|
102
|
+
this.details = details
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
|
+
import { WorkspaceBackendError } from '../../errors.js'
|
|
3
|
+
import {
|
|
4
|
+
type ExecFile,
|
|
5
|
+
type ExecFileResult,
|
|
6
|
+
GitWorktreeDriver,
|
|
7
|
+
parseWorktreeList,
|
|
8
|
+
} from '../git-worktree.js'
|
|
9
|
+
import type { WorkspaceRef } from '../ref.js'
|
|
10
|
+
|
|
11
|
+
function stubLogger() {
|
|
12
|
+
return {
|
|
13
|
+
debug: vi.fn(),
|
|
14
|
+
info: vi.fn(),
|
|
15
|
+
warn: vi.fn(),
|
|
16
|
+
error: vi.fn(),
|
|
17
|
+
child() {
|
|
18
|
+
return stubLogger()
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function okExec(stdout = '', stderr = ''): ExecFileResult {
|
|
24
|
+
return { stdout, stderr }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('GitWorktreeDriver', () => {
|
|
28
|
+
it('create: invokes `git worktree add` with argv array (no shell interpolation)', async () => {
|
|
29
|
+
const calls: Array<{ file: string; args: readonly string[] }> = []
|
|
30
|
+
const exec: ExecFile = async (file, args) => {
|
|
31
|
+
calls.push({ file, args: [...args] })
|
|
32
|
+
return okExec()
|
|
33
|
+
}
|
|
34
|
+
const driver = new GitWorktreeDriver({
|
|
35
|
+
repoRoot: '/repo',
|
|
36
|
+
logger: stubLogger(),
|
|
37
|
+
execFile: exec,
|
|
38
|
+
})
|
|
39
|
+
const ref = await driver.create({ label: 'foo', baseRef: 'main' })
|
|
40
|
+
|
|
41
|
+
expect(ref.id.startsWith('wsp_')).toBe(true)
|
|
42
|
+
expect(ref.meta.backend).toBe('git-worktree')
|
|
43
|
+
expect(ref.meta.branch).toBe('namzu/foo')
|
|
44
|
+
expect(calls).toHaveLength(1)
|
|
45
|
+
const call = calls[0]
|
|
46
|
+
if (!call) throw new Error('missing call')
|
|
47
|
+
expect(call.file).toBe('git')
|
|
48
|
+
expect(call.args).toEqual([
|
|
49
|
+
'-C',
|
|
50
|
+
'/repo',
|
|
51
|
+
'worktree',
|
|
52
|
+
'add',
|
|
53
|
+
'-b',
|
|
54
|
+
'namzu/foo',
|
|
55
|
+
'/repo/.namzu/worktrees/foo',
|
|
56
|
+
'main',
|
|
57
|
+
])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('create: omits baseRef argv slot when not supplied', async () => {
|
|
61
|
+
const calls: Array<{ file: string; args: readonly string[] }> = []
|
|
62
|
+
const exec: ExecFile = async (file, args) => {
|
|
63
|
+
calls.push({ file, args: [...args] })
|
|
64
|
+
return okExec()
|
|
65
|
+
}
|
|
66
|
+
const driver = new GitWorktreeDriver({
|
|
67
|
+
repoRoot: '/repo',
|
|
68
|
+
logger: stubLogger(),
|
|
69
|
+
execFile: exec,
|
|
70
|
+
})
|
|
71
|
+
await driver.create({ label: 'bar' })
|
|
72
|
+
const call = calls[0]
|
|
73
|
+
if (!call) throw new Error('missing call')
|
|
74
|
+
expect(call.args.at(-1)).toBe('/repo/.namzu/worktrees/bar')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('create: wraps failures in WorkspaceBackendError', async () => {
|
|
78
|
+
const exec: ExecFile = async () => {
|
|
79
|
+
throw new Error('invalid baseRef')
|
|
80
|
+
}
|
|
81
|
+
const driver = new GitWorktreeDriver({
|
|
82
|
+
repoRoot: '/repo',
|
|
83
|
+
logger: stubLogger(),
|
|
84
|
+
execFile: exec,
|
|
85
|
+
})
|
|
86
|
+
await expect(driver.create({ baseRef: 'does-not-exist' })).rejects.toBeInstanceOf(
|
|
87
|
+
WorkspaceBackendError,
|
|
88
|
+
)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('dispose: removes worktree with --force', async () => {
|
|
92
|
+
const calls: Array<readonly string[]> = []
|
|
93
|
+
const exec: ExecFile = async (_file, args) => {
|
|
94
|
+
calls.push(args)
|
|
95
|
+
return okExec()
|
|
96
|
+
}
|
|
97
|
+
const driver = new GitWorktreeDriver({
|
|
98
|
+
repoRoot: '/repo',
|
|
99
|
+
logger: stubLogger(),
|
|
100
|
+
execFile: exec,
|
|
101
|
+
})
|
|
102
|
+
const ref: WorkspaceRef = {
|
|
103
|
+
id: 'wsp_x' as unknown as WorkspaceRef['id'],
|
|
104
|
+
meta: {
|
|
105
|
+
backend: 'git-worktree',
|
|
106
|
+
repoRoot: '/repo',
|
|
107
|
+
branch: 'namzu/x',
|
|
108
|
+
worktreePath: '/repo/.namzu/worktrees/x',
|
|
109
|
+
},
|
|
110
|
+
createdAt: new Date(),
|
|
111
|
+
}
|
|
112
|
+
await driver.dispose(ref)
|
|
113
|
+
expect(calls[0]).toEqual([
|
|
114
|
+
'-C',
|
|
115
|
+
'/repo',
|
|
116
|
+
'worktree',
|
|
117
|
+
'remove',
|
|
118
|
+
'/repo/.namzu/worktrees/x',
|
|
119
|
+
'--force',
|
|
120
|
+
])
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('dispose: tolerates missing worktree ("not a working tree")', async () => {
|
|
124
|
+
const exec: ExecFile = async () => {
|
|
125
|
+
throw new Error("'/tmp/gone' is not a working tree")
|
|
126
|
+
}
|
|
127
|
+
const driver = new GitWorktreeDriver({
|
|
128
|
+
repoRoot: '/repo',
|
|
129
|
+
logger: stubLogger(),
|
|
130
|
+
execFile: exec,
|
|
131
|
+
})
|
|
132
|
+
const ref: WorkspaceRef = {
|
|
133
|
+
id: 'wsp_x' as unknown as WorkspaceRef['id'],
|
|
134
|
+
meta: {
|
|
135
|
+
backend: 'git-worktree',
|
|
136
|
+
repoRoot: '/repo',
|
|
137
|
+
branch: 'namzu/x',
|
|
138
|
+
worktreePath: '/tmp/gone',
|
|
139
|
+
},
|
|
140
|
+
createdAt: new Date(),
|
|
141
|
+
}
|
|
142
|
+
// Must NOT throw — roadmap Risk #3 mitigation.
|
|
143
|
+
await expect(driver.dispose(ref)).resolves.toBeUndefined()
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('dispose: surfaces unexpected failures as WorkspaceBackendError', async () => {
|
|
147
|
+
const exec: ExecFile = async () => {
|
|
148
|
+
throw new Error('fatal: permission denied')
|
|
149
|
+
}
|
|
150
|
+
const driver = new GitWorktreeDriver({
|
|
151
|
+
repoRoot: '/repo',
|
|
152
|
+
logger: stubLogger(),
|
|
153
|
+
execFile: exec,
|
|
154
|
+
})
|
|
155
|
+
const ref: WorkspaceRef = {
|
|
156
|
+
id: 'wsp_x' as unknown as WorkspaceRef['id'],
|
|
157
|
+
meta: {
|
|
158
|
+
backend: 'git-worktree',
|
|
159
|
+
repoRoot: '/repo',
|
|
160
|
+
branch: 'namzu/x',
|
|
161
|
+
worktreePath: '/repo/.namzu/worktrees/x',
|
|
162
|
+
},
|
|
163
|
+
createdAt: new Date(),
|
|
164
|
+
}
|
|
165
|
+
await expect(driver.dispose(ref)).rejects.toBeInstanceOf(WorkspaceBackendError)
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('inspect: parses list output and detects clean tree', async () => {
|
|
169
|
+
const listStdout = [
|
|
170
|
+
'worktree /repo/.namzu/worktrees/x',
|
|
171
|
+
'HEAD abc123',
|
|
172
|
+
'branch refs/heads/namzu/x',
|
|
173
|
+
'',
|
|
174
|
+
].join('\n')
|
|
175
|
+
let callIndex = 0
|
|
176
|
+
const exec: ExecFile = async (_file, args) => {
|
|
177
|
+
callIndex++
|
|
178
|
+
if (args.includes('list')) return okExec(listStdout)
|
|
179
|
+
if (args.includes('status')) return okExec('')
|
|
180
|
+
throw new Error(`unexpected call ${callIndex}`)
|
|
181
|
+
}
|
|
182
|
+
const driver = new GitWorktreeDriver({
|
|
183
|
+
repoRoot: '/repo',
|
|
184
|
+
logger: stubLogger(),
|
|
185
|
+
execFile: exec,
|
|
186
|
+
})
|
|
187
|
+
const ref: WorkspaceRef = {
|
|
188
|
+
id: 'wsp_x' as unknown as WorkspaceRef['id'],
|
|
189
|
+
meta: {
|
|
190
|
+
backend: 'git-worktree',
|
|
191
|
+
repoRoot: '/repo',
|
|
192
|
+
branch: 'namzu/x',
|
|
193
|
+
worktreePath: '/repo/.namzu/worktrees/x',
|
|
194
|
+
},
|
|
195
|
+
createdAt: new Date(),
|
|
196
|
+
}
|
|
197
|
+
const inspection = await driver.inspect(ref)
|
|
198
|
+
expect(inspection.exists).toBe(true)
|
|
199
|
+
expect(inspection.isDirty).toBe(false)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
it('inspect: reports dirty when status --porcelain has output', async () => {
|
|
203
|
+
const listStdout = [
|
|
204
|
+
'worktree /repo/.namzu/worktrees/x',
|
|
205
|
+
'HEAD abc123',
|
|
206
|
+
'branch refs/heads/namzu/x',
|
|
207
|
+
'',
|
|
208
|
+
].join('\n')
|
|
209
|
+
const exec: ExecFile = async (_file, args) => {
|
|
210
|
+
if (args.includes('list')) return okExec(listStdout)
|
|
211
|
+
if (args.includes('status')) return okExec(' M path/to/file\n')
|
|
212
|
+
throw new Error('unexpected')
|
|
213
|
+
}
|
|
214
|
+
const driver = new GitWorktreeDriver({
|
|
215
|
+
repoRoot: '/repo',
|
|
216
|
+
logger: stubLogger(),
|
|
217
|
+
execFile: exec,
|
|
218
|
+
})
|
|
219
|
+
const ref: WorkspaceRef = {
|
|
220
|
+
id: 'wsp_x' as unknown as WorkspaceRef['id'],
|
|
221
|
+
meta: {
|
|
222
|
+
backend: 'git-worktree',
|
|
223
|
+
repoRoot: '/repo',
|
|
224
|
+
branch: 'namzu/x',
|
|
225
|
+
worktreePath: '/repo/.namzu/worktrees/x',
|
|
226
|
+
},
|
|
227
|
+
createdAt: new Date(),
|
|
228
|
+
}
|
|
229
|
+
const inspection = await driver.inspect(ref)
|
|
230
|
+
expect(inspection.isDirty).toBe(true)
|
|
231
|
+
})
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
describe('parseWorktreeList', () => {
|
|
235
|
+
it('returns null when the target path is absent', () => {
|
|
236
|
+
const stdout = ['worktree /other', 'HEAD abc', 'branch refs/heads/main', ''].join('\n')
|
|
237
|
+
expect(parseWorktreeList(stdout, '/missing')).toBeNull()
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
it('extracts head + branch for the matching entry', () => {
|
|
241
|
+
const stdout = [
|
|
242
|
+
'worktree /repo/.namzu/worktrees/x',
|
|
243
|
+
'HEAD deadbeef',
|
|
244
|
+
'branch refs/heads/namzu/x',
|
|
245
|
+
'',
|
|
246
|
+
'worktree /other',
|
|
247
|
+
'HEAD abc',
|
|
248
|
+
'bare',
|
|
249
|
+
'',
|
|
250
|
+
].join('\n')
|
|
251
|
+
const entry = parseWorktreeList(stdout, '/repo/.namzu/worktrees/x')
|
|
252
|
+
expect(entry).toEqual({
|
|
253
|
+
path: '/repo/.namzu/worktrees/x',
|
|
254
|
+
head: 'deadbeef',
|
|
255
|
+
branch: 'refs/heads/namzu/x',
|
|
256
|
+
})
|
|
257
|
+
})
|
|
258
|
+
})
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { describe, expect, it } from 'vitest'
|
|
3
|
+
import type { ProjectId, RunId, SessionId } from '../../../types/ids/index.js'
|
|
4
|
+
import type { SubSessionId } from '../../../types/session/ids.js'
|
|
5
|
+
import { DefaultPathBuilder } from '../path-builder.js'
|
|
6
|
+
|
|
7
|
+
const projectId = 'prj_abc' as ProjectId
|
|
8
|
+
const sessionId = 'ses_xyz' as SessionId
|
|
9
|
+
const subSessionId = 'sub_qqq' as SubSessionId
|
|
10
|
+
const runId = 'run_rrr' as RunId
|
|
11
|
+
|
|
12
|
+
describe('DefaultPathBuilder', () => {
|
|
13
|
+
it('rootDir returns the injected root verbatim', () => {
|
|
14
|
+
const pb = new DefaultPathBuilder('/tmp/ns')
|
|
15
|
+
expect(pb.rootDir()).toBe('/tmp/ns')
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('projectDir resolves to {root}/projects/{projectId}', () => {
|
|
19
|
+
const pb = new DefaultPathBuilder('/tmp/ns')
|
|
20
|
+
expect(pb.projectDir(projectId)).toBe(join('/tmp/ns', 'projects', projectId))
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
it('sessionDir resolves to {root}/projects/{projectId}/sessions/{sessionId}', () => {
|
|
24
|
+
const pb = new DefaultPathBuilder('/tmp/ns')
|
|
25
|
+
expect(pb.sessionDir(projectId, sessionId)).toBe(
|
|
26
|
+
join('/tmp/ns', 'projects', projectId, 'sessions', sessionId),
|
|
27
|
+
)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
it('subSessionDir nests under session/subsessions/{subSessionId}', () => {
|
|
31
|
+
const pb = new DefaultPathBuilder('/tmp/ns')
|
|
32
|
+
expect(pb.subSessionDir(projectId, sessionId, subSessionId)).toBe(
|
|
33
|
+
join('/tmp/ns', 'projects', projectId, 'sessions', sessionId, 'subsessions', subSessionId),
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('runDir nests under session/runs/{runId}', () => {
|
|
38
|
+
const pb = new DefaultPathBuilder('/tmp/ns')
|
|
39
|
+
expect(pb.runDir(projectId, sessionId, runId)).toBe(
|
|
40
|
+
join('/tmp/ns', 'projects', projectId, 'sessions', sessionId, 'runs', runId),
|
|
41
|
+
)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('root injection is per-instance (does not mutate global state)', () => {
|
|
45
|
+
const a = new DefaultPathBuilder('/tmp/a')
|
|
46
|
+
const b = new DefaultPathBuilder('/tmp/b')
|
|
47
|
+
expect(a.rootDir()).toBe('/tmp/a')
|
|
48
|
+
expect(b.rootDir()).toBe('/tmp/b')
|
|
49
|
+
expect(a.projectDir(projectId)).not.toBe(b.projectDir(projectId))
|
|
50
|
+
})
|
|
51
|
+
})
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkspaceBackendDriver — pluggable backend contract for workspace
|
|
3
|
+
* provisioning. See session-hierarchy.md §7.1.
|
|
4
|
+
*
|
|
5
|
+
* Every Session persists a {@link WorkspaceRef}; the driver owns the
|
|
6
|
+
* underlying filesystem/container/etc. semantics. Phase 3 ships the
|
|
7
|
+
* git-worktree reference implementation (`git-worktree.ts`); tmpfs,
|
|
8
|
+
* container, and shared variants slot behind this same interface in a
|
|
9
|
+
* later phase (Convention #10: provider abstraction).
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { WorkspaceBackendKind, WorkspaceRef } from './ref.js'
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Params for {@link WorkspaceBackendDriver.create}. `baseRef` selects the
|
|
16
|
+
* commit / branch the workspace should track; when absent the driver
|
|
17
|
+
* provisions from the backend's default (e.g. the current branch of the
|
|
18
|
+
* repo for git-worktree).
|
|
19
|
+
*/
|
|
20
|
+
export interface CreateWorkspaceParams {
|
|
21
|
+
baseRef?: string
|
|
22
|
+
label?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Params for {@link WorkspaceBackendDriver.branch}. `label` is a free-form
|
|
27
|
+
* tag embedded into the new workspace name so operators can trace provenance
|
|
28
|
+
* without reaching into the driver's internal naming scheme.
|
|
29
|
+
*/
|
|
30
|
+
export interface BranchWorkspaceParams {
|
|
31
|
+
label?: string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Snapshot returned by {@link WorkspaceBackendDriver.inspect}. `exists` is
|
|
36
|
+
* `false` when the underlying resource has been disposed out-of-band (e.g.
|
|
37
|
+
* manual `git worktree remove` on disk); consumers can use it to detect
|
|
38
|
+
* divergence between the persisted {@link WorkspaceRef} and the backend.
|
|
39
|
+
*/
|
|
40
|
+
export interface WorkspaceInspection {
|
|
41
|
+
exists: boolean
|
|
42
|
+
currentRef: string
|
|
43
|
+
isDirty: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Backend driver contract. All methods are async and may throw
|
|
48
|
+
* {@link WorkspaceBackendError} on I/O failures. `dispose` is idempotent —
|
|
49
|
+
* calling it against an already-disposed ref is not an error (mitigates
|
|
50
|
+
* roadmap Risk #3: broadcast rollback must not fail on partial state).
|
|
51
|
+
*/
|
|
52
|
+
export interface WorkspaceBackendDriver {
|
|
53
|
+
readonly kind: WorkspaceBackendKind
|
|
54
|
+
create(params: CreateWorkspaceParams): Promise<WorkspaceRef>
|
|
55
|
+
branch(source: WorkspaceRef, params: BranchWorkspaceParams): Promise<WorkspaceRef>
|
|
56
|
+
dispose(ref: WorkspaceRef): Promise<void>
|
|
57
|
+
inspect(ref: WorkspaceRef): Promise<WorkspaceInspection>
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type { WorkspaceBackendKind } from './ref.js'
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitWorktreeDriver — reference implementation of
|
|
3
|
+
* {@link WorkspaceBackendDriver} backed by `git worktree`.
|
|
4
|
+
*
|
|
5
|
+
* See session-hierarchy.md §7.2 (Git-worktree reference backend).
|
|
6
|
+
*
|
|
7
|
+
* Safety: every subprocess invocation uses `execFile` with an argv array —
|
|
8
|
+
* no shell interpolation — so caller-supplied `label`/`baseRef` strings
|
|
9
|
+
* cannot escape their argument slot. Failures surface as
|
|
10
|
+
* {@link WorkspaceBackendError} carrying the underlying cause.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { execFile } from 'node:child_process'
|
|
14
|
+
import { join } from 'node:path'
|
|
15
|
+
import { promisify } from 'node:util'
|
|
16
|
+
import { generateWorkspaceId } from '../../utils/id.js'
|
|
17
|
+
import type { Logger } from '../../utils/logger.js'
|
|
18
|
+
import { WorkspaceBackendError } from '../errors.js'
|
|
19
|
+
import type {
|
|
20
|
+
BranchWorkspaceParams,
|
|
21
|
+
CreateWorkspaceParams,
|
|
22
|
+
WorkspaceBackendDriver,
|
|
23
|
+
WorkspaceInspection,
|
|
24
|
+
} from './driver.js'
|
|
25
|
+
import type { GitWorktreeBackendMeta, WorkspaceRef } from './ref.js'
|
|
26
|
+
|
|
27
|
+
const execFileAsync = promisify(execFile)
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Minimal shape of `execFile`'s promisified result — allows tests to stub
|
|
31
|
+
* via dependency injection without pulling in the full `ChildProcess` type.
|
|
32
|
+
*/
|
|
33
|
+
export interface ExecFileResult {
|
|
34
|
+
stdout: string
|
|
35
|
+
stderr: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Async exec callable used by {@link GitWorktreeDriver}. Matches the shape
|
|
40
|
+
* of `promisify(execFile)` for the argv-array overload. Injected via the
|
|
41
|
+
* constructor so tests can stub without touching `child_process`.
|
|
42
|
+
*/
|
|
43
|
+
export type ExecFile = (file: string, args: readonly string[]) => Promise<ExecFileResult>
|
|
44
|
+
|
|
45
|
+
const defaultExecFile: ExecFile = async (file, args) => execFileAsync(file, [...args])
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Configuration for {@link GitWorktreeDriver}.
|
|
49
|
+
*/
|
|
50
|
+
export interface GitWorktreeDriverConfig {
|
|
51
|
+
/** Absolute path to the repo root whose `.git` backs the worktrees. */
|
|
52
|
+
repoRoot: string
|
|
53
|
+
/**
|
|
54
|
+
* Directory (absolute) where worktree checkouts live. Defaults to
|
|
55
|
+
* `{repoRoot}/.namzu/worktrees` per session-hierarchy.md §7.2.
|
|
56
|
+
*/
|
|
57
|
+
worktreesDir?: string
|
|
58
|
+
logger: Logger
|
|
59
|
+
/** Test-seam: inject a stub `execFile` implementation. */
|
|
60
|
+
execFile?: ExecFile
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export class GitWorktreeDriver implements WorkspaceBackendDriver {
|
|
64
|
+
readonly kind = 'git-worktree' as const
|
|
65
|
+
|
|
66
|
+
private readonly repoRoot: string
|
|
67
|
+
private readonly worktreesDir: string
|
|
68
|
+
private readonly log: Logger
|
|
69
|
+
private readonly exec: ExecFile
|
|
70
|
+
|
|
71
|
+
constructor(config: GitWorktreeDriverConfig) {
|
|
72
|
+
this.repoRoot = config.repoRoot
|
|
73
|
+
this.worktreesDir = config.worktreesDir ?? join(config.repoRoot, '.namzu', 'worktrees')
|
|
74
|
+
this.log = config.logger.child({ component: 'GitWorktreeDriver' })
|
|
75
|
+
this.exec = config.execFile ?? defaultExecFile
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async create(params: CreateWorkspaceParams): Promise<WorkspaceRef> {
|
|
79
|
+
const id = generateWorkspaceId()
|
|
80
|
+
const label = params.label ?? id
|
|
81
|
+
const branch = `namzu/${label}`
|
|
82
|
+
const worktreePath = join(this.worktreesDir, label)
|
|
83
|
+
|
|
84
|
+
const argv = ['-C', this.repoRoot, 'worktree', 'add', '-b', branch, worktreePath]
|
|
85
|
+
if (params.baseRef !== undefined) {
|
|
86
|
+
argv.push(params.baseRef)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
await this.exec('git', argv)
|
|
91
|
+
} catch (cause) {
|
|
92
|
+
throw new WorkspaceBackendError({ op: 'create', kind: this.kind, cause })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const meta: GitWorktreeBackendMeta = {
|
|
96
|
+
backend: 'git-worktree',
|
|
97
|
+
repoRoot: this.repoRoot,
|
|
98
|
+
branch,
|
|
99
|
+
worktreePath,
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
this.log.info('git-worktree created', { id, branch, worktreePath })
|
|
103
|
+
return {
|
|
104
|
+
id,
|
|
105
|
+
meta,
|
|
106
|
+
createdAt: new Date(),
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async branch(source: WorkspaceRef, params: BranchWorkspaceParams): Promise<WorkspaceRef> {
|
|
111
|
+
// Branch from the source worktree's current branch. The source's
|
|
112
|
+
// `meta.branch` is the ref we base off.
|
|
113
|
+
return this.create({ baseRef: source.meta.branch, label: params.label })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async dispose(ref: WorkspaceRef): Promise<void> {
|
|
117
|
+
// Tolerate missing directories per roadmap Risk #3 mitigation: the
|
|
118
|
+
// broadcast-rollback compensating step calls dispose on partially
|
|
119
|
+
// provisioned refs and must not propagate "already gone" errors.
|
|
120
|
+
try {
|
|
121
|
+
await this.exec('git', [
|
|
122
|
+
'-C',
|
|
123
|
+
this.repoRoot,
|
|
124
|
+
'worktree',
|
|
125
|
+
'remove',
|
|
126
|
+
ref.meta.worktreePath,
|
|
127
|
+
'--force',
|
|
128
|
+
])
|
|
129
|
+
this.log.info('git-worktree disposed', { id: ref.id, path: ref.meta.worktreePath })
|
|
130
|
+
} catch (cause) {
|
|
131
|
+
const message = cause instanceof Error ? cause.message : String(cause)
|
|
132
|
+
// git exits non-zero with "is not a working tree" when the path is
|
|
133
|
+
// absent; treat as idempotent success and log at debug.
|
|
134
|
+
if (/not a working tree|does not exist|No such file/i.test(message)) {
|
|
135
|
+
this.log.debug('git-worktree already gone; dispose idempotent', {
|
|
136
|
+
id: ref.id,
|
|
137
|
+
path: ref.meta.worktreePath,
|
|
138
|
+
})
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
throw new WorkspaceBackendError({ op: 'dispose', kind: this.kind, cause })
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async inspect(ref: WorkspaceRef): Promise<WorkspaceInspection> {
|
|
146
|
+
let listStdout: string
|
|
147
|
+
try {
|
|
148
|
+
const result = await this.exec('git', [
|
|
149
|
+
'-C',
|
|
150
|
+
this.repoRoot,
|
|
151
|
+
'worktree',
|
|
152
|
+
'list',
|
|
153
|
+
'--porcelain',
|
|
154
|
+
])
|
|
155
|
+
listStdout = result.stdout
|
|
156
|
+
} catch (cause) {
|
|
157
|
+
throw new WorkspaceBackendError({ op: 'inspect:list', kind: this.kind, cause })
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const entry = parseWorktreeList(listStdout, ref.meta.worktreePath)
|
|
161
|
+
if (!entry) {
|
|
162
|
+
return { exists: false, currentRef: ref.meta.branch, isDirty: false }
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let statusStdout: string
|
|
166
|
+
try {
|
|
167
|
+
const result = await this.exec('git', ['-C', ref.meta.worktreePath, 'status', '--porcelain'])
|
|
168
|
+
statusStdout = result.stdout
|
|
169
|
+
} catch (cause) {
|
|
170
|
+
throw new WorkspaceBackendError({ op: 'inspect:status', kind: this.kind, cause })
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
exists: true,
|
|
175
|
+
currentRef: entry.branch ?? entry.head ?? ref.meta.branch,
|
|
176
|
+
isDirty: statusStdout.trim().length > 0,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Parses `git worktree list --porcelain` output, returning the record for
|
|
183
|
+
* `worktreePath` or `null` when absent. Exported for tests.
|
|
184
|
+
*/
|
|
185
|
+
export function parseWorktreeList(
|
|
186
|
+
stdout: string,
|
|
187
|
+
worktreePath: string,
|
|
188
|
+
): { path: string; head?: string; branch?: string } | null {
|
|
189
|
+
const blocks = stdout.split(/\n\n+/)
|
|
190
|
+
for (const block of blocks) {
|
|
191
|
+
const lines = block.split('\n').filter((l) => l.length > 0)
|
|
192
|
+
let path: string | undefined
|
|
193
|
+
let head: string | undefined
|
|
194
|
+
let branch: string | undefined
|
|
195
|
+
for (const line of lines) {
|
|
196
|
+
if (line.startsWith('worktree ')) path = line.slice('worktree '.length)
|
|
197
|
+
else if (line.startsWith('HEAD ')) head = line.slice('HEAD '.length)
|
|
198
|
+
else if (line.startsWith('branch ')) branch = line.slice('branch '.length)
|
|
199
|
+
}
|
|
200
|
+
if (path === worktreePath) {
|
|
201
|
+
return {
|
|
202
|
+
path,
|
|
203
|
+
...(head !== undefined && { head }),
|
|
204
|
+
...(branch !== undefined && { branch }),
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return null
|
|
209
|
+
}
|