@sema-agent/client-core 0.85.2 → 0.87.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 (79) hide show
  1. package/CHANGELOG.md +263 -0
  2. package/README.md +25 -16
  3. package/dist/adapt/arms.js +14 -1
  4. package/dist/adapter/downstream/terminalToSdkResult.d.ts +3 -3
  5. package/dist/adapter/downstream/terminalToSdkResult.js +4 -1
  6. package/dist/adapter/runStream.js +75 -2
  7. package/dist/adapter/types.d.ts +3 -0
  8. package/dist/agentSession/backgroundView.js +7 -1
  9. package/dist/agentSession/contract.d.ts +1 -1
  10. package/dist/centerWiringCapability.d.ts +38 -0
  11. package/dist/centerWiringCapability.js +202 -0
  12. package/dist/decideFailureNote.js +3 -0
  13. package/dist/decideReceipt.d.ts +1 -1
  14. package/dist/decideReceipt.js +1 -1
  15. package/dist/detachWire.d.ts +6 -1
  16. package/dist/detachWire.js +22 -1
  17. package/dist/engineCapReader.d.ts +8 -0
  18. package/dist/engineCapReader.js +15 -0
  19. package/dist/engineErrorCodes.d.ts +3 -0
  20. package/dist/engineErrorCodes.js +8 -0
  21. package/dist/engineNoticeCodes.d.ts +62 -0
  22. package/dist/engineNoticeCodes.js +247 -29
  23. package/dist/executionLaneCapability.d.ts +6 -0
  24. package/dist/executionLaneCapability.js +55 -2
  25. package/dist/fleet/fleetProjection.js +6 -8
  26. package/dist/fleet/workflowSizeWarning.js +2 -5
  27. package/dist/gateOutcome.d.ts +13 -0
  28. package/dist/gateOutcome.js +48 -1
  29. package/dist/gateVocabulary.d.ts +6 -3
  30. package/dist/gateVocabulary.js +7 -15
  31. package/dist/generated/engineFactTables.d.ts +10 -0
  32. package/dist/generated/engineFactTables.js +113 -0
  33. package/dist/generated/engineNoticeTables.js +14 -0
  34. package/dist/hitl/crashConverged.d.ts +1 -0
  35. package/dist/hitl/crashConverged.js +17 -2
  36. package/dist/hitl/hitlBridge.d.ts +4 -0
  37. package/dist/hitl/hitlBridge.js +6 -0
  38. package/dist/hitl/parkResolver.js +17 -12
  39. package/dist/hitl/refusedToolNameProse.d.ts +4 -0
  40. package/dist/hitl/refusedToolNameProse.js +7 -0
  41. package/dist/hitl/sessionPolicyDeliverable.js +9 -5
  42. package/dist/hitl/sessionPolicyWire.d.ts +16 -0
  43. package/dist/hitl/sessionPolicyWire.js +49 -1
  44. package/dist/hitl/suspendedReopen.d.ts +7 -0
  45. package/dist/hitl/suspendedReopen.js +38 -7
  46. package/dist/hitl/toolApprovalWire.js +4 -5
  47. package/dist/index.d.ts +6 -1
  48. package/dist/index.js +6 -1
  49. package/dist/inheritEnvWire.d.ts +22 -0
  50. package/dist/inheritEnvWire.js +84 -0
  51. package/dist/peerFrames.js +2 -10
  52. package/dist/printInitToolFace.d.ts +6 -0
  53. package/dist/printInitToolFace.js +115 -2
  54. package/dist/readCredentialRefusal.d.ts +2 -0
  55. package/dist/readCredentialRefusal.js +7 -0
  56. package/dist/request/taskRequest.d.ts +4 -5
  57. package/dist/request/taskRequest.js +33 -23
  58. package/dist/resumeRefusalCopy.d.ts +2 -7
  59. package/dist/resumeRefusalCopy.js +2 -24
  60. package/dist/rewindArchiveCapability.js +5 -0
  61. package/dist/runTerminal.js +2 -6
  62. package/dist/seam.d.ts +11 -1
  63. package/dist/seam.js +7 -0
  64. package/dist/sqlEngineCapability.js +15 -2
  65. package/dist/storePostureCapability.d.ts +25 -0
  66. package/dist/storePostureCapability.js +132 -0
  67. package/dist/toolHistoryMismatch.d.ts +63 -0
  68. package/dist/toolHistoryMismatch.js +425 -0
  69. package/dist/toolResult.js +2 -47
  70. package/dist/wireErrorTriage.d.ts +1 -0
  71. package/dist/wireErrorTriage.js +3 -0
  72. package/dist/wireFailureShape.d.ts +3 -0
  73. package/dist/wireFailureShape.js +44 -0
  74. package/dist/wireRefusalCopy.d.ts +39 -1
  75. package/dist/wireRefusalCopy.js +350 -2
  76. package/dist/workflowClient.d.ts +7 -2
  77. package/dist/workflowClient.js +15 -7
  78. package/docs/INTEGRATION-CLIENTS.md +942 -16
  79. package/package.json +2 -2
