@astrosheep/square 0.3.93 → 0.3.94

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 (116) hide show
  1. package/dist/activity-feed.d.ts +3 -5
  2. package/dist/activity-feed.js +16 -14
  3. package/dist/artifact.js +2 -2
  4. package/dist/boundary-presentation.d.ts +2 -2
  5. package/dist/catch-decisions.d.ts +4 -6
  6. package/dist/catch-decisions.js +4 -4
  7. package/dist/claude-delivery-status.js +10 -4
  8. package/dist/claude-delivery.js +19 -11
  9. package/dist/claude-mod.js +24 -53
  10. package/dist/cli/context.d.ts +9 -4
  11. package/dist/cli/context.js +30 -25
  12. package/dist/cli/express-presentation.d.ts +3 -1
  13. package/dist/cli/express-presentation.js +26 -14
  14. package/dist/cli/json.d.ts +3 -0
  15. package/dist/cli/json.js +14 -0
  16. package/dist/cli/mcp-server-command.js +2 -2
  17. package/dist/cli/observation-commands.d.ts +1 -3
  18. package/dist/cli/observation-commands.js +43 -56
  19. package/dist/cli/program.js +2 -2
  20. package/dist/cli/registry.js +6 -3
  21. package/dist/cli/square-commands.d.ts +1 -1
  22. package/dist/cli/square-commands.js +46 -54
  23. package/dist/codex-hook.d.ts +3 -2
  24. package/dist/codex-hook.js +13 -4
  25. package/dist/codex-queue.js +4 -4
  26. package/dist/decisions.d.ts +5 -11
  27. package/dist/decisions.js +9 -20
  28. package/dist/delivery-deferral.js +2 -2
  29. package/dist/delivery-health.d.ts +0 -1
  30. package/dist/delivery-health.js +1 -3
  31. package/dist/delivery-operations.d.ts +2 -2
  32. package/dist/delivery-operations.js +38 -38
  33. package/dist/delivery.d.ts +1 -1
  34. package/dist/delivery.js +1 -1
  35. package/dist/help.js +12 -10
  36. package/dist/inbox.d.ts +9 -22
  37. package/dist/inbox.js +23 -27
  38. package/dist/index.d.ts +1 -3
  39. package/dist/index.js +1 -1
  40. package/dist/mcp.d.ts +3 -0
  41. package/dist/mcp.js +23 -24
  42. package/dist/model.d.ts +8 -15
  43. package/dist/model.js +4 -7
  44. package/dist/notifications.d.ts +1 -1
  45. package/dist/notifications.js +6 -5
  46. package/dist/operation-presentation.d.ts +5 -4
  47. package/dist/operation-presentation.js +50 -45
  48. package/dist/owner.d.ts +11 -0
  49. package/dist/owner.js +32 -0
  50. package/dist/paseo-delivery.d.ts +4 -1
  51. package/dist/paseo-delivery.js +7 -12
  52. package/dist/paseo-state.d.ts +2 -2
  53. package/dist/paseo-state.js +6 -4
  54. package/dist/plugins/opencode/delivery.js +27 -15
  55. package/dist/ports.d.ts +13 -33
  56. package/dist/presentation-operations.js +4 -4
  57. package/dist/presentation.d.ts +5 -8
  58. package/dist/presentation.js +33 -43
  59. package/dist/runtime.d.ts +1 -7
  60. package/dist/runtime.js +0 -14
  61. package/dist/seats.d.ts +2 -3
  62. package/dist/seats.js +7 -15
  63. package/dist/session-lifecycle.d.ts +6 -2
  64. package/dist/session-lifecycle.js +39 -11
  65. package/dist/sqlite-square.d.ts +36 -22
  66. package/dist/sqlite-square.js +138 -161
  67. package/dist/square-api.d.ts +15 -10
  68. package/dist/square-api.js +26 -22
  69. package/dist/square-core.d.ts +0 -5
  70. package/dist/square-core.js +0 -9
  71. package/dist/square-domain.d.ts +4 -2
  72. package/dist/square-domain.js +56 -21
  73. package/dist/square-facade.d.ts +45 -87
  74. package/dist/square-index.d.ts +5 -13
  75. package/dist/square-index.js +13 -38
  76. package/dist/square-location.d.ts +1 -14
  77. package/dist/square-location.js +4 -70
  78. package/dist/square-migration-host-ledger.d.ts +2 -2
  79. package/dist/square-migration-host-ledger.js +6 -5
  80. package/dist/square-migration.d.ts +2 -1
  81. package/dist/square-migration.js +52 -30
  82. package/dist/square-sqlite.d.ts +6 -2
  83. package/dist/square-sqlite.js +54 -28
  84. package/dist/square-tools.d.ts +7 -10
  85. package/dist/square-tools.js +22 -54
  86. package/dist/stream.js +4 -5
  87. package/dist/views.d.ts +21 -10
  88. package/dist/views.js +38 -38
  89. package/dist/wakes.d.ts +1 -2
  90. package/dist/wakes.js +1 -1
  91. package/guides/application-facade-delivery-evidence.md +1 -1
  92. package/guides/claude-native.md +18 -9
  93. package/guides/codex-delivery.md +17 -6
  94. package/guides/mcp-connection-lifetime.md +1 -1
  95. package/guides/mcp.md +3 -3
  96. package/guides/migration.md +142 -151
  97. package/guides/operation-cancellation.md +1 -1
  98. package/guides/participant-ownership.md +78 -47
  99. package/guides/pi-tools.md +33 -4
  100. package/guides/primitive-history-consolidation.md +1 -1
  101. package/guides/row-storage-design.md +8 -8
  102. package/guides/square-api.md +244 -88
  103. package/package.json +1 -1
  104. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  105. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  106. package/plugins/opencode/delivery.ts +27 -17
  107. package/plugins/paseo/client/palette.ts +18 -18
  108. package/plugins/paseo/client/square-message.tsx +17 -23
  109. package/plugins/pi/square-pi.js +17 -17
  110. package/dist/square-backend.d.ts +0 -13
  111. package/dist/square-backend.js +0 -1
  112. package/dist/square-stdio.d.ts +0 -22
  113. package/dist/square-stdio.js +0 -235
  114. package/dist/stdio-server.d.ts +0 -3
  115. package/dist/stdio-server.js +0 -212
  116. package/guides/backends.md +0 -193
