@astrosheep/square 0.4.3 → 0.4.5
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-store.d.ts +1 -1
- package/dist/artifact-store.js +32 -15
- package/dist/artifact.d.ts +1 -4
- package/dist/artifact.js +0 -28
- package/dist/attention-presentation.js +1 -1
- package/dist/boundary-presentation.js +2 -2
- package/dist/claude-boundary-state.d.ts +6 -6
- package/dist/claude-boundary-state.js +13 -13
- package/dist/claude-hook.js +26 -25
- package/dist/claude-mcp-context.d.ts +2 -2
- package/dist/claude-mcp-context.js +6 -6
- package/dist/cli/context.js +3 -6
- package/dist/cli/json.js +1 -1
- package/dist/cli/mcp-server-command.js +3 -3
- package/dist/cli/observation-commands.d.ts +2 -2
- package/dist/cli/observation-commands.js +23 -51
- package/dist/cli/perceive-presentation.d.ts +3 -0
- package/dist/cli/{catch-presentation.js → perceive-presentation.js} +3 -3
- package/dist/cli/registry.js +2 -2
- package/dist/codex-address.d.ts +3 -0
- package/dist/codex-address.js +7 -0
- package/dist/codex-hook.d.ts +2 -2
- package/dist/codex-hook.js +29 -39
- package/dist/codex-queue.d.ts +3 -3
- package/dist/codex-queue.js +9 -15
- package/dist/codex-watch.d.ts +6 -1
- package/dist/codex-watch.js +38 -42
- package/dist/delivery-ledger-sqlite.d.ts +25 -20
- package/dist/delivery-ledger-sqlite.js +76 -61
- package/dist/delivery-ledger.d.ts +24 -19
- package/dist/delivery-ledger.js +150 -81
- package/dist/delivery-lifetime.d.ts +2 -2
- package/dist/delivery-lifetime.js +6 -18
- package/dist/help.js +12 -12
- package/dist/hints.d.ts +2 -2
- package/dist/hints.js +7 -7
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/list.d.ts +2 -2
- package/dist/list.js +4 -3
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +8 -32
- package/dist/neutral.d.ts +5 -5
- package/dist/notifications.js +12 -6
- package/dist/operation-presentation.d.ts +8 -3
- package/dist/operation-presentation.js +50 -12
- package/dist/packages/agent-delivery/src/codex.d.ts +4 -0
- package/dist/packages/agent-delivery/src/codex.js +18 -13
- package/dist/packages/agent-delivery/src/file-lock.js +4 -1
- package/dist/perceive-decisions.d.ts +28 -0
- package/dist/{catch-decisions.js → perceive-decisions.js} +24 -24
- package/dist/{catch-selection.d.ts → perceive-selection.d.ts} +2 -2
- package/dist/{catch-selection.js → perceive-selection.js} +3 -3
- package/dist/plugins/opencode/index.js +4 -4
- package/dist/plugins/opencode-shared/delivery.d.ts +6 -6
- package/dist/plugins/opencode-shared/delivery.js +27 -21
- package/dist/plugins/opencode-v1/runtime.js +3 -3
- package/dist/ports.d.ts +0 -4
- package/dist/presentation.d.ts +1 -1
- package/dist/presentation.js +13 -13
- package/dist/session-lifecycle.d.ts +2 -2
- package/dist/session-lifecycle.js +11 -11
- package/dist/sqlite-square.d.ts +2 -4
- package/dist/sqlite-square.js +47 -54
- package/dist/sqlite-transaction.d.ts +2 -0
- package/dist/sqlite-transaction.js +25 -3
- package/dist/square-api.d.ts +1 -1
- package/dist/square-api.js +1 -1
- package/dist/square-domain.js +1 -1
- package/dist/square-facade.d.ts +15 -13
- package/dist/square-location.js +1 -1
- package/dist/square-posting.d.ts +5 -5
- package/dist/square-posting.js +39 -38
- package/dist/square-tools.d.ts +15 -4
- package/dist/square-tools.js +35 -17
- package/dist/views.d.ts +7 -3
- package/dist/views.js +6 -3
- package/guides/architect.md +2 -2
- package/guides/change-waits.md +1 -1
- package/guides/claude-native.md +2 -2
- package/guides/codex-delivery.md +91 -57
- package/guides/delivery-primitives.md +26 -10
- package/guides/mcp.md +4 -2
- package/guides/operation-cancellation.md +11 -3
- package/guides/pi-native-delivery.md +1 -1
- package/guides/pi-tools.md +11 -6
- package/guides/receive-primitive.md +3 -3
- package/guides/square-api.md +15 -8
- package/package.json +2 -2
- package/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/plugins/claude/hooks/hooks.json +2 -2
- package/plugins/claude/skills/square/SKILL.md +10 -10
- package/plugins/codex/.codex-plugin/plugin.json +2 -2
- package/plugins/codex/hooks/hooks.json +1 -7
- package/plugins/opencode/index.ts +4 -4
- package/plugins/opencode-shared/delivery.ts +27 -21
- package/plugins/opencode-v1/runtime.ts +3 -3
- package/plugins/pi/square-pi.js +18 -12
- package/skills/square/SKILL.md +10 -10
- package/dist/catch-decisions.d.ts +0 -28
- package/dist/cli/catch-presentation.d.ts +0 -3
- package/dist/codex-boundary-state.d.ts +0 -2
- package/dist/codex-boundary-state.js +0 -6
- package/guides/mcp-connection-lifetime.md +0 -22
|
@@ -4,12 +4,11 @@ import { runClaudeHook, runClaudeWatch } from '../claude-hook.js';
|
|
|
4
4
|
import { runCodexHook, runCodexInterrupt } from '../codex-hook.js';
|
|
5
5
|
import { runCodexWatch } from '../codex-watch.js';
|
|
6
6
|
import { isSquareError } from '../model.js';
|
|
7
|
-
import { commandPrefix,
|
|
8
|
-
import { PARTICIPANTS_DEFAULT_LIMIT, PARTICIPANTS_MAX_LIMIT, renderHistoryResult, renderParticipantsResult, renderStatusResult, } from '../operation-presentation.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { renderCatchOutcome, renderCatchProgress } from './catch-presentation.js';
|
|
7
|
+
import { commandPrefix, truncateChars, withPathOutput, quoteShell, } from '../presentation.js';
|
|
8
|
+
import { PARTICIPANTS_DEFAULT_LIMIT, PARTICIPANTS_MAX_LIMIT, renderHistoryResult, renderParticipantsResult, renderStatusResult, renderSquareList, } from '../operation-presentation.js';
|
|
9
|
+
import { formatTimestamp, parseTimeOrRelative } from '../time.js';
|
|
10
|
+
import { discover, inbox, formatActivityId, parseActivityId, PERCEIVE_DEFAULT_LIMIT, PERCEIVE_MAX_LIMIT, HISTORY_MAX_LIMIT, DEFAULT_LIST_DEPTH, MAX_LIST_DEPTH, DEFAULT_LIST_LIMIT, MAX_LIST_LIMIT } from '../square-api.js';
|
|
11
|
+
import { renderPerceiveOutcome, renderPerceiveProgress } from './perceive-presentation.js';
|
|
13
12
|
import { selectOwnSeat } from '../square-api.js';
|
|
14
13
|
import { jsonResult } from './json.js';
|
|
15
14
|
import { withSquare, squareInput, now, fail, parseBoundedLimit, parseDurationMs, parseNameList, parseNonNegativeInteger, readStdin, requireSquarePath, requireValue, usage, commandIdentity, } from './context.js';
|
|
@@ -20,38 +19,6 @@ function inboxDisplay(value) {
|
|
|
20
19
|
const truncated = truncateChars(value, INBOX_DISPLAY_CHARS - 1);
|
|
21
20
|
return truncated.remaining === 0 ? truncated.text : `${truncated.text}…`;
|
|
22
21
|
}
|
|
23
|
-
function renderSquareList(data, projection) {
|
|
24
|
-
const { items, stopped, remaining, next } = data;
|
|
25
|
-
if (data.total === 0) {
|
|
26
|
-
return ['(no squares found)', ...(stopped ? [style('dim', '○ the walk stopped early — deeper squares may still be hiding')] : [])].join('\n') + '\n';
|
|
27
|
-
}
|
|
28
|
-
const lines = ['squares'];
|
|
29
|
-
for (const item of items) {
|
|
30
|
-
const glyph = item.activities > 0 ? '●' : style('dim', '○');
|
|
31
|
-
lines.push(`${glyph} ${inboxDisplay(item.path)}${style('dim', ` · ${formatRelativeTime(item.lastActiveAt, projection.now)} · ${item.participants.length} in the square · ${item.activities} ${item.activities === 1 ? 'activity' : 'activities'}`)}`);
|
|
32
|
-
const context = item.context.slice(0, 2);
|
|
33
|
-
if (context.length === 0)
|
|
34
|
-
lines.push(` ${style('dim', 'context')} · (none)`);
|
|
35
|
-
else {
|
|
36
|
-
context.forEach((line, index) => lines.push(` ${index === 0 ? style('dim', 'context') : ' '} · ${inboxDisplay(line)}`));
|
|
37
|
-
const hidden = item.context.length - context.length;
|
|
38
|
-
if (hidden > 0)
|
|
39
|
-
lines.push(style('dim', ` · … ${hidden} more ${hidden === 1 ? 'line' : 'lines'}`));
|
|
40
|
-
}
|
|
41
|
-
const participants = item.participants.slice(0, 3);
|
|
42
|
-
const hidden = item.participants.length - participants.length;
|
|
43
|
-
const names = participants.length === 0 ? 'nobody' : participants.map((name) => participantIdentity(inboxDisplay(name))).join(' · ');
|
|
44
|
-
lines.push(` ${style('dim', 'participants')} · ${names}${hidden > 0 ? style('dim', ` · … ${hidden} more`) : ''}`);
|
|
45
|
-
}
|
|
46
|
-
if (remaining > 0) {
|
|
47
|
-
lines.push(style('dim', stopped ? ' … more squares' : ` … ${remaining} more ${remaining === 1 ? 'square' : 'squares'}`));
|
|
48
|
-
if (next !== undefined)
|
|
49
|
-
lines.push(`square list --depth ${projection.depth} --limit ${projection.limit} --after ${quoteShell(next)}`);
|
|
50
|
-
}
|
|
51
|
-
if (stopped)
|
|
52
|
-
lines.push(style('dim', '○ the walk stopped early — deeper squares may still be hiding'));
|
|
53
|
-
return lines.join('\n') + '\n';
|
|
54
|
-
}
|
|
55
22
|
export const listCommand = {
|
|
56
23
|
parse(argv, context) {
|
|
57
24
|
let depth = DEFAULT_LIST_DEPTH;
|
|
@@ -81,7 +48,12 @@ export const listCommand = {
|
|
|
81
48
|
return { depth, limit, after };
|
|
82
49
|
},
|
|
83
50
|
async execute(intent, context) {
|
|
84
|
-
|
|
51
|
+
let owner;
|
|
52
|
+
try {
|
|
53
|
+
owner = commandIdentity(context.env, context.owner).owner;
|
|
54
|
+
}
|
|
55
|
+
catch { /* Discovery stays available without a complete harness identity. */ }
|
|
56
|
+
const result = await discover(squareInput(context), { ...intent, owner });
|
|
85
57
|
return renderSquareList(result, { now: now(context), depth: intent.depth ?? DEFAULT_LIST_DEPTH, limit: intent.limit ?? DEFAULT_LIST_LIMIT });
|
|
86
58
|
},
|
|
87
59
|
present: (result) => process.stdout.write(result),
|
|
@@ -156,7 +128,7 @@ export const streamCommand = {
|
|
|
156
128
|
},
|
|
157
129
|
present: () => { },
|
|
158
130
|
};
|
|
159
|
-
export const
|
|
131
|
+
export const perceiveCommand = {
|
|
160
132
|
parse(argv, context) {
|
|
161
133
|
let idle;
|
|
162
134
|
let mention;
|
|
@@ -168,10 +140,10 @@ export const catchCommand = {
|
|
|
168
140
|
for (let index = 0; index < argv.length; index++) {
|
|
169
141
|
if (argv[index] === '--id') {
|
|
170
142
|
if (id !== undefined)
|
|
171
|
-
fail('✕
|
|
143
|
+
fail('✕ perceive accepts one --id\nsquare perceive --help');
|
|
172
144
|
const value = requireValue(argv, index, '--id');
|
|
173
145
|
if (parseActivityId(value) === undefined)
|
|
174
|
-
fail('✕ invalid --id: expected an activity id like act/12\nsquare
|
|
146
|
+
fail('✕ invalid --id: expected an activity id like act/12\nsquare perceive --help');
|
|
175
147
|
id = value;
|
|
176
148
|
index += 1;
|
|
177
149
|
}
|
|
@@ -187,7 +159,7 @@ export const catchCommand = {
|
|
|
187
159
|
mention = true;
|
|
188
160
|
}
|
|
189
161
|
else if (argv[index] === '--limit') {
|
|
190
|
-
limit = parseBoundedLimit(argv[index + 1], '--limit',
|
|
162
|
+
limit = parseBoundedLimit(argv[index + 1], '--limit', PERCEIVE_MAX_LIMIT, `${commandPrefix(requireSquarePath(context))} perceive --limit ${PERCEIVE_MAX_LIMIT}`);
|
|
191
163
|
index += 1;
|
|
192
164
|
}
|
|
193
165
|
else if (argv[index] === '--replace')
|
|
@@ -195,22 +167,22 @@ export const catchCommand = {
|
|
|
195
167
|
else if (argv[index] === '--now')
|
|
196
168
|
now = true;
|
|
197
169
|
else if (argv[index] !== '--json')
|
|
198
|
-
fail(`✕
|
|
170
|
+
fail(`✕ perceive does not know ${argv[index]}\nsquare perceive --help`);
|
|
199
171
|
}
|
|
200
172
|
if (id !== undefined) {
|
|
201
173
|
if (argv.some((flag) => ['--idle', '--from', '--mention', '--limit', '--replace'].includes(flag))) {
|
|
202
|
-
fail('✕ --id cannot be combined with --idle, --from, --mention, --limit or --replace\nsquare
|
|
174
|
+
fail('✕ --id cannot be combined with --idle, --from, --mention, --limit or --replace\nsquare perceive --help');
|
|
203
175
|
}
|
|
204
176
|
return { id };
|
|
205
177
|
}
|
|
206
178
|
if (idle !== undefined && now)
|
|
207
|
-
fail('
|
|
179
|
+
fail('perceive accepts --now or --idle, not both.');
|
|
208
180
|
if (replace && idle === undefined)
|
|
209
181
|
fail('--replace can only be used with --idle.');
|
|
210
182
|
return {
|
|
211
183
|
...(participants.length > 0 ? { from: participants } : {}),
|
|
212
184
|
...(mention === undefined ? {} : { mention }),
|
|
213
|
-
limit: limit ??
|
|
185
|
+
limit: limit ?? PERCEIVE_DEFAULT_LIMIT,
|
|
214
186
|
...(idle === undefined ? {} : { idle }),
|
|
215
187
|
...(replace ? { replace } : {}),
|
|
216
188
|
};
|
|
@@ -221,10 +193,10 @@ export const catchCommand = {
|
|
|
221
193
|
const interrupt = () => controller.abort();
|
|
222
194
|
process.once('SIGINT', interrupt);
|
|
223
195
|
try {
|
|
224
|
-
const outcome = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).
|
|
196
|
+
const outcome = await withSquare(context, async (square) => (await selectOwnSeat(square, commandIdentity(context.env, context.owner).owner, context.name)).perceive({ ...intent, ...(context.json ? {} : { observe: true }) }, {
|
|
225
197
|
signal: controller.signal,
|
|
226
198
|
onProgress(progress) {
|
|
227
|
-
const line =
|
|
199
|
+
const line = renderPerceiveProgress(squarePath, progress);
|
|
228
200
|
if (line !== undefined)
|
|
229
201
|
process.stderr.write(line);
|
|
230
202
|
},
|
|
@@ -234,7 +206,7 @@ export const catchCommand = {
|
|
|
234
206
|
process.exitCode = outcome.reason === 'already-active' || outcome.reason === 'capped' ? 1 : 0;
|
|
235
207
|
}
|
|
236
208
|
else {
|
|
237
|
-
const rendered =
|
|
209
|
+
const rendered = renderPerceiveOutcome(squarePath, outcome, intent, context.owner ?? context.env.SQUARE_OWNER);
|
|
238
210
|
process.stdout.write(rendered.stdout);
|
|
239
211
|
process.exitCode = rendered.exitCode;
|
|
240
212
|
}
|
|
@@ -242,7 +214,7 @@ export const catchCommand = {
|
|
|
242
214
|
catch (error) {
|
|
243
215
|
if (!controller.signal.aborted)
|
|
244
216
|
throw error;
|
|
245
|
-
process.stdout.write(withPathOutput(squarePath, '○ you step back —
|
|
217
|
+
process.stdout.write(withPathOutput(squarePath, '○ you step back — perceive stopped'));
|
|
246
218
|
process.exitCode = 130;
|
|
247
219
|
}
|
|
248
220
|
finally {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { renderWatchForceTakeover, renderWatchReplaceMissing } from '../presentation.js';
|
|
2
|
-
export {
|
|
3
|
-
export function
|
|
4
|
-
if (progress.kind !== '
|
|
2
|
+
export { renderPerceiveOutcome } from '../operation-presentation.js';
|
|
3
|
+
export function renderPerceiveProgress(squarePath, progress) {
|
|
4
|
+
if (progress.kind !== 'perceive-started' || !progress.replacementRequested)
|
|
5
5
|
return undefined;
|
|
6
6
|
const options = { squarePath, name: progress.participant };
|
|
7
7
|
return (progress.replaced
|
package/dist/cli/registry.js
CHANGED
|
@@ -4,7 +4,7 @@ import { harnessCommand, installCommand, uninstallCommand } from './harness-comm
|
|
|
4
4
|
import { helpCommand, versionCommand } from './meta-commands.js';
|
|
5
5
|
import { mcpServerCommand } from './mcp-server-command.js';
|
|
6
6
|
import { JSON_COMMANDS } from './json.js';
|
|
7
|
-
import {
|
|
7
|
+
import { perceiveCommand, claudeHookCommand, claudeWatchCommand, codexHookCommand, codexWatchCommand, codexInterruptCommand, historyCommand, inboxCommand, listCommand, participantsCommand, statusCommand, streamCommand, } from './observation-commands.js';
|
|
8
8
|
/** Every public command is an executable adapter, including aliases and utility commands. */
|
|
9
9
|
export const commandRegistry = [
|
|
10
10
|
{ names: ['build'], spec: buildCommand },
|
|
@@ -17,7 +17,7 @@ export const commandRegistry = [
|
|
|
17
17
|
{ names: ['codex-hook'], spec: codexHookCommand },
|
|
18
18
|
{ names: ['codex-watch'], spec: codexWatchCommand },
|
|
19
19
|
{ names: ['codex-interrupt'], spec: codexInterruptCommand },
|
|
20
|
-
{ names: ['
|
|
20
|
+
{ names: ['perceive'], spec: perceiveCommand },
|
|
21
21
|
{ names: ['express'], spec: expressCommand },
|
|
22
22
|
{ names: ['listen'], spec: listenCommand },
|
|
23
23
|
{ names: ['ignore'], spec: ignoreCommand },
|
package/dist/codex-address.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type CodexBoundaryStore } from './packages/agent-delivery/src/codex-boundary.js';
|
|
1
2
|
import type { DeliveryRoute } from './delivery-ledger.js';
|
|
3
|
+
/** Local Stop/activity epochs shared by native hooks and async watchers. No RPC. */
|
|
4
|
+
export declare function codexBoundaryStore(env: NodeJS.ProcessEnv): CodexBoundaryStore;
|
|
2
5
|
/** Capture the receiver's CLI configuration when its seat is established, not at send time. */
|
|
3
6
|
export declare function codexWakeAddress(threadId: string, env: NodeJS.ProcessEnv): DeliveryRoute;
|
package/dist/codex-address.js
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { resolveCodexHome } from './packages/agent-delivery/src/codex-endpoint.js';
|
|
2
|
+
import { createCodexBoundaryStore } from './packages/agent-delivery/src/codex-boundary.js';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
/** Local Stop/activity epochs shared by native hooks and async watchers. No RPC. */
|
|
6
|
+
export function codexBoundaryStore(env) {
|
|
7
|
+
return createCodexBoundaryStore(env.SQUARE_CODEX_BOUNDARIES ?? path.join(os.homedir(), '.square', 'codex-boundaries.json'));
|
|
8
|
+
}
|
|
2
9
|
/** Capture the receiver's CLI configuration when its seat is established, not at send time. */
|
|
3
10
|
export function codexWakeAddress(threadId, env) {
|
|
4
11
|
const endpoint = env.SQUARE_CODEX_ENDPOINT?.trim();
|
package/dist/codex-hook.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type CodexHookInput } from './packages/agent-delivery/src/codex-hook.js';
|
|
2
2
|
export type HookTrace = (line: string) => void;
|
|
3
3
|
export declare const CODEX_HOOK_BUDGET_MS = 4500;
|
|
4
|
-
/**
|
|
5
|
-
export declare function codexHookResponse(value: unknown, sink: ((text: string) => void | Promise<void>) | undefined, env?: NodeJS.ProcessEnv, trace?: HookTrace): Promise<
|
|
4
|
+
/** Active delivery uses UserPromptSubmit and PostToolUse; each async Stop owns its own wait. */
|
|
5
|
+
export declare function codexHookResponse(value: unknown, sink: ((text: string) => void | Promise<void>) | undefined, env?: NodeJS.ProcessEnv, trace?: HookTrace): Promise<void>;
|
|
6
6
|
export declare function parseCodexHookText(text: string): CodexHookInput | undefined;
|
|
7
7
|
/** The caller owns stdout; no receipt is written when no output sink is supplied. */
|
|
8
8
|
export declare function runCodexHook(inputText: string, env?: NodeJS.ProcessEnv, sink?: (text: string) => void | Promise<void>): Promise<string>;
|
package/dist/codex-hook.js
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { parseCodexHook } from './packages/agent-delivery/src/codex-hook.js';
|
|
2
2
|
import { presentPendingAtBoundary } from './boundary-presentation.js';
|
|
3
3
|
import { DeliveryLedger } from './delivery-ledger.js';
|
|
4
4
|
import { harnessOwner } from './owner.js';
|
|
5
5
|
import { attachDeliverySessionRoutes, detachDeliverySession } from './session-lifecycle.js';
|
|
6
|
-
import { codexBoundaryStore } from './codex-
|
|
7
|
-
import { codexWakeAddress } from './codex-address.js';
|
|
6
|
+
import { codexWakeAddress, codexBoundaryStore } from './codex-address.js';
|
|
8
7
|
export const CODEX_HOOK_BUDGET_MS = 4500;
|
|
9
8
|
const defaultTrace = line => { process.stderr.write(`${line}\n`); };
|
|
10
|
-
/**
|
|
9
|
+
/** Active delivery uses UserPromptSubmit and PostToolUse; each async Stop owns its own wait. */
|
|
11
10
|
export async function codexHookResponse(value, sink, env = process.env, trace = defaultTrace) {
|
|
12
11
|
const input = parseCodexHook(value);
|
|
13
12
|
if (!input)
|
|
@@ -16,47 +15,38 @@ export async function codexHookResponse(value, sink, env = process.env, trace =
|
|
|
16
15
|
const timeoutMs = Math.min(input.hook_event_name === 'Interrupt' || input.hook_event_name === 'SessionEnd' ? 2500 : CODEX_HOOK_BUDGET_MS, Number.isFinite(configured) && configured > 0 ? configured : CODEX_HOOK_BUDGET_MS);
|
|
17
16
|
const signal = AbortSignal.timeout(timeoutMs);
|
|
18
17
|
const owner = harnessOwner({ channel: 'codex', session: input.session_id });
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
if (input.hook_event_name === '
|
|
23
|
-
|
|
18
|
+
// Active native events disarm idle queueing; Stop arms its own async watcher.
|
|
19
|
+
// This file state is the only activity gate: no app-server sampling.
|
|
20
|
+
await codexBoundaryStore(env).record(input.session_id, input.hook_event_name === 'SessionStart' ? 'start' : input.hook_event_name === 'SessionEnd' ? 'end' : 'non-stop', { signal, turnId: input.turn_id });
|
|
21
|
+
if (input.hook_event_name === 'SessionStart') {
|
|
22
|
+
await attachDeliverySessionRoutes(owner, env, [codexWakeAddress(input.session_id, env)], signal);
|
|
23
|
+
}
|
|
24
|
+
else if (input.hook_event_name === 'SessionEnd' || input.hook_event_name === 'Interrupt') {
|
|
25
|
+
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY, signal });
|
|
24
26
|
try {
|
|
25
|
-
|
|
27
|
+
const captured = await ledger.session(owner, signal);
|
|
28
|
+
if (!captured || signal.aborted)
|
|
29
|
+
return;
|
|
30
|
+
if (input.hook_event_name === 'SessionEnd')
|
|
31
|
+
await detachDeliverySession(owner, captured.generation, env, signal);
|
|
32
|
+
else
|
|
33
|
+
await ledger.cancel({ owner, generation: captured.generation }, signal);
|
|
26
34
|
}
|
|
27
35
|
finally {
|
|
28
36
|
await ledger.close();
|
|
29
37
|
}
|
|
30
38
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
await detachDeliverySession(owner, generation, env); },
|
|
43
|
-
interrupt: async () => {
|
|
44
|
-
if (!generation || signal.aborted)
|
|
45
|
-
return;
|
|
46
|
-
const ledger = await DeliveryLedger.open({ location: env.SQUARE_DELIVERY });
|
|
47
|
-
try {
|
|
48
|
-
await ledger.cancel({ owner, generation });
|
|
49
|
-
}
|
|
50
|
-
finally {
|
|
51
|
-
await ledger.close();
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
trace: name => trace(`square-codex-hook: stage=${name}`),
|
|
55
|
-
});
|
|
56
|
-
// Disarm before rotating the native attachment, even if there is no old session.
|
|
57
|
-
if (input.hook_event_name === 'SessionStart' && !signal.aborted)
|
|
58
|
-
await attachDeliverySessionRoutes(owner, env, [codexWakeAddress(input.session_id, env)], signal);
|
|
59
|
-
return result;
|
|
39
|
+
else if ((input.hook_event_name === 'UserPromptSubmit' || input.hook_event_name === 'PostToolUse') && sink) {
|
|
40
|
+
try {
|
|
41
|
+
await presentPendingAtBoundary(owner, async (context) => {
|
|
42
|
+
await sink(`${JSON.stringify({ hookSpecificOutput: { hookEventName: input.hook_event_name, additionalContext: context } })}\n`);
|
|
43
|
+
}, env, signal);
|
|
44
|
+
trace('square-codex-hook: stage=presentation');
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
trace(`square-codex-hook: stage=presentation error=${error instanceof Error ? error.name : 'Error'}`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
60
50
|
}
|
|
61
51
|
export function parseCodexHookText(text) {
|
|
62
52
|
try {
|
package/dist/codex-queue.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { type CodexStopEpoch } from './codex-boundary-state.js';
|
|
2
1
|
import type { DeliveryCard, DeliveryTransport, DeliveryTransportContext, TransportOutcome } from './delivery-ledger.js';
|
|
3
2
|
export interface CodexQueueTransportOptions {
|
|
4
|
-
readonly epoch: CodexStopEpoch;
|
|
5
3
|
readonly address: Readonly<Record<string, string>>;
|
|
6
4
|
readonly env?: NodeJS.ProcessEnv;
|
|
7
5
|
}
|
|
8
|
-
/** Instantiated only by the receiver
|
|
6
|
+
/** Instantiated only by the receiver. The native queue store is shared across
|
|
7
|
+
* processes: the hosting Codex app-server polls it and starts queued input only
|
|
8
|
+
* when the thread is idle, so admission needs no remote idle proof. */
|
|
9
9
|
export declare class CodexQueueTransport implements DeliveryTransport {
|
|
10
10
|
private readonly opts;
|
|
11
11
|
readonly kind = "codex-queue";
|
package/dist/codex-queue.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { connect, DeliveryError } from './packages/agent-delivery/src/index.js';
|
|
2
|
-
import { codexIdleAtStop } from './packages/agent-delivery/src/codex.js';
|
|
3
2
|
import { codexQueueEndpoint } from './packages/agent-delivery/src/codex-endpoint.js';
|
|
4
|
-
import { codexBoundaryStore } from './codex-boundary-state.js';
|
|
5
3
|
import { renderDeliveryReminder } from './delivery-frame.js';
|
|
6
|
-
/** Instantiated only by the receiver
|
|
4
|
+
/** Instantiated only by the receiver. The native queue store is shared across
|
|
5
|
+
* processes: the hosting Codex app-server polls it and starts queued input only
|
|
6
|
+
* when the thread is idle, so admission needs no remote idle proof. */
|
|
7
7
|
export class CodexQueueTransport {
|
|
8
8
|
opts;
|
|
9
9
|
kind = 'codex-queue';
|
|
@@ -16,21 +16,15 @@ export class CodexQueueTransport {
|
|
|
16
16
|
!Object.entries(this.opts.address).every(([key, value]) => address[key] === value))
|
|
17
17
|
return 'not-capable';
|
|
18
18
|
const env = this.opts.env ?? process.env;
|
|
19
|
-
const boundaries = codexBoundaryStore(env), epoch = this.opts.epoch;
|
|
20
19
|
try {
|
|
21
|
-
// Receiver delivery addresses the
|
|
22
|
-
//
|
|
20
|
+
// Receiver delivery addresses the shared queue store explicitly; disappearing
|
|
21
|
+
// native custody must not trigger CLI service startup.
|
|
23
22
|
const endpoint = address.endpoint ?? codexQueueEndpoint({ CODEX_HOME: address.codexHome });
|
|
24
23
|
const agent = await connect({ harness: 'codex', transport: 'cli', sessionId: threadId,
|
|
25
|
-
boundaries: { queueEligible: async (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
// epoch and ledger fences. These reads are not an atomic native enqueue.
|
|
30
|
-
if (!await codexIdleAtStop({ harness: 'codex', sessionId: id, codexHome: address.codexHome,
|
|
31
|
-
endpoint, timeoutMs: Math.max(1, context.deadline - Date.now()), signal: context.signal }, epoch.turnId))
|
|
32
|
-
return false;
|
|
33
|
-
return await boundaries.queueEligible(id, epoch) && await context.beforeSend()
|
|
24
|
+
boundaries: { queueEligible: async () => {
|
|
25
|
+
// Only ledger fences remain: currentness, suppression, cancellation,
|
|
26
|
+
// attachment generation, signal and deadline immediately before launch.
|
|
27
|
+
return await context.beforeSend()
|
|
34
28
|
&& !context.signal.aborted && Date.now() < context.deadline;
|
|
35
29
|
} },
|
|
36
30
|
env, bin: env.SQUARE_CODEX_BIN, codexHome: address.codexHome, endpoint });
|
package/dist/codex-watch.d.ts
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
/** One
|
|
1
|
+
/** One async Stop waits only for its captured native turn; it never adopts another Stop.
|
|
2
|
+
* Queue admission writes the shared native queue store through the CLI; the hosting
|
|
3
|
+
* Codex process starts queued input only when idle, so no remote idle sampling.
|
|
4
|
+
* Local boundary epochs retire this watcher when a newer Stop or any active native
|
|
5
|
+
* event (UserPromptSubmit/PostToolUse/Interrupt) arrives, keeping active-turn
|
|
6
|
+
* presentation on the synchronous hooks. */
|
|
2
7
|
export declare function runCodexWatch(inputText: string, env?: NodeJS.ProcessEnv, signal?: AbortSignal): Promise<string>;
|
package/dist/codex-watch.js
CHANGED
|
@@ -6,12 +6,15 @@ import { retainDeliveryLedger } from './delivery-lifetime.js';
|
|
|
6
6
|
import { harnessOwner } from './owner.js';
|
|
7
7
|
import { withFileLock } from './file-lock.js';
|
|
8
8
|
import { canonicalPath } from './canonical-path.js';
|
|
9
|
-
import { codexBoundaryStore } from './codex-
|
|
10
|
-
import { codexWakeAddress } from './codex-address.js';
|
|
9
|
+
import { codexWakeAddress, codexBoundaryStore } from './codex-address.js';
|
|
11
10
|
import { parseCodexHookText } from './codex-hook.js';
|
|
12
11
|
import { CodexQueueTransport } from './codex-queue.js';
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/** One async Stop waits only for its captured native turn; it never adopts another Stop.
|
|
13
|
+
* Queue admission writes the shared native queue store through the CLI; the hosting
|
|
14
|
+
* Codex process starts queued input only when idle, so no remote idle sampling.
|
|
15
|
+
* Local boundary epochs retire this watcher when a newer Stop or any active native
|
|
16
|
+
* event (UserPromptSubmit/PostToolUse/Interrupt) arrives, keeping active-turn
|
|
17
|
+
* presentation on the synchronous hooks. */
|
|
15
18
|
export async function runCodexWatch(inputText, env = process.env, signal) {
|
|
16
19
|
const input = parseCodexHookText(inputText);
|
|
17
20
|
if (input?.hook_event_name !== 'Stop' || signal?.aborted)
|
|
@@ -22,31 +25,39 @@ export async function runCodexWatch(inputText, env = process.env, signal) {
|
|
|
22
25
|
const combined = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
|
|
23
26
|
process.on('SIGTERM', stop);
|
|
24
27
|
process.on('SIGINT', stop);
|
|
28
|
+
let lifetime;
|
|
25
29
|
try {
|
|
26
30
|
const route = codexWakeAddress(input.session_id, env);
|
|
27
31
|
const home = await canonicalPath(route.address.codexHome);
|
|
28
|
-
const
|
|
32
|
+
const ledger = await DeliveryLedger.open({ location: deliveryLedgerLocation(env), signal: combined });
|
|
33
|
+
lifetime = retainDeliveryLedger(ledger);
|
|
34
|
+
const owner = harnessOwner({ channel: 'codex', session: input.session_id });
|
|
35
|
+
const captured = await ledger.session(owner, combined);
|
|
36
|
+
if (captured?.state !== 'attached')
|
|
37
|
+
return '';
|
|
38
|
+
// Same-turn duplicates cannot overlap; a later Stop need not wait for an old
|
|
39
|
+
// watcher or a slow late settlement to release a session-wide lock.
|
|
40
|
+
const coordinate = [input.session_id, route.address.endpoint ?? home, captured.generation, input.turn_id];
|
|
29
41
|
const root = path.join(path.dirname(deliveryLedgerLocation(env)), 'codex-watchers');
|
|
30
42
|
await fs.mkdir(root, { recursive: true, mode: 0o700 });
|
|
31
43
|
const lock = path.join(root, createHash('sha256').update(JSON.stringify(coordinate)).digest('hex') + '.lock');
|
|
32
44
|
const file = await fs.open(lock, 'a', 0o600);
|
|
33
45
|
await file.close();
|
|
34
46
|
await withFileLock(lock, { retryMs: 10, skipIfBusy: true, signal: combined }, async () => {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
const boundaries = codexBoundaryStore(env);
|
|
48
|
+
// Record only after winning the lock: a duplicate Stop must not replace the
|
|
49
|
+
// live watcher's epoch and retire both. A newer Stop or active event does.
|
|
50
|
+
await boundaries.record(input.session_id, 'Stop', { turnId: input.turn_id, signal: combined });
|
|
51
|
+
const epoch = (await boundaries.read(input.session_id))?.stop;
|
|
52
|
+
if (!epoch)
|
|
53
|
+
return;
|
|
54
|
+
const observer = await ledger.observe(owner);
|
|
38
55
|
try {
|
|
39
|
-
const owner = harnessOwner({ channel: 'codex', session: input.session_id });
|
|
40
|
-
const captured = await ledger.session(owner);
|
|
41
|
-
if (captured?.state !== 'attached')
|
|
42
|
-
return;
|
|
43
|
-
const boundaries = codexBoundaryStore(env);
|
|
44
|
-
// Subscribe before inspecting pending work. Both ledger edges and the
|
|
45
|
-
// bounded wait below cause reconciliation; boundary-only changes matter.
|
|
46
|
-
observer = await ledger.observe(owner);
|
|
47
56
|
while (!combined.aborted) {
|
|
48
|
-
|
|
49
|
-
|
|
57
|
+
const version = await observer.read(combined);
|
|
58
|
+
if (!await boundaries.queueEligible(input.session_id, epoch))
|
|
59
|
+
return;
|
|
60
|
+
const session = await ledger.session(owner, combined);
|
|
50
61
|
if (session?.state !== 'attached' || session.generation !== captured.generation)
|
|
51
62
|
return;
|
|
52
63
|
const registered = session.routes.find(candidate => candidate.kind === 'codex-queue');
|
|
@@ -54,36 +65,20 @@ export async function runCodexWatch(inputText, env = process.env, signal) {
|
|
|
54
65
|
registered.address.endpoint !== route.address.endpoint ||
|
|
55
66
|
!registered.address.codexHome || await canonicalPath(registered.address.codexHome) !== home)
|
|
56
67
|
return;
|
|
57
|
-
const
|
|
58
|
-
if (boundary?.ended)
|
|
59
|
-
return;
|
|
60
|
-
const pending = await ledger.pending(owner, { generation: captured.generation });
|
|
61
|
-
const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }))
|
|
68
|
+
const held = new Set((await ledger.handoffs(owner, { generation: captured.generation }, combined))
|
|
62
69
|
.filter(attempt => !['failed', 'released'].includes(attempt.outcome)).map(attempt => attempt.seq));
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const epoch = (await boundaries.read(input.session_id))?.stop;
|
|
70
|
-
if (!epoch || !await codexIdleAtStop({ harness: 'codex', sessionId: input.session_id,
|
|
71
|
-
codexHome: registered.address.codexHome, endpoint: registered.address.endpoint,
|
|
72
|
-
timeoutMs: 2000, signal: combined }, epoch.turnId))
|
|
73
|
-
break;
|
|
74
|
-
lifetime.track(await ledger.dispatchHandoff(owner, card.key, new CodexQueueTransport({ epoch, env, address: registered.address }), { generation: captured.generation, timeoutMs: 5000, signal: combined }));
|
|
75
|
-
// Drain our own claim/settlement edge. A known-unsent failure must
|
|
76
|
-
// not turn file observation into an immediate retry loop. Any post
|
|
77
|
-
// racing this read is still found by the bounded rescan below.
|
|
78
|
-
version = await observer.read(combined);
|
|
70
|
+
const card = (await ledger.pending(owner, { generation: captured.generation }, combined)).find(card => !held.has(card.seq));
|
|
71
|
+
if (card) {
|
|
72
|
+
lifetime.track(await ledger.dispatchHandoff(owner, card.key, new CodexQueueTransport({ env, address: registered.address }), { generation: captured.generation, timeoutMs: 5000, signal: combined }));
|
|
73
|
+
// One handoff attempt ends this wait, including unknown/refused
|
|
74
|
+
// outcomes. Never replay or turn our own writes into a retry loop.
|
|
75
|
+
return;
|
|
79
76
|
}
|
|
80
|
-
// No claims are held while waiting, including while active or disarmed.
|
|
81
77
|
await observer.changed(version, 1000, combined);
|
|
82
78
|
}
|
|
83
79
|
}
|
|
84
80
|
finally {
|
|
85
|
-
observer
|
|
86
|
-
await lifetime.close();
|
|
81
|
+
observer.close();
|
|
87
82
|
}
|
|
88
83
|
});
|
|
89
84
|
}
|
|
@@ -95,6 +90,7 @@ export async function runCodexWatch(inputText, env = process.env, signal) {
|
|
|
95
90
|
clearTimeout(timer);
|
|
96
91
|
process.off('SIGTERM', stop);
|
|
97
92
|
process.off('SIGINT', stop);
|
|
93
|
+
await lifetime?.close();
|
|
98
94
|
}
|
|
99
95
|
return '';
|
|
100
96
|
}
|
|
@@ -8,8 +8,13 @@ export declare class LedgerStore {
|
|
|
8
8
|
private readonly clock;
|
|
9
9
|
private pending;
|
|
10
10
|
private closed;
|
|
11
|
+
private readonly cancellation;
|
|
12
|
+
private readonly ordinaryCancellation;
|
|
13
|
+
private closing?;
|
|
11
14
|
private constructor();
|
|
12
|
-
static open(location: string, clock: () => number): Promise<LedgerStore>;
|
|
15
|
+
static open(location: string, clock: () => number, signal?: AbortSignal): Promise<LedgerStore>;
|
|
16
|
+
/** Stop ordinary work while captured effects retain access to settlement. */
|
|
17
|
+
retire(): void;
|
|
13
18
|
close(): Promise<void>;
|
|
14
19
|
private transaction;
|
|
15
20
|
private getSession;
|
|
@@ -20,49 +25,49 @@ export declare class LedgerStore {
|
|
|
20
25
|
private normalize;
|
|
21
26
|
private deleteCard;
|
|
22
27
|
private gc;
|
|
23
|
-
session(owner: string): Promise<DeliverySession | undefined>;
|
|
24
|
-
sessions(): Promise<readonly DeliverySession[]>;
|
|
25
|
-
pendingCards(owner: string, generation: string): Promise<readonly DeliveryCard[]>;
|
|
26
|
-
handoffs(owner: string, generation: string): Promise<readonly DeliveryAttempt[]>;
|
|
28
|
+
session(owner: string, signal?: AbortSignal): Promise<DeliverySession | undefined>;
|
|
29
|
+
sessions(signal?: AbortSignal): Promise<readonly DeliverySession[]>;
|
|
30
|
+
pendingCards(owner: string, generation: string, signal?: AbortSignal): Promise<readonly DeliveryCard[]>;
|
|
31
|
+
handoffs(owner: string, generation: string, signal?: AbortSignal): Promise<readonly DeliveryAttempt[]>;
|
|
27
32
|
attach(input: {
|
|
28
33
|
owner: string;
|
|
29
34
|
expectedGeneration: string | null;
|
|
30
35
|
routes: readonly DeliveryRoute[];
|
|
31
|
-
}): Promise<DeliverySession | false>;
|
|
36
|
+
}, signal?: AbortSignal): Promise<DeliverySession | false>;
|
|
32
37
|
updateRoutes(input: {
|
|
33
38
|
owner: string;
|
|
34
39
|
generation: string;
|
|
35
40
|
routes: readonly DeliveryRoute[];
|
|
36
|
-
}): Promise<boolean>;
|
|
41
|
+
}, signal?: AbortSignal): Promise<boolean>;
|
|
37
42
|
lifecycle(input: {
|
|
38
43
|
owner: string;
|
|
39
44
|
generation: string;
|
|
40
|
-
}, state: 'detached' | 'deleted'): Promise<boolean>;
|
|
45
|
+
}, state: 'detached' | 'deleted', signal?: AbortSignal): Promise<boolean>;
|
|
41
46
|
post(input: {
|
|
42
47
|
owner: string;
|
|
43
48
|
key: string;
|
|
44
49
|
text: string;
|
|
45
50
|
details: string | null;
|
|
46
|
-
}): Promise<PostResult>;
|
|
51
|
+
}, signal?: AbortSignal): Promise<PostResult>;
|
|
47
52
|
suppress(input: {
|
|
48
53
|
owner: string;
|
|
49
54
|
key: string;
|
|
50
55
|
scope: 'exact' | 'prefix';
|
|
51
56
|
expiresAt?: number;
|
|
52
|
-
}): Promise<SuppressionResult>;
|
|
57
|
+
}, signal?: AbortSignal): Promise<SuppressionResult>;
|
|
53
58
|
renewSuppression(input: {
|
|
54
59
|
owner: string;
|
|
55
60
|
id: string;
|
|
56
61
|
expiresAt: number;
|
|
57
|
-
}): Promise<boolean>;
|
|
62
|
+
}, signal?: AbortSignal): Promise<boolean>;
|
|
58
63
|
releaseSuppression(input: {
|
|
59
64
|
owner: string;
|
|
60
65
|
id: string;
|
|
61
|
-
}): Promise<boolean>;
|
|
66
|
+
}, signal?: AbortSignal): Promise<boolean>;
|
|
62
67
|
cancel(input: {
|
|
63
68
|
owner: string;
|
|
64
69
|
generation: string;
|
|
65
|
-
}): Promise<boolean>;
|
|
70
|
+
}, signal?: AbortSignal): Promise<boolean>;
|
|
66
71
|
private isSuppressed;
|
|
67
72
|
private eligible;
|
|
68
73
|
private insertAttempt;
|
|
@@ -79,21 +84,21 @@ export declare class LedgerStore {
|
|
|
79
84
|
prepare(token: string, value: DeliveryCorrelation, signal?: AbortSignal): Promise<boolean>;
|
|
80
85
|
private receipt;
|
|
81
86
|
private confirm;
|
|
82
|
-
settle(tokens: readonly string[], outcome: Exclude<DeliveryOutcome, 'dispatching'
|
|
87
|
+
settle(tokens: readonly string[], outcome: Exclude<DeliveryOutcome, 'dispatching'>, signal?: AbortSignal): Promise<boolean>;
|
|
83
88
|
private handoffFacts;
|
|
84
89
|
private matchingHandoff;
|
|
85
90
|
private currentHandoff;
|
|
86
|
-
handoff(token: string): Promise<(DeliveryHandoffRef & {
|
|
91
|
+
handoff(token: string, signal?: AbortSignal): Promise<(DeliveryHandoffRef & {
|
|
87
92
|
routeKind: string;
|
|
88
93
|
}) | undefined>;
|
|
89
|
-
guardHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
|
|
90
|
-
acceptHandoff(ref: DeliveryHandoffRef): Promise<boolean>;
|
|
94
|
+
guardHandoff(ref: DeliveryHandoffRef, signal?: AbortSignal): Promise<boolean>;
|
|
95
|
+
acceptHandoff(ref: DeliveryHandoffRef, signal?: AbortSignal): Promise<boolean>;
|
|
91
96
|
observePresentation(handoff: string | DeliveryHandoffRef, options?: {
|
|
92
97
|
readonly requireCurrent?: boolean;
|
|
93
|
-
}): Promise<boolean>;
|
|
98
|
+
}, signal?: AbortSignal): Promise<boolean>;
|
|
94
99
|
receipts(owner: string, options: {
|
|
95
100
|
keys?: readonly string[];
|
|
96
101
|
since?: number;
|
|
97
|
-
}): Promise<readonly DeliveryReceipt[]>;
|
|
98
|
-
health(owner?: string): Promise<DeliveryHealth>;
|
|
102
|
+
}, signal?: AbortSignal): Promise<readonly DeliveryReceipt[]>;
|
|
103
|
+
health(owner?: string, signal?: AbortSignal): Promise<DeliveryHealth>;
|
|
99
104
|
}
|