@sema-agent/client-core 0.95.0 → 0.97.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 (233) hide show
  1. package/CHANGELOG.md +269 -0
  2. package/README.md +61 -31
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/toolCards.d.ts +1 -0
  5. package/dist/adapt/toolCards.js +16 -3
  6. package/dist/adapt/wireShapes.js +2 -16
  7. package/dist/adapter/activeRunSelfHeal.d.ts +4 -2
  8. package/dist/adapter/activeRunSelfHeal.js +32 -6
  9. package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
  10. package/dist/adapter/downstream/terminalToSdkResult.js +14 -11
  11. package/dist/adapter/runStream.js +30 -47
  12. package/dist/agentSession/backgroundView.js +2 -15
  13. package/dist/agentSession/engineAgentRegistryRead.js +5 -4
  14. package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
  15. package/dist/agentsWireCaps.d.ts +1 -1
  16. package/dist/agentsWireCaps.js +29 -12
  17. package/dist/approvalDecidedByCapability.d.ts +2 -0
  18. package/dist/approvalDecidedByCapability.js +0 -1
  19. package/dist/approvalsStreamLiveCapability.d.ts +2 -0
  20. package/dist/approvalsStreamLiveCapability.js +3 -2
  21. package/dist/attachmentSeatCapability.d.ts +2 -0
  22. package/dist/attachmentSeatCapability.js +0 -1
  23. package/dist/callerCwdCapability.d.ts +2 -4
  24. package/dist/callerCwdCapability.js +0 -7
  25. package/dist/centerWiringCapability.d.ts +2 -4
  26. package/dist/centerWiringCapability.js +4 -9
  27. package/dist/classifierVerdictWire.js +4 -16
  28. package/dist/clientSlice.js +1 -1
  29. package/dist/cloudConfigWireCaps.d.ts +4 -4
  30. package/dist/cloudConfigWireCaps.js +241 -85
  31. package/dist/coreValuePorts.d.ts +11 -7
  32. package/dist/coreValuePorts.js +5 -8
  33. package/dist/credentialProvenance.d.ts +28 -0
  34. package/dist/credentialProvenance.js +76 -0
  35. package/dist/decideFailureNote.js +3 -1
  36. package/dist/decideReceipt.js +7 -10
  37. package/dist/declared/supportFloorDeclared.js +3 -3
  38. package/dist/deviceExecutorManagementCapability.d.ts +2 -0
  39. package/dist/deviceExecutorManagementCapability.js +3 -2
  40. package/dist/displayUntrusted.d.ts +10 -3
  41. package/dist/displayUntrusted.js +371 -60
  42. package/dist/engineCapReader.d.ts +8 -2
  43. package/dist/engineCapReader.js +15 -7
  44. package/dist/engineCapsCache.d.ts +17 -3
  45. package/dist/engineCapsCache.js +268 -43
  46. package/dist/engineDirectory.d.ts +79 -0
  47. package/dist/engineDirectory.js +186 -0
  48. package/dist/engineErrorCodes.d.ts +17 -1
  49. package/dist/engineErrorCodes.js +46 -16
  50. package/dist/engineNoticeCodes.d.ts +17 -0
  51. package/dist/engineNoticeCodes.js +54 -1
  52. package/dist/executionLaneCapability.d.ts +2 -0
  53. package/dist/executionLaneCapability.js +3 -2
  54. package/dist/fileHistoryCaptureCapability.d.ts +16 -0
  55. package/dist/fileHistoryCaptureCapability.js +98 -2
  56. package/dist/fileHistoryLegNotice.js +4 -0
  57. package/dist/fleet/bgSubagentRow.d.ts +15 -0
  58. package/dist/fleet/bgSubagentRow.js +67 -0
  59. package/dist/fleet/fleetLedger.d.ts +1 -0
  60. package/dist/fleet/fleetLedger.js +164 -126
  61. package/dist/fleet/fleetProjection.d.ts +1 -0
  62. package/dist/fleet/fleetProjection.js +10 -3
  63. package/dist/fleetTaskDesc.js +4 -17
  64. package/dist/gateOutcome.d.ts +12 -1
  65. package/dist/gateOutcome.js +56 -1
  66. package/dist/gateVocabulary.d.ts +2 -2
  67. package/dist/gateVocabulary.js +4 -3
  68. package/dist/generated/engineAskFaceTables.js +1 -1
  69. package/dist/generated/engineFactTables.d.ts +5 -3
  70. package/dist/generated/engineFactTables.js +17 -4
  71. package/dist/generated/engineNoticeTables.js +12 -3
  72. package/dist/generated/sdkFactTables.d.ts +2 -0
  73. package/dist/generated/sdkFactTables.js +7 -0
  74. package/dist/generated/settingsSchemaFns.d.ts +3 -0
  75. package/dist/generated/settingsSchemaFns.js +53 -0
  76. package/dist/generated/toolNameTables.d.ts +1 -1
  77. package/dist/generated/toolNameTables.js +1 -42
  78. package/dist/hitl/approvalDecisionNoteAudit.js +2 -2
  79. package/dist/hitl/approvalEndingCause.d.ts +2 -0
  80. package/dist/hitl/approvalEndingCause.js +26 -21
  81. package/dist/hitl/approvalHolds.d.ts +16 -1
  82. package/dist/hitl/approvalHolds.js +52 -12
  83. package/dist/hitl/askGateWire.js +1 -0
  84. package/dist/hitl/askParkRowRouting.js +1 -1
  85. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  86. package/dist/hitl/automaticAnswerVerdict.js +13 -10
  87. package/dist/hitl/decisionConflict.js +4 -5
  88. package/dist/hitl/decisionRefusal.d.ts +8 -0
  89. package/dist/hitl/decisionRefusal.js +55 -14
  90. package/dist/hitl/frameRouter.d.ts +3 -1
  91. package/dist/hitl/frameRouter.js +58 -39
  92. package/dist/hitl/gateLedger.d.ts +0 -5
  93. package/dist/hitl/gateLedger.js +0 -21
  94. package/dist/hitl/hitlBridge.d.ts +6 -2
  95. package/dist/hitl/hitlBridge.js +46 -11
  96. package/dist/hitl/localAllowRule.d.ts +1 -0
  97. package/dist/hitl/localAllowRule.js +2 -2
  98. package/dist/hitl/noAllowCause.d.ts +2 -1
  99. package/dist/hitl/noAllowCause.js +3 -2
  100. package/dist/hitl/parkResolver.d.ts +1 -0
  101. package/dist/hitl/parkResolver.js +14 -56
  102. package/dist/hitl/parkRowBirthWait.d.ts +1 -0
  103. package/dist/hitl/parkRowBirthWait.js +2 -2
  104. package/dist/hitl/planReviewWire.d.ts +1 -0
  105. package/dist/hitl/planReviewWire.js +98 -20
  106. package/dist/hitl/replayedApproval.d.ts +3 -3
  107. package/dist/hitl/replayedApproval.js +21 -16
  108. package/dist/hitl/sessionPolicyDeliverable.d.ts +0 -1
  109. package/dist/hitl/sessionPolicyDeliverable.js +17 -17
  110. package/dist/hitl/sessionPolicyWire.d.ts +2 -0
  111. package/dist/hitl/sessionPolicyWire.js +15 -3
  112. package/dist/hitl/standingRows.d.ts +19 -0
  113. package/dist/hitl/standingRows.js +148 -0
  114. package/dist/hitl/suspendedReopen.js +2 -1
  115. package/dist/hitl/toolApprovalWire.d.ts +22 -10
  116. package/dist/hitl/toolApprovalWire.js +286 -47
  117. package/dist/hookMatcherVerdict.d.ts +1 -9
  118. package/dist/hookMatcherVerdict.js +21 -61
  119. package/dist/hooksWireCaps.js +2 -2
  120. package/dist/hostCallbackGuard.d.ts +1 -0
  121. package/dist/hostCallbackGuard.js +6 -0
  122. package/dist/index.d.ts +26 -12
  123. package/dist/index.js +20 -10
  124. package/dist/liveModelCatalog.d.ts +13 -0
  125. package/dist/liveModelCatalog.js +56 -0
  126. package/dist/mcpProbeCapability.d.ts +2 -4
  127. package/dist/mcpProbeCapability.js +3 -8
  128. package/dist/mcpWireCaps.d.ts +1 -0
  129. package/dist/mcpWireCaps.js +4 -1
  130. package/dist/memoryComplianceCapability.d.ts +2 -4
  131. package/dist/memoryComplianceCapability.js +3 -8
  132. package/dist/memoryOriginCapability.d.ts +2 -4
  133. package/dist/memoryOriginCapability.js +3 -8
  134. package/dist/memorySpecWire.js +2 -2
  135. package/dist/modelIdentityResolvability.d.ts +2 -2
  136. package/dist/modelIdentityResolvability.js +14 -5
  137. package/dist/noticePresentation.d.ts +68 -0
  138. package/dist/noticePresentation.js +247 -0
  139. package/dist/outputStyleSeatCapability.d.ts +2 -1
  140. package/dist/outputStyleSeatCapability.js +0 -4
  141. package/dist/ownKey.d.ts +2 -0
  142. package/dist/ownKey.js +18 -0
  143. package/dist/peerFrames.js +13 -9
  144. package/dist/peerLaneCapability.d.ts +2 -0
  145. package/dist/peerLaneCapability.js +3 -2
  146. package/dist/permissionModesCapability.d.ts +2 -4
  147. package/dist/permissionModesCapability.js +0 -7
  148. package/dist/permissionRuleIssue.js +0 -3
  149. package/dist/permissionRulesWriteCapability.d.ts +2 -0
  150. package/dist/permissionRulesWriteCapability.js +3 -2
  151. package/dist/permissionsAllowGrantCapability.d.ts +2 -0
  152. package/dist/permissionsAllowGrantCapability.js +4 -2
  153. package/dist/permissionsDeclaration.d.ts +2 -0
  154. package/dist/permissionsDeclaration.js +72 -59
  155. package/dist/printPermissionOutcome.d.ts +12 -0
  156. package/dist/printPermissionOutcome.js +50 -0
  157. package/dist/printToolResultFrame.d.ts +1 -0
  158. package/dist/printToolResultFrame.js +5 -9
  159. package/dist/promptBlockedCopy.d.ts +5 -0
  160. package/dist/promptBlockedCopy.js +35 -0
  161. package/dist/protocolSegment.d.ts +9 -6
  162. package/dist/protocolSegment.js +31 -21
  163. package/dist/providerRejection.d.ts +4 -0
  164. package/dist/providerRejection.js +75 -14
  165. package/dist/replayBody.d.ts +12 -0
  166. package/dist/replayBody.js +25 -0
  167. package/dist/request/taskRequest.d.ts +1 -4
  168. package/dist/request/taskRequest.js +74 -19
  169. package/dist/resumeRefusalCopy.js +9 -25
  170. package/dist/retryStatus.d.ts +9 -1
  171. package/dist/retryStatus.js +9 -2
  172. package/dist/rewindArchiveCapability.d.ts +3 -13
  173. package/dist/rewindArchiveCapability.js +12 -58
  174. package/dist/rewindArchiveReading.d.ts +17 -0
  175. package/dist/rewindArchiveReading.js +55 -0
  176. package/dist/ruleCompileVerdict.d.ts +9 -1
  177. package/dist/ruleCompileVerdict.js +29 -4
  178. package/dist/runTerminal.d.ts +48 -5
  179. package/dist/runTerminal.js +215 -12
  180. package/dist/sandboxWire.js +13 -0
  181. package/dist/sdkWireTransit.d.ts +2 -1
  182. package/dist/sdkWireTransit.js +1 -0
  183. package/dist/sealedKeyCapability.d.ts +2 -4
  184. package/dist/sealedKeyCapability.js +0 -7
  185. package/dist/seam.js +4 -2
  186. package/dist/seatContract.d.ts +76 -11
  187. package/dist/seatContract.js +222 -27
  188. package/dist/sessionPolicyCapability.d.ts +2 -0
  189. package/dist/sessionPolicyCapability.js +3 -2
  190. package/dist/sessionReadRedactionCapability.d.ts +2 -4
  191. package/dist/sessionReadRedactionCapability.js +0 -7
  192. package/dist/sessionSyncFileHistory.d.ts +4 -0
  193. package/dist/sessionSyncFileHistory.js +66 -0
  194. package/dist/sessionSyncFileHistoryCapability.d.ts +2 -1
  195. package/dist/sessionSyncFileHistoryCapability.js +3 -5
  196. package/dist/skillToolListsCapability.d.ts +2 -1
  197. package/dist/skillToolListsCapability.js +3 -5
  198. package/dist/sqlEngineCapability.d.ts +2 -0
  199. package/dist/sqlEngineCapability.js +4 -3
  200. package/dist/steerPriorityCapability.d.ts +2 -1
  201. package/dist/steerPriorityCapability.js +3 -5
  202. package/dist/storePostureCapability.d.ts +2 -4
  203. package/dist/storePostureCapability.js +4 -9
  204. package/dist/subagent/engineSubagentSteer.js +7 -5
  205. package/dist/subagentContentStore.js +4 -15
  206. package/dist/supportFloor.d.ts +1 -0
  207. package/dist/supportFloor.js +13 -9
  208. package/dist/systemReminderTag.d.ts +1 -0
  209. package/dist/systemReminderTag.js +8 -0
  210. package/dist/toolHistoryBadTurn.js +3 -2
  211. package/dist/toolHistoryMismatch.d.ts +0 -3
  212. package/dist/toolHistoryMismatch.js +3 -7
  213. package/dist/toolResult.d.ts +12 -1
  214. package/dist/toolResult.js +55 -1
  215. package/dist/toolResultContent.d.ts +2 -0
  216. package/dist/toolResultContent.js +63 -0
  217. package/dist/usageAnalytics.js +40 -16
  218. package/dist/userInvokedSkillsCapability.d.ts +2 -1
  219. package/dist/userInvokedSkillsCapability.js +3 -5
  220. package/dist/webSearchBackendCapability.d.ts +2 -0
  221. package/dist/webSearchBackendCapability.js +3 -2
  222. package/dist/wireErrorTriage.d.ts +2 -0
  223. package/dist/wireErrorTriage.js +91 -38
  224. package/dist/wireFailureShape.d.ts +20 -3
  225. package/dist/wireFailureShape.js +128 -72
  226. package/dist/workflowsJournalCapability.d.ts +2 -0
  227. package/dist/workflowsJournalCapability.js +3 -2
  228. package/dist/writeProtectionCapability.d.ts +2 -0
  229. package/dist/writeProtectionCapability.js +3 -2
  230. package/docs/INTEGRATION-CLIENTS.md +1784 -20
  231. package/package.json +5 -5
  232. package/dist/engineRefusedToolName.d.ts +0 -1
  233. package/dist/engineRefusedToolName.js +0 -8
