@chipschat/protocol 0.0.0-stage → 1.0.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 (371) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +56 -2
  3. package/dist/generated/types.d.ts +4052 -0
  4. package/dist/generated/types.d.ts.map +1 -0
  5. package/dist/generated/types.js +7 -0
  6. package/dist/generated/types.js.map +1 -0
  7. package/dist/src/address.d.ts +106 -0
  8. package/dist/src/address.d.ts.map +1 -0
  9. package/dist/src/address.js +141 -0
  10. package/dist/src/address.js.map +1 -0
  11. package/dist/src/agent-context-windows.d.ts +235 -0
  12. package/dist/src/agent-context-windows.d.ts.map +1 -0
  13. package/dist/src/agent-context-windows.js +213 -0
  14. package/dist/src/agent-context-windows.js.map +1 -0
  15. package/dist/src/agent-heartbeats.d.ts +106 -0
  16. package/dist/src/agent-heartbeats.d.ts.map +1 -0
  17. package/dist/src/agent-heartbeats.js +111 -0
  18. package/dist/src/agent-heartbeats.js.map +1 -0
  19. package/dist/src/agent-models.d.ts +158 -0
  20. package/dist/src/agent-models.d.ts.map +1 -0
  21. package/dist/src/agent-models.js +171 -0
  22. package/dist/src/agent-models.js.map +1 -0
  23. package/dist/src/agent-place.d.ts +76 -0
  24. package/dist/src/agent-place.d.ts.map +1 -0
  25. package/dist/src/agent-place.js +80 -0
  26. package/dist/src/agent-place.js.map +1 -0
  27. package/dist/src/agents.d.ts +24 -0
  28. package/dist/src/agents.d.ts.map +1 -0
  29. package/dist/src/agents.js +58 -0
  30. package/dist/src/agents.js.map +1 -0
  31. package/dist/src/capabilities.d.ts +121 -0
  32. package/dist/src/capabilities.d.ts.map +1 -0
  33. package/dist/src/capabilities.js +120 -0
  34. package/dist/src/capabilities.js.map +1 -0
  35. package/dist/src/checklist.d.ts +19 -0
  36. package/dist/src/checklist.d.ts.map +1 -0
  37. package/dist/src/checklist.js +22 -0
  38. package/dist/src/checklist.js.map +1 -0
  39. package/dist/src/commands.d.ts +50 -0
  40. package/dist/src/commands.d.ts.map +1 -0
  41. package/dist/src/commands.js +274 -0
  42. package/dist/src/commands.js.map +1 -0
  43. package/dist/src/connectors.d.ts +90 -0
  44. package/dist/src/connectors.d.ts.map +1 -0
  45. package/dist/src/connectors.js +119 -0
  46. package/dist/src/connectors.js.map +1 -0
  47. package/dist/src/context-files.d.ts +304 -0
  48. package/dist/src/context-files.d.ts.map +1 -0
  49. package/dist/src/context-files.js +247 -0
  50. package/dist/src/context-files.js.map +1 -0
  51. package/dist/src/context.d.ts +369 -0
  52. package/dist/src/context.d.ts.map +1 -0
  53. package/dist/src/context.js +526 -0
  54. package/dist/src/context.js.map +1 -0
  55. package/dist/src/direct-asks.d.ts +36 -0
  56. package/dist/src/direct-asks.d.ts.map +1 -0
  57. package/dist/src/direct-asks.js +43 -0
  58. package/dist/src/direct-asks.js.map +1 -0
  59. package/dist/src/envelope.d.ts +70 -0
  60. package/dist/src/envelope.d.ts.map +1 -0
  61. package/dist/src/envelope.js +46 -0
  62. package/dist/src/envelope.js.map +1 -0
  63. package/dist/src/events/catalogue.d.ts +50 -0
  64. package/dist/src/events/catalogue.d.ts.map +1 -0
  65. package/dist/src/events/catalogue.js +70 -0
  66. package/dist/src/events/catalogue.js.map +1 -0
  67. package/dist/src/events/chips-to-harness.d.ts +37 -0
  68. package/dist/src/events/chips-to-harness.d.ts.map +1 -0
  69. package/dist/src/events/chips-to-harness.js +511 -0
  70. package/dist/src/events/chips-to-harness.js.map +1 -0
  71. package/dist/src/events/harness-to-chips.d.ts +38 -0
  72. package/dist/src/events/harness-to-chips.d.ts.map +1 -0
  73. package/dist/src/events/harness-to-chips.js +593 -0
  74. package/dist/src/events/harness-to-chips.js.map +1 -0
  75. package/dist/src/events/index.d.ts +6 -0
  76. package/dist/src/events/index.d.ts.map +1 -0
  77. package/dist/src/events/index.js +5 -0
  78. package/dist/src/events/index.js.map +1 -0
  79. package/dist/src/events/workspace.d.ts +222 -0
  80. package/dist/src/events/workspace.d.ts.map +1 -0
  81. package/dist/src/events/workspace.js +1039 -0
  82. package/dist/src/events/workspace.js.map +1 -0
  83. package/dist/src/exchange.d.ts +262 -0
  84. package/dist/src/exchange.d.ts.map +1 -0
  85. package/dist/src/exchange.js +413 -0
  86. package/dist/src/exchange.js.map +1 -0
  87. package/dist/src/first-names.d.ts +29 -0
  88. package/dist/src/first-names.d.ts.map +1 -0
  89. package/dist/src/first-names.js +80 -0
  90. package/dist/src/first-names.js.map +1 -0
  91. package/dist/src/frames.d.ts +526 -0
  92. package/dist/src/frames.d.ts.map +1 -0
  93. package/dist/src/frames.js +346 -0
  94. package/dist/src/frames.js.map +1 -0
  95. package/dist/src/harness-jobs.d.ts +313 -0
  96. package/dist/src/harness-jobs.d.ts.map +1 -0
  97. package/dist/src/harness-jobs.js +210 -0
  98. package/dist/src/harness-jobs.js.map +1 -0
  99. package/dist/src/ids/index.d.ts +3 -0
  100. package/dist/src/ids/index.d.ts.map +1 -0
  101. package/dist/src/ids/index.js +2 -0
  102. package/dist/src/ids/index.js.map +1 -0
  103. package/dist/src/ids/ulid.d.ts +96 -0
  104. package/dist/src/ids/ulid.d.ts.map +1 -0
  105. package/dist/src/ids/ulid.js +212 -0
  106. package/dist/src/ids/ulid.js.map +1 -0
  107. package/dist/src/index.d.ts +81 -0
  108. package/dist/src/index.d.ts.map +1 -0
  109. package/dist/src/index.js +47 -0
  110. package/dist/src/index.js.map +1 -0
  111. package/dist/src/invite-link.d.ts +64 -0
  112. package/dist/src/invite-link.d.ts.map +1 -0
  113. package/dist/src/invite-link.js +99 -0
  114. package/dist/src/invite-link.js.map +1 -0
  115. package/dist/src/join.d.ts +188 -0
  116. package/dist/src/join.d.ts.map +1 -0
  117. package/dist/src/join.js +190 -0
  118. package/dist/src/join.js.map +1 -0
  119. package/dist/src/limits.d.ts +50 -0
  120. package/dist/src/limits.d.ts.map +1 -0
  121. package/dist/src/limits.js +106 -0
  122. package/dist/src/limits.js.map +1 -0
  123. package/dist/src/matters.d.ts +22 -0
  124. package/dist/src/matters.d.ts.map +1 -0
  125. package/dist/src/matters.js +26 -0
  126. package/dist/src/matters.js.map +1 -0
  127. package/dist/src/mentions.d.ts +63 -0
  128. package/dist/src/mentions.d.ts.map +1 -0
  129. package/dist/src/mentions.js +129 -0
  130. package/dist/src/mentions.js.map +1 -0
  131. package/dist/src/names.d.ts +58 -0
  132. package/dist/src/names.d.ts.map +1 -0
  133. package/dist/src/names.js +192 -0
  134. package/dist/src/names.js.map +1 -0
  135. package/dist/src/no-reply.d.ts +17 -0
  136. package/dist/src/no-reply.d.ts.map +1 -0
  137. package/dist/src/no-reply.js +48 -0
  138. package/dist/src/no-reply.js.map +1 -0
  139. package/dist/src/notifications.d.ts +81 -0
  140. package/dist/src/notifications.d.ts.map +1 -0
  141. package/dist/src/notifications.js +65 -0
  142. package/dist/src/notifications.js.map +1 -0
  143. package/dist/src/pages.d.ts +32 -0
  144. package/dist/src/pages.d.ts.map +1 -0
  145. package/dist/src/pages.js +32 -0
  146. package/dist/src/pages.js.map +1 -0
  147. package/dist/src/pairing.d.ts +99 -0
  148. package/dist/src/pairing.d.ts.map +1 -0
  149. package/dist/src/pairing.js +163 -0
  150. package/dist/src/pairing.js.map +1 -0
  151. package/dist/src/primitives.d.ts +267 -0
  152. package/dist/src/primitives.d.ts.map +1 -0
  153. package/dist/src/primitives.js +279 -0
  154. package/dist/src/primitives.js.map +1 -0
  155. package/dist/src/project.d.ts +335 -0
  156. package/dist/src/project.d.ts.map +1 -0
  157. package/dist/src/project.js +499 -0
  158. package/dist/src/project.js.map +1 -0
  159. package/dist/src/ranges.d.ts +31 -0
  160. package/dist/src/ranges.d.ts.map +1 -0
  161. package/dist/src/ranges.js +51 -0
  162. package/dist/src/ranges.js.map +1 -0
  163. package/dist/src/relay.d.ts +177 -0
  164. package/dist/src/relay.d.ts.map +1 -0
  165. package/dist/src/relay.js +147 -0
  166. package/dist/src/relay.js.map +1 -0
  167. package/dist/src/room-files.d.ts +146 -0
  168. package/dist/src/room-files.d.ts.map +1 -0
  169. package/dist/src/room-files.js +135 -0
  170. package/dist/src/room-files.js.map +1 -0
  171. package/dist/src/room-note.d.ts +213 -0
  172. package/dist/src/room-note.d.ts.map +1 -0
  173. package/dist/src/room-note.js +457 -0
  174. package/dist/src/room-note.js.map +1 -0
  175. package/dist/src/space-icons.d.ts +68 -0
  176. package/dist/src/space-icons.d.ts.map +1 -0
  177. package/dist/src/space-icons.js +142 -0
  178. package/dist/src/space-icons.js.map +1 -0
  179. package/dist/src/spend.d.ts +57 -0
  180. package/dist/src/spend.d.ts.map +1 -0
  181. package/dist/src/spend.js +94 -0
  182. package/dist/src/spend.js.map +1 -0
  183. package/dist/src/visibility.d.ts +170 -0
  184. package/dist/src/visibility.d.ts.map +1 -0
  185. package/dist/src/visibility.js +218 -0
  186. package/dist/src/visibility.js.map +1 -0
  187. package/fixtures/agent.asked.json +15 -0
  188. package/fixtures/agent.attached.json +15 -0
  189. package/fixtures/agent.deleted.json +16 -0
  190. package/fixtures/agent.fork.json +14 -0
  191. package/fixtures/agent.lock_set.json +13 -0
  192. package/fixtures/agent.registered.json +17 -0
  193. package/fixtures/agent.released.json +16 -0
  194. package/fixtures/agent.updated.json +14 -0
  195. package/fixtures/approval.executed.json +16 -0
  196. package/fixtures/approval.request.json +45 -0
  197. package/fixtures/approval.resolved.json +20 -0
  198. package/fixtures/approval.routed.json +20 -0
  199. package/fixtures/artifact.create.json +18 -0
  200. package/fixtures/attachment.fetched.json +15 -0
  201. package/fixtures/attachment.limits_set.json +15 -0
  202. package/fixtures/budget.warning.json +20 -0
  203. package/fixtures/checkpoint.json +16 -0
  204. package/fixtures/context.compacted.json +16 -0
  205. package/fixtures/context.usage.json +17 -0
  206. package/fixtures/exchange.limits_set.json +15 -0
  207. package/fixtures/exchange.set.json +13 -0
  208. package/fixtures/feedback.json +21 -0
  209. package/fixtures/frames/ack.json +6 -0
  210. package/fixtures/frames/context.edit.json +11 -0
  211. package/fixtures/frames/context_window.change.json +9 -0
  212. package/fixtures/frames/context_window.compact.json +8 -0
  213. package/fixtures/frames/error.json +6 -0
  214. package/fixtures/frames/event.json +20 -0
  215. package/fixtures/frames/events.json +39 -0
  216. package/fixtures/frames/folder.open.json +6 -0
  217. package/fixtures/frames/folder.open.page.json +7 -0
  218. package/fixtures/frames/folder.run.json +8 -0
  219. package/fixtures/frames/folder.run_state.json +12 -0
  220. package/fixtures/frames/heartbeat.change.json +10 -0
  221. package/fixtures/frames/hello.json +18 -0
  222. package/fixtures/frames/job.change.json +9 -0
  223. package/fixtures/frames/join.json +19 -0
  224. package/fixtures/frames/model.change.json +10 -0
  225. package/fixtures/frames/ping.json +4 -0
  226. package/fixtures/frames/pong.json +4 -0
  227. package/fixtures/frames/presence.json +12 -0
  228. package/fixtures/frames/receipt.json +5 -0
  229. package/fixtures/frames/restart.json +5 -0
  230. package/fixtures/frames/subscribe.json +6 -0
  231. package/fixtures/handoff.json +20 -0
  232. package/fixtures/harness.removed.json +15 -0
  233. package/fixtures/harness.transferred.json +15 -0
  234. package/fixtures/hello.json +61 -0
  235. package/fixtures/join.allowed.json +20 -0
  236. package/fixtures/join.denied.json +17 -0
  237. package/fixtures/lane.updated.json +17 -0
  238. package/fixtures/membership.added.json +16 -0
  239. package/fixtures/membership.removed.json +14 -0
  240. package/fixtures/membership.replaced.json +15 -0
  241. package/fixtures/message.deleted.json +15 -0
  242. package/fixtures/message.edited.json +15 -0
  243. package/fixtures/message.handed.json +14 -0
  244. package/fixtures/message.heard.json +14 -0
  245. package/fixtures/message.held.json +14 -0
  246. package/fixtures/message.hidden.json +14 -0
  247. package/fixtures/message.new.json +96 -0
  248. package/fixtures/message.overheard.json +22 -0
  249. package/fixtures/message.pinned.json +14 -0
  250. package/fixtures/message.post.json +14 -0
  251. package/fixtures/message.shown.json +14 -0
  252. package/fixtures/message.unpinned.json +14 -0
  253. package/fixtures/person.face_set.json +17 -0
  254. package/fixtures/person.joined.json +16 -0
  255. package/fixtures/person.removed.json +16 -0
  256. package/fixtures/person.renamed.json +14 -0
  257. package/fixtures/policy.updated.json +30 -0
  258. package/fixtures/project.changed.json +21 -0
  259. package/fixtures/question.answered.json +23 -0
  260. package/fixtures/question.ask.json +37 -0
  261. package/fixtures/receipt.json +15 -0
  262. package/fixtures/relay.set.json +12 -0
  263. package/fixtures/role.changed.json +15 -0
  264. package/fixtures/run.cancel.json +15 -0
  265. package/fixtures/run.completed.json +20 -0
  266. package/fixtures/run.failed.json +20 -0
  267. package/fixtures/run.pause.json +15 -0
  268. package/fixtures/run.plan.json +24 -0
  269. package/fixtures/run.requested.json +34 -0
  270. package/fixtures/run.resume.json +15 -0
  271. package/fixtures/run.started.json +33 -0
  272. package/fixtures/run.status.json +17 -0
  273. package/fixtures/run.steer.json +21 -0
  274. package/fixtures/session.ended.json +15 -0
  275. package/fixtures/session.renamed.json +15 -0
  276. package/fixtures/session.started.json +17 -0
  277. package/fixtures/space.claimed.json +15 -0
  278. package/fixtures/space.created.json +14 -0
  279. package/fixtures/space.erased.json +13 -0
  280. package/fixtures/space.lead_set.json +13 -0
  281. package/fixtures/space.lock_set.json +13 -0
  282. package/fixtures/space.spend_ceiling_set.json +13 -0
  283. package/fixtures/space.updated.json +14 -0
  284. package/fixtures/step.log.json +26 -0
  285. package/fixtures/task.json +22 -0
  286. package/fixtures/turn.passed.json +14 -0
  287. package/fixtures/usage.json +21 -0
  288. package/fixtures/workspace.renamed.json +13 -0
  289. package/generated/agent-connect.json +13966 -0
  290. package/generated/schema/agent.asked.json +114 -0
  291. package/generated/schema/agent.attached.json +465 -0
  292. package/generated/schema/agent.deleted.json +127 -0
  293. package/generated/schema/agent.fork.json +107 -0
  294. package/generated/schema/agent.lock_set.json +105 -0
  295. package/generated/schema/agent.registered.json +130 -0
  296. package/generated/schema/agent.released.json +129 -0
  297. package/generated/schema/agent.updated.json +151 -0
  298. package/generated/schema/approval.executed.json +125 -0
  299. package/generated/schema/approval.request.json +279 -0
  300. package/generated/schema/approval.resolved.json +162 -0
  301. package/generated/schema/approval.routed.json +185 -0
  302. package/generated/schema/artifact.create.json +206 -0
  303. package/generated/schema/attachment.fetched.json +114 -0
  304. package/generated/schema/attachment.limits_set.json +136 -0
  305. package/generated/schema/budget.warning.json +152 -0
  306. package/generated/schema/checkpoint.json +111 -0
  307. package/generated/schema/context.compacted.json +124 -0
  308. package/generated/schema/context.usage.json +120 -0
  309. package/generated/schema/exchange.limits_set.json +127 -0
  310. package/generated/schema/exchange.set.json +144 -0
  311. package/generated/schema/feedback.json +173 -0
  312. package/generated/schema/handoff.json +122 -0
  313. package/generated/schema/harness.removed.json +122 -0
  314. package/generated/schema/harness.transferred.json +117 -0
  315. package/generated/schema/hello.json +509 -0
  316. package/generated/schema/join.allowed.json +176 -0
  317. package/generated/schema/join.denied.json +150 -0
  318. package/generated/schema/lane.updated.json +223 -0
  319. package/generated/schema/membership.added.json +153 -0
  320. package/generated/schema/membership.removed.json +126 -0
  321. package/generated/schema/membership.replaced.json +106 -0
  322. package/generated/schema/message.deleted.json +105 -0
  323. package/generated/schema/message.edited.json +107 -0
  324. package/generated/schema/message.handed.json +111 -0
  325. package/generated/schema/message.heard.json +100 -0
  326. package/generated/schema/message.held.json +181 -0
  327. package/generated/schema/message.hidden.json +100 -0
  328. package/generated/schema/message.new.json +1369 -0
  329. package/generated/schema/message.overheard.json +701 -0
  330. package/generated/schema/message.pinned.json +100 -0
  331. package/generated/schema/message.post.json +290 -0
  332. package/generated/schema/message.shown.json +100 -0
  333. package/generated/schema/message.unpinned.json +100 -0
  334. package/generated/schema/person.face_set.json +134 -0
  335. package/generated/schema/person.joined.json +128 -0
  336. package/generated/schema/person.removed.json +133 -0
  337. package/generated/schema/person.renamed.json +114 -0
  338. package/generated/schema/policy.updated.json +277 -0
  339. package/generated/schema/project.changed.json +199 -0
  340. package/generated/schema/question.answered.json +154 -0
  341. package/generated/schema/question.ask.json +194 -0
  342. package/generated/schema/receipt.json +109 -0
  343. package/generated/schema/relay.set.json +99 -0
  344. package/generated/schema/role.changed.json +116 -0
  345. package/generated/schema/run.cancel.json +106 -0
  346. package/generated/schema/run.completed.json +139 -0
  347. package/generated/schema/run.failed.json +135 -0
  348. package/generated/schema/run.pause.json +106 -0
  349. package/generated/schema/run.plan.json +128 -0
  350. package/generated/schema/run.requested.json +309 -0
  351. package/generated/schema/run.resume.json +106 -0
  352. package/generated/schema/run.started.json +142 -0
  353. package/generated/schema/run.status.json +120 -0
  354. package/generated/schema/run.steer.json +163 -0
  355. package/generated/schema/session.ended.json +106 -0
  356. package/generated/schema/session.renamed.json +108 -0
  357. package/generated/schema/session.started.json +116 -0
  358. package/generated/schema/space.claimed.json +107 -0
  359. package/generated/schema/space.created.json +148 -0
  360. package/generated/schema/space.erased.json +129 -0
  361. package/generated/schema/space.lead_set.json +108 -0
  362. package/generated/schema/space.lock_set.json +99 -0
  363. package/generated/schema/space.spend_ceiling_set.json +102 -0
  364. package/generated/schema/space.updated.json +112 -0
  365. package/generated/schema/step.log.json +187 -0
  366. package/generated/schema/task.json +268 -0
  367. package/generated/schema/turn.passed.json +99 -0
  368. package/generated/schema/usage.json +155 -0
  369. package/generated/schema/workspace.renamed.json +107 -0
  370. package/generated/types.ts +4178 -0
  371. package/package.json +38 -3
