@astrosheep/square 0.3.106 → 0.4.0

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 (142) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/artifact-store.d.ts +41 -0
  3. package/dist/artifact-store.js +272 -0
  4. package/dist/artifact.d.ts +8 -12
  5. package/dist/artifact.js +66 -343
  6. package/dist/attention-presentation.d.ts +33 -7
  7. package/dist/attention-presentation.js +33 -69
  8. package/dist/boundary-presentation.d.ts +0 -2
  9. package/dist/boundary-presentation.js +17 -22
  10. package/dist/catch-decisions.d.ts +2 -2
  11. package/dist/catch-decisions.js +14 -4
  12. package/dist/catch-selection.d.ts +8 -0
  13. package/dist/catch-selection.js +5 -0
  14. package/dist/channel-artifact.d.ts +10 -0
  15. package/dist/channel-artifact.js +55 -0
  16. package/dist/channel-delivery.d.ts +11 -0
  17. package/dist/channel-delivery.js +51 -0
  18. package/dist/channel-domain.d.ts +3 -0
  19. package/dist/channel-domain.js +83 -0
  20. package/dist/channel-facade.d.ts +76 -0
  21. package/dist/channel-facade.js +1 -0
  22. package/dist/channel-internal.d.ts +126 -0
  23. package/dist/channel-internal.js +440 -0
  24. package/dist/channel-metadata.d.ts +11 -0
  25. package/dist/channel-metadata.js +1 -0
  26. package/dist/channel-ports.d.ts +241 -0
  27. package/dist/channel-ports.js +1 -0
  28. package/dist/channel-presentation.d.ts +17 -0
  29. package/dist/channel-presentation.js +22 -0
  30. package/dist/channel-sqlite-schema.d.ts +13 -0
  31. package/dist/channel-sqlite-schema.js +81 -0
  32. package/dist/channel-transport.d.ts +62 -0
  33. package/dist/channel-transport.js +1 -0
  34. package/dist/channel-values.d.ts +27 -0
  35. package/dist/channel-values.js +90 -0
  36. package/dist/channel.d.ts +31 -21
  37. package/dist/channel.js +42 -74
  38. package/dist/claude-delivery-status.js +1 -1
  39. package/dist/claude-delivery.d.ts +2 -5
  40. package/dist/claude-delivery.js +56 -103
  41. package/dist/claude-mod.js +4 -4
  42. package/dist/claude-transport.d.ts +5 -0
  43. package/dist/claude-transport.js +58 -0
  44. package/dist/cli/context.d.ts +2 -2
  45. package/dist/cli/context.js +0 -1
  46. package/dist/cli/maintenance-commands.d.ts +0 -4
  47. package/dist/cli/maintenance-commands.js +2 -29
  48. package/dist/cli/program.js +1 -1
  49. package/dist/cli/registry.js +1 -2
  50. package/dist/codex-address.d.ts +1 -1
  51. package/dist/codex-queue.d.ts +2 -2
  52. package/dist/codex-queue.js +1 -3
  53. package/dist/decisions.js +3 -3
  54. package/dist/delivery-deferral.d.ts +9 -18
  55. package/dist/delivery-deferral.js +4 -4
  56. package/dist/delivery-health.d.ts +4 -4
  57. package/dist/delivery-health.js +11 -11
  58. package/dist/delivery-operations.d.ts +15 -2
  59. package/dist/delivery-operations.js +60 -30
  60. package/dist/delivery.d.ts +44 -33
  61. package/dist/delivery.js +49 -75
  62. package/dist/help.js +1 -6
  63. package/dist/http-delivery.d.ts +2 -2
  64. package/dist/http-delivery.js +2 -4
  65. package/dist/inbox.d.ts +10 -18
  66. package/dist/inbox.js +44 -53
  67. package/dist/index.d.ts +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/mcp.d.ts +2 -1
  70. package/dist/mcp.js +40 -70
  71. package/dist/message.d.ts +7 -0
  72. package/dist/message.js +23 -0
  73. package/dist/model.d.ts +41 -77
  74. package/dist/model.js +7 -139
  75. package/dist/neutral.d.ts +72 -0
  76. package/dist/neutral.js +27 -0
  77. package/dist/notifications.d.ts +2 -8
  78. package/dist/notifications.js +15 -50
  79. package/dist/operation-presentation.d.ts +1 -2
  80. package/dist/operation-presentation.js +1 -7
  81. package/dist/owner.d.ts +2 -2
  82. package/dist/owner.js +1 -1
  83. package/dist/paseo-delivery.d.ts +1 -1
  84. package/dist/paseo-delivery.js +2 -4
  85. package/dist/plugins/opencode/delivery.js +27 -42
  86. package/dist/ports.d.ts +46 -168
  87. package/dist/presentation-operations.d.ts +1 -14
  88. package/dist/presentation-operations.js +16 -16
  89. package/dist/presentation.d.ts +5 -5
  90. package/dist/presentation.js +1 -0
  91. package/dist/runtime.js +2 -2
  92. package/dist/search.js +1 -1
  93. package/dist/sqlite-channel.d.ts +15 -0
  94. package/dist/sqlite-channel.js +600 -0
  95. package/dist/sqlite-framing.d.ts +8 -0
  96. package/dist/sqlite-framing.js +31 -0
  97. package/dist/sqlite-square.d.ts +12 -13
  98. package/dist/sqlite-square.js +192 -248
  99. package/dist/sqlite-values.d.ts +21 -0
  100. package/dist/sqlite-values.js +78 -0
  101. package/dist/square-api.d.ts +1 -1
  102. package/dist/square-api.js +1 -1
  103. package/dist/square-core.d.ts +1 -1
  104. package/dist/square-domain.d.ts +21 -7
  105. package/dist/square-domain.js +93 -94
  106. package/dist/square-facade.d.ts +12 -66
  107. package/dist/square-index.d.ts +1 -1
  108. package/dist/square-index.js +1 -1
  109. package/dist/square-location.d.ts +6 -1
  110. package/dist/square-location.js +1 -1
  111. package/dist/square-receive.d.ts +2 -3
  112. package/dist/square-sqlite.d.ts +45 -44
  113. package/dist/square-sqlite.js +308 -764
  114. package/dist/square-tools.d.ts +8 -0
  115. package/dist/square-tools.js +39 -0
  116. package/dist/views.d.ts +6 -6
  117. package/dist/views.js +15 -9
  118. package/dist/wake-preview.d.ts +7 -0
  119. package/dist/wake-preview.js +15 -0
  120. package/dist/wake-sink.d.ts +2 -2
  121. package/dist/wake-sink.js +1 -1
  122. package/dist/wake-transports.d.ts +7 -0
  123. package/dist/wake-transports.js +42 -0
  124. package/guides/claude-native.md +13 -10
  125. package/guides/codex-delivery.md +3 -1
  126. package/guides/participant-ownership.md +1 -1
  127. package/guides/row-runtime-delivery-evidence.md +2 -0
  128. package/guides/row-storage-design.md +19 -44
  129. package/guides/square-api.md +137 -60
  130. package/package.json +1 -1
  131. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  132. package/plugins/claude/types.d.ts +1 -1
  133. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  134. package/plugins/opencode/delivery.ts +28 -41
  135. package/plugins/pi/square-pi.js +28 -51
  136. package/dist/square-legacy-framing.d.ts +0 -11
  137. package/dist/square-legacy-framing.js +0 -64
  138. package/dist/square-migration-host-ledger.d.ts +0 -26
  139. package/dist/square-migration-host-ledger.js +0 -169
  140. package/dist/square-migration.d.ts +0 -30
  141. package/dist/square-migration.js +0 -599
  142. package/guides/migration.md +0 -156
