@namzu/sdk 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -5
- package/README.md +14 -9
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +5 -3
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +3 -0
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +18 -5
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +6 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/a2a/task.d.ts +2 -2
- package/dist/bridge/a2a/task.d.ts.map +1 -1
- package/dist/bridge/a2a/task.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +6 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/constants/a2a/index.d.ts +2 -2
- package/dist/constants/a2a/index.d.ts.map +1 -1
- package/dist/constants/a2a/index.js.map +1 -1
- package/dist/contracts/api.d.ts +22 -3
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/index.d.ts +3 -1
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/index.js.map +1 -1
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +6 -0
- package/dist/gateway/local.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/manager/agent/__tests__/lifecycle.test.d.ts +2 -0
- package/dist/manager/agent/__tests__/lifecycle.test.d.ts.map +1 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js +302 -0
- package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -0
- package/dist/manager/agent/lifecycle.d.ts +58 -3
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +311 -12
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +8 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +15 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +25 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/context.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/context.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/context.test.js +84 -0
- package/dist/runtime/query/__tests__/context.test.js.map +1 -0
- package/dist/runtime/query/context.d.ts +55 -2
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +48 -8
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/events.d.ts.map +1 -1
- package/dist/runtime/query/events.js +8 -0
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/index.d.ts +25 -2
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +11 -1
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/session/__tests__/integration/_fixtures.d.ts +115 -0
- package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -0
- package/dist/session/__tests__/integration/_fixtures.js +198 -0
- package/dist/session/__tests__/integration/_fixtures.js.map +1 -0
- package/dist/session/__tests__/integration/capacity-caps.test.d.ts +13 -0
- package/dist/session/__tests__/integration/capacity-caps.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/capacity-caps.test.js +116 -0
- package/dist/session/__tests__/integration/capacity-caps.test.js.map +1 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.d.ts +18 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.js +226 -0
- package/dist/session/__tests__/integration/e2e-spawn.test.js.map +1 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts +15 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.js +323 -0
- package/dist/session/__tests__/integration/event-stream-ordering.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts +12 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js +170 -0
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts +18 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js +146 -0
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js.map +1 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts +15 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js +163 -0
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts +12 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js +157 -0
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js.map +1 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.d.ts +11 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.js +140 -0
- package/dist/session/__tests__/integration/migration-filesystem.test.js.map +1 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts +13 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.js +84 -0
- package/dist/session/__tests__/integration/migration-id-prefix.test.js.map +1 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts +14 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js +241 -0
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js.map +1 -0
- package/dist/session/__tests__/integration/retention-archive.test.d.ts +12 -0
- package/dist/session/__tests__/integration/retention-archive.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/retention-archive.test.js +186 -0
- package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts +18 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.js +200 -0
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.js.map +1 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.d.ts +14 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.d.ts.map +1 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.js +180 -0
- package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -0
- package/dist/session/errors.d.ts +60 -0
- package/dist/session/errors.d.ts.map +1 -0
- package/dist/session/errors.js +50 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/events/index.d.ts +4 -0
- package/dist/session/events/index.d.ts.map +1 -0
- package/dist/session/events/index.js +8 -0
- package/dist/session/events/index.js.map +1 -0
- package/dist/session/events/schema-version.d.ts +13 -0
- package/dist/session/events/schema-version.d.ts.map +1 -0
- package/dist/session/events/schema-version.js +12 -0
- package/dist/session/events/schema-version.js.map +1 -0
- package/dist/session/events/types.d.ts +64 -0
- package/dist/session/events/types.d.ts.map +1 -0
- package/dist/session/events/types.js +2 -0
- package/dist/session/events/types.js.map +1 -0
- package/dist/session/handoff/__tests__/broadcast.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/broadcast.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/broadcast.test.js +243 -0
- package/dist/session/handoff/__tests__/broadcast.test.js.map +1 -0
- package/dist/session/handoff/__tests__/capacity.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/capacity.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/capacity.test.js +100 -0
- package/dist/session/handoff/__tests__/capacity.test.js.map +1 -0
- package/dist/session/handoff/__tests__/single.test.d.ts +2 -0
- package/dist/session/handoff/__tests__/single.test.d.ts.map +1 -0
- package/dist/session/handoff/__tests__/single.test.js +230 -0
- package/dist/session/handoff/__tests__/single.test.js.map +1 -0
- package/dist/session/handoff/assignment.d.ts +59 -0
- package/dist/session/handoff/assignment.d.ts.map +1 -0
- package/dist/session/handoff/assignment.js +11 -0
- package/dist/session/handoff/assignment.js.map +1 -0
- package/dist/session/handoff/broadcast.d.ts +47 -0
- package/dist/session/handoff/broadcast.d.ts.map +1 -0
- package/dist/session/handoff/broadcast.js +296 -0
- package/dist/session/handoff/broadcast.js.map +1 -0
- package/dist/session/handoff/capacity.d.ts +66 -0
- package/dist/session/handoff/capacity.d.ts.map +1 -0
- package/dist/session/handoff/capacity.js +60 -0
- package/dist/session/handoff/capacity.js.map +1 -0
- package/dist/session/handoff/events.d.ts +66 -0
- package/dist/session/handoff/events.d.ts.map +1 -0
- package/dist/session/handoff/events.js +13 -0
- package/dist/session/handoff/events.js.map +1 -0
- package/dist/session/handoff/index.d.ts +12 -0
- package/dist/session/handoff/index.d.ts.map +1 -0
- package/dist/session/handoff/index.js +9 -0
- package/dist/session/handoff/index.js.map +1 -0
- package/dist/session/handoff/single.d.ts +62 -0
- package/dist/session/handoff/single.d.ts.map +1 -0
- package/dist/session/handoff/single.js +217 -0
- package/dist/session/handoff/single.js.map +1 -0
- package/dist/session/handoff/version.d.ts +52 -0
- package/dist/session/handoff/version.d.ts.map +1 -0
- package/dist/session/handoff/version.js +36 -0
- package/dist/session/handoff/version.js.map +1 -0
- package/dist/session/hierarchy/__tests__/session.test.d.ts +2 -0
- package/dist/session/hierarchy/__tests__/session.test.d.ts.map +1 -0
- package/dist/session/hierarchy/__tests__/session.test.js +67 -0
- package/dist/session/hierarchy/__tests__/session.test.js.map +1 -0
- package/dist/session/hierarchy/actor.d.ts +26 -0
- package/dist/session/hierarchy/actor.d.ts.map +1 -0
- package/dist/session/hierarchy/actor.js +2 -0
- package/dist/session/hierarchy/actor.js.map +1 -0
- package/dist/session/hierarchy/index.d.ts +8 -0
- package/dist/session/hierarchy/index.d.ts.map +1 -0
- package/dist/session/hierarchy/index.js +4 -0
- package/dist/session/hierarchy/index.js.map +1 -0
- package/dist/session/hierarchy/lineage.d.ts +15 -0
- package/dist/session/hierarchy/lineage.d.ts.map +1 -0
- package/dist/session/hierarchy/lineage.js +2 -0
- package/dist/session/hierarchy/lineage.js.map +1 -0
- package/dist/session/hierarchy/project.d.ts +40 -0
- package/dist/session/hierarchy/project.d.ts.map +1 -0
- package/dist/session/hierarchy/project.js +2 -0
- package/dist/session/hierarchy/project.js.map +1 -0
- package/dist/session/hierarchy/session.d.ts +59 -0
- package/dist/session/hierarchy/session.d.ts.map +1 -0
- package/dist/session/hierarchy/session.js +51 -0
- package/dist/session/hierarchy/session.js.map +1 -0
- package/dist/session/hierarchy/sub-session.d.ts +76 -0
- package/dist/session/hierarchy/sub-session.d.ts.map +1 -0
- package/dist/session/hierarchy/sub-session.js +2 -0
- package/dist/session/hierarchy/sub-session.js.map +1 -0
- package/dist/session/hierarchy/tenant.d.ts +13 -0
- package/dist/session/hierarchy/tenant.d.ts.map +1 -0
- package/dist/session/hierarchy/tenant.js +2 -0
- package/dist/session/hierarchy/tenant.js.map +1 -0
- package/dist/session/index.d.ts +10 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +15 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.d.ts +2 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.d.ts.map +1 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.js +179 -0
- package/dist/session/intervention/__tests__/prev-artifact.test.js.map +1 -0
- package/dist/session/intervention/index.d.ts +3 -0
- package/dist/session/intervention/index.d.ts.map +1 -0
- package/dist/session/intervention/index.js +8 -0
- package/dist/session/intervention/index.js.map +1 -0
- package/dist/session/intervention/prev-artifact.d.ts +103 -0
- package/dist/session/intervention/prev-artifact.d.ts.map +1 -0
- package/dist/session/intervention/prev-artifact.js +112 -0
- package/dist/session/intervention/prev-artifact.js.map +1 -0
- package/dist/session/migration/__tests__/filesystem.test.d.ts +2 -0
- package/dist/session/migration/__tests__/filesystem.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/filesystem.test.js +188 -0
- package/dist/session/migration/__tests__/filesystem.test.js.map +1 -0
- package/dist/session/migration/__tests__/id-prefix.test.d.ts +2 -0
- package/dist/session/migration/__tests__/id-prefix.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/id-prefix.test.js +83 -0
- package/dist/session/migration/__tests__/id-prefix.test.js.map +1 -0
- package/dist/session/migration/__tests__/marker.test.d.ts +2 -0
- package/dist/session/migration/__tests__/marker.test.d.ts.map +1 -0
- package/dist/session/migration/__tests__/marker.test.js +75 -0
- package/dist/session/migration/__tests__/marker.test.js.map +1 -0
- package/dist/session/migration/errors.d.ts +26 -0
- package/dist/session/migration/errors.d.ts.map +1 -0
- package/dist/session/migration/errors.js +22 -0
- package/dist/session/migration/errors.js.map +1 -0
- package/dist/session/migration/filesystem.d.ts +94 -0
- package/dist/session/migration/filesystem.d.ts.map +1 -0
- package/dist/session/migration/filesystem.js +319 -0
- package/dist/session/migration/filesystem.js.map +1 -0
- package/dist/session/migration/id-prefix.d.ts +98 -0
- package/dist/session/migration/id-prefix.d.ts.map +1 -0
- package/dist/session/migration/id-prefix.js +116 -0
- package/dist/session/migration/id-prefix.js.map +1 -0
- package/dist/session/migration/index.d.ts +8 -0
- package/dist/session/migration/index.d.ts.map +1 -0
- package/dist/session/migration/index.js +8 -0
- package/dist/session/migration/index.js.map +1 -0
- package/dist/session/migration/marker.d.ts +57 -0
- package/dist/session/migration/marker.d.ts.map +1 -0
- package/dist/session/migration/marker.js +111 -0
- package/dist/session/migration/marker.js.map +1 -0
- package/dist/session/retention/__tests__/archive.test.d.ts +2 -0
- package/dist/session/retention/__tests__/archive.test.d.ts.map +1 -0
- package/dist/session/retention/__tests__/archive.test.js +252 -0
- package/dist/session/retention/__tests__/archive.test.js.map +1 -0
- package/dist/session/retention/__tests__/disk-backend.test.d.ts +2 -0
- package/dist/session/retention/__tests__/disk-backend.test.d.ts.map +1 -0
- package/dist/session/retention/__tests__/disk-backend.test.js +154 -0
- package/dist/session/retention/__tests__/disk-backend.test.js.map +1 -0
- package/dist/session/retention/archive-backend-ref.d.ts +18 -0
- package/dist/session/retention/archive-backend-ref.d.ts.map +1 -0
- package/dist/session/retention/archive-backend-ref.js +2 -0
- package/dist/session/retention/archive-backend-ref.js.map +1 -0
- package/dist/session/retention/archive.d.ts +130 -0
- package/dist/session/retention/archive.d.ts.map +1 -0
- package/dist/session/retention/archive.js +203 -0
- package/dist/session/retention/archive.js.map +1 -0
- package/dist/session/retention/backend.d.ts +101 -0
- package/dist/session/retention/backend.d.ts.map +1 -0
- package/dist/session/retention/backend.js +15 -0
- package/dist/session/retention/backend.js.map +1 -0
- package/dist/session/retention/disk-backend.d.ts +59 -0
- package/dist/session/retention/disk-backend.d.ts.map +1 -0
- package/dist/session/retention/disk-backend.js +236 -0
- package/dist/session/retention/disk-backend.js.map +1 -0
- package/dist/session/retention/index.d.ts +9 -0
- package/dist/session/retention/index.d.ts.map +1 -0
- package/dist/session/retention/index.js +6 -0
- package/dist/session/retention/index.js.map +1 -0
- package/dist/session/retention/policy.d.ts +49 -0
- package/dist/session/retention/policy.d.ts.map +1 -0
- package/dist/session/retention/policy.js +21 -0
- package/dist/session/retention/policy.js.map +1 -0
- package/dist/session/summary/__tests__/materialize.test.d.ts +2 -0
- package/dist/session/summary/__tests__/materialize.test.d.ts.map +1 -0
- package/dist/session/summary/__tests__/materialize.test.js +269 -0
- package/dist/session/summary/__tests__/materialize.test.js.map +1 -0
- package/dist/session/summary/deliverable.d.ts +74 -0
- package/dist/session/summary/deliverable.d.ts.map +1 -0
- package/dist/session/summary/deliverable.js +20 -0
- package/dist/session/summary/deliverable.js.map +1 -0
- package/dist/session/summary/index.d.ts +6 -0
- package/dist/session/summary/index.d.ts.map +1 -0
- package/dist/session/summary/index.js +9 -0
- package/dist/session/summary/index.js.map +1 -0
- package/dist/session/summary/materialize.d.ts +82 -0
- package/dist/session/summary/materialize.d.ts.map +1 -0
- package/dist/session/summary/materialize.js +117 -0
- package/dist/session/summary/materialize.js.map +1 -0
- package/dist/session/summary/ref.d.ts +91 -0
- package/dist/session/summary/ref.d.ts.map +1 -0
- package/dist/session/summary/ref.js +51 -0
- package/dist/session/summary/ref.js.map +1 -0
- package/dist/session/workspace/__tests__/git-worktree.test.d.ts +2 -0
- package/dist/session/workspace/__tests__/git-worktree.test.d.ts.map +1 -0
- package/dist/session/workspace/__tests__/git-worktree.test.js +244 -0
- package/dist/session/workspace/__tests__/git-worktree.test.js.map +1 -0
- package/dist/session/workspace/__tests__/path-builder.test.d.ts +2 -0
- package/dist/session/workspace/__tests__/path-builder.test.d.ts.map +1 -0
- package/dist/session/workspace/__tests__/path-builder.test.js +37 -0
- package/dist/session/workspace/__tests__/path-builder.test.js.map +1 -0
- package/dist/session/workspace/driver.d.ts +55 -0
- package/dist/session/workspace/driver.d.ts.map +1 -0
- package/dist/session/workspace/driver.js +12 -0
- package/dist/session/workspace/driver.js.map +1 -0
- package/dist/session/workspace/git-worktree.d.ts +65 -0
- package/dist/session/workspace/git-worktree.d.ts.map +1 -0
- package/dist/session/workspace/git-worktree.js +156 -0
- package/dist/session/workspace/git-worktree.js.map +1 -0
- package/dist/session/workspace/index.d.ts +8 -0
- package/dist/session/workspace/index.d.ts.map +1 -0
- package/dist/session/workspace/index.js +7 -0
- package/dist/session/workspace/index.js.map +1 -0
- package/dist/session/workspace/path-builder.d.ts +50 -0
- package/dist/session/workspace/path-builder.d.ts.map +1 -0
- package/dist/session/workspace/path-builder.js +50 -0
- package/dist/session/workspace/path-builder.js.map +1 -0
- package/dist/session/workspace/ref.d.ts +46 -0
- package/dist/session/workspace/ref.d.ts.map +1 -0
- package/dist/session/workspace/ref.js +11 -0
- package/dist/session/workspace/ref.js.map +1 -0
- package/dist/session/workspace/registry.d.ts +26 -0
- package/dist/session/workspace/registry.d.ts.map +1 -0
- package/dist/session/workspace/registry.js +35 -0
- package/dist/session/workspace/registry.js.map +1 -0
- package/dist/store/conversation/memory.d.ts +22 -0
- package/dist/store/conversation/memory.d.ts.map +1 -1
- package/dist/store/conversation/memory.js +22 -0
- package/dist/store/conversation/memory.js.map +1 -1
- package/dist/store/session/__tests__/disk.test.d.ts +2 -0
- package/dist/store/session/__tests__/disk.test.d.ts.map +1 -0
- package/dist/store/session/__tests__/disk.test.js +240 -0
- package/dist/store/session/__tests__/disk.test.js.map +1 -0
- package/dist/store/session/__tests__/memory.test.d.ts +2 -0
- package/dist/store/session/__tests__/memory.test.d.ts.map +1 -0
- package/dist/store/session/__tests__/memory.test.js +217 -0
- package/dist/store/session/__tests__/memory.test.js.map +1 -0
- package/dist/store/session/disk.d.ts +85 -0
- package/dist/store/session/disk.d.ts.map +1 -0
- package/dist/store/session/disk.js +757 -0
- package/dist/store/session/disk.js.map +1 -0
- package/dist/store/session/index.d.ts +7 -0
- package/dist/store/session/index.d.ts.map +1 -0
- package/dist/store/session/index.js +11 -0
- package/dist/store/session/index.js.map +1 -0
- package/dist/store/session/linkage.d.ts +38 -0
- package/dist/store/session/linkage.d.ts.map +1 -0
- package/dist/store/session/linkage.js +64 -0
- package/dist/store/session/linkage.js.map +1 -0
- package/dist/store/session/memory.d.ts +48 -0
- package/dist/store/session/memory.d.ts.map +1 -0
- package/dist/store/session/memory.js +322 -0
- package/dist/store/session/memory.js.map +1 -0
- package/dist/store/session/messages.d.ts +20 -0
- package/dist/store/session/messages.d.ts.map +1 -0
- package/dist/store/session/messages.js +12 -0
- package/dist/store/session/messages.js.map +1 -0
- package/dist/tools/builtins/__tests__/structuredOutput.example.d.ts +1 -1
- package/dist/types/agent/base.d.ts +28 -1
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +50 -2
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/conversation/index.d.ts +7 -0
- package/dist/types/conversation/index.d.ts.map +1 -1
- package/dist/types/ids/index.d.ts +26 -3
- package/dist/types/ids/index.d.ts.map +1 -1
- package/dist/types/ids/index.js +8 -1
- package/dist/types/ids/index.js.map +1 -1
- package/dist/types/invocation/__tests__/state.test.js +36 -29
- package/dist/types/invocation/__tests__/state.test.js.map +1 -1
- package/dist/types/invocation/index.d.ts +20 -4
- package/dist/types/invocation/index.d.ts.map +1 -1
- package/dist/types/invocation/index.js +10 -7
- package/dist/types/invocation/index.js.map +1 -1
- package/dist/types/run/config.d.ts +11 -1
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +26 -1
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +8 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/types/run/metadata.d.ts +24 -1
- package/dist/types/run/metadata.d.ts.map +1 -1
- package/dist/types/run/status.d.ts +26 -0
- package/dist/types/run/status.d.ts.map +1 -0
- package/dist/types/run/status.js +2 -0
- package/dist/types/run/status.js.map +1 -0
- package/dist/types/session/ids.d.ts +18 -0
- package/dist/types/session/ids.d.ts.map +1 -0
- package/dist/types/session/ids.js +12 -0
- package/dist/types/session/ids.js.map +1 -0
- package/dist/types/session/index.d.ts +3 -0
- package/dist/types/session/index.d.ts.map +1 -0
- package/dist/types/session/index.js +5 -0
- package/dist/types/session/index.js.map +1 -0
- package/dist/types/session/store.d.ts +188 -0
- package/dist/types/session/store.d.ts.map +1 -0
- package/dist/types/session/store.js +14 -0
- package/dist/types/session/store.js.map +1 -0
- package/dist/utils/id.d.ts +18 -1
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +42 -4
- package/dist/utils/id.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/ReactiveAgent.ts +7 -3
- package/src/agents/RouterAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +26 -6
- package/src/bridge/a2a/mapper.ts +7 -0
- package/src/bridge/a2a/task.ts +2 -2
- package/src/bridge/sse/mapper.ts +8 -1
- package/src/constants/a2a/index.ts +2 -2
- package/src/contracts/api.ts +23 -3
- package/src/contracts/index.ts +2 -0
- package/src/gateway/local.ts +6 -0
- package/src/index.ts +14 -0
- package/src/manager/agent/__tests__/lifecycle.test.ts +452 -0
- package/src/manager/agent/lifecycle.ts +434 -19
- package/src/manager/run/persistence.ts +20 -1
- package/src/run/reporter.ts +28 -0
- package/src/runtime/query/__tests__/context.test.ts +101 -0
- package/src/runtime/query/context.ts +106 -10
- package/src/runtime/query/events.ts +8 -0
- package/src/runtime/query/index.ts +41 -3
- package/src/session/__tests__/integration/_fixtures.ts +282 -0
- package/src/session/__tests__/integration/capacity-caps.test.ts +164 -0
- package/src/session/__tests__/integration/e2e-spawn.test.ts +278 -0
- package/src/session/__tests__/integration/event-stream-ordering.test.ts +403 -0
- package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +245 -0
- package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +179 -0
- package/src/session/__tests__/integration/handoff-single-e2e.test.ts +220 -0
- package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +237 -0
- package/src/session/__tests__/integration/migration-filesystem.test.ts +209 -0
- package/src/session/__tests__/integration/migration-id-prefix.test.ts +101 -0
- package/src/session/__tests__/integration/prev-artifact-dag.test.ts +318 -0
- package/src/session/__tests__/integration/retention-archive.test.ts +231 -0
- package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +237 -0
- package/src/session/__tests__/integration/tenant-isolation.test.ts +282 -0
- package/src/session/errors.ts +70 -0
- package/src/session/events/index.ts +16 -0
- package/src/session/events/schema-version.ts +13 -0
- package/src/session/events/types.ts +71 -0
- package/src/session/handoff/__tests__/broadcast.test.ts +350 -0
- package/src/session/handoff/__tests__/capacity.test.ts +123 -0
- package/src/session/handoff/__tests__/single.test.ts +316 -0
- package/src/session/handoff/assignment.ts +62 -0
- package/src/session/handoff/broadcast.ts +381 -0
- package/src/session/handoff/capacity.ts +121 -0
- package/src/session/handoff/events.ts +72 -0
- package/src/session/handoff/index.ts +29 -0
- package/src/session/handoff/single.ts +288 -0
- package/src/session/handoff/version.ts +59 -0
- package/src/session/hierarchy/__tests__/session.test.ts +92 -0
- package/src/session/hierarchy/actor.ts +17 -0
- package/src/session/hierarchy/index.ts +17 -0
- package/src/session/hierarchy/lineage.ts +15 -0
- package/src/session/hierarchy/project.ts +41 -0
- package/src/session/hierarchy/session.ts +97 -0
- package/src/session/hierarchy/sub-session.ts +92 -0
- package/src/session/hierarchy/tenant.ts +13 -0
- package/src/session/index.ts +15 -0
- package/src/session/intervention/__tests__/prev-artifact.test.ts +234 -0
- package/src/session/intervention/index.ts +16 -0
- package/src/session/intervention/prev-artifact.ts +180 -0
- package/src/session/migration/__tests__/filesystem.test.ts +263 -0
- package/src/session/migration/__tests__/id-prefix.test.ts +101 -0
- package/src/session/migration/__tests__/marker.test.ts +84 -0
- package/src/session/migration/errors.ts +23 -0
- package/src/session/migration/filesystem.ts +401 -0
- package/src/session/migration/id-prefix.ts +146 -0
- package/src/session/migration/index.ts +38 -0
- package/src/session/migration/marker.ts +131 -0
- package/src/session/retention/__tests__/archive.test.ts +316 -0
- package/src/session/retention/__tests__/disk-backend.test.ts +180 -0
- package/src/session/retention/archive-backend-ref.ts +17 -0
- package/src/session/retention/archive.ts +281 -0
- package/src/session/retention/backend.ts +107 -0
- package/src/session/retention/disk-backend.ts +304 -0
- package/src/session/retention/index.ts +16 -0
- package/src/session/retention/policy.ts +53 -0
- package/src/session/summary/__tests__/materialize.test.ts +341 -0
- package/src/session/summary/deliverable.ts +84 -0
- package/src/session/summary/index.ts +31 -0
- package/src/session/summary/materialize.ts +169 -0
- package/src/session/summary/ref.ts +104 -0
- package/src/session/workspace/__tests__/git-worktree.test.ts +258 -0
- package/src/session/workspace/__tests__/path-builder.test.ts +51 -0
- package/src/session/workspace/driver.ts +60 -0
- package/src/session/workspace/git-worktree.ts +209 -0
- package/src/session/workspace/index.ts +25 -0
- package/src/session/workspace/path-builder.ts +71 -0
- package/src/session/workspace/ref.ts +50 -0
- package/src/session/workspace/registry.ts +42 -0
- package/src/store/conversation/memory.ts +23 -0
- package/src/store/session/__tests__/disk.test.ts +346 -0
- package/src/store/session/__tests__/memory.test.ts +327 -0
- package/src/store/session/disk.ts +920 -0
- package/src/store/session/index.ts +14 -0
- package/src/store/session/linkage.ts +80 -0
- package/src/store/session/memory.ts +400 -0
- package/src/store/session/messages.ts +21 -0
- package/src/types/agent/base.ts +31 -1
- package/src/types/agent/task.ts +58 -2
- package/src/types/conversation/index.ts +7 -0
- package/src/types/ids/index.ts +41 -3
- package/src/types/invocation/__tests__/state.test.ts +37 -29
- package/src/types/invocation/index.ts +26 -10
- package/src/types/run/config.ts +12 -1
- package/src/types/run/events.ts +36 -1
- package/src/types/run/index.ts +8 -0
- package/src/types/run/metadata.ts +24 -1
- package/src/types/run/status.ts +33 -0
- package/src/types/session/ids.ts +34 -0
- package/src/types/session/index.ts +28 -0
- package/src/types/session/store.ts +229 -0
- package/src/utils/id.ts +55 -4
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Sub-barrel for the retention/archival module (Convention #4).
|
|
2
|
+
// Concrete types + manager live in sibling files.
|
|
3
|
+
|
|
4
|
+
export type { ArchiveBackendRef } from './archive-backend-ref.js'
|
|
5
|
+
export type { RetentionPolicy } from './policy.js'
|
|
6
|
+
export { RETENTION_POLICY_DISABLED } from './policy.js'
|
|
7
|
+
export type { ArchiveBackend, ArchiveInput, ArchiveOutput, SubSessionTombstone } from './backend.js'
|
|
8
|
+
export {
|
|
9
|
+
ArchivalManager,
|
|
10
|
+
ArchiveNotConfiguredError,
|
|
11
|
+
SubSessionNotArchivableError,
|
|
12
|
+
SubSessionNotArchivedError,
|
|
13
|
+
} from './archive.js'
|
|
14
|
+
export type { ArchivalManagerDeps, WorkspaceResolver } from './archive.js'
|
|
15
|
+
export { ArchiveNotFoundError, DiskArchiveBackend } from './disk-backend.js'
|
|
16
|
+
export type { DiskArchiveBackendConfig } from './disk-backend.js'
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RetentionPolicy — per-project configuration for sub-session retention and
|
|
3
|
+
* archival. See session-hierarchy.md §12.3.
|
|
4
|
+
*
|
|
5
|
+
* Deny-by-default (Convention #5): the absence of any field means "no
|
|
6
|
+
* automatic archival." A policy without an `archiveBackend` cannot drive
|
|
7
|
+
* archival at all — {@link ArchivalManager.archive} rejects with
|
|
8
|
+
* {@link ArchiveNotConfiguredError}. The primitive Phase 8 ships is
|
|
9
|
+
* on-demand `archive(subSessionId)`; a scheduler that invokes the primitive
|
|
10
|
+
* is platform-layer (out of SDK scope per the roadmap "What's OUT" list).
|
|
11
|
+
*
|
|
12
|
+
* Phase 8 replaces the Phase 1 `RetentionPolicyRef = unknown` placeholder at
|
|
13
|
+
* `session/hierarchy/project.ts` with this real type.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { ArchiveBackendRef } from './archive-backend-ref.js'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Retention policy. Every field is optional (deny-by-default): no policy, no
|
|
20
|
+
* archival. See pattern doc §12.3 for rationale.
|
|
21
|
+
*/
|
|
22
|
+
export interface RetentionPolicy {
|
|
23
|
+
/**
|
|
24
|
+
* Idle sub-sessions older than this are eligible for archival. Expressed in
|
|
25
|
+
* milliseconds to stay dependency-free — no `Duration` type in the kernel
|
|
26
|
+
* today. Absent field = no TTL (eligibility never triggers on age alone).
|
|
27
|
+
*/
|
|
28
|
+
readonly idleSubSessionTTL?: number
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Hard cap on sub-sessions per project. When exceeded, the oldest-idle
|
|
32
|
+
* sub-sessions become archival-eligible. Absent = no cap.
|
|
33
|
+
*
|
|
34
|
+
* Note: enforcement of this cap is platform-layer (scheduler territory).
|
|
35
|
+
* The SDK primitive `archive()` takes an explicit {@link SubSessionId} —
|
|
36
|
+
* it does not walk the project tree.
|
|
37
|
+
*/
|
|
38
|
+
readonly maxSubSessionsPerProject?: number
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Ref for the backing {@link ArchiveBackend}. Absent = archival disabled
|
|
42
|
+
* even if TTL/cap are set — {@link ArchivalManager.archive} rejects with
|
|
43
|
+
* {@link ArchiveNotConfiguredError}. This is the deny-by-default gate.
|
|
44
|
+
*/
|
|
45
|
+
readonly archiveBackend?: ArchiveBackendRef
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The empty retention policy — archival fully disabled. Exported as a named
|
|
50
|
+
* constant so call sites declare their deny-by-default posture explicitly
|
|
51
|
+
* rather than relying on `{}` literals (Convention #0, #5).
|
|
52
|
+
*/
|
|
53
|
+
export const RETENTION_POLICY_DISABLED: RetentionPolicy = Object.freeze({})
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { TenantIsolationError } from '../../../session/errors.js'
|
|
3
|
+
import type { ActorRef } from '../../../session/hierarchy/actor.js'
|
|
4
|
+
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
5
|
+
import type { AgentId, SessionId, TenantId, UserId } from '../../../types/ids/index.js'
|
|
6
|
+
import type { SummaryId } from '../../../types/session/ids.js'
|
|
7
|
+
import type { DeliverableRef } from '../deliverable.js'
|
|
8
|
+
import { SessionSummaryMaterializer } from '../materialize.js'
|
|
9
|
+
import {
|
|
10
|
+
AGENT_SUMMARY_MAX_CHARS,
|
|
11
|
+
AgentSummaryTooLongError,
|
|
12
|
+
SessionAlreadySummarizedError,
|
|
13
|
+
} from '../ref.js'
|
|
14
|
+
|
|
15
|
+
const tenantA = 'tnt_alpha' as TenantId
|
|
16
|
+
const tenantB = 'tnt_beta' as TenantId
|
|
17
|
+
|
|
18
|
+
function userActor(tenantId: TenantId): ActorRef {
|
|
19
|
+
return { kind: 'user', userId: 'usr_a' as UserId, tenantId }
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function agentActor(tenantId: TenantId): ActorRef {
|
|
23
|
+
return { kind: 'agent', agentId: 'agt_a' as AgentId, tenantId }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function makeSummaryIdGenerator(): () => SummaryId {
|
|
27
|
+
let n = 0
|
|
28
|
+
return (): SummaryId => `sum_test_${++n}` as SummaryId
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function seedActiveSession(store: InMemorySessionStore, tenantId: TenantId) {
|
|
32
|
+
const project = await store.createProject({ tenantId, name: 'p1' }, tenantId)
|
|
33
|
+
const session = await store.createSession(
|
|
34
|
+
{ projectId: project.id, currentActor: agentActor(tenantId) },
|
|
35
|
+
tenantId,
|
|
36
|
+
)
|
|
37
|
+
// Put the session into `active` so the materializer's status-flip behavior
|
|
38
|
+
// is observable.
|
|
39
|
+
await store.updateSession({ ...session, status: 'active' }, tenantId)
|
|
40
|
+
return { project, session: { ...session, status: 'active' as const } }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function buildMaterializer(store: InMemorySessionStore) {
|
|
44
|
+
return new SessionSummaryMaterializer({
|
|
45
|
+
store,
|
|
46
|
+
generateSummaryId: makeSummaryIdGenerator(),
|
|
47
|
+
now: () => new Date('2026-04-17T00:00:00Z'),
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('SessionSummaryMaterializer.materialize', () => {
|
|
52
|
+
it('materializes on an active session and flips status to idle', async () => {
|
|
53
|
+
const store = new InMemorySessionStore()
|
|
54
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
55
|
+
const materializer = buildMaterializer(store)
|
|
56
|
+
|
|
57
|
+
const summary = await materializer.materialize({
|
|
58
|
+
sessionId: session.id,
|
|
59
|
+
tenantId: tenantA,
|
|
60
|
+
finalOutcome: { status: 'succeeded', verdict: 'done' },
|
|
61
|
+
agentSummary: 'Completed task X.',
|
|
62
|
+
declaredDeliverables: [],
|
|
63
|
+
keyDecisions: [],
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
expect(summary.id).toBe('sum_test_1')
|
|
67
|
+
expect(summary.sessionRef).toBe(session.id)
|
|
68
|
+
expect(summary.materializedBy).toBe('kernel')
|
|
69
|
+
|
|
70
|
+
const stored = await store.getSummary(session.id, tenantA)
|
|
71
|
+
expect(stored?.id).toBe(summary.id)
|
|
72
|
+
|
|
73
|
+
const reloaded = await store.getSession(session.id, tenantA)
|
|
74
|
+
expect(reloaded?.status).toBe('idle')
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
it('rejects an agentSummary exceeding AGENT_SUMMARY_MAX_CHARS', async () => {
|
|
78
|
+
const store = new InMemorySessionStore()
|
|
79
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
80
|
+
const materializer = buildMaterializer(store)
|
|
81
|
+
|
|
82
|
+
await expect(
|
|
83
|
+
materializer.materialize({
|
|
84
|
+
sessionId: session.id,
|
|
85
|
+
tenantId: tenantA,
|
|
86
|
+
finalOutcome: { status: 'succeeded' },
|
|
87
|
+
agentSummary: 'x'.repeat(AGENT_SUMMARY_MAX_CHARS + 1),
|
|
88
|
+
declaredDeliverables: [],
|
|
89
|
+
keyDecisions: [],
|
|
90
|
+
}),
|
|
91
|
+
).rejects.toBeInstanceOf(AgentSummaryTooLongError)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('rejects re-materialization when a summary already exists', async () => {
|
|
95
|
+
const store = new InMemorySessionStore()
|
|
96
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
97
|
+
const materializer = buildMaterializer(store)
|
|
98
|
+
|
|
99
|
+
await materializer.materialize({
|
|
100
|
+
sessionId: session.id,
|
|
101
|
+
tenantId: tenantA,
|
|
102
|
+
finalOutcome: { status: 'succeeded' },
|
|
103
|
+
agentSummary: 'first',
|
|
104
|
+
declaredDeliverables: [],
|
|
105
|
+
keyDecisions: [],
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
await expect(
|
|
109
|
+
materializer.materialize({
|
|
110
|
+
sessionId: session.id,
|
|
111
|
+
tenantId: tenantA,
|
|
112
|
+
finalOutcome: { status: 'succeeded' },
|
|
113
|
+
agentSummary: 'second',
|
|
114
|
+
declaredDeliverables: [],
|
|
115
|
+
keyDecisions: [],
|
|
116
|
+
}),
|
|
117
|
+
).rejects.toBeInstanceOf(SessionAlreadySummarizedError)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('rejects cross-tenant materialize with TenantIsolationError', async () => {
|
|
121
|
+
const store = new InMemorySessionStore()
|
|
122
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
123
|
+
const materializer = buildMaterializer(store)
|
|
124
|
+
|
|
125
|
+
await expect(
|
|
126
|
+
materializer.materialize({
|
|
127
|
+
sessionId: session.id,
|
|
128
|
+
tenantId: tenantB,
|
|
129
|
+
finalOutcome: { status: 'succeeded' },
|
|
130
|
+
agentSummary: 'x',
|
|
131
|
+
declaredDeliverables: [],
|
|
132
|
+
keyDecisions: [],
|
|
133
|
+
}),
|
|
134
|
+
).rejects.toBeInstanceOf(TenantIsolationError)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
it('preserves declaredDeliverables verbatim in the emitted ref', async () => {
|
|
138
|
+
const store = new InMemorySessionStore()
|
|
139
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
140
|
+
const materializer = buildMaterializer(store)
|
|
141
|
+
|
|
142
|
+
const deliverables: DeliverableRef[] = [
|
|
143
|
+
{
|
|
144
|
+
id: 'del_a' as DeliverableRef['id'],
|
|
145
|
+
kind: 'file',
|
|
146
|
+
path: 'src/foo.ts',
|
|
147
|
+
contentHash: 'abc123',
|
|
148
|
+
sizeBytes: 42,
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: 'del_b' as DeliverableRef['id'],
|
|
152
|
+
kind: 'artifact_blob',
|
|
153
|
+
storageRef: 'blob://x',
|
|
154
|
+
mediaType: 'application/json',
|
|
155
|
+
},
|
|
156
|
+
]
|
|
157
|
+
|
|
158
|
+
const summary = await materializer.materialize({
|
|
159
|
+
sessionId: session.id,
|
|
160
|
+
tenantId: tenantA,
|
|
161
|
+
finalOutcome: { status: 'partial' },
|
|
162
|
+
agentSummary: '',
|
|
163
|
+
declaredDeliverables: deliverables,
|
|
164
|
+
keyDecisions: [],
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
expect(summary.deliverables).toEqual(deliverables)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('preserves keyDecisions verbatim in the emitted ref', async () => {
|
|
171
|
+
const store = new InMemorySessionStore()
|
|
172
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
173
|
+
const materializer = buildMaterializer(store)
|
|
174
|
+
|
|
175
|
+
const decisions = [
|
|
176
|
+
{ at: new Date('2026-04-17T01:00:00Z'), summary: 'plan approved' },
|
|
177
|
+
{ at: new Date('2026-04-17T02:00:00Z'), summary: 'hitl granted' },
|
|
178
|
+
]
|
|
179
|
+
|
|
180
|
+
const summary = await materializer.materialize({
|
|
181
|
+
sessionId: session.id,
|
|
182
|
+
tenantId: tenantA,
|
|
183
|
+
finalOutcome: { status: 'succeeded' },
|
|
184
|
+
agentSummary: '',
|
|
185
|
+
declaredDeliverables: [],
|
|
186
|
+
keyDecisions: decisions,
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
expect(summary.keyDecisions).toEqual(decisions)
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
it('constructs SummaryId via the injected generator', async () => {
|
|
193
|
+
const store = new InMemorySessionStore()
|
|
194
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
195
|
+
|
|
196
|
+
const gen = makeSummaryIdGenerator()
|
|
197
|
+
const materializer = new SessionSummaryMaterializer({
|
|
198
|
+
store,
|
|
199
|
+
generateSummaryId: gen,
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
const summary = await materializer.materialize({
|
|
203
|
+
sessionId: session.id,
|
|
204
|
+
tenantId: tenantA,
|
|
205
|
+
finalOutcome: { status: 'succeeded' },
|
|
206
|
+
agentSummary: '',
|
|
207
|
+
declaredDeliverables: [],
|
|
208
|
+
keyDecisions: [],
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
expect(summary.id).toBe('sum_test_1')
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('always sets materializedBy to "kernel"', async () => {
|
|
215
|
+
const store = new InMemorySessionStore()
|
|
216
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
217
|
+
const materializer = buildMaterializer(store)
|
|
218
|
+
|
|
219
|
+
const summary = await materializer.materialize({
|
|
220
|
+
sessionId: session.id,
|
|
221
|
+
tenantId: tenantA,
|
|
222
|
+
finalOutcome: { status: 'succeeded' },
|
|
223
|
+
agentSummary: '',
|
|
224
|
+
declaredDeliverables: [],
|
|
225
|
+
keyDecisions: [],
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
expect(summary.materializedBy).toBe('kernel')
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('leaves already-idle sessions in idle (no spurious flip)', async () => {
|
|
232
|
+
const store = new InMemorySessionStore()
|
|
233
|
+
const project = await store.createProject({ tenantId: tenantA, name: 'p1' }, tenantA)
|
|
234
|
+
const session = await store.createSession(
|
|
235
|
+
{ projectId: project.id, currentActor: userActor(tenantA) },
|
|
236
|
+
tenantA,
|
|
237
|
+
)
|
|
238
|
+
// session.status defaults to 'idle'
|
|
239
|
+
const materializer = buildMaterializer(store)
|
|
240
|
+
|
|
241
|
+
await materializer.materialize({
|
|
242
|
+
sessionId: session.id,
|
|
243
|
+
tenantId: tenantA,
|
|
244
|
+
finalOutcome: { status: 'succeeded' },
|
|
245
|
+
agentSummary: '',
|
|
246
|
+
declaredDeliverables: [],
|
|
247
|
+
keyDecisions: [],
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
const reloaded = await store.getSession(session.id, tenantA)
|
|
251
|
+
expect(reloaded?.status).toBe('idle')
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
it('leaves failed sessions in failed (materialize does not resurrect)', async () => {
|
|
255
|
+
const store = new InMemorySessionStore()
|
|
256
|
+
const project = await store.createProject({ tenantId: tenantA, name: 'p1' }, tenantA)
|
|
257
|
+
const session = await store.createSession(
|
|
258
|
+
{ projectId: project.id, currentActor: agentActor(tenantA) },
|
|
259
|
+
tenantA,
|
|
260
|
+
)
|
|
261
|
+
await store.updateSession({ ...session, status: 'failed' }, tenantA)
|
|
262
|
+
const materializer = buildMaterializer(store)
|
|
263
|
+
|
|
264
|
+
await materializer.materialize({
|
|
265
|
+
sessionId: session.id,
|
|
266
|
+
tenantId: tenantA,
|
|
267
|
+
finalOutcome: { status: 'failed', verdict: 'gave up' },
|
|
268
|
+
agentSummary: '',
|
|
269
|
+
declaredDeliverables: [],
|
|
270
|
+
keyDecisions: [],
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
const reloaded = await store.getSession(session.id, tenantA)
|
|
274
|
+
expect(reloaded?.status).toBe('failed')
|
|
275
|
+
})
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
describe('SessionSummaryMaterializer.recover', () => {
|
|
279
|
+
it('returns null when no summary file exists (no side effect)', async () => {
|
|
280
|
+
const store = new InMemorySessionStore()
|
|
281
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
282
|
+
const materializer = buildMaterializer(store)
|
|
283
|
+
|
|
284
|
+
const recovered = await materializer.recover(session.id, tenantA)
|
|
285
|
+
expect(recovered).toBeNull()
|
|
286
|
+
|
|
287
|
+
const reloaded = await store.getSession(session.id, tenantA)
|
|
288
|
+
// No side effect — still active.
|
|
289
|
+
expect(reloaded?.status).toBe('active')
|
|
290
|
+
})
|
|
291
|
+
|
|
292
|
+
it('flips dangling session status idempotently when summary is present', async () => {
|
|
293
|
+
const store = new InMemorySessionStore()
|
|
294
|
+
const { session } = await seedActiveSession(store, tenantA)
|
|
295
|
+
const materializer = buildMaterializer(store)
|
|
296
|
+
|
|
297
|
+
// Simulate mid-crash: write summary directly, then force session back to
|
|
298
|
+
// active (mimicking a process death between the two atomic writes).
|
|
299
|
+
await materializer.materialize({
|
|
300
|
+
sessionId: session.id,
|
|
301
|
+
tenantId: tenantA,
|
|
302
|
+
finalOutcome: { status: 'succeeded' },
|
|
303
|
+
agentSummary: '',
|
|
304
|
+
declaredDeliverables: [],
|
|
305
|
+
keyDecisions: [],
|
|
306
|
+
})
|
|
307
|
+
const mid = await store.getSession(session.id, tenantA)
|
|
308
|
+
if (!mid) throw new Error('seed mid session missing')
|
|
309
|
+
await store.updateSession({ ...mid, status: 'active' }, tenantA)
|
|
310
|
+
|
|
311
|
+
const recovered = await materializer.recover(session.id, tenantA)
|
|
312
|
+
expect(recovered).not.toBeNull()
|
|
313
|
+
|
|
314
|
+
const reloaded = await store.getSession(session.id, tenantA)
|
|
315
|
+
expect(reloaded?.status).toBe('idle')
|
|
316
|
+
|
|
317
|
+
// Idempotent: calling recover again with session already idle is a no-op.
|
|
318
|
+
const recovered2 = await materializer.recover(session.id, tenantA)
|
|
319
|
+
expect(recovered2?.id).toBe(recovered?.id)
|
|
320
|
+
const reloaded2 = await store.getSession(session.id, tenantA)
|
|
321
|
+
expect(reloaded2?.status).toBe('idle')
|
|
322
|
+
})
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
describe('SessionSummaryMaterializer missing session', () => {
|
|
326
|
+
it('throws when the session does not exist', async () => {
|
|
327
|
+
const store = new InMemorySessionStore()
|
|
328
|
+
const materializer = buildMaterializer(store)
|
|
329
|
+
|
|
330
|
+
await expect(
|
|
331
|
+
materializer.materialize({
|
|
332
|
+
sessionId: 'ses_missing' as SessionId,
|
|
333
|
+
tenantId: tenantA,
|
|
334
|
+
finalOutcome: { status: 'succeeded' },
|
|
335
|
+
agentSummary: '',
|
|
336
|
+
declaredDeliverables: [],
|
|
337
|
+
keyDecisions: [],
|
|
338
|
+
}),
|
|
339
|
+
).rejects.toThrow(/not found/)
|
|
340
|
+
})
|
|
341
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DeliverableRef — discriminated union of artifact types a completed
|
|
3
|
+
* sub-session may emit.
|
|
4
|
+
*
|
|
5
|
+
* Consumed by two surfaces:
|
|
6
|
+
* 1. `SessionSummaryRef.deliverables` — authoritative list of artifacts a
|
|
7
|
+
* completed sub-session produced. See session-hierarchy.md §4.7.
|
|
8
|
+
* 2. `SubSession.prevArtifactRef` — intervention chains reference a prior
|
|
9
|
+
* immutable artifact. See session-hierarchy.md §4.5, §8.1.
|
|
10
|
+
*
|
|
11
|
+
* The `session_summary` variant is what intervention sub-sessions use — a
|
|
12
|
+
* follow-up intervention points at the previous completed session's summary,
|
|
13
|
+
* forming a strict acyclic DAG validated by
|
|
14
|
+
* `session/intervention/prev-artifact.ts`.
|
|
15
|
+
*
|
|
16
|
+
* Convention #6: this is a discriminated union on `kind`; any consumer must
|
|
17
|
+
* handle all variants exhaustively.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { RunId, SessionId } from '../../types/ids/index.js'
|
|
21
|
+
import type { DeliverableId, SummaryId } from '../../types/session/ids.js'
|
|
22
|
+
|
|
23
|
+
/** Discriminator for {@link DeliverableRef}. */
|
|
24
|
+
export type DeliverableKind = 'file' | 'session_summary' | 'message' | 'artifact_blob'
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* A file deliverable — a path relative to the workspace root, plus a content
|
|
28
|
+
* hash pinning the file to its state at materialize time. Subsequent edits to
|
|
29
|
+
* the file on disk do not mutate the deliverable; the ref is a snapshot.
|
|
30
|
+
*/
|
|
31
|
+
export interface FileDeliverable {
|
|
32
|
+
readonly id: DeliverableId
|
|
33
|
+
readonly kind: 'file'
|
|
34
|
+
/** Path relative to the owning session's workspace root. */
|
|
35
|
+
readonly path: string
|
|
36
|
+
/** sha256 of the file's content at materialize time. */
|
|
37
|
+
readonly contentHash: string
|
|
38
|
+
readonly sizeBytes: number
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* A pointer to a previously-materialized {@link SessionSummaryRef}. The
|
|
43
|
+
* intervention DAG uses this variant to reference the immutable output of a
|
|
44
|
+
* prior completed session.
|
|
45
|
+
*/
|
|
46
|
+
export interface SessionSummaryDeliverable {
|
|
47
|
+
readonly id: DeliverableId
|
|
48
|
+
readonly kind: 'session_summary'
|
|
49
|
+
readonly sessionId: SessionId
|
|
50
|
+
readonly summaryRef: SummaryId
|
|
51
|
+
readonly at: Date
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* A specific persisted message within a session run — useful for referencing
|
|
56
|
+
* a single LLM turn as an artifact (e.g. a decision record).
|
|
57
|
+
*/
|
|
58
|
+
export interface MessageDeliverable {
|
|
59
|
+
readonly id: DeliverableId
|
|
60
|
+
readonly kind: 'message'
|
|
61
|
+
readonly sessionId: SessionId
|
|
62
|
+
readonly runId: RunId
|
|
63
|
+
/** Opaque identifier bound to the run's persisted message log. */
|
|
64
|
+
readonly messageId: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* An opaque blob deliverable — content lives in an out-of-band storage
|
|
69
|
+
* backend (object store, artifact registry). The SDK does not fetch or
|
|
70
|
+
* validate blob contents; callers resolve via the `storageRef`.
|
|
71
|
+
*/
|
|
72
|
+
export interface ArtifactBlobDeliverable {
|
|
73
|
+
readonly id: DeliverableId
|
|
74
|
+
readonly kind: 'artifact_blob'
|
|
75
|
+
/** Opaque — bound to a storage backend the consumer owns. */
|
|
76
|
+
readonly storageRef: string
|
|
77
|
+
readonly mediaType?: string
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export type DeliverableRef =
|
|
81
|
+
| FileDeliverable
|
|
82
|
+
| SessionSummaryDeliverable
|
|
83
|
+
| MessageDeliverable
|
|
84
|
+
| ArtifactBlobDeliverable
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Sub-barrel for the session summary module (Convention #4).
|
|
2
|
+
//
|
|
3
|
+
// See session-hierarchy.md §4.7 (SessionSummaryRef shape) and §8.1
|
|
4
|
+
// (kernel-only emission invariant). Concrete types and the materializer live
|
|
5
|
+
// in sibling files; re-export them here so consumers import via
|
|
6
|
+
// `../session/summary/index.js`.
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
ArtifactBlobDeliverable,
|
|
10
|
+
DeliverableKind,
|
|
11
|
+
DeliverableRef,
|
|
12
|
+
FileDeliverable,
|
|
13
|
+
MessageDeliverable,
|
|
14
|
+
SessionSummaryDeliverable,
|
|
15
|
+
} from './deliverable.js'
|
|
16
|
+
export {
|
|
17
|
+
AGENT_SUMMARY_MAX_CHARS,
|
|
18
|
+
AgentSummaryTooLongError,
|
|
19
|
+
SessionAlreadySummarizedError,
|
|
20
|
+
} from './ref.js'
|
|
21
|
+
export type {
|
|
22
|
+
SessionSummaryKeyDecision,
|
|
23
|
+
SessionSummaryOutcome,
|
|
24
|
+
SessionSummaryOutcomeStatus,
|
|
25
|
+
SessionSummaryRef,
|
|
26
|
+
} from './ref.js'
|
|
27
|
+
export { SessionSummaryMaterializer } from './materialize.js'
|
|
28
|
+
export type {
|
|
29
|
+
MaterializeInput,
|
|
30
|
+
SessionSummaryMaterializerDeps,
|
|
31
|
+
} from './materialize.js'
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionSummaryMaterializer — kernel terminalization primitive.
|
|
3
|
+
*
|
|
4
|
+
* The sole producer of {@link SessionSummaryRef}. No agent-callable surface
|
|
5
|
+
* exists to bypass this (Convention #0 load-bearing — see
|
|
6
|
+
* session-hierarchy.md §8.1).
|
|
7
|
+
*
|
|
8
|
+
* Atomicity contract (§8.1): the summary write and the owning session's
|
|
9
|
+
* `non-terminal → idle` status flip commit as one logical unit. In the
|
|
10
|
+
* InMemory store the two happen within the same method call under the store's
|
|
11
|
+
* Map guard; in the Disk store they are two sequential write-tmp-renames
|
|
12
|
+
* (summary.json first, then session.json). A mid-step crash leaves the
|
|
13
|
+
* summary persisted but the session still active — `recover()` replays the
|
|
14
|
+
* session-status flip idempotently on next boot.
|
|
15
|
+
*
|
|
16
|
+
* The class is function-call-style (no internal state beyond deps). Tests
|
|
17
|
+
* inject a fake generator for deterministic IDs; production uses
|
|
18
|
+
* `generateSummaryId` from `utils/id.ts`.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { SessionId, TenantId } from '../../types/ids/index.js'
|
|
22
|
+
import type { SummaryId } from '../../types/session/ids.js'
|
|
23
|
+
import type { SessionStore } from '../../types/session/store.js'
|
|
24
|
+
import type { Session } from '../hierarchy/session.js'
|
|
25
|
+
import type { DeliverableRef } from './deliverable.js'
|
|
26
|
+
import {
|
|
27
|
+
AGENT_SUMMARY_MAX_CHARS,
|
|
28
|
+
AgentSummaryTooLongError,
|
|
29
|
+
SessionAlreadySummarizedError,
|
|
30
|
+
type SessionSummaryKeyDecision,
|
|
31
|
+
type SessionSummaryOutcome,
|
|
32
|
+
type SessionSummaryRef,
|
|
33
|
+
} from './ref.js'
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Dependencies for {@link SessionSummaryMaterializer}. The generator is
|
|
37
|
+
* injected so tests can produce deterministic IDs; production code wires
|
|
38
|
+
* `generateSummaryId` from `utils/id.ts`.
|
|
39
|
+
*/
|
|
40
|
+
export interface SessionSummaryMaterializerDeps {
|
|
41
|
+
readonly store: SessionStore
|
|
42
|
+
readonly generateSummaryId: () => SummaryId
|
|
43
|
+
/** Clock hook — defaults to `new Date()` when omitted. */
|
|
44
|
+
readonly now?: () => Date
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Caller-supplied payload for {@link SessionSummaryMaterializer.materialize}.
|
|
49
|
+
* The materializer validates `agentSummary` length, constructs the
|
|
50
|
+
* `SessionSummaryRef`, and delegates the atomic write to the store.
|
|
51
|
+
*/
|
|
52
|
+
export interface MaterializeInput {
|
|
53
|
+
readonly sessionId: SessionId
|
|
54
|
+
readonly tenantId: TenantId
|
|
55
|
+
readonly finalOutcome: SessionSummaryOutcome
|
|
56
|
+
readonly agentSummary: string
|
|
57
|
+
readonly declaredDeliverables: readonly DeliverableRef[]
|
|
58
|
+
readonly keyDecisions: readonly SessionSummaryKeyDecision[]
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Session statuses from which a materialize call may legally transition to
|
|
63
|
+
* `'idle'` as part of the terminalization. Other states (already `'idle'`,
|
|
64
|
+
* `'failed'`, `'archived'`, `'awaiting_hitl'`) are left untouched; the
|
|
65
|
+
* materializer never moves a session *out of* `'failed'` or `'archived'`.
|
|
66
|
+
*/
|
|
67
|
+
const TERMINALIZABLE_STATUSES: ReadonlySet<Session['status']> = new Set([
|
|
68
|
+
'active',
|
|
69
|
+
'locked',
|
|
70
|
+
'awaiting_merge',
|
|
71
|
+
])
|
|
72
|
+
|
|
73
|
+
export class SessionSummaryMaterializer {
|
|
74
|
+
private readonly deps: SessionSummaryMaterializerDeps
|
|
75
|
+
|
|
76
|
+
constructor(deps: SessionSummaryMaterializerDeps) {
|
|
77
|
+
this.deps = deps
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Kernel emission path. Validates, builds the immutable
|
|
82
|
+
* {@link SessionSummaryRef}, and hands it to the store for atomic write +
|
|
83
|
+
* status-flip. Rejects with:
|
|
84
|
+
*
|
|
85
|
+
* - {@link AgentSummaryTooLongError} — `agentSummary` exceeds the max char
|
|
86
|
+
* cap ({@link AGENT_SUMMARY_MAX_CHARS}).
|
|
87
|
+
* - `TenantIsolationError` — session is owned by a different tenant.
|
|
88
|
+
* - {@link SessionAlreadySummarizedError} — the session already has a
|
|
89
|
+
* persisted summary. Re-materialization would duplicate history; the
|
|
90
|
+
* caller should instead open an intervention sub-session (§4.5).
|
|
91
|
+
*/
|
|
92
|
+
async materialize(input: MaterializeInput): Promise<SessionSummaryRef> {
|
|
93
|
+
this.assertSummaryLength(input.agentSummary)
|
|
94
|
+
|
|
95
|
+
const session = await this.deps.store.getSession(input.sessionId, input.tenantId)
|
|
96
|
+
if (!session) {
|
|
97
|
+
throw new Error(`Session ${input.sessionId} not found`)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const existing = await this.deps.store.getSummary(input.sessionId, input.tenantId)
|
|
101
|
+
if (existing) {
|
|
102
|
+
throw new SessionAlreadySummarizedError({
|
|
103
|
+
sessionId: input.sessionId,
|
|
104
|
+
existingSummaryId: existing.id,
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const summary: SessionSummaryRef = {
|
|
109
|
+
id: this.deps.generateSummaryId(),
|
|
110
|
+
sessionRef: input.sessionId,
|
|
111
|
+
tenantId: input.tenantId,
|
|
112
|
+
outcome: input.finalOutcome,
|
|
113
|
+
deliverables: input.declaredDeliverables,
|
|
114
|
+
agentSummary: input.agentSummary,
|
|
115
|
+
keyDecisions: input.keyDecisions,
|
|
116
|
+
at: this.clock(),
|
|
117
|
+
materializedBy: 'kernel',
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
await this.deps.store.recordSummary(summary, input.tenantId)
|
|
121
|
+
return summary
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Recovery path. Called at boot (or explicitly by the lifecycle manager)
|
|
126
|
+
* for sessions whose `summary.json` is persisted but whose `session.json`
|
|
127
|
+
* still reports a non-terminal status — the crash window between the two
|
|
128
|
+
* atomic writes on disk.
|
|
129
|
+
*
|
|
130
|
+
* Idempotent: if the session is already `'idle'` (or another terminal
|
|
131
|
+
* state), no write occurs; the existing summary is returned. If no summary
|
|
132
|
+
* is persisted, returns `null` and no side effect occurs. This is the only
|
|
133
|
+
* non-materialize path that may touch the stored summary — it does not
|
|
134
|
+
* mint a new ID, only re-triggers the store's status flip via
|
|
135
|
+
* `recordSummary` when it detects the dangling session.
|
|
136
|
+
*/
|
|
137
|
+
async recover(sessionId: SessionId, tenantId: TenantId): Promise<SessionSummaryRef | null> {
|
|
138
|
+
const summary = await this.deps.store.getSummary(sessionId, tenantId)
|
|
139
|
+
if (!summary) return null
|
|
140
|
+
|
|
141
|
+
const session = await this.deps.store.getSession(sessionId, tenantId)
|
|
142
|
+
if (!session) return summary
|
|
143
|
+
|
|
144
|
+
if (TERMINALIZABLE_STATUSES.has(session.status)) {
|
|
145
|
+
// Dangling: summary persisted but session never flipped. Replay the
|
|
146
|
+
// status transition by re-invoking `recordSummary` — store impls
|
|
147
|
+
// treat an existing-summary path as "flip-only, no duplicate write".
|
|
148
|
+
// Cast preserves the kernel brand on the recovered record.
|
|
149
|
+
await this.deps.store.recordSummary(
|
|
150
|
+
summary as SessionSummaryRef & { materializedBy: 'kernel' },
|
|
151
|
+
tenantId,
|
|
152
|
+
)
|
|
153
|
+
}
|
|
154
|
+
return summary
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private assertSummaryLength(agentSummary: string): void {
|
|
158
|
+
if (agentSummary.length > AGENT_SUMMARY_MAX_CHARS) {
|
|
159
|
+
throw new AgentSummaryTooLongError({
|
|
160
|
+
actual: agentSummary.length,
|
|
161
|
+
max: AGENT_SUMMARY_MAX_CHARS,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private clock(): Date {
|
|
167
|
+
return this.deps.now ? this.deps.now() : new Date()
|
|
168
|
+
}
|
|
169
|
+
}
|