@namzu/sdk 36.0.0 → 38.0.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 +244 -0
- package/README.md +68 -41
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +4 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +16 -1
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/compaction/extractor.d.ts +1 -1
- package/dist/compaction/extractor.d.ts.map +1 -1
- package/dist/compaction/extractor.js +15 -7
- package/dist/compaction/extractor.js.map +1 -1
- package/dist/compaction/manager.d.ts +2 -0
- package/dist/compaction/manager.d.ts.map +1 -1
- package/dist/compaction/manager.js +12 -1
- package/dist/compaction/manager.js.map +1 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +82 -15
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +16 -6
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +2 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/contracts/api.d.ts +1 -1
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/schemas.d.ts +10 -10
- package/dist/eval/harness-verification.d.ts +63 -0
- package/dist/eval/harness-verification.d.ts.map +1 -0
- package/dist/eval/harness-verification.js +165 -0
- package/dist/eval/harness-verification.js.map +1 -0
- package/dist/eval/index.d.ts +2 -0
- package/dist/eval/index.d.ts.map +1 -1
- package/dist/eval/index.js +1 -0
- package/dist/eval/index.js.map +1 -1
- package/dist/execution/code-runtime/json-value.d.ts +10 -0
- package/dist/execution/code-runtime/json-value.d.ts.map +1 -0
- package/dist/execution/code-runtime/json-value.js +89 -0
- package/dist/execution/code-runtime/json-value.js.map +1 -0
- package/dist/execution/code-runtime/worker-program.d.ts +8 -0
- package/dist/execution/code-runtime/worker-program.d.ts.map +1 -0
- package/dist/execution/code-runtime/worker-program.js +240 -0
- package/dist/execution/code-runtime/worker-program.js.map +1 -0
- package/dist/execution/code-runtime/worker.d.ts +22 -0
- package/dist/execution/code-runtime/worker.d.ts.map +1 -1
- package/dist/execution/code-runtime/worker.js +148 -111
- package/dist/execution/code-runtime/worker.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +12 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +316 -46
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +2 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +6 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +2 -2
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +11 -5
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/provider/capabilities.d.ts +5 -1
- package/dist/provider/capabilities.d.ts.map +1 -1
- package/dist/provider/capabilities.js +24 -0
- package/dist/provider/capabilities.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +6 -2
- package/dist/provider/fallback.js.map +1 -1
- package/dist/public-runtime.d.ts +4 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +3 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +2 -1
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +2 -1
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/tool/execute.d.ts +3 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +11 -80
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/run/command-gate.d.ts.map +1 -1
- package/dist/run/command-gate.js +65 -12
- package/dist/run/command-gate.js.map +1 -1
- package/dist/run/index.d.ts +2 -0
- package/dist/run/index.d.ts.map +1 -1
- package/dist/run/index.js +1 -0
- package/dist/run/index.js.map +1 -1
- package/dist/run/memory-promoter.d.ts +3 -35
- package/dist/run/memory-promoter.d.ts.map +1 -1
- package/dist/run/memory-promoter.js +32 -40
- package/dist/run/memory-promoter.js.map +1 -1
- package/dist/run/memory-recall.d.ts +25 -0
- package/dist/run/memory-recall.d.ts.map +1 -0
- package/dist/run/memory-recall.js +228 -0
- package/dist/run/memory-recall.js.map +1 -0
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +2 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/run/token-budget.d.ts +12 -4
- package/dist/run/token-budget.d.ts.map +1 -1
- package/dist/run/token-budget.js +49 -14
- package/dist/run/token-budget.js.map +1 -1
- package/dist/run/workspace-fingerprint.d.ts +10 -3
- package/dist/run/workspace-fingerprint.d.ts.map +1 -1
- package/dist/run/workspace-fingerprint.js +17 -8
- package/dist/run/workspace-fingerprint.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +14 -0
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +82 -1
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +8 -3
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +94 -30
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +7 -8
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +55 -7
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +12 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +348 -38
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/native-output.d.ts +10 -0
- package/dist/runtime/query/iteration/native-output.d.ts.map +1 -0
- package/dist/runtime/query/iteration/native-output.js +32 -0
- package/dist/runtime/query/iteration/native-output.js.map +1 -0
- package/dist/runtime/query/iteration/phases/advisory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.js +18 -13
- package/dist/runtime/query/iteration/phases/advisory.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +32 -11
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +9 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory.js +23 -6
- package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +12 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/observation-context.d.ts +10 -0
- package/dist/runtime/query/observation-context.d.ts.map +1 -0
- package/dist/runtime/query/observation-context.js +84 -0
- package/dist/runtime/query/observation-context.js.map +1 -0
- package/dist/runtime/query/request-context.d.ts +30 -0
- package/dist/runtime/query/request-context.d.ts.map +1 -0
- package/dist/runtime/query/request-context.js +76 -0
- package/dist/runtime/query/request-context.js.map +1 -0
- package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +6 -3
- package/dist/runtime/query/request-rich-content.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +3 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +4 -1
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/steering.d.ts +6 -2
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js +17 -2
- package/dist/runtime/query/steering.js.map +1 -1
- package/dist/runtime/query/tool-call-budget.d.ts +19 -0
- package/dist/runtime/query/tool-call-budget.d.ts.map +1 -0
- package/dist/runtime/query/tool-call-budget.js +110 -0
- package/dist/runtime/query/tool-call-budget.js.map +1 -0
- package/dist/runtime/query/tooling.d.ts +3 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +5 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/file-walk-program.d.ts.map +1 -1
- package/dist/sandbox/file-walk-program.js +8 -0
- package/dist/sandbox/file-walk-program.js.map +1 -1
- package/dist/sandbox/file-walk.d.ts.map +1 -1
- package/dist/sandbox/file-walk.js +9 -0
- package/dist/sandbox/file-walk.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +3 -0
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +1 -0
- package/dist/scheduler/local.js.map +1 -1
- package/dist/session/handoff/capacity.d.ts +2 -2
- package/dist/session/handoff/capacity.d.ts.map +1 -1
- package/dist/session/handoff/capacity.js +3 -2
- package/dist/session/handoff/capacity.js.map +1 -1
- package/dist/store/memory/disk.d.ts +6 -2
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +42 -25
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/memory/index.d.ts +4 -0
- package/dist/store/memory/index.d.ts.map +1 -1
- package/dist/store/memory/index.js +55 -19
- package/dist/store/memory/index.js.map +1 -1
- package/dist/store/memory/memory.d.ts +3 -2
- package/dist/store/memory/memory.d.ts.map +1 -1
- package/dist/store/memory/memory.js +11 -2
- package/dist/store/memory/memory.js.map +1 -1
- package/dist/store/memory/operation-lock.d.ts +11 -0
- package/dist/store/memory/operation-lock.d.ts.map +1 -0
- package/dist/store/memory/operation-lock.js +94 -0
- package/dist/store/memory/operation-lock.js.map +1 -0
- package/dist/store/run/token-budget-disk.js +3 -0
- package/dist/store/run/token-budget-disk.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +60 -10
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +12 -26
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/file-write-result.d.ts +4 -0
- package/dist/tools/builtins/file-write-result.d.ts.map +1 -0
- package/dist/tools/builtins/file-write-result.js +72 -0
- package/dist/tools/builtins/file-write-result.js.map +1 -0
- package/dist/tools/builtins/glob.d.ts.map +1 -1
- package/dist/tools/builtins/glob.js +8 -1
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +10 -0
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/job.d.ts.map +1 -1
- package/dist/tools/builtins/job.js +6 -0
- package/dist/tools/builtins/job.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +10 -0
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/run-code.d.ts +6 -0
- package/dist/tools/builtins/run-code.d.ts.map +1 -1
- package/dist/tools/builtins/run-code.js +15 -4
- package/dist/tools/builtins/run-code.js.map +1 -1
- package/dist/tools/builtins/search-tools.d.ts.map +1 -1
- package/dist/tools/builtins/search-tools.js +14 -2
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +30 -44
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/defineTool.d.ts +2 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +3 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts +4 -0
- package/dist/tools/file-read-tracker.d.ts.map +1 -0
- package/dist/tools/file-read-tracker.js +16 -0
- package/dist/tools/file-read-tracker.js.map +1 -0
- package/dist/tools/memory/delete.d.ts +4 -0
- package/dist/tools/memory/delete.d.ts.map +1 -0
- package/dist/tools/memory/delete.js +26 -0
- package/dist/tools/memory/delete.js.map +1 -0
- package/dist/tools/memory/index.d.ts +2 -0
- package/dist/tools/memory/index.d.ts.map +1 -1
- package/dist/tools/memory/index.js +6 -0
- package/dist/tools/memory/index.js.map +1 -1
- package/dist/tools/memory/save.d.ts.map +1 -1
- package/dist/tools/memory/save.js +3 -2
- package/dist/tools/memory/save.js.map +1 -1
- package/dist/tools/memory/search.d.ts.map +1 -1
- package/dist/tools/memory/search.js +12 -5
- package/dist/tools/memory/search.js.map +1 -1
- package/dist/tools/memory/update.d.ts +4 -0
- package/dist/tools/memory/update.d.ts.map +1 -0
- package/dist/tools/memory/update.js +47 -0
- package/dist/tools/memory/update.js.map +1 -0
- package/dist/tools/task/list.js +2 -2
- package/dist/tools/task/list.js.map +1 -1
- package/dist/types/agent/scheduler.d.ts +5 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +10 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/computer-use/index.d.ts +6 -0
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/computer-use/index.js.map +1 -1
- package/dist/types/errors/catalog.js +1 -1
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/hitl/index.d.ts +11 -1
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/memory/index.d.ts +14 -1
- package/dist/types/memory/index.d.ts.map +1 -1
- package/dist/types/plugin/index.d.ts +7 -0
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/project/entity.d.ts +1 -0
- package/dist/types/project/entity.d.ts.map +1 -1
- package/dist/types/provider/chat.d.ts +4 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +4 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/model.d.ts +4 -0
- package/dist/types/provider/model.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +6 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/run/answer-review.d.ts +2 -0
- package/dist/types/run/answer-review.d.ts.map +1 -1
- package/dist/types/run/config.d.ts +2 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +19 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/prepare-step.d.ts +19 -1
- package/dist/types/run/prepare-step.d.ts.map +1 -1
- package/dist/types/sandbox/index.d.ts +1 -0
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/structured-output/index.d.ts +8 -2
- package/dist/types/structured-output/index.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +15 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +4 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/dist/utils/json-snapshot.d.ts +3 -0
- package/dist/utils/json-snapshot.d.ts.map +1 -0
- package/dist/utils/json-snapshot.js +79 -0
- package/dist/utils/json-snapshot.js.map +1 -0
- package/package.json +4 -2
- package/src/bridge/a2a/mapper.ts +4 -0
- package/src/bridge/sse/mapper.ts +16 -1
- package/src/compaction/extractor.ts +21 -8
- package/src/compaction/manager.ts +11 -1
- package/src/compaction/verifier.ts +80 -18
- package/src/config/runtime.ts +2 -0
- package/src/contracts/api.ts +1 -0
- package/src/eval/harness-verification.ts +270 -0
- package/src/eval/index.ts +10 -0
- package/src/execution/code-runtime/json-value.ts +82 -0
- package/src/execution/code-runtime/worker-program.ts +239 -0
- package/src/execution/code-runtime/worker.ts +164 -111
- package/src/manager/agent/lifecycle.ts +352 -54
- package/src/manager/run/persistence.ts +7 -0
- package/src/prompt/coding-agent-doctrine.ts +11 -5
- package/src/provider/capabilities.ts +41 -1
- package/src/provider/fallback.ts +9 -2
- package/src/public-runtime.ts +6 -0
- package/src/public-tools.ts +7 -1
- package/src/public-types.ts +7 -0
- package/src/registry/tool/execute.ts +17 -79
- package/src/run/command-gate.ts +61 -13
- package/src/run/index.ts +2 -0
- package/src/run/memory-promoter.ts +32 -40
- package/src/run/memory-recall.ts +254 -0
- package/src/run/reporter.ts +2 -0
- package/src/run/token-budget.ts +55 -14
- package/src/run/workspace-fingerprint.ts +17 -8
- package/src/runtime/query/checkpoint.ts +96 -2
- package/src/runtime/query/executor.ts +100 -29
- package/src/runtime/query/index.ts +76 -16
- package/src/runtime/query/iteration/index.ts +405 -46
- package/src/runtime/query/iteration/native-output.ts +35 -0
- package/src/runtime/query/iteration/phases/advisory.ts +21 -11
- package/src/runtime/query/iteration/phases/compaction.ts +36 -14
- package/src/runtime/query/iteration/phases/context.ts +9 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +5 -1
- package/src/runtime/query/iteration/phases/working-memory.ts +23 -5
- package/src/runtime/query/iteration/stream-turn.ts +15 -0
- package/src/runtime/query/observation-context.ts +86 -0
- package/src/runtime/query/request-context.ts +103 -0
- package/src/runtime/query/request-rich-content.ts +8 -3
- package/src/runtime/query/review-policy.ts +7 -1
- package/src/runtime/query/steering.ts +19 -2
- package/src/runtime/query/tool-call-budget.ts +115 -0
- package/src/runtime/query/tooling.ts +8 -0
- package/src/sandbox/file-walk-program.ts +8 -0
- package/src/sandbox/file-walk.ts +9 -0
- package/src/scheduler/completion-inbox.ts +3 -0
- package/src/scheduler/local.ts +1 -0
- package/src/session/handoff/capacity.ts +7 -3
- package/src/store/memory/disk.ts +52 -30
- package/src/store/memory/index.ts +75 -27
- package/src/store/memory/memory.ts +18 -6
- package/src/store/memory/operation-lock.ts +99 -0
- package/src/store/run/token-budget-disk.ts +3 -0
- package/src/tools/builtins/computer-use.ts +61 -7
- package/src/tools/builtins/edit.ts +12 -27
- package/src/tools/builtins/file-write-result.ts +84 -0
- package/src/tools/builtins/glob.ts +7 -1
- package/src/tools/builtins/grep.ts +10 -0
- package/src/tools/builtins/job.ts +8 -0
- package/src/tools/builtins/read-file.ts +10 -0
- package/src/tools/builtins/run-code.ts +24 -5
- package/src/tools/builtins/search-tools.ts +18 -3
- package/src/tools/builtins/write-file.ts +39 -44
- package/src/tools/defineTool.ts +5 -0
- package/src/tools/file-read-tracker.ts +16 -0
- package/src/tools/memory/delete.ts +29 -0
- package/src/tools/memory/index.ts +6 -0
- package/src/tools/memory/save.ts +4 -2
- package/src/tools/memory/search.ts +12 -5
- package/src/tools/memory/update.ts +50 -0
- package/src/tools/task/list.ts +2 -2
- package/src/types/agent/scheduler.ts +6 -0
- package/src/types/agent/task.ts +13 -0
- package/src/types/computer-use/index.ts +6 -0
- package/src/types/errors/catalog.ts +1 -1
- package/src/types/hitl/index.ts +11 -1
- package/src/types/memory/index.ts +16 -1
- package/src/types/plugin/index.ts +10 -0
- package/src/types/project/entity.ts +1 -0
- package/src/types/provider/chat.ts +2 -0
- package/src/types/provider/config.ts +4 -0
- package/src/types/provider/model.ts +4 -0
- package/src/types/provider/stream.ts +7 -0
- package/src/types/run/answer-review.ts +2 -0
- package/src/types/run/config.ts +2 -0
- package/src/types/run/events.ts +20 -0
- package/src/types/run/prepare-step.ts +19 -1
- package/src/types/sandbox/index.ts +1 -0
- package/src/types/structured-output/index.ts +15 -2
- package/src/types/tool/index.ts +16 -0
- package/src/types/tool/presentation.ts +4 -0
- package/src/utils/json-snapshot.ts +76 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AGENT_MANAGER_DEFAULTS } from '../../constants/agent/index.js'
|
|
2
|
+
import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
|
|
2
3
|
import { GENAI } from '../../constants/telemetry/index.js'
|
|
3
4
|
import type { AgentRegistry } from '../../registry/agent/definitions.js'
|
|
4
5
|
import {
|
|
@@ -91,6 +92,18 @@ interface AgentManagerBaseDeps {
|
|
|
91
92
|
*/
|
|
92
93
|
export type AgentManagerDeps = AgentManagerBaseDeps & TopicManagerDependency
|
|
93
94
|
|
|
95
|
+
/** Internal backpressure: the parent's unmeasured request owns its budget until its receipt. */
|
|
96
|
+
class ParentBudgetRequestPending extends Error {}
|
|
97
|
+
|
|
98
|
+
interface PendingSpawn {
|
|
99
|
+
ready: boolean
|
|
100
|
+
readonly task: AgentTask
|
|
101
|
+
readonly options: SendMessageOptions
|
|
102
|
+
readonly context: AgentTaskContext
|
|
103
|
+
readonly listener?: RunEventListener
|
|
104
|
+
readonly removeAbortListener: () => void
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
interface ChildSpawnRecord {
|
|
95
108
|
subSessionId: SubSessionId
|
|
96
109
|
childSessionId: SessionId
|
|
@@ -141,6 +154,12 @@ export class AgentManager {
|
|
|
141
154
|
private spawnLocks: Map<SessionId, Promise<void>> = new Map()
|
|
142
155
|
private deps: AgentManagerDeps
|
|
143
156
|
private topicManager: TopicManager
|
|
157
|
+
private readonly pendingSpawns = new Map<SessionId, PendingSpawn[]>()
|
|
158
|
+
private readonly drainingParents = new Set<SessionId>()
|
|
159
|
+
private readonly executingTasks = new Set<TaskId>()
|
|
160
|
+
private readonly cancelingTasks = new Set<TaskId>()
|
|
161
|
+
private admissionTimer: ReturnType<typeof setTimeout> | undefined
|
|
162
|
+
private disposed = false
|
|
144
163
|
|
|
145
164
|
constructor(
|
|
146
165
|
registry: AgentRegistry,
|
|
@@ -149,6 +168,9 @@ export class AgentManager {
|
|
|
149
168
|
) {
|
|
150
169
|
this.registry = registry
|
|
151
170
|
this.config = { ...AGENT_MANAGER_DEFAULTS, ...config }
|
|
171
|
+
const maxPending = this.config.maxPendingTasks ?? 128
|
|
172
|
+
if (!Number.isSafeInteger(maxPending) || maxPending < 1)
|
|
173
|
+
throw new Error('maxPendingTasks must be a positive safe integer')
|
|
152
174
|
this.log = resolveLogger(deps.log).child({
|
|
153
175
|
[SCOPE_ATTRIBUTE]: 'manager/agent/lifecycle',
|
|
154
176
|
})
|
|
@@ -161,6 +183,182 @@ export class AgentManager {
|
|
|
161
183
|
context: AgentTaskContext,
|
|
162
184
|
listener?: RunEventListener,
|
|
163
185
|
): Promise<AgentTask> {
|
|
186
|
+
if (this.disposed) throw new Error('Agent manager is disposed')
|
|
187
|
+
if (this.config.capacityBehavior === 'queue')
|
|
188
|
+
return this.enqueueMessage(options, context, listener)
|
|
189
|
+
return this.startMessage(options, context, listener)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private async enqueueMessage(
|
|
193
|
+
options: SendMessageOptions,
|
|
194
|
+
context: AgentTaskContext,
|
|
195
|
+
listener?: RunEventListener,
|
|
196
|
+
): Promise<AgentTask> {
|
|
197
|
+
context.parentAbortController.signal.throwIfAborted()
|
|
198
|
+
if (context.depth >= this.config.maxDepth)
|
|
199
|
+
throw new Error(`Max task depth ${this.config.maxDepth} exceeded`)
|
|
200
|
+
if (options.tenantId !== context.tenantId)
|
|
201
|
+
throw new Error('Tenant mismatch: cross-tenant spawn rejected')
|
|
202
|
+
const definition = this.registry.getOrThrow(options.agentId)
|
|
203
|
+
const count = [...this.pendingSpawns.values()].reduce((sum, queue) => sum + queue.length, 0)
|
|
204
|
+
if (count >= (this.config.maxPendingTasks ?? 128))
|
|
205
|
+
throw new Error(
|
|
206
|
+
`Delegation queue is full (${this.config.maxPendingTasks ?? 128} pending tasks). Wait for existing tasks before submitting more work.`,
|
|
207
|
+
)
|
|
208
|
+
const task: AgentTask = {
|
|
209
|
+
taskId: generateTaskId(),
|
|
210
|
+
agentId: options.agentId,
|
|
211
|
+
agent: definition.typedAgent,
|
|
212
|
+
childAbortController: createChildAbortController(context.parentAbortController),
|
|
213
|
+
context,
|
|
214
|
+
state: 'pending',
|
|
215
|
+
pendingMessages: [],
|
|
216
|
+
createdAt: Date.now(),
|
|
217
|
+
runEventListener: listener,
|
|
218
|
+
}
|
|
219
|
+
this.instances.set(task.taskId, task)
|
|
220
|
+
const onAbort = (): void => {
|
|
221
|
+
// Before admission this observer owns cancellation. Once child scope
|
|
222
|
+
// exists, startup rollback may abort its controller for a configuration
|
|
223
|
+
// error; that is a failure, not a user cancellation.
|
|
224
|
+
if (task.context === context || context.parentAbortController.signal.aborted)
|
|
225
|
+
this.cancel(task.taskId, 'parent')
|
|
226
|
+
}
|
|
227
|
+
task.childAbortController.signal.addEventListener('abort', onAbort, { once: true })
|
|
228
|
+
const entry: PendingSpawn = {
|
|
229
|
+
ready: false,
|
|
230
|
+
task,
|
|
231
|
+
options,
|
|
232
|
+
context,
|
|
233
|
+
listener,
|
|
234
|
+
removeAbortListener: () =>
|
|
235
|
+
task.childAbortController.signal.removeEventListener('abort', onAbort),
|
|
236
|
+
}
|
|
237
|
+
const queue = this.pendingSpawns.get(options.parentSessionId) ?? []
|
|
238
|
+
queue.push(entry)
|
|
239
|
+
this.pendingSpawns.set(options.parentSessionId, queue)
|
|
240
|
+
this.emit({
|
|
241
|
+
type: 'pending',
|
|
242
|
+
taskId: task.taskId,
|
|
243
|
+
agentId: options.agentId,
|
|
244
|
+
parentAgentId: context.parentAgentId,
|
|
245
|
+
depth: context.depth,
|
|
246
|
+
})
|
|
247
|
+
try {
|
|
248
|
+
await listener?.({
|
|
249
|
+
type: 'agent_pending',
|
|
250
|
+
runId: context.parentRunId,
|
|
251
|
+
taskId: task.taskId,
|
|
252
|
+
parentAgentId: context.parentAgentId,
|
|
253
|
+
childAgentId: options.agentId,
|
|
254
|
+
depth: context.depth,
|
|
255
|
+
})
|
|
256
|
+
entry.ready = true
|
|
257
|
+
} catch (error) {
|
|
258
|
+
this.failAdmission(entry, error)
|
|
259
|
+
}
|
|
260
|
+
this.pumpAdmissions(options.parentSessionId)
|
|
261
|
+
return task
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private pumpAdmissions(parentSessionId: SessionId): void {
|
|
265
|
+
if (this.disposed || this.drainingParents.has(parentSessionId)) return
|
|
266
|
+
this.drainingParents.add(parentSessionId)
|
|
267
|
+
void this.drainAdmissions(parentSessionId)
|
|
268
|
+
.finally(() => {
|
|
269
|
+
this.drainingParents.delete(parentSessionId)
|
|
270
|
+
// External handoffs and metadata changes have no manager event. A
|
|
271
|
+
// single slow, unref'ed timer observes those without spinning or
|
|
272
|
+
// keeping a host alive. Actual completions wake admission directly.
|
|
273
|
+
if (!this.disposed && this.pendingSpawns.size > 0 && !this.admissionTimer) {
|
|
274
|
+
this.admissionTimer = setTimeout(() => {
|
|
275
|
+
this.admissionTimer = undefined
|
|
276
|
+
for (const parent of this.pendingSpawns.keys()) this.pumpAdmissions(parent)
|
|
277
|
+
}, 250)
|
|
278
|
+
this.admissionTimer.unref?.()
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
.catch((error) =>
|
|
282
|
+
this.log.error('Delegation admission failed', {
|
|
283
|
+
'exception.message': toErrorMessage(error),
|
|
284
|
+
}),
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
private async drainAdmissions(parentSessionId: SessionId): Promise<void> {
|
|
289
|
+
const queue = this.pendingSpawns.get(parentSessionId)
|
|
290
|
+
if (!queue) return
|
|
291
|
+
while (!this.disposed && queue.length > 0) {
|
|
292
|
+
const entry = queue[0]
|
|
293
|
+
if (!entry) break
|
|
294
|
+
if (isTerminalAgentTaskState(entry.task.state)) {
|
|
295
|
+
entry.removeAbortListener()
|
|
296
|
+
queue.shift()
|
|
297
|
+
continue
|
|
298
|
+
}
|
|
299
|
+
// A concurrent enqueue can pump this same FIFO while the head's
|
|
300
|
+
// pending listener still owns its acknowledgement. Do not provision
|
|
301
|
+
// or spend until that acknowledgement succeeds.
|
|
302
|
+
if (!entry.ready) return
|
|
303
|
+
try {
|
|
304
|
+
await entry.options.beforeStart?.()
|
|
305
|
+
entry.task.childAbortController.signal.throwIfAborted()
|
|
306
|
+
await this.validateSpawn(entry.options, entry.context)
|
|
307
|
+
entry.task.childAbortController.signal.throwIfAborted()
|
|
308
|
+
await this.startMessage(entry.options, entry.context, entry.listener, entry.task)
|
|
309
|
+
entry.removeAbortListener()
|
|
310
|
+
} catch (error) {
|
|
311
|
+
if (
|
|
312
|
+
(error instanceof ParentBudgetRequestPending ||
|
|
313
|
+
(error instanceof DelegationCapacityExceeded && error.details.dimension === 'width')) &&
|
|
314
|
+
!isTerminalAgentTaskState(entry.task.state)
|
|
315
|
+
)
|
|
316
|
+
return
|
|
317
|
+
this.failAdmission(entry, error)
|
|
318
|
+
}
|
|
319
|
+
queue.shift()
|
|
320
|
+
}
|
|
321
|
+
if (queue.length === 0 && this.pendingSpawns.get(parentSessionId) === queue)
|
|
322
|
+
this.pendingSpawns.delete(parentSessionId)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private failAdmission(entry: PendingSpawn, reason: unknown): void {
|
|
326
|
+
entry.removeAbortListener()
|
|
327
|
+
const task = entry.task
|
|
328
|
+
if (isTerminalAgentTaskState(task.state)) return
|
|
329
|
+
task.childAbortController.abort(reason)
|
|
330
|
+
const error = toErrorMessage(reason)
|
|
331
|
+
task.result = {
|
|
332
|
+
runId: entry.context.parentRunId,
|
|
333
|
+
status: 'failed',
|
|
334
|
+
usage: { ...EMPTY_TOKEN_USAGE },
|
|
335
|
+
cost: { ...ZERO_COST },
|
|
336
|
+
iterations: 0,
|
|
337
|
+
durationMs: Date.now() - task.createdAt,
|
|
338
|
+
messages: [],
|
|
339
|
+
lastError: error,
|
|
340
|
+
}
|
|
341
|
+
task.state = 'failed'
|
|
342
|
+
task.completedAt = Date.now()
|
|
343
|
+
this.emit({ type: 'failed', taskId: task.taskId, error })
|
|
344
|
+
this.emitRunEvent(task, {
|
|
345
|
+
type: 'agent_failed',
|
|
346
|
+
runId: entry.context.parentRunId,
|
|
347
|
+
taskId: task.taskId,
|
|
348
|
+
error,
|
|
349
|
+
})
|
|
350
|
+
this.scheduleEviction(task.taskId)
|
|
351
|
+
this.resolveCompletionCallbacks(task.taskId)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
private async startMessage(
|
|
355
|
+
options: SendMessageOptions,
|
|
356
|
+
context: AgentTaskContext,
|
|
357
|
+
listener?: RunEventListener,
|
|
358
|
+
queuedTask?: AgentTask,
|
|
359
|
+
): Promise<AgentTask> {
|
|
360
|
+
await options.beforeStart?.()
|
|
361
|
+
queuedTask?.childAbortController.signal.throwIfAborted()
|
|
164
362
|
if (context.depth >= this.config.maxDepth) {
|
|
165
363
|
throw new Error(
|
|
166
364
|
`Max task depth ${this.config.maxDepth} exceeded (current: ${context.depth}). Recursive agent delegation is limited to prevent resource exhaustion.`,
|
|
@@ -200,23 +398,53 @@ export class AgentManager {
|
|
|
200
398
|
// Reserve synchronously before provisioning yields. The authority is shared
|
|
201
399
|
// across managers and nested sessions, while the session lock below only
|
|
202
400
|
// serializes filesystem capacity checks.
|
|
401
|
+
// Queued siblings share the available budget with the parent. The first
|
|
402
|
+
// of eight children receives at most one ninth; later siblings receive
|
|
403
|
+
// comparable grants instead of a geometric 1/2, 1/4, ... starvation tail.
|
|
404
|
+
let budgetShares = 1
|
|
405
|
+
if (queuedTask) {
|
|
406
|
+
const project = await requireOpenProject(
|
|
407
|
+
this.deps.sessionStore,
|
|
408
|
+
context.projectId,
|
|
409
|
+
context.tenantId,
|
|
410
|
+
'spawn',
|
|
411
|
+
)
|
|
412
|
+
const children = await this.deps.sessionStore.getChildren(
|
|
413
|
+
options.parentSessionId,
|
|
414
|
+
context.tenantId,
|
|
415
|
+
)
|
|
416
|
+
const active = children.filter(
|
|
417
|
+
(child) =>
|
|
418
|
+
child.status !== 'idle' && child.status !== 'failed' && child.status !== 'archived',
|
|
419
|
+
).length
|
|
420
|
+
budgetShares = Math.max(2, project.config.maxDelegationWidth - active + 1)
|
|
421
|
+
}
|
|
422
|
+
context.parentAbortController.signal.throwIfAborted()
|
|
423
|
+
queuedTask?.childAbortController.signal.throwIfAborted()
|
|
203
424
|
const remaining = context.budget.remaining
|
|
204
425
|
const maxAllocation = Number.isFinite(remaining)
|
|
205
|
-
? Math.floor(remaining * this.config.maxBudgetFraction)
|
|
206
|
-
: (options.budgetAllocation?.tokenBudget ??
|
|
207
|
-
(options.configOverrides?.tokenBudget === 0
|
|
208
|
-
? 200_000
|
|
209
|
-
: (options.configOverrides?.tokenBudget ?? 200_000)))
|
|
426
|
+
? Math.floor(Math.min(remaining * this.config.maxBudgetFraction, remaining / budgetShares))
|
|
427
|
+
: (options.budgetAllocation?.tokenBudget ?? options.configOverrides?.tokenBudget ?? 200_000)
|
|
210
428
|
const allocatedTokens = Math.min(
|
|
211
429
|
options.budgetAllocation?.tokenBudget ?? maxAllocation,
|
|
212
430
|
maxAllocation,
|
|
213
431
|
)
|
|
214
|
-
if (
|
|
432
|
+
if (
|
|
433
|
+
!Number.isSafeInteger(allocatedTokens) ||
|
|
434
|
+
allocatedTokens < 0 ||
|
|
435
|
+
(allocatedTokens === 0 && Number.isFinite(remaining))
|
|
436
|
+
) {
|
|
215
437
|
throw new NamzuError({
|
|
216
438
|
code: 'invalid_config',
|
|
217
|
-
message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer.`,
|
|
439
|
+
message: `Cannot spawn "${options.agentId}": the parent has ${remaining} tokens remaining; a child allocation must be a finite positive integer, or zero under an unlimited parent budget.`,
|
|
218
440
|
})
|
|
219
441
|
}
|
|
442
|
+
// No await between this check and reserve: a parent's response may start
|
|
443
|
+
// during the scope reads above. Its own in-flight request is temporary
|
|
444
|
+
// backpressure; descendant requests are expected during a fan-out.
|
|
445
|
+
// Invalid/zero allocations above still fail rather than waiting forever.
|
|
446
|
+
if (queuedTask && context.budget.hasInFlightRequest)
|
|
447
|
+
throw new ParentBudgetRequestPending('The parent provider request is still in flight')
|
|
220
448
|
const childBudget = context.budget.reserve(allocatedTokens)
|
|
221
449
|
let spawnRecord: ChildSpawnRecord
|
|
222
450
|
try {
|
|
@@ -230,9 +458,12 @@ export class AgentManager {
|
|
|
230
458
|
let childAbortController: AbortController | undefined
|
|
231
459
|
let agentTask: AgentTask | undefined
|
|
232
460
|
try {
|
|
233
|
-
childAbortController =
|
|
461
|
+
childAbortController =
|
|
462
|
+
queuedTask?.childAbortController ??
|
|
463
|
+
createChildAbortController(context.parentAbortController)
|
|
234
464
|
|
|
235
|
-
|
|
465
|
+
childAbortController.signal.throwIfAborted()
|
|
466
|
+
const taskId = queuedTask?.taskId ?? generateTaskId()
|
|
236
467
|
|
|
237
468
|
const childParentActor: ActorRef = {
|
|
238
469
|
kind: 'agent',
|
|
@@ -279,40 +510,42 @@ export class AgentManager {
|
|
|
279
510
|
...(resolvedDenies.length > 0 ? { toolDenies: resolvedDenies } : {}),
|
|
280
511
|
}
|
|
281
512
|
|
|
282
|
-
agentTask = {
|
|
513
|
+
agentTask = Object.assign(queuedTask ?? {}, {
|
|
283
514
|
taskId,
|
|
284
515
|
agentId: options.agentId,
|
|
285
516
|
agent,
|
|
286
517
|
childAbortController,
|
|
287
518
|
context: childContext,
|
|
288
519
|
state: 'pending',
|
|
289
|
-
pendingMessages: [],
|
|
290
|
-
createdAt: Date.now(),
|
|
520
|
+
pendingMessages: queuedTask?.pendingMessages ?? [],
|
|
521
|
+
createdAt: queuedTask?.createdAt ?? Date.now(),
|
|
291
522
|
runEventListener: listener,
|
|
292
|
-
}
|
|
523
|
+
} satisfies AgentTask)
|
|
293
524
|
|
|
294
525
|
childAbortController.signal.throwIfAborted()
|
|
295
526
|
this.instances.set(taskId, agentTask)
|
|
296
527
|
if (resolvedDenies.length > 0) spawnRecord.resolvedToolDenies = resolvedDenies
|
|
297
528
|
this.spawnRecords.set(taskId, spawnRecord)
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
agentId: options.agentId,
|
|
302
|
-
parentAgentId: context.parentAgentId,
|
|
303
|
-
depth: context.depth,
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
if (listener) {
|
|
307
|
-
await listener({
|
|
308
|
-
type: 'agent_pending',
|
|
309
|
-
runId: context.parentRunId,
|
|
529
|
+
if (!queuedTask)
|
|
530
|
+
this.emit({
|
|
531
|
+
type: 'pending',
|
|
310
532
|
taskId,
|
|
533
|
+
agentId: options.agentId,
|
|
311
534
|
parentAgentId: context.parentAgentId,
|
|
312
|
-
childAgentId: options.agentId,
|
|
313
535
|
depth: context.depth,
|
|
314
536
|
})
|
|
315
537
|
|
|
538
|
+
if (listener) {
|
|
539
|
+
if (!queuedTask)
|
|
540
|
+
await listener({
|
|
541
|
+
type: 'agent_pending',
|
|
542
|
+
runId: context.parentRunId,
|
|
543
|
+
taskId,
|
|
544
|
+
parentAgentId: context.parentAgentId,
|
|
545
|
+
childAgentId: options.agentId,
|
|
546
|
+
depth: context.depth,
|
|
547
|
+
})
|
|
548
|
+
|
|
316
549
|
const lineage: Lineage = {
|
|
317
550
|
parentSessionId: spawnRecord.parentSessionId,
|
|
318
551
|
rootSessionId: spawnRecord.rootSessionId,
|
|
@@ -454,9 +687,11 @@ export class AgentManager {
|
|
|
454
687
|
})
|
|
455
688
|
}
|
|
456
689
|
childConfig.tokenBudget =
|
|
457
|
-
|
|
458
|
-
?
|
|
459
|
-
:
|
|
690
|
+
allocatedTokens === 0
|
|
691
|
+
? childConfig.tokenBudget
|
|
692
|
+
: childConfig.tokenBudget === 0
|
|
693
|
+
? allocatedTokens
|
|
694
|
+
: Math.min(childConfig.tokenBudget, allocatedTokens)
|
|
460
695
|
childBudget.narrow(childConfig.tokenBudget)
|
|
461
696
|
|
|
462
697
|
// Stamped AFTER the builder, for the fourth time and the same reason:
|
|
@@ -503,17 +738,35 @@ export class AgentManager {
|
|
|
503
738
|
|
|
504
739
|
await childBudget.flush()
|
|
505
740
|
childAbortController.signal.throwIfAborted()
|
|
506
|
-
this.
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
741
|
+
this.executingTasks.add(taskId)
|
|
742
|
+
const runningTask = agentTask
|
|
743
|
+
this.runChild(runningTask, options, childConfig, listener)
|
|
744
|
+
.catch(async (err) => {
|
|
745
|
+
// A thrown invocation supplied no final usage receipt. Keep its
|
|
746
|
+
// reservation, including when its task handle was canceled or evicted.
|
|
747
|
+
let failure = err
|
|
748
|
+
try {
|
|
749
|
+
await childBudget.flush()
|
|
750
|
+
} catch (writeError) {
|
|
751
|
+
failure = writeError
|
|
752
|
+
}
|
|
753
|
+
// Cancellation may already have published a terminal handle. Its
|
|
754
|
+
// invocation has only NOW stopped; release the persisted edge here.
|
|
755
|
+
if (!this.instances.has(taskId) || isTerminalAgentTaskState(runningTask.state)) {
|
|
756
|
+
await this.failSubSession(spawnRecord)
|
|
757
|
+
} else this.markFailed(taskId, toErrorMessage(failure))
|
|
758
|
+
})
|
|
759
|
+
.finally(() => {
|
|
760
|
+
this.executingTasks.delete(taskId)
|
|
761
|
+
if (!this.instances.has(taskId)) this.spawnRecords.delete(taskId)
|
|
762
|
+
this.pumpAdmissions(options.parentSessionId)
|
|
763
|
+
})
|
|
764
|
+
.catch((error) =>
|
|
765
|
+
this.log.warn('Child cleanup failed', {
|
|
766
|
+
'namzu.task.id': taskId,
|
|
767
|
+
'exception.message': toErrorMessage(error),
|
|
768
|
+
}),
|
|
769
|
+
)
|
|
517
770
|
|
|
518
771
|
return agentTask
|
|
519
772
|
} catch (err) {
|
|
@@ -521,7 +774,7 @@ export class AgentManager {
|
|
|
521
774
|
// provisioned resources are still ours to return. A builder/listener
|
|
522
775
|
// failure must not strand a pending task the caller never received.
|
|
523
776
|
if (agentTask) {
|
|
524
|
-
await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err)
|
|
777
|
+
await this.rollbackUnstartedSpawn(agentTask, spawnRecord, err, queuedTask !== undefined)
|
|
525
778
|
} else {
|
|
526
779
|
childAbortController?.abort()
|
|
527
780
|
childBudget.settle(0)
|
|
@@ -536,6 +789,7 @@ export class AgentManager {
|
|
|
536
789
|
agentTask: AgentTask,
|
|
537
790
|
spawnRecord: ChildSpawnRecord,
|
|
538
791
|
reason: unknown,
|
|
792
|
+
retainHandle = false,
|
|
539
793
|
): Promise<void> {
|
|
540
794
|
agentTask.childAbortController.abort()
|
|
541
795
|
agentTask.context.budget.settle(0)
|
|
@@ -559,7 +813,20 @@ export class AgentManager {
|
|
|
559
813
|
}
|
|
560
814
|
}
|
|
561
815
|
this.clearEvictionTimer(agentTask.taskId)
|
|
562
|
-
|
|
816
|
+
if (retainHandle) {
|
|
817
|
+
agentTask.result ??= {
|
|
818
|
+
runId: agentTask.context.parentRunId,
|
|
819
|
+
status: 'failed',
|
|
820
|
+
usage: { ...EMPTY_TOKEN_USAGE },
|
|
821
|
+
cost: { ...ZERO_COST },
|
|
822
|
+
iterations: 0,
|
|
823
|
+
durationMs: Date.now() - agentTask.createdAt,
|
|
824
|
+
messages: [],
|
|
825
|
+
lastError: toErrorMessage(reason),
|
|
826
|
+
}
|
|
827
|
+
agentTask.completedAt = Date.now()
|
|
828
|
+
this.scheduleEviction(agentTask.taskId)
|
|
829
|
+
} else this.instances.delete(agentTask.taskId)
|
|
563
830
|
this.spawnRecords.delete(agentTask.taskId)
|
|
564
831
|
this.resolveCompletionCallbacks(agentTask.taskId)
|
|
565
832
|
await this.rollbackSpawnResources(spawnRecord)
|
|
@@ -581,14 +848,22 @@ export class AgentManager {
|
|
|
581
848
|
}
|
|
582
849
|
|
|
583
850
|
cancel(taskId: TaskId, cause?: CancelCause): void {
|
|
851
|
+
if (this.cancelingTasks.has(taskId)) return
|
|
584
852
|
const agentTask = this.instances.get(taskId)
|
|
585
853
|
if (!agentTask || isTerminalAgentTaskState(agentTask.state)) return
|
|
586
854
|
|
|
587
855
|
// Was the bare string `'canceled'`, which `abortReasonText` suppresses
|
|
588
856
|
// by name — its docblock cites this exact call site — so the child's
|
|
589
857
|
// run saw a cancellation with no attributable origin at all.
|
|
590
|
-
|
|
591
|
-
this
|
|
858
|
+
// Abort listeners run synchronously. A pending task's parent observer
|
|
859
|
+
// must not reenter this method and replace an explicit user's cause.
|
|
860
|
+
this.cancelingTasks.add(taskId)
|
|
861
|
+
try {
|
|
862
|
+
agentTask.childAbortController.abort(cause ? new RunCancelled(cause) : undefined)
|
|
863
|
+
this.markCanceled(taskId, cause)
|
|
864
|
+
} finally {
|
|
865
|
+
this.cancelingTasks.delete(taskId)
|
|
866
|
+
}
|
|
592
867
|
}
|
|
593
868
|
|
|
594
869
|
cancelAll(parentRunId: RunId, cause: CancelCause = 'parent'): void {
|
|
@@ -687,12 +962,18 @@ export class AgentManager {
|
|
|
687
962
|
if (isTerminalAgentTaskState(agentTask.state)) {
|
|
688
963
|
this.clearEvictionTimer(taskId)
|
|
689
964
|
this.instances.delete(taskId)
|
|
690
|
-
this.spawnRecords.delete(taskId)
|
|
965
|
+
if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
|
|
691
966
|
}
|
|
692
967
|
}
|
|
693
968
|
}
|
|
694
969
|
|
|
695
970
|
dispose(): void {
|
|
971
|
+
this.disposed = true
|
|
972
|
+
if (this.admissionTimer) clearTimeout(this.admissionTimer)
|
|
973
|
+
this.admissionTimer = undefined
|
|
974
|
+
for (const queue of this.pendingSpawns.values())
|
|
975
|
+
for (const entry of queue) entry.removeAbortListener()
|
|
976
|
+
this.pendingSpawns.clear()
|
|
696
977
|
for (const taskId of this.instances.keys()) {
|
|
697
978
|
this.clearEvictionTimer(taskId)
|
|
698
979
|
}
|
|
@@ -704,7 +985,8 @@ export class AgentManager {
|
|
|
704
985
|
this.cancel(taskId)
|
|
705
986
|
}
|
|
706
987
|
this.instances.clear()
|
|
707
|
-
this.spawnRecords.
|
|
988
|
+
for (const taskId of this.spawnRecords.keys())
|
|
989
|
+
if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
|
|
708
990
|
this.listeners.length = 0
|
|
709
991
|
}
|
|
710
992
|
|
|
@@ -748,10 +1030,10 @@ export class AgentManager {
|
|
|
748
1030
|
}
|
|
749
1031
|
}
|
|
750
1032
|
|
|
751
|
-
private async
|
|
1033
|
+
private async validateSpawn(
|
|
752
1034
|
options: SendMessageOptions,
|
|
753
1035
|
context: AgentTaskContext,
|
|
754
|
-
): Promise<
|
|
1036
|
+
): Promise<void> {
|
|
755
1037
|
// Phase 9: deps are unconditional required. Every spawn produces a
|
|
756
1038
|
// SubSession + Session + WorkspaceRef triple (Convention #0: no
|
|
757
1039
|
// partial/legacy path).
|
|
@@ -782,6 +1064,8 @@ export class AgentManager {
|
|
|
782
1064
|
`Parent session ${options.parentSessionId} not found for tenant ${context.tenantId} — spawn rejected`,
|
|
783
1065
|
)
|
|
784
1066
|
}
|
|
1067
|
+
if (parentSession.status === 'archived')
|
|
1068
|
+
throw new Error('Delegation parent session is archived')
|
|
785
1069
|
if (parentSession.topicId !== context.topicId) {
|
|
786
1070
|
throw new Error(
|
|
787
1071
|
`Topic mismatch on spawn: parent session ${parentSession.id} is on topic ${parentSession.topicId}, but context.topicId=${context.topicId}. Cross-topic spawn is forbidden (session-hierarchy.md §6.3).`,
|
|
@@ -812,6 +1096,15 @@ export class AgentManager {
|
|
|
812
1096
|
project.config.maxDelegationWidth,
|
|
813
1097
|
context.tenantId,
|
|
814
1098
|
)
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
private async provisionSpawnUnlocked(
|
|
1102
|
+
options: SendMessageOptions,
|
|
1103
|
+
context: AgentTaskContext,
|
|
1104
|
+
): Promise<ChildSpawnRecord> {
|
|
1105
|
+
await this.validateSpawn(options, context)
|
|
1106
|
+
context.parentAbortController.signal.throwIfAborted()
|
|
1107
|
+
const store = this.deps.sessionStore
|
|
815
1108
|
|
|
816
1109
|
// Ancestry walk gives both the child depth and the root session id
|
|
817
1110
|
// attached to every sub-session event from here down.
|
|
@@ -1228,6 +1521,7 @@ export class AgentManager {
|
|
|
1228
1521
|
}
|
|
1229
1522
|
|
|
1230
1523
|
private scheduleEviction(taskId: TaskId): void {
|
|
1524
|
+
if (this.disposed) return
|
|
1231
1525
|
const agentTask = this.instances.get(taskId)
|
|
1232
1526
|
if (!agentTask) return
|
|
1233
1527
|
|
|
@@ -1235,7 +1529,7 @@ export class AgentManager {
|
|
|
1235
1529
|
|
|
1236
1530
|
const timer = setTimeout(() => {
|
|
1237
1531
|
this.instances.delete(taskId)
|
|
1238
|
-
this.spawnRecords.delete(taskId)
|
|
1532
|
+
if (!this.executingTasks.has(taskId)) this.spawnRecords.delete(taskId)
|
|
1239
1533
|
this.evictionTimers.delete(taskId)
|
|
1240
1534
|
this.log.info('Agent task evicted', { 'namzu.agent.task_id': taskId })
|
|
1241
1535
|
}, this.config.evictionMs)
|
|
@@ -1274,15 +1568,19 @@ export class AgentManager {
|
|
|
1274
1568
|
|
|
1275
1569
|
private emitRunEvent(agentTask: AgentTask, event: RunEvent): void {
|
|
1276
1570
|
if (!agentTask.runEventListener) return
|
|
1277
|
-
|
|
1278
|
-
agentTask.runEventListener(event)
|
|
1279
|
-
} catch (err) {
|
|
1571
|
+
const reportFailure = (error: unknown): void => {
|
|
1280
1572
|
this.log.error('RunEvent emission error', {
|
|
1281
1573
|
'namzu.event.type': event.type,
|
|
1282
|
-
'
|
|
1283
|
-
'exception.message': toErrorMessage(err),
|
|
1574
|
+
'exception.message': toErrorMessage(error),
|
|
1284
1575
|
})
|
|
1285
1576
|
}
|
|
1577
|
+
try {
|
|
1578
|
+
// Terminal observation must not delay completion or leak a rejected
|
|
1579
|
+
// listener promise into the host as an unhandled rejection.
|
|
1580
|
+
void Promise.resolve(agentTask.runEventListener(event)).catch(reportFailure)
|
|
1581
|
+
} catch (error) {
|
|
1582
|
+
reportFailure(error)
|
|
1583
|
+
}
|
|
1286
1584
|
}
|
|
1287
1585
|
}
|
|
1288
1586
|
|
|
@@ -306,6 +306,7 @@ export class RunPersistence {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
markFailed(error: string, providerError?: ProviderErrorInfo): void {
|
|
309
|
+
this.clearStructuredOutput()
|
|
309
310
|
this.run.status = 'failed'
|
|
310
311
|
this.run.stopReason = 'error'
|
|
311
312
|
this.run.lastError = error
|
|
@@ -314,6 +315,7 @@ export class RunPersistence {
|
|
|
314
315
|
}
|
|
315
316
|
|
|
316
317
|
markCancelled(): void {
|
|
318
|
+
this.clearStructuredOutput()
|
|
317
319
|
this.run.status = 'cancelled'
|
|
318
320
|
this.run.stopReason = 'cancelled'
|
|
319
321
|
this.run.endedAt = Date.now()
|
|
@@ -459,6 +461,11 @@ export class RunPersistence {
|
|
|
459
461
|
* and without the override flag it would walk the tail and put the stale
|
|
460
462
|
* prose back.
|
|
461
463
|
*/
|
|
464
|
+
/** Invalidate a structured value without replacing the host's textual result. */
|
|
465
|
+
clearStructuredOutput(): void {
|
|
466
|
+
delete this.run.structuredOutput
|
|
467
|
+
}
|
|
468
|
+
|
|
462
469
|
setStructuredOutput(value: unknown): void {
|
|
463
470
|
this.run.structuredOutput = value
|
|
464
471
|
this.setResult(typeof value === 'string' ? value : JSON.stringify(value))
|
|
@@ -49,11 +49,17 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
|
|
|
49
49
|
- Reference code as \`path:line\` so the user can jump to it.
|
|
50
50
|
- Correct an earlier statement only when the error would change the user's code, conclusions or decisions; state it once, plainly, and continue. No apologies, no tally of past mistakes.
|
|
51
51
|
|
|
52
|
+
### Using available evidence
|
|
53
|
+
- Carry the active task and established facts across turns. A question or status request during ongoing work does not cancel that work unless the user says so. Ask for clarification when the intended change is genuinely unspecified; do not ask the user to repeat information already available.
|
|
54
|
+
- Treat a successful tool result together with its exact inputs as evidence of what happened. Content you successfully wrote is available evidence, just like content you read. An announced action, proposed call, failed call, or another agent's unsupported claim is not proof of completion.
|
|
55
|
+
- Reuse relevant evidence still available in conversation instead of observing it again by habit. Distinguish the last observed state from a guarantee that external state has not changed. Re-observe when evidence is missing, partial or invalidated, when another actor may have changed the target, or when freshness matters to the next action. If a check or exact-match edit reports a mismatch, inspect the current state and revise the action; do not force the old plan through.
|
|
56
|
+
- When an observation is needed, ask the narrowest question that resolves the uncertainty. For a known file, inspect that file rather than listing the whole working directory. Recover missing observations with read-only tools, not by repeating a state-changing action.
|
|
57
|
+
|
|
52
58
|
### Reading and editing code
|
|
53
|
-
-
|
|
54
|
-
- Prefer the dedicated tools over shell equivalents: \`read\` over \`cat\`, the \`grep\` tool over running \`grep\` or \`rg\` through bash
|
|
55
|
-
- Independent tool calls go in one response. Reading three files, or running a grep and a glob, does not need three turns.
|
|
56
|
-
-
|
|
59
|
+
- Before editing, use the relevant file content already available from a successful read or write; if it is unavailable or may be stale, read the needed portion. Read additional surrounding code when needed to match its naming and idiom. Respect project instructions and tool-specific prerequisites even when they require a fresh read.
|
|
60
|
+
- Prefer the dedicated tools over shell equivalents: \`read\` over \`cat\`, the \`grep\` tool over running \`grep\` or \`rg\` through \`bash\`, \`glob\` over \`find\` or \`ls -R\`, \`edit\` over \`sed -i\`. To change an existing file use \`edit\`; use \`write\` only to create a file or when the user asked for a whole-file rewrite. Prefer their structured inputs and focused results; use shell commands when the task needs them, within the same configured permissions.
|
|
61
|
+
- Independent tool calls go in one response. Reading three files, or running a grep and a glob, does not need three turns. Only group independent operations. Wait for a mutation to finish before an observation or another action that depends on it; runtime ordering and concurrency limits remain authoritative.
|
|
62
|
+
- Match verification to the task and its risk. For a simple text-file operation, use the successful tool result and known content; do not run unrelated project tests or repeat a read merely to restate that result. For code changes, run the relevant tests, typecheck or lint that would catch a mistake, and follow required project checks. Never claim checks you did not run.
|
|
57
63
|
- Never leave the working tree in a state you have not described. If you created scratch files, say where; if you touched files outside the request, say which.
|
|
58
64
|
|
|
59
65
|
### Working with git
|
|
@@ -72,7 +78,7 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
|
|
|
72
78
|
*/
|
|
73
79
|
export const CODING_AGENT_DELEGATION_DOCTRINE = `### Planning and delegating
|
|
74
80
|
- For work that is genuinely multi-step — several files, several distinct stages, anything you would write a checklist for — open a task list with \`task_create\` and keep it current with \`task_update\`, marking each step done when it is done rather than at the end. Do not open one for a single inspect-edit-test cycle; the list is for the user to follow, and a list of one item tells them nothing.
|
|
75
|
-
- When the \`Agent\` tool is available, delegate genuinely independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts.
|
|
81
|
+
- When the \`Agent\` tool is available, delegate genuinely independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Give each sub-agent the objective, relevant context and completion criteria; do not assume it sees context the delegation interface did not supply.
|
|
76
82
|
- Delegate lookups — where something is defined, which files reference it, how a module works — with \`subagent_type: "explore"\`: it has reading and searching tools only and never interrupts the user for permission. Reserve the default sub-agent for work that changes files or runs commands.`
|
|
77
83
|
|
|
78
84
|
/**
|