@astrosheep/square 0.3.86 → 0.3.87
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.
- package/dist/artifact.d.ts +6 -1
- package/dist/artifact.js +79 -10
- package/dist/boundary-presentation.d.ts +5 -4
- package/dist/boundary-presentation.js +21 -25
- package/dist/catch-decisions.js +2 -1
- package/dist/claude-delivery-status.d.ts +8 -2
- package/dist/claude-delivery-status.js +26 -10
- package/dist/claude-delivery.d.ts +8 -4
- package/dist/claude-delivery.js +114 -104
- package/dist/claude-mcp-context.js +2 -2
- package/dist/claude-mod.js +119 -105
- package/dist/cli/context.d.ts +3 -1
- package/dist/cli/context.js +25 -1
- package/dist/cli/express-presentation.d.ts +3 -1
- package/dist/cli/express-presentation.js +14 -10
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.js +1 -0
- package/dist/cli/registry.js +0 -2
- package/dist/cli/square-commands.d.ts +4 -2
- package/dist/cli/square-commands.js +21 -6
- package/dist/codex-hook.d.ts +3 -3
- package/dist/codex-hook.js +4 -4
- package/dist/codex-queue.d.ts +5 -6
- package/dist/codex-queue.js +9 -5
- package/dist/decisions.d.ts +3 -2
- package/dist/decisions.js +18 -6
- package/dist/delivery-health.d.ts +32 -2
- package/dist/delivery-health.js +108 -45
- package/dist/delivery-operations.d.ts +1 -2
- package/dist/delivery-operations.js +155 -196
- package/dist/delivery.d.ts +14 -23
- package/dist/delivery.js +48 -18
- package/dist/file-lock.d.ts +8 -1
- package/dist/file-lock.js +2 -0
- package/dist/help.js +8 -11
- package/dist/inbox.d.ts +35 -9
- package/dist/inbox.js +228 -144
- package/dist/index.d.ts +3 -4
- package/dist/index.js +1 -3
- package/dist/list.js +9 -4
- package/dist/mcp.js +12 -1
- package/dist/model.d.ts +39 -5
- package/dist/model.js +84 -0
- package/dist/notifications.d.ts +8 -6
- package/dist/notifications.js +56 -143
- package/dist/operation-presentation.js +3 -4
- package/dist/participant-identity.d.ts +0 -3
- package/dist/participant-identity.js +0 -31
- package/dist/paseo-delivery.d.ts +5 -6
- package/dist/paseo-delivery.js +13 -8
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/delivery.d.ts +8 -3
- package/dist/plugins/opencode/delivery.js +187 -132
- package/dist/plugins/opencode/index.js +12 -38
- package/dist/ports.d.ts +112 -40
- package/dist/presentation-operations.d.ts +1 -1
- package/dist/presentation-operations.js +51 -35
- package/dist/presentation.js +8 -3
- package/dist/seats.d.ts +7 -0
- package/dist/seats.js +27 -0
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +10 -12
- package/dist/sqlite-square.d.ts +20 -9
- package/dist/sqlite-square.js +471 -339
- package/dist/square-api.d.ts +1 -0
- package/dist/square-api.js +3 -4
- package/dist/square-core.d.ts +3 -2
- package/dist/square-domain.d.ts +12 -5
- package/dist/square-domain.js +163 -7
- package/dist/square-facade.d.ts +14 -10
- package/dist/square-index.d.ts +24 -0
- package/dist/square-index.js +131 -0
- package/dist/square-legacy-framing.d.ts +9 -0
- package/dist/square-legacy-framing.js +56 -0
- package/dist/square-migration-host-ledger.d.ts +26 -0
- package/dist/square-migration-host-ledger.js +168 -0
- package/dist/square-migration.d.ts +5 -5
- package/dist/square-migration.js +373 -71
- package/dist/square-sqlite.d.ts +9 -9
- package/dist/square-sqlite.js +329 -79
- package/dist/square-stdio.d.ts +1 -1
- package/dist/square-stdio.js +12 -7
- package/dist/square-tools.js +29 -2
- package/dist/stdio-server.js +4 -2
- package/dist/stream.d.ts +3 -2
- package/dist/stream.js +9 -10
- package/dist/views.d.ts +9 -14
- package/dist/views.js +50 -70
- package/dist/wakes.d.ts +6 -5
- package/dist/wakes.js +20 -17
- package/guides/application-facade-delivery-evidence.md +1 -1
- package/guides/claude-native.md +9 -9
- package/guides/mcp.md +1 -1
- package/guides/migration.md +107 -67
- package/guides/row-storage-design.md +1 -1
- package/guides/square-api.md +61 -28
- package/package.json +1 -1
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/register.js +1 -1
- package/plugins/claude/skills/square/SKILL.md +1 -1
- package/plugins/claude/types.d.ts +1 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/opencode/delivery.ts +170 -118
- package/plugins/opencode/index.ts +12 -30
- package/plugins/pi/square-pi.js +55 -17
- package/skills/square/SKILL.md +1 -1
- package/dist/cli/ledger-command.d.ts +0 -12
- package/dist/cli/ledger-command.js +0 -72
- package/dist/host-ledger-file-adapter.d.ts +0 -47
- package/dist/host-ledger-file-adapter.js +0 -384
- package/dist/host-ledger-migration.d.ts +0 -27
- package/dist/host-ledger-migration.js +0 -264
- package/dist/host-ledger-root.d.ts +0 -2
- package/dist/host-ledger-root.js +0 -9
- package/dist/host-ledger-sqlite.d.ts +0 -65
- package/dist/host-ledger-sqlite.js +0 -368
- package/dist/host-ledger.d.ts +0 -195
- package/dist/host-ledger.js +0 -1
- package/dist/ledger-maintenance.d.ts +0 -30
- package/dist/ledger-maintenance.js +0 -110
- package/dist/open-square.d.ts +0 -38
- package/dist/open-square.js +0 -81
- package/dist/participant-host.d.ts +0 -23
- package/dist/participant-host.js +0 -92
- package/dist/registry.d.ts +0 -55
- package/dist/registry.js +0 -123
- package/dist/routes.d.ts +0 -39
- package/dist/routes.js +0 -99
- package/dist/square-actions.d.ts +0 -56
- package/dist/square-actions.js +0 -353
- package/dist/square-projections.d.ts +0 -31
- package/dist/square-projections.js +0 -64
- package/dist/wake-attempts.d.ts +0 -10
- package/dist/wake-attempts.js +0 -39
- package/dist/wake-eligibility.d.ts +0 -53
- package/dist/wake-eligibility.js +0 -123
- package/dist/wake-evidence.d.ts +0 -33
- package/dist/wake-evidence.js +0 -32
- package/guides/host-ledger.md +0 -58
- package/guides/indexed-host-ledger-performance.md +0 -39
package/dist/ports.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { Act, HardCap,
|
|
2
|
-
export type { ClaimResult, EvidenceClaim, EvidenceRelease, EvidenceLookup, EvidenceRecord, NativeTokenLookup, NativeTokenResult, HostLedgerPort, PresenceKey, PresenceLookup, PresenceRecord, PresenceClaimResult, PresenceChannel, PresenceResult, } from './host-ledger.js';
|
|
3
|
-
import type { HostLedgerPort } from './host-ledger.js';
|
|
1
|
+
import type { Act, ActivityId, Body, HardCap, Seat, StoredAct, WakeAddress, WakeRoute, WatchLease } from './model.js';
|
|
4
2
|
export type ActivityIndex = number;
|
|
5
3
|
export interface SquareHeader {
|
|
6
4
|
readonly revision: number;
|
|
5
|
+
readonly deliveryRevision: number;
|
|
7
6
|
readonly nextActIndex: number;
|
|
8
7
|
readonly lastActIndex: number;
|
|
9
8
|
readonly hardCap: HardCap;
|
|
@@ -88,19 +87,92 @@ export interface ReceiptAssignment {
|
|
|
88
87
|
export type JsonValue = null | boolean | number | string | readonly JsonValue[] | {
|
|
89
88
|
readonly [key: string]: JsonValue;
|
|
90
89
|
};
|
|
91
|
-
export
|
|
92
|
-
|
|
90
|
+
export type SeatRef = Omit<Seat, 'wakeAddress'>;
|
|
91
|
+
export type ArtifactWriteAuthority = {
|
|
92
|
+
readonly kind: 'participant';
|
|
93
|
+
readonly seat: SeatRef;
|
|
94
|
+
} | {
|
|
95
|
+
readonly kind: 'join';
|
|
93
96
|
readonly participant: string;
|
|
94
|
-
readonly
|
|
95
|
-
readonly
|
|
96
|
-
|
|
97
|
-
readonly
|
|
98
|
-
readonly
|
|
99
|
-
readonly
|
|
100
|
-
readonly
|
|
97
|
+
readonly body: Body;
|
|
98
|
+
readonly wakeAddress?: WakeAddress;
|
|
99
|
+
} | {
|
|
100
|
+
readonly kind: 'takeover';
|
|
101
|
+
readonly participant: string;
|
|
102
|
+
readonly body: Body;
|
|
103
|
+
readonly expectedGeneration: ActivityId;
|
|
104
|
+
readonly wakeAddress?: WakeAddress;
|
|
105
|
+
};
|
|
106
|
+
export interface DeliveryEvidence {
|
|
107
|
+
readonly participant: string;
|
|
108
|
+
readonly generation: ActivityId;
|
|
109
|
+
readonly activity: ActivityId;
|
|
110
|
+
readonly kind: 'wake' | 'presentation';
|
|
111
|
+
readonly outcome: 'dispatching' | 'accepted' | 'failed' | 'unknown' | 'released' | 'presented' | 'clipped';
|
|
112
|
+
readonly attemptN: number;
|
|
113
|
+
readonly at: number;
|
|
114
|
+
readonly expiresAt?: number;
|
|
115
|
+
readonly claimToken?: string;
|
|
116
|
+
readonly route?: WakeAddress;
|
|
117
|
+
readonly signature?: string;
|
|
118
|
+
readonly message?: string;
|
|
119
|
+
readonly diagnostic?: import('./model.js').JsonValue;
|
|
120
|
+
readonly correlation?: {
|
|
121
|
+
readonly harness: 'claude' | 'opencode' | 'pi';
|
|
122
|
+
readonly token?: string;
|
|
123
|
+
readonly messageId?: string;
|
|
124
|
+
readonly payloadHash?: string;
|
|
125
|
+
};
|
|
126
|
+
readonly nativeObservations?: readonly {
|
|
127
|
+
readonly eventId: string;
|
|
128
|
+
readonly messageId: string;
|
|
129
|
+
readonly state: 'held' | 'released' | 'dropped';
|
|
130
|
+
readonly reason?: string;
|
|
131
|
+
readonly observedAt: number;
|
|
132
|
+
}[];
|
|
133
|
+
}
|
|
134
|
+
export interface DeliveryClaimInput {
|
|
135
|
+
readonly seat: SeatRef;
|
|
136
|
+
readonly activity: ActivityId;
|
|
137
|
+
readonly kind: 'wake' | 'presentation';
|
|
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';
|
|
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'];
|
|
101
166
|
}
|
|
102
167
|
/** Domain facts exist only during a synchronous artifact callback. Values are detached. */
|
|
103
168
|
export interface SquareRead {
|
|
169
|
+
seat(requestedName: string): Seat | undefined;
|
|
170
|
+
seatsForBody(body: Body): readonly Seat[];
|
|
171
|
+
/** Latest completed coordinates only; never participant write authority. */
|
|
172
|
+
completedSeatsForBody(body: Body): readonly Seat[];
|
|
173
|
+
deliveryEvidence(participant: string, generation: ActivityId, activity?: ActivityId): readonly DeliveryEvidence[];
|
|
174
|
+
deliveryByToken(claimToken: string): DeliveryEvidence | undefined;
|
|
175
|
+
cancelledThrough(participant: string, generation: ActivityId): number | undefined;
|
|
104
176
|
header(): SquareHeader;
|
|
105
177
|
participant(requestedName: string): ParticipantFact | undefined;
|
|
106
178
|
participantCounts(): ParticipantCounts;
|
|
@@ -139,16 +211,24 @@ export interface SquareRead {
|
|
|
139
211
|
receiptFacts(requestedName: string, afterIndex: number): ReceiptFacts;
|
|
140
212
|
receiptAssignments(): readonly ReceiptAssignment[];
|
|
141
213
|
lease(requestedName: string): WatchLease | undefined;
|
|
142
|
-
routes(): readonly RouteFact[];
|
|
143
214
|
}
|
|
144
215
|
export interface SquareWrite extends SquareRead {
|
|
145
216
|
land(activity: Act & {
|
|
146
217
|
readonly at: number;
|
|
147
218
|
}): StoredAct;
|
|
219
|
+
updateWakeAddress(seat: SeatRef, wakeAddress?: WakeAddress): boolean;
|
|
220
|
+
cancelThrough(seat: SeatRef, through: ActivityIndex): boolean;
|
|
221
|
+
claimDelivery(input: DeliveryClaimInput): DeliveryClaimResult;
|
|
222
|
+
prepareDelivery(input: DeliveryPreparation): boolean;
|
|
223
|
+
completeDelivery(input: DeliveryCompletion): boolean;
|
|
224
|
+
observeNativeDelivery(input: {
|
|
225
|
+
readonly seat: SeatRef;
|
|
226
|
+
readonly claimToken: string;
|
|
227
|
+
readonly observation: NonNullable<DeliveryEvidence['nativeObservations']>[number];
|
|
228
|
+
}): boolean;
|
|
148
229
|
markSeen(requestedName: string, indexes: readonly ActivityIndex[], at: number): number;
|
|
149
|
-
putLease(requestedName: string, lease: WatchLease): boolean;
|
|
150
|
-
removeLease(requestedName: string, expectedLeaseId: string): boolean;
|
|
151
|
-
replaceRoutes(routes: readonly RouteFact[]): boolean;
|
|
230
|
+
putLease(requestedName: string, lease: WatchLease, generation?: ActivityId): boolean;
|
|
231
|
+
removeLease(requestedName: string, expectedLeaseId: string, generation?: ActivityId): boolean;
|
|
152
232
|
}
|
|
153
233
|
export interface SquareReadResult<R> {
|
|
154
234
|
readonly value: R;
|
|
@@ -160,8 +240,15 @@ export interface SquareCommitResult<R> extends SquareReadResult<R> {
|
|
|
160
240
|
/** Atomic artifact access; no framing, SQL or lazy transaction capability escapes. */
|
|
161
241
|
export interface SquareArtifactPort {
|
|
162
242
|
read<R>(select: (facts: SquareRead) => R, signal?: AbortSignal): Promise<SquareReadResult<R>>;
|
|
163
|
-
|
|
243
|
+
/** Participant/domain writes must carry an explicit authority; no body-less mutation path exists. */
|
|
244
|
+
transactAs<R>(authority: ArtifactWriteAuthority, change: (facts: SquareWrite) => R, signal?: AbortSignal): Promise<SquareCommitResult<R>>;
|
|
164
245
|
revision(signal?: AbortSignal): Promise<number>;
|
|
246
|
+
deliveryRevision(signal?: AbortSignal): Promise<number>;
|
|
247
|
+
claimDelivery(input: DeliveryClaimInput, signal?: AbortSignal): Promise<DeliveryClaimResult>;
|
|
248
|
+
prepareDelivery(input: DeliveryPreparation, signal?: AbortSignal): Promise<boolean>;
|
|
249
|
+
completeDelivery(input: DeliveryCompletion, signal?: AbortSignal): Promise<boolean>;
|
|
250
|
+
/** Persist suppression through a fixed index, or atomically capture the current artifact head. */
|
|
251
|
+
cancelDeliveries(seat: SeatRef, through?: ActivityIndex, signal?: AbortSignal): Promise<boolean>;
|
|
165
252
|
changed(sinceVersion: number, timeoutMs: number, signal?: AbortSignal): Promise<boolean>;
|
|
166
253
|
close(): Promise<void>;
|
|
167
254
|
}
|
|
@@ -175,46 +262,31 @@ export interface SquareResourceOwner {
|
|
|
175
262
|
artifact(location: string): SquareArtifactPort;
|
|
176
263
|
close(): Promise<void>;
|
|
177
264
|
}
|
|
178
|
-
/**
|
|
265
|
+
/** Sole wake executor protocol; implementations own only transport readiness and sending. */
|
|
179
266
|
export interface WakeTransportPort {
|
|
180
267
|
/** Capability check that performs no external wake and writes no evidence. */
|
|
181
268
|
probe?(route: WakeRoute): Promise<boolean | {
|
|
182
269
|
readonly outcome: 'not-capable';
|
|
183
270
|
readonly diagnostic?: string;
|
|
184
271
|
}>;
|
|
185
|
-
attempt(request: WakeRequest, timeoutMs: number, beforeSend
|
|
272
|
+
attempt(request: WakeRequest, timeoutMs: number, beforeSend: () => Promise<boolean>): Promise<WakeOutcome>;
|
|
186
273
|
}
|
|
187
274
|
export interface WakeRequest {
|
|
188
275
|
readonly location: string;
|
|
189
276
|
readonly participant: string;
|
|
190
277
|
readonly activity: string;
|
|
191
278
|
readonly actor: string;
|
|
192
|
-
|
|
279
|
+
readonly seat: SeatRef;
|
|
193
280
|
readonly claimToken?: string;
|
|
194
281
|
readonly attemptN?: number;
|
|
195
282
|
readonly route: WakeRoute;
|
|
283
|
+
/** Artifact context for native adapters that must persist correlation before I/O. */
|
|
284
|
+
readonly artifact?: SquareArtifactPort;
|
|
196
285
|
}
|
|
197
286
|
/** Executor-neutral presentation capability; intentionally separate from wake transport. */
|
|
198
287
|
export interface PresentationSinkPort {
|
|
199
288
|
present(activity: StoredAct): void | Promise<void>;
|
|
200
289
|
}
|
|
201
|
-
/** Stable projection of a host binding; storage rows never cross this boundary. */
|
|
202
|
-
export interface SessionBindingProjection {
|
|
203
|
-
readonly epoch?: number;
|
|
204
|
-
readonly cancelledThrough?: number;
|
|
205
|
-
readonly location: string;
|
|
206
|
-
readonly participant: string;
|
|
207
|
-
readonly sessionId: string;
|
|
208
|
-
readonly channel: import('./host-ledger.js').PresenceChannel;
|
|
209
|
-
readonly updatedAt: number;
|
|
210
|
-
}
|
|
211
|
-
/** Pending boundary presentation projected for one bound session. */
|
|
212
|
-
export interface PresentationProjection {
|
|
213
|
-
readonly binding: SessionBindingProjection;
|
|
214
|
-
readonly joined: boolean;
|
|
215
|
-
readonly notifications: readonly InboxNotification[];
|
|
216
|
-
readonly catchLease?: WatchLease;
|
|
217
|
-
}
|
|
218
290
|
/** Read-only presentation evidence projection for diagnostics and migration callers. */
|
|
219
291
|
export interface PresentationEvidenceProjection {
|
|
220
292
|
readonly location: string;
|
|
@@ -256,7 +328,6 @@ export interface DeliveryResult {
|
|
|
256
328
|
}
|
|
257
329
|
export interface DeliverPendingInput {
|
|
258
330
|
readonly artifact: SquareArtifactPort;
|
|
259
|
-
readonly hostLedger: HostLedgerPort;
|
|
260
331
|
readonly transport: WakeTransportPort;
|
|
261
332
|
readonly location: string;
|
|
262
333
|
readonly timeoutMs?: number;
|
|
@@ -273,8 +344,9 @@ export interface PresentPendingInput {
|
|
|
273
344
|
readonly participant: string;
|
|
274
345
|
readonly activity: string | number;
|
|
275
346
|
readonly sink: PresentationSinkPort;
|
|
276
|
-
|
|
277
|
-
readonly
|
|
347
|
+
/** Captured artifact seat; used to fence both presentation and consumption. */
|
|
348
|
+
readonly seat?: SeatRef;
|
|
349
|
+
readonly generation?: ActivityId;
|
|
278
350
|
readonly now?: number;
|
|
279
351
|
readonly timeoutMs?: number;
|
|
280
352
|
/** One hook deadline propagated into artifact reads/transitions so busy retries stay bounded. */
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { PresentationResult, PresentPendingInput } from './ports.js';
|
|
2
|
-
/** Presentation
|
|
2
|
+
/** Presentation is external; its claim and receipt/evidence commit are generation-fenced in the artifact. */
|
|
3
3
|
export declare function presentPending(input: PresentPendingInput): Promise<PresentationResult>;
|
|
@@ -1,56 +1,72 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
3
|
+
import { isSquareError, sameBody } from './model.js';
|
|
2
4
|
import { pendingHead, sameJoin } from './delivery.js';
|
|
3
|
-
|
|
5
|
+
import { asSeatRef } from './seats.js';
|
|
6
|
+
function currentFor(input, captured, participant, index) {
|
|
7
|
+
return (facts) => {
|
|
8
|
+
const seat = facts.seat(captured.participant);
|
|
9
|
+
return seat !== undefined && sameBody(seat.body, captured.body) && seat.generation === captured.generation
|
|
10
|
+
&& sameJoin(facts, participant) && input.current?.(facts) !== false
|
|
11
|
+
&& pendingHead(facts, captured.participant, index) !== undefined;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** Presentation is external; its claim and receipt/evidence commit are generation-fenced in the artifact. */
|
|
4
15
|
export async function presentPending(input) {
|
|
5
16
|
const index = typeof input.activity === 'number' ? input.activity : parseActivityId(input.activity);
|
|
6
17
|
if (index === undefined)
|
|
7
18
|
return { presented: false };
|
|
8
|
-
const
|
|
9
|
-
|
|
19
|
+
const initial = await input.artifact.read((facts) => {
|
|
20
|
+
const participant = facts.participant(input.participant);
|
|
21
|
+
const seat = facts.seat(input.participant);
|
|
22
|
+
if (!participant?.joined || seat === undefined || input.generation !== undefined && input.generation !== seat.generation
|
|
23
|
+
|| input.seat !== undefined && (input.seat.generation !== seat.generation || !sameBody(input.seat.body, seat.body))
|
|
24
|
+
|| input.current?.(facts) === false || !pendingHead(facts, participant.name, index))
|
|
10
25
|
return undefined;
|
|
11
|
-
|
|
26
|
+
const item = facts.activity(index);
|
|
27
|
+
return item?.kind === 'say' ? { participant, seat, item } : undefined;
|
|
12
28
|
}, input.signal).catch(() => undefined);
|
|
13
|
-
if (
|
|
29
|
+
if (initial?.value === undefined)
|
|
30
|
+
return { presented: false };
|
|
31
|
+
const { participant, seat, item } = initial.value;
|
|
32
|
+
const ref = asSeatRef(seat);
|
|
33
|
+
const current = currentFor(input, seat, participant, index);
|
|
34
|
+
const claimToken = randomUUID();
|
|
35
|
+
const claimed = await input.artifact.claimDelivery({ seat: ref, activity: formatActivityId(index), kind: 'presentation',
|
|
36
|
+
at: input.now ?? Date.now(), leaseMs: input.timeoutMs ?? 5000, claimToken }, input.signal).catch(() => ({ status: 'stale' }));
|
|
37
|
+
if (claimed.status !== 'acquired')
|
|
38
|
+
return { presented: false };
|
|
39
|
+
const release = async (outcome, message) => input.artifact.completeDelivery({
|
|
40
|
+
seat: ref, activity: formatActivityId(index), claimToken, outcome, at: Date.now(),
|
|
41
|
+
...(message === undefined ? {} : { message }),
|
|
42
|
+
}, input.signal).catch(() => false);
|
|
43
|
+
const fresh = await input.artifact.read(current, input.signal).catch(() => undefined);
|
|
44
|
+
if (!fresh?.value) {
|
|
45
|
+
await release('released', 'The notification changed before presentation.');
|
|
14
46
|
return { presented: false };
|
|
15
|
-
const { item, participant } = before.value;
|
|
16
|
-
const current = (facts) => sameJoin(facts, participant) && input.current?.(facts) !== false
|
|
17
|
-
&& pendingHead(facts, participant.name, index) !== undefined;
|
|
18
|
-
const coordinate = { location: input.location, participant: input.participant, session: input.session, activity: formatActivityId(index), kind: 'presentation' };
|
|
19
|
-
let claimToken;
|
|
20
|
-
const release = async () => {
|
|
21
|
-
if (claimToken !== undefined)
|
|
22
|
-
await input.hostLedger.releaseEvidence({ ...coordinate, claimToken });
|
|
23
|
-
};
|
|
24
|
-
if (input.hostLedger !== undefined && input.session !== undefined) {
|
|
25
|
-
const claim = await input.hostLedger.claimEvidence({ ...coordinate, leaseMs: input.timeoutMs ?? 5000 });
|
|
26
|
-
if (claim.status !== 'acquired')
|
|
27
|
-
return { presented: false };
|
|
28
|
-
claimToken = claim.claimToken;
|
|
29
|
-
const fresh = await input.artifact.read(current, input.signal).catch(() => undefined);
|
|
30
|
-
if (!fresh?.value) {
|
|
31
|
-
await release().catch(() => undefined);
|
|
32
|
-
return { presented: false };
|
|
33
|
-
}
|
|
34
47
|
}
|
|
35
48
|
try {
|
|
36
49
|
await input.sink.present(item);
|
|
37
50
|
}
|
|
38
51
|
catch (error) {
|
|
39
|
-
|
|
40
|
-
await input.hostLedger.appendEvidence({ ...coordinate, outcome: 'failed', message: error instanceof Error ? error.message : String(error), claimToken });
|
|
52
|
+
await release('failed', error instanceof Error ? error.message : String(error));
|
|
41
53
|
throw error;
|
|
42
54
|
}
|
|
43
|
-
const
|
|
55
|
+
const outcome = input.markSeen === false ? 'clipped' : 'presented';
|
|
56
|
+
const { value: committed } = await input.artifact.transactAs({ kind: 'participant', seat: ref }, (facts) => {
|
|
44
57
|
if (!current(facts))
|
|
45
58
|
return false;
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
59
|
+
if (!facts.completeDelivery({ seat: ref, activity: formatActivityId(index), claimToken, outcome, at: input.now ?? Date.now() }))
|
|
60
|
+
return false;
|
|
61
|
+
if (input.markSeen === false)
|
|
62
|
+
return true;
|
|
63
|
+
return facts.markSeen(participant.name, [index], input.now ?? Date.now()) > 0;
|
|
64
|
+
}, input.signal).catch(error => {
|
|
65
|
+
if (isSquareError(error) && error.code === 'already_joined')
|
|
66
|
+
return { value: false };
|
|
67
|
+
throw error;
|
|
68
|
+
});
|
|
69
|
+
if (!committed)
|
|
50
70
|
return { presented: false };
|
|
51
|
-
}
|
|
52
|
-
if (claimToken !== undefined)
|
|
53
|
-
await input.hostLedger.appendEvidence({ ...coordinate, outcome: input.markSeen === false ? 'clipped' : 'presented',
|
|
54
|
-
...(input.markSeen === false ? { message: 'presentation clipped' } : {}), claimToken });
|
|
55
71
|
return { presented: true, activity: item };
|
|
56
72
|
}
|
package/dist/presentation.js
CHANGED
|
@@ -168,13 +168,16 @@ function renderEventCliFacts(event, opts = {}) {
|
|
|
168
168
|
return { text: `· ${renderRoomChangeText(event)}`, bodyClipped: false };
|
|
169
169
|
case 'say': {
|
|
170
170
|
const body = renderedBody(event.body, maxBody);
|
|
171
|
+
const visibleBody = body.text === '' && event.metadata !== undefined
|
|
172
|
+
? { text: '(structured activity)', clipped: false }
|
|
173
|
+
: body;
|
|
171
174
|
const mention = opts.mention;
|
|
172
175
|
const mentionSuffix = mention !== undefined && (event.reach === 'bell' || event.mentions?.some((name) => sameName(name, mention)))
|
|
173
176
|
? ` · calls your name across the square — ${participantIdentity(mention)}`
|
|
174
177
|
: '';
|
|
175
178
|
const replySuffix = event.reply === undefined ? '' : ` · replies to ${replyId(event.reply)}`;
|
|
176
179
|
const meta = ` · ${eventId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}`;
|
|
177
|
-
return { text: `● ${participantIdentity(event.actor)}${style('dim', meta)}${bodySuffix(
|
|
180
|
+
return { text: `● ${participantIdentity(event.actor)}${style('dim', meta)}${bodySuffix(visibleBody.text)}`, bodyClipped: visibleBody.clipped };
|
|
178
181
|
}
|
|
179
182
|
case 'done': {
|
|
180
183
|
const body = renderedBody(event.body, maxBody);
|
|
@@ -238,8 +241,10 @@ function renderUnreadSummary(opts) {
|
|
|
238
241
|
if (preview.perception === 'presence') {
|
|
239
242
|
return style('dim', ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered.replace(/\n/g, ' ')}`);
|
|
240
243
|
}
|
|
241
|
-
const compact = preview.act.body.replace(/\s+/g, ' ').trim();
|
|
242
|
-
const bodyPreview = compact === ''
|
|
244
|
+
const compact = (preview.act.body ?? '').replace(/\s+/g, ' ').trim();
|
|
245
|
+
const bodyPreview = compact === ''
|
|
246
|
+
? { text: preview.act.metadata === undefined ? '(empty)' : '(structured activity)', remaining: 0 }
|
|
247
|
+
: truncateChars(compact, UNREAD_PREVIEW_CHARS);
|
|
243
248
|
const bodyText = bodyPreview.remaining === 0 ? bodyPreview.text : `${bodyPreview.text}${style('dim', `… (+${bodyPreview.remaining} chars)`)}`;
|
|
244
249
|
return style('dim', ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · "${bodyText}"`);
|
|
245
250
|
}),
|
package/dist/seats.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Body, type Seat } from './model.js';
|
|
2
|
+
import type { ArtifactWriteAuthority, SeatRef } from './ports.js';
|
|
3
|
+
export declare function requireBody(body: Body | undefined): Body;
|
|
4
|
+
export declare function requireJoinName(name: string | undefined): string;
|
|
5
|
+
export declare function asSeatRef(seat: Seat): SeatRef;
|
|
6
|
+
export declare function participantAuthority(seat: Seat): ArtifactWriteAuthority;
|
|
7
|
+
export declare function requireSeat(seat: Seat | undefined, body: Body | undefined, participant: string): Seat;
|
package/dist/seats.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SquareError, sameBody, sameName, validateBody, validateName } from './model.js';
|
|
2
|
+
export function requireBody(body) {
|
|
3
|
+
if (body === undefined)
|
|
4
|
+
throw new SquareError('invalid_args', 'Participant writes require an explicit body: channel and session.');
|
|
5
|
+
validateBody(body);
|
|
6
|
+
return body;
|
|
7
|
+
}
|
|
8
|
+
export function requireJoinName(name) {
|
|
9
|
+
if (name === undefined)
|
|
10
|
+
throw new SquareError('invalid_args', 'Square.join requires an explicit participant name.');
|
|
11
|
+
validateName(name);
|
|
12
|
+
return name;
|
|
13
|
+
}
|
|
14
|
+
export function asSeatRef(seat) {
|
|
15
|
+
const { wakeAddress: _wakeAddress, ...ref } = seat;
|
|
16
|
+
return ref;
|
|
17
|
+
}
|
|
18
|
+
export function participantAuthority(seat) {
|
|
19
|
+
return { kind: 'participant', seat: asSeatRef(seat) };
|
|
20
|
+
}
|
|
21
|
+
export function requireSeat(seat, body, participant) {
|
|
22
|
+
if (seat === undefined || !sameName(seat.participant, participant))
|
|
23
|
+
throw new SquareError('already_joined', `${participant} needs a current participant handle; join first`);
|
|
24
|
+
if (!sameBody(seat.body, requireBody(body)))
|
|
25
|
+
throw new SquareError('already_joined', `${participant} is not held by this body`);
|
|
26
|
+
return seat;
|
|
27
|
+
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/** Retire only
|
|
1
|
+
import type { AutomaticProvider } from './participant-identity.js';
|
|
2
|
+
/** Retire only seats held by this exact body, discovered through the rebuildable index. */
|
|
3
3
|
export declare function endSessionMemberships(provider: AutomaticProvider, sessionId: string, env?: NodeJS.ProcessEnv): Promise<void>;
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { Square } from './square-api.js';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import { projectSessionSeats } from './inbox.js';
|
|
3
|
+
function bodyFor(provider, sessionId) {
|
|
4
|
+
return { channel: provider === 'claude' ? 'claude-code' : provider, session: sessionId };
|
|
5
|
+
}
|
|
6
|
+
/** Retire only seats held by this exact body, discovered through the rebuildable index. */
|
|
6
7
|
export async function endSessionMemberships(provider, sessionId, env = process.env) {
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
const square = await Square.at({ path: binding.location, hostLedger, env: scoped });
|
|
8
|
+
const body = bodyFor(provider, sessionId);
|
|
9
|
+
const seats = await projectSessionSeats({ body, env });
|
|
10
|
+
for (const { location, seat } of seats) {
|
|
11
|
+
const square = await Square.at({ path: location, env, body, participant: seat.participant });
|
|
12
12
|
try {
|
|
13
|
-
|
|
14
|
-
// endOwnedSession also settles routes and presence under the ownership claim lock.
|
|
15
|
-
await square.endOwnedSession(binding.participant, sessionId, binding.epoch ?? 0);
|
|
13
|
+
await square.endOwnedSession(seat.participant, sessionId, seat.generation, body);
|
|
16
14
|
}
|
|
17
15
|
finally {
|
|
18
16
|
await square.close();
|
package/dist/sqlite-square.d.ts
CHANGED
|
@@ -1,26 +1,37 @@
|
|
|
1
|
+
import { type Body } from './model.js';
|
|
1
2
|
import type { ActivityResult, HistoryQuery, HistoryResult, JoinOptions, JoinResult, ObservationOptions, OperationControl, Participant, SquareAtInput, SquareBuildInput, SquareLocation, StatusOptions, StatusResult, ParticipantsResult, RecoveryResult, DiscoveryOptions, InboxOptions, InboxResult, StreamOptions } from './square-facade.js';
|
|
2
3
|
export type * from './square-facade.js';
|
|
3
|
-
export { createSquareResourceOwner } from './
|
|
4
|
+
export { createSquareResourceOwner } from './artifact.js';
|
|
4
5
|
export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
|
|
5
6
|
export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
|
|
6
7
|
export { HELD_WAIT_BUDGET_MS } from './runtime.js';
|
|
7
8
|
export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
|
|
8
9
|
export { DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from './list.js';
|
|
9
10
|
export declare function squareClock(env: NodeJS.ProcessEnv, clock?: () => number): () => number;
|
|
10
|
-
/** One concrete
|
|
11
|
+
/** One concrete local resource, including its entry-resolved dependencies and lifetime. */
|
|
11
12
|
export declare class SqliteSquare {
|
|
12
13
|
readonly location: string;
|
|
13
|
-
private readonly
|
|
14
|
+
private readonly artifact;
|
|
15
|
+
private readonly clock;
|
|
16
|
+
private readonly body?;
|
|
17
|
+
private readonly wakeAddress?;
|
|
18
|
+
private readonly wakeTransport?;
|
|
19
|
+
private readonly indexPath?;
|
|
20
|
+
private readonly automaticDelivery;
|
|
21
|
+
private readonly ownsArtifact;
|
|
14
22
|
private readonly defaultParticipant?;
|
|
15
|
-
private readonly transport;
|
|
16
23
|
private constructor();
|
|
17
24
|
static at(input: SquareAtInput, control?: OperationControl, automaticDelivery?: boolean): Promise<SqliteSquare>;
|
|
18
25
|
static build(input: SquareBuildInput, control?: OperationControl, automaticDelivery?: boolean): Promise<SqliteSquare>;
|
|
19
26
|
static inMemory(input: Omit<SquareBuildInput, 'path' | 'resources'>): SqliteSquare;
|
|
27
|
+
/** Discovery is best effort and never grants write authority. Finish it before returning. */
|
|
28
|
+
private publishIdentity;
|
|
29
|
+
private observation;
|
|
30
|
+
private contextObservation;
|
|
20
31
|
private handle;
|
|
32
|
+
private static readonly ParticipantHandle;
|
|
21
33
|
participant(requested?: string): Promise<Participant>;
|
|
22
34
|
join(requested?: string, options?: JoinOptions, control?: OperationControl): Promise<JoinResult>;
|
|
23
|
-
doneOwnedSession(name: string, body: string, expectedEpoch: number, control?: OperationControl): Promise<ActivityResult>;
|
|
24
35
|
implicitJoin(name: string, control?: OperationControl): Promise<{
|
|
25
36
|
readonly state: 'joined' | 'active' | 'done';
|
|
26
37
|
readonly participant?: Participant;
|
|
@@ -31,12 +42,12 @@ export declare class SqliteSquare {
|
|
|
31
42
|
participants(options?: ObservationOptions, control?: OperationControl): Promise<ParticipantsResult>;
|
|
32
43
|
recovery(requested?: string, control?: OperationControl): Promise<RecoveryResult>;
|
|
33
44
|
stream(options: StreamOptions, control?: OperationControl): AsyncIterable<import("./square-facade.js").StreamRecord>;
|
|
34
|
-
recognize(
|
|
45
|
+
recognize(): Promise<readonly Participant[]>;
|
|
35
46
|
close(): Promise<void>;
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
cancelDeliveries(name: string, expectedGeneration: import('./square-core.js').ActivityId): Promise<boolean>;
|
|
48
|
+
endOwnedSession(name: string, sessionId: string, expectedGeneration?: import('./square-core.js').ActivityId, body?: Body): Promise<ActivityResult | null>;
|
|
38
49
|
}
|
|
39
50
|
export declare function diagnoseSquare(input: SquareLocation, control?: OperationControl): Promise<import("./artifact.js").DiagnoseResult>;
|
|
40
51
|
export declare function migrateSquare(input: SquareLocation, control?: OperationControl): Promise<import("./square-migration.js").MigrationOutcome>;
|
|
41
52
|
export declare function discover(input: SquareAtInput, options?: DiscoveryOptions, control?: OperationControl): Promise<import("./square-facade.js").DiscoveryResult>;
|
|
42
|
-
export declare function inbox(input: SquareAtInput,
|
|
53
|
+
export declare function inbox(input: SquareAtInput, identity: string | Body, options?: InboxOptions, control?: OperationControl): Promise<InboxResult>;
|