@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,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ThreadManager — thin orchestrator over {@link ThreadStore} and
|
|
3
|
+
* {@link SessionStore}.
|
|
4
|
+
*
|
|
5
|
+
* Owns user-facing lifecycle operations on the Thread topic layer plus the
|
|
6
|
+
* archive-gate contract enforced at session-creation ingress sites.
|
|
7
|
+
*
|
|
8
|
+
* Phase 2.6 wired `ThreadManager.requireOpen` into three ingress paths:
|
|
9
|
+
* - {@link AgentManager.provisionSpawn} (child session creation)
|
|
10
|
+
* - `executeSingleHandoff` (recipient session creation)
|
|
11
|
+
* - `executeBroadcastHandoff` (N recipient sessions per fan-out)
|
|
12
|
+
* Those call sites depend on this manager, so the one-method indirection
|
|
13
|
+
* stopped being "structural overhead" the moment archive/delete needed the
|
|
14
|
+
* session-presence cross-check anyway.
|
|
15
|
+
*
|
|
16
|
+
* Archive + delete require cross-store preconditions (session-presence
|
|
17
|
+
* checks) — enforced here where both stores are in scope. The stores
|
|
18
|
+
* themselves stay unaware of each other's layout (Convention #0), which is
|
|
19
|
+
* why the gate lives at the manager layer rather than as a universal store
|
|
20
|
+
* interceptor (see `archive` JSDoc for the direct-store-bypass boundary).
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import {
|
|
24
|
+
THREAD_NOT_EMPTY_SAMPLE_LIMIT,
|
|
25
|
+
ThreadClosedError,
|
|
26
|
+
ThreadNotEmptyError,
|
|
27
|
+
} from '../../session/errors.js'
|
|
28
|
+
import type { Session, SessionStatus } from '../../session/hierarchy/session.js'
|
|
29
|
+
import type { Thread } from '../../session/hierarchy/thread.js'
|
|
30
|
+
import type { TenantId } from '../../types/ids/index.js'
|
|
31
|
+
import type { ProjectId, ThreadId } from '../../types/session/ids.js'
|
|
32
|
+
import type { SessionStore } from '../../types/session/store.js'
|
|
33
|
+
import type { CreateThreadParams, ThreadStore } from '../../types/thread/store.js'
|
|
34
|
+
|
|
35
|
+
export interface ThreadManagerDeps {
|
|
36
|
+
readonly threadStore: ThreadStore
|
|
37
|
+
readonly sessionStore: SessionStore
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Session statuses that block Thread archival. A session in any of these
|
|
42
|
+
* states has live work in-flight (mid-run, mid-handoff, blocked on human
|
|
43
|
+
* input, or orchestrating a broadcast merge) — freezing the Thread while any
|
|
44
|
+
* of them are active would strand resumable work.
|
|
45
|
+
*
|
|
46
|
+
* `idle`, `failed`, and `archived` are archival-compatible: they are
|
|
47
|
+
* quiescent or already-terminal, so a newly-frozen Thread can safely contain
|
|
48
|
+
* them. This list mirrors the `SessionStatus` discriminants that represent
|
|
49
|
+
* "not-yet-done" work (session-hierarchy.md §5.1).
|
|
50
|
+
*/
|
|
51
|
+
const ARCHIVAL_BLOCKING_STATUSES: ReadonlySet<SessionStatus> = new Set([
|
|
52
|
+
'active',
|
|
53
|
+
'locked',
|
|
54
|
+
'awaiting_hitl',
|
|
55
|
+
'awaiting_merge',
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
export class ThreadManager {
|
|
59
|
+
private readonly deps: ThreadManagerDeps
|
|
60
|
+
|
|
61
|
+
constructor(deps: ThreadManagerDeps) {
|
|
62
|
+
this.deps = deps
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Persist a new Thread. Thin passthrough for uniformity at the manager surface. */
|
|
66
|
+
create(params: CreateThreadParams, tenantId: TenantId): Promise<Thread> {
|
|
67
|
+
return this.deps.threadStore.createThread(params, tenantId)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Read a Thread by id; returns `null` when absent for the tenant. */
|
|
71
|
+
get(threadId: ThreadId, tenantId: TenantId): Promise<Thread | null> {
|
|
72
|
+
return this.deps.threadStore.getThread(threadId, tenantId)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* CAS update on a Thread. Propagates {@link import('../../session/errors.js').StaleThreadError}
|
|
77
|
+
* from the store on `ownerVersion` mismatch — callers re-read, re-apply,
|
|
78
|
+
* and retry.
|
|
79
|
+
*/
|
|
80
|
+
update(thread: Thread, tenantId: TenantId): Promise<void> {
|
|
81
|
+
return this.deps.threadStore.updateThread(thread, tenantId)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** List Threads under a Project, ordered by `createdAt` ascending. */
|
|
85
|
+
list(projectId: ProjectId, tenantId: TenantId): Promise<readonly Thread[]> {
|
|
86
|
+
return this.deps.threadStore.listThreads(projectId, tenantId)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Load a Thread and assert it is in `'open'` state. Used by the spawn path
|
|
91
|
+
* as a precondition — a SubSession cannot be created under an archived
|
|
92
|
+
* Thread. Throws on absence and on archival; returns the loaded Thread on
|
|
93
|
+
* success so callers can avoid the second round-trip.
|
|
94
|
+
*
|
|
95
|
+
* Convention #5: deny-by-default. A missing Thread is a hard error, not a
|
|
96
|
+
* silent "assume archived".
|
|
97
|
+
*/
|
|
98
|
+
async requireOpen(threadId: ThreadId, tenantId: TenantId): Promise<Thread> {
|
|
99
|
+
const thread = await this.deps.threadStore.getThread(threadId, tenantId)
|
|
100
|
+
if (!thread) {
|
|
101
|
+
throw new Error(`Thread ${threadId} not found`)
|
|
102
|
+
}
|
|
103
|
+
if (thread.status === 'archived') {
|
|
104
|
+
throw new ThreadClosedError({ threadId, op: 'require-open' })
|
|
105
|
+
}
|
|
106
|
+
return thread
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Flip a Thread to `'archived'` via CAS on {@link Thread.ownerVersion}.
|
|
111
|
+
*
|
|
112
|
+
* Preconditions (checked in order):
|
|
113
|
+
* 1. Thread exists for the tenant (throws on absence).
|
|
114
|
+
* 2. No attached Session is in a non-terminal state (see
|
|
115
|
+
* {@link ARCHIVAL_BLOCKING_STATUSES}). The presence check runs
|
|
116
|
+
* **before** the idempotent-archive short-circuit so that an already
|
|
117
|
+
* archived thread harboring a live session still surfaces as
|
|
118
|
+
* {@link ThreadNotEmptyError} rather than a silent success.
|
|
119
|
+
* 3. If the thread is already `'archived'` the method short-circuits
|
|
120
|
+
* without an `updateThread` write (idempotent re-archival). The
|
|
121
|
+
* returned record reflects the current persisted state.
|
|
122
|
+
*
|
|
123
|
+
* On a fresh archive transition the underlying
|
|
124
|
+
* {@link ThreadStore.updateThread} call commits with `ownerVersion + 1`.
|
|
125
|
+
* A {@link import('../../session/errors.js').StaleThreadError} from a
|
|
126
|
+
* concurrent writer propagates unchanged — the caller is expected to
|
|
127
|
+
* re-read + retry (mirrors the `updateThread` contract).
|
|
128
|
+
*
|
|
129
|
+
* Gate scope (Phase 2.6): `ThreadManager.requireOpen` is wired into
|
|
130
|
+
* `AgentManager.provisionSpawn` and both handoff flows, so the production
|
|
131
|
+
* ingress paths cannot attach new sessions under an archived thread.
|
|
132
|
+
* `SessionStore.createSession` / `updateSession` remain public and
|
|
133
|
+
* ungated at the store layer — a direct caller can still mutate a
|
|
134
|
+
* session after archival (the store has no `ThreadStore` handle by
|
|
135
|
+
* design; cross-store awareness lives in the manager). The defensive
|
|
136
|
+
* re-check above catches a smuggled live session on a subsequent
|
|
137
|
+
* archive call, but does not prevent the direct-store write from
|
|
138
|
+
* landing. That's an acceptable boundary — kernel callers must go
|
|
139
|
+
* through the ingress paths; direct store consumers are out of scope
|
|
140
|
+
* for the archive invariant.
|
|
141
|
+
*/
|
|
142
|
+
async archive(threadId: ThreadId, tenantId: TenantId): Promise<Thread> {
|
|
143
|
+
const thread = await this.deps.threadStore.getThread(threadId, tenantId)
|
|
144
|
+
if (!thread) {
|
|
145
|
+
throw new Error(`Thread ${threadId} not found`)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Always enforce the blocking-session invariant — even on re-archival.
|
|
149
|
+
// If the thread is already archived but somehow gained a live session
|
|
150
|
+
// (direct store mutation, concurrent spawn before a write-barrier
|
|
151
|
+
// existed), surfacing that via ThreadNotEmptyError is more useful to
|
|
152
|
+
// operators than a silent idempotent success.
|
|
153
|
+
const sessions = await this.deps.sessionStore.listSessions(threadId, tenantId)
|
|
154
|
+
const blocking = sessions.filter((s) => ARCHIVAL_BLOCKING_STATUSES.has(s.status))
|
|
155
|
+
if (blocking.length > 0) {
|
|
156
|
+
throw new ThreadNotEmptyError({
|
|
157
|
+
threadId,
|
|
158
|
+
tenantId,
|
|
159
|
+
op: 'archive',
|
|
160
|
+
blockingSessions: summarizeBlocking(blocking),
|
|
161
|
+
totalBlockingSessions: blocking.length,
|
|
162
|
+
})
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (thread.status === 'archived') {
|
|
166
|
+
// Idempotent: already archived, no live sessions attached. Skip the
|
|
167
|
+
// write (updateThread would still bump ownerVersion for no semantic
|
|
168
|
+
// change).
|
|
169
|
+
return thread
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const next: Thread = { ...thread, status: 'archived' }
|
|
173
|
+
await this.deps.threadStore.updateThread(next, tenantId)
|
|
174
|
+
// updateThread advances ownerVersion + updatedAt; re-read so the returned
|
|
175
|
+
// record reflects the persisted state (callers rely on version monotonicity).
|
|
176
|
+
const reloaded = await this.deps.threadStore.getThread(threadId, tenantId)
|
|
177
|
+
if (!reloaded) {
|
|
178
|
+
throw new Error(`Thread ${threadId} vanished between archive and read-back`)
|
|
179
|
+
}
|
|
180
|
+
return reloaded
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Hard-delete a Thread record. Rejects with {@link ThreadNotEmptyError}
|
|
185
|
+
* (`op: 'delete'`) when ANY Session still references the Thread —
|
|
186
|
+
* deletion is stricter than archival, which tolerates quiescent sessions.
|
|
187
|
+
* Callers must first delete or archive-and-tombstone every attached
|
|
188
|
+
* session (via {@link SessionStore.deleteSession}) before invoking.
|
|
189
|
+
*
|
|
190
|
+
* The session scan runs unconditionally, so orphaned sessions pointing at
|
|
191
|
+
* a missing thread are still detected and reject the delete. Idempotent
|
|
192
|
+
* for genuinely absent threads (no sessions, no thread record) — missing
|
|
193
|
+
* thread + empty session list is a no-op at the store layer. Convention
|
|
194
|
+
* #5: deny-by-default; no implicit cascade into SessionStore.
|
|
195
|
+
*/
|
|
196
|
+
async delete(threadId: ThreadId, tenantId: TenantId): Promise<void> {
|
|
197
|
+
const sessions = await this.deps.sessionStore.listSessions(threadId, tenantId)
|
|
198
|
+
if (sessions.length > 0) {
|
|
199
|
+
throw new ThreadNotEmptyError({
|
|
200
|
+
threadId,
|
|
201
|
+
tenantId,
|
|
202
|
+
op: 'delete',
|
|
203
|
+
blockingSessions: summarizeBlocking(sessions),
|
|
204
|
+
totalBlockingSessions: sessions.length,
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
await this.deps.threadStore.deleteThread(threadId, tenantId)
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function summarizeBlocking(
|
|
212
|
+
sessions: readonly Session[],
|
|
213
|
+
): ReadonlyArray<{ sessionId: Session['id']; status: SessionStatus }> {
|
|
214
|
+
return sessions
|
|
215
|
+
.slice(0, THREAD_NOT_EMPTY_SAMPLE_LIMIT)
|
|
216
|
+
.map((s) => ({ sessionId: s.id, status: s.status }))
|
|
217
|
+
}
|
package/src/rag/retriever.ts
CHANGED
|
@@ -57,11 +57,11 @@ export class DefaultRetriever implements Retriever {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
private expandQuery(query: RetrievalQuery): string {
|
|
60
|
-
if (!query.
|
|
60
|
+
if (!query.recentMessages || query.recentMessages.length === 0) {
|
|
61
61
|
return query.text
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const recentContext = query.
|
|
64
|
+
const recentContext = query.recentMessages.slice(-3).join(' ')
|
|
65
65
|
return `${query.text}\n\nContext: ${recentContext}`
|
|
66
66
|
}
|
|
67
67
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SpanStatusCode } from '@opentelemetry/api'
|
|
2
2
|
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
3
|
-
import { getTracer } from '../../provider/telemetry/setup.js'
|
|
4
3
|
import { GENAI, NAMZU, toolSpanName } from '../../telemetry/attributes.js'
|
|
4
|
+
import { getTracer } from '../../telemetry/runtime-accessors.js'
|
|
5
5
|
import type {
|
|
6
6
|
LLMToolSchema,
|
|
7
7
|
ToolAvailability,
|
|
@@ -3,7 +3,7 @@ import { DefaultPathBuilder, type PathBuilder } from '../../../session/workspace
|
|
|
3
3
|
import type { RunId, SessionId, TenantId } from '../../../types/ids/index.js'
|
|
4
4
|
import type { LLMProvider } from '../../../types/provider/index.js'
|
|
5
5
|
import type { AgentRunConfig } from '../../../types/run/index.js'
|
|
6
|
-
import type { ProjectId } from '../../../types/session/ids.js'
|
|
6
|
+
import type { ProjectId, ThreadId } from '../../../types/session/ids.js'
|
|
7
7
|
import { RunContextFactory } from '../context.js'
|
|
8
8
|
|
|
9
9
|
function mockProvider(): LLMProvider {
|
|
@@ -16,6 +16,7 @@ function mockProvider(): LLMProvider {
|
|
|
16
16
|
|
|
17
17
|
function buildConfig(overrides: Partial<Parameters<typeof RunContextFactory.build>[0]> = {}) {
|
|
18
18
|
const sessionId = 'ses_test' as SessionId
|
|
19
|
+
const threadId = 'thd_test' as ThreadId
|
|
19
20
|
const projectId = 'prj_test' as ProjectId
|
|
20
21
|
const tenantId = 'tnt_test' as TenantId
|
|
21
22
|
const runConfig: AgentRunConfig = {
|
|
@@ -31,6 +32,7 @@ function buildConfig(overrides: Partial<Parameters<typeof RunContextFactory.buil
|
|
|
31
32
|
provider: mockProvider(),
|
|
32
33
|
messages: [],
|
|
33
34
|
sessionId,
|
|
35
|
+
threadId,
|
|
34
36
|
projectId,
|
|
35
37
|
tenantId,
|
|
36
38
|
workingDirectory: '/tmp/run-context-test',
|
|
@@ -38,16 +40,15 @@ function buildConfig(overrides: Partial<Parameters<typeof RunContextFactory.buil
|
|
|
38
40
|
}
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
describe('RunContextFactory.build
|
|
42
|
-
it('requires sessionId, projectId, tenantId and returns them on the context', () => {
|
|
43
|
+
describe('RunContextFactory.build', () => {
|
|
44
|
+
it('requires sessionId, threadId, projectId, tenantId and returns them on the context', () => {
|
|
43
45
|
const cfg = buildConfig()
|
|
44
46
|
const ctx = RunContextFactory.build(cfg)
|
|
45
47
|
|
|
46
48
|
expect(ctx.sessionId).toBe(cfg.sessionId)
|
|
49
|
+
expect(ctx.threadId).toBe(cfg.threadId)
|
|
47
50
|
expect(ctx.projectId).toBe(cfg.projectId)
|
|
48
51
|
expect(ctx.tenantId).toBe(cfg.tenantId)
|
|
49
|
-
// threadId remains as a deprecated mirror of projectId.
|
|
50
|
-
expect(ctx.threadId).toBe(cfg.projectId)
|
|
51
52
|
})
|
|
52
53
|
|
|
53
54
|
it('uses the injected PathBuilder to resolve the output dir (no hardcoded .namzu/threads)', () => {
|
|
@@ -72,17 +73,17 @@ describe('RunContextFactory.build — Phase 6', () => {
|
|
|
72
73
|
const cfg = buildConfig()
|
|
73
74
|
const ctx = RunContextFactory.build(cfg)
|
|
74
75
|
|
|
75
|
-
//
|
|
76
|
-
// projects/{pid}/sessions/{sid}.
|
|
76
|
+
// Layout lives under projects/{pid}/sessions/{sid} — no `.namzu/threads/`.
|
|
77
77
|
expect(ctx.outputDir).toContain('/.namzu/projects/prj_test/sessions/ses_test')
|
|
78
78
|
expect(ctx.outputDir).not.toContain('threads')
|
|
79
79
|
})
|
|
80
80
|
|
|
81
|
-
it('seeds RunPersistence with propagated sessionId/tenantId/projectId', () => {
|
|
81
|
+
it('seeds RunPersistence with propagated sessionId/threadId/tenantId/projectId', () => {
|
|
82
82
|
const cfg = buildConfig()
|
|
83
83
|
const ctx = RunContextFactory.build(cfg)
|
|
84
84
|
|
|
85
85
|
expect(ctx.runMgr.sessionId).toBe(cfg.sessionId)
|
|
86
|
+
expect(ctx.runMgr.threadId).toBe(cfg.threadId)
|
|
86
87
|
expect(ctx.runMgr.tenantId).toBe(cfg.tenantId)
|
|
87
88
|
expect(ctx.runMgr.projectId).toBe(cfg.projectId)
|
|
88
89
|
})
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import type { AgentContextLevel } from '../../types/agent/factory.js'
|
|
3
|
-
import type { ThreadId } from '../../types/ids/index.js'
|
|
4
3
|
import type { AgentPersona } from '../../types/persona/index.js'
|
|
4
|
+
import type { ProjectId } from '../../types/session/ids.js'
|
|
5
5
|
import type { Skill } from '../../types/skills/index.js'
|
|
6
6
|
import type { ToolRegistryContract } from '../../types/tool/index.js'
|
|
7
7
|
import { PromptBuilder, type PromptSegments } from './prompt.js'
|
|
8
8
|
|
|
9
9
|
export interface ContextCacheConfig {
|
|
10
10
|
agentId: string
|
|
11
|
-
|
|
11
|
+
projectId: ProjectId
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export interface PromptCacheInput {
|
|
@@ -21,7 +21,7 @@ export interface PromptCacheInput {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export class ContextCache {
|
|
24
|
-
readonly
|
|
24
|
+
readonly projectId: ProjectId
|
|
25
25
|
readonly agentId: string
|
|
26
26
|
|
|
27
27
|
private cachedPrompt: string | undefined
|
|
@@ -30,7 +30,7 @@ export class ContextCache {
|
|
|
30
30
|
private cachedStaticHash: string | undefined
|
|
31
31
|
|
|
32
32
|
constructor(config: ContextCacheConfig) {
|
|
33
|
-
this.
|
|
33
|
+
this.projectId = config.projectId
|
|
34
34
|
this.agentId = config.agentId
|
|
35
35
|
}
|
|
36
36
|
|
|
@@ -11,29 +11,27 @@ import {
|
|
|
11
11
|
import { DefaultPathBuilder, type PathBuilder } from '../../session/workspace/path-builder.js'
|
|
12
12
|
import { ActivityStore } from '../../store/activity/memory.js'
|
|
13
13
|
import { type ActivityTrackingConfig, resolveActivityTracking } from '../../types/activity/index.js'
|
|
14
|
-
import type { RunId, SessionId, TenantId
|
|
14
|
+
import type { RunId, SessionId, TenantId } from '../../types/ids/index.js'
|
|
15
15
|
import type { Message } from '../../types/message/index.js'
|
|
16
16
|
import type { PermissionMode } from '../../types/permission/index.js'
|
|
17
17
|
import type { LLMProvider } from '../../types/provider/index.js'
|
|
18
18
|
import type { AgentRunConfig } from '../../types/run/index.js'
|
|
19
|
-
import type { ProjectId } from '../../types/session/ids.js'
|
|
19
|
+
import type { ProjectId, ThreadId } from '../../types/session/ids.js'
|
|
20
20
|
import type { ModelPricing } from '../../utils/cost.js'
|
|
21
21
|
import { generateRunId } from '../../utils/id.js'
|
|
22
22
|
import { type Logger, getRootLogger } from '../../utils/logger.js'
|
|
23
23
|
|
|
24
24
|
/**
|
|
25
|
-
* Config accepted by {@link RunContextFactory.build}.
|
|
26
|
-
* `
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* `projectId` — consumers can still pass it, but no new path layout honors it.
|
|
25
|
+
* Config accepted by {@link RunContextFactory.build}. `sessionId`,
|
|
26
|
+
* `threadId`, `projectId`, and `tenantId` are required — runs carry the full
|
|
27
|
+
* five-layer scope (Tenant → Project → Thread → Session → Run) per
|
|
28
|
+
* Convention #17.
|
|
30
29
|
*
|
|
31
30
|
* `pathBuilder` is optional; when absent a {@link DefaultPathBuilder} is
|
|
32
|
-
* constructed against `{workingDirectory}/.namzu
|
|
33
|
-
* `.namzu/threads` path.
|
|
31
|
+
* constructed against `{workingDirectory}/.namzu`.
|
|
34
32
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
33
|
+
* `filesystemMigrator` + `migrationSink` are optional; when absent a
|
|
34
|
+
* {@link DefaultFilesystemMigrator} wired to the
|
|
37
35
|
* {@link NOOP_FILESYSTEM_MIGRATION_SINK} is used. Migration runs once per
|
|
38
36
|
* process via {@link RunContextFactory.ensureMigrated}; the static `build`
|
|
39
37
|
* method stays synchronous so existing call sites are not broken — async
|
|
@@ -51,6 +49,7 @@ export interface RunContextConfig {
|
|
|
51
49
|
signal?: AbortSignal
|
|
52
50
|
|
|
53
51
|
sessionId: SessionId
|
|
52
|
+
threadId: ThreadId
|
|
54
53
|
projectId: ProjectId
|
|
55
54
|
tenantId: TenantId
|
|
56
55
|
|
|
@@ -73,21 +72,13 @@ export interface RunContextConfig {
|
|
|
73
72
|
depth?: number
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
/**
|
|
77
|
-
* Result of {@link RunContextFactory.build}. `threadId` remains as a
|
|
78
|
-
* deprecated read-only mirror of `projectId` for consumers still referencing
|
|
79
|
-
* the old name — scheduled for removal in 0.3.0 (session-hierarchy.md §13.1).
|
|
80
|
-
*/
|
|
75
|
+
/** Result of {@link RunContextFactory.build}. */
|
|
81
76
|
export interface RunContext {
|
|
82
77
|
runId: RunId
|
|
83
78
|
sessionId: SessionId
|
|
79
|
+
threadId: ThreadId
|
|
84
80
|
projectId: ProjectId
|
|
85
81
|
tenantId: TenantId
|
|
86
|
-
/**
|
|
87
|
-
* @deprecated Mirrors `projectId` — remove when callers migrate off the
|
|
88
|
-
* legacy name.
|
|
89
|
-
*/
|
|
90
|
-
threadId: ThreadId
|
|
91
82
|
runMgr: RunPersistence
|
|
92
83
|
activityStore: ActivityStore
|
|
93
84
|
planManager: PlanManager
|
|
@@ -156,6 +147,7 @@ export class RunContextFactory {
|
|
|
156
147
|
agent: config.agentName,
|
|
157
148
|
runId,
|
|
158
149
|
sessionId: config.sessionId,
|
|
150
|
+
threadId: config.threadId,
|
|
159
151
|
projectId: config.projectId,
|
|
160
152
|
tenantId: config.tenantId,
|
|
161
153
|
})
|
|
@@ -170,6 +162,7 @@ export class RunContextFactory {
|
|
|
170
162
|
pricing: config.pricing,
|
|
171
163
|
log,
|
|
172
164
|
sessionId: config.sessionId,
|
|
165
|
+
threadId: config.threadId,
|
|
173
166
|
tenantId: config.tenantId,
|
|
174
167
|
projectId: config.projectId,
|
|
175
168
|
parentRunId: config.parentRunId,
|
|
@@ -183,9 +176,9 @@ export class RunContextFactory {
|
|
|
183
176
|
return {
|
|
184
177
|
runId,
|
|
185
178
|
sessionId: config.sessionId,
|
|
179
|
+
threadId: config.threadId,
|
|
186
180
|
projectId: config.projectId,
|
|
187
181
|
tenantId: config.tenantId,
|
|
188
|
-
threadId: config.projectId as ThreadId,
|
|
189
182
|
runMgr,
|
|
190
183
|
activityStore,
|
|
191
184
|
planManager,
|
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
import { extractFromUserMessage } from '../../compaction/extractor.js'
|
|
8
8
|
import { WorkingStateManager } from '../../compaction/manager.js'
|
|
9
9
|
import type { CompactionConfig } from '../../config/runtime.js'
|
|
10
|
-
import { getTracer } from '../../provider/telemetry/setup.js'
|
|
11
10
|
import type { PathBuilder } from '../../session/workspace/path-builder.js'
|
|
12
11
|
import { GENAI, NAMZU, agentRunSpanName } from '../../telemetry/attributes.js'
|
|
12
|
+
import { getTracer } from '../../telemetry/runtime-accessors.js'
|
|
13
13
|
import { buildAdvisoryTools } from '../../tools/advisory/index.js'
|
|
14
14
|
import { SearchToolsTool } from '../../tools/builtins/search-tools.js'
|
|
15
15
|
import { buildTaskTools } from '../../tools/task/index.js'
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
type ResumeHandler,
|
|
22
22
|
autoApproveHandler,
|
|
23
23
|
} from '../../types/hitl/index.js'
|
|
24
|
-
import type { RunId, SessionId, TenantId
|
|
24
|
+
import type { RunId, SessionId, TenantId } from '../../types/ids/index.js'
|
|
25
25
|
import type { InvocationState } from '../../types/invocation/index.js'
|
|
26
26
|
import { type Message, createSystemMessage } from '../../types/message/index.js'
|
|
27
27
|
import type { AgentPersona } from '../../types/persona/index.js'
|
|
@@ -29,7 +29,7 @@ import type { LLMProvider } from '../../types/provider/index.js'
|
|
|
29
29
|
import type { TaskRouterConfig } from '../../types/router/index.js'
|
|
30
30
|
import type { AgentRun, AgentRunConfig, RunEvent, RunEventListener } from '../../types/run/index.js'
|
|
31
31
|
import type { Sandbox, SandboxProvider } from '../../types/sandbox/index.js'
|
|
32
|
-
import type { ProjectId } from '../../types/session/ids.js'
|
|
32
|
+
import type { ProjectId, ThreadId } from '../../types/session/ids.js'
|
|
33
33
|
import type { Skill } from '../../types/skills/index.js'
|
|
34
34
|
import type { TaskStore } from '../../types/task/index.js'
|
|
35
35
|
import type { ToolRegistryContract } from '../../types/tool/index.js'
|
|
@@ -67,30 +67,28 @@ export interface QueryParams {
|
|
|
67
67
|
resumeHandler: ResumeHandler
|
|
68
68
|
resumeFromCheckpoint?: CheckpointId
|
|
69
69
|
|
|
70
|
+
/** Session scope for the run. Required — every run is attributed to a Session. */
|
|
71
|
+
sessionId: SessionId
|
|
72
|
+
|
|
70
73
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
74
|
+
* Topic the Session lives under. Required in 0.3.0 — every run carries
|
|
75
|
+
* the full five-layer scope (Tenant → Project → Thread → Session →
|
|
76
|
+
* Run). Denormalized from `session.threadId`; callers build this
|
|
77
|
+
* alongside `sessionId` so the query pipeline never needs a second
|
|
78
|
+
* SessionStore round-trip to recover it.
|
|
73
79
|
*/
|
|
74
|
-
|
|
80
|
+
threadId: ThreadId
|
|
75
81
|
|
|
76
82
|
/** Long-lived goal scope for the run. Required. */
|
|
77
83
|
projectId: ProjectId
|
|
78
84
|
|
|
79
|
-
/** Isolation boundary. Required. */
|
|
85
|
+
/** Isolation boundary (Convention #17). Required. */
|
|
80
86
|
tenantId: TenantId
|
|
81
87
|
|
|
82
|
-
/**
|
|
83
|
-
* @deprecated Pass `projectId` instead. When both are present, `projectId`
|
|
84
|
-
* wins. During the 0.2.x migration window a caller supplying only
|
|
85
|
-
* `threadId` must also supply `projectId` — the kernel no longer infers
|
|
86
|
-
* `projectId` from a bare `threadId` on the QueryParams shape.
|
|
87
|
-
*/
|
|
88
|
-
threadId?: ThreadId
|
|
89
|
-
|
|
90
88
|
/**
|
|
91
89
|
* Optional path layout override. Defaults to a {@link DefaultPathBuilder}
|
|
92
|
-
* rooted at `{workingDirectory}/.namzu
|
|
93
|
-
*
|
|
90
|
+
* rooted at `{workingDirectory}/.namzu`. First-call filesystem migration
|
|
91
|
+
* runs on this same entry point.
|
|
94
92
|
*/
|
|
95
93
|
pathBuilder?: PathBuilder
|
|
96
94
|
|
|
@@ -158,6 +156,7 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Agen
|
|
|
158
156
|
messages: params.messages,
|
|
159
157
|
signal: params.signal,
|
|
160
158
|
sessionId: params.sessionId,
|
|
159
|
+
threadId: params.threadId,
|
|
161
160
|
projectId: params.projectId,
|
|
162
161
|
tenantId: params.tenantId,
|
|
163
162
|
pathBuilder: params.pathBuilder,
|
|
@@ -5,9 +5,9 @@ import type { WorkingStateManager } from '../../../compaction/manager.js'
|
|
|
5
5
|
import type { CompactionConfig } from '../../../config/runtime.js'
|
|
6
6
|
import type { PlanManager } from '../../../manager/plan/lifecycle.js'
|
|
7
7
|
import type { RunPersistence } from '../../../manager/run/persistence.js'
|
|
8
|
-
import { getTracer } from '../../../provider/telemetry/setup.js'
|
|
9
8
|
import type { ActivityStore } from '../../../store/activity/memory.js'
|
|
10
9
|
import { GENAI, NAMZU, agentIterationSpanName } from '../../../telemetry/attributes.js'
|
|
10
|
+
import { getTracer } from '../../../telemetry/runtime-accessors.js'
|
|
11
11
|
import type { ResumeHandler } from '../../../types/hitl/index.js'
|
|
12
12
|
import { createAssistantMessage, createUserMessage } from '../../../types/message/index.js'
|
|
13
13
|
import type { LLMProvider } from '../../../types/provider/index.js'
|
|
@@ -15,8 +15,10 @@
|
|
|
15
15
|
import { vi } from 'vitest'
|
|
16
16
|
import { EMPTY_TOKEN_USAGE } from '../../../constants/limits.js'
|
|
17
17
|
import { AgentManager } from '../../../manager/agent/lifecycle.js'
|
|
18
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
18
19
|
import { AgentRegistry } from '../../../registry/agent/definitions.js'
|
|
19
20
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
21
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
20
22
|
import type {
|
|
21
23
|
AgentCapabilities,
|
|
22
24
|
AgentInput,
|
|
@@ -28,7 +30,7 @@ import type { AgentDefinition } from '../../../types/agent/factory.js'
|
|
|
28
30
|
import type { AgentTaskContext, SendMessageOptions } from '../../../types/agent/task.js'
|
|
29
31
|
import type { AgentId, RunId, SessionId, TenantId, UserId } from '../../../types/ids/index.js'
|
|
30
32
|
import { createAssistantMessage } from '../../../types/message/index.js'
|
|
31
|
-
import type { ProjectId, SummaryId } from '../../../types/session/ids.js'
|
|
33
|
+
import type { ProjectId, SummaryId, ThreadId } from '../../../types/session/ids.js'
|
|
32
34
|
import { ZERO_COST } from '../../../utils/cost.js'
|
|
33
35
|
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
34
36
|
import type { ActorRef } from '../../hierarchy/actor.js'
|
|
@@ -149,6 +151,8 @@ export function buildDefinition(agent: Agent<BaseAgentConfig, BaseAgentResult>):
|
|
|
149
151
|
|
|
150
152
|
export interface IntegrationHarness {
|
|
151
153
|
readonly store: InMemorySessionStore
|
|
154
|
+
readonly threadStore: InMemoryThreadStore
|
|
155
|
+
readonly threadManager: ThreadManager
|
|
152
156
|
readonly registry: AgentRegistry
|
|
153
157
|
readonly manager: AgentManager
|
|
154
158
|
readonly materializer: SessionSummaryMaterializer
|
|
@@ -178,6 +182,7 @@ export interface IntegrationHarnessOptions {
|
|
|
178
182
|
export function buildHarness(options: IntegrationHarnessOptions = {}): IntegrationHarness {
|
|
179
183
|
const tenantId = options.tenantId ?? DEFAULT_TENANT
|
|
180
184
|
const store = new InMemorySessionStore()
|
|
185
|
+
const threadStore = new InMemoryThreadStore()
|
|
181
186
|
|
|
182
187
|
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
183
188
|
if (options.withWorktreeDriver !== false) {
|
|
@@ -200,25 +205,42 @@ export function buildHarness(options: IntegrationHarnessOptions = {}): Integrati
|
|
|
200
205
|
})
|
|
201
206
|
|
|
202
207
|
const capacity = new DefaultCapacityValidator(store)
|
|
208
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
203
209
|
const registry = new AgentRegistry()
|
|
204
210
|
const manager = new AgentManager(registry, undefined, {
|
|
205
211
|
sessionStore: store,
|
|
206
212
|
summaryMaterializer: materializer,
|
|
207
213
|
workspaceRegistry,
|
|
208
214
|
capacity,
|
|
215
|
+
threadManager,
|
|
209
216
|
})
|
|
210
217
|
|
|
211
|
-
return {
|
|
218
|
+
return {
|
|
219
|
+
store,
|
|
220
|
+
threadStore,
|
|
221
|
+
threadManager,
|
|
222
|
+
registry,
|
|
223
|
+
manager,
|
|
224
|
+
materializer,
|
|
225
|
+
workspaceRegistry,
|
|
226
|
+
capacity,
|
|
227
|
+
tenantId,
|
|
228
|
+
}
|
|
212
229
|
}
|
|
213
230
|
|
|
214
231
|
/**
|
|
215
|
-
* Seeds a Tenant → Project → Session
|
|
216
|
-
* `active` so it is a legal spawn parent. Returns the project
|
|
217
|
-
* session for the caller to drive spawns against.
|
|
232
|
+
* Seeds a Tenant → Project → Thread → Session quadruple and flips the session
|
|
233
|
+
* into `active` so it is a legal spawn parent. Returns the project, thread,
|
|
234
|
+
* and active session for the caller to drive spawns against.
|
|
218
235
|
*/
|
|
219
236
|
export async function seedActiveParent(
|
|
220
237
|
harness: IntegrationHarness,
|
|
221
|
-
options?: {
|
|
238
|
+
options?: {
|
|
239
|
+
actor?: ActorRef
|
|
240
|
+
projectName?: string
|
|
241
|
+
tenantId?: TenantId
|
|
242
|
+
threadTitle?: string
|
|
243
|
+
},
|
|
222
244
|
) {
|
|
223
245
|
const tenantId = options?.tenantId ?? harness.tenantId
|
|
224
246
|
const actor: ActorRef = options?.actor ?? userActor('usr_root', tenantId)
|
|
@@ -226,12 +248,16 @@ export async function seedActiveParent(
|
|
|
226
248
|
{ tenantId, name: options?.projectName ?? 'integration-project' },
|
|
227
249
|
tenantId,
|
|
228
250
|
)
|
|
251
|
+
const thread = await harness.threadStore.createThread(
|
|
252
|
+
{ projectId: project.id, title: options?.threadTitle ?? 'default' },
|
|
253
|
+
tenantId,
|
|
254
|
+
)
|
|
229
255
|
const session = await harness.store.createSession(
|
|
230
|
-
{ projectId: project.id, currentActor: actor },
|
|
256
|
+
{ threadId: thread.id, projectId: project.id, currentActor: actor },
|
|
231
257
|
tenantId,
|
|
232
258
|
)
|
|
233
259
|
await harness.store.updateSession({ ...session, status: 'active' as Session['status'] }, tenantId)
|
|
234
|
-
return { project, session, actor }
|
|
260
|
+
return { project, thread, session, actor }
|
|
235
261
|
}
|
|
236
262
|
|
|
237
263
|
/**
|
|
@@ -242,6 +268,7 @@ export async function seedActiveParent(
|
|
|
242
268
|
export function buildTaskContext(params: {
|
|
243
269
|
sessionId: SessionId
|
|
244
270
|
projectId: ProjectId
|
|
271
|
+
threadId: ThreadId
|
|
245
272
|
tenantId: TenantId
|
|
246
273
|
parentActor: ActorRef
|
|
247
274
|
depth?: number
|
|
@@ -258,6 +285,7 @@ export function buildTaskContext(params: {
|
|
|
258
285
|
remaining: params.budget ?? 100_000,
|
|
259
286
|
},
|
|
260
287
|
tenantId: params.tenantId,
|
|
288
|
+
threadId: params.threadId,
|
|
261
289
|
sessionId: params.sessionId,
|
|
262
290
|
projectId: params.projectId,
|
|
263
291
|
parentActor: params.parentActor,
|