@aria-cli/tools 1.0.9 → 1.0.11

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 (241) hide show
  1. package/package.json +9 -5
  2. package/src/__tests__/web-fetch-download.test.ts +0 -433
  3. package/src/__tests__/web-tools.test.ts +0 -619
  4. package/src/ask-user-interaction.ts +0 -33
  5. package/src/cache/web-cache.ts +0 -110
  6. package/src/definitions/arion.ts +0 -118
  7. package/src/definitions/browser/browser.ts +0 -502
  8. package/src/definitions/browser/index.ts +0 -5
  9. package/src/definitions/browser/pw-downloads.ts +0 -142
  10. package/src/definitions/browser/pw-interactions.ts +0 -282
  11. package/src/definitions/browser/pw-responses.ts +0 -98
  12. package/src/definitions/browser/pw-session.ts +0 -405
  13. package/src/definitions/browser/pw-shared.ts +0 -85
  14. package/src/definitions/browser/pw-snapshot.ts +0 -383
  15. package/src/definitions/browser/pw-state.ts +0 -101
  16. package/src/definitions/browser/types.ts +0 -203
  17. package/src/definitions/code-intelligence.ts +0 -526
  18. package/src/definitions/core.ts +0 -118
  19. package/src/definitions/delegation.ts +0 -567
  20. package/src/definitions/deploy.ts +0 -73
  21. package/src/definitions/filesystem.ts +0 -217
  22. package/src/definitions/frg.ts +0 -67
  23. package/src/definitions/index.ts +0 -28
  24. package/src/definitions/memory.ts +0 -150
  25. package/src/definitions/messaging.ts +0 -734
  26. package/src/definitions/meta.ts +0 -392
  27. package/src/definitions/network.ts +0 -179
  28. package/src/definitions/outlook.ts +0 -318
  29. package/src/definitions/patch/apply-patch.ts +0 -235
  30. package/src/definitions/patch/fuzzy-match.ts +0 -217
  31. package/src/definitions/patch/index.ts +0 -1
  32. package/src/definitions/patch/patch-parser.ts +0 -297
  33. package/src/definitions/patch/sandbox-paths.ts +0 -129
  34. package/src/definitions/process/index.ts +0 -5
  35. package/src/definitions/process/process-registry.ts +0 -303
  36. package/src/definitions/process/process.ts +0 -456
  37. package/src/definitions/process/pty-keys.ts +0 -298
  38. package/src/definitions/process/session-slug.ts +0 -147
  39. package/src/definitions/quip.ts +0 -225
  40. package/src/definitions/search.ts +0 -67
  41. package/src/definitions/session-history.ts +0 -79
  42. package/src/definitions/shell.ts +0 -202
  43. package/src/definitions/slack.ts +0 -211
  44. package/src/definitions/web.ts +0 -119
  45. package/src/executors/apply-patch.ts +0 -1035
  46. package/src/executors/arion.ts +0 -199
  47. package/src/executors/code-intelligence.ts +0 -1179
  48. package/src/executors/deploy.ts +0 -1066
  49. package/src/executors/filesystem.ts +0 -1428
  50. package/src/executors/frg-freshness.ts +0 -743
  51. package/src/executors/frg.ts +0 -394
  52. package/src/executors/index.ts +0 -280
  53. package/src/executors/learning-meta.ts +0 -1367
  54. package/src/executors/lsp-client.ts +0 -355
  55. package/src/executors/memory.ts +0 -978
  56. package/src/executors/meta.ts +0 -293
  57. package/src/executors/process-registry.ts +0 -570
  58. package/src/executors/pty-session-store.ts +0 -43
  59. package/src/executors/pty.ts +0 -342
  60. package/src/executors/restart.ts +0 -133
  61. package/src/executors/search-freshness.ts +0 -249
  62. package/src/executors/search-types.ts +0 -98
  63. package/src/executors/search.ts +0 -89
  64. package/src/executors/self-diagnose.ts +0 -552
  65. package/src/executors/session-history.ts +0 -435
  66. package/src/executors/shell-safety.ts +0 -519
  67. package/src/executors/shell.ts +0 -1243
  68. package/src/executors/utils.ts +0 -40
  69. package/src/executors/web.ts +0 -786
  70. package/src/extraction/content-extraction.ts +0 -281
  71. package/src/extraction/index.ts +0 -5
  72. package/src/headless-control-contract.ts +0 -1149
  73. package/src/index.ts +0 -788
  74. package/src/local-control-http-auth.ts +0 -2
  75. package/src/mcp/client.ts +0 -218
  76. package/src/mcp/connection.ts +0 -568
  77. package/src/mcp/index.ts +0 -11
  78. package/src/mcp/jsonrpc.ts +0 -195
  79. package/src/mcp/types.ts +0 -199
  80. package/src/network-control-adapter.ts +0 -88
  81. package/src/network-runtime/address-types.ts +0 -218
  82. package/src/network-runtime/db-owner-fencing.ts +0 -91
  83. package/src/network-runtime/delivery-receipts.ts +0 -372
  84. package/src/network-runtime/direct-endpoint-authority.ts +0 -35
  85. package/src/network-runtime/index.ts +0 -316
  86. package/src/network-runtime/local-control-contract.ts +0 -784
  87. package/src/network-runtime/node-store-contract.ts +0 -46
  88. package/src/network-runtime/pair-route-contract.ts +0 -97
  89. package/src/network-runtime/peer-capabilities.ts +0 -48
  90. package/src/network-runtime/peer-principal-ref.ts +0 -20
  91. package/src/network-runtime/peer-state-machine.ts +0 -160
  92. package/src/network-runtime/protocol-schemas.ts +0 -265
  93. package/src/network-runtime/runtime-bootstrap-contract.ts +0 -83
  94. package/src/outlook/desktop-session.ts +0 -409
  95. package/src/policy.ts +0 -171
  96. package/src/providers/brave.ts +0 -80
  97. package/src/providers/duckduckgo.ts +0 -199
  98. package/src/providers/exa.ts +0 -85
  99. package/src/providers/firecrawl.ts +0 -77
  100. package/src/providers/index.ts +0 -8
  101. package/src/providers/jina.ts +0 -70
  102. package/src/providers/router.ts +0 -121
  103. package/src/providers/search-provider.ts +0 -74
  104. package/src/providers/tavily.ts +0 -74
  105. package/src/quip/desktop-session.ts +0 -435
  106. package/src/registry/index.ts +0 -1
  107. package/src/registry/registry.ts +0 -905
  108. package/src/runtime-socket-local-control-client.ts +0 -632
  109. package/src/security/dns-normalization.ts +0 -34
  110. package/src/security/dns-pinning.ts +0 -138
  111. package/src/security/external-content.ts +0 -129
  112. package/src/security/ssrf.ts +0 -207
  113. package/src/slack/desktop-session.ts +0 -493
  114. package/src/tool-factory.ts +0 -91
  115. package/src/types.ts +0 -1341
  116. package/src/utils/retry.ts +0 -163
  117. package/src/utils/safe-parse-json.ts +0 -176
  118. package/src/utils/url.ts +0 -20
  119. package/tests/benchmarks/registry.bench.ts +0 -57
  120. package/tests/cache/web-cache.test.ts +0 -147
  121. package/tests/critical-integration.test.ts +0 -1465
  122. package/tests/definitions/apply-patch.test.ts +0 -586
  123. package/tests/definitions/browser.test.ts +0 -495
  124. package/tests/definitions/delegation-pause-resume.test.ts +0 -758
  125. package/tests/definitions/execution.test.ts +0 -671
  126. package/tests/definitions/messaging-inbox-scope.test.ts +0 -229
  127. package/tests/definitions/messaging.test.ts +0 -1468
  128. package/tests/definitions/outlook.test.ts +0 -30
  129. package/tests/definitions/process.test.ts +0 -469
  130. package/tests/definitions/slack.test.ts +0 -28
  131. package/tests/definitions/tool-inventory.test.ts +0 -218
  132. package/tests/e2e/delegation-quest-orchestration.e2e.test.ts +0 -433
  133. package/tests/e2e/memory-tool-discovery-contract.e2e.test.ts +0 -81
  134. package/tests/executors/apply-patch.test.ts +0 -538
  135. package/tests/executors/arion.test.ts +0 -309
  136. package/tests/executors/conversation-primitives.test.ts +0 -250
  137. package/tests/executors/deploy.test.ts +0 -746
  138. package/tests/executors/filesystem-tools.test.ts +0 -357
  139. package/tests/executors/filesystem.test.ts +0 -959
  140. package/tests/executors/frg-freshness.test.ts +0 -136
  141. package/tests/executors/frg-merge.test.ts +0 -70
  142. package/tests/executors/frg-session-content.test.ts +0 -40
  143. package/tests/executors/frg.test.ts +0 -56
  144. package/tests/executors/memory-bugfixes.test.ts +0 -257
  145. package/tests/executors/memory-real-memoria.integration.test.ts +0 -316
  146. package/tests/executors/memory.test.ts +0 -853
  147. package/tests/executors/meta-tools.test.ts +0 -411
  148. package/tests/executors/meta.test.ts +0 -683
  149. package/tests/executors/path-containment.test.ts +0 -51
  150. package/tests/executors/process-registry.test.ts +0 -505
  151. package/tests/executors/pty.test.ts +0 -664
  152. package/tests/executors/quest-security.test.ts +0 -249
  153. package/tests/executors/read-file-media.test.ts +0 -230
  154. package/tests/executors/recall-knowledge-schema.test.ts +0 -209
  155. package/tests/executors/recall-tags.test.ts +0 -278
  156. package/tests/executors/remember-null-safety.contract.test.ts +0 -41
  157. package/tests/executors/restart.test.ts +0 -67
  158. package/tests/executors/search-unified.test.ts +0 -381
  159. package/tests/executors/session-history.test.ts +0 -340
  160. package/tests/executors/session-transcript.test.ts +0 -561
  161. package/tests/executors/shell-abort.test.ts +0 -416
  162. package/tests/executors/shell-env-blocklist.test.ts +0 -648
  163. package/tests/executors/shell-env-process.test.ts +0 -245
  164. package/tests/executors/shell-process-registry.test.ts +0 -334
  165. package/tests/executors/shell-tools.test.ts +0 -393
  166. package/tests/executors/shell.test.ts +0 -690
  167. package/tests/executors/web-abort-vs-timeout.test.ts +0 -213
  168. package/tests/executors/web-integration.test.ts +0 -633
  169. package/tests/executors/web-symlink.test.ts +0 -18
  170. package/tests/executors/web.test.ts +0 -1400
  171. package/tests/executors/write-stdin.test.ts +0 -145
  172. package/tests/extraction/content-extraction.test.ts +0 -153
  173. package/tests/guards/tools-default-test-lane.integration.test.ts +0 -21
  174. package/tests/guards/tools-package-test-commands.e2e.test.ts +0 -43
  175. package/tests/guards/tools-test-lane-manifest.contract.test.ts +0 -76
  176. package/tests/guards/tools-vitest-workspace-alias.contract.test.ts +0 -63
  177. package/tests/helpers/async-waits.ts +0 -53
  178. package/tests/integration/headless-control-contract.integration.test.ts +0 -153
  179. package/tests/integration/memory-tool-schema-parity.integration.test.ts +0 -67
  180. package/tests/integration/meta-tools-round-trip.integration.test.ts +0 -506
  181. package/tests/integration/quest-round-trip.test.ts +0 -303
  182. package/tests/integration/registry-executor-flow.test.ts +0 -85
  183. package/tests/integration.test.ts +0 -177
  184. package/tests/loading-tier.test.ts +0 -126
  185. package/tests/mcp/client-reconnect.test.ts +0 -267
  186. package/tests/mcp/connection.test.ts +0 -846
  187. package/tests/mcp/injectable-logger.test.ts +0 -83
  188. package/tests/mcp/jsonrpc.test.ts +0 -109
  189. package/tests/mcp/lifecycle.test.ts +0 -879
  190. package/tests/network-runtime/address-types.contract.test.ts +0 -143
  191. package/tests/network-runtime/continuity-bind-schema.contract.test.ts +0 -203
  192. package/tests/network-runtime/local-control-contract.test.ts +0 -869
  193. package/tests/network-runtime/local-control-invite-token.contract.test.ts +0 -146
  194. package/tests/network-runtime/node-store-contract.test.ts +0 -11
  195. package/tests/network-runtime/pair-protocol-nodeid.contract.test.ts +0 -15
  196. package/tests/network-runtime/peer-state-machine.contract.test.ts +0 -148
  197. package/tests/network-runtime/protocol-schemas.contract.test.ts +0 -512
  198. package/tests/network-runtime/relay-pending-nodeid.contract.test.ts +0 -62
  199. package/tests/network-runtime/runtime-bootstrap-contract.test.ts +0 -227
  200. package/tests/network-runtime/runtime-socket-local-control-client.test.ts +0 -621
  201. package/tests/network-runtime/wait-for-message-script.test.ts +0 -288
  202. package/tests/parallel.test.ts +0 -71
  203. package/tests/policy.test.ts +0 -184
  204. package/tests/print-default-test-lane.ts +0 -14
  205. package/tests/print-test-lane-manifest.ts +0 -22
  206. package/tests/providers/brave.test.ts +0 -159
  207. package/tests/providers/duckduckgo.test.ts +0 -207
  208. package/tests/providers/exa.test.ts +0 -175
  209. package/tests/providers/firecrawl.test.ts +0 -168
  210. package/tests/providers/jina.test.ts +0 -144
  211. package/tests/providers/router.test.ts +0 -328
  212. package/tests/providers/tavily.test.ts +0 -165
  213. package/tests/registry/discovery.test.ts +0 -154
  214. package/tests/registry/injectable-logger.test.ts +0 -230
  215. package/tests/registry/input-validation.test.ts +0 -361
  216. package/tests/registry/interface-completeness.test.ts +0 -85
  217. package/tests/registry/mcp-integration.test.ts +0 -103
  218. package/tests/registry/mcp-read-only-hint.test.ts +0 -60
  219. package/tests/registry/memoria-discovery.test.ts +0 -390
  220. package/tests/registry/nested-validation.test.ts +0 -283
  221. package/tests/registry/pseudo-tool-filtering.test.ts +0 -258
  222. package/tests/registry/registration-lifecycle.test.ts +0 -133
  223. package/tests/registry-validation.test.ts +0 -424
  224. package/tests/registry.test.ts +0 -460
  225. package/tests/security/dns-pinning.test.ts +0 -162
  226. package/tests/security/external-content.test.ts +0 -144
  227. package/tests/security/ssrf.test.ts +0 -118
  228. package/tests/shell-safety-integration.test.ts +0 -32
  229. package/tests/shell-safety.test.ts +0 -365
  230. package/tests/slack/desktop-session.test.ts +0 -50
  231. package/tests/test-lane-manifest.ts +0 -440
  232. package/tests/test-utils.ts +0 -27
  233. package/tests/tool-factory.test.ts +0 -188
  234. package/tests/utils/retry.test.ts +0 -231
  235. package/tests/utils/url.test.ts +0 -63
  236. package/tsconfig.cjs.json +0 -24
  237. package/tsconfig.json +0 -12
  238. package/vitest.config.ts +0 -55
  239. package/vitest.e2e.config.ts +0 -24
  240. package/vitest.integration.config.ts +0 -24
  241. package/vitest.native.config.ts +0 -24