@@ -18,6 +18,28 @@ function providerStatusTier(status) {
18
18
  return UNTIERED;
19
19
  }
20
20
  const EDITABLE_TIER = 'invalid_request';
21
+ export const PROVIDER_REJECTION_KINDS = Object.freeze(['schema_invalid', 'auth', 'rate_limited', 'resend_eligible', 'unknown']);
22
+ const SCHEMA_INVALID_CODES = new Set([
23
+ 'invalid_type',
24
+ 'missing_required_parameter',
25
+ 'unknown_parameter',
26
+ 'unsupported_parameter',
27
+ 'invalid_function_parameters',
28
+ 'invalid_json_schema',
29
+ ]);
30
+ function schemaTierStatus(status) {
31
+ return status === 400 || status === 422;
32
+ }
33
+ function kindOfStatus(status, code) {
34
+ const tier = providerStatusTier(status);
35
+ if (tier === 'authentication_failed')
36
+ return 'auth';
37
+ if (tier === 'rate_limit')
38
+ return 'rate_limited';
39
+ if (tier === EDITABLE_TIER)
40
+ return code !== undefined && schemaTierStatus(status) && SCHEMA_INVALID_CODES.has(code) ? 'schema_invalid' : 'resend_eligible';
41
+ return 'unknown';
42
+ }
21
43
  const NOT_BY_EDITING_TAIL = new Map([
22
44
  ['authentication_failed', ': check the provider credentials or access for this account.'],
23
45
  ['model_not_found', ': the model is not available from this provider — choose another model.'],
@@ -30,26 +52,40 @@ function toolHistoryMismatchCarrier(carrier) {
30
52
  }
31
53
  const PROVIDER_REJECTED = 'provider_rejected';
32
54
  const RESEND_TEXT_STATUS = /\(HTTP (\d{3})\)/;
55
+ function ownDataOf(o, key) {
56
+ const d = Object.getOwnPropertyDescriptor(o, key);
57
+ return d !== undefined && Object.hasOwn(d, 'value') ? d.value : undefined;
58
+ }
33
59
  export function readProviderRejectionResendKey(v) {
34
60
  try {
35
61
  if (typeof v !== 'object' || v === null || Array.isArray(v))
36
62
  return undefined;
37
- const got = v;
38
- const resend = got.resend;
39
- const text = got.text;
63
+ const resend = ownDataOf(v, 'resend');
64
+ const text = ownDataOf(v, 'text');
65
+ const kind = ownDataOf(v, 'kind');
40
66
  if (typeof text !== 'string' || (resend !== 'edit_and_resend' && resend !== 'not_by_editing'))
41
67
  return undefined;
42
68
  const hit = RESEND_TEXT_STATUS.exec(text);
43
69
  if (hit === null || hit[1] === undefined)
44
70
  return undefined;
45
- const again = providerRejectionResend({ kind: PROVIDER_REJECTED, status: Number(hit[1]) });
46
- return again !== undefined && again.resend === resend && again.text === text ? { resend: again.resend, text: again.text } : undefined;
71
+ const rej = readRejection({ kind: PROVIDER_REJECTED, status: Number(hit[1]) });
72
+ if (rej === undefined)
73
+ return undefined;
74
+ let wanted;
75
+ if (kind === undefined)
76
+ wanted = kindOfStatus(rej.status, undefined);
77
+ else if (typeof kind === 'string' && PROVIDER_REJECTION_KINDS.includes(kind))
78
+ wanted = kind;
79
+ else
80
+ return undefined;
81
+ const again = verdictOf(rej.status, wanted);
82
+ return again !== undefined && again.resend === resend && again.text === text ? { resend: again.resend, text: again.text, kind: again.kind } : undefined;
47
83
  }
48
84
  catch {
49
85
  return undefined;
50
86
  }
51
87
  }
52
- export function providerRejectionResend(detail, carrier) {
88
+ function readRejection(detail) {
53
89
  let read;
54
90
  try {
55
91
  const r = readRunTerminal({ terminal: { kind: 'failed', detail } });
@@ -63,16 +99,41 @@ export function providerRejectionResend(detail, carrier) {
63
99
  const status = Object.hasOwn(read, 'status') ? read.status : undefined;
64
100
  if (status === undefined)
65
101
  return undefined;
66
- if (toolHistoryMismatchCarrier(carrier))
102
+ return { status, code: Object.hasOwn(read, 'code') ? read.code : undefined };
103
+ }
104
+ function schemaInvalidText(status) {
105
+ return `The model provider rejected the structure of this request (HTTP ${status}) — rewording the message will not fix this. Check that the configured model and endpoint accept this kind of request, or report the problem to whoever runs the engine.`;
106
+ }
107
+ function verdictOf(status, kind) {
108
+ if (kind === 'schema_invalid') {
109
+ if (providerStatusTier(status) !== EDITABLE_TIER || !schemaTierStatus(status))
110
+ return undefined;
111
+ return { resend: 'not_by_editing', text: schemaInvalidText(status), kind };
112
+ }
113
+ if (kind !== kindOfStatus(status, undefined))
67
114
  return undefined;
68
- const tier = providerStatusTier(status);
69
- return tier === EDITABLE_TIER
70
- ? {
115
+ if (kind === 'resend_eligible') {
116
+ return {
71
117
  resend: 'edit_and_resend',
72
118
  text: `The model provider rejected this request as sent (HTTP ${status}). Change it — for example shorten it or remove an attachment — and send it again.`,
73
- }
74
- : {
75
- resend: 'not_by_editing',
76
- text: `The model provider refused this request (HTTP ${status})${NOT_BY_EDITING_TAIL.get(tier) ?? NOT_BY_EDITING_TAIL.get(UNTIERED) ?? ''}`,
119
+ kind,
77
120
  };
121
+ }
122
+ return {
123
+ resend: 'not_by_editing',
124
+ text: `The model provider refused this request (HTTP ${status})${NOT_BY_EDITING_TAIL.get(providerStatusTier(status)) ?? NOT_BY_EDITING_TAIL.get(UNTIERED) ?? ''}`,
125
+ kind,
126
+ };
127
+ }
128
+ export function providerRejectionResend(detail, carrier) {
129
+ const r = readRejection(detail);
130
+ if (r === undefined)
131
+ return undefined;
132
+ if (toolHistoryMismatchCarrier(carrier))
133
+ return undefined;
134
+ return verdictOf(r.status, kindOfStatus(r.status, r.code));
135
+ }
136
+ export function providerRejectionKind(detail) {
137
+ const r = readRejection(detail);
138
+ return r === undefined ? 'unknown' : kindOfStatus(r.status, r.code);
78
139
  }
@@ -0,0 +1,12 @@
1
+ export interface ReplayRowView {
2
+ readonly status?: string;
3
+ readonly errorCode?: string;
4
+ readonly error?: string;
5
+ readonly sessionId?: string;
6
+ }
7
+ export interface DoneResultView {
8
+ readonly replay: boolean;
9
+ readonly engine: unknown;
10
+ readonly row?: ReplayRowView;
11
+ }
12
+ export declare function doneResultView(result: unknown): DoneResultView;
@@ -0,0 +1,25 @@
1
+ function guarded(o, k) {
2
+ try {
3
+ return o[k];
4
+ }
5
+ catch {
6
+ return undefined;
7
+ }
8
+ }
9
+ function guardedString(o, k) {
10
+ const v = guarded(o, k);
11
+ return typeof v === 'string' && v.length > 0 ? v : undefined;
12
+ }
13
+ export function doneResultView(result) {
14
+ if (typeof result !== 'object' || result === null || Array.isArray(result))
15
+ return { replay: false, engine: result };
16
+ if (guarded(result, 'replay') !== true)
17
+ return { replay: false, engine: result };
18
+ const row = {};
19
+ for (const k of ['status', 'errorCode', 'error', 'sessionId']) {
20
+ const v = guardedString(result, k);
21
+ if (v !== undefined)
22
+ row[k] = v;
23
+ }
24
+ return { replay: true, engine: guarded(result, 'result'), row };
25
+ }
@@ -36,6 +36,7 @@ export interface TaskRequestInput {
36
36
  attachments?: Record<string, unknown>;
37
37
  attachmentIds?: readonly string[];
38
38
  excludeTools?: readonly string[];
39
+ allowTools?: readonly string[];
39
40
  finalVerification?: boolean;
40
41
  limits?: Record<string, unknown>;
41
42
  interactiveTools?: false;
@@ -57,10 +58,6 @@ export interface TaskRequestInput {
57
58
  disableSkills?: TaskRequest['disableSkills'];
58
59
  rewind?: Record<string, unknown>;
59
60
  repo?: string;
60
- council?: boolean;
61
- debate?: boolean;
62
- rounds?: number;
63
- lenses?: readonly string[];
64
61
  settings?: {
65
62
  resolved?: Record<string, unknown>;
66
63
  webSearch?: Record<string, unknown>;
@@ -21,10 +21,6 @@ export const REQUEST_FIELD_MATRIX = [
21
21
  { field: 'settings.hooks', lanes: ['interactive', 'print'], live: true, why: '[495]① 用户 settings 文件 hooks 逐字上 wire', whyUtilityLane: '无座:hook 挂在工具门上(零工具面没有门),而且 side-channel 是壳自己的提交,不该继承用户那份 hook 表' },
22
22
  { field: 'settings.<resolved>', lanes: ['interactive', 'print'], live: true, why: 'settings resolver 的 effective 快照(hooks/env/model + 已解析权限投影)。两条车道同源:交互面 replEntry / print 面 printModeEngine 各跑一遍同一个 resolver,产物经同一个 buildWireSettingsStamp 投影。开放集(子键不可枚举)⇒ unregisteredRequestKeys 放行本车道 settings 的**表外**子键,但表内异车道子键仍点名', whyUtilityLane: '无座:这一位是**开放集 spread**(子键原样即 wire 键),摊开的是用户 settings 的已解析面(权限 / env / 模型)。side-channel 是壳自己的提交:摊上去既无用(零工具面没有工具门求值权限)又把用户的配置面带进了一条他没参与的提交。🔴 无座 ⇒ 本车道整只快照不产。🔴 **本行无座的前提由构造期校验兜住**:剥掉的是用户显式的 deny / ask,所以 `refuseUtilityWithoutToolUnload` 强制这条车道必须带字面 `excludeAllTools: true` —— 否则产出会是一条「既没卸工具、又没带用户 deny」的请求(比无人值守车道同一份输入更宽)' },
23
23
  { field: 'repo', lanes: ['interactive', 'print'], live: true, why: 'scan/code-review 场景的仓库坐标(passthrough;server 是权威校验方,本层不铸第二判官)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
24
- { field: 'council', lanes: ['interactive', 'print'], live: true, why: 'code-review council 形开关(server 读 `=== true`;非 true 不 stamp=与缺席同义,发小的那个)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
25
- { field: 'debate', lanes: ['interactive', 'print'], live: true, why: 'council 的 debate 变体开关(server 读 `=== true`)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
26
- { field: 'rounds', lanes: ['interactive', 'print'], live: true, why: 'debate 轮数(server clamp [1,3],非有限数被 server 归 undefined——本层原样透传不预 clamp)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
27
- { field: 'lenses', lanes: ['interactive', 'print'], live: true, why: 'code-review 镜头数(server 场景按数字读、非数字回 400;本层型面仍按旧形声明为字符串数组 —— 已知不一致,随该族退役处理)', whyUtilityLane: '无座:场景键;side-channel 不走场景' },
28
24
  { field: 'memoryCapture', lanes: ['interactive', 'print', 'utility'], live: true, why: '本会话永远不进长期记忆的**声明形**(server §12.4 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryWrite:false 不是一个轴(那是「本 run 只读平面」)。⚠️ parked 续跑腿结构性拿不到声明(决策/续跑端点从持久 body 重建 spec),那是引擎侧事实,不是本层的漏', whyUtilityLane: '有座:side-channel 提交是壳自己的一次性问答,**绝不该**进用户的长期记忆 —— 这一位正是那句声明,且它是收紧方向,带上去只会更严' },
29
25
  { field: 'memory', lanes: ['interactive', 'print', 'utility'], live: true, why: '这一次运行不挂记忆面的**请求级**开关(server §12.7 body 键,单成员闭集 "off");每条车道都是提交面 ⇒ 都能声明。与 memoryCapture(采集面)不是一个轴、可并存;与 memoryWrite:false(只读平面)也不是一个轴', whyUtilityLane: '有座:side-channel 提交不需要挂记忆面(不挂记忆段 / 索引 / 三只记忆工具),省 token 且零记忆写入;与 memoryCapture 不是一个轴,两条都带' },
30
26
  { field: 'fileHistory', lanes: ['interactive', 'print'], live: true, why: '「这一次运行别捕获文件历史」的**请求级声明**(server 7.104.0 提交体键,单成员闭集 "disabled";`POST /v1/tasks` · `/v1/tasks/stream` · `/v1/runs` 同收,子代继承)。两条用户车道都是提交面、都跑在用户的文件上 ⇒ 都能声明;「本会话不捕获」= 每个提交都带(续跑腿重放同一个体)。🔴 只在能力读数 `on-optional` 时发(老 server 对表外键 400),值由片段口 `fileHistoryDeclarationField` 唯一铸出。与 rewind 还原键同交 ⇒ 上游 400 `request.field_conflict`', whyUtilityLane: '无座:side-channel 强制零工具面(`excludeAllTools`),不会改任何文件 ⇒ 没有可捕获的东西;带上它只会在不认识这一键的老 server 上给这条提交多一种 400' },
@@ -41,6 +37,7 @@ export const REQUEST_FIELD_MATRIX = [
41
37
  { field: 'maxTokens', lanes: ['print', 'utility'], live: true, why: '整任务 token 预算上限声明(sdk `TaskRequest.maxTokens`,请求体**顶层**位,与 `maxCostUsd` 同属上游那一族「调用方要求的上限」:向下夹到部署上限 —— 只许要得更少)。🔴 座位集与 `maxCostUsd` 那一行**逐字相同**:同一条预算轴的两种计价单位,分开定座就是让其中一条静默漂。🔴 坏值(非数 / 非有限 / 非正 / 非整)在本层**响亮拒**:上游对这一位的坏值是安静按「调用方没给」处理并回落部署上限,不是 4xx —— 部署没设上限时这一次运行就没有 token 闸,而声明人不会知道(见 `refuseBadMaxTokens`)', whyUtilityLane: '有座:与 maxCostUsd 同轴孪生 —— 小预算是本车道的定义之一,两种计价单位都该能声明' },
42
38
  { field: 'attachmentIds', lanes: ['interactive'], live: true, why: '附件**字节通道**(uploadAttachment 回执 id;server ≥1.289 绑定会话并把文件物化进 run 的 attachments/)。有上传入口的端才有 id 可引用:web/desktop 有,壳走 images / 本地文件路径,`-p` 车道连 uploadAttachment 都没有 ⇒ print 缺席是**没有来源**,不是漏。🔴 与上面的 `attachments` 同名不同物(那是 turn 边界的配置键),两条永远不许合并', whyUtilityLane: '无座:预上传附件句柄要先有上传入口才有 id 可引用,而 side-channel 没有上传面' },
43
39
  { field: 'excludeTools', lanes: ['interactive', 'print', 'utility'], live: true, why: 'design/161 W3(2026-07-30):真 wire 键(SDK types `excludeTools`)且早在 seatContract `START_SESSION_OPTION_KEYS` 里,却是 desktop 手写 taskReq 的第 9 键、矩阵外——而 stamp 门对未登记键**静默丢弃**,真实危险形=「用户显式排除的工具被静默放回」(权限方向回归,类型层不报)。今天只有 desktop 在发;壳的工具面走 roster/interactiveTools 另一条路。0.77.0 补 print 座(三问:① wire 位与车道无关 —— sdk 具名位、提交体真收 `body.excludeTools`;② 来源 —— print 今天没有排除入口,但这是**收紧方向**的声明,传了只进回执 = 端不读回执就把用户排除的工具放回;③ 口径 —— 仍是 live 门后的位,与交互面同一行)', whyUtilityLane: '有座:逐名排除是卸载声明旁边的第二条收紧声明(两键同带各自生效)。⚠️ 它**不是** excludeAllTools 的降级替身 —— 本车道强制带卸载声明(见 `refuseUtilityWithoutToolUnload`),不认那一键的上游会把整条提交拒掉,那时正解是换车道或不发' },
40
+ { field: 'allowTools', lanes: ['interactive', 'print'], live: true, why: '引擎的可用性选择器(收紧方向的声明:只给点名的工具;`[]` = 什么都不给,照发)。两条用户车道都是提交面、都有「只给这几只工具」的用户入口(终端 `--tools`)⇒ 都有座;坏形(不是串数组)在装配期响亮拒,不静默删键', whyUtilityLane: '无座:side-channel 恒带 excludeAllTools(零工具面),可用性选择器在零工具面上没有可选的东西' },
44
41
  { field: 'excludeAllTools', lanes: ['interactive', 'print', 'utility'], live: true, why: '0.78.0(sdk 11.x 具名位 `excludeAllTools`,server ≥7.90.0 请求面):整只工具面卸载 —— 调用方工具 / 脚手架 / 手 / MCP / A2A / skills 一个都不挂,终局必然纯文本(wire 上 `wiring_manifest.tools.count === 0`,整条流零 tool_start / tool_end / tool_disclosure)。与 excludeTools 是两种语义、刻意不折叠(`"*"` 是一个合法的工具名,「卸全部」不能写成 `excludeTools:["*"]`);两键同带各自生效。类型只收字面 `true`(server 只把 literal true 写上任务规格,`false` = 替引擎重申它自己的缺省 ⇒ 不发、与缺席同义、不进回执);坏值响亮拒(收紧方向的声明不许被打字错误安静丢掉)。每条车道都是提交面 ⇒ 都能声明。探测靠键闭集、没有能力位:没接线的 server(≤7.89.x)400 `request.body_shape` 并在 `unknownKeys` 里逐字点名,本层不替它猜;A2A 入站面不承载本键(零工具面走 skill 路由)', whyUtilityLane: '有座,而且**本车道强制**:零工具面就是这条车道的定义 —— side-channel 提交整只工具面卸载,终局必然纯文本。🔴 缺这一位(或给 `false`)⇒ 构造期响亮拒(`refuseUtilityWithoutToolUnload`):本车道上一整片「无座」的理由都压在这个前提上,其中 `settings.<resolved>` 那一行剥掉的是用户显式的 deny / ask ——前提不成立时产出会是一条「既没卸工具、又没带用户 deny」的请求' },
45
42
  { field: 'settings.ultracode', lanes: ['interactive'], live: true, why: 'design/111:sticky `/effort ultracode` 拨盘 + 当轮关键词嗅探,两个来源都只在交互面存在', gap: true, whyUtilityLane: '无座:sticky 拨盘与当轮关键词嗅探两个来源都只在交互面' },
46
43
  { field: 'systemPrompt', lanes: ['interactive', 'print', 'utility'], live: false, why: '整段替换系统提示(sdk `TaskRequest.systemPrompt`;上游字符数超限 400 响亮拒,本层原样透传)。三条车道都有来源:交互面 = CC QueryParams.systemPrompt,无人值守面 = `--system-prompt[-file]` 一类旗,side-channel 面 = 壳整段给出的那一份。与追加位 `appendSystemPrompt` 不是一个位(那一位归 live 兜底层,见 `LIVE_DEFAULT_FIELDS`)', whyUtilityLane: '有座:side-channel 提交的系统提示由壳整段给出 —— 它不是用户会话的续谈,不该继承用户那一份' },
@@ -175,10 +172,7 @@ const INPUT_KEY_ROUTES = {
175
172
  disableSkills: row('disableSkills'),
176
173
  rewind: row('resumeAt/resumeAtMode/restoreFiles/rewindFilesTo'),
177
174
  repo: row('repo'),
178
- council: row('council'),
179
- debate: row('debate'),
180
- rounds: row('rounds'),
181
- lenses: row('lenses'),
175
+ allowTools: row('allowTools'),
182
176
  settings: { via: 'settings' },
183
177
  };
184
178
  const SETTINGS_KEY_ROUTES = {
@@ -234,8 +228,6 @@ const rewindSubKeys = () => {
234
228
  return r.via === 'matrix' ? r.field.split('/') : [];
235
229
  };
236
230
  const VALUE_LEVEL_IN_PLAY = Object.freeze({
237
- council: (v) => v === true,
238
- debate: (v) => v === true,
239
231
  oneShot: (v) => v === true,
240
232
  interactiveTools: (v) => v === false,
241
233
  images: (v) => Array.isArray(v) && v.length > 0,
@@ -481,6 +473,66 @@ const withoutRetiredAttachmentsKeys = (v) => {
481
473
  delete copy[key];
482
474
  return copy;
483
475
  };
476
+ const ALLOW_TOOLS_MAX_LENGTH = 65_536;
477
+ const ALLOW_TOOLS_UNREADABLE_ITEM = Symbol('allowTools-unreadable-item');
478
+ function allowToolsSnapshot(v) {
479
+ if (!Array.isArray(v))
480
+ return v;
481
+ let n;
482
+ try {
483
+ n = Object.getOwnPropertyDescriptor(v, 'length')?.value;
484
+ }
485
+ catch {
486
+ return ALLOW_TOOLS_UNREADABLE_ITEM;
487
+ }
488
+ if (typeof n !== 'number' || !Number.isSafeInteger(n) || n < 0 || n > ALLOW_TOOLS_MAX_LENGTH)
489
+ return ALLOW_TOOLS_UNREADABLE_ITEM;
490
+ const out = new Array(n);
491
+ for (let k = 0; k < n; k += 1) {
492
+ let d;
493
+ try {
494
+ d = Object.getOwnPropertyDescriptor(v, k);
495
+ }
496
+ catch {
497
+ out[k] = ALLOW_TOOLS_UNREADABLE_ITEM;
498
+ continue;
499
+ }
500
+ if (d === undefined)
501
+ continue;
502
+ out[k] = 'value' in d ? d.value : ALLOW_TOOLS_UNREADABLE_ITEM;
503
+ }
504
+ return Object.freeze(out);
505
+ }
506
+ const allowToolsCopy = (v) => {
507
+ if (!Array.isArray(v))
508
+ return [];
509
+ const out = [];
510
+ for (let k = 0; k < v.length; k += 1) {
511
+ const d = Object.getOwnPropertyDescriptor(v, k);
512
+ if (d !== undefined && 'value' in d && typeof d.value === 'string')
513
+ out.push(d.value);
514
+ }
515
+ return out;
516
+ };
517
+ const refuseBadAllowTools = (v) => {
518
+ if (v === undefined || v === null)
519
+ return;
520
+ if (Array.isArray(v)) {
521
+ let ok = true;
522
+ for (let k = 0; k < v.length; k += 1) {
523
+ const d = Object.getOwnPropertyDescriptor(v, k);
524
+ if (d === undefined || !('value' in d) || typeof d.value !== 'string') {
525
+ ok = false;
526
+ break;
527
+ }
528
+ }
529
+ if (ok)
530
+ return;
531
+ }
532
+ throw new TypeError('buildTaskRequest: allowTools 只接受字符串数组(`[]` = 什么都不给;缺席 = 不选择)—— ' +
533
+ `收到 ${coarseShape(v)}。这一位是**可用性选择器**(收紧声明):本层若把坏形静默删键,这一次运行会挂上声明人以为已经收窄掉的全部工具。` +
534
+ '错误文本只报形状,不回显值。');
535
+ };
484
536
  const refuseBadExcludeAllTools = (v) => {
485
537
  if (v === undefined || v === null || v === true || v === false)
486
538
  return;
@@ -591,7 +643,8 @@ export function parseMaxCostUsd(text) {
591
643
  return { ok: false, why: 'not_positive' };
592
644
  return { ok: true, value };
593
645
  }
594
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'fileHistory', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach'];
646
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'fileHistory', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools'];
647
+ const MATERIALIZE_AT_READ = Object.freeze({ allowTools: (v) => allowToolsSnapshot(v) });
595
648
  function dataFirstSnapshot(input, afterData) {
596
649
  let descs;
597
650
  try {
@@ -604,10 +657,10 @@ function dataFirstSnapshot(input, afterData) {
604
657
  const accessors = [];
605
658
  for (const k of Object.keys(descs)) {
606
659
  const d = descs[k];
607
- if (d === undefined || d.enumerable !== true)
660
+ if (d === undefined || (d.enumerable !== true && routeOfTopLevelKey(k) === undefined))
608
661
  continue;
609
662
  if ('value' in d)
610
- bag[k] = d.value;
663
+ bag[k] = Object.hasOwn(MATERIALIZE_AT_READ, k) ? MATERIALIZE_AT_READ[k](d.value) : d.value;
611
664
  else
612
665
  accessors.push(k);
613
666
  }
@@ -616,8 +669,10 @@ function dataFirstSnapshot(input, afterData) {
616
669
  ...ACCESSOR_READ_ORDER.filter((k) => accessors.includes(k)),
617
670
  ...accessors.filter((k) => !ACCESSOR_READ_ORDER.includes(k)),
618
671
  ];
619
- for (const k of ordered)
620
- bag[k] = Reflect.get(input, k);
672
+ for (const k of ordered) {
673
+ const v = Reflect.get(input, k);
674
+ bag[k] = Object.hasOwn(MATERIALIZE_AT_READ, k) ? MATERIALIZE_AT_READ[k](v) : v;
675
+ }
621
676
  return bag;
622
677
  }
623
678
  export function assembleTaskRequest(input, lane) {
@@ -663,6 +718,7 @@ export function assembleTaskRequest(input, lane) {
663
718
  const requireExistingSessionIn = src.requireExistingSession;
664
719
  const disableSkillsIn = src.disableSkills;
665
720
  const approvalReachIn = src.approvalReach;
721
+ const allowToolsIn = src.allowTools;
666
722
  const INPUT_KEYS = Object.keys(INPUT_KEY_ROUTES);
667
723
  const i = snapshotByName(src, INPUT_KEYS, {
668
724
  live,
@@ -677,6 +733,7 @@ export function assembleTaskRequest(input, lane) {
677
733
  requireExistingSession: requireExistingSessionIn,
678
734
  disableSkills: disableSkillsIn,
679
735
  approvalReach: approvalReachIn,
736
+ allowTools: allowToolsIn,
680
737
  settings: settingsIn,
681
738
  });
682
739
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
@@ -692,6 +749,7 @@ export function assembleTaskRequest(input, lane) {
692
749
  refuseBadRequireExistingSession(requireExistingSessionIn);
693
750
  refuseBadDisableSkills(disableSkillsIn, 'buildTaskRequest');
694
751
  refuseBadApprovalReach(approvalReachIn, 'buildTaskRequest');
752
+ refuseBadAllowTools(allowToolsIn);
695
753
  const rewindRoute = INPUT_KEY_ROUTES.rewind;
696
754
  const RETIRED_REWIND_FILES_KEY = 'rewindFiles';
697
755
  const REWIND_KEYS = rewindRoute.via === 'matrix' ? rewindRoute.field.split('/') : [];
@@ -824,10 +882,7 @@ export function assembleTaskRequest(input, lane) {
824
882
  ...(on('enableFork') ? { enableFork: i.enableFork } : {}),
825
883
  ...(on('attachments') ? { attachments: attachmentsForWire } : {}),
826
884
  ...(on('repo') ? { repo: i.repo } : {}),
827
- ...(on('council') ? { council: true } : {}),
828
- ...(on('debate') ? { debate: true } : {}),
829
- ...(on('rounds') ? { rounds: i.rounds } : {}),
830
- ...(on('lenses') ? { lenses: i.lenses } : {}),
885
+ ...(on('allowTools') ? { allowTools: allowToolsCopy(i.allowTools) } : {}),
831
886
  ...(on('attachmentIds') ? { attachmentIds: [...(i.attachmentIds ?? [])] } : {}),
832
887
  ...(on('excludeTools') ? { excludeTools: [...(i.excludeTools ?? [])] } : {}),
833
888
  ...(on('excludeAllTools') ? { excludeAllTools: true } : {}),
@@ -1,6 +1,6 @@
1
- import { readErrorNumber, readErrorString } from './wireFailureShape.js';
2
- import { readOwnDataNonEmptyString } from './ownKey.js';
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, CHECKPOINT_WALLTIME_AXIS_RETIRED, CHECKPOINT_CONFIG_REFUSED, refusalSaysStillPending, refusalRetriableReading, } from './engineErrorCodes.js';
1
+ import { readBlockedBy, readErrorNumber, readErrorString } from './wireFailureShape.js';
2
+ import { guardedRead, ownDataField, readOwnDataNonEmptyString } from './ownKey.js';
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, CHECKPOINT_WALLTIME_AXIS_RETIRED, CHECKPOINT_CONFIG_REFUSED, isRefusedParkCode, refusalSaysStillPending, refusalRetriableReading, } from './engineErrorCodes.js';
4
4
  import { capForDisplay } from './fleetTaskDesc.js';
5
5
  import { engineVersionTier } from './supportFloor.js';
6
6
  import { resumeRetryLaterFromError } from './wireErrorTriage.js';
@@ -11,7 +11,7 @@ export const RESUME_REFUSAL_CODES = Object.freeze([
11
11
  export function resumeRefusalFromError(err) {
12
12
  if (typeof err !== 'object' || err === null)
13
13
  return null;
14
- const code = err.errorCode;
14
+ const code = guardedRead(() => ownDataField(err, 'errorCode'));
15
15
  if (typeof code !== 'string' || !RESUME_REFUSAL_CODES.includes(code))
16
16
  return null;
17
17
  if (code === RESUME_PLACEMENT_MISMATCH) {
@@ -55,22 +55,7 @@ export function resumePolicyBlockFromError(err) {
55
55
  }
56
56
  if (outer !== RESUME_POLICY_FOLD_CODE)
57
57
  return null;
58
- const judge = (v) => ({
59
- blockedBy: typeof v === 'string' && v.length > 0 ? { kind: 'named', code: v } : { kind: 'unreadable' },
60
- });
61
- try {
62
- const top = o['blockedBy'];
63
- if (top !== undefined)
64
- return judge(top);
65
- const extra = o['extra'];
66
- if (typeof extra === 'object' && extra !== null && !Array.isArray(extra) && Object.hasOwn(extra, 'blockedBy')) {
67
- return judge(extra['blockedBy']);
68
- }
69
- return { blockedBy: { kind: 'absent' } };
70
- }
71
- catch {
72
- return { blockedBy: { kind: 'unreadable' } };
73
- }
58
+ return { blockedBy: readBlockedBy(o) };
74
59
  }
75
60
  export function resumePolicyBlockContent(detail) {
76
61
  const b = detail.blockedBy;
@@ -282,7 +267,6 @@ export function checkpointUnsupportedVersionContent(detail, ctx) {
282
267
  }
283
268
  return [CHECKPOINT_UNSUPPORTED_WHAT, CHECKPOINT_UNSUPPORTED_STILL_WAITING, outlet + CHECKPOINT_UNSUPPORTED_DRAIN].join(' · ');
284
269
  }
285
- const REFUSED_PARK_CODES = new Set([CHECKPOINT_UNSUPPORTED_VERSION, CHECKPOINT_WALLTIME_AXIS_RETIRED, CHECKPOINT_CONFIG_REFUSED]);
286
270
  const REFUSED_PARK_FIELD_TOKEN = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
287
271
  export function refusedParkFromError(failure) {
288
272
  if (typeof failure !== 'object' || failure === null)
@@ -290,13 +274,13 @@ export function refusedParkFromError(failure) {
290
274
  let code;
291
275
  let status;
292
276
  try {
293
- code = failure.errorCode;
294
- status = failure.status;
277
+ code = ownDataField(failure, 'errorCode');
278
+ status = ownDataField(failure, 'status');
295
279
  }
296
280
  catch {
297
281
  return null;
298
282
  }
299
- if (typeof code !== 'string' || !REFUSED_PARK_CODES.has(code))
283
+ if (typeof code !== 'string' || !isRefusedParkCode(code))
300
284
  return null;
301
285
  if (status !== undefined && status !== 409)
302
286
  return null;
@@ -305,7 +289,7 @@ export function refusedParkFromError(failure) {
305
289
  const rawField = code === CHECKPOINT_CONFIG_REFUSED ? readErrorString(failure, 'field') : undefined;
306
290
  const field = rawField !== undefined && REFUSED_PARK_FIELD_TOKEN.test(rawField) ? rawField : undefined;
307
291
  return Object.freeze({
308
- code: code,
292
+ code,
309
293
  ...(field !== undefined ? { field } : {}),
310
294
  ...(stillPending !== undefined ? { stillPending } : {}),
311
295
  });
@@ -6,6 +6,8 @@ export type RetryStatus = {
6
6
  errClass?: BrainRetryErrClass | (string & {});
7
7
  retryAtMs?: number;
8
8
  errorStatus?: number;
9
+ providerCondition?: string;
10
+ providerConditionSource?: string;
9
11
  elapsedMs?: number;
10
12
  timeoutMs?: number;
11
13
  detail?: string;
@@ -17,6 +19,8 @@ export type RetryStatus = {
17
19
  errClass?: BrainRetryErrClass | (string & {});
18
20
  retryAtMs?: number;
19
21
  errorStatus?: number;
22
+ providerCondition?: string;
23
+ providerConditionSource?: string;
20
24
  terminal?: true;
21
25
  error: RetryStatusErrorPayload;
22
26
  elapsedMs?: number;
@@ -29,6 +33,8 @@ export type RetryStatus = {
29
33
  errClass?: BrainRetryErrClass | (string & {});
30
34
  retryAtMs?: number;
31
35
  errorStatus?: number;
36
+ providerCondition?: string;
37
+ providerConditionSource?: string;
32
38
  elapsedMs?: number;
33
39
  timeoutMs?: number;
34
40
  terminal?: never;
@@ -60,7 +66,9 @@ export interface BrainStatusPayload {
60
66
  errClass?: BrainRetryErrClass | (string & {});
61
67
  elapsedMs?: number;
62
68
  timeoutMs?: number;
69
+ providerCondition?: string;
70
+ providerConditionSource?: string;
63
71
  }
64
- export declare const BRAIN_STATUS_PAYLOAD_KEYS: readonly ["phase", "detail", "retryInSec", "retryInMs", "attempt", "maxRetries", "errClass", "retryAtMs", "errorStatus", "elapsedMs", "timeoutMs"];
72
+ export declare const BRAIN_STATUS_PAYLOAD_KEYS: readonly ["phase", "detail", "retryInSec", "retryInMs", "attempt", "maxRetries", "errClass", "retryAtMs", "errorStatus", "elapsedMs", "timeoutMs", "providerCondition", "providerConditionSource"];
65
73
  export declare function mapBrainStatusToRetry(p: BrainStatusPayload, nowMs: number): RetryStatus | null;
66
74
  export {};
@@ -20,6 +20,8 @@ export const BRAIN_STATUS_PAYLOAD_KEYS = [
20
20
  'errorStatus',
21
21
  'elapsedMs',
22
22
  'timeoutMs',
23
+ 'providerCondition',
24
+ 'providerConditionSource',
23
25
  ];
24
26
  const _brainStatusKeyPin = [true, true];
25
27
  void _brainStatusKeyPin;
@@ -33,6 +35,10 @@ export function mapBrainStatusToRetry(p, nowMs) {
33
35
  const cause = typeof p.errClass === 'string' && p.errClass.length > 0 ? { errClass: p.errClass } : {};
34
36
  const producerTiming = typeof p.retryAtMs === 'number' ? { retryAtMs: p.retryAtMs } : {};
35
37
  const failureStatus = typeof p.errorStatus === 'number' ? { errorStatus: p.errorStatus } : {};
38
+ const providerJudged = {
39
+ ...(typeof p.providerCondition === 'string' && p.providerCondition.length > 0 ? { providerCondition: p.providerCondition } : {}),
40
+ ...(typeof p.providerConditionSource === 'string' && p.providerConditionSource.length > 0 ? { providerConditionSource: p.providerConditionSource } : {}),
41
+ };
36
42
  const saneMs = (v) => typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : undefined;
37
43
  const elapsedMs = saneMs(p.elapsedMs);
38
44
  const timeoutMs = saneMs(p.timeoutMs);
@@ -40,9 +46,10 @@ export function mapBrainStatusToRetry(p, nowMs) {
40
46
  ...(elapsedMs !== undefined ? { elapsedMs } : {}),
41
47
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
42
48
  };
43
- const stalledDetail = typeof p.detail === 'string' && p.detail.length > 0 ? { detail: p.detail } : {};
49
+ const stalledShown = typeof p.detail === 'string' && p.detail.length > 0 ? displayUntrusted(p.detail) : '';
50
+ const stalledDetail = stalledShown.length > 0 ? { detail: stalledShown } : {};
44
51
  const shown = (absent) => (typeof p.detail === 'string' ? displayUntrusted(p.detail) : absent);
45
- const extra = { ...counts, ...cause, ...producerTiming, ...failureStatus, ...waitProgress };
52
+ const extra = { ...counts, ...cause, ...producerTiming, ...failureStatus, ...providerJudged, ...waitProgress };
46
53
  switch (p.phase) {
47
54
  case 'recovered':
48
55
  return null;
@@ -1,24 +1,14 @@
1
+ import { type RewindArchiveReading, type RewindCodeArchive } from './rewindArchiveReading.js';
1
2
  import type { RewindMode } from './rewindWireCaps.js';
2
- export type RewindArchiveBit = 'on' | 'off' | 'not_reported' | 'malformed';
3
- export type RewindArchiveReading = {
4
- kind: 'unobserved';
5
- } | {
6
- kind: 'present';
7
- conversationAnchor: RewindArchiveBit;
8
- forkRestore: RewindArchiveBit;
9
- codeOnly: RewindArchiveBit;
10
- restoreFilesEpoch: RewindArchiveBit;
11
- };
3
+ export { projectRewindArchiveCapability, rewindConversationAvailability } from './rewindArchiveReading.js';
4
+ export type { RewindArchiveBit, RewindArchiveReading, RewindCodeArchive } from './rewindArchiveReading.js';
12
5
  export type RewindArchiveMode = 'both' | 'code';
13
- export type RewindCodeArchive = 'available' | 'unavailable' | 'unknown';
14
- export declare function projectRewindArchiveCapability(caps: unknown): RewindArchiveReading | undefined;
15
6
  export declare function noteEngineCapsForRewindArchive(baseUrl: string, caps: unknown, opts?: {
16
7
  generation?: number;
17
8
  }): void;
18
9
  export declare function observedRewindArchive(baseUrl?: string | undefined): RewindArchiveReading;
19
10
  export declare function rewindCodeArchiveAvailability(reading: RewindArchiveReading, mode: RewindArchiveMode): RewindCodeArchive;
20
11
  export declare function rewindArchiveDoctorDetail(reading: RewindArchiveReading, mode: RewindArchiveMode): string;
21
- export declare function rewindConversationAvailability(reading: RewindArchiveReading): RewindCodeArchive;
22
12
  export declare function engineRewindRefusedNow(option: RewindMode, reading?: RewindArchiveReading): boolean;
23
13
  export declare function rewindConversationDoctorDetail(reading: RewindArchiveReading): string;
24
14
  export declare function rewindRestoreFilesEpochDetail(reading: RewindArchiveReading): string;
@@ -1,55 +1,14 @@
1
1
  import { engineWireTarget } from './engineWireTarget.js';
2
- import { createEngineCapReader } from './engineCapReader.js';
3
- import { ownDataField } from './ownKey.js';
4
- import { _wireConversationRewindJudge } from './toolHistoryMismatch.js';
5
- const CAPS_KEYS = {
6
- conversationAnchor: 'resumeAt',
7
- forkRestore: 'rewindFiles',
8
- codeOnly: 'rewindFilesTo',
9
- restoreFilesEpoch: 'restoreFiles',
10
- };
11
- const _capsKeyPin = true;
12
- void _capsKeyPin;
13
- function bitOf(caps, key) {
14
- const v = ownDataField(caps, key);
15
- if (v === undefined)
16
- return 'not_reported';
17
- if (v === true)
18
- return 'on';
19
- if (v === false)
20
- return 'off';
21
- return 'malformed';
22
- }
23
- export function projectRewindArchiveCapability(caps) {
24
- try {
25
- if (caps === null || typeof caps !== 'object' || Array.isArray(caps))
26
- return undefined;
27
- return {
28
- kind: 'present',
29
- conversationAnchor: bitOf(caps, CAPS_KEYS.conversationAnchor),
30
- forkRestore: bitOf(caps, CAPS_KEYS.forkRestore),
31
- codeOnly: bitOf(caps, CAPS_KEYS.codeOnly),
32
- restoreFilesEpoch: bitOf(caps, CAPS_KEYS.restoreFilesEpoch),
33
- };
34
- }
35
- catch {
36
- return undefined;
37
- }
38
- }
39
- const reader = createEngineCapReader({
40
- name: 'rewindArchive',
41
- project: projectRewindArchiveCapability,
42
- makeUnobserved: () => ({ kind: 'unobserved' }),
43
- });
2
+ import { resetRewindArchiveReader, rewindArchiveReadingAt, rewindConversationAvailability, } from './rewindArchiveReading.js';
3
+ import { unreadableReadingDetail } from './engineCapReader.js';
4
+ export { projectRewindArchiveCapability, rewindConversationAvailability } from './rewindArchiveReading.js';
44
5
  export function noteEngineCapsForRewindArchive(baseUrl, caps, opts) {
45
- reader.note(baseUrl, caps, opts);
46
- }
47
- function conversationRewindJudge(engineBaseUrl) {
48
- return rewindConversationAvailability(observedRewindArchive(engineBaseUrl));
6
+ void baseUrl;
7
+ void caps;
8
+ void opts;
49
9
  }
50
- _wireConversationRewindJudge(conversationRewindJudge);
51
10
  export function observedRewindArchive(baseUrl = engineWireTarget()?.baseUrl) {
52
- return reader.observed(baseUrl);
11
+ return rewindArchiveReadingAt(baseUrl);
53
12
  }
54
13
  export function rewindCodeArchiveAvailability(reading, mode) {
55
14
  if (reading.kind !== 'present')
@@ -63,6 +22,8 @@ export function rewindCodeArchiveAvailability(reading, mode) {
63
22
  }
64
23
  export function rewindArchiveDoctorDetail(reading, mode) {
65
24
  const lane = mode === 'both' ? 'rewind with code restore' : 'code-only rewind';
25
+ if (reading.kind === 'unreadable')
26
+ return unreadableReadingDetail(lane);
66
27
  if (reading.kind !== 'present') {
67
28
  return `${lane} not observed — the engine reports it on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether the code archive can be restored is unknown`;
68
29
  }
@@ -86,15 +47,6 @@ export function rewindArchiveDoctorDetail(reading, mode) {
86
47
  return `${lane} reported in a shape this reader cannot read — the value is neither true nor false, so the answer is unknown, not "no"`;
87
48
  }
88
49
  }
89
- export function rewindConversationAvailability(reading) {
90
- if (reading.kind !== 'present')
91
- return 'unknown';
92
- if (reading.conversationAnchor === 'on')
93
- return 'available';
94
- if (reading.conversationAnchor === 'off')
95
- return 'unavailable';
96
- return 'unknown';
97
- }
98
50
  export function engineRewindRefusedNow(option, reading = observedRewindArchive()) {
99
51
  switch (option) {
100
52
  case 'both':
@@ -109,6 +61,8 @@ export function engineRewindRefusedNow(option, reading = observedRewindArchive()
109
61
  }
110
62
  export function rewindConversationDoctorDetail(reading) {
111
63
  const lane = 'rewind to a message';
64
+ if (reading.kind === 'unreadable')
65
+ return unreadableReadingDetail(lane);
112
66
  if (reading.kind !== 'present') {
113
67
  return `${lane} not observed — the engine reports conversation anchors on /v1/capabilities; this process has no usable capabilities reading cached for it, so whether the conversation can be rewound is unknown`;
114
68
  }
@@ -139,5 +93,5 @@ export function rewindRestoreFilesEpochDetail(reading) {
139
93
  }
140
94
  }
141
95
  export function __resetRewindArchiveReadingsForTests() {
142
- reader.__resetForTests();
96
+ resetRewindArchiveReader();
143
97
  }