@sema-agent/client-core 0.107.0 → 0.108.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 (98) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/README.md +19 -14
  3. package/dist/adapter/activeRunSelfHeal.d.ts +2 -0
  4. package/dist/adapter/activeRunSelfHeal.js +20 -3
  5. package/dist/checkpointRowVerdict.d.ts +5 -0
  6. package/dist/checkpointRowVerdict.js +26 -0
  7. package/dist/cloudConfigWireCaps.d.ts +10 -0
  8. package/dist/cloudConfigWireCaps.js +33 -0
  9. package/dist/coreValuePorts.d.ts +17 -1
  10. package/dist/coreValuePorts.js +5 -0
  11. package/dist/decideReceipt.js +2 -1
  12. package/dist/engineErrorCodes.d.ts +5 -0
  13. package/dist/engineErrorCodes.js +5 -0
  14. package/dist/engineNoticeCodes.d.ts +19 -1
  15. package/dist/engineNoticeCodes.js +65 -3
  16. package/dist/engineTargetSeat.js +1 -0
  17. package/dist/errorSupplementRoles.js +2 -0
  18. package/dist/eventLeg.js +1 -1
  19. package/dist/executionLaneWords.js +2 -1
  20. package/dist/gateOutcome.d.ts +10 -1
  21. package/dist/gateOutcome.js +48 -1
  22. package/dist/generated/engineFactTables.d.ts +1 -0
  23. package/dist/generated/engineFactTables.js +5 -0
  24. package/dist/generated/engineNoticeTables.js +3 -0
  25. package/dist/generated/settingsSchemaFns.d.ts +4 -2
  26. package/dist/generated/settingsSchemaFns.js +9 -3
  27. package/dist/generated/settingsSchemaMcpRequestEntry.d.ts +1 -1
  28. package/dist/generated/settingsSchemaMcpRequestEntry.js +9 -18
  29. package/dist/generated/settingsSchemaReservedMemberName.d.ts +1 -0
  30. package/dist/generated/settingsSchemaReservedMemberName.js +7 -0
  31. package/dist/generated/settingsSchemaTables.d.ts +2 -0
  32. package/dist/generated/settingsSchemaTables.js +10 -0
  33. package/dist/generated/settingsSchemaWireName.d.ts +1 -0
  34. package/dist/generated/settingsSchemaWireName.js +8 -0
  35. package/dist/headlessReconnectWire.js +12 -7
  36. package/dist/hitl/approvalAskers.d.ts +13 -0
  37. package/dist/hitl/approvalAskers.js +79 -0
  38. package/dist/hitl/approvalResolution.d.ts +3 -0
  39. package/dist/hitl/approvalResolution.js +22 -4
  40. package/dist/hitl/approvalsFeed.js +3 -12
  41. package/dist/hitl/askParkRowRouting.js +5 -1
  42. package/dist/hitl/decisionRefusal.d.ts +1 -0
  43. package/dist/hitl/decisionRefusal.js +5 -2
  44. package/dist/hitl/decisionSend.js +2 -6
  45. package/dist/hitl/frameRouter.d.ts +3 -2
  46. package/dist/hitl/frameRouter.js +28 -6
  47. package/dist/hitl/gateLedger.d.ts +2 -0
  48. package/dist/hitl/gateLedger.js +3 -1
  49. package/dist/hitl/hitlBridge.js +2 -3
  50. package/dist/hitl/parkResolver.js +32 -23
  51. package/dist/hitl/parkRowBirthWait.d.ts +2 -0
  52. package/dist/hitl/parkRowBirthWait.js +8 -1
  53. package/dist/hitl/persistedRulesWire.d.ts +5 -1
  54. package/dist/hitl/persistedRulesWire.js +20 -4
  55. package/dist/hitl/planReviewWire.js +12 -16
  56. package/dist/hitl/policyDenyCause.d.ts +3 -0
  57. package/dist/hitl/policyDenyCause.js +16 -0
  58. package/dist/hitl/sessionPolicyDeliverable.js +1 -1
  59. package/dist/hitl/toolApprovalWire.d.ts +8 -2
  60. package/dist/hitl/toolApprovalWire.js +22 -3
  61. package/dist/hooksReaderGenerations.d.ts +1 -0
  62. package/dist/hooksReaderGenerations.js +12 -0
  63. package/dist/hooksWireCaps.js +8 -4
  64. package/dist/i18n/messages.en.d.ts +38 -1
  65. package/dist/i18n/messages.en.js +40 -3
  66. package/dist/i18n/messages.zh.js +45 -3
  67. package/dist/index.d.ts +6 -1
  68. package/dist/index.js +4 -1
  69. package/dist/liveModelCatalog.d.ts +4 -2
  70. package/dist/liveModelCatalog.js +164 -26
  71. package/dist/model/catalogDefault.d.ts +25 -0
  72. package/dist/model/catalogDefault.js +44 -0
  73. package/dist/permissionWireCaps.d.ts +1 -0
  74. package/dist/permissionWireCaps.js +11 -0
  75. package/dist/pluginHooksWire.d.ts +5 -3
  76. package/dist/pluginHooksWire.js +116 -7
  77. package/dist/queuedDelivery.d.ts +1 -0
  78. package/dist/queuedDelivery.js +12 -0
  79. package/dist/resultFrameKeys.js +4 -2
  80. package/dist/resumeRefusalCopy.js +15 -8
  81. package/dist/runPermissionModeSwitch.d.ts +2 -1
  82. package/dist/runPermissionModeSwitch.js +16 -6
  83. package/dist/seatContract.d.ts +63 -10
  84. package/dist/seatContract.js +164 -11
  85. package/dist/sessionEngine.d.ts +2 -1
  86. package/dist/sessionEngine.js +5 -0
  87. package/dist/settingsHooksSalvage.d.ts +5 -0
  88. package/dist/settingsHooksSalvage.js +54 -0
  89. package/dist/subagentContentStore.js +1 -1
  90. package/dist/supportFloor.js +2 -0
  91. package/dist/taskNotificationAdmission.d.ts +19 -0
  92. package/dist/taskNotificationAdmission.js +84 -0
  93. package/dist/toolResult.js +2 -1
  94. package/dist/webSearchWireCaps.js +9 -4
  95. package/dist/wireRefusalCopy.d.ts +2 -1
  96. package/dist/wireRefusalCopy.js +2 -2
  97. package/docs/INTEGRATION-CLIENTS.md +816 -14
  98. package/package.json +4 -4
