@astrosheep/square 0.3.105 → 0.4.0

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 (143) 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 +8 -12
  5. package/dist/artifact.js +66 -343
  6. package/dist/attention-presentation.d.ts +33 -7
  7. package/dist/attention-presentation.js +33 -69
  8. package/dist/boundary-presentation.d.ts +0 -2
  9. package/dist/boundary-presentation.js +17 -22
  10. package/dist/catch-decisions.d.ts +2 -2
  11. package/dist/catch-decisions.js +14 -4
  12. package/dist/catch-selection.d.ts +8 -0
  13. package/dist/catch-selection.js +5 -0
  14. package/dist/channel-artifact.d.ts +10 -0
  15. package/dist/channel-artifact.js +55 -0
  16. package/dist/channel-delivery.d.ts +11 -0
  17. package/dist/channel-delivery.js +51 -0
  18. package/dist/channel-domain.d.ts +3 -0
  19. package/dist/channel-domain.js +83 -0
  20. package/dist/channel-facade.d.ts +76 -0
  21. package/dist/channel-facade.js +1 -0
  22. package/dist/channel-internal.d.ts +126 -0
  23. package/dist/channel-internal.js +440 -0
  24. package/dist/channel-metadata.d.ts +11 -0
  25. package/dist/channel-metadata.js +1 -0
  26. package/dist/channel-ports.d.ts +241 -0
  27. package/dist/channel-ports.js +1 -0
  28. package/dist/channel-presentation.d.ts +17 -0
  29. package/dist/channel-presentation.js +22 -0
  30. package/dist/channel-sqlite-schema.d.ts +13 -0
  31. package/dist/channel-sqlite-schema.js +81 -0
  32. package/dist/channel-transport.d.ts +62 -0
  33. package/dist/channel-transport.js +1 -0
  34. package/dist/channel-values.d.ts +27 -0
  35. package/dist/channel-values.js +90 -0
  36. package/dist/channel.d.ts +31 -21
  37. package/dist/channel.js +42 -74
  38. package/dist/claude-delivery-status.js +1 -1
  39. package/dist/claude-delivery.d.ts +2 -5
  40. package/dist/claude-delivery.js +56 -103
  41. package/dist/claude-mod.js +4 -4
  42. package/dist/claude-transport.d.ts +5 -0
  43. package/dist/claude-transport.js +58 -0
  44. package/dist/cli/context.d.ts +2 -2
  45. package/dist/cli/context.js +0 -1
  46. package/dist/cli/maintenance-commands.d.ts +0 -4
  47. package/dist/cli/maintenance-commands.js +2 -29
  48. package/dist/cli/program.js +1 -1
  49. package/dist/cli/registry.js +1 -2
  50. package/dist/codex-address.d.ts +1 -1
  51. package/dist/codex-queue.d.ts +2 -2
  52. package/dist/codex-queue.js +1 -3
  53. package/dist/decisions.js +3 -3
  54. package/dist/delivery-deferral.d.ts +9 -18
  55. package/dist/delivery-deferral.js +4 -4
  56. package/dist/delivery-health.d.ts +4 -4
  57. package/dist/delivery-health.js +11 -11
  58. package/dist/delivery-operations.d.ts +15 -2
  59. package/dist/delivery-operations.js +60 -30
  60. package/dist/delivery.d.ts +44 -33
  61. package/dist/delivery.js +49 -75
  62. package/dist/help.js +1 -6
  63. package/dist/http-delivery.d.ts +2 -2
  64. package/dist/http-delivery.js +2 -4
  65. package/dist/inbox.d.ts +10 -18
  66. package/dist/inbox.js +44 -53
  67. package/dist/index.d.ts +1 -1
  68. package/dist/index.js +1 -1
  69. package/dist/mcp.d.ts +2 -1
  70. package/dist/mcp.js +40 -70
  71. package/dist/message.d.ts +7 -0
  72. package/dist/message.js +23 -0
  73. package/dist/model.d.ts +41 -77
  74. package/dist/model.js +7 -139
  75. package/dist/neutral.d.ts +72 -0
  76. package/dist/neutral.js +27 -0
  77. package/dist/notifications.d.ts +2 -8
  78. package/dist/notifications.js +15 -50
  79. package/dist/operation-presentation.d.ts +1 -2
  80. package/dist/operation-presentation.js +1 -7
  81. package/dist/owner.d.ts +2 -2
  82. package/dist/owner.js +1 -1
  83. package/dist/paseo-delivery.d.ts +1 -1
  84. package/dist/paseo-delivery.js +2 -4
  85. package/dist/paseo-timeline.js +1 -0
  86. package/dist/plugins/opencode/delivery.js +27 -42
  87. package/dist/ports.d.ts +46 -168
  88. package/dist/presentation-operations.d.ts +1 -14
  89. package/dist/presentation-operations.js +16 -16
  90. package/dist/presentation.d.ts +5 -5
  91. package/dist/presentation.js +1 -0
  92. package/dist/runtime.js +2 -2
  93. package/dist/search.js +1 -1
  94. package/dist/sqlite-channel.d.ts +15 -0
  95. package/dist/sqlite-channel.js +600 -0
  96. package/dist/sqlite-framing.d.ts +8 -0
  97. package/dist/sqlite-framing.js +31 -0
  98. package/dist/sqlite-square.d.ts +12 -13
  99. package/dist/sqlite-square.js +192 -248
  100. package/dist/sqlite-values.d.ts +21 -0
  101. package/dist/sqlite-values.js +78 -0
  102. package/dist/square-api.d.ts +1 -1
  103. package/dist/square-api.js +1 -1
  104. package/dist/square-core.d.ts +1 -1
  105. package/dist/square-domain.d.ts +21 -7
  106. package/dist/square-domain.js +93 -94
  107. package/dist/square-facade.d.ts +12 -66
  108. package/dist/square-index.d.ts +1 -1
  109. package/dist/square-index.js +1 -1
  110. package/dist/square-location.d.ts +6 -1
  111. package/dist/square-location.js +1 -1
  112. package/dist/square-receive.d.ts +2 -3
  113. package/dist/square-sqlite.d.ts +45 -44
  114. package/dist/square-sqlite.js +308 -764
  115. package/dist/square-tools.d.ts +8 -0
  116. package/dist/square-tools.js +39 -0
  117. package/dist/views.d.ts +6 -6
  118. package/dist/views.js +15 -9
  119. package/dist/wake-preview.d.ts +7 -0
  120. package/dist/wake-preview.js +15 -0
  121. package/dist/wake-sink.d.ts +2 -2
  122. package/dist/wake-sink.js +1 -1
  123. package/dist/wake-transports.d.ts +7 -0
  124. package/dist/wake-transports.js +42 -0
  125. package/guides/claude-native.md +13 -10
  126. package/guides/codex-delivery.md +3 -1
  127. package/guides/participant-ownership.md +1 -1
  128. package/guides/row-runtime-delivery-evidence.md +2 -0
  129. package/guides/row-storage-design.md +19 -44
  130. package/guides/square-api.md +137 -60
  131. package/package.json +3 -3
  132. package/plugins/claude/.claude-plugin/plugin.json +1 -1
  133. package/plugins/claude/types.d.ts +1 -1
  134. package/plugins/codex/.codex-plugin/plugin.json +1 -1
  135. package/plugins/opencode/delivery.ts +28 -41
  136. package/plugins/pi/square-pi.js +28 -51
  137. package/dist/square-legacy-framing.d.ts +0 -11
  138. package/dist/square-legacy-framing.js +0 -64
  139. package/dist/square-migration-host-ledger.d.ts +0 -26
  140. package/dist/square-migration-host-ledger.js +0 -169
  141. package/dist/square-migration.d.ts +0 -30
  142. package/dist/square-migration.js +0 -599
  143. package/guides/migration.md +0 -156
