@astrosheep/square 0.4.4 → 0.4.5
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-store.d.ts +1 -1
- package/dist/artifact-store.js +32 -15
- package/dist/artifact.d.ts +1 -4
- package/dist/artifact.js +0 -28
- package/dist/attention-presentation.js +1 -1
- package/dist/boundary-presentation.js +2 -2
- package/dist/claude-boundary-state.d.ts +6 -6
- package/dist/claude-boundary-state.js +13 -13
- package/dist/claude-hook.js +25 -24
- package/dist/claude-mcp-context.d.ts +2 -2
- package/dist/claude-mcp-context.js +6 -6
- package/dist/cli/context.js +3 -6
- package/dist/cli/json.js +1 -1
- package/dist/cli/mcp-server-command.js +3 -3
- package/dist/cli/observation-commands.d.ts +2 -2
- package/dist/cli/observation-commands.js +23 -51
- package/dist/cli/perceive-presentation.d.ts +3 -0
- package/dist/cli/{catch-presentation.js → perceive-presentation.js} +3 -3
- package/dist/cli/registry.js +2 -2
- package/dist/codex-address.d.ts +3 -0
- package/dist/codex-address.js +7 -0
- package/dist/codex-hook.d.ts +1 -1
- package/dist/codex-hook.js +11 -8
- package/dist/codex-queue.d.ts +3 -2
- package/dist/codex-queue.js +8 -11
- package/dist/codex-watch.d.ts +6 -1
- package/dist/codex-watch.js +27 -29
- package/dist/delivery-ledger-sqlite.d.ts +25 -20
- package/dist/delivery-ledger-sqlite.js +76 -61
- package/dist/delivery-ledger.d.ts +24 -19
- package/dist/delivery-ledger.js +150 -81
- package/dist/delivery-lifetime.d.ts +2 -2
- package/dist/delivery-lifetime.js +6 -18
- package/dist/help.js +12 -12
- package/dist/hints.d.ts +2 -2
- package/dist/hints.js +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list.d.ts +2 -2
- package/dist/list.js +4 -3
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +8 -32
- package/dist/neutral.d.ts +5 -5
- package/dist/notifications.js +12 -6
- package/dist/operation-presentation.d.ts +8 -3
- package/dist/operation-presentation.js +50 -12
- package/dist/packages/agent-delivery/src/file-lock.js +4 -1
- package/dist/perceive-decisions.d.ts +28 -0
- package/dist/{catch-decisions.js → perceive-decisions.js} +24 -24
- package/dist/{catch-selection.d.ts → perceive-selection.d.ts} +2 -2
- package/dist/{catch-selection.js → perceive-selection.js} +3 -3
- package/dist/plugins/opencode/index.js +4 -4
- package/dist/plugins/opencode-shared/delivery.d.ts +6 -6
- package/dist/plugins/opencode-shared/delivery.js +27 -21
- package/dist/plugins/opencode-v1/runtime.js +3 -3
- package/dist/ports.d.ts +0 -4
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +13 -13
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +11 -11
- package/dist/sqlite-square.d.ts +2 -4
- package/dist/sqlite-square.js +47 -54
- package/dist/sqlite-transaction.d.ts +2 -0
- package/dist/sqlite-transaction.js +25 -3
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-domain.js +1 -1
- package/dist/square-facade.d.ts +15 -13
- package/dist/square-location.js +1 -1
- package/dist/square-posting.d.ts +5 -5
- package/dist/square-posting.js +39 -38
- package/dist/square-tools.d.ts +15 -4
- package/dist/square-tools.js +35 -17
- package/dist/views.d.ts +7 -3
- package/dist/views.js +6 -3
- package/guides/architect.md +2 -2
- package/guides/change-waits.md +1 -1
- package/guides/codex-delivery.md +40 -35
- package/guides/delivery-primitives.md +22 -7
- package/guides/mcp.md +4 -2
- package/guides/operation-cancellation.md +11 -3
- package/guides/pi-native-delivery.md +1 -1
- package/guides/pi-tools.md +11 -6
- package/guides/receive-primitive.md +3 -3
- package/guides/square-api.md +15 -8
- package/package.json +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/skills/square/SKILL.md +10 -10
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/codex/hooks/hooks.json +3 -0
- package/plugins/opencode/index.ts +4 -4
- package/plugins/opencode-shared/delivery.ts +27 -21
- package/plugins/opencode-v1/runtime.ts +3 -3
- package/plugins/pi/square-pi.js +18 -12
- package/skills/square/SKILL.md +10 -10
- package/dist/catch-decisions.d.ts +0 -28
- package/dist/cli/catch-presentation.d.ts +0 -3
- package/guides/mcp-connection-lifetime.md +0 -22
package/dist/artifact-store.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface StoreTransaction {
|
|
|
19
19
|
export declare function requireSquareExtension(location: string): void;
|
|
20
20
|
export declare function storageError(error: unknown, location: string): unknown;
|
|
21
21
|
/** Private storage opening; never creates a missing file. */
|
|
22
|
-
export declare function openExistingSquareDatabase(location: string): DatabaseSync;
|
|
22
|
+
export declare function openExistingSquareDatabase(location: string, readOnly?: boolean): DatabaseSync;
|
|
23
23
|
export declare function abort(signal?: AbortSignal): void;
|
|
24
24
|
/** One ordered connection and transaction lifetime, shared by Channel and Agora compositions. */
|
|
25
25
|
export declare function createArtifactStore(location: string, format: StoreFormat, initialize?: StoreInitializer, externalSignal?: AbortSignal): {
|
package/dist/artifact-store.js
CHANGED
|
@@ -6,7 +6,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
6
6
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
7
7
|
import { canonicalPath } from './canonical-path.js';
|
|
8
8
|
import { observeFileVersion } from './file-changes.js';
|
|
9
|
-
import { retrySqliteBusy, inSqliteTransaction } from './sqlite-transaction.js';
|
|
9
|
+
import { retrySqliteBusy, inSqliteTransaction, awaitSqliteQueue } from './sqlite-transaction.js';
|
|
10
10
|
import { InternalSquareError, SquareError } from './neutral.js';
|
|
11
11
|
import { readStorageRevision } from './sqlite-framing.js';
|
|
12
12
|
import { invalidArtifact } from './sqlite-values.js';
|
|
@@ -24,12 +24,12 @@ export function storageError(error, location) {
|
|
|
24
24
|
return error;
|
|
25
25
|
}
|
|
26
26
|
/** Private storage opening; never creates a missing file. */
|
|
27
|
-
export function openExistingSquareDatabase(location) {
|
|
27
|
+
export function openExistingSquareDatabase(location, readOnly = false) {
|
|
28
28
|
requireSquareExtension(location);
|
|
29
29
|
const uri = pathToFileURL(path.resolve(location));
|
|
30
|
-
uri.searchParams.set('mode', 'rw');
|
|
30
|
+
uri.searchParams.set('mode', readOnly ? 'ro' : 'rw');
|
|
31
31
|
try {
|
|
32
|
-
return new DatabaseSync(uri.href);
|
|
32
|
+
return new DatabaseSync(uri.href, { readOnly });
|
|
33
33
|
}
|
|
34
34
|
catch (error) {
|
|
35
35
|
throw storageError(error, location);
|
|
@@ -69,10 +69,19 @@ export function createArtifactStore(location, format, initialize, externalSignal
|
|
|
69
69
|
return database;
|
|
70
70
|
location = await canonical;
|
|
71
71
|
abort(signal);
|
|
72
|
-
|
|
72
|
+
const probe = await retrySqliteBusy(() => openExistingSquareDatabase(location, true), signal);
|
|
73
73
|
try {
|
|
74
|
-
await retrySqliteBusy(() => validate(
|
|
75
|
-
|
|
74
|
+
await retrySqliteBusy(() => validate(probe), signal);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
throw storageError(error, location);
|
|
78
|
+
}
|
|
79
|
+
finally {
|
|
80
|
+
probe.close();
|
|
81
|
+
}
|
|
82
|
+
database = await retrySqliteBusy(() => openExistingSquareDatabase(location), signal);
|
|
83
|
+
try {
|
|
84
|
+
await retrySqliteBusy(() => { validate(database); database.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL'); }, signal);
|
|
76
85
|
}
|
|
77
86
|
catch (error) {
|
|
78
87
|
database.close();
|
|
@@ -86,9 +95,10 @@ export function createArtifactStore(location, format, initialize, externalSignal
|
|
|
86
95
|
throw new TypeError('Square artifact operations cannot nest inside a callback.');
|
|
87
96
|
if (closed)
|
|
88
97
|
return Promise.reject(new Error('Square artifact is closed'));
|
|
89
|
-
|
|
98
|
+
let started = false;
|
|
99
|
+
const result = tail.then(() => { abort(signal); started = true; return operation(signal); });
|
|
90
100
|
tail = result.then(() => undefined, () => undefined);
|
|
91
|
-
return result;
|
|
101
|
+
return awaitSqliteQueue(result, signal, () => started);
|
|
92
102
|
}
|
|
93
103
|
function transaction(write, callback, signal) {
|
|
94
104
|
return connection(signal).then(db => inSqliteTransaction(db, write, () => validate(db), version => {
|
|
@@ -193,7 +203,7 @@ export function createArtifactStore(location, format, initialize, externalSignal
|
|
|
193
203
|
};
|
|
194
204
|
}
|
|
195
205
|
export async function inspectStore(location, format, select, signal) {
|
|
196
|
-
const db = openExistingSquareDatabase(location);
|
|
206
|
+
const db = await retrySqliteBusy(() => openExistingSquareDatabase(location, true), signal);
|
|
197
207
|
return inSqliteTransaction(db, false, () => format.validate(db, location), () => select(db), signal)
|
|
198
208
|
.catch(error => { throw storageError(error, location); }).finally(() => db.close());
|
|
199
209
|
}
|
|
@@ -220,9 +230,9 @@ export async function createStoreFile(location, format, initialize, signal) {
|
|
|
220
230
|
const handle = await fs.promises.open(temporary, 'wx');
|
|
221
231
|
await handle.close();
|
|
222
232
|
try {
|
|
223
|
-
const db = new DatabaseSync(temporary);
|
|
233
|
+
const db = await retrySqliteBusy(() => new DatabaseSync(temporary), signal);
|
|
224
234
|
try {
|
|
225
|
-
db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL');
|
|
235
|
+
await retrySqliteBusy(() => db.exec('PRAGMA journal_mode=DELETE; PRAGMA synchronous=FULL'), signal);
|
|
226
236
|
await inSqliteTransaction(db, true, () => { }, () => { initialize(db); db.exec(`PRAGMA application_id=${format.applicationId}; PRAGMA user_version=${format.userVersion}`); }, signal);
|
|
227
237
|
}
|
|
228
238
|
finally {
|
|
@@ -254,10 +264,17 @@ export async function replaceStoreFile(location, format, initialize, signal) {
|
|
|
254
264
|
if (await createStoreFile(location, format, db => initialize(db, 0), signal))
|
|
255
265
|
return;
|
|
256
266
|
abort(signal);
|
|
257
|
-
|
|
267
|
+
location = await canonicalPath(location);
|
|
268
|
+
const probe = await retrySqliteBusy(() => openExistingSquareDatabase(location, true), signal);
|
|
269
|
+
try {
|
|
270
|
+
await retrySqliteBusy(() => format.validate(probe, location), signal);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
probe.close();
|
|
274
|
+
}
|
|
275
|
+
const db = await retrySqliteBusy(() => openExistingSquareDatabase(location), signal);
|
|
258
276
|
try {
|
|
259
|
-
await retrySqliteBusy(() => format.validate(db, location), signal);
|
|
260
|
-
db.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL');
|
|
277
|
+
await retrySqliteBusy(() => { format.validate(db, location); db.exec('PRAGMA busy_timeout=0; PRAGMA synchronous=FULL'); }, signal);
|
|
261
278
|
await inSqliteTransaction(db, true, () => format.validate(db, location), () => {
|
|
262
279
|
const revision = readStorageRevision(db);
|
|
263
280
|
if (revision === Number.MAX_SAFE_INTEGER)
|
package/dist/artifact.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { type BuildOptions } from './model.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { SquareArtifactPort } from './ports.js';
|
|
3
3
|
import { type SquareState } from './square-sqlite.js';
|
|
4
4
|
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
5
|
-
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
6
|
-
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
7
|
-
export declare function createSquareResourceOwner(): SquareResourceOwner;
|
|
8
5
|
/** All build paths share configuration validation and lazy guide preparation. */
|
|
9
6
|
export declare function prepareSquareBuild(options: Omit<BuildOptions, 'force'>, snippet: string, includeGuides?: boolean): SquareState;
|
|
10
7
|
/** File build owns publication and CLI conflict semantics, not host resources. */
|
package/dist/artifact.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import fs from 'node:fs';
|
|
3
2
|
import { canonicalPath } from './canonical-path.js';
|
|
4
3
|
import { InternalSquareError, SquareError } from './model.js';
|
|
@@ -8,33 +7,6 @@ import { resetStorageMeta } from './sqlite-framing.js';
|
|
|
8
7
|
import { createArtifactStore, createStoreFile, replaceStoreFile, inspectStore, observeStoreRevision } from './artifact-store.js';
|
|
9
8
|
export { openExistingSquareDatabase } from './artifact-store.js';
|
|
10
9
|
const format = squareStoreFormat(resetStorageMeta);
|
|
11
|
-
/** Explicit-lifetime holder of one reused artifact connection. Request cancellation
|
|
12
|
-
* never becomes the shared connection's lifetime; close drains the owner once. */
|
|
13
|
-
export function createSquareResourceOwner() {
|
|
14
|
-
let held;
|
|
15
|
-
let closed = false;
|
|
16
|
-
return {
|
|
17
|
-
artifact(location) {
|
|
18
|
-
if (closed)
|
|
19
|
-
throw new SquareError('unavailable', 'Square resource owner is closed');
|
|
20
|
-
const key = path.resolve(location);
|
|
21
|
-
if (held === undefined)
|
|
22
|
-
held = { location: key, artifact: createArtifactPort(key) };
|
|
23
|
-
if (held.location !== key)
|
|
24
|
-
throw new SquareError('unavailable', `Square resource owner already holds ${held.location}; it cannot also open ${key}`);
|
|
25
|
-
return held.artifact;
|
|
26
|
-
},
|
|
27
|
-
async close() {
|
|
28
|
-
if (closed)
|
|
29
|
-
return;
|
|
30
|
-
closed = true;
|
|
31
|
-
const artifact = held?.artifact;
|
|
32
|
-
held = undefined;
|
|
33
|
-
if (artifact !== undefined)
|
|
34
|
-
await artifact.close();
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
10
|
const guideContents = new Map();
|
|
39
11
|
function readGuide(name) {
|
|
40
12
|
if (!['participant', 'architect', 'brainstorm'].includes(name))
|
|
@@ -41,7 +41,7 @@ export function projectNotification(attention) {
|
|
|
41
41
|
];
|
|
42
42
|
return {
|
|
43
43
|
text: [`<square-activity ${attributes.join(' ')}>`, `${description}.`, '', escapeSquareTags(preview),
|
|
44
|
-
...(!isFull ? ['', escapeSquareTags(`Use
|
|
44
|
+
...(!isFull ? ['', escapeSquareTags(`Use square_perceive with location=${JSON.stringify(attention.squarePath)}, as=${JSON.stringify(attention.recipient)}, id=${JSON.stringify(formatActivityId(attention.actIndex))} for the full activity.`)] : []),
|
|
45
45
|
'</square-activity>'].join('\n'),
|
|
46
46
|
full: ['<square-activity>', `· ${displayAttentionPath(attention.squarePath)} · ${attention.recipient} · ${formatActivityId(attention.actIndex)}`,
|
|
47
47
|
`● ${description}`, '', body, '</square-activity>'].join('\n'),
|
|
@@ -3,9 +3,9 @@ import { deliveryFrameOverhead, renderDeliveryReminder } from './delivery-frame.
|
|
|
3
3
|
const CONTEXT_MAX = 1200;
|
|
4
4
|
/** The sink must resolve only after the host has actually accepted the output. */
|
|
5
5
|
export async function presentPendingAtBoundary(owner, present, env = process.env, signal) {
|
|
6
|
-
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY });
|
|
6
|
+
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY, signal });
|
|
7
7
|
try {
|
|
8
|
-
const session = await ledger.session(owner);
|
|
8
|
+
const session = await ledger.session(owner, signal);
|
|
9
9
|
if (!session || session.state !== 'attached' || signal?.aborted)
|
|
10
10
|
return undefined;
|
|
11
11
|
const claim = await ledger.claimPresentation(owner, { generation: session.generation, budgetChars: CONTEXT_MAX - deliveryFrameOverhead({ tagged: true }), signal });
|
|
@@ -9,12 +9,12 @@ declare const stateSchema: z.ZodObject<{
|
|
|
9
9
|
export type ClaudeBoundary = z.infer<typeof stateSchema>;
|
|
10
10
|
/** Receiver operational facts only: no cards, claims, or payload copies. */
|
|
11
11
|
export declare function claudeBoundaryStore(session: string, env: NodeJS.ProcessEnv): {
|
|
12
|
-
start(attach: () => Promise<string | false
|
|
13
|
-
end(): Promise<ClaudeBoundary | undefined>;
|
|
14
|
-
prompt(promptId: string | undefined): Promise<void>;
|
|
15
|
-
capture(promptId: string): Promise<ClaudeBoundary | undefined>;
|
|
16
|
-
current(captured: ClaudeBoundary): Promise<boolean>;
|
|
17
|
-
consume(captured: ClaudeBoundary): Promise<void>;
|
|
12
|
+
start(attach: () => Promise<string | false>, signal?: AbortSignal): Promise<void>;
|
|
13
|
+
end(signal?: AbortSignal): Promise<ClaudeBoundary | undefined>;
|
|
14
|
+
prompt(promptId: string | undefined, signal?: AbortSignal): Promise<void>;
|
|
15
|
+
capture(promptId: string, signal?: AbortSignal): Promise<ClaudeBoundary | undefined>;
|
|
16
|
+
current(captured: ClaudeBoundary, signal?: AbortSignal): Promise<boolean>;
|
|
17
|
+
consume(captured: ClaudeBoundary, signal?: AbortSignal): Promise<void>;
|
|
18
18
|
watch<T>(signal: AbortSignal, run: () => Promise<T>): Promise<T>;
|
|
19
19
|
};
|
|
20
20
|
export {};
|
|
@@ -13,10 +13,10 @@ export function claudeBoundaryStore(session, env) {
|
|
|
13
13
|
const root = path.join(path.dirname(deliveryLedgerLocation(env)), 'claude-hooks');
|
|
14
14
|
const key = createHash('sha256').update(session).digest('hex');
|
|
15
15
|
const file = path.join(root, `${key}.json`);
|
|
16
|
-
async function mutate(change) {
|
|
16
|
+
async function mutate(change, signal) {
|
|
17
17
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
18
18
|
await fs.chmod(root, 0o700);
|
|
19
|
-
return withFileLock(path.join(root, `${key}.lock`), { retryMs: 10, signal: AbortSignal.timeout(2500) }, async () => {
|
|
19
|
+
return withFileLock(path.join(root, `${key}.lock`), { retryMs: 10, signal: signal ?? AbortSignal.timeout(2500) }, async () => {
|
|
20
20
|
let state;
|
|
21
21
|
try {
|
|
22
22
|
state = stateSchema.parse(JSON.parse(await fs.readFile(file, 'utf8')));
|
|
@@ -40,37 +40,37 @@ export function claudeBoundaryStore(session, env) {
|
|
|
40
40
|
const matches = (state, captured) => !!state && !state.ended && !state.consumed
|
|
41
41
|
&& state.generation === captured.generation && state.promptId === captured.promptId && state.stopEpoch === captured.stopEpoch;
|
|
42
42
|
return {
|
|
43
|
-
async start(attach) {
|
|
43
|
+
async start(attach, signal) {
|
|
44
44
|
await mutate(async (state, save) => {
|
|
45
45
|
if (state)
|
|
46
46
|
await save({ ...state, promptId: undefined, ended: true, stopEpoch: randomUUID() });
|
|
47
47
|
const generation = await attach();
|
|
48
48
|
if (generation)
|
|
49
49
|
await save({ generation, stopEpoch: randomUUID(), consumed: false, ended: false });
|
|
50
|
-
});
|
|
50
|
+
}, signal);
|
|
51
51
|
},
|
|
52
|
-
async end() {
|
|
52
|
+
async end(signal) {
|
|
53
53
|
return mutate(async (state, save) => {
|
|
54
54
|
if (state)
|
|
55
55
|
await save({ ...state, ended: true, stopEpoch: randomUUID() });
|
|
56
56
|
return state;
|
|
57
|
-
});
|
|
57
|
+
}, signal);
|
|
58
58
|
},
|
|
59
|
-
async prompt(promptId) {
|
|
59
|
+
async prompt(promptId, signal) {
|
|
60
60
|
await mutate(async (state, save) => {
|
|
61
61
|
if (state && !state.ended)
|
|
62
62
|
await save({ ...state, promptId, stopEpoch: randomUUID(), consumed: false });
|
|
63
|
-
});
|
|
63
|
+
}, signal);
|
|
64
64
|
},
|
|
65
|
-
capture(promptId) {
|
|
66
|
-
return mutate(async (state) => state && !state.ended && !state.consumed && state.promptId === promptId ? state : undefined);
|
|
65
|
+
capture(promptId, signal) {
|
|
66
|
+
return mutate(async (state) => state && !state.ended && !state.consumed && state.promptId === promptId ? state : undefined, signal);
|
|
67
67
|
},
|
|
68
|
-
current(captured) { return mutate(async (state) => matches(state, captured)); },
|
|
69
|
-
async consume(captured) {
|
|
68
|
+
current(captured, signal) { return mutate(async (state) => matches(state, captured), signal); },
|
|
69
|
+
async consume(captured, signal) {
|
|
70
70
|
await mutate(async (state, save) => {
|
|
71
71
|
if (matches(state, captured))
|
|
72
72
|
await save({ ...state, consumed: true });
|
|
73
|
-
});
|
|
73
|
+
}, signal);
|
|
74
74
|
},
|
|
75
75
|
async watch(signal, run) {
|
|
76
76
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
package/dist/claude-hook.js
CHANGED
|
@@ -53,43 +53,44 @@ export async function runClaudeHook(text, env = process.env, sink = value => { p
|
|
|
53
53
|
await denyMcpTool(sink);
|
|
54
54
|
return '';
|
|
55
55
|
}
|
|
56
|
+
const signal = AbortSignal.timeout(4500);
|
|
56
57
|
const boundaries = claudeBoundaryStore(input.session_id, env);
|
|
57
58
|
const mainOwner = ownerFor(input);
|
|
58
59
|
const owner = harnessOwner({ channel: 'claude-code', session: input.session_id, ...(input.agent_id === undefined ? {} : { agent: input.agent_id }) });
|
|
59
60
|
if (input.hook_event_name === 'SessionStart') {
|
|
60
61
|
await boundaries.start(async () => {
|
|
61
|
-
const session = await attachDeliverySessionRoutes(mainOwner, env, [{ kind: route, address: { sessionId: input.session_id } }],
|
|
62
|
+
const session = await attachDeliverySessionRoutes(mainOwner, env, [{ kind: route, address: { sessionId: input.session_id } }], signal);
|
|
62
63
|
if (!session)
|
|
63
64
|
return false;
|
|
64
|
-
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
|
|
65
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal });
|
|
65
66
|
try {
|
|
66
|
-
await ledger.updateRoutes({ owner: mainOwner, generation: session.generation, routes: session.routes.filter(item => item.kind !== 'claude-native') });
|
|
67
|
+
await ledger.updateRoutes({ owner: mainOwner, generation: session.generation, routes: session.routes.filter(item => item.kind !== 'claude-native') }, signal);
|
|
67
68
|
}
|
|
68
69
|
finally {
|
|
69
70
|
await ledger.close();
|
|
70
71
|
}
|
|
71
72
|
return session.generation;
|
|
72
|
-
});
|
|
73
|
+
}, signal);
|
|
73
74
|
return '';
|
|
74
75
|
}
|
|
75
76
|
if (input.hook_event_name === 'SessionEnd') {
|
|
76
|
-
const captured = await boundaries.end();
|
|
77
|
+
const captured = await boundaries.end(signal);
|
|
77
78
|
if (captured)
|
|
78
|
-
await detachDeliverySession(mainOwner, captured.generation, env);
|
|
79
|
+
await detachDeliverySession(mainOwner, captured.generation, env, signal);
|
|
79
80
|
return '';
|
|
80
81
|
}
|
|
81
82
|
if (input.hook_event_name === 'UserPromptSubmit') {
|
|
82
|
-
await boundaries.prompt(input.prompt_id);
|
|
83
|
+
await boundaries.prompt(input.prompt_id, signal);
|
|
83
84
|
if (!input.prompt_id)
|
|
84
85
|
return '';
|
|
85
86
|
if (input.prompt) {
|
|
86
|
-
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
|
|
87
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal });
|
|
87
88
|
try {
|
|
88
89
|
for (const frame of observedFrames(input.prompt)) {
|
|
89
|
-
const handoff = await ledger.handoff(frame.token);
|
|
90
|
+
const handoff = await ledger.handoff(frame.token, signal);
|
|
90
91
|
if (!handoff || handoff.owner !== owner || handoff.routeKind !== route || handoff.payloadHash !== frame.payloadHash)
|
|
91
92
|
continue;
|
|
92
|
-
await ledger.observePresentation(handoff);
|
|
93
|
+
await ledger.observePresentation(handoff, undefined, signal);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
finally {
|
|
@@ -98,7 +99,7 @@ export async function runClaudeHook(text, env = process.env, sink = value => { p
|
|
|
98
99
|
}
|
|
99
100
|
await presentPendingAtBoundary(owner, async (context) => {
|
|
100
101
|
await sink(`${JSON.stringify({ hookSpecificOutput: { hookEventName: 'UserPromptSubmit', additionalContext: context } })}\n`);
|
|
101
|
-
}, env,
|
|
102
|
+
}, env, signal);
|
|
102
103
|
return '';
|
|
103
104
|
}
|
|
104
105
|
if (input.hook_event_name === 'PreToolUse' && input.tool_name?.startsWith('mcp__plugin_square_square__')) {
|
|
@@ -107,7 +108,7 @@ export async function runClaudeHook(text, env = process.env, sink = value => { p
|
|
|
107
108
|
return '';
|
|
108
109
|
}
|
|
109
110
|
try {
|
|
110
|
-
await beginClaudeMcpCall({ toolUseId: input.tool_use_id, sessionId: input.session_id, cwd: input.cwd, agentId: input.agent_id }, env);
|
|
111
|
+
await beginClaudeMcpCall({ toolUseId: input.tool_use_id, sessionId: input.session_id, cwd: input.cwd, agentId: input.agent_id }, env, signal);
|
|
111
112
|
}
|
|
112
113
|
catch {
|
|
113
114
|
await denyMcpTool(sink);
|
|
@@ -116,12 +117,12 @@ export async function runClaudeHook(text, env = process.env, sink = value => { p
|
|
|
116
117
|
}
|
|
117
118
|
if ((input.hook_event_name === 'PostToolUse' || input.hook_event_name === 'PostToolUseFailure')
|
|
118
119
|
&& input.tool_name?.startsWith('mcp__plugin_square_square__') && input.tool_use_id) {
|
|
119
|
-
await endClaudeMcpCallForSession(input.tool_use_id, input.session_id, env, input.agent_id).catch(() => undefined);
|
|
120
|
+
await endClaudeMcpCallForSession(input.tool_use_id, input.session_id, env, input.agent_id, signal).catch(() => undefined);
|
|
120
121
|
}
|
|
121
122
|
if (input.hook_event_name === 'PostToolUse') {
|
|
122
123
|
await presentPendingAtBoundary(owner, async (context) => {
|
|
123
124
|
await sink(`${JSON.stringify({ hookSpecificOutput: { hookEventName: input.hook_event_name, additionalContext: context } })}\n`);
|
|
124
|
-
}, env,
|
|
125
|
+
}, env, signal);
|
|
125
126
|
}
|
|
126
127
|
return '';
|
|
127
128
|
}
|
|
@@ -151,24 +152,24 @@ export async function runClaudeWatch(text, env = process.env, signal, output = f
|
|
|
151
152
|
process.once('SIGINT', stop);
|
|
152
153
|
const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
153
154
|
try {
|
|
154
|
-
const captured = await boundaries.capture(input.prompt_id);
|
|
155
|
+
const captured = await boundaries.capture(input.prompt_id, combined);
|
|
155
156
|
if (!captured)
|
|
156
157
|
return '';
|
|
157
158
|
await boundaries.watch(combined, async () => {
|
|
158
|
-
if (!await boundaries.current(captured))
|
|
159
|
+
if (!await boundaries.current(captured, combined))
|
|
159
160
|
return;
|
|
160
|
-
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
|
|
161
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal: combined });
|
|
161
162
|
const lifetime = retainDeliveryLedger(ledger);
|
|
162
163
|
const observer = await ledger.observe(owner);
|
|
163
164
|
try {
|
|
164
165
|
while (!combined.aborted) {
|
|
165
166
|
let version = await observer.read(combined);
|
|
166
|
-
const live = await boundaries.current(captured);
|
|
167
|
-
const session = await ledger.session(owner);
|
|
167
|
+
const live = await boundaries.current(captured, combined);
|
|
168
|
+
const session = await ledger.session(owner, combined);
|
|
168
169
|
if (!live || session?.state !== 'attached' || session.generation !== captured.generation || !session.routes.some(item => item.kind === route))
|
|
169
170
|
return;
|
|
170
|
-
const cards = await ledger.pending(owner, { generation: captured.generation });
|
|
171
|
-
const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }))
|
|
171
|
+
const cards = await ledger.pending(owner, { generation: captured.generation }, combined);
|
|
172
|
+
const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }, combined))
|
|
172
173
|
.filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
|
|
173
174
|
for (const card of cards) {
|
|
174
175
|
if (held.has(card.seq))
|
|
@@ -182,18 +183,18 @@ export async function runClaudeWatch(text, env = process.env, signal, output = f
|
|
|
182
183
|
const payloadHash = createHash('sha256').update(frame).digest('hex');
|
|
183
184
|
if (!await context.prepare({ token: context.token, messageId: context.token, payloadHash }))
|
|
184
185
|
return 'not-capable';
|
|
185
|
-
const currentSession = await ledger.session(owner);
|
|
186
|
+
const currentSession = await ledger.session(owner, context.signal);
|
|
186
187
|
if (currentSession?.state !== 'attached' || currentSession.generation !== captured.generation
|
|
187
188
|
|| !currentSession.routes.some(item => item.kind === route) || !await context.beforeSend())
|
|
188
189
|
return 'not-capable';
|
|
189
|
-
const native = await boundaries.current(captured);
|
|
190
|
+
const native = await boundaries.current(captured, context.signal);
|
|
190
191
|
if (!native || context.signal.aborted || Date.now() >= context.deadline)
|
|
191
192
|
return 'not-capable';
|
|
192
193
|
try {
|
|
193
194
|
await output(frame);
|
|
194
195
|
}
|
|
195
196
|
finally {
|
|
196
|
-
await boundaries.consume(captured);
|
|
197
|
+
await boundaries.consume(captured, AbortSignal.timeout(5000));
|
|
197
198
|
}
|
|
198
199
|
process.exitCode = 2;
|
|
199
200
|
return 'unknown';
|
|
@@ -6,10 +6,10 @@ declare const coordinate: z.ZodObject<{
|
|
|
6
6
|
agentId: z.ZodOptional<z.ZodString>;
|
|
7
7
|
}, z.core.$strip>;
|
|
8
8
|
type ClaudeMcpCall = z.infer<typeof coordinate>;
|
|
9
|
-
export declare function beginClaudeMcpCall(input: ClaudeMcpCall, env?: NodeJS.ProcessEnv): Promise<{
|
|
9
|
+
export declare function beginClaudeMcpCall(input: ClaudeMcpCall, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<{
|
|
10
10
|
token: string;
|
|
11
11
|
}>;
|
|
12
12
|
export declare function endClaudeMcpCall(toolUseId: string, token: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
13
|
-
export declare function endClaudeMcpCallForSession(toolUseId: string, sessionId: string, env?: NodeJS.ProcessEnv, agentId?: string): Promise<void>;
|
|
13
|
+
export declare function endClaudeMcpCallForSession(toolUseId: string, sessionId: string, env?: NodeJS.ProcessEnv, agentId?: string, signal?: AbortSignal): Promise<void>;
|
|
14
14
|
export declare function readClaudeMcpCall(toolUseId: unknown, env?: NodeJS.ProcessEnv): Promise<ClaudeMcpCall>;
|
|
15
15
|
export {};
|
|
@@ -26,13 +26,13 @@ async function readRecord(file) {
|
|
|
26
26
|
}
|
|
27
27
|
// Only publishers/cleanup mutate this private transport directory. Readers see an
|
|
28
28
|
// immutable record via atomic rename, so they need neither a lock nor housekeeping.
|
|
29
|
-
async function mutate(env, fn) {
|
|
29
|
+
async function mutate(env, fn, signal) {
|
|
30
30
|
const root = storeRoot(env);
|
|
31
31
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
32
32
|
await fs.chmod(root, 0o700);
|
|
33
|
-
return withFileLock(path.join(root, 'store.lock'), { retryMs: 10, signal: AbortSignal.timeout(2000) }, () => fn(root));
|
|
33
|
+
return withFileLock(path.join(root, 'store.lock'), { retryMs: 10, signal: signal ?? AbortSignal.timeout(2000) }, () => fn(root));
|
|
34
34
|
}
|
|
35
|
-
export async function beginClaudeMcpCall(input, env = process.env) {
|
|
35
|
+
export async function beginClaudeMcpCall(input, env = process.env, signal) {
|
|
36
36
|
const parsed = coordinate.safeParse(input);
|
|
37
37
|
if (!parsed.success)
|
|
38
38
|
throw unavailable();
|
|
@@ -59,7 +59,7 @@ export async function beginClaudeMcpCall(input, env = process.env) {
|
|
|
59
59
|
await fs.rm(temporary, { force: true });
|
|
60
60
|
}
|
|
61
61
|
return { token };
|
|
62
|
-
});
|
|
62
|
+
}, signal);
|
|
63
63
|
}
|
|
64
64
|
export async function endClaudeMcpCall(toolUseId, token, env = process.env) {
|
|
65
65
|
if (!text.safeParse(toolUseId).success || !text.safeParse(token).success)
|
|
@@ -71,7 +71,7 @@ export async function endClaudeMcpCall(toolUseId, token, env = process.env) {
|
|
|
71
71
|
await fs.unlink(file);
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
export async function endClaudeMcpCallForSession(toolUseId, sessionId, env = process.env, agentId) {
|
|
74
|
+
export async function endClaudeMcpCallForSession(toolUseId, sessionId, env = process.env, agentId, signal) {
|
|
75
75
|
if (!text.safeParse(toolUseId).success || !text.safeParse(sessionId).success)
|
|
76
76
|
throw unavailable();
|
|
77
77
|
await mutate(env, async (root) => {
|
|
@@ -79,7 +79,7 @@ export async function endClaudeMcpCallForSession(toolUseId, sessionId, env = pro
|
|
|
79
79
|
const stored = await readRecord(file);
|
|
80
80
|
if (stored?.toolUseId === toolUseId && stored.sessionId === sessionId && stored.agentId === agentId)
|
|
81
81
|
await fs.unlink(file);
|
|
82
|
-
});
|
|
82
|
+
}, signal);
|
|
83
83
|
}
|
|
84
84
|
export async function readClaudeMcpCall(toolUseId, env = process.env) {
|
|
85
85
|
if (!text.safeParse(toolUseId).success)
|
package/dist/cli/context.js
CHANGED
|
@@ -92,7 +92,7 @@ const LOCATION_REQUIRED_COMMANDS = new Set([
|
|
|
92
92
|
'build',
|
|
93
93
|
'stream',
|
|
94
94
|
'join',
|
|
95
|
-
'
|
|
95
|
+
'perceive',
|
|
96
96
|
'express',
|
|
97
97
|
'listen',
|
|
98
98
|
'ignore',
|
|
@@ -130,13 +130,10 @@ export async function parseGlobalArgs(rawArgs) {
|
|
|
130
130
|
if (name !== undefined)
|
|
131
131
|
validateName(name);
|
|
132
132
|
const explicitSquarePath = requestedPath !== undefined;
|
|
133
|
-
|
|
134
|
-
const configured = configuredLocation === '' ? undefined : configuredLocation;
|
|
135
|
-
if (command !== undefined && !['--help', '-h'].includes(command) && locationIsRequired(command) && requestedPath === undefined && configured === undefined) {
|
|
133
|
+
if (command !== undefined && !['--help', '-h'].includes(command) && locationIsRequired(command) && requestedPath === undefined) {
|
|
136
134
|
fail(`✕ ${command} needs a square location\nsquare ls`);
|
|
137
135
|
}
|
|
138
|
-
|
|
139
|
-
return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, owner, args };
|
|
136
|
+
return { squarePath: requestedPath, explicitSquarePath, multipleSquares: false, name, owner, args };
|
|
140
137
|
}
|
|
141
138
|
export function locationIsRequired(command) {
|
|
142
139
|
return LOCATION_REQUIRED_COMMANDS.has(command);
|
package/dist/cli/json.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export const JSON_COMMANDS = new Set([
|
|
2
|
-
'join', 'express', '
|
|
2
|
+
'join', 'express', 'perceive', 'history', 'listen', 'ignore', 'unlisten', 'listening',
|
|
3
3
|
'done', 'status', 'participants', 'inbox',
|
|
4
4
|
]);
|
|
5
5
|
/** Serialize the SDK receipt, not its optional presentation or live participant handle. */
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { fail,
|
|
1
|
+
import { fail, commandIdentity } from './context.js';
|
|
2
2
|
export const mcpServerCommand = {
|
|
3
3
|
parse(argv) {
|
|
4
4
|
if (argv.length > 0)
|
|
5
|
-
fail('Usage: square
|
|
5
|
+
fail('Usage: square mcp-server');
|
|
6
6
|
return undefined;
|
|
7
7
|
},
|
|
8
8
|
async execute(_intent, context) {
|
|
9
9
|
const { runSquareMcpStdio } = await import('../mcp.js');
|
|
10
|
-
await runSquareMcpStdio({
|
|
10
|
+
await runSquareMcpStdio({ cwd: context.cwd, env: context.env, ...commandIdentity(context.env, context.owner) });
|
|
11
11
|
},
|
|
12
12
|
present() { },
|
|
13
13
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { PerceiveOptions, DiscoveryOptions, HistoryQuery, StreamStart } from '../square-facade.js';
|
|
2
2
|
import { type CommandSpec } from './context.js';
|
|
3
3
|
interface HistoryCommandOptions {
|
|
4
4
|
query: HistoryQuery;
|
|
@@ -13,7 +13,7 @@ interface StreamIntent {
|
|
|
13
13
|
start: StreamStart;
|
|
14
14
|
}
|
|
15
15
|
export declare const streamCommand: CommandSpec<StreamIntent>;
|
|
16
|
-
export declare const
|
|
16
|
+
export declare const perceiveCommand: CommandSpec<PerceiveOptions>;
|
|
17
17
|
export declare const historyCommand: CommandSpec<HistoryCommandOptions, string>;
|
|
18
18
|
interface ParticipantsCommandOptions {
|
|
19
19
|
limit: number;
|