@sema-agent/client-core 0.92.0 → 0.93.1

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 (125) hide show
  1. package/CHANGELOG.md +227 -0
  2. package/README.md +25 -16
  3. package/dist/adapt/arms.js +2 -0
  4. package/dist/adapt/toolCards.js +6 -4
  5. package/dist/adapt.d.ts +1 -1
  6. package/dist/adapt.js +1 -1
  7. package/dist/adapter/runStream.js +3 -1
  8. package/dist/agentsWireCaps.js +1 -1
  9. package/dist/approvalDecidedByCapability.d.ts +16 -0
  10. package/dist/approvalDecidedByCapability.js +47 -0
  11. package/dist/approvalsStreamLiveCapability.js +3 -3
  12. package/dist/askSurvivesStandingAnswer.d.ts +1 -1
  13. package/dist/centerWiringCapability.js +1 -1
  14. package/dist/cloudConfigWireCaps.js +5 -5
  15. package/dist/compensations.js +2 -2
  16. package/dist/decideReceipt.d.ts +2 -0
  17. package/dist/decideReceipt.js +34 -9
  18. package/dist/detachWire.d.ts +2 -14
  19. package/dist/detachWire.js +1 -47
  20. package/dist/deviceExecutorManagementCapability.d.ts +6 -2
  21. package/dist/deviceExecutorManagementCapability.js +61 -15
  22. package/dist/engineConfigDrift.d.ts +20 -0
  23. package/dist/engineConfigDrift.js +54 -0
  24. package/dist/engineErrorCodes.d.ts +3 -0
  25. package/dist/engineErrorCodes.js +12 -4
  26. package/dist/engineInlineTaskStats.d.ts +1 -0
  27. package/dist/engineNoticeCodes.d.ts +2 -2
  28. package/dist/engineWireFor.d.ts +22 -0
  29. package/dist/engineWireFor.js +154 -9
  30. package/dist/engineWireSdk.d.ts +0 -1
  31. package/dist/engineWireSdk.js +0 -3
  32. package/dist/executionLaneCapability.js +2 -2
  33. package/dist/fileHistoryCaptureCapability.d.ts +1 -1
  34. package/dist/fileHistoryCaptureCapability.js +1 -6
  35. package/dist/fileHistoryLegNotice.d.ts +1 -1
  36. package/dist/fileHistoryLegNotice.js +1 -6
  37. package/dist/gateVocabulary.d.ts +1 -5
  38. package/dist/gateVocabulary.js +4 -22
  39. package/dist/generated/engineFactTables.d.ts +4 -3
  40. package/dist/generated/engineFactTables.js +10 -0
  41. package/dist/generated/engineNoticeTables.d.ts +1 -1
  42. package/dist/generated/engineNoticeTables.js +1 -1
  43. package/dist/goalStopHook.d.ts +1 -8
  44. package/dist/goalStopHook.js +2 -58
  45. package/dist/headlessPermissionModeWire.d.ts +3 -2
  46. package/dist/headlessPermissionModeWire.js +46 -5
  47. package/dist/hitl/approvalResolution.d.ts +3 -2
  48. package/dist/hitl/approvalResolution.js +13 -0
  49. package/dist/hitl/askParkRowRouting.js +2 -2
  50. package/dist/hitl/frameRouter.js +7 -2
  51. package/dist/hitl/gateLedger.d.ts +1 -0
  52. package/dist/hitl/gateLedger.js +3 -0
  53. package/dist/hitl/hitlBridge.d.ts +1 -0
  54. package/dist/hitl/hitlBridge.js +6 -1
  55. package/dist/hitl/hitlHostSurface.d.ts +6 -2
  56. package/dist/hitl/hitlHostSurface.js +29 -3
  57. package/dist/hitl/parkResolver.js +22 -4
  58. package/dist/hitl/persistedRulesWire.js +29 -17
  59. package/dist/hitl/planReviewWire.d.ts +0 -2
  60. package/dist/hitl/planReviewWire.js +1 -18
  61. package/dist/hitl/sessionPolicyDeliverable.js +3 -3
  62. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  63. package/dist/hitl/sessionPolicyWire.js +7 -4
  64. package/dist/hitl/toolApprovalWire.d.ts +32 -7
  65. package/dist/hitl/toolApprovalWire.js +282 -59
  66. package/dist/hooksWireCaps.d.ts +1 -1
  67. package/dist/hooksWireCaps.js +1 -1
  68. package/dist/index.d.ts +7 -2
  69. package/dist/index.js +6 -2
  70. package/dist/inheritEnvWire.d.ts +1 -4
  71. package/dist/inheritEnvWire.js +0 -18
  72. package/dist/limitsWire.d.ts +0 -6
  73. package/dist/limitsWire.js +0 -38
  74. package/dist/mcpProbeCapability.js +1 -1
  75. package/dist/mcpProbeWire.js +10 -12
  76. package/dist/mcpReconnect.d.ts +5 -0
  77. package/dist/mcpReconnect.js +28 -0
  78. package/dist/memoryComplianceCapability.js +2 -2
  79. package/dist/memoryOriginCapability.js +2 -2
  80. package/dist/memoryVerbsWire.js +14 -13
  81. package/dist/ownKey.d.ts +2 -0
  82. package/dist/ownKey.js +15 -0
  83. package/dist/peerLaneCapability.js +2 -2
  84. package/dist/permissionModesCapability.d.ts +16 -0
  85. package/dist/permissionModesCapability.js +71 -0
  86. package/dist/permissionRuleIssue.js +1 -1
  87. package/dist/permissionRulesWriteCapability.js +2 -2
  88. package/dist/permissionWireCaps.d.ts +2 -0
  89. package/dist/permissionWireCaps.js +8 -1
  90. package/dist/permissionsAllowGrantCapability.js +7 -2
  91. package/dist/postureKnob.js +1 -1
  92. package/dist/protocolSegment.d.ts +2 -0
  93. package/dist/protocolSegment.js +3 -0
  94. package/dist/readFacePosture.js +1 -1
  95. package/dist/resumeRefusalCopy.d.ts +1 -0
  96. package/dist/resumeRefusalCopy.js +26 -3
  97. package/dist/retryStatus.js +2 -1
  98. package/dist/rewindArchiveCapability.js +2 -2
  99. package/dist/runTerminal.d.ts +16 -0
  100. package/dist/runTerminal.js +32 -3
  101. package/dist/seam.d.ts +1 -0
  102. package/dist/sessionPolicyCapability.js +1 -1
  103. package/dist/sessionSyncFileHistoryCapability.js +2 -2
  104. package/dist/skillToolListsCapability.js +1 -1
  105. package/dist/sqlEngineCapability.js +1 -1
  106. package/dist/steerPriorityCapability.js +1 -1
  107. package/dist/storePostureCapability.js +2 -2
  108. package/dist/supportFloor.d.ts +1 -0
  109. package/dist/supportFloor.js +13 -3
  110. package/dist/taskRequestWords.d.ts +4 -2
  111. package/dist/taskRequestWords.js +1 -1
  112. package/dist/toolHistoryBadTurn.js +1 -2
  113. package/dist/toolResult.d.ts +2 -1
  114. package/dist/toolResult.js +33 -3
  115. package/dist/userInvokedSkillsCapability.js +1 -1
  116. package/dist/webSearchBackendCapability.js +2 -2
  117. package/dist/webSearchWireCaps.d.ts +1 -1
  118. package/dist/webSearchWireCaps.js +40 -2
  119. package/dist/wireFailureShape.js +2 -2
  120. package/dist/wireRefusalCopy.d.ts +9 -3
  121. package/dist/wireRefusalCopy.js +41 -4
  122. package/dist/workflowsJournalCapability.js +1 -1
  123. package/dist/writeProtectionCapability.js +1 -1
  124. package/docs/INTEGRATION-CLIENTS.md +999 -15
  125. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import { readErrorNumber, readErrorString } from './wireFailureShape.js';
