@sema-agent/client-core 0.88.0 → 0.90.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 +208 -0
- package/README.md +27 -21
- package/dist/adapter/activeRunSelfHeal.js +4 -2
- package/dist/adapter/downstream/eventToSdkMessage.js +4 -2
- package/dist/adapter/downstream/wiringManifestView.d.ts +9 -0
- package/dist/adapter/downstream/wiringManifestView.js +27 -0
- package/dist/adapter/runStream.d.ts +2 -0
- package/dist/adapter/runStream.js +157 -32
- package/dist/attachmentsWireCaps.d.ts +13 -2
- package/dist/attachmentsWireCaps.js +25 -5
- package/dist/controlRouter.d.ts +7 -1
- package/dist/controlRouter.js +43 -1
- package/dist/coreValuePorts.d.ts +1 -0
- package/dist/disableSkillsWire.d.ts +17 -0
- package/dist/disableSkillsWire.js +52 -0
- package/dist/displayUntrusted.js +122 -55
- package/dist/engineCapReader.js +12 -1
- package/dist/engineNoticeCodes.d.ts +25 -9
- package/dist/engineNoticeCodes.js +161 -68
- package/dist/fileHistoryLegNotice.d.ts +20 -0
- package/dist/fileHistoryLegNotice.js +122 -0
- package/dist/gateOutcome.d.ts +1 -1
- package/dist/gateOutcome.js +1 -0
- package/dist/generated/engineFactTables.d.ts +1 -0
- package/dist/generated/engineFactTables.js +3 -0
- package/dist/generated/engineNoticeTables.d.ts +3 -0
- package/dist/generated/engineNoticeTables.js +107 -2
- package/dist/hitl/toolApprovalWire.d.ts +6 -2
- package/dist/hitl/toolApprovalWire.js +30 -0
- package/dist/hookToolAliases.d.ts +3 -0
- package/dist/hookToolAliases.js +10 -0
- package/dist/hooksWireCaps.js +80 -12
- package/dist/index.d.ts +14 -3
- package/dist/index.js +10 -3
- package/dist/permissionsAllowGrantCapability.d.ts +17 -0
- package/dist/permissionsAllowGrantCapability.js +75 -0
- package/dist/permissionsDeclaration.d.ts +25 -0
- package/dist/permissionsDeclaration.js +227 -0
- package/dist/pluginHooksWire.d.ts +46 -4
- package/dist/pluginHooksWire.js +277 -24
- package/dist/promptDelivery.d.ts +5 -2
- package/dist/promptDelivery.js +8 -3
- package/dist/providerRejection.d.ts +7 -0
- package/dist/providerRejection.js +58 -0
- package/dist/request/taskRequest.d.ts +2 -0
- package/dist/request/taskRequest.js +67 -9
- package/dist/runTerminal.d.ts +6 -0
- package/dist/runTerminal.js +23 -0
- package/dist/sdkRegistryTransit.d.ts +1 -0
- package/dist/sdkRegistryTransit.js +1 -0
- package/dist/seam.d.ts +3 -0
- package/dist/seatContract.d.ts +9 -1
- package/dist/seatContract.js +10 -0
- package/dist/subagent/engineSubagentSteer.d.ts +8 -1
- package/dist/subagent/engineSubagentSteer.js +26 -3
- package/dist/supportFloor.d.ts +10 -0
- package/dist/supportFloor.js +42 -0
- package/dist/taskRequestWords.d.ts +1 -0
- package/dist/taskRequestWords.js +14 -0
- package/docs/INTEGRATION-CLIENTS.md +896 -14
- package/package.json +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { abortableSleep } from '../abortableSleep.js';
|
|
2
2
|
import { corruptStoredRowContent, corruptStoredRowWhere, effectiveWireErrorCode } from '../wireErrorTriage.js';
|
|
3
|
+
import { steeringRefusedBeforeEngine, steeringStatusIs4xx } from '../controlRouter.js';
|
|
3
4
|
export const PLAN_REVIEW_GATE_KIND = 'plan_review';
|
|
4
5
|
export const PLAN_REVIEW_GATE_KINDS = Object.freeze([PLAN_REVIEW_GATE_KIND, 'dry_run_review']);
|
|
5
6
|
export const ASK_PARK_GATE_KINDS = Object.freeze(['human', 'irreversible_ask', 'policy_ask', 'tool_approval']);
|
|
@@ -260,8 +261,9 @@ function reopenedOfOutcome(outcome) {
|
|
|
260
261
|
}
|
|
261
262
|
}
|
|
262
263
|
export function atMostOnceFailureClass(e) {
|
|
263
|
-
|
|
264
|
-
|
|
264
|
+
if (typeof e === 'object' && e !== null && steeringStatusIs4xx(e))
|
|
265
|
+
return 'rejected';
|
|
266
|
+
return typeof e === 'object' && e !== null && steeringRefusedBeforeEngine(e) !== undefined ? 'rejected' : 'unknown';
|
|
265
267
|
}
|
|
266
268
|
export function readSteerDelivery(receipt) {
|
|
267
269
|
try {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { wiringManifestViewOf } from './wiringManifestView.js';
|
|
1
|
+
import { readInstructionsSegment, wiringManifestViewOf } from './wiringManifestView.js';
|
|
2
2
|
import { stamp, snapshotSegmentIdentity, } from '../types.js';
|
|
3
3
|
import { turnUsageToModelUsage } from './turnUsageToModelUsage.js';
|
|
4
4
|
import { ccToolDenialKindForToolEnd, gateOutcomeOf } from '../../gateOutcome.js';
|
|
@@ -550,7 +550,8 @@ function wiringManifestSupersetBody(ev) {
|
|
|
550
550
|
const readDeny = projectReadDenySection(ev.readDeny);
|
|
551
551
|
const hands = projectHandsSection(ev);
|
|
552
552
|
const fileHistory = projectFileHistorySection(ev);
|
|
553
|
-
|
|
553
|
+
const instructions = readInstructionsSegment(ev, 'instructions');
|
|
554
|
+
if (modelGate === undefined && autoMode === undefined && mcp === undefined && tools === undefined && hooks === undefined && lsp === undefined && writeProtection === undefined && autoConsolidation === undefined && readDeny === undefined && hands === undefined && fileHistory === undefined && instructions === undefined)
|
|
554
555
|
return undefined;
|
|
555
556
|
return {
|
|
556
557
|
...(modelGate !== undefined ? { _sema_modelGate: modelGate } : {}),
|
|
@@ -564,6 +565,7 @@ function wiringManifestSupersetBody(ev) {
|
|
|
564
565
|
...(readDeny !== undefined ? { _sema_readDeny: readDeny } : {}),
|
|
565
566
|
...(hands !== undefined ? { _sema_hands: hands } : {}),
|
|
566
567
|
...(fileHistory !== undefined ? { _sema_fileHistory: fileHistory } : {}),
|
|
568
|
+
...(instructions !== undefined ? { _sema_instructions: instructions } : {}),
|
|
567
569
|
};
|
|
568
570
|
}
|
|
569
571
|
function projectFileHistorySection(holder) {
|
|
@@ -12,6 +12,9 @@ export type WiringManifestSections = {
|
|
|
12
12
|
_sema_readDeny?: WiringManifestReadDenyView;
|
|
13
13
|
_sema_hands?: WiringManifestHandsView;
|
|
14
14
|
_sema_fileHistory?: string;
|
|
15
|
+
_sema_instructions?: {
|
|
16
|
+
readonly selected: readonly string[];
|
|
17
|
+
};
|
|
15
18
|
eventId?: string;
|
|
16
19
|
};
|
|
17
20
|
export interface WiringManifestView {
|
|
@@ -26,6 +29,12 @@ export interface WiringManifestView {
|
|
|
26
29
|
readDeny?: WiringManifestReadDenyView;
|
|
27
30
|
hands?: WiringManifestHandsView;
|
|
28
31
|
fileHistory?: string;
|
|
32
|
+
instructions?: {
|
|
33
|
+
readonly selected: readonly string[];
|
|
34
|
+
};
|
|
29
35
|
eventId?: string;
|
|
30
36
|
}
|
|
37
|
+
export declare function readInstructionsSegment(holder: object, key: 'instructions' | '_sema_instructions'): {
|
|
38
|
+
readonly selected: readonly string[];
|
|
39
|
+
} | undefined;
|
|
31
40
|
export declare function wiringManifestViewOf(s: WiringManifestSections): WiringManifestView | undefined;
|
|
@@ -2,8 +2,34 @@ const _viewPin = true;
|
|
|
2
2
|
void _viewPin;
|
|
3
3
|
const _viewKeysPin = true;
|
|
4
4
|
void _viewKeysPin;
|
|
5
|
+
export function readInstructionsSegment(holder, key) {
|
|
6
|
+
try {
|
|
7
|
+
if (!Object.hasOwn(holder, key))
|
|
8
|
+
return undefined;
|
|
9
|
+
const raw = holder[key];
|
|
10
|
+
if (typeof raw !== 'object' || raw === null || Array.isArray(raw) || !Object.hasOwn(raw, 'selected'))
|
|
11
|
+
return undefined;
|
|
12
|
+
const sel = raw.selected;
|
|
13
|
+
if (!Array.isArray(sel))
|
|
14
|
+
return undefined;
|
|
15
|
+
const out = [];
|
|
16
|
+
for (let i = 0; i < sel.length; i++) {
|
|
17
|
+
if (!Object.hasOwn(sel, i))
|
|
18
|
+
return undefined;
|
|
19
|
+
const v = sel[i];
|
|
20
|
+
if (typeof v !== 'string' || v.length === 0)
|
|
21
|
+
return undefined;
|
|
22
|
+
out.push(v);
|
|
23
|
+
}
|
|
24
|
+
return Object.freeze({ selected: Object.freeze(out) });
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
5
30
|
const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
6
31
|
export function wiringManifestViewOf(s) {
|
|
32
|
+
const instructions = readInstructionsSegment(s, '_sema_instructions');
|
|
7
33
|
const view = {
|
|
8
34
|
...(isObj(s._sema_modelGate) ? { modelGate: s._sema_modelGate } : {}),
|
|
9
35
|
...(isObj(s._sema_autoMode) ? { autoMode: s._sema_autoMode } : {}),
|
|
@@ -16,6 +42,7 @@ export function wiringManifestViewOf(s) {
|
|
|
16
42
|
...(isObj(s._sema_readDeny) ? { readDeny: s._sema_readDeny } : {}),
|
|
17
43
|
...(isObj(s._sema_hands) && typeof s._sema_hands.mounted === 'boolean' ? { hands: s._sema_hands } : {}),
|
|
18
44
|
...(typeof s._sema_fileHistory === 'string' && s._sema_fileHistory.length > 0 ? { fileHistory: s._sema_fileHistory } : {}),
|
|
45
|
+
...(instructions !== undefined ? { instructions } : {}),
|
|
19
46
|
};
|
|
20
47
|
if (Object.keys(view).length === 0)
|
|
21
48
|
return undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { AgentEvent } from '@sema-agent/sdk';
|
|
2
2
|
import { type SDKMessage, type EmitContext, type ModelUsage } from './types.js';
|
|
3
3
|
import { type EngineTurnUsage } from './downstream/turnUsageToModelUsage.js';
|
|
4
|
+
import { type ToolHistoryErrorCarrier } from '../toolHistoryMismatch.js';
|
|
4
5
|
export interface ActiveRunPendingGate {
|
|
5
6
|
readonly kind: string | null;
|
|
6
7
|
readonly decidePath: string | null;
|
|
@@ -27,6 +28,7 @@ export declare function isGovernanceTerminal(input: {
|
|
|
27
28
|
status: string | undefined;
|
|
28
29
|
errorCode: string | undefined;
|
|
29
30
|
}): boolean;
|
|
31
|
+
export declare function toolHistoryErrorCarrierOf(terminalOrResult: object | null | undefined): ToolHistoryErrorCarrier;
|
|
30
32
|
export declare function droppedWhySentence(why: string): string;
|
|
31
33
|
export declare function _resetDroppedFrameReportForTest(): void;
|
|
32
34
|
export declare function _droppedFrameMemoSizeForTest(): number;
|
|
@@ -140,11 +140,6 @@ function engineSessionIdOf(result) {
|
|
|
140
140
|
const v = result?.sessionId;
|
|
141
141
|
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
142
142
|
}
|
|
143
|
-
function providerFailureStatusOf(result) {
|
|
144
|
-
const af = result?.apiFailure;
|
|
145
|
-
const status = typeof af === 'object' && af !== null ? af.status : undefined;
|
|
146
|
-
return typeof status === 'number' && Number.isFinite(status) ? status : undefined;
|
|
147
|
-
}
|
|
148
143
|
export function isOutcomeUnknownRowText(text) {
|
|
149
144
|
return text.trimStart().startsWith(`${OUTCOME_UNKNOWN_ROW_PREFIX}:`);
|
|
150
145
|
}
|
|
@@ -155,6 +150,157 @@ export function isGovernanceTerminal(input) {
|
|
|
155
150
|
return true;
|
|
156
151
|
return input.errorCode === OUTPUT_INVALID;
|
|
157
152
|
}
|
|
153
|
+
function terminalRowFacts(ev) {
|
|
154
|
+
const doneTerminal = ev.type === 'done' ? readRunTerminal(ev.result) : null;
|
|
155
|
+
const errTextRaw = ev.type === 'failed'
|
|
156
|
+
? (ev.errorMessage ?? ev.errorCode ?? 'run failed')
|
|
157
|
+
: doneTerminal?.kind === 'failed'
|
|
158
|
+
? (doneTerminal.message ?? doneTerminal.code ?? 'run failed')
|
|
159
|
+
: doneTerminal?.kind === 'blocked'
|
|
160
|
+
? (doneTerminal.reason
|
|
161
|
+
? `Run blocked: ${doneTerminal.reason}`
|
|
162
|
+
: 'Run blocked (the agent reported it cannot proceed)')
|
|
163
|
+
: null;
|
|
164
|
+
const errText = errTextRaw === '' ? null : errTextRaw;
|
|
165
|
+
if (errText === null)
|
|
166
|
+
return { doneTerminal, errText, activeTaskId: undefined, busy: { busy: false, handle: null }, governance: false };
|
|
167
|
+
const activeTaskId = (ev.type === 'done' ? ev.result?.activeTaskId : undefined) ??
|
|
168
|
+
(ev.type === 'failed' ? ev.activeTaskId : undefined);
|
|
169
|
+
const busy = classifyActiveRunBusy({
|
|
170
|
+
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
171
|
+
activeTaskId,
|
|
172
|
+
});
|
|
173
|
+
const governance = isGovernanceTerminal({
|
|
174
|
+
status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
|
|
175
|
+
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
176
|
+
});
|
|
177
|
+
return { doneTerminal, errText, activeTaskId, busy, governance };
|
|
178
|
+
}
|
|
179
|
+
const PROVIDER_REJECTED_DETAIL = 'provider_rejected';
|
|
180
|
+
function ownDataBit(o, k) {
|
|
181
|
+
try {
|
|
182
|
+
const d = Object.getOwnPropertyDescriptor(o, k);
|
|
183
|
+
return d !== undefined && Object.prototype.hasOwnProperty.call(d, 'value') ? d.value : undefined;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function plainObjectOf(v) {
|
|
190
|
+
if (typeof v !== 'object' || v === null)
|
|
191
|
+
return undefined;
|
|
192
|
+
try {
|
|
193
|
+
return Array.isArray(v) ? undefined : v;
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
return undefined;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function ownDataCopy(o, keys) {
|
|
200
|
+
const out = Object.create(null);
|
|
201
|
+
for (const k of keys) {
|
|
202
|
+
const v = ownDataBit(o, k);
|
|
203
|
+
if (v !== undefined)
|
|
204
|
+
out[k] = v;
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
}
|
|
208
|
+
function ownTerminalSnapshot(result) {
|
|
209
|
+
let causePlane;
|
|
210
|
+
try {
|
|
211
|
+
causePlane = 'terminal' in result;
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const snap = ownDataCopy(result, ['status', 'errorCode', 'errorMessage']);
|
|
217
|
+
if (!causePlane)
|
|
218
|
+
return snap;
|
|
219
|
+
let d;
|
|
220
|
+
try {
|
|
221
|
+
d = Object.getOwnPropertyDescriptor(result, 'terminal');
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return undefined;
|
|
225
|
+
}
|
|
226
|
+
if (d === undefined || !Object.prototype.hasOwnProperty.call(d, 'value'))
|
|
227
|
+
return undefined;
|
|
228
|
+
const t = plainObjectOf(d.value);
|
|
229
|
+
if (t === undefined) {
|
|
230
|
+
snap.terminal = null;
|
|
231
|
+
return snap;
|
|
232
|
+
}
|
|
233
|
+
const ts = ownDataCopy(t, ['kind', 'code', 'message']);
|
|
234
|
+
const detail = plainObjectOf(ownDataBit(t, 'detail'));
|
|
235
|
+
if (detail !== undefined)
|
|
236
|
+
ts.detail = ownDataCopy(detail, ['kind', 'status', 'code']);
|
|
237
|
+
snap.terminal = ts;
|
|
238
|
+
return snap;
|
|
239
|
+
}
|
|
240
|
+
function nonEmptyStr(v) {
|
|
241
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
242
|
+
}
|
|
243
|
+
function mintToolHistoryCarrier(code, status, message) {
|
|
244
|
+
const out = Object.create(null);
|
|
245
|
+
if (code !== undefined)
|
|
246
|
+
out.code = code;
|
|
247
|
+
if (status !== undefined)
|
|
248
|
+
out.status = status;
|
|
249
|
+
if (message !== undefined)
|
|
250
|
+
out.message = message;
|
|
251
|
+
return Object.freeze(out);
|
|
252
|
+
}
|
|
253
|
+
export function toolHistoryErrorCarrierOf(terminalOrResult) {
|
|
254
|
+
const empty = () => mintToolHistoryCarrier(undefined, undefined, undefined);
|
|
255
|
+
try {
|
|
256
|
+
const x = plainObjectOf(terminalOrResult);
|
|
257
|
+
if (x === undefined)
|
|
258
|
+
return empty();
|
|
259
|
+
const type = ownDataBit(x, 'type');
|
|
260
|
+
const frame = (type === 'done' || type === 'failed' ? x : { type: 'done', result: x });
|
|
261
|
+
return toolHistoryCarrierFromFacts(x, terminalRowFacts(frame));
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
return empty();
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function toolHistoryCarrierFromFacts(x, facts) {
|
|
268
|
+
const empty = () => mintToolHistoryCarrier(undefined, undefined, undefined);
|
|
269
|
+
try {
|
|
270
|
+
const type = ownDataBit(x, 'type');
|
|
271
|
+
if (facts.errText === null || facts.busy.busy || facts.governance || isModelOutputErrorText(facts.errText))
|
|
272
|
+
return empty();
|
|
273
|
+
if (type === 'failed')
|
|
274
|
+
return mintToolHistoryCarrier(nonEmptyStr(ownDataBit(x, 'errorCode')), undefined, nonEmptyStr(ownDataBit(x, 'errorMessage')));
|
|
275
|
+
const result = type === 'done' ? plainObjectOf(ownDataBit(x, 'result')) : x;
|
|
276
|
+
if (result === undefined)
|
|
277
|
+
return empty();
|
|
278
|
+
const snap = ownTerminalSnapshot(result);
|
|
279
|
+
if (snap === undefined)
|
|
280
|
+
return empty();
|
|
281
|
+
const read = readRunTerminal(snap);
|
|
282
|
+
if (read === null || read.kind !== 'failed')
|
|
283
|
+
return empty();
|
|
284
|
+
const detail = plainObjectOf(ownDataBit(read, 'detail'));
|
|
285
|
+
const detailStatus = detail !== undefined && ownDataBit(detail, 'kind') === PROVIDER_REJECTED_DETAIL ? ownDataBit(detail, 'status') : undefined;
|
|
286
|
+
const apiFailure = plainObjectOf(ownDataBit(result, 'apiFailure'));
|
|
287
|
+
const apiStatus = apiFailure === undefined ? undefined : ownDataBit(apiFailure, 'status');
|
|
288
|
+
const status = typeof apiStatus === 'number' && Number.isFinite(apiStatus) ? apiStatus : typeof detailStatus === 'number' ? detailStatus : undefined;
|
|
289
|
+
return mintToolHistoryCarrier(nonEmptyStr(ownDataBit(read, 'code')), status, nonEmptyStr(ownDataBit(read, 'message')));
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return empty();
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function toolHistoryCarrierOfRowFrame(ev, facts) {
|
|
296
|
+
try {
|
|
297
|
+
const x = plainObjectOf(ev);
|
|
298
|
+
return x === undefined ? mintToolHistoryCarrier(undefined, undefined, undefined) : toolHistoryCarrierFromFacts(x, facts);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return mintToolHistoryCarrier(undefined, undefined, undefined);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
158
304
|
const reportedDroppedTypes = new Set();
|
|
159
305
|
const DROPPED_TYPE_MEMO_CAP = 64;
|
|
160
306
|
const DROPPED_TYPE_DISPLAY_CAP = 60;
|
|
@@ -477,30 +623,15 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
477
623
|
continue;
|
|
478
624
|
}
|
|
479
625
|
if (ev.type === 'done' || ev.type === 'failed') {
|
|
480
|
-
const
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
: doneTerminal?.kind === 'failed'
|
|
484
|
-
? (doneTerminal.message ?? doneTerminal.code ?? 'run failed')
|
|
485
|
-
: doneTerminal?.kind === 'blocked'
|
|
486
|
-
? (doneTerminal.reason
|
|
487
|
-
? `Run blocked: ${doneTerminal.reason}`
|
|
488
|
-
: 'Run blocked (the agent reported it cannot proceed)')
|
|
489
|
-
: null;
|
|
626
|
+
const rowFacts = terminalRowFacts(ev);
|
|
627
|
+
const doneTerminal = rowFacts.doneTerminal;
|
|
628
|
+
const errText = rowFacts.errText;
|
|
490
629
|
if (errText) {
|
|
491
|
-
const
|
|
492
|
-
(ev.type === 'failed' ? ev.activeTaskId : undefined);
|
|
493
|
-
const busy = classifyActiveRunBusy({
|
|
494
|
-
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
495
|
-
activeTaskId,
|
|
496
|
-
});
|
|
630
|
+
const busy = rowFacts.busy;
|
|
497
631
|
const maxTokHint = /max_tokens/i.test(errText)
|
|
498
632
|
? '\nyour provider caps max_tokens lower — lower it in /model (press m, or M to type a value)'
|
|
499
633
|
: '';
|
|
500
|
-
const governance =
|
|
501
|
-
status: ev.type === 'failed' ? undefined : doneTerminal?.kind === 'blocked' ? 'blocked' : undefined,
|
|
502
|
-
errorCode: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
503
|
-
});
|
|
634
|
+
const governance = rowFacts.governance;
|
|
504
635
|
const busyHandle = busy.handle;
|
|
505
636
|
const rowText = busy.busy
|
|
506
637
|
? `This session is locked by an earlier run${busyHandle ? ` (run ${busyHandle})` : ''} that ` +
|
|
@@ -516,13 +647,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
|
|
|
516
647
|
const sessionUnreadable = ev.type === 'failed'
|
|
517
648
|
? isCorruptSessionCode(ev.errorCode)
|
|
518
649
|
: doneTerminal?.kind === 'failed' && isCorruptSessionCode(doneTerminal.code);
|
|
519
|
-
const historyMismatch =
|
|
520
|
-
? classifyToolHistoryMismatch({
|
|
521
|
-
code: ev.type === 'failed' ? ev.errorCode : runTerminalCode(doneTerminal),
|
|
522
|
-
status: ev.type === 'done' ? providerFailureStatusOf(ev.result) : undefined,
|
|
523
|
-
message: errText,
|
|
524
|
-
})
|
|
525
|
-
: null;
|
|
650
|
+
const historyMismatch = classifyToolHistoryMismatch(toolHistoryCarrierOfRowFrame(ev, rowFacts));
|
|
526
651
|
const historyEchoedSid = ev.type === 'done' ? engineSessionIdOf(ev.result) : undefined;
|
|
527
652
|
const historyRepeat = noteToolHistoryTerminal({
|
|
528
653
|
sessionId: historyEchoedSid ?? ctx.sessionId,
|
|
@@ -6,7 +6,6 @@ export interface AttachmentsSpec {
|
|
|
6
6
|
changedFiles?: true | {
|
|
7
7
|
maxFiles?: number;
|
|
8
8
|
};
|
|
9
|
-
planModeReminder?: true;
|
|
10
9
|
budgetUsd?: true;
|
|
11
10
|
backgroundTasks?: boolean;
|
|
12
11
|
toolsDelta?: true;
|
|
@@ -14,6 +13,18 @@ export interface AttachmentsSpec {
|
|
|
14
13
|
agentListing?: boolean;
|
|
15
14
|
skillsListing?: boolean;
|
|
16
15
|
}
|
|
17
|
-
export declare const ATTACHMENTS_SPEC_KEYS: readonly ["todoReminder", "todoReminderMode", "changedFiles", "
|
|
16
|
+
export declare const ATTACHMENTS_SPEC_KEYS: readonly ["todoReminder", "todoReminderMode", "changedFiles", "budgetUsd", "backgroundTasks", "toolsDelta", "mcpInstructions", "agentListing", "skillsListing"];
|
|
18
17
|
export declare const ATTACHMENTS_DEFAULT_ON_KEYS: readonly ["agentListing", "skillsListing", "backgroundTasks"];
|
|
18
|
+
export declare class AttachmentsSpecRefused extends TypeError {
|
|
19
|
+
readonly token: string;
|
|
20
|
+
readonly why: string;
|
|
21
|
+
readonly allowed: readonly string[];
|
|
22
|
+
readonly key?: string;
|
|
23
|
+
constructor(message: string, init: {
|
|
24
|
+
readonly token: string;
|
|
25
|
+
readonly why: string;
|
|
26
|
+
readonly allowed: readonly string[];
|
|
27
|
+
readonly key?: string;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
19
30
|
export declare function attachmentsForRequest(env?: EnvLike): AttachmentsSpec | undefined;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { hostEnv } from './hostEnv.js';
|
|
2
2
|
import { envFlagOff } from './envFlag.js';
|
|
3
|
+
import { retiredAttachmentsKeyWhy } from './taskRequestWords.js';
|
|
3
4
|
export const ATTACHMENTS_ENV = 'SEMA_ATTACHMENTS';
|
|
4
5
|
export const ATTACHMENTS_SPEC_KEYS = Object.freeze([
|
|
5
6
|
'todoReminder',
|
|
6
7
|
'todoReminderMode',
|
|
7
8
|
'changedFiles',
|
|
8
|
-
'planModeReminder',
|
|
9
9
|
'budgetUsd',
|
|
10
10
|
'backgroundTasks',
|
|
11
11
|
'toolsDelta',
|
|
@@ -17,7 +17,6 @@ export const ATTACHMENTS_DEFAULT_ON_KEYS = ['agentListing', 'skillsListing', 'ba
|
|
|
17
17
|
const OPT_IN_KEYS = [
|
|
18
18
|
'todoReminder',
|
|
19
19
|
'changedFiles',
|
|
20
|
-
'planModeReminder',
|
|
21
20
|
'budgetUsd',
|
|
22
21
|
'backgroundTasks',
|
|
23
22
|
'toolsDelta',
|
|
@@ -28,16 +27,37 @@ const DEFAULT_PAIR = ['backgroundTasks', 'toolsDelta'];
|
|
|
28
27
|
const _attachmentsKeyPins = [true, true, true, true, true, true];
|
|
29
28
|
void _attachmentsKeyPins;
|
|
30
29
|
const TODO_REMINDER_MODES = ['baseline', 'off'];
|
|
30
|
+
export class AttachmentsSpecRefused extends TypeError {
|
|
31
|
+
token;
|
|
32
|
+
why;
|
|
33
|
+
allowed;
|
|
34
|
+
constructor(message, init) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'AttachmentsSpecRefused';
|
|
37
|
+
this.token = init.token;
|
|
38
|
+
this.why = init.why;
|
|
39
|
+
this.allowed = Object.freeze([...init.allowed]);
|
|
40
|
+
if (init.key !== undefined)
|
|
41
|
+
this.key = init.key;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function legalAttachmentsTokens() {
|
|
45
|
+
return ['off', 'full', 'all', 'default', '<literalTrueKey>', '+<literalTrueKey>', ...DEFAULT_ON_ONLY_KEYS.map(k => `-${k}`), `todoReminderMode=${TODO_REMINDER_MODES.join('|')}`];
|
|
46
|
+
}
|
|
31
47
|
function refuse(token, why) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
`-agentListing | -skillsListing | todoReminderMode=baseline|off`);
|
|
48
|
+
const allowed = legalAttachmentsTokens();
|
|
49
|
+
throw new AttachmentsSpecRefused(`${ATTACHMENTS_ENV}: refusing token "${token}" — ${why}. Legal tokens: ${allowed.join(' | ')}`, { token, why, allowed });
|
|
35
50
|
}
|
|
36
51
|
export function attachmentsForRequest(env = hostEnv()) {
|
|
37
52
|
const raw = (env[ATTACHMENTS_ENV] ?? '').trim().toLowerCase();
|
|
38
53
|
if (envFlagOff(raw))
|
|
39
54
|
return allDefaultOnOff();
|
|
40
55
|
const tokens = raw.split(',').map(t => t.trim()).filter(t => t.length > 0);
|
|
56
|
+
for (const token of tokens) {
|
|
57
|
+
const named = retiredAttachmentsKeyWhy(token);
|
|
58
|
+
if (named !== undefined)
|
|
59
|
+
refuse(token, named);
|
|
60
|
+
}
|
|
41
61
|
if (tokens.length === 0)
|
|
42
62
|
return { backgroundTasks: true, toolsDelta: true };
|
|
43
63
|
if (tokens.length === 1 && (tokens[0] === 'full' || tokens[0] === 'all'))
|
package/dist/controlRouter.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface RunsResourceLike {
|
|
|
17
17
|
export interface ControlClientLike {
|
|
18
18
|
runs: RunsResourceLike;
|
|
19
19
|
}
|
|
20
|
-
export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full' | 'duplicate_input_id' | 'ambiguous_target' | 'still_running' | 'blocked_by_hook' | 'steering_other' | 'cancel_lost_race' | 'cancel_run_not_running' | 'not_found';
|
|
20
|
+
export type ControlSafetyCode = 'not_running' | 'invalid_content' | 'queue_full' | 'duplicate_input_id' | 'ambiguous_target' | 'still_running' | 'blocked_by_hook' | 'ledger_unavailable' | 'steering_other' | 'cancel_lost_race' | 'cancel_run_not_running' | 'not_found';
|
|
21
21
|
export declare class ControlSafetyError extends Error {
|
|
22
22
|
readonly code: ControlSafetyCode;
|
|
23
23
|
readonly cause?: unknown | undefined;
|
|
@@ -71,3 +71,9 @@ export interface ErrCodes {
|
|
|
71
71
|
status?: number | undefined;
|
|
72
72
|
}
|
|
73
73
|
export declare function errCodes(e: unknown): ErrCodes;
|
|
74
|
+
export declare function steeringStatusIs4xx(e: object): boolean;
|
|
75
|
+
export interface SteeringRefusedBeforeEngine {
|
|
76
|
+
safety: ControlSafetyCode;
|
|
77
|
+
advice: string;
|
|
78
|
+
}
|
|
79
|
+
export declare function steeringRefusedBeforeEngine(e: object): SteeringRefusedBeforeEngine | undefined;
|
package/dist/controlRouter.js
CHANGED
|
@@ -64,8 +64,9 @@ export class ControlRouter {
|
|
|
64
64
|
catch (e) {
|
|
65
65
|
const verdict = steeringVerdict(e);
|
|
66
66
|
if (verdict !== undefined) {
|
|
67
|
+
const safety = steeringSafetyOf(e, verdict);
|
|
67
68
|
const wireCode = errCodes(e).code ?? errCodes(e).name ?? 'steering.<unknown>';
|
|
68
|
-
throw new ControlSafetyError(`${wireCode} — ${
|
|
69
|
+
throw new ControlSafetyError(`${wireCode} — ${steeringAdviceOf(e, safety) ?? 'steering refused'}`, safety, e);
|
|
69
70
|
}
|
|
70
71
|
throw e;
|
|
71
72
|
}
|
|
@@ -138,12 +139,14 @@ const STEERING_CODE_TO_SAFETY = new Map([
|
|
|
138
139
|
['steering.ambiguous_label', 'ambiguous_target'],
|
|
139
140
|
['steering.still_running', 'still_running'],
|
|
140
141
|
['steering.blocked_by_hook', 'blocked_by_hook'],
|
|
142
|
+
['steering.ledger_unavailable', 'ledger_unavailable'],
|
|
141
143
|
]);
|
|
142
144
|
const STEERING_NAME_TO_SAFETY = new Map([
|
|
143
145
|
['SteeringNotRunningError', 'not_running'],
|
|
144
146
|
['SteeringInvalidContentError', 'invalid_content'],
|
|
145
147
|
['SteeringAmbiguousTargetError', 'ambiguous_target'],
|
|
146
148
|
['SteeringStillRunningError', 'still_running'],
|
|
149
|
+
['SteeringLedgerUnavailableError', 'ledger_unavailable'],
|
|
147
150
|
['SteeringError', 'steering_other'],
|
|
148
151
|
]);
|
|
149
152
|
const STEERING_ADVICE = new Map([
|
|
@@ -154,8 +157,47 @@ const STEERING_ADVICE = new Map([
|
|
|
154
157
|
['ambiguous_target', 'more than one live target matches — address it uniquely (sub-agent: parentToolCallId; workflow: a more specific label); retrying verbatim yields the same result'],
|
|
155
158
|
['still_running', 'the target is STILL RUNNING — steer it instead of resuming, or wait for it to settle (this is the exact opposite of not_running: never collapse the two)'],
|
|
156
159
|
['blocked_by_hook', "this deployment's userPromptSubmit gate refused the input (block, timeout and crash all report this one code, fail-closed) — the input was NOT accepted: no human_input frame, the inputId is not on the ledger, so editing the text and sending again is a normal retry, not a duplicate"],
|
|
160
|
+
['ledger_unavailable', 'the server could not record this steer in its user-input ledger and refused it before anything reached the engine — the input was NOT delivered; resend the same text later (the same Idempotency-Key is fine)'],
|
|
157
161
|
['steering_other', 'an unrecognized steering.* refusal (open set) — the steer did NOT land; log the errorCode verbatim, do not guess its meaning'],
|
|
158
162
|
]);
|
|
163
|
+
const STEERING_OTHER_UNKNOWN_ADVICE = 'an unrecognized steering.* refusal (open set) — treat delivery as unknown (do not resend as if it had certainly been dropped); log the errorCode verbatim, do not guess its meaning';
|
|
164
|
+
export function steeringStatusIs4xx(e) {
|
|
165
|
+
let status;
|
|
166
|
+
try {
|
|
167
|
+
status = errCodes(e).status;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
return status !== undefined && status >= 400 && status < 500;
|
|
173
|
+
}
|
|
174
|
+
function steeringAdviceOf(e, safety) {
|
|
175
|
+
if (safety === 'steering_other' && !(typeof e === 'object' && e !== null && steeringStatusIs4xx(e)))
|
|
176
|
+
return STEERING_OTHER_UNKNOWN_ADVICE;
|
|
177
|
+
return STEERING_ADVICE.get(safety);
|
|
178
|
+
}
|
|
179
|
+
const STEERING_REFUSED_BEFORE_ENGINE = new Map([['ledger_unavailable', 503]]);
|
|
180
|
+
export function steeringRefusedBeforeEngine(e) {
|
|
181
|
+
try {
|
|
182
|
+
const { status } = errCodes(e);
|
|
183
|
+
if (status === undefined)
|
|
184
|
+
return undefined;
|
|
185
|
+
const safety = steeringVerdict(e);
|
|
186
|
+
if (safety === undefined || STEERING_REFUSED_BEFORE_ENGINE.get(safety) !== status)
|
|
187
|
+
return undefined;
|
|
188
|
+
const advice = STEERING_ADVICE.get(safety);
|
|
189
|
+
return advice === undefined ? undefined : { safety, advice };
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function steeringSafetyOf(e, verdict) {
|
|
196
|
+
if (!STEERING_REFUSED_BEFORE_ENGINE.has(verdict))
|
|
197
|
+
return verdict;
|
|
198
|
+
const refused = typeof e === 'object' && e !== null ? steeringRefusedBeforeEngine(e) : undefined;
|
|
199
|
+
return refused !== undefined && refused.safety === verdict ? verdict : 'steering_other';
|
|
200
|
+
}
|
|
159
201
|
function steeringVerdict(e) {
|
|
160
202
|
const { code, name } = errCodes(e);
|
|
161
203
|
if (typeof code === 'string' && code.startsWith('steering.')) {
|
package/dist/coreValuePorts.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export interface AllowDenyPolicyOptionsView {
|
|
|
16
16
|
deny?: string[];
|
|
17
17
|
onInvalidName?: 'throw' | 'skip';
|
|
18
18
|
onInvalidNameIssue?: (issue: AllowDenyInvalidNameIssueView) => void;
|
|
19
|
+
scope?: 'grant' | 'narrowing';
|
|
19
20
|
}
|
|
20
21
|
export interface AllowDenyInvalidNameIssueView {
|
|
21
22
|
entry: string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function refuseBadDisableSkills(v: unknown, who: string): void;
|
|
2
|
+
export declare function refuseDisableSkillsConflict(disableSkills: unknown, skills: unknown, agents: unknown, who: string): void;
|
|
3
|
+
type DisableSkillsWithheldCause = 'not_reported' | 'unsupported' | 'unprobed' | 'unreadable';
|
|
4
|
+
export type DisableSkillsDeclaration = {
|
|
5
|
+
readonly field: {
|
|
6
|
+
readonly disableSkills: true;
|
|
7
|
+
};
|
|
8
|
+
readonly withheld?: undefined;
|
|
9
|
+
} | {
|
|
10
|
+
readonly field: Readonly<Record<string, never>>;
|
|
11
|
+
readonly withheld?: {
|
|
12
|
+
readonly cause: DisableSkillsWithheldCause;
|
|
13
|
+
readonly text: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
export declare function disableSkillsDeclaration(want: unknown, baseUrl: string | undefined): DisableSkillsDeclaration;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { engineCapState } from './engineCapsCache.js';
|
|
2
|
+
const KEY = 'disableSkills';
|
|
3
|
+
const shapeOf = (v) => {
|
|
4
|
+
try {
|
|
5
|
+
return v === null ? 'null' : Array.isArray(v) ? 'typeof=object / array' : typeof v === 'string' ? `typeof=string(长 ${v.length})` : `typeof=${typeof v}`;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return `typeof=${typeof v} / unreadable`;
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
export function refuseBadDisableSkills(v, who) {
|
|
12
|
+
if (v === undefined || v === null || v === true || v === false)
|
|
13
|
+
return;
|
|
14
|
+
throw new TypeError(`${who}: disableSkills 只接受布尔或缺席(undefined/null)—— 收到 ${shapeOf(v)}。` +
|
|
15
|
+
'这一位是「这一发一只技能都不装」的声明:本层若把坏值折成缺席,这一次运行会照常装上技能而声明人不会知道;' +
|
|
16
|
+
'上游对非布尔值同样拒收。错误文本只报形状,不回显值。');
|
|
17
|
+
}
|
|
18
|
+
const nonEmptyArray = (v) => Array.isArray(v) && v.length > 0;
|
|
19
|
+
const anyAgentCarriesSkills = (agents) => Array.isArray(agents) && agents.some((a) => typeof a === 'object' && a !== null && nonEmptyArray(a.skills));
|
|
20
|
+
export function refuseDisableSkillsConflict(disableSkills, skills, agents, who) {
|
|
21
|
+
if (disableSkills !== true)
|
|
22
|
+
return;
|
|
23
|
+
const inSkills = nonEmptyArray(skills);
|
|
24
|
+
const inAgents = anyAgentCarriesSkills(agents);
|
|
25
|
+
if (!inSkills && !inAgents)
|
|
26
|
+
return;
|
|
27
|
+
const which = inSkills && inAgents ? 'skills 与 agents[].skills' : inSkills ? 'skills' : 'agents[].skills';
|
|
28
|
+
throw new TypeError(`${who}: disableSkills: true(这一发一只技能都不装)与非空的 ${which} 同在 —— 二选一:要关技能就别交技能,要交技能就别带 disableSkills。` +
|
|
29
|
+
'上游对这一组合同样拒收(在任何东西跑起来之前);本层在装配前拒,不让它走一次往返。错误文本只报键名,不回显技能名。');
|
|
30
|
+
}
|
|
31
|
+
const WITHHELD_TEXT = Object.freeze({
|
|
32
|
+
not_reported: 'Skills were not turned off for this request: the connected engine does not report that it can turn them off, and an engine that does not know this setting rejects the whole request, so the setting was left out and this run keeps its usual skills.',
|
|
33
|
+
unsupported: 'Skills were not turned off for this request: the connected engine reports that it cannot turn them off, so the setting was left out and this run keeps its usual skills.',
|
|
34
|
+
unprobed: 'Skills were not turned off for this request: what the connected engine supports has not been read yet, and an engine that does not know this setting rejects the whole request, so the setting was held back and this run keeps its usual skills.',
|
|
35
|
+
unreadable: 'Skills were not turned off for this request: the connected engine reported whether it can turn them off in a form this version cannot read, so the setting was left out and this run keeps its usual skills.',
|
|
36
|
+
});
|
|
37
|
+
const WITHHELD_CAUSE_OF = {
|
|
38
|
+
absent: 'not_reported',
|
|
39
|
+
false: 'unsupported',
|
|
40
|
+
unprobed: 'unprobed',
|
|
41
|
+
non_boolean: 'unreadable',
|
|
42
|
+
};
|
|
43
|
+
export function disableSkillsDeclaration(want, baseUrl) {
|
|
44
|
+
refuseBadDisableSkills(want, 'disableSkillsDeclaration');
|
|
45
|
+
if (want !== true)
|
|
46
|
+
return Object.freeze({ field: Object.freeze({}) });
|
|
47
|
+
const state = engineCapState(baseUrl, KEY);
|
|
48
|
+
if (state === 'true')
|
|
49
|
+
return Object.freeze({ field: Object.freeze({ disableSkills: true }) });
|
|
50
|
+
const cause = WITHHELD_CAUSE_OF[state];
|
|
51
|
+
return Object.freeze({ field: Object.freeze({}), withheld: Object.freeze({ cause, text: WITHHELD_TEXT[cause] }) });
|
|
52
|
+
}
|