@animalabs/agent-framework 0.22.2 → 0.23.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 +108 -4
- package/dist/src/agent.d.ts +108 -17
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +207 -67
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.js +1 -1
- package/dist/src/api/mcp-server-core.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +5 -1
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/api/types.d.ts +1 -1
- package/dist/src/api/types.d.ts.map +1 -1
- package/dist/src/branch-names.d.ts +14 -0
- package/dist/src/branch-names.d.ts.map +1 -0
- package/dist/src/branch-names.js +22 -0
- package/dist/src/branch-names.js.map +1 -0
- package/dist/src/closest-names.d.ts +28 -0
- package/dist/src/closest-names.d.ts.map +1 -0
- package/dist/src/closest-names.js +98 -0
- package/dist/src/closest-names.js.map +1 -0
- package/dist/src/code-execution/index.d.ts +1 -1
- package/dist/src/code-execution/index.d.ts.map +1 -1
- package/dist/src/code-execution/index.js.map +1 -1
- package/dist/src/code-execution/py-runner.d.ts +50 -7
- package/dist/src/code-execution/py-runner.d.ts.map +1 -1
- package/dist/src/code-execution/py-runner.js +258 -90
- package/dist/src/code-execution/py-runner.js.map +1 -1
- package/dist/src/code-execution/runtime-py.d.ts +3 -2
- package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
- package/dist/src/code-execution/runtime-py.js +94 -8
- package/dist/src/code-execution/runtime-py.js.map +1 -1
- package/dist/src/code-execution/tool-definition.d.ts +17 -1
- package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
- package/dist/src/code-execution/tool-definition.js +72 -6
- package/dist/src/code-execution/tool-definition.js.map +1 -1
- package/dist/src/config-checks.d.ts +28 -0
- package/dist/src/config-checks.d.ts.map +1 -0
- package/dist/src/config-checks.js +56 -0
- package/dist/src/config-checks.js.map +1 -0
- package/dist/src/deadline-timer.d.ts +21 -0
- package/dist/src/deadline-timer.d.ts.map +1 -0
- package/dist/src/deadline-timer.js +43 -0
- package/dist/src/deadline-timer.js.map +1 -0
- package/dist/src/error-message.d.ts +30 -0
- package/dist/src/error-message.d.ts.map +1 -0
- package/dist/src/error-message.js +50 -0
- package/dist/src/error-message.js.map +1 -0
- package/dist/src/framework.d.ts +1529 -165
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +8264 -1939
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +8 -0
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +36 -5
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/held-writes.d.ts +16 -0
- package/dist/src/held-writes.d.ts.map +1 -0
- package/dist/src/held-writes.js +56 -0
- package/dist/src/held-writes.js.map +1 -0
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +11 -5
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-grant.d.ts +4 -0
- package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
- package/dist/src/mcpl/capability-grant.js +1 -1
- package/dist/src/mcpl/capability-grant.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +233 -18
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +975 -386
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
- package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
- package/dist/src/mcpl/checkpoint-manager.js +91 -30
- package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +19 -3
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +23 -8
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts +20 -0
- package/dist/src/mcpl/convert-block.d.ts.map +1 -0
- package/dist/src/mcpl/convert-block.js +88 -0
- package/dist/src/mcpl/convert-block.js.map +1 -0
- package/dist/src/mcpl/errors.d.ts +13 -0
- package/dist/src/mcpl/errors.d.ts.map +1 -1
- package/dist/src/mcpl/errors.js +28 -0
- package/dist/src/mcpl/errors.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +83 -72
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +114 -0
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
- package/dist/src/mcpl/inbound-source.js +83 -0
- package/dist/src/mcpl/inbound-source.js.map +1 -0
- package/dist/src/mcpl/index.d.ts +7 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +5 -1
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +19 -13
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +155 -87
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/log-value.d.ts +7 -0
- package/dist/src/mcpl/log-value.d.ts.map +1 -1
- package/dist/src/mcpl/log-value.js +9 -0
- package/dist/src/mcpl/log-value.js.map +1 -1
- package/dist/src/mcpl/modern-connection.d.ts +227 -0
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -0
- package/dist/src/mcpl/modern-connection.js +1088 -0
- package/dist/src/mcpl/modern-connection.js.map +1 -0
- package/dist/src/mcpl/prefix-rule.d.ts +18 -0
- package/dist/src/mcpl/prefix-rule.d.ts.map +1 -0
- package/dist/src/mcpl/prefix-rule.js +25 -0
- package/dist/src/mcpl/prefix-rule.js.map +1 -0
- package/dist/src/mcpl/protocol-family.d.ts +179 -0
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -0
- package/dist/src/mcpl/protocol-family.js +697 -0
- package/dist/src/mcpl/protocol-family.js.map +1 -0
- package/dist/src/mcpl/push-coalescer.d.ts +58 -17
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +93 -32
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/push-handler.d.ts +16 -9
- package/dist/src/mcpl/push-handler.d.ts.map +1 -1
- package/dist/src/mcpl/push-handler.js +18 -83
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +76 -13
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +275 -66
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +98 -18
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +170 -32
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +406 -31
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +1331 -272
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/server-registry.d.ts +35 -6
- package/dist/src/mcpl/server-registry.d.ts.map +1 -1
- package/dist/src/mcpl/server-registry.js +63 -13
- package/dist/src/mcpl/server-registry.js.map +1 -1
- package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
- package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
- package/dist/src/mcpl/skip-reply-wake.js +55 -0
- package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts +54 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.js +232 -0
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +104 -12
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +315 -85
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +76 -19
- 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 +16 -0
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +26 -0
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +34 -6
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +119 -31
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/api/index.d.ts +1 -1
- package/dist/src/modules/api/index.d.ts.map +1 -1
- package/dist/src/modules/api/index.js +3 -2
- package/dist/src/modules/api/index.js.map +1 -1
- package/dist/src/modules/discord/discord-js-client.d.ts +43 -2
- package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
- package/dist/src/modules/discord/discord-js-client.js +286 -67
- package/dist/src/modules/discord/discord-js-client.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts +53 -5
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +712 -186
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/discord/refusal.d.ts +14 -0
- package/dist/src/modules/discord/refusal.d.ts.map +1 -0
- package/dist/src/modules/discord/refusal.js +14 -0
- package/dist/src/modules/discord/refusal.js.map +1 -0
- package/dist/src/modules/discord/types.d.ts +27 -4
- package/dist/src/modules/discord/types.d.ts.map +1 -1
- package/dist/src/modules/health/index.d.ts.map +1 -1
- package/dist/src/modules/health/index.js +8 -3
- package/dist/src/modules/health/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +3 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +359 -127
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +28 -1
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +177 -32
- 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 +1 -1
- package/dist/src/modules/index.js.map +1 -1
- package/dist/src/modules/unknown-tool.d.ts +10 -0
- package/dist/src/modules/unknown-tool.d.ts.map +1 -0
- package/dist/src/modules/unknown-tool.js +17 -0
- package/dist/src/modules/unknown-tool.js.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts +57 -4
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +75 -7
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +66 -7
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +303 -46
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +313 -45
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +1404 -304
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +315 -16
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +727 -37
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +58 -7
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +521 -56
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +28 -5
- 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 +49 -2
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js +158 -29
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/prose-drafts.d.ts +240 -0
- package/dist/src/prose-drafts.d.ts.map +1 -0
- package/dist/src/prose-drafts.js +362 -0
- package/dist/src/prose-drafts.js.map +1 -0
- package/dist/src/prose-segments.d.ts +21 -2
- package/dist/src/prose-segments.d.ts.map +1 -1
- package/dist/src/prose-segments.js +46 -9
- package/dist/src/prose-segments.js.map +1 -1
- package/dist/src/recovery/offline-branch.d.ts +1 -0
- package/dist/src/recovery/offline-branch.d.ts.map +1 -1
- package/dist/src/recovery/offline-branch.js +3 -1
- package/dist/src/recovery/offline-branch.js.map +1 -1
- package/dist/src/safe-slice.d.ts +5 -1
- package/dist/src/safe-slice.d.ts.map +1 -1
- package/dist/src/safe-slice.js +9 -1
- package/dist/src/safe-slice.js.map +1 -1
- package/dist/src/silent-heartbeat.d.ts +112 -39
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js +100 -29
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/tool-args.d.ts +40 -0
- package/dist/src/tool-args.d.ts.map +1 -0
- package/dist/src/tool-args.js +207 -0
- package/dist/src/tool-args.js.map +1 -0
- package/dist/src/tool-image-ledger.d.ts +87 -19
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +135 -32
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts +9 -0
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +266 -62
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +7 -10
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +32 -3
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +106 -19
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +25 -1
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +260 -65
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/types/agent.d.ts +44 -22
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +25 -0
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +38 -7
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/inference-log.d.ts +1 -1
- package/dist/src/types/inference-log.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +77 -14
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +97 -8
- package/dist/src/types/trace.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.d.ts +5 -0
- package/dist/src/usage/usage-tracker.d.ts.map +1 -1
- package/dist/src/usage/usage-tracker.js +35 -8
- package/dist/src/usage/usage-tracker.js.map +1 -1
- package/package.json +5 -4
- package/src/agent.ts +257 -72
- package/src/api/mcp-server-core.ts +1 -1
- package/src/api/server.ts +4 -1
- package/src/api/types.ts +1 -1
- package/src/branch-names.ts +19 -0
- package/src/closest-names.ts +94 -0
- package/src/code-execution/index.ts +1 -0
- package/src/code-execution/py-runner.ts +304 -93
- package/src/code-execution/runtime-py.ts +94 -8
- package/src/code-execution/tool-definition.ts +85 -7
- package/src/config-checks.ts +50 -0
- package/src/deadline-timer.ts +44 -0
- package/src/error-message.ts +48 -0
- package/src/framework.ts +8625 -1984
- package/src/gate/event-gate.ts +38 -5
- package/src/held-writes.ts +59 -0
- package/src/index.ts +29 -5
- package/src/mcpl/capability-grant.ts +1 -1
- package/src/mcpl/channel-registry.ts +1095 -411
- package/src/mcpl/checkpoint-manager.ts +104 -29
- package/src/mcpl/conversation-router.ts +28 -8
- package/src/mcpl/convert-block.ts +105 -0
- package/src/mcpl/errors.ts +28 -0
- package/src/mcpl/hook-orchestrator.ts +84 -77
- package/src/mcpl/inbound-source.ts +156 -0
- package/src/mcpl/index.ts +23 -1
- package/src/mcpl/inference-router.ts +179 -103
- package/src/mcpl/log-value.ts +10 -0
- package/src/mcpl/modern-connection.ts +1182 -0
- package/src/mcpl/prefix-rule.ts +32 -0
- package/src/mcpl/protocol-family.ts +691 -0
- package/src/mcpl/push-coalescer.ts +110 -36
- package/src/mcpl/push-handler.ts +20 -90
- package/src/mcpl/reference-fetcher.ts +318 -67
- package/src/mcpl/references.ts +183 -34
- package/src/mcpl/server-connection.ts +1404 -280
- package/src/mcpl/server-registry.ts +63 -14
- package/src/mcpl/skip-reply-wake.ts +73 -0
- package/src/mcpl/tool-result-normalize.ts +294 -0
- package/src/mcpl/transport.ts +320 -76
- package/src/mcpl/types.ts +77 -19
- package/src/mcpl/visible-content.ts +28 -0
- package/src/module-registry.ts +126 -36
- package/src/modules/api/index.ts +3 -2
- package/src/modules/discord/discord-js-client.ts +306 -74
- package/src/modules/discord/index.ts +737 -201
- package/src/modules/discord/refusal.ts +13 -0
- package/src/modules/discord/types.ts +27 -6
- package/src/modules/health/index.ts +8 -3
- package/src/modules/history/index.ts +410 -120
- package/src/modules/history/semantic.ts +189 -32
- package/src/modules/index.ts +1 -1
- package/src/modules/unknown-tool.ts +16 -0
- package/src/modules/workspace/disk-agreement.ts +97 -8
- package/src/modules/workspace/effects.ts +291 -36
- package/src/modules/workspace/index.ts +1469 -321
- package/src/modules/workspace/observe.ts +784 -41
- package/src/modules/workspace/reconcile.ts +619 -64
- package/src/modules/workspace/types.ts +28 -5
- package/src/modules/workspace/watcher.ts +154 -30
- package/src/prose-drafts.ts +481 -0
- package/src/prose-segments.ts +51 -8
- package/src/recovery/offline-branch.ts +4 -1
- package/src/safe-slice.ts +9 -1
- package/src/silent-heartbeat.ts +184 -59
- package/src/tool-args.ts +213 -0
- package/src/tool-image-ledger.ts +153 -34
- package/src/tool-presentation.ts +233 -42
- package/src/tool-result-guard.ts +7 -8
- package/src/tool-result-history.ts +124 -20
- package/src/tune-out/coordinator.ts +259 -65
- package/src/types/agent.ts +44 -24
- package/src/types/events.ts +25 -0
- package/src/types/framework.ts +38 -7
- package/src/types/inference-log.ts +1 -1
- package/src/types/module.ts +79 -14
- package/src/types/trace.ts +103 -8
- package/src/usage/usage-tracker.ts +32 -8
- package/dist/test/activation-request-sanitize.test.d.ts +0 -2
- package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
- package/dist/test/activation-request-sanitize.test.js +0 -89
- package/dist/test/activation-request-sanitize.test.js.map +0 -1
- package/dist/test/agent-cache-ttl.test.d.ts +0 -2
- package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
- package/dist/test/agent-cache-ttl.test.js +0 -41
- package/dist/test/agent-cache-ttl.test.js.map +0 -1
- package/dist/test/agent-runtime-settings.test.d.ts +0 -2
- package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
- package/dist/test/agent-runtime-settings.test.js +0 -1598
- package/dist/test/agent-runtime-settings.test.js.map +0 -1
- package/dist/test/api-admin-gate.test.d.ts +0 -2
- package/dist/test/api-admin-gate.test.d.ts.map +0 -1
- package/dist/test/api-admin-gate.test.js +0 -116
- package/dist/test/api-admin-gate.test.js.map +0 -1
- package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
- package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
- package/dist/test/assistant-persist-refusal.test.js +0 -63
- package/dist/test/assistant-persist-refusal.test.js.map +0 -1
- package/dist/test/batched-wake-locus.test.d.ts +0 -2
- package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
- package/dist/test/batched-wake-locus.test.js +0 -166
- package/dist/test/batched-wake-locus.test.js.map +0 -1
- package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
- package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
- package/dist/test/busy-agent-scheduler.test.js +0 -92
- package/dist/test/busy-agent-scheduler.test.js.map +0 -1
- package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
- package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
- package/dist/test/channel-incoming-targeting.test.js +0 -81
- package/dist/test/channel-incoming-targeting.test.js.map +0 -1
- package/dist/test/channel-label-history.test.d.ts +0 -2
- package/dist/test/channel-label-history.test.d.ts.map +0 -1
- package/dist/test/channel-label-history.test.js +0 -424
- package/dist/test/channel-label-history.test.js.map +0 -1
- package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
- package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
- package/dist/test/channel-lifecycle-provenance.test.js +0 -47
- package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
- package/dist/test/channel-registry-routing.test.d.ts +0 -2
- package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
- package/dist/test/channel-registry-routing.test.js +0 -446
- package/dist/test/channel-registry-routing.test.js.map +0 -1
- package/dist/test/channel-registry-typing.test.d.ts +0 -2
- package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
- package/dist/test/channel-registry-typing.test.js +0 -91
- package/dist/test/channel-registry-typing.test.js.map +0 -1
- package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
- package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
- package/dist/test/channels-changed-respond-first.test.js +0 -74
- package/dist/test/channels-changed-respond-first.test.js.map +0 -1
- package/dist/test/checkpoint-attribution.test.d.ts +0 -2
- package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
- package/dist/test/checkpoint-attribution.test.js +0 -200
- package/dist/test/checkpoint-attribution.test.js.map +0 -1
- package/dist/test/code-execution.test.d.ts +0 -11
- package/dist/test/code-execution.test.d.ts.map +0 -1
- package/dist/test/code-execution.test.js +0 -1272
- package/dist/test/code-execution.test.js.map +0 -1
- package/dist/test/component-defaults.test.d.ts +0 -2
- package/dist/test/component-defaults.test.d.ts.map +0 -1
- package/dist/test/component-defaults.test.js +0 -54
- package/dist/test/component-defaults.test.js.map +0 -1
- package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
- package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
- package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
- package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
- package/dist/test/conversation-router.test.d.ts +0 -2
- package/dist/test/conversation-router.test.d.ts.map +0 -1
- package/dist/test/conversation-router.test.js +0 -174
- package/dist/test/conversation-router.test.js.map +0 -1
- package/dist/test/conversation-routing.test.d.ts +0 -7
- package/dist/test/conversation-routing.test.d.ts.map +0 -1
- package/dist/test/conversation-routing.test.js +0 -621
- package/dist/test/conversation-routing.test.js.map +0 -1
- package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
- package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
- package/dist/test/deferred-wake-integrity.test.js +0 -320
- package/dist/test/deferred-wake-integrity.test.js.map +0 -1
- package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
- package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
- package/dist/test/discord-awareness-outbox.test.js +0 -371
- package/dist/test/discord-awareness-outbox.test.js.map +0 -1
- package/dist/test/discord-history-delete-window.test.d.ts +0 -2
- package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
- package/dist/test/discord-history-delete-window.test.js +0 -81
- package/dist/test/discord-history-delete-window.test.js.map +0 -1
- package/dist/test/discord-history-sync.test.d.ts +0 -2
- package/dist/test/discord-history-sync.test.d.ts.map +0 -1
- package/dist/test/discord-history-sync.test.js +0 -56
- package/dist/test/discord-history-sync.test.js.map +0 -1
- package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
- package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
- package/dist/test/discord-typing-keepalive.test.js +0 -77
- package/dist/test/discord-typing-keepalive.test.js.map +0 -1
- package/dist/test/ephemeral-settle.test.d.ts +0 -2
- package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
- package/dist/test/ephemeral-settle.test.js +0 -354
- package/dist/test/ephemeral-settle.test.js.map +0 -1
- package/dist/test/event-gate-passthrough.test.d.ts +0 -15
- package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
- package/dist/test/event-gate-passthrough.test.js +0 -289
- package/dist/test/event-gate-passthrough.test.js.map +0 -1
- package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
- package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
- package/dist/test/event-gate-policy-mutation.test.js +0 -137
- package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
- package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
- package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
- package/dist/test/event-gate-rate-limit.test.js +0 -627
- package/dist/test/event-gate-rate-limit.test.js.map +0 -1
- package/dist/test/event-gate-script.test.d.ts +0 -2
- package/dist/test/event-gate-script.test.d.ts.map +0 -1
- package/dist/test/event-gate-script.test.js +0 -95
- package/dist/test/event-gate-script.test.js.map +0 -1
- package/dist/test/event-gate-selfwake.test.d.ts +0 -15
- package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
- package/dist/test/event-gate-selfwake.test.js +0 -125
- package/dist/test/event-gate-selfwake.test.js.map +0 -1
- package/dist/test/event-gate-tags.test.d.ts +0 -2
- package/dist/test/event-gate-tags.test.d.ts.map +0 -1
- package/dist/test/event-gate-tags.test.js +0 -76
- package/dist/test/event-gate-tags.test.js.map +0 -1
- package/dist/test/event-gate.test.d.ts +0 -2
- package/dist/test/event-gate.test.d.ts.map +0 -1
- package/dist/test/event-gate.test.js +0 -919
- package/dist/test/event-gate.test.js.map +0 -1
- package/dist/test/explicit-prose-routing.test.d.ts +0 -11
- package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
- package/dist/test/explicit-prose-routing.test.js +0 -1715
- package/dist/test/explicit-prose-routing.test.js.map +0 -1
- package/dist/test/framework.test.d.ts +0 -5
- package/dist/test/framework.test.d.ts.map +0 -1
- package/dist/test/framework.test.js +0 -1048
- package/dist/test/framework.test.js.map +0 -1
- package/dist/test/gather-context-timeout.test.d.ts +0 -2
- package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
- package/dist/test/gather-context-timeout.test.js +0 -120
- package/dist/test/gather-context-timeout.test.js.map +0 -1
- package/dist/test/health-snapshot.test.d.ts +0 -2
- package/dist/test/health-snapshot.test.d.ts.map +0 -1
- package/dist/test/health-snapshot.test.js +0 -74
- package/dist/test/health-snapshot.test.js.map +0 -1
- package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
- package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
- package/dist/test/helpers/coalescing-fixture.js +0 -152
- package/dist/test/helpers/coalescing-fixture.js.map +0 -1
- package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
- package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
- package/dist/test/helpers/coalescing-kill-child.js +0 -24
- package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
- package/dist/test/helpers/mock-membrane.d.ts +0 -53
- package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
- package/dist/test/helpers/mock-membrane.js +0 -168
- package/dist/test/helpers/mock-membrane.js.map +0 -1
- package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
- package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
- package/dist/test/helpers/pytc-stop-rows.js +0 -143
- package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
- package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
- package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
- package/dist/test/helpers/quiesce-crash-child.js +0 -156
- package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
- package/dist/test/hide-then-turn.test.d.ts +0 -2
- package/dist/test/hide-then-turn.test.d.ts.map +0 -1
- package/dist/test/hide-then-turn.test.js +0 -101
- package/dist/test/hide-then-turn.test.js.map +0 -1
- package/dist/test/history-module-overview.test.d.ts +0 -2
- package/dist/test/history-module-overview.test.d.ts.map +0 -1
- package/dist/test/history-module-overview.test.js +0 -711
- package/dist/test/history-module-overview.test.js.map +0 -1
- package/dist/test/history-module-realstore.test.d.ts +0 -2
- package/dist/test/history-module-realstore.test.d.ts.map +0 -1
- package/dist/test/history-module-realstore.test.js +0 -363
- package/dist/test/history-module-realstore.test.js.map +0 -1
- package/dist/test/history-module-semantic.test.d.ts +0 -2
- package/dist/test/history-module-semantic.test.d.ts.map +0 -1
- package/dist/test/history-module-semantic.test.js +0 -213
- package/dist/test/history-module-semantic.test.js.map +0 -1
- package/dist/test/history-module-ux.test.d.ts +0 -2
- package/dist/test/history-module-ux.test.d.ts.map +0 -1
- package/dist/test/history-module-ux.test.js +0 -304
- package/dist/test/history-module-ux.test.js.map +0 -1
- package/dist/test/history-module.test.d.ts +0 -2
- package/dist/test/history-module.test.d.ts.map +0 -1
- package/dist/test/history-module.test.js +0 -619
- package/dist/test/history-module.test.js.map +0 -1
- package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
- package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
- package/dist/test/history-semantic-review-findings.test.js +0 -338
- package/dist/test/history-semantic-review-findings.test.js.map +0 -1
- package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
- package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
- package/dist/test/history-semantic-sync-converges.test.js +0 -109
- package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
- package/dist/test/host-quiesce.test.d.ts +0 -2
- package/dist/test/host-quiesce.test.d.ts.map +0 -1
- package/dist/test/host-quiesce.test.js +0 -988
- package/dist/test/host-quiesce.test.js.map +0 -1
- package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
- package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
- package/dist/test/inference-exhaustion-rewind.test.js +0 -387
- package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
- package/dist/test/inference-failure-bounds.test.d.ts +0 -2
- package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
- package/dist/test/inference-failure-bounds.test.js +0 -1202
- package/dist/test/inference-failure-bounds.test.js.map +0 -1
- package/dist/test/inference-failure-observability.test.d.ts +0 -2
- package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
- package/dist/test/inference-failure-observability.test.js +0 -84
- package/dist/test/inference-failure-observability.test.js.map +0 -1
- package/dist/test/journal-tool.test.d.ts +0 -2
- package/dist/test/journal-tool.test.d.ts.map +0 -1
- package/dist/test/journal-tool.test.js +0 -33
- package/dist/test/journal-tool.test.js.map +0 -1
- package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
- package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
- package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
- package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
- package/dist/test/kv-unified-wiring.test.d.ts +0 -2
- package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
- package/dist/test/kv-unified-wiring.test.js +0 -95
- package/dist/test/kv-unified-wiring.test.js.map +0 -1
- package/dist/test/late-events-after-stop.test.d.ts +0 -2
- package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
- package/dist/test/late-events-after-stop.test.js +0 -122
- package/dist/test/late-events-after-stop.test.js.map +0 -1
- package/dist/test/liveness-watchdog.test.d.ts +0 -2
- package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
- package/dist/test/liveness-watchdog.test.js +0 -94
- package/dist/test/liveness-watchdog.test.js.map +0 -1
- package/dist/test/machine-close-provenance.test.d.ts +0 -2
- package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
- package/dist/test/machine-close-provenance.test.js +0 -116
- package/dist/test/machine-close-provenance.test.js.map +0 -1
- package/dist/test/maintenance-timer.test.d.ts +0 -2
- package/dist/test/maintenance-timer.test.d.ts.map +0 -1
- package/dist/test/maintenance-timer.test.js +0 -1827
- package/dist/test/maintenance-timer.test.js.map +0 -1
- package/dist/test/mcp-server-bin.test.d.ts +0 -2
- package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
- package/dist/test/mcp-server-bin.test.js +0 -142
- package/dist/test/mcp-server-bin.test.js.map +0 -1
- package/dist/test/mcpl-access-provider.test.d.ts +0 -2
- package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
- package/dist/test/mcpl-access-provider.test.js +0 -20
- package/dist/test/mcpl-access-provider.test.js.map +0 -1
- package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
- package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
- package/dist/test/mcpl-awareness-barrier.test.js +0 -769
- package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
- package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
- package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
- package/dist/test/mcpl-capability-grant.test.js +0 -111
- package/dist/test/mcpl-capability-grant.test.js.map +0 -1
- package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
- package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
- package/dist/test/mcpl-capability-scoping.test.js +0 -266
- package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
- package/dist/test/mcpl-child-env.test.d.ts +0 -2
- package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
- package/dist/test/mcpl-child-env.test.js +0 -158
- package/dist/test/mcpl-child-env.test.js.map +0 -1
- package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
- package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
- package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
- package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
- package/dist/test/mcpl-coalescing.test.d.ts +0 -2
- package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
- package/dist/test/mcpl-coalescing.test.js +0 -1125
- package/dist/test/mcpl-coalescing.test.js.map +0 -1
- package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
- package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
- package/dist/test/mcpl-config-diagnostics.test.js +0 -452
- package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
- package/dist/test/mcpl-empty-content.test.d.ts +0 -2
- package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
- package/dist/test/mcpl-empty-content.test.js +0 -368
- package/dist/test/mcpl-empty-content.test.js.map +0 -1
- package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
- package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
- package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
- package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
- package/dist/test/mcpl-list-status.test.d.ts +0 -2
- package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
- package/dist/test/mcpl-list-status.test.js +0 -82
- package/dist/test/mcpl-list-status.test.js.map +0 -1
- package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
- package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
- package/dist/test/mcpl-manifest-changed.test.js +0 -178
- package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
- package/dist/test/mcpl-push-convert.test.d.ts +0 -2
- package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
- package/dist/test/mcpl-push-convert.test.js +0 -42
- package/dist/test/mcpl-push-convert.test.js.map +0 -1
- package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
- package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
- package/dist/test/mcpl-reconnect-events.test.js +0 -144
- package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
- package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
- package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
- package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
- package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
- package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
- package/dist/test/mcpl-reference-fetcher.test.js +0 -427
- package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
- package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
- package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
- package/dist/test/mcpl-reference-fields.test.js +0 -149
- package/dist/test/mcpl-reference-fields.test.js.map +0 -1
- package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
- package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
- package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
- package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
- package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
- package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
- package/dist/test/mcpl-request-timeout.test.js +0 -138
- package/dist/test/mcpl-request-timeout.test.js.map +0 -1
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
- package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
- package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
- package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
- package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
- package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
- package/dist/test/mcpl-stderr-tail.test.js +0 -210
- package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
- package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
- package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-class-listing.test.js +0 -147
- package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
- package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
- package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-classes.test.js +0 -80
- package/dist/test/mcpl-tool-classes.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
- package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
- package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
- package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
- package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
- package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
- package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
- package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
- package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
- package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
- package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
- package/dist/test/mcpl-transport-close.test.d.ts +0 -2
- package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
- package/dist/test/mcpl-transport-close.test.js +0 -373
- package/dist/test/mcpl-transport-close.test.js.map +0 -1
- package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
- package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
- package/dist/test/mcpl-transport-spawn.test.js +0 -58
- package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
- package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
- package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
- package/dist/test/mcpl-ws-transport.test.js +0 -259
- package/dist/test/mcpl-ws-transport.test.js.map +0 -1
- package/dist/test/module-context-integration.test.d.ts +0 -18
- package/dist/test/module-context-integration.test.d.ts.map +0 -1
- package/dist/test/module-context-integration.test.js +0 -181
- package/dist/test/module-context-integration.test.js.map +0 -1
- package/dist/test/offline-recovery-branch.test.d.ts +0 -2
- package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
- package/dist/test/offline-recovery-branch.test.js +0 -570
- package/dist/test/offline-recovery-branch.test.js.map +0 -1
- package/dist/test/operator-surgery.test.d.ts +0 -2
- package/dist/test/operator-surgery.test.d.ts.map +0 -1
- package/dist/test/operator-surgery.test.js +0 -595
- package/dist/test/operator-surgery.test.js.map +0 -1
- package/dist/test/ops-alert.test.d.ts +0 -2
- package/dist/test/ops-alert.test.d.ts.map +0 -1
- package/dist/test/ops-alert.test.js +0 -153
- package/dist/test/ops-alert.test.js.map +0 -1
- package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
- package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
- package/dist/test/overbudget-drain-breaker.test.js +0 -198
- package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
- package/dist/test/pack-installs-alone.test.d.ts +0 -2
- package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
- package/dist/test/pack-installs-alone.test.js +0 -284
- package/dist/test/pack-installs-alone.test.js.map +0 -1
- package/dist/test/per-agent-delivery.test.d.ts +0 -2
- package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
- package/dist/test/per-agent-delivery.test.js +0 -108
- package/dist/test/per-agent-delivery.test.js.map +0 -1
- package/dist/test/physical-window-restart.test.d.ts +0 -16
- package/dist/test/physical-window-restart.test.d.ts.map +0 -1
- package/dist/test/physical-window-restart.test.js +0 -487
- package/dist/test/physical-window-restart.test.js.map +0 -1
- package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
- package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
- package/dist/test/placed-reaction-baseline.test.js +0 -312
- package/dist/test/placed-reaction-baseline.test.js.map +0 -1
- package/dist/test/present-while-acting.test.d.ts +0 -19
- package/dist/test/present-while-acting.test.d.ts.map +0 -1
- package/dist/test/present-while-acting.test.js +0 -1649
- package/dist/test/present-while-acting.test.js.map +0 -1
- package/dist/test/prose-segments.test.d.ts +0 -2
- package/dist/test/prose-segments.test.d.ts.map +0 -1
- package/dist/test/prose-segments.test.js +0 -52
- package/dist/test/prose-segments.test.js.map +0 -1
- package/dist/test/prose-stream-router.test.d.ts +0 -2
- package/dist/test/prose-stream-router.test.d.ts.map +0 -1
- package/dist/test/prose-stream-router.test.js +0 -171
- package/dist/test/prose-stream-router.test.js.map +0 -1
- package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
- package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
- package/dist/test/provider-acceleration-cooldown.test.js +0 -435
- package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
- package/dist/test/provider-waits.test.d.ts +0 -2
- package/dist/test/provider-waits.test.d.ts.map +0 -1
- package/dist/test/provider-waits.test.js +0 -2259
- package/dist/test/provider-waits.test.js.map +0 -1
- package/dist/test/puppet-tool-call.test.d.ts +0 -2
- package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
- package/dist/test/puppet-tool-call.test.js +0 -226
- package/dist/test/puppet-tool-call.test.js.map +0 -1
- package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
- package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
- package/dist/test/puppet-turn-reservation.test.js +0 -139
- package/dist/test/puppet-turn-reservation.test.js.map +0 -1
- package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
- package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
- package/dist/test/reaction-baseline-platform.test.js +0 -124
- package/dist/test/reaction-baseline-platform.test.js.map +0 -1
- package/dist/test/record-journal.test.d.ts +0 -6
- package/dist/test/record-journal.test.d.ts.map +0 -1
- package/dist/test/record-journal.test.js +0 -269
- package/dist/test/record-journal.test.js.map +0 -1
- package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
- package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
- package/dist/test/refusal-reaction-baseline.test.js +0 -81
- package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
- package/dist/test/refusal-rewind.test.d.ts +0 -2
- package/dist/test/refusal-rewind.test.d.ts.map +0 -1
- package/dist/test/refusal-rewind.test.js +0 -117
- package/dist/test/refusal-rewind.test.js.map +0 -1
- package/dist/test/refusal-single-call.test.d.ts +0 -2
- package/dist/test/refusal-single-call.test.d.ts.map +0 -1
- package/dist/test/refusal-single-call.test.js +0 -177
- package/dist/test/refusal-single-call.test.js.map +0 -1
- package/dist/test/release-bump.test.d.ts +0 -2
- package/dist/test/release-bump.test.d.ts.map +0 -1
- package/dist/test/release-bump.test.js +0 -248
- package/dist/test/release-bump.test.js.map +0 -1
- package/dist/test/release-changelog.test.d.ts +0 -2
- package/dist/test/release-changelog.test.d.ts.map +0 -1
- package/dist/test/release-changelog.test.js +0 -159
- package/dist/test/release-changelog.test.js.map +0 -1
- package/dist/test/release-holds.test.d.ts +0 -2
- package/dist/test/release-holds.test.d.ts.map +0 -1
- package/dist/test/release-holds.test.js +0 -456
- package/dist/test/release-holds.test.js.map +0 -1
- package/dist/test/same-round-think-policy.test.d.ts +0 -2
- package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
- package/dist/test/same-round-think-policy.test.js +0 -853
- package/dist/test/same-round-think-policy.test.js.map +0 -1
- package/dist/test/save-recent-image.test.d.ts +0 -26
- package/dist/test/save-recent-image.test.d.ts.map +0 -1
- package/dist/test/save-recent-image.test.js +0 -974
- package/dist/test/save-recent-image.test.js.map +0 -1
- package/dist/test/shutdown-errors.test.d.ts +0 -2
- package/dist/test/shutdown-errors.test.d.ts.map +0 -1
- package/dist/test/shutdown-errors.test.js +0 -662
- package/dist/test/shutdown-errors.test.js.map +0 -1
- package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
- package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
- package/dist/test/silent-heartbeat-rows.test.js +0 -451
- package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
- package/dist/test/silent-heartbeat.test.d.ts +0 -2
- package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
- package/dist/test/silent-heartbeat.test.js +0 -165
- package/dist/test/silent-heartbeat.test.js.map +0 -1
- package/dist/test/state-scaling-e2e.test.d.ts +0 -22
- package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
- package/dist/test/state-scaling-e2e.test.js +0 -206
- package/dist/test/state-scaling-e2e.test.js.map +0 -1
- package/dist/test/state-scaling.test.d.ts +0 -12
- package/dist/test/state-scaling.test.d.ts.map +0 -1
- package/dist/test/state-scaling.test.js +0 -251
- package/dist/test/state-scaling.test.js.map +0 -1
- package/dist/test/stop-with-host-operations.test.d.ts +0 -20
- package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
- package/dist/test/stop-with-host-operations.test.js +0 -1174
- package/dist/test/stop-with-host-operations.test.js.map +0 -1
- package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
- package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
- package/dist/test/stop-with-work-in-flight.test.js +0 -784
- package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
- package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
- package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
- package/dist/test/surgery-marks-receipt.test.js +0 -540
- package/dist/test/surgery-marks-receipt.test.js.map +0 -1
- package/dist/test/thinking-token-stamp.test.d.ts +0 -12
- package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
- package/dist/test/thinking-token-stamp.test.js +0 -129
- package/dist/test/thinking-token-stamp.test.js.map +0 -1
- package/dist/test/timezone.test.d.ts +0 -2
- package/dist/test/timezone.test.d.ts.map +0 -1
- package/dist/test/timezone.test.js +0 -18
- package/dist/test/timezone.test.js.map +0 -1
- package/dist/test/tool-batch-hook.test.d.ts +0 -8
- package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
- package/dist/test/tool-batch-hook.test.js +0 -275
- package/dist/test/tool-batch-hook.test.js.map +0 -1
- package/dist/test/tool-policy.test.d.ts +0 -2
- package/dist/test/tool-policy.test.d.ts.map +0 -1
- package/dist/test/tool-policy.test.js +0 -51
- package/dist/test/tool-policy.test.js.map +0 -1
- package/dist/test/tool-presentation.test.d.ts +0 -2
- package/dist/test/tool-presentation.test.d.ts.map +0 -1
- package/dist/test/tool-presentation.test.js +0 -268
- package/dist/test/tool-presentation.test.js.map +0 -1
- package/dist/test/tool-result-content.test.d.ts +0 -2
- package/dist/test/tool-result-content.test.d.ts.map +0 -1
- package/dist/test/tool-result-content.test.js +0 -37
- package/dist/test/tool-result-content.test.js.map +0 -1
- package/dist/test/tool-result-guard.test.d.ts +0 -2
- package/dist/test/tool-result-guard.test.d.ts.map +0 -1
- package/dist/test/tool-result-guard.test.js +0 -3729
- package/dist/test/tool-result-guard.test.js.map +0 -1
- package/dist/test/tool-result-history.test.d.ts +0 -7
- package/dist/test/tool-result-history.test.d.ts.map +0 -1
- package/dist/test/tool-result-history.test.js +0 -156
- package/dist/test/tool-result-history.test.js.map +0 -1
- package/dist/test/tool-result-spill.test.d.ts +0 -13
- package/dist/test/tool-result-spill.test.d.ts.map +0 -1
- package/dist/test/tool-result-spill.test.js +0 -586
- package/dist/test/tool-result-spill.test.js.map +0 -1
- package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
- package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
- package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
- package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
- package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
- package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
- package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
- package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
- package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
- package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
- package/dist/test/tools-observe-parser-conformance.test.js +0 -129
- package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
- package/dist/test/trunk-channel-routing.test.d.ts +0 -19
- package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
- package/dist/test/trunk-channel-routing.test.js +0 -652
- package/dist/test/trunk-channel-routing.test.js.map +0 -1
- package/dist/test/tune-out-e2e.test.d.ts +0 -2
- package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
- package/dist/test/tune-out-e2e.test.js +0 -142
- package/dist/test/tune-out-e2e.test.js.map +0 -1
- package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
- package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
- package/dist/test/tune-out-gate-composition.test.js +0 -221
- package/dist/test/tune-out-gate-composition.test.js.map +0 -1
- package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
- package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
- package/dist/test/tune-out-lifecycle.test.js +0 -121
- package/dist/test/tune-out-lifecycle.test.js.map +0 -1
- package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
- package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
- package/dist/test/turn-trigger-provenance.test.js +0 -137
- package/dist/test/turn-trigger-provenance.test.js.map +0 -1
- package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
- package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
- package/dist/test/unconvertible-tool-result.test.js +0 -358
- package/dist/test/unconvertible-tool-result.test.js.map +0 -1
- package/dist/test/unsent-compiles.test.d.ts +0 -2
- package/dist/test/unsent-compiles.test.d.ts.map +0 -1
- package/dist/test/unsent-compiles.test.js +0 -139
- package/dist/test/unsent-compiles.test.js.map +0 -1
- package/dist/test/usage-accounting.test.d.ts +0 -2
- package/dist/test/usage-accounting.test.d.ts.map +0 -1
- package/dist/test/usage-accounting.test.js +0 -13
- package/dist/test/usage-accounting.test.js.map +0 -1
- package/dist/test/usage-tracker.test.d.ts +0 -2
- package/dist/test/usage-tracker.test.d.ts.map +0 -1
- package/dist/test/usage-tracker.test.js +0 -129
- package/dist/test/usage-tracker.test.js.map +0 -1
- package/dist/test/utils-surface.test.d.ts +0 -2
- package/dist/test/utils-surface.test.d.ts.map +0 -1
- package/dist/test/utils-surface.test.js +0 -145
- package/dist/test/utils-surface.test.js.map +0 -1
- package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
- package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
- package/dist/test/workspace-branch-continuation.test.js +0 -183
- package/dist/test/workspace-branch-continuation.test.js.map +0 -1
- package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
- package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
- package/dist/test/workspace-materialize-freshness.test.js +0 -252
- package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
- package/dist/test/workspace-path-containment.test.d.ts +0 -19
- package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
- package/dist/test/workspace-path-containment.test.js +0 -91
- package/dist/test/workspace-path-containment.test.js.map +0 -1
- package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
- package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
- package/dist/test/workspace-read-file-from-disk.test.js +0 -174
- package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
- package/dist/test/workspace-read-image.test.d.ts +0 -2
- package/dist/test/workspace-read-image.test.d.ts.map +0 -1
- package/dist/test/workspace-read-image.test.js +0 -597
- package/dist/test/workspace-read-image.test.js.map +0 -1
- package/dist/test/workspace-read-text.test.d.ts +0 -2
- package/dist/test/workspace-read-text.test.d.ts.map +0 -1
- package/dist/test/workspace-read-text.test.js +0 -101
- package/dist/test/workspace-read-text.test.js.map +0 -1
- package/dist/test/workspace-restore-order.test.d.ts +0 -16
- package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
- package/dist/test/workspace-restore-order.test.js +0 -140
- package/dist/test/workspace-restore-order.test.js.map +0 -1
- package/dist/test/workspace-three-way.test.d.ts +0 -12
- package/dist/test/workspace-three-way.test.d.ts.map +0 -1
- package/dist/test/workspace-three-way.test.js +0 -4094
- package/dist/test/workspace-three-way.test.js.map +0 -1
- package/dist/test/workspace-watcher.test.d.ts +0 -2
- package/dist/test/workspace-watcher.test.d.ts.map +0 -1
- package/dist/test/workspace-watcher.test.js +0 -281
- package/dist/test/workspace-watcher.test.js.map +0 -1
- package/dist/test/xml-round-persist.test.d.ts +0 -19
- package/dist/test/xml-round-persist.test.d.ts.map +0 -1
- package/dist/test/xml-round-persist.test.js +0 -235
- package/dist/test/xml-round-persist.test.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -7,8 +7,12 @@
|
|
|
7
7
|
* - Tools for explicit Discord operations (send, DM, react, etc.)
|
|
8
8
|
*/
|
|
9
9
|
import { formatZonedDateTime, resolveTimeZone } from '../../timezone.js';
|
|
10
|
+
import { logValue } from '../../mcpl/log-value.js';
|
|
11
|
+
import { unknownToolMessage } from '../unknown-tool.js';
|
|
12
|
+
import { DiscordRefusal } from './refusal.js';
|
|
10
13
|
export * from './types.js';
|
|
11
14
|
export { DiscordJsClient } from './discord-js-client.js';
|
|
15
|
+
export { DiscordRefusal } from './refusal.js';
|
|
12
16
|
const DEFAULT_CONFIG = {
|
|
13
17
|
handleDMs: true,
|
|
14
18
|
ignoreBots: true,
|
|
@@ -24,6 +28,13 @@ const TYPING_REFRESH_MS = 8_000;
|
|
|
24
28
|
// is somehow missed (crash, dropped stream), this guarantees typing can't get
|
|
25
29
|
// stuck on forever.
|
|
26
30
|
const TYPING_MAX_MS = 5 * 60_000;
|
|
31
|
+
// The most agents whose latest post the module keeps for their turn's
|
|
32
|
+
// thoughts (lastPosts). Past it, the entry recorded longest ago goes, which
|
|
33
|
+
// bounds the entries of agents that are gone, such as closed conversations'.
|
|
34
|
+
// An entry is wanted only until its turn's speech arrives, so one goes while
|
|
35
|
+
// still wanted only if this many other agents post in that time, and its
|
|
36
|
+
// turn's thoughts then go into no message.
|
|
37
|
+
const MAX_LAST_POSTS = 1_024;
|
|
27
38
|
// Trace event types that end an agent's turn (onTrace's contract) — any of
|
|
28
39
|
// these stops the typing keepalive. A turn's tool-call cycles stay inside its
|
|
29
40
|
// stream, so they come after them. inference:completed is the normal end.
|
|
@@ -48,6 +59,223 @@ const TEXT_FILE_EXTENSIONS = [
|
|
|
48
59
|
'.html', '.css', '.xml', '.csv', '.log', '.sh', '.bash',
|
|
49
60
|
];
|
|
50
61
|
const CONVERSATION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
62
|
+
/** A text attachment this many bytes or larger isn't read into the agent's context. */
|
|
63
|
+
const TEXT_FILE_LIMIT_BYTES = 100_000;
|
|
64
|
+
/** activate's and deactivate's answer before the module has started, or once it has stopped. */
|
|
65
|
+
const NOT_RUNNING = "Speech handling wasn't changed: the Discord module hasn't started on this host, or has stopped. Calling it again won't change that until it runs; ask whoever runs this host if you need Discord.";
|
|
66
|
+
/** What the agent reads for a text attachment whose contents couldn't be
|
|
67
|
+
* fetched: by an error, or by an answer that wasn't OK. */
|
|
68
|
+
function attachmentNotFetched(filename) {
|
|
69
|
+
return {
|
|
70
|
+
type: 'text',
|
|
71
|
+
text: `[Attachment: ${filename} couldn't be fetched, so its text isn't in your context. If you need it, ask the sender in the channel to paste it.]`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* What the agent reads when a tool stops on an error that isn't a refusal
|
|
76
|
+
* (DiscordRefusal), such as a network failure: its outcome is unknown. A send
|
|
77
|
+
* repeated blindly can post twice, so those say how to check; the other tools
|
|
78
|
+
* are safe to repeat, since a second call changes nothing more.
|
|
79
|
+
*/
|
|
80
|
+
function unexpectedFailure(call, error) {
|
|
81
|
+
const stopped = `discord--${call.name} stopped on an unexpected error: ${errorText(error)}.`;
|
|
82
|
+
const channelId = call.input?.channelId;
|
|
83
|
+
switch (call.name) {
|
|
84
|
+
case 'send':
|
|
85
|
+
return `${stopped} Whether the message was sent is unknown: discord--fetch_history with channelId ${String(channelId)} shows it, so check before you send it again.`;
|
|
86
|
+
case 'dm':
|
|
87
|
+
return `${stopped} Whether the message was sent is unknown. If an earlier discord--dm to this user gave you its channelId, discord--fetch_history with it shows whether this one went; without it, sending it again may send it twice.`;
|
|
88
|
+
case 'edit':
|
|
89
|
+
case 'react':
|
|
90
|
+
case 'delete':
|
|
91
|
+
case 'create_thread':
|
|
92
|
+
return `${stopped} Whether it took effect is unknown. Calling it again is safe: if it did, the second call changes nothing more.`;
|
|
93
|
+
case 'list_guilds':
|
|
94
|
+
case 'list_channels':
|
|
95
|
+
case 'fetch_history':
|
|
96
|
+
return `${stopped} It changes nothing on Discord, so you can call it again.`;
|
|
97
|
+
default:
|
|
98
|
+
return `${stopped} Calling it again is safe.`;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** An error's own words, for a sentence about it. */
|
|
102
|
+
function errorText(error) {
|
|
103
|
+
return error instanceof Error ? error.message : String(error);
|
|
104
|
+
}
|
|
105
|
+
/** A count of seconds, for the agent's eyes. */
|
|
106
|
+
function seconds(n) {
|
|
107
|
+
return `${n} second${n === 1 ? '' : 's'}`;
|
|
108
|
+
}
|
|
109
|
+
/** What a stored message reads when it has no text of its own and nothing the
|
|
110
|
+
* host added, so the agent still sees that a message was there. */
|
|
111
|
+
const EMPTY_MESSAGE = '[Empty message]';
|
|
112
|
+
/**
|
|
113
|
+
* Metadata on a stored Discord message whose first block is its own text,
|
|
114
|
+
* '' when it has none, and every block after it the host's: images, inlined
|
|
115
|
+
* file text, attachment notes and EMPTY_MESSAGE. An edit replaces content and
|
|
116
|
+
* can't touch metadata, so the mark holds for the row's life, and it tells an
|
|
117
|
+
* edit or a sync which block is Discord's to replace. For the same reason a
|
|
118
|
+
* row stored before the mark never gets it, even once a history sync
|
|
119
|
+
* rebuilds it in this layout. The request builder drops an empty text block,
|
|
120
|
+
* as it does wherever one occurs.
|
|
121
|
+
*/
|
|
122
|
+
const TEXT_FIRST = 'discordTextFirst';
|
|
123
|
+
/** A message's own text as a stored row holds it: '' when it's blank. */
|
|
124
|
+
function ownText(content) {
|
|
125
|
+
return content.trim() ? content : '';
|
|
126
|
+
}
|
|
127
|
+
const isEmptyMarker = (block) => block.type === 'text' && block.text === EMPTY_MESSAGE;
|
|
128
|
+
const isBlankText = (block) => block.type === 'text' && !block.text.trim();
|
|
129
|
+
/**
|
|
130
|
+
* A text-first row's content: the message's own text, '' when it has none,
|
|
131
|
+
* then `after`, what the host added; EMPTY_MESSAGE when there's neither.
|
|
132
|
+
*/
|
|
133
|
+
function textFirst(text, after = []) {
|
|
134
|
+
return [{ type: 'text', text }, ...(text || after.length > 0 ? after : [{ type: 'text', text: EMPTY_MESSAGE }])];
|
|
135
|
+
}
|
|
136
|
+
/** A text-first row's content with `text` as the message's own text, in
|
|
137
|
+
* place of its first block, keeping what the host added after it. */
|
|
138
|
+
function withOwnText(content, text) {
|
|
139
|
+
return textFirst(ownText(text), content.slice(1).filter((block) => !isEmptyMarker(block)));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* The content a stored Discord message should hold once Discord's text for it
|
|
143
|
+
* is `discordText`, or null when it holds that already.
|
|
144
|
+
*
|
|
145
|
+
* A text-first row keeps every block the host added and replaces only its own
|
|
146
|
+
* text; EMPTY_MESSAGE goes once there is text, and comes back when nothing is
|
|
147
|
+
* left. A row stored before that layout held its own text first when it had
|
|
148
|
+
* any, but a message without text began with what the host added, which may
|
|
149
|
+
* be text, a file's contents or a note, so its first block can't be told
|
|
150
|
+
* apart. Such a row is left alone unless Discord's text is there and differs
|
|
151
|
+
* from its first block, and then that text takes the first block's place
|
|
152
|
+
* when it's text, and goes before it when it isn't, and EMPTY_MESSAGE goes.
|
|
153
|
+
* So a message without text, stored beginning with a file's contents or a
|
|
154
|
+
* note, loses that block. The history sync, which has the message's
|
|
155
|
+
* attachments, rebuilds such a row instead (needsRebuild).
|
|
156
|
+
*/
|
|
157
|
+
function contentForText(stored, discordText) {
|
|
158
|
+
const text = ownText(discordText);
|
|
159
|
+
const [first, ...rest] = stored.content;
|
|
160
|
+
if (stored.metadata?.[TEXT_FIRST] === true) {
|
|
161
|
+
if (first?.type === 'text' && first.text === text)
|
|
162
|
+
return null;
|
|
163
|
+
return withOwnText(stored.content, text);
|
|
164
|
+
}
|
|
165
|
+
if (!text || (first?.type === 'text' && first.text === discordText))
|
|
166
|
+
return null;
|
|
167
|
+
return [{ type: 'text', text: discordText }, ...(first?.type === 'text' ? rest : stored.content).filter((block) => !isEmptyMarker(block))];
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Whether a stored row lacks or holds more than the live conversion gives
|
|
171
|
+
* Discord's `message` now, its own text aside, so the history sync stores it
|
|
172
|
+
* through that conversion again. The conversion gives each attachment one
|
|
173
|
+
* block: an image, a file's text, or a note when it isn't fetched or isn't
|
|
174
|
+
* read. So a text-first row holds one block per attachment after its text,
|
|
175
|
+
* EMPTY_MESSAGE aside, which the text's rule keeps. A row stored before that
|
|
176
|
+
* layout held its text, when it had any, then those blocks, or EMPTY_MESSAGE
|
|
177
|
+
* alone; one that doesn't was blanked or stripped by an earlier edit or sync,
|
|
178
|
+
* or its message gained or lost text, or lost an attachment. Its first block,
|
|
179
|
+
* when it is the message's own text, counts as that text, even when the text
|
|
180
|
+
* reads as EMPTY_MESSAGE. For a message without text of its own, a first
|
|
181
|
+
* block that's text may be what the host added or a caption the message has
|
|
182
|
+
* since lost, which no count tells apart, so that row is stored again; it
|
|
183
|
+
* then begins with its own '' text. One whose text alone changed is left to
|
|
184
|
+
* the text's rule, which keeps the blocks after it. History without
|
|
185
|
+
* attachments can't be compared, so it rebuilds nothing.
|
|
186
|
+
*/
|
|
187
|
+
function needsRebuild(stored, message) {
|
|
188
|
+
if (message.attachments === undefined)
|
|
189
|
+
return false;
|
|
190
|
+
const attachments = message.attachments.length;
|
|
191
|
+
const held = (blocks) => blocks.filter((block) => !isBlankText(block) && !isEmptyMarker(block)).length;
|
|
192
|
+
if (stored.metadata?.[TEXT_FIRST] === true)
|
|
193
|
+
return held(stored.content.slice(1)) !== attachments;
|
|
194
|
+
const own = ownText(message.content);
|
|
195
|
+
const [first, ...rest] = stored.content;
|
|
196
|
+
if (!own && first?.type === 'text' && !isBlankText(first) && !isEmptyMarker(first))
|
|
197
|
+
return true;
|
|
198
|
+
const expected = (own ? 1 : 0) + attachments;
|
|
199
|
+
const counted = own && first?.type === 'text' && first.text === own ? 1 + held(rest) : held(stored.content);
|
|
200
|
+
return counted !== expected || (expected === 0 && !stored.content.some(isEmptyMarker));
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Whether a row holds non-blank text besides its message's own text now,
|
|
204
|
+
* which may be what a file gave. A marked row whose message's text changed
|
|
205
|
+
* counts its old text here too, which can delay its rebuild until a fetch
|
|
206
|
+
* succeeds but never loses text.
|
|
207
|
+
*/
|
|
208
|
+
function holdsOtherText(stored, message) {
|
|
209
|
+
const own = ownText(message.content);
|
|
210
|
+
const [first, ...rest] = stored.content;
|
|
211
|
+
const others = own && first?.type === 'text' && first.text === own ? rest : stored.content;
|
|
212
|
+
return others.some((block) => block.type === 'text' && !isBlankText(block) && !isEmptyMarker(block));
|
|
213
|
+
}
|
|
214
|
+
/** Whether a message's conversion holds, after the message's own text, the
|
|
215
|
+
* note for an attachment of its whose text wasn't fetched. */
|
|
216
|
+
function holdsNotFetched(content, message) {
|
|
217
|
+
const notes = new Set((message.attachments ?? []).map((attachment) => attachmentNotFetched(attachment.filename).text));
|
|
218
|
+
return content.slice(1).some((block) => block.type === 'text' && notes.has(block.text));
|
|
219
|
+
}
|
|
220
|
+
/** A URL without its query or fragment: Discord's CDN signs an attachment's
|
|
221
|
+
* URL afresh in each payload, so only its path names the attachment. */
|
|
222
|
+
function urlPath(url) {
|
|
223
|
+
return url.split(/[?#]/)[0] ?? url;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Whether a block the host added for an attachment stands for none of
|
|
227
|
+
* `attachments`, matched as the live conversion writes it: an image by its
|
|
228
|
+
* URL, a file's text by its header, and a note by the filename it opens
|
|
229
|
+
* with, as every note for an attachment does, whatever its words after it.
|
|
230
|
+
* A block that stands for no attachment, such as EMPTY_MESSAGE, never does.
|
|
231
|
+
*/
|
|
232
|
+
function forGoneAttachment(block, attachments) {
|
|
233
|
+
if (block.type === 'image') {
|
|
234
|
+
const source = block.source;
|
|
235
|
+
if (source?.type !== 'url' || typeof source.url !== 'string')
|
|
236
|
+
return false;
|
|
237
|
+
const path = urlPath(source.url);
|
|
238
|
+
return !attachments.some((attachment) => urlPath(attachment.url) === path);
|
|
239
|
+
}
|
|
240
|
+
if (block.type !== 'text')
|
|
241
|
+
return false;
|
|
242
|
+
const { text } = block;
|
|
243
|
+
const opening = text.startsWith('\n--- File: ') ? (filename) => `\n--- File: ${filename} ---\n`
|
|
244
|
+
: text.startsWith('[Attachment: ') ? (filename) => `[Attachment: ${filename} `
|
|
245
|
+
: null;
|
|
246
|
+
return opening !== null && !attachments.some((attachment) => text.startsWith(opening(attachment.filename)));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* A text-first row's content without the blocks the host added for
|
|
250
|
+
* attachments its message no longer has, or `content` itself when none is
|
|
251
|
+
* gone. A block stays while an attachment it may stand for remains, so of two
|
|
252
|
+
* files of one name, removing one keeps both blocks, and a block that names
|
|
253
|
+
* no attachment stays. EMPTY_MESSAGE comes back once nothing is left.
|
|
254
|
+
*/
|
|
255
|
+
function withoutGoneAttachments(content, attachments) {
|
|
256
|
+
const [first, ...rest] = content;
|
|
257
|
+
if (first?.type !== 'text')
|
|
258
|
+
return content;
|
|
259
|
+
const kept = rest.filter((block) => !forGoneAttachment(block, attachments));
|
|
260
|
+
if (kept.length === rest.length)
|
|
261
|
+
return content;
|
|
262
|
+
return textFirst(first.text, kept);
|
|
263
|
+
}
|
|
264
|
+
/** The ids to remove for a message's rows: each row once, and a body stored
|
|
265
|
+
* in shards once, since removing one shard takes out its whole body. */
|
|
266
|
+
function oncePerBody(rows) {
|
|
267
|
+
const bodies = new Set();
|
|
268
|
+
const ids = [];
|
|
269
|
+
for (const row of rows) {
|
|
270
|
+
if (row.bodyGroupId) {
|
|
271
|
+
if (bodies.has(row.bodyGroupId))
|
|
272
|
+
continue;
|
|
273
|
+
bodies.add(row.bodyGroupId);
|
|
274
|
+
}
|
|
275
|
+
ids.push(row.id);
|
|
276
|
+
}
|
|
277
|
+
return ids;
|
|
278
|
+
}
|
|
51
279
|
/**
|
|
52
280
|
* Discord module for the agent framework.
|
|
53
281
|
*/
|
|
@@ -65,8 +293,39 @@ export class DiscordModule {
|
|
|
65
293
|
};
|
|
66
294
|
// Track current reply context per agent
|
|
67
295
|
replyContexts = new Map();
|
|
68
|
-
//
|
|
69
|
-
|
|
296
|
+
// Where a tool turn's thoughts go (onAgentSpeech edits them in as a
|
|
297
|
+
// spoiler): each agent's latest message posted in a turn, with that turn's
|
|
298
|
+
// id (notePost). A `send` or `reply` records the message its client call
|
|
299
|
+
// returns, and so does the module's own speech. Thoughts go into it only
|
|
300
|
+
// when their speech ends the same turn, and taking them removes it. So
|
|
301
|
+
// neither another agent's message nor one from an earlier turn takes them,
|
|
302
|
+
// and an attempt in the turn that is refused or throws leaves the turn's own
|
|
303
|
+
// post in place. Turn ids increase, so an earlier turn's post that finishes
|
|
304
|
+
// after a later turn's leaves the later one in place. A DM records nothing,
|
|
305
|
+
// since its channel isn't known here, and neither does a post whose call or
|
|
306
|
+
// speech carries no turn. An entry goes when its thoughts are taken, when
|
|
307
|
+
// the agent's next post replaces it, or past MAX_LAST_POSTS agents.
|
|
308
|
+
lastPosts = new Map();
|
|
309
|
+
// One set per running history sync: the Discord messages the live path
|
|
310
|
+
// stored, edited or was told were deleted since that sync began. What the
|
|
311
|
+
// live path saw is newer than what the sync fetched, so the sync leaves
|
|
312
|
+
// those messages alone.
|
|
313
|
+
liveDuringSync = new Set();
|
|
314
|
+
// The Discord messages whose live create is on its way to the store: from
|
|
315
|
+
// the moment the live path takes one up, through its conversion and the
|
|
316
|
+
// event queue, until a query shows the row onProcess answered with, stored
|
|
317
|
+
// or held. The framework holds a module's writes while the agent's turn is
|
|
318
|
+
// alive or the host is quiesced, and queryMessages shows a held write's
|
|
319
|
+
// row, marked held, so the next Discord event finds it either way and ends
|
|
320
|
+
// its entry. A history sync stores none of these messages, so no message is
|
|
321
|
+
// stored twice. A delete before the create is handled drops it: nothing is
|
|
322
|
+
// stored and nobody is woken. Any message's entry ends when its delete is
|
|
323
|
+
// handled, since a sync running then skips the message (noteLive) and a
|
|
324
|
+
// later one doesn't fetch it. An edit while it converts is folded into it.
|
|
325
|
+
// A create whose answer the framework fails to write or hold shows no row,
|
|
326
|
+
// and keeps its entry, and the sync off its message, until its delete is
|
|
327
|
+
// handled or the module stops.
|
|
328
|
+
creating = new Map();
|
|
70
329
|
// Typing keepalive: agentName -> { interval, deadline }. Started on
|
|
71
330
|
// inference:started, stopped on the turn's terminal trace event. Discord's
|
|
72
331
|
// typing indicator auto-expires after ~10s, so we re-trigger on an interval
|
|
@@ -109,7 +368,7 @@ export class DiscordModule {
|
|
|
109
368
|
}
|
|
110
369
|
// Set up Discord event handlers
|
|
111
370
|
this.client.onMessage((message) => this.handleDiscordMessage(message));
|
|
112
|
-
this.client.onMessageEdit((id, content) => this.handleDiscordMessageEdit(id, content));
|
|
371
|
+
this.client.onMessageEdit((id, content, attachments) => this.handleDiscordMessageEdit(id, content, attachments));
|
|
113
372
|
this.client.onMessageDelete((id) => this.handleDiscordMessageDelete(id));
|
|
114
373
|
// Set up ready handler for history sync on (re)connect
|
|
115
374
|
if (this.client.onReady) {
|
|
@@ -143,8 +402,13 @@ export class DiscordModule {
|
|
|
143
402
|
clearInterval(interval);
|
|
144
403
|
}
|
|
145
404
|
this.typingLoops.clear();
|
|
146
|
-
|
|
405
|
+
// From here the module writes nothing, though the framework may dispatch
|
|
406
|
+
// to it until stop() resolves: the live path takes nothing up, a create
|
|
407
|
+
// still converting finds the context gone, and onProcess answers nothing.
|
|
408
|
+
// The record of creates on their way goes with the context.
|
|
147
409
|
this.ctx = null;
|
|
410
|
+
this.creating.clear();
|
|
411
|
+
await this.client.disconnect();
|
|
148
412
|
}
|
|
149
413
|
/**
|
|
150
414
|
* Start (or refresh) the typing keepalive for an agent's reply channel.
|
|
@@ -328,7 +592,7 @@ export class DiscordModule {
|
|
|
328
592
|
},
|
|
329
593
|
{
|
|
330
594
|
name: 'list_channels',
|
|
331
|
-
description: 'List
|
|
595
|
+
description: 'List the channels in a Discord server, with their ids. Threads aren\'t listed.',
|
|
332
596
|
inputSchema: {
|
|
333
597
|
type: 'object',
|
|
334
598
|
properties: {
|
|
@@ -339,7 +603,7 @@ export class DiscordModule {
|
|
|
339
603
|
},
|
|
340
604
|
{
|
|
341
605
|
name: 'fetch_history',
|
|
342
|
-
description: 'Fetch recent message history from a channel',
|
|
606
|
+
description: 'Fetch recent message history from a channel. A message with attachments lists each one\'s filename, type and size; their contents aren\'t included.',
|
|
343
607
|
inputSchema: {
|
|
344
608
|
type: 'object',
|
|
345
609
|
properties: {
|
|
@@ -356,11 +620,11 @@ export class DiscordModule {
|
|
|
356
620
|
try {
|
|
357
621
|
switch (call.name) {
|
|
358
622
|
case 'send':
|
|
359
|
-
return await this.handleSend(call.input);
|
|
623
|
+
return await this.handleSend(call.input, call);
|
|
360
624
|
case 'dm':
|
|
361
625
|
return await this.handleDM(call.input);
|
|
362
626
|
case 'reply':
|
|
363
|
-
return await this.handleReply(call.input);
|
|
627
|
+
return await this.handleReply(call.input, call);
|
|
364
628
|
case 'react':
|
|
365
629
|
return await this.handleReact(call.input);
|
|
366
630
|
case 'edit':
|
|
@@ -372,7 +636,8 @@ export class DiscordModule {
|
|
|
372
636
|
case 'set_reply_context':
|
|
373
637
|
return await this.handleSetReplyContext(call.input);
|
|
374
638
|
case 'activate':
|
|
375
|
-
|
|
639
|
+
// A call with no input at all activates with the defaults.
|
|
640
|
+
return await this.handleActivate((call.input ?? {}));
|
|
376
641
|
case 'deactivate':
|
|
377
642
|
return await this.handleDeactivate();
|
|
378
643
|
case 'list_guilds':
|
|
@@ -382,21 +647,38 @@ export class DiscordModule {
|
|
|
382
647
|
case 'fetch_history':
|
|
383
648
|
return await this.handleFetchHistory(call.input);
|
|
384
649
|
default:
|
|
385
|
-
return { success: false, error:
|
|
650
|
+
return { success: false, error: unknownToolMessage(this.name, call.name, this.getTools()), isError: true };
|
|
386
651
|
}
|
|
387
652
|
}
|
|
388
653
|
catch (error) {
|
|
654
|
+
// A refusal says what was refused and what to do instead; anything else
|
|
655
|
+
// is unexpected, and its outcome is worded by the tool.
|
|
389
656
|
return {
|
|
390
657
|
success: false,
|
|
391
|
-
error: error instanceof
|
|
658
|
+
error: error instanceof DiscordRefusal ? error.message : unexpectedFailure(call, error),
|
|
392
659
|
isError: true,
|
|
393
660
|
};
|
|
394
661
|
}
|
|
395
662
|
}
|
|
396
663
|
async onProcess(event, state) {
|
|
664
|
+
// A stopped module answers nothing (stop()).
|
|
665
|
+
if (!this.ctx)
|
|
666
|
+
return {};
|
|
667
|
+
// Each Discord event ends the entries whose row a query shows now, stored
|
|
668
|
+
// or held.
|
|
669
|
+
if (event.type.startsWith('discord:')) {
|
|
670
|
+
for (const [id, live] of this.creating)
|
|
671
|
+
if (live.phase === 'writing' && this.storedRow(id))
|
|
672
|
+
this.creating.delete(id);
|
|
673
|
+
}
|
|
397
674
|
// Handle new Discord message
|
|
398
675
|
if (event.type === 'discord:message') {
|
|
399
676
|
const msg = event;
|
|
677
|
+
const live = this.creating.get(msg.discordMessageId);
|
|
678
|
+
// Deleted while its create waited in the queue: nothing is stored, and
|
|
679
|
+
// nobody is woken by a message its author took back.
|
|
680
|
+
if (live?.deleted)
|
|
681
|
+
return {};
|
|
400
682
|
const currentState = state.getState() ?? {
|
|
401
683
|
connectedGuilds: [],
|
|
402
684
|
conversations: {},
|
|
@@ -435,21 +717,33 @@ export class DiscordModule {
|
|
|
435
717
|
console.warn('Discord: Failed to send typing indicator:', err);
|
|
436
718
|
});
|
|
437
719
|
}
|
|
438
|
-
//
|
|
439
|
-
|
|
720
|
+
// Built from the message and its attachments, or from its text alone
|
|
721
|
+
// when an event carries no blocks; text first, as TEXT_FIRST marks.
|
|
722
|
+
const contentBlocks = msg.contentBlocks ?? textFirst(ownText(msg.content));
|
|
723
|
+
this.noteLive(msg.discordMessageId);
|
|
724
|
+
// A message a history sync wrote first, stored or held, keeps that row:
|
|
725
|
+
// the create adds none, and still sets the reply context and asks for
|
|
726
|
+
// inference, since the message arrived live. Either way the entry waits
|
|
727
|
+
// until a Discord event finds the message's row.
|
|
728
|
+
const stored = this.storedRow(msg.discordMessageId);
|
|
729
|
+
if (live)
|
|
730
|
+
live.phase = 'writing';
|
|
440
731
|
// Return declarative response - framework applies all writes atomically
|
|
441
732
|
return {
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
733
|
+
...(stored ? {} : {
|
|
734
|
+
addMessages: [
|
|
735
|
+
{
|
|
736
|
+
participant: msg.authorName,
|
|
737
|
+
content: contentBlocks,
|
|
738
|
+
metadata: {
|
|
739
|
+
external: { source: 'discord', id: msg.discordMessageId },
|
|
740
|
+
channelId: msg.channelId,
|
|
741
|
+
timestamp: msg.timestamp,
|
|
742
|
+
[TEXT_FIRST]: true,
|
|
743
|
+
},
|
|
450
744
|
},
|
|
451
|
-
|
|
452
|
-
|
|
745
|
+
],
|
|
746
|
+
}),
|
|
453
747
|
stateUpdate: newState,
|
|
454
748
|
requestInference: msg.shouldTriggerInference ?? true,
|
|
455
749
|
};
|
|
@@ -457,28 +751,50 @@ export class DiscordModule {
|
|
|
457
751
|
// Handle Discord message edit
|
|
458
752
|
if (event.type === 'discord:edit') {
|
|
459
753
|
const edit = event;
|
|
460
|
-
|
|
461
|
-
|
|
754
|
+
// The message's row, not only its id: an edit changes the message's own
|
|
755
|
+
// text and keeps what the host added beside it, but for the blocks of
|
|
756
|
+
// attachments the edit took away, when the client passed the edit's
|
|
757
|
+
// attachments and the row is text-first.
|
|
758
|
+
// An edit that comes while the live path converts the message's create
|
|
759
|
+
// is folded into that create (handleDiscordMessageEdit). Any other edit
|
|
760
|
+
// to a message with no row, stored or held, is dropped: a history sync
|
|
761
|
+
// running now stores the text it fetched, and the next sync's text rule
|
|
762
|
+
// changes it. A row stored in shards can't be edited one shard at a
|
|
763
|
+
// time, so it keeps its text, and the host's log says so. Only an edit
|
|
764
|
+
// that writes marks the message live, so an update that changes nothing
|
|
765
|
+
// leaves a running sync free to rewrite the row.
|
|
766
|
+
const stored = this.storedRow(edit.discordMessageId);
|
|
767
|
+
if (!stored)
|
|
768
|
+
return {};
|
|
769
|
+
if (stored.bodyGroupId) {
|
|
770
|
+
console.warn(`Discord: An update to message ${edit.discordMessageId} wasn't applied: it's stored in shards, which can't be edited, so if its text changed, the agent still has the earlier text`);
|
|
462
771
|
return {};
|
|
463
772
|
}
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
};
|
|
773
|
+
const texted = contentForText(stored, edit.newContent);
|
|
774
|
+
const content = edit.attachments && stored.metadata?.[TEXT_FIRST] === true
|
|
775
|
+
? withoutGoneAttachments(texted ?? stored.content, edit.attachments)
|
|
776
|
+
: texted;
|
|
777
|
+
if (!content || content === stored.content)
|
|
778
|
+
return {};
|
|
779
|
+
this.noteLive(edit.discordMessageId);
|
|
780
|
+
return { editMessages: [{ messageId: stored.id, content }] };
|
|
472
781
|
}
|
|
473
782
|
// Handle Discord message delete
|
|
474
783
|
if (event.type === 'discord:delete') {
|
|
475
784
|
const del = event;
|
|
476
|
-
|
|
477
|
-
|
|
785
|
+
// Every row of the message goes, so one stored twice doesn't outlive
|
|
786
|
+
// its delete as a copy. A row stored in shards is removed whole: the
|
|
787
|
+
// framework takes out the body a shard belongs to. The message is the
|
|
788
|
+
// live path's even when no row is found, so a running sync doesn't
|
|
789
|
+
// store what its fetch may still hold, and that ends its entry.
|
|
790
|
+
this.noteLive(del.discordMessageId);
|
|
791
|
+
this.creating.delete(del.discordMessageId);
|
|
792
|
+
const rows = this.storedRows(del.discordMessageId);
|
|
793
|
+
if (rows.length === 0) {
|
|
478
794
|
return {};
|
|
479
795
|
}
|
|
480
796
|
return {
|
|
481
|
-
removeMessages:
|
|
797
|
+
removeMessages: oncePerBody(rows),
|
|
482
798
|
};
|
|
483
799
|
}
|
|
484
800
|
return {};
|
|
@@ -487,13 +803,15 @@ export class DiscordModule {
|
|
|
487
803
|
* Handle agent speech - send to Discord.
|
|
488
804
|
*/
|
|
489
805
|
async onAgentSpeech(agentName, content, context) {
|
|
490
|
-
// If thoughts are present and
|
|
491
|
-
// to prepend the thoughts as spoiler text.
|
|
492
|
-
|
|
806
|
+
// If thoughts are present and this agent posted a message in the turn
|
|
807
|
+
// they end (lastPosts), edit it to prepend the thoughts as spoiler text.
|
|
808
|
+
const post = this.lastPosts.get(agentName);
|
|
809
|
+
if (context.thoughts && context.thoughts.length > 0 && post && post.turnId === context.turnId) {
|
|
810
|
+
this.lastPosts.delete(agentName);
|
|
493
811
|
const thoughtText = this.extractText(context.thoughts);
|
|
494
812
|
if (thoughtText) {
|
|
495
813
|
try {
|
|
496
|
-
const { channelId, messageId } =
|
|
814
|
+
const { channelId, messageId } = post;
|
|
497
815
|
// Fetch the current message content via the client isn't available,
|
|
498
816
|
// but we know the tool just sent it — reconstruct by editing with
|
|
499
817
|
// spoiler-wrapped thoughts prepended.
|
|
@@ -506,7 +824,6 @@ export class DiscordModule {
|
|
|
506
824
|
console.warn('Discord: Failed to edit thoughts into message:', err);
|
|
507
825
|
}
|
|
508
826
|
}
|
|
509
|
-
this.lastSentMessage = null;
|
|
510
827
|
}
|
|
511
828
|
// Get reply context for this agent
|
|
512
829
|
const replyCtx = this.replyContexts.get(agentName) ?? this.replyContexts.get('default');
|
|
@@ -521,7 +838,7 @@ export class DiscordModule {
|
|
|
521
838
|
return;
|
|
522
839
|
}
|
|
523
840
|
// Check rate limit
|
|
524
|
-
if (
|
|
841
|
+
if (this.rateLimitWait(replyCtx.channelId) > 0) {
|
|
525
842
|
console.warn(`Discord: Rate limited for channel ${replyCtx.channelId}`);
|
|
526
843
|
return;
|
|
527
844
|
}
|
|
@@ -530,8 +847,8 @@ export class DiscordModule {
|
|
|
530
847
|
const result = await this.client.sendMessage(targetChannel, text, {
|
|
531
848
|
replyTo: replyCtx.replyToMessageId,
|
|
532
849
|
});
|
|
533
|
-
// Track for thought-editing (in case of chained speech)
|
|
534
|
-
this.
|
|
850
|
+
// Track for thought-editing (in case of chained speech in the same turn)
|
|
851
|
+
this.notePost(agentName, context.turnId, targetChannel, result.messageId);
|
|
535
852
|
// Clear reply-to after sending (don't keep replying to same message)
|
|
536
853
|
replyCtx.replyToMessageId = undefined;
|
|
537
854
|
replyCtx.lastActivity = Date.now();
|
|
@@ -612,114 +929,190 @@ export class DiscordModule {
|
|
|
612
929
|
}
|
|
613
930
|
/**
|
|
614
931
|
* Sync history for a specific channel - compare with Context Manager and update.
|
|
615
|
-
* Uses queryMessages() to enumerate
|
|
932
|
+
* Uses queryMessages() to enumerate the channel's rows, a held write's among
|
|
933
|
+
* them, for proper edit/delete detection.
|
|
934
|
+
*
|
|
935
|
+
* The live path can store, edit or remove a row whenever the sync awaits.
|
|
936
|
+
* So the sync fetches history and runs the live conversion it needs first,
|
|
937
|
+
* then reads the store again and writes with no await between that read
|
|
938
|
+
* and its writes. It leaves alone any message the live path stored,
|
|
939
|
+
* changed or was told was deleted while it ran (liveDuringSync), an update
|
|
940
|
+
* that changed nothing aside. A live edit to a message nobody had stored
|
|
941
|
+
* yet is dropped, so a message the sync stores holds the text it fetched
|
|
942
|
+
* until the next sync's text rule changes it. The framework writes an
|
|
943
|
+
* event's response only once every module has processed the event, so an
|
|
944
|
+
* event processed before the sync began can still be written after the
|
|
945
|
+
* sync's own writes, which this doesn't cover.
|
|
616
946
|
*/
|
|
617
947
|
async syncChannelHistory(channelId) {
|
|
618
|
-
|
|
619
|
-
|
|
948
|
+
const unchanged = { newMessages: 0, editedMessages: 0, deletedMessages: 0 };
|
|
949
|
+
const ctx = this.ctx;
|
|
950
|
+
if (!ctx) {
|
|
951
|
+
return unchanged;
|
|
620
952
|
}
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
953
|
+
const live = new Set();
|
|
954
|
+
this.liveDuringSync.add(live);
|
|
955
|
+
try {
|
|
956
|
+
const scrollback = this.config.historyScrollback ?? 50;
|
|
957
|
+
// The rows stored as the sync begins. One that is gone by the time it
|
|
958
|
+
// writes was removed meanwhile, and isn't the sync's to bring back.
|
|
959
|
+
const before = this.storedByDiscordId(ctx, channelId);
|
|
960
|
+
// Fetch recent history from Discord. If the module stopped or restarted
|
|
961
|
+
// meanwhile, this sync's context is gone and it writes nothing.
|
|
962
|
+
const discordMessages = await this.client.fetchHistory(channelId, { limit: scrollback });
|
|
963
|
+
if (this.ctx !== ctx)
|
|
964
|
+
return unchanged;
|
|
965
|
+
// Discord's ids, for delete detection inside the fetched window.
|
|
966
|
+
const discordIds = new Set(discordMessages.map((msg) => msg.id));
|
|
967
|
+
// Discord history is newest-first, while addMessage appends to the stored
|
|
968
|
+
// conversation. Process oldest-first so missed messages retain chronology.
|
|
969
|
+
const chronologicalMessages = [...discordMessages].reverse();
|
|
970
|
+
// The live conversion, which fetches attachments, for a message the
|
|
971
|
+
// store lacks and for a row that doesn't hold what the conversion gives
|
|
972
|
+
// the message now (needsRebuild). A row stored in shards can't be
|
|
973
|
+
// edited one shard at a time, so it's left as it is.
|
|
974
|
+
const converted = new Map();
|
|
975
|
+
for (const discordMsg of chronologicalMessages) {
|
|
976
|
+
const stored = before.get(discordMsg.id)?.[0];
|
|
977
|
+
if (stored && (stored.bodyGroupId || !needsRebuild(stored, discordMsg)))
|
|
978
|
+
continue;
|
|
979
|
+
const content = await this.convertMessageToContent(discordMsg);
|
|
980
|
+
if (this.ctx !== ctx)
|
|
981
|
+
return unchanged;
|
|
982
|
+
// A failed fetch never costs a row the text it holds: when a file
|
|
983
|
+
// wasn't fetched, a row holding other text, which may be that file's,
|
|
984
|
+
// keeps what it holds, and the next sync tries again.
|
|
985
|
+
if (stored && holdsNotFetched(content, discordMsg) && holdsOtherText(stored, discordMsg))
|
|
986
|
+
continue;
|
|
987
|
+
converted.set(discordMsg.id, content);
|
|
655
988
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
989
|
+
// From here on nothing awaits, so the rows read now are the rows written.
|
|
990
|
+
const storedMessageMap = this.storedByDiscordId(ctx, channelId);
|
|
991
|
+
let newMessages = 0;
|
|
992
|
+
let editedMessages = 0;
|
|
993
|
+
let deletedMessages = 0;
|
|
994
|
+
for (const discordMsg of chronologicalMessages) {
|
|
995
|
+
if (live.has(discordMsg.id))
|
|
996
|
+
continue;
|
|
997
|
+
const stored = storedMessageMap.get(discordMsg.id)?.[0];
|
|
998
|
+
if (!stored) {
|
|
999
|
+
// A message we haven't stored. One that was stored when the sync
|
|
1000
|
+
// began, and isn't now, was removed meanwhile: not the sync's to
|
|
1001
|
+
// bring back. One the live path is storing (creating) is the live
|
|
1002
|
+
// path's, so it isn't stored twice.
|
|
1003
|
+
const content = converted.get(discordMsg.id);
|
|
1004
|
+
if (!content || before.has(discordMsg.id) || this.creating.has(discordMsg.id))
|
|
1005
|
+
continue;
|
|
1006
|
+
// For bot's own messages (from previous sessions), use the agent name
|
|
1007
|
+
// This ensures continuity of conversation history
|
|
1008
|
+
const isBotMessage = discordMsg.authorId === this.state.botUserId;
|
|
1009
|
+
const agentName = ctx.getAgents()[0]?.name;
|
|
1010
|
+
const participantName = isBotMessage
|
|
1011
|
+
? (agentName ?? discordMsg.authorName)
|
|
1012
|
+
: discordMsg.authorName;
|
|
1013
|
+
ctx.addMessage(participantName, content, {
|
|
1014
|
+
external: { source: 'discord', id: discordMsg.id },
|
|
1015
|
+
channelId,
|
|
1016
|
+
timestamp: discordMsg.timestamp.getTime(),
|
|
1017
|
+
isOwnMessage: isBotMessage, // Mark as our own for reference
|
|
1018
|
+
[TEXT_FIRST]: true,
|
|
1019
|
+
});
|
|
1020
|
+
newMessages++;
|
|
1021
|
+
}
|
|
1022
|
+
else if (!stored.bodyGroupId) {
|
|
1023
|
+
// We have this message - check whether it changed (offline edit detection)
|
|
1024
|
+
const content = needsRebuild(stored, discordMsg)
|
|
1025
|
+
? converted.get(discordMsg.id)
|
|
1026
|
+
: contentForText(stored, discordMsg.content);
|
|
1027
|
+
if (content) {
|
|
1028
|
+
ctx.editMessage(stored.id, content);
|
|
1029
|
+
editedMessages++;
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
681
1032
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1033
|
+
// Check for deletes only inside the history we actually fetched. A short
|
|
1034
|
+
// response covers complete available history; a full page has an older
|
|
1035
|
+
// boundary whose tied millisecond must be preserved.
|
|
1036
|
+
const fetchedCompleteHistory = discordMessages.length < scrollback;
|
|
1037
|
+
const oldestFetchedTimestamp = discordMessages.length > 0
|
|
1038
|
+
? Math.min(...discordMessages.map((msg) => msg.timestamp.getTime()))
|
|
1039
|
+
: undefined;
|
|
1040
|
+
for (const [externalId, rows] of storedMessageMap) {
|
|
1041
|
+
const stored = rows[0];
|
|
1042
|
+
const sourceTimestamp = typeof stored.metadata?.timestamp === 'number'
|
|
1043
|
+
? stored.metadata.timestamp
|
|
1044
|
+
: undefined;
|
|
1045
|
+
const isInsideFetchedWindow = fetchedCompleteHistory || (sourceTimestamp !== undefined &&
|
|
1046
|
+
oldestFetchedTimestamp !== undefined &&
|
|
1047
|
+
// Discord timestamps are millisecond-precise, so an older message just
|
|
1048
|
+
// beyond the page can tie the oldest fetched timestamp. Preserve ties
|
|
1049
|
+
// rather than guessing that an unseen boundary message was deleted.
|
|
1050
|
+
sourceTimestamp > oldestFetchedTimestamp);
|
|
1051
|
+
if (isInsideFetchedWindow && !discordIds.has(externalId) && !live.has(externalId)) {
|
|
1052
|
+
// Message exists in our store but not in Discord - it was deleted
|
|
1053
|
+
// Note: This only catches deletes within the scrollback window.
|
|
1054
|
+
// A message the live path wrote while the sync ran is newer than
|
|
1055
|
+
// the fetch, so it stays. Every row of the message goes, and one
|
|
1056
|
+
// shard's id takes out a body whole.
|
|
1057
|
+
for (const id of oncePerBody(rows))
|
|
1058
|
+
ctx.removeMessage(id);
|
|
1059
|
+
deletedMessages++;
|
|
689
1060
|
}
|
|
690
1061
|
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
const oldestFetchedTimestamp = discordMessages.length > 0
|
|
697
|
-
? Math.min(...discordMessages.map((msg) => msg.timestamp.getTime()))
|
|
698
|
-
: undefined;
|
|
699
|
-
for (const [externalId, stored] of storedMessageMap) {
|
|
700
|
-
const isInsideFetchedWindow = fetchedCompleteHistory || (stored.sourceTimestamp !== undefined &&
|
|
701
|
-
oldestFetchedTimestamp !== undefined &&
|
|
702
|
-
// Discord timestamps are millisecond-precise, so an older message just
|
|
703
|
-
// beyond the page can tie the oldest fetched timestamp. Preserve ties
|
|
704
|
-
// rather than guessing that an unseen boundary message was deleted.
|
|
705
|
-
stored.sourceTimestamp > oldestFetchedTimestamp);
|
|
706
|
-
if (isInsideFetchedWindow && !discordMessageMap.has(externalId)) {
|
|
707
|
-
// Message exists in our store but not in Discord - it was deleted
|
|
708
|
-
// Note: This only catches deletes within the scrollback window
|
|
709
|
-
this.ctx.removeMessage(stored.internalId);
|
|
710
|
-
deletedMessages++;
|
|
1062
|
+
// Update last read
|
|
1063
|
+
if (discordMessages.length > 0) {
|
|
1064
|
+
// Messages are typically newest first
|
|
1065
|
+
const newestId = discordMessages[0].id;
|
|
1066
|
+
this.state.lastReadMessageId[channelId] = newestId;
|
|
711
1067
|
}
|
|
1068
|
+
return { newMessages, editedMessages, deletedMessages };
|
|
1069
|
+
}
|
|
1070
|
+
finally {
|
|
1071
|
+
this.liveDuringSync.delete(live);
|
|
712
1072
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
1073
|
+
}
|
|
1074
|
+
/** A channel's stored Discord rows by their Discord message id, whole: an
|
|
1075
|
+
* edit replaces a row's own text and keeps what the host added beside it.
|
|
1076
|
+
* Each id's rows are in store order. A message stored twice holds two,
|
|
1077
|
+
* of which a sync acts on the first, as the live path does (storedRow),
|
|
1078
|
+
* and a delete takes both. A write the framework holds is a row too
|
|
1079
|
+
* (storedRows). */
|
|
1080
|
+
storedByDiscordId(ctx, channelId) {
|
|
1081
|
+
const rows = new Map();
|
|
1082
|
+
for (const msg of ctx.queryMessages({ source: 'discord', metadata: { channelId } }).messages) {
|
|
1083
|
+
const external = msg.metadata?.external;
|
|
1084
|
+
if (!external?.id)
|
|
1085
|
+
continue;
|
|
1086
|
+
const held = rows.get(external.id);
|
|
1087
|
+
if (held)
|
|
1088
|
+
held.push(msg);
|
|
1089
|
+
else
|
|
1090
|
+
rows.set(external.id, [msg]);
|
|
718
1091
|
}
|
|
719
|
-
return
|
|
1092
|
+
return rows;
|
|
1093
|
+
}
|
|
1094
|
+
/** The stored rows of a Discord message, by its id in the row's metadata,
|
|
1095
|
+
* in store order. A write the framework holds comes after them as a held
|
|
1096
|
+
* row, which an edit or removal reaches as it does a stored one. The
|
|
1097
|
+
* module context's findMessageByExternalId holds only rows the module
|
|
1098
|
+
* stored through its context, not those a process response added, so it
|
|
1099
|
+
* misses what the live path stored. */
|
|
1100
|
+
storedRows(discordMessageId) {
|
|
1101
|
+
return this.ctx?.queryMessages({ source: 'discord', externalIds: [discordMessageId] }).messages ?? [];
|
|
1102
|
+
}
|
|
1103
|
+
/** The row an edit or a create acts on: a message's first row, a stored
|
|
1104
|
+
* one before a held one (storedRows). */
|
|
1105
|
+
storedRow(discordMessageId) {
|
|
1106
|
+
return this.storedRows(discordMessageId)[0];
|
|
1107
|
+
}
|
|
1108
|
+
/** Marks a Discord message as the live path's for every history sync running now. */
|
|
1109
|
+
noteLive(discordMessageId) {
|
|
1110
|
+
for (const live of this.liveDuringSync)
|
|
1111
|
+
live.add(discordMessageId);
|
|
720
1112
|
}
|
|
721
1113
|
async handleDiscordMessage(message) {
|
|
722
|
-
|
|
1114
|
+
const ctx = this.ctx;
|
|
1115
|
+
if (!ctx)
|
|
723
1116
|
return;
|
|
724
1117
|
// ALWAYS ignore our own messages to prevent feedback loops
|
|
725
1118
|
if (this.state.botUserId && message.authorId === this.state.botUserId) {
|
|
@@ -746,35 +1139,65 @@ export class DiscordModule {
|
|
|
746
1139
|
}
|
|
747
1140
|
// Determine if this message should trigger inference
|
|
748
1141
|
const shouldTrigger = this.shouldTriggerInference(message, isDM);
|
|
1142
|
+
// From here the message is the live path's to store (creating). A second
|
|
1143
|
+
// delivery of a message whose create is already on its way is ignored.
|
|
1144
|
+
if (this.creating.has(message.id))
|
|
1145
|
+
return;
|
|
1146
|
+
const live = { phase: 'converting', deleted: false };
|
|
1147
|
+
this.creating.set(message.id, live);
|
|
1148
|
+
const drop = () => { if (this.creating.get(message.id) === live)
|
|
1149
|
+
this.creating.delete(message.id); };
|
|
749
1150
|
// Convert message content including attachments
|
|
750
|
-
|
|
1151
|
+
let contentBlocks;
|
|
1152
|
+
try {
|
|
1153
|
+
contentBlocks = await this.convertMessageToContent(message);
|
|
1154
|
+
}
|
|
1155
|
+
catch (error) {
|
|
1156
|
+
drop();
|
|
1157
|
+
throw error;
|
|
1158
|
+
}
|
|
1159
|
+
// A module stopped or started again meanwhile writes nothing.
|
|
1160
|
+
if (this.ctx !== ctx) {
|
|
1161
|
+
drop();
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1164
|
+
// A delete that came meanwhile drops the create: nothing is stored,
|
|
1165
|
+
// nobody is woken. Its entry stays until that delete is handled, so no
|
|
1166
|
+
// sync stores the message before then.
|
|
1167
|
+
if (live.deleted)
|
|
1168
|
+
return;
|
|
1169
|
+
// An edit that came meanwhile: its text, and the blocks of the
|
|
1170
|
+
// attachments it left.
|
|
1171
|
+
if (live.text !== undefined)
|
|
1172
|
+
contentBlocks = withOwnText(contentBlocks, live.text);
|
|
1173
|
+
if (live.attachments)
|
|
1174
|
+
contentBlocks = withoutGoneAttachments(contentBlocks, live.attachments);
|
|
1175
|
+
live.phase = 'queued';
|
|
751
1176
|
// Only queue the event - all state writes happen in onProcess()
|
|
752
|
-
|
|
1177
|
+
ctx.pushEvent({
|
|
753
1178
|
type: 'discord:message',
|
|
754
1179
|
discordMessageId: message.id,
|
|
755
1180
|
channelId: message.channelId,
|
|
756
1181
|
guildId: message.guildId,
|
|
757
1182
|
authorId: message.authorId,
|
|
758
1183
|
authorName: message.authorName,
|
|
759
|
-
content: message.content,
|
|
1184
|
+
content: live.text ?? message.content,
|
|
760
1185
|
contentBlocks,
|
|
761
1186
|
timestamp: message.timestamp.getTime(),
|
|
762
|
-
attachments: message.attachments,
|
|
1187
|
+
attachments: live.attachments ?? message.attachments,
|
|
763
1188
|
shouldTriggerInference: shouldTrigger,
|
|
764
1189
|
});
|
|
765
1190
|
}
|
|
766
1191
|
/**
|
|
767
|
-
* Convert Discord message to ContentBlock array
|
|
768
|
-
*
|
|
1192
|
+
* Convert a Discord message, live or from history, to ContentBlock array:
|
|
1193
|
+
* its own text first, '' when it has none, then any supported attachments
|
|
1194
|
+
* (images, text files) and notes for the rest, as TEXT_FIRST marks.
|
|
769
1195
|
*/
|
|
770
1196
|
async convertMessageToContent(message) {
|
|
1197
|
+
// What the host adds after the message's own text: one block per attachment.
|
|
771
1198
|
const blocks = [];
|
|
772
|
-
// Add text content if present
|
|
773
|
-
if (message.content.trim()) {
|
|
774
|
-
blocks.push({ type: 'text', text: message.content });
|
|
775
|
-
}
|
|
776
1199
|
// Process attachments
|
|
777
|
-
for (const attachment of message.attachments) {
|
|
1200
|
+
for (const attachment of message.attachments ?? []) {
|
|
778
1201
|
const contentType = attachment.contentType?.toLowerCase() ?? '';
|
|
779
1202
|
const filename = attachment.filename.toLowerCase();
|
|
780
1203
|
// Check for supported image types
|
|
@@ -790,7 +1213,15 @@ export class DiscordModule {
|
|
|
790
1213
|
}
|
|
791
1214
|
// Check for text files - fetch and include content
|
|
792
1215
|
const isTextFile = TEXT_FILE_EXTENSIONS.some(ext => filename.endsWith(ext));
|
|
793
|
-
if (isTextFile && attachment.size
|
|
1216
|
+
if (isTextFile && attachment.size >= TEXT_FILE_LIMIT_BYTES) {
|
|
1217
|
+
blocks.push({
|
|
1218
|
+
type: 'text',
|
|
1219
|
+
text: `[Attachment: ${attachment.filename} (${attachment.size.toLocaleString('en-US')} bytes): this host reads text files under ${TEXT_FILE_LIMIT_BYTES.toLocaleString('en-US')} bytes into your context, so its text isn't here. If you need it, ask the sender to paste the part that matters.]`,
|
|
1220
|
+
});
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
// A text file of size 0 gets the note below, as before.
|
|
1224
|
+
if (isTextFile && attachment.size) {
|
|
794
1225
|
try {
|
|
795
1226
|
const response = await fetch(attachment.url);
|
|
796
1227
|
if (response.ok) {
|
|
@@ -800,27 +1231,26 @@ export class DiscordModule {
|
|
|
800
1231
|
text: `\n--- File: ${attachment.filename} ---\n${textContent}\n--- End of ${attachment.filename} ---\n`,
|
|
801
1232
|
});
|
|
802
1233
|
}
|
|
1234
|
+
else {
|
|
1235
|
+
// An answer whose body is never read holds its connection.
|
|
1236
|
+
await response.body?.cancel().catch(() => { });
|
|
1237
|
+
console.warn(`Discord: Attachment ${logValue(attachment.filename)} in message ${message.id} wasn't fetched: HTTP ${response.status}`);
|
|
1238
|
+
blocks.push(attachmentNotFetched(attachment.filename));
|
|
1239
|
+
}
|
|
803
1240
|
}
|
|
804
1241
|
catch (error) {
|
|
805
|
-
|
|
806
|
-
blocks.push(
|
|
807
|
-
type: 'text',
|
|
808
|
-
text: `[Attachment: ${attachment.filename} (failed to fetch)]`,
|
|
809
|
-
});
|
|
1242
|
+
console.warn(`Discord: Attachment ${logValue(attachment.filename)} in message ${message.id} wasn't fetched:`, error);
|
|
1243
|
+
blocks.push(attachmentNotFetched(attachment.filename));
|
|
810
1244
|
}
|
|
811
1245
|
continue;
|
|
812
1246
|
}
|
|
813
1247
|
// For unsupported attachments, add a note
|
|
814
1248
|
blocks.push({
|
|
815
1249
|
type: 'text',
|
|
816
|
-
text: `[Attachment: ${attachment.filename} (${contentType || 'unknown type'})]`,
|
|
1250
|
+
text: `[Attachment: ${attachment.filename} (${contentType || 'unknown type'}): this host didn't read its contents in, so they aren't in your context. If you need them, ask the sender for them in a form you can read, such as pasted text.]`,
|
|
817
1251
|
});
|
|
818
1252
|
}
|
|
819
|
-
|
|
820
|
-
if (blocks.length === 0) {
|
|
821
|
-
blocks.push({ type: 'text', text: '[Empty message]' });
|
|
822
|
-
}
|
|
823
|
-
return blocks;
|
|
1253
|
+
return textFirst(ownText(message.content), blocks);
|
|
824
1254
|
}
|
|
825
1255
|
/**
|
|
826
1256
|
* Determine if a message should trigger agent inference based on config.
|
|
@@ -854,19 +1284,35 @@ export class DiscordModule {
|
|
|
854
1284
|
return isMentioned || isReplyToBot;
|
|
855
1285
|
}
|
|
856
1286
|
}
|
|
857
|
-
handleDiscordMessageEdit(messageId, newContent) {
|
|
1287
|
+
handleDiscordMessageEdit(messageId, newContent, attachments) {
|
|
858
1288
|
if (!this.ctx)
|
|
859
1289
|
return;
|
|
1290
|
+
// An edit while the message's create converts is folded into that
|
|
1291
|
+
// create, which would otherwise store the text it was sent with; one that
|
|
1292
|
+
// comes after follows the create through the queue. Its event still goes,
|
|
1293
|
+
// for a row a history sync stored first.
|
|
1294
|
+
const live = this.creating.get(messageId);
|
|
1295
|
+
if (live) {
|
|
1296
|
+
live.text = newContent;
|
|
1297
|
+
if (attachments)
|
|
1298
|
+
live.attachments = attachments;
|
|
1299
|
+
}
|
|
860
1300
|
// Only queue the event - state writes happen in onProcess()
|
|
861
1301
|
this.ctx.pushEvent({
|
|
862
1302
|
type: 'discord:edit',
|
|
863
1303
|
discordMessageId: messageId,
|
|
864
1304
|
newContent,
|
|
1305
|
+
...(attachments ? { attachments } : {}),
|
|
865
1306
|
});
|
|
866
1307
|
}
|
|
867
1308
|
handleDiscordMessageDelete(messageId) {
|
|
868
1309
|
if (!this.ctx)
|
|
869
1310
|
return;
|
|
1311
|
+
// A delete before the message's create is handled drops that create.
|
|
1312
|
+
// Its event still goes, for a row a history sync stored first.
|
|
1313
|
+
const live = this.creating.get(messageId);
|
|
1314
|
+
if (live)
|
|
1315
|
+
live.deleted = true;
|
|
870
1316
|
// Only queue the event - state writes happen in onProcess()
|
|
871
1317
|
this.ctx.pushEvent({
|
|
872
1318
|
type: 'discord:delete',
|
|
@@ -876,40 +1322,100 @@ export class DiscordModule {
|
|
|
876
1322
|
// ==========================================================================
|
|
877
1323
|
// Tool Handlers
|
|
878
1324
|
// ==========================================================================
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1325
|
+
/** Record a message the agent posted in a turn, for that turn's thoughts (lastPosts). */
|
|
1326
|
+
notePost(agentName, turnId, channelId, messageId) {
|
|
1327
|
+
if (agentName === undefined || turnId === undefined)
|
|
1328
|
+
return;
|
|
1329
|
+
// A later turn's post stays: this one's turn has ended, and its thoughts
|
|
1330
|
+
// have gone or will go nowhere.
|
|
1331
|
+
const current = this.lastPosts.get(agentName);
|
|
1332
|
+
if (current && current.turnId > turnId)
|
|
1333
|
+
return;
|
|
1334
|
+
// Set it last, so the entry recorded longest ago is the first to go.
|
|
1335
|
+
this.lastPosts.delete(agentName);
|
|
1336
|
+
this.lastPosts.set(agentName, { turnId, channelId, messageId });
|
|
1337
|
+
if (this.lastPosts.size > MAX_LAST_POSTS) {
|
|
1338
|
+
const oldest = this.lastPosts.keys().next().value;
|
|
1339
|
+
if (oldest !== undefined)
|
|
1340
|
+
this.lastPosts.delete(oldest);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
async handleSend(input, call) {
|
|
1344
|
+
const wait = this.rateLimitWait(input.channelId);
|
|
1345
|
+
if (wait > 0) {
|
|
1346
|
+
return {
|
|
1347
|
+
success: false,
|
|
1348
|
+
error: `Nothing was sent: channel ${input.channelId} has reached ${this.rateLimitRule()}. Send it again in ${seconds(wait)}; other channels have their own limit.`,
|
|
1349
|
+
isError: true,
|
|
1350
|
+
};
|
|
882
1351
|
}
|
|
883
1352
|
const result = await this.client.sendMessage(input.channelId, input.content, {
|
|
884
1353
|
replyTo: input.replyTo,
|
|
885
1354
|
createThread: input.createThread,
|
|
886
1355
|
});
|
|
887
|
-
// Track for thought-editing
|
|
888
|
-
|
|
889
|
-
|
|
1356
|
+
// Track for thought-editing: the message is out, whether or not its
|
|
1357
|
+
// thread was started.
|
|
1358
|
+
this.notePost(call.callerAgentName, call.callerTurnId, input.channelId, result.messageId);
|
|
1359
|
+
if (result.threadFailure !== undefined) {
|
|
1360
|
+
return { success: false, error: result.threadFailure, isError: true };
|
|
1361
|
+
}
|
|
1362
|
+
return { success: true, data: { messageId: result.messageId, ...(result.note !== undefined ? { note: result.note } : {}) } };
|
|
890
1363
|
}
|
|
891
1364
|
async handleDM(input) {
|
|
892
1365
|
const result = await this.client.sendDM(input.userId, input.content);
|
|
893
|
-
|
|
1366
|
+
// The direct message channel's id is what fetch_history reads it by, should a later dm need checking.
|
|
1367
|
+
return { success: true, data: { messageId: result.messageId, ...(result.channelId !== undefined ? { channelId: result.channelId } : {}) } };
|
|
894
1368
|
}
|
|
895
|
-
async handleReply(input) {
|
|
1369
|
+
async handleReply(input, call) {
|
|
896
1370
|
const replyCtx = this.replyContexts.get('default');
|
|
897
1371
|
if (!replyCtx) {
|
|
898
|
-
return {
|
|
1372
|
+
return {
|
|
1373
|
+
success: false,
|
|
1374
|
+
error: 'Nothing was sent: there\'s no conversation to reply in. Use discord--send with a channelId to post somewhere, or discord--set_reply_context with a channelId to choose where replies go, then reply again.',
|
|
1375
|
+
isError: true,
|
|
1376
|
+
};
|
|
899
1377
|
}
|
|
900
|
-
|
|
901
|
-
|
|
1378
|
+
// The reply goes to the context's thread when it names one, an empty id
|
|
1379
|
+
// as well, as it always has; the words name an empty id as what it is.
|
|
1380
|
+
const thread = replyCtx.threadId;
|
|
1381
|
+
const wait = this.rateLimitWait(replyCtx.channelId);
|
|
1382
|
+
if (wait > 0) {
|
|
1383
|
+
return {
|
|
1384
|
+
success: false,
|
|
1385
|
+
error: `Nothing was sent: this conversation's channel (${replyCtx.channelId}) has reached ${this.rateLimitRule()}`
|
|
1386
|
+
+ `${thread ? `, which counts its replies in thread ${thread} too` : ''}. Reply again in ${seconds(wait)}.`,
|
|
1387
|
+
isError: true,
|
|
1388
|
+
};
|
|
902
1389
|
}
|
|
903
1390
|
let content = input.content;
|
|
904
1391
|
if (input.mention && replyCtx.userId) {
|
|
905
1392
|
content = `<@${replyCtx.userId}> ${content}`;
|
|
906
1393
|
}
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
}
|
|
1394
|
+
// reply's call names no channel or message: its reply context does, as it
|
|
1395
|
+
// stands now, before the send, which an incoming message can change.
|
|
1396
|
+
const targetChannel = thread ?? replyCtx.channelId;
|
|
1397
|
+
const pointsAt = thread === undefined ? `channel ${targetChannel}` : thread === '' ? 'an empty thread id' : `thread ${targetChannel}`;
|
|
1398
|
+
const check = thread === ''
|
|
1399
|
+
? `, and its reply context names an empty thread id: discord--set_reply_context with a threadId, or without one to reply in channel ${replyCtx.channelId}, puts that right`
|
|
1400
|
+
: `: discord--fetch_history with channelId ${targetChannel}, its reply context's ${thread !== undefined ? 'thread' : 'channel'}, shows it, so check before you reply again`;
|
|
1401
|
+
const replyTo = replyCtx.replyToMessageId;
|
|
1402
|
+
let result;
|
|
1403
|
+
try {
|
|
1404
|
+
result = await this.client.sendMessage(targetChannel, content, { replyTo });
|
|
1405
|
+
}
|
|
1406
|
+
catch (error) {
|
|
1407
|
+
if (error instanceof DiscordRefusal) {
|
|
1408
|
+
throw new DiscordRefusal(`${error.message} A reply goes where its reply context points: ${pointsAt}`
|
|
1409
|
+
+ `${replyTo ? `, answering message ${replyTo}` : ''}. discord--set_reply_context changes that.`, { cause: error });
|
|
1410
|
+
}
|
|
1411
|
+
return {
|
|
1412
|
+
success: false,
|
|
1413
|
+
error: `discord--reply stopped on an unexpected error: ${errorText(error)}. Whether the reply was sent is unknown${check}.`,
|
|
1414
|
+
isError: true,
|
|
1415
|
+
};
|
|
1416
|
+
}
|
|
911
1417
|
// Track for thought-editing
|
|
912
|
-
this.
|
|
1418
|
+
this.notePost(call.callerAgentName, call.callerTurnId, targetChannel, result.messageId);
|
|
913
1419
|
// Clear reply-to after sending
|
|
914
1420
|
replyCtx.replyToMessageId = undefined;
|
|
915
1421
|
replyCtx.lastActivity = Date.now();
|
|
@@ -957,14 +1463,14 @@ export class DiscordModule {
|
|
|
957
1463
|
}
|
|
958
1464
|
async handleActivate(input) {
|
|
959
1465
|
if (!this.ctx) {
|
|
960
|
-
return { success: false, error:
|
|
1466
|
+
return { success: false, error: NOT_RUNNING, isError: true };
|
|
961
1467
|
}
|
|
962
1468
|
this.ctx.registerSpeechHandler('*', { additive: input.additive });
|
|
963
1469
|
return { success: true, data: { message: 'Discord is now handling speech' } };
|
|
964
1470
|
}
|
|
965
1471
|
async handleDeactivate() {
|
|
966
1472
|
if (!this.ctx) {
|
|
967
|
-
return { success: false, error:
|
|
1473
|
+
return { success: false, error: NOT_RUNNING, isError: true };
|
|
968
1474
|
}
|
|
969
1475
|
this.ctx.unregisterSpeechHandler();
|
|
970
1476
|
return { success: true, data: { message: 'Discord is no longer handling speech' } };
|
|
@@ -1020,6 +1526,11 @@ export class DiscordModule {
|
|
|
1020
1526
|
authorId: m.authorId,
|
|
1021
1527
|
isBot: m.isBot,
|
|
1022
1528
|
content: m.content,
|
|
1529
|
+
// Not their URLs: a link Discord signs works for anyone holding it
|
|
1530
|
+
// until it expires, and an agent could repeat it where others read.
|
|
1531
|
+
...(m.attachments?.length
|
|
1532
|
+
? { attachments: m.attachments.map(({ filename, contentType, size }) => ({ filename, contentType, size })) }
|
|
1533
|
+
: {}),
|
|
1023
1534
|
timestamp: formatZonedDateTime(m.timestamp, this.timeZone),
|
|
1024
1535
|
replyTo: m.replyTo,
|
|
1025
1536
|
})),
|
|
@@ -1035,8 +1546,23 @@ export class DiscordModule {
|
|
|
1035
1546
|
.map((block) => block.text)
|
|
1036
1547
|
.join('\n');
|
|
1037
1548
|
}
|
|
1038
|
-
|
|
1039
|
-
|
|
1549
|
+
rateLimitPerMinute() {
|
|
1550
|
+
return this.config.rateLimitPerMinute ?? 30;
|
|
1551
|
+
}
|
|
1552
|
+
/** The limit, as the rate-limit refusals word it. */
|
|
1553
|
+
rateLimitRule() {
|
|
1554
|
+
const limit = this.rateLimitPerMinute();
|
|
1555
|
+
return `this host's limit of ${limit} send${limit === 1 ? '' : 's'} a minute from the bot`;
|
|
1556
|
+
}
|
|
1557
|
+
/**
|
|
1558
|
+
* Counts a send to `channelId` against its limit and returns 0, or, when
|
|
1559
|
+
* the channel has had its sends for this window, returns the whole seconds
|
|
1560
|
+
* until the window ends, at least 1. A window is the minute from the first
|
|
1561
|
+
* send this lets through, and each send it lets through counts, whether or
|
|
1562
|
+
* not Discord then takes it.
|
|
1563
|
+
*/
|
|
1564
|
+
rateLimitWait(channelId) {
|
|
1565
|
+
const limit = this.rateLimitPerMinute();
|
|
1040
1566
|
const now = Date.now();
|
|
1041
1567
|
const windowMs = 60 * 1000;
|
|
1042
1568
|
let info = this.state.rateLimits[channelId];
|
|
@@ -1046,10 +1572,10 @@ export class DiscordModule {
|
|
|
1046
1572
|
this.state.rateLimits[channelId] = info;
|
|
1047
1573
|
}
|
|
1048
1574
|
if (info.count >= limit) {
|
|
1049
|
-
return
|
|
1575
|
+
return Math.max(1, Math.ceil((info.windowStart + windowMs - now) / 1000));
|
|
1050
1576
|
}
|
|
1051
1577
|
info.count++;
|
|
1052
|
-
return
|
|
1578
|
+
return 0;
|
|
1053
1579
|
}
|
|
1054
1580
|
cleanupOldConversations() {
|
|
1055
1581
|
const now = Date.now();
|