2
+ import { readOwnDataNonEmptyString } from './ownKey.js';
2
3
  import { RESUME_CONTEXT_UNAVAILABLE, RESUME_PLACEMENT_MISMATCH, RESUME_PREFLIGHT_REJECTED, RESUME_POLICY_FOLD_CODE, PARKED_RESUME_STARTUP_FAILED, isStatusDerivedCoarseCode, RESUME_SESSION_NOT_FOUND, REQUEST_FIELD_CONFLICT, CHECKPOINT_UNSUPPORTED_VERSION, refusalSaysStillPending, } from './engineErrorCodes.js';
3
4
  import { capForDisplay } from './fleetTaskDesc.js';
4
5
  import { resumeRetryLaterFromError } from './wireErrorTriage.js';
@@ -115,7 +116,7 @@ export function requireExistingSessionRefusalFromError(err) {
115
116
  }
116
117
  }
117
118
  const REQUIRE_EXISTING_SESSION_SENTENCES = Object.freeze({
118
- session_not_found: 'The engine did not continue this conversation: the conversation it was asked to continue does not exist there (it may have expired, been removed, or never been created on that engine), and this request asked to continue only an existing one, so no new conversation was started · Start a new conversation, or pick another one to continue',
119
+ session_not_found: 'The engine did not continue this conversation: the conversation it was asked to continue does not exist there (it may have expired, been removed, or never been created on that engine; this client cannot tell which), and this request asked to continue only an existing one, so no new conversation was started · Start a new conversation, or pick another one to continue',
119
120
  session_id_missing: 'The engine did not continue this conversation: this request asked to continue only an existing conversation but named none, so there was nothing to continue and no new conversation was started · Name the conversation to continue, or start a new one',
120
121
  });
121
122
  const REQUIRE_EXISTING_SESSION_GENERIC = 'The engine did not continue this conversation: this request asked to continue only an existing conversation and there was none it could continue, so no new conversation was started · Start a new conversation, or pick another one to continue';
@@ -158,6 +159,26 @@ export function resumeContextUnavailableContent(d) {
158
159
  third,
159
160
  ].join(' · ');
160
161
  }
