@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
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
11
|
+
import type { StoredMessage } from '@animalabs/context-manager';
|
|
11
12
|
import type {
|
|
12
13
|
Module,
|
|
13
14
|
ModuleContext,
|
|
@@ -24,11 +25,13 @@ import type {
|
|
|
24
25
|
DiscordModuleConfig,
|
|
25
26
|
DiscordModuleState,
|
|
26
27
|
ConversationContext,
|
|
28
|
+
DiscordAttachment,
|
|
27
29
|
DiscordClientInterface,
|
|
28
30
|
DiscordMessageData,
|
|
29
31
|
DiscordMessageEvent,
|
|
30
32
|
DiscordEditEvent,
|
|
31
33
|
DiscordDeleteEvent,
|
|
34
|
+
HistoryMessage,
|
|
32
35
|
SendInput,
|
|
33
36
|
SendDMInput,
|
|
34
37
|
ReplyInput,
|
|
@@ -42,12 +45,16 @@ import type {
|
|
|
42
45
|
FetchHistoryInput,
|
|
43
46
|
} from './types.js';
|
|
44
47
|
import { formatZonedDateTime, resolveTimeZone } from '../../timezone.js';
|
|
48
|
+
import { logValue } from '../../mcpl/log-value.js';
|
|
49
|
+
import { unknownToolMessage } from '../unknown-tool.js';
|
|
50
|
+
import { DiscordRefusal } from './refusal.js';
|
|
45
51
|
|
|
46
52
|
// Re-export event types for consumers who want to handle them
|
|
47
53
|
export type { DiscordMessageEvent, DiscordEditEvent, DiscordDeleteEvent } from './types.js';
|
|
48
54
|
|
|
49
55
|
export * from './types.js';
|
|
50
56
|
export { DiscordJsClient, type DiscordJsClientConfig } from './discord-js-client.js';
|
|
57
|
+
export { DiscordRefusal } from './refusal.js';
|
|
51
58
|
|
|
52
59
|
const DEFAULT_CONFIG: Partial<DiscordModuleConfig> = {
|
|
53
60
|
handleDMs: true,
|
|
@@ -67,6 +74,14 @@ const TYPING_REFRESH_MS = 8_000;
|
|
|
67
74
|
// stuck on forever.
|
|
68
75
|
const TYPING_MAX_MS = 5 * 60_000;
|
|
69
76
|
|
|
77
|
+
// The most agents whose latest post the module keeps for their turn's
|
|
78
|
+
// thoughts (lastPosts). Past it, the entry recorded longest ago goes, which
|
|
79
|
+
// bounds the entries of agents that are gone, such as closed conversations'.
|
|
80
|
+
// An entry is wanted only until its turn's speech arrives, so one goes while
|
|
81
|
+
// still wanted only if this many other agents post in that time, and its
|
|
82
|
+
// turn's thoughts then go into no message.
|
|
83
|
+
const MAX_LAST_POSTS = 1_024;
|
|
84
|
+
|
|
70
85
|
// Trace event types that end an agent's turn (onTrace's contract) — any of
|
|
71
86
|
// these stops the typing keepalive. A turn's tool-call cycles stay inside its
|
|
72
87
|
// stream, so they come after them. inference:completed is the normal end.
|
|
@@ -95,6 +110,248 @@ const TEXT_FILE_EXTENSIONS = [
|
|
|
95
110
|
|
|
96
111
|
const CONVERSATION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes
|
|
97
112
|
|
|
113
|
+
/** A text attachment this many bytes or larger isn't read into the agent's context. */
|
|
114
|
+
const TEXT_FILE_LIMIT_BYTES = 100_000;
|
|
115
|
+
|
|
116
|
+
/** activate's and deactivate's answer before the module has started, or once it has stopped. */
|
|
117
|
+
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.";
|
|
118
|
+
|
|
119
|
+
/** What the agent reads for a text attachment whose contents couldn't be
|
|
120
|
+
* fetched: by an error, or by an answer that wasn't OK. */
|
|
121
|
+
function attachmentNotFetched(filename: string): { type: 'text'; text: string } {
|
|
122
|
+
return {
|
|
123
|
+
type: 'text',
|
|
124
|
+
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.]`,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* What the agent reads when a tool stops on an error that isn't a refusal
|
|
130
|
+
* (DiscordRefusal), such as a network failure: its outcome is unknown. A send
|
|
131
|
+
* repeated blindly can post twice, so those say how to check; the other tools
|
|
132
|
+
* are safe to repeat, since a second call changes nothing more.
|
|
133
|
+
*/
|
|
134
|
+
function unexpectedFailure(call: ToolCall, error: unknown): string {
|
|
135
|
+
const stopped = `discord--${call.name} stopped on an unexpected error: ${errorText(error)}.`;
|
|
136
|
+
const channelId = (call.input as { channelId?: unknown } | null | undefined)?.channelId;
|
|
137
|
+
switch (call.name) {
|
|
138
|
+
case 'send':
|
|
139
|
+
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.`;
|
|
140
|
+
case 'dm':
|
|
141
|
+
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.`;
|
|
142
|
+
case 'edit':
|
|
143
|
+
case 'react':
|
|
144
|
+
case 'delete':
|
|
145
|
+
case 'create_thread':
|
|
146
|
+
return `${stopped} Whether it took effect is unknown. Calling it again is safe: if it did, the second call changes nothing more.`;
|
|
147
|
+
case 'list_guilds':
|
|
148
|
+
case 'list_channels':
|
|
149
|
+
case 'fetch_history':
|
|
150
|
+
return `${stopped} It changes nothing on Discord, so you can call it again.`;
|
|
151
|
+
default:
|
|
152
|
+
return `${stopped} Calling it again is safe.`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** An error's own words, for a sentence about it. */
|
|
157
|
+
function errorText(error: unknown): string {
|
|
158
|
+
return error instanceof Error ? error.message : String(error);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** A count of seconds, for the agent's eyes. */
|
|
162
|
+
function seconds(n: number): string {
|
|
163
|
+
return `${n} second${n === 1 ? '' : 's'}`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** What a stored message reads when it has no text of its own and nothing the
|
|
167
|
+
* host added, so the agent still sees that a message was there. */
|
|
168
|
+
const EMPTY_MESSAGE = '[Empty message]';
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Metadata on a stored Discord message whose first block is its own text,
|
|
172
|
+
* '' when it has none, and every block after it the host's: images, inlined
|
|
173
|
+
* file text, attachment notes and EMPTY_MESSAGE. An edit replaces content and
|
|
174
|
+
* can't touch metadata, so the mark holds for the row's life, and it tells an
|
|
175
|
+
* edit or a sync which block is Discord's to replace. For the same reason a
|
|
176
|
+
* row stored before the mark never gets it, even once a history sync
|
|
177
|
+
* rebuilds it in this layout. The request builder drops an empty text block,
|
|
178
|
+
* as it does wherever one occurs.
|
|
179
|
+
*/
|
|
180
|
+
const TEXT_FIRST = 'discordTextFirst';
|
|
181
|
+
|
|
182
|
+
/** A message's own text as a stored row holds it: '' when it's blank. */
|
|
183
|
+
function ownText(content: string): string {
|
|
184
|
+
return content.trim() ? content : '';
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const isEmptyMarker = (block: ContentBlock): boolean => block.type === 'text' && block.text === EMPTY_MESSAGE;
|
|
188
|
+
const isBlankText = (block: ContentBlock): boolean => block.type === 'text' && !block.text.trim();
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* A text-first row's content: the message's own text, '' when it has none,
|
|
192
|
+
* then `after`, what the host added; EMPTY_MESSAGE when there's neither.
|
|
193
|
+
*/
|
|
194
|
+
function textFirst(text: string, after: ContentBlock[] = []): ContentBlock[] {
|
|
195
|
+
return [{ type: 'text', text }, ...(text || after.length > 0 ? after : [{ type: 'text' as const, text: EMPTY_MESSAGE }])];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** A text-first row's content with `text` as the message's own text, in
|
|
199
|
+
* place of its first block, keeping what the host added after it. */
|
|
200
|
+
function withOwnText(content: ContentBlock[], text: string): ContentBlock[] {
|
|
201
|
+
return textFirst(ownText(text), content.slice(1).filter((block) => !isEmptyMarker(block)));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* The content a stored Discord message should hold once Discord's text for it
|
|
206
|
+
* is `discordText`, or null when it holds that already.
|
|
207
|
+
*
|
|
208
|
+
* A text-first row keeps every block the host added and replaces only its own
|
|
209
|
+
* text; EMPTY_MESSAGE goes once there is text, and comes back when nothing is
|
|
210
|
+
* left. A row stored before that layout held its own text first when it had
|
|
211
|
+
* any, but a message without text began with what the host added, which may
|
|
212
|
+
* be text, a file's contents or a note, so its first block can't be told
|
|
213
|
+
* apart. Such a row is left alone unless Discord's text is there and differs
|
|
214
|
+
* from its first block, and then that text takes the first block's place
|
|
215
|
+
* when it's text, and goes before it when it isn't, and EMPTY_MESSAGE goes.
|
|
216
|
+
* So a message without text, stored beginning with a file's contents or a
|
|
217
|
+
* note, loses that block. The history sync, which has the message's
|
|
218
|
+
* attachments, rebuilds such a row instead (needsRebuild).
|
|
219
|
+
*/
|
|
220
|
+
function contentForText(stored: StoredMessage, discordText: string): ContentBlock[] | null {
|
|
221
|
+
const text = ownText(discordText);
|
|
222
|
+
const [first, ...rest] = stored.content;
|
|
223
|
+
if (stored.metadata?.[TEXT_FIRST] === true) {
|
|
224
|
+
if (first?.type === 'text' && first.text === text) return null;
|
|
225
|
+
return withOwnText(stored.content, text);
|
|
226
|
+
}
|
|
227
|
+
if (!text || (first?.type === 'text' && first.text === discordText)) return null;
|
|
228
|
+
return [{ type: 'text', text: discordText }, ...(first?.type === 'text' ? rest : stored.content).filter((block) => !isEmptyMarker(block))];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Whether a stored row lacks or holds more than the live conversion gives
|
|
233
|
+
* Discord's `message` now, its own text aside, so the history sync stores it
|
|
234
|
+
* through that conversion again. The conversion gives each attachment one
|
|
235
|
+
* block: an image, a file's text, or a note when it isn't fetched or isn't
|
|
236
|
+
* read. So a text-first row holds one block per attachment after its text,
|
|
237
|
+
* EMPTY_MESSAGE aside, which the text's rule keeps. A row stored before that
|
|
238
|
+
* layout held its text, when it had any, then those blocks, or EMPTY_MESSAGE
|
|
239
|
+
* alone; one that doesn't was blanked or stripped by an earlier edit or sync,
|
|
240
|
+
* or its message gained or lost text, or lost an attachment. Its first block,
|
|
241
|
+
* when it is the message's own text, counts as that text, even when the text
|
|
242
|
+
* reads as EMPTY_MESSAGE. For a message without text of its own, a first
|
|
243
|
+
* block that's text may be what the host added or a caption the message has
|
|
244
|
+
* since lost, which no count tells apart, so that row is stored again; it
|
|
245
|
+
* then begins with its own '' text. One whose text alone changed is left to
|
|
246
|
+
* the text's rule, which keeps the blocks after it. History without
|
|
247
|
+
* attachments can't be compared, so it rebuilds nothing.
|
|
248
|
+
*/
|
|
249
|
+
function needsRebuild(stored: StoredMessage, message: HistoryMessage): boolean {
|
|
250
|
+
if (message.attachments === undefined) return false;
|
|
251
|
+
const attachments = message.attachments.length;
|
|
252
|
+
const held = (blocks: ContentBlock[]) => blocks.filter((block) => !isBlankText(block) && !isEmptyMarker(block)).length;
|
|
253
|
+
if (stored.metadata?.[TEXT_FIRST] === true) return held(stored.content.slice(1)) !== attachments;
|
|
254
|
+
const own = ownText(message.content);
|
|
255
|
+
const [first, ...rest] = stored.content;
|
|
256
|
+
if (!own && first?.type === 'text' && !isBlankText(first) && !isEmptyMarker(first)) return true;
|
|
257
|
+
const expected = (own ? 1 : 0) + attachments;
|
|
258
|
+
const counted = own && first?.type === 'text' && first.text === own ? 1 + held(rest) : held(stored.content);
|
|
259
|
+
return counted !== expected || (expected === 0 && !stored.content.some(isEmptyMarker));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Whether a row holds non-blank text besides its message's own text now,
|
|
264
|
+
* which may be what a file gave. A marked row whose message's text changed
|
|
265
|
+
* counts its old text here too, which can delay its rebuild until a fetch
|
|
266
|
+
* succeeds but never loses text.
|
|
267
|
+
*/
|
|
268
|
+
function holdsOtherText(stored: StoredMessage, message: HistoryMessage): boolean {
|
|
269
|
+
const own = ownText(message.content);
|
|
270
|
+
const [first, ...rest] = stored.content;
|
|
271
|
+
const others = own && first?.type === 'text' && first.text === own ? rest : stored.content;
|
|
272
|
+
return others.some((block) => block.type === 'text' && !isBlankText(block) && !isEmptyMarker(block));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Whether a message's conversion holds, after the message's own text, the
|
|
276
|
+
* note for an attachment of its whose text wasn't fetched. */
|
|
277
|
+
function holdsNotFetched(content: ContentBlock[], message: HistoryMessage): boolean {
|
|
278
|
+
const notes = new Set((message.attachments ?? []).map((attachment) => attachmentNotFetched(attachment.filename).text));
|
|
279
|
+
return content.slice(1).some((block) => block.type === 'text' && notes.has(block.text));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/** A URL without its query or fragment: Discord's CDN signs an attachment's
|
|
283
|
+
* URL afresh in each payload, so only its path names the attachment. */
|
|
284
|
+
function urlPath(url: string): string {
|
|
285
|
+
return url.split(/[?#]/)[0] ?? url;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Whether a block the host added for an attachment stands for none of
|
|
290
|
+
* `attachments`, matched as the live conversion writes it: an image by its
|
|
291
|
+
* URL, a file's text by its header, and a note by the filename it opens
|
|
292
|
+
* with, as every note for an attachment does, whatever its words after it.
|
|
293
|
+
* A block that stands for no attachment, such as EMPTY_MESSAGE, never does.
|
|
294
|
+
*/
|
|
295
|
+
function forGoneAttachment(block: ContentBlock, attachments: readonly DiscordAttachment[]): boolean {
|
|
296
|
+
if (block.type === 'image') {
|
|
297
|
+
const source = (block as { source?: { type?: string; url?: unknown } }).source;
|
|
298
|
+
if (source?.type !== 'url' || typeof source.url !== 'string') return false;
|
|
299
|
+
const path = urlPath(source.url);
|
|
300
|
+
return !attachments.some((attachment) => urlPath(attachment.url) === path);
|
|
301
|
+
}
|
|
302
|
+
if (block.type !== 'text') return false;
|
|
303
|
+
const { text } = block;
|
|
304
|
+
const opening = text.startsWith('\n--- File: ') ? (filename: string) => `\n--- File: ${filename} ---\n`
|
|
305
|
+
: text.startsWith('[Attachment: ') ? (filename: string) => `[Attachment: ${filename} `
|
|
306
|
+
: null;
|
|
307
|
+
return opening !== null && !attachments.some((attachment) => text.startsWith(opening(attachment.filename)));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* A text-first row's content without the blocks the host added for
|
|
312
|
+
* attachments its message no longer has, or `content` itself when none is
|
|
313
|
+
* gone. A block stays while an attachment it may stand for remains, so of two
|
|
314
|
+
* files of one name, removing one keeps both blocks, and a block that names
|
|
315
|
+
* no attachment stays. EMPTY_MESSAGE comes back once nothing is left.
|
|
316
|
+
*/
|
|
317
|
+
function withoutGoneAttachments(content: ContentBlock[], attachments: readonly DiscordAttachment[]): ContentBlock[] {
|
|
318
|
+
const [first, ...rest] = content;
|
|
319
|
+
if (first?.type !== 'text') return content;
|
|
320
|
+
const kept = rest.filter((block) => !forGoneAttachment(block, attachments));
|
|
321
|
+
if (kept.length === rest.length) return content;
|
|
322
|
+
return textFirst(first.text, kept);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** The ids to remove for a message's rows: each row once, and a body stored
|
|
326
|
+
* in shards once, since removing one shard takes out its whole body. */
|
|
327
|
+
function oncePerBody(rows: readonly StoredMessage[]): string[] {
|
|
328
|
+
const bodies = new Set<string>();
|
|
329
|
+
const ids: string[] = [];
|
|
330
|
+
for (const row of rows) {
|
|
331
|
+
if (row.bodyGroupId) {
|
|
332
|
+
if (bodies.has(row.bodyGroupId)) continue;
|
|
333
|
+
bodies.add(row.bodyGroupId);
|
|
334
|
+
}
|
|
335
|
+
ids.push(row.id);
|
|
336
|
+
}
|
|
337
|
+
return ids;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** A live create on its way to the store (DiscordModule.creating). */
|
|
341
|
+
interface LiveCreate {
|
|
342
|
+
/** converting: the live path fetches its attachments; queued: its event
|
|
343
|
+
* waits in the queue; writing: onProcess answered it, and each Discord
|
|
344
|
+
* event after it looks for the message's row, and ends the entry once it
|
|
345
|
+
* shows. */
|
|
346
|
+
phase: 'converting' | 'queued' | 'writing';
|
|
347
|
+
/** A delete for the message came before its create was handled, so no row
|
|
348
|
+
* comes, and the entry waits for that delete to be handled. */
|
|
349
|
+
deleted: boolean;
|
|
350
|
+
/** The latest edit's text and attachments, from while it converted. */
|
|
351
|
+
text?: string;
|
|
352
|
+
attachments?: DiscordAttachment[];
|
|
353
|
+
}
|
|
354
|
+
|
|
98
355
|
/**
|
|
99
356
|
* Discord module for the agent framework.
|
|
100
357
|
*/
|
|
@@ -115,8 +372,41 @@ export class DiscordModule implements Module {
|
|
|
115
372
|
// Track current reply context per agent
|
|
116
373
|
private replyContexts: Map<string, ConversationContext> = new Map();
|
|
117
374
|
|
|
118
|
-
//
|
|
119
|
-
|
|
375
|
+
// Where a tool turn's thoughts go (onAgentSpeech edits them in as a
|
|
376
|
+
// spoiler): each agent's latest message posted in a turn, with that turn's
|
|
377
|
+
// id (notePost). A `send` or `reply` records the message its client call
|
|
378
|
+
// returns, and so does the module's own speech. Thoughts go into it only
|
|
379
|
+
// when their speech ends the same turn, and taking them removes it. So
|
|
380
|
+
// neither another agent's message nor one from an earlier turn takes them,
|
|
381
|
+
// and an attempt in the turn that is refused or throws leaves the turn's own
|
|
382
|
+
// post in place. Turn ids increase, so an earlier turn's post that finishes
|
|
383
|
+
// after a later turn's leaves the later one in place. A DM records nothing,
|
|
384
|
+
// since its channel isn't known here, and neither does a post whose call or
|
|
385
|
+
// speech carries no turn. An entry goes when its thoughts are taken, when
|
|
386
|
+
// the agent's next post replaces it, or past MAX_LAST_POSTS agents.
|
|
387
|
+
private lastPosts: Map<string, { turnId: number; channelId: string; messageId: string }> = new Map();
|
|
388
|
+
|
|
389
|
+
// One set per running history sync: the Discord messages the live path
|
|
390
|
+
// stored, edited or was told were deleted since that sync began. What the
|
|
391
|
+
// live path saw is newer than what the sync fetched, so the sync leaves
|
|
392
|
+
// those messages alone.
|
|
393
|
+
private readonly liveDuringSync = new Set<Set<string>>();
|
|
394
|
+
|
|
395
|
+
// The Discord messages whose live create is on its way to the store: from
|
|
396
|
+
// the moment the live path takes one up, through its conversion and the
|
|
397
|
+
// event queue, until a query shows the row onProcess answered with, stored
|
|
398
|
+
// or held. The framework holds a module's writes while the agent's turn is
|
|
399
|
+
// alive or the host is quiesced, and queryMessages shows a held write's
|
|
400
|
+
// row, marked held, so the next Discord event finds it either way and ends
|
|
401
|
+
// its entry. A history sync stores none of these messages, so no message is
|
|
402
|
+
// stored twice. A delete before the create is handled drops it: nothing is
|
|
403
|
+
// stored and nobody is woken. Any message's entry ends when its delete is
|
|
404
|
+
// handled, since a sync running then skips the message (noteLive) and a
|
|
405
|
+
// later one doesn't fetch it. An edit while it converts is folded into it.
|
|
406
|
+
// A create whose answer the framework fails to write or hold shows no row,
|
|
407
|
+
// and keeps its entry, and the sync off its message, until its delete is
|
|
408
|
+
// handled or the module stops.
|
|
409
|
+
private readonly creating = new Map<string, LiveCreate>();
|
|
120
410
|
|
|
121
411
|
// Typing keepalive: agentName -> { interval, deadline }. Started on
|
|
122
412
|
// inference:started, stopped on the turn's terminal trace event. Discord's
|
|
@@ -165,7 +455,7 @@ export class DiscordModule implements Module {
|
|
|
165
455
|
|
|
166
456
|
// Set up Discord event handlers
|
|
167
457
|
this.client.onMessage((message) => this.handleDiscordMessage(message));
|
|
168
|
-
this.client.onMessageEdit((id, content) => this.handleDiscordMessageEdit(id, content));
|
|
458
|
+
this.client.onMessageEdit((id, content, attachments) => this.handleDiscordMessageEdit(id, content, attachments));
|
|
169
459
|
this.client.onMessageDelete((id) => this.handleDiscordMessageDelete(id));
|
|
170
460
|
|
|
171
461
|
// Set up ready handler for history sync on (re)connect
|
|
@@ -204,8 +494,14 @@ export class DiscordModule implements Module {
|
|
|
204
494
|
}
|
|
205
495
|
this.typingLoops.clear();
|
|
206
496
|
|
|
207
|
-
|
|
497
|
+
// From here the module writes nothing, though the framework may dispatch
|
|
498
|
+
// to it until stop() resolves: the live path takes nothing up, a create
|
|
499
|
+
// still converting finds the context gone, and onProcess answers nothing.
|
|
500
|
+
// The record of creates on their way goes with the context.
|
|
208
501
|
this.ctx = null;
|
|
502
|
+
this.creating.clear();
|
|
503
|
+
|
|
504
|
+
await this.client.disconnect();
|
|
209
505
|
}
|
|
210
506
|
|
|
211
507
|
/**
|
|
@@ -395,7 +691,7 @@ export class DiscordModule implements Module {
|
|
|
395
691
|
},
|
|
396
692
|
{
|
|
397
693
|
name: 'list_channels',
|
|
398
|
-
description: 'List
|
|
694
|
+
description: 'List the channels in a Discord server, with their ids. Threads aren\'t listed.',
|
|
399
695
|
inputSchema: {
|
|
400
696
|
type: 'object',
|
|
401
697
|
properties: {
|
|
@@ -406,7 +702,7 @@ export class DiscordModule implements Module {
|
|
|
406
702
|
},
|
|
407
703
|
{
|
|
408
704
|
name: 'fetch_history',
|
|
409
|
-
description: 'Fetch recent message history from a channel',
|
|
705
|
+
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.',
|
|
410
706
|
inputSchema: {
|
|
411
707
|
type: 'object',
|
|
412
708
|
properties: {
|
|
@@ -424,11 +720,11 @@ export class DiscordModule implements Module {
|
|
|
424
720
|
try {
|
|
425
721
|
switch (call.name) {
|
|
426
722
|
case 'send':
|
|
427
|
-
return await this.handleSend(call.input as SendInput);
|
|
723
|
+
return await this.handleSend(call.input as SendInput, call);
|
|
428
724
|
case 'dm':
|
|
429
725
|
return await this.handleDM(call.input as SendDMInput);
|
|
430
726
|
case 'reply':
|
|
431
|
-
return await this.handleReply(call.input as ReplyInput);
|
|
727
|
+
return await this.handleReply(call.input as ReplyInput, call);
|
|
432
728
|
case 'react':
|
|
433
729
|
return await this.handleReact(call.input as ReactInput);
|
|
434
730
|
case 'edit':
|
|
@@ -440,7 +736,8 @@ export class DiscordModule implements Module {
|
|
|
440
736
|
case 'set_reply_context':
|
|
441
737
|
return await this.handleSetReplyContext(call.input as SetReplyContextInput);
|
|
442
738
|
case 'activate':
|
|
443
|
-
|
|
739
|
+
// A call with no input at all activates with the defaults.
|
|
740
|
+
return await this.handleActivate((call.input ?? {}) as { additive?: boolean });
|
|
444
741
|
case 'deactivate':
|
|
445
742
|
return await this.handleDeactivate();
|
|
446
743
|
case 'list_guilds':
|
|
@@ -450,21 +747,36 @@ export class DiscordModule implements Module {
|
|
|
450
747
|
case 'fetch_history':
|
|
451
748
|
return await this.handleFetchHistory(call.input as FetchHistoryInput);
|
|
452
749
|
default:
|
|
453
|
-
return { success: false, error:
|
|
750
|
+
return { success: false, error: unknownToolMessage(this.name, call.name, this.getTools()), isError: true };
|
|
454
751
|
}
|
|
455
752
|
} catch (error) {
|
|
753
|
+
// A refusal says what was refused and what to do instead; anything else
|
|
754
|
+
// is unexpected, and its outcome is worded by the tool.
|
|
456
755
|
return {
|
|
457
756
|
success: false,
|
|
458
|
-
error: error instanceof
|
|
757
|
+
error: error instanceof DiscordRefusal ? error.message : unexpectedFailure(call, error),
|
|
459
758
|
isError: true,
|
|
460
759
|
};
|
|
461
760
|
}
|
|
462
761
|
}
|
|
463
762
|
|
|
464
763
|
async onProcess(event: ProcessEvent, state: ProcessState): Promise<EventResponse> {
|
|
764
|
+
// A stopped module answers nothing (stop()).
|
|
765
|
+
if (!this.ctx) return {};
|
|
766
|
+
|
|
767
|
+
// Each Discord event ends the entries whose row a query shows now, stored
|
|
768
|
+
// or held.
|
|
769
|
+
if (event.type.startsWith('discord:')) {
|
|
770
|
+
for (const [id, live] of this.creating) if (live.phase === 'writing' && this.storedRow(id)) this.creating.delete(id);
|
|
771
|
+
}
|
|
772
|
+
|
|
465
773
|
// Handle new Discord message
|
|
466
774
|
if (event.type === 'discord:message') {
|
|
467
775
|
const msg = event as DiscordMessageEvent;
|
|
776
|
+
const live = this.creating.get(msg.discordMessageId);
|
|
777
|
+
// Deleted while its create waited in the queue: nothing is stored, and
|
|
778
|
+
// nobody is woken by a message its author took back.
|
|
779
|
+
if (live?.deleted) return {};
|
|
468
780
|
const currentState = state.getState<DiscordModuleState>() ?? {
|
|
469
781
|
connectedGuilds: [],
|
|
470
782
|
conversations: {},
|
|
@@ -509,22 +821,34 @@ export class DiscordModule implements Module {
|
|
|
509
821
|
});
|
|
510
822
|
}
|
|
511
823
|
|
|
512
|
-
//
|
|
513
|
-
|
|
824
|
+
// Built from the message and its attachments, or from its text alone
|
|
825
|
+
// when an event carries no blocks; text first, as TEXT_FIRST marks.
|
|
826
|
+
const contentBlocks = msg.contentBlocks ?? textFirst(ownText(msg.content));
|
|
827
|
+
this.noteLive(msg.discordMessageId);
|
|
828
|
+
|
|
829
|
+
// A message a history sync wrote first, stored or held, keeps that row:
|
|
830
|
+
// the create adds none, and still sets the reply context and asks for
|
|
831
|
+
// inference, since the message arrived live. Either way the entry waits
|
|
832
|
+
// until a Discord event finds the message's row.
|
|
833
|
+
const stored = this.storedRow(msg.discordMessageId);
|
|
834
|
+
if (live) live.phase = 'writing';
|
|
514
835
|
|
|
515
836
|
// Return declarative response - framework applies all writes atomically
|
|
516
837
|
return {
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
838
|
+
...(stored ? {} : {
|
|
839
|
+
addMessages: [
|
|
840
|
+
{
|
|
841
|
+
participant: msg.authorName,
|
|
842
|
+
content: contentBlocks,
|
|
843
|
+
metadata: {
|
|
844
|
+
external: { source: 'discord', id: msg.discordMessageId },
|
|
845
|
+
channelId: msg.channelId,
|
|
846
|
+
timestamp: msg.timestamp,
|
|
847
|
+
[TEXT_FIRST]: true,
|
|
848
|
+
},
|
|
525
849
|
},
|
|
526
|
-
|
|
527
|
-
|
|
850
|
+
],
|
|
851
|
+
}),
|
|
528
852
|
stateUpdate: newState,
|
|
529
853
|
requestInference: msg.shouldTriggerInference ?? true,
|
|
530
854
|
};
|
|
@@ -533,29 +857,49 @@ export class DiscordModule implements Module {
|
|
|
533
857
|
// Handle Discord message edit
|
|
534
858
|
if (event.type === 'discord:edit') {
|
|
535
859
|
const edit = event as DiscordEditEvent;
|
|
536
|
-
|
|
537
|
-
|
|
860
|
+
// The message's row, not only its id: an edit changes the message's own
|
|
861
|
+
// text and keeps what the host added beside it, but for the blocks of
|
|
862
|
+
// attachments the edit took away, when the client passed the edit's
|
|
863
|
+
// attachments and the row is text-first.
|
|
864
|
+
// An edit that comes while the live path converts the message's create
|
|
865
|
+
// is folded into that create (handleDiscordMessageEdit). Any other edit
|
|
866
|
+
// to a message with no row, stored or held, is dropped: a history sync
|
|
867
|
+
// running now stores the text it fetched, and the next sync's text rule
|
|
868
|
+
// changes it. A row stored in shards can't be edited one shard at a
|
|
869
|
+
// time, so it keeps its text, and the host's log says so. Only an edit
|
|
870
|
+
// that writes marks the message live, so an update that changes nothing
|
|
871
|
+
// leaves a running sync free to rewrite the row.
|
|
872
|
+
const stored = this.storedRow(edit.discordMessageId);
|
|
873
|
+
if (!stored) return {};
|
|
874
|
+
if (stored.bodyGroupId) {
|
|
875
|
+
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`);
|
|
538
876
|
return {};
|
|
539
877
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
};
|
|
878
|
+
const texted = contentForText(stored, edit.newContent);
|
|
879
|
+
const content = edit.attachments && stored.metadata?.[TEXT_FIRST] === true
|
|
880
|
+
? withoutGoneAttachments(texted ?? stored.content, edit.attachments)
|
|
881
|
+
: texted;
|
|
882
|
+
if (!content || content === stored.content) return {};
|
|
883
|
+
this.noteLive(edit.discordMessageId);
|
|
884
|
+
return { editMessages: [{ messageId: stored.id, content }] };
|
|
548
885
|
}
|
|
549
886
|
|
|
550
887
|
// Handle Discord message delete
|
|
551
888
|
if (event.type === 'discord:delete') {
|
|
552
889
|
const del = event as DiscordDeleteEvent;
|
|
553
|
-
|
|
554
|
-
|
|
890
|
+
// Every row of the message goes, so one stored twice doesn't outlive
|
|
891
|
+
// its delete as a copy. A row stored in shards is removed whole: the
|
|
892
|
+
// framework takes out the body a shard belongs to. The message is the
|
|
893
|
+
// live path's even when no row is found, so a running sync doesn't
|
|
894
|
+
// store what its fetch may still hold, and that ends its entry.
|
|
895
|
+
this.noteLive(del.discordMessageId);
|
|
896
|
+
this.creating.delete(del.discordMessageId);
|
|
897
|
+
const rows = this.storedRows(del.discordMessageId);
|
|
898
|
+
if (rows.length === 0) {
|
|
555
899
|
return {};
|
|
556
900
|
}
|
|
557
901
|
return {
|
|
558
|
-
removeMessages:
|
|
902
|
+
removeMessages: oncePerBody(rows),
|
|
559
903
|
};
|
|
560
904
|
}
|
|
561
905
|
|
|
@@ -570,13 +914,15 @@ export class DiscordModule implements Module {
|
|
|
570
914
|
content: ContentBlock[],
|
|
571
915
|
context: SpeechContext
|
|
572
916
|
): Promise<void> {
|
|
573
|
-
// If thoughts are present and
|
|
574
|
-
// to prepend the thoughts as spoiler text.
|
|
575
|
-
|
|
917
|
+
// If thoughts are present and this agent posted a message in the turn
|
|
918
|
+
// they end (lastPosts), edit it to prepend the thoughts as spoiler text.
|
|
919
|
+
const post = this.lastPosts.get(agentName);
|
|
920
|
+
if (context.thoughts && context.thoughts.length > 0 && post && post.turnId === context.turnId) {
|
|
921
|
+
this.lastPosts.delete(agentName);
|
|
576
922
|
const thoughtText = this.extractText(context.thoughts);
|
|
577
923
|
if (thoughtText) {
|
|
578
924
|
try {
|
|
579
|
-
const { channelId, messageId } =
|
|
925
|
+
const { channelId, messageId } = post;
|
|
580
926
|
// Fetch the current message content via the client isn't available,
|
|
581
927
|
// but we know the tool just sent it — reconstruct by editing with
|
|
582
928
|
// spoiler-wrapped thoughts prepended.
|
|
@@ -588,7 +934,6 @@ export class DiscordModule implements Module {
|
|
|
588
934
|
console.warn('Discord: Failed to edit thoughts into message:', err);
|
|
589
935
|
}
|
|
590
936
|
}
|
|
591
|
-
this.lastSentMessage = null;
|
|
592
937
|
}
|
|
593
938
|
|
|
594
939
|
// Get reply context for this agent
|
|
@@ -606,7 +951,7 @@ export class DiscordModule implements Module {
|
|
|
606
951
|
}
|
|
607
952
|
|
|
608
953
|
// Check rate limit
|
|
609
|
-
if (
|
|
954
|
+
if (this.rateLimitWait(replyCtx.channelId) > 0) {
|
|
610
955
|
console.warn(`Discord: Rate limited for channel ${replyCtx.channelId}`);
|
|
611
956
|
return;
|
|
612
957
|
}
|
|
@@ -617,8 +962,8 @@ export class DiscordModule implements Module {
|
|
|
617
962
|
replyTo: replyCtx.replyToMessageId,
|
|
618
963
|
});
|
|
619
964
|
|
|
620
|
-
// Track for thought-editing (in case of chained speech)
|
|
621
|
-
this.
|
|
965
|
+
// Track for thought-editing (in case of chained speech in the same turn)
|
|
966
|
+
this.notePost(agentName, context.turnId, targetChannel, result.messageId);
|
|
622
967
|
|
|
623
968
|
// Clear reply-to after sending (don't keep replying to same message)
|
|
624
969
|
replyCtx.replyToMessageId = undefined;
|
|
@@ -723,136 +1068,201 @@ export class DiscordModule implements Module {
|
|
|
723
1068
|
|
|
724
1069
|
/**
|
|
725
1070
|
* Sync history for a specific channel - compare with Context Manager and update.
|
|
726
|
-
* Uses queryMessages() to enumerate
|
|
1071
|
+
* Uses queryMessages() to enumerate the channel's rows, a held write's among
|
|
1072
|
+
* them, for proper edit/delete detection.
|
|
1073
|
+
*
|
|
1074
|
+
* The live path can store, edit or remove a row whenever the sync awaits.
|
|
1075
|
+
* So the sync fetches history and runs the live conversion it needs first,
|
|
1076
|
+
* then reads the store again and writes with no await between that read
|
|
1077
|
+
* and its writes. It leaves alone any message the live path stored,
|
|
1078
|
+
* changed or was told was deleted while it ran (liveDuringSync), an update
|
|
1079
|
+
* that changed nothing aside. A live edit to a message nobody had stored
|
|
1080
|
+
* yet is dropped, so a message the sync stores holds the text it fetched
|
|
1081
|
+
* until the next sync's text rule changes it. The framework writes an
|
|
1082
|
+
* event's response only once every module has processed the event, so an
|
|
1083
|
+
* event processed before the sync began can still be written after the
|
|
1084
|
+
* sync's own writes, which this doesn't cover.
|
|
727
1085
|
*/
|
|
728
1086
|
private async syncChannelHistory(channelId: string): Promise<{
|
|
729
1087
|
newMessages: number;
|
|
730
1088
|
editedMessages: number;
|
|
731
1089
|
deletedMessages: number;
|
|
732
1090
|
}> {
|
|
733
|
-
|
|
734
|
-
|
|
1091
|
+
const unchanged = { newMessages: 0, editedMessages: 0, deletedMessages: 0 };
|
|
1092
|
+
const ctx = this.ctx;
|
|
1093
|
+
if (!ctx) {
|
|
1094
|
+
return unchanged;
|
|
735
1095
|
}
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
sourceTimestamp: typeof msg.metadata?.timestamp === 'number'
|
|
772
|
-
? msg.metadata.timestamp
|
|
773
|
-
: undefined,
|
|
774
|
-
});
|
|
1096
|
+
const live = new Set<string>();
|
|
1097
|
+
this.liveDuringSync.add(live);
|
|
1098
|
+
try {
|
|
1099
|
+
const scrollback = this.config.historyScrollback ?? 50;
|
|
1100
|
+
// The rows stored as the sync begins. One that is gone by the time it
|
|
1101
|
+
// writes was removed meanwhile, and isn't the sync's to bring back.
|
|
1102
|
+
const before = this.storedByDiscordId(ctx, channelId);
|
|
1103
|
+
|
|
1104
|
+
// Fetch recent history from Discord. If the module stopped or restarted
|
|
1105
|
+
// meanwhile, this sync's context is gone and it writes nothing.
|
|
1106
|
+
const discordMessages = await this.client.fetchHistory(channelId, { limit: scrollback });
|
|
1107
|
+
if (this.ctx !== ctx) return unchanged;
|
|
1108
|
+
|
|
1109
|
+
// Discord's ids, for delete detection inside the fetched window.
|
|
1110
|
+
const discordIds = new Set(discordMessages.map((msg) => msg.id));
|
|
1111
|
+
|
|
1112
|
+
// Discord history is newest-first, while addMessage appends to the stored
|
|
1113
|
+
// conversation. Process oldest-first so missed messages retain chronology.
|
|
1114
|
+
const chronologicalMessages = [...discordMessages].reverse();
|
|
1115
|
+
|
|
1116
|
+
// The live conversion, which fetches attachments, for a message the
|
|
1117
|
+
// store lacks and for a row that doesn't hold what the conversion gives
|
|
1118
|
+
// the message now (needsRebuild). A row stored in shards can't be
|
|
1119
|
+
// edited one shard at a time, so it's left as it is.
|
|
1120
|
+
const converted = new Map<string, ContentBlock[]>();
|
|
1121
|
+
for (const discordMsg of chronologicalMessages) {
|
|
1122
|
+
const stored = before.get(discordMsg.id)?.[0];
|
|
1123
|
+
if (stored && (stored.bodyGroupId || !needsRebuild(stored, discordMsg))) continue;
|
|
1124
|
+
const content = await this.convertMessageToContent(discordMsg);
|
|
1125
|
+
if (this.ctx !== ctx) return unchanged;
|
|
1126
|
+
// A failed fetch never costs a row the text it holds: when a file
|
|
1127
|
+
// wasn't fetched, a row holding other text, which may be that file's,
|
|
1128
|
+
// keeps what it holds, and the next sync tries again.
|
|
1129
|
+
if (stored && holdsNotFetched(content, discordMsg) && holdsOtherText(stored, discordMsg)) continue;
|
|
1130
|
+
converted.set(discordMsg.id, content);
|
|
775
1131
|
}
|
|
776
|
-
}
|
|
777
1132
|
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1133
|
+
// From here on nothing awaits, so the rows read now are the rows written.
|
|
1134
|
+
const storedMessageMap = this.storedByDiscordId(ctx, channelId);
|
|
1135
|
+
let newMessages = 0;
|
|
1136
|
+
let editedMessages = 0;
|
|
1137
|
+
let deletedMessages = 0;
|
|
1138
|
+
for (const discordMsg of chronologicalMessages) {
|
|
1139
|
+
if (live.has(discordMsg.id)) continue;
|
|
1140
|
+
const stored = storedMessageMap.get(discordMsg.id)?.[0];
|
|
1141
|
+
|
|
1142
|
+
if (!stored) {
|
|
1143
|
+
// A message we haven't stored. One that was stored when the sync
|
|
1144
|
+
// began, and isn't now, was removed meanwhile: not the sync's to
|
|
1145
|
+
// bring back. One the live path is storing (creating) is the live
|
|
1146
|
+
// path's, so it isn't stored twice.
|
|
1147
|
+
const content = converted.get(discordMsg.id);
|
|
1148
|
+
if (!content || before.has(discordMsg.id) || this.creating.has(discordMsg.id)) continue;
|
|
1149
|
+
// For bot's own messages (from previous sessions), use the agent name
|
|
1150
|
+
// This ensures continuity of conversation history
|
|
1151
|
+
const isBotMessage = discordMsg.authorId === this.state.botUserId;
|
|
1152
|
+
const agentName = ctx.getAgents()[0]?.name;
|
|
1153
|
+
const participantName = isBotMessage
|
|
1154
|
+
? (agentName ?? discordMsg.authorName)
|
|
1155
|
+
: discordMsg.authorName;
|
|
1156
|
+
ctx.addMessage(
|
|
1157
|
+
participantName,
|
|
1158
|
+
content,
|
|
1159
|
+
{
|
|
1160
|
+
external: { source: 'discord', id: discordMsg.id },
|
|
1161
|
+
channelId,
|
|
1162
|
+
timestamp: discordMsg.timestamp.getTime(),
|
|
1163
|
+
isOwnMessage: isBotMessage, // Mark as our own for reference
|
|
1164
|
+
[TEXT_FIRST]: true,
|
|
1165
|
+
}
|
|
1166
|
+
);
|
|
1167
|
+
newMessages++;
|
|
1168
|
+
} else if (!stored.bodyGroupId) {
|
|
1169
|
+
// We have this message - check whether it changed (offline edit detection)
|
|
1170
|
+
const content = needsRebuild(stored, discordMsg)
|
|
1171
|
+
? converted.get(discordMsg.id)
|
|
1172
|
+
: contentForText(stored, discordMsg.content);
|
|
1173
|
+
if (content) {
|
|
1174
|
+
ctx.editMessage(stored.id, content);
|
|
1175
|
+
editedMessages++;
|
|
806
1176
|
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Check for deletes only inside the history we actually fetched. A short
|
|
1181
|
+
// response covers complete available history; a full page has an older
|
|
1182
|
+
// boundary whose tied millisecond must be preserved.
|
|
1183
|
+
const fetchedCompleteHistory = discordMessages.length < scrollback;
|
|
1184
|
+
const oldestFetchedTimestamp = discordMessages.length > 0
|
|
1185
|
+
? Math.min(...discordMessages.map((msg) => msg.timestamp.getTime()))
|
|
1186
|
+
: undefined;
|
|
1187
|
+
for (const [externalId, rows] of storedMessageMap) {
|
|
1188
|
+
const stored = rows[0]!;
|
|
1189
|
+
const sourceTimestamp = typeof stored.metadata?.timestamp === 'number'
|
|
1190
|
+
? stored.metadata.timestamp
|
|
1191
|
+
: undefined;
|
|
1192
|
+
const isInsideFetchedWindow = fetchedCompleteHistory || (
|
|
1193
|
+
sourceTimestamp !== undefined &&
|
|
1194
|
+
oldestFetchedTimestamp !== undefined &&
|
|
1195
|
+
// Discord timestamps are millisecond-precise, so an older message just
|
|
1196
|
+
// beyond the page can tie the oldest fetched timestamp. Preserve ties
|
|
1197
|
+
// rather than guessing that an unseen boundary message was deleted.
|
|
1198
|
+
sourceTimestamp > oldestFetchedTimestamp
|
|
807
1199
|
);
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1200
|
+
if (isInsideFetchedWindow && !discordIds.has(externalId) && !live.has(externalId)) {
|
|
1201
|
+
// Message exists in our store but not in Discord - it was deleted
|
|
1202
|
+
// Note: This only catches deletes within the scrollback window.
|
|
1203
|
+
// A message the live path wrote while the sync ran is newer than
|
|
1204
|
+
// the fetch, so it stays. Every row of the message goes, and one
|
|
1205
|
+
// shard's id takes out a body whole.
|
|
1206
|
+
for (const id of oncePerBody(rows)) ctx.removeMessage(id);
|
|
1207
|
+
deletedMessages++;
|
|
816
1208
|
}
|
|
817
1209
|
}
|
|
818
|
-
}
|
|
819
1210
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
? Math.min(...discordMessages.map((msg) => msg.timestamp.getTime()))
|
|
826
|
-
: undefined;
|
|
827
|
-
for (const [externalId, stored] of storedMessageMap) {
|
|
828
|
-
const isInsideFetchedWindow = fetchedCompleteHistory || (
|
|
829
|
-
stored.sourceTimestamp !== undefined &&
|
|
830
|
-
oldestFetchedTimestamp !== undefined &&
|
|
831
|
-
// Discord timestamps are millisecond-precise, so an older message just
|
|
832
|
-
// beyond the page can tie the oldest fetched timestamp. Preserve ties
|
|
833
|
-
// rather than guessing that an unseen boundary message was deleted.
|
|
834
|
-
stored.sourceTimestamp > oldestFetchedTimestamp
|
|
835
|
-
);
|
|
836
|
-
if (isInsideFetchedWindow && !discordMessageMap.has(externalId)) {
|
|
837
|
-
// Message exists in our store but not in Discord - it was deleted
|
|
838
|
-
// Note: This only catches deletes within the scrollback window
|
|
839
|
-
this.ctx.removeMessage(stored.internalId);
|
|
840
|
-
deletedMessages++;
|
|
1211
|
+
// Update last read
|
|
1212
|
+
if (discordMessages.length > 0) {
|
|
1213
|
+
// Messages are typically newest first
|
|
1214
|
+
const newestId = discordMessages[0].id;
|
|
1215
|
+
this.state.lastReadMessageId[channelId] = newestId;
|
|
841
1216
|
}
|
|
1217
|
+
|
|
1218
|
+
return { newMessages, editedMessages, deletedMessages };
|
|
1219
|
+
} finally {
|
|
1220
|
+
this.liveDuringSync.delete(live);
|
|
842
1221
|
}
|
|
1222
|
+
}
|
|
843
1223
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1224
|
+
/** A channel's stored Discord rows by their Discord message id, whole: an
|
|
1225
|
+
* edit replaces a row's own text and keeps what the host added beside it.
|
|
1226
|
+
* Each id's rows are in store order. A message stored twice holds two,
|
|
1227
|
+
* of which a sync acts on the first, as the live path does (storedRow),
|
|
1228
|
+
* and a delete takes both. A write the framework holds is a row too
|
|
1229
|
+
* (storedRows). */
|
|
1230
|
+
private storedByDiscordId(ctx: ModuleContext, channelId: string): Map<string, StoredMessage[]> {
|
|
1231
|
+
const rows = new Map<string, StoredMessage[]>();
|
|
1232
|
+
for (const msg of ctx.queryMessages({ source: 'discord', metadata: { channelId } }).messages) {
|
|
1233
|
+
const external = msg.metadata?.external as { id?: string } | undefined;
|
|
1234
|
+
if (!external?.id) continue;
|
|
1235
|
+
const held = rows.get(external.id);
|
|
1236
|
+
if (held) held.push(msg);
|
|
1237
|
+
else rows.set(external.id, [msg]);
|
|
849
1238
|
}
|
|
1239
|
+
return rows;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
/** The stored rows of a Discord message, by its id in the row's metadata,
|
|
1243
|
+
* in store order. A write the framework holds comes after them as a held
|
|
1244
|
+
* row, which an edit or removal reaches as it does a stored one. The
|
|
1245
|
+
* module context's findMessageByExternalId holds only rows the module
|
|
1246
|
+
* stored through its context, not those a process response added, so it
|
|
1247
|
+
* misses what the live path stored. */
|
|
1248
|
+
private storedRows(discordMessageId: string): StoredMessage[] {
|
|
1249
|
+
return this.ctx?.queryMessages({ source: 'discord', externalIds: [discordMessageId] }).messages ?? [];
|
|
1250
|
+
}
|
|
850
1251
|
|
|
851
|
-
|
|
1252
|
+
/** The row an edit or a create acts on: a message's first row, a stored
|
|
1253
|
+
* one before a held one (storedRows). */
|
|
1254
|
+
private storedRow(discordMessageId: string): StoredMessage | undefined {
|
|
1255
|
+
return this.storedRows(discordMessageId)[0];
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
/** Marks a Discord message as the live path's for every history sync running now. */
|
|
1259
|
+
private noteLive(discordMessageId: string): void {
|
|
1260
|
+
for (const live of this.liveDuringSync) live.add(discordMessageId);
|
|
852
1261
|
}
|
|
853
1262
|
|
|
854
1263
|
private async handleDiscordMessage(message: DiscordMessageData): Promise<void> {
|
|
855
|
-
|
|
1264
|
+
const ctx = this.ctx;
|
|
1265
|
+
if (!ctx) return;
|
|
856
1266
|
|
|
857
1267
|
// ALWAYS ignore our own messages to prevent feedback loops
|
|
858
1268
|
if (this.state.botUserId && message.authorId === this.state.botUserId) {
|
|
@@ -887,39 +1297,65 @@ export class DiscordModule implements Module {
|
|
|
887
1297
|
// Determine if this message should trigger inference
|
|
888
1298
|
const shouldTrigger = this.shouldTriggerInference(message, isDM);
|
|
889
1299
|
|
|
1300
|
+
// From here the message is the live path's to store (creating). A second
|
|
1301
|
+
// delivery of a message whose create is already on its way is ignored.
|
|
1302
|
+
if (this.creating.has(message.id)) return;
|
|
1303
|
+
const live: LiveCreate = { phase: 'converting', deleted: false };
|
|
1304
|
+
this.creating.set(message.id, live);
|
|
1305
|
+
const drop = () => { if (this.creating.get(message.id) === live) this.creating.delete(message.id); };
|
|
1306
|
+
|
|
890
1307
|
// Convert message content including attachments
|
|
891
|
-
|
|
1308
|
+
let contentBlocks: ContentBlock[];
|
|
1309
|
+
try {
|
|
1310
|
+
contentBlocks = await this.convertMessageToContent(message);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
drop();
|
|
1313
|
+
throw error;
|
|
1314
|
+
}
|
|
1315
|
+
// A module stopped or started again meanwhile writes nothing.
|
|
1316
|
+
if (this.ctx !== ctx) {
|
|
1317
|
+
drop();
|
|
1318
|
+
return;
|
|
1319
|
+
}
|
|
1320
|
+
// A delete that came meanwhile drops the create: nothing is stored,
|
|
1321
|
+
// nobody is woken. Its entry stays until that delete is handled, so no
|
|
1322
|
+
// sync stores the message before then.
|
|
1323
|
+
if (live.deleted) return;
|
|
1324
|
+
// An edit that came meanwhile: its text, and the blocks of the
|
|
1325
|
+
// attachments it left.
|
|
1326
|
+
if (live.text !== undefined) contentBlocks = withOwnText(contentBlocks, live.text);
|
|
1327
|
+
if (live.attachments) contentBlocks = withoutGoneAttachments(contentBlocks, live.attachments);
|
|
1328
|
+
live.phase = 'queued';
|
|
892
1329
|
|
|
893
1330
|
// Only queue the event - all state writes happen in onProcess()
|
|
894
|
-
|
|
1331
|
+
ctx.pushEvent({
|
|
895
1332
|
type: 'discord:message',
|
|
896
1333
|
discordMessageId: message.id,
|
|
897
1334
|
channelId: message.channelId,
|
|
898
1335
|
guildId: message.guildId,
|
|
899
1336
|
authorId: message.authorId,
|
|
900
1337
|
authorName: message.authorName,
|
|
901
|
-
content: message.content,
|
|
1338
|
+
content: live.text ?? message.content,
|
|
902
1339
|
contentBlocks,
|
|
903
1340
|
timestamp: message.timestamp.getTime(),
|
|
904
|
-
attachments: message.attachments,
|
|
1341
|
+
attachments: live.attachments ?? message.attachments,
|
|
905
1342
|
shouldTriggerInference: shouldTrigger,
|
|
906
1343
|
} as DiscordMessageEvent);
|
|
907
1344
|
}
|
|
908
1345
|
|
|
909
1346
|
/**
|
|
910
|
-
* Convert Discord message to ContentBlock array
|
|
911
|
-
*
|
|
1347
|
+
* Convert a Discord message, live or from history, to ContentBlock array:
|
|
1348
|
+
* its own text first, '' when it has none, then any supported attachments
|
|
1349
|
+
* (images, text files) and notes for the rest, as TEXT_FIRST marks.
|
|
912
1350
|
*/
|
|
913
|
-
private async convertMessageToContent(
|
|
1351
|
+
private async convertMessageToContent(
|
|
1352
|
+
message: { id: string; content: string; attachments?: readonly DiscordAttachment[] },
|
|
1353
|
+
): Promise<ContentBlock[]> {
|
|
1354
|
+
// What the host adds after the message's own text: one block per attachment.
|
|
914
1355
|
const blocks: ContentBlock[] = [];
|
|
915
1356
|
|
|
916
|
-
// Add text content if present
|
|
917
|
-
if (message.content.trim()) {
|
|
918
|
-
blocks.push({ type: 'text', text: message.content });
|
|
919
|
-
}
|
|
920
|
-
|
|
921
1357
|
// Process attachments
|
|
922
|
-
for (const attachment of message.attachments) {
|
|
1358
|
+
for (const attachment of message.attachments ?? []) {
|
|
923
1359
|
const contentType = attachment.contentType?.toLowerCase() ?? '';
|
|
924
1360
|
const filename = attachment.filename.toLowerCase();
|
|
925
1361
|
|
|
@@ -937,7 +1373,15 @@ export class DiscordModule implements Module {
|
|
|
937
1373
|
|
|
938
1374
|
// Check for text files - fetch and include content
|
|
939
1375
|
const isTextFile = TEXT_FILE_EXTENSIONS.some(ext => filename.endsWith(ext));
|
|
940
|
-
if (isTextFile && attachment.size
|
|
1376
|
+
if (isTextFile && attachment.size >= TEXT_FILE_LIMIT_BYTES) {
|
|
1377
|
+
blocks.push({
|
|
1378
|
+
type: 'text',
|
|
1379
|
+
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.]`,
|
|
1380
|
+
});
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
// A text file of size 0 gets the note below, as before.
|
|
1384
|
+
if (isTextFile && attachment.size) {
|
|
941
1385
|
try {
|
|
942
1386
|
const response = await fetch(attachment.url);
|
|
943
1387
|
if (response.ok) {
|
|
@@ -946,13 +1390,15 @@ export class DiscordModule implements Module {
|
|
|
946
1390
|
type: 'text',
|
|
947
1391
|
text: `\n--- File: ${attachment.filename} ---\n${textContent}\n--- End of ${attachment.filename} ---\n`,
|
|
948
1392
|
});
|
|
1393
|
+
} else {
|
|
1394
|
+
// An answer whose body is never read holds its connection.
|
|
1395
|
+
await response.body?.cancel().catch(() => {});
|
|
1396
|
+
console.warn(`Discord: Attachment ${logValue(attachment.filename)} in message ${message.id} wasn't fetched: HTTP ${response.status}`);
|
|
1397
|
+
blocks.push(attachmentNotFetched(attachment.filename));
|
|
949
1398
|
}
|
|
950
1399
|
} catch (error) {
|
|
951
|
-
|
|
952
|
-
blocks.push(
|
|
953
|
-
type: 'text',
|
|
954
|
-
text: `[Attachment: ${attachment.filename} (failed to fetch)]`,
|
|
955
|
-
});
|
|
1400
|
+
console.warn(`Discord: Attachment ${logValue(attachment.filename)} in message ${message.id} wasn't fetched:`, error);
|
|
1401
|
+
blocks.push(attachmentNotFetched(attachment.filename));
|
|
956
1402
|
}
|
|
957
1403
|
continue;
|
|
958
1404
|
}
|
|
@@ -960,16 +1406,11 @@ export class DiscordModule implements Module {
|
|
|
960
1406
|
// For unsupported attachments, add a note
|
|
961
1407
|
blocks.push({
|
|
962
1408
|
type: 'text',
|
|
963
|
-
text: `[Attachment: ${attachment.filename} (${contentType || 'unknown type'})]`,
|
|
1409
|
+
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.]`,
|
|
964
1410
|
});
|
|
965
1411
|
}
|
|
966
1412
|
|
|
967
|
-
|
|
968
|
-
if (blocks.length === 0) {
|
|
969
|
-
blocks.push({ type: 'text', text: '[Empty message]' });
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
return blocks;
|
|
1413
|
+
return textFirst(ownText(message.content), blocks);
|
|
973
1414
|
}
|
|
974
1415
|
|
|
975
1416
|
/**
|
|
@@ -1012,20 +1453,36 @@ export class DiscordModule implements Module {
|
|
|
1012
1453
|
}
|
|
1013
1454
|
}
|
|
1014
1455
|
|
|
1015
|
-
private handleDiscordMessageEdit(messageId: string, newContent: string): void {
|
|
1456
|
+
private handleDiscordMessageEdit(messageId: string, newContent: string, attachments?: DiscordAttachment[]): void {
|
|
1016
1457
|
if (!this.ctx) return;
|
|
1017
1458
|
|
|
1459
|
+
// An edit while the message's create converts is folded into that
|
|
1460
|
+
// create, which would otherwise store the text it was sent with; one that
|
|
1461
|
+
// comes after follows the create through the queue. Its event still goes,
|
|
1462
|
+
// for a row a history sync stored first.
|
|
1463
|
+
const live = this.creating.get(messageId);
|
|
1464
|
+
if (live) {
|
|
1465
|
+
live.text = newContent;
|
|
1466
|
+
if (attachments) live.attachments = attachments;
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1018
1469
|
// Only queue the event - state writes happen in onProcess()
|
|
1019
1470
|
this.ctx.pushEvent({
|
|
1020
1471
|
type: 'discord:edit',
|
|
1021
1472
|
discordMessageId: messageId,
|
|
1022
1473
|
newContent,
|
|
1474
|
+
...(attachments ? { attachments } : {}),
|
|
1023
1475
|
});
|
|
1024
1476
|
}
|
|
1025
1477
|
|
|
1026
1478
|
private handleDiscordMessageDelete(messageId: string): void {
|
|
1027
1479
|
if (!this.ctx) return;
|
|
1028
1480
|
|
|
1481
|
+
// A delete before the message's create is handled drops that create.
|
|
1482
|
+
// Its event still goes, for a row a history sync stored first.
|
|
1483
|
+
const live = this.creating.get(messageId);
|
|
1484
|
+
if (live) live.deleted = true;
|
|
1485
|
+
|
|
1029
1486
|
// Only queue the event - state writes happen in onProcess()
|
|
1030
1487
|
this.ctx.pushEvent({
|
|
1031
1488
|
type: 'discord:delete',
|
|
@@ -1037,9 +1494,30 @@ export class DiscordModule implements Module {
|
|
|
1037
1494
|
// Tool Handlers
|
|
1038
1495
|
// ==========================================================================
|
|
1039
1496
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1497
|
+
/** Record a message the agent posted in a turn, for that turn's thoughts (lastPosts). */
|
|
1498
|
+
private notePost(agentName: string | undefined, turnId: number | undefined, channelId: string, messageId: string): void {
|
|
1499
|
+
if (agentName === undefined || turnId === undefined) return;
|
|
1500
|
+
// A later turn's post stays: this one's turn has ended, and its thoughts
|
|
1501
|
+
// have gone or will go nowhere.
|
|
1502
|
+
const current = this.lastPosts.get(agentName);
|
|
1503
|
+
if (current && current.turnId > turnId) return;
|
|
1504
|
+
// Set it last, so the entry recorded longest ago is the first to go.
|
|
1505
|
+
this.lastPosts.delete(agentName);
|
|
1506
|
+
this.lastPosts.set(agentName, { turnId, channelId, messageId });
|
|
1507
|
+
if (this.lastPosts.size > MAX_LAST_POSTS) {
|
|
1508
|
+
const oldest = this.lastPosts.keys().next().value;
|
|
1509
|
+
if (oldest !== undefined) this.lastPosts.delete(oldest);
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
private async handleSend(input: SendInput, call: ToolCall): Promise<ToolResult> {
|
|
1514
|
+
const wait = this.rateLimitWait(input.channelId);
|
|
1515
|
+
if (wait > 0) {
|
|
1516
|
+
return {
|
|
1517
|
+
success: false,
|
|
1518
|
+
error: `Nothing was sent: channel ${input.channelId} has reached ${this.rateLimitRule()}. Send it again in ${seconds(wait)}; other channels have their own limit.`,
|
|
1519
|
+
isError: true,
|
|
1520
|
+
};
|
|
1043
1521
|
}
|
|
1044
1522
|
|
|
1045
1523
|
const result = await this.client.sendMessage(input.channelId, input.content, {
|
|
@@ -1047,25 +1525,43 @@ export class DiscordModule implements Module {
|
|
|
1047
1525
|
createThread: input.createThread,
|
|
1048
1526
|
});
|
|
1049
1527
|
|
|
1050
|
-
// Track for thought-editing
|
|
1051
|
-
|
|
1528
|
+
// Track for thought-editing: the message is out, whether or not its
|
|
1529
|
+
// thread was started.
|
|
1530
|
+
this.notePost(call.callerAgentName, call.callerTurnId, input.channelId, result.messageId);
|
|
1052
1531
|
|
|
1053
|
-
|
|
1532
|
+
if (result.threadFailure !== undefined) {
|
|
1533
|
+
return { success: false, error: result.threadFailure, isError: true };
|
|
1534
|
+
}
|
|
1535
|
+
return { success: true, data: { messageId: result.messageId, ...(result.note !== undefined ? { note: result.note } : {}) } };
|
|
1054
1536
|
}
|
|
1055
1537
|
|
|
1056
1538
|
private async handleDM(input: SendDMInput): Promise<ToolResult> {
|
|
1057
1539
|
const result = await this.client.sendDM(input.userId, input.content);
|
|
1058
|
-
|
|
1540
|
+
// The direct message channel's id is what fetch_history reads it by, should a later dm need checking.
|
|
1541
|
+
return { success: true, data: { messageId: result.messageId, ...(result.channelId !== undefined ? { channelId: result.channelId } : {}) } };
|
|
1059
1542
|
}
|
|
1060
1543
|
|
|
1061
|
-
private async handleReply(input: ReplyInput): Promise<ToolResult> {
|
|
1544
|
+
private async handleReply(input: ReplyInput, call: ToolCall): Promise<ToolResult> {
|
|
1062
1545
|
const replyCtx = this.replyContexts.get('default');
|
|
1063
1546
|
if (!replyCtx) {
|
|
1064
|
-
return {
|
|
1547
|
+
return {
|
|
1548
|
+
success: false,
|
|
1549
|
+
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.',
|
|
1550
|
+
isError: true,
|
|
1551
|
+
};
|
|
1065
1552
|
}
|
|
1066
1553
|
|
|
1067
|
-
|
|
1068
|
-
|
|
1554
|
+
// The reply goes to the context's thread when it names one, an empty id
|
|
1555
|
+
// as well, as it always has; the words name an empty id as what it is.
|
|
1556
|
+
const thread = replyCtx.threadId;
|
|
1557
|
+
const wait = this.rateLimitWait(replyCtx.channelId);
|
|
1558
|
+
if (wait > 0) {
|
|
1559
|
+
return {
|
|
1560
|
+
success: false,
|
|
1561
|
+
error: `Nothing was sent: this conversation's channel (${replyCtx.channelId}) has reached ${this.rateLimitRule()}`
|
|
1562
|
+
+ `${thread ? `, which counts its replies in thread ${thread} too` : ''}. Reply again in ${seconds(wait)}.`,
|
|
1563
|
+
isError: true,
|
|
1564
|
+
};
|
|
1069
1565
|
}
|
|
1070
1566
|
|
|
1071
1567
|
let content = input.content;
|
|
@@ -1073,13 +1569,31 @@ export class DiscordModule implements Module {
|
|
|
1073
1569
|
content = `<@${replyCtx.userId}> ${content}`;
|
|
1074
1570
|
}
|
|
1075
1571
|
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1572
|
+
// reply's call names no channel or message: its reply context does, as it
|
|
1573
|
+
// stands now, before the send, which an incoming message can change.
|
|
1574
|
+
const targetChannel = thread ?? replyCtx.channelId;
|
|
1575
|
+
const pointsAt = thread === undefined ? `channel ${targetChannel}` : thread === '' ? 'an empty thread id' : `thread ${targetChannel}`;
|
|
1576
|
+
const check = thread === ''
|
|
1577
|
+
? `, 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`
|
|
1578
|
+
: `: discord--fetch_history with channelId ${targetChannel}, its reply context's ${thread !== undefined ? 'thread' : 'channel'}, shows it, so check before you reply again`;
|
|
1579
|
+
const replyTo = replyCtx.replyToMessageId;
|
|
1580
|
+
let result: { messageId: string };
|
|
1581
|
+
try {
|
|
1582
|
+
result = await this.client.sendMessage(targetChannel, content, { replyTo });
|
|
1583
|
+
} catch (error) {
|
|
1584
|
+
if (error instanceof DiscordRefusal) {
|
|
1585
|
+
throw new DiscordRefusal(`${error.message} A reply goes where its reply context points: ${pointsAt}`
|
|
1586
|
+
+ `${replyTo ? `, answering message ${replyTo}` : ''}. discord--set_reply_context changes that.`, { cause: error });
|
|
1587
|
+
}
|
|
1588
|
+
return {
|
|
1589
|
+
success: false,
|
|
1590
|
+
error: `discord--reply stopped on an unexpected error: ${errorText(error)}. Whether the reply was sent is unknown${check}.`,
|
|
1591
|
+
isError: true,
|
|
1592
|
+
};
|
|
1593
|
+
}
|
|
1080
1594
|
|
|
1081
1595
|
// Track for thought-editing
|
|
1082
|
-
this.
|
|
1596
|
+
this.notePost(call.callerAgentName, call.callerTurnId, targetChannel, result.messageId);
|
|
1083
1597
|
|
|
1084
1598
|
// Clear reply-to after sending
|
|
1085
1599
|
replyCtx.replyToMessageId = undefined;
|
|
@@ -1142,7 +1656,7 @@ export class DiscordModule implements Module {
|
|
|
1142
1656
|
|
|
1143
1657
|
private async handleActivate(input: { additive?: boolean }): Promise<ToolResult> {
|
|
1144
1658
|
if (!this.ctx) {
|
|
1145
|
-
return { success: false, error:
|
|
1659
|
+
return { success: false, error: NOT_RUNNING, isError: true };
|
|
1146
1660
|
}
|
|
1147
1661
|
|
|
1148
1662
|
this.ctx.registerSpeechHandler('*', { additive: input.additive });
|
|
@@ -1151,7 +1665,7 @@ export class DiscordModule implements Module {
|
|
|
1151
1665
|
|
|
1152
1666
|
private async handleDeactivate(): Promise<ToolResult> {
|
|
1153
1667
|
if (!this.ctx) {
|
|
1154
|
-
return { success: false, error:
|
|
1668
|
+
return { success: false, error: NOT_RUNNING, isError: true };
|
|
1155
1669
|
}
|
|
1156
1670
|
|
|
1157
1671
|
this.ctx.unregisterSpeechHandler();
|
|
@@ -1213,6 +1727,11 @@ export class DiscordModule implements Module {
|
|
|
1213
1727
|
authorId: m.authorId,
|
|
1214
1728
|
isBot: m.isBot,
|
|
1215
1729
|
content: m.content,
|
|
1730
|
+
// Not their URLs: a link Discord signs works for anyone holding it
|
|
1731
|
+
// until it expires, and an agent could repeat it where others read.
|
|
1732
|
+
...(m.attachments?.length
|
|
1733
|
+
? { attachments: m.attachments.map(({ filename, contentType, size }) => ({ filename, contentType, size })) }
|
|
1734
|
+
: {}),
|
|
1216
1735
|
timestamp: formatZonedDateTime(m.timestamp, this.timeZone),
|
|
1217
1736
|
replyTo: m.replyTo,
|
|
1218
1737
|
})),
|
|
@@ -1231,8 +1750,25 @@ export class DiscordModule implements Module {
|
|
|
1231
1750
|
.join('\n');
|
|
1232
1751
|
}
|
|
1233
1752
|
|
|
1234
|
-
private
|
|
1235
|
-
|
|
1753
|
+
private rateLimitPerMinute(): number {
|
|
1754
|
+
return this.config.rateLimitPerMinute ?? 30;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
/** The limit, as the rate-limit refusals word it. */
|
|
1758
|
+
private rateLimitRule(): string {
|
|
1759
|
+
const limit = this.rateLimitPerMinute();
|
|
1760
|
+
return `this host's limit of ${limit} send${limit === 1 ? '' : 's'} a minute from the bot`;
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
/**
|
|
1764
|
+
* Counts a send to `channelId` against its limit and returns 0, or, when
|
|
1765
|
+
* the channel has had its sends for this window, returns the whole seconds
|
|
1766
|
+
* until the window ends, at least 1. A window is the minute from the first
|
|
1767
|
+
* send this lets through, and each send it lets through counts, whether or
|
|
1768
|
+
* not Discord then takes it.
|
|
1769
|
+
*/
|
|
1770
|
+
private rateLimitWait(channelId: string): number {
|
|
1771
|
+
const limit = this.rateLimitPerMinute();
|
|
1236
1772
|
const now = Date.now();
|
|
1237
1773
|
const windowMs = 60 * 1000;
|
|
1238
1774
|
|
|
@@ -1244,11 +1780,11 @@ export class DiscordModule implements Module {
|
|
|
1244
1780
|
}
|
|
1245
1781
|
|
|
1246
1782
|
if (info.count >= limit) {
|
|
1247
|
-
return
|
|
1783
|
+
return Math.max(1, Math.ceil((info.windowStart + windowMs - now) / 1000));
|
|
1248
1784
|
}
|
|
1249
1785
|
|
|
1250
1786
|
info.count++;
|
|
1251
|
-
return
|
|
1787
|
+
return 0;
|
|
1252
1788
|
}
|
|
1253
1789
|
|
|
1254
1790
|
private cleanupOldConversations(): void {
|