@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,142 @@
1
+ /**
2
+ * The face a person has chosen for a space: a mark from Chips' own library and the ground it
3
+ * sits on (21 September 2026).
4
+ *
5
+ * It is a person's own setting, not a fact about the room (the alternative — `space.icon_set`
6
+ * on the log, one face for everybody — is a bigger thing and nobody has asked for it). So it
7
+ * lives beside the notification settings from M5.6 rather than in the event log: read and
8
+ * written over `/v1/me/space-icons`, private to the person who chose it, and carried to every
9
+ * device they sign in on. Two people may see one room differently, which is the point.
10
+ *
11
+ * An agent's face is the same pair, a mark and a ground, but it is the agent's and one for
12
+ * everybody: it rides on `agent.updated` and in the directory, not here (ADR 0089). So is a
13
+ * person's own face, on `person.face_set` (ADR 0139).
14
+ *
15
+ * Since 7 October 2026 a face may be letters in place of a mark: one or two of them, or an emoji
16
+ * or two, typed by the person choosing it ("AB", "X", "🦊"). Arjun asked for it on every face at
17
+ * once — a space's, an agent's and a person's own — so it is a third field of the one shape, not
18
+ * a shape of its own (ADR 0139).
19
+ *
20
+ * The ground is an **index**, not a colour. A hex on the wire would freeze the palette into
21
+ * every stored row: the lift of 21 September 2026 moved all eight avatar hues, and an index
22
+ * means everyone's chosen face moved with it instead of stranding them on the old paint.
23
+ */
24
+ import { z } from "zod";
25
+ /**
26
+ * How many grounds a client offers: the design system's eight avatar hues, and nineteen more
27
+ * that belong to no participant. A client draws index `n` from its own list; the wire only
28
+ * agrees on how many there are.
29
+ *
30
+ * Twelve until 26 September 2026, when Arjun asked for more colours: fifteen were added after
31
+ * them (a red, an orange, an olive, three greys and a deep shade of every hue), so a face chosen
32
+ * on the twelve kept its index and its colour. A client older than that draws an index past its
33
+ * own list on its first ground.
34
+ */
35
+ export const SPACE_ICON_GROUNDS = 27;
36
+ /** As many letters as a face holds: "AB", or one emoji beside another. */
37
+ export const MOST_FACE_LETTERS = 2;
38
+ /**
39
+ * One letter as a reader sees it: a base and whatever joins it — a combining accent, a skin tone,
40
+ * an emoji's variation selector or keycap, a flag's tags, or another emoji after a joiner — and a
41
+ * flag's two regional indicators. A face's "two letters" are two of these, so "👨‍👩‍👧" is one and
42
+ * "é" is one however it was typed.
43
+ *
44
+ * Written out rather than `Intl.Segmenter`, which Hermes does not have: the phone, the server and
45
+ * the web count the same letters the same way, and a face one of them takes the others take. It
46
+ * leaves out what a face would never be (Hangul typed jamo by jamo); a cluster it splits in two
47
+ * counts as two letters, which is a refusal, never a face drawn wrong.
48
+ */
49
+ const LETTER = /\p{Regional_Indicator}{2}|\P{M}(?:[\p{M}\p{Emoji_Modifier}\u{E0020}-\u{E007F}]|\u200d\P{M}?)*|\p{M}+/gu;
50
+ /** The letters of some text, as a reader counts them (`LETTER`). */
51
+ export function lettersIn(text) {
52
+ return text.match(LETTER) ?? [];
53
+ }
54
+ /**
55
+ * What a face's letters are, from what was typed: no spaces, and no more than two. What a field
56
+ * keeps as somebody types, so a third letter or a space never reaches the face.
57
+ */
58
+ export function faceLetters(typed) {
59
+ return lettersIn(typed.replace(/\s+/gu, "")).slice(0, MOST_FACE_LETTERS).join("");
60
+ }
61
+ /** How wide one letter is, in the font's size: a capital, a CJK character, an emoji. */
62
+ const WIDTHS = { letter: 0.7, wide: 1, emoji: 1.2 };
63
+ /** As wide as a face's letters may stand, in the initials' size: what two capitals take. */
64
+ const ROOM = 1.7;
65
+ /** A single emoji is this much larger than the initials, filling the face as one capital does. */
66
+ const ONE_EMOJI = 1.25;
67
+ const EMOJI = /\p{Extended_Pictographic}|\p{Regional_Indicator}/u;
68
+ const WIDE = /\p{Ideographic}|[\u3040-\u30ff\uac00-\ud7a3]/u;
69
+ function widthOf(letter) {
70
+ if (EMOJI.test(letter))
71
+ return WIDTHS.emoji;
72
+ return WIDE.test(letter) ? WIDTHS.wide : WIDTHS.letter;
73
+ }
74
+ /**
75
+ * How large a face's letters are drawn, against the size its initials are (ADR 0139): 1 for
76
+ * letters, which is the initials' own size; larger for one emoji, which fills a face as one
77
+ * capital does; smaller where two emoji, an emoji and a letter, or two wide characters would not
78
+ * fit inside it. Here, beside the letters' own rule, so the phone and the Mac draw them alike.
79
+ */
80
+ export function lettersScale(letters) {
81
+ const each = lettersIn(letters);
82
+ const only = each[0];
83
+ if (each.length === 1 && only !== undefined && EMOJI.test(only))
84
+ return ONE_EMOJI;
85
+ const width = each.reduce((sum, letter) => sum + widthOf(letter), 0);
86
+ return width <= ROOM ? 1 : Math.round((ROOM / width) * 100) / 100;
87
+ }
88
+ /** Whether these are a face's letters as they are: one or two, nothing round them, nothing hidden. */
89
+ function areFaceLetters(letters) {
90
+ const count = lettersIn(letters).length;
91
+ return (count >= 1 &&
92
+ count <= MOST_FACE_LETTERS &&
93
+ !/[\s\p{Cc}]/u.test(letters) &&
94
+ faceLetters(letters) === letters);
95
+ }
96
+ export const spaceIcon = z.object({
97
+ /**
98
+ * A mark's name in the library — letters only, as every name in it is — or empty, for a face
99
+ * that is a colour and nothing else.
100
+ *
101
+ * A ground is a choice in its own right: a room can be *the teal one* before it is the one
102
+ * with the rocket on it, and down a list of eighty rows the colour is what the eye catches
103
+ * first. Requiring a mark meant the picker's colour strip wrote nothing until a mark had been
104
+ * tapped — the swatch ticked, the row behind did not move, and it read as a broken button
105
+ * (Arjun, 22 September 2026: "it always fails the first time, then after 2-3 tries it starts
106
+ * working"). An empty mark draws the initials on the chosen ground, which is exactly what the
107
+ * picker previews.
108
+ */
109
+ icon: z
110
+ .string()
111
+ .max(40)
112
+ .regex(/^[a-zA-Z]*$/, "A mark's name is letters."),
113
+ ground: z
114
+ .number()
115
+ .int()
116
+ .min(0)
117
+ .max(SPACE_ICON_GROUNDS - 1),
118
+ /**
119
+ * Letters the face wears in place of a mark (ADR 0139): one or two, or an emoji or two, on the
120
+ * ground. With them the mark is empty. Absent, the face is its mark, or the initials on the
121
+ * ground when the mark is empty too. A client older than them draws the initials on the ground.
122
+ */
123
+ letters: z
124
+ .string()
125
+ .min(1)
126
+ .max(32)
127
+ .refine(areFaceLetters, "Up to two letters or emoji, and no spaces.")
128
+ .optional()
129
+ .describe("One or two letters, or an emoji or two, the face wears on its ground in place of a mark (ADR 0139); the mark is empty beside them. Absent on a face that is a mark, or only a colour."),
130
+ });
131
+ /** The face a person sent, or the reason it is not one. */
132
+ export function parseSpaceIcon(raw) {
133
+ const parsed = spaceIcon.safeParse(raw);
134
+ if (parsed.success)
135
+ return { ok: true, value: parsed.data };
136
+ const first = parsed.error.issues[0];
137
+ return {
138
+ ok: false,
139
+ message: first === undefined ? "not a face" : `${first.path.join(".")}: ${first.message}`,
140
+ };
141
+ }
142
+ //# sourceMappingURL=space-icons.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"space-icons.js","sourceRoot":"","sources":["../../src/space-icons.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * What a room's job has spent, and what the room allows a job (ADR 0117).
3
+ *
4
+ * A **job** is the work a room's agents do after the last thing a person did there (a message, an
5
+ * answer, letting the agents carry on: the `PERSON_ACTS` the referee counts from), or after a brief
6
+ * was handed into the room, whichever is later. Its **spend** is what the runs reported costing
7
+ * after that point, of any harness: a run's own report is its total so far, so a run that began
8
+ * before the point counts only what it reported after it. A run that reported no cost counts
9
+ * nothing, and the room says how many did.
10
+ *
11
+ * Every room allows a job a **ceiling**: `DEFAULT_SPEND_CEILING_USD` until the workspace's owner,
12
+ * in the room, sets another (`space.spend_ceiling_set`, ADR 0196), from `SPEND_CEILING_MIN` to
13
+ * `SPEND_CEILING_MAX` in steps of `SPEND_CEILING_STEP`. Chips works the spend out and hands it to
14
+ * every harness with the message that gives a turn (`context.spend`), and to the agent in the
15
+ * room's note (`spendWords`).
16
+ *
17
+ * At `SPEND_WARN_AT` of the ceiling Chips warns the room's harnesses, and at the ceiling it holds
18
+ * the agents until a person acts (ADR 0118): `spendStage` says which.
19
+ */
20
+ import { z } from "zod";
21
+ /** What a room allows a job until a person there says otherwise, in US dollars. */
22
+ export declare const DEFAULT_SPEND_CEILING_USD = 3;
23
+ /** The least a room may allow a job. */
24
+ export declare const SPEND_CEILING_MIN = 0.25;
25
+ /** The most a room may allow a job. */
26
+ export declare const SPEND_CEILING_MAX = 100;
27
+ /** A ceiling is a whole number of these. */
28
+ export declare const SPEND_CEILING_STEP = 0.25;
29
+ /** The share of the ceiling at which Chips warns the room's harnesses (ADR 0118). */
30
+ export declare const SPEND_WARN_AT = 0.8;
31
+ /** Whether `usd` is a ceiling a room may have: in range, and a whole number of steps. */
32
+ export declare function isSpendCeiling(usd: number): boolean;
33
+ /** A ceiling as a room sets it: an amount, or `null` for the default. */
34
+ export declare const spendCeiling: z.ZodNullable<z.ZodNumber>;
35
+ export declare const jobSpend: z.ZodObject<{
36
+ ceiling_usd: z.ZodNumber;
37
+ spent_usd: z.ZodNumber;
38
+ since_seq: z.ZodInt;
39
+ uncounted_runs: z.ZodInt;
40
+ }, z.core.$strip>;
41
+ export type JobSpend = z.infer<typeof jobSpend>;
42
+ /** An amount as the room's words say it: "$3.00", "$0.25". */
43
+ export declare function dollars(usd: number): string;
44
+ /**
45
+ * Where a job stands against its room's ceiling (ADR 0118): `within` it, `most` spent (from
46
+ * `SPEND_WARN_AT`), or at the `ceiling`, where Chips holds the agents until a person acts.
47
+ */
48
+ export type SpendStage = "within" | "most" | "ceiling";
49
+ export declare function spendStage(spend: Pick<JobSpend, "ceiling_usd" | "spent_usd">): SpendStage;
50
+ /**
51
+ * The room note's line about the job's spend (ADR 0117), with what Chips does at the ceiling
52
+ * (ADR 0118): "This job has spent $1.20 of the $3.00 this room allows a job; at $3.00 Chips holds
53
+ * the agents until a person acts." From most of it spent, what to do about it; at the ceiling,
54
+ * that the agents are held. Then how many runs reported no cost, when any did.
55
+ */
56
+ export declare function spendWords(spend: JobSpend): string;
57
+ //# sourceMappingURL=spend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../../src/spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,mFAAmF;AACnF,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,wCAAwC;AACxC,eAAO,MAAM,iBAAiB,OAAO,CAAC;AACtC,uCAAuC;AACvC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,4CAA4C;AAC5C,eAAO,MAAM,kBAAkB,OAAO,CAAC;AACvC,qFAAqF;AACrF,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAGnD;AAED,yEAAyE;AACzE,eAAO,MAAM,YAAY,4BAKZ,CAAC;AAEd,eAAO,MAAM,QAAQ;;;;;iBAyBqE,CAAC;AAC3F,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,QAAQ,CAAC,CAAC;AAEhD,8DAA8D;AAC9D,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3C;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;AAEvD,wBAAgB,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,GAAG,WAAW,CAAC,GAAG,UAAU,CAMzF;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,GAAG,MAAM,CAWlD"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * What a room's job has spent, and what the room allows a job (ADR 0117).
3
+ *
4
+ * A **job** is the work a room's agents do after the last thing a person did there (a message, an
5
+ * answer, letting the agents carry on: the `PERSON_ACTS` the referee counts from), or after a brief
6
+ * was handed into the room, whichever is later. Its **spend** is what the runs reported costing
7
+ * after that point, of any harness: a run's own report is its total so far, so a run that began
8
+ * before the point counts only what it reported after it. A run that reported no cost counts
9
+ * nothing, and the room says how many did.
10
+ *
11
+ * Every room allows a job a **ceiling**: `DEFAULT_SPEND_CEILING_USD` until the workspace's owner,
12
+ * in the room, sets another (`space.spend_ceiling_set`, ADR 0196), from `SPEND_CEILING_MIN` to
13
+ * `SPEND_CEILING_MAX` in steps of `SPEND_CEILING_STEP`. Chips works the spend out and hands it to
14
+ * every harness with the message that gives a turn (`context.spend`), and to the agent in the
15
+ * room's note (`spendWords`).
16
+ *
17
+ * At `SPEND_WARN_AT` of the ceiling Chips warns the room's harnesses, and at the ceiling it holds
18
+ * the agents until a person acts (ADR 0118): `spendStage` says which.
19
+ */
20
+ import { z } from "zod";
21
+ /** What a room allows a job until a person there says otherwise, in US dollars. */
22
+ export const DEFAULT_SPEND_CEILING_USD = 3;
23
+ /** The least a room may allow a job. */
24
+ export const SPEND_CEILING_MIN = 0.25;
25
+ /** The most a room may allow a job. */
26
+ export const SPEND_CEILING_MAX = 100;
27
+ /** A ceiling is a whole number of these. */
28
+ export const SPEND_CEILING_STEP = 0.25;
29
+ /** The share of the ceiling at which Chips warns the room's harnesses (ADR 0118). */
30
+ export const SPEND_WARN_AT = 0.8;
31
+ /** Whether `usd` is a ceiling a room may have: in range, and a whole number of steps. */
32
+ export function isSpendCeiling(usd) {
33
+ if (!Number.isFinite(usd) || usd < SPEND_CEILING_MIN || usd > SPEND_CEILING_MAX)
34
+ return false;
35
+ return Number.isInteger(Math.round(usd * 1e6) / (SPEND_CEILING_STEP * 1e6));
36
+ }
37
+ /** A ceiling as a room sets it: an amount, or `null` for the default. */
38
+ export const spendCeiling = z
39
+ .number()
40
+ .refine(isSpendCeiling, {
41
+ message: `From $${SPEND_CEILING_MIN} to $${SPEND_CEILING_MAX}, in steps of $${SPEND_CEILING_STEP}.`,
42
+ })
43
+ .nullable();
44
+ export const jobSpend = z
45
+ .object({
46
+ ceiling_usd: z
47
+ .number()
48
+ .positive()
49
+ .describe("What the room allows a job, in US dollars: its own ceiling, or the default (3) when nobody has set one."),
50
+ spent_usd: z
51
+ .number()
52
+ .nonnegative()
53
+ .describe("What the room's runs reported costing since the job began, in US dollars, every harness's together."),
54
+ since_seq: z
55
+ .int()
56
+ .nonnegative()
57
+ .describe("Where the job began: the `seq` of the last thing a person did in the room, or of the last brief handed into it, whichever is later; 0 when neither has happened."),
58
+ uncounted_runs: z
59
+ .int()
60
+ .nonnegative()
61
+ .describe("How many of the job's runs reported no cost, and so count nothing."),
62
+ })
63
+ .describe("What a room's job has spent, against what the room allows a job (ADR 0117).");
64
+ /** An amount as the room's words say it: "$3.00", "$0.25". */
65
+ export function dollars(usd) {
66
+ return `$${usd.toFixed(2)}`;
67
+ }
68
+ export function spendStage(spend) {
69
+ // To the cent, as the words say it: a job at $2.999 has spent the $3.00 it was allowed.
70
+ const spent = Math.round(spend.spent_usd * 100);
71
+ const ceiling = Math.round(spend.ceiling_usd * 100);
72
+ if (spent >= ceiling)
73
+ return "ceiling";
74
+ return spent >= Math.round(ceiling * SPEND_WARN_AT) ? "most" : "within";
75
+ }
76
+ /**
77
+ * The room note's line about the job's spend (ADR 0117), with what Chips does at the ceiling
78
+ * (ADR 0118): "This job has spent $1.20 of the $3.00 this room allows a job; at $3.00 Chips holds
79
+ * the agents until a person acts." From most of it spent, what to do about it; at the ceiling,
80
+ * that the agents are held. Then how many runs reported no cost, when any did.
81
+ */
82
+ export function spendWords(spend) {
83
+ const spent = dollars(spend.spent_usd);
84
+ const ceiling = dollars(spend.ceiling_usd);
85
+ const stage = spendStage(spend);
86
+ const line = stage === "ceiling"
87
+ ? `This job has spent ${spent} of the ${ceiling} this space allows a job, so Chips holds the agents until a person acts: start nothing new, and say what is done and what is left.`
88
+ : `This job has spent ${spent} of the ${ceiling} this space allows a job; at ${ceiling} Chips holds the agents until a person acts.${stage === "most" ? " Most of it is spent: finish the step you are on, and say what is left." : ""}`;
89
+ const n = spend.uncounted_runs;
90
+ if (n === 0)
91
+ return line;
92
+ return `${line} ${n === 1 ? "1 run here reported no cost and is" : `${n} runs here reported no cost and are`} not counted.`;
93
+ }
94
+ //# sourceMappingURL=spend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spend.js","sourceRoot":"","sources":["../../src/spend.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Who may see an event (phase-0-review finding 6).
3
+ *
4
+ * Reading the log and subscribing to it are both scoped by membership: a credential sees the
5
+ * spaces it belongs to, and nothing else. Before this, any credential in a workspace
6
+ * could read every space's events, including a direct space it was not in.
7
+ *
8
+ * The rule, in one place so the live path and the replay path cannot drift:
9
+ *
10
+ * - An event that belongs to a space is visible only to that space's members.
11
+ * - An event with no space — a workspace-wide thing — is visible to everyone in the
12
+ * workspace. In Phase 1 nothing appends one in normal flow; the ones that arrive in M1.4 and
13
+ * M3 (a policy change, a budget warning) will want per-recipient scoping, which is theirs to
14
+ * add. What an agent said in no room (`message.held`, ADR 0087) has no space either, and is
15
+ * its harness's alone: its words are for the room it joins next, not for the workspace.
16
+ * - A person asking for a copy of an agent (`agent.fork`, ADR 0087) is visible to the agent's
17
+ * harness, which makes the copy for a room it is not in.
18
+ * - A person asking a harness for a new agent (`agent.asked`, ADR 0108) is visible to that
19
+ * harness, which makes the agent for a room it is not in.
20
+ * - A harness's agent being made, renamed, archived, given a face or deleted (`agent.registered`,
21
+ * `agent.updated`, `agent.deleted`) has no space either, and went to everybody: a guest in no room with the
22
+ * agent, and every other harness, were sent its name and its harness's own key for it (agents
23
+ * review, finding 3). It is its harness's to hear, and anybody's who is told every agent: an
24
+ * owner or admin of the workspace. Since his call of 19 October 2026 (ADR 0034's addendum) that
25
+ * is no longer every person who is not a guest: a member is told the agents in their rooms, as
26
+ * a guest is. Whoever shares a room with the agent, and a person who owns its harness, are told
27
+ * too, which the caller works out, since a room is not something an event with none can say.
28
+ * - The event that adds or removes a participant is always visible to that participant, so a
29
+ * person can see themselves join a space they are not yet listed in.
30
+ * - The event that opens a space is visible to whoever opened it, who is its first
31
+ * member (ADR 0018) but is not yet listed anywhere a live session has read — and to the
32
+ * person it was opened *for*, since ADR 0024, because a harness opens a space for its owner
33
+ * as it finds one and they would otherwise never be sent the event that names it — and to the
34
+ * harness it was asked *of*, when a person opens a room for a harness to claim, which is how
35
+ * the harness hears that there is one before it is added to it.
36
+ * - The event that opens a thread is visible to everyone in the space it grows in (M5.4): a
37
+ * thread has no members of its own, and its events are theirs from then on.
38
+ * - An approval that names the viewer as somebody who may answer it is visible to them
39
+ * wherever it is (M5.5): the lane's lead, handed a low-risk ask by policy, is not in the
40
+ * specialist's room and would otherwise never be sent what it is to decide. Since ADR 0042 a
41
+ * person named from elsewhere in the workspace is in the same position, and the decision and
42
+ * its outcome name them too, so what they were sent is answered on their screen as well.
43
+ * - A message a person deleted for themselves, and their Undo (ADR 0167), are theirs alone,
44
+ * though they belong to a room: nobody else in it, and no harness, is told that one person
45
+ * stopped seeing a line. The one rule that takes something away from a room's members.
46
+ *
47
+ * The replay path builds a SQL predicate from the same member set (see the server's event store)
48
+ * plus the membership events naming the viewer. The join needs no special case — a viewer's set
49
+ * already lists every space they are in — but a removal is from a space they are no longer in,
50
+ * and a client resuming after it left a space elsewhere must still be told.
51
+ *
52
+ * It lives with the protocol so that the server and the conformance fake ask the same function:
53
+ * the fake's own copy had come to send what an agent said in no room to everybody (agents
54
+ * review, finding 37).
55
+ */
56
+ /** An event as the rule needs it: what it is, where, by whom, and what it carries. */
57
+ export interface SeenEvent {
58
+ readonly type: string;
59
+ readonly space_id: string | null;
60
+ readonly actor: {
61
+ readonly id: string;
62
+ };
63
+ readonly payload: Readonly<Record<string, unknown>>;
64
+ }
65
+ export interface Viewer {
66
+ readonly participantId: string;
67
+ /** The spaces this viewer is a member of, resolved from the memberships projection. */
68
+ readonly memberSpaces: ReadonlySet<string>;
69
+ /**
70
+ * Whether the viewer is told who every person in the workspace is: a person who is not a guest
71
+ * (ADR 0034). A guest and a harness are told only whom they share a room with.
72
+ */
73
+ readonly seesDirectory?: boolean | undefined;
74
+ /**
75
+ * Whether the viewer is told every agent in the workspace: an owner or admin, who may move,
76
+ * archive or delete any of them (his call of 19 October 2026, ADR 0034's addendum). Anybody
77
+ * else is told the agents in their rooms, and a harness its own.
78
+ */
79
+ readonly seesAgents?: boolean | undefined;
80
+ /**
81
+ * Who else the viewer is in rooms: a harness's agents (ADR 0087), which it speaks for on one
82
+ * connection. What names one of them names the viewer.
83
+ */
84
+ readonly alsoIs?: ReadonlySet<string> | undefined;
85
+ }
86
+ /** The events that add a participant to a space or remove one. Always visible to that participant. */
87
+ export declare const MEMBERSHIP_EVENTS: ReadonlySet<string>;
88
+ /**
89
+ * Where an agent is (ADR 0087): put in a room or taken out of one. The room is told, as of
90
+ * anything in it, and so is the agent's harness, which has left the room by the time its agent's
91
+ * release is written. Nobody else: a person reads an agent's room from the directory, and an
92
+ * event naming a room a client has never heard of would make it one.
93
+ */
94
+ export declare const WHEREABOUTS_EVENTS: ReadonlySet<string>;
95
+ /**
96
+ * A person asking for a copy of an agent in a room (ADR 0087, Fork). The room is told, as of
97
+ * anything in it, and so is the agent's harness, which makes the copy: the agent it names is in
98
+ * another room, so its harness is not a member of the room the copy is for.
99
+ */
100
+ export declare const FORK_EVENTS: ReadonlySet<string>;
101
+ /**
102
+ * A person asking a harness for a new agent in a room (ADR 0108). The room is told, as of anything
103
+ * in it, and so is the harness the event names, which makes the agent: no agent of its is there
104
+ * yet, and a harness is a member of no room itself.
105
+ */
106
+ export declare const ASK_EVENTS: ReadonlySet<string>;
107
+ /**
108
+ * What an agent said while in no room (ADR 0087, rule 6). It has no space, so the workspace-wide
109
+ * rule would send it to everyone — every person, guests too, and every other harness — before
110
+ * the room it is meant for hears it. Only the agent's own harness is told; a person reads how
111
+ * many wait from the directory (`held`).
112
+ */
113
+ export declare const HELD_EVENTS: ReadonlySet<string>;
114
+ /**
115
+ * A harness's agent made or changed (ADR 0087): who it is, what it is called, whether it is
116
+ * archived, the face it wears. No space, and not everybody's: see the rule above.
117
+ */
118
+ export declare const DIRECTORY_EVENTS: ReadonlySet<string>;
119
+ /**
120
+ * What came of a join request (ADR 0122): who asked, from where, and what a person let in. No
121
+ * space, and not everybody's: a harness and a guest are told nothing of who else is let in. A
122
+ * person who sees the whole directory is, as they see the harness appear in it.
123
+ */
124
+ export declare const JOIN_EVENTS: ReadonlySet<string>;
125
+ /**
126
+ * A person's face chosen (ADR 0139). No space, and a person's to see, not a harness's: a harness
127
+ * draws nobody, and one that does not know the event has no business being sent it. A person who
128
+ * sees the directory is told, as of a join; a guest reads it the next time it reads who is who.
129
+ */
130
+ export declare const FACE_EVENTS: ReadonlySet<string>;
131
+ /**
132
+ * An agent's changes locked or let go (ADR 0199). No space, and a person's to see, not a harness's,
133
+ * as a face is: the lock holds people's hands, and a harness is held by nothing it says. A person
134
+ * told every agent is told; anybody else reads it the next time they read who is who.
135
+ */
136
+ export declare const LOCK_EVENTS: ReadonlySet<string>;
137
+ /**
138
+ * A person deleting a message for themselves, and taking that back (ADR 0167). They belong to the
139
+ * room the message is in, and are sent only to the person who did them, on each of their devices.
140
+ */
141
+ export declare const SELF_EVENTS: ReadonlySet<string>;
142
+ /**
143
+ * Whether a live event should be delivered to a viewer. For `DIRECTORY_EVENTS` a caller that
144
+ * gets `false` asks one thing more, which this cannot: whether the viewer shares a room with the
145
+ * agent the event is about, or is a person who owns its harness.
146
+ */
147
+ export declare function canSee(event: SeenEvent, viewer: Viewer): boolean;
148
+ /**
149
+ * The events that carry who may answer an approval, resolved to ids. The two decisions are
150
+ * here as well since ADR 0042: somebody named outside the room is sent the ask, so they are
151
+ * sent what came of it — otherwise the card on their phone would never cool.
152
+ */
153
+ export declare const APPROVER_EVENTS: ReadonlySet<string>;
154
+ /** True when an approval names the viewer as somebody who may answer it (M5.5). */
155
+ export declare function namesApprover(event: SeenEvent, participantId: string): boolean;
156
+ /** True when this opens a thread in one of the viewer's spaces, which makes it theirs too (M5.4). */
157
+ export declare function opensThreadIn(event: SeenEvent, memberSpaces: ReadonlySet<string>): boolean;
158
+ /**
159
+ * True when this is a space being opened for the viewer: by them, on their behalf, or for them
160
+ * to claim. A harness asked beside another from the + (`also_asked_of`) is asked as much as the
161
+ * first: it was sent nothing until its next `hello`, so it never learnt the room was asked of it,
162
+ * and the first words there went unanswered (agents review, finding 17).
163
+ */
164
+ export declare function opensFor(event: SeenEvent, participantId: string): boolean;
165
+ /**
166
+ * True when a membership event is about this viewer, so they see the moment they are added: the
167
+ * one added or removed, or either side of a replacement (ADR 0087).
168
+ */
169
+ export declare function namesViewer(event: SeenEvent, participantId: string): boolean;
170
+ //# sourceMappingURL=visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../src/visibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,sFAAsF;AACtF,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC3C;;;OAGG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7C;;;;OAIG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,CAAC;CACnD;AAOD,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAIhD,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,WAAW,CAAC,MAAM,CAGjD,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAA2B,CAAC;AAExE;;;;GAIG;AACH,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,MAAM,CAA4B,CAAC;AAExE;;;;;GAKG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAA6B,CAAC;AAE1E;;;GAGG;AACH,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,MAAM,CAI/C,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAA4C,CAAC;AAEzF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAAgC,CAAC;AAE7E;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAA+B,CAAC;AAE5E;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,WAAW,CAAC,MAAM,CAAgD,CAAC;AAkB7F;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAgBhE;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,WAAW,CAAC,MAAM,CAK9C,CAAC;AAEH,mFAAmF;AACnF,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAI9E;AAED,qGAAqG;AACrG,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAI1F;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CASzE;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAO5E"}