@astrosheep/square 0.4.4 → 0.4.6

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 (100) hide show
  1. package/dist/artifact-store.d.ts +1 -1
  2. package/dist/artifact-store.js +32 -15
  3. package/dist/artifact.d.ts +1 -4
  4. package/dist/artifact.js +0 -28
  5. package/dist/attention-presentation.js +1 -1
  6. package/dist/boundary-presentation.js +2 -2
  7. package/dist/claude-boundary-state.d.ts +6 -6
  8. package/dist/claude-boundary-state.js +13 -13
  9. package/dist/claude-hook.js +25 -24
  10. package/dist/claude-mcp-context.d.ts +2 -2
  11. package/dist/claude-mcp-context.js +6 -6
  12. package/dist/cli/context.js +3 -6
  13. package/dist/cli/json.js +1 -1
  14. package/dist/cli/mcp-server-command.js +3 -3
  15. package/dist/cli/observation-commands.d.ts +2 -2
  16. package/dist/cli/observation-commands.js +23 -51
  17. package/dist/cli/perceive-presentation.d.ts +3 -0
  18. package/dist/cli/{catch-presentation.js → perceive-presentation.js} +3 -3
  19. package/dist/cli/registry.js +2 -2
  20. package/dist/codex-address.d.ts +3 -0
  21. package/dist/codex-address.js +7 -0
  22. package/dist/codex-hook.d.ts +1 -1
  23. package/dist/codex-hook.js +11 -8
  24. package/dist/codex-queue.d.ts +3 -2
  25. package/dist/codex-queue.js +8 -11
  26. package/dist/codex-watch.d.ts +6 -1
  27. package/dist/codex-watch.js +27 -29
  28. package/dist/delivery-ledger-sqlite.d.ts +25 -20
  29. package/dist/delivery-ledger-sqlite.js +76 -61
  30. package/dist/delivery-ledger.d.ts +24 -19
  31. package/dist/delivery-ledger.js +150 -81
  32. package/dist/delivery-lifetime.d.ts +2 -2
  33. package/dist/delivery-lifetime.js +6 -18
  34. package/dist/help.js +12 -12
  35. package/dist/hints.d.ts +2 -2
  36. package/dist/hints.js +7 -7
  37. package/dist/index.d.ts +1 -1
  38. package/dist/index.js +1 -1
  39. package/dist/list.d.ts +2 -2
  40. package/dist/list.js +4 -3
  41. package/dist/mcp.d.ts +2 -2
  42. package/dist/mcp.js +8 -32
  43. package/dist/neutral.d.ts +5 -5
  44. package/dist/notifications.js +12 -6
  45. package/dist/operation-presentation.d.ts +8 -3
  46. package/dist/operation-presentation.js +50 -12
  47. package/dist/packages/agent-delivery/src/file-lock.js +4 -1
  48. package/dist/perceive-decisions.d.ts +28 -0
  49. package/dist/{catch-decisions.js → perceive-decisions.js} +24 -24
  50. package/dist/{catch-selection.d.ts → perceive-selection.d.ts} +2 -2
  51. package/dist/{catch-selection.js → perceive-selection.js} +3 -3
  52. package/dist/plugins/opencode/index.js +4 -4
  53. package/dist/plugins/opencode-shared/delivery.d.ts +6 -6
  54. package/dist/plugins/opencode-shared/delivery.js +27 -21
  55. package/dist/plugins/opencode-v1/runtime.js +3 -3
  56. package/dist/ports.d.ts +0 -4
  57. package/dist/presentation.d.ts +1 -1
  58. package/dist/presentation.js +13 -13
  59. package/dist/session-lifecycle.d.ts +2 -2
  60. package/dist/session-lifecycle.js +11 -11
  61. package/dist/sqlite-square.d.ts +2 -4
  62. package/dist/sqlite-square.js +47 -54
  63. package/dist/sqlite-transaction.d.ts +2 -0
  64. package/dist/sqlite-transaction.js +25 -3
  65. package/dist/square-api.d.ts +1 -1
  66. package/dist/square-api.js +1 -1
  67. package/dist/square-domain.js +1 -1
  68. package/dist/square-facade.d.ts +15 -13
  69. package/dist/square-location.js +1 -1
  70. package/dist/square-posting.d.ts +5 -5
  71. package/dist/square-posting.js +39 -38
  72. package/dist/square-tools.d.ts +15 -4
  73. package/dist/square-tools.js +35 -17
  74. package/dist/views.d.ts +7 -3
  75. package/dist/views.js +6 -3
  76. package/guides/architect.md +2 -2
  77. package/guides/change-waits.md +1 -1
  78. package/guides/codex-delivery.md +40 -35
  79. package/guides/delivery-primitives.md +22 -7
  80. package/guides/mcp.md +4 -2
  81. package/guides/operation-cancellation.md +11 -3
  82. package/guides/pi-native-delivery.md +1 -1
  83. package/guides/pi-tools.md +11 -6
  84. package/guides/receive-primitive.md +3 -3
  85. package/guides/square-api.md +15 -8
  86. package/package.json +2 -2
  87. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  88. package/plugins/claude/skills/square/SKILL.md +10 -10
  89. package/plugins/codex/.codex-plugin/plugin.json +2 -2
  90. package/plugins/codex/hooks/hooks.json +3 -0
  91. package/plugins/opencode/index.ts +4 -4
  92. package/plugins/opencode-shared/delivery.ts +27 -21
  93. package/plugins/opencode-v1/runtime.ts +3 -3
  94. package/plugins/paseo/client/markdown.tsx +9 -26
  95. package/plugins/paseo/client/square-message.tsx +10 -14
  96. package/plugins/pi/square-pi.js +100 -20
  97. package/skills/square/SKILL.md +10 -10
  98. package/dist/catch-decisions.d.ts +0 -28
  99. package/dist/cli/catch-presentation.d.ts +0 -3
  100. package/guides/mcp-connection-lifetime.md +0 -22
