@astrosheep/square 0.4.0 → 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 (187) 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-delivery.d.ts +13 -8
  11. package/dist/claude-delivery.js +40 -93
  12. package/dist/claude-mcp-context.js +2 -2
  13. package/dist/claude-mod.js +28 -140
  14. package/dist/claude-transport.d.ts +3 -3
  15. package/dist/claude-transport.js +20 -44
  16. package/dist/cli/context.js +7 -9
  17. package/dist/cli/observation-commands.d.ts +2 -0
  18. package/dist/cli/observation-commands.js +12 -2
  19. package/dist/cli/registry.js +3 -1
  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-links.js +61 -7
  41. package/dist/harness.js +12 -17
  42. package/dist/help.js +2 -0
  43. package/dist/http-delivery.d.ts +5 -9
  44. package/dist/http-delivery.js +20 -77
  45. package/dist/inbox.d.ts +6 -49
  46. package/dist/inbox.js +24 -371
  47. package/dist/index.d.ts +6 -3
  48. package/dist/index.js +4 -1
  49. package/dist/mcp.js +1 -1
  50. package/dist/model.d.ts +7 -47
  51. package/dist/model.js +1 -7
  52. package/dist/notifications.d.ts +24 -20
  53. package/dist/notifications.js +39 -108
  54. package/dist/operation-control.d.ts +4 -0
  55. package/dist/operation-control.js +20 -0
  56. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  57. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  58. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  59. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  60. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  61. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  62. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  63. package/dist/packages/agent-delivery/src/codex.js +41 -4
  64. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  65. package/dist/packages/agent-delivery/src/index.js +1 -1
  66. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  67. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  68. package/dist/paseo-delivery.d.ts +5 -5
  69. package/dist/paseo-delivery.js +19 -97
  70. package/dist/paseo.d.ts +1 -1
  71. package/dist/paseo.js +1 -1
  72. package/dist/plugins/opencode/index.d.ts +5 -3
  73. package/dist/plugins/opencode/index.js +99 -21
  74. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  75. package/dist/plugins/opencode-shared/delivery.js +109 -0
  76. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  77. package/dist/plugins/opencode-v1/index.js +41 -0
  78. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  79. package/dist/plugins/opencode-v1/runtime.js +152 -0
  80. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  81. package/dist/plugins/opencode-v1/transport.js +50 -0
  82. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  83. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  84. package/dist/ports.d.ts +31 -90
  85. package/dist/session-lifecycle.d.ts +8 -7
  86. package/dist/session-lifecycle.js +40 -42
  87. package/dist/sqlite-square.d.ts +14 -23
  88. package/dist/sqlite-square.js +132 -139
  89. package/dist/square-api.js +4 -7
  90. package/dist/square-core.d.ts +0 -1
  91. package/dist/square-domain.d.ts +5 -17
  92. package/dist/square-domain.js +104 -221
  93. package/dist/square-facade.d.ts +18 -15
  94. package/dist/square-post.d.ts +53 -0
  95. package/dist/square-post.js +4 -0
  96. package/dist/square-posting.d.ts +13 -0
  97. package/dist/square-posting.js +174 -0
  98. package/dist/square-producer-sqlite.d.ts +11 -0
  99. package/dist/square-producer-sqlite.js +210 -0
  100. package/dist/square-sqlite.d.ts +15 -25
  101. package/dist/square-sqlite.js +150 -226
  102. package/dist/views.d.ts +2 -2
  103. package/dist/views.js +0 -1
  104. package/dist/wake-sink.d.ts +4 -11
  105. package/dist/wake-sink.js +8 -32
  106. package/dist/wake-transports.d.ts +4 -7
  107. package/dist/wake-transports.js +8 -39
  108. package/examples/symmetric-domains.mjs +29 -249
  109. package/guides/change-waits.md +13 -53
  110. package/guides/claude-native.md +19 -175
  111. package/guides/codex-delivery.md +55 -80
  112. package/guides/delivery-primitives.md +48 -207
  113. package/guides/operation-cancellation.md +2 -5
  114. package/guides/participant-ownership.md +25 -89
  115. package/guides/pi-native-delivery.md +19 -56
  116. package/guides/pi-tools.md +6 -8
  117. package/guides/square-api.md +113 -615
  118. package/package.json +8 -8
  119. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  120. package/plugins/claude/hooks/hooks.json +1 -1
  121. package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
  122. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  123. package/plugins/codex/hooks/hooks.json +11 -23
  124. package/plugins/opencode/index.ts +91 -23
  125. package/plugins/opencode-shared/delivery.ts +91 -0
  126. package/plugins/opencode-v1/index.ts +36 -0
  127. package/plugins/opencode-v1/runtime.ts +134 -0
  128. package/plugins/opencode-v1/transport.ts +41 -0
  129. package/plugins/paseo/OVERVIEW.md +5 -5
  130. package/plugins/paseo/README.md +7 -6
  131. package/plugins/paseo/client/markdown.tsx +5 -5
  132. package/plugins/paseo/client/square-message.tsx +28 -24
  133. package/plugins/paseo/client/transform.ts +8 -5
  134. package/plugins/paseo/shared/card-presentation.ts +16 -0
  135. package/plugins/paseo/shared/message.ts +6 -11
  136. package/plugins/pi/square-pi.js +156 -417
  137. package/dist/channel-artifact.d.ts +0 -10
  138. package/dist/channel-artifact.js +0 -55
  139. package/dist/channel-delivery.d.ts +0 -11
  140. package/dist/channel-delivery.js +0 -51
  141. package/dist/channel-domain.d.ts +0 -3
  142. package/dist/channel-domain.js +0 -83
  143. package/dist/channel-facade.d.ts +0 -76
  144. package/dist/channel-facade.js +0 -1
  145. package/dist/channel-internal.d.ts +0 -126
  146. package/dist/channel-internal.js +0 -440
  147. package/dist/channel-metadata.d.ts +0 -11
  148. package/dist/channel-metadata.js +0 -1
  149. package/dist/channel-ports.d.ts +0 -241
  150. package/dist/channel-ports.js +0 -1
  151. package/dist/channel-presentation.d.ts +0 -17
  152. package/dist/channel-presentation.js +0 -22
  153. package/dist/channel-sqlite-schema.d.ts +0 -13
  154. package/dist/channel-sqlite-schema.js +0 -81
  155. package/dist/channel-transport.d.ts +0 -62
  156. package/dist/channel-transport.js +0 -1
  157. package/dist/channel.d.ts +0 -84
  158. package/dist/channel.js +0 -60
  159. package/dist/claude-delivery-status.d.ts +0 -13
  160. package/dist/claude-delivery-status.js +0 -46
  161. package/dist/cmd/notify-once.d.ts +0 -2
  162. package/dist/cmd/notify-once.js +0 -29
  163. package/dist/delivery-attempts.d.ts +0 -52
  164. package/dist/delivery-attempts.js +0 -73
  165. package/dist/delivery-effects.d.ts +0 -139
  166. package/dist/delivery-effects.js +0 -163
  167. package/dist/delivery-health.d.ts +0 -51
  168. package/dist/delivery-health.js +0 -173
  169. package/dist/delivery-operations.d.ts +0 -22
  170. package/dist/delivery-operations.js +0 -198
  171. package/dist/delivery-primitives.d.ts +0 -5
  172. package/dist/delivery-primitives.js +0 -3
  173. package/dist/delivery.d.ts +0 -56
  174. package/dist/delivery.js +0 -80
  175. package/dist/plugins/opencode/delivery.d.ts +0 -19
  176. package/dist/plugins/opencode/delivery.js +0 -223
  177. package/dist/presentation-operations.d.ts +0 -5
  178. package/dist/presentation-operations.js +0 -86
  179. package/dist/sqlite-channel.d.ts +0 -15
  180. package/dist/sqlite-channel.js +0 -600
  181. package/dist/square-index.d.ts +0 -16
  182. package/dist/square-index.js +0 -106
  183. package/dist/wake-preview.d.ts +0 -7
  184. package/dist/wake-preview.js +0 -15
  185. package/plugins/opencode/delivery.ts +0 -198
  186. /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
  187. /package/dist/{channel-values.js → square-values.js} +0 -0
