@astrosheep/square 0.3.85 → 0.3.87

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 (148) hide show
  1. package/dist/artifact.d.ts +6 -1
  2. package/dist/artifact.js +79 -10
  3. package/dist/boundary-presentation.d.ts +5 -4
  4. package/dist/boundary-presentation.js +21 -25
  5. package/dist/catch-decisions.js +2 -1
  6. package/dist/claude-delivery-status.d.ts +8 -2
  7. package/dist/claude-delivery-status.js +26 -10
  8. package/dist/claude-delivery.d.ts +8 -4
  9. package/dist/claude-delivery.js +114 -104
  10. package/dist/claude-mcp-context.js +2 -2
  11. package/dist/claude-mod.js +119 -105
  12. package/dist/cli/context.d.ts +3 -1
  13. package/dist/cli/context.js +25 -1
  14. package/dist/cli/express-presentation.d.ts +3 -1
  15. package/dist/cli/express-presentation.js +15 -12
  16. package/dist/cli/mcp-server-command.js +2 -2
  17. package/dist/cli/observation-commands.js +2 -1
  18. package/dist/cli/registry.js +0 -2
  19. package/dist/cli/square-commands.d.ts +4 -2
  20. package/dist/cli/square-commands.js +21 -6
  21. package/dist/codex-hook.d.ts +3 -3
  22. package/dist/codex-hook.js +4 -4
  23. package/dist/codex-queue.d.ts +5 -6
  24. package/dist/codex-queue.js +9 -5
  25. package/dist/decisions.d.ts +3 -2
  26. package/dist/decisions.js +18 -6
  27. package/dist/delivery-health.d.ts +32 -2
  28. package/dist/delivery-health.js +108 -45
  29. package/dist/delivery-operations.d.ts +1 -2
  30. package/dist/delivery-operations.js +155 -196
  31. package/dist/delivery.d.ts +14 -23
  32. package/dist/delivery.js +48 -18
  33. package/dist/file-lock.d.ts +8 -1
  34. package/dist/file-lock.js +2 -0
  35. package/dist/help.js +12 -14
  36. package/dist/hints.js +2 -3
  37. package/dist/inbox.d.ts +35 -9
  38. package/dist/inbox.js +228 -144
  39. package/dist/index.d.ts +4 -4
  40. package/dist/index.js +1 -3
  41. package/dist/list.js +9 -4
  42. package/dist/mcp.js +12 -1
  43. package/dist/model.d.ts +39 -5
  44. package/dist/model.js +84 -0
  45. package/dist/notifications.d.ts +8 -6
  46. package/dist/notifications.js +56 -143
  47. package/dist/operation-presentation.js +12 -14
  48. package/dist/participant-identity.d.ts +0 -3
  49. package/dist/participant-identity.js +0 -31
  50. package/dist/paseo-delivery.d.ts +5 -6
  51. package/dist/paseo-delivery.js +13 -8
  52. package/dist/paseo.d.ts +1 -1
  53. package/dist/paseo.js +1 -1
  54. package/dist/plugins/opencode/delivery.d.ts +8 -3
  55. package/dist/plugins/opencode/delivery.js +187 -132
  56. package/dist/plugins/opencode/index.js +12 -38
  57. package/dist/ports.d.ts +112 -40
  58. package/dist/presentation-operations.d.ts +1 -1
  59. package/dist/presentation-operations.js +51 -35
  60. package/dist/presentation.d.ts +0 -1
  61. package/dist/presentation.js +18 -13
  62. package/dist/seats.d.ts +7 -0
  63. package/dist/seats.js +27 -0
  64. package/dist/session-lifecycle.d.ts +2 -2
  65. package/dist/session-lifecycle.js +10 -12
  66. package/dist/sqlite-square.d.ts +53 -0
  67. package/dist/sqlite-square.js +602 -0
  68. package/dist/square-api.d.ts +12 -41
  69. package/dist/square-api.js +26 -474
  70. package/dist/square-backend.d.ts +13 -0
  71. package/dist/square-core.d.ts +3 -2
  72. package/dist/square-domain.d.ts +12 -5
  73. package/dist/square-domain.js +163 -7
  74. package/dist/square-facade.d.ts +16 -10
  75. package/dist/square-index.d.ts +24 -0
  76. package/dist/square-index.js +131 -0
  77. package/dist/square-legacy-framing.d.ts +9 -0
  78. package/dist/square-legacy-framing.js +56 -0
  79. package/dist/square-location.d.ts +15 -0
  80. package/dist/square-location.js +76 -0
  81. package/dist/square-migration-host-ledger.d.ts +26 -0
  82. package/dist/square-migration-host-ledger.js +168 -0
  83. package/dist/square-migration.d.ts +5 -5
  84. package/dist/square-migration.js +373 -71
  85. package/dist/square-sqlite.d.ts +9 -9
  86. package/dist/square-sqlite.js +329 -79
  87. package/dist/square-stdio.d.ts +22 -0
  88. package/dist/square-stdio.js +222 -0
  89. package/dist/square-tools.js +30 -3
  90. package/dist/stdio-server.d.ts +3 -0
  91. package/dist/stdio-server.js +192 -0
  92. package/dist/stream.d.ts +3 -2
  93. package/dist/stream.js +9 -10
  94. package/dist/views.d.ts +9 -14
  95. package/dist/views.js +50 -70
  96. package/dist/wakes.d.ts +6 -5
  97. package/dist/wakes.js +20 -17
  98. package/guides/application-facade-delivery-evidence.md +1 -1
  99. package/guides/backends.md +180 -0
  100. package/guides/claude-native.md +9 -9
  101. package/guides/mcp.md +1 -1
  102. package/guides/migration.md +107 -67
  103. package/guides/row-storage-design.md +1 -1
  104. package/guides/square-api.md +69 -30
  105. package/package.json +1 -1
  106. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  107. package/plugins/claude/hooks/register.js +1 -1
  108. package/plugins/claude/skills/square/SKILL.md +7 -7
  109. package/plugins/claude/types.d.ts +1 -1
  110. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  111. package/plugins/opencode/delivery.ts +170 -118
  112. package/plugins/opencode/index.ts +12 -30
  113. package/plugins/pi/square-pi.js +58 -20
  114. package/skills/square/SKILL.md +7 -7
  115. package/dist/cli/ledger-command.d.ts +0 -12
  116. package/dist/cli/ledger-command.js +0 -72
  117. package/dist/host-ledger-file-adapter.d.ts +0 -47
  118. package/dist/host-ledger-file-adapter.js +0 -384
  119. package/dist/host-ledger-migration.d.ts +0 -27
  120. package/dist/host-ledger-migration.js +0 -264
  121. package/dist/host-ledger-root.d.ts +0 -2
  122. package/dist/host-ledger-root.js +0 -9
  123. package/dist/host-ledger-sqlite.d.ts +0 -65
  124. package/dist/host-ledger-sqlite.js +0 -368
  125. package/dist/host-ledger.d.ts +0 -195
  126. package/dist/ledger-maintenance.d.ts +0 -30
  127. package/dist/ledger-maintenance.js +0 -110
  128. package/dist/open-square.d.ts +0 -36
  129. package/dist/open-square.js +0 -81
  130. package/dist/participant-host.d.ts +0 -22
  131. package/dist/participant-host.js +0 -92
  132. package/dist/registry.d.ts +0 -55
  133. package/dist/registry.js +0 -123
  134. package/dist/routes.d.ts +0 -39
  135. package/dist/routes.js +0 -99
  136. package/dist/square-actions.d.ts +0 -56
  137. package/dist/square-actions.js +0 -353
  138. package/dist/square-projections.d.ts +0 -31
  139. package/dist/square-projections.js +0 -64
  140. package/dist/wake-attempts.d.ts +0 -10
  141. package/dist/wake-attempts.js +0 -39
  142. package/dist/wake-eligibility.d.ts +0 -53
  143. package/dist/wake-eligibility.js +0 -123
  144. package/dist/wake-evidence.d.ts +0 -33
  145. package/dist/wake-evidence.js +0 -32
  146. package/guides/host-ledger.md +0 -58
  147. package/guides/indexed-host-ledger-performance.md +0 -39
  148. /package/dist/{host-ledger.js → square-backend.js} +0 -0
