@sema-agent/client-core 0.84.0 → 0.85.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 (101) hide show
  1. package/CHANGELOG.md +247 -0
  2. package/README.md +27 -13
  3. package/dist/abortableSleep.d.ts +10 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +1 -1
  6. package/dist/adapt.d.ts +1 -1
  7. package/dist/adapt.js +1 -1
  8. package/dist/adapter/downstream/terminalToSdkResult.d.ts +1 -0
  9. package/dist/adapter/downstream/terminalToSdkResult.js +19 -10
  10. package/dist/adapter/runStream.js +6 -2
  11. package/dist/agentSession/engineSessionBackgroundStop.d.ts +50 -0
  12. package/dist/agentSession/engineSessionBackgroundStop.js +291 -0
  13. package/dist/agentsWireCaps.d.ts +2 -1
  14. package/dist/agentsWireCaps.js +8 -0
  15. package/dist/cloudConfigWireCaps.js +2 -1
  16. package/dist/corruptSession.d.ts +3 -0
  17. package/dist/corruptSession.js +29 -0
  18. package/dist/corruptSessionCopy.d.ts +1 -0
  19. package/dist/corruptSessionCopy.js +4 -0
  20. package/dist/decideFailureNote.d.ts +2 -1
  21. package/dist/decideFailureNote.js +8 -3
  22. package/dist/decideReceipt.d.ts +6 -0
  23. package/dist/decideReceipt.js +30 -1
  24. package/dist/detachWire.d.ts +1 -0
  25. package/dist/detachWire.js +13 -5
  26. package/dist/displayUntrusted.js +114 -18
  27. package/dist/effortWire.d.ts +2 -1
  28. package/dist/effortWire.js +2 -4
  29. package/dist/engineErrorCodes.d.ts +11 -0
  30. package/dist/engineErrorCodes.js +32 -0
  31. package/dist/engineNoticeCodes.d.ts +39 -4
  32. package/dist/engineNoticeCodes.js +117 -161
  33. package/dist/engineWireFor.d.ts +43 -0
  34. package/dist/engineWireFor.js +399 -0
  35. package/dist/engineWireSdk.d.ts +6 -0
  36. package/dist/fileHistoryCaptureCapability.d.ts +4 -1
  37. package/dist/fileHistoryCaptureCapability.js +43 -9
  38. package/dist/gateVocabulary.d.ts +1 -2
  39. package/dist/gateVocabulary.js +1 -22
  40. package/dist/generated/engineNoticeTables.d.ts +3 -0
  41. package/dist/generated/engineNoticeTables.js +156 -0
  42. package/dist/generated/toolNameTables.d.ts +2 -0
  43. package/dist/generated/toolNameTables.js +43 -0
  44. package/dist/headlessPermissionModeWire.js +2 -4
  45. package/dist/hitl/askGateWire.d.ts +1 -1
  46. package/dist/hitl/askGateWire.js +1 -1
  47. package/dist/hitl/frameRouter.js +4 -2
  48. package/dist/hitl/hitlHostSurface.js +1 -1
  49. package/dist/hitl/parkResolver.d.ts +1 -0
  50. package/dist/hitl/parkResolver.js +11 -3
  51. package/dist/hitl/planReviewWire.d.ts +41 -3
  52. package/dist/hitl/planReviewWire.js +264 -49
  53. package/dist/hitl/rosterMountedNames.d.ts +2 -0
  54. package/dist/hitl/rosterMountedNames.js +38 -0
  55. package/dist/hitl/sessionPolicyDeliverable.d.ts +8 -2
  56. package/dist/hitl/sessionPolicyDeliverable.js +39 -48
  57. package/dist/hitl/sessionPolicyWire.d.ts +40 -0
  58. package/dist/hitl/sessionPolicyWire.js +173 -0
  59. package/dist/hitl/toolApprovalWire.d.ts +4 -2
  60. package/dist/hitl/toolApprovalWire.js +6 -4
  61. package/dist/index.d.ts +10 -3
  62. package/dist/index.js +7 -2
  63. package/dist/liveInitToolFace.js +28 -10
  64. package/dist/permissionWireCaps.d.ts +2 -1
  65. package/dist/permissionWireCaps.js +2 -7
  66. package/dist/request/taskRequest.d.ts +2 -0
  67. package/dist/request/taskRequest.js +18 -1
  68. package/dist/resumeRefusalCopy.d.ts +11 -0
  69. package/dist/resumeRefusalCopy.js +42 -2
  70. package/dist/runTerminal.js +4 -0
  71. package/dist/sandboxWire.js +1 -1
  72. package/dist/sealedKeyCapability.d.ts +17 -0
  73. package/dist/sealedKeyCapability.js +42 -0
  74. package/dist/seatContract.d.ts +2 -1
  75. package/dist/seatContract.js +5 -21
  76. package/dist/subagent/engineCompactWire.d.ts +13 -2
  77. package/dist/subagent/engineCompactWire.js +48 -37
  78. package/dist/subagent/engineDelegatedPrompt.d.ts +9 -1
  79. package/dist/subagent/engineDelegatedPrompt.js +16 -16
  80. package/dist/subagent/engineRowStopGate.d.ts +2 -2
  81. package/dist/subagent/engineRowStopGate.js +4 -4
  82. package/dist/subagent/engineSubagentOutput.d.ts +8 -0
  83. package/dist/subagent/engineSubagentOutput.js +37 -18
  84. package/dist/subagent/engineSubagentResume.d.ts +11 -2
  85. package/dist/subagent/engineSubagentResume.js +28 -14
  86. package/dist/subagent/engineSubagentSteer.d.ts +10 -1
  87. package/dist/subagent/engineSubagentSteer.js +16 -11
  88. package/dist/subagent/engineSubagentTail.d.ts +10 -1
  89. package/dist/subagent/engineSubagentTail.js +35 -17
  90. package/dist/subagent/engineTaskHandleWire.d.ts +13 -0
  91. package/dist/subagent/engineTaskHandleWire.js +41 -20
  92. package/dist/systemReminderTag.d.ts +5 -0
  93. package/dist/systemReminderTag.js +20 -9
  94. package/dist/taskRequestWords.d.ts +5 -0
  95. package/dist/taskRequestWords.js +4 -0
  96. package/dist/wireErrorTriage.d.ts +1 -0
  97. package/dist/wireErrorTriage.js +3 -1
  98. package/dist/wireRefusalCopy.d.ts +10 -0
  99. package/dist/wireRefusalCopy.js +62 -2
  100. package/docs/INTEGRATION-CLIENTS.md +1128 -22
  101. package/package.json +1 -1
