@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.
- package/dist/artifact.d.ts +6 -1
- package/dist/artifact.js +79 -10
- package/dist/boundary-presentation.d.ts +5 -4
- package/dist/boundary-presentation.js +21 -25
- package/dist/catch-decisions.js +2 -1
- package/dist/claude-delivery-status.d.ts +8 -2
- package/dist/claude-delivery-status.js +26 -10
- package/dist/claude-delivery.d.ts +8 -4
- package/dist/claude-delivery.js +114 -104
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +119 -105
- package/dist/cli/context.d.ts +3 -1
- package/dist/cli/context.js +25 -1
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +15 -12
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.js +2 -1
- package/dist/cli/registry.js +0 -2
- package/dist/cli/square-commands.d.ts +4 -2
- package/dist/cli/square-commands.js +21 -6
- package/dist/codex-hook.d.ts +3 -3
- package/dist/codex-hook.js +4 -4
- package/dist/codex-queue.d.ts +5 -6
- package/dist/codex-queue.js +9 -5
- package/dist/decisions.d.ts +3 -2
- package/dist/decisions.js +18 -6
- package/dist/delivery-health.d.ts +32 -2
- package/dist/delivery-health.js +108 -45
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +155 -196
- package/dist/delivery.d.ts +14 -23
- package/dist/delivery.js +48 -18
- package/dist/file-lock.d.ts +8 -1
- package/dist/file-lock.js +2 -0
- package/dist/help.js +12 -14
- package/dist/hints.js +2 -3
- package/dist/inbox.d.ts +35 -9
- package/dist/inbox.js +228 -144
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -3
- package/dist/list.js +9 -4
- package/dist/mcp.js +12 -1
- package/dist/model.d.ts +39 -5
- package/dist/model.js +84 -0
- package/dist/notifications.d.ts +8 -6
- package/dist/notifications.js +56 -143
- package/dist/operation-presentation.js +12 -14
- package/dist/participant-identity.d.ts +0 -3
- package/dist/participant-identity.js +0 -31
- package/dist/paseo-delivery.d.ts +5 -6
- package/dist/paseo-delivery.js +13 -8
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/delivery.d.ts +8 -3
- package/dist/plugins/opencode/delivery.js +187 -132
- package/dist/plugins/opencode/index.js +12 -38
- package/dist/ports.d.ts +112 -40
- package/dist/presentation-operations.d.ts +1 -1
- package/dist/presentation-operations.js +51 -35
- package/dist/presentation.d.ts +0 -1
- package/dist/presentation.js +18 -13
- package/dist/seats.d.ts +7 -0
- package/dist/seats.js +27 -0
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +10 -12
- package/dist/sqlite-square.d.ts +53 -0
- package/dist/sqlite-square.js +602 -0
- package/dist/square-api.d.ts +12 -41
- package/dist/square-api.js +26 -474
- package/dist/square-backend.d.ts +13 -0
- package/dist/square-core.d.ts +3 -2
- package/dist/square-domain.d.ts +12 -5
- package/dist/square-domain.js +163 -7
- package/dist/square-facade.d.ts +16 -10
- package/dist/square-index.d.ts +24 -0
- package/dist/square-index.js +131 -0
- package/dist/square-legacy-framing.d.ts +9 -0
- package/dist/square-legacy-framing.js +56 -0
- package/dist/square-location.d.ts +15 -0
- package/dist/square-location.js +76 -0
- package/dist/square-migration-host-ledger.d.ts +26 -0
- package/dist/square-migration-host-ledger.js +168 -0
- package/dist/square-migration.d.ts +5 -5
- package/dist/square-migration.js +373 -71
- package/dist/square-sqlite.d.ts +9 -9
- package/dist/square-sqlite.js +329 -79
- package/dist/square-stdio.d.ts +22 -0
- package/dist/square-stdio.js +222 -0
- package/dist/square-tools.js +30 -3
- package/dist/stdio-server.d.ts +3 -0
- package/dist/stdio-server.js +192 -0
- package/dist/stream.d.ts +3 -2
- package/dist/stream.js +9 -10
- package/dist/views.d.ts +9 -14
- package/dist/views.js +50 -70
- package/dist/wakes.d.ts +6 -5
- package/dist/wakes.js +20 -17
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/guides/backends.md +180 -0
- package/guides/claude-native.md +9 -9
- package/guides/mcp.md +1 -1
- package/guides/migration.md +107 -67
- package/guides/row-storage-design.md +1 -1
- package/guides/square-api.md +69 -30
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/register.js +1 -1
- package/plugins/claude/skills/square/SKILL.md +7 -7
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +170 -118
- package/plugins/opencode/index.ts +12 -30
- package/plugins/pi/square-pi.js +58 -20
- package/skills/square/SKILL.md +7 -7
- package/dist/cli/ledger-command.d.ts +0 -12
- package/dist/cli/ledger-command.js +0 -72
- package/dist/host-ledger-file-adapter.d.ts +0 -47
- package/dist/host-ledger-file-adapter.js +0 -384
- package/dist/host-ledger-migration.d.ts +0 -27
- package/dist/host-ledger-migration.js +0 -264
- package/dist/host-ledger-root.d.ts +0 -2
- package/dist/host-ledger-root.js +0 -9
- package/dist/host-ledger-sqlite.d.ts +0 -65
- package/dist/host-ledger-sqlite.js +0 -368
- package/dist/host-ledger.d.ts +0 -195
- package/dist/ledger-maintenance.d.ts +0 -30
- package/dist/ledger-maintenance.js +0 -110
- package/dist/open-square.d.ts +0 -36
- package/dist/open-square.js +0 -81
- package/dist/participant-host.d.ts +0 -22
- package/dist/participant-host.js +0 -92
- package/dist/registry.d.ts +0 -55
- package/dist/registry.js +0 -123
- package/dist/routes.d.ts +0 -39
- package/dist/routes.js +0 -99
- package/dist/square-actions.d.ts +0 -56
- package/dist/square-actions.js +0 -353
- package/dist/square-projections.d.ts +0 -31
- package/dist/square-projections.js +0 -64
- package/dist/wake-attempts.d.ts +0 -10
- package/dist/wake-attempts.js +0 -39
- package/dist/wake-eligibility.d.ts +0 -53
- package/dist/wake-eligibility.js +0 -123
- package/dist/wake-evidence.d.ts +0 -33
- package/dist/wake-evidence.js +0 -32
- package/guides/host-ledger.md +0 -58
- package/guides/indexed-host-ledger-performance.md +0 -39
- /package/dist/{host-ledger.js → square-backend.js} +0 -0
package/dist/square-migration.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import { openExistingSquareDatabase } from './artifact.js';
|
|
2
3
|
import { inSqliteTransaction } from './sqlite-transaction.js';
|
|
3
|
-
import { canonicalPath } from './canonical-path.js';
|
|
4
|
-
import { isWakeRouteKind } from './model.js';
|
|
4
|
+
import { canonicalPath, canonicalPathSync } from './canonical-path.js';
|
|
5
|
+
import { formatActivityId, isWakeRouteKind, nameKey, sameBody, validateBody } from './model.js';
|
|
5
6
|
import { parseActivityId } from './square-core.js';
|
|
6
|
-
import {
|
|
7
|
+
import { readLegacyHostData } from './square-migration-host-ledger.js';
|
|
8
|
+
import { legacyRowFramingIssue, FORMAT4_ADDED_OBJECTS, FORMAT4_REPLACED_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, SCHEMA_SQL, createSqliteDriver, invalidArtifact, validateStoredActivity } from './square-sqlite.js';
|
|
7
10
|
function isObject(value) {
|
|
8
11
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
12
|
}
|
|
@@ -30,7 +33,7 @@ function validateRuntime(value) {
|
|
|
30
33
|
&& Object.entries(candidate).every(([id, observation]) => parseActivityId(id) !== undefined && isObject(observation) && hasExactKeys(observation, ['state', 'at']) && observation.state === 'seen' && isFiniteNumber(observation.at)))
|
|
31
34
|
&& validateRecord(value.leases, (candidate) => {
|
|
32
35
|
try {
|
|
33
|
-
|
|
36
|
+
validateLegacyLease(candidate);
|
|
34
37
|
return true;
|
|
35
38
|
}
|
|
36
39
|
catch {
|
|
@@ -126,37 +129,6 @@ function identity(database) {
|
|
|
126
129
|
function standing(database) {
|
|
127
130
|
return createSqliteDriver(database).roster().filter((participant) => participant.joined).length;
|
|
128
131
|
}
|
|
129
|
-
/** The snapshot row, decoded only by the legacy validator. */
|
|
130
|
-
function decodeLegacySnapshot(database) {
|
|
131
|
-
const rows = database.prepare(`SELECT id, revision, state FROM ${V1_SNAPSHOT_TABLE}`).all();
|
|
132
|
-
if (rows.length !== 1 || rows[0].id !== 1 || !Number.isSafeInteger(rows[0].revision) || rows[0].revision < 0)
|
|
133
|
-
throw invalidArtifact('snapshot row is malformed.');
|
|
134
|
-
if (typeof rows[0].state !== 'string')
|
|
135
|
-
throw invalidArtifact('snapshot state is malformed.');
|
|
136
|
-
let parsed;
|
|
137
|
-
try {
|
|
138
|
-
parsed = JSON.parse(rows[0].state);
|
|
139
|
-
}
|
|
140
|
-
catch {
|
|
141
|
-
throw invalidArtifact('snapshot state is not valid JSON.');
|
|
142
|
-
}
|
|
143
|
-
return { revision: rows[0].revision, state: validateSquareState(parsed) };
|
|
144
|
-
}
|
|
145
|
-
function counts(state) {
|
|
146
|
-
let receipts = 0;
|
|
147
|
-
for (const observations of Object.values(state.runtime.observations))
|
|
148
|
-
receipts += Object.keys(observations).length;
|
|
149
|
-
return {
|
|
150
|
-
activities: state.acts.length,
|
|
151
|
-
receipts,
|
|
152
|
-
leases: Object.keys(state.runtime.leases).length,
|
|
153
|
-
routes: state.routes?.length ?? 0,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
function rowCounts(database) {
|
|
157
|
-
const count = (table) => Number(database.prepare(`SELECT COUNT(*) AS n FROM ${table}`).get().n);
|
|
158
|
-
return { activities: count('activities'), receipts: count('receipts'), leases: count('catch_leases'), routes: count('routes') };
|
|
159
|
-
}
|
|
160
132
|
const ACTIVITY_INDEX_NAMES = ['activity_kind_index', 'activity_actor_index', 'activity_say_actor_index', 'activity_say_time', 'activity_bell_actor_time'];
|
|
161
133
|
function rebuildV2Activities(database) {
|
|
162
134
|
database.exec([
|
|
@@ -168,63 +140,393 @@ function rebuildV2Activities(database) {
|
|
|
168
140
|
...ACTIVITY_INDEXES_SQL.map((sql) => `${sql};`),
|
|
169
141
|
].join('\n'));
|
|
170
142
|
}
|
|
143
|
+
function normalizedSql(sql) {
|
|
144
|
+
return sql.trim().replace(/;$/, '').replace(/\s+/g, ' ').replace(/\s*([(),=])\s*/g, '$1');
|
|
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.');
|
|
163
|
+
createSqliteDriver(database).header();
|
|
164
|
+
}
|
|
165
|
+
function assertSource(condition, detail = 'row is malformed.') {
|
|
166
|
+
if (!condition)
|
|
167
|
+
throw invalidArtifact(detail);
|
|
168
|
+
}
|
|
169
|
+
function parseJson(value, detail) {
|
|
170
|
+
assertSource(typeof value === 'string', detail);
|
|
171
|
+
try {
|
|
172
|
+
return JSON.parse(value);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
throw invalidArtifact(detail);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function decodeJsonString(value, nonempty = false) {
|
|
179
|
+
const parsed = parseJson(value, 'row contains invalid JSON.');
|
|
180
|
+
assertSource(typeof parsed === 'string' && (!nonempty || parsed.length > 0) && JSON.stringify(parsed) === value, 'string encoding is malformed.');
|
|
181
|
+
return parsed;
|
|
182
|
+
}
|
|
183
|
+
function sourceInteger(value, minimum = 0) {
|
|
184
|
+
assertSource(typeof value === 'number' && Number.isSafeInteger(value) && value >= minimum);
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
function sourceFinite(value) {
|
|
188
|
+
assertSource(typeof value === 'number' && Number.isFinite(value));
|
|
189
|
+
return value;
|
|
190
|
+
}
|
|
191
|
+
function sourceStringArray(value) {
|
|
192
|
+
const parsed = parseJson(value, 'header contains invalid JSON.');
|
|
193
|
+
assertSource(Array.isArray(parsed) && parsed.every((item) => typeof item === 'string'));
|
|
194
|
+
assertSource(JSON.stringify(parsed) === value, 'header string encoding is malformed.');
|
|
195
|
+
return parsed;
|
|
196
|
+
}
|
|
197
|
+
function decodeLegacyRoute(row) {
|
|
198
|
+
assertSource(sourceInteger(row.position) >= 0);
|
|
199
|
+
const parsed = parseJson(row.payload_json, 'route payload is malformed.');
|
|
200
|
+
assertSource(isObject(parsed)
|
|
201
|
+
&& hasExactKeys(parsed, ['location', 'participant', 'channel', 'address', 'extra'], ['epoch']), 'route payload is malformed.');
|
|
202
|
+
assertSource(typeof parsed.location === 'string' && typeof parsed.participant === 'string'
|
|
203
|
+
&& typeof parsed.channel === 'string' && isObject(parsed.address)
|
|
204
|
+
&& Object.values(parsed.address).every((value) => typeof value === 'string')
|
|
205
|
+
&& isObject(parsed.extra), 'route payload is malformed.');
|
|
206
|
+
assertSource(isWakeRouteKind(row.kind), 'route kind is malformed.');
|
|
207
|
+
const participant = parsed.participant;
|
|
208
|
+
assertSource(row.participant_key_json === JSON.stringify(nameKey(participant)), 'route participant key is malformed.');
|
|
209
|
+
const sessionId = decodeJsonString(row.session_json);
|
|
210
|
+
sourceFinite(row.updated_at);
|
|
211
|
+
const body = { channel: parsed.channel, session: sessionId, ...(parsed.extra.agent === undefined ? {} : { agent: parsed.extra.agent }) };
|
|
212
|
+
validateBody(body);
|
|
213
|
+
const address = parsed.address;
|
|
214
|
+
if (row.kind === 'claude-native')
|
|
215
|
+
assertSource(address.sessionId === undefined || address.sessionId === sessionId, 'native route session conflicts with its body.');
|
|
216
|
+
return { location: parsed.location, participant, body,
|
|
217
|
+
wakeAddress: { kind: row.kind, address: { ...address, ...(row.kind === 'claude-native' ? { sessionId } : {}) } } };
|
|
218
|
+
}
|
|
219
|
+
function validateLegacyLease(value) {
|
|
220
|
+
assertSource(isObject(value) && hasExactKeys(value, ['leaseId', 'heartbeatAt', 'expiresAt'], ['filter']), 'legacy lease is malformed.');
|
|
221
|
+
assertSource(typeof value.leaseId === 'string' && value.leaseId.length > 0
|
|
222
|
+
&& typeof value.heartbeatAt === 'number' && Number.isFinite(value.heartbeatAt)
|
|
223
|
+
&& typeof value.expiresAt === 'number' && Number.isFinite(value.expiresAt)
|
|
224
|
+
&& value.expiresAt >= value.heartbeatAt, 'legacy lease is malformed.');
|
|
225
|
+
if (!Object.hasOwn(value, 'filter'))
|
|
226
|
+
return;
|
|
227
|
+
const filter = value.filter;
|
|
228
|
+
assertSource(isObject(filter) && hasExactKeys(filter, [], ['participants', 'mention']), 'legacy lease filter is malformed.');
|
|
229
|
+
assertSource((!Object.hasOwn(filter, 'participants') || Array.isArray(filter.participants) && filter.participants.every((item) => typeof item === 'string'))
|
|
230
|
+
&& (!Object.hasOwn(filter, 'mention') || typeof filter.mention === 'string'), 'legacy lease filter is malformed.');
|
|
231
|
+
}
|
|
232
|
+
function readLegacyRoutes(database) {
|
|
233
|
+
return database.prepare('SELECT position,participant_key_json,session_json,kind,updated_at,payload_json FROM routes ORDER BY position').all()
|
|
234
|
+
.map(decodeLegacyRoute);
|
|
235
|
+
}
|
|
236
|
+
function uniqueSeatAddress(routes) {
|
|
237
|
+
const addresses = new Map();
|
|
238
|
+
for (const route of routes) {
|
|
239
|
+
const entries = Object.entries(route.wakeAddress.address).sort(([left], [right]) => left.localeCompare(right));
|
|
240
|
+
addresses.set(JSON.stringify([route.wakeAddress.kind, entries]), route.wakeAddress);
|
|
241
|
+
}
|
|
242
|
+
return addresses.size === 1 ? [...addresses.values()][0] : undefined;
|
|
243
|
+
}
|
|
244
|
+
function establishingJoin(driver, participant) {
|
|
245
|
+
const join = driver.activityHeads({ actors: [participant.name], kinds: ['join'] }, { edge: 'newest', limit: 1 }).items[0];
|
|
246
|
+
assertSource(join?.kind === 'join' && join.index === participant.lastJoinIndex, `participant ${participant.name} has no verifiable establishing join.`);
|
|
247
|
+
return join.index;
|
|
248
|
+
}
|
|
249
|
+
function planSeats(location, roster, routes, owners, driver) {
|
|
250
|
+
const standing = roster.filter((participant) => participant.joined);
|
|
251
|
+
return standing.map((participant) => {
|
|
252
|
+
const joined = establishingJoin(driver, participant);
|
|
253
|
+
const candidates = routes.filter((route) => canonicalPathSync(route.location) === location && nameKey(route.participant) === nameKey(participant.name));
|
|
254
|
+
const owner = owners.find(row => nameKey(row.participant) === nameKey(participant.name));
|
|
255
|
+
assertSource(owner !== undefined, `standing participant ${participant.name} has no legacy owner.`);
|
|
256
|
+
const { body } = owner;
|
|
257
|
+
assertSource(candidates.length > 0 && candidates.every(route => sameBody(route.body, body)), `standing participant ${participant.name} has no unique legacy body agreeing with its owner.`);
|
|
258
|
+
const shared = [...owners, ...routes.filter(route => canonicalPathSync(route.location) === location)]
|
|
259
|
+
.some(row => nameKey(row.participant) !== nameKey(participant.name) && sameBody(row.body, body));
|
|
260
|
+
assertSource(!shared, `standing participant ${participant.name} shares a legacy body with another owner.`);
|
|
261
|
+
const address = uniqueSeatAddress(candidates);
|
|
262
|
+
return {
|
|
263
|
+
participant: participant.name,
|
|
264
|
+
body,
|
|
265
|
+
generation: formatActivityId(joined),
|
|
266
|
+
...(address === undefined ? {} : { wakeAddress: address }),
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
function tableToken(database, table, order) {
|
|
271
|
+
const rows = database.prepare(`SELECT * FROM ${table} ORDER BY ${order}`).all();
|
|
272
|
+
return JSON.stringify(rows.map((row) => Object.entries(row).sort(([left], [right]) => left.localeCompare(right))));
|
|
273
|
+
}
|
|
274
|
+
function legacySourceToken(database) {
|
|
275
|
+
return JSON.stringify([
|
|
276
|
+
['square_meta', tableToken(database, 'square_meta', 'id')],
|
|
277
|
+
['activities', tableToken(database, 'activities', 'act_index')],
|
|
278
|
+
['participants', tableToken(database, 'participants', 'name_key_json')],
|
|
279
|
+
['attention', tableToken(database, 'attention', 'listener_key_json,target_key_json')],
|
|
280
|
+
['landed_audience', tableToken(database, 'landed_audience', 'act_index,recipient_order')],
|
|
281
|
+
['receipts', tableToken(database, 'receipts', 'owner_json,act_index')],
|
|
282
|
+
['catch_leases', tableToken(database, 'catch_leases', 'owner_json')],
|
|
283
|
+
['routes', tableToken(database, 'routes', 'position')],
|
|
284
|
+
]);
|
|
285
|
+
}
|
|
286
|
+
function inspectLegacyRowArtifact(database, location, host) {
|
|
287
|
+
const metaRows = database.prepare('SELECT * FROM square_meta').all();
|
|
288
|
+
assertSource(metaRows.length === 1 && metaRows[0].id === 1, 'header row is malformed.');
|
|
289
|
+
const meta = metaRows[0];
|
|
290
|
+
sourceInteger(meta.revision);
|
|
291
|
+
const nextActIndex = sourceInteger(meta.next_act_index);
|
|
292
|
+
assertSource(meta.hard_cap === null || (typeof meta.hard_cap === 'number' && Number.isSafeInteger(meta.hard_cap) && meta.hard_cap > 0), 'header row is malformed.');
|
|
293
|
+
assertSource(meta.throttle_per_minute === null || (typeof meta.throttle_per_minute === 'number' && Number.isSafeInteger(meta.throttle_per_minute) && meta.throttle_per_minute > 0), 'header row is malformed.');
|
|
294
|
+
sourceStringArray(meta.preamble_json);
|
|
295
|
+
sourceStringArray(meta.warmup_json);
|
|
296
|
+
const driver = createSqliteDriver(database);
|
|
297
|
+
let activityCount = 0;
|
|
298
|
+
let previous = -1;
|
|
299
|
+
for (const row of database.prepare('SELECT act_index FROM activities ORDER BY act_index').iterate()) {
|
|
300
|
+
const index = sourceInteger(row.act_index);
|
|
301
|
+
assertSource(index > previous && index < nextActIndex, 'activity index is malformed or unassigned.');
|
|
302
|
+
const activity = driver.activity(index);
|
|
303
|
+
assertSource(activity !== undefined);
|
|
304
|
+
validateStoredActivity(activity);
|
|
305
|
+
activityCount++;
|
|
306
|
+
previous = index;
|
|
307
|
+
}
|
|
308
|
+
assertSource(nextActIndex > previous, 'nextActIndex is behind the activity history.');
|
|
309
|
+
const roster = driver.roster();
|
|
310
|
+
const routes = readLegacyRoutes(database);
|
|
311
|
+
for (const row of database.prepare('SELECT owner_json,act_index,at FROM receipts').iterate()) {
|
|
312
|
+
decodeJsonString(row.owner_json, true);
|
|
313
|
+
assertSource(sourceInteger(row.act_index) < nextActIndex, 'receipt references an unassigned activity index.');
|
|
314
|
+
sourceFinite(row.at);
|
|
315
|
+
}
|
|
316
|
+
const leaseGenerations = [];
|
|
317
|
+
const leaseOwners = [];
|
|
318
|
+
for (const row of database.prepare('SELECT owner_json,lease_id_json,heartbeat_at,expires_at,filter_json FROM catch_leases').iterate()) {
|
|
319
|
+
const owner = decodeJsonString(row.owner_json, true);
|
|
320
|
+
leaseOwners.push(row.owner_json);
|
|
321
|
+
const leaseId = decodeJsonString(row.lease_id_json, true);
|
|
322
|
+
const lease = {
|
|
323
|
+
leaseId,
|
|
324
|
+
heartbeatAt: sourceFinite(row.heartbeat_at),
|
|
325
|
+
expiresAt: sourceFinite(row.expires_at),
|
|
326
|
+
...(row.filter_json === null ? {} : { filter: parseJson(row.filter_json, 'lease filter is malformed.') }),
|
|
327
|
+
};
|
|
328
|
+
validateLegacyLease(lease);
|
|
329
|
+
const ownerParticipant = roster.find((participant) => participant.joined && participant.name === owner);
|
|
330
|
+
if (ownerParticipant?.lastJoinIndex !== undefined)
|
|
331
|
+
leaseGenerations.push({ ownerJson: row.owner_json, generation: formatActivityId(ownerParticipant.lastJoinIndex) });
|
|
332
|
+
}
|
|
333
|
+
const seats = planSeats(location, roster, routes, host.owners, driver);
|
|
334
|
+
const counts = {
|
|
335
|
+
activities: activityCount,
|
|
336
|
+
receipts: Number(database.prepare('SELECT COUNT(*) AS n FROM receipts').get().n),
|
|
337
|
+
leases: leaseOwners.length,
|
|
338
|
+
routes: routes.length,
|
|
339
|
+
};
|
|
340
|
+
const sourceToken = legacySourceToken(database);
|
|
341
|
+
return { standing: seats.length, seats, leaseGenerations, sourceToken, counts,
|
|
342
|
+
deliveries: planLegacyDeliveries(host.evidence, seats, driver) };
|
|
343
|
+
}
|
|
344
|
+
function legacyPayloadHash(payload) {
|
|
345
|
+
return createHash('sha256').update(payload).digest('hex');
|
|
346
|
+
}
|
|
347
|
+
function legacyDeliveryPayload(source, current) {
|
|
348
|
+
const native = source.nativeDelivery;
|
|
349
|
+
if (native === undefined)
|
|
350
|
+
return {};
|
|
351
|
+
assertSource(isObject(native) && typeof native.payload === 'string' && native.payload.length > 0, 'native delivery correlation is malformed.');
|
|
352
|
+
if (native.harness === 'claude') {
|
|
353
|
+
assertSource(typeof source.claimToken === 'string' && /^[a-zA-Z0-9-]+$/.test(source.claimToken)
|
|
354
|
+
&& typeof native.endpoint === 'string' && native.endpoint.length > 0
|
|
355
|
+
&& (native.messageId === undefined || typeof native.messageId === 'string' && native.messageId.length > 0), 'Claude native correlation is malformed.');
|
|
356
|
+
const address = current?.kind === 'claude-native' && current.address.endpoint === native.endpoint
|
|
357
|
+
? current.address : { endpoint: native.endpoint, loaded: 'true' };
|
|
358
|
+
return { correlation: { harness: 'claude', token: source.claimToken,
|
|
359
|
+
...(native.messageId === undefined ? {} : { messageId: native.messageId }), payloadHash: legacyPayloadHash(native.payload) },
|
|
360
|
+
route: { kind: 'claude-native', address: { ...address, sessionId: source.session } } };
|
|
361
|
+
}
|
|
362
|
+
assertSource(native.harness === 'opencode' && typeof native.inputId === 'string' && native.inputId.length > 0, 'OpenCode native correlation is malformed.');
|
|
363
|
+
return { correlation: { harness: 'opencode', token: native.inputId, payloadHash: legacyPayloadHash(native.payload) },
|
|
364
|
+
route: current?.kind === 'opencode-server' && current.address.sessionId === source.session
|
|
365
|
+
? current : { kind: 'opencode-server', address: { sessionId: source.session } } };
|
|
366
|
+
}
|
|
367
|
+
function planLegacyDeliveries(records, seats, driver) {
|
|
368
|
+
const byGroup = new Map();
|
|
369
|
+
const tokens = new Set(), completedBodies = new Map();
|
|
370
|
+
for (const source of records) {
|
|
371
|
+
const participant = driver.participant(source.participant);
|
|
372
|
+
const native = source.kind === 'wake' && source.nativeDelivery !== undefined;
|
|
373
|
+
if (!participant?.joined && !native)
|
|
374
|
+
continue; // Noncritical completed diagnostics are not imported.
|
|
375
|
+
assertSource(participant?.joined || participant?.done, `native delivery evidence for ${source.participant} has no unique participant.`);
|
|
376
|
+
const joined = establishingJoin(driver, participant);
|
|
377
|
+
const generation = formatActivityId(joined);
|
|
378
|
+
const seat = seats.find(seat => nameKey(seat.participant) === nameKey(participant.name));
|
|
379
|
+
if (participant.joined)
|
|
380
|
+
assertSource(seat !== undefined && seat.body.session === source.session
|
|
381
|
+
&& source.agent === seat.body.agent && (source.channel === undefined || source.channel === seat.body.channel), `delivery evidence for standing participant ${source.participant} has no unique owner.`);
|
|
382
|
+
if (native && seat !== undefined)
|
|
383
|
+
assertSource(source.nativeDelivery?.harness ===
|
|
384
|
+
(seat.body.channel === 'claude-code' ? 'claude' : seat.body.channel === 'opencode' ? 'opencode' : undefined), 'native delivery harness conflicts with its owner body.');
|
|
385
|
+
const index = parseActivityId(source.activity);
|
|
386
|
+
assertSource(index !== undefined && index > joined && driver.activity(index)?.kind === 'say'
|
|
387
|
+
&& driver.directedAt(participant.name, index) !== undefined, `delivery evidence for ${source.participant} has no unique activity in its join generation.`);
|
|
388
|
+
const payload = legacyDeliveryPayload(source, seat?.wakeAddress);
|
|
389
|
+
if (participant.done) {
|
|
390
|
+
const done = driver.activityHeads({ actors: [participant.name], kinds: ['done'] }, { edge: 'newest', limit: 1 }).items[0];
|
|
391
|
+
assertSource(done !== undefined && index < done.index && source.session.length > 0, `native delivery evidence for ${source.participant} has no unique completed generation.`);
|
|
392
|
+
const harness = source.nativeDelivery.harness;
|
|
393
|
+
const body = { channel: harness === 'claude' ? 'claude-code' : 'opencode', session: source.session,
|
|
394
|
+
...(source.agent === undefined ? {} : { agent: source.agent }) };
|
|
395
|
+
validateBody(body);
|
|
396
|
+
const coordinate = JSON.stringify([body.channel, body.session, body.agent]);
|
|
397
|
+
const previous = completedBodies.get(nameKey(participant.name));
|
|
398
|
+
assertSource(previous === undefined || previous === coordinate, 'completed native delivery owner mapping is ambiguous.');
|
|
399
|
+
completedBodies.set(nameKey(participant.name), coordinate);
|
|
400
|
+
}
|
|
401
|
+
assertSource(source.kind === 'wake' || source.kind === 'presentation', 'delivery evidence kind is unsupported.');
|
|
402
|
+
assertSource(['dispatching', 'accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(source.outcome), 'delivery evidence outcome is unsupported.');
|
|
403
|
+
const at = sourceFinite(source.at);
|
|
404
|
+
const expiresAt = source.expiresAt === undefined ? undefined : sourceFinite(source.expiresAt);
|
|
405
|
+
const claimToken = source.claimToken;
|
|
406
|
+
assertSource(claimToken === undefined || typeof claimToken === 'string' && claimToken.length > 0, 'delivery claim token is malformed.');
|
|
407
|
+
if (claimToken !== undefined) {
|
|
408
|
+
assertSource(!tokens.has(claimToken), 'delivery claim token is ambiguous.');
|
|
409
|
+
tokens.add(claimToken);
|
|
410
|
+
}
|
|
411
|
+
const group = JSON.stringify([nameKey(participant.name), generation, index, source.kind]);
|
|
412
|
+
const row = {
|
|
413
|
+
participant: participant.name,
|
|
414
|
+
generation,
|
|
415
|
+
index,
|
|
416
|
+
kind: source.kind,
|
|
417
|
+
outcome: source.outcome,
|
|
418
|
+
at,
|
|
419
|
+
...(expiresAt === undefined ? {} : { expiresAt }),
|
|
420
|
+
...(claimToken === undefined ? {} : { claimToken }),
|
|
421
|
+
payload,
|
|
422
|
+
...(source.attemptN === undefined ? {} : { attemptN: source.attemptN }),
|
|
423
|
+
};
|
|
424
|
+
const rows = byGroup.get(group) ?? [];
|
|
425
|
+
rows.push(row);
|
|
426
|
+
byGroup.set(group, rows);
|
|
427
|
+
}
|
|
428
|
+
const deliveries = [];
|
|
429
|
+
for (const rows of byGroup.values()) {
|
|
430
|
+
const attempts = rows.map(row => row.attemptN);
|
|
431
|
+
const explicit = attempts.filter((attempt) => attempt !== undefined);
|
|
432
|
+
assertSource(explicit.every((attempt) => Number.isSafeInteger(attempt) && attempt >= 0) && new Set(explicit).size === explicit.length, 'delivery attempt mapping is ambiguous.');
|
|
433
|
+
if (explicit.length !== rows.length)
|
|
434
|
+
assertSource(rows.length === 1 && explicit.length === 0, 'delivery attempt mapping is ambiguous.');
|
|
435
|
+
rows.forEach((row, index) => deliveries.push({ ...row, attemptN: explicit.length === 0 ? 0 : explicit[index] }));
|
|
436
|
+
}
|
|
437
|
+
return deliveries;
|
|
438
|
+
}
|
|
439
|
+
const V4_REBUILT_TABLES = ['square_meta', 'catch_leases'];
|
|
440
|
+
function tableDefinition(name) {
|
|
441
|
+
const definition = SCHEMA_SQL.split(';').map((statement) => statement.trim()).find((statement) => statement.startsWith(`CREATE TABLE ${name} (`));
|
|
442
|
+
if (definition === undefined)
|
|
443
|
+
throw new Error(`Missing current table definition for ${name}.`);
|
|
444
|
+
return `${definition};`;
|
|
445
|
+
}
|
|
446
|
+
function currentAdditionStatements() {
|
|
447
|
+
return SCHEMA_SQL.split(';').map((statement) => statement.trim()).filter((statement) => {
|
|
448
|
+
const match = /^(?:CREATE (?:UNIQUE )?TABLE|CREATE (?:UNIQUE )?INDEX)\s+([a-z_]+)/i.exec(statement);
|
|
449
|
+
return match !== null && FORMAT4_ADDED_OBJECTS.has(match[1]);
|
|
450
|
+
}).map((statement) => `${statement};`);
|
|
451
|
+
}
|
|
452
|
+
function importOwnershipAndEvidence(database, plan) {
|
|
453
|
+
for (const generation of plan.leaseGenerations) {
|
|
454
|
+
database.prepare('UPDATE catch_leases SET generation_json=? WHERE owner_json=?').run(JSON.stringify(generation.generation), generation.ownerJson);
|
|
455
|
+
}
|
|
456
|
+
const insertSeat = database.prepare('INSERT INTO seats(participant_key_json,participant_json,channel_json,session_json,agent_json,generation_json,wake_address_json) VALUES(?,?,?,?,?,?,?)');
|
|
457
|
+
for (const seat of plan.seats) {
|
|
458
|
+
insertSeat.run(JSON.stringify(nameKey(seat.participant)), JSON.stringify(seat.participant), JSON.stringify(seat.body.channel), JSON.stringify(seat.body.session), seat.body.agent === undefined ? null : JSON.stringify(seat.body.agent), JSON.stringify(seat.generation), seat.wakeAddress === undefined ? null : JSON.stringify(seat.wakeAddress));
|
|
459
|
+
}
|
|
460
|
+
const insertDelivery = database.prepare('INSERT INTO delivery_evidence(participant_key_json,participant_json,generation_json,act_index,kind,attempt_n,outcome,at,expires_at,claim_token_json,payload_json) VALUES(?,?,?,?,?,?,?,?,?,?,?)');
|
|
461
|
+
for (const evidence of plan.deliveries) {
|
|
462
|
+
insertDelivery.run(JSON.stringify(nameKey(evidence.participant)), JSON.stringify(evidence.participant), JSON.stringify(evidence.generation), evidence.index, evidence.kind, evidence.attemptN, evidence.outcome, evidence.at, evidence.expiresAt ?? null, evidence.claimToken === undefined ? null : JSON.stringify(evidence.claimToken), JSON.stringify(evidence.payload));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
function upgradeRowArtifact(database, location, plan) {
|
|
466
|
+
database.exec('ALTER TABLE square_meta RENAME TO square_meta_v3;');
|
|
467
|
+
database.exec(tableDefinition('square_meta'));
|
|
468
|
+
database.exec('INSERT INTO square_meta(id,revision,delivery_revision,next_act_index,hard_cap,throttle_per_minute,preamble_json,warmup_json) SELECT id,revision,0,next_act_index,hard_cap,throttle_per_minute,preamble_json,warmup_json FROM square_meta_v3;');
|
|
469
|
+
database.exec('DROP TABLE square_meta_v3;');
|
|
470
|
+
database.exec('ALTER TABLE catch_leases RENAME TO catch_leases_v3;');
|
|
471
|
+
database.exec(tableDefinition('catch_leases'));
|
|
472
|
+
database.exec('INSERT INTO catch_leases(owner_json,generation_json,lease_id_json,heartbeat_at,expires_at,filter_json) SELECT owner_json, \'""\', lease_id_json,heartbeat_at,expires_at,filter_json FROM catch_leases_v3;');
|
|
473
|
+
database.exec('DROP TABLE catch_leases_v3;');
|
|
474
|
+
currentAdditionStatements().forEach((statement) => database.exec(statement));
|
|
475
|
+
importOwnershipAndEvidence(database, plan);
|
|
476
|
+
database.exec('DROP TABLE routes;');
|
|
477
|
+
database.exec(`PRAGMA user_version = ${CURRENT_USER_VERSION};`);
|
|
478
|
+
validateFormat4Framing(database, location);
|
|
479
|
+
}
|
|
171
480
|
function validateMigrationSource(database, location) {
|
|
172
481
|
const before = identity(database);
|
|
173
482
|
if (before.applicationId !== APPLICATION_ID)
|
|
174
483
|
throw invalidArtifact('unrelated SQLite database.');
|
|
175
|
-
if (before.userVersion ===
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
validateRowFraming(database, V2_USER_VERSION, location);
|
|
181
|
-
createSqliteDriver(database).header();
|
|
182
|
-
return 2;
|
|
484
|
+
if (before.userVersion === V2_USER_VERSION || before.userVersion === V3_USER_VERSION) {
|
|
485
|
+
const issue = legacyRowFramingIssue(database, APPLICATION_ID, before.userVersion, location, SCHEMA_SQL);
|
|
486
|
+
if (issue !== undefined)
|
|
487
|
+
throw invalidArtifact(issue);
|
|
488
|
+
return before.userVersion;
|
|
183
489
|
}
|
|
184
490
|
if (before.userVersion === CURRENT_USER_VERSION) {
|
|
185
|
-
|
|
491
|
+
validateFormat4Framing(database, location);
|
|
186
492
|
return undefined;
|
|
187
493
|
}
|
|
188
494
|
throw invalidArtifact('unsupported SQLite format version.');
|
|
189
495
|
}
|
|
190
496
|
/**
|
|
191
|
-
* Explicit
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
497
|
+
* Explicit conversion of the supported legacy formats. Row-backed versions 2 and 3 are
|
|
498
|
+
* preflighted read-only, then receive the format-4 ownership/evidence rows in one transaction.
|
|
499
|
+
* Legacy delivery evidence is copied from HostLedger only when its owner/session mapping is
|
|
500
|
+
* unique; no source record is retired. Version 1 is not a supported source for this format transition.
|
|
195
501
|
*/
|
|
196
|
-
export async function migrateSquareFile(location, signal) {
|
|
502
|
+
export async function migrateSquareFile(location, signal, env = process.env) {
|
|
197
503
|
const squarePath = await canonicalPath(location);
|
|
198
504
|
const database = openExistingSquareDatabase(squarePath);
|
|
199
505
|
try {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return alreadyCurrent;
|
|
506
|
+
const source = await inSqliteTransaction(database, false, () => validateMigrationSource(database, squarePath), value => value, signal);
|
|
507
|
+
if (source === undefined)
|
|
508
|
+
return { status: 'already-current', standing: standing(database) };
|
|
509
|
+
const host = await readLegacyHostData(squarePath, env, signal);
|
|
510
|
+
const plan = await inSqliteTransaction(database, false, () => {
|
|
511
|
+
assertSource(validateMigrationSource(database, squarePath) === source, 'source format changed during migration preflight.');
|
|
512
|
+
return inspectLegacyRowArtifact(database, squarePath, host);
|
|
513
|
+
}, value => value, signal);
|
|
209
514
|
database.exec('PRAGMA busy_timeout = 0; PRAGMA synchronous = FULL');
|
|
210
515
|
const result = {};
|
|
211
516
|
await inSqliteTransaction(database, true, () => {
|
|
212
|
-
|
|
213
|
-
|
|
517
|
+
assertSource(validateMigrationSource(database, squarePath) === source, 'source format changed during migration preflight.');
|
|
518
|
+
// Resolve the same frozen host input again against the locked artifact before any DDL.
|
|
519
|
+
const lockedPlan = inspectLegacyRowArtifact(database, squarePath, host);
|
|
520
|
+
assertSource(lockedPlan.sourceToken === plan.sourceToken, 'source changed during migration preflight.');
|
|
521
|
+
result.plan = lockedPlan;
|
|
214
522
|
}, () => {
|
|
215
|
-
if (
|
|
216
|
-
const legacy = decodeLegacySnapshot(database);
|
|
217
|
-
initializeSqlite(database, legacy.state, legacy.revision);
|
|
218
|
-
database.exec(`DROP TABLE ${V1_SNAPSHOT_TABLE}; PRAGMA user_version = ${CURRENT_USER_VERSION}`);
|
|
219
|
-
result.outcome = { status: 'migrated', standing: standing(database), counts: counts(legacy.state) };
|
|
220
|
-
}
|
|
221
|
-
else if (result.source === 2) {
|
|
523
|
+
if (source === 2)
|
|
222
524
|
rebuildV2Activities(database);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
}
|
|
525
|
+
const migratedPlan = result.plan;
|
|
526
|
+
upgradeRowArtifact(database, squarePath, migratedPlan);
|
|
527
|
+
result.outcome = { status: 'migrated', standing: migratedPlan.standing, counts: migratedPlan.counts };
|
|
226
528
|
}, signal);
|
|
227
|
-
return result.outcome
|
|
529
|
+
return result.outcome;
|
|
228
530
|
}
|
|
229
531
|
finally {
|
|
230
532
|
database.close();
|
package/dist/square-sqlite.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import { SquareError, type ActivityObservation, type HardCap, type StoredAct, type
|
|
3
|
-
import type { SquareHeader, SquareRead,
|
|
2
|
+
import { SquareError, type ActivityObservation, type HardCap, type StoredAct, type WatchLease } from './model.js';
|
|
3
|
+
import type { SquareHeader, SquareRead, DeliveryClaimInput, DeliveryClaimResult, DeliveryCompletion, DeliveryPreparation } from './ports.js';
|
|
4
4
|
import { type LandingWriter } from './square-domain.js';
|
|
5
5
|
/** Explicit import/export DTOs, not a runtime transaction model. */
|
|
6
6
|
export interface SquareRuntimeState {
|
|
@@ -14,13 +14,13 @@ export interface SquareState {
|
|
|
14
14
|
preamble: string[];
|
|
15
15
|
warmup: string[];
|
|
16
16
|
acts: StoredAct[];
|
|
17
|
-
routes?: WakeRoute[];
|
|
18
17
|
runtime: SquareRuntimeState;
|
|
19
18
|
}
|
|
20
19
|
export declare function emptyRuntimeState(nextActIndex?: number): SquareRuntimeState;
|
|
21
20
|
export declare const APPLICATION_ID = 1397838405;
|
|
22
21
|
export declare const V2_USER_VERSION = 2;
|
|
23
|
-
export declare const
|
|
22
|
+
export declare const V3_USER_VERSION = 3;
|
|
23
|
+
export declare const CURRENT_USER_VERSION = 4;
|
|
24
24
|
export declare const V1_SNAPSHOT_TABLE = "square_snapshot";
|
|
25
25
|
export interface LegacyFraming {
|
|
26
26
|
readonly applicationId: number;
|
|
@@ -37,21 +37,21 @@ export declare function validateLegacyFraming(database: DatabaseSync, applicatio
|
|
|
37
37
|
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
38
|
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'"];
|
|
39
39
|
export declare const SCHEMA_SQL: string;
|
|
40
|
-
export declare const SCHEMA_SQL_V2: string;
|
|
41
40
|
export declare function invalidArtifact(detail: string): SquareError;
|
|
42
41
|
export declare function validateStoredActivity(value: unknown): StoredAct;
|
|
43
42
|
export declare function validateWatchLease(value: unknown): WatchLease;
|
|
44
|
-
export declare function
|
|
45
|
-
export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3, location?: string): void;
|
|
43
|
+
export declare function validateRowFraming(db: DatabaseSync, version: 2 | 3 | 4, location?: string): void;
|
|
46
44
|
export declare function validateSqliteHeader(db: DatabaseSync, location?: string): SquareHeader;
|
|
47
45
|
export interface SqliteDriver extends SquareRead, LandingWriter {
|
|
48
46
|
readonly changed: boolean;
|
|
47
|
+
readonly deliveryChanged: boolean;
|
|
48
|
+
claimDelivery(input: DeliveryClaimInput): DeliveryClaimResult;
|
|
49
|
+
prepareDelivery(input: DeliveryPreparation): boolean;
|
|
50
|
+
completeDelivery(input: DeliveryCompletion): boolean;
|
|
49
51
|
}
|
|
50
52
|
export declare function createSqliteDriver(db: DatabaseSync): SqliteDriver;
|
|
51
|
-
export declare function legacyRouteFact(value: Record<string, unknown>): RouteFact;
|
|
52
53
|
/** Explicit import only. Caller owns transaction, legacy validation and format publication. */
|
|
53
54
|
export declare function initializeSqlite(db: DatabaseSync, state: SquareState, revision?: number): void;
|
|
54
|
-
/** Exceptional export projection for fixtures, never a runtime fallback. */
|
|
55
55
|
export declare function exportSqliteState(db: DatabaseSync): SquareState;
|
|
56
56
|
/** Full read-only integrity inspection, including maintained-index replay comparison. */
|
|
57
57
|
export declare function inspectSqlite(db: DatabaseSync): {
|