@@ -1,7 +1,10 @@
1
1
  import { DatabaseSync } from 'node:sqlite';
2
2
  import { type BuildOptions } from './model.js';
3
- import type { SquareArtifactPort } from './ports.js';
3
+ import type { SquareResourceOwner, SquareArtifactPort } from './ports.js';
4
4
  import { type SquareState } from './square-sqlite.js';
5
+ /** Explicit-lifetime holder of one reused artifact connection. Request cancellation
6
+ * never becomes the shared connection's lifetime; close drains the owner once. */
7
+ export declare function createSquareResourceOwner(): SquareResourceOwner;
5
8
  /** All build paths share configuration validation and lazy guide preparation. */
6
9
  export declare function prepareSquareBuild(options: Omit<BuildOptions, 'force'>, snippet: string): SquareState;
7
10
  /** File build owns publication and CLI conflict semantics, not host resources. */
@@ -32,4 +35,6 @@ export declare function writeSquareFile(location: string, state: SquareState, si
32
35
  export declare function readSquareSnapshot(location: string, signal?: AbortSignal): Promise<SquareSnapshot>;
33
36
  export declare function readSquareRevision(location: string, signal?: AbortSignal): Promise<number>;
34
37
  export declare function observeSquareRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
38
+ export declare function readSquareDeliveryRevision(location: string, signal?: AbortSignal): Promise<number>;
39
+ export declare function observeSquareDeliveryRevision(location: string): Promise<import('./file-changes.js').VersionObserver<number | null>>;
35
40
  export declare function diagnoseSquareFile(location: string): Promise<DiagnoseResult>;
package/dist/artifact.js CHANGED
@@ -10,6 +10,33 @@ import { canonicalPath } from './canonical-path.js';
10
10
  import { InternalSquareError, SquareError } from './model.js';
11
11
  import { wrapSquareWrite } from './square-domain.js';
12
12
  import { APPLICATION_ID, CURRENT_USER_VERSION, createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, invalidArtifact, validateSqliteHeader, emptyRuntimeState } from './square-sqlite.js';
13
+ /** Explicit-lifetime holder of one reused artifact connection. Request cancellation
14
+ * never becomes the shared connection's lifetime; close drains the owner once. */
15
+ export function createSquareResourceOwner() {
16
+ let held;
17
+ let closed = false;
18
+ return {
19
+ artifact(location) {
20
+ if (closed)
21
+ throw new SquareError('unavailable', 'Square resource owner is closed');
22
+ const key = path.resolve(location);
23
+ if (held === undefined)
24
+ held = { location: key, artifact: createArtifactPort(key) };
25
+ if (held.location !== key)
26
+ throw new SquareError('unavailable', `Square resource owner already holds ${held.location}; it cannot also open ${key}`);
27
+ return held.artifact;
28
+ },
29
+ async close() {
30
+ if (closed)
31
+ return;
32
+ closed = true;
33
+ const artifact = held?.artifact;
34
+ held = undefined;
35
+ if (artifact !== undefined)
36
+ await artifact.close();
37
+ },
38
+ };
39
+ }
13
40
  const guideContents = new Map();
14
41
  function readGuide(name) {
15
42
  if (!['participant', 'architect', 'brainstorm'].includes(name))
@@ -40,7 +67,7 @@ export function prepareSquareBuild(options, snippet) {
40
67
  hardCap: options.hardCap ?? null,
41
68
  ...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
42
69
  preamble: normalizedLines(snippet), warmup: normalizedLines(guides.join('\n\n')),
43
- acts: [], routes: [], runtime: emptyRuntimeState(),
70
+ acts: [], runtime: emptyRuntimeState(),
44
71
  };
45
72
  }
46
73
  /** File build owns publication and CLI conflict semantics, not host resources. */
@@ -93,9 +120,10 @@ const readMethods = [
93
120
  'header', 'participant', 'roster', 'attention', 'incomingAttention', 'hold',
94
121
  'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
95
122
  'audience', 'directed', 'directedAt', 'directedCount', 'pendingAudience',
96
- 'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'routes', 'participantCounts',
123
+ 'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', 'seatsForBody', 'completedSeatsForBody',
124
+ 'deliveryEvidence', 'deliveryByToken', 'cancelledThrough',
97
125
  ];
98
- const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'replaceRoutes'];
126
+ const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery'];
99
127
  /** One connection, ordered operations, guarded synchronous transaction capabilities. */