@@ -10,11 +10,19 @@ import { registerSubagentAlias, registerSubagentContentAlias } from '../subagent
10
10
  import { readAsyncLaunchedAgentReceipt } from '../toolResult.js';
11
11
  import { isWorkflowAgentTaskId, recordWorkflowAgentTaskId } from '../workflow.js';
12
12
  import { isTerminalStatus } from '../runTerminal.js';
13
+ import { readToolHistoryMismatchKey } from '../toolHistoryMismatch.js';
13
14
  import { resolveEnginePanelTaskId } from '../engineAgentPanelStore.js';
14
15
  import { fleetRowAgentType } from '../fleet/fleetRowAgentType.js';
15
16
  import { chrome, mainLane, transcript, messageIdentityOf } from './ids.js';
16
17
  import { wiringManifestViewOf } from '../adapter/downstream/wiringManifestView.js';
17
18
  import { CANCEL_MESSAGE, decisionOf, flattenWireOutput, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, TASK_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './wireShapes.js';
19
+ function toolHistoryKeyCarry(m) {
20
+ if (m._sema_api_error_message !== true)
21
+ return {};
22
+ const raw = m._sema_tool_history_mismatch;
23
+ const key = readToolHistoryMismatchKey(typeof raw === 'object' && raw !== null ? raw : undefined);
24
+ return key === undefined ? {} : { _sema_tool_history_mismatch: key };
25
+ }
18
26
  const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
19
27
  const message = m.message;
20
28
  const blocks = Array.isArray(message?.content)
@@ -120,6 +128,7 @@ const assistantArm = function* (m, { ctx, idOf, text, cards, inst }) {
120
128
  session_id: ctx.sessionId,
121
129
  parent_tool_use_id: null,
122
130
  ...(m._sema_api_error_message === true ? { _sema_api_error_message: true } : {}),
131
+ ...toolHistoryKeyCarry(m),
123
132
  }, ctx.now());
124
133
  text.markEmittedText(textBlock.text);
125
134
  }
@@ -685,6 +694,9 @@ function prefixOf(whole, part) {
685
694
  return trimmed;
686
695
  return null;
687
696
  }
697
+ const ERROR_SUBTYPE_PREFIX = 'error_';
698
+ const _errorSubtypePrefixPin = true;
699
+ void _errorSubtypePrefixPin;
688
700
  const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
689
701
  yield chrome({ kind: 'retry_status', laneProof: mainLane(), status: null });
690
702
  yield* panel.settle(null, false);
@@ -698,7 +710,8 @@ const resultArm = function* (m, { ctx, idOf, text, cards, panel, flags }) {
698
710
  });
699
711
  yield* text.takeAnswerSegment();
700
712
  const salvaged = m._sema_salvaged_result;
701
- const terminalText = typeof salvaged === 'string' ? salvaged : typeof m.result === 'string' ? m.result : '';
713
+ const errorEnvelope = typeof m.subtype === 'string' && m.subtype.startsWith(ERROR_SUBTYPE_PREFIX);
714
+ const terminalText = typeof salvaged === 'string' ? salvaged : !errorEnvelope && typeof m.result === 'string' ? m.result : '';
702
715
  const committed = text.lastCommittedAnswerText;
703
716
  const terminalIdentity = messageIdentityOf(m, ctx);
704
717
  const emitTerminal = function* (body, tag) {
@@ -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
  }
