@astrosheep/square 0.3.93 → 0.3.95

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 (146) hide show
  1. package/dist/activity-feed.d.ts +3 -5
  2. package/dist/activity-feed.js +16 -14
  3. package/dist/artifact.js +3 -3
  4. package/dist/attention.d.ts +19 -0
  5. package/dist/attention.js +30 -0
  6. package/dist/boundary-presentation.d.ts +2 -2
  7. package/dist/boundary-presentation.js +33 -40
  8. package/dist/catch-decisions.d.ts +5 -8
  9. package/dist/catch-decisions.js +45 -25
  10. package/dist/claude-delivery-status.js +10 -4
  11. package/dist/claude-delivery.js +19 -11
  12. package/dist/claude-mod.js +24 -53
  13. package/dist/cli/context.d.ts +9 -4
  14. package/dist/cli/context.js +30 -27
  15. package/dist/cli/express-presentation.d.ts +3 -1
  16. package/dist/cli/express-presentation.js +28 -16
  17. package/dist/cli/json.d.ts +3 -0
  18. package/dist/cli/json.js +14 -0
  19. package/dist/cli/mcp-server-command.js +2 -2
  20. package/dist/cli/observation-commands.d.ts +1 -3
  21. package/dist/cli/observation-commands.js +43 -56
  22. package/dist/cli/program.js +3 -3
  23. package/dist/cli/registry.js +7 -6
  24. package/dist/cli/square-commands.d.ts +0 -2
  25. package/dist/cli/square-commands.js +30 -44
  26. package/dist/codex-hook.d.ts +3 -2
  27. package/dist/codex-hook.js +13 -4
  28. package/dist/codex-queue.js +4 -4
  29. package/dist/decisions.d.ts +5 -16
  30. package/dist/decisions.js +9 -33
  31. package/dist/delivery-attempts.d.ts +52 -0
  32. package/dist/delivery-attempts.js +73 -0
  33. package/dist/delivery-deferral.js +2 -2
  34. package/dist/delivery-effects.d.ts +139 -0
  35. package/dist/delivery-effects.js +163 -0
  36. package/dist/delivery-health.d.ts +0 -1
  37. package/dist/delivery-health.js +1 -3
  38. package/dist/delivery-operations.d.ts +2 -2
  39. package/dist/delivery-operations.js +76 -101
  40. package/dist/delivery-primitives.d.ts +5 -0
  41. package/dist/delivery-primitives.js +3 -0
  42. package/dist/delivery.d.ts +1 -1
  43. package/dist/delivery.js +7 -6
  44. package/dist/help.js +14 -14
  45. package/dist/http-delivery.d.ts +16 -0
  46. package/dist/http-delivery.js +101 -0
  47. package/dist/inbox.d.ts +9 -22
  48. package/dist/inbox.js +23 -27
  49. package/dist/index.d.ts +1 -3
  50. package/dist/index.js +1 -1
  51. package/dist/mcp.d.ts +3 -0
  52. package/dist/mcp.js +23 -24
  53. package/dist/model.d.ts +12 -21
  54. package/dist/model.js +5 -8
  55. package/dist/notifications.d.ts +1 -1
  56. package/dist/notifications.js +8 -5
  57. package/dist/operation-presentation.d.ts +6 -6
  58. package/dist/operation-presentation.js +55 -58
  59. package/dist/owner.d.ts +11 -0
  60. package/dist/owner.js +32 -0
  61. package/dist/paseo-delivery.d.ts +4 -1
  62. package/dist/paseo-delivery.js +7 -12
  63. package/dist/paseo-state.d.ts +2 -2
  64. package/dist/paseo-state.js +6 -4
  65. package/dist/plugins/opencode/delivery.js +27 -15
  66. package/dist/ports.d.ts +18 -47
  67. package/dist/presentation-operations.d.ts +14 -19
  68. package/dist/presentation-operations.js +51 -61
  69. package/dist/presentation.d.ts +7 -17
  70. package/dist/presentation.js +46 -91
  71. package/dist/receive.d.ts +66 -0
  72. package/dist/receive.js +54 -0
  73. package/dist/runtime.d.ts +1 -8
  74. package/dist/runtime.js +2 -17
  75. package/dist/seats.d.ts +2 -3
  76. package/dist/seats.js +7 -15
  77. package/dist/session-lifecycle.d.ts +6 -2
  78. package/dist/session-lifecycle.js +39 -11
  79. package/dist/sqlite-square.d.ts +36 -23
  80. package/dist/sqlite-square.js +162 -211
  81. package/dist/square-api.d.ts +16 -11
  82. package/dist/square-api.js +27 -23
  83. package/dist/square-core.d.ts +0 -14
  84. package/dist/square-core.js +0 -9
  85. package/dist/square-domain.d.ts +4 -2
  86. package/dist/square-domain.js +65 -52
  87. package/dist/square-facade.d.ts +47 -105
  88. package/dist/square-index.d.ts +5 -13
  89. package/dist/square-index.js +13 -38
  90. package/dist/square-legacy-framing.d.ts +2 -0
  91. package/dist/square-legacy-framing.js +8 -0
  92. package/dist/square-location.d.ts +1 -14
  93. package/dist/square-location.js +4 -70
  94. package/dist/square-migration-host-ledger.d.ts +2 -2
  95. package/dist/square-migration-host-ledger.js +6 -5
  96. package/dist/square-migration.d.ts +4 -1
  97. package/dist/square-migration.js +93 -38
  98. package/dist/square-receive.d.ts +6 -0
  99. package/dist/square-receive.js +18 -0
  100. package/dist/square-sqlite.d.ts +9 -3
  101. package/dist/square-sqlite.js +139 -114
  102. package/dist/square-tools.d.ts +7 -10
  103. package/dist/square-tools.js +21 -61
  104. package/dist/stream.js +4 -5
  105. package/dist/views.d.ts +23 -13
  106. package/dist/views.js +47 -53
  107. package/dist/wakes.d.ts +1 -5
  108. package/dist/wakes.js +4 -6
  109. package/examples/symmetric-domains.mjs +250 -0
  110. package/guides/application-facade-delivery-evidence.md +6 -6
  111. package/guides/attention-primitive.md +32 -0
  112. package/guides/change-waits.md +2 -2
  113. package/guides/claude-native.md +18 -9
  114. package/guides/codex-delivery.md +17 -6
  115. package/guides/delivery-primitives.md +180 -0
  116. package/guides/mcp-connection-lifetime.md +1 -1
  117. package/guides/mcp.md +3 -3
  118. package/guides/migration.md +148 -151
  119. package/guides/operation-cancellation.md +1 -1
  120. package/guides/participant-ownership.md +78 -47
  121. package/guides/pi-tools.md +34 -5
  122. package/guides/primitive-catch-consolidation.md +5 -5
  123. package/guides/primitive-history-consolidation.md +2 -2
  124. package/guides/primitive-status-consolidation.md +3 -3
  125. package/guides/primitive-storage-consolidation.md +1 -1
  126. package/guides/receive-primitive.md +90 -0
  127. package/guides/row-runtime-delivery-evidence.md +1 -1
  128. package/guides/row-storage-design.md +22 -28
  129. package/guides/square-api.md +314 -91
  130. package/guides/symmetric-domains.md +92 -0
  131. package/package.json +14 -1
  132. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  133. package/plugins/claude/skills/square/SKILL.md +2 -6
  134. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  135. package/plugins/opencode/delivery.ts +27 -17
  136. package/plugins/paseo/client/palette.ts +18 -18
  137. package/plugins/paseo/client/square-message.tsx +17 -23
  138. package/plugins/pi/square-pi.js +17 -17
  139. package/skills/square/SKILL.md +2 -6
  140. package/dist/square-backend.d.ts +0 -13
  141. package/dist/square-backend.js +0 -1
  142. package/dist/square-stdio.d.ts +0 -22
  143. package/dist/square-stdio.js +0 -235
  144. package/dist/stdio-server.d.ts +0 -3
  145. package/dist/stdio-server.js +0 -212
  146. package/guides/backends.md +0 -193
