@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
|
@@ -1,31 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host's own interface to MCPL dynamic tools (RFC-014 §8.2): four tools,
|
|
3
|
+
* constant and static, aggregated across every server that presents
|
|
4
|
+
* toolsets.
|
|
5
|
+
*
|
|
6
|
+
* toolsets--open {server?, toolset?, instance?, options?}
|
|
7
|
+
* toolsets--act {server?, tool, instance?, args?}
|
|
8
|
+
* toolsets--look {server?, toolset?, instance?, tool?}
|
|
9
|
+
* toolsets--close {server?, toolset?, instance?}
|
|
10
|
+
*
|
|
11
|
+
* Dynamic tools never enter the API tool list: changing it rebuilds the
|
|
12
|
+
* prompt cache. They're announced in the conversation (in these tools'
|
|
13
|
+
* results, in the results of calls that changed them, in push events and in
|
|
14
|
+
* notices) and called through `toolsets--act`, which this module only
|
|
15
|
+
* resolves (resolveAct): the framework dispatches the call it resolves to
|
|
16
|
+
* down the same path as a direct call of the server's tool. The module is registered at create(),
|
|
17
|
+
* for every agent (the host's own toolsets use it too), unless a module given to create() holds
|
|
18
|
+
* the name; its tools never change after that.
|
|
19
|
+
*
|
|
20
|
+
* Its gatherContext publishes the RFC §8.3 reminder, as a stored `afterUser`
|
|
21
|
+
* injection, when an agent's earlier announcements of an open toolset have
|
|
22
|
+
* left its context (ToolsetHub.observeCompile).
|
|
23
|
+
*
|
|
24
|
+
* Ported from mcpl-cc-bridge's meta-tools.ts, the reference host.
|
|
25
|
+
*/
|
|
26
|
+
import { nameToTypeBack, quotedValue, suppliedValue } from '../supplied-text.js';
|
|
1
27
|
import { unknownToolMessage } from '../modules/unknown-tool.js';
|
|
2
28
|
import { META_TOOL_NAMES, TOOLSETS_MODULE } from './toolset-host.js';
|
|
3
29
|
import { keyOf } from './tool-presence.js';
|
|
4
|
-
import { checkArgs, renderToolDefinition } from './tool-signature.js';
|
|
30
|
+
import { checkArgs, jsonText, renderToolDefinition } from './tool-signature.js';
|
|
31
|
+
import { McplRequestError } from './server-connection.js';
|
|
32
|
+
import { BUILTIN_TOOLSET } from '../host-presentation.js';
|
|
5
33
|
/** The namespace a reminder's stored injection carries. */
|
|
6
34
|
export const TOOLSETS_REMINDER_NAMESPACE = 'mcpl-toolsets';
|
|
7
35
|
const META_TOOLS = [
|
|
8
36
|
{
|
|
9
37
|
name: 'open',
|
|
10
|
-
description: 'Open a toolset
|
|
11
|
-
'
|
|
12
|
-
'
|
|
38
|
+
description: 'Open a toolset so its tools can be called with toolsets--act: the host\'s own settings and controls (toolset "settings"), ' +
|
|
39
|
+
'an MCPL server\'s tab (server alone: its tools without putting them in your tool list; a closed tab is connected again; ' +
|
|
40
|
+
'full: true puts the server back in your tool list), or a toolset an MCPL server declares (a game, a building mode, a card table: ' +
|
|
41
|
+
'server and toolset). Returns the tools available now. instance names a separate copy where a toolset allows several; ' +
|
|
13
42
|
'options as toolsets--look lists them.',
|
|
14
43
|
inputSchema: {
|
|
15
44
|
type: 'object',
|
|
16
45
|
properties: {
|
|
17
46
|
server: { type: 'string', description: 'the MCPL server id' },
|
|
18
|
-
toolset: { type: 'string' },
|
|
47
|
+
toolset: { type: 'string', description: '"settings" for the host\'s own; a server\'s toolset with server' },
|
|
19
48
|
instance: { type: 'string', description: 'a name for this copy (toolsets with instances only)' },
|
|
20
49
|
options: { type: 'object', description: 'toolset options, as toolsets--look shows them' },
|
|
50
|
+
full: { type: 'boolean', description: 'with server alone: put the server\'s tools back in your tool list' },
|
|
21
51
|
},
|
|
22
52
|
},
|
|
23
53
|
},
|
|
24
54
|
{
|
|
25
55
|
name: 'act',
|
|
26
|
-
description: 'Call a tool
|
|
56
|
+
description: 'Call a tool of an open toolset (the host\'s "settings", a server\'s tab, or a server\'s own toolset) or another dynamic tool, as announced in the conversation; they are not in your tool list. ' +
|
|
27
57
|
'args are the arguments its signature shows; instance picks the open copy when several are open; server is needed only when two servers have the tool. ' +
|
|
28
|
-
'Returns the server\'s result, then any change in which tools are available.',
|
|
58
|
+
'Returns the server\'s result, then any change it made in which tools are available, unless that change already went out as a notice. toolsets--look shows what is available now.',
|
|
29
59
|
inputSchema: {
|
|
30
60
|
type: 'object',
|
|
31
61
|
properties: {
|
|
@@ -39,7 +69,7 @@ const META_TOOLS = [
|
|
|
39
69
|
},
|
|
40
70
|
{
|
|
41
71
|
name: 'look',
|
|
42
|
-
description: 'Show what is open and what can be opened
|
|
72
|
+
description: 'Show what is open and what can be opened (the host\'s "settings", each MCPL server\'s tab, servers\' own toolsets), with the tools usable now: announcements earlier in the conversation may be out of date. ' +
|
|
43
73
|
'With tool: that tool\'s full description and input schema. With toolset or instance: just that one.',
|
|
44
74
|
inputSchema: {
|
|
45
75
|
type: 'object',
|
|
@@ -53,18 +83,52 @@ const META_TOOLS = [
|
|
|
53
83
|
},
|
|
54
84
|
{
|
|
55
85
|
name: 'close',
|
|
56
|
-
description: 'Close an open toolset or instance: its tools and its events
|
|
86
|
+
description: 'Close an open toolset or instance: its tools stop, and for a toolset the server declares, the server is told and stops its events too. Closing a game in progress may count as leaving it. ' +
|
|
87
|
+
'server alone moves a server out of your tool list into a tab (connected, its events still come), or hides its open tab; with disconnect: true it is disconnected until you open it again.',
|
|
57
88
|
inputSchema: {
|
|
58
89
|
type: 'object',
|
|
59
90
|
properties: {
|
|
60
91
|
server: { type: 'string', description: 'the MCPL server id (may be left out when one server has the toolset open)' },
|
|
61
|
-
toolset: { type: 'string', description: 'may be left out when only one thing is open on the server' },
|
|
92
|
+
toolset: { type: 'string', description: '"settings" for the host\'s own; may be left out when only one thing is open on the server' },
|
|
62
93
|
instance: { type: 'string' },
|
|
94
|
+
disconnect: { type: 'boolean', description: 'with server alone: disconnect it as a closed tab until it is opened again' },
|
|
63
95
|
},
|
|
64
96
|
},
|
|
65
97
|
},
|
|
66
98
|
];
|
|
67
99
|
const str = (v) => (typeof v === 'string' && v.trim() ? v.trim() : undefined);
|
|
100
|
+
const selector = (v, has) => typeof v === 'string' && v !== v.trim() && v.trim() !== '' && has(v) ? v : str(v);
|
|
101
|
+
const presentersOf = (hub, server) => {
|
|
102
|
+
const id = str(server);
|
|
103
|
+
if (!id)
|
|
104
|
+
return hub.presenting();
|
|
105
|
+
const c = hub.get(id);
|
|
106
|
+
return c ? [c] : [];
|
|
107
|
+
};
|
|
108
|
+
const toolNamed = (hub, server, v) => selector(v, (name) => presentersOf(hub, server).some((c) => !!c.presence.tool(name)));
|
|
109
|
+
const toolsetNamed = (hub, server, v) => selector(v, (name) => presentersOf(hub, server).some((c) => c.presence.isDeclared(name) || c.presence.undeclaredGroups.includes(name)));
|
|
110
|
+
const instanceNamed = (presenters, toolset, v) => selector(v, (name) => presenters.some((c) => c.presence.openRecords(toolset).some((o) => o.instance === name)));
|
|
111
|
+
const SELECTORS = ['server', 'toolset', 'instance', 'tool'];
|
|
112
|
+
function checkSelectors(args) {
|
|
113
|
+
for (const key of SELECTORS) {
|
|
114
|
+
const v = args[key];
|
|
115
|
+
if (v === undefined || v === null || str(v) !== undefined)
|
|
116
|
+
continue;
|
|
117
|
+
const got = typeof v === 'string' ? 'a blank string' : Array.isArray(v) ? 'a list' : typeof v === 'object' ? 'an object' : `a ${typeof v}`;
|
|
118
|
+
fail(`${key} must be a name, or left out; got ${got}, so nothing was done`);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
/** A failure whose outcome is unknown, with how to find it out. */
|
|
122
|
+
export function withLookHint(serverId, text) {
|
|
123
|
+
const said = text.trimEnd();
|
|
124
|
+
return `${said}${/[.!?]$/.test(said) ? '' : '.'} ${META_TOOL_NAMES.look} {server:"${serverId}"} asks it what's open, or says it couldn't.`;
|
|
125
|
+
}
|
|
126
|
+
function requestFailure(serverId, did, unconfirmed, error) {
|
|
127
|
+
const message = suppliedValue(error instanceof Error ? error.message : String(error));
|
|
128
|
+
if (error instanceof McplRequestError && error.outcome === 'not-sent')
|
|
129
|
+
return fail(`${serverId} ${did}: ${message}`);
|
|
130
|
+
return fail(withLookHint(serverId, `${serverId} ${unconfirmed}: ${message}`));
|
|
131
|
+
}
|
|
68
132
|
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
69
133
|
class MetaError extends Error {
|
|
70
134
|
}
|
|
@@ -106,13 +170,13 @@ function pick(hub, server) {
|
|
|
106
170
|
if (!id) {
|
|
107
171
|
if (ids.length === 1)
|
|
108
172
|
return hub.get(ids[0]);
|
|
109
|
-
return fail(`name a server: ${ids.join(', ')
|
|
173
|
+
return fail(ids.length ? `name a server: ${ids.join(', ')}` : NONE_PRESENTS);
|
|
110
174
|
}
|
|
111
175
|
const c = hub.get(id);
|
|
112
176
|
if (!c)
|
|
113
177
|
return fail(`no server "${id}" presents toolsets; the ones that do: ${ids.join(', ') || 'none'}`);
|
|
114
178
|
if (!c.presence.active)
|
|
115
|
-
return fail(`${id} sends no tool hints now: its tools are
|
|
179
|
+
return fail(`${id} sends no tool hints now: its tools are called directly, by name, not through ${META_TOOL_NAMES.act}`);
|
|
116
180
|
return c;
|
|
117
181
|
}
|
|
118
182
|
/**
|
|
@@ -125,59 +189,71 @@ function owner(hub, server, has, what) {
|
|
|
125
189
|
const owners = hub.presenting().filter(has);
|
|
126
190
|
if (owners.length === 1)
|
|
127
191
|
return owners[0];
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
192
|
+
if (owners.length)
|
|
193
|
+
return fail(`${what} is on several servers (${owners.map((c) => c.serverId).join(', ')}): add server`);
|
|
194
|
+
return fail(hub.presenting().length ? `no server that presents toolsets has ${what}: ${META_TOOL_NAMES.look} shows what they have` : NONE_PRESENTS);
|
|
195
|
+
}
|
|
196
|
+
const NONE_PRESENTS = 'no MCPL server presents toolsets now: MCPL tools are called directly, by name';
|
|
197
|
+
function noToolset(c, toolset) {
|
|
198
|
+
const names = [...Object.keys(c.presence.declared), ...c.presence.undeclaredGroups];
|
|
199
|
+
return `${c.serverId} has no toolset "${toolset}"; ${names.length ? `it has ${names.map(nameToTypeBack).join(', ')}` : 'it has none'}`;
|
|
131
200
|
}
|
|
132
201
|
/**
|
|
133
202
|
* Resolve a `toolsets--act` call (RFC §8.2) to the dynamic tool it calls,
|
|
134
203
|
* or the reason it can't be sent. The framework then dispatches it exactly as
|
|
135
204
|
* a direct call of that tool, with `_meta["mcpl/instance"]` added (RFC §5.5).
|
|
136
205
|
* Refused here, before anything is sent: a tool no presenting server has, a
|
|
137
|
-
*
|
|
206
|
+
* tool neither the published list nor the server's latest tools/list puts as dynamic (RFC §4.2), an instance that isn't named where
|
|
138
207
|
* several are open, a group the host has closed (RFC §5.4), and arguments
|
|
139
208
|
* that don't fit the definition in force for that instance (RFC §6.1).
|
|
140
209
|
*/
|
|
210
|
+
const hasInstances = (p, ts) => ts !== undefined && p.isDeclared(ts) && !!p.declared[ts].instances;
|
|
211
|
+
/** The key whose definition act checks a call of `tool` against (RFC §6.1): an instance's own, else the toolset's, else the listed one. */
|
|
212
|
+
function actKey(p, tool, instance) {
|
|
213
|
+
const ts = p.toolsetOf(tool);
|
|
214
|
+
return ts ? keyOf(ts, hasInstances(p, ts) ? instance : undefined) : keyOf('');
|
|
215
|
+
}
|
|
141
216
|
export function resolveAct(hub, input) {
|
|
142
217
|
try {
|
|
143
218
|
const args = isObj(input) ? input : {};
|
|
144
|
-
|
|
219
|
+
checkSelectors(args);
|
|
220
|
+
const tool = toolNamed(hub, args.server, args.tool) ?? fail('name the tool');
|
|
145
221
|
const c = owner(hub, args.server, (x) => !!x.presence.tool(tool), `a tool "${tool}"`);
|
|
146
222
|
const p = c.presence;
|
|
147
223
|
if (!p.tool(tool))
|
|
148
224
|
fail(`${c.serverId} has no tool "${tool}": ${META_TOOL_NAMES.look} {server:"${c.serverId}"} shows what's available`);
|
|
149
|
-
if (p.
|
|
150
|
-
fail(`${tool} is not a dynamic tool: ${p.toolStatus(tool)}. Call it there.`);
|
|
225
|
+
if (!p.shownAs(tool, 'dynamic'))
|
|
226
|
+
fail(`${nameToTypeBack(tool)} is not a dynamic tool: ${p.toolStatus(tool)}. Call it there.`);
|
|
151
227
|
if (args.args !== undefined && !isObj(args.args))
|
|
152
228
|
fail('args must be an object of the tool\'s arguments');
|
|
153
229
|
const callArgs = (args.args ?? {});
|
|
154
|
-
let instance = str(args.instance);
|
|
155
230
|
const ts = p.toolsetOf(tool);
|
|
231
|
+
let instance = instanceNamed([c], ts, args.instance);
|
|
156
232
|
const declared = ts !== undefined && p.isDeclared(ts);
|
|
157
|
-
const withInstances =
|
|
233
|
+
const withInstances = hasInstances(p, ts);
|
|
158
234
|
if (withInstances && !instance) {
|
|
159
235
|
// RFC §5.5: the instance rides in _meta; with exactly one open, it's that one.
|
|
160
236
|
const open = p.openRecords(ts);
|
|
161
237
|
if (open.length === 1)
|
|
162
238
|
instance = open[0].instance;
|
|
163
239
|
else if (!open.length)
|
|
164
|
-
fail(`${tool} belongs to ${ts}, which isn't open: ${p.openHint(ts)}`);
|
|
240
|
+
fail(`${nameToTypeBack(tool)} belongs to ${nameToTypeBack(ts)}, which isn't open: ${p.openHint(ts)}`);
|
|
165
241
|
else
|
|
166
|
-
fail(`several ${ts} instances are open (${open.map((o) => o.instance).join(', ')}): say which with instance`);
|
|
242
|
+
fail(`several ${nameToTypeBack(ts)} instances are open (${open.map((o) => nameToTypeBack(o.instance)).join(', ')}): say which with instance`);
|
|
167
243
|
}
|
|
168
244
|
if (instance && !withInstances)
|
|
169
|
-
fail(`${tool} ${ts ? `belongs to ${ts}, which has no instances` : 'belongs to no toolset'}: call it without instance`);
|
|
245
|
+
fail(`${nameToTypeBack(tool)} ${ts ? `belongs to ${nameToTypeBack(ts)}, which has no instances` : 'belongs to no toolset'}: call it without instance`);
|
|
170
246
|
// RFC §5.4: the host gates calls into an undeclared group it has closed.
|
|
171
247
|
if (ts && !declared && !p.isOpen(ts))
|
|
172
|
-
fail(`${tool} is in ${ts}, which isn't open: ${p.openHint(ts)}`);
|
|
248
|
+
fail(`${nameToTypeBack(tool)} is in ${nameToTypeBack(ts)}, which isn't open: ${p.openHint(ts)}`);
|
|
173
249
|
// RFC §6.1: arguments are checked against the definition in force for
|
|
174
250
|
// that instance: its own from hints (the legal moves of one game), else
|
|
175
251
|
// the toolset's, else the listed one.
|
|
176
|
-
const key =
|
|
252
|
+
const key = actKey(p, tool, instance);
|
|
177
253
|
const problem = checkArgs(p.defOf(key, tool).inputSchema, callArgs);
|
|
178
254
|
if (problem) {
|
|
179
|
-
fail(`Invalid arguments for ${tool} in ${p.label(key)}: ${problem}. Nothing was sent; ` +
|
|
180
|
-
`${META_TOOL_NAMES.look} {server:"${c.serverId}", tool
|
|
255
|
+
fail(`Invalid arguments for ${nameToTypeBack(tool)} in ${p.label(key)}: ${problem}. Nothing was sent; ` +
|
|
256
|
+
`${META_TOOL_NAMES.look} {server:"${c.serverId}", tool:${quotedValue(tool)}${instance ? `, instance:${quotedValue(instance)}` : ''}} shows its current definition.`);
|
|
181
257
|
}
|
|
182
258
|
return { ok: true, serverId: c.serverId, tool, ...(instance ? { instance } : {}), args: callArgs };
|
|
183
259
|
}
|
|
@@ -189,9 +265,18 @@ export function resolveAct(hub, input) {
|
|
|
189
265
|
}
|
|
190
266
|
export class ToolsetsModule {
|
|
191
267
|
hub;
|
|
268
|
+
takerFor;
|
|
269
|
+
host;
|
|
192
270
|
name = TOOLSETS_MODULE;
|
|
193
|
-
|
|
271
|
+
/** `takerFor`: who shows a call's result to an agent, taking the tool
|
|
272
|
+
* change rendered into it; null when the result may reach none, and its
|
|
273
|
+
* change is left to a notice (RFC-014 §6.1: told once). */
|
|
274
|
+
constructor(hub, takerFor,
|
|
275
|
+
/** The host's own toolsets (the built-in "settings" and server tabs), when the framework presents them. */
|
|
276
|
+
host = null) {
|
|
194
277
|
this.hub = hub;
|
|
278
|
+
this.takerFor = takerFor;
|
|
279
|
+
this.host = host;
|
|
195
280
|
}
|
|
196
281
|
async start(_ctx) { }
|
|
197
282
|
async stop() { }
|
|
@@ -211,11 +296,25 @@ export class ToolsetsModule {
|
|
|
211
296
|
async handleToolCall(call) {
|
|
212
297
|
const args = isObj(call.input) ? call.input : {};
|
|
213
298
|
try {
|
|
299
|
+
checkSelectors(args);
|
|
300
|
+
const hosted = await this.hostCall(call.name, args, call.callerAgentName);
|
|
301
|
+
if (hosted)
|
|
302
|
+
return hosted;
|
|
303
|
+
const take = this.takerFor(call);
|
|
304
|
+
const lead = call.name === 'look' ? this.hostLookLead(args, call.callerAgentName) : null;
|
|
305
|
+
if (lead !== null) {
|
|
306
|
+
if (!this.hub.presenting().length)
|
|
307
|
+
return textResult(lead);
|
|
308
|
+
const theirs = await this.look(args, take);
|
|
309
|
+
if (theirs.isError || typeof theirs.data !== 'string')
|
|
310
|
+
return theirs;
|
|
311
|
+
return textResult(`${lead}\n\n${theirs.data}`);
|
|
312
|
+
}
|
|
214
313
|
switch (call.name) {
|
|
215
|
-
case 'open': return await this.open(args);
|
|
314
|
+
case 'open': return await this.open(args, take);
|
|
216
315
|
case 'act': return this.act(args);
|
|
217
|
-
case 'look': return await this.look(args);
|
|
218
|
-
case 'close': return await this.close(args);
|
|
316
|
+
case 'look': return await this.look(args, take);
|
|
317
|
+
case 'close': return await this.close(args, take);
|
|
219
318
|
default: return { success: false, isError: true, error: unknownToolMessage(this.name, call.name, META_TOOLS) };
|
|
220
319
|
}
|
|
221
320
|
}
|
|
@@ -226,16 +325,87 @@ export class ToolsetsModule {
|
|
|
226
325
|
return { success: false, isError: true, error: message };
|
|
227
326
|
}
|
|
228
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* The host's own toolsets: `toolset: "settings"` alone, or `server` alone
|
|
330
|
+
* (a server's tab), for open, close and look; a tool a host toolset has, for
|
|
331
|
+
* look. Null for everything else, which the servers' toolsets answer.
|
|
332
|
+
*/
|
|
333
|
+
async hostCall(name, args, agentName) {
|
|
334
|
+
const host = this.host;
|
|
335
|
+
if (!host || !agentName || !host.active(agentName))
|
|
336
|
+
return null;
|
|
337
|
+
const server = str(args.server);
|
|
338
|
+
const toolset = str(args.toolset);
|
|
339
|
+
const instance = str(args.instance);
|
|
340
|
+
const tool = str(args.tool);
|
|
341
|
+
const builtin = toolset === BUILTIN_TOOLSET && !server && !instance;
|
|
342
|
+
const tab = !!server && !toolset && !instance && args.options === undefined;
|
|
343
|
+
if (args.full !== undefined && typeof args.full !== 'boolean')
|
|
344
|
+
fail('full must be true or false');
|
|
345
|
+
if (args.disconnect !== undefined && typeof args.disconnect !== 'boolean')
|
|
346
|
+
fail('disconnect must be true or false');
|
|
347
|
+
if (tab && !host.knows(server) && !this.hub.get(server))
|
|
348
|
+
fail(`no MCPL server "${server}" is configured here`);
|
|
349
|
+
switch (name) {
|
|
350
|
+
case 'open':
|
|
351
|
+
if (builtin)
|
|
352
|
+
return textResult(host.openBuiltin(agentName));
|
|
353
|
+
if (tab)
|
|
354
|
+
return textResult(await host.openServer(agentName, server, { full: args.full === true }));
|
|
355
|
+
if (args.full !== undefined)
|
|
356
|
+
fail('full goes with server alone');
|
|
357
|
+
return null;
|
|
358
|
+
case 'close':
|
|
359
|
+
if (builtin)
|
|
360
|
+
return textResult(host.closeBuiltin(agentName));
|
|
361
|
+
if (tab)
|
|
362
|
+
return textResult(await host.closeServer(agentName, server, { disconnect: args.disconnect === true }));
|
|
363
|
+
if (args.disconnect !== undefined)
|
|
364
|
+
fail('disconnect goes with server alone');
|
|
365
|
+
return null;
|
|
366
|
+
case 'look': {
|
|
367
|
+
// A host toolset's tool, with or without its server named (as act takes it).
|
|
368
|
+
if (tool && !toolset && !instance) {
|
|
369
|
+
const shown = host.lookTool(agentName, tool, server);
|
|
370
|
+
if (shown)
|
|
371
|
+
return textResult(shown);
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
if (builtin)
|
|
375
|
+
return textResult(host.lookToolset(agentName, { builtin: true }));
|
|
376
|
+
// A server's tab, and every host toolset with no selector, go first in
|
|
377
|
+
// what the servers' own look says (handleToolCall adds them).
|
|
378
|
+
if (tab && !tool && !this.hub.get(server)?.presents)
|
|
379
|
+
return textResult(host.lookToolset(agentName, { serverId: server }));
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
default:
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/** What the host's own toolsets add ahead of the servers' look: everything, or one presenting server's tab. */
|
|
387
|
+
hostLookLead(args, agentName) {
|
|
388
|
+
const host = this.host;
|
|
389
|
+
if (!host || !agentName || !host.active(agentName))
|
|
390
|
+
return null;
|
|
391
|
+
const server = str(args.server);
|
|
392
|
+
if (str(args.toolset) || str(args.instance) || str(args.tool))
|
|
393
|
+
return null;
|
|
394
|
+
if (!server)
|
|
395
|
+
return host.catalog(agentName).join('\n');
|
|
396
|
+
return host.knows(server) ? host.lookToolset(agentName, { serverId: server }) : null;
|
|
397
|
+
}
|
|
229
398
|
toolsAt(c, key) {
|
|
230
399
|
return [...(c.presence.view().get(key)?.tools.values() ?? [])];
|
|
231
400
|
}
|
|
232
401
|
/** A change set rendered without the key the caller already showed whole. */
|
|
233
|
-
renderOthers(c, cs, key) {
|
|
234
|
-
return c.presence.render({ ...cs, changes: cs.changes.filter((ch) => ch.key !== key) });
|
|
402
|
+
renderOthers(c, cs, key, shown) {
|
|
403
|
+
return c.presence.render({ ...cs, changes: cs.changes.filter((ch) => ch.key !== key) }, { mark: shown, describeAll: !shown });
|
|
235
404
|
}
|
|
236
405
|
// ── the tools ──
|
|
237
|
-
async open(args) {
|
|
238
|
-
|
|
406
|
+
async open(args, take) {
|
|
407
|
+
const shown = take !== null;
|
|
408
|
+
let toolset = toolsetNamed(this.hub, args.server, args.toolset);
|
|
239
409
|
const c = toolset && !str(args.server)
|
|
240
410
|
? owner(this.hub, undefined, (x) => x.presence.isDeclared(toolset) || x.presence.undeclaredGroups.includes(toolset), `a toolset "${toolset}"`)
|
|
241
411
|
: pick(this.hub, args.server);
|
|
@@ -243,55 +413,60 @@ export class ToolsetsModule {
|
|
|
243
413
|
const openable = [...Object.keys(p.declared), ...p.undeclaredGroups];
|
|
244
414
|
if (!toolset) {
|
|
245
415
|
if (openable.length !== 1)
|
|
246
|
-
fail(`${c.serverId} has ${openable.length ? `several toolsets: name one of ${openable.join(', ')}` : 'no toolsets'}`);
|
|
416
|
+
fail(`${c.serverId} has ${openable.length ? `several toolsets: name one of ${openable.map(nameToTypeBack).join(', ')}` : 'no toolsets'}`);
|
|
247
417
|
toolset = openable[0];
|
|
248
418
|
}
|
|
249
|
-
const instance =
|
|
419
|
+
const instance = instanceNamed([c], toolset, args.instance);
|
|
250
420
|
if (args.options !== undefined && !isObj(args.options))
|
|
251
421
|
fail('options must be an object');
|
|
252
422
|
const options = args.options;
|
|
253
423
|
if (p.isDeclared(toolset)) {
|
|
254
424
|
if (instance && !p.declared[toolset].instances)
|
|
255
|
-
fail(`${c.serverId}/${toolset} has no instances: open it without one`);
|
|
425
|
+
fail(`${c.serverId}/${nameToTypeBack(toolset)} has no instances: open it without one`);
|
|
256
426
|
let opened;
|
|
257
427
|
try {
|
|
258
|
-
opened = await c.open(toolset, instance, options);
|
|
428
|
+
opened = await c.open(toolset, instance, options, { shown });
|
|
259
429
|
}
|
|
260
430
|
catch (error) {
|
|
261
|
-
return
|
|
431
|
+
return requestFailure(c.serverId, `did not open ${nameToTypeBack(toolset)}`, `didn't confirm opening ${nameToTypeBack(toolset)}`, error);
|
|
262
432
|
}
|
|
263
433
|
// RFC §5.2: opening what is open adopts it; its content says which options weren't applied.
|
|
264
434
|
const blocks = [
|
|
265
|
-
{ type: 'text', text: `${p.label(opened.key)}${opened.title ? ` ${opened.title}` : ''}${opened.adopted ? ' (already open)' : ''}` },
|
|
435
|
+
{ type: 'text', text: `${p.label(opened.key)}${opened.title ? ` ${suppliedValue(opened.title)}` : ''}${opened.adopted ? ' (already open)' : ''}` },
|
|
266
436
|
...asBlocks(opened.content),
|
|
267
437
|
];
|
|
268
|
-
const tail = [...p.toolBlock(opened.key, this.toolsAt(c, opened.key), { headed: false })];
|
|
438
|
+
const tail = [...p.toolBlock(opened.key, this.toolsAt(c, opened.key), { headed: false, describeAll: !shown, seen: shown ? undefined : new Set() })];
|
|
269
439
|
const gone = opened.change.changes.find((ch) => ch.key === opened.key)?.gone ?? [];
|
|
270
440
|
if (gone.length)
|
|
271
|
-
tail.push(`now unavailable: ${gone.join(', ')}`);
|
|
272
|
-
const others = this.renderOthers(c, opened.change, opened.key);
|
|
441
|
+
tail.push(`now unavailable: ${gone.map(suppliedValue).join(', ')}`);
|
|
442
|
+
const others = this.renderOthers(c, opened.change, opened.key, shown);
|
|
273
443
|
if (others)
|
|
274
444
|
tail.push(others);
|
|
275
445
|
const close = p.closeHint(opened.key);
|
|
276
446
|
if (close)
|
|
277
447
|
tail.push(`${close} closes it.`);
|
|
278
448
|
blocks.push({ type: 'text', text: tail.join('\n') });
|
|
449
|
+
take?.({ toolsets: c, cs: opened.change });
|
|
279
450
|
return blocksResult(blocks);
|
|
280
451
|
}
|
|
281
452
|
if (p.undeclaredGroups.includes(toolset)) {
|
|
282
453
|
// RFC §5.4: a grouping the server offers but doesn't track: the host opens it, the server isn't told.
|
|
283
454
|
if (instance || options)
|
|
284
|
-
fail(`${c.serverId}/${toolset} takes no instance or options (the server doesn't declare it)`);
|
|
455
|
+
fail(`${c.serverId}/${nameToTypeBack(toolset)} takes no instance or options (the server doesn't declare it)`);
|
|
285
456
|
const cs = c.setGroupOpen(toolset, true);
|
|
286
457
|
const key = keyOf(toolset);
|
|
287
|
-
const lines = [`${p.label(key)} open.`, ...p.toolBlock(key, this.toolsAt(c, key), { headed: false })];
|
|
288
|
-
const others = this.renderOthers(c, cs, key);
|
|
458
|
+
const lines = [`${p.label(key)} open.`, ...p.toolBlock(key, this.toolsAt(c, key), { headed: false, describeAll: !shown, seen: shown ? undefined : new Set() })];
|
|
459
|
+
const others = this.renderOthers(c, cs, key, shown);
|
|
289
460
|
if (others)
|
|
290
461
|
lines.push(others);
|
|
291
462
|
lines.push(`${p.closeHint(key)} closes it.`);
|
|
463
|
+
if (take)
|
|
464
|
+
take({ toolsets: c, cs });
|
|
465
|
+
else
|
|
466
|
+
c.tell(cs);
|
|
292
467
|
return textResult(lines.join('\n'));
|
|
293
468
|
}
|
|
294
|
-
return fail(
|
|
469
|
+
return fail(noToolset(c, toolset));
|
|
295
470
|
}
|
|
296
471
|
/**
|
|
297
472
|
* act is dispatched by the framework, not here: it resolves the call
|
|
@@ -307,42 +482,80 @@ export class ToolsetsModule {
|
|
|
307
482
|
return { success: false, isError: true, error: `${META_TOOL_NAMES.act} is dispatched by the framework; nothing was sent` };
|
|
308
483
|
}
|
|
309
484
|
/** look restates the present: titles and visibility are re-read from the servers first (toolsets/list). */
|
|
310
|
-
async look(args) {
|
|
485
|
+
async look(args, take) {
|
|
311
486
|
const server = str(args.server);
|
|
312
487
|
const targets = server ? [this.hub.get(server)].filter((c) => !!c && c.presents) : this.hub.presenting();
|
|
313
|
-
const
|
|
314
|
-
const
|
|
315
|
-
|
|
488
|
+
const shown = take !== null;
|
|
489
|
+
const reads = await Promise.all(targets.map(async (c) => ({ c, cs: await c.refreshOpen({ shown }).catch(() => null) })));
|
|
490
|
+
const read = reads.filter((r) => r.cs !== null);
|
|
491
|
+
const unread = reads.filter((r) => r.cs === null).map(({ c }) => `${c.serverId} couldn't be asked just now: this is what the host last knew of it.`);
|
|
492
|
+
const giveBack = () => {
|
|
493
|
+
if (shown)
|
|
494
|
+
for (const { c, cs } of read)
|
|
495
|
+
c.tell(cs);
|
|
496
|
+
};
|
|
497
|
+
let result;
|
|
498
|
+
try {
|
|
499
|
+
result = this.lookNow(args, server, toolNamed(this.hub, server, args.tool), shown);
|
|
500
|
+
}
|
|
501
|
+
catch (error) {
|
|
502
|
+
giveBack();
|
|
503
|
+
if (error instanceof MetaError && unread.length)
|
|
504
|
+
fail([error.message, ...unread].join('\n'));
|
|
505
|
+
throw error;
|
|
506
|
+
}
|
|
507
|
+
if (result.isError || typeof result.data !== 'string') {
|
|
508
|
+
giveBack();
|
|
509
|
+
return result;
|
|
510
|
+
}
|
|
511
|
+
const restates = !str(args.tool) && !str(args.toolset) && !str(args.instance);
|
|
512
|
+
if (!restates)
|
|
513
|
+
giveBack();
|
|
514
|
+
const lines = restates
|
|
515
|
+
? read.flatMap(({ c, cs }) => {
|
|
516
|
+
const said = [...cs.suggest.map((s) => c.presence.renderSuggestion(s)), ...c.presence.untoldNotes(cs), ...cs.notes];
|
|
517
|
+
// The catalog above counted what it showed; the change's own blocks weren't shown, but stay for a notice if its taker can't show the result.
|
|
518
|
+
take?.({ toolsets: c, cs: { ...cs, blocksShown: false } });
|
|
519
|
+
return said;
|
|
520
|
+
})
|
|
521
|
+
: [];
|
|
522
|
+
lines.push(...unread);
|
|
523
|
+
return lines.length ? textResult(`${result.data}\n${lines.join('\n')}`) : result;
|
|
316
524
|
}
|
|
317
|
-
lookNow(args, server, tool) {
|
|
525
|
+
lookNow(args, server, tool, mark) {
|
|
318
526
|
if (!server) {
|
|
319
|
-
if (tool)
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
527
|
+
if (tool) {
|
|
528
|
+
const c = owner(this.hub, undefined, (x) => !!x.presence.tool(tool), `a tool "${tool}"`);
|
|
529
|
+
return this.lookTool(c, tool, instanceNamed([c], c.presence.toolsetOf(tool), args.instance));
|
|
530
|
+
}
|
|
531
|
+
const ts = toolsetNamed(this.hub, undefined, args.toolset);
|
|
532
|
+
const inst = instanceNamed(this.hub.presenting(), ts, args.instance);
|
|
323
533
|
if (ts || inst) {
|
|
324
|
-
const c = owner(this.hub, undefined, (x) => (ts ? x.presence.isDeclared(ts) || x.presence.undeclaredGroups.includes(ts) : true) && (!inst || x.presence.openRecords().some((o) => o.instance === inst)), ts ? `a toolset "${ts}"${inst ? ` with instance "${inst}"` : ''}` : `an open instance "${inst}"`);
|
|
325
|
-
return this.lookNow(args, c.serverId, tool);
|
|
534
|
+
const c = owner(this.hub, undefined, (x) => (ts ? x.presence.isDeclared(ts) || x.presence.undeclaredGroups.includes(ts) : true) && (!inst || x.presence.openRecords(ts).some((o) => o.instance === inst)), ts ? `a toolset "${ts}"${inst ? ` with instance "${inst}"` : ''}` : `an open instance "${inst}"`);
|
|
535
|
+
return this.lookNow(args, c.serverId, tool, mark);
|
|
326
536
|
}
|
|
327
537
|
const all = this.hub.presenting();
|
|
328
538
|
if (!all.length)
|
|
329
|
-
return textResult('No MCPL server presents toolsets or dynamic tools now;
|
|
330
|
-
return textResult(all.map((c) => c.presence.catalog()).join('\n\n'));
|
|
539
|
+
return textResult('No MCPL server presents toolsets or dynamic tools now; MCPL tools are called directly, by name.');
|
|
540
|
+
return textResult(all.map((c) => c.presence.catalog({ mark })).join('\n\n'));
|
|
331
541
|
}
|
|
332
542
|
const c = pick(this.hub, server);
|
|
333
543
|
if (tool)
|
|
334
|
-
return this.lookTool(c, tool,
|
|
544
|
+
return this.lookTool(c, tool, instanceNamed([c], c.presence.toolsetOf(tool), args.instance));
|
|
335
545
|
const p = c.presence;
|
|
336
|
-
let toolset =
|
|
337
|
-
const instance =
|
|
338
|
-
if (!toolset && instance)
|
|
339
|
-
|
|
546
|
+
let toolset = toolsetNamed(this.hub, server, args.toolset);
|
|
547
|
+
const instance = instanceNamed([c], toolset, args.instance);
|
|
548
|
+
if (!toolset && instance) {
|
|
549
|
+
const open = p.openRecords().filter((o) => o.instance !== undefined);
|
|
550
|
+
toolset = open.find((o) => o.instance === instance)?.toolset
|
|
551
|
+
?? fail(`no open instance "${instance}" on ${c.serverId}; ${open.length ? `open: ${open.map((o) => `${nameToTypeBack(o.toolset)}:${nameToTypeBack(String(o.instance))}`).join(', ')}` : 'no instance is open there'}`);
|
|
552
|
+
}
|
|
340
553
|
if (!toolset)
|
|
341
|
-
return textResult(p.catalog());
|
|
554
|
+
return textResult(p.catalog({ mark }));
|
|
342
555
|
if (p.isOpen(toolset, instance) || (!instance && p.openRecords(toolset).length === 1)) {
|
|
343
556
|
const rec = p.isDeclared(toolset) ? (instance ? p.openRecords(toolset).find((o) => o.instance === instance) : p.openRecords(toolset)[0]) : undefined;
|
|
344
557
|
const key = keyOf(toolset, rec?.instance);
|
|
345
|
-
const lines = p.toolBlock(key, this.toolsAt(c, key), { describeAll: true, title: rec?.title });
|
|
558
|
+
const lines = p.toolBlock(key, this.toolsAt(c, key), { describeAll: true, title: rec?.title, seen: mark ? undefined : new Set() });
|
|
346
559
|
const close = p.closeHint(key);
|
|
347
560
|
if (close)
|
|
348
561
|
lines.push(`${close} closes it.`);
|
|
@@ -352,37 +565,39 @@ export class ToolsetsModule {
|
|
|
352
565
|
if (d) {
|
|
353
566
|
const open = p.openRecords(toolset);
|
|
354
567
|
return textResult([
|
|
355
|
-
`${c.serverId}/${toolset}${d.title ? ` — ${d.title}` : ''}${d.summary ? `: ${d.summary}` : ''}`,
|
|
356
|
-
open.length ? `open: ${open.map((o) => o.instance ?? toolset).join(', ')}` : 'not open',
|
|
568
|
+
`${c.serverId}/${nameToTypeBack(toolset)}${d.title ? ` — ${suppliedValue(d.title)}` : ''}${d.summary ? `: ${suppliedValue(d.summary)}` : ''}`,
|
|
569
|
+
open.length ? `open: ${open.map((o) => nameToTypeBack(o.instance ?? toolset)).join(', ')}` : 'not open',
|
|
357
570
|
...(d.instances ? ['several can be open at once; name each with instance'] : []),
|
|
358
|
-
...(d.options ? [`options: ${
|
|
571
|
+
...(d.options ? [`options: ${jsonText(d.options)}`] : []),
|
|
359
572
|
`${p.openHint(toolset)} opens it.`,
|
|
360
573
|
].join('\n'));
|
|
361
574
|
}
|
|
362
575
|
if (p.undeclaredGroups.includes(toolset)) {
|
|
363
576
|
const names = p.allTools.filter((t) => p.toolsetOf(t.name) === toolset).map((t) => t.name);
|
|
364
|
-
return textResult(`${c.serverId}/${toolset}: ${names.join(', ')}; not open: ${p.openHint(toolset)} opens it.`);
|
|
577
|
+
return textResult(`${c.serverId}/${nameToTypeBack(toolset)}: ${names.map(nameToTypeBack).join(', ')}; not open: ${p.openHint(toolset)} opens it.`);
|
|
365
578
|
}
|
|
366
|
-
return fail(
|
|
579
|
+
return fail(noToolset(c, toolset));
|
|
367
580
|
}
|
|
368
581
|
lookTool(c, tool, instance) {
|
|
369
582
|
const p = c.presence;
|
|
370
583
|
if (!p.tool(tool))
|
|
371
|
-
fail(`${c.serverId} has no tool "${tool}"`);
|
|
584
|
+
fail(`${c.serverId} has no tool "${tool}": ${META_TOOL_NAMES.look} {server:"${c.serverId}"} shows what's available`);
|
|
372
585
|
const ts = p.toolsetOf(tool);
|
|
373
|
-
|
|
374
|
-
const
|
|
586
|
+
// The definition act would check, wherever act would take the tool.
|
|
587
|
+
const open = !instance && hasInstances(p, ts) ? p.openRecords(ts) : [];
|
|
588
|
+
const key = !p.shownAs(tool, 'dynamic') ? undefined : actKey(p, tool, instance ?? (open.length === 1 ? open[0].instance : undefined));
|
|
375
589
|
const def = key ? p.defOf(key, tool) : p.tool(tool);
|
|
376
590
|
return textResult(`${renderToolDefinition(def)}\n${p.toolStatus(tool)}`);
|
|
377
591
|
}
|
|
378
|
-
async close(args) {
|
|
379
|
-
const
|
|
592
|
+
async close(args, take) {
|
|
593
|
+
const shown = take !== null;
|
|
594
|
+
const named = toolsetNamed(this.hub, args.server, args.toolset);
|
|
380
595
|
const c = named && !str(args.server)
|
|
381
|
-
? owner(this.hub, undefined, (x) => x.presence.isOpen(named,
|
|
596
|
+
? owner(this.hub, undefined, (x) => x.presence.isOpen(named, instanceNamed([x], named, args.instance)) || x.presence.openRecords(named).length > 0, `an open toolset "${named}"`)
|
|
382
597
|
: pick(this.hub, args.server);
|
|
383
598
|
const p = c.presence;
|
|
384
599
|
let toolset = named;
|
|
385
|
-
let instance =
|
|
600
|
+
let instance = instanceNamed([c], named, args.instance);
|
|
386
601
|
if (!toolset) {
|
|
387
602
|
const open = [
|
|
388
603
|
...p.openRecords().filter((o) => !instance || o.instance === instance),
|
|
@@ -390,7 +605,7 @@ export class ToolsetsModule {
|
|
|
390
605
|
];
|
|
391
606
|
if (open.length !== 1) {
|
|
392
607
|
fail(open.length
|
|
393
|
-
? `several are open on ${c.serverId}: name one of ${open.map((o) => `${o.toolset}${o.instance ? `:${o.instance}` : ''}`).join(', ')}`
|
|
608
|
+
? `several are open on ${c.serverId}: name one of ${open.map((o) => `${nameToTypeBack(o.toolset)}${o.instance ? `:${nameToTypeBack(o.instance)}` : ''}`).join(', ')}`
|
|
394
609
|
: `nothing is open on ${c.serverId}`);
|
|
395
610
|
}
|
|
396
611
|
toolset = open[0].toolset;
|
|
@@ -399,33 +614,40 @@ export class ToolsetsModule {
|
|
|
399
614
|
if (p.isDeclared(toolset)) {
|
|
400
615
|
// As open refuses one: a toolset without instances is closed without one.
|
|
401
616
|
if (instance && !p.declared[toolset].instances)
|
|
402
|
-
fail(`${c.serverId}/${toolset} has no instances: close it without one`);
|
|
617
|
+
fail(`${c.serverId}/${nameToTypeBack(toolset)} has no instances: close it without one`);
|
|
403
618
|
if (p.declared[toolset].instances && !instance) {
|
|
404
619
|
const open = p.openRecords(toolset);
|
|
405
620
|
if (open.length !== 1)
|
|
406
|
-
fail(open.length ? `several ${toolset} instances are open (${open.map((o) => o.instance).join(', ')}): say which` : `${toolset} is not open`);
|
|
621
|
+
fail(open.length ? `several ${nameToTypeBack(toolset)} instances are open (${open.map((o) => nameToTypeBack(String(o.instance))).join(', ')}): say which` : `${nameToTypeBack(toolset)} is not open`);
|
|
407
622
|
instance = open[0].instance;
|
|
408
623
|
}
|
|
409
624
|
let closed;
|
|
410
625
|
try {
|
|
411
|
-
closed = await c.close(toolset, instance);
|
|
626
|
+
closed = await c.close(toolset, instance, { shown });
|
|
412
627
|
}
|
|
413
628
|
catch (error) {
|
|
414
|
-
|
|
629
|
+
const what = `${nameToTypeBack(toolset)}${instance ? `:${nameToTypeBack(instance)}` : ''}`;
|
|
630
|
+
return requestFailure(c.serverId, `did not close ${what}`, `didn't confirm closing ${what}`, error);
|
|
415
631
|
}
|
|
416
632
|
const key = keyOf(toolset, instance);
|
|
417
|
-
const change = p.render(closed.change) || `${p.label(key)} closed.`;
|
|
633
|
+
const change = p.render(closed.change, { mark: shown, describeAll: !shown }) || `${p.label(key)} closed.`;
|
|
634
|
+
take?.({ toolsets: c, cs: closed.change });
|
|
418
635
|
return blocksResult([...asBlocks(closed.content), { type: 'text', text: change }]);
|
|
419
636
|
}
|
|
420
637
|
if (p.undeclaredGroups.includes(toolset)) {
|
|
421
638
|
if (instance)
|
|
422
|
-
fail(`${c.serverId}/${toolset} takes no instance (the server doesn't declare it)`);
|
|
639
|
+
fail(`${c.serverId}/${nameToTypeBack(toolset)} takes no instance (the server doesn't declare it)`);
|
|
423
640
|
if (!p.isOpen(toolset))
|
|
424
|
-
fail(`${c.serverId}/${toolset} is not open`);
|
|
641
|
+
fail(`${c.serverId}/${nameToTypeBack(toolset)} is not open`);
|
|
425
642
|
const cs = c.setGroupOpen(toolset, false);
|
|
426
|
-
|
|
643
|
+
const text = p.render(cs, { mark: shown, describeAll: !shown }) || `${p.label(keyOf(toolset))} closed.`;
|
|
644
|
+
if (take)
|
|
645
|
+
take({ toolsets: c, cs });
|
|
646
|
+
else
|
|
647
|
+
c.tell(cs);
|
|
648
|
+
return textResult(text);
|
|
427
649
|
}
|
|
428
|
-
return fail(
|
|
650
|
+
return fail(noToolset(c, toolset));
|
|
429
651
|
}
|
|
430
652
|
}
|
|
431
653
|
//# sourceMappingURL=toolsets-module.js.map
|