@astrosheep/square 0.3.106 → 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 (206) hide show
  1. package/dist/activity-feed.js +1 -1
  2. package/dist/artifact-store.d.ts +41 -0
  3. package/dist/artifact-store.js +272 -0
  4. package/dist/artifact.d.ts +6 -12
  5. package/dist/artifact.js +43 -344
  6. package/dist/attention-presentation.d.ts +13 -7
  7. package/dist/attention-presentation.js +29 -68
  8. package/dist/boundary-presentation.d.ts +3 -16
  9. package/dist/boundary-presentation.js +29 -109
  10. package/dist/card-presentation.d.ts +1 -0
  11. package/dist/card-presentation.js +2 -0
  12. package/dist/catch-decisions.d.ts +2 -2
  13. package/dist/catch-decisions.js +7 -3
  14. package/dist/catch-selection.d.ts +8 -0
  15. package/dist/catch-selection.js +5 -0
  16. package/dist/claude-delivery.d.ts +14 -12
  17. package/dist/claude-delivery.js +42 -142
  18. package/dist/claude-mcp-context.js +2 -2
  19. package/dist/claude-mod.js +28 -140
  20. package/dist/claude-transport.d.ts +5 -0
  21. package/dist/claude-transport.js +34 -0
  22. package/dist/cli/context.d.ts +2 -2
  23. package/dist/cli/context.js +7 -10
  24. package/dist/cli/maintenance-commands.d.ts +0 -4
  25. package/dist/cli/maintenance-commands.js +2 -29
  26. package/dist/cli/observation-commands.d.ts +2 -0
  27. package/dist/cli/observation-commands.js +12 -2
  28. package/dist/cli/program.js +1 -1
  29. package/dist/cli/registry.js +4 -3
  30. package/dist/codex-address.d.ts +2 -2
  31. package/dist/codex-boundary-state.d.ts +1 -5
  32. package/dist/codex-boundary-state.js +0 -4
  33. package/dist/codex-hook.d.ts +7 -10
  34. package/dist/codex-hook.js +69 -33
  35. package/dist/codex-queue.d.ts +10 -11
  36. package/dist/codex-queue.js +32 -34
  37. package/dist/codex-watch.d.ts +2 -0
  38. package/dist/codex-watch.js +100 -0
  39. package/dist/decisions.js +3 -3
  40. package/dist/delivery-deferral.d.ts +1 -19
  41. package/dist/delivery-deferral.js +1 -29
  42. package/dist/delivery-frame.d.ts +7 -0
  43. package/dist/delivery-frame.js +11 -0
  44. package/dist/delivery-ledger-sqlite.d.ts +97 -0
  45. package/dist/delivery-ledger-sqlite.js +496 -0
  46. package/dist/delivery-ledger.d.ts +196 -0
  47. package/dist/delivery-ledger.js +268 -0
  48. package/dist/delivery-lifetime.d.ts +6 -0
  49. package/dist/delivery-lifetime.js +45 -0
  50. package/dist/harness-links.js +61 -7
  51. package/dist/harness.js +12 -17
  52. package/dist/help.js +3 -6
  53. package/dist/http-delivery.d.ts +5 -9
  54. package/dist/http-delivery.js +20 -79
  55. package/dist/inbox.d.ts +6 -57
  56. package/dist/inbox.js +24 -380
  57. package/dist/index.d.ts +7 -4
  58. package/dist/index.js +5 -2
  59. package/dist/mcp.d.ts +2 -1
  60. package/dist/mcp.js +40 -70
  61. package/dist/message.d.ts +7 -0
  62. package/dist/message.js +23 -0
  63. package/dist/model.d.ts +15 -91
  64. package/dist/model.js +2 -140
  65. package/dist/neutral.d.ts +72 -0
  66. package/dist/neutral.js +27 -0
  67. package/dist/notifications.d.ts +24 -26
  68. package/dist/notifications.js +39 -143
  69. package/dist/operation-control.d.ts +4 -0
  70. package/dist/operation-control.js +20 -0
  71. package/dist/operation-presentation.d.ts +1 -2
  72. package/dist/operation-presentation.js +1 -7
  73. package/dist/owner.d.ts +2 -2
  74. package/dist/owner.js +1 -1
  75. package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
  76. package/dist/packages/agent-delivery/src/claude-native.js +5 -0
  77. package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
  78. package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
  79. package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
  80. package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
  81. package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
  82. package/dist/packages/agent-delivery/src/codex.js +41 -4
  83. package/dist/packages/agent-delivery/src/index.d.ts +3 -1
  84. package/dist/packages/agent-delivery/src/index.js +1 -1
  85. package/dist/packages/agent-delivery/src/paseo.js +11 -2
  86. package/dist/packages/agent-delivery/src/types.d.ts +1 -0
  87. package/dist/paseo-delivery.d.ts +5 -5
  88. package/dist/paseo-delivery.js +19 -99
  89. package/dist/paseo.d.ts +1 -1
  90. package/dist/paseo.js +1 -1
  91. package/dist/plugins/opencode/index.d.ts +5 -3
  92. package/dist/plugins/opencode/index.js +99 -21
  93. package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
  94. package/dist/plugins/opencode-shared/delivery.js +109 -0
  95. package/dist/plugins/opencode-v1/index.d.ts +28 -0
  96. package/dist/plugins/opencode-v1/index.js +41 -0
  97. package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
  98. package/dist/plugins/opencode-v1/runtime.js +152 -0
  99. package/dist/plugins/opencode-v1/transport.d.ts +13 -0
  100. package/dist/plugins/opencode-v1/transport.js +50 -0
  101. package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
  102. package/dist/plugins/paseo/shared/card-presentation.js +7 -0
  103. package/dist/ports.d.ts +33 -214
  104. package/dist/presentation.d.ts +5 -5
  105. package/dist/presentation.js +1 -0
  106. package/dist/runtime.js +2 -2
  107. package/dist/search.js +1 -1
  108. package/dist/session-lifecycle.d.ts +8 -7
  109. package/dist/session-lifecycle.js +40 -42
  110. package/dist/sqlite-framing.d.ts +8 -0
  111. package/dist/sqlite-framing.js +31 -0
  112. package/dist/sqlite-square.d.ts +16 -26
  113. package/dist/sqlite-square.js +199 -262
  114. package/dist/sqlite-values.d.ts +21 -0
  115. package/dist/sqlite-values.js +78 -0
  116. package/dist/square-api.d.ts +1 -1
  117. package/dist/square-api.js +5 -8
  118. package/dist/square-core.d.ts +0 -1
  119. package/dist/square-domain.d.ts +12 -10
  120. package/dist/square-domain.js +105 -223
  121. package/dist/square-facade.d.ts +25 -76
  122. package/dist/square-location.d.ts +6 -1
  123. package/dist/square-location.js +1 -1
  124. package/dist/square-post.d.ts +53 -0
  125. package/dist/square-post.js +4 -0
  126. package/dist/square-posting.d.ts +13 -0
  127. package/dist/square-posting.js +174 -0
  128. package/dist/square-producer-sqlite.d.ts +11 -0
  129. package/dist/square-producer-sqlite.js +210 -0
  130. package/dist/square-receive.d.ts +2 -3
  131. package/dist/square-sqlite.d.ts +33 -42
  132. package/dist/square-sqlite.js +236 -768
  133. package/dist/square-tools.d.ts +8 -0
  134. package/dist/square-tools.js +39 -0
  135. package/dist/square-values.d.ts +27 -0
  136. package/dist/square-values.js +90 -0
  137. package/dist/views.d.ts +6 -6
  138. package/dist/views.js +14 -9
  139. package/dist/wake-sink.d.ts +4 -11
  140. package/dist/wake-sink.js +8 -32
  141. package/dist/wake-transports.d.ts +4 -0
  142. package/dist/wake-transports.js +11 -0
  143. package/examples/symmetric-domains.mjs +29 -249
  144. package/guides/change-waits.md +13 -53
  145. package/guides/claude-native.md +19 -172
  146. package/guides/codex-delivery.md +55 -78
  147. package/guides/delivery-primitives.md +48 -207
  148. package/guides/operation-cancellation.md +2 -5
  149. package/guides/participant-ownership.md +25 -89
  150. package/guides/pi-native-delivery.md +19 -56
  151. package/guides/pi-tools.md +6 -8
  152. package/guides/row-runtime-delivery-evidence.md +2 -0
  153. package/guides/row-storage-design.md +19 -44
  154. package/guides/square-api.md +113 -538
  155. package/package.json +8 -8
  156. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  157. package/plugins/claude/hooks/hooks.json +1 -1
  158. package/plugins/claude/{hooks/register.js → mods/square.js} +4 -7
  159. package/plugins/claude/types.d.ts +1 -1
  160. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  161. package/plugins/codex/hooks/hooks.json +11 -23
  162. package/plugins/opencode/index.ts +91 -23
  163. package/plugins/opencode-shared/delivery.ts +91 -0
  164. package/plugins/opencode-v1/index.ts +36 -0
  165. package/plugins/opencode-v1/runtime.ts +134 -0
  166. package/plugins/opencode-v1/transport.ts +41 -0
  167. package/plugins/paseo/OVERVIEW.md +5 -5
  168. package/plugins/paseo/README.md +7 -6
  169. package/plugins/paseo/client/markdown.tsx +5 -5
  170. package/plugins/paseo/client/square-message.tsx +28 -24
  171. package/plugins/paseo/client/transform.ts +8 -5
  172. package/plugins/paseo/shared/card-presentation.ts +16 -0
  173. package/plugins/paseo/shared/message.ts +6 -11
  174. package/plugins/pi/square-pi.js +167 -451
  175. package/dist/channel.d.ts +0 -74
  176. package/dist/channel.js +0 -92
  177. package/dist/claude-delivery-status.d.ts +0 -13
  178. package/dist/claude-delivery-status.js +0 -46
  179. package/dist/cmd/notify-once.d.ts +0 -2
  180. package/dist/cmd/notify-once.js +0 -29
  181. package/dist/delivery-attempts.d.ts +0 -52
  182. package/dist/delivery-attempts.js +0 -73
  183. package/dist/delivery-effects.d.ts +0 -139
  184. package/dist/delivery-effects.js +0 -163
  185. package/dist/delivery-health.d.ts +0 -51
  186. package/dist/delivery-health.js +0 -173
  187. package/dist/delivery-operations.d.ts +0 -9
  188. package/dist/delivery-operations.js +0 -168
  189. package/dist/delivery-primitives.d.ts +0 -5
  190. package/dist/delivery-primitives.js +0 -3
  191. package/dist/delivery.d.ts +0 -45
  192. package/dist/delivery.js +0 -106
  193. package/dist/plugins/opencode/delivery.d.ts +0 -19
  194. package/dist/plugins/opencode/delivery.js +0 -238
  195. package/dist/presentation-operations.d.ts +0 -18
  196. package/dist/presentation-operations.js +0 -86
  197. package/dist/square-index.d.ts +0 -16
  198. package/dist/square-index.js +0 -106
  199. package/dist/square-legacy-framing.d.ts +0 -11
  200. package/dist/square-legacy-framing.js +0 -64
  201. package/dist/square-migration-host-ledger.d.ts +0 -26
  202. package/dist/square-migration-host-ledger.js +0 -169
  203. package/dist/square-migration.d.ts +0 -30
  204. package/dist/square-migration.js +0 -599
  205. package/guides/migration.md +0 -156
  206. package/plugins/opencode/delivery.ts +0 -211
