@joe-broadhead/messaging 0.0.0-stage → 0.7.1

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 (273) hide show
  1. package/CHANGELOG.md +220 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +12 -0
  4. package/README.md +36 -2
  5. package/dist/bin/messaging-mcp.js +26524 -0
  6. package/dist/bin/messaging-relay.js +19768 -0
  7. package/dist/bin/messaging.js +15465 -0
  8. package/dist/bin/messagingd.js +29449 -0
  9. package/dist/lib/core-testing.js +1 -0
  10. package/dist/lib/core.js +1 -0
  11. package/dist/lib/ipc-testing.js +1 -0
  12. package/dist/lib/ipc.js +1 -0
  13. package/dist/lib/modules/core/address.js +42 -0
  14. package/dist/lib/modules/core/agent/operations.js +595 -0
  15. package/dist/lib/modules/core/agent/present.js +244 -0
  16. package/dist/lib/modules/core/agent/text.js +79 -0
  17. package/dist/lib/modules/core/agent-tools.js +477 -0
  18. package/dist/lib/modules/core/b64u.js +20 -0
  19. package/dist/lib/modules/core/cards.js +55 -0
  20. package/dist/lib/modules/core/check.js +20 -0
  21. package/dist/lib/modules/core/consent.js +199 -0
  22. package/dist/lib/modules/core/contact-card.js +74 -0
  23. package/dist/lib/modules/core/conversation/context.js +80 -0
  24. package/dist/lib/modules/core/conversation/event.js +100 -0
  25. package/dist/lib/modules/core/conversation/ids.js +38 -0
  26. package/dist/lib/modules/core/conversation/link.js +35 -0
  27. package/dist/lib/modules/core/conversation/log.js +766 -0
  28. package/dist/lib/modules/core/conversation/ratchet.js +243 -0
  29. package/dist/lib/modules/core/conversation/seal.js +198 -0
  30. package/dist/lib/modules/core/delegation.js +174 -0
  31. package/dist/lib/modules/core/directory/device-log.js +258 -0
  32. package/dist/lib/modules/core/directory/epoch.js +103 -0
  33. package/dist/lib/modules/core/directory/handle.js +29 -0
  34. package/dist/lib/modules/core/directory/jelly-device.js +204 -0
  35. package/dist/lib/modules/core/directory/jelly-issuer.js +12 -0
  36. package/dist/lib/modules/core/directory/jelly-token.js +305 -0
  37. package/dist/lib/modules/core/directory/keys-statement.js +43 -0
  38. package/dist/lib/modules/core/directory/link.js +81 -0
  39. package/dist/lib/modules/core/directory/merkle-log.js +250 -0
  40. package/dist/lib/modules/core/directory/person-view.js +197 -0
  41. package/dist/lib/modules/core/directory/person.js +40 -0
  42. package/dist/lib/modules/core/directory/realm.js +59 -0
  43. package/dist/lib/modules/core/directory/smt.js +266 -0
  44. package/dist/lib/modules/core/envelope.js +320 -0
  45. package/dist/lib/modules/core/hash.js +5 -0
  46. package/dist/lib/modules/core/head.js +27 -0
  47. package/dist/lib/modules/core/identity.js +71 -0
  48. package/dist/lib/modules/core/index.js +53 -0
  49. package/dist/lib/modules/core/jcs.js +96 -0
  50. package/dist/lib/modules/core/jelly-host-double.js +35 -0
  51. package/dist/lib/modules/core/jelly-host.js +211 -0
  52. package/dist/lib/modules/core/keys.js +79 -0
  53. package/dist/lib/modules/core/peer-text.js +563 -0
  54. package/dist/lib/modules/core/realm.js +1 -0
  55. package/dist/lib/modules/core/roster.js +454 -0
  56. package/dist/lib/modules/core/schema/a2a.js +85 -0
  57. package/dist/lib/modules/core/schema/common.js +310 -0
  58. package/dist/lib/modules/core/schema/consent.js +120 -0
  59. package/dist/lib/modules/core/schema/conversation.js +292 -0
  60. package/dist/lib/modules/core/schema/delegation.js +58 -0
  61. package/dist/lib/modules/core/schema/directory-frames.js +270 -0
  62. package/dist/lib/modules/core/schema/directory.js +169 -0
  63. package/dist/lib/modules/core/schema/envelope.js +211 -0
  64. package/dist/lib/modules/core/schema/errors.js +104 -0
  65. package/dist/lib/modules/core/schema/index.js +16 -0
  66. package/dist/lib/modules/core/schema/ipc/consent.js +181 -0
  67. package/dist/lib/modules/core/schema/ipc/device.js +276 -0
  68. package/dist/lib/modules/core/schema/ipc/jelly.js +189 -0
  69. package/dist/lib/modules/core/schema/ipc/shapes.js +185 -0
  70. package/dist/lib/modules/core/schema/ipc/surfaces.js +223 -0
  71. package/dist/lib/modules/core/schema/ipc.js +451 -0
  72. package/dist/lib/modules/core/schema/jelly.js +122 -0
  73. package/dist/lib/modules/core/schema/realm.js +27 -0
  74. package/dist/lib/modules/core/schema/relay.js +112 -0
  75. package/dist/lib/modules/core/schema/roster.js +126 -0
  76. package/dist/lib/modules/core/sign.js +43 -0
  77. package/dist/lib/modules/core/task.js +37 -0
  78. package/dist/lib/modules/core/testing.js +21 -0
  79. package/dist/lib/modules/core/ulid.js +36 -0
  80. package/dist/lib/modules/core/version.js +6 -0
  81. package/dist/lib/modules/core/wire.js +255 -0
  82. package/dist/lib/modules/ipc/client.js +289 -0
  83. package/dist/lib/modules/ipc/framing.js +33 -0
  84. package/dist/lib/modules/ipc/index.js +6 -0
  85. package/dist/lib/modules/ipc/paths.js +102 -0
  86. package/dist/lib/modules/ipc/pipes.js +65 -0
  87. package/dist/lib/modules/ipc/rpc.js +8 -0
  88. package/dist/lib/modules/ipc/server.js +317 -0
  89. package/dist/lib/modules/ipc/testing.js +145 -0
  90. package/dist/lib/modules/ipc/trust.js +166 -0
  91. package/dist/lib/modules/ipc/windows-acl.js +275 -0
  92. package/dist/lib/modules/vendor/zod/index.js +7 -0
  93. package/dist/lib/modules/vendor/zod/v4/classic/checks.js +1 -0
  94. package/dist/lib/modules/vendor/zod/v4/classic/coerce.js +17 -0
  95. package/dist/lib/modules/vendor/zod/v4/classic/compat.js +31 -0
  96. package/dist/lib/modules/vendor/zod/v4/classic/deep-partial.js +13 -0
  97. package/dist/lib/modules/vendor/zod/v4/classic/errors.js +57 -0
  98. package/dist/lib/modules/vendor/zod/v4/classic/external.js +17 -0
  99. package/dist/lib/modules/vendor/zod/v4/classic/from-json-schema.js +881 -0
  100. package/dist/lib/modules/vendor/zod/v4/classic/in-out.js +36 -0
  101. package/dist/lib/modules/vendor/zod/v4/classic/iso.js +15 -0
  102. package/dist/lib/modules/vendor/zod/v4/classic/parse.js +16 -0
  103. package/dist/lib/modules/vendor/zod/v4/classic/schemas.js +1535 -0
  104. package/dist/lib/modules/vendor/zod/v4/core/api.js +1107 -0
  105. package/dist/lib/modules/vendor/zod/v4/core/checks.js +516 -0
  106. package/dist/lib/modules/vendor/zod/v4/core/compile.js +1866 -0
  107. package/dist/lib/modules/vendor/zod/v4/core/core.js +141 -0
  108. package/dist/lib/modules/vendor/zod/v4/core/doc.js +38 -0
  109. package/dist/lib/modules/vendor/zod/v4/core/errors.js +267 -0
  110. package/dist/lib/modules/vendor/zod/v4/core/index.js +19 -0
  111. package/dist/lib/modules/vendor/zod/v4/core/json-schema-generator.js +99 -0
  112. package/dist/lib/modules/vendor/zod/v4/core/json-schema-processors.js +843 -0
  113. package/dist/lib/modules/vendor/zod/v4/core/json-schema.js +1 -0
  114. package/dist/lib/modules/vendor/zod/v4/core/memoizer.js +311 -0
  115. package/dist/lib/modules/vendor/zod/v4/core/parse.js +175 -0
  116. package/dist/lib/modules/vendor/zod/v4/core/regexes.js +156 -0
  117. package/dist/lib/modules/vendor/zod/v4/core/registries.js +51 -0
  118. package/dist/lib/modules/vendor/zod/v4/core/schemas.js +2636 -0
  119. package/dist/lib/modules/vendor/zod/v4/core/to-json-schema.js +651 -0
  120. package/dist/lib/modules/vendor/zod/v4/core/util.js +873 -0
  121. package/dist/lib/modules/vendor/zod/v4/core/versions.js +5 -0
  122. package/dist/lib/modules/vendor/zod/v4/core/visit.js +169 -0
  123. package/dist/lib/modules/vendor/zod/v4/locales/ar.js +111 -0
  124. package/dist/lib/modules/vendor/zod/v4/locales/az.js +110 -0
  125. package/dist/lib/modules/vendor/zod/v4/locales/be.js +168 -0
  126. package/dist/lib/modules/vendor/zod/v4/locales/bg.js +125 -0
  127. package/dist/lib/modules/vendor/zod/v4/locales/bn.js +113 -0
  128. package/dist/lib/modules/vendor/zod/v4/locales/ca.js +112 -0
  129. package/dist/lib/modules/vendor/zod/v4/locales/ckb.js +133 -0
  130. package/dist/lib/modules/vendor/zod/v4/locales/cs.js +116 -0
  131. package/dist/lib/modules/vendor/zod/v4/locales/da.js +120 -0
  132. package/dist/lib/modules/vendor/zod/v4/locales/de.js +113 -0
  133. package/dist/lib/modules/vendor/zod/v4/locales/el.js +112 -0
  134. package/dist/lib/modules/vendor/zod/v4/locales/en.js +125 -0
  135. package/dist/lib/modules/vendor/zod/v4/locales/eo.js +114 -0
  136. package/dist/lib/modules/vendor/zod/v4/locales/es.js +136 -0
  137. package/dist/lib/modules/vendor/zod/v4/locales/fa.js +119 -0
  138. package/dist/lib/modules/vendor/zod/v4/locales/fi.js +117 -0
  139. package/dist/lib/modules/vendor/zod/v4/locales/fr-CA.js +112 -0
  140. package/dist/lib/modules/vendor/zod/v4/locales/fr.js +129 -0
  141. package/dist/lib/modules/vendor/zod/v4/locales/gu.js +113 -0
  142. package/dist/lib/modules/vendor/zod/v4/locales/he.js +221 -0
  143. package/dist/lib/modules/vendor/zod/v4/locales/hi.js +111 -0
  144. package/dist/lib/modules/vendor/zod/v4/locales/hr.js +126 -0
  145. package/dist/lib/modules/vendor/zod/v4/locales/hu.js +113 -0
  146. package/dist/lib/modules/vendor/zod/v4/locales/hy.js +158 -0
  147. package/dist/lib/modules/vendor/zod/v4/locales/id.js +111 -0
  148. package/dist/lib/modules/vendor/zod/v4/locales/index.js +63 -0
  149. package/dist/lib/modules/vendor/zod/v4/locales/is.js +114 -0
  150. package/dist/lib/modules/vendor/zod/v4/locales/it.js +113 -0
  151. package/dist/lib/modules/vendor/zod/v4/locales/ja.js +112 -0
  152. package/dist/lib/modules/vendor/zod/v4/locales/ka.js +117 -0
  153. package/dist/lib/modules/vendor/zod/v4/locales/kh.js +5 -0
  154. package/dist/lib/modules/vendor/zod/v4/locales/km.js +115 -0
  155. package/dist/lib/modules/vendor/zod/v4/locales/kn.js +116 -0
  156. package/dist/lib/modules/vendor/zod/v4/locales/ko.js +116 -0
  157. package/dist/lib/modules/vendor/zod/v4/locales/lt.js +207 -0
  158. package/dist/lib/modules/vendor/zod/v4/locales/mk.js +114 -0
  159. package/dist/lib/modules/vendor/zod/v4/locales/ms.js +112 -0
  160. package/dist/lib/modules/vendor/zod/v4/locales/ne.js +111 -0
  161. package/dist/lib/modules/vendor/zod/v4/locales/nl.js +115 -0
  162. package/dist/lib/modules/vendor/zod/v4/locales/nn.js +113 -0
  163. package/dist/lib/modules/vendor/zod/v4/locales/no.js +113 -0
  164. package/dist/lib/modules/vendor/zod/v4/locales/ota.js +114 -0
  165. package/dist/lib/modules/vendor/zod/v4/locales/pl.js +114 -0
  166. package/dist/lib/modules/vendor/zod/v4/locales/ps.js +119 -0
  167. package/dist/lib/modules/vendor/zod/v4/locales/pt-BR.js +143 -0
  168. package/dist/lib/modules/vendor/zod/v4/locales/pt.js +142 -0
  169. package/dist/lib/modules/vendor/zod/v4/locales/ro.js +122 -0
  170. package/dist/lib/modules/vendor/zod/v4/locales/ru.js +168 -0
  171. package/dist/lib/modules/vendor/zod/v4/locales/sk.js +116 -0
  172. package/dist/lib/modules/vendor/zod/v4/locales/sl.js +114 -0
  173. package/dist/lib/modules/vendor/zod/v4/locales/sv.js +115 -0
  174. package/dist/lib/modules/vendor/zod/v4/locales/ta.js +115 -0
  175. package/dist/lib/modules/vendor/zod/v4/locales/tg.js +117 -0
  176. package/dist/lib/modules/vendor/zod/v4/locales/th.js +115 -0
  177. package/dist/lib/modules/vendor/zod/v4/locales/tk.js +107 -0
  178. package/dist/lib/modules/vendor/zod/v4/locales/tr.js +110 -0
  179. package/dist/lib/modules/vendor/zod/v4/locales/ua.js +5 -0
  180. package/dist/lib/modules/vendor/zod/v4/locales/uk.js +113 -0
  181. package/dist/lib/modules/vendor/zod/v4/locales/ur.js +115 -0
  182. package/dist/lib/modules/vendor/zod/v4/locales/uz.js +113 -0
  183. package/dist/lib/modules/vendor/zod/v4/locales/vi.js +113 -0
  184. package/dist/lib/modules/vendor/zod/v4/locales/yo.js +112 -0
  185. package/dist/lib/modules/vendor/zod/v4/locales/zh-CN.js +114 -0
  186. package/dist/lib/modules/vendor/zod/v4/locales/zh-TW.js +112 -0
  187. package/dist/lib/testing-jelly-id.js +1219 -0
  188. package/dist/lib/zod.js +1 -0
  189. package/dist/spec/messaging-v1.md +3744 -0
  190. package/dist/third-party-notices.md +239 -0
  191. package/dist/third-party.json +88 -0
  192. package/dist/types/core/address.d.ts +16 -0
  193. package/dist/types/core/agent/operations.d.ts +129 -0
  194. package/dist/types/core/agent/present.d.ts +57 -0
  195. package/dist/types/core/agent/text.d.ts +62 -0
  196. package/dist/types/core/agent-tools.d.ts +966 -0
  197. package/dist/types/core/b64u.d.ts +6 -0
  198. package/dist/types/core/cards.d.ts +21 -0
  199. package/dist/types/core/check.d.ts +10 -0
  200. package/dist/types/core/consent.d.ts +132 -0
  201. package/dist/types/core/contact-card.d.ts +34 -0
  202. package/dist/types/core/conversation/context.d.ts +56 -0
  203. package/dist/types/core/conversation/event.d.ts +36 -0
  204. package/dist/types/core/conversation/ids.d.ts +18 -0
  205. package/dist/types/core/conversation/link.d.ts +16 -0
  206. package/dist/types/core/conversation/log.d.ts +142 -0
  207. package/dist/types/core/conversation/ratchet.d.ts +95 -0
  208. package/dist/types/core/conversation/seal.d.ts +47 -0
  209. package/dist/types/core/delegation.d.ts +71 -0
  210. package/dist/types/core/directory/device-log.d.ts +119 -0
  211. package/dist/types/core/directory/epoch.d.ts +53 -0
  212. package/dist/types/core/directory/handle.d.ts +11 -0
  213. package/dist/types/core/directory/jelly-device.d.ts +88 -0
  214. package/dist/types/core/directory/jelly-issuer.d.ts +8 -0
  215. package/dist/types/core/directory/jelly-token.d.ts +116 -0
  216. package/dist/types/core/directory/keys-statement.d.ts +22 -0
  217. package/dist/types/core/directory/link.d.ts +36 -0
  218. package/dist/types/core/directory/merkle-log.d.ts +40 -0
  219. package/dist/types/core/directory/person-view.d.ts +90 -0
  220. package/dist/types/core/directory/person.d.ts +8 -0
  221. package/dist/types/core/directory/realm.d.ts +23 -0
  222. package/dist/types/core/directory/smt.d.ts +52 -0
  223. package/dist/types/core/envelope.d.ts +68 -0
  224. package/dist/types/core/hash.d.ts +2 -0
  225. package/dist/types/core/head.d.ts +34 -0
  226. package/dist/types/core/identity.d.ts +35 -0
  227. package/dist/types/core/index.d.ts +45 -0
  228. package/dist/types/core/jcs.d.ts +12 -0
  229. package/dist/types/core/jelly-host-double.d.ts +21 -0
  230. package/dist/types/core/jelly-host.d.ts +86 -0
  231. package/dist/types/core/keys.d.ts +16 -0
  232. package/dist/types/core/peer-text.d.ts +87 -0
  233. package/dist/types/core/realm.d.ts +36 -0
  234. package/dist/types/core/roster.d.ts +146 -0
  235. package/dist/types/core/schema/a2a.d.ts +251 -0
  236. package/dist/types/core/schema/common.d.ts +328 -0
  237. package/dist/types/core/schema/consent.d.ts +140 -0
  238. package/dist/types/core/schema/conversation.d.ts +1657 -0
  239. package/dist/types/core/schema/delegation.d.ts +58 -0
  240. package/dist/types/core/schema/directory-frames.d.ts +1301 -0
  241. package/dist/types/core/schema/directory.d.ts +375 -0
  242. package/dist/types/core/schema/envelope.d.ts +756 -0
  243. package/dist/types/core/schema/errors.d.ts +142 -0
  244. package/dist/types/core/schema/index.d.ts +16 -0
  245. package/dist/types/core/schema/ipc/consent.d.ts +555 -0
  246. package/dist/types/core/schema/ipc/device.d.ts +735 -0
  247. package/dist/types/core/schema/ipc/jelly.d.ts +501 -0
  248. package/dist/types/core/schema/ipc/shapes.d.ts +414 -0
  249. package/dist/types/core/schema/ipc/surfaces.d.ts +680 -0
  250. package/dist/types/core/schema/ipc.d.ts +3146 -0
  251. package/dist/types/core/schema/jelly.d.ts +160 -0
  252. package/dist/types/core/schema/realm.d.ts +38 -0
  253. package/dist/types/core/schema/relay.d.ts +1134 -0
  254. package/dist/types/core/schema/roster.d.ts +347 -0
  255. package/dist/types/core/sign.d.ts +13 -0
  256. package/dist/types/core/task.d.ts +6 -0
  257. package/dist/types/core/testing.d.ts +19 -0
  258. package/dist/types/core/ulid.d.ts +5 -0
  259. package/dist/types/core/version.d.ts +6 -0
  260. package/dist/types/core/wire.d.ts +14 -0
  261. package/dist/types/ipc/client.d.ts +85 -0
  262. package/dist/types/ipc/framing.d.ts +12 -0
  263. package/dist/types/ipc/index.d.ts +6 -0
  264. package/dist/types/ipc/paths.d.ts +39 -0
  265. package/dist/types/ipc/pipes.d.ts +16 -0
  266. package/dist/types/ipc/rpc.d.ts +13 -0
  267. package/dist/types/ipc/server.d.ts +94 -0
  268. package/dist/types/ipc/testing.d.ts +89 -0
  269. package/dist/types/ipc/trust.d.ts +49 -0
  270. package/dist/types/ipc/windows-acl.d.ts +56 -0
  271. package/dist/types/testing/jelly-id.d.ts +518 -0
  272. package/dist/types/zod.d.ts +1 -0
  273. package/package.json +77 -4
