@sema-agent/client-core 0.84.1 → 0.85.1
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/CHANGELOG.md +206 -0
- package/README.md +20 -13
- package/dist/adapt/arms.js +1 -1
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +1 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
- package/dist/adapter/activeRunSelfHeal.js +149 -17
- package/dist/adapter/downstream/terminalToSdkResult.js +9 -5
- package/dist/adapter/runStream.js +6 -2
- package/dist/agentSession/engineSessionBackgroundStop.d.ts +50 -0
- package/dist/agentSession/engineSessionBackgroundStop.js +291 -0
- package/dist/agentsWireCaps.d.ts +2 -1
- package/dist/cloudConfigWireCaps.js +2 -1
- package/dist/corruptSession.d.ts +3 -0
- package/dist/corruptSession.js +29 -0
- package/dist/corruptSessionCopy.d.ts +1 -0
- package/dist/corruptSessionCopy.js +4 -0
- package/dist/decideFailureNote.js +4 -1
- package/dist/decideReceipt.d.ts +6 -0
- package/dist/decideReceipt.js +30 -1
- package/dist/effortWire.d.ts +2 -1
- package/dist/effortWire.js +2 -4
- package/dist/engineErrorCodes.d.ts +9 -0
- package/dist/engineErrorCodes.js +29 -0
- package/dist/engineNoticeCodes.d.ts +9 -0
- package/dist/engineNoticeCodes.js +44 -156
- package/dist/engineWireFor.d.ts +43 -0
- package/dist/engineWireFor.js +399 -0
- package/dist/engineWireSdk.d.ts +4 -0
- package/dist/fileHistoryCaptureCapability.d.ts +4 -1
- package/dist/fileHistoryCaptureCapability.js +43 -9
- package/dist/gateVocabulary.d.ts +1 -2
- package/dist/gateVocabulary.js +1 -22
- package/dist/generated/engineNoticeTables.d.ts +3 -0
- package/dist/generated/engineNoticeTables.js +156 -0
- package/dist/generated/toolNameTables.d.ts +2 -0
- package/dist/generated/toolNameTables.js +43 -0
- package/dist/headlessPermissionModeWire.js +2 -4
- package/dist/hitl/armedGateRegistry.d.ts +1 -0
- package/dist/hitl/armedGateRegistry.js +1 -1
- package/dist/hitl/frameRouter.js +4 -2
- package/dist/hitl/parkResolver.js +3 -1
- package/dist/hitl/planReviewWire.d.ts +11 -0
- package/dist/hitl/planReviewWire.js +191 -72
- package/dist/hitl/rosterMountedNames.d.ts +2 -0
- package/dist/hitl/rosterMountedNames.js +38 -0
- package/dist/hitl/sessionPolicyDeliverable.d.ts +6 -2
- package/dist/hitl/sessionPolicyDeliverable.js +25 -45
- package/dist/hitl/sessionPolicyWire.js +1 -38
- package/dist/hitl/toolApprovalWire.d.ts +4 -2
- package/dist/hitl/toolApprovalWire.js +4 -2
- package/dist/host.d.ts +1 -1
- package/dist/host.js +11 -2
- package/dist/index.d.ts +10 -4
- package/dist/index.js +7 -3
- package/dist/permissionWireCaps.d.ts +2 -1
- package/dist/permissionWireCaps.js +2 -7
- package/dist/request/taskRequest.d.ts +4 -0
- package/dist/request/taskRequest.js +30 -1
- package/dist/resumeRefusalCopy.js +4 -2
- package/dist/runTerminal.js +4 -0
- package/dist/sandboxWire.js +1 -1
- package/dist/sealedKeyCapability.d.ts +17 -0
- package/dist/sealedKeyCapability.js +42 -0
- package/dist/seatContract.d.ts +2 -1
- package/dist/seatContract.js +5 -21
- package/dist/subagent/engineCompactWire.d.ts +13 -2
- package/dist/subagent/engineCompactWire.js +48 -37
- package/dist/subagent/engineDelegatedPrompt.d.ts +9 -1
- package/dist/subagent/engineDelegatedPrompt.js +16 -16
- package/dist/subagent/engineRowStopGate.d.ts +2 -2
- package/dist/subagent/engineRowStopGate.js +4 -4
- package/dist/subagent/engineSubagentOutput.d.ts +8 -0
- package/dist/subagent/engineSubagentOutput.js +37 -18
- package/dist/subagent/engineSubagentResume.d.ts +11 -2
- package/dist/subagent/engineSubagentResume.js +28 -14
- package/dist/subagent/engineSubagentSteer.d.ts +10 -1
- package/dist/subagent/engineSubagentSteer.js +16 -11
- package/dist/subagent/engineSubagentTail.d.ts +10 -1
- package/dist/subagent/engineSubagentTail.js +35 -17
- package/dist/subagent/engineTaskHandleWire.d.ts +13 -0
- package/dist/subagent/engineTaskHandleWire.js +41 -20
- package/dist/subagent/subagentOwnerAbsence.js +2 -5
- package/dist/taskRequestWords.d.ts +5 -0
- package/dist/taskRequestWords.js +4 -0
- package/dist/wireErrorTriage.d.ts +1 -0
- package/dist/wireErrorTriage.js +3 -1
- package/dist/wireRefusalCopy.d.ts +10 -0
- package/dist/wireRefusalCopy.js +62 -2
- package/docs/INTEGRATION-CLIENTS.md +867 -22
- package/package.json +1 -1
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
import { hostLog } from '../host.js';
|
|
2
|
-
import {
|
|
3
|
-
import { engineWireTargetFor } from '../engineWireTarget.js';
|
|
2
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, settleInjected } from '../engineWireFor.js';
|
|
4
3
|
import { engineSessionParamFor } from '../engineSessionParam.js';
|
|
5
4
|
import { resolveOwnerContext } from './engineSubagentResume.js';
|
|
6
5
|
import { coerceOutput, parentToolCallIdOf, publishSubagentContentEvent, settleSubagentContent, } from '../subagentContentStore.js';
|
|
7
6
|
import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
|
|
7
|
+
function usableTailClient(client) {
|
|
8
|
+
return clientHasVerbs(client, [['capabilities'], ['runs', 'subagentStream']]);
|
|
9
|
+
}
|
|
8
10
|
const capByBase = new Map();
|
|
9
11
|
const CAP_FALSE_RETRY_TTL_MS = 5 * 60_000;
|
|
10
|
-
async function subagentStreamCapable(client,
|
|
11
|
-
const hit = capByBase.get(
|
|
12
|
+
async function subagentStreamCapable(client, cacheKey, signal, deadlineMs) {
|
|
13
|
+
const hit = cacheKey !== undefined ? capByBase.get(cacheKey) : undefined;
|
|
12
14
|
if (hit && (hit.ok || Date.now() - hit.at < CAP_FALSE_RETRY_TTL_MS))
|
|
13
15
|
return hit.ok;
|
|
14
16
|
try {
|
|
15
|
-
const caps =
|
|
17
|
+
const caps = deadlineMs !== undefined
|
|
18
|
+
? await settleInjected(deadlineMs, signal, (s) => client.capabilities({ signal: s }))
|
|
19
|
+
: await client.capabilities({ ...(signal ? { signal } : {}) });
|
|
16
20
|
const ok = caps.subagentStream === true;
|
|
17
|
-
|
|
21
|
+
if (cacheKey !== undefined)
|
|
22
|
+
capByBase.set(cacheKey, { ok, at: Date.now() });
|
|
18
23
|
return ok;
|
|
19
24
|
}
|
|
20
25
|
catch {
|
|
@@ -44,24 +49,37 @@ export function installSubagentTailMetaSink(sink) {
|
|
|
44
49
|
export function isEngineSubagentTailActive(taskId) {
|
|
45
50
|
return activeTails.has(taskId);
|
|
46
51
|
}
|
|
47
|
-
export function tailEngineSubagent(taskId) {
|
|
52
|
+
export function tailEngineSubagent(taskId, opts) {
|
|
48
53
|
if (activeTails.has(taskId))
|
|
49
54
|
return;
|
|
50
55
|
const { record: owner, runId, key } = resolveOwnerContext(taskId);
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
56
|
+
const wire = engineWireFor(opts?.wire, key, usableTailClient);
|
|
57
|
+
if (wire.kind === 'no-target')
|
|
58
|
+
return;
|
|
59
|
+
if (wire.kind === 'injected-unusable') {
|
|
60
|
+
hostLog('debug', `[subagent-tail] ${taskId}: the engine client supplied by the host is not usable (needs capabilities and runs.subagentStream) — tail not opened`);
|
|
53
61
|
return;
|
|
62
|
+
}
|
|
54
63
|
if (!runId) {
|
|
55
64
|
noteBgOwnerAbsence('subagent-tail', taskId);
|
|
56
65
|
return;
|
|
57
66
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
let client;
|
|
68
|
+
let cacheKey;
|
|
69
|
+
let injected;
|
|
70
|
+
if (wire.kind === 'injected') {
|
|
71
|
+
client = wire.client;
|
|
72
|
+
cacheKey = wire.capsBaseUrl;
|
|
73
|
+
injected = true;
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const made = wire.make();
|
|
77
|
+
if (!made)
|
|
78
|
+
return;
|
|
79
|
+
client = made;
|
|
80
|
+
cacheKey = wire.target.baseUrl;
|
|
81
|
+
injected = false;
|
|
82
|
+
}
|
|
65
83
|
const ac = new AbortController();
|
|
66
84
|
activeTails.set(taskId, ac);
|
|
67
85
|
const openedAt = Date.now();
|
|
@@ -70,7 +88,7 @@ export function tailEngineSubagent(taskId) {
|
|
|
70
88
|
let firstContentLogged = false;
|
|
71
89
|
void (async () => {
|
|
72
90
|
try {
|
|
73
|
-
if (!(await subagentStreamCapable(client,
|
|
91
|
+
if (!(await subagentStreamCapable(client, cacheKey, ac.signal, injected ? INJECTED_WIRE_SETTLE_MS : undefined)))
|
|
74
92
|
return;
|
|
75
93
|
const session = owner?.sessionId ?? engineSessionParamFor(key);
|
|
76
94
|
const stream = client.runs.subagentStream(runId, taskId, {
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import { type AgentClient } from '@sema-agent/sdk';
|
|
2
|
+
import type { EngineWireInjection } from '../engineWireSdk.js';
|
|
3
|
+
export type EngineTaskOutputWireClient = {
|
|
4
|
+
runs: Pick<AgentClient['runs'], 'taskOutput'>;
|
|
5
|
+
};
|
|
6
|
+
export type EngineTaskOutputWire = EngineWireInjection<EngineTaskOutputWireClient>;
|
|
7
|
+
export type EngineTaskStopWireClient = {
|
|
8
|
+
runs: Pick<AgentClient['runs'], 'taskStop'>;
|
|
9
|
+
};
|
|
10
|
+
export type EngineTaskStopWire = EngineWireInjection<EngineTaskStopWireClient>;
|
|
1
11
|
export interface EngineTaskOutputResult {
|
|
2
12
|
content: string;
|
|
3
13
|
status?: string;
|
|
@@ -7,6 +17,7 @@ export interface EngineTaskOutputResult {
|
|
|
7
17
|
export declare function __resetTaskHandleWireForTests(): void;
|
|
8
18
|
export declare function fetchEngineTaskOutput(handle: string, opts?: {
|
|
9
19
|
signal?: AbortSignal;
|
|
20
|
+
wire?: EngineTaskOutputWire;
|
|
10
21
|
}): Promise<EngineTaskOutputResult | null>;
|
|
11
22
|
export interface TaskOutputMergeHint {
|
|
12
23
|
readonly type?: string;
|
|
@@ -41,8 +52,10 @@ export type EngineTaskStopOutcome = {
|
|
|
41
52
|
ok: false;
|
|
42
53
|
reason: 'unavailable' | 'error';
|
|
43
54
|
detail?: string;
|
|
55
|
+
unconfirmed?: true;
|
|
44
56
|
};
|
|
45
57
|
export declare function classifyTaskStopConflict(e: unknown): EngineTaskStopOutcome;
|
|
46
58
|
export declare function stopEngineTask(handle: string, opts?: {
|
|
47
59
|
signal?: AbortSignal;
|
|
60
|
+
wire?: EngineTaskStopWire;
|
|
48
61
|
}): Promise<EngineTaskStopOutcome>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { TaskStopConflictError } from '@sema-agent/sdk';
|
|
2
2
|
import { hostLog } from '../host.js';
|
|
3
|
-
import {
|
|
4
|
-
import { engineWireDebugEnabled
|
|
3
|
+
import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, sendOnce, settleInjected } from '../engineWireFor.js';
|
|
4
|
+
import { engineWireDebugEnabled } from '../engineWireTarget.js';
|
|
5
|
+
import { engineCapTrue } from '../engineCapsCache.js';
|
|
5
6
|
import { engineSessionParamFor } from '../engineSessionParam.js';
|
|
6
7
|
import { resolveOwnerContext } from './engineSubagentResume.js';
|
|
7
8
|
import { engineTaskHandlesCapable } from './engineRowStopGate.js';
|
|
8
9
|
import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
|
|
10
|
+
function usableTaskOutputClient(client) {
|
|
11
|
+
return clientHasVerbs(client, [['runs', 'taskOutput']]);
|
|
12
|
+
}
|
|
13
|
+
function usableTaskStopClient(client) {
|
|
14
|
+
return clientHasVerbs(client, [['runs', 'taskStop']]);
|
|
15
|
+
}
|
|
16
|
+
const CAPS_KEY_REQUIRED_DETAIL = 'capability evidence for taskHandles is not available under the key the host named (capsBaseUrl) — nothing sent';
|
|
9
17
|
import { spoolMarkerOf } from '../toolResult.js';
|
|
10
18
|
import { STOP_CONFLICT_CODES, STOP_NOT_LANDED, STOP_NOT_LOCAL, STOP_PARKED, STOP_PARK_ARBITER_UNREACHABLE, STOP_PARK_RESUME_WON, } from '../engineErrorCodes.js';
|
|
11
19
|
function resolveHostRun(handle) {
|
|
@@ -26,24 +34,30 @@ export function __resetTaskHandleWireForTests() {
|
|
|
26
34
|
}
|
|
27
35
|
export async function fetchEngineTaskOutput(handle, opts) {
|
|
28
36
|
const owner = resolveOwnerContext(handle);
|
|
29
|
-
const
|
|
30
|
-
if (
|
|
37
|
+
const wire = engineWireFor(opts?.wire, owner.key, usableTaskOutputClient);
|
|
38
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
39
|
+
return null;
|
|
40
|
+
if (wire.kind === 'injected') {
|
|
41
|
+
if (!engineCapTrue(wire.capsBaseUrl, 'taskHandles')) {
|
|
42
|
+
hostLog('debug', `[task-handle] output ${handle}: ${CAPS_KEY_REQUIRED_DETAIL}`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
else if (!engineTaskHandlesCapable(owner.key)) {
|
|
31
47
|
return null;
|
|
48
|
+
}
|
|
32
49
|
const runId = resolveHostRun(handle);
|
|
33
50
|
if (!runId)
|
|
34
51
|
return null;
|
|
35
|
-
const client =
|
|
36
|
-
baseUrl: cfg.baseUrl,
|
|
37
|
-
...(cfg.token ? { token: cfg.token } : {}),
|
|
38
|
-
principal: cfg.principal,
|
|
39
|
-
});
|
|
52
|
+
const client = wire.kind === 'injected' ? wire.client : wire.make();
|
|
40
53
|
if (!client)
|
|
41
54
|
return null;
|
|
42
55
|
try {
|
|
43
|
-
const
|
|
44
|
-
...(
|
|
56
|
+
const read = (signal) => client.runs.taskOutput(runId, handle, {
|
|
57
|
+
...(signal ? { signal } : {}),
|
|
45
58
|
...(sessionOf(owner)),
|
|
46
59
|
});
|
|
60
|
+
const r = wire.kind === 'injected' ? await settleInjected(INJECTED_WIRE_SETTLE_MS, opts?.signal, read) : await read(opts?.signal);
|
|
47
61
|
const fresh = typeof r?.content === 'string' ? r.content : '';
|
|
48
62
|
const prev = accumulated.get(handle) ?? '';
|
|
49
63
|
const cursorSemantics = r?.cursorSemantics;
|
|
@@ -121,24 +135,28 @@ export function classifyTaskStopConflict(e) {
|
|
|
121
135
|
}
|
|
122
136
|
export async function stopEngineTask(handle, opts) {
|
|
123
137
|
const owner = resolveOwnerContext(handle);
|
|
124
|
-
const
|
|
125
|
-
if (
|
|
138
|
+
const wire = engineWireFor(opts?.wire, owner.key, usableTaskStopClient);
|
|
139
|
+
if (wire.kind === 'no-target' || wire.kind === 'injected-unusable')
|
|
140
|
+
return { ok: false, reason: 'unavailable' };
|
|
141
|
+
if (wire.kind === 'injected') {
|
|
142
|
+
if (!engineCapTrue(wire.capsBaseUrl, 'taskHandles'))
|
|
143
|
+
return { ok: false, reason: 'unavailable', detail: CAPS_KEY_REQUIRED_DETAIL };
|
|
144
|
+
}
|
|
145
|
+
else if (!engineTaskHandlesCapable(owner.key)) {
|
|
126
146
|
return { ok: false, reason: 'unavailable' };
|
|
147
|
+
}
|
|
127
148
|
const runId = resolveHostRun(handle);
|
|
128
149
|
if (!runId)
|
|
129
150
|
return { ok: false, reason: 'unavailable', detail: 'no host run mapping' };
|
|
130
|
-
const client =
|
|
131
|
-
baseUrl: cfg.baseUrl,
|
|
132
|
-
...(cfg.token ? { token: cfg.token } : {}),
|
|
133
|
-
principal: cfg.principal,
|
|
134
|
-
});
|
|
151
|
+
const client = wire.kind === 'injected' ? wire.client : wire.make();
|
|
135
152
|
if (!client)
|
|
136
153
|
return { ok: false, reason: 'unavailable' };
|
|
137
154
|
try {
|
|
138
|
-
const
|
|
139
|
-
...(
|
|
155
|
+
const stop = (signal) => client.runs.taskStop(runId, handle, {
|
|
156
|
+
...(signal ? { signal } : {}),
|
|
140
157
|
...(sessionOf(owner)),
|
|
141
158
|
});
|
|
159
|
+
const r = wire.kind === 'injected' ? await settleInjected(INJECTED_WIRE_SETTLE_MS, opts?.signal, stop) : await sendOnce(opts?.signal, () => stop(opts?.signal));
|
|
142
160
|
const out = r?.output ?? {};
|
|
143
161
|
if (engineWireDebugEnabled()) {
|
|
144
162
|
hostLog('debug', `[task-handle] stop ${handle} via run ${runId}: ok status=${typeof out.status === 'string' ? out.status : '-'} stoppedBy=${typeof out.stoppedBy === 'string' ? out.stoppedBy : '-'}`);
|
|
@@ -156,6 +174,9 @@ export async function stopEngineTask(handle, opts) {
|
|
|
156
174
|
if (e instanceof TaskStopConflictError || status === 409) {
|
|
157
175
|
return classifyTaskStopConflict(e);
|
|
158
176
|
}
|
|
177
|
+
const noAnswer = typeof e === 'object' ? noAnswerDetailFrom(e) : null;
|
|
178
|
+
if (noAnswer !== null)
|
|
179
|
+
return { ok: false, reason: 'error', detail: noAnswer, unconfirmed: true };
|
|
159
180
|
return { ok: false, reason: 'error', detail };
|
|
160
181
|
}
|
|
161
182
|
}
|
|
@@ -11,12 +11,9 @@ export function noteBgOwnerAbsence(leg, taskId) {
|
|
|
11
11
|
noted.delete(oldest);
|
|
12
12
|
}
|
|
13
13
|
noted.add(key);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
14
|
+
const delivered = hostLog('warn', `[subagent-owner] ${leg}: no host-run mapping for ${taskId} — not connecting (owner ledger absent ⇒ honest absence; active-run fallback removed per [4000] Q3)`);
|
|
15
|
+
if (!delivered)
|
|
18
16
|
noted.delete(key);
|
|
19
|
-
}
|
|
20
17
|
}
|
|
21
18
|
export function __resetBgOwnerAbsenceForTests() {
|
|
22
19
|
noted.clear();
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TaskRequest } from '@sema-agent/sdk';
|
|
2
|
+
export type TaskRequestReasoningEffort = NonNullable<TaskRequest['reasoningEffort']>;
|
|
3
|
+
export declare const REASONING_EFFORT_WORDS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
4
|
+
export type TaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
|
|
5
|
+
export declare const PERMISSION_MODE_WORDS: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "auto"];
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export const REASONING_EFFORT_WORDS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
|
|
2
|
+
export const PERMISSION_MODE_WORDS = ['default', 'plan', 'acceptEdits', 'bypassPermissions', 'auto'];
|
|
3
|
+
const _taskRequestWordPins = [true, true, true, true];
|
|
4
|
+
void _taskRequestWordPins;
|
package/dist/wireErrorTriage.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readErrorNumber, readErrorString, readErrorStringList } from './wireFailureShape.js';
|
|
2
2
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
3
|
-
import { DRAINING_ERROR_CODE, RESUME_AT_ERROR_CODE_PREFIX, RESUME_POLICY_FOLD_CODE, RESUME_RETRY_LATER_CODES, RESUME_USAGE_WINDOW_EXHAUSTED, SCENARIO_NOT_ALLOWED_ERROR_CODE, } from './engineErrorCodes.js';
|
|
3
|
+
import { DRAINING_ERROR_CODE, isCorruptSessionCode, RESUME_AT_ERROR_CODE_PREFIX, RESUME_POLICY_FOLD_CODE, RESUME_RETRY_LATER_CODES, RESUME_USAGE_WINDOW_EXHAUSTED, SCENARIO_NOT_ALLOWED_ERROR_CODE, } from './engineErrorCodes.js';
|
|
4
4
|
export const WIRE_NETWORK_ERROR_PATTERN = /fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|EHOSTUNREACH|ENETUNREACH|EPIPE|socket hang up|UND_ERR|terminated|other side closed/i;
|
|
5
5
|
function wireErrorDetail(err) {
|
|
6
6
|
const e = err;
|
|
@@ -15,12 +15,14 @@ export function classifyTurnWireError(err) {
|
|
|
15
15
|
const errorCode = typeof e.errorCode === 'string' && e.errorCode.length > 0 ? e.errorCode : undefined;
|
|
16
16
|
const message = typeof e.message === 'string' && e.message.length > 0 ? e.message : String(err);
|
|
17
17
|
const storedRowWhere = corruptStoredRowWhere(err);
|
|
18
|
+
const sessionUnreadable = isCorruptSessionCode(effectiveWireErrorCode(err));
|
|
18
19
|
return {
|
|
19
20
|
kind: 'http',
|
|
20
21
|
status: e.status,
|
|
21
22
|
...(errorCode !== undefined ? { errorCode } : {}),
|
|
22
23
|
message,
|
|
23
24
|
...(storedRowWhere !== undefined ? { storedRowWhere } : {}),
|
|
25
|
+
...(sessionUnreadable ? { sessionUnreadable: true } : {}),
|
|
24
26
|
};
|
|
25
27
|
}
|
|
26
28
|
const errName = typeof err === 'object' && err !== null && typeof err.name === 'string'
|
|
@@ -23,3 +23,13 @@ export interface DenyAttributionRefusalDetail {
|
|
|
23
23
|
}
|
|
24
24
|
export declare function denyAttributionRefusalFromError(err: unknown, sentSettledBy: boolean): DenyAttributionRefusalDetail | null;
|
|
25
25
|
export declare function denyAttributionRefusalContent(detail: Pick<DenyAttributionRefusalDetail, 'kind' | 'field'>): string;
|
|
26
|
+
export declare const CONFLICT_SESSION_CHILDREN_ALIVE = "conflict.session_children_alive";
|
|
27
|
+
export type SessionDeleteConflictKind = 'children_alive' | 'active_run' | 'unrecognized';
|
|
28
|
+
export interface SessionDeleteConflictDetail {
|
|
29
|
+
kind: SessionDeleteConflictKind;
|
|
30
|
+
code?: string;
|
|
31
|
+
remaining?: readonly string[];
|
|
32
|
+
activeTaskId?: string;
|
|
33
|
+
}
|
|
34
|
+
export declare function sessionDeleteConflictFromError(err: unknown): SessionDeleteConflictDetail | null;
|
|
35
|
+
export declare function sessionDeleteConflictContent(detail: Pick<SessionDeleteConflictDetail, 'kind' | 'remaining' | 'activeTaskId'>): string;
|
package/dist/wireRefusalCopy.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { readErrorNumber } from './wireFailureShape.js';
|
|
2
|
-
import { USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
|
|
1
|
+
import { readErrorNumber, readErrorString, readErrorStringList } from './wireFailureShape.js';
|
|
2
|
+
import { ACTIVE_RUN_BUSY_ERROR_CODE, USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
|
|
3
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
3
4
|
export const CONFLICT_APPROVAL_SETTLED = 'conflict.approval_settled';
|
|
4
5
|
export const CONFLICT_RUN_NOT_RUNNING = 'conflict.run_not_running';
|
|
5
6
|
function readStringField(o, key) {
|
|
@@ -151,3 +152,62 @@ export function denyAttributionRefusalContent(detail) {
|
|
|
151
152
|
}
|
|
152
153
|
}
|
|
153
154
|
}
|
|
155
|
+
export const CONFLICT_SESSION_CHILDREN_ALIVE = 'conflict.session_children_alive';
|
|
156
|
+
export function sessionDeleteConflictFromError(err) {
|
|
157
|
+
try {
|
|
158
|
+
if (err === null || typeof err !== 'object')
|
|
159
|
+
return null;
|
|
160
|
+
const o = err;
|
|
161
|
+
const rawStatus = o.status ?? o.statusCode;
|
|
162
|
+
const is409 = rawStatus === 409 || (rawStatus === undefined && o.name === 'ConflictError');
|
|
163
|
+
if (!is409)
|
|
164
|
+
return null;
|
|
165
|
+
const code = readStringField(o, 'errorCode');
|
|
166
|
+
if (code === CONFLICT_SESSION_CHILDREN_ALIVE) {
|
|
167
|
+
const remaining = readErrorStringList(err, 'remaining')?.filter((id) => id.length > 0);
|
|
168
|
+
return { kind: 'children_alive', code, ...(remaining !== undefined && remaining.length > 0 ? { remaining: Object.freeze(remaining) } : {}) };
|
|
169
|
+
}
|
|
170
|
+
if (code === ACTIVE_RUN_BUSY_ERROR_CODE) {
|
|
171
|
+
const activeTaskId = readErrorString(err, 'activeTaskId');
|
|
172
|
+
return { kind: 'active_run', code, ...(activeTaskId !== undefined && activeTaskId.length > 0 ? { activeTaskId } : {}) };
|
|
173
|
+
}
|
|
174
|
+
return { kind: 'unrecognized', ...(code !== undefined ? { code } : {}) };
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const DELETE_CONFLICT_IDS_SHOWN = 3;
|
|
181
|
+
const DELETE_CONFLICT_ID_MAX = 64;
|
|
182
|
+
function sessionDeleteConflictUnrecognizedContent() {
|
|
183
|
+
return ('The engine refused to delete this session with a conflict it did not explain · ' +
|
|
184
|
+
'Check whether the session is still listed and whether anything of it is still running, then try the delete again');
|
|
185
|
+
}
|
|
186
|
+
export function sessionDeleteConflictContent(detail) {
|
|
187
|
+
try {
|
|
188
|
+
if (detail === null || typeof detail !== 'object')
|
|
189
|
+
return sessionDeleteConflictUnrecognizedContent();
|
|
190
|
+
switch (detail.kind) {
|
|
191
|
+
case 'children_alive': {
|
|
192
|
+
const raw = detail.remaining;
|
|
193
|
+
const ids = Array.isArray(raw) ? raw.filter((id) => typeof id === 'string' && id.length > 0) : [];
|
|
194
|
+
const shown = ids.slice(0, DELETE_CONFLICT_IDS_SHOWN).map((id) => capForDisplay(id, DELETE_CONFLICT_ID_MAX));
|
|
195
|
+
const more = ids.length > shown.length ? ` and ${ids.length - shown.length} more` : '';
|
|
196
|
+
const which = shown.length > 0 ? ` (${shown.join(', ')}${more})` : '';
|
|
197
|
+
return (`This session was not deleted: background agents it started are still running where this engine cannot stop them${which} · ` +
|
|
198
|
+
'Nothing was deleted — delete it again once they finish, or stop them where they run first');
|
|
199
|
+
}
|
|
200
|
+
case 'active_run': {
|
|
201
|
+
const active = detail.activeTaskId;
|
|
202
|
+
const run = typeof active === 'string' && active.length > 0 ? ` (run ${capForDisplay(active, DELETE_CONFLICT_ID_MAX)})` : '';
|
|
203
|
+
return (`This session was not deleted: it still has a run in progress${run} · ` +
|
|
204
|
+
'Nothing was deleted — cancel that run or let it finish, then delete the session again');
|
|
205
|
+
}
|
|
206
|
+
default:
|
|
207
|
+
return sessionDeleteConflictUnrecognizedContent();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return sessionDeleteConflictUnrecognizedContent();
|
|
212
|
+
}
|
|
213
|
+
}
|