@@ -1,4 +1,3 @@
1
- import { wakePreview } from './delivery.js';
2
1
  import { DeliveryError } from './packages/agent-delivery/src/index.js';
3
2
  import { resolvePaseoDaemon } from './packages/agent-delivery/src/paseo-native.js';
4
3
  import { discoverPaseoAgents, waitForPaseoWakeBoundary } from './paseo-state.js';
@@ -44,8 +43,7 @@ export class PaseoWakeTransport {
44
43
  diagnostic: diagnostic('selection', address, 'invalid_address'),
45
44
  };
46
45
  }
47
- const payload = request.artifact === undefined ? undefined
48
- : (await request.artifact.read((facts) => wakePreview(facts, request))).value;
46
+ const payload = request.presentation?.payload;
49
47
  if (payload === undefined)
50
48
  return { outcome: 'not-capable', diagnostic: 'No pending Square activity to preview.' };
51
49
  let remaining = remainingMs();
@@ -94,7 +92,7 @@ export class PaseoWakeTransport {
94
92
  prompt: payload,
95
93
  location: request.location,
96
94
  participant: request.participant,
97
- activity: request.activity,
95
+ message: request.message,
98
96
  ...(request.attemptN === undefined ? {} : { attemptN: request.attemptN }),
99
97
  }, { timeoutMs: remaining });
100
98
  return { outcome: 'accepted' };
