@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,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration — AgentManager.provisionSpawn compensating rollback.
|
|
3
|
+
*
|
|
4
|
+
* Covers Codex SPAWN-ROLLBACK critique (ses_001-hierarchy-redesign Phase 2
|
|
5
|
+
* adversarial review, 2026-04-18). Without the try/catch wrapper around the
|
|
6
|
+
* createSession → updateSession → createSubSession → workspace.create
|
|
7
|
+
* mutation block, a failure after createSession leaves an `active` child
|
|
8
|
+
* session with no subsession edge — invisible to the parent, but counted
|
|
9
|
+
* against `maxDelegationWidth` and visible to SessionStore.listSessions
|
|
10
|
+
* consumers (archive/delete flows in ThreadManager).
|
|
11
|
+
*
|
|
12
|
+
* Failure modes exercised:
|
|
13
|
+
* A. Workspace driver throws on create. Subsession exists; must flip to
|
|
14
|
+
* 'failed' for audit. Child session must be hard-deleted.
|
|
15
|
+
* B. Subsession insert fails (store injection). No subsession recorded.
|
|
16
|
+
* Child session must be hard-deleted.
|
|
17
|
+
*
|
|
18
|
+
* Assertions in both cases:
|
|
19
|
+
* - sendMessage rejects with the underlying error.
|
|
20
|
+
* - SessionStore.listSessions(threadId) returns no row with the child id.
|
|
21
|
+
* - Parent session remains untouched (status, currentActor).
|
|
22
|
+
* - Fan-out cap reclaims the slot (next spawn succeeds up to the same
|
|
23
|
+
* width).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { describe, expect, it } from 'vitest'
|
|
27
|
+
import { EMPTY_TOKEN_USAGE } from '../../../constants/limits.js'
|
|
28
|
+
import { AgentManager } from '../../../manager/agent/lifecycle.js'
|
|
29
|
+
import { ThreadManager } from '../../../manager/thread/lifecycle.js'
|
|
30
|
+
import { AgentRegistry } from '../../../registry/agent/definitions.js'
|
|
31
|
+
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
32
|
+
import { InMemoryThreadStore } from '../../../store/thread/memory.js'
|
|
33
|
+
import type {
|
|
34
|
+
AgentCapabilities,
|
|
35
|
+
AgentInput,
|
|
36
|
+
BaseAgentConfig,
|
|
37
|
+
BaseAgentResult,
|
|
38
|
+
} from '../../../types/agent/base.js'
|
|
39
|
+
import type { Agent } from '../../../types/agent/core.js'
|
|
40
|
+
import type { AgentDefinition } from '../../../types/agent/factory.js'
|
|
41
|
+
import type { AgentTaskContext, SendMessageOptions } from '../../../types/agent/task.js'
|
|
42
|
+
import type { RunId, TenantId, UserId } from '../../../types/ids/index.js'
|
|
43
|
+
import { createAssistantMessage } from '../../../types/message/index.js'
|
|
44
|
+
import type { SummaryId } from '../../../types/session/ids.js'
|
|
45
|
+
import { ZERO_COST } from '../../../utils/cost.js'
|
|
46
|
+
import { DefaultCapacityValidator } from '../../handoff/capacity.js'
|
|
47
|
+
import type { ActorRef } from '../../hierarchy/actor.js'
|
|
48
|
+
import { SessionSummaryMaterializer } from '../../summary/materialize.js'
|
|
49
|
+
import type {
|
|
50
|
+
BranchWorkspaceParams,
|
|
51
|
+
CreateWorkspaceParams,
|
|
52
|
+
WorkspaceBackendDriver,
|
|
53
|
+
WorkspaceInspection,
|
|
54
|
+
} from '../../workspace/driver.js'
|
|
55
|
+
import type { WorkspaceRef } from '../../workspace/ref.js'
|
|
56
|
+
import { WorkspaceBackendRegistry } from '../../workspace/registry.js'
|
|
57
|
+
|
|
58
|
+
const tenant = 'tnt_alpha' as TenantId
|
|
59
|
+
|
|
60
|
+
const capabilities: AgentCapabilities = {
|
|
61
|
+
supportsTools: false,
|
|
62
|
+
supportsStreaming: false,
|
|
63
|
+
supportsConcurrency: false,
|
|
64
|
+
supportsSubAgents: false,
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildAgent(id: string): Agent<BaseAgentConfig, BaseAgentResult> {
|
|
68
|
+
return {
|
|
69
|
+
type: 'reactive',
|
|
70
|
+
metadata: {
|
|
71
|
+
type: 'reactive',
|
|
72
|
+
id,
|
|
73
|
+
name: id,
|
|
74
|
+
version: '1.0.0',
|
|
75
|
+
category: 'test',
|
|
76
|
+
description: id,
|
|
77
|
+
capabilities,
|
|
78
|
+
},
|
|
79
|
+
run: async (_input: AgentInput, _config: BaseAgentConfig): Promise<BaseAgentResult> => ({
|
|
80
|
+
runId: 'run_child' as RunId,
|
|
81
|
+
status: 'completed',
|
|
82
|
+
usage: { ...EMPTY_TOKEN_USAGE },
|
|
83
|
+
cost: { ...ZERO_COST },
|
|
84
|
+
iterations: 1,
|
|
85
|
+
durationMs: 1,
|
|
86
|
+
messages: [createAssistantMessage('child did the work')],
|
|
87
|
+
result: 'child did the work',
|
|
88
|
+
}),
|
|
89
|
+
cancel: async () => undefined,
|
|
90
|
+
getCapabilities: () => capabilities,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function buildDefinition(agent: Agent<BaseAgentConfig, BaseAgentResult>): AgentDefinition {
|
|
95
|
+
return {
|
|
96
|
+
info: {
|
|
97
|
+
id: agent.metadata.id,
|
|
98
|
+
name: agent.metadata.name,
|
|
99
|
+
version: agent.metadata.version,
|
|
100
|
+
category: agent.metadata.category,
|
|
101
|
+
description: agent.metadata.description,
|
|
102
|
+
tools: [],
|
|
103
|
+
defaults: { model: 'test', tokenBudget: 1_000 },
|
|
104
|
+
},
|
|
105
|
+
typedAgent: agent,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class FailingWorkspaceDriver implements WorkspaceBackendDriver {
|
|
110
|
+
readonly kind = 'git-worktree' as const
|
|
111
|
+
createCalls = 0
|
|
112
|
+
|
|
113
|
+
async create(_params: CreateWorkspaceParams): Promise<WorkspaceRef> {
|
|
114
|
+
this.createCalls += 1
|
|
115
|
+
throw new Error('synthetic workspace backend failure')
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async branch(_source: WorkspaceRef, _params: BranchWorkspaceParams): Promise<WorkspaceRef> {
|
|
119
|
+
throw new Error('unused in this test')
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async dispose(_ref: WorkspaceRef): Promise<void> {
|
|
123
|
+
/* no-op */
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async inspect(_ref: WorkspaceRef): Promise<WorkspaceInspection> {
|
|
127
|
+
throw new Error('unused in this test')
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
describe('provisionSpawn compensating rollback', () => {
|
|
132
|
+
it('workspace driver failure — deletes child session, marks subsession failed, leaves no orphan', async () => {
|
|
133
|
+
const store = new InMemorySessionStore()
|
|
134
|
+
const threadStore = new InMemoryThreadStore()
|
|
135
|
+
const project = await store.createProject(
|
|
136
|
+
{ tenantId: tenant, name: 'rollback-project' },
|
|
137
|
+
tenant,
|
|
138
|
+
)
|
|
139
|
+
const thread = await threadStore.createThread(
|
|
140
|
+
{ projectId: project.id, title: 'rollback-topic' },
|
|
141
|
+
tenant,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
const userActor: ActorRef = {
|
|
145
|
+
kind: 'user',
|
|
146
|
+
userId: 'usr_root' as UserId,
|
|
147
|
+
tenantId: tenant,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const parentSession = await store.createSession(
|
|
151
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor },
|
|
152
|
+
tenant,
|
|
153
|
+
)
|
|
154
|
+
await store.updateSession({ ...parentSession, status: 'active' }, tenant)
|
|
155
|
+
|
|
156
|
+
let summaryCounter = 0
|
|
157
|
+
const materializer = new SessionSummaryMaterializer({
|
|
158
|
+
store,
|
|
159
|
+
generateSummaryId: () => `sum_test_${++summaryCounter}` as SummaryId,
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const registry = new AgentRegistry()
|
|
163
|
+
registry.register(buildDefinition(buildAgent('worker')))
|
|
164
|
+
|
|
165
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
166
|
+
const failingDriver = new FailingWorkspaceDriver()
|
|
167
|
+
workspaceRegistry.register(failingDriver)
|
|
168
|
+
|
|
169
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
170
|
+
const manager = new AgentManager(registry, undefined, {
|
|
171
|
+
sessionStore: store,
|
|
172
|
+
summaryMaterializer: materializer,
|
|
173
|
+
workspaceRegistry,
|
|
174
|
+
capacity: new DefaultCapacityValidator(store),
|
|
175
|
+
threadManager,
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
const taskContext: AgentTaskContext = {
|
|
179
|
+
parentRunId: 'run_parent' as RunId,
|
|
180
|
+
parentAgentId: 'supervisor',
|
|
181
|
+
parentAbortController: new AbortController(),
|
|
182
|
+
depth: 0,
|
|
183
|
+
budgetTracker: { total: 100_000, remaining: 100_000 },
|
|
184
|
+
tenantId: tenant,
|
|
185
|
+
threadId: thread.id,
|
|
186
|
+
sessionId: parentSession.id,
|
|
187
|
+
projectId: project.id,
|
|
188
|
+
parentActor: userActor,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const options: SendMessageOptions = {
|
|
192
|
+
agentId: 'worker',
|
|
193
|
+
input: { messages: [], workingDirectory: '/tmp' },
|
|
194
|
+
parentSessionId: parentSession.id,
|
|
195
|
+
tenantId: tenant,
|
|
196
|
+
projectId: project.id,
|
|
197
|
+
parentActor: userActor,
|
|
198
|
+
workspaceBackend: 'git-worktree',
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
await expect(manager.sendMessage(options, taskContext)).rejects.toThrow(
|
|
202
|
+
'synthetic workspace backend failure',
|
|
203
|
+
)
|
|
204
|
+
expect(failingDriver.createCalls).toBe(1)
|
|
205
|
+
|
|
206
|
+
// Child session is gone — archive/delete flows and fan-out caps see
|
|
207
|
+
// zero child attached to the thread beyond the parent.
|
|
208
|
+
const sessionsOnThread = await store.listSessions(thread.id, tenant)
|
|
209
|
+
expect(sessionsOnThread.map((s) => s.id)).toEqual([parentSession.id])
|
|
210
|
+
|
|
211
|
+
// Parent session is untouched.
|
|
212
|
+
const refetchedParent = await store.getSession(parentSession.id, tenant)
|
|
213
|
+
expect(refetchedParent?.status).toBe('active')
|
|
214
|
+
expect(refetchedParent?.currentActor).toEqual(userActor)
|
|
215
|
+
|
|
216
|
+
// No subsession breadcrumb — `subsession_spawned` never fired
|
|
217
|
+
// (provisionSpawn aborted before buildSpawnRecord), so nothing is
|
|
218
|
+
// expecting an audit row. Leaving a `status: 'failed'` record would
|
|
219
|
+
// dangle with no corresponding emission.
|
|
220
|
+
const subsessions = await store.getChildren(parentSession.id, tenant)
|
|
221
|
+
expect(subsessions).toHaveLength(0)
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
it('repeated rollback does not accumulate orphan sessions or subsessions', async () => {
|
|
225
|
+
const store = new InMemorySessionStore()
|
|
226
|
+
const threadStore = new InMemoryThreadStore()
|
|
227
|
+
const project = await store.createProject(
|
|
228
|
+
{
|
|
229
|
+
tenantId: tenant,
|
|
230
|
+
name: 'rollback-repeat-project',
|
|
231
|
+
},
|
|
232
|
+
tenant,
|
|
233
|
+
)
|
|
234
|
+
const thread = await threadStore.createThread(
|
|
235
|
+
{ projectId: project.id, title: 'rollback-width-topic' },
|
|
236
|
+
tenant,
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
const userActor: ActorRef = {
|
|
240
|
+
kind: 'user',
|
|
241
|
+
userId: 'usr_root' as UserId,
|
|
242
|
+
tenantId: tenant,
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const parentSession = await store.createSession(
|
|
246
|
+
{ threadId: thread.id, projectId: project.id, currentActor: userActor },
|
|
247
|
+
tenant,
|
|
248
|
+
)
|
|
249
|
+
await store.updateSession({ ...parentSession, status: 'active' }, tenant)
|
|
250
|
+
|
|
251
|
+
let summaryCounter = 0
|
|
252
|
+
const materializer = new SessionSummaryMaterializer({
|
|
253
|
+
store,
|
|
254
|
+
generateSummaryId: () => `sum_test_${++summaryCounter}` as SummaryId,
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
const registry = new AgentRegistry()
|
|
258
|
+
registry.register(buildDefinition(buildAgent('worker')))
|
|
259
|
+
|
|
260
|
+
const workspaceRegistry = new WorkspaceBackendRegistry()
|
|
261
|
+
workspaceRegistry.register(new FailingWorkspaceDriver())
|
|
262
|
+
|
|
263
|
+
const threadManager = new ThreadManager({ threadStore, sessionStore: store })
|
|
264
|
+
const manager = new AgentManager(registry, undefined, {
|
|
265
|
+
sessionStore: store,
|
|
266
|
+
summaryMaterializer: materializer,
|
|
267
|
+
workspaceRegistry,
|
|
268
|
+
capacity: new DefaultCapacityValidator(store),
|
|
269
|
+
threadManager,
|
|
270
|
+
})
|
|
271
|
+
|
|
272
|
+
const taskContext: AgentTaskContext = {
|
|
273
|
+
parentRunId: 'run_parent' as RunId,
|
|
274
|
+
parentAgentId: 'supervisor',
|
|
275
|
+
parentAbortController: new AbortController(),
|
|
276
|
+
depth: 0,
|
|
277
|
+
budgetTracker: { total: 100_000, remaining: 100_000 },
|
|
278
|
+
tenantId: tenant,
|
|
279
|
+
threadId: thread.id,
|
|
280
|
+
sessionId: parentSession.id,
|
|
281
|
+
projectId: project.id,
|
|
282
|
+
parentActor: userActor,
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
const options: SendMessageOptions = {
|
|
286
|
+
agentId: 'worker',
|
|
287
|
+
input: { messages: [], workingDirectory: '/tmp' },
|
|
288
|
+
parentSessionId: parentSession.id,
|
|
289
|
+
tenantId: tenant,
|
|
290
|
+
projectId: project.id,
|
|
291
|
+
parentActor: userActor,
|
|
292
|
+
workspaceBackend: 'git-worktree',
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Two consecutive failing spawns. Without rollback, two orphan
|
|
296
|
+
// `active` child sessions would accumulate; with rollback, each
|
|
297
|
+
// attempt cleans up after itself and the store stays at { parent }.
|
|
298
|
+
await expect(manager.sendMessage(options, taskContext)).rejects.toThrow(
|
|
299
|
+
'synthetic workspace backend failure',
|
|
300
|
+
)
|
|
301
|
+
await expect(manager.sendMessage(options, taskContext)).rejects.toThrow(
|
|
302
|
+
'synthetic workspace backend failure',
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
// Still no child session under the thread.
|
|
306
|
+
const sessionsOnThread = await store.listSessions(thread.id, tenant)
|
|
307
|
+
expect(sessionsOnThread.map((s) => s.id)).toEqual([parentSession.id])
|
|
308
|
+
|
|
309
|
+
// No lingering subsession rows — both attempts rolled back cleanly.
|
|
310
|
+
const subsessions = await store.getChildren(parentSession.id, tenant)
|
|
311
|
+
expect(subsessions).toHaveLength(0)
|
|
312
|
+
})
|
|
313
|
+
})
|
|
@@ -18,18 +18,20 @@
|
|
|
18
18
|
import { describe, expect, it } from 'vitest'
|
|
19
19
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
20
20
|
import type { SessionId } from '../../../types/ids/index.js'
|
|
21
|
-
import type { SummaryId } from '../../../types/session/ids.js'
|
|
21
|
+
import type { SummaryId, ThreadId } from '../../../types/session/ids.js'
|
|
22
22
|
import { SessionSummaryMaterializer } from '../../summary/materialize.js'
|
|
23
23
|
import { SessionAlreadySummarizedError } from '../../summary/ref.js'
|
|
24
24
|
import { DEFAULT_TENANT, agentActor } from './_fixtures.js'
|
|
25
25
|
|
|
26
|
+
const TEST_THREAD_ID = 'thd_test' as ThreadId
|
|
27
|
+
|
|
26
28
|
async function seedActive(store: InMemorySessionStore) {
|
|
27
29
|
const project = await store.createProject(
|
|
28
30
|
{ tenantId: DEFAULT_TENANT, name: 'summary' },
|
|
29
31
|
DEFAULT_TENANT,
|
|
30
32
|
)
|
|
31
33
|
const session = await store.createSession(
|
|
32
|
-
{ projectId: project.id, currentActor: agentActor('agt_worker') },
|
|
34
|
+
{ threadId: TEST_THREAD_ID, projectId: project.id, currentActor: agentActor('agt_worker') },
|
|
33
35
|
DEFAULT_TENANT,
|
|
34
36
|
)
|
|
35
37
|
await store.updateSession({ ...session, status: 'active' }, DEFAULT_TENANT)
|
|
@@ -14,10 +14,12 @@
|
|
|
14
14
|
import { describe, expect, it } from 'vitest'
|
|
15
15
|
import { InMemorySessionStore } from '../../../store/session/memory.js'
|
|
16
16
|
import { createUserMessage } from '../../../types/message/index.js'
|
|
17
|
-
import type { ProjectId, SubSessionId, SummaryId } from '../../../types/session/ids.js'
|
|
17
|
+
import type { ProjectId, SubSessionId, SummaryId, ThreadId } from '../../../types/session/ids.js'
|
|
18
18
|
import { TenantIsolationError } from '../../errors.js'
|
|
19
19
|
import { DEFAULT_TENANT, OTHER_TENANT, agentActor, userActor } from './_fixtures.js'
|
|
20
20
|
|
|
21
|
+
const TEST_THREAD_ID = 'thd_test' as ThreadId
|
|
22
|
+
|
|
21
23
|
async function seedTenantAResources() {
|
|
22
24
|
const store = new InMemorySessionStore()
|
|
23
25
|
const project = await store.createProject(
|
|
@@ -25,11 +27,11 @@ async function seedTenantAResources() {
|
|
|
25
27
|
DEFAULT_TENANT,
|
|
26
28
|
)
|
|
27
29
|
const parent = await store.createSession(
|
|
28
|
-
{ projectId: project.id, currentActor: userActor('usr_a') },
|
|
30
|
+
{ threadId: TEST_THREAD_ID, projectId: project.id, currentActor: userActor('usr_a') },
|
|
29
31
|
DEFAULT_TENANT,
|
|
30
32
|
)
|
|
31
33
|
const child = await store.createSession(
|
|
32
|
-
{ projectId: project.id, currentActor: agentActor('agt_a') },
|
|
34
|
+
{ threadId: TEST_THREAD_ID, projectId: project.id, currentActor: agentActor('agt_a') },
|
|
33
35
|
DEFAULT_TENANT,
|
|
34
36
|
)
|
|
35
37
|
const sub = await store.createSubSession(
|
|
@@ -200,7 +202,11 @@ describe('Integration — tenant isolation', () => {
|
|
|
200
202
|
const { store, project } = await seedTenantAResources()
|
|
201
203
|
await expect(
|
|
202
204
|
store.createSession(
|
|
203
|
-
{
|
|
205
|
+
{
|
|
206
|
+
threadId: TEST_THREAD_ID,
|
|
207
|
+
projectId: project.id,
|
|
208
|
+
currentActor: userActor('usr_intruder', OTHER_TENANT),
|
|
209
|
+
},
|
|
204
210
|
OTHER_TENANT,
|
|
205
211
|
),
|
|
206
212
|
).rejects.toBeInstanceOf(TenantIsolationError)
|
|
@@ -232,7 +238,11 @@ describe('Integration — tenant isolation', () => {
|
|
|
232
238
|
OTHER_TENANT,
|
|
233
239
|
)
|
|
234
240
|
const otherChild = await store.createSession(
|
|
235
|
-
{
|
|
241
|
+
{
|
|
242
|
+
threadId: TEST_THREAD_ID,
|
|
243
|
+
projectId: otherProject.id,
|
|
244
|
+
currentActor: userActor('usr_other', OTHER_TENANT),
|
|
245
|
+
},
|
|
236
246
|
OTHER_TENANT,
|
|
237
247
|
)
|
|
238
248
|
|
|
@@ -264,7 +274,7 @@ describe('Integration — tenant isolation', () => {
|
|
|
264
274
|
await store.createProject({ tenantId: DEFAULT_TENANT, name: 'del' }, DEFAULT_TENANT)
|
|
265
275
|
).id
|
|
266
276
|
const lonely = await store.createSession(
|
|
267
|
-
{ projectId: project, currentActor: userActor('usr_lonely') },
|
|
277
|
+
{ threadId: TEST_THREAD_ID, projectId: project, currentActor: userActor('usr_lonely') },
|
|
268
278
|
DEFAULT_TENANT,
|
|
269
279
|
)
|
|
270
280
|
await expect(store.deleteSession(lonely.id, OTHER_TENANT)).rejects.toBeInstanceOf(
|
package/src/session/errors.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { SessionId, TenantId } from '../types/ids/index.js'
|
|
11
|
+
import type { ThreadId } from '../types/session/ids.js'
|
|
12
|
+
import type { SessionStatus } from './hierarchy/session.js'
|
|
11
13
|
import type { WorkspaceBackendKind } from './workspace/driver.js'
|
|
12
14
|
|
|
13
15
|
/**
|
|
@@ -68,3 +70,90 @@ export class WorkspaceBackendError extends Error {
|
|
|
68
70
|
this.details = details
|
|
69
71
|
}
|
|
70
72
|
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Raised by {@link import('../types/thread/store.js').ThreadStore.updateThread}
|
|
76
|
+
* when the supplied {@link Thread.ownerVersion} does not match the persisted
|
|
77
|
+
* record. The caller must re-read via `getThread`, re-apply its intended
|
|
78
|
+
* mutation on top of the fresh record, and retry. Mirrors the Session
|
|
79
|
+
* handoff CAS pattern (§6.1).
|
|
80
|
+
*/
|
|
81
|
+
export class StaleThreadError extends Error {
|
|
82
|
+
readonly details: {
|
|
83
|
+
threadId: ThreadId
|
|
84
|
+
expectedVersion: number
|
|
85
|
+
actualVersion: number
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
constructor(details: { threadId: ThreadId; expectedVersion: number; actualVersion: number }) {
|
|
89
|
+
super(
|
|
90
|
+
`Stale Thread ${details.threadId}: expected ownerVersion=${details.expectedVersion}, actual=${details.actualVersion}`,
|
|
91
|
+
)
|
|
92
|
+
this.name = 'StaleThreadError'
|
|
93
|
+
this.details = details
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Raised by the spawn path (and any caller that enforces the open-thread
|
|
99
|
+
* precondition) when a Thread is in `'archived'` state and would-be mutations
|
|
100
|
+
* require it to be `'open'`. Convention #5: deny-by-default — archival is a
|
|
101
|
+
* hard read-only boundary.
|
|
102
|
+
*/
|
|
103
|
+
export class ThreadClosedError extends Error {
|
|
104
|
+
readonly details: {
|
|
105
|
+
threadId: ThreadId
|
|
106
|
+
op: string
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
constructor(details: { threadId: ThreadId; op: string }) {
|
|
110
|
+
super(`Thread ${details.threadId} is archived; operation '${details.op}' rejected`)
|
|
111
|
+
this.name = 'ThreadClosedError'
|
|
112
|
+
this.details = details
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Raised by {@link import('../manager/thread/lifecycle.js').ThreadManager.archive}
|
|
118
|
+
* and `.delete` when the Thread's session-presence precondition is violated:
|
|
119
|
+
*
|
|
120
|
+
* - `op: 'archive'` — at least one Session under the Thread is in a
|
|
121
|
+
* non-terminal state (`active | locked | awaiting_hitl | awaiting_merge`).
|
|
122
|
+
* The caller must first quiesce those sessions (let them reach `idle`,
|
|
123
|
+
* `failed`, or `archived`) before flipping the Thread to archived.
|
|
124
|
+
* - `op: 'delete'` — the Thread still has at least one attached Session.
|
|
125
|
+
* Callers must either archive + tombstone those sessions (`deleteSession`)
|
|
126
|
+
* before calling `deleteThread`, or accept that deletion is not yet safe.
|
|
127
|
+
*
|
|
128
|
+
* `blockingSessions` carries the first {@link THREAD_NOT_EMPTY_SAMPLE_LIMIT}
|
|
129
|
+
* offenders with their current status so operator tooling can surface an
|
|
130
|
+
* actionable list without unbounded error payloads on large threads.
|
|
131
|
+
* `totalBlockingSessions` holds the full count even when the sample is
|
|
132
|
+
* truncated. Convention #5: deny-by-default — no implicit cascade, no silent
|
|
133
|
+
* no-op.
|
|
134
|
+
*/
|
|
135
|
+
export const THREAD_NOT_EMPTY_SAMPLE_LIMIT = 50
|
|
136
|
+
|
|
137
|
+
export class ThreadNotEmptyError extends Error {
|
|
138
|
+
readonly details: {
|
|
139
|
+
threadId: ThreadId
|
|
140
|
+
tenantId: TenantId
|
|
141
|
+
op: 'archive' | 'delete'
|
|
142
|
+
blockingSessions: ReadonlyArray<{ sessionId: SessionId; status: SessionStatus }>
|
|
143
|
+
totalBlockingSessions: number
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
constructor(details: {
|
|
147
|
+
threadId: ThreadId
|
|
148
|
+
tenantId: TenantId
|
|
149
|
+
op: 'archive' | 'delete'
|
|
150
|
+
blockingSessions: ReadonlyArray<{ sessionId: SessionId; status: SessionStatus }>
|
|
151
|
+
totalBlockingSessions: number
|
|
152
|
+
}) {
|
|
153
|
+
super(
|
|
154
|
+
`Thread ${details.threadId} ${details.op} blocked: ${details.totalBlockingSessions} session(s) still attached`,
|
|
155
|
+
)
|
|
156
|
+
this.name = 'ThreadNotEmptyError'
|
|
157
|
+
this.details = details
|
|
158
|
+
}
|
|
159
|
+
}
|