@astrosheep/square 0.3.85 → 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 +15 -12
- package/dist/cli/mcp-server-command.js +2 -2
- package/dist/cli/observation-commands.js +2 -1
- 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 +12 -14
- package/dist/hints.js +2 -3
- package/dist/inbox.d.ts +35 -9
- package/dist/inbox.js +228 -144
- package/dist/index.d.ts +4 -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 +12 -14
- 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.d.ts +0 -1
- package/dist/presentation.js +18 -13
- 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 +53 -0
- package/dist/sqlite-square.js +602 -0
- package/dist/square-api.d.ts +12 -41
- package/dist/square-api.js +26 -474
- package/dist/square-backend.d.ts +13 -0
- 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 +16 -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-location.d.ts +15 -0
- package/dist/square-location.js +76 -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 +22 -0
- package/dist/square-stdio.js +222 -0
- package/dist/square-tools.js +30 -3
- package/dist/stdio-server.d.ts +3 -0
- package/dist/stdio-server.js +192 -0
- 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/backends.md +180 -0
- 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 +69 -30
- 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 +7 -7
- 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 +58 -20
- package/skills/square/SKILL.md +7 -7
- 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/ledger-maintenance.d.ts +0 -30
- package/dist/ledger-maintenance.js +0 -110
- package/dist/open-square.d.ts +0 -36
- package/dist/open-square.js +0 -81
- package/dist/participant-host.d.ts +0 -22
- 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/{host-ledger.js → square-backend.js} +0 -0
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.d.ts
CHANGED
|
@@ -22,7 +22,6 @@ interface ActivityLimitOptions extends ParticipantOutputOptions {
|
|
|
22
22
|
}
|
|
23
23
|
interface ActivityBlockedOptions extends ParticipantOutputOptions {
|
|
24
24
|
retryCommand: string;
|
|
25
|
-
forceCommand: string;
|
|
26
25
|
activitySummaries: readonly UnreadActivitySummary[];
|
|
27
26
|
draftPath?: string;
|
|
28
27
|
}
|
package/dist/presentation.js
CHANGED
|
@@ -9,6 +9,8 @@ import { formatDuration, formatRelativeTime, formatTimestamp } from './time.js';
|
|
|
9
9
|
import { grepSnippet } from './search.js';
|
|
10
10
|
import { style } from './tty-style.js';
|
|
11
11
|
export function displayPath(squarePath, cwd = process.cwd()) {
|
|
12
|
+
if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(squarePath))
|
|
13
|
+
return squarePath;
|
|
12
14
|
if (!path.isAbsolute(squarePath))
|
|
13
15
|
return squarePath.split(path.sep).join('/');
|
|
14
16
|
const comparableCwd = fs.realpathSync.native(cwd);
|
|
@@ -49,7 +51,7 @@ export function commandPrefix(squarePath) {
|
|
|
49
51
|
return `square --location ${quoteShell(squarePath)}`;
|
|
50
52
|
}
|
|
51
53
|
export function participantCommandPrefix(squarePath, name) {
|
|
52
|
-
return `square --location ${quoteShell(path.resolve(squarePath))} --as ${quoteShell(name)}`;
|
|
54
|
+
return `square --location ${quoteShell(/^[A-Za-z][A-Za-z0-9+.-]*:/.test(squarePath) ? squarePath : path.resolve(squarePath))} --as ${quoteShell(name)}`;
|
|
53
55
|
}
|
|
54
56
|
/** Blocked actions end with a full copy-pasteable recovery command. */
|
|
55
57
|
export function joinRecoveryCommand(squarePath, name) {
|
|
@@ -166,13 +168,16 @@ function renderEventCliFacts(event, opts = {}) {
|
|
|
166
168
|
return { text: `· ${renderRoomChangeText(event)}`, bodyClipped: false };
|
|
167
169
|
case 'say': {
|
|
168
170
|
const body = renderedBody(event.body, maxBody);
|
|
171
|
+
const visibleBody = body.text === '' && event.metadata !== undefined
|
|
172
|
+
? { text: '(structured activity)', clipped: false }
|
|
173
|
+
: body;
|
|
169
174
|
const mention = opts.mention;
|
|
170
175
|
const mentionSuffix = mention !== undefined && (event.reach === 'bell' || event.mentions?.some((name) => sameName(name, mention)))
|
|
171
176
|
? ` · calls your name across the square — ${participantIdentity(mention)}`
|
|
172
177
|
: '';
|
|
173
178
|
const replySuffix = event.reply === undefined ? '' : ` · replies to ${replyId(event.reply)}`;
|
|
174
|
-
const meta = `
|
|
175
|
-
return { text: `● ${participantIdentity(event.actor)}${style('dim', meta)}${bodySuffix(
|
|
179
|
+
const meta = ` · ${eventId(event)} · ${formatRelativeTime(event.at, now)}${mentionSuffix}${replySuffix}`;
|
|
180
|
+
return { text: `● ${participantIdentity(event.actor)}${style('dim', meta)}${bodySuffix(visibleBody.text)}`, bodyClipped: visibleBody.clipped };
|
|
176
181
|
}
|
|
177
182
|
case 'done': {
|
|
178
183
|
const body = renderedBody(event.body, maxBody);
|
|
@@ -190,7 +195,7 @@ export function renderEventCli(event, opts = {}) {
|
|
|
190
195
|
}
|
|
191
196
|
function renderPresenceOnlySay(event, opts = {}) {
|
|
192
197
|
const targets = event.reach === 'bell' ? [] : event.mentions ?? [];
|
|
193
|
-
const meta = style('dim', `
|
|
198
|
+
const meta = style('dim', ` · ${eventId(event)} · ${formatRelativeTime(event.at, opts.now)}`);
|
|
194
199
|
if (targets.length === 0) {
|
|
195
200
|
return `● ${participantIdentity(event.actor)}${meta}`;
|
|
196
201
|
}
|
|
@@ -236,8 +241,10 @@ function renderUnreadSummary(opts) {
|
|
|
236
241
|
if (preview.perception === 'presence') {
|
|
237
242
|
return style('dim', ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · ${rendered.replace(/\n/g, ' ')}`);
|
|
238
243
|
}
|
|
239
|
-
const compact = preview.act.body.replace(/\s+/g, ' ').trim();
|
|
240
|
-
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);
|
|
241
248
|
const bodyText = bodyPreview.remaining === 0 ? bodyPreview.text : `${bodyPreview.text}${style('dim', `… (+${bodyPreview.remaining} chars)`)}`;
|
|
242
249
|
return style('dim', ` · ${participantIdentity(item.name)} spoke — ${formatAge(item.latestActivityAgeMs)} ago · "${bodyText}"`);
|
|
243
250
|
}),
|
|
@@ -251,14 +258,12 @@ export function renderPendingFeed(publicItems, sayNumbers, perceptions, viewer)
|
|
|
251
258
|
export function renderActivityBlocked(opts) {
|
|
252
259
|
const readNowCommand = `${participantCommandPrefix(opts.squarePath, opts.name)} catch`;
|
|
253
260
|
return withPathOutput(opts.squarePath, [
|
|
254
|
-
`${style('blocked', '✕')} your activity
|
|
261
|
+
`${style('blocked', '✕')} your activity hasn't landed — someone spoke while you were away`,
|
|
255
262
|
...renderUnreadSummary({ activitySummaries: opts.activitySummaries, viewer: opts.name }),
|
|
256
263
|
...draftSavedLines(opts.draftPath),
|
|
257
264
|
`${readNowCommand}`,
|
|
258
265
|
' · take it in, then retry:',
|
|
259
266
|
withDraftInput(opts.retryCommand, opts.draftPath),
|
|
260
|
-
' · optional: express over unread activity with --force:',
|
|
261
|
-
withDraftInput(opts.forceCommand, opts.draftPath),
|
|
262
267
|
].join('\n'), { participantCount: opts.participantCount, held: opts.held });
|
|
263
268
|
}
|
|
264
269
|
/** An indeterminate or already-committed send only points to inspection, never resend. */
|
|
@@ -279,14 +284,14 @@ function holdReasonLines(reason) {
|
|
|
279
284
|
return text === '' ? [] : [style('dim', ` · ${text}`)];
|
|
280
285
|
}
|
|
281
286
|
function heldMainLine(holder, styled) {
|
|
282
|
-
const glyph = styled ? style('blocked', '✕') : '
|
|
287
|
+
const glyph = styled ? style('blocked', '✕') : '·';
|
|
283
288
|
const who = holder === undefined ? 'a hand is raised' : `${participantIdentity(holder)} has a hand raised`;
|
|
284
289
|
return `${glyph} ${who} — voices drop, yours too`;
|
|
285
290
|
}
|
|
286
291
|
export function renderExpressWaiting(opts) {
|
|
287
292
|
if (opts.reason === 'throttled') {
|
|
288
293
|
return [
|
|
289
|
-
'
|
|
294
|
+
'· the square is packed — your activity is waiting',
|
|
290
295
|
` · a lull opens in ${formatDuration(opts.delayMs)}`,
|
|
291
296
|
' · --no-wait saves a draft and returns now',
|
|
292
297
|
].join('\n');
|
|
@@ -370,7 +375,7 @@ export function renderActivityLimit(opts) {
|
|
|
370
375
|
const spoken = opts.count !== undefined && opts.hardCap !== undefined ? ` — ${opts.count}/${opts.hardCap} spoken` : '';
|
|
371
376
|
const doneCommand = `${participantCommandPrefix(opts.squarePath, opts.name)} done`;
|
|
372
377
|
return withPathOutput(opts.squarePath, [
|
|
373
|
-
`${style('blocked', '✕')}
|
|
378
|
+
`${style('blocked', '✕')} you’ve reached your speaking cap${spoken}`,
|
|
374
379
|
...draftSavedLines(opts.draftPath),
|
|
375
380
|
' · your draft stays unsent; done only steps out',
|
|
376
381
|
doneCommand,
|
|
@@ -425,7 +430,7 @@ export function renderWatchStatus(opts) {
|
|
|
425
430
|
const spoken = opts.hardCap === undefined || opts.hardCap === null
|
|
426
431
|
? ''
|
|
427
432
|
: ` — ${opts.ownActivityCount ?? 0}/${opts.hardCap} spoken`;
|
|
428
|
-
return [`${style('blocked', '✕')}
|
|
433
|
+
return [`${style('blocked', '✕')} you’ve reached your speaking cap${spoken}`, `${participantCommandPrefix(opts.squarePath, opts.name)} done`].join('\n');
|
|
429
434
|
}
|
|
430
435
|
}
|
|
431
436
|
}
|
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();
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Body } from './model.js';
|
|
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';
|
|
3
|
+
export type * from './square-facade.js';
|
|
4
|
+
export { createSquareResourceOwner } from './artifact.js';
|
|
5
|
+
export { formatActivityId, parseActivityId, MAX_IDENTITY_SET_SIZE } from './square-core.js';
|
|
6
|
+
export { CATCH_DEFAULT_LIMIT, CATCH_MAX_LIMIT } from './catch-decisions.js';
|
|
7
|
+
export { HELD_WAIT_BUDGET_MS } from './runtime.js';
|
|
8
|
+
export { HISTORY_DEFAULT_LIMIT, HISTORY_MAX_LIMIT } from './views.js';
|
|
9
|
+
export { DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from './list.js';
|
|
10
|
+
export declare function squareClock(env: NodeJS.ProcessEnv, clock?: () => number): () => number;
|
|
11
|
+
/** One concrete local resource, including its entry-resolved dependencies and lifetime. */
|
|
12
|
+
export declare class SqliteSquare {
|
|
13
|
+
readonly location: string;
|
|
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;
|
|
22
|
+
private readonly defaultParticipant?;
|
|
23
|
+
private constructor();
|
|
24
|
+
static at(input: SquareAtInput, control?: OperationControl, automaticDelivery?: boolean): Promise<SqliteSquare>;
|
|
25
|
+
static build(input: SquareBuildInput, control?: OperationControl, automaticDelivery?: boolean): Promise<SqliteSquare>;
|
|
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;
|
|
31
|
+
private handle;
|
|
32
|
+
private static readonly ParticipantHandle;
|
|
33
|
+
participant(requested?: string): Promise<Participant>;
|
|
34
|
+
join(requested?: string, options?: JoinOptions, control?: OperationControl): Promise<JoinResult>;
|
|
35
|
+
implicitJoin(name: string, control?: OperationControl): Promise<{
|
|
36
|
+
readonly state: 'joined' | 'active' | 'done';
|
|
37
|
+
readonly participant?: Participant;
|
|
38
|
+
}>;
|
|
39
|
+
snapshot(control?: OperationControl): Promise<import("./square-facade.js").SquareSnapshot>;
|
|
40
|
+
history(query?: HistoryQuery, control?: OperationControl): Promise<HistoryResult>;
|
|
41
|
+
status(options?: StatusOptions, control?: OperationControl): Promise<StatusResult>;
|
|
42
|
+
participants(options?: ObservationOptions, control?: OperationControl): Promise<ParticipantsResult>;
|
|
43
|
+
recovery(requested?: string, control?: OperationControl): Promise<RecoveryResult>;
|
|
44
|
+
stream(options: StreamOptions, control?: OperationControl): AsyncIterable<import("./square-facade.js").StreamRecord>;
|
|
45
|
+
recognize(): Promise<readonly Participant[]>;
|
|
46
|
+
close(): Promise<void>;
|
|
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>;
|
|
49
|
+
}
|
|
50
|
+
export declare function diagnoseSquare(input: SquareLocation, control?: OperationControl): Promise<import("./artifact.js").DiagnoseResult>;
|
|
51
|
+
export declare function migrateSquare(input: SquareLocation, control?: OperationControl): Promise<import("./square-migration.js").MigrationOutcome>;
|
|
52
|
+
export declare function discover(input: SquareAtInput, options?: DiscoveryOptions, control?: OperationControl): Promise<import("./square-facade.js").DiscoveryResult>;
|
|
53
|
+
export declare function inbox(input: SquareAtInput, identity: string | Body, options?: InboxOptions, control?: OperationControl): Promise<InboxResult>;
|