@@ -1 +1,11 @@
1
1
  export declare function abortableSleep(ms: number, signal: AbortSignal): Promise<void>;
2
+ export type DeadlineSettled<T> = {
3
+ kind: 'value';
4
+ value: T;
5
+ } | {
6
+ kind: 'error';
7
+ detail: string;
8
+ } | {
9
+ kind: 'deadline';
10
+ };
11
+ export declare function settleWithinDeadline<T>(ms: number, run: (signal: AbortSignal) => Promise<T>): Promise<DeadlineSettled<T>>;
@@ -13,3 +13,40 @@ export function abortableSleep(ms, signal) {
13
13
  signal.addEventListener('abort', onAbort, { once: true });
14
14
  });
15
15
  }
16
+ function errorDetail(error) {
17
+ try {
18
+ return String(error);
19
+ }
20
+ catch {
21
+ return '';
22
+ }
23
+ }
24
+ export function settleWithinDeadline(ms, run) {
25
+ const ac = new AbortController();
26
+ return new Promise((resolve) => {
27
+ let settled = false;
28
+ const timer = setTimeout(() => {
29
+ if (settled)
30
+ return;
31
+ settled = true;
32
+ resolve({ kind: 'deadline' });
33
+ ac.abort();
34
+ }, ms);
35
+ const finish = (out) => {
36
+ if (settled)
37
+ return;
38
+ settled = true;
39
+ clearTimeout(timer);
40
+ resolve(out);
41
+ };
42
+ let pending;
43
+ try {
44
+ pending = Promise.resolve(run(ac.signal));
45
+ }
46
+ catch (error) {
47
+ finish({ kind: 'error', detail: errorDetail(error) });
48
+ return;
49
+ }
50
+ pending.then((value) => finish({ kind: 'value', value }), (error) => finish({ kind: 'error', detail: errorDetail(error) }));
51
+ });
52
+ }
@@ -141,7 +141,7 @@ const userArm = function* (m, { ctx, idOf }) {
141
141
  };
