@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.
Files changed (196) hide show
  1. package/dist/artifact.d.ts +1 -3
  2. package/dist/artifact.js +12 -36
  3. package/dist/attention-presentation.d.ts +2 -22
  4. package/dist/attention-presentation.js +9 -12
  5. package/dist/boundary-presentation.d.ts +3 -14
  6. package/dist/boundary-presentation.js +29 -104
  7. package/dist/card-presentation.d.ts +1 -0
  8. package/dist/card-presentation.js +2 -0
  9. package/dist/catch-decisions.js +1 -7
  10. package/dist/claude-boundary-state.d.ts +20 -0
  11. package/dist/claude-boundary-state.js +84 -0
  12. package/dist/claude-hook.d.ts +2 -0
  13. package/dist/claude-hook.js +231 -0
  14. package/dist/claude-mcp-context.d.ts +1 -1
  15. package/dist/claude-mcp-context.js +12 -3
  16. package/dist/cli/context.js +7 -9
  17. package/dist/cli/observation-commands.d.ts +4 -1
  18. package/dist/cli/observation-commands.js +37 -4
  19. package/dist/cli/registry.js +5 -2
  20. package/dist/codex-address.d.ts +2 -2
  21. package/dist/codex-boundary-state.d.ts +1 -5
  22. package/dist/codex-boundary-state.js +0 -4
  23. package/dist/codex-hook.d.ts +7 -10
  24. package/dist/codex-hook.js +69 -33
  25. package/dist/codex-queue.d.ts +10 -11
  26. package/dist/codex-queue.js +32 -32
  27. package/dist/codex-watch.d.ts +2 -0
  28. package/dist/codex-watch.js +100 -0
  29. package/dist/decisions.js +2 -2
  30. package/dist/delivery-deferral.d.ts +8 -17
  31. package/dist/delivery-deferral.js +0 -28
  32. package/dist/delivery-frame.d.ts +7 -0
  33. package/dist/delivery-frame.js +11 -0
  34. package/dist/delivery-ledger-sqlite.d.ts +97 -0
  35. package/dist/delivery-ledger-sqlite.js +496 -0
  36. package/dist/delivery-ledger.d.ts +196 -0
  37. package/dist/delivery-ledger.js +268 -0
  38. package/dist/delivery-lifetime.d.ts +6 -0
  39. package/dist/delivery-lifetime.js +45 -0
  40. package/dist/harness-claude.js +3 -4
  41. package/dist/harness-links.js +63 -7
  42. package/dist/harness.js +12 -17
  43. package/dist/help.js +4 -1
  44. package/dist/http-delivery.d.ts +5 -9
  45. package/dist/http-delivery.js +20 -77
  46. package/dist/inbox.d.ts +6 -49
  47. package/dist/inbox.js +24 -371
  48. package/dist/index.d.ts +6 -3
  49. package/dist/index.js +4 -1
  50. package/dist/mcp.js +2 -3
  51. package/dist/model.d.ts +7 -47
  52. package/dist/model.js +1 -7
  53. package/dist/notifications.d.ts +24 -20
  54. package/dist/notifications.js +39 -108
  55. package/dist/operation-control.d.ts +4 -0
  56. package/dist/operation-control.js +20 -0
  57. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  58. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  59. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  60. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  61. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  62. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  63. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  64. package/dist/packages/agent-delivery/src/codex.js +41 -4
  65. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  66. package/dist/packages/agent-delivery/src/index.js +1 -1
  67. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  68. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  69. package/dist/paseo-delivery.d.ts +5 -5
  70. package/dist/paseo-delivery.js +19 -97
  71. package/dist/paseo.d.ts +1 -1
  72. package/dist/paseo.js +1 -1
  73. package/dist/plugins/opencode/index.d.ts +5 -3
  74. package/dist/plugins/opencode/index.js +99 -21
  75. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  76. package/dist/plugins/opencode-shared/delivery.js +109 -0
  77. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  78. package/dist/plugins/opencode-v1/index.js +41 -0
  79. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  80. package/dist/plugins/opencode-v1/runtime.js +152 -0
  81. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  82. package/dist/plugins/opencode-v1/transport.js +50 -0
  83. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  84. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  85. package/dist/ports.d.ts +31 -90
  86. package/dist/session-lifecycle.d.ts +8 -7
  87. package/dist/session-lifecycle.js +40 -42
  88. package/dist/sqlite-square.d.ts +14 -23
  89. package/dist/sqlite-square.js +132 -139
  90. package/dist/square-api.js +4 -7
  91. package/dist/square-core.d.ts +0 -1
  92. package/dist/square-domain.d.ts +5 -17
  93. package/dist/square-domain.js +104 -221
  94. package/dist/square-facade.d.ts +18 -15
  95. package/dist/square-post.d.ts +53 -0
  96. package/dist/square-post.js +4 -0
  97. package/dist/square-posting.d.ts +13 -0
  98. package/dist/square-posting.js +174 -0
  99. package/dist/square-producer-sqlite.d.ts +11 -0
  100. package/dist/square-producer-sqlite.js +210 -0
  101. package/dist/square-sqlite.d.ts +15 -25
  102. package/dist/square-sqlite.js +150 -226
  103. package/dist/views.d.ts +2 -2
  104. package/dist/views.js +0 -1
  105. package/dist/wake-sink.d.ts +4 -11
  106. package/dist/wake-sink.js +8 -32
  107. package/dist/wake-transports.d.ts +4 -7
  108. package/dist/wake-transports.js +7 -39
  109. package/examples/symmetric-domains.mjs +29 -249
  110. package/guides/change-waits.md +13 -53
  111. package/guides/claude-native.md +7 -171
  112. package/guides/codex-delivery.md +55 -80
  113. package/guides/delivery-primitives.md +48 -207
  114. package/guides/mcp.md +1 -1
  115. package/guides/operation-cancellation.md +2 -5
  116. package/guides/participant-ownership.md +25 -89
  117. package/guides/pi-native-delivery.md +19 -56
  118. package/guides/pi-tools.md +6 -8
  119. package/guides/square-api.md +113 -615
  120. package/package.json +8 -8
  121. package/plugins/claude/.claude-plugin/plugin.json +2 -3
  122. package/plugins/claude/hooks/hooks.json +11 -1
  123. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  124. package/plugins/codex/hooks/hooks.json +11 -23
  125. package/plugins/opencode/index.ts +91 -23
  126. package/plugins/opencode-shared/delivery.ts +91 -0
  127. package/plugins/opencode-v1/index.ts +36 -0
  128. package/plugins/opencode-v1/runtime.ts +134 -0
  129. package/plugins/opencode-v1/transport.ts +41 -0
  130. package/plugins/paseo/OVERVIEW.md +5 -5
  131. package/plugins/paseo/README.md +7 -6
  132. package/plugins/paseo/client/markdown.tsx +5 -5
  133. package/plugins/paseo/client/square-message.tsx +28 -24
  134. package/plugins/paseo/client/transform.ts +8 -5
  135. package/plugins/paseo/shared/card-presentation.ts +16 -0
  136. package/plugins/paseo/shared/message.ts +6 -11
  137. package/plugins/pi/square-pi.js +156 -417
  138. package/dist/channel-artifact.d.ts +0 -10
  139. package/dist/channel-artifact.js +0 -55
  140. package/dist/channel-delivery.d.ts +0 -11
  141. package/dist/channel-delivery.js +0 -51
  142. package/dist/channel-domain.d.ts +0 -3
  143. package/dist/channel-domain.js +0 -83
  144. package/dist/channel-facade.d.ts +0 -76
  145. package/dist/channel-facade.js +0 -1
  146. package/dist/channel-internal.d.ts +0 -126
  147. package/dist/channel-internal.js +0 -440
  148. package/dist/channel-metadata.d.ts +0 -11
  149. package/dist/channel-metadata.js +0 -1
  150. package/dist/channel-ports.d.ts +0 -241
  151. package/dist/channel-ports.js +0 -1
  152. package/dist/channel-presentation.d.ts +0 -17
  153. package/dist/channel-presentation.js +0 -22
  154. package/dist/channel-sqlite-schema.d.ts +0 -13
  155. package/dist/channel-sqlite-schema.js +0 -81
  156. package/dist/channel-transport.d.ts +0 -62
  157. package/dist/channel-transport.js +0 -1
  158. package/dist/channel.d.ts +0 -84
  159. package/dist/channel.js +0 -60
  160. package/dist/claude-delivery-status.d.ts +0 -13
  161. package/dist/claude-delivery-status.js +0 -46
  162. package/dist/claude-delivery.d.ts +0 -12
  163. package/dist/claude-delivery.js +0 -108
  164. package/dist/claude-mod.d.ts +0 -8
  165. package/dist/claude-mod.js +0 -172
  166. package/dist/claude-transport.d.ts +0 -5
  167. package/dist/claude-transport.js +0 -58
  168. package/dist/cmd/notify-once.d.ts +0 -2
  169. package/dist/cmd/notify-once.js +0 -29
  170. package/dist/delivery-attempts.d.ts +0 -52
  171. package/dist/delivery-attempts.js +0 -73
  172. package/dist/delivery-effects.d.ts +0 -139
  173. package/dist/delivery-effects.js +0 -163
  174. package/dist/delivery-health.d.ts +0 -51
  175. package/dist/delivery-health.js +0 -173
  176. package/dist/delivery-operations.d.ts +0 -22
  177. package/dist/delivery-operations.js +0 -198
  178. package/dist/delivery-primitives.d.ts +0 -5
  179. package/dist/delivery-primitives.js +0 -3
  180. package/dist/delivery.d.ts +0 -56
  181. package/dist/delivery.js +0 -80
  182. package/dist/plugins/opencode/delivery.d.ts +0 -19
  183. package/dist/plugins/opencode/delivery.js +0 -223
  184. package/dist/presentation-operations.d.ts +0 -5
  185. package/dist/presentation-operations.js +0 -86
  186. package/dist/sqlite-channel.d.ts +0 -15
  187. package/dist/sqlite-channel.js +0 -600
  188. package/dist/square-index.d.ts +0 -16
  189. package/dist/square-index.js +0 -106
  190. package/dist/wake-preview.d.ts +0 -7
  191. package/dist/wake-preview.js +0 -15
  192. package/plugins/claude/hooks/register.js +0 -132
  193. package/plugins/claude/types.d.ts +0 -7
  194. package/plugins/opencode/delivery.ts +0 -198
  195. /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
  196. /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
- };
@@ -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
- }
@@ -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>;
@@ -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;
@@ -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
- }
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env node
2
- export {};
@@ -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
- }