@astrosheep/square 0.3.106 → 0.4.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 (206) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/artifact-store.d.ts +41 -0
  3. package/dist/artifact-store.js +272 -0
  4. package/dist/artifact.d.ts +6 -12
  5. package/dist/artifact.js +43 -344
  6. package/dist/attention-presentation.d.ts +13 -7
  7. package/dist/attention-presentation.js +29 -68
  8. package/dist/boundary-presentation.d.ts +3 -16
  9. package/dist/boundary-presentation.js +29 -109
  10. package/dist/card-presentation.d.ts +1 -0
  11. package/dist/card-presentation.js +2 -0
  12. package/dist/catch-decisions.d.ts +2 -2
  13. package/dist/catch-decisions.js +7 -3
  14. package/dist/catch-selection.d.ts +8 -0
  15. package/dist/catch-selection.js +5 -0
  16. package/dist/claude-delivery.d.ts +14 -12
  17. package/dist/claude-delivery.js +42 -142
  18. package/dist/claude-mcp-context.js +2 -2
  19. package/dist/claude-mod.js +28 -140
  20. package/dist/claude-transport.d.ts +5 -0
  21. package/dist/claude-transport.js +34 -0
  22. package/dist/cli/context.d.ts +2 -2
  23. package/dist/cli/context.js +7 -10
  24. package/dist/cli/maintenance-commands.d.ts +0 -4
  25. package/dist/cli/maintenance-commands.js +2 -29
  26. package/dist/cli/observation-commands.d.ts +2 -0
  27. package/dist/cli/observation-commands.js +12 -2
  28. package/dist/cli/program.js +1 -1
  29. package/dist/cli/registry.js +4 -3
  30. package/dist/codex-address.d.ts +2 -2
  31. package/dist/codex-boundary-state.d.ts +1 -5
  32. package/dist/codex-boundary-state.js +0 -4
  33. package/dist/codex-hook.d.ts +7 -10
  34. package/dist/codex-hook.js +69 -33
  35. package/dist/codex-queue.d.ts +10 -11
  36. package/dist/codex-queue.js +32 -34
  37. package/dist/codex-watch.d.ts +2 -0
  38. package/dist/codex-watch.js +100 -0
  39. package/dist/decisions.js +3 -3
  40. package/dist/delivery-deferral.d.ts +1 -19
  41. package/dist/delivery-deferral.js +1 -29
  42. package/dist/delivery-frame.d.ts +7 -0
  43. package/dist/delivery-frame.js +11 -0
  44. package/dist/delivery-ledger-sqlite.d.ts +97 -0
  45. package/dist/delivery-ledger-sqlite.js +496 -0
  46. package/dist/delivery-ledger.d.ts +196 -0
  47. package/dist/delivery-ledger.js +268 -0
  48. package/dist/delivery-lifetime.d.ts +6 -0
  49. package/dist/delivery-lifetime.js +45 -0
  50. package/dist/harness-links.js +61 -7
  51. package/dist/harness.js +12 -17
  52. package/dist/help.js +3 -6
  53. package/dist/http-delivery.d.ts +5 -9
  54. package/dist/http-delivery.js +20 -79
  55. package/dist/inbox.d.ts +6 -57
  56. package/dist/inbox.js +24 -380
  57. package/dist/index.d.ts +7 -4
  58. package/dist/index.js +5 -2
  59. package/dist/mcp.d.ts +2 -1
  60. package/dist/mcp.js +40 -70
  61. package/dist/message.d.ts +7 -0
  62. package/dist/message.js +23 -0
  63. package/dist/model.d.ts +15 -91
  64. package/dist/model.js +2 -140
  65. package/dist/neutral.d.ts +72 -0
  66. package/dist/neutral.js +27 -0
  67. package/dist/notifications.d.ts +24 -26
  68. package/dist/notifications.js +39 -143
  69. package/dist/operation-control.d.ts +4 -0
  70. package/dist/operation-control.js +20 -0
  71. package/dist/operation-presentation.d.ts +1 -2
  72. package/dist/operation-presentation.js +1 -7
  73. package/dist/owner.d.ts +2 -2
  74. package/dist/owner.js +1 -1
  75. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  76. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  77. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  78. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  79. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  80. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  81. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  82. package/dist/packages/agent-delivery/src/codex.js +41 -4
  83. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  84. package/dist/packages/agent-delivery/src/index.js +1 -1
  85. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  86. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  87. package/dist/paseo-delivery.d.ts +5 -5
  88. package/dist/paseo-delivery.js +19 -99
  89. package/dist/paseo.d.ts +1 -1
  90. package/dist/paseo.js +1 -1
  91. package/dist/plugins/opencode/index.d.ts +5 -3
  92. package/dist/plugins/opencode/index.js +99 -21
  93. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  94. package/dist/plugins/opencode-shared/delivery.js +109 -0
  95. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  96. package/dist/plugins/opencode-v1/index.js +41 -0
  97. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  98. package/dist/plugins/opencode-v1/runtime.js +152 -0
  99. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  100. package/dist/plugins/opencode-v1/transport.js +50 -0
  101. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  102. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  103. package/dist/ports.d.ts +33 -214
  104. package/dist/presentation.d.ts +5 -5
  105. package/dist/presentation.js +1 -0
  106. package/dist/runtime.js +2 -2
  107. package/dist/search.js +1 -1
  108. package/dist/session-lifecycle.d.ts +8 -7
  109. package/dist/session-lifecycle.js +40 -42
  110. package/dist/sqlite-framing.d.ts +8 -0
  111. package/dist/sqlite-framing.js +31 -0
  112. package/dist/sqlite-square.d.ts +16 -26
  113. package/dist/sqlite-square.js +199 -262
  114. package/dist/sqlite-values.d.ts +21 -0
  115. package/dist/sqlite-values.js +78 -0
  116. package/dist/square-api.d.ts +1 -1
  117. package/dist/square-api.js +5 -8
  118. package/dist/square-core.d.ts +0 -1
  119. package/dist/square-domain.d.ts +12 -10
  120. package/dist/square-domain.js +105 -223
  121. package/dist/square-facade.d.ts +25 -76
  122. package/dist/square-location.d.ts +6 -1
  123. package/dist/square-location.js +1 -1
  124. package/dist/square-post.d.ts +53 -0
  125. package/dist/square-post.js +4 -0
  126. package/dist/square-posting.d.ts +13 -0
  127. package/dist/square-posting.js +174 -0
  128. package/dist/square-producer-sqlite.d.ts +11 -0
  129. package/dist/square-producer-sqlite.js +210 -0
  130. package/dist/square-receive.d.ts +2 -3
  131. package/dist/square-sqlite.d.ts +33 -42
  132. package/dist/square-sqlite.js +236 -768
  133. package/dist/square-tools.d.ts +8 -0
  134. package/dist/square-tools.js +39 -0
  135. package/dist/square-values.d.ts +27 -0
  136. package/dist/square-values.js +90 -0
  137. package/dist/views.d.ts +6 -6
  138. package/dist/views.js +14 -9
  139. package/dist/wake-sink.d.ts +4 -11
  140. package/dist/wake-sink.js +8 -32
  141. package/dist/wake-transports.d.ts +4 -0
  142. package/dist/wake-transports.js +11 -0
  143. package/examples/symmetric-domains.mjs +29 -249
  144. package/guides/change-waits.md +13 -53
  145. package/guides/claude-native.md +19 -172
  146. package/guides/codex-delivery.md +55 -78
  147. package/guides/delivery-primitives.md +48 -207
  148. package/guides/operation-cancellation.md +2 -5
  149. package/guides/participant-ownership.md +25 -89
  150. package/guides/pi-native-delivery.md +19 -56
  151. package/guides/pi-tools.md +6 -8
  152. package/guides/row-runtime-delivery-evidence.md +2 -0
  153. package/guides/row-storage-design.md +19 -44
  154. package/guides/square-api.md +113 -538
  155. package/package.json +8 -8
  156. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  157. package/plugins/claude/hooks/hooks.json +1 -1
  158. package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
  159. package/plugins/claude/types.d.ts +1 -1
  160. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  161. package/plugins/codex/hooks/hooks.json +11 -23
  162. package/plugins/opencode/index.ts +91 -23
  163. package/plugins/opencode-shared/delivery.ts +91 -0
  164. package/plugins/opencode-v1/index.ts +36 -0
  165. package/plugins/opencode-v1/runtime.ts +134 -0
  166. package/plugins/opencode-v1/transport.ts +41 -0
  167. package/plugins/paseo/OVERVIEW.md +5 -5
  168. package/plugins/paseo/README.md +7 -6
  169. package/plugins/paseo/client/markdown.tsx +5 -5
  170. package/plugins/paseo/client/square-message.tsx +28 -24
  171. package/plugins/paseo/client/transform.ts +8 -5
  172. package/plugins/paseo/shared/card-presentation.ts +16 -0
  173. package/plugins/paseo/shared/message.ts +6 -11
  174. package/plugins/pi/square-pi.js +167 -451
  175. package/dist/channel.d.ts +0 -74
  176. package/dist/channel.js +0 -92
  177. package/dist/claude-delivery-status.d.ts +0 -13
  178. package/dist/claude-delivery-status.js +0 -46
  179. package/dist/cmd/notify-once.d.ts +0 -2
  180. package/dist/cmd/notify-once.js +0 -29
  181. package/dist/delivery-attempts.d.ts +0 -52
  182. package/dist/delivery-attempts.js +0 -73
  183. package/dist/delivery-effects.d.ts +0 -139
  184. package/dist/delivery-effects.js +0 -163
  185. package/dist/delivery-health.d.ts +0 -51
  186. package/dist/delivery-health.js +0 -173
  187. package/dist/delivery-operations.d.ts +0 -9
  188. package/dist/delivery-operations.js +0 -168
  189. package/dist/delivery-primitives.d.ts +0 -5
  190. package/dist/delivery-primitives.js +0 -3
  191. package/dist/delivery.d.ts +0 -45
  192. package/dist/delivery.js +0 -106
  193. package/dist/plugins/opencode/delivery.d.ts +0 -19
  194. package/dist/plugins/opencode/delivery.js +0 -238
  195. package/dist/presentation-operations.d.ts +0 -18
  196. package/dist/presentation-operations.js +0 -86
  197. package/dist/square-index.d.ts +0 -16
  198. package/dist/square-index.js +0 -106
  199. package/dist/square-legacy-framing.d.ts +0 -11
  200. package/dist/square-legacy-framing.js +0 -64
  201. package/dist/square-migration-host-ledger.d.ts +0 -26
  202. package/dist/square-migration-host-ledger.js +0 -169
  203. package/dist/square-migration.d.ts +0 -30
  204. package/dist/square-migration.js +0 -599
  205. package/guides/migration.md +0 -156
  206. package/plugins/opencode/delivery.ts +0 -211
