@sema-agent/client-core 0.95.0 → 0.96.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 (181) hide show
  1. package/CHANGELOG.md +180 -0
  2. package/README.md +49 -25
  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 -0
  8. package/dist/adapter/activeRunSelfHeal.js +30 -4
  9. package/dist/adapter/downstream/eventToSdkMessage.js +11 -3
  10. package/dist/adapter/downstream/terminalToSdkResult.js +8 -9
  11. package/dist/adapter/runStream.js +30 -47
  12. package/dist/agentSession/engineAgentRegistryRead.js +5 -4
  13. package/dist/agentSession/engineSessionBackgroundStop.js +22 -15
  14. package/dist/agentsWireCaps.js +13 -2
  15. package/dist/callerCwdCapability.d.ts +0 -4
  16. package/dist/callerCwdCapability.js +0 -6
  17. package/dist/centerWiringCapability.d.ts +0 -4
  18. package/dist/centerWiringCapability.js +0 -6
  19. package/dist/classifierVerdictWire.js +4 -16
  20. package/dist/clientSlice.js +1 -1
  21. package/dist/cloudConfigWireCaps.d.ts +3 -3
  22. package/dist/cloudConfigWireCaps.js +65 -45
  23. package/dist/coreValuePorts.d.ts +11 -7
  24. package/dist/coreValuePorts.js +5 -8
  25. package/dist/credentialProvenance.d.ts +28 -0
  26. package/dist/credentialProvenance.js +76 -0
  27. package/dist/decideFailureNote.js +3 -1
  28. package/dist/decideReceipt.js +7 -10
  29. package/dist/declared/supportFloorDeclared.js +3 -3
  30. package/dist/displayUntrusted.d.ts +7 -3
  31. package/dist/displayUntrusted.js +356 -60
  32. package/dist/engineCapsCache.d.ts +6 -0
  33. package/dist/engineCapsCache.js +226 -32
  34. package/dist/engineDirectory.d.ts +79 -0
  35. package/dist/engineDirectory.js +186 -0
  36. package/dist/engineErrorCodes.d.ts +14 -1
  37. package/dist/engineErrorCodes.js +37 -15
  38. package/dist/engineNoticeCodes.d.ts +17 -0
  39. package/dist/engineNoticeCodes.js +57 -3
  40. package/dist/engineWordRenames.d.ts +8 -0
  41. package/dist/engineWordRenames.js +13 -0
  42. package/dist/fileHistoryCaptureCapability.d.ts +14 -0
  43. package/dist/fileHistoryCaptureCapability.js +70 -0
  44. package/dist/fleet/bgSubagentRow.d.ts +15 -0
  45. package/dist/fleet/bgSubagentRow.js +67 -0
  46. package/dist/fleet/fleetLedger.d.ts +1 -0
  47. package/dist/fleet/fleetLedger.js +103 -89
  48. package/dist/fleet/fleetProjection.d.ts +1 -0
  49. package/dist/fleet/fleetProjection.js +10 -3
  50. package/dist/fleetTaskDesc.js +4 -17
  51. package/dist/gateOutcome.d.ts +12 -1
  52. package/dist/gateOutcome.js +58 -1
  53. package/dist/gateVocabulary.d.ts +7 -2
  54. package/dist/gateVocabulary.js +5 -1
  55. package/dist/generated/engineAskFaceTables.js +1 -1
  56. package/dist/generated/engineFactTables.d.ts +5 -3
  57. package/dist/generated/engineFactTables.js +17 -4
  58. package/dist/generated/engineNoticeTables.js +12 -3
  59. package/dist/generated/settingsSchemaFns.d.ts +3 -0
  60. package/dist/generated/settingsSchemaFns.js +53 -0
  61. package/dist/generated/toolNameTables.d.ts +1 -0
  62. package/dist/generated/toolNameTables.js +1 -0
  63. package/dist/hitl/approvalEndingCause.d.ts +2 -0
  64. package/dist/hitl/approvalEndingCause.js +31 -21
  65. package/dist/hitl/askParkRowRouting.js +1 -1
  66. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  67. package/dist/hitl/automaticAnswerVerdict.js +13 -8
  68. package/dist/hitl/decisionConflict.js +0 -2
  69. package/dist/hitl/decisionRefusal.d.ts +8 -0
  70. package/dist/hitl/decisionRefusal.js +48 -14
  71. package/dist/hitl/frameRouter.d.ts +0 -1
  72. package/dist/hitl/frameRouter.js +6 -14
  73. package/dist/hitl/hitlBridge.d.ts +6 -2
  74. package/dist/hitl/hitlBridge.js +28 -7
  75. package/dist/hitl/localAllowRule.d.ts +1 -0
  76. package/dist/hitl/localAllowRule.js +2 -2
  77. package/dist/hitl/noAllowCause.d.ts +2 -1
  78. package/dist/hitl/noAllowCause.js +3 -2
  79. package/dist/hitl/parkResolver.d.ts +1 -0
  80. package/dist/hitl/parkResolver.js +8 -54
  81. package/dist/hitl/parkRowBirthWait.d.ts +1 -0
  82. package/dist/hitl/parkRowBirthWait.js +2 -2
  83. package/dist/hitl/planReviewWire.d.ts +1 -0
  84. package/dist/hitl/planReviewWire.js +98 -20
  85. package/dist/hitl/replayedApproval.d.ts +1 -0
  86. package/dist/hitl/replayedApproval.js +14 -0
  87. package/dist/hitl/sessionPolicyDeliverable.js +8 -6
  88. package/dist/hitl/standingRows.d.ts +18 -0
  89. package/dist/hitl/standingRows.js +127 -0
  90. package/dist/hitl/toolApprovalWire.d.ts +15 -3
  91. package/dist/hitl/toolApprovalWire.js +238 -25
  92. package/dist/hookMatcherVerdict.d.ts +1 -9
  93. package/dist/hookMatcherVerdict.js +21 -61
  94. package/dist/hooksWireCaps.js +2 -2
  95. package/dist/hostCallbackGuard.d.ts +1 -0
  96. package/dist/hostCallbackGuard.js +6 -0
  97. package/dist/index.d.ts +21 -9
  98. package/dist/index.js +17 -8
  99. package/dist/mcpProbeCapability.d.ts +0 -4
  100. package/dist/mcpProbeCapability.js +0 -6
  101. package/dist/memoryComplianceCapability.d.ts +0 -4
  102. package/dist/memoryComplianceCapability.js +0 -6
  103. package/dist/memoryOriginCapability.d.ts +0 -4
  104. package/dist/memoryOriginCapability.js +0 -6
  105. package/dist/memorySpecWire.js +2 -2
  106. package/dist/modelIdentityResolvability.d.ts +2 -2
  107. package/dist/modelIdentityResolvability.js +12 -3
  108. package/dist/noticePresentation.d.ts +68 -0
  109. package/dist/noticePresentation.js +247 -0
  110. package/dist/outputStyleSeatCapability.d.ts +0 -1
  111. package/dist/outputStyleSeatCapability.js +0 -3
  112. package/dist/ownKey.d.ts +2 -0
  113. package/dist/ownKey.js +18 -0
  114. package/dist/peerFrames.js +13 -9
  115. package/dist/permissionModesCapability.d.ts +0 -4
  116. package/dist/permissionModesCapability.js +0 -6
  117. package/dist/permissionRuleIssue.js +0 -3
  118. package/dist/permissionsDeclaration.d.ts +2 -0
  119. package/dist/permissionsDeclaration.js +56 -41
  120. package/dist/printPermissionOutcome.d.ts +12 -0
  121. package/dist/printPermissionOutcome.js +50 -0
  122. package/dist/printToolResultFrame.d.ts +1 -0
  123. package/dist/printToolResultFrame.js +5 -9
  124. package/dist/promptBlockedCopy.d.ts +5 -0
  125. package/dist/promptBlockedCopy.js +35 -0
  126. package/dist/protocolSegment.d.ts +9 -6
  127. package/dist/protocolSegment.js +31 -21
  128. package/dist/providerRejection.d.ts +4 -0
  129. package/dist/providerRejection.js +75 -14
  130. package/dist/replayBody.d.ts +12 -0
  131. package/dist/replayBody.js +25 -0
  132. package/dist/resumeRefusalCopy.js +5 -21
  133. package/dist/retryStatus.d.ts +9 -1
  134. package/dist/retryStatus.js +9 -2
  135. package/dist/rewindArchiveCapability.d.ts +3 -13
  136. package/dist/rewindArchiveCapability.js +7 -58
  137. package/dist/rewindArchiveReading.d.ts +15 -0
  138. package/dist/rewindArchiveReading.js +56 -0
  139. package/dist/ruleCompileVerdict.d.ts +9 -1
  140. package/dist/ruleCompileVerdict.js +29 -4
  141. package/dist/runTerminal.d.ts +15 -1
  142. package/dist/runTerminal.js +56 -8
  143. package/dist/sandboxWire.js +13 -0
  144. package/dist/sealedKeyCapability.d.ts +0 -4
  145. package/dist/sealedKeyCapability.js +0 -6
  146. package/dist/seam.js +4 -2
  147. package/dist/seatContract.d.ts +73 -11
  148. package/dist/seatContract.js +195 -14
  149. package/dist/sessionReadRedactionCapability.d.ts +0 -4
  150. package/dist/sessionReadRedactionCapability.js +0 -6
  151. package/dist/sessionSyncFileHistory.d.ts +4 -0
  152. package/dist/sessionSyncFileHistory.js +66 -0
  153. package/dist/sessionSyncFileHistoryCapability.d.ts +0 -1
  154. package/dist/sessionSyncFileHistoryCapability.js +0 -3
  155. package/dist/skillToolListsCapability.d.ts +0 -1
  156. package/dist/skillToolListsCapability.js +0 -3
  157. package/dist/steerPriorityCapability.d.ts +0 -1
  158. package/dist/steerPriorityCapability.js +0 -3
  159. package/dist/storePostureCapability.d.ts +0 -4
  160. package/dist/storePostureCapability.js +0 -6
  161. package/dist/subagent/engineSubagentSteer.js +7 -5
  162. package/dist/subagentContentStore.js +4 -15
  163. package/dist/supportFloor.d.ts +1 -0
  164. package/dist/supportFloor.js +13 -9
  165. package/dist/systemReminderTag.d.ts +1 -0
  166. package/dist/systemReminderTag.js +8 -0
  167. package/dist/toolHistoryMismatch.d.ts +0 -3
  168. package/dist/toolHistoryMismatch.js +3 -7
  169. package/dist/toolResult.d.ts +1 -1
  170. package/dist/toolResult.js +2 -1
  171. package/dist/toolResultContent.d.ts +2 -0
  172. package/dist/toolResultContent.js +63 -0
  173. package/dist/usageAnalytics.js +40 -16
  174. package/dist/userInvokedSkillsCapability.d.ts +0 -1
  175. package/dist/userInvokedSkillsCapability.js +0 -3
  176. package/dist/wireErrorTriage.d.ts +2 -0
  177. package/dist/wireErrorTriage.js +34 -5
  178. package/dist/wireFailureShape.d.ts +19 -2
  179. package/dist/wireFailureShape.js +55 -50
  180. package/docs/INTEGRATION-CLIENTS.md +1262 -17
  181. package/package.json +5 -5
