@astrosheep/square 0.3.87 → 0.3.89
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.
- package/dist/artifact.js +11 -9
- package/dist/boundary-presentation.d.ts +3 -2
- package/dist/boundary-presentation.js +50 -77
- package/dist/cli/context.js +1 -3
- package/dist/delivery-deferral.d.ts +28 -0
- package/dist/delivery-deferral.js +83 -0
- package/dist/delivery-operations.js +8 -6
- package/dist/delivery.js +4 -1
- package/dist/inbox.js +21 -10
- package/dist/mcp.js +0 -3
- package/dist/model.d.ts +1 -0
- package/dist/ports.d.ts +11 -3
- package/dist/presentation-operations.d.ts +20 -1
- package/dist/presentation-operations.js +68 -44
- package/dist/sqlite-square.js +31 -6
- package/dist/square-domain.d.ts +1 -1
- package/dist/square-domain.js +12 -0
- package/dist/square-facade.d.ts +8 -0
- package/dist/square-migration.d.ts +2 -1
- package/dist/square-migration.js +25 -26
- package/dist/square-sqlite.d.ts +5 -2
- package/dist/square-sqlite.js +79 -6
- package/dist/square-stdio.d.ts +1 -1
- package/dist/square-stdio.js +30 -17
- package/dist/stdio-server.js +23 -3
- package/guides/backends.md +26 -13
- package/guides/change-waits.md +40 -11
- package/guides/claude-native.md +9 -9
- package/guides/migration.md +30 -12
- package/guides/operation-cancellation.md +16 -1
- package/guides/participant-ownership.md +60 -186
- package/guides/square-api.md +72 -9
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +125 -185
- package/plugins/paseo/client/square-message.tsx +9 -25
- package/plugins/paseo/client/transform.ts +5 -4
- package/plugins/paseo/index.client.tsx +14 -12
- package/plugins/paseo/package.json +3 -0
- package/plugins/paseo/shared/message.ts +50 -1
|
@@ -2,71 +2,95 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
3
|
import { isSquareError, sameBody } from './model.js';
|
|
4
4
|
import { pendingHead, sameJoin } from './delivery.js';
|
|
5
|
+
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
5
6
|
import { asSeatRef } from './seats.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const seat = facts.seat(captured.participant);
|
|
9
|
-
return seat !== undefined && sameBody(seat.body, captured.body) && seat.generation === captured.generation
|
|
10
|
-
&& sameJoin(facts, participant) && input.current?.(facts) !== false
|
|
11
|
-
&& pendingHead(facts, captured.participant, index) !== undefined;
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
/** Presentation is external; its claim and receipt/evidence commit are generation-fenced in the artifact. */
|
|
15
|
-
export async function presentPending(input) {
|
|
7
|
+
/** Prepare custody before any external effect. A batch prepares every included item first. */
|
|
8
|
+
export async function preparePendingPresentation(input) {
|
|
16
9
|
const index = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
|
|
17
10
|
if (index === undefined)
|
|
18
|
-
return
|
|
11
|
+
return undefined;
|
|
19
12
|
const initial = await input.artifact.read((facts) => {
|
|
20
13
|
const participant = facts.participant(input.participant);
|
|
21
14
|
const seat = facts.seat(input.participant);
|
|
22
15
|
if (!participant?.joined || seat === undefined || input.generation !== undefined && input.generation !== seat.generation
|
|
23
16
|
|| input.seat !== undefined && (input.seat.generation !== seat.generation || !sameBody(input.seat.body, seat.body))
|
|
24
|
-
|| input.current?.(facts) === false || !pendingHead(facts, participant.name, index)
|
|
17
|
+
|| input.current?.(facts) === false || !pendingHead(facts, participant.name, index)
|
|
18
|
+
|| deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) !== undefined)
|
|
25
19
|
return undefined;
|
|
26
20
|
const item = facts.activity(index);
|
|
27
21
|
return item?.kind === 'say' ? { participant, seat, item } : undefined;
|
|
28
22
|
}, input.signal).catch(() => undefined);
|
|
29
23
|
if (initial?.value === undefined)
|
|
30
|
-
return
|
|
24
|
+
return undefined;
|
|
31
25
|
const { participant, seat, item } = initial.value;
|
|
32
26
|
const ref = asSeatRef(seat);
|
|
33
|
-
const
|
|
27
|
+
const owns = (facts) => {
|
|
28
|
+
const current = facts.seat(seat.participant);
|
|
29
|
+
return current !== undefined && sameBody(current.body, seat.body) && current.generation === seat.generation
|
|
30
|
+
&& sameJoin(facts, participant);
|
|
31
|
+
};
|
|
34
32
|
const claimToken = randomUUID();
|
|
35
33
|
const claimed = await input.artifact.claimDelivery({ seat: ref, activity: formatActivityId(index), kind: 'presentation',
|
|
36
34
|
at: input.now ?? Date.now(), leaseMs: input.timeoutMs ?? 5000, claimToken }, input.signal).catch(() => ({ status: 'stale' }));
|
|
37
35
|
if (claimed.status !== 'acquired')
|
|
36
|
+
return undefined;
|
|
37
|
+
let effectObserved = false, released = false;
|
|
38
|
+
return {
|
|
39
|
+
activity: item,
|
|
40
|
+
async eligible() {
|
|
41
|
+
return (await input.artifact.read(facts => owns(facts) && input.current?.(facts) !== false
|
|
42
|
+
&& pendingHead(facts, participant.name, index) !== undefined
|
|
43
|
+
&& deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) === undefined, input.signal).catch(() => undefined))?.value === true;
|
|
44
|
+
},
|
|
45
|
+
async release(outcome = 'released', message) {
|
|
46
|
+
if (effectObserved || released)
|
|
47
|
+
return;
|
|
48
|
+
released = true;
|
|
49
|
+
await input.artifact.settleAs({ kind: 'participant', seat: ref }, facts => facts.completeDelivery({
|
|
50
|
+
seat: ref, activity: formatActivityId(index), claimToken, outcome, at: Date.now(),
|
|
51
|
+
...(message === undefined ? {} : { message }),
|
|
52
|
+
})).catch(() => false);
|
|
53
|
+
},
|
|
54
|
+
async recordPresented() {
|
|
55
|
+
// Success is an observed external fact, not another eligibility decision. Neither
|
|
56
|
+
// post-effect cancellation nor a new deferral can erase it and authorize replay.
|
|
57
|
+
effectObserved = true;
|
|
58
|
+
const outcome = input.markSeen === false ? 'clipped' : 'presented';
|
|
59
|
+
const { value: committed } = await input.artifact.settleAs({ kind: 'participant', seat: ref }, facts => {
|
|
60
|
+
if (!owns(facts))
|
|
61
|
+
return false;
|
|
62
|
+
if (!facts.completeDelivery({ seat: ref, activity: formatActivityId(index), claimToken, outcome, at: input.now ?? Date.now() }))
|
|
63
|
+
return false;
|
|
64
|
+
if (input.markSeen !== false)
|
|
65
|
+
facts.markSeen(participant.name, [index], input.now ?? Date.now());
|
|
66
|
+
return true;
|
|
67
|
+
}).catch(error => {
|
|
68
|
+
if (isSquareError(error) && error.code === 'already_joined')
|
|
69
|
+
return { value: false };
|
|
70
|
+
throw error;
|
|
71
|
+
});
|
|
72
|
+
return committed ? { presented: true, activity: item } : { presented: false };
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/** Eligibility precedes the sink; only captured ownership fences its truthful completion. */
|
|
77
|
+
export async function presentPending(input) {
|
|
78
|
+
const prepared = await preparePendingPresentation(input);
|
|
79
|
+
if (prepared === undefined)
|
|
38
80
|
return { presented: false };
|
|
39
|
-
const release = async (outcome, message) => input.artifact.completeDelivery({
|
|
40
|
-
seat: ref, activity: formatActivityId(index), claimToken, outcome, at: Date.now(),
|
|
41
|
-
...(message === undefined ? {} : { message }),
|
|
42
|
-
}, input.signal).catch(() => false);
|
|
43
|
-
const fresh = await input.artifact.read(current, input.signal).catch(() => undefined);
|
|
44
|
-
if (!fresh?.value) {
|
|
45
|
-
await release('released', 'The notification changed before presentation.');
|
|
46
|
-
return { presented: false };
|
|
47
|
-
}
|
|
48
81
|
try {
|
|
49
|
-
await
|
|
82
|
+
if (!await prepared.eligible() || input.signal?.aborted)
|
|
83
|
+
return { presented: false };
|
|
84
|
+
try {
|
|
85
|
+
await input.sink.present(prepared.activity);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
await prepared.release('failed', error instanceof Error ? error.message : String(error));
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
return await prepared.recordPresented();
|
|
50
92
|
}
|
|
51
|
-
|
|
52
|
-
await release(
|
|
53
|
-
throw error;
|
|
93
|
+
finally {
|
|
94
|
+
await prepared.release();
|
|
54
95
|
}
|
|
55
|
-
const outcome = input.markSeen === false ? 'clipped' : 'presented';
|
|
56
|
-
const { value: committed } = await input.artifact.transactAs({ kind: 'participant', seat: ref }, (facts) => {
|
|
57
|
-
if (!current(facts))
|
|
58
|
-
return false;
|
|
59
|
-
if (!facts.completeDelivery({ seat: ref, activity: formatActivityId(index), claimToken, outcome, at: input.now ?? Date.now() }))
|
|
60
|
-
return false;
|
|
61
|
-
if (input.markSeen === false)
|
|
62
|
-
return true;
|
|
63
|
-
return facts.markSeen(participant.name, [index], input.now ?? Date.now()) > 0;
|
|
64
|
-
}, input.signal).catch(error => {
|
|
65
|
-
if (isSquareError(error) && error.code === 'already_joined')
|
|
66
|
-
return { value: false };
|
|
67
|
-
throw error;
|
|
68
|
-
});
|
|
69
|
-
if (!committed)
|
|
70
|
-
return { presented: false };
|
|
71
|
-
return { presented: true, activity: item };
|
|
72
96
|
}
|
package/dist/sqlite-square.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatActivityId } from './square-core.js';
|
|
2
|
+
import { validateMetadataMatch, validateDeferralDuration, validateDeferralId } from './delivery-deferral.js';
|
|
2
3
|
import { squareLocation, localSquareLocation } from './square-location.js';
|
|
3
4
|
import crypto from 'node:crypto';
|
|
4
5
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
@@ -55,10 +56,6 @@ function rejectUnsentActivity(operation) {
|
|
|
55
56
|
throw error;
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
/** A generic managed address must not displace the receiver's native registration on reconnect. */
|
|
59
|
-
function canRefreshWakeAddress(current, proposed) {
|
|
60
|
-
return proposed.kind !== 'paseo' || current === undefined || current.kind === 'paseo';
|
|
61
|
-
}
|
|
62
59
|
/** One concrete local resource, including its entry-resolved dependencies and lifetime. */
|
|
63
60
|
export class SqliteSquare {
|
|
64
61
|
location;
|
|
@@ -382,6 +379,34 @@ export class SqliteSquare {
|
|
|
382
379
|
await releaseWatchLease(square.artifact, seat, leaseId).catch(() => undefined);
|
|
383
380
|
}
|
|
384
381
|
}
|
|
382
|
+
async deferDelivery(input, control) {
|
|
383
|
+
checkControl(control);
|
|
384
|
+
const match = validateMetadataMatch(input?.match), duration = validateDeferralDuration(input?.durationMs);
|
|
385
|
+
const square = this.owner, seat = requireSeat(this.seat, square.body, this.name);
|
|
386
|
+
return (await square.artifact.transactAs(participantAuthority(seat), facts => {
|
|
387
|
+
const now = square.clock(), expiresAt = now + duration, id = crypto.randomUUID();
|
|
388
|
+
const result = { id, expiresAt: new Date(expiresAt).toISOString() };
|
|
389
|
+
facts.putDeliveryDeferral(asSeatRef(seat), id, match, expiresAt, now);
|
|
390
|
+
return result;
|
|
391
|
+
}, control?.signal)).value;
|
|
392
|
+
}
|
|
393
|
+
async renewDeliveryDeferral(id, durationMs, control) {
|
|
394
|
+
checkControl(control);
|
|
395
|
+
validateDeferralId(id);
|
|
396
|
+
const duration = validateDeferralDuration(durationMs), square = this.owner;
|
|
397
|
+
const seat = requireSeat(this.seat, square.body, this.name);
|
|
398
|
+
return (await square.artifact.transactAs(participantAuthority(seat), facts => {
|
|
399
|
+
const now = square.clock(), expiresAt = now + duration;
|
|
400
|
+
const result = { id, expiresAt: new Date(expiresAt).toISOString() };
|
|
401
|
+
return facts.renewDeliveryDeferral(asSeatRef(seat), id, expiresAt, now) ? result : null;
|
|
402
|
+
}, control?.signal)).value;
|
|
403
|
+
}
|
|
404
|
+
async releaseDeliveryDeferral(id, control) {
|
|
405
|
+
checkControl(control);
|
|
406
|
+
validateDeferralId(id);
|
|
407
|
+
const square = this.owner, seat = requireSeat(this.seat, square.body, this.name);
|
|
408
|
+
return (await square.artifact.transactAs(participantAuthority(seat), facts => facts.releaseDeliveryDeferral(asSeatRef(seat), id, square.clock()), control?.signal)).value;
|
|
409
|
+
}
|
|
385
410
|
history(query, control) { return this.owner.history(query, control); }
|
|
386
411
|
async lifecycle(verb, body, options, control) {
|
|
387
412
|
if (verb === 'done')
|
|
@@ -441,7 +466,7 @@ export class SqliteSquare {
|
|
|
441
466
|
else if (current?.joined) {
|
|
442
467
|
if (currentSeat === undefined || !sameBody(currentSeat.body, body))
|
|
443
468
|
throw new SquareError('already_joined', `${participantIdentity(participant)} is held by another body; take it over explicitly`);
|
|
444
|
-
if (this.wakeAddress !== undefined
|
|
469
|
+
if (this.wakeAddress !== undefined)
|
|
445
470
|
facts.updateWakeAddress(asSeatRef(currentSeat), this.wakeAddress);
|
|
446
471
|
kind = 'reconnected';
|
|
447
472
|
}
|
|
@@ -482,7 +507,7 @@ export class SqliteSquare {
|
|
|
482
507
|
if (decision.joinAct === undefined) {
|
|
483
508
|
if (decision.state === 'active' && (seat === undefined || !sameBody(seat.body, body)))
|
|
484
509
|
throw new SquareError('already_joined', `${participantIdentity(decision.joinedName)} is held by another body`);
|
|
485
|
-
if (decision.state === 'active' && this.wakeAddress !== undefined
|
|
510
|
+
if (decision.state === 'active' && this.wakeAddress !== undefined)
|
|
486
511
|
facts.updateWakeAddress(asSeatRef(seat), this.wakeAddress);
|
|
487
512
|
}
|
|
488
513
|
else {
|
package/dist/square-domain.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export interface LandingFacts {
|
|
|
21
21
|
/** The single landing transition, shared by both adapters and explicit rebuild. */
|
|
22
22
|
export declare function deriveLanding(activity: StoredAct, facts: LandingFacts): LandingEffect;
|
|
23
23
|
export declare function landingFacts(read: SquareRead, activity: StoredAct): LandingFacts;
|
|
24
|
-
export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'cancelThrough' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
|
|
24
|
+
export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'cancelThrough' | 'putDeliveryDeferral' | 'renewDeliveryDeferral' | 'releaseDeliveryDeferral' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
|
|
25
25
|
/** Artifact-private seat persistence used only by this gate's lifecycle transition. */
|
|
26
26
|
storeSeat(seat: Seat): boolean;
|
|
27
27
|
removeSeat(seat: import('./ports.js').SeatRef): boolean;
|
package/dist/square-domain.js
CHANGED
|
@@ -206,6 +206,18 @@ export function wrapSquareWrite(read, writes, authority) {
|
|
|
206
206
|
assertParticipant(name, generation);
|
|
207
207
|
return mutations.removeLease(name, leaseId, generation);
|
|
208
208
|
},
|
|
209
|
+
putDeliveryDeferral(...args) {
|
|
210
|
+
assertSeat(args[0]);
|
|
211
|
+
return mutations.putDeliveryDeferral(...args);
|
|
212
|
+
},
|
|
213
|
+
renewDeliveryDeferral(...args) {
|
|
214
|
+
assertSeat(args[0]);
|
|
215
|
+
return mutations.renewDeliveryDeferral(...args);
|
|
216
|
+
},
|
|
217
|
+
releaseDeliveryDeferral(...args) {
|
|
218
|
+
assertSeat(args[0]);
|
|
219
|
+
return mutations.releaseDeliveryDeferral(...args);
|
|
220
|
+
},
|
|
209
221
|
claimDelivery(input) {
|
|
210
222
|
assertSeat(input.seat);
|
|
211
223
|
return mutations.claimDelivery(input);
|
package/dist/square-facade.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { SquareBackends } from './square-backend.js';
|
|
2
|
+
import type { DeliveryDeferral, MetadataMatch } from './delivery-deferral.js';
|
|
3
|
+
export type { DeliveryDeferral, MetadataMatch, MetadataScalar } from './delivery-deferral.js';
|
|
2
4
|
import type { ActivityId, Perception, Reach } from './square-core.js';
|
|
3
5
|
import type { Body, StoredAct, WakeAddress, ActivityMetadata } from './model.js';
|
|
4
6
|
import type { WakeTransportPort, DeliveryResult, SquareResourceOwner } from './ports.js';
|
|
@@ -203,6 +205,12 @@ export interface Participant {
|
|
|
203
205
|
resume(options?: ObservationOptions, control?: OperationControl): Promise<LifecycleResult>;
|
|
204
206
|
/** Leave without a message. Only an omitted or empty body is accepted. */
|
|
205
207
|
done(body?: string, options?: ObservationOptions, control?: OperationControl): Promise<LifecycleResult>;
|
|
208
|
+
deferDelivery(input: Readonly<{
|
|
209
|
+
match: MetadataMatch;
|
|
210
|
+
durationMs: number;
|
|
211
|
+
}>, control?: OperationControl): Promise<DeliveryDeferral>;
|
|
212
|
+
renewDeliveryDeferral(id: string, durationMs: number, control?: OperationControl): Promise<DeliveryDeferral | null>;
|
|
213
|
+
releaseDeliveryDeferral(id: string, control?: OperationControl): Promise<boolean>;
|
|
206
214
|
}
|
|
207
215
|
export type CatchTerminalStatus = 'already-active' | 'replaced' | 'capped' | 'quorum';
|
|
208
216
|
export type CatchStopStatus = CatchTerminalStatus | 'empty-now' | 'stale';
|
|
@@ -15,7 +15,8 @@ export interface MigrationOutcome {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Explicit conversion of the supported legacy formats. Row-backed versions 2 and 3 are
|
|
18
|
-
* preflighted read-only, then receive
|
|
18
|
+
* preflighted read-only, then receive ownership/evidence and deferral storage in one
|
|
19
|
+
* format-5 transaction. Format 4 adds only the empty deferral table and its index.
|
|
19
20
|
* Legacy delivery evidence is copied from HostLedger only when its owner/session mapping is
|
|
20
21
|
* unique; no source record is retired. Version 1 is not a supported source for this format transition.
|
|
21
22
|
*/
|
package/dist/square-migration.js
CHANGED
|
@@ -5,8 +5,8 @@ import { canonicalPath, canonicalPathSync } from './canonical-path.js';
|
|
|
5
5
|
import { formatActivityId, isWakeRouteKind, nameKey, sameBody, validateBody } from './model.js';
|
|
6
6
|
import { parseActivityId } from './square-core.js';
|
|
7
7
|
import { readLegacyHostData } from './square-migration-host-ledger.js';
|
|
8
|
-
import { legacyRowFramingIssue, FORMAT4_ADDED_OBJECTS
|
|
9
|
-
import { APPLICATION_ID, ACTIVITIES_TABLE_SQL, ACTIVITY_INDEXES_SQL, CURRENT_USER_VERSION, V2_USER_VERSION, V3_USER_VERSION, SCHEMA_SQL, createSqliteDriver, invalidArtifact, validateStoredActivity } from './square-sqlite.js';
|
|
8
|
+
import { legacyRowFramingIssue, FORMAT4_ADDED_OBJECTS } from './square-legacy-framing.js';
|
|
9
|
+
import { APPLICATION_ID, ACTIVITIES_TABLE_SQL, ACTIVITY_INDEXES_SQL, CURRENT_USER_VERSION, V2_USER_VERSION, V3_USER_VERSION, V4_USER_VERSION, SCHEMA_SQL, FORMAT4_SCHEMA_SQL, DELIVERY_DEFERRALS_SQL, validateRowFraming, inspectSqlite, createSqliteDriver, invalidArtifact, validateStoredActivity } from './square-sqlite.js';
|
|
10
10
|
function isObject(value) {
|
|
11
11
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
12
12
|
}
|
|
@@ -140,26 +140,8 @@ function rebuildV2Activities(database) {
|
|
|
140
140
|
...ACTIVITY_INDEXES_SQL.map((sql) => `${sql};`),
|
|
141
141
|
].join('\n'));
|
|
142
142
|
}
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
function currentFormat4Schema() {
|
|
147
|
-
return SCHEMA_SQL.split(';').map((statement) => statement.trim()).filter(Boolean)
|
|
148
|
-
.filter((statement) => {
|
|
149
|
-
const match = /^(?:CREATE (?:UNIQUE )?TABLE|CREATE (?:UNIQUE )?INDEX)\s+([a-z_]+)/i.exec(statement);
|
|
150
|
-
return match === null || !FORMAT4_REPLACED_OBJECTS.has(match[1]);
|
|
151
|
-
}).map(normalizedSql).sort();
|
|
152
|
-
}
|
|
153
|
-
function validateFormat4Framing(database, location) {
|
|
154
|
-
const before = identity(database);
|
|
155
|
-
assertSource(before.applicationId === APPLICATION_ID, 'unrelated SQLite database.');
|
|
156
|
-
assertSource(before.userVersion === CURRENT_USER_VERSION, 'unsupported SQLite format version.');
|
|
157
|
-
const actual = database.prepare("SELECT sql FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*' AND sql IS NOT NULL").all()
|
|
158
|
-
.map((row) => typeof row.sql === 'string' ? normalizedSql(row.sql) : '').sort();
|
|
159
|
-
const expected = currentFormat4Schema();
|
|
160
|
-
assertSource(actual.length === expected.length && actual.every((sql, index) => sql === expected[index]), 'unsupported row schema.');
|
|
161
|
-
const journalMode = database.prepare('PRAGMA journal_mode').get().journal_mode;
|
|
162
|
-
assertSource(journalMode === (location === ':memory:' ? 'memory' : 'delete'), 'unsupported journal mode.');
|
|
143
|
+
function validateCurrentFraming(database, location) {
|
|
144
|
+
validateRowFraming(database, CURRENT_USER_VERSION, location);
|
|
163
145
|
createSqliteDriver(database).header();
|
|
164
146
|
}
|
|
165
147
|
function assertSource(condition, detail = 'row is malformed.') {
|
|
@@ -474,28 +456,35 @@ function upgradeRowArtifact(database, location, plan) {
|
|
|
474
456
|
currentAdditionStatements().forEach((statement) => database.exec(statement));
|
|
475
457
|
importOwnershipAndEvidence(database, plan);
|
|
476
458
|
database.exec('DROP TABLE routes;');
|
|
459
|
+
database.exec(DELIVERY_DEFERRALS_SQL);
|
|
477
460
|
database.exec(`PRAGMA user_version = ${CURRENT_USER_VERSION};`);
|
|
478
|
-
|
|
461
|
+
validateCurrentFraming(database, location);
|
|
479
462
|
}
|
|
480
463
|
function validateMigrationSource(database, location) {
|
|
481
464
|
const before = identity(database);
|
|
482
465
|
if (before.applicationId !== APPLICATION_ID)
|
|
483
466
|
throw invalidArtifact('unrelated SQLite database.');
|
|
484
467
|
if (before.userVersion === V2_USER_VERSION || before.userVersion === V3_USER_VERSION) {
|
|
485
|
-
const issue = legacyRowFramingIssue(database, APPLICATION_ID, before.userVersion, location,
|
|
468
|
+
const issue = legacyRowFramingIssue(database, APPLICATION_ID, before.userVersion, location, FORMAT4_SCHEMA_SQL);
|
|
486
469
|
if (issue !== undefined)
|
|
487
470
|
throw invalidArtifact(issue);
|
|
488
471
|
return before.userVersion;
|
|
489
472
|
}
|
|
473
|
+
if (before.userVersion === V4_USER_VERSION) {
|
|
474
|
+
validateRowFraming(database, V4_USER_VERSION, location);
|
|
475
|
+
inspectSqlite(database);
|
|
476
|
+
return V4_USER_VERSION;
|
|
477
|
+
}
|
|
490
478
|
if (before.userVersion === CURRENT_USER_VERSION) {
|
|
491
|
-
|
|
479
|
+
validateCurrentFraming(database, location);
|
|
492
480
|
return undefined;
|
|
493
481
|
}
|
|
494
482
|
throw invalidArtifact('unsupported SQLite format version.');
|
|
495
483
|
}
|
|
496
484
|
/**
|
|
497
485
|
* Explicit conversion of the supported legacy formats. Row-backed versions 2 and 3 are
|
|
498
|
-
* preflighted read-only, then receive
|
|
486
|
+
* preflighted read-only, then receive ownership/evidence and deferral storage in one
|
|
487
|
+
* format-5 transaction. Format 4 adds only the empty deferral table and its index.
|
|
499
488
|
* Legacy delivery evidence is copied from HostLedger only when its owner/session mapping is
|
|
500
489
|
* unique; no source record is retired. Version 1 is not a supported source for this format transition.
|
|
501
490
|
*/
|
|
@@ -506,6 +495,16 @@ export async function migrateSquareFile(location, signal, env = process.env) {
|
|
|
506
495
|
const source = await inSqliteTransaction(database, false, () => validateMigrationSource(database, squarePath), value => value, signal);
|
|
507
496
|
if (source === undefined)
|
|
508
497
|
return { status: 'already-current', standing: standing(database) };
|
|
498
|
+
if (source === V4_USER_VERSION) {
|
|
499
|
+
return await inSqliteTransaction(database, true, () => {
|
|
500
|
+
assertSource(validateMigrationSource(database, squarePath) === source, 'source format changed during migration preflight.');
|
|
501
|
+
}, () => {
|
|
502
|
+
database.exec(DELIVERY_DEFERRALS_SQL);
|
|
503
|
+
database.exec(`PRAGMA user_version = ${CURRENT_USER_VERSION};`);
|
|
504
|
+
validateCurrentFraming(database, squarePath);
|
|
505
|
+
return { status: 'migrated', standing: standing(database) };
|
|
506
|
+
}, signal);
|
|
507
|
+
}
|
|
509
508
|
const host = await readLegacyHostData(squarePath, env, signal);
|
|
510
509
|
const plan = await inSqliteTransaction(database, false, () => {
|
|
511
510
|
assertSource(validateMigrationSource(database, squarePath) === source, 'source format changed during migration preflight.');
|
package/dist/square-sqlite.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ export declare function emptyRuntimeState(nextActIndex?: number): SquareRuntimeS
|
|
|
20
20
|
export declare const APPLICATION_ID = 1397838405;
|
|
21
21
|
export declare const V2_USER_VERSION = 2;
|
|
22
22
|
export declare const V3_USER_VERSION = 3;
|
|
23
|
-
export declare const
|
|
23
|
+
export declare const V4_USER_VERSION = 4;
|
|
24
|
+
export declare const CURRENT_USER_VERSION = 5;
|
|
24
25
|
export declare const V1_SNAPSHOT_TABLE = "square_snapshot";
|
|
25
26
|
export interface LegacyFraming {
|
|
26
27
|
readonly applicationId: number;
|
|
@@ -36,11 +37,13 @@ export interface LegacyFraming {
|
|
|
36
37
|
export declare function validateLegacyFraming(database: DatabaseSync, applicationId: number, location?: string): LegacyFraming;
|
|
37
38
|
export declare const ACTIVITIES_TABLE_SQL = "CREATE TABLE activities (\n act_index INTEGER PRIMARY KEY CHECK (act_index BETWEEN 0 AND 9007199254740991),\n at REAL NOT NULL,\n kind TEXT NOT NULL CHECK (kind IN ('join','done','say','hold','resume','read','listen','ignore','unlisten')),\n actor_json TEXT COLLATE BINARY,\n actor_key_json TEXT COLLATE BINARY,\n reach TEXT CHECK (reach = 'bell'),\n reply_index INTEGER CHECK (reply_index >= 0 AND reply_index < act_index),\n payload_json TEXT NOT NULL CHECK (json_valid(payload_json)),\n CHECK ((actor_json IS NULL) = (actor_key_json IS NULL)),\n CHECK (kind IN ('hold','resume') OR actor_json IS NOT NULL),\n CHECK (kind = 'say' OR (reach IS NULL AND reply_index IS NULL))\n);";
|
|
38
39
|
export declare const ACTIVITY_INDEXES_SQL: readonly ["CREATE INDEX activity_kind_index ON activities(kind, act_index)", "CREATE INDEX activity_actor_index ON activities(actor_key_json, act_index)", "CREATE INDEX activity_say_actor_index ON activities(actor_key_json, act_index) WHERE kind='say'", "CREATE INDEX activity_say_time ON activities(at, act_index) WHERE kind='say'", "CREATE INDEX activity_bell_actor_time ON activities(actor_key_json, at, act_index) WHERE kind='say' AND reach='bell'"];
|
|
40
|
+
export declare const DELIVERY_DEFERRALS_SQL = "CREATE TABLE delivery_deferrals (\n participant_key_json TEXT COLLATE BINARY NOT NULL,\n participant_json TEXT NOT NULL,\n generation_json TEXT NOT NULL,\n deferral_id_json TEXT NOT NULL,\n match_json TEXT NOT NULL CHECK (json_valid(match_json)),\n expires_at REAL NOT NULL,\n PRIMARY KEY (participant_key_json,generation_json,deferral_id_json)\n) WITHOUT ROWID;\nCREATE INDEX delivery_deferral_expiry ON delivery_deferrals(participant_key_json,generation_json,expires_at);";
|
|
39
41
|
export declare const SCHEMA_SQL: string;
|
|
42
|
+
export declare const FORMAT4_SCHEMA_SQL: string;
|
|
40
43
|
export declare function invalidArtifact(detail: string): SquareError;
|
|
41
44
|
export declare function validateStoredActivity(value: unknown): StoredAct;
|
|
42
45
|
export declare function validateWatchLease(value: unknown): WatchLease;
|
|
43
|
-
export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3 | 4, location?: string): void;
|
|
46
|
+
export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3 | 4 | 5, location?: string): void;
|
|
44
47
|
export declare function validateSqliteHeader(db: DatabaseSync, location?: string): SquareHeader;
|
|
45
48
|
export interface SqliteDriver extends SquareRead, LandingWriter {
|
|
46
49
|
readonly changed: boolean;
|
package/dist/square-sqlite.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DatabaseSync as MemoryDatabase } from 'node:sqlite';
|
|
2
|
-
import { SquareError, nameKey, isWakeRouteKind, validateBody, validateActivityMetadata, activityMetadataHasContent } from './model.js';
|
|
2
|
+
import { sameBody, SquareError, nameKey, isWakeRouteKind, validateBody, validateActivityMetadata, activityMetadataHasContent } from './model.js';
|
|
3
|
+
import { validateMetadataMatch, matchingDeferrals, validateDeferralId } from './delivery-deferral.js';
|
|
3
4
|
import { deriveLanding, landingFacts } from './square-domain.js';
|
|
4
5
|
import { legacyRowFramingIssue } from './square-legacy-framing.js';
|
|
5
6
|
export function emptyRuntimeState(nextActIndex = 0) {
|
|
@@ -9,7 +10,8 @@ export const APPLICATION_ID = 0x53515245;
|
|
|
9
10
|
const V1_USER_VERSION = 1;
|
|
10
11
|
export const V2_USER_VERSION = 2;
|
|
11
12
|
export const V3_USER_VERSION = 3;
|
|
12
|
-
export const
|
|
13
|
+
export const V4_USER_VERSION = 4;
|
|
14
|
+
export const CURRENT_USER_VERSION = 5;
|
|
13
15
|
export const V1_SNAPSHOT_TABLE = 'square_snapshot';
|
|
14
16
|
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)`;
|
|
15
17
|
const V1_SNAPSHOT_COLUMNS = [
|
|
@@ -75,6 +77,16 @@ export const ACTIVITY_INDEXES_SQL = [
|
|
|
75
77
|
"CREATE INDEX activity_say_time ON activities(at, act_index) WHERE kind='say'",
|
|
76
78
|
"CREATE INDEX activity_bell_actor_time ON activities(actor_key_json, at, act_index) WHERE kind='say' AND reach='bell'",
|
|
77
79
|
];
|
|
80
|
+
export const DELIVERY_DEFERRALS_SQL = `CREATE TABLE delivery_deferrals (
|
|
81
|
+
participant_key_json TEXT COLLATE BINARY NOT NULL,
|
|
82
|
+
participant_json TEXT NOT NULL,
|
|
83
|
+
generation_json TEXT NOT NULL,
|
|
84
|
+
deferral_id_json TEXT NOT NULL,
|
|
85
|
+
match_json TEXT NOT NULL CHECK (json_valid(match_json)),
|
|
86
|
+
expires_at REAL NOT NULL,
|
|
87
|
+
PRIMARY KEY (participant_key_json,generation_json,deferral_id_json)
|
|
88
|
+
) WITHOUT ROWID;
|
|
89
|
+
CREATE INDEX delivery_deferral_expiry ON delivery_deferrals(participant_key_json,generation_json,expires_at);`;
|
|
78
90
|
export const SCHEMA_SQL = `CREATE TABLE square_meta (
|
|
79
91
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
|
80
92
|
revision INTEGER NOT NULL CHECK (revision BETWEEN 0 AND 9007199254740991),
|
|
@@ -137,6 +149,8 @@ CREATE TABLE delivery_cancellations (
|
|
|
137
149
|
PRIMARY KEY (participant_key_json,generation_json)
|
|
138
150
|
) WITHOUT ROWID;
|
|
139
151
|
|
|
152
|
+
${DELIVERY_DEFERRALS_SQL}
|
|
153
|
+
|
|
140
154
|
CREATE TABLE participants (
|
|
141
155
|
name_key_json TEXT COLLATE BINARY PRIMARY KEY NOT NULL,
|
|
142
156
|
name_json TEXT NOT NULL,
|
|
@@ -166,7 +180,9 @@ CREATE TABLE landed_audience (
|
|
|
166
180
|
UNIQUE (act_index, recipient_order)
|
|
167
181
|
) WITHOUT ROWID;
|
|
168
182
|
CREATE INDEX audience_recipient ON landed_audience(recipient_key_json, act_index);`;
|
|
183
|
+
export const FORMAT4_SCHEMA_SQL = SCHEMA_SQL.replace(DELIVERY_DEFERRALS_SQL, '');
|
|
169
184
|
const EXPECTED_SCHEMA_SQL = SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
185
|
+
const FORMAT4_SCHEMA = FORMAT4_SCHEMA_SQL.split(';').map(s => s.trim()).filter(Boolean).map(normalizedSql).sort();
|
|
170
186
|
const encode = JSON.stringify;
|
|
171
187
|
const key = (name) => encode(nameKey(name));
|
|
172
188
|
export function invalidArtifact(detail) {
|
|
@@ -412,6 +428,15 @@ function deliveryEvidence(row) {
|
|
|
412
428
|
...(nativeObservations === undefined ? {} : { nativeObservations }),
|
|
413
429
|
});
|
|
414
430
|
}
|
|
431
|
+
function deliveryDeferral(row) {
|
|
432
|
+
const participant = decoded(row.participant_json, true);
|
|
433
|
+
requireFact(row.participant_key_json === key(participant));
|
|
434
|
+
const generation = activityId(decoded(row.generation_json, true));
|
|
435
|
+
const id = validateDeferralId(decoded(row.deferral_id_json, true));
|
|
436
|
+
const match = validateMetadataMatch(json(row.match_json));
|
|
437
|
+
requireFact(encode(match) === row.match_json, 'delivery deferral match encoding is malformed.');
|
|
438
|
+
return immutable({ participant, generation, id, match, expiresAt: finite(row.expires_at) });
|
|
439
|
+
}
|
|
415
440
|
function normalizedSql(sql) { return sql.trim().replace(/;$/, '').replace(/\s+/g, ' ').replace(/\s*([(),=])\s*/g, '$1'); }
|
|
416
441
|
function validateOwnedSchema(db, expected, location) {
|
|
417
442
|
const actual = db.prepare("SELECT sql FROM sqlite_schema WHERE name NOT GLOB 'sqlite_*' AND sql IS NOT NULL").all().map(r => normalizedSql(string(r.sql))).sort();
|
|
@@ -420,7 +445,7 @@ function validateOwnedSchema(db, expected, location) {
|
|
|
420
445
|
}
|
|
421
446
|
export function validateRowFraming(db, version, location = '') {
|
|
422
447
|
if (version === V2_USER_VERSION || version === V3_USER_VERSION) {
|
|
423
|
-
const issue = legacyRowFramingIssue(db, APPLICATION_ID, version, location,
|
|
448
|
+
const issue = legacyRowFramingIssue(db, APPLICATION_ID, version, location, FORMAT4_SCHEMA_SQL);
|
|
424
449
|
if (issue !== undefined)
|
|
425
450
|
throw invalidArtifact(issue);
|
|
426
451
|
return;
|
|
@@ -428,7 +453,7 @@ export function validateRowFraming(db, version, location = '') {
|
|
|
428
453
|
const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
|
|
429
454
|
requireFact(identity.application_id === APPLICATION_ID, 'unrelated SQLite database.');
|
|
430
455
|
requireFact(identity.user_version === version, 'unsupported SQLite format version.');
|
|
431
|
-
validateOwnedSchema(db, EXPECTED_SCHEMA_SQL, location);
|
|
456
|
+
validateOwnedSchema(db, version === V4_USER_VERSION ? FORMAT4_SCHEMA : EXPECTED_SCHEMA_SQL, location);
|
|
432
457
|
}
|
|
433
458
|
export function validateSqliteHeader(db, location = '') {
|
|
434
459
|
const identity = db.prepare('SELECT application_id,user_version FROM pragma_application_id,pragma_user_version').get();
|
|
@@ -437,7 +462,7 @@ export function validateSqliteHeader(db, location = '') {
|
|
|
437
462
|
validateLegacyFraming(db, APPLICATION_ID, location);
|
|
438
463
|
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' });
|
|
439
464
|
}
|
|
440
|
-
if (identity.user_version === V2_USER_VERSION || identity.user_version === V3_USER_VERSION) {
|
|
465
|
+
if (identity.user_version === V2_USER_VERSION || identity.user_version === V3_USER_VERSION || identity.user_version === V4_USER_VERSION) {
|
|
441
466
|
validateRowFraming(db, identity.user_version, location);
|
|
442
467
|
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' });
|
|
443
468
|
}
|
|
@@ -669,6 +694,13 @@ export function createSqliteDriver(db) {
|
|
|
669
694
|
return immutable({ ...(r.latest === null ? {} : { latestAt: finite(r.latest) }), ...(e.latest === null ? {} : { latestActorActivityIndex: integer(e.latest) }) });
|
|
670
695
|
},
|
|
671
696
|
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) }))),
|
|
697
|
+
deliveryDeferrals(name, generation) {
|
|
698
|
+
const canonical = owner(name);
|
|
699
|
+
const current = read.seat(canonical);
|
|
700
|
+
if (current === undefined || current.generation !== generation)
|
|
701
|
+
return [];
|
|
702
|
+
return immutable(all('SELECT * FROM delivery_deferrals WHERE participant_key_json=? AND generation_json=? ORDER BY expires_at,deferral_id_json', [key(canonical), encode(generation)]).map(deliveryDeferral));
|
|
703
|
+
},
|
|
672
704
|
lease(name) {
|
|
673
705
|
const canonical = owner(name), current = read.seat(canonical);
|
|
674
706
|
const r = current === undefined
|
|
@@ -690,6 +722,7 @@ export function createSqliteDriver(db) {
|
|
|
690
722
|
const wake = value.wakeAddress === undefined ? null : encode(value.wakeAddress);
|
|
691
723
|
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
724
|
run('DELETE FROM catch_leases WHERE owner_json=?', [encode(value.participant)]);
|
|
725
|
+
run('DELETE FROM delivery_deferrals WHERE participant_key_json=?', [key(value.participant)]);
|
|
693
726
|
deliveryChanged = true;
|
|
694
727
|
return true;
|
|
695
728
|
},
|
|
@@ -713,6 +746,7 @@ export function createSqliteDriver(db) {
|
|
|
713
746
|
return false;
|
|
714
747
|
run('UPDATE seats SET wake_address_json=NULL WHERE participant_key_json=?', [key(value.participant)]);
|
|
715
748
|
run('DELETE FROM catch_leases WHERE owner_json=?', [encode(value.participant)]);
|
|
749
|
+
run('DELETE FROM delivery_deferrals WHERE participant_key_json=?', [key(value.participant)]);
|
|
716
750
|
changed = true;
|
|
717
751
|
deliveryChanged = true;
|
|
718
752
|
return true;
|
|
@@ -791,6 +825,32 @@ export function createSqliteDriver(db) {
|
|
|
791
825
|
changed = true;
|
|
792
826
|
return true;
|
|
793
827
|
},
|
|
828
|
+
putDeliveryDeferral(seatRef, id, match, expiresAt, now) {
|
|
829
|
+
finite(now);
|
|
830
|
+
finite(expiresAt);
|
|
831
|
+
const canonicalMatch = validateMetadataMatch(match);
|
|
832
|
+
run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
|
|
833
|
+
run('INSERT INTO delivery_deferrals(participant_key_json,participant_json,generation_json,deferral_id_json,match_json,expires_at) VALUES(?,?,?,?,?,?)', [key(seatRef.participant), encode(seatRef.participant), encode(seatRef.generation), encode(id), encode(canonicalMatch), expiresAt]);
|
|
834
|
+
deliveryChanged = true;
|
|
835
|
+
return true;
|
|
836
|
+
},
|
|
837
|
+
renewDeliveryDeferral(seatRef, id, expiresAt, now) {
|
|
838
|
+
finite(now);
|
|
839
|
+
finite(expiresAt);
|
|
840
|
+
const pruned = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
|
|
841
|
+
const count = run('UPDATE delivery_deferrals SET expires_at=? WHERE participant_key_json=? AND generation_json=? AND deferral_id_json=? AND expires_at>?', [expiresAt, key(seatRef.participant), encode(seatRef.generation), encode(id), now]);
|
|
842
|
+
if (count || pruned)
|
|
843
|
+
deliveryChanged = true;
|
|
844
|
+
return count > 0;
|
|
845
|
+
},
|
|
846
|
+
releaseDeliveryDeferral(seatRef, id, now) {
|
|
847
|
+
finite(now);
|
|
848
|
+
const pruned = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND expires_at<=?', [key(seatRef.participant), now]);
|
|
849
|
+
const count = run('DELETE FROM delivery_deferrals WHERE participant_key_json=? AND generation_json=? AND deferral_id_json=? AND expires_at>?', [key(seatRef.participant), encode(seatRef.generation), encode(id), now]);
|
|
850
|
+
if (count || pruned)
|
|
851
|
+
deliveryChanged = true;
|
|
852
|
+
return count > 0;
|
|
853
|
+
},
|
|
794
854
|
removeLease(name, expected, generation) {
|
|
795
855
|
const canonical = owner(name), row = one('SELECT generation_json FROM catch_leases WHERE owner_json=? AND lease_id_json=?', [encode(canonical), encode(expected)]);
|
|
796
856
|
if (row === undefined || generation !== undefined && decoded(row.generation_json) !== generation)
|
|
@@ -810,8 +870,13 @@ export function createSqliteDriver(db) {
|
|
|
810
870
|
string(input.claimToken, true);
|
|
811
871
|
if (input.route !== undefined)
|
|
812
872
|
wakeAddress(input.route);
|
|
813
|
-
|
|
873
|
+
const currentSeat = read.seat(input.seat.participant);
|
|
874
|
+
const item = read.activity(index);
|
|
875
|
+
if (currentSeat === undefined || currentSeat.generation !== input.seat.generation || !sameBody(currentSeat.body, input.seat.body)
|
|
876
|
+
|| item?.kind !== 'say' || read.directedAt(input.seat.participant, index) === undefined)
|
|
814
877
|
return { status: 'stale' };
|
|
878
|
+
if (matchingDeferrals(read.deliveryDeferrals(currentSeat.participant, currentSeat.generation), item.metadata, input.at).length > 0)
|
|
879
|
+
return { status: 'deferred' };
|
|
815
880
|
const cancelledThrough = read.cancelledThrough(input.seat.participant, input.seat.generation);
|
|
816
881
|
if (cancelledThrough !== undefined && index <= cancelledThrough)
|
|
817
882
|
return { status: 'stale' };
|
|
@@ -962,6 +1027,14 @@ export function inspectSqlite(db) {
|
|
|
962
1027
|
requireFact(check.length === 1 && check[0].integrity_check === 'ok', 'SQLite integrity check failed.');
|
|
963
1028
|
const state = exportSqliteState(db), d = createSqliteDriver(db);
|
|
964
1029
|
d.roster();
|
|
1030
|
+
for (const row of db.prepare('SELECT * FROM seats').all())
|
|
1031
|
+
seat(row);
|
|
1032
|
+
for (const row of db.prepare('SELECT * FROM delivery_evidence').all())
|
|
1033
|
+
deliveryEvidence(row);
|
|
1034
|
+
if (db.prepare('PRAGMA user_version').get().user_version === CURRENT_USER_VERSION) {
|
|
1035
|
+
for (const row of db.prepare('SELECT * FROM delivery_deferrals').all())
|
|
1036
|
+
deliveryDeferral(row);
|
|
1037
|
+
}
|
|
965
1038
|
for (const p of d.roster()) {
|
|
966
1039
|
d.attention(p.name);
|
|
967
1040
|
d.incomingAttention(p.name);
|
package/dist/square-stdio.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { type SquareErrorCode, type SquareErrorFacts } from './model.js';
|
|
|
2
2
|
import type { SquareBackends, SquareBackend } from './square-backend.js';
|
|
3
3
|
import type { ParsedSquareLocation } from './square-location.js';
|
|
4
4
|
import type { OperationControl, OperationFailureDetail, SquareAtInput, SquareBuildInput } from './square-facade.js';
|
|
5
|
-
export declare const STDIO_PROTOCOL_VERSION =
|
|
5
|
+
export declare const STDIO_PROTOCOL_VERSION = 2;
|
|
6
6
|
export declare const STDIO_MAX_FRAME_BYTES: number;
|
|
7
7
|
export declare const identityVariables: (import("./participant-identity.js").HarnessSessionVariable | "CLAUDE_CODE_CHILD_SESSION")[];
|
|
8
8
|
export declare function wireIdentity(env: NodeJS.ProcessEnv): Record<string, string>;
|