package/dist/artifact.js CHANGED
@@ -1,15 +1,13 @@
1
- import { DatabaseSync } from 'node:sqlite';
2
- import { retrySqliteBusy, inSqliteTransaction } from './sqlite-transaction.js';
3
- import { randomUUID } from 'node:crypto';
4
- import fs from 'node:fs';
5
1
  import path from 'node:path';
6
- import { pathToFileURL } from 'node:url';
7
- import { setTimeout as sleep } from 'node:timers/promises';
8
- import { observeFileVersion } from './file-changes.js';
2
+ import fs from 'node:fs';
9
3
  import { canonicalPath } from './canonical-path.js';
10
4
  import { InternalSquareError, SquareError } from './model.js';
11
5
  import { wrapSquareWrite } from './square-domain.js';
12
- import { APPLICATION_ID, CURRENT_USER_VERSION, createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, invalidArtifact, validateSqliteHeader, emptyRuntimeState } from './square-sqlite.js';
6
+ import { createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, emptyRuntimeState, squareStoreFormat } from './square-sqlite.js';
7
+ import { resetStorageMeta } from './sqlite-framing.js';
8
+ import { createArtifactStore, createStoreFile, replaceStoreFile, inspectStore, observeStoreRevision } from './artifact-store.js';
9
+ export { openExistingSquareDatabase } from './artifact-store.js';
10
+ const format = squareStoreFormat(resetStorageMeta);
13
11
  /** Explicit-lifetime holder of one reused artifact connection. Request cancellation
14
12
  * never becomes the shared connection's lifetime; close drains the owner once. */
