@astrosheep/square 0.3.63 → 0.3.64
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.js +12 -24
- 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-hook.js +9 -12
- 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 +13 -30
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list.js +1 -4
- package/dist/mcp.js +18 -40
- 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 +70 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +28 -0
- package/dist/packages/agent-delivery/src/claude-native.js +65 -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 +25 -23
- 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.js +4 -7
- package/dist/square-projections.d.ts +40 -45
- package/dist/square-projections.js +43 -73
- 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 +0 -5
- package/dist/square-wiring.js +3 -21
- package/dist/stream.js +4 -6
- package/dist/time.js +2 -7
- package/dist/views.d.ts +4 -16
- package/dist/views.js +12 -19
- 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 +10 -5
- 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 +43 -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-core.d.ts
CHANGED
|
@@ -139,8 +139,11 @@ export declare function audienceIncludes(audience: Audience, name: string): bool
|
|
|
139
139
|
export declare function resolveAudience(audience: Audience, candidateNames: readonly string[]): string[];
|
|
140
140
|
export declare function activeListeners(state: FoldedSquareState, sender: string): string[];
|
|
141
141
|
export declare function isIgnored(state: FoldedSquareState, listener: string, sender: string): boolean;
|
|
142
|
+
/** The author of every landed activity, keyed by index; a reply audience derives from this. */
|
|
143
|
+
export declare function activityAuthors(acts: readonly Act[]): Map<number, Participant>;
|
|
142
144
|
export declare function listeningTo(state: FoldedSquareState, listener: string): string[];
|
|
143
145
|
export declare function isListening(state: FoldedSquareState, listener: string, sender: string): boolean;
|
|
146
|
+
export declare function actorOf(act: Act): Participant | undefined;
|
|
144
147
|
export declare function fold(acts: readonly Act[]): FoldedSquareState;
|
|
145
148
|
/** Replay the activity stream once to fix every say's audience at landing. */
|
|
146
149
|
export declare function replayLandedAudiences(acts: readonly Act[]): LandedAudienceReplay;
|
package/dist/square-core.js
CHANGED
|
@@ -60,12 +60,28 @@ export function activeListeners(state, sender) {
|
|
|
60
60
|
export function isIgnored(state, listener, sender) {
|
|
61
61
|
return (state.ignored.get(nameKey(listener)) ?? []).some((target) => sameName(target, sender));
|
|
62
62
|
}
|
|
63
|
-
|
|
63
|
+
/** The author of every landed activity, keyed by index; a reply audience derives from this. */
|
|
64
|
+
export function activityAuthors(acts) {
|
|
65
|
+
const authors = new Map();
|
|
66
|
+
for (const act of acts) {
|
|
67
|
+
const actor = actorOf(act);
|
|
68
|
+
if (actor === undefined || !('index' in act) || typeof act.index !== 'number')
|
|
69
|
+
continue;
|
|
70
|
+
authors.set(act.index, actor);
|
|
71
|
+
}
|
|
72
|
+
return authors;
|
|
73
|
+
}
|
|
74
|
+
function recipientsAtLanding(before, say, authors) {
|
|
64
75
|
const audience = audienceOf(say);
|
|
65
|
-
const
|
|
76
|
+
const standing = before.participants.filter((participant) => participant.joined).map((participant) => participant.name);
|
|
77
|
+
const mentionTargets = resolveAudience(audience, standing);
|
|
78
|
+
// A reply is directed attention in its own right: it reaches the author of the
|
|
79
|
+
// replied-to activity under the same standing and ignore rules as a mention.
|
|
80
|
+
const replyAuthor = say.reply === undefined ? undefined : authors.get(say.reply);
|
|
81
|
+
const replyTargets = replyAuthor === undefined ? [] : resolveAudience({ kind: 'mentions', names: [replyAuthor] }, standing);
|
|
66
82
|
const listeners = activeListeners(before, say.actor);
|
|
67
83
|
const recipients = [];
|
|
68
|
-
for (const name of [...mentionTargets, ...listeners]) {
|
|
84
|
+
for (const name of [...mentionTargets, ...replyTargets, ...listeners]) {
|
|
69
85
|
if (sameName(name, say.actor) || recipients.some((existing) => sameName(existing, name)))
|
|
70
86
|
continue;
|
|
71
87
|
if (audience.kind !== 'bell' && isIgnored(before, name, say.actor))
|
|
@@ -80,7 +96,7 @@ export function listeningTo(state, listener) {
|
|
|
80
96
|
export function isListening(state, listener, sender) {
|
|
81
97
|
return listeningTo(state, listener).some((target) => sameName(target, sender));
|
|
82
98
|
}
|
|
83
|
-
function actorOf(act) {
|
|
99
|
+
export function actorOf(act) {
|
|
84
100
|
if ('actor' in act && typeof act.actor === 'string')
|
|
85
101
|
return act.actor;
|
|
86
102
|
return undefined;
|
|
@@ -238,9 +254,10 @@ export function replayLandedAudiences(acts) {
|
|
|
238
254
|
const byActivity = new Map();
|
|
239
255
|
const byIndex = new Map();
|
|
240
256
|
const lastJoinByKey = new Map();
|
|
257
|
+
const authors = activityAuthors(acts);
|
|
241
258
|
for (const act of acts) {
|
|
242
259
|
if (act.kind === 'say') {
|
|
243
|
-
const recipients = recipientsAtLanding(accumulator.state, act);
|
|
260
|
+
const recipients = recipientsAtLanding(accumulator.state, act, authors);
|
|
244
261
|
byActivity.set(act, recipients);
|
|
245
262
|
const index = 'index' in act ? act.index : undefined;
|
|
246
263
|
if (typeof index === 'number')
|
package/dist/square-facade.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface ExpressOptions {
|
|
|
24
24
|
export interface ExpressResult {
|
|
25
25
|
readonly activity: Activity;
|
|
26
26
|
readonly delivery?: DeliveryResult;
|
|
27
|
+
/** The replied-to author was not standing when the reply landed, so no notification reached them. */
|
|
28
|
+
readonly replyAuthorNotHere?: string;
|
|
27
29
|
}
|
|
28
30
|
export interface ListenerChangeResult {
|
|
29
31
|
readonly activity: Activity | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { observeSquareRevision as observeSquareChanges } from './artifact.js';
|
|
2
2
|
import { hostLedgerRoot } from './host-ledger-root.js';
|
|
3
|
-
import { createSquareState, createSquareSnapshot, probeSquareFile, writeSquareSnapshot,
|
|
3
|
+
import { createSquareState, createSquareSnapshot, probeSquareFile, writeSquareSnapshot, createFileCell, createMemoryCell, } from './square-storage.js';
|
|
4
4
|
import { InternalSquareError, SquareError, } from './model.js';
|
|
5
5
|
import { createHostLedgerPort } from './host-ledger-file-adapter.js';
|
|
6
6
|
/** File-owned artifact creation for the CLI and path-backed public facade. */
|
|
@@ -51,7 +51,7 @@ export async function openSquare(squarePath, options = {}) {
|
|
|
51
51
|
}
|
|
52
52
|
export async function probeSquare(squarePath) {
|
|
53
53
|
const state = await probeSquareFile(squarePath);
|
|
54
|
-
return state === undefined ? undefined : { artifact:
|
|
54
|
+
return state === undefined ? undefined : { artifact: createMemoryCell(state), clock: Date.now, location: squarePath, hostLedger: createHostLedgerPort({ rootPath: hostLedgerRoot(process.env) }) };
|
|
55
55
|
}
|
|
56
56
|
export async function buildSquare(squarePath, options) {
|
|
57
57
|
validateBuildOptions(options);
|
|
@@ -77,12 +77,9 @@ export function buildMemorySquare(options) {
|
|
|
77
77
|
hardCap: options.hardCap ?? null,
|
|
78
78
|
...(options.throttlePerMinute === undefined ? {} : { throttlePerMinute: options.throttlePerMinute }),
|
|
79
79
|
}, options.markdown);
|
|
80
|
-
return { artifact:
|
|
80
|
+
return { artifact: createMemoryCell(squareState), clock: options.clock ?? Date.now, location: 'memory', hostLedger: options.hostLedger, wakeTransport: options.wakeTransport };
|
|
81
81
|
}
|
|
82
82
|
/** A projection reads and validates its snapshot once; no preliminary duplicate read. */
|
|
83
83
|
export function openSquareArtifact(squarePath, signal) {
|
|
84
|
-
return
|
|
85
|
-
}
|
|
86
|
-
function memoryArtifact(cell) {
|
|
87
|
-
return { read: (signal) => cell.read(signal), transact: (fn, signal) => cell.transact(fn, signal), changed: (since, timeout, signal) => cell.changed(since, timeout, signal), close: () => cell.close() };
|
|
84
|
+
return createFileCell(squarePath, signal);
|
|
88
85
|
}
|
|
@@ -1,28 +1,56 @@
|
|
|
1
1
|
import { type InboxMembership, type SquareState } from './model.js';
|
|
2
2
|
import type { HostLedgerPort, SquareArtifactPort, PresentationEvidenceProjection, PresentationProjection, SessionBindingProjection } from './ports.js';
|
|
3
3
|
import { deriveDeliveryModel } from './delivery.js';
|
|
4
|
-
import
|
|
4
|
+
import { type WakeEligibility } from './wake-eligibility.js';
|
|
5
5
|
/** A fresh blocking catch owns only the notifications admitted by its filter. */
|
|
6
6
|
export declare function pendingAtBoundary(inbox: InboxMembership[]): InboxMembership[];
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* One eligibility projection for a freshly read session inbox: a fresh catch lease owns what it
|
|
9
|
+
* admitted, and a delivered (or clipped) boundary already raised awareness for its activity, so
|
|
10
|
+
* neither belongs at the next boundary. Ordering, session scoping, suppression outcomes and
|
|
11
|
+
* ledger errors stay exactly as the boundary callers read them.
|
|
12
|
+
*/
|
|
13
|
+
export declare function projectBoundaryEligibility(input: {
|
|
8
14
|
readonly hostLedger: HostLedgerPort;
|
|
9
15
|
readonly sessionId: string;
|
|
16
|
+
readonly inbox: readonly InboxMembership[];
|
|
17
|
+
}): Promise<InboxMembership[]>;
|
|
18
|
+
export declare function projectSessionBindings(input: {
|
|
19
|
+
readonly hostLedger: HostLedgerPort;
|
|
20
|
+
readonly sessionId?: string;
|
|
10
21
|
readonly location?: string;
|
|
11
22
|
readonly now?: number;
|
|
12
23
|
}): Promise<readonly SessionBindingProjection[]>;
|
|
13
|
-
export declare function sessionIdsFromEnvironment(env?: NodeJS.ProcessEnv): readonly string[];
|
|
14
|
-
export declare function projectLocalParticipantBinding(input: {
|
|
15
|
-
readonly hostLedger: HostLedgerPort;
|
|
16
|
-
readonly location: string;
|
|
17
|
-
readonly participant: string;
|
|
18
|
-
readonly sessionIds: readonly string[];
|
|
19
|
-
readonly now?: number;
|
|
20
|
-
}): Promise<SessionBindingProjection | undefined>;
|
|
21
24
|
export declare function projectPresentation(input: {
|
|
22
25
|
readonly artifact: SquareArtifactPort;
|
|
23
26
|
readonly binding: SessionBindingProjection;
|
|
24
27
|
readonly now?: number;
|
|
25
28
|
}): Promise<PresentationProjection>;
|
|
29
|
+
export interface NativePendingPreview {
|
|
30
|
+
readonly payload: string;
|
|
31
|
+
readonly clipped: boolean;
|
|
32
|
+
}
|
|
33
|
+
/** The one native pending preview. `epoch` matches exactly, and each harness pins its own address entry. */
|
|
34
|
+
export type NativePendingPreviewInput = {
|
|
35
|
+
readonly location: string;
|
|
36
|
+
readonly sessionId: string;
|
|
37
|
+
readonly participant: string;
|
|
38
|
+
readonly actIndex: number;
|
|
39
|
+
readonly epoch?: number;
|
|
40
|
+
readonly cancelledThrough?: number;
|
|
41
|
+
readonly now: number;
|
|
42
|
+
} & ({
|
|
43
|
+
readonly routeKind: 'claude-native';
|
|
44
|
+
readonly address: {
|
|
45
|
+
readonly endpoint: string;
|
|
46
|
+
};
|
|
47
|
+
} | {
|
|
48
|
+
readonly routeKind: 'opencode-server';
|
|
49
|
+
readonly address: {
|
|
50
|
+
readonly sessionId: string;
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
export declare function nativePendingPreview(state: SquareState, input: NativePendingPreviewInput): NativePendingPreview | undefined;
|
|
26
54
|
export declare function projectPresentationEvidence(input: {
|
|
27
55
|
readonly hostLedger: HostLedgerPort;
|
|
28
56
|
readonly location?: string;
|
|
@@ -33,44 +61,11 @@ export declare function projectPresentationEvidence(input: {
|
|
|
33
61
|
}): Promise<readonly PresentationEvidenceProjection[]>;
|
|
34
62
|
/** Successful boundary output, including a clipped preview, suppresses awareness wake. */
|
|
35
63
|
export declare function presentationSuppressesWake(evidence: readonly Pick<PresentationEvidenceProjection, 'outcome'>[]): boolean;
|
|
36
|
-
/**
|
|
37
|
-
export declare function currentSessionBindings<T extends {
|
|
38
|
-
readonly participant: string;
|
|
39
|
-
readonly updatedAt?: number;
|
|
40
|
-
}>(bindings: readonly T[]): T[];
|
|
41
|
-
export interface WakeAttempt {
|
|
42
|
-
readonly at: number;
|
|
43
|
-
readonly attention: {
|
|
44
|
-
readonly squarePath: string;
|
|
45
|
-
readonly actIndex: number;
|
|
46
|
-
readonly recipient: string;
|
|
47
|
-
};
|
|
48
|
-
readonly routeKind: WakeRouteKind;
|
|
49
|
-
readonly outcome: 'accepted' | 'unknown' | 'failed';
|
|
50
|
-
readonly signature?: string;
|
|
51
|
-
readonly attemptN: number;
|
|
52
|
-
readonly session?: string;
|
|
53
|
-
readonly message?: string;
|
|
54
|
-
readonly diagnostic?: unknown;
|
|
55
|
-
}
|
|
56
|
-
export declare function terminalWakeEvidence(attempts: readonly WakeAttempt[]): WakeAttempt | undefined;
|
|
57
|
-
export declare function isWakeRouteAttemptable(route: Pick<WakeRoute, 'kind' | 'updatedAt'>, attempts: readonly WakeAttempt[]): boolean;
|
|
58
|
-
export declare function hasAttemptableWakeRoute(routes: readonly Pick<WakeRoute, 'kind' | 'updatedAt'>[], attempts: readonly WakeAttempt[]): boolean;
|
|
59
|
-
export interface WakeEvidence {
|
|
60
|
-
readonly delivered: boolean;
|
|
61
|
-
readonly presented: boolean;
|
|
62
|
-
readonly attempts: readonly WakeAttempt[];
|
|
63
|
-
readonly terminal?: WakeAttempt;
|
|
64
|
-
readonly attemptableRoutes: readonly WakeRoute[];
|
|
65
|
-
}
|
|
66
|
-
export interface WakeEvidenceProjection {
|
|
67
|
-
evidence(recipient: string, actIndex: number): WakeEvidence;
|
|
68
|
-
}
|
|
64
|
+
/** Read primary evidence once; wake behavior belongs to the pure eligibility projection. */
|
|
69
65
|
export declare function projectWakeEvidenceFromState(input: {
|
|
70
66
|
readonly location: string;
|
|
71
67
|
readonly state: SquareState;
|
|
72
68
|
readonly hostLedger: HostLedgerPort;
|
|
73
69
|
readonly now: number;
|
|
74
70
|
readonly delivery?: ReturnType<typeof deriveDeliveryModel>;
|
|
75
|
-
}): Promise<
|
|
76
|
-
export declare function wakeIsEligible(evidence: WakeEvidence): boolean;
|
|
71
|
+
}): Promise<WakeEligibility>;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import { formatActivityId
|
|
1
|
+
import { formatActivityId } from './square-core.js';
|
|
2
2
|
import { nameKey } from './model.js';
|
|
3
3
|
import { deriveDeliveryModel, leaseOwnsNotification } from './delivery.js';
|
|
4
4
|
import { freshWatchLease } from './runtime.js';
|
|
5
|
-
import {
|
|
5
|
+
import { attentionBodyIsClipped, renderAttentionPreview } from './attention-presentation.js';
|
|
6
|
+
import { canonicalPath } from './canonical-path.js';
|
|
7
|
+
import { projectWakeEligibility } from './wake-eligibility.js';
|
|
6
8
|
/** A fresh blocking catch owns only the notifications admitted by its filter. */
|
|
7
9
|
export function pendingAtBoundary(inbox) {
|
|
8
10
|
return inbox.map((membership) => {
|
|
@@ -12,37 +14,33 @@ export function pendingAtBoundary(inbox) {
|
|
|
12
14
|
return { ...membership, notifications: membership.notifications.filter((notification) => !leaseOwnsNotification(lease, { ...notification, recipient: membership.name })) };
|
|
13
15
|
}).filter((membership) => membership.notifications.length > 0);
|
|
14
16
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* One eligibility projection for a freshly read session inbox: a fresh catch lease owns what it
|
|
19
|
+
* admitted, and a delivered (or clipped) boundary already raised awareness for its activity, so
|
|
20
|
+
* neither belongs at the next boundary. Ordering, session scoping, suppression outcomes and
|
|
21
|
+
* ledger errors stay exactly as the boundary callers read them.
|
|
22
|
+
*/
|
|
23
|
+
export async function projectBoundaryEligibility(input) {
|
|
24
|
+
const pending = [];
|
|
25
|
+
for (const membership of pendingAtBoundary([...input.inbox])) {
|
|
26
|
+
const evidence = await projectPresentationEvidence({ hostLedger: input.hostLedger, location: membership.squarePath, participant: membership.name, sessionId: input.sessionId });
|
|
27
|
+
const notifications = membership.notifications.filter((notification) => !presentationSuppressesWake(evidence.filter((row) => row.activity === formatActivityId(notification.actIndex))));
|
|
28
|
+
if (notifications.length > 0)
|
|
29
|
+
pending.push({ ...membership, notifications });
|
|
30
|
+
}
|
|
31
|
+
return pending;
|
|
32
|
+
}
|
|
33
|
+
export async function projectSessionBindings(input) {
|
|
34
|
+
const rows = await input.hostLedger.listPresence({ location: input.location, session: input.sessionId, now: input.now });
|
|
35
|
+
return rows.map((record) => ({
|
|
17
36
|
location: record.location,
|
|
18
37
|
participant: record.participant,
|
|
19
38
|
sessionId: record.session,
|
|
20
39
|
channel: record.channel,
|
|
21
|
-
...(record.
|
|
22
|
-
|
|
23
|
-
location: record.location,
|
|
24
|
-
participant: record.participant,
|
|
25
|
-
sessionId: record.session,
|
|
26
|
-
channel: record.channel,
|
|
27
|
-
kind: record.route.kind,
|
|
28
|
-
address: { ...record.route.address },
|
|
29
|
-
updatedAt: record.updatedAt ?? 0,
|
|
30
|
-
},
|
|
31
|
-
}),
|
|
40
|
+
...(record.epoch === undefined ? {} : { epoch: record.epoch }),
|
|
41
|
+
...(record.cancelledThrough === undefined ? {} : { cancelledThrough: record.cancelledThrough }),
|
|
32
42
|
updatedAt: record.updatedAt ?? 0,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
export async function projectSessionBindings(input) {
|
|
36
|
-
const rows = await input.hostLedger.listPresence({ location: input.location, session: input.sessionId, now: input.now });
|
|
37
|
-
return rows.map(bindingProjection);
|
|
38
|
-
}
|
|
39
|
-
export function sessionIdsFromEnvironment(env = process.env) {
|
|
40
|
-
return [env.CLAUDE_CODE_SESSION_ID, env.CODEX_THREAD_ID, env.OPENCODE_SESSION_ID, env.PI_SESSION_ID, env.PASEO_AGENT_ID]
|
|
41
|
-
.map((value) => value?.trim()).filter((value) => Boolean(value));
|
|
42
|
-
}
|
|
43
|
-
export async function projectLocalParticipantBinding(input) {
|
|
44
|
-
const bindings = (await Promise.all(input.sessionIds.map((sessionId) => projectSessionBindings({ hostLedger: input.hostLedger, location: input.location, sessionId, now: input.now })))).flat();
|
|
45
|
-
return bindings.find((binding) => binding.participant.toLocaleLowerCase() === input.participant.toLocaleLowerCase());
|
|
43
|
+
}));
|
|
46
44
|
}
|
|
47
45
|
export async function projectPresentation(input) {
|
|
48
46
|
const { state } = await input.artifact.read();
|
|
@@ -55,6 +53,18 @@ export async function projectPresentation(input) {
|
|
|
55
53
|
const lease = freshWatchLease(state, known, input.now ?? Date.now());
|
|
56
54
|
return { binding, joined: true, notifications, ...(lease === undefined ? {} : { catchLease: lease }) };
|
|
57
55
|
}
|
|
56
|
+
export function nativePendingPreview(state, input) {
|
|
57
|
+
const route = state.routes?.find((candidate) => candidate.kind === input.routeKind && candidate.sessionId === input.sessionId
|
|
58
|
+
&& candidate.epoch === input.epoch && nameKey(candidate.participant) === nameKey(input.participant)
|
|
59
|
+
&& (input.routeKind === 'claude-native' ? candidate.address.endpoint === input.address.endpoint : candidate.address.sessionId === input.address.sessionId));
|
|
60
|
+
if (!route || input.actIndex <= (input.cancelledThrough ?? -1))
|
|
61
|
+
return undefined;
|
|
62
|
+
const notification = deriveDeliveryModel(state).pendingFor(input.participant).find((entry) => entry.item.index === input.actIndex);
|
|
63
|
+
const lease = freshWatchLease(state, input.participant, input.now);
|
|
64
|
+
if (!notification || (lease !== undefined && leaseOwnsNotification(lease, { ...notification.item, recipient: input.participant, route: notification.route })))
|
|
65
|
+
return undefined;
|
|
66
|
+
return { payload: renderAttentionPreview({ squarePath: input.location, recipient: input.participant, actIndex: input.actIndex, actor: notification.item.actor, route: notification.route, body: notification.item.body }), clipped: attentionBodyIsClipped(notification.item.body) };
|
|
67
|
+
}
|
|
58
68
|
export async function projectPresentationEvidence(input) {
|
|
59
69
|
const rows = await input.hostLedger.listEvidence({ kind: 'presentation', location: input.location, participant: input.participant, session: input.sessionId, activity: input.activity, now: input.now });
|
|
60
70
|
return rows.map((row) => ({ location: row.location, participant: row.participant, sessionId: row.session, activity: row.activity, outcome: row.outcome, ...(row.at === undefined ? {} : { at: row.at }) }));
|
|
@@ -63,52 +73,12 @@ export async function projectPresentationEvidence(input) {
|
|
|
63
73
|
export function presentationSuppressesWake(evidence) {
|
|
64
74
|
return evidence.some((row) => row.outcome === 'clipped' || row.outcome === 'presented');
|
|
65
75
|
}
|
|
66
|
-
/**
|
|
67
|
-
export function currentSessionBindings(bindings) {
|
|
68
|
-
const latest = new Map();
|
|
69
|
-
for (const binding of bindings) {
|
|
70
|
-
const participant = nameKey(binding.participant);
|
|
71
|
-
latest.set(participant, Math.max(latest.get(participant) ?? Number.NEGATIVE_INFINITY, binding.updatedAt ?? 0));
|
|
72
|
-
}
|
|
73
|
-
return bindings.filter((binding) => (binding.updatedAt ?? 0) === latest.get(nameKey(binding.participant)));
|
|
74
|
-
}
|
|
75
|
-
export function terminalWakeEvidence(attempts) { return attempts.findLast((attempt) => attempt.outcome === 'accepted'); }
|
|
76
|
-
export function isWakeRouteAttemptable(route, attempts) {
|
|
77
|
-
if (terminalWakeEvidence(attempts) !== undefined)
|
|
78
|
-
return false;
|
|
79
|
-
if (attempts.some((attempt) => attempt.routeKind === route.kind && attempt.outcome === 'unknown'))
|
|
80
|
-
return false;
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
export function hasAttemptableWakeRoute(routes, attempts) { return routes.some((route) => isWakeRouteAttemptable(route, attempts)); }
|
|
76
|
+
/** Read primary evidence once; wake behavior belongs to the pure eligibility projection. */
|
|
84
77
|
export async function projectWakeEvidenceFromState(input) {
|
|
85
|
-
const canonicalLocation = await
|
|
78
|
+
const canonicalLocation = await canonicalPath(input.location);
|
|
86
79
|
const delivery = input.delivery ?? deriveDeliveryModel(input.state);
|
|
87
|
-
const
|
|
88
|
-
.filter((route) => route.location === canonicalLocation || route.location === input.location)
|
|
89
|
-
.map((route) => ({ location: route.location, participant: route.participant, sessionId: route.sessionId, channel: route.channel, route: { ...route, address: { ...route.address } }, updatedAt: route.updatedAt }));
|
|
80
|
+
const owners = await input.hostLedger.listPresence({ location: canonicalLocation, now: input.now });
|
|
90
81
|
const wakeRecords = await input.hostLedger.listEvidence({ location: canonicalLocation, kind: 'wake', now: input.now });
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
const actIndex = parseActivityId(record.activity);
|
|
94
|
-
if (actIndex === undefined || record.routeKind === undefined || typeof record.attemptN !== 'number')
|
|
95
|
-
continue;
|
|
96
|
-
const attempt = { attention: { squarePath: record.location, recipient: record.participant, actIndex }, outcome: record.outcome, at: record.at ?? input.now, routeKind: record.routeKind, 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 }) };
|
|
97
|
-
const key = JSON.stringify([nameKey(record.participant), actIndex, record.session]);
|
|
98
|
-
const existing = attemptsByBinding.get(key) ?? [];
|
|
99
|
-
existing.push(attempt);
|
|
100
|
-
attemptsByBinding.set(key, existing);
|
|
101
|
-
}
|
|
102
|
-
const presentedRows = await projectPresentationEvidence({ hostLedger: input.hostLedger, location: canonicalLocation, now: input.now });
|
|
103
|
-
return {
|
|
104
|
-
evidence(recipient, actIndex) {
|
|
105
|
-
const recipientBindings = currentSessionBindings(bindings.filter((binding) => nameKey(binding.participant) === nameKey(recipient)));
|
|
106
|
-
const routes = recipientBindings.flatMap((binding) => binding.route === undefined ? [] : [binding.route]);
|
|
107
|
-
const attempts = recipientBindings.flatMap((binding) => attemptsByBinding.get(JSON.stringify([nameKey(recipient), actIndex, binding.sessionId])) ?? []);
|
|
108
|
-
const terminal = terminalWakeEvidence(attempts);
|
|
109
|
-
const presented = presentedRows.some((row) => row.activity === formatActivityId(actIndex) && row.participant.toLocaleLowerCase() === recipient.toLocaleLowerCase() && recipientBindings.some((binding) => binding.sessionId === row.sessionId) && presentationSuppressesWake([row]));
|
|
110
|
-
return { delivered: delivery.isSeen(recipient, actIndex), presented, attempts, ...(terminal === undefined ? {} : { terminal }), attemptableRoutes: terminal === undefined ? routes.filter((route) => isWakeRouteAttemptable(route, attempts)) : [] };
|
|
111
|
-
},
|
|
112
|
-
};
|
|
82
|
+
const presentations = await projectPresentationEvidence({ hostLedger: input.hostLedger, location: canonicalLocation, now: input.now });
|
|
83
|
+
return projectWakeEligibility({ state: input.state, location: canonicalLocation, owners, wakeRecords, presentations, now: input.now, delivery });
|
|
113
84
|
}
|
|
114
|
-
export function wakeIsEligible(evidence) { return !evidence.delivered && !evidence.presented && evidence.terminal === undefined && evidence.attemptableRoutes.length > 0; }
|
package/dist/square-state.js
CHANGED
|
@@ -25,9 +25,6 @@ function isNonblankString(value) {
|
|
|
25
25
|
function isStringArray(value) {
|
|
26
26
|
return Array.isArray(value) && value.every((item) => typeof item === 'string');
|
|
27
27
|
}
|
|
28
|
-
function validateObservation(value) {
|
|
29
|
-
return isObject(value) && hasExactKeys(value, ['state', 'at']) && value.state === 'seen' && isFiniteNumber(value.at);
|
|
30
|
-
}
|
|
31
28
|
function validateWatchLease(value) {
|
|
32
29
|
if (!isObject(value) || !hasExactKeys(value, ['leaseId', 'heartbeatAt', 'expiresAt'], ['filter'])
|
|
33
30
|
|| !isNonblankString(value.leaseId) || !isFiniteNumber(value.heartbeatAt)
|
|
@@ -46,7 +43,7 @@ function validateRuntime(value) {
|
|
|
46
43
|
return isObject(value) && hasExactKeys(value, ['nextActIndex', 'observations', 'leases'])
|
|
47
44
|
&& isNonNegativeInteger(value.nextActIndex)
|
|
48
45
|
&& validateRecord(value.observations, (candidate) => isObject(candidate)
|
|
49
|
-
&& Object.entries(candidate).every(([id, observation]) => parseActivityId(id) !== undefined &&
|
|
46
|
+
&& Object.entries(candidate).every(([id, observation]) => parseActivityId(id) !== undefined && isObject(observation) && hasExactKeys(observation, ['state', 'at']) && observation.state === 'seen' && isFiniteNumber(observation.at)))
|
|
50
47
|
&& validateRecord(value.leases, validateWatchLease);
|
|
51
48
|
}
|
|
52
49
|
function validateAssignedRuntimeReferences(runtime) {
|
package/dist/square-storage.d.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import { createSquareState, diagnoseSquareFile as diagnoseArtifactFile } from './artifact.js';
|
|
2
2
|
import type { SquareState } from './model.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { SquareArtifactPort } from './ports.js';
|
|
4
4
|
export { createSquareState };
|
|
5
|
-
export declare function readSquareFile(squarePath: string): Promise<SquareState>;
|
|
6
5
|
export declare function probeSquareFile(squarePath: string): Promise<SquareState | undefined>;
|
|
7
6
|
export declare function diagnoseSquareFile(squarePath: string): ReturnType<typeof diagnoseArtifactFile>;
|
|
8
7
|
/** Test-helper snapshot replacement backed by one SQLite artifact. */
|
|
9
8
|
export declare function writeSquareSnapshot(squarePath: string, squareState: SquareState): Promise<void>;
|
|
10
9
|
/** Race-safe non-force creation for the file adapter. */
|
|
11
10
|
export declare function createSquareSnapshot(squarePath: string, squareState: SquareState): Promise<boolean>;
|
|
12
|
-
/** In-process
|
|
13
|
-
export declare function createMemoryCell(initial: SquareState):
|
|
14
|
-
/** SQLite-backed
|
|
15
|
-
export declare function createFileCell(squarePath: string, externalSignal?: AbortSignal):
|
|
16
|
-
/** Consumer-facing file cell factory; keeps SQLite framing behind this module. */
|
|
17
|
-
export declare function openSquareCell(squarePath: string, signal?: AbortSignal): StateCell;
|
|
11
|
+
/** In-process artifact for fast behavior tests and embedded consumers. */
|
|
12
|
+
export declare function createMemoryCell(initial: SquareState): SquareArtifactPort;
|
|
13
|
+
/** SQLite-backed artifact. Revisions are read from the authoritative database, not file metadata. */
|
|
14
|
+
export declare function createFileCell(squarePath: string, externalSignal?: AbortSignal): SquareArtifactPort;
|
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
|
@@ -31,8 +31,3 @@ export declare class Square {
|
|
|
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;
|
|
@@ -90,8 +90,7 @@ export class Square {
|
|
|
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) {
|