@@ -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,7 +1,9 @@
1
- import type { Act, ActivityId, Owner, HardCap, Seat, StoredAct, WakeAddress, WakeRoute, WatchLease } from './model.js';
2
- import type { AttemptOutcome } from './delivery-attempts.js';
3
- import type { MetadataMatch, DeliveryDeferralFact } from './delivery-deferral.js';
1
+ import type { ActDraft, HydratedAct, Owner, HardCap, Seat, StoredAct, WakeAddress, WatchLease, DirectedNotificationRoute } from './model.js';
4
2
  import type { ActivitySearch } from './square-facade.js';
3
+ import type { IndexPage, FactPage } from './neutral.js';
4
+ import type { MetadataMatch } from './delivery-deferral.js';
5
+ import type { ProducerRead, ProducerWrite } from './square-post.js';
6
+ export type { IndexPage, FactPage } from './neutral.js';
5
7
  export type ActivityIndex = number;
6
8
  export interface SquareHeader {
7
9
  readonly revision: number;
@@ -50,20 +52,12 @@ export interface ActivityFilter {
50
52
  readonly actors?: readonly string[];
51
53
  readonly actorMatch?: 'identity' | 'exact';
52
54
  readonly afterAt?: number;
53
- /** Shared query predicates, applied before paging or counting. */
54
55
  readonly metadataMatch?: MetadataMatch;
55
56
  readonly search?: ActivitySearch;
56
57
  }
57
- export interface IndexPage {
58
- readonly edge: 'oldest' | 'newest';
59
- readonly limit: number;
60
- }
61
- export interface FactPage<T> {
62
- readonly items: readonly T[];
63
- readonly hasMore: boolean;
64
- }
65
58
  export interface DirectedFilter extends Omit<ActivityFilter, 'actors' | 'actorMatch'> {
66
59
  readonly unseenOnly?: true;
60
+ readonly currentGeneration?: true;
67
61
  readonly senders?: readonly string[];
68
62
  readonly attentionOnly?: true;
69
63
  }
@@ -72,18 +66,21 @@ export interface DirectedHead {
72
66
  readonly at: number;
73
67
  readonly actor: string;
74
68
  readonly recipient: string;
75
- readonly route: import('./attention.js').AudienceRoute;
69
+ readonly route: DirectedNotificationRoute;
70
+ readonly generation?: string;
71
+ }
72
+ export interface LandedRecipient {
73
+ readonly recipient: string;
74
+ readonly route: DirectedNotificationRoute;
75
+ readonly generation?: string;
76
+ readonly owner?: Owner;
76
77
  }
77
- export type LandedRecipient = import('./attention.js').AudienceRecipient;
78
78
  export interface ReceiptFacts {
79
79
  readonly latestAt?: number;
80
80
  readonly latestActorActivityIndex?: number;
81
81
  }
82
82
  export type SeatRef = Pick<Seat, 'participant' | 'owner' | 'generation'>;
83
83
  export type ArtifactWriteAuthority = {
84
- readonly kind: 'send';
85
- readonly actor: string;
86
- } | {
87
84
  readonly kind: 'participant';
88
85
  readonly seat: SeatRef;
89
86
  } | {
@@ -98,88 +95,27 @@ export type ArtifactWriteAuthority = {
98
95
  readonly kind: 'takeover';
99
96
  readonly participant: string;
100
97
  readonly owner: Owner;
101
- readonly expectedGeneration: ActivityId;
98
+ readonly expectedGeneration: string;
102
99
  readonly wakeAddress?: WakeAddress;
103
- };
104
- export interface DeliveryEvidence {
105
- readonly participant: string;
106
- readonly generation: ActivityId;
107
- readonly activity: ActivityId;
108
- readonly kind: 'wake' | 'presentation';
109
- readonly outcome: AttemptOutcome;
110
- readonly attemptN: number;
111
- readonly at: number;
112
- readonly expiresAt?: number;
113
- readonly claimToken?: string;
114
- readonly route?: WakeAddress;
115
- readonly signature?: string;
116
- readonly message?: string;
117
- readonly diagnostic?: import('./model.js').JsonValue;
118
- readonly correlation?: {
119
- readonly harness: 'claude' | 'opencode' | 'pi';
120
- readonly token?: string;
121
- readonly messageId?: string;
122
- readonly payloadHash?: string;
123
- };
124
- readonly nativeObservations?: readonly {
125
- readonly eventId: string;
126
- readonly messageId: string;
127
- readonly state: 'held' | 'released' | 'dropped';
128
- readonly reason?: string;
129
- readonly observedAt: number;
130
- }[];
131
- }
132
- export interface DeliveryClaimInput {
133
- readonly seat: SeatRef;
134
- readonly activity: ActivityId;
135
- readonly kind: 'wake' | 'presentation';
136
- /** Observe an existing wake's exact token; never authorizes another content injection. */
137
- readonly observationOf?: string;
138
- readonly route?: WakeAddress;
139
- readonly at: number;
140
- readonly leaseMs: number;
141
- readonly claimToken: string;
142
- }
143
- export type DeliveryClaimResult = {
144
- readonly status: 'acquired';
145
- readonly claimToken: string;
146
- readonly attemptN: number;
147
100
  } | {
148
- readonly status: 'busy' | 'terminal' | 'stale' | 'deferred';
149
- readonly evidence?: DeliveryEvidence;
101
+ readonly kind: 'producer';
102
+ } | {
103
+ readonly kind: 'send';
104
+ readonly actor: string;
150
105
  };
151
- export interface DeliveryPreparation {
152
- readonly seat: SeatRef;
153
- readonly claimToken: string;
154
- readonly correlation: NonNullable<DeliveryEvidence['correlation']>;
155
- }
156
- export interface DeliveryCompletion {
157
- readonly seat: SeatRef;
158
- readonly activity: ActivityId;
159
- readonly claimToken: string;
160
- readonly outcome: Exclude<DeliveryEvidence['outcome'], 'dispatching'>;
161
- readonly at: number;
162
- readonly signature?: string;
163
- readonly message?: string;
164
- readonly diagnostic?: import('./model.js').JsonValue;
165
- readonly correlation?: DeliveryEvidence['correlation'];
166
- }
167
- /** Domain facts exist only during a synchronous artifact callback. Values are detached. */
168
- export interface SquareRead {
169
- 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;
170
109
  seatsForOwner(owner: Owner): readonly Seat[];
171
- deliveryEvidence(participant: string, generation: ActivityId, activity?: ActivityId): readonly DeliveryEvidence[];
172
- deliveryByToken(claimToken: string): DeliveryEvidence | undefined;
173
- cancelledThrough(participant: string, generation: ActivityId): number | undefined;
174
110
  header(): SquareHeader;
175
- participant(requestedName: string): ParticipantFact | undefined;
111
+ participant(name: string): ParticipantFact | undefined;
176
112
  participantCounts(): ParticipantCounts;
177
113
  roster(): readonly ParticipantFact[];
178
114
  attention(listener: string): readonly AttentionFact[];
179
115
  incomingAttention(sender: string): readonly AttentionFact[];
180
116
  activity(index: ActivityIndex): StoredAct | undefined;
181
117
  activityHeads(filter: ActivityFilter, page: IndexPage): FactPage<ActivityHead>;
182
- activities(indexes: readonly ActivityIndex[]): readonly StoredAct[];
118
+ activities(indexes: readonly ActivityIndex[]): readonly HydratedAct[];
183
119
  activityCount(filter: ActivityFilter): number;
184
120
  contextIndexes(centers: readonly ActivityIndex[], before: number, after: number): readonly ActivityIndex[];
185
121
  sayOrdinals(indexes: readonly ActivityIndex[]): Readonly<Record<number, number>>;
@@ -188,46 +124,18 @@ export interface SquareRead {
188
124
  directed(recipient: string, filter: DirectedFilter, page: IndexPage): FactPage<DirectedHead>;
189
125
  directedAt(recipient: string, index: ActivityIndex): DirectedHead | undefined;
190
126
  directedCount(recipient: string, filter: DirectedFilter): number;
191
- pendingAudience(afterExclusive: number, throughInclusive: number, activityLimit: number): {
192
- readonly groups: readonly {
193
- readonly index: ActivityIndex;
194
- readonly at: number;
195
- readonly actor: string;
196
- readonly recipients: readonly LandedRecipient[];
197
- }[];
198
- readonly hasMore: boolean;
199
- };
200
- receipt(requestedName: string, index: ActivityIndex): {
127
+ receipt(name: string, index: ActivityIndex): {
201
128
  readonly at: number;
202
129
  } | undefined;
203
- receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
204
- lease(requestedName: string): WatchLease | undefined;
205
- deliveryDeferrals(participant: string, generation: ActivityId): readonly DeliveryDeferralFact[];
130
+ receiptFacts(name: string, afterIndex: number): ReceiptFacts;
131
+ lease(name: string): WatchLease | undefined;
206
132
  }
207
- export interface SquareWrite extends SquareRead {
208
- land(activity: Act & {
209
- readonly at: number;
210
- }): StoredAct;
211
- updateWakeAddress(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
212
- /** Join authority only; preserves membership and its establishing generation. */
213
- reattachSeat(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
214
- /** Host-cleanup authority only; stale/already-detached coordinates are a no-op. */
215
- detachSeat(seat: SeatRef): boolean;
216
- cancelThrough(seat: SeatRef, through: ActivityIndex): boolean;
217
- claimDelivery(input: DeliveryClaimInput): DeliveryClaimResult;
218
- prepareDelivery(input: DeliveryPreparation): boolean;
219
- completeDelivery(input: DeliveryCompletion): boolean;
220
- observeNativeDelivery(input: {
221
- readonly seat: SeatRef;
222
- readonly claimToken: string;
223
- readonly observation: NonNullable<DeliveryEvidence['nativeObservations']>[number];
224
- }): boolean;
225
- markSeen(requestedName: string, indexes: readonly ActivityIndex[], at: number): number;
226
- putLease(requestedName: string, lease: WatchLease, generation?: ActivityId): boolean;
227
- removeLease(requestedName: string, expectedLeaseId: string, generation?: ActivityId): boolean;
228
- putDeliveryDeferral(seat: SeatRef, id: string, match: MetadataMatch, expiresAt: number, now: number): boolean;
229
- renewDeliveryDeferral(seat: SeatRef, id: string, expiresAt: number, now: number): boolean;
230
- releaseDeliveryDeferral(seat: SeatRef, id: string, now: number): boolean;
133
+ export interface SquareWrite extends SquareRead, ProducerWrite {
134
+ land(draft: ActDraft): StoredAct;
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;
231
139
  }
232
140
  export interface SquareReadResult<R> {
233
141
  readonly value: R;
@@ -236,105 +144,16 @@ export interface SquareReadResult<R> {
236
144
  export interface SquareCommitResult<R> extends SquareReadResult<R> {
237
145
  readonly changed: boolean;
238
146
  }
239
- /** Atomic artifact access; no framing, SQL or lazy transaction capability escapes. */
240
147
  export interface SquareArtifactPort {
241
148
  read<R>(select: (facts: SquareRead) => R, signal?: AbortSignal): Promise<SquareReadResult<R>>;
242
- /** Participant/domain writes must carry an explicit owner; no owner-less mutation path exists. */
243
149
  transactAs<R>(authority: ArtifactWriteAuthority, change: (facts: SquareWrite) => R, signal?: AbortSignal): Promise<SquareCommitResult<R>>;
244
- /** Bounded cleanup/evidence settlement on this same connection after custody was acquired.
245
- * Independent of request/lifetime cancellation; explicit close and authority fences still apply. */
246
150
  settleAs<R>(authority: ArtifactWriteAuthority, change: (facts: SquareWrite) => R): Promise<SquareCommitResult<R>>;
247
151
  revision(signal?: AbortSignal): Promise<number>;
248
152
  deliveryRevision(signal?: AbortSignal): Promise<number>;
249
- claimDelivery(input: DeliveryClaimInput, signal?: AbortSignal): Promise<DeliveryClaimResult>;
250
- prepareDelivery(input: DeliveryPreparation, signal?: AbortSignal): Promise<boolean>;
251
- completeDelivery(input: DeliveryCompletion, signal?: AbortSignal): Promise<boolean>;
252
- /** Persist suppression through a fixed index, or atomically capture the current artifact head. */
253
- cancelDeliveries(seat: SeatRef, through?: ActivityIndex, signal?: AbortSignal): Promise<boolean>;
254
153
  changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
255
154
  close(): Promise<void>;
256
155
  }
257
- /** Explicit lifetime for an artifact connection reused by a long-lived caller.
258
- * Only the connection is shared: every open still derives its own environment,
259
- * clock, ledger and transport, and no request signal becomes a connection lifetime.
260
- * The owner holds one location and refuses a second rather than sharing the wrong
261
- * square; the caller lends the connection and never closes it. */
262
156
  export interface SquareResourceOwner {
263
- /** The shared connection for a location. Never close it; the owner does. */
264
157
  artifact(location: string): SquareArtifactPort;
265
158
  close(): Promise<void>;
266
159
  }
267
- /** Sole wake executor protocol; implementations own only transport readiness and sending. */
268
- export interface WakeTransportPort {
269
- /** Capability check that performs no external wake and writes no evidence. */
270
- probe?(route: WakeRoute): Promise<boolean | {
271
- readonly outcome: 'not-capable';
272
- readonly diagnostic?: string;
273
- }>;
274
- attempt(request: WakeRequest, timeoutMs: number, beforeSend: () => Promise<boolean>): Promise<WakeOutcome>;
275
- }
276
- export interface WakeRequest {
277
- readonly location: string;
278
- readonly participant: string;
279
- readonly activity: string;
280
- readonly actor: string;
281
- readonly seat: SeatRef;
282
- readonly claimToken?: string;
283
- readonly attemptN?: number;
284
- readonly route: WakeRoute;
285
- /** Artifact context for native adapters that must persist correlation before I/O. */
286
- readonly artifact?: SquareArtifactPort;
287
- }
288
- /** Executor-neutral presentation capability; intentionally separate from wake transport. */
289
- export interface PresentationSinkPort {
290
- present(activity: StoredAct): void | Promise<void>;
291
- }
292
- export type WakeOutcome = {
293
- readonly outcome: 'accepted';
294
- readonly signature?: string;
295
- } | {
296
- readonly outcome: 'failed';
297
- readonly message?: string;
298
- readonly signature?: string;
299
- readonly diagnostic?: unknown;
300
- } | {
301
- readonly outcome: 'not-capable';
302
- readonly diagnostic?: string;
303
- } | {
304
- readonly outcome: 'unknown';
305
- readonly signature?: string;
306
- readonly message?: string;
307
- readonly diagnostic?: unknown;
308
- };
309
- export interface DeliverPendingInput {
310
- readonly artifact: SquareArtifactPort;
311
- readonly transport: WakeTransportPort;
312
- readonly location: string;
313
- readonly timeoutMs?: number;
314
- readonly now?: number;
315
- readonly activity?: string | number;
316
- }
317
- export interface PresentationResult {
318
- readonly presented: boolean;
319
- readonly activity?: StoredAct;
320
- }
321
- export interface PresentPendingInput {
322
- readonly artifact: SquareArtifactPort;
323
- readonly location: string;
324
- readonly participant: string;
325
- readonly activity: string | number;
326
- readonly sink: PresentationSinkPort;
327
- /** Existing wake observed by a native receiver; the sink must not inject content. */
328
- readonly observationOf?: string;
329
- /** Captured artifact seat; used to fence both presentation and consumption. */
330
- readonly seat?: SeatRef;
331
- readonly generation?: ActivityId;
332
- readonly now?: number;
333
- readonly timeoutMs?: number;
334
- /** Pre-effect work control only; successful sink evidence settles independently, with a bounded lifetime. */
335
- readonly signal?: AbortSignal;
336
- /** Boundary previews that were clipped remain pending for a later full presentation. */
337
- readonly markSeen?: boolean;
338
- /** Receiver-specific pre-effect eligibility, never a veto of an already successful sink. */
339
- readonly current?: (facts: SquareRead) => boolean;
340
- }
@@ -1,4 +1,4 @@
1
- import { type StoredAct, type RoomChangeAct } from './model.js';
1
+ import { type HydratedAct, type RoomChangeAct } from './model.js';
2
2
  export { participantIdentity } from './participant-identity.js';
3
3
  import { type Perception } from './square-core.js';
4
4
  import type { Activity, UnreadActivitySummary, ParticipantSummary } from './square-facade.js';
@@ -72,19 +72,19 @@ interface RenderedEvent {
72
72
  /** The rendered body was cut short by `preview`. */
73
73
  bodyClipped: boolean;
74
74
  }
75
- export declare function renderEventCli(event: StoredAct | Activity, opts?: {
75
+ export declare function renderEventCli(event: HydratedAct | Activity, opts?: {
76
76
  now?: number;
77
77
  preview?: number;
78
78
  mention?: string;
79
79
  }): string;
80
- export declare function renderAmbientEventFacts(event: StoredAct | Activity, viewer: string, opts?: {
80
+ export declare function renderAmbientEventFacts(event: HydratedAct | Activity, viewer: string, opts?: {
81
81
  now?: number;
82
82
  preview?: number;
83
83
  presencePreview?: number;
84
84
  mention?: string;
85
85
  perception?: Perception;
86
86
  }): RenderedEvent;
87
- export declare function renderAmbientEvent(event: StoredAct | Activity, viewer: string, opts?: {
87
+ export declare function renderAmbientEvent(event: HydratedAct | Activity, viewer: string, opts?: {
88
88
  now?: number;
89
89
  preview?: number;
90
90
  presencePreview?: number;
@@ -101,7 +101,7 @@ export declare function renderActivityUncertain(opts: ParticipantOutputOptions &
101
101
  export declare function renderExpressWaiting(opts: ExpressWaitingOptions): string;
102
102
  export declare function renderExpressNoWait(opts: ExpressNoWaitOptions): string;
103
103
  export declare function renderPresenceAnchor(names: readonly string[]): string;
104
- export declare function renderGrepActivitiesView(visible: readonly StoredAct[], totalMatches: number, noTruncate: boolean | undefined, squarePath: string, pattern: string, fixed?: boolean, perception?: (act: StoredAct) => Perception): string;
104
+ export declare function renderGrepActivitiesView(visible: readonly HydratedAct[], totalMatches: number, noTruncate: boolean | undefined, squarePath: string, pattern: string, fixed?: boolean, perception?: (act: HydratedAct) => Perception): string;
105
105
  export declare function renderActivityLimit(opts: ActivityLimitOptions): string;
106
106
  export declare function renderWatchAlreadyActive(opts: ParticipantOutputOptions): string;
107
107
  export declare function renderWatchForceTakeover(_opts: ParticipantOutputOptions): string;
@@ -177,6 +177,7 @@ function renderEventCliFacts(event, opts = {}) {
177
177
  bodyClipped: body.clipped,
178
178
  };
179
179
  }
180
+ case 'say':
180
181
  case 'read':
181
182
  return { text: '', bodyClipped: false };
182
183
  }
package/dist/runtime.js CHANGED
@@ -30,7 +30,7 @@ export function watchTerminalStatus(facts, name) {
30
30
  /** A continuous prefix stops at the first unseen historical audience edge after join. */
31
31
  export function readCursor(facts, name) {
32
32
  const boundary = facts.participant(name)?.lastJoinIndex ?? -1;
33
- const block = facts.directed(name, { afterIndex: boundary, unseenOnly: true }, { edge: 'oldest', limit: 1 }).items[0]?.index;
33
+ const block = facts.directed(name, { kinds: ['say'], afterIndex: boundary, unseenOnly: true }, { edge: 'oldest', limit: 1 }).items[0]?.index;
34
34
  const last = facts.activityHeads({
35
35
  afterIndex: boundary, ...(block === undefined ? {} : { beforeIndex: block }),
36
36
  kinds: ['join', 'done', 'say', 'listen', 'ignore', 'unlisten'],
@@ -42,7 +42,7 @@ export function presenceAnchor(facts, name) {
42
42
  const boundary = facts.participant(name)?.lastJoinIndex ?? -1;
43
43
  if (boundary < 0)
44
44
  return -1;
45
- const own = facts.activityHeads({ afterIndex: boundary, actors: [name] }, { edge: 'newest', limit: 1 }).items[0]?.index;
45
+ const own = facts.activityHeads({ afterIndex: boundary, actors: [name], kinds: ['join', 'done', 'say', 'read', 'listen', 'ignore', 'unlisten'] }, { edge: 'newest', limit: 1 }).items[0]?.index;
46
46
  const evidence = Math.max(boundary, own ?? boundary, facts.receiptFacts(name, boundary).latestActorActivityIndex ?? boundary);
47
47
  return facts.activityHeads({ afterIndex: boundary, throughIndex: evidence, kinds: ['say', 'done'] }, { edge: 'newest', limit: 1 }).items[0]?.index ?? -1;
48
48
  }
package/dist/search.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Shared case-insensitive search semantics for storage filtering and presentation.
2
2
  // Regex mode intentionally uses JavaScript's built-in engine, without a ReDoS
3
3
  // timeout. Callers should use literal mode for plain text and avoid untrusted regexes.
4
- import { SquareError } from './model.js';
4
+ import { SquareError } from './neutral.js';
5
5
  export function compileSearchPattern(pattern, fixed) {
6
6
  if (fixed)
7
7
  return new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i');
@@ -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>;