@astrosheep/square 0.4.0 → 0.4.1

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 (187) 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-delivery.d.ts +13 -8
  11. package/dist/claude-delivery.js +40 -93
  12. package/dist/claude-mcp-context.js +2 -2
  13. package/dist/claude-mod.js +28 -140
  14. package/dist/claude-transport.d.ts +3 -3
  15. package/dist/claude-transport.js +20 -44
  16. package/dist/cli/context.js +7 -9
  17. package/dist/cli/observation-commands.d.ts +2 -0
  18. package/dist/cli/observation-commands.js +12 -2
  19. package/dist/cli/registry.js +3 -1
  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-links.js +61 -7
  41. package/dist/harness.js +12 -17
  42. package/dist/help.js +2 -0
  43. package/dist/http-delivery.d.ts +5 -9
  44. package/dist/http-delivery.js +20 -77
  45. package/dist/inbox.d.ts +6 -49
  46. package/dist/inbox.js +24 -371
  47. package/dist/index.d.ts +6 -3
  48. package/dist/index.js +4 -1
  49. package/dist/mcp.js +1 -1
  50. package/dist/model.d.ts +7 -47
  51. package/dist/model.js +1 -7
  52. package/dist/notifications.d.ts +24 -20
  53. package/dist/notifications.js +39 -108
  54. package/dist/operation-control.d.ts +4 -0
  55. package/dist/operation-control.js +20 -0
  56. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  57. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  58. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  59. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  60. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  61. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  62. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  63. package/dist/packages/agent-delivery/src/codex.js +41 -4
  64. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  65. package/dist/packages/agent-delivery/src/index.js +1 -1
  66. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  67. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  68. package/dist/paseo-delivery.d.ts +5 -5
  69. package/dist/paseo-delivery.js +19 -97
  70. package/dist/paseo.d.ts +1 -1
  71. package/dist/paseo.js +1 -1
  72. package/dist/plugins/opencode/index.d.ts +5 -3
  73. package/dist/plugins/opencode/index.js +99 -21
  74. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  75. package/dist/plugins/opencode-shared/delivery.js +109 -0
  76. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  77. package/dist/plugins/opencode-v1/index.js +41 -0
  78. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  79. package/dist/plugins/opencode-v1/runtime.js +152 -0
  80. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  81. package/dist/plugins/opencode-v1/transport.js +50 -0
  82. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  83. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  84. package/dist/ports.d.ts +31 -90
  85. package/dist/session-lifecycle.d.ts +8 -7
  86. package/dist/session-lifecycle.js +40 -42
  87. package/dist/sqlite-square.d.ts +14 -23
  88. package/dist/sqlite-square.js +132 -139
  89. package/dist/square-api.js +4 -7
  90. package/dist/square-core.d.ts +0 -1
  91. package/dist/square-domain.d.ts +5 -17
  92. package/dist/square-domain.js +104 -221
  93. package/dist/square-facade.d.ts +18 -15
  94. package/dist/square-post.d.ts +53 -0
  95. package/dist/square-post.js +4 -0
  96. package/dist/square-posting.d.ts +13 -0
  97. package/dist/square-posting.js +174 -0
  98. package/dist/square-producer-sqlite.d.ts +11 -0
  99. package/dist/square-producer-sqlite.js +210 -0
  100. package/dist/square-sqlite.d.ts +15 -25
  101. package/dist/square-sqlite.js +150 -226
  102. package/dist/views.d.ts +2 -2
  103. package/dist/views.js +0 -1
  104. package/dist/wake-sink.d.ts +4 -11
  105. package/dist/wake-sink.js +8 -32
  106. package/dist/wake-transports.d.ts +4 -7
  107. package/dist/wake-transports.js +8 -39
  108. package/examples/symmetric-domains.mjs +29 -249
  109. package/guides/change-waits.md +13 -53
  110. package/guides/claude-native.md +19 -175
  111. package/guides/codex-delivery.md +55 -80
  112. package/guides/delivery-primitives.md +48 -207
  113. package/guides/operation-cancellation.md +2 -5
  114. package/guides/participant-ownership.md +25 -89
  115. package/guides/pi-native-delivery.md +19 -56
  116. package/guides/pi-tools.md +6 -8
  117. package/guides/square-api.md +113 -615
  118. package/package.json +8 -8
  119. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  120. package/plugins/claude/hooks/hooks.json +1 -1
  121. package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
  122. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  123. package/plugins/codex/hooks/hooks.json +11 -23
  124. package/plugins/opencode/index.ts +91 -23
  125. package/plugins/opencode-shared/delivery.ts +91 -0
  126. package/plugins/opencode-v1/index.ts +36 -0
  127. package/plugins/opencode-v1/runtime.ts +134 -0
  128. package/plugins/opencode-v1/transport.ts +41 -0
  129. package/plugins/paseo/OVERVIEW.md +5 -5
  130. package/plugins/paseo/README.md +7 -6
  131. package/plugins/paseo/client/markdown.tsx +5 -5
  132. package/plugins/paseo/client/square-message.tsx +28 -24
  133. package/plugins/paseo/client/transform.ts +8 -5
  134. package/plugins/paseo/shared/card-presentation.ts +16 -0
  135. package/plugins/paseo/shared/message.ts +6 -11
  136. package/plugins/pi/square-pi.js +156 -417
  137. package/dist/channel-artifact.d.ts +0 -10
  138. package/dist/channel-artifact.js +0 -55
  139. package/dist/channel-delivery.d.ts +0 -11
  140. package/dist/channel-delivery.js +0 -51
  141. package/dist/channel-domain.d.ts +0 -3
  142. package/dist/channel-domain.js +0 -83
  143. package/dist/channel-facade.d.ts +0 -76
  144. package/dist/channel-facade.js +0 -1
  145. package/dist/channel-internal.d.ts +0 -126
  146. package/dist/channel-internal.js +0 -440
  147. package/dist/channel-metadata.d.ts +0 -11
  148. package/dist/channel-metadata.js +0 -1
  149. package/dist/channel-ports.d.ts +0 -241
  150. package/dist/channel-ports.js +0 -1
  151. package/dist/channel-presentation.d.ts +0 -17
  152. package/dist/channel-presentation.js +0 -22
  153. package/dist/channel-sqlite-schema.d.ts +0 -13
  154. package/dist/channel-sqlite-schema.js +0 -81
  155. package/dist/channel-transport.d.ts +0 -62
  156. package/dist/channel-transport.js +0 -1
  157. package/dist/channel.d.ts +0 -84
  158. package/dist/channel.js +0 -60
  159. package/dist/claude-delivery-status.d.ts +0 -13
  160. package/dist/claude-delivery-status.js +0 -46
  161. package/dist/cmd/notify-once.d.ts +0 -2
  162. package/dist/cmd/notify-once.js +0 -29
  163. package/dist/delivery-attempts.d.ts +0 -52
  164. package/dist/delivery-attempts.js +0 -73
  165. package/dist/delivery-effects.d.ts +0 -139
  166. package/dist/delivery-effects.js +0 -163
  167. package/dist/delivery-health.d.ts +0 -51
  168. package/dist/delivery-health.js +0 -173
  169. package/dist/delivery-operations.d.ts +0 -22
  170. package/dist/delivery-operations.js +0 -198
  171. package/dist/delivery-primitives.d.ts +0 -5
  172. package/dist/delivery-primitives.js +0 -3
  173. package/dist/delivery.d.ts +0 -56
  174. package/dist/delivery.js +0 -80
  175. package/dist/plugins/opencode/delivery.d.ts +0 -19
  176. package/dist/plugins/opencode/delivery.js +0 -223
  177. package/dist/presentation-operations.d.ts +0 -5
  178. package/dist/presentation-operations.js +0 -86
  179. package/dist/sqlite-channel.d.ts +0 -15
  180. package/dist/sqlite-channel.js +0 -600
  181. package/dist/square-index.d.ts +0 -16
  182. package/dist/square-index.js +0 -106
  183. package/dist/wake-preview.d.ts +0 -7
  184. package/dist/wake-preview.js +0 -15
  185. package/plugins/opencode/delivery.ts +0 -198
  186. /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
  187. /package/dist/{channel-values.js → square-values.js} +0 -0