@@ -224,7 +224,8 @@ Square; there is no `Participant.history`.
224
224
  body may carry `{}`; an omitted, empty or whitespace-only body requires non-empty metadata. Metadata-only
225
225
  says omit `body` from public activity records. Invalid objects, cycles, unsupported values and oversized
226
226
  metadata refuse before landing with `activityUnsent`. Metadata is carried unchanged by express, history,
227
- catch, status/latest, unread projections and stream records through the SDK, CLI and stream.
227
+ catch, status/latest, unread projections and stream records through the SDK, CLI and stream, and
228
+ by channel send/receive/history/pending messages.
228
229
  MCP and Pi tools are not a metadata surface: their `express` requires a body and its schema rejects a
229
230
  `metadata` argument. Tool history/catch return rendered text with a structured-activity placeholder,
230
231
  not metadata JSON or activity objects. Metadata does not affect mentions, replies, listening, audience,
@@ -232,14 +233,15 @@ Square; there is no `Participant.history`.
232
233
  by exact metadata match through the SDK lease API below. Metadata may nest up to 256 levels; the bound is enforced before
233
234
  persistence and public reads. Human text uses a concise structured-activity placeholder and never prints
234
235
  metadata JSON. SDK history and pending queries accept exact metadata matches; there is no query
235
- language or metadata index. CLI `--metadata <json>` remains an express input flag.
236
+ language or metadata index. CLI `--metadata <json>` remains an express input flag. Channel
237
+ `send` accepts the same metadata and returns it on `ChannelMessage` records.
236
238
  - Catch returns `{activities, remaining, reason, hint?, observation?}`. Each activity includes
237
239
  viewer-relative `perception` and `via`. Reasons are `drained`, `idle`, `capped`, `quorum`,
238
240
  `replaced`, or `already-active`. Omitted/zero idle is immediate; positive idle uses the single
239
241
  leased quiet wait. Receipt selection, perception, routes, counts and terminal facts settle
240
242
  in the receipt transaction. Cancellation/replacement releases only that waiter's lease and never
241
243
  consumes future activity.
242
- - All history callers return a `HistoryResult` object with `activities`. Default page size is ten;
244
+ - Agora history (`Square.history`) returns `{activities}`. Default page size is ten;
243
245
  `limit: null` explicitly requests an unbounded archive. Numeric limits must be 1–100. Contextual
244
246
  queries still reject an implicit merged window over 100 activities; an explicit numeric limit
245
247
  bounds it. CLI/tool schemas accept numeric limits only. History never writes receipts; history
@@ -271,9 +273,68 @@ Operations that accept an `OperationControl` use a final argument with `signal`
271
273
  `onProgress`. Listener/lifecycle operations take optional observation options before that argument;
272
274
  participant selection, seat discovery and host cleanup methods do not take an `OperationControl`.
273
275
 