15
13
  export function createSquareResourceOwner() {
@@ -53,26 +51,26 @@ function normalizedLines(value) {
53
51
  return normalized === '' ? [] : normalized.split('\n');
54
52
  }
55
53
  /** All build paths share configuration validation and lazy guide preparation. */
56
- export function prepareSquareBuild(options, snippet) {
54
+ export function prepareSquareBuild(options, snippet, includeGuides = true) {
57
55
  if (options.hardCap !== undefined && options.hardCap !== null
58
56
  && (!Number.isSafeInteger(options.hardCap) || options.hardCap < 1))
59
57
  throw new SquareError('invalid_args', 'hardCap must be a positive integer or null');
60
58
  if (options.throttlePerMinute !== undefined
61
59
  && (!Number.isSafeInteger(options.throttlePerMinute) || options.throttlePerMinute < 1))
62
60
  throw new SquareError('invalid_args', 'throttlePerMinute must be a positive integer');
63
- const guides = [readGuide('participant')];
64
- if (options.template !== undefined)
61
+ const guides = includeGuides ? [readGuide('participant')] : [];
62
+ if (includeGuides && options.template !== undefined)
65
63
  guides.push(readGuide(options.template));
66
64
  return {
67
65
  hardCap: options.hardCap ?? null,
68
66
  ...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
69
- preamble: normalizedLines(snippet), warmup: normalizedLines(guides.join('\n\n')),
67
+ preamble: normalizedLines(snippet), warmup: includeGuides ? normalizedLines(guides.join('\n\n')) : [],
70
68
  acts: [], runtime: emptyRuntimeState(),
71
69
  };
72
70
  }
73
71
  /** File build owns publication and CLI conflict semantics, not host resources. */
74
- export async function createSquare(location, options, snippet, signal) {
75
- const state = prepareSquareBuild(options, snippet);
72
+ export async function createSquare(location, options, snippet, signal, includeGuides = true) {
73
+ const state = prepareSquareBuild(options, snippet, includeGuides);
76
74
  if (options.force) {
77
75
  await writeSquareFile(location, state, signal);
78
76
  }
@@ -81,359 +79,60 @@ export async function createSquare(location, options, snippet, signal) {
81
79
  }
82
80
  return { hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }) };
83
81
  }
84
- function requireSquareExtension(location) {
85
- if (!location.endsWith('.square'))
86
- throw new SquareError('invalid_args', `Square artifacts must use the .square extension: ${location}`);
87
- }
88
- function storageError(error, location) {
89
- if (!(error instanceof Error) || error instanceof SquareError || error instanceof InternalSquareError)
90
- return error;
91
- if (/unable to open database file|no such file|SQLITE_CANTOPEN/i.test(error.message))
92
- return new InternalSquareError('not_found', `square file not found: ${location}`);
93
- if (/not a database|SQLITE_NOTADB/i.test(error.message))
94
- return invalidArtifact('not a SQLite database.');
95
- return error;
96
- }
97
- /** Private artifact-boundary connection shared with explicit migration. Never creates a path. */
98
- export function openExistingSquareDatabase(location) {
99
- requireSquareExtension(location);
100
- const uri = pathToFileURL(path.resolve(location));
101
- uri.searchParams.set('mode', 'rw');
102
- try {
103
- return new DatabaseSync(uri.href);
104
- }
105
- catch (error) {
106
- throw storageError(error, location);
107
- }
108
- }
109
- function abort(signal) {
110
- if (signal?.aborted)
111
- throw signal.reason ?? new Error('Square artifact operation aborted');
112
- }
113
- function synchronous(value) {
114
- if (value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function') {
115
- void Promise.resolve(value).catch(() => undefined);
116
- throw new TypeError('Square artifact callbacks must be synchronous.');
117
- }
118
- }
119
82
  const readMethods = [
120
83
  'header', 'participant', 'roster', 'attention', 'incomingAttention',
121
84
  'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
122
- 'audience', 'directed', 'directedAt', 'directedCount', 'pendingAudience',
85
+ 'audience', 'directed', 'directedAt', 'directedCount',
123
86
  'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
124
- 'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
87
+ 'pendingPosts', 'postedPosts', 'ownsPost',
125
88
  ];
126
- const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
127
- /** One connection, ordered operations, guarded synchronous transaction capabilities. */
89
+ const writeMethods = ['applyLanding', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'markSeen', 'setSeatAttachment', 'insertNotice', 'ackMatching', 'acquirePost', 'settlePost', 'foldReceipt'];
90
+ /** Square social state and producer state share one synchronous artifact transaction. */
128
91
  export function createArtifactPort(location, initial, externalSignal) {
129
- let database;
130
- let closed = false, inCallback = false;
131
- let tail = Promise.resolve();
132
- const cancel = new AbortController();
133
- const canonical = initial === undefined ? canonicalPath(location) : Promise.resolve(location);
134
- if (initial !== undefined) {
135
- database = new DatabaseSync(':memory:');
136
- try {
137
- initializeSqlite(database, initial);
138
- database.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
139
- }
140
- catch (error) {
141
- database.close();
142
- throw error;
143
- }
144
- }
145
- const combined = (signal) => AbortSignal.any([cancel.signal, ...(externalSignal ? [externalSignal] : []), ...(signal ? [signal] : [])]);
146
- const validate = (db) => validateSqliteHeader(db, initial === undefined ? location : ':memory:');
147
- async function connection(signal) {
148
- if (database)
149
- return database;
150
- location = await canonical;
151
- abort(signal);
152
- database = openExistingSquareDatabase(location);
153
- try {
154
- await retrySqliteBusy(() => validate(database), signal);
155
- database.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
156
- }
157
- catch (error) {
158
- database.close();
159
- database = undefined;
160
- throw storageError(error, location);
161
- }
162
- return database;
163
- }
164
- function queue(operation, operationSignal) {
165
- if (inCallback)
166
- throw new TypeError('Square artifact operations cannot nest inside a callback.');
167
- if (closed)
168
- return Promise.reject(new Error('Square artifact is closed'));
169
- const result = tail.then(() => {
170
- abort(operationSignal);
171
- return operation(operationSignal);
172
- });
173
- tail = result.then(() => undefined, () => undefined);
174
- return result;
175
- }
176
- async function transaction(callback, write, signal, authority) {
177
- const db = await connection(signal);
178
- return inSqliteTransaction(db, write, () => validate(db), (header) => {
179
- const driver = createSqliteDriver(db), version = header.revision;
180
- let active = true, failed = false, failure;
181
- function guard(fn) {
182
- return ((...args) => {
183
- if (!active)
184
- throw new Error('Square artifact transaction capability expired');
185
- abort(signal);
186
- if (failed)
187
- throw failure;
188
- try {
189
- return fn(...args);
190
- }
191
- catch (error) {
192
- failed = true;
193
- failure = storageError(error, location);
194
- throw failure;
195
- }
196
- });
197
- }
92
+ const store = createArtifactStore(location, format, initial === undefined ? undefined : db => initializeSqlite(db, initial, 0), externalSignal);
93
+ function transaction(select, write, authority, signal, settlement = false) {
94
+ return store.run(write, ({ db, guard, track }) => {
95
+ const driver = createSqliteDriver(db);
96
+ track(driver);
198
97
  const reads = Object.fromEntries(readMethods.map(name => [name, guard(driver[name])]));
199
98
  const writes = Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])]));
200
99
  const gate = write ? wrapSquareWrite(reads, writes, authority) : undefined;
201
- const facts = gate?.facts ?? reads;
202
- let value;
203
- inCallback = true;
204
- try {
205
- value = callback(facts);
206
- synchronous(value);
207
- if (!failed)
208
- gate?.complete();
209
- }
210
- finally {
211
- inCallback = false;
212
- active = false;
213
- }
214
- if (failed)
215
- throw failure;
216
- const changed = write && driver.changed;
217
- const deliveryChanged = write && driver.deliveryChanged;
218
- if (changed || deliveryChanged) {
219
- if (version === Number.MAX_SAFE_INTEGER)
220
- throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
221
- db.prepare('UPDATE square_meta SET revision=revision+?, delivery_revision=delivery_revision+? WHERE id=1').run(Number(changed), Number(deliveryChanged));
222
- }
223
- return {
224
- value, version: version + Number(changed), changed, deliveryChanged
225
- };
226
- }, signal);
227
- }
228
- const revision = signal => queue(async (s) => {
229
- const db = await connection(s);
230
- return inSqliteTransaction(db, false, () => validate(db), (header) => header.revision, s);
231
- }, combined(signal));
232
- return {
233
- read: (select, signal) => queue(async (s) => {
234
- const { value, version } = await transaction(select, false, s);
235
- return { value, version };
236
- }, combined(signal)),
237
- transactAs: (authority, change, signal) => queue(s => transaction(change, true, s, authority), combined(signal)),
238
- // Keep the acquired connection and authority, not the cancelled work lifetime. A
239
- // blocked evidence commit/cleanup gets five seconds, and close still cancels it.
240
- settleAs: (authority, change) => queue(s => transaction(change, true, s, authority), AbortSignal.any([cancel.signal, AbortSignal.timeout(5000)])),
241
- revision,
242
- async deliveryRevision(signal) {
243
- return queue(async (s) => {
244
- const db = await connection(s);
245
- return inSqliteTransaction(db, false, () => validate(db), (header) => header.deliveryRevision, s);
246
- }, combined(signal));
247
- },
248
- async claimDelivery(input, signal) {
249
- return (await this.transactAs({ kind: 'participant', seat: input.seat }, (facts) => facts.claimDelivery(input), signal)).value;
250
- },
251
- async prepareDelivery(input, signal) {
252
- return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.prepareDelivery(input), signal)).value;
253
- },
254
- async completeDelivery(input, signal) {
255
- return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.completeDelivery(input), signal)).value;
256
- },
257
- async cancelDeliveries(seat, through, signal) {
258
- return (await this.transactAs({ kind: 'participant', seat }, facts => {
259
- const boundary = through ?? facts.header().lastActIndex;
260
- return boundary < 0 ? false : facts.cancelThrough(seat, boundary);
261
- }, signal)).value;
262
- },
263
- async changed(since, timeout, signal) {
264
- if (inCallback)
265
- throw new TypeError('Square artifact operations cannot nest inside a callback.');
266
- if (closed)
267
- return false;
268
- const s = combined(signal), deadline = Date.now() + Math.max(0, timeout);
269
- if (initial !== undefined) {
270
- try {
271
- do {
272
- if (await revision(s) > since)
273
- return true;
274
- if (Date.now() >= deadline)
275
- return false;
276
- await sleep(Math.min(25, deadline - Date.now()), undefined, { signal: s });
277
- } while (!closed);
278
- }
279
- catch (error) {
280
- if (closed)
281
- return false;
282
- abort(s);
283
- throw error;
284
- }
285
- return false;
286
- }
287
- const observer = await observeSquareRevision(await canonical);
288
- try {
289
- return await observer.changed(since, Math.max(0, deadline - Date.now()), s);
290
- }
291
- catch (error) {
292
- if (closed)
293
- return false;
294
- throw error;
295
- }
296
- finally {
297
- observer.close();
298
- }
299
- },
300
- async close() {
301
- if (inCallback)
302
- throw new TypeError('Square artifact cannot close inside a callback.');
303
- if (closed) {
304
- await tail;
305
- return;
306
- }
307
- closed = true;
308
- cancel.abort(new Error('Square artifact is closed'));
309
- await tail;
310
- database?.close();
311
- database = undefined;
312
- },
100
+ const value = select(gate?.facts ?? reads);
101
+ gate?.complete();
102
+ return value;
103
+ }, signal, settlement);
104
+ }
105
+ const port = {
106
+ read: (select, signal) => transaction(select, false, undefined, signal),
107
+ transactAs: (authority, change, signal) => transaction(change, true, authority, signal),
108
+ settleAs: (authority, change) => transaction(change, true, authority, undefined, true),
109
+ revision: store.revision, deliveryRevision: store.deliveryRevision,
110
+ changed: store.changed, close: store.close,
313
111
  };