142
142
  const systemArm = function* (m, { ctx, idOf }) {
143
143
  yield transcript(m, ctx.now());
144
- const attachedFiles = m._sema_attached_files ?? m.attachedFiles;
144
+ const attachedFiles = m._sema_attached_files;
145
145
  if (Array.isArray(attachedFiles)) {
146
146
  let i = 0;
147
147
  for (const f of attachedFiles) {
package/dist/adapt.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { AdapterContext, AdapterOutput, WireToCcAdapter } from './seam.js';
2
- export { CANCEL_MESSAGE, estimateCjkTokens, flattenWireOutput, FLUSH_INTERVAL_MS, IDLE_FLUSH_MS, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, SUBAGENT_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './adapt/wireShapes.js';
2
+ export { CANCEL_MESSAGE, estimateCjkTokens, flattenWireOutput, FLUSH_INTERVAL_MS, IDLE_FLUSH_MS, REJECT_MESSAGE, sanitizeToolUseBlock, SUBAGENT_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './adapt/wireShapes.js';
3
3
  export interface AdapterLedgerState {
4
4
  version: 1;
5
5
  renderedNotifications: string[];
package/dist/adapt.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { isEngineWorkflowNotified, isWorkflowCompletionCardEnqueued, listNotifiedRuns, listWorkflowCompletionCardsEnqueued, markEngineWorkflowNotified, noteWorkflowCompletionCardEnqueued, parseTranscriptNotificationSeeds, } from './notifications.js';
2
- export { CANCEL_MESSAGE, estimateCjkTokens, flattenWireOutput, FLUSH_INTERVAL_MS, IDLE_FLUSH_MS, REJECT_MESSAGE, sanitizeToolUseBlock, shortTaskLabel, SUBAGENT_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './adapt/wireShapes.js';
2
+ export { CANCEL_MESSAGE, estimateCjkTokens, flattenWireOutput, FLUSH_INTERVAL_MS, IDLE_FLUSH_MS, REJECT_MESSAGE, sanitizeToolUseBlock, SUBAGENT_TOOL_NAMES, WORKFLOW_TOOL_NAMES, } from './adapt/wireShapes.js';
3
3
  import { IDLE_FLUSH_MS } from './adapt/wireShapes.js';
4
4
  import { chrome, mainLane, makeIdOf } from './adapt/ids.js';
5
5
  import { createTextStream } from './adapt/textStream.js';
@@ -30,6 +30,7 @@ export interface SemaPermissionDenial {
30
30
  readonly tool_use_id?: string;
31
31
  readonly tool_input?: Record<string, unknown>;
32
32
  readonly _sema_tool_input_source?: 'tool_start';
33
+ readonly _sema_tool_input_redacted?: true;
33
34
  readonly _sema_tool_arg?: string;
34
35
  readonly deniedBy?: DeniedBy;
35
36
  readonly _sema_denied_by_source?: 'tool_end';
@@ -5,7 +5,11 @@ import { REDACTION_MARKER_PREFIX, washCredentials } from '../../displayUntrusted
5
5
  import { readEffectiveReasoning, readEffectiveMemoryScopes } from '../../effectiveFacts.js';
6
6
  import { isReviewPark, readRunTerminal, runTerminalCode, runTerminalGateToolName, } from '../../runTerminal.js';
7
7
  import { toCcModelUsage } from './turnUsageToModelUsage.js';
8
- import { LIMITS_MAX_COST_EXCEEDED, LIMITS_MAX_TOKENS_EXCEEDED, LIMITS_MAX_TURNS_EXCEEDED, LIMITS_MAX_WALLTIME_EXCEEDED, OUTPUT_INVALID, isRewindFamilyCode, } from '../../engineErrorCodes.js';
8
+ import { LIMITS_MAX_COST_EXCEEDED, LIMITS_MAX_TOKENS_EXCEEDED, LIMITS_MAX_TURNS_EXCEEDED, LIMITS_MAX_WALLTIME_EXCEEDED, OUTPUT_INVALID, isCorruptSessionCode, isRewindFamilyCode, } from '../../engineErrorCodes.js';
9
+ import { corruptSessionContent } from '../../corruptSessionCopy.js';
10
+ function withSessionUnreadableNote(text, code) {
11
+ return isCorruptSessionCode(code) ? `${text}\n${corruptSessionContent()}` : text;
12
+ }
9
13
  function finiteOr0(v) {
10
14
  return typeof v === 'number' && Number.isFinite(v) ? v : 0;
11
15
  }
@@ -40,14 +44,15 @@ function joinableToolInput(args) {
40
44
  if (args === null || typeof args !== 'object' || Array.isArray(args))
41
45
  return undefined;
42
46
  let nodes = 0;
47
+ let transportRedacted = false;
43
48
  const stack = [{ v: args, depth: 0 }];
44
49
  while (stack.length > 0) {
45
50
  const { v, depth } = stack.pop();
46
51
  if (++nodes > TOOL_INPUT_SCAN_MAX_NODES || depth > TOOL_INPUT_SCAN_MAX_DEPTH)
47
52
  return undefined;
48
53
  if (typeof v === 'string') {
49
- if (v.includes(REDACTION_MARKER_PREFIX) || isTransportPlaceholderLeaf(v))
50
- return undefined;
54
+ if (!transportRedacted && (v.includes(REDACTION_MARKER_PREFIX) || isTransportPlaceholderLeaf(v)))
55
+ transportRedacted = true;
51
56
  continue;
52
57
  }
53
58
  if (v === null || typeof v !== 'object')
@@ -56,7 +61,7 @@ function joinableToolInput(args) {
56
61
  for (const c of children)
57
62
  stack.push({ v: c, depth: depth + 1 });
58
63
  }
59
- return args;
64
+ return { input: args, transportRedacted };
60
65
  }
61
66
  const GATE_DENIED_DECISIONS = new Set(['deny']);
62
67
  const GATE_NON_DENIAL_DECISIONS = new Set(['allow', 'approve', 'edit', 'reject']);
@@ -92,9 +97,10 @@ function permissionDenialParts(stats, observed) {
92
97
  const ledgerInput = rawInput !== null && typeof rawInput === 'object' && !Array.isArray(rawInput)
93
98
  ? rawInput
94
99
  : undefined;
95
- const joinedInput = ledgerInput === undefined && toolUseId !== undefined && toolInputByCallId?.has(toolUseId) === true
100
+ const joined = ledgerInput === undefined && toolUseId !== undefined && toolInputByCallId?.has(toolUseId) === true
96
101
  ? joinableToolInput(toolInputByCallId.get(toolUseId))
97
102
  : undefined;
103
+ const joinedInput = joined?.input;
98
104
  const toolInput = ledgerInput ?? joinedInput;
99
105
  const ledgerDeniedByRaw = row.deniedBy;
100
106
  const ledgerDeniedBy = typeof ledgerDeniedByRaw === 'string' && isGateDeniedByWord(ledgerDeniedByRaw)
@@ -116,6 +122,7 @@ function permissionDenialParts(stats, observed) {
116
122
  ...(toolUseId !== undefined ? { tool_use_id: toolUseId } : {}),
117
123
  ...(toolInput !== undefined ? { tool_input: toolInput } : {}),
118
124
  ...(joinedInput !== undefined ? { _sema_tool_input_source: 'tool_start' } : {}),
125
+ ...(joined?.transportRedacted === true ? { _sema_tool_input_redacted: true } : {}),
119
126
  ...(deniedBy !== undefined ? { deniedBy } : {}),
120
127
  ...(joinedDeniedBy !== undefined && ledgerDeniedBy === undefined
121
128
  ? { _sema_denied_by_source: 'tool_end' }
@@ -145,7 +152,8 @@ function permissionDenialParts(stats, observed) {
145
152
  continue;
146
153
  }
147
154
  const toolName = nonEmptyStr(toolNameRaw);
148
- const joinedInput = toolInputByCallId?.has(callId) === true ? joinableToolInput(toolInputByCallId.get(callId)) : undefined;
155
+ const joined = toolInputByCallId?.has(callId) === true ? joinableToolInput(toolInputByCallId.get(callId)) : undefined;
156
+ const joinedInput = joined?.input;
149
157
  const deniedByRaw = gateDeniedByByCallId?.has(callId) === true ? gateDeniedByByCallId.get(callId) : undefined;
150
158
  const deniedBy = typeof deniedByRaw === 'string' && isGateDeniedByWord(deniedByRaw) ? deniedByRaw : undefined;
151
159
  const toolDenialKind = isCcToolDenialKind(mergedKind) ? mergedKind : undefined;
@@ -153,6 +161,7 @@ function permissionDenialParts(stats, observed) {
153
161
  ...(toolName !== undefined ? { tool_name: toolName } : {}),
154
162
  tool_use_id: callId,
155
163
  ...(joinedInput !== undefined ? { tool_input: joinedInput, _sema_tool_input_source: 'tool_start' } : {}),
164
+ ...(joined?.transportRedacted === true ? { _sema_tool_input_redacted: true } : {}),
156
165
  ...(deniedBy !== undefined ? { deniedBy, _sema_denied_by_source: 'tool_end' } : {}),
157
166
  ...(toolDenialKind !== undefined ? { toolDenialKind } : {}),
158
167
  });
@@ -522,9 +531,9 @@ export function doneToSdkResult(ev, ctx, observed) {
522
531
  ...errorBase,
523
532
  subtype: failedSubtype,
524
533
  errors: [
525
- isRewindFamilyCode(failedCode) && failedMessage
534
+ withSessionUnreadableNote(isRewindFamilyCode(failedCode) && failedMessage
526
535
  ? `${failedMessage} (${failedCode})`
527
- : (failedMessage ?? 'run failed'),
536
+ : (failedMessage ?? 'run failed'), failedCode),
528
537
  ],
529
538
  });
530
539
  }
@@ -614,9 +623,9 @@ export function failedToSdkResult(ev, ctx, observed) {
614
623
  durationMs: elapsedMs(ctx),
615
624
  errorCode: ev.errorCode,
616
625
  errors: [
617
- isRewindFamilyCode(ev.errorCode) && ev.errorMessage
626
+ withSessionUnreadableNote(isRewindFamilyCode(ev.errorCode) && ev.errorMessage
618
627
  ? `${ev.errorMessage} (${ev.errorCode})`
619
- : (ev.errorMessage ?? ev.errorCode ?? 'run failed'),
628
+ : (ev.errorMessage ?? ev.errorCode ?? 'run failed'), ev.errorCode),
620
629
  ],
621
630
  });
622
631
  }
@@ -3,7 +3,8 @@ import { eventToSdkMessage, turnEndUsage } from './downstream/eventToSdkMessage.
3
3
  import { turnUsageToModelUsage } from './downstream/turnUsageToModelUsage.js';
4
4
  import { readRunCostFacts, terminalToSdkResult, TOOL_INPUT_JOIN_MAX_CALLS } from './downstream/terminalToSdkResult.js';
5
5
  import { coerceOutput, publishSubagentContentEvent } from '../subagentContentStore.js';
6
- import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isLimitsExceededCode } from '../engineErrorCodes.js';
6
+ import { ACTIVE_RUN_BUSY_ERROR_CODE, OUTPUT_INVALID, isCorruptSessionCode, isLimitsExceededCode } from '../engineErrorCodes.js';
7
+ import { corruptSessionContent } from '../corruptSessionCopy.js';
7
8
  import { isReviewPark, readRunTerminal, runTerminalCode } from '../runTerminal.js';
8
9
  import { washCredentials } from '../displayUntrusted.js';
9
10
  import { ccToolDenialKindForToolEnd, gateDeniedBy, gateOutcomeOf } from '../gateOutcome.js';
@@ -474,7 +475,10 @@ async function* runStreamInner(events, ctx, handle = {}) {
474
475
  ?
475
476
  `${MODEL_OUTPUT_ERROR_PREFIX}: ${errText}${maxTokHint}`
476
477
  : `API Error: ${errText}${maxTokHint}`;
477
- yield syntheticTerminalRow(ctx, rowText);
478
+ const sessionUnreadable = ev.type === 'failed'
479
+ ? isCorruptSessionCode(ev.errorCode)
480
+ : doneTerminal?.kind === 'failed' && isCorruptSessionCode(doneTerminal.code);
481
+ yield syntheticTerminalRow(ctx, sessionUnreadable ? `${rowText}\n${corruptSessionContent()}` : rowText);
478
482
  }
479
483
  if (ev.type === 'done' && isReviewPark(doneTerminal) && ctx.emitChrome) {
480
484
  try {
@@ -0,0 +1,50 @@
1
+ import type { AgentClient, SessionBackgroundStopOutcome } from '@sema-agent/sdk';
2
+ import type { EngineWireInjection } from '../engineWireSdk.js';
3
+ export type EngineSessionBackgroundStopClient = {
4
+ sessions: Pick<AgentClient['sessions'], 'backgroundStop'>;
5
+ };
6
+ export type EngineSessionBackgroundStopWire = EngineWireInjection<EngineSessionBackgroundStopClient>;
7
+ export interface EngineSessionBackgroundStopOptions {
8
+ wire?: EngineSessionBackgroundStopWire;
9
+ signal?: AbortSignal;
10
+ sessionKey?: string;
11
+ }
12
+ export interface EngineSessionBackgroundReceipt {
13
+ readonly id: string;
14
+ readonly outcome: SessionBackgroundStopOutcome | (string & {});
15
+ }
16
+ export type EngineSessionBackgroundStopUnavailableWhy = 'bad_input' | 'no_wire' | 'capability_unknown' | 'no_exit_face';
17
+ export type EngineSessionBackgroundStopRefusedWhy = 'credentials_required' | 'not_permitted' | 'ownership_required' | 'session_not_found' | 'route_absent' | 'bad_request' | 'rate_limited' | 'other';
18
+ export type EngineSessionBackgroundStopResult = {
19
+ kind: 'stopped';
20
+ receipts?: readonly EngineSessionBackgroundReceipt[];
21
+ unreadableReceipts?: number;
22
+ } | {
23
+ kind: 'unavailable';
24
+ why: EngineSessionBackgroundStopUnavailableWhy;
25
+ detail: string;
26
+ } | {
27
+ kind: 'refused';
28
+ why: EngineSessionBackgroundStopRefusedWhy;
29
+ detail: string;
30
+ status?: number;
31
+ errorCode?: string;
32
+ retryAfterMs?: number;
33
+ } | {
34
+ kind: 'error';
35
+ detail: string;
36
+ status?: number;
37
+ errorCode?: string;
38
+ unconfirmed?: true;
39
+ };
40
+ export declare function stopEngineSessionBackground(sessionId: string, body: {
41
+ readonly includeRetained: boolean;
42
+ }, opts?: EngineSessionBackgroundStopOptions): Promise<EngineSessionBackgroundStopResult>;
43
+ export type EngineSessionBackgroundReceiptState = 'stopped' | 'still_running' | 'may_be_running';
44
+ export declare const ENGINE_SESSION_BACKGROUND_RECEIPT_STATES: readonly EngineSessionBackgroundReceiptState[];
45
+ export interface EngineSessionBackgroundReceiptReading {
46
+ readonly state: EngineSessionBackgroundReceiptState;
47
+ readonly text: string;
48
+ readonly unrecognizedOutcome?: string;
49
+ }
50
+ export declare function engineSessionBackgroundReceiptReadingOf(outcome: string): EngineSessionBackgroundReceiptReading;
@@ -0,0 +1,291 @@
1
+ import { engineCapValue } from '../engineCapsCache.js';
2
+ import { isStatusDerivedCoarseCode } from '../engineErrorCodes.js';
3
+ import { clientHasVerbs, engineWireFor, INJECTED_WIRE_SETTLE_MS, noAnswerDetailFrom, sendOnce, settleInjected } from '../engineWireFor.js';
4
+ import { engineWireDebugEnabled } from '../engineWireTarget.js';
5
+ import { hostLog } from '../host.js';
6
+ import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
7
+ const NOTHING_SENT = ' — nothing sent';
8
+ const DETAIL_BAD_SESSION = `the session id must be a non-empty string${NOTHING_SENT}`;
9
+ const DETAIL_BAD_INCLUDE_RETAINED = `includeRetained must be true or false (the engine has no default for it)${NOTHING_SENT}`;
10
+ const DETAIL_BAD_OPTIONS = `the options could not be read${NOTHING_SENT}`;
11
+ const DETAIL_BAD_ARGUMENTS = `the arguments could not be read${NOTHING_SENT}`;
12
+ const DETAIL_BAD_SESSION_KEY = `the session key must be a non-empty string${NOTHING_SENT}`;
13
+ const DETAIL_BAD_SIGNAL = `the signal must be an AbortSignal${NOTHING_SENT}`;
14
+ const DETAIL_INJECTED_UNUSABLE = `the connection supplied by the host cannot send this request (no usable sessions.backgroundStop)${NOTHING_SENT}`;
15
+ const DETAIL_NO_TARGET = `no engine connection is installed for this session${NOTHING_SENT}`;
16
+ const DETAIL_NO_CLIENT = `an engine client could not be built from the installed connection${NOTHING_SENT}`;
17
+ const DETAIL_CAPS_KEY_REQUIRED = `capability evidence for background.exitFaces is not available under the key the host named (capsBaseUrl)${NOTHING_SENT}`;
18
+ const DETAIL_CAPS_UNOBSERVED = `the engine capabilities have not been read for this connection yet — wait for the capability probe to settle, then call again${NOTHING_SENT}`;
19
+ const DETAIL_NO_EXIT_FACE = `this engine does not offer the session background stop (capabilities.background.exitFaces is not true); background tasks may keep running${NOTHING_SENT}`;
20
+ function refusedDetail(why) {
21
+ switch (why) {
22
+ case 'credentials_required':
23
+ return 'this engine does not accept stop requests until it is configured with a service credential';
24
+ case 'not_permitted':
25
+ return 'this caller is not permitted to stop the background tasks of this session';
26
+ case 'ownership_required':
27
+ return 'this engine has no session ownership face, so it cannot stop session background tasks';
28
+ case 'session_not_found':
29
+ return 'the engine does not know this session';
30
+ case 'route_absent':
31
+ return 'this engine has no session background stop route';
32
+ case 'bad_request':
33
+ return 'the engine rejected the request shape';
34
+ case 'rate_limited':
35
+ return 'the engine is rate-limiting stop requests — try again later';
36
+ case 'other':
37
+ return 'the engine refused the stop';
38
+ default: {
39
+ const unreachable = why;
40
+ return unreachable;
41
+ }
42
+ }
43
+ }
44
+ function note(msg) {
45
+ try {
46
+ hostLog('debug', `[session-background] stop: ${msg}`);
47
+ return true;
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }
53
+ function debugOn() {
54
+ try {
55
+ return engineWireDebugEnabled();
56
+ }
57
+ catch {
58
+ return false;
59
+ }
60
+ }
61
+ function unavailable(why, detail) {
62
+ note(`unavailable (${why})`);
63
+ return { kind: 'unavailable', why, detail };
64
+ }
65
+ function usableStopClient(client) {
66
+ return clientHasVerbs(client, [['sessions', 'backgroundStop']]);
67
+ }
68
+ function exitFaceGate(capsKey) {
69
+ try {
70
+ const v = engineCapValue(capsKey, 'background');
71
+ if (v.kind === 'unobserved')
72
+ return { why: 'capability_unknown', detail: DETAIL_CAPS_UNOBSERVED };
73
+ if (v.kind !== 'value')
74
+ return { why: 'no_exit_face', detail: DETAIL_NO_EXIT_FACE };
75
+ const bg = v.value;
76
+ if (typeof bg !== 'object' || bg === null || !Object.hasOwn(bg, 'exitFaces'))
77
+ return { why: 'no_exit_face', detail: DETAIL_NO_EXIT_FACE };
78
+ return bg.exitFaces === true ? null : { why: 'no_exit_face', detail: DETAIL_NO_EXIT_FACE };
79
+ }
80
+ catch {
81
+ return { why: 'capability_unknown', detail: DETAIL_CAPS_UNOBSERVED };
82
+ }
83
+ }
84
+ function readReceipt(v) {
85
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
86
+ return undefined;
87
+ const id = Object.hasOwn(v, 'id') ? v.id : undefined;
88
+ const outcome = Object.hasOwn(v, 'outcome') ? v.outcome : undefined;
89
+ if (typeof id !== 'string' || id.length === 0 || typeof outcome !== 'string' || outcome.length === 0)
90
+ return undefined;
91
+ return { id, outcome };
92
+ }
93
+ function readStopped(body) {
94
+ if (typeof body !== 'object' || body === null || !Object.hasOwn(body, 'receipts')) {
95
+ note('stopped (receipt list unreadable)');
96
+ return { kind: 'stopped' };
97
+ }
98
+ const raw = body.receipts;
99
+ if (!Array.isArray(raw)) {
100
+ note('stopped (receipt list unreadable)');
101
+ return { kind: 'stopped' };
102
+ }
103
+ const receipts = [];
104
+ let unreadable = 0;
105
+ for (const r of raw) {
106
+ const got = readReceipt(r);
107
+ if (got === undefined)
108
+ unreadable++;
109
+ else
110
+ receipts.push(got);
111
+ }
112
+ if (unreadable > 0)
113
+ note(`stopped (${unreadable} receipt(s) unreadable)`);
114
+ else if (debugOn())
115
+ note(`stopped (${receipts.length} receipt(s))`);
116
+ return { kind: 'stopped', receipts, ...(unreadable > 0 ? { unreadableReceipts: unreadable } : {}) };
117
+ }
118
+ function refusedWhyOf(rawCode, status) {
119
+ const code = rawCode !== undefined && status !== undefined && isStatusDerivedCoarseCode(rawCode) ? undefined : rawCode;
120
+ if (code !== undefined) {
121
+ if (code === 'auth.service_token_required')
122
+ return 'credentials_required';
123
+ if (code === 'capability.session_ownership_required')
124
+ return 'ownership_required';
125
+ if (code === 'not_found.session')
126
+ return 'session_not_found';
127
+ if (code === 'not_found.route')
128
+ return 'route_absent';
129
+ if (code === 'limit.rate_exceeded')
130
+ return 'rate_limited';
131
+ if (code.startsWith('auth.'))
132
+ return 'not_permitted';
133
+ if (code.startsWith('request.'))
134
+ return 'bad_request';
135
+ }
136
+ if (status === undefined)
137
+ return undefined;
138
+ if (status === 429)
139
+ return 'rate_limited';
140
+ if (status === 401 || status === 403)
141
+ return 'not_permitted';
142
+ if (status === 400)
143
+ return 'bad_request';
144
+ if (status >= 400 && status < 500)
145
+ return 'other';
146
+ return undefined;
147
+ }
148
+ function diagOf(e) {
149
+ const o = e;
150
+ const rawStatus = o.status ?? o.statusCode;
151
+ const status = typeof rawStatus === 'number' && Number.isInteger(rawStatus) ? rawStatus : undefined;
152
+ const errorCode = typeof o.errorCode === 'string' && o.errorCode.length > 0 ? o.errorCode : undefined;
153
+ const ra = o.retryAfterMs;
154
+ const retryAfterMs = typeof ra === 'number' && Number.isFinite(ra) && ra >= 0 ? ra : undefined;
155
+ const message = typeof o.message === 'string' && o.message.length > 0 ? o.message : undefined;
156
+ return {
157
+ ...(status !== undefined ? { status } : {}),
158
+ ...(errorCode !== undefined ? { errorCode } : {}),
159
+ ...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
160
+ ...(message !== undefined ? { message } : {}),
161
+ };
162
+ }
163
+ function classifyFailure(e) {
164
+ try {
165
+ if (typeof e !== 'object' || e === null) {
166
+ note('error (non-object failure)');
167
+ return { kind: 'error', detail: `the stop request failed: ${String(e).slice(0, 200)}` };
168
+ }
169
+ const d = diagOf(e);
170
+ if (d.status === undefined && d.errorCode === undefined) {
171
+ const noAnswer = noAnswerDetailFrom(e);
172
+ if (noAnswer !== null) {
173
+ note('error (no answer — the stop may have taken effect)');
174
+ return { kind: 'error', detail: noAnswer, unconfirmed: true };
175
+ }
176
+ }
177
+ const diag = { ...(d.status !== undefined ? { status: d.status } : {}), ...(d.errorCode !== undefined ? { errorCode: d.errorCode } : {}) };
178
+ const bits = [d.status !== undefined ? `status=${d.status}` : '', d.errorCode !== undefined ? `errorCode=${d.errorCode}` : ''].filter((b) => b.length > 0).join(' ');
179
+ const why = refusedWhyOf(d.errorCode, d.status);
180
+ if (why !== undefined) {
181
+ note(`refused (${why})${bits.length > 0 ? ` ${bits}` : ''}`);
182
+ return {
183
+ kind: 'refused',
184
+ why,
185
+ detail: `${refusedDetail(why)}${bits.length > 0 ? ` (${bits})` : ''}`,
186
+ ...diag,
187
+ ...(why === 'rate_limited' && d.retryAfterMs !== undefined ? { retryAfterMs: d.retryAfterMs } : {}),
188
+ };
189
+ }
190
+ note(`error${bits.length > 0 ? ` ${bits}` : ''}`);
191
+ return { kind: 'error', detail: (d.message ?? 'the stop request failed').slice(0, 300), ...diag };
192
+ }
193
+ catch {
194
+ note('error (the failure could not be read)');
195
+ return { kind: 'error', detail: 'the stop request failed (the error could not be read)' };
196
+ }
197
+ }
198
+ export async function stopEngineSessionBackground(sessionId, body, opts) {
199
+ let includeRetained;
200
+ let wireIn;
201
+ let signal;
202
+ let sessionKey;
203
+ try {
204
+ if (typeof sessionId !== 'string' || sessionId.length === 0)
205
+ return unavailable('bad_input', DETAIL_BAD_SESSION);
206
+ const flag = typeof body === 'object' && body !== null && Object.hasOwn(body, 'includeRetained') ? body.includeRetained : undefined;
207
+ if (typeof flag !== 'boolean')
208
+ return unavailable('bad_input', DETAIL_BAD_INCLUDE_RETAINED);
209
+ includeRetained = flag;
210
+ if (opts !== undefined && opts !== null && typeof opts !== 'object')
211
+ return unavailable('bad_input', DETAIL_BAD_OPTIONS);
212
+ wireIn = opts?.wire;
213
+ const sig = opts?.signal;
214
+ if (sig !== undefined && sig !== null && (typeof sig !== 'object' || typeof sig.addEventListener !== 'function' || typeof sig.aborted !== 'boolean')) {
215
+ return unavailable('bad_input', DETAIL_BAD_SIGNAL);
216
+ }
217
+ signal = sig ?? undefined;
218
+ const key = opts?.sessionKey;
219
+ if (key !== undefined && (typeof key !== 'string' || key.length === 0))
220
+ return unavailable('bad_input', DETAIL_BAD_SESSION_KEY);
221
+ sessionKey = key ?? DEFAULT_SESSION_KEY;
222
+ }
223
+ catch {
224
+ return unavailable('bad_input', DETAIL_BAD_ARGUMENTS);
225
+ }
226
+ let resolved;
227
+ try {
228
+ resolved = engineWireFor(wireIn, sessionKey, usableStopClient);
229
+ }
230
+ catch {
231
+ return unavailable('no_wire', wireIn !== undefined ? DETAIL_INJECTED_UNUSABLE : DETAIL_NO_TARGET);
232
+ }
233
+ if (resolved.kind === 'injected-unusable')
234
+ return unavailable('no_wire', DETAIL_INJECTED_UNUSABLE);
235
+ if (resolved.kind === 'no-target')
236
+ return unavailable('no_wire', DETAIL_NO_TARGET);
237
+ if (resolved.kind === 'injected' && resolved.capsBaseUrl === undefined)
238
+ return unavailable('capability_unknown', DETAIL_CAPS_KEY_REQUIRED);
239
+ const gate = exitFaceGate(resolved.kind === 'injected' ? resolved.capsBaseUrl : resolved.target.baseUrl);
240
+ if (gate !== null)
241
+ return unavailable(gate.why, gate.detail);
242
+ let client;
243
+ try {
244
+ client = resolved.kind === 'injected' ? resolved.client : resolved.make();
245
+ }
246
+ catch {
247
+ client = null;
248
+ }
249
+ if (client === null)
250
+ return unavailable('no_wire', DETAIL_NO_CLIENT);
251
+ const stopBody = { includeRetained };
252
+ const stopOnce = (s) => client.sessions.backgroundStop(sessionId, stopBody, s !== undefined ? { signal: s } : undefined);
253
+ let answered;
254
+ try {
255
+ answered = resolved.kind === 'injected' ? await settleInjected(INJECTED_WIRE_SETTLE_MS, signal, stopOnce) : await sendOnce(signal, () => stopOnce(signal));
256
+ }
257
+ catch (e) {
258
+ return classifyFailure(e);
259
+ }
260
+ try {
261
+ return readStopped(answered);
262
+ }
263
+ catch {
264
+ note('stopped (receipt list unreadable)');
265
+ return { kind: 'stopped' };
266
+ }
267
+ }
268
+ const RECEIPT_STATE_TEXT = {
269
+ stopped: 'Stopped.',
270
+ still_running: 'Still running — it was set to keep running, so it was left in place.',
271
+ may_be_running: 'May still be running — the engine could not confirm that it ended.',
272
+ };
273
+ export const ENGINE_SESSION_BACKGROUND_RECEIPT_STATES = Object.freeze(Object.keys(RECEIPT_STATE_TEXT));
274
+ function receiptReading(state) {
275
+ return { state, text: RECEIPT_STATE_TEXT[state] };
276
+ }
277
+ export function engineSessionBackgroundReceiptReadingOf(outcome) {
278
+ if (typeof outcome !== 'string')
279
+ return receiptReading('may_be_running');
280
+ switch (outcome) {
281
+ case 'killed':
282
+ case 'already_gone':
283
+ return receiptReading('stopped');
284
+ case 'retained_skipped':
285
+ return receiptReading('still_running');
286
+ case 'no_pgid':
287
+ return receiptReading('may_be_running');
288
+ default:
289
+ return { ...receiptReading('may_be_running'), unrecognizedOutcome: outcome };
290
+ }
291
+ }
@@ -1,4 +1,5 @@
1
1
  import { type EngineProbeOpts, type WireAuthTokenSource } from './engineWireSdk.js';
2
+ import type { ReasoningEffort } from './effortWire.js';
2
3
  export declare const MAX_TASK_AGENTS = 32;
3
4
  export declare const MAX_AGENT_NAME_CHARS = 64;
4
5
  export declare const MAX_AGENT_TEXT_CHARS = 4096;
@@ -8,7 +9,7 @@ export declare const MAX_AGENT_TOOL_NAME_CHARS = 128;
8
9
  export declare const MAX_AGENT_SKILL_CONTENT_CHARS = 1048576;
9
10
  export declare const MAX_AGENT_MAX_TURNS = 1000;
10
11
  export declare const TASK_AGENT_WIRE_FIELDS: readonly ["name", "whenToUse", "whenToUseLean", "allowTools", "denyTools", "skills", "background", "isolation", "model", "thinking", "systemPrompt", "maxTurns", "memory", "observer", "observerMessage"];
11
- export type WireThinkingLevel = 'off' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
12
+ export type WireThinkingLevel = ReasoningEffort;
12
13
  export type TaskAgentWireSkill = {
13
14
  name: string;
14
15
  description: string;
@@ -1,6 +1,7 @@
1
1
  import { hostEnv } from './hostEnv.js';
2
2
  import { makeEngineWireClient } from './engineWireSdk.js';
3
3
  import { frozenSet } from './frozenSet.js';
4
+ import { settleWithinDeadline } from './abortableSleep.js';
4
5
  export const MAX_TASK_AGENTS = 32;
5
6
  export const MAX_AGENT_NAME_CHARS = 64;
6
7
  export const MAX_AGENT_TEXT_CHARS = 4_096;
@@ -173,6 +174,13 @@ export function projectAgentsForWire(defs, deps) {
173
174
  return { agents: out.length > 0 ? out : undefined, warnings };
174
175
  }
175
176
  export async function engineSupportsTaskAgents(baseUrl, opts) {
177
+ if (opts?.client !== undefined) {
178
+ const injected = opts.client;
179
+ if (typeof injected?.capabilities !== 'function')
180
+ return undefined;
181
+ const got = await settleWithinDeadline(opts.timeoutMs ?? 3000, (signal) => injected.capabilities({ signal }));
182
+ return got.kind === 'value' ? got.value?.taskAgents === true : undefined;
183
+ }
176
184
  const client = makeEngineWireClient({
177
185
  baseUrl,
178
186
  ...(opts?.authToken ? { token: opts.authToken } : {}),
@@ -1,5 +1,6 @@
1
1
  import { MCP_CAPS } from './mcpWireCaps.js';
2
2
  import { toCappedSpec } from './skillsWireCaps.js';
3
+ import { TIER_ORDER } from './model/tierVocabulary.js';
3
4
  const asRec = (v) => (typeof v === 'object' && v !== null && !Array.isArray(v) ? v : undefined);
4
5
  const asArr = (v) => (Array.isArray(v) ? v : []);
5
6
  const asStr = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
@@ -127,7 +128,7 @@ function withoutRegistryAnnotations(e) {
127
128
  }
128
129
  return out;
129
130
  }
130
- const TIER_WORDS = Object.freeze(['ultra', 'max', 'pro', 'flash', 'lite']);
131
+ const TIER_WORDS = TIER_ORDER;
131
132
  const TIER_GROUP_NAME = /^[a-z0-9][a-z0-9._-]*$/i;
132
133
  const setOwn = (o, k, v) => {
133
134
  Object.defineProperty(o, k, { value: v, enumerable: true, writable: true, configurable: true });
@@ -0,0 +1,3 @@
1
+ export declare function isCorruptSessionError(err: unknown): boolean;
2
+ export declare function isCorruptSessionResult(result: unknown): boolean;
3
+ export declare function isCorruptSessionRunRow(row: unknown): boolean;
@@ -0,0 +1,29 @@
1
+ import { isCorruptSessionCode } from './engineErrorCodes.js';
2
+ import { effectiveWireErrorCode } from './wireErrorTriage.js';
3
+ import { readRunTerminal } from './runTerminal.js';
4
+ export function isCorruptSessionError(err) {
5
+ return isCorruptSessionCode(effectiveWireErrorCode(err));
6
+ }
7
+ export function isCorruptSessionResult(result) {
8
+ try {
9
+ const read = readRunTerminal(result);
10
+ return read !== null && read.kind === 'failed' && isCorruptSessionCode(read.code);
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ export function isCorruptSessionRunRow(row) {
17
+ if (typeof row !== 'object' || row === null || Array.isArray(row))
18
+ return false;
19
+ try {
20
+ const read = readRunTerminal(row.result);
21
+ if (read !== null)
22
+ return read.kind === 'failed' && isCorruptSessionCode(read.code);
23
+ const column = row.errorCode;
24
+ return typeof column === 'string' && isCorruptSessionCode(column);
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
@@ -0,0 +1 @@
1
+ export declare function corruptSessionContent(): string;
@@ -0,0 +1,4 @@
1
+ export function corruptSessionContent() {
2
+ return ("This session's saved history cannot be read — its stored log is damaged — so the engine cannot open this session. " +
3
+ 'Retrying will not help: whoever operates this engine has to repair or remove that stored log. Until then, continue in a new session.');
4
+ }
@@ -1 +1,2 @@
1
- export declare function decideFailureNote(err: unknown): string;
1
+ import { type ParkedResumeStartupDecision } from './resumeRefusalCopy.js';
2
+ export declare function decideFailureNote(err: unknown, decision?: ParkedResumeStartupDecision): string;