@animalabs/agent-framework 0.26.2 → 0.28.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 +99 -8
- package/dist/src/agent.d.ts +291 -23
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +596 -61
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/server.d.ts.map +1 -1
- package/dist/src/api/server.js +5 -4
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +34 -0
- package/dist/src/auto-continue.d.ts.map +1 -0
- package/dist/src/auto-continue.js +36 -0
- package/dist/src/auto-continue.js.map +1 -0
- package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +5 -1
- package/dist/src/code-execution/isolate/scripts.js.map +1 -1
- package/dist/src/cut-off-round.d.ts +24 -0
- package/dist/src/cut-off-round.d.ts.map +1 -0
- package/dist/src/cut-off-round.js +87 -0
- package/dist/src/cut-off-round.js.map +1 -0
- package/dist/src/framework.d.ts +1038 -193
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +5967 -1514
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +405 -46
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +2646 -399
- package/dist/src/gate/event-gate.js.map +1 -1
- package/dist/src/gate/index.d.ts +3 -2
- package/dist/src/gate/index.d.ts.map +1 -1
- package/dist/src/gate/index.js +2 -1
- package/dist/src/gate/index.js.map +1 -1
- package/dist/src/gate/recipe-reconcile.d.ts +103 -0
- package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
- package/dist/src/gate/recipe-reconcile.js +325 -0
- package/dist/src/gate/recipe-reconcile.js.map +1 -0
- package/dist/src/gate/reply-judge.d.ts +116 -0
- package/dist/src/gate/reply-judge.d.ts.map +1 -0
- package/dist/src/gate/reply-judge.js +467 -0
- package/dist/src/gate/reply-judge.js.map +1 -0
- package/dist/src/gate/types.d.ts +229 -8
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/heartbeat/heartbeat.d.ts +184 -0
- package/dist/src/heartbeat/heartbeat.d.ts.map +1 -0
- package/dist/src/heartbeat/heartbeat.js +752 -0
- package/dist/src/heartbeat/heartbeat.js.map +1 -0
- package/dist/src/heartbeat/reminder-time.d.ts +5 -0
- package/dist/src/heartbeat/reminder-time.d.ts.map +1 -0
- package/dist/src/heartbeat/reminder-time.js +107 -0
- package/dist/src/heartbeat/reminder-time.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 +7 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcpl/capability-mask.d.ts.map +1 -1
- package/dist/src/mcpl/capability-mask.js +3 -2
- package/dist/src/mcpl/capability-mask.js.map +1 -1
- package/dist/src/mcpl/channel-registry.d.ts +433 -113
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +1154 -435
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts +16 -8
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +29 -15
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +14 -2
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +32 -7
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +26 -10
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +40 -69
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/index.d.ts +5 -1
- package/dist/src/mcpl/index.d.ts.map +1 -1
- package/dist/src/mcpl/index.js +4 -0
- package/dist/src/mcpl/index.js.map +1 -1
- package/dist/src/mcpl/inference-router.d.ts +14 -2
- package/dist/src/mcpl/inference-router.d.ts.map +1 -1
- package/dist/src/mcpl/inference-router.js +73 -28
- package/dist/src/mcpl/inference-router.js.map +1 -1
- package/dist/src/mcpl/prose-grammar.d.ts +4 -0
- package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
- package/dist/src/mcpl/prose-grammar.js +6 -0
- package/dist/src/mcpl/prose-grammar.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +3 -0
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-coalescer.d.ts +20 -2
- package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
- package/dist/src/mcpl/push-coalescer.js +33 -11
- package/dist/src/mcpl/push-coalescer.js.map +1 -1
- package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
- package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
- package/dist/src/mcpl/reference-fetcher.js +177 -145
- package/dist/src/mcpl/reference-fetcher.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +380 -46
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +736 -77
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +93 -4
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +176 -11
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/tags.d.ts +9 -2
- package/dist/src/mcpl/tags.d.ts.map +1 -1
- package/dist/src/mcpl/tags.js +12 -2
- package/dist/src/mcpl/tags.js.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
- package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
- package/dist/src/mcpl/tool-lifecycle.js +21 -0
- package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
- package/dist/src/mcpl/tool-presence.d.ts +311 -0
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
- package/dist/src/mcpl/tool-presence.js +697 -0
- package/dist/src/mcpl/tool-presence.js.map +1 -0
- package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
- package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
- package/dist/src/mcpl/tool-result-normalize.js +14 -6
- package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +46 -0
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
- package/dist/src/mcpl/tool-signature.js +321 -0
- package/dist/src/mcpl/tool-signature.js.map +1 -0
- package/dist/src/mcpl/toolset-host.d.ts +251 -0
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
- package/dist/src/mcpl/toolset-host.js +720 -0
- package/dist/src/mcpl/toolset-host.js.map +1 -0
- package/dist/src/mcpl/toolsets-module.d.ts +83 -0
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
- package/dist/src/mcpl/toolsets-module.js +523 -0
- package/dist/src/mcpl/toolsets-module.js.map +1 -0
- package/dist/src/mcpl/transport.d.ts +2 -2
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +3 -2
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +24 -2
- 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 +7 -9
- package/dist/src/mcpl/visible-content.d.ts.map +1 -1
- package/dist/src/mcpl/visible-content.js +21 -9
- package/dist/src/mcpl/visible-content.js.map +1 -1
- package/dist/src/module-registry.d.ts +27 -9
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +77 -17
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/discord/index.js +1 -1
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts +2 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +73 -28
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/history/semantic.d.ts +108 -6
- package/dist/src/modules/history/semantic.d.ts.map +1 -1
- package/dist/src/modules/history/semantic.js +273 -57
- package/dist/src/modules/history/semantic.js.map +1 -1
- package/dist/src/modules/workspace/answers.d.ts +22 -0
- package/dist/src/modules/workspace/answers.d.ts.map +1 -0
- package/dist/src/modules/workspace/answers.js +123 -0
- package/dist/src/modules/workspace/answers.js.map +1 -0
- package/dist/src/modules/workspace/branch-intent.d.ts +5 -3
- package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
- package/dist/src/modules/workspace/branch-intent.js +6 -2
- package/dist/src/modules/workspace/branch-intent.js.map +1 -1
- package/dist/src/modules/workspace/direct-write.d.ts +184 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
- package/dist/src/modules/workspace/direct-write.js +417 -0
- package/dist/src/modules/workspace/direct-write.js.map +1 -0
- package/dist/src/modules/workspace/disk-agreement.d.ts +33 -11
- package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
- package/dist/src/modules/workspace/disk-agreement.js +48 -17
- package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
- package/dist/src/modules/workspace/edit-text.d.ts +27 -12
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +61 -29
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +10 -0
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +21 -14
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +308 -78
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +2103 -720
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +75 -63
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +366 -180
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts +41 -0
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
- package/dist/src/modules/workspace/read-page.js +219 -0
- package/dist/src/modules/workspace/read-page.js.map +1 -0
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +26 -16
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/result-text.d.ts +68 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
- package/dist/src/modules/workspace/result-text.js +94 -0
- package/dist/src/modules/workspace/result-text.js.map +1 -0
- package/dist/src/modules/workspace/sync.d.ts +9 -0
- package/dist/src/modules/workspace/sync.d.ts.map +1 -1
- package/dist/src/modules/workspace/sync.js +14 -0
- package/dist/src/modules/workspace/sync.js.map +1 -1
- package/dist/src/modules/workspace/text.d.ts +29 -12
- package/dist/src/modules/workspace/text.d.ts.map +1 -1
- package/dist/src/modules/workspace/text.js +35 -33
- package/dist/src/modules/workspace/text.js.map +1 -1
- package/dist/src/modules/workspace/types.d.ts +137 -60
- package/dist/src/modules/workspace/types.d.ts.map +1 -1
- package/dist/src/modules/workspace/types.js.map +1 -1
- package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
- package/dist/src/modules/workspace/watcher.js +8 -3
- package/dist/src/modules/workspace/watcher.js.map +1 -1
- package/dist/src/prose-drafts.d.ts +4 -3
- package/dist/src/prose-drafts.d.ts.map +1 -1
- package/dist/src/prose-drafts.js +3 -2
- package/dist/src/prose-drafts.js.map +1 -1
- package/dist/src/reasoning-loss-notice.d.ts +23 -0
- package/dist/src/reasoning-loss-notice.d.ts.map +1 -0
- package/dist/src/reasoning-loss-notice.js +50 -0
- package/dist/src/reasoning-loss-notice.js.map +1 -0
- package/dist/src/reasoning-loss.d.ts +24 -0
- package/dist/src/reasoning-loss.d.ts.map +1 -0
- package/dist/src/reasoning-loss.js +102 -0
- package/dist/src/reasoning-loss.js.map +1 -0
- package/dist/src/send-tools.d.ts +3 -2
- package/dist/src/send-tools.d.ts.map +1 -1
- package/dist/src/send-tools.js +3 -2
- package/dist/src/send-tools.js.map +1 -1
- package/dist/src/sent-rows.d.ts +59 -0
- package/dist/src/sent-rows.d.ts.map +1 -0
- package/dist/src/sent-rows.js +145 -0
- package/dist/src/sent-rows.js.map +1 -0
- package/dist/src/silent-heartbeat.d.ts +17 -10
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/stable-hash.d.ts +3 -0
- package/dist/src/stable-hash.d.ts.map +1 -0
- package/dist/src/stable-hash.js +15 -0
- package/dist/src/stable-hash.js.map +1 -0
- package/dist/src/stored-injections.d.ts +99 -0
- package/dist/src/stored-injections.d.ts.map +1 -0
- package/dist/src/stored-injections.js +106 -0
- package/dist/src/stored-injections.js.map +1 -0
- package/dist/src/text-as-stored.d.ts +4 -3
- package/dist/src/text-as-stored.d.ts.map +1 -1
- package/dist/src/text-as-stored.js +4 -3
- package/dist/src/text-as-stored.js.map +1 -1
- package/dist/src/thinking-drops.d.ts +74 -0
- package/dist/src/thinking-drops.d.ts.map +1 -0
- package/dist/src/thinking-drops.js +96 -0
- package/dist/src/thinking-drops.js.map +1 -0
- package/dist/src/tool-args.d.ts +12 -0
- package/dist/src/tool-args.d.ts.map +1 -1
- package/dist/src/tool-args.js +35 -1
- package/dist/src/tool-args.js.map +1 -1
- package/dist/src/tool-image-ledger.d.ts +9 -8
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +9 -8
- package/dist/src/tool-image-ledger.js.map +1 -1
- package/dist/src/tool-presentation.d.ts.map +1 -1
- package/dist/src/tool-presentation.js +3 -1
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +12 -6
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +44 -17
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tool-result-history.d.ts +176 -24
- package/dist/src/tool-result-history.d.ts.map +1 -1
- package/dist/src/tool-result-history.js +411 -91
- package/dist/src/tool-result-history.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +17 -9
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +84 -37
- package/dist/src/tune-out/coordinator.js.map +1 -1
- package/dist/src/tune-out/tools.d.ts +4 -1
- package/dist/src/tune-out/tools.d.ts.map +1 -1
- package/dist/src/types/agent.d.ts +217 -7
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +33 -9
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +39 -3
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +95 -9
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +91 -3
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/agent.ts +661 -67
- package/src/api/server.ts +5 -4
- package/src/auto-continue.ts +43 -0
- package/src/code-execution/isolate/scripts.ts +11 -3
- package/src/cut-off-round.ts +98 -0
- package/src/framework.ts +6168 -1540
- package/src/gate/event-gate.ts +2895 -405
- package/src/gate/index.ts +10 -1
- package/src/gate/recipe-reconcile.ts +355 -0
- package/src/gate/reply-judge.ts +512 -0
- package/src/gate/types.ts +234 -9
- package/src/heartbeat/heartbeat.ts +804 -0
- package/src/heartbeat/reminder-time.ts +107 -0
- package/src/index.ts +11 -4
- package/src/mcpl/capability-mask.ts +3 -2
- package/src/mcpl/channel-registry.ts +1222 -468
- package/src/mcpl/conversation-router.ts +34 -16
- package/src/mcpl/convert-block.ts +14 -1
- package/src/mcpl/hook-orchestrator.ts +33 -6
- package/src/mcpl/inbound-source.ts +42 -69
- package/src/mcpl/index.ts +6 -1
- package/src/mcpl/inference-router.ts +79 -34
- package/src/mcpl/prose-grammar.ts +7 -0
- package/src/mcpl/protocol-family.ts +2 -0
- package/src/mcpl/push-coalescer.ts +51 -13
- package/src/mcpl/reference-fetcher.ts +201 -162
- package/src/mcpl/references.ts +851 -77
- package/src/mcpl/server-connection.ts +189 -11
- package/src/mcpl/tags.ts +13 -2
- package/src/mcpl/tool-lifecycle.ts +21 -0
- package/src/mcpl/tool-presence.ts +716 -0
- package/src/mcpl/tool-result-normalize.ts +18 -6
- package/src/mcpl/tool-signature.ts +316 -0
- package/src/mcpl/toolset-host.ts +727 -0
- package/src/mcpl/toolsets-module.ts +545 -0
- package/src/mcpl/transport.ts +3 -2
- package/src/mcpl/types.ts +25 -2
- package/src/mcpl/visible-content.ts +22 -9
- package/src/module-registry.ts +85 -26
- package/src/modules/discord/index.ts +1 -1
- package/src/modules/history/index.ts +70 -30
- package/src/modules/history/semantic.ts +308 -54
- package/src/modules/workspace/answers.ts +133 -0
- package/src/modules/workspace/branch-intent.ts +10 -5
- package/src/modules/workspace/direct-write.ts +460 -0
- package/src/modules/workspace/disk-agreement.ts +52 -21
- package/src/modules/workspace/edit-text.ts +84 -35
- package/src/modules/workspace/effects.ts +22 -13
- package/src/modules/workspace/index.ts +2142 -723
- package/src/modules/workspace/observe.ts +381 -177
- package/src/modules/workspace/read-page.ts +228 -0
- package/src/modules/workspace/reconcile.ts +27 -15
- package/src/modules/workspace/result-text.ts +121 -0
- package/src/modules/workspace/sync.ts +16 -0
- package/src/modules/workspace/text.ts +58 -32
- package/src/modules/workspace/types.ts +137 -60
- package/src/modules/workspace/watcher.ts +8 -3
- package/src/prose-drafts.ts +4 -3
- package/src/reasoning-loss-notice.ts +62 -0
- package/src/reasoning-loss.ts +120 -0
- package/src/send-tools.ts +3 -2
- package/src/sent-rows.ts +162 -0
- package/src/silent-heartbeat.ts +17 -10
- package/src/stable-hash.ts +14 -0
- package/src/stored-injections.ts +131 -0
- package/src/text-as-stored.ts +4 -3
- package/src/thinking-drops.ts +135 -0
- package/src/tool-args.ts +34 -1
- package/src/tool-image-ledger.ts +9 -8
- package/src/tool-presentation.ts +3 -1
- package/src/tool-result-guard.ts +45 -17
- package/src/tool-result-history.ts +471 -90
- package/src/tune-out/coordinator.ts +80 -37
- package/src/tune-out/tools.ts +4 -1
- package/src/types/agent.ts +220 -7
- package/src/types/events.ts +33 -9
- package/src/types/framework.ts +36 -3
- package/src/types/module.ts +91 -9
- package/src/types/trace.ts +94 -3
|
@@ -14,31 +14,44 @@
|
|
|
14
14
|
* a stored message.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { classifyBlock, NON_STRING_TEXT_PLACEHOLDER } from './references.js';
|
|
18
|
+
|
|
17
19
|
/**
|
|
18
20
|
* True when `content` holds nothing the model would see: no non-text block,
|
|
19
21
|
* and no text block whose text has a non-whitespace character. Mirrors the
|
|
20
22
|
* request sanitizer, which drops text that is not a string or trims to ''.
|
|
21
|
-
* Accepts MCPL wire blocks and converted membrane blocks alike.
|
|
23
|
+
* Accepts MCPL wire blocks and converted membrane blocks alike. A wire
|
|
24
|
+
* block is judged as the block the converter makes of it where that's text:
|
|
25
|
+
* MCP's embedded resource shows as the block its contents make
|
|
26
|
+
* (classifyBlock's `embedded`), so one whose contents are blank text holds
|
|
27
|
+
* nothing to see.
|
|
22
28
|
*/
|
|
23
29
|
export function isVisiblyEmptyContent(content: readonly unknown[] | null | undefined): boolean {
|
|
24
30
|
if (!Array.isArray(content)) return true;
|
|
25
31
|
for (const block of content) {
|
|
26
32
|
if (!block || typeof block !== 'object') continue;
|
|
27
33
|
const b = block as { type?: unknown; text?: unknown };
|
|
34
|
+
const shown = b.type === 'resource' ? embeddedText(b) : undefined;
|
|
35
|
+
if (shown !== undefined) {
|
|
36
|
+
if (shown.trim() !== '') return false;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
28
39
|
if (b.type !== 'text') return false;
|
|
29
40
|
if (typeof b.text === 'string' && b.text.trim() !== '') return false;
|
|
30
41
|
}
|
|
31
42
|
return true;
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
/**
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
45
|
+
/** The text an embedded resource's contents make, or undefined for any other
|
|
46
|
+
* block, an embedded resource shown as anything but text among them. */
|
|
47
|
+
function embeddedText(block: unknown): string | undefined {
|
|
48
|
+
const c = classifyBlock(block);
|
|
49
|
+
return c.kind === 'embedded' && c.contents.kind === 'block' && c.contents.block.type === 'text' ? c.contents.block.text : undefined;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// What the converter shows for a text block whose text isn't a string,
|
|
53
|
+
// beside the converters' other markers in references.ts.
|
|
54
|
+
export { NON_STRING_TEXT_PLACEHOLDER };
|
|
42
55
|
|
|
43
56
|
/** A wire text block whose text isn't a string, which the converter shows as
|
|
44
57
|
* NON_STRING_TEXT_PLACEHOLDER. */
|
package/src/module-registry.ts
CHANGED
|
@@ -21,13 +21,14 @@ import type {
|
|
|
21
21
|
ProcessEvent,
|
|
22
22
|
TraceEventListener,
|
|
23
23
|
ModuleMessage,
|
|
24
|
+
ModuleContextInjection,
|
|
24
25
|
MessageQueryResult,
|
|
25
26
|
} from './types/index.js';
|
|
26
27
|
import { heldWriteId } from './held-writes.js';
|
|
27
28
|
import type { Agent } from './agent.js';
|
|
28
29
|
import { DEFAULT_SHUTDOWN_TIMEOUT_MS, validateShutdownTimeout, waitForShutdown } from './shutdown.js';
|
|
29
30
|
import { logValue } from './mcpl/log-value.js';
|
|
30
|
-
import { toolArgsProblems } from './tool-args.js';
|
|
31
|
+
import { asCalled, markedInputRefusal, toolArgsProblems } from './tool-args.js';
|
|
31
32
|
|
|
32
33
|
/** How the registry queues a module's event: its push names the module. A
|
|
33
34
|
* `ProcessQueue` serves too, ignoring the name. */
|
|
@@ -110,11 +111,11 @@ export class ModuleRegistry {
|
|
|
110
111
|
private store: JsStore;
|
|
111
112
|
private queue: ModuleQueue;
|
|
112
113
|
private getAgents: () => Agent[];
|
|
113
|
-
private addMessageFn: (participant: string, content: ContentBlock[], metadata?: MessageMetadata) => MessageId;
|
|
114
|
+
private addMessageFn: (participant: string, content: ContentBlock[], metadata?: MessageMetadata, opts?: { forAgent?: string }) => MessageId;
|
|
114
115
|
private editMessageFn: (id: MessageId, content: ContentBlock[]) => void;
|
|
115
116
|
private removeMessageFn: (id: MessageId) => void;
|
|
116
117
|
private getMessageFn: (id: MessageId) => ModuleMessage | null;
|
|
117
|
-
private queryMessagesFn: (filter: MessageQuery) => MessageQueryResult;
|
|
118
|
+
private queryMessagesFn: (filter: MessageQuery, opts?: { forAgent?: string }) => MessageQueryResult;
|
|
118
119
|
|
|
119
120
|
private pushEventFn: (event: ProcessEvent, moduleName?: string) => void;
|
|
120
121
|
private onTraceFn: (listener: TraceEventListener) => () => void;
|
|
@@ -134,11 +135,11 @@ export class ModuleRegistry {
|
|
|
134
135
|
queue: ModuleQueue,
|
|
135
136
|
options: {
|
|
136
137
|
getAgents: () => Agent[];
|
|
137
|
-
addMessage: (participant: string, content: ContentBlock[], metadata?: MessageMetadata) => MessageId;
|
|
138
|
+
addMessage: (participant: string, content: ContentBlock[], metadata?: MessageMetadata, opts?: { forAgent?: string }) => MessageId;
|
|
138
139
|
editMessage: (id: MessageId, content: ContentBlock[]) => void;
|
|
139
140
|
removeMessage: (id: MessageId) => void;
|
|
140
141
|
getMessage: (id: MessageId) => ModuleMessage | null;
|
|
141
|
-
queryMessages: (filter: MessageQuery) => MessageQueryResult;
|
|
142
|
+
queryMessages: (filter: MessageQuery, opts?: { forAgent?: string }) => MessageQueryResult;
|
|
142
143
|
/** Called with the pushing module's name. */
|
|
143
144
|
pushEvent: (event: ProcessEvent, moduleName?: string) => void;
|
|
144
145
|
onTrace: (listener: TraceEventListener) => () => void;
|
|
@@ -230,7 +231,8 @@ export class ModuleRegistry {
|
|
|
230
231
|
|
|
231
232
|
/**
|
|
232
233
|
* Set state for a module by name.
|
|
233
|
-
* Used by the framework to apply stateUpdate from EventResponse
|
|
234
|
+
* Used by the framework to apply stateUpdate from EventResponse after its
|
|
235
|
+
* message changes.
|
|
234
236
|
*/
|
|
235
237
|
setModuleState(moduleName: string, state: unknown): void {
|
|
236
238
|
const ctx = this.moduleContexts.get(moduleName);
|
|
@@ -315,11 +317,17 @@ export class ModuleRegistry {
|
|
|
315
317
|
* A call whose input isn't an object, or lacks a key the tool's schema
|
|
316
318
|
* requires, is answered before the module runs, with an error result that
|
|
317
319
|
* opens "Nothing was done:" and names what to send (toolArgsProblems).
|
|
320
|
+
* So is a call membrane marked `unparseableInput`, unless its text is JSON
|
|
321
|
+
* `null`, which reaches the module as a call with no arguments
|
|
322
|
+
* (markedInputRefusal).
|
|
318
323
|
* A caller that has already resolved the tool's definition, as the `utils`
|
|
319
324
|
* tool has a utility's, passes it as `definition`, and it's checked against
|
|
320
325
|
* that one.
|
|
321
326
|
*/
|
|
322
|
-
async handleToolCall(
|
|
327
|
+
async handleToolCall(given: ToolCall, options: { definition?: ToolDefinition } = {}): Promise<ToolResult> {
|
|
328
|
+
const marked = markedInputRefusal(given);
|
|
329
|
+
if (marked !== undefined) return { success: false, error: marked, isError: true };
|
|
330
|
+
const call = asCalled(given);
|
|
323
331
|
// Parse module--tool name
|
|
324
332
|
const sepIndex = call.name.indexOf('--');
|
|
325
333
|
if (sepIndex === -1) {
|
|
@@ -439,8 +447,18 @@ export class ModuleRegistry {
|
|
|
439
447
|
|
|
440
448
|
/**
|
|
441
449
|
* Gather context injections from all modules before inference.
|
|
442
|
-
* Calls each module's gatherContext() in parallel with a per-module timeout
|
|
443
|
-
*
|
|
450
|
+
* Calls each module's gatherContext() in parallel with a per-module timeout,
|
|
451
|
+
* and returns their injections in the modules' registration order, whatever
|
|
452
|
+
* order they finish in. Fail-open: timed-out or erroring modules are skipped
|
|
453
|
+
* (with a stderr line), whether the call throws, rejects, or answers with
|
|
454
|
+
* something that isn't an array of objects, or with a `system` entry whose
|
|
455
|
+
* content isn't an array of objects, every block of which the agent's
|
|
456
|
+
* buildSystemPrompt reads, and one that answers after its timeout adds
|
|
457
|
+
* nothing. An array returned without a promise is taken as the module's
|
|
458
|
+
* answer. An entry's other fields are the module's to get right. Each
|
|
459
|
+
* module's budget starts once its call returns, after its own synchronous
|
|
460
|
+
* work and every earlier module's; a later module's synchronous work still
|
|
461
|
+
* counts against it.
|
|
444
462
|
*
|
|
445
463
|
* The per-module budget is `module.contextTimeoutMs` when declared, else the
|
|
446
464
|
* `timeoutMs` default (15s). Modules whose gatherContext does real work —
|
|
@@ -450,12 +468,16 @@ export class ModuleRegistry {
|
|
|
450
468
|
* to MAX_CONTEXT_TIMEOUT_MS so no single module can hold every turn hostage.
|
|
451
469
|
* Adapted from Anarchid/agent-framework@mcpl-module-proto.
|
|
452
470
|
*/
|
|
453
|
-
async gatherContext(agentName: string, timeoutMs = 15_000): Promise<
|
|
454
|
-
|
|
471
|
+
async gatherContext(agentName: string, timeoutMs = 15_000): Promise<ModuleContextInjection[]> {
|
|
472
|
+
// Each module's result in registration order, whatever order they finish
|
|
473
|
+
// in: a stored injection's content hash follows that order, and a module
|
|
474
|
+
// that answers after its timeout adds nothing (it's skipped, fail-open).
|
|
475
|
+
const results: ModuleContextInjection[][] = [];
|
|
455
476
|
const promises: Promise<void>[] = [];
|
|
456
477
|
|
|
457
478
|
for (const module of this.modules.values()) {
|
|
458
|
-
|
|
479
|
+
const gather = module.gatherContext;
|
|
480
|
+
if (!gather) continue;
|
|
459
481
|
|
|
460
482
|
// Clamp the per-module budget. A module declaring `contextTimeoutMs`
|
|
461
483
|
// legitimately needs more than the shared default (e.g. a retrieval
|
|
@@ -467,14 +489,43 @@ export class ModuleRegistry {
|
|
|
467
489
|
MAX_CONTEXT_TIMEOUT_MS,
|
|
468
490
|
);
|
|
469
491
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
492
|
+
let timedOut = false;
|
|
493
|
+
const slot = results.length;
|
|
494
|
+
results.push([]);
|
|
495
|
+
// Called here, before its timer is set, so its budget starts after its
|
|
496
|
+
// synchronous work; a call that throws is skipped as a rejection is.
|
|
497
|
+
let call: Promise<ModuleContextInjection[]>;
|
|
498
|
+
try {
|
|
499
|
+
call = Promise.resolve(gather.call(module, agentName));
|
|
500
|
+
} catch (err) {
|
|
501
|
+
call = Promise.reject(err);
|
|
502
|
+
}
|
|
470
503
|
const promise = Promise.race([
|
|
471
|
-
|
|
472
|
-
|
|
504
|
+
call.then(r => {
|
|
505
|
+
if (timedOut) return;
|
|
506
|
+
// Checked before it's kept, so an answer that isn't an array of
|
|
507
|
+
// objects skips the module, as does a system entry whose content
|
|
508
|
+
// isn't an array of objects: buildSystemPrompt reads every block
|
|
509
|
+
// of it, and one it can't read would fail the turn. Each array is
|
|
510
|
+
// read through Array.from, which reads a hole as `undefined`, as
|
|
511
|
+
// context-manager's compile does when it spreads the content.
|
|
512
|
+
if (!Array.isArray(r) || Array.from(r).some((entry) => entry === null || typeof entry !== 'object')) {
|
|
513
|
+
throw new TypeError(`gatherContext answered with ${Array.isArray(r) ? 'an array holding an entry that is not an object' : r === null ? 'null' : typeof r}, not an array of objects`);
|
|
514
|
+
}
|
|
515
|
+
const unreadable = r.find((entry) => entry.position === 'system'
|
|
516
|
+
&& !(Array.isArray(entry.content) && Array.from(entry.content).every((block) => block !== null && typeof block === 'object')));
|
|
517
|
+
if (unreadable) {
|
|
518
|
+
throw new TypeError(`gatherContext answered with a system injection in namespace ${logValue(unreadable.namespace)} whose content isn't an array of objects`);
|
|
519
|
+
}
|
|
473
520
|
this.noteBlocksNotShown(module.name, r);
|
|
521
|
+
results[slot] = [...r];
|
|
474
522
|
}),
|
|
475
523
|
new Promise<void>((_, rej) => {
|
|
476
524
|
timer = setTimeout(
|
|
477
|
-
() =>
|
|
525
|
+
() => {
|
|
526
|
+
timedOut = true;
|
|
527
|
+
rej(new Error(`gatherContext timed out after ${budgetMs}ms`));
|
|
528
|
+
},
|
|
478
529
|
budgetMs,
|
|
479
530
|
);
|
|
480
531
|
}),
|
|
@@ -486,7 +537,7 @@ export class ModuleRegistry {
|
|
|
486
537
|
}
|
|
487
538
|
|
|
488
539
|
await Promise.all(promises);
|
|
489
|
-
return
|
|
540
|
+
return results.flat();
|
|
490
541
|
}
|
|
491
542
|
|
|
492
543
|
/**
|
|
@@ -607,11 +658,11 @@ class ModuleContextImpl implements ModuleContext {
|
|
|
607
658
|
private registry: ModuleRegistry;
|
|
608
659
|
readonly isRestart: boolean;
|
|
609
660
|
private getAgentsFn: () => Agent[];
|
|
610
|
-
private addMessageFn: (participant: string, content: ContentBlock[], metadata?: MessageMetadata) => MessageId;
|
|
661
|
+
private addMessageFn: (participant: string, content: ContentBlock[], metadata?: MessageMetadata, opts?: { forAgent?: string }) => MessageId;
|
|
611
662
|
private editMessageFn: (id: MessageId, content: ContentBlock[]) => void;
|
|
612
663
|
private removeMessageFn: (id: MessageId) => void;
|
|
613
664
|
private getMessageFn: (id: MessageId) => ModuleMessage | null;
|
|
614
|
-
private queryMessagesFn: (filter: MessageQuery) => MessageQueryResult;
|
|
665
|
+
private queryMessagesFn: (filter: MessageQuery, opts?: { forAgent?: string }) => MessageQueryResult;
|
|
615
666
|
private pushEventFn: (event: ProcessEvent, moduleName?: string) => void;
|
|
616
667
|
private onTraceFn: (listener: TraceEventListener) => () => void;
|
|
617
668
|
private callToolFn: (call: ToolCall) => Promise<ToolResult>;
|
|
@@ -627,11 +678,11 @@ class ModuleContextImpl implements ModuleContext {
|
|
|
627
678
|
registry: ModuleRegistry,
|
|
628
679
|
isRestart: boolean,
|
|
629
680
|
getAgents: () => Agent[],
|
|
630
|
-
addMessage: (participant: string, content: ContentBlock[], metadata?: MessageMetadata) => MessageId,
|
|
681
|
+
addMessage: (participant: string, content: ContentBlock[], metadata?: MessageMetadata, opts?: { forAgent?: string }) => MessageId,
|
|
631
682
|
editMessage: (id: MessageId, content: ContentBlock[]) => void,
|
|
632
683
|
removeMessage: (id: MessageId) => void,
|
|
633
684
|
getMessage: (id: MessageId) => ModuleMessage | null,
|
|
634
|
-
queryMessages: (filter: MessageQuery) => MessageQueryResult,
|
|
685
|
+
queryMessages: (filter: MessageQuery, opts?: { forAgent?: string }) => MessageQueryResult,
|
|
635
686
|
pushEvent: (event: ProcessEvent, moduleName?: string) => void,
|
|
636
687
|
onTrace: (listener: TraceEventListener) => () => void,
|
|
637
688
|
callTool: (call: ToolCall) => Promise<ToolResult>
|
|
@@ -753,9 +804,17 @@ class ModuleContextImpl implements ModuleContext {
|
|
|
753
804
|
addMessage(
|
|
754
805
|
participant: string,
|
|
755
806
|
content: ContentBlock[],
|
|
756
|
-
metadata?: MessageMetadata & { external?: ExternalIdRef }
|
|
807
|
+
metadata?: MessageMetadata & { external?: ExternalIdRef },
|
|
808
|
+
opts?: { forAgent?: string },
|
|
757
809
|
): MessageId {
|
|
758
|
-
|
|
810
|
+
// The external id map is read in the primary's store
|
|
811
|
+
// (findMessageByExternalId), so a mapping into another window would
|
|
812
|
+
// answer wrong.
|
|
813
|
+
if (metadata?.external && opts?.forAgent !== undefined) {
|
|
814
|
+
throw new Error(`A message for agent ${JSON.stringify(opts.forAgent)} can't carry metadata.external: `
|
|
815
|
+
+ "its external id would be looked up in the primary's window. Leave forAgent out, or the external id.");
|
|
816
|
+
}
|
|
817
|
+
const id = this.addMessageFn(participant, content, metadata, opts);
|
|
759
818
|
|
|
760
819
|
// Track external ID if provided. A write the framework holds keeps '',
|
|
761
820
|
// as it did before held writes had handles: the map is module state an
|
|
@@ -819,8 +878,8 @@ class ModuleContextImpl implements ModuleContext {
|
|
|
819
878
|
return this.getMessageFn(id);
|
|
820
879
|
}
|
|
821
880
|
|
|
822
|
-
queryMessages(filter: MessageQuery): MessageQueryResult {
|
|
823
|
-
return this.queryMessagesFn(filter);
|
|
881
|
+
queryMessages(filter: MessageQuery, opts?: { forAgent?: string }): MessageQueryResult {
|
|
882
|
+
return this.queryMessagesFn(filter, opts);
|
|
824
883
|
}
|
|
825
884
|
|
|
826
885
|
getAgents(): AgentInfo[] {
|
|
@@ -976,7 +1035,7 @@ function withoutRefusal(call: ToolCall, result: ToolResult): ToolResult {
|
|
|
976
1035
|
}
|
|
977
1036
|
|
|
978
1037
|
/** A result's flags, which consumers read for their truth: booleans here. */
|
|
979
|
-
const RESULT_FLAGS = ['success', 'isError', 'endTurn'] as const;
|
|
1038
|
+
const RESULT_FLAGS = ['success', 'isError', 'endTurn', 'dataIsContent'] as const;
|
|
980
1039
|
|
|
981
1040
|
/**
|
|
982
1041
|
* A module's tool result as the framework can store, send and trace it. A
|
|
@@ -1047,7 +1106,7 @@ export function checkedToolResult(call: ToolCall, result: unknown): { result: To
|
|
|
1047
1106
|
endTurn = Boolean(fields.endTurn);
|
|
1048
1107
|
read('error');
|
|
1049
1108
|
ownText = errorText(fields.error);
|
|
1050
|
-
for (const key of ['success', 'isError', 'data']) read(key);
|
|
1109
|
+
for (const key of ['success', 'isError', 'data', 'dataIsContent']) read(key);
|
|
1051
1110
|
for (const key of Object.keys(candidate)) {
|
|
1052
1111
|
if (key !== 'toJSON' && !(key in fields)) fields[key] = candidate[key];
|
|
1053
1112
|
}
|
|
@@ -828,7 +828,7 @@ export class DiscordModule implements Module {
|
|
|
828
828
|
const stored = this.storedRow(msg.discordMessageId);
|
|
829
829
|
if (live) live.phase = 'writing';
|
|
830
830
|
|
|
831
|
-
// Return declarative response - framework applies
|
|
831
|
+
// Return declarative response - the framework applies its writes
|
|
832
832
|
return {
|
|
833
833
|
...(stored ? {} : {
|
|
834
834
|
addMessages: [
|
|
@@ -49,7 +49,7 @@ import type { ToolDefinition, ToolCall, ToolResult, ProcessEvent } from '../../t
|
|
|
49
49
|
import type { EventResponse, ProcessState } from '../../types/module.js';
|
|
50
50
|
import type { SearchWorkerMessage, SearchWorkerMatch } from './search-regex-worker.js';
|
|
51
51
|
import type { ChannelRegistry } from '../../mcpl/channel-registry.js';
|
|
52
|
-
import { EmbedServiceError, SemanticIndexClient, SemanticIndexer, messageIndexText, summaryIndexText, type SearchResponse, type SemanticIndexConfig, type SyncReport, type PendingChanges } from './semantic.js';
|
|
52
|
+
import { EmbedServiceError, SemanticIndexClient, SemanticIndexer, messageIndexText, parseSummaryKey, summaryIndexText, type SearchResponse, type SemanticIndexConfig, type SyncReport, type PendingChanges, type SyncCursors } from './semantic.js';
|
|
53
53
|
import { unknownToolMessage } from '../unknown-tool.js';
|
|
54
54
|
import { safeSlice } from '../../safe-slice.js';
|
|
55
55
|
|
|
@@ -535,7 +535,7 @@ export class HistoryModule implements Module {
|
|
|
535
535
|
channelId?: string;
|
|
536
536
|
n: number;
|
|
537
537
|
side: 'newest' | 'oldest';
|
|
538
|
-
}): { messages: StoredMessage[]; more: boolean } {
|
|
538
|
+
}): { messages: StoredMessage[]; more: boolean; total?: number } {
|
|
539
539
|
const cm = this.cm as ContextManager;
|
|
540
540
|
const { fromMs, toMs, channelId, n, side } = opts;
|
|
541
541
|
const newest = side === 'newest';
|
|
@@ -553,8 +553,8 @@ export class HistoryModule implements Module {
|
|
|
553
553
|
const fetch = (f: number | undefined, t: number | undefined, limit: number, offset = 0) =>
|
|
554
554
|
limit <= 0 ? [] : cm.queryMessagesByTimeAndChannel({ fromMs: f, toMs: t, channelId, limit, offset }).messages;
|
|
555
555
|
const total = count(fromMs, toMs);
|
|
556
|
-
if (n === 0) return { messages: [], more: total > 0 };
|
|
557
|
-
if (total <= n) return { messages: dirSort(fetch(fromMs, toMs, total)), more: false };
|
|
556
|
+
if (n === 0) return { messages: [], more: total > 0, total };
|
|
557
|
+
if (total <= n) return { messages: dirSort(fetch(fromMs, toMs, total)), more: false, total };
|
|
558
558
|
|
|
559
559
|
// The window is parameterised by its FAR bound `e`: [e, toMs] for the
|
|
560
560
|
// newest side, [fromMs, e] for the oldest; its count is monotone in e.
|
|
@@ -580,7 +580,7 @@ export class HistoryModule implements Module {
|
|
|
580
580
|
}
|
|
581
581
|
const fetchCap = Math.max(4 * n, n + 1000);
|
|
582
582
|
if (inCount <= fetchCap) {
|
|
583
|
-
return { messages: dirSort(fetch(...win(inside), inCount)).slice(0, n), more: true };
|
|
583
|
+
return { messages: dirSort(fetch(...win(inside), inCount)).slice(0, n), more: true, total };
|
|
584
584
|
}
|
|
585
585
|
// Overshoot: bisect the far bound. The empty side starts one past the
|
|
586
586
|
// range's near end (newest: past toMs / any date; oldest: before lo).
|
|
@@ -595,7 +595,7 @@ export class HistoryModule implements Module {
|
|
|
595
595
|
inside = mid;
|
|
596
596
|
inCount = c;
|
|
597
597
|
if (c <= fetchCap) {
|
|
598
|
-
return { messages: dirSort(fetch(...win(mid), c)).slice(0, n), more: true };
|
|
598
|
+
return { messages: dirSort(fetch(...win(mid), c)).slice(0, n), more: true, total };
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
601
|
}
|
|
@@ -607,7 +607,7 @@ export class HistoryModule implements Module {
|
|
|
607
607
|
const need = n - near.length;
|
|
608
608
|
const ties = count(inside, inside);
|
|
609
609
|
const edgeMs = fetch(inside, inside, need, newest ? Math.max(0, ties - need) : 0);
|
|
610
|
-
return { messages: dirSort([...near, ...edgeMs]).slice(0, n), more: true };
|
|
610
|
+
return { messages: dirSort([...near, ...edgeMs]).slice(0, n), more: true, total };
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
async start(ctx: ModuleContext): Promise<void> {
|
|
@@ -624,6 +624,16 @@ export class HistoryModule implements Module {
|
|
|
624
624
|
const prev = this.ctx?.getState<Record<string, unknown>>() ?? {};
|
|
625
625
|
this.ctx?.setState({ ...prev, semanticPending: p });
|
|
626
626
|
};
|
|
627
|
+
// Module state follows the store's branches, so each branch keeps its
|
|
628
|
+
// own sync cursors, and a branch made at an earlier point inherits them
|
|
629
|
+
// as they were there.
|
|
630
|
+
this.indexer.cursorStore = {
|
|
631
|
+
read: () => this.ctx?.getState<{ semanticCursors?: SyncCursors }>()?.semanticCursors ?? null,
|
|
632
|
+
write: (cursors) => {
|
|
633
|
+
const prev = this.ctx?.getState<Record<string, unknown>>() ?? {};
|
|
634
|
+
this.ctx?.setState({ ...prev, semanticCursors: cursors });
|
|
635
|
+
},
|
|
636
|
+
};
|
|
627
637
|
this.indexer.attach();
|
|
628
638
|
}
|
|
629
639
|
|
|
@@ -631,13 +641,16 @@ export class HistoryModule implements Module {
|
|
|
631
641
|
this.ctx = null;
|
|
632
642
|
if (this.syncTimer) { clearInterval(this.syncTimer); this.syncTimer = null; }
|
|
633
643
|
if (this.firstSyncTimer) { clearTimeout(this.firstSyncTimer); this.firstSyncTimer = null; }
|
|
634
|
-
|
|
644
|
+
// Unwired from the state it can no longer reach, the indexer goes on
|
|
645
|
+
// from the cursors it last wrote for each branch, kept in memory.
|
|
646
|
+
if (this.indexer) { this.indexer.onPendingChange = null; this.indexer.cursorStore = null; }
|
|
635
647
|
this.indexer?.dispose();
|
|
636
648
|
}
|
|
637
649
|
|
|
638
650
|
/**
|
|
639
651
|
* Background sync into the semantic index: every `syncIntervalMs` (default
|
|
640
|
-
* 60 s)
|
|
652
|
+
* 60 s) write at most `maxSyncPerTick` items to the index, counted as its
|
|
653
|
+
* doc says. Idempotent to call — starts
|
|
641
654
|
* once we have both a context-manager (bind) and a config; `unref`'d so it
|
|
642
655
|
* never keeps a shutting-down process alive. A first tick runs after 5 s so
|
|
643
656
|
* a fresh store starts backfilling immediately rather than a minute later.
|
|
@@ -1137,7 +1150,8 @@ export class HistoryModule implements Module {
|
|
|
1137
1150
|
hint:
|
|
1138
1151
|
(pageFull
|
|
1139
1152
|
? 'More matching messages exist. '
|
|
1140
|
-
: `Stopped after scanning ${scanned} messages of the window without reaching its end
|
|
1153
|
+
: `Stopped after scanning ${scanned} messages of the window without reaching its end` +
|
|
1154
|
+
(page.length === 0 ? ', so returning none is not a negative result for the window. ' : '. ')) +
|
|
1141
1155
|
`Continue by repeating this call with the same from/to/channelId/author plus ` +
|
|
1142
1156
|
`the fields of \`resume\` (windowOffset:${resume.windowOffset}, offset:${resume.offset}, afterId).`,
|
|
1143
1157
|
};
|
|
@@ -1360,6 +1374,7 @@ export class HistoryModule implements Module {
|
|
|
1360
1374
|
!!skipRange && m.timestamp.getTime() === edgeMs && m.sequence >= skipRange[0] && m.sequence <= skipRange[1];
|
|
1361
1375
|
let windowMessages: StoredMessage[];
|
|
1362
1376
|
let truncated: boolean;
|
|
1377
|
+
let unscanned: number | undefined;
|
|
1363
1378
|
{
|
|
1364
1379
|
// Over-fetch by the number of messages at the edge millisecond (the
|
|
1365
1380
|
// only ones that can be skipped), then drop the skipped ones.
|
|
@@ -1368,6 +1383,7 @@ export class HistoryModule implements Module {
|
|
|
1368
1383
|
const pool = skipRange ? w.messages.filter((m) => !isSkipped(m)) : w.messages;
|
|
1369
1384
|
truncated = w.more || pool.length > maxScan;
|
|
1370
1385
|
windowMessages = pool.slice(0, maxScan);
|
|
1386
|
+
if (w.total !== undefined) unscanned = w.total - (w.messages.length - pool.length) - windowMessages.length;
|
|
1371
1387
|
}
|
|
1372
1388
|
const candidatePoolSize = windowMessages.length;
|
|
1373
1389
|
const candidates = authorFilter ? windowMessages.filter(authorFilter) : windowMessages;
|
|
@@ -1381,7 +1397,7 @@ export class HistoryModule implements Module {
|
|
|
1381
1397
|
// the pool was never looked at) or the pool itself was truncated. The
|
|
1382
1398
|
// timestamp is the continuation point — from: (oldest) / to: (newest).
|
|
1383
1399
|
// Boundary is inclusive, so a message at exactly that instant may repeat.
|
|
1384
|
-
const resumeInfo = (scanned: number): Record<string, unknown> => {
|
|
1400
|
+
const resumeInfo = (scanned: number, matched: number): Record<string, unknown> => {
|
|
1385
1401
|
const stoppedEarly = scanned < candidates.length;
|
|
1386
1402
|
if (!stoppedEarly && !truncated) return {};
|
|
1387
1403
|
// Stopped at limit: the last candidate actually looked at. Scanned the
|
|
@@ -1419,9 +1435,16 @@ export class HistoryModule implements Module {
|
|
|
1419
1435
|
hint:
|
|
1420
1436
|
(stoppedEarly
|
|
1421
1437
|
? 'Stopped at limit; more candidates remain. '
|
|
1422
|
-
:
|
|
1438
|
+
: matched === 0
|
|
1439
|
+
? `No match among the ${order} ${candidatePoolSize} ${candidatePoolSize === 1 ? 'message' : 'messages'} ` +
|
|
1440
|
+
'scanned (maxScan); ' +
|
|
1441
|
+
(unscanned === undefined
|
|
1442
|
+
? 'the rest of this range was not searched'
|
|
1443
|
+
: `${unscanned} more in this range ${unscanned === 1 ? 'was' : 'were'} not searched`) +
|
|
1444
|
+
', so this is not a negative result for the range. '
|
|
1445
|
+
: `Only the ${order} ${candidatePoolSize} messages of this range were scanned (maxScan). `) +
|
|
1423
1446
|
`Continue by repeating the call with the fields of \`resume\` (${bound}:"${at}" and skipSequences), ` +
|
|
1424
|
-
|
|
1447
|
+
`or raise limit/maxScan, or narrow with ${[channelId === undefined ? 'channelId' : null, stoppedEarly && input.author === undefined ? 'author' : null, 'dates'].filter((w) => w !== null).join('/')}.`,
|
|
1425
1448
|
};
|
|
1426
1449
|
};
|
|
1427
1450
|
|
|
@@ -1436,7 +1459,7 @@ export class HistoryModule implements Module {
|
|
|
1436
1459
|
// on a deadline. See search-regex-worker.ts's header for the full
|
|
1437
1460
|
// rationale.
|
|
1438
1461
|
const { matches, scanned } = await this.searchWithRegexWorker(candidates, query, flags, limit);
|
|
1439
|
-
return { success: true, data: { scanned, ...poolInfo, truncated, ...resumeInfo(scanned), matches } };
|
|
1462
|
+
return { success: true, data: { scanned, ...poolInfo, truncated, ...resumeInfo(scanned, matches.length), matches } };
|
|
1440
1463
|
}
|
|
1441
1464
|
|
|
1442
1465
|
// Plain substring search (String.prototype.indexOf) is inherently
|
|
@@ -1468,7 +1491,7 @@ export class HistoryModule implements Module {
|
|
|
1468
1491
|
|
|
1469
1492
|
return {
|
|
1470
1493
|
success: true,
|
|
1471
|
-
data: { scanned, ...poolInfo, truncated, ...resumeInfo(scanned), matches },
|
|
1494
|
+
data: { scanned, ...poolInfo, truncated, ...resumeInfo(scanned, matches.length), matches },
|
|
1472
1495
|
};
|
|
1473
1496
|
}
|
|
1474
1497
|
|
|
@@ -1608,7 +1631,10 @@ export class HistoryModule implements Module {
|
|
|
1608
1631
|
);
|
|
1609
1632
|
}
|
|
1610
1633
|
|
|
1611
|
-
//
|
|
1634
|
+
// A catch-up bounded in what it writes, so the newest messages are
|
|
1635
|
+
// searchable without waiting on a backlog. It can re-send many items the
|
|
1636
|
+
// index holds unchanged, as on a branch's first pass (maxSyncPerTick's
|
|
1637
|
+
// doc), which costs a request per batch and no embedding.
|
|
1612
1638
|
let sync: SyncReport | null = null;
|
|
1613
1639
|
if (this.indexer) {
|
|
1614
1640
|
try { sync = await this.indexer.catchUp(this.semanticCfg.maxSyncBeforeSearch ?? 256); } catch { /* reported via indexer.lastError */ }
|
|
@@ -1672,9 +1698,10 @@ export class HistoryModule implements Module {
|
|
|
1672
1698
|
// to the model — getMessage/getSummary rebuild on branch switch, so an
|
|
1673
1699
|
// undone message, or a summary this branch doesn't hold, answers null.
|
|
1674
1700
|
// A branch made from an earlier point, as an undo makes one, can mint a
|
|
1675
|
-
// summary id another branch already used, so a summary
|
|
1676
|
-
//
|
|
1677
|
-
// k is small, so this is at most `limit` local
|
|
1701
|
+
// summary id another branch already used, so a summary's key carries its
|
|
1702
|
+
// creation time too, and a hit whose time isn't this branch's summary's
|
|
1703
|
+
// is another branch's. k is small, so this is at most `limit` local
|
|
1704
|
+
// lookups.
|
|
1678
1705
|
// A message hit whose current text has nothing left to index (edited
|
|
1679
1706
|
// blank, delete not yet applied) is dropped too, so its stale snippet is
|
|
1680
1707
|
// never served. Dropped hits are replaced by over-fetching: when the
|
|
@@ -1685,14 +1712,12 @@ export class HistoryModule implements Module {
|
|
|
1685
1712
|
// A message hit's snippet is rebuilt from the CURRENT local text, never
|
|
1686
1713
|
// the remote copy: an edit not yet synced (or one the index never got)
|
|
1687
1714
|
// must not leak a passage the edit removed. Only the ranking can be stale.
|
|
1688
|
-
// A summary hit's snippet is rebuilt from the local summary too
|
|
1689
|
-
// service that keeps only vectors and ids (no text) still yields one
|
|
1690
|
-
// a summary whose id another branch also minted shows this branch's text
|
|
1691
|
-
// in its snippet and chars.
|
|
1715
|
+
// A summary hit's snippet is rebuilt from the local summary too, so a
|
|
1716
|
+
// service that keeps only vectors and ids (no text) still yields one.
|
|
1692
1717
|
const currentText = new Map<string, string>();
|
|
1693
|
-
const keep = (h: { id: string }): 'ok' | 'offBranch' | 'stale' => {
|
|
1718
|
+
const keep = (h: { id: string }): 'ok' | 'offBranch' | 'stale' | 'superseded' => {
|
|
1694
1719
|
const msgId = /^msg:(.+)$/.exec(h.id)?.[1];
|
|
1695
|
-
const
|
|
1720
|
+
const sum = parseSummaryKey(h.id);
|
|
1696
1721
|
if (msgId !== undefined) {
|
|
1697
1722
|
const m = cm.getMessage(msgId);
|
|
1698
1723
|
if (!m) return 'offBranch';
|
|
@@ -1701,9 +1726,18 @@ export class HistoryModule implements Module {
|
|
|
1701
1726
|
currentText.set(h.id, text);
|
|
1702
1727
|
return 'ok';
|
|
1703
1728
|
}
|
|
1704
|
-
if (
|
|
1705
|
-
|
|
1729
|
+
if (sum !== null) {
|
|
1730
|
+
// An item from before keys carried the creation time is never a hit.
|
|
1731
|
+
// One whose id names no summary on this branch is another branch's,
|
|
1732
|
+
// and is counted as such. One whose id this branch has can hold this
|
|
1733
|
+
// branch's summary or another's under the same id, and isn't counted.
|
|
1734
|
+
// Until this branch's passes send the summary under its own key, as
|
|
1735
|
+
// the search's catch-up does within its budget after any queued
|
|
1736
|
+
// edits, a search can miss it.
|
|
1737
|
+
const s = cm.getSummary(sum.id);
|
|
1706
1738
|
if (!s) return 'offBranch';
|
|
1739
|
+
if (sum.createdMs === null) return 'superseded';
|
|
1740
|
+
if (s.created !== sum.createdMs) return 'offBranch';
|
|
1707
1741
|
const text = summaryIndexText(s, this.semanticCfg!);
|
|
1708
1742
|
// An empty text here is mostly defensive: context-manager drops a
|
|
1709
1743
|
// summary that's empty after trimming when it loads, and refuses to
|
|
@@ -1771,10 +1805,13 @@ export class HistoryModule implements Module {
|
|
|
1771
1805
|
const meta = typeof h.meta === 'object' && h.meta !== null && !Array.isArray(h.meta)
|
|
1772
1806
|
? h.meta as { participant?: unknown; author?: unknown }
|
|
1773
1807
|
: null;
|
|
1808
|
+
// A summary is shown as `sum:<id>`, as it was before its key carried its creation time.
|
|
1809
|
+
const sum = parseSummaryKey(h.id);
|
|
1810
|
+
const id = sum ? `sum:${sum.id}` : h.id;
|
|
1774
1811
|
const missing = [...(h.ts !== null && timestamp === null ? ['ts'] : []), ...(meta === null ? ['meta'] : [])];
|
|
1775
|
-
if (missing.length > 0) unread.push(`${shown(
|
|
1812
|
+
if (missing.length > 0) unread.push(`${shown(id)} (${missing.join(' and ')})`);
|
|
1776
1813
|
return {
|
|
1777
|
-
id
|
|
1814
|
+
id,
|
|
1778
1815
|
/** Raw message id for a `msg:` hit (null for summaries) — pass it to extract({ aroundId }). */
|
|
1779
1816
|
messageId: h.id.startsWith('msg:') ? h.id.slice(4) : null,
|
|
1780
1817
|
kind: h.kind,
|
|
@@ -1797,7 +1834,10 @@ export class HistoryModule implements Module {
|
|
|
1797
1834
|
hits,
|
|
1798
1835
|
index: {
|
|
1799
1836
|
indexed: res.count_indexed,
|
|
1800
|
-
|
|
1837
|
+
// The pass may have read another branch: one the search joined
|
|
1838
|
+
// that started before a switch, or one the search left while it
|
|
1839
|
+
// waited.
|
|
1840
|
+
behind: this.indexer === null ? null : sync && this.indexer.passedOn(sync) === this.indexer.branchId() ? sync.more : true,
|
|
1801
1841
|
syncedThisCall: sync ? sync.pushed : 0,
|
|
1802
1842
|
lastError: this.indexer?.lastError ?? null,
|
|
1803
1843
|
/** Hits the index returned for messages/summaries not on the current branch (undone, checked out past). */
|