112
+ return port;
314
113
  }
315
- async function temporaryArtifact(location) {
316
- await fs.promises.mkdir(path.dirname(location), { recursive: true });
317
- const temporary = path.join(path.dirname(location), `.${path.basename(location)}.${process.pid}.${randomUUID()}.tmp`);
318
- const handle = await fs.promises.open(temporary, 'wx');
319
- await handle.close();
320
- return temporary;
114
+ export function createSquareFile(location, state, signal) {
115
+ return createStoreFile(location, format, db => initializeSqlite(db, state, 0), signal);
321
116
  }
322
- async function initializeFile(location, state, signal) {
323
- const db = new DatabaseSync(location);
324
- try {
325
- db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL');
326
- await inSqliteTransaction(db, true, () => { }, () => {
327
- initializeSqlite(db, state);
328
- db.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
329
- }, signal);
330
- }
331
- finally {
332
- db.close();
333
- }
334
- const handle = await fs.promises.open(location, 'r+');
335
- try {
336
- await handle.sync();
337
- }
338
- finally {
339
- await handle.close();
340
- }
341
- }
342
- /** New-format creation; initial authority is accepted only at this explicit boundary. */
343
- export async function createSquareFile(location, state, signal) {
344
- abort(signal);
345
- location = await canonicalPath(location);
346
- requireSquareExtension(location);
347
- const temporary = await temporaryArtifact(location);
348
- try {
349
- await initializeFile(temporary, state, signal);
350
- abort(signal);
351
- try {
352
- await fs.promises.link(temporary, location);
353
- return true;
354
- }
355
- catch (error) {
356
- if (error.code === 'EEXIST')
357
- return false;
358
- throw error;
359
- }
360
- }
361
- finally {
362
- await fs.promises.unlink(temporary).catch(() => undefined);
363
- }
117
+ export function writeSquareFile(location, state, signal) {
118
+ return replaceStoreFile(location, format, (db, revision) => initializeSqlite(db, state, revision), signal);
364
119
  }
365
- /** Explicit build --force / fixture import, never a runtime state-replacement callback. */
366
- export async function writeSquareFile(location, state, signal) {
367
- location = await canonicalPath(location);
368
- if (await createSquareFile(location, state, signal))
369
- return;
370
- abort(signal);
371
- const db = openExistingSquareDatabase(location);
372
- try {
373
- await retrySqliteBusy(() => validateSqliteHeader(db, location), signal);
374
- db.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
375
- await inSqliteTransaction(db, true, () => validateSqliteHeader(db, location), () => {
376
- const revision = createSqliteDriver(db).header().revision;
377
- if (revision === Number.MAX_SAFE_INTEGER)
378
- throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
379
- for (const table of ['landed_audience', 'attention', 'participants', 'delivery_deferrals', 'delivery_cancellations', 'delivery_evidence', 'seats', 'catch_leases', 'receipts', 'activities', 'square_meta'])
380
- db.exec(`DROP TABLE ${table}`);
381
- initializeSqlite(db, state, revision + 1);
382
- }, signal);
383
- }
384
- finally {
385
- db.close();
386
- }
387
- }
388
- async function inspectFile(location, select, signal) {
389
- const db = openExistingSquareDatabase(location);
390
- try {
391
- return await inSqliteTransaction(db, false, () => validateSqliteHeader(db, location), () => select(db), signal);
392
- }
393
- catch (error) {
394
- throw storageError(error, location);
395
- }
396
- finally {
397
- db.close();
398
- }
399
- }
400
- /** Explicit test/export projection. Hot callers must use the typed port. */
401
120
  export function readSquareSnapshot(location, signal) {
402
- return inspectFile(location, db => ({ state: exportSqliteState(db), revision: createSqliteDriver(db).header().revision }), signal);
121
+ return inspectStore(location, format, db => ({ state: exportSqliteState(db), revision: createSqliteDriver(db).header().revision }), signal);
403
122
  }
404
123
  export function readSquareRevision(location, signal) {
405
- return inspectFile(location, db => createSqliteDriver(db).header().revision, signal);
124
+ return inspectStore(location, format, db => createSqliteDriver(db).header().revision, signal);
406
125
  }
407
126
  export function readSquareDeliveryRevision(location, signal) {
408
- return inspectFile(location, db => createSqliteDriver(db).header().deliveryRevision, signal);
409
- }
410
- function observeRevision(location, revision) {
411
- const read = revision === 'square-revision' ? readSquareRevision : readSquareDeliveryRevision;
412
- return observeFileVersion(location, revision, async (file, signal) => {
413
- try {
414
- return await read(file, signal);
415
- }
416
- catch (error) {
417
- abort(signal);
418
- if (error instanceof InternalSquareError && error.code === 'not_found')
419
- return null;
420
- throw error;
421
- }
422
- });
423
- }
424
- export function observeSquareRevision(location) {
425
- return observeRevision(location, 'square-revision');
426
- }
427
- export function observeSquareDeliveryRevision(location) {
428
- return observeRevision(location, 'square-delivery-revision');
127
+ return inspectStore(location, format, db => createSqliteDriver(db).header().deliveryRevision, signal);
429
128
  }
129
+ export const observeSquareRevision = (location) => observeStoreRevision(location, format);
130
+ export const observeSquareDeliveryRevision = (location) => observeStoreRevision(location, format, true);
430
131
  export async function diagnoseSquareFile(location) {
431
132
  try {
432
- return { problems: [], ...await inspectFile(await canonicalPath(location), inspectSqlite) };
133
+ return { problems: [], ...await inspectStore(await canonicalPath(location), format, db => inspectSqlite(db)) };
433
134
  }
434
135
  catch (error) {
435
- if (error instanceof SquareError && error.facts?.reason === 'migration_required')
436
- return { migrationRequired: error.message, problems: [] };
437
136
  return { unfixable: error instanceof Error ? error.message : String(error), problems: [] };
438
137
  }
439
138
  }
@@ -1,4 +1,4 @@
1
- import type { ActivityDelivery, DirectedNotificationRoute } from './model.js';
1
+ import type { DirectedNotificationRoute } from './model.js';
2
2
  export declare const ATTENTION_BODY_MAX = 200;
3
3
  export interface AttentionPreview {
4
4
  squarePath: string;
@@ -7,14 +7,20 @@ export interface AttentionPreview {
7
7
  actor: string;
8
8
  route: DirectedNotificationRoute;
9
9
  body: string;
10
- delivery?: ActivityDelivery;
10
+ metadataOnly?: boolean;
11
11
  }
12
- export declare function previewAttentionBody(body: string, delivery?: ActivityDelivery): string;
13
- export declare function attentionBodyIsClipped(body: string, delivery?: ActivityDelivery): boolean;
12
+ export declare function previewAttentionBody(body: string): string;
13
+ export declare function attentionBodyIsClipped(body: string): boolean;
14
14
  export declare function displayAttentionPath(squarePath: string): string;
15
15
  export declare function renderAttentionDescription({ actor, route }: Pick<AttentionPreview, 'actor' | 'route'>): string;
16
- /** Full notification text for human-facing expansion, never the bounded model preview. */
16
+ /** The sole source adapter: transports and cards consume this projection, not delivery modes. */
17
+ export declare function projectNotification(attention: AttentionPreview): {
18
+ text: string;
19
+ full: string;
20
+ preview: string;
21
+ description: string;
22
+ isFull: boolean;
23
+ };
24
+ /** Full human expansion never substitutes a sender-owned preview for the stored body. */
17
25
  export declare function renderAttentionFull(attention: AttentionPreview): string;
18
- /** Shared model-facing envelope; transport correlation and human cards stay outside it. */
19
- export declare function renderSquareReminder(previews: readonly string[], deliveries?: readonly (ActivityDelivery | undefined)[]): string;
20
26
  export declare function renderAttentionPreview(attention: AttentionPreview): string;
@@ -1,21 +1,15 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { formatActivityId } from './square-core.js';
3
3
  export const ATTENTION_BODY_MAX = 200;
4
- export function previewAttentionBody(body, delivery) {
5
- const compact = (delivery?.mode === 'direct' ? delivery.preview ?? body : body).replace(/\r\n/g, '\n');
6
- if (delivery?.mode === 'direct' || compact.length <= ATTENTION_BODY_MAX)
7
- return compact;
8
- return `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
4
+ export function previewAttentionBody(body) {
5
+ const compact = body.replace(/\r\n/g, '\n');
6
+ return compact.length <= ATTENTION_BODY_MAX ? compact : `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
9
7
  }
10
- export function attentionBodyIsClipped(body, delivery) {
11
- return delivery?.mode === 'direct'
12
- ? delivery.preview !== undefined
13
- : body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
8
+ export function attentionBodyIsClipped(body) {
9
+ return body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
14
10
  }
15
11
  export function displayAttentionPath(squarePath) {
16
- return squarePath.startsWith(homedir())
17
- ? `~${squarePath.slice(homedir().length)}`
18
- : squarePath;
12
+ return squarePath.startsWith(homedir()) ? `~${squarePath.slice(homedir().length)}` : squarePath;
19
13
  }
20
14
  export function renderAttentionDescription({ actor, route }) {
21
15
  switch (route) {
@@ -25,56 +19,19 @@ export function renderAttentionDescription({ actor, route }) {
25
19
  case 'bell': return `${actor} rang the bell`;
26
20
  }
27
21
  }
28
- /** Full notification text for human-facing expansion, never the bounded model preview. */
29
- export function renderAttentionFull(attention) {
30
- return attention.delivery?.mode === 'direct' ? renderDirectBlock(attention) : renderAttentionBlock(attention, attention.body);
31
- }
32
- /** Attribute values keep their quotes; the stored body and the human card stay unescaped. */
33
- function escapeAttribute(value) {
34
- return value
35
- .replace(/&/g, '&amp;')
36
- .replace(/"/g, '&quot;')
37
- .replace(/</g, '&lt;')
38
- .replace(/>/g, '&gt;');
39
- }
40
- /** A body may quote the envelope's own tags; in the preview they must never read as one. */
41
22
  function escapeSquareTags(text) {
42
- return text.replace(/<(\/?)(square-|system-reminder)/gi, '<\\$1$2');
43
- }
44
- function renderAttentionBlock(attention, body) {
45
- return [
46
- '<square-activity>',
47
- `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
48
- `● ${renderAttentionDescription(attention)}`,
49
- '',
50
- body,
51
- '</square-activity>',
52
- ].join('\n');
23
+ return text.replace(/<(\/?)(square-|channel-|system-reminder)/gi, '<\\$1$2');
53
24
  }
54
- function renderDirectBlock(attention) {
55
- return escapeSquareTags(previewAttentionBody(attention.body, attention.delivery));
56
- }
57
- /** Shared model-facing envelope; transport correlation and human cards stay outside it. */
58
- export function renderSquareReminder(previews, deliveries = []) {
59
- if (previews.length === 0)
60
- return '';
61
- const header = deliveries.some(item => item?.mode === 'direct')
62
- ? 'This is an external notification, not a direct message from the user.'
63
- : 'This is an external reminder from Square, not a direct message from the user.';
64
- return [
65
- '',
66
- '<system-reminder>',
67
- header,
68
- '',
69
- previews.join('\n\n'),
70
- '</system-reminder>',
71
- '',
72
- ].join('\n');
73
- }
74
- export function renderAttentionPreview(attention) {
75
- if (attention.delivery?.mode === 'direct')
76
- return renderDirectBlock(attention);
77
- const remaining = attention.body.replace(/\r\n/g, '\n').length - ATTENTION_BODY_MAX;
25
+ function escapeAttribute(value) {
26
+ return value.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
27
+ }
28
+ /** The sole source adapter: transports and cards consume this projection, not delivery modes. */
29
+ export function projectNotification(attention) {
30
+ const body = attention.metadataOnly ? '(structured activity)' : attention.body.replace(/\r\n/g, '\n');
31
+ const description = renderAttentionDescription(attention);
32
+ const remaining = body.length - ATTENTION_BODY_MAX;
33
+ const isFull = !attention.metadataOnly && remaining <= 0;
34
+ const preview = previewAttentionBody(body);
78
35
  const attributes = [
79
36
  `square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
80
37
  `as="${escapeAttribute(attention.recipient)}"`,
@@ -82,11 +39,15 @@ export function renderAttentionPreview(attention) {
82
39
  `from="${escapeAttribute(attention.actor)}"`,
83
40
  ...(remaining > 0 ? [`truncated="${remaining} more chars"`] : []),
84
41
  ];
85
- return [
86
- `<square-activity ${attributes.join(' ')}>`,
87
- `${renderAttentionDescription(attention)}.`,
88
- '',
89
- escapeSquareTags(previewAttentionBody(attention.body)),
90
- '</square-activity>',
91
- ].join('\n');
92
- }
42
+ return {
43
+ text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview),
44
+ ...(!isFull ? ['', escapeSquareTags(`Use square_catch with location=${JSON.stringify(attention.squarePath)}, as=${JSON.stringify(attention.recipient)}, id=${JSON.stringify(formatActivityId(attention.actIndex))} for the full activity.`)] : []),
45
+ '</square-activity>'].join('\n'),
46
+ full: ['<square-activity>', `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
47
+ `● ${description}`, '', body, '</square-activity>'].join('\n'),
48
+ preview, description, isFull,
49
+ };
50
+ }
51
+ /** Full human expansion never substitutes a sender-owned preview for the stored body. */
52
+ export function renderAttentionFull(attention) { return projectNotification(attention).full; }
53
+ export function renderAttentionPreview(attention) { return projectNotification(attention).text; }
@@ -1,16 +1,3 @@
1
- import { pendingAtBoundary } from './inbox.js';
2
- import type { Owner, PendingMembership } from './model.js';
3
- export { pendingAtBoundary };
4
- export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
5
- /**
6
- * Serialize a boundary across hook processes so one stale inbox cannot render twice.
7
- *
8
- * `lookup` returns the session's memberships for this boundary. It may reuse an observation it has
9
- * verified against the current presence binding and artifact revision; the boundary still projects
10
- * presentation evidence live. Every item is claimed and revalidated before the batch sink;
11
- * after success, captured-seat commits record the winning batch, including budget omissions as
12
- * clipped (no consumption receipt), without an eligibility veto. Coverage is conservative if a
13
- * competing claim's removal lets a previously omitted body fit in the final preview.
14
- * A source that cannot verify its reuse must read the inbox again.
15
- */
16
- export declare function presentPendingAtBoundary<T>(identity: Owner, present: (context: string, delivered: PendingMembership[]) => T | Promise<T>, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
1
+ import type { Owner } from './owner.js';
2
+ /** The sink must resolve only after the host has actually accepted the output. */
3
+ export declare function presentPendingAtBoundary<T>(owner: Owner, present: (context: string) => T | Promise<T>, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;