@qjp88995/agent-runtime 0.0.0-stage → 0.1.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/dist/agent-extensions.d.ts +30 -0
- package/dist/agent-extensions.d.ts.map +1 -0
- package/dist/agent-extensions.js +4 -0
- package/dist/agent-extensions.js.map +1 -0
- package/dist/builtins/ask-outcome.d.ts +49 -0
- package/dist/builtins/ask-outcome.d.ts.map +1 -0
- package/dist/builtins/ask-outcome.js +128 -0
- package/dist/builtins/ask-outcome.js.map +1 -0
- package/dist/builtins/ask-schema.d.ts +110 -0
- package/dist/builtins/ask-schema.d.ts.map +1 -0
- package/dist/builtins/ask-schema.js +124 -0
- package/dist/builtins/ask-schema.js.map +1 -0
- package/dist/builtins/ask.service.d.ts +210 -0
- package/dist/builtins/ask.service.d.ts.map +1 -0
- package/dist/builtins/ask.service.js +373 -0
- package/dist/builtins/ask.service.js.map +1 -0
- package/dist/builtins/ask.tool.d.ts +49 -0
- package/dist/builtins/ask.tool.d.ts.map +1 -0
- package/dist/builtins/ask.tool.js +109 -0
- package/dist/builtins/ask.tool.js.map +1 -0
- package/dist/builtins/index.d.ts +8 -0
- package/dist/builtins/index.d.ts.map +1 -0
- package/dist/builtins/index.js +8 -0
- package/dist/builtins/index.js.map +1 -0
- package/dist/builtins/todo-schema.d.ts +90 -0
- package/dist/builtins/todo-schema.d.ts.map +1 -0
- package/dist/builtins/todo-schema.js +97 -0
- package/dist/builtins/todo-schema.js.map +1 -0
- package/dist/builtins/todo-settle.hook.d.ts +110 -0
- package/dist/builtins/todo-settle.hook.d.ts.map +1 -0
- package/dist/builtins/todo-settle.hook.js +174 -0
- package/dist/builtins/todo-settle.hook.js.map +1 -0
- package/dist/builtins/todo.tool.d.ts +59 -0
- package/dist/builtins/todo.tool.d.ts.map +1 -0
- package/dist/builtins/todo.tool.js +107 -0
- package/dist/builtins/todo.tool.js.map +1 -0
- package/dist/context/context-budget-tracker.d.ts +185 -0
- package/dist/context/context-budget-tracker.d.ts.map +1 -0
- package/dist/context/context-budget-tracker.js +400 -0
- package/dist/context/context-budget-tracker.js.map +1 -0
- package/dist/context/context-budget.d.ts +48 -0
- package/dist/context/context-budget.d.ts.map +1 -0
- package/dist/context/context-budget.js +89 -0
- package/dist/context/context-budget.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +3 -0
- package/dist/context/index.js.map +1 -0
- package/dist/extensions/repeat-call.hook.d.ts +32 -0
- package/dist/extensions/repeat-call.hook.d.ts.map +1 -0
- package/dist/extensions/repeat-call.hook.js +54 -0
- package/dist/extensions/repeat-call.hook.js.map +1 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/hooks/turn-hook.registry.d.ts +51 -0
- package/dist/hooks/turn-hook.registry.d.ts.map +1 -0
- package/dist/hooks/turn-hook.registry.js +233 -0
- package/dist/hooks/turn-hook.registry.js.map +1 -0
- package/dist/hooks/turn-hook.types.d.ts +179 -0
- package/dist/hooks/turn-hook.types.d.ts.map +1 -0
- package/dist/hooks/turn-hook.types.js +48 -0
- package/dist/hooks/turn-hook.types.js.map +1 -0
- package/dist/images/byte-cache.d.ts +24 -0
- package/dist/images/byte-cache.d.ts.map +1 -0
- package/dist/images/byte-cache.js +51 -0
- package/dist/images/byte-cache.js.map +1 -0
- package/dist/images/delivery.d.ts +115 -0
- package/dist/images/delivery.d.ts.map +1 -0
- package/dist/images/delivery.js +321 -0
- package/dist/images/delivery.js.map +1 -0
- package/dist/images/index.d.ts +5 -0
- package/dist/images/index.d.ts.map +1 -0
- package/dist/images/index.js +5 -0
- package/dist/images/index.js.map +1 -0
- package/dist/images/prompt.d.ts +151 -0
- package/dist/images/prompt.d.ts.map +1 -0
- package/dist/images/prompt.js +172 -0
- package/dist/images/prompt.js.map +1 -0
- package/dist/images/vendor-files.d.ts +93 -0
- package/dist/images/vendor-files.d.ts.map +1 -0
- package/dist/images/vendor-files.js +156 -0
- package/dist/images/vendor-files.js.map +1 -0
- package/dist/in-process/conversation-store.d.ts +64 -0
- package/dist/in-process/conversation-store.d.ts.map +1 -0
- package/dist/in-process/conversation-store.js +222 -0
- package/dist/in-process/conversation-store.js.map +1 -0
- package/dist/in-process/coordination.d.ts +54 -0
- package/dist/in-process/coordination.d.ts.map +1 -0
- package/dist/in-process/coordination.js +137 -0
- package/dist/in-process/coordination.js.map +1 -0
- package/dist/in-process/image-source.d.ts +15 -0
- package/dist/in-process/image-source.d.ts.map +1 -0
- package/dist/in-process/image-source.js +27 -0
- package/dist/in-process/image-source.js.map +1 -0
- package/dist/in-process/index.d.ts +36 -0
- package/dist/in-process/index.d.ts.map +1 -0
- package/dist/in-process/index.js +37 -0
- package/dist/in-process/index.js.map +1 -0
- package/dist/in-process/question-store.d.ts +28 -0
- package/dist/in-process/question-store.d.ts.map +1 -0
- package/dist/in-process/question-store.js +122 -0
- package/dist/in-process/question-store.js.map +1 -0
- package/dist/in-process/run-store.d.ts +36 -0
- package/dist/in-process/run-store.d.ts.map +1 -0
- package/dist/in-process/run-store.js +81 -0
- package/dist/in-process/run-store.js.map +1 -0
- package/dist/in-process/session-store.d.ts +33 -0
- package/dist/in-process/session-store.d.ts.map +1 -0
- package/dist/in-process/session-store.js +93 -0
- package/dist/in-process/session-store.js.map +1 -0
- package/dist/in-process/stream-bus.d.ts +52 -0
- package/dist/in-process/stream-bus.d.ts.map +1 -0
- package/dist/in-process/stream-bus.js +200 -0
- package/dist/in-process/stream-bus.js.map +1 -0
- package/dist/in-process/vendor-file-cache.d.ts +11 -0
- package/dist/in-process/vendor-file-cache.d.ts.map +1 -0
- package/dist/in-process/vendor-file-cache.js +47 -0
- package/dist/in-process/vendor-file-cache.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/loop/active-tick.registry.d.ts +31 -0
- package/dist/loop/active-tick.registry.d.ts.map +1 -0
- package/dist/loop/active-tick.registry.js +45 -0
- package/dist/loop/active-tick.registry.js.map +1 -0
- package/dist/loop/index.d.ts +6 -0
- package/dist/loop/index.d.ts.map +1 -0
- package/dist/loop/index.js +6 -0
- package/dist/loop/index.js.map +1 -0
- package/dist/loop/loop-driver.d.ts +127 -0
- package/dist/loop/loop-driver.d.ts.map +1 -0
- package/dist/loop/loop-driver.js +199 -0
- package/dist/loop/loop-driver.js.map +1 -0
- package/dist/loop/message-hub.d.ts +236 -0
- package/dist/loop/message-hub.d.ts.map +1 -0
- package/dist/loop/message-hub.js +439 -0
- package/dist/loop/message-hub.js.map +1 -0
- package/dist/loop/orphan-run.sweeper.d.ts +79 -0
- package/dist/loop/orphan-run.sweeper.d.ts.map +1 -0
- package/dist/loop/orphan-run.sweeper.js +145 -0
- package/dist/loop/orphan-run.sweeper.js.map +1 -0
- package/dist/loop/root-host.d.ts +51 -0
- package/dist/loop/root-host.d.ts.map +1 -0
- package/dist/loop/root-host.js +65 -0
- package/dist/loop/root-host.js.map +1 -0
- package/dist/ports/context-store.d.ts +69 -0
- package/dist/ports/context-store.d.ts.map +1 -0
- package/dist/ports/context-store.js +2 -0
- package/dist/ports/context-store.js.map +1 -0
- package/dist/ports/conversation-store.d.ts +144 -0
- package/dist/ports/conversation-store.d.ts.map +1 -0
- package/dist/ports/conversation-store.js +2 -0
- package/dist/ports/conversation-store.js.map +1 -0
- package/dist/ports/coordination.d.ts +76 -0
- package/dist/ports/coordination.d.ts.map +1 -0
- package/dist/ports/coordination.js +12 -0
- package/dist/ports/coordination.js.map +1 -0
- package/dist/ports/image-source.d.ts +26 -0
- package/dist/ports/image-source.d.ts.map +1 -0
- package/dist/ports/image-source.js +2 -0
- package/dist/ports/image-source.js.map +1 -0
- package/dist/ports/index.d.ts +11 -0
- package/dist/ports/index.d.ts.map +1 -0
- package/dist/ports/index.js +4 -0
- package/dist/ports/index.js.map +1 -0
- package/dist/ports/logger.d.ts +14 -0
- package/dist/ports/logger.d.ts.map +1 -0
- package/dist/ports/logger.js +23 -0
- package/dist/ports/logger.js.map +1 -0
- package/dist/ports/question-store.d.ts +91 -0
- package/dist/ports/question-store.d.ts.map +1 -0
- package/dist/ports/question-store.js +8 -0
- package/dist/ports/question-store.js.map +1 -0
- package/dist/ports/run-store.d.ts +66 -0
- package/dist/ports/run-store.d.ts.map +1 -0
- package/dist/ports/run-store.js +2 -0
- package/dist/ports/run-store.js.map +1 -0
- package/dist/ports/session-store.d.ts +72 -0
- package/dist/ports/session-store.d.ts.map +1 -0
- package/dist/ports/session-store.js +9 -0
- package/dist/ports/session-store.js.map +1 -0
- package/dist/ports/stream-bus.d.ts +52 -0
- package/dist/ports/stream-bus.d.ts.map +1 -0
- package/dist/ports/stream-bus.js +2 -0
- package/dist/ports/stream-bus.js.map +1 -0
- package/dist/ports/vendor-file-cache.d.ts +46 -0
- package/dist/ports/vendor-file-cache.d.ts.map +1 -0
- package/dist/ports/vendor-file-cache.js +2 -0
- package/dist/ports/vendor-file-cache.js.map +1 -0
- package/dist/schema/agent-event.d.ts +35 -0
- package/dist/schema/agent-event.d.ts.map +1 -0
- package/dist/schema/agent-event.js +46 -0
- package/dist/schema/agent-event.js.map +1 -0
- package/dist/schema/agent-run.d.ts +23 -0
- package/dist/schema/agent-run.d.ts.map +1 -0
- package/dist/schema/agent-run.js +37 -0
- package/dist/schema/agent-run.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +4 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/message-part.d.ts +73 -0
- package/dist/schema/message-part.d.ts.map +1 -0
- package/dist/schema/message-part.js +90 -0
- package/dist/schema/message-part.js.map +1 -0
- package/dist/subagent/delegate.tool.d.ts +145 -0
- package/dist/subagent/delegate.tool.d.ts.map +1 -0
- package/dist/subagent/delegate.tool.js +329 -0
- package/dist/subagent/delegate.tool.js.map +1 -0
- package/dist/subagent/index.d.ts +10 -0
- package/dist/subagent/index.d.ts.map +1 -0
- package/dist/subagent/index.js +10 -0
- package/dist/subagent/index.js.map +1 -0
- package/dist/subagent/milestone-body.d.ts +82 -0
- package/dist/subagent/milestone-body.d.ts.map +1 -0
- package/dist/subagent/milestone-body.js +112 -0
- package/dist/subagent/milestone-body.js.map +1 -0
- package/dist/subagent/output-scan.d.ts +14 -0
- package/dist/subagent/output-scan.d.ts.map +1 -0
- package/dist/subagent/output-scan.js +41 -0
- package/dist/subagent/output-scan.js.map +1 -0
- package/dist/subagent/registry-spawn-policy.d.ts +32 -0
- package/dist/subagent/registry-spawn-policy.d.ts.map +1 -0
- package/dist/subagent/registry-spawn-policy.js +48 -0
- package/dist/subagent/registry-spawn-policy.js.map +1 -0
- package/dist/subagent/roster-block.d.ts +45 -0
- package/dist/subagent/roster-block.d.ts.map +1 -0
- package/dist/subagent/roster-block.js +69 -0
- package/dist/subagent/roster-block.js.map +1 -0
- package/dist/subagent/situation-block.d.ts +16 -0
- package/dist/subagent/situation-block.d.ts.map +1 -0
- package/dist/subagent/situation-block.js +71 -0
- package/dist/subagent/situation-block.js.map +1 -0
- package/dist/subagent/spawn-guards.d.ts +32 -0
- package/dist/subagent/spawn-guards.d.ts.map +1 -0
- package/dist/subagent/spawn-guards.js +39 -0
- package/dist/subagent/spawn-guards.js.map +1 -0
- package/dist/subagent/subagent-registry.d.ts +150 -0
- package/dist/subagent/subagent-registry.d.ts.map +1 -0
- package/dist/subagent/subagent-registry.js +167 -0
- package/dist/subagent/subagent-registry.js.map +1 -0
- package/dist/subagent/subagent-sessions.d.ts +213 -0
- package/dist/subagent/subagent-sessions.d.ts.map +1 -0
- package/dist/subagent/subagent-sessions.js +295 -0
- package/dist/subagent/subagent-sessions.js.map +1 -0
- package/dist/testing/contracts.d.ts +37 -0
- package/dist/testing/contracts.d.ts.map +1 -0
- package/dist/testing/contracts.js +607 -0
- package/dist/testing/contracts.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/stub.d.ts +28 -0
- package/dist/testing/stub.d.ts.map +1 -0
- package/dist/testing/stub.js +3 -0
- package/dist/testing/stub.js.map +1 -0
- package/dist/tools/agent-tool.base.d.ts +186 -0
- package/dist/tools/agent-tool.base.d.ts.map +1 -0
- package/dist/tools/agent-tool.base.js +243 -0
- package/dist/tools/agent-tool.base.js.map +1 -0
- package/dist/tools/agent-tool.interface.d.ts +95 -0
- package/dist/tools/agent-tool.interface.d.ts.map +1 -0
- package/dist/tools/agent-tool.interface.js +2 -0
- package/dist/tools/agent-tool.interface.js.map +1 -0
- package/dist/tools/builtin-names.d.ts +18 -0
- package/dist/tools/builtin-names.d.ts.map +1 -0
- package/dist/tools/builtin-names.js +20 -0
- package/dist/tools/builtin-names.js.map +1 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/tool-definition.d.ts +18 -0
- package/dist/tools/tool-definition.d.ts.map +1 -0
- package/dist/tools/tool-definition.js +77 -0
- package/dist/tools/tool-definition.js.map +1 -0
- package/dist/tools/tool-meta.d.ts +55 -0
- package/dist/tools/tool-meta.d.ts.map +1 -0
- package/dist/tools/tool-meta.js +2 -0
- package/dist/tools/tool-meta.js.map +1 -0
- package/dist/tools/tool-registry.d.ts +190 -0
- package/dist/tools/tool-registry.d.ts.map +1 -0
- package/dist/tools/tool-registry.js +379 -0
- package/dist/tools/tool-registry.js.map +1 -0
- package/dist/tools/tool-result.d.ts +41 -0
- package/dist/tools/tool-result.d.ts.map +1 -0
- package/dist/tools/tool-result.js +19 -0
- package/dist/tools/tool-result.js.map +1 -0
- package/dist/tools/tool-wire.d.ts +24 -0
- package/dist/tools/tool-wire.d.ts.map +1 -0
- package/dist/tools/tool-wire.js +2 -0
- package/dist/tools/tool-wire.js.map +1 -0
- package/dist/turn/agent-runtime.d.ts +31 -0
- package/dist/turn/agent-runtime.d.ts.map +1 -0
- package/dist/turn/agent-runtime.js +30 -0
- package/dist/turn/agent-runtime.js.map +1 -0
- package/dist/turn/chunk-projection.d.ts +32 -0
- package/dist/turn/chunk-projection.d.ts.map +1 -0
- package/dist/turn/chunk-projection.js +62 -0
- package/dist/turn/chunk-projection.js.map +1 -0
- package/dist/turn/compaction.d.ts +48 -0
- package/dist/turn/compaction.d.ts.map +1 -0
- package/dist/turn/compaction.js +89 -0
- package/dist/turn/compaction.js.map +1 -0
- package/dist/turn/context-overflow.d.ts +11 -0
- package/dist/turn/context-overflow.d.ts.map +1 -0
- package/dist/turn/context-overflow.js +53 -0
- package/dist/turn/context-overflow.js.map +1 -0
- package/dist/turn/context-pressure.d.ts +100 -0
- package/dist/turn/context-pressure.d.ts.map +1 -0
- package/dist/turn/context-pressure.js +21 -0
- package/dist/turn/context-pressure.js.map +1 -0
- package/dist/turn/events-to-steps.d.ts +8 -0
- package/dist/turn/events-to-steps.d.ts.map +1 -0
- package/dist/turn/events-to-steps.js +64 -0
- package/dist/turn/events-to-steps.js.map +1 -0
- package/dist/turn/index.d.ts +17 -0
- package/dist/turn/index.d.ts.map +1 -0
- package/dist/turn/index.js +16 -0
- package/dist/turn/index.js.map +1 -0
- package/dist/turn/json-preview.d.ts +22 -0
- package/dist/turn/json-preview.d.ts.map +1 -0
- package/dist/turn/json-preview.js +34 -0
- package/dist/turn/json-preview.js.map +1 -0
- package/dist/turn/json-string-field.d.ts +30 -0
- package/dist/turn/json-string-field.d.ts.map +1 -0
- package/dist/turn/json-string-field.js +125 -0
- package/dist/turn/json-string-field.js.map +1 -0
- package/dist/turn/loop-reminder.d.ts +79 -0
- package/dist/turn/loop-reminder.d.ts.map +1 -0
- package/dist/turn/loop-reminder.js +0 -0
- package/dist/turn/loop-reminder.js.map +1 -0
- package/dist/turn/nul-safe.d.ts +21 -0
- package/dist/turn/nul-safe.d.ts.map +1 -0
- package/dist/turn/nul-safe.js +23 -0
- package/dist/turn/nul-safe.js.map +1 -0
- package/dist/turn/parts-from-events.d.ts +71 -0
- package/dist/turn/parts-from-events.d.ts.map +1 -0
- package/dist/turn/parts-from-events.js +217 -0
- package/dist/turn/parts-from-events.js.map +1 -0
- package/dist/turn/parts-to-model-messages.d.ts +158 -0
- package/dist/turn/parts-to-model-messages.d.ts.map +1 -0
- package/dist/turn/parts-to-model-messages.js +334 -0
- package/dist/turn/parts-to-model-messages.js.map +1 -0
- package/dist/turn/pending-work.d.ts +10 -0
- package/dist/turn/pending-work.d.ts.map +1 -0
- package/dist/turn/pending-work.js +14 -0
- package/dist/turn/pending-work.js.map +1 -0
- package/dist/turn/provider-error.d.ts +45 -0
- package/dist/turn/provider-error.d.ts.map +1 -0
- package/dist/turn/provider-error.js +154 -0
- package/dist/turn/provider-error.js.map +1 -0
- package/dist/turn/tool-input-preview.d.ts +94 -0
- package/dist/turn/tool-input-preview.d.ts.map +1 -0
- package/dist/turn/tool-input-preview.js +143 -0
- package/dist/turn/tool-input-preview.js.map +1 -0
- package/dist/turn/tool-input-progress.d.ts +47 -0
- package/dist/turn/tool-input-progress.d.ts.map +1 -0
- package/dist/turn/tool-input-progress.js +50 -0
- package/dist/turn/tool-input-progress.js.map +1 -0
- package/dist/turn/turn-envelope.d.ts +184 -0
- package/dist/turn/turn-envelope.d.ts.map +1 -0
- package/dist/turn/turn-envelope.js +2 -0
- package/dist/turn/turn-envelope.js.map +1 -0
- package/dist/turn/turn-journal.d.ts +49 -0
- package/dist/turn/turn-journal.d.ts.map +1 -0
- package/dist/turn/turn-journal.js +51 -0
- package/dist/turn/turn-journal.js.map +1 -0
- package/dist/turn/turn-run.d.ts +487 -0
- package/dist/turn/turn-run.d.ts.map +1 -0
- package/dist/turn/turn-run.js +1167 -0
- package/dist/turn/turn-run.js.map +1 -0
- package/dist/turn/usage.d.ts +27 -0
- package/dist/turn/usage.d.ts.map +1 -0
- package/dist/turn/usage.js +51 -0
- package/dist/turn/usage.js.map +1 -0
- package/package.json +55 -3
- package/src/agent-extensions.ts +33 -0
- package/src/builtins/ask-outcome.ts +168 -0
- package/src/builtins/ask-schema.ts +186 -0
- package/src/builtins/ask.service.ts +482 -0
- package/src/builtins/ask.tool.ts +138 -0
- package/src/builtins/index.ts +25 -0
- package/src/builtins/todo-schema.ts +108 -0
- package/src/builtins/todo-settle.hook.ts +221 -0
- package/src/builtins/todo.tool.ts +134 -0
- package/src/context/context-budget-tracker.ts +519 -0
- package/src/context/context-budget.ts +124 -0
- package/src/context/index.ts +17 -0
- package/src/extensions/repeat-call.hook.ts +77 -0
- package/src/hooks/index.ts +19 -0
- package/src/hooks/turn-hook.registry.ts +307 -0
- package/src/hooks/turn-hook.types.ts +210 -0
- package/src/images/byte-cache.ts +61 -0
- package/src/images/delivery.ts +508 -0
- package/src/images/index.ts +35 -0
- package/src/images/prompt.ts +289 -0
- package/src/images/vendor-files.ts +235 -0
- package/src/in-process/conversation-store.ts +304 -0
- package/src/in-process/coordination.ts +177 -0
- package/src/in-process/image-source.ts +29 -0
- package/src/in-process/index.ts +52 -0
- package/src/in-process/question-store.ts +155 -0
- package/src/in-process/run-store.ts +115 -0
- package/src/in-process/session-store.ts +118 -0
- package/src/in-process/stream-bus.ts +244 -0
- package/src/in-process/vendor-file-cache.ts +67 -0
- package/src/index.ts +28 -0
- package/src/loop/active-tick.registry.ts +49 -0
- package/src/loop/index.ts +25 -0
- package/src/loop/loop-driver.ts +275 -0
- package/src/loop/message-hub.ts +532 -0
- package/src/loop/orphan-run.sweeper.ts +204 -0
- package/src/loop/root-host.ts +119 -0
- package/src/ports/context-store.ts +71 -0
- package/src/ports/conversation-store.ts +165 -0
- package/src/ports/coordination.ts +81 -0
- package/src/ports/image-source.ts +26 -0
- package/src/ports/index.ts +56 -0
- package/src/ports/logger.ts +34 -0
- package/src/ports/question-store.ts +117 -0
- package/src/ports/run-store.ts +69 -0
- package/src/ports/session-store.ts +80 -0
- package/src/ports/stream-bus.ts +59 -0
- package/src/ports/vendor-file-cache.ts +54 -0
- package/src/schema/agent-event.ts +69 -0
- package/src/schema/agent-run.ts +44 -0
- package/src/schema/index.ts +25 -0
- package/src/schema/message-part.ts +100 -0
- package/src/subagent/delegate.tool.ts +476 -0
- package/src/subagent/index.ts +63 -0
- package/src/subagent/milestone-body.ts +144 -0
- package/src/subagent/output-scan.ts +56 -0
- package/src/subagent/registry-spawn-policy.ts +85 -0
- package/src/subagent/roster-block.ts +103 -0
- package/src/subagent/situation-block.ts +83 -0
- package/src/subagent/spawn-guards.ts +54 -0
- package/src/subagent/subagent-registry.ts +283 -0
- package/src/subagent/subagent-sessions.ts +496 -0
- package/src/testing/contracts.ts +882 -0
- package/src/testing/index.ts +14 -0
- package/src/testing/stub.ts +28 -0
- package/src/tools/agent-tool.base.ts +361 -0
- package/src/tools/agent-tool.interface.ts +101 -0
- package/src/tools/builtin-names.ts +26 -0
- package/src/tools/index.ts +39 -0
- package/src/tools/tool-definition.ts +93 -0
- package/src/tools/tool-meta.ts +60 -0
- package/src/tools/tool-registry.ts +513 -0
- package/src/tools/tool-result.ts +45 -0
- package/src/tools/tool-wire.ts +24 -0
- package/src/turn/agent-runtime.ts +43 -0
- package/src/turn/chunk-projection.ts +81 -0
- package/src/turn/compaction.ts +103 -0
- package/src/turn/context-overflow.ts +54 -0
- package/src/turn/context-pressure.ts +114 -0
- package/src/turn/events-to-steps.ts +73 -0
- package/src/turn/index.ts +71 -0
- package/src/turn/json-preview.ts +36 -0
- package/src/turn/json-string-field.ts +134 -0
- package/src/turn/loop-reminder.ts +0 -0
- package/src/turn/nul-safe.ts +22 -0
- package/src/turn/parts-from-events.ts +248 -0
- package/src/turn/parts-to-model-messages.ts +428 -0
- package/src/turn/pending-work.ts +14 -0
- package/src/turn/provider-error.ts +160 -0
- package/src/turn/tool-input-preview.ts +223 -0
- package/src/turn/tool-input-progress.ts +76 -0
- package/src/turn/turn-envelope.ts +185 -0
- package/src/turn/turn-journal.ts +102 -0
- package/src/turn/turn-run.ts +1479 -0
- package/src/turn/usage.ts +56 -0
- package/README.md +0 -3
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 重复工具调用提醒,**作为扩展接在 turn hook 上**——内核因此不再认识「重复调用」
|
|
3
|
+
* 这件事,它只知道「发起一次工具调用前问一句 preToolUse」。
|
|
4
|
+
*
|
|
5
|
+
* 纯文本层(链怎么记、什么时候出声、说什么)仍在 `runtime/loop-reminder.ts`,
|
|
6
|
+
* 本文件只做接线。
|
|
7
|
+
*
|
|
8
|
+
* **记账与投递在同一个点上**:记在**调用发起处**(被拒绝、报错的调用一样计数
|
|
9
|
+
* ——模型反复撞一个必失败的调用正是最该提醒的循环),劝告随决策的 `systemNote`
|
|
10
|
+
* 挂到**同一次调用的结果**上,归属天然精确。
|
|
11
|
+
*
|
|
12
|
+
* **不能改成「订阅事件流去记账」**(讨论中出现过这个说法,是错的):note 是同步
|
|
13
|
+
* 调用、同步返回劝告,而劝告要跟着这次调用的结果走。改成订阅就是异步消费同步
|
|
14
|
+
* 生产,`tool-call` 事件还没被处理、结果已经落定——竞态,症状是「提醒偶尔不
|
|
15
|
+
* 出现」。能靠订阅的只有不影响本轮行为的事后观测。
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { TurnHookRegistry } from '../hooks/turn-hook.registry.js';
|
|
19
|
+
import {
|
|
20
|
+
type PreToolUseResult,
|
|
21
|
+
type TurnContext,
|
|
22
|
+
type TurnHookImpl,
|
|
23
|
+
type TurnHookMeta,
|
|
24
|
+
turnStateOf,
|
|
25
|
+
} from '../hooks/turn-hook.types.js';
|
|
26
|
+
import type { ToolRegistry } from '../tools/tool-registry.js';
|
|
27
|
+
import {
|
|
28
|
+
createRepeatCallChain,
|
|
29
|
+
type RepeatCallChain,
|
|
30
|
+
} from '../turn/loop-reminder.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 记账链。**order 取最小**:链要看到最接近模型原始输出的那份入参,别的
|
|
34
|
+
* `preToolUse` 插件改写过之后再记,会把「同一次调用」判成换了参数。
|
|
35
|
+
*/
|
|
36
|
+
export const REPEAT_CALL_HOOK_META: TurnHookMeta = {
|
|
37
|
+
name: 'repeat-call-reminder',
|
|
38
|
+
point: 'preToolUse',
|
|
39
|
+
order: 10,
|
|
40
|
+
onError: 'open',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export class RepeatCallHook implements TurnHookImpl {
|
|
44
|
+
constructor(private readonly tools: ToolRegistry) {}
|
|
45
|
+
|
|
46
|
+
preToolUse(
|
|
47
|
+
ctx: TurnContext,
|
|
48
|
+
toolName: string,
|
|
49
|
+
input: unknown
|
|
50
|
+
): Promise<PreToolUseResult> {
|
|
51
|
+
// 链住在本轮的状态槽里:**一个 tick 一条**(见 `createRepeatCallChain`),
|
|
52
|
+
// 生命周期跟着 `TurnRun` 走,插件自己不持有任何跨轮状态。
|
|
53
|
+
const chain = turnStateOf<RepeatCallChain>(
|
|
54
|
+
ctx,
|
|
55
|
+
'repeat-call-reminder',
|
|
56
|
+
() =>
|
|
57
|
+
// 「哪些工具对链透明」由工具自己声明(`ToolMeta.accounting`)——插件同样
|
|
58
|
+
// 不持有任何具体工具名,加一个新的记账工具零改动。
|
|
59
|
+
createRepeatCallChain({ untracked: this.tools.accountingToolNames() })
|
|
60
|
+
);
|
|
61
|
+
const advice = chain.note(toolName, input);
|
|
62
|
+
// **只提醒,不阻止**:模型有时重复调用是对的(轮询一个还没就绪的东西),
|
|
63
|
+
// 拦错了它没有申辩余地;提醒错了它可以无视。
|
|
64
|
+
return Promise.resolve({
|
|
65
|
+
action: 'continue',
|
|
66
|
+
...(advice ? { systemNote: advice } : {}),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** 把重复调用提醒挂到注册表上(runtime 的缺省扩展,宿主可以不挂)。 */
|
|
72
|
+
export function registerRepeatCallHook(
|
|
73
|
+
hooks: TurnHookRegistry,
|
|
74
|
+
tools: ToolRegistry
|
|
75
|
+
): void {
|
|
76
|
+
hooks.register(REPEAT_CALL_HOOK_META, new RepeatCallHook(tools));
|
|
77
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { TurnHookRegistry } from './turn-hook.registry.js';
|
|
2
|
+
export {
|
|
3
|
+
type BeforeTurnResult,
|
|
4
|
+
HOOK_TIMEOUT_MS,
|
|
5
|
+
type ObservePhase,
|
|
6
|
+
ONERROR_HONORED_POINTS,
|
|
7
|
+
type PreToolUseDecision,
|
|
8
|
+
type PreToolUseResult,
|
|
9
|
+
type ReviewOutputResult,
|
|
10
|
+
SINGLETON_POINTS,
|
|
11
|
+
type ToolOutcome,
|
|
12
|
+
TURN_HOOK_POINTS,
|
|
13
|
+
type TurnContext,
|
|
14
|
+
type TurnHookAt,
|
|
15
|
+
type TurnHookImpl,
|
|
16
|
+
type TurnHookMeta,
|
|
17
|
+
type TurnHookPoint,
|
|
18
|
+
turnStateOf,
|
|
19
|
+
} from './turn-hook.types.js';
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { type LoggerFactory, silentLogger } from '../ports/logger.js';
|
|
2
|
+
import type {
|
|
3
|
+
ContextPressure,
|
|
4
|
+
ContextRelief,
|
|
5
|
+
} from '../turn/context-pressure.js';
|
|
6
|
+
import {
|
|
7
|
+
type BeforeTurnResult,
|
|
8
|
+
HOOK_TIMEOUT_MS,
|
|
9
|
+
type ObservePhase,
|
|
10
|
+
ONERROR_HONORED_POINTS,
|
|
11
|
+
type PreToolUseDecision,
|
|
12
|
+
type PreToolUseResult,
|
|
13
|
+
type ReviewOutputResult,
|
|
14
|
+
SINGLETON_POINTS,
|
|
15
|
+
type ToolOutcome,
|
|
16
|
+
type TurnContext,
|
|
17
|
+
type TurnHookAt,
|
|
18
|
+
type TurnHookImpl,
|
|
19
|
+
type TurnHookMeta,
|
|
20
|
+
type TurnHookPoint,
|
|
21
|
+
} from './turn-hook.types.js';
|
|
22
|
+
|
|
23
|
+
interface Entry {
|
|
24
|
+
meta: TurnHookMeta;
|
|
25
|
+
impl: TurnHookImpl;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const TOOL_POINTS: readonly TurnHookPoint[] = ['preToolUse', 'postToolUse'];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* 轮钩子注册表。搬自 agent-base:去掉 Nest 的扫描,宿主显式 `register`(Nest 适配包负责
|
|
32
|
+
* 把装饰器接过来)。同一 point 上按 `order` 升序串行;order 相同按注册顺序。
|
|
33
|
+
*/
|
|
34
|
+
export class TurnHookRegistry {
|
|
35
|
+
private readonly logger: ReturnType<LoggerFactory>;
|
|
36
|
+
private readonly entries: Entry[] = [];
|
|
37
|
+
private readonly names = new Set<string>();
|
|
38
|
+
|
|
39
|
+
constructor(options: { logger?: LoggerFactory } = {}) {
|
|
40
|
+
this.logger = (options.logger ?? silentLogger)('TurnHookRegistry');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** 登记一个钩子。名字重复、没实现那个 point、违反单例 / onError 约束 → 抛。 */
|
|
44
|
+
register(meta: TurnHookMeta, impl: TurnHookImpl): void {
|
|
45
|
+
this.add(meta, impl);
|
|
46
|
+
// 稳定排序:order 相同的保持注册顺序。
|
|
47
|
+
this.entries.sort((a, b) => a.meta.order - b.meta.order);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private add(meta: TurnHookMeta, impl: TurnHookImpl): void {
|
|
51
|
+
if (this.names.has(meta.name)) {
|
|
52
|
+
throw new Error(`duplicate hook name '${meta.name}'`);
|
|
53
|
+
}
|
|
54
|
+
if (typeof impl?.[meta.point] !== 'function') {
|
|
55
|
+
throw new Error(`hook '${meta.name}' must implement ${meta.point}()`);
|
|
56
|
+
}
|
|
57
|
+
if (meta.tools && !TOOL_POINTS.includes(meta.point)) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
`hook '${meta.name}': tools[] only valid on preToolUse/postToolUse`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
if (
|
|
63
|
+
meta.onError === 'closed' &&
|
|
64
|
+
!ONERROR_HONORED_POINTS.includes(meta.point)
|
|
65
|
+
) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`hook '${meta.name}': onError 'closed' is meaningless on point ${meta.point} (nothing to hold back)`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
if (
|
|
71
|
+
SINGLETON_POINTS.includes(meta.point) &&
|
|
72
|
+
this.entries.some(e => e.meta.point === meta.point)
|
|
73
|
+
) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`hook '${meta.name}': ${meta.point} allows at most one implementation`
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
this.names.add(meta.name);
|
|
79
|
+
this.entries.push({ meta, impl });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 取某个 point 上的 hook,**并把 impl 收窄成「那个方法一定在」**(见
|
|
84
|
+
* `TurnHookAt`)。
|
|
85
|
+
*
|
|
86
|
+
* 本文件唯一的类型断言就在这里:准入条件在 `register` 里验过,而 `entries`
|
|
87
|
+
* 是一张异构表(六个 point 混在一起),读出来时类型系统没办法自己重建那条
|
|
88
|
+
* 不变量。断言收在这一处,六个 run 方法就都不必写 `!`。
|
|
89
|
+
*/
|
|
90
|
+
private of<P extends TurnHookPoint>(
|
|
91
|
+
point: P
|
|
92
|
+
): Array<{ meta: TurnHookMeta; impl: TurnHookAt<P> }> {
|
|
93
|
+
return this.entries.filter(e => e.meta.point === point) as Array<{
|
|
94
|
+
meta: TurnHookMeta;
|
|
95
|
+
impl: TurnHookAt<P>;
|
|
96
|
+
}>;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Promise.race 在调用当下就同步给 fn() 和 timeout promise 各挂上
|
|
101
|
+
* then/catch handler,故落败的一方(超时后 fn() 才 reject,或 fn()
|
|
102
|
+
* 先行完成后 timeout 定时器仍会触发但已无人等待)都不会产生
|
|
103
|
+
* unhandled rejection —— 无需额外 `.catch()`。finally 保证
|
|
104
|
+
* clearTimeout 在成功/hook 抛错/超时三条路径下都执行,避免定时器泄漏。
|
|
105
|
+
*/
|
|
106
|
+
private async withTimeout<T>(
|
|
107
|
+
meta: TurnHookMeta,
|
|
108
|
+
fn: () => Promise<T>
|
|
109
|
+
): Promise<T> {
|
|
110
|
+
// 超时**按 hook 声明**(缺省 5s):慢的那个自己声明一个大的,而不是把
|
|
111
|
+
// 默认值抬上去让其余每个点的保护一起松掉(见 `TurnHookMeta.timeoutMs`)。
|
|
112
|
+
const limit = meta.timeoutMs ?? HOOK_TIMEOUT_MS;
|
|
113
|
+
let timer: NodeJS.Timeout | undefined;
|
|
114
|
+
try {
|
|
115
|
+
return await Promise.race([
|
|
116
|
+
fn(),
|
|
117
|
+
new Promise<never>((_, reject) => {
|
|
118
|
+
timer = setTimeout(
|
|
119
|
+
() => reject(new Error(`hook timed out after ${limit}ms`)),
|
|
120
|
+
limit
|
|
121
|
+
);
|
|
122
|
+
}),
|
|
123
|
+
]);
|
|
124
|
+
} finally {
|
|
125
|
+
if (timer) clearTimeout(timer);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async runBeforeTurn(ctx: TurnContext): Promise<BeforeTurnResult> {
|
|
130
|
+
for (const { meta, impl } of this.of('beforeTurn')) {
|
|
131
|
+
let res: BeforeTurnResult;
|
|
132
|
+
try {
|
|
133
|
+
res = await this.withTimeout(meta, () => impl.beforeTurn(ctx));
|
|
134
|
+
} catch (err) {
|
|
135
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
136
|
+
if (meta.onError === 'closed') {
|
|
137
|
+
return { action: 'reject', reply: '系统繁忙,请稍后再试。' };
|
|
138
|
+
}
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (res.action === 'reject') {
|
|
142
|
+
// 断言:reject 不带 reply 会挂死 SSE 流。这是显式抛错(而非吞掉
|
|
143
|
+
// 继续下一个 hook)—— 配错的 hook 不该被 onError:'open' 悄悄放行,
|
|
144
|
+
// 因为放行的后果是流挂死,比让本轮直接报错更糟。
|
|
145
|
+
if (!res.reply) {
|
|
146
|
+
throw new Error(
|
|
147
|
+
`hook '${meta.name}': reject must carry reply text (empty reply hangs the SSE stream)`
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return res;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { action: 'continue' };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async runTransformContext(ctx: TurnContext): Promise<string[]> {
|
|
157
|
+
const appended: string[] = [];
|
|
158
|
+
for (const { meta, impl } of this.of('transformContext')) {
|
|
159
|
+
try {
|
|
160
|
+
const res = await this.withTimeout(meta, () =>
|
|
161
|
+
impl.transformContext(ctx)
|
|
162
|
+
);
|
|
163
|
+
if (res.appendSystem) appended.push(res.appendSystem);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return appended;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async runReviewOutput(
|
|
172
|
+
ctx: TurnContext,
|
|
173
|
+
text: string
|
|
174
|
+
): Promise<ReviewOutputResult> {
|
|
175
|
+
let current = text;
|
|
176
|
+
let changed = false;
|
|
177
|
+
for (const { meta, impl } of this.of('reviewOutput')) {
|
|
178
|
+
try {
|
|
179
|
+
const res = await this.withTimeout(meta, () =>
|
|
180
|
+
impl.reviewOutput(ctx, current)
|
|
181
|
+
);
|
|
182
|
+
if (res.action === 'block') return res;
|
|
183
|
+
if (res.action === 'rewrite') {
|
|
184
|
+
current = res.text;
|
|
185
|
+
changed = true;
|
|
186
|
+
}
|
|
187
|
+
} catch (err) {
|
|
188
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
189
|
+
if (meta.onError === 'closed') {
|
|
190
|
+
return { action: 'block', reply: '系统繁忙,请稍后再试。' };
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// 用显式 changed 标记而非 `current === text`:某个 hook 若把文本改写成
|
|
195
|
+
// 恰好与原文相等的新字符串(例如脱敏后又还原),=== 会误判成「没改过」
|
|
196
|
+
// 而把 rewrite 事实上退化成 keep(丢事件/丢审计,虽结果字节相同但语义
|
|
197
|
+
// 不对)。
|
|
198
|
+
return changed ? { action: 'rewrite', text: current } : { action: 'keep' };
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async runPreToolUse(
|
|
202
|
+
ctx: TurnContext,
|
|
203
|
+
toolName: string,
|
|
204
|
+
input: unknown
|
|
205
|
+
): Promise<PreToolUseDecision> {
|
|
206
|
+
let current = input;
|
|
207
|
+
let changed = false;
|
|
208
|
+
// 各 hook 要追加给模型的话,按 `order` 攒起来(entries 已排过序)。
|
|
209
|
+
const notes: string[] = [];
|
|
210
|
+
for (const { meta, impl } of this.of('preToolUse')) {
|
|
211
|
+
if (meta.tools && !meta.tools.includes(toolName)) continue;
|
|
212
|
+
try {
|
|
213
|
+
const res = await this.withTimeout(meta, () =>
|
|
214
|
+
impl.preToolUse(ctx, toolName, current)
|
|
215
|
+
);
|
|
216
|
+
if (res.action === 'deny') {
|
|
217
|
+
// **拒绝也要把已攒下的话带走**:拦下这次调用的是另一个 hook,而先前
|
|
218
|
+
// 那句提醒说的仍是这一次调用(被拒绝的调用一样计数,那正是判据)。
|
|
219
|
+
// 丢在这里的话,症状是「偏偏在被拦的那次不提醒」。
|
|
220
|
+
return withNotes(res, notes);
|
|
221
|
+
}
|
|
222
|
+
if (res.systemNote) notes.push(res.systemNote);
|
|
223
|
+
if (res.action === 'modifyInput') {
|
|
224
|
+
current = res.input;
|
|
225
|
+
changed = true;
|
|
226
|
+
}
|
|
227
|
+
} catch (err) {
|
|
228
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
229
|
+
if (meta.onError === 'closed') {
|
|
230
|
+
return withNotes({ action: 'deny', reason: 'hook failure' }, notes);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// 同上:显式 changed 标记,防止「改成引用不同但内容恰好 === 的新对象」
|
|
235
|
+
// 或「原地改了同一个对象引用」两种场景被 === 误判。preToolUse 尤其容易
|
|
236
|
+
// 踩后者——hook 拿到的 current 是同一个可变对象引用,若原地 mutate 而不
|
|
237
|
+
// 返回新引用,`current === input` 恒真,会把已生效的 modifyInput 错报
|
|
238
|
+
// 成 continue(下游若按 action 分支决定要不要重新校验/审计,会被漏过)。
|
|
239
|
+
return withNotes(
|
|
240
|
+
changed
|
|
241
|
+
? { action: 'modifyInput', input: current }
|
|
242
|
+
: { action: 'continue' },
|
|
243
|
+
notes
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async runPostToolUse(
|
|
248
|
+
ctx: TurnContext,
|
|
249
|
+
toolName: string,
|
|
250
|
+
outcome: ToolOutcome,
|
|
251
|
+
toolCallId: string
|
|
252
|
+
): Promise<void> {
|
|
253
|
+
for (const { meta, impl } of this.of('postToolUse')) {
|
|
254
|
+
if (meta.tools && !meta.tools.includes(toolName)) continue;
|
|
255
|
+
try {
|
|
256
|
+
await this.withTimeout(meta, () =>
|
|
257
|
+
impl.postToolUse(ctx, toolName, outcome, toolCallId)
|
|
258
|
+
);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* 调模型前腾空间。**单例点**(注册期保证至多一个实现),故这里的循环最多转
|
|
267
|
+
* 一圈;没有实现方时返回 `null` —— 与「不用腾」同一个值,内核两条路径的处理
|
|
268
|
+
* 本来就一样(阈值路径继续发请求,overflow 路径把原始错误原样抛出)。
|
|
269
|
+
*
|
|
270
|
+
* `onError: 'closed'` 在这里的含义是「腾不出来就别再发请求了」:把异常继续
|
|
271
|
+
* 往上抛,由内核的 catch 收成一轮 failed。缺省 `'open'` 则当作「没腾出来」。
|
|
272
|
+
*/
|
|
273
|
+
async runBeforeModelCall(
|
|
274
|
+
ctx: TurnContext,
|
|
275
|
+
pressure: ContextPressure
|
|
276
|
+
): Promise<ContextRelief | null> {
|
|
277
|
+
for (const { meta, impl } of this.of('beforeModelCall')) {
|
|
278
|
+
try {
|
|
279
|
+
return await this.withTimeout(meta, () =>
|
|
280
|
+
impl.beforeModelCall(ctx, pressure)
|
|
281
|
+
);
|
|
282
|
+
} catch (err) {
|
|
283
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
284
|
+
if (meta.onError === 'closed') throw err;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async emit(ctx: TurnContext, phase: ObservePhase): Promise<void> {
|
|
291
|
+
for (const { meta, impl } of this.of('observe')) {
|
|
292
|
+
try {
|
|
293
|
+
await this.withTimeout(meta, () => impl.observe(ctx, phase));
|
|
294
|
+
} catch (err) {
|
|
295
|
+
this.logger.warn(`hook '${meta.name}' failed: ${String(err)}`);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 把攒下的 `systemNote` 合成一条挂到决策上。多个 hook 各说一句时**合成一条**:
|
|
303
|
+
* 它们描述的是同一次调用,分成两条只会在同一份工具结果后面并排出现两段话。
|
|
304
|
+
*/
|
|
305
|
+
function withNotes(res: PreToolUseResult, notes: string[]): PreToolUseDecision {
|
|
306
|
+
return notes.length > 0 ? { ...res, systemNote: notes.join('\n\n') } : res;
|
|
307
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import type { AgentExtensions } from '../agent-extensions.js';
|
|
2
|
+
import type {
|
|
3
|
+
ContextPressure,
|
|
4
|
+
ContextRelief,
|
|
5
|
+
} from '../turn/context-pressure.js';
|
|
6
|
+
|
|
7
|
+
export const TURN_HOOK_POINTS = [
|
|
8
|
+
'beforeTurn',
|
|
9
|
+
'transformContext',
|
|
10
|
+
'reviewOutput',
|
|
11
|
+
'observe',
|
|
12
|
+
'preToolUse',
|
|
13
|
+
'postToolUse',
|
|
14
|
+
'beforeModelCall',
|
|
15
|
+
] as const;
|
|
16
|
+
export type TurnHookPoint = (typeof TURN_HOOK_POINTS)[number];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* **单例 point**:注册期校验至多一个实现,多于一个直接抛。
|
|
20
|
+
*
|
|
21
|
+
* `beforeModelCall` 要替换 `messages` 的头部,而「两个插件先后替换头部」的语义
|
|
22
|
+
* 定义不出来——第二个看到的已经不是原始历史,它算出的切点对不上任何一段真实
|
|
23
|
+
* 前缀。与其定一套顺序规则让人去猜,不如在注册期拒绝。
|
|
24
|
+
*/
|
|
25
|
+
export const SINGLETON_POINTS: readonly TurnHookPoint[] = ['beforeModelCall'];
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `onError` 真正被消费的 point。**其余点上声明 `'closed'` 会被注册期拒绝**。
|
|
29
|
+
*
|
|
30
|
+
* 判据是「这个点崩了之后,还有没有一个『拦下来』的分支可走」:门禁类(放行/
|
|
31
|
+
* 拒绝)有,观测类(observe/postToolUse)与注入类(transformContext)没有——
|
|
32
|
+
* 后者失败的唯一可能反应就是少注入一段文本,此时 `'closed'` 是一句**声明了却
|
|
33
|
+
* 不生效**的话,而那正是本仓最不想留的那种静默。
|
|
34
|
+
*/
|
|
35
|
+
export const ONERROR_HONORED_POINTS: readonly TurnHookPoint[] = [
|
|
36
|
+
'beforeTurn',
|
|
37
|
+
'reviewOutput',
|
|
38
|
+
'preToolUse',
|
|
39
|
+
'beforeModelCall',
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
export interface TurnHookMeta {
|
|
43
|
+
name: string;
|
|
44
|
+
point: TurnHookPoint;
|
|
45
|
+
/** 升序串行。 */
|
|
46
|
+
order: number;
|
|
47
|
+
/** 仅 preToolUse/postToolUse 可设:限定作用的工具名。 */
|
|
48
|
+
tools?: readonly string[];
|
|
49
|
+
/** 'open' = 崩了当 no-op(默认);鉴权类必须 'closed'。 */
|
|
50
|
+
onError: 'open' | 'closed';
|
|
51
|
+
/**
|
|
52
|
+
* 这个 hook 的超时,缺省 `HOOK_TIMEOUT_MS`(5s)。
|
|
53
|
+
*
|
|
54
|
+
* **给它一个逃生口,而不是把默认值调大**:5s 对门禁/观测类是合适的保护,
|
|
55
|
+
* 而 `beforeModelCall` 的实现要跑一次完整的 LLM 调用(压缩摘要,分钟级)。
|
|
56
|
+
* 把默认值抬到分钟级,等于让其余每个点的超时保护一起松掉——那些点上一个
|
|
57
|
+
* 卡死的 hook 会把整轮对话拖住,正是这道保护要防的事。**慢的那个自己声明。**
|
|
58
|
+
*/
|
|
59
|
+
timeoutMs?: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface TurnContext {
|
|
63
|
+
streamId: string;
|
|
64
|
+
runId: string;
|
|
65
|
+
/** 本轮跑在哪个会话上——往它的 inbox 回投(`todo-settle.hook.ts`)要用。 */
|
|
66
|
+
sessionId: string;
|
|
67
|
+
agentId: string;
|
|
68
|
+
/** 宿主经 `AgentExtensions` 加的 agent 字段(如跑的是哪种形式的哪一个)。 */
|
|
69
|
+
agent: AgentExtensions;
|
|
70
|
+
userId: string | null;
|
|
71
|
+
depth: number;
|
|
72
|
+
/**
|
|
73
|
+
* 本轮 drain 到的消息(只露 `metadata`)——「这一轮是被什么叫起来的」。
|
|
74
|
+
*
|
|
75
|
+
* 内核在首次 drain 之后填进来,之前是空数组;插件按 `metadata.kind` 分辨
|
|
76
|
+
* 「这一轮是在回应平台自己插的提醒」(防止一条提醒催出下一条)。**只给
|
|
77
|
+
* metadata 不给正文**:正文是用户的话,观测类插件不该顺手读到它。
|
|
78
|
+
*/
|
|
79
|
+
inbound: Array<{ metadata?: Record<string, unknown> }>;
|
|
80
|
+
/**
|
|
81
|
+
* **插件的 per-turn 状态槽**,键用插件自己的 `name`(取用见 `turnStateOf`)。
|
|
82
|
+
*
|
|
83
|
+
* 这个 Map 是 `TurnRun` 的一个字段,生命周期跟着那个对象走——插件本身是单例
|
|
84
|
+
* provider,自己攒 `Map<runId, T>` 就要在终态里记得清理,而那条路要三个 phase
|
|
85
|
+
* 全覆盖、漏一个就漏一类会话(且症状是内存慢慢涨,没有人会看见)。这里**结构上
|
|
86
|
+
* 不可能泄漏**。
|
|
87
|
+
*/
|
|
88
|
+
turnState: Map<string, unknown>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 取(没有就建)本插件在这一轮的状态。
|
|
93
|
+
*
|
|
94
|
+
* 断言收在这一处:`turnState` 是一张异构表(各插件的状态混在一起),读出来时
|
|
95
|
+
* 类型系统重建不出「这个键上放的是 T」——同 `TurnHookAt` 那条理由。**键必须用
|
|
96
|
+
* 插件自己的 `name`**(registry 保证全局唯一),别用点名或类型名。
|
|
97
|
+
*/
|
|
98
|
+
export function turnStateOf<T>(
|
|
99
|
+
ctx: TurnContext,
|
|
100
|
+
key: string,
|
|
101
|
+
create: () => T
|
|
102
|
+
): T {
|
|
103
|
+
const existing = ctx.turnState.get(key);
|
|
104
|
+
if (existing !== undefined) return existing as T;
|
|
105
|
+
const created = create();
|
|
106
|
+
ctx.turnState.set(key, created);
|
|
107
|
+
return created;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* beforeTurn 返回 reject 时 **必须** 带 reply 文案 —— 否则 SSE 流会挂死
|
|
112
|
+
* (smart-property 的 human-takeover-gate 踩过,它因此只敢在微信渠道启用)。
|
|
113
|
+
* registry 层有断言强制。
|
|
114
|
+
*/
|
|
115
|
+
export type BeforeTurnResult =
|
|
116
|
+
{ action: 'continue' } | { action: 'reject'; reply: string };
|
|
117
|
+
|
|
118
|
+
export type ReviewOutputResult =
|
|
119
|
+
| { action: 'keep' }
|
|
120
|
+
| { action: 'rewrite'; text: string }
|
|
121
|
+
| { action: 'block'; reply: string };
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* 一次工具调用的结果切面,交给 `postToolUse`。
|
|
125
|
+
*
|
|
126
|
+
* **`output` 是三处同源的那份文本**(SSE 事件 / 落库 part / 模型上下文),不是
|
|
127
|
+
* 工具内部的结构化返回值——需要程序判据的读 `ref`,别从这段文本里抠(它是随时
|
|
128
|
+
* 会改的文案)。`isError` 与落库用的是同一个判据(`ToolRegistry.didFail`,
|
|
129
|
+
* 两层信封都看),观测方因此不会看到一个与界面上颜色相反的成败。
|
|
130
|
+
*/
|
|
131
|
+
export interface ToolOutcome {
|
|
132
|
+
output: string;
|
|
133
|
+
isError: boolean;
|
|
134
|
+
ref?: Record<string, unknown>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* `preToolUse` 的产出。
|
|
139
|
+
*
|
|
140
|
+
* `systemNote` = 平台要针对**这一次调用**追加给模型的一句话(当前唯一来源:
|
|
141
|
+
* 重复调用提醒)。内核把它放进那次 tool-result part 的 `systemNote` 字段,
|
|
142
|
+
* 投影期才拼进文本——**插件只决定注入什么,不决定怎么落库**。
|
|
143
|
+
*
|
|
144
|
+
* 挂在这里而不是另开一个步末的注入点,是因为**归属**:劝告说的就是这一次调用,
|
|
145
|
+
* 挂在它的结果上,一步里多个调用各自撞线时不必合成一条,也不需要「让提醒的 seq
|
|
146
|
+
* 落在它引发的那段文字之前」那套切段规程。
|
|
147
|
+
*/
|
|
148
|
+
export type PreToolUseResult =
|
|
149
|
+
| { action: 'continue'; systemNote?: string }
|
|
150
|
+
| { action: 'modifyInput'; input: unknown; systemNote?: string }
|
|
151
|
+
| { action: 'deny'; reason: string };
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* registry 把各 hook 的产出聚合之后交给内核的那份。
|
|
155
|
+
*
|
|
156
|
+
* 与 `PreToolUseResult` 的唯一差别是 **deny 也可能带 `systemNote`**:拦下这次
|
|
157
|
+
* 调用的是某个 hook,而先前另一个 hook 攒的那句提醒说的仍是这一次调用。单个
|
|
158
|
+
* hook 的 deny 不必自己带(拒绝理由已经进了结果正文)。
|
|
159
|
+
*/
|
|
160
|
+
export type PreToolUseDecision = PreToolUseResult & { systemNote?: string };
|
|
161
|
+
|
|
162
|
+
export type ObservePhase = 'started' | 'rejected' | 'replied' | 'failed';
|
|
163
|
+
|
|
164
|
+
export interface TurnHookImpl {
|
|
165
|
+
beforeTurn?(ctx: TurnContext): Promise<BeforeTurnResult>;
|
|
166
|
+
transformContext?(ctx: TurnContext): Promise<{ appendSystem?: string }>;
|
|
167
|
+
reviewOutput?(ctx: TurnContext, text: string): Promise<ReviewOutputResult>;
|
|
168
|
+
observe?(ctx: TurnContext, phase: ObservePhase): Promise<void>;
|
|
169
|
+
preToolUse?(
|
|
170
|
+
ctx: TurnContext,
|
|
171
|
+
toolName: string,
|
|
172
|
+
input: unknown
|
|
173
|
+
): Promise<PreToolUseResult>;
|
|
174
|
+
/**
|
|
175
|
+
* `toolCallId` 放在最后、不进 `ToolOutcome`:outcome 会被原样铺进事件与落库
|
|
176
|
+
* part,多一个键就多落一份。
|
|
177
|
+
*/
|
|
178
|
+
postToolUse?(
|
|
179
|
+
ctx: TurnContext,
|
|
180
|
+
toolName: string,
|
|
181
|
+
outcome: ToolOutcome,
|
|
182
|
+
toolCallId: string
|
|
183
|
+
): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* 调模型前腾空间。返回 `null` = 不用动。**至多一个实现**(见 `SINGLETON_POINTS`)。
|
|
186
|
+
*
|
|
187
|
+
* 内核只测压与替换头部;「阈值多少、模型容量多大、腾出来的是什么」全在实现方。
|
|
188
|
+
* 两条调用路径的差别不许合并(阈值路径的 null = 不用腾;`pressure.overflowed`
|
|
189
|
+
* 路径的 null = 腾不出来,把 provider 的原始错误原样抛出)。
|
|
190
|
+
*/
|
|
191
|
+
beforeModelCall?(
|
|
192
|
+
ctx: TurnContext,
|
|
193
|
+
pressure: ContextPressure
|
|
194
|
+
): Promise<ContextRelief | null>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* 「注册在 point P 上」的 impl 视图:P 那个方法一定在。
|
|
199
|
+
*
|
|
200
|
+
* 注册期唯一的准入条件就是 `typeof impl[meta.point] === 'function'`(见
|
|
201
|
+
* `TurnHookRegistry.register`),但 `TurnHookImpl` 的成员全是可选的,类型系统
|
|
202
|
+
* 重建不出这条已经验过的不变量——六个 run 方法因此各写过一个 `impl.xxx!(…)`。
|
|
203
|
+
* `!` 的问题不在于它丑:它**不检查过滤出来的 point 与调用的方法是不是同一个**,
|
|
204
|
+
* `of('observe')` 的循环里写 `impl.beforeTurn!(ctx)` 一样编译通过,要到运行时
|
|
205
|
+
* 才炸。收窄成这个类型之后,那种错配是编译错误。
|
|
206
|
+
*/
|
|
207
|
+
export type TurnHookAt<P extends TurnHookPoint> = TurnHookImpl &
|
|
208
|
+
Required<Pick<TurnHookImpl, P>>;
|
|
209
|
+
|
|
210
|
+
export const HOOK_TIMEOUT_MS = 5_000;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
|
|
3
|
+
import type { ImageSource } from '../ports/image-source.js';
|
|
4
|
+
|
|
5
|
+
/** 递送用的字节:带上了 sha256(缓存键)。 */
|
|
6
|
+
export interface LoadedImage {
|
|
7
|
+
data: Uint8Array;
|
|
8
|
+
contentType: string;
|
|
9
|
+
contentHash: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** 进程内字节缓存的缺省上限。 */
|
|
13
|
+
export const DEFAULT_BYTE_CACHE_LIMIT = 256 * 1024 * 1024;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 按 `src` 缓存字节的 LRU:同一条会话每一步都要重发同一批图。`src` 内容不可变,所以永不
|
|
17
|
+
* 失效,只按体积淘汰(至少留一张)。同一 `src` 的并发读合流成一次。
|
|
18
|
+
*/
|
|
19
|
+
export class ImageByteCache {
|
|
20
|
+
private readonly entries = new Map<string, LoadedImage>();
|
|
21
|
+
private readonly inflight = new Map<string, Promise<LoadedImage>>();
|
|
22
|
+
private total = 0;
|
|
23
|
+
|
|
24
|
+
constructor(
|
|
25
|
+
private readonly source: ImageSource,
|
|
26
|
+
private readonly limitBytes = DEFAULT_BYTE_CACHE_LIMIT
|
|
27
|
+
) {}
|
|
28
|
+
|
|
29
|
+
load(src: string): Promise<LoadedImage> {
|
|
30
|
+
const hit = this.entries.get(src);
|
|
31
|
+
if (hit) {
|
|
32
|
+
this.entries.delete(src);
|
|
33
|
+
this.entries.set(src, hit);
|
|
34
|
+
return Promise.resolve(hit);
|
|
35
|
+
}
|
|
36
|
+
const pending = this.inflight.get(src);
|
|
37
|
+
if (pending) return pending;
|
|
38
|
+
const p = this.read(src).finally(() => this.inflight.delete(src));
|
|
39
|
+
this.inflight.set(src, p);
|
|
40
|
+
return p;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
private async read(src: string): Promise<LoadedImage> {
|
|
44
|
+
const raw = await this.source.read(src);
|
|
45
|
+
const entry: LoadedImage = {
|
|
46
|
+
data: raw.data,
|
|
47
|
+
contentType: raw.contentType,
|
|
48
|
+
contentHash:
|
|
49
|
+
raw.contentHash ?? createHash('sha256').update(raw.data).digest('hex'),
|
|
50
|
+
};
|
|
51
|
+
this.entries.set(src, entry);
|
|
52
|
+
this.total += entry.data.byteLength;
|
|
53
|
+
while (this.total > this.limitBytes && this.entries.size > 1) {
|
|
54
|
+
const oldest = this.entries.keys().next().value as string;
|
|
55
|
+
const evicted = this.entries.get(oldest);
|
|
56
|
+
this.entries.delete(oldest);
|
|
57
|
+
if (evicted) this.total -= evicted.data.byteLength;
|
|
58
|
+
}
|
|
59
|
+
return entry;
|
|
60
|
+
}
|
|
61
|
+
}
|