@sema-agent/client-core 0.110.0 → 0.112.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 (243) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +59 -18
  3. package/dist/abortableSleep.d.ts +7 -0
  4. package/dist/abortableSleep.js +37 -0
  5. package/dist/adapt/arms.js +25 -4
  6. package/dist/adapt/toolCards.d.ts +3 -1
  7. package/dist/adapt/toolCards.js +4 -2
  8. package/dist/adapter/activeRunSelfHeal.d.ts +39 -0
  9. package/dist/adapter/activeRunSelfHeal.js +171 -3
  10. package/dist/adapter/downstream/eventToSdkMessage.js +10 -10
  11. package/dist/adapter/downstream/terminalToSdkResult.d.ts +2 -0
  12. package/dist/adapter/downstream/terminalToSdkResult.js +11 -0
  13. package/dist/adapter/runStream.d.ts +5 -0
  14. package/dist/adapter/runStream.js +15 -3
  15. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  16. package/dist/attachmentRoute.d.ts +20 -0
  17. package/dist/attachmentRoute.js +39 -0
  18. package/dist/attachmentsWireCaps.js +1 -1
  19. package/dist/builtinOutputStyles.d.ts +12 -0
  20. package/dist/builtinOutputStyles.js +147 -0
  21. package/dist/checkpointRowVerdict.d.ts +3 -0
  22. package/dist/checkpointRowVerdict.js +14 -1
  23. package/dist/compactionStatus.d.ts +9 -0
  24. package/dist/compactionStatus.js +29 -5
  25. package/dist/consoleCaPin.d.ts +136 -0
  26. package/dist/consoleCaPin.js +827 -0
  27. package/dist/controlRouter.js +2 -1
  28. package/dist/coreValuePorts.d.ts +39 -1
  29. package/dist/coreValuePorts.js +10 -0
  30. package/dist/decideReceipt.d.ts +2 -1
  31. package/dist/deviceRebind.js +3 -3
  32. package/dist/displayUntrusted.d.ts +1 -0
  33. package/dist/displayUntrusted.js +6 -0
  34. package/dist/effectiveFacts.d.ts +9 -0
  35. package/dist/effectiveFacts.js +33 -0
  36. package/dist/engineAddress.d.ts +1 -0
  37. package/dist/engineAddress.js +4 -1
  38. package/dist/engineCredentialRelease.d.ts +54 -0
  39. package/dist/engineCredentialRelease.js +146 -0
  40. package/dist/engineErrorCodes.d.ts +1 -0
  41. package/dist/engineErrorCodes.js +1 -0
  42. package/dist/engineHealthProbeVerdict.d.ts +24 -0
  43. package/dist/engineHealthProbeVerdict.js +123 -0
  44. package/dist/engineNoticeCodes.js +5 -7
  45. package/dist/engineRepoint.d.ts +12 -5
  46. package/dist/engineRepoint.js +44 -19
  47. package/dist/engineRowRef.d.ts +3 -0
  48. package/dist/engineRowRef.js +11 -1
  49. package/dist/engineSwapCrossing.d.ts +57 -0
  50. package/dist/engineSwapCrossing.js +297 -0
  51. package/dist/engineUnavailable.d.ts +45 -0
  52. package/dist/engineUnavailable.js +496 -0
  53. package/dist/engineUrl.js +4 -2
  54. package/dist/eventLeg.js +3 -3
  55. package/dist/gateFailureCodes.js +2 -2
  56. package/dist/generated/sdkFactTables.d.ts +2 -1
  57. package/dist/generated/sdkFactTables.js +14 -0
  58. package/dist/generated/settingsSchemaFns.d.ts +1 -0
  59. package/dist/generated/settingsSchemaFns.js +3 -0
  60. package/dist/generated/settingsSchemaHooksClassifier.js +38 -9
  61. package/dist/generated/settingsSchemaTables.d.ts +56 -0
  62. package/dist/generated/settingsSchemaTables.js +19 -0
  63. package/dist/generated/settingsSchemaTypes.d.ts +23 -13
  64. package/dist/generated/settingsSchemaUrlShape.d.ts +1 -0
  65. package/dist/generated/settingsSchemaUrlShape.js +17 -0
  66. package/dist/hitl/approvalDelivery.d.ts +1 -0
  67. package/dist/hitl/approvalDelivery.js +1 -1
  68. package/dist/hitl/approvalResolution.d.ts +1 -1
  69. package/dist/hitl/approvalResolution.js +2 -1
  70. package/dist/hitl/askGateWire.js +5 -6
  71. package/dist/hitl/decisionRefusal.d.ts +6 -0
  72. package/dist/hitl/decisionRefusal.js +19 -10
  73. package/dist/hitl/decisionRefusalCarrier.d.ts +3 -0
  74. package/dist/hitl/decisionRefusalCarrier.js +348 -0
  75. package/dist/hitl/decisionSend.d.ts +7 -0
  76. package/dist/hitl/decisionSend.js +34 -12
  77. package/dist/hitl/editedApprovalAcceptance.js +1 -1
  78. package/dist/hitl/elicitationRefusal.d.ts +4 -0
  79. package/dist/hitl/elicitationRefusal.js +23 -0
  80. package/dist/hitl/frameRouter.d.ts +2 -2
  81. package/dist/hitl/frameRouter.js +8 -15
  82. package/dist/hitl/gateIdentity.d.ts +5 -0
  83. package/dist/hitl/gateIdentity.js +23 -0
  84. package/dist/hitl/hitlBridge.d.ts +6 -0
  85. package/dist/hitl/hitlBridge.js +10 -4
  86. package/dist/hitl/hitlHostSurface.d.ts +2 -1
  87. package/dist/hitl/hitlHostSurface.js +7 -2
  88. package/dist/hitl/localAllowRedemption.d.ts +70 -0
  89. package/dist/hitl/localAllowRedemption.js +276 -0
  90. package/dist/hitl/parkOwnership.js +2 -1
  91. package/dist/hitl/parkResolver.js +14 -4
  92. package/dist/hitl/persistedRulesWire.d.ts +16 -0
  93. package/dist/hitl/persistedRulesWire.js +70 -0
  94. package/dist/hitl/planReviewWire.d.ts +3 -0
  95. package/dist/hitl/planReviewWire.js +23 -6
  96. package/dist/hitl/policyDenyCause.d.ts +1 -1
  97. package/dist/hitl/policyDenyCause.js +1 -1
  98. package/dist/hitl/resumeRunningCard.d.ts +1 -0
  99. package/dist/hitl/resumeRunningCard.js +1 -0
  100. package/dist/hitl/screenCardCarriers.d.ts +15 -0
  101. package/dist/hitl/screenCardCarriers.js +41 -0
  102. package/dist/hitl/screenDecisionCards.d.ts +98 -0
  103. package/dist/hitl/screenDecisionCards.js +363 -0
  104. package/dist/hitl/sessionPolicyWire.js +2 -2
  105. package/dist/hitl/standingRows.d.ts +6 -2
  106. package/dist/hitl/standingRows.js +27 -2
  107. package/dist/hitl/toolApprovalWire.d.ts +30 -8
  108. package/dist/hitl/toolApprovalWire.js +264 -121
  109. package/dist/hooksReaderGenerations.js +1 -1
  110. package/dist/hooksWireCaps.js +3 -3
  111. package/dist/hostSettingNoticeArm.d.ts +3 -0
  112. package/dist/hostSettingNoticeArm.js +12 -0
  113. package/dist/hostTurnSettings.d.ts +3 -0
  114. package/dist/hostTurnSettings.js +38 -5
  115. package/dist/humanTurnRead.js +11 -1
  116. package/dist/i18n/messages.en.d.ts +292 -0
  117. package/dist/i18n/messages.en.js +298 -7
  118. package/dist/i18n/messages.zh.js +316 -6
  119. package/dist/i18n/translate.d.ts +3 -0
  120. package/dist/i18n/translate.js +78 -10
  121. package/dist/i18n/zh.d.ts +1 -0
  122. package/dist/i18n/zh.js +1 -0
  123. package/dist/index.d.ts +71 -20
  124. package/dist/index.js +44 -13
  125. package/dist/interactiveHalt.js +2 -1
  126. package/dist/interactiveInterrupt.js +20 -2
  127. package/dist/liveModelCatalog.d.ts +4 -0
  128. package/dist/liveModelCatalog.js +12 -3
  129. package/dist/localConfig/documents.d.ts +61 -0
  130. package/dist/localConfig/documents.js +51 -0
  131. package/dist/localConfig/kernel.d.ts +149 -0
  132. package/dist/localConfig/kernel.js +597 -0
  133. package/dist/localConfig/mcp/edit.d.ts +161 -0
  134. package/dist/localConfig/mcp/edit.js +846 -0
  135. package/dist/localConfig/mcp/servers.d.ts +165 -0
  136. package/dist/localConfig/mcp/servers.js +776 -0
  137. package/dist/localConfig/mcp/views.d.ts +170 -0
  138. package/dist/localConfig/mcp/views.js +429 -0
  139. package/dist/localConfig/modelPool.d.ts +262 -0
  140. package/dist/localConfig/modelPool.js +1489 -0
  141. package/dist/localConfig/modelPoolWords.d.ts +13 -0
  142. package/dist/localConfig/modelPoolWords.js +20 -0
  143. package/dist/localConfig/receiptShape.d.ts +7 -0
  144. package/dist/localConfig/receiptShape.js +135 -0
  145. package/dist/localConfig/settingsWrite.d.ts +127 -0
  146. package/dist/localConfig/settingsWrite.js +237 -0
  147. package/dist/localConfig/skills.d.ts +124 -0
  148. package/dist/localConfig/skills.js +397 -0
  149. package/dist/localConfig/skillsWords.d.ts +10 -0
  150. package/dist/localConfig/skillsWords.js +5 -0
  151. package/dist/localEngineSeatReading.d.ts +2 -2
  152. package/dist/localEngineSeatReading.js +5 -3
  153. package/dist/localEngineSpawn.d.ts +0 -7
  154. package/dist/localEngineSpawn.js +0 -92
  155. package/dist/localEngineStep.d.ts +2 -2
  156. package/dist/localEngineStep.js +2 -0
  157. package/dist/mcpProbeWire.js +2 -1
  158. package/dist/mcpReconnect.js +3 -3
  159. package/dist/mcpWireCaps.js +1 -1
  160. package/dist/memoryVerbsWire.js +3 -2
  161. package/dist/model/engineModelWords.d.ts +3 -0
  162. package/dist/model/engineModelWords.js +13 -0
  163. package/dist/model/onboardJudgement.d.ts +14 -4
  164. package/dist/model/onboardJudgement.js +91 -139
  165. package/dist/model/subagentModelWord.d.ts +21 -2
  166. package/dist/model/subagentModelWord.js +100 -29
  167. package/dist/modelCapabilityProbe.js +3 -40
  168. package/dist/modelConfigGapNotice.d.ts +20 -0
  169. package/dist/modelConfigGapNotice.js +102 -0
  170. package/dist/modsKernelIdentity.js +2 -47
  171. package/dist/notifications.d.ts +19 -0
  172. package/dist/notifications.js +114 -12
  173. package/dist/ownKey.d.ts +7 -0
  174. package/dist/ownKey.js +71 -8
  175. package/dist/pendingUserRow.d.ts +4 -0
  176. package/dist/pendingUserRow.js +16 -0
  177. package/dist/permissionsDeclaration.d.ts +3 -1
  178. package/dist/permissionsDeclaration.js +33 -9
  179. package/dist/planReviewCardFacts.d.ts +3 -0
  180. package/dist/planReviewCardFacts.js +52 -2
  181. package/dist/pluginHooksWire.d.ts +9 -1
  182. package/dist/pluginHooksWire.js +40 -6
  183. package/dist/printToolResultFrame.js +4 -2
  184. package/dist/promptBlockedCopy.js +12 -7
  185. package/dist/promptSuggestionBatch.d.ts +7 -0
  186. package/dist/promptSuggestionBatch.js +9 -0
  187. package/dist/promptSuggestionsTail.d.ts +39 -0
  188. package/dist/promptSuggestionsTail.js +116 -0
  189. package/dist/request/taskRequest.js +10 -6
  190. package/dist/requestCoordinate.d.ts +1 -0
  191. package/dist/requestCoordinate.js +13 -0
  192. package/dist/resumeRefusalCopy.d.ts +3 -1
  193. package/dist/resumeRefusalCopy.js +12 -4
  194. package/dist/rewindArchiveReading.js +4 -4
  195. package/dist/runCancelContext.d.ts +2 -1
  196. package/dist/runCancelContext.js +1 -0
  197. package/dist/runLedgerSnapshot.js +3 -3
  198. package/dist/runPermissionModeSwitch.js +3 -4
  199. package/dist/scenarioRefusal.js +11 -13
  200. package/dist/scenarioSkills.js +11 -6
  201. package/dist/seam.d.ts +16 -2
  202. package/dist/seam.js +10 -0
  203. package/dist/seatContract.d.ts +259 -9
  204. package/dist/seatContract.js +444 -15
  205. package/dist/sessionCostReading.d.ts +70 -0
  206. package/dist/sessionCostReading.js +150 -0
  207. package/dist/sessionHooksRoster.d.ts +4 -0
  208. package/dist/sessionHooksRoster.js +158 -0
  209. package/dist/sessionIdSegment.d.ts +1 -0
  210. package/dist/sessionIdSegment.js +4 -1
  211. package/dist/sessionMemoryErase.js +2 -1
  212. package/dist/sessionMemoryStatus.js +2 -2
  213. package/dist/sessionReadRedaction.d.ts +7 -0
  214. package/dist/sessionReadRedaction.js +26 -0
  215. package/dist/sessionSyncFileHistory.js +8 -9
  216. package/dist/settingsHooksSalvage.js +2 -1
  217. package/dist/sha256Bytes.d.ts +1 -0
  218. package/dist/sha256Bytes.js +46 -0
  219. package/dist/skillOverrideChain.d.ts +83 -0
  220. package/dist/skillOverrideChain.js +378 -0
  221. package/dist/skillsWireCaps.d.ts +3 -0
  222. package/dist/skillsWireCaps.js +16 -1
  223. package/dist/sseIdleTriage.js +2 -1
  224. package/dist/storeDurabilityWord.d.ts +15 -0
  225. package/dist/storeDurabilityWord.js +18 -0
  226. package/dist/storePostureCapability.d.ts +7 -3
  227. package/dist/storePostureCapability.js +69 -39
  228. package/dist/subagent/engineSubagentResume.js +2 -2
  229. package/dist/subagent/engineSubagentTail.js +2 -1
  230. package/dist/subagentContentStore.d.ts +11 -0
  231. package/dist/subagentContentStore.js +28 -2
  232. package/dist/toolResult.d.ts +1 -0
  233. package/dist/toolResult.js +2 -0
  234. package/dist/turnFailurePresentation.js +4 -10
  235. package/dist/userAttachmentEnvelope.d.ts +2 -1
  236. package/dist/userAttachmentEnvelope.js +10 -0
  237. package/dist/wireErrorTriage.js +2 -1
  238. package/dist/wireFailureShape.d.ts +4 -0
  239. package/dist/wireFailureShape.js +141 -19
  240. package/dist/wireRefusalCopy.d.ts +6 -5
  241. package/dist/wireRefusalCopy.js +49 -92
  242. package/docs/INTEGRATION-CLIENTS.md +2718 -17
  243. package/package.json +7 -3
