@animalabs/agent-framework 0.23.1 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -42
- package/dist/src/agent.d.ts +60 -9
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +92 -12
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +14 -12
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/code-execution/engine.d.ts +177 -0
- package/dist/src/code-execution/engine.d.ts.map +1 -0
- package/dist/src/code-execution/engine.js +494 -0
- package/dist/src/code-execution/engine.js.map +1 -0
- package/dist/src/code-execution/index.d.ts +5 -10
- package/dist/src/code-execution/index.d.ts.map +1 -1
- package/dist/src/code-execution/index.js +5 -9
- package/dist/src/code-execution/index.js.map +1 -1
- package/dist/src/code-execution/isolate/calls.d.ts +114 -0
- package/dist/src/code-execution/isolate/calls.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/calls.js +145 -0
- package/dist/src/code-execution/isolate/calls.js.map +1 -0
- package/dist/src/code-execution/isolate/child-entry.d.ts +2 -0
- package/dist/src/code-execution/isolate/child-entry.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/child-entry.js +5 -0
- package/dist/src/code-execution/isolate/child-entry.js.map +1 -0
- package/dist/src/code-execution/isolate/child-main.d.ts +26 -0
- package/dist/src/code-execution/isolate/child-main.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/child-main.js +132 -0
- package/dist/src/code-execution/isolate/child-main.js.map +1 -0
- package/dist/src/code-execution/isolate/child.d.ts +44 -0
- package/dist/src/code-execution/isolate/child.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/child.js +315 -0
- package/dist/src/code-execution/isolate/child.js.map +1 -0
- package/dist/src/code-execution/isolate/frames.d.ts +80 -0
- package/dist/src/code-execution/isolate/frames.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/frames.js +210 -0
- package/dist/src/code-execution/isolate/frames.js.map +1 -0
- package/dist/src/code-execution/isolate/output.d.ts +68 -0
- package/dist/src/code-execution/isolate/output.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/output.js +143 -0
- package/dist/src/code-execution/isolate/output.js.map +1 -0
- package/dist/src/code-execution/isolate/prelude.d.ts +109 -0
- package/dist/src/code-execution/isolate/prelude.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/prelude.js +401 -0
- package/dist/src/code-execution/isolate/prelude.js.map +1 -0
- package/dist/src/code-execution/isolate/protocol.d.ts +151 -0
- package/dist/src/code-execution/isolate/protocol.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/protocol.js +150 -0
- package/dist/src/code-execution/isolate/protocol.js.map +1 -0
- package/dist/src/code-execution/isolate/render.d.ts +57 -0
- package/dist/src/code-execution/isolate/render.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/render.js +214 -0
- package/dist/src/code-execution/isolate/render.js.map +1 -0
- package/dist/src/code-execution/isolate/runner.d.ts +250 -0
- package/dist/src/code-execution/isolate/runner.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/runner.js +854 -0
- package/dist/src/code-execution/isolate/runner.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +175 -0
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -0
- package/dist/src/code-execution/isolate/scripts.js +357 -0
- package/dist/src/code-execution/isolate/scripts.js.map +1 -0
- package/dist/src/code-execution/quickjs.wasm +0 -0
- package/dist/src/code-execution/tool-definition.d.ts +93 -43
- package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
- package/dist/src/code-execution/tool-definition.js +176 -142
- package/dist/src/code-execution/tool-definition.js.map +1 -1
- package/dist/src/deadline-timer.d.ts +3 -0
- package/dist/src/deadline-timer.d.ts.map +1 -1
- package/dist/src/deadline-timer.js +1 -1
- package/dist/src/deadline-timer.js.map +1 -1
- package/dist/src/framework.d.ts +1036 -440
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +4131 -2228
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +28 -1
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +88 -10
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/held-writes.d.ts +16 -3
- package/dist/src/held-writes.d.ts.map +1 -1
- package/dist/src/held-writes.js +16 -3
- package/dist/src/held-writes.js.map +1 -1
- package/dist/src/index.d.ts +6 -6
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +9 -8
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +471 -75
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +1673 -337
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +12 -7
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +12 -7
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +9 -5
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +50 -0
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +130 -0
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +2 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +3 -2
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/log-value.d.ts +6 -0
- package/dist/src/mcpl/log-value.d.ts.map +1 -1
- package/dist/src/mcpl/log-value.js +8 -0
- package/dist/src/mcpl/log-value.js.map +1 -1
- package/dist/src/mcpl/modern-connection.d.ts +15 -6
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
- package/dist/src/mcpl/modern-connection.js +168 -53
- package/dist/src/mcpl/modern-connection.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +4 -5
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
- package/dist/src/mcpl/prose-grammar.js +4 -5
- package/dist/src/mcpl/prose-grammar.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts +97 -10
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +173 -16
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +132 -14
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +227 -50
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +6 -1
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +4 -1
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +2 -2
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +4 -4
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +50 -21
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +158 -74
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tags.d.ts +24 -0
- package/dist/src/mcpl/tags.d.ts.map +1 -1
- package/dist/src/mcpl/tags.js +33 -0
- package/dist/src/mcpl/tags.js.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.d.ts +2 -1
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +55 -7
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +163 -34
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +22 -6
- 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 +4 -2
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +100 -13
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +8 -13
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +27 -4
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +15 -3
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +27 -5
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/index.d.ts +1 -1
- package/dist/src/modules/index.d.ts.map +1 -1
- package/dist/src/modules/index.js.map +1 -1
- package/dist/src/modules/unknown-tool.d.ts +4 -1
- package/dist/src/modules/unknown-tool.d.ts.map +1 -1
- package/dist/src/modules/unknown-tool.js +4 -1
- package/dist/src/modules/unknown-tool.js.map +1 -1
- package/dist/src/modules/workspace/branch-intent.d.ts +3 -1
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
- package/dist/src/modules/workspace/branch-intent.js +3 -1
- package/dist/src/modules/workspace/branch-intent.js.map +1 -1
- package/dist/src/modules/workspace/edit-plan.d.ts +265 -0
- package/dist/src/modules/workspace/edit-plan.d.ts.map +1 -0
- package/dist/src/modules/workspace/edit-plan.js +794 -0
- package/dist/src/modules/workspace/edit-plan.js.map +1 -0
- package/dist/src/modules/workspace/edit-text.d.ts +117 -0
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -0
- package/dist/src/modules/workspace/edit-text.js +584 -0
- package/dist/src/modules/workspace/edit-text.js.map +1 -0
- package/dist/src/modules/workspace/effects.d.ts +5 -4
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +78 -25
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +484 -61
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +2173 -645
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +213 -38
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +416 -36
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +15 -8
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/text.d.ts +32 -0
- package/dist/src/modules/workspace/text.d.ts.map +1 -0
- package/dist/src/modules/workspace/text.js +67 -0
- package/dist/src/modules/workspace/text.js.map +1 -0
- package/dist/src/modules/workspace/types.d.ts +210 -6
- 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 +3 -2
- 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 +52 -24
- package/dist/src/operator-log.d.ts.map +1 -1
- package/dist/src/operator-log.js.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.d.ts +496 -72
- package/dist/src/recovery/discord-awareness-outbox.d.ts.map +1 -1
- package/dist/src/recovery/discord-awareness-outbox.js +1196 -289
- package/dist/src/recovery/discord-awareness-outbox.js.map +1 -1
- package/dist/src/recovery/offline-branch.d.ts +26 -1
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +164 -39
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/recovery/recover-cli.js +23 -4
- package/dist/src/recovery/recover-cli.js.map +1 -1
- package/dist/src/send-tools.d.ts +11 -0
- package/dist/src/send-tools.d.ts.map +1 -0
- package/dist/src/send-tools.js +11 -0
- package/dist/src/send-tools.js.map +1 -0
- package/dist/src/tool-result-guard.d.ts +4 -3
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +5 -4
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +1 -1
- package/dist/src/tool-result-history.js +1 -1
- package/dist/src/tune-out/coordinator.d.ts +21 -0
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +139 -37
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/types/agent.d.ts +4 -0
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +62 -9
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +62 -58
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +40 -8
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +111 -10
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/src/usage/types.d.ts +16 -0
- package/dist/src/usage/types.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.d.ts +14 -1
- package/dist/src/usage/usage-tracker.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.js +81 -41
- package/dist/src/usage/usage-tracker.js.map +1 -1
- package/package.json +6 -5
- package/src/agent.ts +93 -12
- package/src/api/server.ts +14 -12
- package/src/code-execution/engine.ts +595 -0
- package/src/code-execution/index.ts +5 -17
- package/src/code-execution/isolate/calls.ts +199 -0
- package/src/code-execution/isolate/child-entry.ts +5 -0
- package/src/code-execution/isolate/child-main.ts +132 -0
- package/src/code-execution/isolate/child.ts +327 -0
- package/src/code-execution/isolate/frames.ts +209 -0
- package/src/code-execution/isolate/output.ts +161 -0
- package/src/code-execution/isolate/prelude.ts +409 -0
- package/src/code-execution/isolate/protocol.ts +240 -0
- package/src/code-execution/isolate/render.ts +230 -0
- package/src/code-execution/isolate/runner.ts +911 -0
- package/src/code-execution/isolate/scripts.ts +479 -0
- package/src/code-execution/quickjs.wasm +0 -0
- package/src/code-execution/quickjs.wasm.LICENSES +149 -0
- package/src/code-execution/tool-definition.ts +236 -167
- package/src/deadline-timer.ts +1 -1
- package/src/framework.ts +4507 -2406
- package/src/gate/event-gate.ts +78 -11
- package/src/held-writes.ts +17 -3
- package/src/index.ts +30 -22
- package/src/mcpl/channel-registry.ts +1825 -368
- package/src/mcpl/conversation-router.ts +12 -7
- package/src/mcpl/convert-block.ts +9 -5
- package/src/mcpl/inbound-source.ts +131 -0
- package/src/mcpl/index.ts +4 -2
- package/src/mcpl/log-value.ts +9 -0
- package/src/mcpl/modern-connection.ts +162 -53
- package/src/mcpl/prose-grammar.ts +4 -5
- package/src/mcpl/protocol-family.ts +199 -21
- package/src/mcpl/push-coalescer.ts +306 -62
- package/src/mcpl/push-handler.ts +10 -2
- package/src/mcpl/reference-fetcher.ts +4 -4
- package/src/mcpl/server-connection.ts +180 -79
- package/src/mcpl/tags.ts +36 -0
- package/src/mcpl/tool-result-normalize.ts +2 -1
- package/src/mcpl/transport.ts +162 -33
- package/src/mcpl/types.ts +23 -6
- package/src/module-registry.ts +92 -13
- package/src/modules/discord/index.ts +8 -13
- package/src/modules/history/index.ts +25 -3
- package/src/modules/history/semantic.ts +29 -6
- package/src/modules/index.ts +1 -0
- package/src/modules/unknown-tool.ts +4 -1
- package/src/modules/workspace/branch-intent.ts +3 -1
- package/src/modules/workspace/edit-plan.ts +954 -0
- package/src/modules/workspace/edit-text.ts +618 -0
- package/src/modules/workspace/effects.ts +70 -19
- package/src/modules/workspace/index.ts +2247 -642
- package/src/modules/workspace/observe.ts +465 -56
- package/src/modules/workspace/reconcile.ts +14 -7
- package/src/modules/workspace/text.ts +68 -0
- package/src/modules/workspace/types.ts +211 -6
- package/src/modules/workspace/watcher.ts +3 -2
- package/src/operator-log.ts +47 -19
- package/src/recovery/discord-awareness-outbox.ts +1500 -356
- package/src/recovery/offline-branch.ts +189 -55
- package/src/recovery/recover-cli.ts +25 -4
- package/src/send-tools.ts +11 -0
- package/src/tool-result-guard.ts +6 -5
- package/src/tool-result-history.ts +1 -1
- package/src/tune-out/coordinator.ts +163 -42
- package/src/types/agent.ts +4 -0
- package/src/types/events.ts +62 -9
- package/src/types/framework.ts +62 -58
- package/src/types/module.ts +40 -8
- package/src/types/trace.ts +121 -15
- package/src/usage/types.ts +16 -0
- package/src/usage/usage-tracker.ts +86 -43
- package/dist/src/code-execution/py-runner.d.ts +0 -191
- package/dist/src/code-execution/py-runner.d.ts.map +0 -1
- package/dist/src/code-execution/py-runner.js +0 -655
- package/dist/src/code-execution/py-runner.js.map +0 -1
- package/dist/src/code-execution/runtime-py.d.ts +0 -50
- package/dist/src/code-execution/runtime-py.d.ts.map +0 -1
- package/dist/src/code-execution/runtime-py.js +0 -677
- package/dist/src/code-execution/runtime-py.js.map +0 -1
- package/dist/src/mcpl/prose-stream-router.d.ts +0 -88
- package/dist/src/mcpl/prose-stream-router.d.ts.map +0 -1
- package/dist/src/mcpl/prose-stream-router.js +0 -291
- package/dist/src/mcpl/prose-stream-router.js.map +0 -1
- package/src/code-execution/py-runner.ts +0 -791
- package/src/code-execution/runtime-py.ts +0 -677
- package/src/mcpl/prose-stream-router.ts +0 -268
|
@@ -15,12 +15,15 @@ import { openTransport, STDERR_END_WAIT_MS, type McplTransport, type TransportCl
|
|
|
15
15
|
import { maskNegotiatedCapabilities } from './capability-mask.js';
|
|
16
16
|
import { CapabilityGrant, expandAdvertisementShorthand } from './capability-grant.js';
|
|
17
17
|
import { CAPABILITY_DISABLED, boundedForLog, describeCause } from './errors.js';
|
|
18
|
-
import { logDetail, logValue, logValueRedacted } from './log-value.js';
|
|
18
|
+
import { logDetail, logValue, logValueRedacted, loggedCode } from './log-value.js';
|
|
19
19
|
import {
|
|
20
20
|
LEGACY_MCP_PROTOCOL_VERSION,
|
|
21
21
|
MODERN_MCP_PROTOCOL_VERSION,
|
|
22
22
|
checkServerConfig,
|
|
23
23
|
redactServerText,
|
|
24
|
+
redactedData,
|
|
25
|
+
serverTextRedactor,
|
|
26
|
+
type ServerTextRedactor,
|
|
24
27
|
} from './protocol-family.js';
|
|
25
28
|
|
|
26
29
|
import type {
|
|
@@ -84,18 +87,30 @@ export interface McplConnectionCloseInfo {
|
|
|
84
87
|
* Spring Boot + JDA servers can take 5-10s to boot, so 30s is safe. */
|
|
85
88
|
const INITIALIZE_TIMEOUT_MS = 30_000;
|
|
86
89
|
|
|
90
|
+
/** How many errors down a `cause` chain stderrTailOf looks. */
|
|
91
|
+
const STDERR_TAIL_CAUSE_LINKS = 8;
|
|
92
|
+
|
|
87
93
|
/** A failed connect's `stderrTail`, the last of the server's stderr that its
|
|
88
|
-
* handshake's error keeps, when there is one
|
|
94
|
+
* handshake's error keeps, when there is one: the error's own, or the
|
|
95
|
+
* nearest one in its `cause` chain, since a launch that couldn't be reaped
|
|
96
|
+
* (McplUnreapedLaunchError) and the framework's refusals wrap the
|
|
97
|
+
* handshake's error. It looks at most STDERR_TAIL_CAUSE_LINKS causes down,
|
|
98
|
+
* so a chain that loops ends. */
|
|
89
99
|
export function stderrTailOf(error: unknown): string | undefined {
|
|
90
|
-
|
|
91
|
-
|
|
100
|
+
let at = error;
|
|
101
|
+
for (let link = 0; link <= STDERR_TAIL_CAUSE_LINKS && typeof at === 'object' && at !== null; link++) {
|
|
102
|
+
const { stderrTail, cause } = at as { stderrTail?: unknown; cause?: unknown };
|
|
103
|
+
if (typeof stderrTail === 'string' && stderrTail !== '') return stderrTail;
|
|
104
|
+
at = cause;
|
|
105
|
+
}
|
|
106
|
+
return undefined;
|
|
92
107
|
}
|
|
93
108
|
|
|
94
109
|
/** For the host's own log line about a failed connect: the server's stderr
|
|
95
|
-
* that the error keeps as `stderrTail
|
|
96
|
-
* taken out by `redact` (its
|
|
97
|
-
*
|
|
98
|
-
* none. */
|
|
110
|
+
* that the error, or the nearest error it wraps, keeps as `stderrTail`
|
|
111
|
+
* (stderrTailOf), with the server's credentials taken out by `redact` (its
|
|
112
|
+
* redactServerText), one marked line per line of it, each through
|
|
113
|
+
* log-value, since it's the server's text. '' when there's none. */
|
|
99
114
|
export function withStderr(error: unknown, redact: (text: string) => string): string {
|
|
100
115
|
const tail = stderrTailOf(error);
|
|
101
116
|
if (tail === undefined) return '';
|
|
@@ -156,11 +171,13 @@ export class McplProtocolVersionError extends Error {
|
|
|
156
171
|
constructor(
|
|
157
172
|
message: string,
|
|
158
173
|
readonly kind: 'rejected' | 'mismatch',
|
|
159
|
-
/** Revision the server answered with (`mismatch`; null when it gave
|
|
174
|
+
/** Revision the server answered with (`mismatch`; null when it gave
|
|
175
|
+
* none), redacted as the server's words are. */
|
|
160
176
|
readonly returned: string | null,
|
|
161
177
|
/** Revisions the server says it supports (`rejected`, when stated). */
|
|
162
178
|
readonly supported: readonly string[] | null,
|
|
163
|
-
/** The JSON-RPC error's code and data
|
|
179
|
+
/** The JSON-RPC error's code, and its data with each string in it
|
|
180
|
+
* redacted (`rejected`). */
|
|
164
181
|
readonly code?: number,
|
|
165
182
|
readonly data?: unknown,
|
|
166
183
|
/** The revision this client offered: the legacy engine's, unless a
|
|
@@ -172,8 +189,13 @@ export class McplProtocolVersionError extends Error {
|
|
|
172
189
|
}
|
|
173
190
|
}
|
|
174
191
|
|
|
175
|
-
function protocolVersionRejection(
|
|
176
|
-
const
|
|
192
|
+
function protocolVersionRejection(config: McplServerConfig, error: { code: number; message: string; data?: unknown }): McplProtocolVersionError {
|
|
193
|
+
const id = config.id;
|
|
194
|
+
// The server's words and data, redacted before they're bounded or kept:
|
|
195
|
+
// the error goes to the host. One build for all of them, so a
|
|
196
|
+
// configuration that can't be read is read once.
|
|
197
|
+
const redact = serverTextRedactor(config).now();
|
|
198
|
+
const data = redactedData(error.data, redact) as { supported?: unknown } | undefined;
|
|
177
199
|
const supported = Array.isArray(data?.supported) && data.supported.every((v) => typeof v === 'string')
|
|
178
200
|
? (data.supported as string[])
|
|
179
201
|
: null;
|
|
@@ -183,14 +205,15 @@ function protocolVersionRejection(id: string, error: { code: number; message: st
|
|
|
183
205
|
return new McplProtocolVersionError(
|
|
184
206
|
`MCPL server "${id}" does not support MCP ${MCP_PROTOCOL_VERSION}` +
|
|
185
207
|
// The server's message can be any JSON value, so it's read without
|
|
186
|
-
// throwing (describeCause) and bounded, as the
|
|
187
|
-
|
|
208
|
+
// throwing (describeCause), and redacted, then bounded, as the
|
|
209
|
+
// initialize error is.
|
|
210
|
+
(supported ? ` (it supports: ${boundedForLog(supported.join(', ')) || 'none listed'}).` : ` (${boundedForLog(redact(describeCause(error.message)))}).`) +
|
|
188
211
|
modernHint,
|
|
189
212
|
'rejected',
|
|
190
213
|
null,
|
|
191
214
|
supported,
|
|
192
215
|
error.code,
|
|
193
|
-
|
|
216
|
+
data,
|
|
194
217
|
);
|
|
195
218
|
}
|
|
196
219
|
|
|
@@ -218,16 +241,21 @@ class HandshakeCleanupError extends Error {
|
|
|
218
241
|
* is confirmed, so the caller can still clean up, and should start nothing
|
|
219
242
|
* else for this server until then. connect() and connectWithReconnect()
|
|
220
243
|
* throw this, and the modern engine's ModernMcpConnection.connect() too.
|
|
221
|
-
* `cause` is the connect's own failure.
|
|
244
|
+
* `cause` is the connect's own failure. `connection` isn't enumerable, so
|
|
245
|
+
* printing the error doesn't print the connection's configuration, its
|
|
246
|
+
* credentials included.
|
|
222
247
|
*/
|
|
223
248
|
export class McplUnreapedLaunchError<Connection extends { close(): Promise<void> } = McplServerConnection> extends Error {
|
|
249
|
+
declare readonly connection: Connection;
|
|
250
|
+
|
|
224
251
|
constructor(
|
|
225
|
-
|
|
252
|
+
connection: Connection,
|
|
226
253
|
failure: Error,
|
|
227
254
|
/** The cleanup's own failure: the child it couldn't confirm exited. */
|
|
228
255
|
readonly cleanup: Error,
|
|
229
256
|
) {
|
|
230
257
|
super(`${failure.message}; its cleanup also failed: ${cleanup.message}`, { cause: failure });
|
|
258
|
+
Object.defineProperty(this, 'connection', { value: connection, enumerable: false });
|
|
231
259
|
this.name = 'McplUnreapedLaunchError';
|
|
232
260
|
}
|
|
233
261
|
}
|
|
@@ -250,8 +278,9 @@ export class McplRequestError extends Error {
|
|
|
250
278
|
constructor(
|
|
251
279
|
message: string,
|
|
252
280
|
readonly outcome: 'not-sent' | 'error-response' | 'no-response',
|
|
253
|
-
/** The JSON-RPC error's `data`,
|
|
254
|
-
*
|
|
281
|
+
/** The JSON-RPC error's `data`, when the server supplied one (e.g. which
|
|
282
|
+
* parts of a multi-part send were posted), with each string in it
|
|
283
|
+
* redacted (redactedData): a host hands it on. */
|
|
255
284
|
readonly data?: unknown,
|
|
256
285
|
) {
|
|
257
286
|
super(message);
|
|
@@ -529,8 +558,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
529
558
|
|
|
530
559
|
// Event buffering: control-plane traffic can be released independently from
|
|
531
560
|
// inference-bearing data-plane traffic. This lets a server finish its own
|
|
532
|
-
// registration (and answer host tools/call requests) while
|
|
533
|
-
//
|
|
561
|
+
// registration (and answer host tools/call requests) while its data plane
|
|
562
|
+
// stays held, as through a quiesce.
|
|
534
563
|
private controlPlaneReady = false;
|
|
535
564
|
private dataPlaneReady = false;
|
|
536
565
|
private bufferedEvents: HeldItem[] = [];
|
|
@@ -568,6 +597,15 @@ export class McplServerConnection extends EventEmitter {
|
|
|
568
597
|
|
|
569
598
|
// Reconnect state (adapted from Anarchid/agent-framework@mcpl-module-proto)
|
|
570
599
|
private config: McplServerConfig | null = null;
|
|
600
|
+
/** The redactor of the server's text for `config`, made at its first error
|
|
601
|
+
* answer, by when `config` is set, and kept: a framework-launched child's
|
|
602
|
+
* `env` is a getter that reads the awareness ledger, so a redactor built
|
|
603
|
+
* per answer would read it per answer (#540's review, R1-2). An answer
|
|
604
|
+
* takes one build of it for its words, data and log line (`now()`), so
|
|
605
|
+
* it reads the ledger at most once. It builds again only after a build
|
|
606
|
+
* that failed, which withholds that answer's text, or once a dial notes
|
|
607
|
+
* something new (serverTextRedactor). */
|
|
608
|
+
private redactor: ServerTextRedactor | null = null;
|
|
571
609
|
private hostCapabilities: McplHostCapabilities | null = null;
|
|
572
610
|
private reconnectEnabled = false;
|
|
573
611
|
private reconnectIntervalMs = 5000;
|
|
@@ -666,8 +704,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
666
704
|
`(long pause backlog${dropped > 0 ? `; ${dropped} dropped at cap` : ''})`,
|
|
667
705
|
);
|
|
668
706
|
}
|
|
669
|
-
// Re-enter the gate for every buffered item. A control handler (
|
|
670
|
-
//
|
|
707
|
+
// Re-enter the gate for every buffered item. A control handler (a
|
|
708
|
+
// host/command quiesce, say) may pause the data plane while this
|
|
671
709
|
// snapshot is being flushed; later data items must observe that pause.
|
|
672
710
|
this.flushHeld(pending);
|
|
673
711
|
}
|
|
@@ -831,9 +869,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
831
869
|
|
|
832
870
|
/**
|
|
833
871
|
* Close the data plane synchronously while leaving an already-released
|
|
834
|
-
* control plane live. Used
|
|
835
|
-
*
|
|
836
|
-
* planes instead.
|
|
872
|
+
* control plane live. Used by quiesce, and by reconnectAfterFailure before
|
|
873
|
+
* its teardown. A reconnect closes both planes instead.
|
|
837
874
|
*/
|
|
838
875
|
pauseDataPlane(): void {
|
|
839
876
|
this.dataPlaneReady = false;
|
|
@@ -841,8 +878,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
841
878
|
|
|
842
879
|
/**
|
|
843
880
|
* Override emit to buffer server→host events until the corresponding plane
|
|
844
|
-
* is ready. Lifecycle events always pass through so reconnect
|
|
845
|
-
*
|
|
881
|
+
* is ready. Lifecycle events always pass through so the reconnect listener
|
|
882
|
+
* runs before the fresh transport's buffered traffic is released.
|
|
846
883
|
*/
|
|
847
884
|
override emit(event: string | symbol, ...args: unknown[]): boolean {
|
|
848
885
|
const name = typeof event === 'string' ? event : '';
|
|
@@ -1169,18 +1206,22 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1169
1206
|
},
|
|
1170
1207
|
};
|
|
1171
1208
|
|
|
1209
|
+
// When the transport fails, the end of the server's stderr often says
|
|
1210
|
+
// why, such as a missing token or a crash at startup. It goes on the
|
|
1211
|
+
// error as `stderrTail`, never into the message: an error's message
|
|
1212
|
+
// travels to traces and tool results, and a server's stderr is its own
|
|
1213
|
+
// text, which can hold a credential.
|
|
1214
|
+
const failed = (failure: string): Error & { stderrTail?: string } => {
|
|
1215
|
+
// It goes to the host too, so it's redacted, from a window of the
|
|
1216
|
+
// stream longer than the tail, so a credential the tail's cut
|
|
1217
|
+
// would split is whole when it's matched (stderrTailRedacted).
|
|
1218
|
+
const tail = transport.stderrTailRedacted(serverTextRedactor(config)).trimEnd();
|
|
1219
|
+
return Object.assign(new Error(failure), tail ? { stderrTail: tail } : {});
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1172
1222
|
// Await the initialize response, racing an early transport close/error,
|
|
1173
1223
|
// a timeout and the owner's close. Every path cleans up its listeners.
|
|
1174
1224
|
const { mcpl: rawCapabilities, mcpToolsAdvertised, protocolVersion } = await new Promise<{ mcpl: McplCapabilities | null; mcpToolsAdvertised: boolean; protocolVersion: string }>((resolve, reject) => {
|
|
1175
|
-
// When the transport fails, the end of the server's stderr often says
|
|
1176
|
-
// why, such as a missing token or a crash at startup. It goes on the
|
|
1177
|
-
// error as `stderrTail`, never into the message: an error's message
|
|
1178
|
-
// travels to traces and tool results, and a server's stderr is its
|
|
1179
|
-
// own text, which can hold a credential.
|
|
1180
|
-
const failed = (failure: string): Error & { stderrTail?: string } => {
|
|
1181
|
-
const tail = transport.stderrTail.trimEnd();
|
|
1182
|
-
return Object.assign(new Error(failure), tail ? { stderrTail: tail } : {});
|
|
1183
|
-
};
|
|
1184
1225
|
const cleanup = () => {
|
|
1185
1226
|
transport.off('line', onLine);
|
|
1186
1227
|
transport.off('close', onClose);
|
|
@@ -1222,21 +1263,25 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1222
1263
|
// listener after cleanup() has cleared the timeout: an uncaught
|
|
1223
1264
|
// exception, which ends a host that doesn't catch them, and a
|
|
1224
1265
|
// handshake left to wait forever. It's the server's text in an
|
|
1225
|
-
// error
|
|
1266
|
+
// error the host gets, so it's redacted, then bounded as
|
|
1267
|
+
// errors.ts says: a cut or a replaced character first would
|
|
1268
|
+
// leave part of a credential the redactor can't match.
|
|
1226
1269
|
reject(msg.error.code === UNSUPPORTED_PROTOCOL_VERSION_CODE
|
|
1227
|
-
? protocolVersionRejection(config
|
|
1228
|
-
: new Error(`MCPL server "${config.id}" initialize error: ${boundedForLog(describeCause(msg.error.message))}`));
|
|
1270
|
+
? protocolVersionRejection(config, msg.error)
|
|
1271
|
+
: new Error(`MCPL server "${config.id}" initialize error: ${boundedForLog(redactServerText(describeCause(msg.error.message), config))}`));
|
|
1229
1272
|
return;
|
|
1230
1273
|
}
|
|
1231
1274
|
const result = msg.result as Record<string, unknown> | undefined;
|
|
1232
1275
|
// The server's answer is the established revision. This engine
|
|
1233
1276
|
// implements exactly one, so any other answer (or none) means the
|
|
1234
1277
|
// two sides never agreed on what they're speaking.
|
|
1235
|
-
const
|
|
1236
|
-
if (
|
|
1278
|
+
const answered = typeof result?.protocolVersion === 'string' ? result.protocolVersion : null;
|
|
1279
|
+
if (answered !== MCP_PROTOCOL_VERSION) {
|
|
1280
|
+
// The server's words go to the host, so they're redacted.
|
|
1281
|
+
const returned = answered === null ? null : redactServerText(answered, config);
|
|
1237
1282
|
reject(new McplProtocolVersionError(
|
|
1238
1283
|
`MCPL server "${config.id}" answered initialize with protocolVersion ` +
|
|
1239
|
-
`${returned === null ? '(none)' : JSON.stringify(returned)}; this client's legacy engine speaks only ${MCP_PROTOCOL_VERSION}`,
|
|
1284
|
+
`${returned === null ? '(none)' : boundedForLog(JSON.stringify(returned))}; this client's legacy engine speaks only ${MCP_PROTOCOL_VERSION}`,
|
|
1240
1285
|
'mismatch',
|
|
1241
1286
|
returned,
|
|
1242
1287
|
null,
|
|
@@ -1251,7 +1296,7 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1251
1296
|
resolve({
|
|
1252
1297
|
mcpl: (experimental?.mcpl as McplCapabilities) ?? null,
|
|
1253
1298
|
mcpToolsAdvertised: caps?.tools !== undefined,
|
|
1254
|
-
protocolVersion:
|
|
1299
|
+
protocolVersion: answered,
|
|
1255
1300
|
});
|
|
1256
1301
|
};
|
|
1257
1302
|
const onClose = (info: TransportCloseInfo) => {
|
|
@@ -1281,6 +1326,22 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1281
1326
|
transport.writeLine(JSON.stringify(initRequest));
|
|
1282
1327
|
});
|
|
1283
1328
|
|
|
1329
|
+
// The transport has found the child's stdin or stdout failed before the
|
|
1330
|
+
// handshake could hand it on, such as when this host's answer to a ping
|
|
1331
|
+
// the server sent met EPIPE. Nothing more is written to the child then,
|
|
1332
|
+
// so the connection couldn't serve. It's read here, once the answer has
|
|
1333
|
+
// passed its own checks, rather than as the answer arrives: a write's
|
|
1334
|
+
// EPIPE is reported only once the callback that wrote it has returned,
|
|
1335
|
+
// so a ping and the answer read in one callback would pass a check made
|
|
1336
|
+
// there. A child that outlived SIGKILL may already have said so with
|
|
1337
|
+
// nothing listening (the transport's 'unreaped' is wired only once the
|
|
1338
|
+
// handshake hands it on). So the handshake fails, and its cleanup holds
|
|
1339
|
+
// the launch if it can't reap the child. A failure of the write below
|
|
1340
|
+
// is reported only once the caller has wired the transport, so it
|
|
1341
|
+
// reaches the connection as a loss, like any later one.
|
|
1342
|
+
const pipeFailure = transport.pipeFailure;
|
|
1343
|
+
if (pipeFailure) throw failed(`MCPL server "${config.id}" stdio pipe failed with ${pipeFailure.message} during the handshake`);
|
|
1344
|
+
|
|
1284
1345
|
// Send `initialized` notification (no id)
|
|
1285
1346
|
transport.writeLine(JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' }));
|
|
1286
1347
|
|
|
@@ -1485,9 +1546,13 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1485
1546
|
return this.sendRequest(McplMethod.ChannelsOpen, params as unknown as Record<string, unknown>, { timeoutMs }) as Promise<ChannelsOpenResult>;
|
|
1486
1547
|
}
|
|
1487
1548
|
|
|
1488
|
-
/** Send `channels/close` request and await result.
|
|
1549
|
+
/** Send `channels/close` request and await result. It keeps a deadline as
|
|
1550
|
+
* channels/open does: a delivery waits for a channel's queued lifecycle
|
|
1551
|
+
* work, closes included, so a close that is never answered would hold the
|
|
1552
|
+
* delivery, and the turn that waits for it, for good. */
|
|
1489
1553
|
sendChannelsClose(params: ChannelsCloseParams): Promise<ChannelsCloseResult> {
|
|
1490
|
-
|
|
1554
|
+
const timeoutMs = this.requestTimeoutMs > 0 ? this.requestTimeoutMs : this.publishDeadlineMs;
|
|
1555
|
+
return this.sendRequest(McplMethod.ChannelsClose, params as unknown as Record<string, unknown>, { timeoutMs }) as Promise<ChannelsCloseResult>;
|
|
1491
1556
|
}
|
|
1492
1557
|
|
|
1493
1558
|
/** Send `channels/acknowledge` request and await result. */
|
|
@@ -1585,10 +1650,11 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1585
1650
|
return { ...rest, tools };
|
|
1586
1651
|
}
|
|
1587
1652
|
if (typeof result.nextCursor !== 'string') {
|
|
1588
|
-
|
|
1653
|
+
// The cursor is the server's, so it's redacted, then bounded.
|
|
1654
|
+
throw malformed(`nextCursor must be a string, got ${boundedForLog(redactServerText(JSON.stringify(result.nextCursor), this.config ?? {}))}`);
|
|
1589
1655
|
}
|
|
1590
1656
|
if (seen.has(result.nextCursor)) {
|
|
1591
|
-
throw new Error(`MCPL server "${this.id}" repeated tools/list cursor ${JSON.stringify(result.nextCursor)}`);
|
|
1657
|
+
throw new Error(`MCPL server "${this.id}" repeated tools/list cursor ${boundedForLog(redactServerText(JSON.stringify(result.nextCursor), this.config ?? {}))}`);
|
|
1592
1658
|
}
|
|
1593
1659
|
seen.add(result.nextCursor);
|
|
1594
1660
|
cursor = result.nextCursor;
|
|
@@ -1895,9 +1961,8 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1895
1961
|
// boundary resets, so control traffic too (a server's channels/register
|
|
1896
1962
|
// right after initialize) must wait for the new grant rather than be
|
|
1897
1963
|
// refused against the empty one. The reconnect lifecycle event bypasses
|
|
1898
|
-
// buffering; its framework listener
|
|
1899
|
-
//
|
|
1900
|
-
// and then opens the planes.
|
|
1964
|
+
// buffering; its framework listener re-establishes the grant (§5.3
|
|
1965
|
+
// policy responses are never buffered), and then opens the planes.
|
|
1901
1966
|
this.controlPlaneReady = false;
|
|
1902
1967
|
this.dataPlaneReady = false;
|
|
1903
1968
|
this.resetPolicyForTransportBoundary();
|
|
@@ -1921,6 +1986,18 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1921
1986
|
// Private: message sending
|
|
1922
1987
|
// ==========================================================================
|
|
1923
1988
|
|
|
1989
|
+
/**
|
|
1990
|
+
* A request refused before anything is kept for it, as `error`, with its
|
|
1991
|
+
* operator's line logged after the refusal: a host logger that throws then
|
|
1992
|
+
* loses that line, and can't turn the refusal into a throw from the call.
|
|
1993
|
+
*/
|
|
1994
|
+
private static refused(error: McplRequestError, line: string): Promise<never> {
|
|
1995
|
+
return new Promise((_, reject) => {
|
|
1996
|
+
reject(error);
|
|
1997
|
+
console.error(line);
|
|
1998
|
+
});
|
|
1999
|
+
}
|
|
2000
|
+
|
|
1924
2001
|
/**
|
|
1925
2002
|
* Send a JSON-RPC request and return a promise that resolves with the result
|
|
1926
2003
|
* or rejects with a JSON-RPC error.
|
|
@@ -1958,14 +2035,16 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1958
2035
|
// pipe's error are the operator's, so they go to the log.
|
|
1959
2036
|
const pipeFailure = this.transport?.pipeFailure;
|
|
1960
2037
|
if (pipeFailure) {
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2038
|
+
return McplServerConnection.refused(
|
|
2039
|
+
new McplRequestError(
|
|
2040
|
+
`The request wasn't sent: this host can no longer write to the "${this.id}" server, so nothing ran. ` +
|
|
2041
|
+
(this.willReconnect
|
|
2042
|
+
? 'This host is reconnecting to it; try again later.'
|
|
2043
|
+
: "This host won't reconnect to it on its own. If you need it, ask whoever runs this host."),
|
|
2044
|
+
'not-sent',
|
|
2045
|
+
),
|
|
2046
|
+
`MCPL server "${this.id}": ${method} not sent, since its stdio pipe failed: ${logDetail(pipeFailure.message)}`,
|
|
2047
|
+
);
|
|
1969
2048
|
}
|
|
1970
2049
|
|
|
1971
2050
|
const id = this.nextRequestId++;
|
|
@@ -1975,6 +2054,23 @@ export class McplServerConnection extends EventEmitter {
|
|
|
1975
2054
|
id,
|
|
1976
2055
|
params,
|
|
1977
2056
|
};
|
|
2057
|
+
// A request JSON can't write (a BigInt, a cycle) never leaves this host,
|
|
2058
|
+
// and calling again fails the same way, so it's refused before anything
|
|
2059
|
+
// is kept for it, in the modern engine's words (#556). The method, id and
|
|
2060
|
+
// the serializer's words are the operator's.
|
|
2061
|
+
let line: string;
|
|
2062
|
+
try {
|
|
2063
|
+
line = JSON.stringify(request);
|
|
2064
|
+
} catch (err) {
|
|
2065
|
+
return McplServerConnection.refused(
|
|
2066
|
+
new McplRequestError(
|
|
2067
|
+
`The request wasn't sent: this host couldn't send it to the "${this.id}" server, so nothing ran. ` +
|
|
2068
|
+
'If calling it again fails, ask whoever runs this host.',
|
|
2069
|
+
'not-sent',
|
|
2070
|
+
),
|
|
2071
|
+
`MCPL server "${this.id}": ${method} (id=${id}) not sent, since it can't be written as JSON: ${logDetail(err instanceof Error ? err.message : err)}`,
|
|
2072
|
+
);
|
|
2073
|
+
}
|
|
1978
2074
|
|
|
1979
2075
|
return new Promise((resolve, reject) => {
|
|
1980
2076
|
const pending: PendingRequest = { resolve, reject, method };
|
|
@@ -2017,20 +2113,21 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2017
2113
|
}
|
|
2018
2114
|
this.pendingRequests.set(id, pending);
|
|
2019
2115
|
try {
|
|
2020
|
-
this.transport!.writeLine(
|
|
2116
|
+
this.transport!.writeLine(line);
|
|
2021
2117
|
} catch (err) {
|
|
2022
2118
|
// A synchronous write rejection sent no request. Remove its pending
|
|
2023
2119
|
// state and watchdog before reporting that it is safe to retry. The
|
|
2024
|
-
// method, id and the transport's words are the operator's
|
|
2120
|
+
// method, id and the transport's words are the operator's, logged
|
|
2121
|
+
// after the refusal, as refused() logs.
|
|
2025
2122
|
this.pendingRequests.delete(id);
|
|
2026
2123
|
if (pending.timer) clearTimeout(pending.timer);
|
|
2027
|
-
console.error(
|
|
2028
|
-
`MCPL server "${this.id}": writing ${method} (id=${id}) failed: ${logDetail(err instanceof Error ? err.message : err)}`,
|
|
2029
|
-
);
|
|
2030
2124
|
reject(new McplRequestError(
|
|
2031
2125
|
`The request wasn't sent: this host couldn't write it to the "${this.id}" server, so nothing ran. Try again later.`,
|
|
2032
2126
|
'not-sent',
|
|
2033
2127
|
));
|
|
2128
|
+
console.error(
|
|
2129
|
+
`MCPL server "${this.id}": writing ${method} (id=${id}) failed: ${logDetail(err instanceof Error ? err.message : err)}`,
|
|
2130
|
+
);
|
|
2034
2131
|
}
|
|
2035
2132
|
});
|
|
2036
2133
|
}
|
|
@@ -2389,16 +2486,18 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2389
2486
|
if (pending.timer) clearTimeout(pending.timer);
|
|
2390
2487
|
|
|
2391
2488
|
if (response.error) {
|
|
2392
|
-
// The server's own words, after a word on whose they are,
|
|
2393
|
-
// errors.ts says, since they're the server's text
|
|
2394
|
-
//
|
|
2395
|
-
//
|
|
2396
|
-
//
|
|
2397
|
-
//
|
|
2398
|
-
//
|
|
2399
|
-
//
|
|
2400
|
-
//
|
|
2401
|
-
|
|
2489
|
+
// The server's own words, after a word on whose they are, redacted and
|
|
2490
|
+
// then bounded as errors.ts says, since they're the server's text in
|
|
2491
|
+
// an error the caller gets (redactServerText's scope), and its data's
|
|
2492
|
+
// strings redacted too. It's trimmed after it's redacted, so a
|
|
2493
|
+
// credential that ends in a space is whole when it's matched. Only a
|
|
2494
|
+
// string is words: a message of any other JSON value reads as none.
|
|
2495
|
+
// The method and id are the operator's, so they go to the log, and the
|
|
2496
|
+
// code (loggedCode) and the message, whatever JSON values they are, go
|
|
2497
|
+
// there redacted (logValueRedacted), as every other line this
|
|
2498
|
+
// connection writes is.
|
|
2499
|
+
const redact = (this.redactor ??= serverTextRedactor(this.config ?? {})).now();
|
|
2500
|
+
const said = typeof response.error.message === 'string' ? boundedForLog(redact(response.error.message).trim()) : '';
|
|
2402
2501
|
// The call is settled before its log line: its entry is gone and its
|
|
2403
2502
|
// timer cleared, so nothing else would settle it, and a host's logger
|
|
2404
2503
|
// can throw. The dispatch's containment reports such a throw. (A
|
|
@@ -2410,11 +2509,11 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2410
2509
|
: `The "${this.id}" server answered with an error without saying what went wrong, so whether the request took effect is unknown. ` +
|
|
2411
2510
|
'Check its effect before trying again.',
|
|
2412
2511
|
'error-response',
|
|
2413
|
-
(response.error as { data?: unknown }).data,
|
|
2512
|
+
redactedData((response.error as { data?: unknown }).data, redact),
|
|
2414
2513
|
));
|
|
2415
2514
|
console.error(
|
|
2416
|
-
`MCPL server "${this.id}" answered ${pending.method} (id=${response.id}) with error ${
|
|
2417
|
-
logValueRedacted(response.error.message,
|
|
2515
|
+
`MCPL server "${this.id}" answered ${pending.method} (id=${response.id}) with error ${loggedCode(response.error.code, redact)}: ` +
|
|
2516
|
+
logValueRedacted(response.error.message, redact),
|
|
2418
2517
|
);
|
|
2419
2518
|
} else {
|
|
2420
2519
|
pending.resolve(response.result);
|
|
@@ -2540,8 +2639,10 @@ export class McplServerConnection extends EventEmitter {
|
|
|
2540
2639
|
// reconnecting, and is lost as on a close, with the permanent failure
|
|
2541
2640
|
// that raises the alert and takes its tools off the list. Its id stays
|
|
2542
2641
|
// held until close() confirms the exit. A close the host has begun
|
|
2543
|
-
// reports the failure itself. During a handshake nothing listens yet:
|
|
2544
|
-
//
|
|
2642
|
+
// reports the failure itself. During a handshake nothing listens yet: a
|
|
2643
|
+
// handshake whose pipe has failed fails, when its answer comes (it's
|
|
2644
|
+
// refused then), when the child's exit closes the transport, or at its
|
|
2645
|
+
// timeout, and its own cleanup holds the launch.
|
|
2545
2646
|
transport.on('unreaped', (failure: Error) => {
|
|
2546
2647
|
if (this.closed || this.transport !== transport) return;
|
|
2547
2648
|
this.holdUnreaped(transport, failure);
|
package/src/mcpl/tags.ts
CHANGED
|
@@ -18,6 +18,42 @@ const CORE_IMPLIES: Readonly<Record<string, readonly string[]>> = {
|
|
|
18
18
|
'chat:dm': ['chat:addressed', 'chat:private'],
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
/**
|
|
22
|
+
* RFC-001 core tags that mark a message as being ABOUT the conversation
|
|
23
|
+
* rather than part of it: reactions (added or removed), edits and deletions
|
|
24
|
+
* of an existing message. These are markers, not turns — they must not clear
|
|
25
|
+
* explicit-send suppression, count as a new conversational round, or
|
|
26
|
+
* retarget the default publish channel. An edit can also be the first
|
|
27
|
+
* version of a message the agent sees, and then counts as that message:
|
|
28
|
+
* isMarkerTagged reads the host's mark for the case the coalescer knows
|
|
29
|
+
* (`firstView`), for isConversationalInjection and for the channel
|
|
30
|
+
* registry's publish channel and reply anchor. Tags are
|
|
31
|
+
* still never authority (§16.6): this set only ever WITHHOLDS default
|
|
32
|
+
* conversational treatment, it never grants anything.
|
|
33
|
+
*/
|
|
34
|
+
export const NON_CONVERSATIONAL_TAGS: ReadonlySet<string> = new Set([
|
|
35
|
+
'chat:reaction',
|
|
36
|
+
'chat:reaction-remove',
|
|
37
|
+
'chat:edited',
|
|
38
|
+
'chat:deleted',
|
|
39
|
+
]);
|
|
40
|
+
|
|
41
|
+
/** True when a (possibly untyped) tag list carries a non-conversational marker tag. */
|
|
42
|
+
export function hasNonConversationalTag(tags: unknown): boolean {
|
|
43
|
+
return Array.isArray(tags) && tags.some((t) => typeof t === 'string' && NON_CONVERSATIONAL_TAGS.has(t));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* True when a message is a marker by its tags. `firstView` is the host's own
|
|
48
|
+
* mark that the coalescer delivered the message as the agent's first view of
|
|
49
|
+
* it (a stored message's metadata `firstView`, or in the channel registry the
|
|
50
|
+
* admission's receipt), never a server's claim: an edit so delivered counts
|
|
51
|
+
* as the message it replaced, while a reaction or deletion stays a notice.
|
|
52
|
+
*/
|
|
53
|
+
export function isMarkerTagged(tags: unknown, firstView: boolean): boolean {
|
|
54
|
+
return hasNonConversationalTag(firstView && Array.isArray(tags) ? tags.filter((t) => t !== 'chat:edited') : tags);
|
|
55
|
+
}
|
|
56
|
+
|
|
21
57
|
/**
|
|
22
58
|
* Expand a raw tag list over the normative core closure, then resolve the
|
|
23
59
|
* §16.2 mutual exclusion: `chat:addressed` and `chat:ambient` cannot both
|
|
@@ -36,7 +36,8 @@ export const MALFORMED_CONTENT_NOTICE =
|
|
|
36
36
|
'[malformed result: its content is not a list of blocks; the tool may already have completed]';
|
|
37
37
|
|
|
38
38
|
/** Saves a payload under a host-chosen name; resolves to its workspace path,
|
|
39
|
-
* or null when there is
|
|
39
|
+
* or null when there is no writable workspace mount without an include to
|
|
40
|
+
* put it in. */
|
|
40
41
|
export type SaveToolPayload = (fileName: string, bytes: Buffer, mimeType: string) => Promise<string | null>;
|
|
41
42
|
|
|
42
43
|
/** Stub fields are bounded independently of anything the server sends. */
|