@astrosheep/square 0.3.63 → 0.3.65
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/claude-plugin/.claude-plugin/plugin.json +3 -2
- package/claude-plugin/hooks/hooks.json +1 -22
- package/claude-plugin/hooks/register.js +119 -0
- package/claude-plugin/types.d.ts +7 -0
- package/codex-plugin/.codex-plugin/plugin.json +1 -1
- package/dist/activity-feed.d.ts +1 -10
- package/dist/activity-feed.js +1 -27
- package/dist/activity.js +57 -25
- package/dist/artifact.d.ts +1 -1
- package/dist/artifact.js +24 -25
- package/dist/attention-presentation.js +1 -0
- package/dist/automatic-session.d.ts +3 -1
- package/dist/automatic-session.js +21 -34
- package/dist/boundary-presentation.js +4 -13
- package/dist/canonical-path.d.ts +11 -0
- package/dist/canonical-path.js +47 -0
- package/dist/catch-decisions.js +14 -10
- package/dist/catch-selection.d.ts +6 -0
- package/dist/catch-selection.js +14 -0
- package/dist/claude-delivery.d.ts +11 -0
- package/dist/claude-delivery.js +145 -0
- package/dist/claude-mod.d.ts +8 -0
- package/dist/claude-mod.js +164 -0
- package/dist/cli/context.d.ts +0 -1
- package/dist/cli/context.js +9 -21
- package/dist/cli/harness-command.d.ts +0 -1
- package/dist/cli/harness-command.js +0 -6
- package/dist/cli/observation-commands.d.ts +1 -1
- package/dist/cli/observation-commands.js +31 -58
- package/dist/cli/program.js +20 -18
- package/dist/cli/registry.js +2 -2
- package/dist/cli/square-commands.js +10 -6
- package/dist/codex-boundary-state.d.ts +2 -2
- package/dist/codex-boundary-state.js +4 -4
- package/dist/codex-hook.js +12 -15
- package/dist/codex-queue.d.ts +2 -2
- package/dist/codex-queue.js +7 -5
- package/dist/decisions.d.ts +2 -6
- package/dist/decisions.js +16 -17
- package/dist/delivery-health.d.ts +1 -2
- package/dist/delivery-health.js +4 -5
- package/dist/delivery-operations.d.ts +2 -5
- package/dist/delivery-operations.js +77 -214
- package/dist/delivery.d.ts +25 -47
- package/dist/delivery.js +14 -46
- package/dist/diagnostic-redaction.js +7 -9
- package/dist/file-changes.d.ts +0 -1
- package/dist/file-changes.js +2 -19
- package/dist/harness-claude.js +8 -6
- package/dist/harness-codex.d.ts +0 -2
- package/dist/harness-codex.js +0 -2
- package/dist/harness-links.js +13 -17
- package/dist/harness.js +3 -7
- package/dist/help.js +4 -3
- package/dist/host-ledger-file-adapter.d.ts +17 -8
- package/dist/host-ledger-file-adapter.js +164 -80
- package/dist/host-ledger.d.ts +55 -50
- package/dist/inbox.d.ts +0 -3
- package/dist/inbox.js +88 -50
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list.js +1 -4
- package/dist/mcp.js +24 -41
- package/dist/mention-parse.js +3 -9
- package/dist/model.d.ts +11 -2
- package/dist/model.js +1 -1
- package/dist/notifications.d.ts +3 -16
- package/dist/notifications.js +41 -122
- package/dist/opencode-delivery.d.ts +14 -0
- package/dist/opencode-delivery.js +171 -0
- package/dist/opencode.d.ts +39 -18
- package/dist/opencode.js +145 -40
- package/dist/packages/agent-delivery/src/claude-auth.d.ts +13 -0
- package/dist/packages/agent-delivery/src/claude-auth.js +75 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +28 -0
- package/dist/packages/agent-delivery/src/claude-native.js +68 -0
- package/dist/packages/agent-delivery/src/claude.d.ts +14 -0
- package/dist/packages/agent-delivery/src/claude.js +66 -0
- package/dist/packages/agent-delivery/src/deadline.d.ts +11 -0
- package/dist/packages/agent-delivery/src/deadline.js +41 -0
- package/dist/packages/agent-delivery/src/errors.d.ts +16 -0
- package/dist/packages/agent-delivery/src/errors.js +41 -0
- package/dist/packages/agent-delivery/src/guards.d.ts +6 -0
- package/dist/packages/agent-delivery/src/guards.js +6 -0
- package/dist/packages/agent-delivery/src/index.d.ts +83 -0
- package/dist/packages/agent-delivery/src/index.js +110 -0
- package/dist/packages/agent-delivery/src/local-endpoint.d.ts +26 -0
- package/dist/packages/agent-delivery/src/local-endpoint.js +114 -0
- package/dist/packages/agent-delivery/src/opencode-native.d.ts +33 -0
- package/dist/packages/agent-delivery/src/opencode-native.js +75 -0
- package/dist/packages/agent-delivery/src/opencode.d.ts +14 -0
- package/dist/packages/agent-delivery/src/opencode.js +127 -0
- package/dist/packages/agent-delivery/src/paseo-native.d.ts +58 -0
- package/dist/packages/agent-delivery/src/paseo-native.js +122 -0
- package/dist/packages/agent-delivery/src/paseo.d.ts +19 -0
- package/dist/packages/agent-delivery/src/paseo.js +106 -0
- package/dist/packages/agent-delivery/src/pi-client.d.ts +10 -0
- package/dist/packages/agent-delivery/src/pi-client.js +106 -0
- package/dist/packages/agent-delivery/src/pi-protocol.d.ts +15 -0
- package/dist/packages/agent-delivery/src/pi-protocol.js +50 -0
- package/dist/packages/agent-delivery/src/pi-send.d.ts +23 -0
- package/dist/packages/agent-delivery/src/pi-send.js +4 -0
- package/dist/packages/agent-delivery/src/pi.d.ts +25 -0
- package/dist/packages/agent-delivery/src/pi.js +209 -0
- package/dist/packages/agent-delivery/src/types.d.ts +120 -0
- package/dist/participant-host.js +12 -13
- package/dist/participant-identity.d.ts +21 -0
- package/dist/participant-identity.js +32 -11
- package/dist/paseo-delivery.d.ts +2 -2
- package/dist/paseo-delivery.js +35 -27
- package/dist/paseo-timeline.js +25 -21
- package/dist/paseo.d.ts +2 -2
- package/dist/paseo.js +1 -2
- package/dist/ports.d.ts +7 -20
- package/dist/presentation-operations.js +16 -4
- package/dist/presentation.d.ts +19 -10
- package/dist/presentation.js +99 -177
- package/dist/registry.d.ts +15 -66
- package/dist/registry.js +45 -123
- package/dist/routes.d.ts +1 -28
- package/dist/routes.js +22 -104
- package/dist/runtime.d.ts +3 -9
- package/dist/runtime.js +14 -67
- package/dist/search.d.ts +0 -2
- package/dist/search.js +3 -10
- package/dist/square-actions.js +48 -72
- package/dist/square-application.js +32 -30
- package/dist/square-core.d.ts +3 -0
- package/dist/square-core.js +22 -5
- package/dist/square-facade.d.ts +2 -0
- package/dist/square-file-adapter.d.ts +2 -1
- package/dist/square-file-adapter.js +6 -9
- package/dist/square-projections.d.ts +40 -45
- package/dist/square-projections.js +60 -74
- package/dist/square-state.js +1 -4
- package/dist/square-storage.d.ts +5 -8
- package/dist/square-storage.js +13 -36
- package/dist/square-wiring.d.ts +2 -7
- package/dist/square-wiring.js +6 -24
- package/dist/stream.js +4 -6
- package/dist/time.js +2 -7
- package/dist/views.d.ts +6 -18
- package/dist/views.js +14 -21
- package/dist/wake-attempts.d.ts +2 -56
- package/dist/wake-attempts.js +15 -110
- package/dist/wake-eligibility.d.ts +43 -0
- package/dist/wake-eligibility.js +106 -0
- package/dist/wake-evidence.d.ts +32 -9
- package/dist/wake-evidence.js +29 -24
- package/dist/wake-sink.d.ts +13 -9
- package/dist/wake-sink.js +35 -54
- package/dist/watch.js +13 -21
- package/extensions/square-pi.js +27 -13
- package/guides/change-waits.md +1 -1
- package/guides/claude-native.md +101 -0
- package/guides/host-ledger.md +3 -1
- package/guides/participant.md +14 -7
- package/guides/pi-native-delivery.md +49 -0
- package/package.json +4 -3
- package/dist/claude-hook.d.ts +0 -10
- package/dist/claude-hook.js +0 -60
- package/dist/paseo-connection.d.ts +0 -15
- package/dist/paseo-connection.js +0 -141
- package/dist/presence.d.ts +0 -2
- package/dist/presence.js +0 -24
- package/dist/presented.d.ts +0 -2
- package/dist/presented.js +0 -10
- package/dist/state-cell.d.ts +0 -14
- package/dist/wake-port.d.ts +0 -23
- package/dist/wake-port.js +0 -27
- /package/dist/{state-cell.js → packages/agent-delivery/src/types.js} +0 -0
package/dist/square-storage.js
CHANGED
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
1
|
import { isDeepStrictEqual } from 'node:util';
|
|
4
|
-
import { createSquareFile, createSquareState, diagnoseSquareFile as diagnoseArtifactFile,
|
|
2
|
+
import { createSquareFile, createSquareState, diagnoseSquareFile as diagnoseArtifactFile, probeSquare, observeSquareRevision, readSquareSnapshot, transactSquareSnapshot, writeSquareFile, } from './artifact.js';
|
|
3
|
+
import { canonicalPath } from './canonical-path.js';
|
|
5
4
|
export { createSquareState };
|
|
6
|
-
async function canonicalPath(squarePath) {
|
|
7
|
-
const absolute = path.resolve(squarePath);
|
|
8
|
-
try {
|
|
9
|
-
return await fs.promises.realpath(absolute);
|
|
10
|
-
}
|
|
11
|
-
catch (error) {
|
|
12
|
-
if (error.code !== 'ENOENT')
|
|
13
|
-
throw error;
|
|
14
|
-
const parent = await fs.promises.realpath(path.dirname(absolute)).catch(() => path.dirname(absolute));
|
|
15
|
-
return path.join(parent, path.basename(absolute));
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export async function readSquareFile(squarePath) {
|
|
19
|
-
return loadSquare(await canonicalPath(squarePath));
|
|
20
|
-
}
|
|
21
5
|
export async function probeSquareFile(squarePath) {
|
|
22
6
|
if (!squarePath.endsWith('.square'))
|
|
23
7
|
return undefined;
|
|
@@ -39,7 +23,7 @@ function cloneState(squareState) {
|
|
|
39
23
|
}
|
|
40
24
|
function assertCellOpen(closed) {
|
|
41
25
|
if (closed)
|
|
42
|
-
throw new Error('
|
|
26
|
+
throw new Error('Square artifact is closed');
|
|
43
27
|
}
|
|
44
28
|
function isThenable(value) {
|
|
45
29
|
return (typeof value === 'object' || typeof value === 'function') && value !== null
|
|
@@ -48,7 +32,7 @@ function isThenable(value) {
|
|
|
48
32
|
function discardThenable(value) {
|
|
49
33
|
void Promise.resolve(value).catch(() => undefined);
|
|
50
34
|
}
|
|
51
|
-
/** In-process
|
|
35
|
+
/** In-process artifact for fast behavior tests and embedded consumers. */
|
|
52
36
|
export function createMemoryCell(initial) {
|
|
53
37
|
let state = cloneState(initial);
|
|
54
38
|
let version = 0;
|
|
@@ -69,11 +53,11 @@ export function createMemoryCell(initial) {
|
|
|
69
53
|
transact(fn, signal) {
|
|
70
54
|
assertCellOpen(closed);
|
|
71
55
|
if (signal?.aborted)
|
|
72
|
-
throw signal.reason ?? new Error('
|
|
56
|
+
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
73
57
|
const operation = tail.then(() => {
|
|
74
58
|
assertCellOpen(closed);
|
|
75
59
|
if (signal?.aborted)
|
|
76
|
-
throw signal.reason ?? new Error('
|
|
60
|
+
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
77
61
|
const current = cloneState(state);
|
|
78
62
|
const outcome = fn(current, version);
|
|
79
63
|
if (isThenable(outcome)) {
|
|
@@ -96,27 +80,27 @@ export function createMemoryCell(initial) {
|
|
|
96
80
|
async read(signal) {
|
|
97
81
|
assertCellOpen(closed);
|
|
98
82
|
if (signal?.aborted)
|
|
99
|
-
throw signal.reason ?? new Error('
|
|
83
|
+
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
100
84
|
await tail;
|
|
101
85
|
assertCellOpen(closed);
|
|
102
86
|
if (signal?.aborted)
|
|
103
|
-
throw signal.reason ?? new Error('
|
|
87
|
+
throw signal.reason ?? new Error('Square artifact operation aborted');
|
|
104
88
|
return { state: cloneState(state), version };
|
|
105
89
|
},
|
|
106
90
|
changed(sinceVersion, timeoutMs, signal) {
|
|
107
91
|
assertCellOpen(closed);
|
|
108
92
|
if (signal?.aborted)
|
|
109
|
-
return Promise.reject(signal.reason ?? new Error("
|
|
93
|
+
return Promise.reject(signal.reason ?? new Error("Square artifact operation aborted"));
|
|
110
94
|
if (version > sinceVersion)
|
|
111
95
|
return Promise.resolve(true);
|
|
112
96
|
if (timeoutMs <= 0)
|
|
113
97
|
return Promise.resolve(false);
|
|
114
98
|
return new Promise((resolve, reject) => {
|
|
115
99
|
if (signal?.aborted) {
|
|
116
|
-
reject(signal.reason ?? new Error("
|
|
100
|
+
reject(signal.reason ?? new Error("Square artifact operation aborted"));
|
|
117
101
|
return;
|
|
118
102
|
}
|
|
119
|
-
const abort = () => { waiters.delete(waiter); clearTimeout(waiter.timer); signal?.removeEventListener("abort", abort); reject(signal?.reason ?? new Error("
|
|
103
|
+
const abort = () => { waiters.delete(waiter); clearTimeout(waiter.timer); signal?.removeEventListener("abort", abort); reject(signal?.reason ?? new Error("Square artifact operation aborted")); };
|
|
120
104
|
const waiter = {
|
|
121
105
|
since: sinceVersion,
|
|
122
106
|
resolve,
|
|
@@ -142,7 +126,7 @@ export function createMemoryCell(initial) {
|
|
|
142
126
|
},
|
|
143
127
|
};
|
|
144
128
|
}
|
|
145
|
-
/** SQLite-backed
|
|
129
|
+
/** SQLite-backed artifact. Revisions are read from the authoritative database, not file metadata. */
|
|
146
130
|
export function createFileCell(squarePath, externalSignal) {
|
|
147
131
|
let closed = false;
|
|
148
132
|
let storage;
|
|
@@ -152,9 +136,6 @@ export function createFileCell(squarePath, externalSignal) {
|
|
|
152
136
|
storage ??= canonicalPath(squarePath);
|
|
153
137
|
return storage;
|
|
154
138
|
}
|
|
155
|
-
function abortError() {
|
|
156
|
-
return new Error('StateCell is closed');
|
|
157
|
-
}
|
|
158
139
|
/** Close wins over every operation; a per-call deadline bounds busy retries alongside it. */
|
|
159
140
|
function operationSignal(signal) {
|
|
160
141
|
if (signal === undefined && externalSignal === undefined)
|
|
@@ -200,12 +181,8 @@ export function createFileCell(squarePath, externalSignal) {
|
|
|
200
181
|
if (closed)
|
|
201
182
|
return;
|
|
202
183
|
closed = true;
|
|
203
|
-
cancel.abort(
|
|
184
|
+
cancel.abort(new Error('Square artifact is closed'));
|
|
204
185
|
await tail;
|
|
205
186
|
},
|
|
206
187
|
};
|
|
207
188
|
}
|
|
208
|
-
/** Consumer-facing file cell factory; keeps SQLite framing behind this module. */
|
|
209
|
-
export function openSquareCell(squarePath, signal) {
|
|
210
|
-
return createFileCell(squarePath, signal);
|
|
211
|
-
}
|
package/dist/square-wiring.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export declare class Square {
|
|
|
5
5
|
private readonly square;
|
|
6
6
|
private readonly context;
|
|
7
7
|
private constructor();
|
|
8
|
-
static at(input: SquareAtInput): Promise<Square>;
|
|
8
|
+
static at(input: SquareAtInput, control?: OperationControl): Promise<Square>;
|
|
9
9
|
static build(input: SquareBuildInput): Promise<Square>;
|
|
10
10
|
static inMemory(input: Omit<SquareBuildInput, 'path'>): Square;
|
|
11
11
|
join(name: string, control?: OperationControl): Promise<Participant>;
|
|
@@ -24,15 +24,10 @@ export declare class Square {
|
|
|
24
24
|
readonly participant?: Participant;
|
|
25
25
|
}>;
|
|
26
26
|
participants(): Promise<ParticipantStatus[]>;
|
|
27
|
-
snapshot(): Promise<SquareSnapshot>;
|
|
27
|
+
snapshot(control?: OperationControl): Promise<SquareSnapshot>;
|
|
28
28
|
history(query?: HistoryQuery, control?: OperationControl): Promise<Activity[]>;
|
|
29
29
|
recognize(env: NodeJS.ProcessEnv): Promise<Participant | null>;
|
|
30
30
|
close(): Promise<void>;
|
|
31
31
|
endOwnedSession(name: string, sessionId: string, expectedEpoch?: number): Promise<ExpressResult | null>;
|
|
32
32
|
retireEndedSessionRoutes(sessionId: string): Promise<void>;
|
|
33
33
|
}
|
|
34
|
-
export declare function markBoundarySeen(squarePath: string, name: string, actIndexes: readonly number[], at?: number): Promise<void>;
|
|
35
|
-
export declare function openParticipant(input: SquareAtInput, name: string): Promise<{
|
|
36
|
-
readonly participant: Participant;
|
|
37
|
-
close(): Promise<void>;
|
|
38
|
-
}>;
|
package/dist/square-wiring.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { closeOpenSquare } from './open-square.js';
|
|
2
2
|
import { buildMemorySquare, buildSquare, openSquare } from './square-file-adapter.js';
|
|
3
3
|
import { catchUp, done, endOwnedSession, express, hold, ignore, implicitJoin, join, takeover, listen, listening, resume, retireEndedSessionRoutes, } from './square-actions.js';
|
|
4
|
-
import {
|
|
5
|
-
import { history, participantHistory, participants, resolveParticipant, snapshot } from './views.js';
|
|
4
|
+
import { history, participantHistory, participants, snapshot } from './views.js';
|
|
6
5
|
import { currentParticipant } from './views.js';
|
|
7
6
|
import { projectSessionBindings } from './square-projections.js';
|
|
7
|
+
import { sessionIdsFromEnvironment } from './participant-identity.js';
|
|
8
8
|
class ParticipantHandle {
|
|
9
9
|
name;
|
|
10
10
|
square;
|
|
@@ -51,8 +51,8 @@ export class Square {
|
|
|
51
51
|
this.square = square;
|
|
52
52
|
this.context = { ...square, location };
|
|
53
53
|
}
|
|
54
|
-
static async at(input) {
|
|
55
|
-
return new Square(input.path, await openSquare(input.path, input));
|
|
54
|
+
static async at(input, control) {
|
|
55
|
+
return new Square(input.path, await openSquare(input.path, input, control));
|
|
56
56
|
}
|
|
57
57
|
static async build(input) {
|
|
58
58
|
return new Square(input.path, await buildSquare(input.path, input));
|
|
@@ -85,13 +85,12 @@ export class Square {
|
|
|
85
85
|
: { state: joined.state, participant: new ParticipantHandle(joined.name, this.square, this.context) };
|
|
86
86
|
}
|
|
87
87
|
participants() { return participants(this.square); }
|
|
88
|
-
snapshot() { return snapshot(this.square); }
|
|
88
|
+
snapshot(control) { return snapshot(this.square, control); }
|
|
89
89
|
history(query, control) { return history(this.square, query, control); }
|
|
90
90
|
async recognize(env) {
|
|
91
91
|
if (this.square.hostLedger === undefined)
|
|
92
92
|
return null;
|
|
93
|
-
const sessions =
|
|
94
|
-
.map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
93
|
+
const sessions = sessionIdsFromEnvironment(env);
|
|
95
94
|
if (sessions.length === 0)
|
|
96
95
|
return null;
|
|
97
96
|
const candidates = (await Promise.all(sessions.map((sessionId) => projectSessionBindings({
|
|
@@ -108,20 +107,3 @@ export class Square {
|
|
|
108
107
|
endOwnedSession(name, sessionId, expectedEpoch) { return endOwnedSession(this.context, name, sessionId, expectedEpoch); }
|
|
109
108
|
retireEndedSessionRoutes(sessionId) { return retireEndedSessionRoutes(this.context, sessionId); }
|
|
110
109
|
}
|
|
111
|
-
export function markBoundarySeen(squarePath, name, actIndexes, at) {
|
|
112
|
-
return recordBoundarySeen(squarePath, name, actIndexes, at);
|
|
113
|
-
}
|
|
114
|
-
export async function openParticipant(input, name) {
|
|
115
|
-
const square = await openSquare(input.path, input);
|
|
116
|
-
try {
|
|
117
|
-
const known = await resolveParticipant(square, name);
|
|
118
|
-
return {
|
|
119
|
-
participant: new ParticipantHandle(known.name, square, square),
|
|
120
|
-
close: () => closeOpenSquare(square),
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
catch (error) {
|
|
124
|
-
await closeOpenSquare(square);
|
|
125
|
-
throw error;
|
|
126
|
-
}
|
|
127
|
-
}
|
package/dist/stream.js
CHANGED
|
@@ -5,10 +5,6 @@ import { quoteShell } from './presentation.js';
|
|
|
5
5
|
import { openSquare, observeSquareChanges } from './square-file-adapter.js';
|
|
6
6
|
import { closeOpenSquare } from './open-square.js';
|
|
7
7
|
import { streamProjection, streamTailProjection } from './views.js';
|
|
8
|
-
async function writeNdjson(value) {
|
|
9
|
-
if (!process.stdout.write(`${JSON.stringify(value)}\n`))
|
|
10
|
-
await once(process.stdout, 'drain');
|
|
11
|
-
}
|
|
12
8
|
/** Machine-readable tailing stays available; interactive terminal rendering was retired. */
|
|
13
9
|
export async function cmdStreamNdjson(squarePath, recipient, start = { kind: 'tail', last: 10 }) {
|
|
14
10
|
if (!fs.existsSync(squarePath)) {
|
|
@@ -31,8 +27,10 @@ export async function cmdStreamNdjson(squarePath, recipient, start = { kind: 'ta
|
|
|
31
27
|
? await streamProjection(square, cursor, recipient)
|
|
32
28
|
: await streamTailProjection(square, initial.last, recipient);
|
|
33
29
|
for (const item of projection.activities) {
|
|
34
|
-
|
|
35
|
-
...(item.route === undefined ? {} : { route: item.route }) }
|
|
30
|
+
const line = { seq: item.activity.index, square: squarePath, ...item.activity,
|
|
31
|
+
...(item.route === undefined ? {} : { route: item.route }) };
|
|
32
|
+
if (!process.stdout.write(`${JSON.stringify(line)}\n`))
|
|
33
|
+
await once(process.stdout, 'drain');
|
|
36
34
|
}
|
|
37
35
|
cursor = projection.cursor;
|
|
38
36
|
initial = undefined;
|
package/dist/time.js
CHANGED
|
@@ -1,12 +1,6 @@
|
|
|
1
1
|
function pad(value, length = 2) {
|
|
2
2
|
return String(value).padStart(length, '0');
|
|
3
3
|
}
|
|
4
|
-
function formatOffset(date) {
|
|
5
|
-
const offsetMinutes = -date.getTimezoneOffset();
|
|
6
|
-
const sign = offsetMinutes >= 0 ? '+' : '-';
|
|
7
|
-
const abs = Math.abs(offsetMinutes);
|
|
8
|
-
return `${sign}${pad(Math.floor(abs / 60))}:${pad(abs % 60)}`;
|
|
9
|
-
}
|
|
10
4
|
export function formatTimestamp(at) {
|
|
11
5
|
const date = new Date(at);
|
|
12
6
|
const time = date.getMilliseconds() !== 0
|
|
@@ -14,10 +8,11 @@ export function formatTimestamp(at) {
|
|
|
14
8
|
: date.getSeconds() !== 0
|
|
15
9
|
? `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
|
|
16
10
|
: `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
11
|
+
const offsetMinutes = -date.getTimezoneOffset();
|
|
17
12
|
return [
|
|
18
13
|
`${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`,
|
|
19
14
|
time,
|
|
20
|
-
|
|
15
|
+
`${offsetMinutes >= 0 ? '+' : '-'}${pad(Math.floor(Math.abs(offsetMinutes) / 60))}:${pad(Math.abs(offsetMinutes) % 60)}`,
|
|
21
16
|
].join(' ');
|
|
22
17
|
}
|
|
23
18
|
export function formatDuration(ms) {
|
package/dist/views.d.ts
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { type ActivityId } from './square-core.js';
|
|
2
2
|
import { coreParticipants, coreStatus } from './decisions.js';
|
|
3
3
|
import { type DeliveryModel, type PlannedNotification } from './delivery.js';
|
|
4
|
-
import { type ActivitiesOptions, type
|
|
4
|
+
import { type ActivitiesOptions, type PublicAct, type SquareState, type StoredAct } from './model.js';
|
|
5
5
|
import type { OpenSquare } from './open-square.js';
|
|
6
6
|
import type { Activity, HistoryQuery, OperationControl, ParticipantStatus, SquareSnapshot } from './square-facade.js';
|
|
7
7
|
export interface ActivityPresentation {
|
|
8
8
|
readonly name: string;
|
|
9
9
|
readonly roster: readonly string[];
|
|
10
10
|
readonly pendingPublic: readonly PublicAct[];
|
|
11
|
-
readonly pendingRoomChanges: readonly RoomChangeAct[];
|
|
12
11
|
readonly activities: readonly StoredAct[];
|
|
13
12
|
readonly state: SquareState;
|
|
14
13
|
readonly participantCount: number;
|
|
@@ -53,12 +52,6 @@ export interface WatchPresentation {
|
|
|
53
52
|
};
|
|
54
53
|
readonly terminalStatus?: 'capped' | 'quorum';
|
|
55
54
|
}
|
|
56
|
-
export interface InboxProjection {
|
|
57
|
-
readonly name: string;
|
|
58
|
-
readonly joined: boolean;
|
|
59
|
-
readonly notifications: readonly InboxNotification[];
|
|
60
|
-
readonly catchLease?: import('./model.js').WatchLease;
|
|
61
|
-
}
|
|
62
55
|
export interface StreamProjection {
|
|
63
56
|
readonly activities: readonly {
|
|
64
57
|
readonly activity: StoredAct;
|
|
@@ -71,15 +64,18 @@ export interface PendingDeliveryProjection {
|
|
|
71
64
|
readonly recipient: string;
|
|
72
65
|
readonly notifications: readonly PlannedNotification[];
|
|
73
66
|
}
|
|
67
|
+
/** The one StoredAct-to-public-Activity field mapping. Rejects read and actorless acts. */
|
|
68
|
+
export declare function toPublicActivity(stored: StoredAct): Activity;
|
|
69
|
+
export declare function parseRequiredActivityId(id: ActivityId): number;
|
|
74
70
|
export declare function history(square: OpenSquare, query?: HistoryQuery, control?: OperationControl): Promise<Activity[]>;
|
|
75
71
|
export declare function participantHistory(square: OpenSquare, _name: string, query?: HistoryQuery, control?: OperationControl): Promise<Activity[]>;
|
|
76
|
-
export declare function resolveParticipant(square: OpenSquare, name: string): Promise<{
|
|
72
|
+
export declare function resolveParticipant(square: OpenSquare, name: string, control?: OperationControl): Promise<{
|
|
77
73
|
readonly name: string;
|
|
78
74
|
readonly roster: readonly string[];
|
|
79
75
|
}>;
|
|
80
76
|
export declare function currentParticipant(square: OpenSquare, name: string): Promise<string | undefined>;
|
|
81
77
|
export declare function participants(square: OpenSquare): Promise<ParticipantStatus[]>;
|
|
82
|
-
export declare function snapshot(square: OpenSquare): Promise<SquareSnapshot>;
|
|
78
|
+
export declare function snapshot(square: OpenSquare, control?: OperationControl): Promise<SquareSnapshot>;
|
|
83
79
|
export declare function activityPresentation(square: OpenSquare, name: string): Promise<ActivityPresentation>;
|
|
84
80
|
export declare function entryPresentation(square: OpenSquare, name: string, lastN?: number | null): Promise<EntryPresentation>;
|
|
85
81
|
export declare function historyPresentation(square: OpenSquare, options: ActivitiesOptions): Promise<HistoryPresentation>;
|
|
@@ -92,7 +88,6 @@ export declare function eventPresentation(square: OpenSquare, id: ActivityId): P
|
|
|
92
88
|
readonly held: boolean;
|
|
93
89
|
}>;
|
|
94
90
|
export declare function watchPresentation(square: OpenSquare, name: string): Promise<WatchPresentation>;
|
|
95
|
-
export declare function inboxProjection(square: OpenSquare, name: string, _sessionId?: string): Promise<InboxProjection>;
|
|
96
91
|
export declare function streamProjection(square: OpenSquare | {
|
|
97
92
|
readonly cell: {
|
|
98
93
|
read(): Promise<{
|
|
@@ -107,11 +102,4 @@ export declare function streamTailProjection(square: OpenSquare | {
|
|
|
107
102
|
}>;
|
|
108
103
|
};
|
|
109
104
|
}, last?: number, recipient?: string): Promise<StreamProjection>;
|
|
110
|
-
export declare function notificationForAct(square: OpenSquare, actIndex: number): Promise<readonly PlannedNotification[]>;
|
|
111
105
|
export declare function pendingDeliveriesFromState(state: SquareState, delivery?: DeliveryModel): readonly PendingDeliveryProjection[];
|
|
112
|
-
export declare function pendingDeliveries(square: OpenSquare): Promise<readonly PendingDeliveryProjection[]>;
|
|
113
|
-
export declare function notificationEvidence(square: OpenSquare, recipient: string, actIndex: number): Promise<{
|
|
114
|
-
readonly delivered: boolean;
|
|
115
|
-
readonly observation: ActivityObservation | undefined;
|
|
116
|
-
}>;
|
|
117
|
-
export declare function notificationDelivered(square: OpenSquare, recipient: string, actIndex: number): Promise<boolean>;
|
package/dist/views.js
CHANGED
|
@@ -3,13 +3,14 @@ import { deliveryDelta, directedPeerSays } from './activity-feed.js';
|
|
|
3
3
|
import { coreActivities, coreParticipants, coreStatus, resolveKnownName } from './decisions.js';
|
|
4
4
|
import { deriveDeliveryModel, isActivitySeen } from './delivery.js';
|
|
5
5
|
import { SquareError, nameKey } from './model.js';
|
|
6
|
-
import { countSays, currentHold, foldedState,
|
|
7
|
-
|
|
6
|
+
import { countSays, currentHold, foldedState, inSquareCount, isCurrentlyJoined, presenceAnchor, resolveRosterName, rosterNames, watchTerminalStatus } from './runtime.js';
|
|
7
|
+
/** The one StoredAct-to-public-Activity field mapping. Rejects read and actorless acts. */
|
|
8
|
+
export function toPublicActivity(stored) {
|
|
8
9
|
if (stored.kind === 'read' || stored.actor === undefined)
|
|
9
10
|
throw new Error(`Cannot expose stored activity ${formatActivityId(stored.index)}`);
|
|
10
11
|
return { id: formatActivityId(stored.index), at: stored.at, kind: stored.kind, actor: stored.actor, ...(stored.kind === 'say' && stored.reach !== undefined ? { reach: stored.reach } : {}), ...('body' in stored && stored.body !== undefined ? { body: stored.body } : {}), mentions: stored.kind === 'say' ? stored.mentions ?? [] : [], ...('target' in stored ? { target: stored.target } : {}), ...(stored.kind === 'say' && stored.reply !== undefined ? { reply: formatActivityId(stored.reply) } : {}) };
|
|
11
12
|
}
|
|
12
|
-
function parseRequiredActivityId(id) {
|
|
13
|
+
export function parseRequiredActivityId(id) {
|
|
13
14
|
const index = parseActivityId(id);
|
|
14
15
|
if (index === undefined)
|
|
15
16
|
throw new SquareError('invalid_args', `Invalid activity id: ${id}`);
|
|
@@ -59,13 +60,13 @@ function sayNumbers(state) {
|
|
|
59
60
|
}
|
|
60
61
|
return result;
|
|
61
62
|
}
|
|
62
|
-
export async function history(square, query = {}, control) { const { state } = await square.artifact.read(control?.signal); return selectHistory(coreActivities(state, historyOptions(query)), query).map(
|
|
63
|
-
export async function participantHistory(square, _name, query = {}, control) { const { state } = await square.artifact.read(control?.signal); const effective = query.limit !== undefined ? query : { ...query, limit: 10 }; return selectHistory(coreActivities(state, historyOptions(effective)), effective).map(
|
|
64
|
-
export async function resolveParticipant(square, name) { const { state } = await square.artifact.read(); return { name: resolveKnownName(state, name), roster: rosterNames(state) }; }
|
|
63
|
+
export async function history(square, query = {}, control) { const { state } = await square.artifact.read(control?.signal); return selectHistory(coreActivities(state, historyOptions(query)), query).map(toPublicActivity); }
|
|
64
|
+
export async function participantHistory(square, _name, query = {}, control) { const { state } = await square.artifact.read(control?.signal); const effective = query.limit !== undefined ? query : { ...query, limit: 10 }; return selectHistory(coreActivities(state, historyOptions(effective)), effective).map(toPublicActivity); }
|
|
65
|
+
export async function resolveParticipant(square, name, control) { const { state } = await square.artifact.read(control?.signal); return { name: resolveKnownName(state, name), roster: rosterNames(state) }; }
|
|
65
66
|
export async function currentParticipant(square, name) { const { state } = await square.artifact.read(); const known = resolveRosterName(state, name); return known !== undefined && isCurrentlyJoined(state.acts, known) ? known : undefined; }
|
|
66
67
|
export async function participants(square) { const { state } = await square.artifact.read(); return statuses(square, state); }
|
|
67
|
-
export async function snapshot(square) { const { state } = await square.artifact.read(); const folded = foldedState(state); return { context: [...state.preamble, ...state.warmup].join('\n'), actCount: state.acts.filter((activity) => activity.kind !== 'read').length, hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }), held: folded.hold.active && folded.hold.actor !== undefined ? { by: folded.hold.actor, ...(folded.hold.reason === undefined ? {} : { reason: folded.hold.reason }) } : null, participants: statuses(square, state), delivered(name, id) { return isActivitySeen(state, name, parseRequiredActivityId(id)); } }; }
|
|
68
|
-
export async function activityPresentation(square, name) { const { state } = await square.artifact.read(); const known = resolveKnownName(state, name); const delivery = deriveDeliveryModel(state); const delta = deliveryDelta(state, known, delivery); const hold = currentHold(state.acts); return { name: known, roster: rosterNames(state), pendingPublic: directedPeerSays(state, delta, known, delivery),
|
|
68
|
+
export async function snapshot(square, control) { const { state } = await square.artifact.read(control?.signal); const folded = foldedState(state); return { context: [...state.preamble, ...state.warmup].join('\n'), actCount: state.acts.filter((activity) => activity.kind !== 'read').length, hardCap: state.hardCap, ...(state.throttlePerMinute === undefined ? {} : { throttlePerMinute: state.throttlePerMinute }), held: folded.hold.active && folded.hold.actor !== undefined ? { by: folded.hold.actor, ...(folded.hold.reason === undefined ? {} : { reason: folded.hold.reason }) } : null, participants: statuses(square, state), delivered(name, id) { return isActivitySeen(state, name, parseRequiredActivityId(id)); } }; }
|
|
69
|
+
export async function activityPresentation(square, name) { const { state } = await square.artifact.read(); const known = resolveKnownName(state, name); const delivery = deriveDeliveryModel(state); const delta = deliveryDelta(state, known, delivery); const hold = currentHold(state.acts); return { name: known, roster: rosterNames(state), pendingPublic: directedPeerSays(state, delta, known, delivery), activities: state.acts, state, participantCount: inSquareCount(state), held: hold.active, ...(hold.reason === undefined ? {} : { holdReason: hold.reason }), ownActivityCount: countSays(state.acts, known), hardCap: state.hardCap }; }
|
|
69
70
|
export async function entryPresentation(square, name, lastN = 10) { const { state } = await square.artifact.read(); const known = resolveRosterName(state, name) ?? name; const publicActivities = state.acts.filter((activity) => activity.kind === 'say' || activity.kind === 'done'); return { joined: isCurrentlyJoined(state.acts, known), scene: state.warmup.join('\n').trim(), context: state.preamble.join('\n').trim(), joinContext: (state.preamble.at(-1) === '---' ? state.preamble.slice(0, -1) : state.preamble).join('\n').trim(), recentActivities: lastN === null ? publicActivities : publicActivities.slice(-lastN), state, sayNumbers: sayNumbers(state), participantCount: inSquareCount(state) }; }
|
|
70
71
|
export async function historyPresentation(square, options) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); return { activities: coreActivities(state, options, delivery), sayNumbers: sayNumbers(state), presenceAnchors: anchors(state, delivery), participantCount: inSquareCount(state) }; }
|
|
71
72
|
export async function participantsPresentation(square) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); return coreParticipants(state, square.clock(), delivery); }
|
|
@@ -74,8 +75,6 @@ export async function statusPresentation(square) { const { state } = await squar
|
|
|
74
75
|
export async function eventPresentation(square, id) { const { state } = await square.artifact.read(); const activity = state.acts.find((candidate) => candidate.index === parseRequiredActivityId(id)); if (activity === undefined)
|
|
75
76
|
throw new SquareError('invalid_args', `Unknown activity id: ${id}`); return { activity, participantCount: inSquareCount(state), held: currentHold(state.acts).active }; }
|
|
76
77
|
export async function watchPresentation(square, name) { const { state } = await square.artifact.read(); const known = resolveKnownName(state, name); const now = square.clock(); const terminal = watchTerminalStatus(state, known); const delivery = deriveDeliveryModel(state); return { activities: state.acts, state, participantCount: inSquareCount(state), presence: { participants: coreParticipants(state, now, delivery), now }, ...(terminal === undefined ? {} : { terminalStatus: terminal }) }; }
|
|
77
|
-
export async function inboxProjection(square, name, _sessionId) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); const known = delivery.knownParticipant(name); if (known === undefined || !delivery.joinedRecipients().some((recipient) => nameKey(recipient) === nameKey(known)))
|
|
78
|
-
return { name, joined: false, notifications: [] }; const lease = freshWatchLease(state, known, square.clock()); return { name: known, joined: true, notifications: delivery.pendingFor(known).map(({ item, route }) => ({ actIndex: item.index, actor: item.actor, at: item.at, route, body: item.body })), ...(lease === undefined ? {} : { catchLease: lease }) }; }
|
|
79
78
|
const STREAM_BATCH_MAX = 100;
|
|
80
79
|
function projectStreamActivities(state, activities, recipient) {
|
|
81
80
|
const delivery = recipient === undefined ? undefined : deriveDeliveryModel(state);
|
|
@@ -86,12 +85,6 @@ function projectStreamActivities(state, activities, recipient) {
|
|
|
86
85
|
return notification === undefined ? [] : [{ activity, route: notification.route }];
|
|
87
86
|
});
|
|
88
87
|
}
|
|
89
|
-
function selectStreamTail(activities, last) {
|
|
90
|
-
if (!Number.isSafeInteger(last) || last < 0 || last > STREAM_BATCH_MAX) {
|
|
91
|
-
throw new SquareError('invalid_args', `Invalid stream tail: expected a non-negative safe integer no greater than ${STREAM_BATCH_MAX}.`);
|
|
92
|
-
}
|
|
93
|
-
return last === 0 ? [] : activities.slice(-last);
|
|
94
|
-
}
|
|
95
88
|
export async function streamProjection(square, cursor, recipient) {
|
|
96
89
|
const artifact = 'artifact' in square ? square.artifact : square.cell;
|
|
97
90
|
const { state } = await artifact.read();
|
|
@@ -106,14 +99,14 @@ export async function streamProjection(square, cursor, recipient) {
|
|
|
106
99
|
export async function streamTailProjection(square, last = 10, recipient) {
|
|
107
100
|
const artifact = 'artifact' in square ? square.artifact : square.cell;
|
|
108
101
|
const { state } = await artifact.read();
|
|
102
|
+
const activities = projectStreamActivities(state, state.acts, recipient);
|
|
103
|
+
if (!Number.isSafeInteger(last) || last < 0 || last > STREAM_BATCH_MAX) {
|
|
104
|
+
throw new SquareError('invalid_args', `Invalid stream tail: expected a non-negative safe integer no greater than ${STREAM_BATCH_MAX}.`);
|
|
105
|
+
}
|
|
109
106
|
return {
|
|
110
|
-
activities:
|
|
107
|
+
activities: last === 0 ? [] : activities.slice(-last),
|
|
111
108
|
cursor: state.acts.at(-1)?.index ?? -1,
|
|
112
109
|
hasMore: false,
|
|
113
110
|
};
|
|
114
111
|
}
|
|
115
|
-
export async function notificationForAct(square, actIndex) { const { state } = await square.artifact.read(); const activity = state.acts.find((candidate) => candidate.index === actIndex); return activity === undefined ? [] : deriveDeliveryModel(state).plan(activity); }
|
|
116
112
|
export function pendingDeliveriesFromState(state, delivery = deriveDeliveryModel(state)) { return delivery.joinedRecipients().map((recipient) => ({ recipient, notifications: delivery.pendingFor(recipient) })); }
|
|
117
|
-
export async function pendingDeliveries(square) { const { state } = await square.artifact.read(); return pendingDeliveriesFromState(state); }
|
|
118
|
-
export async function notificationEvidence(square, recipient, actIndex) { const { state } = await square.artifact.read(); const delivery = deriveDeliveryModel(state); const known = delivery.knownParticipant(recipient) ?? recipient; return { delivered: delivery.isSeen(known, actIndex), observation: state.runtime.observations?.[known]?.[formatActivityId(actIndex)] }; }
|
|
119
|
-
export async function notificationDelivered(square, recipient, actIndex) { const { state } = await square.artifact.read(); return deriveDeliveryModel(state).isSeen(recipient, actIndex); }
|
package/dist/wake-attempts.d.ts
CHANGED
|
@@ -1,51 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
export { hasAttemptableWakeRoute, isWakeRouteAttemptable, terminalWakeEvidence } from './square-projections.js';
|
|
4
|
-
export type WakeOutcome = 'accepted' | 'unknown' | 'failed';
|
|
5
|
-
export interface WakeAttention {
|
|
6
|
-
squarePath: string;
|
|
7
|
-
actIndex: number;
|
|
8
|
-
recipient: string;
|
|
9
|
-
}
|
|
10
|
-
export interface WakeReleaseDiagnostic {
|
|
11
|
-
readonly at: number;
|
|
12
|
-
readonly attention: WakeAttention;
|
|
13
|
-
readonly routeKind?: WakeRouteKind;
|
|
14
|
-
readonly attemptN?: number;
|
|
15
|
-
readonly session?: string;
|
|
16
|
-
readonly signature?: string;
|
|
17
|
-
readonly message?: string;
|
|
18
|
-
readonly diagnostic?: unknown;
|
|
19
|
-
}
|
|
20
|
-
export type WakeAttempt = ProjectedWakeAttempt;
|
|
21
|
-
export interface WakeDispatchLease {
|
|
22
|
-
readonly leaseId: string;
|
|
23
|
-
readonly expiresAt: number;
|
|
24
|
-
readonly phase: 'claimed' | 'dispatching';
|
|
25
|
-
readonly routeKind?: WakeRouteKind;
|
|
26
|
-
readonly attemptN?: number;
|
|
27
|
-
readonly session?: string;
|
|
28
|
-
}
|
|
29
|
-
export type WakeDispatchClaim = {
|
|
30
|
-
readonly type: 'acquired';
|
|
31
|
-
readonly leaseId: string;
|
|
32
|
-
} | {
|
|
33
|
-
readonly type: 'busy';
|
|
34
|
-
} | {
|
|
35
|
-
readonly type: 'ambiguous';
|
|
36
|
-
readonly lease: WakeDispatchLease;
|
|
37
|
-
};
|
|
38
|
-
export declare function wakeAttemptsPath(env?: NodeJS.ProcessEnv): string;
|
|
39
|
-
export declare function wakeAttentionKey(attention: WakeAttention): Promise<string>;
|
|
40
|
-
export declare function claimWakeDispatch(attention: WakeAttention, leaseId: string, leaseMs: number, env?: NodeJS.ProcessEnv, at?: number, session?: string): Promise<WakeDispatchClaim>;
|
|
41
|
-
export declare function transitionWakeDispatch(attention: WakeAttention, leaseId: string, phase: WakeDispatchLease['phase'], leaseMs: number, routeKind: WakeRouteKind | undefined, attemptN: number | undefined, session: string | undefined, env?: NodeJS.ProcessEnv, at?: number): Promise<boolean>;
|
|
42
|
-
export declare function releaseWakeDispatch(attention: WakeAttention, leaseId: string, env?: NodeJS.ProcessEnv, at?: number): Promise<void>;
|
|
43
|
-
export declare function readWakeAttempts(opts?: {
|
|
44
|
-
attention?: WakeAttention;
|
|
45
|
-
sessionId?: string;
|
|
46
|
-
now?: number;
|
|
47
|
-
env?: NodeJS.ProcessEnv;
|
|
48
|
-
}): Promise<WakeAttempt[]>;
|
|
1
|
+
import type { WakeAttention } from './host-ledger.js';
|
|
2
|
+
import { type WakeReleaseDiagnostic } from './wake-evidence.js';
|
|
49
3
|
export declare function redactWakeDiagnostic(value: unknown, env?: NodeJS.ProcessEnv): unknown;
|
|
50
4
|
export declare function readWakeReleaseDiagnostics(opts?: {
|
|
51
5
|
location?: string;
|
|
@@ -54,11 +8,3 @@ export declare function readWakeReleaseDiagnostics(opts?: {
|
|
|
54
8
|
now?: number;
|
|
55
9
|
env?: NodeJS.ProcessEnv;
|
|
56
10
|
}): Promise<WakeReleaseDiagnostic[]>;
|
|
57
|
-
export declare function recordWakeAttempt(attempt: Omit<WakeAttempt, 'at'> & {
|
|
58
|
-
at?: number;
|
|
59
|
-
}, env?: NodeJS.ProcessEnv): Promise<WakeAttempt>;
|
|
60
|
-
export declare function recordRecoveredUnknown(attention: WakeAttention, lease: {
|
|
61
|
-
attemptN?: number;
|
|
62
|
-
routeKind?: WakeRouteKind;
|
|
63
|
-
session?: string;
|
|
64
|
-
}, env?: NodeJS.ProcessEnv, at?: number): Promise<WakeAttempt | undefined>;
|
package/dist/wake-attempts.js
CHANGED
|
@@ -1,134 +1,39 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
1
|
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
2
|
+
import { nameKey } from './model.js';
|
|
3
|
+
import { canonicalPath } from './canonical-path.js';
|
|
4
|
+
import { formatActivityId } from './square-core.js';
|
|
7
5
|
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
8
|
-
import { terminalWakeEvidence } from './square-projections.js';
|
|
9
6
|
import { redactDiagnostic } from './diagnostic-redaction.js';
|
|
10
|
-
|
|
11
|
-
const VALID_OUTCOMES = new Set(['accepted', 'unknown', 'failed']);
|
|
12
|
-
function ledger(env) {
|
|
13
|
-
return createHostLedgerPort({ rootPath: hostLedgerRoot(env) });
|
|
14
|
-
}
|
|
15
|
-
export function wakeAttemptsPath(env = process.env) {
|
|
16
|
-
return env.SQUARE_WAKE_ATTEMPTS || path.join(os.homedir(), '.square', 'wake-attempts.ndjsonl');
|
|
17
|
-
}
|
|
18
|
-
export async function wakeAttentionKey(attention) {
|
|
19
|
-
return JSON.stringify([await canonicalSquarePath(attention.squarePath), formatActivityId(attention.actIndex), nameKey(attention.recipient)]);
|
|
20
|
-
}
|
|
21
|
-
export async function claimWakeDispatch(attention, leaseId, leaseMs, env = process.env, at = Date.now(), session) {
|
|
22
|
-
return ledger(env).claimWakeDispatch({ attention, leaseId, leaseMs, at, session });
|
|
23
|
-
}
|
|
24
|
-
export async function transitionWakeDispatch(attention, leaseId, phase, leaseMs, routeKind, attemptN, session, env = process.env, at = Date.now()) {
|
|
25
|
-
return ledger(env).transitionWakeDispatch({ attention, leaseId, phase, leaseMs, routeKind, attemptN, session, at });
|
|
26
|
-
}
|
|
27
|
-
export async function releaseWakeDispatch(attention, leaseId, env = process.env, at = Date.now()) {
|
|
28
|
-
await ledger(env).releaseWakeDispatch({ attention, leaseId, at });
|
|
29
|
-
}
|
|
30
|
-
async function readRows(env, now) {
|
|
31
|
-
const records = await ledger(env).listWakeAttempts({ now });
|
|
32
|
-
return records.flatMap((record) => {
|
|
33
|
-
const actIndex = parseActivityId(record.activity);
|
|
34
|
-
if (actIndex === undefined || !isWakeRouteKind(record.routeKind) || !VALID_OUTCOMES.has(record.outcome) || typeof record.attemptN !== 'number')
|
|
35
|
-
return [];
|
|
36
|
-
return [{ at: record.at ?? now, attention: { squarePath: record.location, actIndex, recipient: record.participant }, routeKind: record.routeKind, outcome: record.outcome, attemptN: record.attemptN, ...(record.signature === undefined ? {} : { signature: record.signature }), ...(record.session === undefined ? {} : { session: record.session }), ...(record.message === undefined ? {} : { message: record.message }), ...(record.diagnostic === undefined ? {} : { diagnostic: record.diagnostic }) }];
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
|
-
export async function readWakeAttempts(opts = {}) {
|
|
40
|
-
const now = opts.now ?? Date.now();
|
|
41
|
-
const expected = opts.attention === undefined ? undefined : await wakeAttentionKey(opts.attention);
|
|
42
|
-
const attempts = await readRows(opts.env ?? process.env, now);
|
|
43
|
-
const scoped = opts.sessionId === undefined ? attempts : attempts.filter((attempt) => attempt.session === opts.sessionId);
|
|
44
|
-
if (expected === undefined)
|
|
45
|
-
return scoped;
|
|
46
|
-
const keys = await Promise.all(scoped.map((attempt) => wakeAttentionKey(attempt.attention)));
|
|
47
|
-
return scoped.filter((_, index) => keys[index] === expected);
|
|
48
|
-
}
|
|
7
|
+
import { decodeWakeEvidence } from './wake-evidence.js';
|
|
49
8
|
export function redactWakeDiagnostic(value, env = process.env) {
|
|
50
9
|
return redactDiagnostic(value, env.PASEO_PASSWORD);
|
|
51
10
|
}
|
|
52
11
|
export async function readWakeReleaseDiagnostics(opts = {}) {
|
|
53
12
|
const now = opts.now ?? Date.now();
|
|
54
13
|
const env = opts.env ?? process.env;
|
|
55
|
-
const rows = await
|
|
14
|
+
const rows = await createHostLedgerPort({ rootPath: hostLedgerRoot(env) }).listEvidence({
|
|
56
15
|
kind: 'wake',
|
|
57
16
|
location: opts.location ?? opts.attention?.squarePath,
|
|
58
17
|
includeReleased: true,
|
|
59
18
|
now,
|
|
60
19
|
});
|
|
61
|
-
const expected = opts.attention === undefined ? undefined : await
|
|
20
|
+
const expected = opts.attention === undefined ? undefined : JSON.stringify([await canonicalPath(opts.attention.squarePath), formatActivityId(opts.attention.actIndex), nameKey(opts.attention.recipient)]);
|
|
62
21
|
const releases = [];
|
|
63
22
|
for (const row of rows) {
|
|
64
|
-
|
|
23
|
+
const decoded = decodeWakeEvidence(row, now);
|
|
24
|
+
if (decoded?.kind !== 'release')
|
|
65
25
|
continue;
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
26
|
+
const release = decoded.value;
|
|
27
|
+
if (opts.sessionId !== undefined && release.session !== opts.sessionId)
|
|
68
28
|
continue;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
const attention = { squarePath: row.location, recipient: row.participant, actIndex };
|
|
72
|
-
if (expected !== undefined && JSON.stringify([row.location, formatActivityId(actIndex), nameKey(row.participant)]) !== expected)
|
|
29
|
+
const { attention } = release;
|
|
30
|
+
if (expected !== undefined && JSON.stringify([attention.squarePath, formatActivityId(attention.actIndex), nameKey(attention.recipient)]) !== expected)
|
|
73
31
|
continue;
|
|
74
32
|
releases.push({
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
...(
|
|
78
|
-
...(row.attemptN === undefined ? {} : { attemptN: row.attemptN }),
|
|
79
|
-
...(row.session === undefined ? {} : { session: row.session }),
|
|
80
|
-
...(row.signature === undefined ? {} : { signature: row.signature }),
|
|
81
|
-
...(row.message === undefined ? {} : { message: redactWakeDiagnostic(row.message, env) }),
|
|
82
|
-
...(row.diagnostic === undefined ? {} : { diagnostic: redactWakeDiagnostic(row.diagnostic, env) }),
|
|
33
|
+
...release,
|
|
34
|
+
...(release.message === undefined ? {} : { message: redactWakeDiagnostic(release.message, env) }),
|
|
35
|
+
...(release.diagnostic === undefined ? {} : { diagnostic: redactWakeDiagnostic(release.diagnostic, env) }),
|
|
83
36
|
});
|
|
84
37
|
}
|
|
85
38
|
return releases.sort((left, right) => right.at - left.at);
|
|
86
39
|
}
|
|
87
|
-
export async function recordWakeAttempt(attempt, env = process.env) {
|
|
88
|
-
const value = { ...attempt, at: attempt.at ?? Date.now() };
|
|
89
|
-
if (!isWakeRouteKind(value.routeKind))
|
|
90
|
-
throw new Error('Wake attempts require a real adapter route kind.');
|
|
91
|
-
if (value.outcome !== 'accepted' && !value.signature) {
|
|
92
|
-
throw new Error(`${value.outcome} wake attempts require a transport signature.`);
|
|
93
|
-
}
|
|
94
|
-
const safe = redactWakeDiagnostic(value, env);
|
|
95
|
-
const session = safe.session ?? safe.signature ?? `route:${safe.routeKind}`;
|
|
96
|
-
const claim = await ledger(env).claimEvidence({ location: safe.attention.squarePath, participant: safe.attention.recipient, session, activity: formatActivityId(safe.attention.actIndex), kind: 'wake', leaseMs: 5000 });
|
|
97
|
-
if (claim.status === 'acquired')
|
|
98
|
-
await ledger(env).appendWakeAttempt({ location: safe.attention.squarePath, participant: safe.attention.recipient, session, activity: formatActivityId(safe.attention.actIndex), kind: 'wake', outcome: safe.outcome, routeKind: safe.routeKind, signature: safe.signature, attemptN: safe.attemptN, message: safe.message, diagnostic: safe.diagnostic, at: safe.at, claimToken: claim.claimToken });
|
|
99
|
-
return value;
|
|
100
|
-
}
|
|
101
|
-
export async function recordRecoveredUnknown(attention, lease, env = process.env, at = Date.now()) {
|
|
102
|
-
const routeKind = lease.routeKind;
|
|
103
|
-
if (lease.attemptN === undefined || !isWakeRouteKind(routeKind))
|
|
104
|
-
return undefined;
|
|
105
|
-
const rows = await readRows(env, at);
|
|
106
|
-
{
|
|
107
|
-
const expected = await wakeAttentionKey(attention);
|
|
108
|
-
const attempts = [];
|
|
109
|
-
for (const attempt of rows) {
|
|
110
|
-
if (await wakeAttentionKey(attempt.attention) !== expected)
|
|
111
|
-
continue;
|
|
112
|
-
if (lease.session !== undefined && attempt.session !== lease.session)
|
|
113
|
-
continue;
|
|
114
|
-
attempts.push(attempt);
|
|
115
|
-
}
|
|
116
|
-
const terminal = terminalWakeEvidence(attempts);
|
|
117
|
-
if (terminal !== undefined)
|
|
118
|
-
return terminal;
|
|
119
|
-
const value = {
|
|
120
|
-
at,
|
|
121
|
-
attention,
|
|
122
|
-
routeKind,
|
|
123
|
-
outcome: 'unknown',
|
|
124
|
-
signature: 'worker_interrupted_during_dispatch',
|
|
125
|
-
attemptN: lease.attemptN,
|
|
126
|
-
message: 'The notification worker ended after dispatch began; transport acceptance is unknown.',
|
|
127
|
-
};
|
|
128
|
-
const session = lease.session ?? value.signature;
|
|
129
|
-
const claim = await ledger(env).claimEvidence({ location: attention.squarePath, participant: attention.recipient, session, activity: formatActivityId(attention.actIndex), kind: 'wake', leaseMs: 5000 });
|
|
130
|
-
if (claim.status === 'acquired')
|
|
131
|
-
await ledger(env).appendWakeAttempt({ location: attention.squarePath, participant: attention.recipient, session, activity: formatActivityId(attention.actIndex), kind: 'wake', outcome: 'unknown', routeKind, signature: value.signature, attemptN: value.attemptN, message: value.message, at, claimToken: claim.claimToken });
|
|
132
|
-
return value;
|
|
133
|
-
}
|
|
134
|
-
}
|