@animalabs/agent-framework 0.22.0 → 0.22.2
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/README.md +12 -5
- package/dist/src/agent.d.ts +192 -19
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +407 -66
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts +41 -0
- package/dist/src/api/mcp-server-core.d.ts.map +1 -0
- package/dist/src/api/mcp-server-core.js +593 -0
- package/dist/src/api/mcp-server-core.js.map +1 -0
- package/dist/src/api/mcp-server.d.ts +2 -36
- package/dist/src/api/mcp-server.d.ts.map +1 -1
- package/dist/src/api/mcp-server.js +15 -595
- package/dist/src/api/mcp-server.js.map +1 -1
- package/dist/src/api/server.d.ts +9 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +84 -23
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/api/types.d.ts +9 -1
- package/dist/src/api/types.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/framework.d.ts +682 -103
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3202 -718
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/gate-script.d.ts.map +1 -1
- package/dist/src/gate/gate-script.js.map +1 -1
- package/dist/src/index.d.ts +4 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +26 -7
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +102 -22
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/errors.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js +4 -3
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/index.d.ts +2 -0
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/log-value.d.ts +54 -0
- package/dist/src/mcpl/log-value.d.ts.map +1 -0
- package/dist/src/mcpl/log-value.js +109 -0
- package/dist/src/mcpl/log-value.js.map +1 -0
- package/dist/src/mcpl/prose-stream-router.d.ts +6 -6
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
- package/dist/src/mcpl/prose-stream-router.js +4 -4
- package/dist/src/mcpl/prose-stream-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +38 -3
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +81 -16
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +26 -3
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +87 -23
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +2 -1
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +20 -9
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +29 -6
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +57 -16
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/refusal-lines.d.ts +37 -0
- package/dist/src/mcpl/refusal-lines.d.ts.map +1 -0
- package/dist/src/mcpl/refusal-lines.js +61 -0
- package/dist/src/mcpl/refusal-lines.js.map +1 -0
- package/dist/src/mcpl/scope-manager.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +46 -3
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +98 -15
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.d.ts +1 -1
- package/dist/src/mcpl/tool-classes.d.ts.map +1 -1
- package/dist/src/mcpl/tool-classes.js +3 -2
- package/dist/src/mcpl/tool-classes.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +23 -5
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +103 -10
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +30 -26
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +9 -3
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +3 -2
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/api/index.d.ts.map +1 -1
- package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
- package/dist/src/modules/discord/discord-js-client.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +4 -2
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/health/index.d.ts.map +1 -1
- package/dist/src/modules/health/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
- package/dist/src/modules/workspace/branch-intent.js.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.d.ts +22 -4
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +63 -25
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +8 -4
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +85 -16
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +56 -11
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +288 -43
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +31 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +63 -12
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +27 -2
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/operator-log.d.ts +41 -0
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/provider-waits.d.ts +216 -0
- package/dist/src/provider-waits.d.ts.map +1 -0
- package/dist/src/provider-waits.js +482 -0
- package/dist/src/provider-waits.js.map +1 -0
- package/dist/src/queue.d.ts.map +1 -1
- package/dist/src/record-journal.d.ts.map +1 -1
- package/dist/src/record-journal.js.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +29 -24
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/refusal-reactions.d.ts +14 -12
- package/dist/src/refusal-reactions.d.ts.map +1 -1
- package/dist/src/refusal-reactions.js +14 -12
- package/dist/src/refusal-reactions.js.map +1 -1
- package/dist/src/runtime/liveness-watchdog.d.ts.map +1 -1
- package/dist/src/runtime/liveness-watchdog.js.map +1 -1
- package/dist/src/shutdown.d.ts.map +1 -1
- package/dist/src/shutdown.js.map +1 -1
- package/dist/src/stored-removal.d.ts +73 -0
- package/dist/src/stored-removal.d.ts.map +1 -0
- package/dist/src/stored-removal.js +100 -0
- package/dist/src/stored-removal.js.map +1 -0
- package/dist/src/thinking-token-stamp.js.map +1 -1
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +30 -13
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +71 -25
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +1 -1
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/types/agent.d.ts +42 -6
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +12 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +10 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +20 -8
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +75 -13
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.d.ts.map +1 -1
- package/dist/test/agent-runtime-settings.test.js +1139 -3
- package/dist/test/agent-runtime-settings.test.js.map +1 -1
- package/dist/test/batched-wake-locus.test.js.map +1 -1
- package/dist/test/channel-label-history.test.js.map +1 -1
- package/dist/test/code-execution.test.js.map +1 -1
- package/dist/test/conversation-fork-injection-scoping.test.js.map +1 -1
- package/dist/test/conversation-router.test.js.map +1 -1
- package/dist/test/conversation-routing.test.js +240 -6
- package/dist/test/conversation-routing.test.js.map +1 -1
- package/dist/test/discord-awareness-outbox.test.js +107 -0
- package/dist/test/discord-awareness-outbox.test.js.map +1 -1
- package/dist/test/discord-typing-keepalive.test.d.ts +2 -0
- package/dist/test/discord-typing-keepalive.test.d.ts.map +1 -0
- package/dist/test/discord-typing-keepalive.test.js +77 -0
- package/dist/test/discord-typing-keepalive.test.js.map +1 -0
- package/dist/test/ephemeral-settle.test.js.map +1 -1
- package/dist/test/event-gate-passthrough.test.js.map +1 -1
- package/dist/test/event-gate-policy-mutation.test.js.map +1 -1
- package/dist/test/event-gate-rate-limit.test.js.map +1 -1
- package/dist/test/event-gate-script.test.js.map +1 -1
- package/dist/test/event-gate-selfwake.test.js.map +1 -1
- package/dist/test/event-gate-tags.test.js.map +1 -1
- package/dist/test/event-gate.test.js.map +1 -1
- package/dist/test/explicit-prose-routing.test.js +363 -44
- package/dist/test/explicit-prose-routing.test.js.map +1 -1
- package/dist/test/framework.test.js +160 -1
- package/dist/test/framework.test.js.map +1 -1
- package/dist/test/helpers/coalescing-fixture.d.ts +2 -0
- package/dist/test/helpers/coalescing-fixture.d.ts.map +1 -1
- package/dist/test/helpers/coalescing-fixture.js +11 -4
- package/dist/test/helpers/coalescing-fixture.js.map +1 -1
- package/dist/test/helpers/mock-membrane.d.ts.map +1 -1
- package/dist/test/helpers/mock-membrane.js.map +1 -1
- package/dist/test/hide-then-turn.test.d.ts +2 -0
- package/dist/test/hide-then-turn.test.d.ts.map +1 -0
- package/dist/test/hide-then-turn.test.js +101 -0
- package/dist/test/hide-then-turn.test.js.map +1 -0
- package/dist/test/history-module-overview.test.js.map +1 -1
- package/dist/test/history-module-semantic.test.js.map +1 -1
- package/dist/test/history-module.test.js.map +1 -1
- package/dist/test/history-semantic-review-findings.test.js.map +1 -1
- package/dist/test/host-quiesce.test.js +125 -0
- package/dist/test/host-quiesce.test.js.map +1 -1
- package/dist/test/inference-exhaustion-rewind.test.js +220 -14
- package/dist/test/inference-exhaustion-rewind.test.js.map +1 -1
- package/dist/test/inference-failure-bounds.test.d.ts +2 -0
- package/dist/test/inference-failure-bounds.test.d.ts.map +1 -0
- package/dist/test/inference-failure-bounds.test.js +1202 -0
- package/dist/test/inference-failure-bounds.test.js.map +1 -0
- package/dist/test/kv-unified-open-flight-retry.test.js.map +1 -1
- package/dist/test/late-events-after-stop.test.d.ts +2 -0
- package/dist/test/late-events-after-stop.test.d.ts.map +1 -0
- package/dist/test/late-events-after-stop.test.js +122 -0
- package/dist/test/late-events-after-stop.test.js.map +1 -0
- package/dist/test/liveness-watchdog.test.js.map +1 -1
- package/dist/test/maintenance-timer.test.js +1564 -2
- package/dist/test/maintenance-timer.test.js.map +1 -1
- package/dist/test/mcp-server-bin.test.d.ts +2 -0
- package/dist/test/mcp-server-bin.test.d.ts.map +1 -0
- package/dist/test/mcp-server-bin.test.js +142 -0
- package/dist/test/mcp-server-bin.test.js.map +1 -0
- package/dist/test/mcpl-awareness-barrier.test.js +141 -0
- package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
- package/dist/test/mcpl-child-env.test.js +1 -1
- package/dist/test/mcpl-child-env.test.js.map +1 -1
- package/dist/test/mcpl-coalesced-content-parity.test.d.ts +2 -0
- package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +1 -0
- package/dist/test/mcpl-coalesced-content-parity.test.js +532 -0
- package/dist/test/mcpl-coalesced-content-parity.test.js.map +1 -0
- package/dist/test/mcpl-coalescing.test.js +78 -16
- package/dist/test/mcpl-coalescing.test.js.map +1 -1
- package/dist/test/mcpl-config-diagnostics.test.js +6 -3
- package/dist/test/mcpl-config-diagnostics.test.js.map +1 -1
- package/dist/test/mcpl-gate-roundtrip.test.js.map +1 -1
- package/dist/test/mcpl-manifest-changed.test.js +2 -1
- package/dist/test/mcpl-manifest-changed.test.js.map +1 -1
- package/dist/test/mcpl-reconnect-events.test.js.map +1 -1
- package/dist/test/mcpl-reconnect-reregistration.test.js +2 -0
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +1 -1
- package/dist/test/mcpl-reference-fetcher.test.js +232 -4
- package/dist/test/mcpl-reference-fetcher.test.js.map +1 -1
- package/dist/test/mcpl-reference-fields.test.js +69 -2
- package/dist/test/mcpl-reference-fields.test.js.map +1 -1
- package/dist/test/mcpl-rejection-log-lines.test.d.ts +2 -0
- package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +1 -0
- package/dist/test/mcpl-rejection-log-lines.test.js +349 -0
- package/dist/test/mcpl-rejection-log-lines.test.js.map +1 -0
- package/dist/test/mcpl-stderr-tail.test.d.ts +2 -0
- package/dist/test/mcpl-stderr-tail.test.d.ts.map +1 -0
- package/dist/test/mcpl-stderr-tail.test.js +210 -0
- package/dist/test/mcpl-stderr-tail.test.js.map +1 -0
- package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle.test.js.map +1 -1
- package/dist/test/mcpl-transport-close.test.d.ts +2 -0
- package/dist/test/mcpl-transport-close.test.d.ts.map +1 -0
- package/dist/test/mcpl-transport-close.test.js +373 -0
- package/dist/test/mcpl-transport-close.test.js.map +1 -0
- package/dist/test/mcpl-transport-spawn.test.d.ts +2 -0
- package/dist/test/mcpl-transport-spawn.test.d.ts.map +1 -0
- package/dist/test/mcpl-transport-spawn.test.js +58 -0
- package/dist/test/mcpl-transport-spawn.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +104 -0
- package/dist/test/mcpl-ws-transport.test.js.map +1 -1
- package/dist/test/offline-recovery-branch.test.js +344 -0
- package/dist/test/offline-recovery-branch.test.js.map +1 -1
- package/dist/test/operator-surgery.test.js +197 -0
- package/dist/test/operator-surgery.test.js.map +1 -1
- package/dist/test/ops-alert.test.js +2 -1
- package/dist/test/ops-alert.test.js.map +1 -1
- package/dist/test/overbudget-drain-breaker.test.js +1 -0
- package/dist/test/overbudget-drain-breaker.test.js.map +1 -1
- package/dist/test/pack-installs-alone.test.d.ts +2 -0
- package/dist/test/pack-installs-alone.test.d.ts.map +1 -0
- package/dist/test/pack-installs-alone.test.js +284 -0
- package/dist/test/pack-installs-alone.test.js.map +1 -0
- package/dist/test/physical-window-restart.test.d.ts +6 -4
- package/dist/test/physical-window-restart.test.d.ts.map +1 -1
- package/dist/test/physical-window-restart.test.js +275 -10
- package/dist/test/physical-window-restart.test.js.map +1 -1
- package/dist/test/placed-reaction-baseline.test.d.ts +2 -0
- package/dist/test/placed-reaction-baseline.test.d.ts.map +1 -0
- package/dist/test/placed-reaction-baseline.test.js +312 -0
- package/dist/test/placed-reaction-baseline.test.js.map +1 -0
- package/dist/test/present-while-acting.test.js +949 -6
- package/dist/test/present-while-acting.test.js.map +1 -1
- package/dist/test/provider-acceleration-cooldown.test.js +5 -1
- package/dist/test/provider-acceleration-cooldown.test.js.map +1 -1
- package/dist/test/provider-waits.test.d.ts +2 -0
- package/dist/test/provider-waits.test.d.ts.map +1 -0
- package/dist/test/provider-waits.test.js +2259 -0
- package/dist/test/provider-waits.test.js.map +1 -0
- package/dist/test/puppet-tool-call.test.js +2 -0
- package/dist/test/puppet-tool-call.test.js.map +1 -1
- package/dist/test/reaction-baseline-platform.test.d.ts +2 -0
- package/dist/test/reaction-baseline-platform.test.d.ts.map +1 -0
- package/dist/test/reaction-baseline-platform.test.js +124 -0
- package/dist/test/reaction-baseline-platform.test.js.map +1 -0
- package/dist/test/record-journal.test.js.map +1 -1
- package/dist/test/refusal-reaction-baseline.test.js +10 -3
- package/dist/test/refusal-reaction-baseline.test.js.map +1 -1
- package/dist/test/refusal-rewind.test.js +9 -0
- package/dist/test/refusal-rewind.test.js.map +1 -1
- package/dist/test/refusal-single-call.test.js.map +1 -1
- package/dist/test/release-bump.test.d.ts +2 -0
- package/dist/test/release-bump.test.d.ts.map +1 -0
- package/dist/test/release-bump.test.js +248 -0
- package/dist/test/release-bump.test.js.map +1 -0
- package/dist/test/release-changelog.test.js.map +1 -1
- package/dist/test/release-holds.test.d.ts +2 -0
- package/dist/test/release-holds.test.d.ts.map +1 -0
- package/dist/test/release-holds.test.js +456 -0
- package/dist/test/release-holds.test.js.map +1 -0
- package/dist/test/same-round-think-policy.test.js.map +1 -1
- package/dist/test/save-recent-image.test.js +138 -2
- package/dist/test/save-recent-image.test.js.map +1 -1
- package/dist/test/shutdown-errors.test.js.map +1 -1
- package/dist/test/silent-heartbeat-rows.test.js.map +1 -1
- package/dist/test/stop-with-host-operations.test.d.ts +20 -0
- package/dist/test/stop-with-host-operations.test.d.ts.map +1 -0
- package/dist/test/stop-with-host-operations.test.js +1174 -0
- package/dist/test/stop-with-host-operations.test.js.map +1 -0
- package/dist/test/stop-with-work-in-flight.test.js +162 -50
- package/dist/test/stop-with-work-in-flight.test.js.map +1 -1
- package/dist/test/surgery-marks-receipt.test.d.ts +2 -0
- package/dist/test/surgery-marks-receipt.test.d.ts.map +1 -0
- package/dist/test/surgery-marks-receipt.test.js +540 -0
- package/dist/test/surgery-marks-receipt.test.js.map +1 -0
- package/dist/test/tool-batch-hook.test.js.map +1 -1
- package/dist/test/tool-result-guard.test.js +352 -16
- package/dist/test/tool-result-guard.test.js.map +1 -1
- package/dist/test/tool-result-spill.test.js.map +1 -1
- package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +1 -1
- package/dist/test/trunk-channel-routing.test.js +330 -7
- package/dist/test/trunk-channel-routing.test.js.map +1 -1
- package/dist/test/tune-out-e2e.test.js.map +1 -1
- package/dist/test/unconvertible-tool-result.test.js.map +1 -1
- package/dist/test/unsent-compiles.test.d.ts +2 -0
- package/dist/test/unsent-compiles.test.d.ts.map +1 -0
- package/dist/test/unsent-compiles.test.js +139 -0
- package/dist/test/unsent-compiles.test.js.map +1 -0
- package/dist/test/workspace-branch-continuation.test.js.map +1 -1
- package/dist/test/workspace-materialize-freshness.test.js.map +1 -1
- package/dist/test/workspace-read-text.test.js.map +1 -1
- package/dist/test/workspace-three-way.test.js +1022 -7
- package/dist/test/workspace-three-way.test.js.map +1 -1
- package/dist/test/workspace-watcher.test.js.map +1 -1
- package/dist/test/xml-round-persist.test.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -6
- package/src/agent.ts +415 -67
- package/src/api/mcp-server-core.ts +681 -0
- package/src/api/mcp-server.ts +16 -685
- package/src/api/server.ts +87 -26
- package/src/api/types.ts +9 -1
- package/src/framework.ts +3389 -782
- package/src/index.ts +9 -4
- package/src/mcpl/channel-registry.ts +126 -31
- package/src/mcpl/feature-set-manager.ts +4 -3
- package/src/mcpl/index.ts +2 -0
- package/src/mcpl/log-value.ts +116 -0
- package/src/mcpl/prose-stream-router.ts +6 -6
- package/src/mcpl/push-coalescer.ts +76 -12
- package/src/mcpl/push-handler.ts +95 -23
- package/src/mcpl/reference-fetcher.ts +18 -8
- package/src/mcpl/references.ts +57 -13
- package/src/mcpl/refusal-lines.ts +59 -0
- package/src/mcpl/server-connection.ts +125 -15
- package/src/mcpl/tool-classes.ts +3 -2
- package/src/mcpl/transport.ts +105 -14
- package/src/mcpl/types.ts +5 -1
- package/src/module-registry.ts +17 -9
- package/src/modules/discord/index.ts +4 -2
- package/src/modules/workspace/disk-agreement.ts +74 -18
- package/src/modules/workspace/index.ts +85 -15
- package/src/modules/workspace/observe.ts +295 -43
- package/src/modules/workspace/reconcile.ts +90 -12
- package/src/modules/workspace/types.ts +26 -1
- package/src/operator-log.ts +30 -0
- package/src/provider-waits.ts +523 -0
- package/src/recovery/offline-branch.ts +32 -28
- package/src/refusal-reactions.ts +14 -12
- package/src/stored-removal.ts +126 -0
- package/src/tool-result-guard.ts +72 -24
- package/src/types/agent.ts +42 -6
- package/src/types/events.ts +12 -0
- package/src/types/framework.ts +10 -0
- package/src/types/module.ts +20 -8
- package/src/types/trace.ts +76 -14
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { JsStore } from '@animalabs/chronicle';
|
|
2
|
-
import { ContextManager } from '@animalabs/context-manager';
|
|
2
|
+
import { ContextManager, continuesBody, type BodyShard } from '@animalabs/context-manager';
|
|
3
|
+
import { removeStoredInterval } from '../stored-removal.js';
|
|
3
4
|
import {
|
|
4
5
|
DEFAULT_DISCORD_AWARENESS_EMOJI,
|
|
5
6
|
DiscordAwarenessOutbox,
|
|
@@ -202,11 +203,12 @@ export async function createOfflineRecoveryBranch(
|
|
|
202
203
|
const createdBranch = contextManager.branchAt(target.id, targetBranch);
|
|
203
204
|
try {
|
|
204
205
|
await contextManager.switchBranch(createdBranch);
|
|
205
|
-
//
|
|
206
|
-
//
|
|
206
|
+
// Each interval is removed by its messages' ids, so the order doesn't
|
|
207
|
+
// change the result. removeStoredInterval takes a single message outside
|
|
208
|
+
// any body through removeMessage, and a shard or a longer interval as a
|
|
209
|
+
// range, which refuses one that splits a body.
|
|
207
210
|
for (const interval of [...suppression.intervals].reverse()) {
|
|
208
|
-
|
|
209
|
-
else contextManager.removeMessages(interval.fromId, interval.toId);
|
|
211
|
+
removeStoredInterval(contextManager, interval.fromId, interval.toId);
|
|
210
212
|
}
|
|
211
213
|
if (batch) outbox.activate(batch.id);
|
|
212
214
|
} catch (error) {
|
|
@@ -238,9 +240,7 @@ interface SuppressionInterval {
|
|
|
238
240
|
|
|
239
241
|
interface MessageLocation {
|
|
240
242
|
first: number;
|
|
241
|
-
last: number;
|
|
242
243
|
firstId: StoredWindowMessage['id'];
|
|
243
|
-
lastId: StoredWindowMessage['id'];
|
|
244
244
|
/** Usually one run; multiple runs tolerate duplicate external metadata. */
|
|
245
245
|
runs: SuppressionInterval[];
|
|
246
246
|
}
|
|
@@ -299,14 +299,15 @@ function validateAnchorBoundary(
|
|
|
299
299
|
total: number,
|
|
300
300
|
target: StoredWindowMessage,
|
|
301
301
|
): void {
|
|
302
|
-
|
|
303
|
-
if (!groupId || targetIndex + 1 >= total) return;
|
|
302
|
+
if (targetIndex + 1 >= total) return;
|
|
304
303
|
const next = contextManager.getMessageWindow(targetIndex + 1, 1, {
|
|
305
304
|
resolveBlobs: false,
|
|
306
|
-
}).messages[0]
|
|
307
|
-
|
|
305
|
+
}).messages[0];
|
|
306
|
+
// A body continues only into a later shard of the same ingestion: a
|
|
307
|
+
// reposted document shares its group id but starts a body of its own.
|
|
308
|
+
if (continuesBody(target as BodyShard, next as BodyShard | undefined)) {
|
|
308
309
|
throw new Error(
|
|
309
|
-
`Recovery anchor ${String(target.id)} splits body group ${
|
|
310
|
+
`Recovery anchor ${String(target.id)} splits body group ${(target as BodyShard).bodyGroupId}; choose its final shard.`,
|
|
310
311
|
);
|
|
311
312
|
}
|
|
312
313
|
}
|
|
@@ -321,7 +322,9 @@ function validateRemovalIntegrity(
|
|
|
321
322
|
index >= interval.start && index <= interval.end);
|
|
322
323
|
const toolUses = new Map<string, boolean>();
|
|
323
324
|
const toolResults = new Map<string, boolean>();
|
|
324
|
-
|
|
325
|
+
// The message before, in store order: a selection splits a body when a
|
|
326
|
+
// message continuing the one before it lands on the other side.
|
|
327
|
+
let previous: { message: BodyShard; removed: boolean } | null = null;
|
|
325
328
|
|
|
326
329
|
for (let offset = 0; offset < total; offset += RECOVERY_SCAN_WINDOW) {
|
|
327
330
|
const messages = contextManager.getMessageWindow(
|
|
@@ -332,16 +335,14 @@ function validateRemovalIntegrity(
|
|
|
332
335
|
for (let i = 0; i < messages.length; i++) {
|
|
333
336
|
const index = offset + i;
|
|
334
337
|
const isRemoved = removed(index);
|
|
335
|
-
const message = messages[i]
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
groupMembership.set(message.bodyGroupId, isRemoved);
|
|
338
|
+
const message = messages[i];
|
|
339
|
+
const shard = message as BodyShard;
|
|
340
|
+
if (previous && previous.removed !== isRemoved && continuesBody(previous.message, shard)) {
|
|
341
|
+
throw new Error(
|
|
342
|
+
`Recovery selection splits body group ${shard.bodyGroupId}; adjust the anchor or suppression boundary.`,
|
|
343
|
+
);
|
|
344
344
|
}
|
|
345
|
+
previous = { message: shard, removed: isRemoved };
|
|
345
346
|
for (const rawBlock of message.content) {
|
|
346
347
|
const block = rawBlock as { type?: unknown; id?: unknown; toolUseId?: unknown };
|
|
347
348
|
if (block.type === 'tool_use' && typeof block.id === 'string') {
|
|
@@ -402,9 +403,7 @@ function buildSuppressionPlan(
|
|
|
402
403
|
};
|
|
403
404
|
locations.set(externalId, {
|
|
404
405
|
first: index,
|
|
405
|
-
last: index,
|
|
406
406
|
firstId: messages[i].id,
|
|
407
|
-
lastId: messages[i].id,
|
|
408
407
|
runs: [run],
|
|
409
408
|
});
|
|
410
409
|
} else {
|
|
@@ -420,8 +419,6 @@ function buildSuppressionPlan(
|
|
|
420
419
|
toId: messages[i].id,
|
|
421
420
|
});
|
|
422
421
|
}
|
|
423
|
-
location.last = index;
|
|
424
|
-
location.lastId = messages[i].id;
|
|
425
422
|
}
|
|
426
423
|
}
|
|
427
424
|
}
|
|
@@ -444,12 +441,19 @@ function buildSuppressionPlan(
|
|
|
444
441
|
const to = locations.get(range.toMessageId)!;
|
|
445
442
|
const earlier = from.first <= to.first ? from : to;
|
|
446
443
|
const later = from.first <= to.first ? to : from;
|
|
444
|
+
// What's between the two messages, after the earlier one began and
|
|
445
|
+
// before the later one began: from the earlier one's first stored copy
|
|
446
|
+
// through the later one's first. Each end is every copy of it besides,
|
|
447
|
+
// as a single suppression is, so a copy stored outside that span (a
|
|
448
|
+
// write cut short and retried later) is taken on its own, and nothing
|
|
449
|
+
// else stored after the later one began is taken.
|
|
447
450
|
intervals.push({
|
|
448
451
|
start: earlier.first,
|
|
449
|
-
end: later.
|
|
452
|
+
end: later.first,
|
|
450
453
|
fromId: earlier.firstId,
|
|
451
|
-
toId: later.
|
|
454
|
+
toId: later.firstId,
|
|
452
455
|
});
|
|
456
|
+
for (const end of [from, to]) intervals.push(...end.runs.map((run) => ({ ...run })));
|
|
453
457
|
}
|
|
454
458
|
|
|
455
459
|
intervals.sort((a, b) => a.start - b.start || a.end - b.end);
|
package/src/refusal-reactions.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Refusal-reaction markers — the single source of truth for the emoji the
|
|
3
3
|
* framework places on a message when inference is refused (see
|
|
4
|
-
* AgentFramework.reactToRefusal)
|
|
5
|
-
* a protective suppression baseline from the exact set the framework emits.
|
|
4
|
+
* AgentFramework.reactToRefusal).
|
|
6
5
|
*
|
|
7
6
|
* These annotations are placed by the framework on the resident's account
|
|
8
7
|
* and must never re-enter a resident's context as reaction events — that is
|
|
9
8
|
* the self-amplifying loop behind the 8/3 Mythos incident. The Discord
|
|
10
|
-
* adapter suppresses them when its operator config or
|
|
11
|
-
* `DISCORD_SUPPRESSED_REACTIONS_BASELINE` names them
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
9
|
+
* adapter suppresses them when its operator config or
|
|
10
|
+
* `DISCORD_SUPPRESSED_REACTIONS_BASELINE` names them. That variable's
|
|
11
|
+
* protective default, which the framework injects into stdio children, is
|
|
12
|
+
* `framework.getPlacedReactionBaseline()` on an instance: these refusal
|
|
13
|
+
* markers plus the awareness markers. REFUSAL_REACTION_BASELINE below is the
|
|
14
|
+
* refusal set only. Keeping the emitted refusal set and the exported one as
|
|
15
|
+
* one constant is what makes drift between "what we stamp" and "what we
|
|
16
|
+
* suppress" structurally impossible, rather than a promise kept in two
|
|
17
|
+
* files.
|
|
15
18
|
*/
|
|
16
19
|
|
|
17
20
|
/** Refusal category → Discord reaction emoji. Unknown categories get the
|
|
@@ -27,11 +30,10 @@ export const REFUSAL_REACTIONS: Readonly<Record<string, string>> = {
|
|
|
27
30
|
/** Marker used when the refusal category has no dedicated emoji. */
|
|
28
31
|
export const REFUSAL_REACTION_FALLBACK = '🛑';
|
|
29
32
|
|
|
30
|
-
/** Every marker the framework can emit
|
|
31
|
-
* fallback, deduplicated
|
|
32
|
-
* protective
|
|
33
|
-
* `
|
|
34
|
-
* reactions. */
|
|
33
|
+
/** Every refusal marker the framework can emit: the category map plus the
|
|
34
|
+
* fallback, deduplicated in stable declaration order. For the deployment's
|
|
35
|
+
* complete protective default, including awareness markers, use
|
|
36
|
+
* `framework.getPlacedReactionBaseline()` on an instance. */
|
|
35
37
|
export const REFUSAL_REACTION_BASELINE: readonly string[] = [
|
|
36
38
|
...new Set([...Object.values(REFUSAL_REACTIONS), REFUSAL_REACTION_FALLBACK]),
|
|
37
39
|
];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { bodyBoundsIn, type BodyShard, type ContextManager, type MessageId } from '@animalabs/context-manager';
|
|
2
|
+
|
|
3
|
+
type StoredLike = BodyShard & { id: MessageId };
|
|
4
|
+
type Removals = Pick<ContextManager, 'removeMessage' | 'removeMessages'>;
|
|
5
|
+
|
|
6
|
+
/** Minimal read surface the live-surgery helpers need from a ContextManager. */
|
|
7
|
+
export interface SurgeryReadableCm {
|
|
8
|
+
getMessageCount(): number;
|
|
9
|
+
getMessageWindow(
|
|
10
|
+
offset: number,
|
|
11
|
+
limit: number,
|
|
12
|
+
opts?: { resolveBlobs?: boolean; alignToBodyGroups?: boolean },
|
|
13
|
+
): { messages: Array<{ id: unknown; bodyGroupId?: string }>; startIndex: number };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SURGERY_SCAN_WINDOW = 500;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Slot index of `messageId` on the active branch, or -1. Scans from the tail
|
|
20
|
+
* in blob-free windows: operator targets are almost always recent, and a
|
|
21
|
+
* full `getAllMessages()` re-inflates every attachment on the branch.
|
|
22
|
+
*/
|
|
23
|
+
export function locateMessageIndex(cm: SurgeryReadableCm, messageId: string): number {
|
|
24
|
+
const hit = locateMessageIndices(cm, new Set([messageId])).get(messageId);
|
|
25
|
+
return hit ? hit.index : -1;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Tail-first scan resolving many ids at once; stops as soon as all are found. */
|
|
29
|
+
export function locateMessageIndices(
|
|
30
|
+
cm: SurgeryReadableCm,
|
|
31
|
+
ids: Set<string>,
|
|
32
|
+
): Map<string, { index: number; bodyGroupId?: string }> {
|
|
33
|
+
const found = new Map<string, { index: number; bodyGroupId?: string }>();
|
|
34
|
+
const total = cm.getMessageCount();
|
|
35
|
+
for (let end = total; end > 0 && found.size < ids.size; end -= SURGERY_SCAN_WINDOW) {
|
|
36
|
+
const start = Math.max(0, end - SURGERY_SCAN_WINDOW);
|
|
37
|
+
const { messages, startIndex } = cm.getMessageWindow(start, end - start, { resolveBlobs: false });
|
|
38
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
39
|
+
const id = String(messages[i].id);
|
|
40
|
+
if (ids.has(id) && !found.has(id)) {
|
|
41
|
+
found.set(id, {
|
|
42
|
+
index: startIndex + i,
|
|
43
|
+
...(messages[i].bodyGroupId ? { bodyGroupId: messages[i].bodyGroupId } : {}),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return found;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Inclusive slot range of the body-group run containing `index` (the slot
|
|
53
|
+
* itself when it is not sharded). Delegates to the store's own
|
|
54
|
+
* `alignToBodyGroups` edge walk — one contiguity assumption, not two.
|
|
55
|
+
*/
|
|
56
|
+
export function bodyGroupRun(
|
|
57
|
+
cm: SurgeryReadableCm,
|
|
58
|
+
index: number,
|
|
59
|
+
): { from: number; to: number; fromId: string; toId: string; group: boolean } {
|
|
60
|
+
const win = cm.getMessageWindow(index, 1, { alignToBodyGroups: true, resolveBlobs: false });
|
|
61
|
+
const from = win.startIndex;
|
|
62
|
+
const to = win.startIndex + win.messages.length - 1;
|
|
63
|
+
return {
|
|
64
|
+
from,
|
|
65
|
+
to,
|
|
66
|
+
fromId: String(win.messages[0].id),
|
|
67
|
+
toId: String(win.messages[win.messages.length - 1].id),
|
|
68
|
+
group: win.messages[0].bodyGroupId !== undefined,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Remove the stored messages `first`..`last` (inclusive, in store order).
|
|
74
|
+
*
|
|
75
|
+
* A single message outside any body goes through `removeMessage`.
|
|
76
|
+
* context-manager's `removeMessage` refuses any message that carries a body
|
|
77
|
+
* group id, a one-shard body's included, so a shard, and any longer interval,
|
|
78
|
+
* goes through `removeMessages`, which refuses an interval that splits a
|
|
79
|
+
* body.
|
|
80
|
+
*/
|
|
81
|
+
export function removeStoredMessages(cm: Removals, first: StoredLike, last: StoredLike): void {
|
|
82
|
+
if (first.id === last.id && !first.bodyGroupId) {
|
|
83
|
+
cm.removeMessage(first.id);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
cm.removeMessages(first.id, last.id);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Remove one stored message by its id, with the rest of its body when it's
|
|
91
|
+
* in one: a module or a coalescer removes a message it added by the id
|
|
92
|
+
* `addMessage` returned, which for a long message stored in shards is its
|
|
93
|
+
* first shard's.
|
|
94
|
+
*/
|
|
95
|
+
export function removeStoredMessage(cm: ContextManager, id: MessageId): void {
|
|
96
|
+
const message = cm.getMessage(id) as StoredLike | null;
|
|
97
|
+
if (!message?.bodyGroupId) {
|
|
98
|
+
cm.removeMessage(id);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Found in blob-free windows: a full read of the branch would inflate every
|
|
102
|
+
// attachment on it, once for each message a sweep removes.
|
|
103
|
+
const index = locateMessageIndex(cm, String(id));
|
|
104
|
+
if (index < 0) {
|
|
105
|
+
cm.removeMessage(id);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const run = bodyGroupRun(cm, index);
|
|
109
|
+
cm.removeMessages(run.fromId as MessageId, run.toId as MessageId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** removeStoredMessages for an interval known by its ids. */
|
|
113
|
+
export function removeStoredInterval(cm: ContextManager, fromId: MessageId, toId: MessageId): void {
|
|
114
|
+
const first = cm.getMessage(fromId) as StoredLike | null;
|
|
115
|
+
removeStoredMessages(cm, first ?? { id: fromId }, { id: toId });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* The first and last positions (inclusive) of the body that holds
|
|
120
|
+
* `messages[index]`, read the way context-manager 0.14.0 reads bodies
|
|
121
|
+
* (`continuesBody`, pairwise in store order). A message outside any body is a
|
|
122
|
+
* body of its own.
|
|
123
|
+
*/
|
|
124
|
+
export function bodyRangeIn(messages: readonly unknown[], index: number): { from: number; to: number } {
|
|
125
|
+
return bodyBoundsIn(messages as readonly (BodyShard | null | undefined)[], index);
|
|
126
|
+
}
|
package/src/tool-result-guard.ts
CHANGED
|
@@ -4,6 +4,8 @@ import type { ContextManager, MessageId, MessageMetadata } from '@animalabs/cont
|
|
|
4
4
|
import type { ContentBlock, NormalizedMessage, ToolResult } from '@animalabs/membrane';
|
|
5
5
|
import type { CompletedToolCall } from './types/index.js';
|
|
6
6
|
import { isStateExistsError } from './module-registry.js';
|
|
7
|
+
import { toolResultDataToHistoryString } from './tool-result-history.js';
|
|
8
|
+
import { safeSlice } from './safe-slice.js';
|
|
7
9
|
|
|
8
10
|
export const TOOL_RESULT_GUARD_NOTICE = 'Tool result withheld by the guard. The tool has already executed.';
|
|
9
11
|
export const TOOL_RESULT_GUARD_AUDIT_STATE = 'framework/tool-result-guard';
|
|
@@ -52,31 +54,60 @@ export interface WithheldHost {
|
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* The stub a withheld result settles to: #159's neutral notice, then where
|
|
55
|
-
* the original is
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* its retry
|
|
57
|
+
* the original is, then, when its tool marked any, the fields kept from its
|
|
58
|
+
* result (`kept`, from keptWhenWithheld). It names no refusal and no
|
|
59
|
+
* category (#159 keeps those in operational logs), so reading the rest back
|
|
60
|
+
* is the agent's deliberate choice (agent-framework #277). The place holds
|
|
61
|
+
* the result as stored, which for one over the inline cap is its capped
|
|
62
|
+
* text, whose own note names the file with the whole output; so the stub
|
|
63
|
+
* says "its result", not its full result. A failed write's own error text
|
|
64
|
+
* stays in the audit record and the logs, not here. The audit record is
|
|
65
|
+
* said to keep the original only when it was saved: a staged record whose
|
|
66
|
+
* save failed is still queued in memory, and a restart before its retry
|
|
67
|
+
* loses it.
|
|
65
68
|
*/
|
|
66
|
-
export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean): string {
|
|
69
|
+
export function withheldResultNotice(spill: WithheldSpill, auditDurable: boolean, kept?: string): string {
|
|
67
70
|
const audit = `the guard's audit record (${TOOL_RESULT_GUARD_AUDIT_STATE}), for an operator`;
|
|
68
|
-
const
|
|
71
|
+
const keptIn = (only: string) => auditDurable
|
|
69
72
|
? `is kept ${only}in ${audit}`
|
|
70
73
|
: `went ${only}to ${audit}, though saving that record had failed when the result arrived`;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
`
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const place = !spill || 'skipped' in spill
|
|
75
|
+
? `Its result ${keptIn('')}.`
|
|
76
|
+
: spill.unfinished
|
|
77
|
+
? `Its result was still being written to workspace file ${spill.path} when this note was made; ` +
|
|
78
|
+
`if that file is missing, it ${keptIn('')}.`
|
|
79
|
+
: spill.error === undefined
|
|
80
|
+
? `Its result is in workspace file ${spill.path}.`
|
|
81
|
+
: `Writing its result to workspace file ${spill.path} failed, so it ${keptIn('only ')}.`;
|
|
82
|
+
const fields = kept === undefined ? '' : ` Kept from its result: ${kept}`;
|
|
83
|
+
return `${TOOL_RESULT_GUARD_NOTICE} ${place}${fields}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* What a withheld result's stub keeps of it: the fields of its data that its
|
|
88
|
+
* tool marked as saying what it acted on (ToolResult.keepWhenWithheld), in
|
|
89
|
+
* the order marked, serialized as a stored result is. Past the inline cap
|
|
90
|
+
* they're cut, with a note that says so and where the whole result is, since
|
|
91
|
+
* a later field can then be cut away entirely. Undefined when it keeps
|
|
92
|
+
* nothing, as for a failed result (agent-framework #277). A write's file
|
|
93
|
+
* list is evidence of what it did, rarely what drew a refusal, and keeping it
|
|
94
|
+
* is what shows a clobbered file when the receipt is withheld.
|
|
95
|
+
*/
|
|
96
|
+
export function keptWhenWithheld(result: CompletedToolCall['result'], maxChars: number | undefined): string | undefined {
|
|
97
|
+
const { data, keepWhenWithheld: keys } = result;
|
|
98
|
+
if (!result.success || result.isError || !keys?.length || !data || typeof data !== 'object' || Array.isArray(data)) {
|
|
99
|
+
return undefined;
|
|
78
100
|
}
|
|
79
|
-
|
|
101
|
+
const fields = data as Record<string, unknown>;
|
|
102
|
+
const kept = Object.fromEntries(keys.filter((key) => Object.hasOwn(fields, key)).map((key) => [key, fields[key]]));
|
|
103
|
+
if (Object.keys(kept).length === 0) return undefined;
|
|
104
|
+
const whole = toolResultDataToHistoryString(kept);
|
|
105
|
+
if (!maxChars || whole.length <= maxChars) return whole;
|
|
106
|
+
// Never half a surrogate pair: the stub goes to the provider as text, so
|
|
107
|
+
// the cut can be one character short of the cap.
|
|
108
|
+
const head = safeSlice(whole, 0, maxChars);
|
|
109
|
+
return `${head} [cut at the inline cap: ${head.length} of these fields' ${whole.length} ` +
|
|
110
|
+
'characters; the whole result is where this note says]';
|
|
80
111
|
}
|
|
81
112
|
|
|
82
113
|
interface PendingBatch {
|
|
@@ -89,6 +120,9 @@ interface PendingBatch {
|
|
|
89
120
|
submitted: boolean;
|
|
90
121
|
/** False when the audit sync failed: originals then never go on the wire. */
|
|
91
122
|
durable: boolean;
|
|
123
|
+
/** What each result's stub keeps if the batch is withheld, by its index in
|
|
124
|
+
* content: the fields its tool marked (keptWhenWithheld). */
|
|
125
|
+
kept: Map<number, string>;
|
|
92
126
|
}
|
|
93
127
|
|
|
94
128
|
interface AuditOperation {
|
|
@@ -280,11 +314,15 @@ export class ToolResultGuard {
|
|
|
280
314
|
: JSON.parse(json);
|
|
281
315
|
}
|
|
282
316
|
|
|
317
|
+
/** `inlineCap` is the cap the caller stored `content` under; what a stub
|
|
318
|
+
* keeps of a withheld result is cut at it too. */
|
|
283
319
|
storeResults(
|
|
284
320
|
content: ContentBlock[],
|
|
285
321
|
wireResults: ToolResult[],
|
|
286
322
|
originals: CompletedToolCall[],
|
|
287
|
-
metadata
|
|
323
|
+
metadata: MessageMetadata | undefined,
|
|
324
|
+
/** The cap the results were stored under: what a stub keeps is cut there too. */
|
|
325
|
+
inlineCap: number | undefined,
|
|
288
326
|
): MessageId {
|
|
289
327
|
if (!this.enabled) return this.cm.addMessage('user', content, metadata);
|
|
290
328
|
if (this.pending) {
|
|
@@ -312,13 +350,22 @@ export class ToolResultGuard {
|
|
|
312
350
|
const withheld: ContentBlock[] = content.map((block) => block.type === 'tool_result'
|
|
313
351
|
? { type: 'tool_result', toolUseId: block.toolUseId, content: TOOL_RESULT_GUARD_NOTICE, isError: block.isError }
|
|
314
352
|
: block);
|
|
353
|
+
// What each stub keeps if the batch is withheld, matched to its tool's
|
|
354
|
+
// result by tool use id, keyed by index as the stubs and files are.
|
|
355
|
+
const results = new Map(originals.map((call) => [call.id, call.result]));
|
|
356
|
+
const kept = new Map<number, string>();
|
|
357
|
+
for (const [index, block] of content.entries()) {
|
|
358
|
+
const result = block.type === 'tool_result' ? results.get(block.toolUseId) : undefined;
|
|
359
|
+
const text = result ? keptWhenWithheld(result, inlineCap) : undefined;
|
|
360
|
+
if (text !== undefined) kept.set(index, text);
|
|
361
|
+
}
|
|
315
362
|
// Compression hold (CM >= 0.12): placed BEFORE onNewMessage fires, so no
|
|
316
363
|
// chunk is ever summarized from the placeholder; released after the
|
|
317
364
|
// settlement edit. Holds are in-memory only — a reopened manager has none,
|
|
318
365
|
// which is correct since a reopened guard has no pending batch.
|
|
319
366
|
const messageId = this.cm.addMessage('user', withheld, metadata, undefined, { holdCompression: true });
|
|
320
367
|
this.pending = { id, messageId, content, withheld, wireResults,
|
|
321
|
-
branch: this.cm.currentBranch().name, submitted: false, durable: false };
|
|
368
|
+
branch: this.cm.currentBranch().name, submitted: false, durable: false, kept };
|
|
322
369
|
// Durability barrier: the audit must reach Chronicle's chain heads before
|
|
323
370
|
// the originals can go to a provider. On a failed sync the batch fails
|
|
324
371
|
// CLOSED: the placeholders go on the wire instead (the turn continues,
|
|
@@ -452,7 +499,8 @@ export class ToolResultGuard {
|
|
|
452
499
|
/**
|
|
453
500
|
* Turn a withheld batch's notices into stubs that say where each original
|
|
454
501
|
* is (agent-framework #277): written to the workspace when a writable mount
|
|
455
|
-
* takes it, else kept in the audit record.
|
|
502
|
+
* takes it, else kept in the audit record. A stub also keeps the file
|
|
503
|
+
* lists its tool marked. Its own unedited placeholder is
|
|
456
504
|
* the only thing it edits: an operator's later edit, removal or rollback
|
|
457
505
|
* stands.
|
|
458
506
|
*
|
|
@@ -540,7 +588,7 @@ export class ToolResultGuard {
|
|
|
540
588
|
spills.set(index, spill);
|
|
541
589
|
}
|
|
542
590
|
const annotated: ContentBlock[] = pending.withheld.map((block, index) => block.type === 'tool_result'
|
|
543
|
-
? { ...block, content: withheldResultNotice(spills.get(index) ?? null, pending.durable) }
|
|
591
|
+
? { ...block, content: withheldResultNotice(spills.get(index) ?? null, pending.durable, pending.kept.get(index)) }
|
|
544
592
|
: block);
|
|
545
593
|
try {
|
|
546
594
|
this.append({
|
package/src/types/agent.ts
CHANGED
|
@@ -53,11 +53,14 @@ export interface AgentConfig {
|
|
|
53
53
|
/**
|
|
54
54
|
* The model's PHYSICAL context window (provider hard cap, e.g. 200000).
|
|
55
55
|
* When set, the framework projects each continuation round's real size
|
|
56
|
-
* (cache-inclusive input of the
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
56
|
+
* (cache-inclusive input of the latest call that reported its prompt +
|
|
57
|
+
* that call's output + what the stream has grown by since, if later calls
|
|
58
|
+
* reported none + the blocks about to be appended + reserve for response)
|
|
59
|
+
* and restarts the stream through a fresh compile instead of dispatching a
|
|
60
|
+
* request the provider will 400 (issue #92: a legal compile can walk past
|
|
61
|
+
* the physical window mid-turn). The projection runs once a call on the
|
|
62
|
+
* stream has reported its prompt. Unset → no projection; only the
|
|
63
|
+
* maxStreamTokens restart applies.
|
|
61
64
|
*/
|
|
62
65
|
physicalWindowTokens?: number;
|
|
63
66
|
|
|
@@ -223,7 +226,20 @@ export interface AgentRuntimeSettingsPatch {
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
export interface AgentRuntimeSettingsSnapshot {
|
|
229
|
+
/**
|
|
230
|
+
* The budget the agent is set to: while a lower one converges, its target,
|
|
231
|
+
* and while a persisted one the agent couldn't apply is kept, that one.
|
|
232
|
+
*/
|
|
226
233
|
contextBudgetTokens: number;
|
|
234
|
+
/**
|
|
235
|
+
* The budget compiles plan at now: the live budget, or the default when the
|
|
236
|
+
* recipe sets none (the context manager's own default is the same). It
|
|
237
|
+
* equals `contextBudgetTokens` except while a lower budget converges, when
|
|
238
|
+
* it is still the budget the descent started from (context-manager #162),
|
|
239
|
+
* and while a budget the agent couldn't apply is kept, when it is the
|
|
240
|
+
* budget the agent runs at instead.
|
|
241
|
+
*/
|
|
242
|
+
effectiveContextBudgetTokens: number;
|
|
227
243
|
tailTokens?: number;
|
|
228
244
|
transitionPaceTokens?: number;
|
|
229
245
|
sameRoundThinkTextPolicy: SameRoundThinkTextPolicy;
|
|
@@ -233,7 +249,27 @@ export interface AgentRuntimeSettingsSnapshot {
|
|
|
233
249
|
}
|
|
234
250
|
|
|
235
251
|
/** Persisted values differ from recipe defaults; omitted keys inherit config. */
|
|
236
|
-
export type AgentRuntimeSettingsOverrides = AgentRuntimeSettingsPatch
|
|
252
|
+
export type AgentRuntimeSettingsOverrides = AgentRuntimeSettingsPatch & {
|
|
253
|
+
/**
|
|
254
|
+
* The record's own `contextBudgetTokens`, written when a compile at the
|
|
255
|
+
* agent's live budget fits at it, with nothing about the budget changed
|
|
256
|
+
* while it ran (a paced descent's settle included): the store fit that
|
|
257
|
+
* budget. A compile the context manager refuses at it as over budget
|
|
258
|
+
* withdraws it, unless a compile at it that started later has already
|
|
259
|
+
* ended.
|
|
260
|
+
* When it equals `contextBudgetTokens`, a restore applies that budget at
|
|
261
|
+
* once, rather than pacing a descent again from the recipe's budget
|
|
262
|
+
* (context-manager #162), if a preview at boot says it still fits; if not,
|
|
263
|
+
* a budget below the recipe's restores paced, and if a compile at one that
|
|
264
|
+
* the boot applied at once is refused, the agent restores it paced then.
|
|
265
|
+
* Since the store fit it when the field was written, the planner can fold
|
|
266
|
+
* to it at once even if its carried frontier was lost. Any other value is
|
|
267
|
+
* stale (the budget changed since, or an earlier release carried the field
|
|
268
|
+
* forward), and a budget below the recipe's restores paced, as it does
|
|
269
|
+
* without the field. Never part of a patch.
|
|
270
|
+
*/
|
|
271
|
+
contextBudgetReachedTokens?: number;
|
|
272
|
+
};
|
|
237
273
|
|
|
238
274
|
/**
|
|
239
275
|
* Result of running inference.
|
package/src/types/events.ts
CHANGED
|
@@ -199,6 +199,18 @@ export interface ToolResult {
|
|
|
199
199
|
* Adapted from Anarchid/agent-framework@mcpl-module-proto.
|
|
200
200
|
*/
|
|
201
201
|
endTurn?: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* Fields of `data` that say what the tool acted on, such as the paths a
|
|
204
|
+
* write wrote, removed or took from disk, the path it deleted, or a mount
|
|
205
|
+
* whose root it accepted, in the order they should be kept. When the
|
|
206
|
+
* tool-result guard withholds this result, these fields stay in its stub,
|
|
207
|
+
* so the agent still sees what the tool did; the result as stored goes
|
|
208
|
+
* where every withheld original goes (agent-framework #277). Past the
|
|
209
|
+
* inline cap they're cut, in this order, so a later field can be cut away
|
|
210
|
+
* entirely; put the short, telling ones first. Only data's own fields can
|
|
211
|
+
* be kept, and a failed result (success false) keeps nothing.
|
|
212
|
+
*/
|
|
213
|
+
keepWhenWithheld?: string[];
|
|
202
214
|
}
|
|
203
215
|
|
|
204
216
|
/**
|
package/src/types/framework.ts
CHANGED
|
@@ -302,6 +302,10 @@ export interface ContextMaintenanceAgentRun {
|
|
|
302
302
|
pendingAfter?: string;
|
|
303
303
|
progressBefore?: Record<string, unknown>;
|
|
304
304
|
progressAfter?: Record<string, unknown>;
|
|
305
|
+
/**
|
|
306
|
+
* The agent's failure in this pass: a tick that threw, or its readiness
|
|
307
|
+
* that couldn't be read, before the ticks or after them.
|
|
308
|
+
*/
|
|
305
309
|
error?: string;
|
|
306
310
|
}
|
|
307
311
|
|
|
@@ -323,6 +327,12 @@ export interface ContextMaintenanceSnapshot {
|
|
|
323
327
|
agentName: string;
|
|
324
328
|
ready: boolean;
|
|
325
329
|
pending?: string;
|
|
330
|
+
/**
|
|
331
|
+
* Why the agent's readiness can't be read: the context manager refuses
|
|
332
|
+
* while its strategy's initialization is pending or has failed, and a
|
|
333
|
+
* strategy can refuse on its own. `ready` is false then.
|
|
334
|
+
*/
|
|
335
|
+
error?: string;
|
|
326
336
|
progress?: Record<string, unknown>;
|
|
327
337
|
}>;
|
|
328
338
|
}
|
package/src/types/module.ts
CHANGED
|
@@ -217,14 +217,19 @@ export interface ModuleContext {
|
|
|
217
217
|
getLogLength(name: string): number;
|
|
218
218
|
|
|
219
219
|
/**
|
|
220
|
-
* Process queue for pushing events from external listeners.
|
|
220
|
+
* Process queue for pushing events from external listeners. Once stop()
|
|
221
|
+
* has begun, a push is dropped with a log line naming this module, as
|
|
222
|
+
* pushEvent()'s is.
|
|
221
223
|
* @deprecated Use pushEvent() instead — it also emits process:received traces.
|
|
222
224
|
*/
|
|
223
225
|
readonly queue: ProcessQueue;
|
|
224
226
|
|
|
225
227
|
/**
|
|
226
228
|
* Push a process event to the queue and emit a process:received trace.
|
|
227
|
-
* Preferred over direct queue.push() for proper observability.
|
|
229
|
+
* Preferred over direct queue.push() for proper observability. Once stop()
|
|
230
|
+
* has begun, nothing will process the event: it's dropped with a log line
|
|
231
|
+
* naming this module, untraced, rather than thrown into the listener or
|
|
232
|
+
* stop() that pushed it.
|
|
228
233
|
*/
|
|
229
234
|
pushEvent(event: ProcessEvent): void;
|
|
230
235
|
|
|
@@ -316,10 +321,14 @@ export interface ModuleContext {
|
|
|
316
321
|
*
|
|
317
322
|
* Modules use this to react to agent lifecycle without polling — e.g. an
|
|
318
323
|
* adapter that wants to show a "typing" indicator can bracket it between
|
|
319
|
-
* `inference:started` and the terminal events
|
|
320
|
-
*
|
|
321
|
-
*
|
|
322
|
-
*
|
|
324
|
+
* `inference:started` and the terminal events: `inference:completed` (the
|
|
325
|
+
* normal end), `inference:turn_ended` (a tool result ended the turn),
|
|
326
|
+
* `inference:aborted`, `inference:failed` and `inference:exhausted`, all of
|
|
327
|
+
* which carry `agentName`. A turn's tool-call cycles stay inside its
|
|
328
|
+
* stream, so one bracket spans them. A context-budget restart emits
|
|
329
|
+
* `inference:started` again with no terminal event for the stream it
|
|
330
|
+
* replaced, and a failing turn can emit `inference:failed` and then
|
|
331
|
+
* `inference:exhausted`, so a bracket should allow a repeated start or end.
|
|
323
332
|
*
|
|
324
333
|
* Returns an unsubscribe function. Call it in the module's `stop()` to avoid
|
|
325
334
|
* leaking listeners across teardown/recreate (e.g. session switch).
|
|
@@ -370,14 +379,17 @@ export interface ProcessState {
|
|
|
370
379
|
getActiveTools(): ToolDefinition[];
|
|
371
380
|
|
|
372
381
|
/**
|
|
373
|
-
* Queue for emitting follow-up events.
|
|
382
|
+
* Queue for emitting follow-up events. Once stop() has begun, a push is
|
|
383
|
+
* dropped with a log line naming the module, as pushEvent()'s is.
|
|
374
384
|
* @deprecated Use pushEvent() instead — it also emits process:received traces.
|
|
375
385
|
*/
|
|
376
386
|
readonly queue: ProcessQueue;
|
|
377
387
|
|
|
378
388
|
/**
|
|
379
389
|
* Push a process event to the queue and emit a process:received trace.
|
|
380
|
-
* Preferred over direct queue.push() for proper observability.
|
|
390
|
+
* Preferred over direct queue.push() for proper observability. Once stop()
|
|
391
|
+
* has begun, the event is dropped with a log line naming the module,
|
|
392
|
+
* untraced.
|
|
381
393
|
*/
|
|
382
394
|
pushEvent(event: ProcessEvent): void;
|
|
383
395
|
}
|