@sema-agent/client-core 0.92.0 → 0.93.0
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 +179 -0
- package/README.md +23 -16
- package/dist/adapt/arms.js +2 -0
- package/dist/adapt/toolCards.js +6 -4
- package/dist/adapt.d.ts +1 -1
- package/dist/adapt.js +1 -1
- package/dist/adapter/runStream.js +3 -1
- package/dist/agentsWireCaps.js +1 -1
- package/dist/approvalDecidedByCapability.d.ts +16 -0
- package/dist/approvalDecidedByCapability.js +47 -0
- package/dist/approvalsStreamLiveCapability.js +3 -3
- package/dist/askSurvivesStandingAnswer.d.ts +1 -1
- package/dist/centerWiringCapability.js +1 -1
- package/dist/cloudConfigWireCaps.js +5 -5
- package/dist/compensations.js +2 -2
- package/dist/decideReceipt.d.ts +2 -0
- package/dist/decideReceipt.js +34 -9
- package/dist/detachWire.d.ts +2 -14
- package/dist/detachWire.js +1 -47
- package/dist/deviceExecutorManagementCapability.d.ts +6 -2
- package/dist/deviceExecutorManagementCapability.js +61 -15
- package/dist/engineConfigDrift.d.ts +20 -0
- package/dist/engineConfigDrift.js +54 -0
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +12 -4
- package/dist/engineInlineTaskStats.d.ts +1 -0
- package/dist/engineNoticeCodes.d.ts +2 -2
- package/dist/engineWireFor.d.ts +22 -0
- package/dist/engineWireFor.js +154 -9
- package/dist/engineWireSdk.d.ts +0 -1
- package/dist/engineWireSdk.js +0 -3
- package/dist/executionLaneCapability.js +2 -2
- package/dist/fileHistoryCaptureCapability.d.ts +1 -1
- package/dist/fileHistoryCaptureCapability.js +1 -6
- package/dist/fileHistoryLegNotice.d.ts +1 -1
- package/dist/fileHistoryLegNotice.js +1 -6
- package/dist/gateVocabulary.d.ts +1 -5
- package/dist/gateVocabulary.js +4 -22
- package/dist/generated/engineFactTables.d.ts +4 -3
- package/dist/generated/engineFactTables.js +10 -0
- package/dist/generated/engineNoticeTables.d.ts +1 -1
- package/dist/generated/engineNoticeTables.js +1 -1
- package/dist/goalStopHook.d.ts +1 -8
- package/dist/goalStopHook.js +2 -58
- package/dist/headlessPermissionModeWire.d.ts +3 -2
- package/dist/headlessPermissionModeWire.js +46 -5
- package/dist/hitl/askParkRowRouting.js +2 -2
- package/dist/hitl/frameRouter.js +7 -2
- package/dist/hitl/gateLedger.d.ts +1 -0
- package/dist/hitl/gateLedger.js +3 -0
- package/dist/hitl/hitlBridge.d.ts +1 -0
- package/dist/hitl/hitlBridge.js +6 -1
- package/dist/hitl/hitlHostSurface.d.ts +3 -2
- package/dist/hitl/hitlHostSurface.js +18 -3
- package/dist/hitl/parkResolver.js +22 -4
- package/dist/hitl/persistedRulesWire.js +29 -17
- package/dist/hitl/planReviewWire.d.ts +0 -2
- package/dist/hitl/planReviewWire.js +1 -18
- package/dist/hitl/sessionPolicyDeliverable.js +3 -3
- package/dist/hitl/sessionPolicyWire.d.ts +2 -0
- package/dist/hitl/sessionPolicyWire.js +7 -4
- package/dist/hitl/toolApprovalWire.d.ts +22 -7
- package/dist/hitl/toolApprovalWire.js +133 -54
- package/dist/hooksWireCaps.d.ts +1 -1
- package/dist/hooksWireCaps.js +1 -1
- package/dist/index.d.ts +7 -2
- package/dist/index.js +6 -2
- package/dist/inheritEnvWire.d.ts +1 -4
- package/dist/inheritEnvWire.js +0 -18
- package/dist/limitsWire.d.ts +0 -6
- package/dist/limitsWire.js +0 -38
- package/dist/mcpProbeCapability.js +1 -1
- package/dist/mcpProbeWire.js +10 -12
- package/dist/mcpReconnect.d.ts +5 -0
- package/dist/mcpReconnect.js +28 -0
- package/dist/memoryComplianceCapability.js +2 -2
- package/dist/memoryOriginCapability.js +2 -2
- package/dist/memoryVerbsWire.js +14 -13
- package/dist/ownKey.d.ts +2 -0
- package/dist/ownKey.js +15 -0
- package/dist/peerLaneCapability.js +2 -2
- package/dist/permissionModesCapability.d.ts +16 -0
- package/dist/permissionModesCapability.js +71 -0
- package/dist/permissionRuleIssue.js +1 -1
- package/dist/permissionRulesWriteCapability.js +2 -2
- package/dist/permissionWireCaps.d.ts +2 -0
- package/dist/permissionWireCaps.js +8 -1
- package/dist/permissionsAllowGrantCapability.js +7 -2
- package/dist/postureKnob.js +1 -1
- package/dist/protocolSegment.d.ts +2 -0
- package/dist/protocolSegment.js +3 -0
- package/dist/readFacePosture.js +1 -1
- package/dist/resumeRefusalCopy.d.ts +1 -0
- package/dist/resumeRefusalCopy.js +26 -3
- package/dist/retryStatus.js +2 -1
- package/dist/rewindArchiveCapability.js +2 -2
- package/dist/runTerminal.d.ts +16 -0
- package/dist/runTerminal.js +32 -3
- package/dist/seam.d.ts +1 -0
- package/dist/sessionPolicyCapability.js +1 -1
- package/dist/sessionSyncFileHistoryCapability.js +2 -2
- package/dist/skillToolListsCapability.js +1 -1
- package/dist/sqlEngineCapability.js +1 -1
- package/dist/steerPriorityCapability.js +1 -1
- package/dist/storePostureCapability.js +2 -2
- package/dist/supportFloor.d.ts +1 -0
- package/dist/supportFloor.js +13 -3
- package/dist/taskRequestWords.d.ts +4 -2
- package/dist/taskRequestWords.js +1 -1
- package/dist/toolHistoryBadTurn.js +1 -2
- package/dist/toolResult.d.ts +2 -1
- package/dist/toolResult.js +33 -3
- package/dist/userInvokedSkillsCapability.js +1 -1
- package/dist/webSearchBackendCapability.js +2 -2
- package/dist/webSearchWireCaps.d.ts +1 -1
- package/dist/webSearchWireCaps.js +40 -2
- package/dist/wireFailureShape.js +2 -2
- package/dist/wireRefusalCopy.d.ts +9 -3
- package/dist/wireRefusalCopy.js +41 -4
- package/dist/workflowsJournalCapability.js +1 -1
- package/dist/writeProtectionCapability.js +1 -1
- package/docs/INTEGRATION-CLIENTS.md +901 -15
- package/package.json +2 -2
package/dist/limitsWire.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ export declare const MAX_TURNS_MIN = 1;
|
|
|
8
8
|
export declare const MAX_TURNS_MAX: number;
|
|
9
9
|
export declare const MAX_TOKENS_MIN = 1;
|
|
10
10
|
export declare const MAX_TOKENS_MAX: number;
|
|
11
|
-
export declare const LIMITS_WIRE_MIN_ENGINE: readonly [number, number, number];
|
|
12
11
|
export interface HeadlessLimits {
|
|
13
12
|
maxWalltimeMs?: number;
|
|
14
13
|
maxTurns?: number;
|
|
@@ -32,8 +31,3 @@ export type HeadlessEnvLimitsResult = {
|
|
|
32
31
|
};
|
|
33
32
|
export declare function headlessLimitsFromEnv(env?: EnvLike): HeadlessEnvLimitsResult;
|
|
34
33
|
export declare function limitsForPrint(argv: string[], env?: EnvLike): LimitsParseResult;
|
|
35
|
-
export declare function versionSupportsLimits(v: string | undefined): boolean;
|
|
36
|
-
export declare function engineLimitsSupportWarning(baseUrl: string, flaggedFlags: string[], opts?: {
|
|
37
|
-
fetchImpl?: typeof fetch;
|
|
38
|
-
timeoutMs?: number;
|
|
39
|
-
}): Promise<string | undefined>;
|
package/dist/limitsWire.js
CHANGED
|
@@ -10,7 +10,6 @@ export const MAX_TURNS_MAX = Number.MAX_SAFE_INTEGER;
|
|
|
10
10
|
export const MAX_TOKENS_MIN = 1;
|
|
11
11
|
export const MAX_TOKENS_MAX = Number.MAX_SAFE_INTEGER;
|
|
12
12
|
const SEC_TO_MS = 1_000;
|
|
13
|
-
export const LIMITS_WIRE_MIN_ENGINE = [1, 196, 0];
|
|
14
13
|
const DEADLINE_USAGE = `sema: --deadline expects a whole number of seconds between ${DEADLINE_SEC_MIN} and ${DEADLINE_SEC_MAX}.\n` +
|
|
15
14
|
' --deadline <sec> wall-clock budget for this run (sema superset — no upstream equivalent):\n' +
|
|
16
15
|
' reaching it ends the run LOUDLY (failed / limits.max_walltime_exceeded)\n' +
|
|
@@ -112,40 +111,3 @@ export function limitsForPrint(argv, env = hostEnv()) {
|
|
|
112
111
|
const merged = { ...(fromEnv.limits ?? {}), ...(parsed.limits ?? {}) };
|
|
113
112
|
return { ok: true, ...(hasAnyLimit(merged) ? { limits: merged } : {}), flaggedFlags: parsed.flaggedFlags };
|
|
114
113
|
}
|
|
115
|
-
export function versionSupportsLimits(v) {
|
|
116
|
-
const m = v ? /^(\d+)\.(\d+)\.(\d+)/.exec(v) : null;
|
|
117
|
-
if (!m)
|
|
118
|
-
return false;
|
|
119
|
-
const [a, b, c] = [Number(m[1]), Number(m[2]), Number(m[3])];
|
|
120
|
-
const [ma, mb, mc] = LIMITS_WIRE_MIN_ENGINE;
|
|
121
|
-
if (a !== ma)
|
|
122
|
-
return a > ma;
|
|
123
|
-
if (b !== mb)
|
|
124
|
-
return b > mb;
|
|
125
|
-
return c >= mc;
|
|
126
|
-
}
|
|
127
|
-
export async function engineLimitsSupportWarning(baseUrl, flaggedFlags, opts) {
|
|
128
|
-
const fetchImpl = opts?.fetchImpl ?? fetch;
|
|
129
|
-
const controller = new AbortController();
|
|
130
|
-
const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 1500);
|
|
131
|
-
try {
|
|
132
|
-
const res = await fetchImpl(`${baseUrl.replace(/\/+$/, '')}/health`, {
|
|
133
|
-
method: 'GET',
|
|
134
|
-
signal: controller.signal,
|
|
135
|
-
});
|
|
136
|
-
if (!res.ok)
|
|
137
|
-
return undefined;
|
|
138
|
-
const body = (await res.json());
|
|
139
|
-
const version = typeof body?.version === 'string' ? body.version : undefined;
|
|
140
|
-
if (versionSupportsLimits(version))
|
|
141
|
-
return undefined;
|
|
142
|
-
const flags = flaggedFlags.length > 0 ? flaggedFlags.join('/') : '--deadline/--max-turns/--max-tokens';
|
|
143
|
-
return `[sema] engine ${version ?? '(unversioned, pre-1.96)'} does not support ${flags} (needs ≥1.196.0); the flag will be ignored by the engine`;
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return undefined;
|
|
147
|
-
}
|
|
148
|
-
finally {
|
|
149
|
-
clearTimeout(timer);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
@@ -34,7 +34,7 @@ export function mcpProbeDoctorDetail(reading) {
|
|
|
34
34
|
switch (reading.kind) {
|
|
35
35
|
case 'unobserved':
|
|
36
36
|
return ('per-server MCP status face not observed — the engine reports whether it has one on /v1/capabilities; ' +
|
|
37
|
-
'this process has no usable capabilities reading cached for it (none received, or the last one was
|
|
37
|
+
'this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)');
|
|
38
38
|
case 'not_reported':
|
|
39
39
|
return ('per-server MCP status face not reported by this engine — this binary predates the face, so that path is not expected to exist here ' +
|
|
40
40
|
'(asking it usually answers with a machine-readable route-not-found, though a timeout or an uncoded gateway answer still says nothing either way); ' +
|
package/dist/mcpProbeWire.js
CHANGED
|
@@ -111,32 +111,32 @@ function narrowProbeFace(body) {
|
|
|
111
111
|
let servers;
|
|
112
112
|
try {
|
|
113
113
|
if (!plainObject(body))
|
|
114
|
-
return { ok: false, why: 'result_unreadable' };
|
|
114
|
+
return { ok: false, why: 'result_unreadable', what: 'the body is not a plain object' };
|
|
115
115
|
probedAt = body.probedAt;
|
|
116
116
|
ttlSec = body.ttlSec;
|
|
117
117
|
servers = body.servers;
|
|
118
118
|
}
|
|
119
119
|
catch {
|
|
120
|
-
return { ok: false, why: 'result_unreadable' };
|
|
120
|
+
return { ok: false, why: 'result_unreadable', what: 'the body threw when read' };
|
|
121
121
|
}
|
|
122
122
|
if (!nonEmptyString(probedAt))
|
|
123
|
-
return { ok: false, why: 'result_unreadable' };
|
|
123
|
+
return { ok: false, why: 'result_unreadable', what: 'the reading time is missing or empty' };
|
|
124
124
|
if (typeof ttlSec !== 'number' || !Number.isFinite(ttlSec) || ttlSec < 0)
|
|
125
|
-
return { ok: false, why: 'result_unreadable' };
|
|
125
|
+
return { ok: false, why: 'result_unreadable', what: 'the reuse window is not a non-negative number' };
|
|
126
126
|
const raw = snapshotArray(servers);
|
|
127
127
|
if (raw === null)
|
|
128
|
-
return { ok: false, why: 'result_unreadable' };
|
|
128
|
+
return { ok: false, why: 'result_unreadable', what: 'the per-server rows are not a readable list' };
|
|
129
129
|
let rows;
|
|
130
130
|
try {
|
|
131
131
|
rows = projectMcpSection(raw);
|
|
132
132
|
}
|
|
133
133
|
catch {
|
|
134
|
-
return { ok: false, why: 'result_unreadable' };
|
|
134
|
+
return { ok: false, why: 'result_unreadable', what: 'the per-server rows could not be read' };
|
|
135
135
|
}
|
|
136
136
|
if (rows === undefined)
|
|
137
|
-
return { ok: false, why: 'result_unreadable' };
|
|
137
|
+
return { ok: false, why: 'result_unreadable', what: 'none of the per-server rows could be read' };
|
|
138
138
|
if (rows.length !== raw.length)
|
|
139
|
-
return { ok: false, why: 'result_unreadable' };
|
|
139
|
+
return { ok: false, why: 'result_unreadable', what: 'some of the per-server rows could not be read' };
|
|
140
140
|
return { ok: true, face: { probedAt, ttlSec, servers: rows } };
|
|
141
141
|
}
|
|
142
142
|
const FACE_ABSENT_MESSAGE = 'this engine reports no per-server MCP status face on this deployment, so nothing was sent; ' +
|
|
@@ -172,8 +172,7 @@ export async function readMcpCapabilities(facade, call, opts) {
|
|
|
172
172
|
}
|
|
173
173
|
const narrowed = narrowProbeFace(res);
|
|
174
174
|
if (!narrowed.ok) {
|
|
175
|
-
return unknownBy(narrowed.why,
|
|
176
|
-
'(the reading time, the reuse window, or the per-server rows) — refusing to present it as a reading, ' +
|
|
175
|
+
return unknownBy(narrowed.why, `the per-server MCP status call answered 2xx with a body this client cannot stand behind (${narrowed.what}) — refusing to present it as a reading, ` +
|
|
177
176
|
'because a normal-looking empty list is exactly the false answer this face exists to remove');
|
|
178
177
|
}
|
|
179
178
|
return { status: 'face', face: narrowed.face };
|
|
@@ -206,8 +205,7 @@ export async function probeMcpServers(facade, call, opts) {
|
|
|
206
205
|
}
|
|
207
206
|
const narrowed = narrowProbeFace(res);
|
|
208
207
|
if (!narrowed.ok) {
|
|
209
|
-
return unknownBy(narrowed.why,
|
|
210
|
-
'(the reading time, the reuse window, or the per-server rows) — refusing to present it as a reading');
|
|
208
|
+
return unknownBy(narrowed.why, `the declaration probe answered 2xx with a body this client cannot stand behind (${narrowed.what}) — refusing to present it as a reading`);
|
|
211
209
|
}
|
|
212
210
|
if (narrowed.face.servers.length !== submitted.length) {
|
|
213
211
|
return unknownBy('index_misaligned', `the declaration probe answered with ${narrowed.face.servers.length} row(s) for ${submitted.length} declaration(s) — ` +
|
package/dist/mcpReconnect.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const MCP_RECONNECT_OUTCOMES: readonly string[];
|
|
2
2
|
export declare const MCP_RECONNECT_TRANSACTION_NOTICE: string;
|
|
3
|
+
export declare const MCP_RECONNECT_UNSETTLED_NOTICE: string;
|
|
3
4
|
export interface McpReconnectStatusView {
|
|
4
5
|
name: string;
|
|
5
6
|
status: string;
|
|
@@ -51,6 +52,10 @@ export type McpReconnectVerdict = {
|
|
|
51
52
|
readonly reason: 'capability' | 'route' | 'feature';
|
|
52
53
|
} | {
|
|
53
54
|
readonly kind: 'not_found';
|
|
55
|
+
} | {
|
|
56
|
+
readonly kind: 'unsettled';
|
|
57
|
+
readonly errorCode: 'state.mcp_reconnect_unsettled';
|
|
58
|
+
readonly error?: string;
|
|
54
59
|
} | {
|
|
55
60
|
readonly kind: 'failed';
|
|
56
61
|
readonly error: unknown;
|
package/dist/mcpReconnect.js
CHANGED
|
@@ -2,6 +2,9 @@ import { capForDisplay } from './fleetTaskDesc.js';
|
|
|
2
2
|
export const MCP_RECONNECT_OUTCOMES = Object.freeze(['accepted', 'refused', 'unsupported']);
|
|
3
3
|
export const MCP_RECONNECT_TRANSACTION_NOTICE = 'Reconnecting closes the current connection before dialing again. On a healthy server this is a transaction, ' +
|
|
4
4
|
'not a refresh: a failed re-dial leaves the server down and withdraws its tools from the model until a later re-dial succeeds.';
|
|
5
|
+
export const MCP_RECONNECT_UNSETTLED_NOTICE = 'The MCP re-dial did not settle within the time this request waits, so there is no result yet: the re-dial was not cancelled and may still be running. ' +
|
|
6
|
+
"Check the server's state before re-dialing it again — a second request that arrives after the dial has settled closes the connection and dials once more.";
|
|
7
|
+
const UNSETTLED_CODE = 'state.mcp_reconnect_unsettled';
|
|
5
8
|
const RECONNECT_TEXT_MAX = 200;
|
|
6
9
|
const RECONNECT_WORD_MAX = 40;
|
|
7
10
|
const SERVER_NAME_MAX = 190;
|
|
@@ -88,6 +91,27 @@ export function projectMcpReconnectResult(body) {
|
|
|
88
91
|
}
|
|
89
92
|
return view;
|
|
90
93
|
}
|
|
94
|
+
function unsettledVerdict(carrier) {
|
|
95
|
+
let text;
|
|
96
|
+
try {
|
|
97
|
+
const r = carrier;
|
|
98
|
+
text = nonEmpty(r.error) ? r.error : r.message;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
text = undefined;
|
|
102
|
+
}
|
|
103
|
+
return nonEmpty(text)
|
|
104
|
+
? { kind: 'unsettled', errorCode: UNSETTLED_CODE, error: capForDisplay(text, RECONNECT_TEXT_MAX) }
|
|
105
|
+
: { kind: 'unsettled', errorCode: UNSETTLED_CODE };
|
|
106
|
+
}
|
|
107
|
+
function isUnsettledBody(raw) {
|
|
108
|
+
try {
|
|
109
|
+
return isRecord(raw) && !('outcome' in raw) && raw.errorCode === UNSETTLED_CODE;
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
91
115
|
export function classifyMcpReconnectFailure(e) {
|
|
92
116
|
let code;
|
|
93
117
|
try {
|
|
@@ -96,6 +120,8 @@ export function classifyMcpReconnectFailure(e) {
|
|
|
96
120
|
catch {
|
|
97
121
|
return { kind: 'failed', error: e };
|
|
98
122
|
}
|
|
123
|
+
if (code === UNSETTLED_CODE)
|
|
124
|
+
return unsettledVerdict(e);
|
|
99
125
|
if (code === 'steering.invalid_content')
|
|
100
126
|
return { kind: 'invalid' };
|
|
101
127
|
if (code === 'steering.not_running')
|
|
@@ -123,6 +149,8 @@ export async function reconnectMcpServer(client, sessionId, server, opts) {
|
|
|
123
149
|
return { kind: 'failed', error: opts.signal.reason ?? new Error('mcpReconnect: aborted before the verb was called') };
|
|
124
150
|
}
|
|
125
151
|
const raw = await client.sessions.mcpReconnect(sessionId, { server }, opts?.signal ? { signal: opts.signal } : undefined);
|
|
152
|
+
if (isUnsettledBody(raw))
|
|
153
|
+
return unsettledVerdict(raw);
|
|
126
154
|
const view = projectMcpReconnectResult(raw);
|
|
127
155
|
if (view === undefined)
|
|
128
156
|
return { kind: 'failed', error: new Error('mcpReconnect: malformed body') };
|
|
@@ -35,9 +35,9 @@ export function memoryComplianceVerbsAvailable(reading) {
|
|
|
35
35
|
export function memoryComplianceDoctorDetail(reading) {
|
|
36
36
|
switch (reading.kind) {
|
|
37
37
|
case 'unobserved':
|
|
38
|
-
return 'memory compliance face not observed — the engine reports it on /v1/capabilities (memoryCompliance); this process has no usable capabilities reading cached for it (none received, or the last one was
|
|
38
|
+
return 'memory compliance face not observed — the engine reports it on /v1/capabilities (memoryCompliance); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
|
|
39
39
|
case 'not_reported':
|
|
40
|
-
return 'memory compliance face not reported by this engine
|
|
40
|
+
return 'memory compliance face not reported by this engine; this does not say whether the provenance and erasure endpoints exist, probe them directly';
|
|
41
41
|
case 'absent':
|
|
42
42
|
return `memory compliance face off on this deployment — the entry-provenance and erasure endpoints are unavailable (${MEMORY_ENGINE_REQUIRED_CODE}); the engine does not say which half is missing (a memory backend that does not own the engine control plane, or an empty operator roster), so do not guess`;
|
|
43
43
|
case 'present':
|
|
@@ -35,9 +35,9 @@ export function memoryOriginVerbsAvailable(reading) {
|
|
|
35
35
|
export function memoryOriginDoctorDetail(reading) {
|
|
36
36
|
switch (reading.kind) {
|
|
37
37
|
case 'unobserved':
|
|
38
|
-
return 'external-origin face not observed — the engine reports it on /v1/capabilities (memoryOrigin); this process has no usable capabilities reading cached for it (none received, or the last one was
|
|
38
|
+
return 'external-origin face not observed — the engine reports it on /v1/capabilities (memoryOrigin); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
|
|
39
39
|
case 'not_reported':
|
|
40
|
-
return 'external-origin face not reported by this engine
|
|
40
|
+
return 'external-origin face not reported by this engine; this does not say whether the origin listing and clearance endpoints exist, probe them directly';
|
|
41
41
|
case 'absent':
|
|
42
42
|
return `external-origin face off on this deployment — the origin listing and clearance endpoints are unavailable (${MEMORY_ENGINE_REQUIRED_CODE}); the engine does not say which half is missing (a memory backend that does not own the engine control plane, or an empty operator roster), so do not guess`;
|
|
43
43
|
case 'present':
|
package/dist/memoryVerbsWire.js
CHANGED
|
@@ -176,7 +176,7 @@ function narrowProvenanceAccount(body, entryId) {
|
|
|
176
176
|
let exclusionCode;
|
|
177
177
|
try {
|
|
178
178
|
if (!plainObject(body))
|
|
179
|
-
return { ok: false, why: 'result_unreadable' };
|
|
179
|
+
return { ok: false, why: 'result_unreadable', what: 'the body is not a plain object' };
|
|
180
180
|
v = body.v;
|
|
181
181
|
id = body.id;
|
|
182
182
|
binding = body.binding;
|
|
@@ -191,27 +191,28 @@ function narrowProvenanceAccount(body, entryId) {
|
|
|
191
191
|
exclusionCode = plainObject(exclusion) ? exclusion.code : undefined;
|
|
192
192
|
}
|
|
193
193
|
catch {
|
|
194
|
-
return { ok: false, why: 'result_unreadable' };
|
|
194
|
+
return { ok: false, why: 'result_unreadable', what: 'the body threw when read' };
|
|
195
195
|
}
|
|
196
196
|
if (typeof v !== 'number' || !Number.isFinite(v))
|
|
197
|
-
return { ok: false, why: 'result_unreadable' };
|
|
197
|
+
return { ok: false, why: 'result_unreadable', what: 'the envelope version is missing or not a number' };
|
|
198
198
|
if (v !== PROVENANCE_ACCOUNT_VERSION)
|
|
199
|
-
return { ok: false, why: 'unsupported_version' };
|
|
199
|
+
return { ok: false, why: 'unsupported_version', what: 'the envelope version is newer than this client knows' };
|
|
200
200
|
if (!nonEmptyString(id))
|
|
201
|
-
return { ok: false, why: 'result_unreadable' };
|
|
201
|
+
return { ok: false, why: 'result_unreadable', what: 'the entry id is missing or empty' };
|
|
202
202
|
if (!plainObject(binding) || !nonEmptyString(bindingState))
|
|
203
|
-
return { ok: false, why: 'result_unreadable' };
|
|
203
|
+
return { ok: false, why: 'result_unreadable', what: 'the binding is missing or has no usable state' };
|
|
204
204
|
if (!plainObject(custody) || !nonEmptyString(custodyState) || !Array.isArray(custodyEvents)) {
|
|
205
|
-
return { ok: false, why: 'result_unreadable' };
|
|
206
|
-
}
|
|
207
|
-
if (!Array.isArray(contributors) || !nonEmptyString(contentState)) {
|
|
208
|
-
return { ok: false, why: 'result_unreadable' };
|
|
205
|
+
return { ok: false, why: 'result_unreadable', what: 'the custody chain is missing or has no usable state or event list' };
|
|
209
206
|
}
|
|
207
|
+
if (!Array.isArray(contributors))
|
|
208
|
+
return { ok: false, why: 'result_unreadable', what: 'the contributor list is missing or not a list' };
|
|
209
|
+
if (!nonEmptyString(contentState))
|
|
210
|
+
return { ok: false, why: 'result_unreadable', what: 'the content state is missing or empty' };
|
|
210
211
|
if (hasExclusion && !(plainObject(exclusion) && nonEmptyString(exclusionCode))) {
|
|
211
|
-
return { ok: false, why: 'result_unreadable' };
|
|
212
|
+
return { ok: false, why: 'result_unreadable', what: 'the exclusion record is present but unreadable' };
|
|
212
213
|
}
|
|
213
214
|
if (id !== entryId)
|
|
214
|
-
return { ok: false, why: 'identity_mismatch' };
|
|
215
|
+
return { ok: false, why: 'identity_mismatch', what: 'the account is for a different entry id' };
|
|
215
216
|
return { ok: true, account: body };
|
|
216
217
|
}
|
|
217
218
|
export async function readEntryProvenance(facade, call, opts) {
|
|
@@ -238,7 +239,7 @@ export async function readEntryProvenance(facade, call, opts) {
|
|
|
238
239
|
? 'the provenance account came back stamped with a newer envelope version than this client knows — refusing to reinterpret it; upgrade the client'
|
|
239
240
|
: narrowed.why === 'identity_mismatch'
|
|
240
241
|
? 'the provenance account came back for a different entry id than the one that was asked about — refusing to present it as this entry account'
|
|
241
|
-
:
|
|
242
|
+
: `the provenance call answered 2xx with a body this client cannot branch on (${narrowed.what}) — refusing to present it as an account`);
|
|
242
243
|
}
|
|
243
244
|
return { status: 'account', account: narrowed.account };
|
|
244
245
|
}
|
package/dist/ownKey.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export declare function putOwnKey<T>(table: Record<string, T>, key: string, value: T): void;
|
|
2
2
|
export declare function readOwnNonEmptyString(v: object | null | undefined, key: string): string | undefined;
|
|
3
|
+
export declare function readOwnDataNonEmptyString(v: object | null | undefined, key: string): string | undefined;
|
|
4
|
+
export declare function readOwnDataValue<T>(v: object | null | undefined, key: string): T | undefined;
|
package/dist/ownKey.js
CHANGED
|
@@ -7,3 +7,18 @@ export function readOwnNonEmptyString(v, key) {
|
|
|
7
7
|
const raw = v[key];
|
|
8
8
|
return typeof raw === 'string' && raw !== '' ? raw : undefined;
|
|
9
9
|
}
|
|
10
|
+
export function readOwnDataNonEmptyString(v, key) {
|
|
11
|
+
if (typeof v !== 'object' || v === null)
|
|
12
|
+
return undefined;
|
|
13
|
+
const d = Object.getOwnPropertyDescriptor(v, key);
|
|
14
|
+
if (d === undefined || !Object.hasOwn(d, 'value'))
|
|
15
|
+
return undefined;
|
|
16
|
+
const raw = d.value;
|
|
17
|
+
return typeof raw === 'string' && raw !== '' ? raw : undefined;
|
|
18
|
+
}
|
|
19
|
+
export function readOwnDataValue(v, key) {
|
|
20
|
+
if (typeof v !== 'object' || v === null)
|
|
21
|
+
return undefined;
|
|
22
|
+
const d = Object.getOwnPropertyDescriptor(v, key);
|
|
23
|
+
return d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
|
|
24
|
+
}
|
|
@@ -35,9 +35,9 @@ export function peerLaneAvailable(reading) {
|
|
|
35
35
|
export function peerLaneDoctorDetail(reading) {
|
|
36
36
|
switch (reading.kind) {
|
|
37
37
|
case 'unobserved':
|
|
38
|
-
return 'cross-session lane not observed — the engine reports it on /v1/capabilities (peerLane); this process has no usable capabilities reading cached for it (none received, or the last one was
|
|
38
|
+
return 'cross-session lane not observed — the engine reports it on /v1/capabilities (peerLane); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
|
|
39
39
|
case 'not_reported':
|
|
40
|
-
return 'cross-session lane not reported by this engine —
|
|
40
|
+
return 'cross-session lane not reported by this engine — its capabilities reading carries no entry for the lane, so whether this deployment has cross-session discovery or delivery is unknown to this client';
|
|
41
41
|
case 'absent':
|
|
42
42
|
return 'no cross-session lane on this deployment — cross-session discovery and delivery (listing other sessions, addressing one by id) are unavailable';
|
|
43
43
|
case 'present':
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type PermissionModesReading = {
|
|
2
|
+
kind: 'unobserved';
|
|
3
|
+
} | {
|
|
4
|
+
kind: 'not_reported';
|
|
5
|
+
} | {
|
|
6
|
+
kind: 'present';
|
|
7
|
+
modes: readonly string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function projectPermissionModesCapability(caps: unknown): PermissionModesReading | undefined;
|
|
10
|
+
export declare function noteEngineCapsForPermissionModes(baseUrl: string, caps: unknown, opts?: {
|
|
11
|
+
generation?: number;
|
|
12
|
+
}): void;
|
|
13
|
+
export declare function observedPermissionModes(baseUrl?: string | undefined): PermissionModesReading;
|
|
14
|
+
export declare function permissionModeAdmitted(reading: PermissionModesReading | undefined, mode: string): boolean;
|
|
15
|
+
export declare function forgetPermissionModesReading(baseUrl: string | undefined): void;
|
|
16
|
+
export declare function __resetPermissionModesReadingsForTests(): void;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
2
|
+
import { createEngineCapReader } from './engineCapReader.js';
|
|
3
|
+
import { readOwnDataValue } from './ownKey.js';
|
|
4
|
+
function readModesStrict(v) {
|
|
5
|
+
try {
|
|
6
|
+
if (!Array.isArray(v))
|
|
7
|
+
return undefined;
|
|
8
|
+
const length = v.length;
|
|
9
|
+
if (length === 0)
|
|
10
|
+
return undefined;
|
|
11
|
+
const modes = [];
|
|
12
|
+
for (let i = 0; i < length; i++) {
|
|
13
|
+
const item = readOwnDataValue(v, String(i));
|
|
14
|
+
if (typeof item !== 'string' || item.length === 0)
|
|
15
|
+
return undefined;
|
|
16
|
+
modes.push(item);
|
|
17
|
+
}
|
|
18
|
+
return modes;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return undefined;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export function projectPermissionModesCapability(caps) {
|
|
25
|
+
try {
|
|
26
|
+
if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
|
|
27
|
+
return undefined;
|
|
28
|
+
if (!Object.hasOwn(caps, 'permissionModes'))
|
|
29
|
+
return { kind: 'not_reported' };
|
|
30
|
+
const v = caps.permissionModes;
|
|
31
|
+
if (v === undefined)
|
|
32
|
+
return { kind: 'not_reported' };
|
|
33
|
+
const modes = readModesStrict(v);
|
|
34
|
+
return modes === undefined ? undefined : { kind: 'present', modes };
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
const reader = createEngineCapReader({
|
|
41
|
+
name: 'permissionModes',
|
|
42
|
+
project: projectPermissionModesCapability,
|
|
43
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
44
|
+
});
|
|
45
|
+
export function noteEngineCapsForPermissionModes(baseUrl, caps, opts) {
|
|
46
|
+
reader.note(baseUrl, caps, opts);
|
|
47
|
+
}
|
|
48
|
+
export function observedPermissionModes(baseUrl = engineWireTarget()?.baseUrl) {
|
|
49
|
+
return reader.observed(baseUrl);
|
|
50
|
+
}
|
|
51
|
+
export function permissionModeAdmitted(reading, mode) {
|
|
52
|
+
try {
|
|
53
|
+
if (typeof mode !== 'string' || mode.length === 0)
|
|
54
|
+
return false;
|
|
55
|
+
if (reading === null || reading === undefined || typeof reading !== 'object' || Array.isArray(reading))
|
|
56
|
+
return false;
|
|
57
|
+
if (readOwnDataValue(reading, 'kind') !== 'present')
|
|
58
|
+
return false;
|
|
59
|
+
const modes = readModesStrict(readOwnDataValue(reading, 'modes'));
|
|
60
|
+
return modes !== undefined && modes.includes(mode);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function forgetPermissionModesReading(baseUrl) {
|
|
67
|
+
reader.forget(baseUrl);
|
|
68
|
+
}
|
|
69
|
+
export function __resetPermissionModesReadingsForTests() {
|
|
70
|
+
reader.__resetForTests();
|
|
71
|
+
}
|
|
@@ -23,7 +23,7 @@ const ISSUE_SENTENCES = Object.freeze({
|
|
|
23
23
|
'invalid.paren': 'this rule is invalid: its parentheses do not parse (escape a literal one as \\( or \\))',
|
|
24
24
|
'invalid.param_split': 'this rule is invalid: its parentheses do not hold a parameter rule written name:pattern',
|
|
25
25
|
'invalid.primary_field': "this rule is invalid: it patterns the tool's primary field, which matches the whole trimmed string rather than a prefix",
|
|
26
|
-
'invalid.cap_exceeded': 'this rule set is invalid: it is over one of the deployment caps (rule count, rule length, pattern length, or star count)',
|
|
26
|
+
'invalid.cap_exceeded': 'this rule set is invalid: it is over one of the deployment caps (rule count, rule length, pattern length, or star count); this client cannot tell which from the code alone',
|
|
27
27
|
'invalid.behavior': 'this rule is invalid: its behavior is neither deny nor ask, so it would never fire',
|
|
28
28
|
'invalid.command_rule': 'this rule is invalid: the command pattern was rejected by the command-rule validator',
|
|
29
29
|
'invalid.entry_type': 'this allow list is invalid: it must be a list of rule strings, and it or one of its entries is not a string',
|
|
@@ -31,11 +31,11 @@ export function permissionRulesWriteAvailable(reading) {
|
|
|
31
31
|
export function permissionRulesWriteDoctorDetail(reading) {
|
|
32
32
|
switch (reading.kind) {
|
|
33
33
|
case 'unobserved':
|
|
34
|
-
return 'permission-rule write entry not observed — the engine reports it on /v1/capabilities (permissionRulesWrite); this process has no usable capabilities reading cached for it (none received, or the last one was
|
|
34
|
+
return 'permission-rule write entry not observed — the engine reports it on /v1/capabilities (permissionRulesWrite); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
|
|
35
35
|
case 'not_reported':
|
|
36
36
|
return 'permission-rule write entry not reported by this engine — this binary predates the entry itself; hide it rather than calling it';
|
|
37
37
|
case 'absent':
|
|
38
|
-
return 'permission-rule write entry unreachable for this call — either the rules lane is not open on this deployment, or this call cannot bring a verifiable identity';
|
|
38
|
+
return 'permission-rule write entry unreachable for this call — either the rules lane is not open on this deployment, or this call cannot bring a verifiable identity; the engine reports one bit for both, so this client cannot tell which';
|
|
39
39
|
case 'present':
|
|
40
40
|
return 'permission-rule write entry reachable for this call — a per-call attempt still answers for itself whether the direction, text, and scope are accepted';
|
|
41
41
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { type TaskRequestPermissionMode } from './taskRequestWords.js';
|
|
2
|
+
import { type PermissionModesReading } from './permissionModesCapability.js';
|
|
2
3
|
export type PermissionMode = TaskRequestPermissionMode;
|
|
3
4
|
export interface PermissionModeField {
|
|
4
5
|
permissionMode?: PermissionMode;
|
|
5
6
|
}
|
|
6
7
|
export declare function permissionModeField(mode: string | undefined, opts?: {
|
|
7
8
|
stampDefault?: boolean;
|
|
9
|
+
permissionModes?: PermissionModesReading | undefined;
|
|
8
10
|
}): PermissionModeField;
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { PERMISSION_MODE_WORDS } from './taskRequestWords.js';
|
|
2
|
-
|
|
2
|
+
import { permissionModeAdmitted } from './permissionModesCapability.js';
|
|
3
|
+
const BIT_GATED_MODE = 'dontAsk';
|
|
4
|
+
const KNOWN = new Set(PERMISSION_MODE_WORDS.filter((w) => w !== BIT_GATED_MODE));
|
|
3
5
|
export function permissionModeField(mode, opts) {
|
|
4
6
|
if (typeof mode === 'string' && mode !== 'default' && KNOWN.has(mode)) {
|
|
5
7
|
return { permissionMode: mode };
|
|
6
8
|
}
|
|
9
|
+
if (mode === BIT_GATED_MODE) {
|
|
10
|
+
if (permissionModeAdmitted(opts?.permissionModes, mode))
|
|
11
|
+
return { permissionMode: mode };
|
|
12
|
+
return opts?.stampDefault === true ? { permissionMode: 'default' } : {};
|
|
13
|
+
}
|
|
7
14
|
if (opts?.stampDefault === true && (mode === undefined || mode === 'default')) {
|
|
8
15
|
return { permissionMode: 'default' };
|
|
9
16
|
}
|
|
@@ -65,7 +65,12 @@ export function permissionsAllowGrantDoctorDetail(reading, settingsRead) {
|
|
|
65
65
|
return 'allow rules sent to the engine — it reads them as rules, not as an exclusive list: a matching call counts as answered for, and the engine still raises its own prompts where it requires them';
|
|
66
66
|
}
|
|
67
67
|
if (gate === 'settings_read_not_clean') {
|
|
68
|
-
|
|
68
|
+
const why = settingsRead === 'partial'
|
|
69
|
+
? 'was reported partial (at least one settings file carried a bad field)'
|
|
70
|
+
: settingsRead === 'unreadable'
|
|
71
|
+
? 'was reported unreadable (at least one settings file could not be read)'
|
|
72
|
+
: 'came with no recognized read health';
|
|
73
|
+
return `allow rules from settings files kept on this side — this engine reads allow rules as rules, but the settings read that produced them ${why}, so they are not sent; command-line and session allow rules are still sent, and matching approvals are still answered here`;
|
|
69
74
|
}
|
|
70
75
|
const kind = ownReadingField(reading, 'kind');
|
|
71
76
|
if (kind === 'present' && ownReadingField(reading, 'grants') === false) {
|
|
@@ -74,7 +79,7 @@ export function permissionsAllowGrantDoctorDetail(reading, settingsRead) {
|
|
|
74
79
|
if (kind === 'not_reported') {
|
|
75
80
|
return 'allow rules kept on this side — this engine does not say how it reads allow rules; sending them to an engine that reads them as an exclusive list would block every tool not on it, so they are not sent and matching approvals are still answered here';
|
|
76
81
|
}
|
|
77
|
-
return 'allow rules kept on this side — no usable capabilities reading is cached for this engine (none received, or the last one was
|
|
82
|
+
return 'allow rules kept on this side — no usable capabilities reading is cached for this engine (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which), so allow rules are not sent and matching approvals are still answered here';
|
|
78
83
|
}
|
|
79
84
|
export function forgetPermissionsAllowGrantReading(baseUrl) {
|
|
80
85
|
reader.forget(baseUrl);
|
package/dist/postureKnob.js
CHANGED
|
@@ -87,7 +87,7 @@ export function postureKnobDetail(knob, opts) {
|
|
|
87
87
|
? `${label} ${word} (source: ${source} — ${note})`
|
|
88
88
|
: `${label} ${word} (source: ${source})`;
|
|
89
89
|
}
|
|
90
|
-
return `${label} not reported by this engine (
|
|
90
|
+
return `${label} not reported by this engine (this end found no usable reading for it: the engine does not send one, or what it sent could not be parsed here; this end cannot tell which)`;
|
|
91
91
|
}
|
|
92
92
|
const _postureKnobShapePin = (w) => w;
|
|
93
93
|
void _postureKnobShapePin;
|
package/dist/protocolSegment.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { hasCoreValuePort, mintNamespacePrefix, protocolOf } from './coreValuePorts.js';
|
|
2
|
+
import { CORE_PROTOCOL_PREFIXES } from './generated/toolNameTables.js';
|
|
2
3
|
export function protocolSegmentOf(name) {
|
|
4
|
+
if (CORE_PROTOCOL_PREFIXES.some((prefix) => name.startsWith(prefix) && name.slice(prefix.length).split('__')[0] === ''))
|
|
5
|
+
return { kind: 'empty' };
|
|
3
6
|
const ns = protocolOf(name);
|
|
4
7
|
if (ns === undefined)
|
|
5
8
|
return hasCoreValuePort('protocolOf') ? { kind: 'ok' } : { kind: 'unknown' };
|
package/dist/readFacePosture.js
CHANGED
|
@@ -30,7 +30,7 @@ export function readFacePostureDetail(view, opts) {
|
|
|
30
30
|
if (!opts.reachable) {
|
|
31
31
|
return "read face not observed (this end could not read the engine's diagnostics)";
|
|
32
32
|
}
|
|
33
|
-
return 'read face not reported by this engine (
|
|
33
|
+
return 'read face not reported by this engine (this end found no usable reading for it: the engine does not send one, or what it sent could not be parsed here; this end cannot tell which)';
|
|
34
34
|
}
|
|
35
35
|
export function readFaceDisagreement(capFace, posture) {
|
|
36
36
|
if (posture === undefined)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readErrorNumber, readErrorString } from './wireFailureShape.js';
|
|
2
|
+
import { readOwnDataNonEmptyString } from './ownKey.js';
|
|
2
3
|
import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED, RESUME_POLICY_FOLD_CODE, PARKED_RESUME_STARTUP_FAILED, isStatusDerivedCoarseCode, RESUME_SESSION_NOT_FOUND, REQUEST_FIELD_CONFLICT, CHECKPOINT_UNSUPPORTED_VERSION, refusalSaysStillPending, } from './engineErrorCodes.js';
|
|
3
4
|
import { capForDisplay } from './fleetTaskDesc.js';
|
|
4
5
|
import { resumeRetryLaterFromError } from './wireErrorTriage.js';
|
|
@@ -115,7 +116,7 @@ export function requireExistingSessionRefusalFromError(err) {
|
|
|
115
116
|
}
|
|
116
117
|
}
|
|
117
118
|
const REQUIRE_EXISTING_SESSION_SENTENCES = Object.freeze({
|
|
118
|
-
session_not_found: 'The engine did not continue this conversation: the conversation it was asked to continue does not exist there (it may have expired, been removed, or never been created on that engine), and this request asked to continue only an existing one, so no new conversation was started · Start a new conversation, or pick another one to continue',
|
|
119
|
+
session_not_found: 'The engine did not continue this conversation: the conversation it was asked to continue does not exist there (it may have expired, been removed, or never been created on that engine; this client cannot tell which), and this request asked to continue only an existing one, so no new conversation was started · Start a new conversation, or pick another one to continue',
|
|
119
120
|
session_id_missing: 'The engine did not continue this conversation: this request asked to continue only an existing conversation but named none, so there was nothing to continue and no new conversation was started · Name the conversation to continue, or start a new one',
|
|
120
121
|
});
|
|
121
122
|
const REQUIRE_EXISTING_SESSION_GENERIC = 'The engine did not continue this conversation: this request asked to continue only an existing conversation and there was none it could continue, so no new conversation was started · Start a new conversation, or pick another one to continue';
|
|
@@ -158,6 +159,26 @@ export function resumeContextUnavailableContent(d) {
|
|
|
158
159
|
third,
|
|
159
160
|
].join(' · ');
|
|
160
161
|
}
|
|
162
|
+
function parkedResumeStartupCauseWord(err) {
|
|
163
|
+
let named;
|
|
164
|
+
try {
|
|
165
|
+
named = readOwnDataNonEmptyString(err, 'startupCause');
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
named = undefined;
|
|
169
|
+
}
|
|
170
|
+
if (named !== undefined)
|
|
171
|
+
return named;
|
|
172
|
+
try {
|
|
173
|
+
const extra = err.extra;
|
|
174
|
+
if (typeof extra !== 'object' || extra === null || Array.isArray(extra))
|
|
175
|
+
return undefined;
|
|
176
|
+
return readOwnDataNonEmptyString(extra, 'cause');
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return undefined;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
161
182
|
export function parkedResumeStartupFromError(err, decision) {
|
|
162
183
|
if (typeof err !== 'object' || err === null)
|
|
163
184
|
return null;
|
|
@@ -172,10 +193,12 @@ export function parkedResumeStartupFromError(err, decision) {
|
|
|
172
193
|
return null;
|
|
173
194
|
const rawTaskId = readErrorString(err, 'taskId');
|
|
174
195
|
const taskId = rawTaskId !== undefined && rawTaskId.length > 0 ? rawTaskId : undefined;
|
|
196
|
+
const startupCause = parkedResumeStartupCauseWord(err);
|
|
175
197
|
return {
|
|
176
198
|
code: PARKED_RESUME_STARTUP_FAILED,
|
|
177
199
|
cause: decision === 'deny' ? 'resume_rejected' : 'undetermined',
|
|
178
200
|
...(taskId !== undefined ? { taskId } : {}),
|
|
201
|
+
...(startupCause !== undefined ? { startupCause } : {}),
|
|
179
202
|
resendable: false,
|
|
180
203
|
denyStaysOpen: true,
|
|
181
204
|
};
|
|
@@ -183,7 +206,7 @@ export function parkedResumeStartupFromError(err, decision) {
|
|
|
183
206
|
const PARKED_RESUME_STARTUP_STATE = 'This refusal does not say whether the card is still waiting — re-fetch the pending approvals';
|
|
184
207
|
const PARKED_RESUME_STARTUP_SENTENCES = Object.freeze({
|
|
185
208
|
resume_rejected: [
|
|
186
|
-
'The engine could not start the parked work to apply this decision (its session may be gone, or the engine rejected the decision itself)',
|
|
209
|
+
'The engine could not start the parked work to apply this decision (its session may be gone, or the engine rejected the decision itself; this client cannot tell which)',
|
|
187
210
|
PARKED_RESUME_STARTUP_STATE,
|
|
188
211
|
'If the card is still there, decide it again from the fresh list',
|
|
189
212
|
].join(' · '),
|
|
@@ -249,7 +272,7 @@ export function checkpointUnsupportedVersionContent(detail, ctx) {
|
|
|
249
272
|
}
|
|
250
273
|
else {
|
|
251
274
|
outlet =
|
|
252
|
-
'If it was parked before this server was upgraded, submit the work again as a new task: when it still has a run waiting on it (a tool approval, a plan review or a paused resource), cancel that run first; when it was waiting to be woken up, or was a workflow step continuing its host conversation, there is no run to cancel, so start a new task in that conversation';
|
|
275
|
+
'If it was parked before this server was upgraded, submit the work again as a new task (this client cannot tell which kind of parked work this is, so both ways out are given): when it still has a run waiting on it (a tool approval, a plan review or a paused resource), cancel that run first; when it was waiting to be woken up, or was a workflow step continuing its host conversation, there is no run to cancel, so start a new task in that conversation';
|
|
253
276
|
}
|
|
254
277
|
return [CHECKPOINT_UNSUPPORTED_WHAT, CHECKPOINT_UNSUPPORTED_STILL_WAITING, outlet + CHECKPOINT_UNSUPPORTED_DRAIN].join(' · ');
|
|
255
278
|
}
|