@astrosheep/square 0.4.0 → 0.4.2

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 (196) hide show
  1. package/dist/artifact.d.ts +1 -3
  2. package/dist/artifact.js +12 -36
  3. package/dist/attention-presentation.d.ts +2 -22
  4. package/dist/attention-presentation.js +9 -12
  5. package/dist/boundary-presentation.d.ts +3 -14
  6. package/dist/boundary-presentation.js +29 -104
  7. package/dist/card-presentation.d.ts +1 -0
  8. package/dist/card-presentation.js +2 -0
  9. package/dist/catch-decisions.js +1 -7
  10. package/dist/claude-boundary-state.d.ts +20 -0
  11. package/dist/claude-boundary-state.js +84 -0
  12. package/dist/claude-hook.d.ts +2 -0
  13. package/dist/claude-hook.js +231 -0
  14. package/dist/claude-mcp-context.d.ts +1 -1
  15. package/dist/claude-mcp-context.js +12 -3
  16. package/dist/cli/context.js +7 -9
  17. package/dist/cli/observation-commands.d.ts +4 -1
  18. package/dist/cli/observation-commands.js +37 -4
  19. package/dist/cli/registry.js +5 -2
  20. package/dist/codex-address.d.ts +2 -2
  21. package/dist/codex-boundary-state.d.ts +1 -5
  22. package/dist/codex-boundary-state.js +0 -4
  23. package/dist/codex-hook.d.ts +7 -10
  24. package/dist/codex-hook.js +69 -33
  25. package/dist/codex-queue.d.ts +10 -11
  26. package/dist/codex-queue.js +32 -32
  27. package/dist/codex-watch.d.ts +2 -0
  28. package/dist/codex-watch.js +100 -0
  29. package/dist/decisions.js +2 -2
  30. package/dist/delivery-deferral.d.ts +8 -17
  31. package/dist/delivery-deferral.js +0 -28
  32. package/dist/delivery-frame.d.ts +7 -0
  33. package/dist/delivery-frame.js +11 -0
  34. package/dist/delivery-ledger-sqlite.d.ts +97 -0
  35. package/dist/delivery-ledger-sqlite.js +496 -0
  36. package/dist/delivery-ledger.d.ts +196 -0
  37. package/dist/delivery-ledger.js +268 -0
  38. package/dist/delivery-lifetime.d.ts +6 -0
  39. package/dist/delivery-lifetime.js +45 -0
  40. package/dist/harness-claude.js +3 -4
  41. package/dist/harness-links.js +63 -7
  42. package/dist/harness.js +12 -17
  43. package/dist/help.js +4 -1
  44. package/dist/http-delivery.d.ts +5 -9
  45. package/dist/http-delivery.js +20 -77
  46. package/dist/inbox.d.ts +6 -49
  47. package/dist/inbox.js +24 -371
  48. package/dist/index.d.ts +6 -3
  49. package/dist/index.js +4 -1
  50. package/dist/mcp.js +2 -3
  51. package/dist/model.d.ts +7 -47
  52. package/dist/model.js +1 -7
  53. package/dist/notifications.d.ts +24 -20
  54. package/dist/notifications.js +39 -108
  55. package/dist/operation-control.d.ts +4 -0
  56. package/dist/operation-control.js +20 -0
  57. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  58. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  59. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  60. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  61. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  62. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  63. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  64. package/dist/packages/agent-delivery/src/codex.js +41 -4
  65. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  66. package/dist/packages/agent-delivery/src/index.js +1 -1
  67. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  68. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  69. package/dist/paseo-delivery.d.ts +5 -5
  70. package/dist/paseo-delivery.js +19 -97
  71. package/dist/paseo.d.ts +1 -1
  72. package/dist/paseo.js +1 -1
  73. package/dist/plugins/opencode/index.d.ts +5 -3
  74. package/dist/plugins/opencode/index.js +99 -21
  75. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  76. package/dist/plugins/opencode-shared/delivery.js +109 -0
  77. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  78. package/dist/plugins/opencode-v1/index.js +41 -0
  79. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  80. package/dist/plugins/opencode-v1/runtime.js +152 -0
  81. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  82. package/dist/plugins/opencode-v1/transport.js +50 -0
  83. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  84. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  85. package/dist/ports.d.ts +31 -90
  86. package/dist/session-lifecycle.d.ts +8 -7
  87. package/dist/session-lifecycle.js +40 -42
  88. package/dist/sqlite-square.d.ts +14 -23
  89. package/dist/sqlite-square.js +132 -139
  90. package/dist/square-api.js +4 -7
  91. package/dist/square-core.d.ts +0 -1
  92. package/dist/square-domain.d.ts +5 -17
  93. package/dist/square-domain.js +104 -221
  94. package/dist/square-facade.d.ts +18 -15
  95. package/dist/square-post.d.ts +53 -0
  96. package/dist/square-post.js +4 -0
  97. package/dist/square-posting.d.ts +13 -0
  98. package/dist/square-posting.js +174 -0
  99. package/dist/square-producer-sqlite.d.ts +11 -0
  100. package/dist/square-producer-sqlite.js +210 -0
  101. package/dist/square-sqlite.d.ts +15 -25
  102. package/dist/square-sqlite.js +150 -226
  103. package/dist/views.d.ts +2 -2
  104. package/dist/views.js +0 -1
  105. package/dist/wake-sink.d.ts +4 -11
  106. package/dist/wake-sink.js +8 -32
  107. package/dist/wake-transports.d.ts +4 -7
  108. package/dist/wake-transports.js +7 -39
  109. package/examples/symmetric-domains.mjs +29 -249
  110. package/guides/change-waits.md +13 -53
  111. package/guides/claude-native.md +7 -171
  112. package/guides/codex-delivery.md +55 -80
  113. package/guides/delivery-primitives.md +48 -207
  114. package/guides/mcp.md +1 -1
  115. package/guides/operation-cancellation.md +2 -5
  116. package/guides/participant-ownership.md +25 -89
  117. package/guides/pi-native-delivery.md +19 -56
  118. package/guides/pi-tools.md +6 -8
  119. package/guides/square-api.md +113 -615
  120. package/package.json +8 -8
  121. package/plugins/claude/.claude-plugin/plugin.json +2 -3
  122. package/plugins/claude/hooks/hooks.json +11 -1
  123. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  124. package/plugins/codex/hooks/hooks.json +11 -23
  125. package/plugins/opencode/index.ts +91 -23
  126. package/plugins/opencode-shared/delivery.ts +91 -0
  127. package/plugins/opencode-v1/index.ts +36 -0
  128. package/plugins/opencode-v1/runtime.ts +134 -0
  129. package/plugins/opencode-v1/transport.ts +41 -0
  130. package/plugins/paseo/OVERVIEW.md +5 -5
  131. package/plugins/paseo/README.md +7 -6
  132. package/plugins/paseo/client/markdown.tsx +5 -5
  133. package/plugins/paseo/client/square-message.tsx +28 -24
  134. package/plugins/paseo/client/transform.ts +8 -5
  135. package/plugins/paseo/shared/card-presentation.ts +16 -0
  136. package/plugins/paseo/shared/message.ts +6 -11
  137. package/plugins/pi/square-pi.js +156 -417
  138. package/dist/channel-artifact.d.ts +0 -10
  139. package/dist/channel-artifact.js +0 -55
  140. package/dist/channel-delivery.d.ts +0 -11
  141. package/dist/channel-delivery.js +0 -51
  142. package/dist/channel-domain.d.ts +0 -3
  143. package/dist/channel-domain.js +0 -83
  144. package/dist/channel-facade.d.ts +0 -76
  145. package/dist/channel-facade.js +0 -1
  146. package/dist/channel-internal.d.ts +0 -126
  147. package/dist/channel-internal.js +0 -440
  148. package/dist/channel-metadata.d.ts +0 -11
  149. package/dist/channel-metadata.js +0 -1
  150. package/dist/channel-ports.d.ts +0 -241
  151. package/dist/channel-ports.js +0 -1
  152. package/dist/channel-presentation.d.ts +0 -17
  153. package/dist/channel-presentation.js +0 -22
  154. package/dist/channel-sqlite-schema.d.ts +0 -13
  155. package/dist/channel-sqlite-schema.js +0 -81
  156. package/dist/channel-transport.d.ts +0 -62
  157. package/dist/channel-transport.js +0 -1
  158. package/dist/channel.d.ts +0 -84
  159. package/dist/channel.js +0 -60
  160. package/dist/claude-delivery-status.d.ts +0 -13
  161. package/dist/claude-delivery-status.js +0 -46
  162. package/dist/claude-delivery.d.ts +0 -12
  163. package/dist/claude-delivery.js +0 -108
  164. package/dist/claude-mod.d.ts +0 -8
  165. package/dist/claude-mod.js +0 -172
  166. package/dist/claude-transport.d.ts +0 -5
  167. package/dist/claude-transport.js +0 -58
  168. package/dist/cmd/notify-once.d.ts +0 -2
  169. package/dist/cmd/notify-once.js +0 -29
  170. package/dist/delivery-attempts.d.ts +0 -52
  171. package/dist/delivery-attempts.js +0 -73
  172. package/dist/delivery-effects.d.ts +0 -139
  173. package/dist/delivery-effects.js +0 -163
  174. package/dist/delivery-health.d.ts +0 -51
  175. package/dist/delivery-health.js +0 -173
  176. package/dist/delivery-operations.d.ts +0 -22
  177. package/dist/delivery-operations.js +0 -198
  178. package/dist/delivery-primitives.d.ts +0 -5
  179. package/dist/delivery-primitives.js +0 -3
  180. package/dist/delivery.d.ts +0 -56
  181. package/dist/delivery.js +0 -80
  182. package/dist/plugins/opencode/delivery.d.ts +0 -19
  183. package/dist/plugins/opencode/delivery.js +0 -223
  184. package/dist/presentation-operations.d.ts +0 -5
  185. package/dist/presentation-operations.js +0 -86
  186. package/dist/sqlite-channel.d.ts +0 -15
  187. package/dist/sqlite-channel.js +0 -600
  188. package/dist/square-index.d.ts +0 -16
  189. package/dist/square-index.js +0 -106
  190. package/dist/wake-preview.d.ts +0 -7
  191. package/dist/wake-preview.js +0 -15
  192. package/plugins/claude/hooks/register.js +0 -132
  193. package/plugins/claude/types.d.ts +0 -7
  194. package/plugins/opencode/delivery.ts +0 -198
  195. /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
  196. /package/dist/{channel-values.js → square-values.js} +0 -0