@@ -5,6 +5,7 @@ import { allowRulesGate, allowWireForm } from './permissionsAllowGrantCapability
5
5
  import { SETTINGS_IS_AT_LEAST } from './generated/settingsSchemaFns.js';
6
6
  import { THROWN_SLOT_UNREADABLE, asAssemblyRefusal, assemblyRefusalOf, isSlotCarrier, readThrownSlot, withAssemblyRefusal } from './wireFailureShape.js';
7
7
  import { appendOwnElement } from './ownKey.js';
8
+ import { msg, renderMessage } from './i18n/translate.js';
8
9
  const SETTINGS_SOURCE_TIERS = Object.freeze({
9
10
  policySettings: 'managed',
10
11
  flagSettings: 'global',
@@ -530,7 +531,24 @@ function allowHeldBySettingsReadCount(report) {
530
531
  }
531
532
  return n;
532
533
  }
533
- export function permissionsAllowKeptNotice(report, settingsRead, facts) {
534
+ const ALLOW_KEPT_MANAGED_KEYS = Object.freeze({
535
+ in_progress: Object.freeze({ one: 'session.hostSettings.allowKept.managed.inProgress.one', other: 'session.hostSettings.allowKept.managed.inProgress.other' }),
536
+ failed: Object.freeze({ one: 'session.hostSettings.allowKept.managed.failed.one', other: 'session.hostSettings.allowKept.managed.failed.other' }),
537
+ unknown: Object.freeze({ one: 'session.hostSettings.allowKept.managed.unknown.one', other: 'session.hostSettings.allowKept.managed.unknown.other' }),
538
+ });
539
+ const ALLOW_KEPT_GENERIC_KEYS = Object.freeze({
540
+ one: 'session.hostSettings.allowKept.generic.one',
541
+ other: 'session.hostSettings.allowKept.generic.other',
542
+ });
543
+ const ALLOW_KEPT_COUNTED_KEYS = Object.freeze({
544
+ one: 'session.hostSettings.allowKept.why.counted.one',
545
+ other: 'session.hostSettings.allowKept.why.counted.other',
546
+ });
547
+ const ALLOW_KEPT_COUNTED_WHERE_KEYS = Object.freeze({
548
+ one: 'session.hostSettings.allowKept.why.countedWhere.one',
549
+ other: 'session.hostSettings.allowKept.why.countedWhere.other',
550
+ });
551
+ export function permissionsAllowKeptNotice(report, settingsRead, facts, lang) {
534
552
  const held = allowHeldBySettingsReadCount(report);
535
553
  if (held <= 0)
536
554
  return undefined;
@@ -543,16 +561,22 @@ export function permissionsAllowKeptNotice(report, settingsRead, facts) {
543
561
  const rawCount = readFact('problemCount');
544
562
  const rawWhere = readFact('where');
545
563
  const managedPending = readFact('managedPolicyPending') === true;
564
+ const rawFetch = readFact('managedPolicyFetch');
565
+ const fetchState = rawFetch === 'in_progress' || rawFetch === 'failed' ? rawFetch : 'unknown';
546
566
  const count = typeof rawCount === 'number' && Number.isInteger(rawCount) && rawCount >= 1 ? rawCount : undefined;
547
567
  const where = typeof rawWhere === 'string' && rawWhere.length >= 1 && rawWhere.length <= 200 && !/\p{Cc}/u.test(rawWhere) ? rawWhere : undefined;
548
- const head = `${String(held)} permissions.allow rule${held === 1 ? ' was' : 's were'} not sent to the engine for this run because `;
568
+ const plural = held === 1 ? 'one' : 'other';
549
569
  if (settingsRead === 'partial' && managedPending && count === 1) {
550
- return (`${head}the managed policy for your account has not been received yet (sema keeps fetching it; allow rules apply once it arrives). ` +
551
- 'Run again once it has been received (deny and ask rules, and rules given with --allowedTools, are still sent).');
570
+ return renderMessage(msg(ALLOW_KEPT_MANAGED_KEYS[fetchState][plural], { held }), lang);
552
571
  }
553
- const why = settingsRead === 'partial'
554
- ? `the settings read reported ${count === undefined ? 'problems' : `${String(count)} problem${count === 1 ? '' : 's'}`}${where === undefined ? '' : ` (${where})`}`
555
- : 'the health of this settings read could not be checked';
556
- return (`${head}${why}; an allow rule from a settings file is only sent from a settings read with no problems. Fix the reported settings, then run again ` +
557
- '(deny and ask rules from this read, and rules given with --allowedTools, are still sent).');
572
+ const why = settingsRead !== 'partial'
573
+ ? msg('session.hostSettings.allowKept.why.unreadable')
574
+ : count === undefined
575
+ ? where === undefined
576
+ ? msg('session.hostSettings.allowKept.why.problems')
577
+ : msg('session.hostSettings.allowKept.why.problemsWhere', { where })
578
+ : where === undefined
579
+ ? msg(ALLOW_KEPT_COUNTED_KEYS[count === 1 ? 'one' : 'other'], { count })
580
+ : msg(ALLOW_KEPT_COUNTED_WHERE_KEYS[count === 1 ? 'one' : 'other'], { count, where });
581
+ return renderMessage(msg(ALLOW_KEPT_GENERIC_KEYS[plural], { held, why }), lang);
558
582
  }
@@ -16,6 +16,7 @@ export interface PlanReviewCardFacts {
16
16
  readonly taskId: string;
17
17
  readonly checkpointId?: string;
18
18
  readonly body: PlanReviewBody;
19
+ readonly evidenceRevision?: number;
19
20
  }
20
21
  export declare function planReviewBodyOf(text: string): Exclude<PlanReviewBody, {
21
22
  kind: 'unknown';
@@ -91,10 +92,12 @@ export interface PlanBodyClaimLeg {
91
92
  }>;
92
93
  overflow: boolean;
93
94
  unreadable: boolean;
95
+ readonly seen: Set<string>;
94
96
  }
95
97
  export declare function openPlanBodyClaimLeg(): PlanBodyClaimLeg;
96
98
  export declare function stepPlanBodyClaim(leg: PlanBodyClaimLeg, frame: object, subFlow: boolean): void;
97
99
  export declare function planReviewParkFacts(leg: PlanBodyClaimLeg, result: {} | null | undefined): PlanReviewCardFacts | undefined;
100
+ export declare function planReviewClaimStep(leg: PlanBodyClaimLeg, frame: object, subFlow: boolean, seq: string | undefined): PlanReviewCardFacts | undefined;
98
101
  export interface PlanReviewFactsTaken {
99
102
  readonly sessionKey: string;
100
103
  readonly taskId: string;
@@ -3,6 +3,7 @@ import { readRunTerminal, runTerminalGateKind } from './runTerminal.js';
3
3
  import { SDK_PLAN_REVIEW_GATE_KINDS } from './generated/sdkFactTables.js';
4
4
  import { CORE_PRESENT_PLAN_TOOL_NAME } from './generated/engineFactTables.js';
5
5
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
6
+ import { doneResultView } from './replayBody.js';
6
7
  const PLAN_BODY_MAX_LENGTH = 200_000;
7
8
  const PLAN_BODY_UNKNOWN_CAUSES = Object.freeze([
8
9
  'not_observed', 'gate_unidentified', 'gate_moved', 'unreadable', 'ambiguous_call', 'malformed_frame', 'conflicting_claims', 'not_presented', 'not_carried',
@@ -29,6 +30,13 @@ export function mintPlanReviewCardFacts(taskId, checkpointId, body) {
29
30
  const b = checkpointId === undefined ? unknownBody('gate_unidentified') : body;
30
31
  return Object.freeze({ taskId, ...(checkpointId !== undefined ? { checkpointId } : {}), body: Object.isFrozen(b) ? b : Object.freeze({ ...b }) });
31
32
  }
33
+ let evidenceRevisionSeq = 0;
34
+ function withEvidenceRevision(facts, evidenceRevision) {
35
+ return Object.freeze({ taskId: facts.taskId, ...(facts.checkpointId !== undefined ? { checkpointId: facts.checkpointId } : {}), body: facts.body, evidenceRevision });
36
+ }
37
+ function isEvidenceRevision(v) {
38
+ return typeof v === 'number' && Number.isSafeInteger(v) && v > 0;
39
+ }
32
40
  export function unknownBody(cause) {
33
41
  return Object.freeze({ kind: 'unknown', cause });
34
42
  }
@@ -59,7 +67,11 @@ export function planReviewCardFactsFrom(v) {
59
67
  const checkpointId = ck.present ? ck.value : undefined;
60
68
  if (checkpointId === undefined && !(body.kind === 'unknown' && body.cause === 'gate_unidentified'))
61
69
  return undefined;
62
- return mintPlanReviewCardFacts(taskId, checkpointId, body);
70
+ const eg = ownData(v, 'evidenceRevision');
71
+ if (eg.present && !isEvidenceRevision(eg.value))
72
+ return undefined;
73
+ const minted = mintPlanReviewCardFacts(taskId, checkpointId, body);
74
+ return eg.present ? withEvidenceRevision(minted, eg.value) : minted;
63
75
  }
64
76
  catch {
65
77
  return undefined;
@@ -167,6 +179,10 @@ export function commitPlanReviewFacts(ticket, facts, claim, highWater) {
167
179
  if (prev !== undefined && prev.checkpointId === checkpointId && (isConflict(prev.facts) || !samePlanClaim(prev.claim, claim))) {
168
180
  out = mintPlanReviewCardFacts(facts.taskId, checkpointId, unknownBody('conflicting_claims'));
169
181
  }
182
+ const reused = prev !== undefined && prev.checkpointId === checkpointId && prev.facts.evidenceRevision !== undefined && samePlanReviewCardFacts(prev.facts, out)
183
+ ? prev.facts.evidenceRevision
184
+ : undefined;
185
+ out = withEvidenceRevision(out, reused ?? ++evidenceRevisionSeq);
170
186
  rec.gate = { checkpointId, facts: out, claim, generation: ticket.generation, ...(highWater !== undefined ? { highWater } : {}) };
171
187
  return out;
172
188
  }
@@ -338,7 +354,7 @@ function foldMarker(rec, frame) {
338
354
  }
339
355
  const LEG_BUFFER_MAX = 256;
340
356
  export function openPlanBodyClaimLeg() {
341
- return { buffer: [], overflow: false, unreadable: false };
357
+ return { buffer: [], overflow: false, unreadable: false, seen: new Set() };
342
358
  }
343
359
  function bindLeg(leg, taskId) {
344
360
  const rec = recordFor(DEFAULT_SESSION_KEY, taskId);
@@ -425,6 +441,40 @@ export function planReviewParkFacts(leg, result) {
425
441
  return taskId === undefined ? undefined : mintPlanReviewCardFacts(taskId, checkpointId, unknownBody('unreadable'));
426
442
  }
427
443
  }
444
+ export function planReviewClaimStep(leg, frame, subFlow, seq) {
445
+ if (claimSeqDedupe(leg, frame, seq) === 'duplicate')
446
+ return undefined;
447
+ stepPlanBodyClaim(leg, frame, subFlow);
448
+ let result;
449
+ try {
450
+ if (frame.type !== 'done')
451
+ return undefined;
452
+ result = frame.result;
453
+ }
454
+ catch {
455
+ return undefined;
456
+ }
457
+ return planReviewParkFacts(leg, doneResultView(result).body);
458
+ }
459
+ function claimSeqDedupe(leg, frame, seq) {
460
+ try {
461
+ if (seq !== undefined && classOf(frame) !== 'other') {
462
+ if (leg.seen.has(seq))
463
+ return 'duplicate';
464
+ leg.seen.add(seq);
465
+ while (leg.seen.size > SETTLED_CALLS_MAX) {
466
+ const oldest = leg.seen.values().next();
467
+ if (oldest.done === true)
468
+ break;
469
+ leg.seen.delete(oldest.value);
470
+ }
471
+ }
472
+ return 'fresh';
473
+ }
474
+ catch {
475
+ return 'class_unreadable';
476
+ }
477
+ }
428
478
  function legRecord(leg, taskId) {
429
479
  return leg.taskId === undefined || leg.taskId === taskId ? bindLeg(leg, taskId) : recordFor(DEFAULT_SESSION_KEY, taskId);
430
480
  }
@@ -12,8 +12,10 @@ export declare const PLUGIN_HOOK_REASONS: readonly ["projected", "shell_event",
12
12
  export type PluginHookReason = (typeof PLUGIN_HOOK_REASONS)[number];
13
13
  export declare const PLUGIN_HOOK_EXCLUSION_REASONS: readonly ["workspace_untrusted", "governance_unreadable", "policy_disable_all", "startup_mode", "managed_plugins_only", "prompt_not_user_authored"];
14
14
  export type PluginHookExclusionReason = (typeof PLUGIN_HOOK_EXCLUSION_REASONS)[number];
15
- export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported", "key_not_read", "source_not_authored", "workspace_untrusted", "governance_unreadable", "policy_disable_all"];
15
+ export declare const SETTINGS_HOOK_DROP_REASONS: readonly ["exec_form_unsupported", "mcp_tool_unsupported", "key_not_read", "source_not_authored", "workspace_untrusted", "governance_unreadable", "policy_disable_all", "no_executor"];
16
16
  export type SettingsHookDropReason = (typeof SETTINGS_HOOK_DROP_REASONS)[number];
17
+ export type HookEntryTypeWord = 'command' | 'prompt' | 'agent' | 'http' | 'mcp_tool';
18
+ export declare const HOOK_ENTRY_TYPE_WORDS: readonly ["command", "prompt", "agent", "http", "mcp_tool"];
17
19
  export type HookSettingsSource = 'policySettings' | 'userSettings' | 'projectSettings' | 'localSettings' | 'flagSettings';
18
20
  export interface PluginHookKey {
19
21
  readonly pluginId: string;
@@ -34,6 +36,7 @@ export interface SettingsHookDrop {
34
36
  readonly event: string;
35
37
  readonly groupIndex: number;
36
38
  readonly hookIndex: number;
39
+ readonly type?: HookEntryTypeWord;
37
40
  readonly reason: SettingsHookDropReason;
38
41
  readonly notRun: boolean;
39
42
  readonly unreadKeys?: readonly (readonly (string | number)[])[];
@@ -43,6 +46,7 @@ export interface SettingsHookVerdict {
43
46
  readonly event: string;
44
47
  readonly groupIndex: number;
45
48
  readonly hookIndex: number;
49
+ readonly type?: HookEntryTypeWord;
46
50
  readonly disposition: PluginHookDisposition;
47
51
  readonly reason: PluginHookReason;
48
52
  }
@@ -51,6 +55,7 @@ export interface SettingsHookExclusion {
51
55
  readonly event: string;
52
56
  readonly groupIndex: number;
53
57
  readonly hookIndex: number;
58
+ readonly type?: HookEntryTypeWord;
54
59
  readonly reason: PluginHookExclusionReason;
55
60
  }
56
61
  export interface SettingsHooksSalvageRow {
@@ -169,6 +174,9 @@ export declare function decideSessionHooks(input: SessionHookDecisionInput): {
169
174
  readonly unreadable: number;
170
175
  };
171
176
  export declare function filterSettingsHookGroups(source: HookSettingsSource, event: string, groups: readonly WireHookGroup[], execFormCapable: boolean, shellRunsType: (type: string | undefined) => boolean, judge: SettingsHookJudge, readerNotRead: readonly string[], mask?: HooksSalvageMask): SettingsHookFilterResult;
177
+ export declare function hookEntryTypeWordOf(entry: {} | null | undefined): {
178
+ readonly type?: HookEntryTypeWord;
179
+ };
172
180
  export interface SettingsHookFilterResult {
173
181
  readonly groups: WireHookGroup[];
174
182
  readonly drops: SettingsHookDrop[];
@@ -3,6 +3,7 @@ import { appendOwnElement, readOwnDataValue } from './ownKey.js';
3
3
  import { inSlotReadOf } from './wireFailureShape.js';
4
4
  import { msg, renderMessage } from './i18n/translate.js';
5
5
  import { SETTINGS_ENGINE_OWNED_HOOK_EVENTS } from './generated/settingsSchemaTables.js';
6
+ import { SETTINGS_HOOK_EVENTS } from './generated/settingsSchemaFns.js';
6
7
  import { HOOK_SETTINGS_SOURCE_TEXT, hooksEntryUnreadKeys, hooksGroupAuthoredSource, hooksGroupUnreadKeys, hooksKeyCoordinateText, hooksSalvageMaskOf, salvageSettingsHooksEntries, } from './settingsHooksSalvage.js';
7
8
  export const ENGINE_FIRED_HOOK_EVENTS = SETTINGS_ENGINE_OWNED_HOOK_EVENTS;
8
9
  export const PLUGIN_HOOK_DISPOSITIONS = ['engine', 'shell', 'not_run'];
@@ -49,7 +50,11 @@ export const SETTINGS_HOOK_DROP_REASONS = [
49
50
  'workspace_untrusted',
50
51
  'governance_unreadable',
51
52
  'policy_disable_all',
53
+ 'no_executor',
52
54
  ];
55
+ export const HOOK_ENTRY_TYPE_WORDS = Object.freeze(['command', 'prompt', 'agent', 'http', 'mcp_tool']);
56
+ const _hookEntryTypePins = [true, true, true, true];
57
+ void _hookEntryTypePins;
53
58
  export function hooksWirePlanShapeOf(v) {
54
59
  try {
55
60
  if (typeof v !== 'object' || v === null || Array.isArray(v))
@@ -144,6 +149,7 @@ function hookExecutorOf(x) {
144
149
  return judged(x.shellRuns ? 'shell' : 'not_run', why);
145
150
  }
146
151
  const ENGINE_EVENTS = new Set(ENGINE_FIRED_HOOK_EVENTS);
152
+ const SHELL_OWNED_EVENTS = new Set(SETTINGS_HOOK_EVENTS.filter((e) => !ENGINE_EVENTS.has(e)));
147
153
  const USER_CONFIG_REF_ONE = /\$\{user_config\.[^}]+\}/;
148
154
  const USER_CONFIG_REF_ALL = /\$\{user_config\.([^}]+)\}/g;
149
155
  function isRecord(v) {
@@ -493,10 +499,23 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
493
499
  const excluded = [];
494
500
  const out = [];
495
501
  let stripped = 0;
496
- const engineEvent = ENGINE_EVENTS.has(event);
502
+ const engineEvent = isEngineFiredHookEvent(event);
503
+ const shellOwned = !engineEvent && SHELL_OWNED_EVENTS.has(event);
497
504
  groups.forEach((group, groupIndex) => {
498
505
  if (mask !== undefined && mask.groupDropped(event, groupIndex))
499
506
  return;
507
+ if (!engineEvent) {
508
+ if (!shellOwned || !isRecord(group) || !Array.isArray(group.hooks))
509
+ return;
510
+ group.hooks.forEach((entry, hookIndex) => {
511
+ if (mask !== undefined && mask.entryDropped(event, groupIndex, hookIndex))
512
+ return;
513
+ if (shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined))
514
+ return;
515
+ appendOwnElement(drops, { source, event, groupIndex, hookIndex, reason: 'no_executor', notRun: true });
516
+ });
517
+ return;
518
+ }
500
519
  if (!isRecord(group) || !Array.isArray(group.hooks)) {
501
520
  appendOwnElement(out, group);
502
521
  return;
@@ -522,27 +541,29 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
522
541
  const sourceNotAuthored = earlier === undefined && groupSourceAuthored && (authored === 'unreadable' || entryUnread !== undefined);
523
542
  const unreadKeys = earlier === undefined && !sourceNotAuthored ? unreadKeyPathsOf(event, groupIndex, hookIndex, groupUnread ?? [], entryUnread) : [];
524
543
  const reason = earlier ?? (sourceNotAuthored ? 'source_not_authored' : unreadKeys.length > 0 ? 'key_not_read' : undefined);
544
+ const typed = hookEntryTypeWordOf(entry);
525
545
  if (reason !== undefined) {
526
546
  appendOwnElement(drops, {
527
547
  source,
528
548
  event,
529
549
  groupIndex,
530
550
  hookIndex,
551
+ ...typed,
531
552
  reason,
532
- notRun: engineEvent && !shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined),
553
+ notRun: !shellRunsType(isRecord(entry) && typeof entry.type === 'string' ? entry.type : undefined),
533
554
  ...(reason === 'key_not_read' ? { unreadKeys } : {}),
534
555
  });
535
556
  changed = true;
536
557
  return;
537
558
  }
538
- if (engineEvent && isRecord(entry) && judgeableSettingsEntry(entry)) {
559
+ if (isRecord(entry) && judgeableSettingsEntry(entry)) {
539
560
  const ex = judge(event, group, entry);
540
561
  if (ex.kind === 'excluded') {
541
- appendOwnElement(excluded, { source, event, groupIndex, hookIndex, reason: ex.reason });
562
+ appendOwnElement(excluded, { source, event, groupIndex, hookIndex, ...typed, reason: ex.reason });
542
563
  changed = true;
543
564
  return;
544
565
  }
545
- appendOwnElement(verdicts, { source, event, groupIndex, hookIndex, disposition: ex.disposition, reason: ex.reason });
566
+ appendOwnElement(verdicts, { source, event, groupIndex, hookIndex, ...typed, disposition: ex.disposition, reason: ex.reason });
546
567
  if (ex.disposition === 'shell') {
547
568
  changed = true;
548
569
  return;
@@ -567,6 +588,17 @@ export function filterSettingsHookGroups(source, event, groups, execFormCapable,
567
588
  });
568
589
  return { groups: out, drops, stripped, verdicts, excluded };
569
590
  }
591
+ export function hookEntryTypeWordOf(entry) {
592
+ try {
593
+ if (!isRecord(entry))
594
+ return {};
595
+ const t = readOwnDataValue(entry, 'type');
596
+ return typeof t === 'string' && HOOK_ENTRY_TYPE_WORDS.includes(t) ? { type: t } : {};
597
+ }
598
+ catch {
599
+ return {};
600
+ }
601
+ }
570
602
  function judgeableSettingsEntry(entry) {
571
603
  const type = entry.type;
572
604
  if (typeof type !== 'string' || !ENGINE_HOOK_TYPE_RUNS.has(type))
@@ -899,7 +931,7 @@ function settingsExclusionRowOf(v) {
899
931
  const REASON_TEXT = {
900
932
  projected: 'it is sent to the engine',
901
933
  shell_event: 'this client runs this event itself',
902
- no_executor: 'neither the engine nor this client runs this event',
934
+ no_executor: renderMessage(msg('config.hooks.plan.reason.noExecutor')),
903
935
  engine_not_local: 'the engine was not started by this client on this machine, so it cannot reach the plugin files',
904
936
  engine_locality_unknown: 'it is not known whether the engine was started by this client on this machine, so plugin hooks are not sent to it',
905
937
  engine_capability_absent: 'the connected engine does not support plugin hooks',
@@ -931,6 +963,7 @@ const DROP_TEXT = {
931
963
  workspace_untrusted: renderMessage(msg('config.hooks.plan.reason.closedUntrusted')),
932
964
  governance_unreadable: renderMessage(msg('config.hooks.plan.reason.closedGovernanceUnreadable')),
933
965
  policy_disable_all: renderMessage(msg('config.hooks.plan.reason.closedDisableAll')),
966
+ no_executor: REASON_TEXT.no_executor,
934
967
  };
935
968
  const SOURCE_TEXT = HOOK_SETTINGS_SOURCE_TEXT;
936
969
  export function pluginHookReasonText(reason) {
@@ -1011,6 +1044,7 @@ function namedPathsOfRow(d) {
1011
1044
  case 'workspace_untrusted':
1012
1045
  case 'governance_unreadable':
1013
1046
  case 'policy_disable_all':
1047
+ case 'no_executor':
1014
1048
  return [];
1015
1049
  }
1016
1050
  }
@@ -24,6 +24,8 @@ export function toolEndResultToUserFrame(arm) {
24
24
  }, undefined, parent === null ? 'leader' : 'subagent');
25
25
  const envelope = toolResultEnvelopeParts(outcome);
26
26
  const gateView = guardedRead(() => gateOutcomeOf(arm));
27
+ const denialKindRaw = arm._sema_denial_kind;
28
+ const denialKind = typeof denialKindRaw === 'string' ? denialKindRaw : undefined;
27
29
  const content = Array.isArray(outcome.body.content) ? outcome.body.content : arm.truncated === true ? `${text}\n…(truncated)` : text;
28
30
  return {
29
31
  type: 'user',
@@ -39,9 +41,9 @@ export function toolEndResultToUserFrame(arm) {
39
41
  ],
40
42
  },
41
43
  parent_tool_use_id: parent,
42
- ...(isCcToolDenialKind(arm._sema_denial_kind) ? { _sema_denial_kind: arm._sema_denial_kind } : {}),
44
+ ...(isCcToolDenialKind(denialKind) ? { _sema_denial_kind: denialKind } : {}),
43
45
  ...(gateView !== undefined ? { _sema_gate: gateView } : {}),
44
- ...toolResultRowFlagParts(arm, gateView),
46
+ ...toolResultRowFlagParts(arm, gateView, denialKind),
45
47
  ...(Object.hasOwn(envelope, 'tool_use_result') ? { tool_use_result: envelope.tool_use_result } : {}),
46
48
  ...(Object.hasOwn(envelope, '_sema_degraded') ? { _sema_degraded: envelope._sema_degraded } : {}),
47
49
  uuid: rowIdentity.uuid,
@@ -1,10 +1,13 @@
1
- import { displayUntrusted } from './displayUntrusted.js';
1
+ import { displayUntrustedCapped } from './displayUntrusted.js';
2
2
  import { stripSystemReminderTagTokens, unwrapSystemReminder } from './systemReminderTag.js';
3
3
  import { PROMPT_BLOCKED_BY_HOOK_ROW_LEAD } from './rowKind.js';
4
4
  export { PROMPT_BLOCKED_BY_HOOK_ROW_LEAD };
5
5
  const RUN_BLOCKED_REASONED = 'Run blocked';
6
6
  const RUN_BLOCKED_NO_REASON = 'Run blocked (the agent reported it cannot proceed)';
7
- const HOOK_WORDS_SHOWN_MAX = 1000;
7
+ const WORDS_SHOWN_MAX = 1000;
8
+ function shownWords(words) {
9
+ return displayUntrustedCapped(words, WORDS_SHOWN_MAX).trim();
10
+ }
8
11
  const CAUSE_CLAUSE = Object.freeze({
9
12
  crashed: 'the hook failed before it answered; the prompt was not submitted',
10
13
  timeout: 'the hook did not answer in time; the prompt was not submitted',
@@ -13,7 +16,7 @@ const CAUSE_CLAUSE = Object.freeze({
13
16
  function shownHookWords(words) {
14
17
  if (words === undefined)
15
18
  return undefined;
16
- const shown = stripSystemReminderTagTokens(displayUntrusted(stripSystemReminderTagTokens(words), { max: HOOK_WORDS_SHOWN_MAX })).trim();
19
+ const shown = stripSystemReminderTagTokens(shownWords(stripSystemReminderTagTokens(words))).trim();
17
20
  return shown === '' ? undefined : shown;
18
21
  }
19
22
  export function runBlockedText(blocked) {
@@ -29,8 +32,10 @@ export function runBlockedText(blocked) {
29
32
  if (reason === undefined)
30
33
  return RUN_BLOCKED_NO_REASON;
31
34
  const unwrapped = unwrapSystemReminder(reason);
32
- if (unwrapped === reason)
33
- return `${RUN_BLOCKED_REASONED}: ${reason}`;
34
- const body = stripSystemReminderTagTokens(unwrapped).trim();
35
- return body === '' ? RUN_BLOCKED_NO_REASON : `${RUN_BLOCKED_REASONED}: ${body}`;
35
+ if (unwrapped === reason) {
36
+ const shown = shownWords(reason);
37
+ return shown === '' ? RUN_BLOCKED_NO_REASON : `${RUN_BLOCKED_REASONED}: ${shown}`;
38
+ }
39
+ const body = shownHookWords(unwrapped);
40
+ return body === undefined ? RUN_BLOCKED_NO_REASON : `${RUN_BLOCKED_REASONED}: ${body}`;
36
41
  }
@@ -0,0 +1,7 @@
1
+ export declare const SUGGESTION_BATCH_CAP = 8;
2
+ export declare const SUGGESTION_CHARS_CAP = 200;
3
+ export interface PromptSuggestionBatch {
4
+ readonly list: readonly string[];
5
+ readonly truncatedCount?: number;
6
+ }
7
+ export declare function promptSuggestionBatchOf(raw: {} | null | undefined): PromptSuggestionBatch | undefined;
@@ -0,0 +1,9 @@
1
+ export const SUGGESTION_BATCH_CAP = 8;
2
+ export const SUGGESTION_CHARS_CAP = 200;
3
+ export function promptSuggestionBatchOf(raw) {
4
+ const filtered = (Array.isArray(raw) ? raw : []).filter((s) => typeof s === 'string' && s.trim().length > 0);
5
+ const list = filtered.slice(0, SUGGESTION_BATCH_CAP).map((s) => (s.length > SUGGESTION_CHARS_CAP ? `${s.slice(0, SUGGESTION_CHARS_CAP)}…` : s));
6
+ if (list.length === 0)
7
+ return undefined;
8
+ return { list, ...(filtered.length > list.length ? { truncatedCount: filtered.length - list.length } : {}) };
9
+ }
@@ -0,0 +1,39 @@
1
+ import type { AgentClient } from '@sema-agent/sdk';
2
+ import type { LaneProof } from './seam.js';
3
+ export declare const PROMPT_SUGGESTIONS_TAIL_MAX_ATTEMPTS = 6;
4
+ export declare const PROMPT_SUGGESTIONS_TAIL_BUDGET_MS = 12000;
5
+ export interface PromptSuggestionsTailEvent {
6
+ readonly kind: 'prompt_suggestions';
7
+ readonly laneProof: LaneProof;
8
+ readonly suggestions: readonly string[];
9
+ }
10
+ export type PromptSuggestionsStreamSeen = 'frame' | 'done' | 'cut';
11
+ export interface PromptSuggestionsTailInput {
12
+ readonly runs: Pick<AgentClient['runs'], 'get'>;
13
+ readonly runId: string;
14
+ readonly sessionId?: string | undefined;
15
+ readonly requested: boolean;
16
+ readonly stream: PromptSuggestionsStreamSeen;
17
+ readonly baseUrl: string | undefined;
18
+ readonly signal?: AbortSignal | undefined;
19
+ readonly sleep?: ((ms: number) => Promise<void>) | undefined;
20
+ readonly now?: (() => number) | undefined;
21
+ }
22
+ export type PromptSuggestionsTailOutcome = {
23
+ readonly kind: 'skipped';
24
+ readonly why: 'not_requested' | 'frame_on_stream' | 'settled_on_stream';
25
+ } | {
26
+ readonly kind: 'settled';
27
+ readonly event?: PromptSuggestionsTailEvent;
28
+ } | {
29
+ readonly kind: 'not_completed';
30
+ readonly attempts: number;
31
+ } | {
32
+ readonly kind: 'gave_up';
33
+ readonly attempts: number;
34
+ } | {
35
+ readonly kind: 'unavailable';
36
+ readonly attempts: number;
37
+ };
38
+ export declare function promptSuggestionsOnStream(baseUrl: string | undefined): boolean;
39
+ export declare function followUpPromptSuggestions(input: PromptSuggestionsTailInput): Promise<PromptSuggestionsTailOutcome>;
@@ -0,0 +1,116 @@
1
+ import { abortableSleep, requestDeadline } from './abortableSleep.js';
2
+ import { engineCapString, engineCapValue } from './engineCapsCache.js';
3
+ import { readOwnDataValue } from './ownKey.js';
4
+ import { promptSuggestionBatchOf } from './promptSuggestionBatch.js';
5
+ import { engineVersionTier } from './supportFloor.js';
6
+ export const PROMPT_SUGGESTIONS_TAIL_MAX_ATTEMPTS = 6;
7
+ export const PROMPT_SUGGESTIONS_TAIL_BUDGET_MS = 12_000;
8
+ const RETRY_STEP_MS = 750;
9
+ const RETRY_CAP_MS = 3_000;
10
+ const ON_STREAM_SERVER_FLOOR = '7.149.0';
11
+ const MAY_STILL_SUGGEST = Object.freeze(['completed', 'running']);
12
+ export function promptSuggestionsOnStream(baseUrl) {
13
+ try {
14
+ const bit = engineCapValue(baseUrl, 'promptSuggestions');
15
+ if (bit.kind !== 'value' || bit.value !== true)
16
+ return false;
17
+ return engineVersionTier(engineCapString(baseUrl, 'version'), ON_STREAM_SERVER_FLOOR) === 'engine';
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ const freeze = (o) => Object.freeze(o);
24
+ const own = (o, key) => {
25
+ try {
26
+ return typeof o === 'object' && o !== null ? readOwnDataValue(o, key) : undefined;
27
+ }
28
+ catch {
29
+ return undefined;
30
+ }
31
+ };
32
+ function eventOf(raw) {
33
+ const batch = promptSuggestionBatchOf(raw);
34
+ return batch === undefined ? undefined : freeze({ kind: 'prompt_suggestions', laneProof: freeze({ lane: 'main' }), suggestions: freeze([...batch.list]) });
35
+ }
36
+ function skipWhy(input) {
37
+ if (own(input, 'requested') !== true)
38
+ return 'not_requested';
39
+ const seen = own(input, 'stream');
40
+ if (seen === 'frame')
41
+ return 'frame_on_stream';
42
+ if (seen === 'done' && promptSuggestionsOnStream(own(input, 'baseUrl')))
43
+ return 'settled_on_stream';
44
+ return undefined;
45
+ }
46
+ export async function followUpPromptSuggestions(input) {
47
+ let attempts = 0;
48
+ try {
49
+ const why = skipWhy(input);
50
+ if (why !== undefined)
51
+ return freeze({ kind: 'skipped', why });
52
+ const runs = own(input, 'runs');
53
+ const runId = own(input, 'runId');
54
+ const get = typeof runs === 'object' && runs !== null ? runs.get : undefined;
55
+ if (typeof get !== 'function' || typeof runId !== 'string' || runId.length === 0)
56
+ return freeze({ kind: 'unavailable', attempts });
57
+ const sessionId = own(input, 'sessionId');
58
+ const signal = own(input, 'signal');
59
+ const sleepIn = own(input, 'sleep');
60
+ const nowIn = own(input, 'now');
61
+ const sleep = typeof sleepIn === 'function' ? sleepIn : (ms) => abortableSleep(ms, signal ?? new AbortController().signal);
62
+ const now = typeof nowIn === 'function' ? nowIn : Date.now;
63
+ const signalState = () => {
64
+ if (signal === undefined || signal === null)
65
+ return 'live';
66
+ try {
67
+ return signal.aborted === true ? 'aborted' : 'live';
68
+ }
69
+ catch {
70
+ return 'unreadable';
71
+ }
72
+ };
73
+ const t0 = now();
74
+ for (;;) {
75
+ if (signalState() !== 'live')
76
+ return freeze({ kind: 'unavailable', attempts });
77
+ const left = PROMPT_SUGGESTIONS_TAIL_BUDGET_MS - (now() - t0);
78
+ if (!(left > 0))
79
+ return freeze({ kind: 'gave_up', attempts });
80
+ attempts++;
81
+ const d = requestDeadline(left, signal);
82
+ let rec;
83
+ try {
84
+ rec = await d.race(Promise.resolve(get.call(runs, runId, {
85
+ ...(typeof sessionId === 'string' && sessionId.length > 0 ? { session: sessionId } : {}),
86
+ signal: d.signal,
87
+ })));
88
+ }
89
+ catch {
90
+ return freeze({ kind: d.expired() ? 'gave_up' : 'unavailable', attempts });
91
+ }
92
+ finally {
93
+ d.abort();
94
+ }
95
+ const suggestions = own(rec, 'suggestions');
96
+ if (Array.isArray(suggestions)) {
97
+ const event = eventOf(suggestions);
98
+ return freeze({ kind: 'settled', ...(event !== undefined ? { event } : {}) });
99
+ }
100
+ const status = own(rec, 'status');
101
+ if (typeof status === 'string' && !MAY_STILL_SUGGEST.includes(status))
102
+ return freeze({ kind: 'not_completed', attempts });
103
+ if (attempts >= PROMPT_SUGGESTIONS_TAIL_MAX_ATTEMPTS)
104
+ return freeze({ kind: 'gave_up', attempts });
105
+ const rest = PROMPT_SUGGESTIONS_TAIL_BUDGET_MS - (now() - t0);
106
+ if (!(rest > 0))
107
+ return freeze({ kind: 'gave_up', attempts });
108
+ if (signalState() !== 'live')
109
+ return freeze({ kind: 'unavailable', attempts });
110
+ await sleep(Math.min(RETRY_STEP_MS * attempts, RETRY_CAP_MS, rest));
111
+ }
112
+ }
113
+ catch {
114
+ return freeze({ kind: 'unavailable', attempts });
115
+ }
116
+ }
@@ -548,8 +548,12 @@ export function refilterHostSettings(request, fresh, lane) {
548
548
  }
549
549
  }
550
550
  }
551
- if (Object.hasOwn(nextSettings, 'env')) {
552
- const sentEnv = nextSettings.env;
551
+ const nowEnv = intake.carried.env;
552
+ const envUnwired = intake.dispositions['settings.env'] === 'unwired';
553
+ const envMaybeAdded = nowEnv !== undefined && !envUnwired && hostRefuseRowSeated('settings.env', lane, true);
554
+ const hasSentEnv = Object.hasOwn(nextSettings, 'env');
555
+ if (hasSentEnv || envMaybeAdded) {
556
+ const sentEnv = hasSentEnv ? nextSettings.env : {};
553
557
  if (!isPlainRecord(sentEnv))
554
558
  return unjudged();
555
559
  let names;
@@ -559,14 +563,14 @@ export function refilterHostSettings(request, fresh, lane) {
559
563
  catch {
560
564
  return unjudged();
561
565
  }
562
- const nowEnv = intake.carried.env;
563
- const envUnwired = intake.dispositions['settings.env'] === 'unwired';
564
566
  const sentKeys = Object.keys(sentEnv);
565
567
  const kept = sentKeys.filter((k) => names.includes(k) && (envUnwired || (nowEnv !== undefined && Object.hasOwn(nowEnv, k))));
566
- if (kept.length !== sentKeys.length) {
568
+ const added = envMaybeAdded && nowEnv !== undefined ? names.filter((n) => !sentKeys.includes(n) && Object.hasOwn(nowEnv, n)) : [];
569
+ if (kept.length !== sentKeys.length || added.length > 0) {
567
570
  const { env: _sentEnv, ...restSettings } = nextSettings;
568
571
  void _sentEnv;
569
- nextSettings = kept.length === 0 ? restSettings : { ...restSettings, env: Object.fromEntries(kept.map((k) => [k, sentEnv[k]])) };
572
+ const rows = [...kept.map((k) => [k, sentEnv[k]]), ...added.map((n) => [n, nowEnv?.[n]])];
573
+ nextSettings = rows.length === 0 ? restSettings : { ...restSettings, env: Object.fromEntries(rows) };
570
574
  }
571
575
  }
572
576
  if (nextSettings === settings)
@@ -0,0 +1 @@
1
+ export declare function requestCoordinateText(root: string, segs: readonly (string | number)[], label?: (seg: string) => string): string;
@@ -0,0 +1,13 @@
1
+ const PLAIN_SEGMENT = /^[^.[\]"\s\p{Cc}]+$/u;
2
+ export function requestCoordinateText(root, segs, label) {
3
+ let out = root;
4
+ for (const seg of segs) {
5
+ if (typeof seg === 'number') {
6
+ out += `[${seg}]`;
7
+ continue;
8
+ }
9
+ const shown = label === undefined ? seg : label(seg);
10
+ out += PLAIN_SEGMENT.test(shown) ? `.${shown}` : `[${JSON.stringify(shown)}]`;
11
+ }
12
+ return out;
13
+ }