@@ -0,0 +1,244 @@
1
+ import { taskStateName } from "../agent-tools.js";
2
+ import { createFence, PEER_FENCE_TAG, provenanceOf } from "../peer-text.js";
3
+ import { chainPath, EXPECT_TEXT, isSafeId, messageAttributes } from "./text.js";
4
+ /** Per-part cap in tool outputs; the protocol caps a text part at 65,536 chars. */
5
+ const PART_CHARS = 32_768;
6
+ /** Task history shown by `read`: the most recent messages. */
7
+ const HISTORY_MESSAGES = 20;
8
+ const ARTIFACTS = 20;
9
+ const TITLE_CHARS = 120;
10
+ export const isoOf = (millis) => new Date(millis).toISOString();
11
+ /** A peer-chosen identifier: raw when it is a plain id, fenced otherwise. */
12
+ function idField(framer, value) {
13
+ return isSafeId(value) ? value : framer.wrap(value);
14
+ }
15
+ /** How a request from `person` runs here (spec §11): your own person, a trusted teammate, or a guarded peer turn. */
16
+ export function consentOf(person, self, trusted) {
17
+ if (person === self)
18
+ return "self";
19
+ return trusted.has(person) ? "trusted" : "guarded";
20
+ }
21
+ function presentChain(framer, chain) {
22
+ return {
23
+ path: framer.wrap(chainPath(chain), { source: "delegation" }, 2_048),
24
+ depth: chain.depth,
25
+ maxDepth: chain.maxDepth,
26
+ ...(chain.scope !== undefined ? { scope: framer.wrap(chain.scope.join(" "), { source: "delegation" }, 1_024) } : {}),
27
+ };
28
+ }
29
+ /**
30
+ * Text this side wrote, shown without a fence: cleaned like peer text (hidden characters made
31
+ * visible, fence-like and host-tag openers neutralised), because a model may have copied peer text
32
+ * into its own status or artifact. Only `neutralize` is used, which draws no nonce.
33
+ */
34
+ const OWN = createFence({ tag: PEER_FENCE_TAG });
35
+ function shown(framer, author, attributes) {
36
+ return author === "peer" ? (text, maxChars) => framer.wrap(text, attributes, maxChars) : (text, maxChars) => OWN.neutralize(text, maxChars === undefined ? {} : { maxChars });
37
+ }
38
+ function presentPart(framer, part, author, attributes = {}) {
39
+ const show = shown(framer, author, attributes);
40
+ const extras = {
41
+ ...(part.filename !== undefined ? { filename: show(part.filename) } : {}),
42
+ ...(part.mediaType !== undefined ? { mediaType: show(part.mediaType) } : {}),
43
+ };
44
+ if (part.text !== undefined)
45
+ return { type: "text", author, content: show(part.text, PART_CHARS), ...extras };
46
+ if (part.url !== undefined)
47
+ return { type: "url", author, content: show(part.url, 2_048), ...extras };
48
+ if (part.raw !== undefined) {
49
+ const bytes = Math.floor((part.raw.replace(/=+$/, "").length * 3) / 4);
50
+ return { type: "file", author, content: show(`[file: ${bytes} bytes; contents not shown]`), bytes, ...extras };
51
+ }
52
+ return { type: "data", author, content: show(JSON.stringify(part.data) ?? "null", PART_CHARS), ...extras };
53
+ }
54
+ /** The side of a task that `author` wrote for, given this side's role. */
55
+ function sideOf(record, author) {
56
+ if (author === "ours")
57
+ return record.role;
58
+ return record.role === "requester" ? "executor" : "requester";
59
+ }
60
+ /**
61
+ * Who wrote a message of a task's history. The requester's daemon appends only its own sends; the
62
+ * executor's only what the requester sent (spec §6.2). So ours only on the requester's side, and only
63
+ * for a requester's message: an executor never shows history as its own, whatever role the peer set.
64
+ */
65
+ function historyAuthor(record, message) {
66
+ return record.role === "requester" && message.role === "ROLE_USER" ? "ours" : "peer";
67
+ }
68
+ /** Who wrote the status and the artifacts: the executor. */
69
+ function executorAuthor(record) {
70
+ return record.role === "executor" ? "ours" : "peer";
71
+ }
72
+ // ---- contacts ----
73
+ /** A session's state for a contact: live ones idle or busy, kept ones kept, those on an offline machine unknown. */
74
+ function agentState(entry) {
75
+ if (entry.online)
76
+ return entry.state;
77
+ return entry.expect === "offline" ? "unknown" : "kept";
78
+ }
79
+ export function presentAgent(framer, entry) {
80
+ const from = { from: entry.address };
81
+ return {
82
+ address: entry.address,
83
+ state: agentState(entry),
84
+ ...(entry.expect !== undefined ? { expect: entry.expect, seen: EXPECT_TEXT[entry.expect] } : {}),
85
+ ...(entry.repo !== undefined ? { repo: framer.wrap(entry.repo, from, 200) } : {}),
86
+ ...(entry.branch !== undefined ? { branch: framer.wrap(entry.branch, from, 200) } : {}),
87
+ ...(entry.doing !== undefined ? { doing: framer.wrap(entry.doing, from, 200) } : {}),
88
+ };
89
+ }
90
+ /**
91
+ * What an agent session accepts under today's team policy (spec §8.3): a `cli` session takes mail
92
+ * but not tasks. Nothing is granted or narrowed per person yet.
93
+ */
94
+ export function agentCan(entry) {
95
+ return { chat: false, agentMail: true, ask: entry.client === "cli" ? "no" : "open", source: "default" };
96
+ }
97
+ // ---- inbox ----
98
+ export function presentInboxItem(framer, item, consent) {
99
+ return {
100
+ id: item.id,
101
+ conversation: conversationOfPeer(item.from),
102
+ from: item.from.address,
103
+ kind: item.kind === "task" ? "task" : "mail",
104
+ preview: framer.wrap(item.preview, messageAttributes(item)),
105
+ at: isoOf(item.receivedAt),
106
+ stage: item.stage,
107
+ consent,
108
+ ...(item.taskId !== undefined ? { taskId: item.taskId } : {}),
109
+ ...(item.chain !== undefined ? { chain: presentChain(framer, item.chain) } : {}),
110
+ };
111
+ }
112
+ /** The agent conversation with a peer: `agent:<person>/<agent>`, or `person:<person>` for the person's own. */
113
+ export function conversationOfPeer(peer) {
114
+ return peer.agent === undefined ? `person:${peer.person}` : `agent:${peer.person}/${peer.agent}`;
115
+ }
116
+ // ---- read ----
117
+ export function presentMail(framer, result, task) {
118
+ const { item, message } = result;
119
+ const attributes = messageAttributes(item, message);
120
+ const provenance = provenanceOf(message.metadata, item.id);
121
+ return {
122
+ id: item.id,
123
+ conversation: conversationOfPeer(item.from),
124
+ kind: item.kind === "task" ? "task" : "mail",
125
+ from: provenance?.from ?? item.from.address,
126
+ at: isoOf(item.receivedAt),
127
+ parts: message.parts.map((part) => presentPart(framer, part, "peer", attributes)),
128
+ verified: provenance ? "signature" : "none",
129
+ ...(item.replyTo !== undefined ? { replyTo: item.replyTo } : {}),
130
+ ...(item.chain !== undefined ? { chain: presentChain(framer, item.chain) } : {}),
131
+ ...(task !== undefined ? { task } : {}),
132
+ };
133
+ }
134
+ /** A task as a read item: its request, with the whole task. */
135
+ export function presentTaskItem(framer, record, self, consent) {
136
+ const request = record.task.history?.[0];
137
+ const author = request ? historyAuthor(record, request) : "peer";
138
+ return {
139
+ id: record.task.id,
140
+ conversation: conversationOfPeer(record.peer),
141
+ kind: "task",
142
+ from: author === "ours" ? self : record.peer.address,
143
+ at: isoOf(record.createdAt),
144
+ parts: request ? request.parts.map((part) => presentPart(framer, part, author, peerAttributes(record))) : [],
145
+ ...(record.chain !== undefined ? { chain: presentChain(framer, record.chain) } : {}),
146
+ task: presentTaskView(framer, record, consent),
147
+ };
148
+ }
149
+ /** Something this session sent: no copy is kept, so only how far it got. */
150
+ export function presentSent(result, self) {
151
+ const [person = "", rest = ""] = result.to.split("/");
152
+ const session = rest.split("@")[0];
153
+ return {
154
+ id: result.envelopeId,
155
+ conversation: `agent:${person}/${session}`,
156
+ kind: "sent",
157
+ from: self,
158
+ at: isoOf(result.history[0]?.at ?? 0),
159
+ parts: [],
160
+ delivery: result.history.map((entry) => ({ to: result.to, stage: entry.stage, at: isoOf(entry.at), ...(entry.code !== undefined ? { code: entry.code } : {}) })),
161
+ };
162
+ }
163
+ // ---- conversation pages ----
164
+ export function presentLine(framer, line, kind, conversation) {
165
+ const from = line.agent === undefined ? line.person : `${line.person}/${line.agent}`;
166
+ return {
167
+ id: line.id,
168
+ from,
169
+ kind: line.notice ? "notice" : kind,
170
+ at: isoOf(line.at),
171
+ text: framer.wrap(line.body, { from, conversation, id: line.id }, 8_000),
172
+ ...(line.thread !== undefined ? { thread: line.thread } : {}),
173
+ ...(line.state !== undefined ? { stage: line.state } : {}),
174
+ };
175
+ }
176
+ export function presentConvEntry(framer, entry, self) {
177
+ const author = entry.direction === "out" ? "ours" : "peer";
178
+ const from = author === "ours" ? self : entry.peer;
179
+ return {
180
+ id: entry.id,
181
+ from,
182
+ kind: entry.kind,
183
+ at: isoOf(entry.at),
184
+ ...(entry.text !== undefined ? { author, text: shown(framer, author, { from, id: entry.id })(entry.text) } : {}),
185
+ ...(entry.taskId !== undefined ? { taskId: entry.taskId } : {}),
186
+ ...(entry.stage !== undefined ? { stage: entry.stage } : {}),
187
+ ...(entry.state !== undefined ? { state: taskStateName(entry.state) } : {}),
188
+ };
189
+ }
190
+ // ---- tasks ----
191
+ function textOf(message) {
192
+ if (!message)
193
+ return undefined;
194
+ const text = message.parts.flatMap((part) => (part.text !== undefined ? [part.text] : [])).join("\n");
195
+ return text.length > 0 ? text : undefined;
196
+ }
197
+ /** The fence attributes of the peer's text in a task. */
198
+ function peerAttributes(record) {
199
+ return { from: record.peer.address, task: record.task.id };
200
+ }
201
+ export function presentTask(framer, record, consent) {
202
+ const request = record.task.history?.[0];
203
+ const title = textOf(request);
204
+ const status = textOf(record.task.status.message);
205
+ const titleAuthor = request ? historyAuthor(record, request) : "peer";
206
+ const statusAuthor = executorAuthor(record);
207
+ return {
208
+ id: idField(framer, record.task.id),
209
+ role: record.role === "requester" ? "mine" : "asked",
210
+ counterpart: record.peer.address,
211
+ state: taskStateName(record.task.status.state),
212
+ ...(title !== undefined ? { title: shown(framer, titleAuthor, peerAttributes(record))(title, TITLE_CHARS), titleAuthor } : {}),
213
+ ...(status !== undefined ? { status: shown(framer, statusAuthor, peerAttributes(record))(status, PART_CHARS), statusAuthor } : {}),
214
+ updatedAt: isoOf(record.updatedAt),
215
+ ...(consent !== undefined && record.role === "executor" ? { consent } : {}),
216
+ ...(record.chain !== undefined ? { chain: presentChain(framer, record.chain) } : {}),
217
+ };
218
+ }
219
+ /** A follow-up's stage says the executor read it. */
220
+ export function followUpRead(stage) {
221
+ return stage === "read" || stage === "replied";
222
+ }
223
+ export function presentTaskView(framer, record, consent) {
224
+ const attributes = peerAttributes(record);
225
+ const history = (record.task.history ?? []).slice(-HISTORY_MESSAGES);
226
+ const artifacts = (record.task.artifacts ?? []).slice(-ARTIFACTS);
227
+ const executor = executorAuthor(record);
228
+ return {
229
+ ...presentTask(framer, record, consent),
230
+ createdAt: isoOf(record.createdAt),
231
+ history: history.map((message) => {
232
+ const author = historyAuthor(record, message);
233
+ return { from: sideOf(record, author), author, parts: message.parts.map((part) => presentPart(framer, part, author, attributes)) };
234
+ }),
235
+ artifacts: artifacts.map((artifact) => ({
236
+ author: executor,
237
+ ...(artifact.name !== undefined ? { name: shown(framer, executor, attributes)(artifact.name, 200) } : {}),
238
+ parts: artifact.parts.map((part) => presentPart(framer, part, executor, attributes)),
239
+ })),
240
+ ...(record.followUps !== undefined
241
+ ? { followUps: record.followUps.map((followUp) => ({ id: followUp.envelopeId, sentAt: isoOf(followUp.sentAt), stage: followUp.stage, read: followUpRead(followUp.stage) })) }
242
+ : {}),
243
+ };
244
+ }
@@ -0,0 +1,79 @@
1
+ import { cleanPeerText, provenanceOf } from "../peer-text.js";
2
+ import { MachineLabel, PersonId, SessionName } from "../schema/common.js";
3
+ /**
4
+ * Fixed text and small helpers the agent tools of every client share (spec §11, "Agent tools").
5
+ * Pure. Peer-derived values here are raw: callers fence or clean them.
6
+ */
7
+ /**
8
+ * What a send's `expect` (spec §8.2) means for the agent: fixed text, never peer text. A hint from
9
+ * the recipient's last presence, not a promise.
10
+ */
11
+ export const EXPECT_TEXT = {
12
+ next_turn: "The recipient is busy: it will likely see this after its next tool call or at its next turn.",
13
+ wake: "The recipient takes wakes: its client may start a turn for this soon.",
14
+ next_prompt: "The recipient is idle and will not be woken: it sees this when someone next prompts it.",
15
+ on_read: "The recipient is not pushed messages: it sees this when it next checks its inbox.",
16
+ kept: "No client holds the recipient session now: this waits until it comes back.",
17
+ offline: "The recipient's machine is offline: the relay holds this until it connects or the message expires.",
18
+ };
19
+ const SAFE_ID = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
20
+ /** True for opaque identifiers that are safe to show outside a fence (ULIDs, UUIDs, slugs). */
21
+ export function isSafeId(value) {
22
+ return SAFE_ID.test(value);
23
+ }
24
+ /** A peer reference reduced to a validated address, or undefined when any part fails its schema. */
25
+ export function validatedAddress(peer) {
26
+ if (!peer.agent)
27
+ return undefined;
28
+ if (!PersonId.safeParse(peer.person).success)
29
+ return undefined;
30
+ if (!SessionName.safeParse(peer.agent).success)
31
+ return undefined;
32
+ if (!MachineLabel.safeParse(peer.label).success)
33
+ return undefined;
34
+ return { person: peer.person, session: peer.agent, label: peer.label };
35
+ }
36
+ /**
37
+ * Whether a chain says more than the item's `from` (spec §6.7): a hop before the sender, or a scope. A
38
+ * direct request from the sender's own session says nothing more.
39
+ */
40
+ export function delegated(chain) {
41
+ return chain.depth > 1 || chain.scope !== undefined;
42
+ }
43
+ /** Who asked whom, root first, as raw peer-derived text: callers fence or clean it. */
44
+ export function chainPath(chain) {
45
+ return chain.addresses.join(" → ");
46
+ }
47
+ /**
48
+ * The `delegation` field of a task send's permission prompt. It is shown to the user in the
49
+ * approval UI, not to the model, so the chain is cleaned (hidden characters made visible, one line)
50
+ * rather than fenced.
51
+ */
52
+ export function delegationPrompt(chain) {
53
+ if (chain === undefined)
54
+ return "new chain: the recipient sees this session as the one who asked";
55
+ return `on behalf of ${cleanPeerText(chainPath(chain), { singleLine: true, maxChars: 400 })}`;
56
+ }
57
+ /**
58
+ * Fence attributes for a message: the sender's address, the envelope id, and the verification the
59
+ * daemon performed. Inbox items exist only after the daemon verified the envelope signature (spec
60
+ * §5), so an item without a full message is still `verified="signature"`. A message whose
61
+ * provenance is missing, malformed or for another envelope is shown as `verified="none"`.
62
+ */
63
+ export function messageAttributes(item, message) {
64
+ const provenance = message ? provenanceOf(message.metadata, item.id) : undefined;
65
+ const verified = message === undefined || provenance !== undefined ? "signature" : "none";
66
+ const chain = item.chain && delegated(item.chain) ? item.chain : undefined;
67
+ return {
68
+ from: provenance?.from ?? item.from.address,
69
+ id: item.id,
70
+ kind: item.kind,
71
+ verified,
72
+ chain: chain && chainPath(chain),
73
+ scope: chain?.scope?.join(" "),
74
+ };
75
+ }
76
+ /** Drop undefined members so optional IPC params and output keys stay absent. */
77
+ export function compact(record) {
78
+ return Object.fromEntries(Object.entries(record).filter(([, value]) => value !== undefined));
79
+ }