@@ -1,600 +0,0 @@
1
- import { validateOwner, nameKey } from './neutral.js';
2
- import { isWakeRouteKind, validateMessageMetadata } from './channel-values.js';
3
- import { validateMetadataMatch, matchingDeferrals, validateDeferralId } from './delivery-deferral.js';
4
- import { claimAttempt, prepareAttempt, settleAttempt } from './delivery-attempts.js';
5
- import { receive, recordConsumed } from './receive.js';
6
- import { formatMessageId, parseMessageId, validateEndpointGeneration } from './message.js';
7
- import { compileSearchPattern } from './search.js';
8
- import { readStorageRevision } from './sqlite-framing.js';
9
- import { encode, key, invalidArtifact, requireFact, integer, finite, string, json, object, shape, decoded, immutable, stableJson, pageRows, pageLimit } from './sqlite-values.js';
10
- function message(row) {
11
- const index = integer(row.msg_index), at = finite(row.at);
12
- const from = decoded(row.from_json, true);
13
- requireFact(row.from_key_json === key(from));
14
- const metadata = row.metadata_json === null ? undefined : validateMessageMetadata(json(row.metadata_json));
15
- return immutable({ index, at, from, body: decoded(row.body_json),
16
- ...(metadata === undefined ? {} : { metadata }),
17
- ...(row.preview === null || row.preview === undefined ? {} : { preview: string(row.preview, true) }) });
18
- }
19
- export function messageAddress(row) {
20
- const recipient = decoded(row.recipient_json, true);
21
- requireFact(row.recipient_key_json === key(recipient), 'message address identity is malformed.');
22
- integer(row.msg_index);
23
- integer(row.recipient_order);
24
- const generation = row.generation_json === null ? undefined : endpointGeneration(decoded(row.generation_json, true));
25
- return immutable({ recipient, ...(generation === undefined ? {} : { generation }) });
26
- }
27
- export function endpointGeneration(value) {
28
- try {
29
- return validateEndpointGeneration(value);
30
- }
31
- catch {
32
- throw invalidArtifact('endpoint generation is malformed.');
33
- }
34
- }
35
- function messageId(value) {
36
- requireFact(typeof value === 'string' && parseMessageId(value) !== undefined, 'message id is malformed.');
37
- return value;
38
- }
39
- function wakeAddress(value) {
40
- const address = shape(value, ['kind', 'address']);
41
- requireFact(isWakeRouteKind(address.kind));
42
- const entries = object(address.address);
43
- requireFact(Object.values(entries).every((item) => typeof item === 'string'));
44
- stableJson(entries);
45
- return immutable({ kind: address.kind, address: entries });
46
- }
47
- function ownerFrom(row, column = 'owner_json') {
48
- const owner = decoded(row[column], true);
49
- validateOwner(owner);
50
- return owner;
51
- }
52
- export function endpoint(row) {
53
- const participant = decoded(row.participant_json, true);
54
- requireFact(row.participant_key_json === key(participant));
55
- const generation = endpointGeneration(decoded(row.generation_json, true));
56
- const parsedWake = row.wake_address_json === null ? undefined : wakeAddress(json(row.wake_address_json));
57
- requireFact(row.attachment === 'attached' || row.attachment === 'detached', 'endpoint attachment is malformed.');
58
- requireFact(row.attachment !== 'detached' || parsedWake === undefined, 'a detached endpoint has a wake address.');
59
- return immutable({ participant, owner: ownerFrom(row), generation, attachment: row.attachment, ...(parsedWake === undefined ? {} : { wakeAddress: parsedWake }) });
60
- }
61
- function nativeCorrelation(value) {
62
- const item = shape(value, ['harness'], ['token', 'messageId', 'payloadHash']);
63
- requireFact(['claude', 'opencode', 'pi'].includes(string(item.harness)));
64
- for (const key of ['token', 'messageId', 'payloadHash'])
65
- if (Object.hasOwn(item, key))
66
- string(item[key], true);
67
- return immutable({ harness: item.harness,
68
- ...(item.token === undefined ? {} : { token: item.token }),
69
- ...(item.messageId === undefined ? {} : { messageId: item.messageId }),
70
- ...(item.payloadHash === undefined ? {} : { payloadHash: item.payloadHash }) });
71
- }
72
- export function deliveryEvidence(row, payload = object(json(row.payload_json))) {
73
- const participant = decoded(row.participant_json, true);
74
- requireFact(row.participant_key_json === key(participant));
75
- const generation = endpointGeneration(decoded(row.generation_json, true));
76
- const id = messageId(formatMessageId(integer(row.msg_index)));
77
- requireFact(['wake', 'presentation'].includes(string(row.kind)) && ['dispatching', 'accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(string(row.outcome)));
78
- requireFact(Number.isSafeInteger(row.attempt_n) && row.attempt_n >= 0);
79
- const nativeObservations = payload.nativeObservations === undefined ? undefined : (() => {
80
- requireFact(Array.isArray(payload.nativeObservations));
81
- return payload.nativeObservations.map((value) => {
82
- const item = shape(value, ['eventId', 'messageId', 'state', 'observedAt'], ['reason']);
83
- requireFact(['held', 'released', 'dropped'].includes(string(item.state)));
84
- return {
85
- eventId: string(item.eventId, true), messageId: string(item.messageId, true),
86
- state: item.state,
87
- ...(Object.hasOwn(item, 'reason') ? { reason: string(item.reason) } : {}), observedAt: finite(item.observedAt),
88
- };
89
- });
90
- })();
91
- const coverage = payload.coverage === undefined ? undefined : (() => { requireFact(payload.coverage === 'full' || payload.coverage === 'clipped'); return payload.coverage; })();
92
- return immutable({
93
- participant, generation, message: id, kind: row.kind,
94
- outcome: row.outcome, attemptN: integer(row.attempt_n), at: finite(row.at),
95
- ...(row.expires_at === null ? {} : { expiresAt: finite(row.expires_at) }),
96
- ...(row.claim_token_json === null ? {} : { claimToken: decoded(row.claim_token_json, true) }),
97
- ...(payload.route === undefined ? {} : { route: wakeAddress(payload.route) }),
98
- ...(payload.signature === undefined ? {} : { signature: string(payload.signature) }),
99
- ...(payload.message === undefined ? {} : { messageText: string(payload.message) }),
100
- ...(payload.diagnostic === undefined ? {} : { diagnostic: payload.diagnostic }),
101
- ...(payload.correlation === undefined ? {} : { correlation: nativeCorrelation(payload.correlation) }),
102
- ...(coverage === undefined ? {} : { coverage }),
103
- ...(payload.description === undefined ? {} : { description: string(payload.description) }),
104
- ...(nativeObservations === undefined ? {} : { nativeObservations }),
105
- });
106
- }
107
- export function deliveryDeferral(row) {
108
- const participant = decoded(row.participant_json, true);
109
- requireFact(row.participant_key_json === key(participant));
110
- const generation = endpointGeneration(decoded(row.generation_json, true));
111
- const id = validateDeferralId(decoded(row.deferral_id_json, true));
112
- const match = validateMetadataMatch(json(row.match_json));
113
- requireFact(encode(match) === row.match_json, 'delivery deferral match encoding is malformed.');
114
- return immutable({ participant, generation, id, match, expiresAt: finite(row.expires_at) });
115
- }
116
- export function receiveClaim(row) {
117
- const owner = decoded(row.owner_key_json, true), namespace = decoded(row.namespace_key_json, true);
118
- const generation = endpointGeneration(decoded(row.generation_json, true));
119
- const suppressed = json(row.suppressed_json);
120
- requireFact(Array.isArray(suppressed) && suppressed.every(value => Number.isSafeInteger(value) && value >= 0), 'receive claim coverage is malformed.');
121
- return immutable({ namespace, id: decoded(row.claim_id_json, true), owner, generation,
122
- selector: json(row.selector_json), suppressed: suppressed,
123
- heartbeatAt: finite(row.heartbeat_at), expiresAt: finite(row.expires_at) });
124
- }
125
- function messageSelection(filter, alias = 'm') {
126
- const terms = [], args = [];
127
- const add = (sql, value) => {
128
- terms.push(sql);
129
- args.push(value);
130
- };
131
- if (filter.afterIndex !== undefined)
132
- add(`${alias}.msg_index > ?`, integer(filter.afterIndex, -1));
133
- if (filter.beforeIndex !== undefined)
134
- add(`${alias}.msg_index < ?`, integer(filter.beforeIndex));
135
- if (filter.throughIndex !== undefined)
136
- add(`${alias}.msg_index <= ?`, integer(filter.throughIndex, -1));
137
- if (filter.afterAt !== undefined)
138
- add(`${alias}.at > ?`, finite(filter.afterAt));
139
- for (const [column, values] of [['msg_index', filter.indexes], [filter.senderMatch === 'exact' ? 'from_json' : 'from_key_json', filter.senders?.map(name => filter.senderMatch === 'exact' ? encode(name) : key(name))]]) {
140
- if (values !== undefined) {
141
- if (column === 'msg_index')
142
- values.forEach(v => integer(v));
143
- add(`${alias}.${column} IN (SELECT value FROM json_each(?))`, encode(values));
144
- }
145
- }
146
- if (filter.metadataMatch !== undefined) {
147
- terms.push(`NOT EXISTS (
148
- SELECT 1 FROM json_each(?) field WHERE NOT EXISTS (
149
- WITH RECURSIVE walk(value,type,depth) AS (
150
- SELECT json_extract(${alias}.metadata_json,'$'),json_type(${alias}.metadata_json,'$'),0
151
- UNION ALL
152
- SELECT child.value,child.type,walk.depth+1
153
- FROM walk JOIN json_each(CASE WHEN walk.type='object' THEN walk.value ELSE '{}' END) child
154
- ON child.key=json_extract(field.value,'$.path['||walk.depth||']')
155
- WHERE walk.depth<json_array_length(field.value,'$.path')
156
- )
157
- SELECT 1 FROM walk WHERE depth=json_array_length(field.value,'$.path')
158
- AND (type=json_type(field.value,'$.equals')
159
- OR type IN ('integer','real') AND json_type(field.value,'$.equals') IN ('integer','real'))
160
- AND value IS json_extract(field.value,'$.equals')
161
- )
162
- )`);
163
- args.push(encode(filter.metadataMatch.fields));
164
- }
165
- if (filter.search !== undefined) {
166
- terms.push(`square_search(?,?,'msg/'||${alias}.msg_index,${alias}.from_json,json_extract(${alias}.body_json,'$'))=1`);
167
- args.push(filter.search.mode, filter.search.text);
168
- }
169
- return { sql: terms.length ? terms.join(' AND ') : '1', args };
170
- }
171
- export function createChannelSqlite(db) {
172
- let changed = false, deliveryChanged = false;
173
- const all = (sql, args = []) => db.prepare(sql).all(...args);
174
- const one = (sql, args = []) => db.prepare(sql).get(...args);
175
- const run = (sql, args = []) => Number(db.prepare(sql).run(...args).changes);
176
- let searchRegistered = false;
177
- function ensureSearch() {
178
- if (searchRegistered)
179
- return;
180
- const patterns = new Map();
181
- db.function('square_search', { deterministic: true }, (mode, text, index, actor, body) => {
182
- const cacheKey = encode([mode, text]);
183
- let pattern = patterns.get(cacheKey);
184
- if (pattern === undefined) {
185
- pattern = compileSearchPattern(string(text), mode === 'literal');
186
- patterns.set(cacheKey, pattern);
187
- }
188
- return Number([string(index), decoded(actor, true), body === null ? '' : string(body)]
189
- .some(field => pattern.test(field)));
190
- });
191
- searchRegistered = true;
192
- }
193
- function selectMessage(filter) {
194
- if (filter.search !== undefined)
195
- ensureSearch();
196
- return messageSelection(filter);
197
- }
198
- const channel = {};
199
- function currentEndpoint(name) {
200
- const r = one('SELECT * FROM endpoints WHERE participant_key_json=?', [key(name)]);
201
- return r ? endpoint(r) : undefined;
202
- }
203
- function owner(requested) {
204
- const current = currentEndpoint(requested);
205
- if (current !== undefined)
206
- return current.participant;
207
- const historical = one('SELECT recipient_json FROM message_addresses WHERE recipient_key_json=? ORDER BY msg_index LIMIT 1', [key(requested)]);
208
- return historical === undefined ? requested : decoded(historical.recipient_json, true);
209
- }
210
- function directedSelection(recipient, filter) {
211
- const endpoint = currentEndpoint(recipient), canonical = endpoint?.participant ?? recipient, base = selectMessage(filter);
212
- base.sql += ' AND ma.recipient_key_json=?';
213
- base.args.push(key(canonical));
214
- if (filter.currentGeneration) {
215
- base.sql += ' AND (ma.generation_json IS NULL OR ma.generation_json=?)';
216
- base.args.push(endpoint === undefined ? encode('') : encode(endpoint.generation));
217
- }
218
- if (filter.unseenOnly) {
219
- base.sql += ' AND NOT EXISTS (SELECT 1 FROM receipts r WHERE r.owner_key_json=? AND r.msg_index=m.msg_index)';
220
- base.args.push(encode(canonical));
221
- }
222
- return base;
223
- }
224
- // ----- Lower Channel capabilities -----
225
- const channelRead = {
226
- header() {
227
- const rows = all('SELECT m.*, (SELECT COALESCE(MAX(msg_index),-1) FROM messages) AS last_msg_index FROM channel_meta m');
228
- requireFact(rows.length === 1 && rows[0].id === 1, 'header row is malformed.');
229
- const r = rows[0], nextMessageIndex = integer(r.next_msg_index), lastMessageIndex = integer(r.last_msg_index, -1);
230
- requireFact(nextMessageIndex > lastMessageIndex, 'nextMessageIndex is behind the message history.');
231
- return immutable({ revision: readStorageRevision(db), deliveryRevision: readStorageRevision(db, true), nextMessageIndex, lastMessageIndex });
232
- },
233
- message(index) { const r = one('SELECT * FROM messages WHERE msg_index=?', [integer(index)]); return r ? message(r) : undefined; },
234
- messages(indexes) {
235
- indexes.forEach(v => integer(v));
236
- return immutable(all('SELECT * FROM messages WHERE msg_index IN (SELECT value FROM json_each(?)) ORDER BY msg_index', [encode(indexes)]).map(message));
237
- },
238
- messageHeads(filter, page) {
239
- const s = selectMessage(filter), limit = pageLimit(page);
240
- return pageRows(all(`SELECT m.* FROM messages m WHERE ${s.sql} ORDER BY m.msg_index ${page.edge === 'newest' ? 'DESC' : 'ASC'} LIMIT ?`, [...s.args, limit]), page, message);
241
- },
242
- messageCount(filter) { const s = selectMessage(filter); return integer(one(`SELECT COUNT(*) AS n FROM messages m WHERE ${s.sql}`, s.args).n); },
243
- addresses(index) {
244
- return immutable(all('SELECT * FROM message_addresses WHERE msg_index=? ORDER BY recipient_order', [integer(index)]).map(messageAddress));
245
- },
246
- receipt(name, index) { const r = one('SELECT at FROM receipts WHERE owner_key_json=? AND msg_index=?', [encode(owner(name)), integer(index)]); return r ? immutable({ at: finite(r.at) }) : undefined; },
247
- receiptFacts(name, afterIndex) {
248
- const k = encode(owner(name));
249
- const r = one('SELECT MAX(at) AS latest FROM receipts WHERE owner_key_json=?', [k]);
250
- const e = one(`SELECT MAX(r.msg_index) AS latest FROM receipts r JOIN messages m ON m.msg_index=r.msg_index WHERE r.owner_key_json=? AND r.msg_index>?`, [k, integer(afterIndex, -1)]);
251
- return immutable({ ...(r.latest === null ? {} : { latestAt: finite(r.latest) }), ...(e.latest === null ? {} : { latestActorMessageIndex: integer(e.latest) }) });
252
- },
253
- directed(recipient, filter, page) {
254
- const base = directedSelection(recipient, filter), limit = pageLimit(page);
255
- const rows = all(`SELECT m.*,ma.recipient_json,ma.generation_json FROM messages m JOIN message_addresses ma ON ma.msg_index=m.msg_index WHERE ${base.sql} ORDER BY m.msg_index ${page.edge === 'newest' ? 'DESC' : 'ASC'} LIMIT ?`, [...base.args, limit]);
256
- return pageRows(rows, page, r => ({ index: integer(r.msg_index), at: finite(r.at), from: decoded(r.from_json, true),
257
- recipient: decoded(r.recipient_json, true), ...(r.generation_json === null ? {} : { generation: endpointGeneration(decoded(r.generation_json, true)) }) }));
258
- },
259
- directedCount(recipient, filter) {
260
- const base = directedSelection(recipient, filter);
261
- return integer(one(`SELECT COUNT(*) AS n FROM messages m JOIN message_addresses ma ON ma.msg_index=m.msg_index WHERE ${base.sql}`, base.args).n);
262
- },
263
- endpoint(name) { return currentEndpoint(name); },
264
- endpointsForOwner(requested) {
265
- validateOwner(requested);
266
- return immutable(all('SELECT * FROM endpoints WHERE owner_json=? ORDER BY participant_key_json', [encode(requested)]).map(endpoint));
267
- },
268
- endpoints() { return immutable(all('SELECT * FROM endpoints ORDER BY participant_key_json').map(endpoint)); },
269
- deliveryEvidence(participant, generation, activity) {
270
- const args = [key(participant), encode(endpointGeneration(generation))];
271
- const filter = activity === undefined ? '' : ' AND msg_index=?';
272
- if (activity !== undefined) {
273
- const resolved = parseMessageId(activity);
274
- requireFact(resolved !== undefined, 'message id is malformed.');
275
- args.push(resolved);
276
- }
277
- return immutable(all(`SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=?${filter} ORDER BY msg_index,kind,attempt_n`, args).map(row => deliveryEvidence(row)));
278
- },
279
- deliveryByToken(claimToken) { const row = one('SELECT * FROM delivery_evidence WHERE claim_token_json=?', [encode(claimToken)]); return row === undefined ? undefined : deliveryEvidence(row); },
280
- cancelledThrough(participant, generation) {
281
- const r = one('SELECT through_index FROM delivery_cancellations WHERE participant_key_json=? AND generation_json=?', [key(participant), encode(endpointGeneration(generation))]);
282
- return r === undefined ? undefined : integer(r.through_index, -1);
283
- },
284
- deliveryDeferrals(name, generation) {
285
- const canonical = owner(name), current = currentEndpoint(canonical);
286
- if (current === undefined || current.generation !== generation)
287
- return [];
288
- return immutable(all('SELECT * FROM delivery_deferrals WHERE participant_key_json=? AND generation_json=? ORDER BY expires_at,deferral_id_json', [key(canonical), encode(generation)]).map(deliveryDeferral));
289
- },
290
- receiveClaims(name, namespace) {
291
- const canonical = owner(name), args = [encode(canonical)];
292
- let where = 'owner_key_json=?';
293
- if (namespace !== undefined) {
294
- where += ' AND namespace_key_json=?';
295
- args.push(encode(namespace));
296
- }
297
- return immutable(all(`SELECT * FROM receive_claims WHERE ${where} ORDER BY namespace_key_json`, args).map(receiveClaim));
298
- },
299
- claimLease(name, namespace) { return channelRead.receiveClaims(name, namespace)[0]; },
300
- suppressed(name, generation, index, now) {
301
- integer(index);
302
- finite(now);
303
- endpointGeneration(generation);
304
- const rows = all('SELECT * FROM receive_claims WHERE owner_key_json=? AND expires_at>?', [encode(owner(name)), now]);
305
- return rows.some(row => {
306
- const claim = receiveClaim(row);
307
- return claim.generation === generation && claim.suppressed.includes(index);
308
- });
309
- },
310
- };
311
- function writeMessage(draft) {
312
- const from = string(draft.from, true), body = string(draft.body);
313
- requireFact(Array.isArray(draft.to), 'message addresses are malformed.');
314
- const next = integer(one('SELECT next_msg_index FROM channel_meta WHERE id=1').next_msg_index);
315
- requireFact(next < Number.MAX_SAFE_INTEGER, 'message index is exhausted.');
316
- const metadata = draft.metadata === undefined ? undefined : validateMessageMetadata(draft.metadata);
317
- if (draft.preview !== undefined)
318
- string(draft.preview, true);
319
- const seen = new Set();
320
- const audience = draft.to.flatMap(address => {
321
- const recipient = string(address.recipient, true);
322
- if (seen.has(nameKey(recipient)))
323
- return [];
324
- seen.add(nameKey(recipient));
325
- return [{ recipient, via: address }];
326
- });
327
- receive({
328
- insertItem: () => {
329
- run('INSERT INTO messages(msg_index,at,from_json,from_key_json,body_json,metadata_json,preview) VALUES(?,?,?,?,?,?,?)', [next, finite(draft.at), encode(from), key(from), encode(body), metadata === undefined ? null : encode(metadata), draft.preview ?? null]);
330
- },
331
- insertAddress: (index, { recipient, via }, order) => {
332
- run('INSERT INTO message_addresses(msg_index,recipient_key_json,recipient_json,recipient_order,generation_json) VALUES(?,?,?,?,?)', [index, key(recipient), encode(recipient), order, via.generation === undefined ? null : encode(endpointGeneration(via.generation))]);
333
- },
334
- }, { item: { key: next, data: draft }, audience });
335
- run('UPDATE channel_meta SET next_msg_index=? WHERE id=1', [next + 1]);
336
- changed = true;
337
- return message(one('SELECT * FROM messages WHERE msg_index=?', [next]));
338
- }
339
- function endpointMatches(current, expected) {
340
- return current !== undefined && current.owner === expected.owner && current.generation === expected.generation;
341
- }
342
- function deleteEndpointClaims(name) {
343
- run('DELETE FROM receive_claims WHERE owner_key_json=?', [encode(name)]);
344
- run('DELETE FROM delivery_deferrals WHERE participant_key_json=?', [key(name)]);
345
- }
346
- const channelWrite = Object.assign({}, channelRead, {
347
- publishMessage: writeMessage,
348
- storeEndpoint(value) {
349
- validateOwner(value.owner);
350
- endpointGeneration(value.generation);
351
- string(value.participant, true);
352
- const existing = currentEndpoint(value.participant);
353
- if (existing !== undefined && stableJson(existing) === stableJson(value))
354
- return false;
355
- run('INSERT INTO endpoints(participant_key_json,participant_json,owner_json,generation_json,wake_address_json,attachment) VALUES(?,?,?,?,?,?) ON CONFLICT(participant_key_json) DO UPDATE SET participant_json=excluded.participant_json,owner_json=excluded.owner_json,generation_json=excluded.generation_json,wake_address_json=excluded.wake_address_json,attachment=excluded.attachment', [key(value.participant), encode(value.participant), encode(value.owner), encode(value.generation), value.wakeAddress === undefined ? null : encode(value.wakeAddress), value.attachment]);
356
- deleteEndpointClaims(value.participant);
357
- deliveryChanged = true;
358
- return true;
359
- },
360
- updateWakeAddress(value, wakeAddress) {
361
- if (wakeAddress !== undefined) {
362
- requireFact(isWakeRouteKind(wakeAddress.kind));
363
- requireFact(Object.values(wakeAddress.address).every((item) => typeof item === 'string'));
364
- }
365
- const current = one('SELECT wake_address_json FROM endpoints WHERE participant_key_json=? AND generation_json=?', [key(value.participant), encode(value.generation)]);
366
- if (stableJson(current.wake_address_json === null ? null : json(current.wake_address_json)) === stableJson(wakeAddress ?? null))
367
- return false;
368
- const count = run('UPDATE endpoints SET wake_address_json=? WHERE participant_key_json=? AND generation_json=?', [wakeAddress === undefined ? null : encode(wakeAddress), key(value.participant), encode(value.generation)]);
369
- if (count)
370
- deliveryChanged = true;
371
- return count > 0;
372
- },
373
- reattachEndpoint(value, address) {
374
- const current = currentEndpoint(value.participant);
375
- if (current?.attachment !== 'detached' || !endpointMatches(current, value))
376
- return false;
377
- run("UPDATE endpoints SET attachment='attached',wake_address_json=? WHERE participant_key_json=? AND generation_json=?", [address === undefined ? null : encode(wakeAddress(address)), key(value.participant), encode(value.generation)]);
378
- deliveryChanged = true;
379
- return true;
380
- },
381
- detachEndpoint(value) {
382
- const count = run("UPDATE endpoints SET attachment='detached',wake_address_json=NULL WHERE participant_key_json=? AND generation_json=? AND owner_json=? AND attachment='attached'", [key(value.participant), encode(value.generation), encode(value.owner)]);
383
- if (!count)
384
- return false;
385
- deleteEndpointClaims(value.participant);
386
- deliveryChanged = true;
387
- return true;
388
- },
389
- removeEndpoint(value) {
390
- const current = currentEndpoint(value.participant);
391
- if (!endpointMatches(current, value))
392
- return false;
393
- run('DELETE FROM endpoints WHERE participant_key_json=? AND generation_json=? AND owner_json=?', [key(value.participant), encode(value.generation), encode(value.owner)]);
394
- deleteEndpointClaims(value.participant);
395
- deliveryChanged = true;
396
- return true;
397
- },
398
- cancelThrough(value, through) {
399
- endpointGeneration(value.generation);
400
- integer(through, -1);
401
- const prior = channelRead.cancelledThrough(value.participant, value.generation) ?? -1;
402
- if (through <= prior)
403
- return false;
404
- run('INSERT INTO delivery_cancellations(participant_key_json,generation_json,through_index) VALUES(?,?,?) ON CONFLICT(participant_key_json,generation_json) DO UPDATE SET through_index=excluded.through_index', [key(value.participant), encode(value.generation), through]);
405
- deliveryChanged = true;
406
- return true;
407
- },
408
- markSeen(name, indexes, at) {
409
- finite(at);
410
- const next = integer(one('SELECT next_msg_index FROM channel_meta WHERE id=1').next_msg_index);
411
- indexes.forEach(index => requireFact(integer(index) < next, 'receipt references an unassigned message index.'));
412
- const canonical = owner(name);
413
- string(canonical, true);
414
- const count = recordConsumed({
415
- insertReceipt: (ref, time) => run('INSERT OR IGNORE INTO receipts(owner_key_json,msg_index,at) VALUES(?,?,?)', [encode(ref.recipient), ref.item, time]) > 0,
416
- }, indexes.map(item => ({ recipient: canonical, item })), at);
417
- if (count)
418
- changed = true;
419
- return count;
420
- },
421
- putDeliveryDeferral(seatRef, id, match, expiresAt, now) {
422
- finite(now);
423
- finite(expiresAt);
424
- const canonicalMatch = validateMetadataMatch(match);
425
- run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
426
- run('INSERT INTO delivery_deferrals(participant_key_json,participant_json,generation_json,deferral_id_json,match_json,expires_at) VALUES(?,?,?,?,?,?)', [key(seatRef.participant), encode(seatRef.participant), encode(seatRef.generation), encode(id), encode(canonicalMatch), expiresAt]);
427
- deliveryChanged = true;
428
- return true;
429
- },
430
- renewDeliveryDeferral(seatRef, id, expiresAt, now) {
431
- finite(now);
432
- finite(expiresAt);
433
- const pruned = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
434
- const count = run('UPDATE delivery_deferrals SET expires_at=? WHERE participant_key_json=? AND generation_json=? AND deferral_id_json=? AND expires_at>?', [expiresAt, key(seatRef.participant), encode(seatRef.generation), encode(id), now]);
435
- if (count || pruned)
436
- deliveryChanged = true;
437
- return count > 0;
438
- },
439
- releaseDeliveryDeferral(seatRef, id, now) {
440
- finite(now);
441
- const pruned = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
442
- const count = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND generation_json=? AND deferral_id_json=? AND expires_at>?', [key(seatRef.participant), encode(seatRef.generation), encode(id), now]);
443
- if (count || pruned)
444
- deliveryChanged = true;
445
- return count > 0;
446
- },
447
- putReceiveClaim(claim) {
448
- string(claim.namespace, true);
449
- string(claim.id, true);
450
- string(claim.owner, true);
451
- endpointGeneration(claim.generation);
452
- finite(claim.heartbeatAt);
453
- finite(claim.expiresAt);
454
- requireFact(claim.expiresAt >= claim.heartbeatAt);
455
- requireFact(Array.isArray(claim.suppressed) && claim.suppressed.every(value => Number.isSafeInteger(value) && value >= 0), 'receive claim coverage is malformed.');
456
- const selector = stableJson(claim.selector), suppressed = stableJson([...new Set(claim.suppressed)].sort((a, b) => a - b));
457
- const previous = one('SELECT * FROM receive_claims WHERE namespace_key_json=? AND owner_key_json=?', [encode(claim.namespace), encode(claim.owner)]);
458
- const prior = previous === undefined ? undefined : receiveClaim(previous);
459
- if (prior !== undefined && prior.id === claim.id && stableJson(prior.selector) === selector
460
- && stableJson(prior.suppressed) === suppressed && prior.generation === claim.generation
461
- && prior.heartbeatAt === claim.heartbeatAt && prior.expiresAt === claim.expiresAt)
462
- return false;
463
- run('INSERT INTO receive_claims(namespace_key_json,owner_key_json,generation_json,claim_id_json,heartbeat_at,expires_at,selector_json,suppressed_json) VALUES(?,?,?,?,?,?,?,?) ON CONFLICT(namespace_key_json,owner_key_json) DO UPDATE SET generation_json=excluded.generation_json,claim_id_json=excluded.claim_id_json,heartbeat_at=excluded.heartbeat_at,expires_at=excluded.expires_at,selector_json=excluded.selector_json,suppressed_json=excluded.suppressed_json', [encode(claim.namespace), encode(claim.owner), encode(claim.generation), encode(claim.id), claim.heartbeatAt, claim.expiresAt, encode(claim.selector), suppressed]);
464
- changed = true;
465
- return true;
466
- },
467
- removeReceiveClaim(ownerName, namespace, id) {
468
- const count = run('DELETE FROM receive_claims WHERE namespace_key_json=? AND owner_key_json=? AND claim_id_json=?', [encode(namespace), encode(ownerName), encode(id)]);
469
- if (count)
470
- changed = true;
471
- return count > 0;
472
- },
473
- });
474
- Object.assign(channel, channelWrite);
475
- const lower = channelRead;
476
- function attempt(row) {
477
- const data = object(json(row.payload_json)), evidence = deliveryEvidence(row, data);
478
- return { purpose: evidence.kind === 'wake' ? 'dispatch' : 'presentation', token: evidence.claimToken ?? '',
479
- ordinal: evidence.attemptN, outcome: evidence.outcome, at: evidence.at, expiresAt: evidence.expiresAt, data };
480
- }
481
- function attemptRows(seat, index, kind, known) {
482
- const scope = [key(seat.participant), encode(seat.generation), index, kind];
483
- let sources = [];
484
- return {
485
- list() { sources = all('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND msg_index=? AND kind=? ORDER BY attempt_n DESC', scope); return sources.map(attempt); },
486
- evidence(value) { const source = sources.find(row => row.attempt_n === value.ordinal); return deliveryEvidence({ ...source, outcome: value.outcome, at: value.at, expires_at: value.expiresAt ?? null }, value.data); },
487
- byToken(token) {
488
- if (known?.token === token)
489
- return known;
490
- const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND msg_index=? AND kind=? AND claim_token_json=?', [...scope, encode(token)]);
491
- return row === undefined ? undefined : attempt(row);
492
- },
493
- insert(value) {
494
- run('INSERT INTO delivery_evidence(participant_key_json,participant_json,generation_json,msg_index,kind,attempt_n,outcome,at,expires_at,claim_token_json,payload_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)', [scope[0], encode(seat.participant), scope[1], index, kind, value.ordinal, value.outcome, value.at, value.expiresAt ?? null, encode(value.token), encode(value.data)]);
495
- deliveryChanged = true;
496
- },
497
- replace(value) {
498
- const count = run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=?,payload_json=? WHERE participant_key_json=? AND generation_json=? AND msg_index=? AND kind=? AND attempt_n=? AND claim_token_json IS ?', [value.outcome, value.at, value.expiresAt ?? null, encode(value.data), ...scope, value.ordinal, value.token === '' ? null : encode(value.token)]);
499
- if (count)
500
- deliveryChanged = true;
501
- },
502
- };
503
- }
504
- const delivery = {
505
- claimDelivery(input) {
506
- requireFact(input.kind === 'wake' || input.kind === 'presentation');
507
- const index = parseMessageId(input.message);
508
- requireFact(index !== undefined, 'message id is malformed.');
509
- finite(input.at);
510
- const leaseMs = integer(input.leaseMs, 1);
511
- string(input.claimToken, true);
512
- if (input.route !== undefined)
513
- wakeAddress(input.route);
514
- const currentSeat = currentEndpoint(input.seat.participant);
515
- if (currentSeat === undefined || currentSeat.attachment !== 'attached' || currentSeat.generation !== input.seat.generation || currentSeat.owner !== input.seat.owner
516
- || lower.directed(input.seat.participant, { afterIndex: index - 1, throughIndex: index, currentGeneration: true }, { edge: 'oldest', limit: 1 }).items.length === 0)
517
- return { status: 'stale' };
518
- const metadata = lower.message(index)?.metadata;
519
- if (matchingDeferrals(lower.deliveryDeferrals(currentSeat.participant, currentSeat.generation), metadata, input.at).length > 0)
520
- return { status: 'deferred' };
521
- const cancelledThrough = lower.cancelledThrough(input.seat.participant, input.seat.generation);
522
- if (cancelledThrough !== undefined && index <= cancelledThrough)
523
- return { status: 'stale' };
524
- const existing = lower.deliveryEvidence(input.seat.participant, input.seat.generation, formatMessageId(index));
525
- if (input.observationOf !== undefined) {
526
- if (input.kind !== 'presentation' || !existing.some(row => row.kind === 'wake'
527
- && row.claimToken === input.observationOf && ['dispatching', 'accepted', 'unknown'].includes(row.outcome)))
528
- return { status: 'stale' };
529
- }
530
- else {
531
- const blocker = existing.find(row => row.kind !== input.kind && row.outcome !== 'failed' && row.outcome !== 'released');
532
- if (blocker !== undefined) {
533
- if (blocker.outcome === 'dispatching' && blocker.expiresAt !== undefined && blocker.expiresAt <= input.at) {
534
- // Normalize abandoned custody to unknown in the same transaction, never steal it.
535
- const rows = attemptRows(input.seat, index, blocker.kind);
536
- const normalized = { ...blocker, outcome: 'unknown', at: input.at, expiresAt: undefined };
537
- rows.replace({ purpose: blocker.kind === 'wake' ? 'dispatch' : 'presentation', token: blocker.claimToken ?? '', ordinal: blocker.attemptN,
538
- outcome: 'unknown', at: input.at, data: {} });
539
- return { status: 'terminal', evidence: normalized };
540
- }
541
- return { status: blocker.outcome === 'dispatching' ? 'busy' : 'terminal', evidence: blocker };
542
- }
543
- }
544
- const rows = attemptRows(input.seat, index, input.kind);
545
- const result = claimAttempt({ ...rows, replace(value) { rows.replace({ ...value, data: { ...value.data, diagnostic: 'interrupted dispatch' } }); } }, { purpose: input.kind === 'wake' ? 'dispatch' : 'presentation', token: input.claimToken, at: input.at, leaseMs, data: input.route === undefined ? {} : { route: input.route } });
546
- if (result.status === 'acquired')
547
- return { status: 'acquired', claimToken: result.token, attemptN: result.ordinal };
548
- const evidence = result.interrupted ? { ...result.evidence, data: { ...result.evidence.data, diagnostic: 'interrupted dispatch' } } : result.evidence;
549
- return { status: result.status, evidence: rows.evidence(evidence) };
550
- },
551
- prepareDelivery(input) {
552
- const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
553
- if (row === undefined || row.outcome !== 'dispatching')
554
- return false;
555
- requireFact(input.coverage === 'full' || input.coverage === 'clipped');
556
- const previous = attempt(row), correlation = nativeCorrelation(input.correlation), payload = previous.data;
557
- if (stableJson(payload.correlation ?? null) === stableJson(correlation) && payload.coverage === input.coverage
558
- && stableJson(payload.description ?? null) === stableJson(input.description ?? null))
559
- return true;
560
- return prepareAttempt(attemptRows(input.seat, integer(row.msg_index), row.kind, previous), { token: input.claimToken, data: { ...payload, correlation, coverage: input.coverage, ...(input.description === undefined ? {} : { description: input.description }) } });
561
- },
562
- completeDelivery(input) {
563
- const index = parseMessageId(input.message);
564
- requireFact(index !== undefined, 'message id is malformed.');
565
- finite(input.at);
566
- requireFact(['accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(input.outcome));
567
- string(input.claimToken, true);
568
- const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND msg_index=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), index, encode(input.claimToken)]);
569
- if (row === undefined)
570
- return false;
571
- const previous = attempt(row);
572
- const payload = { ...previous.data, ...(input.signature === undefined ? {} : { signature: input.signature }), ...(input.messageText === undefined ? {} : { message: input.messageText }), ...(input.diagnostic === undefined ? {} : { diagnostic: input.diagnostic }), ...(input.correlation === undefined ? {} : { correlation: input.correlation }) };
573
- return settleAttempt(attemptRows(input.seat, index, row.kind, previous), { token: input.claimToken, outcome: input.outcome, at: input.at, data: payload });
574
- },
575
- observeNativeDelivery(input) {
576
- const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
577
- if (row === undefined || !['dispatching', 'accepted', 'unknown'].includes(string(row.outcome)))
578
- return false;
579
- const observation = input.observation;
580
- requireFact(typeof observation.eventId === 'string' && observation.eventId.length > 0);
581
- requireFact(typeof observation.messageId === 'string' && observation.messageId.length > 0);
582
- requireFact(observation.state === 'held' || observation.state === 'released' || observation.state === 'dropped');
583
- finite(observation.observedAt);
584
- if (observation.reason !== undefined)
585
- string(observation.reason);
586
- const payload = object(json(row.payload_json));
587
- const observations = payload.nativeObservations === undefined ? [] : payload.nativeObservations;
588
- requireFact(Array.isArray(observations));
589
- if (observations.some((item) => object(item).eventId === observation.eventId))
590
- return true;
591
- const next = [...observations, observation];
592
- const count = run('UPDATE delivery_evidence SET payload_json=? WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [encode({ ...payload, nativeObservations: next }), key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
593
- if (count)
594
- deliveryChanged = true;
595
- return count > 0;
596
- },
597
- };
598
- Object.assign(channel, delivery);
599
- return Object.defineProperties(channel, { changed: { get: () => changed }, deliveryChanged: { get: () => deliveryChanged } });
600
- }
@@ -1,16 +0,0 @@
1
- import { type VersionObserver } from './file-changes.js';
2
- import { type Owner } from './neutral.js';
3
- /** A rebuildable discovery hint: the owner that may have used an artifact and its path. */
4
- export interface SquareIndexEntry {
5
- readonly location: string;
6
- readonly owner: Owner;
7
- }
8
- export interface SquareIndexQuery {
9
- readonly owner?: Owner;
10
- }
11
- export declare function squareIndexPath(env?: NodeJS.ProcessEnv): string;
12
- export declare function indexedSquareEntries(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly SquareIndexEntry[]>;
13
- export declare function indexedSquarePaths(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly string[]>;
14
- /** Best-effort discovery only: a busy index skips publication, never queues or delays artifact work. */
15
- export declare function rememberSquarePath(location: string, owner: Owner, index: string): Promise<boolean>;
16
- export declare function observeSquareIndex(env?: NodeJS.ProcessEnv): Promise<VersionObserver<string>>;