@sema-agent/client-core 0.99.0 → 0.100.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 (128) hide show
  1. package/CHANGELOG.md +177 -0
  2. package/README.md +32 -19
  3. package/dist/adapt/arms.js +60 -36
  4. package/dist/adapt/panelTasks.d.ts +2 -1
  5. package/dist/adapt/panelTasks.js +32 -9
  6. package/dist/adapt/textStream.d.ts +1 -1
  7. package/dist/adapt/textStream.js +51 -28
  8. package/dist/adapter/activeRunSelfHeal.d.ts +7 -0
  9. package/dist/adapter/activeRunSelfHeal.js +58 -11
  10. package/dist/adapter/downstream/eventToSdkMessage.d.ts +25 -6
  11. package/dist/adapter/downstream/eventToSdkMessage.js +125 -9
  12. package/dist/adapter/downstream/terminalToSdkResult.js +10 -0
  13. package/dist/adapter/downstream/wiringManifestView.d.ts +3 -1
  14. package/dist/adapter/downstream/wiringManifestView.js +1 -0
  15. package/dist/adapter/runStream.js +32 -5
  16. package/dist/adapter/types.d.ts +2 -0
  17. package/dist/agentsWireCaps.d.ts +7 -0
  18. package/dist/agentsWireCaps.js +100 -15
  19. package/dist/cloudConfigWireCaps.d.ts +10 -1
  20. package/dist/cloudConfigWireCaps.js +156 -62
  21. package/dist/compactionStatus.d.ts +34 -0
  22. package/dist/compactionStatus.js +196 -0
  23. package/dist/decideFailureNote.js +3 -3
  24. package/dist/displayUntrusted.js +150 -5
  25. package/dist/engineAgentPanelStore.d.ts +7 -4
  26. package/dist/engineErrorCodes.d.ts +9 -1
  27. package/dist/engineErrorCodes.js +9 -5
  28. package/dist/engineRowRef.js +1 -0
  29. package/dist/engineToolDirectory.d.ts +3 -0
  30. package/dist/engineToolDirectory.js +4 -2
  31. package/dist/eventLeg.d.ts +1 -7
  32. package/dist/eventLeg.js +7 -8
  33. package/dist/fileHistoryCaptureCapability.d.ts +0 -3
  34. package/dist/fileHistoryCaptureCapability.js +0 -4
  35. package/dist/generated/engineFactTables.d.ts +1 -0
  36. package/dist/generated/engineFactTables.js +27 -0
  37. package/dist/generated/engineNoticeTables.js +3 -0
  38. package/dist/generated/settingsSchemaFns.d.ts +12 -0
  39. package/dist/generated/settingsSchemaFns.js +13 -0
  40. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -0
  41. package/dist/generated/settingsSchemaMcpRequestEntry.js +162 -0
  42. package/dist/generated/settingsSchemaTables.d.ts +1 -0
  43. package/dist/generated/settingsSchemaTables.js +5 -0
  44. package/dist/generated/settingsSchemaTypes.d.ts +28 -0
  45. package/dist/hitl/allowRuleShadow.js +2 -2
  46. package/dist/hitl/approvalCardReasons.js +2 -2
  47. package/dist/hitl/approvalHolds.d.ts +14 -0
  48. package/dist/hitl/approvalHolds.js +41 -0
  49. package/dist/hitl/askClassPair.d.ts +2 -2
  50. package/dist/hitl/askClassPair.js +2 -2
  51. package/dist/hitl/attributionLadder.d.ts +23 -0
  52. package/dist/hitl/attributionLadder.js +67 -0
  53. package/dist/hitl/decisionLedger.d.ts +55 -0
  54. package/dist/hitl/decisionLedger.js +156 -0
  55. package/dist/hitl/decisionRefusal.d.ts +1 -0
  56. package/dist/hitl/decisionRefusal.js +1 -1
  57. package/dist/hitl/decisionSend.d.ts +113 -0
  58. package/dist/hitl/decisionSend.js +579 -0
  59. package/dist/hitl/hitlBridge.js +18 -6
  60. package/dist/hitl/planReviewWire.js +45 -76
  61. package/dist/hitl/sessionPolicyDeliverable.js +3 -6
  62. package/dist/hitl/toolApprovalWire.js +43 -109
  63. package/dist/hostTurnSettings.d.ts +46 -7
  64. package/dist/hostTurnSettings.js +408 -7
  65. package/dist/humanTurn.d.ts +2 -1
  66. package/dist/humanTurn.js +3 -3
  67. package/dist/i18n/messages.en.d.ts +131 -0
  68. package/dist/i18n/messages.en.js +143 -12
  69. package/dist/i18n/messages.zh.js +148 -12
  70. package/dist/index.d.ts +18 -2
  71. package/dist/index.js +10 -1
  72. package/dist/inheritEnvCapability.d.ts +1 -0
  73. package/dist/inheritEnvCapability.js +9 -2
  74. package/dist/inheritEnvWire.js +2 -2
  75. package/dist/injectionLaneNotice.d.ts +5 -0
  76. package/dist/injectionLaneNotice.js +46 -0
  77. package/dist/mcpPanel.js +2 -0
  78. package/dist/mcpReconnect.js +2 -2
  79. package/dist/mcpToolToggles.d.ts +6 -0
  80. package/dist/mcpToolToggles.js +46 -0
  81. package/dist/mcpWireCaps.d.ts +10 -4
  82. package/dist/mcpWireCaps.js +106 -71
  83. package/dist/mcpWireTransport.d.ts +4 -0
  84. package/dist/mcpWireTransport.js +18 -0
  85. package/dist/modelCapabilityProbe.d.ts +2 -1
  86. package/dist/modelCapabilityProbe.js +10 -9
  87. package/dist/modelIdentityResolvability.d.ts +2 -0
  88. package/dist/modelIdentityResolvability.js +23 -5
  89. package/dist/ownKey.d.ts +26 -0
  90. package/dist/ownKey.js +75 -0
  91. package/dist/panelReduce.d.ts +5 -0
  92. package/dist/panelReduce.js +45 -13
  93. package/dist/panelStateHolder.d.ts +1 -0
  94. package/dist/panelStateHolder.js +10 -1
  95. package/dist/permissionRuleIssue.d.ts +2 -1
  96. package/dist/permissionRuleIssue.js +22 -21
  97. package/dist/pluginHooksWire.js +12 -10
  98. package/dist/prepareRefusal.d.ts +8 -0
  99. package/dist/prepareRefusal.js +72 -0
  100. package/dist/request/taskRequest.d.ts +10 -10
  101. package/dist/request/taskRequest.js +106 -65
  102. package/dist/resumeRefusalCopy.d.ts +3 -1
  103. package/dist/resumeRefusalCopy.js +22 -4
  104. package/dist/retryStatus.d.ts +18 -0
  105. package/dist/retryStatus.js +76 -19
  106. package/dist/ruleRefusalSentence.d.ts +4 -0
  107. package/dist/ruleRefusalSentence.js +58 -0
  108. package/dist/sandboxWire.d.ts +2 -1
  109. package/dist/sandboxWire.js +55 -21
  110. package/dist/scenarioRefusal.d.ts +2 -1
  111. package/dist/scenarioRefusal.js +5 -5
  112. package/dist/seam.d.ts +24 -9
  113. package/dist/seam.js +44 -4
  114. package/dist/seatContract.d.ts +16 -8
  115. package/dist/seatContract.js +21 -6
  116. package/dist/settingsCredentialKeyPaths.d.ts +8 -0
  117. package/dist/settingsCredentialKeyPaths.js +219 -0
  118. package/dist/skillsWireCaps.d.ts +0 -3
  119. package/dist/skillsWireCaps.js +1 -2
  120. package/dist/subagentContentStore.d.ts +1 -0
  121. package/dist/subagentContentStore.js +135 -27
  122. package/dist/toolsFlagNames.d.ts +8 -0
  123. package/dist/toolsFlagNames.js +110 -0
  124. package/dist/turnFailurePresentation.js +15 -7
  125. package/dist/wireRefusalCopy.d.ts +51 -17
  126. package/dist/wireRefusalCopy.js +298 -135
  127. package/docs/INTEGRATION-CLIENTS.md +423 -26
  128. package/package.json +4 -4
