@animalabs/agent-framework 0.27.0 → 0.29.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 +145 -41
- package/dist/src/agent.d.ts +29 -2
- package/dist/src/agent.d.ts.map +1 -1
- package/dist/src/agent.js +73 -12
- package/dist/src/agent.js.map +1 -1
- package/dist/src/api/mcp-server-core.d.ts.map +1 -1
- package/dist/src/api/mcp-server-core.js +3 -12
- 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 +11 -5
- package/dist/src/api/server.js.map +1 -1
- package/dist/src/auto-continue.d.ts +5 -2
- package/dist/src/auto-continue.d.ts.map +1 -1
- package/dist/src/auto-continue.js +9 -5
- package/dist/src/auto-continue.js.map +1 -1
- package/dist/src/code-execution/isolate/runner.d.ts +7 -5
- package/dist/src/code-execution/isolate/runner.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/runner.js +7 -5
- package/dist/src/code-execution/isolate/runner.js.map +1 -1
- package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
- package/dist/src/code-execution/isolate/scripts.js +3 -2
- 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 +468 -125
- package/dist/src/framework.d.ts.map +1 -1
- package/dist/src/framework.js +3651 -1111
- package/dist/src/framework.js.map +1 -1
- package/dist/src/gate/event-gate.d.ts +133 -13
- package/dist/src/gate/event-gate.d.ts.map +1 -1
- package/dist/src/gate/event-gate.js +654 -59
- 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/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 +173 -2
- package/dist/src/gate/types.d.ts.map +1 -1
- package/dist/src/heartbeat/heartbeat.d.ts +202 -0
- package/dist/src/heartbeat/heartbeat.d.ts.map +1 -0
- package/dist/src/heartbeat/heartbeat.js +778 -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/held-writes.d.ts +3 -3
- package/dist/src/held-writes.js +3 -3
- package/dist/src/host-presentation.d.ts +170 -0
- package/dist/src/host-presentation.d.ts.map +1 -0
- package/dist/src/host-presentation.js +561 -0
- package/dist/src/host-presentation.js.map +1 -0
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/line-reader.d.ts +4 -0
- package/dist/src/line-reader.d.ts.map +1 -0
- package/dist/src/line-reader.js +59 -0
- package/dist/src/line-reader.js.map +1 -0
- 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 +34 -6
- package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
- package/dist/src/mcpl/channel-registry.js +133 -54
- package/dist/src/mcpl/channel-registry.js.map +1 -1
- package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
- package/dist/src/mcpl/conversation-router.js +2 -1
- package/dist/src/mcpl/conversation-router.js.map +1 -1
- package/dist/src/mcpl/convert-block.d.ts +1 -0
- package/dist/src/mcpl/convert-block.d.ts.map +1 -1
- package/dist/src/mcpl/convert-block.js +16 -4
- package/dist/src/mcpl/convert-block.js.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
- package/dist/src/mcpl/hook-orchestrator.js +4 -3
- package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
- package/dist/src/mcpl/inbound-source.d.ts +29 -11
- package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
- package/dist/src/mcpl/inbound-source.js +43 -70
- package/dist/src/mcpl/inbound-source.js.map +1 -1
- package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
- package/dist/src/mcpl/modern-connection.js +11 -7
- package/dist/src/mcpl/modern-connection.js.map +1 -1
- package/dist/src/mcpl/protocol-family.d.ts +3 -2
- package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
- package/dist/src/mcpl/protocol-family.js +25 -3
- package/dist/src/mcpl/protocol-family.js.map +1 -1
- package/dist/src/mcpl/push-handler.js +2 -2
- package/dist/src/mcpl/push-handler.js.map +1 -1
- package/dist/src/mcpl/references.d.ts +12 -40
- package/dist/src/mcpl/references.d.ts.map +1 -1
- package/dist/src/mcpl/references.js +29 -23
- package/dist/src/mcpl/references.js.map +1 -1
- package/dist/src/mcpl/server-connection.d.ts +18 -8
- package/dist/src/mcpl/server-connection.d.ts.map +1 -1
- package/dist/src/mcpl/server-connection.js +70 -30
- package/dist/src/mcpl/server-connection.js.map +1 -1
- package/dist/src/mcpl/skip-reply-wake.d.ts +25 -0
- package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -1
- package/dist/src/mcpl/skip-reply-wake.js +25 -0
- package/dist/src/mcpl/skip-reply-wake.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-presence.d.ts +66 -14
- package/dist/src/mcpl/tool-presence.d.ts.map +1 -1
- package/dist/src/mcpl/tool-presence.js +150 -80
- package/dist/src/mcpl/tool-presence.js.map +1 -1
- package/dist/src/mcpl/tool-signature.d.ts +10 -3
- package/dist/src/mcpl/tool-signature.d.ts.map +1 -1
- package/dist/src/mcpl/tool-signature.js +60 -30
- package/dist/src/mcpl/tool-signature.js.map +1 -1
- package/dist/src/mcpl/toolset-host.d.ts +88 -55
- package/dist/src/mcpl/toolset-host.d.ts.map +1 -1
- package/dist/src/mcpl/toolset-host.js +220 -111
- package/dist/src/mcpl/toolset-host.js.map +1 -1
- package/dist/src/mcpl/toolsets-module.d.ts +27 -35
- package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
- package/dist/src/mcpl/toolsets-module.js +313 -91
- package/dist/src/mcpl/toolsets-module.js.map +1 -1
- package/dist/src/mcpl/transport.d.ts +1 -1
- package/dist/src/mcpl/transport.d.ts.map +1 -1
- package/dist/src/mcpl/transport.js +16 -16
- package/dist/src/mcpl/transport.js.map +1 -1
- package/dist/src/mcpl/types.d.ts +31 -13
- package/dist/src/mcpl/types.d.ts.map +1 -1
- package/dist/src/mcpl/types.js.map +1 -1
- package/dist/src/module-registry.d.ts +10 -8
- package/dist/src/module-registry.d.ts.map +1 -1
- package/dist/src/module-registry.js +15 -9
- package/dist/src/module-registry.js.map +1 -1
- package/dist/src/modules/api/index.d.ts.map +1 -1
- package/dist/src/modules/api/index.js +4 -2
- package/dist/src/modules/api/index.js.map +1 -1
- package/dist/src/modules/discord/index.d.ts.map +1 -1
- package/dist/src/modules/discord/index.js +33 -25
- package/dist/src/modules/discord/index.js.map +1 -1
- package/dist/src/modules/history/index.d.ts.map +1 -1
- package/dist/src/modules/history/index.js +30 -17
- package/dist/src/modules/history/index.js.map +1 -1
- package/dist/src/modules/sender-name.d.ts +7 -0
- package/dist/src/modules/sender-name.d.ts.map +1 -0
- package/dist/src/modules/sender-name.js +10 -0
- package/dist/src/modules/sender-name.js.map +1 -0
- package/dist/src/modules/workspace/answers.d.ts +26 -0
- package/dist/src/modules/workspace/answers.d.ts.map +1 -0
- package/dist/src/modules/workspace/answers.js +138 -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 +3 -0
- package/dist/src/modules/workspace/direct-write.d.ts.map +1 -1
- package/dist/src/modules/workspace/direct-write.js +59 -24
- package/dist/src/modules/workspace/direct-write.js.map +1 -1
- 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 +2 -1
- package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/edit-text.js +4 -2
- package/dist/src/modules/workspace/edit-text.js.map +1 -1
- package/dist/src/modules/workspace/effects.d.ts +4 -2
- package/dist/src/modules/workspace/effects.d.ts.map +1 -1
- package/dist/src/modules/workspace/effects.js +67 -50
- package/dist/src/modules/workspace/effects.js.map +1 -1
- package/dist/src/modules/workspace/index.d.ts +63 -51
- package/dist/src/modules/workspace/index.d.ts.map +1 -1
- package/dist/src/modules/workspace/index.js +980 -547
- package/dist/src/modules/workspace/index.js.map +1 -1
- package/dist/src/modules/workspace/observe.d.ts +125 -82
- package/dist/src/modules/workspace/observe.d.ts.map +1 -1
- package/dist/src/modules/workspace/observe.js +465 -241
- package/dist/src/modules/workspace/observe.js.map +1 -1
- package/dist/src/modules/workspace/read-page.d.ts.map +1 -1
- package/dist/src/modules/workspace/read-page.js +7 -1
- package/dist/src/modules/workspace/read-page.js.map +1 -1
- package/dist/src/modules/workspace/reconcile.d.ts +7 -1
- package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
- package/dist/src/modules/workspace/reconcile.js +156 -50
- package/dist/src/modules/workspace/reconcile.js.map +1 -1
- package/dist/src/modules/workspace/result-text.d.ts +1 -0
- package/dist/src/modules/workspace/result-text.d.ts.map +1 -1
- package/dist/src/modules/workspace/result-text.js.map +1 -1
- 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/types.d.ts +96 -43
- 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/notice-tags.d.ts +79 -0
- package/dist/src/notice-tags.d.ts.map +1 -0
- package/dist/src/notice-tags.js +83 -0
- package/dist/src/notice-tags.js.map +1 -0
- 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 +52 -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.js +1 -1
- package/dist/src/silent-heartbeat.d.ts +1 -53
- package/dist/src/silent-heartbeat.d.ts.map +1 -1
- package/dist/src/silent-heartbeat.js +56 -3
- package/dist/src/silent-heartbeat.js.map +1 -1
- package/dist/src/supplied-text.d.ts +35 -0
- package/dist/src/supplied-text.d.ts.map +1 -0
- package/dist/src/supplied-text.js +141 -0
- package/dist/src/supplied-text.js.map +1 -0
- package/dist/src/thinking-drops.d.ts +4 -11
- package/dist/src/thinking-drops.d.ts.map +1 -1
- package/dist/src/thinking-drops.js +7 -14
- package/dist/src/thinking-drops.js.map +1 -1
- 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 +39 -4
- package/dist/src/tool-args.js.map +1 -1
- package/dist/src/tool-image-ledger.d.ts.map +1 -1
- package/dist/src/tool-image-ledger.js +2 -1
- 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 +5 -4
- package/dist/src/tool-presentation.js.map +1 -1
- package/dist/src/tool-result-guard.d.ts +8 -4
- package/dist/src/tool-result-guard.d.ts.map +1 -1
- package/dist/src/tool-result-guard.js +23 -11
- package/dist/src/tool-result-guard.js.map +1 -1
- package/dist/src/tune-out/coordinator.d.ts +3 -2
- package/dist/src/tune-out/coordinator.d.ts.map +1 -1
- package/dist/src/tune-out/coordinator.js +24 -26
- 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 +63 -18
- package/dist/src/types/agent.d.ts.map +1 -1
- package/dist/src/types/events.d.ts +18 -8
- package/dist/src/types/events.d.ts.map +1 -1
- package/dist/src/types/framework.d.ts +58 -0
- package/dist/src/types/framework.d.ts.map +1 -1
- package/dist/src/types/module.d.ts +27 -13
- package/dist/src/types/module.d.ts.map +1 -1
- package/dist/src/types/trace.d.ts +34 -1
- package/dist/src/types/trace.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/agent.ts +76 -12
- package/src/api/mcp-server-core.ts +3 -14
- package/src/api/server.ts +11 -5
- package/src/auto-continue.ts +12 -5
- package/src/code-execution/isolate/runner.ts +7 -5
- package/src/code-execution/isolate/scripts.ts +3 -2
- package/src/cut-off-round.ts +98 -0
- package/src/framework.ts +3772 -1138
- package/src/gate/event-gate.ts +705 -60
- package/src/gate/index.ts +9 -1
- package/src/gate/reply-judge.ts +512 -0
- package/src/gate/types.ts +176 -3
- package/src/heartbeat/heartbeat.ts +843 -0
- package/src/heartbeat/reminder-time.ts +107 -0
- package/src/held-writes.ts +3 -3
- package/src/host-presentation.ts +605 -0
- package/src/index.ts +5 -1
- package/src/line-reader.ts +58 -0
- package/src/mcpl/capability-mask.ts +3 -2
- package/src/mcpl/channel-registry.ts +139 -53
- package/src/mcpl/conversation-router.ts +2 -1
- package/src/mcpl/convert-block.ts +17 -4
- package/src/mcpl/hook-orchestrator.ts +4 -3
- package/src/mcpl/inbound-source.ts +45 -70
- package/src/mcpl/modern-connection.ts +11 -7
- package/src/mcpl/protocol-family.ts +24 -3
- package/src/mcpl/push-handler.ts +2 -2
- package/src/mcpl/references.ts +31 -23
- package/src/mcpl/server-connection.ts +76 -34
- package/src/mcpl/skip-reply-wake.ts +35 -0
- package/src/mcpl/tags.ts +13 -2
- package/src/mcpl/tool-presence.ts +149 -82
- package/src/mcpl/tool-signature.ts +64 -32
- package/src/mcpl/toolset-host.ts +212 -110
- package/src/mcpl/toolsets-module.ts +277 -91
- package/src/mcpl/transport.ts +18 -14
- package/src/mcpl/types.ts +32 -13
- package/src/module-registry.ts +14 -9
- package/src/modules/api/index.ts +4 -2
- package/src/modules/discord/index.ts +37 -25
- package/src/modules/history/index.ts +30 -18
- package/src/modules/sender-name.ts +9 -0
- package/src/modules/workspace/answers.ts +151 -0
- package/src/modules/workspace/branch-intent.ts +10 -5
- package/src/modules/workspace/direct-write.ts +59 -23
- package/src/modules/workspace/disk-agreement.ts +52 -21
- package/src/modules/workspace/edit-text.ts +7 -5
- package/src/modules/workspace/effects.ts +69 -50
- package/src/modules/workspace/index.ts +966 -556
- package/src/modules/workspace/observe.ts +497 -242
- package/src/modules/workspace/read-page.ts +7 -1
- package/src/modules/workspace/reconcile.ts +184 -49
- package/src/modules/workspace/result-text.ts +2 -0
- package/src/modules/workspace/sync.ts +16 -0
- package/src/modules/workspace/types.ts +96 -43
- package/src/modules/workspace/watcher.ts +8 -3
- package/src/notice-tags.ts +84 -0
- package/src/prose-drafts.ts +4 -3
- package/src/reasoning-loss-notice.ts +64 -0
- package/src/reasoning-loss.ts +120 -0
- package/src/send-tools.ts +3 -2
- package/src/sent-rows.ts +1 -1
- package/src/silent-heartbeat.ts +4 -3
- package/src/supplied-text.ts +154 -0
- package/src/thinking-drops.ts +10 -16
- package/src/tool-args.ts +38 -4
- package/src/tool-image-ledger.ts +2 -1
- package/src/tool-presentation.ts +5 -4
- package/src/tool-result-guard.ts +26 -11
- package/src/tune-out/coordinator.ts +25 -27
- package/src/tune-out/tools.ts +4 -1
- package/src/types/agent.ts +63 -18
- package/src/types/events.ts +18 -8
- package/src/types/framework.ts +59 -0
- package/src/types/module.ts +28 -13
- package/src/types/trace.ts +36 -1
|
@@ -81,8 +81,9 @@ function anyMatches(patterns: string[], path: string): boolean {
|
|
|
81
81
|
return patterns.some((pattern) => patternMatchesPath(pattern, path));
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
/** Keys that are negotiation metadata,
|
|
85
|
-
|
|
84
|
+
/** Keys that are negotiation metadata, or RFC-014 declarations (toolHints,
|
|
85
|
+
* toolsets, surface), not grantable capabilities. */
|
|
86
|
+
const UNMASKABLE_KEYS = new Set(['version', 'featureSets', 'toolHints', 'toolsets', 'surface']);
|
|
86
87
|
|
|
87
88
|
/**
|
|
88
89
|
* Interior object paths whose object value is itself the capability, with
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
* - Build channel context for beforeInference params
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { NOTICE_TAG } from '../notice-tags.js';
|
|
16
17
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
17
|
-
import {
|
|
18
|
+
import { convertBlocks } from './convert-block.js';
|
|
19
|
+
import { cutToFit, markedText, markNoticeOpenings, nameToTypeBack, quotedValue, showsAsItself, suppliedValue } from '../supplied-text.js';
|
|
18
20
|
import type { JsStore } from '@animalabs/chronicle';
|
|
19
21
|
import { isDeepStrictEqual } from 'node:util';
|
|
20
22
|
import { ensureStateRegistered } from '../module-registry.js';
|
|
@@ -45,7 +47,7 @@ import { RefusalLines } from './refusal-lines.js';
|
|
|
45
47
|
import { CapabilityGrant } from './capability-grant.js';
|
|
46
48
|
import type { InboundSource } from './inbound-source.js';
|
|
47
49
|
import { McplRequestError, type McplServerConnection } from './server-connection.js';
|
|
48
|
-
import { readSkipReplyWake } from './skip-reply-wake.js';
|
|
50
|
+
import { readSkipReplyWake, skipReplyNote, type SkipReplyEarlierWake } from './skip-reply-wake.js';
|
|
49
51
|
import { isProseTargetToken } from './prose-grammar.js';
|
|
50
52
|
|
|
51
53
|
// ============================================================================
|
|
@@ -643,19 +645,21 @@ const CHANNEL_TOOL_DEFINITIONS: ToolDefinition[] = [
|
|
|
643
645
|
// Constructor Options
|
|
644
646
|
// ============================================================================
|
|
645
647
|
|
|
646
|
-
/** Bound connector values for notices, logs and an outcome's reason
|
|
647
|
-
*
|
|
648
|
-
*
|
|
648
|
+
/** Bound connector values for notices, logs and an outcome's reason, on one
|
|
649
|
+
* line with their notice openings marked (suppliedValue). A PublishOutcome
|
|
650
|
+
* keeps the connector's structured data (`detail`) as the connection gives
|
|
651
|
+
* it, its strings redacted (redactedData). */
|
|
649
652
|
export function connectorText(value: unknown): string {
|
|
650
653
|
let text: string;
|
|
651
654
|
try {
|
|
652
655
|
text = typeof value === 'string' ? value : JSON.stringify(value) ?? String(value);
|
|
653
656
|
} catch {
|
|
654
|
-
return
|
|
657
|
+
return `[${NOTICE_TAG.unserializableConnectorValue}]`;
|
|
655
658
|
}
|
|
656
659
|
const limit = 2000;
|
|
657
|
-
const suffix =
|
|
658
|
-
|
|
660
|
+
const suffix = `… [${NOTICE_TAG.truncated}]`;
|
|
661
|
+
const shown = suppliedValue(text);
|
|
662
|
+
return shown.length > limit ? cutToFit(shown, limit - suffix.length) + suffix : shown;
|
|
659
663
|
}
|
|
660
664
|
|
|
661
665
|
/**
|
|
@@ -688,6 +692,9 @@ export function sameChannel(a: ChannelRef | null | undefined, b: ChannelRef | nu
|
|
|
688
692
|
return a.serverId === b.serverId && a.channelId === b.channelId;
|
|
689
693
|
}
|
|
690
694
|
|
|
695
|
+
export const NO_CHANNEL_EXAMPLE = '#<channel>';
|
|
696
|
+
export const NO_PERSON_EXAMPLE = '@<person>';
|
|
697
|
+
|
|
691
698
|
/**
|
|
692
699
|
* A channel's server-qualified name, `serverId:channelId`. A name given as a
|
|
693
700
|
* string (a tool's channelId, a `>>` target, a host's call) may take this
|
|
@@ -699,12 +706,32 @@ export function qualifiedChannelName(ref: ChannelRef): string {
|
|
|
699
706
|
return `${ref.serverId}:${ref.channelId}`;
|
|
700
707
|
}
|
|
701
708
|
|
|
709
|
+
function markedHistory(history: readonly unknown[]): unknown[] {
|
|
710
|
+
return history.map((item) => {
|
|
711
|
+
if (item === null || typeof item !== 'object' || Array.isArray(item) || !('content' in item)) return item;
|
|
712
|
+
const { content } = item as { content: unknown };
|
|
713
|
+
if (Array.isArray(content)) return { ...item, content: markNoticeOpenings(content.map(withEmbeddedTextMarked) as object[]) };
|
|
714
|
+
return typeof content === 'string' ? { ...item, content: markedText(content) } : item;
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
function withEmbeddedTextMarked(block: unknown): unknown {
|
|
719
|
+
if (block === null || typeof block !== 'object' || (block as { type?: unknown }).type !== 'resource') return block;
|
|
720
|
+
const { resource } = block as { resource?: unknown };
|
|
721
|
+
if (resource === null || typeof resource !== 'object' || typeof (resource as { text?: unknown }).text !== 'string') return block;
|
|
722
|
+
return { ...block, resource: { ...resource, text: markedText((resource as { text: string }).text) } };
|
|
723
|
+
}
|
|
724
|
+
|
|
702
725
|
/** A channel told apart by its id and its server, for one no string picks
|
|
703
|
-
* alone: `"b:room" on server "a"
|
|
726
|
+
* alone or whose name wouldn't show as itself: `"b:room" on server "a"`,
|
|
727
|
+
* each a JSON literal a channel tool's channelId and serverId take back
|
|
728
|
+
* (quotedValue). No input takes the whole form for a name. */
|
|
704
729
|
function channelOnServer(ref: ChannelRef): string {
|
|
705
|
-
return `${
|
|
730
|
+
return `${quotedValue(ref.channelId)} on server ${quotedValue(ref.serverId)}`;
|
|
706
731
|
}
|
|
707
732
|
|
|
733
|
+
const CHANNEL_ON_SERVER_AT_END = /("(?:[^"\\]|\\.)*") on server ("(?:[^"\\]|\\.)*")$/;
|
|
734
|
+
|
|
708
735
|
/**
|
|
709
736
|
* What one channels/publish attempt established (ChannelRegistry.publish).
|
|
710
737
|
* - `delivered`: the connector returned `delivered: true`. A historical
|
|
@@ -1089,6 +1116,10 @@ export type ChannelToolOrigin =
|
|
|
1089
1116
|
* name one of them answers to by its id means that channel
|
|
1090
1117
|
* (ChannelRegistry.ownChannelNamed). */
|
|
1091
1118
|
own?: readonly ChannelRef[];
|
|
1119
|
+
/** For a skip_reply whose self-wake the framework armed: the heartbeat's
|
|
1120
|
+
* wake due before it, when there is one, which its confirmation names
|
|
1121
|
+
* (skipReplyNote). */
|
|
1122
|
+
earlierWake?: SkipReplyEarlierWake;
|
|
1092
1123
|
};
|
|
1093
1124
|
|
|
1094
1125
|
/** The machine decision sources a module-origin channel_close may record.
|
|
@@ -1894,7 +1925,7 @@ export class ChannelRegistry {
|
|
|
1894
1925
|
}
|
|
1895
1926
|
}
|
|
1896
1927
|
if (message.tags) message.tags = expandCoreTags(message.tags);
|
|
1897
|
-
const convertedContent: ContentBlock[] = message.content
|
|
1928
|
+
const convertedContent: ContentBlock[] = convertBlocks(message.content, 'attachment on channel message', serverId);
|
|
1898
1929
|
if (!emptyAllowed && isVisiblyEmptyConversion(message.content, convertedContent)) {
|
|
1899
1930
|
rejectEmpty();
|
|
1900
1931
|
continue;
|
|
@@ -1909,7 +1940,8 @@ export class ChannelRegistry {
|
|
|
1909
1940
|
const incomingEntry = this.channels.get(incomingKey)!;
|
|
1910
1941
|
const markAccepted = (firstView = false) => {
|
|
1911
1942
|
// Non-conversational markers (a reaction added or removed, an edit, a
|
|
1912
|
-
// deletion
|
|
1943
|
+
// deletion, and a chat:system notice such as a join or the weather)
|
|
1944
|
+
// are ABOUT a message or the place, not one the agent replies to: they
|
|
1913
1945
|
// must not retarget the default publish locus or become
|
|
1914
1946
|
// `context.incoming`'s reply anchor. An edit the coalescer delivered
|
|
1915
1947
|
// as the agent's first view counts as the message it replaced.
|
|
@@ -2385,7 +2417,10 @@ export class ChannelRegistry {
|
|
|
2385
2417
|
return this.handleToolJournal(input as { content?: string });
|
|
2386
2418
|
|
|
2387
2419
|
case 'skip_reply':
|
|
2388
|
-
return this.handleToolSkipReply(
|
|
2420
|
+
return this.handleToolSkipReply(
|
|
2421
|
+
input as { reason?: string; wake_in_seconds?: unknown },
|
|
2422
|
+
origin?.kind === 'agent' ? origin.earlierWake : undefined,
|
|
2423
|
+
);
|
|
2389
2424
|
|
|
2390
2425
|
default:
|
|
2391
2426
|
// Not every agent is given every channel tool, so the refusal points
|
|
@@ -2441,7 +2476,8 @@ export class ChannelRegistry {
|
|
|
2441
2476
|
// declared it inbound-only, since speech isn't posted there): a conversation
|
|
2442
2477
|
// fork's home channel, else this turn's channel (single trunk agent — item-3
|
|
2443
2478
|
// redux: its locus, else its triggering channel), else the global default
|
|
2444
|
-
// (
|
|
2479
|
+
// (a no-trigger turn's, but not a heartbeat's, whose turn takes none).
|
|
2480
|
+
// Without this, a fork
|
|
2445
2481
|
// was advertised the global locus but published somewhere else; and a trunk
|
|
2446
2482
|
// agent was told the wrong channel under concurrency.
|
|
2447
2483
|
// Each lookup is keyed on whether a name was given, not its truthiness:
|
|
@@ -3920,12 +3956,20 @@ export class ChannelRegistry {
|
|
|
3920
3956
|
* name, only where no registered channel takes that name, as a channel
|
|
3921
3957
|
* tool reads it or a `>>` target does (by label or raw id too), so the
|
|
3922
3958
|
* name can't send anything to another channel; else by its id and server.
|
|
3959
|
+
* A name that wouldn't show as itself (showsAsItself: one holding a line
|
|
3960
|
+
* break, a notice opening, a quote, a backslash or a lone surrogate, or
|
|
3961
|
+
* whitespace at its edges) is shown by its id and its server too, which
|
|
3962
|
+
* the channel tools take exactly and the prose resolver doesn't, so no name
|
|
3963
|
+
* shown can be another channel's.
|
|
3923
3964
|
*/
|
|
3924
3965
|
nameShown(ref: ChannelRef): string {
|
|
3925
3966
|
const entry = this.entryAt(ref);
|
|
3926
|
-
if (entry)
|
|
3967
|
+
if (entry) {
|
|
3968
|
+
const name = this.nameFor(entry);
|
|
3969
|
+
return name !== undefined && showsAsItself(name) ? name : channelOnServer(ref);
|
|
3970
|
+
}
|
|
3927
3971
|
for (const name of [ref.channelId, qualifiedChannelName(ref)]) {
|
|
3928
|
-
if (this.entriesNamed(name).length === 0 && !('channelId' in this.resolveProseTarget(name))) return name;
|
|
3972
|
+
if (showsAsItself(name) && this.entriesNamed(name).length === 0 && !('channelId' in this.resolveProseTarget(name))) return name;
|
|
3929
3973
|
}
|
|
3930
3974
|
return channelOnServer(ref);
|
|
3931
3975
|
}
|
|
@@ -3965,7 +4009,7 @@ export class ChannelRegistry {
|
|
|
3965
4009
|
private tuneOutEndText(entry: ChannelEntry, tool?: string): string {
|
|
3966
4010
|
const close = tool === 'channel_close';
|
|
3967
4011
|
return this.nameFor(entry) !== undefined
|
|
3968
|
-
? `Call tune_out with channelId ${this.
|
|
4012
|
+
? `Call tune_out with channelId ${quotedValue(this.nameFor(entry)!)} and mode "cancel" for it to resume normal ` +
|
|
3969
4013
|
`attention; cancelling delivers the diverted backlog${close ? ', and channel_close closes it after that.' : '.'}`
|
|
3970
4014
|
: 'No channelId names it alone, so tune_out can\'t cancel it: its tune-out ends by itself when its duration ' +
|
|
3971
4015
|
'runs out, or at the first wake past its wake budget, and its backlog is delivered then.' +
|
|
@@ -3978,7 +4022,7 @@ export class ChannelRegistry {
|
|
|
3978
4022
|
*/
|
|
3979
4023
|
quotedShown(ref: ChannelRef): string {
|
|
3980
4024
|
const shown = this.nameShown(ref);
|
|
3981
|
-
return shown === channelOnServer(ref) ? shown :
|
|
4025
|
+
return shown === channelOnServer(ref) ? shown : quotedValue(shown);
|
|
3982
4026
|
}
|
|
3983
4027
|
|
|
3984
4028
|
/**
|
|
@@ -4016,7 +4060,7 @@ export class ChannelRegistry {
|
|
|
4016
4060
|
const nothing = opts.nothing ?? 'nothing ran';
|
|
4017
4061
|
const names = entries.map((e) => this.nameFor(e));
|
|
4018
4062
|
if (names.every((n) => n !== undefined)) {
|
|
4019
|
-
const listed = names.map((n) =>
|
|
4063
|
+
const listed = names.map((n) => quotedValue(n!)).join(', ');
|
|
4020
4064
|
const servers = [...new Set(entries.map((e) => JSON.stringify(e.serverId)))];
|
|
4021
4065
|
// One server's two channels can answer to one name too, as `s`'s
|
|
4022
4066
|
// `s:room` and `room` both do to `s:room`; its server picks neither.
|
|
@@ -4037,8 +4081,8 @@ export class ChannelRegistry {
|
|
|
4037
4081
|
const own = names[i];
|
|
4038
4082
|
if (own === undefined && !opts.takesServerId) return `- ${where}: no channelId reaches it alone.`;
|
|
4039
4083
|
return own !== undefined
|
|
4040
|
-
? `- ${where}: call again with channelId ${
|
|
4041
|
-
: `- ${where}: call again with channelId ${
|
|
4084
|
+
? `- ${where}: call again with channelId ${quotedValue(own)}.`
|
|
4085
|
+
: `- ${where}: call again with channelId ${quotedValue(e.descriptor.id)} and serverId ${JSON.stringify(e.serverId)}.`;
|
|
4042
4086
|
});
|
|
4043
4087
|
const count = entries.length === 2 ? 'either of two channels' : `any of ${entries.length} channels`;
|
|
4044
4088
|
return `Channel ${JSON.stringify(name)} could mean ${count}, so ${nothing}:\n${lines.join('\n')}`;
|
|
@@ -4077,7 +4121,7 @@ export class ChannelRegistry {
|
|
|
4077
4121
|
// so the text says whose.
|
|
4078
4122
|
if (!alone) return 'channel_list shows the channels and their servers.';
|
|
4079
4123
|
const one = named[0]!;
|
|
4080
|
-
const names = `${JSON.stringify(channelId)} alone names channel ${
|
|
4124
|
+
const names = `${JSON.stringify(channelId)} alone names channel ${quotedValue(one.descriptor.id)} on server `
|
|
4081
4125
|
+ JSON.stringify(one.serverId);
|
|
4082
4126
|
return takesServerId ? `${names}: for that one, call again with serverId left out.` : `${names}.`;
|
|
4083
4127
|
}
|
|
@@ -4089,7 +4133,7 @@ export class ChannelRegistry {
|
|
|
4089
4133
|
const places = elsewhere.map((e) => {
|
|
4090
4134
|
const at = `on server ${JSON.stringify(e.serverId)}`;
|
|
4091
4135
|
const name = this.nameFor(e);
|
|
4092
|
-
return name === undefined ? `${at} (no channelId reaches it alone)` : `${at} as ${
|
|
4136
|
+
return name === undefined ? `${at} (no channelId reaches it alone)` : `${at} as ${quotedValue(name)}`;
|
|
4093
4137
|
});
|
|
4094
4138
|
return `It's ${places.length === 1 ? places[0] : `${places.slice(0, -1).join(', ')} and ${places.at(-1)}`}.`;
|
|
4095
4139
|
}
|
|
@@ -4393,17 +4437,19 @@ export class ChannelRegistry {
|
|
|
4393
4437
|
spec: string,
|
|
4394
4438
|
opts?: { forSpeech?: boolean; own?: readonly ChannelRef[] },
|
|
4395
4439
|
): PublishDestination | { error: string; candidates?: string[]; refusedChannelId?: string } {
|
|
4396
|
-
const
|
|
4440
|
+
const name = spec.trim();
|
|
4441
|
+
if (name === NO_CHANNEL_EXAMPLE || name === NO_PERSON_EXAMPLE) return { error: `${name} is the example's placeholder: write the name it stands for in its place` };
|
|
4442
|
+
const own = this.ownChannelNamed(name, opts?.own);
|
|
4397
4443
|
let resolved: PublishDestination | { error: string; candidates?: string[] };
|
|
4398
4444
|
if (own === undefined) {
|
|
4399
|
-
resolved = this.resolveTargetByName(
|
|
4445
|
+
resolved = this.resolveTargetByName(name);
|
|
4400
4446
|
} else if ('neither' in own) {
|
|
4401
|
-
resolved = { error: `"${
|
|
4447
|
+
resolved = { error: `"${name}" is the id of this turn's channels on more than one server`, candidates: this.ownCandidates(own.neither).map((c) => c.prose) };
|
|
4402
4448
|
} else {
|
|
4403
4449
|
const entry = this.entryAt(own.channel);
|
|
4404
4450
|
resolved = entry
|
|
4405
4451
|
? { channelId: entry.descriptor.id, serverId: entry.serverId, label: entry.descriptor.label }
|
|
4406
|
-
: { error: `${this.ownChannelGoneText(
|
|
4452
|
+
: { error: `${this.ownChannelGoneText(name, own.channel)}, and nothing can be sent there until that server registers it again` };
|
|
4407
4453
|
}
|
|
4408
4454
|
if (!opts?.forSpeech || 'error' in resolved) return resolved;
|
|
4409
4455
|
const entry = this.entryAt(resolved);
|
|
@@ -4462,7 +4508,7 @@ export class ChannelRegistry {
|
|
|
4462
4508
|
if (!mine) return undefined;
|
|
4463
4509
|
if ('neither' in mine) {
|
|
4464
4510
|
const done = tool === 'publish' ? 'was posted' : tool === 'deliver' ? 'was delivered' : 'ran';
|
|
4465
|
-
return { error: this.ownNeitherRefusal(name, mine.neither, done) };
|
|
4511
|
+
return { error: this.ownNeitherRefusal(name, mine.neither, done, false) };
|
|
4466
4512
|
}
|
|
4467
4513
|
return this.entryAt(mine.channel) ? { channel: mine.channel } : { error: this.ownChannelGoneRefusal(name, mine.channel, tool) };
|
|
4468
4514
|
}
|
|
@@ -4481,32 +4527,73 @@ export class ChannelRegistry {
|
|
|
4481
4527
|
/**
|
|
4482
4528
|
* The turn's channels a name means neither of (ownChannelNamed), each by a
|
|
4483
4529
|
* string that reaches it outside the turn's rule: its server-qualified
|
|
4484
|
-
* name, where that picks it alone, since its id is the name refused.
|
|
4485
|
-
*
|
|
4486
|
-
*
|
|
4530
|
+
* name, where that picks it alone, since its id is the name refused. `text`
|
|
4531
|
+
* is that name, which a refusal writes as JSON; `prose`, which a `>>`
|
|
4532
|
+
* target reads, is that name where it shows as itself, else its id and
|
|
4533
|
+
* server. One no string picks alone, and one its server no longer
|
|
4534
|
+
* registers, are named by id and server in both, saying so. `reaches` says
|
|
4535
|
+
* which a name reaches.
|
|
4487
4536
|
*/
|
|
4488
|
-
private ownCandidates(channels: readonly ChannelRef[]): Array<{ text: string; reaches: boolean }> {
|
|
4537
|
+
private ownCandidates(channels: readonly ChannelRef[]): Array<{ text: string; prose: string; reaches: boolean; byServer: boolean }> {
|
|
4489
4538
|
return channels.map((c) => {
|
|
4490
4539
|
const entry = this.entryAt(c);
|
|
4491
|
-
if (!entry)
|
|
4540
|
+
if (!entry) {
|
|
4541
|
+
const text = `${channelOnServer(c)} (its server no longer registers it)`;
|
|
4542
|
+
return { text, prose: text, reaches: false, byServer: false };
|
|
4543
|
+
}
|
|
4492
4544
|
const qualified = qualifiedChannelName(c);
|
|
4493
4545
|
const picked = this.entriesNamed(qualified);
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4546
|
+
if (picked.length !== 1 || picked[0] !== entry) {
|
|
4547
|
+
const text = `${channelOnServer(c)} (no channelId reaches it alone)`;
|
|
4548
|
+
return { text, prose: text, reaches: false, byServer: true };
|
|
4549
|
+
}
|
|
4550
|
+
return { text: qualified, prose: showsAsItself(qualified) ? qualified : channelOnServer(c), reaches: true, byServer: false };
|
|
4497
4551
|
});
|
|
4498
4552
|
}
|
|
4499
4553
|
|
|
4554
|
+
/**
|
|
4555
|
+
* What a refusal listing these candidates says of each one that is a
|
|
4556
|
+
* channel shown by its id and server: the channelId channel_publish reaches
|
|
4557
|
+
* it by, or that no channelId reaches it alone. That is its server-qualified
|
|
4558
|
+
* name where that picks it alone, since a bare id is what a turn's own
|
|
4559
|
+
* channels answer to first, whether or not their server still registers
|
|
4560
|
+
* them (ownChannelNamed), and else its id where that picks it alone and
|
|
4561
|
+
* isn't empty (channel_publish refuses an empty one). It leaves out a channel no
|
|
4562
|
+
* longer registered and a candidate that already says no channelId reaches
|
|
4563
|
+
* it alone, and is empty when nothing is left.
|
|
4564
|
+
*/
|
|
4565
|
+
publishRouteFor(candidates: readonly string[]): string {
|
|
4566
|
+
const parts = new Set<string>();
|
|
4567
|
+
for (const candidate of candidates) {
|
|
4568
|
+
const shown = CHANNEL_ON_SERVER_AT_END.exec(candidate);
|
|
4569
|
+
if (!shown) continue;
|
|
4570
|
+
const ref: ChannelRef = { channelId: JSON.parse(shown[1]!) as string, serverId: JSON.parse(shown[2]!) as string };
|
|
4571
|
+
const entry = this.entryAt(ref);
|
|
4572
|
+
if (!entry) continue;
|
|
4573
|
+
const name = [qualifiedChannelName(ref), ref.channelId].find((n) => {
|
|
4574
|
+
const named = n === '' ? [] : this.entriesNamed(n);
|
|
4575
|
+
return named.length === 1 && named[0] === entry;
|
|
4576
|
+
});
|
|
4577
|
+
parts.add(name !== undefined
|
|
4578
|
+
? `channel_publish reaches ${channelOnServer(ref)} with channelId ${quotedValue(name)}`
|
|
4579
|
+
: `no channelId reaches ${channelOnServer(ref)} alone`);
|
|
4580
|
+
}
|
|
4581
|
+
return parts.size ? ` For a channel shown by its id and server: ${[...parts].join('; ')}.` : '';
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4500
4584
|
/** A tool's refusal of a name the turn's channels on more than one server
|
|
4501
4585
|
* answer to by their id (ownChannelNamed's neither). */
|
|
4502
|
-
private ownNeitherRefusal(name: string, channels: readonly ChannelRef[], done: string): string {
|
|
4586
|
+
private ownNeitherRefusal(name: string, channels: readonly ChannelRef[], done: string, takesServer = true): string {
|
|
4503
4587
|
const candidates = this.ownCandidates(channels);
|
|
4504
|
-
const named = candidates.filter((c) => c.reaches).map((c) =>
|
|
4588
|
+
const named = candidates.filter((c) => c.reaches).map((c) => quotedValue(c.text));
|
|
4505
4589
|
const pick = named.length === candidates.length ? ' Call again with channelId set to one of them.'
|
|
4506
4590
|
: named.length === 1 ? ` Call again with channelId ${named[0]}.`
|
|
4507
4591
|
: named.length > 1 ? ` Call again with channelId set to one of ${named.join(', ')}.` : '';
|
|
4508
|
-
const listed = candidates.map((c) => (c.reaches ?
|
|
4509
|
-
|
|
4592
|
+
const listed = candidates.map((c) => (c.reaches ? quotedValue(c.text) : c.text)).join(', ');
|
|
4593
|
+
const byServer = takesServer && candidates.some((c) => c.byServer)
|
|
4594
|
+
? ' For a channel shown by its id and server, call again with that channelId and that serverId.'
|
|
4595
|
+
: '';
|
|
4596
|
+
return `"${name}" is the id of this turn's channels on more than one server (${listed}), so nothing ${done}.${pick}${byServer}`;
|
|
4510
4597
|
}
|
|
4511
4598
|
|
|
4512
4599
|
private resolveTargetByName(
|
|
@@ -4520,7 +4607,7 @@ export class ChannelRegistry {
|
|
|
4520
4607
|
// A candidate is named by the token that picks it alone (nameFor), so
|
|
4521
4608
|
// two servers' channels with one id or one label can be told apart, or,
|
|
4522
4609
|
// where no string picks it alone, by its id and its server.
|
|
4523
|
-
const token = (e: ChannelEntry): string => this.
|
|
4610
|
+
const token = (e: ChannelEntry): string => this.nameShown(refOf(e));
|
|
4524
4611
|
// A DM is shown by its label, the person, where that label, given as a
|
|
4525
4612
|
// target, picks that DM. It doesn't where another channel answers to that
|
|
4526
4613
|
// label too, as two servers' DMs with one person do (a label is matched
|
|
@@ -4536,7 +4623,7 @@ export class ChannelRegistry {
|
|
|
4536
4623
|
};
|
|
4537
4624
|
const dmShown = (list: ChannelEntry[]): string[] => list.map((e) => {
|
|
4538
4625
|
const label = e.descriptor.label ?? e.descriptor.id;
|
|
4539
|
-
return labelPicks(e, label) ? label : token(e);
|
|
4626
|
+
return showsAsItself(label) && labelPicks(e, label) ? label : token(e);
|
|
4540
4627
|
});
|
|
4541
4628
|
|
|
4542
4629
|
// A channel id, or its server-qualified name (entriesNamed).
|
|
@@ -4654,7 +4741,7 @@ export class ChannelRegistry {
|
|
|
4654
4741
|
if (byName.length > 1) {
|
|
4655
4742
|
return {
|
|
4656
4743
|
error: `"${trimmed}" matches several channels`,
|
|
4657
|
-
candidates: byName.map((e) => `${e.descriptor.label} = ${token(e)}`),
|
|
4744
|
+
candidates: byName.map((e) => `${nameToTypeBack(e.descriptor.label ?? '')} = ${token(e)}`),
|
|
4658
4745
|
};
|
|
4659
4746
|
}
|
|
4660
4747
|
|
|
@@ -4663,7 +4750,7 @@ export class ChannelRegistry {
|
|
|
4663
4750
|
const near = entries
|
|
4664
4751
|
.filter((e) => norm(e.descriptor.label ?? '').includes(norm(trimmed)))
|
|
4665
4752
|
.slice(0, 5)
|
|
4666
|
-
.map((e) => `${e.descriptor.label} = ${token(e)}`);
|
|
4753
|
+
.map((e) => `${nameToTypeBack(e.descriptor.label ?? '')} = ${token(e)}`);
|
|
4667
4754
|
return { error: `no channel matches "${trimmed}"`, ...(near.length ? { candidates: near } : {}) };
|
|
4668
4755
|
}
|
|
4669
4756
|
|
|
@@ -4700,7 +4787,7 @@ export class ChannelRegistry {
|
|
|
4700
4787
|
qualifiedChannelName(ref),
|
|
4701
4788
|
];
|
|
4702
4789
|
for (const c of candidates) {
|
|
4703
|
-
if (!isProseTargetToken(c) || c === '#') continue;
|
|
4790
|
+
if (!isProseTargetToken(c) || c === '#' || !showsAsItself(c)) continue;
|
|
4704
4791
|
const r = this.resolveProseTarget(c, { forSpeech: true });
|
|
4705
4792
|
if ('channelId' in r && sameChannel(r, ref)) return c;
|
|
4706
4793
|
}
|
|
@@ -4989,7 +5076,7 @@ export class ChannelRegistry {
|
|
|
4989
5076
|
channelId: input.channelId,
|
|
4990
5077
|
serverId: entry.serverId,
|
|
4991
5078
|
status: alreadyDesiredOpen ? 'reconciled' : 'opened',
|
|
4992
|
-
...(result.history ? { history: result.history } : {}),
|
|
5079
|
+
...(result.history ? { history: markedHistory(result.history) } : {}),
|
|
4993
5080
|
...(result.historyTruncated ? { historyTruncated: true } : {}),
|
|
4994
5081
|
},
|
|
4995
5082
|
};
|
|
@@ -5478,13 +5565,14 @@ export class ChannelRegistry {
|
|
|
5478
5565
|
* tool, so any trailing prose this turn is NOT posted. Replaces the old
|
|
5479
5566
|
* overloaded use of `think` for staying silent.
|
|
5480
5567
|
*/
|
|
5481
|
-
private handleToolSkipReply(input: { reason?: string; wake_in_seconds?: unknown }): ToolResult {
|
|
5568
|
+
private handleToolSkipReply(input: { reason?: string; wake_in_seconds?: unknown }, earlierWake?: SkipReplyEarlierWake): ToolResult {
|
|
5482
5569
|
// The self-wake itself is armed by the framework (which owns the
|
|
5483
5570
|
// EventGate) BEFORE its dispatch reaches this handler, from the same
|
|
5484
5571
|
// reading, and the framework refuses a wake the host can't set before
|
|
5485
5572
|
// dispatching. A caller of this registry's handleChannelToolCall
|
|
5486
5573
|
// (AgentFramework.channels) goes past both, so its note names a wake
|
|
5487
|
-
// nothing armed.
|
|
5574
|
+
// nothing armed. The framework, which also owns the heartbeat, says
|
|
5575
|
+
// which of its wakes is due before the self-wake (`earlierWake`).
|
|
5488
5576
|
const wake = readSkipReplyWake(input);
|
|
5489
5577
|
const selfWake = wake.kind === 'wake' ? wake.seconds : undefined;
|
|
5490
5578
|
return {
|
|
@@ -5500,9 +5588,7 @@ export class ChannelRegistry {
|
|
|
5500
5588
|
// on Fable: ~60% of skip_reply result bytes were verbatim input echo).
|
|
5501
5589
|
data: {
|
|
5502
5590
|
skipped: true,
|
|
5503
|
-
note: selfWake !== undefined
|
|
5504
|
-
? `Turn ended; nothing sent. Self-wake in ~${selfWake}s unless something wakes you first.`
|
|
5505
|
-
: 'Turn ended; nothing sent.',
|
|
5591
|
+
note: skipReplyNote(selfWake, selfWake !== undefined ? earlierWake : undefined),
|
|
5506
5592
|
},
|
|
5507
5593
|
};
|
|
5508
5594
|
}
|
|
@@ -6095,7 +6181,7 @@ export class ChannelRegistry {
|
|
|
6095
6181
|
entry = this.entryAt(serverGiven ? { serverId: input.serverId!, channelId: fallback!.channelId } : fallback!);
|
|
6096
6182
|
} else if (own && 'neither' in own) {
|
|
6097
6183
|
if (seen) seen.refusal = 'shared-id';
|
|
6098
|
-
return { success: false, error: this.ownNeitherRefusal(input.channelId, own.neither, 'was posted'), isError: true };
|
|
6184
|
+
return { success: false, error: this.ownNeitherRefusal(input.channelId, own.neither, 'was posted', false), isError: true };
|
|
6099
6185
|
} else if (own) {
|
|
6100
6186
|
entry = this.entryAt(own.channel);
|
|
6101
6187
|
if (!entry) {
|
|
@@ -6126,7 +6212,7 @@ export class ChannelRegistry {
|
|
|
6126
6212
|
// channel with its id isn't where its message came from.
|
|
6127
6213
|
error: input.channelId === undefined
|
|
6128
6214
|
? serverGiven
|
|
6129
|
-
? `Server "${input.serverId}" has no channel
|
|
6215
|
+
? `Server "${input.serverId}" has no channel ${quotedValue(fallback!.channelId)}, the id of the channel this call would default to, so nothing was posted.`
|
|
6130
6216
|
: `The channel this would default to, ${this.quotedShown(fallback!)}, isn't registered now, so nothing was posted.`
|
|
6131
6217
|
// This tool's schema has no serverId, so one given here is a host's
|
|
6132
6218
|
// ref (publishForAgent), or one an agent added unasked, and the
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
* follow-up.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
+
import { NOTICE_TAG } from '../notice-tags.js';
|
|
37
38
|
import { createHash } from 'node:crypto';
|
|
38
39
|
import type { ContextStrategy } from '@animalabs/context-manager';
|
|
39
40
|
import type { ChannelDescriptor } from './types.js';
|
|
@@ -144,7 +145,7 @@ export type RoutingDecision =
|
|
|
144
145
|
const DEFAULT_IDLE_TTL_MS = 12 * 60 * 60 * 1000;
|
|
145
146
|
|
|
146
147
|
export const DEFAULT_CLOSURE_PROMPT =
|
|
147
|
-
|
|
148
|
+
`[${NOTICE_TAG.system}] This engagement is closing due to inactivity. Finalize your ` +
|
|
148
149
|
'work: post any promised results to the channel and record outstanding ' +
|
|
149
150
|
'threads as unresolved. Do not ask follow-up questions.';
|
|
150
151
|
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* about a malformed block holds on all three.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { NOTICE_TAG } from '../notice-tags.js';
|
|
7
8
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
8
9
|
import {
|
|
9
10
|
AUDIO_NO_DATA_TEXT,
|
|
@@ -18,6 +19,7 @@ import {
|
|
|
18
19
|
} from './references.js';
|
|
19
20
|
import { NON_STRING_TEXT_PLACEHOLDER } from './visible-content.js';
|
|
20
21
|
import type { McplContentBlock } from './types.js';
|
|
22
|
+
import { markNoticeOpenings } from '../supplied-text.js';
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Convert a single MCPL wire-format content block to a membrane ContentBlock.
|
|
@@ -50,7 +52,7 @@ export function convertBlock(block: McplContentBlock, provenance: string, server
|
|
|
50
52
|
// is a claim, one the reference rejects included, as classifyBlock
|
|
51
53
|
// and isInlineContradiction read it: a falsy one such as 0, false
|
|
52
54
|
// or '' stubs too, rather than sending the URI as the image's source.
|
|
53
|
-
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ??
|
|
55
|
+
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ?? `[${NOTICE_TAG.reference}]` };
|
|
54
56
|
}
|
|
55
57
|
if (isFilledString(block.data) && isFilledString(block.mimeType)) {
|
|
56
58
|
return {
|
|
@@ -76,7 +78,7 @@ export function convertBlock(block: McplContentBlock, provenance: string, server
|
|
|
76
78
|
// RFC-005 uri-form media with a disposition claim, a falsy one
|
|
77
79
|
// included, as for an image (above): stub, do not hand the URI to
|
|
78
80
|
// the provider or inline it.
|
|
79
|
-
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ??
|
|
81
|
+
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ?? `[${NOTICE_TAG.reference}]` };
|
|
80
82
|
}
|
|
81
83
|
if (isFilledString(block.data) && isFilledString(block.mimeType)) {
|
|
82
84
|
return {
|
|
@@ -95,14 +97,14 @@ export function convertBlock(block: McplContentBlock, provenance: string, server
|
|
|
95
97
|
}
|
|
96
98
|
// RFC-005: reference blocks become bounded stubs — never raw URIs
|
|
97
99
|
// (a signed URL is a bearer credential that looks like a location).
|
|
98
|
-
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ??
|
|
100
|
+
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ?? `[${NOTICE_TAG.reference}]` };
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
default: {
|
|
102
104
|
// MCP's link to a resource isn't one of MCPL's own blocks, but a server
|
|
103
105
|
// can send one, and RFC-005 §8.1 makes it a reference, shown as one.
|
|
104
106
|
if ((block as { type?: unknown }).type === 'resource_link') {
|
|
105
|
-
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ??
|
|
107
|
+
return { type: 'text', text: referenceStubOrNull(block, provenance, serverId) ?? `[${NOTICE_TAG.reference}]` };
|
|
106
108
|
}
|
|
107
109
|
// Unknown wire block types previously fell off the exhaustive switch
|
|
108
110
|
// and propagated `undefined` into ContentBlock[]. Fail visibly. A type
|
|
@@ -113,6 +115,17 @@ export function convertBlock(block: McplContentBlock, provenance: string, server
|
|
|
113
115
|
}
|
|
114
116
|
}
|
|
115
117
|
|
|
118
|
+
export function convertBlocks(blocks: readonly McplContentBlock[], provenance: string, serverId: string): ContentBlock[] {
|
|
119
|
+
const converted = blocks.map((block) => convertBlock(block, provenance, serverId));
|
|
120
|
+
return markNoticeOpenings(converted, (index) => sendersText(blocks[index]));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function sendersText(block: McplContentBlock | undefined): boolean {
|
|
124
|
+
if (block?.type === 'text') return typeof block.text === 'string';
|
|
125
|
+
const c = classifyBlock(block);
|
|
126
|
+
return c.kind === 'embedded' && c.contents.kind === 'block' && c.contents.block.type === 'text';
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
/** A string with something in it: the only data, mimeType or uri a block's
|
|
117
130
|
* source is built from, and the only uri that makes it a reference. Anything
|
|
118
131
|
* else a sender puts there leaves the block without it, so conversion never
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import type { ContentBlock } from '@animalabs/membrane';
|
|
17
|
-
import {
|
|
17
|
+
import { convertBlocks } from './convert-block.js';
|
|
18
|
+
import { markedText } from '../supplied-text.js';
|
|
18
19
|
import type { ContextInjection } from '@animalabs/context-manager';
|
|
19
20
|
|
|
20
21
|
import type {
|
|
@@ -64,9 +65,9 @@ function withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise
|
|
|
64
65
|
*/
|
|
65
66
|
function convertContent(content: string | McplContentBlock[], serverId: string): ContentBlock[] {
|
|
66
67
|
if (typeof content === 'string') {
|
|
67
|
-
return [{ type: 'text', text: content }];
|
|
68
|
+
return [{ type: 'text', text: markedText(content) }];
|
|
68
69
|
}
|
|
69
|
-
return content
|
|
70
|
+
return convertBlocks(content, 'attachment on context injection', serverId);
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/**
|