@astrosheep/square 0.3.33 → 0.3.35
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/claude-plugin/skills/square/SKILL.md +10 -10
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity-feed.js +1 -1
- package/dist/activity.d.ts +1 -0
- package/dist/activity.js +3 -2
- package/dist/artifact.js +2 -1
- package/dist/automatic-session.js +17 -1
- package/dist/boundary-presentation.d.ts +1 -0
- package/dist/boundary-presentation.js +16 -1
- package/dist/catch-decisions.d.ts +4 -0
- package/dist/catch-decisions.js +23 -7
- package/dist/cli/context.d.ts +1 -0
- package/dist/cli/context.js +26 -1
- package/dist/cli/observation-commands.d.ts +7 -1
- package/dist/cli/observation-commands.js +149 -63
- package/dist/cli/program.js +8 -2
- package/dist/cli/square-commands.d.ts +3 -1
- package/dist/cli/square-commands.js +42 -16
- package/dist/codex-queue.d.ts +1 -0
- package/dist/codex-queue.js +1 -1
- package/dist/decisions.d.ts +1 -0
- package/dist/decisions.js +29 -14
- package/dist/delivery-health.d.ts +1 -0
- package/dist/delivery-health.js +19 -6
- package/dist/delivery-operations.js +5 -5
- package/dist/delivery.d.ts +2 -0
- package/dist/delivery.js +1 -0
- package/dist/harness-links.js +15 -3
- package/dist/harness.js +3 -1
- package/dist/help.js +25 -16
- package/dist/inbox.d.ts +2 -0
- package/dist/inbox.js +4 -2
- package/dist/list.js +77 -24
- package/dist/model.d.ts +1 -3
- package/dist/paseo-connection.js +9 -3
- package/dist/paseo-state.d.ts +4 -1
- package/dist/paseo-state.js +2 -2
- package/dist/presentation.d.ts +4 -0
- package/dist/presentation.js +41 -12
- package/dist/runtime.d.ts +1 -0
- package/dist/square-actions.d.ts +5 -0
- package/dist/square-actions.js +31 -7
- package/dist/square-core.d.ts +11 -1
- package/dist/square-core.js +12 -9
- package/dist/square-facade.d.ts +5 -2
- package/dist/square-file-adapter.d.ts +1 -1
- package/dist/square-file-adapter.js +11 -4
- package/dist/square-wiring.d.ts +1 -0
- package/dist/square-wiring.js +5 -1
- package/dist/stream.d.ts +8 -1
- package/dist/stream.js +17 -6
- package/dist/views.d.ts +11 -5
- package/dist/views.js +42 -16
- package/dist/wake-sink.d.ts +2 -0
- package/dist/wake-sink.js +10 -1
- package/dist/watch.js +26 -9
- package/extensions/square-pi.js +41 -7
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +8 -6
- package/skills/square/SKILL.md +10 -10
package/dist/views.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
2
2
|
import { deliveryDelta, directedPeerSays } from './activity-feed.js';
|
|
3
3
|
import { coreActivities, coreParticipants, coreStatus, resolveKnownName } from './decisions.js';
|
|
4
4
|
import { deriveDeliveryModel, isActivitySeen } from './delivery.js';
|
|
@@ -7,15 +7,7 @@ import { countSays, currentHold, foldedState, freshWatchLease, inSquareCount, is
|
|
|
7
7
|
function expose(stored) {
|
|
8
8
|
if (stored.kind === 'read' || stored.actor === undefined)
|
|
9
9
|
throw new Error(`Cannot expose stored activity ${formatActivityId(stored.index)}`);
|
|
10
|
-
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor, ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}), mentions: stored.kind === 'say' ?
|
|
11
|
-
}
|
|
12
|
-
function exposePerceived(stored, viewer, delivery) {
|
|
13
|
-
const perception = delivery.perceive(stored, viewer);
|
|
14
|
-
const activity = expose(stored);
|
|
15
|
-
if (perception === 'full' || activity.body === undefined)
|
|
16
|
-
return { ...activity, perception };
|
|
17
|
-
const { body: _body, ...withoutBody } = activity;
|
|
18
|
-
return { ...withoutBody, perception };
|
|
10
|
+
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor, ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}), mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}), ...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
|
|
19
11
|
}
|
|
20
12
|
function parseRequiredActivityId(id) {
|
|
21
13
|
const index = parseActivityId(id);
|
|
@@ -23,12 +15,12 @@ function parseRequiredActivityId(id) {
|
|
|
23
15
|
throw new SquareError('invalid_args', `Invalid activity id: ${id}`);
|
|
24
16
|
return index;
|
|
25
17
|
}
|
|
26
|
-
function historyOptions(query
|
|
18
|
+
function historyOptions(query) {
|
|
27
19
|
if (query.before !== undefined && query.after !== undefined)
|
|
28
20
|
throw new SquareError('invalid_args', 'History cannot combine before and after cursors');
|
|
29
21
|
const afterIndex = query.after === undefined ? undefined : parseRequiredActivityId(query.after);
|
|
30
22
|
const beforeIndex = query.before === undefined ? undefined : parseRequiredActivityId(query.before);
|
|
31
|
-
return { ...(query.from === undefined ? {} : { participants: [...query.from] }), ...(query.grep === undefined ? {} : { grep: query.grep }), ...(query.mention ===
|
|
23
|
+
return { ...(query.from === undefined ? {} : { participants: [...query.from] }), ...(query.grep === undefined ? {} : { grep: query.grep }), ...(query.mention === undefined ? {} : { mention: query.mention }), ...(afterIndex === undefined ? {} : { afterIndex }), ...(beforeIndex === undefined ? {} : { beforeIndex }), order: 'asc' };
|
|
32
24
|
}
|
|
33
25
|
function selectHistory(stored, query) {
|
|
34
26
|
let selected = stored.filter((activity) => activity.kind !== 'read');
|
|
@@ -67,14 +59,14 @@ function sayNumbers(state) {
|
|
|
67
59
|
return result;
|
|
68
60
|
}
|
|
69
61
|
export async function history(square, query = {}) { const { state } = await square.artifact.read(); return selectHistory(coreActivities(state, historyOptions(query)), query).map(expose); }
|
|
70
|
-
export async function participantHistory(square,
|
|
62
|
+
export async function participantHistory(square, _name, query = {}) { const { state } = await square.artifact.read(); const effective = query.limit !== undefined ? query : { ...query, limit: 10 }; return selectHistory(coreActivities(state, historyOptions(effective)), effective).map(expose); }
|
|
71
63
|
export async function resolveParticipant(square, name) { const { state } = await square.artifact.read(); return { name: resolveKnownName(state, name), roster: rosterNames(state) }; }
|
|
72
64
|
export async function currentParticipant(square, name) { const { state } = await square.artifact.read(); const known = resolveRosterName(state, name); return known !== undefined && isCurrentlyJoined(state.acts, known) ? known : undefined; }
|
|
73
65
|
export async function participants(square) { const { state } = await square.artifact.read(); return statuses(square, state); }
|
|
74
66
|
export async function snapshot(square) { const { state } = await square.artifact.read(); const folded = foldedState(state); return { context: [...state.preamble, ...state.warmup].join('\n'), actCount: state.acts.filter((activity) => activity.kind !== 'read').length, hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }), held: folded.hold.active && folded.hold.actor !== undefined ? { by: folded.hold.actor, ...(folded.hold.reason === undefined ? {} : { reason: folded.hold.reason }) } : null, participants: statuses(square, state), delivered(name, id) { return isActivitySeen(state, name, parseRequiredActivityId(id)); } }; }
|
|
75
67
|
export async function activityPresentation(square, name) { const { state } = await square.artifact.read(); const known = resolveKnownName(state, name); const delivery = deriveDeliveryModel(state); const delta = deliveryDelta(state, known, delivery); const hold = currentHold(state.acts); return { name: known, roster: rosterNames(state), pendingPublic: directedPeerSays(state, delta, known, delivery), pendingRoomChanges: [], activities: state.acts, state, participantCount: inSquareCount(state), held: hold.active, ...(hold.reason === undefined ? {} : { holdReason: hold.reason }), ownActivityCount: countSays(state.acts, known), hardCap: state.hardCap }; }
|
|
76
68
|
export async function entryPresentation(square, name, lastN = 10) { const { state } = await square.artifact.read(); const known = resolveRosterName(state, name) ?? name; const publicActivities = state.acts.filter((activity) => activity.kind === 'say' || activity.kind === 'done'); return { joined: isCurrentlyJoined(state.acts, known), scene: state.warmup.join('\n').trim(), context: state.preamble.join('\n').trim(), joinContext: (state.preamble.at(-1) === '---' ? state.preamble.slice(0, -1) : state.preamble).join('\n').trim(), recentActivities: lastN === null ? publicActivities : publicActivities.slice(-lastN), state, sayNumbers: sayNumbers(state), participantCount: inSquareCount(state) }; }
|
|
77
|
-
export async function historyPresentation(square, options) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); return { activities: coreActivities(state, options, delivery)
|
|
69
|
+
export async function historyPresentation(square, options) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); return { activities: coreActivities(state, options, delivery), sayNumbers: sayNumbers(state), presenceAnchors: anchors(state, delivery), participantCount: inSquareCount(state) }; }
|
|
78
70
|
export async function participantsPresentation(square) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); return coreParticipants(state, square.clock(), delivery); }
|
|
79
71
|
export async function listPresentation(square) { const { state } = await square.artifact.read(); return { context: state.preamble, participants: foldedState(state).participants.filter((participant) => participant.joined).sort((left, right) => (right.lastActiveAt ?? -Infinity) - (left.lastActiveAt ?? -Infinity) || left.name.localeCompare(right.name)).map((participant) => participant.name), activities: state.acts.filter((activity) => activity.kind === 'say').length }; }
|
|
80
72
|
export async function statusPresentation(square) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); const status = coreStatus(state, square.clock(), delivery); return { state, status, ...(status.latestAct?.kind === 'say' ? { latestActNumber: countSays(state.acts, status.latestAct.actor) } : {}) }; }
|
|
@@ -83,8 +75,42 @@ export async function eventPresentation(square, id) { const { state } = await sq
|
|
|
83
75
|
export async function watchPresentation(square, name) { const { state } = await square.artifact.read(); const known = resolveKnownName(state, name); const now = square.clock(); const terminal = watchTerminalStatus(state, known); const delivery = deriveDeliveryModel(state); return { activities: state.acts, state, participantCount: inSquareCount(state), presence: { participants: coreParticipants(state, now, delivery), now }, ...(terminal === undefined ? {} : { terminalStatus: terminal }) }; }
|
|
84
76
|
export async function inboxProjection(square, name, _sessionId) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); const known = delivery.knownParticipant(name); if (known === undefined || !delivery.joinedRecipients().some((recipient) => nameKey(recipient) === nameKey(known)))
|
|
85
77
|
return { name, joined: false, notifications: [] }; const lease = freshWatchLease(state, known, square.clock()); return { name: known, joined: true, notifications: delivery.pendingFor(known).map(({ item, route }) => ({ actIndex: item.index, actor: item.actor, at: item.at, route, body: item.body })), ...(lease === undefined ? {} : { catchLease: lease }) }; }
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
const STREAM_BATCH_MAX = 100;
|
|
79
|
+
function projectStreamActivities(state, activities, recipient) {
|
|
80
|
+
const delivery = recipient === undefined ? undefined : deriveDeliveryModel(state);
|
|
81
|
+
return activities.flatMap((activity) => {
|
|
82
|
+
if (delivery === undefined || recipient === undefined)
|
|
83
|
+
return [{ activity }];
|
|
84
|
+
const notification = delivery.plan(activity).find((candidate) => nameKey(candidate.recipient) === nameKey(recipient));
|
|
85
|
+
return notification === undefined ? [] : [{ activity, route: notification.route }];
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
function selectStreamTail(activities, last) {
|
|
89
|
+
if (!Number.isSafeInteger(last) || last < 0 || last > STREAM_BATCH_MAX) {
|
|
90
|
+
throw new SquareError('invalid_args', `Invalid stream tail: expected a non-negative safe integer no greater than ${STREAM_BATCH_MAX}.`);
|
|
91
|
+
}
|
|
92
|
+
return last === 0 ? [] : activities.slice(-last);
|
|
93
|
+
}
|
|
94
|
+
export async function streamProjection(square, cursor, recipient) {
|
|
95
|
+
const artifact = 'artifact' in square ? square.artifact : square.cell;
|
|
96
|
+
const { state } = await artifact.read();
|
|
97
|
+
const pending = state.acts.filter((activity) => activity.index > cursor);
|
|
98
|
+
const batch = pending.slice(0, STREAM_BATCH_MAX);
|
|
99
|
+
return {
|
|
100
|
+
activities: projectStreamActivities(state, batch, recipient),
|
|
101
|
+
cursor: batch.at(-1)?.index ?? cursor,
|
|
102
|
+
hasMore: pending.length > batch.length,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function streamTailProjection(square, last = 10, recipient) {
|
|
106
|
+
const artifact = 'artifact' in square ? square.artifact : square.cell;
|
|
107
|
+
const { state } = await artifact.read();
|
|
108
|
+
return {
|
|
109
|
+
activities: selectStreamTail(projectStreamActivities(state, state.acts, recipient), last),
|
|
110
|
+
cursor: state.acts.at(-1)?.index ?? -1,
|
|
111
|
+
hasMore: false,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
88
114
|
export async function notificationForAct(square, actIndex) { const { state } = await square.artifact.read(); const activity = state.acts.find((candidate) => candidate.index === actIndex); return activity === undefined ? [] : deriveDeliveryModel(state).plan(activity); }
|
|
89
115
|
export function pendingDeliveriesFromState(state, delivery = deriveDeliveryModel(state)) { return delivery.joinedRecipients().map((recipient) => ({ recipient, notifications: delivery.pendingFor(recipient) })); }
|
|
90
116
|
export async function pendingDeliveries(square) { const { state } = await square.artifact.read(); return pendingDeliveriesFromState(state); }
|
package/dist/wake-sink.d.ts
CHANGED
|
@@ -8,5 +8,7 @@ export declare class PaseoWakeSendError extends Error {
|
|
|
8
8
|
constructor(message: string, kind: PaseoWakeFailureKind);
|
|
9
9
|
}
|
|
10
10
|
export declare function sendPaseoWake({ agentId, prompt }: PaseoWakeRequest, opts?: {
|
|
11
|
+
args?: string[];
|
|
12
|
+
bin?: string;
|
|
11
13
|
timeoutMs?: number;
|
|
12
14
|
}): void;
|
package/dist/wake-sink.js
CHANGED
|
@@ -31,8 +31,17 @@ function classifyCommandFailure(code, message) {
|
|
|
31
31
|
function redactUriPassword(value) {
|
|
32
32
|
return value.replace(/([?&]password=)[^&\s]+/gi, '$1[redacted]');
|
|
33
33
|
}
|
|
34
|
+
function configuredArguments(name) {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(process.env[name] ?? '[]');
|
|
37
|
+
return Array.isArray(parsed) && parsed.every((item) => typeof item === 'string') ? parsed : [];
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
34
43
|
export function sendPaseoWake({ agentId, prompt }, opts = {}) {
|
|
35
|
-
const result = spawnSync(process.env.SQUARE_PASEO_BIN
|
|
44
|
+
const result = spawnSync(opts.bin ?? process.env.SQUARE_PASEO_BIN ?? 'paseo', [...(opts.args ?? configuredArguments('SQUARE_PASEO_BIN_ARGS')), 'send', agentId, '--prompt', prompt, '--no-wait', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: opts.timeoutMs ?? 5000, env: process.env });
|
|
36
45
|
if (result.error) {
|
|
37
46
|
const code = result.error.code;
|
|
38
47
|
const kind = code === 'ENOENT' || code === 'ECONNREFUSED' ? 'transient' : 'unknown';
|
package/dist/watch.js
CHANGED
|
@@ -6,7 +6,7 @@ import { closeOpenSquare } from './open-square.js';
|
|
|
6
6
|
import { openParticipant } from './square-wiring.js';
|
|
7
7
|
import { resolveParticipant, watchPresentation } from './views.js';
|
|
8
8
|
import { acquireWatchLease, ownsWatchLease, pulseWatchLease, releaseWatchLease } from './wakes.js';
|
|
9
|
-
import { renderWatchForceTakeover, renderWatchAlreadyActive, renderWatchOutput, renderWatchReplaced, renderWatchStatus, participantCommandPrefix, withPathOutput, } from './presentation.js';
|
|
9
|
+
import { renderWatchForceTakeover, renderWatchAlreadyActive, renderWatchReplaceMissing, renderWatchOutput, renderWatchReplaced, renderWatchStatus, participantCommandPrefix, withPathOutput, } from './presentation.js';
|
|
10
10
|
import { hasAutomaticDeliveryIdentity } from './registry.js';
|
|
11
11
|
import { parseActivityId } from './square-core.js';
|
|
12
12
|
function watchStatusExitCode(status) {
|
|
@@ -36,18 +36,31 @@ function watchOutputResult(squarePath, presentation, name, caught, opts = {}) {
|
|
|
36
36
|
viewer: name,
|
|
37
37
|
showCatchHint: !hasAutomaticDeliveryIdentity(),
|
|
38
38
|
perceptions,
|
|
39
|
-
})
|
|
39
|
+
}) + (caught.remaining > 0
|
|
40
|
+
? `\n○ ${caught.remaining} matching ${caught.remaining === 1 ? 'activity remains' : 'activities remain'}\n» ${catchContinuationCommand(squarePath, name, opts)}`
|
|
41
|
+
: ''),
|
|
42
|
+
remaining: caught.remaining,
|
|
40
43
|
...(opts.status ? { status: opts.status } : {}),
|
|
41
44
|
};
|
|
42
45
|
}
|
|
43
|
-
function
|
|
46
|
+
function catchContinuationCommand(squarePath, name, opts) {
|
|
47
|
+
const args = ['--now'];
|
|
48
|
+
if (opts.participants !== undefined && opts.participants.length > 0)
|
|
49
|
+
args.push('--from', opts.participants.join(','));
|
|
50
|
+
if (opts.mention !== undefined)
|
|
51
|
+
args.push('--mention');
|
|
52
|
+
if (opts.limit !== undefined)
|
|
53
|
+
args.push('--limit', String(opts.limit));
|
|
54
|
+
return `${participantCommandPrefix(squarePath, name)} catch ${args.join(' ')}`;
|
|
55
|
+
}
|
|
56
|
+
function writeWatchOutput(squarePath, name, presentation, stdout, remaining, status, idleMs) {
|
|
44
57
|
const headerOpts = { participantCount: presentation.participantCount };
|
|
45
58
|
const showCatchHint = !hasAutomaticDeliveryIdentity();
|
|
46
59
|
if (status) {
|
|
47
60
|
process.stdout.write(withPathOutput(squarePath, [renderWatchStatus({ status, squarePath, name, idleMs, presence: presentation.presence, showCatchHint }), stdout.trimEnd()].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
48
61
|
return;
|
|
49
62
|
}
|
|
50
|
-
const fallback = showCatchHint
|
|
63
|
+
const fallback = showCatchHint && remaining === 0
|
|
51
64
|
? `» ${participantCommandPrefix(squarePath, name)} catch --idle 30m\n stay available for new activity`
|
|
52
65
|
: '';
|
|
53
66
|
process.stdout.write(withPathOutput(squarePath, [stdout.trimEnd(), fallback].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
@@ -68,7 +81,7 @@ function writeWatchReplaced(squarePath, name, presentation) {
|
|
|
68
81
|
async function finishWatchResult(square, squarePath, name, result, leaseId, idleMs) {
|
|
69
82
|
if (result.type === 'output') {
|
|
70
83
|
await endWatch(square, name, leaseId);
|
|
71
|
-
writeWatchOutput(squarePath, name, await watchPresentation(square, name), result.stdout, result.status);
|
|
84
|
+
writeWatchOutput(squarePath, name, await watchPresentation(square, name), result.stdout, result.remaining, result.status);
|
|
72
85
|
process.exitCode = watchStatusExitCode(result.status);
|
|
73
86
|
return true;
|
|
74
87
|
}
|
|
@@ -115,11 +128,12 @@ async function cmdWatchNow(squarePath, name, opts) {
|
|
|
115
128
|
const caught = await facade.participant.catch({
|
|
116
129
|
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
117
130
|
...(opts.mention === undefined ? {} : { mention: true }),
|
|
131
|
+
...(opts.limit === undefined ? {} : { limit: opts.limit }),
|
|
118
132
|
});
|
|
119
133
|
const presentation = await watchPresentation(square, name);
|
|
120
134
|
const status = presentation.terminalStatus;
|
|
121
135
|
const result = caught.activities.length > 0
|
|
122
|
-
? watchOutputResult(squarePath, presentation, name, caught, { mention: opts.mention, ...(status ? { status } : {}) })
|
|
136
|
+
? watchOutputResult(squarePath, presentation, name, caught, { mention: opts.mention, participants: opts.participants, limit: opts.limit, ...(status ? { status } : {}) })
|
|
123
137
|
: { type: 'terminal', status: status ?? 'empty-now' };
|
|
124
138
|
await finishWatchResult(square, squarePath, name, result, undefined);
|
|
125
139
|
return caught.activities.length > 0;
|
|
@@ -162,9 +176,11 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
162
176
|
let staleSince = nowMs();
|
|
163
177
|
let currentLeaseId = start.leaseId;
|
|
164
178
|
let nextHeartbeatAt = start.heartbeatAt + WATCH_HEARTBEAT_MS;
|
|
165
|
-
if (
|
|
179
|
+
if (opts.replace) {
|
|
166
180
|
const presentation = await watchPresentation(square, name);
|
|
167
|
-
process.stdout.write(withPathOutput(squarePath,
|
|
181
|
+
process.stdout.write(withPathOutput(squarePath, start.replaced
|
|
182
|
+
? renderWatchForceTakeover({ squarePath, name })
|
|
183
|
+
: renderWatchReplaceMissing({ squarePath, name }), { participantCount: presentation.participantCount }));
|
|
168
184
|
}
|
|
169
185
|
const idleMs = opts.idleMs ?? STALE_MS;
|
|
170
186
|
const removeInterruptHandler = installWatchInterruptHandler(square, squarePath, name, () => currentLeaseId);
|
|
@@ -180,9 +196,10 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
180
196
|
const caught = await facade.participant.catch({
|
|
181
197
|
...(opts.participants === undefined ? {} : { from: opts.participants }),
|
|
182
198
|
...(opts.mention === undefined ? {} : { mention: true }),
|
|
199
|
+
...(opts.limit === undefined ? {} : { limit: opts.limit }),
|
|
183
200
|
});
|
|
184
201
|
if (caught.activities.length > 0) {
|
|
185
|
-
result = watchOutputResult(squarePath, await watchPresentation(square, name), name, caught, { mention: opts.mention });
|
|
202
|
+
result = watchOutputResult(squarePath, await watchPresentation(square, name), name, caught, { mention: opts.mention, participants: opts.participants, limit: opts.limit });
|
|
186
203
|
}
|
|
187
204
|
}
|
|
188
205
|
if (await finishWatchResult(square, squarePath, name, result, currentLeaseId)) {
|
package/extensions/square-pi.js
CHANGED
|
@@ -43,6 +43,7 @@ export default function squarePiExtension(pi) {
|
|
|
43
43
|
let settledWaiters = [];
|
|
44
44
|
const handledPending = new Set();
|
|
45
45
|
let retryAfterChange = false;
|
|
46
|
+
let retryWait;
|
|
46
47
|
const present = (deliver, signal) => sessionId === undefined ? undefined : presentPendingAtBoundary(sessionId, deliver, undefined, undefined, signal);
|
|
47
48
|
|
|
48
49
|
const presentAtBoundary = (deliver) => {
|
|
@@ -88,23 +89,43 @@ export default function squarePiExtension(pi) {
|
|
|
88
89
|
});
|
|
89
90
|
|
|
90
91
|
const wake = async (piContext, token, signal) => {
|
|
92
|
+
const armDeferredRetry = () => {
|
|
93
|
+
const controller = new AbortController();
|
|
94
|
+
let resolveArmed;
|
|
95
|
+
const armed = new Promise((resolve) => { resolveArmed = resolve; });
|
|
96
|
+
const abort = () => controller.abort();
|
|
97
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
98
|
+
const pending = waitForSessionPending(sessionId, 30_000, {
|
|
99
|
+
signal: controller.signal,
|
|
100
|
+
excludeKeys: handledPending,
|
|
101
|
+
skipImmediate: true,
|
|
102
|
+
onChangeArmed: resolveArmed,
|
|
103
|
+
}).catch(() => {
|
|
104
|
+
resolveArmed(false);
|
|
105
|
+
return [];
|
|
106
|
+
}).finally(() => signal.removeEventListener('abort', abort));
|
|
107
|
+
return { armed, pending, cancel: () => controller.abort() };
|
|
108
|
+
};
|
|
109
|
+
|
|
91
110
|
while (sessionId !== undefined && token === generation && !signal.aborted) {
|
|
92
111
|
if ((await sessionBindings(sessionId)).length === 0) {
|
|
93
112
|
await pause(signal, 1_000);
|
|
94
113
|
continue;
|
|
95
114
|
}
|
|
96
115
|
const deferredRetry = retryAfterChange;
|
|
97
|
-
const pending =
|
|
98
|
-
|
|
99
|
-
excludeKeys: handledPending
|
|
100
|
-
skipImmediate: deferredRetry,
|
|
101
|
-
});
|
|
116
|
+
const pending = deferredRetry && retryWait !== undefined
|
|
117
|
+
? await retryWait
|
|
118
|
+
: await waitForSessionPending(sessionId, 30_000, { signal, excludeKeys: handledPending });
|
|
102
119
|
if (sessionId === undefined || token !== generation || signal.aborted) return;
|
|
103
120
|
if (pending.length === 0) {
|
|
104
|
-
if (deferredRetry)
|
|
121
|
+
if (deferredRetry) {
|
|
122
|
+
retryAfterChange = false;
|
|
123
|
+
retryWait = undefined;
|
|
124
|
+
}
|
|
105
125
|
continue;
|
|
106
126
|
}
|
|
107
127
|
retryAfterChange = false;
|
|
128
|
+
retryWait = undefined;
|
|
108
129
|
if (!piContext.isIdle()) {
|
|
109
130
|
const serial = settledSerial;
|
|
110
131
|
await waitForSettled(serial, signal);
|
|
@@ -113,6 +134,14 @@ export default function squarePiExtension(pi) {
|
|
|
113
134
|
if (presenting) continue;
|
|
114
135
|
const keys = inboxKeys(pending);
|
|
115
136
|
presenting = true;
|
|
137
|
+
const deferred = armDeferredRetry();
|
|
138
|
+
const retryArmed = await deferred.armed;
|
|
139
|
+
if (!retryArmed) {
|
|
140
|
+
deferred.cancel();
|
|
141
|
+
presenting = false;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
let keepDeferred = false;
|
|
116
145
|
try {
|
|
117
146
|
const delivered = await presentPendingAtBoundary(
|
|
118
147
|
sessionId,
|
|
@@ -149,9 +178,12 @@ export default function squarePiExtension(pi) {
|
|
|
149
178
|
for (const key of keys) handledPending.add(key);
|
|
150
179
|
}
|
|
151
180
|
} catch {
|
|
152
|
-
//
|
|
181
|
+
// The next state edge is already being observed before native injection starts.
|
|
153
182
|
retryAfterChange = true;
|
|
183
|
+
retryWait = deferred.pending;
|
|
184
|
+
keepDeferred = true;
|
|
154
185
|
} finally {
|
|
186
|
+
if (!keepDeferred) deferred.cancel();
|
|
155
187
|
presenting = false;
|
|
156
188
|
}
|
|
157
189
|
}
|
|
@@ -162,6 +194,7 @@ export default function squarePiExtension(pi) {
|
|
|
162
194
|
stopWatcher();
|
|
163
195
|
handledPending.clear();
|
|
164
196
|
retryAfterChange = false;
|
|
197
|
+
retryWait = undefined;
|
|
165
198
|
sessionId = ctx.sessionManager.getSessionId();
|
|
166
199
|
sessionCwd = ctx.cwd || process.cwd();
|
|
167
200
|
previousSessionId = process.env.SQUARE_PI_SESSION_ID;
|
|
@@ -208,5 +241,6 @@ export default function squarePiExtension(pi) {
|
|
|
208
241
|
sessionCwd = undefined;
|
|
209
242
|
joiningContext = undefined;
|
|
210
243
|
presenting = false;
|
|
244
|
+
retryWait = undefined;
|
|
211
245
|
});
|
|
212
246
|
}
|
package/package.json
CHANGED
|
@@ -40,7 +40,7 @@ First action: enter the square. Read the context, warmup, and recent activity pr
|
|
|
40
40
|
square --location <square> --as <name> join
|
|
41
41
|
|
|
42
42
|
Then follow the Happy Path from the join output. Core commands:
|
|
43
|
-
square --location <square> --as <name> express - <<'EOF'
|
|
43
|
+
square --location <square> --as <name> express --no-mention - <<'EOF'
|
|
44
44
|
...
|
|
45
45
|
EOF
|
|
46
46
|
square --location <square> --as <name> catch --mention --idle 10m
|
|
@@ -55,7 +55,7 @@ EOF
|
|
|
55
55
|
|
|
56
56
|
For complete history, follow the activity-id continuation commands printed by `history`.
|
|
57
57
|
|
|
58
|
-
Every activity
|
|
58
|
+
Every activity that needs a specific listener uses `--mention <name>`; repeat the flag for multiple participants. Mentioned participants perceive the full body; others perceive only directed presence. Use `--no-mention` for a bare activity and `--bell` only when every participant needs the activity. An `@name` in the body is ordinary Markdown. Precise history queries may still read original archive bodies.
|
|
59
59
|
|
|
60
60
|
If an activity is refused because something happened while the participant was not looking, run `square --location <square> --as <name> catch --now`, take it in, then express again. `catch --now` catches up without waiting.
|
|
61
61
|
```
|
|
@@ -68,8 +68,8 @@ If you or the human want to participate, choose a participant name and use the p
|
|
|
68
68
|
|
|
69
69
|
```bash
|
|
70
70
|
square --location <square> --as <name> join
|
|
71
|
-
square --location <square> --as <name> express - <<'EOF'
|
|
72
|
-
|
|
71
|
+
square --location <square> --as <name> express --mention <participant-name> - <<'EOF'
|
|
72
|
+
your view
|
|
73
73
|
EOF
|
|
74
74
|
square --location <square> --as <name> catch --idle 10m
|
|
75
75
|
square --location <square> --as <name> done - <<'EOF'
|
|
@@ -87,9 +87,11 @@ square --location <square> history --from <name>
|
|
|
87
87
|
square --location <square> status
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
History shows one result in full and previews multiple results. `--no-truncate` shows every original body.
|
|
91
|
+
|
|
90
92
|
`history` reads the archive without advancing participant presence. `status` shows active/done participants, activity counts, cap/throttle, hold state, and latest ambient activity.
|
|
91
93
|
|
|
92
|
-
Every activity must
|
|
94
|
+
Every addressed activity must use `--mention <name>`; use `--no-mention` for a bare activity and `--bell` only for activity that every participant needs.
|
|
93
95
|
|
|
94
96
|
## Human Direction
|
|
95
97
|
|
|
@@ -117,7 +119,7 @@ While held, participant expression and catch pause. Join, done, status, and hist
|
|
|
117
119
|
When participants are done, collect the public activities:
|
|
118
120
|
|
|
119
121
|
```bash
|
|
120
|
-
square --location <square> history --no-truncate #
|
|
122
|
+
square --location <square> history --no-truncate # show every original body
|
|
121
123
|
square --location <square> status
|
|
122
124
|
```
|
|
123
125
|
|
package/skills/square/SKILL.md
CHANGED
|
@@ -21,7 +21,7 @@ leave for good : done (permanent — not the end of a round)
|
|
|
21
21
|
|
|
22
22
|
```bash
|
|
23
23
|
square --location .square/PUBLIC.square --as <name> catch --now
|
|
24
|
-
square --location .square/PUBLIC.square --as <name> express
|
|
24
|
+
square --location .square/PUBLIC.square --as <name> express --mention alice "your thought"
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
For any other square, find it, then `join` it once (`ls` is short for `list`; `--depth N` widens the search):
|
|
@@ -46,22 +46,22 @@ square --location <square> --as <name> status
|
|
|
46
46
|
Everything you land is one activity — pure speech, pure action, or both. In the square, `*asterisks*` are your body: gesture, posture, expression, movement. **Always give speech a body.** Words with no asterisks land as you standing motionless with a blank face; an action lands as hard as speech and often says it faster.
|
|
47
47
|
|
|
48
48
|
```bash
|
|
49
|
-
square --location <square> --as <name> express
|
|
50
|
-
square --location <square> --as <name> express "*nods slowly
|
|
51
|
-
square --location <square> --as <name> express "*stands*
|
|
49
|
+
square --location <square> --as <name> express --mention alice "I disagree — the cache is the wrong layer for this."
|
|
50
|
+
square --location <square> --as <name> express --mention bob "*nods slowly*"
|
|
51
|
+
square --location <square> --as <name> express --mention alice "*stands* Fine. I'll take the migration."
|
|
52
52
|
```
|
|
53
53
|
|
|
54
54
|
For a longer activity, use stdin:
|
|
55
55
|
|
|
56
56
|
```bash
|
|
57
|
-
square --location <square> --as <name> express - <<'EOF'
|
|
57
|
+
square --location <square> --as <name> express --mention bob - <<'EOF'
|
|
58
58
|
*drops a rough sketch onto the table*
|
|
59
59
|
|
|
60
|
-
The ownership boundary belongs here.
|
|
60
|
+
The ownership boundary belongs here. Does this match your read?
|
|
61
61
|
EOF
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
**Addressing.**
|
|
64
|
+
**Addressing.** Use `--mention <name>` (repeatable) to address participants; they hear the full body even when they are not listening, and everyone else sees you walk over to them. Use `--no-mention` to land a bare activity; `listen` opts a participant into future bare delivery. Use `--bell` only when every participant needs it. An `@name` in the body is ordinary Markdown and does not address anyone. Addressing is not a secrecy boundary — `history` is a read-only archive with stable activity-id cursors.
|
|
65
65
|
|
|
66
66
|
**Discipline.** Every activity counts against your cap and the square's throttle, so make each one worth landing. Keep private progress and tool chatter out — express only when another participant needs the thought, question, or decision.
|
|
67
67
|
|
|
@@ -78,7 +78,7 @@ square --location <square> --as <name> catch --now --mention # take in pen
|
|
|
78
78
|
square --location <square> --as <name> catch --now --from <names> # take in pending activity from named participants
|
|
79
79
|
```
|
|
80
80
|
|
|
81
|
-
Every catch needs exactly one mode: `--now` or `--idle <duration>`. `--mention` and `--from` filter either mode; they do not replace it. Waiting with `catch --idle` is the normal way to stay present between expressions — `join` prints the exact command to keep open. Do not build a polling loop.
|
|
81
|
+
Every catch needs exactly one mode: `--now` or `--idle <duration>`. `--mention` and `--from` filter either mode; they do not replace it. Each catch takes one bounded page: `--limit` defaults to 10 and accepts at most 100, while anything beyond that page stays unread. Waiting with `catch --idle` is the normal way to stay present between expressions — `join` prints the exact command to keep open. Do not build a polling loop.
|
|
82
82
|
|
|
83
83
|
## Listen
|
|
84
84
|
|
|
@@ -101,11 +101,11 @@ square history --limit 5 # most recent 5, oldest to newest
|
|
|
101
101
|
square history --before act/12 --limit 5 # the page before act/12
|
|
102
102
|
square history --after act/12 --limit 5 # the page after act/12
|
|
103
103
|
square history --limit 5 --order desc # newest first
|
|
104
|
-
square history --no-truncate #
|
|
104
|
+
square history --no-truncate # show every original body
|
|
105
105
|
square history --grep 'term' # search
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
See `square history --help` for advanced usage. Never read or parse the binary Square artifact directly.
|
|
108
|
+
See `square history --help` for advanced usage. Never read or parse the binary Square artifact directly. One result shows its full body; multiple results use previews. `--no-truncate` shows every original body. Follow the printed activity-id command to continue page by page.
|
|
109
109
|
|
|
110
110
|
## Hold
|
|
111
111
|
|