100
128
  export function createArtifactPort(location, initial, externalSignal) {
101
129
  let database;
@@ -146,7 +174,7 @@ export function createArtifactPort(location, initial, externalSignal) {
146
174
  tail = result.then(() => undefined, () => undefined);
147
175
  return result;
148
176
  }
149
- async function transaction(callback, write, signal) {
177
+ async function transaction(callback, write, signal, authority) {
150
178
  const db = await connection(signal);
151
179
  return inSqliteTransaction(db, write, () => validate(db), (header) => {
152
180
  const driver = createSqliteDriver(db), version = header.revision;
@@ -170,12 +198,15 @@ export function createArtifactPort(location, initial, externalSignal) {
170
198
  }
171
199
  const reads = Object.fromEntries(readMethods.map(name => [name, guard(driver[name])]));
172
200
  const writes = Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])]));
173
- const facts = write ? wrapSquareWrite(reads, writes) : reads;
201
+ const gate = write ? wrapSquareWrite(reads, writes, authority) : undefined;
202
+ const facts = gate?.facts ?? reads;
174
203
  let value;
175
204
  inCallback = true;
176
205
  try {
177
206
  value = callback(facts);
178
207
  synchronous(value);
208
+ if (!failed)
209
+ gate?.complete();
179
210
  }
