@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/activity-feed.d.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import type { SquareRead } from './ports.js';
|
|
2
|
-
import type {
|
|
3
|
-
/**
|
|
4
|
-
export declare function
|
|
5
|
-
kind: 'say';
|
|
6
|
-
}>[];
|
|
2
|
+
import type { ExpressContext } from './square-facade.js';
|
|
3
|
+
/** A bounded, nonconsuming glimpse; seen rows beyond a cursor hole must not reappear. */
|
|
4
|
+
export declare function expressionContext(facts: SquareRead, name: string): ExpressContext;
|
package/dist/activity-feed.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { formatActivityId } from './square-core.js';
|
|
2
|
+
const CONTEXT_LIMIT = 5;
|
|
3
|
+
const PREVIEW_CHARS = 400;
|
|
4
|
+
/** A bounded, nonconsuming glimpse; seen rows beyond a cursor hole must not reappear. */
|
|
5
|
+
export function expressionContext(facts, name) {
|
|
6
|
+
const filter = { afterIndex: facts.participant(name)?.lastJoinIndex ?? -1, unseenOnly: true };
|
|
7
|
+
const page = facts.directed(name, filter, { edge: 'newest', limit: CONTEXT_LIMIT });
|
|
8
|
+
const activities = facts.activities(page.items.map(head => head.index)).filter(act => act.kind === 'say')
|
|
9
|
+
.sort((a, b) => a.index - b.index).map(act => {
|
|
10
|
+
const body = act.body ?? '(structured activity)';
|
|
11
|
+
// Keep the boundary between Unicode code points, including non-BMP characters.
|
|
12
|
+
const chars = Array.from(body.slice(0, PREVIEW_CHARS * 2));
|
|
13
|
+
const preview = chars.slice(0, PREVIEW_CHARS).join('');
|
|
14
|
+
return { id: formatActivityId(act.index), actor: act.actor, preview, truncated: preview.length < body.length };
|
|
15
|
+
});
|
|
16
|
+
return { activities, remaining: Math.max(0, facts.directedCount(name, filter) - activities.length) };
|
|
15
17
|
}
|
package/dist/artifact.js
CHANGED
|
@@ -120,10 +120,10 @@ const readMethods = [
|
|
|
120
120
|
'header', 'participant', 'roster', 'attention', 'incomingAttention', 'hold',
|
|
121
121
|
'activity', 'activityHeads', 'activities', 'activityCount', 'contextIndexes', 'sayOrdinals', 'recentSayTimes',
|
|
122
122
|
'audience', 'directed', 'directedAt', 'directedCount', 'pendingAudience',
|
|
123
|
-
'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', '
|
|
123
|
+
'receipt', 'receiptFacts', 'receiptAssignments', 'lease', 'participantCounts', 'seat', 'seatsForOwner',
|
|
124
124
|
'deliveryEvidence', 'deliveryByToken', 'cancelledThrough', 'deliveryDeferrals',
|
|
125
125
|
];
|
|
126
|
-
const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
|
|
126
|
+
const writeMethods = ['applyLanding', 'markSeen', 'putLease', 'removeLease', 'storeSeat', 'removeSeat', 'updateWakeAddress', 'reattachSeat', 'detachSeat', 'cancelThrough', 'claimDelivery', 'prepareDelivery', 'completeDelivery', 'observeNativeDelivery', 'putDeliveryDeferral', 'renewDeliveryDeferral', 'releaseDeliveryDeferral'];
|
|
127
127
|
/** One connection, ordered operations, guarded synchronous transaction capabilities. */
|
|
128
128
|
export function createArtifactPort(location, initial, externalSignal) {
|
|
129
129
|
let database;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pendingAtBoundary } from './inbox.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Owner, PendingMembership } from './model.js';
|
|
3
3
|
export { pendingAtBoundary };
|
|
4
4
|
export declare function renderPendingAtBoundary(inbox: PendingMembership[]): string;
|
|
5
5
|
/**
|
|
@@ -11,4 +11,4 @@ export declare function renderPendingAtBoundary(inbox: PendingMembership[]): str
|
|
|
11
11
|
* after success, captured-seat commits record only its included items without an eligibility veto.
|
|
12
12
|
* A source that cannot verify its reuse must read the inbox again.
|
|
13
13
|
*/
|
|
14
|
-
export declare function presentPendingAtBoundary<T>(identity:
|
|
14
|
+
export declare function presentPendingAtBoundary<T>(identity: Owner, present: (context: string) => T | Promise<T>, lookup?: (identity: Owner, env?: NodeJS.ProcessEnv, signal?: AbortSignal) => Promise<PendingMembership[]> | PendingMembership[], env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<T | undefined>;
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import { type ActivityId } from './square-core.js';
|
|
2
1
|
import { type StoredAct } from './model.js';
|
|
3
2
|
import type { ActivityIndex, DirectedFilter, SquareRead, SquareWrite } from './ports.js';
|
|
4
|
-
import type { CatchOptions } from './square-facade.js';
|
|
3
|
+
import type { CatchOptions, CatchReason, PerceivedActivity } from './square-facade.js';
|
|
5
4
|
export interface CatchSelection {
|
|
6
5
|
readonly participant: string;
|
|
7
|
-
readonly activities: readonly
|
|
8
|
-
readonly consumedThrough: ActivityId | null;
|
|
6
|
+
readonly activities: readonly PerceivedActivity[];
|
|
9
7
|
readonly remaining: number;
|
|
10
|
-
readonly sayNumbers: Readonly<Record<number, number>>;
|
|
11
8
|
readonly standingCount: number;
|
|
12
9
|
readonly held: boolean;
|
|
10
|
+
readonly deliveryAvailable: boolean;
|
|
13
11
|
readonly now: number;
|
|
14
12
|
readonly ownActivityCount: number;
|
|
15
13
|
readonly hardCap: number | null;
|
|
16
|
-
readonly terminalStatus: 'capped' | 'quorum' | undefined;
|
|
14
|
+
readonly terminalStatus: Extract<CatchReason, 'capped' | 'quorum'> | undefined;
|
|
17
15
|
readonly hint?: string;
|
|
18
16
|
}
|
|
19
17
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
package/dist/catch-decisions.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
2
|
-
import {
|
|
2
|
+
import { terminalStatusFor } from './runtime.js';
|
|
3
3
|
import { SquareError } from './model.js';
|
|
4
4
|
import { resolveKnownName } from './decisions.js';
|
|
5
|
+
import { toPerceivedActivity } from './views.js';
|
|
5
6
|
import { catchHint } from './hints.js';
|
|
6
7
|
/** Capture a selected page (or an empty completion) without opening another resource. */
|
|
7
8
|
export function observeCatchSelection(facts, participant, activities, filter, at) {
|
|
8
9
|
if (typeof at !== 'number' || !Number.isFinite(at))
|
|
9
10
|
throw new SquareError('invalid_args', 'Catch time must be a finite number.');
|
|
10
|
-
const cursor = readCursor(facts, participant);
|
|
11
11
|
const counts = facts.participantCounts(), header = facts.header(), current = facts.participant(participant);
|
|
12
12
|
return {
|
|
13
|
-
participant, activities
|
|
13
|
+
participant, activities: activities.map(activity => toPerceivedActivity(facts, activity, participant)),
|
|
14
14
|
remaining: facts.directedCount(participant, filter),
|
|
15
|
-
sayNumbers: activities.length === 0 ? {} : facts.sayOrdinals(activities.map((item) => item.index)),
|
|
16
15
|
standingCount: counts.joined, held: facts.hold().active, now: at,
|
|
16
|
+
deliveryAvailable: facts.seat(participant)?.wakeAddress !== undefined,
|
|
17
17
|
ownActivityCount: current.sayCount, hardCap: header.hardCap,
|
|
18
18
|
terminalStatus: terminalStatusFor(current, header.hardCap, counts),
|
|
19
19
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createArtifactPort } from './artifact.js';
|
|
2
|
+
import { decodeHarnessOwner } from './owner.js';
|
|
2
3
|
import { indexedSquareEntries } from './square-index.js';
|
|
3
4
|
const holdReasons = new Set(['explicit-setting', 'managed-setting', 'repo-setting', 'invalid-setting', 'bypass-default', 'mode-unknown', 'mode-mismatch', 'no-mode-asserted']);
|
|
4
5
|
const dropReasons = new Set(['expired', 'refused', 'dropped', 'discarded']);
|
|
@@ -13,12 +14,17 @@ export async function recordClaudeDeliveryStatus(input, env = process.env) {
|
|
|
13
14
|
|| (input.state === 'held' ? !holdReasons.has(input.reason ?? '')
|
|
14
15
|
: input.state === 'dropped' ? !dropReasons.has(input.reason ?? '') : input.reason !== undefined))
|
|
15
16
|
return false;
|
|
16
|
-
|
|
17
|
-
const
|
|
17
|
+
const paths = new Set((await indexedSquareEntries({}, env)).filter(entry => {
|
|
18
|
+
const coordinate = decodeHarnessOwner(entry.owner);
|
|
19
|
+
return coordinate?.channel === 'claude-code' && coordinate.session === input.sessionId;
|
|
20
|
+
}).map(entry => entry.location));
|
|
21
|
+
for (const location of paths) {
|
|
22
|
+
const artifact = createArtifactPort(location);
|
|
18
23
|
try {
|
|
19
24
|
const { value: match } = await artifact.read((facts) => facts.roster().flatMap((participant) => {
|
|
20
25
|
const seat = facts.seat(participant.name);
|
|
21
|
-
if (seat === undefined || seat.
|
|
26
|
+
if (seat === undefined || seat.attachment !== 'attached' || seat.wakeAddress?.kind !== 'claude-native'
|
|
27
|
+
|| seat.wakeAddress.address.sessionId !== input.sessionId)
|
|
22
28
|
return [];
|
|
23
29
|
const evidence = facts.deliveryEvidence(seat.participant, seat.generation).find((row) => row.correlation?.harness === 'claude' && row.correlation.messageId === input.messageId && row.claimToken !== undefined);
|
|
24
30
|
return evidence === undefined ? [] : [{ seat, evidence }];
|
|
@@ -26,7 +32,7 @@ export async function recordClaudeDeliveryStatus(input, env = process.env) {
|
|
|
26
32
|
if (match.length === 0)
|
|
27
33
|
continue;
|
|
28
34
|
const { seat, evidence } = match[0];
|
|
29
|
-
const ref = { participant: seat.participant,
|
|
35
|
+
const ref = { participant: seat.participant, owner: seat.owner, generation: seat.generation };
|
|
30
36
|
return (await artifact.transactAs({ kind: 'participant', seat: ref }, (facts) => facts.observeNativeDelivery({ seat: ref, claimToken: evidence.claimToken, observation: {
|
|
31
37
|
eventId: input.eventId, messageId: input.messageId, state: input.state,
|
|
32
38
|
...(input.reason === undefined ? {} : { reason: input.reason }), observedAt: input.observedAt,
|
package/dist/claude-delivery.js
CHANGED
|
@@ -2,9 +2,10 @@ import { stat } from 'node:fs/promises';
|
|
|
2
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
3
3
|
import { createArtifactPort } from './artifact.js';
|
|
4
4
|
import { presentPending } from './presentation-operations.js';
|
|
5
|
-
import {
|
|
6
|
-
import { sameBody } from './model.js';
|
|
5
|
+
import { nativePendingPreview } from './inbox.js';
|
|
7
6
|
import { parseActivityId } from './square-core.js';
|
|
7
|
+
import { decodeHarnessOwner } from './owner.js';
|
|
8
|
+
import { indexedSquareEntries } from './square-index.js';
|
|
8
9
|
import { asSeatRef } from './seats.js';
|
|
9
10
|
import { writeClaudeNative } from './packages/agent-delivery/src/claude-native.js';
|
|
10
11
|
const marker = /(?:^|\n)\[square-inbox:([a-zA-Z0-9-]+)\](?:\n|$)/g;
|
|
@@ -22,8 +23,9 @@ function previewFacts(facts, request) {
|
|
|
22
23
|
const index = parseActivityId(request.activity);
|
|
23
24
|
if (index === undefined || request.route.kind !== 'claude-native' || request.route.address.endpoint === undefined)
|
|
24
25
|
return undefined;
|
|
25
|
-
return nativePendingPreview(facts, { location: request.location, routeKind: 'claude-native',
|
|
26
|
-
|
|
26
|
+
return nativePendingPreview(facts, { location: request.location, routeKind: 'claude-native', generation: request.seat.generation,
|
|
27
|
+
address: request.route.address,
|
|
28
|
+
participant: request.participant, actIndex: index, now: Date.now() });
|
|
27
29
|
}
|
|
28
30
|
async function preview(artifact, request) {
|
|
29
31
|
return (await artifact.read((facts) => previewFacts(facts, request))).value;
|
|
@@ -31,7 +33,7 @@ async function preview(artifact, request) {
|
|
|
31
33
|
async function writeClaudeWake(sessionId, endpoint, messageId, payload, timeoutMs, signal) {
|
|
32
34
|
const result = await writeClaudeNative({ sessionId, endpoint, messageId }, payload, { deadline: Date.now() + timeoutMs, signal });
|
|
33
35
|
if (result.outcome === 'unavailable')
|
|
34
|
-
return { outcome: 'failed',
|
|
36
|
+
return { outcome: 'failed', signature: 'claude_endpoint_unavailable', message: result.message };
|
|
35
37
|
return { outcome: 'unknown', signature: 'native_admission_unconfirmed', message: result.outcome === 'written' ? 'Claude accepted the local write; native admission is unconfirmed.' : result.message };
|
|
36
38
|
}
|
|
37
39
|
export function createClaudeWakeTransport(env = process.env) {
|
|
@@ -69,13 +71,15 @@ async function dispatchClaude(request, timeoutMs, beforeSend, env = process.env)
|
|
|
69
71
|
return { outcome: 'not-capable', diagnostic: 'Claude activity changed before native write.' };
|
|
70
72
|
if (signal.aborted || !await beforeSend())
|
|
71
73
|
return { outcome: 'not-capable', diagnostic: 'Claude activity was consumed before native write.' };
|
|
72
|
-
|
|
74
|
+
const sessionId = request.route.address.sessionId;
|
|
75
|
+
if (sessionId === undefined)
|
|
76
|
+
return { outcome: 'not-capable', diagnostic: 'Claude native route has no session address.' };
|
|
77
|
+
return writeClaudeWake(sessionId, request.route.address.endpoint, messageId, payload, timeoutMs, signal);
|
|
73
78
|
}
|
|
74
79
|
function currentPayload(facts, request) {
|
|
75
80
|
const seat = facts.seat(request.participant), evidence = facts.deliveryByToken(request.claimToken);
|
|
76
|
-
if (seat === undefined || evidence === undefined || seat.generation !== evidence.generation
|
|
77
|
-
|| seat.generation !== request.seat.generation ||
|
|
78
|
-
|| seat.body.channel !== 'claude-code' || seat.body.session !== request.route.sessionId
|
|
81
|
+
if (seat === undefined || seat.attachment !== 'attached' || evidence === undefined || seat.generation !== evidence.generation
|
|
82
|
+
|| seat.generation !== request.seat.generation || seat.owner !== request.seat.owner
|
|
79
83
|
|| evidence.activity !== request.activity || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
|
|
80
84
|
|| seat.wakeAddress?.kind !== 'claude-native' || !sameAddress(seat.wakeAddress.address, request.route.address))
|
|
81
85
|
return undefined;
|
|
@@ -87,7 +91,11 @@ function currentPayload(facts, request) {
|
|
|
87
91
|
}
|
|
88
92
|
async function rowsForSession(sessionId, env, tokens) {
|
|
89
93
|
const rows = [];
|
|
90
|
-
const
|
|
94
|
+
const candidates = (await indexedSquareEntries({}, env)).filter(entry => {
|
|
95
|
+
const coordinate = decodeHarnessOwner(entry.owner);
|
|
96
|
+
return coordinate?.channel === 'claude-code' && coordinate.session === sessionId;
|
|
97
|
+
});
|
|
98
|
+
const paths = new Set(candidates.map(candidate => candidate.location));
|
|
91
99
|
for (const location of paths) {
|
|
92
100
|
const artifact = createArtifactPort(location);
|
|
93
101
|
try {
|
|
@@ -101,7 +109,7 @@ async function rowsForSession(sessionId, env, tokens) {
|
|
|
101
109
|
const item = index === undefined ? undefined : facts.activity(index);
|
|
102
110
|
if (seat === undefined || item?.kind !== 'say')
|
|
103
111
|
return [row];
|
|
104
|
-
const route = { location, participant: seat.participant,
|
|
112
|
+
const route = { location, participant: seat.participant,
|
|
105
113
|
...evidence.route, updatedAt: evidence.at };
|
|
106
114
|
const request = { location, participant: seat.participant, activity: evidence.activity,
|
|
107
115
|
actor: item.actor, seat: asSeatRef(seat), claimToken: token, attemptN: evidence.attemptN, route, artifact };
|
package/dist/claude-mod.js
CHANGED
|
@@ -7,11 +7,13 @@ import { processActNotificationsOnce } from './notifications.js';
|
|
|
7
7
|
import { sweepPending } from './delivery-operations.js';
|
|
8
8
|
import { beginClaudeMcpCall, endClaudeMcpCall } from './claude-mcp-context.js';
|
|
9
9
|
import { asSeatRef } from './seats.js';
|
|
10
|
-
import { projectSessionSeats
|
|
11
|
-
import {
|
|
10
|
+
import { projectSessionSeats } from './inbox.js';
|
|
11
|
+
import { restoreSessionSeats } from './session-lifecycle.js';
|
|
12
|
+
import { harnessOwner } from './owner.js';
|
|
13
|
+
import { InternalSquareError, SquareError } from './model.js';
|
|
12
14
|
/** Persisted hook state keeps its wire coordinate; internal consumers keep the located seat. */
|
|
13
|
-
function claudeBinding({ location, seat }) {
|
|
14
|
-
return { location, participant: seat.participant, session:
|
|
15
|
+
function claudeBinding({ location, seat }, sessionId) {
|
|
16
|
+
return { location, participant: seat.participant, session: sessionId, generation: seat.generation };
|
|
15
17
|
}
|
|
16
18
|
/** JSON stdin bridge. Identity is explicit per operation; process.env is never rewritten. */
|
|
17
19
|
export async function runClaudeMod(inputText, env = process.env) {
|
|
@@ -33,29 +35,29 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
33
35
|
}
|
|
34
36
|
const scoped = operationEnv('claude', input.sessionId, env);
|
|
35
37
|
const signal = AbortSignal.timeout(2500);
|
|
36
|
-
const
|
|
38
|
+
const owner = harnessOwner({ channel: 'claude-code', session: input.sessionId, ...(input.agentId === undefined ? {} : { agent: input.agentId }) });
|
|
37
39
|
const supported = process.platform === 'darwin' && typeof input.endpoint === 'string' && input.endpoint.startsWith('/');
|
|
38
|
-
const sessionSeats = () => projectSessionSeats({
|
|
40
|
+
const sessionSeats = async () => (await projectSessionSeats({ owner, env }, signal)).filter(member => member.seat.attachment === 'attached');
|
|
39
41
|
if (input.operation === 'end') {
|
|
40
42
|
const retired = [];
|
|
41
43
|
const captured = [...input.bindings ?? []];
|
|
42
44
|
// Discover committed late joins at the pinned end boundary, never later owners.
|
|
43
45
|
if (input.endedAt !== undefined)
|
|
44
|
-
for (const member of await
|
|
46
|
+
for (const member of await projectSessionSeats({ owner, env }, signal)) {
|
|
45
47
|
if (member.joinedAt > input.endedAt || captured.some((item) => item.location === member.location
|
|
46
48
|
&& item.participant === member.seat.participant && item.generation === member.seat.generation))
|
|
47
49
|
continue;
|
|
48
|
-
captured.push(claudeBinding(member));
|
|
50
|
+
captured.push(claudeBinding(member, input.sessionId));
|
|
49
51
|
}
|
|
50
52
|
for (const binding of captured) {
|
|
51
53
|
if (binding.session !== input.sessionId)
|
|
52
54
|
continue;
|
|
53
|
-
const square = await Square.at({ path: binding.location, env: scoped
|
|
54
|
-
participant: binding.participant });
|
|
55
|
+
const square = await Square.at({ path: binding.location, env: scoped });
|
|
55
56
|
try {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const detached = binding.generation === undefined ? null
|
|
58
|
+
: await square.detach(binding.participant, { owner, generation: binding.generation });
|
|
59
|
+
if (detached)
|
|
60
|
+
retired.push(binding);
|
|
59
61
|
}
|
|
60
62
|
finally {
|
|
61
63
|
await square.close();
|
|
@@ -67,41 +69,10 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
67
69
|
}
|
|
68
70
|
const recovery = new Set();
|
|
69
71
|
const cancelledBindings = [...input.cancelledBindings ?? []];
|
|
70
|
-
if (input.operation === 'start'
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const seen = new Set();
|
|
75
|
-
for (const candidate of candidates) {
|
|
76
|
-
if (signal.aborted)
|
|
77
|
-
break;
|
|
78
|
-
const probe = createArtifactPort(candidate.location, undefined, signal);
|
|
79
|
-
let names;
|
|
80
|
-
try {
|
|
81
|
-
names = (await probe.read(facts => facts.completedSeatsForBody(body).map(seat => seat.participant), signal)).value;
|
|
82
|
-
}
|
|
83
|
-
catch (error) {
|
|
84
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
85
|
-
throw new SquareError('unavailable', `Square is unavailable at ${candidate.location}`);
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
|
-
finally {
|
|
89
|
-
await probe.close();
|
|
90
|
-
}
|
|
91
|
-
for (const participant of names) {
|
|
92
|
-
const key = `${candidate.location}\0${participant}`;
|
|
93
|
-
if (seen.has(key))
|
|
94
|
-
continue;
|
|
95
|
-
seen.add(key);
|
|
96
|
-
const square = await Square.at({ path: candidate.location, env: scoped, body, participant });
|
|
97
|
-
try {
|
|
98
|
-
await square.join(participant);
|
|
99
|
-
}
|
|
100
|
-
finally {
|
|
101
|
-
await square.close();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
72
|
+
if (input.operation === 'start') {
|
|
73
|
+
await restoreSessionSeats(owner, scoped, supported ? {
|
|
74
|
+
kind: 'claude-native', address: { sessionId: input.sessionId, endpoint: input.endpoint, version: input.version, platform: 'darwin', loaded: 'true' },
|
|
75
|
+
} : undefined, signal);
|
|
105
76
|
}
|
|
106
77
|
if (input.operation === 'start' || input.operation === 'reconcile' || input.operation === 'cancel' || input.cancelAt !== undefined) {
|
|
107
78
|
for (const member of await sessionSeats()) {
|
|
@@ -109,21 +80,21 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
109
80
|
break;
|
|
110
81
|
const { location, seat: capturedSeat, joinedAt } = member;
|
|
111
82
|
const matches = (item) => item !== undefined
|
|
112
|
-
&& item.location === location && item.participant === capturedSeat.participant && item.session ===
|
|
83
|
+
&& item.location === location && item.participant === capturedSeat.participant && item.session === input.sessionId
|
|
113
84
|
&& (item.generation === undefined || item.generation === capturedSeat.generation);
|
|
114
85
|
const expected = input.bindings?.find(matches);
|
|
115
86
|
const cancelled = input.cancelledBindings?.find(matches) ?? (input.operation === 'cancel' ? expected : undefined);
|
|
116
87
|
const lateCancelledJoin = input.operation === 'cancel' && input.cancelAt !== undefined
|
|
117
88
|
&& joinedAt <= input.cancelAt && expected === undefined;
|
|
118
89
|
if (lateCancelledJoin && !cancelledBindings.some(matches))
|
|
119
|
-
cancelledBindings.push(claudeBinding(member));
|
|
90
|
+
cancelledBindings.push(claudeBinding(member, input.sessionId));
|
|
120
91
|
if (input.operation !== 'start' && input.operation !== 'reconcile' && !lateCancelledJoin && expected === undefined)
|
|
121
92
|
continue;
|
|
122
93
|
const artifact = createArtifactPort(location, undefined, signal);
|
|
123
94
|
try {
|
|
124
95
|
const { value: currentSeat } = await artifact.read((facts) => facts.seat(capturedSeat.participant), signal);
|
|
125
|
-
if (currentSeat === undefined || currentSeat.generation !== capturedSeat.generation
|
|
126
|
-
||
|
|
96
|
+
if (currentSeat === undefined || currentSeat.attachment !== 'attached' || currentSeat.generation !== capturedSeat.generation
|
|
97
|
+
|| currentSeat.owner !== capturedSeat.owner)
|
|
127
98
|
continue;
|
|
128
99
|
const seat = asSeatRef(currentSeat);
|
|
129
100
|
const existing = currentSeat.wakeAddress?.kind === 'claude-native' ? currentSeat.wakeAddress : undefined;
|
|
@@ -193,7 +164,7 @@ export async function runClaudeMod(inputText, env = process.env) {
|
|
|
193
164
|
await processActNotificationsOnce(location, index, { env: { ...env, SQUARE_NOTIFY_DELIVERY_WAIT_MS: '200' } });
|
|
194
165
|
}
|
|
195
166
|
}
|
|
196
|
-
return `${JSON.stringify({ available: supported, diagnostic: supported ? undefined : 'Native inbox unavailable: requires macOS Claude with an interactive loaded mod and its real endpoint.', bindings: members.map(claudeBinding), cancelledBindings })}\n`;
|
|
167
|
+
return `${JSON.stringify({ available: supported, diagnostic: supported ? undefined : 'Native inbox unavailable: requires macOS Claude with an interactive loaded mod and its real endpoint.', bindings: members.map(member => claudeBinding(member, input.sessionId)), cancelledBindings })}\n`;
|
|
197
168
|
}
|
|
198
169
|
if (!['guard', 'admitted', 'stored'].includes(input.operation) || typeof input.text !== 'string')
|
|
199
170
|
throw new Error('Unknown Claude bridge operation.');
|
package/dist/cli/context.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
+
import { type Owner } from '../square-api.js';
|
|
1
2
|
import { type HardCap } from '../model.js';
|
|
2
3
|
import { Square } from '../square-api.js';
|
|
3
|
-
import type {
|
|
4
|
+
import type { OperationControl } from '../square-facade.js';
|
|
4
5
|
export interface CommandContext {
|
|
5
6
|
cwd: string;
|
|
6
7
|
env: NodeJS.ProcessEnv;
|
|
7
8
|
squarePath?: string;
|
|
8
9
|
name?: string;
|
|
10
|
+
owner?: Owner;
|
|
9
11
|
homeDir: string;
|
|
10
12
|
command: string;
|
|
13
|
+
json?: boolean;
|
|
11
14
|
}
|
|
12
15
|
export interface CommandSpec<Intent = unknown, Result = void> {
|
|
13
16
|
parse(argv: string[], context: CommandContext): Intent;
|
|
@@ -30,18 +33,20 @@ export interface ParsedGlobalArgs {
|
|
|
30
33
|
explicitSquarePath: boolean;
|
|
31
34
|
multipleSquares: boolean;
|
|
32
35
|
name?: string;
|
|
36
|
+
owner?: Owner;
|
|
33
37
|
args: string[];
|
|
34
38
|
}
|
|
35
39
|
export declare function parseGlobalArgs(rawArgs: string[]): Promise<ParsedGlobalArgs>;
|
|
36
40
|
export declare function locationIsRequired(command: string): boolean;
|
|
37
41
|
export declare function defaultContext(command: string, squarePath?: string, name?: string): CommandContext;
|
|
38
42
|
export declare function squareInput(context: CommandContext): {
|
|
39
|
-
wakeAddress?: import("../model.js").WakeAddress | undefined;
|
|
40
|
-
body?: Body | undefined;
|
|
41
43
|
cwd: string;
|
|
42
44
|
env: NodeJS.ProcessEnv;
|
|
43
45
|
path: string | undefined;
|
|
44
|
-
|
|
46
|
+
};
|
|
47
|
+
export declare function commandIdentity(env: NodeJS.ProcessEnv, explicit?: Owner): {
|
|
48
|
+
owner: Owner;
|
|
49
|
+
wakeAddress?: import('../model.js').WakeAddress;
|
|
45
50
|
};
|
|
46
51
|
/** Adapter lifetime only; each callback uses the actual public resource API. */
|
|
47
52
|
export declare function withSquare<T>(context: CommandContext, run: (square: Square) => Promise<T>, control?: OperationControl): Promise<T>;
|
package/dist/cli/context.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
|
+
import { harnessOwner, localUserOwner } from '../square-api.js';
|
|
3
|
+
import { harnessSessionSources } from '../participant-identity.js';
|
|
2
4
|
import { codexWakeAddress } from '../codex-address.js';
|
|
3
5
|
import { commandUsageHint } from '../help.js';
|
|
4
6
|
import { parseParticipantList, validateName } from '../model.js';
|
|
5
7
|
import { Square, squareClock } from '../square-api.js';
|
|
6
|
-
import { harnessSessionSources } from '../participant-identity.js';
|
|
7
8
|
export async function readStdin() {
|
|
8
9
|
try {
|
|
9
10
|
let content = '';
|
|
@@ -110,12 +111,18 @@ export async function parseGlobalArgs(rawArgs) {
|
|
|
110
111
|
const args = [...rawArgs];
|
|
111
112
|
let requestedPath;
|
|
112
113
|
let name;
|
|
114
|
+
let owner;
|
|
113
115
|
for (let index = 0; index < args.length; index++) {
|
|
114
116
|
if (args[index] === '--location') {
|
|
115
117
|
requestedPath = requireValue(args, index, args[index]);
|
|
116
118
|
args.splice(index, 2);
|
|
117
119
|
index -= 1;
|
|
118
120
|
}
|
|
121
|
+
else if (args[index] === '--owner') {
|
|
122
|
+
owner = requireValue(args, index, args[index]);
|
|
123
|
+
args.splice(index, 2);
|
|
124
|
+
index -= 1;
|
|
125
|
+
}
|
|
119
126
|
else if (args[index] === '--as') {
|
|
120
127
|
name = requireValue(args, index, args[index]);
|
|
121
128
|
args.splice(index, 2);
|
|
@@ -132,39 +139,37 @@ export async function parseGlobalArgs(rawArgs) {
|
|
|
132
139
|
fail(`✕ ${command} needs a square location\nsquare ls`);
|
|
133
140
|
}
|
|
134
141
|
const squarePath = requestedPath ?? configured;
|
|
135
|
-
return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, args };
|
|
142
|
+
return { squarePath, explicitSquarePath: explicitSquarePath || configured !== undefined, multipleSquares: false, name, owner, args };
|
|
136
143
|
}
|
|
137
144
|
export function locationIsRequired(command) {
|
|
138
145
|
return LOCATION_REQUIRED_COMMANDS.has(command);
|
|
139
146
|
}
|
|
140
147
|
export function defaultContext(command, squarePath, name) {
|
|
141
|
-
return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, homeDir: os.homedir() };
|
|
148
|
+
return { command, cwd: process.cwd(), env: { ...process.env }, squarePath, name, owner: undefined, homeDir: os.homedir() };
|
|
142
149
|
}
|
|
143
150
|
export function squareInput(context) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
const
|
|
150
|
-
|
|
151
|
-
return
|
|
152
|
-
});
|
|
153
|
-
const native =
|
|
154
|
-
if (native.length > 1)
|
|
155
|
-
throw new Error('Multiple harness execution coordinates are present; select one explicit body.');
|
|
156
|
-
const paseo = coordinates.find((coordinate) => coordinate.channel === 'paseo');
|
|
157
|
-
// A child marker without a reliable agent id must never inherit the parent or Paseo identity.
|
|
151
|
+
return { cwd: context.cwd, env: context.env, path: context.squarePath };
|
|
152
|
+
}
|
|
153
|
+
export function commandIdentity(env, explicit) {
|
|
154
|
+
if (explicit !== undefined)
|
|
155
|
+
return { owner: explicit };
|
|
156
|
+
const configured = env.SQUARE_OWNER;
|
|
157
|
+
if (configured !== undefined)
|
|
158
|
+
return { owner: configured };
|
|
159
|
+
const selected = harnessSessionSources.flatMap(({ variable, channel }) => env[variable]?.trim() ? [{ channel, session: env[variable].trim() }] : []);
|
|
160
|
+
const native = selected.filter(source => source.channel !== 'paseo');
|
|
158
161
|
if (env.CLAUDE_CODE_CHILD_SESSION === '1')
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
162
|
+
throw new Error('Harness identity is incomplete; provide --owner explicitly.');
|
|
163
|
+
if (native.length > 1)
|
|
164
|
+
throw new Error('Multiple harness execution coordinates are present; select one explicit owner.');
|
|
165
|
+
const paseo = selected.find(source => source.channel === 'paseo');
|
|
166
|
+
const coordinate = native[0] ?? paseo;
|
|
167
|
+
if (coordinate === undefined)
|
|
168
|
+
return { owner: localUserOwner() };
|
|
169
|
+
const owner = harnessOwner({ channel: coordinate.channel, session: coordinate.session });
|
|
170
|
+
const wakeAddress = coordinate.channel === 'codex' ? codexWakeAddress(coordinate.session, env)
|
|
166
171
|
: paseo === undefined ? undefined : { kind: 'paseo', address: { agentId: paseo.session } };
|
|
167
|
-
return {
|
|
172
|
+
return { owner, ...(wakeAddress === undefined ? {} : { wakeAddress }) };
|
|
168
173
|
}
|
|
169
174
|
/** Adapter lifetime only; each callback uses the actual public resource API. */
|
|
170
175
|
export async function withSquare(context, run, control) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type Square } from '../square-api.js';
|
|
2
|
+
import type { Owner } from '../square-api.js';
|
|
2
3
|
import type { OperationControl, ExpressOptions } from '../square-facade.js';
|
|
3
4
|
export { renderExpression } from '../operation-presentation.js';
|
|
4
5
|
export interface ActivityOptions extends Omit<ExpressOptions, 'reply'> {
|
|
6
|
+
readonly recoveryOwner?: Owner;
|
|
5
7
|
readonly reply?: string;
|
|
6
8
|
readonly metadataJson?: string;
|
|
7
9
|
readonly bodyProvided?: boolean;
|
|
@@ -10,4 +12,4 @@ export declare function saveActivityDraft(squarePath: string, name: string, body
|
|
|
10
12
|
/** Retry choices are transport syntax, never a second admission decision. */
|
|
11
13
|
export declare function activityRetryCommand(squarePath: string, name: string, opts: ActivityOptions, force?: boolean): string;
|
|
12
14
|
/** Body adaptation, terminal IO and drafts only; Participant owns every attempt and wait. */
|
|
13
|
-
export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string | undefined, opts: ActivityOptions): Promise<void>;
|
|
15
|
+
export declare function executeExpression(open: (control?: OperationControl) => Promise<Square>, squarePath: string, requestedName: string | undefined, rawInput: string | undefined, opts: ActivityOptions, json?: boolean, resolveOwner?: Owner | (() => Owner)): Promise<void>;
|