@sema-agent/client-core 0.85.1 → 0.86.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 +242 -0
- package/README.md +21 -14
- package/dist/adapt/arms.js +5 -1
- package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
- package/dist/adapter/activeRunSelfHeal.js +126 -36
- package/dist/adapter/downstream/terminalToSdkResult.d.ts +3 -3
- package/dist/agentSession/backgroundView.js +7 -1
- package/dist/agentSession/contract.d.ts +1 -1
- package/dist/centerWiringCapability.d.ts +38 -0
- package/dist/centerWiringCapability.js +202 -0
- package/dist/decideFailureNote.js +3 -0
- package/dist/engineCapReader.d.ts +8 -0
- package/dist/engineCapReader.js +15 -0
- package/dist/engineErrorCodes.d.ts +3 -0
- package/dist/engineErrorCodes.js +8 -0
- package/dist/engineNoticeCodes.d.ts +62 -0
- package/dist/engineNoticeCodes.js +247 -29
- package/dist/executionLaneCapability.d.ts +6 -0
- package/dist/executionLaneCapability.js +55 -2
- package/dist/fleet/fleetProjection.js +6 -8
- package/dist/fleet/workflowSizeWarning.js +2 -5
- package/dist/gateOutcome.d.ts +13 -0
- package/dist/gateOutcome.js +48 -1
- package/dist/gateVocabulary.d.ts +6 -3
- package/dist/gateVocabulary.js +7 -15
- package/dist/generated/engineFactTables.d.ts +10 -0
- package/dist/generated/engineFactTables.js +113 -0
- package/dist/generated/engineNoticeTables.js +14 -0
- package/dist/hitl/refusedToolNameProse.d.ts +4 -0
- package/dist/hitl/refusedToolNameProse.js +7 -0
- package/dist/hitl/sessionPolicyDeliverable.js +9 -5
- package/dist/hitl/sessionPolicyWire.d.ts +16 -0
- package/dist/hitl/sessionPolicyWire.js +49 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +5 -1
- package/dist/inheritEnvWire.d.ts +22 -0
- package/dist/inheritEnvWire.js +84 -0
- package/dist/peerFrames.js +2 -10
- package/dist/printInitToolFace.d.ts +6 -0
- package/dist/printInitToolFace.js +115 -2
- package/dist/readCredentialRefusal.d.ts +2 -0
- package/dist/readCredentialRefusal.js +7 -0
- package/dist/request/taskRequest.d.ts +4 -5
- package/dist/request/taskRequest.js +33 -23
- package/dist/runTerminal.js +2 -6
- package/dist/sqlEngineCapability.js +15 -2
- package/dist/storePostureCapability.d.ts +25 -0
- package/dist/storePostureCapability.js +132 -0
- package/dist/toolResult.js +2 -47
- package/dist/wireErrorTriage.d.ts +1 -0
- package/dist/wireErrorTriage.js +3 -0
- package/dist/wireFailureShape.d.ts +3 -0
- package/dist/wireFailureShape.js +44 -0
- package/dist/wireRefusalCopy.d.ts +39 -1
- package/dist/wireRefusalCopy.js +350 -2
- package/dist/workflowClient.d.ts +4 -0
- package/dist/workflowClient.js +12 -4
- package/docs/INTEGRATION-CLIENTS.md +853 -14
- package/package.json +2 -2
package/dist/adapt/arms.js
CHANGED
|
@@ -685,6 +685,9 @@ function prefixOf(whole, part) {
|
|
|
685
685
|
return trimmed;
|
|
686
686
|
return null;
|
|
687
687
|
}
|
|
688
|
+
const ERROR_SUBTYPE_PREFIX = 'error_';
|
|
689
|
+
const _errorSubtypePrefixPin = true;
|
|
690
|
+
void _errorSubtypePrefixPin;
|
|
688
691
|
const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
|
|
689
692
|
yield chrome({ kind: 'retry_status', laneProof: mainLane(), status: null });
|
|
690
693
|
yield* panel.settle(null, false);
|
|
@@ -698,7 +701,8 @@ const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
|
|
|
698
701
|
});
|
|
699
702
|
yield* text.takeAnswerSegment();
|
|
700
703
|
const salvaged = m._sema_salvaged_result;
|
|
701
|
-
const
|
|
704
|
+
const errorEnvelope = typeof m.subtype === 'string' && m.subtype.startsWith(ERROR_SUBTYPE_PREFIX);
|
|
705
|
+
const terminalText = typeof salvaged === 'string' ? salvaged : !errorEnvelope && typeof m.result === 'string' ? m.result : '';
|
|
702
706
|
const committed = text.lastCommittedAnswerText;
|
|
703
707
|
const terminalIdentity = messageIdentityOf(m, ctx);
|
|
704
708
|
const emitTerminal = function* (body, tag) {
|
|
@@ -36,6 +36,7 @@ export type ReopenCardVerdict = {
|
|
|
36
36
|
_sema_decisionInFlight?: true;
|
|
37
37
|
dismissedByUser?: true;
|
|
38
38
|
_sema_noPresentationSurface?: true;
|
|
39
|
+
_sema_errorCode?: string;
|
|
39
40
|
} | {
|
|
40
41
|
reopened: true;
|
|
41
42
|
firstSight: boolean;
|
|
@@ -88,6 +89,7 @@ export type SelfHealOutcome = {
|
|
|
88
89
|
pendingRowGone?: true;
|
|
89
90
|
dismissedByUser?: true;
|
|
90
91
|
noPresentationSurface?: true;
|
|
92
|
+
errorCode?: string;
|
|
91
93
|
} | {
|
|
92
94
|
kind: 'not-parked';
|
|
93
95
|
taskId: string;
|
|
@@ -111,6 +113,7 @@ export type SelfHealOutcome = {
|
|
|
111
113
|
decisionInFlight?: true;
|
|
112
114
|
pendingRowGone?: true;
|
|
113
115
|
noPresentationSurface?: true;
|
|
116
|
+
errorCode?: string;
|
|
114
117
|
} | {
|
|
115
118
|
kind: 'ask-decided-without-card';
|
|
116
119
|
taskId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { abortableSleep } from '../abortableSleep.js';
|
|
2
|
-
import { corruptStoredRowContent, corruptStoredRowWhere } from '../wireErrorTriage.js';
|
|
2
|
+
import { corruptStoredRowContent, corruptStoredRowWhere, effectiveWireErrorCode } from '../wireErrorTriage.js';
|
|
3
3
|
export const PLAN_REVIEW_GATE_KIND = 'plan_review';
|
|
4
4
|
export const PLAN_REVIEW_GATE_KINDS = Object.freeze([PLAN_REVIEW_GATE_KIND, 'dry_run_review']);
|
|
5
5
|
export const ASK_PARK_GATE_KINDS = Object.freeze(['human', 'irreversible_ask', 'policy_ask', 'tool_approval']);
|
|
@@ -45,20 +45,42 @@ function sessionOpts(deps) {
|
|
|
45
45
|
const sessionId = deps?.sessionId;
|
|
46
46
|
return typeof sessionId === 'string' && sessionId.length > 0 ? { session: sessionId } : {};
|
|
47
47
|
}
|
|
48
|
+
function errorSlot(e, key) {
|
|
49
|
+
if (e === null || e === undefined)
|
|
50
|
+
return undefined;
|
|
51
|
+
try {
|
|
52
|
+
return e[key];
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
48
58
|
function describeFailure(e) {
|
|
49
59
|
const where = corruptStoredRowWhere(e);
|
|
50
60
|
const rowNote = where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return `${m.length > 200 ? `${m.slice(0, 200)}…` : m}${rowNote}`;
|
|
61
|
+
let m;
|
|
62
|
+
try {
|
|
63
|
+
m = typeof e === 'object' && e !== null && 'message' in e ? e.message : undefined;
|
|
55
64
|
}
|
|
65
|
+
catch {
|
|
66
|
+
m = undefined;
|
|
67
|
+
}
|
|
68
|
+
if (typeof m === 'string' && m.length > 0)
|
|
69
|
+
return `${m.length > 200 ? `${m.slice(0, 200)}…` : m}${rowNote}`;
|
|
56
70
|
return `the engine gave no detail${rowNote}`;
|
|
57
71
|
}
|
|
58
72
|
function readStatus(record) {
|
|
59
73
|
const st = record?.status;
|
|
60
74
|
return typeof st === 'string' && st.length > 0 ? st : null;
|
|
61
75
|
}
|
|
76
|
+
function readStatusGuarded(record) {
|
|
77
|
+
try {
|
|
78
|
+
return readStatus(record);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
62
84
|
function readClaimHolder(record) {
|
|
63
85
|
if (record === null || typeof record !== 'object')
|
|
64
86
|
return 'unknown';
|
|
@@ -88,6 +110,7 @@ const REOPEN_VERDICT_KEYS = [
|
|
|
88
110
|
'_sema_decisionInFlight',
|
|
89
111
|
'dismissedByUser',
|
|
90
112
|
'_sema_noPresentationSurface',
|
|
113
|
+
'_sema_errorCode',
|
|
91
114
|
];
|
|
92
115
|
function snapshotReopenVerdict(raw) {
|
|
93
116
|
if (raw === null || raw === undefined)
|
|
@@ -179,6 +202,29 @@ function reopenRefusedForNoPresentationSurface(verdict) {
|
|
|
179
202
|
return false;
|
|
180
203
|
}
|
|
181
204
|
}
|
|
205
|
+
function reopenErrorCodeOf(verdict) {
|
|
206
|
+
try {
|
|
207
|
+
if (verdict.reopened !== false)
|
|
208
|
+
return undefined;
|
|
209
|
+
const v = verdict._sema_errorCode;
|
|
210
|
+
return typeof v === 'string' && v.length > 0 ? v : undefined;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function failureWireCode(e) {
|
|
217
|
+
try {
|
|
218
|
+
return effectiveWireErrorCode(e);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
function thrownReopenVerdict(e) {
|
|
225
|
+
const code = failureWireCode(e);
|
|
226
|
+
return code !== undefined ? { reopened: false, _sema_errorCode: code } : { reopened: false };
|
|
227
|
+
}
|
|
182
228
|
function planReviewReopenTriggerFor(origin) {
|
|
183
229
|
if (origin === 'user')
|
|
184
230
|
return 'user';
|
|
@@ -214,20 +260,30 @@ function reopenedOfOutcome(outcome) {
|
|
|
214
260
|
}
|
|
215
261
|
}
|
|
216
262
|
export function atMostOnceFailureClass(e) {
|
|
217
|
-
const status = e
|
|
263
|
+
const status = errorSlot(e, 'status');
|
|
218
264
|
return typeof status === 'number' && status >= 400 && status < 500 ? 'rejected' : 'unknown';
|
|
219
265
|
}
|
|
220
266
|
export function readSteerDelivery(receipt) {
|
|
221
|
-
|
|
267
|
+
try {
|
|
268
|
+
if (typeof receipt !== 'object' || receipt === null || !('delivery' in receipt))
|
|
269
|
+
return null;
|
|
270
|
+
const d = receipt.delivery;
|
|
271
|
+
return typeof d === 'string' && d.length > 0 ? d : null;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
222
274
|
return null;
|
|
223
|
-
|
|
224
|
-
return typeof d === 'string' && d.length > 0 ? d : null;
|
|
275
|
+
}
|
|
225
276
|
}
|
|
226
277
|
export function readSteerReceiptStatus(receipt) {
|
|
227
|
-
|
|
278
|
+
try {
|
|
279
|
+
if (typeof receipt !== 'object' || receipt === null || !('status' in receipt))
|
|
280
|
+
return null;
|
|
281
|
+
const s = receipt.status;
|
|
282
|
+
return typeof s === 'string' && s.length > 0 ? s : null;
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
228
285
|
return null;
|
|
229
|
-
|
|
230
|
-
return typeof s === 'string' && s.length > 0 ? s : null;
|
|
286
|
+
}
|
|
231
287
|
}
|
|
232
288
|
const CANCEL_RELEASE_WAIT_MS = 10_000;
|
|
233
289
|
const RUNNING_LIVENESS_RECHECK_TIMEOUT_MS = 4_000;
|
|
@@ -345,11 +401,14 @@ async function readOwnedPendingCount(read, deps) {
|
|
|
345
401
|
claimPollSleep(OWNED_PENDING_RECHECK_TIMEOUT_MS, recheckCtl.signal).then(() => null),
|
|
346
402
|
]);
|
|
347
403
|
if (deps?.signal?.aborted === true)
|
|
348
|
-
return null;
|
|
349
|
-
return typeof count === 'number' ? count : null;
|
|
404
|
+
return { count: null };
|
|
405
|
+
return { count: typeof count === 'number' ? count : null };
|
|
350
406
|
}
|
|
351
|
-
catch {
|
|
352
|
-
|
|
407
|
+
catch (e) {
|
|
408
|
+
if (deps?.signal?.aborted === true)
|
|
409
|
+
return { count: null };
|
|
410
|
+
const errorCode = failureWireCode(e);
|
|
411
|
+
return errorCode !== undefined ? { count: null, errorCode } : { count: null };
|
|
353
412
|
}
|
|
354
413
|
finally {
|
|
355
414
|
deps?.signal?.removeEventListener('abort', onCallerAbort);
|
|
@@ -366,7 +425,7 @@ export async function attemptActiveRunSelfHeal(signal, runs, deps) {
|
|
|
366
425
|
}
|
|
367
426
|
const callerAborted = () => deps?.signal?.aborted === true;
|
|
368
427
|
if (pending && typeof deps?.listOwnedPendingApprovals === 'function' && !callerAborted()) {
|
|
369
|
-
const count = await readOwnedPendingCount(deps.listOwnedPendingApprovals, deps);
|
|
428
|
+
const { count } = await readOwnedPendingCount(deps.listOwnedPendingApprovals, deps);
|
|
370
429
|
if (count === 0 && !callerAborted())
|
|
371
430
|
pending = false;
|
|
372
431
|
}
|
|
@@ -397,7 +456,7 @@ export async function attemptActiveRunSelfHeal(signal, runs, deps) {
|
|
|
397
456
|
catch (e) {
|
|
398
457
|
return { kind: 'state-unknown', taskId, detail: describeFailure(e) };
|
|
399
458
|
}
|
|
400
|
-
status =
|
|
459
|
+
status = readStatusGuarded(record);
|
|
401
460
|
}
|
|
402
461
|
if (status === null) {
|
|
403
462
|
return { kind: 'state-unknown', taskId, detail: 'the engine reported no status for that run' };
|
|
@@ -419,30 +478,39 @@ async function planVerdict(taskId, deps) {
|
|
|
419
478
|
const verdict = trigger === undefined ? deps?.reopenPlanReview?.(taskId) : deps?.reopenPlanReview?.(taskId, { trigger });
|
|
420
479
|
return snapshotReopenVerdict(await verdict);
|
|
421
480
|
}
|
|
422
|
-
catch {
|
|
423
|
-
|
|
481
|
+
catch (e) {
|
|
482
|
+
if (deps?.signal?.aborted === true)
|
|
483
|
+
return { reopened: false };
|
|
484
|
+
return thrownReopenVerdict(e);
|
|
424
485
|
}
|
|
425
486
|
}
|
|
426
487
|
async function askVerdict(taskId, deps) {
|
|
427
488
|
try {
|
|
428
489
|
return snapshotReopenVerdict(await deps?.reopenAskPark?.(taskId));
|
|
429
490
|
}
|
|
430
|
-
catch {
|
|
431
|
-
|
|
491
|
+
catch (e) {
|
|
492
|
+
if (deps?.signal?.aborted === true)
|
|
493
|
+
return { reopened: false };
|
|
494
|
+
return thrownReopenVerdict(e);
|
|
432
495
|
}
|
|
433
496
|
}
|
|
434
497
|
async function planReviewArm(taskId, signal, deps) {
|
|
435
498
|
const verdict = await planVerdict(taskId, deps);
|
|
436
499
|
if (reopenDelivered(verdict))
|
|
437
500
|
return { kind: 'plan-review-reopened', taskId, firstSight: verdict.firstSight === true };
|
|
501
|
+
const inFlight = reopenRefusedForDecisionInFlight(verdict);
|
|
502
|
+
const dismissed = reopenRefusedForUserDismissal(verdict);
|
|
503
|
+
const noView = reopenRefusedForNoPresentationSurface(verdict);
|
|
504
|
+
const errorCode = inFlight || dismissed || noView ? undefined : reopenErrorCodeOf(verdict);
|
|
438
505
|
return {
|
|
439
506
|
kind: 'plan-review-reopen-failed',
|
|
440
507
|
taskId,
|
|
441
508
|
decidePath: signal.pendingGate?.decidePath ?? null,
|
|
442
|
-
...(
|
|
509
|
+
...(inFlight ? { decisionInFlight: true } : {}),
|
|
443
510
|
...(verdict.pendingRowGone === true ? { pendingRowGone: true } : {}),
|
|
444
|
-
...(
|
|
445
|
-
...(
|
|
511
|
+
...(dismissed ? { dismissedByUser: true } : {}),
|
|
512
|
+
...(noView ? { noPresentationSurface: true } : {}),
|
|
513
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
446
514
|
};
|
|
447
515
|
}
|
|
448
516
|
async function askParkArm(taskId, signal, runs, deps) {
|
|
@@ -477,30 +545,41 @@ async function askParkArm(taskId, signal, runs, deps) {
|
|
|
477
545
|
if (verdict.reopened === false && verdict.decidedWithoutCard !== true && verdict.pendingRowGone === true && !inFlight) {
|
|
478
546
|
return staleParkArm(taskId, signal, runs, deps);
|
|
479
547
|
}
|
|
548
|
+
const noView = reopenRefusedForNoPresentationSurface(verdict);
|
|
549
|
+
const errorCode = inFlight || noView || reopenRefusedForUserDismissal(verdict) ? undefined : reopenErrorCodeOf(verdict);
|
|
480
550
|
return {
|
|
481
551
|
kind: 'ask-reopen-failed',
|
|
482
552
|
taskId,
|
|
483
553
|
decidePath: signal.pendingGate?.decidePath ?? null,
|
|
484
554
|
...(inFlight ? { decisionInFlight: true } : {}),
|
|
485
555
|
...(verdict.pendingRowGone === true ? { pendingRowGone: true } : {}),
|
|
486
|
-
...(
|
|
556
|
+
...(noView ? { noPresentationSurface: true } : {}),
|
|
557
|
+
...(errorCode !== undefined ? { errorCode } : {}),
|
|
487
558
|
};
|
|
488
559
|
}
|
|
489
560
|
async function probeRunStatusOnce(taskId, durable, durableGet, deps) {
|
|
490
561
|
let status = null;
|
|
491
562
|
let ghost = false;
|
|
563
|
+
let errorCode;
|
|
564
|
+
let record = null;
|
|
565
|
+
let answered = false;
|
|
492
566
|
const lease = claimProbeLease(deps?.signal, RUNNING_LIVENESS_RECHECK_TIMEOUT_MS);
|
|
493
567
|
try {
|
|
494
|
-
|
|
568
|
+
record = await durableGet.call(durable, taskId, { signal: lease.signal, ...sessionOpts(deps) });
|
|
569
|
+
answered = true;
|
|
495
570
|
}
|
|
496
571
|
catch (e) {
|
|
497
|
-
if (e
|
|
572
|
+
if (errorSlot(e, 'status') === 404)
|
|
498
573
|
ghost = true;
|
|
574
|
+
else
|
|
575
|
+
errorCode = failureWireCode(e);
|
|
499
576
|
}
|
|
500
577
|
finally {
|
|
501
578
|
lease.release();
|
|
502
579
|
}
|
|
503
|
-
|
|
580
|
+
if (answered)
|
|
581
|
+
status = readStatusGuarded(record);
|
|
582
|
+
return errorCode !== undefined ? { status, ghost, errorCode } : { status, ghost };
|
|
504
583
|
}
|
|
505
584
|
async function staleParkArm(taskId, busy, runs, deps) {
|
|
506
585
|
const reopenFailed = {
|
|
@@ -514,6 +593,7 @@ async function staleParkArm(taskId, busy, runs, deps) {
|
|
|
514
593
|
taskId,
|
|
515
594
|
decidePath: busy.pendingGate?.decidePath ?? null,
|
|
516
595
|
};
|
|
596
|
+
const withFailureCode = (base, code) => code !== undefined ? { ...base, errorCode: code } : base;
|
|
517
597
|
if (runs === undefined || typeof runs.get !== 'function')
|
|
518
598
|
return reopenFailed;
|
|
519
599
|
const durableGet = runs.get;
|
|
@@ -527,7 +607,7 @@ async function staleParkArm(taskId, busy, runs, deps) {
|
|
|
527
607
|
return reopenFailed;
|
|
528
608
|
const fresh = first.status;
|
|
529
609
|
if (fresh === null)
|
|
530
|
-
return reopenFailed;
|
|
610
|
+
return withFailureCode(reopenFailed, first.errorCode);
|
|
531
611
|
if (RUNNING_STATES.includes(fresh))
|
|
532
612
|
return runningChoiceArm(taskId, fresh, busy, false, runs, deps);
|
|
533
613
|
if (PLAN_REVIEW_STATES.includes(fresh))
|
|
@@ -566,12 +646,14 @@ async function staleParkArm(taskId, busy, runs, deps) {
|
|
|
566
646
|
return { kind: 'ask-run-not-found', taskId };
|
|
567
647
|
if (callerAborted())
|
|
568
648
|
return reopenFailedAfterCard;
|
|
569
|
-
if (afterCard.status === null
|
|
570
|
-
return
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
649
|
+
if (afterCard.status === null)
|
|
650
|
+
return withFailureCode(reopenFailedAfterCard, afterCard.errorCode);
|
|
651
|
+
if (!ASK_PARK_STATES.includes(afterCard.status)) {
|
|
652
|
+
return !CLAIM_HELD_STATES.includes(afterCard.status) ? reopenFailed : reopenFailedAfterCard;
|
|
653
|
+
}
|
|
654
|
+
const recheck = await readOwnedPendingCount(recheckOwnedPending, deps);
|
|
655
|
+
if (recheck.count !== 0)
|
|
656
|
+
return withFailureCode(reopenFailedAfterCard, recheck.errorCode);
|
|
575
657
|
const verdict = await cancelAndConfirmRelease(taskId, runs, durableGet, deps);
|
|
576
658
|
if (verdict.outcome === 'released') {
|
|
577
659
|
return { kind: 'stale-park-cancelled', taskId, ...(verdict.engineStatedRelease ? { releaseConfirmedByEngine: true } : {}) };
|
|
@@ -816,6 +898,14 @@ function injectedSubmissionRow(outcome, following = false) {
|
|
|
816
898
|
return (`${lead} has not reached the model: the earlier reply${id} had already finished, so the message was set aside on it ` +
|
|
817
899
|
`instead of starting a new reply, and sema did not retry it.`);
|
|
818
900
|
}
|
|
901
|
+
if (outcome.kind === 'running-steered' && outcome.delivery !== 'applied') {
|
|
902
|
+
const unknown = `${lead} was accepted for the reply already in progress${id}, but how it will be delivered was not reported, ` +
|
|
903
|
+
`so sema cannot tell whether that reply will pick it up`;
|
|
904
|
+
return following
|
|
905
|
+
? `${unknown}. sema is following that reply and will show whatever it asks for next; check what it does ` +
|
|
906
|
+
`before deciding whether anything needs to be sent again.`
|
|
907
|
+
: `${unknown} — watch that reply before deciding whether anything needs to be sent again.`;
|
|
908
|
+
}
|
|
819
909
|
switch (selfHealSubmissionDisposition(outcome)) {
|
|
820
910
|
case 'held-for-decision':
|
|
821
911
|
return (`${lead} is on hold: this session is busy with an earlier reply${id} that is waiting on a decision. ` +
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AgentEvent,
|
|
2
|
-
import { type CcToolDenialKind } from '../../gateVocabulary.js';
|
|
1
|
+
import type { AgentEvent, TaskStats } from '@sema-agent/sdk';
|
|
2
|
+
import { type CcToolDenialKind, type GateDeniedByWord } from '../../gateVocabulary.js';
|
|
3
3
|
import { type SDKMessage, type EmitContext } from '../types.js';
|
|
4
4
|
import type { TerminalReason } from '@sema-agent/agent-types';
|
|
5
5
|
import { type SemaModelUsage } from './turnUsageToModelUsage.js';
|
|
@@ -32,7 +32,7 @@ export interface SemaPermissionDenial {
|
|
|
32
32
|
readonly _sema_tool_input_source?: 'tool_start';
|
|
33
33
|
readonly _sema_tool_input_redacted?: true;
|
|
34
34
|
readonly _sema_tool_arg?: string;
|
|
35
|
-
readonly deniedBy?:
|
|
35
|
+
readonly deniedBy?: GateDeniedByWord;
|
|
36
36
|
readonly _sema_denied_by_source?: 'tool_end';
|
|
37
37
|
readonly toolDenialKind?: CcToolDenialKind;
|
|
38
38
|
}
|
|
@@ -2,6 +2,7 @@ import { abortableSleep } from '../abortableSleep.js';
|
|
|
2
2
|
import { errCodes } from '../controlRouter.js';
|
|
3
3
|
import { engineWireDebugEnabled } from '../engineWireTarget.js';
|
|
4
4
|
import { hostLog } from '../host.js';
|
|
5
|
+
import { isReadCredentialRefusal } from '../readCredentialRefusal.js';
|
|
5
6
|
export function backgroundStatusFromAssistant(s) {
|
|
6
7
|
switch (s) {
|
|
7
8
|
case 'running':
|
|
@@ -169,7 +170,12 @@ export function createBackgroundView(client, opts) {
|
|
|
169
170
|
}
|
|
170
171
|
catch (err) {
|
|
171
172
|
debugLog(`fleet.snapshot degrade: ${String(err)}`);
|
|
172
|
-
|
|
173
|
+
const health = isNotImplemented(err)
|
|
174
|
+
? 'not-configured'
|
|
175
|
+
: isReadCredentialRefusal(err)
|
|
176
|
+
? 'unauthorized'
|
|
177
|
+
: 'unavailable';
|
|
178
|
+
return { health, tasks: [] };
|
|
173
179
|
}
|
|
174
180
|
}
|
|
175
181
|
async function pollOnce() {
|
|
@@ -47,7 +47,7 @@ export type AttachSeatSession = (seatApi: unknown) => AgentSession & {
|
|
|
47
47
|
readonly supportedFields: readonly string[];
|
|
48
48
|
};
|
|
49
49
|
export type BackgroundSource = 'assistant' | 'fleet';
|
|
50
|
-
export type BackgroundSourceHealth = 'ok' | 'unavailable' | 'not-configured';
|
|
50
|
+
export type BackgroundSourceHealth = 'ok' | 'unavailable' | 'not-configured' | 'unauthorized';
|
|
51
51
|
export type BackgroundStatus = 'running' | 'waiting-on-human' | 'queued' | 'stopping' | 'done' | 'failed' | 'stopped' | 'unknown';
|
|
52
52
|
export interface BackgroundRow {
|
|
53
53
|
readonly key: {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface CenterWiringRevisionView {
|
|
2
|
+
version: number;
|
|
3
|
+
updatedAt: string;
|
|
4
|
+
}
|
|
5
|
+
export interface CenterWiringCallerView {
|
|
6
|
+
lease: string;
|
|
7
|
+
retryAfterSec?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface CenterWiringView {
|
|
10
|
+
effectivePull: boolean;
|
|
11
|
+
effectiveRevision: CenterWiringRevisionView | null;
|
|
12
|
+
lease: string;
|
|
13
|
+
leaseHealth?: string;
|
|
14
|
+
failMode?: string;
|
|
15
|
+
usageReport: boolean;
|
|
16
|
+
issuer: string;
|
|
17
|
+
caller?: CenterWiringCallerView;
|
|
18
|
+
}
|
|
19
|
+
export type CenterWiringReading = {
|
|
20
|
+
kind: 'unobserved';
|
|
21
|
+
} | {
|
|
22
|
+
kind: 'not_reported';
|
|
23
|
+
} | {
|
|
24
|
+
kind: 'none';
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'present';
|
|
27
|
+
view: CenterWiringView;
|
|
28
|
+
};
|
|
29
|
+
export declare function projectCenterWiringCapability(caps: unknown): CenterWiringReading | undefined;
|
|
30
|
+
export declare function noteEngineCapsForCenterWiring(baseUrl: string, caps: unknown, opts?: {
|
|
31
|
+
generation?: number;
|
|
32
|
+
}): void;
|
|
33
|
+
export declare function observedCenterWiring(baseUrl?: string | undefined): CenterWiringReading;
|
|
34
|
+
export type CallerQuotaLease = 'enforced' | 'not-enforced' | 'exhausted' | 'unobserved' | 'no-lease' | 'unknown';
|
|
35
|
+
export declare function callerQuotaLease(reading: CenterWiringReading): CallerQuotaLease;
|
|
36
|
+
export declare function centerWiringDoctorDetail(reading: CenterWiringReading): string;
|
|
37
|
+
export declare function forgetCenterWiringReading(baseUrl: string | undefined): void;
|
|
38
|
+
export declare function __resetCenterWiringReadingsForTests(): void;
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { capForDisplay } from './fleetTaskDesc.js';
|
|
2
|
+
import { engineWireTarget } from './engineWireTarget.js';
|
|
3
|
+
import { capsGenerationOf, createEngineCapReader } from './engineCapReader.js';
|
|
4
|
+
const nonEmpty = (v) => typeof v === 'string' && v !== '';
|
|
5
|
+
const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
6
|
+
function optionalSeat(o, key) {
|
|
7
|
+
if (!Object.hasOwn(o, key))
|
|
8
|
+
return { state: 'absent' };
|
|
9
|
+
const value = o[key];
|
|
10
|
+
return value === undefined ? { state: 'absent' } : { state: 'value', value };
|
|
11
|
+
}
|
|
12
|
+
export function projectCenterWiringCapability(caps) {
|
|
13
|
+
try {
|
|
14
|
+
return projectCenterWiringOf(caps);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function projectCenterWiringOf(caps) {
|
|
21
|
+
if (!isPlainObject(caps))
|
|
22
|
+
return undefined;
|
|
23
|
+
const cw = Object.hasOwn(caps, 'centerWiring') ? caps.centerWiring : undefined;
|
|
24
|
+
if (cw === undefined)
|
|
25
|
+
return capsGenerationOf(caps).generation === 'new' ? { kind: 'none' } : { kind: 'not_reported' };
|
|
26
|
+
if (!isPlainObject(cw))
|
|
27
|
+
return undefined;
|
|
28
|
+
const effectivePull = cw.effectivePull;
|
|
29
|
+
const revisionRaw = cw.effectiveRevision;
|
|
30
|
+
const lease = cw.lease;
|
|
31
|
+
const usageReport = cw.usageReport;
|
|
32
|
+
const issuer = cw.issuer;
|
|
33
|
+
if (typeof effectivePull !== 'boolean' || typeof usageReport !== 'boolean')
|
|
34
|
+
return undefined;
|
|
35
|
+
if (!nonEmpty(lease) || !nonEmpty(issuer))
|
|
36
|
+
return undefined;
|
|
37
|
+
let effectiveRevision;
|
|
38
|
+
if (revisionRaw === null) {
|
|
39
|
+
effectiveRevision = null;
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
if (!isPlainObject(revisionRaw))
|
|
43
|
+
return undefined;
|
|
44
|
+
const version = revisionRaw.version;
|
|
45
|
+
const updatedAt = revisionRaw.updatedAt;
|
|
46
|
+
if (typeof version !== 'number' || !Number.isFinite(version) || !nonEmpty(updatedAt))
|
|
47
|
+
return undefined;
|
|
48
|
+
effectiveRevision = { version, updatedAt };
|
|
49
|
+
}
|
|
50
|
+
const health = optionalSeat(cw, 'leaseHealth');
|
|
51
|
+
if (health.state === 'value' && !nonEmpty(health.value))
|
|
52
|
+
return undefined;
|
|
53
|
+
const mode = optionalSeat(cw, 'failMode');
|
|
54
|
+
if (mode.state === 'value' && !nonEmpty(mode.value))
|
|
55
|
+
return undefined;
|
|
56
|
+
const callerSeat = optionalSeat(cw, 'caller');
|
|
57
|
+
let caller;
|
|
58
|
+
if (callerSeat.state === 'value') {
|
|
59
|
+
if (!isPlainObject(callerSeat.value))
|
|
60
|
+
return undefined;
|
|
61
|
+
const callerLease = callerSeat.value.lease;
|
|
62
|
+
if (!nonEmpty(callerLease))
|
|
63
|
+
return undefined;
|
|
64
|
+
const wait = optionalSeat(callerSeat.value, 'retryAfterSec');
|
|
65
|
+
if (wait.state === 'value' && (typeof wait.value !== 'number' || !Number.isFinite(wait.value) || wait.value < 0))
|
|
66
|
+
return undefined;
|
|
67
|
+
caller = { lease: callerLease, ...(wait.state === 'value' ? { retryAfterSec: wait.value } : {}) };
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
kind: 'present',
|
|
71
|
+
view: {
|
|
72
|
+
effectivePull,
|
|
73
|
+
effectiveRevision,
|
|
74
|
+
lease,
|
|
75
|
+
...(health.state === 'value' ? { leaseHealth: health.value } : {}),
|
|
76
|
+
...(mode.state === 'value' ? { failMode: mode.value } : {}),
|
|
77
|
+
usageReport,
|
|
78
|
+
issuer,
|
|
79
|
+
...(caller !== undefined ? { caller } : {}),
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
const reader = createEngineCapReader({
|
|
84
|
+
name: 'centerWiring',
|
|
85
|
+
project: projectCenterWiringCapability,
|
|
86
|
+
makeUnobserved: () => ({ kind: 'unobserved' }),
|
|
87
|
+
});
|
|
88
|
+
export function noteEngineCapsForCenterWiring(baseUrl, caps, opts) {
|
|
89
|
+
reader.note(baseUrl, caps, opts);
|
|
90
|
+
}
|
|
91
|
+
export function observedCenterWiring(baseUrl = engineWireTarget()?.baseUrl) {
|
|
92
|
+
return reader.observed(baseUrl);
|
|
93
|
+
}
|
|
94
|
+
function guarded(reading) {
|
|
95
|
+
if (typeof reading !== 'object' || reading === null)
|
|
96
|
+
return undefined;
|
|
97
|
+
try {
|
|
98
|
+
const kind = reading.kind;
|
|
99
|
+
if (kind === 'not_reported' || kind === 'unobserved' || kind === 'none')
|
|
100
|
+
return { kind };
|
|
101
|
+
if (kind !== 'present')
|
|
102
|
+
return undefined;
|
|
103
|
+
const view = reading.view;
|
|
104
|
+
return isPlainObject(view) ? { kind: 'present', view } : undefined;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function callerQuotaLease(reading) {
|
|
111
|
+
const r = guarded(reading);
|
|
112
|
+
if (r === undefined)
|
|
113
|
+
return 'unknown';
|
|
114
|
+
if (r.kind === 'none')
|
|
115
|
+
return 'no-lease';
|
|
116
|
+
if (r.kind !== 'present')
|
|
117
|
+
return 'unknown';
|
|
118
|
+
try {
|
|
119
|
+
const lease = r.view.lease;
|
|
120
|
+
if (lease === 'absent')
|
|
121
|
+
return 'no-lease';
|
|
122
|
+
if (lease !== 'configured')
|
|
123
|
+
return 'unknown';
|
|
124
|
+
const caller = r.view.caller;
|
|
125
|
+
if (!isPlainObject(caller))
|
|
126
|
+
return 'unknown';
|
|
127
|
+
const callerLease = caller.lease;
|
|
128
|
+
return callerLease === 'enforced' || callerLease === 'not-enforced' || callerLease === 'exhausted' || callerLease === 'unobserved' ? callerLease : 'unknown';
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return 'unknown';
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const WORD_DETAIL_MAX = 40;
|
|
135
|
+
const word = (v) => capForDisplay(v, WORD_DETAIL_MAX);
|
|
136
|
+
function healthClause(v) {
|
|
137
|
+
if (v === undefined)
|
|
138
|
+
return 'lease health not reported';
|
|
139
|
+
if (v === 'ok')
|
|
140
|
+
return 'the quota service is answering';
|
|
141
|
+
if (v === 'auth-rejected')
|
|
142
|
+
return "the quota service rejected this engine's credentials";
|
|
143
|
+
if (v === 'unreachable')
|
|
144
|
+
return 'the quota service could not be reached';
|
|
145
|
+
if (v === 'unobserved')
|
|
146
|
+
return 'no lease negotiated yet';
|
|
147
|
+
return nonEmpty(v) ? `lease health reported as "${word(v)}"` : 'lease health not reported';
|
|
148
|
+
}
|
|
149
|
+
function failModeClause(v) {
|
|
150
|
+
if (v === undefined)
|
|
151
|
+
return 'failure posture not reported';
|
|
152
|
+
if (v === 'closed')
|
|
153
|
+
return 'billed requests are refused while a budget cannot be confirmed';
|
|
154
|
+
if (v === 'open')
|
|
155
|
+
return 'billed requests are let through while a budget cannot be confirmed';
|
|
156
|
+
return nonEmpty(v) ? `failure posture reported as "${word(v)}"` : 'failure posture not reported';
|
|
157
|
+
}
|
|
158
|
+
export function centerWiringDoctorDetail(reading) {
|
|
159
|
+
const unobserved = 'center wiring not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this engine is connected to a configuration center is unknown';
|
|
160
|
+
const r = guarded(reading);
|
|
161
|
+
if (r?.kind === 'not_reported') {
|
|
162
|
+
return 'center wiring not reported by this engine — only newer engines advertise it; this does not say whether the engine is connected to a configuration center';
|
|
163
|
+
}
|
|
164
|
+
if (r?.kind === 'none') {
|
|
165
|
+
return 'no configuration center on this deployment — the engine runs on its own local configuration; no quota lease or usage reporting goes through a center';
|
|
166
|
+
}
|
|
167
|
+
if (r?.kind !== 'present')
|
|
168
|
+
return unobserved;
|
|
169
|
+
try {
|
|
170
|
+
const v = r.view;
|
|
171
|
+
const pull = v.effectivePull === true ? 'configuration pulled from the center' : 'latest configuration pull did not succeed (running on what it already had)';
|
|
172
|
+
const rev = v.effectiveRevision;
|
|
173
|
+
const revision = isPlainObject(rev) && typeof rev.version === 'number' && Number.isFinite(rev.version) ? `revision ${String(rev.version)} in effect` : 'no revision in effect yet';
|
|
174
|
+
const lease = v.lease;
|
|
175
|
+
const leasePart = lease === 'absent'
|
|
176
|
+
? 'quota lease not configured'
|
|
177
|
+
: lease === 'configured'
|
|
178
|
+
? `quota lease configured (${healthClause(v.leaseHealth)}; ${failModeClause(v.failMode)})`
|
|
179
|
+
: nonEmpty(lease)
|
|
180
|
+
? `quota lease reported as "${word(lease)}"`
|
|
181
|
+
: 'quota lease not reported';
|
|
182
|
+
const usage = v.usageReport === true ? 'usage reporting on' : 'usage reporting off';
|
|
183
|
+
const iss = v.issuer;
|
|
184
|
+
const issuer = iss === 'explicit'
|
|
185
|
+
? 'token issuer set explicitly'
|
|
186
|
+
: iss === 'fallback'
|
|
187
|
+
? 'token issuer defaults to the center'
|
|
188
|
+
: nonEmpty(iss)
|
|
189
|
+
? `token issuer reported as "${word(iss)}"`
|
|
190
|
+
: 'token issuer not reported';
|
|
191
|
+
return `connected to a configuration center — ${pull}, ${revision} · ${leasePart} · ${usage} · ${issuer}`;
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return unobserved;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export function forgetCenterWiringReading(baseUrl) {
|
|
198
|
+
reader.forget(baseUrl);
|
|
199
|
+
}
|
|
200
|
+
export function __resetCenterWiringReadingsForTests() {
|
|
201
|
+
reader.__resetForTests();
|
|
202
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { corruptStoredRowContent, corruptStoredRowWhere } from './wireErrorTriage.js';
|
|
2
2
|
import { parkedResumeStartupContent, parkedResumeStartupFromError, resumePolicyBlockContent, resumePolicyBlockFromError, } from './resumeRefusalCopy.js';
|
|
3
3
|
import { decideParkMovedContent, decideParkMovedFromError } from './decideReceipt.js';
|
|
4
|
+
import { settingsPermissionBucketRefusalContent, settingsPermissionBucketRefusalFromError } from './wireRefusalCopy.js';
|
|
4
5
|
export function decideFailureNote(err, decision) {
|
|
5
6
|
const where = corruptStoredRowWhere(err);
|
|
6
7
|
const fold = resumePolicyBlockFromError(err);
|
|
7
8
|
const startup = parkedResumeStartupFromError(err, decision);
|
|
8
9
|
const moved = decideParkMovedFromError(err);
|
|
10
|
+
const bucket = settingsPermissionBucketRefusalFromError(err);
|
|
9
11
|
return ((where === undefined ? '' : ` — ${corruptStoredRowContent(where)}`) +
|
|
10
12
|
(fold === null ? '' : ` — ${resumePolicyBlockContent(fold)}`) +
|
|
13
|
+
(bucket === null ? '' : ` — ${settingsPermissionBucketRefusalContent(bucket)}`) +
|
|
11
14
|
(startup === null ? '' : ` — ${parkedResumeStartupContent(startup)}`) +
|
|
12
15
|
(moved === null ? '' : ` — ${decideParkMovedContent(moved)}`));
|
|
13
16
|
}
|