@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,13 +1,15 @@
1
1
  import { DatabaseSync as MemoryDatabase } from 'node:sqlite';
2
- import { SquareError, nameKey, isWakeRouteKind } from './model.js';
2
+ import { SquareError, nameKey, isWakeRouteKind, validateBody, validateActivityMetadata, activityMetadataHasContent } from './model.js';
3
3
  import { deriveLanding, landingFacts } from './square-domain.js';
4
+ import { legacyRowFramingIssue } from './square-legacy-framing.js';
4
5
  export function emptyRuntimeState(nextActIndex = 0) {
5
6
  return { nextActIndex, observations: {}, leases: {} };
6
7
  }
7
8
  export const APPLICATION_ID = 0x53515245;
8
9
  const V1_USER_VERSION = 1;
9
10
  export const V2_USER_VERSION = 2;
10
- export const CURRENT_USER_VERSION = 3;
11
+ export const V3_USER_VERSION = 3;
12
+ export const CURRENT_USER_VERSION = 4;
11
13
  export const V1_SNAPSHOT_TABLE = 'square_snapshot';
12
14
  const V1_SNAPSHOT_DDL = `CREATE TABLE ${V1_SNAPSHOT_TABLE} (id INTEGER PRIMARY KEY CHECK(id=1), revision INTEGER NOT NULL CHECK(revision>=0), state TEXT NOT NULL)`;