@@ -1,12 +1,14 @@
1
+ import { DatabaseSync } from 'node:sqlite';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { openExistingSquareDatabase } from './artifact.js';
3
4
  import { inSqliteTransaction } from './sqlite-transaction.js';
4
5
  import { canonicalPath, canonicalPathSync } from './canonical-path.js';
5
- import { formatActivityId, isWakeRouteKind, nameKey, sameBody, validateBody } from './model.js';
6
+ import { harnessOwner, decodeHarnessOwner } from './owner.js';
7
+ import { formatActivityId, isWakeRouteKind, nameKey } from './model.js';
6
8
  import { parseActivityId } from './square-core.js';
7
9
  import { readLegacyHostData } from './square-migration-host-ledger.js';
8
10
  import { legacyRowFramingIssue, FORMAT4_ADDED_OBJECTS } from './square-legacy-framing.js';
9
- import { APPLICATION_ID, ACTIVITIES_TABLE_SQL, ACTIVITY_INDEXES_SQL, CURRENT_USER_VERSION, V2_USER_VERSION, V3_USER_VERSION, V4_USER_VERSION, SCHEMA_SQL, FORMAT4_SCHEMA_SQL, DELIVERY_DEFERRALS_SQL, validateRowFraming, inspectSqlite, createSqliteDriver, invalidArtifact, validateStoredActivity } from './square-sqlite.js';
11
+ import { APPLICATION_ID, ACTIVITIES_TABLE_SQL, ACTIVITY_INDEXES_SQL, CURRENT_USER_VERSION, V2_USER_VERSION, V3_USER_VERSION, V4_USER_VERSION, V5_USER_VERSION, V6_USER_VERSION, V7_USER_VERSION, SCHEMA_SQL, FORMAT4_SCHEMA_SQL, DELIVERY_DEFERRALS_SQL, validateRowFraming, inspectSqlite, createSqliteDriver, invalidArtifact, validateStoredActivity, exportSqliteState, initializeSqlite } from './square-sqlite.js';
10
12
  function isObject(value) {
11
13
  return typeof value === 'object' && value !== null && !Array.isArray(value);
12
14
  }
