@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
@@ -1,48 +1,46 @@
1
1
  import { connect, DeliveryError } from './packages/agent-delivery/src/index.js';
2
- export { sendCodexQueue, CodexQueueSendError } from './packages/agent-delivery/src/codex-queue.js';
2
+ import { codexIdleAtStop } from './packages/agent-delivery/src/codex.js';
3
+ import { codexQueueEndpoint } from './packages/agent-delivery/src/codex-endpoint.js';
3
4
  import { codexBoundaryStore } from './codex-boundary-state.js';
4
- import { wakePreview } from './delivery.js';
5
+ import { renderDeliveryReminder } from './delivery-frame.js';
6
+ /** Instantiated only by the receiver, for one captured Stop epoch. */
5
7
  export class CodexQueueTransport {
6
8
  opts;
7
- constructor(opts = {}) {
9
+ kind = 'codex-queue';
10
+ constructor(opts) {
8
11
  this.opts = opts;
9
12
  }
10
- async attempt(request, timeoutMs, beforeSend) {
11
- const deadline = Date.now() + timeoutMs;
12
- const { address } = request.route;
13
+ async send(card, address, context) {
13
14
  const threadId = address.threadId?.trim();
14
- if (!threadId) {
15
- return { outcome: 'failed', signature: 'invalid_address', message: 'Codex route has no thread id.' };
16
- }
15
+ if (!threadId || !address.codexHome || Object.keys(address).length !== Object.keys(this.opts.address).length ||
16
+ !Object.entries(this.opts.address).every(([key, value]) => address[key] === value))
17
+ return 'not-capable';
17
18
  const env = this.opts.env ?? process.env;
18
- const payload = request.artifact === undefined ? undefined
19
- : (await request.artifact.read((facts) => wakePreview(facts, request))).value;
20
- if (payload === undefined)
21
- return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
22
- if (!(await beforeSend()))
23
- return { outcome: 'not-capable' };
19
+ const boundaries = codexBoundaryStore(env), epoch = this.opts.epoch;
24
20
  try {
25
- const agent = await (this.opts.connect ?? connect)({
26
- harness: 'codex', transport: 'cli', sessionId: threadId, boundaries: codexBoundaryStore(env),
27
- env, bin: env.SQUARE_CODEX_BIN, codexHome: address.codexHome, endpoint: address.endpoint,
28
- });
29
- const remaining = deadline - Date.now();
30
- if (remaining <= 0) {
31
- return { outcome: 'failed', signature: 'dispatch_budget_exhausted', message: 'The wake dispatch budget elapsed before queueing.' };
32
- }
33
- await agent.queue(payload, { timeoutMs: remaining });
34
- return { outcome: 'accepted' };
21
+ // Receiver delivery addresses the already-hosting endpoint explicitly;
22
+ // disappearing native custody must not trigger CLI service startup.
23
+ const endpoint = address.endpoint ?? codexQueueEndpoint({ CODEX_HOME: address.codexHome });
24
+ const agent = await connect({ harness: 'codex', transport: 'cli', sessionId: threadId,
25
+ boundaries: { queueEligible: async (id) => {
26
+ if (!await boundaries.queueEligible(id, epoch))
27
+ return false;
28
+ // Repeat the native sample after claim, immediately before the final
29
+ // epoch and ledger fences. These reads are not an atomic native enqueue.
30
+ if (!await codexIdleAtStop({ harness: 'codex', sessionId: id, codexHome: address.codexHome,
31
+ endpoint, timeoutMs: Math.max(1, context.deadline - Date.now()), signal: context.signal }, epoch.turnId))
32
+ return false;
33
+ return await boundaries.queueEligible(id, epoch) && await context.beforeSend()
34
+ && !context.signal.aborted && Date.now() < context.deadline;
35
+ } },
36
+ env, bin: env.SQUARE_CODEX_BIN, codexHome: address.codexHome, endpoint });
37
+ if (context.signal.aborted || Date.now() >= context.deadline)
38
+ return 'not-capable';
39
+ await agent.queue(renderDeliveryReminder([card.text], { tagged: true }), { timeoutMs: context.deadline - Date.now(), signal: context.signal });
40
+ return 'accepted';
35
41
  }
36
42
  catch (error) {
37
- if (error instanceof DeliveryError && error.code === 'session_inactive') {
38
- return { outcome: 'failed', signature: 'boundary_not_stopped',
39
- message: 'The Codex thread has no current Stop boundary for queueing.' };
40
- }
41
- const message = error instanceof Error ? error.message : String(error);
42
- if (!(error instanceof DeliveryError) || error.maybeDelivered)
43
- return { outcome: 'unknown', signature: 'queue_unknown', message };
44
- return { outcome: 'failed', signature: ['unavailable', 'timeout', 'aborted'].includes(error.code)
45
- ? 'queue_pre_accept_transient' : 'queue_pre_accept_rejected', message };
43
+ return error instanceof DeliveryError && !error.maybeDelivered ? 'not-capable' : 'unknown';
46
44
  }
47
45
  }
48
46
  }
@@ -0,0 +1,2 @@
1
+ /** One native async hook owns observation; the lock itself conveys no idle authority. */
2
+ export declare function runCodexWatch(inputText: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<string>;
@@ -0,0 +1,100 @@
1
+ import path from 'node:path';
2
+ import { promises as fs } from 'node:fs';
3
+ import { createHash } from 'node:crypto';
4
+ import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
5
+ import { retainDeliveryLedger } from './delivery-lifetime.js';
6
+ import { harnessOwner } from './owner.js';
7
+ import { withFileLock } from './file-lock.js';
8
+ import { canonicalPath } from './canonical-path.js';
9
+ import { codexBoundaryStore } from './codex-boundary-state.js';
10
+ import { codexWakeAddress } from './codex-address.js';
11
+ import { parseCodexHookText } from './codex-hook.js';
12
+ import { CodexQueueTransport } from './codex-queue.js';
13
+ import { codexIdleAtStop } from './packages/agent-delivery/src/codex.js';
14
+ /** One native async hook owns observation; the lock itself conveys no idle authority. */
15
+ export async function runCodexWatch(inputText, env = process.env, signal) {
16
+ const input = parseCodexHookText(inputText);
17
+ if (input?.hook_event_name !== 'Stop' || signal?.aborted)
18
+ return '';
19
+ const controller = new AbortController();
20
+ const stop = () => controller.abort();
21
+ const timer = setTimeout(stop, 86_400_000);
22
+ const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
23
+ process.on('SIGTERM', stop);
24
+ process.on('SIGINT', stop);
25
+ try {
26
+ const route = codexWakeAddress(input.session_id, env);
27
+ const home = await canonicalPath(route.address.codexHome);
28
+ const coordinate = [input.session_id, route.address.endpoint ?? home];
29
+ const root = path.join(path.dirname(deliveryLedgerLocation(env)), 'codex-watchers');
30
+ await fs.mkdir(root, { recursive: true, mode: 0o700 });
31
+ const lock = path.join(root, createHash('sha256').update(JSON.stringify(coordinate)).digest('hex') + '.lock');
32
+ const file = await fs.open(lock, 'a', 0o600);
33
+ await file.close();
34
+ await withFileLock(lock, { retryMs: 10, skipIfBusy: true, signal: combined }, async () => {
35
+ const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
36
+ const lifetime = retainDeliveryLedger(ledger);
37
+ let observer;
38
+ try {
39
+ const owner = harnessOwner({ channel: 'codex', session: input.session_id });
40
+ const captured = await ledger.session(owner);
41
+ if (captured?.state !== 'attached')
42
+ return;
43
+ const boundaries = codexBoundaryStore(env);
44
+ // Subscribe before inspecting pending work. Both ledger edges and the
45
+ // bounded wait below cause reconciliation; boundary-only changes matter.
46
+ observer = await ledger.observe(owner);
47
+ while (!combined.aborted) {
48
+ let version = await observer.read(combined);
49
+ const session = await ledger.session(owner);
50
+ if (session?.state !== 'attached' || session.generation !== captured.generation)
51
+ return;
52
+ const registered = session.routes.find(candidate => candidate.kind === 'codex-queue');
53
+ if (!registered || registered.address.threadId !== input.session_id ||
54
+ registered.address.endpoint !== route.address.endpoint ||
55
+ !registered.address.codexHome || await canonicalPath(registered.address.codexHome) !== home)
56
+ return;
57
+ const boundary = await boundaries.read(input.session_id);
58
+ if (boundary?.ended)
59
+ return;
60
+ const pending = await ledger.pending(owner, { generation: captured.generation });
61
+ const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }))
62
+ .filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
63
+ for (const card of pending) {
64
+ if (combined.aborted)
65
+ break;
66
+ if (held.has(card.seq))
67
+ continue;
68
+ // Follow the latest Stop, regardless of which Stop launched this process.
69
+ const epoch = (await boundaries.read(input.session_id))?.stop;
70
+ if (!epoch || !await codexIdleAtStop({ harness: 'codex', sessionId: input.session_id,
71
+ codexHome: registered.address.codexHome, endpoint: registered.address.endpoint,
72
+ timeoutMs: 2000, signal: combined }, epoch.turnId))
73
+ break;
74
+ lifetime.track(await ledger.dispatchHandoff(owner, card.key, new CodexQueueTransport({ epoch, env, address: registered.address }), { generation: captured.generation, timeoutMs: 5000, signal: combined }));
75
+ // Drain our own claim/settlement edge. A known-unsent failure must
76
+ // not turn file observation into an immediate retry loop. Any post
77
+ // racing this read is still found by the bounded rescan below.
78
+ version = await observer.read(combined);
79
+ }
80
+ // No claims are held while waiting, including while active or disarmed.
81
+ await observer.changed(version, 1000, combined);
82
+ }
83
+ }
84
+ finally {
85
+ observer?.close();
86
+ await lifetime.close();
87
+ }
88
+ });
89
+ }
90
+ catch {
91
+ if (!combined.aborted)
92
+ throw new Error('Codex watcher unavailable.');
93
+ }
94
+ finally {
95
+ clearTimeout(timer);
96
+ process.off('SIGTERM', stop);
97
+ process.off('SIGINT', stop);
98
+ }
99
+ return '';
100
+ }
package/dist/decisions.js CHANGED
@@ -51,7 +51,7 @@ export function unreadActivitySummaries(facts, name, now) {
51
51
  let afterIndex = readCursor(facts, name);
52
52
  const summaries = new Map();
53
53
  while (true) {
54
- const page = facts.directed(name, { afterIndex }, { edge: 'oldest', limit: FACT_PAGE_SIZE });
54
+ const page = facts.directed(name, { afterIndex, kinds: ['say'], unseenOnly: true }, { edge: 'oldest', limit: FACT_PAGE_SIZE });
55
55
  for (const item of page.items) {
56
56
  const summary = summaries.get(item.actor) ?? { count: 0, latestAt: -Infinity, lastIndex: item.index };
57
57
  summary.count++;
@@ -194,8 +194,8 @@ export function coreParticipants(facts, now) {
194
194
  activityCount: current.sayCount, lastActiveAt,
195
195
  presence: current.done ? 'done' : !current.joined ? 'never-joined' : facts.seat(current.name)?.attachment === 'detached' ? 'away'
196
196
  : freshWatchLease(facts, current.name, now) !== undefined ? 'watching' : 'active',
197
- unreadActivityCount: current.joined ? facts.directedCount(current.name, { afterIndex: readCursor(facts, current.name) }) : 0,
198
- pendingMentionCount: current.joined ? facts.directedCount(current.name, { afterIndex: current.lastJoinIndex ?? -1, unseenOnly: true }) : 0,
197
+ unreadActivityCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: readCursor(facts, current.name), unseenOnly: true }) : 0,
198
+ pendingMentionCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: current.lastJoinIndex ?? -1, unseenOnly: true }) : 0,
199
199
  };