@@ -499,6 +499,7 @@ function errorResult(ctx, parts) {
499
499
  ...selectedModelParts(parts.model),
500
500
  ...(parts.salvagedResult !== undefined ? { _sema_salvaged_result: parts.salvagedResult } : {}),
501
501
  ...(parts.outcomeUnknown === true ? { _sema_outcome: 'unknown' } : {}),
502
+ ...(parts.parkFailedReason !== undefined ? { _sema_park_failed_reason: washCredentials(parts.parkFailedReason) } : {}),
502
503
  });
503
504
  }
504
505
  export function doneToSdkResult(ev, ctx, observed) {
@@ -513,7 +514,9 @@ export function doneToSdkResult(ev, ctx, observed) {
513
514
  const durationMs = elapsedMs(ctx);
514
515
  const errorCode = runTerminalCode(terminal);
515
516
  const degraded = degradedOf(r);
516
- const errorBase = { durationMs, stats, model: r.model, errorCode, degraded, observed, facts: effectiveFactParts(rec) };
517
+ const parkFailedRaw = ev._sema_park_failed_reason;
518
+ const parkFailedReason = typeof parkFailedRaw === 'string' && parkFailedRaw.length > 0 ? parkFailedRaw : undefined;
519
+ const errorBase = { durationMs, stats, model: r.model, errorCode, degraded, observed, facts: effectiveFactParts(rec), parkFailedReason };
517
520
  if (terminal?.kind === 'failed') {
518
521
  const failedCode = terminal.code;
519
522
  const failedMessage = terminal.message;
@@ -5,9 +5,11 @@ import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from
5
5
  import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
6
6
  import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isCorruptSessionCode, isLimitsExceededCode } from '../engineErrorCodes.js';
7
7
  import { corruptSessionContent } from '../corruptSessionCopy.js';
8
+ import { classifyToolHistoryMismatch, noteToolHistoryTerminal, openToolHistoryGeneration, TOOL_HISTORY_REWIND_SENTENCE, toolHistoryMismatchRowKey, toolHistoryRepeatSentence, toolHistoryRewindOffered, } from '../toolHistoryMismatch.js';
8
9
  import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
9
10
  import { washCredentials } from '../displayUntrusted.js';
10
11
  import { ccToolDenialKindForToolEnd, gateDeniedBy, gateOutcomeOf } from '../gateOutcome.js';
12
+ import { resumeReopenContent, resumeReopenFromError, suspendedReopenOf } from '../hitl/suspendedReopen.js';
11
13
  import { isCcToolDenialKind, isCcToolDenialKindADenial, isGateDeniedByWord } from '../gateVocabulary.js';
12
14
  const mainLane = () => ({ lane: 'main' });
13
15
  function emitChromeFireAndForget(ctx, event) {
@@ -19,6 +21,27 @@ function emitChromeFireAndForget(ctx, event) {
19
21
  }
20
22
  catch { }
21
23
  }
24
+ function suspendedReopenedNotice(ev) {
25
+ try {
26
+ const reopen = suspendedReopenOf(ev);
27
+ if (reopen.kind !== 'reopened')
28
+ return undefined;
29
+ const detail = resumeReopenFromError({ errorCode: reopen.code, retriable: true });
30
+ const rawTaskId = ev.taskId;
31
+ const eventId = eventSeq(ev);
32
+ return {
33
+ kind: 'suspended_reopened',
34
+ laneProof: mainLane(),
35
+ reopen,
36
+ ...(detail !== null ? { content: resumeReopenContent(detail) } : {}),
37
+ ...(typeof eventId === 'string' && eventId.length > 0 ? { eventId } : {}),
38
+ ...(typeof rawTaskId === 'string' && rawTaskId.length > 0 ? { taskId: rawTaskId } : {}),
39
+ };
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
22
45
  export function pendingGateIsProvablyDifferent(prev, next) {
23
46
  const a = prev?.checkpointId;
24
47
  const b = next?.checkpointId;
@@ -98,7 +121,7 @@ export function isModelOutputErrorRowText(text) {
98
121
  return text.trimStart().startsWith(`${MODEL_OUTPUT_ERROR_PREFIX}:`);
99
122
  }
100
123
  export const OUTCOME_UNKNOWN_ROW_PREFIX = 'Outcome unknown';
101
- function syntheticTerminalRow(ctx, text) {
124
+ function syntheticTerminalRow(ctx, text, historyKey) {
102
125
  return stamp(ctx, {
103
126
  uuid: undefined,
104
127
  session_id: undefined,
@@ -106,8 +129,22 @@ function syntheticTerminalRow(ctx, text) {
106
129
  message: { role: 'assistant', model: '<synthetic>', content: [{ type: 'text', text: washCredentials(text) }] },
107
130
  parent_tool_use_id: null,
108
131
  _sema_api_error_message: true,
132
+ ...(historyKey !== undefined ? { _sema_tool_history_mismatch: historyKey } : {}),
109
133
  });
110
134
  }
135
+ function engineRunIdOf(result) {
136
+ const v = result?.runId;
137
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
138
+ }
139
+ function engineSessionIdOf(result) {
140
+ const v = result?.sessionId;
141
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
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
+ }
111
148
  export function isOutcomeUnknownRowText(text) {
112
149
  return text.trimStart().startsWith(`${OUTCOME_UNKNOWN_ROW_PREFIX}:`);
113
150
  }
@@ -192,6 +229,7 @@ async function* runStreamInner(events, ctx, handle = {}) {
192
229
  const seen = new Set();
193
230
  if (ctx.startedAtMs === undefined)
194
231
  ctx.startedAtMs = Date.now();
232
+ const historyGeneration = openToolHistoryGeneration();
195
233
  const nestedUsageByTask = new Map();
196
234
  let usageMissingObserved = false;
197
235
  const toolInputByCallId = new Map();
@@ -478,7 +516,37 @@ async function* runStreamInner(events, ctx, handle = {}) {
478
516
  const sessionUnreadable = ev.type === 'failed'
479
517
  ? isCorruptSessionCode(ev.errorCode)
480
518
  : doneTerminal?.kind === 'failed' && isCorruptSessionCode(doneTerminal.code);
481
- yield syntheticTerminalRow(ctx, sessionUnreadable ? `${rowText}\n${corruptSessionContent()}` : rowText);
519
+ const historyMismatch = !busy.busy && !governance && !isModelOutputErrorText(errText)
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;
526
+ const historyEchoedSid = ev.type === 'done' ? engineSessionIdOf(ev.result) : undefined;
527
+ const historyRepeat = noteToolHistoryTerminal({
528
+ sessionId: historyEchoedSid ?? ctx.sessionId,
529
+ sessionEchoed: historyEchoedSid !== undefined,
530
+ generation: historyGeneration,
531
+ runId: ev.type === 'done' ? engineRunIdOf(ev.result) : undefined,
532
+ replay: ev.type === 'done' && ev.replay === true,
533
+ mismatch: historyMismatch,
534
+ });
535
+ const rewindLine = historyMismatch !== null && ctx.lane !== 'print' && ctx.lane !== 'utility' && toolHistoryRewindOffered(ctx.offersRewind)
536
+ ? `\n${TOOL_HISTORY_REWIND_SENTENCE}${historyRepeat !== undefined && historyRepeat >= 2 ? ` ${toolHistoryRepeatSentence(historyRepeat)}` : ''}`
537
+ : '';
538
+ yield syntheticTerminalRow(ctx, `${sessionUnreadable ? `${rowText}\n${corruptSessionContent()}` : rowText}${rewindLine}`, historyMismatch === null ? undefined : toolHistoryMismatchRowKey(historyMismatch, historyRepeat));
539
+ }
540
+ else {
541
+ const echoedSid = ev.type === 'done' ? engineSessionIdOf(ev.result) : undefined;
542
+ noteToolHistoryTerminal({
543
+ sessionId: echoedSid ?? ctx.sessionId,
544
+ sessionEchoed: echoedSid !== undefined,
545
+ generation: historyGeneration,
546
+ runId: ev.type === 'done' ? engineRunIdOf(ev.result) : undefined,
547
+ replay: ev.type === 'done' && ev.replay === true,
548
+ mismatch: null,
549
+ });
482
550
  }
483
551
  if (ev.type === 'done' && isReviewPark(doneTerminal) && ctx.emitChrome) {
484
552
  try {
@@ -514,6 +582,11 @@ async function* runStreamInner(events, ctx, handle = {}) {
514
582
  yield resultFrame;
515
583
  return;
516
584
  }
585
+ if (ev.type === 'suspended') {
586
+ const notice = suspendedReopenedNotice(ev);
587
+ if (notice !== undefined)
588
+ emitChromeFireAndForget(ctx, notice);
589
+ }
517
590
  const projection = eventToSdkMessage(ev, ctx);
518
591
  if (projection.kind === 'message') {
519
592
  yield projection.message;
@@ -18,6 +18,9 @@ export interface EmitContext {
18
18
  startedAtMs?: number;
19
19
  model?: string;
20
20
  lane?: RequestLane;
21
+ offersRewind?: boolean | {
22
+ readonly engineBaseUrl: string;
23
+ };
21
24
  emitChrome?(event: ChromeEvent): void | Promise<void>;
22
25
  onDroppedFrame?(info: DroppedFrameInfo): void;
23
26
  }
@@ -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
  }
@@ -1,5 +1,5 @@
1
1
  import { type GateOutcomeView } from './gateOutcome.js';
2
- import { type ResumeReopenDetail } from './resumeRefusalCopy.js';
2
+ import { type ResumeReopenDetail } from './hitl/suspendedReopen.js';
3
3
  export interface DecideReceiptView {
4
4
  status?: string;
5
5
  taskId?: string;
@@ -1,5 +1,5 @@
1
1
  import { gateOutcomeOf } from './gateOutcome.js';
2
- import { resumeReopenFromError } from './resumeRefusalCopy.js';
2
+ import { resumeReopenFromError } from './hitl/suspendedReopen.js';
3
3
  import { DECIDE_CLAIM_LOST, DECIDE_GATE_MOVED, DECIDE_NOT_PARKED, DECIDE_WORKFLOW_HOST_UNKNOWN, DECIDE_WORKFLOW_LANE_CODES, DECIDE_WORKFLOW_REMEMBER_UNSUPPORTED, isDecideParkMovedCode, } from './engineErrorCodes.js';
4
4
  import { effectiveWireErrorCode } from './wireErrorTriage.js';
5
5
  import { readErrorString } from './wireFailureShape.js';
@@ -1,4 +1,5 @@
1
1
  import { type EnvLike } from './hostEnv.js';
2
+ import { type WireAuthTokenSource } from './engineWireSdk.js';
2
3
  export declare const DETACH_HEADER = "x-detach-on-disconnect";
3
4
  export declare const HEADLESS_DETACH_ENV = "SEMA_HEADLESS_DETACH";
4
5
  export declare const DETACH_WIRE_MIN_ENGINE: readonly [number, number, number];
@@ -36,7 +37,11 @@ export interface DetachCancelArm {
36
37
  principal: string | undefined;
37
38
  getTaskId: () => string | null;
38
39
  }
39
- export declare function armDetachCancel(arm: DetachCancelArm): void;
40
+ export declare function armDetachCancel(arm: Omit<DetachCancelArm, 'authToken'> & {
41
+ authToken: WireAuthTokenSource | {
42
+ mode: 'loopback-unauthed';
43
+ };
44
+ }): void;
40
45
  export declare function isDetachArmed(): boolean;
41
46
  export declare function detachedTaskId(): string | null;
42
47
  export declare function detachCancelArm(): DetachCancelArm | null;
@@ -1,6 +1,7 @@
1
1
  import { hostEnv } from './hostEnv.js';
2
2
  import { envFlagOff } from './envFlag.js';
3
3
  import { versionSupportsLimits } from './limitsWire.js';
4
+ import { resolveWireAuth } from './engineWireSdk.js';
4
5
  export const DETACH_HEADER = 'x-detach-on-disconnect';
5
6
  export const HEADLESS_DETACH_ENV = 'SEMA_HEADLESS_DETACH';
6
7
  export const DETACH_WIRE_MIN_ENGINE = [1, 196, 0];
@@ -80,6 +81,17 @@ let cancelArm = null;
80
81
  export function armDetachCancel(arm) {
81
82
  cancelArm = arm;
82
83
  }
84
+ function armAuthNow(baseUrl, source) {
85
+ let token;
86
+ try {
87
+ const v = source();
88
+ token = typeof v === 'string' ? v : undefined;
89
+ }
90
+ catch {
91
+ token = undefined;
92
+ }
93
+ return resolveWireAuth(baseUrl, token);
94
+ }
83
95
  export function isDetachArmed() {
84
96
  return cancelArm !== null;
85
97
  }
@@ -87,7 +99,16 @@ export function detachedTaskId() {
87
99
  return cancelArm?.getTaskId() ?? null;
88
100
  }
89
101
  export function detachCancelArm() {
90
- return cancelArm;
102
+ const arm = cancelArm;
103
+ if (arm === null)
104
+ return null;
105
+ const source = arm.authToken;
106
+ if (typeof source === 'function') {
107
+ const view = Object.defineProperties(Object.create(Object.getPrototypeOf(arm)), Object.getOwnPropertyDescriptors(arm));
108
+ Object.defineProperty(view, 'authToken', { value: armAuthNow(arm.baseUrl, source), enumerable: true, writable: true, configurable: true });
109
+ return view;
110
+ }
111
+ return arm;
91
112
  }
92
113
  export function _resetDetachWireForTest() {
93
114
  cancelArm = null;
@@ -12,4 +12,12 @@ interface EngineCapReader<R> {
12
12
  __resetForTests(): void;
13
13
  }
14
14
  export declare function createEngineCapReader<R>(spec: EngineCapReaderSpec<R>): EngineCapReader<R>;
15
+ export type CapsGeneration = {
16
+ generation: 'new';
17
+ store: object;
18
+ } | {
19
+ generation: 'old';
20
+ store: 'absent' | 'malformed';
21
+ };
22
+ export declare function capsGenerationOf(caps: object): CapsGeneration;
15
23
  export {};
@@ -43,3 +43,18 @@ export function createEngineCapReader(spec) {
43
43
  },
44
44
  };
45
45
  }
46
+ export function capsGenerationOf(caps) {
47
+ try {
48
+ if (!Object.hasOwn(caps, 'store'))
49
+ return { generation: 'old', store: 'absent' };
50
+ const store = caps.store;
51
+ if (store === undefined)
52
+ return { generation: 'old', store: 'absent' };
53
+ if (typeof store !== 'object' || store === null || Array.isArray(store))
54
+ return { generation: 'old', store: 'malformed' };
55
+ return { generation: 'new', store };
56
+ }
57
+ catch {
58
+ return { generation: 'old', store: 'malformed' };
59
+ }
60
+ }
@@ -5,6 +5,7 @@ export declare const LIMITS_MAX_WALLTIME_EXCEEDED = "limits.max_walltime_exceede
5
5
  export declare function isLimitsExceededCode(code: string | undefined): boolean;
6
6
  export declare const USAGE_WINDOW_EXHAUSTED = "usage.window_exhausted";
7
7
  export declare const ENV_LIFETIME_EXPIRED = "env.lifetime_expired";
8
+ export declare const FLEET_LEASE_UNAVAILABLE = "fleet.lease_unavailable";
8
9
  export declare const CONFIG_LIMIT_INVALID = "config.limit_invalid";
9
10
  export declare const CONFIG_STRATEGY_FIND_LIMIT_INVALID = "config.strategy_find_limit_invalid";
10
11
  export declare const CONFIG_STRATEGY_MAX_SIZE_INVALID = "config.strategy_max_size_invalid";
@@ -14,6 +15,8 @@ export declare const CONFIG_MEMORY_PROJECT_KEY_SPELLING = "config.memory_project
14
15
  export declare const CONFIG_MEMORY_WRITE_SCOPE_MISMATCH = "config.memory_write_scope_mismatch";
15
16
  export declare const CONFIG_LEGACY_TOOL_NAME = "config.legacy_tool_name";
16
17
  export declare const CONFIG_REFUSAL_CODES: ReadonlySet<string>;
18
+ export declare const RULES_LEGACY_TOOL_NAME = "rules.legacy_tool_name";
19
+ export declare function isRulesRefusalCode(code: string | undefined): boolean;
17
20
  export declare const DELEGATION_CONCURRENCY_CAP = "delegation.concurrency_cap";
18
21
  export declare const DELEGATION_SESSION_CAP = "delegation.session_cap";
19
22
  export declare const DELEGATION_CAP_CODES: ReadonlySet<string>;