180
211
  finally {
181
212
  inCallback = false;
@@ -184,13 +215,14 @@ export function createArtifactPort(location, initial, externalSignal) {
184
215
  if (failed)
185
216
  throw failure;
186
217
  const changed = write && driver.changed;
187
- if (changed) {
218
+ const deliveryChanged = write && driver.deliveryChanged;
219
+ if (changed || deliveryChanged) {
188
220
  if (version === Number.MAX_SAFE_INTEGER)
189
221
  throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
190
- db.prepare('UPDATE square_meta SET revision=? WHERE id=1').run(version + 1);
222
+ db.prepare('UPDATE square_meta SET revision=revision+?, delivery_revision=delivery_revision+? WHERE id=1').run(Number(changed), Number(deliveryChanged));
191
223
  }
192
224
  return {
193
- value, version: version + Number(changed), changed
225
+ value, version: version + Number(changed), changed, deliveryChanged
194
226
  };
195
227
  }, signal);
196
228
  }
@@ -203,8 +235,29 @@ export function createArtifactPort(location, initial, externalSignal) {
203
235
  const { value, version } = await transaction(select, false, s);
204
236
  return { value, version };
205
237
  }, signal),
206
- transact: (change, signal) => queue(s => transaction(change, true, s), signal),
238
+ transactAs: (authority, change, signal) => queue(s => transaction(change, true, s, authority), signal),
207
239
  revision,