@@ -40,6 +40,7 @@ export async function waitForPaseoToolBoundary(agentId, opts = {}) {
40
40
  let client;
41
41
  try {
42
42
  client = await openPaseoClient(target, daemonCredential({ endpointPassword: target.password }), 3_000);
43
+ await client.connect();
43
44
  return await waitSnapshots(agentId, async (id) => {
44
45
  const payload = await client.fetchAgentTimeline(id, {
45
46
  direction: 'tail',
@@ -1,10 +1,11 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { createArtifactPort } from '../../artifact.js';
2
+ import { squareWakeProjection } from '../../wake-preview.js';
3
+ import { channelArtifact, createArtifactPort } from '../../artifact.js';
3
4
  import { deliverPending } from '../../delivery-operations.js';
4
- import { nativePendingPreview, observeSessionPending, projectSessionSeats } from '../../inbox.js';
5
- import { automaticWakeEligible, presentationBlocksWake } from '../../delivery.js';
5
+ import { nativePendingSource, observeSessionPending, projectSessionSeats } from '../../inbox.js';
6
+ import { automaticWakeEligible, nativePendingHead, presentationBlocksWake } from '../../delivery.js';
6
7
  import { harnessOwner, decodeHarnessOwner } from '../../owner.js';
7
- import { formatActivityId, parseActivityId } from '../../square-core.js';
8
+ import { formatMessageId, parseMessageId } from '../../message.js';
8
9
  import { indexedSquareEntries } from '../../square-index.js';
9
10
  import { asSeatRef } from '../../seats.js';
10
11
  import { restoreSessionSeats } from '../../session-lifecycle.js';
@@ -13,18 +14,6 @@ import { createNativeInputId, sendNativeText } from '../../packages/agent-delive
13
14
  const routeKind = 'opencode-server';
14
15
  const ownerFor = (sessionId) => harnessOwner({ channel: 'opencode', session: sessionId });
15
16
  const payloadHash = (payload) => createHash('sha256').update(payload).digest('hex');
16
- function preview(facts, location, captured, index) {
17
- const seat = facts.seat(captured.participant);
18
- if (seat === undefined || seat.attachment !== 'attached' || seat.generation !== captured.generation || seat.owner !== captured.owner)
19
- return undefined;
20
- const wake = seat.wakeAddress;
21
- if (wake?.kind !== routeKind)
22
- return undefined;
23
- return nativePendingPreview(facts, {
24
- location, routeKind, generation: seat.generation, participant: seat.participant,
25
- actIndex: index, address: wake.address, now: Date.now(),
26
- });
27
- }
28
17
  export async function indexedOpenCodeSessions(env) {
29
18
  return [...new Set((await indexedSquareEntries({}, env)).flatMap((entry) => {
30
19
  const coordinate = decodeHarnessOwner(entry.owner);
@@ -42,9 +31,8 @@ function createOpenCodeWakeTransport(target, lifetime) {
42
31
  probe: async (route) => !lifetime.aborted && route.kind === routeKind
43
32
  && route.address.sessionId === target.sessionId,
44
33
  async attempt(request, timeoutMs, beforeSend) {
45
- const index = parseActivityId(request.activity);
46
- const artifact = request.artifact;
47
- if (artifact === undefined || request.claimToken === undefined || index === undefined
34
+ const presentation = request.presentation;
35
+ if (request.claimToken === undefined || parseMessageId(request.message) === undefined || presentation === undefined
48
36
  || request.route.kind !== routeKind || request.route.address.sessionId !== target.sessionId) {
49
37
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention has no matching artifact claim or session.' };
50
38
  }
@@ -52,19 +40,14 @@ function createOpenCodeWakeTransport(target, lifetime) {
52
40
  const started = Date.now();
53
41
  if (signal.aborted || !await beforeSend())
54
42
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before send.' };
55
- const { value: prepared } = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
56
- if (prepared === undefined)
57
- return { outcome: 'not-capable', diagnostic: 'OpenCode attention is no longer current.' };
58
43
  const inputId = createNativeInputId();
59
- const correlation = { harness: 'opencode', token: inputId, payloadHash: payloadHash(prepared.payload) };
60
- if (!await artifact.prepareDelivery({ seat: request.seat, claimToken: request.claimToken, correlation }, signal))
44
+ const correlation = { harness: 'opencode', token: inputId, payloadHash: payloadHash(presentation.payload) };
45
+ if (!await request.channel.prepare({ correlation,
46
+ coverage: presentation.coverage, ...(presentation.description === undefined ? {} : { description: presentation.description }) }, signal))
61
47
  return { outcome: 'not-capable', diagnostic: 'OpenCode delivery claim is no longer current.' };
62
- const fresh = await artifact.read((facts) => preview(facts, request.location, request.seat, index), signal);
63
- if (fresh.value === undefined || payloadHash(fresh.value.payload) !== correlation.payloadHash)
64
- return { outcome: 'not-capable', diagnostic: 'OpenCode attention changed before native send.' };
65
48
  if (signal.aborted || !await beforeSend())
66
49
  return { outcome: 'not-capable', diagnostic: 'OpenCode attention was consumed before send.' };
67
- const result = await sendNativeText(target, prepared.payload, {
50
+ const result = await sendNativeText(target, presentation.payload, {
68
51
  inputId, delivery: 'steer', timeoutMs: Math.max(1, timeoutMs - (Date.now() - started)), signal,
69
52
  });
70
53
  if (result.state === 'accepted')
@@ -99,28 +82,30 @@ export async function observeOpenCodeContext(sessionId, messages, env, signal) {
99
82
  return facts.deliveryEvidence(seat.participant, seat.generation).flatMap((evidence) => {
100
83
  const token = evidence.correlation?.harness === 'opencode' ? evidence.correlation.token : undefined;
101
84
  const hash = token === undefined ? undefined : observed.get(token);
102
- const index = parseActivityId(evidence.activity);
85
+ const index = parseMessageId(evidence.message), now = Date.now();
103
86
  if (token === undefined || hash === undefined || evidence.claimToken === undefined || index === undefined
104
- || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
105
- || evidence.correlation?.payloadHash !== hash)
87
+ || evidence.route === undefined || !['dispatching', 'unknown', 'accepted'].includes(evidence.outcome)
88
+ || evidence.correlation?.payloadHash !== hash
89
+ || evidence.route.kind !== routeKind
90
+ || presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.message), now))
106
91
  return [];
107
- const prepared = preview(facts, location, seat, index);
108
- if (prepared === undefined || payloadHash(prepared.payload) !== hash
109
- || presentationBlocksWake(facts.deliveryEvidence(seat.participant, seat.generation, evidence.activity), Date.now()))
92
+ const source = nativePendingSource(facts, { location, participant: seat.participant, generation: seat.generation,
93
+ owner: seat.owner, index, route: evidence.route, now });
94
+ if (source === undefined)
110
95
  return [];
111
- return [{ evidence, index, hash, clipped: prepared.clipped }];
96
+ return [{ evidence, index, hash, clipped: evidence.coverage === 'clipped' }];
112
97
  });
113
98
  }, signal);
114
99
  for (const { evidence, index, hash, clipped } of matches) {
115
100
  if (signal.aborted || evidence.claimToken === undefined)
116
101
  return;
117
102
  await presentPending({ artifact, location, participant: seat.participant,
118
- activity: formatActivityId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() { } }, markSeen: !clipped, signal,
103
+ message: formatMessageId(index), seat: asSeatRef(seat), observationOf: evidence.claimToken, sink: { present() { } }, markSeen: !clipped, signal,
119
104
  current: (facts) => {
120
- const current = facts.seat(seat.participant);
121
- const prepared = current === undefined || !sameSeat(current, seat) ? undefined : preview(facts, location, seat, index);
122
105
  const correlation = facts.deliveryByToken(evidence.claimToken);
123
- return prepared !== undefined && payloadHash(prepared.payload) === hash
106
+ return nativePendingHead(facts, { location, participant: seat.participant, generation: seat.generation,
107
+ owner: seat.owner, index, route: evidence.route, now: Date.now() }) !== undefined
108
+ && ['dispatching', 'unknown', 'accepted'].includes(correlation?.outcome ?? '')
124
109
  && correlation?.correlation?.harness === 'opencode'
125
110
  && correlation.correlation.token !== undefined
126
111
  && correlation.correlation.payloadHash === hash;
@@ -157,12 +142,12 @@ export async function receiveOpenCodePending(target, env, signal) {
157
142
  const { value: notifications } = await artifact.read((facts) => {
158
143
  if (!sameSeat(facts.seat(seat.participant), seat))
159
144
  return [];
160
- return membership.notifications.filter((note) => automaticWakeEligible(facts.deliveryEvidence(seat.participant, seat.generation, formatActivityId(note.actIndex)), Date.now()));
145
+ return membership.notifications.filter((note) => automaticWakeEligible(facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.messageIndex)), Date.now()));
161
146
  }, signal);
162
147
  for (const note of notifications) {
163
148
  if (signal.aborted)
164
149
  return;
165
- await deliverPending({ artifact, transport, location, activity: formatActivityId(note.actIndex), now: Date.now() });
150
+ await deliverPending({ artifact: channelArtifact(artifact), preparePresentation: squareWakeProjection(artifact), transport, location, message: formatMessageId(note.messageIndex), now: Date.now() });
166
151
  }
167
152
  }
168
153
  catch (error) {
@@ -213,7 +198,7 @@ export async function cancelOpenCodeDeliveries(sessionId, env, signal) {
213
198
  continue;
214
199
  const artifact = createArtifactPort(location, undefined, signal);
215
200
  try {
216
- const { value: through } = await artifact.read((facts) => facts.header().lastActIndex, signal);
201
+ const { value: through } = await artifact.read((facts) => facts.channel.header().lastMessageIndex, signal);
217
202
  if (through >= 0)
218
203
  await artifact.cancelDeliveries(asSeatRef(seat), through, signal);
219
204
  }
package/dist/ports.d.ts CHANGED
@@ -1,7 +1,10 @@
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, SayRelation, Owner, HardCap, Seat, StoredAct, WakeAddress, WatchLease } from './model.js';
4
2
  import type { ActivitySearch } from './square-facade.js';
3
+ import type { IndexPage, FactPage } from './neutral.js';
4
+ export type { WakeTransportPort, WakePresentation, WakeProjectionContext, WakeProjection, WakeRequest, WakeOutcome } from './channel-transport.js';
5
+ export type { IndexPage, FactPage } from './neutral.js';
6
+ import type { ChannelRead, ChannelWrite, ChannelWriteAuthority, ChannelDeliveryEvidence, ChannelDeliveryClaimInput, ChannelDeliveryClaimResult, ChannelDeliveryPreparation, ChannelDeliveryCompletion, MessageId, EndpointGeneration } from './channel-ports.js';
7
+ export type { ChannelArtifact, ChannelRead, ChannelWrite, ChannelWriteAuthority, ChannelHeader, MessageFact, MessageAddressFact, MessageHead, MessageFilter, MessageDraft, EndpointFact, EndpointRef, ReceiveClaimFact, ChannelDeliveryEvidence, ChannelDeliveryClaimInput, ChannelDeliveryClaimResult, ChannelDeliveryPreparation, ChannelDeliveryCompletion } from './channel-ports.js';
5
8
  export type ActivityIndex = number;
6
9
  export interface SquareHeader {
7
10
  readonly revision: number;
@@ -40,6 +43,7 @@ export interface ActivityHead {
40
43
  readonly actor: string;
41
44
  readonly reach?: 'bell';
42
45
  readonly reply?: ActivityIndex;
46
+ readonly messageIndex?: number;
43
47
  }
44
48
  export interface ActivityFilter {
45
49
  readonly afterIndex?: number;
@@ -51,19 +55,13 @@ export interface ActivityFilter {
51
55
  readonly actorMatch?: 'identity' | 'exact';
52
56
  readonly afterAt?: number;
53
57
  /** Shared query predicates, applied before paging or counting. */
54
- readonly metadataMatch?: MetadataMatch;
58
+ readonly metadataMatch?: import('./delivery-deferral.js').MetadataMatch;
55
59
  readonly search?: ActivitySearch;
56
60
  }
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
- }
61
+ /** Upper attention filter over its own landed reasons and lower message addresses. */
65
62
  export interface DirectedFilter extends Omit<ActivityFilter, 'actors' | 'actorMatch'> {
66
63
  readonly unseenOnly?: true;
64
+ readonly currentGeneration?: true;
67
65
  readonly senders?: readonly string[];
68
66
  readonly attentionOnly?: true;
69
67
  }
@@ -72,21 +70,21 @@ export interface DirectedHead {
72
70
  readonly at: number;
73
71
  readonly actor: string;
74
72
  readonly recipient: string;
75
- readonly route: import('./attention.js').AudienceRoute;
73
+ readonly route: import('./model.js').DirectedNotificationRoute;
74
+ readonly messageIndex: number;
75
+ readonly generation?: EndpointGeneration;
76
+ }
77
+ export interface LandedRecipient {
78
+ readonly recipient: string;
79
+ readonly route: import('./model.js').DirectedNotificationRoute;
80
+ readonly generation?: EndpointGeneration;
76
81
  }
77
- export type LandedRecipient = import('./attention.js').AudienceRecipient;
78
82
  export interface ReceiptFacts {
79
83
  readonly latestAt?: number;
80
84
  readonly latestActorActivityIndex?: number;
81
85
  }
82
86
  export type SeatRef = Pick<Seat, 'participant' | 'owner' | 'generation'>;
83
- export type ArtifactWriteAuthority = {
84
- readonly kind: 'send';
85
- readonly actor: string;
86
- } | {
87
- readonly kind: 'participant';
88
- readonly seat: SeatRef;
89
- } | {
87
+ export type ArtifactWriteAuthority = ChannelWriteAuthority | {
90
88
  readonly kind: 'host-cleanup';
91
89
  readonly seat: SeatRef;
92
90
  } | {
@@ -98,79 +96,24 @@ export type ArtifactWriteAuthority = {
98
96
  readonly kind: 'takeover';
99
97
  readonly participant: string;
100
98
  readonly owner: Owner;
101
- readonly expectedGeneration: ActivityId;
99
+ readonly expectedGeneration: EndpointGeneration;
102
100
  readonly wakeAddress?: WakeAddress;
103
101
  };
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
- } | {
148
- readonly status: 'busy' | 'terminal' | 'stale' | 'deferred';
149
- readonly evidence?: DeliveryEvidence;
150
- };
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
- }
102
+ export type DeliveryEvidence = ChannelDeliveryEvidence;
103
+ export type DeliveryClaimInput = ChannelDeliveryClaimInput;
104
+ export type DeliveryClaimResult = ChannelDeliveryClaimResult;
105
+ export type DeliveryPreparation = ChannelDeliveryPreparation;
106
+ export type DeliveryCompletion = ChannelDeliveryCompletion;
107
+ export type { MessageId, EndpointGeneration };
167
108
  /** Domain facts exist only during a synchronous artifact callback. Values are detached. */
168
109
  export interface SquareRead {
110
+ /** Lower Channel facts. Upper layers may use them; Channel never sees this composite. */
111
+ readonly channel: ChannelRead;
169
112
  seat(requestedName: string): Seat | undefined;
170
113
  seatsForOwner(owner: Owner): readonly Seat[];
171
- deliveryEvidence(participant: string, generation: ActivityId, activity?: ActivityId): readonly DeliveryEvidence[];
114
+ deliveryEvidence(participant: string, generation: EndpointGeneration, message?: MessageId): readonly DeliveryEvidence[];
172
115
  deliveryByToken(claimToken: string): DeliveryEvidence | undefined;
173
- cancelledThrough(participant: string, generation: ActivityId): number | undefined;
116
+ cancelledThrough(participant: string, generation: EndpointGeneration): number | undefined;
174
117
  header(): SquareHeader;
175
118
  participant(requestedName: string): ParticipantFact | undefined;
176
119
  participantCounts(): ParticipantCounts;
@@ -178,8 +121,10 @@ export interface SquareRead {
178
121
  attention(listener: string): readonly AttentionFact[];
179
122
  incomingAttention(sender: string): readonly AttentionFact[];
180
123
  activity(index: ActivityIndex): StoredAct | undefined;
124
+ activityByMessage(messageIndex: number): SayRelation | undefined;
181
125
  activityHeads(filter: ActivityFilter, page: IndexPage): FactPage<ActivityHead>;
182
- activities(indexes: readonly ActivityIndex[]): readonly StoredAct[];
126
+ /** The explicit content projection: each say resolved against its Channel message in one read. */
127
+ activities(indexes: readonly ActivityIndex[]): readonly HydratedAct[];
183
128
  activityCount(filter: ActivityFilter): number;
184
129
  contextIndexes(centers: readonly ActivityIndex[], before: number, after: number): readonly ActivityIndex[];
185
130
  sayOrdinals(indexes: readonly ActivityIndex[]): Readonly<Record<number, number>>;
@@ -188,46 +133,24 @@ export interface SquareRead {
188
133
  directed(recipient: string, filter: DirectedFilter, page: IndexPage): FactPage<DirectedHead>;
189
134
  directedAt(recipient: string, index: ActivityIndex): DirectedHead | undefined;
190
135
  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
136
  receipt(requestedName: string, index: ActivityIndex): {
201
137
  readonly at: number;
202
138
  } | undefined;
203
139
  receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
204
140
  lease(requestedName: string): WatchLease | undefined;
205
- deliveryDeferrals(participant: string, generation: ActivityId): readonly DeliveryDeferralFact[];
141
+ deliveryDeferrals(participant: string, generation: EndpointGeneration): readonly import('./delivery-deferral.js').DeliveryDeferralFact[];
206
142
  }
207
143
  export interface SquareWrite extends SquareRead {
208
- land(activity: Act & {
209
- readonly at: number;
210
- }): StoredAct;
144
+ readonly channel: ChannelWrite;
145
+ land(draft: ActDraft): StoredAct;
211
146
  updateWakeAddress(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
212
147
  /** Join authority only; preserves membership and its establishing generation. */
213
148
  reattachSeat(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
214
149
  /** Host-cleanup authority only; stale/already-detached coordinates are a no-op. */
215
150
  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;
151
+ /** Upper catch claim: the lease plus the message coverage it currently owns. */
152
+ putLease(requestedName: string, lease: WatchLease, generation?: EndpointGeneration, suppressed?: readonly number[]): boolean;
153
+ removeLease(requestedName: string, expectedLeaseId: string, generation?: EndpointGeneration): boolean;
231
154
  }
232
155
  export interface SquareReadResult<R> {
233
156
  readonly value: R;
@@ -249,8 +172,8 @@ export interface SquareArtifactPort {
249
172
  claimDelivery(input: DeliveryClaimInput, signal?: AbortSignal): Promise<DeliveryClaimResult>;
250
173
  prepareDelivery(input: DeliveryPreparation, signal?: AbortSignal): Promise<boolean>;
251
174
  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>;
175
+ /** Persist suppression through a fixed message, or atomically capture the current artifact head. */
176
+ cancelDeliveries(seat: SeatRef, through?: number, signal?: AbortSignal): Promise<boolean>;
254
177
  changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
255
178
  close(): Promise<void>;
256
179
  }
@@ -264,71 +187,25 @@ export interface SquareResourceOwner {
264
187
  artifact(location: string): SquareArtifactPort;
265
188
  close(): Promise<void>;
266
189
  }
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
190
  /** Executor-neutral presentation capability; intentionally separate from wake transport. */
289
191
  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;
192
+ present(message: MessageFact): void | Promise<void>;
316
193
  }
317
194
  export interface PresentationResult {
318
195
  readonly presented: boolean;
319
- readonly activity?: StoredAct;
196
+ readonly message?: MessageFact;
320
197
  }
321
198
  export interface PresentPendingInput {
322
199
  readonly artifact: SquareArtifactPort;
323
200
  readonly location: string;
324
201
  readonly participant: string;
325
- readonly activity: string | number;
202
+ readonly message: string | number;
326
203
  readonly sink: PresentationSinkPort;
327
204
  /** Existing wake observed by a native receiver; the sink must not inject content. */
328
205
  readonly observationOf?: string;
329
206
  /** Captured artifact seat; used to fence both presentation and consumption. */
330
207
  readonly seat?: SeatRef;
331
- readonly generation?: ActivityId;
208
+ readonly generation?: EndpointGeneration;
332
209
  readonly now?: number;
333
210
  readonly timeoutMs?: number;
334
211
  /** Pre-effect work control only; successful sink evidence settles independently, with a bounded lifetime. */
@@ -338,3 +215,4 @@ export interface PresentPendingInput {
338
215
  /** Receiver-specific pre-effect eligibility, never a veto of an already successful sink. */
339
216
  readonly current?: (facts: SquareRead) => boolean;
340
217
  }
218
+ import type { MessageFact } from './channel-ports.js';
@@ -1,18 +1,5 @@
1
1
  import type { PresentationResult, PresentPendingInput } from './ports.js';
2
2
  /** Prepare custody before any external effect. A batch prepares every included item first. */
3
- export declare function preparePendingPresentation(input: Omit<PresentPendingInput, 'sink'>): Promise<import("./delivery-effects.js").PreparedPresentation<{
4
- kind: "say";
5
- actor: import("./square-core.js").Participant;
6
- at?: number;
7
- body?: string;
8
- metadata?: import("./model.js").ActivityMetadata;
9
- delivery?: import("./model.js").ActivityDelivery;
10
- mentions?: readonly import("./square-core.js").Participant[];
11
- reach?: import("./square-core.js").Reach;
12
- reply?: number;
13
- } & {
14
- index: number;
15
- at: number;
16
- }> | undefined>;
3
+ export declare function preparePendingPresentation(input: Omit<PresentPendingInput, 'sink'>): Promise<import("./delivery-effects.js").PreparedPresentation<import("./channel-ports.js").MessageFact> | undefined>;
17
4
  /** A sink error cannot prove that content was never injected. */
18
5
  export declare function presentPending(input: PresentPendingInput): Promise<PresentationResult>;
@@ -1,5 +1,5 @@
1
1
  import { randomUUID } from 'node:crypto';
2
- import { formatActivityId, parseActivityId } from './square-core.js';
2
+ import { formatMessageId, parseMessageId } from './message.js';
3
3
  import { isSquareError } from './model.js';
4
4
  import { pendingHead, sameJoin } from './delivery.js';
5
5
  import { deliveryDeferralExpiry } from './delivery-deferral.js';
@@ -7,19 +7,19 @@ import { asSeatRef } from './seats.js';
7
7
  import { preparePresentation, presentPrepared } from './delivery-effects.js';
8
8
  /** Prepare custody before any external effect. A batch prepares every included item first. */
9
9
  export async function preparePendingPresentation(input) {
10
- const index = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
10
+ const named = input.message;
11
+ const index = typeof named === 'number' ? named : parseMessageId(named);
11
12
  if (index === undefined)
12
13
  return undefined;
13
14
  const initial = await input.artifact.read((facts) => {
14
- const participant = facts.participant(input.participant);
15
- const seat = facts.seat(input.participant);
16
- if (!participant?.joined || seat === undefined || seat.attachment !== 'attached' || input.generation !== undefined && input.generation !== seat.generation
15
+ const seat = facts.channel.endpoint(input.participant);
16
+ if (seat === undefined || seat.attachment !== 'attached' || input.generation !== undefined && input.generation !== seat.generation
17
17
  || input.seat !== undefined && (input.seat.generation !== seat.generation || input.seat.owner !== seat.owner)
18
- || input.current?.(facts) === false || !pendingHead(facts, participant.name, index)
19
- || deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) !== undefined)
18
+ || input.current?.(facts) === false || !pendingHead(facts, input.participant, index)
19
+ || deliveryDeferralExpiry(facts.channel, seat, index, input.now ?? Date.now()) !== undefined)
20
20
  return undefined;
21
- const item = facts.activity(index);
22
- return item?.kind === 'say' ? { participant, seat, item } : undefined;
21
+ const item = facts.channel.message(index);
22
+ return item === undefined ? undefined : { participant: { name: input.participant, generation: seat.generation }, seat, item };
23
23
  }, input.signal).catch(() => undefined);
24
24
  if (initial?.value === undefined)
25
25
  return undefined;
@@ -35,14 +35,14 @@ export async function preparePendingPresentation(input) {
35
35
  payload: item, coverage: input.markSeen === false ? 'clipped' : 'full',
36
36
  port: {
37
37
  async claim(signal) {
38
- const claimed = await input.artifact.claimDelivery({ seat: ref, activity: formatActivityId(index), kind: 'presentation',
38
+ const claimed = await input.artifact.claimDelivery({ seat: ref, message: formatMessageId(index), kind: 'presentation',
39
39
  observationOf: input.observationOf, at: input.now ?? Date.now(), leaseMs: input.timeoutMs ?? 5000, claimToken }, signal).catch(() => ({ status: 'stale' }));
40
40
  return claimed.status === 'acquired' ? { token: claimToken, ordinal: claimed.attemptN } : undefined;
41
41
  },
42
42
  async current(signal) {
43
43
  return (await input.artifact.read(facts => owns(facts) && input.current?.(facts) !== false
44
44
  && pendingHead(facts, participant.name, index) !== undefined
45
- && deliveryDeferralExpiry(facts, seat, index, input.now ?? Date.now()) === undefined, signal ?? input.signal)).value;
45
+ && deliveryDeferralExpiry(facts.channel, seat, index, input.now ?? Date.now()) === undefined, signal ?? input.signal)).value;
46
46
  },
47
47
  async settle(claim, completion) {
48
48
  const success = completion.outcome === 'presented' || completion.outcome === 'clipped';
@@ -50,13 +50,13 @@ export async function preparePendingPresentation(input) {
50
50
  // Captured ownership fences truth; post-effect policy/cancellation cannot veto it.
51
51
  if (!owns(facts))
52
52
  return false;
53
- if (!facts.completeDelivery({ seat: ref, activity: formatActivityId(index), claimToken: claim.token,
53
+ if (!facts.channel.completeDelivery({ seat: ref, message: formatMessageId(index), claimToken: claim.token,
54
54
  outcome: completion.outcome, at: success ? input.now ?? Date.now() : Date.now(),
55
- ...(completion.reason === 'threw' ? { message: completion.error instanceof Error ? completion.error.message : String(completion.error) } : {}),
55
+ ...(completion.reason === 'threw' ? { messageText: completion.error instanceof Error ? completion.error.message : String(completion.error) } : {}),
56
56
  }))
57
57
  return false;
58
58
  if (completion.outcome === 'presented')
59
- facts.markSeen(participant.name, [index], input.now ?? Date.now());
59
+ facts.channel.markSeen(participant.name, [index], input.now ?? Date.now());
60
60
  return true;
61
61
  }).catch(error => {
62
62
  if (!success || isSquareError(error) && error.code === 'already_joined')
@@ -73,7 +73,7 @@ export async function presentPending(input) {
73
73
  const prepared = await preparePendingPresentation(input);
74
74
  if (prepared === undefined)
75
75
  return { presented: false };
76
- const report = await presentPrepared(prepared, activity => input.sink.present(activity), {
76
+ const report = await presentPrepared(prepared, message => input.sink.present(message), {
77
77
  signal: input.signal,
78
78
  });
79
79
  if (report.observation.kind === 'threw')
@@ -82,5 +82,5 @@ export async function presentPending(input) {
82
82
  if (result.persistence.status === 'error')
83
83
  throw result.persistence.error;
84
84
  return report.observation.kind === 'returned' && result.persistence.status === 'recorded'
85
- ? { presented: true, activity: prepared.payload } : { presented: false };
85
+ ? { presented: true, message: prepared.payload } : { presented: false };
86
86
  }
@@ -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;