@astrosheep/square 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact.d.ts +1 -3
- package/dist/artifact.js +12 -36
- package/dist/attention-presentation.d.ts +2 -22
- package/dist/attention-presentation.js +9 -12
- package/dist/boundary-presentation.d.ts +3 -14
- package/dist/boundary-presentation.js +29 -104
- package/dist/card-presentation.d.ts +1 -0
- package/dist/card-presentation.js +2 -0
- package/dist/catch-decisions.js +1 -7
- package/dist/claude-boundary-state.d.ts +20 -0
- package/dist/claude-boundary-state.js +84 -0
- package/dist/claude-hook.d.ts +2 -0
- package/dist/claude-hook.js +231 -0
- package/dist/claude-mcp-context.d.ts +1 -1
- package/dist/claude-mcp-context.js +12 -3
- package/dist/cli/context.js +7 -9
- package/dist/cli/observation-commands.d.ts +4 -1
- package/dist/cli/observation-commands.js +37 -4
- package/dist/cli/registry.js +5 -2
- package/dist/codex-address.d.ts +2 -2
- package/dist/codex-boundary-state.d.ts +1 -5
- package/dist/codex-boundary-state.js +0 -4
- package/dist/codex-hook.d.ts +7 -10
- package/dist/codex-hook.js +69 -33
- package/dist/codex-queue.d.ts +10 -11
- package/dist/codex-queue.js +32 -32
- package/dist/codex-watch.d.ts +2 -0
- package/dist/codex-watch.js +100 -0
- package/dist/decisions.js +2 -2
- package/dist/delivery-deferral.d.ts +8 -17
- package/dist/delivery-deferral.js +0 -28
- package/dist/delivery-frame.d.ts +7 -0
- package/dist/delivery-frame.js +11 -0
- package/dist/delivery-ledger-sqlite.d.ts +97 -0
- package/dist/delivery-ledger-sqlite.js +496 -0
- package/dist/delivery-ledger.d.ts +196 -0
- package/dist/delivery-ledger.js +268 -0
- package/dist/delivery-lifetime.d.ts +6 -0
- package/dist/delivery-lifetime.js +45 -0
- package/dist/harness-claude.js +3 -4
- package/dist/harness-links.js +63 -7
- package/dist/harness.js +12 -17
- package/dist/help.js +4 -1
- package/dist/http-delivery.d.ts +5 -9
- package/dist/http-delivery.js +20 -77
- package/dist/inbox.d.ts +6 -49
- package/dist/inbox.js +24 -371
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/mcp.js +2 -3
- package/dist/model.d.ts +7 -47
- package/dist/model.js +1 -7
- package/dist/notifications.d.ts +24 -20
- package/dist/notifications.js +39 -108
- package/dist/operation-control.d.ts +4 -0
- package/dist/operation-control.js +20 -0
- package/dist/packages/agent-delivery/src/claude-native.d.ts +2 -0
- package/dist/packages/agent-delivery/src/claude-native.js +5 -0
- package/dist/packages/agent-delivery/src/codex-boundary.d.ts +11 -6
- package/dist/packages/agent-delivery/src/codex-boundary.js +31 -40
- package/dist/packages/agent-delivery/src/codex-hook.d.ts +17 -12
- package/dist/packages/agent-delivery/src/codex-hook.js +26 -39
- package/dist/packages/agent-delivery/src/codex.d.ts +3 -0
- package/dist/packages/agent-delivery/src/codex.js +41 -4
- package/dist/packages/agent-delivery/src/index.d.ts +3 -1
- package/dist/packages/agent-delivery/src/index.js +1 -1
- package/dist/packages/agent-delivery/src/paseo.js +11 -2
- package/dist/packages/agent-delivery/src/types.d.ts +1 -0
- package/dist/paseo-delivery.d.ts +5 -5
- package/dist/paseo-delivery.js +19 -97
- package/dist/paseo.d.ts +1 -1
- package/dist/paseo.js +1 -1
- package/dist/plugins/opencode/index.d.ts +5 -3
- package/dist/plugins/opencode/index.js +99 -21
- package/dist/plugins/opencode-shared/delivery.d.ts +23 -0
- package/dist/plugins/opencode-shared/delivery.js +109 -0
- package/dist/plugins/opencode-v1/index.d.ts +28 -0
- package/dist/plugins/opencode-v1/index.js +41 -0
- package/dist/plugins/opencode-v1/runtime.d.ts +56 -0
- package/dist/plugins/opencode-v1/runtime.js +152 -0
- package/dist/plugins/opencode-v1/transport.d.ts +13 -0
- package/dist/plugins/opencode-v1/transport.js +50 -0
- package/dist/plugins/paseo/shared/card-presentation.d.ts +10 -0
- package/dist/plugins/paseo/shared/card-presentation.js +7 -0
- package/dist/ports.d.ts +31 -90
- package/dist/session-lifecycle.d.ts +8 -7
- package/dist/session-lifecycle.js +40 -42
- package/dist/sqlite-square.d.ts +14 -23
- package/dist/sqlite-square.js +132 -139
- package/dist/square-api.js +4 -7
- package/dist/square-core.d.ts +0 -1
- package/dist/square-domain.d.ts +5 -17
- package/dist/square-domain.js +104 -221
- package/dist/square-facade.d.ts +18 -15
- package/dist/square-post.d.ts +53 -0
- package/dist/square-post.js +4 -0
- package/dist/square-posting.d.ts +13 -0
- package/dist/square-posting.js +174 -0
- package/dist/square-producer-sqlite.d.ts +11 -0
- package/dist/square-producer-sqlite.js +210 -0
- package/dist/square-sqlite.d.ts +15 -25
- package/dist/square-sqlite.js +150 -226
- package/dist/views.d.ts +2 -2
- package/dist/views.js +0 -1
- package/dist/wake-sink.d.ts +4 -11
- package/dist/wake-sink.js +8 -32
- package/dist/wake-transports.d.ts +4 -7
- package/dist/wake-transports.js +7 -39
- package/examples/symmetric-domains.mjs +29 -249
- package/guides/change-waits.md +13 -53
- package/guides/claude-native.md +7 -171
- package/guides/codex-delivery.md +55 -80
- package/guides/delivery-primitives.md +48 -207
- package/guides/mcp.md +1 -1
- package/guides/operation-cancellation.md +2 -5
- package/guides/participant-ownership.md +25 -89
- package/guides/pi-native-delivery.md +19 -56
- package/guides/pi-tools.md +6 -8
- package/guides/square-api.md +113 -615
- package/package.json +8 -8
- package/plugins/claude/.claude-plugin/plugin.json +2 -3
- package/plugins/claude/hooks/hooks.json +11 -1
- package/plugins/codex/.codex-plugin/plugin.json +1 -1
- package/plugins/codex/hooks/hooks.json +11 -23
- package/plugins/opencode/index.ts +91 -23
- package/plugins/opencode-shared/delivery.ts +91 -0
- package/plugins/opencode-v1/index.ts +36 -0
- package/plugins/opencode-v1/runtime.ts +134 -0
- package/plugins/opencode-v1/transport.ts +41 -0
- package/plugins/paseo/OVERVIEW.md +5 -5
- package/plugins/paseo/README.md +7 -6
- package/plugins/paseo/client/markdown.tsx +5 -5
- package/plugins/paseo/client/square-message.tsx +28 -24
- package/plugins/paseo/client/transform.ts +8 -5
- package/plugins/paseo/shared/card-presentation.ts +16 -0
- package/plugins/paseo/shared/message.ts +6 -11
- package/plugins/pi/square-pi.js +156 -417
- package/dist/channel-artifact.d.ts +0 -10
- package/dist/channel-artifact.js +0 -55
- package/dist/channel-delivery.d.ts +0 -11
- package/dist/channel-delivery.js +0 -51
- package/dist/channel-domain.d.ts +0 -3
- package/dist/channel-domain.js +0 -83
- package/dist/channel-facade.d.ts +0 -76
- package/dist/channel-facade.js +0 -1
- package/dist/channel-internal.d.ts +0 -126
- package/dist/channel-internal.js +0 -440
- package/dist/channel-metadata.d.ts +0 -11
- package/dist/channel-metadata.js +0 -1
- package/dist/channel-ports.d.ts +0 -241
- package/dist/channel-ports.js +0 -1
- package/dist/channel-presentation.d.ts +0 -17
- package/dist/channel-presentation.js +0 -22
- package/dist/channel-sqlite-schema.d.ts +0 -13
- package/dist/channel-sqlite-schema.js +0 -81
- package/dist/channel-transport.d.ts +0 -62
- package/dist/channel-transport.js +0 -1
- package/dist/channel.d.ts +0 -84
- package/dist/channel.js +0 -60
- package/dist/claude-delivery-status.d.ts +0 -13
- package/dist/claude-delivery-status.js +0 -46
- package/dist/claude-delivery.d.ts +0 -12
- package/dist/claude-delivery.js +0 -108
- package/dist/claude-mod.d.ts +0 -8
- package/dist/claude-mod.js +0 -172
- package/dist/claude-transport.d.ts +0 -5
- package/dist/claude-transport.js +0 -58
- package/dist/cmd/notify-once.d.ts +0 -2
- package/dist/cmd/notify-once.js +0 -29
- package/dist/delivery-attempts.d.ts +0 -52
- package/dist/delivery-attempts.js +0 -73
- package/dist/delivery-effects.d.ts +0 -139
- package/dist/delivery-effects.js +0 -163
- package/dist/delivery-health.d.ts +0 -51
- package/dist/delivery-health.js +0 -173
- package/dist/delivery-operations.d.ts +0 -22
- package/dist/delivery-operations.js +0 -198
- package/dist/delivery-primitives.d.ts +0 -5
- package/dist/delivery-primitives.js +0 -3
- package/dist/delivery.d.ts +0 -56
- package/dist/delivery.js +0 -80
- package/dist/plugins/opencode/delivery.d.ts +0 -19
- package/dist/plugins/opencode/delivery.js +0 -223
- package/dist/presentation-operations.d.ts +0 -5
- package/dist/presentation-operations.js +0 -86
- package/dist/sqlite-channel.d.ts +0 -15
- package/dist/sqlite-channel.js +0 -600
- package/dist/square-index.d.ts +0 -16
- package/dist/square-index.js +0 -106
- package/dist/wake-preview.d.ts +0 -7
- package/dist/wake-preview.js +0 -15
- package/plugins/claude/hooks/register.js +0 -132
- package/plugins/claude/types.d.ts +0 -7
- package/plugins/opencode/delivery.ts +0 -198
- /package/dist/{channel-values.d.ts → square-values.d.ts} +0 -0
- /package/dist/{channel-values.js → square-values.js} +0 -0
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
export interface EffectClaim {
|
|
2
|
-
readonly token: string;
|
|
3
|
-
readonly ordinal: number;
|
|
4
|
-
}
|
|
5
|
-
/** Bound to one captured item/recipient/authority/purpose, not an encoded series key. */
|
|
6
|
-
export interface EffectPort<C> {
|
|
7
|
-
/** The caller's existing atomic claim operation. Undefined means no custody acquired. */
|
|
8
|
-
claim(signal?: AbortSignal): Promise<EffectClaim | undefined>;
|
|
9
|
-
/** Pre-effect policy only. Rejection means unverifiable/not current. */
|
|
10
|
-
current(signal?: AbortSignal): Promise<boolean>;
|
|
11
|
-
/**
|
|
12
|
-
* No request signal: persistence has its own bounded lifetime. For presentation,
|
|
13
|
-
* `presented` MUST complete the attempt and record ALL captured full-consumption
|
|
14
|
-
* receipts in ONE atomic transaction (rollback both on error). `clipped`, `failed`,
|
|
15
|
-
* `unknown` and `released` complete the attempt without full-consumption receipts.
|
|
16
|
-
* No post-effect currentness/deferral/cancellation veto; captured authority still fences.
|
|
17
|
-
*/
|
|
18
|
-
settle(claim: EffectClaim, completion: C): Promise<boolean>;
|
|
19
|
-
}
|
|
20
|
-
export type Persistence = {
|
|
21
|
-
readonly status: 'recorded';
|
|
22
|
-
} | {
|
|
23
|
-
readonly status: 'not-recorded';
|
|
24
|
-
} | {
|
|
25
|
-
readonly status: 'error';
|
|
26
|
-
readonly error: unknown;
|
|
27
|
-
};
|
|
28
|
-
export interface Settlement<C> {
|
|
29
|
-
readonly completion: C;
|
|
30
|
-
readonly persistence: Persistence;
|
|
31
|
-
}
|
|
32
|
-
export interface DispatchOutcome {
|
|
33
|
-
readonly outcome: 'accepted' | 'failed' | 'unknown' | 'not-capable';
|
|
34
|
-
readonly data?: unknown;
|
|
35
|
-
}
|
|
36
|
-
export type DispatchCompletion = {
|
|
37
|
-
readonly outcome: 'accepted' | 'failed' | 'unknown';
|
|
38
|
-
readonly reason: 'returned';
|
|
39
|
-
readonly data?: unknown;
|
|
40
|
-
} | {
|
|
41
|
-
readonly outcome: 'unknown';
|
|
42
|
-
readonly reason: 'threw';
|
|
43
|
-
readonly error: unknown;
|
|
44
|
-
} | {
|
|
45
|
-
readonly outcome: 'unknown';
|
|
46
|
-
readonly reason: 'timeout';
|
|
47
|
-
} | {
|
|
48
|
-
readonly outcome: 'released';
|
|
49
|
-
readonly reason: 'not-current';
|
|
50
|
-
readonly phase: 'initial' | 'final';
|
|
51
|
-
} | {
|
|
52
|
-
readonly outcome: 'released';
|
|
53
|
-
readonly reason: 'not-capable';
|
|
54
|
-
readonly data?: unknown;
|
|
55
|
-
} | {
|
|
56
|
-
readonly outcome: 'released';
|
|
57
|
-
readonly reason: 'cancelled';
|
|
58
|
-
};
|
|
59
|
-
export interface DispatchContext {
|
|
60
|
-
readonly claim: EffectClaim;
|
|
61
|
-
readonly timeoutMs: number;
|
|
62
|
-
readonly deadline: number;
|
|
63
|
-
readonly signal?: AbortSignal;
|
|
64
|
-
/** Policy only. Sender MUST recheck deadline/cancellation after this await before I/O. */
|
|
65
|
-
beforeSend(): Promise<boolean>;
|
|
66
|
-
}
|
|
67
|
-
export interface DispatchReport {
|
|
68
|
-
readonly observation: {
|
|
69
|
-
readonly kind: 'not-invoked';
|
|
70
|
-
} | {
|
|
71
|
-
readonly kind: 'returned';
|
|
72
|
-
readonly result: DispatchOutcome;
|
|
73
|
-
} | {
|
|
74
|
-
readonly kind: 'threw';
|
|
75
|
-
readonly error: unknown;
|
|
76
|
-
} | {
|
|
77
|
-
readonly kind: 'timeout';
|
|
78
|
-
};
|
|
79
|
-
readonly settlement?: Settlement<DispatchCompletion>;
|
|
80
|
-
}
|
|
81
|
-
/** Timer bounds the external attempt only, not claim/currentness/persistence. No automatic retry. */
|
|
82
|
-
export declare function dispatchEffect(port: EffectPort<DispatchCompletion>, send: (context: DispatchContext) => Promise<DispatchOutcome>, options: {
|
|
83
|
-
readonly timeoutMs: number;
|
|
84
|
-
readonly signal?: AbortSignal;
|
|
85
|
-
}): Promise<DispatchReport>;
|
|
86
|
-
export type PresentationCompletion = {
|
|
87
|
-
readonly outcome: 'presented' | 'clipped';
|
|
88
|
-
readonly reason: 'returned';
|
|
89
|
-
} | {
|
|
90
|
-
readonly outcome: 'failed' | 'unknown';
|
|
91
|
-
readonly reason: 'threw';
|
|
92
|
-
readonly error: unknown;
|
|
93
|
-
} | {
|
|
94
|
-
readonly outcome: 'released';
|
|
95
|
-
readonly reason: 'not-current' | 'cancelled' | 'unused';
|
|
96
|
-
};
|
|
97
|
-
export interface PresentationWork<P> {
|
|
98
|
-
readonly payload: P;
|
|
99
|
-
readonly coverage: 'full' | 'clipped';
|
|
100
|
-
readonly port: EffectPort<PresentationCompletion>;
|
|
101
|
-
}
|
|
102
|
-
declare const preparedBrand: unique symbol;
|
|
103
|
-
export interface PreparedPresentation<P> {
|
|
104
|
-
readonly [preparedBrand]: true;
|
|
105
|
-
readonly payload: P;
|
|
106
|
-
eligible(signal?: AbortSignal): Promise<boolean>;
|
|
107
|
-
release(): Promise<Settlement<PresentationCompletion>>;
|
|
108
|
-
}
|
|
109
|
-
export interface PresentationControl {
|
|
110
|
-
readonly signal?: AbortSignal;
|
|
111
|
-
/** Default unknown; throwing or invalid classifiers also stay unknown. */
|
|
112
|
-
readonly classifyError?: (error: unknown) => 'failed' | 'unknown';
|
|
113
|
-
}
|
|
114
|
-
export interface PresentationReport<P, R> {
|
|
115
|
-
readonly observation: {
|
|
116
|
-
readonly kind: 'not-invoked';
|
|
117
|
-
} | {
|
|
118
|
-
readonly kind: 'returned';
|
|
119
|
-
readonly value: R;
|
|
120
|
-
} | {
|
|
121
|
-
readonly kind: 'threw';
|
|
122
|
-
readonly error: unknown;
|
|
123
|
-
readonly outcome: 'failed' | 'unknown';
|
|
124
|
-
};
|
|
125
|
-
readonly settlements: readonly {
|
|
126
|
-
readonly prepared: PreparedPresentation<P>;
|
|
127
|
-
readonly result: Settlement<PresentationCompletion>;
|
|
128
|
-
}[];
|
|
129
|
-
}
|
|
130
|
-
export declare function preparePresentation<P>(work: PresentationWork<P>, control?: {
|
|
131
|
-
readonly signal?: AbortSignal;
|
|
132
|
-
}): Promise<PreparedPresentation<P> | undefined>;
|
|
133
|
-
export declare function presentPrepared<P, R>(prepared: PreparedPresentation<P>, sink: (payload: P) => R | Promise<R>, control?: PresentationControl): Promise<PresentationReport<P, R>>;
|
|
134
|
-
/** Takes all local custody synchronously, then owns eligibility/effect/settlement/cleanup. */
|
|
135
|
-
export declare function presentPreparedBatch<P, B, R>(prepared: readonly PreparedPresentation<P>[], render: (eligible: readonly PreparedPresentation<P>[]) => {
|
|
136
|
-
readonly payload: B;
|
|
137
|
-
readonly included: readonly PreparedPresentation<P>[];
|
|
138
|
-
}, sink: (payload: B) => R | Promise<R>, control?: PresentationControl): Promise<PresentationReport<P, R>>;
|
|
139
|
-
export {};
|
package/dist/delivery-effects.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
async function settle(port, claim, completion) {
|
|
2
|
-
try {
|
|
3
|
-
return { completion, persistence: { status: await port.settle(claim, completion) ? 'recorded' : 'not-recorded' } };
|
|
4
|
-
}
|
|
5
|
-
catch (error) {
|
|
6
|
-
return { completion, persistence: { status: 'error', error } };
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
async function current(port, signal) {
|
|
10
|
-
try {
|
|
11
|
-
return await port.current(signal);
|
|
12
|
-
}
|
|
13
|
-
catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/** Timer bounds the external attempt only, not claim/currentness/persistence. No automatic retry. */
|
|
18
|
-
export async function dispatchEffect(port, send, options) {
|
|
19
|
-
const notInvoked = { kind: 'not-invoked' };
|
|
20
|
-
if (options.signal?.aborted)
|
|
21
|
-
return { observation: notInvoked };
|
|
22
|
-
const claim = await port.claim(options.signal);
|
|
23
|
-
if (claim === undefined)
|
|
24
|
-
return { observation: notInvoked };
|
|
25
|
-
if (!await current(port, options.signal))
|
|
26
|
-
return { observation: notInvoked,
|
|
27
|
-
settlement: await settle(port, claim, { outcome: 'released', reason: 'not-current', phase: 'initial' }) };
|
|
28
|
-
if (options.signal?.aborted)
|
|
29
|
-
return { observation: notInvoked,
|
|
30
|
-
settlement: await settle(port, claim, { outcome: 'released', reason: 'cancelled' }) };
|
|
31
|
-
let denied = false;
|
|
32
|
-
let timer;
|
|
33
|
-
const deadline = Date.now() + options.timeoutMs;
|
|
34
|
-
const timeout = new Promise(resolve => {
|
|
35
|
-
timer = setTimeout(() => resolve({ kind: 'timeout' }), options.timeoutMs);
|
|
36
|
-
});
|
|
37
|
-
let observation;
|
|
38
|
-
let completion;
|
|
39
|
-
try {
|
|
40
|
-
observation = await Promise.race([timeout, (async () => {
|
|
41
|
-
try {
|
|
42
|
-
return { kind: 'returned', result: await send({ claim, deadline, ...options,
|
|
43
|
-
beforeSend: async () => {
|
|
44
|
-
const allowed = await current(port, options.signal);
|
|
45
|
-
if (!allowed)
|
|
46
|
-
denied = true;
|
|
47
|
-
return allowed;
|
|
48
|
-
},
|
|
49
|
-
}) };
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
return { kind: 'threw', error };
|
|
53
|
-
}
|
|
54
|
-
})()]);
|
|
55
|
-
// Freeze before persistence. Late policy results/sender completions cannot rewrite it.
|
|
56
|
-
completion = observation.kind === 'timeout' ? { outcome: 'unknown', reason: 'timeout' }
|
|
57
|
-
: denied ? { outcome: 'released', reason: 'not-current', phase: 'final' }
|
|
58
|
-
: observation.kind === 'threw' ? { outcome: 'unknown', reason: 'threw', error: observation.error }
|
|
59
|
-
: observation.result.outcome === 'not-capable'
|
|
60
|
-
? { outcome: 'released', reason: 'not-capable', data: observation.result.data }
|
|
61
|
-
: { outcome: observation.result.outcome, reason: 'returned', data: observation.result.data };
|
|
62
|
-
}
|
|
63
|
-
finally {
|
|
64
|
-
if (timer !== undefined)
|
|
65
|
-
clearTimeout(timer);
|
|
66
|
-
}
|
|
67
|
-
return { observation, settlement: await settle(port, claim, completion) };
|
|
68
|
-
}
|
|
69
|
-
const preparedBrand = Symbol('prepared presentation');
|
|
70
|
-
const custody = new WeakMap();
|
|
71
|
-
export async function preparePresentation(work, control) {
|
|
72
|
-
if (control?.signal?.aborted)
|
|
73
|
-
return undefined;
|
|
74
|
-
const claim = await work.port.claim(control?.signal);
|
|
75
|
-
if (claim === undefined)
|
|
76
|
-
return undefined;
|
|
77
|
-
let resolve;
|
|
78
|
-
let decided = false;
|
|
79
|
-
const state = {
|
|
80
|
-
owned: false, coverage: work.coverage,
|
|
81
|
-
eligible: signal => signal?.aborted ? Promise.resolve(false) : current(work.port, signal),
|
|
82
|
-
done: new Promise(res => { resolve = res; }),
|
|
83
|
-
finish(completion) {
|
|
84
|
-
if (decided)
|
|
85
|
-
return;
|
|
86
|
-
decided = true;
|
|
87
|
-
// Defer callbacks: every included handle is marked before any settlement starts.
|
|
88
|
-
void Promise.resolve().then(() => settle(work.port, claim, completion)).then(resolve);
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
const prepared = Object.freeze({
|
|
92
|
-
[preparedBrand]: true,
|
|
93
|
-
payload: work.payload,
|
|
94
|
-
eligible: (signal) => state.owned ? Promise.resolve(false) : state.eligible(signal),
|
|
95
|
-
release() {
|
|
96
|
-
if (!state.owned) {
|
|
97
|
-
state.owned = true;
|
|
98
|
-
state.finish({ outcome: 'released', reason: 'unused' });
|
|
99
|
-
}
|
|
100
|
-
return state.done;
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
custody.set(prepared, state);
|
|
104
|
-
return prepared;
|
|
105
|
-
}
|
|
106
|
-
export function presentPrepared(prepared, sink, control) {
|
|
107
|
-
return presentPreparedBatch([prepared], eligible => ({ payload: prepared.payload, included: eligible }), sink, control);
|
|
108
|
-
}
|
|
109
|
-
/** Takes all local custody synchronously, then owns eligibility/effect/settlement/cleanup. */
|
|
110
|
-
export async function presentPreparedBatch(prepared, render, sink, control = {}) {
|
|
111
|
-
const handles = [...prepared];
|
|
112
|
-
const states = handles.map(handle => custody.get(handle));
|
|
113
|
-
if (new Set(handles).size !== handles.length || states.some(state => state === undefined || state.owned)) {
|
|
114
|
-
await Promise.all([...new Set(handles)].filter(handle => custody.has(handle) && !custody.get(handle).owned).map(handle => handle.release()));
|
|
115
|
-
throw new TypeError('Presentation requires unique, unused prepared custody.');
|
|
116
|
-
}
|
|
117
|
-
const owned = states;
|
|
118
|
-
for (const state of owned)
|
|
119
|
-
state.owned = true;
|
|
120
|
-
let observation = { kind: 'not-invoked' };
|
|
121
|
-
try {
|
|
122
|
-
const eligible = [];
|
|
123
|
-
for (const [index, state] of owned.entries()) {
|
|
124
|
-
if (await state.eligible(control.signal))
|
|
125
|
-
eligible.push(handles[index]);
|
|
126
|
-
else
|
|
127
|
-
state.finish({ outcome: 'released', reason: control.signal?.aborted ? 'cancelled' : 'not-current' });
|
|
128
|
-
}
|
|
129
|
-
const rendered = render(Object.freeze(eligible));
|
|
130
|
-
const included = [...rendered.included];
|
|
131
|
-
if (new Set(included).size !== included.length || included.some(handle => !eligible.includes(handle)))
|
|
132
|
-
throw new TypeError('Included presentations must be a unique subset of eligible custody.');
|
|
133
|
-
if (included.length > 0 && !control.signal?.aborted) {
|
|
134
|
-
let completion;
|
|
135
|
-
try {
|
|
136
|
-
const value = await sink(rendered.payload);
|
|
137
|
-
observation = { kind: 'returned', value };
|
|
138
|
-
completion = { outcome: 'presented', reason: 'returned' };
|
|
139
|
-
}
|
|
140
|
-
catch (error) {
|
|
141
|
-
let outcome = 'unknown';
|
|
142
|
-
try {
|
|
143
|
-
if (control.classifyError?.(error) === 'failed')
|
|
144
|
-
outcome = 'failed';
|
|
145
|
-
}
|
|
146
|
-
catch { /* conservative */ }
|
|
147
|
-
observation = { kind: 'threw', error, outcome };
|
|
148
|
-
completion = { outcome, reason: 'threw', error };
|
|
149
|
-
}
|
|
150
|
-
for (const handle of included) {
|
|
151
|
-
const state = custody.get(handle);
|
|
152
|
-
state.finish(completion.outcome === 'presented'
|
|
153
|
-
? { outcome: state.coverage === 'full' ? 'presented' : 'clipped', reason: 'returned' } : completion);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
finally {
|
|
158
|
-
for (const state of owned)
|
|
159
|
-
state.finish({ outcome: 'released', reason: control.signal?.aborted ? 'cancelled' : 'unused' });
|
|
160
|
-
await Promise.all(owned.map(state => state.done));
|
|
161
|
-
}
|
|
162
|
-
return { observation, settlements: await Promise.all(handles.map(async (handle, index) => ({ prepared: handle, result: await owned[index].done }))) };
|
|
163
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { type DirectedNotificationRoute } from './delivery.js';
|
|
2
|
-
import type { DeliveryEvidence } from './ports.js';
|
|
3
|
-
export interface WakeAttempt {
|
|
4
|
-
readonly nativeObservations?: readonly NonNullable<DeliveryEvidence['nativeObservations']>[number][];
|
|
5
|
-
readonly at: number;
|
|
6
|
-
readonly attention: {
|
|
7
|
-
readonly squarePath: string;
|
|
8
|
-
readonly recipient: string;
|
|
9
|
-
readonly messageIndex: number;
|
|
10
|
-
};
|
|
11
|
-
readonly routeKind: import('./model.js').WakeRouteKind;
|
|
12
|
-
readonly outcome: 'accepted' | 'unknown' | 'failed';
|
|
13
|
-
readonly signature?: string;
|
|
14
|
-
readonly attemptN: number;
|
|
15
|
-
readonly message?: string;
|
|
16
|
-
readonly diagnostic?: unknown;
|
|
17
|
-
}
|
|
18
|
-
export interface WakeReleaseDiagnostic {
|
|
19
|
-
readonly at: number;
|
|
20
|
-
readonly attention: {
|
|
21
|
-
readonly squarePath: string;
|
|
22
|
-
readonly recipient: string;
|
|
23
|
-
readonly messageIndex: number;
|
|
24
|
-
};
|
|
25
|
-
readonly routeKind?: import('./model.js').WakeRouteKind;
|
|
26
|
-
readonly attemptN?: number;
|
|
27
|
-
readonly session?: string;
|
|
28
|
-
readonly signature?: string;
|
|
29
|
-
readonly message?: string;
|
|
30
|
-
readonly diagnostic?: unknown;
|
|
31
|
-
}
|
|
32
|
-
export type DeliveryHealthKind = 'awaiting' | 'wake-accepted' | 'wake-unknown' | 'presented-not-delivered' | 'unreachable';
|
|
33
|
-
export interface DeliveryHealthItem {
|
|
34
|
-
squarePath: string;
|
|
35
|
-
recipient: string;
|
|
36
|
-
messageIndex: number;
|
|
37
|
-
actor: string;
|
|
38
|
-
at: number;
|
|
39
|
-
ageMs: number;
|
|
40
|
-
route?: DirectedNotificationRoute;
|
|
41
|
-
kind: DeliveryHealthKind;
|
|
42
|
-
attempt?: WakeAttempt;
|
|
43
|
-
}
|
|
44
|
-
/** Classify current pending attention from the artifact's seats and delivery evidence. */
|
|
45
|
-
export declare function classifyDeliveryHealth(squarePath: string, opts: {
|
|
46
|
-
graceMs: number;
|
|
47
|
-
now?: number;
|
|
48
|
-
env?: NodeJS.ProcessEnv;
|
|
49
|
-
}): Promise<DeliveryHealthItem[]>;
|
|
50
|
-
export declare function renderDeliveryHealth(items: readonly DeliveryHealthItem[], releases?: readonly WakeReleaseDiagnostic[], now?: number, nativeAttempts?: readonly WakeAttempt[]): string[];
|
|
51
|
-
export declare function doctorDeliveryHealth(squarePath: string, graceMs: number, now?: number, env?: NodeJS.ProcessEnv): Promise<string[]>;
|
package/dist/delivery-health.js
DELETED
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { collectPending } from './delivery.js';
|
|
2
|
-
import { formatMessageId, parseMessageId } from './message.js';
|
|
3
|
-
import { participantIdentity, truncateChars } from './presentation.js';
|
|
4
|
-
import { formatDuration } from './time.js';
|
|
5
|
-
import { InternalSquareError, SquareError } from './model.js';
|
|
6
|
-
import { createArtifactPort } from './artifact.js';
|
|
7
|
-
const DISPLAY_ORDER = [
|
|
8
|
-
'awaiting',
|
|
9
|
-
'wake-accepted',
|
|
10
|
-
'wake-unknown',
|
|
11
|
-
'presented-not-delivered',
|
|
12
|
-
'unreachable',
|
|
13
|
-
];
|
|
14
|
-
const ACTIONABLE = new Set(['wake-unknown', 'unreachable']);
|
|
15
|
-
const DETAIL_LIMIT = 20;
|
|
16
|
-
const DISPLAY_FIELD_LIMIT = 160;
|
|
17
|
-
/** Classify current pending attention from the artifact's seats and delivery evidence. */
|
|
18
|
-
export async function classifyDeliveryHealth(squarePath, opts) {
|
|
19
|
-
const now = opts.now ?? Date.now();
|
|
20
|
-
const artifact = createArtifactPort(squarePath);
|
|
21
|
-
try {
|
|
22
|
-
const { pending } = await collectPending(artifact);
|
|
23
|
-
const current = (await artifact.read((facts) => pending.map((note) => {
|
|
24
|
-
const seat = facts.seat(note.recipient);
|
|
25
|
-
const evidence = seat === undefined ? [] : facts.deliveryEvidence(seat.participant, seat.generation, formatMessageId(note.index));
|
|
26
|
-
return { note, seat, evidence };
|
|
27
|
-
}))).value;
|
|
28
|
-
const items = [];
|
|
29
|
-
for (const { note, seat, evidence } of current) {
|
|
30
|
-
const ageMs = Math.max(0, now - note.at);
|
|
31
|
-
const presentation = evidence.some((row) => row.kind === 'presentation' && (row.outcome === 'presented' || row.outcome === 'clipped' || row.outcome === 'unknown'
|
|
32
|
-
|| row.outcome === 'dispatching'));
|
|
33
|
-
const terminal = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown'));
|
|
34
|
-
const attemptRow = evidence.findLast((row) => row.kind === 'wake' && (row.outcome === 'accepted' || row.outcome === 'unknown' || row.outcome === 'failed'));
|
|
35
|
-
const attempt = attemptRow === undefined || seat === undefined ? undefined : deliveryAttempt(squarePath, attemptRow, { squarePath, recipient: seat.participant, messageIndex: note.index });
|
|
36
|
-
const kind = presentation
|
|
37
|
-
? 'presented-not-delivered'
|
|
38
|
-
: terminal?.outcome === 'accepted'
|
|
39
|
-
? 'wake-accepted'
|
|
40
|
-
: terminal?.outcome === 'unknown'
|
|
41
|
-
? 'wake-unknown'
|
|
42
|
-
: ageMs > opts.graceMs && seat?.wakeAddress === undefined
|
|
43
|
-
? 'unreachable'
|
|
44
|
-
: 'awaiting';
|
|
45
|
-
items.push({
|
|
46
|
-
squarePath,
|
|
47
|
-
recipient: note.recipient,
|
|
48
|
-
messageIndex: note.index,
|
|
49
|
-
actor: note.actor,
|
|
50
|
-
at: note.at,
|
|
51
|
-
ageMs,
|
|
52
|
-
route: note.route,
|
|
53
|
-
kind,
|
|
54
|
-
...(attempt === undefined ? {} : { attempt }),
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
// The captured pending set is the ownership boundary: a candidate that was
|
|
58
|
-
// not pending at the scan cannot become a health item during this read.
|
|
59
|
-
return items;
|
|
60
|
-
}
|
|
61
|
-
catch (error) {
|
|
62
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
63
|
-
throw new SquareError('unavailable', `Square is unavailable at ${squarePath}`);
|
|
64
|
-
throw error;
|
|
65
|
-
}
|
|
66
|
-
finally {
|
|
67
|
-
await artifact.close();
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function displayField(value) {
|
|
71
|
-
const truncated = truncateChars(value, DISPLAY_FIELD_LIMIT - 1);
|
|
72
|
-
return truncated.remaining === 0 ? truncated.text : `${truncated.text}…`;
|
|
73
|
-
}
|
|
74
|
-
export function renderDeliveryHealth(items, releases = [], now = Date.now(), nativeAttempts = []) {
|
|
75
|
-
const out = items.length === 0 ? ['✓ no pending delivery attention'] : [`· delivery attention · ${items.length} pending`];
|
|
76
|
-
if (items.length > 0) {
|
|
77
|
-
let displayed = 0;
|
|
78
|
-
for (const kind of DISPLAY_ORDER) {
|
|
79
|
-
const group = items.filter((item) => item.kind === kind);
|
|
80
|
-
if (group.length === 0)
|
|
81
|
-
continue;
|
|
82
|
-
out.push(`${ACTIONABLE.has(kind) ? '✕' : '○'} ${kind}: ${group.length}`);
|
|
83
|
-
const details = group.slice(0, DETAIL_LIMIT - displayed);
|
|
84
|
-
out.push(...details.map((item) => {
|
|
85
|
-
const evidence = item.attempt?.signature === undefined ? '' : ` · ${displayField(item.attempt.signature)}`;
|
|
86
|
-
return ` · ${formatMessageId(item.messageIndex)} → ${displayField(participantIdentity(item.recipient))} from ${displayField(participantIdentity(item.actor))} · ${formatDuration(item.ageMs)}${evidence}`;
|
|
87
|
-
}));
|
|
88
|
-
displayed += details.length;
|
|
89
|
-
}
|
|
90
|
-
if (items.length > DETAIL_LIMIT)
|
|
91
|
-
out.push(`${DETAIL_LIMIT} of ${items.length} pending details shown`);
|
|
92
|
-
}
|
|
93
|
-
if (nativeAttempts.length > 0) {
|
|
94
|
-
out.push(`· Claude native custody · ${nativeAttempts.length}`);
|
|
95
|
-
out.push(...nativeAttempts.slice(0, DETAIL_LIMIT).map((attempt) => {
|
|
96
|
-
const observations = [...attempt.nativeObservations ?? []].sort((a, b) => a.observedAt - b.observedAt);
|
|
97
|
-
const last = observations.at(-1);
|
|
98
|
-
const states = observations.map((observation) => `${observation.state}${observation.reason ? ` (${observation.reason})` : ''}`).join(' → ');
|
|
99
|
-
return ` · ${formatMessageId(attempt.attention.messageIndex)} → ${displayField(participantIdentity(attempt.attention.recipient))} · ${attempt.outcome} · native ${displayField(states)} · ${formatDuration(Math.max(0, now - last.observedAt))} · message ${displayField(last.messageId)}`;
|
|
100
|
-
}));
|
|
101
|
-
if (nativeAttempts.length > DETAIL_LIMIT)
|
|
102
|
-
out.push(`${DETAIL_LIMIT} of ${nativeAttempts.length} native custody details shown`);
|
|
103
|
-
}
|
|
104
|
-
if (releases.length > 0) {
|
|
105
|
-
out.push(`· recent wake releases · ${releases.length}`);
|
|
106
|
-
out.push(...releases.slice(0, DETAIL_LIMIT).map((release) => formatRelease(release, now)));
|
|
107
|
-
if (releases.length > DETAIL_LIMIT)
|
|
108
|
-
out.push(`${DETAIL_LIMIT} of ${releases.length} wake releases shown`);
|
|
109
|
-
}
|
|
110
|
-
return out;
|
|
111
|
-
}
|
|
112
|
-
function formatRelease(release, now) {
|
|
113
|
-
const route = displayField(release.routeKind ?? 'unknown route');
|
|
114
|
-
const reason = displayField(release.signature ?? 'reason not recorded');
|
|
115
|
-
const age = formatDuration(Math.max(0, now - release.at));
|
|
116
|
-
const message = release.message === undefined ? '' : ` — ${displayField(release.message)}`;
|
|
117
|
-
const diagnostic = release.diagnostic === undefined ? '' : ` · ${displayField(JSON.stringify(release.diagnostic))}`;
|
|
118
|
-
return ` · ${formatMessageId(release.attention.messageIndex)} → ${displayField(participantIdentity(release.attention.recipient))} · ${route} · ${reason} · ${age}${message}${diagnostic}`;
|
|
119
|
-
}
|
|
120
|
-
function deliveryAttempt(squarePath, row, attention = { squarePath, recipient: row.participant, messageIndex: parseMessageId(row.message) ?? 0 }) {
|
|
121
|
-
if (row.route?.kind === undefined || row.outcome === 'released' || row.outcome === 'dispatching')
|
|
122
|
-
return undefined;
|
|
123
|
-
return {
|
|
124
|
-
at: row.at,
|
|
125
|
-
attention,
|
|
126
|
-
routeKind: row.route.kind,
|
|
127
|
-
outcome: row.outcome,
|
|
128
|
-
attemptN: row.attemptN,
|
|
129
|
-
...(row.signature === undefined ? {} : { signature: row.signature }),
|
|
130
|
-
...(row.messageText === undefined ? {} : { message: row.messageText }),
|
|
131
|
-
...(row.diagnostic === undefined ? {} : { diagnostic: row.diagnostic }),
|
|
132
|
-
...(row.nativeObservations === undefined ? {} : { nativeObservations: row.nativeObservations }),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
function deliveryRelease(squarePath, row) {
|
|
136
|
-
return {
|
|
137
|
-
at: row.at,
|
|
138
|
-
attention: { squarePath, recipient: row.participant, messageIndex: parseMessageId(row.message) ?? 0 },
|
|
139
|
-
routeKind: row.route?.kind,
|
|
140
|
-
attemptN: row.attemptN,
|
|
141
|
-
...(row.signature === undefined ? {} : { signature: row.signature }),
|
|
142
|
-
...(row.messageText === undefined ? {} : { message: row.messageText }),
|
|
143
|
-
...(row.diagnostic === undefined ? {} : { diagnostic: row.diagnostic }),
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
export async function doctorDeliveryHealth(squarePath, graceMs, now = Date.now(), env = process.env) {
|
|
147
|
-
const items = await classifyDeliveryHealth(squarePath, { graceMs, now, env });
|
|
148
|
-
const artifact = createArtifactPort(squarePath);
|
|
149
|
-
try {
|
|
150
|
-
const { value: rows } = await artifact.read((facts) => facts.roster().flatMap((participant) => {
|
|
151
|
-
const seat = facts.seat(participant.name);
|
|
152
|
-
return seat === undefined ? [] : facts.deliveryEvidence(seat.participant, seat.generation);
|
|
153
|
-
}));
|
|
154
|
-
const releases = rows.filter((row) => row.kind === 'wake' && row.outcome === 'released')
|
|
155
|
-
.map((row) => deliveryRelease(squarePath, row))
|
|
156
|
-
.sort((left, right) => right.at - left.at);
|
|
157
|
-
const nativeAttempts = rows.flatMap((row) => {
|
|
158
|
-
if (row.kind !== 'wake' || row.nativeObservations?.length === 0)
|
|
159
|
-
return [];
|
|
160
|
-
const attempt = deliveryAttempt(squarePath, row);
|
|
161
|
-
return attempt?.nativeObservations?.length ? [attempt] : [];
|
|
162
|
-
}).sort((a, b) => Math.max(...b.nativeObservations.map((row) => row.observedAt)) - Math.max(...a.nativeObservations.map((row) => row.observedAt)));
|
|
163
|
-
return renderDeliveryHealth(items, releases, now, nativeAttempts);
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
166
|
-
if (error instanceof InternalSquareError && error.code === 'not_found')
|
|
167
|
-
throw new SquareError('unavailable', `Square is unavailable at ${squarePath}`);
|
|
168
|
-
throw error;
|
|
169
|
-
}
|
|
170
|
-
finally {
|
|
171
|
-
await artifact.close();
|
|
172
|
-
}
|
|
173
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { ChannelArtifact } from './channel-ports.js';
|
|
2
|
-
import type { WakeProjection, WakeProjectionContext, WakeRequest, WakeTransportPort } from './channel-transport.js';
|
|
3
|
-
interface DeliverPendingInput {
|
|
4
|
-
readonly artifact: ChannelArtifact;
|
|
5
|
-
readonly preparePresentation?: WakeProjection;
|
|
6
|
-
readonly transport: WakeTransportPort;
|
|
7
|
-
readonly location: string;
|
|
8
|
-
readonly timeoutMs?: number;
|
|
9
|
-
readonly now?: number;
|
|
10
|
-
readonly message?: string | number;
|
|
11
|
-
}
|
|
12
|
-
/** Standalone Channel projection; final send currentness is still checked by the dispatcher. */
|
|
13
|
-
export declare function channelWakePreview(artifact: ChannelArtifact, request: WakeProjectionContext, signal?: AbortSignal): Promise<NonNullable<WakeRequest['presentation']> | undefined>;
|
|
14
|
-
export declare function deliverPending(input: DeliverPendingInput): Promise<void>;
|
|
15
|
-
export declare function sweepPending(input: {
|
|
16
|
-
readonly artifact: ChannelArtifact;
|
|
17
|
-
readonly location: string;
|
|
18
|
-
readonly now: number;
|
|
19
|
-
readonly graceMs: number;
|
|
20
|
-
readonly limit: number;
|
|
21
|
-
}): Promise<number[]>;
|
|
22
|
-
export {};
|