@namzu/sdk 51.1.0 → 52.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/CHANGELOG.md +119 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +2 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +80 -4
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/authorization/protected-paths.d.ts +36 -0
- package/dist/authorization/protected-paths.d.ts.map +1 -0
- package/dist/authorization/protected-paths.js +107 -0
- package/dist/authorization/protected-paths.js.map +1 -0
- package/dist/authorization/readonly-commands.d.ts +39 -0
- package/dist/authorization/readonly-commands.d.ts.map +1 -0
- package/dist/authorization/readonly-commands.js +797 -0
- package/dist/authorization/readonly-commands.js.map +1 -0
- package/dist/authorization/readonly-flags.d.ts +19 -0
- package/dist/authorization/readonly-flags.d.ts.map +1 -0
- package/dist/authorization/readonly-flags.js +178 -0
- package/dist/authorization/readonly-flags.js.map +1 -0
- package/dist/authorization/removal.d.ts +52 -0
- package/dist/authorization/removal.d.ts.map +1 -0
- package/dist/authorization/removal.js +283 -0
- package/dist/authorization/removal.js.map +1 -0
- package/dist/authorization/shell-paths.d.ts +66 -0
- package/dist/authorization/shell-paths.d.ts.map +1 -0
- package/dist/authorization/shell-paths.js +697 -0
- package/dist/authorization/shell-paths.js.map +1 -0
- package/dist/bridge/acp/update.d.ts.map +1 -1
- package/dist/bridge/acp/update.js +1 -0
- package/dist/bridge/acp/update.js.map +1 -1
- package/dist/compaction/context-window.d.ts +24 -0
- package/dist/compaction/context-window.d.ts.map +1 -1
- package/dist/compaction/context-window.js +34 -0
- package/dist/compaction/context-window.js.map +1 -1
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +10 -2
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/plan.d.ts +4 -0
- package/dist/compaction/plan.d.ts.map +1 -1
- package/dist/compaction/plan.js +6 -2
- package/dist/compaction/plan.js.map +1 -1
- package/dist/compaction/verifier.d.ts +11 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +75 -23
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +55 -7
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +23 -2
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
- package/dist/connector/web/guarded-fetch.js +24 -0
- package/dist/connector/web/guarded-fetch.js.map +1 -1
- package/dist/connector/web/index.d.ts +1 -1
- package/dist/connector/web/index.d.ts.map +1 -1
- package/dist/connector/web/page.d.ts +59 -0
- package/dist/connector/web/page.d.ts.map +1 -0
- package/dist/connector/web/page.js +112 -0
- package/dist/connector/web/page.js.map +1 -0
- package/dist/connector/web/types.d.ts +47 -0
- package/dist/connector/web/types.d.ts.map +1 -1
- package/dist/connector/web/types.js.map +1 -1
- package/dist/constants/continuation.d.ts +8 -0
- package/dist/constants/continuation.d.ts.map +1 -1
- package/dist/constants/continuation.js +8 -0
- package/dist/constants/continuation.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +1 -1
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +1 -1
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +2 -2
- package/dist/manager/agent/lifecycle.d.ts +7 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +11 -2
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +4 -2
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts +6 -0
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +98 -114
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/shell-hook.d.ts +90 -22
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +434 -41
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/provider/capabilities.d.ts +9 -0
- package/dist/provider/capabilities.d.ts.map +1 -1
- package/dist/provider/capabilities.js +15 -0
- package/dist/provider/capabilities.js.map +1 -1
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +13 -0
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +10 -0
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/idle-timeout.d.ts.map +1 -1
- package/dist/provider/idle-timeout.js +5 -0
- package/dist/provider/idle-timeout.js.map +1 -1
- package/dist/provider/image-input-refusal.d.ts +9 -0
- package/dist/provider/image-input-refusal.d.ts.map +1 -0
- package/dist/provider/image-input-refusal.js +54 -0
- package/dist/provider/image-input-refusal.js.map +1 -0
- package/dist/provider/index.d.ts +2 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +2 -1
- package/dist/provider/index.js.map +1 -1
- package/dist/provider/retry.d.ts +7 -2
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +38 -4
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +5 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -2
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +21 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +15 -2
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/deferred-sandbox.d.ts +54 -0
- package/dist/runtime/query/deferred-sandbox.d.ts.map +1 -0
- package/dist/runtime/query/deferred-sandbox.js +75 -0
- package/dist/runtime/query/deferred-sandbox.js.map +1 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +74 -13
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +51 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +214 -9
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/finalize-turn.d.ts +6 -0
- package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
- package/dist/runtime/query/finalize-turn.js +1 -1
- package/dist/runtime/query/finalize-turn.js.map +1 -1
- package/dist/runtime/query/index.d.ts +29 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +108 -28
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +57 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +380 -28
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +61 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +94 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts +42 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +203 -23
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +10 -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 +165 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts +21 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.js +53 -7
- package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -1
- package/dist/runtime/query/iteration/request-repair.d.ts +108 -0
- package/dist/runtime/query/iteration/request-repair.d.ts.map +1 -0
- package/dist/runtime/query/iteration/request-repair.js +593 -0
- package/dist/runtime/query/iteration/request-repair.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +6 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +2 -2
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/path-escalation.d.ts +36 -0
- package/dist/runtime/query/path-escalation.d.ts.map +1 -0
- package/dist/runtime/query/path-escalation.js +184 -0
- package/dist/runtime/query/path-escalation.js.map +1 -0
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +8 -0
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/reminders.d.ts +35 -0
- package/dist/runtime/query/reminders.d.ts.map +1 -0
- package/dist/runtime/query/reminders.js +7 -0
- package/dist/runtime/query/reminders.js.map +1 -0
- package/dist/runtime/query/request-rich-content.d.ts +5 -1
- package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +67 -25
- package/dist/runtime/query/request-rich-content.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +26 -0
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +26 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +79 -5
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts +46 -0
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +85 -1
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +1 -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/session/paths.d.ts +5 -1
- package/dist/session/paths.d.ts.map +1 -1
- package/dist/session/paths.js +8 -0
- package/dist/session/paths.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +2 -2
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/memory/markdown.d.ts.map +1 -1
- package/dist/store/memory/markdown.js +2 -2
- package/dist/store/memory/markdown.js.map +1 -1
- package/dist/store/memory/naming.d.ts +9 -0
- package/dist/store/memory/naming.d.ts.map +1 -1
- package/dist/store/memory/naming.js +12 -0
- package/dist/store/memory/naming.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +30 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +170 -26
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +1 -1
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +12 -5
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
- package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
- package/dist/tools/builtins/content-fingerprint.js +4 -0
- package/dist/tools/builtins/content-fingerprint.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +60 -18
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts +8 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +152 -33
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +1 -0
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +44 -5
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/read-render.d.ts +52 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -1
- package/dist/tools/builtins/read-render.js +143 -0
- package/dist/tools/builtins/read-render.js.map +1 -1
- package/dist/tools/builtins/web.d.ts +13 -2
- package/dist/tools/builtins/web.d.ts.map +1 -1
- package/dist/tools/builtins/web.js +225 -32
- package/dist/tools/builtins/web.js.map +1 -1
- package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
- package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
- package/dist/tools/coordinator/ask-user-question.js +256 -92
- package/dist/tools/coordinator/ask-user-question.js.map +1 -1
- package/dist/tools/coordinator/question-options.d.ts +1 -0
- package/dist/tools/coordinator/question-options.d.ts.map +1 -1
- package/dist/tools/coordinator/question-options.js +6 -1
- package/dist/tools/coordinator/question-options.js.map +1 -1
- package/dist/tools/defineTool.d.ts +4 -1
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +6 -1
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/paths.d.ts +6 -0
- package/dist/tools/paths.d.ts.map +1 -1
- package/dist/tools/paths.js +1 -1
- package/dist/tools/paths.js.map +1 -1
- package/dist/types/agent/query.d.ts +3 -0
- package/dist/types/agent/query.d.ts.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 +104 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +2 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +93 -19
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +3 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/errors.d.ts +19 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +54 -5
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/provider/interface.d.ts +12 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +1 -1
- package/dist/types/session/config.d.ts +14 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +27 -4
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +9 -9
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +1 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/stop-reason.d.ts +5 -0
- package/dist/types/session/stop-reason.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +52 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +4 -2
- package/src/agents/QueryAgent.ts +5 -1
- package/src/agents/SupervisorAgent.ts +1 -1
- package/src/agents/examples/SupervisorAgent.ts +217 -106
- package/src/agents/index.ts +1 -1
- package/src/authorization/protected-paths.ts +110 -0
- package/src/authorization/readonly-commands.ts +771 -0
- package/src/authorization/readonly-flags.ts +196 -0
- package/src/authorization/removal.ts +326 -0
- package/src/authorization/shell-paths.ts +927 -0
- package/src/bridge/acp/update.ts +1 -0
- package/src/compaction/context-window.ts +41 -0
- package/src/compaction/manual.ts +10 -2
- package/src/compaction/plan.ts +11 -2
- package/src/compaction/verifier.ts +84 -22
- package/src/config/runtime.ts +23 -2
- package/src/connector/web/guarded-fetch.ts +26 -0
- package/src/connector/web/index.ts +3 -0
- package/src/connector/web/page.ts +130 -0
- package/src/connector/web/types.ts +47 -0
- package/src/constants/continuation.ts +10 -0
- package/src/constants/plugin/index.ts +1 -1
- package/src/manager/agent/lifecycle.ts +12 -4
- package/src/manager/session/turn-recorder.ts +10 -2
- package/src/plugin/index.ts +1 -0
- package/src/plugin/lifecycle.ts +107 -119
- package/src/plugin/shell-hook.ts +477 -43
- package/src/provider/capabilities.ts +17 -0
- package/src/provider/errors.ts +13 -0
- package/src/provider/fallback.ts +10 -0
- package/src/provider/idle-timeout.ts +5 -0
- package/src/provider/image-input-refusal.ts +54 -0
- package/src/provider/index.ts +2 -0
- package/src/provider/retry.ts +42 -4
- package/src/public-runtime.ts +11 -0
- package/src/public-types.ts +4 -0
- package/src/runtime/jobs/registry.ts +39 -2
- package/src/runtime/query/deferred-sandbox.ts +110 -0
- package/src/runtime/query/executor/tool-call-admission.ts +79 -13
- package/src/runtime/query/executor.ts +267 -11
- package/src/runtime/query/finalize-turn.ts +7 -1
- package/src/runtime/query/index.ts +169 -52
- package/src/runtime/query/iteration/index.ts +439 -35
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +108 -0
- package/src/runtime/query/iteration/phases/compaction.ts +241 -38
- package/src/runtime/query/iteration/phases/context.ts +11 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
- package/src/runtime/query/iteration/provider-rejected-image.ts +71 -5
- package/src/runtime/query/iteration/request-repair.ts +743 -0
- package/src/runtime/query/iteration/stream-turn.ts +11 -1
- package/src/runtime/query/path-escalation.ts +204 -0
- package/src/runtime/query/plugin-hooks.ts +8 -0
- package/src/runtime/query/reminders.ts +45 -0
- package/src/runtime/query/request-rich-content.ts +77 -27
- package/src/runtime/query/resume-pending.ts +27 -0
- package/src/runtime/query/review-policy.ts +106 -5
- package/src/runtime/query/tool-output-budget.ts +115 -1
- package/src/runtime/query/tooling.ts +6 -0
- package/src/session/paths.ts +11 -1
- package/src/store/memory/disk.ts +2 -1
- package/src/store/memory/markdown.ts +2 -1
- package/src/store/memory/naming.ts +13 -0
- package/src/tools/builtins/bash.ts +203 -34
- package/src/tools/builtins/computer-use.ts +13 -4
- package/src/tools/builtins/content-fingerprint.ts +5 -0
- package/src/tools/builtins/edit.ts +69 -19
- package/src/tools/builtins/grep.ts +159 -38
- package/src/tools/builtins/read-file.ts +48 -5
- package/src/tools/builtins/read-render.ts +193 -0
- package/src/tools/builtins/web.ts +258 -33
- package/src/tools/coordinator/ask-user-question.ts +309 -103
- package/src/tools/coordinator/question-options.ts +8 -1
- package/src/tools/defineTool.ts +11 -2
- package/src/tools/paths.ts +1 -1
- package/src/types/agent/query.ts +3 -0
- package/src/types/errors/catalog.ts +1 -1
- package/src/types/hitl/index.ts +104 -0
- package/src/types/message/index.ts +2 -0
- package/src/types/plugin/index.ts +101 -18
- package/src/types/provider/errors.ts +66 -5
- package/src/types/provider/interface.ts +13 -0
- package/src/types/provider/stream.ts +1 -1
- package/src/types/session/config.ts +14 -0
- package/src/types/session/events.ts +32 -4
- package/src/types/session/records.ts +1 -0
- package/src/types/session/stop-reason.ts +5 -0
- package/src/types/tool/index.ts +53 -2
package/src/bridge/acp/update.ts
CHANGED
|
@@ -105,6 +105,47 @@ export function lookupContextWindow(model: string | undefined): number | undefin
|
|
|
105
105
|
return undefined
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
+
/** Output room the trigger keeps free: the model's output cap, at most this many tokens. */
|
|
109
|
+
export const TRIGGER_OUTPUT_RESERVE_CAP_TOKENS = 20_000
|
|
110
|
+
/** Extra headroom below the reserved window before a summary is due. */
|
|
111
|
+
export const TRIGGER_BUFFER_TOKENS = 13_000
|
|
112
|
+
/**
|
|
113
|
+
* The derived trigger never drops below this fraction of the window. The
|
|
114
|
+
* reference subtracts 33,000 tokens outright; on a window under about 66,000
|
|
115
|
+
* that is most of it (or more than all of it), and a summary on every
|
|
116
|
+
* iteration is not a trigger.
|
|
117
|
+
*/
|
|
118
|
+
export const MIN_DERIVED_TRIGGER_FRACTION = 0.5
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* The fraction of the window at which older history is summarised.
|
|
122
|
+
*
|
|
123
|
+
* A host that set `triggerThreshold` gets exactly that. Otherwise the
|
|
124
|
+
* trigger is the one Claude Code uses: the window, less room kept for the
|
|
125
|
+
* reply (the model's output cap, at most 20,000 tokens), less a 13,000-token
|
|
126
|
+
* buffer. On a 200,000-token window that is 167,000 tokens (83.5%); on a
|
|
127
|
+
* 1,000,000-token window, 967,000 (96.7%).
|
|
128
|
+
*
|
|
129
|
+
* `maxOutputTokens` is what the driver declares for the model; absent, the
|
|
130
|
+
* full 20,000 is reserved.
|
|
131
|
+
*/
|
|
132
|
+
export function resolveTriggerThreshold(
|
|
133
|
+
configured: number | undefined,
|
|
134
|
+
windowTokens: number,
|
|
135
|
+
maxOutputTokens?: number,
|
|
136
|
+
): number {
|
|
137
|
+
if (configured !== undefined) return configured
|
|
138
|
+
if (!(windowTokens > 0)) return MIN_DERIVED_TRIGGER_FRACTION
|
|
139
|
+
const reserve = Math.min(
|
|
140
|
+
maxOutputTokens !== undefined && maxOutputTokens > 0
|
|
141
|
+
? maxOutputTokens
|
|
142
|
+
: TRIGGER_OUTPUT_RESERVE_CAP_TOKENS,
|
|
143
|
+
TRIGGER_OUTPUT_RESERVE_CAP_TOKENS,
|
|
144
|
+
)
|
|
145
|
+
const fraction = (windowTokens - reserve - TRIGGER_BUFFER_TOKENS) / windowTokens
|
|
146
|
+
return Math.min(1, Math.max(MIN_DERIVED_TRIGGER_FRACTION, fraction))
|
|
147
|
+
}
|
|
148
|
+
|
|
108
149
|
export interface ResolvedContextWindow {
|
|
109
150
|
readonly tokens: number
|
|
110
151
|
/**
|
package/src/compaction/manual.ts
CHANGED
|
@@ -198,7 +198,11 @@ export async function compactNow(input: CompactNowInput): Promise<CompactionResu
|
|
|
198
198
|
usage = accumulateTokenUsage(usage, reported)
|
|
199
199
|
},
|
|
200
200
|
input.model ?? '',
|
|
201
|
-
{
|
|
201
|
+
{
|
|
202
|
+
signal: input.signal,
|
|
203
|
+
streamIdleTimeoutMs: input.streamIdleTimeoutMs,
|
|
204
|
+
instructions: input.instructions,
|
|
205
|
+
},
|
|
202
206
|
)
|
|
203
207
|
|
|
204
208
|
const { messages, summary } = splice(preservedSystem, body, retainedOlder, plan.recentMessages)
|
|
@@ -281,7 +285,11 @@ export async function compactRegion(input: CompactRegionInput): Promise<Compacti
|
|
|
281
285
|
usage = accumulateTokenUsage(usage, reported)
|
|
282
286
|
},
|
|
283
287
|
input.model ?? '',
|
|
284
|
-
{
|
|
288
|
+
{
|
|
289
|
+
signal: input.signal,
|
|
290
|
+
streamIdleTimeoutMs: input.streamIdleTimeoutMs,
|
|
291
|
+
instructions: input.instructions,
|
|
292
|
+
},
|
|
285
293
|
)
|
|
286
294
|
|
|
287
295
|
// Same cross-run ownership as compactNow: a selected region has been
|
package/src/compaction/plan.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CompactionConfig } from '../config/runtime.js'
|
|
2
2
|
import type { Message } from '../types/message/index.js'
|
|
3
|
+
import { resolveTriggerThreshold } from './context-window.js'
|
|
3
4
|
import { findSafeTrimIndex } from './dangling.js'
|
|
4
5
|
import { buildGoal } from './salience/goal.js'
|
|
5
6
|
import { scoreMessages } from './salience/score.js'
|
|
@@ -64,6 +65,8 @@ export interface CompactionPlanInput {
|
|
|
64
65
|
readonly messages: readonly Message[]
|
|
65
66
|
readonly config: CompactionConfig
|
|
66
67
|
readonly contextWindowTokens: number
|
|
68
|
+
/** The model's output cap, when known; sizes the derived trigger. */
|
|
69
|
+
readonly maxOutputTokens?: number
|
|
67
70
|
readonly estimatedTokens: number
|
|
68
71
|
readonly force?: boolean
|
|
69
72
|
/**
|
|
@@ -137,6 +140,8 @@ export function planSalienceWorkingSet(input: {
|
|
|
137
140
|
readonly messages: readonly Message[]
|
|
138
141
|
readonly config: CompactionConfig
|
|
139
142
|
readonly contextWindowTokens: number
|
|
143
|
+
/** The model's output cap, when known; sizes the derived trigger. */
|
|
144
|
+
readonly maxOutputTokens?: number
|
|
140
145
|
readonly estimatedTokens: number
|
|
141
146
|
readonly openTasks?: readonly string[]
|
|
142
147
|
}): Extract<CompactionPlan, { kind: 'cleared' }> & { readonly working: WorkingSetPlan } {
|
|
@@ -161,7 +166,9 @@ export function planSalienceWorkingSet(input: {
|
|
|
161
166
|
stubbedCount: working.stubbedCount,
|
|
162
167
|
charsReclaimed: working.charsReclaimed,
|
|
163
168
|
reclaimedTokens: working.reclaimedTokens,
|
|
164
|
-
reliefWasEnough:
|
|
169
|
+
reliefWasEnough:
|
|
170
|
+
(estimatedTokens - working.reclaimedTokens) / budget <
|
|
171
|
+
resolveTriggerThreshold(config.triggerThreshold, budget, input.maxOutputTokens),
|
|
165
172
|
working,
|
|
166
173
|
}
|
|
167
174
|
}
|
|
@@ -189,7 +196,9 @@ export function planCompaction(input: CompactionPlanInput): CompactionPlan {
|
|
|
189
196
|
// would declare success after clearing one result and hand back a
|
|
190
197
|
// history that overflows again on the retry.
|
|
191
198
|
const reliefWasEnough =
|
|
192
|
-
!input.force &&
|
|
199
|
+
!input.force &&
|
|
200
|
+
(estimatedTokens - reclaimedTokens) / budget <
|
|
201
|
+
resolveTriggerThreshold(config.triggerThreshold, budget, input.maxOutputTokens)
|
|
193
202
|
return {
|
|
194
203
|
kind: 'cleared',
|
|
195
204
|
messages: edit.messages,
|
|
@@ -3,6 +3,7 @@ import { collectChatCompletion } from '../provider/collect-chat-completion.js'
|
|
|
3
3
|
import { resolveStreamIdleTimeoutMs, withStreamIdleTimeout } from '../provider/idle-timeout.js'
|
|
4
4
|
import type { TokenUsage } from '../types/common/index.js'
|
|
5
5
|
import type { Message, MessageAttachment, ToolResultBlock } from '../types/message/index.js'
|
|
6
|
+
import { classifyProviderError } from '../types/provider/errors.js'
|
|
6
7
|
import type { LLMProvider } from '../types/provider/interface.js'
|
|
7
8
|
import type { WorkingStateManager } from './manager.js'
|
|
8
9
|
import { serializeState } from './serializer.js'
|
|
@@ -85,6 +86,23 @@ function* conversationExcerpt(messages: readonly Message[]): Generator<string> {
|
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
|
|
89
|
+
/** Verifier requests dropped to a shorter excerpt before the extracted state stands alone. */
|
|
90
|
+
const MAX_VERIFIER_HEAD_TRUNCATIONS = 3
|
|
91
|
+
|
|
92
|
+
/** What stands where the older half of a verifier excerpt was cut. */
|
|
93
|
+
const EXCERPT_TRUNCATED_PLACEHOLDER = '[earlier conversation truncated for compaction retry]'
|
|
94
|
+
|
|
95
|
+
/** Drop the older half of an excerpt, keeping the newer half behind a placeholder. */
|
|
96
|
+
function truncateExcerptHead(excerpt: string): string {
|
|
97
|
+
const body = excerpt.startsWith(EXCERPT_TRUNCATED_PLACEHOLDER)
|
|
98
|
+
? excerpt.slice(EXCERPT_TRUNCATED_PLACEHOLDER.length).trimStart()
|
|
99
|
+
: excerpt
|
|
100
|
+
let kept = body.slice(Math.floor(body.length / 2))
|
|
101
|
+
const first = kept.charCodeAt(0)
|
|
102
|
+
if (first >= 0xdc00 && first <= 0xdfff) kept = kept.slice(1)
|
|
103
|
+
return `${EXCERPT_TRUNCATED_PLACEHOLDER}\n\n${kept}`
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
function truncateMessages(messages: readonly Message[], budget: number): string {
|
|
89
107
|
if (budget <= 0) return ''
|
|
90
108
|
const parts: string[] = []
|
|
@@ -127,6 +145,26 @@ export interface CompactionVerificationOptions {
|
|
|
127
145
|
* only for explicit unbounded compatibility.
|
|
128
146
|
*/
|
|
129
147
|
readonly streamIdleTimeoutMs?: number
|
|
148
|
+
/**
|
|
149
|
+
* What the person asked the summary to pay attention to, in their words
|
|
150
|
+
* (`/compact keep the failing test names`). Given to the verifier model as
|
|
151
|
+
* a request to add whatever of it the extracted state lacks. Naming
|
|
152
|
+
* instructions makes the model step run even when `llmVerification` is off,
|
|
153
|
+
* because it is the only step that can act on free text.
|
|
154
|
+
*/
|
|
155
|
+
readonly instructions?: string
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Longest instruction text passed on; a pasted page is not an instruction. */
|
|
159
|
+
export const MAX_COMPACTION_INSTRUCTIONS_CHARS = 2_000
|
|
160
|
+
|
|
161
|
+
function withInstructions(prompt: string, instructions: string | undefined): string {
|
|
162
|
+
const text = instructions?.trim().slice(0, MAX_COMPACTION_INSTRUCTIONS_CHARS)
|
|
163
|
+
if (!text) return prompt
|
|
164
|
+
return `${prompt}
|
|
165
|
+
|
|
166
|
+
The person who asked for this summary also said what it must keep: ${text}
|
|
167
|
+
Treat that as part of "important information". If the structured state lacks something it asks for and the excerpt has it, list that too.`
|
|
130
168
|
}
|
|
131
169
|
|
|
132
170
|
export async function buildVerifiedSummary(
|
|
@@ -157,6 +195,7 @@ export async function buildVerifiedSummary(
|
|
|
157
195
|
onUsage,
|
|
158
196
|
model,
|
|
159
197
|
options.signal,
|
|
198
|
+
options.instructions,
|
|
160
199
|
)
|
|
161
200
|
}
|
|
162
201
|
|
|
@@ -174,6 +213,7 @@ export async function buildVerifiedSummaryWithBoundedProvider(
|
|
|
174
213
|
onUsage?: UsageSink,
|
|
175
214
|
model?: string,
|
|
176
215
|
signal?: AbortSignal,
|
|
216
|
+
instructions?: string,
|
|
177
217
|
): Promise<string> {
|
|
178
218
|
signal?.throwIfAborted()
|
|
179
219
|
return await buildVerifiedSummaryWithProvider(
|
|
@@ -184,6 +224,7 @@ export async function buildVerifiedSummaryWithBoundedProvider(
|
|
|
184
224
|
onUsage,
|
|
185
225
|
model,
|
|
186
226
|
signal,
|
|
227
|
+
instructions,
|
|
187
228
|
)
|
|
188
229
|
}
|
|
189
230
|
|
|
@@ -195,14 +236,16 @@ async function buildVerifiedSummaryWithProvider(
|
|
|
195
236
|
onUsage?: UsageSink,
|
|
196
237
|
model?: string,
|
|
197
238
|
signal?: AbortSignal,
|
|
239
|
+
instructions?: string,
|
|
198
240
|
): Promise<string> {
|
|
199
241
|
const serialized = serializeState(manager.getState())
|
|
242
|
+
const steered = Boolean(instructions?.trim())
|
|
200
243
|
|
|
201
|
-
if (manager.slotCount() >= config.richStateThreshold) {
|
|
244
|
+
if (!steered && manager.slotCount() >= config.richStateThreshold) {
|
|
202
245
|
return serialized
|
|
203
246
|
}
|
|
204
247
|
|
|
205
|
-
if (!config.llmVerification) {
|
|
248
|
+
if (!steered && !config.llmVerification) {
|
|
206
249
|
return serialized
|
|
207
250
|
}
|
|
208
251
|
|
|
@@ -212,26 +255,45 @@ async function buildVerifiedSummaryWithProvider(
|
|
|
212
255
|
return serialized
|
|
213
256
|
}
|
|
214
257
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
258
|
+
// The verifier's own request can overflow a small window. The reference
|
|
259
|
+
// terminal answers that with a ladder: send less, and when nothing is left to
|
|
260
|
+
// cut, work from everything it already has. Here "less" is the older half of
|
|
261
|
+
// the excerpt dropped behind a placeholder, at most three times, and
|
|
262
|
+
// "everything it already has" is the extracted state, which the verifier only
|
|
263
|
+
// ever adds to.
|
|
264
|
+
let excerpt = conversationExcerpt
|
|
265
|
+
let truncations = 0
|
|
266
|
+
let response: Awaited<ReturnType<typeof collectChatCompletion>>
|
|
267
|
+
for (;;) {
|
|
268
|
+
const verificationMessages: Message[] = [
|
|
269
|
+
{
|
|
270
|
+
role: 'system' as const,
|
|
271
|
+
content: withInstructions(VERIFICATION_PROMPT, instructions),
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
role: 'user' as const,
|
|
275
|
+
content: `## Structured State\n\n${serialized}\n\n## Conversation Excerpt\n\n${excerpt}`,
|
|
276
|
+
},
|
|
277
|
+
]
|
|
278
|
+
try {
|
|
279
|
+
response = await collectChatCompletion(
|
|
280
|
+
provider.chatStream({
|
|
281
|
+
model: model ?? '',
|
|
282
|
+
messages: verificationMessages,
|
|
283
|
+
maxTokens: config.llmVerificationMaxTokens,
|
|
284
|
+
temperature: 0,
|
|
285
|
+
...(signal ? { signal } : {}),
|
|
286
|
+
}),
|
|
287
|
+
)
|
|
288
|
+
break
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (signal?.aborted) throw error
|
|
291
|
+
if (classifyProviderError(error).code !== 'context_length_exceeded') throw error
|
|
292
|
+
if (truncations >= MAX_VERIFIER_HEAD_TRUNCATIONS) return serialized
|
|
293
|
+
truncations++
|
|
294
|
+
excerpt = truncateExcerptHead(excerpt)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
235
297
|
|
|
236
298
|
onUsage?.(response.usage)
|
|
237
299
|
|
package/src/config/runtime.ts
CHANGED
|
@@ -41,8 +41,16 @@ export const CompactionConfigSchema = z.object({
|
|
|
41
41
|
* window-pressure instead of being a silent no-op.
|
|
42
42
|
*/
|
|
43
43
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
44
|
-
/**
|
|
45
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Fraction of the context window at which older history is summarised.
|
|
46
|
+
*
|
|
47
|
+
* Absent, the trigger is derived from the window: the window less room for
|
|
48
|
+
* the reply (the model's output cap, at most 20,000 tokens) less a
|
|
49
|
+
* 13,000-token buffer, so 83.5% of a 200,000-token window and 96.7% of a
|
|
50
|
+
* 1,000,000-token one (see `resolveTriggerThreshold`). It used to default
|
|
51
|
+
* to a flat `0.7`; pass that to keep the earlier behaviour.
|
|
52
|
+
*/
|
|
53
|
+
triggerThreshold: z.number().min(0).max(1).optional(),
|
|
46
54
|
/**
|
|
47
55
|
* Fraction the pass must get the context BELOW to be worth keeping.
|
|
48
56
|
*
|
|
@@ -58,6 +66,19 @@ export const CompactionConfigSchema = z.object({
|
|
|
58
66
|
* failure mode once the trigger actually fires.
|
|
59
67
|
*/
|
|
60
68
|
resetThreshold: z.number().min(0).max(1).default(0.4),
|
|
69
|
+
/**
|
|
70
|
+
* Give up on automatic compaction when it stops working.
|
|
71
|
+
*
|
|
72
|
+
* Three failed passes in a row switch it off for the rest of the turn.
|
|
73
|
+
* Three compactions in a row, each followed by the window filling again
|
|
74
|
+
* within three iterations, stop the turn: the loop refills the context
|
|
75
|
+
* faster than a summary can empty it. Each is announced once, as a
|
|
76
|
+
* `compaction_failed` event.
|
|
77
|
+
*
|
|
78
|
+
* Off unless set. Without it a pass that sheds nothing is retried on every
|
|
79
|
+
* iteration, and a turn that refills the window is never stopped for it.
|
|
80
|
+
*/
|
|
81
|
+
circuitBreaker: z.boolean().optional(),
|
|
61
82
|
keepRecentMessages: z.number().int().positive().default(4),
|
|
62
83
|
/**
|
|
63
84
|
* Size the retained tail by TOKENS instead of by message count.
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
validateConnectorMaxResponseBytes,
|
|
9
9
|
validateConnectorTimeoutMs,
|
|
10
10
|
} from '../http-operation.js'
|
|
11
|
+
import { isSameSiteRedirect } from './page.js'
|
|
11
12
|
import {
|
|
12
13
|
type WebFetchProvider,
|
|
13
14
|
WebFetchRefusedError,
|
|
@@ -304,6 +305,31 @@ export class GuardedFetchProvider implements WebFetchProvider {
|
|
|
304
305
|
const next = new URL(location, current)
|
|
305
306
|
cancelResponseBody(response, new Error('following redirect'))
|
|
306
307
|
operation.throwIfStopped()
|
|
308
|
+
if (request.redirects === 'same-host' && !isSameSiteRedirect(current, next)) {
|
|
309
|
+
// Handed back, not followed. It is still checked: a caller that
|
|
310
|
+
// goes there next goes through this guard again, but a target
|
|
311
|
+
// that can never be fetched is better refused here than offered.
|
|
312
|
+
try {
|
|
313
|
+
await this.assertAllowed(next.toString(), operation)
|
|
314
|
+
} catch (err) {
|
|
315
|
+
operation.throwIfStopped()
|
|
316
|
+
if (err instanceof WebFetchRefusedError) {
|
|
317
|
+
throw new WebFetchRefusedError(
|
|
318
|
+
`A redirect from "${current.toString()}" pointed at "${next.toString()}", which was refused: ${err.message}`,
|
|
319
|
+
{ url: next.toString(), reason: 'redirect-target' },
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
throw err
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
url: current.toString(),
|
|
326
|
+
status: response.status,
|
|
327
|
+
body: '',
|
|
328
|
+
truncated: false,
|
|
329
|
+
redirects,
|
|
330
|
+
redirect: { url: next.toString(), status: response.status },
|
|
331
|
+
}
|
|
332
|
+
}
|
|
307
333
|
try {
|
|
308
334
|
current = await this.assertAllowed(next.toString(), operation)
|
|
309
335
|
} catch (err) {
|
|
@@ -6,7 +6,10 @@ export { GuardedFetchProvider, isPrivateAddress } from './guarded-fetch.js'
|
|
|
6
6
|
export type { GuardedFetchConfig } from './guarded-fetch.js'
|
|
7
7
|
export { WebFetchRefusedError } from './types.js'
|
|
8
8
|
export type {
|
|
9
|
+
WebContentExtractor,
|
|
10
|
+
WebExtractRequest,
|
|
9
11
|
WebFetchProvider,
|
|
12
|
+
WebFetchRedirectPolicy,
|
|
10
13
|
WebFetchRefusalReason,
|
|
11
14
|
WebFetchRequest,
|
|
12
15
|
WebFetchResult,
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What `web_fetch` does with a page after the guard has let it through:
|
|
3
|
+
* decide which redirects stay on the same site, turn HTML into markdown, and
|
|
4
|
+
* keep a page for a quarter of an hour so a second question about it does not
|
|
5
|
+
* fetch it again.
|
|
6
|
+
*
|
|
7
|
+
* None of it touches the network or a model. The guard (`guarded-fetch.ts`)
|
|
8
|
+
* decides what may be fetched; this decides what the model is shown.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** How long a fetched page is kept. */
|
|
12
|
+
export const WEB_PAGE_CACHE_TTL_MS = 15 * 60 * 1000
|
|
13
|
+
/** Most the cache holds, counted in bytes of page text. */
|
|
14
|
+
export const WEB_PAGE_CACHE_MAX_BYTES = 50 * 1024 * 1024
|
|
15
|
+
/** Most HTML handed to the converter; the rest is dropped with a marker. */
|
|
16
|
+
export const WEB_PAGE_CONVERT_MAX_CHARS = 1024 * 1024
|
|
17
|
+
/** Most page text a model is asked about in one call. */
|
|
18
|
+
export const WEB_PAGE_MODEL_MAX_CHARS = 100_000
|
|
19
|
+
/** Marker the reference-shaped cuts end with. */
|
|
20
|
+
export const WEB_PAGE_TRUNCATED_MARKER = '[Content truncated due to length...]'
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* A redirect stays on the site when only the path or query changes (and
|
|
24
|
+
* `www.` comes or goes). `http:` to `https:` on the same host and default
|
|
25
|
+
* port is the same site too: the reference upgrades every URL to `https:`
|
|
26
|
+
* before it asks, so that hop never reaches it.
|
|
27
|
+
*/
|
|
28
|
+
export function isSameSiteRedirect(from: URL, to: URL): boolean {
|
|
29
|
+
const upgrade = from.protocol === 'http:' && to.protocol === 'https:'
|
|
30
|
+
if (to.protocol !== from.protocol && !upgrade) return false
|
|
31
|
+
if (to.port !== from.port) return false
|
|
32
|
+
if (to.username !== '' || to.password !== '') return false
|
|
33
|
+
const bare = (host: string): string => host.replace(/^www\./u, '')
|
|
34
|
+
return bare(to.hostname) === bare(from.hostname)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
type Converter = { turndown(html: string): string }
|
|
38
|
+
let converter: Promise<Converter> | undefined
|
|
39
|
+
|
|
40
|
+
function loadConverter(): Promise<Converter> {
|
|
41
|
+
converter ??= import('turndown').then((module) => {
|
|
42
|
+
const Turndown = module.default
|
|
43
|
+
const service = new Turndown()
|
|
44
|
+
service.remove(['style', 'script', 'noscript', 'iframe'])
|
|
45
|
+
return service
|
|
46
|
+
})
|
|
47
|
+
return converter
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* HTML as markdown. Style, script, noscript and iframe elements are dropped,
|
|
52
|
+
* and the input is cut at {@link WEB_PAGE_CONVERT_MAX_CHARS}. Never throws: a
|
|
53
|
+
* page the converter cannot read comes back as the HTML it was.
|
|
54
|
+
*/
|
|
55
|
+
export async function htmlToMarkdown(html: string): Promise<string> {
|
|
56
|
+
try {
|
|
57
|
+
const markdown = (await loadConverter()).turndown(html.slice(0, WEB_PAGE_CONVERT_MAX_CHARS))
|
|
58
|
+
return html.length > WEB_PAGE_CONVERT_MAX_CHARS
|
|
59
|
+
? `${markdown}\n\n${WEB_PAGE_TRUNCATED_MARKER}`
|
|
60
|
+
: markdown
|
|
61
|
+
} catch {
|
|
62
|
+
return html
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface CachedPage {
|
|
67
|
+
readonly content: string
|
|
68
|
+
readonly status: number
|
|
69
|
+
readonly contentType?: string
|
|
70
|
+
/** Where the fetch ended up. */
|
|
71
|
+
readonly url: string
|
|
72
|
+
readonly redirects: readonly string[]
|
|
73
|
+
readonly truncated: boolean
|
|
74
|
+
/** Bytes of text the page held before conversion. */
|
|
75
|
+
readonly bytes: number
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface Entry {
|
|
79
|
+
readonly page: CachedPage
|
|
80
|
+
readonly expiresAt: number
|
|
81
|
+
readonly size: number
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Pages by the URL that was asked for, for {@link WEB_PAGE_CACHE_TTL_MS}.
|
|
86
|
+
* Least recently used goes first when the byte cap is reached. The clock is
|
|
87
|
+
* `Date.now`, so a test with fake timers moves it.
|
|
88
|
+
*/
|
|
89
|
+
export class WebPageCache {
|
|
90
|
+
private readonly entries = new Map<string, Entry>()
|
|
91
|
+
private bytes = 0
|
|
92
|
+
|
|
93
|
+
constructor(
|
|
94
|
+
private readonly ttlMs: number = WEB_PAGE_CACHE_TTL_MS,
|
|
95
|
+
private readonly maxBytes: number = WEB_PAGE_CACHE_MAX_BYTES,
|
|
96
|
+
) {}
|
|
97
|
+
|
|
98
|
+
get(url: string): CachedPage | undefined {
|
|
99
|
+
const entry = this.entries.get(url)
|
|
100
|
+
if (entry === undefined) return undefined
|
|
101
|
+
if (entry.expiresAt <= Date.now()) {
|
|
102
|
+
this.remove(url)
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
105
|
+
// Re-inserted so the map's order is the order of use.
|
|
106
|
+
this.entries.delete(url)
|
|
107
|
+
this.entries.set(url, entry)
|
|
108
|
+
return entry.page
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
set(url: string, page: CachedPage): void {
|
|
112
|
+
this.remove(url)
|
|
113
|
+
const size = Math.max(1, Buffer.byteLength(page.content, 'utf8'))
|
|
114
|
+
if (size > this.maxBytes) return
|
|
115
|
+
this.entries.set(url, { page, expiresAt: Date.now() + this.ttlMs, size })
|
|
116
|
+
this.bytes += size
|
|
117
|
+
for (const [oldest, entry] of this.entries) {
|
|
118
|
+
if (this.bytes <= this.maxBytes) break
|
|
119
|
+
this.entries.delete(oldest)
|
|
120
|
+
this.bytes -= entry.size
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
private remove(url: string): void {
|
|
125
|
+
const entry = this.entries.get(url)
|
|
126
|
+
if (entry === undefined) return
|
|
127
|
+
this.entries.delete(url)
|
|
128
|
+
this.bytes -= entry.size
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -15,11 +15,25 @@
|
|
|
15
15
|
* `GuardedFetchProvider` for what "guarded" is doing the work of.
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* What a fetch does with a redirect.
|
|
20
|
+
*
|
|
21
|
+
* `follow` (the default) follows each hop after checking it. `same-host`
|
|
22
|
+
* follows only a hop that stays on the same site (same scheme and port,
|
|
23
|
+
* same host up to a leading `www.`, no credentials) and hands any other back
|
|
24
|
+
* unfollowed in {@link WebFetchResult.redirect}, so the caller decides whether
|
|
25
|
+
* to go there: a page that moved to another host is not the page that was
|
|
26
|
+
* asked for.
|
|
27
|
+
*/
|
|
28
|
+
export type WebFetchRedirectPolicy = 'follow' | 'same-host'
|
|
29
|
+
|
|
18
30
|
export interface WebFetchRequest {
|
|
19
31
|
readonly url: string
|
|
20
32
|
/** Extra headers. The provider may refuse or strip some; see the guard. */
|
|
21
33
|
readonly headers?: Readonly<Record<string, string>>
|
|
22
34
|
readonly signal?: AbortSignal
|
|
35
|
+
/** Defaults to `follow`. A provider that does not know `same-host` follows. */
|
|
36
|
+
readonly redirects?: WebFetchRedirectPolicy
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
export interface WebFetchResult {
|
|
@@ -38,6 +52,12 @@ export interface WebFetchResult {
|
|
|
38
52
|
readonly truncated: boolean
|
|
39
53
|
/** Every URL in the redirect chain, in order, including the final one. */
|
|
40
54
|
readonly redirects: readonly string[]
|
|
55
|
+
/**
|
|
56
|
+
* A redirect that was not followed (see {@link WebFetchRedirectPolicy}).
|
|
57
|
+
* `url` is where the server pointed, already checked against the same
|
|
58
|
+
* rules as any fetch, and `body` is empty.
|
|
59
|
+
*/
|
|
60
|
+
readonly redirect?: { readonly url: string; readonly status: number }
|
|
41
61
|
}
|
|
42
62
|
|
|
43
63
|
/** Why a fetch was refused before it left the process. */
|
|
@@ -62,6 +82,33 @@ export interface WebFetchProvider {
|
|
|
62
82
|
fetch(request: WebFetchRequest): Promise<WebFetchResult>
|
|
63
83
|
}
|
|
64
84
|
|
|
85
|
+
/** One question about one page, for a model to answer. */
|
|
86
|
+
export interface WebExtractRequest {
|
|
87
|
+
readonly url: string
|
|
88
|
+
/** What the caller wants from the page. */
|
|
89
|
+
readonly prompt: string
|
|
90
|
+
/** The page as markdown (or its raw text), already cut to the size a model should read. */
|
|
91
|
+
readonly content: string
|
|
92
|
+
/** The system instruction to run `body` under. */
|
|
93
|
+
readonly instruction: string
|
|
94
|
+
/** The user message: the page and the question, built by the tool. */
|
|
95
|
+
readonly body: string
|
|
96
|
+
readonly signal?: AbortSignal
|
|
97
|
+
readonly sessionId: string
|
|
98
|
+
readonly turnId: string
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Answers a prompt from a page with a small model.
|
|
103
|
+
*
|
|
104
|
+
* Supplied by the host: this kernel runs no model of its own here. Resolves
|
|
105
|
+
* the answer, or `undefined` / rejects when it cannot; `web_fetch` then
|
|
106
|
+
* returns the page itself, as it did before there was an extractor.
|
|
107
|
+
*/
|
|
108
|
+
export interface WebContentExtractor {
|
|
109
|
+
extract(request: WebExtractRequest): Promise<string | undefined>
|
|
110
|
+
}
|
|
111
|
+
|
|
65
112
|
export interface WebSearchRequest {
|
|
66
113
|
readonly query: string
|
|
67
114
|
readonly limit?: number
|
|
@@ -19,3 +19,13 @@
|
|
|
19
19
|
*/
|
|
20
20
|
export const AUTO_CONTINUATION_USER_MESSAGE =
|
|
21
21
|
'Continue exactly where you left off. Do not repeat content you already wrote — pick up at the next token.'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The nudge for a reply cut off while the model was still reasoning, before
|
|
25
|
+
* it wrote any text or called a tool. There is no partial answer to pick up,
|
|
26
|
+
* so this one asks for a shorter think instead of a continuation. Like
|
|
27
|
+
* {@link AUTO_CONTINUATION_USER_MESSAGE}, it is transparent when a turn's
|
|
28
|
+
* result is assembled.
|
|
29
|
+
*/
|
|
30
|
+
export const REASONING_CUT_USER_MESSAGE =
|
|
31
|
+
'The output limit was reached while you were still reasoning, before any text or tool call. Continue from the conversation above: think much more briefly and give your answer or next tool call well within the limit.'
|
|
@@ -34,7 +34,7 @@ export const MAX_CONNECTORS_PER_PLUGIN = 10
|
|
|
34
34
|
export const MAX_PERSONAS_PER_PLUGIN = 5
|
|
35
35
|
|
|
36
36
|
/** Hook execution */
|
|
37
|
-
export const HOOK_TIMEOUT_MS =
|
|
37
|
+
export const HOOK_TIMEOUT_MS = 600_000
|
|
38
38
|
|
|
39
39
|
/**
|
|
40
40
|
* Priority a hook gets when it does not declare one.
|
|
@@ -346,6 +346,16 @@ export class AgentManager {
|
|
|
346
346
|
return this.startMessage(options, context, listener)
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
/**
|
|
350
|
+
* The refusal for a delegation past the depth limit. It tells the model what
|
|
351
|
+
* to do instead (the reference terminal's rule): a bare limit leaves it
|
|
352
|
+
* retrying the same spawn, while an instruction to do the work itself ends the
|
|
353
|
+
* attempt. The limit is `AgentManagerConfig.maxDepth`; there is no env override.
|
|
354
|
+
*/
|
|
355
|
+
private depthLimitMessage(depth: number): string {
|
|
356
|
+
return `Max task depth ${this.config.maxDepth} exceeded (current: ${depth}). Do this task yourself with your own tools instead of delegating it to another agent.`
|
|
357
|
+
}
|
|
358
|
+
|
|
349
359
|
private async enqueueMessage(
|
|
350
360
|
options: SendMessageOptions,
|
|
351
361
|
context: AgentTaskContext,
|
|
@@ -353,7 +363,7 @@ export class AgentManager {
|
|
|
353
363
|
): Promise<AgentTask> {
|
|
354
364
|
context.parentAbortController.signal.throwIfAborted()
|
|
355
365
|
if (context.depth >= this.config.maxDepth)
|
|
356
|
-
throw new Error(
|
|
366
|
+
throw new Error(this.depthLimitMessage(context.depth))
|
|
357
367
|
if (options.tenantId !== context.tenantId)
|
|
358
368
|
throw new Error('Tenant mismatch: cross-tenant spawn rejected')
|
|
359
369
|
const definition = this.registry.getOrThrow(options.agentId)
|
|
@@ -527,9 +537,7 @@ export class AgentManager {
|
|
|
527
537
|
await options.beforeStart?.()
|
|
528
538
|
queuedTask?.childAbortController.signal.throwIfAborted()
|
|
529
539
|
if (context.depth >= this.config.maxDepth) {
|
|
530
|
-
throw new Error(
|
|
531
|
-
`Max task depth ${this.config.maxDepth} exceeded (current: ${context.depth}). Recursive agent delegation is limited to prevent resource exhaustion.`,
|
|
532
|
-
)
|
|
540
|
+
throw new Error(this.depthLimitMessage(context.depth))
|
|
533
541
|
}
|
|
534
542
|
|
|
535
543
|
if (options.tenantId !== context.tenantId) {
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
AUTO_CONTINUATION_USER_MESSAGE,
|
|
3
|
+
REASONING_CUT_USER_MESSAGE,
|
|
4
|
+
} from '../../constants/continuation.js'
|
|
2
5
|
import { EMPTY_TOKEN_USAGE } from '../../constants/limits.js'
|
|
3
6
|
import { NAMZU } from '../../constants/telemetry/index.js'
|
|
4
7
|
import { resolveModelPricing } from '../../pricing/index.js'
|
|
@@ -567,7 +570,12 @@ export class TurnRecorder {
|
|
|
567
570
|
if (msg.content !== null) chunks.push(msg.content)
|
|
568
571
|
continue
|
|
569
572
|
}
|
|
570
|
-
if (
|
|
573
|
+
if (
|
|
574
|
+
msg.role === 'user' &&
|
|
575
|
+
(msg.content === AUTO_CONTINUATION_USER_MESSAGE ||
|
|
576
|
+
msg.content === REASONING_CUT_USER_MESSAGE)
|
|
577
|
+
)
|
|
578
|
+
continue
|
|
571
579
|
break
|
|
572
580
|
}
|
|
573
581
|
if (chunks.length > 0) this.#turn.result = chunks.reverse().join('')
|