200
200
  }).sort(compareParticipantActivity);
201
201
  }
@@ -1,5 +1,4 @@
1
- import { type ActivityMetadata, type ActivityId } from './model.js';
2
- import type { SeatRef, SquareRead } from './ports.js';
1
+ import type { ActivityMetadata } from './model.js';
3
2
  export type MetadataScalar = null | boolean | number | string;
4
3
  export interface MetadataMatch {
5
4
  readonly fields: readonly Readonly<{
@@ -7,22 +6,5 @@ export interface MetadataMatch {
7
6
  equals: MetadataScalar;
8
7
  }>[];
9
8
  }
10
- export interface DeliveryDeferral {
11
- readonly id: string;
12
- readonly expiresAt: string;
13
- }
14
- export interface DeliveryDeferralFact {
15
- readonly participant: string;
16
- readonly generation: ActivityId;
17
- readonly id: string;
18
- readonly match: MetadataMatch;
19
- readonly expiresAt: number;
20
- }
21
- export declare const MAX_DELIVERY_DEFERRAL_MS: number;
22
9
  export declare function validateMetadataMatch(value: unknown): MetadataMatch;
23
- export declare function validateDeferralDuration(value: unknown): number;
24
- export declare function validateDeferralId(value: unknown): string;
25
10
  export declare function matchesMetadata(metadata: ActivityMetadata | undefined, match: MetadataMatch): boolean;
26
- export declare function matchingDeferrals(deferrals: readonly DeliveryDeferralFact[], metadata: ActivityMetadata | undefined, now: number): readonly DeliveryDeferralFact[];
27
- export declare function deliveryDeferralExpiry(facts: SquareRead, captured: SeatRef, index: number, now: number): number | undefined;
28
- export declare function earliestMatchingDeferralExpiry(deferrals: readonly DeliveryDeferralFact[], metadata: ActivityMetadata | undefined, now: number): number | undefined;
@@ -1,5 +1,4 @@
1
- import { SquareError } from './model.js';
2
- export const MAX_DELIVERY_DEFERRAL_MS = 24 * 60 * 60 * 1000;
1
+ import { SquareError } from './neutral.js';
3
2
  export function validateMetadataMatch(value) {
4
3
  const fail = () => { throw new SquareError('invalid_args', 'Invalid metadata match.'); };
5
4
  const record = (item) => {
@@ -41,16 +40,6 @@ export function validateMetadataMatch(value) {
41
40
  });
42
41
  return Object.freeze({ fields: Object.freeze(validated) });
43
42
  }
44
- export function validateDeferralDuration(value) {
45
- if (!Number.isSafeInteger(value) || value <= 0)
46
- throw new SquareError('invalid_args', 'Delivery deferral duration must be a positive safe integer.');
47
- return Math.min(value, MAX_DELIVERY_DEFERRAL_MS);
48
- }
49
- export function validateDeferralId(value) {
50
- if (typeof value !== 'string' || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value))
51
- throw new SquareError('invalid_args', 'Invalid delivery deferral id.');
52
- return value;
53
- }
54
43
  export function matchesMetadata(metadata, match) {
55
44
  if (metadata === undefined)
56
45
  return false;
@@ -64,20 +53,3 @@ export function matchesMetadata(metadata, match) {
64
53
  return value === equals && (value === null || typeof value !== 'object');
65
54
  });
