@astrosheep/square 0.3.93 → 0.3.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/activity-feed.d.ts +3 -5
- package/dist/activity-feed.js +16 -14
- package/dist/artifact.js +2 -2
- package/dist/boundary-presentation.d.ts +2 -2
- package/dist/catch-decisions.d.ts +4 -6
- package/dist/catch-decisions.js +4 -4
- package/dist/claude-delivery-status.js +10 -4
- package/dist/claude-delivery.js +19 -11
- package/dist/claude-mod.js +24 -53
- package/dist/cli/context.d.ts +9 -4
- package/dist/cli/context.js +30 -25
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +26 -14
- package/dist/cli/json.d.ts +3 -0
- package/dist/cli/json.js +14 -0
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.d.ts +1 -3
- package/dist/cli/observation-commands.js +43 -56
- package/dist/cli/program.js +2 -2
- package/dist/cli/registry.js +6 -3
- package/dist/cli/square-commands.d.ts +1 -1
- package/dist/cli/square-commands.js +46 -54
- package/dist/codex-hook.d.ts +3 -2
- package/dist/codex-hook.js +13 -4
- package/dist/codex-queue.js +4 -4
- package/dist/decisions.d.ts +5 -11
- package/dist/decisions.js +9 -20
- package/dist/delivery-deferral.js +2 -2
- package/dist/delivery-health.d.ts +0 -1
- package/dist/delivery-health.js +1 -3
- package/dist/delivery-operations.d.ts +2 -2
- package/dist/delivery-operations.js +38 -38
- package/dist/delivery.d.ts +1 -1
- package/dist/delivery.js +1 -1
- package/dist/help.js +12 -10
- package/dist/inbox.d.ts +9 -22
- package/dist/inbox.js +23 -27
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +3 -0
- package/dist/mcp.js +23 -24
- package/dist/model.d.ts +8 -15
- package/dist/model.js +4 -7
- package/dist/notifications.d.ts +1 -1
- package/dist/notifications.js +6 -5
- package/dist/operation-presentation.d.ts +5 -4
- package/dist/operation-presentation.js +50 -45
- package/dist/owner.d.ts +11 -0
- package/dist/owner.js +32 -0
- package/dist/paseo-delivery.d.ts +4 -1
- package/dist/paseo-delivery.js +7 -12
- package/dist/paseo-state.d.ts +2 -2
- package/dist/paseo-state.js +6 -4
- package/dist/plugins/opencode/delivery.js +27 -15
- package/dist/ports.d.ts +13 -33
- package/dist/presentation-operations.js +4 -4
- package/dist/presentation.d.ts +5 -8
- package/dist/presentation.js +33 -43
- package/dist/runtime.d.ts +1 -7
- package/dist/runtime.js +0 -14
- package/dist/seats.d.ts +2 -3
- package/dist/seats.js +7 -15
- package/dist/session-lifecycle.d.ts +6 -2
- package/dist/session-lifecycle.js +39 -11
- package/dist/sqlite-square.d.ts +36 -22
- package/dist/sqlite-square.js +138 -161
- package/dist/square-api.d.ts +15 -10
- package/dist/square-api.js +26 -22
- package/dist/square-core.d.ts +0 -5
- package/dist/square-core.js +0 -9
- package/dist/square-domain.d.ts +4 -2
- package/dist/square-domain.js +56 -21
- package/dist/square-facade.d.ts +45 -87
- package/dist/square-index.d.ts +5 -13
- package/dist/square-index.js +13 -38
- package/dist/square-location.d.ts +1 -14
- package/dist/square-location.js +4 -70
- package/dist/square-migration-host-ledger.d.ts +2 -2
- package/dist/square-migration-host-ledger.js +6 -5
- package/dist/square-migration.d.ts +2 -1
- package/dist/square-migration.js +52 -30
- package/dist/square-sqlite.d.ts +6 -2
- package/dist/square-sqlite.js +54 -28
- package/dist/square-tools.d.ts +7 -10
- package/dist/square-tools.js +22 -54
- package/dist/stream.js +4 -5
- package/dist/views.d.ts +21 -10
- package/dist/views.js +38 -38
- package/dist/wakes.d.ts +1 -2
- package/dist/wakes.js +1 -1
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/guides/claude-native.md +18 -9
- package/guides/codex-delivery.md +17 -6
- package/guides/mcp-connection-lifetime.md +1 -1
- package/guides/mcp.md +3 -3
- package/guides/migration.md +142 -151
- package/guides/operation-cancellation.md +1 -1
- package/guides/participant-ownership.md +78 -47
- package/guides/pi-tools.md +33 -4
- package/guides/primitive-history-consolidation.md +1 -1
- package/guides/row-storage-design.md +8 -8
- package/guides/square-api.md +244 -88
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +27 -17
- package/plugins/paseo/client/palette.ts +18 -18
- package/plugins/paseo/client/square-message.tsx +17 -23
- package/plugins/pi/square-pi.js +17 -17
- package/dist/square-backend.d.ts +0 -13
- package/dist/square-backend.js +0 -1
- package/dist/square-stdio.d.ts +0 -22
- package/dist/square-stdio.js +0 -235
- package/dist/stdio-server.d.ts +0 -3
- package/dist/stdio-server.js +0 -212
- package/guides/backends.md +0 -193
package/dist/codex-queue.js
CHANGED
|
@@ -12,7 +12,7 @@ export class CodexQueueTransport {
|
|
|
12
12
|
const { address } = request.route;
|
|
13
13
|
const threadId = address.threadId?.trim();
|
|
14
14
|
if (!threadId) {
|
|
15
|
-
return { outcome: 'failed',
|
|
15
|
+
return { outcome: 'failed', signature: 'invalid_address', message: 'Codex route has no thread id.' };
|
|
16
16
|
}
|
|
17
17
|
const env = this.opts.env ?? process.env;
|
|
18
18
|
const payload = request.artifact === undefined ? undefined
|
|
@@ -28,15 +28,15 @@ export class CodexQueueTransport {
|
|
|
28
28
|
});
|
|
29
29
|
const remaining = deadline - Date.now();
|
|
30
30
|
if (remaining <= 0) {
|
|
31
|
-
return { outcome: 'failed',
|
|
31
|
+
return { outcome: 'failed', signature: 'dispatch_budget_exhausted', message: 'The wake dispatch budget elapsed before queueing.' };
|
|
32
32
|
}
|
|
33
33
|
await agent.queue(payload, { timeoutMs: remaining });
|
|
34
34
|
return { outcome: 'accepted' };
|
|
35
35
|
}
|
|
36
36
|
catch (error) {
|
|
37
37
|
if (error instanceof DeliveryError && error.code === 'session_inactive') {
|
|
38
|
-
return { outcome: 'failed',
|
|
39
|
-
message: 'The Codex thread has no current Stop boundary for queueing.'
|
|
38
|
+
return { outcome: 'failed', signature: 'boundary_not_stopped',
|
|
39
|
+
message: 'The Codex thread has no current Stop boundary for queueing.' };
|
|
40
40
|
}
|
|
41
41
|
const message = error instanceof Error ? error.message : String(error);
|
|
42
42
|
if (!(error instanceof DeliveryError) || error.maybeDelivered)
|
package/dist/decisions.d.ts
CHANGED
|
@@ -5,21 +5,14 @@ type TimedAct<K extends Act['kind'] = Act['kind']> = Extract<Act, {
|
|
|
5
5
|
}> & {
|
|
6
6
|
at: number;
|
|
7
7
|
};
|
|
8
|
-
import type { ExpressResult, ParticipantSummary
|
|
9
|
-
export type { ParticipantSummary as ParticipantStatus, ParticipantPresence as CorePresenceState, UnreadActivitySummary, UnreadActivityPreview } from './square-facade.js';
|
|
8
|
+
import type { ExpressResult, ParticipantSummary, UnreadActivitySummary } from './square-facade.js';
|
|
10
9
|
export declare function resolveKnownName(facts: SquareRead, name: string): string;
|
|
11
10
|
export interface DecideJoinResult {
|
|
12
11
|
joinedName: string;
|
|
13
12
|
addParticipant: boolean;
|
|
14
13
|
joinAct?: TimedAct<'join'>;
|
|
15
14
|
}
|
|
16
|
-
export interface DecideImplicitJoinResult {
|
|
17
|
-
joinedName: string;
|
|
18
|
-
state: 'joined' | 'active' | 'done';
|
|
19
|
-
joinAct?: TimedAct<'join'>;
|
|
20
|
-
}
|
|
21
15
|
export declare function decideJoin(facts: SquareRead, name: string, now: number): DecideJoinResult;
|
|
22
|
-
export declare function decideImplicitJoin(facts: SquareRead, name: string, now: number): DecideImplicitJoinResult;
|
|
23
16
|
export type ActDecision = ({
|
|
24
17
|
type: 'sent';
|
|
25
18
|
act: TimedAct;
|
|
@@ -40,7 +33,7 @@ export type ActDecision = ({
|
|
|
40
33
|
type: 'bell_quota';
|
|
41
34
|
nextAt: number;
|
|
42
35
|
};
|
|
43
|
-
/** Count the directed suffix
|
|
36
|
+
/** Count the directed suffix and hydrate only its last activity by index per spelling. */
|
|
44
37
|
export declare function unreadActivitySummaries(facts: SquareRead, name: string, now: number): UnreadActivitySummary[];
|
|
45
38
|
export declare function decideAct(facts: SquareRead, input: {
|
|
46
39
|
name: string;
|
|
@@ -61,5 +54,6 @@ export declare function coreUnlisten(facts: SquareRead, actor: string, target: s
|
|
|
61
54
|
export declare function coreListening(facts: SquareRead, actor: string): readonly string[];
|
|
62
55
|
export declare function coreHold(facts: SquareRead, actor: string, body: string, now: number): TimedAct<'hold'>;
|
|
63
56
|
export declare function coreResume(facts: SquareRead, actor: string, now: number): TimedAct<'resume'>;
|
|
64
|
-
export declare function compareParticipantActivity(a: Pick<
|
|
65
|
-
export declare function coreParticipants(facts: SquareRead, now: number):
|
|
57
|
+
export declare function compareParticipantActivity(a: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>, b: Pick<ParticipantSummary, 'name' | 'lastActiveAt'>): number;
|
|
58
|
+
export declare function coreParticipants(facts: SquareRead, now: number): ParticipantSummary[];
|
|
59
|
+
export {};
|
package/dist/decisions.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SquareError, isSquareError, sameName,
|
|
1
|
+
import { SquareError, isSquareError, sameName, validateName, validateActivityMetadata, activityMetadataHasContent, } from './model.js';
|
|
2
2
|
import { participantIdentity } from './participant-identity.js';
|
|
3
3
|
import { UNREAD_BLOCK_GRACE_MS, freshWatchLease, readCursor, FACT_PAGE_SIZE, THROTTLE_WINDOW_MS } from './runtime.js';
|
|
4
4
|
import { formatActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
|
|
@@ -17,12 +17,6 @@ export function decideJoin(facts, name, now) {
|
|
|
17
17
|
return current?.joined ? { joinedName, addParticipant: false }
|
|
18
18
|
: { joinedName, addParticipant: current === undefined, joinAct: { kind: 'join', actor: joinedName, at: now } };
|
|
19
19
|
}
|
|
20
|
-
export function decideImplicitJoin(facts, name, now) {
|
|
21
|
-
validateName(name);
|
|
22
|
-
const current = facts.participant(name);
|
|
23
|
-
return current === undefined ? { joinedName: name, state: 'joined', joinAct: { kind: 'join', actor: name, at: now } }
|
|
24
|
-
: { joinedName: current.name, state: current.joined ? 'active' : 'done' };
|
|
25
|
-
}
|
|
26
20
|
function validateActivityName(name) {
|
|
27
21
|
try {
|
|
28
22
|
validateName(name);
|
|
@@ -54,34 +48,28 @@ function requireStanding(current) {
|
|
|
54
48
|
if (!current.joined)
|
|
55
49
|
throw new SquareError('not_joined', `${participantIdentity(current.name)} has not joined this square`);
|
|
56
50
|
}
|
|
57
|
-
/** Count the directed suffix
|
|
51
|
+
/** Count the directed suffix and hydrate only its last activity by index per spelling. */
|
|
58
52
|
export function unreadActivitySummaries(facts, name, now) {
|
|
59
53
|
let afterIndex = readCursor(facts, name);
|
|
60
|
-
const numbers = new Map();
|
|
61
54
|
const summaries = new Map();
|
|
62
55
|
while (true) {
|
|
63
56
|
const page = facts.directed(name, { afterIndex }, { edge: 'oldest', limit: FACT_PAGE_SIZE });
|
|
64
57
|
for (const item of page.items) {
|
|
65
|
-
const
|
|
66
|
-
const number = (numbers.get(key) ?? 0) + 1;
|
|
67
|
-
numbers.set(key, number);
|
|
68
|
-
const summary = summaries.get(item.actor) ?? { count: 0, latestAt: -Infinity, previews: [] };
|
|
58
|
+
const summary = summaries.get(item.actor) ?? { count: 0, latestAt: -Infinity, lastIndex: item.index };
|
|
69
59
|
summary.count++;
|
|
70
60
|
summary.latestAt = Math.max(summary.latestAt, item.at);
|
|
71
|
-
summary.
|
|
72
|
-
if (summary.previews.length > 3)
|
|
73
|
-
summary.previews.shift();
|
|
61
|
+
summary.lastIndex = item.index;
|
|
74
62
|
summaries.set(item.actor, summary);
|
|
75
63
|
}
|
|
76
64
|
if (!page.hasMore)
|
|
77
65
|
break;
|
|
78
66
|
afterIndex = page.items.at(-1).index;
|
|
79
67
|
}
|
|
80
|
-
const selected = facts.activities([...summaries.values()].
|
|
68
|
+
const selected = facts.activities([...summaries.values()].map((item) => item.lastIndex));
|
|
81
69
|
const byIndex = new Map(selected.map((item) => [item.index, item]));
|
|
82
70
|
return [...summaries].map(([name, summary]) => ({
|
|
83
71
|
name, count: summary.count, latestActivityAgeMs: Math.max(0, now - summary.latestAt),
|
|
84
|
-
|
|
72
|
+
preview: byIndex.get(summary.lastIndex),
|
|
85
73
|
})).sort((a, b) => a.latestActivityAgeMs - b.latestActivityAgeMs || a.name.localeCompare(b.name));
|
|
86
74
|
}
|
|
87
75
|
export function decideAct(facts, input) {
|
|
@@ -219,10 +207,11 @@ export function coreParticipants(facts, now) {
|
|
|
219
207
|
lastActiveAt = Math.max(lastActiveAt ?? -Infinity, seenAt);
|
|
220
208
|
}
|
|
221
209
|
return {
|
|
222
|
-
name: current.name,
|
|
210
|
+
name: current.name, deliveryAvailable: facts.seat(current.name)?.wakeAddress !== undefined,
|
|
223
211
|
listening: facts.attention(current.name).filter((item) => item.kind === 'listen').map((item) => item.target),
|
|
224
212
|
activityCount: current.sayCount, lastActiveAt,
|
|
225
|
-
presence: current.done ? 'done' : !current.joined ? 'never-joined' :
|
|
213
|
+
presence: current.done ? 'done' : !current.joined ? 'never-joined' : facts.seat(current.name)?.attachment === 'detached' ? 'away'
|
|
214
|
+
: freshWatchLease(facts, current.name, now) !== undefined ? 'watching' : 'active',
|
|
226
215
|
unreadActivityCount: current.joined ? facts.directedCount(current.name, { afterIndex: readCursor(facts, current.name) }) : 0,
|
|
227
216
|
pendingMentionCount: current.joined ? facts.directedCount(current.name, { afterIndex: current.lastJoinIndex ?? -1, unseenOnly: true }) : 0,
|
|
228
217
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SquareError
|
|
1
|
+
import { SquareError } from './model.js';
|
|
2
2
|
export const MAX_DELIVERY_DEFERRAL_MS = 24 * 60 * 60 * 1000;
|
|
3
3
|
export function validateMetadataMatch(value) {
|
|
4
4
|
const fail = () => { throw new SquareError('invalid_args', 'Invalid metadata match.'); };
|
|
@@ -69,7 +69,7 @@ export function matchingDeferrals(deferrals, metadata, now) {
|
|
|
69
69
|
}
|
|
70
70
|
export function deliveryDeferralExpiry(facts, captured, index, now) {
|
|
71
71
|
const seat = facts.seat(captured.participant);
|
|
72
|
-
if (seat === undefined || seat.generation !== captured.generation ||
|
|
72
|
+
if (seat === undefined || seat.generation !== captured.generation || seat.owner !== captured.owner)
|
|
73
73
|
return undefined;
|
|
74
74
|
const leases = facts.deliveryDeferrals(seat.participant, seat.generation);
|
|
75
75
|
if (!leases.some(lease => lease.expiresAt > now))
|
package/dist/delivery-health.js
CHANGED
|
@@ -38,7 +38,6 @@ export async function classifyDeliveryHealth(squarePath, opts) {
|
|
|
38
38
|
routeKind: attemptRow.route.kind,
|
|
39
39
|
outcome: attemptRow.outcome,
|
|
40
40
|
attemptN: attemptRow.attemptN,
|
|
41
|
-
session: seat.body.session,
|
|
42
41
|
...(attemptRow.signature === undefined ? {} : { signature: attemptRow.signature }),
|
|
43
42
|
...(attemptRow.message === undefined ? {} : { message: attemptRow.message }),
|
|
44
43
|
...(attemptRow.diagnostic === undefined ? {} : { diagnostic: attemptRow.diagnostic }),
|
|
@@ -128,7 +127,7 @@ function formatRelease(release, now) {
|
|
|
128
127
|
const diagnostic = release.diagnostic === undefined ? '' : ` · ${displayField(JSON.stringify(release.diagnostic))}`;
|
|
129
128
|
return ` · ${formatActivityId(release.attention.actIndex)} → ${displayField(participantIdentity(release.attention.recipient))} · ${route} · ${reason} · ${age}${message}${diagnostic}`;
|
|
130
129
|
}
|
|
131
|
-
function deliveryAttempt(squarePath, row
|
|
130
|
+
function deliveryAttempt(squarePath, row) {
|
|
132
131
|
if (row.route?.kind === undefined || row.outcome === 'released' || row.outcome === 'dispatching')
|
|
133
132
|
return undefined;
|
|
134
133
|
return {
|
|
@@ -137,7 +136,6 @@ function deliveryAttempt(squarePath, row, session) {
|
|
|
137
136
|
routeKind: row.route.kind,
|
|
138
137
|
outcome: row.outcome,
|
|
139
138
|
attemptN: row.attemptN,
|
|
140
|
-
...(session === undefined ? {} : { session }),
|
|
141
139
|
...(row.signature === undefined ? {} : { signature: row.signature }),
|
|
142
140
|
...(row.message === undefined ? {} : { message: row.message }),
|
|
143
141
|
...(row.diagnostic === undefined ? {} : { diagnostic: row.diagnostic }),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { SquareArtifactPort, DeliverPendingInput
|
|
2
|
-
export declare function deliverPending(input: DeliverPendingInput): Promise<
|
|
1
|
+
import type { SquareArtifactPort, DeliverPendingInput } from './ports.js';
|
|
2
|
+
export declare function deliverPending(input: DeliverPendingInput): Promise<void>;
|
|
3
3
|
export declare function sweepPending(input: {
|
|
4
4
|
readonly artifact: SquareArtifactPort;
|
|
5
5
|
readonly location: string;
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
|
-
import { sameBody } from './model.js';
|
|
4
3
|
import { pendingHead, pendingHeads, pendingRecipients, presentationBlocksWake } from './delivery.js';
|
|
5
4
|
import { asSeatRef } from './seats.js';
|
|
6
|
-
import { canonicalPath } from './canonical-path.js';
|
|
7
5
|
import { freshWatchLease } from './runtime.js';
|
|
8
6
|
import { deliveryDeferralExpiry } from './delivery-deferral.js';
|
|
9
7
|
import { leaseOwnsNotification } from './delivery.js';
|
|
10
8
|
function routeFor(location, seat, now) {
|
|
11
|
-
if (seat.wakeAddress === undefined)
|
|
9
|
+
if (seat.attachment !== 'attached' || seat.wakeAddress === undefined)
|
|
12
10
|
return undefined;
|
|
13
11
|
return {
|
|
14
|
-
location, participant: seat.participant,
|
|
12
|
+
location, participant: seat.participant,
|
|
15
13
|
kind: seat.wakeAddress.kind, address: { ...seat.wakeAddress.address }, updatedAt: now,
|
|
16
14
|
};
|
|
17
15
|
}
|
|
@@ -19,8 +17,8 @@ async function currentness(artifact, request, now) {
|
|
|
19
17
|
const index = parseActivityId(request.activity);
|
|
20
18
|
const { value } = await artifact.read((facts) => {
|
|
21
19
|
const currentSeat = facts.seat(request.participant);
|
|
22
|
-
const seatCurrent = currentSeat !== undefined && currentSeat.generation === request.seat.generation
|
|
23
|
-
&&
|
|
20
|
+
const seatCurrent = currentSeat !== undefined && currentSeat.attachment === 'attached' && currentSeat.generation === request.seat.generation
|
|
21
|
+
&& currentSeat.owner === request.seat.owner
|
|
24
22
|
&& currentSeat.wakeAddress?.kind === request.route.kind
|
|
25
23
|
&& Object.keys(currentSeat.wakeAddress.address).length === Object.keys(request.route.address).length
|
|
26
24
|
&& Object.entries(currentSeat.wakeAddress.address).every(([key, val]) => request.route.address[key] === val);
|
|
@@ -59,7 +57,7 @@ async function candidates(artifact, requested, now = Date.now()) {
|
|
|
59
57
|
: pendingRecipients(facts, requested);
|
|
60
58
|
return heads.flatMap((head) => {
|
|
61
59
|
const seat = facts.seat(head.recipient);
|
|
62
|
-
return seat === undefined || deliveryDeferralExpiry(facts, seat, head.index, now) !== undefined ? [] : [{ seat, head }];
|
|
60
|
+
return seat === undefined || seat.attachment !== 'attached' || deliveryDeferralExpiry(facts, seat, head.index, now) !== undefined ? [] : [{ seat, head }];
|
|
63
61
|
});
|
|
64
62
|
});
|
|
65
63
|
return value;
|
|
@@ -81,19 +79,25 @@ function safeJson(value) {
|
|
|
81
79
|
return String(value);
|
|
82
80
|
}
|
|
83
81
|
export async function deliverPending(input) {
|
|
84
|
-
|
|
82
|
+
// The resource owner supplies its identity; delivery does not interpret it as a file.
|
|
83
|
+
const location = input.location;
|
|
85
84
|
const requested = input.activity === undefined ? undefined
|
|
86
85
|
: typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
|
|
87
86
|
if (input.activity !== undefined && requested === undefined)
|
|
88
|
-
return
|
|
87
|
+
return;
|
|
89
88
|
const pending = await candidates(input.artifact, requested, input.now ?? Date.now());
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
// Preserve order within a receiving owner; unrelated receivers need not wait for each other.
|
|
90
|
+
const receivers = new Map();
|
|
91
|
+
for (const candidate of pending) {
|
|
92
|
+
const key = candidate.seat.owner;
|
|
93
|
+
const group = receivers.get(key) ?? [];
|
|
94
|
+
group.push(candidate);
|
|
95
|
+
receivers.set(key, group);
|
|
96
|
+
}
|
|
97
|
+
const dispatch = async ({ seat, head }) => {
|
|
92
98
|
const route = routeFor(location, seat, input.now ?? Date.now());
|
|
93
|
-
if (route === undefined)
|
|
94
|
-
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
99
|
+
if (route === undefined)
|
|
100
|
+
return;
|
|
97
101
|
const request = {
|
|
98
102
|
location, participant: seat.participant, activity: formatActivityId(head.index), actor: head.actor,
|
|
99
103
|
seat: asSeatRef(seat), route,
|
|
@@ -102,14 +106,11 @@ export async function deliverPending(input) {
|
|
|
102
106
|
if (input.transport.probe !== undefined) {
|
|
103
107
|
try {
|
|
104
108
|
const probe = await input.transport.probe(route);
|
|
105
|
-
if (probe === false || typeof probe === 'object' && probe.outcome === 'not-capable')
|
|
106
|
-
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
+
if (probe === false || typeof probe === 'object' && probe.outcome === 'not-capable')
|
|
110
|
+
return;
|
|
109
111
|
}
|
|
110
112
|
catch {
|
|
111
|
-
|
|
112
|
-
continue;
|
|
113
|
+
return;
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
const claimToken = randomUUID();
|
|
@@ -117,7 +118,7 @@ export async function deliverPending(input) {
|
|
|
117
118
|
const claim = await input.artifact.claimDelivery({ seat: request.seat, activity: request.activity, kind: 'wake',
|
|
118
119
|
route: seat.wakeAddress, at: input.now ?? Date.now(), leaseMs, claimToken }).catch(() => ({ status: 'stale' }));
|
|
119
120
|
if (claim.status !== 'acquired')
|
|
120
|
-
|
|
121
|
+
return;
|
|
121
122
|
const withClaim = { ...request, claimToken, attemptN: claim.attemptN };
|
|
122
123
|
let final;
|
|
123
124
|
let suppressed = false;
|
|
@@ -138,7 +139,7 @@ export async function deliverPending(input) {
|
|
|
138
139
|
catch { /* unverifiable */ }
|
|
139
140
|
if (!before?.current) {
|
|
140
141
|
await releaseClaim(input.artifact, request.seat, request.activity, claimToken, 'pre_send_revalidation_failed', before === undefined ? 'Square delivery state could not be verified.' : 'The notification is no longer current.', before);
|
|
141
|
-
|
|
142
|
+
return;
|
|
142
143
|
}
|
|
143
144
|
let outcome;
|
|
144
145
|
try {
|
|
@@ -149,32 +150,31 @@ export async function deliverPending(input) {
|
|
|
149
150
|
}
|
|
150
151
|
if (suppressed) {
|
|
151
152
|
await releaseClaim(input.artifact, request.seat, request.activity, claimToken, 'pre_send_revalidation_failed', 'The notification changed before the final send check.', final ?? { observationAvailable: false });
|
|
152
|
-
|
|
153
|
+
return;
|
|
153
154
|
}
|
|
154
155
|
if (outcome.outcome === 'not-capable') {
|
|
155
156
|
await releaseClaim(input.artifact, request.seat, request.activity, claimToken, 'transport_not_capable', undefined, outcome.diagnostic);
|
|
156
|
-
|
|
157
|
-
continue;
|
|
157
|
+
return;
|
|
158
158
|
}
|
|
159
|
-
attempted += 1;
|
|
160
159
|
const mapped = outcome.outcome === 'accepted' ? 'accepted'
|
|
161
160
|
: outcome.outcome === 'unknown' ? 'unknown' : 'failed';
|
|
162
|
-
|
|
161
|
+
await input.artifact.completeDelivery({ seat: request.seat, activity: request.activity, claimToken,
|
|
163
162
|
outcome: mapped, at: Date.now(),
|
|
164
163
|
...('signature' in outcome && outcome.signature !== undefined ? { signature: outcome.signature } : {}),
|
|
165
164
|
...('message' in outcome && outcome.message !== undefined ? { message: outcome.message } : {}),
|
|
166
165
|
...('diagnostic' in outcome && outcome.diagnostic !== undefined ? { diagnostic: safeJson(outcome.diagnostic) } : {}),
|
|
167
166
|
}).catch(() => false);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
167
|
+
};
|
|
168
|
+
const groups = receivers.values();
|
|
169
|
+
const settled = await Promise.allSettled(Array.from({ length: Math.min(4, receivers.size) }, async () => {
|
|
170
|
+
for (const group of groups)
|
|
171
|
+
for (const candidate of group)
|
|
172
|
+
await dispatch(candidate);
|
|
173
|
+
}));
|
|
174
|
+
// Even an unexpected failure must drain siblings before the caller closes its artifact.
|
|
175
|
+
for (const result of settled)
|
|
176
|
+
if (result.status === 'rejected')
|
|
177
|
+
throw result.reason;
|
|
178
178
|
}
|
|
179
179
|
export async function sweepPending(input) {
|
|
180
180
|
if (input.limit <= 0)
|
package/dist/delivery.d.ts
CHANGED
|
@@ -40,7 +40,7 @@ export declare function pendingPages(artifact: SquareArtifactPort, scan: Pending
|
|
|
40
40
|
at: number;
|
|
41
41
|
actor: string;
|
|
42
42
|
recipient: string;
|
|
43
|
-
route: "bell" | "
|
|
43
|
+
route: "bell" | "mention" | "reply" | "attention";
|
|
44
44
|
}[], void, unknown>;
|
|
45
45
|
/** A pass retains only its captured joins; routes and individual receipts are fresh. */
|
|
46
46
|
export declare function pendingFacts(facts: SquareRead, scan: PendingScan, heads: readonly DirectedHead[]): {
|
package/dist/delivery.js
CHANGED
|
@@ -8,7 +8,7 @@ export function wakePreview(facts, request) {
|
|
|
8
8
|
const head = index === undefined ? undefined : pendingHead(facts, request.participant, index);
|
|
9
9
|
const item = index === undefined ? undefined : facts.activity(index);
|
|
10
10
|
const seat = facts.seat(request.participant);
|
|
11
|
-
if (head === undefined || item?.kind !== 'say' || seat === undefined
|
|
11
|
+
if (head === undefined || item?.kind !== 'say' || seat === undefined || seat.attachment !== 'attached'
|
|
12
12
|
|| deliveryDeferralExpiry(facts, request.seat, item.index, Date.now()) !== undefined)
|
|
13
13
|
return undefined;
|
|
14
14
|
return renderSquareReminder([renderAttentionPreview({
|
package/dist/help.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { JSON_COMMANDS } from './cli/json.js';
|
|
1
2
|
const COMMANDS = [
|
|
2
3
|
{ names: ['help'], usage: 'help [command]', summary: 'Show the command index or help for one command.' },
|
|
3
4
|
{ names: ['version'], usage: 'version', summary: 'Print the installed version.', hiddenFromIndex: true },
|
|
@@ -52,9 +53,9 @@ const COMMANDS = [
|
|
|
52
53
|
details: ['Options:', ' --ndjson Emit one JSON event per line.', ' --for <name> With --ndjson, emit notifications for one participant.', ' --last <N> Emit the last N eligible activities first (default 10, maximum 100).', ' --after <id> Resume after a canonical activity id (act/12).'],
|
|
53
54
|
},
|
|
54
55
|
{
|
|
55
|
-
names: ['inbox'], usage: 'inbox --for-
|
|
56
|
+
names: ['inbox'], usage: 'inbox --for-owner <owner> [--limit <N>]', hiddenFromIndex: true,
|
|
56
57
|
summary: 'Inspect a bounded machine-local notification snapshot for a native session.',
|
|
57
|
-
details: ['Options:', ' --for-
|
|
58
|
+
details: ['Options:', ' --for-owner <id> Required opaque owner.', ' --limit <N> Snapshot memberships (default 20, maximum 100).'],
|
|
58
59
|
},
|
|
59
60
|
{ names: ['codex-hook'], usage: '{command}', summary: 'Present pending attention at one native agent boundary.', hiddenFromIndex: true },
|
|
60
61
|
{ names: ['claude-mod'], usage: '{command}', summary: 'Receive a structured operation from the loaded Claude mod.', hiddenFromIndex: true },
|
|
@@ -67,7 +68,7 @@ const COMMANDS = [
|
|
|
67
68
|
{
|
|
68
69
|
names: ['participants'], usage: 'participants [--limit <count>]', usesSquare: true, group: 'host',
|
|
69
70
|
summary: 'Show the participant roster and current states.',
|
|
70
|
-
details: ['Options:', ' --limit <count> Show the first count names in
|
|
71
|
+
details: ['Options:', ' --limit <count> Show the first count names in human text (default 20, maximum 100).'],
|
|
71
72
|
},
|
|
72
73
|
{ names: ['hold'], usage: '--as <name> hold [reason | -]', usesSquare: true, group: 'participant', summary: 'Raise a hand and pause participant activity.' },
|
|
73
74
|
{ names: ['resume'], usage: '--as <name> resume', usesSquare: true, group: 'participant', summary: 'Lower the raised hand and resume activity.' },
|
|
@@ -93,7 +94,7 @@ const COMMANDS = [
|
|
|
93
94
|
{
|
|
94
95
|
names: ['migrate'], usage: 'migrate', usesSquare: true, group: 'maintenance',
|
|
95
96
|
summary: 'Convert a supported older square to the current format in place.',
|
|
96
|
-
details: ['For the selected .square artifact only. Stop sessions using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'Versions 2
|
|
97
|
+
details: ['For the selected .square artifact only. Stop sessions using this artifact before conversion. Back up the stopped artifact, migrate it, then run square --location <path> doctor before starting updated clients.', 'Versions 2–6 migrate explicitly to format 7; format 6 attachment is preserved and Body coordinates become opaque owners. Versions 4/5 first gain attached seats; 4 also gains empty deferrals. Version 1, unrelated or unsupported artifacts refuse without a write. Names whose legacy owner and routes agree may share an owner; ambiguous names stay standing without custody. Rejoin with the same owner after migration to republish discovery. There is no --check, --backup or --force.'],
|
|
97
98
|
},
|
|
98
99
|
];
|
|
99
100
|
function definitionFor(command) {
|
|
@@ -117,10 +118,11 @@ export function renderGlobalHelp() {
|
|
|
117
118
|
'',
|
|
118
119
|
]);
|
|
119
120
|
return [
|
|
120
|
-
'Usage: square [--location <square>] [--as <name>] <command> [args...]',
|
|
121
|
-
'Environment: SQUARE_LOCATION sets the CLI
|
|
122
|
-
'
|
|
123
|
-
'
|
|
121
|
+
'Usage: square [--location <square>] [--as <name>] [--owner <id>] <command> [args...]',
|
|
122
|
+
'Environment: SQUARE_LOCATION sets the CLI local artifact path; square-accessing commands require it or --location. Use --as to select a participant.',
|
|
123
|
+
'Identity: --owner overrides SQUARE_OWNER, then a complete harness identity, then user:<OS user>@<host>. Incomplete or ambiguous harness identities refuse. Owners without wake use catch --idle.',
|
|
124
|
+
'Output: --json on join, express, catch, history, listen, ignore, unlisten, listening, hold, resume, done, status, participants and inbox prints the SDK result without observation; a participant handle becomes {name}.',
|
|
125
|
+
'History emits one JSON document; stream --ndjson emits one SDK record per line.',
|
|
124
126
|
'',
|
|
125
127
|
...commandLines,
|
|
126
128
|
"Run 'square <command> --help' for command options.",
|
|
@@ -132,7 +134,7 @@ export function renderSubcommandHelp(command) {
|
|
|
132
134
|
if (definition === undefined)
|
|
133
135
|
return undefined;
|
|
134
136
|
const aliases = definition.names.filter((name) => name !== command);
|
|
135
|
-
const usage = definition.usage.replace('{command}', command);
|
|
137
|
+
const usage = definition.usage.replace('{command}', command) + (JSON_COMMANDS.has(command) ? ' [--json]' : '');
|
|
136
138
|
return [
|
|
137
139
|
`Usage: square ${definition.usesSquare ? '[--location <square>] ' : ''}${usage}`,
|
|
138
140
|
...(aliases.length > 0 ? [`Aliases: ${aliases.join(', ')}`] : []),
|
|
@@ -151,7 +153,7 @@ export function helpRequest(rawArgs) {
|
|
|
151
153
|
const args = [];
|
|
152
154
|
for (let index = 0; index < rawArgs.length; index++) {
|
|
153
155
|
const arg = rawArgs[index];
|
|
154
|
-
if (arg === '--location' || arg === '--as') {
|
|
156
|
+
if (arg === '--location' || arg === '--as' || arg === '--owner') {
|
|
155
157
|
const value = rawArgs[index + 1];
|
|
156
158
|
if (value === undefined || value.startsWith('--'))
|
|
157
159
|
return undefined;
|
package/dist/inbox.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Owner, type LocatedSeat, type PendingMembership } from './model.js';
|
|
2
2
|
import type { SquareRead } from './ports.js';
|
|
3
3
|
export interface PendingWaitOptions {
|
|
4
4
|
signal?: AbortSignal;
|
|
@@ -14,15 +14,12 @@ export declare function pendingAtBoundary(inbox: readonly PendingMembership[]):
|
|
|
14
14
|
export declare function projectBoundaryEligibility(inbox: readonly PendingMembership[]): Promise<PendingMembership[]>;
|
|
15
15
|
export interface SessionBindingCandidate {
|
|
16
16
|
readonly location: string;
|
|
17
|
-
readonly
|
|
17
|
+
readonly owner: Owner;
|
|
18
18
|
}
|
|
19
19
|
/** HOME is a candidate path index; current seats and delivery evidence live in the artifact. */
|
|
20
20
|
export declare function projectSessionCandidates(input: SessionSeatQuery): Promise<readonly SessionBindingCandidate[]>;
|
|
21
21
|
export interface SessionSeatQuery {
|
|
22
|
-
readonly
|
|
23
|
-
readonly sessionId?: string;
|
|
24
|
-
readonly channel?: string;
|
|
25
|
-
readonly agent?: string;
|
|
22
|
+
readonly owner?: Owner;
|
|
26
23
|
readonly location?: string;
|
|
27
24
|
readonly env?: NodeJS.ProcessEnv;
|
|
28
25
|
}
|
|
@@ -36,31 +33,21 @@ export interface NativePendingPreview {
|
|
|
36
33
|
}
|
|
37
34
|
export type NativePendingPreviewInput = {
|
|
38
35
|
readonly location: string;
|
|
39
|
-
readonly
|
|
36
|
+
readonly generation: import('./model.js').ActivityId;
|
|
40
37
|
readonly participant: string;
|
|
41
38
|
readonly actIndex: number;
|
|
42
39
|
readonly cancelledThrough?: number;
|
|
43
40
|
readonly now: number;
|
|
44
41
|
} & ({
|
|
45
42
|
readonly routeKind: 'claude-native';
|
|
46
|
-
readonly address:
|
|
47
|
-
readonly endpoint: string;
|
|
48
|
-
};
|
|
43
|
+
readonly address: Readonly<Record<string, string>>;
|
|
49
44
|
} | {
|
|
50
45
|
readonly routeKind: 'opencode-server';
|
|
51
|
-
readonly address:
|
|
52
|
-
readonly sessionId: string;
|
|
53
|
-
};
|
|
46
|
+
readonly address: Readonly<Record<string, string>>;
|
|
54
47
|
});
|
|
55
|
-
/**
|
|
48
|
+
/** Current-generation native attention, owned by its exact wake address and catch state. */
|
|
56
49
|
export declare function nativePendingPreview(facts: SquareRead, input: NativePendingPreviewInput): NativePendingPreview | undefined;
|
|
57
|
-
export
|
|
58
|
-
readonly channel: string;
|
|
59
|
-
readonly session: string;
|
|
60
|
-
readonly allAgents?: true;
|
|
61
|
-
}
|
|
62
|
-
export type SessionIdentity = Body | SessionIdentityQuery;
|
|
63
|
-
export declare function sessionInbox(identity: SessionIdentity, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PendingMembership[]>;
|
|
50
|
+
export declare function sessionInbox(owner: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<PendingMembership[]>;
|
|
64
51
|
export interface SessionPendingObserver {
|
|
65
52
|
/** Read committed revisions before reusing content; filesystem hints are only for waiting. */
|
|
66
53
|
read(options?: Pick<PendingWaitOptions, 'signal' | 'excludeKeys'>): Promise<PendingMembership[]>;
|
|
@@ -68,4 +55,4 @@ export interface SessionPendingObserver {
|
|
|
68
55
|
close(): void;
|
|
69
56
|
}
|
|
70
57
|
/** Session-scoped projection over process-shared storage change detectors. */
|
|
71
|
-
export declare function observeSessionPending(
|
|
58
|
+
export declare function observeSessionPending(owner: Owner, suppliedEnv?: NodeJS.ProcessEnv): Promise<SessionPendingObserver>;
|