@animalabs/agent-framework 0.28.0 → 0.30.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.
Files changed (289) hide show
  1. package/README.md +88 -25
  2. package/dist/src/admission/admit.d.ts +90 -0
  3. package/dist/src/admission/admit.d.ts.map +1 -0
  4. package/dist/src/admission/admit.js +43 -0
  5. package/dist/src/admission/admit.js.map +1 -0
  6. package/dist/src/agent.d.ts +21 -0
  7. package/dist/src/agent.d.ts.map +1 -1
  8. package/dist/src/agent.js +47 -7
  9. package/dist/src/agent.js.map +1 -1
  10. package/dist/src/api/mcp-server-core.d.ts.map +1 -1
  11. package/dist/src/api/mcp-server-core.js +3 -12
  12. package/dist/src/api/mcp-server-core.js.map +1 -1
  13. package/dist/src/api/server.d.ts.map +1 -1
  14. package/dist/src/api/server.js +7 -3
  15. package/dist/src/api/server.js.map +1 -1
  16. package/dist/src/author-presentation.d.ts +50 -0
  17. package/dist/src/author-presentation.d.ts.map +1 -0
  18. package/dist/src/author-presentation.js +58 -0
  19. package/dist/src/author-presentation.js.map +1 -0
  20. package/dist/src/auto-continue.d.ts +2 -0
  21. package/dist/src/auto-continue.d.ts.map +1 -1
  22. package/dist/src/auto-continue.js +5 -2
  23. package/dist/src/auto-continue.js.map +1 -1
  24. package/dist/src/code-execution/isolate/runner.d.ts +7 -5
  25. package/dist/src/code-execution/isolate/runner.d.ts.map +1 -1
  26. package/dist/src/code-execution/isolate/runner.js +7 -5
  27. package/dist/src/code-execution/isolate/runner.js.map +1 -1
  28. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  29. package/dist/src/code-execution/isolate/scripts.js +3 -2
  30. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  31. package/dist/src/dendrite/index.d.ts +6 -0
  32. package/dist/src/dendrite/index.d.ts.map +1 -0
  33. package/dist/src/dendrite/index.js +3 -0
  34. package/dist/src/dendrite/index.js.map +1 -0
  35. package/dist/src/dendrite/presets.d.ts +60 -0
  36. package/dist/src/dendrite/presets.d.ts.map +1 -0
  37. package/dist/src/dendrite/presets.js +117 -0
  38. package/dist/src/dendrite/presets.js.map +1 -0
  39. package/dist/src/dendrite/registry.d.ts +260 -0
  40. package/dist/src/dendrite/registry.d.ts.map +1 -0
  41. package/dist/src/dendrite/registry.js +742 -0
  42. package/dist/src/dendrite/registry.js.map +1 -0
  43. package/dist/src/dendrite/types.d.ts +392 -0
  44. package/dist/src/dendrite/types.d.ts.map +1 -0
  45. package/dist/src/dendrite/types.js +24 -0
  46. package/dist/src/dendrite/types.js.map +1 -0
  47. package/dist/src/framework.d.ts +846 -67
  48. package/dist/src/framework.d.ts.map +1 -1
  49. package/dist/src/framework.js +4213 -924
  50. package/dist/src/framework.js.map +1 -1
  51. package/dist/src/gate/event-gate.d.ts +46 -14
  52. package/dist/src/gate/event-gate.d.ts.map +1 -1
  53. package/dist/src/gate/event-gate.js +143 -53
  54. package/dist/src/gate/event-gate.js.map +1 -1
  55. package/dist/src/gate/index.d.ts +1 -1
  56. package/dist/src/gate/index.d.ts.map +1 -1
  57. package/dist/src/gate/index.js +1 -1
  58. package/dist/src/gate/index.js.map +1 -1
  59. package/dist/src/gate/types.d.ts +12 -2
  60. package/dist/src/gate/types.d.ts.map +1 -1
  61. package/dist/src/heartbeat/heartbeat.d.ts +18 -0
  62. package/dist/src/heartbeat/heartbeat.d.ts.map +1 -1
  63. package/dist/src/heartbeat/heartbeat.js +34 -8
  64. package/dist/src/heartbeat/heartbeat.js.map +1 -1
  65. package/dist/src/held-writes.d.ts +3 -3
  66. package/dist/src/held-writes.js +3 -3
  67. package/dist/src/host-presentation.d.ts +174 -0
  68. package/dist/src/host-presentation.d.ts.map +1 -0
  69. package/dist/src/host-presentation.js +565 -0
  70. package/dist/src/host-presentation.js.map +1 -0
  71. package/dist/src/index.d.ts +7 -4
  72. package/dist/src/index.d.ts.map +1 -1
  73. package/dist/src/index.js +7 -2
  74. package/dist/src/index.js.map +1 -1
  75. package/dist/src/line-reader.d.ts +4 -0
  76. package/dist/src/line-reader.d.ts.map +1 -0
  77. package/dist/src/line-reader.js +59 -0
  78. package/dist/src/line-reader.js.map +1 -0
  79. package/dist/src/mcpl/channel-registry.d.ts +118 -15
  80. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  81. package/dist/src/mcpl/channel-registry.js +266 -110
  82. package/dist/src/mcpl/channel-registry.js.map +1 -1
  83. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  84. package/dist/src/mcpl/conversation-router.js +2 -1
  85. package/dist/src/mcpl/conversation-router.js.map +1 -1
  86. package/dist/src/mcpl/convert-block.d.ts +1 -0
  87. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  88. package/dist/src/mcpl/convert-block.js +16 -4
  89. package/dist/src/mcpl/convert-block.js.map +1 -1
  90. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  91. package/dist/src/mcpl/hook-orchestrator.js +4 -3
  92. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  93. package/dist/src/mcpl/inbound-source.d.ts +3 -1
  94. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  95. package/dist/src/mcpl/inbound-source.js +3 -1
  96. package/dist/src/mcpl/inbound-source.js.map +1 -1
  97. package/dist/src/mcpl/modern-connection.d.ts.map +1 -1
  98. package/dist/src/mcpl/modern-connection.js +11 -7
  99. package/dist/src/mcpl/modern-connection.js.map +1 -1
  100. package/dist/src/mcpl/protocol-family.d.ts +3 -2
  101. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  102. package/dist/src/mcpl/protocol-family.js +25 -3
  103. package/dist/src/mcpl/protocol-family.js.map +1 -1
  104. package/dist/src/mcpl/push-coalescer.d.ts +9 -0
  105. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  106. package/dist/src/mcpl/push-coalescer.js +21 -0
  107. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  108. package/dist/src/mcpl/push-handler.js +2 -2
  109. package/dist/src/mcpl/push-handler.js.map +1 -1
  110. package/dist/src/mcpl/references.d.ts +12 -40
  111. package/dist/src/mcpl/references.d.ts.map +1 -1
  112. package/dist/src/mcpl/references.js +29 -23
  113. package/dist/src/mcpl/references.js.map +1 -1
  114. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  115. package/dist/src/mcpl/server-connection.js +20 -20
  116. package/dist/src/mcpl/server-connection.js.map +1 -1
  117. package/dist/src/mcpl/skip-reply-wake.d.ts +25 -0
  118. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -1
  119. package/dist/src/mcpl/skip-reply-wake.js +25 -0
  120. package/dist/src/mcpl/skip-reply-wake.js.map +1 -1
  121. package/dist/src/mcpl/tool-presence.d.ts.map +1 -1
  122. package/dist/src/mcpl/tool-presence.js +18 -17
  123. package/dist/src/mcpl/tool-presence.js.map +1 -1
  124. package/dist/src/mcpl/tool-signature.d.ts.map +1 -1
  125. package/dist/src/mcpl/tool-signature.js +17 -16
  126. package/dist/src/mcpl/tool-signature.js.map +1 -1
  127. package/dist/src/mcpl/toolset-host.d.ts +8 -26
  128. package/dist/src/mcpl/toolset-host.d.ts.map +1 -1
  129. package/dist/src/mcpl/toolset-host.js +16 -6
  130. package/dist/src/mcpl/toolset-host.js.map +1 -1
  131. package/dist/src/mcpl/toolsets-module.d.ts +14 -25
  132. package/dist/src/mcpl/toolsets-module.d.ts.map +1 -1
  133. package/dist/src/mcpl/toolsets-module.js +187 -50
  134. package/dist/src/mcpl/toolsets-module.js.map +1 -1
  135. package/dist/src/mcpl/transport.d.ts +1 -1
  136. package/dist/src/mcpl/transport.d.ts.map +1 -1
  137. package/dist/src/mcpl/transport.js +16 -16
  138. package/dist/src/mcpl/transport.js.map +1 -1
  139. package/dist/src/mcpl/types.d.ts +28 -12
  140. package/dist/src/mcpl/types.d.ts.map +1 -1
  141. package/dist/src/mcpl/types.js.map +1 -1
  142. package/dist/src/module-registry.d.ts +6 -7
  143. package/dist/src/module-registry.d.ts.map +1 -1
  144. package/dist/src/module-registry.js +6 -7
  145. package/dist/src/module-registry.js.map +1 -1
  146. package/dist/src/modules/api/index.d.ts.map +1 -1
  147. package/dist/src/modules/api/index.js +4 -2
  148. package/dist/src/modules/api/index.js.map +1 -1
  149. package/dist/src/modules/discord/index.d.ts.map +1 -1
  150. package/dist/src/modules/discord/index.js +33 -25
  151. package/dist/src/modules/discord/index.js.map +1 -1
  152. package/dist/src/modules/history/index.js +8 -6
  153. package/dist/src/modules/history/index.js.map +1 -1
  154. package/dist/src/modules/sender-name.d.ts +7 -0
  155. package/dist/src/modules/sender-name.d.ts.map +1 -0
  156. package/dist/src/modules/sender-name.js +10 -0
  157. package/dist/src/modules/sender-name.js.map +1 -0
  158. package/dist/src/modules/workspace/answers.d.ts +5 -1
  159. package/dist/src/modules/workspace/answers.d.ts.map +1 -1
  160. package/dist/src/modules/workspace/answers.js +16 -1
  161. package/dist/src/modules/workspace/answers.js.map +1 -1
  162. package/dist/src/modules/workspace/direct-write.d.ts.map +1 -1
  163. package/dist/src/modules/workspace/direct-write.js +6 -4
  164. package/dist/src/modules/workspace/direct-write.js.map +1 -1
  165. package/dist/src/modules/workspace/effects.d.ts +4 -2
  166. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  167. package/dist/src/modules/workspace/effects.js +50 -40
  168. package/dist/src/modules/workspace/effects.js.map +1 -1
  169. package/dist/src/modules/workspace/index.d.ts +1 -0
  170. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  171. package/dist/src/modules/workspace/index.js +66 -36
  172. package/dist/src/modules/workspace/index.js.map +1 -1
  173. package/dist/src/modules/workspace/observe.d.ts +51 -20
  174. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  175. package/dist/src/modules/workspace/observe.js +120 -82
  176. package/dist/src/modules/workspace/observe.js.map +1 -1
  177. package/dist/src/modules/workspace/reconcile.d.ts +7 -1
  178. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  179. package/dist/src/modules/workspace/reconcile.js +133 -37
  180. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  181. package/dist/src/notice-tags.d.ts +81 -0
  182. package/dist/src/notice-tags.d.ts.map +1 -0
  183. package/dist/src/notice-tags.js +85 -0
  184. package/dist/src/notice-tags.js.map +1 -0
  185. package/dist/src/reasoning-loss-notice.d.ts.map +1 -1
  186. package/dist/src/reasoning-loss-notice.js +5 -3
  187. package/dist/src/reasoning-loss-notice.js.map +1 -1
  188. package/dist/src/silent-heartbeat.d.ts +5 -55
  189. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  190. package/dist/src/silent-heartbeat.js +63 -7
  191. package/dist/src/silent-heartbeat.js.map +1 -1
  192. package/dist/src/stored-injections.d.ts +4 -0
  193. package/dist/src/stored-injections.d.ts.map +1 -1
  194. package/dist/src/stored-injections.js +6 -0
  195. package/dist/src/stored-injections.js.map +1 -1
  196. package/dist/src/supplied-text.d.ts +35 -0
  197. package/dist/src/supplied-text.d.ts.map +1 -0
  198. package/dist/src/supplied-text.js +141 -0
  199. package/dist/src/supplied-text.js.map +1 -0
  200. package/dist/src/tool-args.d.ts.map +1 -1
  201. package/dist/src/tool-args.js +9 -8
  202. package/dist/src/tool-args.js.map +1 -1
  203. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  204. package/dist/src/tool-image-ledger.js +2 -1
  205. package/dist/src/tool-image-ledger.js.map +1 -1
  206. package/dist/src/tool-presentation.d.ts.map +1 -1
  207. package/dist/src/tool-presentation.js +5 -4
  208. package/dist/src/tool-presentation.js.map +1 -1
  209. package/dist/src/tune-out/coordinator.d.ts +382 -11
  210. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  211. package/dist/src/tune-out/coordinator.js +1100 -119
  212. package/dist/src/tune-out/coordinator.js.map +1 -1
  213. package/dist/src/tune-out/tools.d.ts +101 -5
  214. package/dist/src/tune-out/tools.d.ts.map +1 -1
  215. package/dist/src/tune-out/tools.js +55 -18
  216. package/dist/src/tune-out/tools.js.map +1 -1
  217. package/dist/src/types/agent.d.ts +48 -9
  218. package/dist/src/types/agent.d.ts.map +1 -1
  219. package/dist/src/types/framework.d.ts +142 -0
  220. package/dist/src/types/framework.d.ts.map +1 -1
  221. package/dist/src/types/module.d.ts +22 -11
  222. package/dist/src/types/module.d.ts.map +1 -1
  223. package/dist/src/types/trace.d.ts +136 -0
  224. package/dist/src/types/trace.d.ts.map +1 -1
  225. package/package.json +3 -3
  226. package/src/admission/admit.ts +113 -0
  227. package/src/agent.ts +52 -7
  228. package/src/api/mcp-server-core.ts +3 -14
  229. package/src/api/server.ts +7 -3
  230. package/src/author-presentation.ts +81 -0
  231. package/src/auto-continue.ts +8 -2
  232. package/src/code-execution/isolate/runner.ts +7 -5
  233. package/src/code-execution/isolate/scripts.ts +3 -2
  234. package/src/dendrite/index.ts +32 -0
  235. package/src/dendrite/presets.ts +149 -0
  236. package/src/dendrite/registry.ts +808 -0
  237. package/src/dendrite/types.ts +397 -0
  238. package/src/framework.ts +4362 -852
  239. package/src/gate/event-gate.ts +159 -52
  240. package/src/gate/index.ts +1 -1
  241. package/src/gate/types.ts +12 -2
  242. package/src/heartbeat/heartbeat.ts +48 -9
  243. package/src/held-writes.ts +3 -3
  244. package/src/host-presentation.ts +612 -0
  245. package/src/index.ts +17 -4
  246. package/src/line-reader.ts +58 -0
  247. package/src/mcpl/channel-registry.ts +306 -111
  248. package/src/mcpl/conversation-router.ts +2 -1
  249. package/src/mcpl/convert-block.ts +17 -4
  250. package/src/mcpl/hook-orchestrator.ts +4 -3
  251. package/src/mcpl/inbound-source.ts +3 -1
  252. package/src/mcpl/modern-connection.ts +11 -7
  253. package/src/mcpl/protocol-family.ts +24 -3
  254. package/src/mcpl/push-coalescer.ts +19 -0
  255. package/src/mcpl/push-handler.ts +2 -2
  256. package/src/mcpl/references.ts +31 -23
  257. package/src/mcpl/server-connection.ts +20 -18
  258. package/src/mcpl/skip-reply-wake.ts +35 -0
  259. package/src/mcpl/tool-presence.ts +18 -17
  260. package/src/mcpl/tool-signature.ts +17 -16
  261. package/src/mcpl/toolset-host.ts +18 -6
  262. package/src/mcpl/toolsets-module.ts +149 -50
  263. package/src/mcpl/transport.ts +18 -14
  264. package/src/mcpl/types.ts +29 -12
  265. package/src/module-registry.ts +6 -7
  266. package/src/modules/api/index.ts +4 -2
  267. package/src/modules/discord/index.ts +37 -25
  268. package/src/modules/history/index.ts +8 -6
  269. package/src/modules/sender-name.ts +9 -0
  270. package/src/modules/workspace/answers.ts +20 -2
  271. package/src/modules/workspace/direct-write.ts +4 -3
  272. package/src/modules/workspace/effects.ts +50 -40
  273. package/src/modules/workspace/index.ts +58 -34
  274. package/src/modules/workspace/observe.ts +139 -88
  275. package/src/modules/workspace/reconcile.ts +159 -36
  276. package/src/notice-tags.ts +86 -0
  277. package/src/reasoning-loss-notice.ts +5 -3
  278. package/src/silent-heartbeat.ts +11 -7
  279. package/src/stored-injections.ts +7 -0
  280. package/src/supplied-text.ts +154 -0
  281. package/src/tool-args.ts +9 -8
  282. package/src/tool-image-ledger.ts +2 -1
  283. package/src/tool-presentation.ts +5 -4
  284. package/src/tune-out/coordinator.ts +1252 -123
  285. package/src/tune-out/tools.ts +152 -11
  286. package/src/types/agent.ts +51 -9
  287. package/src/types/framework.ts +146 -0
  288. package/src/types/module.ts +23 -11
  289. package/src/types/trace.ts +150 -0
