@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.
Files changed (59) hide show
  1. package/CHANGELOG.md +242 -0
  2. package/README.md +21 -14
  3. package/dist/adapt/arms.js +5 -1
  4. package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
  5. package/dist/adapter/activeRunSelfHeal.js +126 -36
  6. package/dist/adapter/downstream/terminalToSdkResult.d.ts +3 -3
  7. package/dist/agentSession/backgroundView.js +7 -1
  8. package/dist/agentSession/contract.d.ts +1 -1
  9. package/dist/centerWiringCapability.d.ts +38 -0
  10. package/dist/centerWiringCapability.js +202 -0
  11. package/dist/decideFailureNote.js +3 -0
  12. package/dist/engineCapReader.d.ts +8 -0
  13. package/dist/engineCapReader.js +15 -0
  14. package/dist/engineErrorCodes.d.ts +3 -0
  15. package/dist/engineErrorCodes.js +8 -0
  16. package/dist/engineNoticeCodes.d.ts +62 -0
  17. package/dist/engineNoticeCodes.js +247 -29
  18. package/dist/executionLaneCapability.d.ts +6 -0
  19. package/dist/executionLaneCapability.js +55 -2
  20. package/dist/fleet/fleetProjection.js +6 -8
  21. package/dist/fleet/workflowSizeWarning.js +2 -5
  22. package/dist/gateOutcome.d.ts +13 -0
  23. package/dist/gateOutcome.js +48 -1
  24. package/dist/gateVocabulary.d.ts +6 -3
  25. package/dist/gateVocabulary.js +7 -15
  26. package/dist/generated/engineFactTables.d.ts +10 -0
  27. package/dist/generated/engineFactTables.js +113 -0
  28. package/dist/generated/engineNoticeTables.js +14 -0
  29. package/dist/hitl/refusedToolNameProse.d.ts +4 -0
  30. package/dist/hitl/refusedToolNameProse.js +7 -0
  31. package/dist/hitl/sessionPolicyDeliverable.js +9 -5
  32. package/dist/hitl/sessionPolicyWire.d.ts +16 -0
  33. package/dist/hitl/sessionPolicyWire.js +49 -1
  34. package/dist/index.d.ts +5 -1
  35. package/dist/index.js +5 -1
  36. package/dist/inheritEnvWire.d.ts +22 -0
  37. package/dist/inheritEnvWire.js +84 -0
  38. package/dist/peerFrames.js +2 -10
  39. package/dist/printInitToolFace.d.ts +6 -0
  40. package/dist/printInitToolFace.js +115 -2
  41. package/dist/readCredentialRefusal.d.ts +2 -0
  42. package/dist/readCredentialRefusal.js +7 -0
  43. package/dist/request/taskRequest.d.ts +4 -5
  44. package/dist/request/taskRequest.js +33 -23
  45. package/dist/runTerminal.js +2 -6
  46. package/dist/sqlEngineCapability.js +15 -2
  47. package/dist/storePostureCapability.d.ts +25 -0
  48. package/dist/storePostureCapability.js +132 -0
  49. package/dist/toolResult.js +2 -47
  50. package/dist/wireErrorTriage.d.ts +1 -0
  51. package/dist/wireErrorTriage.js +3 -0
  52. package/dist/wireFailureShape.d.ts +3 -0
  53. package/dist/wireFailureShape.js +44 -0
  54. package/dist/wireRefusalCopy.d.ts +39 -1
  55. package/dist/wireRefusalCopy.js +350 -2
  56. package/dist/workflowClient.d.ts +4 -0
  57. package/dist/workflowClient.js +12 -4
  58. package/docs/INTEGRATION-CLIENTS.md +853 -14
  59. package/package.json +2 -2
@@ -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 terminalText = typeof salvaged === 'string' ? salvaged : typeof m.result === 'string' ? m.result : '';
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
- if (typeof e === 'object' && e !== null && 'message' in e) {
52
- const m = e.message;
53
- if (typeof m === 'string' && m.length > 0)
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?.status;
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
- if (typeof receipt !== 'object' || receipt === null || !('delivery' in receipt))
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
- const d = receipt.delivery;
224
- return typeof d === 'string' && d.length > 0 ? d : null;
275
+ }
225
276
  }
226
277
  export function readSteerReceiptStatus(receipt) {
227
- if (typeof receipt !== 'object' || receipt === null || !('status' in receipt))
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
- const s = receipt.status;
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
- return null;
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 = readStatus(record);
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
- return { reopened: false };
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
- return { reopened: false };
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
- ...(reopenRefusedForDecisionInFlight(verdict) ? { decisionInFlight: true } : {}),
509
+ ...(inFlight ? { decisionInFlight: true } : {}),
443
510
  ...(verdict.pendingRowGone === true ? { pendingRowGone: true } : {}),
444
- ...(reopenRefusedForUserDismissal(verdict) ? { dismissedByUser: true } : {}),
445
- ...(reopenRefusedForNoPresentationSurface(verdict) ? { noPresentationSurface: true } : {}),
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
- ...(reopenRefusedForNoPresentationSurface(verdict) ? { noPresentationSurface: true } : {}),
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
- status = readStatus(await durableGet.call(durable, taskId, { signal: lease.signal, ...sessionOpts(deps) }));
568
+ record = await durableGet.call(durable, taskId, { signal: lease.signal, ...sessionOpts(deps) });
569
+ answered = true;
495
570
  }
496
571
  catch (e) {
497
- if (e?.status === 404)
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
- return { status, ghost };
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 || !ASK_PARK_STATES.includes(afterCard.status)) {
570
- return afterCard.status !== null && !CLAIM_HELD_STATES.includes(afterCard.status) ? reopenFailed : reopenFailedAfterCard;
571
- }
572
- const stillGone = await readOwnedPendingCount(recheckOwnedPending, deps);
573
- if (stillGone !== 0)
574
- return reopenFailedAfterCard;
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, DeniedBy, TaskStats } from '@sema-agent/sdk';
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?: 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
- return { health: isNotImplemented(err) ? 'not-configured' : 'unavailable', tasks: [] };
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
  }