@@ -1,26 +1,30 @@
1
1
  import { SqliteSquare } from './sqlite-square.js';
2
- import { squareLocation, backendConfiguration, parseSquareLocation } from './square-location.js';
3
- import { openStdioSquare } from './square-stdio.js';
2
+ import { SquareError } from './model.js';
3
+ import { requireOwner } from './seats.js';
4
+ import { createDefaultWakeTransport } from './notifications.js';
5
+ export const Square = { at: async (input, control) => SqliteSquare.at({ ...input, wakeTransport: await resolveWakeTransport(input) }, control),
6
+ build: async (input, control) => SqliteSquare.build({ ...input, wakeTransport: await resolveWakeTransport(input) }, control), inMemory: SqliteSquare.inMemory };
7
+ const resolveWakeTransport = (input) => input.wakeTransport !== undefined
8
+ ? Promise.resolve(input.wakeTransport)
9
+ : createDefaultWakeTransport(input.env ?? process.env).catch(() => undefined);
10
+ export { requireOwner, requireJoinName } from './seats.js';
4
11
  export { squareLocation } from './square-location.js';
12
+ export async function selectOwnSeat(square, owner, name, options) {
13
+ if (owner === undefined && options?.optional && name === undefined)
14
+ return undefined;
15
+ const caller = requireOwner(owner);
16
+ if (name !== undefined)
17
+ return square.participant(name, { owner: caller });
18
+ const seats = (await square.seatsOf(caller)).filter(seat => seat.attachment === 'attached');
19
+ if (seats.length !== 1) {
20
+ if (options?.optional)
21
+ return undefined;
22
+ throw new SquareError('invalid_args', seats.length > 1
23
+ ? 'Square needs an explicit participant name: this owner holds multiple names'
24
+ : 'Square needs one unambiguous participant name');
25
+ }
26
+ return square.participant(seats[0].name, { owner: caller });
27
+ }
5
28
  export { ACTIVITY_METADATA_MAX_BYTES } from './model.js';
29
+ export { localUserOwner, harnessOwner, decodeHarnessOwner } from './owner.js';
6
30
  export { squareClock, createSquareResourceOwner, formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE, CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT, HELD_WAIT_BUDGET_MS, HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT, diagnoseSquare, migrateSquare, discover, inbox } from './sqlite-square.js';
7
- export const Square = {
8
- async at(input, control) {
9
- const captured = { ...input, cwd: input.cwd ?? process.cwd(), env: { ...(input.env ?? process.env) } };
10
- const location = squareLocation(captured);
11
- const selected = parseSquareLocation(location);
12
- return selected.kind === 'local' ? SqliteSquare.at({ ...captured, path: location }, control)
13
- : openStdioSquare(location, selected, captured, await backendConfiguration(captured), undefined, control);
14
- },
15
- async build(input, control) {
16
- const captured = { ...input, cwd: input.cwd ?? process.cwd(), env: { ...(input.env ?? process.env) } };
17
- const location = squareLocation(captured);
18
- const selected = parseSquareLocation(location);
19
- return selected.kind === 'local' ? SqliteSquare.build({ ...captured, path: location }, control)
20
- : openStdioSquare(location, selected, captured, await backendConfiguration(captured), {
21
- markdown: input.markdown, hardCap: input.hardCap, throttlePerMinute: input.throttlePerMinute,
22
- template: input.template, force: input.force,
23
- }, control);
24
- },
25
- inMemory(input) { return SqliteSquare.inMemory(input); },
26
- };
@@ -59,9 +59,4 @@ export type Act = {
59
59
  target: Participant;
60
60
  at?: number;
61
61
  };
62
- export declare function audienceOf(say: {
63
- body?: string;
64
- mentions?: readonly Participant[];
65
- reach?: Reach;
66
- }): Audience;
67
62
  export declare function audienceIncludes(audience: Audience, name: string): boolean;