276
+ ## Channel endpoints and point-to-point messages
277
+
278
+ `@astrosheep/square/channel` exports the endpoint and messaging resource `Channel`; the Agora
279
+ resource (`Square` / `Participant`) uses the same message substrate. `Channel` has static `open`,
280
+ `create` and `inMemory`, a `location` property, and methods `attach`, `reattach`, `endpoint`,
281
+ `endpointsOf`, `listEndpoints`, `detach`, `cancelDeliveries`, `remove`, `send`, `history` and
282
+ `close`. Obtain a `ChannelEndpoint` from a Channel; it has `name` and `generation` properties and
283
+ methods `pending`, `ack`, `receive`, `deferDelivery`, `renewDeliveryDeferral`,
284
+ `releaseDeliveryDeferral`, `send` and `leave`. Endpoint generations are opaque strings: retain
285
+ and compare them for equality, without interpreting their contents.
286
+
287
+ The subpath exports the types `MessageId`, `MessageCursor`, `MessageQuery`, `MessageSearch`,
288
+ `MessageMetadata`, `ChannelTruncate`, `ChannelSendInput`, `ChannelSendResult`, `ChannelMessage`,
289
+ `ChannelPendingOptions`, `ChannelPendingResult`, `ChannelReceiveResult`, `ChannelHistoryQuery`,
290
+ `ChannelHistoryResult`, `AckResult`, `ChannelEndpointSummary`, `ChannelEndpointHold`,
291
+ `DeliveryDeferral`, `MetadataMatch`, `Owner`, `OperationControl`, `WakeAddress`, `WakeTransportPort`,
292
+ `ChannelOpenInput`, `ChannelCreateInput`, `HarnessSessionChannel` and `ResolvedHarnessSession`.
293
+ It also exports `harnessOwner`, `decodeHarnessOwner`, `localUserOwner` and `resolveHarnessSession`.
294
+ The package root exports `MessageId`, `MessageCursor` and `MessageQuery`, but not `MessageMetadata`
295
+ or `MessageSearch` under those names; its Agora equivalents are `ActivityMetadata` and `ActivitySearch`.
296
+
297
+ Square and Participant expose their own Agora lifecycle operations; in particular,
298
+ `square.endHeld` lands a fenced `done`, whereas `channel.remove` removes an endpoint and is not
299
+ `Channel.endHeld`.
300
+ `Square.history` and `Participant.pending` remain Agora projections, not channel reads: Agora
301
+ history selects joins, says, departures and listener changes but never native messages, and Agora
302
+ pending selects addressed, unconsumed says as perceived activities.
303
+
304
+ - `Channel.send({from, to, body, truncate?, metadata?})` addresses standing endpoints explicitly.
305
+ `truncate` defaults to `false`, so the full body is delivered and presentation records a receipt.
306
+ An explicit positive integer at least as long as the body is likewise unclipped; when the body is
307
+ longer, the landed message stores `preview`, so presentation shows that preview and records clipped
308
+ coverage without consuming the full stored body. `pending`, `receive` and `ack` still read and
309
+ consume that full body. Any other `truncate` value is refused before landing. `to` in the send
310
+ result and in every `ChannelMessage` is the actual landed audience after address resolution. A
311
+ stored message may have an empty body only when imported from a legacy metadata-only direct say;
312
+ `Channel.send` itself always requires a non-empty body.
313
+ - `endpoint.receive(options?, control?)` selects the oldest unconsumed addressed messages matching the
314
+ shared filters and records their receipts in one transaction, returning
315
+ `{messages, consumed, remaining}`. Each `ChannelMessage` is `{id, at, from, to, body, metadata?}`,
316
+ symmetric with the send result. `remaining` counts matching unconsumed messages left after that
317
+ transaction. Nothing is consumed by `pending` or `history`.
318
+ - `Channel.history(query?, control?)` returns `{messages}` for native messages and says, selecting
319
+ the newest page oldest-first by default; `order: 'desc'` reverses the page. It shares the
320
+ `MessageQuery` filters (`at`, `from`, `match`, `search`, `cursor`) but has no Agora
321
+ `contextBefore`/`contextAfter`, `since`, `mention` or `observe` options. Its default page size is
322
+ 100; `limit` accepts 1–100 or `null` for all matches.
323
+ - `listEndpoints(control?)` reports every standing endpoint without an owner argument as
324
+ `{name, generation, attachment, deliveryAvailable, unreadCount, lastActiveAt?}`. `endpointsOf(owner)`
325
+ remains the owner-scoped custody list, and neither operation joins, restores or reveals an address.
326
+ - Direct notifications have no separate envelope: the model-facing text is the stored preview, or
327
+ the body when untruncated, escaped inside the shared `<system-reminder>` frame; sender, recipients
328
+ and ID are not printed. Body text that would read as `<channel-`, `<square-` or
329
+ `<system-reminder` is escaped. A mixed boundary keeps notification order; its fixed budget and
330
+ `N more activities not shown.` line apply only to Agora activity blocks, and the line trails the
331
+ rendered batch.
332
+ - An unconsumed direct message stays eligible for the proactive inbox and wake horizon across a seat
333
+ takeover or rejoin. Earlier Agora activity still ends at the recipient's new read horizon.
334
+
274
335
  ## Shared history and pending filters
275
336
 
276
- `HistoryQuery` and `PendingOptions` extend the same exported `ActivityQuery`:
337
+ Agora `HistoryQuery` and `PendingOptions` extend the exported `ActivityQuery`:
277
338
 
278
339
  ```ts
279
340
  interface ActivityQuery {
@@ -285,6 +346,12 @@ interface ActivityQuery {
285
346
  }
286
347
  ```
287
348
 
349
+ Channel `ChannelHistoryQuery` and `ChannelPendingOptions` extend `MessageQuery`, whose `at` IDs
350
+ and `MessageCursor.id` are `MessageId` (`msg/N`) rather than `ActivityId` (`act/N`). The message
351
+ query types are exported from `@astrosheep/square/channel` and the package root. Channel history,
352
+ pending, receive and ack use `msg/N`; Agora history, pending and ack use `act/N`. A say has both
353
+ coordinates, which are not interchangeable.
354
+
288
355
  - Filters combine with AND. IDs in `at` and senders in `from` each combine with OR; an absent array
289
356
  is unrestricted, while an explicitly empty array matches nothing. `at` is an exact ID selection;
290
357
  only history can expand those IDs with `contextBefore` / `contextAfter`.