@@ -0,0 +1,152 @@
1
+ import { canonicalPath } from '../../canonical-path.js';
2
+ import { cancelOpenCodeDeliveries, currentOpenCodeGeneration, detachOpenCodeSession, endOpenCodeSession, listOpenCodeSessions, observeOpenCodeContext, observeOpenCodeSessions, publishOpenCodeAddress, receiveOpenCodePending } from '../opencode-shared/delivery.js';
3
+ import { legacyTransport } from './transport.js';
4
+ export async function startReceiver(ctx) {
5
+ const env = { ...process.env };
6
+ const directory = await canonicalPath(ctx.directory);
7
+ const discovery = await observeOpenCodeSessions(env);
8
+ const lifetime = new AbortController();
9
+ const receivers = new Map();
10
+ const terminal = new Set();
11
+ const interrupting = new Set();
12
+ const cancellationFailed = new Set();
13
+ let coordinating = Promise.resolve();
14
+ const coordinate = (operation) => {
15
+ coordinating = coordinating.then(() => lifetime.signal.aborted ? undefined : operation()).catch(() => { });
16
+ return coordinating;
17
+ };
18
+ const stop = (id) => {
19
+ const receiver = receivers.get(id);
20
+ receiver?.abort.abort();
21
+ receivers.delete(id);
22
+ return receiver?.running;
23
+ };
24
+ const validate = async (id) => {
25
+ const result = await ctx.client.session.get({ path: { id }, signal: lifetime.signal }).catch(() => undefined);
26
+ const info = result?.data;
27
+ return !result?.error && info?.id === id && info.projectID === ctx.project.id && typeof info.directory === 'string'
28
+ && await canonicalPath(info.directory) === directory;
29
+ };
30
+ const reconcile = async (active) => {
31
+ const candidates = new Set(await listOpenCodeSessions(env));
32
+ if (active)
33
+ candidates.add(active);
34
+ for (const id of candidates) {
35
+ if (lifetime.signal.aborted)
36
+ return;
37
+ if (interrupting.has(id) || cancellationFailed.has(id))
38
+ continue;
39
+ if (terminal.has(id) || !await validate(id) || terminal.has(id)) {
40
+ const receiver = receivers.get(id);
41
+ stop(id);
42
+ if (receiver)
43
+ await detachOpenCodeSession(id, receiver.generation, env);
44
+ continue;
45
+ }
46
+ const existing = receivers.get(id);
47
+ if (existing?.settled === false || interrupting.has(id))
48
+ continue;
49
+ const generation = existing?.generation ?? await publishOpenCodeAddress(id, env, lifetime.signal);
50
+ if (!generation) {
51
+ stop(id);
52
+ continue;
53
+ }
54
+ if (terminal.has(id) || interrupting.has(id) || lifetime.signal.aborted) {
55
+ await detachOpenCodeSession(id, generation, env);
56
+ continue;
57
+ }
58
+ const abort = new AbortController();
59
+ const signal = AbortSignal.any([lifetime.signal, abort.signal]);
60
+ const receiver = { abort, generation, settled: false, running: receiveOpenCodePending(id, generation, legacyTransport(ctx.client, id, signal), env, signal).catch(() => { }).finally(() => { receiver.settled = true; }) };
61
+ receivers.set(id, receiver);
62
+ }
63
+ for (const id of receivers.keys())
64
+ if (!candidates.has(id))
65
+ stop(id);
66
+ };
67
+ const loop = (async () => {
68
+ while (!lifetime.signal.aborted) {
69
+ const baseline = discovery.observer.generation;
70
+ await coordinate(() => reconcile());
71
+ await discovery.observer.hinted(baseline, 2000, lifetime.signal);
72
+ }
73
+ })().catch(() => { });
74
+ let disposal;
75
+ return {
76
+ 'experimental.chat.messages.transform': async (_input, output) => {
77
+ const ids = new Set(output.messages.map(message => message.info?.sessionID));
78
+ if (ids.size !== 1 || typeof [...ids][0] !== 'string')
79
+ return;
80
+ const id = [...ids][0];
81
+ if (terminal.has(id))
82
+ return;
83
+ await coordinate(() => reconcile(id));
84
+ const receiver = receivers.get(id);
85
+ if (!receiver || terminal.has(id) || lifetime.signal.aborted)
86
+ return;
87
+ const messages = output.messages.flatMap(({ info, parts }) => {
88
+ if (info?.role !== 'user' || !info.id || !parts?.length || !parts.every(part => part.type === 'text'
89
+ && typeof part.text === 'string' && part.sessionID === id && part.messageID === info.id && !part.synthetic && !part.ignored))
90
+ return [];
91
+ return [{ id: info.id, role: 'user', content: parts.map(part => ({ type: 'text', text: part.text })) }];
92
+ });
93
+ await observeOpenCodeContext(id, receiver.generation, messages, env, AbortSignal.any([lifetime.signal, receiver.abort.signal])).catch(() => { });
94
+ },
95
+ event: async ({ event }) => {
96
+ const interrupted = event.type === 'session.error' && event.properties?.error?.name === 'MessageAbortedError'
97
+ ? event.properties.sessionID : undefined;
98
+ if (interrupted) {
99
+ const receiver = receivers.get(interrupted);
100
+ if (!receiver || interrupting.has(interrupted))
101
+ return;
102
+ interrupting.add(interrupted);
103
+ stop(interrupted);
104
+ await coordinate(async () => {
105
+ try {
106
+ await receiver.running;
107
+ if (!await cancelOpenCodeDeliveries(interrupted, receiver.generation, env))
108
+ throw new Error('OpenCode cancellation fence lost');
109
+ }
110
+ catch (error) {
111
+ cancellationFailed.add(interrupted);
112
+ await detachOpenCodeSession(interrupted, receiver.generation, env).catch(() => { });
113
+ throw error;
114
+ }
115
+ finally {
116
+ interrupting.delete(interrupted);
117
+ }
118
+ await reconcile(interrupted);
119
+ });
120
+ return;
121
+ }
122
+ const info = event.properties?.info;
123
+ if (event.type !== 'session.deleted' || !info?.id || info.projectID !== ctx.project.id || typeof info.directory !== 'string'
124
+ || await canonicalPath(info.directory).catch(() => undefined) !== directory)
125
+ return;
126
+ const id = info.id;
127
+ terminal.add(id);
128
+ const receiver = receivers.get(id);
129
+ stop(id);
130
+ await coordinate(async () => {
131
+ const generation = receiver?.generation ?? await currentOpenCodeGeneration(id, env);
132
+ if (generation)
133
+ await endOpenCodeSession(id, generation, env);
134
+ });
135
+ },
136
+ dispose: () => disposal ??= (async () => {
137
+ lifetime.abort();
138
+ const captured = [...receivers.entries()];
139
+ const tasks = [loop, coordinating, ...captured.map(([id]) => stop(id))];
140
+ let timer;
141
+ try {
142
+ await Promise.race([Promise.allSettled(tasks), new Promise(resolve => { timer = setTimeout(resolve, 3000); })]);
143
+ }
144
+ finally {
145
+ if (timer !== undefined)
146
+ clearTimeout(timer);
147
+ }
148
+ await Promise.allSettled(captured.map(([id, receiver]) => detachOpenCodeSession(id, receiver.generation, env)));
149
+ await discovery.close();
150
+ })(),
151
+ };
152
+ }
@@ -0,0 +1,13 @@
1
+ import type { DeliveryTransport } from '../../delivery-ledger.js';
2
+ type Client = {
3
+ session: {
4
+ promptAsync(input: unknown): Promise<{
5
+ error?: unknown;
6
+ response?: Response;
7
+ }>;
8
+ };
9
+ };
10
+ /** OpenCode's 48-bit timestamp/counter prefix followed by 14 random base62 digits. */
11
+ export declare function nativeMessageId(): string;
12
+ export declare function legacyTransport(client: Client, sessionId: string, lifetime: AbortSignal): DeliveryTransport;
13
+ export {};
@@ -0,0 +1,50 @@
1
+ import { randomInt, createHash } from 'node:crypto';
2
+ import { renderDeliveryReminder } from '../../delivery-frame.js';
3
+ let lastTime = 0, counter = 0;
4
+ const alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
5
+ /** OpenCode's 48-bit timestamp/counter prefix followed by 14 random base62 digits. */
6
+ export function nativeMessageId() {
7
+ const now = Math.max(Date.now(), lastTime);
8
+ counter = now === lastTime ? counter + 1 : 0;
9
+ if (counter >= 0x1000) {
10
+ lastTime = now + 1;
11
+ counter = 0;
12
+ }
13
+ else
14
+ lastTime = now;
15
+ const prefix = (BigInt(lastTime) * 0x1000n + BigInt(counter)).toString(16).slice(-12).padStart(12, '0');
16
+ return `msg_${prefix}${Array.from({ length: 14 }, () => alphabet[randomInt(62)]).join('')}`;
17
+ }
18
+ export function legacyTransport(client, sessionId, lifetime) {
19
+ return {
20
+ kind: 'opencode-server',
21
+ async send(card, address, context) {
22
+ const signal = AbortSignal.any([lifetime, context.signal]);
23
+ if (address.sessionId !== sessionId || signal.aborted)
24
+ return 'not-capable';
25
+ const messageId = nativeMessageId();
26
+ const text = renderDeliveryReminder([card.text], { tagged: true });
27
+ const payloadHash = createHash('sha256').update(text).digest('hex');
28
+ if (!await context.prepare({ token: context.token, messageId, payloadHash }) || signal.aborted
29
+ || !await context.beforeSend() || signal.aborted || Date.now() >= context.deadline)
30
+ return 'not-capable';
31
+ let onAbort;
32
+ try {
33
+ await Promise.race([
34
+ client.session.promptAsync({ path: { id: sessionId }, body: { messageID: messageId, parts: [{ type: 'text', text }] }, signal }),
35
+ new Promise((_, reject) => { onAbort = () => reject(signal.reason); signal.addEventListener('abort', onAbort, { once: true }); if (signal.aborted)
36
+ onAbort(); }),
37
+ ]);
38
+ // promptAsync acknowledges only submission, not native acceptance or presentation.
39
+ return 'unknown';
40
+ }
41
+ catch {
42
+ return 'unknown';
43
+ }
44
+ finally {
45
+ if (onAbort)
46
+ signal.removeEventListener('abort', onAbort);
47
+ }
48
+ },
49
+ };
50
+ }
@@ -0,0 +1,10 @@
1
+ /** Producer-neutral display facts; independent of model-facing delivery text. */
2
+ export type CardPresentation = {
3
+ readonly kind: 'presentation';
4
+ readonly origin: string;
5
+ readonly author?: string;
6
+ readonly ref?: string;
7
+ readonly badge?: string;
8
+ readonly body: string;
9
+ };
10
+ export declare function isCardPresentation(value: unknown): value is CardPresentation;
@@ -0,0 +1,7 @@
1
+ export function isCardPresentation(value) {
2
+ if (!value || typeof value !== 'object' || Array.isArray(value))
3
+ return false;
4
+ const item = value;
5
+ return item.kind === 'presentation' && typeof item.origin === 'string' && typeof item.body === 'string'
6
+ && ['author', 'ref', 'badge'].every(key => item[key] === undefined || typeof item[key] === 'string');
7
+ }
package/dist/ports.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import type { ActDraft, HydratedAct, SayRelation, Owner, HardCap, Seat, StoredAct, WakeAddress, WatchLease } from './model.js';
1
+ import type { ActDraft, HydratedAct, Owner, HardCap, Seat, StoredAct, WakeAddress, WatchLease, DirectedNotificationRoute } from './model.js';
2
2
  import type { ActivitySearch } from './square-facade.js';
