@namzu/sdk 0.2.0 → 0.4.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 +74 -2
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +3 -2
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +5 -2
- 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/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 +4 -3
- 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/contracts/api.d.ts +6 -38
- 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 -5
- 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/index.d.ts +2 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -6
- package/dist/index.js.map +1 -1
- package/dist/manager/agent/__tests__/lifecycle.test.js +27 -13
- package/dist/manager/agent/__tests__/lifecycle.test.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +9 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +93 -31
- 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 +3 -1
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +5 -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/registry/tool/execute.js +1 -1
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/runtime/query/__tests__/context.test.js +8 -7
- package/dist/runtime/query/__tests__/context.test.js.map +1 -1
- 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 +12 -21
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +3 -1
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +13 -15
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +2 -1
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +1 -1
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/session/__tests__/integration/_fixtures.d.ts +11 -4
- package/dist/session/__tests__/integration/_fixtures.d.ts.map +1 -1
- package/dist/session/__tests__/integration/_fixtures.js +23 -6
- package/dist/session/__tests__/integration/_fixtures.js.map +1 -1
- 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.js +13 -6
- package/dist/session/__tests__/integration/capacity-caps.test.js.map +1 -1
- package/dist/session/__tests__/integration/e2e-spawn.test.js +14 -2
- package/dist/session/__tests__/integration/e2e-spawn.test.js.map +1 -1
- package/dist/session/__tests__/integration/event-stream-ordering.test.js +14 -7
- package/dist/session/__tests__/integration/event-stream-ordering.test.js.map +1 -1
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js +26 -14
- package/dist/session/__tests__/integration/handoff-broadcast-e2e.test.js.map +1 -1
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js +30 -20
- package/dist/session/__tests__/integration/handoff-illegal-transition.test.js.map +1 -1
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js +25 -9
- package/dist/session/__tests__/integration/handoff-single-e2e.test.js.map +1 -1
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js +11 -10
- package/dist/session/__tests__/integration/hierarchy-lifecycle.test.js.map +1 -1
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js +5 -4
- package/dist/session/__tests__/integration/prev-artifact-dag.test.js.map +1 -1
- package/dist/session/__tests__/integration/retention-archive.test.js +3 -2
- package/dist/session/__tests__/integration/retention-archive.test.js.map +1 -1
- 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.js +2 -1
- package/dist/session/__tests__/integration/summary-materialization-e2e.test.js.map +1 -1
- package/dist/session/__tests__/integration/tenant-isolation.test.js +14 -5
- package/dist/session/__tests__/integration/tenant-isolation.test.js.map +1 -1
- package/dist/session/errors.d.ts +79 -0
- package/dist/session/errors.d.ts.map +1 -1
- package/dist/session/errors.js +57 -0
- package/dist/session/errors.js.map +1 -1
- package/dist/session/handoff/__tests__/broadcast.test.js +49 -31
- package/dist/session/handoff/__tests__/broadcast.test.js.map +1 -1
- package/dist/session/handoff/__tests__/capacity.test.js +21 -18
- package/dist/session/handoff/__tests__/capacity.test.js.map +1 -1
- package/dist/session/handoff/__tests__/single.test.js +39 -30
- package/dist/session/handoff/__tests__/single.test.js.map +1 -1
- package/dist/session/handoff/assignment.d.ts +13 -1
- package/dist/session/handoff/assignment.d.ts.map +1 -1
- package/dist/session/handoff/broadcast.d.ts +7 -0
- package/dist/session/handoff/broadcast.d.ts.map +1 -1
- package/dist/session/handoff/broadcast.js +16 -1
- package/dist/session/handoff/broadcast.js.map +1 -1
- package/dist/session/handoff/single.d.ts +7 -0
- package/dist/session/handoff/single.d.ts.map +1 -1
- package/dist/session/handoff/single.js +13 -1
- package/dist/session/handoff/single.js.map +1 -1
- package/dist/session/hierarchy/__tests__/session.test.js +2 -0
- package/dist/session/hierarchy/__tests__/session.test.js.map +1 -1
- package/dist/session/hierarchy/index.d.ts +1 -0
- package/dist/session/hierarchy/index.d.ts.map +1 -1
- package/dist/session/hierarchy/index.js.map +1 -1
- package/dist/session/hierarchy/session.d.ts +15 -3
- package/dist/session/hierarchy/session.d.ts.map +1 -1
- package/dist/session/hierarchy/session.js.map +1 -1
- 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/migration/id-prefix.d.ts +8 -13
- package/dist/session/migration/id-prefix.d.ts.map +1 -1
- package/dist/session/migration/id-prefix.js +8 -13
- package/dist/session/migration/id-prefix.js.map +1 -1
- package/dist/session/retention/__tests__/archive.test.js +3 -2
- package/dist/session/retention/__tests__/archive.test.js.map +1 -1
- package/dist/session/summary/__tests__/materialize.test.js +4 -3
- package/dist/session/summary/__tests__/materialize.test.js.map +1 -1
- 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.js +32 -5
- package/dist/store/session/__tests__/disk.test.js.map +1 -1
- package/dist/store/session/__tests__/memory.test.js +50 -9
- package/dist/store/session/__tests__/memory.test.js.map +1 -1
- package/dist/store/session/disk.d.ts +2 -1
- package/dist/store/session/disk.d.ts.map +1 -1
- package/dist/store/session/disk.js +61 -0
- package/dist/store/session/disk.js.map +1 -1
- package/dist/store/session/index.d.ts.map +1 -1
- package/dist/store/session/index.js +3 -4
- package/dist/store/session/index.js.map +1 -1
- package/dist/store/session/memory.d.ts +2 -1
- package/dist/store/session/memory.d.ts.map +1 -1
- package/dist/store/session/memory.js +13 -0
- package/dist/store/session/memory.js.map +1 -1
- 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/telemetry/runtime-accessors.d.ts +4 -0
- package/dist/telemetry/runtime-accessors.d.ts.map +1 -0
- package/dist/telemetry/runtime-accessors.js +17 -0
- package/dist/telemetry/runtime-accessors.js.map +1 -0
- package/dist/types/agent/base.d.ts +17 -21
- 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 +18 -11
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/ids/index.d.ts +5 -9
- package/dist/types/ids/index.d.ts.map +1 -1
- package/dist/types/ids/index.js +4 -4
- package/dist/types/ids/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 +6 -5
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/metadata.d.ts +5 -18
- package/dist/types/run/metadata.d.ts.map +1 -1
- package/dist/types/session/ids.d.ts +4 -13
- package/dist/types/session/ids.d.ts.map +1 -1
- package/dist/types/session/ids.js +3 -6
- package/dist/types/session/ids.js.map +1 -1
- package/dist/types/session/index.d.ts +1 -1
- package/dist/types/session/index.d.ts.map +1 -1
- package/dist/types/session/store.d.ts +32 -10
- package/dist/types/session/store.d.ts.map +1 -1
- package/dist/types/session/store.js +3 -8
- package/dist/types/session/store.js.map +1 -1
- 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 +1 -12
- package/dist/utils/id.d.ts.map +1 -1
- package/dist/utils/id.js +3 -23
- package/dist/utils/id.js.map +1 -1
- package/package.json +11 -20
- package/src/agents/ReactiveAgent.ts +3 -2
- package/src/agents/SupervisorAgent.ts +5 -2
- package/src/bridge/a2a/index.ts +0 -1
- package/src/bridge/a2a/message.ts +0 -32
- package/src/bridge/a2a/task.ts +8 -7
- package/src/contracts/api.ts +6 -42
- package/src/contracts/ids.ts +1 -1
- package/src/contracts/index.ts +2 -8
- package/src/contracts/schemas.ts +1 -8
- package/src/index.ts +3 -15
- package/src/manager/agent/__tests__/lifecycle.test.ts +34 -13
- package/src/manager/agent/lifecycle.ts +114 -35
- package/src/manager/index.ts +3 -0
- package/src/manager/run/persistence.ts +7 -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/registry/tool/execute.ts +1 -1
- package/src/runtime/query/__tests__/context.test.ts +9 -8
- package/src/runtime/query/context-cache.ts +4 -4
- package/src/runtime/query/context.ts +15 -22
- package/src/runtime/query/index.ts +16 -17
- package/src/runtime/query/iteration/index.ts +1 -1
- package/src/session/__tests__/integration/_fixtures.ts +36 -8
- package/src/session/__tests__/integration/archive-gate.test.ts +288 -0
- package/src/session/__tests__/integration/capacity-caps.test.ts +13 -6
- package/src/session/__tests__/integration/e2e-spawn.test.ts +20 -2
- package/src/session/__tests__/integration/event-stream-ordering.test.ts +14 -7
- package/src/session/__tests__/integration/handoff-broadcast-e2e.test.ts +39 -13
- package/src/session/__tests__/integration/handoff-illegal-transition.test.ts +54 -19
- package/src/session/__tests__/integration/handoff-single-e2e.test.ts +40 -9
- package/src/session/__tests__/integration/hierarchy-lifecycle.test.ts +13 -10
- package/src/session/__tests__/integration/prev-artifact-dag.test.ts +12 -5
- package/src/session/__tests__/integration/retention-archive.test.ts +5 -3
- package/src/session/__tests__/integration/spawn-rollback.test.ts +313 -0
- package/src/session/__tests__/integration/summary-materialization-e2e.test.ts +4 -2
- package/src/session/__tests__/integration/tenant-isolation.test.ts +16 -6
- package/src/session/errors.ts +89 -0
- package/src/session/handoff/__tests__/broadcast.test.ts +56 -28
- package/src/session/handoff/__tests__/capacity.test.ts +26 -20
- package/src/session/handoff/__tests__/single.test.ts +45 -28
- package/src/session/handoff/assignment.ts +13 -1
- package/src/session/handoff/broadcast.ts +26 -1
- package/src/session/handoff/single.ts +23 -1
- package/src/session/hierarchy/__tests__/session.test.ts +9 -1
- package/src/session/hierarchy/index.ts +1 -0
- package/src/session/hierarchy/session.ts +15 -3
- package/src/session/hierarchy/thread.ts +55 -0
- package/src/session/migration/id-prefix.ts +8 -13
- package/src/session/retention/__tests__/archive.test.ts +5 -3
- package/src/session/summary/__tests__/materialize.test.ts +6 -4
- package/src/store/index.ts +0 -3
- package/src/store/session/__tests__/disk.test.ts +57 -6
- package/src/store/session/__tests__/memory.test.ts +84 -9
- package/src/store/session/disk.ts +57 -1
- package/src/store/session/index.ts +3 -4
- package/src/store/session/memory.ts +13 -1
- 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/telemetry/runtime-accessors.ts +19 -0
- package/src/types/agent/base.ts +17 -21
- package/src/types/agent/factory.ts +8 -3
- package/src/types/agent/task.ts +19 -11
- package/src/types/ids/index.ts +8 -15
- package/src/types/rag/retrieval.ts +4 -3
- package/src/types/run/config.ts +6 -5
- package/src/types/run/metadata.ts +5 -18
- package/src/types/session/ids.ts +4 -15
- package/src/types/session/index.ts +1 -2
- package/src/types/session/store.ts +34 -11
- package/src/types/thread/index.ts +5 -0
- package/src/types/thread/store.ts +92 -0
- package/src/utils/id.ts +3 -24
- package/dist/provider/telemetry/setup.d.ts +0 -19
- package/dist/provider/telemetry/setup.d.ts.map +0 -1
- package/dist/provider/telemetry/setup.js +0 -102
- package/dist/provider/telemetry/setup.js.map +0 -1
- package/dist/store/conversation/memory.d.ts +0 -43
- package/dist/store/conversation/memory.d.ts.map +0 -1
- package/dist/store/conversation/memory.js +0 -108
- package/dist/store/conversation/memory.js.map +0 -1
- package/dist/telemetry/index.d.ts +0 -6
- package/dist/telemetry/index.d.ts.map +0 -1
- package/dist/telemetry/index.js +0 -4
- package/dist/telemetry/index.js.map +0 -1
- package/dist/telemetry/metrics.d.ts +0 -8
- package/dist/telemetry/metrics.d.ts.map +0 -1
- package/dist/telemetry/metrics.js +0 -53
- package/dist/telemetry/metrics.js.map +0 -1
- package/dist/types/conversation/index.d.ts +0 -14
- 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/dist/types/telemetry/index.d.ts +0 -10
- package/dist/types/telemetry/index.d.ts.map +0 -1
- package/dist/types/telemetry/index.js +0 -2
- package/dist/types/telemetry/index.js.map +0 -1
- package/src/provider/telemetry/setup.ts +0 -125
- package/src/store/conversation/memory.ts +0 -144
- package/src/telemetry/index.ts +0 -14
- package/src/telemetry/metrics.ts +0 -69
- package/src/types/conversation/index.ts +0 -15
- package/src/types/telemetry/index.ts +0 -10
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration — Thread archive gate enforced at session-creation ingress
|
|
3
|
+
* sites (Phase 2.6).
|
|
4
|
+
*
|
|
5
|
+
* The Phase 2.5 commit flagged this as a known gap:
|
|
6
|
+
* > spawn and handoff paths do not yet invoke `ThreadManager.requireOpen`
|
|
7
|
+
* > before creating a child session. Until they do, the archive invariant
|
|
8
|
+
* > is best-effort.
|
|
9
|
+
*
|
|
10
|
+
* Phase 2.6 wires `requireOpen` into `AgentManager.provisionSpawn` + both
|
|
11
|
+
* handoff flows. These tests drive the archived-thread-then-attempt-creation
|
|
12
|
+
* scenarios end-to-end against the real stack.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
16
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
17
|
+
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
18
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
19
|
+
import type { AgentId, RunId, UserId } from '../../../types/ids/index.js'
|
|
20
|
+
import { generateHandoffId } from '../../../utils/id.js'
|
|
21
|
+
import { ThreadClosedError } from '../../errors.js'
|
|
22
|
+
import type { HandoffAssignment } from '../../handoff/assignment.js'
|
|
23
|
+
import { type BroadcastHandoffDeps, executeBroadcastHandoff } from '../../handoff/broadcast.js'
|
|
24
|
+
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
25
|
+
import type { HandoffEventSink } from '../../handoff/events.js'
|
|
26
|
+
import { type SingleHandoffDeps, executeSingleHandoff } from '../../handoff/single.js'
|
|
27
|
+
import type { ActorRef } from '../../hierarchy/actor.js'
|
|
28
|
+
import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
|
|
29
|
+
import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
|
|
30
|
+
import {
|
|
31
|
+
DEFAULT_TENANT,
|
|
32
|
+
buildAgent,
|
|
33
|
+
buildDefinition,
|
|
34
|
+
buildHarness,
|
|
35
|
+
buildSendMessageOptions,
|
|
36
|
+
buildTaskContext,
|
|
37
|
+
okExec,
|
|
38
|
+
seedActiveParent,
|
|
39
|
+
stubLogger,
|
|
40
|
+
userActor,
|
|
41
|
+
} from './_fixtures.js'
|
|
42
|
+
|
|
43
|
+
describe('Integration — archive gate (Phase 2.6)', () => {
|
|
44
|
+
it('AgentManager spawn: rejects with ThreadClosedError when parent thread is archived', async () => {
|
|
45
|
+
const harness = buildHarness()
|
|
46
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
47
|
+
|
|
48
|
+
// Archive the thread via the manager so the invariant is enforced — no
|
|
49
|
+
// in-flight sessions under this thread, so archive succeeds.
|
|
50
|
+
await harness.store.updateSession({ ...session, status: 'idle' }, DEFAULT_TENANT)
|
|
51
|
+
await harness.threadManager.archive(thread.id, DEFAULT_TENANT)
|
|
52
|
+
// Flip session back to active so the spawn's capacity path reaches
|
|
53
|
+
// provisionSpawn (the archive gate should still reject).
|
|
54
|
+
await harness.store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
|
|
55
|
+
|
|
56
|
+
harness.registry.register(buildDefinition(buildAgent('worker')))
|
|
57
|
+
|
|
58
|
+
await expect(
|
|
59
|
+
harness.manager.sendMessage(
|
|
60
|
+
buildSendMessageOptions({
|
|
61
|
+
agentId: 'worker',
|
|
62
|
+
parentSessionId: session.id,
|
|
63
|
+
projectId: project.id,
|
|
64
|
+
tenantId: DEFAULT_TENANT,
|
|
65
|
+
parentActor: actor,
|
|
66
|
+
}),
|
|
67
|
+
buildTaskContext({
|
|
68
|
+
sessionId: session.id,
|
|
69
|
+
projectId: project.id,
|
|
70
|
+
threadId: thread.id,
|
|
71
|
+
tenantId: DEFAULT_TENANT,
|
|
72
|
+
parentActor: actor,
|
|
73
|
+
}),
|
|
74
|
+
),
|
|
75
|
+
).rejects.toBeInstanceOf(ThreadClosedError)
|
|
76
|
+
|
|
77
|
+
// Archive invariant held: no new child sessions under the archived thread.
|
|
78
|
+
const underThread = await harness.store.listSessions(thread.id, DEFAULT_TENANT)
|
|
79
|
+
expect(underThread).toHaveLength(1)
|
|
80
|
+
expect(underThread[0]?.id).toBe(session.id)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('Single handoff: rejects with ThreadClosedError when thread is archived (before CAS lock)', async () => {
|
|
84
|
+
const store = new InMemorySessionStore()
|
|
85
|
+
const threadStore = new InMemoryThreadStore()
|
|
86
|
+
const project = await store.createProject(
|
|
87
|
+
{ tenantId: DEFAULT_TENANT, name: 'archive-single' },
|
|
88
|
+
DEFAULT_TENANT,
|
|
89
|
+
)
|
|
90
|
+
const thread = await threadStore.createThread(
|
|
91
|
+
{ projectId: project.id, title: 'archive-single' },
|
|
92
|
+
DEFAULT_TENANT,
|
|
93
|
+
)
|
|
94
|
+
const sourceActor: ActorRef = {
|
|
95
|
+
kind: 'user',
|
|
96
|
+
userId: 'usr_source' as UserId,
|
|
97
|
+
tenantId: DEFAULT_TENANT,
|
|
98
|
+
}
|
|
99
|
+
const session = await store.createSession(
|
|
100
|
+
{ threadId: thread.id, projectId: project.id, currentActor: sourceActor },
|
|
101
|
+
DEFAULT_TENANT,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
// Archive the thread directly — session is idle, so archive precondition
|
|
105
|
+
// holds via listSessions.
|
|
106
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
107
|
+
await threadManager.archive(thread.id, DEFAULT_TENANT)
|
|
108
|
+
|
|
109
|
+
const driver = new GitWorktreeDriver({
|
|
110
|
+
repoRoot: '/repo',
|
|
111
|
+
logger: stubLogger(),
|
|
112
|
+
execFile: async () => okExec(),
|
|
113
|
+
})
|
|
114
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
115
|
+
workspaceRegistry.register(driver)
|
|
116
|
+
|
|
117
|
+
const events: HandoffEventSink = {
|
|
118
|
+
onLocked: vi.fn(),
|
|
119
|
+
onUnlocked: vi.fn(),
|
|
120
|
+
onCommitted: vi.fn(),
|
|
121
|
+
onBroadcastRollback: vi.fn(),
|
|
122
|
+
}
|
|
123
|
+
const deps: SingleHandoffDeps = {
|
|
124
|
+
store,
|
|
125
|
+
workspaceRegistry,
|
|
126
|
+
capacity: new DefaultCapacityValidator(store),
|
|
127
|
+
events,
|
|
128
|
+
threadManager,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const assignment: HandoffAssignment = {
|
|
132
|
+
id: generateHandoffId(),
|
|
133
|
+
mode: 'single',
|
|
134
|
+
sourceSessionId: session.id,
|
|
135
|
+
tenantId: DEFAULT_TENANT,
|
|
136
|
+
threadId: thread.id,
|
|
137
|
+
projectId: project.id,
|
|
138
|
+
sourceActor,
|
|
139
|
+
recipientActor: userActor('usr_target'),
|
|
140
|
+
expectedOwnerVersion: 0,
|
|
141
|
+
createdAt: new Date('2026-04-19'),
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
await expect(executeSingleHandoff(deps, assignment, DEFAULT_TENANT)).rejects.toBeInstanceOf(
|
|
145
|
+
ThreadClosedError,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
// Critical: source session must still be idle (lock never acquired).
|
|
149
|
+
const reloaded = await store.getSession(session.id, DEFAULT_TENANT)
|
|
150
|
+
expect(reloaded?.status).toBe('idle')
|
|
151
|
+
expect(reloaded?.ownerVersion).toBe(0)
|
|
152
|
+
expect(events.onLocked).not.toHaveBeenCalled()
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('Broadcast handoff: rejects with ThreadClosedError when thread is archived (no CAS, no worktrees)', async () => {
|
|
156
|
+
const store = new InMemorySessionStore()
|
|
157
|
+
const threadStore = new InMemoryThreadStore()
|
|
158
|
+
const project = await store.createProject(
|
|
159
|
+
{ tenantId: DEFAULT_TENANT, name: 'archive-bc' },
|
|
160
|
+
DEFAULT_TENANT,
|
|
161
|
+
)
|
|
162
|
+
const thread = await threadStore.createThread(
|
|
163
|
+
{ projectId: project.id, title: 'archive-bc' },
|
|
164
|
+
DEFAULT_TENANT,
|
|
165
|
+
)
|
|
166
|
+
const source = await store.createSession(
|
|
167
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor('usr_source') },
|
|
168
|
+
DEFAULT_TENANT,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
172
|
+
await threadManager.archive(thread.id, DEFAULT_TENANT)
|
|
173
|
+
|
|
174
|
+
let worktreeAdds = 0
|
|
175
|
+
const driver = new GitWorktreeDriver({
|
|
176
|
+
repoRoot: '/repo',
|
|
177
|
+
logger: stubLogger(),
|
|
178
|
+
execFile: async (_file, args) => {
|
|
179
|
+
if (args.includes('add')) worktreeAdds += 1
|
|
180
|
+
return okExec()
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
184
|
+
workspaceRegistry.register(driver)
|
|
185
|
+
|
|
186
|
+
const events: HandoffEventSink = {
|
|
187
|
+
onLocked: vi.fn(),
|
|
188
|
+
onUnlocked: vi.fn(),
|
|
189
|
+
onCommitted: vi.fn(),
|
|
190
|
+
onBroadcastRollback: vi.fn(),
|
|
191
|
+
}
|
|
192
|
+
const deps: BroadcastHandoffDeps = {
|
|
193
|
+
store,
|
|
194
|
+
workspaceRegistry,
|
|
195
|
+
capacity: new DefaultCapacityValidator(store),
|
|
196
|
+
events,
|
|
197
|
+
threadManager,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const assignments: HandoffAssignment[] = [userActor('usr_b'), userActor('usr_c')].map(
|
|
201
|
+
(recipientActor) => ({
|
|
202
|
+
id: generateHandoffId(),
|
|
203
|
+
mode: 'broadcast' as const,
|
|
204
|
+
sourceSessionId: source.id,
|
|
205
|
+
tenantId: DEFAULT_TENANT,
|
|
206
|
+
threadId: thread.id,
|
|
207
|
+
projectId: project.id,
|
|
208
|
+
sourceActor: userActor('usr_source'),
|
|
209
|
+
recipientActor,
|
|
210
|
+
expectedOwnerVersion: 0,
|
|
211
|
+
broadcastId: 'bc_archive',
|
|
212
|
+
createdAt: new Date('2026-04-19'),
|
|
213
|
+
}),
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
await expect(executeBroadcastHandoff(deps, assignments, DEFAULT_TENANT)).rejects.toBeInstanceOf(
|
|
217
|
+
ThreadClosedError,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
// Source never locked, no worktrees provisioned, no rollback event —
|
|
221
|
+
// the archive gate short-circuited before any side-effect landed.
|
|
222
|
+
const reloaded = await store.getSession(source.id, DEFAULT_TENANT)
|
|
223
|
+
expect(reloaded?.status).toBe('idle')
|
|
224
|
+
expect(reloaded?.ownerVersion).toBe(0)
|
|
225
|
+
expect(events.onLocked).not.toHaveBeenCalled()
|
|
226
|
+
expect(events.onBroadcastRollback).not.toHaveBeenCalled()
|
|
227
|
+
expect(worktreeAdds).toBe(0)
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
it('Phase 2.6 closure: post-archive spawn via AgentManager rejects (kernel ingress path gated)', async () => {
|
|
231
|
+
// End-to-end proof that the production ingress path honors the
|
|
232
|
+
// archive status. Direct `SessionStore.createSession` / `updateSession`
|
|
233
|
+
// remain ungated at the store layer (by design — the store has no
|
|
234
|
+
// cross-store awareness), so this test exercises the manager path,
|
|
235
|
+
// not the store boundary.
|
|
236
|
+
const harness = buildHarness()
|
|
237
|
+
const { project, thread, session } = await seedActiveParent(harness)
|
|
238
|
+
|
|
239
|
+
await harness.store.updateSession({ ...session, status: 'idle' }, DEFAULT_TENANT)
|
|
240
|
+
await harness.threadManager.archive(thread.id, DEFAULT_TENANT)
|
|
241
|
+
|
|
242
|
+
// Attempt to spawn directly via context threading (bypass the fixture
|
|
243
|
+
// builder to prove the gate trips from AgentManager, not from the
|
|
244
|
+
// fixture).
|
|
245
|
+
harness.registry.register(buildDefinition(buildAgent('leaker')))
|
|
246
|
+
|
|
247
|
+
const childActor: ActorRef = {
|
|
248
|
+
kind: 'agent',
|
|
249
|
+
agentId: 'leaker' as AgentId,
|
|
250
|
+
tenantId: DEFAULT_TENANT,
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Flip session to active again so capacity checks pass and the
|
|
254
|
+
// archive-gate is what fails — not an earlier guard.
|
|
255
|
+
await harness.store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
|
|
256
|
+
|
|
257
|
+
await expect(
|
|
258
|
+
harness.manager.sendMessage(
|
|
259
|
+
{
|
|
260
|
+
agentId: 'leaker',
|
|
261
|
+
input: { messages: [], workingDirectory: '/tmp' },
|
|
262
|
+
parentSessionId: session.id,
|
|
263
|
+
tenantId: DEFAULT_TENANT,
|
|
264
|
+
projectId: project.id,
|
|
265
|
+
parentActor: childActor,
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
parentRunId: 'run_post_archive' as RunId,
|
|
269
|
+
parentAgentId: 'supervisor',
|
|
270
|
+
parentAbortController: new AbortController(),
|
|
271
|
+
depth: 0,
|
|
272
|
+
budgetTracker: { total: 10_000, remaining: 10_000 },
|
|
273
|
+
tenantId: DEFAULT_TENANT,
|
|
274
|
+
threadId: thread.id,
|
|
275
|
+
sessionId: session.id,
|
|
276
|
+
projectId: project.id,
|
|
277
|
+
parentActor: childActor,
|
|
278
|
+
},
|
|
279
|
+
),
|
|
280
|
+
).rejects.toBeInstanceOf(ThreadClosedError)
|
|
281
|
+
|
|
282
|
+
// The gate tripped before any observable side effect — listSessions
|
|
283
|
+
// still shows only the original seeded session, not a smuggled child.
|
|
284
|
+
const underThread = await harness.store.listSessions(thread.id, DEFAULT_TENANT)
|
|
285
|
+
expect(underThread).toHaveLength(1)
|
|
286
|
+
expect(underThread[0]?.id).toBe(session.id)
|
|
287
|
+
})
|
|
288
|
+
})
|
|
@@ -28,14 +28,18 @@ import {
|
|
|
28
28
|
describe('Integration — capacity caps at spawn sites', () => {
|
|
29
29
|
it('spawn at depth 4 accepted; depth 5 rejected (default maxDelegationDepth=4)', async () => {
|
|
30
30
|
const harness = buildHarness()
|
|
31
|
-
const { project } = await seedActiveParent(harness)
|
|
31
|
+
const { project, thread } = await seedActiveParent(harness)
|
|
32
32
|
|
|
33
33
|
// Build a depth-4 ancestry chain under the project. Each layer flips to
|
|
34
34
|
// `active` so it is a legal spawn parent via the real AgentManager.
|
|
35
35
|
const chainActors: ActorRef[] = [userActor('usr_root')]
|
|
36
36
|
let parentSessionId = (
|
|
37
37
|
await harness.store.createSession(
|
|
38
|
-
{
|
|
38
|
+
{
|
|
39
|
+
threadId: thread.id,
|
|
40
|
+
projectId: project.id,
|
|
41
|
+
currentActor: chainActors[0] ?? userActor('usr_root'),
|
|
42
|
+
},
|
|
39
43
|
DEFAULT_TENANT,
|
|
40
44
|
)
|
|
41
45
|
).id
|
|
@@ -44,7 +48,7 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
44
48
|
|
|
45
49
|
for (let i = 0; i < 4; i++) {
|
|
46
50
|
const child = await harness.store.createSession(
|
|
47
|
-
{ projectId: project.id, currentActor: agentActor(`agt_${i}`) },
|
|
51
|
+
{ threadId: thread.id, projectId: project.id, currentActor: agentActor(`agt_${i}`) },
|
|
48
52
|
DEFAULT_TENANT,
|
|
49
53
|
)
|
|
50
54
|
await harness.store.createSubSession(
|
|
@@ -68,6 +72,7 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
68
72
|
const context = buildTaskContext({
|
|
69
73
|
sessionId: tail,
|
|
70
74
|
projectId: project.id,
|
|
75
|
+
threadId: thread.id,
|
|
71
76
|
tenantId: DEFAULT_TENANT,
|
|
72
77
|
parentActor: userActor('usr_root'),
|
|
73
78
|
})
|
|
@@ -86,12 +91,12 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
86
91
|
|
|
87
92
|
it('spawn at width 8 accepted; 9th sibling rejected (default maxDelegationWidth=8)', async () => {
|
|
88
93
|
const harness = buildHarness()
|
|
89
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
94
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
90
95
|
|
|
91
96
|
// Seed 8 existing children directly through the store.
|
|
92
97
|
for (let i = 0; i < 8; i++) {
|
|
93
98
|
const child = await harness.store.createSession(
|
|
94
|
-
{ projectId: project.id, currentActor: agentActor(`agt_${i}`) },
|
|
99
|
+
{ threadId: thread.id, projectId: project.id, currentActor: agentActor(`agt_${i}`) },
|
|
95
100
|
DEFAULT_TENANT,
|
|
96
101
|
)
|
|
97
102
|
await harness.store.createSubSession(
|
|
@@ -110,6 +115,7 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
110
115
|
const context = buildTaskContext({
|
|
111
116
|
sessionId: session.id,
|
|
112
117
|
projectId: project.id,
|
|
118
|
+
threadId: thread.id,
|
|
113
119
|
tenantId: DEFAULT_TENANT,
|
|
114
120
|
parentActor: actor,
|
|
115
121
|
})
|
|
@@ -133,7 +139,7 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
133
139
|
// DefaultCapacityValidator(store) so it walks the actual persisted
|
|
134
140
|
// parent chain — confirms the wiring rather than a direct unit call.
|
|
135
141
|
const harness = buildHarness()
|
|
136
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
142
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
137
143
|
|
|
138
144
|
harness.registry.register(buildDefinition(buildAgent('worker')))
|
|
139
145
|
|
|
@@ -142,6 +148,7 @@ describe('Integration — capacity caps at spawn sites', () => {
|
|
|
142
148
|
const context = buildTaskContext({
|
|
143
149
|
sessionId: session.id,
|
|
144
150
|
projectId: project.id,
|
|
151
|
+
threadId: thread.id,
|
|
145
152
|
tenantId: DEFAULT_TENANT,
|
|
146
153
|
parentActor: actor,
|
|
147
154
|
})
|
|
@@ -18,8 +18,10 @@
|
|
|
18
18
|
import { describe, expect, it } from 'vitest'
|
|
19
19
|
import { EMPTY_TOKEN_USAGE } from '../../../constants/limits.js'
|
|
20
20
|
import { AgentManager } from '../../../manager/agent/lifecycle.js'
|
|
21
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
21
22
|
import { AgentRegistry } from '../../../registry/agent/definitions.js'
|
|
22
23
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
24
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
23
25
|
import type {
|
|
24
26
|
AgentCapabilities,
|
|
25
27
|
AgentInput,
|
|
@@ -93,7 +95,12 @@ function buildDefinition(agent: Agent<BaseAgentConfig, BaseAgentResult>): AgentD
|
|
|
93
95
|
describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
94
96
|
it('emits the §10.5 event sequence with lineage + schemaVersion stamping', async () => {
|
|
95
97
|
const store = new InMemorySessionStore()
|
|
98
|
+
const threadStore = new InMemoryThreadStore()
|
|
96
99
|
const project = await store.createProject({ tenantId: tenant, name: 'e2e-project' }, tenant)
|
|
100
|
+
const thread = await threadStore.createThread(
|
|
101
|
+
{ projectId: project.id, title: 'e2e-spawn' },
|
|
102
|
+
tenant,
|
|
103
|
+
)
|
|
97
104
|
|
|
98
105
|
const userActor: ActorRef = {
|
|
99
106
|
kind: 'user',
|
|
@@ -102,7 +109,7 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
const parentSession = await store.createSession(
|
|
105
|
-
{ projectId: project.id, currentActor: userActor },
|
|
112
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor },
|
|
106
113
|
tenant,
|
|
107
114
|
)
|
|
108
115
|
// Parent Run in flight — session active while the spawn is happening.
|
|
@@ -117,11 +124,13 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
117
124
|
const registry = new AgentRegistry()
|
|
118
125
|
registry.register(buildDefinition(buildAgent('worker')))
|
|
119
126
|
|
|
127
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
120
128
|
const manager = new AgentManager(registry, undefined, {
|
|
121
129
|
sessionStore: store,
|
|
122
130
|
summaryMaterializer: materializer,
|
|
123
131
|
workspaceRegistry: new WorkspaceBackendRegistry(),
|
|
124
132
|
capacity: new DefaultCapacityValidator(store),
|
|
133
|
+
threadManager,
|
|
125
134
|
})
|
|
126
135
|
|
|
127
136
|
const capturedEvents: RunEvent[] = []
|
|
@@ -136,6 +145,7 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
136
145
|
depth: 0,
|
|
137
146
|
budgetTracker: { total: 100_000, remaining: 100_000 },
|
|
138
147
|
tenantId: tenant,
|
|
148
|
+
threadId: thread.id,
|
|
139
149
|
sessionId: parentSession.id,
|
|
140
150
|
projectId: project.id,
|
|
141
151
|
parentActor: userActor,
|
|
@@ -212,7 +222,12 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
212
222
|
|
|
213
223
|
it('closes the parent→child message gap: parent can re-ingest child content via drill + summary', async () => {
|
|
214
224
|
const store = new InMemorySessionStore()
|
|
225
|
+
const threadStore = new InMemoryThreadStore()
|
|
215
226
|
const project = await store.createProject({ tenantId: tenant, name: 'e2e-gap' }, tenant)
|
|
227
|
+
const thread = await threadStore.createThread(
|
|
228
|
+
{ projectId: project.id, title: 'e2e-gap' },
|
|
229
|
+
tenant,
|
|
230
|
+
)
|
|
216
231
|
|
|
217
232
|
const userActor: ActorRef = {
|
|
218
233
|
kind: 'user',
|
|
@@ -220,7 +235,7 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
220
235
|
tenantId: tenant,
|
|
221
236
|
}
|
|
222
237
|
const parentSession = await store.createSession(
|
|
223
|
-
{ projectId: project.id, currentActor: userActor },
|
|
238
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor },
|
|
224
239
|
tenant,
|
|
225
240
|
)
|
|
226
241
|
await store.updateSession({ ...parentSession, status: 'active' }, tenant)
|
|
@@ -234,11 +249,13 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
234
249
|
const registry = new AgentRegistry()
|
|
235
250
|
registry.register(buildDefinition(buildAgent('worker')))
|
|
236
251
|
|
|
252
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
237
253
|
const manager = new AgentManager(registry, undefined, {
|
|
238
254
|
sessionStore: store,
|
|
239
255
|
summaryMaterializer: materializer,
|
|
240
256
|
workspaceRegistry: new WorkspaceBackendRegistry(),
|
|
241
257
|
capacity: new DefaultCapacityValidator(store),
|
|
258
|
+
threadManager,
|
|
242
259
|
})
|
|
243
260
|
|
|
244
261
|
const task = await manager.sendMessage(
|
|
@@ -257,6 +274,7 @@ describe('E2E — SubSession spawn → kernel summary → parent drill', () => {
|
|
|
257
274
|
depth: 0,
|
|
258
275
|
budgetTracker: { total: 10_000, remaining: 10_000 },
|
|
259
276
|
tenantId: tenant,
|
|
277
|
+
threadId: thread.id,
|
|
260
278
|
sessionId: parentSession.id,
|
|
261
279
|
projectId: project.id,
|
|
262
280
|
parentActor: userActor,
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
describe('Integration — event stream ordering + lineage + schemaVersion', () => {
|
|
35
35
|
it('every sub-session RunEvent carries schemaVersion: 2', async () => {
|
|
36
36
|
const harness = buildHarness()
|
|
37
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
37
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
38
38
|
harness.registry.register(buildDefinition(buildAgent('worker')))
|
|
39
39
|
|
|
40
40
|
const captured: RunEvent[] = []
|
|
@@ -49,6 +49,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
49
49
|
buildTaskContext({
|
|
50
50
|
sessionId: session.id,
|
|
51
51
|
projectId: project.id,
|
|
52
|
+
threadId: thread.id,
|
|
52
53
|
tenantId: DEFAULT_TENANT,
|
|
53
54
|
parentActor: actor,
|
|
54
55
|
}),
|
|
@@ -73,7 +74,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
73
74
|
|
|
74
75
|
it('every sub-session event carries lineage { parentSessionId, rootSessionId, depth }', async () => {
|
|
75
76
|
const harness = buildHarness()
|
|
76
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
77
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
77
78
|
harness.registry.register(buildDefinition(buildAgent('worker')))
|
|
78
79
|
|
|
79
80
|
const captured: RunEvent[] = []
|
|
@@ -88,6 +89,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
88
89
|
buildTaskContext({
|
|
89
90
|
sessionId: session.id,
|
|
90
91
|
projectId: project.id,
|
|
92
|
+
threadId: thread.id,
|
|
91
93
|
tenantId: DEFAULT_TENANT,
|
|
92
94
|
parentActor: actor,
|
|
93
95
|
}),
|
|
@@ -116,7 +118,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
116
118
|
|
|
117
119
|
it('3-deep delegation: rootSessionId identical across tree; depth ascends 1→2→3', async () => {
|
|
118
120
|
const harness = buildHarness()
|
|
119
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
121
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
120
122
|
|
|
121
123
|
// Wire a cascading agent: level-1 child spawns level-2 via its own
|
|
122
124
|
// sendMessage. We hand a reference to the manager into the child agent
|
|
@@ -129,7 +131,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
129
131
|
'mid',
|
|
130
132
|
async (_input: AgentInput, config: BaseAgentConfig): Promise<BaseAgentResult> => {
|
|
131
133
|
// Spawn a level-2 child from inside the mid-agent's run.
|
|
132
|
-
if (!config.sessionId || !config.projectId || !config.tenantId) {
|
|
134
|
+
if (!config.sessionId || !config.threadId || !config.projectId || !config.tenantId) {
|
|
133
135
|
throw new Error('mid agent missing session scoping')
|
|
134
136
|
}
|
|
135
137
|
// Flip child session to active so it is a legal spawn parent.
|
|
@@ -154,6 +156,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
154
156
|
depth: 1,
|
|
155
157
|
budgetTracker: { total: 10_000, remaining: 10_000 },
|
|
156
158
|
tenantId: config.tenantId,
|
|
159
|
+
threadId: config.threadId,
|
|
157
160
|
sessionId: childSessionId,
|
|
158
161
|
projectId: config.projectId,
|
|
159
162
|
parentActor: { kind: 'agent', agentId: 'mid' as never, tenantId: config.tenantId },
|
|
@@ -192,6 +195,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
192
195
|
buildTaskContext({
|
|
193
196
|
sessionId: session.id,
|
|
194
197
|
projectId: project.id,
|
|
198
|
+
threadId: thread.id,
|
|
195
199
|
tenantId: DEFAULT_TENANT,
|
|
196
200
|
parentActor: actor,
|
|
197
201
|
}),
|
|
@@ -237,7 +241,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
237
241
|
// its own listener — the outer listener does NOT see the nested
|
|
238
242
|
// listener's events (no cross-contamination).
|
|
239
243
|
const harness = buildHarness()
|
|
240
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
244
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
241
245
|
|
|
242
246
|
const outerCaptured: RunEvent[] = []
|
|
243
247
|
const nestedCaptured: RunEvent[] = []
|
|
@@ -246,7 +250,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
246
250
|
const midAgent = buildAgentCustom(
|
|
247
251
|
'mid',
|
|
248
252
|
async (_input: AgentInput, config: BaseAgentConfig): Promise<BaseAgentResult> => {
|
|
249
|
-
if (!config.sessionId || !config.projectId || !config.tenantId) {
|
|
253
|
+
if (!config.sessionId || !config.threadId || !config.projectId || !config.tenantId) {
|
|
250
254
|
throw new Error('mid missing scope')
|
|
251
255
|
}
|
|
252
256
|
const cs = await harness.store.getSession(config.sessionId, config.tenantId)
|
|
@@ -273,6 +277,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
273
277
|
depth: 1,
|
|
274
278
|
budgetTracker: { total: 10_000, remaining: 10_000 },
|
|
275
279
|
tenantId: config.tenantId,
|
|
280
|
+
threadId: config.threadId,
|
|
276
281
|
sessionId: config.sessionId,
|
|
277
282
|
projectId: config.projectId,
|
|
278
283
|
parentActor: {
|
|
@@ -313,6 +318,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
313
318
|
buildTaskContext({
|
|
314
319
|
sessionId: session.id,
|
|
315
320
|
projectId: project.id,
|
|
321
|
+
threadId: thread.id,
|
|
316
322
|
tenantId: DEFAULT_TENANT,
|
|
317
323
|
parentActor: actor,
|
|
318
324
|
}),
|
|
@@ -349,7 +355,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
349
355
|
// therefore inherit the envelope even though they have no lineage in
|
|
350
356
|
// their own type definition.
|
|
351
357
|
const harness = buildHarness()
|
|
352
|
-
const { project, session, actor } = await seedActiveParent(harness)
|
|
358
|
+
const { project, thread, session, actor } = await seedActiveParent(harness)
|
|
353
359
|
|
|
354
360
|
const leafAgent = buildAgentCustom(
|
|
355
361
|
'leaf-emit',
|
|
@@ -385,6 +391,7 @@ describe('Integration — event stream ordering + lineage + schemaVersion', () =
|
|
|
385
391
|
buildTaskContext({
|
|
386
392
|
sessionId: session.id,
|
|
387
393
|
projectId: project.id,
|
|
394
|
+
threadId: thread.id,
|
|
388
395
|
tenantId: DEFAULT_TENANT,
|
|
389
396
|
parentActor: actor,
|
|
390
397
|
}),
|