@@ -294,13 +361,15 @@ interface ActivityQuery {
294
361
  - `match` uses the same nested own-property, exact type-sensitive scalar equalities as deferrals.
295
362
  `search` tests each activity's ID, sender and body independently, case-insensitively. Literal mode
296
363
  treats regex characters as plain text; regex mode uses JavaScript's built-in regular expressions,
297
- without a ReDoS timeout. Search text must be nonempty and regex syntax must be valid. History
298
- searches says and departures, not joins; pending always selects addressed, unconsumed says.
299
- - A cursor is an exclusive activity-ID boundary, independent of `limit`: before excludes the anchor
364
+ without a ReDoS timeout. Search text must be nonempty and regex syntax must be valid. Agora
365
+ history searches says and departures, not joins. Participant `pending` selects addressed,
366
+ unconsumed says; `ChannelEndpoint` `pending`/`receive` select addressed, unconsumed messages and
367
+ says.
368
+ - A cursor is an exclusive boundary in its own coordinate space, independent of `limit`: before excludes the anchor
300
369
  and everything newer; after excludes the anchor and everything older. No consumption is involved.
301
370
  Storage applies the shared filters before limiting and uses the same predicates for counts.
302
- - History keeps its archive-specific `order`, `mention`, `since` and context options. By default it
303
- selects the newest page and returns it oldest first; an after cursor selects the oldest following
371
+ - Agora history keeps its archive-specific `order`, `mention`, `since` and context options. By
372
+ default it selects the newest page and returns it oldest first; an after cursor selects the oldest following
304
373
  page. `order: 'desc'` reverses the selected page, not which activities belong to it. Numeric limits
305
374
  stay 1–100, default ten; `limit: null` reads all matches. Exact/context history keeps its existing
306
375
  implicit full-window selection with a 100-activity bound unless a numeric limit is supplied.
@@ -311,18 +380,24 @@ interface ActivityQuery {
311
380
  The SDK accepts only `search` and `cursor`, not the removed `grep` / `fixed` / `before` / `after`
312
381
  fields. CLI flags and tool arguments keep those familiar spellings and translate at the adapter
313
382
  boundary, e.g. `--fixed text` becomes `search: {text, mode: 'literal'}` and `--after act/12` becomes
314
- `cursor: {id: 'act/12', direction: 'after'}`. The artifact format and stored activities do not change.
383
+ `cursor: {id: 'act/12', direction: 'after'}`. The query surface keeps these `act/N` and `msg/N` coordinates; format 11 is a separate fresh-store
384
+ breaking upgrade rather than a query change.
315
385
 
316
386
  ## Pending query and exact acknowledgement
317
387
 
318
388
  `Participant.pending(options?, control?)` and `Participant.ack(ids, control?)` separate a read-only
319
- query from explicit consumption. `ChannelEndpoint` from `@astrosheep/square/channel` exposes the
320
- identical methods by forwarding to its captured Participant; it has no independent inbox or storage.
321
-
322
- `Channel.send` / `Square.send` address joined members, including detached members. Attachment gates
323
- proactive wake, not landing: a detached recipient retains its unread addressed activity without a
324
- wake and can query/ack it after reattachment. Recipient validation remains all-or-nothing; a missing
325
- or departed recipient rejects the entire send without landing activity or waking other recipients.
389
+ query from explicit consumption. Agora `pending` returns addressed, unconsumed `say` records as
390
+ perceived activities, so native messages never appear there. `ChannelEndpoint` from
391
+ `@astrosheep/square/channel` returns `{messages}` for both native messages and says; `ack` records
392
+ receipts for the addressed message IDs. Neither layer keeps a second inbox, read horizon or storage.
393
+
394
+ Agora publication is participant-scoped: `Participant.express(...)` publishes a say/activity for
395
+ that standing participant. Standalone `Channel.send` addresses standing Channel endpoints, without
396
+ requiring Agora membership, and lands a Channel message, not an Agora activity. Attachment gates
397
+ proactive wake, not landing: a detached Channel recipient retains its unread addressed item and can
398
+ query/ack it after reattachment. Recipient validation is all-or-nothing; a missing or departed
399
+ recipient rejects the entire send without landing a message or waking other recipients. An omitted
400
+ `truncate` sends the full body; truncation is an explicit per-message choice.
326
401
 
327
402
  ```ts
328
403
  import type { ChannelEndpoint, MetadataMatch } from '@astrosheep/square/channel';
@@ -330,30 +405,36 @@ import type { ChannelEndpoint, MetadataMatch } from '@astrosheep/square/channel'
330
405
  async function drain(endpoint: ChannelEndpoint, match: MetadataMatch) {
331
406
  for (;;) {
332
407
  const page = await endpoint.pending({ match });
333
- if (page.activities.length === 0) break;
408
+ if (page.messages.length === 0) break;
334
409
  // Process the entire page successfully before acknowledging its exact IDs.
335
- for (const activity of page.activities) await processNotice(activity);
336
- const { consumed } = await endpoint.ack(page.activities.map(activity => activity.id));
410
+ for (const message of page.messages) await processNotice(message);
411
+ const { consumed } = await endpoint.ack(page.messages.map(message => message.id));
337
412
  // consumed may be smaller if another consumer acknowledged some IDs first.
338
413
  }
339
414
  }
340
415
  ```
341
416
 
342
- - `pending(query: PendingOptions = {}, control?)` returns
343
- `{activities: readonly PerceivedActivity[], remaining: number}`. It selects only landed,
344
- addressed, unconsumed says, oldest first. Body, metadata, delivery information, perception and
345
- route use the same public records as catch; bodies are full stored bodies, not clipped previews.
417
+ - `Participant.pending(query: PendingOptions = {}, control?)` returns
418
+ `{activities: readonly PerceivedActivity[], remaining: number}` and selects landed, addressed,
419
+ unconsumed says oldest first, with viewer-relative `perception` and `via` as in catch.
420
+ `ChannelEndpoint.pending` returns `{messages: readonly ChannelMessage[], remaining: number}` for
421
+ addressed, unconsumed messages and says; each message is `{id, at, from, to, body, metadata?}`.
422
+ Both read full stored bodies, never clipped previews or delivery remnants.
346
423
  - Limit defaults to 100 and must be an integer from 1 through 100. The shared filters above are
347
424
  available alongside that limit. Storage applies addressedness, consumption and all supplied
348
- filters **before** limiting. `remaining` counts matching unconsumed activities outside the returned
349
- page in the same read transaction. It is not the global unread count or a durable cursor.
425
+ filters **before** limiting. `remaining` counts matching unconsumed activities for Agora and
426
+ messages for Channel outside the returned page in the same read transaction. It is not the global
427
+ unread count or a durable cursor.
350
428
  - A pending query consumes nothing, changes no read horizon and writes no delivery/presentation
351
429
  evidence. Deferrals, catch leases, cancellation and prior clipped presentation do not hide these
352
- explicit reads. Like catch, pending includes historical addressed receipt holes before a rejoin;
353
- it is not the proactive delivery eligibility query.
354
- - `ack(ids: readonly ActivityId[], control?)` returns `{consumed: number}`, the count of newly
430
+ explicit reads. Agora catch and pending include historical addressed activity receipt holes from
431
+ before a rejoin; they are not proactive delivery eligibility queries. Channel pending, receive and
432
+ ack use only addresses valid for the current endpoint generation. Ordinary unfenced `Channel.send`
433
+ messages remain addressed across takeover or rejoin until consumed.
434
+ - Agora `ack(ids: readonly ActivityId[], control?)` and Channel `ack(ids: readonly MessageId[], control?)`
435
+ return `{consumed: number}`, the count of newly
355
436
  consumed exact IDs. It validates that **every** supplied ID is addressed to this participant before
356
- recording any receipts. Malformed, nonexistent or unaddressed IDs reject the whole batch with
437
+ recording any receipts; Channel also checks that the address is valid for the current generation. Malformed, nonexistent or unaddressed IDs reject the whole batch with
357
438
  `invalid_args` (`reason: 'not_addressed'` for valid but unaddressed IDs). There is no implicit prefix
358
439
  acknowledgement, no match reselection and no requirement for an earlier pending call.
359
440
  - Duplicate IDs, repeated acknowledgement and an empty array are idempotent. Existing receipt
@@ -365,7 +446,8 @@ async function drain(endpoint: ChannelEndpoint, match: MetadataMatch) {
365
446
  Cancellation before the receipt transaction prevents consumption; a committed ack is not undone.
366
447
  - To drain all matching notices, keep querying and acknowledging pages until an **empty** page.
367
448
  Acknowledging one page exposes the next without a cursor. For read-only pagination instead, pass
368
- `cursor: {id: page.activities.at(-1)!.id, direction: 'after'}` with the same filters; earlier pages
449
+ `cursor: {id: page.messages.at(-1)!.id, direction: 'after'}` on a channel endpoint (or
450
+ `page.activities` on a Participant) with the same filters; earlier pages
369
451
  stay unconsumed. Do not silently stop after the default page or treat a snapshot `remaining: 0`
370
452
  as proof that concurrent producers have stopped. Query and
371
453
  ack are separate operations, not an exclusive claim or an exactly-once external-effect guarantee.
@@ -480,37 +562,32 @@ It overlaps at most four receiving sessions per dispatch, preserving order withi
480
562
  session and draining started work even on failure. Claims, final freshness checks and
481
563
  uncertain-delivery suppression remain unchanged. Paseo inventory discovery is asynchronous;
482
564
  overlapping attempts share only the in-flight inventory read, never a cached idle status.
483
- The standalone `discoverPaseoAgents` export now returns a promise.
484
-
485
- `discover(input, options?, control?)`, `inbox(input, owner, options?, control?)`,
486
- `diagnoseSquare(input, control?)`, and `migrateSquare(input, control?)` are ordinary public exports.
487
- They are not methods on a synthetic cross-square object. Diagnose/migrate resolve a path without
488
- opening a valid current-format Square first.
489
-
490
- ## Artifact format and explicit migration
491
-
492
- The current artifact format is 7. It stores opaque owners rather than harness coordinates and
493
- preserves format 6's attached/detached distinction. Seats, delivery evidence, cancellation and
494
- independent delivery deferrals remain separate from conversation activities and catch ownership.
495
- Ordinary opens refuse older formats with migration guidance and never convert them implicitly.
496
-
497
- Stop all sessions and services using the artifact, make an offline backup, then run
498
- `square --location <path.square> migrate` and `doctor` before reopening with upgraded runtimes.
499
- Supported formats 2–6 convert to 7 in one transaction. Format 6's Body coordinates become encoded
500
- owners while attachment, generations, wakes, evidence and revisions survive. Formats 4/5 first gain
501
- attached seats; format 4 also gains empty deferral support. Formats 4–6 never consult HostLedger.
502
- Historic host-cleanup departures cannot be distinguished from voluntary `done` and stay done.
503
- For formats 2/3, also freeze and back up the legacy HostLedger used for verified owner/evidence
504
- import; no authority is manufactured for done participants. Replay-critical native ambiguity
505
- refuses rather than disappears, including any nonempty legacy token-only tombstones.
506
- Current-format migration is a byte-preserving no-op. See [migration.md](migration.md) for the
507
- narrow metadata policy, source-preserving refusal and filesystem requirements. Updating a package
508
- does not migrate files or install a new runtime.
565
+ Paseo inventory discovery returns a promise.
566
+
567
+ `discover(input, options?, control?)`, `inbox(input, owner, options?, control?)`, and
568
+ `diagnoseSquare(input, control?)` are ordinary public exports. They are not methods on a synthetic
569
+ cross-square object. Diagnose resolves a path without opening a valid current-format Square first.
570
+
571
+ ## Artifact format
572
+
573
+ The current artifact format is 11. It is a breaking fresh-store format, not an upgrade of format
574
+ 10. A full Square file combines three separately owned layers in one SQLite database and one
575
+ transaction: the neutral storage revision header, the same Channel message/endpoint/delivery
576
+ tables used by a standalone Channel, and the Agora social tables plus the activity allocator and
577
+ square settings. Standalone Channel storage uses its own format identity and version and contains
578
+ only the neutral and Channel structures; it never creates or reads Agora tables.
579
+
580
+ Ordinary opens accept only the exact current identity, version and owned schema. Any other
581
+ identity or version is refused read-only, with no conversion, no write and no migrate command.
582
+ There is no automatic upgrade, compatibility decoder or recovery instruction. Stop any session
583
+ using an older `.square` file and start a fresh square; the old bytes are never touched.
509
584
 
510
585
  ## SDK breaking migration: opaque owners
511
586
 
512
- Artifact format 7 introduced opaque owners; format-6 artifacts required explicit migration before
513
- opening with that format. Current artifacts use format 8 and require explicit migration from format 7.
587
+ Artifact format 7 introduced opaque owners, format 8 removed hold/resume activities, and format 9
588
+ stores native messages. Format 10 unified message content while keeping Agora relations separate.
589
+ Format 11 splits neutral storage, standalone Channel and Agora, and has no migration from any
590
+ older format. Current artifacts must be created fresh at format 11.
514
591
  `Body`, `sameBody` and `validateBody` are removed from the core and public API. Replace identity
515
592
  objects with an explicit owner at each identity-bearing operation; do not put identity on a resource:
516
593
 
@@ -551,5 +628,5 @@ are deleted. Consumers must open/close an actual Square and select a Participant
551
628
  full archive reads with `limit: null`. SDK express now waits by default. MCP constructor configuration
552
629
  uses `path`, consistent with `Square.at`, rather than `squarePath`.
553
630
 
554
- External consumers must migrate before taking this source version. Editing source does not update
631
+ External consumers must adapt their code before taking this source version. Editing source does not update
555
632
  an installed plugin, build, or running extension.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astrosheep/square",
3
- "version": "0.3.106",
3
+ "version": "0.4.0",
4
4
  "description": "A shared public square where agents join, catch activity, express, and step out when done.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.106",
3
+ "version": "0.4.0",
4
4
  "description": "Native Claude Code inbox delivery for Square participants",
5
5
  "types": "./types.d.ts",
6
6
  "author": {
@@ -1,4 +1,4 @@
1
- export type SquareInboxBinding = { location: string; participant: string; session: string; generation?: `act/${number}` };
1
+ export type SquareInboxBinding = { location: string; participant: string; session: string; generation?: string };
2
2
  export type SquareInboxCoordinate = { sessionId: string; cwd: string; version: string; endpoint?: string };
3
3
  export type SquareInboxCancellation = { at: number; bindings?: SquareInboxBinding[] };
4
4
  export type SquareInboxState = { coordinate: SquareInboxCoordinate; bindings?: SquareInboxBinding[] };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "square",
3
- "version": "0.3.106",
3
+ "version": "0.4.0",
4
4
  "description": "Shared Square activity with reliable participant attention at Codex boundaries.",
5
5
  "author": {
6
6
  "name": "Square"
@@ -1,12 +1,13 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { createArtifactPort } from '../../artifact.js';
2
+ import { squareWakeProjection } from '../../wake-preview.js';
3
+ import { channelArtifact, createArtifactPort } from '../../artifact.js';
3
4
  import { deliverPending } from '../../delivery-operations.js';
4
- import { nativePendingPreview, observeSessionPending, projectSessionSeats } from '../../inbox.js';
5
- import { automaticWakeEligible, presentationBlocksWake } from '../../delivery.js';
5
+ import { nativePendingSource, observeSessionPending, projectSessionSeats } from '../../inbox.js';
6
+ import { automaticWakeEligible, nativePendingHead, presentationBlocksWake } from '../../delivery.js';
6
7
  import { type Seat } from '../../model.js';
7
8
  import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
8
- import type { SquareRead, WakeRequest, WakeTransportPort } from '../../ports.js';
9
- import { formatActivityId, parseActivityId } from '../../square-core.js';
9
+ import type { WakeRequest, WakeTransportPort } from '../../ports.js';
10
+ import { formatMessageId, parseMessageId } from '../../message.js';
10
11
  import { indexedSquareEntries } from '../../square-index.js';
11
12
  import { asSeatRef } from '../../seats.js';
12
13
  import { restoreSessionSeats } from '../../session-lifecycle.js';
@@ -18,17 +19,6 @@ const ownerFor = (sessionId: string) => harnessOwner({ channel: 'opencode', sess
18
19
  const payloadHash = (payload: string) => createHash('sha256').update(payload).digest('hex');
19
20
  export interface OpenCodeContextMessage { readonly id?: string; readonly role: string; readonly content: readonly { readonly type: string; readonly text?: string }[] }
20
21
 
21
- function preview(facts: SquareRead, location: string, captured: Pick<Seat, 'participant' | 'owner' | 'generation'>, index: number) {
22
- const seat = facts.seat(captured.participant);
23
- if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== captured.generation || seat.owner !== captured.owner) return undefined;
24
- const wake = seat.wakeAddress;
25
- if (wake?.kind !== routeKind) return undefined;
26
- return nativePendingPreview(facts, {
27
- location, routeKind, generation: seat.generation, participant: seat.participant,
28
- actIndex: index, address: wake.address, now: Date.now(),
29
- });
30
- }
31
-
32
22
  export async function indexedOpenCodeSessions(env: NodeJS.ProcessEnv): Promise<readonly string[]> {
33
23
  return [...new Set((await indexedSquareEntries({ }, env)).flatMap((entry) => {
34
24
  const coordinate = decodeHarnessOwner(entry.owner);
@@ -49,9 +39,8 @@ function createOpenCodeWakeTransport(target: OpenCodeNativeSession, lifetime: Ab
49
39
  probe: async (route) => !lifetime.aborted && route.kind === routeKind
50
40
  && route.address.sessionId === target.sessionId,
51
41
  async attempt(request: WakeRequest, timeoutMs: number, beforeSend) {
52
- const index = parseActivityId(request.activity);
53
- const artifact = request.artifact;
54
- if (artifact === undefined || request.claimToken === undefined || index === undefined
42
+ const presentation = request.presentation;
43
+ if (request.claimToken === undefined || parseMessageId(request.message) === undefined || presentation === undefined
55
44
  || request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
56
45
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
57
46
  }
@@ -59,18 +48,14 @@ function createOpenCodeWakeTransport(target: OpenCodeNativeSession, lifetime: Ab
59
48
  const started = Date.now();
60
49
  if (signal.aborted || !await beforeSend())
61
50
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
62
- const { value: prepared } = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
63
- if (prepared === undefined) return { outcome: 'not-capable', diagnostic: 'OpenCode attention is no longer current.' };
64
51
  const inputId = createNativeInputId();
65
- const correlation = { harness: 'opencode' as const, token: inputId, payloadHash: payloadHash(prepared.payload) };
66
- if (!await artifact.prepareDelivery({ seat: request.seat, claimToken: request.claimToken, correlation }, signal))
52
+ const correlation = { harness: 'opencode' as const, token: inputId, payloadHash: payloadHash(presentation.payload) };
53
+ if (!await request.channel.prepare({ correlation,
54
+ coverage: presentation.coverage, ...(presentation.description === undefined ? {} : { description: presentation.description }) }, signal))
67
55
  return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
68
- const fresh = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
69
- if (fresh.value === undefined || payloadHash(fresh.value.payload) !== correlation.payloadHash)
70
- return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before native send.' };
71
56
  if (signal.aborted || !await beforeSend())
72
57
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
73
- const result = await sendNativeText(target, prepared.payload, {
58
+ const result = await sendNativeText(target, presentation.payload, {
74
59
  inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
75
60
  });
76
61
  if (result.state === 'accepted') return { outcome: 'accepted' };
@@ -101,25 +86,27 @@ export async function observeOpenCodeContext(sessionId: string, messages: readon
101
86
  return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
102
87
  const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
103
88
  const hash = token === undefined ? undefined : observed.get(token);
104
- const index = parseActivityId(evidence.activity);
89
+ const index = parseMessageId(evidence.message), now = Date.now();
105
90
  if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
106
- || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
107
- || evidence.correlation?.payloadHash !== hash) return [];
108
- const prepared = preview(facts, location, seat, index);
109
- if (prepared === undefined || payloadHash(prepared.payload) !== hash
110
- || presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.activity), Date.now())) return [];
111
- return [{ evidence, index, hash, clipped: prepared.clipped }];
91
+ || evidence.route === undefined || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
92
+ || evidence.correlation?.payloadHash !== hash
93
+ || evidence.route.kind !== routeKind
94
+ || presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.message), now)) return [];
95
+ const source = nativePendingSource(facts, { location, participant: seat.participant, generation: seat.generation,
96
+ owner: seat.owner, index, route: evidence.route, now });
97
+ if (source === undefined) return [];
98
+ return [{ evidence, index, hash, clipped: evidence.coverage === 'clipped' }];
112
99
  });
113
100
  }, signal);
114
101
  for (const { evidence, index, hash, clipped } of matches) {
115
102
  if (signal.aborted || evidence.claimToken === undefined) return;
116
103
  await presentPending({ artifact, location, participant: seat.participant,
117
- activity: formatActivityId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() {} }, markSeen: !clipped, signal,
104
+ message: formatMessageId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() {} }, markSeen: !clipped, signal,
118
105
  current: (facts) => {
119
- const current = facts.seat(seat.participant);
120
- const prepared = current === undefined || !sameSeat(current, seat) ? undefined : preview(facts, location, seat, index);
121
106
  const correlation = facts.deliveryByToken(evidence.claimToken!);
122
- return prepared !== undefined && payloadHash(prepared.payload) === hash
107
+ return nativePendingHead(facts, { location, participant: seat.participant, generation: seat.generation,
108
+ owner: seat.owner, index, route: evidence.route!, now: Date.now() }) !== undefined
109
+ && ['dispatching', 'unknown', 'accepted'].includes(correlation?.outcome ?? '')
123
110
  && correlation?.correlation?.harness === 'opencode'
124
111
  && correlation.correlation.token !== undefined
125
112
  && correlation.correlation.payloadHash === hash;
@@ -150,11 +137,11 @@ export async function receiveOpenCodePending(target: OpenCodeNativeSession, env:
150
137
  const { value: notifications } = await artifact.read((facts) => {
151
138
  if (!sameSeat(facts.seat(seat.participant), seat)) return [];
152
139
  return membership.notifications.filter((note) => automaticWakeEligible(
153
- facts.deliveryEvidence(seat.participant, seat.generation, formatActivityId(note.actIndex)), Date.now()));
140
+ facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.messageIndex)), Date.now()));
154
141
  }, signal);
155
142
  for (const note of notifications) {
156
143
  if (signal.aborted) return;
157
- await deliverPending({ artifact, transport, location, activity: formatActivityId(note.actIndex), now: Date.now() });
144
+ await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location, message: formatMessageId(note.messageIndex), now: Date.now() });
158
145
  }
159
146
  } catch (error) {
160
147
  if (signal.aborted) throw signal.reason ?? error;
@@ -194,7 +181,7 @@ export async function cancelOpenCodeDeliveries(sessionId: string, env: NodeJS.Pr
194
181
  if (seat.attachment !== 'attached') continue;
195
182
  const artifact = createArtifactPort(location, undefined, signal);
196
183
  try {
197
- const { value: through } = await artifact.read((facts) => facts.header().lastActIndex, signal);
184
+ const { value: through } = await artifact.read((facts) => facts.channel.header().lastMessageIndex, signal);
198
185
  if (through >= 0) await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
199
186
  } finally { await artifact.close(); }
200
187
  }
@@ -3,14 +3,15 @@ import { Container, Markdown, Spacer, Text } from '@earendil-works/pi-tui';
3
3
  import { z } from 'zod/v4';
4
4
 
5
5
  import { Square, harnessOwner } from '../../dist/square-api.js';
6
- import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, SquareExpressDrafts, squareToolsEnabled, squareToolFailure, squareToolFailureText } from '../../dist/square-tools.js';
6
+ import { SQUARE_TOOLS, SQUARE_TOOL_INSTRUCTIONS, createSquareToolRunner, squareToolsEnabled } from '../../dist/square-tools.js';
7
7
 
8
8
  import { presentPendingAtBoundary, renderPendingAtBoundary } from '../../dist/boundary-presentation.js';
9
9
  import { operationEnv } from '../../dist/participant-identity.js';
10
10
  import { detachSessionSeats, restoreSessionSeats } from '../../dist/session-lifecycle.js';
11
11
  import { projectSessionSeats, observeSessionPending, pendingKey } from '../../dist/inbox.js';
12
- import { previewAttentionBody, renderAttentionDescription, renderAttentionFull } from '../../dist/attention-presentation.js';
12
+ import { projectMessageNotification } from '../../dist/attention-presentation.js';
13
13
  import { formatActivityId } from '../../dist/square-core.js';
14
+ import { formatMessageId } from '../../dist/message.js';
14
15
  import { createPiReceiver, sendPiMessage } from '../../dist/packages/agent-delivery/src/pi.js';
15
16
 
16
17
  class PiDeliveryDroppedError extends Error {
@@ -26,7 +27,7 @@ export function pendingInbox(inbox) {
26
27
 
27
28
  export function inboxKeys(inbox) {
28
29
  return pendingInbox(inbox).flatMap((item) => item.notifications.map((note) =>
29
- pendingKey(item, note.actIndex)
30
+ pendingKey(item, note.messageIndex)
30
31
  ));
31
32
  }
32
33
 
@@ -40,25 +41,26 @@ function framePiMessage(content) {
40
41
 
41
42
  /** Renderer-only card facts for Pi and Paseo; Pi never sends `details` to the model. */
42
43
  export function pendingCardDetails(pending) {
43
- const items = pending.flatMap((membership) => membership.notifications.map((note) => ({
44
+ const items = pending.flatMap((membership) => membership.notifications.map((note) => {
45
+ const projected = projectMessageNotification({ squarePath: membership.location, recipient: membership.seat.participant,
46
+ actor: note.actor, body: note.body, ...(note.preview === undefined ? {} : { preview: note.preview }),
47
+ ...(note.actIndex === undefined || note.route === undefined ? {} : { agora: { actIndex: note.actIndex, route: note.route } }) });
48
+ return ({
44
49
  square: path.basename(membership.location, '.square'),
45
50
  actor: note.actor,
46
51
  recipient: membership.seat.participant,
47
52
  route: note.route,
48
- act: formatActivityId(note.actIndex),
49
- preview: previewAttentionBody(note.body, note.delivery),
50
- description: note.delivery?.mode === 'direct'
51
- ? `${note.actor} → ${membership.seat.participant}`
52
- : renderAttentionDescription({ actor: note.actor, route: note.route }),
53
+ act: note.actIndex === undefined ? formatMessageId(note.messageIndex) : formatActivityId(note.actIndex),
54
+ preview: projected.preview,
55
+ description: projected.description,
53
56
  body: note.body,
54
- })));
57
+ }); }));
55
58
  if (items.length === 0) return undefined;
56
- const fullText = pending.flatMap((membership) => membership.notifications.map((note) => renderAttentionFull({
57
- squarePath: membership.location,
58
- actIndex: note.actIndex,
59
- recipient: membership.seat.participant,
60
- ...note,
61
- }))).join('\n\n');
59
+ const fullText = pending.flatMap((membership) => membership.notifications.map((note) => projectMessageNotification({
60
+ squarePath: membership.location, recipient: membership.seat.participant, actor: note.actor, body: note.body,
61
+ ...(note.preview === undefined ? {} : { preview: note.preview }),
62
+ ...(note.actIndex === undefined || note.route === undefined ? {} : { agora: { actIndex: note.actIndex, route: note.route } }),
63
+ }).full)).join('\n\n');
62
64
  return { items, fullText };
63
65
  }
64
66
 
@@ -143,7 +145,7 @@ export function renderSquareCard(message, { expanded, outputPad = 1 }, theme) {
143
145
  }
144
146
 
145
147
  export default function squarePiExtension(pi) {
146
- const drafts = new SquareExpressDrafts();
148
+ const invoke = createSquareToolRunner();
147
149
  if (squareToolsEnabled(process.env)) for (const tool of SQUARE_TOOLS) {
148
150
  pi.registerTool({
149
151
  name: `square_${tool.name}`,
@@ -153,41 +155,16 @@ export default function squarePiExtension(pi) {
153
155
  exposure: 'codemode',
154
156
  namespace: { name: 'square', description: 'A shared square where agents gather to talk, think, and act together.', instructions: SQUARE_TOOL_INSTRUCTIONS },
155
157
  async execute(_id, args, signal, _onUpdate, ctx) {
156
- let toolArgs = args;
157
- let owner;
158
- let draftToken;
159
- let expressAttempted = false;
160
- let square;
161
- try {
158
+ const { text, isError } = await invoke(tool, args, { signal }, () => {
162
159
  const sessionId = ctx.sessionManager.getSessionId();
163
- owner = harnessOwner({ channel: 'pi', session: sessionId });
164
- if (tool.name === 'express') {
165
- const resolved = drafts.resolve(toolArgs, owner);
166
- toolArgs = resolved.args;
167
- draftToken = resolved.token;
168
- }
169
- square = await Square.at({
170
- cwd: ctx.cwd,
171
- env: operationEnv('pi', sessionId, process.env),
172
- path: toolArgs.location,
173
- }, { signal });
174
- let text;
175
- try {
176
- expressAttempted = tool.name === 'express';
177
- text = await tool.run(square, toolArgs, { signal }, { owner });
178
- drafts.consume(draftToken);
179
- }
180
- finally { await square.close(); }
181
- return { content: [{ type: 'text', text }] };
182
- } catch (error) {
183
- const failure = squareToolFailure(error, signal);
184
- const draft = tool.name === 'express' ? drafts.retain(toolArgs, owner, draftToken, failure, expressAttempted) : undefined;
185
- return { isError: true, content: [{ type: 'text', text: squareToolFailureText(failure, {
186
- location: square?.location ?? toolArgs.location ?? process.env.SQUARE_LOCATION,
187
- participant: toolArgs.as,
188
- draft,
189
- }) }] };
190
- }
160
+ return {
161
+ identity: { owner: harnessOwner({ channel: 'pi', session: sessionId }) },
162
+ open: (location, control) => Square.at({
163
+ cwd: ctx.cwd, env: operationEnv('pi', sessionId, process.env), path: location,
164
+ }, control),
165
+ };
166
+ }, process.env.SQUARE_LOCATION);
167
+ return { ...(isError ? { isError } : {}), content: [{ type: 'text', text }] };
191
168
  },
192
169
  });
193
170
  }
@@ -1,11 +0,0 @@
1
- import type { DatabaseSync } from 'node:sqlite';
2
- export declare const FORMAT4_ADDED_OBJECTS: ReadonlySet<string>;
3
- export declare const FORMAT4_REPLACED_OBJECTS: ReadonlySet<string>;
4
- /** Historical activity framing only; no runtime activity decoder accepts these kinds. */
5
- export declare function legacyActivitySchema(currentSchemaSql: string): string;
6
- /**
7
- * Read-only recognition of the exact row framing accepted by explicit migration.
8
- * It inspects identity, owned schema and journal mode only; it never reads row
9
- * contents (including legacy routes), changes pragmas, or performs DDL.
10
- */
11
- export declare function legacyRowFramingIssue(database: DatabaseSync, applicationId: number, version: 2 | 3, location: string, currentSchemaSql: string): string | undefined;