@astrosheep/square 0.4.0 → 0.4.2
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/artifact.d.ts +1 -3
- package/dist/artifact.js +12 -36
- package/dist/attention-presentation.d.ts +2 -22
- package/dist/attention-presentation.js +9 -12
- package/dist/boundary-presentation.d.ts +3 -14
- package/dist/boundary-presentation.js +29 -104
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.js +1 -7
- package/dist/claude-boundary-state.d.ts +20 -0
- package/dist/claude-boundary-state.js +84 -0
- package/dist/claude-hook.d.ts +2 -0
- package/dist/claude-hook.js +231 -0
- package/dist/claude-mcp-context.d.ts +1 -1
- package/dist/claude-mcp-context.js +12 -3
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +4 -1
- package/dist/cli/observation-commands.js +37 -4
- package/dist/cli/registry.js +5 -2
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -32
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +2 -2
- package/dist/delivery-deferral.d.ts +8 -17
- package/dist/delivery-deferral.js +0 -28
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-claude.js +3 -4
- package/dist/harness-links.js +63 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +4 -1
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -77
- package/dist/inbox.d.ts +6 -49
- package/dist/inbox.js +24 -371
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/mcp.js +2 -3
- package/dist/model.d.ts +7 -47
- package/dist/model.js +1 -7
- package/dist/notifications.d.ts +24 -20
- package/dist/notifications.js +39 -108
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -97
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +31 -90
- package/dist/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-square.d.ts +14 -23
- package/dist/sqlite-square.js +132 -139
- package/dist/square-api.js +4 -7
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +5 -17
- package/dist/square-domain.js +104 -221
- package/dist/square-facade.d.ts +18 -15
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-sqlite.d.ts +15 -25
- package/dist/square-sqlite.js +150 -226
- package/dist/views.d.ts +2 -2
- package/dist/views.js +0 -1
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -7
- package/dist/wake-transports.js +7 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +7 -171
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/mcp.md +1 -1
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/square-api.md +113 -615
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +2 -3
- package/plugins/claude/hooks/hooks.json +11 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +156 -417
- package/dist/channel-artifact.d.ts +0 -10
- package/dist/channel-artifact.js +0 -55
- package/dist/channel-delivery.d.ts +0 -11
- package/dist/channel-delivery.js +0 -51
- package/dist/channel-domain.d.ts +0 -3
- package/dist/channel-domain.js +0 -83
- package/dist/channel-facade.d.ts +0 -76
- package/dist/channel-facade.js +0 -1
- package/dist/channel-internal.d.ts +0 -126
- package/dist/channel-internal.js +0 -440
- package/dist/channel-metadata.d.ts +0 -11
- package/dist/channel-metadata.js +0 -1
- package/dist/channel-ports.d.ts +0 -241
- package/dist/channel-ports.js +0 -1
- package/dist/channel-presentation.d.ts +0 -17
- package/dist/channel-presentation.js +0 -22
- package/dist/channel-sqlite-schema.d.ts +0 -13
- package/dist/channel-sqlite-schema.js +0 -81
- package/dist/channel-transport.d.ts +0 -62
- package/dist/channel-transport.js +0 -1
- package/dist/channel.d.ts +0 -84
- package/dist/channel.js +0 -60
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/claude-delivery.d.ts +0 -12
- package/dist/claude-delivery.js +0 -108
- package/dist/claude-mod.d.ts +0 -8
- package/dist/claude-mod.js +0 -172
- package/dist/claude-transport.d.ts +0 -5
- package/dist/claude-transport.js +0 -58
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -22
- package/dist/delivery-operations.js +0 -198
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -56
- package/dist/delivery.js +0 -80
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -223
- package/dist/presentation-operations.d.ts +0 -5
- package/dist/presentation-operations.js +0 -86
- package/dist/sqlite-channel.d.ts +0 -15
- package/dist/sqlite-channel.js +0 -600
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- package/dist/wake-preview.d.ts +0 -7
- package/dist/wake-preview.js +0 -15
- package/plugins/claude/hooks/register.js +0 -132
- package/plugins/claude/types.d.ts +0 -7
- package/plugins/opencode/delivery.ts +0 -198
- /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
- /package/dist/{channel-values.js → square-values.js} +0 -0
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export { createClaudeWakeTransport, nativeTokens, nativePayload } from './claude-transport.js';
|
|
2
|
-
/** Recognize the whole frame first; one inactive token vetoes it before any evidence/receipt write. */
|
|
3
|
-
export declare function observeClaudeDelivery(sessionId: string, text: string, operation: 'guard' | 'admitted' | 'stored', env?: NodeJS.ProcessEnv): Promise<{
|
|
4
|
-
recognized: boolean;
|
|
5
|
-
current: boolean;
|
|
6
|
-
}>;
|
|
7
|
-
export type ClaudeBinding = {
|
|
8
|
-
readonly location: string;
|
|
9
|
-
readonly participant: string;
|
|
10
|
-
readonly session: string;
|
|
11
|
-
readonly generation?: string;
|
|
12
|
-
};
|
package/dist/claude-delivery.js
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { createArtifactPort } from './artifact.js';
|
|
2
|
-
import { presentPending } from './presentation-operations.js';
|
|
3
|
-
import { nativePendingHead } from './delivery.js';
|
|
4
|
-
import { decodeHarnessOwner } from './owner.js';
|
|
5
|
-
import { indexedSquareEntries } from './square-index.js';
|
|
6
|
-
import { parseMessageId } from './message.js';
|
|
7
|
-
export { createClaudeWakeTransport, nativeTokens, nativePayload } from './claude-transport.js';
|
|
8
|
-
import { nativeTokens, hashPayload } from './claude-transport.js';
|
|
9
|
-
async function rowsForSession(sessionId, env, tokens) {
|
|
10
|
-
const rows = [];
|
|
11
|
-
const candidates = (await indexedSquareEntries({}, env)).filter(entry => {
|
|
12
|
-
const coordinate = decodeHarnessOwner(entry.owner);
|
|
13
|
-
return coordinate?.channel === 'claude-code' && coordinate.session === sessionId;
|
|
14
|
-
});
|
|
15
|
-
const paths = new Set(candidates.map(candidate => candidate.location));
|
|
16
|
-
for (const location of paths) {
|
|
17
|
-
const artifact = createArtifactPort(location);
|
|
18
|
-
try {
|
|
19
|
-
const found = await artifact.read((facts) => tokens.flatMap((token) => {
|
|
20
|
-
const evidence = facts.deliveryByToken(token);
|
|
21
|
-
if (evidence?.kind !== 'wake' || evidence.correlation?.harness !== 'claude' || evidence.correlation.token !== token
|
|
22
|
-
|| evidence.route?.kind !== 'claude-native' || evidence.route.address.sessionId !== sessionId)
|
|
23
|
-
return [];
|
|
24
|
-
const seat = facts.seat(evidence.participant);
|
|
25
|
-
return [{ artifact, evidence, location, index: parseMessageId(evidence.message),
|
|
26
|
-
...(seat === undefined ? {} : { seat: { participant: evidence.participant, owner: seat.owner, generation: evidence.generation } }) }];
|
|
27
|
-
}));
|
|
28
|
-
rows.push(...found.value);
|
|
29
|
-
if (found.value.length === 0)
|
|
30
|
-
await artifact.close();
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
await artifact.close();
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return rows;
|
|
37
|
-
}
|
|
38
|
-
/** The token, participant, activity and captured generation must still be the exact wake. */
|
|
39
|
-
function sameCapturedEvidence(facts, row) {
|
|
40
|
-
const token = row.evidence.claimToken;
|
|
41
|
-
const evidence = token === undefined ? undefined : facts.deliveryByToken(token);
|
|
42
|
-
return token !== undefined && evidence !== undefined && row.seat !== undefined && row.seat.participant === row.evidence.participant
|
|
43
|
-
&& row.seat.generation === row.evidence.generation && evidence.kind === 'wake' && evidence.claimToken === token
|
|
44
|
-
&& evidence.participant === row.evidence.participant && evidence.message === row.evidence.message
|
|
45
|
-
&& evidence.generation === row.evidence.generation
|
|
46
|
-
&& evidence.correlation?.harness === 'claude' && evidence.correlation.token === token
|
|
47
|
-
&& evidence.correlation.payloadHash === row.evidence.correlation?.payloadHash
|
|
48
|
-
&& ['dispatching', 'unknown', 'accepted'].includes(evidence.outcome);
|
|
49
|
-
}
|
|
50
|
-
function capturedInput(row, now) {
|
|
51
|
-
if (row.seat === undefined || row.index === undefined || row.evidence.route === undefined)
|
|
52
|
-
return undefined;
|
|
53
|
-
return { location: row.location, participant: row.evidence.participant, generation: row.seat.generation,
|
|
54
|
-
owner: row.seat.owner, index: row.index, route: row.evidence.route, now };
|
|
55
|
-
}
|
|
56
|
-
function currentRow(facts, row, now) {
|
|
57
|
-
const input = capturedInput(row, now);
|
|
58
|
-
return input !== undefined && sameCapturedEvidence(facts, row) && nativePendingHead(facts, input) !== undefined;
|
|
59
|
-
}
|
|
60
|
-
/** Recognize the whole frame first; one inactive token vetoes it before any evidence/receipt write. */
|
|
61
|
-
export async function observeClaudeDelivery(sessionId, text, operation, env = process.env) {
|
|
62
|
-
const tokens = nativeTokens(text);
|
|
63
|
-
if (tokens.length === 0)
|
|
64
|
-
return { recognized: false, current: false };
|
|
65
|
-
const rows = await rowsForSession(sessionId, env, [...new Set(tokens)]);
|
|
66
|
-
const recognized = rows.length > 0;
|
|
67
|
-
const frames = [...text.matchAll(/(?:^|\n)(\[square-inbox:([a-zA-Z0-9-]+)\]\n[\s\S]*?\n\[\/square-inbox:\2\])(?=\n|$)/g)];
|
|
68
|
-
try {
|
|
69
|
-
// Every recognized token is vetted before any row can write: actual bytes against the
|
|
70
|
-
// stored correlation hash, then independent current eligibility.
|
|
71
|
-
const contentCurrent = rows.every(row => {
|
|
72
|
-
const token = row.evidence.claimToken, hash = row.evidence.correlation?.payloadHash;
|
|
73
|
-
if (token === undefined || hash === undefined || row.seat === undefined || row.index === undefined
|
|
74
|
-
|| !['dispatching', 'unknown', 'accepted'].includes(row.evidence.outcome))
|
|
75
|
-
return false;
|
|
76
|
-
const matching = frames.filter(frame => frame[2] === token);
|
|
77
|
-
return matching.length === tokens.filter(candidate => candidate === token).length
|
|
78
|
-
&& matching.every(frame => hashPayload(frame[1]) === hash);
|
|
79
|
-
});
|
|
80
|
-
if (!recognized || !contentCurrent)
|
|
81
|
-
return { recognized, current: false };
|
|
82
|
-
const now = Date.now();
|
|
83
|
-
for (const row of rows) {
|
|
84
|
-
if (!(await row.artifact.read(facts => currentRow(facts, row, now))).value)
|
|
85
|
-
return { recognized, current: false };
|
|
86
|
-
}
|
|
87
|
-
for (const row of rows) {
|
|
88
|
-
if (operation === 'guard')
|
|
89
|
-
continue;
|
|
90
|
-
const seat = row.seat;
|
|
91
|
-
const current = (facts) => currentRow(facts, row, Date.now());
|
|
92
|
-
const admitted = await row.artifact.transactAs({ kind: 'participant', seat }, facts => current(facts) && facts.channel.completeDelivery({ seat, message: row.evidence.message,
|
|
93
|
-
claimToken: row.evidence.claimToken, outcome: 'accepted', at: Date.now() })).catch(() => ({ value: false }));
|
|
94
|
-
if (!admitted.value)
|
|
95
|
-
return { recognized, current: false };
|
|
96
|
-
if (operation === 'stored') {
|
|
97
|
-
await presentPending({ artifact: row.artifact, location: row.location,
|
|
98
|
-
// Persisted coverage decides consumption; the current renderer never guesses.
|
|
99
|
-
participant: row.evidence.participant, message: row.evidence.message, seat, observationOf: row.evidence.claimToken,
|
|
100
|
-
markSeen: row.evidence.coverage !== 'clipped', sink: { present() { } }, current });
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return { recognized, current: true };
|
|
104
|
-
}
|
|
105
|
-
finally {
|
|
106
|
-
await Promise.all([...new Set(rows.map(row => row.artifact))].map(artifact => artifact.close()));
|
|
107
|
-
}
|
|
108
|
-
}
|
package/dist/claude-mod.d.ts
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
export interface ClaudeCoordinate {
|
|
2
|
-
readonly sessionId: string;
|
|
3
|
-
readonly cwd: string;
|
|
4
|
-
readonly version: string;
|
|
5
|
-
readonly endpoint?: string;
|
|
6
|
-
}
|
|
7
|
-
/** JSON stdin bridge. Identity is explicit per operation; process.env is never rewritten. */
|
|
8
|
-
export declare function runClaudeMod(inputText: string, env?: NodeJS.ProcessEnv): Promise<string>;
|
package/dist/claude-mod.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import { operationEnv } from './participant-identity.js';
|
|
2
|
-
import { channelArtifact, createArtifactPort } from './artifact.js';
|
|
3
|
-
import { recordClaudeDeliveryStatus } from './claude-delivery-status.js';
|
|
4
|
-
import { Square } from './square-api.js';
|
|
5
|
-
import { observeClaudeDelivery } from './claude-delivery.js';
|
|
6
|
-
import { processActNotificationsOnce } from './notifications.js';
|
|
7
|
-
import { sweepPending } from './delivery-operations.js';
|
|
8
|
-
import { beginClaudeMcpCall, endClaudeMcpCall } from './claude-mcp-context.js';
|
|
9
|
-
import { asSeatRef } from './seats.js';
|
|
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';
|
|
14
|
-
/** Persisted hook state keeps its wire coordinate; internal consumers keep the located seat. */
|
|
15
|
-
function claudeBinding({ location, seat }, sessionId) {
|
|
16
|
-
return { location, participant: seat.participant, session: sessionId, generation: seat.generation };
|
|
17
|
-
}
|
|
18
|
-
/** JSON stdin bridge. Identity is explicit per operation; process.env is never rewritten. */
|
|
19
|
-
export async function runClaudeMod(inputText, env = process.env) {
|
|
20
|
-
if (inputText.length > 512_000)
|
|
21
|
-
throw new Error('Claude bridge input exceeds its bound.');
|
|
22
|
-
const input = JSON.parse(inputText);
|
|
23
|
-
if (!input || typeof input.sessionId !== 'string' || !input.sessionId || typeof input.cwd !== 'string')
|
|
24
|
-
throw new Error('Claude bridge coordinate missing.');
|
|
25
|
-
if (input.operation === 'status') {
|
|
26
|
-
const recorded = input.status?.sessionId === input.sessionId && await recordClaudeDeliveryStatus(input.status, env);
|
|
27
|
-
return `${JSON.stringify({ recorded })}\n`;
|
|
28
|
-
}
|
|
29
|
-
if (input.operation === 'tool-begin') {
|
|
30
|
-
return `${JSON.stringify(await beginClaudeMcpCall({ toolUseId: input.toolUseId, sessionId: input.sessionId, cwd: input.cwd, agentId: input.agentId }, env))}\n`;
|
|
31
|
-
}
|
|
32
|
-
if (input.operation === 'tool-end') {
|
|
33
|
-
await endClaudeMcpCall(input.toolUseId, input.token, env);
|
|
34
|
-
return '{}\n';
|
|
35
|
-
}
|
|
36
|
-
const scoped = operationEnv('claude', input.sessionId, env);
|
|
37
|
-
const signal = AbortSignal.timeout(2500);
|
|
38
|
-
const owner = harnessOwner({ channel: 'claude-code', session: input.sessionId, ...(input.agentId === undefined ? {} : { agent: input.agentId }) });
|
|
39
|
-
const supported = process.platform === 'darwin' && typeof input.endpoint === 'string' && input.endpoint.startsWith('/');
|
|
40
|
-
const sessionSeats = async () => (await projectSessionSeats({ owner, env }, signal)).filter(member => member.seat.attachment === 'attached');
|
|
41
|
-
if (input.operation === 'end') {
|
|
42
|
-
const retired = [];
|
|
43
|
-
const captured = [...input.bindings ?? []];
|
|
44
|
-
// Discover committed late joins at the pinned end boundary, never later owners.
|
|
45
|
-
if (input.endedAt !== undefined)
|
|
46
|
-
for (const member of await projectSessionSeats({ owner, env }, signal)) {
|
|
47
|
-
if (member.joinedAt > input.endedAt || captured.some((item) => item.location === member.location
|
|
48
|
-
&& item.participant === member.seat.participant && item.generation === member.seat.generation))
|
|
49
|
-
continue;
|
|
50
|
-
captured.push(claudeBinding(member, input.sessionId));
|
|
51
|
-
}
|
|
52
|
-
for (const binding of captured) {
|
|
53
|
-
if (binding.session !== input.sessionId)
|
|
54
|
-
continue;
|
|
55
|
-
const square = await Square.at({ path: binding.location, env: scoped });
|
|
56
|
-
try {
|
|
57
|
-
const detached = binding.generation === undefined ? null
|
|
58
|
-
: await square.detach(binding.participant, { owner, generation: binding.generation });
|
|
59
|
-
if (detached)
|
|
60
|
-
retired.push(binding);
|
|
61
|
-
}
|
|
62
|
-
finally {
|
|
63
|
-
await square.close();
|
|
64
|
-
}
|
|
65
|
-
if (signal.aborted)
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
return `${JSON.stringify({ retired })}\n`;
|
|
69
|
-
}
|
|
70
|
-
const recovery = new Set();
|
|
71
|
-
const cancelledBindings = [...input.cancelledBindings ?? []];
|
|
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);
|
|
76
|
-
}
|
|
77
|
-
if (input.operation === 'start' || input.operation === 'reconcile' || input.operation === 'cancel' || input.cancelAt !== undefined) {
|
|
78
|
-
for (const member of await sessionSeats()) {
|
|
79
|
-
if (signal.aborted)
|
|
80
|
-
break;
|
|
81
|
-
const { location, seat: capturedSeat, joinedAt } = member;
|
|
82
|
-
const matches = (item) => item !== undefined
|
|
83
|
-
&& item.location === location && item.participant === capturedSeat.participant && item.session === input.sessionId
|
|
84
|
-
&& (item.generation === undefined || item.generation === capturedSeat.generation);
|
|
85
|
-
const expected = input.bindings?.find(matches);
|
|
86
|
-
const cancelled = input.cancelledBindings?.find(matches) ?? (input.operation === 'cancel' ? expected : undefined);
|
|
87
|
-
const lateCancelledJoin = input.operation === 'cancel' && input.cancelAt !== undefined
|
|
88
|
-
&& joinedAt <= input.cancelAt && expected === undefined;
|
|
89
|
-
if (lateCancelledJoin && !cancelledBindings.some(matches))
|
|
90
|
-
cancelledBindings.push(claudeBinding(member, input.sessionId));
|
|
91
|
-
if (input.operation !== 'start' && input.operation !== 'reconcile' && !lateCancelledJoin && expected === undefined)
|
|
92
|
-
continue;
|
|
93
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
94
|
-
try {
|
|
95
|
-
const { value: currentSeat } = await artifact.read((facts) => facts.seat(capturedSeat.participant), signal);
|
|
96
|
-
if (currentSeat === undefined || currentSeat.attachment !== 'attached' || currentSeat.generation !== capturedSeat.generation
|
|
97
|
-
|| currentSeat.owner !== capturedSeat.owner)
|
|
98
|
-
continue;
|
|
99
|
-
const seat = asSeatRef(currentSeat);
|
|
100
|
-
const existing = currentSeat.wakeAddress?.kind === 'claude-native' ? currentSeat.wakeAddress : undefined;
|
|
101
|
-
const cutoff = input.cancelAt !== undefined && (cancelled !== undefined || lateCancelledJoin) ? input.cancelAt : undefined;
|
|
102
|
-
if (cutoff !== undefined) {
|
|
103
|
-
const { value: through } = await artifact.read((facts) => {
|
|
104
|
-
let beforeIndex;
|
|
105
|
-
for (;;) {
|
|
106
|
-
const page = facts.activityHeads({ beforeIndex }, { edge: 'newest', limit: 100 });
|
|
107
|
-
const last = page.items.find((activity) => activity.at <= cutoff && activity.messageIndex !== undefined);
|
|
108
|
-
if (last !== undefined)
|
|
109
|
-
return last.messageIndex;
|
|
110
|
-
if (!page.hasMore)
|
|
111
|
-
return -1;
|
|
112
|
-
beforeIndex = page.items[0].index;
|
|
113
|
-
}
|
|
114
|
-
}, signal);
|
|
115
|
-
if (through >= 0)
|
|
116
|
-
await artifact.cancelDeliveries(seat, through, signal);
|
|
117
|
-
}
|
|
118
|
-
if (!supported) {
|
|
119
|
-
if (existing !== undefined)
|
|
120
|
-
await artifact.transactAs({ kind: 'participant', seat }, (facts) => facts.updateWakeAddress(seat, undefined), signal);
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (existing === undefined || existing.address.endpoint !== input.endpoint)
|
|
124
|
-
recovery.add(location);
|
|
125
|
-
await artifact.transactAs({ kind: 'participant', seat }, (facts) => facts.updateWakeAddress(seat, {
|
|
126
|
-
kind: 'claude-native', address: { sessionId: input.sessionId, endpoint: input.endpoint, version: input.version, platform: 'darwin', loaded: 'true' },
|
|
127
|
-
}), signal);
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
131
|
-
throw new SquareError('unavailable', `Square is unavailable at ${location}`);
|
|
132
|
-
throw error;
|
|
133
|
-
}
|
|
134
|
-
finally {
|
|
135
|
-
await artifact.close();
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
if (input.operation === 'start' || input.operation === 'reconcile' || input.operation === 'cancel') {
|
|
140
|
-
const members = await sessionSeats();
|
|
141
|
-
// One startup recovery pass for known unsent work. Unknown/admitted sends never replay.
|
|
142
|
-
if (supported && input.operation !== 'cancel')
|
|
143
|
-
for (const { location } of members) {
|
|
144
|
-
if (signal.aborted)
|
|
145
|
-
break;
|
|
146
|
-
if (input.operation !== 'start' && !recovery.has(location))
|
|
147
|
-
continue;
|
|
148
|
-
const artifact = createArtifactPort(location, undefined, signal);
|
|
149
|
-
let indices;
|
|
150
|
-
try {
|
|
151
|
-
indices = await sweepPending({ artifact: channelArtifact(artifact), location, now: Date.now(), graceMs: 0, limit: 8 });
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
155
|
-
throw new SquareError('unavailable', `Square is unavailable at ${location}`);
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
finally {
|
|
159
|
-
await artifact.close();
|
|
160
|
-
}
|
|
161
|
-
for (const index of indices) {
|
|
162
|
-
if (signal.aborted)
|
|
163
|
-
break;
|
|
164
|
-
await processActNotificationsOnce(location, index, { env: { ...env, SQUARE_NOTIFY_DELIVERY_WAIT_MS: '200' } });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
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`;
|
|
168
|
-
}
|
|
169
|
-
if (!['guard', 'admitted', 'stored'].includes(input.operation) || typeof input.text !== 'string')
|
|
170
|
-
throw new Error('Unknown Claude bridge operation.');
|
|
171
|
-
return `${JSON.stringify(await observeClaudeDelivery(input.sessionId, input.text, input.operation, env))}\n`;
|
|
172
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { WakeTransportPort } from './channel-transport.js';
|
|
2
|
-
export declare function nativeTokens(text: string): string[];
|
|
3
|
-
export declare function nativePayload(token: string, preview: string): string;
|
|
4
|
-
export declare function hashPayload(payload: string): string;
|
|
5
|
-
export declare function createClaudeWakeTransport(env?: NodeJS.ProcessEnv): WakeTransportPort;
|
package/dist/claude-transport.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { stat } from 'node:fs/promises';
|
|
2
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
3
|
-
import { writeClaudeNative } from './packages/agent-delivery/src/claude-native.js';
|
|
4
|
-
const marker = /(?:^|\n)\[square-inbox:([a-zA-Z0-9-]+)\](?:\n|$)/g;
|
|
5
|
-
export function nativeTokens(text) { return [...text.matchAll(marker)].map((match) => match[1]); }
|
|
6
|
-
export function nativePayload(token, preview) { return `[square-inbox:${token}]\n${preview}\n[/square-inbox:${token}]`; }
|
|
7
|
-
function nativeSupported(route) { return route.address.loaded === 'true' && !!route.address.endpoint; }
|
|
8
|
-
export function hashPayload(payload) { return createHash('sha256').update(payload).digest('hex'); }
|
|
9
|
-
async function preview(request) {
|
|
10
|
-
return request.presentation;
|
|
11
|
-
}
|
|
12
|
-
async function writeClaudeWake(sessionId, endpoint, messageId, payload, timeoutMs, signal) {
|
|
13
|
-
const result = await writeClaudeNative({ sessionId, endpoint, messageId }, payload, { deadline: Date.now() + timeoutMs, signal });
|
|
14
|
-
if (result.outcome === 'unavailable')
|
|
15
|
-
return { outcome: 'failed', signature: 'claude_endpoint_unavailable', message: result.message };
|
|
16
|
-
return { outcome: 'unknown', signature: 'native_admission_unconfirmed', message: result.outcome === 'written' ? 'Claude accepted the local write; native admission is unconfirmed.' : result.message };
|
|
17
|
-
}
|
|
18
|
-
export function createClaudeWakeTransport(env = process.env) {
|
|
19
|
-
return {
|
|
20
|
-
async probe(route) {
|
|
21
|
-
if (!nativeSupported(route))
|
|
22
|
-
return { outcome: 'not-capable', diagnostic: 'Claude native inbox unavailable: a loaded interactive Square mod is required.' };
|
|
23
|
-
try {
|
|
24
|
-
return (await stat(route.address.endpoint)).isSocket() || { outcome: 'not-capable', diagnostic: 'Claude native inbox endpoint is not a socket.' };
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return { outcome: 'not-capable', diagnostic: 'Claude native inbox endpoint unavailable.' };
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
attempt: (request, timeoutMs, beforeSend) => dispatchClaude(request, timeoutMs, beforeSend, env),
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
async function dispatchClaude(request, timeoutMs, beforeSend, env = process.env) {
|
|
34
|
-
void env;
|
|
35
|
-
const signal = AbortSignal.timeout(timeoutMs);
|
|
36
|
-
if (!nativeSupported(request.route) || request.claimToken === undefined)
|
|
37
|
-
return { outcome: 'not-capable', diagnostic: 'Claude native delivery needs a current artifact claim and loaded inbox.' };
|
|
38
|
-
if (signal.aborted || !await beforeSend())
|
|
39
|
-
return { outcome: 'unknown', signature: 'native_send_suppressed' };
|
|
40
|
-
const prepared = await preview(request);
|
|
41
|
-
if (prepared === undefined)
|
|
42
|
-
return { outcome: 'not-capable', diagnostic: 'Claude activity is no longer current.' };
|
|
43
|
-
const messageId = randomUUID();
|
|
44
|
-
const payload = nativePayload(request.claimToken, prepared.payload);
|
|
45
|
-
const correlation = { harness: 'claude', token: request.claimToken, messageId, payloadHash: hashPayload(payload) };
|
|
46
|
-
if (!await request.channel.prepare({ correlation, coverage: prepared.coverage,
|
|
47
|
-
...(prepared.description === undefined ? {} : { description: prepared.description }) }))
|
|
48
|
-
return { outcome: 'not-capable', diagnostic: 'Claude delivery claim is no longer current.' };
|
|
49
|
-
const fresh = await preview(request);
|
|
50
|
-
if (fresh === undefined || nativePayload(request.claimToken, fresh.payload) !== payload)
|
|
51
|
-
return { outcome: 'not-capable', diagnostic: 'Claude activity changed before native write.' };
|
|
52
|
-
if (signal.aborted || !await beforeSend())
|
|
53
|
-
return { outcome: 'not-capable', diagnostic: 'Claude activity was consumed before native write.' };
|
|
54
|
-
const sessionId = request.route.address.sessionId;
|
|
55
|
-
if (sessionId === undefined)
|
|
56
|
-
return { outcome: 'not-capable', diagnostic: 'Claude native route has no session address.' };
|
|
57
|
-
return writeClaudeWake(sessionId, request.route.address.endpoint, messageId, payload, timeoutMs, signal);
|
|
58
|
-
}
|
package/dist/cmd/notify-once.js
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
import { setTimeout as sleep } from 'node:timers/promises';
|
|
4
|
-
import { processActNotificationsOnce, wakeGraceMs } from '../notifications.js';
|
|
5
|
-
function args(argv) {
|
|
6
|
-
let squarePath;
|
|
7
|
-
let actIndex;
|
|
8
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
9
|
-
if (argv[index] === '--location' && argv[index + 1] !== undefined)
|
|
10
|
-
squarePath = resolve(argv[++index]);
|
|
11
|
-
else if (argv[index] === '--act-index' && /^\d+$/.test(argv[index + 1] ?? ''))
|
|
12
|
-
actIndex = Number(argv[++index]);
|
|
13
|
-
else
|
|
14
|
-
throw new Error(`Unknown notify-once argument: ${argv[index]}`);
|
|
15
|
-
}
|
|
16
|
-
if (squarePath === undefined || actIndex === undefined)
|
|
17
|
-
throw new Error('notify-once requires --location and --act-index.');
|
|
18
|
-
return { squarePath, actIndex };
|
|
19
|
-
}
|
|
20
|
-
async function main() {
|
|
21
|
-
if (process.env.SQUARE_DISABLE_PASEO_WAKE === '1')
|
|
22
|
-
return;
|
|
23
|
-
const { squarePath, actIndex } = args(process.argv.slice(2));
|
|
24
|
-
await sleep(wakeGraceMs());
|
|
25
|
-
await processActNotificationsOnce(squarePath, actIndex);
|
|
26
|
-
}
|
|
27
|
-
main().catch(() => {
|
|
28
|
-
process.exitCode = 0;
|
|
29
|
-
});
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
export type DeliveryPurpose = 'dispatch' | 'presentation';
|
|
2
|
-
export type AttemptOutcome = 'dispatching' | 'accepted' | 'failed' | 'unknown' | 'released' | 'presented' | 'clipped';
|
|
3
|
-
export interface Attempt<D> {
|
|
4
|
-
readonly purpose: DeliveryPurpose;
|
|
5
|
-
readonly token: string;
|
|
6
|
-
readonly ordinal: number;
|
|
7
|
-
readonly outcome: AttemptOutcome;
|
|
8
|
-
readonly at: number;
|
|
9
|
-
readonly expiresAt?: number;
|
|
10
|
-
readonly data: D;
|
|
11
|
-
}
|
|
12
|
-
/** One item/recipient/authority/purpose series, bound inside the caller's transaction.
|
|
13
|
-
* The caller owns token uniqueness, isolation and rollback; replacements retain coordinates.
|
|
14
|
-
*/
|
|
15
|
-
export interface AttemptRows<D> {
|
|
16
|
-
list(): readonly Attempt<D>[];
|
|
17
|
-
byToken(token: string): Attempt<D> | undefined;
|
|
18
|
-
insert(attempt: Attempt<D>): void;
|
|
19
|
-
replace(attempt: Attempt<D>): void;
|
|
20
|
-
}
|
|
21
|
-
export type AttemptClaim<D> = {
|
|
22
|
-
readonly status: 'acquired';
|
|
23
|
-
readonly token: string;
|
|
24
|
-
readonly ordinal: number;
|
|
25
|
-
} | {
|
|
26
|
-
readonly status: 'busy' | 'terminal';
|
|
27
|
-
readonly evidence: Attempt<D>;
|
|
28
|
-
readonly interrupted?: true;
|
|
29
|
-
};
|
|
30
|
-
export declare function claimAttempt<D>(rows: AttemptRows<D>, input: {
|
|
31
|
-
purpose: DeliveryPurpose;
|
|
32
|
-
token: string;
|
|
33
|
-
at: number;
|
|
34
|
-
leaseMs: number;
|
|
35
|
-
data: D;
|
|
36
|
-
}): AttemptClaim<D>;
|
|
37
|
-
export declare function prepareAttempt<D>(rows: AttemptRows<D>, input: {
|
|
38
|
-
token: string;
|
|
39
|
-
data: D;
|
|
40
|
-
}): boolean;
|
|
41
|
-
export declare function settleAttempt<D>(rows: AttemptRows<D>, input: {
|
|
42
|
-
token: string;
|
|
43
|
-
outcome: Exclude<AttemptOutcome, 'dispatching'>;
|
|
44
|
-
at: number;
|
|
45
|
-
data: D;
|
|
46
|
-
}): boolean;
|
|
47
|
-
type Custody = Pick<Attempt<unknown>, 'purpose' | 'outcome' | 'expiresAt'>;
|
|
48
|
-
/** Presentation custody suppresses dispatch independently of dispatch acceptance. */
|
|
49
|
-
export declare function presentationBlocksDispatch(attempts: readonly Custody[], now: number): boolean;
|
|
50
|
-
/** An expired dispatch is uncertain, not permission for automatic replay. */
|
|
51
|
-
export declare function automaticDispatchEligible(attempts: readonly Custody[], now: number): boolean;
|
|
52
|
-
export {};
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
function finite(value) {
|
|
2
|
-
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
3
|
-
throw new RangeError('time must be finite');
|
|
4
|
-
}
|
|
5
|
-
function token(value) {
|
|
6
|
-
if (typeof value !== 'string' || value.length === 0)
|
|
7
|
-
throw new TypeError('token must be nonempty');
|
|
8
|
-
}
|
|
9
|
-
export function claimAttempt(rows, input) {
|
|
10
|
-
if (input.purpose !== 'dispatch' && input.purpose !== 'presentation')
|
|
11
|
-
throw new TypeError('unsupported purpose');
|
|
12
|
-
token(input.token);
|
|
13
|
-
finite(input.at);
|
|
14
|
-
if (!Number.isSafeInteger(input.leaseMs) || input.leaseMs <= 0)
|
|
15
|
-
throw new RangeError('leaseMs must be a positive safe integer');
|
|
16
|
-
const expiresAt = input.at + input.leaseMs;
|
|
17
|
-
finite(expiresAt);
|
|
18
|
-
const attempts = rows.list();
|
|
19
|
-
const terminal = attempts.find(row => row.outcome === 'unknown'
|
|
20
|
-
|| row.outcome === (input.purpose === 'dispatch' ? 'accepted' : 'presented'));
|
|
21
|
-
if (terminal !== undefined)
|
|
22
|
-
return { status: 'terminal', evidence: terminal };
|
|
23
|
-
const dispatching = attempts.find(row => row.outcome === 'dispatching');
|
|
24
|
-
if (dispatching !== undefined) {
|
|
25
|
-
if (dispatching.expiresAt === undefined || dispatching.expiresAt > input.at)
|
|
26
|
-
return { status: 'busy', evidence: dispatching };
|
|
27
|
-
const { expiresAt: _expiry, ...previous } = dispatching;
|
|
28
|
-
const evidence = { ...previous, outcome: 'unknown', at: input.at };
|
|
29
|
-
rows.replace(evidence);
|
|
30
|
-
return { status: 'terminal', evidence, interrupted: true };
|
|
31
|
-
}
|
|
32
|
-
const ordinal = (attempts[0]?.ordinal ?? -1) + 1;
|
|
33
|
-
if (!Number.isSafeInteger(ordinal) || ordinal < 0)
|
|
34
|
-
throw new RangeError('attempt ordinal exhausted');
|
|
35
|
-
rows.insert({ purpose: input.purpose, token: input.token, ordinal, outcome: 'dispatching', at: input.at, expiresAt, data: input.data });
|
|
36
|
-
return { status: 'acquired', token: input.token, ordinal };
|
|
37
|
-
}
|
|
38
|
-
export function prepareAttempt(rows, input) {
|
|
39
|
-
token(input.token);
|
|
40
|
-
const previous = rows.byToken(input.token);
|
|
41
|
-
if (previous?.outcome !== 'dispatching')
|
|
42
|
-
return false;
|
|
43
|
-
rows.replace({ ...previous, data: input.data });
|
|
44
|
-
return true;
|
|
45
|
-
}
|
|
46
|
-
export function settleAttempt(rows, input) {
|
|
47
|
-
token(input.token);
|
|
48
|
-
finite(input.at);
|
|
49
|
-
if (!['accepted', 'failed', 'unknown', 'released', 'presented', 'clipped'].includes(input.outcome))
|
|
50
|
-
throw new TypeError('unsupported completion');
|
|
51
|
-
const previous = rows.byToken(input.token);
|
|
52
|
-
if (previous === undefined)
|
|
53
|
-
return false;
|
|
54
|
-
if (previous.outcome === 'accepted' && input.outcome === 'accepted')
|
|
55
|
-
return true;
|
|
56
|
-
if (previous.outcome !== 'dispatching' && !(previous.outcome === 'unknown' && input.outcome === 'accepted'))
|
|
57
|
-
return false;
|
|
58
|
-
const { expiresAt: _expiry, ...retained } = previous;
|
|
59
|
-
rows.replace({ ...retained, outcome: input.outcome, at: input.at, data: input.data });
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
/** Presentation custody suppresses dispatch independently of dispatch acceptance. */
|
|
63
|
-
export function presentationBlocksDispatch(attempts, now) {
|
|
64
|
-
finite(now);
|
|
65
|
-
return attempts.some(row => row.purpose === 'presentation'
|
|
66
|
-
&& (row.outcome === 'presented' || row.outcome === 'clipped' || row.outcome === 'unknown'
|
|
67
|
-
|| row.outcome === 'dispatching'));
|
|
68
|
-
}
|
|
69
|
-
/** An expired dispatch is uncertain, not permission for automatic replay. */
|
|
70
|
-
export function automaticDispatchEligible(attempts, now) {
|
|
71
|
-
return !presentationBlocksDispatch(attempts, now) && !attempts.some(row => row.purpose === 'dispatch'
|
|
72
|
-
&& (row.outcome === 'accepted' || row.outcome === 'unknown' || row.outcome === 'dispatching'));
|
|
73
|
-
}
|