@@ -23,6 +25,9 @@ function isNonNegativeInteger(value) {
23
25
  function isStringArray(value) {
24
26
  return Array.isArray(value) && value.every((item) => typeof item === 'string');
25
27
  }
28
+ function sameCoordinate(left, right) {
29
+ return left.channel === right.channel && left.session === right.session && left.agent === right.agent;
30
+ }
26
31
  function validateRecord(value, item) {
27
32
  return isObject(value) && Object.entries(value).every(([key, candidate]) => key.length > 0 && item(candidate));
28
33
  }
@@ -130,19 +135,48 @@ function standing(database) {
130
135
  return createSqliteDriver(database).roster().filter((participant) => participant.joined).length;
131
136
  }
132
137
  const ACTIVITY_INDEX_NAMES = ['activity_kind_index', 'activity_actor_index', 'activity_say_actor_index', 'activity_say_time', 'activity_bell_actor_time'];
133
- function rebuildV2Activities(database) {
138
+ function migrateActivities(database) {
139
+ // Only activity identity references are deleted. Range watermarks (read.through and
140
+ // delivery cancellation boundaries) remain meaningful even when their endpoint is gone.
141
+ for (const table of ['landed_audience', 'receipts', 'delivery_evidence']) {
142
+ database.exec(`DELETE FROM ${table} WHERE act_index IN (SELECT act_index FROM activities WHERE kind IN ('hold','resume'));`);
143
+ }
134
144
  database.exec([
145
+ "UPDATE activities SET reply_index=NULL WHERE reply_index IN (SELECT act_index FROM activities WHERE kind IN ('hold','resume'));",
135
146
  ...ACTIVITY_INDEX_NAMES.map((name) => `DROP INDEX ${name};`),
136
- 'ALTER TABLE activities RENAME TO activities_v2;',
147
+ 'ALTER TABLE activities RENAME TO activities_legacy;',
137
148
  ACTIVITIES_TABLE_SQL,
138
- 'INSERT INTO activities(act_index,at,kind,actor_json,actor_key_json,reach,reply_index,payload_json) SELECT act_index,at,kind,actor_json,actor_key_json,reach,reply_index,payload_json FROM activities_v2 ORDER BY act_index;',
139
- 'DROP TABLE activities_v2;',
149
+ "INSERT INTO activities(act_index,at,kind,actor_json,actor_key_json,reach,reply_index,payload_json) SELECT act_index,at,kind,actor_json,actor_key_json,reach,reply_index,payload_json FROM activities_legacy WHERE kind NOT IN ('hold','resume') ORDER BY act_index;",
150
+ 'DROP TABLE activities_legacy;',
140
151
  ...ACTIVITY_INDEXES_SQL.map((sql) => `${sql};`),
141
152
  ].join('\n'));
153
+ // Actor-bearing deleted activities and cleared replies can change maintained indices.
154
+ // Replay surviving facts only, without lowering the authoritative next allocator.
155
+ const names = database.prepare('SELECT name_key_json,name_json FROM participants').all();
156
+ const rebuilt = new DatabaseSync(':memory:');
157
+ try {
158
+ initializeSqlite(rebuilt, exportSqliteState(database));
159
+ // Canonical spelling is an established identity fact, not the spelling of
160
+ // whichever actor activity happens to survive this migration first.
161
+ const retainName = rebuilt.prepare('UPDATE participants SET name_json=? WHERE name_key_json=?');
162
+ for (const row of names)
163
+ retainName.run(row.name_json, row.name_key_json);
164
+ for (const table of ['participants', 'attention', 'landed_audience']) {
165
+ database.exec(`DELETE FROM ${table};`);
166
+ for (const row of rebuilt.prepare(`SELECT * FROM ${table}`).all()) {
167
+ const columns = Object.keys(row);
168
+ database.prepare(`INSERT INTO ${table}(${columns.join(',')}) VALUES(${columns.map(() => '?').join(',')})`)
169
+ .run(...Object.values(row));
170
+ }
171
+ }
172
+ }
173
+ finally {
174
+ rebuilt.close();
175
+ }
142
176
  }
143
177
  function validateCurrentFraming(database, location) {
144
178
  validateRowFraming(database, CURRENT_USER_VERSION, location);
145
- createSqliteDriver(database).header();
179
+ inspectSqlite(database);
146
180
  }
147
181
  function assertSource(condition, detail = 'row is malformed.') {
148
182
  if (!condition)
@@ -190,12 +224,12 @@ function decodeLegacyRoute(row) {
190
224
  assertSource(row.participant_key_json === JSON.stringify(nameKey(participant)), 'route participant key is malformed.');
191
225
  const sessionId = decodeJsonString(row.session_json);
192
226
  sourceFinite(row.updated_at);
193
- const body = { channel: parsed.channel, session: sessionId, ...(parsed.extra.agent === undefined ? {} : { agent: parsed.extra.agent }) };
194
- validateBody(body);
227
+ const coordinate = { channel: parsed.channel, session: sessionId, ...(parsed.extra.agent === undefined ? {} : { agent: parsed.extra.agent }) };
228
+ harnessOwner(coordinate);
195
229
  const address = parsed.address;
196
230
  if (row.kind === 'claude-native')
197
231
  assertSource(address.sessionId === undefined || address.sessionId === sessionId, 'native route session conflicts with its body.');
198
- return { location: parsed.location, participant, body,
232
+ return { location: parsed.location, participant, coordinate,
199
233
  wakeAddress: { kind: row.kind, address: { ...address, ...(row.kind === 'claude-native' ? { sessionId } : {}) } } };
200
234
  }
201
235
  function validateLegacyLease(value) {
@@ -235,14 +269,15 @@ function planSeats(location, roster, routes, owners, driver) {
235
269
  const joined = establishingJoin(driver, participant);
236
270
  const candidates = localRoutes.filter((route) => nameKey(route.participant) === nameKey(participant.name));
237
271
  const owner = owners.find(row => nameKey(row.participant) === nameKey(participant.name));
238
- const body = owner?.body ?? candidates[0]?.body;
239
- if (body === undefined || !candidates.every(route => sameBody(route.body, body)))
272
+ const coordinates = [...candidates.map(route => route.coordinate), ...(owner === undefined ? [] : [owner.coordinate])];
273
+ if (coordinates.length === 0 || !coordinates.every(coordinate => sameCoordinate(coordinate, coordinates[0])))
240
274
  continue;
241
275
  const address = uniqueSeatAddress(candidates);
242
276
  seats.push({
243
277
  participant: participant.name,
244
- body,
278
+ owner: harnessOwner(coordinates[0]),
245
279
  generation: formatActivityId(joined),
280
+ attachment: 'attached',
246
281
  ...(address === undefined ? {} : { wakeAddress: address }),
247
282
  });
248
283
  }
@@ -277,7 +312,7 @@ function inspectLegacyRowArtifact(database, location, host) {
277
312
  const driver = createSqliteDriver(database);
278
313
  let activityCount = 0;
279
314
  let previous = -1;
280
- for (const row of database.prepare('SELECT act_index FROM activities ORDER BY act_index').iterate()) {
315
+ for (const row of database.prepare("SELECT act_index FROM activities WHERE kind NOT IN ('hold','resume') ORDER BY act_index").iterate()) {
281
316
  const index = sourceInteger(row.act_index);
282
317
  assertSource(index > previous && index < nextActIndex, 'activity index is malformed or unassigned.');
283
318
  const activity = driver.activity(index);
@@ -321,7 +356,7 @@ function inspectLegacyRowArtifact(database, location, host) {
321
356
  routes: routes.length,
322
357
  recoveredSeats: seats.length,
323
358
  unownedStanding: standingCount - seats.length,
324
- sharedBodySeats: seats.filter(seat => seats.some(other => other !== seat && sameBody(other.body, seat.body))).length,
359
+ sharedBodySeats: seats.filter(seat => seats.some(other => other !== seat && other.owner === seat.owner)).length,
325
360
  skippedEvidence: host.evidence.length - deliveries.length,
326
361
  };
327
362
  const sourceToken = legacySourceToken(database);
@@ -373,26 +408,26 @@ function planLegacyDeliveries(records, seats, driver) {
373
408
  const native = source.kind === 'wake' && source.nativeDelivery !== undefined;
374
409
  if (!participant?.joined && !(participant?.done && native))
375
410
  continue;
376
- if (participant.joined && (seat === undefined || seat.body.session !== source.session
377
- || source.agent !== seat.body.agent || source.channel !== undefined && source.channel !== seat.body.channel))
411
+ const seatCoordinate = seat === undefined ? undefined : decodeHarnessOwner(seat.owner);
412
+ if (participant.joined && (seatCoordinate === undefined || seatCoordinate.session !== source.session
413
+ || seatCoordinate.agent !== source.agent || source.channel !== undefined && seatCoordinate.channel !== source.channel))
378
414
  continue;
379
415
  const harness = source.nativeDelivery?.harness;
380
- if (native && seat !== undefined && harness !==
381
- (seat.body.channel === 'claude-code' ? 'claude' : seat.body.channel === 'opencode' ? 'opencode' : undefined))
416
+ if (native && source.channel !== undefined && source.channel !== (harness === 'claude' ? 'claude-code' : 'opencode'))
417
+ continue;
418
+ if (native && seat !== undefined && harness !== undefined && seat.owner !== harnessOwner({ channel: harness === 'claude' ? 'claude-code' : 'opencode', session: source.session, ...(source.agent === undefined ? {} : { agent: source.agent }) }))
382
419
  continue;
383
420
  const joined = establishingJoin(driver, participant);
384
421
  const generation = formatActivityId(joined);
385
- if (index <= joined || driver.activity(index)?.kind !== 'say' || driver.directedAt(participant.name, index) === undefined)
422
+ if (index <= joined || driver.activityHeads({ indexes: [index], kinds: ['say'] }, { edge: 'oldest', limit: 1 }).items.length === 0 || driver.directedAt(participant.name, index) === undefined)
386
423
  continue;
387
424
  if (participant.done) {
388
425
  const done = driver.activityHeads({ actors: [participant.name], kinds: ['done'] }, { edge: 'newest', limit: 1 }).items[0];
389
426
  if (done === undefined || index >= done.index)
390
427
  continue;
391
- const body = { channel: harness === 'claude' ? 'claude-code' : 'opencode', session: source.session,
392
- ...(source.agent === undefined ? {} : { agent: source.agent }) };
393
- if (source.channel !== undefined && source.channel !== body.channel)
394
- continue;
395
- const coordinate = JSON.stringify([body.channel, body.session, body.agent]);
428
+ const owner = harnessOwner({ channel: harness === 'claude' ? 'claude-code' : 'opencode', session: source.session,
429
+ ...(source.agent === undefined ? {} : { agent: source.agent }) });
430
+ const coordinate = JSON.stringify([owner]);
396
431
  const key = nameKey(participant.name), previous = completedBodies.get(key);
397
432
  completedBodies.set(key, previous === undefined || previous === coordinate ? coordinate : null);
398
433
  }
@@ -429,7 +464,6 @@ function planLegacyDeliveries(records, seats, driver) {
429
464
  }
430
465
  return deliveries.filter(row => row.claimToken === undefined || tokens.get(row.claimToken) === 1);
431
466
  }
432
- const V4_REBUILT_TABLES = ['square_meta', 'catch_leases'];
433
467
  function tableDefinition(name) {
434
468
  const definition = SCHEMA_SQL.split(';').map((statement) => statement.trim()).find((statement) => statement.startsWith(`CREATE TABLE ${name} (`));
435
469
  if (definition === undefined)
@@ -439,16 +473,32 @@ function tableDefinition(name) {
439
473
  function currentAdditionStatements() {
440
474
  return SCHEMA_SQL.split(';').map((statement) => statement.trim()).filter((statement) => {
441
475
  const match = /^(?:CREATE (?:UNIQUE )?TABLE|CREATE (?:UNIQUE )?INDEX)\s+([a-z_]+)/i.exec(statement);
442
- return match !== null && FORMAT4_ADDED_OBJECTS.has(match[1]);
476
+ return match !== null && (FORMAT4_ADDED_OBJECTS.has(match[1]) || match[1] === 'seat_owner');
443
477
  }).map((statement) => `${statement};`);
444
478
  }
479
+ function migrateSeatOwnership(database, source) {
480
+ const rows = database.prepare('SELECT * FROM seats ORDER BY participant_key_json').all();
481
+ database.exec('ALTER TABLE seats RENAME TO seats_legacy;');
482
+ database.exec(tableDefinition('seats'));
483
+ database.exec('CREATE INDEX seat_owner ON seats(owner_json);');
484
+ const insert = database.prepare('INSERT INTO seats(participant_key_json,participant_json,owner_json,generation_json,wake_address_json,attachment) VALUES(?,?,?,?,?,?)');
485
+ for (const row of rows) {
486
+ const channel = decodeJsonString(row.channel_json, true), session = decodeJsonString(row.session_json, true);
487
+ const agent = row.agent_json === null ? undefined : decodeJsonString(row.agent_json, true);
488
+ const owner = harnessOwner({ channel, session, ...(agent === undefined ? {} : { agent }) });
489
+ const attachment = source === 6 ? row.attachment : 'attached';
490
+ assertSource(attachment === 'attached' || attachment === 'detached', 'seat attachment is malformed.');
491
+ insert.run(row.participant_key_json, row.participant_json, JSON.stringify(owner), row.generation_json, row.wake_address_json, attachment);
492
+ }
493
+ database.exec('DROP TABLE seats_legacy;');
494
+ }
445
495
  function importOwnershipAndEvidence(database, plan) {
446
496
  for (const generation of plan.leaseGenerations) {
447
497
  database.prepare('UPDATE catch_leases SET generation_json=? WHERE owner_json=?').run(JSON.stringify(generation.generation), generation.ownerJson);
448
498
  }
449
- const insertSeat = database.prepare('INSERT INTO seats(participant_key_json,participant_json,channel_json,session_json,agent_json,generation_json,wake_address_json) VALUES(?,?,?,?,?,?,?)');
499
+ const insertSeat = database.prepare('INSERT INTO seats(participant_key_json,participant_json,owner_json,generation_json,wake_address_json,attachment) VALUES(?,?,?,?,?,?)');
450
500
  for (const seat of plan.seats) {
451
- insertSeat.run(JSON.stringify(nameKey(seat.participant)), JSON.stringify(seat.participant), JSON.stringify(seat.body.channel), JSON.stringify(seat.body.session), seat.body.agent === undefined ? null : JSON.stringify(seat.body.agent), JSON.stringify(seat.generation), seat.wakeAddress === undefined ? null : JSON.stringify(seat.wakeAddress));
501
+ insertSeat.run(JSON.stringify(nameKey(seat.participant)), JSON.stringify(seat.participant), JSON.stringify(seat.owner), JSON.stringify(seat.generation), seat.wakeAddress === undefined ? null : JSON.stringify(seat.wakeAddress), seat.attachment);
452
502
  }
453
503
  const insertDelivery = database.prepare('INSERT INTO delivery_evidence(participant_key_json,participant_json,generation_json,act_index,kind,attempt_n,outcome,at,expires_at,claim_token_json,payload_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)');
454
504
  for (const evidence of plan.deliveries) {
@@ -468,6 +518,7 @@ function upgradeRowArtifact(database, location, plan) {
468
518
  importOwnershipAndEvidence(database, plan);
469
519
  database.exec('DROP TABLE routes;');
470
520
  database.exec(DELIVERY_DEFERRALS_SQL);
521
+ migrateActivities(database);
471
522
  database.exec(`PRAGMA user_version = ${CURRENT_USER_VERSION};`);
472
523
  validateCurrentFraming(database, location);
473
524
  }
@@ -481,10 +532,9 @@ function validateMigrationSource(database, location) {
481
532
  throw invalidArtifact(issue);
482
533
  return before.userVersion;
483
534
  }
484
- if (before.userVersion === V4_USER_VERSION) {
485
- validateRowFraming(database, V4_USER_VERSION, location);
486
- inspectSqlite(database);
487
- return V4_USER_VERSION;
535
+ if (before.userVersion === V4_USER_VERSION || before.userVersion === V5_USER_VERSION || before.userVersion === V6_USER_VERSION || before.userVersion === V7_USER_VERSION) {
536
+ validateRowFraming(database, before.userVersion, location);
537
+ return before.userVersion;
488
538
  }
489
539
  if (before.userVersion === CURRENT_USER_VERSION) {
490
540
  validateCurrentFraming(database, location);
@@ -495,7 +545,10 @@ function validateMigrationSource(database, location) {
495
545
  /**
496
546
  * Explicit conversion of the supported legacy formats. Row-backed versions 2 and 3 are
497
547
  * preflighted read-only, then receive ownership/evidence and deferral storage in one
498
- * format-5 transaction. Format 4 adds only the empty deferral table and its index.
548
+ * format-8 transaction. Formats 4/5 gain attachment; format 4 also gains empty deferrals.
549
+ * All legacy seats are encoded as opaque owners, preserving format-6 attachment.
550
+ * Every source drops obsolete activities and their identity references, clears dangling
551
+ * replies and rebuilds derived indices; surviving ids and allocation watermarks remain stable.
499
552
  * Legacy delivery evidence is copied from HostLedger only when its owner/session mapping is
500
553
  * unique; no source record is retired. Version 1 is not a supported source for this format transition.
501
554
  */
@@ -506,11 +559,15 @@ export async function migrateSquareFile(location, signal, env = process.env) {
506
559
  const source = await inSqliteTransaction(database, false, () => validateMigrationSource(database, squarePath), value => value, signal);
507
560
  if (source === undefined)
508
561
  return { status: 'already-current', standing: standing(database) };
509
- if (source === V4_USER_VERSION) {
562
+ if (source === V4_USER_VERSION || source === V5_USER_VERSION || source === V6_USER_VERSION || source === V7_USER_VERSION) {
510
563
  return await inSqliteTransaction(database, true, () => {
511
564
  assertSource(validateMigrationSource(database, squarePath) === source, 'source format changed during migration preflight.');
512
565
  }, () => {
513
- database.exec(DELIVERY_DEFERRALS_SQL);
566
+ if (source === V4_USER_VERSION)
567
+ database.exec(DELIVERY_DEFERRALS_SQL);
568
+ if (source !== V7_USER_VERSION)
569
+ migrateSeatOwnership(database, source);
570
+ migrateActivities(database);
514
571
  database.exec(`PRAGMA user_version = ${CURRENT_USER_VERSION};`);
515
572
  validateCurrentFraming(database, squarePath);
516
573
  return { status: 'migrated', standing: standing(database) };
@@ -530,8 +587,6 @@ export async function migrateSquareFile(location, signal, env = process.env) {
530
587
  assertSource(lockedPlan.sourceToken === plan.sourceToken, 'source changed during migration preflight.');
531
588
  result.plan = lockedPlan;
532
589
  }, () => {
533
- if (source === 2)
534
- rebuildV2Activities(database);
535
590
  const migratedPlan = result.plan;
536
591
  upgradeRowArtifact(database, squarePath, migratedPlan);
537
592
  result.outcome = { status: 'migrated', standing: migratedPlan.standing, counts: migratedPlan.counts };
@@ -0,0 +1,6 @@
1
+ import type { AudienceRoute } from './attention.js';
2
+ import type { StoredAct } from './model.js';
3
+ import type { DirectedFilter, SquareRead } from './ports.js';
4
+ import type { ReceiveRead } from './receive.js';
5
+ /** Transaction-local projections only; the artifact still owns indexed selection and bodies. */
6
+ export declare function squareReceiveRead(facts: SquareRead): ReceiveRead<number, StoredAct, AudienceRoute, DirectedFilter>;
@@ -0,0 +1,18 @@
1
+ /** Transaction-local projections only; the artifact still owns indexed selection and bodies. */
2
+ export function squareReceiveRead(facts) {
3
+ const filter = (query, unconsumed) => {
4
+ const { unseenOnly: _, ...rest } = query;
5
+ return unconsumed ? { ...rest, unseenOnly: true } : rest;
6
+ };
7
+ return {
8
+ heads: (recipient, query, options) => facts.directed(recipient, filter(query, options.unconsumed), { edge: 'oldest', limit: options.limit })
9
+ .items.map(item => ({ key: item.index, via: item.route })),
10
+ addressed(recipient, index) {
11
+ const head = facts.directedAt(recipient, index);
12
+ return head === undefined ? undefined : { key: head.index, via: head.route };
13
+ },
14
+ load: keys => new Map(facts.activities(keys).map(item => [item.index, item])),
15
+ count: (recipient, query, unconsumed) => facts.directedCount(recipient, filter(query, unconsumed)),
16
+ receipt: ref => facts.receipt(ref.recipient, ref.item),
17
+ };
18
+ }
@@ -21,7 +21,10 @@ export declare const APPLICATION_ID = 1397838405;
21
21
  export declare const V2_USER_VERSION = 2;
22
22
  export declare const V3_USER_VERSION = 3;
23
23
  export declare const V4_USER_VERSION = 4;
24
- export declare const CURRENT_USER_VERSION = 5;
24
+ export declare const V5_USER_VERSION = 5;
25
+ export declare const V6_USER_VERSION = 6;
26
+ export declare const V7_USER_VERSION = 7;
27
+ export declare const CURRENT_USER_VERSION = 8;
25
28
  export declare const V1_SNAPSHOT_TABLE = "square_snapshot";
26
29
  export interface LegacyFraming {
27
30
  readonly applicationId: number;
@@ -35,15 +38,18 @@ export interface LegacyFraming {
35
38
  * migration. Anything else is unrelated or unsupported, not repaired.
36
39
  */
37
40
  export declare function validateLegacyFraming(database: DatabaseSync, applicationId: number, location?: string): LegacyFraming;
38
- export declare const ACTIVITIES_TABLE_SQL = "CREATE TABLE activities (\n act_index INTEGER PRIMARY KEY CHECK (act_index BETWEEN 0 AND 9007199254740991),\n at REAL NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('join','done','say','hold','resume','read','listen','ignore','unlisten')),\n actor_json TEXT COLLATE BINARY,\n actor_key_json TEXT COLLATE BINARY,\n reach TEXT CHECK (reach = 'bell'),\n reply_index INTEGER CHECK (reply_index >= 0 AND reply_index < act_index),\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n CHECK ((actor_json IS NULL) = (actor_key_json IS NULL)),\n CHECK (kind IN ('hold','resume') OR actor_json IS NOT NULL),\n CHECK (kind = 'say' OR (reach IS NULL AND reply_index IS NULL))\n);";
41
+ export declare const ACTIVITIES_TABLE_SQL = "CREATE TABLE activities (\n act_index INTEGER PRIMARY KEY CHECK (act_index BETWEEN 0 AND 9007199254740991),\n at REAL NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('join','done','say','read','listen','ignore','unlisten')),\n actor_json TEXT COLLATE BINARY NOT NULL,\n actor_key_json TEXT COLLATE BINARY NOT NULL,\n reach TEXT CHECK (reach = 'bell'),\n reply_index INTEGER CHECK (reply_index >= 0 AND reply_index < act_index),\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n CHECK (kind = 'say' OR (reach IS NULL AND reply_index IS NULL))\n);";
39
42
  export declare const ACTIVITY_INDEXES_SQL: readonly ["CREATE INDEX activity_kind_index ON activities(kind, act_index)", "CREATE INDEX activity_actor_index ON activities(actor_key_json, act_index)", "CREATE INDEX activity_say_actor_index ON activities(actor_key_json, act_index) WHERE kind='say'", "CREATE INDEX activity_say_time ON activities(at, act_index) WHERE kind='say'", "CREATE INDEX activity_bell_actor_time ON activities(actor_key_json, at, act_index) WHERE kind='say' AND reach='bell'"];
40
43
  export declare const DELIVERY_DEFERRALS_SQL = "CREATE TABLE delivery_deferrals (\n participant_key_json TEXT COLLATE BINARY NOT NULL,\n participant_json TEXT NOT NULL,\n generation_json TEXT NOT NULL,\n deferral_id_json TEXT NOT NULL,\n match_json TEXT NOT NULL CHECK (json_valid(match_json)),\n expires_at REAL NOT NULL,\n PRIMARY KEY (participant_key_json,generation_json,deferral_id_json)\n) WITHOUT ROWID;\nCREATE INDEX delivery_deferral_expiry ON delivery_deferrals(participant_key_json,generation_json,expires_at);";
41
44
  export declare const SCHEMA_SQL: string;
45
+ export declare const FORMAT7_SCHEMA_SQL: string;
46
+ export declare const FORMAT6_SCHEMA_SQL: string;
47
+ export declare const FORMAT5_SCHEMA_SQL: string;
42
48
  export declare const FORMAT4_SCHEMA_SQL: string;
43
49
  export declare function invalidArtifact(detail: string): SquareError;
44
50
  export declare function validateStoredActivity(value: unknown): StoredAct;
45
51
  export declare function validateWatchLease(value: unknown): WatchLease;
46
- export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3 | 4 | 5, location?: string): void;
52
+ export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3 | 4 | 5 | 6 | 7 | 8, location?: string): void;
47
53
  export declare function validateSqliteHeader(db: DatabaseSync, location?: string): SquareHeader;
48
54
  export interface SqliteDriver extends SquareRead, LandingWriter {
49
55
  readonly changed: boolean;