240
+ async deliveryRevision(signal) {
241
+ return queue(async (s) => {
242
+ const db = await connection(s);
243
+ return inSqliteTransaction(db, false, () => validate(db), (header) => header.deliveryRevision, s);
244
+ }, signal);
245
+ },
246
+ async claimDelivery(input, signal) {
247
+ return (await this.transactAs({ kind: 'participant', seat: input.seat }, (facts) => facts.claimDelivery(input), signal)).value;
248
+ },
249
+ async prepareDelivery(input, signal) {
250
+ return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.prepareDelivery(input), signal)).value;
251
+ },
252
+ async completeDelivery(input, signal) {
253
+ return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.completeDelivery(input), signal)).value;
254
+ },
255
+ async cancelDeliveries(seat, through, signal) {
256
+ return (await this.transactAs({ kind: 'participant', seat }, facts => {
257
+ const boundary = through ?? facts.header().lastActIndex;
258
+ return boundary < 0 ? false : facts.cancelThrough(seat, boundary);
259
+ }, signal)).value;
260
+ },
208
261
  async changed(since, timeout, signal) {
209
262
  if (inCallback)
210
263
  throw new TypeError('Square artifact operations cannot nest inside a callback.');
@@ -321,7 +374,7 @@ export async function writeSquareFile(location, state, signal) {
321
374
  const revision = createSqliteDriver(db).header().revision;
322
375
  if (revision === Number.MAX_SAFE_INTEGER)
323
376
  throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
324
- for (const table of ['landed_audience', 'attention', 'participants', 'routes', 'catch_leases', 'receipts', 'activities', 'square_meta'])
377
+ for (const table of ['landed_audience', 'attention', 'participants', 'delivery_cancellations', 'delivery_evidence', 'seats', 'catch_leases', 'receipts', 'activities', 'square_meta'])
325
378
  db.exec(`DROP TABLE ${table}`);
326
379
  initializeSqlite(db, state, revision + 1);
327
380
  }, signal);
@@ -362,6 +415,22 @@ export function observeSquareRevision(location) {
362
415
  }
363
416
  });
364
417
  }