162
+ function parkedResumeStartupCauseWord(err) {
163
+ let named;
164
+ try {
165
+ named = readOwnDataNonEmptyString(err, 'startupCause');
166
+ }
167
+ catch {
168
+ named = undefined;
169
+ }
170
+ if (named !== undefined)
171
+ return named;
172
+ try {
173
+ const extra = err.extra;
174
+ if (typeof extra !== 'object' || extra === null || Array.isArray(extra))
175
+ return undefined;
176
+ return readOwnDataNonEmptyString(extra, 'cause');
177
+ }
178
+ catch {
179
+ return undefined;
180
+ }
181
+ }
161
182
  export function parkedResumeStartupFromError(err, decision) {
162
183
  if (typeof err !== 'object' || err === null)
163
184
  return null;
@@ -172,10 +193,12 @@ export function parkedResumeStartupFromError(err, decision) {
172
193
  return null;
173
194
  const rawTaskId = readErrorString(err, 'taskId');
174
195
  const taskId = rawTaskId !== undefined && rawTaskId.length > 0 ? rawTaskId : undefined;
196
+ const startupCause = parkedResumeStartupCauseWord(err);
175
197
  return {
176
198
  code: PARKED_RESUME_STARTUP_FAILED,
177
199
  cause: decision === 'deny' ? 'resume_rejected' : 'undetermined',
178
200
  ...(taskId !== undefined ? { taskId } : {}),
201
+ ...(startupCause !== undefined ? { startupCause } : {}),
179
202
  resendable: false,
180
203
  denyStaysOpen: true,
181
204
  };
@@ -183,7 +206,7 @@ export function parkedResumeStartupFromError(err, decision) {
183
206
  const PARKED_RESUME_STARTUP_STATE = 'This refusal does not say whether the card is still waiting — re-fetch the pending approvals';
184
207
  const PARKED_RESUME_STARTUP_SENTENCES = Object.freeze({
185
208
  resume_rejected: [
186
- 'The engine could not start the parked work to apply this decision (its session may be gone, or the engine rejected the decision itself)',
209
+ 'The engine could not start the parked work to apply this decision (its session may be gone, or the engine rejected the decision itself; this client cannot tell which)',
187
210
  PARKED_RESUME_STARTUP_STATE,
188
211
  'If the card is still there, decide it again from the fresh list',
189
212
  ].join(' · '),
@@ -249,7 +272,7 @@ export function checkpointUnsupportedVersionContent(detail, ctx) {
249
272
  }
250
273
  else {
251
274
  outlet =
252
- 'If it was parked before this server was upgraded, submit the work again as a new task: when it still has a run waiting on it (a tool approval, a plan review or a paused resource), cancel that run first; when it was waiting to be woken up, or was a workflow step continuing its host conversation, there is no run to cancel, so start a new task in that conversation';
275
+ 'If it was parked before this server was upgraded, submit the work again as a new task (this client cannot tell which kind of parked work this is, so both ways out are given): when it still has a run waiting on it (a tool approval, a plan review or a paused resource), cancel that run first; when it was waiting to be woken up, or was a workflow step continuing its host conversation, there is no run to cancel, so start a new task in that conversation';
253
276
  }
254
277
  return [CHECKPOINT_UNSUPPORTED_WHAT, CHECKPOINT_UNSUPPORTED_STILL_WAITING, outlet + CHECKPOINT_UNSUPPORTED_DRAIN].join(' · ');
255
278
  }
@@ -47,7 +47,7 @@ export function mapBrainStatusToRetry(p, nowMs) {
47
47
  case 'reconnecting':
48
48
  return { kind: 'stalled', deadline, ...extra, ...stalledDetail };
49
49
  case 'rate_limited':
50
- return { kind: 'error', deadline, ...extra, error: { formatted: '', rateLimits: {} } };
50
+ return { kind: 'error', deadline, ...extra, error: { formatted: p.detail ?? '', rateLimits: null } };
51
51
  case 'circuit_open':
52
52
  return {
53
53
  kind: 'error',
@@ -60,6 +60,7 @@ export function mapBrainStatusToRetry(p, nowMs) {
60
60
  case 'waiting_first_token':
61
61
  return { kind: 'waiting', deadline, ...extra, error: { formatted: '' } };
62
62
  case 'retrying':
63
+ return { kind: 'error', deadline, ...extra, error: { formatted: p.detail ?? '' } };
63
64
  default:
64
65
  return { kind: 'error', deadline, ...extra, error: { formatted: '' } };
65
66
  }
@@ -77,7 +77,7 @@ export function rewindArchiveDoctorDetail(reading, mode) {
77
77
  ? `${lane} off on this deployment (no file history store) — the working tree cannot be restored here; whether the conversation alone can be rewound is a separate reading`
78
78
  : `${lane} off on this deployment — it needs both a file history store and conversation anchors, and the engine reports at least one missing (it does not say which)`;
79
79
  case 'not_reported':
80
- return `${lane} not reported by this engine — only newer engines advertise it, so this does not say whether the code archive can be restored`;
80
+ return `${lane} not reported by this engine, so this does not say whether the code archive can be restored`;
81
81
  case 'malformed':
82
82
  return `${lane} reported in a shape this reader cannot read — the value is neither true nor false, so the answer is unknown, not "no"`;
83
83
  }
@@ -102,7 +102,7 @@ export function rewindConversationDoctorDetail(reading) {
102
102
  case 'off':
103
103
  return `${lane} off on this deployment (no conversation anchors) — the conversation cannot be moved back to an earlier message here`;
104
104
  case 'not_reported':
105
- return `conversation anchors not reported by this engine — only newer engines advertise them, so this does not say whether the conversation can be rewound`;
105
+ return `conversation anchors not reported by this engine, so this does not say whether the conversation can be rewound`;
106
106
  case 'malformed':
107
107
  return `conversation anchors reported in a shape this reader cannot read — the value is neither true nor false, so the answer is unknown, not "no"`;
108
108
  }
@@ -16,6 +16,21 @@ export interface RunTerminalFailureDetail {
16
16
  status?: number;
17
17
  code?: string;
18
18
  }
19
+ export declare const PROMPT_BLOCK_CAUSES: readonly ["block", "crashed", "timeout", "cancelled"];
20
+ export type PromptBlockCause = (typeof PROMPT_BLOCK_CAUSES)[number];
21
+ export type RunTerminalBlockedDetail = {
22
+ kind: 'prompt_blocked_by_hook';
23
+ cause: PromptBlockCause;
24
+ hookReason?: string;
25
+ } | {
26
+ kind: 'prompt_blocked_by_hook';
27
+ cause: 'unrecognized';
28
+ word: string;
29
+ hookReason?: string;
30
+ } | {
31
+ kind: 'unrecognized';
32
+ word: string;
33
+ };
19
34
  export type RunTerminalRead = {
20
35
  kind: 'completed';
21
36
  plane: RunTerminalPlane;
@@ -31,6 +46,7 @@ export type RunTerminalRead = {
31
46
  plane: RunTerminalPlane;
32
47
  reason?: string;
33
48
  code?: string;
49
+ detail?: RunTerminalBlockedDetail;
34
50
  } | RunTerminalPausedRead | {
35
51
  kind: 'unknown';
36
52
  plane: RunTerminalPlane;
@@ -1,4 +1,6 @@
1
- import { CORE_TERMINAL_CAUSE_KINDS } from './generated/engineFactTables.js';
1
+ import { CORE_PROMPT_BLOCK_CAUSES, CORE_TERMINAL_CAUSE_KINDS } from './generated/engineFactTables.js';
2
+ export const PROMPT_BLOCK_CAUSES = CORE_PROMPT_BLOCK_CAUSES;
3
+ const PROMPT_BLOCKED_BY_HOOK_DETAIL_KIND = 'prompt_blocked_by_hook';
2
4
  function str(v) {
3
5
  return typeof v === 'string' && v.length > 0 ? v : undefined;
4
6
  }
@@ -20,10 +22,13 @@ function pauseOf(v) {
20
22
  ...(restoreMode !== undefined ? { restoreMode } : {}),
21
23
  };
22
24
  }
25
+ function ownValueOf(o, k) {
26
+ return Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined;
27
+ }
23
28
  const PROVIDER_REJECTED_DETAIL_KIND = 'provider_rejected';
24
29
  function failureDetailOf(cause) {
25
30
  try {
26
- const own = (o, k) => (Object.prototype.hasOwnProperty.call(o, k) ? o[k] : undefined);
31
+ const own = ownValueOf;
27
32
  const v = own(cause, 'detail');
28
33
  if (typeof v !== 'object' || v === null || Array.isArray(v))
29
34
  return undefined;
@@ -41,6 +46,29 @@ function failureDetailOf(cause) {
41
46
  return undefined;
42
47
  }
43
48
  }
49
+ function blockedDetailOf(cause) {
50
+ try {
51
+ const v = ownValueOf(cause, 'detail');
52
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
53
+ return undefined;
54
+ const kind = str(ownValueOf(v, 'kind'));
55
+ if (kind === undefined)
56
+ return undefined;
57
+ if (kind !== PROMPT_BLOCKED_BY_HOOK_DETAIL_KIND)
58
+ return { kind: 'unrecognized', word: kind };
59
+ const causeWord = str(ownValueOf(v, 'cause'));
60
+ if (causeWord === undefined)
61
+ return undefined;
62
+ const hookReason = str(ownValueOf(v, 'hookReason'));
63
+ const hook = hookReason !== undefined ? { hookReason } : {};
64
+ return PROMPT_BLOCK_CAUSES.includes(causeWord)
65
+ ? { kind: PROMPT_BLOCKED_BY_HOOK_DETAIL_KIND, cause: causeWord, ...hook }
66
+ : { kind: PROMPT_BLOCKED_BY_HOOK_DETAIL_KIND, cause: 'unrecognized', word: causeWord, ...hook };
67
+ }
68
+ catch {
69
+ return undefined;
70
+ }
71
+ }
44
72
  const FLAT_PAUSED_STATUSES = ['suspended', 'needs_review'];
45
73
  export function readRunTerminal(result) {
46
74
  if (typeof result !== 'object' || result === null || Array.isArray(result))
@@ -77,7 +105,8 @@ export function readRunTerminal(result) {
77
105
  }
78
106
  case 'blocked': {
79
107
  const reason = str(c.reason);
80
- return { kind: 'blocked', plane: 'cause', ...(reason !== undefined ? { reason } : {}) };
108
+ const detail = blockedDetailOf(c);
109
+ return { kind: 'blocked', plane: 'cause', ...(reason !== undefined ? { reason } : {}), ...(detail !== undefined ? { detail } : {}) };
81
110
  }
82
111
  case 'paused':
83
112
  return { kind: 'paused', plane: 'cause', ...(pauseOf(c) ?? {}) };
package/dist/seam.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface InlineTaskTick {
15
15
  totalTokens: number;
16
16
  durationMs: number;
17
17
  currentAction?: string;
18
+ model?: string;
18
19
  }
19
20
  export interface AdapterContext {
20
21
  sessionId: string;
@@ -34,7 +34,7 @@ export function sessionPolicyFaceAvailable(reading) {
34
34
  export function sessionPolicyDoctorDetail(reading) {
35
35
  switch (reading.kind) {
36
36
  case 'unobserved':
37
- return 'per-session rule face not observed — the engine reports it on /v1/capabilities (sessionPolicy); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
37
+ return 'per-session rule face not observed — the engine reports it on /v1/capabilities (sessionPolicy); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
38
38
  case 'not_reported':
39
39
  return 'per-session rule face not reported by this engine — this binary predates the position itself; it says nothing about whether the face exists, so read the rules rather than writing them blind';
40
40
  case 'absent':
@@ -33,9 +33,9 @@ export function sessionSyncFileHistoryAvailable(reading) {
33
33
  export function sessionSyncFileHistoryDoctorDetail(reading) {
34
34
  switch (reading.kind) {
35
35
  case 'unobserved':
36
- return 'session-sync file history not observed — the engine reports it on /v1/capabilities (sessionSyncFileHistory); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
36
+ return 'session-sync file history not observed — the engine reports it on /v1/capabilities (sessionSyncFileHistory); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
37
37
  case 'not_reported':
38
- return 'session-sync file history not reported by this engine — only newer engines advertise it separately; this does not say whether file history can be synced';
38
+ return 'session-sync file history not reported separately by this engine; this does not say whether file history can be synced';
39
39
  case 'absent':
40
40
  return 'no session-sync file history on this deployment — syncing a session moves the conversation only; edited-file history stays behind';
41
41
  case 'present':
@@ -33,7 +33,7 @@ export function skillToolListsAvailable(reading) {
33
33
  export function skillToolListsDoctorDetail(reading) {
34
34
  switch (reading.kind) {
35
35
  case 'unobserved':
36
- return 'skill tool lists not observed — the engine reports it on /v1/capabilities (skillToolLists); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
36
+ return 'skill tool lists not observed — the engine reports it on /v1/capabilities (skillToolLists); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
37
37
  case 'not_reported':
38
38
  return 'skill tool lists not reported by this engine — the engine predates them: tool lists on request skills are not read, so they do not limit any tool';
39
39
  case 'absent':
@@ -52,7 +52,7 @@ export function sqlEngineDoctorDetail(reading) {
52
52
  switch (reading.kind) {
53
53
  case 'unobserved':
54
54
  return ('not observed — the engine reports it on /v1/capabilities; this process has no usable ' +
55
- 'capabilities reading cached for it (none received, or the last one was unreadable)');
55
+ 'capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)');
56
56
  case 'not_reported':
57
57
  return 'not reported by this engine — the capability position needs a newer engine';
58
58
  case 'none':
@@ -61,7 +61,7 @@ export function steerPriorityDoctorDetail(reading) {
61
61
  return unobserved;
62
62
  }
63
63
  if (kind === 'not_reported') {
64
- return 'steer priority not reported by this engine — it predates the field or has no fleet face, so a "now" or "later" steer is not sent (steers without a priority work as before)';
64
+ return 'steer priority not reported by this engine — its capabilities reading carries no fleet.steerPriority (the fleet face is not reported, or the face carries no such field; this client cannot tell which), so a "now" or "later" steer is not sent (steers without a priority work as before)';
65
65
  }
66
66
  if (kind !== 'present' || typeof word !== 'string' || word.length === 0)
67
67
  return unobserved;
@@ -90,7 +90,7 @@ export function storePostureDoctorDetail(reading) {
90
90
  const unobserved = 'store posture not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether this deployment persists its state is unknown';
91
91
  const r = presentView(reading);
92
92
  if (r?.kind === 'not_reported') {
93
- return 'store posture not reported by this engine — only newer engines advertise it; this does not say whether this deployment persists its state';
93
+ return 'store posture not reported by this engine; this does not say whether this deployment persists its state';
94
94
  }
95
95
  if (r?.kind !== 'present')
96
96
  return unobserved;
@@ -120,7 +120,7 @@ export function storePostureDoctorDetail(reading) {
120
120
  return `${assembled} — ${lost} (degraded: the declared database could not be opened at start-up, so the engine fell back rather than refusing to start)`;
121
121
  }
122
122
  if (reason === 'local_unwritable') {
123
- return `${assembled} — ${lost} (degraded: the default local file store could not be created, for example a read-only or full data directory)`;
123
+ return `${assembled} — ${lost} (degraded: the default local file store could not be created — a read-only or full data directory is one possible cause, and this client cannot tell which applies)`;
124
124
  }
125
125
  return `${assembled} — ${lost} (degraded for a reason this client does not know yet: "${word(reason)}")`;
126
126
  }
@@ -8,3 +8,4 @@ export declare const SUPPORT_FLOOR: Readonly<SupportFloor>;
8
8
  export declare function supportFloorOf(component: unknown): string | undefined;
9
9
  export type EngineVersionTier = 'engine' | 'legacy' | 'unknown';
10
10
  export declare function engineVersionTier(version: unknown, floor?: string): EngineVersionTier;
11
+ export declare function coreVersionTier(version: unknown, floor?: string): EngineVersionTier;
@@ -25,12 +25,16 @@ function compareTriple(a, b) {
25
25
  }
26
26
  return 0;
27
27
  }
28
- export function engineVersionTier(version, floor) {
29
- let need = releaseTriple(SUPPORT_FLOOR.server);
28
+ function versionTierAgainst(version, base, floor, caller) {
29
+ const baseTriple = releaseTriple(base);
30
+ if (baseTriple === undefined) {
31
+ throw new TypeError(`${caller}: 门槛只接受纯 x.y.z 版本串(整串、无前导零、无前后缀)—— 收到 typeof=${base === null ? 'null' : typeof base}。错误文本只报形状,不回显值。`);
32
+ }
33
+ let need = baseTriple;
30
34
  if (floor !== undefined) {
31
35
  const own = releaseTriple(floor);
32
36
  if (own === undefined) {
33
- throw new TypeError(`engineVersionTier: floor 只接受纯 x.y.z 版本串(整串、无前导零、无前后缀)或缺席 —— 收到 typeof=${floor === null ? 'null' : typeof floor}。错误文本只报形状,不回显值。`);
37
+ throw new TypeError(`${caller}: floor 只接受纯 x.y.z 版本串(整串、无前导零、无前后缀)或缺席 —— 收到 typeof=${floor === null ? 'null' : typeof floor}。错误文本只报形状,不回显值。`);
34
38
  }
35
39
  if (compareTriple(own, need) > 0)
36
40
  need = own;
@@ -40,3 +44,9 @@ export function engineVersionTier(version, floor) {
40
44
  return 'unknown';
41
45
  return compareTriple(got, need) >= 0 ? 'engine' : 'legacy';
42
46
  }
47
+ export function engineVersionTier(version, floor) {
48
+ return versionTierAgainst(version, SUPPORT_FLOOR.server, floor, 'engineVersionTier');
49
+ }
50
+ export function coreVersionTier(version, floor) {
51
+ return versionTierAgainst(version, SUPPORT_FLOOR.core, floor, 'coreVersionTier');
52
+ }
@@ -1,8 +1,9 @@
1
1
  import type { TaskRequest } from '@sema-agent/sdk';
2
2
  export type TaskRequestReasoningEffort = NonNullable<TaskRequest['reasoningEffort']>;
3
3
  export declare const REASONING_EFFORT_WORDS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
4
- export type TaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
5
- export declare const PERMISSION_MODE_WORDS: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "auto"];
4
+ type SdkTaskRequestPermissionMode = NonNullable<TaskRequest['permissionMode']>;
5
+ export type TaskRequestPermissionMode = SdkTaskRequestPermissionMode | 'dontAsk';
6
+ export declare const PERMISSION_MODE_WORDS: readonly ["default", "plan", "acceptEdits", "bypassPermissions", "auto", "dontAsk"];
6
7
  export interface RetiredAttachmentsKey {
7
8
  readonly key: string;
8
9
  readonly why: string;
@@ -10,3 +11,4 @@ export interface RetiredAttachmentsKey {
10
11
  readonly remedy: string;
11
12
  }
12
13
  export declare function retiredAttachmentsKeyOf(nameOrToken: string, form?: 'token' | 'key'): RetiredAttachmentsKey | undefined;
14
+ export {};
@@ -1,5 +1,5 @@
1
1
  export const REASONING_EFFORT_WORDS = ['off', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max'];
2
- export const PERMISSION_MODE_WORDS = ['default', 'plan', 'acceptEdits', 'bypassPermissions', 'auto'];
2
+ export const PERMISSION_MODE_WORDS = ['default', 'plan', 'acceptEdits', 'bypassPermissions', 'auto', 'dontAsk'];
3
3
  const _taskRequestWordPins = [true, true, true, true];
4
4
  void _taskRequestWordPins;
5
5
  export function retiredAttachmentsKeyOf(nameOrToken, form = 'token') {
@@ -2,8 +2,7 @@ import { displayUntrusted } from './displayUntrusted.js';
2
2
  const BAD_TURN_PREVIEW_MAX = 60;
3
3
  export function toolHistoryBadTurnSentence(promptText) {
4
4
  const text = typeof promptText === 'string' ? promptText : '';
5
- const flat = stripDisplayTagBlocks(text).replace(/\s+/g, ' ').trim();
6
- const washed = displayUntrusted(flat, { foldLines: false, mark: 'dot' });
5
+ const washed = displayUntrusted(stripDisplayTagBlocks(text), { mark: 'dot' }).replace(/\s+/g, ' ').trim();
7
6
  const points = Array.from(washed);
8
7
  const preview = points.length > BAD_TURN_PREVIEW_MAX ? `${points.slice(0, BAD_TURN_PREVIEW_MAX).join('')}...` : washed;
9
8
  return preview.length > 0
@@ -100,7 +100,8 @@ export interface EngineBgShellRegistration {
100
100
  receipt: string;
101
101
  source: 'bash' | 'monitor';
102
102
  }
103
- export declare function detectEngineBgShellReceipt(toolName: string, rawInput: unknown, outputText: string | undefined): EngineBgShellRegistration | null;
103
+ export declare function detectEngineBgShellReceipt(toolName: string, rawInput: unknown, outputText: string | undefined, structured?: unknown): EngineBgShellRegistration | null;
104
+ export declare function detectEngineBgShellDetach(toolName: string, rawInput: unknown, structured: unknown, outputText: string | undefined): EngineBgShellRegistration | null;
104
105
  export declare function todoWriteToolUseResult(rawInput: unknown, oldTodos: unknown[], isError: boolean): {
105
106
  toolUseResult: unknown;
106
107
  isError: boolean;
@@ -674,7 +674,7 @@ export function structuredToToolUseResult(structured, modelText, leaderLedger =
674
674
  return null;
675
675
  }
676
676
  }
677
- export function detectEngineBgShellReceipt(toolName, rawInput, outputText) {
677
+ export function detectEngineBgShellReceipt(toolName, rawInput, outputText, structured) {
678
678
  if (typeof outputText !== 'string')
679
679
  return null;
680
680
  const input = (rawInput ?? {});
@@ -683,6 +683,8 @@ export function detectEngineBgShellReceipt(toolName, rawInput, outputText) {
683
683
  if (toolName === 'Bash') {
684
684
  if (input.run_in_background !== true)
685
685
  return null;
686
+ if (readBashDetachView(structured)?.registerFailed === true)
687
+ return null;
686
688
  m = THREE_GEN_BG_RECEIPT_RE.exec(outputText);
687
689
  source = 'bash';
688
690
  }
@@ -695,17 +697,45 @@ export function detectEngineBgShellReceipt(toolName, rawInput, outputText) {
695
697
  }
696
698
  if (!m?.[1])
697
699
  return null;
700
+ return engineBgShellRegistrationBody(m[1], input, outputText, source);
701
+ }
702
+ function engineBgShellRegistrationBody(taskId, rawInput, receipt, source) {
703
+ const input = (rawInput ?? {});
698
704
  const command = typeof input.command === 'string' ? input.command : '(engine bash)';
699
705
  const description = typeof input.description === 'string' ? input.description : undefined;
700
706
  return {
701
707
  kind: 'register',
702
- taskId: m[1],
708
+ taskId,
703
709
  command,
704
710
  ...(description !== undefined ? { description } : {}),
705
- receipt: outputText,
711
+ receipt,
706
712
  source,
707
713
  };
708
714
  }
715
+ export function detectEngineBgShellDetach(toolName, rawInput, structured, outputText) {
716
+ if (toolName !== 'Bash')
717
+ return null;
718
+ const view = readBashDetachView(structured);
719
+ if (view === null || !view.detached || view.registerFailed || view.taskId === undefined)
720
+ return null;
721
+ return engineBgShellRegistrationBody(view.taskId, rawInput, typeof outputText === 'string' ? outputText : '', 'bash');
722
+ }
723
+ function readBashDetachView(structured) {
724
+ if (typeof structured !== 'object' || structured === null)
725
+ return null;
726
+ const s = structured;
727
+ const type = s.type;
728
+ if (type !== 'bash')
729
+ return null;
730
+ const detached = s.detached;
731
+ const registerFailed = s.registerFailed;
732
+ const taskId = s.task_id;
733
+ return {
734
+ detached: detached === true,
735
+ registerFailed: registerFailed === true,
736
+ taskId: typeof taskId === 'string' && taskId.length > 0 ? taskId : undefined,
737
+ };
738
+ }
709
739
  export function todoWriteToolUseResult(rawInput, oldTodos, isError) {
710
740
  const todos = rawInput?.todos;
711
741
  if (!Array.isArray(todos))
@@ -33,7 +33,7 @@ export function userInvokedSkillsAvailable(reading) {
33
33
  export function userInvokedSkillsDoctorDetail(reading) {
34
34
  switch (reading.kind) {
35
35
  case 'unobserved':
36
- return 'user-invoked skills not observed — the engine reports it on /v1/capabilities (userInvokedSkills); this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)';
36
+ return 'user-invoked skills not observed — the engine reports it on /v1/capabilities (userInvokedSkills); this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)';
37
37
  case 'not_reported':
38
38
  return 'user-invoked skills not reported by this engine — the engine predates it, so naming skills on a request is refused (do not send the key)';
39
39
  case 'absent':
@@ -38,9 +38,9 @@ export function webSearchBackendDoctorDetail(reading) {
38
38
  switch (reading.kind) {
39
39
  case 'unobserved':
40
40
  return ('deployment default backend not observed — the engine reports it on /v1/capabilities; ' +
41
- 'this process has no usable capabilities reading cached for it (none received, or the last one was unreadable)');
41
+ 'this process has no usable capabilities reading cached for it (none received, the last one was unreadable, or the last one was discarded when the engine connection was reset; this process cannot tell which)');
42
42
  case 'not_reported':
43
- return ('deployment default backend not reported by this engine — only newer engines advertise it; ' +
43
+ return ('deployment default backend not reported by this engine; ' +
44
44
  'this does not say whether the deployment has a search backend');
45
45
  case 'none':
46
46
  return (`deployment default backend none — ${DEPLOY_ENV} is unset or not a provider word on the engine, ` +
@@ -22,7 +22,7 @@ export type WebSearchSettingsVerdict = {
22
22
  settings: WebSearchSettings;
23
23
  } | {
24
24
  kind: 'malformed';
25
- source: 'env' | 'settings';
25
+ source: 'env' | 'settings' | 'host';
26
26
  field: WebSearchSettingsField;
27
27
  issue: string;
28
28
  };
@@ -32,6 +32,37 @@ function judgeText(raw) {
32
32
  return { ok: false, issue: 'must be a string' };
33
33
  return { ok: true, value: isBlankText(raw) ? undefined : raw };
34
34
  }
35
+ const API_KEY_ISSUE = 'must be usable as an HTTP header value (it contains a character an HTTP header cannot carry, such as a line break)';
36
+ const isHttpWhitespaceCode = (c) => c === 0x09 || c === 0x0a || c === 0x0d || c === 0x20;
37
+ function isHttpHeaderValue(value) {
38
+ let start = 0;
39
+ let end = value.length;
40
+ while (start < end && isHttpWhitespaceCode(value.charCodeAt(start)))
41
+ start++;
42
+ while (end > start && isHttpWhitespaceCode(value.charCodeAt(end - 1)))
43
+ end--;
44
+ for (let i = start; i < end; i++) {
45
+ const c = value.charCodeAt(i);
46
+ if (!(c === 0x09 || (c >= 0x20 && c <= 0x7e) || (c >= 0x80 && c <= 0xff)))
47
+ return false;
48
+ }
49
+ return true;
50
+ }
51
+ function apiKeyHeaderValue(provider, key) {
52
+ switch (provider) {
53
+ case 'brave':
54
+ case 'searxng':
55
+ return key;
56
+ case 'tavily':
57
+ return `Bearer ${key}`;
58
+ }
59
+ }
60
+ function judgeApiKey(provider, raw) {
61
+ const text = judgeText(raw);
62
+ if (!text.ok || text.value === undefined)
63
+ return text;
64
+ return isHttpHeaderValue(apiKeyHeaderValue(provider, text.value)) ? text : { ok: false, issue: API_KEY_ISSUE };
65
+ }
35
66
  const ENDPOINT_ISSUE = 'must be an absolute URL with a scheme and a host, such as https://search.example.com (a bare host or host:port is not one)';
36
67
  function judgeEndpoint(raw) {
37
68
  if (raw === undefined || raw === null)
@@ -114,9 +145,10 @@ function judgeSection(raw, source, apiKeyFor) {
114
145
  injected = undefined;
115
146
  }
116
147
  }
117
- const apiKey = typeof injected === 'string' && !isBlankText(injected) ? { ok: true, value: injected } : judgeText(s.apiKey);
148
+ const useInjected = typeof injected === 'string' && !isBlankText(injected);
149
+ const apiKey = judgeApiKey(provider.value, useInjected ? injected : s.apiKey);
118
150
  if (!apiKey.ok)
119
- return { kind: 'malformed', source, field: 'apiKey', issue: apiKey.issue };
151
+ return { kind: 'malformed', source: useInjected ? 'host' : source, field: 'apiKey', issue: apiKey.issue };
120
152
  const endpoint = judgeEndpoint(s.endpoint);
121
153
  if (!endpoint.ok)
122
154
  return { kind: 'malformed', source, field: 'endpoint', issue: endpoint.issue };
@@ -191,6 +223,12 @@ function envNameOfField(field) {
191
223
  export function webSearchVerdictMessage(verdict, context = 'submit') {
192
224
  if (verdict.kind !== 'malformed')
193
225
  return undefined;
226
+ if (verdict.source === 'host') {
227
+ const head = `Web search is misconfigured: the API key your host supplied for webSearch is malformed; it ${verdict.issue}.`;
228
+ return context === 'submit'
229
+ ? `${head} The request was not sent — the key needs replacing where your host keeps it.`
230
+ : `${head} Requests will be refused until the key is replaced where your host keeps it.`;
231
+ }
194
232
  const where = verdict.source === 'env' ? envNameOfField(verdict.field) : verdict.field === 'section' ? 'the webSearch section in settings' : `webSearch.${verdict.field} in settings`;
195
233
  const fix = verdict.source === 'env' ? 'fix it or unset the SEMA_WEBSEARCH_* variables' : 'fix it or remove the webSearch section';
196
234
  return context === 'submit'
@@ -66,9 +66,9 @@ export function readErrorStrictStringList(o, key) {
66
66
  return true;
67
67
  };
68
68
  const v = readErrorSlot(o, key, strict);
69
- if (!Array.isArray(v))
70
- return undefined;
71
69
  try {
70
+ if (!Array.isArray(v))
71
+ return undefined;
72
72
  const out = [];
73
73
  const len = v.length;
74
74
  for (let i = 0; i < len; i++) {
@@ -1,4 +1,4 @@
1
- import { FLEET_LEASE_UNAVAILABLE, USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
1
+ import { FLEET_LEASE_UNAVAILABLE, SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE, USAGE_WINDOW_EXHAUSTED } from './engineErrorCodes.js';
2
2
  import type { TaskRequestLike } from './request/taskRequest.js';
3
3
  export declare const CONFLICT_APPROVAL_SETTLED = "conflict.approval_settled";
4
4
  export declare const CONFLICT_RUN_NOT_RUNNING = "conflict.run_not_running";
@@ -22,14 +22,20 @@ export interface FleetLeaseUnavailableDetail {
22
22
  }
23
23
  export declare function fleetLeaseUnavailableFromError(err: unknown): FleetLeaseUnavailableDetail | null;
24
24
  export declare function fleetLeaseUnavailableContent(detail: FleetLeaseUnavailableDetail): string;
25
- export type DenyAttributionRefusalKind = 'not_in_proof' | 'decision_conflict' | 'attribution_rejected';
25
+ export interface ScenarioAssignmentUnknownDetail {
26
+ code: typeof SCENARIO_ASSIGNMENT_UNKNOWN_ERROR_CODE;
27
+ scenario?: string;
28
+ source?: 'ruling' | 'deployment';
29
+ }
30
+ export declare function scenarioAssignmentUnknownFromError(err: unknown): ScenarioAssignmentUnknownDetail | null;
31
+ export type DenyAttributionRefusalKind = 'not_in_proof' | 'decision_conflict' | 'attribution_rejected' | 'retired_key';
26
32
  export interface DenyAttributionRefusalDetail {
27
33
  kind: DenyAttributionRefusalKind;
28
34
  code: string;
29
35
  status: number;
30
36
  field?: string;
31
37
  }
32
- export declare function denyAttributionRefusalFromError(err: unknown, sentSettledBy: boolean): DenyAttributionRefusalDetail | null;
38
+ export declare function denyAttributionRefusalFromError(err: unknown, sentAttributionKey: boolean): DenyAttributionRefusalDetail | null;
33
39
  export declare function denyAttributionRefusalContent(detail: Pick<DenyAttributionRefusalDetail, 'kind' | 'field'>): string;
34
40
  export declare const CONFLICT_SESSION_CHILDREN_ALIVE = "conflict.session_children_alive";
35
41
  export type SessionDeleteConflictKind = 'children_alive' | 'active_run' | 'unrecognized';