@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
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { RunId } from '../../types/ids/index.js'
|
|
2
|
+
import type { RunEvent } from '../../types/run/index.js'
|
|
3
|
+
|
|
4
|
+
export function assertMaxToolCalls(limit: number | undefined): void {
|
|
5
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 0)) {
|
|
6
|
+
throw new RangeError('maxToolCalls must be a nonnegative safe integer.')
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** A run-owned admission ledger; durability belongs to the run's event store. */
|
|
11
|
+
export class ToolCallBudget {
|
|
12
|
+
private used = 0
|
|
13
|
+
private initialized?: Promise<void>
|
|
14
|
+
private chain: Promise<void> = Promise.resolve()
|
|
15
|
+
private failed: unknown
|
|
16
|
+
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly limit: number,
|
|
19
|
+
private readonly runId: RunId,
|
|
20
|
+
private readonly emit: (event: RunEvent) => Promise<void>,
|
|
21
|
+
private readonly read?: () => Promise<readonly RunEvent[]>,
|
|
22
|
+
) {
|
|
23
|
+
assertMaxToolCalls(limit)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private async initialize(): Promise<void> {
|
|
27
|
+
const events = (await this.read?.()) ?? []
|
|
28
|
+
if (events.length > 100_000) throw new Error('Tool-call budget recovery exceeds 100000 events.')
|
|
29
|
+
let initialized = false
|
|
30
|
+
let expectedSeq = 0
|
|
31
|
+
for (const event of events) {
|
|
32
|
+
// A replayed transcript must be complete and belong to this run.
|
|
33
|
+
if (event.runId !== this.runId || event.seq !== ++expectedSeq)
|
|
34
|
+
throw new Error('Tool-call budget recovery requires a complete run event log.')
|
|
35
|
+
if (event.type === 'tool_calls_admitted') {
|
|
36
|
+
if (
|
|
37
|
+
!Number.isSafeInteger(event.count) ||
|
|
38
|
+
event.count < 0 ||
|
|
39
|
+
!Number.isSafeInteger(event.used) ||
|
|
40
|
+
event.used !== this.used + event.count ||
|
|
41
|
+
!Number.isSafeInteger(event.limit) ||
|
|
42
|
+
event.limit < event.used ||
|
|
43
|
+
!['initialize', 'batch', 'nested', 'retry'].includes(event.kind) ||
|
|
44
|
+
(event.kind === 'initialize'
|
|
45
|
+
? initialized || event.count !== 0
|
|
46
|
+
: !initialized || event.count === 0)
|
|
47
|
+
)
|
|
48
|
+
throw new Error('Invalid tool-call admission ledger.')
|
|
49
|
+
initialized = true
|
|
50
|
+
this.used = event.used
|
|
51
|
+
} else if (
|
|
52
|
+
!initialized &&
|
|
53
|
+
(event.type === 'tool_executing' || event.type === 'tool_completed')
|
|
54
|
+
) {
|
|
55
|
+
throw new Error('Cannot establish tool-call usage for an earlier unbudgeted execution.')
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (!initialized) {
|
|
59
|
+
await this.emit({
|
|
60
|
+
type: 'tool_calls_admitted',
|
|
61
|
+
runId: this.runId,
|
|
62
|
+
kind: 'initialize',
|
|
63
|
+
count: 0,
|
|
64
|
+
used: 0,
|
|
65
|
+
limit: this.limit,
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Reserve before work, never refund. A refused group consumes no new slots. */
|
|
71
|
+
async admit(
|
|
72
|
+
count: number,
|
|
73
|
+
kind: 'batch' | 'nested' | 'retry',
|
|
74
|
+
signal: AbortSignal,
|
|
75
|
+
): Promise<string | undefined> {
|
|
76
|
+
signal.throwIfAborted()
|
|
77
|
+
const previous = this.chain
|
|
78
|
+
let release!: () => void
|
|
79
|
+
this.chain = new Promise<void>((resolve) => {
|
|
80
|
+
release = resolve
|
|
81
|
+
})
|
|
82
|
+
try {
|
|
83
|
+
await previous
|
|
84
|
+
signal.throwIfAborted()
|
|
85
|
+
if (this.failed !== undefined) throw this.failed
|
|
86
|
+
this.initialized ??= this.initialize()
|
|
87
|
+
await this.initialized
|
|
88
|
+
signal.throwIfAborted()
|
|
89
|
+
if (count > Math.max(0, this.limit - this.used)) {
|
|
90
|
+
return `Tool-call budget exhausted: this ${kind} needs ${count} slots; ${Math.max(0, this.limit - this.used)} remain of maxToolCalls=${this.limit}. No calls in this admission were executed.`
|
|
91
|
+
}
|
|
92
|
+
if (count === 0) return undefined
|
|
93
|
+
const used = this.used + count
|
|
94
|
+
try {
|
|
95
|
+
await this.emit({
|
|
96
|
+
type: 'tool_calls_admitted',
|
|
97
|
+
runId: this.runId,
|
|
98
|
+
kind,
|
|
99
|
+
count,
|
|
100
|
+
used,
|
|
101
|
+
limit: this.limit,
|
|
102
|
+
})
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// An uncertain write must not be retried as a fresh allowance.
|
|
105
|
+
this.failed = error ?? new Error('Tool-call admission persistence failed.')
|
|
106
|
+
throw this.failed
|
|
107
|
+
}
|
|
108
|
+
this.used = used
|
|
109
|
+
signal.throwIfAborted()
|
|
110
|
+
return undefined
|
|
111
|
+
} finally {
|
|
112
|
+
release()
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -20,6 +20,7 @@ import { ToolExecutor } from './executor.js'
|
|
|
20
20
|
export type EmitEvent = (event: RunEvent) => Promise<void>
|
|
21
21
|
|
|
22
22
|
export interface ToolingBootstrapConfig {
|
|
23
|
+
fileReadTracker?: import('../../types/tool/index.js').FileReadTracker
|
|
23
24
|
tools: ToolRegistryContract
|
|
24
25
|
runId: RunId
|
|
25
26
|
workingDirectory: string
|
|
@@ -43,6 +44,8 @@ export interface ToolingBootstrapConfig {
|
|
|
43
44
|
web?: import('../../types/tool/index.js').ToolContext['web']
|
|
44
45
|
toolRetryBackoff?: Partial<BackoffPolicy>
|
|
45
46
|
maxToolConcurrency?: number
|
|
47
|
+
maxToolCalls?: number
|
|
48
|
+
readToolCallBudgetEvents?: () => Promise<readonly RunEvent[]>
|
|
46
49
|
maxToolOutputChars?: number
|
|
47
50
|
maxToolContentBytes?: number
|
|
48
51
|
toolOutputDir?: string
|
|
@@ -65,6 +68,7 @@ export class ToolingBootstrap {
|
|
|
65
68
|
return new ToolExecutor(
|
|
66
69
|
{
|
|
67
70
|
tools: config.tools,
|
|
71
|
+
...(config.fileReadTracker ? { fileReadTracker: config.fileReadTracker } : {}),
|
|
68
72
|
runId: config.runId,
|
|
69
73
|
workingDirectory: config.workingDirectory,
|
|
70
74
|
...(config.additionalDirectories?.length
|
|
@@ -84,6 +88,10 @@ export class ToolingBootstrap {
|
|
|
84
88
|
...(config.toolRetryBackoff !== undefined
|
|
85
89
|
? { toolRetryBackoff: config.toolRetryBackoff }
|
|
86
90
|
: {}),
|
|
91
|
+
...(config.maxToolCalls !== undefined ? { maxToolCalls: config.maxToolCalls } : {}),
|
|
92
|
+
...(config.readToolCallBudgetEvents
|
|
93
|
+
? { readToolCallBudgetEvents: config.readToolCallBudgetEvents }
|
|
94
|
+
: {}),
|
|
87
95
|
...(config.maxToolConcurrency !== undefined
|
|
88
96
|
? { maxToolConcurrency: config.maxToolConcurrency }
|
|
89
97
|
: {}),
|
|
@@ -51,6 +51,14 @@ async function run() {
|
|
|
51
51
|
try { canonicalRoot = await fs.realpath(plan.root); }
|
|
52
52
|
catch (error) { if (missing(error)) return; throw error; }
|
|
53
53
|
if (canonicalRoot !== path.resolve(plan.root)) throw new Error('File walk root follows a symbolic link; use its authorized real directory');
|
|
54
|
+
const rootInfo = await fs.lstat(plan.root);
|
|
55
|
+
if (rootInfo.isFile()) {
|
|
56
|
+
visit(++visited);
|
|
57
|
+
if (matchers.some(matcher => matcher.test(path.basename(plan.root)))) {
|
|
58
|
+
await write({type:'entry', path:plan.root, size:rootInfo.size});
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
54
62
|
let start = plan.root;
|
|
55
63
|
for (const part of plan.prefix) {
|
|
56
64
|
start = path.join(start, part);
|
package/src/sandbox/file-walk.ts
CHANGED
|
@@ -227,6 +227,15 @@ async function* executeWalk(
|
|
|
227
227
|
}
|
|
228
228
|
if (canonicalRoot !== path.resolve(plan.root))
|
|
229
229
|
throw new Error('File walk root follows a symbolic link; use its authorized real directory')
|
|
230
|
+
// A named regular file is a one-entry search, not an empty directory.
|
|
231
|
+
const rootInfo = await wait(fs.lstat(plan.root))
|
|
232
|
+
check()
|
|
233
|
+
if (rootInfo.isFile()) {
|
|
234
|
+
noteVisit()
|
|
235
|
+
if (matchers.some((matcher) => matcher.test(path.basename(plan.root))))
|
|
236
|
+
yield { path: plan.root, size: rootInfo.size }
|
|
237
|
+
return
|
|
238
|
+
}
|
|
230
239
|
let start = plan.root
|
|
231
240
|
// Never follow a static-prefix symlink to speed up a pattern: traversal
|
|
232
241
|
// and its optimized starting point must have the same link policy.
|
|
@@ -464,6 +464,9 @@ export function formatCompletionNotification(handles: readonly TaskHandle[]): st
|
|
|
464
464
|
`task_id: ${handle.taskId}`,
|
|
465
465
|
`agent: ${handle.agentId}`,
|
|
466
466
|
`state: ${handle.state}`,
|
|
467
|
+
...(handle.state === 'completed' && run?.stopReason && run.stopReason !== 'end_turn'
|
|
468
|
+
? ['outcome: incomplete — execution stopped; this does not establish task completion.']
|
|
469
|
+
: []),
|
|
467
470
|
...(handle.result?.stopReason ? [`stop_reason: ${handle.result.stopReason}`] : []),
|
|
468
471
|
...(durationMs !== undefined ? [`duration_ms: ${durationMs}`] : []),
|
|
469
472
|
'',
|
package/src/scheduler/local.ts
CHANGED
|
@@ -111,6 +111,7 @@ export class LocalTaskScheduler implements TaskScheduler {
|
|
|
111
111
|
const task = await this.agentManager.sendMessage(
|
|
112
112
|
{
|
|
113
113
|
agentId: options.agentId,
|
|
114
|
+
beforeStart: options.beforeStart,
|
|
114
115
|
input: {
|
|
115
116
|
messages: [createUserMessage(options.prompt)],
|
|
116
117
|
workingDirectory: options.workingDirectory,
|
|
@@ -62,7 +62,7 @@ export interface CapacityValidator {
|
|
|
62
62
|
): Promise<void>
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Asserts `
|
|
65
|
+
* Asserts `liveDirectChildren + pendingNewChildren ≤ projectMaxWidth`
|
|
66
66
|
* under `parentSessionId`. The width cap applies to a single spawn call —
|
|
67
67
|
* a broadcast of N recipients passes `pendingNewChildren = N`.
|
|
68
68
|
*/
|
|
@@ -77,7 +77,7 @@ export interface CapacityValidator {
|
|
|
77
77
|
/**
|
|
78
78
|
* Default validator backed by {@link SessionStore}. Uses `getAncestry` for
|
|
79
79
|
* depth (root-to-self chain length) and `getChildren` for width (count of
|
|
80
|
-
*
|
|
80
|
+
* pending or active direct sub-sessions). Historical edges stay queryable.
|
|
81
81
|
*/
|
|
82
82
|
export class DefaultCapacityValidator implements CapacityValidator {
|
|
83
83
|
constructor(private readonly store: SessionStore) {}
|
|
@@ -108,7 +108,11 @@ export class DefaultCapacityValidator implements CapacityValidator {
|
|
|
108
108
|
tenantId: TenantId,
|
|
109
109
|
): Promise<void> {
|
|
110
110
|
const existing = await this.store.getChildren(parentSessionId, tenantId)
|
|
111
|
-
const
|
|
111
|
+
const live = existing.filter(
|
|
112
|
+
(child) =>
|
|
113
|
+
child.status !== 'idle' && child.status !== 'failed' && child.status !== 'archived',
|
|
114
|
+
)
|
|
115
|
+
const total = live.length + pendingNewChildren
|
|
112
116
|
if (total > projectMaxWidth) {
|
|
113
117
|
throw new DelegationCapacityExceeded({
|
|
114
118
|
dimension: 'width',
|
package/src/store/memory/disk.ts
CHANGED
|
@@ -6,16 +6,24 @@ import type {
|
|
|
6
6
|
CreateMemoryParams,
|
|
7
7
|
MemoryContent,
|
|
8
8
|
MemoryIndexEntry,
|
|
9
|
+
MemoryRecord,
|
|
9
10
|
MemorySearchParams,
|
|
10
11
|
MemorySearchResult,
|
|
11
12
|
MemoryStore,
|
|
13
|
+
UpdateMemoryParams,
|
|
12
14
|
} from '../../types/memory/index.js'
|
|
15
|
+
import { assertMemoryStatus } from '../../types/memory/index.js'
|
|
13
16
|
import { generateMemoryId, isEntityId } from '../../utils/id.js'
|
|
14
17
|
import { SCOPE_ATTRIBUTE } from '../../utils/log/types.js'
|
|
15
18
|
import { type Logger, resolveLogger } from '../../utils/logger.js'
|
|
16
19
|
import { DiskRecordStore } from '../kv/record-store.js'
|
|
17
20
|
import { defineSchema } from '../schema.js'
|
|
18
|
-
import { InMemoryMemoryIndex } from './index.js'
|
|
21
|
+
import { InMemoryMemoryIndex, searchMemoryEntries } from './index.js'
|
|
22
|
+
import {
|
|
23
|
+
DEFAULT_MEMORY_LOCK_TIMEOUT_MS,
|
|
24
|
+
acquireMemoryOperationLock,
|
|
25
|
+
validateMemoryLockTimeout,
|
|
26
|
+
} from './operation-lock.js'
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
* This store's on-disk format, versioned as a unit — which is how a
|
|
@@ -32,27 +40,6 @@ interface StorageLocation {
|
|
|
32
40
|
readonly contentDir: string
|
|
33
41
|
}
|
|
34
42
|
|
|
35
|
-
const operationTails = new Map<string, Promise<void>>()
|
|
36
|
-
|
|
37
|
-
/** Serialize every operation over one canonical disk projection in this process. */
|
|
38
|
-
async function withMemoryOperationLock<T>(key: string, operation: () => Promise<T>): Promise<T> {
|
|
39
|
-
const previous = operationTails.get(key) ?? Promise.resolve()
|
|
40
|
-
let release = () => {}
|
|
41
|
-
const current = new Promise<void>((resolveCurrent) => {
|
|
42
|
-
release = resolveCurrent
|
|
43
|
-
})
|
|
44
|
-
const tail = previous.then(() => current)
|
|
45
|
-
operationTails.set(key, tail)
|
|
46
|
-
|
|
47
|
-
await previous
|
|
48
|
-
try {
|
|
49
|
-
return await operation()
|
|
50
|
-
} finally {
|
|
51
|
-
release()
|
|
52
|
-
if (operationTails.get(key) === tail) operationTails.delete(key)
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
43
|
function invalidIndex(
|
|
57
44
|
reason: string,
|
|
58
45
|
details: { entryIndex?: number; field?: string } = {},
|
|
@@ -163,11 +150,14 @@ function assertMemoryContent(value: unknown, expectedId: MemoryId): asserts valu
|
|
|
163
150
|
export interface DiskMemoryStoreConfig {
|
|
164
151
|
baseDir: string
|
|
165
152
|
logger?: Logger
|
|
153
|
+
/** Maximum wait for another process's operation; default 10 seconds. Never breaks stale locks. */
|
|
154
|
+
lockTimeoutMs?: number
|
|
166
155
|
}
|
|
167
156
|
|
|
168
157
|
export class DiskMemoryStore implements MemoryStore {
|
|
169
158
|
private readonly baseDir: string
|
|
170
159
|
private readonly log: Logger
|
|
160
|
+
private readonly lockTimeoutMs: number
|
|
171
161
|
private readonly index = new InMemoryMemoryIndex()
|
|
172
162
|
private location?: StorageLocation
|
|
173
163
|
// Two instances of one primitive rather than two copies of its body.
|
|
@@ -180,6 +170,8 @@ export class DiskMemoryStore implements MemoryStore {
|
|
|
180
170
|
constructor(config: DiskMemoryStoreConfig) {
|
|
181
171
|
this.baseDir = join(config.baseDir, 'memory')
|
|
182
172
|
this.log = resolveLogger(config.logger).child({ [SCOPE_ATTRIBUTE]: 'store/memory/disk' })
|
|
173
|
+
this.lockTimeoutMs = config.lockTimeoutMs ?? DEFAULT_MEMORY_LOCK_TIMEOUT_MS
|
|
174
|
+
validateMemoryLockTimeout(this.lockTimeoutMs)
|
|
183
175
|
}
|
|
184
176
|
|
|
185
177
|
private get contentDir(): string {
|
|
@@ -235,10 +227,16 @@ export class DiskMemoryStore implements MemoryStore {
|
|
|
235
227
|
operation: (location: StorageLocation) => Promise<T>,
|
|
236
228
|
): Promise<T> {
|
|
237
229
|
const location = await this.storageLocation()
|
|
238
|
-
|
|
230
|
+
const release = await acquireMemoryOperationLock(
|
|
231
|
+
join(dirname(location.indexPath), 'operation.lock'),
|
|
232
|
+
this.lockTimeoutMs,
|
|
233
|
+
)
|
|
234
|
+
try {
|
|
239
235
|
await this.reloadIndex(location)
|
|
240
|
-
return operation(location)
|
|
241
|
-
}
|
|
236
|
+
return await operation(location)
|
|
237
|
+
} finally {
|
|
238
|
+
await release()
|
|
239
|
+
}
|
|
242
240
|
}
|
|
243
241
|
|
|
244
242
|
private async readContent(location: StorageLocation, id: MemoryId): Promise<MemoryContent> {
|
|
@@ -307,10 +305,17 @@ export class DiskMemoryStore implements MemoryStore {
|
|
|
307
305
|
})
|
|
308
306
|
}
|
|
309
307
|
|
|
310
|
-
async
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
308
|
+
async getRecord(id: MemoryId): Promise<MemoryRecord | undefined> {
|
|
309
|
+
return this.withAuthoritativeIndex(async (location) => {
|
|
310
|
+
assertStorageMemoryId(id, (reason) => invalidContent(id, reason, { field: 'id' }))
|
|
311
|
+
const entry = this.index.getEntry(id)
|
|
312
|
+
if (!entry) return undefined
|
|
313
|
+
return structuredClone({ entry, content: await this.readContent(location, id) })
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async update(id: MemoryId, updates: UpdateMemoryParams): Promise<MemoryIndexEntry | undefined> {
|
|
318
|
+
if (updates.status !== undefined) assertMemoryStatus(updates.status)
|
|
314
319
|
return this.withAuthoritativeIndex(async (location) => {
|
|
315
320
|
assertStorageMemoryId(id, (reason) => invalidContent(id, reason, { field: 'id' }))
|
|
316
321
|
const existing = this.index.getEntry(id)
|
|
@@ -321,6 +326,7 @@ export class DiskMemoryStore implements MemoryStore {
|
|
|
321
326
|
title: updates.title ?? existing.title,
|
|
322
327
|
summary: updates.summary ?? existing.summary,
|
|
323
328
|
tags: updates.tags ? [...updates.tags] : existing.tags,
|
|
329
|
+
status: updates.status ?? existing.status,
|
|
324
330
|
updatedAt: Date.now(),
|
|
325
331
|
}
|
|
326
332
|
const updatesContent =
|
|
@@ -367,7 +373,23 @@ export class DiskMemoryStore implements MemoryStore {
|
|
|
367
373
|
}
|
|
368
374
|
|
|
369
375
|
async list(params?: MemorySearchParams): Promise<MemorySearchResult> {
|
|
370
|
-
return this.withAuthoritativeIndex(async () =>
|
|
376
|
+
return this.withAuthoritativeIndex(async (location) => {
|
|
377
|
+
if (!params?.query?.trim() && !params?.requiredIdentifiers?.length)
|
|
378
|
+
return this.index.search(params ?? {})
|
|
379
|
+
// Search the current bodies inside the same operation as the index
|
|
380
|
+
// snapshot. No retained cache can hide another process's update.
|
|
381
|
+
const candidates = this.index.search({
|
|
382
|
+
...params,
|
|
383
|
+
query: undefined,
|
|
384
|
+
limit: undefined,
|
|
385
|
+
requiredIdentifiers: undefined,
|
|
386
|
+
})
|
|
387
|
+
const contents = new Map<MemoryId, string>()
|
|
388
|
+
for (const entry of candidates.entries) {
|
|
389
|
+
contents.set(entry.id, (await this.readContent(location, entry.id)).content)
|
|
390
|
+
}
|
|
391
|
+
return searchMemoryEntries(candidates.entries, params, (id) => contents.get(id) ?? '')
|
|
392
|
+
})
|
|
371
393
|
}
|
|
372
394
|
|
|
373
395
|
getIndex(): InMemoryMemoryIndex {
|
|
@@ -6,37 +6,85 @@ import type {
|
|
|
6
6
|
MemorySearchResult,
|
|
7
7
|
} from '../../types/memory/index.js'
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
results = results.filter(
|
|
18
|
-
(e) => e.title.toLowerCase().includes(q) || e.summary.toLowerCase().includes(q),
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (params.tags && params.tags.length > 0) {
|
|
23
|
-
const requiredTags = params.tags
|
|
24
|
-
results = results.filter((e) => requiredTags.every((tag) => e.tags.includes(tag)))
|
|
25
|
-
}
|
|
9
|
+
function terms(text: string): Set<string> {
|
|
10
|
+
return new Set(
|
|
11
|
+
text
|
|
12
|
+
.normalize('NFKC')
|
|
13
|
+
.toLowerCase()
|
|
14
|
+
.match(/[\p{L}\p{N}]+/gu) ?? [],
|
|
15
|
+
)
|
|
16
|
+
}
|
|
26
17
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
/** Exact normalized word membership; no substring, stemming or alias inference. */
|
|
19
|
+
export function matchesMemoryIdentifier(text: string, identifiers: ReadonlySet<string>): boolean {
|
|
20
|
+
for (const match of text
|
|
21
|
+
.normalize('NFKC')
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.matchAll(/[\p{L}\p{N}_]+/gu)) {
|
|
24
|
+
if (identifiers.has(match[0])) return true
|
|
25
|
+
}
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
/** Shared lexical ranking; a caller-owned index can omit body content. */
|
|
30
|
+
export function searchMemoryEntries(
|
|
31
|
+
entries: readonly MemoryIndexEntry[],
|
|
32
|
+
params: MemorySearchParams,
|
|
33
|
+
contentOf: (id: MemoryId) => string = () => '',
|
|
34
|
+
): MemorySearchResult {
|
|
35
|
+
const query = terms(params.query ?? '')
|
|
36
|
+
const identifiers = new Set(
|
|
37
|
+
params.requiredIdentifiers?.map((id) => id.normalize('NFKC').toLowerCase()),
|
|
38
|
+
)
|
|
39
|
+
if (params.query?.trim() && query.size === 0) return { entries: [], totalCount: 0 }
|
|
40
|
+
const ranked = entries
|
|
41
|
+
.filter(
|
|
42
|
+
(entry) =>
|
|
43
|
+
(!params.status || entry.status === params.status) &&
|
|
44
|
+
(!params.tags?.length || params.tags.every((tag) => entry.tags.includes(tag))),
|
|
45
|
+
)
|
|
46
|
+
.filter(
|
|
47
|
+
(entry) =>
|
|
48
|
+
!identifiers.size ||
|
|
49
|
+
[entry.id, entry.title, entry.summary, contentOf(entry.id)].some((text) =>
|
|
50
|
+
matchesMemoryIdentifier(text, identifiers),
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
.map((entry) => {
|
|
54
|
+
let coverage = 0
|
|
55
|
+
let score = 0
|
|
56
|
+
if (query.size > 0) {
|
|
57
|
+
const title = terms(entry.title)
|
|
58
|
+
const summary = terms(entry.summary)
|
|
59
|
+
const body = terms(contentOf(entry.id))
|
|
60
|
+
for (const term of query) {
|
|
61
|
+
const weight =
|
|
62
|
+
(title.has(term) ? 8 : 0) + (summary.has(term) ? 4 : 0) + (body.has(term) ? 1 : 0)
|
|
63
|
+
if (weight > 0) coverage++
|
|
64
|
+
score += weight
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return { entry, coverage, score }
|
|
68
|
+
})
|
|
69
|
+
.filter(({ coverage }) => query.size === 0 || coverage > 0)
|
|
70
|
+
.sort(
|
|
71
|
+
(a, b) =>
|
|
72
|
+
b.coverage - a.coverage ||
|
|
73
|
+
b.score - a.score ||
|
|
74
|
+
b.entry.updatedAt - a.entry.updatedAt ||
|
|
75
|
+
(a.entry.id < b.entry.id ? -1 : a.entry.id > b.entry.id ? 1 : 0),
|
|
76
|
+
)
|
|
77
|
+
return {
|
|
78
|
+
entries: ranked.slice(0, params.limit ?? ranked.length).map(({ entry }) => entry),
|
|
79
|
+
totalCount: ranked.length,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
32
82
|
|
|
33
|
-
|
|
34
|
-
|
|
83
|
+
export class InMemoryMemoryIndex implements MemoryIndex {
|
|
84
|
+
private entries = new Map<string, MemoryIndexEntry>()
|
|
35
85
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
totalCount,
|
|
39
|
-
}
|
|
86
|
+
search(params: MemorySearchParams): MemorySearchResult {
|
|
87
|
+
return searchMemoryEntries([...this.entries.values()], params)
|
|
40
88
|
}
|
|
41
89
|
|
|
42
90
|
getEntry(id: MemoryId): MemoryIndexEntry | undefined {
|
|
@@ -3,12 +3,15 @@ import type {
|
|
|
3
3
|
CreateMemoryParams,
|
|
4
4
|
MemoryContent,
|
|
5
5
|
MemoryIndexEntry,
|
|
6
|
+
MemoryRecord,
|
|
6
7
|
MemorySearchParams,
|
|
7
8
|
MemorySearchResult,
|
|
8
9
|
MemoryStore,
|
|
10
|
+
UpdateMemoryParams,
|
|
9
11
|
} from '../../types/memory/index.js'
|
|
12
|
+
import { assertMemoryStatus } from '../../types/memory/index.js'
|
|
10
13
|
import { generateMemoryId } from '../../utils/id.js'
|
|
11
|
-
import { InMemoryMemoryIndex } from './index.js'
|
|
14
|
+
import { InMemoryMemoryIndex, searchMemoryEntries } from './index.js'
|
|
12
15
|
|
|
13
16
|
export class InMemoryMemoryStore implements MemoryStore {
|
|
14
17
|
private content = new Map<string, MemoryContent>()
|
|
@@ -47,10 +50,14 @@ export class InMemoryMemoryStore implements MemoryStore {
|
|
|
47
50
|
return this.content.get(id)
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
async
|
|
51
|
-
id
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
async getRecord(id: MemoryId): Promise<MemoryRecord | undefined> {
|
|
54
|
+
const entry = this.index.getEntry(id)
|
|
55
|
+
const content = this.content.get(id)
|
|
56
|
+
return entry && content ? structuredClone({ entry, content }) : undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async update(id: MemoryId, updates: UpdateMemoryParams): Promise<MemoryIndexEntry | undefined> {
|
|
60
|
+
if (updates.status !== undefined) assertMemoryStatus(updates.status)
|
|
54
61
|
const existing = this.index.getEntry(id)
|
|
55
62
|
if (!existing) return undefined
|
|
56
63
|
|
|
@@ -61,6 +68,7 @@ export class InMemoryMemoryStore implements MemoryStore {
|
|
|
61
68
|
title: updates.title ?? existing.title,
|
|
62
69
|
summary: updates.summary ?? existing.summary,
|
|
63
70
|
tags: updates.tags ? [...updates.tags] : existing.tags,
|
|
71
|
+
status: updates.status ?? existing.status,
|
|
64
72
|
updatedAt: now,
|
|
65
73
|
}
|
|
66
74
|
|
|
@@ -94,7 +102,11 @@ export class InMemoryMemoryStore implements MemoryStore {
|
|
|
94
102
|
}
|
|
95
103
|
|
|
96
104
|
async list(params?: MemorySearchParams): Promise<MemorySearchResult> {
|
|
97
|
-
return
|
|
105
|
+
return searchMemoryEntries(
|
|
106
|
+
this.index.allEntries(),
|
|
107
|
+
params ?? {},
|
|
108
|
+
(id) => this.content.get(id)?.content ?? '',
|
|
109
|
+
)
|
|
98
110
|
}
|
|
99
111
|
|
|
100
112
|
getIndex(): InMemoryMemoryIndex {
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto'
|
|
2
|
+
import { type FileHandle, lstat, open, readFile, unlink } from 'node:fs/promises'
|
|
3
|
+
import { setTimeout as delay } from 'node:timers/promises'
|
|
4
|
+
|
|
5
|
+
import { NamzuError } from '../../types/errors/index.js'
|
|
6
|
+
|
|
7
|
+
/** Wait bound, not a lease: a slow or crashed owner is never silently replaced. */
|
|
8
|
+
export const DEFAULT_MEMORY_LOCK_TIMEOUT_MS = 10_000
|
|
9
|
+
|
|
10
|
+
export function validateMemoryLockTimeout(timeoutMs: number): void {
|
|
11
|
+
if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
|
|
12
|
+
throw new NamzuError({
|
|
13
|
+
code: 'invalid_config',
|
|
14
|
+
message: 'DiskMemoryStore lockTimeoutMs must be a positive safe integer.',
|
|
15
|
+
retryable: false,
|
|
16
|
+
})
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function lockError(path: string, reason: string, timeoutMs: number): NamzuError {
|
|
21
|
+
return new NamzuError({
|
|
22
|
+
code: 'storage_error',
|
|
23
|
+
message: `Memory operation lock ${path}: ${reason}. Stop all processes using this memory store and inspect the lock owner before removing a stale lock; it is never broken automatically.`,
|
|
24
|
+
details: { lockPath: path, timeoutMs },
|
|
25
|
+
retryable: false,
|
|
26
|
+
})
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Local-filesystem exclusion for cooperating processes. Atomic rename of the
|
|
31
|
+
* index alone cannot serialize the index/content read-modify-write transaction.
|
|
32
|
+
* An abandoned lock fails closed; guessing a lease expired would permit two
|
|
33
|
+
* live writers to overwrite each other again.
|
|
34
|
+
*/
|
|
35
|
+
export async function acquireMemoryOperationLock(
|
|
36
|
+
path: string,
|
|
37
|
+
timeoutMs: number,
|
|
38
|
+
): Promise<() => Promise<void>> {
|
|
39
|
+
const deadline = performance.now() + timeoutMs
|
|
40
|
+
let attempted = false
|
|
41
|
+
for (;;) {
|
|
42
|
+
if (attempted && performance.now() >= deadline) {
|
|
43
|
+
throw lockError(path, `acquisition timed out after ${timeoutMs} ms`, timeoutMs)
|
|
44
|
+
}
|
|
45
|
+
attempted = true
|
|
46
|
+
let handle: FileHandle
|
|
47
|
+
try {
|
|
48
|
+
handle = await open(path, 'wx', 0o600)
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error
|
|
51
|
+
const existing = await lstat(path).catch((statError: NodeJS.ErrnoException) => {
|
|
52
|
+
if (statError.code === 'ENOENT') return undefined
|
|
53
|
+
throw statError
|
|
54
|
+
})
|
|
55
|
+
if (!existing) continue
|
|
56
|
+
if (!existing.isFile() || existing.isSymbolicLink()) {
|
|
57
|
+
throw lockError(path, 'the occupied lock is not a regular file', timeoutMs)
|
|
58
|
+
}
|
|
59
|
+
if (performance.now() >= deadline) {
|
|
60
|
+
throw lockError(path, `acquisition timed out after ${timeoutMs} ms`, timeoutMs)
|
|
61
|
+
}
|
|
62
|
+
await delay(Math.min(25, Math.max(1, deadline - performance.now())))
|
|
63
|
+
continue
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const owner = `${JSON.stringify({ pid: process.pid, token: randomUUID(), acquiredAt: Date.now() })}\n`
|
|
67
|
+
const identity = await handle.stat().catch(async (error: unknown) => {
|
|
68
|
+
await handle.close()
|
|
69
|
+
// Exclusive create happened, but ownership could not be established.
|
|
70
|
+
// Leave the path for explicit recovery rather than unlink an unknown inode.
|
|
71
|
+
throw error
|
|
72
|
+
})
|
|
73
|
+
try {
|
|
74
|
+
await handle.writeFile(owner, 'utf8')
|
|
75
|
+
await handle.sync()
|
|
76
|
+
} catch (error) {
|
|
77
|
+
await handle.close()
|
|
78
|
+
const current = await lstat(path).catch(() => undefined)
|
|
79
|
+
if (current?.dev === identity.dev && current.ino === identity.ino) {
|
|
80
|
+
await unlink(path).catch(() => undefined)
|
|
81
|
+
}
|
|
82
|
+
throw error
|
|
83
|
+
}
|
|
84
|
+
await handle.close()
|
|
85
|
+
return async () => {
|
|
86
|
+
const current = await lstat(path)
|
|
87
|
+
if (
|
|
88
|
+
!current.isFile() ||
|
|
89
|
+
current.isSymbolicLink() ||
|
|
90
|
+
current.dev !== identity.dev ||
|
|
91
|
+
current.ino !== identity.ino ||
|
|
92
|
+
(await readFile(path, 'utf8')) !== owner
|
|
93
|
+
) {
|
|
94
|
+
throw lockError(path, 'the lock owner changed before release', timeoutMs)
|
|
95
|
+
}
|
|
96
|
+
await unlink(path)
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -227,6 +227,9 @@ function assertSameRoot(before: TokenBudgetSnapshot, after: TokenBudgetSnapshot)
|
|
|
227
227
|
!current ||
|
|
228
228
|
current.accountId !== previous.accountId ||
|
|
229
229
|
current.runId !== previous.runId ||
|
|
230
|
+
(previous.unresolved === true &&
|
|
231
|
+
current.unresolved !== true &&
|
|
232
|
+
(active.has(previous.id) || current.usage === undefined)) ||
|
|
230
233
|
(reconciled && current.usage === undefined) ||
|
|
231
234
|
!usageAtLeast(current.usage, previous.usage)
|
|
232
235
|
) {
|