@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
package/src/index.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export * from './types/index.js';
|
|
3
3
|
|
|
4
4
|
// Core classes
|
|
5
|
-
export { AgentFramework, BudgetPreflightError, ResumeBlockedError } from './framework.js';
|
|
5
|
+
export { AgentFramework, BudgetPreflightError, ProviderWaitNotSleptError, ResumeBlockedError } from './framework.js';
|
|
6
6
|
export type { RuntimeSettingsPreview, HostModeStatus } from './framework.js';
|
|
7
7
|
export { Agent } from './agent.js';
|
|
8
8
|
export type { StartStreamResult } from './agent.js';
|
|
@@ -17,7 +17,7 @@ export * from './modules/index.js';
|
|
|
17
17
|
|
|
18
18
|
// API
|
|
19
19
|
export { ApiServer } from './api/server.js';
|
|
20
|
-
export { McpServer } from './api/mcp-server.js';
|
|
20
|
+
export { McpServer } from './api/mcp-server-core.js';
|
|
21
21
|
export type {
|
|
22
22
|
ApiServerConfig,
|
|
23
23
|
ApiMessage,
|
|
@@ -92,7 +92,7 @@ export type { McplServerConfig } from './mcpl/index.js';
|
|
|
92
92
|
// health probes, protocol harnesses — can dial an MCPL server exactly the
|
|
93
93
|
// way the framework host does, over stdio or WebSocket)
|
|
94
94
|
export { McplServerConnection } from './mcpl/index.js';
|
|
95
|
-
export type { McplHostCapabilities } from './mcpl/index.js';
|
|
95
|
+
export type { McplHostCapabilities, McplConnectionCloseInfo } from './mcpl/index.js';
|
|
96
96
|
|
|
97
97
|
// MCPL tool lifecycle (RFC-007) and tool classes (RFC-008): the types a host
|
|
98
98
|
// needs to write McplServerConfig.toolLifecycle policy and class tables, and
|
|
@@ -143,7 +143,12 @@ export type {
|
|
|
143
143
|
} from './recovery/discord-awareness-outbox.js';
|
|
144
144
|
// Live operator surgery (rollback / suppress) and its durable action log
|
|
145
145
|
export { OperatorLog, OperatorActionError, defaultOperatorLogPath } from './operator-log.js';
|
|
146
|
-
export type {
|
|
146
|
+
export type {
|
|
147
|
+
OperatorLogEntry,
|
|
148
|
+
OperatorLogInput,
|
|
149
|
+
OperatorRequester,
|
|
150
|
+
SurgeryMarkerReceipt,
|
|
151
|
+
} from './operator-log.js';
|
|
147
152
|
export { createOfflineRecoveryBranch } from './recovery/offline-branch.js';
|
|
148
153
|
export type {
|
|
149
154
|
OfflineRecoveryBranchOptions,
|
|
@@ -37,8 +37,10 @@ import type { McplServerRegistry } from './server-registry.js';
|
|
|
37
37
|
import type { FeatureSetManager } from './feature-set-manager.js';
|
|
38
38
|
import type { ToolDefinition, ToolResult, ProcessEvent } from '../types/index.js';
|
|
39
39
|
import { expandCoreTags } from './tags.js';
|
|
40
|
-
import { EmptyContentError, validateCoalescedContent } from './push-coalescer.js';
|
|
40
|
+
import { coalesceRejectionReason, EmptyContentError, validateCoalescedContent } from './push-coalescer.js';
|
|
41
41
|
import { isVisiblyEmptyContent } from './visible-content.js';
|
|
42
|
+
import { logDetail, logValue } from './log-value.js';
|
|
43
|
+
import { RefusalLines } from './refusal-lines.js';
|
|
42
44
|
import { CapabilityGrant } from './capability-grant.js';
|
|
43
45
|
|
|
44
46
|
// ============================================================================
|
|
@@ -249,7 +251,9 @@ interface McplChannelIncomingEvent {
|
|
|
249
251
|
function convertBlock(block: McplContentBlock): ContentBlock {
|
|
250
252
|
switch (block.type) {
|
|
251
253
|
case 'text':
|
|
252
|
-
|
|
254
|
+
// Text that isn't a string is no text, as the request sanitizer and
|
|
255
|
+
// the visible-content check already read it.
|
|
256
|
+
return { type: 'text', text: typeof block.text === 'string' ? block.text : '' };
|
|
253
257
|
|
|
254
258
|
case 'image':
|
|
255
259
|
if (isInlineContradiction(block)) {
|
|
@@ -257,24 +261,24 @@ function convertBlock(block: McplContentBlock): ContentBlock {
|
|
|
257
261
|
// closed, withhold the data (checked BEFORE the data branch).
|
|
258
262
|
return { type: 'text', text: INLINE_WITHHELD_TEXT };
|
|
259
263
|
}
|
|
260
|
-
if (block.uri && block.disposition) {
|
|
264
|
+
if (isFilledString(block.uri) && block.disposition) {
|
|
261
265
|
// RFC-005 uri-form media with a disposition claim: stub, do not
|
|
262
266
|
// hand the URI to the provider or inline it.
|
|
263
267
|
return { type: 'text', text: referenceStubOrNull(block, 'attachment on channel message') ?? '[reference]' };
|
|
264
268
|
}
|
|
265
|
-
if (block.data && block.mimeType) {
|
|
269
|
+
if (isFilledString(block.data) && isFilledString(block.mimeType)) {
|
|
266
270
|
return {
|
|
267
271
|
type: 'image',
|
|
268
272
|
source: { type: 'base64', data: block.data, mediaType: block.mimeType },
|
|
269
273
|
} as ContentBlock;
|
|
270
274
|
}
|
|
271
|
-
if (block.uri) {
|
|
275
|
+
if (isFilledString(block.uri)) {
|
|
272
276
|
return {
|
|
273
277
|
type: 'image',
|
|
274
278
|
source: { type: 'url', url: block.uri },
|
|
275
279
|
} as ContentBlock;
|
|
276
280
|
}
|
|
277
|
-
return { type: 'text', text: '[Image: no data]' };
|
|
281
|
+
return { type: 'text', text: isFilledString(block.data) ? '[Image: no media type]' : '[Image: no data]' };
|
|
278
282
|
|
|
279
283
|
case 'audio':
|
|
280
284
|
if (isInlineContradiction(block)) {
|
|
@@ -282,31 +286,43 @@ function convertBlock(block: McplContentBlock): ContentBlock {
|
|
|
282
286
|
// closed, withhold the data (checked BEFORE the data branch).
|
|
283
287
|
return { type: 'text', text: INLINE_WITHHELD_TEXT };
|
|
284
288
|
}
|
|
285
|
-
if (block.uri && block.disposition) {
|
|
289
|
+
if (isFilledString(block.uri) && block.disposition) {
|
|
286
290
|
// RFC-005 uri-form media with a disposition claim: stub, do not
|
|
287
291
|
// hand the URI to the provider or inline it.
|
|
288
292
|
return { type: 'text', text: referenceStubOrNull(block, 'attachment on channel message') ?? '[reference]' };
|
|
289
293
|
}
|
|
290
|
-
if (block.data && block.mimeType) {
|
|
294
|
+
if (isFilledString(block.data) && isFilledString(block.mimeType)) {
|
|
291
295
|
return {
|
|
292
296
|
type: 'audio',
|
|
293
297
|
source: { type: 'base64', data: block.data, mediaType: block.mimeType },
|
|
294
298
|
} as ContentBlock;
|
|
295
299
|
}
|
|
296
|
-
return { type: 'text', text: '[Audio: no data]' };
|
|
300
|
+
return { type: 'text', text: isFilledString(block.data) ? '[Audio: no media type]' : '[Audio: no data]' };
|
|
297
301
|
|
|
298
302
|
case 'resource':
|
|
299
303
|
// RFC-005: reference blocks become bounded stubs — never raw URIs
|
|
300
304
|
// (a signed URL is a bearer credential that looks like a location).
|
|
301
305
|
return { type: 'text', text: referenceStubOrNull(block, 'attachment on channel message') ?? '[reference]' };
|
|
302
306
|
|
|
303
|
-
default:
|
|
307
|
+
default: {
|
|
304
308
|
// Unknown wire block types previously fell off the exhaustive switch
|
|
305
|
-
// and propagated `undefined` into ContentBlock[]. Fail visibly.
|
|
306
|
-
|
|
309
|
+
// and propagated `undefined` into ContentBlock[]. Fail visibly. A type
|
|
310
|
+
// that isn't a string is no type, and is never converted to one: an
|
|
311
|
+
// object can throw when it is.
|
|
312
|
+
const type: unknown = (block as { type?: unknown }).type;
|
|
313
|
+
return { type: 'text', text: `[unrecognized content block: ${typeof type === 'string' ? type : 'untyped'}]` };
|
|
314
|
+
}
|
|
307
315
|
}
|
|
308
316
|
}
|
|
309
317
|
|
|
318
|
+
/** A string with something in it: the only data, mimeType or uri a block's
|
|
319
|
+
* source is built from, and the only uri that makes it a reference. Anything
|
|
320
|
+
* else a sender puts there leaves the block without it, so conversion never
|
|
321
|
+
* throws and never copies a non-string. */
|
|
322
|
+
function isFilledString(value: unknown): value is string {
|
|
323
|
+
return typeof value === 'string' && value !== '';
|
|
324
|
+
}
|
|
325
|
+
|
|
310
326
|
// ============================================================================
|
|
311
327
|
// Channel Tool Definitions
|
|
312
328
|
// ============================================================================
|
|
@@ -512,17 +528,19 @@ interface ChannelRegistryOptions {
|
|
|
512
528
|
*/
|
|
513
529
|
homeChannelResolver?: (agentName: string) => string | undefined;
|
|
514
530
|
/**
|
|
515
|
-
* Resolve
|
|
516
|
-
*
|
|
531
|
+
* Resolve an agent's channel for its CURRENT inference turn, by agent name:
|
|
532
|
+
* the turn's locus once it has one, else the channel that triggered the
|
|
533
|
+
* turn. This is the fix for single-TRUNK agents (the only mode
|
|
517
534
|
* connectome-host runs — it never exposes conversation forks). A trunk agent
|
|
518
535
|
* has no fork home, so without this its plain-text speech falls back to the
|
|
519
536
|
* process-global `defaultPublishChannel`, which tracks the most-recent inbound
|
|
520
537
|
* across ALL channels and misroutes a reply to whichever channel last spoke
|
|
521
|
-
* under concurrency (item-3 redux). The framework
|
|
522
|
-
*
|
|
523
|
-
*
|
|
524
|
-
* global fallback
|
|
525
|
-
* wins), BEFORE
|
|
538
|
+
* under concurrency (item-3 redux). The framework keeps both on the live
|
|
539
|
+
* turn's routing state and exposes them here. For a heartbeat / no-trigger
|
|
540
|
+
* turn it resolves to undefined until the turn's locus is resolved, so that
|
|
541
|
+
* resolution takes the global fallback, which the turn then keeps.
|
|
542
|
+
* Consulted AFTER `homeChannelResolver` (a fork's home always wins), BEFORE
|
|
543
|
+
* `defaultPublishChannel`.
|
|
526
544
|
*/
|
|
527
545
|
activeChannelResolver?: (agentName: string) => string | undefined;
|
|
528
546
|
}
|
|
@@ -577,6 +595,10 @@ export class ChannelRegistry {
|
|
|
577
595
|
/** Registered channels, keyed by `{serverId}:{channelId}`. */
|
|
578
596
|
private channels = new Map<string, ChannelEntry>();
|
|
579
597
|
|
|
598
|
+
/** Holds back repeats of the line for a message refused because its
|
|
599
|
+
* channel isn't registered (RefusalLines). */
|
|
600
|
+
private unknownChannelLines = new RefusalLines();
|
|
601
|
+
|
|
580
602
|
/**
|
|
581
603
|
* Most-recently-known label per channelId, replayed from
|
|
582
604
|
* `CHANNEL_LABEL_HISTORY_LOG_ID` (last sighting wins) and kept current by
|
|
@@ -848,6 +870,45 @@ export class ChannelRegistry {
|
|
|
848
870
|
});
|
|
849
871
|
}
|
|
850
872
|
|
|
873
|
+
/**
|
|
874
|
+
* A coalesced message the host refused. Until now the host said nothing, so
|
|
875
|
+
* a dropped message (agent-framework#266: every Discord message with a
|
|
876
|
+
* screenshot) left no trace where operators look. The sender gets the
|
|
877
|
+
* per-message result this lane gives: `coalesce_invalid` for a malformed
|
|
878
|
+
* item, or the thrown error's message for anything else. `answered` is the
|
|
879
|
+
* code that result stands for (-32602 for `coalesce_invalid`), and the
|
|
880
|
+
* reason follows it (coalesceRejectionReason): `coalesce-failed` when the
|
|
881
|
+
* host itself failed, `coalesce-invalid` when the sender's item was
|
|
882
|
+
* refused, with the failing `field` and the error's message as `detail`.
|
|
883
|
+
* The line prints the ids the sender sent, whatever their type; the trace
|
|
884
|
+
* carries them when they are strings.
|
|
885
|
+
*/
|
|
886
|
+
private traceCoalesceRejection(
|
|
887
|
+
serverId: string,
|
|
888
|
+
message: { channelId?: unknown; messageId?: unknown } | null | undefined,
|
|
889
|
+
error: { field?: unknown; message: string },
|
|
890
|
+
answered: number,
|
|
891
|
+
): void {
|
|
892
|
+
const reason = coalesceRejectionReason(answered);
|
|
893
|
+
const field = typeof error.field === 'string' && error.field ? error.field : undefined;
|
|
894
|
+
const channelId = typeof message?.channelId === 'string' ? message.channelId : '';
|
|
895
|
+
const messageId = typeof message?.messageId === 'string' && message.messageId ? message.messageId : undefined;
|
|
896
|
+
console.error(
|
|
897
|
+
`[channel-incoming-rejected] server=${serverId} channel=${logValue(message?.channelId ?? '')} ` +
|
|
898
|
+
`messageId=${logValue(message?.messageId ?? '')} ` +
|
|
899
|
+
`reason=${reason}${field ? ` field=${logValue(field)}` : ''}: ${logDetail(error.message)}`,
|
|
900
|
+
);
|
|
901
|
+
this.emitTraceFn({
|
|
902
|
+
type: 'mcpl:channel-incoming-rejected',
|
|
903
|
+
serverId,
|
|
904
|
+
channelId,
|
|
905
|
+
...(messageId ? { messageId } : {}),
|
|
906
|
+
reason,
|
|
907
|
+
...(field ? { field } : {}),
|
|
908
|
+
detail: error.message,
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
|
|
851
912
|
/**
|
|
852
913
|
* Handle `channels/incoming` from a server.
|
|
853
914
|
*
|
|
@@ -864,10 +925,20 @@ export class ChannelRegistry {
|
|
|
864
925
|
for (const message of params.messages) {
|
|
865
926
|
if (!message || typeof message.channelId !== 'string' || !message.channelId
|
|
866
927
|
|| typeof message.messageId !== 'string' || !message.messageId) {
|
|
928
|
+
// The same gate as admission below: with no coalescer wired, a
|
|
929
|
+
// message carrying `coalesce` is handled uncoalesced, and refused as
|
|
930
|
+
// any uncoalesced message without an identity is.
|
|
931
|
+
const coalescedItem = message?.coalesce !== undefined && !!this.handleCoalescedIncoming;
|
|
932
|
+
if (coalescedItem) {
|
|
933
|
+
this.traceCoalesceRejection(serverId, message, {
|
|
934
|
+
field: typeof message.channelId !== 'string' || !message.channelId ? 'channelId' : 'messageId',
|
|
935
|
+
message: 'a coalesced message needs a channelId and a messageId',
|
|
936
|
+
}, -32602);
|
|
937
|
+
}
|
|
867
938
|
results.push({
|
|
868
939
|
messageId: typeof message?.messageId === 'string' ? message.messageId : '',
|
|
869
940
|
accepted: false,
|
|
870
|
-
reason:
|
|
941
|
+
reason: coalescedItem ? 'coalesce_invalid' : 'invalid channel/message identity',
|
|
871
942
|
});
|
|
872
943
|
continue;
|
|
873
944
|
}
|
|
@@ -899,11 +970,24 @@ export class ChannelRegistry {
|
|
|
899
970
|
// routable entry rather than an open self-attested one.
|
|
900
971
|
const incomingKey = `${serverId}:${message.channelId}`;
|
|
901
972
|
if (!this.channels.has(incomingKey)) {
|
|
973
|
+
// Loud, coalesced or not, like every other refusal of a message, but
|
|
974
|
+
// at most once a minute per channel (RefusalLines): a server that
|
|
975
|
+
// never registered a busy channel would otherwise print a line for
|
|
976
|
+
// every message in it. Every refusal is traced.
|
|
977
|
+
const held = this.unknownChannelLines.admit(serverId, message.channelId);
|
|
978
|
+
if (held !== null) {
|
|
979
|
+
console.error(
|
|
980
|
+
`[channel-incoming-rejected] server=${serverId} channel=${logValue(message.channelId)} ` +
|
|
981
|
+
`messageId=${logValue(message.messageId)} reason=unknown-channel ` +
|
|
982
|
+
`(not registered, §14.5${held ? `; ${held} more since the last line` : ''})`,
|
|
983
|
+
);
|
|
984
|
+
}
|
|
902
985
|
this.emitTraceFn({
|
|
903
986
|
type: 'mcpl:channel-incoming-rejected',
|
|
904
987
|
serverId,
|
|
905
988
|
channelId: message.channelId,
|
|
906
|
-
|
|
989
|
+
messageId: message.messageId,
|
|
990
|
+
reason: 'unknown-channel',
|
|
907
991
|
});
|
|
908
992
|
results.push({
|
|
909
993
|
messageId: message.messageId,
|
|
@@ -921,7 +1005,7 @@ export class ChannelRegistry {
|
|
|
921
1005
|
// shows the model nothing would wake it with no visible cause.
|
|
922
1006
|
const emptyAllowed = coalesced && (message.coalesce as { retract?: unknown } | null)?.retract === true;
|
|
923
1007
|
const rejectEmpty = () => {
|
|
924
|
-
console.error(`[channel-incoming-rejected] server=${serverId} channel=${message.channelId} messageId=${message.messageId} reason=empty-content`);
|
|
1008
|
+
console.error(`[channel-incoming-rejected] server=${serverId} channel=${logValue(message.channelId)} messageId=${logValue(message.messageId)} reason=empty-content`);
|
|
925
1009
|
this.emitTraceFn({
|
|
926
1010
|
type: 'mcpl:channel-incoming-rejected',
|
|
927
1011
|
serverId,
|
|
@@ -935,10 +1019,14 @@ export class ChannelRegistry {
|
|
|
935
1019
|
// RFC-006 §13: malformed content on a coalesced item is that item's
|
|
936
1020
|
// failure, not the batch's — check the shape before converting.
|
|
937
1021
|
try {
|
|
938
|
-
validateCoalescedContent(message.content,
|
|
1022
|
+
validateCoalescedContent(message.content, { allowEmpty: emptyAllowed });
|
|
939
1023
|
} catch (error) {
|
|
940
1024
|
if (error instanceof EmptyContentError) rejectEmpty();
|
|
941
|
-
else
|
|
1025
|
+
else {
|
|
1026
|
+
// Answered coalesce_invalid whatever the error's code: -32602.
|
|
1027
|
+
this.traceCoalesceRejection(serverId, message, error as Error & { field?: string }, -32602);
|
|
1028
|
+
results.push({ messageId: message.messageId, accepted: false, reason: 'coalesce_invalid' });
|
|
1029
|
+
}
|
|
942
1030
|
continue;
|
|
943
1031
|
}
|
|
944
1032
|
}
|
|
@@ -970,8 +1058,11 @@ export class ChannelRegistry {
|
|
|
970
1058
|
// Determine whether to trigger inference
|
|
971
1059
|
let triggerInference = true;
|
|
972
1060
|
if (this.shouldTriggerInference) {
|
|
973
|
-
|
|
974
|
-
|
|
1061
|
+
// The text the agent's context gets, as the push lane's filter reads
|
|
1062
|
+
// it: the converted text blocks, a placeholder or reference stub
|
|
1063
|
+
// among them, and a text block's non-string text as empty text.
|
|
1064
|
+
const textContent = convertedContent
|
|
1065
|
+
.filter((b): b is ContentBlock & { type: 'text' } => b.type === 'text')
|
|
975
1066
|
.map((b) => b.text)
|
|
976
1067
|
.join('\n');
|
|
977
1068
|
triggerInference = this.shouldTriggerInference(
|
|
@@ -1014,7 +1105,10 @@ export class ChannelRegistry {
|
|
|
1014
1105
|
if (result.accepted) markAccepted();
|
|
1015
1106
|
results.push(result);
|
|
1016
1107
|
} catch (error) {
|
|
1017
|
-
const err = error as Error & { code?: number };
|
|
1108
|
+
const err = error as Error & { code?: number; field?: string };
|
|
1109
|
+
// -32602 is answered coalesce_invalid; anything else as its message,
|
|
1110
|
+
// a plain Error standing for an internal error (-32603).
|
|
1111
|
+
this.traceCoalesceRejection(serverId, message, err, typeof err.code === 'number' ? err.code : -32603);
|
|
1018
1112
|
results.push({
|
|
1019
1113
|
messageId: message.messageId,
|
|
1020
1114
|
accepted: false,
|
|
@@ -1361,8 +1455,9 @@ export class ChannelRegistry {
|
|
|
1361
1455
|
|
|
1362
1456
|
// Resolve the outbound locus the SAME way routeSpeech does, so the agent is
|
|
1363
1457
|
// told the channel its speech will actually land in: a conversation fork's
|
|
1364
|
-
// home channel, else this turn's
|
|
1365
|
-
//
|
|
1458
|
+
// home channel, else this turn's channel (single trunk agent — item-3
|
|
1459
|
+
// redux: its locus, else its triggering channel), else the global default
|
|
1460
|
+
// (heartbeats). Without this, a fork
|
|
1366
1461
|
// was advertised the global locus but published somewhere else; and a trunk
|
|
1367
1462
|
// agent was told the wrong channel under concurrency.
|
|
1368
1463
|
const home = agentName ? this.homeChannelResolver?.(agentName) : undefined;
|
|
@@ -3018,7 +3113,7 @@ export class ChannelRegistry {
|
|
|
3018
3113
|
try {
|
|
3019
3114
|
await this.openChannelNow(entry, 'opened-by-delivery');
|
|
3020
3115
|
console.error(
|
|
3021
|
-
`[routeSpeech] ${conversationId}: locus ${channelId} was closed — opened by delivery (speech implies engagement)`,
|
|
3116
|
+
`[routeSpeech] ${conversationId}: locus ${logValue(channelId)} was closed — opened by delivery (speech implies engagement)`,
|
|
3022
3117
|
);
|
|
3023
3118
|
this.emitTraceFn({
|
|
3024
3119
|
type: 'mcpl:channel-opened-by-send',
|
|
@@ -3071,7 +3166,7 @@ export class ChannelRegistry {
|
|
|
3071
3166
|
return fail(channelId, `server "${entry.serverId}" reported delivered:false for "${channelId}"`);
|
|
3072
3167
|
}
|
|
3073
3168
|
|
|
3074
|
-
console.error(`[routeSpeech] ${conversationId}: routed ${text.length} chars -> ${channelId} (server=${entry.serverId}, delivered=${delivered})`);
|
|
3169
|
+
console.error(`[routeSpeech] ${conversationId}: routed ${text.length} chars -> ${logValue(channelId)} (server=${entry.serverId}, delivered=${delivered})`);
|
|
3075
3170
|
this.emitTraceFn({
|
|
3076
3171
|
type: 'mcpl:speech-routed',
|
|
3077
3172
|
conversationId,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
UNKNOWN_FEATURE_SET,
|
|
20
20
|
} from './errors.js';
|
|
21
21
|
import { isKnownCapabilityPath } from './capability-grant.js';
|
|
22
|
+
import { logValue } from './log-value.js';
|
|
22
23
|
|
|
23
24
|
// ============================================================================
|
|
24
25
|
// Error Class
|
|
@@ -222,7 +223,7 @@ export class FeatureSetManager {
|
|
|
222
223
|
): void => {
|
|
223
224
|
const selectedByConfig = state.enabled.delete(name);
|
|
224
225
|
const note = explicitSelection && selectedByConfig ? ' — overrides enabledFeatureSets' : '';
|
|
225
|
-
console.error(`[mcpl] ${serverId}/${name} disabled: ${detail}${note}`);
|
|
226
|
+
console.error(`[mcpl] ${serverId}/${logValue(name)} disabled: ${detail}${note}`);
|
|
226
227
|
this.emitTrace?.({
|
|
227
228
|
type: 'mcpl:feature-set-disabled',
|
|
228
229
|
serverId,
|
|
@@ -240,14 +241,14 @@ export class FeatureSetManager {
|
|
|
240
241
|
}
|
|
241
242
|
const unknown = uses.filter((u) => typeof u !== 'string' || !isKnownCapabilityPath(u));
|
|
242
243
|
if (unknown.length > 0) {
|
|
243
|
-
disable(name, 'invalid_uses', `invalid_uses (§6.4 — unrecognized: ${unknown.join(', ')})`, {
|
|
244
|
+
disable(name, 'invalid_uses', `invalid_uses (§6.4 — unrecognized: ${unknown.map(logValue).join(', ')})`, {
|
|
244
245
|
unrecognized: unknown.map((u) => (typeof u === 'string' ? u : JSON.stringify(u))),
|
|
245
246
|
});
|
|
246
247
|
continue;
|
|
247
248
|
}
|
|
248
249
|
const missing = (uses as string[]).filter((u) => !grant.has(u));
|
|
249
250
|
if (missing.length > 0) {
|
|
250
|
-
disable(name, 'missing_capabilities', `missing capabilities ${missing.join(', ')} (§6.4)`, { missing });
|
|
251
|
+
disable(name, 'missing_capabilities', `missing capabilities ${missing.map(logValue).join(', ')} (§6.4)`, { missing });
|
|
251
252
|
}
|
|
252
253
|
}
|
|
253
254
|
}
|
package/src/mcpl/index.ts
CHANGED
|
@@ -107,6 +107,8 @@ export {
|
|
|
107
107
|
|
|
108
108
|
// Server connection and registry
|
|
109
109
|
export { McplServerConnection } from './server-connection.js';
|
|
110
|
+
export type { McplConnectionCloseInfo } from './server-connection.js';
|
|
111
|
+
export type { TransportCloseInfo } from './transport.js';
|
|
110
112
|
export { McplServerRegistry, type McplCapabilityQuery } from './server-registry.js';
|
|
111
113
|
|
|
112
114
|
// Feature set management (permission layer)
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A value a connected server chose, as one field of a host log line.
|
|
3
|
+
*
|
|
4
|
+
* The rejection lines (`[channel-incoming-rejected]`, `[push-event-rejected]`)
|
|
5
|
+
* and the other host lines that name a server's own identifiers are read a
|
|
6
|
+
* line at a time, by eye and with grep. Printed raw, an identifier carrying a
|
|
7
|
+
* line break prints a second line that reads as another host record, about
|
|
8
|
+
* another server; one carrying a space can pose as another field of the same
|
|
9
|
+
* line; and one carrying `=` can hold a lookalike field (`reason=…`) that a
|
|
10
|
+
* search for that field finds before the real one.
|
|
11
|
+
*
|
|
12
|
+
* A plain token (visible characters only: no space, quote, backslash or `=`)
|
|
13
|
+
* prints as it is, so ordinary ids read and grep as they always have.
|
|
14
|
+
* Anything else prints as a JSON string with every `=` and every invisible
|
|
15
|
+
* character escaped: controls, format and bidi characters, line and paragraph
|
|
16
|
+
* separators, every space (the plain one included), default-ignorable
|
|
17
|
+
* characters (Hangul fillers, the combining grapheme joiner, variation
|
|
18
|
+
* selectors) and the blank Braille pattern. So it stays one field on one line,
|
|
19
|
+
* a reader that splits on whitespace keeps it whole, no field holds a raw
|
|
20
|
+
* `=` (so every `=` before a line's free-text tail, `logDetail`, belongs to
|
|
21
|
+
* a real field), and `JSON.parse` of a quoted value that isn't cut gives
|
|
22
|
+
* back exactly what was sent. Escaping rather than stripping keeps two
|
|
23
|
+
* identifiers that differ in invisible characters within the units a line
|
|
24
|
+
* prints from printing alike; canonically equivalent or lookalike
|
|
25
|
+
* identifiers still can.
|
|
26
|
+
*
|
|
27
|
+
* A value longer than 256 UTF-16 units is cut, so one value can't make a line
|
|
28
|
+
* long: it prints quoted, as its first 256 units (255 when the 256th is a
|
|
29
|
+
* high surrogate, so a pair is never split), each at most six characters once
|
|
30
|
+
* escaped, followed by `…(N)`, N being its full length. `JSON.parse` of the
|
|
31
|
+
* quoted part gives back those first units. A cut value always opens with a
|
|
32
|
+
* quote, and its marker follows the closing quote. A plain token never holds
|
|
33
|
+
* a quote, and a quoted value that isn't cut ends at its closing quote, so
|
|
34
|
+
* only a value that was cut prints as one.
|
|
35
|
+
*
|
|
36
|
+
* A value that isn't a string prints as its JSON text, quoted as any text is
|
|
37
|
+
* that isn't a plain token: the number 5 prints as `5`, and `{"toString":1}`
|
|
38
|
+
* as `"{\"toString\":1}"`. So the number 5 and the string "5" print alike, as
|
|
39
|
+
* they name the same message. A number JSON writes as `null` (NaN, Infinity)
|
|
40
|
+
* prints as `String()` of it. Converting a value never throws: one JSON can't
|
|
41
|
+
* write prints as `String()` of it, or else as its object tag, and one none
|
|
42
|
+
* of those can read (a revoked Proxy) as `[unprintable]`.
|
|
43
|
+
*/
|
|
44
|
+
export function logValue(value: unknown): string {
|
|
45
|
+
return printed(textOf(value), FIELD);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The host's own message, as the free text that ends a log line after its
|
|
50
|
+
* fields: `reason=coalesce-invalid field=payload.content: "content must be an
|
|
51
|
+
* array"`. It prints as `logValue` prints a field, cut the same way, except
|
|
52
|
+
* that a plain space and `=` stay raw, for reading. That is safe because the
|
|
53
|
+
* text is the host's (an error the host raised), not an id a sender chose,
|
|
54
|
+
* and every real field of the line comes before it.
|
|
55
|
+
*/
|
|
56
|
+
export function logDetail(value: unknown): string {
|
|
57
|
+
return printed(textOf(value), DETAIL);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** How a form prints: the text that stands as it is, and what it escapes. */
|
|
61
|
+
interface Form {
|
|
62
|
+
plain: RegExp;
|
|
63
|
+
escaped: RegExp;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const FIELD: Form = {
|
|
67
|
+
plain: /^[^"\\=\p{C}\p{Z}\p{Default_Ignorable_Code_Point}\u2800]+$/u,
|
|
68
|
+
escaped: /[=\p{C}\p{Z}\p{Default_Ignorable_Code_Point}\u2800]/gu,
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const DETAIL: Form = {
|
|
72
|
+
plain: /^[^"\\\p{C}\p{Z}\p{Default_Ignorable_Code_Point}\u2800]+$/u,
|
|
73
|
+
escaped: /(?! )[\p{C}\p{Z}\p{Default_Ignorable_Code_Point}\u2800]/gu,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/** The most of a value's text a line prints, in UTF-16 units. */
|
|
77
|
+
const MAX_PRINTED = 256;
|
|
78
|
+
|
|
79
|
+
function printed(text: string, form: Form): string {
|
|
80
|
+
if (text.length <= MAX_PRINTED) return form.plain.test(text) ? text : quoted(text, form);
|
|
81
|
+
let end = MAX_PRINTED;
|
|
82
|
+
const last = text.charCodeAt(end - 1);
|
|
83
|
+
if (last >= 0xd800 && last <= 0xdbff) end--;
|
|
84
|
+
return `${quoted(text.slice(0, end), form)}…(${text.length})`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function quoted(text: string, form: Form): string {
|
|
88
|
+
// JSON escapes C0 controls, quotes and backslashes and leaves the rest of
|
|
89
|
+
// the escaped set raw: escape those here, per UTF-16 unit, as JSON does.
|
|
90
|
+
return JSON.stringify(text).replace(form.escaped, escapeUnits);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function textOf(value: unknown): string {
|
|
94
|
+
if (typeof value === 'string') return value;
|
|
95
|
+
// JSON writes a non-finite number as null, which reads as a missing value.
|
|
96
|
+
if (typeof value === 'number' && !Number.isFinite(value)) return String(value);
|
|
97
|
+
const readers = [
|
|
98
|
+
() => JSON.stringify(value) ?? String(value),
|
|
99
|
+
() => String(value),
|
|
100
|
+
() => Object.prototype.toString.call(value),
|
|
101
|
+
];
|
|
102
|
+
for (const read of readers) {
|
|
103
|
+
try {
|
|
104
|
+
return read();
|
|
105
|
+
} catch {
|
|
106
|
+
// the next reader
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return '[unprintable]';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function escapeUnits(chars: string): string {
|
|
113
|
+
let out = '';
|
|
114
|
+
for (let i = 0; i < chars.length; i++) out += `\\u${chars.charCodeAt(i).toString(16).padStart(4, '0')}`;
|
|
115
|
+
return out;
|
|
116
|
+
}
|
|
@@ -21,13 +21,13 @@
|
|
|
21
21
|
*
|
|
22
22
|
* Three modes, mirroring proseRouting:
|
|
23
23
|
* - explicit: parse `>>` prefixes. A fresh turn's stream starts suppressed;
|
|
24
|
-
*
|
|
25
|
-
* each round's end returns the stream to it (retarget).
|
|
26
|
-
* - hybrid: parse `>>>` envelopes, with the
|
|
24
|
+
* a later stream of the turn starts at the turn's destination,
|
|
25
|
+
* and each round's end returns the stream to it (retarget).
|
|
26
|
+
* - hybrid: parse `>>>` envelopes, with the turn's locus as the
|
|
27
27
|
* fallback. An envelope with no target, or `>>>skip_reply`,
|
|
28
28
|
* holds back what follows until a later `>>>` names a target.
|
|
29
29
|
* - locus: NO prefix parsing (locus mode's `>>` lines are plain text);
|
|
30
|
-
* everything streams to the
|
|
30
|
+
* everything streams to the turn's locus.
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
export interface RoutedDelta {
|
|
@@ -52,9 +52,9 @@ export class ProseStreamRouter {
|
|
|
52
52
|
private opts: {
|
|
53
53
|
/** explicit parses `>>`; hybrid parses `>>>` while retaining locus fallback. */
|
|
54
54
|
mode: 'explicit' | 'hybrid' | 'locus';
|
|
55
|
-
/** Initial destination: the
|
|
55
|
+
/** Initial destination: the turn's locus (locus and hybrid mode); in
|
|
56
56
|
* explicit mode, null on a fresh turn, or the turn's destination on a
|
|
57
|
-
* stream
|
|
57
|
+
* later stream of the turn. */
|
|
58
58
|
initialTarget: string | null;
|
|
59
59
|
/** Resolve a `>>spec` to a channelId, or null (unresolved/ambiguous). */
|
|
60
60
|
resolve: (spec: string) => string | null;
|