@namzu/sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +53 -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 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- 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/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 +1 -0
- package/dist/runtime/query/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/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 +6 -11
- 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 +0 -4
- 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/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 +15 -16
- 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/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/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/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/src/store/conversation/memory.ts +0 -144
- package/src/types/conversation/index.ts +0 -15
|
@@ -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
|
}),
|
|
@@ -10,9 +10,11 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { describe, expect, it, vi } from 'vitest'
|
|
13
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
13
14
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
15
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
14
16
|
import type { SessionId } from '../../../types/ids/index.js'
|
|
15
|
-
import type { ProjectId } from '../../../types/session/ids.js'
|
|
17
|
+
import type { ProjectId, ThreadId } from '../../../types/session/ids.js'
|
|
16
18
|
import { generateHandoffId } from '../../../utils/id.js'
|
|
17
19
|
import type { HandoffAssignment } from '../../handoff/assignment.js'
|
|
18
20
|
import { type BroadcastHandoffDeps, executeBroadcastHandoff } from '../../handoff/broadcast.js'
|
|
@@ -26,6 +28,7 @@ import { DEFAULT_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
|
|
|
26
28
|
|
|
27
29
|
function buildDeps(
|
|
28
30
|
store: InMemorySessionStore,
|
|
31
|
+
threadStore: InMemoryThreadStore,
|
|
29
32
|
execOverride?: ExecFile,
|
|
30
33
|
): {
|
|
31
34
|
deps: BroadcastHandoffDeps
|
|
@@ -54,6 +57,7 @@ function buildDeps(
|
|
|
54
57
|
workspaceRegistry,
|
|
55
58
|
capacity: new DefaultCapacityValidator(store),
|
|
56
59
|
events: sink,
|
|
60
|
+
threadManager: new ThreadManager({ threadStore, sessionStore: store }),
|
|
57
61
|
},
|
|
58
62
|
events: { ...sink, onBroadcastRollback },
|
|
59
63
|
}
|
|
@@ -62,6 +66,7 @@ function buildDeps(
|
|
|
62
66
|
function buildAssignments(
|
|
63
67
|
sourceSessionId: SessionId,
|
|
64
68
|
projectId: ProjectId,
|
|
69
|
+
threadId: ThreadId,
|
|
65
70
|
recipients: ActorRef[],
|
|
66
71
|
broadcastId = 'bc_integration',
|
|
67
72
|
expectedOwnerVersion = 0,
|
|
@@ -71,6 +76,7 @@ function buildAssignments(
|
|
|
71
76
|
mode: 'broadcast' as const,
|
|
72
77
|
sourceSessionId,
|
|
73
78
|
tenantId: DEFAULT_TENANT,
|
|
79
|
+
threadId,
|
|
74
80
|
projectId,
|
|
75
81
|
sourceActor: userActor('usr_source'),
|
|
76
82
|
recipientActor,
|
|
@@ -83,12 +89,17 @@ function buildAssignments(
|
|
|
83
89
|
describe('Integration — broadcast handoff E2E', () => {
|
|
84
90
|
it('happy: 3-recipient fan-out → each recipient has isolated worktree + source reaches awaiting_merge', async () => {
|
|
85
91
|
const store = new InMemorySessionStore()
|
|
92
|
+
const threadStore = new InMemoryThreadStore()
|
|
86
93
|
const project = await store.createProject(
|
|
87
94
|
{ tenantId: DEFAULT_TENANT, name: 'bc-happy' },
|
|
88
95
|
DEFAULT_TENANT,
|
|
89
96
|
)
|
|
97
|
+
const thread = await threadStore.createThread(
|
|
98
|
+
{ projectId: project.id, title: 'bc-happy' },
|
|
99
|
+
DEFAULT_TENANT,
|
|
100
|
+
)
|
|
90
101
|
const source = await store.createSession(
|
|
91
|
-
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
102
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor('usr_source') },
|
|
92
103
|
DEFAULT_TENANT,
|
|
93
104
|
)
|
|
94
105
|
|
|
@@ -103,10 +114,10 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
103
114
|
}
|
|
104
115
|
return okExec()
|
|
105
116
|
}
|
|
106
|
-
const { deps } = buildDeps(store, exec)
|
|
117
|
+
const { deps } = buildDeps(store, threadStore, exec)
|
|
107
118
|
|
|
108
119
|
const recipients = [userActor('usr_bob'), userActor('usr_carol'), userActor('usr_dan')]
|
|
109
|
-
const assignments = buildAssignments(source.id, project.id, recipients)
|
|
120
|
+
const assignments = buildAssignments(source.id, project.id, thread.id, recipients)
|
|
110
121
|
|
|
111
122
|
const outcomes = await executeBroadcastHandoff(deps, assignments, DEFAULT_TENANT)
|
|
112
123
|
expect(outcomes).toHaveLength(3)
|
|
@@ -128,12 +139,17 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
128
139
|
|
|
129
140
|
it('rollback on 2nd-recipient provisioning failure: zero orphan records, partialState accurate', async () => {
|
|
130
141
|
const store = new InMemorySessionStore()
|
|
142
|
+
const threadStore = new InMemoryThreadStore()
|
|
131
143
|
const project = await store.createProject(
|
|
132
144
|
{ tenantId: DEFAULT_TENANT, name: 'bc-rb' },
|
|
133
145
|
DEFAULT_TENANT,
|
|
134
146
|
)
|
|
147
|
+
const thread = await threadStore.createThread(
|
|
148
|
+
{ projectId: project.id, title: 'bc-rb' },
|
|
149
|
+
DEFAULT_TENANT,
|
|
150
|
+
)
|
|
135
151
|
const source = await store.createSession(
|
|
136
|
-
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
152
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor('usr_source') },
|
|
137
153
|
DEFAULT_TENANT,
|
|
138
154
|
)
|
|
139
155
|
|
|
@@ -145,9 +161,9 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
145
161
|
}
|
|
146
162
|
return okExec()
|
|
147
163
|
}
|
|
148
|
-
const { deps, events } = buildDeps(store, exec)
|
|
164
|
+
const { deps, events } = buildDeps(store, threadStore, exec)
|
|
149
165
|
|
|
150
|
-
const assignments = buildAssignments(source.id, project.id, [
|
|
166
|
+
const assignments = buildAssignments(source.id, project.id, thread.id, [
|
|
151
167
|
userActor('usr_b'),
|
|
152
168
|
userActor('usr_c'),
|
|
153
169
|
userActor('usr_d'),
|
|
@@ -186,18 +202,23 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
186
202
|
|
|
187
203
|
it('source transitions to awaiting_merge + retains currentActor as coordinator (§5.4)', async () => {
|
|
188
204
|
const store = new InMemorySessionStore()
|
|
205
|
+
const threadStore = new InMemoryThreadStore()
|
|
189
206
|
const project = await store.createProject(
|
|
190
207
|
{ tenantId: DEFAULT_TENANT, name: 'coord' },
|
|
191
208
|
DEFAULT_TENANT,
|
|
192
209
|
)
|
|
210
|
+
const thread = await threadStore.createThread(
|
|
211
|
+
{ projectId: project.id, title: 'coord' },
|
|
212
|
+
DEFAULT_TENANT,
|
|
213
|
+
)
|
|
193
214
|
const coordinator = userActor('usr_source')
|
|
194
215
|
const source = await store.createSession(
|
|
195
|
-
{ projectId: project.id, currentActor: coordinator },
|
|
216
|
+
{ threadId: thread.id, projectId: project.id, currentActor: coordinator },
|
|
196
217
|
DEFAULT_TENANT,
|
|
197
218
|
)
|
|
198
219
|
|
|
199
|
-
const { deps } = buildDeps(store)
|
|
200
|
-
const assignments = buildAssignments(source.id, project.id, [
|
|
220
|
+
const { deps } = buildDeps(store, threadStore)
|
|
221
|
+
const assignments = buildAssignments(source.id, project.id, thread.id, [
|
|
201
222
|
userActor('usr_b'),
|
|
202
223
|
userActor('usr_c'),
|
|
203
224
|
])
|
|
@@ -213,12 +234,17 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
213
234
|
|
|
214
235
|
it('all recipients get isolated worktrees — zero path collisions even under N=8', async () => {
|
|
215
236
|
const store = new InMemorySessionStore()
|
|
237
|
+
const threadStore = new InMemoryThreadStore()
|
|
216
238
|
const project = await store.createProject(
|
|
217
239
|
{ tenantId: DEFAULT_TENANT, name: 'iso' },
|
|
218
240
|
DEFAULT_TENANT,
|
|
219
241
|
)
|
|
242
|
+
const thread = await threadStore.createThread(
|
|
243
|
+
{ projectId: project.id, title: 'iso' },
|
|
244
|
+
DEFAULT_TENANT,
|
|
245
|
+
)
|
|
220
246
|
const source = await store.createSession(
|
|
221
|
-
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
247
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor('usr_source') },
|
|
222
248
|
DEFAULT_TENANT,
|
|
223
249
|
)
|
|
224
250
|
|
|
@@ -233,10 +259,10 @@ describe('Integration — broadcast handoff E2E', () => {
|
|
|
233
259
|
}
|
|
234
260
|
return okExec()
|
|
235
261
|
}
|
|
236
|
-
const { deps } = buildDeps(store, exec)
|
|
262
|
+
const { deps } = buildDeps(store, threadStore, exec)
|
|
237
263
|
|
|
238
264
|
const recipients = Array.from({ length: 8 }, (_, i) => userActor(`usr_${i}`))
|
|
239
|
-
const assignments = buildAssignments(source.id, project.id, recipients)
|
|
265
|
+
const assignments = buildAssignments(source.id, project.id, thread.id, recipients)
|
|
240
266
|
|
|
241
267
|
const outcomes = await executeBroadcastHandoff(deps, assignments, DEFAULT_TENANT)
|
|
242
268
|
expect(outcomes).toHaveLength(8)
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { describe, expect, it } from 'vitest'
|
|
19
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
19
20
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
21
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
22
|
+
import type { ThreadId } from '../../../types/session/ids.js'
|
|
20
23
|
import { generateHandoffId } from '../../../utils/id.js'
|
|
21
24
|
import type { HandoffAssignment } from '../../handoff/assignment.js'
|
|
22
25
|
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
@@ -31,7 +34,11 @@ import { GitWorktreeDriver } from '../../workspace/git-worktree.js'
|
|
|
31
34
|
import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
|
|
32
35
|
import { DEFAULT_TENANT, okExec, stubLogger, userActor } from './_fixtures.js'
|
|
33
36
|
|
|
34
|
-
function buildDeps(
|
|
37
|
+
function buildDeps(
|
|
38
|
+
store: InMemorySessionStore,
|
|
39
|
+
threadStore: InMemoryThreadStore,
|
|
40
|
+
runStatus?: RunStatusResolver,
|
|
41
|
+
): SingleHandoffDeps {
|
|
35
42
|
const driver = new GitWorktreeDriver({
|
|
36
43
|
repoRoot: '/repo',
|
|
37
44
|
logger: stubLogger(),
|
|
@@ -47,31 +54,38 @@ function buildDeps(store: InMemorySessionStore, runStatus?: RunStatusResolver):
|
|
|
47
54
|
workspaceRegistry,
|
|
48
55
|
capacity: new DefaultCapacityValidator(store),
|
|
49
56
|
events,
|
|
57
|
+
threadManager: new ThreadManager({ threadStore, sessionStore: store }),
|
|
50
58
|
...(runStatus !== undefined && { runStatus }),
|
|
51
59
|
}
|
|
52
60
|
}
|
|
53
61
|
|
|
54
|
-
async function seedIdleSession(store: InMemorySessionStore) {
|
|
62
|
+
async function seedIdleSession(store: InMemorySessionStore, threadStore: InMemoryThreadStore) {
|
|
55
63
|
const project = await store.createProject(
|
|
56
64
|
{ tenantId: DEFAULT_TENANT, name: 'illegal' },
|
|
57
65
|
DEFAULT_TENANT,
|
|
58
66
|
)
|
|
67
|
+
const thread = await threadStore.createThread(
|
|
68
|
+
{ projectId: project.id, title: 'illegal' },
|
|
69
|
+
DEFAULT_TENANT,
|
|
70
|
+
)
|
|
59
71
|
const session = await store.createSession(
|
|
60
|
-
{ projectId: project.id, currentActor: userActor('usr_source') },
|
|
72
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor('usr_source') },
|
|
61
73
|
DEFAULT_TENANT,
|
|
62
74
|
)
|
|
63
|
-
return { project, session }
|
|
75
|
+
return { project, thread, session }
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
function buildAssignment(
|
|
67
79
|
sourceSessionId: Awaited<ReturnType<InMemorySessionStore['createSession']>>['id'],
|
|
68
80
|
projectId: Awaited<ReturnType<InMemorySessionStore['createProject']>>['id'],
|
|
81
|
+
threadId: ThreadId,
|
|
69
82
|
): HandoffAssignment {
|
|
70
83
|
return {
|
|
71
84
|
id: generateHandoffId(),
|
|
72
85
|
mode: 'single',
|
|
73
86
|
sourceSessionId,
|
|
74
87
|
tenantId: DEFAULT_TENANT,
|
|
88
|
+
threadId,
|
|
75
89
|
projectId,
|
|
76
90
|
sourceActor: userActor('usr_source'),
|
|
77
91
|
recipientActor: userActor('usr_target'),
|
|
@@ -83,13 +97,14 @@ function buildAssignment(
|
|
|
83
97
|
describe('Integration — illegal handoff transitions (§5.1)', () => {
|
|
84
98
|
it('running Run → HandoffLockRejected { reason: active_run }', async () => {
|
|
85
99
|
const store = new InMemorySessionStore()
|
|
86
|
-
const
|
|
87
|
-
const
|
|
100
|
+
const threadStore = new InMemoryThreadStore()
|
|
101
|
+
const { project, thread, session } = await seedIdleSession(store, threadStore)
|
|
102
|
+
const deps = buildDeps(store, threadStore, {
|
|
88
103
|
async blockingRun() {
|
|
89
104
|
return { reason: 'active_run' }
|
|
90
105
|
},
|
|
91
106
|
})
|
|
92
|
-
const assignment = buildAssignment(session.id, project.id)
|
|
107
|
+
const assignment = buildAssignment(session.id, project.id, thread.id)
|
|
93
108
|
|
|
94
109
|
try {
|
|
95
110
|
await executeSingleHandoff(deps, assignment, DEFAULT_TENANT)
|
|
@@ -107,15 +122,20 @@ describe('Integration — illegal handoff transitions (§5.1)', () => {
|
|
|
107
122
|
|
|
108
123
|
it('awaiting_hitl → HandoffLockRejected { reason: pending_hitl }', async () => {
|
|
109
124
|
const store = new InMemorySessionStore()
|
|
110
|
-
const
|
|
111
|
-
const
|
|
125
|
+
const threadStore = new InMemoryThreadStore()
|
|
126
|
+
const { project, thread, session } = await seedIdleSession(store, threadStore)
|
|
127
|
+
const deps = buildDeps(store, threadStore, {
|
|
112
128
|
async blockingRun() {
|
|
113
129
|
return { reason: 'pending_hitl' }
|
|
114
130
|
},
|
|
115
131
|
})
|
|
116
132
|
|
|
117
133
|
try {
|
|
118
|
-
await executeSingleHandoff(
|
|
134
|
+
await executeSingleHandoff(
|
|
135
|
+
deps,
|
|
136
|
+
buildAssignment(session.id, project.id, thread.id),
|
|
137
|
+
DEFAULT_TENANT,
|
|
138
|
+
)
|
|
119
139
|
expect.fail('expected HandoffLockRejected')
|
|
120
140
|
} catch (err) {
|
|
121
141
|
expect(err).toBeInstanceOf(HandoffLockRejected)
|
|
@@ -129,29 +149,39 @@ describe('Integration — illegal handoff transitions (§5.1)', () => {
|
|
|
129
149
|
// `pending_hitl` for both. This keeps the lock-rejection enum
|
|
130
150
|
// conservative (no new reason variant for a sub-state).
|
|
131
151
|
const store = new InMemorySessionStore()
|
|
132
|
-
const
|
|
133
|
-
const
|
|
152
|
+
const threadStore = new InMemoryThreadStore()
|
|
153
|
+
const { project, thread, session } = await seedIdleSession(store, threadStore)
|
|
154
|
+
const deps = buildDeps(store, threadStore, {
|
|
134
155
|
async blockingRun() {
|
|
135
156
|
return { reason: 'pending_hitl' }
|
|
136
157
|
},
|
|
137
158
|
})
|
|
138
159
|
|
|
139
160
|
await expect(
|
|
140
|
-
executeSingleHandoff(
|
|
161
|
+
executeSingleHandoff(
|
|
162
|
+
deps,
|
|
163
|
+
buildAssignment(session.id, project.id, thread.id),
|
|
164
|
+
DEFAULT_TENANT,
|
|
165
|
+
),
|
|
141
166
|
).rejects.toBeInstanceOf(HandoffLockRejected)
|
|
142
167
|
})
|
|
143
168
|
|
|
144
169
|
it('awaiting_subsession → HandoffLockRejected { reason: pending_subsession }', async () => {
|
|
145
170
|
const store = new InMemorySessionStore()
|
|
146
|
-
const
|
|
147
|
-
const
|
|
171
|
+
const threadStore = new InMemoryThreadStore()
|
|
172
|
+
const { project, thread, session } = await seedIdleSession(store, threadStore)
|
|
173
|
+
const deps = buildDeps(store, threadStore, {
|
|
148
174
|
async blockingRun() {
|
|
149
175
|
return { reason: 'pending_subsession' }
|
|
150
176
|
},
|
|
151
177
|
})
|
|
152
178
|
|
|
153
179
|
try {
|
|
154
|
-
await executeSingleHandoff(
|
|
180
|
+
await executeSingleHandoff(
|
|
181
|
+
deps,
|
|
182
|
+
buildAssignment(session.id, project.id, thread.id),
|
|
183
|
+
DEFAULT_TENANT,
|
|
184
|
+
)
|
|
155
185
|
expect.fail('expected HandoffLockRejected')
|
|
156
186
|
} catch (err) {
|
|
157
187
|
expect(err).toBeInstanceOf(HandoffLockRejected)
|
|
@@ -163,17 +193,22 @@ describe('Integration — illegal handoff transitions (§5.1)', () => {
|
|
|
163
193
|
// When the session itself is already non-idle (e.g. `active`), the lock
|
|
164
194
|
// rejection fires from the status check — no RunStatusResolver invoked.
|
|
165
195
|
const store = new InMemorySessionStore()
|
|
166
|
-
const
|
|
196
|
+
const threadStore = new InMemoryThreadStore()
|
|
197
|
+
const { project, thread, session } = await seedIdleSession(store, threadStore)
|
|
167
198
|
await store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
|
|
168
199
|
|
|
169
|
-
const deps = buildDeps(store, {
|
|
200
|
+
const deps = buildDeps(store, threadStore, {
|
|
170
201
|
async blockingRun() {
|
|
171
202
|
return null // resolver would allow, but status guard trips first
|
|
172
203
|
},
|
|
173
204
|
})
|
|
174
205
|
|
|
175
206
|
await expect(
|
|
176
|
-
executeSingleHandoff(
|
|
207
|
+
executeSingleHandoff(
|
|
208
|
+
deps,
|
|
209
|
+
buildAssignment(session.id, project.id, thread.id),
|
|
210
|
+
DEFAULT_TENANT,
|
|
211
|
+
),
|
|
177
212
|
).rejects.toBeInstanceOf(HandoffLockRejected)
|
|
178
213
|
})
|
|
179
214
|
})
|