@@ -1,734 +0,0 @@
1
- /**
2
- * Messaging tool definitions
3
- *
4
- * Tools: send_message, check_messages, search_messages, get_thread (4 tools)
5
- *
6
- * Inter-agent messaging for arions, workers, and leaders.
7
- * All tools use context.messageStore (injected by runner when Memoria is available).
8
- * When the message store isn't wired, tools return a clear error.
9
- */
10
-
11
- import crypto from "node:crypto";
12
- import { z } from "zod";
13
- import { tool } from "../tool-factory.js";
14
- import type { ToolContext } from "../types.js";
15
- import { canonicalizeDeliveryReceipt, InboxAddressSchema } from "../network-runtime/index.js";
16
- import type {
17
- AttachedClientView,
18
- ClientId,
19
- InboxAddress,
20
- NodeId,
21
- } from "../network-runtime/index.js";
22
-
23
- const SENDER_INBOX_METADATA_KEY = "senderInbox";
24
-
25
- /**
26
- * Resolve the local inbox address from ToolContext.
27
- *
28
- * Throws instead of returning null — a missing inbox address is a wiring bug
29
- * (the entrypoint failed to set nodeId), not a user error. Silent message
30
- * drops are structurally impossible: tools either route correctly or crash loudly.
31
- */
32
- function resolveInboxAddress(context: ToolContext): InboxAddress {
33
- if (context.inboxAddress) {
34
- return context.inboxAddress;
35
- }
36
- if (context.nodeId) {
37
- return { kind: "node", nodeId: context.nodeId };
38
- }
39
- throw new Error(
40
- "Inbox address not available: neither context.inboxAddress nor context.nodeId is set. " +
41
- "The entrypoint must wire nodeId into ToolContext (via RunOptions.nodeId or direct inboxAddress). " +
42
- "This is a wiring bug — check tool-executor.ts and RunSession.toRunOptions().",
43
- );
44
- }
45
-
46
- /**
47
- * Resolve ALL inbox addresses the caller should see.
48
- *
49
- * When the runner is an attached client ({kind: "client", clientId}), remote
50
- * peer messages still land in the node inbox ({kind: "node", nodeId}). The TUI
51
- * status bar already polls both — this helper lets the read tools do the same
52
- * so the LLM sees everything the user sees.
53
- */
54
- function resolveAllInboxAddresses(context: ToolContext): InboxAddress[] {
55
- const primary = resolveInboxAddress(context);
56
- if (primary.kind === "client" && context.nodeId) {
57
- return [primary, { kind: "node", nodeId: context.nodeId }];
58
- }
59
- return [primary];
60
- }
61
-
62
- function resolveSenderIdentity(
63
- context: ToolContext,
64
- inboxAddress: InboxAddress,
65
- ): { id: string; name: string; type: "arion" | "worker" | "leader" } {
66
- const senderType: "arion" | "worker" | "leader" = context.arion
67
- ? "arion"
68
- : (context.senderType ?? "leader");
69
-
70
- if (context.arion) {
71
- return {
72
- id: context.arion.id,
73
- name: context.arion.name,
74
- type: senderType,
75
- };
76
- }
77
-
78
- return {
79
- id:
80
- context.nodeId ??
81
- (inboxAddress.kind === "client" ? inboxAddress.clientId : inboxAddress.nodeId),
82
- name: "ARIA",
83
- type: senderType,
84
- };
85
- }
86
-
87
- type ResolvedRecipient =
88
- | {
89
- kind: "remote";
90
- nodeId: NodeId;
91
- recipientName: string;
92
- }
93
- | {
94
- kind: "local";
95
- mailboxId: string;
96
- recipientName: string;
97
- }
98
- | {
99
- kind: "client";
100
- clientId: ClientId;
101
- recipientName: string;
102
- self: boolean;
103
- };
104
-
105
- async function listAttachedClients(context: ToolContext): Promise<AttachedClientView[]> {
106
- return Promise.resolve(context.networkControl?.listAttachedClients?.() ?? []);
107
- }
108
-
109
- function buildUnknownRecipientError(context: ToolContext, recipientRef: string): string {
110
- const localRecipientHint = context.manager
111
- ? "registered local mailbox id or arion name"
112
- : "registered local mailbox id or alias";
113
- const sameHomeHint = context.networkControl?.listAttachedClients
114
- ? "exact same-home clientId from list_clients"
115
- : "exact same-home clientId";
116
- return (
117
- `Unknown recipient "${recipientRef}". ` +
118
- `send_message requires an exact recipient identity: ${localRecipientHint}, exact remote nodeId, or ${sameHomeHint}.`
119
- );
120
- }
121
-
122
- async function resolveManagedLocalRecipient(
123
- context: ToolContext,
124
- recipientRef: string,
125
- ): Promise<ResolvedRecipient | null> {
126
- const manager = context.manager;
127
- if (!manager) {
128
- return null;
129
- }
130
-
131
- const [directByName, listed] = await Promise.all([
132
- manager.get(recipientRef).catch(() => null),
133
- manager.list().catch(() => []),
134
- ]);
135
-
136
- const exactMatch =
137
- directByName ??
138
- listed.find((candidate) => candidate.id === recipientRef || candidate.name === recipientRef) ??
139
- null;
140
- if (!exactMatch) {
141
- return null;
142
- }
143
-
144
- return {
145
- kind: "local",
146
- mailboxId: exactMatch.id,
147
- recipientName: exactMatch.name,
148
- };
149
- }
150
-
151
- function extractSenderInboxFromMetadata(
152
- metadata: string | null | undefined,
153
- ): InboxAddress | undefined {
154
- if (!metadata) {
155
- return undefined;
156
- }
157
-
158
- try {
159
- const parsed = JSON.parse(metadata) as Record<string, unknown>;
160
- if (!(SENDER_INBOX_METADATA_KEY in parsed)) {
161
- return undefined;
162
- }
163
- return InboxAddressSchema.parse(parsed[SENDER_INBOX_METADATA_KEY]);
164
- } catch {
165
- return undefined;
166
- }
167
- }
168
-
169
- async function resolveRecipientFromReplyTarget(
170
- context: ToolContext,
171
- replyTo: string,
172
- inboxAddress: InboxAddress,
173
- ): Promise<{ ok: true; recipient: ResolvedRecipient } | { ok: false; error: string }> {
174
- if (typeof context.messageStore?.getMessageForInbox !== "function") {
175
- return {
176
- ok: false,
177
- error:
178
- "Reply routing is unavailable: the message store cannot resolve inbox-scoped message ids.",
179
- };
180
- }
181
-
182
- const original = context.messageStore.getMessageForInbox(inboxAddress, replyTo);
183
- if (!original) {
184
- return {
185
- ok: false,
186
- error: `Cannot reply to message "${replyTo}" because it is not present in the current inbox.`,
187
- };
188
- }
189
-
190
- const senderInbox = extractSenderInboxFromMetadata(original.metadata);
191
- if (senderInbox?.kind === "client") {
192
- return {
193
- ok: true,
194
- recipient: {
195
- kind: "client",
196
- clientId: senderInbox.clientId,
197
- recipientName: original.sender_name,
198
- self: inboxAddress.kind === "client" && senderInbox.clientId === inboxAddress.clientId,
199
- },
200
- };
201
- }
202
-
203
- return resolveRecipientIdentity(context, original.sender_id, inboxAddress);
204
- }
205
-
206
- async function resolveRecipientIdentity(
207
- context: ToolContext,
208
- recipientRef: string,
209
- inboxAddress: InboxAddress,
210
- ): Promise<{ ok: true; recipient: ResolvedRecipient } | { ok: false; error: string }> {
211
- const localMailbox = context.mailbox;
212
- const peers = context.networkControl?.listPeers() ?? [];
213
- const attachedClients = await listAttachedClients(context);
214
- const exactClient = attachedClients.find((client) => client.clientId === recipientRef);
215
- if (exactClient) {
216
- return {
217
- ok: true,
218
- recipient: {
219
- kind: "client",
220
- clientId: exactClient.clientId,
221
- recipientName: exactClient.displayLabel.trim() || exactClient.clientId,
222
- self: exactClient.self,
223
- },
224
- };
225
- }
226
- const exactPeer = peers.find((peer) => peer.nodeId === recipientRef);
227
- if (exactPeer) {
228
- if (
229
- exactPeer.routeOwnership === "superseded" ||
230
- exactPeer.deliveryReadiness === "cannot_address"
231
- ) {
232
- return {
233
- ok: false,
234
- error:
235
- `Peer "${recipientRef}" is not currently addressable` +
236
- (exactPeer.routeOwnership === "superseded"
237
- ? " because its direct route claim has been superseded."
238
- : "."),
239
- };
240
- }
241
- return {
242
- ok: true,
243
- recipient: {
244
- kind: "remote",
245
- nodeId: exactPeer.nodeId,
246
- recipientName: exactPeer.displayNameSnapshot ?? exactPeer.nodeId,
247
- },
248
- };
249
- }
250
-
251
- const hasLocalRecipientId =
252
- typeof localMailbox?.has === "function" && localMailbox.has(recipientRef);
253
- if (hasLocalRecipientId) {
254
- return {
255
- ok: true,
256
- recipient: {
257
- kind: "local",
258
- mailboxId: recipientRef,
259
- recipientName: recipientRef,
260
- },
261
- };
262
- }
263
-
264
- const hasLocalRecipientAlias =
265
- typeof localMailbox?.hasByName === "function" && localMailbox.hasByName(recipientRef);
266
- if (hasLocalRecipientAlias) {
267
- const canonicalLocalRecipientId =
268
- typeof localMailbox?.resolveId === "function"
269
- ? localMailbox.resolveId(recipientRef)
270
- : undefined;
271
- if (!canonicalLocalRecipientId) {
272
- return {
273
- ok: false,
274
- error: `Registered local alias "${recipientRef}" is missing a canonical mailbox id`,
275
- };
276
- }
277
- return {
278
- ok: true,
279
- recipient: {
280
- kind: "local",
281
- mailboxId: canonicalLocalRecipientId,
282
- recipientName: recipientRef,
283
- },
284
- };
285
- }
286
-
287
- const managedLocalRecipient = await resolveManagedLocalRecipient(context, recipientRef);
288
- if (managedLocalRecipient) {
289
- return {
290
- ok: true,
291
- recipient: managedLocalRecipient,
292
- };
293
- }
294
-
295
- // Resolve by display name — allows @mentioning peers by name instead of nodeId.
296
- const recipientRefLower = recipientRef.toLowerCase().replace(/^@/, "");
297
- const displayMatches = peers.filter(
298
- (peer) =>
299
- peer.displayNameSnapshot?.toLowerCase() === recipientRefLower ||
300
- peer.displayNameSnapshot?.toLowerCase() === recipientRef.toLowerCase(),
301
- );
302
- if (displayMatches.length > 1) {
303
- return { ok: false, error: `Ambiguous peer display name "${recipientRef}"` };
304
- }
305
- if (displayMatches.length === 1) {
306
- const matchedPeer = displayMatches[0]!;
307
- if (
308
- matchedPeer.routeOwnership === "superseded" ||
309
- matchedPeer.deliveryReadiness === "cannot_address"
310
- ) {
311
- return {
312
- ok: false,
313
- error: `Peer "${matchedPeer.displayNameSnapshot}" is not currently addressable.`,
314
- };
315
- }
316
- return {
317
- ok: true,
318
- recipient: {
319
- kind: "remote",
320
- nodeId: matchedPeer.nodeId,
321
- recipientName: matchedPeer.displayNameSnapshot ?? matchedPeer.nodeId,
322
- },
323
- };
324
- }
325
-
326
- const clientDisplayMatches = attachedClients.filter(
327
- (client) =>
328
- client.displayLabel?.toLowerCase() === recipientRefLower ||
329
- client.displayLabel?.toLowerCase() === recipientRef.toLowerCase(),
330
- );
331
- if (clientDisplayMatches.length > 1) {
332
- return { ok: false, error: `Ambiguous same-home client label "${recipientRef}"` };
333
- }
334
- if (clientDisplayMatches.length === 1) {
335
- const matchedClient = clientDisplayMatches[0]!;
336
- return {
337
- ok: true,
338
- recipient: {
339
- kind: "client",
340
- clientId: matchedClient.clientId,
341
- recipientName: matchedClient.displayLabel?.trim() || matchedClient.clientId,
342
- self: matchedClient.self,
343
- },
344
- };
345
- }
346
-
347
- if (inboxAddress.kind === "client") {
348
- return {
349
- ok: false,
350
- error: buildUnknownRecipientError(context, recipientRef),
351
- };
352
- }
353
-
354
- return {
355
- ok: true,
356
- recipient: {
357
- kind: "local",
358
- mailboxId: recipientRef,
359
- recipientName: recipientRef,
360
- },
361
- };
362
- }
363
-
364
- /**
365
- * Send a message to any agent (arion, worker, or leader).
366
- * Messages are persisted and can be threaded via replyTo or correlated via correlationId.
367
- */
368
- const send_message = tool({
369
- name: "send_message",
370
- description:
371
- "Send a message to any agent (arion, worker, or leader). " +
372
- "Use @name or display name to address peers and clients (e.g. @vm1, @local-machine), " +
373
- "or use an exact nodeId/clientId for precision. " +
374
- "When replying to a received message, prefer replyTo and omit to so the tool routes back to the original sender automatically. " +
375
- "Messages are persisted and can be threaded via replyTo or correlated via correlationId.",
376
- parameters: z
377
- .object({
378
- to: z
379
- .string()
380
- .optional()
381
- .describe(
382
- "Recipient: @name or display name of a peer/client (e.g. @vm1, @local-machine), " +
383
- "or exact nodeId/clientId for precision",
384
- ),
385
- type: z
386
- .enum([
387
- "quest",
388
- "quest_update",
389
- "progress",
390
- "finding",
391
- "question",
392
- "answer",
393
- "review_request",
394
- "review_result",
395
- "approval",
396
- "directive",
397
- "context",
398
- "announcement",
399
- "error",
400
- ])
401
- .describe("Message type"),
402
- content: z.string().describe("Message content"),
403
- replyTo: z
404
- .string()
405
- .optional()
406
- .describe(
407
- "Message ID to reply to. When provided without to, the tool replies to the original sender automatically.",
408
- ),
409
- correlationId: z
410
- .string()
411
- .optional()
412
- .describe("Correlation ID for sub-thread conversations within a quest"),
413
- questId: z
414
- .string()
415
- .optional()
416
- .describe("Quest ID this message belongs to (e.g. quest_abc123)"),
417
- priority: z
418
- .number()
419
- .min(0)
420
- .max(4)
421
- .optional()
422
- .describe("Priority: 0=critical, 2=normal (default), 4=backlog"),
423
- })
424
- .superRefine((input, ctx) => {
425
- if (!input.to && !input.replyTo) {
426
- ctx.addIssue({
427
- code: z.ZodIssueCode.custom,
428
- path: ["to"],
429
- message: 'Either "to" or "replyTo" is required.',
430
- });
431
- }
432
- }),
433
- category: "messaging",
434
- riskLevel: "moderate",
435
- isReadOnly: false,
436
- execute: async (input, context) => {
437
- if (!context.messageStore) {
438
- return {
439
- success: false,
440
- message: "Message store not available. Messaging requires Memoria.",
441
- };
442
- }
443
- const inboxAddress = resolveInboxAddress(context);
444
- const recipient =
445
- input.to && input.to.trim().length > 0
446
- ? await resolveRecipientIdentity(context, input.to, inboxAddress)
447
- : input.replyTo
448
- ? await resolveRecipientFromReplyTarget(context, input.replyTo, inboxAddress)
449
- : { ok: false as const, error: 'Either "to" or "replyTo" is required.' };
450
- if (!recipient.ok) {
451
- return {
452
- success: false,
453
- message: recipient.error,
454
- };
455
- }
456
- if (recipient.recipient.kind === "client" && recipient.recipient.self) {
457
- return {
458
- success: false,
459
- message: "Cannot send a same-home message to yourself.",
460
- };
461
- }
462
- const recipientId =
463
- recipient.recipient.kind === "remote"
464
- ? recipient.recipient.nodeId
465
- : recipient.recipient.kind === "client"
466
- ? recipient.recipient.clientId
467
- : recipient.recipient.mailboxId;
468
- const sender = resolveSenderIdentity(context, inboxAddress);
469
- const resolvedTargetLabel = input.to ?? recipient.recipient.recipientName;
470
- const msg = {
471
- id: crypto.randomUUID(),
472
- version: 1 as const,
473
- sender,
474
- recipient: { id: recipientId, name: recipient.recipient.recipientName },
475
- replyTo: input.replyTo,
476
- correlationId: input.correlationId,
477
- questId: input.questId,
478
- type: input.type,
479
- content: input.content,
480
- metadata:
481
- inboxAddress.kind === "client"
482
- ? {
483
- [SENDER_INBOX_METADATA_KEY]: inboxAddress,
484
- }
485
- : undefined,
486
- timestamp: Date.now(),
487
- priority: (input.priority ?? 2) as 0 | 1 | 2 | 3 | 4,
488
- ...(recipient.recipient.kind === "client"
489
- ? {
490
- recipientInbox: {
491
- kind: "client" as const,
492
- clientId: recipient.recipient.clientId,
493
- },
494
- }
495
- : {}),
496
- };
497
-
498
- // Route through Mailbox if available (handles delivery + persistence)
499
- if (context.mailbox) {
500
- try {
501
- const receipt = canonicalizeDeliveryReceipt(await context.mailbox.sendDurable(msg));
502
- const receiptSummary =
503
- receipt.deliveryState === "queued_for_route"
504
- ? `accepted and queued for route establishment`
505
- : receipt.deliveryState === "dispatching"
506
- ? `sent and awaiting remote acknowledgement`
507
- : receipt.delivered
508
- ? `delivered`
509
- : `sent`;
510
- const routeSummary = receipt.sessionState
511
- ? ` session=${receipt.sessionState}, delivery=${receipt.deliveryReadiness ?? "unknown"}`
512
- : "";
513
- const reasonSummary = receipt.queuedReason ? ` reason=${receipt.queuedReason}` : "";
514
- return {
515
- success: true,
516
- message:
517
- `Message ${receiptSummary} for "${resolvedTargetLabel}" via ${receipt.transport}` +
518
- `${routeSummary}${reasonSummary} (type: ${input.type}, id: ${msg.id})`,
519
- data: {
520
- id: msg.id,
521
- to: recipientId,
522
- type: input.type,
523
- delivered: receipt.delivered,
524
- queued: receipt.queued,
525
- transport: receipt.transport,
526
- accepted: receipt.accepted ?? true,
527
- deliveryState: receipt.deliveryState,
528
- sessionState: receipt.sessionState,
529
- deliveryReadiness: receipt.deliveryReadiness,
530
- queuedReason: receipt.queuedReason,
531
- },
532
- };
533
- } catch (e) {
534
- // Fail closed for known remote peers — a local fallback is not delivery.
535
- if (recipient.recipient.kind === "remote") {
536
- context.messageStore.store(msg, "sent", inboxAddress);
537
- return {
538
- success: false,
539
- message: `Message delivery failed for "${resolvedTargetLabel}": ${(e as Error).message}`,
540
- data: {
541
- id: msg.id,
542
- to: recipient.recipient.nodeId,
543
- type: input.type,
544
- delivered: false,
545
- },
546
- };
547
- }
548
-
549
- // Local fallback only makes sense when no remote peer principal was resolved.
550
- context.messageStore.store(msg, "sent", inboxAddress);
551
- return {
552
- success: true,
553
- message: `Message stored locally for "${resolvedTargetLabel}" but delivery failed: ${(e as Error).message}`,
554
- data: { id: msg.id, to: recipientId, type: input.type, delivered: false },
555
- };
556
- }
557
- }
558
-
559
- // No Mailbox — store locally only (single-process mode)
560
- context.messageStore.store(msg, "sent", inboxAddress);
561
- return {
562
- success: true,
563
- message: `Message sent to "${resolvedTargetLabel}" (type: ${input.type}, id: ${msg.id})`,
564
- data: { id: msg.id, to: recipientId, type: input.type, delivered: false },
565
- };
566
- },
567
- });
568
-
569
- /**
570
- * Check your message inbox.
571
- * Returns unread messages by default, with optional filters.
572
- */
573
- const check_messages = tool({
574
- name: "check_messages",
575
- description:
576
- "Check your message inbox. Returns unread messages by default, with optional filters.",
577
- parameters: z.object({
578
- unreadOnly: z.boolean().optional().describe("Only show unread messages (default: true)"),
579
- from: z.string().optional().describe("Filter by sender name"),
580
- type: z.string().optional().describe("Filter by message type"),
581
- correlationId: z.string().optional().describe("Filter by correlation ID"),
582
- limit: z.number().optional().describe("Max messages to return (default: 20)"),
583
- }),
584
- category: "messaging",
585
- riskLevel: "safe",
586
- isReadOnly: true,
587
- execute: async (input, context) => {
588
- if (!context.messageStore) {
589
- return { success: false, message: "Message store not available." };
590
- }
591
- const inboxAddresses = resolveAllInboxAddresses(context);
592
- const unreadOnly = input.unreadOnly ?? true;
593
- const limit = input.limit ?? 20;
594
-
595
- if (unreadOnly) {
596
- const seen = new Set<string>();
597
- const messages = inboxAddresses
598
- .flatMap((addr) => context.messageStore!.getUnreadForInbox(addr, limit))
599
- .filter((m) => {
600
- if (seen.has(m.id)) return false;
601
- seen.add(m.id);
602
- return true;
603
- })
604
- .slice(0, limit);
605
- if (messages.length === 0) {
606
- return { success: true, message: "No unread messages.", data: { messages: [] } };
607
- }
608
- // Auto-mark as read
609
- context.messageStore.markRead(messages.map((m) => m.id));
610
- return {
611
- success: true,
612
- message: `${messages.length} message(s) received`,
613
- data: { messages },
614
- };
615
- }
616
-
617
- // Search with filters
618
- const seen = new Set<string>();
619
- const messages = inboxAddresses
620
- .flatMap((addr) =>
621
- context.messageStore!.searchInbox(addr, "", {
622
- from: input.from,
623
- type: input.type,
624
- correlationId: input.correlationId,
625
- limit,
626
- }),
627
- )
628
- .filter((m) => {
629
- if (seen.has(m.id)) return false;
630
- seen.add(m.id);
631
- return true;
632
- })
633
- .slice(0, limit);
634
- return {
635
- success: true,
636
- message: `${messages.length} message(s) found`,
637
- data: { messages },
638
- };
639
- },
640
- });
641
-
642
- /**
643
- * Search message history by content with optional filters.
644
- */
645
- const search_messages = tool({
646
- name: "search_messages",
647
- description: "Search message history by content with optional filters.",
648
- parameters: z.object({
649
- query: z.string().describe("Search text (matched against message content)"),
650
- from: z.string().optional().describe("Filter by sender name"),
651
- type: z.string().optional().describe("Filter by message type"),
652
- correlationId: z.string().optional().describe("Filter by correlation ID"),
653
- after: z.number().optional().describe("Only messages after this timestamp (epoch ms)"),
654
- limit: z.number().optional().describe("Max results (default: 20)"),
655
- }),
656
- category: "messaging",
657
- riskLevel: "safe",
658
- isReadOnly: true,
659
- execute: async (input, context) => {
660
- if (!context.messageStore) {
661
- return { success: false, message: "Message store not available." };
662
- }
663
- const inboxAddresses = resolveAllInboxAddresses(context);
664
- const limit = input.limit ?? 20;
665
- const seen = new Set<string>();
666
- const messages = inboxAddresses
667
- .flatMap((addr) =>
668
- context.messageStore!.searchInbox(addr, input.query, {
669
- from: input.from,
670
- type: input.type,
671
- correlationId: input.correlationId,
672
- after: input.after,
673
- limit,
674
- }),
675
- )
676
- .filter((m) => {
677
- if (seen.has(m.id)) return false;
678
- seen.add(m.id);
679
- return true;
680
- })
681
- .slice(0, limit);
682
- return {
683
- success: true,
684
- message: `${messages.length} message(s) found`,
685
- data: { messages },
686
- };
687
- },
688
- });
689
-
690
- /**
691
- * Reconstruct a conversation thread from any message ID in the chain.
692
- * Follows replyTo links to build the full thread.
693
- */
694
- const get_thread = tool({
695
- name: "get_thread",
696
- description:
697
- "Reconstruct a conversation thread from any message ID in the chain. Follows replyTo links to build the full thread.",
698
- parameters: z.object({
699
- messageId: z.string().describe("Any message ID in the thread"),
700
- }),
701
- category: "messaging",
702
- riskLevel: "safe",
703
- isReadOnly: true,
704
- execute: async (input, context) => {
705
- if (!context.messageStore) {
706
- return { success: false, message: "Message store not available." };
707
- }
708
- const inboxAddresses = resolveAllInboxAddresses(context);
709
- const seen = new Set<string>();
710
- const thread = inboxAddresses
711
- .flatMap((addr) => context.messageStore!.getThreadForInbox(addr, input.messageId))
712
- .filter((m) => {
713
- if (seen.has(m.id)) return false;
714
- seen.add(m.id);
715
- return true;
716
- });
717
- if (thread.length === 0) {
718
- return { success: false, message: `No thread found for message ${input.messageId}` };
719
- }
720
- return {
721
- success: true,
722
- message: `Thread with ${thread.length} message(s)`,
723
- data: { thread },
724
- };
725
- },
726
- });
727
-
728
- /** Messaging tool definitions (4) */
729
- export const MESSAGING_TOOL_DEFINITIONS = [
730
- send_message,
731
- check_messages,
732
- search_messages,
733
- get_thread,
734
- ];