@@ -1,7 +1,7 @@
1
1
  import { PERMISSION_MODE_WORDS, REASONING_EFFORT_WORDS } from "./taskRequestWords.js";
2
2
  import { liveDefaultModelCopy, liveModelRowCopy } from "./model/liveCatalogShape.js";
3
3
  import { REWIND_MODES } from "./rewindWireCaps.js";
4
- import { ownDataField, ownDataRefusalKeyOf, ownSendableField } from "./ownKey.js";
4
+ import { guardedRead, ownDataField, ownDataRefusalKeyOf, ownSendableField, readOwnDataValue } from "./ownKey.js";
5
5
  const bad = (reason, field) => field === undefined ? { reason } : { reason, field };
6
6
  const nested = (prefix, verdict) => {
7
7
  const path = verdict.field === undefined
@@ -13,8 +13,15 @@ const nested = (prefix, verdict) => {
13
13
  return bad(reason, path);
14
14
  };
15
15
  const isObject = (v) => typeof v === "object" && v !== null;
16
+ const isPlainRecord = (v) => {
17
+ if (!isObject(v) || Array.isArray(v))
18
+ return false;
19
+ const proto = Object.getPrototypeOf(v);
20
+ return proto === Object.prototype || proto === null;
21
+ };
16
22
  const isAbsent = (v) => typeof v === "undefined";
17
23
  const optionalString = (v) => isAbsent(v) || typeof v === "string";
24
+ const isFolderPath = (v) => typeof v === "string" && v.length > 0;
18
25
  const inTuple = (tuple, v) => tuple.includes(v);
19
26
  const own = (o, k) => ownSendableField(o, k);
20
27
  const ownItems = (a) => {
@@ -112,14 +119,16 @@ const SEAT_EVENT_TYPE_TUPLE = [
112
119
  "error",
113
120
  "tool_permission_resolved",
114
121
  ];
115
- export const SEAT_EVENT_TYPES = SEAT_EVENT_TYPE_TUPLE;
122
+ export const SEAT_EVENT_TYPES = Object.freeze(SEAT_EVENT_TYPE_TUPLE);
116
123
  const CHROME_EVENT_KIND_TUPLE = [
117
124
  "panel_task",
118
125
  "notification_terminal",
119
126
  "prompt_suggestions",
120
127
  "subagent_lifecycle",
128
+ "text_segment_end",
129
+ "thinking_segment_end",
121
130
  ];
122
- export const CHROME_EVENT_KINDS = CHROME_EVENT_KIND_TUPLE;
131
+ export const CHROME_EVENT_KINDS = Object.freeze(CHROME_EVENT_KIND_TUPLE);
123
132
  const LANE_PROOF_LANE_TUPLE = ["main", "subagent", "workflow"];
124
133
  export const SEAT_CHROME_ENVELOPE_KEYS = ["kind", "laneProof"];
125
134
  export const HITL_RESOLUTION_OUTCOMES = [
@@ -132,7 +141,7 @@ export const HITL_RESOLUTION_OUTCOMES = [
132
141
  "decline",
133
142
  "cancel",
134
143
  ];
135
- export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind"]);
144
+ export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind", "recents", "patchSession"]);
136
145
  const notReported = () => Object.freeze({ kind: "not_reported" });
137
146
  const malformedReading = () => Object.freeze({ kind: "malformed" });
138
147
  const reported = (available) => Object.freeze({ kind: "reported", available });
@@ -206,6 +215,7 @@ export function watchSeatCapability(store, name, onReading) {
206
215
  const SEAT_CAPABILITY_PHRASES = Object.freeze({
207
216
  pickFolder: Object.freeze({ action: "Opening a folder", ability: "show a folder picker", support: "folder picker support" }),
208
217
  rewind: Object.freeze({ action: "Rewinding to an earlier message", ability: "rewind this session", support: "rewind support" }),
218
+ recents: Object.freeze({ action: "Remembering recent folders", ability: "remember recent folders", support: "recent folders support" }), patchSession: Object.freeze({ action: "Renaming or pinning a conversation", ability: "rename or pin conversations", support: "rename and pin support" }),
209
219
  });
210
220
  export function seatCapabilityUnavailableDetail(name, reading) {
211
221
  const phrase = Object.hasOwn(SEAT_CAPABILITY_PHRASES, name)
@@ -228,6 +238,96 @@ export function seatCapabilityUnavailableDetail(name, reading) {
228
238
  return `${phrase.action} isn't available: this app hasn't reported ${phrase.support}.`;
229
239
  return `${phrase.action} isn't available: this app's support report couldn't be read.`;
230
240
  }
241
+ const NO_TIME = Number.NEGATIVE_INFINITY;
242
+ const stampMs = (v) => {
243
+ if (typeof v !== "string")
244
+ return NO_TIME;
245
+ const t = Date.parse(v);
246
+ return Number.isFinite(t) ? t : NO_TIME;
247
+ };
248
+ const softField = (o, key) => typeof o === "object" && o !== null ? guardedRead(() => readOwnDataValue(o, key)) : undefined;
249
+ const softItems = (a) => {
250
+ const isArray = guardedRead(() => Array.isArray(a));
251
+ if (isArray !== true)
252
+ return undefined;
253
+ const n = softField(a, "length");
254
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0)
255
+ return undefined;
256
+ const out = [];
257
+ for (let i = 0; i < n; i += 1)
258
+ out.push(softField(a, String(i)));
259
+ return out;
260
+ };
261
+ export function recentFoldersOf(input) {
262
+ const sessions = softItems(softField(input, "sessions"));
263
+ const registered = softItems(softField(input, "registered"));
264
+ if (sessions === undefined && registered === undefined)
265
+ return null;
266
+ const byPath = new Map();
267
+ let order = 0;
268
+ const place = (path, at) => {
269
+ const cur = byPath.get(path);
270
+ order += 1;
271
+ if (cur === undefined)
272
+ byPath.set(path, { at, order });
273
+ else if (at > cur.at)
274
+ cur.at = at;
275
+ };
276
+ for (const row of registered ?? []) {
277
+ const path = softField(row, "path");
278
+ if (isFolderPath(path))
279
+ place(path, stampMs(softField(row, "lastUsedAt")));
280
+ }
281
+ for (const rec of sessions ?? []) {
282
+ const cwd = softField(rec, "cwd");
283
+ if (!isFolderPath(cwd))
284
+ continue;
285
+ const active = stampMs(softField(rec, "lastActivityAt"));
286
+ place(cwd, active !== NO_TIME ? active : stampMs(softField(rec, "createdAt")));
287
+ }
288
+ return [...byPath.entries()]
289
+ .sort(([, x], [, y]) => (x.at === y.at ? x.order - y.order : x.at > y.at ? -1 : 1))
290
+ .map(([path]) => path);
291
+ }
292
+ export async function readRecentFolders(input) {
293
+ const capability = guardedRead(() => input.capability);
294
+ const list = guardedRead(() => input.list);
295
+ const sessions = guardedRead(() => input.sessions);
296
+ let registered;
297
+ let host = "skipped";
298
+ let unreadableRows = 0;
299
+ if (capability !== undefined && seatCapabilityUnavailableDetail("recents", capability) === undefined) {
300
+ try {
301
+ const items = softItems(softField(await list(), "folders"));
302
+ if (items === undefined) {
303
+ host = "unreadable";
304
+ }
305
+ else {
306
+ const rows = [];
307
+ for (const item of items) {
308
+ const path = softField(item, "path");
309
+ if (!isFolderPath(path)) {
310
+ unreadableRows += 1;
311
+ continue;
312
+ }
313
+ const lastUsedAt = softField(item, "lastUsedAt");
314
+ rows.push(typeof lastUsedAt === "string" ? { path, lastUsedAt } : { path });
315
+ }
316
+ if (items.length > 0 && rows.length === 0) {
317
+ host = "unreadable";
318
+ }
319
+ else {
320
+ registered = rows;
321
+ host = "listed";
322
+ }
323
+ }
324
+ }
325
+ catch {
326
+ host = "unreadable";
327
+ }
328
+ }
329
+ return Object.freeze({ folders: recentFoldersOf({ sessions, registered }), host, unreadableRows });
330
+ }
231
331
  export const PERMISSION_MODE_INTENTS = [...PERMISSION_MODE_WORDS];
232
332
  export const ATTACHMENT_IDS_PER_TASK_CAP = 16;
233
333
  export const SEAT_REASONING_EFFORTS = [...REASONING_EFFORT_WORDS];
@@ -241,6 +341,7 @@ export const SEAT_TURN_SETTINGS_ORIGINS = Object.freeze({
241
341
  });
242
342
  export const SEAT_REWIND_INTENT_KEYS = ["mode", "targetMessageUuid"];
243
343
  export const ATTACHMENT_UPLOAD_REQUEST_KEYS = ["name", "mimeType", "bytes"];
344
+ export const SESSION_ROW_PATCH_REQUEST_KEYS = Object.freeze(["sessionId", "title", "pinned"]);
244
345
  export const SESSION_SEARCH_RESULT_KEYS = ["sessionId", "title", "matches"];
245
346
  export const GET_TRANSCRIPT_RESULT_KEYS = ["sessionId", "messages"];
246
347
  const checkLocalSessionRecord = seatCheck((s) => {
@@ -255,7 +356,7 @@ const checkLocalSessionRecord = seatCheck((s) => {
255
356
  if (!optionalString(own(s, "title")))
256
357
  return bad("title must be a string when present", "title");
257
358
  const cwd = own(s, "cwd");
258
- if (!isAbsent(cwd) && (typeof cwd !== "string" || cwd.length === 0)) {
359
+ if (!isAbsent(cwd) && !isFolderPath(cwd)) {
259
360
  return bad("cwd must be a non-empty path string when present (absent = no folder chosen)", "cwd");
260
361
  }
261
362
  if (typeof own(s, "isRunning") !== "boolean")
@@ -274,6 +375,10 @@ const checkLocalSessionRecord = seatCheck((s) => {
274
375
  if (!isAbsent(parked) && typeof parked !== "boolean") {
275
376
  return bad("parked must be a boolean when present", "parked");
276
377
  }
378
+ const pinned = own(s, "pinned");
379
+ if (!isAbsent(pinned) && typeof pinned !== "boolean") {
380
+ return bad("pinned must be a boolean when present (absent = not pinned or unknown)", "pinned");
381
+ }
277
382
  return true;
278
383
  });
279
384
  export function isLocalSessionRecord(s) {
@@ -597,6 +702,30 @@ export function checkSendMessageRequest(v) {
597
702
  export function isSendMessageRequest(v) {
598
703
  return checkSendMessageRequest(v) === true;
599
704
  }
705
+ export const checkSessionRowPatchRequest = seatCheck((v) => {
706
+ if (!isObject(v) || Array.isArray(v))
707
+ return bad("patchSession request is not an object");
708
+ const body = {};
709
+ for (const k of Object.keys(v)) {
710
+ if (!inTuple(SESSION_ROW_PATCH_REQUEST_KEYS, k))
711
+ return bad(`unknown key (closed shape: ${SESSION_ROW_PATCH_REQUEST_KEYS.join(", ")})`, k);
712
+ body[k] = own(v, k);
713
+ }
714
+ if (typeof body.sessionId !== "string" || body.sessionId.length === 0)
715
+ return bad("sessionId must be a non-empty string", "sessionId");
716
+ const { title, pinned } = body;
717
+ if (!isAbsent(title) && title !== null && (typeof title !== "string" || title.length === 0)) {
718
+ return bad('title must be a non-empty string, or null to clear it back to the automatic title (an empty string is not a way to clear)', "title");
719
+ }
720
+ if (!isAbsent(pinned) && typeof pinned !== "boolean")
721
+ return bad("pinned must be a boolean when present", "pinned");
722
+ if (isAbsent(title) && isAbsent(pinned))
723
+ return bad("nothing to change: carry at least one of title / pinned");
724
+ return true;
725
+ });
726
+ export function isSessionRowPatchRequest(v) {
727
+ return checkSessionRowPatchRequest(v) === true;
728
+ }
600
729
  const checkSendMessageAck = seatCheck((v) => {
601
730
  if (!isObject(v))
602
731
  return bad("sendMessage ack is not an object");
@@ -678,11 +807,10 @@ const checkFocusedSessionState = seatCheck((v) => {
678
807
  return true;
679
808
  });
680
809
  const checkSettingsState = seatCheck((v) => {
681
- if (!isObject(v))
682
- return bad("settings state is not an object");
683
- const defaultModel = own(v, "defaultModel");
684
- if (defaultModel !== null && typeof defaultModel !== "string") {
685
- return bad("defaultModel must be a string or null", "defaultModel");
810
+ if (!isPlainRecord(v))
811
+ return bad("settings state is not a plain object");
812
+ if (Object.hasOwn(v, "defaultModel")) {
813
+ return bad("defaultModel is not carried in settings state: read the deployment default model from the model catalog (liveDefaultModelOf over the listModels() result)", "defaultModel");
686
814
  }
687
815
  if (!optionalString(own(v, "engineVersion")))
688
816
  return bad("engineVersion must be a string when present", "engineVersion");
@@ -690,9 +818,32 @@ const checkSettingsState = seatCheck((v) => {
690
818
  return bad("dataRoot must be a string when present", "dataRoot");
691
819
  return true;
692
820
  });
693
- const checkPickFolderResult = (v) => v === null || (typeof v === "string" && v.length > 0)
821
+ const checkPickFolderResult = (v) => v === null || isFolderPath(v)
694
822
  ? true
695
823
  : bad("pickFolder result must be null (cancelled) or a non-empty path string");
824
+ const checkRecentFolderRow = seatCheck((v) => {
825
+ if (!isPlainRecord(v))
826
+ return bad("recent folder row is not a plain object");
827
+ if (!isFolderPath(own(v, "path")))
828
+ return bad("path must be a non-empty path string", "path");
829
+ if (!optionalString(own(v, "lastUsedAt")))
830
+ return bad("lastUsedAt must be a string when present", "lastUsedAt");
831
+ return true;
832
+ });
833
+ const checkRecentFoldersResult = seatCheck((v) => {
834
+ if (!isPlainRecord(v))
835
+ return bad("recent folders result is not a plain object");
836
+ const folders = own(v, "folders");
837
+ if (!Array.isArray(folders))
838
+ return bad("folders must be an array", "folders");
839
+ const rows = ownItems(folders);
840
+ for (let i = 0; i < rows.length; i += 1) {
841
+ const verdict = checkRecentFolderRow(rows[i]);
842
+ if (verdict !== true)
843
+ return nested(`folders[${i}]`, verdict);
844
+ }
845
+ return true;
846
+ });
696
847
  const checkSeatCapabilitiesState = seatCheck((v) => {
697
848
  if (!isObject(v) || Array.isArray(v))
698
849
  return bad("seat capabilities state is not an object");
@@ -730,6 +881,8 @@ export const LOCAL_SESSIONS_SPEC = {
730
881
  listModels: { kind: "invoke", check: checkLiveModelCatalog },
731
882
  applyModelConfig: { kind: "invoke", check: checkApplyModelConfigResult },
732
883
  pickFolder: { kind: "invoke", check: checkPickFolderResult },
884
+ listRecentFolders: { kind: "invoke", check: checkRecentFoldersResult },
885
+ noteRecentFolder: { kind: "invoke", check: checkOkAck }, patchSession: { kind: "invoke", check: checkLocalSessionRecord },
733
886
  onEvent: { kind: "event", check: checkLocalSessionEvent },
734
887
  onToolPermissionRequest: { kind: "event", check: checkToolPermissionRequest },
735
888
  },
@@ -757,6 +910,8 @@ export const SEAT_METHOD_NAMES = [
757
910
  "listModels",
758
911
  "applyModelConfig",
759
912
  "pickFolder",
913
+ "listRecentFolders",
914
+ "noteRecentFolder", "patchSession",
760
915
  "onEvent",
761
916
  "onToolPermissionRequest",
762
917
  ];
@@ -804,6 +959,7 @@ export const TOOL_PERMISSION_REQUEST_KEYS = [
804
959
  "ruleOffersAbsence",
805
960
  "askClass",
806
961
  "classes",
962
+ "rememberRefused",
807
963
  ];
808
964
  export const LOCAL_SESSION_RECORD_KEYS = [
809
965
  "sessionId",
@@ -816,6 +972,7 @@ export const LOCAL_SESSION_RECORD_KEYS = [
816
972
  "lastActivityAt",
817
973
  "queuedMessages",
818
974
  "parked",
975
+ "pinned",
819
976
  ];
820
977
  export const SEND_MESSAGE_REQUEST_KEYS = [
821
978
  "sessionId",
@@ -860,7 +1017,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
860
1017
  LocalSessionRecord: {
861
1018
  guard: "isLocalSessionRecord",
862
1019
  check: checkLocalSessionRecord,
863
- validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked"],
1020
+ validated: ["sessionId", "engineSessionId", "model", "title", "cwd", "isRunning", "createdAt", "lastActivityAt", "queuedMessages", "parked", "pinned"],
864
1021
  unchecked: [],
865
1022
  },
866
1023
  ToolPermissionRequest: {
@@ -900,6 +1057,10 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
900
1057
  key: "classes",
901
1058
  why: "与 askClass 同一对(CC-284),同在同缺、同一只成对窄读(非空、每员非空串、无重复、首员等于 askClass;开集、原样顺序);座位门不设第二判官,理由同 askClass。",
902
1059
  },
1060
+ {
1061
+ key: "rememberRefused",
1062
+ why: "展示增强位(CC-642),权威窄读在卡口两条腿与过境口 toolPermissionRequestAskBits(同一只 readApprovalRememberRefused:自有键 ∧ 严格 true 才上座,永不铸 false);座位门不设第二判官,理由同 mandated(为一个展示位拒掉整条 request = 把安全 ask 丢给引擎 TTL deny)。消费端 `=== true` 读,其余(缺席 / 坏形)一律当缺席 ⇒ 照旧渲「本会话放行」。缺席 = 服务端没说(记得住或老服务端),不是 false。不经过境口自铸座位请求的宿主要自己按同一把窄读。",
1063
+ },
903
1064
  {
904
1065
  key: "ruleOffersAbsence",
905
1066
  why: "喂强制判据口 approvalIsMandated 的位(CC-203;寄存行那条腿的强制只以缺席词 mandated 表达),按开集读(非空串原文字节、词表不校);权威窄读在卡口与过境口,座位门不设第二判官,理由同 mandated。坏形读成缺席 ⇒ 这一路不判强制(放宽方向:寄存行上的强制就丢了;另两路 mandated / mandate 照判);表外串只是不等于 mandated。不经过境口自铸座位请求的宿主要自己按同一把窄读。",
@@ -971,6 +1132,12 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
971
1132
  ],
972
1133
  unchecked: [],
973
1134
  },
1135
+ SessionRowPatchRequest: {
1136
+ guard: "isSessionRowPatchRequest",
1137
+ check: checkSessionRowPatchRequest,
1138
+ validated: ["sessionId", "title", "pinned"],
1139
+ unchecked: [],
1140
+ },
974
1141
  ApplyModelConfigResult: {
975
1142
  check: checkApplyModelConfigResult,
976
1143
  validated: ["ok", "requiresRestart", "reason"],
@@ -1019,12 +1186,22 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
1019
1186
  },
1020
1187
  SettingsState: {
1021
1188
  check: checkSettingsState,
1022
- validated: ["defaultModel", "engineVersion", "dataRoot"],
1189
+ validated: ["engineVersion", "dataRoot"],
1023
1190
  unchecked: [],
1024
1191
  },
1025
1192
  SeatCapabilitiesState: {
1026
1193
  check: checkSeatCapabilitiesState,
1027
- validated: ["pickFolder", "rewind"],
1194
+ validated: ["pickFolder", "rewind", "recents", "patchSession"],
1195
+ unchecked: [],
1196
+ },
1197
+ RecentFoldersResult: {
1198
+ check: checkRecentFoldersResult,
1199
+ validated: ["folders"],
1200
+ unchecked: [],
1201
+ },
1202
+ RecentFolderRow: {
1203
+ check: checkRecentFolderRow,
1204
+ validated: ["path", "lastUsedAt"],
1028
1205
  unchecked: [],
1029
1206
  },
1030
1207
  SeatRewindIntent: {
@@ -1087,5 +1264,9 @@ const _seatContractPins = [
1087
1264
  true, true, true, true, true, true, true, true,
1088
1265
  true, true, true, true, true,
1089
1266
  true, true, true, true, true, true, true, true,
1267
+ true, true,
1268
+ true, true,
1269
+ true, true, true, true,
1270
+ true, true, true, true, true, true,
1090
1271
  ];
1091
1272
  void _seatContractPins;
@@ -7,9 +7,5 @@ export type SessionReadRedactionReading = {
7
7
  value: true;
8
8
  };
9
9
  export declare function projectSessionReadRedactionCapability(caps: unknown): SessionReadRedactionReading | undefined;
10
- export declare function noteEngineCapsForSessionReadRedaction(baseUrl: string, caps: unknown, opts?: {
11
- generation?: number;
12
- }): void;
13
10
  export declare function observedSessionReadRedaction(baseUrl?: string | undefined): SessionReadRedactionReading;
14
11
  export declare function sessionReadRedactionAdmitted(reading: SessionReadRedactionReading | undefined): boolean;
15
- export declare function __resetSessionReadRedactionReadingsForTests(): void;
@@ -19,9 +19,6 @@ const reader = createEngineCapReader({
19
19
  project: projectSessionReadRedactionCapability,
20
20
  makeUnobserved: () => ({ kind: 'unobserved' }),
21
21
  });
22
- export function noteEngineCapsForSessionReadRedaction(baseUrl, caps, opts) {
23
- reader.note(baseUrl, caps, opts);
24
- }
25
22
  export function observedSessionReadRedaction(baseUrl = engineWireTarget()?.baseUrl) {
26
23
  return reader.observed(baseUrl);
27
24
  }
@@ -37,6 +34,3 @@ export function sessionReadRedactionAdmitted(reading) {
37
34
  return false;
38
35
  }
39
36
  }
40
- export function __resetSessionReadRedactionReadingsForTests() {
41
- reader.__resetForTests();
42
- }
@@ -0,0 +1,4 @@
1
+ export declare const FILE_HISTORY_SKIPPED_NOTICE: string;
2
+ export declare function fileHistorySyncNotice(result: unknown): string | undefined;
3
+ export declare function fileHistorySyncNoticeIdentity(result: unknown): string | undefined;
4
+ export declare function isFileHistoryBlobMissing(err: unknown): boolean;
@@ -0,0 +1,66 @@
1
+ import { NOT_FOUND_BLOB } from './engineErrorCodes.js';
2
+ import { effectiveWireErrorCode } from './wireErrorTriage.js';
3
+ import { readThrownSlot } from './wireFailureShape.js';
4
+ export const FILE_HISTORY_SKIPPED_NOTICE = 'File history was not copied: only the conversation was synced, because one of the two engines does not confirm that it can ' +
5
+ 'transfer file history (the record /rewind uses to restore files). Syncing this session again will not add it until ' +
6
+ 'the conversation has new turns and both engines can transfer file history.';
7
+ function getOwn(o, key) {
8
+ return Object.hasOwn(o, key) ? o[key] : undefined;
9
+ }
10
+ function envelopeSaidEmpty(result) {
11
+ try {
12
+ const manifest = getOwn(result, 'manifest');
13
+ if (manifest === null || typeof manifest !== 'object')
14
+ return false;
15
+ const fh = getOwn(manifest, 'fileHistory');
16
+ if (fh === null || typeof fh !== 'object')
17
+ return false;
18
+ const tracked = getOwn(fh, 'tracked');
19
+ const boundaries = getOwn(fh, 'boundaries');
20
+ return Array.isArray(tracked) && tracked.length === 0 && Array.isArray(boundaries) && boundaries.length === 0;
21
+ }
22
+ catch {
23
+ return false;
24
+ }
25
+ }
26
+ export function fileHistorySyncNotice(result) {
27
+ if (result === null || typeof result !== 'object')
28
+ return undefined;
29
+ try {
30
+ if (getOwn(result, 'fileHistory') !== 'skipped')
31
+ return undefined;
32
+ return envelopeSaidEmpty(result) ? undefined : FILE_HISTORY_SKIPPED_NOTICE;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ export function fileHistorySyncNoticeIdentity(result) {
39
+ if (fileHistorySyncNotice(result) === undefined)
40
+ return undefined;
41
+ try {
42
+ const manifest = getOwn(result, 'manifest');
43
+ if (manifest === null || typeof manifest !== 'object')
44
+ return undefined;
45
+ const sessionId = getOwn(manifest, 'sessionId');
46
+ const leafId = getOwn(manifest, 'leafId');
47
+ if (typeof sessionId !== 'string' || sessionId === '' || typeof leafId !== 'string' || leafId === '')
48
+ return undefined;
49
+ return JSON.stringify([sessionId, leafId]);
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ }
55
+ export function isFileHistoryBlobMissing(err) {
56
+ if (typeof err !== 'object' || err === null)
57
+ return false;
58
+ try {
59
+ if (readThrownSlot(err, 'status') !== 404)
60
+ return false;
61
+ return effectiveWireErrorCode(err) === NOT_FOUND_BLOB;
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
@@ -14,4 +14,3 @@ export declare function noteEngineCapsForSessionSyncFileHistory(baseUrl: string,
14
14
  export declare function observedSessionSyncFileHistory(baseUrl?: string | undefined): SessionSyncFileHistoryReading;
15
15
  export declare function sessionSyncFileHistoryAvailable(reading: SessionSyncFileHistoryReading): 'yes' | 'no' | 'unknown';
16
16
  export declare function sessionSyncFileHistoryDoctorDetail(reading: SessionSyncFileHistoryReading): string;
17
- export declare function __resetSessionSyncFileHistoryReadingsForTests(): void;
@@ -46,6 +46,3 @@ export function sessionSyncFileHistoryDoctorDetail(reading) {
46
46
  return 'session-sync file history on — syncing a session can also move its edited-file history';
47
47
  }
48
48
  }
49
- export function __resetSessionSyncFileHistoryReadingsForTests() {
50
- reader.__resetForTests();
51
- }
@@ -14,4 +14,3 @@ export declare function noteEngineCapsForSkillToolLists(baseUrl: string, caps: u
14
14
  export declare function observedSkillToolLists(baseUrl?: string | undefined): SkillToolListsReading;
15
15
  export declare function skillToolListsAvailable(reading: SkillToolListsReading): 'yes' | 'no' | 'unknown';
16
16
  export declare function skillToolListsDoctorDetail(reading: SkillToolListsReading): string;
17
- export declare function __resetSkillToolListsReadingsForTests(): void;
@@ -46,6 +46,3 @@ export function skillToolListsDoctorDetail(reading) {
46
46
  return 'skill tool lists on — a request skill’s allowed and disallowed tool lists are read as this run’s rules';
47
47
  }
48
48
  }
49
- export function __resetSkillToolListsReadingsForTests() {
50
- reader.__resetForTests();
51
- }
@@ -14,4 +14,3 @@ export declare function noteEngineCapsForSteerPriority(baseUrl: string, caps: un
14
14
  export declare function observedSteerPriority(baseUrl?: string | undefined): SteerPriorityReading;
15
15
  export declare function steerPriorityMode(reading: SteerPriorityReading): SteerPriorityMode;
16
16
  export declare function steerPriorityDoctorDetail(reading: SteerPriorityReading): string;
17
- export declare function __resetSteerPriorityReadingsForTests(): void;
@@ -74,6 +74,3 @@ export function steerPriorityDoctorDetail(reading) {
74
74
  }
75
75
  return `steer priority reported as "${capForDisplay(word, WORD_DETAIL_MAX)}" — a value this client does not know yet, so whether it is applied is unknown and a "now" or "later" steer is not sent`;
76
76
  }
77
- export function __resetSteerPriorityReadingsForTests() {
78
- reader.__resetForTests();
79
- }
@@ -15,11 +15,7 @@ export type StorePostureReading = {
15
15
  view: StorePostureView;
16
16
  };
17
17
  export declare function projectStorePostureCapability(caps: unknown): StorePostureReading | undefined;
18
- export declare function noteEngineCapsForStorePosture(baseUrl: string, caps: unknown, opts?: {
19
- generation?: number;
20
- }): void;
21
18
  export declare function observedStorePosture(baseUrl?: string | undefined): StorePostureReading;
22
19
  export declare function storePersistence(reading: StorePostureReading): 'durable' | 'not_durable' | 'unknown';
23
20
  export declare function storePostureHeadWord(reading: StorePostureReading): string | undefined;
24
21
  export declare function storePostureDoctorDetail(reading: StorePostureReading): string;
25
- export declare function __resetStorePostureReadingsForTests(): void;
@@ -46,9 +46,6 @@ const reader = createEngineCapReader({
46
46
  project: projectStorePostureCapability,
47
47
  makeUnobserved: () => ({ kind: 'unobserved' }),
48
48
  });
49
- export function noteEngineCapsForStorePosture(baseUrl, caps, opts) {
50
- reader.note(baseUrl, caps, opts);
51
- }
52
49
  export function observedStorePosture(baseUrl = engineWireTarget()?.baseUrl) {
53
50
  return reader.observed(baseUrl);
54
51
  }
@@ -127,6 +124,3 @@ export function storePostureDoctorDetail(reading) {
127
124
  }
128
125
  return `${assembled} — ${lost} (degraded for a reason this client does not know yet: "${word(reason)}")`;
129
126
  }
130
- export function __resetStorePostureReadingsForTests() {
131
- reader.__resetForTests();
132
- }
@@ -5,6 +5,7 @@ import { resolveOwnerContext, resolveOwnerRunId } from './engineSubagentResume.j
5
5
  import { noteBgOwnerAbsence } from './subagentOwnerAbsence.js';
6
6
  import { errCodes, steeringRefusedBeforeEngine } from '../controlRouter.js';
7
7
  import { ownNonEmptyStringOf } from '../steerReceipt.js';
8
+ import { wireFailureTextsOf } from '../wireFailureShape.js';
8
9
  function usableSteerClient(client) {
9
10
  return clientHasVerbs(client, [['runs', 'steerSubagent']]);
10
11
  }
@@ -53,14 +54,14 @@ export async function steerEngineSubagent(target, text, childTaskId, opts) {
53
54
  status = undefined;
54
55
  }
55
56
  if (status !== undefined) {
56
- const detail = e instanceof Error ? e.message : String(e);
57
- hostLog('debug', `engineSubagentSteer: ${status} ${detail.slice(0, 200)}`);
57
+ const texts = wireFailureTextsOf(e);
58
+ hostLog('debug', `engineSubagentSteer: ${status} ${texts.log.slice(0, 200)}`);
58
59
  const refused = typeof e === 'object' && e !== null ? steeringRefusedBeforeEngine(e) : undefined;
59
60
  if (refused !== undefined && refused.safety === 'ledger_unavailable') {
60
61
  return { ok: false, reason: 'not-delivered', safety: refused.safety, detail: refused.advice };
61
62
  }
62
63
  if (status === 409 || status === 404) {
63
- return { ok: false, reason: 'not-running', detail };
64
+ return { ok: false, reason: 'not-running', detail: texts.reason };
64
65
  }
65
66
  return { ok: false, reason: 'error', detail: `${status}` };
66
67
  }
@@ -69,7 +70,8 @@ export async function steerEngineSubagent(target, text, childTaskId, opts) {
69
70
  hostLog('debug', `engineSubagentSteer: may have been received, no answer — ${noAnswer}`);
70
71
  return { ok: false, reason: 'error', detail: noAnswer, unconfirmed: true };
71
72
  }
72
- hostLog('debug', `engineSubagentSteer: fetch failed ${String(e)}`);
73
- return { ok: false, reason: 'error', detail: String(e) };
73
+ const texts = wireFailureTextsOf(e);
74
+ hostLog('debug', `engineSubagentSteer: fetch failed ${texts.log}`);
75
+ return { ok: false, reason: 'error', detail: texts.reason };
74
76
  }
75
77
  }
@@ -1,22 +1,11 @@
1
1
  import { DEFAULT_SESSION_KEY } from './sessionSlot.js';
2
+ import { toolEndOutputText } from './toolResultContent.js';
2
3
  export function coerceOutput(v) {
3
4
  if (v === undefined || v === null)
4
5
  return undefined;
5
- if (typeof v === 'string')
6
- return v;
7
- if (Array.isArray(v)) {
8
- const parts = [];
9
- for (const block of v) {
10
- if (block && typeof block === 'object') {
11
- const b = block;
12
- if (b.type === 'text' && typeof b.text === 'string')
13
- parts.push(b.text);
14
- else if (b.type === 'image')
15
- parts.push('[image]');
16
- }
17
- }
18
- return parts.join('');
19
- }
6
+ const text = toolEndOutputText(v);
7
+ if (text !== undefined)
8
+ return text;
20
9
  try {
21
10
  return JSON.stringify(v);
22
11
  }
@@ -8,4 +8,5 @@ export declare const SUPPORT_FLOOR: Readonly<SupportFloor>;
8
8
  export declare function supportFloorOf(component: unknown): string | undefined;
9
9
  export type EngineVersionTier = 'engine' | 'legacy' | 'unknown';
10
10
  export declare function engineVersionTier(version: unknown, floor?: string): EngineVersionTier;
11
+ export declare function engineSupportFloorEffective(floor?: string): string;
11
12
  export declare function coreVersionTier(version: unknown, floor?: string): EngineVersionTier;