3
3
  import type { IndexPage, FactPage } from './neutral.js';
4
- export type { WakeTransportPort, WakePresentation, WakeProjectionContext, WakeProjection, WakeRequest, WakeOutcome } from './channel-transport.js';
4
+ import type { MetadataMatch } from './delivery-deferral.js';
5
+ import type { ProducerRead, ProducerWrite } from './square-post.js';
5
6
  export type { IndexPage, FactPage } from './neutral.js';
6
- import type { ChannelRead, ChannelWrite, ChannelWriteAuthority, ChannelDeliveryEvidence, ChannelDeliveryClaimInput, ChannelDeliveryClaimResult, ChannelDeliveryPreparation, ChannelDeliveryCompletion, MessageId, EndpointGeneration } from './channel-ports.js';
7
- export type { ChannelArtifact, ChannelRead, ChannelWrite, ChannelWriteAuthority, ChannelHeader, MessageFact, MessageAddressFact, MessageHead, MessageFilter, MessageDraft, EndpointFact, EndpointRef, ReceiveClaimFact, ChannelDeliveryEvidence, ChannelDeliveryClaimInput, ChannelDeliveryClaimResult, ChannelDeliveryPreparation, ChannelDeliveryCompletion } from './channel-ports.js';
8
7
  export type ActivityIndex = number;