@@ -5,12 +5,13 @@ import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
5
5
  import { attachDeliverySessionRoutes, detachDeliverySession, markDeliverySessionDeleted } from '../../session-lifecycle.js';
6
6
  const routeKind = 'opencode-server';
7
7
  const ownerFor = (sessionId) => harnessOwner({ channel: 'opencode', session: sessionId });
8
- const open = (env) => DeliveryLedger.open(env.SQUARE_DELIVERY ? { location: env.SQUARE_DELIVERY } : {});
8
+ const open = (env, signal) => DeliveryLedger.open({ location: env.SQUARE_DELIVERY, signal });
9
9
  const payloadHash = (payload) => createHash('sha256').update(payload).digest('hex');
10
- export async function listOpenCodeSessions(env) {
11
- const ledger = await open(env);
10
+ export async function listOpenCodeSessions(env, signal) {
11
+ signal ??= AbortSignal.timeout(5000);
12
+ const ledger = await open(env, signal);
12
13
  try {
13
- return (await ledger.sessions()).flatMap(session => {
14
+ return (await ledger.sessions(signal)).flatMap(session => {
14
15
  const coordinate = decodeHarnessOwner(session.owner);
15
16
  return session.state !== 'deleted' && coordinate?.channel === 'opencode' ? [coordinate.session] : [];
16
17
  });
@@ -20,8 +21,9 @@ export async function listOpenCodeSessions(env) {
20
21
  }
21
22
  }
22
23
  /** The file observer is a hint; callers must always reread the ledger after an edge. */
23
- export async function observeOpenCodeSessions(env) {
24
- const ledger = await open(env);
24
+ export async function observeOpenCodeSessions(env, signal) {
25
+ signal ??= AbortSignal.timeout(5000);
26
+ const ledger = await open(env, signal);
25
27
  const observer = await ledger.observe(ownerFor('discovery'));
26
28
  return { observer, close: async () => { observer.close(); await ledger.close(); } };
27
29
  }
@@ -29,25 +31,29 @@ export async function publishOpenCodeAddress(sessionId, env, signal) {
29
31
  const session = await attachDeliverySessionRoutes(ownerFor(sessionId), env, [{ kind: routeKind, address: { sessionId } }], signal);
30
32
  return session ? session.generation : undefined;
31
33
  }
32
- export async function currentOpenCodeGeneration(sessionId, env) {
33
- const ledger = await open(env);
34
+ export async function currentOpenCodeGeneration(sessionId, env, signal) {
35
+ signal ??= AbortSignal.timeout(5000);
36
+ const ledger = await open(env, signal);
34
37
  try {
35
- return (await ledger.session(ownerFor(sessionId)))?.generation;
38
+ return (await ledger.session(ownerFor(sessionId), signal))?.generation;
36
39
  }
37
40
  finally {
38
41
  await ledger.close();
39
42
  }
40
43
  }
41
- export async function detachOpenCodeSession(sessionId, generation, env) {
42
- await detachDeliverySession(ownerFor(sessionId), generation, env);
44
+ export async function detachOpenCodeSession(sessionId, generation, env, signal) {
45
+ signal ??= AbortSignal.timeout(5000);
46
+ await detachDeliverySession(ownerFor(sessionId), generation, env, signal);
43
47
  }
44
- export async function endOpenCodeSession(sessionId, generation, env) {
45
- await markDeliverySessionDeleted(ownerFor(sessionId), generation, env);
48
+ export async function endOpenCodeSession(sessionId, generation, env, signal) {
49
+ signal ??= AbortSignal.timeout(5000);
50
+ await markDeliverySessionDeleted(ownerFor(sessionId), generation, env, signal);
46
51
  }
47
- export async function cancelOpenCodeDeliveries(sessionId, generation, env) {
48
- const ledger = await open(env);
52
+ export async function cancelOpenCodeDeliveries(sessionId, generation, env, signal) {
53
+ signal ??= AbortSignal.timeout(5000);
54
+ const ledger = await open(env, signal);
49
55
  try {
50
- return await ledger.cancel({ owner: ownerFor(sessionId), generation });
56
+ return await ledger.cancel({ owner: ownerFor(sessionId), generation }, signal);
51
57
  }
52
58
  finally {
53
59
  await ledger.close();
@@ -66,15 +72,15 @@ export async function observeOpenCodeContext(sessionId, generation, messages, en
66
72
  }));
67
73
  if (!observed.size || signal.aborted)
68
74
  return;
69
- const ledger = await open(env);
75
+ const ledger = await open(env, signal);
70
76
  try {
71
- for (const attempt of await ledger.handoffs(ownerFor(sessionId), { generation })) {
77
+ for (const attempt of await ledger.handoffs(ownerFor(sessionId), { generation }, signal)) {
72
78
  if (signal.aborted)
73
79
  return;
74
80
  const { correlation } = attempt;
75
81
  if (attempt.routeKind === routeKind && correlation.token === attempt.token && correlation.messageId !== undefined
76
82
  && correlation.payloadHash !== undefined && observed.get(correlation.messageId) === correlation.payloadHash)
77
- await ledger.observePresentation({ token: attempt.token, owner: ownerFor(sessionId), generation, routeKind, payloadHash: correlation.payloadHash }, { requireCurrent: true });
83
+ await ledger.observePresentation({ token: attempt.token, owner: ownerFor(sessionId), generation, routeKind, payloadHash: correlation.payloadHash }, { requireCurrent: true }, signal);
78
84
  }
79
85
  }
80
86
  finally {
@@ -83,13 +89,13 @@ export async function observeOpenCodeContext(sessionId, generation, messages, en
83
89
  }
84
90
  export async function receiveOpenCodePending(sessionId, generation, transport, env, signal) {
85
91
  const owner = ownerFor(sessionId);
86
- const ledger = await open(env);
92
+ const ledger = await open(env, signal);
87
93
  const lifetime = retainDeliveryLedger(ledger);
88
94
  const observer = await ledger.observe(owner);
89
95
  try {
90
96
  while (!signal.aborted) {
91
97
  const baseline = observer.generation;
92
- for (const card of await ledger.pending(owner, { generation })) {
98
+ for (const card of await ledger.pending(owner, { generation }, signal)) {
93
99
  if (signal.aborted)
94
100
  return;
95
101
  lifetime.track(await ledger.dispatchHandoff(owner, card.key, transport, { generation, timeoutMs: 5000, signal }));
@@ -4,8 +4,8 @@ import { legacyTransport } from './transport.js';
4
4
  export async function startReceiver(ctx) {
5
5
  const env = { ...process.env };
6
6
  const directory = await canonicalPath(ctx.directory);
7
- const discovery = await observeOpenCodeSessions(env);
8
7
  const lifetime = new AbortController();
8
+ const discovery = await observeOpenCodeSessions(env, lifetime.signal);
9
9
  const receivers = new Map();
10
10
  const terminal = new Set();
11
11
  const interrupting = new Set();
@@ -28,7 +28,7 @@ export async function startReceiver(ctx) {
28
28
  && await canonicalPath(info.directory) === directory;
29
29
  };
30
30
  const reconcile = async (active) => {
31
- const candidates = new Set(await listOpenCodeSessions(env));
31
+ const candidates = new Set(await listOpenCodeSessions(env, lifetime.signal));
32
32
  if (active)
33
33
  candidates.add(active);
34
34
  for (const id of candidates) {
@@ -40,7 +40,7 @@ export async function startReceiver(ctx) {
40
40
  const receiver = receivers.get(id);
41
41
  stop(id);
42
42
  if (receiver)
43
- await detachOpenCodeSession(id, receiver.generation, env);
43
+ await detachOpenCodeSession(id, receiver.generation, env, lifetime.signal);
44
44
  continue;
45
45
  }
46
46
  const existing = receivers.get(id);
package/dist/ports.d.ts CHANGED
@@ -153,7 +153,3 @@ export interface SquareArtifactPort {
153
153
  changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
154
154
  close(): Promise<void>;
155
155
  }
156
- export interface SquareResourceOwner {
157
- artifact(location: string): SquareArtifactPort;
158
- close(): Promise<void>;
159
- }
@@ -41,7 +41,7 @@ interface WatchStatusOptions extends ParticipantOutputOptions {
41
41
  participants: readonly ParticipantSummary[];
42
42
  now: number;
43
43
  };
44
- showCatchHint?: boolean;
44
+ showPerceiveHint?: boolean;
45
45
  ownActivityCount?: number;
46
46
  hardCap?: number | null;
47
47
  }
@@ -82,7 +82,7 @@ export function renderPresenceLines(recent, now, limit = 5) {
82
82
  ? (p.lastActiveAt === undefined ? 'stepped out of the square' : `stepped out of the square · ${formatRelativeTime(p.lastActiveAt, now)}`)
83
83
  : p.presence === 'away' ? 'is away'
84
84
  : p.presence === 'watching'
85
- ? (p.lastActiveAt === undefined ? 'is nearby · catching' : `is nearby · catching · ${formatRelativeTime(p.lastActiveAt, now)}`)
85
+ ? (p.lastActiveAt === undefined ? 'is nearby · perceiving' : `is nearby · perceiving · ${formatRelativeTime(p.lastActiveAt, now)}`)
86
86
  : p.lastActiveAt === undefined ? 'is nearby · quiet'
87
87
  : p.activityCount > 0 ? `is nearby · ${formatRelativeTime(p.lastActiveAt, now)}` : `is nearby · quiet · ${formatRelativeTime(p.lastActiveAt, now)}`;
88
88
  return ` ${glyph === '○' ? style('dim', glyph) : glyph} ${participantIdentity(p.name)}${style('dim', ` ${text}`)}`;
@@ -237,7 +237,7 @@ function renderUnreadSummary(activitySummaries) {
237
237
  ];
238
238
  }
239
239
  export function renderActivityBlocked(opts) {
240
- const readNowCommand = `${participantCommandPrefix(opts.squarePath, opts.name, opts.owner)} catch`;
240
+ const readNowCommand = `${participantCommandPrefix(opts.squarePath, opts.name, opts.owner)} perceive`;
241
241
  return withPathOutput(opts.squarePath, [
242
242
  `${style('blocked', '✕')} your activity hasn't landed — someone spoke while you were away`,
243
243
  ...renderUnreadSummary(opts.activitySummaries),
@@ -339,20 +339,20 @@ export function renderActivityLimit(opts) {
339
339
  }
340
340
  export function renderWatchAlreadyActive(opts) {
341
341
  return [
342
- `${style('blocked', '✕')} you are already catching`,
343
- ` · an active catch is already running for ${participantIdentity(opts.name)}`,
344
- ' · --replace lets a new catch take over',
345
- `${participantCommandPrefix(opts.squarePath, opts.name, opts.owner)} catch --idle 30m --replace`,
342
+ `${style('blocked', '✕')} you are already perceiving`,
343
+ ` · an active perceive is already running for ${participantIdentity(opts.name)}`,
344
+ ' · --replace lets a new perceive take over',
345
+ `${participantCommandPrefix(opts.squarePath, opts.name, opts.owner)} perceive --idle 30m --replace`,
346
346
  ].join('\n');
347
347
  }
348
348
  export function renderWatchForceTakeover(_opts) {
349
- return '✓ your new catch takes over';
349
+ return '✓ your new perceive takes over';
350
350
  }
351
351
  export function renderWatchReplaceMissing(_opts) {
352
- return '· no catch stood here — yours takes the spot';
352
+ return '· no perceive stood here — yours takes the spot';
353
353
  }
354
354
  export function renderWatchReplaced(_opts) {
355
- return '✕ a newer catch took over';
355
+ return '✕ a newer perceive took over';
356
356
  }
357
357
  export function renderWatchStatus(opts) {
358
358
  const others = opts.presence === undefined
@@ -365,18 +365,18 @@ export function renderWatchStatus(opts) {
365
365
  case 'idle':
366
366
  case 'drained': {
367
367
  const prefix = participantCommandPrefix(opts.squarePath, opts.name, opts.owner);
368
- // Quiet graduates with the idle length; an empty catch has no idle budget to report.
368
+ // Quiet graduates with the idle length; an empty perceive has no idle budget to report.
369
369
  const idleMs = opts.idleMs;
370
370
  const quiet = idleMs !== undefined && idleMs >= 60 * 60 * 1000
371
371
  ? style('dim', `○ dust lies thick — ${formatDuration(idleMs)} of quiet`)
372
372
  : idleMs !== undefined && idleMs >= 60 * 1000
373
373
  ? style('dim', `○ dust settles on the flagstones — ${formatDuration(idleMs)} of quiet`)
374
374
  : style('dim', '○ only footsteps in the square — nothing new for you');
375
- // The quiet report never repeats the catch just run.
376
- const hints = [`${prefix} catch --idle 30m`];
375
+ // The quiet report never repeats the perceive just run.
376
+ const hints = [`${prefix} perceive --idle 30m`];
377
377
  return [
378
378
  quiet,
379
- ...(opts.showCatchHint === false ? [] : hints),
379
+ ...(opts.showPerceiveHint === false ? [] : hints),
380
380
  ...presenceLines,
381
381
  ].join('\n');
382
382
  }
@@ -4,5 +4,5 @@ import type { Owner } from './owner.js';
4
4
  export declare function attachDeliverySession(owner: Owner, env?: NodeJS.ProcessEnv, routes?: readonly DeliveryRoute[], signal?: AbortSignal): Promise<DeliverySession | false>;
5
5
  /** Replace the supplied route kinds while preserving every other registered route. */
6
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>;
7
+ export declare function detachDeliverySession(owner: Owner, generation: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<boolean>;
8
+ export declare function markDeliverySessionDeleted(owner: Owner, generation: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<boolean>;
@@ -1,6 +1,6 @@
1
1
  import { DeliveryLedger, deliveryLedgerLocation } from './delivery-ledger.js';
2
- function openLedger(env) {
3
- return DeliveryLedger.open({ location: deliveryLedgerLocation(env) });
2
+ function openLedger(env, signal) {
3
+ return DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal });
4
4
  }
5
5
  /** Rotate the captured session generation; omitted routes retain its registrations. */
6
6
  export async function attachDeliverySession(owner, env = process.env, routes, signal) {
@@ -13,31 +13,31 @@ export async function attachDeliverySessionRoutes(owner, env = process.env, rout
13
13
  async function attach(owner, env, routes, merge, signal) {
14
14
  if (signal?.aborted)
15
15
  return false;
16
- const ledger = await openLedger(env);
16
+ const ledger = await openLedger(env, signal);
17
17
  try {
18
- const previous = await ledger.session(owner);
18
+ const previous = await ledger.session(owner, signal);
19
19
  if (signal?.aborted)
20
20
  return false;
21
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 });
22
+ return await ledger.attach({ owner, expectedGeneration: previous?.generation ?? null, routes: selected }, signal);
23
23
  }
24
24
  finally {
25
25
  await ledger.close();
26
26
  }
27
27
  }
28
- export async function detachDeliverySession(owner, generation, env = process.env) {
29
- const ledger = await openLedger(env);
28
+ export async function detachDeliverySession(owner, generation, env = process.env, signal) {
29
+ const ledger = await openLedger(env, signal);
30
30
  try {
31
- return await ledger.detach({ owner, generation });
31
+ return await ledger.detach({ owner, generation }, signal);
32
32
  }
33
33
  finally {
34
34
  await ledger.close();
35
35
  }
36
36
  }
37
- export async function markDeliverySessionDeleted(owner, generation, env = process.env) {
38
- const ledger = await openLedger(env);
37
+ export async function markDeliverySessionDeleted(owner, generation, env = process.env, signal) {
38
+ const ledger = await openLedger(env, signal);
39
39
  try {
40
- return await ledger.markDeleted({ owner, generation });
40
+ return await ledger.markDeleted({ owner, generation }, signal);
41
41
  }
42
42
  finally {
43
43
  await ledger.close();
@@ -1,10 +1,9 @@
1
1
  import { type Owner } from './model.js';
2
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
- export { createSquareResourceOwner } from './artifact.js';
5
4
  export { squareClock } from './operation-control.js';
6
5
  export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
7
- export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
6
+ export { PERCEIVE_DEFAULT_LIMIT, PERCEIVE_MAX_LIMIT } from './perceive-decisions.js';
8
7
  export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
9
8
  export { DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from './list.js';
10
9
  /** One concrete local resource, including its entry-resolved dependencies and lifetime. */
@@ -13,14 +12,13 @@ export declare class SqliteSquare {
13
12
  private readonly artifact;
14
13
  private readonly clock;
15
14
  private readonly env;
16
- private readonly closesArtifact;
17
15
  private readonly deliveryLocation;
18
16
  private constructor();
19
17
  private reconcile;
20
18
  private flush;
21
19
  static at(input: SquareAtInput, control?: OperationControl): Promise<SqliteSquare>;
22
20
  static build(input: SquareBuildInput, control?: OperationControl): Promise<SqliteSquare>;
23
- static inMemory(input: Omit<SquareBuildInput, 'path' | 'resources'>): SqliteSquare;
21
+ static inMemory(input: Omit<SquareBuildInput, 'path'>): SqliteSquare;
24
22
  /** Join establishes membership and local producer custody in one transaction. */
25
23
  private joinSeat;
26
24
  private requiredSeat;
@@ -10,11 +10,11 @@ import { validateMetadataMatch } from './delivery-deferral.js';
10
10
  import { flushSquarePosts, reconcileSquareDelivery, readSquareSessions, ensureInitialDeliverySession } from './square-posting.js';
11
11
  import { attentionChange, coreDone, coreListening, decideAct, decideJoin, resolveKnownName, validateDoneBody } from './decisions.js';
12
12
  import { WATCH_HEARTBEAT_MS, inSquareCount, presenceAnchor } from './runtime.js';
13
- import { catchLimit, decideCatch, observeCatchSelection, selectCatchPage } from './catch-decisions.js';
13
+ import { perceiveLimit, decidePerceive, observePerceiveSelection, selectPerceivePage } from './perceive-decisions.js';
14
14
  import { expressionContext } from './activity-feed.js';
15
15
  import { expressHint } from './hints.js';
16
16
  import { asSeatRef, participantAuthority, requireJoinName, requireOwner } from './seats.js';
17
- import { recoveryObservation, entryPresentation, queryStatus, queryParticipants, catchPresence, parseRequiredActivityId, toPublicActivity, toPerceivedActivity, publicActivities, selectHistory, NON_READ, HISTORY_MAX_LIMIT, HISTORY_DEFAULT_LIMIT } from './views.js';
17
+ import { recoveryObservation, entryPresentation, queryStatus, queryParticipants, perceivePresence, parseRequiredActivityId, toPublicActivity, toPerceivedActivity, publicActivities, selectHistory, NON_READ, HISTORY_MAX_LIMIT, HISTORY_DEFAULT_LIMIT } from './views.js';
18
18
  import { sessionInboxCounts } from './inbox.js';
19
19
  import { participantIdentity } from './participant-identity.js';
20
20
  import { joinRecoveryCommand } from './presentation.js';
@@ -23,10 +23,9 @@ import { createArtifactPort, createSquare, diagnoseSquareFile, prepareSquareBuil
23
23
  import { activityQueryFilter } from './activity-query.js';
24
24
  import { discoverSquares } from './list.js';
25
25
  import { streamSquare } from './stream.js';
26
- export { createSquareResourceOwner } from './artifact.js';
27
26
  export { squareClock } from './operation-control.js';
28
27
  export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
29
- export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
28
+ export { PERCEIVE_DEFAULT_LIMIT, PERCEIVE_MAX_LIMIT } from './perceive-decisions.js';
30
29
  export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
31
30
  export { DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from './list.js';
32
31
  function rejectUnsentActivity(operation) {
@@ -45,32 +44,27 @@ export class SqliteSquare {
45
44
  artifact;
46
45
  clock;
47
46
  env;
48
- closesArtifact;
49
47
  deliveryLocation;
50
- constructor(location, artifact, clock, env, closesArtifact = true, deliveryLocation = location) {
48
+ constructor(location, artifact, clock, env, deliveryLocation = location) {
51
49
  this.location = location;
52
50
  this.artifact = artifact;
53
51
  this.clock = clock;
54
52
  this.env = env;
55
- this.closesArtifact = closesArtifact;
56
53
  this.deliveryLocation = deliveryLocation;
57
54
  }
58
- reconcile() { return reconcileSquareDelivery(this.artifact, this.deliveryLocation, this.env, this.clock); }
55
+ reconcile(signal) { return reconcileSquareDelivery(this.artifact, this.deliveryLocation, this.env, this.clock, signal); }
59
56
  // Posting is retryable after commit; a delivery failure cannot make a landed write unsent.
60
- async flush() { await flushSquarePosts(this.artifact, this.deliveryLocation, this.env, this.clock).catch(() => undefined); }
57
+ async flush(signal) { await flushSquarePosts(this.artifact, this.deliveryLocation, this.env, this.clock, signal).catch(() => undefined); }
61
58
  static async at(input, control) {
62
59
  const env = { ...(input.env ?? process.env) }, location = squareLocation({ ...input, env });
63
60
  const clock = squareClock(env, input.clock);
64
- // A request can abort its own borrowed reads, never the owner's connection lifetime.
65
- const borrowed = input.resources?.artifact(location);
66
- const artifact = borrowed ?? createArtifactPort(location, undefined, control?.signal);
61
+ const artifact = createArtifactPort(location, undefined, control?.signal);
67
62
  try {
68
63
  await artifact.read(facts => facts.header(), control?.signal);
69
- return new SqliteSquare(location, artifact, clock, env, borrowed === undefined, await canonicalPath(location));
64
+ return new SqliteSquare(location, artifact, clock, env, await canonicalPath(location));
70
65
  }
71
66
  catch (error) {
72
- if (borrowed === undefined)
73
- await artifact.close();
67
+ await artifact.close();
74
68
  if (error instanceof InternalSquareError && error.code === 'not_found')
75
69
  throw new SquareError('unavailable', `Square is unavailable at ${location}`);
76
70
  throw error;
@@ -156,7 +150,7 @@ export class SqliteSquare {
156
150
  async withHostPresence(select, control) {
157
151
  const seats = (await this.artifact.read(facts => facts.roster().filter(person => person.joined)
158
152
  .flatMap(person => { const seat = facts.seat(person.name); return seat === undefined ? [] : [seat]; }), control?.signal)).value;
159
- const sessions = await readSquareSessions(seats, this.env);
153
+ const sessions = await readSquareSessions(seats, this.env, control?.signal);
160
154
  const captured = new Map(seats.map(seat => [nameKey(seat.participant), seat]));
161
155
  return (await this.artifact.read(facts => {
162
156
  const seat = (name) => {
@@ -197,14 +191,14 @@ export class SqliteSquare {
197
191
  let landedId;
198
192
  try {
199
193
  checkControl(control);
200
- await square.reconcile();
194
+ await square.reconcile(control?.signal);
201
195
  name = (await square.artifact.read(facts => resolveKnownName(facts, this.name), control?.signal)).value;
202
196
  const seat = this.seat;
203
197
  const context = !options.observe ? undefined : (await square.artifact.read(facts => expressionContext(facts, name), control?.signal)).value;
204
198
  while (true) {
205
199
  checkControl(control);
206
200
  try {
207
- await square.reconcile();
201
+ await square.reconcile(control?.signal);
208
202
  effect = 'unknown';
209
203
  const now = square.clock();
210
204
  const reply = rejectUnsentActivity(() => {
@@ -240,7 +234,7 @@ export class SqliteSquare {
240
234
  }, control?.signal)).value;
241
235
  landedId = formatActivityId(committed.stored.index);
242
236
  effect = 'landed';
243
- await square.flush();
237
+ await square.flush(control?.signal);
244
238
  const { stored, standingCount, ownActivityCount, hardCap, ...metadata } = committed;
245
239
  const hint = expressHint(ownActivityCount);
246
240
  return { activity: toPublicActivity(stored), ...metadata,
@@ -267,7 +261,7 @@ export class SqliteSquare {
267
261
  async listener(verb, target, options, control) {
268
262
  const square = this.owner;
269
263
  checkControl(control);
270
- await square.reconcile();
264
+ await square.reconcile(control?.signal);
271
265
  const name = (await square.artifact.read(facts => resolveKnownName(facts, this.name), control?.signal)).value;
272
266
  const seat = this.seat, now = square.clock();
273
267
  const result = (await square.artifact.transactAs(participantAuthority(seat), facts => {
@@ -278,7 +272,7 @@ export class SqliteSquare {
278
272
  target: activity?.target ?? target,
279
273
  } }) };
280
274
  }, control?.signal)).value;
281
- await square.flush();
275
+ await square.flush(control?.signal);
282
276
  return result;
283
277
  }
284
278
  listen(target, options = {}, control) { return this.listener('listen', target, options, control); }
@@ -292,27 +286,27 @@ export class SqliteSquare {
292
286
  }, control?.signal)).value;
293
287
  }
294
288
  /** Receipt selection rechecks the lease inside the writer, not at its last heartbeat. */
295
- async catchPage(name, seat, options, control, leaseId) {
296
- await this.owner.reconcile();
289
+ async perceivePage(name, seat, options, control, leaseId) {
290
+ await this.owner.reconcile(control?.signal);
297
291
  return (await this.owner.artifact.transactAs(participantAuthority(seat), facts => {
298
292
  if (leaseId !== undefined && facts.lease(name)?.leaseId !== leaseId)
299
293
  return null;
300
- return decideCatch(facts, name, options, this.owner.clock());
294
+ return decidePerceive(facts, name, options, this.owner.clock());
301
295
  }, control?.signal)).value;
302
296
  }
303
- async catch(options = {}, control) {
297
+ async perceive(options = {}, control) {
304
298
  const square = this.owner;
305
299
  let name = this.name, subject = 'caller';
306
300
  let leaseId, seat;
307
301
  let selection = options;
308
302
  const idle = options.idle ?? 0;
309
- const complete = async (caught, reason = 'drained') => {
310
- const view = caught ?? (await square.artifact.read(facts => observeCatchSelection(facts, name, [], {
303
+ const complete = async (perceived, reason = 'drained') => {
304
+ const view = perceived ?? (await square.artifact.read(facts => observePerceiveSelection(facts, name, [], {
311
305
  unseenOnly: true, senders: selection.from, ...(selection.mention === true ? { attentionOnly: true } : {}),
312
306
  }, square.clock()))).value;
313
307
  if (reason === 'drained')
314
308
  reason = view.terminalStatus ?? reason;
315
- await square.flush();
309
+ await square.flush(control?.signal);
316
310
  if (leaseId !== undefined) {
317
311
  await releaseWatchLease(square.artifact, seat, leaseId).catch(() => undefined);
318
312
  leaseId = undefined;
@@ -321,7 +315,7 @@ export class SqliteSquare {
321
315
  if (options.observe) {
322
316
  try {
323
317
  const observed = await square.withHostPresence((facts, available) => {
324
- const data = catchPresence(facts, square.clock());
318
+ const data = perceivePresence(facts, square.clock());
325
319
  return { presence: { ...data, participants: data.participants.map(person => ({ ...person, deliveryAvailable: available(person.name) })) },
326
320
  deliveryAvailable: available(view.participant) };
327
321
  }, control);
@@ -340,15 +334,15 @@ export class SqliteSquare {
340
334
  };
341
335
  try {
342
336
  checkControl(control);
343
- await square.reconcile();
337
+ await square.reconcile(control?.signal);
344
338
  validateName(name);
345
339
  if (!Number.isFinite(idle) || idle < 0)
346
- throw new SquareError('invalid_args', 'Catch idle duration must be a non-negative number');
340
+ throw new SquareError('invalid_args', 'Perceive idle duration must be a non-negative number');
347
341
  if (options.id !== undefined && ['idle', 'from', 'mention', 'limit', 'replace'].some(key => Object.hasOwn(options, key)))
348
- throw new SquareError('invalid_args', 'Catch id cannot be combined with idle, from, mention, limit or replace');
342
+ throw new SquareError('invalid_args', 'Perceive id cannot be combined with idle, from, mention, limit or replace');
349
343
  if (Object.hasOwn(options, 'replace') && idle === 0)
350
- throw new SquareError('invalid_args', 'Catch replacement requires a positive idle duration');
351
- catchLimit(options.limit);
344
+ throw new SquareError('invalid_args', 'Perceive replacement requires a positive idle duration');
345
+ perceiveLimit(options.limit);
352
346
  const probe = (await square.artifact.read(facts => {
353
347
  name = resolveKnownName(facts, this.name);
354
348
  seat = this.seat;
@@ -359,11 +353,11 @@ export class SqliteSquare {
359
353
  subject = 'caller';
360
354
  if (idle !== 0 || options.id !== undefined)
361
355
  return undefined;
362
- const page = selectCatchPage(facts, name, selection);
363
- return page.indexes.length === 0 ? observeCatchSelection(facts, page.participant, [], page.filter, square.clock()) : undefined;
356
+ const page = selectPerceivePage(facts, name, selection);
357
+ return page.indexes.length === 0 ? observePerceiveSelection(facts, page.participant, [], page.filter, square.clock()) : undefined;
364
358
  }, control?.signal)).value;
365
359
  if (idle === 0) {
366
- const data = probe ?? (await this.catchPage(name, seat, selection, control));
360
+ const data = probe ?? (await this.perceivePage(name, seat, selection, control));
367
361
  return await complete(data);
368
362
  }
369
363
  const watch = {
@@ -374,7 +368,7 @@ export class SqliteSquare {
374
368
  if (start.type === 'active')
375
369
  return await complete(undefined, 'already-active');
376
370
  leaseId = start.leaseId;
377
- control?.onProgress?.({ kind: 'catch-started', participant: name, idleMs: idle, replacementRequested: options.replace ?? false, replaced: start.replaced });
371
+ control?.onProgress?.({ kind: 'perceive-started', participant: name, idleMs: idle, replacementRequested: options.replace ?? false, replaced: start.replaced });
378
372
  const quietSince = square.clock();
379
373
  let nextHeartbeat = start.heartbeatAt + WATCH_HEARTBEAT_MS;
380
374
  while (true) {
@@ -388,11 +382,11 @@ export class SqliteSquare {
388
382
  return await complete(undefined, pulse.status);
389
383
  if (pulse.heartbeatAt !== undefined)
390
384
  nextHeartbeat = pulse.heartbeatAt + WATCH_HEARTBEAT_MS;
391
- const caught = await this.catchPage(name, seat, selection, control, leaseId);
392
- if (caught === null)
385
+ const perceived = await this.perceivePage(name, seat, selection, control, leaseId);
386
+ if (perceived === null)
393
387
  return await complete(undefined, 'replaced');
394
- if (caught.activities.length > 0)
395
- return await complete(caught);
388
+ if (perceived.activities.length > 0)
389
+ return await complete(perceived);
396
390
  if (square.clock() - quietSince >= idle) {
397
391
  const owns = await ownsWatchLease(square.artifact, seat, square.clock(), leaseId, control?.signal);
398
392
  return await complete(undefined, owns ? 'idle' : 'replaced');
@@ -402,7 +396,7 @@ export class SqliteSquare {
402
396
  }
403
397
  }
404
398
  catch (error) {
405
- throw failure(error, { operation: 'catch', subject, participant: name });
399
+ throw failure(error, { operation: 'perceive', subject, participant: name });
406
400
  }
407
401
  finally {
408
402
  // Cancellation releases only this captured generation and this lease.
@@ -459,7 +453,7 @@ export class SqliteSquare {
459
453
  validateDoneBody(body);
460
454
  checkControl(control);
461
455
  const square = this.owner;
462
- await square.reconcile();
456
+ await square.reconcile(control?.signal);
463
457
  const name = (await square.artifact.read(facts => resolveKnownName(facts, this.name), control?.signal)).value;
464
458
  const seat = this.seat, now = square.clock();
465
459
  const result = (await square.artifact.transactAs(participantAuthority(seat), facts => {
@@ -467,15 +461,15 @@ export class SqliteSquare {
467
461
  return { activity: toPublicActivity(stored), ...(!options.observe ? {} : { observation: square.observation(inSquareCount(facts), name, now),
468
462
  }) };
469
463
  }, control?.signal)).value;
470
- await square.flush();
464
+ await square.flush(control?.signal);
471
465
  return result;
472
466
  }
473
467
  };
474
468
  async send(input, control) {
475
469
  checkControl(control);
476
- await this.reconcile();
470
+ await this.reconcile(control?.signal);
477
471
  const result = (await this.artifact.transactAs({ kind: 'send', actor: input.from }, facts => facts.insertNotice(input, this.clock()), control?.signal)).value;
478
- await this.flush();
472
+ await this.flush(control?.signal);
479
473
  return result;
480
474
  }
481
475
  async participant(name, options) {
@@ -489,11 +483,11 @@ export class SqliteSquare {
489
483
  const last = options.last ?? HISTORY_DEFAULT_LIMIT;
490
484
  if (!Number.isSafeInteger(last) || last < 1 || last > HISTORY_MAX_LIMIT)
491
485
  throw new SquareError('invalid_args', 'Join history limit must be between 1 and 100');
492
- if (!await ensureInitialDeliverySession(requireOwner(options.owner), this.env, options.wakeAddress))
486
+ if (!await ensureInitialDeliverySession(requireOwner(options.owner), this.env, options.wakeAddress, control?.signal))
493
487
  throw new SquareError('already_joined', `${participantIdentity(name)} is away; resume the host session first`, { reason: 'needs_seat' });
494
- await this.reconcile();
488
+ await this.reconcile(control?.signal);
495
489
  const committed = await this.joinSeat(name, options, control);
496
- await this.flush();
490
+ await this.flush(control?.signal);
497
491
  const participant = this.handle(committed.seat);
498
492
  const result = { participant, activity: committed.activity, kind: committed.kind,
499
493
  ...(committed.observation === undefined ? {} : { observation: committed.observation }) };
@@ -579,14 +573,13 @@ export class SqliteSquare {
579
573
  .filter(seat => facts.participant(seat.participant)?.joined && seat.attachment === 'attached')
580
574
  .map(seat => ({ name: seat.participant, generation: seat.generation })))).value;
581
575
  }
582
- close() { return this.closesArtifact ? this.artifact.close() : Promise.resolve(); }
576
+ close() { return this.artifact.close(); }
583
577
  }
584
578
  export function diagnoseSquare(input, control) { checkControl(control); return diagnoseSquareFile(squareLocation(input)); }
585
579
  export function discover(input, options = {}, control) {
586
- if (input.path !== undefined || (input.env ?? process.env).SQUARE_LOCATION)
580
+ if (input.path !== undefined)
587
581
  squareLocation(input);
588
- const env = { ...(input.env ?? process.env) };
589
- return discoverSquares(input.cwd ?? process.cwd(), options, control, { clock: squareClock(env, input.clock) });
582
+ return discoverSquares(input.cwd ?? process.cwd(), options, control);
590
583
  }
591
584
  export async function inbox(input, owner, options = {}, control) {
592
585
  checkControl(control);
@@ -1,5 +1,7 @@
1
1
  import type { DatabaseSync } from 'node:sqlite';
2
2
  /** Yield rather than blocking the process while another connection owns SQLite. */
3
3
  export declare function retrySqliteBusy<T>(operation: () => T, signal?: AbortSignal, retryMs?: number | (() => number)): Promise<T>;
4
+ /** Cancel a queued caller promptly without releasing its place in the connection's serial chain. */
5
+ export declare function awaitSqliteQueue<T>(result: Promise<T>, signal: AbortSignal | undefined, started: () => boolean): Promise<T>;
4
6
  /** Acquisition may retry; COMMIT retries retain the callback's result and transaction. */
5
7
  export declare function inSqliteTransaction<T, V>(db: DatabaseSync, write: boolean, validate: () => V, change: (validated: V) => T, signal?: AbortSignal): Promise<T>;