@@ -4,7 +4,7 @@ import { retiredAttachmentsKeyOf } from '../taskRequestWords.js';
4
4
  import { msg, renderMessage } from '../i18n/translate.js';
5
5
  import { outputStyleSeatAdmitted } from '../outputStyleSeatCapability.js';
6
6
  import { refuseBadApprovalReach } from '../approvalReachWire.js';
7
- import { HOST_SUPPLIED_KEYS, HostSettingsRefused, buildHostTurnSettings, hostTurnSettingsOf, rethrowHostResolvedDeferred, } from '../hostTurnSettings.js';
7
+ import { HOST_SUPPLIED_KEYS, HostSettingsRefused, UNWIRED_HOST_TURN_SETTINGS, buildHostTurnSettings, hostIntakeSnapshotOf, hostProjectionNotice, hostTurnSettingsOf, intersectHostHooks, rethrowHostResolvedDeferred, } from '../hostTurnSettings.js';
8
8
  export const REQUEST_FIELD_MATRIX = [
9
9
  { field: 'objective', lanes: ['interactive', 'print', 'utility'], live: false, why: '本 turn 的真实输入,每条车道都必需', whyUtilityLane: '恒座:三条车道都必须带上本次提交的模型面输入' },
10
10
  { field: 'sessionId', lanes: ['interactive', 'print', 'utility'], live: false, why: '单命名空间(TP-A Step 2):壳 id 即引擎 session id;print 在非 live 下保留 mock 常量', whyUtilityLane: '恒座:side-channel 提交走**独立**会话 id(壳另铸一个,绝不复用用户会话那一个 —— 复用会把这次提交的转录混进用户会话)' },
@@ -168,8 +168,6 @@ const INPUT_KEY_ROUTES = {
168
168
  why: '思考 token 数上限:sdk `TaskRequest` 上思考强度只有档位词(`reasoningEffort` 七词闭集),没有 token 数这一位;把数字折成档位词 = 替用户编一个他没说过的档位',
169
169
  },
170
170
  memoryCapture: row('memoryCapture'),
171
- memory: row('memory'),
172
- fileHistory: row('fileHistory'),
173
171
  inheritEnv: row('inheritEnv'),
174
172
  requireExistingSession: row('requireExistingSession'),
175
173
  disableSkills: row('disableSkills'),
@@ -179,8 +177,16 @@ const INPUT_KEY_ROUTES = {
179
177
  hostSettings: { via: 'control' },
180
178
  settings: { via: 'settings' },
181
179
  };
180
+ const HOST_TOP_KEY_ROUTES = {
181
+ memory: row('memory'),
182
+ fileHistory: row('fileHistory'),
183
+ };
184
+ const HOST_TOP_KEYS = Object.freeze(Object.keys(HOST_TOP_KEY_ROUTES));
185
+ const _hostTopKeysPin = [true];
186
+ void _hostTopKeysPin;
187
+ const HOST_TOP_ASK_AFTER = 'memoryCapture';
182
188
  const HOST_SETTINGS_SUBKEYS = Object.freeze(HOST_SUPPLIED_KEYS.filter((k) => k.startsWith('settings.')).map((k) => k.slice('settings.'.length)));
183
- const _hostSubkeysPin = [true, true, true, true];
189
+ const _hostSubkeysPin = [true, true, true];
184
190
  void _hostSubkeysPin;
185
191
  const SETTINGS_KEY_ROUTES = {
186
192
  resolved: row('settings.<resolved>'),
@@ -195,6 +201,8 @@ const liveDefaultKeyNames = () => new Set(LIVE_DEFAULT_FIELDS.flatMap(item => it
195
201
  const routeOfTopLevelKey = (key) => {
196
202
  if (hasOwn(INPUT_KEY_ROUTES, key))
197
203
  return INPUT_KEY_ROUTES[key];
204
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key))
205
+ return HOST_TOP_KEY_ROUTES[key];
198
206
  if (!key.includes('.') && liveDefaultKeyNames().has(key))
199
207
  return { via: 'fixed', cause: 'other_channel' };
200
208
  return undefined;
@@ -221,15 +229,16 @@ const OMISSION_JUDGES = {
221
229
  not_live: (s, _lane, live) => s.via === 'matrix' && s.spec.live && !live,
222
230
  };
223
231
  const omissionCauseOf = (s, lane, live) => TASK_REQUEST_OMISSION_CAUSES.find(cause => OMISSION_JUDGES[cause](s, lane, live));
224
- const hostRefusalNoticeOf = (host, lane, live, verdictNotice) => {
232
+ const hostRefusalNoticeOf = (intake, lane, live) => {
233
+ const verdictNotice = intake.notice ?? hostProjectionNotice();
225
234
  try {
226
- const refusing = host.rows.filter((row) => row.disposition === 'refuse_turn');
235
+ const refusing = intake.refusingKeys;
227
236
  if (refusing.length === 0)
228
237
  return verdictNotice;
229
- const turnNotices = host.notices.filter((n) => n.plane === 'turn');
230
- const aligned = turnNotices.length === refusing.length;
231
- for (const [i, row] of refusing.entries()) {
232
- if (!hostRefuseRowSeated(row.key, lane, live))
238
+ const turnNotices = intake.turnNotices;
239
+ const aligned = turnNotices !== undefined && turnNotices.length === refusing.length;
240
+ for (const [i, key] of refusing.entries()) {
241
+ if (!hostRefuseRowSeated(key, lane, live))
233
242
  continue;
234
243
  return aligned ? (turnNotices[i] ?? verdictNotice) : verdictNotice;
235
244
  }
@@ -240,6 +249,12 @@ const hostRefusalNoticeOf = (host, lane, live, verdictNotice) => {
240
249
  }
241
250
  };
242
251
  const hostRefuseRowSeated = (key, lane, live) => {
252
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key)) {
253
+ const top = HOST_TOP_KEY_ROUTES[key];
254
+ return top === undefined || (top.via !== 'matrix' && top.via !== 'fixed') || omissionCauseOf(standingOf(top), lane, live) === undefined;
255
+ }
256
+ if (!key.startsWith('settings.'))
257
+ return true;
243
258
  const sub = key.slice('settings.'.length);
244
259
  if (!hasOwn(SETTINGS_KEY_ROUTES, sub))
245
260
  return true;
@@ -444,16 +459,49 @@ const resolvedSnapshotForWire = (resolved) => {
444
459
  return out;
445
460
  };
446
461
  export function hostTurnSettings(input) {
447
- return buildHostTurnSettings(input, (snapshot) => resolvedSnapshotForWire(snapshot));
462
+ return buildHostTurnSettings(input, { materialize: (snapshot) => resolvedSnapshotForWire(snapshot), memoryOffDeclared });
463
+ }
464
+ export function refilterHostSettings(request, fresh, lane) {
465
+ refuseBadLane(lane);
466
+ const host = hostTurnSettingsOf(fresh);
467
+ const intake = host === undefined || host === UNWIRED_HOST_TURN_SETTINGS ? undefined : hostIntakeSnapshotOf(host);
468
+ if (host === undefined || intake === undefined) {
469
+ throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(fresh) })));
470
+ }
471
+ const refuseNotice = () => intake.notice ?? hostProjectionNotice();
472
+ if (intake.refuses) {
473
+ const refusing = hostRefusalNoticeOf(intake, lane, true);
474
+ if (refusing !== undefined)
475
+ return Object.freeze({ kind: 'refuse', notice: refusing });
476
+ }
477
+ const settings = request.settings;
478
+ if (!isPlainRecord(settings) || !Object.hasOwn(settings, 'hooks'))
479
+ return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
480
+ const disposition = intake.dispositions['settings.hooks'];
481
+ if (disposition === 'unwired')
482
+ return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
483
+ const now = intake.carried.hooks;
484
+ const sentHooks = settings.hooks;
485
+ if (!isPlainRecord(sentHooks))
486
+ return Object.freeze({ kind: 'refuse', notice: refuseNotice() });
487
+ const cut = intersectHostHooks(sentHooks, now);
488
+ if (!cut.judged)
489
+ return Object.freeze({ kind: 'refuse', notice: refuseNotice() });
490
+ if (cut.dropped === 0)
491
+ return Object.freeze({ kind: 'send', request, hooksDropped: 0 });
492
+ const { hooks: _sentHooks, ...restSettings } = settings;
493
+ void _sentHooks;
494
+ const nextSettings = cut.hooks === undefined ? restSettings : { ...restSettings, hooks: cut.hooks };
495
+ const { settings: _sentSettings, ...restRequest } = request;
496
+ void _sentSettings;
497
+ const next = Object.keys(nextSettings).length > 0 ? { ...restRequest, settings: nextSettings } : restRequest;
498
+ return Object.freeze({ kind: 'send', request: next, hooksDropped: cut.dropped });
448
499
  }
449
500
  export const MEMORY_CAPTURE_OFF = 'off';
450
501
  export function memoryCaptureDeclarationField(declared) {
451
502
  return declared ? { memoryCapture: MEMORY_CAPTURE_OFF } : {};
452
503
  }
453
504
  export const MEMORY_OFF = 'off';
454
- export function memoryOffDeclarationField(declared) {
455
- return declared ? { memory: MEMORY_OFF } : {};
456
- }
457
505
  export const FILE_HISTORY_DISABLED = 'disabled';
458
506
  export const INHERIT_ENV_WORDS = Object.freeze(['all', 'scrub']);
459
507
  export function isInheritEnvWord(v) {
@@ -481,16 +529,6 @@ export function memoryOffDeclared(input) {
481
529
  }
482
530
  return flag === MEMORY_OFF || fromSettings;
483
531
  }
484
- const refuseBadMemoryOff = (v) => {
485
- if (v === undefined || v === null)
486
- return;
487
- if (v === 'off')
488
- return;
489
- throw new TypeError('buildTaskRequest: memory 只接受字面量 `\'off\'` 或缺席(undefined/null)—— ' +
490
- `收到 ${coarseShape(v)}。这一位是**请求级整面开关**(「这一次别给我记忆」),上游是**单成员闭集**(没有 ` +
491
- '`"on"`,`{ enabled: false }` 对象形也不是 wire 拼法)且对坏拼写**响亮拒**(server 400 `request.field_invalid`)。' +
492
- '本层若静默删键,这一次运行会照常挂上记忆面而声明人不会知道 —— 与 memoryCapture 同一条纪律。错误文本只报形状,不回显值。');
493
- };
494
532
  const withoutRetiredAttachmentsKeys = (v) => {
495
533
  if (typeof v !== 'object' || v === null)
496
534
  return v;
@@ -598,15 +636,6 @@ const refuseBadExcludeAllTools = (v) => {
598
636
  `收到 ${coarseShape(v)}。这一位是**整只工具面卸载**的收紧声明:本层若把坏值静默删键,这一次运行会挂上声明人以为已经卸掉的全部工具。` +
599
637
  '错误文本只报形状,不回显值。');
600
638
  };
601
- const refuseBadFileHistory = (v) => {
602
- if (v === undefined || v === null)
603
- return;
604
- if (v === FILE_HISTORY_DISABLED)
605
- return;
606
- throw new TypeError('buildTaskRequest: fileHistory 只接受字面量 `\'disabled\'` 或缺席(undefined/null)—— ' +
607
- `收到 ${coarseShape(v)}。这一位是「这一次运行别捕获文件历史」的收紧声明,上游是**单成员闭集**(没有第二个词)且对坏值` +
608
- '**响亮拒**。本层若静默删键,这一次运行照常捕获而声明人不会知道。错误文本只报形状,不回显值。');
609
- };
610
639
  const refuseBadInheritEnv = (v) => {
611
640
  if (v === undefined || v === null)
612
641
  return;
@@ -704,7 +733,7 @@ export function parseMaxCostUsd(text) {
704
733
  return { ok: false, why: 'not_positive' };
705
734
  return { ok: true, value };
706
735
  }
707
- const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'memory', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'fileHistory', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills'];
736
+ const ACCESSOR_READ_ORDER = ['live', 'memoryCapture', 'hostSettings', 'settings', 'maxCostUsd', 'excludeAllTools', 'maxTokens', 'outputRetries', 'inheritEnv', 'requireExistingSession', 'disableSkills', 'approvalReach', 'allowTools', 'userInvokedSkills'];
708
737
  const MATERIALIZE_AT_READ = Object.freeze({ allowTools: (v) => allowToolsSnapshot(v), userInvokedSkills: (v) => allowToolsSnapshot(v) });
709
738
  function dataFirstSnapshot(input, afterData) {
710
739
  let descs;
@@ -742,25 +771,28 @@ export function assembleTaskRequest(input, lane) {
742
771
  let resolvedMaterialized;
743
772
  let resolvedMaterializeError;
744
773
  let resolvedHookRan = false;
774
+ let intakeEarly;
775
+ const materializeAtRead = (product) => (raw) => {
776
+ resolvedInEarly = raw;
777
+ try {
778
+ rethrowHostResolvedDeferred(product);
779
+ resolvedMaterialized = resolvedSnapshotForWire(resolvedInEarly);
780
+ }
781
+ catch (e) {
782
+ resolvedMaterializeError = e;
783
+ }
784
+ };
745
785
  const src = dataFirstSnapshot(input, (bag) => {
746
786
  if (!Object.hasOwn(bag, 'hostSettings'))
747
787
  return;
748
788
  const product = hostTurnSettingsOf(bag['hostSettings'], 'shape');
749
789
  if (product !== undefined) {
750
790
  resolvedHookRan = true;
751
- resolvedInEarly = product.carried.resolved;
752
- try {
753
- rethrowHostResolvedDeferred(product);
754
- resolvedMaterialized = resolvedSnapshotForWire(resolvedInEarly);
755
- }
756
- catch (e) {
757
- resolvedMaterializeError = e;
758
- }
791
+ intakeEarly = hostIntakeSnapshotOf(product, materializeAtRead(product));
759
792
  }
760
793
  });
761
794
  const live = src.live;
762
795
  const memoryCaptureIn = src.memoryCapture;
763
- const memoryIn = src.memory;
764
796
  const settingsIn = src.settings;
765
797
  if (present(settingsIn) && (typeof settingsIn !== 'object' || Array.isArray(settingsIn))) {
766
798
  throw new TypeError(`buildTaskRequest: settings 只接受对象或缺席(undefined/null)—— 收到 ${coarseShape(settingsIn)}。` +
@@ -772,19 +804,19 @@ export function assembleTaskRequest(input, lane) {
772
804
  if (host === undefined) {
773
805
  throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(hostIn) })));
774
806
  }
775
- const resolvedIn = resolvedHookRan ? resolvedInEarly : host.carried.resolved;
807
+ const intake = resolvedHookRan ? intakeEarly : hostIntakeSnapshotOf(host, materializeAtRead(host));
808
+ if (intake === undefined)
809
+ throw new TypeError(renderMessage(msg('config.taskRequest.hostSettingsMalformed', { shape: coarseShape(hostIn) })));
810
+ const resolvedIn = resolvedInEarly;
776
811
  const resolvedRoute = SETTINGS_KEY_ROUTES.resolved;
777
812
  const resolvedOpen = resolvedRoute.via === 'matrix' && omissionCauseOf(standingOf(resolvedRoute), lane, live) === undefined;
778
- if (resolvedOpen && resolvedHookRan && resolvedMaterializeError !== undefined)
813
+ if (resolvedOpen && resolvedMaterializeError !== undefined)
779
814
  throw resolvedMaterializeError;
780
- if (resolvedOpen && !resolvedHookRan)
781
- rethrowHostResolvedDeferred(host);
782
815
  const resolvedWire = resolvedOpen ? (resolvedMaterialized ?? resolvedSnapshotForWire(resolvedIn)) : {};
783
816
  const maxCostUsdIn = src.maxCostUsd;
784
817
  const excludeAllToolsIn = src.excludeAllTools;
785
818
  const maxTokensIn = src.maxTokens;
786
819
  const outputRetriesIn = src.outputRetries;
787
- const fileHistoryIn = src.fileHistory;
788
820
  const inheritEnvIn = src.inheritEnv;
789
821
  const requireExistingSessionIn = src.requireExistingSession;
790
822
  const disableSkillsIn = src.disableSkills;
@@ -794,12 +826,10 @@ export function assembleTaskRequest(input, lane) {
794
826
  const i = snapshotByName(src, INPUT_KEYS, {
795
827
  live,
796
828
  memoryCapture: memoryCaptureIn,
797
- memory: memoryIn,
798
829
  maxCostUsd: maxCostUsdIn,
799
830
  maxTokens: maxTokensIn,
800
831
  excludeAllTools: excludeAllToolsIn,
801
832
  outputRetries: outputRetriesIn,
802
- fileHistory: fileHistoryIn,
803
833
  inheritEnv: inheritEnvIn,
804
834
  requireExistingSession: requireExistingSessionIn,
805
835
  disableSkills: disableSkillsIn,
@@ -809,20 +839,19 @@ export function assembleTaskRequest(input, lane) {
809
839
  });
810
840
  const SETTINGS_KEYS = Object.keys(SETTINGS_KEY_ROUTES);
811
841
  const hostSettingsKeysIn = HOST_SETTINGS_SUBKEYS.filter((k) => hasOwn(settingsObj, k) && present(Reflect.get(settingsObj, k)));
842
+ const hostTopKeysIn = HOST_TOP_KEYS.filter((k) => hasOwn(src, k) && present(Reflect.get(src, k)));
812
843
  const s = {
813
844
  resolved: resolvedIn,
814
- webSearch: host.carried.webSearch,
845
+ webSearch: intake.carried.webSearch,
815
846
  ultracode: snapshotByName(settingsObj, ['ultracode']).ultracode,
816
- hooks: host.carried.hooks,
817
- outputStyle: host.carried.outputStyle,
847
+ hooks: intake.carried.hooks,
848
+ outputStyle: intake.carried.outputStyle,
818
849
  };
819
850
  refuseBadMemoryCapture(memoryCaptureIn);
820
- refuseBadMemoryOff(memoryIn);
821
851
  refuseBadMaxCostUsd(maxCostUsdIn);
822
852
  refuseBadExcludeAllTools(excludeAllToolsIn);
823
853
  refuseBadMaxTokens(maxTokensIn);
824
854
  refuseBadOutputRetries(outputRetriesIn);
825
- refuseBadFileHistory(fileHistoryIn);
826
855
  refuseBadInheritEnv(inheritEnvIn);
827
856
  refuseBadRequireExistingSession(requireExistingSessionIn);
828
857
  refuseBadDisableSkills(disableSkillsIn, 'buildTaskRequest');
@@ -856,6 +885,8 @@ export function assembleTaskRequest(input, lane) {
856
885
  for (const key of Object.keys(src)) {
857
886
  if (hasOwn(INPUT_KEY_ROUTES, key))
858
887
  continue;
888
+ if (hasOwn(HOST_TOP_KEY_ROUTES, key))
889
+ continue;
859
890
  const stray = Reflect.get(src, key);
860
891
  if (!present(stray))
861
892
  continue;
@@ -895,10 +926,10 @@ export function assembleTaskRequest(input, lane) {
895
926
  '处置:删掉这个键;要在回退会话时一并还原文件,改交 ' +
896
927
  'rewind.restoreFiles: true(必须与 rewind.resumeAt 同交);只还原文件、不动会话,交 rewind.rewindFilesTo。错误文本只报键名,不回显值。');
897
928
  }
898
- if (hostSettingsKeysIn.length > 0) {
929
+ if (hostSettingsKeysIn.length > 0 || hostTopKeysIn.length > 0) {
899
930
  throw new TypeError(renderMessage(msg('config.taskRequest.hostSuppliedKeysByHand', {
900
- keys: hostSettingsKeysIn.map((k) => `settings.${k}`).join(', '),
901
- fields: hostSettingsKeysIn.map((k) => `${k}: …`).join(', '),
931
+ keys: [...hostSettingsKeysIn.map((k) => `settings.${k}`), ...hostTopKeysIn].join(', '),
932
+ fields: [...hostSettingsKeysIn, ...hostTopKeysIn].map((k) => `${k}: …`).join(', '),
902
933
  })));
903
934
  }
904
935
  if (unknownKeys.length > 0) {
@@ -914,8 +945,8 @@ export function assembleTaskRequest(input, lane) {
914
945
  refuseRequireExistingWithoutSession(requireExistingSessionIn, i.sessionId);
915
946
  refuseDisableSkillsConflict(disableSkillsIn, i.skills, Reflect.get(src, 'agents'), 'buildTaskRequest');
916
947
  refuseUtilityWithoutToolUnload(lane, excludeAllToolsIn);
917
- if (host.verdict.kind === 'refuse') {
918
- const refusing = hostRefusalNoticeOf(host, lane, live, host.verdict.notice);
948
+ if (intake.refuses) {
949
+ const refusing = hostRefusalNoticeOf(intake, lane, live);
919
950
  if (refusing !== undefined)
920
951
  throw new HostSettingsRefused(refusing);
921
952
  }
@@ -933,15 +964,25 @@ export function assembleTaskRequest(input, lane) {
933
964
  return false;
934
965
  };
935
966
  const gate = Object.create(null);
936
- for (const key of INPUT_KEYS) {
967
+ const askInput = (key) => {
937
968
  const route = INPUT_KEY_ROUTES[key];
938
969
  if (route.via === 'settings') {
939
970
  for (const sub of SETTINGS_KEYS)
940
971
  gate[`settings.${sub}`] = carries(SETTINGS_KEY_ROUTES[sub], s[sub], `settings.${sub}`);
941
- continue;
972
+ return;
942
973
  }
943
974
  gate[key] = carries(route, key === 'rewind' ? rewindForGate : i[key], key);
944
- }
975
+ };
976
+ const anchorAt = INPUT_KEYS.indexOf(HOST_TOP_ASK_AFTER);
977
+ const hostTopAt = anchorAt < 0 ? INPUT_KEYS.length : anchorAt + 1;
978
+ for (const key of INPUT_KEYS.slice(0, hostTopAt))
979
+ askInput(key);
980
+ const hostTopGate = {
981
+ memory: carries(HOST_TOP_KEY_ROUTES.memory, intake.carried.memory, 'memory'),
982
+ fileHistory: carries(HOST_TOP_KEY_ROUTES.fileHistory, intake.carried.fileHistory, 'fileHistory'),
983
+ };
984
+ for (const key of INPUT_KEYS.slice(hostTopAt))
985
+ askInput(key);
945
986
  const on = (key) => gate[key] === true;
946
987
  const onSetting = (sub) => gate[`settings.${sub}`] === true;
947
988
  const settingsOut = {
@@ -989,8 +1030,8 @@ export function assembleTaskRequest(input, lane) {
989
1030
  ...(on('maxCostUsd') ? { maxCostUsd: maxCostUsdIn } : {}),
990
1031
  ...(on('maxTokens') ? { maxTokens: i.maxTokens } : {}),
991
1032
  ...(on('memoryCapture') ? { memoryCapture: 'off' } : {}),
992
- ...(on('memory') ? { memory: 'off' } : {}),
993
- ...(on('fileHistory') ? { fileHistory: FILE_HISTORY_DISABLED } : {}),
1033
+ ...(hostTopGate.memory ? { memory: 'off' } : {}),
1034
+ ...(hostTopGate.fileHistory ? { fileHistory: FILE_HISTORY_DISABLED } : {}),
994
1035
  ...(on('inheritEnv') ? { inheritEnv: inheritEnvIn } : {}),
995
1036
  ...(on('requireExistingSession') ? { requireExistingSession: true } : {}),
996
1037
  ...(on('disableSkills') ? { disableSkills: true } : {}),
@@ -48,7 +48,7 @@ export interface ParkedResumeStartupDetail {
48
48
  denyStaysOpen: true;
49
49
  }
50
50
  declare const RESUME_CAUSE_CORE_AHEAD: readonly ["transcript_corrupt"];
51
- type ResumeCauseCoreAhead = (typeof RESUME_CAUSE_CORE_AHEAD)[number];
51
+ type ResumeCauseCoreAhead = Exclude<(typeof RESUME_CAUSE_CORE_AHEAD)[number], ResumeStartupCause>;
52
52
  export declare function parkedResumeStartupFromError(err: unknown, decision?: ParkedResumeStartupDecision): ParkedResumeStartupDetail | null;
53
53
  export declare function parkedResumeStartupContent(detail: ParkedResumeStartupDetail, lang?: UiLanguage): string;
54
54
  export interface CheckpointUnsupportedVersionDetail {
@@ -69,6 +69,8 @@ export interface RefusedParkFacts {
69
69
  readonly code: RefusedParkCode;
70
70
  readonly field?: string;
71
71
  readonly stillPending?: boolean;
72
+ readonly blockedBy?: string;
73
+ readonly reason?: string;
72
74
  }
73
75
  export declare function refusedParkFromError(failure: unknown): RefusedParkFacts | null;
74
76
  export interface RefusedParkContentInput {
@@ -238,6 +238,7 @@ export function checkpointUnsupportedVersionContent(detail, ctx, lang) {
238
238
  return renderMessage(msg('resume.refusal.checkpoint.content', { outlet }), lang);
239
239
  }
240
240
  const REFUSED_PARK_FIELD_TOKEN = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;
241
+ const REFUSED_PARK_CODE_TOKEN = /^(?=.{1,128}$)[A-Za-z][A-Za-z0-9_]*(?:\.[A-Za-z0-9_]+)*$/;
241
242
  export function refusedParkFromError(failure) {
242
243
  if (typeof failure !== 'object' || failure === null)
243
244
  return null;
@@ -258,10 +259,22 @@ export function refusedParkFromError(failure) {
258
259
  const stillPending = reading === true ? true : status === 409 && reading === false ? false : undefined;
259
260
  const rawField = code === CHECKPOINT_CONFIG_REFUSED ? readErrorString(failure, 'field') : undefined;
260
261
  const field = rawField !== undefined && REFUSED_PARK_FIELD_TOKEN.test(rawField) ? rawField : undefined;
262
+ let blockedBy;
263
+ try {
264
+ const b = readBlockedBy(failure);
265
+ blockedBy = b.kind === 'named' && REFUSED_PARK_CODE_TOKEN.test(b.code) ? b.code : undefined;
266
+ }
267
+ catch {
268
+ blockedBy = undefined;
269
+ }
270
+ const rawReason = readErrorString(failure, 'reason');
271
+ const reason = rawReason !== undefined && REFUSED_PARK_FIELD_TOKEN.test(rawReason) ? rawReason : undefined;
261
272
  return Object.freeze({
262
273
  code,
263
274
  ...(field !== undefined ? { field } : {}),
264
275
  ...(stillPending !== undefined ? { stillPending } : {}),
276
+ ...(blockedBy !== undefined ? { blockedBy } : {}),
277
+ ...(reason !== undefined ? { reason } : {}),
265
278
  });
266
279
  }
267
280
  function refusedParkShownRunId(runId) {
@@ -272,6 +285,8 @@ function refusedParkWhy(code, field) {
272
285
  case CHECKPOINT_WALLTIME_AXIS_RETIRED:
273
286
  return msg('resume.refusal.park.whyWalltime');
274
287
  case CHECKPOINT_CONFIG_REFUSED:
288
+ if (field === 'deps')
289
+ return msg('resume.refusal.park.whyDeps');
275
290
  return msg(field === 'inheritedGate' ? 'resume.refusal.park.whyInheritedGate' : 'resume.refusal.park.whyConfigRefused');
276
291
  default:
277
292
  return msg('resume.refusal.park.whyUnsupported');
@@ -284,6 +299,7 @@ export function refusedParkContent(input, lang) {
284
299
  let rawCode;
285
300
  let field;
286
301
  let stillPending;
302
+ let blockedBy;
287
303
  try {
288
304
  what = input.what;
289
305
  lane = input.lane;
@@ -297,21 +313,23 @@ export function refusedParkContent(input, lang) {
297
313
  rawCode = facts.code;
298
314
  field = facts.field;
299
315
  stillPending = facts.stillPending;
316
+ blockedBy = facts.blockedBy;
300
317
  }
301
318
  catch {
302
- rawCode = field = stillPending = undefined;
319
+ rawCode = field = stillPending = blockedBy = undefined;
303
320
  }
304
321
  const code = rawCode === CHECKPOINT_WALLTIME_AXIS_RETIRED || rawCode === CHECKPOINT_CONFIG_REFUSED ? rawCode : CHECKPOINT_UNSUPPORTED_VERSION;
305
322
  const shownWhat = typeof what === 'string' && what !== '' ? capForDisplay(what, 40) : msg('resume.refusal.park.whatDefault');
306
323
  const why = refusedParkWhy(code, typeof field === 'string' ? field : undefined);
307
324
  const shownRun = refusedParkShownRunId(runId);
308
325
  const head = msg('resume.refusal.park.head', { what: shownWhat, why });
309
- const configMayReturn = code === CHECKPOINT_CONFIG_REFUSED && field !== 'inheritedGate' ? msg('resume.refusal.park.configMayReturn') : '';
326
+ const configMayReturn = code === CHECKPOINT_CONFIG_REFUSED && field !== 'inheritedGate' ? msg(field === 'deps' ? 'resume.refusal.park.depsMayReturn' : 'resume.refusal.park.configMayReturn') : '';
310
327
  if (lane === 'print') {
328
+ const blocked = typeof blockedBy === 'string' && REFUSED_PARK_CODE_TOKEN.test(blockedBy) ? msg('resume.refusal.park.blockedByTail', { blockedBy }) : '';
311
329
  if (stillPending === false)
312
- return renderMessage(msg('resume.refusal.park.printEnded', { head, code }), lang);
330
+ return renderMessage(msg('resume.refusal.park.printEnded', { head, code, blocked }), lang);
313
331
  const cancel = `POST /v1/runs/${shownRun ?? '<run id>'}/cancel`;
314
- return renderMessage(msg('resume.refusal.park.printHolds', { head, configMayReturn, code, cancel }), lang);
332
+ return renderMessage(msg('resume.refusal.park.printHolds', { head, configMayReturn, code, cancel, blocked }), lang);
315
333
  }
316
334
  if (stillPending === false)
317
335
  return renderMessage(msg('resume.refusal.park.interactiveEnded', { head }), lang);
@@ -93,6 +93,24 @@ export interface RetryStatusNotice {
93
93
  readonly recourse?: 'switch_model' | 'wait' | 'fix_request';
94
94
  readonly phase: 'retrying' | 'reconnecting' | 'waiting_first_token' | 'gave_up' | 'cleared';
95
95
  }
96
+ export interface RetryStatusParts {
97
+ readonly phase: RetryStatusNotice['phase'];
98
+ readonly model?: string;
99
+ readonly condition?: string;
100
+ readonly resendNote?: string;
101
+ readonly httpStatus?: number;
102
+ readonly source?: string;
103
+ readonly attempt?: number;
104
+ readonly maxAttempts?: number;
105
+ readonly elapsedMs?: number;
106
+ readonly timeoutMs?: number;
107
+ readonly retryAtMs?: number;
108
+ readonly detail?: string;
109
+ readonly recourse?: RetryStatusNotice['recourse'];
110
+ }
111
+ export declare function retryStatusParts(status: RetryStatus | null, ctx?: {
112
+ readonly modelName?: string;
113
+ }): RetryStatusParts;
96
114
  export declare function retryStatusNotice(status: RetryStatus | null, ctx?: {
97
115
  readonly modelName?: string;
98
116
  }): RetryStatusNotice;
@@ -1,5 +1,6 @@
1
1
  import { displayUntrusted } from './displayUntrusted.js';
2
2
  import { guardedRead, readOwnDataFiniteNumber, readOwnDataNonEmptyString, readOwnDataValue } from './ownKey.js';
3
+ import { msg, renderMessage } from './i18n/translate.js';
3
4
  export const BRAIN_STATUS_PHASES = [
4
5
  'rate_limited',
5
6
  'retrying',
@@ -172,11 +173,38 @@ function noticeAttemptTail(attempt, maxRetries) {
172
173
  function sealNotice(line, phase, recourse) {
173
174
  return Object.freeze(recourse === undefined ? { line, phase } : { line, recourse, phase });
174
175
  }
175
- export function retryStatusNotice(status, ctx) {
176
+ const SOURCE_PHRASES = Object.freeze({
177
+ unified_header: 'notice.retry.source.unifiedHeader',
178
+ provider_code: 'notice.retry.source.providerCode',
179
+ status: 'notice.retry.source.status',
180
+ });
181
+ function sourcePhraseOf(word) {
182
+ if (word === undefined || !Object.hasOwn(SOURCE_PHRASES, word))
183
+ return undefined;
184
+ return renderMessage(msg(SOURCE_PHRASES[word]));
185
+ }
186
+ function sealParts(p) {
187
+ return Object.freeze({
188
+ phase: p.phase,
189
+ ...(p.model !== undefined ? { model: p.model } : {}),
190
+ ...(p.condition !== undefined ? { condition: p.condition } : {}),
191
+ ...(p.resendNote !== undefined ? { resendNote: p.resendNote } : {}),
192
+ ...(p.httpStatus !== undefined ? { httpStatus: p.httpStatus } : {}),
193
+ ...(p.source !== undefined ? { source: p.source } : {}),
194
+ ...(p.attempt !== undefined ? { attempt: p.attempt } : {}),
195
+ ...(p.maxAttempts !== undefined ? { maxAttempts: p.maxAttempts } : {}),
196
+ ...(p.elapsedMs !== undefined ? { elapsedMs: p.elapsedMs } : {}),
197
+ ...(p.timeoutMs !== undefined ? { timeoutMs: p.timeoutMs } : {}),
198
+ ...(p.retryAtMs !== undefined ? { retryAtMs: p.retryAtMs } : {}),
199
+ ...(p.detail !== undefined ? { detail: p.detail } : {}),
200
+ ...(p.recourse !== undefined ? { recourse: p.recourse } : {}),
201
+ });
202
+ }
203
+ export function retryStatusParts(status, ctx) {
176
204
  const modelName = washedText(noticeText(ctx, 'modelName'))?.trim();
177
- const prefix = modelName === undefined || modelName === '' ? '' : `${modelName}: `;
205
+ const model = modelName === undefined || modelName === '' ? undefined : modelName;
178
206
  if (status === null)
179
- return sealNotice('', 'cleared', undefined);
207
+ return sealParts({ phase: 'cleared' });
180
208
  const armWord = noticeText(status, 'kind');
181
209
  const kind = armWord === 'stalled' || armWord === 'waiting' ? armWord : 'error';
182
210
  if (kind === 'waiting') {
@@ -184,10 +212,8 @@ export function retryStatusNotice(status, ctx) {
184
212
  const timeoutRaw = noticeNumber(status, 'timeoutMs');
185
213
  const elapsed = elapsedRaw !== undefined && elapsedRaw >= 0 && elapsedRaw <= Number.MAX_SAFE_INTEGER ? elapsedRaw : undefined;
186
214
  const ceiling = timeoutRaw !== undefined && timeoutRaw > 0 && timeoutRaw <= Number.MAX_SAFE_INTEGER ? timeoutRaw : undefined;
187
- const progress = [...(elapsed === undefined ? [] : [`${spanText(elapsed)} so far`]), ...(ceiling === undefined ? [] : [`limit ${spanText(ceiling)}`])];
188
- const offerSwitch = prefix !== '' && elapsed !== undefined && ceiling !== undefined && elapsed >= ceiling * WAIT_SWITCH_FRACTION;
189
- const text = `${prefix}Waiting for the model's first token${progress.length === 0 ? '' : ` — ${progress.join(', ')}`}${offerSwitch ? '; keep waiting or switch model' : ''}`;
190
- return sealNotice(text, 'waiting_first_token', offerSwitch ? 'switch_model' : 'wait');
215
+ const offerSwitch = model !== undefined && elapsed !== undefined && ceiling !== undefined && elapsed >= ceiling * WAIT_SWITCH_FRACTION;
216
+ return sealParts({ phase: 'waiting_first_token', model, elapsedMs: elapsed, timeoutMs: ceiling, recourse: offerSwitch ? 'switch_model' : 'wait' });
191
217
  }
192
218
  const terminal = kind === 'error' && noticeValue(status, 'terminal') === true;
193
219
  const reading = conditionReadingOf(noticeText(status, 'providerCondition'));
@@ -195,18 +221,9 @@ export function retryStatusNotice(status, ctx) {
195
221
  const attempt = noticeCount(noticeNumber(status, 'attempt'));
196
222
  const maxRetries = noticeCount(noticeNumber(status, 'maxRetries'));
197
223
  const engineWords = kind === 'stalled' ? noticeText(status, 'detail') : noticeText(noticeValue(status, 'error'), 'formatted');
198
- const httpPart = http === undefined ? '' : ` (HTTP ${http})`;
199
- let first;
200
- if (terminal) {
201
- const after = attempt === undefined ? '' : ` after ${attempt} attempt${attempt === 1 ? '' : 's'}`;
202
- const why = reading === undefined ? '' : `: ${reading.clause}${reading.resendNote === undefined ? '' : `, and ${reading.resendNote}`}`;
203
- first = `Stopped${after}${why}${httpPart}`;
204
- }
205
- else {
206
- const head = reading !== undefined ? `${reading.clause.charAt(0).toUpperCase()}${reading.clause.slice(1)}` : http !== undefined || kind === 'error' ? 'Request failed' : 'Waiting for the API response';
207
- first = `${head}${httpPart} — ${kind === 'stalled' ? 'will retry' : 'retrying'}${noticeAttemptTail(attempt, maxRetries)}`;
208
- }
209
224
  const note = washedText(engineWords);
225
+ const source = reading === undefined ? undefined : sourcePhraseOf(noticeText(status, 'providerConditionSource'));
226
+ const condition = reading === undefined ? undefined : `${reading.clause.charAt(0).toUpperCase()}${reading.clause.slice(1)}`;
210
227
  const serviceSide = http !== undefined && (http === 429 || (http >= 500 && http <= 599));
211
228
  const knownArm = armWord === 'stalled' || armWord === 'error';
212
229
  const recourse = !knownArm
@@ -218,7 +235,47 @@ export function retryStatusNotice(status, ctx) {
218
235
  : !terminal
219
236
  ? 'wait'
220
237
  : undefined;
221
- return sealNotice(`${prefix}${first}${note === undefined ? '' : ` · ${note}`}`, kind === 'stalled' ? 'reconnecting' : terminal ? 'gave_up' : 'retrying', recourse);
238
+ const deadlineRaw = knownArm && !terminal && noticeNumber(status, 'retryAtMs') !== undefined ? noticeNumber(status, 'deadline') : undefined;
239
+ const retryAtMs = deadlineRaw !== undefined && deadlineRaw > 0 ? deadlineRaw : undefined;
240
+ return sealParts({
241
+ phase: kind === 'stalled' ? 'reconnecting' : terminal ? 'gave_up' : 'retrying',
242
+ model,
243
+ condition,
244
+ resendNote: terminal ? reading?.resendNote : undefined,
245
+ httpStatus: http,
246
+ source,
247
+ attempt,
248
+ maxAttempts: maxRetries,
249
+ retryAtMs,
250
+ detail: note,
251
+ recourse,
252
+ });
253
+ }
254
+ function retryLineOfParts(parts) {
255
+ if (parts.phase === 'cleared')
256
+ return '';
257
+ const prefix = parts.model === undefined ? '' : `${parts.model}: `;
258
+ if (parts.phase === 'waiting_first_token') {
259
+ const progress = [...(parts.elapsedMs === undefined ? [] : [`${spanText(parts.elapsedMs)} so far`]), ...(parts.timeoutMs === undefined ? [] : [`limit ${spanText(parts.timeoutMs)}`])];
260
+ const offerSwitch = parts.recourse === 'switch_model';
261
+ return `${prefix}Waiting for the model's first token${progress.length === 0 ? '' : ` — ${progress.join(', ')}`}${offerSwitch ? '; keep waiting or switch model' : ''}`;
262
+ }
263
+ const httpPart = parts.httpStatus === undefined ? '' : ` (HTTP ${parts.httpStatus})`;
264
+ let first;
265
+ if (parts.phase === 'gave_up') {
266
+ const after = parts.attempt === undefined ? '' : ` after ${parts.attempt} attempt${parts.attempt === 1 ? '' : 's'}`;
267
+ const why = parts.condition === undefined ? '' : `: ${parts.condition.charAt(0).toLowerCase()}${parts.condition.slice(1)}${parts.resendNote === undefined ? '' : `, and ${parts.resendNote}`}`;
268
+ first = `Stopped${after}${why}${httpPart}`;
269
+ }
270
+ else {
271
+ const head = parts.condition !== undefined ? parts.condition : parts.httpStatus !== undefined || parts.phase === 'retrying' ? 'Request failed' : 'Waiting for the API response';
272
+ first = `${head}${httpPart} — ${parts.phase === 'reconnecting' ? 'will retry' : 'retrying'}${noticeAttemptTail(parts.attempt, parts.maxAttempts)}`;
273
+ }
274
+ return `${prefix}${first}${parts.detail === undefined ? '' : ` · ${parts.detail}`}`;
275
+ }
276
+ export function retryStatusNotice(status, ctx) {
277
+ const parts = retryStatusParts(status, ctx);
278
+ return sealNotice(retryLineOfParts(parts), parts.phase, parts.recourse);
222
279
  }
223
280
  const API_ERROR_PROVIDER_TEXT_MAX = 200;
224
281
  function isRecordObject(v) {
@@ -0,0 +1,4 @@
1
+ import type { UiLanguage } from './env/uiLanguage.js';
2
+ import type { PermissionsKeptRule } from './permissionsDeclaration.js';
3
+ export type RuleRefusalRow = Pick<PermissionsKeptRule, 'rule' | 'reason' | 'code' | 'message'>;
4
+ export declare function ruleRefusalSentence(rows: readonly RuleRefusalRow[], lang?: UiLanguage): string | undefined;