@astrosheep/square 0.3.106 → 0.4.1
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/activity-feed.js +1 -1
- package/dist/artifact-store.d.ts +41 -0
- package/dist/artifact-store.js +272 -0
- package/dist/artifact.d.ts +6 -12
- package/dist/artifact.js +43 -344
- package/dist/attention-presentation.d.ts +13 -7
- package/dist/attention-presentation.js +29 -68
- package/dist/boundary-presentation.d.ts +3 -16
- package/dist/boundary-presentation.js +29 -109
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.d.ts +2 -2
- package/dist/catch-decisions.js +7 -3
- package/dist/catch-selection.d.ts +8 -0
- package/dist/catch-selection.js +5 -0
- package/dist/claude-delivery.d.ts +14 -12
- package/dist/claude-delivery.js +42 -142
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +28 -140
- package/dist/claude-transport.d.ts +5 -0
- package/dist/claude-transport.js +34 -0
- package/dist/cli/context.d.ts +2 -2
- package/dist/cli/context.js +7 -10
- package/dist/cli/maintenance-commands.d.ts +0 -4
- package/dist/cli/maintenance-commands.js +2 -29
- package/dist/cli/observation-commands.d.ts +2 -0
- package/dist/cli/observation-commands.js +12 -2
- package/dist/cli/program.js +1 -1
- package/dist/cli/registry.js +4 -3
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -34
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +3 -3
- package/dist/delivery-deferral.d.ts +1 -19
- package/dist/delivery-deferral.js +1 -29
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-links.js +61 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +3 -6
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -79
- package/dist/inbox.d.ts +6 -57
- package/dist/inbox.js +24 -380
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -2
- package/dist/mcp.d.ts +2 -1
- package/dist/mcp.js +40 -70
- package/dist/message.d.ts +7 -0
- package/dist/message.js +23 -0
- package/dist/model.d.ts +15 -91
- package/dist/model.js +2 -140
- package/dist/neutral.d.ts +72 -0
- package/dist/neutral.js +27 -0
- package/dist/notifications.d.ts +24 -26
- package/dist/notifications.js +39 -143
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- package/dist/operation-presentation.d.ts +1 -2
- package/dist/operation-presentation.js +1 -7
- package/dist/owner.d.ts +2 -2
- package/dist/owner.js +1 -1
- package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -99
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +33 -214
- package/dist/presentation.d.ts +5 -5
- package/dist/presentation.js +1 -0
- package/dist/runtime.js +2 -2
- package/dist/search.js +1 -1
- package/dist/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-framing.d.ts +8 -0
- package/dist/sqlite-framing.js +31 -0
- package/dist/sqlite-square.d.ts +16 -26
- package/dist/sqlite-square.js +199 -262
- package/dist/sqlite-values.d.ts +21 -0
- package/dist/sqlite-values.js +78 -0
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +5 -8
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +12 -10
- package/dist/square-domain.js +105 -223
- package/dist/square-facade.d.ts +25 -76
- package/dist/square-location.d.ts +6 -1
- package/dist/square-location.js +1 -1
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-receive.d.ts +2 -3
- package/dist/square-sqlite.d.ts +33 -42
- package/dist/square-sqlite.js +236 -768
- package/dist/square-tools.d.ts +8 -0
- package/dist/square-tools.js +39 -0
- package/dist/square-values.d.ts +27 -0
- package/dist/square-values.js +90 -0
- package/dist/views.d.ts +6 -6
- package/dist/views.js +14 -9
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -0
- package/dist/wake-transports.js +11 -0
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +19 -172
- package/guides/codex-delivery.md +55 -78
- package/guides/delivery-primitives.md +48 -207
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/row-runtime-delivery-evidence.md +2 -0
- package/guides/row-storage-design.md +19 -44
- package/guides/square-api.md +113 -538
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/hooks.json +1 -1
- package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +167 -451
- package/dist/channel.d.ts +0 -74
- package/dist/channel.js +0 -92
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -9
- package/dist/delivery-operations.js +0 -168
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -45
- package/dist/delivery.js +0 -106
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -238
- package/dist/presentation-operations.d.ts +0 -18
- package/dist/presentation-operations.js +0 -86
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- package/dist/square-legacy-framing.d.ts +0 -11
- package/dist/square-legacy-framing.js +0 -64
- package/dist/square-migration-host-ledger.d.ts +0 -26
- package/dist/square-migration-host-ledger.js +0 -169
- package/dist/square-migration.d.ts +0 -30
- package/dist/square-migration.js +0 -599
- package/guides/migration.md +0 -156
- package/plugins/opencode/delivery.ts +0 -211
package/dist/artifact.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
-
import { retrySqliteBusy, inSqliteTransaction } from './sqlite-transaction.js';
|
|
3
|
-
import { randomUUID } from 'node:crypto';
|
|
4
|
-
import fs from 'node:fs';
|
|
5
1
|
import path from 'node:path';
|
|
6
|
-
import
|
|
7
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
8
|
-
import { observeFileVersion } from './file-changes.js';
|
|
2
|
+
import fs from 'node:fs';
|
|
9
3
|
import { canonicalPath } from './canonical-path.js';
|
|
10
4
|
import { InternalSquareError, SquareError } from './model.js';
|
|
11
5
|
import { wrapSquareWrite } from './square-domain.js';
|
|
12
|
-
import {
|
|
6
|
+
import { createSqliteDriver, initializeSqlite, exportSqliteState, inspectSqlite, emptyRuntimeState, squareStoreFormat } from './square-sqlite.js';
|
|
7
|
+
import { resetStorageMeta } from './sqlite-framing.js';
|
|
8
|
+
import { createArtifactStore, createStoreFile, replaceStoreFile, inspectStore, observeStoreRevision } from './artifact-store.js';
|
|
9
|
+
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
10
|
+
const format = squareStoreFormat(resetStorageMeta);
|
|
13
11
|
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
14
12
|
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
15
13
|
export function createSquareResourceOwner() {
|
|
@@ -53,26 +51,26 @@ function normalizedLines(value) {
|
|
|
53
51
|
return normalized === '' ? [] : normalized.split('\n');
|
|
54
52
|
}
|
|
55
53
|
/** All build paths share configuration validation and lazy guide preparation. */
|
|
56
|
-
export function prepareSquareBuild(options, snippet) {
|
|
54
|
+
export function prepareSquareBuild(options, snippet, includeGuides = true) {
|
|
57
55
|
if (options.hardCap !== undefined && options.hardCap !== null
|
|
58
56
|
&& (!Number.isSafeInteger(options.hardCap) || options.hardCap < 1))
|
|
59
57
|
throw new SquareError('invalid_args', 'hardCap must be a positive integer or null');
|
|
60
58
|
if (options.throttlePerMinute !== undefined
|
|
61
59
|
&& (!Number.isSafeInteger(options.throttlePerMinute) || options.throttlePerMinute < 1))
|
|
62
60
|
throw new SquareError('invalid_args', 'throttlePerMinute must be a positive integer');
|
|
63
|
-
const guides = [readGuide('participant')];
|
|
64
|
-
if (options.template !== undefined)
|
|
61
|
+
const guides = includeGuides ? [readGuide('participant')] : [];
|
|
62
|
+
if (includeGuides && options.template !== undefined)
|
|
65
63
|
guides.push(readGuide(options.template));
|
|
66
64
|
return {
|
|
67
65
|
hardCap: options.hardCap ?? null,
|
|
68
66
|
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
69
|
-
preamble: normalizedLines(snippet), warmup: normalizedLines(guides.join('\n\n')),
|
|
67
|
+
preamble: normalizedLines(snippet), warmup: includeGuides ? normalizedLines(guides.join('\n\n')) : [],
|
|
70
68
|
acts: [], runtime: emptyRuntimeState(),
|
|
71
69
|
};
|
|
72
70
|
}
|
|
73
71
|
/** File build owns publication and CLI conflict semantics, not host resources. */
|
|
74
|
-
export async function createSquare(location, options, snippet, signal) {
|
|
75
|
-
const state = prepareSquareBuild(options, snippet);
|
|
72
|
+
export async function createSquare(location, options, snippet, signal, includeGuides = true) {
|
|
73
|
+
const state = prepareSquareBuild(options, snippet, includeGuides);
|
|
76
74
|
if (options.force) {
|
|
77
75
|
await writeSquareFile(location, state, signal);
|
|
78
76
|
}
|
|
@@ -81,359 +79,60 @@ export async function createSquare(location, options, snippet, signal) {
|
|
|
81
79
|
}
|
|
82
80
|
return { hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }) };
|
|
83
81
|
}
|
|
84
|
-
function requireSquareExtension(location) {
|
|
85
|
-
if (!location.endsWith('.square'))
|
|
86
|
-
throw new SquareError('invalid_args', `Square artifacts must use the .square extension: ${location}`);
|
|
87
|
-
}
|
|
88
|
-
function storageError(error, location) {
|
|
89
|
-
if (!(error instanceof Error) || error instanceof SquareError || error instanceof InternalSquareError)
|
|
90
|
-
return error;
|
|
91
|
-
if (/unable to open database file|no such file|SQLITE_CANTOPEN/i.test(error.message))
|
|
92
|
-
return new InternalSquareError('not_found', `square file not found: ${location}`);
|
|
93
|
-
if (/not a database|SQLITE_NOTADB/i.test(error.message))
|
|
94
|
-
return invalidArtifact('not a SQLite database.');
|
|
95
|
-
return error;
|
|
96
|
-
}
|
|
97
|
-
/** Private artifact-boundary connection shared with explicit migration. Never creates a path. */
|
|
98
|
-
export function openExistingSquareDatabase(location) {
|
|
99
|
-
requireSquareExtension(location);
|
|
100
|
-
const uri = pathToFileURL(path.resolve(location));
|
|
101
|
-
uri.searchParams.set('mode', 'rw');
|
|
102
|
-
try {
|
|
103
|
-
return new DatabaseSync(uri.href);
|
|
104
|
-
}
|
|
105
|
-
catch (error) {
|
|
106
|
-
throw storageError(error, location);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
function abort(signal) {
|
|
110
|
-
if (signal?.aborted)
|
|
111
|
-
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
112
|
-
}
|
|
113
|
-
function synchronous(value) {
|
|
114
|
-
if (value !== null && (typeof value === 'object' || typeof value === 'function') && typeof value.then === 'function') {
|
|
115
|
-
void Promise.resolve(value).catch(() => undefined);
|
|
116
|
-
throw new TypeError('Square artifact callbacks must be synchronous.');
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
82
|
const readMethods = [
|
|
120
83
|
'header', 'participant', 'roster', 'attention', 'incomingAttention',
|
|
121
84
|
'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
122
|
-
'audience', 'directed', 'directedAt', 'directedCount',
|
|
85
|
+
'audience', 'directed', 'directedAt', 'directedCount',
|
|
123
86
|
'receipt', 'receiptFacts', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
124
|
-
'
|
|
87
|
+
'pendingPosts', 'postedPosts', 'ownsPost',
|
|
125
88
|
];
|
|
126
|
-
const writeMethods = ['applyLanding', '
|
|
127
|
-
/**
|
|
89
|
+
const writeMethods = ['applyLanding', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'markSeen', 'setSeatAttachment', 'insertNotice', 'ackMatching', 'acquirePost', 'settlePost', 'foldReceipt'];
|
|
90
|
+
/** Square social state and producer state share one synchronous artifact transaction. */
|
|
128
91
|
export function createArtifactPort(location, initial, externalSignal) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (initial !== undefined) {
|
|
135
|
-
database = new DatabaseSync(':memory:');
|
|
136
|
-
try {
|
|
137
|
-
initializeSqlite(database, initial);
|
|
138
|
-
database.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
database.close();
|
|
142
|
-
throw error;
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
const combined = (signal) => AbortSignal.any([cancel.signal, ...(externalSignal ? [externalSignal] : []), ...(signal ? [signal] : [])]);
|
|
146
|
-
const validate = (db) => validateSqliteHeader(db, initial === undefined ? location : ':memory:');
|
|
147
|
-
async function connection(signal) {
|
|
148
|
-
if (database)
|
|
149
|
-
return database;
|
|
150
|
-
location = await canonical;
|
|
151
|
-
abort(signal);
|
|
152
|
-
database = openExistingSquareDatabase(location);
|
|
153
|
-
try {
|
|
154
|
-
await retrySqliteBusy(() => validate(database), signal);
|
|
155
|
-
database.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
|
|
156
|
-
}
|
|
157
|
-
catch (error) {
|
|
158
|
-
database.close();
|
|
159
|
-
database = undefined;
|
|
160
|
-
throw storageError(error, location);
|
|
161
|
-
}
|
|
162
|
-
return database;
|
|
163
|
-
}
|
|
164
|
-
function queue(operation, operationSignal) {
|
|
165
|
-
if (inCallback)
|
|
166
|
-
throw new TypeError('Square artifact operations cannot nest inside a callback.');
|
|
167
|
-
if (closed)
|
|
168
|
-
return Promise.reject(new Error('Square artifact is closed'));
|
|
169
|
-
const result = tail.then(() => {
|
|
170
|
-
abort(operationSignal);
|
|
171
|
-
return operation(operationSignal);
|
|
172
|
-
});
|
|
173
|
-
tail = result.then(() => undefined, () => undefined);
|
|
174
|
-
return result;
|
|
175
|
-
}
|
|
176
|
-
async function transaction(callback, write, signal, authority) {
|
|
177
|
-
const db = await connection(signal);
|
|
178
|
-
return inSqliteTransaction(db, write, () => validate(db), (header) => {
|
|
179
|
-
const driver = createSqliteDriver(db), version = header.revision;
|
|
180
|
-
let active = true, failed = false, failure;
|
|
181
|
-
function guard(fn) {
|
|
182
|
-
return ((...args) => {
|
|
183
|
-
if (!active)
|
|
184
|
-
throw new Error('Square artifact transaction capability expired');
|
|
185
|
-
abort(signal);
|
|
186
|
-
if (failed)
|
|
187
|
-
throw failure;
|
|
188
|
-
try {
|
|
189
|
-
return fn(...args);
|
|
190
|
-
}
|
|
191
|
-
catch (error) {
|
|
192
|
-
failed = true;
|
|
193
|
-
failure = storageError(error, location);
|
|
194
|
-
throw failure;
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
}
|
|
92
|
+
const store = createArtifactStore(location, format, initial === undefined ? undefined : db => initializeSqlite(db, initial, 0), externalSignal);
|
|
93
|
+
function transaction(select, write, authority, signal, settlement = false) {
|
|
94
|
+
return store.run(write, ({ db, guard, track }) => {
|
|
95
|
+
const driver = createSqliteDriver(db);
|
|
96
|
+
track(driver);
|
|
198
97
|
const reads = Object.fromEntries(readMethods.map(name => [name, guard(driver[name])]));
|
|
199
98
|
const writes = Object.fromEntries(writeMethods.map(name => [name, guard(driver[name])]));
|
|
200
99
|
const gate = write ? wrapSquareWrite(reads, writes, authority) : undefined;
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
active = false;
|
|
213
|
-
}
|
|
214
|
-
if (failed)
|
|
215
|
-
throw failure;
|
|
216
|
-
const changed = write && driver.changed;
|
|
217
|
-
const deliveryChanged = write && driver.deliveryChanged;
|
|
218
|
-
if (changed || deliveryChanged) {
|
|
219
|
-
if (version === Number.MAX_SAFE_INTEGER)
|
|
220
|
-
throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
|
|
221
|
-
db.prepare('UPDATE square_meta SET revision=revision+?, delivery_revision=delivery_revision+? WHERE id=1').run(Number(changed), Number(deliveryChanged));
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
value, version: version + Number(changed), changed, deliveryChanged
|
|
225
|
-
};
|
|
226
|
-
}, signal);
|
|
227
|
-
}
|
|
228
|
-
const revision = signal => queue(async (s) => {
|
|
229
|
-
const db = await connection(s);
|
|
230
|
-
return inSqliteTransaction(db, false, () => validate(db), (header) => header.revision, s);
|
|
231
|
-
}, combined(signal));
|
|
232
|
-
return {
|
|
233
|
-
read: (select, signal) => queue(async (s) => {
|
|
234
|
-
const { value, version } = await transaction(select, false, s);
|
|
235
|
-
return { value, version };
|
|
236
|
-
}, combined(signal)),
|
|
237
|
-
transactAs: (authority, change, signal) => queue(s => transaction(change, true, s, authority), combined(signal)),
|
|
238
|
-
// Keep the acquired connection and authority, not the cancelled work lifetime. A
|
|
239
|
-
// blocked evidence commit/cleanup gets five seconds, and close still cancels it.
|
|
240
|
-
settleAs: (authority, change) => queue(s => transaction(change, true, s, authority), AbortSignal.any([cancel.signal, AbortSignal.timeout(5000)])),
|
|
241
|
-
revision,
|
|
242
|
-
async deliveryRevision(signal) {
|
|
243
|
-
return queue(async (s) => {
|
|
244
|
-
const db = await connection(s);
|
|
245
|
-
return inSqliteTransaction(db, false, () => validate(db), (header) => header.deliveryRevision, s);
|
|
246
|
-
}, combined(signal));
|
|
247
|
-
},
|
|
248
|
-
async claimDelivery(input, signal) {
|
|
249
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, (facts) => facts.claimDelivery(input), signal)).value;
|
|
250
|
-
},
|
|
251
|
-
async prepareDelivery(input, signal) {
|
|
252
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.prepareDelivery(input), signal)).value;
|
|
253
|
-
},
|
|
254
|
-
async completeDelivery(input, signal) {
|
|
255
|
-
return (await this.transactAs({ kind: 'participant', seat: input.seat }, facts => facts.completeDelivery(input), signal)).value;
|
|
256
|
-
},
|
|
257
|
-
async cancelDeliveries(seat, through, signal) {
|
|
258
|
-
return (await this.transactAs({ kind: 'participant', seat }, facts => {
|
|
259
|
-
const boundary = through ?? facts.header().lastActIndex;
|
|
260
|
-
return boundary < 0 ? false : facts.cancelThrough(seat, boundary);
|
|
261
|
-
}, signal)).value;
|
|
262
|
-
},
|
|
263
|
-
async changed(since, timeout, signal) {
|
|
264
|
-
if (inCallback)
|
|
265
|
-
throw new TypeError('Square artifact operations cannot nest inside a callback.');
|
|
266
|
-
if (closed)
|
|
267
|
-
return false;
|
|
268
|
-
const s = combined(signal), deadline = Date.now() + Math.max(0, timeout);
|
|
269
|
-
if (initial !== undefined) {
|
|
270
|
-
try {
|
|
271
|
-
do {
|
|
272
|
-
if (await revision(s) > since)
|
|
273
|
-
return true;
|
|
274
|
-
if (Date.now() >= deadline)
|
|
275
|
-
return false;
|
|
276
|
-
await sleep(Math.min(25, deadline - Date.now()), undefined, { signal: s });
|
|
277
|
-
} while (!closed);
|
|
278
|
-
}
|
|
279
|
-
catch (error) {
|
|
280
|
-
if (closed)
|
|
281
|
-
return false;
|
|
282
|
-
abort(s);
|
|
283
|
-
throw error;
|
|
284
|
-
}
|
|
285
|
-
return false;
|
|
286
|
-
}
|
|
287
|
-
const observer = await observeSquareRevision(await canonical);
|
|
288
|
-
try {
|
|
289
|
-
return await observer.changed(since, Math.max(0, deadline - Date.now()), s);
|
|
290
|
-
}
|
|
291
|
-
catch (error) {
|
|
292
|
-
if (closed)
|
|
293
|
-
return false;
|
|
294
|
-
throw error;
|
|
295
|
-
}
|
|
296
|
-
finally {
|
|
297
|
-
observer.close();
|
|
298
|
-
}
|
|
299
|
-
},
|
|
300
|
-
async close() {
|
|
301
|
-
if (inCallback)
|
|
302
|
-
throw new TypeError('Square artifact cannot close inside a callback.');
|
|
303
|
-
if (closed) {
|
|
304
|
-
await tail;
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
closed = true;
|
|
308
|
-
cancel.abort(new Error('Square artifact is closed'));
|
|
309
|
-
await tail;
|
|
310
|
-
database?.close();
|
|
311
|
-
database = undefined;
|
|
312
|
-
},
|
|
100
|
+
const value = select(gate?.facts ?? reads);
|
|
101
|
+
gate?.complete();
|
|
102
|
+
return value;
|
|
103
|
+
}, signal, settlement);
|
|
104
|
+
}
|
|
105
|
+
const port = {
|
|
106
|
+
read: (select, signal) => transaction(select, false, undefined, signal),
|
|
107
|
+
transactAs: (authority, change, signal) => transaction(change, true, authority, signal),
|
|
108
|
+
settleAs: (authority, change) => transaction(change, true, authority, undefined, true),
|
|
109
|
+
revision: store.revision, deliveryRevision: store.deliveryRevision,
|
|
110
|
+
changed: store.changed, close: store.close,
|
|
313
111
|
};
|
|
112
|
+
return port;
|
|
314
113
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
const temporary = path.join(path.dirname(location), `.${path.basename(location)}.${process.pid}.${randomUUID()}.tmp`);
|
|
318
|
-
const handle = await fs.promises.open(temporary, 'wx');
|
|
319
|
-
await handle.close();
|
|
320
|
-
return temporary;
|
|
114
|
+
export function createSquareFile(location, state, signal) {
|
|
115
|
+
return createStoreFile(location, format, db => initializeSqlite(db, state, 0), signal);
|
|
321
116
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
try {
|
|
325
|
-
db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL');
|
|
326
|
-
await inSqliteTransaction(db, true, () => { }, () => {
|
|
327
|
-
initializeSqlite(db, state);
|
|
328
|
-
db.exec(`PRAGMA application_id=${APPLICATION_ID}; PRAGMA user_version=${CURRENT_USER_VERSION}`);
|
|
329
|
-
}, signal);
|
|
330
|
-
}
|
|
331
|
-
finally {
|
|
332
|
-
db.close();
|
|
333
|
-
}
|
|
334
|
-
const handle = await fs.promises.open(location, 'r+');
|
|
335
|
-
try {
|
|
336
|
-
await handle.sync();
|
|
337
|
-
}
|
|
338
|
-
finally {
|
|
339
|
-
await handle.close();
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
/** New-format creation; initial authority is accepted only at this explicit boundary. */
|
|
343
|
-
export async function createSquareFile(location, state, signal) {
|
|
344
|
-
abort(signal);
|
|
345
|
-
location = await canonicalPath(location);
|
|
346
|
-
requireSquareExtension(location);
|
|
347
|
-
const temporary = await temporaryArtifact(location);
|
|
348
|
-
try {
|
|
349
|
-
await initializeFile(temporary, state, signal);
|
|
350
|
-
abort(signal);
|
|
351
|
-
try {
|
|
352
|
-
await fs.promises.link(temporary, location);
|
|
353
|
-
return true;
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
if (error.code === 'EEXIST')
|
|
357
|
-
return false;
|
|
358
|
-
throw error;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
finally {
|
|
362
|
-
await fs.promises.unlink(temporary).catch(() => undefined);
|
|
363
|
-
}
|
|
117
|
+
export function writeSquareFile(location, state, signal) {
|
|
118
|
+
return replaceStoreFile(location, format, (db, revision) => initializeSqlite(db, state, revision), signal);
|
|
364
119
|
}
|
|
365
|
-
/** Explicit build --force / fixture import, never a runtime state-replacement callback. */
|
|
366
|
-
export async function writeSquareFile(location, state, signal) {
|
|
367
|
-
location = await canonicalPath(location);
|
|
368
|
-
if (await createSquareFile(location, state, signal))
|
|
369
|
-
return;
|
|
370
|
-
abort(signal);
|
|
371
|
-
const db = openExistingSquareDatabase(location);
|
|
372
|
-
try {
|
|
373
|
-
await retrySqliteBusy(() => validateSqliteHeader(db, location), signal);
|
|
374
|
-
db.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
|
|
375
|
-
await inSqliteTransaction(db, true, () => validateSqliteHeader(db, location), () => {
|
|
376
|
-
const revision = createSqliteDriver(db).header().revision;
|
|
377
|
-
if (revision === Number.MAX_SAFE_INTEGER)
|
|
378
|
-
throw invalidArtifact('revision cannot advance beyond the safe integer limit.');
|
|
379
|
-
for (const table of ['landed_audience', 'attention', 'participants', 'delivery_deferrals', 'delivery_cancellations', 'delivery_evidence', 'seats', 'catch_leases', 'receipts', 'activities', 'square_meta'])
|
|
380
|
-
db.exec(`DROP TABLE ${table}`);
|
|
381
|
-
initializeSqlite(db, state, revision + 1);
|
|
382
|
-
}, signal);
|
|
383
|
-
}
|
|
384
|
-
finally {
|
|
385
|
-
db.close();
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
async function inspectFile(location, select, signal) {
|
|
389
|
-
const db = openExistingSquareDatabase(location);
|
|
390
|
-
try {
|
|
391
|
-
return await inSqliteTransaction(db, false, () => validateSqliteHeader(db, location), () => select(db), signal);
|
|
392
|
-
}
|
|
393
|
-
catch (error) {
|
|
394
|
-
throw storageError(error, location);
|
|
395
|
-
}
|
|
396
|
-
finally {
|
|
397
|
-
db.close();
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
/** Explicit test/export projection. Hot callers must use the typed port. */
|
|
401
120
|
export function readSquareSnapshot(location, signal) {
|
|
402
|
-
return
|
|
121
|
+
return inspectStore(location, format, db => ({ state: exportSqliteState(db), revision: createSqliteDriver(db).header().revision }), signal);
|
|
403
122
|
}
|
|
404
123
|
export function readSquareRevision(location, signal) {
|
|
405
|
-
return
|
|
124
|
+
return inspectStore(location, format, db => createSqliteDriver(db).header().revision, signal);
|
|
406
125
|
}
|
|
407
126
|
export function readSquareDeliveryRevision(location, signal) {
|
|
408
|
-
return
|
|
409
|
-
}
|
|
410
|
-
function observeRevision(location, revision) {
|
|
411
|
-
const read = revision === 'square-revision' ? readSquareRevision : readSquareDeliveryRevision;
|
|
412
|
-
return observeFileVersion(location, revision, async (file, signal) => {
|
|
413
|
-
try {
|
|
414
|
-
return await read(file, signal);
|
|
415
|
-
}
|
|
416
|
-
catch (error) {
|
|
417
|
-
abort(signal);
|
|
418
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
419
|
-
return null;
|
|
420
|
-
throw error;
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
export function observeSquareRevision(location) {
|
|
425
|
-
return observeRevision(location, 'square-revision');
|
|
426
|
-
}
|
|
427
|
-
export function observeSquareDeliveryRevision(location) {
|
|
428
|
-
return observeRevision(location, 'square-delivery-revision');
|
|
127
|
+
return inspectStore(location, format, db => createSqliteDriver(db).header().deliveryRevision, signal);
|
|
429
128
|
}
|
|
129
|
+
export const observeSquareRevision = (location) => observeStoreRevision(location, format);
|
|
130
|
+
export const observeSquareDeliveryRevision = (location) => observeStoreRevision(location, format, true);
|
|
430
131
|
export async function diagnoseSquareFile(location) {
|
|
431
132
|
try {
|
|
432
|
-
return { problems: [], ...await
|
|
133
|
+
return { problems: [], ...await inspectStore(await canonicalPath(location), format, db => inspectSqlite(db)) };
|
|
433
134
|
}
|
|
434
135
|
catch (error) {
|
|
435
|
-
if (error instanceof SquareError && error.facts?.reason === 'migration_required')
|
|
436
|
-
return { migrationRequired: error.message, problems: [] };
|
|
437
136
|
return { unfixable: error instanceof Error ? error.message : String(error), problems: [] };
|
|
438
137
|
}
|
|
439
138
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DirectedNotificationRoute } from './model.js';
|
|
2
2
|
export declare const ATTENTION_BODY_MAX = 200;
|
|
3
3
|
export interface AttentionPreview {
|
|
4
4
|
squarePath: string;
|
|
@@ -7,14 +7,20 @@ export interface AttentionPreview {
|
|
|
7
7
|
actor: string;
|
|
8
8
|
route: DirectedNotificationRoute;
|
|
9
9
|
body: string;
|
|
10
|
-
|
|
10
|
+
metadataOnly?: boolean;
|
|
11
11
|
}
|
|
12
|
-
export declare function previewAttentionBody(body: string
|
|
13
|
-
export declare function attentionBodyIsClipped(body: string
|
|
12
|
+
export declare function previewAttentionBody(body: string): string;
|
|
13
|
+
export declare function attentionBodyIsClipped(body: string): boolean;
|
|
14
14
|
export declare function displayAttentionPath(squarePath: string): string;
|
|
15
15
|
export declare function renderAttentionDescription({ actor, route }: Pick<AttentionPreview, 'actor' | 'route'>): string;
|
|
16
|
-
/**
|
|
16
|
+
/** The sole source adapter: transports and cards consume this projection, not delivery modes. */
|
|
17
|
+
export declare function projectNotification(attention: AttentionPreview): {
|
|
18
|
+
text: string;
|
|
19
|
+
full: string;
|
|
20
|
+
preview: string;
|
|
21
|
+
description: string;
|
|
22
|
+
isFull: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
17
25
|
export declare function renderAttentionFull(attention: AttentionPreview): string;
|
|
18
|
-
/** Shared model-facing envelope; transport correlation and human cards stay outside it. */
|
|
19
|
-
export declare function renderSquareReminder(previews: readonly string[], deliveries?: readonly (ActivityDelivery | undefined)[]): string;
|
|
20
26
|
export declare function renderAttentionPreview(attention: AttentionPreview): string;
|
|
@@ -1,21 +1,15 @@
|
|
|
1
1
|
import { homedir } from 'node:os';
|
|
2
2
|
import { formatActivityId } from './square-core.js';
|
|
3
3
|
export const ATTENTION_BODY_MAX = 200;
|
|
4
|
-
export function previewAttentionBody(body
|
|
5
|
-
const compact =
|
|
6
|
-
|
|
7
|
-
return compact;
|
|
8
|
-
return `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
|
|
4
|
+
export function previewAttentionBody(body) {
|
|
5
|
+
const compact = body.replace(/\r\n/g, '\n');
|
|
6
|
+
return compact.length <= ATTENTION_BODY_MAX ? compact : `${compact.slice(0, ATTENTION_BODY_MAX).trimEnd()}…`;
|
|
9
7
|
}
|
|
10
|
-
export function attentionBodyIsClipped(body
|
|
11
|
-
return
|
|
12
|
-
? delivery.preview !== undefined
|
|
13
|
-
: body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
|
|
8
|
+
export function attentionBodyIsClipped(body) {
|
|
9
|
+
return body.replace(/\r\n/g, '\n').length > ATTENTION_BODY_MAX;
|
|
14
10
|
}
|
|
15
11
|
export function displayAttentionPath(squarePath) {
|
|
16
|
-
return squarePath.startsWith(homedir())
|
|
17
|
-
? `~${squarePath.slice(homedir().length)}`
|
|
18
|
-
: squarePath;
|
|
12
|
+
return squarePath.startsWith(homedir()) ? `~${squarePath.slice(homedir().length)}` : squarePath;
|
|
19
13
|
}
|
|
20
14
|
export function renderAttentionDescription({ actor, route }) {
|
|
21
15
|
switch (route) {
|
|
@@ -25,56 +19,19 @@ export function renderAttentionDescription({ actor, route }) {
|
|
|
25
19
|
case 'bell': return `${actor} rang the bell`;
|
|
26
20
|
}
|
|
27
21
|
}
|
|
28
|
-
/** Full notification text for human-facing expansion, never the bounded model preview. */
|
|
29
|
-
export function renderAttentionFull(attention) {
|
|
30
|
-
return attention.delivery?.mode === 'direct' ? renderDirectBlock(attention) : renderAttentionBlock(attention, attention.body);
|
|
31
|
-
}
|
|
32
|
-
/** Attribute values keep their quotes; the stored body and the human card stay unescaped. */
|
|
33
|
-
function escapeAttribute(value) {
|
|
34
|
-
return value
|
|
35
|
-
.replace(/&/g, '&')
|
|
36
|
-
.replace(/"/g, '"')
|
|
37
|
-
.replace(/</g, '<')
|
|
38
|
-
.replace(/>/g, '>');
|
|
39
|
-
}
|
|
40
|
-
/** A body may quote the envelope's own tags; in the preview they must never read as one. */
|
|
41
22
|
function escapeSquareTags(text) {
|
|
42
|
-
return text.replace(/<(\/?)(square-|system-reminder)/gi, '<\\$1$2');
|
|
43
|
-
}
|
|
44
|
-
function renderAttentionBlock(attention, body) {
|
|
45
|
-
return [
|
|
46
|
-
'<square-activity>',
|
|
47
|
-
`· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
48
|
-
`● ${renderAttentionDescription(attention)}`,
|
|
49
|
-
'',
|
|
50
|
-
body,
|
|
51
|
-
'</square-activity>',
|
|
52
|
-
].join('\n');
|
|
23
|
+
return text.replace(/<(\/?)(square-|channel-|system-reminder)/gi, '<\\$1$2');
|
|
53
24
|
}
|
|
54
|
-
function
|
|
55
|
-
return
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
export function
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return [
|
|
65
|
-
'',
|
|
66
|
-
'<system-reminder>',
|
|
67
|
-
header,
|
|
68
|
-
'',
|
|
69
|
-
previews.join('\n\n'),
|
|
70
|
-
'</system-reminder>',
|
|
71
|
-
'',
|
|
72
|
-
].join('\n');
|
|
73
|
-
}
|
|
74
|
-
export function renderAttentionPreview(attention) {
|
|
75
|
-
if (attention.delivery?.mode === 'direct')
|
|
76
|
-
return renderDirectBlock(attention);
|
|
77
|
-
const remaining = attention.body.replace(/\r\n/g, '\n').length - ATTENTION_BODY_MAX;
|
|
25
|
+
function escapeAttribute(value) {
|
|
26
|
+
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/</g, '<').replace(/>/g, '>');
|
|
27
|
+
}
|
|
28
|
+
/** The sole source adapter: transports and cards consume this projection, not delivery modes. */
|
|
29
|
+
export function projectNotification(attention) {
|
|
30
|
+
const body = attention.metadataOnly ? '(structured activity)' : attention.body.replace(/\r\n/g, '\n');
|
|
31
|
+
const description = renderAttentionDescription(attention);
|
|
32
|
+
const remaining = body.length - ATTENTION_BODY_MAX;
|
|
33
|
+
const isFull = !attention.metadataOnly && remaining <= 0;
|
|
34
|
+
const preview = previewAttentionBody(body);
|
|
78
35
|
const attributes = [
|
|
79
36
|
`square="${escapeAttribute(displayAttentionPath(attention.squarePath))}"`,
|
|
80
37
|
`as="${escapeAttribute(attention.recipient)}"`,
|
|
@@ -82,11 +39,15 @@ export function renderAttentionPreview(attention) {
|
|
|
82
39
|
`from="${escapeAttribute(attention.actor)}"`,
|
|
83
40
|
...(remaining > 0 ? [`truncated="${remaining} more chars"`] : []),
|
|
84
41
|
];
|
|
85
|
-
return
|
|
86
|
-
`<square-activity ${attributes.join(' ')}>`,
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
42
|
+
return {
|
|
43
|
+
text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview),
|
|
44
|
+
...(!isFull ? ['', escapeSquareTags(`Use square_catch with location=${JSON.stringify(attention.squarePath)}, as=${JSON.stringify(attention.recipient)}, id=${JSON.stringify(formatActivityId(attention.actIndex))} for the full activity.`)] : []),
|
|
45
|
+
'</square-activity>'].join('\n'),
|
|
46
|
+
full: ['<square-activity>', `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
47
|
+
`● ${description}`, '', body, '</square-activity>'].join('\n'),
|
|
48
|
+
preview, description, isFull,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** Full human expansion never substitutes a sender-owned preview for the stored body. */
|
|
52
|
+
export function renderAttentionFull(attention) { return projectNotification(attention).full; }
|
|
53
|
+
export function renderAttentionPreview(attention) { return projectNotification(attention).text; }
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
|
|
5
|
-
/**
|
|
6
|
-
* Serialize a boundary across hook processes so one stale inbox cannot render twice.
|
|
7
|
-
*
|
|
8
|
-
* `lookup` returns the session's memberships for this boundary. It may reuse an observation it has
|
|
9
|
-
* verified against the current presence binding and artifact revision; the boundary still projects
|
|
10
|
-
* presentation evidence live. Every item is claimed and revalidated before the batch sink;
|
|
11
|
-
* after success, captured-seat commits record the winning batch, including budget omissions as
|
|
12
|
-
* clipped (no consumption receipt), without an eligibility veto. Coverage is conservative if a
|
|
13
|
-
* competing claim's removal lets a previously omitted body fit in the final preview.
|
|
14
|
-
* A source that cannot verify its reuse must read the inbox again.
|
|
15
|
-
*/
|
|
16
|
-
export declare function presentPendingAtBoundary<T>(identity: Owner, present: (context: string, delivered: PendingMembership[]) => T | Promise<T>, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
1
|
+
import type { Owner } from './owner.js';
|
|
2
|
+
/** The sink must resolve only after the host has actually accepted the output. */
|
|
3
|
+
export declare function presentPendingAtBoundary<T>(owner: Owner, present: (context: string) => T | Promise<T>, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|