13
15
  const V1_SNAPSHOT_COLUMNS = [
@@ -52,7 +54,6 @@ export function validateLegacyFraming(database, applicationId, location = '') {
52
54
  function normalizedLegacySql(sql) {
53
55
  return sql.trim().replace(/\s+/g, ' ').replace(/\s*(>=|[(),=])\s*/g, '$1');
54
56
  }
55
- const V2_ACTIVITY_KIND_CHECK = "kind IN ('join','done','say','hold','resume','read','listen','ignore')";
56
57
  const CURRENT_ACTIVITY_KIND_CHECK = "kind IN ('join','done','say','hold','resume','read','listen','ignore','unlisten')";
57
58
  export const ACTIVITIES_TABLE_SQL = `CREATE TABLE activities (
58
59
  act_index INTEGER PRIMARY KEY CHECK (act_index BETWEEN 0 AND 9007199254740991),
@@ -77,6 +78,7 @@ export const ACTIVITY_INDEXES_SQL = [
77
78
  export const SCHEMA_SQL = `CREATE TABLE square_meta (
78
79
  id INTEGER PRIMARY KEY CHECK (id = 1),
79
80
  revision INTEGER NOT NULL CHECK (revision BETWEEN 0 AND 9007199254740991),
81
+ delivery_revision INTEGER NOT NULL CHECK (delivery_revision BETWEEN 0 AND 9007199254740991),
80
82
  next_act_index INTEGER NOT NULL CHECK (next_act_index BETWEEN 0 AND 9007199254740991),
81
83
  hard_cap INTEGER CHECK (hard_cap BETWEEN 1 AND 9007199254740991),
82
84
  throttle_per_minute INTEGER CHECK (throttle_per_minute BETWEEN 1 AND 9007199254740991),
@@ -95,22 +97,45 @@ CREATE TABLE receipts (
95
97
  CREATE INDEX receipt_owner_time ON receipts(owner_json, at);
96
98
  CREATE TABLE catch_leases (
97
99
  owner_json TEXT COLLATE BINARY PRIMARY KEY NOT NULL,
100
+ generation_json TEXT NOT NULL DEFAULT '""',
98
101
  lease_id_json TEXT NOT NULL,
99
102
  heartbeat_at REAL NOT NULL,
100
103
  expires_at REAL NOT NULL CHECK (expires_at >= heartbeat_at),
101
104
  filter_json TEXT CHECK (filter_json IS NULL OR json_valid(filter_json))
102
105
  ) WITHOUT ROWID;
103
106
 
104
- CREATE TABLE routes (
105
- position INTEGER PRIMARY KEY CHECK (position >= 0),
107
+ CREATE TABLE seats (
108
+ participant_key_json TEXT COLLATE BINARY PRIMARY KEY NOT NULL,
109
+ participant_json TEXT NOT NULL,
110
+ channel_json TEXT NOT NULL,
111
+ session_json TEXT NOT NULL,
112
+ agent_json TEXT,
113
+ generation_json TEXT NOT NULL,
114
+ wake_address_json TEXT CHECK (wake_address_json IS NULL OR json_valid(wake_address_json))
115
+ ) WITHOUT ROWID;
116
+ CREATE INDEX seat_body ON seats(channel_json, session_json, agent_json);
117
+ CREATE TABLE delivery_evidence (
106
118
  participant_key_json TEXT COLLATE BINARY NOT NULL,
107
- session_json TEXT COLLATE BINARY NOT NULL,
108
- kind TEXT NOT NULL CHECK (kind IN ('opencode-server','codex-queue','claude-native','paseo')),
109
- updated_at REAL NOT NULL,
110
- payload_json TEXT NOT NULL CHECK (json_valid(payload_json))
111
- );
112
- CREATE INDEX route_participant ON routes(participant_key_json, position);
113
- CREATE INDEX route_session ON routes(session_json, position);
119
+ participant_json TEXT NOT NULL,
120
+ generation_json TEXT NOT NULL,
121
+ act_index INTEGER NOT NULL CHECK (act_index BETWEEN 0 AND 9007199254740991),
122
+ kind TEXT NOT NULL CHECK (kind IN ('wake','presentation')),
123
+ attempt_n INTEGER NOT NULL CHECK (attempt_n >= 0),
124
+ outcome TEXT NOT NULL CHECK (outcome IN ('dispatching','accepted','failed','unknown','released','presented','clipped')),
125
+ at REAL NOT NULL,
126
+ expires_at REAL,
127
+ claim_token_json TEXT,
128
+ payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),
129
+ PRIMARY KEY (participant_key_json,generation_json,act_index,kind,attempt_n)
130
+ ) WITHOUT ROWID;
131
+ CREATE UNIQUE INDEX delivery_claim_token ON delivery_evidence(claim_token_json) WHERE claim_token_json IS NOT NULL;
132
+ CREATE INDEX delivery_activity ON delivery_evidence(participant_key_json,generation_json,act_index,kind,attempt_n);
133
+ CREATE TABLE delivery_cancellations (
134
+ participant_key_json TEXT COLLATE BINARY NOT NULL,
135
+ generation_json TEXT NOT NULL,
136
+ through_index INTEGER NOT NULL CHECK (through_index BETWEEN 0 AND 9007199254740991),
137
+ PRIMARY KEY (participant_key_json,generation_json)
138
+ ) WITHOUT ROWID;
114
139
 
115
140
  CREATE TABLE participants (
116
141
  name_key_json TEXT COLLATE BINARY PRIMARY KEY NOT NULL,
@@ -142,8 +167,6 @@ CREATE TABLE landed_audience (
142
167
  ) WITHOUT ROWID;
143
168
  CREATE INDEX audience_recipient ON landed_audience(recipient_key_json, act_index);`;
144
169
  const EXPECTED_SCHEMA_SQL = SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
145
- export const SCHEMA_SQL_V2 = SCHEMA_SQL.replace(CURRENT_ACTIVITY_KIND_CHECK, V2_ACTIVITY_KIND_CHECK);
146
- const EXPECTED_SCHEMA_SQL_V2 = SCHEMA_SQL_V2.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
147
170
  const encode = JSON.stringify;
148
171
  const key = (name) => encode(nameKey(name));
149
172
  export function invalidArtifact(detail) {
@@ -226,8 +249,19 @@ export function validateStoredActivity(value) {
226
249
  string(a.actor, true);
227
250
  switch (a.kind) {
228
251
  case 'say':
229
- shape(a, [...common, 'body'], ['mentions', 'reach', 'reply']);
230
- string(a.body);
252
+ shape(a, [...common], ['body', 'mentions', 'reach', 'reply', 'metadata']);
253
+ if (Object.hasOwn(a, 'body'))
254
+ string(a.body);
255
+ if (Object.hasOwn(a, 'metadata')) {
256
+ requireFact(a.metadata !== undefined, 'say metadata is malformed.');
257
+ try {
258
+ validateActivityMetadata(a.metadata);
259
+ }
260
+ catch {
261
+ throw invalidArtifact('say metadata is malformed.');
262
+ }
263
+ }
264
+ requireFact((Object.hasOwn(a, 'body') && typeof a.body === 'string' && a.body.trim() !== '') || activityMetadataHasContent(a.metadata), 'say must contain a non-empty body or non-empty metadata.');
231
265
  if (Object.hasOwn(a, 'mentions'))
232
266
  strings(a.mentions);
233
267
  if (Object.hasOwn(a, 'reach'))
@@ -258,7 +292,7 @@ export function validateStoredActivity(value) {
258
292
  function activity(row) {
259
293
  const h = head(row), payload = object(json(row.payload_json));
260
294
  // Payload cannot shadow indexed structural columns.
261
- const allowed = h.kind === 'say' ? ['body', 'mentions'] : h.kind === 'done' || h.kind === 'hold' ? ['body'] : h.kind === 'read' ? ['through'] : h.kind === 'listen' || h.kind === 'ignore' || h.kind === 'unlisten' ? ['target'] : [];
295
+ const allowed = h.kind === 'say' ? ['body', 'mentions', 'metadata'] : h.kind === 'done' || h.kind === 'hold' ? ['body'] : h.kind === 'read' ? ['through'] : h.kind === 'listen' || h.kind === 'ignore' || h.kind === 'unlisten' ? ['target'] : [];
262
296
  requireFact(Object.keys(payload).every(k => allowed.includes(k)));
263
297
  return immutable(validateStoredActivity({ ...h, ...payload }));
264
298
  }
@@ -296,34 +330,87 @@ export function validateWatchLease(value) {
296
330
  // Sorted object keys express small-value structural equality, never whole-state comparison.
297
331
  function stableJson(value) {
298
332
  if (value === null || typeof value !== 'object') {
299
- requireFact(value === null || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number' && Number.isFinite(value), 'route contains a non-finite or unsupported JSON value.');
333
+ requireFact(value === null || typeof value === 'string' || typeof value === 'boolean' || typeof value === 'number' && Number.isFinite(value), 'value contains a non-finite or unsupported JSON value.');
300
334
  return encode(value);
301
335
  }
302
336
  if (Array.isArray(value))
303
337
  return '[' + value.map(stableJson).join(',') + ']';
304
338
  return '{' + Object.keys(value).sort().map(k => encode(k) + ':' + stableJson(value[k])).join(',') + '}';
305
339
  }
306
- export function validateRoute(value) {
307
- const r = shape(value, ['location', 'participant', 'sessionId', 'channel', 'kind', 'address', 'updatedAt', 'extra'], ['epoch']);
308
- for (const k of ['location', 'participant', 'sessionId', 'channel'])
309
- string(r[k]);
310
- requireFact(isWakeRouteKind(r.kind));
311
- finite(r.updatedAt);
312
- requireFact(Object.values(object(r.address)).every(v => typeof v === 'string'));
313
- const extra = object(r.extra);
314
- requireFact(Object.keys(extra).every(k => !['location', 'participant', 'sessionId', 'channel', 'kind', 'address', 'updatedAt', 'epoch'].includes(k)));
315
- stableJson(extra);
316
- if (Object.hasOwn(r, 'epoch'))
317
- stableJson(r.epoch);
340
+ function activityId(value) {
341
+ const parsed = typeof value === 'string' ? /^act\/(0|[1-9]\d*)$/.exec(value) : null;
342
+ requireFact(parsed !== null, 'activity generation is malformed.');
343
+ const index = Number(parsed[1]);
344
+ requireFact(Number.isSafeInteger(index));
318
345
  return value;
319
346
  }
320
- function route(row) {
321
- integer(row.position);
322
- const payload = shape(json(row.payload_json), ['location', 'participant', 'channel', 'address', 'extra'], ['epoch']);
323
- requireFact(row.participant_key_json === key(string(payload.participant)));
324
- return immutable(validateRoute({
325
- ...payload, sessionId: decoded(row.session_json), kind: row.kind, updatedAt: row.updated_at
326
- }));
347
+ function activityIndex(value) {
348
+ const parsed = activityId(value);
349
+ return integer(Number(parsed.slice(4)));
350
+ }
351
+ function wakeAddress(value) {
352
+ const address = shape(value, ['kind', 'address']);
353
+ requireFact(isWakeRouteKind(address.kind));
354
+ const entries = object(address.address);
355
+ requireFact(Object.values(entries).every((item) => typeof item === 'string'));
356
+ stableJson(entries);
357
+ return immutable({ kind: address.kind, address: entries });
358
+ }
359
+ function bodyFrom(row) {
360
+ const body = { channel: decoded(row.channel_json, true), session: decoded(row.session_json, true), ...(row.agent_json === null ? {} : { agent: decoded(row.agent_json, true) }) };
361
+ validateBody(body);
362
+ return immutable(body);
363
+ }
364
+ function seat(row) {
365
+ const participant = decoded(row.participant_json, true);
366
+ requireFact(row.participant_key_json === key(participant));
367
+ const generation = activityId(decoded(row.generation_json, true));
368
+ const parsedWake = row.wake_address_json === null ? undefined : wakeAddress(json(row.wake_address_json));
369
+ return immutable({ participant, body: bodyFrom(row), generation, ...(parsedWake === undefined ? {} : { wakeAddress: parsedWake }) });
370
+ }
371
+ function nativeCorrelation(value) {
372
+ const item = shape(value, ['harness'], ['token', 'messageId', 'payloadHash']);
373
+ requireFact(['claude', 'opencode', 'pi'].includes(string(item.harness)));
374
+ for (const key of ['token', 'messageId', 'payloadHash'])
375
+ if (Object.hasOwn(item, key))
376
+ string(item[key], true);
377
+ return immutable({ harness: item.harness,
378
+ ...(item.token === undefined ? {} : { token: item.token }),
379
+ ...(item.messageId === undefined ? {} : { messageId: item.messageId }),
380
+ ...(item.payloadHash === undefined ? {} : { payloadHash: item.payloadHash }) });
381
+ }
382
+ function deliveryEvidence(row) {
383
+ const payload = object(json(row.payload_json));
384
+ const participant = decoded(row.participant_json, true);
385
+ requireFact(row.participant_key_json === key(participant));
386
+ const generation = activityId(decoded(row.generation_json, true));
387
+ const activity = activityId(`act/${integer(row.act_index)}`);
388
+ requireFact(['wake', 'presentation'].includes(string(row.kind)) && ['dispatching', 'accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(string(row.outcome)));
389
+ requireFact(Number.isSafeInteger(row.attempt_n) && row.attempt_n >= 0);
390
+ const nativeObservations = payload.nativeObservations === undefined ? undefined : (() => {
391
+ requireFact(Array.isArray(payload.nativeObservations));
392
+ return payload.nativeObservations.map((value) => {
393
+ const item = shape(value, ['eventId', 'messageId', 'state', 'observedAt'], ['reason']);
394
+ requireFact(['held', 'released', 'dropped'].includes(string(item.state)));
395
+ return {
396
+ eventId: string(item.eventId, true), messageId: string(item.messageId, true),
397
+ state: item.state,
398
+ ...(Object.hasOwn(item, 'reason') ? { reason: string(item.reason) } : {}), observedAt: finite(item.observedAt),
399
+ };
400
+ });
401
+ })();
402
+ return immutable({
403
+ participant, generation, activity, kind: row.kind,
404
+ outcome: row.outcome, attemptN: integer(row.attempt_n), at: finite(row.at),
405
+ ...(row.expires_at === null ? {} : { expiresAt: finite(row.expires_at) }),
406
+ ...(row.claim_token_json === null ? {} : { claimToken: decoded(row.claim_token_json, true) }),
407
+ ...(payload.route === undefined ? {} : { route: wakeAddress(payload.route) }),
408
+ ...(payload.signature === undefined ? {} : { signature: string(payload.signature) }),
409
+ ...(payload.message === undefined ? {} : { message: string(payload.message) }),
410
+ ...(payload.diagnostic === undefined ? {} : { diagnostic: payload.diagnostic }),
411
+ ...(payload.correlation === undefined ? {} : { correlation: nativeCorrelation(payload.correlation) }),
412
+ ...(nativeObservations === undefined ? {} : { nativeObservations }),
413
+ });
327
414
  }
328
415
  function normalizedSql(sql) { return sql.trim().replace(/;$/, '').replace(/\s+/g, ' ').replace(/\s*([(),=])\s*/g, '$1'); }
329
416
  function validateOwnedSchema(db, expected, location) {
@@ -332,10 +419,16 @@ function validateOwnedSchema(db, expected, location) {
332
419
  requireFact(db.prepare('PRAGMA journal_mode').get().journal_mode === (location === ':memory:' ? 'memory' : 'delete'), 'unsupported journal mode.');
333
420
  }
334
421
  export function validateRowFraming(db, version, location = '') {
422
+ if (version === V2_USER_VERSION || version === V3_USER_VERSION) {
423
+ const issue = legacyRowFramingIssue(db, APPLICATION_ID, version, location, SCHEMA_SQL);
424
+ if (issue !== undefined)
425
+ throw invalidArtifact(issue);
426
+ return;
427
+ }
335
428
  const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
336
429
  requireFact(identity.application_id === APPLICATION_ID, 'unrelated SQLite database.');
337
430
  requireFact(identity.user_version === version, 'unsupported SQLite format version.');
338
- validateOwnedSchema(db, version === V2_USER_VERSION ? EXPECTED_SCHEMA_SQL_V2 : EXPECTED_SCHEMA_SQL, location);
431
+ validateOwnedSchema(db, EXPECTED_SCHEMA_SQL, location);
339
432
  }
340
433
  export function validateSqliteHeader(db, location = '') {
341
434
  const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
@@ -344,13 +437,15 @@ export function validateSqliteHeader(db, location = '') {
344
437
  validateLegacyFraming(db, APPLICATION_ID, location);
345
438
  throw new SquareError('invalid_args', `Square artifact at ${location} needs migration.\nStop sessions using it, back it up, then migrate and verify before restarting.`, { reason: 'migration_required' });
346
439
  }
347
- if (identity.user_version === V2_USER_VERSION) {
348
- validateRowFraming(db, V2_USER_VERSION, location);
440
+ if (identity.user_version === V2_USER_VERSION || identity.user_version === V3_USER_VERSION) {
441
+ validateRowFraming(db, identity.user_version, location);
349
442
  throw new SquareError('invalid_args', `Square artifact at ${location} needs migration.\nStop sessions using it, back it up, then migrate and verify before restarting.`, { reason: 'migration_required' });
350
443
  }
351
- requireFact(identity.user_version === CURRENT_USER_VERSION, 'unsupported SQLite format version.');
352
- validateRowFraming(db, CURRENT_USER_VERSION, location);
353
- return createSqliteDriver(db).header();
444
+ if (identity.user_version === CURRENT_USER_VERSION) {
445
+ validateRowFraming(db, CURRENT_USER_VERSION, location);
446
+ return createSqliteDriver(db).header();
447
+ }
448
+ throw invalidArtifact('unsupported SQLite format version.');
354
449
  }
355
450
  function activitySelection(filter, alias = 'a') {
356
451
  const terms = [], args = [];
@@ -390,7 +485,7 @@ function pageLimit(page) {
390
485
  return Math.min(integer(page.limit, 1) + 1, Number.MAX_SAFE_INTEGER);
391
486
  }
392
487
  export function createSqliteDriver(db) {
393
- let changed = false;
488
+ let changed = false, deliveryChanged = false;
394
489
  const all = (sql, args = []) => db.prepare(sql).all(...args);
395
490
  const one = (sql, args = []) => db.prepare(sql).get(...args);
396
491
  const run = (sql, args = []) => Number(db.prepare(sql).run(...args).changes);
@@ -420,14 +515,42 @@ export function createSqliteDriver(db) {
420
515
  const directedFrom = 'landed_audience d JOIN activities a ON a.act_index=d.act_index';
421
516
  const pendingFrom = `landed_audience d JOIN activities a ON a.act_index=d.act_index JOIN participants p ON p.name_key_json=d.recipient_key_json`;
422
517
  const pendingWhere = `p.joined=1 AND d.act_index>COALESCE(p.last_join_index,-1) AND NOT EXISTS (SELECT 1 FROM receipts r WHERE r.owner_json=p.name_json AND r.act_index=d.act_index)`;
518
+ const latestSeats = `seats s JOIN participants p ON p.name_key_json=s.participant_key_json
519
+ JOIN activities a ON a.act_index=p.last_join_index AND a.kind='join' AND a.actor_key_json=p.name_key_json
520
+ AND s.generation_json='"act/'||a.act_index||'"'`;
521
+ function bodySeats(body, lifecycle) {
522
+ validateBody(body);
523
+ return immutable(all(`SELECT s.* FROM ${latestSeats} WHERE p.${lifecycle}=1 AND s.channel_json=? AND s.session_json=? AND s.agent_json IS ? ORDER BY s.participant_key_json`, [encode(body.channel), encode(body.session), body.agent === undefined ? null : encode(body.agent)]).map(seat));
524
+ }
423
525
  const read = {
526
+ seat(name) {
527
+ const r = one(`SELECT s.* FROM ${latestSeats} WHERE p.joined=1 AND s.participant_key_json=?`, [key(name)]);
528
+ return r ? seat(r) : undefined;
529
+ },
530
+ seatsForBody: body => bodySeats(body, 'joined'),
531
+ completedSeatsForBody: body => bodySeats(body, 'done'),
532
+ deliveryEvidence(participant, generation, activityIndex) {
533
+ const args = [key(participant), encode(generation)];
534
+ const filter = activityIndex === undefined ? '' : ' AND act_index=?';
535
+ if (activityIndex !== undefined)
536
+ args.push(integer(Number(activityIndex.slice(4))));
537
+ return immutable(all(`SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=?${filter} ORDER BY act_index,kind,attempt_n`, args).map(deliveryEvidence));
538
+ },
539
+ deliveryByToken(claimToken) {
540
+ const row = one('SELECT * FROM delivery_evidence WHERE claim_token_json=?', [encode(claimToken)]);
541
+ return row === undefined ? undefined : deliveryEvidence(row);
542
+ },
543
+ cancelledThrough(participant, generation) {
544
+ const r = one('SELECT through_index FROM delivery_cancellations WHERE participant_key_json=? AND generation_json=?', [key(participant), encode(generation)]);
545
+ return r === undefined ? undefined : integer(r.through_index);
546
+ },
424
547
  header() {
425
548
  const rows = all('SELECT m.*, (SELECT COALESCE(MAX(act_index),-1) FROM activities) AS last_act_index FROM square_meta m');
426
549
  requireFact(rows.length === 1 && rows[0].id === 1, 'header row is malformed.');
427
550
  const r = rows[0], nextActIndex = integer(r.next_act_index), lastActIndex = integer(r.last_act_index, -1);
428
551
  requireFact(nextActIndex > lastActIndex, 'nextActIndex is behind the activity history.');
429
552
  return immutable({
430
- revision: integer(r.revision), nextActIndex, lastActIndex, hardCap: r.hard_cap === null ? null : integer(r.hard_cap, 1), ...(r.throttle_per_minute === null ? {} : { throttlePerMinute: integer(r.throttle_per_minute, 1) }), preamble: strings(json(r.preamble_json)), warmup: strings(json(r.warmup_json))
553
+ revision: integer(r.revision), deliveryRevision: integer(r.delivery_revision), nextActIndex, lastActIndex, hardCap: r.hard_cap === null ? null : integer(r.hard_cap, 1), ...(r.throttle_per_minute === null ? {} : { throttlePerMinute: integer(r.throttle_per_minute, 1) }), preamble: strings(json(r.preamble_json)), warmup: strings(json(r.warmup_json))
431
554
  });
432
555
  },
433
556
  participant(name) {
@@ -547,14 +670,63 @@ export function createSqliteDriver(db) {
547
670
  },
548
671
  receiptAssignments: () => immutable(all('SELECT owner_json,act_index FROM receipts ORDER BY owner_json,act_index').map(r => ({ owner: decoded(r.owner_json, true), index: integer(r.act_index) }))),
549
672
  lease(name) {
550
- const r = one('SELECT * FROM catch_leases WHERE owner_json=?', [encode(owner(name))]);
673
+ const canonical = owner(name), current = read.seat(canonical);
674
+ const r = current === undefined
675
+ ? one('SELECT * FROM catch_leases WHERE owner_json=?', [encode(canonical)])
676
+ : one('SELECT * FROM catch_leases WHERE owner_json=? AND (generation_json=? OR generation_json=?)', [encode(canonical), encode(current.generation), encode('')]);
551
677
  return r ? immutable(validateWatchLease({
552
678
  leaseId: decoded(r.lease_id_json, true), heartbeatAt: finite(r.heartbeat_at), expiresAt: finite(r.expires_at), ...(r.filter_json === null ? {} : { filter: json(r.filter_json) })
553
679
  })) : undefined;
554
680
  },
555
- routes: () => immutable(all('SELECT * FROM routes ORDER BY position').map(route)),
556
681
  };
557
682
  const writes = {
683
+ storeSeat(value) {
684
+ validateBody(value.body);
685
+ activityId(value.generation);
686
+ string(value.participant, true);
687
+ const existing = read.seat(value.participant);
688
+ if (existing !== undefined && stableJson(existing) === stableJson(value))
689
+ return false;
690
+ const wake = value.wakeAddress === undefined ? null : encode(value.wakeAddress);
691
+ run('INSERT INTO seats(participant_key_json,participant_json,channel_json,session_json,agent_json,generation_json,wake_address_json) VALUES(?,?,?,?,?,?,?) ON CONFLICT(participant_key_json) DO UPDATE SET participant_json=excluded.participant_json,channel_json=excluded.channel_json,session_json=excluded.session_json,agent_json=excluded.agent_json,generation_json=excluded.generation_json,wake_address_json=excluded.wake_address_json', [key(value.participant), encode(value.participant), encode(value.body.channel), encode(value.body.session), value.body.agent === undefined ? null : encode(value.body.agent), encode(value.generation), wake]);
692
+ run('DELETE FROM catch_leases WHERE owner_json=?', [encode(value.participant)]);
693
+ deliveryChanged = true;
694
+ return true;
695
+ },
696
+ updateWakeAddress(value, wakeAddress) {
697
+ const current = one('SELECT wake_address_json FROM seats WHERE participant_key_json=? AND generation_json=?', [key(value.participant), encode(value.generation)]);
698
+ if (wakeAddress !== undefined) {
699
+ requireFact(isWakeRouteKind(wakeAddress.kind));
700
+ requireFact(Object.values(wakeAddress.address).every((item) => typeof item === 'string'));
701
+ }
702
+ if (stableJson(current.wake_address_json === null ? null : json(current.wake_address_json)) === stableJson(wakeAddress ?? null))
703
+ return false;
704
+ const count = run('UPDATE seats SET wake_address_json=? WHERE participant_key_json=? AND generation_json=?', [wakeAddress === undefined ? null : encode(wakeAddress), key(value.participant), encode(value.generation)]);
705
+ if (count)
706
+ deliveryChanged = true;
707
+ return count > 0;
708
+ },
709
+ removeSeat(value) {
710
+ // Done hides current authority; retain only its latest coordinate for exact-body resume.
711
+ const row = one(`SELECT s.* FROM ${latestSeats} WHERE p.done=1 AND s.participant_key_json=?`, [key(value.participant)]);
712
+ if (row === undefined || decoded(row.generation_json) !== value.generation)
713
+ return false;
714
+ run('UPDATE seats SET wake_address_json=NULL WHERE participant_key_json=?', [key(value.participant)]);
715
+ run('DELETE FROM catch_leases WHERE owner_json=?', [encode(value.participant)]);
716
+ changed = true;
717
+ deliveryChanged = true;
718
+ return true;
719
+ },
720
+ cancelThrough(value, through) {
721
+ activityId(value.generation);
722
+ integer(through);
723
+ const prior = read.cancelledThrough(value.participant, value.generation) ?? -1;
724
+ if (through <= prior)
725
+ return false;
726
+ run('INSERT INTO delivery_cancellations(participant_key_json,generation_json,through_index) VALUES(?,?,?) ON CONFLICT(participant_key_json,generation_json) DO UPDATE SET through_index=excluded.through_index', [key(value.participant), encode(value.generation), through]);
727
+ deliveryChanged = true;
728
+ return true;
729
+ },
558
730
  applyLanding(effect) {
559
731
  const a = validateStoredActivity(effect.activity);
560
732
  requireFact(a.index >= read.header().nextActIndex && a.index < Number.MAX_SAFE_INTEGER, 'activity index cannot advance.');
@@ -605,47 +777,130 @@ export function createSqliteDriver(db) {
605
777
  changed = true;
606
778
  return count;
607
779
  },
608
- putLease(name, value) {
609
- const l = validateWatchLease(value), old = read.lease(name);
610
- if (old && stableJson(old) === stableJson(l))
780
+ putLease(name, value, generation) {
781
+ const l = validateWatchLease(value), canonical = owner(name);
782
+ const old = read.lease(canonical), row = one('SELECT generation_json FROM catch_leases WHERE owner_json=?', [encode(canonical)]);
783
+ if (generation !== undefined)
784
+ activityId(generation);
785
+ if (row !== undefined && generation !== undefined && decoded(row.generation_json) !== '' && decoded(row.generation_json) !== generation)
786
+ return false;
787
+ if (old && stableJson(old) === stableJson(l) && (generation === undefined || row?.generation_json === encode(generation)))
611
788
  return false;
612
- const canonical = owner(name);
613
789
  string(canonical, true);
614
- run('INSERT INTO catch_leases(owner_json,lease_id_json,heartbeat_at,expires_at,filter_json) VALUES(?,?,?,?,?) ON CONFLICT(owner_json) DO UPDATE SET lease_id_json=excluded.lease_id_json,heartbeat_at=excluded.heartbeat_at,expires_at=excluded.expires_at,filter_json=excluded.filter_json', [encode(canonical), encode(l.leaseId), l.heartbeatAt, l.expiresAt, l.filter === undefined ? null : encode(l.filter)]);
790
+ run('INSERT INTO catch_leases(owner_json,generation_json,lease_id_json,heartbeat_at,expires_at,filter_json) VALUES(?,?,?,?,?,?) ON CONFLICT(owner_json) DO UPDATE SET generation_json=excluded.generation_json,lease_id_json=excluded.lease_id_json,heartbeat_at=excluded.heartbeat_at,expires_at=excluded.expires_at,filter_json=excluded.filter_json', [encode(canonical), encode(generation ?? ''), encode(l.leaseId), l.heartbeatAt, l.expiresAt, l.filter === undefined ? null : encode(l.filter)]);
615
791
  changed = true;
616
792
  return true;
617
793
  },
618
- removeLease(name, expected) {
619
- const count = run('DELETE FROM catch_leases WHERE owner_json=? AND lease_id_json=?', [encode(owner(name)), encode(expected)]);
794
+ removeLease(name, expected, generation) {
795
+ const canonical = owner(name), row = one('SELECT generation_json FROM catch_leases WHERE owner_json=? AND lease_id_json=?', [encode(canonical), encode(expected)]);
796
+ if (row === undefined || generation !== undefined && decoded(row.generation_json) !== generation)
797
+ return false;
798
+ const count = run('DELETE FROM catch_leases WHERE owner_json=? AND lease_id_json=? AND generation_json=?', [encode(canonical), encode(expected), row.generation_json]);
620
799
  if (count)
621
800
  changed = true;
622
801
  return count > 0;
623
802
  },
624
- replaceRoutes(values) {
625
- values.forEach(validateRoute);
626
- if (stableJson(values) === stableJson(read.routes()))
803
+ };
804
+ const delivery = {
805
+ claimDelivery(input) {
806
+ requireFact(input.kind === 'wake' || input.kind === 'presentation');
807
+ const index = activityIndex(input.activity);
808
+ finite(input.at);
809
+ const leaseMs = integer(input.leaseMs, 1);
810
+ string(input.claimToken, true);
811
+ if (input.route !== undefined)
812
+ wakeAddress(input.route);
813
+ if (read.activity(index)?.kind !== 'say' || read.directedAt(input.seat.participant, index) === undefined)
814
+ return { status: 'stale' };
815
+ const cancelledThrough = read.cancelledThrough(input.seat.participant, input.seat.generation);
816
+ if (cancelledThrough !== undefined && index <= cancelledThrough)
817
+ return { status: 'stale' };
818
+ const rows = all('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? ORDER BY attempt_n DESC', [key(input.seat.participant), encode(input.seat.generation), index, input.kind]);
819
+ const decoded = rows.map(deliveryEvidence);
820
+ const terminal = decoded.find((row) => input.kind === 'wake'
821
+ ? row.outcome === 'accepted' || row.outcome === 'unknown'
822
+ : row.outcome === 'presented' || row.outcome === 'unknown');
823
+ if (terminal !== undefined)
824
+ return { status: 'terminal', evidence: terminal };
825
+ const dispatching = decoded.find((row) => row.outcome === 'dispatching');
826
+ if (dispatching !== undefined && (dispatching.expiresAt === undefined || dispatching.expiresAt > input.at))
827
+ return { status: 'busy', evidence: dispatching };
828
+ if (dispatching !== undefined) {
829
+ const updated = { ...dispatching, outcome: 'unknown', at: input.at, expiresAt: undefined, diagnostic: 'interrupted dispatch' };
830
+ const source = rows.find((row) => row.attempt_n === dispatching.attemptN);
831
+ run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=NULL,payload_json=? WHERE participant_key_json=? AND generation_json=? AND act_index=? AND kind=? AND attempt_n=?', ['unknown', input.at, encode({ ...object(json(source.payload_json)), diagnostic: 'interrupted dispatch' }), key(input.seat.participant), encode(input.seat.generation), index, input.kind, dispatching.attemptN]);
832
+ deliveryChanged = true;
833
+ return { status: 'terminal', evidence: updated };
834
+ }
835
+ const attemptN = (decoded[0]?.attemptN ?? -1) + 1;
836
+ const payload = input.route === undefined ? {} : { route: input.route };
837
+ const expiry = finite(input.at + leaseMs);
838
+ run('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(?,?,?,?,?,?,?,?,?,?,?)', [key(input.seat.participant), encode(input.seat.participant), encode(input.seat.generation), index, input.kind, attemptN, 'dispatching', input.at, expiry, encode(input.claimToken), encode(payload)]);
839
+ deliveryChanged = true;
840
+ return { status: 'acquired', claimToken: input.claimToken, attemptN };
841
+ },
842
+ prepareDelivery(input) {
843
+ const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
844
+ if (row === undefined || row.outcome !== 'dispatching')
627
845
  return false;
628
- run('DELETE FROM routes');
629
- values.forEach((r, position) => {
630
- const { sessionId, kind, updatedAt, extra, ...payload } = r;
631
- run('INSERT INTO routes(position,participant_key_json,session_json,kind,updated_at,payload_json) VALUES(?,?,?,?,?,?)', [position, key(r.participant), encode(sessionId), kind, updatedAt, encode({ ...payload, extra })]);
632
- });
633
- changed = true;
634
- return true;
846
+ const correlation = nativeCorrelation(input.correlation);
847
+ const payload = object(json(row.payload_json));
848
+ if (stableJson(payload.correlation ?? null) === stableJson(correlation))
849
+ return true;
850
+ const count = run('UPDATE delivery_evidence SET payload_json=? WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [encode({ ...payload, correlation }), key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
851
+ if (count)
852
+ deliveryChanged = true;
853
+ return count > 0;
854
+ },
855
+ completeDelivery(input) {
856
+ const index = activityIndex(input.activity);
857
+ finite(input.at);
858
+ requireFact(['accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(input.outcome));
859
+ string(input.claimToken, true);
860
+ const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND act_index=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), index, encode(input.claimToken)]);
861
+ if (row === undefined)
862
+ return false;
863
+ if (row.outcome === 'accepted' && input.outcome === 'accepted')
864
+ return true;
865
+ if (row.outcome !== 'dispatching' && !(row.outcome === 'unknown' && input.outcome === 'accepted'))
866
+ return false;
867
+ const previous = object(json(row.payload_json));
868
+ const payload = { ...previous, ...(input.signature === undefined ? {} : { signature: input.signature }), ...(input.message === undefined ? {} : { message: input.message }), ...(input.diagnostic === undefined ? {} : { diagnostic: input.diagnostic }), ...(input.correlation === undefined ? {} : { correlation: input.correlation }) };
869
+ const count = run('UPDATE delivery_evidence SET outcome=?,at=?,expires_at=NULL,payload_json=? WHERE participant_key_json=? AND generation_json=? AND act_index=? AND claim_token_json=?', [input.outcome, input.at, encode(payload), key(input.seat.participant), encode(input.seat.generation), index, encode(input.claimToken)]);
870
+ if (count)
871
+ deliveryChanged = true;
872
+ return count > 0;
873
+ },
874
+ observeNativeDelivery(input) {
875
+ const row = one('SELECT * FROM delivery_evidence WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
876
+ if (row === undefined || !['dispatching', 'accepted', 'unknown'].includes(string(row.outcome)))
877
+ return false;
878
+ const observation = input.observation;
879
+ requireFact(typeof observation.eventId === 'string' && observation.eventId.length > 0);
880
+ requireFact(typeof observation.messageId === 'string' && observation.messageId.length > 0);
881
+ requireFact(observation.state === 'held' || observation.state === 'released' || observation.state === 'dropped');
882
+ finite(observation.observedAt);
883
+ if (observation.reason !== undefined)
884
+ string(observation.reason);
885
+ const payload = object(json(row.payload_json));
886
+ const observations = payload.nativeObservations === undefined ? [] : payload.nativeObservations;
887
+ requireFact(Array.isArray(observations));
888
+ if (observations.some((item) => object(item).eventId === observation.eventId))
889
+ return true;
890
+ const next = [...observations, observation];
891
+ const count = run('UPDATE delivery_evidence SET payload_json=? WHERE participant_key_json=? AND generation_json=? AND claim_token_json=?', [encode({ ...payload, nativeObservations: next }), key(input.seat.participant), encode(input.seat.generation), encode(input.claimToken)]);
892
+ if (count)
893
+ deliveryChanged = true;
894
+ return count > 0;
635
895
  },
636
896
  };
637
- return Object.defineProperty(Object.assign(read, writes), 'changed', { get: () => changed });
638
- }
639
- export function legacyRouteFact(value) {
640
- const { location, participant, sessionId, channel, kind, address, updatedAt, epoch, ...extra } = value;
641
- return validateRoute({
642
- location, participant, sessionId, channel, kind, address, updatedAt, ...(Object.hasOwn(value, 'epoch') ? { epoch } : {}), extra
897
+ return Object.defineProperties(Object.assign(read, writes, delivery), {
898
+ changed: { get: () => changed },
899
+ deliveryChanged: { get: () => deliveryChanged },
643
900
  });
644
901
  }
645
902
  /** Explicit import only. Caller owns transaction, legacy validation and format publication. */
646
903
  export function initializeSqlite(db, state, revision = 0) {
647
- // Reject unsupported routes BEFORE any schema changes, also for direct fixture callers.
648
- const routes = (state.routes ?? []).map(r => legacyRouteFact(r));
649
904
  integer(revision);
650
905
  integer(state.runtime.nextActIndex);
651
906
  if (state.hardCap !== null)
@@ -653,7 +908,7 @@ export function initializeSqlite(db, state, revision = 0) {
653
908
  if (state.throttlePerMinute !== undefined)
654
909
  integer(state.throttlePerMinute, 1);
655
910
  db.exec(SCHEMA_SQL);
656
- db.prepare('INSERT INTO square_meta(id,revision,next_act_index,hard_cap,throttle_per_minute,preamble_json,warmup_json) VALUES(1,?,0,?,?,?,?)').run(revision, state.hardCap, state.throttlePerMinute ?? null, encode(strings(state.preamble)), encode(strings(state.warmup)));
911
+ db.prepare('INSERT INTO square_meta(id,revision,delivery_revision,next_act_index,hard_cap,throttle_per_minute,preamble_json,warmup_json) VALUES(1,?,0,0,?,?,?,?)').run(revision, state.hardCap, state.throttlePerMinute ?? null, encode(strings(state.preamble)), encode(strings(state.warmup)));
657
912
  const driver = createSqliteDriver(db);
658
913
  for (const activity of state.acts)
659
914
  driver.applyLanding(deriveLanding(validateStoredActivity(activity), landingFacts(driver, activity)));
@@ -676,9 +931,7 @@ export function initializeSqlite(db, state, revision = 0) {
676
931
  const l = validateWatchLease(value);
677
932
  db.prepare('INSERT INTO catch_leases(owner_json,lease_id_json,heartbeat_at,expires_at,filter_json) VALUES(?,?,?,?,?)').run(encode(owner), encode(l.leaseId), l.heartbeatAt, l.expiresAt, l.filter === undefined ? null : encode(l.filter));
678
933
  }
679
- driver.replaceRoutes(routes);
680
934
  }
681
- /** Exceptional export projection for fixtures, never a runtime fallback. */
682
935
  export function exportSqliteState(db) {
683
936
  const d = createSqliteDriver(db), h = d.header();
684
937
  const observations = {}, leases = {};
@@ -698,10 +951,7 @@ export function exportSqliteState(db) {
698
951
  }), enumerable: true });
699
952
  }
700
953
  return {
701
- hardCap: h.hardCap, ...(h.throttlePerMinute === undefined ? {} : { throttlePerMinute: h.throttlePerMinute }), preamble: [...h.preamble], warmup: [...h.warmup], acts: db.prepare('SELECT * FROM activities ORDER BY act_index').all().map(activity), routes: d.routes().map(r => {
702
- const { extra, ...fields } = r;
703
- return { ...fields, ...extra };
704
- }), runtime: {
954
+ hardCap: h.hardCap, ...(h.throttlePerMinute === undefined ? {} : { throttlePerMinute: h.throttlePerMinute }), preamble: [...h.preamble], warmup: [...h.warmup], acts: db.prepare('SELECT * FROM activities ORDER BY act_index').all().map(activity), runtime: {
705
955
  nextActIndex: h.nextActIndex, observations, leases
706
956
  }
707
957
  };
@@ -0,0 +1,22 @@
1
+ import { type SquareErrorCode, type SquareErrorFacts } from './model.js';
2
+ import type { SquareBackends, SquareBackend } from './square-backend.js';
3
+ import type { ParsedSquareLocation } from './square-location.js';
4
+ import type { OperationControl, OperationFailureDetail, SquareAtInput, SquareBuildInput } from './square-facade.js';
5
+ export declare const STDIO_PROTOCOL_VERSION = 1;
6
+ export declare const STDIO_MAX_FRAME_BYTES: number;
7
+ export declare const identityVariables: (import("./participant-identity.js").HarnessSessionVariable | "CLAUDE_CODE_CHILD_SESSION")[];
8
+ export declare function wireIdentity(env: NodeJS.ProcessEnv): Record<string, string>;
9
+ export interface WireError {
10
+ readonly code: number;
11
+ readonly message: string;
12
+ readonly data?: {
13
+ readonly code?: SquareErrorCode;
14
+ readonly facts?: SquareErrorFacts;
15
+ readonly operation?: OperationFailureDetail;
16
+ };
17
+ }
18
+ export declare function encodeError(error: unknown): WireError;
19
+ export declare function unsupported(feature: string): never;
20
+ export declare function openStdioSquare(location: string, selected: Extract<ParsedSquareLocation, {
21
+ kind: 'stdio';
22
+ }>, input: SquareAtInput, backends: SquareBackends, build?: Pick<SquareBuildInput, 'markdown' | 'hardCap' | 'throttlePerMinute' | 'template' | 'force'>, control?: OperationControl): Promise<SquareBackend>;