@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/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,6 +64,9 @@ 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
72
|
export declare function resolveParticipant(square: OpenSquare, name: string): Promise<{
|
|
@@ -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(
|
|
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); }
|
|
64
65
|
export async function resolveParticipant(square, name) { const { state } = await square.artifact.read(); 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
68
|
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),
|
|
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
|
-
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type SquareState, type WakeRoute } from './model.js';
|
|
2
|
+
import { deriveDeliveryModel } from './delivery.js';
|
|
3
|
+
import type { EvidenceRecord, PresenceRecord } from './host-ledger.js';
|
|
4
|
+
import type { PresentationEvidenceProjection, WakeRequest } from './ports.js';
|
|
5
|
+
import { type WakeAttempt } from './wake-evidence.js';
|
|
6
|
+
export interface WakeSnapshot {
|
|
7
|
+
readonly state: SquareState;
|
|
8
|
+
/** Canonicalized by the caller; every identity in this snapshot is square-local. */
|
|
9
|
+
readonly location: string;
|
|
10
|
+
readonly owners: readonly PresenceRecord[];
|
|
11
|
+
readonly wakeRecords: readonly EvidenceRecord[];
|
|
12
|
+
readonly presentations: readonly PresentationEvidenceProjection[];
|
|
13
|
+
readonly now: number;
|
|
14
|
+
readonly delivery?: ReturnType<typeof deriveDeliveryModel>;
|
|
15
|
+
}
|
|
16
|
+
export interface OwnedWakeRoute {
|
|
17
|
+
readonly route: WakeRoute;
|
|
18
|
+
readonly owner: PresenceRecord;
|
|
19
|
+
}
|
|
20
|
+
export interface WakeCurrentness {
|
|
21
|
+
readonly current: boolean;
|
|
22
|
+
readonly activityPending: boolean;
|
|
23
|
+
readonly sessionBound: boolean;
|
|
24
|
+
readonly routePublished: boolean;
|
|
25
|
+
readonly selectedOwner: boolean;
|
|
26
|
+
readonly cancelled: boolean;
|
|
27
|
+
readonly presented: boolean;
|
|
28
|
+
}
|
|
29
|
+
export interface WakeEvidence {
|
|
30
|
+
readonly delivered: boolean;
|
|
31
|
+
/** All selected, uncancelled routes have session-local presentation evidence. */
|
|
32
|
+
readonly presented: boolean;
|
|
33
|
+
readonly attempts: readonly WakeAttempt[];
|
|
34
|
+
readonly terminal?: WakeAttempt;
|
|
35
|
+
readonly attemptableRoutes: readonly WakeRoute[];
|
|
36
|
+
}
|
|
37
|
+
export interface WakeEligibility {
|
|
38
|
+
evidence(recipient: string, actIndex: number): WakeEvidence;
|
|
39
|
+
currentness(request: WakeRequest): WakeCurrentness;
|
|
40
|
+
}
|
|
41
|
+
/** One strict ownership join, latest selection, and request fence over primary evidence only. */
|
|
42
|
+
export declare function projectWakeEligibility(input: WakeSnapshot): WakeEligibility;
|
|
43
|
+
export declare function wakeIsEligible(evidence: WakeEvidence): boolean;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { nameKey } from './model.js';
|
|
2
|
+
import { deriveDeliveryModel } from './delivery.js';
|
|
3
|
+
import { formatActivityId, parseActivityId } from './square-core.js';
|
|
4
|
+
import { decodeWakeEvidence } from './wake-evidence.js';
|
|
5
|
+
function identity(...parts) { return JSON.stringify(parts); }
|
|
6
|
+
/** One strict ownership join, latest selection, and request fence over primary evidence only. */
|
|
7
|
+
export function projectWakeEligibility(input) {
|
|
8
|
+
const delivery = input.delivery ?? deriveDeliveryModel(input.state);
|
|
9
|
+
const ownersBySession = new Map();
|
|
10
|
+
for (const owner of input.owners) {
|
|
11
|
+
if (owner.location !== input.location)
|
|
12
|
+
continue;
|
|
13
|
+
const key = identity(nameKey(owner.participant), owner.session);
|
|
14
|
+
const rows = ownersBySession.get(key) ?? [];
|
|
15
|
+
rows.push(owner);
|
|
16
|
+
ownersBySession.set(key, rows);
|
|
17
|
+
}
|
|
18
|
+
const ownerFor = (route) => route.location !== input.location ? undefined
|
|
19
|
+
: ownersBySession.get(identity(nameKey(route.participant), route.sessionId))?.find((row) => route.epoch === undefined || row.epoch === route.epoch);
|
|
20
|
+
const ownedRoutes = [];
|
|
21
|
+
const latest = new Map();
|
|
22
|
+
for (const route of input.state.routes ?? []) {
|
|
23
|
+
const participant = nameKey(route.participant);
|
|
24
|
+
const owner = ownerFor(route);
|
|
25
|
+
if (owner === undefined)
|
|
26
|
+
continue;
|
|
27
|
+
ownedRoutes.push({ route, owner });
|
|
28
|
+
latest.set(participant, Math.max(latest.get(participant) ?? Number.NEGATIVE_INFINITY, route.updatedAt));
|
|
29
|
+
}
|
|
30
|
+
const selectedByRecipient = new Map();
|
|
31
|
+
for (const owned of ownedRoutes) {
|
|
32
|
+
const key = nameKey(owned.route.participant);
|
|
33
|
+
if (owned.route.updatedAt !== latest.get(key))
|
|
34
|
+
continue;
|
|
35
|
+
const rows = selectedByRecipient.get(key) ?? [];
|
|
36
|
+
rows.push(owned);
|
|
37
|
+
selectedByRecipient.set(key, rows);
|
|
38
|
+
}
|
|
39
|
+
const pending = new Set();
|
|
40
|
+
for (const recipient of delivery.joinedRecipients()) {
|
|
41
|
+
for (const notification of delivery.pendingFor(recipient))
|
|
42
|
+
pending.add(identity(nameKey(recipient), notification.item.index));
|
|
43
|
+
}
|
|
44
|
+
const attemptsByAttention = new Map();
|
|
45
|
+
for (const record of input.wakeRecords) {
|
|
46
|
+
if (record.location !== input.location)
|
|
47
|
+
continue;
|
|
48
|
+
const decoded = decodeWakeEvidence(record, input.now);
|
|
49
|
+
if (decoded?.kind !== 'attempt')
|
|
50
|
+
continue;
|
|
51
|
+
const attempt = decoded.value;
|
|
52
|
+
const key = identity(nameKey(attempt.attention.recipient), attempt.attention.actIndex);
|
|
53
|
+
const rows = attemptsByAttention.get(key) ?? [];
|
|
54
|
+
rows.push(attempt);
|
|
55
|
+
attemptsByAttention.set(key, rows);
|
|
56
|
+
}
|
|
57
|
+
const presentedSessions = new Set();
|
|
58
|
+
for (const row of input.presentations) {
|
|
59
|
+
if (row.location !== input.location || (row.outcome !== 'presented' && row.outcome !== 'clipped'))
|
|
60
|
+
continue;
|
|
61
|
+
presentedSessions.add(identity(nameKey(row.participant), row.activity, row.sessionId));
|
|
62
|
+
}
|
|
63
|
+
const isPresented = (route, actIndex) => presentedSessions.has(identity(nameKey(route.participant), formatActivityId(actIndex), route.sessionId));
|
|
64
|
+
const isCancelled = (owner, actIndex) => actIndex <= (owner.cancelledThrough ?? -1);
|
|
65
|
+
return {
|
|
66
|
+
evidence(recipient, actIndex) {
|
|
67
|
+
const key = nameKey(recipient);
|
|
68
|
+
// Accepted/unknown exclusion belongs to attention, not to a surviving owner or route kind.
|
|
69
|
+
const attempts = attemptsByAttention.get(identity(key, actIndex)) ?? [];
|
|
70
|
+
const terminal = attempts.findLast((attempt) => attempt.outcome === 'accepted')
|
|
71
|
+
?? attempts.findLast((attempt) => attempt.outcome === 'unknown');
|
|
72
|
+
const routes = (selectedByRecipient.get(key) ?? []).filter(({ owner }) => !isCancelled(owner, actIndex));
|
|
73
|
+
const presented = routes.length > 0 && routes.every(({ route }) => isPresented(route, actIndex));
|
|
74
|
+
return {
|
|
75
|
+
delivered: delivery.isSeen(recipient, actIndex), presented, attempts,
|
|
76
|
+
...(terminal === undefined ? {} : { terminal }),
|
|
77
|
+
attemptableRoutes: terminal === undefined && pending.has(identity(key, actIndex))
|
|
78
|
+
? routes.filter(({ route }) => !isPresented(route, actIndex)).map(({ route }) => route) : [],
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
currentness(request) {
|
|
82
|
+
const actIndex = parseActivityId(request.activity);
|
|
83
|
+
const route = request.route;
|
|
84
|
+
const sameRecipient = nameKey(request.participant) === nameKey(route.participant);
|
|
85
|
+
const sameLocation = request.location === input.location && route.location === input.location;
|
|
86
|
+
const activityPending = actIndex !== undefined && request.location === input.location
|
|
87
|
+
&& pending.has(identity(nameKey(request.participant), actIndex));
|
|
88
|
+
const owner = sameLocation && sameRecipient ? ownerFor(route) : undefined;
|
|
89
|
+
const sessionBound = owner !== undefined;
|
|
90
|
+
const matchesRequest = (published) => published.location === route.location
|
|
91
|
+
&& nameKey(published.participant) === nameKey(route.participant) && published.sessionId === route.sessionId
|
|
92
|
+
&& published.kind === route.kind && published.epoch === route.epoch && Object.keys(published.address).length === Object.keys(route.address).length && Object.keys(published.address).every((key) => published.address[key] === route.address[key]);
|
|
93
|
+
const routePublished = sameLocation && sameRecipient && (input.state.routes ?? []).some(matchesRequest);
|
|
94
|
+
const selectedOwner = sameLocation && sameRecipient
|
|
95
|
+
&& (selectedByRecipient.get(nameKey(route.participant)) ?? []).some(({ route: published }) => matchesRequest(published));
|
|
96
|
+
const cancelled = actIndex !== undefined && owner !== undefined && isCancelled(owner, actIndex);
|
|
97
|
+
const presented = actIndex !== undefined && sameLocation && sameRecipient && isPresented(route, actIndex);
|
|
98
|
+
// Dispatching evidence deliberately does not fence the request that owns the atomic claim.
|
|
99
|
+
return { current: activityPending && sessionBound && routePublished && selectedOwner && !cancelled && !presented,
|
|
100
|
+
activityPending, sessionBound, routePublished, selectedOwner, cancelled, presented };
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export function wakeIsEligible(evidence) {
|
|
105
|
+
return !evidence.delivered && !evidence.presented && evidence.terminal === undefined && evidence.attemptableRoutes.length > 0;
|
|
106
|
+
}
|
package/dist/wake-evidence.d.ts
CHANGED
|
@@ -1,9 +1,32 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import type { EvidenceRecord, WakeAttention } from './host-ledger.js';
|
|
2
|
+
import type { WakeRouteKind } from './model.js';
|
|
3
|
+
export interface WakeAttempt {
|
|
4
|
+
readonly at: number;
|
|
5
|
+
readonly attention: WakeAttention;
|
|
6
|
+
readonly routeKind: WakeRouteKind;
|
|
7
|
+
readonly outcome: 'accepted' | 'unknown' | 'failed';
|
|
8
|
+
readonly signature?: string;
|
|
9
|
+
readonly attemptN: number;
|
|
10
|
+
readonly session?: string;
|
|
11
|
+
readonly message?: string;
|
|
12
|
+
readonly diagnostic?: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface WakeReleaseDiagnostic {
|
|
15
|
+
readonly at: number;
|
|
16
|
+
readonly attention: WakeAttention;
|
|
17
|
+
readonly routeKind?: WakeRouteKind;
|
|
18
|
+
readonly attemptN?: number;
|
|
19
|
+
readonly session?: string;
|
|
20
|
+
readonly signature?: string;
|
|
21
|
+
readonly message?: string;
|
|
22
|
+
readonly diagnostic?: unknown;
|
|
23
|
+
}
|
|
24
|
+
export type DecodedWakeEvidence = {
|
|
25
|
+
readonly kind: 'attempt';
|
|
26
|
+
readonly value: WakeAttempt;
|
|
27
|
+
} | {
|
|
28
|
+
readonly kind: 'release';
|
|
29
|
+
readonly value: WakeReleaseDiagnostic;
|
|
30
|
+
};
|
|
31
|
+
/** Decode primary attempts and diagnostic-only releases without mixing their evidence. */
|
|
32
|
+
export declare function decodeWakeEvidence(record: EvidenceRecord, now: number): DecodedWakeEvidence | undefined;
|
package/dist/wake-evidence.js
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
import { parseActivityId } from './square-core.js';
|
|
2
|
+
/** Decode primary attempts and diagnostic-only releases without mixing their evidence. */
|
|
3
|
+
export function decodeWakeEvidence(record, now) {
|
|
4
|
+
if (record.kind !== 'wake')
|
|
5
|
+
return undefined;
|
|
6
|
+
const actIndex = parseActivityId(record.activity);
|
|
7
|
+
if (actIndex === undefined)
|
|
8
|
+
return undefined;
|
|
9
|
+
const value = {
|
|
10
|
+
at: record.at ?? now,
|
|
11
|
+
attention: { squarePath: record.location, recipient: record.participant, actIndex },
|
|
12
|
+
...(record.session === undefined ? {} : { session: record.session }),
|
|
13
|
+
...(record.signature === undefined ? {} : { signature: record.signature }),
|
|
14
|
+
...(record.message === undefined ? {} : { message: record.message }),
|
|
15
|
+
...(record.diagnostic === undefined ? {} : { diagnostic: record.diagnostic }),
|
|
16
|
+
};
|
|
17
|
+
if (record.outcome === 'released') {
|
|
18
|
+
if (record.at === undefined)
|
|
19
|
+
return undefined;
|
|
20
|
+
return { kind: 'release', value: {
|
|
21
|
+
...value,
|
|
22
|
+
...(record.routeKind === undefined ? {} : { routeKind: record.routeKind }),
|
|
23
|
+
...(record.attemptN === undefined ? {} : { attemptN: record.attemptN }),
|
|
24
|
+
} };
|
|
19
25
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return (await wakeEvidenceProjection(squarePath, now, env)).evidence(recipient, actIndex);
|
|
26
|
+
if (record.outcome !== 'accepted' && record.outcome !== 'unknown' && record.outcome !== 'failed')
|
|
27
|
+
return undefined;
|
|
28
|
+
if (record.routeKind === undefined || typeof record.attemptN !== 'number')
|
|
29
|
+
return undefined;
|
|
30
|
+
return { kind: 'attempt', value: { ...value, routeKind: record.routeKind, attemptN: record.attemptN, outcome: record.outcome } };
|
|
26
31
|
}
|