@@ -13,15 +13,6 @@ export function parseActivityId(value) {
13
13
  const index = Number(value.slice(4));
14
14
  return Number.isSafeInteger(index) ? index : undefined;
15
15
  }
16
- export function audienceOf(say) {
17
- if (say.reach === 'bell')
18
- return { kind: 'bell' };
19
- const names = [];
20
- for (const name of say.mentions ?? [])
21
- if (!names.some((existing) => sameName(existing, name)))
22
- names.push(name);
23
- return { kind: 'mentions', names };
24
- }
25
16
  export function audienceIncludes(audience, name) {
26
17
  return audience.kind === 'bell' || audience.names.some((mentioned) => sameName(mentioned, name));
27
18
  }
@@ -1,5 +1,5 @@
1
1
  import { type Seat, type StoredAct } from './model.js';
2
- import type { ArtifactWriteAuthority, AttentionFact, LandedRecipient, ParticipantFact, SquareRead, SquareWrite } from './ports.js';
2
+ import type { ArtifactWriteAuthority, AttentionFact, LandedRecipient, ParticipantFact, SeatRef, SquareRead, SquareWrite } from './ports.js';
3
3
  export interface LandingEffect {
4
4
  readonly activity: StoredAct;
5
5
  readonly participant?: ParticipantFact;
@@ -21,7 +21,7 @@ export interface LandingFacts {
21
21
  /** The single landing transition, shared by both adapters and explicit rebuild. */
22
22
  export declare function deriveLanding(activity: StoredAct, facts: LandingFacts): LandingEffect;
23
23
  export declare function landingFacts(read: SquareRead, activity: StoredAct): LandingFacts;
24
- export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'cancelThrough' | 'putDeliveryDeferral' | 'renewDeliveryDeferral' | 'releaseDeliveryDeferral' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
24
+ export interface LandingWriter extends Pick<SquareWrite, 'markSeen' | 'putLease' | 'removeLease' | 'updateWakeAddress' | 'reattachSeat' | 'detachSeat' | 'cancelThrough' | 'putDeliveryDeferral' | 'renewDeliveryDeferral' | 'releaseDeliveryDeferral' | 'claimDelivery' | 'prepareDelivery' | 'completeDelivery' | 'observeNativeDelivery'> {
25
25
  /** Artifact-private seat persistence used only by this gate's lifecycle transition. */
26
26
  storeSeat(seat: Seat): boolean;
27
27
  removeSeat(seat: import('./ports.js').SeatRef): boolean;
@@ -31,5 +31,7 @@ export interface SquareWriteGate {
31
31
  readonly facts: SquareWrite;
32
32
  complete(): void;
33
33
  }
34
+ /** Read probes and writes share the same captured-coordinate and attachment fence. */
35
+ export declare function assertCurrentSeat(read: SquareRead, expected: SeatRef, allowDetached?: boolean): void;
34
36
  /** The artifact transaction supplies one captured authority; every mutation checks it here. */
35
37
  export declare function wrapSquareWrite(read: SquareRead, writes: LandingWriter, authority: ArtifactWriteAuthority): SquareWriteGate;
@@ -1,4 +1,4 @@
1
- import { SquareError, formatActivityId, parseActivityId, sameBody, sameName, validateBody, validateName } from './model.js';
1
+ import { SquareError, formatActivityId, parseActivityId, sameName, validateOwner, validateName } from './model.js';
2
2
  /** The single landing transition, shared by both adapters and explicit rebuild. */
3
3
  export function deriveLanding(activity, facts) {
4
4
  const audience = [];
@@ -68,31 +68,38 @@ function validateGeneration(value) {
68
68
  if (typeof value !== 'string' || parseActivityId(value) === undefined)
69
69
  throw new SquareError('invalid_args', 'Artifact authority requires a valid join generation.');
70
70
  }
71
+ /** Read probes and writes share the same captured-coordinate and attachment fence. */
72
+ export function assertCurrentSeat(read, expected, allowDetached = false) {
73
+ const current = read.seat(expected.participant);
74
+ if (current === undefined || current.owner !== expected.owner || current.generation !== expected.generation)
75
+ throw new SquareError('already_joined', 'This participant seat has been replaced.');
76
+ if (!allowDetached && current.attachment !== 'attached')
77
+ throw new SquareError('already_joined', 'This participant is away; join again before writing.', { reason: 'needs_seat', activityUnsent: true });
78
+ }
71
79
  /** The artifact transaction supplies one captured authority; every mutation checks it here. */
72
80
  export function wrapSquareWrite(read, writes, authority) {
73
81
  if (authority === undefined || typeof authority !== 'object' || authority === null)
74
82
  throw new SquareError('invalid_args', 'Artifact writes require an explicit authority.');
75
83
  const { applyLanding, ...mutations } = writes;
76
- const expectedName = authority.kind === 'participant' ? authority.seat?.participant : authority.participant;
77
- if (!['participant', 'join', 'takeover'].includes(authority.kind) || typeof expectedName !== 'string')
84
+ const seated = authority.kind === 'participant' || authority.kind === 'host-cleanup';
85
+ const expectedName = seated ? authority.seat?.participant : authority.participant;
86
+ if (!['participant', 'host-cleanup', 'join', 'takeover'].includes(authority.kind) || typeof expectedName !== 'string')
78
87
  throw new SquareError('invalid_args', 'Artifact write authority is malformed.');
79
88
  validateName(expectedName);
80
- if (authority.kind === 'participant') {
81
- validateBody(authority.seat.body);
89
+ if (seated) {
90
+ validateOwner(authority.seat.owner);
82
91
  validateGeneration(authority.seat.generation);
83
92
  }
84
93
  else {
85
- validateBody(authority.body);
94
+ validateOwner(authority.owner);
86
95
  if (authority.kind === 'takeover')
87
96
  validateGeneration(authority.expectedGeneration);
88
97
  }
89
- const assertSeat = (seat, expected = authority.kind === 'participant' ? authority.seat : undefined) => {
98
+ const assertSeat = (seat, expected = authority.kind === 'participant' ? authority.seat : undefined, allowDetached = false) => {
90
99
  if (expected === undefined || !sameName(expected.participant, seat.participant)
91
- || seat.generation !== expected.generation || !sameBody(seat.body, expected.body))
100
+ || seat.generation !== expected.generation || seat.owner !== expected.owner)
92
101
  throw new SquareError('already_joined', 'This write needs the current participant seat.');
93
- const current = read.seat(seat.participant);
94
- if (current === undefined || !sameBody(current.body, expected.body) || current.generation !== expected.generation)
95
- throw new SquareError('already_joined', 'This participant seat has been replaced.');
102
+ assertCurrentSeat(read, expected, allowDetached);
96
103
  };
97
104
  const assertParticipant = (name, generation) => {
98
105
  if (authority.kind !== 'participant')
@@ -104,11 +111,14 @@ export function wrapSquareWrite(read, writes, authority) {
104
111
  if (authority.kind === 'participant') {
105
112
  assertSeat(authority.seat);
106
113
  }
114
+ else if (authority.kind === 'host-cleanup') {
115
+ // A late host cleanup is allowed to inspect a stale coordinate and return a no-op.
116
+ }
107
117
  else if (authority.kind === 'join') {
108
118
  const participant = read.participant(expectedName), seat = read.seat(expectedName);
109
119
  if (participant?.joined) {
110
- if (seat === undefined || !sameBody(seat.body, authority.body))
111
- throw new SquareError('already_joined', `${expectedName} is held by another body; take it over explicitly`);
120
+ if (seat === undefined || seat.owner !== authority.owner)
121
+ throw new SquareError('already_joined', `${expectedName} is held by another owner; take it over explicitly`);
112
122
  reconnectSeat = seat;
113
123
  }
114
124
  }
@@ -131,6 +141,11 @@ export function wrapSquareWrite(read, writes, authority) {
131
141
  if (activity.kind === 'join')
132
142
  throw new SquareError('invalid_args', 'A participant seat can only be re-established by join.');
133
143
  }
144
+ else if (authority.kind === 'host-cleanup') {
145
+ assertSeat(authority.seat, authority.seat, true);
146
+ if (activity.kind !== 'done')
147
+ throw new SquareError('invalid_args', 'Host cleanup may only end membership.');
148
+ }
134
149
  else if (authority.kind === 'join') {
135
150
  if (activity.kind !== 'join')
136
151
  throw new SquareError('invalid_args', 'A new seat must be established by its join activity.');
@@ -157,27 +172,28 @@ export function wrapSquareWrite(read, writes, authority) {
157
172
  const stored = { ...activity, index };
158
173
  applyLanding(deriveLanding(stored, landingFacts(read, stored)));
159
174
  if (stored.kind === 'join') {
160
- if (authority.kind === 'participant')
161
- throw new SquareError('invalid_args', 'Participant authority cannot establish a seat.');
175
+ if (seated)
176
+ throw new SquareError('invalid_args', 'Seat authority cannot establish another seat.');
162
177
  const seat = {
163
178
  participant: stored.actor,
164
- body: authority.body,
179
+ owner: authority.owner,
165
180
  generation: formatActivityId(stored.index),
181
+ attachment: 'attached',
166
182
  ...(authority.wakeAddress === undefined ? {} : { wakeAddress: authority.wakeAddress }),
167
183
  };
168
184
  mutations.storeSeat(seat);
169
185
  const established = read.seat(seat.participant);
170
- if (established === undefined || !sameBody(established.body, seat.body) || established.generation !== seat.generation)
186
+ if (established === undefined || established.owner !== seat.owner || established.generation !== seat.generation)
171
187
  throw new SquareError('invalid_args', 'The join activity did not establish its artifact seat.');
172
188
  if (authority.kind === 'takeover')
173
189
  takeoverPhase = 'joined';
174
190
  }
175
- else if (stored.kind === 'done' && authority.kind === 'participant') {
191
+ else if (stored.kind === 'done' && seated) {
176
192
  if (!mutations.removeSeat(authority.seat))
177
193
  throw new SquareError('already_joined', 'The participant seat changed while it was being completed.');
178
194
  }
179
195
  else if (stored.kind === 'done' && authority.kind === 'takeover') {
180
- if (captured === undefined || !mutations.removeSeat({ participant: expectedName, body: captured.body, generation: captured.generation }))
196
+ if (captured === undefined || !mutations.removeSeat({ participant: expectedName, owner: captured.owner, generation: captured.generation }))
181
197
  throw new SquareError('already_joined', 'The participant seat changed while takeover was completing it.');
182
198
  takeoverPhase = 'done';
183
199
  }
@@ -185,9 +201,28 @@ export function wrapSquareWrite(read, writes, authority) {
185
201
  return lastActivity;
186
202
  },
187
203
  updateWakeAddress(seat, wakeAddress) {
188
- assertSeat(seat, authority.kind === 'join' ? reconnectSeat : undefined);
204
+ if (authority.kind !== 'join' && authority.kind !== 'participant')
205
+ throw new SquareError('already_joined', 'This write needs the current participant seat.');
206
+ assertSeat(seat, authority.kind === 'join' ? reconnectSeat : authority.seat);
189
207
  return mutations.updateWakeAddress(seat, wakeAddress);
190
208
  },
209
+ reattachSeat(seat, wakeAddress) {
210
+ if (authority.kind !== 'join')
211
+ throw new SquareError('already_joined', 'Reattachment requires a same-owner join.');
212
+ assertSeat(seat, reconnectSeat, true);
213
+ return mutations.reattachSeat(seat, wakeAddress);
214
+ },
215
+ detachSeat(seat) {
216
+ if (authority.kind !== 'host-cleanup')
217
+ throw new SquareError('already_joined', 'Detach requires captured host cleanup authority.');
218
+ const current = read.seat(seat.participant), expected = authority.seat;
219
+ if (!sameName(seat.participant, expected.participant) || seat.owner !== expected.owner || seat.generation !== expected.generation)
220
+ throw new SquareError('already_joined', 'Host cleanup does not match its captured seat.');
221
+ if (current === undefined || current.attachment !== 'attached' || current.owner !== expected.owner
222
+ || current.generation !== expected.generation)
223
+ return false;
224
+ return mutations.detachSeat(seat);
225
+ },
191
226
  cancelThrough(seat, through) {
192
227
  assertSeat(seat);
193
228
  if (!Number.isSafeInteger(through) || through < 0 || through > read.header().lastActIndex || read.activity(through) === undefined)
@@ -242,7 +277,7 @@ export function wrapSquareWrite(read, writes, authority) {
242
277
  return;
243
278
  const seat = read.seat(expectedName);
244
279
  if (takeoverPhase !== 'joined' || lastActivity?.kind !== 'join' || seat === undefined
245
- || !sameBody(seat.body, authority.body) || seat.generation !== formatActivityId(lastActivity.index))
280
+ || seat.owner !== authority.owner || seat.generation !== formatActivityId(lastActivity.index))
246
281
  throw new SquareError('already_joined', 'Takeover did not complete its replacement join.');
247
282
  },
248
283
  };
@@ -1,11 +1,10 @@
1
- import type { SquareBackends } from './square-backend.js';
2
1
  import type { DeliveryDeferral, MetadataMatch } from './delivery-deferral.js';
3
2
  export type { DeliveryDeferral, MetadataMatch, MetadataScalar } from './delivery-deferral.js';
4
3
  import type { ActivityId, Perception, Reach } from './square-core.js';
5
- import type { Body, StoredAct, WakeAddress, ActivityMetadata } from './model.js';
6
- import type { WakeTransportPort, DeliveryResult, SquareResourceOwner } from './ports.js';
4
+ import type { Owner, StoredAct, WakeAddress, ActivityMetadata, DirectedNotificationRoute } from './model.js';
5
+ import type { WakeTransportPort, SquareResourceOwner } from './ports.js';
7
6
  export type { ActivityId, Perception, Reach } from './square-core.js';
8
- export type { Body, WakeAddress, ActivityMetadata, JsonValue } from './model.js';
7
+ export type { Owner, WakeAddress, ActivityMetadata, JsonValue, DirectedNotificationRoute } from './model.js';
9
8
  /** Indexed archive event, including read and historical actorless lifecycle records. */
10
9
  export type ArchiveRecord = Readonly<StoredAct>;
11
10
  export interface Activity {
@@ -22,6 +21,7 @@ export interface Activity {
22
21
  }
23
22
  export interface PerceivedActivity extends Activity {
24
23
  readonly perception: Perception;
24
+ readonly via?: DirectedNotificationRoute;
25
25
  }
26
26
  /** Request extra facts for rendering, without changing operation semantics. */
27
27
  export interface ObservationOptions {
@@ -39,12 +39,8 @@ export interface ExpressOptions extends ObservationOptions {
39
39
  export interface ExpressInput extends ExpressOptions {
40
40
  readonly body?: string;
41
41
  }
42
- export interface ActivityResult {
42
+ export interface ExpressResult {
43
43
  readonly activity: Activity;
44
- readonly delivery?: DeliveryResult;
45
- }
46
- export interface ExpressResult extends ActivityResult {
47
- readonly waited: boolean;
48
44
  /** The replied-to author was not standing when the reply landed. */
49
45
  readonly replyAuthorNotHere?: string;
50
46
  /** Body @name tokens with no standing match at landing; never stored on the activity. */
@@ -61,8 +57,9 @@ export interface ListeningResult {
61
57
  readonly listening: readonly string[];
62
58
  readonly observation?: SquareObservation;
63
59
  }
64
- export interface LifecycleResult extends ActivityResult {
65
- readonly observation?: LifecycleObservation;
60
+ export interface LifecycleResult {
61
+ readonly activity: Activity;
62
+ readonly observation?: SquareObservation;
66
63
  }
67
64
  export interface CatchOptions extends ObservationOptions {
68
65
  readonly id?: ActivityId;
@@ -72,12 +69,11 @@ export interface CatchOptions extends ObservationOptions {
72
69
  readonly limit?: number;
73
70
  readonly replace?: boolean;
74
71
  }
72
+ export type CatchReason = 'drained' | 'idle' | 'capped' | 'quorum' | 'replaced' | 'already-active';
75
73
  export interface CatchResult {
76
74
  readonly activities: readonly PerceivedActivity[];
77
- readonly consumedThrough: ActivityId | null;
78
- readonly idleExpired: boolean;
79
75
  readonly remaining: number;
80
- readonly status?: CatchTerminalStatus;
76
+ readonly reason: CatchReason;
81
77
  /** Occasional participant-facing nudge; not part of any activity. */
82
78
  readonly hint?: string;
83
79
  readonly observation?: CatchObservation;
@@ -134,13 +130,6 @@ export interface HistoryResult {
134
130
  readonly activities: readonly Activity[];
135
131
  readonly observation?: HistoryObservation;
136
132
  }
137
- export interface ParticipantStatus {
138
- readonly name: string;
139
- readonly state: 'joined' | 'done';
140
- readonly consumedThrough: ActivityId | null;
141
- readonly watching: boolean;
142
- readonly listening: readonly string[];
143
- }
144
133
  export interface SquareSnapshot {
145
134
  readonly context: string;
146
135
  readonly actCount: number;
@@ -150,16 +139,12 @@ export interface SquareSnapshot {
150
139
  readonly by: string;
151
140
  readonly reason?: string;
152
141
  } | null;
153
- readonly participants: readonly ParticipantStatus[];
142
+ readonly participants: readonly Pick<ParticipantSummary, 'name' | 'presence' | 'listening'>[];
154
143
  delivered(name: string, id: ActivityId): boolean;
155
144
  }
156
145
  export interface OpenOptions {
157
146
  readonly clock?: () => number;
158
- readonly wakeTransport?: WakeTransportPort;
159
- /** Explicit execution coordinate resolved by an adapter. */
160
- readonly body?: Body;
161
- /** Optional wake address attached to this body. */
162
- readonly wakeAddress?: WakeAddress;
147
+ readonly wakeTransport?: WakeTransportPort | false;
163
148
  }
164
149
  export interface SquareLocation {
165
150
  readonly path?: string;
@@ -167,8 +152,6 @@ export interface SquareLocation {
167
152
  readonly env?: NodeJS.ProcessEnv;
168
153
  }
169
154
  export interface SquareAtInput extends SquareLocation, OpenOptions {
170
- readonly backends?: SquareBackends;
171
- readonly participant?: string;
172
155
  /** A caller-owned connection; closing this Square never closes its owner. */
173
156
  readonly resources?: SquareResourceOwner;
174
157
  }
@@ -180,6 +163,8 @@ export interface SquareBuildInput extends SquareAtInput {
180
163
  readonly force?: boolean;
181
164
  }
182
165
  export interface JoinOptions extends ObservationOptions {
166
+ readonly owner: Owner;
167
+ readonly wakeAddress?: WakeAddress;
183
168
  readonly takeover?: boolean;
184
169
  readonly last?: number;
185
170
  }
@@ -187,8 +172,6 @@ export interface JoinResult {
187
172
  readonly participant: Participant;
188
173
  readonly activity: Activity | null;
189
174
  readonly kind: 'joined' | 'reconnected' | 'taken-over';
190
- /** Known joined-or-done participants, not the current standing count. */
191
- readonly participantCount: number;
192
175
  readonly observation?: JoinObservation;
193
176
  }
194
177
  export interface Participant {
@@ -200,7 +183,6 @@ export interface Participant {
200
183
  unlisten(target: string, options?: ObservationOptions, control?: OperationControl): Promise<ListenerChangeResult>;
201
184
  listening(options?: ObservationOptions, control?: OperationControl): Promise<ListeningResult>;
202
185
  catch(options?: CatchOptions, control?: OperationControl): Promise<CatchResult>;
203
- history(query?: HistoryQuery, control?: OperationControl): Promise<HistoryResult>;
204
186
  hold(reason?: string, options?: ObservationOptions, control?: OperationControl): Promise<LifecycleResult>;
205
187
  resume(options?: ObservationOptions, control?: OperationControl): Promise<LifecycleResult>;
206
188
  /** Leave without a message. Only an omitted or empty body is accepted. */
@@ -212,12 +194,15 @@ export interface Participant {
212
194
  renewDeliveryDeferral(id: string, durationMs: number, control?: OperationControl): Promise<DeliveryDeferral | null>;
213
195
  releaseDeliveryDeferral(id: string, control?: OperationControl): Promise<boolean>;
214
196
  }
215
- export type CatchTerminalStatus = 'already-active' | 'replaced' | 'capped' | 'quorum';
216
- export type CatchStopStatus = CatchTerminalStatus | 'empty-now' | 'stale';
217
- export type ParticipantPresence = 'never-joined' | 'active' | 'watching' | 'done';
197
+ export interface HeldSeat {
198
+ readonly name: string;
199
+ readonly generation: ActivityId;
200
+ readonly attachment: 'attached' | 'detached';
201
+ }
202
+ export type ParticipantPresence = 'never-joined' | 'active' | 'watching' | 'away' | 'done';
218
203
  export interface ParticipantSummary {
219
204
  readonly name: string;
220
- readonly state: 'active' | 'done' | 'not joined';
205
+ readonly deliveryAvailable: boolean;
221
206
  readonly listening: readonly string[];
222
207
  readonly activityCount: number;
223
208
  readonly lastActiveAt?: number;
@@ -232,12 +217,11 @@ export interface StatusResult {
232
217
  /** Present when a viewer resolves; an empty list means they listen to nobody. */
233
218
  readonly listening?: readonly string[];
234
219
  readonly hardCap: number | null;
235
- readonly activeCount: number;
236
- readonly doneCount: number;
237
220
  readonly participants: readonly ParticipantSummary[];
238
221
  readonly now: number;
239
222
  readonly throttlePerMinute?: number;
240
- readonly hold: {
223
+ /** Non-null whenever held; by is present with an actor and omitted only for historical actorless holds. */
224
+ readonly held: {
241
225
  readonly by?: string;
242
226
  readonly reason?: string;
243
227
  readonly at?: number;
@@ -284,10 +268,8 @@ export interface RecoveryResult {
284
268
  readonly participant?: string;
285
269
  readonly standingCount?: number;
286
270
  readonly held?: boolean;
287
- readonly holdReason?: string;
288
271
  readonly ownActivityCount?: number;
289
272
  readonly hardCap?: number | null;
290
- readonly roster?: readonly string[];
291
273
  readonly activitySummaries?: readonly UnreadActivitySummary[];
292
274
  }
293
275
  export type StreamStart = {
@@ -295,69 +277,45 @@ export type StreamStart = {
295
277
  readonly last: number;
296
278
  } | {
297
279
  readonly kind: 'after';
298
- readonly after: number;
280
+ readonly after: ActivityId;
299
281
  };
300
282
  export interface StreamOptions {
301
283
  readonly recipient?: string;
302
284
  readonly start?: StreamStart;
303
285
  }
304
- export type StreamRecord = ArchiveRecord & {
305
- readonly seq: number;
306
- readonly square: string;
307
- readonly route?: string;
308
- };
309
- export interface UnreadActivityPreview {
310
- readonly number: number;
311
- readonly act: Extract<ArchiveRecord, {
312
- kind: 'say';
313
- }>;
314
- readonly perception: Perception;
315
- }
286
+ export type StreamRecord = Activity | PerceivedActivity;
316
287
  export interface UnreadActivitySummary {
317
288
  readonly name: string;
318
289
  readonly count: number;
319
290
  readonly latestActivityAgeMs: number;
320
- readonly previews: readonly UnreadActivityPreview[];
291
+ readonly preview: Extract<ArchiveRecord, {
292
+ kind: 'say';
293
+ }>;
321
294
  }
322
295
  export interface EntryPresentation {
323
296
  readonly joinedBefore: boolean;
324
297
  readonly scene: string;
325
298
  readonly joinContext: string;
326
- readonly recentActivities: readonly ArchiveRecord[];
327
- readonly perceptions: Readonly<Record<number, Perception>>;
328
- readonly sayNumbers: Readonly<Record<number, number>>;
329
- }
330
- export interface ListPresentation {
331
- readonly context: readonly string[];
332
- readonly participants: readonly string[];
333
- readonly activities: number;
299
+ readonly recentActivities: readonly PerceivedActivity[];
334
300
  }
335
301
  export interface PresenceClassification {
336
302
  readonly orderedHereNames: readonly string[];
337
303
  readonly lingeringCount: number;
338
- readonly classificationByName: Readonly<Record<string, 'here' | 'lingering' | 'done' | 'not-joined'>>;
304
+ readonly classificationByName: Readonly<Record<string, 'here' | 'lingering' | 'away' | 'done' | 'not-joined'>>;
339
305
  }
340
306
  export interface CatchPresence {
341
307
  readonly participants: readonly ParticipantSummary[];
342
308
  readonly now: number;
343
309
  }
344
- export interface StreamProjection {
345
- readonly activities: readonly {
346
- readonly activity: ArchiveRecord;
347
- readonly route?: string;
348
- }[];
349
- readonly cursor: number;
350
- readonly hasMore: boolean;
351
- }
352
310
  export interface SquareObservation {
353
311
  readonly location: string;
354
312
  readonly standingCount: number;
355
313
  readonly held: boolean;
356
314
  readonly now: number;
357
315
  readonly participant?: string;
358
- readonly automaticDeliveryAvailable: boolean;
359
316
  }
360
317
  export interface JoinObservation extends SquareObservation {
318
+ readonly deliveryAvailable: boolean;
361
319
  readonly entry?: EntryPresentation;
362
320
  /** The receipt succeeded, but optional recent-history/context enrichment was unavailable. */
363
321
  readonly enrichmentUnavailable?: true;
@@ -366,19 +324,24 @@ export interface ExpressionObservation extends Pick<SquareObservation, 'standing
366
324
  readonly ownActivityCount: number;
367
325
  readonly hardCap: number | null;
368
326
  }
327
+ export interface ExpressContext {
328
+ readonly activities: readonly {
329
+ readonly id: ActivityId;
330
+ readonly actor: string;
331
+ readonly preview: string;
332
+ readonly truncated: boolean;
333
+ }[];
334
+ /** Unread activities outside this preview, not a consumption cursor. */
335
+ readonly remaining: number;
336
+ }
369
337
  export interface ExpressObservation extends SquareObservation, ExpressionObservation {
370
338
  readonly participant: string;
371
- readonly pendingPublic: readonly ArchiveRecord[];
372
- readonly sayNumbers: Readonly<Record<number, number>>;
373
- readonly perceptions: Readonly<Record<number, Perception>>;
339
+ readonly context: ExpressContext;
374
340
  }
375
341
  export interface CatchObservation extends SquareObservation, ExpressionObservation {
342
+ readonly deliveryAvailable: boolean;
376
343
  readonly participant: string;
377
- readonly sayNumbers: Readonly<Record<number, number>>;
378
344
  readonly presence?: CatchPresence;
379
- readonly stop?: CatchStopStatus;
380
- readonly waitStarted?: boolean;
381
- readonly replaced?: boolean;
382
345
  readonly enrichmentUnavailable?: true;
383
346
  }
384
347
  export interface HistoryContinuation {
@@ -397,17 +360,12 @@ export interface HistoryObservation extends SquareObservation {
397
360
  export interface ListenerObservation extends SquareObservation {
398
361
  readonly participant: string;
399
362
  readonly target: string;
400
- readonly requestedTarget: string;
401
- }
402
- export interface LifecycleObservation extends SquareObservation {
403
- readonly activity: ArchiveRecord;
404
363
  }
405
- export interface StatusObservation extends SquareObservation, PresenceClassification {
364
+ export interface StatusObservation extends Omit<SquareObservation, 'now' | 'held'>, PresenceClassification {
406
365
  readonly knownParticipantCount: number;
407
- readonly latestActNumber?: number;
408
366
  readonly latestPerception?: Perception;
409
367
  readonly joins: readonly {
410
- readonly activity: ArchiveRecord;
368
+ readonly activity: Activity;
411
369
  readonly rejoined: boolean;
412
370
  }[];
413
371
  readonly joinCount: number;
@@ -1,24 +1,16 @@
1
1
  import { type VersionObserver } from './file-changes.js';
2
- import { type Body } from './model.js';
3
- /** A rebuildable discovery hint: the body that may have used an artifact and its path.
4
- * It is deliberately not a participant, generation, ownership, suppression, or evidence row. */
2
+ import { type Owner } from './model.js';
3
+ /** A rebuildable discovery hint: the owner that may have used an artifact and its path. */
5
4
  export interface SquareIndexEntry {
6
5
  readonly location: string;
7
- readonly body: Body;
6
+ readonly owner: Owner;
8
7
  }
9
8
  export interface SquareIndexQuery {
10
- readonly body?: Body;
11
- readonly channel?: string;
12
- readonly session?: string;
13
- readonly agent?: string;
9
+ readonly owner?: Owner;
14
10
  }
15
- /** The index is the only host-side Square state. Its path is test/configuration plumbing, not authority. */
16
11
  export declare function squareIndexPath(env?: NodeJS.ProcessEnv): string;
17
- /** Read and verify the disposable index. Paths are canonicalized on access and duplicates collapse. */
18
12
  export declare function indexedSquareEntries(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly SquareIndexEntry[]>;
19
13
  export declare function indexedSquarePaths(query?: SquareIndexQuery, env?: NodeJS.ProcessEnv): Promise<readonly string[]>;
20
14
  /** Best-effort discovery only: a busy index skips publication, never queues or delays artifact work. */
21
- export declare function rememberSquarePath(location: string, body: Body, index: string): Promise<boolean>;
22
- /** Index loss is tolerated by every caller; this observer is only a wake hint for rediscovery. */
15
+ export declare function rememberSquarePath(location: string, owner: Owner, index: string): Promise<boolean>;
23
16
  export declare function observeSquareIndex(env?: NodeJS.ProcessEnv): Promise<VersionObserver<string>>;
24
- export declare function bodyKey(body: Body): string;