@@ -4,6 +4,8 @@ import { REWIND_MODES } from "./rewindWireCaps.js";
4
4
  import { SETTINGS_MODEL_API_WORDS } from "./generated/settingsSchemaTables.js";
5
5
  import { PROBE_VERDICTS } from "./modelCapabilityProbe.js";
6
6
  import { guardedRead, ownDataField, ownDataRefusalKeyOf, ownSendableField, readOwnDataValue } from "./ownKey.js";
7
+ import { isPlainRecord as isJsonPlainRecord } from "./jsonFaithful.js";
8
+ import { displayUntrustedMarks, quoteUntrustedForSentence } from "./displayUntrusted.js";
7
9
  import { MCP_LIVENESS_STATES } from "./mcpLiveness.js";
8
10
  import { THINKING_LATCH_EVENTS, THINKING_LATCH_ROW_WORDS } from "./thinkingLatchWords.js";
9
11
  import { EXECUTION_PLACEMENT_ROLES } from "./executionPlacementWords.js";
@@ -18,12 +20,7 @@ const nested = (prefix, verdict) => {
18
20
  const reason = verdict.reason.startsWith(`${prefix}:`) ? verdict.reason : `${prefix}: ${verdict.reason}`;
19
21
  return bad(reason, path);
20
22
  };
21
- const isPlainRecord = (v) => {
22
- if (typeof v !== "object" || v === null || Array.isArray(v))
23
- return false;
24
- const proto = Object.getPrototypeOf(v);
25
- return proto === null || (typeof proto === "object" && Object.getPrototypeOf(proto) === null);
26
- };
23
+ const isPlainRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && isJsonPlainRecord(v);
27
24
  const isAbsent = (v) => typeof v === "undefined";
28
25
  const optionalString = (v) => isAbsent(v) || typeof v === "string";
29
26
  const isFolderPath = (v) => typeof v === "string" && v.length > 0;