@@ -1,31 +1,8 @@
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 when a server is
17
- * configured with `toolHints: true`; its tools never change after that.
18
- *
19
- * Its gatherContext publishes the RFC §8.3 reminder, as a stored `afterUser`
20
- * injection, when an agent's earlier announcements of an open toolset have
21
- * left its context (ToolsetHub.observeCompile).
22
- *
23
- * Ported from mcpl-cc-bridge's meta-tools.ts, the reference host.
24
- */
25
1
  import type { EventResponse, Module, ModuleContext, ModuleContextInjection, ProcessState } from '../types/module.js';
26
2
  import type { ProcessEvent, ToolCall, ToolDefinition, ToolResult } from '../types/events.js';
27
3
  import { type ToolsetController, type ToolsetHub } from './toolset-host.js';
28
4
  import { type ChangeSet } from './tool-presence.js';
5
+ import { type HostPresentation } from '../host-presentation.js';
29
6
  /** The namespace a reminder's stored injection carries. */
30
7
  export declare const TOOLSETS_REMINDER_NAMESPACE = "mcpl-toolsets";
31
8
  /** A failure whose outcome is unknown, with how to find it out. */
@@ -50,11 +27,15 @@ export declare function resolveAct(hub: ToolsetHub, input: unknown): ActTarget;
50
27
  export declare class ToolsetsModule implements Module {
51
28
  private readonly hub;
52
29
  private readonly takerFor;
30
+ /** The host's own toolsets (the built-in "settings" and server tabs), when the framework presents them. */
31
+ private readonly host;
53
32
  readonly name = "toolsets";
54
33
  /** `takerFor`: who shows a call's result to an agent, taking the tool
55
34
  * change rendered into it; null when the result may reach none, and its
56
35
  * change is left to a notice (RFC-014 §6.1: told once). */
57
- constructor(hub: ToolsetHub, takerFor: (call: ToolCall) => ((taken: TakenChange) => void) | null);
36
+ constructor(hub: ToolsetHub, takerFor: (call: ToolCall) => ((taken: TakenChange) => void) | null,
37
+ /** The host's own toolsets (the built-in "settings" and server tabs), when the framework presents them. */
38
+ host?: HostPresentation | null);
58
39
  start(_ctx: ModuleContext): Promise<void>;
59
40
  stop(): Promise<void>;
60
41
  getTools(): ToolDefinition[];
@@ -62,6 +43,14 @@ export declare class ToolsetsModule implements Module {
62
43
  /** RFC §8.3: the reminder due for this agent, stored where compile() would place it. */
63
44
  gatherContext(agentName: string): Promise<ModuleContextInjection[]>;
64
45
  handleToolCall(call: ToolCall): Promise<ToolResult>;
46
+ /**
47
+ * The host's own toolsets: `toolset: "settings"` alone, or `server` alone
48
+ * (a server's tab), for open, close and look; a tool a host toolset has, for
49
+ * look. Null for everything else, which the servers' toolsets answer.
50
+ */
51
+ private hostCall;
52
+ /** What the host's own toolsets add ahead of the servers' look: everything, or one presenting server's tab. */
53
+ private hostLookLead;
65
54
  private toolsAt;
66
55
  /** A change set rendered without the key the caller already showed whole. */
67
56
  private renderOthers;
@@ -1 +1 @@
1
- {"version":3,"file":"toolsets-module.d.ts","sourceRoot":"","sources":["../../../src/mcpl/toolsets-module.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EAAoC,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9G,OAAO,EAAS,KAAK,SAAS,EAAqB,MAAM,oBAAoB,CAAC;AAI9E,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AA+E3D,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnE;AAQD,gGAAgG;AAChG,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,EAAE,EAAE,SAAS,CAAA;CAAE,CAAC;AA0EzE,8FAA8F;AAC9F,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBtJ,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAuCrE;AAED,qBAAa,cAAe,YAAW,MAAM;IAOzC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAP3B,QAAQ,CAAC,IAAI,cAAmB;IAEhC;;gEAE4D;IAC5D,YACmB,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,IAAI,EAClF;IAEE,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAG;IAC5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAG;IAE9B,QAAQ,IAAI,cAAc,EAAE,CAE3B;IAEK,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAElF;IAED,wFAAwF;IAClF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAIxE;IAEK,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAkBxD;IAED,OAAO,CAAC,OAAO;IAIf,6EAA6E;IAC7E,OAAO,CAAC,YAAY;YAMN,IAAI;IAwDlB;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAMX,2GAA2G;YAC7F,IAAI;IAoClB,OAAO,CAAC,OAAO;IAsDf,OAAO,CAAC,QAAQ;YAWF,KAAK;CAqDpB"}
1
+ {"version":3,"file":"toolsets-module.d.ts","sourceRoot":"","sources":["../../../src/mcpl/toolsets-module.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACrH,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,EAAoC,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC9G,OAAO,EAAS,KAAK,SAAS,EAAqB,MAAM,oBAAoB,CAAC;AAG9E,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEjF,2DAA2D;AAC3D,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAkG3D,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGnE;AAQD,gGAAgG;AAChG,MAAM,MAAM,WAAW,GAAG;IAAE,QAAQ,EAAE,iBAAiB,CAAC;IAAC,EAAE,EAAE,SAAS,CAAA;CAAE,CAAC;AA0EzE,8FAA8F;AAC9F,MAAM,MAAM,SAAS,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBtJ,wBAAgB,UAAU,CAAC,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,CAuCrE;AAED,qBAAa,cAAe,YAAW,MAAM;IAOzC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,2GAA2G;IAC3G,OAAO,CAAC,QAAQ,CAAC,IAAI;IATvB,QAAQ,CAAC,IAAI,cAAmB;IAEhC;;gEAE4D;IAC5D,YACmB,GAAG,EAAE,UAAU,EACf,QAAQ,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,IAAI;IACpF,2GAA2G;IAC1F,IAAI,GAAE,gBAAgB,GAAG,IAAW,EACnD;IAEE,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAG;IAC5C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAG;IAE9B,QAAQ,IAAI,cAAc,EAAE,CAE3B;IAEK,SAAS,CAAC,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAElF;IAED,wFAAwF;IAClF,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAIxE;IAEK,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAkCxD;IAED;;;;OAIG;YACW,QAAQ;IAyCtB,+GAA+G;IAC/G,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,OAAO;IAIf,6EAA6E;IAC7E,OAAO,CAAC,YAAY;YAMN,IAAI;IAwDlB;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAMX,2GAA2G;YAC7F,IAAI;IAoClB,OAAO,CAAC,OAAO;IAyDf,OAAO,CAAC,QAAQ;YAWF,KAAK;CAqDpB"}
@@ -1,30 +1,59 @@
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
30
  import { checkArgs, jsonText, renderToolDefinition } from './tool-signature.js';
5
31
  import { McplRequestError } from './server-connection.js';
32
+ import { BUILTIN_TOOLSET } from '../host-presentation.js';
6
33
  /** The namespace a reminder's stored injection carries. */
7
34
  export const TOOLSETS_REMINDER_NAMESPACE = 'mcpl-toolsets';
8
35
  const META_TOOLS = [
9
36
  {
10
37
  name: 'open',
11
- description: 'Open a toolset of an MCPL server (a game, a building mode, a card table) so its tools can be called with toolsets--act. ' +
12
- 'Returns what the server shows on opening and the tools available now. server may be left out when one server has the toolset, ' +
13
- 'and toolset when the server has only one; instance names a separate copy where a toolset allows several (two games at once); ' +
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; ' +
14
42
  'options as toolsets--look lists them.',
15
43
  inputSchema: {
16
44
  type: 'object',
17
45
  properties: {
18
46
  server: { type: 'string', description: 'the MCPL server id' },
19
- toolset: { type: 'string' },
47
+ toolset: { type: 'string', description: '"settings" for the host\'s own; a server\'s toolset with server' },
20
48
  instance: { type: 'string', description: 'a name for this copy (toolsets with instances only)' },
21
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' },
22
51
  },
23
52
  },
24
53
  },
25
54
  {
26
55
  name: 'act',
27
- description: 'Call a tool that was announced in the conversation (a tool of an open toolset, or another dynamic tool); they are not in your tool list. ' +
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. ' +
28
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. ' +
29
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.',
30
59
  inputSchema: {
@@ -40,7 +69,7 @@ const META_TOOLS = [
40
69
  },
41
70
  {
42
71
  name: 'look',
43
- description: 'Show what is open and what can be opened on MCPL servers, with the tools usable now: announcements earlier in the conversation may be out of date. ' +
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. ' +
44
73
  'With tool: that tool\'s full description and input schema. With toolset or instance: just that one.',
45
74
  inputSchema: {
46
75
  type: 'object',
@@ -54,18 +83,31 @@ const META_TOOLS = [
54
83
  },
55
84
  {
56
85
  name: 'close',
57
- 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.',
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.',
58
88
  inputSchema: {
59
89
  type: 'object',
60
90
  properties: {
61
91
  server: { type: 'string', description: 'the MCPL server id (may be left out when one server has the toolset open)' },
62
- 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' },
63
93
  instance: { type: 'string' },
94
+ disconnect: { type: 'boolean', description: 'with server alone: disconnect it as a closed tab until it is opened again' },
64
95
  },
65
96
  },
66
97
  },
67
98
  ];
68
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)));
69
111
  const SELECTORS = ['server', 'toolset', 'instance', 'tool'];
70
112
  function checkSelectors(args) {
71
113
  for (const key of SELECTORS) {
@@ -82,7 +124,7 @@ export function withLookHint(serverId, text) {
82
124
  return `${said}${/[.!?]$/.test(said) ? '' : '.'} ${META_TOOL_NAMES.look} {server:"${serverId}"} asks it what's open, or says it couldn't.`;
83
125
  }
84
126
  function requestFailure(serverId, did, unconfirmed, error) {
85
- const message = error instanceof Error ? error.message : String(error);
127
+ const message = suppliedValue(error instanceof Error ? error.message : String(error));
86
128
  if (error instanceof McplRequestError && error.outcome === 'not-sent')
87
129
  return fail(`${serverId} ${did}: ${message}`);
88
130
  return fail(withLookHint(serverId, `${serverId} ${unconfirmed}: ${message}`));
@@ -154,7 +196,7 @@ function owner(hub, server, has, what) {
154
196
  const NONE_PRESENTS = 'no MCPL server presents toolsets now: MCPL tools are called directly, by name';
155
197
  function noToolset(c, toolset) {
156
198
  const names = [...Object.keys(c.presence.declared), ...c.presence.undeclaredGroups];
157
- return `${c.serverId} has no toolset "${toolset}"; ${names.length ? `it has ${names.join(', ')}` : 'it has none'}`;
199
+ return `${c.serverId} has no toolset "${toolset}"; ${names.length ? `it has ${names.map(nameToTypeBack).join(', ')}` : 'it has none'}`;
158
200
  }
159
201
  /**
160
202
  * Resolve a `toolsets--act` call (RFC §8.2) to the dynamic tool it calls,
@@ -175,18 +217,18 @@ export function resolveAct(hub, input) {
175
217
  try {
176
218
  const args = isObj(input) ? input : {};
177
219
  checkSelectors(args);
178
- const tool = str(args.tool) ?? fail('name the tool');
220
+ const tool = toolNamed(hub, args.server, args.tool) ?? fail('name the tool');
179
221
  const c = owner(hub, args.server, (x) => !!x.presence.tool(tool), `a tool "${tool}"`);
180
222
  const p = c.presence;
181
223
  if (!p.tool(tool))
182
224
  fail(`${c.serverId} has no tool "${tool}": ${META_TOOL_NAMES.look} {server:"${c.serverId}"} shows what's available`);
183
225
  if (!p.shownAs(tool, 'dynamic'))
184
- fail(`${tool} is not a dynamic tool: ${p.toolStatus(tool)}. Call it there.`);
226
+ fail(`${nameToTypeBack(tool)} is not a dynamic tool: ${p.toolStatus(tool)}. Call it there.`);
185
227
  if (args.args !== undefined && !isObj(args.args))
186
228
  fail('args must be an object of the tool\'s arguments');
187
229
  const callArgs = (args.args ?? {});
188
- let instance = str(args.instance);
189
230
  const ts = p.toolsetOf(tool);
231
+ let instance = instanceNamed([c], ts, args.instance);
190
232
  const declared = ts !== undefined && p.isDeclared(ts);
191
233
  const withInstances = hasInstances(p, ts);
192
234
  if (withInstances && !instance) {
@@ -195,23 +237,23 @@ export function resolveAct(hub, input) {
195
237
  if (open.length === 1)
196
238
  instance = open[0].instance;
197
239
  else if (!open.length)
198
- 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)}`);
199
241
  else
200
- 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`);
201
243
  }
202
244
  if (instance && !withInstances)
203
- 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`);
204
246
  // RFC §5.4: the host gates calls into an undeclared group it has closed.
205
247
  if (ts && !declared && !p.isOpen(ts))
206
- 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)}`);
207
249
  // RFC §6.1: arguments are checked against the definition in force for
208
250
  // that instance: its own from hints (the legal moves of one game), else
209
251
  // the toolset's, else the listed one.
210
252
  const key = actKey(p, tool, instance);
211
253
  const problem = checkArgs(p.defOf(key, tool).inputSchema, callArgs);
212
254
  if (problem) {
213
- fail(`Invalid arguments for ${tool} in ${p.label(key)}: ${problem}. Nothing was sent; ` +
214
- `${META_TOOL_NAMES.look} {server:"${c.serverId}", tool:"${tool}"${instance ? `, instance:"${instance}"` : ''}} shows its current definition.`);
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.`);
215
257
  }
216
258
  return { ok: true, serverId: c.serverId, tool, ...(instance ? { instance } : {}), args: callArgs };
217
259
  }
@@ -224,13 +266,17 @@ export function resolveAct(hub, input) {
224
266
  export class ToolsetsModule {
225
267
  hub;
226
268
  takerFor;
269
+ host;
227
270
  name = TOOLSETS_MODULE;
228
271
  /** `takerFor`: who shows a call's result to an agent, taking the tool
229
272
  * change rendered into it; null when the result may reach none, and its
230
273
  * change is left to a notice (RFC-014 §6.1: told once). */
231
- constructor(hub, takerFor) {
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) {
232
277
  this.hub = hub;
233
278
  this.takerFor = takerFor;
279
+ this.host = host;
234
280
  }
235
281
  async start(_ctx) { }
236
282
  async stop() { }
@@ -251,7 +297,26 @@ export class ToolsetsModule {
251
297
  const args = isObj(call.input) ? call.input : {};
252
298
  try {
253
299
  checkSelectors(args);
300
+ // An agent shown another's tool list can't switch it: what it opened
301
+ // or closed would change its owner's list, or nothing it is shown.
302
+ const caller = call.callerAgentName;
303
+ const owner = caller && this.host ? this.host.ownerOf(caller) : caller;
304
+ if ((call.name === 'open' || call.name === 'close') && caller && owner !== caller) {
305
+ fail(`${caller} is shown ${owner}'s tools and can't ${call.name} a toolset; it reaches the toolsets ${owner} has open. Nothing changed.`);
306
+ }
307
+ const hosted = await this.hostCall(call.name, args, call.callerAgentName);
308
+ if (hosted)
309
+ return hosted;
254
310
  const take = this.takerFor(call);
311
+ const lead = call.name === 'look' ? this.hostLookLead(args, call.callerAgentName) : null;
312
+ if (lead !== null) {
313
+ if (!this.hub.presenting().length)
314
+ return textResult(lead);
315
+ const theirs = await this.look(args, take);
316
+ if (theirs.isError || typeof theirs.data !== 'string')
317
+ return theirs;
318
+ return textResult(`${lead}\n\n${theirs.data}`);
319
+ }
255
320
  switch (call.name) {
256
321
  case 'open': return await this.open(args, take);
257
322
  case 'act': return this.act(args);
@@ -267,6 +332,76 @@ export class ToolsetsModule {
267
332
  return { success: false, isError: true, error: message };
268
333
  }
269
334
  }
335
+ /**
336
+ * The host's own toolsets: `toolset: "settings"` alone, or `server` alone
337
+ * (a server's tab), for open, close and look; a tool a host toolset has, for
338
+ * look. Null for everything else, which the servers' toolsets answer.
339
+ */
340
+ async hostCall(name, args, agentName) {
341
+ const host = this.host;
342
+ if (!host || !agentName || !host.active(agentName))
343
+ return null;
344
+ const server = str(args.server);
345
+ const toolset = str(args.toolset);
346
+ const instance = str(args.instance);
347
+ const tool = str(args.tool);
348
+ const builtin = toolset === BUILTIN_TOOLSET && !server && !instance;
349
+ const tab = !!server && !toolset && !instance && args.options === undefined;
350
+ if (args.full !== undefined && typeof args.full !== 'boolean')
351
+ fail('full must be true or false');
352
+ if (args.disconnect !== undefined && typeof args.disconnect !== 'boolean')
353
+ fail('disconnect must be true or false');
354
+ if (tab && !host.knows(server) && !this.hub.get(server))
355
+ fail(`no MCPL server "${server}" is configured here`);
356
+ switch (name) {
357
+ case 'open':
358
+ if (builtin)
359
+ return textResult(host.openBuiltin(agentName));
360
+ if (tab)
361
+ return textResult(await host.openServer(agentName, server, { full: args.full === true }));
362
+ if (args.full !== undefined)
363
+ fail('full goes with server alone');
364
+ return null;
365
+ case 'close':
366
+ if (builtin)
367
+ return textResult(host.closeBuiltin(agentName));
368
+ if (tab)
369
+ return textResult(await host.closeServer(agentName, server, { disconnect: args.disconnect === true }));
370
+ if (args.disconnect !== undefined)
371
+ fail('disconnect goes with server alone');
372
+ return null;
373
+ case 'look': {
374
+ // A host toolset's tool, with or without its server named (as act takes it).
375
+ if (tool && !toolset && !instance) {
376
+ const shown = host.lookTool(agentName, tool, server);
377
+ if (shown)
378
+ return textResult(shown);
379
+ return null;
380
+ }
381
+ if (builtin)
382
+ return textResult(host.lookToolset(agentName, { builtin: true }));
383
+ // A server's tab, and every host toolset with no selector, go first in
384
+ // what the servers' own look says (handleToolCall adds them).
385
+ if (tab && !tool && !this.hub.get(server)?.presents)
386
+ return textResult(host.lookToolset(agentName, { serverId: server }));
387
+ return null;
388
+ }
389
+ default:
390
+ return null;
391
+ }
392
+ }
393
+ /** What the host's own toolsets add ahead of the servers' look: everything, or one presenting server's tab. */
394
+ hostLookLead(args, agentName) {
395
+ const host = this.host;
396
+ if (!host || !agentName || !host.active(agentName))
397
+ return null;
398
+ const server = str(args.server);
399
+ if (str(args.toolset) || str(args.instance) || str(args.tool))
400
+ return null;
401
+ if (!server)
402
+ return host.catalog(agentName).join('\n');
403
+ return host.knows(server) ? host.lookToolset(agentName, { serverId: server }) : null;
404
+ }
270
405
  toolsAt(c, key) {
271
406
  return [...(c.presence.view().get(key)?.tools.values() ?? [])];
272
407
  }
@@ -277,7 +412,7 @@ export class ToolsetsModule {
277
412
  // ── the tools ──
278
413
  async open(args, take) {
279
414
  const shown = take !== null;
280
- let toolset = str(args.toolset);
415
+ let toolset = toolsetNamed(this.hub, args.server, args.toolset);
281
416
  const c = toolset && !str(args.server)
282
417
  ? owner(this.hub, undefined, (x) => x.presence.isDeclared(toolset) || x.presence.undeclaredGroups.includes(toolset), `a toolset "${toolset}"`)
283
418
  : pick(this.hub, args.server);
@@ -285,32 +420,32 @@ export class ToolsetsModule {
285
420
  const openable = [...Object.keys(p.declared), ...p.undeclaredGroups];
286
421
  if (!toolset) {
287
422
  if (openable.length !== 1)
288
- fail(`${c.serverId} has ${openable.length ? `several toolsets: name one of ${openable.join(', ')}` : 'no toolsets'}`);
423
+ fail(`${c.serverId} has ${openable.length ? `several toolsets: name one of ${openable.map(nameToTypeBack).join(', ')}` : 'no toolsets'}`);
289
424
  toolset = openable[0];
290
425
  }
291
- const instance = str(args.instance);
426
+ const instance = instanceNamed([c], toolset, args.instance);
292
427
  if (args.options !== undefined && !isObj(args.options))
293
428
  fail('options must be an object');
294
429
  const options = args.options;
295
430
  if (p.isDeclared(toolset)) {
296
431
  if (instance && !p.declared[toolset].instances)
297
- fail(`${c.serverId}/${toolset} has no instances: open it without one`);
432
+ fail(`${c.serverId}/${nameToTypeBack(toolset)} has no instances: open it without one`);
298
433
  let opened;
299
434
  try {
300
435
  opened = await c.open(toolset, instance, options, { shown });
301
436
  }
302
437
  catch (error) {
303
- return requestFailure(c.serverId, `did not open ${toolset}`, `didn't confirm opening ${toolset}`, error);
438
+ return requestFailure(c.serverId, `did not open ${nameToTypeBack(toolset)}`, `didn't confirm opening ${nameToTypeBack(toolset)}`, error);
304
439
  }
305
440
  // RFC §5.2: opening what is open adopts it; its content says which options weren't applied.
306
441
  const blocks = [
307
- { type: 'text', text: `${p.label(opened.key)}${opened.title ? ` ${opened.title}` : ''}${opened.adopted ? ' (already open)' : ''}` },
442
+ { type: 'text', text: `${p.label(opened.key)}${opened.title ? ` ${suppliedValue(opened.title)}` : ''}${opened.adopted ? ' (already open)' : ''}` },
308
443
  ...asBlocks(opened.content),
309
444
  ];
310
445
  const tail = [...p.toolBlock(opened.key, this.toolsAt(c, opened.key), { headed: false, describeAll: !shown, seen: shown ? undefined : new Set() })];
311
446
  const gone = opened.change.changes.find((ch) => ch.key === opened.key)?.gone ?? [];
312
447
  if (gone.length)
313
- tail.push(`now unavailable: ${gone.join(', ')}`);
448
+ tail.push(`now unavailable: ${gone.map(suppliedValue).join(', ')}`);
314
449
  const others = this.renderOthers(c, opened.change, opened.key, shown);
315
450
  if (others)
316
451
  tail.push(others);
@@ -324,7 +459,7 @@ export class ToolsetsModule {
324
459
  if (p.undeclaredGroups.includes(toolset)) {
325
460
  // RFC §5.4: a grouping the server offers but doesn't track: the host opens it, the server isn't told.
326
461
  if (instance || options)
327
- fail(`${c.serverId}/${toolset} takes no instance or options (the server doesn't declare it)`);
462
+ fail(`${c.serverId}/${nameToTypeBack(toolset)} takes no instance or options (the server doesn't declare it)`);
328
463
  const cs = c.setGroupOpen(toolset, true);
329
464
  const key = keyOf(toolset);
330
465
  const lines = [`${p.label(key)} open.`, ...p.toolBlock(key, this.toolsAt(c, key), { headed: false, describeAll: !shown, seen: shown ? undefined : new Set() })];
@@ -368,7 +503,7 @@ export class ToolsetsModule {
368
503
  };
369
504
  let result;
370
505
  try {
371
- result = this.lookNow(args, server, str(args.tool), shown);
506
+ result = this.lookNow(args, server, toolNamed(this.hub, server, args.tool), shown);
372
507
  }
373
508
  catch (error) {
374
509
  giveBack();
@@ -396,12 +531,14 @@ export class ToolsetsModule {
396
531
  }
397
532
  lookNow(args, server, tool, mark) {
398
533
  if (!server) {
399
- if (tool)
400
- return this.lookTool(owner(this.hub, undefined, (c) => !!c.presence.tool(tool), `a tool "${tool}"`), tool, str(args.instance));
401
- const ts = str(args.toolset);
402
- const inst = str(args.instance);
534
+ if (tool) {
535
+ const c = owner(this.hub, undefined, (x) => !!x.presence.tool(tool), `a tool "${tool}"`);
536
+ return this.lookTool(c, tool, instanceNamed([c], c.presence.toolsetOf(tool), args.instance));
537
+ }
538
+ const ts = toolsetNamed(this.hub, undefined, args.toolset);
539
+ const inst = instanceNamed(this.hub.presenting(), ts, args.instance);
403
540
  if (ts || inst) {
404
- 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}"`);
541
+ 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}"`);
405
542
  return this.lookNow(args, c.serverId, tool, mark);
406
543
  }
407
544
  const all = this.hub.presenting();
@@ -411,14 +548,14 @@ export class ToolsetsModule {
411
548
  }
412
549
  const c = pick(this.hub, server);
413
550
  if (tool)
414
- return this.lookTool(c, tool, str(args.instance));
551
+ return this.lookTool(c, tool, instanceNamed([c], c.presence.toolsetOf(tool), args.instance));
415
552
  const p = c.presence;
416
- let toolset = str(args.toolset);
417
- const instance = str(args.instance);
553
+ let toolset = toolsetNamed(this.hub, server, args.toolset);
554
+ const instance = instanceNamed([c], toolset, args.instance);
418
555
  if (!toolset && instance) {
419
556
  const open = p.openRecords().filter((o) => o.instance !== undefined);
420
557
  toolset = open.find((o) => o.instance === instance)?.toolset
421
- ?? fail(`no open instance "${instance}" on ${c.serverId}; ${open.length ? `open: ${open.map((o) => `${o.toolset}:${o.instance}`).join(', ')}` : 'no instance is open there'}`);
558
+ ?? 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'}`);
422
559
  }
423
560
  if (!toolset)
424
561
  return textResult(p.catalog({ mark }));
@@ -435,8 +572,8 @@ export class ToolsetsModule {
435
572
  if (d) {
436
573
  const open = p.openRecords(toolset);
437
574
  return textResult([
438
- `${c.serverId}/${toolset}${d.title ? ` — ${d.title}` : ''}${d.summary ? `: ${d.summary}` : ''}`,
439
- open.length ? `open: ${open.map((o) => o.instance ?? toolset).join(', ')}` : 'not open',
575
+ `${c.serverId}/${nameToTypeBack(toolset)}${d.title ? ` — ${suppliedValue(d.title)}` : ''}${d.summary ? `: ${suppliedValue(d.summary)}` : ''}`,
576
+ open.length ? `open: ${open.map((o) => nameToTypeBack(o.instance ?? toolset)).join(', ')}` : 'not open',
440
577
  ...(d.instances ? ['several can be open at once; name each with instance'] : []),
441
578
  ...(d.options ? [`options: ${jsonText(d.options)}`] : []),
442
579
  `${p.openHint(toolset)} opens it.`,
@@ -444,7 +581,7 @@ export class ToolsetsModule {
444
581
  }
445
582
  if (p.undeclaredGroups.includes(toolset)) {
446
583
  const names = p.allTools.filter((t) => p.toolsetOf(t.name) === toolset).map((t) => t.name);
447
- return textResult(`${c.serverId}/${toolset}: ${names.join(', ')}; not open: ${p.openHint(toolset)} opens it.`);
584
+ return textResult(`${c.serverId}/${nameToTypeBack(toolset)}: ${names.map(nameToTypeBack).join(', ')}; not open: ${p.openHint(toolset)} opens it.`);
448
585
  }
449
586
  return fail(noToolset(c, toolset));
450
587
  }
@@ -461,13 +598,13 @@ export class ToolsetsModule {
461
598
  }
462
599
  async close(args, take) {
463
600
  const shown = take !== null;
464
- const named = str(args.toolset);
601
+ const named = toolsetNamed(this.hub, args.server, args.toolset);
465
602
  const c = named && !str(args.server)
466
- ? owner(this.hub, undefined, (x) => x.presence.isOpen(named, str(args.instance)) || x.presence.openRecords(named).length > 0, `an open toolset "${named}"`)
603
+ ? owner(this.hub, undefined, (x) => x.presence.isOpen(named, instanceNamed([x], named, args.instance)) || x.presence.openRecords(named).length > 0, `an open toolset "${named}"`)
467
604
  : pick(this.hub, args.server);
468
605
  const p = c.presence;
469
606
  let toolset = named;
470
- let instance = str(args.instance);
607
+ let instance = instanceNamed([c], named, args.instance);
471
608
  if (!toolset) {
472
609
  const open = [
473
610
  ...p.openRecords().filter((o) => !instance || o.instance === instance),
@@ -475,7 +612,7 @@ export class ToolsetsModule {
475
612
  ];
476
613
  if (open.length !== 1) {
477
614
  fail(open.length
478
- ? `several are open on ${c.serverId}: name one of ${open.map((o) => `${o.toolset}${o.instance ? `:${o.instance}` : ''}`).join(', ')}`
615
+ ? `several are open on ${c.serverId}: name one of ${open.map((o) => `${nameToTypeBack(o.toolset)}${o.instance ? `:${nameToTypeBack(o.instance)}` : ''}`).join(', ')}`
479
616
  : `nothing is open on ${c.serverId}`);
480
617
  }
481
618
  toolset = open[0].toolset;
@@ -484,11 +621,11 @@ export class ToolsetsModule {
484
621
  if (p.isDeclared(toolset)) {
485
622
  // As open refuses one: a toolset without instances is closed without one.
486
623
  if (instance && !p.declared[toolset].instances)
487
- fail(`${c.serverId}/${toolset} has no instances: close it without one`);
624
+ fail(`${c.serverId}/${nameToTypeBack(toolset)} has no instances: close it without one`);
488
625
  if (p.declared[toolset].instances && !instance) {
489
626
  const open = p.openRecords(toolset);
490
627
  if (open.length !== 1)
491
- fail(open.length ? `several ${toolset} instances are open (${open.map((o) => o.instance).join(', ')}): say which` : `${toolset} is not open`);
628
+ fail(open.length ? `several ${nameToTypeBack(toolset)} instances are open (${open.map((o) => nameToTypeBack(String(o.instance))).join(', ')}): say which` : `${nameToTypeBack(toolset)} is not open`);
492
629
  instance = open[0].instance;
493
630
  }
494
631
  let closed;
@@ -496,7 +633,7 @@ export class ToolsetsModule {
496
633
  closed = await c.close(toolset, instance, { shown });
497
634
  }
498
635
  catch (error) {
499
- const what = `${toolset}${instance ? `:${instance}` : ''}`;
636
+ const what = `${nameToTypeBack(toolset)}${instance ? `:${nameToTypeBack(instance)}` : ''}`;
500
637
  return requestFailure(c.serverId, `did not close ${what}`, `didn't confirm closing ${what}`, error);
501
638
  }
502
639
  const key = keyOf(toolset, instance);
@@ -506,9 +643,9 @@ export class ToolsetsModule {
506
643
  }
507
644
  if (p.undeclaredGroups.includes(toolset)) {
508
645
  if (instance)
509
- fail(`${c.serverId}/${toolset} takes no instance (the server doesn't declare it)`);
646
+ fail(`${c.serverId}/${nameToTypeBack(toolset)} takes no instance (the server doesn't declare it)`);
510
647
  if (!p.isOpen(toolset))
511
- fail(`${c.serverId}/${toolset} is not open`);
648
+ fail(`${c.serverId}/${nameToTypeBack(toolset)} is not open`);
512
649
  const cs = c.setGroupOpen(toolset, false);
513
650
  const text = p.render(cs, { mark: shown, describeAll: !shown }) || `${p.label(keyOf(toolset))} closed.`;
514
651
  if (take)