@astrosheep/square 0.3.57 → 0.3.61
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/activity.js +16 -14
- package/dist/attention-presentation.js +19 -19
- package/dist/automatic-session.js +2 -2
- package/dist/catch-decisions.js +1 -1
- package/dist/cli/context.js +16 -16
- package/dist/cli/observation-commands.js +85 -45
- package/dist/cli/program.js +33 -9
- package/dist/cli/square-commands.js +21 -19
- package/dist/decisions.d.ts +2 -0
- package/dist/decisions.js +38 -31
- package/dist/delivery-health.d.ts +3 -2
- package/dist/delivery-health.js +79 -47
- package/dist/delivery-operations.js +50 -8
- package/dist/diagnostic-redaction.d.ts +2 -0
- package/dist/diagnostic-redaction.js +40 -0
- package/dist/help.js +1 -1
- package/dist/host-ledger-file-adapter.js +2 -2
- package/dist/host-ledger.d.ts +9 -2
- package/dist/list.js +12 -9
- package/dist/notifications.js +28 -11
- package/dist/ports.d.ts +5 -1
- package/dist/presentation.d.ts +7 -0
- package/dist/presentation.js +75 -67
- package/dist/registry.js +2 -2
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.js +37 -0
- package/dist/square-actions.js +4 -4
- package/dist/stream.js +1 -1
- package/dist/tty-style.d.ts +7 -0
- package/dist/tty-style.js +13 -0
- package/dist/views.js +6 -5
- package/dist/wake-attempts.d.ts +18 -0
- package/dist/wake-attempts.js +38 -11
- package/dist/watch.js +24 -11
- package/extensions/square-pi.js +16 -6
- package/package.json +3 -3
package/dist/runtime.js
CHANGED
|
@@ -184,6 +184,43 @@ export function readCursor(squareState, name, landed = replayLandedAudiences(squ
|
|
|
184
184
|
export function latestActIndex(acts) {
|
|
185
185
|
return acts.reduce((max, act) => Math.max(max, act.index), -1);
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* "Was here" is a claim of presence, so it rides on evidence: the participant's own acts
|
|
189
|
+
* and seen receipts after their latest join. Anything else — in particular acts merely
|
|
190
|
+
* not addressed to them — says nothing about where they actually were.
|
|
191
|
+
*/
|
|
192
|
+
export function presenceEvidenceCursor(squareState, name, landed = replayLandedAudiences(squareState.acts)) {
|
|
193
|
+
const recipient = landed.resolveParticipant(name) ?? name;
|
|
194
|
+
const boundary = landed.lastJoinIndex(recipient) ?? -1;
|
|
195
|
+
if (boundary < 0)
|
|
196
|
+
return -1;
|
|
197
|
+
let evidence = boundary;
|
|
198
|
+
for (const act of squareState.acts) {
|
|
199
|
+
if (act.index <= boundary || act.index <= evidence || act.actor === undefined)
|
|
200
|
+
continue;
|
|
201
|
+
if (sameName(act.actor, recipient)
|
|
202
|
+
|| squareState.runtime.observations?.[recipient]?.[formatActivityId(act.index)]?.state === 'seen') {
|
|
203
|
+
evidence = act.index;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return evidence;
|
|
207
|
+
}
|
|
208
|
+
/** The last public act the participant was verifiably present for, or -1 when there is no evidence. */
|
|
209
|
+
export function presenceAnchor(squareState, name, landed = replayLandedAudiences(squareState.acts)) {
|
|
210
|
+
const recipient = landed.resolveParticipant(name) ?? name;
|
|
211
|
+
const boundary = landed.lastJoinIndex(recipient) ?? -1;
|
|
212
|
+
if (boundary < 0)
|
|
213
|
+
return -1;
|
|
214
|
+
const evidence = presenceEvidenceCursor(squareState, recipient, landed);
|
|
215
|
+
for (let i = squareState.acts.length - 1; i >= 0; i--) {
|
|
216
|
+
const event = squareState.acts[i];
|
|
217
|
+
if (event.index <= boundary || event.index > evidence)
|
|
218
|
+
continue;
|
|
219
|
+
if (event.kind === 'say' || event.kind === 'done')
|
|
220
|
+
return event.index;
|
|
221
|
+
}
|
|
222
|
+
return -1;
|
|
223
|
+
}
|
|
187
224
|
export function freshWatchLease(squareState, name, at = Date.now()) {
|
|
188
225
|
const key = canonicalRuntimeName(squareState, name);
|
|
189
226
|
const lease = watchLease(squareState, key);
|
package/dist/square-actions.js
CHANGED
|
@@ -29,7 +29,7 @@ export async function catchUp(square, name, options = {}, project) {
|
|
|
29
29
|
const deadline = Date.now() + idle;
|
|
30
30
|
while (true) {
|
|
31
31
|
if (!await assertLiveOwner(square, name)) {
|
|
32
|
-
throw new SquareError('already_joined',
|
|
32
|
+
throw new SquareError('already_joined', `✕ ${participantIdentity(name)} already stands here — another session holds the name`);
|
|
33
33
|
}
|
|
34
34
|
const attempt = await square.artifact.transact((state, version) => {
|
|
35
35
|
const decision = decideCatch(state, name, options, square.clock(), project);
|
|
@@ -192,7 +192,7 @@ export async function takeover(square, name, _oldSessionIds = []) {
|
|
|
192
192
|
return committed;
|
|
193
193
|
});
|
|
194
194
|
if (outcome.status === 'busy')
|
|
195
|
-
throw new SquareError('already_joined',
|
|
195
|
+
throw new SquareError('already_joined', `✕ ${participantIdentity(name)} already stands here — another session holds the name`);
|
|
196
196
|
return { name: outcome.result.name, activities: outcome.result.stored.map(exposeActivity), epoch: outcome.epoch };
|
|
197
197
|
}
|
|
198
198
|
const committed = await commitLifecycle();
|
|
@@ -227,7 +227,7 @@ export async function implicitJoin(square, name) {
|
|
|
227
227
|
}
|
|
228
228
|
export async function express(square, name, body, options = {}) {
|
|
229
229
|
if (!await assertLiveOwner(square, name)) {
|
|
230
|
-
throw new SquareError('already_joined',
|
|
230
|
+
throw new SquareError('already_joined', `✕ ${participantIdentity(name)} already stands here — another session holds the name`);
|
|
231
231
|
}
|
|
232
232
|
const now = square.clock();
|
|
233
233
|
const reply = options.reply === undefined ? undefined : parseRequiredActivityId(options.reply);
|
|
@@ -280,7 +280,7 @@ async function landCore(square, verb, actor, body = '', fence = {}) {
|
|
|
280
280
|
// be completed by a stale done — an old expected epoch refuses instead of appending.
|
|
281
281
|
const commit = async () => {
|
|
282
282
|
if (verb === 'done' && !await assertLiveOwner(square, actor, fence.expectedEpoch)) {
|
|
283
|
-
throw new SquareError('already_done',
|
|
283
|
+
throw new SquareError('already_done', `✕ ${participantIdentity(actor)} already stands here — another session holds the name`);
|
|
284
284
|
}
|
|
285
285
|
const now = square.clock();
|
|
286
286
|
return square.artifact.transact((state) => {
|
package/dist/stream.js
CHANGED
|
@@ -52,6 +52,6 @@ export async function cmdStreamNdjson(squarePath, recipient, start = { kind: 'ta
|
|
|
52
52
|
}
|
|
53
53
|
export async function cmdStream(squarePath) {
|
|
54
54
|
process.stderr.write('✕ interactive stream was removed\n');
|
|
55
|
-
process.stderr.write(
|
|
55
|
+
process.stderr.write(`square --location ${quoteShell(path.resolve(squarePath))} stream --ndjson\n`);
|
|
56
56
|
process.exitCode = 2;
|
|
57
57
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const PALETTE = {
|
|
2
|
+
blocked: '\x1b[38;5;203m',
|
|
3
|
+
release: '\x1b[38;5;114m',
|
|
4
|
+
changed: '\x1b[38;5;179m',
|
|
5
|
+
dim: '\x1b[38;5;244m',
|
|
6
|
+
match: '\x1b[38;5;222m\x1b[1m',
|
|
7
|
+
};
|
|
8
|
+
export function style(role, text, opts = {}) {
|
|
9
|
+
if (!(opts.stream ?? process.stdout).isTTY || (opts.env ?? process.env).NO_COLOR !== undefined || text === '') {
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
return `${PALETTE[role]}${text}\x1b[0m`;
|
|
13
|
+
}
|
package/dist/views.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
1
|
+
import { formatActivityId, parseActivityId, replayLandedAudiences } 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';
|
|
5
5
|
import { SquareError, nameKey } from './model.js';
|
|
6
|
-
import { countSays, currentHold, foldedState, freshWatchLease, inSquareCount, isCurrentlyJoined, resolveRosterName, rosterNames, watchTerminalStatus } from './runtime.js';
|
|
6
|
+
import { countSays, currentHold, foldedState, freshWatchLease, inSquareCount, isCurrentlyJoined, presenceAnchor, resolveRosterName, rosterNames, watchTerminalStatus } from './runtime.js';
|
|
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)}`);
|
|
@@ -40,10 +40,11 @@ function statuses(square, state) {
|
|
|
40
40
|
}
|
|
41
41
|
function anchors(state, delivery) {
|
|
42
42
|
const result = {};
|
|
43
|
+
const landed = replayLandedAudiences(state.acts);
|
|
43
44
|
for (const name of delivery.participants()) {
|
|
44
|
-
const
|
|
45
|
-
if (
|
|
46
|
-
result[
|
|
45
|
+
const anchor = presenceAnchor(state, name, landed);
|
|
46
|
+
if (anchor >= 0)
|
|
47
|
+
result[anchor] = [...(result[anchor] ?? []), name];
|
|
47
48
|
}
|
|
48
49
|
return result;
|
|
49
50
|
}
|
package/dist/wake-attempts.d.ts
CHANGED
|
@@ -7,6 +7,16 @@ export interface WakeAttention {
|
|
|
7
7
|
actIndex: number;
|
|
8
8
|
recipient: string;
|
|
9
9
|
}
|
|
10
|
+
export interface WakeReleaseDiagnostic {
|
|
11
|
+
readonly at: number;
|
|
12
|
+
readonly attention: WakeAttention;
|
|
13
|
+
readonly routeKind?: WakeRouteKind;
|
|
14
|
+
readonly attemptN?: number;
|
|
15
|
+
readonly session?: string;
|
|
16
|
+
readonly signature?: string;
|
|
17
|
+
readonly message?: string;
|
|
18
|
+
readonly diagnostic?: unknown;
|
|
19
|
+
}
|
|
10
20
|
export type WakeAttempt = ProjectedWakeAttempt;
|
|
11
21
|
export interface WakeDispatchLease {
|
|
12
22
|
readonly leaseId: string;
|
|
@@ -36,6 +46,14 @@ export declare function readWakeAttempts(opts?: {
|
|
|
36
46
|
now?: number;
|
|
37
47
|
env?: NodeJS.ProcessEnv;
|
|
38
48
|
}): Promise<WakeAttempt[]>;
|
|
49
|
+
export declare function redactWakeDiagnostic(value: unknown, env?: NodeJS.ProcessEnv): unknown;
|
|
50
|
+
export declare function readWakeReleaseDiagnostics(opts?: {
|
|
51
|
+
location?: string;
|
|
52
|
+
attention?: WakeAttention;
|
|
53
|
+
sessionId?: string;
|
|
54
|
+
now?: number;
|
|
55
|
+
env?: NodeJS.ProcessEnv;
|
|
56
|
+
}): Promise<WakeReleaseDiagnostic[]>;
|
|
39
57
|
export declare function recordWakeAttempt(attempt: Omit<WakeAttempt, 'at'> & {
|
|
40
58
|
at?: number;
|
|
41
59
|
}, env?: NodeJS.ProcessEnv): Promise<WakeAttempt>;
|
package/dist/wake-attempts.js
CHANGED
|
@@ -5,6 +5,7 @@ import { canonicalSquarePath } from './registry.js';
|
|
|
5
5
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
6
6
|
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
7
7
|
import { terminalWakeEvidence } from './square-projections.js';
|
|
8
|
+
import { redactDiagnostic } from './diagnostic-redaction.js';
|
|
8
9
|
export { hasAttemptableWakeRoute, isWakeRouteAttemptable, terminalWakeEvidence } from './square-projections.js';
|
|
9
10
|
const VALID_OUTCOMES = new Set(['accepted', 'unknown', 'failed']);
|
|
10
11
|
function ledger(env) {
|
|
@@ -44,17 +45,43 @@ export async function readWakeAttempts(opts = {}) {
|
|
|
44
45
|
const keys = await Promise.all(scoped.map((attempt) => wakeAttentionKey(attempt.attention)));
|
|
45
46
|
return scoped.filter((_, index) => keys[index] === expected);
|
|
46
47
|
}
|
|
47
|
-
function
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
export function redactWakeDiagnostic(value, env = process.env) {
|
|
49
|
+
return redactDiagnostic(value, env.PASEO_PASSWORD);
|
|
50
|
+
}
|
|
51
|
+
export async function readWakeReleaseDiagnostics(opts = {}) {
|
|
52
|
+
const now = opts.now ?? Date.now();
|
|
53
|
+
const env = opts.env ?? process.env;
|
|
54
|
+
const rows = await ledger(env).listEvidence({
|
|
55
|
+
kind: 'wake',
|
|
56
|
+
location: opts.location ?? opts.attention?.squarePath,
|
|
57
|
+
includeReleased: true,
|
|
58
|
+
now,
|
|
59
|
+
});
|
|
60
|
+
const expected = opts.attention === undefined ? undefined : await wakeAttentionKey(opts.attention);
|
|
61
|
+
const releases = [];
|
|
62
|
+
for (const row of rows) {
|
|
63
|
+
if (row.outcome !== 'released')
|
|
64
|
+
continue;
|
|
65
|
+
const actIndex = parseActivityId(row.activity);
|
|
66
|
+
if (actIndex === undefined || row.at === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
if (opts.sessionId !== undefined && row.session !== opts.sessionId)
|
|
69
|
+
continue;
|
|
70
|
+
const attention = { squarePath: row.location, recipient: row.participant, actIndex };
|
|
71
|
+
if (expected !== undefined && JSON.stringify([row.location, formatActivityId(actIndex), nameKey(row.participant)]) !== expected)
|
|
72
|
+
continue;
|
|
73
|
+
releases.push({
|
|
74
|
+
at: row.at,
|
|
75
|
+
attention,
|
|
76
|
+
...(row.routeKind === undefined ? {} : { routeKind: row.routeKind }),
|
|
77
|
+
...(row.attemptN === undefined ? {} : { attemptN: row.attemptN }),
|
|
78
|
+
...(row.session === undefined ? {} : { session: row.session }),
|
|
79
|
+
...(row.signature === undefined ? {} : { signature: row.signature }),
|
|
80
|
+
...(row.message === undefined ? {} : { message: redactWakeDiagnostic(row.message, env) }),
|
|
81
|
+
...(row.diagnostic === undefined ? {} : { diagnostic: redactWakeDiagnostic(row.diagnostic, env) }),
|
|
82
|
+
});
|
|
56
83
|
}
|
|
57
|
-
return
|
|
84
|
+
return releases.sort((left, right) => right.at - left.at);
|
|
58
85
|
}
|
|
59
86
|
export async function recordWakeAttempt(attempt, env = process.env) {
|
|
60
87
|
const value = { ...attempt, at: attempt.at ?? Date.now() };
|
|
@@ -63,7 +90,7 @@ export async function recordWakeAttempt(attempt, env = process.env) {
|
|
|
63
90
|
if (value.outcome !== 'accepted' && !value.signature) {
|
|
64
91
|
throw new Error(`${value.outcome} wake attempts require a transport signature.`);
|
|
65
92
|
}
|
|
66
|
-
const safe =
|
|
93
|
+
const safe = redactWakeDiagnostic(value, env);
|
|
67
94
|
const session = safe.session ?? safe.signature ?? `route:${safe.routeKind}`;
|
|
68
95
|
const claim = await ledger(env).claimEvidence({ location: safe.attention.squarePath, participant: safe.attention.recipient, session, activity: formatActivityId(safe.attention.actIndex), kind: 'wake', leaseMs: 5000 });
|
|
69
96
|
if (claim.status === 'acquired')
|
package/dist/watch.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { setTimeout as sleep } from 'node:timers/promises';
|
|
2
2
|
import { isSquareError, } from './model.js';
|
|
3
|
-
import { SLEEP_MS, STALE_MS, WATCH_HEARTBEAT_MS, nowMs, } from './runtime.js';
|
|
3
|
+
import { SLEEP_MS, STALE_MS, WATCH_HEARTBEAT_MS, inSquareCount, nowMs, } from './runtime.js';
|
|
4
4
|
import { openSquare } from './square-file-adapter.js';
|
|
5
5
|
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, renderWatchReplaceMissing, renderWatchOutput, renderWatchReplaced, renderWatchStatus, participantCommandPrefix, joinRecoveryCommand, participantsRecoveryCommand, withPathOutput, } from './presentation.js';
|
|
9
|
+
import { renderWatchForceTakeover, renderWatchAlreadyActive, renderWatchReplaceMissing, renderWatchOutput, renderWatchReplaced, renderWatchStatus, formatRefusal, participantCommandPrefix, joinRecoveryCommand, participantsRecoveryCommand, withPathOutput, } from './presentation.js';
|
|
10
10
|
import { hasAutomaticDeliveryIdentity } from './registry.js';
|
|
11
11
|
import { parseActivityId } from './square-core.js';
|
|
12
12
|
function watchStatusExitCode(status) {
|
|
@@ -37,7 +37,7 @@ function watchOutputResult(squarePath, presentation, name, caught, opts = {}) {
|
|
|
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
|
|
40
|
+
? `\n○ ${caught.remaining} matching ${caught.remaining === 1 ? 'activity remains' : 'activities remain'}\n${catchContinuationCommand(squarePath, name, opts)}`
|
|
41
41
|
: ''),
|
|
42
42
|
remaining: caught.remaining,
|
|
43
43
|
...(opts.status ? { status: opts.status } : {}),
|
|
@@ -61,7 +61,7 @@ function writeWatchOutput(squarePath, name, presentation, stdout, remaining, sta
|
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
63
|
const fallback = showCatchHint && remaining === 0
|
|
64
|
-
?
|
|
64
|
+
? `${participantCommandPrefix(squarePath, name)} catch --idle 30m\n stay available for new activity`
|
|
65
65
|
: '';
|
|
66
66
|
process.stdout.write(withPathOutput(squarePath, [stdout.trimEnd(), fallback].filter(Boolean).join('\n\n').trimEnd(), headerOpts));
|
|
67
67
|
}
|
|
@@ -109,7 +109,7 @@ function installWatchInterruptHandler(square, squarePath, name, currentLeaseId)
|
|
|
109
109
|
const onInterrupt = () => {
|
|
110
110
|
void (async () => {
|
|
111
111
|
await endWatch(square, name, currentLeaseId());
|
|
112
|
-
process.stdout.write(withPathOutput(squarePath, '
|
|
112
|
+
process.stdout.write(withPathOutput(squarePath, '○ you step back — catch stopped'));
|
|
113
113
|
process.exit(130);
|
|
114
114
|
})().catch((error) => {
|
|
115
115
|
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
@@ -144,19 +144,30 @@ async function cmdWatchNow(squarePath, name, opts) {
|
|
|
144
144
|
await closeOpenSquare(square);
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
|
+
async function countParticipants(square) {
|
|
148
|
+
try {
|
|
149
|
+
return inSquareCount((await square.artifact.read()).state);
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
147
155
|
/** `false` is reserved for a quiet --now; idle completion preserves its existing sweep boundary. */
|
|
148
156
|
export async function cmdWatch(squarePath, name, opts) {
|
|
149
157
|
let square;
|
|
158
|
+
let participantCount;
|
|
150
159
|
try {
|
|
151
160
|
square = await openSquare(squarePath, { clock: nowMs });
|
|
161
|
+
participantCount = inSquareCount((await square.artifact.read()).state);
|
|
152
162
|
name = (await resolveParticipant(square, name)).name;
|
|
153
163
|
}
|
|
154
164
|
catch (err) {
|
|
155
165
|
if (isSquareError(err)) {
|
|
156
|
-
|
|
166
|
+
const bodyLines = [err.message];
|
|
157
167
|
// The caller's own name has never joined; only a join admits it.
|
|
158
|
-
if (
|
|
159
|
-
|
|
168
|
+
if (/^✕ .* has never stepped into/.test(err.message))
|
|
169
|
+
bodyLines.push(joinRecoveryCommand(squarePath, name));
|
|
170
|
+
process.stderr.write(formatRefusal(squarePath, bodyLines, participantCount === undefined ? {} : { participantCount }));
|
|
160
171
|
process.exit(err.code === 'not_found' ? 1 : 2);
|
|
161
172
|
}
|
|
162
173
|
throw err;
|
|
@@ -169,12 +180,14 @@ export async function cmdWatch(squarePath, name, opts) {
|
|
|
169
180
|
}
|
|
170
181
|
}
|
|
171
182
|
catch (err) {
|
|
183
|
+
const participantCount = await countParticipants(square);
|
|
172
184
|
await closeOpenSquare(square).catch(() => undefined);
|
|
173
185
|
if (isSquareError(err)) {
|
|
174
|
-
|
|
186
|
+
const bodyLines = [err.message];
|
|
175
187
|
// A filter target is unknown, not the caller; the roster is the useful next read.
|
|
176
|
-
if (
|
|
177
|
-
|
|
188
|
+
if (/^✕ .*(has never stepped into|is not standing in this square)/.test(err.message))
|
|
189
|
+
bodyLines.push(participantsRecoveryCommand(squarePath));
|
|
190
|
+
process.stderr.write(formatRefusal(squarePath, bodyLines, participantCount === undefined ? {} : { participantCount }));
|
|
178
191
|
process.exit(err.code === 'not_found' ? 1 : 2);
|
|
179
192
|
}
|
|
180
193
|
throw err;
|
package/extensions/square-pi.js
CHANGED
|
@@ -31,12 +31,20 @@ export function renderPiInbox(inbox) {
|
|
|
31
31
|
|
|
32
32
|
const NOTIFY_BODY_MAX = 80;
|
|
33
33
|
|
|
34
|
+
function framePiMessage(content) {
|
|
35
|
+
return `\n${content.replace(/^\n+/, '').replace(/\n+$/, '')}\n`;
|
|
36
|
+
}
|
|
37
|
+
|
|
34
38
|
export function summarizePendingForNotify(pending) {
|
|
35
39
|
const lines = pending.flatMap((membership) => membership.notifications.map((note) => {
|
|
36
|
-
const verb = note.route === 'bell'
|
|
40
|
+
const verb = note.route === 'bell'
|
|
41
|
+
? 'rang the bell'
|
|
42
|
+
: note.route === 'mention'
|
|
43
|
+
? `called your name (${membership.name})`
|
|
44
|
+
: `spoke within your earshot (${membership.name})`;
|
|
37
45
|
const body = note.body.replace(/\s+/g, ' ').trim();
|
|
38
46
|
const clipped = body.length > NOTIFY_BODY_MAX ? `${body.slice(0, NOTIFY_BODY_MAX).trimEnd()}…` : body;
|
|
39
|
-
return `${note.actor} ${verb}
|
|
47
|
+
return `${note.actor} ${verb}${clipped ? ` — ${clipped}` : ''}`;
|
|
40
48
|
}));
|
|
41
49
|
if (lines.length === 0) return undefined;
|
|
42
50
|
return lines.length === 1 ? `■ square · ${lines[0]}` : `■ square · ${lines[0]} (+${lines.length - 1} more)`;
|
|
@@ -203,10 +211,11 @@ export default function squarePiExtension(pi) {
|
|
|
203
211
|
// reaches the model; a brief notify tells the human what just landed.
|
|
204
212
|
const summary = summarizePendingForNotify(pending);
|
|
205
213
|
if (summary) ui?.notify?.(summary, 'info');
|
|
206
|
-
const
|
|
214
|
+
const messageContent = framePiMessage(content);
|
|
215
|
+
const landing = waitForLanding(messageContent, signal, 'steer');
|
|
207
216
|
try {
|
|
208
217
|
Promise.resolve(pi.sendMessage(
|
|
209
|
-
{ customType: 'square', content, display: false },
|
|
218
|
+
{ customType: 'square', content: messageContent, display: false },
|
|
210
219
|
{ deliverAs: 'steer', triggerTurn: true },
|
|
211
220
|
)).catch((error) => landing.ack.settle(error));
|
|
212
221
|
} catch (error) {
|
|
@@ -266,10 +275,11 @@ export default function squarePiExtension(pi) {
|
|
|
266
275
|
const signal = watcherAbort.signal;
|
|
267
276
|
void automaticSessionStart('pi', sessionId, sessionCwd, sessionEnv(sessionId)).then((context) => {
|
|
268
277
|
if (context === undefined || sessionId === undefined || token !== generation) return;
|
|
269
|
-
const
|
|
278
|
+
const messageContent = framePiMessage(context);
|
|
279
|
+
const landing = waitForLanding(messageContent, signal, 'nextTurn');
|
|
270
280
|
try {
|
|
271
281
|
Promise.resolve(pi.sendMessage(
|
|
272
|
-
{ customType: 'square', content:
|
|
282
|
+
{ customType: 'square', content: messageContent, display: true },
|
|
273
283
|
{ deliverAs: 'nextTurn' },
|
|
274
284
|
)).catch((error) => landing.ack.settle(error));
|
|
275
285
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astrosheep/square",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.61",
|
|
4
4
|
"description": "A shared public square where agents join, catch activity, express, and step out when done.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"sync-manifests": "node scripts/sync-manifests.mjs",
|
|
22
22
|
"release": "npm version patch && npm run sync-manifests -- --commit && npm publish && git push --follow-tags",
|
|
23
23
|
"prepublishOnly": "npm run sync-manifests && npm run build && npm run verify-release",
|
|
24
|
-
"test": "npm run build && node --test test/*.test.js",
|
|
25
|
-
"test:cli-process": "npm run build && node --test test/process/square-cli-render.test.js"
|
|
24
|
+
"test": "npm run build && node --import ./test/sandbox-env.js --test test/*.test.js",
|
|
25
|
+
"test:cli-process": "npm run build && node --import ./test/sandbox-env.js --test test/process/square-cli-render.test.js"
|
|
26
26
|
},
|
|
27
27
|
"keywords": [
|
|
28
28
|
"brainstorm",
|