@@ -185,6 +182,8 @@ const R_NUM = ruleOf("finite number", (v) => typeof v === "number" && Number.isF
185
182
  const R_NUM0 = ruleOf("finite non-negative number", (v) => typeof v === "number" && Number.isFinite(v) && v >= 0);
186
183
  const R_INT0 = ruleOf("non-negative integer", (v) => typeof v === "number" && Number.isSafeInteger(v) && v >= 0);
187
184
  const R_INT1 = ruleOf("positive integer", (v) => typeof v === "number" && Number.isSafeInteger(v) && v > 0);
185
+ const SESSION_ENGINE_KIND_WORDS = Object.freeze(["local", "cloud"]);
186
+ const R_ENGINE_LANE = oneOfRule("engine lane", SESSION_ENGINE_KIND_WORDS);
188
187
  const keyWords = (k) => k
189
188
  .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
190
189
  .toLowerCase()
@@ -333,6 +332,7 @@ export const HITL_RESOLUTION_OUTCOMES = [
333
332
  "accept",
334
333
  "decline",
335
334
  "cancel",
335
+ "engine_unavailable",
336
336
  ];
337
337
  export const SEAT_STREAM_DELTA_TYPE_OF_CHANNEL = Object.freeze({ text: "text_delta", thinking: "thinking_delta" });
338
338
  export function seatStreamDeltaMessage(event, ids) {
@@ -381,7 +381,7 @@ export function readSeatStreamDelta(message) {
381
381
  return Object.freeze({ kind: "malformed" });
382
382
  }
383
383
  }
384
- export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch", "computerUseSwitch", "engineModeDefaultOnly"]);
384
+ export const SEAT_CAPABILITY_NAMES = Object.freeze(["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch", "computerUseSwitch", "engineModeDefaultOnly", "seatActions"]);
385
385
  const notReported = () => Object.freeze({ kind: "not_reported" });
386
386
  const malformedReading = () => Object.freeze({ kind: "malformed" });
387
387
  const reported = (available) => Object.freeze({ kind: "reported", available });
@@ -460,6 +460,7 @@ const SEAT_CAPABILITY_PHRASES = Object.freeze({
460
460
  thinkingLatch: Object.freeze({ action: "Switching thinking", ability: "switch thinking", support: "thinking-switch support" }),
461
461
  computerUseSwitch: Object.freeze({ action: "Computer use", ability: "be switched", support: "switch support" }),
462
462
  engineModeDefaultOnly: Object.freeze({ action: "Default-only engine switching", ability: "keep existing sessions on their engine", support: "default-only engine switching" }),
463
+ seatActions: Object.freeze({ action: "App menu actions", ability: "send actions to this window", support: "app action support" }),
463
464
  });
464
465
  export function seatCapabilityUnavailableDetail(name, reading) {
465
466
  const phrase = Object.hasOwn(SEAT_CAPABILITY_PHRASES, name)
@@ -668,6 +669,7 @@ export const ENGINE_TARGET_REFUSALS = Object.freeze([
668
669
  "not_configured",
669
670
  "not_signed_in",
670
671
  "probe_failed",
672
+ "cloud_unreachable",
671
673
  "org_policy",
672
674
  "url_refused",
673
675
  "login_failed",
@@ -766,7 +768,7 @@ const ENGINE_TARGET_LOCAL = shapeRule("local", {
766
768
  mcp: optF(SEAT_LOCAL_MCP_READING),
767
769
  });
768
770
  const ENGINE_TARGET_STATE = shapeRule("engine target", {
769
- mode: reqF(oneOfRule("engine mode", ["local", "cloud"])),
771
+ mode: reqF(R_ENGINE_LANE),
770
772
  generation: reqF(R_INT0),
771
773
  cloud: reqF(ENGINE_TARGET_CLOUD),
772
774
  local: reqF(ENGINE_TARGET_LOCAL),
@@ -1064,6 +1066,119 @@ export function planSetComputerUse(request, lockedByEnv) {
1064
1066
  return Object.freeze({ kind: "refused", reason: "confirmation-required" });
1065
1067
  return Object.freeze({ kind: "apply", enabled: intent.kind === "enable" });
1066
1068
  }
1069
+ export const SEAT_ACTION_NAMES = Object.freeze(["new-project", "prefill-composer"]);
1070
+ const SEAT_PREFILL_TEXT_MAX = 14336;
1071
+ const SEAT_PREFILL_CWD_MAX = 4096;
1072
+ const SEAT_ACTIONS_CLAIM_MAX = 16;
1073
+ const SEAT_ACTION_WORD_MAX = 64;
1074
+ const SEAT_TEXT_FACE = Object.freeze({ foldLines: false, keepLayout: "crlf", credentialUrls: false, credentialWords: false });
1075
+ const SEAT_PATH_FACE = Object.freeze({ foldLines: false, credentialUrls: false, credentialWords: false });
1076
+ const faceClean = (v, face) => displayUntrustedMarks(v, face).marks.length === 0;
1077
+ const R_PREFILL_TEXT = ruleOf(`prefill text (1–${SEAT_PREFILL_TEXT_MAX} chars, no control, bidi or other invisible characters)`, (v) => typeof v === "string" && v.length > 0 && v.length <= SEAT_PREFILL_TEXT_MAX && faceClean(v, SEAT_TEXT_FACE));
1078
+ const R_PREFILL_CWD = ruleOf(`folder path (1–${SEAT_PREFILL_CWD_MAX} chars, no control, bidi or other invisible characters)`, (v) => isFolderPath(v) && v.length <= SEAT_PREFILL_CWD_MAX && faceClean(v, SEAT_PATH_FACE));
1079
+ const SEAT_ACTION_ARMS = {
1080
+ "new-project": {},
1081
+ "prefill-composer": { text: reqF(R_PREFILL_TEXT), cwd: optF(R_PREFILL_CWD) },
1082
+ };
1083
+ export const checkSeatAction = seatShapeCheck(armsRule("seat action", "action", SEAT_ACTION_ARMS));
1084
+ const seatActionMalformed = (reason) => Object.freeze({ kind: "malformed", reason });
1085
+ export function readSeatAction(ev) {
1086
+ try {
1087
+ if (!isPlainRecord(ev))
1088
+ return seatActionMalformed("not a plain object");
1089
+ const name = own(ev, "action");
1090
+ if (typeof name !== "string")
1091
+ return seatActionMalformed("action must be a string");
1092
+ if (!inTuple(SEAT_ACTION_NAMES, name))
1093
+ return Object.freeze({ kind: "unknown", name: name.slice(0, SEAT_ACTION_WORD_MAX) });
1094
+ const verdict = checkSeatAction(ev);
1095
+ if (verdict !== true) {
1096
+ return seatActionMalformed(`${verdict.field ? `${verdict.field}: ` : ""}${verdict.reason}`);
1097
+ }
1098
+ if (name === "new-project")
1099
+ return Object.freeze({ kind: "action", action: Object.freeze({ action: "new-project" }) });
1100
+ const text = own(ev, "text");
1101
+ const cwd = own(ev, "cwd");
1102
+ if (R_PREFILL_TEXT.test(text, "text") !== true || typeof text !== "string")
1103
+ return seatActionMalformed("text: payload changed while being read");
1104
+ if (!isAbsent(cwd) && (R_PREFILL_CWD.test(cwd, "cwd") !== true || typeof cwd !== "string"))
1105
+ return seatActionMalformed("cwd: payload changed while being read");
1106
+ return Object.freeze({
1107
+ kind: "action",
1108
+ action: Object.freeze({ action: "prefill-composer", text, ...(typeof cwd === "string" ? { cwd } : {}) }),
1109
+ });
1110
+ }
1111
+ catch {
1112
+ return seatActionMalformed("payload could not be read");
1113
+ }
1114
+ }
1115
+ const quoteSeatWord = (w) => quoteUntrustedForSentence(w, SEAT_ACTION_WORD_MAX) ?? '""';
1116
+ const R_CLAIM_WORD = {
1117
+ label: "action word",
1118
+ test: (v, path) => typeof v === "string"
1119
+ ? inTuple(SEAT_ACTION_NAMES, v)
1120
+ ? true
1121
+ : bad(`unknown action word ${quoteSeatWord(v)} (closed set: ${SEAT_ACTION_NAMES.join("|")})`, path)
1122
+ : bad("bad value (action word)", path),
1123
+ };
1124
+ const claimListOnce = (v, path) => {
1125
+ const n = ownDataField(v, "length");
1126
+ if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0)
1127
+ return bad("bad value (action list)", path);
1128
+ if (n > SEAT_ACTIONS_CLAIM_MAX)
1129
+ return bad(`bad value (at most ${SEAT_ACTIONS_CLAIM_MAX} actions)`, path);
1130
+ for (const k of Reflect.ownKeys(v)) {
1131
+ if (typeof k === "string" && !isArraySlotKey(k, n))
1132
+ return bad("unknown key", pathAt(path, k));
1133
+ }
1134
+ const words = [];
1135
+ for (let i = 0; i < n; i += 1)
1136
+ words.push(own(v, String(i)));
1137
+ return words;
1138
+ };
1139
+ const R_CLAIM_LIST = {
1140
+ label: `action list (at most ${SEAT_ACTIONS_CLAIM_MAX})`,
1141
+ test: (v, path) => {
1142
+ if (!Array.isArray(v))
1143
+ return bad("bad value (action list)", path);
1144
+ const words = claimListOnce(v, path);
1145
+ if (!Array.isArray(words))
1146
+ return words;
1147
+ for (let i = 0; i < words.length; i += 1) {
1148
+ const verdict = R_CLAIM_WORD.test(words[i], `${path}[${i}]`);
1149
+ if (verdict !== true)
1150
+ return verdict;
1151
+ }
1152
+ return true;
1153
+ },
1154
+ };
1155
+ const checkSeatActionsClaimShape = seatShapeCheck(shapeRule("seat actions claim", { actions: reqF(R_CLAIM_LIST) }));
1156
+ export const checkSeatActionsClaim = seatCheck((v) => isPlainRecord(v) ? checkSeatActionsClaimShape(v) : bad("claimSeatActions request is not a plain object"));
1157
+ export function claimedSeatActionsOf(claim) {
1158
+ try {
1159
+ if (!isPlainRecord(claim))
1160
+ return Object.freeze([]);
1161
+ const snap = Object.create(null);
1162
+ for (const k of Reflect.ownKeys(claim)) {
1163
+ if (typeof k === "string")
1164
+ snap[k] = own(claim, k);
1165
+ }
1166
+ const list = snap.actions;
1167
+ if (Array.isArray(list)) {
1168
+ const words = claimListOnce(list, "actions");
1169
+ if (!Array.isArray(words))
1170
+ return Object.freeze([]);
1171
+ snap.actions = words;
1172
+ }
1173
+ if (checkSeatActionsClaim(snap) !== true)
1174
+ return Object.freeze([]);
1175
+ const taken = snap.actions;
1176
+ return Object.freeze(SEAT_ACTION_NAMES.filter((name) => taken.includes(name)));
1177
+ }
1178
+ catch {
1179
+ return Object.freeze([]);
1180
+ }
1181
+ }
1067
1182
  const checkLocalSessionRecord = seatCheck((s) => {
1068
1183
  if (!isPlainRecord(s))
1069
1184
  return bad("session record is not a plain object");
@@ -1608,6 +1723,16 @@ export const checkSetThinkingLatchRequest = seatCheck((v) => isPlainRecord(v) ?
1608
1723
  export function isSetThinkingLatchRequest(v) {
1609
1724
  return checkSetThinkingLatchRequest(v) === true;
1610
1725
  }
1726
+ const checkStartSessionOptionsShape = seatShapeCheck(shapeRule("start options", {
1727
+ model: optF(R_STR),
1728
+ title: optF(R_STR),
1729
+ cwd: optF(R_STR),
1730
+ excludeTools: optF(arrayRule(R_STR)),
1731
+ engine: optF(R_ENGINE_LANE),
1732
+ }));
1733
+ export const checkStartSessionOptions = seatCheck((v) => isAbsent(v) ? true : isPlainRecord(v) ? checkStartSessionOptionsShape(v) : bad("start options are not a plain object"));
1734
+ const checkSeatResourceScopeShape = seatShapeCheck(shapeRule("resource scope", { sessionId: reqF(R_NONEMPTY) }));
1735
+ export const checkSeatResourceScope = seatCheck((v) => isAbsent(v) ? true : isPlainRecord(v) ? checkSeatResourceScopeShape(v) : bad("resource scope is not a plain object"));
1611
1736
  const checkSendMessageAck = seatCheck((v) => {
1612
1737
  if (!isPlainRecord(v))
1613
1738
  return bad("sendMessage ack is not a plain object");
@@ -1782,8 +1907,10 @@ export const LOCAL_SESSIONS_SPEC = {
1782
1907
  setThinkingLatch: { kind: "invoke", check: checkOkAck },
1783
1908
  getComputerUse: { kind: "invoke", check: checkComputerUseState },
1784
1909
  setComputerUse: { kind: "invoke", check: checkSetComputerUseResult },
1910
+ claimSeatActions: { kind: "invoke", check: checkOkAck },
1785
1911
  onEvent: { kind: "event", check: checkLocalSessionEvent },
1786
1912
  onToolPermissionRequest: { kind: "event", check: checkToolPermissionRequest },
1913
+ onSeatAction: { kind: "event", check: checkSeatAction },
1787
1914
  },
1788
1915
  stores: {
1789
1916
  sessionsState: { check: checkSessionsState },
@@ -1832,8 +1959,10 @@ export const SEAT_METHOD_NAMES = [
1832
1959
  "setThinkingLatch",
1833
1960
  "getComputerUse",
1834
1961
  "setComputerUse",
1962
+ "claimSeatActions",
1835
1963
  "onEvent",
1836
1964
  "onToolPermissionRequest",
1965
+ "onSeatAction",
1837
1966
  ];
1838
1967
  export const SEAT_STORE_PROPS = [
1839
1968
  "sessionsStateStore",
@@ -1842,7 +1971,7 @@ export const SEAT_STORE_PROPS = [
1842
1971
  "seatCapabilitiesStateStore",
1843
1972
  "engineTargetStateStore",
1844
1973
  ];
1845
- export const SEAT_EVENT_METHOD_NAMES = ["onEvent", "onToolPermissionRequest"];
1974
+ export const SEAT_EVENT_METHOD_NAMES = ["onEvent", "onToolPermissionRequest", "onSeatAction"];
1846
1975
  export const LOCAL_SESSION_EVENT_KEYS = [
1847
1976
  "type",
1848
1977
  "sessionId",
@@ -1934,7 +2063,7 @@ export const TOOL_PERMISSION_DECISION_KEYS = [
1934
2063
  "updatedInput",
1935
2064
  "remember",
1936
2065
  ];
1937
- export const START_SESSION_OPTION_KEYS = ["model", "title", "cwd", "excludeTools"];
2066
+ export const START_SESSION_OPTION_KEYS = ["model", "title", "cwd", "excludeTools", "engine"];
1938
2067
  export const SEAT_VALIDATOR_KEY_COVERAGE = {
1939
2068
  LocalSessionRecord: {
1940
2069
  guard: "isLocalSessionRecord",
@@ -2039,6 +2168,10 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
2039
2168
  key: "modelPlane",
2040
2169
  why: "诊断面(0.106.0 CC-1156;这台引擎服务的是哪张模型表 / 最近一份目录候选的结局),三态读法归 modelPlaneOf(含坏形 unreadable、表外词整位不给);出站门为它拒掉整份目录 = 选单为一行诊断整个消失。",
2041
2170
  },
2171
+ {
2172
+ key: "aliases",
2173
+ why: "受理面(0.108.0 CC-944;除行名之外引擎还认的模型词),读法归 modelAcceptanceOf(缺席 = 更早的服务端,照旧判;在场而坏形 ⇒ 没找到行的词读 catalog_unreadable);出站门为它拒掉整份目录 = 选单为一份受理数据整个消失。",
2174
+ },
2042
2175
  ],
2043
2176
  },
2044
2177
  SendMessageRequest: {
@@ -2073,6 +2206,21 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
2073
2206
  validated: ["sessionId", "event"],
2074
2207
  unchecked: [],
2075
2208
  },
2209
+ StartSessionOptions: {
2210
+ check: checkStartSessionOptions,
2211
+ validated: ["model", "title", "cwd", "excludeTools", "engine"],
2212
+ unchecked: [],
2213
+ },
2214
+ SeatResourceScope: {
2215
+ check: checkSeatResourceScope,
2216
+ validated: ["sessionId"],
2217
+ unchecked: [],
2218
+ },
2219
+ SeatActionsClaim: {
2220
+ check: checkSeatActionsClaim,
2221
+ validated: ["actions"],
2222
+ unchecked: [],
2223
+ },
2076
2224
  ApplyModelConfigResult: {
2077
2225
  check: checkApplyModelConfigResult,
2078
2226
  validated: ["ok", "requiresRestart", "reason", "detail"],
@@ -2126,7 +2274,7 @@ export const SEAT_VALIDATOR_KEY_COVERAGE = {
2126
2274
  },
2127
2275
  SeatCapabilitiesState: {
2128
2276
  check: checkSeatCapabilitiesState,
2129
- validated: ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch", "computerUseSwitch", "engineModeDefaultOnly"],
2277
+ validated: ["pickFolder", "rewind", "recents", "patchSession", "devices", "engineTarget", "onboard", "mcpToolToggles", "thinkingLatch", "computerUseSwitch", "engineModeDefaultOnly", "seatActions"],
2130
2278
  unchecked: [],
2131
2279
  },
2132
2280
  RecentFoldersResult: {
@@ -2192,6 +2340,7 @@ export const SEAT_VALIDATOR_COVERAGE_EXEMPT = [
2192
2340
  ["ComputerUseState", checkComputerUseState],
2193
2341
  ["SetComputerUseResult", checkSetComputerUseResult],
2194
2342
  ["SetComputerUseRequest", checkSetComputerUseRequest],
2343
+ ["SeatAction", checkSeatAction],
2195
2344
  ].map(([label, check]) => ({
2196
2345
  label,
2197
2346
  check,
@@ -2251,5 +2400,9 @@ const _seatContractPins = [
2251
2400
  true, true,
2252
2401
  true, true, true, true,
2253
2402
  true, true,
2403
+ true, true, true,
2404
+ true, true, true, true,
2405
+ true, true,
2406
+ true, true, true, true, true, true, true,
2254
2407
  ];
2255
2408
  void _seatContractPins;
@@ -134,7 +134,7 @@ export interface SessionEngineWiring {
134
134
  readonly auth: EngineTargetCloudAuth | undefined;
135
135
  readonly explicitAddresses?: readonly string[];
136
136
  }
137
- export type SessionDispatchRefusal = 'not-wired' | 'scope-mismatch' | 'scope-unproven' | 'unresolved';
137
+ export type SessionDispatchRefusal = 'not-wired' | 'scope-mismatch' | 'scope-unproven' | 'unresolved' | 'session-ambiguous';
138
138
  export type SessionDispatchDecision = {
139
139
  readonly kind: 'self';
140
140
  readonly wiringSnapshotId: string;
@@ -153,4 +153,5 @@ export declare function sessionEngineBindingFor(input: SessionEngineBindingInput
153
153
  export declare function isSessionEngineView(v: {} | null | undefined): boolean;
154
154
  export declare function readSessionEngine(record: SessionMapRecord | undefined): SessionEngineReading;
155
155
  export declare function commitSessionEngine(store: SessionMapStorePort, session: SessionEngineSessionRef, request: SessionEngineCommitRequest): SessionEngineCommitResult;
156
+ export declare function sessionDispatchRefusedFor(refusal: SessionDispatchRefusal): SessionDispatchRefused;
156
157
  export declare function sessionDispatchTarget(reading: SessionEngineReading, ctx: SessionEngineWiring): SessionDispatchDecision;
@@ -382,8 +382,13 @@ const REFUSAL_KEYS = {
382
382
  'scope-mismatch': 'session.engine.dispatch.scopeMismatch',
383
383
  'scope-unproven': 'session.engine.dispatch.scopeUnproven',
384
384
  unresolved: 'session.engine.dispatch.unresolved',
385
+ 'session-ambiguous': 'session.engine.dispatch.sessionAmbiguous',
385
386
  };
386
387
  const refused = (refusal) => Object.freeze({ kind: 'refused', refusal, message: msg(REFUSAL_KEYS[refusal]) });
388
+ export function sessionDispatchRefusedFor(refusal) {
389
+ const word = Object.keys(REFUSAL_KEYS).find((w) => w === refusal);
390
+ return refused(word ?? 'unresolved');
391
+ }
387
392
  function authOf(ctx) {
388
393
  const slot = slotReadOf(ctx, 'auth');
389
394
  if (slot.presence === 'absent' || (slot.presence === 'present' && slot.value === undefined))
@@ -40,6 +40,11 @@ export declare function hooksSalvageMaskOf(dropped: readonly {
40
40
  readonly path: readonly (string | number)[];
41
41
  }[]): HooksSalvageMask;
42
42
  export declare function salvageSettingsHooksEntries(doc: SettingsDocument | null | undefined): HooksSalvage;
43
+ export declare function hooksGroupUnreadKeys(event: string, group: object): readonly string[] | undefined;
44
+ export type HooksGroupAuthoredSource = 'absent' | 'present' | 'unreadable';
45
+ export declare function hooksGroupAuthoredSource(group: object): HooksGroupAuthoredSource;
46
+ export declare function hooksEntryUnreadKeys(event: string, entry: object, notRead: readonly string[]): readonly string[] | undefined;
47
+ export declare function hooksKeyCoordinateText(path: readonly (string | number)[]): string;
43
48
  export declare const HOOK_SETTINGS_SOURCE_TEXT: Readonly<Record<HookSettingsSource, string>>;
44
49
  export declare function layerNotAppliedRefusesTurn(source: HookSettingsSource): boolean;
45
50
  export declare function settingsHooksSalvageNotice(salvage: HooksSalvage, source: HookSettingsSource, doc?: SettingsDocument | null): string | undefined;
@@ -1,5 +1,6 @@
1
1
  import { displayUntrusted } from './displayUntrusted.js';
2
2
  import { msg, renderMessage } from './i18n/translate.js';
3
+ import { appendOwnElement } from './ownKey.js';
3
4
  import { SETTINGS_CLASSIFY_HOOKS_CONFIG, } from './generated/settingsSchemaFns.js';
4
5
  const _tuplePins = [true, true, true, true, true, true];
5
6
  void _tuplePins;
@@ -110,6 +111,59 @@ export function salvageSettingsHooksEntries(doc) {
110
111
  rejections: Object.freeze([]),
111
112
  });
112
113
  }
114
+ const READER_GROUP_WIRE_KEYS = ['source'];
115
+ const READER_COMMAND_WIRE_KEYS = ['plugin', 'args'];
116
+ function unreadOf(raw, carried, wireKeys, notRead) {
117
+ const out = [];
118
+ for (const k of Object.keys(raw)) {
119
+ if (own(raw, k) === undefined || wireKeys.includes(k))
120
+ continue;
121
+ if (Object.hasOwn(carried, k) && !notRead.includes(k))
122
+ continue;
123
+ appendOwnElement(out, k);
124
+ }
125
+ return Object.freeze(out);
126
+ }
127
+ function carriedGroupOf(event, group) {
128
+ const r = SETTINGS_CLASSIFY_HOOKS_CONFIG({ hooks: { [event]: [group] } });
129
+ if (r.outcome !== 'loaded' || !isRecord(r.hooks))
130
+ return undefined;
131
+ const groups = own(r.hooks, event);
132
+ const g = Array.isArray(groups) ? groups[0] : undefined;
133
+ return isRecord(g) ? g : undefined;
134
+ }
135
+ export function hooksGroupUnreadKeys(event, group) {
136
+ const raw = group;
137
+ const carried = carriedGroupOf(event, { ...raw, hooks: [] });
138
+ return carried === undefined ? undefined : unreadOf(raw, carried, READER_GROUP_WIRE_KEYS, []);
139
+ }
140
+ export function hooksGroupAuthoredSource(group) {
141
+ try {
142
+ const d = Object.getOwnPropertyDescriptor(group, READER_GROUP_WIRE_KEYS[0]);
143
+ if (d === undefined)
144
+ return 'absent';
145
+ if (!Object.hasOwn(d, 'value'))
146
+ return 'unreadable';
147
+ if (d.value !== undefined)
148
+ return 'present';
149
+ return Reflect.get(group, READER_GROUP_WIRE_KEYS[0]) === undefined ? 'absent' : 'unreadable';
150
+ }
151
+ catch {
152
+ return 'unreadable';
153
+ }
154
+ }
155
+ export function hooksEntryUnreadKeys(event, entry, notRead) {
156
+ const raw = entry;
157
+ const g = carriedGroupOf(event, { hooks: [raw] });
158
+ const entries = g === undefined ? undefined : own(g, 'hooks');
159
+ const carried = Array.isArray(entries) ? entries[0] : undefined;
160
+ if (!isRecord(carried))
161
+ return undefined;
162
+ return unreadOf(raw, carried, own(carried, 'type') === 'command' ? READER_COMMAND_WIRE_KEYS : [], notRead);
163
+ }
164
+ export function hooksKeyCoordinateText(path) {
165
+ return `settings.${path.map(labelOf).join('.')}`;
166
+ }
113
167
  export const HOOK_SETTINGS_SOURCE_TEXT = Object.freeze({
114
168
  policySettings: 'Managed settings',
115
169
  userSettings: 'User settings',
@@ -812,7 +812,7 @@ export function recordSubagentOwnerFromProgress(ev, ownerRunId, ownerSessionId,
812
812
  if (typeof ev !== 'object' || ev === null)
813
813
  return;
814
814
  const o = ev;
815
- if (o.type !== 'task_progress')
815
+ if (o.type !== 'task_progress' && o.type !== 'task_started')
816
816
  return;
817
817
  if (typeof o.taskId !== 'string' || o.taskId === '')
818
818
  return;
@@ -40,6 +40,8 @@ function judgeAgainst(version, base, floor, caller) {
40
40
  const got = releaseTriple(version);
41
41
  if (got === undefined)
42
42
  return { need: need.text, tier: 'unknown' };
43
+ if (got[0] === 0)
44
+ return { need: need.text, tier: 'unknown' };
43
45
  return { need: need.text, tier: compareTriple(got, need.triple) >= 0 ? 'engine' : 'legacy' };
44
46
  }
45
47
  export function engineVersionTier(version, floor) {
@@ -0,0 +1,19 @@
1
+ import type { RunRecord } from '@sema-agent/sdk';
2
+ export type TaskNotificationAdmissionVerdict = 'admitted' | 'pending' | 'not_found' | 'unreadable';
3
+ export type TaskNotificationAdmissionWhy = 'admitted_at' | 'turns_positive' | 'zero_turns' | 'no_turn_count' | 'key_unbound' | 'read_error' | 'record_malformed' | 'input_unrecognized';
4
+ export type TaskNotificationRunRecordView = RunRecord & {
5
+ readonly admittedAt?: string | null;
6
+ };
7
+ export type TaskNotificationReadResult = {
8
+ readonly kind: 'record';
9
+ readonly record: TaskNotificationRunRecordView;
10
+ } | {
11
+ readonly kind: 'not_found';
12
+ } | {
13
+ readonly kind: 'error';
14
+ };
15
+ export interface TaskNotificationAdmission {
16
+ readonly verdict: TaskNotificationAdmissionVerdict;
17
+ readonly why: TaskNotificationAdmissionWhy;
18
+ }
19
+ export declare function taskNotificationAdmissionOf(readResult: TaskNotificationReadResult): TaskNotificationAdmission;
@@ -0,0 +1,84 @@
1
+ import { guardedRead, readOwnDataValue } from './ownKey.js';
2
+ function verdictOfWhy(why) {
3
+ switch (why) {
4
+ case 'admitted_at':
5
+ case 'turns_positive':
6
+ return 'admitted';
7
+ case 'zero_turns':
8
+ case 'no_turn_count':
9
+ return 'pending';
10
+ case 'key_unbound':
11
+ return 'not_found';
12
+ case 'read_error':
13
+ case 'record_malformed':
14
+ case 'input_unrecognized':
15
+ return 'unreadable';
16
+ default: {
17
+ const unreachable = why;
18
+ return unreachable;
19
+ }
20
+ }
21
+ }
22
+ function ownData(o, key) {
23
+ return guardedRead(() => readOwnDataValue(o, key));
24
+ }
25
+ function isJsonObject(v) {
26
+ if (typeof v !== 'object' || v === null)
27
+ return false;
28
+ try {
29
+ return !Array.isArray(v);
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ }
35
+ function admittedAtProves(v) {
36
+ if (typeof v !== 'string')
37
+ return false;
38
+ const m = /^(\d{4})-(\d{2})-(\d{2})T\d{2}:\d{2}:\d{2}(?:\.\d{1,9})?(?:Z|[+-]\d{2}:\d{2})$/.exec(v);
39
+ if (m === null)
40
+ return false;
41
+ const year = Number(m[1]);
42
+ const month = Number(m[2]);
43
+ const day = Number(m[3]);
44
+ const leap = (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
45
+ const daysInMonth = month === 2 ? (leap ? 29 : 28) : month === 4 || month === 6 || month === 9 || month === 11 ? 30 : 31;
46
+ return month >= 1 && month <= 12 && day >= 1 && day <= daysInMonth && Number.isFinite(Date.parse(v));
47
+ }
48
+ function recordWhy(record) {
49
+ if (admittedAtProves(ownData(record, 'admittedAt')))
50
+ return 'admitted_at';
51
+ const result = ownData(record, 'result');
52
+ const stats = isJsonObject(result) ? ownData(result, 'stats') : undefined;
53
+ const turns = isJsonObject(stats) ? ownData(stats, 'turns') : undefined;
54
+ if (typeof turns === 'number' && Number.isInteger(turns)) {
55
+ if (turns > 0)
56
+ return 'turns_positive';
57
+ if (turns === 0)
58
+ return 'zero_turns';
59
+ }
60
+ return 'no_turn_count';
61
+ }
62
+ function readWhy(readResult) {
63
+ if (typeof readResult !== 'object' || readResult === null)
64
+ return 'input_unrecognized';
65
+ const kind = ownData(readResult, 'kind');
66
+ if (kind === 'not_found')
67
+ return 'key_unbound';
68
+ if (kind === 'error')
69
+ return 'read_error';
70
+ if (kind !== 'record')
71
+ return 'input_unrecognized';
72
+ const record = ownData(readResult, 'record');
73
+ return isJsonObject(record) ? recordWhy(record) : 'record_malformed';
74
+ }
75
+ export function taskNotificationAdmissionOf(readResult) {
76
+ let why;
77
+ try {
78
+ why = readWhy(readResult);
79
+ }
80
+ catch {
81
+ why = 'input_unrecognized';
82
+ }
83
+ return Object.freeze({ verdict: verdictOfWhy(why), why });
84
+ }
@@ -9,11 +9,12 @@ import { displayUntrusted } from './displayUntrusted.js';
9
9
  import { isCoreSemaTaskTerminal } from './terminalStatuses.js';
10
10
  import { CORE_ENGINE_CARD_TYPES } from './generated/engineFactTables.js';
11
11
  export const STRUCTURED_DETAIL_TYPES = frozenSet(CORE_ENGINE_CARD_TYPES);
12
+ const LEGACY_STRUCTURED_DETAIL_TYPE = 'task-output';
12
13
  export function structuredDetailType(structured) {
13
14
  if (typeof structured !== 'object' || structured === null)
14
15
  return undefined;
15
16
  const t = structured.type;
16
- return typeof t === 'string' && STRUCTURED_DETAIL_TYPES.has(t) ? t : undefined;
17
+ return typeof t === 'string' && (STRUCTURED_DETAIL_TYPES.has(t) || t === LEGACY_STRUCTURED_DETAIL_TYPE) ? t : undefined;
17
18
  }
18
19
  const MAX_STRUCTURED_FIELD_CHARS = 1_000_000;
19
20
  export const PROTOCOL_MARKERS = {
@@ -1,5 +1,6 @@
1
1
  import { SETTINGS_IS_HIERARCHICAL_URL } from './generated/settingsSchemaFns.js';
2
2
  import { hostEnv } from './hostEnv.js';
3
+ import { msg, renderMessage } from './i18n/translate.js';
3
4
  export const WEBSEARCH_ENV = {
4
5
  provider: 'SEMA_WEBSEARCH_PROVIDER',
5
6
  endpoint: 'SEMA_WEBSEARCH_ENDPOINT',
@@ -49,6 +50,9 @@ function apiKeyHeaderValue(provider, key) {
49
50
  return `Bearer ${key}`;
50
51
  }
51
52
  }
53
+ function apiKeyWayOutSentence() {
54
+ return renderMessage(msg('config.webSearch.apiKeyWayOut'));
55
+ }
52
56
  function judgeApiKey(provider, raw) {
53
57
  const text = judgeText(raw);
54
58
  if (!text.ok || text.value === undefined)
@@ -215,17 +219,18 @@ function envNameOfField(field) {
215
219
  export function webSearchVerdictMessage(verdict, context = 'submit') {
216
220
  if (verdict.kind !== 'malformed')
217
221
  return undefined;
222
+ const wayOut = verdict.field === 'apiKey' && verdict.issue === API_KEY_ISSUE ? ` ${apiKeyWayOutSentence()}` : '';
218
223
  if (verdict.source === 'host') {
219
224
  const head = `Web search is misconfigured: the API key your host supplied for webSearch is malformed; it ${verdict.issue}.`;
220
225
  return context === 'submit'
221
- ? `${head} The request was not sent — the key needs replacing where your host keeps it.`
222
- : `${head} Requests will be refused until the key is replaced where your host keeps it.`;
226
+ ? `${head} The request was not sent — the key needs replacing where your host keeps it.${wayOut}`
227
+ : `${head} Requests will be refused until the key is replaced where your host keeps it.${wayOut}`;
223
228
  }
224
229
  const where = verdict.source === 'env' ? envNameOfField(verdict.field) : verdict.field === 'section' ? 'the webSearch section in settings' : `webSearch.${verdict.field} in settings`;
225
230
  const fix = verdict.source === 'env' ? 'fix it or unset the SEMA_WEBSEARCH_* variables' : 'fix it or remove the webSearch section';
226
231
  return context === 'submit'
227
- ? `Web search is misconfigured: ${where} ${verdict.issue}. The request was not sent — ${fix}.`
228
- : `Web search is misconfigured: ${where} ${verdict.issue}. Requests will be refused until you ${fix}.`;
232
+ ? `Web search is misconfigured: ${where} ${verdict.issue}. The request was not sent — ${fix}.${wayOut}`
233
+ : `Web search is misconfigured: ${where} ${verdict.issue}. Requests will be refused until you ${fix}.${wayOut}`;
229
234
  }
230
235
  export function webSearchEndpointForDisplay(raw) {
231
236
  if (typeof raw !== 'string' || !SETTINGS_IS_HIERARCHICAL_URL(raw))
@@ -143,7 +143,8 @@ export interface SessionArchiveStampedDetail {
143
143
  readonly code: typeof CONFLICT_SESSION_ARCHIVE_STAMPED;
144
144
  }
145
145
  export declare function sessionArchiveStampedFromError(err: {} | null | undefined): SessionArchiveStampedDetail | null;
146
- export declare function sessionArchiveStampedContent(lang?: UiLanguage): string;
146
+ export type SessionArchiveStampedScenario = 'fork' | 'push';
147
+ export declare function sessionArchiveStampedContent(lang?: UiLanguage, scenario?: SessionArchiveStampedScenario): string;
147
148
  export interface UnsupportedRuleRefusalProblem {
148
149
  readonly code: string;
149
150
  readonly field?: string;
@@ -812,8 +812,8 @@ export function autonomyLaneRefusalContent(lang) {
812
812
  export function sessionArchiveStampedFromError(err) {
813
813
  return effectiveWireErrorCode(err) === CONFLICT_SESSION_ARCHIVE_STAMPED ? { code: CONFLICT_SESSION_ARCHIVE_STAMPED } : null;
814
814
  }
815
- export function sessionArchiveStampedContent(lang) {
816
- return renderMessage(msg('notice.wireRefusal.sessionArchiveStamped'), lang);
815
+ export function sessionArchiveStampedContent(lang, scenario) {
816
+ return renderMessage(msg(scenario === 'fork' ? 'notice.wireRefusal.sessionArchiveStamped.fork' : 'notice.wireRefusal.sessionArchiveStamped'), lang);
817
817
  }
818
818
  const REQUEST_UNPROCESSABLE = 'request.unprocessable';
819
819
  const UNSUPPORTED_RULE_PROBLEMS_MAX = REFUSAL_PROBLEMS_READ_MAX;