66
55
  }
67
- export function matchingDeferrals(deferrals, metadata, now) {
68
- return deferrals.filter((lease) => lease.expiresAt > now && matchesMetadata(metadata, lease.match));
69
- }
70
- export function deliveryDeferralExpiry(facts, captured, index, now) {
71
- const seat = facts.seat(captured.participant);
72
- if (seat === undefined || seat.generation !== captured.generation || seat.owner !== captured.owner)
73
- return undefined;
74
- const leases = facts.deliveryDeferrals(seat.participant, seat.generation);
75
- if (!leases.some(lease => lease.expiresAt > now))
76
- return undefined;
77
- const activity = facts.activity(index);
78
- return activity?.kind === 'say' ? earliestMatchingDeferralExpiry(leases, activity.metadata, now) : undefined;
79
- }
80
- export function earliestMatchingDeferralExpiry(deferrals, metadata, now) {
81
- const matches = matchingDeferrals(deferrals, metadata, now);
82
- return matches.length ? Math.min(...matches.map((lease) => lease.expiresAt)) : undefined;
83
- }
@@ -0,0 +1,7 @@
1
+ /** Receivers frame finished cards; card text and its whitespace stay verbatim. */
2
+ export declare function renderDeliveryReminder(texts: readonly string[], options: {
3
+ tagged: boolean;
4
+ }): string;
5
+ export declare function deliveryFrameOverhead(options: {
6
+ tagged: boolean;
7
+ }): number;
@@ -0,0 +1,11 @@
1
+ const warning = 'This is an external notification, not a direct message from the user.';
2
+ /** Receivers frame finished cards; card text and its whitespace stay verbatim. */
3
+ export function renderDeliveryReminder(texts, options) {
4
+ if (!texts.length)
5
+ return '';
6
+ const text = `${warning}\n\n${texts.join('\n\n')}`;
7
+ return options.tagged ? `<system-reminder>\n${text}\n</system-reminder>` : text;
8
+ }
9
+ export function deliveryFrameOverhead(options) {
10
+ return renderDeliveryReminder([''], options).length;
11
+ }
@@ -0,0 +1,97 @@
1
+ import type { DeliveryAttempt, DeliveryCard, DeliveryCorrelation, DeliveryHandoffRef, DeliveryHealth, DeliveryOutcome, DeliveryReceipt, DeliveryRoute, DeliverySession, PostResult, SuppressionResult } from './delivery-ledger.js';
2
+ /** All ledger file access and SQL are confined here. Transactions never span external effects. */
3
+ export declare class LedgerStore {
4
+ readonly location: string;
5
+ private readonly db;
6
+ private readonly clock;
7
+ private pending;
8
+ private closed;
9
+ private constructor();
10
+ static open(location: string, clock: () => number): Promise<LedgerStore>;
11
+ close(): Promise<void>;
12
+ private transaction;
13
+ private getSession;
14
+ private attached;
15
+ private listAttempts;
16
+ private awaitingEffect;
17
+ private getAttempt;
18
+ private normalize;
19
+ private deleteCard;
20
+ private gc;
21
+ session(owner: string): Promise<DeliverySession | undefined>;
22
+ sessions(): Promise<readonly DeliverySession[]>;
23
+ pendingCards(owner: string, generation: string): Promise<readonly DeliveryCard[]>;
24
+ handoffs(owner: string, generation: string): Promise<readonly DeliveryAttempt[]>;
25
+ attach(input: {
26
+ owner: string;
27
+ expectedGeneration: string | null;
28
+ routes: readonly DeliveryRoute[];
29
+ }): Promise<DeliverySession | false>;
30
+ updateRoutes(input: {
31
+ owner: string;
32
+ generation: string;
33
+ routes: readonly DeliveryRoute[];
34
+ }): Promise<boolean>;
35
+ lifecycle(input: {
36
+ owner: string;
37
+ generation: string;
38
+ }, state: 'detached' | 'deleted'): Promise<boolean>;
39
+ post(input: {
40
+ owner: string;
41
+ key: string;
42
+ text: string;
43
+ details: string | null;
44
+ }): Promise<PostResult>;
45
+ suppress(input: {
46
+ owner: string;
47
+ key: string;
48
+ scope: 'exact' | 'prefix';
49
+ expiresAt?: number;
50
+ }): Promise<SuppressionResult>;
51
+ renewSuppression(input: {
52
+ owner: string;
53
+ id: string;
54
+ expiresAt: number;
55
+ }): Promise<boolean>;
56
+ releaseSuppression(input: {
57
+ owner: string;
58
+ id: string;
59
+ }): Promise<boolean>;
60
+ cancel(input: {
61
+ owner: string;
62
+ generation: string;
63
+ }): Promise<boolean>;
64
+ private isSuppressed;
65
+ private eligible;
66
+ private insertAttempt;
67
+ claimPresentation(owner: string, generation: string, budgetChars: number, leaseMs: number, signal?: AbortSignal): Promise<{
68
+ cards: DeliveryCard[];
69
+ attempts: DeliveryAttempt[];
70
+ } | undefined>;
71
+ claimHandoff(owner: string, key: string, generation: string, kind: string, leaseMs: number, signal?: AbortSignal): Promise<{
72
+ card: DeliveryCard;
73
+ route: DeliveryRoute;
74
+ attempt: DeliveryAttempt;
75
+ } | undefined>;
76
+ current(tokens: readonly string[], signal?: AbortSignal, route?: DeliveryRoute): Promise<boolean>;
77
+ prepare(token: string, value: DeliveryCorrelation, signal?: AbortSignal): Promise<boolean>;
78
+ private receipt;
79
+ private confirm;
80
+ settle(tokens: readonly string[], outcome: Exclude<DeliveryOutcome, 'dispatching'>): Promise<boolean>;
81
+ private handoffFacts;
82
+ private matchingHandoff;
83
+ private currentHandoff;
84
+ handoff(token: string): Promise<(DeliveryHandoffRef & {
85
+ routeKind: string;
86
+ }) | undefined>;
87
+ guardHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
88
+ acceptHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
89
+ observePresentation(handoff: string | DeliveryHandoffRef, options?: {
90
+ readonly requireCurrent?: boolean;
91
+ }): Promise<boolean>;
92
+ receipts(owner: string, options: {
93
+ keys?: readonly string[];
94
+ since?: number;
95
+ }): Promise<readonly DeliveryReceipt[]>;
96
+ health(owner?: string): Promise<DeliveryHealth>;
97
+ }