@@ -1,46 +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';
5
+ import { renderDeliveryReminder } from './delivery-frame.js';
6
+ /** Instantiated only by the receiver, for one captured Stop epoch. */
4
7
  export class CodexQueueTransport {
5
8
  opts;
6
- constructor(opts = {}) {
9
+ kind = 'codex-queue';
10
+ constructor(opts) {
7
11
  this.opts = opts;
8
12
  }
9
- async attempt(request, timeoutMs, beforeSend) {
10
- const deadline = Date.now() + timeoutMs;
11
- const { address } = request.route;
13
+ async send(card, address, context) {
12
14
  const threadId = address.threadId?.trim();
13
- if (!threadId) {
14
- return { outcome: 'failed', signature: 'invalid_address', message: 'Codex route has no thread id.' };
15
- }
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';
16
18
  const env = this.opts.env ?? process.env;
17
- const payload = request.presentation?.payload;
18
- if (payload === undefined)
19
- return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
20
- if (!(await beforeSend()))
21
- return { outcome: 'not-capable' };
19
+ const boundaries = codexBoundaryStore(env), epoch = this.opts.epoch;
22
20
  try {
23
- const agent = await (this.opts.connect ?? connect)({
24
- harness: 'codex', transport: 'cli', sessionId: threadId, boundaries: codexBoundaryStore(env),
25
- env, bin: env.SQUARE_CODEX_BIN, codexHome: address.codexHome, endpoint: address.endpoint,
26
- });
27
- const remaining = deadline - Date.now();
28
- if (remaining <= 0) {
29
- return { outcome: 'failed', signature: 'dispatch_budget_exhausted', message: 'The wake dispatch budget elapsed before queueing.' };
30
- }
31
- await agent.queue(payload, { timeoutMs: remaining });
32
- 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';
33
41
  }
34
42
  catch (error) {
35
- if (error instanceof DeliveryError && error.code === 'session_inactive') {
36
- return { outcome: 'failed', signature: 'boundary_not_stopped',
37
- message: 'The Codex thread has no current Stop boundary for queueing.' };
38
- }
39
- const message = error instanceof Error ? error.message : String(error);
40
- if (!(error instanceof DeliveryError) || error.maybeDelivered)
41
- return { outcome: 'unknown', signature: 'queue_unknown', message };
42
- return { outcome: 'failed', signature: ['unavailable', 'timeout', 'aborted'].includes(error.code)
43
- ? 'queue_pre_accept_transient' : 'queue_pre_accept_rejected', message };
43
+ return error instanceof DeliveryError && !error.maybeDelivered ? 'not-capable' : 'unknown';
44
44
  }
45
45
  }
46
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, kinds: ['say'] }, { 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,7 +194,7 @@ 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, { kinds: ['say'], afterIndex: readCursor(facts, current.name) }) : 0,
197
+ unreadActivityCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: readCursor(facts, current.name), unseenOnly: true }) : 0,
198
198
  pendingMentionCount: current.joined ? facts.directedCount(current.name, { kinds: ['say'], afterIndex: current.lastJoinIndex ?? -1, unseenOnly: true }) : 0,