9
8
  export interface SquareHeader {
10
9
  readonly revision: number;
@@ -43,7 +42,6 @@ export interface ActivityHead {
43
42
  readonly actor: string;
44
43
  readonly reach?: 'bell';
45
44
  readonly reply?: ActivityIndex;
46
- readonly messageIndex?: number;
47
45
  }
48
46
  export interface ActivityFilter {
49
47
  readonly afterIndex?: number;
@@ -54,11 +52,9 @@ export interface ActivityFilter {
54
52
  readonly actors?: readonly string[];
55
53
  readonly actorMatch?: 'identity' | 'exact';
56
54
  readonly afterAt?: number;
57
- /** Shared query predicates, applied before paging or counting. */
58
- readonly metadataMatch?: import('./delivery-deferral.js').MetadataMatch;
55
+ readonly metadataMatch?: MetadataMatch;
59
56
  readonly search?: ActivitySearch;
60
57
  }
61
- /** Upper attention filter over its own landed reasons and lower message addresses. */
62
58
  export interface DirectedFilter extends Omit<ActivityFilter, 'actors' | 'actorMatch'> {
63
59
  readonly unseenOnly?: true;
64
60
  readonly currentGeneration?: true;
@@ -70,21 +66,24 @@ export interface DirectedHead {
70
66
  readonly at: number;
71
67
  readonly actor: string;
72
68
  readonly recipient: string;
73
- readonly route: import('./model.js').DirectedNotificationRoute;
74
- readonly messageIndex: number;
75
- readonly generation?: EndpointGeneration;
69
+ readonly route: DirectedNotificationRoute;
70
+ readonly generation?: string;
76
71
  }
77
72
  export interface LandedRecipient {
78
73
  readonly recipient: string;
79
- readonly route: import('./model.js').DirectedNotificationRoute;
80
- readonly generation?: EndpointGeneration;
74
+ readonly route: DirectedNotificationRoute;
75
+ readonly generation?: string;
76
+ readonly owner?: Owner;
81
77
  }
82
78
  export interface ReceiptFacts {
83
79
  readonly latestAt?: number;
84
80
  readonly latestActorActivityIndex?: number;
85
81
  }
86
82
  export type SeatRef = Pick<Seat, 'participant' | 'owner' | 'generation'>;
87
- export type ArtifactWriteAuthority = ChannelWriteAuthority | {
83
+ export type ArtifactWriteAuthority = {
84
+ readonly kind: 'participant';
85
+ readonly seat: SeatRef;
86
+ } | {
88
87
  readonly kind: 'host-cleanup';
89
88
  readonly seat: SeatRef;
90
89
  } | {
@@ -96,34 +95,26 @@ export type ArtifactWriteAuthority = ChannelWriteAuthority | {
96
95
  readonly kind: 'takeover';
97
96
  readonly participant: string;
98
97
  readonly owner: Owner;
99
- readonly expectedGeneration: EndpointGeneration;
98
+ readonly expectedGeneration: string;
100
99
  readonly wakeAddress?: WakeAddress;
100
+ } | {
101
+ readonly kind: 'producer';
102
+ } | {
103
+ readonly kind: 'send';
104
+ readonly actor: string;
101
105
  };
102
- export type DeliveryEvidence = ChannelDeliveryEvidence;
103
- export type DeliveryClaimInput = ChannelDeliveryClaimInput;
104
- export type DeliveryClaimResult = ChannelDeliveryClaimResult;
105
- export type DeliveryPreparation = ChannelDeliveryPreparation;
106
- export type DeliveryCompletion = ChannelDeliveryCompletion;
107
- export type { MessageId, EndpointGeneration };
108
- /** Domain facts exist only during a synchronous artifact callback. Values are detached. */
109
- export interface SquareRead {
110
- /** Lower Channel facts. Upper layers may use them; Channel never sees this composite. */
111
- readonly channel: ChannelRead;
112
- seat(requestedName: string): Seat | undefined;
106
+ /** All callbacks are synchronous and their facts cannot escape the transaction. */
107
+ export interface SquareRead extends ProducerRead {
108
+ seat(name: string): Seat | undefined;
113
109
  seatsForOwner(owner: Owner): readonly Seat[];
114
- deliveryEvidence(participant: string, generation: EndpointGeneration, message?: MessageId): readonly DeliveryEvidence[];
115
- deliveryByToken(claimToken: string): DeliveryEvidence | undefined;
116
- cancelledThrough(participant: string, generation: EndpointGeneration): number | undefined;
117
110
  header(): SquareHeader;
118
- participant(requestedName: string): ParticipantFact | undefined;
111
+ participant(name: string): ParticipantFact | undefined;
119
112
  participantCounts(): ParticipantCounts;
120
113
  roster(): readonly ParticipantFact[];
121
114
  attention(listener: string): readonly AttentionFact[];
122
115
  incomingAttention(sender: string): readonly AttentionFact[];
123
116
  activity(index: ActivityIndex): StoredAct | undefined;
124
- activityByMessage(messageIndex: number): SayRelation | undefined;
125
117
  activityHeads(filter: ActivityFilter, page: IndexPage): FactPage<ActivityHead>;
126
- /** The explicit content projection: each say resolved against its Channel message in one read. */
127
118
  activities(indexes: readonly ActivityIndex[]): readonly HydratedAct[];
128
119
  activityCount(filter: ActivityFilter): number;
129
120
  contextIndexes(centers: readonly ActivityIndex[], before: number, after: number): readonly ActivityIndex[];
@@ -133,24 +124,18 @@ export interface SquareRead {
133
124
  directed(recipient: string, filter: DirectedFilter, page: IndexPage): FactPage<DirectedHead>;
134
125
  directedAt(recipient: string, index: ActivityIndex): DirectedHead | undefined;
135
126
  directedCount(recipient: string, filter: DirectedFilter): number;
136
- receipt(requestedName: string, index: ActivityIndex): {
127
+ receipt(name: string, index: ActivityIndex): {
137
128
  readonly at: number;
138
129
  } | undefined;
139
- receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
140
- lease(requestedName: string): WatchLease | undefined;
141
- deliveryDeferrals(participant: string, generation: EndpointGeneration): readonly import('./delivery-deferral.js').DeliveryDeferralFact[];
130
+ receiptFacts(name: string, afterIndex: number): ReceiptFacts;
131
+ lease(name: string): WatchLease | undefined;
142
132
  }
143
- export interface SquareWrite extends SquareRead {
144
- readonly channel: ChannelWrite;
133
+ export interface SquareWrite extends SquareRead, ProducerWrite {
145
134
  land(draft: ActDraft): StoredAct;
146
- updateWakeAddress(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
147
- /** Join authority only; preserves membership and its establishing generation. */
148
- reattachSeat(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
149
- /** Host-cleanup authority only; stale/already-detached coordinates are a no-op. */
150
- detachSeat(seat: SeatRef): boolean;
151
- /** Upper catch claim: the lease plus the message coverage it currently owns. */
152
- putLease(requestedName: string, lease: WatchLease, generation?: EndpointGeneration, suppressed?: readonly number[]): boolean;
153
- removeLease(requestedName: string, expectedLeaseId: string, generation?: EndpointGeneration): boolean;
135
+ putLease(name: string, lease: WatchLease, generation?: string): boolean;
136
+ removeLease(name: string, expectedLeaseId: string, generation?: string): boolean;
137
+ markSeen(recipient: string, indexes: readonly number[], at: number): number;
138
+ setSeatAttachment(seat: SeatRef, attachment: 'attached' | 'detached'): boolean;
154
139
  }
155
140
  export interface SquareReadResult<R> {
156
141
  readonly value: R;
@@ -159,60 +144,16 @@ export interface SquareReadResult<R> {
159
144
  export interface SquareCommitResult<R> extends SquareReadResult<R> {
160
145
  readonly changed: boolean;
161
146
  }
162
- /** Atomic artifact access; no framing, SQL or lazy transaction capability escapes. */
163
147
  export interface SquareArtifactPort {
164
148
  read<R>(select: (facts: SquareRead) => R, signal?: AbortSignal): Promise<SquareReadResult<R>>;
165
- /** Participant/domain writes must carry an explicit owner; no owner-less mutation path exists. */
166
149
  transactAs<R>(authority: ArtifactWriteAuthority, change: (facts: SquareWrite) => R, signal?: AbortSignal): Promise<SquareCommitResult<R>>;
167
- /** Bounded cleanup/evidence settlement on this same connection after custody was acquired.
168
- * Independent of request/lifetime cancellation; explicit close and authority fences still apply. */
169
150
  settleAs<R>(authority: ArtifactWriteAuthority, change: (facts: SquareWrite) => R): Promise<SquareCommitResult<R>>;
170
151
  revision(signal?: AbortSignal): Promise<number>;
171
152
  deliveryRevision(signal?: AbortSignal): Promise<number>;
172
- claimDelivery(input: DeliveryClaimInput, signal?: AbortSignal): Promise<DeliveryClaimResult>;
173
- prepareDelivery(input: DeliveryPreparation, signal?: AbortSignal): Promise<boolean>;
174
- completeDelivery(input: DeliveryCompletion, signal?: AbortSignal): Promise<boolean>;
175
- /** Persist suppression through a fixed message, or atomically capture the current artifact head. */
176
- cancelDeliveries(seat: SeatRef, through?: number, signal?: AbortSignal): Promise<boolean>;
177
153
  changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
178
154
  close(): Promise<void>;
179
155
  }
180
- /** Explicit lifetime for an artifact connection reused by a long-lived caller.
181
- * Only the connection is shared: every open still derives its own environment,
182
- * clock, ledger and transport, and no request signal becomes a connection lifetime.
183
- * The owner holds one location and refuses a second rather than sharing the wrong
184
- * square; the caller lends the connection and never closes it. */
185
156
  export interface SquareResourceOwner {
186
- /** The shared connection for a location. Never close it; the owner does. */
187
157
  artifact(location: string): SquareArtifactPort;
188
158
  close(): Promise<void>;
189
159
  }
190
- /** Executor-neutral presentation capability; intentionally separate from wake transport. */
191
- export interface PresentationSinkPort {
192
- present(message: MessageFact): void | Promise<void>;
193
- }
194
- export interface PresentationResult {
195
- readonly presented: boolean;
196
- readonly message?: MessageFact;
197
- }
198
- export interface PresentPendingInput {
199
- readonly artifact: SquareArtifactPort;
200
- readonly location: string;
201
- readonly participant: string;
202
- readonly message: string | number;
203
- readonly sink: PresentationSinkPort;
204
- /** Existing wake observed by a native receiver; the sink must not inject content. */
205
- readonly observationOf?: string;
206
- /** Captured artifact seat; used to fence both presentation and consumption. */
207
- readonly seat?: SeatRef;
208
- readonly generation?: EndpointGeneration;
209
- readonly now?: number;
210
- readonly timeoutMs?: number;
211
- /** Pre-effect work control only; successful sink evidence settles independently, with a bounded lifetime. */
212
- readonly signal?: AbortSignal;
213
- /** Boundary previews that were clipped remain pending for a later full presentation. */
214
- readonly markSeen?: boolean;
215
- /** Receiver-specific pre-effect eligibility, never a veto of an already successful sink. */
216
- readonly current?: (facts: SquareRead) => boolean;
217
- }
218
- import type { MessageFact } from './channel-ports.js';
@@ -1,7 +1,8 @@
1
- import { type Owner } from './owner.js';
2
- import type { AutomaticProvider } from './participant-identity.js';
3
- import type { WakeAddress } from './model.js';
4
- /** Host shutdown suspends execution, not membership. Discovery never grants custody. */
5
- export declare function detachSessionSeats(provider: AutomaticProvider, sessionId: string, env?: NodeJS.ProcessEnv): Promise<void>;
6
- /** Restore only detached, standing seats of this exact owner before starting a receiver. */
7
- export declare function restoreSessionSeats(owner: Owner, env?: NodeJS.ProcessEnv, wakeAddress?: WakeAddress, signal?: AbortSignal): Promise<void>;
1
+ import { type DeliveryRoute, type DeliverySession } from './delivery-ledger.js';
2
+ import type { Owner } from './owner.js';
3
+ /** Rotate the captured session generation; omitted routes retain its registrations. */
4
+ export declare function attachDeliverySession(owner: Owner, env?: NodeJS.ProcessEnv, routes?: readonly DeliveryRoute[], signal?: AbortSignal): Promise<DeliverySession | false>;
5
+ /** Replace the supplied route kinds while preserving every other registered route. */
6
+ export declare function attachDeliverySessionRoutes(owner: Owner, env: NodeJS.ProcessEnv | undefined, routes: readonly DeliveryRoute[], signal?: AbortSignal): Promise<DeliverySession | false>;
7
+ export declare function detachDeliverySession(owner: Owner, generation: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
8
+ export declare function markDeliverySessionDeleted(owner: Owner, generation: string, env?: NodeJS.ProcessEnv): Promise<boolean>;
@@ -1,47 +1,45 @@
1
- import { Square } from './square-api.js';
2
- import { harnessOwner } from './owner.js';
3
- import { projectSessionCandidates, projectSessionSeats } from './inbox.js';
4
- function ownerFor(provider, sessionId) {
5
- return harnessOwner({ channel: provider === 'claude' ? 'claude-code' : provider, session: sessionId });
1
+ import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
2
+ function openLedger(env) {
3
+ return DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
6
4
  }
7
- /** Host shutdown suspends execution, not membership. Discovery never grants custody. */
8
- export async function detachSessionSeats(provider, sessionId, env = process.env) {
9
- const owner = ownerFor(provider, sessionId);
10
- for (const { location, seat } of await projectSessionSeats({ owner, env })) {
11
- let square;
12
- try {
13
- square = await Square.at({ path: location, env });
14
- await square.detach(seat.participant, { owner, generation: seat.generation });
15
- }
16
- catch { /* One unavailable or replaced seat must not block cleanup of the others. */ }
17
- finally {
18
- await square?.close();
19
- }
20
- }
5
+ /** Rotate the captured session generation; omitted routes retain its registrations. */
6
+ export async function attachDeliverySession(owner, env = process.env, routes, signal) {
7
+ return attach(owner, env, routes, false, signal);
8
+ }
9
+ /** Replace the supplied route kinds while preserving every other registered route. */
10
+ export async function attachDeliverySessionRoutes(owner, env = process.env, routes, signal) {
11
+ return attach(owner, env, routes, true, signal);
21
12
  }
22
- /** Restore only detached, standing seats of this exact owner before starting a receiver. */
23
- export async function restoreSessionSeats(owner, env = process.env, wakeAddress, signal) {
24
- const locations = new Set((await projectSessionCandidates({ owner, env })).map(candidate => candidate.location));
25
- for (const location of locations) {
13
+ async function attach(owner, env, routes, merge, signal) {
14
+ if (signal?.aborted)
15
+ return false;
16
+ const ledger = await openLedger(env);
17
+ try {
18
+ const previous = await ledger.session(owner);
26
19
  if (signal?.aborted)
27
- return;
28
- let square;
29
- try {
30
- square = await Square.at({ path: location, env }, { signal });
31
- for (const seat of await square.seatsOf(owner)) {
32
- if (signal?.aborted)
33
- return;
34
- if (seat.attachment !== 'detached')
35
- continue;
36
- try {
37
- await square.reattach(seat.name, { owner, wakeAddress }, { signal });
38
- }
39
- catch { /* A replacement or one failed seat does not block its peers. */ }
40
- }
41
- }
42
- catch { /* An indexed location is only a candidate, not a promise of availability. */ }
43
- finally {
44
- await square?.close();
45
- }
20
+ return false;
21
+ const selected = merge && routes ? [...(previous?.routes ?? []).filter(old => !routes.some(route => route.kind === old.kind)), ...routes] : routes ?? previous?.routes ?? [];
22
+ return await ledger.attach({ owner, expectedGeneration: previous?.generation ?? null, routes: selected });
23
+ }
24
+ finally {
25
+ await ledger.close();
26
+ }
27
+ }
28
+ export async function detachDeliverySession(owner, generation, env = process.env) {
29
+ const ledger = await openLedger(env);
30
+ try {
31
+ return await ledger.detach({ owner, generation });
32
+ }
33
+ finally {
34
+ await ledger.close();
35
+ }
36
+ }
37
+ export async function markDeliverySessionDeleted(owner, generation, env = process.env) {
38
+ const ledger = await openLedger(env);
39
+ try {
40
+ return await ledger.markDeleted({ owner, generation });
41
+ }
42
+ finally {
43
+ await ledger.close();
46
44
  }
47
45
  }
@@ -1,8 +1,8 @@
1
1
  import { type Owner } from './model.js';
2
- import type { HistoryQuery, HistoryResult, JoinOptions, JoinResult, LifecycleResult, ObservationOptions, OperationControl, Participant, SquareAtInput, SquareBuildInput, SquareLocation, StatusOptions, StatusResult, ParticipantsResult, RecoveryResult, DiscoveryOptions, InboxOptions, InboxResult, StreamOptions, StreamRecord, Activity, PerceivedActivity, ChannelSendInput, ChannelSendResult } from './square-facade.js';
2
+ import type { HistoryQuery, HistoryResult, JoinOptions, JoinResult, ObservationOptions, OperationControl, Participant, SquareAtInput, SquareBuildInput, SquareLocation, StatusOptions, StatusResult, ParticipantsResult, RecoveryResult, DiscoveryOptions, InboxOptions, InboxResult, StreamOptions, StreamRecord, Activity, PerceivedActivity, SendInput, SendResult } from './square-facade.js';
3
3
  export type * from './square-facade.js';
4
4
  export { createSquareResourceOwner } from './artifact.js';
5
- export { squareClock } from './channel-internal.js';
5
+ export { squareClock } from './operation-control.js';
6
6
  export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
7
7
  export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
8
8
  export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
@@ -12,32 +12,35 @@ export declare class SqliteSquare {
12
12
  readonly location: string;
13
13
  private readonly artifact;
14
14
  private readonly clock;
15
- private readonly wakeTransport?;
15
+ private readonly env;
16
+ private readonly closesArtifact;
16
17
  private readonly deliveryLocation;
17
- private readonly channel;
18
18
  private constructor();
19
+ private reconcile;
20
+ private flush;
19
21
  static at(input: SquareAtInput, control?: OperationControl): Promise<SqliteSquare>;
20
22
  static build(input: SquareBuildInput, control?: OperationControl): Promise<SqliteSquare>;
21
23
  static inMemory(input: Omit<SquareBuildInput, 'path' | 'resources'>): SqliteSquare;
22
- /** Agora join composes membership and endpoint custody in one transaction. */
24
+ /** Join establishes membership and local producer custody in one transaction. */
23
25
  private joinSeat;
24
26
  private requiredSeat;
25
- private reattachSeat;
26
- private endHeldSeat;
27
- private cancelHeld;
28
27
  private observation;
29
28
  private contextObservation;
29
+ /** Host liveness is a read-only, non-atomic overlay; it never changes membership. */
30
+ private withHostPresence;
30
31
  private handle;
31
32
  private static readonly ParticipantHandle;
32
- send(input: ChannelSendInput, control?: OperationControl): Promise<ChannelSendResult>;
33
+ send(input: SendInput, control?: OperationControl): Promise<SendResult>;
33
34
  participant(name: string, options: {
34
35
  readonly owner: Owner;
35
36
  }): Promise<Participant>;
36
37
  join(requested: string, options: JoinOptions, control?: OperationControl): Promise<JoinResult>;
37
- reattach(name: string, { owner, wakeAddress }: Pick<JoinOptions, 'owner' | 'wakeAddress'>, control?: OperationControl): Promise<Participant | null>;
38
38
  history(query?: HistoryQuery, control?: OperationControl): Promise<HistoryResult>;
39
39
  status(options?: StatusOptions, control?: OperationControl): Promise<StatusResult>;
40
- participants(options?: ObservationOptions, control?: OperationControl): Promise<ParticipantsResult>;
40
+ /** Local mode reads only World-known facts; it does not verify host attachment. */
41
+ participants(options?: ObservationOptions & {
42
+ readonly local?: boolean;
43
+ }, control?: OperationControl): Promise<ParticipantsResult>;
41
44
  recovery(requested?: string, control?: OperationControl): Promise<RecoveryResult>;
42
45
  stream(options: StreamOptions & {
43
46
  readonly recipient: string;
@@ -48,18 +51,6 @@ export declare class SqliteSquare {
48
51
  stream(options: StreamOptions, control?: OperationControl): AsyncIterable<StreamRecord>;
49
52
  seatsOf(owner: Owner): Promise<readonly import('./square-facade.js').HeldSeat[]>;
50
53
  close(): Promise<void>;
51
- cancelDeliveries(name: string, identity: {
52
- readonly owner: Owner;
53
- readonly generation: string;
54
- }): Promise<boolean>;
55
- detach(name: string, identity: {
56
- readonly owner: Owner;
57
- readonly generation: string;
58
- }): Promise<true | null>;
59
- endHeld(name: string, identity: {
60
- readonly owner: Owner;
61
- readonly generation: string;
62
- }): Promise<LifecycleResult | null>;
63
54
  }
64
55
  export declare function diagnoseSquare(input: SquareLocation, control?: OperationControl): Promise<import("./artifact.js").DiagnoseResult>;
65
56
  export declare function discover(input: SquareAtInput, options?: DiscoveryOptions, control?: OperationControl): Promise<import("./square-facade.js").DiscoveryResult>;