@animalabs/agent-framework 0.21.1 → 0.22.1
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 +54 -4
- package/dist/src/agent.d.ts +17 -4
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +105 -43
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +41 -4
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/code-execution/py-runner.d.ts +22 -1
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js +64 -9
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/code-execution/runtime-py.d.ts +4 -0
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
- package/dist/src/code-execution/runtime-py.js +215 -38
- package/dist/src/code-execution/runtime-py.js.map +1 -1
- package/dist/src/framework.d.ts +569 -59
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3091 -542
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +21 -0
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +40 -7
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/index.d.ts +7 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +26 -3
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +28 -1
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +17 -0
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/feature-set-manager.d.ts +11 -0
- package/dist/src/mcpl/feature-set-manager.d.ts.map +1 -1
- package/dist/src/mcpl/feature-set-manager.js +31 -7
- package/dist/src/mcpl/feature-set-manager.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +4 -0
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +1 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/prose-stream-router.d.ts +27 -6
- package/dist/src/mcpl/prose-stream-router.d.ts.map +1 -1
- package/dist/src/mcpl/prose-stream-router.js +85 -22
- package/dist/src/mcpl/prose-stream-router.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +32 -4
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +46 -13
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +5 -3
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +41 -11
- 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/server-connection.d.ts +32 -1
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +76 -11
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts +5 -2
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/server-registry.js +25 -7
- package/dist/src/mcpl/server-registry.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +52 -2
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +47 -1
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-pattern-check.d.ts +51 -0
- package/dist/src/mcpl/tool-pattern-check.d.ts.map +1 -0
- package/dist/src/mcpl/tool-pattern-check.js +79 -0
- package/dist/src/mcpl/tool-pattern-check.js.map +1 -0
- package/dist/src/mcpl/types.d.ts +15 -5
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/mcpl/visible-content.d.ts +39 -0
- package/dist/src/mcpl/visible-content.d.ts.map +1 -0
- package/dist/src/mcpl/visible-content.js +54 -0
- package/dist/src/mcpl/visible-content.js.map +1 -0
- package/dist/src/module-registry.d.ts +90 -6
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +260 -18
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/workspace/branch-intent.d.ts +72 -0
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -0
- package/dist/src/modules/workspace/branch-intent.js +83 -0
- package/dist/src/modules/workspace/branch-intent.js.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts +167 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.js +259 -0
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -0
- package/dist/src/modules/workspace/effects.d.ts +84 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -0
- package/dist/src/modules/workspace/effects.js +368 -0
- package/dist/src/modules/workspace/effects.js.map +1 -0
- package/dist/src/modules/workspace/index.d.ts +155 -17
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1101 -424
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +288 -0
- package/dist/src/modules/workspace/observe.d.ts.map +1 -0
- package/dist/src/modules/workspace/observe.js +825 -0
- package/dist/src/modules/workspace/observe.js.map +1 -0
- package/dist/src/modules/workspace/reconcile.d.ts +238 -0
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -0
- package/dist/src/modules/workspace/reconcile.js +909 -0
- package/dist/src/modules/workspace/reconcile.js.map +1 -0
- package/dist/src/modules/workspace/sync.d.ts +6 -59
- package/dist/src/modules/workspace/sync.d.ts.map +1 -1
- package/dist/src/modules/workspace/sync.js +6 -257
- package/dist/src/modules/workspace/sync.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +77 -10
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.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/record-journal.d.ts +127 -0
- package/dist/src/record-journal.d.ts.map +1 -0
- package/dist/src/record-journal.js +202 -0
- package/dist/src/record-journal.js.map +1 -0
- 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/shutdown.d.ts +13 -0
- package/dist/src/shutdown.d.ts.map +1 -0
- package/dist/src/shutdown.js +46 -0
- package/dist/src/shutdown.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +74 -0
- package/dist/src/silent-heartbeat.d.ts.map +1 -0
- package/dist/src/silent-heartbeat.js +77 -0
- package/dist/src/silent-heartbeat.js.map +1 -0
- package/dist/src/tool-presentation.d.ts +50 -0
- package/dist/src/tool-presentation.d.ts.map +1 -0
- package/dist/src/tool-presentation.js +235 -0
- package/dist/src/tool-presentation.js.map +1 -0
- package/dist/src/tool-result-guard.d.ts +142 -6
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +357 -36
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +13 -0
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +21 -1
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/types/agent.d.ts +18 -2
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +16 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +12 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +26 -4
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +87 -3
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/test/batched-wake-locus.test.d.ts +2 -0
- package/dist/test/batched-wake-locus.test.d.ts.map +1 -0
- package/dist/test/batched-wake-locus.test.js +166 -0
- package/dist/test/batched-wake-locus.test.js.map +1 -0
- package/dist/test/code-execution.test.js +386 -2
- package/dist/test/code-execution.test.js.map +1 -1
- package/dist/test/component-defaults.test.d.ts +2 -0
- package/dist/test/component-defaults.test.d.ts.map +1 -0
- package/dist/test/component-defaults.test.js +54 -0
- package/dist/test/component-defaults.test.js.map +1 -0
- package/dist/test/conversation-routing.test.js +57 -1
- package/dist/test/conversation-routing.test.js.map +1 -1
- package/dist/test/discord-awareness-outbox.test.js +3 -0
- package/dist/test/discord-awareness-outbox.test.js.map +1 -1
- package/dist/test/ephemeral-settle.test.js +3 -0
- package/dist/test/ephemeral-settle.test.js.map +1 -1
- package/dist/test/event-gate.test.js +118 -0
- package/dist/test/event-gate.test.js.map +1 -1
- package/dist/test/explicit-prose-routing.test.js +975 -0
- 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.map +1 -1
- package/dist/test/helpers/coalescing-fixture.js +3 -2
- package/dist/test/helpers/coalescing-fixture.js.map +1 -1
- package/dist/test/helpers/pytc-stop-rows.d.ts +11 -0
- package/dist/test/helpers/pytc-stop-rows.d.ts.map +1 -0
- package/dist/test/helpers/pytc-stop-rows.js +143 -0
- package/dist/test/helpers/pytc-stop-rows.js.map +1 -0
- package/dist/test/inference-exhaustion-rewind.test.js +142 -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 +1201 -0
- package/dist/test/inference-failure-bounds.test.js.map +1 -0
- 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/mcpl-awareness-barrier.test.js +141 -0
- package/dist/test/mcpl-awareness-barrier.test.js.map +1 -1
- package/dist/test/mcpl-coalescing.test.js +97 -22
- package/dist/test/mcpl-coalescing.test.js.map +1 -1
- package/dist/test/mcpl-config-diagnostics.test.d.ts +2 -0
- package/dist/test/mcpl-config-diagnostics.test.d.ts.map +1 -0
- package/dist/test/mcpl-config-diagnostics.test.js +452 -0
- package/dist/test/mcpl-config-diagnostics.test.js.map +1 -0
- package/dist/test/mcpl-empty-content.test.d.ts +2 -0
- package/dist/test/mcpl-empty-content.test.d.ts.map +1 -0
- package/dist/test/mcpl-empty-content.test.js +368 -0
- package/dist/test/mcpl-empty-content.test.js.map +1 -0
- 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-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-tool-lifecycle-emitter.test.js +106 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +1 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +2 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +1 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.js +288 -0
- package/dist/test/mcpl-tool-lifecycle-script.test.js.map +1 -0
- package/dist/test/mcpl-ws-transport.test.js +103 -0
- package/dist/test/mcpl-ws-transport.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/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/prose-stream-router.test.js +41 -0
- package/dist/test/prose-stream-router.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 +2196 -0
- package/dist/test/provider-waits.test.js.map +1 -0
- 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.d.ts +6 -0
- package/dist/test/record-journal.test.d.ts.map +1 -0
- package/dist/test/record-journal.test.js +269 -0
- package/dist/test/record-journal.test.js.map +1 -0
- package/dist/test/refusal-reaction-baseline.test.js +10 -3
- package/dist/test/refusal-reaction-baseline.test.js.map +1 -1
- package/dist/test/save-recent-image.test.js +240 -2
- package/dist/test/save-recent-image.test.js.map +1 -1
- package/dist/test/shutdown-errors.test.d.ts +2 -0
- package/dist/test/shutdown-errors.test.d.ts.map +1 -0
- package/dist/test/shutdown-errors.test.js +662 -0
- package/dist/test/shutdown-errors.test.js.map +1 -0
- package/dist/test/silent-heartbeat-rows.test.d.ts +2 -0
- package/dist/test/silent-heartbeat-rows.test.d.ts.map +1 -0
- package/dist/test/silent-heartbeat-rows.test.js +451 -0
- package/dist/test/silent-heartbeat-rows.test.js.map +1 -0
- package/dist/test/silent-heartbeat.test.js +49 -38
- package/dist/test/silent-heartbeat.test.js.map +1 -1
- package/dist/test/stop-with-work-in-flight.test.d.ts +23 -0
- package/dist/test/stop-with-work-in-flight.test.d.ts.map +1 -0
- package/dist/test/stop-with-work-in-flight.test.js +672 -0
- package/dist/test/stop-with-work-in-flight.test.js.map +1 -0
- 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.d.ts +8 -0
- package/dist/test/tool-batch-hook.test.d.ts.map +1 -0
- package/dist/test/tool-batch-hook.test.js +275 -0
- package/dist/test/tool-batch-hook.test.js.map +1 -0
- package/dist/test/tool-presentation.test.d.ts +2 -0
- package/dist/test/tool-presentation.test.d.ts.map +1 -0
- package/dist/test/tool-presentation.test.js +268 -0
- package/dist/test/tool-presentation.test.js.map +1 -0
- package/dist/test/tool-result-guard.test.js +2545 -19
- package/dist/test/tool-result-guard.test.js.map +1 -1
- package/dist/test/tool-result-history.test.js +2 -1
- package/dist/test/tool-result-history.test.js.map +1 -1
- package/dist/test/turn-trigger-provenance.test.js +35 -0
- package/dist/test/turn-trigger-provenance.test.js.map +1 -1
- package/dist/test/unconvertible-tool-result.test.d.ts +2 -0
- package/dist/test/unconvertible-tool-result.test.d.ts.map +1 -0
- package/dist/test/unconvertible-tool-result.test.js +358 -0
- package/dist/test/unconvertible-tool-result.test.js.map +1 -0
- package/dist/test/workspace-materialize-freshness.test.d.ts +16 -0
- package/dist/test/workspace-materialize-freshness.test.d.ts.map +1 -0
- package/dist/test/workspace-materialize-freshness.test.js +252 -0
- package/dist/test/workspace-materialize-freshness.test.js.map +1 -0
- package/dist/test/workspace-three-way.test.d.ts +12 -0
- package/dist/test/workspace-three-way.test.d.ts.map +1 -0
- package/dist/test/workspace-three-way.test.js +3194 -0
- package/dist/test/workspace-three-way.test.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/agent.ts +124 -45
- package/src/api/server.ts +43 -4
- package/src/code-execution/py-runner.ts +66 -9
- package/src/code-execution/runtime-py.ts +215 -38
- package/src/framework.ts +3191 -544
- package/src/gate/event-gate.ts +59 -7
- package/src/index.ts +18 -2
- package/src/mcpl/channel-registry.ts +24 -3
- package/src/mcpl/conversation-router.ts +34 -1
- package/src/mcpl/feature-set-manager.ts +36 -11
- package/src/mcpl/index.ts +4 -0
- package/src/mcpl/prose-stream-router.ts +79 -23
- package/src/mcpl/push-coalescer.ts +62 -13
- package/src/mcpl/push-handler.ts +41 -12
- package/src/mcpl/reference-fetcher.ts +18 -8
- package/src/mcpl/references.ts +57 -13
- package/src/mcpl/server-connection.ts +95 -13
- package/src/mcpl/server-registry.ts +24 -7
- package/src/mcpl/tool-lifecycle.ts +77 -2
- package/src/mcpl/tool-pattern-check.ts +106 -0
- package/src/mcpl/types.ts +15 -5
- package/src/mcpl/visible-content.ts +57 -0
- package/src/module-registry.ts +269 -25
- package/src/modules/workspace/branch-intent.ts +123 -0
- package/src/modules/workspace/disk-agreement.ts +325 -0
- package/src/modules/workspace/effects.ts +362 -0
- package/src/modules/workspace/index.ts +1098 -446
- package/src/modules/workspace/observe.ts +865 -0
- package/src/modules/workspace/reconcile.ts +1157 -0
- package/src/modules/workspace/sync.ts +6 -336
- package/src/modules/workspace/types.ts +75 -10
- package/src/operator-log.ts +30 -0
- package/src/provider-waits.ts +523 -0
- package/src/record-journal.ts +246 -0
- package/src/refusal-reactions.ts +14 -12
- package/src/shutdown.ts +49 -0
- package/src/silent-heartbeat.ts +141 -0
- package/src/tool-presentation.ts +188 -0
- package/src/tool-result-guard.ts +384 -34
- package/src/tool-result-history.ts +25 -1
- package/src/types/agent.ts +18 -2
- package/src/types/events.ts +16 -0
- package/src/types/framework.ts +12 -0
- package/src/types/module.ts +27 -4
- package/src/types/trace.ts +94 -3
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import type { McplServerConfig, McplHostCapabilities } from './types.js';
|
|
8
8
|
import { McplServerConnection } from './server-connection.js';
|
|
9
|
+
import { DEFAULT_SHUTDOWN_TIMEOUT_MS, validateShutdownTimeout, waitForShutdown } from '../shutdown.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Capability keys that can be queried via `getServersWithCapability`.
|
|
@@ -25,6 +26,7 @@ export type McplCapabilityQuery =
|
|
|
25
26
|
*/
|
|
26
27
|
export class McplServerRegistry {
|
|
27
28
|
private servers = new Map<string, McplServerConnection>();
|
|
29
|
+
private closing = new Map<McplServerConnection, Promise<void>>();
|
|
28
30
|
|
|
29
31
|
/**
|
|
30
32
|
* Connect to an MCPL server and register it.
|
|
@@ -70,8 +72,7 @@ export class McplServerRegistry {
|
|
|
70
72
|
if (!connection) {
|
|
71
73
|
return;
|
|
72
74
|
}
|
|
73
|
-
this.
|
|
74
|
-
await connection.close();
|
|
75
|
+
await this.closeServer(id, connection);
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
/**
|
|
@@ -135,11 +136,27 @@ export class McplServerRegistry {
|
|
|
135
136
|
}
|
|
136
137
|
|
|
137
138
|
/**
|
|
138
|
-
* Close all server connections
|
|
139
|
+
* Close all server connections, retaining failed or pending attempts.
|
|
140
|
+
* An explicit timeout bounds the caller's wait without cancelling cleanup.
|
|
139
141
|
*/
|
|
140
|
-
async closeAll(): Promise<void> {
|
|
141
|
-
|
|
142
|
-
this.servers
|
|
143
|
-
|
|
142
|
+
async closeAll(timeoutMs = DEFAULT_SHUTDOWN_TIMEOUT_MS): Promise<void> {
|
|
143
|
+
validateShutdownTimeout(timeoutMs);
|
|
144
|
+
await waitForShutdown(Array.from(this.servers, ([id, connection]) => ({
|
|
145
|
+
label: `mcpl:${id}`, promise: this.closeServer(id, connection),
|
|
146
|
+
})), timeoutMs);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
private closeServer(id: string, connection: McplServerConnection): Promise<void> {
|
|
150
|
+
const existing = this.closing.get(connection);
|
|
151
|
+
if (existing) return existing;
|
|
152
|
+
const attempt = Promise.resolve().then(() => connection.close()).then(() => {
|
|
153
|
+
if (this.servers.get(id) === connection) this.servers.delete(id);
|
|
154
|
+
this.closing.delete(connection);
|
|
155
|
+
}, error => {
|
|
156
|
+
this.closing.delete(connection);
|
|
157
|
+
throw error;
|
|
158
|
+
});
|
|
159
|
+
this.closing.set(connection, attempt);
|
|
160
|
+
return attempt;
|
|
144
161
|
}
|
|
145
162
|
}
|
|
@@ -50,8 +50,20 @@ export interface ToolLifecycleParams {
|
|
|
50
50
|
inputWithheld?: boolean;
|
|
51
51
|
isError?: boolean;
|
|
52
52
|
durationMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* MCP's extension slot. Carries only SCRIPT_PARENT_META_KEY, on calls a
|
|
55
|
+
* code_execution script made: RFC-007 has no field for a call's origin,
|
|
56
|
+
* and the `mcpl/` prefix is reserved for keys MCPL defines (RFC-008 §3).
|
|
57
|
+
*/
|
|
58
|
+
_meta?: Record<string, unknown>;
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
/**
|
|
62
|
+
* `_meta` key naming the `toolCallId` of the code_execution call whose script
|
|
63
|
+
* made this call. Host-specific: not defined by RFC-007.
|
|
64
|
+
*/
|
|
65
|
+
export const SCRIPT_PARENT_META_KEY = 'agent-framework/parentToolCallId';
|
|
66
|
+
|
|
55
67
|
export interface ToolObserveMatch {
|
|
56
68
|
tool?: string;
|
|
57
69
|
serverTool?: string;
|
|
@@ -78,7 +90,8 @@ export interface ToolObserveRule {
|
|
|
78
90
|
* RFC-007 §6.2 grammar.
|
|
79
91
|
*/
|
|
80
92
|
export interface ToolLifecycleNarrowing {
|
|
81
|
-
/** Patterns over the model-facing tool name (`computer--*`)
|
|
93
|
+
/** Patterns over the model-facing tool name (`computer--*`): for an MCPL
|
|
94
|
+
* tool `<toolPrefix>--<tool>`, toolPrefix defaulting to `mcpl--<serverId>`. */
|
|
82
95
|
tools?: string[];
|
|
83
96
|
/** The tool's effective class must intersect this set. `'default'` is
|
|
84
97
|
* DEFAULT_INPUT_CLASSES (computer, shell, files, web, media, body). */
|
|
@@ -242,6 +255,8 @@ export interface ToolCallDescriptor {
|
|
|
242
255
|
serverId?: string;
|
|
243
256
|
serverTool?: string;
|
|
244
257
|
input: unknown;
|
|
258
|
+
/** Script-inner calls: the code_execution call's `toolCallId`, when it had one. */
|
|
259
|
+
parentToolCallId?: string;
|
|
245
260
|
}
|
|
246
261
|
|
|
247
262
|
function anyGlob(patterns: readonly string[], subject: string): boolean {
|
|
@@ -466,6 +481,7 @@ function baseParams(call: ToolCallDescriptor, phase: ToolPhase): ToolLifecyclePa
|
|
|
466
481
|
class: [...call.class],
|
|
467
482
|
...(call.serverId !== undefined ? { serverId: call.serverId, serverTool: call.serverTool } : {}),
|
|
468
483
|
phase,
|
|
484
|
+
...(call.parentToolCallId !== undefined ? { _meta: { [SCRIPT_PARENT_META_KEY]: call.parentToolCallId } } : {}),
|
|
469
485
|
};
|
|
470
486
|
}
|
|
471
487
|
|
|
@@ -537,10 +553,23 @@ export interface ToolLifecycleHost {
|
|
|
537
553
|
describe(tool: string): { class: ToolClass[]; serverId?: string; serverTool?: string };
|
|
538
554
|
}
|
|
539
555
|
|
|
556
|
+
/**
|
|
557
|
+
* What a call made from inside a code_execution script inherits from the
|
|
558
|
+
* code_execution call that ran the script (scriptOrigin()).
|
|
559
|
+
*/
|
|
560
|
+
export interface ScriptCallOrigin {
|
|
561
|
+
/** The inference whose output requested the code_execution call. */
|
|
562
|
+
inferenceId: string;
|
|
563
|
+
/** That call's `toolCallId`. Absent when nothing observed it, so it has none. */
|
|
564
|
+
parentToolCallId?: string;
|
|
565
|
+
}
|
|
566
|
+
|
|
540
567
|
interface TrackedCall {
|
|
541
568
|
agentName: string;
|
|
542
569
|
modelCallId: string;
|
|
543
570
|
call: ToolCallDescriptor;
|
|
571
|
+
/** Made by a code_execution script, not by the model (registerScriptCall). */
|
|
572
|
+
scriptInner: boolean;
|
|
544
573
|
/** Connection id → transport epoch the opening went to. */
|
|
545
574
|
openedTo: Map<string, number>;
|
|
546
575
|
/** `started` has been sent (open() ran and the call was not refused). */
|
|
@@ -569,6 +598,9 @@ export class ToolLifecycleEmitter {
|
|
|
569
598
|
private readonly recentIds = new Set<string>();
|
|
570
599
|
private readonly recentOrder: string[] = [];
|
|
571
600
|
private mintSeq = 0;
|
|
601
|
+
/** The call between register() and open(), kept even when nothing observes,
|
|
602
|
+
* so a code_execution dispatch can learn its inference (scriptOrigin()). */
|
|
603
|
+
private dispatching: { agentName: string; callId: string; inferenceId: string } | null = null;
|
|
572
604
|
|
|
573
605
|
constructor(host: ToolLifecycleHost, now: () => number = Date.now) {
|
|
574
606
|
this.host = host;
|
|
@@ -608,6 +640,44 @@ export class ToolLifecycleEmitter {
|
|
|
608
640
|
* host may still refuse it (refuse()) before it executes.
|
|
609
641
|
*/
|
|
610
642
|
register(agentName: string, inferenceId: string, call: { id: string; name: string; input: unknown }): void {
|
|
643
|
+
this.dispatching = { agentName, callId: call.id, inferenceId };
|
|
644
|
+
this.track(agentName, inferenceId, call, false);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* A call a code_execution script made, about to go through the same
|
|
649
|
+
* dispatch as a model call (then refuse() / open() / onResult() as for
|
|
650
|
+
* one). Its class, provider, grant narrowing and filter matching are the
|
|
651
|
+
* INNER tool's own; its inference is the one that issued the
|
|
652
|
+
* code_execution call. Unlike a model call it is not aborted when its
|
|
653
|
+
* stream ends: the tool keeps running after the turn or the script is
|
|
654
|
+
* gone, so its terminal is the result's, or `failed` when the host gives
|
|
655
|
+
* up waiting for one.
|
|
656
|
+
*/
|
|
657
|
+
registerScriptCall(agentName: string, origin: ScriptCallOrigin, call: { id: string; name: string; input: unknown }): void {
|
|
658
|
+
this.track(agentName, origin.inferenceId, call, true, origin.parentToolCallId);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* The origin a code_execution call passes to the calls its script makes.
|
|
663
|
+
* Valid only while that call is being dispatched (between register() and
|
|
664
|
+
* open()), which is where the framework asks.
|
|
665
|
+
*/
|
|
666
|
+
scriptOrigin(agentName: string, callId: string): ScriptCallOrigin | undefined {
|
|
667
|
+
const tracked = this.calls.get(callKey(agentName, callId));
|
|
668
|
+
if (tracked) return { inferenceId: tracked.call.inferenceId, parentToolCallId: tracked.call.toolCallId };
|
|
669
|
+
const d = this.dispatching;
|
|
670
|
+
if (d && d.agentName === agentName && d.callId === callId) return { inferenceId: d.inferenceId };
|
|
671
|
+
return undefined;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
private track(
|
|
675
|
+
agentName: string,
|
|
676
|
+
inferenceId: string,
|
|
677
|
+
call: { id: string; name: string; input: unknown },
|
|
678
|
+
scriptInner: boolean,
|
|
679
|
+
parentToolCallId?: string,
|
|
680
|
+
): void {
|
|
611
681
|
if (!this.anyObserver()) return;
|
|
612
682
|
const described = this.host.describe(call.name);
|
|
613
683
|
this.calls.set(callKey(agentName, call.id), {
|
|
@@ -623,7 +693,9 @@ export class ToolLifecycleEmitter {
|
|
|
623
693
|
? { serverId: described.serverId, serverTool: described.serverTool }
|
|
624
694
|
: {}),
|
|
625
695
|
input: call.input,
|
|
696
|
+
...(parentToolCallId !== undefined ? { parentToolCallId } : {}),
|
|
626
697
|
},
|
|
698
|
+
scriptInner,
|
|
627
699
|
openedTo: new Map(),
|
|
628
700
|
opened: false,
|
|
629
701
|
// Taken before dispatch, so durationMs includes work a synchronous
|
|
@@ -652,6 +724,7 @@ export class ToolLifecycleEmitter {
|
|
|
652
724
|
* run synchronously inside dispatch, before this.
|
|
653
725
|
*/
|
|
654
726
|
open(agentName: string, callId: string): void {
|
|
727
|
+
this.dispatching = null;
|
|
655
728
|
const key = callKey(agentName, callId);
|
|
656
729
|
const tracked = this.calls.get(key);
|
|
657
730
|
if (!tracked || tracked.opened) return;
|
|
@@ -725,12 +798,14 @@ export class ToolLifecycleEmitter {
|
|
|
725
798
|
* A stream ended: its calls still open were cancelled before a result.
|
|
726
799
|
* Scoped to the inference ids that stream minted, so a successor stream
|
|
727
800
|
* for the same agent (a budget restart overlapping this one's teardown)
|
|
728
|
-
* keeps its live calls. A result arriving later finds nothing.
|
|
801
|
+
* keeps its live calls. A result arriving later finds nothing. Calls a
|
|
802
|
+
* script made are left open: they end with their own result.
|
|
729
803
|
*/
|
|
730
804
|
abortOpen(agentName: string, inferenceIds: Iterable<string>): void {
|
|
731
805
|
if (this.calls.size === 0) return;
|
|
732
806
|
const ids = new Set(inferenceIds);
|
|
733
807
|
for (const tracked of [...this.calls.values()]) {
|
|
808
|
+
if (tracked.scriptInner) continue;
|
|
734
809
|
if (tracked.agentName === agentName && ids.has(tracked.call.inferenceId)) {
|
|
735
810
|
if (tracked.opened) this.terminate(tracked, 'aborted');
|
|
736
811
|
else this.calls.delete(callKey(tracked.agentName, tracked.modelCallId));
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Zero-match diagnostics for operator tool-name patterns.
|
|
3
|
+
*
|
|
4
|
+
* `toolClassOverrides` keys and `toolLifecycle.{observe,inputs}.tools` are
|
|
5
|
+
* RFC-007 §6.2 patterns over the MODEL-FACING tool name. For an MCPL tool
|
|
6
|
+
* that is `<toolPrefix>--<tool>`, and `toolPrefix` defaults to
|
|
7
|
+
* `mcpl--<serverId>`. A pattern written against the bare server id
|
|
8
|
+
* (`search--*` for server `search` with no toolPrefix) is valid config that
|
|
9
|
+
* matches nothing, and nothing else reports it: the override classes no tool
|
|
10
|
+
* and the narrowing admits no call. This module decides, for one pattern and
|
|
11
|
+
* the current tool listing, whether that is the case and what was probably
|
|
12
|
+
* meant.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { globMatch } from './tool-glob.js';
|
|
16
|
+
|
|
17
|
+
export interface PatternServer {
|
|
18
|
+
id: string;
|
|
19
|
+
/** Effective tool prefix: config `toolPrefix ?? mcpl--<id>`. */
|
|
20
|
+
prefix: string;
|
|
21
|
+
/** True when this server's tools are in the current listing. */
|
|
22
|
+
listed: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type ToolPatternVerdict =
|
|
26
|
+
| { kind: 'matched' }
|
|
27
|
+
/** Could still match tools of servers whose listing is not in (yet). */
|
|
28
|
+
| { kind: 'pending'; servers: string[] }
|
|
29
|
+
| { kind: 'unmatched'; suggestion?: string; hint: string };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Could `pattern` match some name under `<prefix>--`? Decided on the literal
|
|
33
|
+
* text before the first `*`; anything after it is not consulted, so this
|
|
34
|
+
* errs toward "could" (which only defers a warning, never invents one).
|
|
35
|
+
*/
|
|
36
|
+
export function couldMatchUnderPrefix(pattern: string, prefix: string): boolean {
|
|
37
|
+
const head = `${prefix}--`;
|
|
38
|
+
const star = pattern.indexOf('*');
|
|
39
|
+
if (star === -1) return pattern.startsWith(head);
|
|
40
|
+
const literal = pattern.slice(0, star);
|
|
41
|
+
return literal.startsWith(head) || head.startsWith(literal);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check one pattern against the model-facing tool names offered right now.
|
|
46
|
+
*
|
|
47
|
+
* `pending` while a server whose tools the pattern could name has not
|
|
48
|
+
* listed them (not connected yet, reconnecting, or removed): its listing is
|
|
49
|
+
* the only evidence, and an early verdict would be a false alarm.
|
|
50
|
+
*/
|
|
51
|
+
export function checkToolPattern(
|
|
52
|
+
pattern: string,
|
|
53
|
+
toolNames: Iterable<string>,
|
|
54
|
+
servers: readonly PatternServer[],
|
|
55
|
+
): ToolPatternVerdict {
|
|
56
|
+
const names = [...toolNames];
|
|
57
|
+
for (const name of names) {
|
|
58
|
+
if (globMatch(pattern, name)) return { kind: 'matched' };
|
|
59
|
+
}
|
|
60
|
+
const pending = servers.filter((s) => !s.listed && couldMatchUnderPrefix(pattern, s.prefix));
|
|
61
|
+
if (pending.length > 0) return { kind: 'pending', servers: [...new Set(pending.map((s) => s.id))] };
|
|
62
|
+
|
|
63
|
+
// Written against the wrong namespace for a known server: the bare id
|
|
64
|
+
// (`<id>--…`) where the effective prefix differs, or the default form
|
|
65
|
+
// (`mcpl--<id>--…`) for a server that set its own toolPrefix. Never when
|
|
66
|
+
// the namespace as written is real — another server's toolPrefix or a
|
|
67
|
+
// module's tools — since the pattern then names that, not a mistake.
|
|
68
|
+
const inRealNamespace = (ns: string) =>
|
|
69
|
+
servers.some((o) => o.prefix === ns) || names.some((n) => n.startsWith(`${ns}--`));
|
|
70
|
+
for (const s of servers) {
|
|
71
|
+
let rest: string | undefined;
|
|
72
|
+
if (s.prefix !== s.id && pattern.startsWith(`${s.id}--`) && !inRealNamespace(s.id)) {
|
|
73
|
+
rest = pattern.slice(s.id.length + 2);
|
|
74
|
+
} else if (
|
|
75
|
+
s.prefix !== `mcpl--${s.id}` && pattern.startsWith(`mcpl--${s.id}--`) && !inRealNamespace(`mcpl--${s.id}`)
|
|
76
|
+
) {
|
|
77
|
+
rest = pattern.slice(s.id.length + 8);
|
|
78
|
+
}
|
|
79
|
+
if (rest === undefined) continue;
|
|
80
|
+
const why = s.prefix === `mcpl--${s.id}` ? ' (no toolPrefix set; the default is "mcpl--<serverId>")' : '';
|
|
81
|
+
return {
|
|
82
|
+
kind: 'unmatched',
|
|
83
|
+
suggestion: `${s.prefix}--${rest}`,
|
|
84
|
+
hint: `server "${s.id}" names its tools "${s.prefix}--<tool>"${why}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (pattern.includes('--') && !servers.some((s) => couldMatchUnderPrefix(pattern, s.prefix))) {
|
|
89
|
+
return { kind: 'unmatched', hint: 'no connected MCPL server has a toolPrefix it could match' };
|
|
90
|
+
}
|
|
91
|
+
return { kind: 'unmatched', hint: 'no tool the framework offers has a matching name' };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** The operator-facing line for an `unmatched` verdict. */
|
|
95
|
+
export function describeUnmatchedPattern(
|
|
96
|
+
where: string,
|
|
97
|
+
pattern: string,
|
|
98
|
+
verdict: Extract<ToolPatternVerdict, { kind: 'unmatched' }>,
|
|
99
|
+
): string {
|
|
100
|
+
const meant = verdict.suggestion ? ` — did you mean ${JSON.stringify(verdict.suggestion)}?` : '';
|
|
101
|
+
return (
|
|
102
|
+
`[mcpl] ${where} pattern ${JSON.stringify(pattern)} matches no tool${meant} ` +
|
|
103
|
+
`(${verdict.hint}). Patterns match the full model-facing tool name, ` +
|
|
104
|
+
`"<toolPrefix>--<tool>" for MCPL tools.`
|
|
105
|
+
);
|
|
106
|
+
}
|
package/src/mcpl/types.ts
CHANGED
|
@@ -282,10 +282,16 @@ export interface McplServerConfig {
|
|
|
282
282
|
*/
|
|
283
283
|
allowHostCommands?: boolean;
|
|
284
284
|
|
|
285
|
-
/**
|
|
285
|
+
/**
|
|
286
|
+
* Feature sets to enable on connect (`.`-segment wildcards). Omitted:
|
|
287
|
+
* every declared set is a candidate. `[]`: none. Either way a set is
|
|
288
|
+
* enabled only if its declared `uses` are recognized capabilities the
|
|
289
|
+
* grant covers (§6.4) — a set declaring no `uses` stays disabled even when
|
|
290
|
+
* listed here (logged, and traced as `mcpl:feature-set-disabled`).
|
|
291
|
+
*/
|
|
286
292
|
enabledFeatureSets?: string[];
|
|
287
293
|
|
|
288
|
-
/** Feature sets to explicitly disable on connect */
|
|
294
|
+
/** Feature sets to explicitly disable on connect (wins over enabledFeatureSets). */
|
|
289
295
|
disabledFeatureSets?: string[];
|
|
290
296
|
|
|
291
297
|
/**
|
|
@@ -388,11 +394,15 @@ export interface McplServerConfig {
|
|
|
388
394
|
|
|
389
395
|
/**
|
|
390
396
|
* Tool name prefix for this server's tools.
|
|
391
|
-
* Tools are exposed as `{toolPrefix}
|
|
397
|
+
* Tools are exposed as `{toolPrefix}--{toolName}`.
|
|
392
398
|
*
|
|
393
|
-
* Default: `mcpl
|
|
399
|
+
* Default: `mcpl--{id}` (e.g., `mcpl--zk--lobby_start_game`).
|
|
394
400
|
* Set to the server ID for cleaner names (e.g., `toolPrefix: 'zk'`
|
|
395
|
-
* gives `zk
|
|
401
|
+
* gives `zk--lobby_start_game`).
|
|
402
|
+
*
|
|
403
|
+
* Tool-name patterns elsewhere (`toolClassOverrides` keys,
|
|
404
|
+
* `toolLifecycle.*.tools`) match these full names; `enabledTools` /
|
|
405
|
+
* `disabledTools` use the bare server-side names.
|
|
396
406
|
*
|
|
397
407
|
* Must not collide with any module name or another server's prefix.
|
|
398
408
|
*/
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Visibly-empty MCPL content, and the one empty push that is allowed.
|
|
3
|
+
*
|
|
4
|
+
* A push event or channel message whose content gives the model nothing to
|
|
5
|
+
* read cannot be shown: the request sanitizer strips empty text blocks and
|
|
6
|
+
* drops the message they leave empty, so a wake queued for it presents the
|
|
7
|
+
* model with `[Continue]` or with an older message re-presented as the newest.
|
|
8
|
+
* That is a wake with no visible cause, and it invites the model to invent
|
|
9
|
+
* one. Such content is rejected at the MCPL boundary, and the store/wake site
|
|
10
|
+
* drops it as a backstop.
|
|
11
|
+
*
|
|
12
|
+
* The exception is the silent-heartbeat marker: the heartbeat server's own
|
|
13
|
+
* scheduled tick, which wakes with ephemeral host-supplied context instead of
|
|
14
|
+
* a stored message.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* True when `content` holds nothing the model would see: no non-text block,
|
|
19
|
+
* and no text block whose text has a non-whitespace character. Mirrors the
|
|
20
|
+
* request sanitizer, which drops text that is not a string or trims to ''.
|
|
21
|
+
* Accepts MCPL wire blocks and converted membrane blocks alike.
|
|
22
|
+
*/
|
|
23
|
+
export function isVisiblyEmptyContent(content: readonly unknown[] | null | undefined): boolean {
|
|
24
|
+
if (!Array.isArray(content)) return true;
|
|
25
|
+
for (const block of content) {
|
|
26
|
+
if (!block || typeof block !== 'object') continue;
|
|
27
|
+
const b = block as { type?: unknown; text?: unknown };
|
|
28
|
+
if (b.type !== 'text') return false;
|
|
29
|
+
if (typeof b.text === 'string' && b.text.trim() !== '') return false;
|
|
30
|
+
}
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** The server id and feature set the marker must carry. */
|
|
35
|
+
export const SILENT_HEARTBEAT_SERVER_ID = 'heartbeat';
|
|
36
|
+
export const SILENT_HEARTBEAT_FEATURE_SET = 'heartbeat';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The exact silent-heartbeat marker: an empty push from the `heartbeat`
|
|
40
|
+
* server on its `heartbeat` feature set, with `origin.source === 'heartbeat'`
|
|
41
|
+
* and `origin.silent === true`. Anything else that is empty is not a silent
|
|
42
|
+
* tick — arbitrary servers cannot claim the silent path by setting
|
|
43
|
+
* `origin.silent`, and a marker that carries content is ordinary content.
|
|
44
|
+
*/
|
|
45
|
+
export function isSilentHeartbeatMarker(event: {
|
|
46
|
+
serverId: string;
|
|
47
|
+
featureSet?: string;
|
|
48
|
+
origin?: Record<string, unknown> | null;
|
|
49
|
+
content: readonly unknown[] | null | undefined;
|
|
50
|
+
}): boolean {
|
|
51
|
+
return event.serverId === SILENT_HEARTBEAT_SERVER_ID
|
|
52
|
+
&& event.featureSet === SILENT_HEARTBEAT_FEATURE_SET
|
|
53
|
+
&& event.origin?.source === 'heartbeat'
|
|
54
|
+
&& event.origin?.silent === true
|
|
55
|
+
&& Array.isArray(event.content)
|
|
56
|
+
&& event.content.length === 0;
|
|
57
|
+
}
|