199
199
  };
200
200
  }).sort(compareParticipantActivity);
@@ -1,19 +1,10 @@
1
- import type { MessageMetadata } from './channel-values.js';
2
- import type { ChannelRead, EndpointRef } from './channel-ports.js';
3
- import type { MetadataMatch } from './channel-metadata.js';
4
- export type { MetadataMatch, MetadataScalar, DeliveryDeferral } from './channel-metadata.js';
5
- export interface DeliveryDeferralFact {
6
- readonly participant: string;
7
- readonly generation: string;
8
- readonly id: string;
9
- readonly match: MetadataMatch;
10
- readonly expiresAt: number;
1
+ import type { ActivityMetadata } from './model.js';
2
+ export type MetadataScalar = null | boolean | number | string;
3
+ export interface MetadataMatch {
4
+ readonly fields: readonly Readonly<{
5
+ path: readonly string[];
6
+ equals: MetadataScalar;
7
+ }>[];
11
8
  }
12
- export declare const MAX_DELIVERY_DEFERRAL_MS: number;
13
9
  export declare function validateMetadataMatch(value: unknown): MetadataMatch;
14
- export declare function validateDeferralDuration(value: unknown): number;
15
- export declare function validateDeferralId(value: unknown): string;
16
- export declare function matchesMetadata(metadata: MessageMetadata | undefined, match: MetadataMatch): boolean;
17
- export declare function matchingDeferrals(deferrals: readonly DeliveryDeferralFact[], metadata: MessageMetadata | undefined, now: number): readonly DeliveryDeferralFact[];
18
- export declare function deliveryDeferralExpiry(facts: ChannelRead, captured: EndpointRef, index: number, now: number): number | undefined;
19
- export declare function earliestMatchingDeferralExpiry(deferrals: readonly DeliveryDeferralFact[], metadata: MessageMetadata | undefined, now: number): number | undefined;
10
+ export declare function matchesMetadata(metadata: ActivityMetadata | undefined, match: MetadataMatch): boolean;
@@ -1,5 +1,4 @@
1
1
  import { SquareError } from './neutral.js';
2
- export const MAX_DELIVERY_DEFERRAL_MS = 24 * 60 * 60 * 1000;
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.endpoint(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 message = facts.message(index);
78
- return message === undefined ? undefined : earliestMatchingDeferralExpiry(leases, message.metadata, now);
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
+ }