@@ -0,0 +1,177 @@
1
+ /**
2
+ * The relay's control frames (M4.1, ADR 0038).
3
+ *
4
+ * The relay is a switchboard: a home server dials out to it and says which key it holds; a
5
+ * phone dials in and asks for a server by that key; the relay holds the two connections
6
+ * together and moves bytes between them. The bytes are ciphertext it cannot read — the tunnel
7
+ * inside is `@chipschat/tunnel`'s — and these frames are the only thing it does understand: text
8
+ * frames on the WebSocket, JSON, one `frame` word each. Data travels in binary frames beside
9
+ * them and is never parsed here.
10
+ *
11
+ * Every frame carries a version, and the two that open a path — `serve` and `connect` — carry
12
+ * a list of candidate addresses that is empty in v0 (ADR 0038, decisions 3 and 4). A peer that
13
+ * receives a version it does not know reads the fields it does know and uses the relay,
14
+ * rather than failing: the version is the room left for NAT traversal, and it costs one field.
15
+ */
16
+ import { z } from "zod";
17
+ /** The relay protocol's version. Bumped when a frame's meaning changes, never for a new field. */
18
+ export declare const RELAY_VERSION = 1;
19
+ /** How many bytes name a session in the relay's binary frames, before the ciphertext. */
20
+ export declare const RELAY_SESSION_ID_BYTES = 16;
21
+ /**
22
+ * The tunnel door on the server's own network (ADR 0191): a WebSocket at this path, on the same
23
+ * port as `/v1/ws`, where a phone at home opens the same Noise tunnel it opens through the relay
24
+ * when it is away, to the same key. `/v1/health` names it as `tunnel.path`, so a client reads the
25
+ * path rather than knowing it.
26
+ */
27
+ export declare const TUNNEL_DOOR_PATH = "/v1/tunnel";
28
+ /** A session's name: sixteen random bytes as hex. */
29
+ export declare const relaySessionId: z.ZodString;
30
+ /**
31
+ * A way a peer might be reached without the relay. Nothing sends one in v0 and nothing reads
32
+ * one; the shape is loose so that a v1 peer can carry a v2 peer's candidates unread rather
33
+ * than refuse the frame (ADR 0038, decision 3).
34
+ */
35
+ export declare const candidateAddress: z.ZodObject<{
36
+ address: z.ZodString;
37
+ }, z.core.$loose>;
38
+ export declare const candidateAddresses: z.ZodDefault<z.ZodArray<z.ZodObject<{
39
+ address: z.ZodString;
40
+ }, z.core.$loose>>>;
41
+ /** Relay → server, first: prove you hold the key you are about to claim. */
42
+ export declare const relayChallengeFrame: z.ZodObject<{
43
+ frame: z.ZodLiteral<"challenge">;
44
+ v: z.ZodInt;
45
+ nonce: z.ZodString;
46
+ ephemeral: z.ZodString;
47
+ }, z.core.$strip>;
48
+ /** Server → relay: this is my key, and here is the proof. */
49
+ export declare const relayServeFrame: z.ZodObject<{
50
+ frame: z.ZodLiteral<"serve">;
51
+ v: z.ZodInt;
52
+ key: z.ZodString;
53
+ proof: z.ZodString;
54
+ candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
55
+ address: z.ZodString;
56
+ }, z.core.$loose>>>;
57
+ }, z.core.$strip>;
58
+ /** Relay → server: registered; phones may ask for you now. */
59
+ export declare const relayServingFrame: z.ZodObject<{
60
+ frame: z.ZodLiteral<"serving">;
61
+ v: z.ZodInt;
62
+ session_limit: z.ZodInt;
63
+ }, z.core.$strip>;
64
+ /** Phone → relay: put me through to this server. */
65
+ export declare const relayConnectFrame: z.ZodObject<{
66
+ frame: z.ZodLiteral<"connect">;
67
+ v: z.ZodInt;
68
+ key: z.ZodString;
69
+ candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
70
+ address: z.ZodString;
71
+ }, z.core.$loose>>>;
72
+ }, z.core.$strip>;
73
+ /** Relay → phone: you are through; the binary frames from here on are the tunnel's. */
74
+ export declare const relayConnectedFrame: z.ZodObject<{
75
+ frame: z.ZodLiteral<"connected">;
76
+ v: z.ZodInt;
77
+ session: z.ZodString;
78
+ }, z.core.$strip>;
79
+ /** Relay → server: a phone is here; its bytes arrive under this session id. */
80
+ export declare const relayOpenFrame: z.ZodObject<{
81
+ frame: z.ZodLiteral<"open">;
82
+ v: z.ZodInt;
83
+ session: z.ZodString;
84
+ from: z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodLiteral<"">]>;
85
+ }, z.core.$strip>;
86
+ /** Either way: a session is over. The relay tells the other side and closes the phone's socket. */
87
+ export declare const relayCloseFrame: z.ZodObject<{
88
+ frame: z.ZodLiteral<"close">;
89
+ v: z.ZodInt;
90
+ session: z.ZodString;
91
+ reason: z.ZodString;
92
+ }, z.core.$strip>;
93
+ export declare const RELAY_ERROR_CODES: readonly ["unknown_version", "bad_frame", "bad_proof", "no_such_server", "busy", "replaced", "not_allowed"];
94
+ export type RelayErrorCode = (typeof RELAY_ERROR_CODES)[number];
95
+ /** Relay → either side: no, and why. The socket closes after it. */
96
+ export declare const relayErrorFrame: z.ZodObject<{
97
+ frame: z.ZodLiteral<"error">;
98
+ v: z.ZodInt;
99
+ code: z.ZodEnum<{
100
+ unknown_version: "unknown_version";
101
+ bad_frame: "bad_frame";
102
+ bad_proof: "bad_proof";
103
+ no_such_server: "no_such_server";
104
+ busy: "busy";
105
+ replaced: "replaced";
106
+ not_allowed: "not_allowed";
107
+ }>;
108
+ message: z.ZodString;
109
+ }, z.core.$strip>;
110
+ export declare const relayFrame: z.ZodDiscriminatedUnion<[z.ZodObject<{
111
+ frame: z.ZodLiteral<"challenge">;
112
+ v: z.ZodInt;
113
+ nonce: z.ZodString;
114
+ ephemeral: z.ZodString;
115
+ }, z.core.$strip>, z.ZodObject<{
116
+ frame: z.ZodLiteral<"serve">;
117
+ v: z.ZodInt;
118
+ key: z.ZodString;
119
+ proof: z.ZodString;
120
+ candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
121
+ address: z.ZodString;
122
+ }, z.core.$loose>>>;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ frame: z.ZodLiteral<"serving">;
125
+ v: z.ZodInt;
126
+ session_limit: z.ZodInt;
127
+ }, z.core.$strip>, z.ZodObject<{
128
+ frame: z.ZodLiteral<"connect">;
129
+ v: z.ZodInt;
130
+ key: z.ZodString;
131
+ candidates: z.ZodDefault<z.ZodArray<z.ZodObject<{
132
+ address: z.ZodString;
133
+ }, z.core.$loose>>>;
134
+ }, z.core.$strip>, z.ZodObject<{
135
+ frame: z.ZodLiteral<"connected">;
136
+ v: z.ZodInt;
137
+ session: z.ZodString;
138
+ }, z.core.$strip>, z.ZodObject<{
139
+ frame: z.ZodLiteral<"open">;
140
+ v: z.ZodInt;
141
+ session: z.ZodString;
142
+ from: z.ZodUnion<readonly [z.ZodIPv4, z.ZodIPv6, z.ZodLiteral<"">]>;
143
+ }, z.core.$strip>, z.ZodObject<{
144
+ frame: z.ZodLiteral<"close">;
145
+ v: z.ZodInt;
146
+ session: z.ZodString;
147
+ reason: z.ZodString;
148
+ }, z.core.$strip>, z.ZodObject<{
149
+ frame: z.ZodLiteral<"error">;
150
+ v: z.ZodInt;
151
+ code: z.ZodEnum<{
152
+ unknown_version: "unknown_version";
153
+ bad_frame: "bad_frame";
154
+ bad_proof: "bad_proof";
155
+ no_such_server: "no_such_server";
156
+ busy: "busy";
157
+ replaced: "replaced";
158
+ not_allowed: "not_allowed";
159
+ }>;
160
+ message: z.ZodString;
161
+ }, z.core.$strip>], "frame">;
162
+ export type RelayFrame = z.infer<typeof relayFrame>;
163
+ export type RelayFrameName = RelayFrame["frame"];
164
+ export type CandidateAddress = z.infer<typeof candidateAddress>;
165
+ /**
166
+ * Read a text frame. A frame from a newer peer parses by the fields this version knows; the
167
+ * caller can see `v` and say so in its log. Anything else is refused with a reason the relay
168
+ * puts in an `error` frame.
169
+ */
170
+ export declare function parseRelayFrame(text: string): {
171
+ ok: true;
172
+ frame: RelayFrame;
173
+ } | {
174
+ ok: false;
175
+ reason: "not_json" | "bad_frame";
176
+ };
177
+ //# sourceMappingURL=relay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.d.ts","sourceRoot":"","sources":["../../src/relay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,kGAAkG;AAClG,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,yFAAyF;AACzF,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAK7C,qDAAqD;AACrD,eAAO,MAAM,cAAc,aAAqD,CAAC;AAOjF;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;iBAIqC,CAAC;AAEnE,eAAO,MAAM,kBAAkB;;mBAI0C,CAAC;AAE1E,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB;;;;;iBAK9B,CAAC;AAEH,6DAA6D;AAC7D,eAAO,MAAM,eAAe;;;;;;;;iBAQ1B,CAAC;AAEH,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB;;;;iBAI5B,CAAC;AAEH,oDAAoD;AACpD,eAAO,MAAM,iBAAiB;;;;;;;iBAK5B,CAAC;AAEH,uFAAuF;AACvF,eAAO,MAAM,mBAAmB;;;;iBAI9B,CAAC;AAEH,+EAA+E;AAC/E,eAAO,MAAM,cAAc;;;;;iBASzB,CAAC;AAEH,mGAAmG;AACnG,eAAO,MAAM,eAAe;;;;;iBAK1B,CAAC;AAEH,eAAO,MAAM,iBAAiB,6GAYpB,CAAC;AACX,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEhE,oEAAoE;AACpE,eAAO,MAAM,eAAe;;;;;;;;;;;;;iBAK1B,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BASrB,CAAC;AAEH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AACpD,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AACjD,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,GACX;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,GAAG,WAAW,CAAA;CAAE,CASnF"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * The relay's control frames (M4.1, ADR 0038).
3
+ *
4
+ * The relay is a switchboard: a home server dials out to it and says which key it holds; a
5
+ * phone dials in and asks for a server by that key; the relay holds the two connections
6
+ * together and moves bytes between them. The bytes are ciphertext it cannot read — the tunnel
7
+ * inside is `@chipschat/tunnel`'s — and these frames are the only thing it does understand: text
8
+ * frames on the WebSocket, JSON, one `frame` word each. Data travels in binary frames beside
9
+ * them and is never parsed here.
10
+ *
11
+ * Every frame carries a version, and the two that open a path — `serve` and `connect` — carry
12
+ * a list of candidate addresses that is empty in v0 (ADR 0038, decisions 3 and 4). A peer that
13
+ * receives a version it does not know reads the fields it does know and uses the relay,
14
+ * rather than failing: the version is the room left for NAT traversal, and it costs one field.
15
+ */
16
+ import { z } from "zod";
17
+ /** The relay protocol's version. Bumped when a frame's meaning changes, never for a new field. */
18
+ export const RELAY_VERSION = 1;
19
+ /** How many bytes name a session in the relay's binary frames, before the ciphertext. */
20
+ export const RELAY_SESSION_ID_BYTES = 16;
21
+ /**
22
+ * The tunnel door on the server's own network (ADR 0191): a WebSocket at this path, on the same
23
+ * port as `/v1/ws`, where a phone at home opens the same Noise tunnel it opens through the relay
24
+ * when it is away, to the same key. `/v1/health` names it as `tunnel.path`, so a client reads the
25
+ * path rather than knowing it.
26
+ */
27
+ export const TUNNEL_DOOR_PATH = "/v1/tunnel";
28
+ /** Thirty-two bytes as base64url without padding, which is what every key and nonce here is. */
29
+ const bytes32 = z.string().regex(/^[A-Za-z0-9_-]{43}$/, "thirty-two bytes, base64url");
30
+ /** A session's name: sixteen random bytes as hex. */
31
+ export const relaySessionId = z.string().regex(/^[0-9a-f]{32}$/, "a session id");
32
+ const version = z
33
+ .int()
34
+ .positive()
35
+ .describe("The relay protocol version the sender speaks. Unknown fields are ignored.");
36
+ /**
37
+ * A way a peer might be reached without the relay. Nothing sends one in v0 and nothing reads
38
+ * one; the shape is loose so that a v1 peer can carry a v2 peer's candidates unread rather
39
+ * than refuse the frame (ADR 0038, decision 3).
40
+ */
41
+ export const candidateAddress = z
42
+ .looseObject({
43
+ address: z.string().min(1).max(255).describe("Where, in a form the kind decides."),
44
+ })
45
+ .describe("A candidate address for a direct path. Empty in v0.");
46
+ export const candidateAddresses = z
47
+ .array(candidateAddress)
48
+ .max(16)
49
+ .default([])
50
+ .describe("Candidate addresses for a direct path. Always empty in v0.");
51
+ /** Relay → server, first: prove you hold the key you are about to claim. */
52
+ export const relayChallengeFrame = z.object({
53
+ frame: z.literal("challenge"),
54
+ v: version,
55
+ nonce: bytes32.describe("Fresh for this connection."),
56
+ ephemeral: bytes32.describe("The relay's X25519 ephemeral public key, for the proof."),
57
+ });
58
+ /** Server → relay: this is my key, and here is the proof. */
59
+ export const relayServeFrame = z.object({
60
+ frame: z.literal("serve"),
61
+ v: version,
62
+ key: bytes32.describe("The server's X25519 static public key: what a phone asks for."),
63
+ proof: bytes32.describe("HMAC-SHA256 over the nonce and the key, keyed by X25519(static, ephemeral)."),
64
+ candidates: candidateAddresses,
65
+ });
66
+ /** Relay → server: registered; phones may ask for you now. */
67
+ export const relayServingFrame = z.object({
68
+ frame: z.literal("serving"),
69
+ v: version,
70
+ session_limit: z.int().positive().describe("How many phones this relay holds open for you."),
71
+ });
72
+ /** Phone → relay: put me through to this server. */
73
+ export const relayConnectFrame = z.object({
74
+ frame: z.literal("connect"),
75
+ v: version,
76
+ key: bytes32.describe("The server's key, from the pairing payload."),
77
+ candidates: candidateAddresses,
78
+ });
79
+ /** Relay → phone: you are through; the binary frames from here on are the tunnel's. */
80
+ export const relayConnectedFrame = z.object({
81
+ frame: z.literal("connected"),
82
+ v: version,
83
+ session: relaySessionId,
84
+ });
85
+ /** Relay → server: a phone is here; its bytes arrive under this session id. */
86
+ export const relayOpenFrame = z.object({
87
+ frame: z.literal("open"),
88
+ v: version,
89
+ session: relaySessionId,
90
+ from: z
91
+ .union([z.ipv4(), z.ipv6(), z.literal("")])
92
+ .describe("The phone's address as the relay saw it, for the server's limiter and its join cards: an IP address, or nothing when the relay could not tell (SEC-27)."),
93
+ });
94
+ /** Either way: a session is over. The relay tells the other side and closes the phone's socket. */
95
+ export const relayCloseFrame = z.object({
96
+ frame: z.literal("close"),
97
+ v: version,
98
+ session: relaySessionId,
99
+ reason: z.string().max(120),
100
+ });
101
+ export const RELAY_ERROR_CODES = [
102
+ "unknown_version",
103
+ "bad_frame",
104
+ "bad_proof",
105
+ "no_such_server",
106
+ "busy",
107
+ "replaced",
108
+ /**
109
+ * A relay that serves a list of servers, and this key is not on it. Its operator adds the key
110
+ * to the list; the server keeps dialling meanwhile, so it is listed the moment that happens.
111
+ */
112
+ "not_allowed",
113
+ ];
114
+ /** Relay → either side: no, and why. The socket closes after it. */
115
+ export const relayErrorFrame = z.object({
116
+ frame: z.literal("error"),
117
+ v: version,
118
+ code: z.enum(RELAY_ERROR_CODES),
119
+ message: z.string().max(240),
120
+ });
121
+ export const relayFrame = z.discriminatedUnion("frame", [
122
+ relayChallengeFrame,
123
+ relayServeFrame,
124
+ relayServingFrame,
125
+ relayConnectFrame,
126
+ relayConnectedFrame,
127
+ relayOpenFrame,
128
+ relayCloseFrame,
129
+ relayErrorFrame,
130
+ ]);
131
+ /**
132
+ * Read a text frame. A frame from a newer peer parses by the fields this version knows; the
133
+ * caller can see `v` and say so in its log. Anything else is refused with a reason the relay
134
+ * puts in an `error` frame.
135
+ */
136
+ export function parseRelayFrame(text) {
137
+ let parsed;
138
+ try {
139
+ parsed = JSON.parse(text);
140
+ }
141
+ catch {
142
+ return { ok: false, reason: "not_json" };
143
+ }
144
+ const frame = relayFrame.safeParse(parsed);
145
+ return frame.success ? { ok: true, frame: frame.data } : { ok: false, reason: "bad_frame" };
146
+ }
147
+ //# sourceMappingURL=relay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relay.js","sourceRoot":"","sources":["../../src/relay.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The room's files (ADR 0099). A file posted in a room is the room's: the files of one name are
3
+ * its versions, in the order they were posted, and the newest is the current one. The ASCII Art
4
+ * room (2 October 2026) had none of this: its agents were handed a path on another machine, and
5
+ * the one page they shared was written over by whoever posted last.
6
+ *
7
+ * - **Versions.** Walking a room's files of one name in order, a file whose bytes differ from the
8
+ * version before it is the next version. The same bytes again are the same version, and bytes
9
+ * of an older one again are a new one: a revert is a change.
10
+ * - **What a harness is handed.** The current version of each name, newest first, with a message
11
+ * for it (`context.files`), from `GET /v1/spaces/:id/files`, and from the context route.
12
+ * - **Made from what.** A post says which version each of its files was made from (`replaces`). One
13
+ * made from bytes that are no longer the current version's is refused, `stale_file`, so two agents
14
+ * editing one file at once lose nothing silently: the one that loses is told who won, fetches the
15
+ * current version and redoes. A post that says nothing is the next version, unchecked.
16
+ *
17
+ * The server, the conformance fake and the SDK hand over the same list and refuse the same posts,
18
+ * so the rules and the words live here, with the schemas.
19
+ */
20
+ import { z } from "zod";
21
+ /** The most of a room's files a message hands over: its newest. The rest are the route's. */
22
+ export declare const ROOM_FILES_IN_CONTEXT = 20;
23
+ /** The most `GET /v1/spaces/:id/files` lists. */
24
+ export declare const ROOM_FILES_CEILING = 200;
25
+ /** The heading the room's files open with in the plain form. */
26
+ export declare const ROOM_FILES_HEADING = "Files in this space:";
27
+ /** The code a post is refused with when a file in it was made from a version no longer current. */
28
+ export declare const STALE_FILE = "stale_file";
29
+ export declare const roomFile: z.ZodObject<{
30
+ name: z.ZodString;
31
+ version: z.ZodInt;
32
+ attachment_id: z.ZodString;
33
+ sha256: z.ZodString;
34
+ byte_size: z.ZodInt;
35
+ mime_type: z.ZodString;
36
+ by: z.ZodObject<{
37
+ id: z.ZodString;
38
+ kind: z.ZodEnum<{
39
+ person: "person";
40
+ harness: "harness";
41
+ agent: "agent";
42
+ system: "system";
43
+ }>;
44
+ name: z.ZodString;
45
+ }, z.core.$strip>;
46
+ at: z.ZodISODateTime;
47
+ message_id: z.ZodOptional<z.ZodString>;
48
+ width: z.ZodOptional<z.ZodInt>;
49
+ height: z.ZodOptional<z.ZodInt>;
50
+ duration_ms: z.ZodOptional<z.ZodInt>;
51
+ replaces: z.ZodOptional<z.ZodString>;
52
+ url: z.ZodOptional<z.ZodURL>;
53
+ url_expires_at: z.ZodOptional<z.ZodISODateTime>;
54
+ }, z.core.$strip>;
55
+ export type RoomFile = z.infer<typeof roomFile>;
56
+ export declare const roomFiles: z.ZodArray<z.ZodObject<{
57
+ name: z.ZodString;
58
+ version: z.ZodInt;
59
+ attachment_id: z.ZodString;
60
+ sha256: z.ZodString;
61
+ byte_size: z.ZodInt;
62
+ mime_type: z.ZodString;
63
+ by: z.ZodObject<{
64
+ id: z.ZodString;
65
+ kind: z.ZodEnum<{
66
+ person: "person";
67
+ harness: "harness";
68
+ agent: "agent";
69
+ system: "system";
70
+ }>;
71
+ name: z.ZodString;
72
+ }, z.core.$strip>;
73
+ at: z.ZodISODateTime;
74
+ message_id: z.ZodOptional<z.ZodString>;
75
+ width: z.ZodOptional<z.ZodInt>;
76
+ height: z.ZodOptional<z.ZodInt>;
77
+ duration_ms: z.ZodOptional<z.ZodInt>;
78
+ replaces: z.ZodOptional<z.ZodString>;
79
+ url: z.ZodOptional<z.ZodURL>;
80
+ url_expires_at: z.ZodOptional<z.ZodISODateTime>;
81
+ }, z.core.$strip>>;
82
+ /** A file a message carried, where and by whom: what versions are worked out from. */
83
+ export interface PostedFile {
84
+ /** The seq of the message that carried it. */
85
+ readonly seq: number;
86
+ readonly at: string;
87
+ /** Who posted it: a participant id. */
88
+ readonly by: string;
89
+ readonly attachment_id: string;
90
+ readonly filename: string;
91
+ readonly sha256: string;
92
+ readonly byte_size: number;
93
+ readonly mime_type: string;
94
+ /** The message that carried it. */
95
+ readonly message_id?: string | undefined;
96
+ /** Pixels and milliseconds, where the server measured them. */
97
+ readonly width?: number | undefined;
98
+ readonly height?: number | undefined;
99
+ readonly duration_ms?: number | undefined;
100
+ }
101
+ /** A version of a file: the post that made it, and which version it is. */
102
+ export interface FileVersion extends PostedFile {
103
+ readonly version: number;
104
+ }
105
+ /**
106
+ * Each name's versions, oldest first, from a room's files in the order they were posted. A file
107
+ * whose bytes are the version before it is no version of its own.
108
+ */
109
+ export declare function versionsOf(posted: readonly PostedFile[]): Map<string, FileVersion[]>;
110
+ /** The current version of each name, newest first, as many as `most`. */
111
+ export declare function currentFiles(posted: readonly PostedFile[], most: number): FileVersion[];
112
+ /** One name's versions, newest first, as many as `most` (ADR 0100). None for a name never posted. */
113
+ export declare function versionsNamed(posted: readonly PostedFile[], name: string, most: number): FileVersion[];
114
+ /** A file a post carries that says which version it was made from. */
115
+ export interface MadeFrom {
116
+ readonly filename: string;
117
+ /** The bytes posted. */
118
+ readonly sha256: string;
119
+ /** The bytes of the version it says it was made from. */
120
+ readonly from: string;
121
+ }
122
+ /** Why a post may not be taken: the first of its files made from a version no longer current. */
123
+ export interface StaleRefusal {
124
+ readonly kind: typeof STALE_FILE;
125
+ readonly message: string;
126
+ }
127
+ /**
128
+ * The current version a post's file was not made from, when one was not: the first file made from
129
+ * bytes that are not its name's current version, unless what it posts is the current version
130
+ * already. A name with no version left in the room (each one taken back) has nothing to be stale
131
+ * against. Nothing when the post may be taken.
132
+ */
133
+ export declare function staleVersion(made: readonly MadeFrom[], versions: ReadonlyMap<string, readonly FileVersion[]>): FileVersion | undefined;
134
+ /** The refusal of a post made from a version no longer current, naming who posted the current one. */
135
+ export declare function staleRefusal(current: Pick<FileVersion, "filename" | "version">, by: string): StaleRefusal;
136
+ /**
137
+ * The refusal's words, which name the file so an adapter can leave out just that one:
138
+ * `ascii-alphabet.html is now v3, by Oc2. Fetch it and redo.`
139
+ */
140
+ export declare function staleWords(current: Pick<FileVersion, "filename" | "version">, by: string): string;
141
+ /**
142
+ * The room's files in the plain form, after the lines: one per file, `name · v3 · by Oc2 · when ·
143
+ * url`. Nothing when there are none.
144
+ */
145
+ export declare function renderRoomFiles(files: readonly RoomFile[] | undefined): string;
146
+ //# sourceMappingURL=room-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-files.d.ts","sourceRoot":"","sources":["../../src/room-files.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,6FAA6F;AAC7F,eAAO,MAAM,qBAAqB,KAAK,CAAC;AACxC,iDAAiD;AACjD,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,gEAAgE;AAChE,eAAO,MAAM,kBAAkB,yBAAyB,CAAC;AAEzD,mGAAmG;AACnG,eAAO,MAAM,UAAU,eAAe,CAAC;AAEvC,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;iBA2ClB,CAAC;AACJ,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;kBAKnB,CAAC;AAEJ,sFAAsF;AACtF,MAAM,WAAW,UAAU;IACzB,8CAA8C;IAC9C,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mCAAmC;IACnC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC3C;AAED,2EAA2E;AAC3E,MAAM,WAAW,WAAY,SAAQ,UAAU;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CASpF;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,GAAG,WAAW,EAAE,CAGvF;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,UAAU,EAAE,EAC7B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,GACX,WAAW,EAAE,CAGf;AAED,sEAAsE;AACtE,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,iGAAiG;AACjG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,OAAO,UAAU,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,QAAQ,EAAE,EACzB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC,GACpD,WAAW,GAAG,SAAS,CAQzB;AAED,sGAAsG;AACtG,wBAAgB,YAAY,CAC1B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC,EAClD,EAAE,EAAE,MAAM,GACT,YAAY,CAEd;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,UAAU,GAAG,SAAS,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,GAAG,SAAS,GAAG,MAAM,CAQ9E"}
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The room's files (ADR 0099). A file posted in a room is the room's: the files of one name are
3
+ * its versions, in the order they were posted, and the newest is the current one. The ASCII Art
4
+ * room (2 October 2026) had none of this: its agents were handed a path on another machine, and
5
+ * the one page they shared was written over by whoever posted last.
6
+ *
7
+ * - **Versions.** Walking a room's files of one name in order, a file whose bytes differ from the
8
+ * version before it is the next version. The same bytes again are the same version, and bytes
9
+ * of an older one again are a new one: a revert is a change.
10
+ * - **What a harness is handed.** The current version of each name, newest first, with a message
11
+ * for it (`context.files`), from `GET /v1/spaces/:id/files`, and from the context route.
12
+ * - **Made from what.** A post says which version each of its files was made from (`replaces`). One
13
+ * made from bytes that are no longer the current version's is refused, `stale_file`, so two agents
14
+ * editing one file at once lose nothing silently: the one that loses is told who won, fetches the
15
+ * current version and redoes. A post that says nothing is the next version, unchecked.
16
+ *
17
+ * The server, the conformance fake and the SDK hand over the same list and refuse the same posts,
18
+ * so the rules and the words live here, with the schemas.
19
+ */
20
+ import { z } from "zod";
21
+ import { id, isoTimestamp } from "./primitives.js";
22
+ /** The most of a room's files a message hands over: its newest. The rest are the route's. */
23
+ export const ROOM_FILES_IN_CONTEXT = 20;
24
+ /** The most `GET /v1/spaces/:id/files` lists. */
25
+ export const ROOM_FILES_CEILING = 200;
26
+ /** The heading the room's files open with in the plain form. */
27
+ export const ROOM_FILES_HEADING = "Files in this space:";
28
+ /** The code a post is refused with when a file in it was made from a version no longer current. */
29
+ export const STALE_FILE = "stale_file";
30
+ export const roomFile = z
31
+ .object({
32
+ name: z.string().min(1).max(255).describe("The file's name, which its versions share."),
33
+ version: z
34
+ .int()
35
+ .positive()
36
+ .describe("Which version this is: 1 for the first, counting each change of its bytes."),
37
+ attachment_id: id("attachment").describe("The attachment that is this version: what a post names in `replaces` when it changes it."),
38
+ sha256: z
39
+ .string()
40
+ .regex(/^[a-f0-9]{64}$/)
41
+ .describe("Lowercase hex digest of this version's bytes."),
42
+ byte_size: z.int().nonnegative().describe("Size in bytes."),
43
+ mime_type: z.string().min(1).max(255).describe("Sniffed from the bytes on upload."),
44
+ by: z
45
+ .object({
46
+ id: z.string().min(1).max(120).describe("Their participant id."),
47
+ kind: z.enum(["person", "harness", "agent", "system"]),
48
+ name: z.string().min(1).max(120).describe("What they go by now."),
49
+ })
50
+ .describe("Who posted this version, as a line's `from` says it."),
51
+ at: isoTimestamp.describe("When this version was posted."),
52
+ message_id: id("message").optional().describe("The message that posted this version."),
53
+ width: z.int().positive().optional().describe("Pixels, for a photo or a video."),
54
+ height: z.int().positive().optional().describe("Pixels, for a photo or a video."),
55
+ duration_ms: z.int().nonnegative().optional().describe("Milliseconds, for a sound or a video."),
56
+ replaces: id("attachment")
57
+ .optional()
58
+ .describe("In one name's versions (`?name=`): the version this one was made from, as its post said (`message.post.replaces`). Absent where the post said nothing, or named no version of this file."),
59
+ url: z
60
+ .url()
61
+ .optional()
62
+ .describe("For a harness: a signed URL for this version, bound to its token, as a message's attachments are. Absent for a person, who fetches it by id."),
63
+ url_expires_at: isoTimestamp.optional().describe("When `url` stops working."),
64
+ })
65
+ .describe("One of a room's files at one of its versions: the current one, in the room's list (ADR 0099).");
66
+ export const roomFiles = z
67
+ .array(roomFile)
68
+ .max(ROOM_FILES_CEILING)
69
+ .describe("The current version of each of the room's files, newest first (ADR 0099); or, asked for one name (`?name=`), that name's versions, newest first (ADR 0100).");
70
+ /**
71
+ * Each name's versions, oldest first, from a room's files in the order they were posted. A file
72
+ * whose bytes are the version before it is no version of its own.
73
+ */
74
+ export function versionsOf(posted) {
75
+ const versions = new Map();
76
+ for (const file of posted) {
77
+ const known = versions.get(file.filename) ?? [];
78
+ if (known.at(-1)?.sha256 === file.sha256)
79
+ continue;
80
+ known.push({ ...file, version: known.length + 1 });
81
+ versions.set(file.filename, known);
82
+ }
83
+ return versions;
84
+ }
85
+ /** The current version of each name, newest first, as many as `most`. */
86
+ export function currentFiles(posted, most) {
87
+ const current = [...versionsOf(posted).values()].flatMap((known) => known.slice(-1));
88
+ return current.sort((a, b) => b.seq - a.seq).slice(0, Math.max(0, most));
89
+ }
90
+ /** One name's versions, newest first, as many as `most` (ADR 0100). None for a name never posted. */
91
+ export function versionsNamed(posted, name, most) {
92
+ const known = versionsOf(posted.filter((file) => file.filename === name)).get(name) ?? [];
93
+ return [...known].reverse().slice(0, Math.max(0, most));
94
+ }
95
+ /**
96
+ * The current version a post's file was not made from, when one was not: the first file made from
97
+ * bytes that are not its name's current version, unless what it posts is the current version
98
+ * already. A name with no version left in the room (each one taken back) has nothing to be stale
99
+ * against. Nothing when the post may be taken.
100
+ */
101
+ export function staleVersion(made, versions) {
102
+ for (const file of made) {
103
+ const current = versions.get(file.filename)?.at(-1);
104
+ if (current === undefined)
105
+ continue;
106
+ if (file.from === current.sha256 || file.sha256 === current.sha256)
107
+ continue;
108
+ return current;
109
+ }
110
+ return undefined;
111
+ }
112
+ /** The refusal of a post made from a version no longer current, naming who posted the current one. */
113
+ export function staleRefusal(current, by) {
114
+ return { kind: STALE_FILE, message: staleWords(current, by) };
115
+ }
116
+ /**
117
+ * The refusal's words, which name the file so an adapter can leave out just that one:
118
+ * `ascii-alphabet.html is now v3, by Oc2. Fetch it and redo.`
119
+ */
120
+ export function staleWords(current, by) {
121
+ return `${current.filename} is now v${current.version}, by ${by}. Fetch it and redo.`;
122
+ }
123
+ /**
124
+ * The room's files in the plain form, after the lines: one per file, `name · v3 · by Oc2 · when ·
125
+ * url`. Nothing when there are none.
126
+ */
127
+ export function renderRoomFiles(files) {
128
+ if (files === undefined || files.length === 0)
129
+ return "";
130
+ const lines = files.map((file) => [file.name, `v${file.version}`, `by ${file.by.name}`, file.at, file.url]
131
+ .filter((part) => part !== undefined)
132
+ .join(" · "));
133
+ return [ROOM_FILES_HEADING, ...lines].join("\n");
134
+ }
135
+ //# sourceMappingURL=room-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room-files.js","sourceRoot":"","sources":["../../src/room-files.ts"],"names":[],"mappings":"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"}