@astrosheep/square 0.3.48 → 0.3.50
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/claude-plugin/.claude-plugin/plugin.json +1 -1
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/automatic-session.js +2 -2
- package/dist/boundary-presentation.js +4 -2
- package/dist/codex-hook.js +5 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/participant-host.js +1 -1
- package/dist/participant-identity.js +1 -1
- package/dist/registry.js +1 -1
- package/dist/square-projections.js +1 -1
- package/dist/square-wiring.js +1 -1
- package/extensions/square-pi.js +62 -47
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ const providerEnv = {
|
|
|
15
15
|
codex: 'CODEX_THREAD_ID',
|
|
16
16
|
claude: 'CLAUDE_CODE_SESSION_ID',
|
|
17
17
|
opencode: 'OPENCODE_SESSION_ID',
|
|
18
|
-
pi: '
|
|
18
|
+
pi: 'PI_SESSION_ID',
|
|
19
19
|
};
|
|
20
20
|
function operationEnv(provider, sessionId, env) {
|
|
21
21
|
return {
|
|
@@ -24,7 +24,7 @@ function operationEnv(provider, sessionId, env) {
|
|
|
24
24
|
CLAUDE_CODE_CHILD_SESSION: '',
|
|
25
25
|
CODEX_THREAD_ID: '',
|
|
26
26
|
OPENCODE_SESSION_ID: '',
|
|
27
|
-
|
|
27
|
+
PI_SESSION_ID: '',
|
|
28
28
|
[providerEnv[provider]]: sessionId,
|
|
29
29
|
};
|
|
30
30
|
}
|
|
@@ -7,7 +7,7 @@ import { openSquare } from './square-file-adapter.js';
|
|
|
7
7
|
import { closeOpenSquare } from './open-square.js';
|
|
8
8
|
import { presentPending } from './presentation-operations.js';
|
|
9
9
|
import { sessionInbox } from './inbox.js';
|
|
10
|
-
import {
|
|
10
|
+
import { renderAttentionPreview } from './attention-presentation.js';
|
|
11
11
|
const CONTEXT_MAX = 1200;
|
|
12
12
|
const presentationLocks = new Map();
|
|
13
13
|
function pendingCount(inbox) {
|
|
@@ -72,7 +72,9 @@ function renderBoundary(inbox) {
|
|
|
72
72
|
complete.push({
|
|
73
73
|
membership,
|
|
74
74
|
actIndexes: [notification.actIndex],
|
|
75
|
-
|
|
75
|
+
// The hook output is itself a user-visible presentation, even when the
|
|
76
|
+
// preview had to clip the body. Keep clipped items from being replayed.
|
|
77
|
+
markSeen: true,
|
|
76
78
|
});
|
|
77
79
|
}
|
|
78
80
|
return {
|
package/dist/codex-hook.js
CHANGED
|
@@ -17,9 +17,11 @@ export async function codexHookResponse(input, lookup = sessionInbox, env = proc
|
|
|
17
17
|
if (hookEventName === undefined)
|
|
18
18
|
return undefined;
|
|
19
19
|
await recordCodexBoundary(input.session_id, hookEventName === 'Stop' ? 'Stop' : 'non-stop', env);
|
|
20
|
-
|
|
21
|
-
?
|
|
22
|
-
|
|
20
|
+
if (hookEventName === 'Stop') {
|
|
21
|
+
await sweepPrivilegedPending(typeof input.cwd === 'string' ? input.cwd : process.cwd(), env, deliveryAdapters, sweepDeadline).catch(() => undefined);
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
const response = await presentPendingAtBoundary(input.session_id, (context) => ({ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: context } }), lookup, env);
|
|
23
25
|
await sweepPrivilegedPending(typeof input.cwd === 'string' ? input.cwd : process.cwd(), env, deliveryAdapters, sweepDeadline).catch(() => undefined);
|
|
24
26
|
return response;
|
|
25
27
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export { Square } from './square-wiring.js';
|
|
2
2
|
export { SquareError } from './model.js';
|
|
3
3
|
export { bindCurrentParticipant, squareAssignedParticipantName, unbindCurrentParticipant } from './registry.js';
|
|
4
|
+
export { createDefaultWakeTransport } from './notifications.js';
|
|
4
5
|
export { createHostLedgerPort, FileHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
5
6
|
export type { HostLedgerPort, PresenceRecord, EvidenceRecord } from './host-ledger.js';
|
|
6
7
|
export type { PresentationSinkPort } from './ports.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { Square } from './square-wiring.js';
|
|
2
2
|
export { SquareError } from './model.js';
|
|
3
3
|
export { bindCurrentParticipant, squareAssignedParticipantName, unbindCurrentParticipant } from './registry.js';
|
|
4
|
+
export { createDefaultWakeTransport } from './notifications.js';
|
|
4
5
|
export { createHostLedgerPort, FileHostLedgerPort } from './host-ledger-file-adapter.js';
|
package/dist/participant-host.js
CHANGED
|
@@ -3,7 +3,7 @@ import { defaultWakeRouteCapabilities, publishWakeRoute, retireWakeRouteFromArti
|
|
|
3
3
|
export function processIdentity(env) {
|
|
4
4
|
const choices = [
|
|
5
5
|
[env.CLAUDE_CODE_SESSION_ID, 'claude-code'], [env.CODEX_THREAD_ID, 'codex'],
|
|
6
|
-
[env.OPENCODE_SESSION_ID, 'opencode'], [env.
|
|
6
|
+
[env.OPENCODE_SESSION_ID, 'opencode'], [env.PI_SESSION_ID, 'pi'], [env.PASEO_AGENT_ID, 'paseo'],
|
|
7
7
|
];
|
|
8
8
|
const found = choices.find(([session]) => session?.trim());
|
|
9
9
|
return found === undefined ? { session: `process:${process.pid}`, channel: 'unknown' } : { session: found[0].trim(), channel: found[1] };
|
|
@@ -7,7 +7,7 @@ const nativeSessions = [
|
|
|
7
7
|
{ variable: 'CLAUDE_CODE_SESSION_ID', provider: 'claude' },
|
|
8
8
|
{ variable: 'CODEX_THREAD_ID', provider: 'codex' },
|
|
9
9
|
{ variable: 'OPENCODE_SESSION_ID', provider: 'opencode' },
|
|
10
|
-
{ variable: '
|
|
10
|
+
{ variable: 'PI_SESSION_ID', provider: 'pi' },
|
|
11
11
|
];
|
|
12
12
|
export function automaticParticipant(provider, sessionId, env) {
|
|
13
13
|
const configured = env.SQUARE_PARTICIPANT_NAME?.trim();
|
package/dist/registry.js
CHANGED
|
@@ -12,7 +12,7 @@ const LOCAL_SESSION_SOURCES = [
|
|
|
12
12
|
{ variable: 'CLAUDE_CODE_SESSION_ID', channel: 'claude-code', child: 'CLAUDE_CODE_CHILD_SESSION' },
|
|
13
13
|
{ variable: 'CODEX_THREAD_ID', channel: 'codex' },
|
|
14
14
|
{ variable: 'OPENCODE_SESSION_ID', channel: 'opencode' },
|
|
15
|
-
{ variable: '
|
|
15
|
+
{ variable: 'PI_SESSION_ID', channel: 'pi' },
|
|
16
16
|
];
|
|
17
17
|
export function registryPath(env = process.env) { return env.SQUARE_REGISTRY || path.join(homedir(), '.square', 'sessions.ndjsonl'); }
|
|
18
18
|
export async function canonicalSquarePath(squarePath) { const absolute = path.resolve(squarePath); try {
|
|
@@ -28,7 +28,7 @@ export async function projectSessionBindings(input) {
|
|
|
28
28
|
return rows.map(bindingProjection);
|
|
29
29
|
}
|
|
30
30
|
export function sessionIdsFromEnvironment(env = process.env) {
|
|
31
|
-
return [env.CLAUDE_CODE_SESSION_ID, env.CODEX_THREAD_ID, env.OPENCODE_SESSION_ID, env.
|
|
31
|
+
return [env.CLAUDE_CODE_SESSION_ID, env.CODEX_THREAD_ID, env.OPENCODE_SESSION_ID, env.PI_SESSION_ID, env.PASEO_AGENT_ID]
|
|
32
32
|
.map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
33
33
|
}
|
|
34
34
|
export async function projectLocalParticipantBinding(input) {
|
package/dist/square-wiring.js
CHANGED
|
@@ -85,7 +85,7 @@ export class Square {
|
|
|
85
85
|
async recognize(env) {
|
|
86
86
|
if (this.square.hostLedger === undefined)
|
|
87
87
|
return null;
|
|
88
|
-
const sessions = [env.CLAUDE_CODE_SESSION_ID, env.CODEX_THREAD_ID, env.OPENCODE_SESSION_ID, env.
|
|
88
|
+
const sessions = [env.CLAUDE_CODE_SESSION_ID, env.CODEX_THREAD_ID, env.OPENCODE_SESSION_ID, env.PI_SESSION_ID, env.PASEO_AGENT_ID]
|
|
89
89
|
.map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
90
90
|
if (sessions.length === 0)
|
|
91
91
|
return null;
|
package/extensions/square-pi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { presentPendingAtBoundary, renderPendingAtBoundary } from '../dist/boundary-presentation.js';
|
|
2
2
|
import { automaticSessionEnd, automaticSessionStart } from '../dist/automatic-session.js';
|
|
3
|
-
import { waitForSessionPending } from '../dist/inbox.js';
|
|
3
|
+
import { sessionInbox, waitForSessionPending } from '../dist/inbox.js';
|
|
4
4
|
import { projectSessionBindings } from '../dist/square-projections.js';
|
|
5
5
|
import { createHostLedgerPort } from '../dist/host-ledger-file-adapter.js';
|
|
6
6
|
|
|
@@ -32,39 +32,28 @@ export function renderPiInbox(inbox) {
|
|
|
32
32
|
export default function squarePiExtension(pi) {
|
|
33
33
|
let sessionId;
|
|
34
34
|
let sessionCwd;
|
|
35
|
-
let previousSessionId;
|
|
36
35
|
let watcher;
|
|
37
36
|
let watcherAbort;
|
|
38
37
|
let generation = 0;
|
|
39
38
|
const handledPending = new Set();
|
|
39
|
+
const observedPending = new Set();
|
|
40
40
|
const landingAcks = new Map();
|
|
41
41
|
let retryAfterChange = false;
|
|
42
42
|
let retryWait;
|
|
43
43
|
let turnIndex = 0;
|
|
44
44
|
let activeTurn;
|
|
45
45
|
let currentRunSignal;
|
|
46
|
-
|
|
46
|
+
let detachRunAbort;
|
|
47
|
+
let cancelledRun = false;
|
|
47
48
|
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const abort = () => {
|
|
57
|
-
deferredSteers.delete(deferred);
|
|
58
|
-
signal.removeEventListener('abort', abort);
|
|
59
|
-
reject(signal.reason || new Error('Pi native injection aborted'));
|
|
60
|
-
};
|
|
61
|
-
deferredSteers.add(deferred);
|
|
62
|
-
if (signal.aborted) abort();
|
|
63
|
-
else signal.addEventListener('abort', abort, { once: true });
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
const releaseDeferredSteers = () => {
|
|
67
|
-
for (const deferred of [...deferredSteers]) deferred.release();
|
|
49
|
+
const cancelRunDelivery = () => {
|
|
50
|
+
cancelledRun = true;
|
|
51
|
+
for (const key of observedPending) handledPending.add(key);
|
|
52
|
+
generation += 1;
|
|
53
|
+
stopWatcher();
|
|
54
|
+
failAcks(new Error('Pi notification delivery cancelled'));
|
|
55
|
+
retryAfterChange = false;
|
|
56
|
+
retryWait = undefined;
|
|
68
57
|
};
|
|
69
58
|
|
|
70
59
|
const failAcks = (error) => {
|
|
@@ -87,7 +76,6 @@ export default function squarePiExtension(pi) {
|
|
|
87
76
|
kind,
|
|
88
77
|
sentAfterTurn: turnIndex,
|
|
89
78
|
emptyTurnWindows: 0,
|
|
90
|
-
dropAfterSettled: false,
|
|
91
79
|
settle(error) {
|
|
92
80
|
if (ack.settled) return;
|
|
93
81
|
ack.settled = true;
|
|
@@ -110,6 +98,10 @@ export default function squarePiExtension(pi) {
|
|
|
110
98
|
return { promise, ack };
|
|
111
99
|
};
|
|
112
100
|
|
|
101
|
+
// Each operation carries its own session coordinate. Never rewrite the process environment:
|
|
102
|
+
// Pi owns PI_SESSION_ID and several SDK sessions may coexist in one process.
|
|
103
|
+
const sessionEnv = (id) => ({ ...process.env, ...(id === undefined ? {} : { PI_SESSION_ID: id }) });
|
|
104
|
+
|
|
113
105
|
// A transport may keep its promise pending while Pi is shutting down or
|
|
114
106
|
// replacing a session. Never make a lifecycle hook wait for that transport.
|
|
115
107
|
const stopWatcher = () => {
|
|
@@ -140,7 +132,7 @@ export default function squarePiExtension(pi) {
|
|
|
140
132
|
excludeKeys: handledPending,
|
|
141
133
|
skipImmediate: true,
|
|
142
134
|
onChangeArmed: resolveArmed,
|
|
143
|
-
}).catch(() => {
|
|
135
|
+
}, sessionEnv(sessionId)).catch(() => {
|
|
144
136
|
resolveArmed(false);
|
|
145
137
|
return [];
|
|
146
138
|
}).finally(() => signal.removeEventListener('abort', abort));
|
|
@@ -163,7 +155,7 @@ export default function squarePiExtension(pi) {
|
|
|
163
155
|
try {
|
|
164
156
|
pending = deferredRetry && retryWait !== undefined
|
|
165
157
|
? await retryWait
|
|
166
|
-
: await waitForSessionPending(sessionId, 30_000, { signal, excludeKeys: handledPending });
|
|
158
|
+
: await waitForSessionPending(sessionId, 30_000, { signal, excludeKeys: handledPending }, sessionEnv(sessionId));
|
|
167
159
|
} catch {
|
|
168
160
|
await pause(signal, 1_000);
|
|
169
161
|
continue;
|
|
@@ -180,6 +172,7 @@ export default function squarePiExtension(pi) {
|
|
|
180
172
|
retryWait = undefined;
|
|
181
173
|
|
|
182
174
|
const keys = inboxKeys(pending);
|
|
175
|
+
for (const key of keys) observedPending.add(key);
|
|
183
176
|
const deferred = armDeferredRetry();
|
|
184
177
|
const retryArmed = await deferred.armed;
|
|
185
178
|
if (!retryArmed) {
|
|
@@ -191,7 +184,7 @@ export default function squarePiExtension(pi) {
|
|
|
191
184
|
await presentPendingAtBoundary(
|
|
192
185
|
sessionId,
|
|
193
186
|
async (content) => {
|
|
194
|
-
if (currentRunSignal?.aborted)
|
|
187
|
+
if (signal.aborted || currentRunSignal?.aborted) throw new Error('Pi notification delivery cancelled');
|
|
195
188
|
const landing = waitForLanding(content, signal, 'steer');
|
|
196
189
|
try {
|
|
197
190
|
Promise.resolve(pi.sendMessage(
|
|
@@ -203,12 +196,23 @@ export default function squarePiExtension(pi) {
|
|
|
203
196
|
}
|
|
204
197
|
return landing.promise;
|
|
205
198
|
},
|
|
206
|
-
|
|
207
|
-
|
|
199
|
+
async (id, env) => {
|
|
200
|
+
const inbox = await sessionInbox(id, env ?? sessionEnv(id));
|
|
201
|
+
if (signal.aborted || token !== generation) return [];
|
|
202
|
+
for (const key of inboxKeys(inbox)) observedPending.add(key);
|
|
203
|
+
return inbox.map((membership) => ({
|
|
204
|
+
...membership,
|
|
205
|
+
notifications: membership.notifications.filter((note) => !handledPending.has(
|
|
206
|
+
`${membership.squarePath}\u0000${membership.name.toLocaleLowerCase()}\u0000${note.actIndex}`,
|
|
207
|
+
)),
|
|
208
|
+
}));
|
|
209
|
+
},
|
|
210
|
+
sessionEnv(sessionId),
|
|
208
211
|
signal,
|
|
209
212
|
);
|
|
210
213
|
for (const key of keys) handledPending.add(key);
|
|
211
214
|
} catch (error) {
|
|
215
|
+
if (signal.aborted || token !== generation) return;
|
|
212
216
|
if (error instanceof PiDeliveryDroppedError) {
|
|
213
217
|
deferred.cancel();
|
|
214
218
|
continue;
|
|
@@ -224,10 +228,13 @@ export default function squarePiExtension(pi) {
|
|
|
224
228
|
};
|
|
225
229
|
|
|
226
230
|
pi.on('session_start', async (_event, ctx) => {
|
|
231
|
+
detachRunAbort?.();
|
|
232
|
+
cancelledRun = false;
|
|
227
233
|
generation += 1;
|
|
228
234
|
failAcks(new Error('Pi session replaced'));
|
|
229
235
|
stopWatcher();
|
|
230
236
|
handledPending.clear();
|
|
237
|
+
observedPending.clear();
|
|
231
238
|
retryAfterChange = false;
|
|
232
239
|
retryWait = undefined;
|
|
233
240
|
turnIndex = 0;
|
|
@@ -235,12 +242,10 @@ export default function squarePiExtension(pi) {
|
|
|
235
242
|
currentRunSignal = undefined;
|
|
236
243
|
sessionId = ctx.sessionManager.getSessionId();
|
|
237
244
|
sessionCwd = ctx.cwd || process.cwd();
|
|
238
|
-
previousSessionId = process.env.SQUARE_PI_SESSION_ID;
|
|
239
|
-
process.env.SQUARE_PI_SESSION_ID = sessionId;
|
|
240
245
|
const token = generation;
|
|
241
246
|
watcherAbort = new AbortController();
|
|
242
247
|
const signal = watcherAbort.signal;
|
|
243
|
-
void automaticSessionStart('pi', sessionId, sessionCwd).then((context) => {
|
|
248
|
+
void automaticSessionStart('pi', sessionId, sessionCwd, sessionEnv(sessionId)).then((context) => {
|
|
244
249
|
if (context === undefined || sessionId === undefined || token !== generation) return;
|
|
245
250
|
const landing = waitForLanding(context, signal, 'nextTurn');
|
|
246
251
|
try {
|
|
@@ -258,7 +263,13 @@ export default function squarePiExtension(pi) {
|
|
|
258
263
|
});
|
|
259
264
|
|
|
260
265
|
pi.on('agent_start', async (_event, ctx) => {
|
|
266
|
+
detachRunAbort?.();
|
|
261
267
|
currentRunSignal = ctx?.getSignal?.();
|
|
268
|
+
if (ctx?.mode !== 'tui' || currentRunSignal === undefined) return;
|
|
269
|
+
const runSignal = currentRunSignal;
|
|
270
|
+
detachRunAbort = () => runSignal.removeEventListener('abort', cancelRunDelivery);
|
|
271
|
+
if (runSignal.aborted) cancelRunDelivery();
|
|
272
|
+
else runSignal.addEventListener('abort', cancelRunDelivery, { once: true });
|
|
262
273
|
});
|
|
263
274
|
|
|
264
275
|
pi.on('message_end', async (event) => {
|
|
@@ -277,15 +288,13 @@ export default function squarePiExtension(pi) {
|
|
|
277
288
|
const turn = activeTurn;
|
|
278
289
|
activeTurn = undefined;
|
|
279
290
|
const acks = [...landingAcks.values()].flatMap((waiters) => [...waiters]);
|
|
280
|
-
if (event.message?.stopReason === 'aborted' && ctx?.mode === 'tui'
|
|
281
|
-
|
|
282
|
-
if (ack.kind === 'steer') ack.dropAfterSettled = true;
|
|
283
|
-
}
|
|
291
|
+
if (event.message?.stopReason === 'aborted' && ctx?.mode === 'tui') {
|
|
292
|
+
if (!cancelledRun) cancelRunDelivery();
|
|
284
293
|
return;
|
|
285
294
|
}
|
|
286
295
|
if (turn === undefined) return;
|
|
287
296
|
for (const ack of acks) {
|
|
288
|
-
if (ack.kind !== 'steer' ||
|
|
297
|
+
if (ack.kind !== 'steer' || turn.index <= ack.sentAfterTurn) continue;
|
|
289
298
|
if (turn.hasInput) {
|
|
290
299
|
ack.emptyTurnWindows = 0;
|
|
291
300
|
} else {
|
|
@@ -296,23 +305,29 @@ export default function squarePiExtension(pi) {
|
|
|
296
305
|
});
|
|
297
306
|
|
|
298
307
|
pi.on('agent_settled', async () => {
|
|
308
|
+
detachRunAbort?.();
|
|
309
|
+
detachRunAbort = undefined;
|
|
299
310
|
currentRunSignal = undefined;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
311
|
+
if (!cancelledRun || sessionId === undefined) return;
|
|
312
|
+
const token = generation;
|
|
313
|
+
// Cancel the entire pending batch, including entries not yet sent to Pi.
|
|
314
|
+
// This is local suppression, never evidence that the model saw the activity.
|
|
315
|
+
const cancelled = await sessionInbox(sessionId, sessionEnv(sessionId));
|
|
316
|
+
if (token !== generation || sessionId === undefined) return;
|
|
317
|
+
for (const key of inboxKeys(cancelled)) handledPending.add(key);
|
|
318
|
+
cancelledRun = false;
|
|
319
|
+
watcherAbort = new AbortController();
|
|
320
|
+
watcher = wake(token, watcherAbort.signal).catch(() => undefined);
|
|
305
321
|
});
|
|
306
322
|
|
|
307
323
|
pi.on('session_shutdown', async () => {
|
|
324
|
+
detachRunAbort?.();
|
|
325
|
+
detachRunAbort = undefined;
|
|
326
|
+
cancelledRun = false;
|
|
308
327
|
generation += 1;
|
|
309
328
|
failAcks(new Error('Pi session ended'));
|
|
310
329
|
stopWatcher();
|
|
311
|
-
if (sessionId && sessionCwd) void automaticSessionEnd('pi', sessionId, sessionCwd).catch(() => undefined);
|
|
312
|
-
if (process.env.SQUARE_PI_SESSION_ID === sessionId) {
|
|
313
|
-
if (previousSessionId === undefined) delete process.env.SQUARE_PI_SESSION_ID;
|
|
314
|
-
else process.env.SQUARE_PI_SESSION_ID = previousSessionId;
|
|
315
|
-
}
|
|
330
|
+
if (sessionId && sessionCwd) void automaticSessionEnd('pi', sessionId, sessionCwd, sessionEnv(sessionId)).catch(() => undefined);
|
|
316
331
|
sessionId = undefined;
|
|
317
332
|
sessionCwd = undefined;
|
|
318
333
|
retryWait = undefined;
|