@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
|
@@ -10,6 +10,7 @@ import type { DeliverableRef } from '../../../session/summary/deliverable.js'
|
|
|
10
10
|
import { SessionSummaryMaterializer } from '../../../session/summary/materialize.js'
|
|
11
11
|
import { WorkspaceBackendRegistry } from '../../../session/workspace/registry.js'
|
|
12
12
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
13
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
13
14
|
import type {
|
|
14
15
|
AgentCapabilities,
|
|
15
16
|
AgentInput,
|
|
@@ -23,8 +24,9 @@ import type { AgentTaskContext, SendMessageOptions } from '../../../types/agent/
|
|
|
23
24
|
import type { AgentId, SessionId, TenantId, UserId } from '../../../types/ids/index.js'
|
|
24
25
|
import { createAssistantMessage } from '../../../types/message/index.js'
|
|
25
26
|
import type { RunEvent } from '../../../types/run/events.js'
|
|
26
|
-
import type { SummaryId } from '../../../types/session/ids.js'
|
|
27
|
+
import type { SummaryId, ThreadId } from '../../../types/session/ids.js'
|
|
27
28
|
import { ZERO_COST } from '../../../utils/cost.js'
|
|
29
|
+
import { ThreadManager } from '../../thread/lifecycle.js'
|
|
28
30
|
import { AgentManager } from '../lifecycle.js'
|
|
29
31
|
|
|
30
32
|
const tenant = 'tnt_alpha' as TenantId
|
|
@@ -113,10 +115,13 @@ function agentActor(id: string, tid: TenantId = tenant): ActorRef {
|
|
|
113
115
|
|
|
114
116
|
interface Harness {
|
|
115
117
|
store: InMemorySessionStore
|
|
118
|
+
threadStore: InMemoryThreadStore
|
|
119
|
+
threadManager: ThreadManager
|
|
116
120
|
materializer: SessionSummaryMaterializer
|
|
117
121
|
manager: AgentManager
|
|
118
122
|
parentSession: Awaited<ReturnType<InMemorySessionStore['createSession']>>
|
|
119
123
|
projectId: import('../../../types/session/ids.js').ProjectId
|
|
124
|
+
threadId: ThreadId
|
|
120
125
|
registry: AgentRegistry
|
|
121
126
|
}
|
|
122
127
|
|
|
@@ -125,9 +130,15 @@ async function buildHarness(
|
|
|
125
130
|
tenantId: TenantId = tenant,
|
|
126
131
|
): Promise<Harness> {
|
|
127
132
|
const store = new InMemorySessionStore()
|
|
133
|
+
const threadStore = new InMemoryThreadStore()
|
|
134
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
128
135
|
const project = await store.createProject({ tenantId, name: 'p1' }, tenantId)
|
|
136
|
+
const thread = await threadStore.createThread(
|
|
137
|
+
{ projectId: project.id, title: 'lifecycle-test' },
|
|
138
|
+
tenantId,
|
|
139
|
+
)
|
|
129
140
|
const parentSession = await store.createSession(
|
|
130
|
-
{ projectId: project.id, currentActor: user(tenantId) },
|
|
141
|
+
{ threadId: thread.id, projectId: project.id, currentActor: user(tenantId) },
|
|
131
142
|
tenantId,
|
|
132
143
|
)
|
|
133
144
|
// Parent runs kick the session into 'active' so the materializer can
|
|
@@ -148,14 +159,18 @@ async function buildHarness(
|
|
|
148
159
|
summaryMaterializer: materializer,
|
|
149
160
|
workspaceRegistry: new WorkspaceBackendRegistry(),
|
|
150
161
|
capacity: new DefaultCapacityValidator(store),
|
|
162
|
+
threadManager,
|
|
151
163
|
})
|
|
152
164
|
|
|
153
165
|
return {
|
|
154
166
|
store,
|
|
167
|
+
threadStore,
|
|
168
|
+
threadManager,
|
|
155
169
|
materializer,
|
|
156
170
|
manager,
|
|
157
171
|
parentSession: { ...parentSession, status: 'active' },
|
|
158
172
|
projectId: project.id,
|
|
173
|
+
threadId: thread.id,
|
|
159
174
|
registry,
|
|
160
175
|
}
|
|
161
176
|
}
|
|
@@ -163,6 +178,7 @@ async function buildHarness(
|
|
|
163
178
|
function buildContext(
|
|
164
179
|
parentSessionId: SessionId,
|
|
165
180
|
projectId: import('../../../types/session/ids.js').ProjectId,
|
|
181
|
+
threadId: ThreadId,
|
|
166
182
|
tenantId: TenantId = tenant,
|
|
167
183
|
depth = 0,
|
|
168
184
|
): AgentTaskContext {
|
|
@@ -173,6 +189,7 @@ function buildContext(
|
|
|
173
189
|
depth,
|
|
174
190
|
budgetTracker: { total: 100_000, remaining: 100_000 },
|
|
175
191
|
tenantId,
|
|
192
|
+
threadId,
|
|
176
193
|
sessionId: parentSessionId,
|
|
177
194
|
projectId,
|
|
178
195
|
parentActor: user(tenantId),
|
|
@@ -214,7 +231,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
214
231
|
|
|
215
232
|
const task = await harness.manager.sendMessage(
|
|
216
233
|
buildOptions('child-1', harness.parentSession.id, harness.projectId),
|
|
217
|
-
buildContext(harness.parentSession.id, harness.projectId),
|
|
234
|
+
buildContext(harness.parentSession.id, harness.projectId, harness.threadId),
|
|
218
235
|
listener,
|
|
219
236
|
)
|
|
220
237
|
await waitForTask(harness.manager, task.taskId)
|
|
@@ -259,7 +276,11 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
259
276
|
// Pre-fill 8 direct sub-sessions under the parent, up to the default width cap.
|
|
260
277
|
for (let i = 0; i < 8; i++) {
|
|
261
278
|
const sibling = await harness.store.createSession(
|
|
262
|
-
{
|
|
279
|
+
{
|
|
280
|
+
threadId: harness.threadId,
|
|
281
|
+
projectId: harness.projectId,
|
|
282
|
+
currentActor: agentActor('sibling'),
|
|
283
|
+
},
|
|
263
284
|
tenant,
|
|
264
285
|
)
|
|
265
286
|
await harness.store.createSubSession(
|
|
@@ -276,7 +297,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
276
297
|
await expect(
|
|
277
298
|
harness.manager.sendMessage(
|
|
278
299
|
buildOptions('child-1', harness.parentSession.id, harness.projectId),
|
|
279
|
-
buildContext(harness.parentSession.id, harness.projectId),
|
|
300
|
+
buildContext(harness.parentSession.id, harness.projectId, harness.threadId),
|
|
280
301
|
),
|
|
281
302
|
).rejects.toBeInstanceOf(DelegationCapacityExceeded)
|
|
282
303
|
})
|
|
@@ -290,7 +311,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
290
311
|
let parentId: SessionId = harness.parentSession.id
|
|
291
312
|
for (let i = 0; i < 4; i++) {
|
|
292
313
|
const child = await harness.store.createSession(
|
|
293
|
-
{ projectId: harness.projectId, currentActor: agentActor('c') },
|
|
314
|
+
{ threadId: harness.threadId, projectId: harness.projectId, currentActor: agentActor('c') },
|
|
294
315
|
tenant,
|
|
295
316
|
)
|
|
296
317
|
await harness.store.createSubSession(
|
|
@@ -308,7 +329,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
308
329
|
await expect(
|
|
309
330
|
harness.manager.sendMessage(
|
|
310
331
|
buildOptions('child-1', parentId, harness.projectId),
|
|
311
|
-
buildContext(parentId, harness.projectId, tenant, 0),
|
|
332
|
+
buildContext(parentId, harness.projectId, harness.threadId, tenant, 0),
|
|
312
333
|
),
|
|
313
334
|
).rejects.toBeInstanceOf(DelegationCapacityExceeded)
|
|
314
335
|
})
|
|
@@ -319,7 +340,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
319
340
|
|
|
320
341
|
const task = await harness.manager.sendMessage(
|
|
321
342
|
buildOptions('child-fail', harness.parentSession.id, harness.projectId),
|
|
322
|
-
buildContext(harness.parentSession.id, harness.projectId),
|
|
343
|
+
buildContext(harness.parentSession.id, harness.projectId, harness.threadId),
|
|
323
344
|
)
|
|
324
345
|
await waitForTask(harness.manager, task.taskId)
|
|
325
346
|
|
|
@@ -342,7 +363,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
342
363
|
|
|
343
364
|
const task = await harness.manager.sendMessage(
|
|
344
365
|
buildOptions('child-msgs', harness.parentSession.id, harness.projectId),
|
|
345
|
-
buildContext(harness.parentSession.id, harness.projectId),
|
|
366
|
+
buildContext(harness.parentSession.id, harness.projectId, harness.threadId),
|
|
346
367
|
)
|
|
347
368
|
await waitForTask(harness.manager, task.taskId)
|
|
348
369
|
|
|
@@ -364,7 +385,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
364
385
|
|
|
365
386
|
// Seed c1 under parentSession, c2 under c1.
|
|
366
387
|
const c1 = await harness.store.createSession(
|
|
367
|
-
{ projectId: harness.projectId, currentActor: agentActor('c1') },
|
|
388
|
+
{ threadId: harness.threadId, projectId: harness.projectId, currentActor: agentActor('c1') },
|
|
368
389
|
tenant,
|
|
369
390
|
)
|
|
370
391
|
await harness.store.createSubSession(
|
|
@@ -377,7 +398,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
377
398
|
tenant,
|
|
378
399
|
)
|
|
379
400
|
const c2 = await harness.store.createSession(
|
|
380
|
-
{ projectId: harness.projectId, currentActor: agentActor('c2') },
|
|
401
|
+
{ threadId: harness.threadId, projectId: harness.projectId, currentActor: agentActor('c2') },
|
|
381
402
|
tenant,
|
|
382
403
|
)
|
|
383
404
|
await harness.store.createSubSession(
|
|
@@ -393,7 +414,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
393
414
|
const events: RunEvent[] = []
|
|
394
415
|
const task = await harness.manager.sendMessage(
|
|
395
416
|
buildOptions('grandchild', c2.id, harness.projectId),
|
|
396
|
-
buildContext(c2.id, harness.projectId),
|
|
417
|
+
buildContext(c2.id, harness.projectId, harness.threadId),
|
|
397
418
|
(e) => {
|
|
398
419
|
events.push(e)
|
|
399
420
|
},
|
|
@@ -440,7 +461,7 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
440
461
|
await expect(
|
|
441
462
|
harness.manager.sendMessage(
|
|
442
463
|
mismatchedOptions,
|
|
443
|
-
buildContext(harness.parentSession.id, harness.projectId, tenant),
|
|
464
|
+
buildContext(harness.parentSession.id, harness.projectId, harness.threadId, tenant),
|
|
444
465
|
),
|
|
445
466
|
).rejects.toThrow(/Tenant mismatch/)
|
|
446
467
|
})
|
|
@@ -35,6 +35,7 @@ import { ZERO_COST } from '../../utils/cost.js'
|
|
|
35
35
|
import { toErrorMessage } from '../../utils/error.js'
|
|
36
36
|
import { generateTaskId } from '../../utils/id.js'
|
|
37
37
|
import { type Logger, getRootLogger } from '../../utils/logger.js'
|
|
38
|
+
import type { ThreadManager } from '../thread/lifecycle.js'
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Dependencies threaded into {@link AgentManager}. Phase 6 promoted the
|
|
@@ -60,6 +61,14 @@ export interface AgentManagerDeps {
|
|
|
60
61
|
readonly workspaceRegistry: WorkspaceBackendRegistry
|
|
61
62
|
readonly summaryMaterializer: SessionSummaryMaterializer
|
|
62
63
|
readonly capacity: CapacityValidator
|
|
64
|
+
/**
|
|
65
|
+
* Gate session creation on the parent Thread being `'open'` via
|
|
66
|
+
* {@link ThreadManager.requireOpen}. Added in Phase 2.6 to close the
|
|
67
|
+
* archive-gate gap flagged by the Phase 2.5 commit: without this,
|
|
68
|
+
* `ThreadManager.archive` was best-effort because spawn could still
|
|
69
|
+
* attach a live session under an archived Thread.
|
|
70
|
+
*/
|
|
71
|
+
readonly threadManager: ThreadManager
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
interface ChildSpawnRecord {
|
|
@@ -146,6 +155,7 @@ export class AgentManager {
|
|
|
146
155
|
budgetTracker: context.budgetTracker,
|
|
147
156
|
factoryOptions: context.factoryOptions,
|
|
148
157
|
tenantId: context.tenantId,
|
|
158
|
+
threadId: context.threadId,
|
|
149
159
|
sessionId: spawnRecord.childSessionId,
|
|
150
160
|
projectId: context.projectId,
|
|
151
161
|
parentActor: childParentActor,
|
|
@@ -203,12 +213,16 @@ export class AgentManager {
|
|
|
203
213
|
|
|
204
214
|
const definition = this.registry.getOrThrow(options.agentId)
|
|
205
215
|
let childConfig: BaseAgentConfig
|
|
206
|
-
if (definition.configBuilder
|
|
216
|
+
if (definition.configBuilder) {
|
|
217
|
+
// Call the configBuilder regardless of whether factoryOptions were
|
|
218
|
+
// supplied. BYO-provider flows (Bedrock IAM, custom ProviderRegistry)
|
|
219
|
+
// commonly omit factoryOptions because the provider resolves its own
|
|
220
|
+
// credentials; the builder still needs to run to wire provider+tools.
|
|
221
|
+
// Defaults: empty factoryOptions when omitted; configOverrides win.
|
|
207
222
|
childConfig = await definition.configBuilder({
|
|
208
|
-
...context.factoryOptions,
|
|
223
|
+
...(context.factoryOptions ?? {}),
|
|
209
224
|
tokenBudget: allocatedTokens,
|
|
210
225
|
timeoutMs: options.budgetAllocation?.timeoutMs ?? context.budgetTracker.remaining,
|
|
211
|
-
threadId: context.projectId as string,
|
|
212
226
|
parentRunId: context.parentRunId as string | undefined,
|
|
213
227
|
depth: context.depth + 1,
|
|
214
228
|
...options.configOverrides,
|
|
@@ -222,10 +236,11 @@ export class AgentManager {
|
|
|
222
236
|
// configBuilder may not have been updated to emit these yet; we
|
|
223
237
|
// stamp them here so query() sees them regardless.
|
|
224
238
|
childConfig.sessionId = spawnRecord?.childSessionId ?? context.sessionId
|
|
239
|
+
childConfig.threadId = context.threadId
|
|
225
240
|
childConfig.projectId = context.projectId
|
|
226
241
|
childConfig.tenantId = context.tenantId
|
|
227
242
|
} else {
|
|
228
|
-
this.log.warn('No configBuilder
|
|
243
|
+
this.log.warn('No configBuilder, using bare config', {
|
|
229
244
|
agentId: options.agentId,
|
|
230
245
|
})
|
|
231
246
|
childConfig = {
|
|
@@ -236,8 +251,8 @@ export class AgentManager {
|
|
|
236
251
|
maxIterations: options.configOverrides?.maxIterations,
|
|
237
252
|
maxResponseTokens: options.configOverrides?.maxResponseTokens,
|
|
238
253
|
env: options.configOverrides?.env,
|
|
239
|
-
threadId: context.projectId,
|
|
240
254
|
sessionId: spawnRecord.childSessionId,
|
|
255
|
+
threadId: context.threadId,
|
|
241
256
|
projectId: context.projectId,
|
|
242
257
|
tenantId: context.tenantId,
|
|
243
258
|
parentRunId: context.parentRunId,
|
|
@@ -367,6 +382,42 @@ export class AgentManager {
|
|
|
367
382
|
// partial/legacy path).
|
|
368
383
|
const store = this.deps.sessionStore
|
|
369
384
|
|
|
385
|
+
// Thread archive gate — runs FIRST so an archived thread fails fastest
|
|
386
|
+
// with the correct error (not DelegationCapacityExceeded or a project
|
|
387
|
+
// lookup error). Phase 2.6 closes the gap the Phase 2.5 commit
|
|
388
|
+
// flagged: without it, `ThreadManager.archive` could be undermined by
|
|
389
|
+
// a concurrent spawn landing a live session post-archival.
|
|
390
|
+
// Scope: this gate enforces the archive invariant at the production
|
|
391
|
+
// ingress path (AgentManager.sendMessage + handoff flows). Direct
|
|
392
|
+
// callers of `SessionStore.createSession` bypass it — the store layer
|
|
393
|
+
// is intentionally unaware of thread status to preserve its
|
|
394
|
+
// single-responsibility boundary.
|
|
395
|
+
await this.deps.threadManager.requireOpen(context.threadId, context.tenantId)
|
|
396
|
+
|
|
397
|
+
// Parent session cross-check: validate that `options.parentSessionId`
|
|
398
|
+
// exists for this tenant AND lives under the same thread as the
|
|
399
|
+
// context. A mismatched `context.threadId` would otherwise attach the
|
|
400
|
+
// child's sub-session edge to a parent in a different thread —
|
|
401
|
+
// corrupting the hierarchy invariant (cross-thread spawn is forbidden
|
|
402
|
+
// by design). Mirrors the `source.threadId === assignment.threadId`
|
|
403
|
+
// check in handoff (Phase 2.4).
|
|
404
|
+
const parentSession = await store.getSession(options.parentSessionId, context.tenantId)
|
|
405
|
+
if (!parentSession) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Parent session ${options.parentSessionId} not found for tenant ${context.tenantId} — spawn rejected`,
|
|
408
|
+
)
|
|
409
|
+
}
|
|
410
|
+
if (parentSession.threadId !== context.threadId) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`Thread mismatch on spawn: parent session ${parentSession.id} is on thread ${parentSession.threadId}, but context.threadId=${context.threadId}. Cross-thread spawn is forbidden (session-hierarchy.md §6.3).`,
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
if (parentSession.projectId !== context.projectId) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`Project mismatch on spawn: parent session ${parentSession.id} is on project ${parentSession.projectId}, but context.projectId=${context.projectId}.`,
|
|
418
|
+
)
|
|
419
|
+
}
|
|
420
|
+
|
|
370
421
|
const project = await store.getProject(context.projectId, context.tenantId)
|
|
371
422
|
if (!project) {
|
|
372
423
|
throw new Error(
|
|
@@ -401,45 +452,73 @@ export class AgentManager {
|
|
|
401
452
|
parentActor: context.parentActor,
|
|
402
453
|
}
|
|
403
454
|
|
|
455
|
+
// Child session inherits the parent's threadId verbatim (cross-thread
|
|
456
|
+
// spawn is forbidden by design — a delegated sub-agent stays on the
|
|
457
|
+
// same topic). Phase 2.6 elides the previous parent-session read by
|
|
458
|
+
// carrying `threadId` on `AgentTaskContext`.
|
|
404
459
|
const childSession = await store.createSession(
|
|
405
|
-
{ projectId: context.projectId, currentActor: childActor },
|
|
406
|
-
context.tenantId,
|
|
407
|
-
)
|
|
408
|
-
|
|
409
|
-
// Flip to 'active' so the materializer's atomic write + status flip
|
|
410
|
-
// lands on terminal — §5.3: pending→active→idle.
|
|
411
|
-
await store.updateSession({ ...childSession, status: 'active' }, context.tenantId)
|
|
412
|
-
|
|
413
|
-
const subSession = await store.createSubSession(
|
|
414
460
|
{
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
spawnedBy: context.parentActor,
|
|
419
|
-
failureMode: 'delegate',
|
|
420
|
-
completionMode: 'summary_ref',
|
|
461
|
+
threadId: context.threadId,
|
|
462
|
+
projectId: context.projectId,
|
|
463
|
+
currentActor: childActor,
|
|
421
464
|
},
|
|
422
465
|
context.tenantId,
|
|
423
466
|
)
|
|
424
467
|
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
//
|
|
429
|
-
//
|
|
430
|
-
//
|
|
468
|
+
// Compensating rollback wraps every mutation after createSession so a
|
|
469
|
+
// mid-flight failure (status flip, subsession insert, workspace driver)
|
|
470
|
+
// leaves no orphan child session. Codex SPAWN-ROLLBACK critique (Phase
|
|
471
|
+
// 2 review, 2026-04-18): without this, `workspaceRegistry.get().create`
|
|
472
|
+
// throwing — or a concurrent `updateSession` race — strands an
|
|
473
|
+
// `active` child session with no subsession edge, invisible to the
|
|
474
|
+
// parent but counted against `maxDelegationWidth`.
|
|
475
|
+
let subSession: Awaited<ReturnType<typeof store.createSubSession>> | undefined
|
|
431
476
|
let workspaceRef: WorkspaceRef | undefined
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
477
|
+
try {
|
|
478
|
+
// Flip to 'active' so the materializer's atomic write + status flip
|
|
479
|
+
// lands on terminal — §5.3: pending→active→idle.
|
|
480
|
+
await store.updateSession({ ...childSession, status: 'active' }, context.tenantId)
|
|
481
|
+
|
|
482
|
+
subSession = await store.createSubSession(
|
|
483
|
+
{
|
|
484
|
+
parentSessionId: options.parentSessionId,
|
|
485
|
+
childSessionId: childSession.id,
|
|
486
|
+
kind: 'agent_spawn',
|
|
487
|
+
spawnedBy: context.parentActor,
|
|
488
|
+
failureMode: 'delegate',
|
|
489
|
+
completionMode: 'summary_ref',
|
|
490
|
+
},
|
|
491
|
+
context.tenantId,
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
// Workspace provisioning — best-effort. When the requested backend
|
|
495
|
+
// is registered we create a new workspace for the child; failures
|
|
496
|
+
// surface as WorkspaceBackendError and abort the spawn (Convention
|
|
497
|
+
// #0: no silent fallback). Pattern doc §7.1 allows lazy
|
|
498
|
+
// provisioning: an unregistered backend leaves `workspaceRef:
|
|
499
|
+
// undefined` on the spawn record, not a hard error — the registry
|
|
500
|
+
// is the capability surface.
|
|
501
|
+
const backend = options.workspaceBackend ?? 'git-worktree'
|
|
502
|
+
if (this.deps.workspaceRegistry.has(backend)) {
|
|
503
|
+
const driver = this.deps.workspaceRegistry.get(backend)
|
|
436
504
|
workspaceRef = await driver.create({ label: subSession.id })
|
|
437
|
-
} catch (err) {
|
|
438
|
-
// Surface the failure — the subsession record exists but is
|
|
439
|
-
// unusable without a workspace. Dispose any partial state.
|
|
440
|
-
await store.updateSubSession({ ...subSession, status: 'failed' }, context.tenantId)
|
|
441
|
-
throw err
|
|
442
505
|
}
|
|
506
|
+
} catch (err) {
|
|
507
|
+
// Compensating rollback order is mandated by the store's
|
|
508
|
+
// deny-by-default cascade policy (Convention #5): `deleteSession`
|
|
509
|
+
// throws when any subsession still references it, so the subsession
|
|
510
|
+
// record must be removed first. No failed-subsession audit row is
|
|
511
|
+
// kept — the `subsession_spawned` run event never fired (we aborted
|
|
512
|
+
// before `buildSpawnRecord`), so no observer is expecting one, and
|
|
513
|
+
// leaving a `status: 'failed'` breadcrumb would be a dangling
|
|
514
|
+
// record with no corresponding emission. The original `err` is the
|
|
515
|
+
// caller-visible signal; cleanup errors are swallowed so they
|
|
516
|
+
// cannot mask it.
|
|
517
|
+
if (subSession !== undefined) {
|
|
518
|
+
await store.deleteSubSession(subSession.id, context.tenantId).catch(() => undefined)
|
|
519
|
+
}
|
|
520
|
+
await store.deleteSession(childSession.id, context.tenantId).catch(() => undefined)
|
|
521
|
+
throw err
|
|
443
522
|
}
|
|
444
523
|
|
|
445
524
|
return {
|
package/src/manager/index.ts
CHANGED
|
@@ -16,4 +16,7 @@ export type {
|
|
|
16
16
|
export { PlanManager } from './plan/lifecycle.js'
|
|
17
17
|
export type { PlanEvent, PlanEventListener, PlanApprovalHandler } from './plan/lifecycle.js'
|
|
18
18
|
|
|
19
|
+
export { ThreadManager } from './thread/lifecycle.js'
|
|
20
|
+
export type { ThreadManagerDeps } from './thread/lifecycle.js'
|
|
21
|
+
|
|
19
22
|
export { AgentManager } from './agent/lifecycle.js'
|
|
@@ -5,7 +5,7 @@ import type { RunId, SessionId, TenantId } from '../../types/ids/index.js'
|
|
|
5
5
|
import type { AssistantMessage, Message } from '../../types/message/index.js'
|
|
6
6
|
import type { EmergencySaveData } from '../../types/run/emergency.js'
|
|
7
7
|
import type { AgentRun, RunPersistenceConfig, StopReason } from '../../types/run/index.js'
|
|
8
|
-
import type { ProjectId } from '../../types/session/ids.js'
|
|
8
|
+
import type { ProjectId, ThreadId } from '../../types/session/ids.js'
|
|
9
9
|
import { type ModelPricing, ZERO_COST, accumulateCost } from '../../utils/cost.js'
|
|
10
10
|
import { generateEmergencySaveId } from '../../utils/id.js'
|
|
11
11
|
import type { Logger } from '../../utils/logger.js'
|
|
@@ -16,6 +16,7 @@ export class RunPersistence {
|
|
|
16
16
|
private pricing?: ModelPricing
|
|
17
17
|
private log: Logger
|
|
18
18
|
private readonly _sessionId: SessionId
|
|
19
|
+
private readonly _threadId: ThreadId
|
|
19
20
|
private readonly _tenantId: TenantId
|
|
20
21
|
private readonly _projectId: ProjectId
|
|
21
22
|
|
|
@@ -23,6 +24,7 @@ export class RunPersistence {
|
|
|
23
24
|
this.pricing = config.pricing
|
|
24
25
|
this.log = config.log
|
|
25
26
|
this._sessionId = config.sessionId
|
|
27
|
+
this._threadId = config.threadId
|
|
26
28
|
this._tenantId = config.tenantId
|
|
27
29
|
this._projectId = config.projectId
|
|
28
30
|
|
|
@@ -58,6 +60,10 @@ export class RunPersistence {
|
|
|
58
60
|
return this._sessionId
|
|
59
61
|
}
|
|
60
62
|
|
|
63
|
+
get threadId(): ThreadId {
|
|
64
|
+
return this._threadId
|
|
65
|
+
}
|
|
66
|
+
|
|
61
67
|
get tenantId(): TenantId {
|
|
62
68
|
return this._tenantId
|
|
63
69
|
}
|