418
+ export function readSquareDeliveryRevision(location, signal) {
419
+ return inspectFile(location, db => createSqliteDriver(db).header().deliveryRevision, signal);
420
+ }
421
+ export function observeSquareDeliveryRevision(location) {
422
+ return observeFileVersion(location, 'square-delivery-revision', async (file, signal) => {
423
+ try {
424
+ return await readSquareDeliveryRevision(file, signal);
425
+ }
426
+ catch (error) {
427
+ abort(signal);
428
+ if (error instanceof InternalSquareError && error.code === 'not_found')
429
+ return null;
430
+ throw error;
431
+ }
432
+ });
433
+ }
365
434
  export async function diagnoseSquareFile(location) {
366
435
  try {
367
436
  return { problems: [], ...await inspectFile(await canonicalPath(location), inspectSqlite) };
@@ -1,6 +1,7 @@
1
- import type { InboxMembership } from './model.js';
2
- export { pendingAtBoundary } from './square-projections.js';
3
- export declare function renderPendingAtBoundary(inbox: InboxMembership[]): string;
1
+ import { pendingAtBoundary } from './inbox.js';
2
+ import type { Body, PendingMembership } from './model.js';
3
+ export { pendingAtBoundary };
4
+ export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
4
5
  /**
5
6
  * Serialize a boundary across hook processes so one stale inbox cannot render twice.
6
7
  *
@@ -9,4 +10,4 @@ export declare function renderPendingAtBoundary(inbox: InboxMembership[]): strin
9
10
  * presentation evidence live, and `presentPending` still re-checks the pending head, join, ownership
10
11
  * claim and seen CAS. A source that cannot verify its reuse must read the inbox again.
11
12
  */
12
- export declare function presentPendingAtBoundary<T>(sessionId: string, present: (context: string) => T | Promise<T>, lookup?: (sessionId: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<InboxMembership[]> | InboxMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
13
+ export declare function presentPendingAtBoundary<T>(identity: Body, present: (context: string) => T | Promise<T>, lookup?: (identity: Body, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
@@ -1,15 +1,11 @@
1
- import { hostLedgerRoot } from './host-ledger-root.js';
1
+ import { squareIndexPath } from './square-index.js';
2
2
  import { createHash } from 'node:crypto';
3
- import path from 'node:path';
4
3
  import { withFileLock } from './file-lock.js';
5
- import { openSquare } from './open-square.js';
6
- import { closeOpenSquare } from './open-square.js';
4
+ import { createArtifactPort } from './artifact.js';
7
5
  import { presentPending } from './presentation-operations.js';
8
- import { hostLedgerForEnv } from './registry.js';
9
- import { sessionInbox } from './inbox.js';
6
+ import { sessionInbox, projectBoundaryEligibility, pendingAtBoundary, pendingKey } from './inbox.js';
10
7
  import { attentionBodyIsClipped, renderAttentionPreview, renderSquareReminder } from './attention-presentation.js';
11
- import { projectBoundaryEligibility } from './square-projections.js';
12
- export { pendingAtBoundary } from './square-projections.js';
8
+ export { pendingAtBoundary };
13
9
  const CONTEXT_MAX = 1200;
14
10
  const presentationLocks = new Map();
15
11
  /** One settled promise loses to an abort so an in-process presentation wait stays bounded too. */
@@ -48,9 +44,9 @@ function renderBoundary(inbox) {
48
44
  const { membership, notification } = entry;
49
45
  const block = [
50
46
  renderAttentionPreview({
51
- squarePath: membership.squarePath,
47
+ squarePath: membership.location,
52
48
  actIndex: notification.actIndex,
53
- recipient: membership.name,
49
+ recipient: membership.seat.participant,
54
50
  actor: notification.actor,
55
51
  route: notification.route,
56
52
  body: notification.body,
@@ -74,12 +70,11 @@ function renderBoundary(inbox) {
74
70
  complete,
75
71
  };
76
72
  }
77
- async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = sessionInbox, env = process.env, signal) {
78
- const inbox = await lookup(sessionId, env, signal);
73
+ async function presentPendingAtBoundaryUnlocked(identity, present, lookup = sessionInbox, env = process.env, signal) {
74
+ const inbox = await lookup(identity, env, signal);
79
75
  if (signal?.aborted)
80
76
  return undefined;
81
- const hostLedger = hostLedgerForEnv(env);
82
- const pending = await projectBoundaryEligibility({ hostLedger, sessionId, inbox });
77
+ const pending = await projectBoundaryEligibility(inbox);
83
78
  if (pending.length === 0 || signal?.aborted)
84
79
  return undefined;
85
80
  const delivered = renderBoundary(pending);
@@ -92,16 +87,17 @@ async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = ses
92
87
  result = await present(delivered.context);
93
88
  } };
94
89
  for (const entry of delivered.complete) {
95
- let square;
90
+ let artifact;
96
91
  try {
92
+ artifact = createArtifactPort(entry.membership.location, undefined, signal);
97
93
  try {
98
- square = await openSquare(entry.membership.squarePath, { env, signal });
94
+ await artifact.read(facts => facts.header(), signal);
99
95
  }
100
96
  catch {
101
97
  continue;
102
98
  }
103
99
  for (const index of entry.actIndexes) {
104
- const key = `${entry.membership.squarePath}\u0000${entry.membership.name.toLocaleLowerCase()}\u0000${index}`;
100
+ const key = pendingKey(entry.membership, index);
105
101
  const prior = presentationLocks.get(key);
106
102
  if (prior !== undefined) {
107
103
  await awaitWithSignal(prior, signal);
@@ -111,11 +107,11 @@ async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = ses
111
107
  return undefined;
112
108
  continue;
113
109
  }
114
- const work = presentPending({ artifact: square.artifact, location: entry.membership.squarePath, participant: entry.membership.name, activity: index, hostLedger: square.hostLedger, session: sessionId, sink: { present: renderOnce }, markSeen: entry.markSeen, now: Date.now(), signal }).then(async (outcome) => {
110
+ const work = presentPending({ artifact, location: entry.membership.location, participant: entry.membership.seat.participant, activity: index, seat: entry.membership.seat, sink: { present: renderOnce }, markSeen: entry.markSeen, now: Date.now(), signal }).then(async (outcome) => {
115
111
  // A stale projection can outlive its activity; still surface the bounded preview,
116
112
  // but there is no artifact observation to commit.
117
113
  if (!outcome.presented) {
118
- const exists = await square.artifact.read((facts) => facts.activity(index) !== undefined, signal).catch(() => undefined);
114
+ const exists = await artifact.read((facts) => facts.activity(index) !== undefined, signal).catch(() => undefined);
119
115
  if (exists?.value === false)
120
116
  await renderOnce();
121
117
  }
@@ -132,8 +128,8 @@ async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = ses
132
128
  }
133
129
  }
134
130
  finally {
135
- if (square !== undefined)
136
- await closeOpenSquare(square);
131
+ if (artifact !== undefined)
132
+ await artifact.close();
137
133
  }
138
134
  }
139
135
  return result;
@@ -146,8 +142,8 @@ async function presentPendingAtBoundaryUnlocked(sessionId, present, lookup = ses
146
142
  * presentation evidence live, and `presentPending` still re-checks the pending head, join, ownership
147
143
  * claim and seen CAS. A source that cannot verify its reuse must read the inbox again.
148
144
  */
149
- export async function presentPendingAtBoundary(sessionId, present, lookup = sessionInbox, env = process.env, signal) {
150
- const ledgerRoot = hostLedgerRoot(env);
151
- const ownerKey = createHash('sha256').update(sessionId).digest('hex');
152
- return withFileLock(path.join(ledgerRoot, `presentation-boundary-${ownerKey}.lock`), { retryMs: 10, signal }, () => presentPendingAtBoundaryUnlocked(sessionId, present, lookup, env, signal));
145
+ export async function presentPendingAtBoundary(identity, present, lookup = sessionInbox, env = process.env, signal) {
146
+ const indexPath = squareIndexPath(env);
147
+ const ownerKey = createHash('sha256').update(JSON.stringify(identity)).digest('hex');
148
+ return withFileLock(`${indexPath}.boundary-${ownerKey}.lock`, { retryMs: 10, signal }, () => presentPendingAtBoundaryUnlocked(identity, present, lookup, env, signal));
153
149
  }
@@ -31,7 +31,8 @@ export function catchLimit(value) {
31
31
  export function selectCatchPage(facts, requestedName, options) {
32
32
  const participant = resolveKnownName(facts, requestedName);
33
33
  const limit = catchLimit(options.limit);
34
- const filter = { unseenOnly: true, ...(options.from === undefined ? {} : { senders: options.from }), ...(options.mention === true ? { attentionOnly: true } : {}) };
34
+ const senders = options.from?.map((name) => resolveKnownName(facts, name));
35
+ const filter = { unseenOnly: true, ...(senders === undefined ? {} : { senders }), ...(options.mention === true ? { attentionOnly: true } : {}) };
35
36
  return { participant, indexes: facts.directed(participant, filter, { edge: 'oldest', limit }).items.map((item) => item.index), filter };
36
37
  }
37
38
  /** Selection, receipts and resulting cursor belong to one artifact write transaction. */
@@ -1,5 +1,11 @@
1
- import type { NativeDeliveryObservation } from './host-ledger.js';
2
- export interface ClaudeDeliveryStatus extends NativeDeliveryObservation {
1
+ export interface ClaudeDeliveryObservation {
2
+ readonly eventId: string;
3
+ readonly messageId: string;
4
+ readonly state: 'held' | 'released' | 'dropped';
5
+ readonly reason?: string;
6
+ readonly observedAt: number;
7
+ }
8
+ export interface ClaudeDeliveryStatus extends ClaudeDeliveryObservation {
3
9
  readonly provider: 'claude';
4
10
  readonly sessionId: string;
5
11
  }
@@ -1,4 +1,5 @@
1
- import { hostLedgerForEnv } from './registry.js';
1
+ import { createArtifactPort } from './artifact.js';
2
+ import { indexedSquareEntries } from './square-index.js';
2
3
  const holdReasons = new Set(['explicit-setting', 'managed-setting', 'repo-setting', 'invalid-setting', 'bypass-default', 'mode-unknown', 'mode-mismatch', 'no-mode-asserted']);
3
4
  const dropReasons = new Set(['expired', 'refused', 'dropped', 'discarded']);
4
5
  const uuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -12,13 +13,28 @@ export async function recordClaudeDeliveryStatus(input, env = process.env) {
12
13
  || (input.state === 'held' ? !holdReasons.has(input.reason ?? '')
13
14
  : input.state === 'dropped' ? !dropReasons.has(input.reason ?? '') : input.reason !== undefined))
14
15
  return false;
15
- const ledger = hostLedgerForEnv(env);
16
- const rows = await ledger.listEvidence({ kind: 'wake', session: input.sessionId, includeReleased: true });
17
- const row = rows.find((row) => row.nativeDelivery?.harness === 'claude' && row.nativeDelivery.messageId === input.messageId);
18
- if (!row?.claimToken)
19
- return false;
20
- const { eventId, messageId, state, reason, observedAt } = input;
21
- return ledger.observeNativeDelivery({ ...row, claimToken: row.claimToken }, {
22
- eventId, messageId, state, ...(reason === undefined ? {} : { reason }), observedAt,
23
- });
16
+ for (const entry of await indexedSquareEntries({ channel: 'claude-code', session: input.sessionId }, env)) {
17
+ const artifact = createArtifactPort(entry.location);
18
+ try {
19
+ const { value: match } = await artifact.read((facts) => facts.roster().flatMap((participant) => {
20
+ const seat = facts.seat(participant.name);
21
+ if (seat === undefined || seat.body.session !== input.sessionId || seat.body.channel !== 'claude-code')
22
+ return [];
23
+ const evidence = facts.deliveryEvidence(seat.participant, seat.generation).find((row) => row.correlation?.harness === 'claude' && row.correlation.messageId === input.messageId && row.claimToken !== undefined);
24
+ return evidence === undefined ? [] : [{ seat, evidence }];
25
+ }));
26
+ if (match.length === 0)
27
+ continue;
28
+ const { seat, evidence } = match[0];
29
+ const ref = { participant: seat.participant, body: seat.body, generation: seat.generation };
30
+ return (await artifact.transactAs({ kind: 'participant', seat: ref }, (facts) => facts.observeNativeDelivery({ seat: ref, claimToken: evidence.claimToken, observation: {
31
+ eventId: input.eventId, messageId: input.messageId, state: input.state,
32
+ ...(input.reason === undefined ? {} : { reason: input.reason }), observedAt: input.observedAt,
33
+ } }))).value;
34
+ }
35
+ finally {
36
+ await artifact.close();
37
+ }
38
+ }
39
+ return false;
24
40
  }
@@ -1,11 +1,15 @@
1
- import type { HostLedgerPort, PresenceRecord } from './host-ledger.js';
2
1
  import type { WakeTransportPort } from './ports.js';
3
2
  export declare function nativeTokens(text: string): string[];
4
3
  export declare function nativePayload(token: string, preview: string): string;
5
- export declare function createClaudeWakeTransport(hostLedger: HostLedgerPort, env?: NodeJS.ProcessEnv): WakeTransportPort;
6
- /** Retired tokens veto a frame; only live correlations and bound memberships confer authority. */
4
+ export declare function createClaudeWakeTransport(env?: NodeJS.ProcessEnv): WakeTransportPort;
5
+ /** Recognize the whole frame first; one inactive token vetoes it before any evidence/receipt write. */
7
6
  export declare function observeClaudeDelivery(sessionId: string, text: string, operation: 'guard' | 'admitted' | 'stored', env?: NodeJS.ProcessEnv): Promise<{
8
7
  recognized: boolean;
9
8
  current: boolean;
10
9
  }>;
11
- export type ClaudeBinding = Pick<PresenceRecord, 'location' | 'participant' | 'session' | 'epoch'>;
10
+ export type ClaudeBinding = {
11
+ readonly location: string;
12
+ readonly participant: string;
13
+ readonly session: string;
14
+ readonly generation?: import('./square-core.js').ActivityId;
15
+ };