@sema-agent/client-core 0.112.1 → 0.113.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 (178) hide show
  1. package/CHANGELOG.md +130 -0
  2. package/README.md +36 -23
  3. package/dist/adapt/arms.js +7 -1
  4. package/dist/adapt/textStream.d.ts +2 -0
  5. package/dist/adapt/textStream.js +27 -1
  6. package/dist/adapter/activeRunSelfHeal.d.ts +7 -5
  7. package/dist/adapter/activeRunSelfHeal.js +38 -66
  8. package/dist/adapter/downstream/eventToSdkMessage.js +4 -0
  9. package/dist/adapter/downstream/terminalToSdkResult.d.ts +8 -0
  10. package/dist/adapter/downstream/terminalToSdkResult.js +36 -1
  11. package/dist/adapter/runStream.d.ts +2 -12
  12. package/dist/adapter/runStream.js +46 -19
  13. package/dist/agentSession/engineAgentRegistryRead.js +2 -1
  14. package/dist/agentSession/engineSessionBackgroundStop.js +2 -2
  15. package/dist/attachmentOutcome.d.ts +35 -0
  16. package/dist/attachmentOutcome.js +192 -0
  17. package/dist/attachmentPathPlan.d.ts +67 -0
  18. package/dist/attachmentPathPlan.js +203 -0
  19. package/dist/attachmentRoute.d.ts +9 -1
  20. package/dist/attachmentRoute.js +23 -2
  21. package/dist/declared/supportFloorDeclared.d.ts +1 -1
  22. package/dist/declared/supportFloorDeclared.js +1 -0
  23. package/dist/deviceRebind.js +2 -3
  24. package/dist/displayUntrusted.js +126 -19
  25. package/dist/engineAddress.d.ts +1 -1
  26. package/dist/engineAddress.js +9 -6
  27. package/dist/engineErrorCodes.d.ts +3 -0
  28. package/dist/engineErrorCodes.js +4 -0
  29. package/dist/engineNoticeCodes.d.ts +34 -4
  30. package/dist/engineNoticeCodes.js +133 -6
  31. package/dist/engineUnavailable.d.ts +4 -2
  32. package/dist/engineUnavailable.js +99 -17
  33. package/dist/fleet/fleetLedger.d.ts +9 -3
  34. package/dist/fleet/fleetLedger.js +112 -16
  35. package/dist/fleet/fleetProjection.d.ts +5 -0
  36. package/dist/fleet/fleetProjection.js +9 -1
  37. package/dist/gateVocabulary.d.ts +7 -0
  38. package/dist/gateVocabulary.js +40 -14
  39. package/dist/generated/engineFactTables.d.ts +4 -2
  40. package/dist/generated/engineFactTables.js +198 -2
  41. package/dist/generated/engineNoticeTables.js +56 -8
  42. package/dist/generated/settingsSchemaFns.d.ts +7 -0
  43. package/dist/generated/settingsSchemaFns.js +11 -0
  44. package/dist/generated/settingsSchemaHash.d.ts +1 -0
  45. package/dist/generated/settingsSchemaHash.js +58 -0
  46. package/dist/generated/settingsSchemaOwnKeyRead.js +6 -1
  47. package/dist/generated/settingsSchemaTables.d.ts +3 -2
  48. package/dist/generated/settingsSchemaTables.js +14 -0
  49. package/dist/generated/settingsSchemaTightenVerdict.d.ts +1 -0
  50. package/dist/generated/settingsSchemaTightenVerdict.js +125 -0
  51. package/dist/headlessPermissionModeWire.d.ts +7 -2
  52. package/dist/headlessPermissionModeWire.js +77 -8
  53. package/dist/hitl/approvalDecisionDoor.d.ts +24 -0
  54. package/dist/hitl/approvalDecisionDoor.js +77 -0
  55. package/dist/hitl/approvalDelivery.d.ts +6 -0
  56. package/dist/hitl/approvalDelivery.js +57 -7
  57. package/dist/hitl/approvalResolution.d.ts +6 -3
  58. package/dist/hitl/approvalResolution.js +24 -2
  59. package/dist/hitl/approvalsFeed.js +1 -1
  60. package/dist/hitl/automaticAnswerVerdict.d.ts +1 -1
  61. package/dist/hitl/decisionSend.d.ts +7 -4
  62. package/dist/hitl/decisionSend.js +90 -41
  63. package/dist/hitl/frameRouter.d.ts +3 -3
  64. package/dist/hitl/frameRouter.js +5 -0
  65. package/dist/hitl/hitlHostSurface.d.ts +3 -2
  66. package/dist/hitl/hitlHostSurface.js +5 -3
  67. package/dist/hitl/legacyApprovalShapes.d.ts +142 -0
  68. package/dist/hitl/legacyApprovalShapes.js +1 -0
  69. package/dist/hitl/livePendingAsk.d.ts +4 -4
  70. package/dist/hitl/livePendingAsk.js +5 -3
  71. package/dist/hitl/parkResolver.js +1 -1
  72. package/dist/hitl/persistedRulesWire.js +2 -2
  73. package/dist/hitl/planReviewCard.d.ts +2 -1
  74. package/dist/hitl/planReviewCard.js +64 -1
  75. package/dist/hitl/planReviewWire.d.ts +30 -0
  76. package/dist/hitl/planReviewWire.js +204 -65
  77. package/dist/hitl/sessionPolicyWire.js +16 -7
  78. package/dist/hitl/subagentSource.d.ts +1 -0
  79. package/dist/hitl/subagentSource.js +11 -0
  80. package/dist/hitl/toolApprovalWire.d.ts +34 -10
  81. package/dist/hitl/toolApprovalWire.js +182 -33
  82. package/dist/hookMatcherVerdict.d.ts +2 -1
  83. package/dist/hookMatcherVerdict.js +3 -2
  84. package/dist/hooksReaderGenerations.d.ts +8 -0
  85. package/dist/hooksReaderGenerations.js +49 -0
  86. package/dist/hooksWireCaps.js +43 -18
  87. package/dist/hostSettingsSource.d.ts +1 -1
  88. package/dist/hostSettingsSource.js +2 -2
  89. package/dist/hostTurnSettings.d.ts +2 -2
  90. package/dist/hostTurnSettings.js +87 -47
  91. package/dist/humanTurn.d.ts +2 -1
  92. package/dist/humanTurn.js +44 -13
  93. package/dist/humanTurnRead.d.ts +7 -0
  94. package/dist/humanTurnRead.js +23 -14
  95. package/dist/humanTurnSentences.js +3 -0
  96. package/dist/i18n/messages.en.d.ts +91 -5
  97. package/dist/i18n/messages.en.js +98 -12
  98. package/dist/i18n/messages.zh.js +108 -15
  99. package/dist/index.d.ts +29 -15
  100. package/dist/index.js +17 -9
  101. package/dist/liveModelCatalog.js +1 -0
  102. package/dist/liveQuestionStore.d.ts +2 -0
  103. package/dist/liveQuestionStore.js +7 -2
  104. package/dist/localConfig/kernel.d.ts +2 -1
  105. package/dist/localConfig/kernel.js +12 -18
  106. package/dist/localConfig/mcp/edit.js +25 -27
  107. package/dist/localConfig/mcp/servers.js +5 -8
  108. package/dist/localConfig/modelPool.d.ts +6 -1
  109. package/dist/localConfig/modelPool.js +104 -27
  110. package/dist/localConfig/modelPoolWords.d.ts +2 -0
  111. package/dist/localConfig/modelPoolWords.js +1 -0
  112. package/dist/localConfig/settingsWrite.js +1 -14
  113. package/dist/localConfig/skills.js +9 -16
  114. package/dist/localEngineSpawn.d.ts +29 -1
  115. package/dist/localEngineSpawn.js +62 -1
  116. package/dist/mcpLiveness.d.ts +1 -1
  117. package/dist/mcpProbeWire.js +2 -2
  118. package/dist/mcpReconnect.js +2 -2
  119. package/dist/model/catalogLoader.js +3 -17
  120. package/dist/model/onboardJudgement.d.ts +8 -0
  121. package/dist/model/onboardJudgement.js +8 -3
  122. package/dist/model/onboardSteps.d.ts +19 -0
  123. package/dist/model/onboardSteps.js +76 -0
  124. package/dist/notifications.js +6 -0
  125. package/dist/ownKey.d.ts +4 -0
  126. package/dist/ownKey.js +61 -0
  127. package/dist/pathShape.d.ts +14 -0
  128. package/dist/pathShape.js +44 -0
  129. package/dist/planReviewCardFacts.d.ts +3 -0
  130. package/dist/planReviewCardFacts.js +20 -3
  131. package/dist/pluginHooksWire.d.ts +20 -8
  132. package/dist/pluginHooksWire.js +204 -86
  133. package/dist/prepareRefusal.d.ts +2 -0
  134. package/dist/prepareRefusal.js +11 -1
  135. package/dist/request/taskRequest.js +3 -1
  136. package/dist/resultFrameKeys.d.ts +1 -0
  137. package/dist/resultFrameKeys.js +3 -0
  138. package/dist/resumeRefusalCopy.js +3 -1
  139. package/dist/retryStatus.d.ts +11 -1
  140. package/dist/retryStatus.js +15 -4
  141. package/dist/runTerminal.d.ts +1 -0
  142. package/dist/runTerminal.js +15 -1
  143. package/dist/sdkWireTransit.d.ts +1 -1
  144. package/dist/seatContract.d.ts +12 -3
  145. package/dist/seatContract.js +24 -13
  146. package/dist/sessionEngine.js +1 -1
  147. package/dist/sessionHooksRoster.js +8 -6
  148. package/dist/sessionIdSegment.d.ts +0 -1
  149. package/dist/sessionIdSegment.js +1 -3
  150. package/dist/sessionMemoryErase.js +2 -2
  151. package/dist/sessionMemoryStatus.js +2 -1
  152. package/dist/sessionOccupant.d.ts +29 -0
  153. package/dist/sessionOccupant.js +78 -0
  154. package/dist/settingsHooksSalvage.d.ts +4 -1
  155. package/dist/settingsHooksSalvage.js +22 -9
  156. package/dist/skillOverrideChain.d.ts +3 -0
  157. package/dist/skillOverrideChain.js +31 -4
  158. package/dist/skillsWireCaps.d.ts +16 -2
  159. package/dist/skillsWireCaps.js +47 -20
  160. package/dist/subagent/agentStateDetail.js +32 -8
  161. package/dist/subagent/engineCompactWire.js +48 -36
  162. package/dist/subagent/engineDelegatedPrompt.d.ts +3 -2
  163. package/dist/subagent/engineDelegatedPrompt.js +65 -28
  164. package/dist/subagentEditOutcomes.d.ts +9 -0
  165. package/dist/subagentEditOutcomes.js +43 -0
  166. package/dist/supportFloor.d.ts +2 -1
  167. package/dist/supportFloor.js +1 -1
  168. package/dist/toolResult.js +4 -2
  169. package/dist/toolRoster.d.ts +1 -0
  170. package/dist/toolRoster.js +1 -0
  171. package/dist/usageAnalytics.js +2 -1
  172. package/dist/userAttachmentEnvelope.d.ts +11 -0
  173. package/dist/userAttachmentEnvelope.js +7 -2
  174. package/dist/userAttachmentSeat.d.ts +7 -1
  175. package/dist/userAttachmentSeat.js +44 -7
  176. package/dist/wiringInstructionsAttribution.js +8 -2
  177. package/docs/INTEGRATION-CLIENTS.md +1588 -16
  178. package/package.json +5 -5
@@ -17,7 +17,17 @@ export const ENGINE_UNAVAILABLE_REASONS = Object.freeze([
17
17
  ]);
18
18
  export const ENGINE_INSTALL_SHAPES = Object.freeze(['npm-global', 'managed', 'npm-local', 'dev', 'app', 'unknown']);
19
19
  const SCHEMA_MISMATCH_CODE = 'config.schema_mismatch';
20
- const WAYS_OUT_ANCHOR = /Ways out \(these three and no others\):/;
20
+ const WAYS_OUT_FORMS = Object.freeze([
21
+ Object.freeze({ header: /Ways out \(these three and no others\):/, waysOut: Object.freeze(['newer_build', 'separate_data_root', 'restore_backup', 'repair_access']) }),
22
+ Object.freeze({
23
+ header: /Ways out \(these four and no others\):/,
24
+ waysOut: Object.freeze(['newer_build', 'separate_data_root', 'restore_backup', 'repair_access', 'start_migration_release']),
25
+ }),
26
+ ]);
27
+ const WAYS_OUT_HEADER_PREFIX = 'Ways out (';
28
+ const LOCAL_DATA_ROOT_NAMED = /(?:^|\n)[ \t]*LocalDataRootSchemaMismatchError(?: \[config\.schema_mismatch\])?: config\.schema_mismatch:/;
29
+ const LIBRARY_NAMED = /(?:^|\n)[ \t]*SchemaMismatchRefusedError(?: \[config\.schema_mismatch\])?: config\.schema_mismatch:/;
30
+ const SKIPPED_MIGRATION_FINDING = /Start the root once with a release at or above (\S+) that still carries that migration/;
21
31
  const MARKER_ROLE = /^the local engine high-water marker$/;
22
32
  export const ENGINE_BOOT_REFUSAL_CODES = Object.freeze(Object.fromEntries([
23
33
  ...SETTINGS_MODEL_PLANE_REFUSAL_CODES.map((c) => [c, 'model_setup']),
@@ -86,13 +96,32 @@ function refusalCodesIn(text) {
86
96
  const BOOT_REFUSAL_MAX_CHARS = 800;
87
97
  const BOOT_REFUSAL_MAX_HINTS = 2;
88
98
  const BOOT_REFUSAL_MAX_FINDINGS = 8;
99
+ const BOOT_REFUSAL_MAX_READ_LINES = 4096;
89
100
  const CONFIG_CODE = /^config\.[A-Za-z0-9_.-]+$/;
90
101
  function capSentence(s) {
91
102
  const t = s.trim();
92
103
  return t.length > BOOT_REFUSAL_MAX_CHARS ? `${t.slice(0, BOOT_REFUSAL_MAX_CHARS)}…` : t;
93
104
  }
105
+ function waysOutFormOf(text) {
106
+ let best;
107
+ let bestAt = Number.POSITIVE_INFINITY;
108
+ for (const form of WAYS_OUT_FORMS) {
109
+ const at = text.search(form.header);
110
+ if (at >= 0 && at < bestAt) {
111
+ best = form;
112
+ bestAt = at;
113
+ }
114
+ }
115
+ return best;
116
+ }
117
+ function waysOutHeaderUnlisted(text) {
118
+ return refusalCodesIn(text).includes(SCHEMA_MISMATCH_CODE) && waysOutFormOf(text) === undefined && text.includes(WAYS_OUT_HEADER_PREFIX);
119
+ }
94
120
  function dataRootReadingOf(text) {
95
- if (!text.includes(SCHEMA_MISMATCH_CODE) || !WAYS_OUT_ANCHOR.test(text))
121
+ if (!text.includes(SCHEMA_MISMATCH_CODE) || LIBRARY_NAMED.test(text))
122
+ return undefined;
123
+ const form = waysOutFormOf(text);
124
+ if (form === undefined && !LOCAL_DATA_ROOT_NAMED.test(text))
96
125
  return undefined;
97
126
  const lines = text.split(/\r?\n/);
98
127
  const at = lines.findIndex((l) => l.includes(`${SCHEMA_MISMATCH_CODE}:`));
@@ -107,14 +136,17 @@ function dataRootReadingOf(text) {
107
136
  let highWater;
108
137
  let engineVersion;
109
138
  let markerPath;
139
+ let migrationRelease;
110
140
  let newer = false;
111
141
  let unreadable = false;
112
- for (let i = at + 1; i < lines.length && findings.length < BOOT_REFUSAL_MAX_FINDINGS; i++) {
142
+ let skipped = false;
143
+ for (let i = at + 1; i < lines.length && i - at <= BOOT_REFUSAL_MAX_READ_LINES; i++) {
113
144
  const l = lines[i];
114
- if (WAYS_OUT_ANCHOR.test(l) || !/^\s+\S/.test(l))
145
+ if (l.includes(WAYS_OUT_HEADER_PREFIX) || !/^\s+\S/.test(l))
115
146
  break;
116
- const f = capSentence(l);
117
- findings.push(f);
147
+ if (findings.length < BOOT_REFUSAL_MAX_FINDINGS)
148
+ findings.push(capSentence(l));
149
+ const f = l.trim();
118
150
  const hw = /: engine high-water (\S+) — .*NEWER than this build \(([^()\s]+)\)/.exec(f);
119
151
  if (hw !== null) {
120
152
  newer = true;
@@ -131,6 +163,14 @@ function dataRootReadingOf(text) {
131
163
  newer = true;
132
164
  continue;
133
165
  }
166
+ const sk = SKIPPED_MIGRATION_FINDING.exec(l);
167
+ if (sk !== null) {
168
+ skipped = true;
169
+ const r = sk[1];
170
+ if (isRelease(r) && (migrationRelease === undefined || compareReleaseVersions(r, migrationRelease) === 1))
171
+ migrationRelease = r;
172
+ continue;
173
+ }
134
174
  const un = /^\S+ \(([^()]*)\) at (.+?): unreadable \([^()\s]+\)/.exec(f);
135
175
  if (un !== null) {
136
176
  unreadable = true;
@@ -139,11 +179,14 @@ function dataRootReadingOf(text) {
139
179
  }
140
180
  }
141
181
  return {
142
- variant: newer ? 'newer_root' : unreadable ? 'unreadable' : 'unknown',
182
+ variant: newer ? 'newer_root' : unreadable ? 'unreadable' : skipped ? 'skipped_migration' : 'unknown',
143
183
  findings: [head, ...findings],
144
184
  ...(highWater !== undefined ? { highWater } : {}),
145
185
  ...(engineVersion !== undefined ? { engineVersion } : {}),
146
186
  ...(markerPath !== undefined ? { markerPath } : {}),
187
+ ...(migrationRelease !== undefined ? { migrationRelease } : {}),
188
+ ...(form !== undefined ? { waysOut: form.waysOut } : {}),
189
+ waysOutUnlisted: form === undefined,
147
190
  };
148
191
  }
149
192
  function classifyRefusalText(text) {
@@ -177,6 +220,7 @@ export function engineBootRefusalOf(lines, scrub) {
177
220
  let fatalClass;
178
221
  let fatalCode;
179
222
  let entryClass;
223
+ let fatalUnlisted = false;
180
224
  const list = Array.isArray(lines) ? [...lines] : [];
181
225
  const plain = [];
182
226
  for (const raw of list) {
@@ -204,6 +248,7 @@ export function engineBootRefusalOf(lines, scrub) {
204
248
  if (s !== undefined && s.trim() !== '') {
205
249
  fatal = capSentence(s);
206
250
  fatalClass = classifyRefusalText(s);
251
+ fatalUnlisted = fatalClass === undefined && waysOutHeaderUnlisted(s);
207
252
  fatalCode = refusalCodesIn(s).find((x) => CONFIG_CODE.test(x));
208
253
  }
209
254
  continue;
@@ -250,6 +295,9 @@ export function engineBootRefusalOf(lines, scrub) {
250
295
  if (text === undefined)
251
296
  text = capSentence(block);
252
297
  }
298
+ else if (block !== undefined && waysOutHeaderUnlisted(block)) {
299
+ fatalUnlisted = true;
300
+ }
253
301
  }
254
302
  }
255
303
  if (text === undefined)
@@ -257,6 +305,7 @@ export function engineBootRefusalOf(lines, scrub) {
257
305
  if (code === undefined)
258
306
  code = cls?.code ?? fatalCode;
259
307
  const dr = cls?.dataRoot;
308
+ const unlisted = dr !== undefined ? dr.waysOutUnlisted : cls === undefined && fatalUnlisted;
260
309
  return deepFreeze({
261
310
  ...(code !== undefined ? { code } : {}),
262
311
  ...(hint !== undefined ? { hint } : {}),
@@ -269,9 +318,11 @@ export function engineBootRefusalOf(lines, scrub) {
269
318
  ...(dr.highWater !== undefined ? { highWater: dr.highWater } : {}),
270
319
  ...(dr.engineVersion !== undefined ? { engineVersion: dr.engineVersion } : {}),
271
320
  ...(dr.markerPath !== undefined ? { markerPath: dr.markerPath } : {}),
272
- waysOut: ['newer_build', 'separate_data_root', 'restore_backup', 'repair_access'],
321
+ ...(dr.migrationRelease !== undefined ? { migrationRelease: dr.migrationRelease } : {}),
322
+ ...(dr.waysOut !== undefined ? { waysOut: [...dr.waysOut] } : {}),
273
323
  }
274
324
  : {}),
325
+ ...(unlisted ? { waysOutUnrecognized: true } : {}),
275
326
  });
276
327
  }
277
328
  catch {
@@ -301,16 +352,21 @@ function formOf(v) {
301
352
  const dataDir = own(v, 'dataDir');
302
353
  return { kind: 'embedded', install: install, ...(typeof dataDir === 'string' && dataDir.trim() !== '' ? { dataDir: flat(dataDir) } : {}) };
303
354
  }
304
- function dataRootWaysOut(form, highWater, retry) {
355
+ function dataRootWaysOut(form, highWater, retry, migration) {
305
356
  const again = againOf(retry, form);
357
+ const releaseClause = () => migration === undefined ? undefined : migration.release !== undefined ? msg('engine.bootRefusal.release.atLeast', { version: migration.release }) : msg('engine.bootRefusal.release.any');
306
358
  if (form.kind === 'deployment') {
307
359
  const who = highWater !== undefined ? msg('engine.bootRefusal.who.buildAtLeast', { version: highWater }) : msg('engine.bootRefusal.who.build');
308
- return msg('engine.bootRefusal.waysOut', {
360
+ const dep = {
309
361
  newer: msg('engine.bootRefusal.newer.deployment', { who }),
310
362
  separate: msg('engine.bootRefusal.separate.deployment'),
311
363
  restore: msg('engine.bootRefusal.restoreDeployment'),
312
364
  repair: msg('engine.bootRefusal.repairDeployment'),
313
- });
365
+ };
366
+ const release = releaseClause();
367
+ return release === undefined
368
+ ? msg('engine.bootRefusal.waysOut', dep)
369
+ : msg('engine.bootRefusal.waysOutFour', { ...dep, migration: msg('engine.bootRefusal.migrationDeployment', { release }) });
314
370
  }
315
371
  const app = form.install === 'app';
316
372
  const who = app
@@ -326,12 +382,16 @@ function dataRootWaysOut(form, highWater, retry) {
326
382
  ? 'engine.bootRefusal.newer.app'
327
383
  : 'engine.bootRefusal.newer.unknown';
328
384
  const key = configDirKey() ?? '';
329
- return msg('engine.bootRefusal.waysOut', {
385
+ const emb = {
330
386
  newer: msg(newerKey, { who, again }),
331
387
  separate: msg(app ? 'engine.bootRefusal.separate.app' : 'engine.bootRefusal.separate.embedded', { key }),
332
388
  restore: msg('engine.bootRefusal.restore', { folder: form.dataDir ?? msg('engine.bootRefusal.folder.default'), again }),
333
389
  repair: msg('engine.bootRefusal.repair', { again }),
334
- });
390
+ };
391
+ const release = releaseClause();
392
+ return release === undefined
393
+ ? msg('engine.bootRefusal.waysOut', emb)
394
+ : msg('engine.bootRefusal.waysOutFour', { ...emb, migration: msg('engine.bootRefusal.migration', { release, again }) });
335
395
  }
336
396
  function armFix(reason, retry, facts, form) {
337
397
  const again = againOf(retry, form);
@@ -359,6 +419,21 @@ function armFix(reason, retry, facts, form) {
359
419
  return msg('engine.unavailable.fix.diagnose', { retry: msg(retry === 'command' ? 'engine.unavailable.retry.command' : 'engine.unavailable.retry.session') });
360
420
  }
361
421
  }
422
+ function hasMigrationWayOut(v) {
423
+ try {
424
+ if (!Array.isArray(v))
425
+ return false;
426
+ const n = readOwnDataValue(v, 'length');
427
+ for (let i = 0; typeof n === 'number' && i < n && i < 16; i++) {
428
+ if (readOwnDataValue(v, String(i)) === 'start_migration_release')
429
+ return true;
430
+ }
431
+ return false;
432
+ }
433
+ catch {
434
+ return false;
435
+ }
436
+ }
362
437
  function refusalView(r) {
363
438
  const text = own(r, 'text');
364
439
  const code = own(r, 'code');
@@ -382,6 +457,7 @@ function refusalView(r) {
382
457
  findings = undefined;
383
458
  }
384
459
  const hw = own(r, 'highWater');
460
+ const mr = own(r, 'migrationRelease');
385
461
  const validReason = typeof reason === 'string' && Object.values(ENGINE_BOOT_REFUSAL_CODES).includes(reason) ? reason : undefined;
386
462
  return {
387
463
  ...(typeof text === 'string' && text.trim() !== '' ? { text } : {}),
@@ -389,8 +465,14 @@ function refusalView(r) {
389
465
  ...(validReason !== undefined ? { reason: validReason } : {}),
390
466
  ...(findings !== undefined ? { findings } : {}),
391
467
  ...(typeof hw === 'string' && isRelease(hw) ? { highWater: hw } : {}),
468
+ ...(hasMigrationWayOut(own(r, 'waysOut')) ? { fourWays: true } : {}),
469
+ ...(typeof mr === 'string' && isRelease(mr) ? { migrationRelease: mr } : {}),
470
+ ...(own(r, 'waysOutUnrecognized') === true ? { unrecognized: true } : {}),
392
471
  };
393
472
  }
473
+ function migrationWayOf(r) {
474
+ return r.fourWays === true ? (r.migrationRelease !== undefined ? { release: r.migrationRelease } : {}) : undefined;
475
+ }
394
476
  export function engineBootRefusalSentence(refusal, lang, form) {
395
477
  try {
396
478
  const r = refusalView(refusal);
@@ -398,8 +480,8 @@ export function engineBootRefusalSentence(refusal, lang, form) {
398
480
  return undefined;
399
481
  const f = formOf(form);
400
482
  if (f !== undefined && r.reason !== undefined) {
401
- if (r.reason === 'data_root_refused' && r.findings !== undefined) {
402
- return renderMessage(msg('engine.bootRefusal.withWaysOut', { reason: flat(r.findings.join(' ')), waysOut: dataRootWaysOut(f, r.highWater, 'session') }), lang);
483
+ if (r.reason === 'data_root_refused' && r.findings !== undefined && r.unrecognized !== true) {
484
+ return renderMessage(msg('engine.bootRefusal.withWaysOut', { reason: flat(r.findings.join(' ')), waysOut: dataRootWaysOut(f, r.highWater, 'session', migrationWayOf(r)) }), lang);
403
485
  }
404
486
  const fix = r.reason !== 'data_root_refused' ? armFix(r.reason, 'session', r.code !== undefined ? { code: r.code } : {}, f) : undefined;
405
487
  if (fix !== undefined)
@@ -440,9 +522,9 @@ export function engineUnavailableLine(facts, lane, lang) {
440
522
  let detail;
441
523
  let fix;
442
524
  if (refusal.reason !== undefined && refusal.text !== undefined) {
443
- if (refusal.reason === 'data_root_refused' && form !== undefined && refusal.findings !== undefined) {
525
+ if (refusal.reason === 'data_root_refused' && form !== undefined && refusal.findings !== undefined && refusal.unrecognized !== true) {
444
526
  detail = flat(refusal.findings.join(' '));
445
- fix = dataRootWaysOut(form, refusal.highWater, retry);
527
+ fix = dataRootWaysOut(form, refusal.highWater, retry, migrationWayOf(refusal));
446
528
  }
447
529
  else if (refusal.reason === 'data_root_refused') {
448
530
  detail = flat(refusal.text);
@@ -1,6 +1,7 @@
1
- import type { FleetFrame, FleetTaskRow, FleetWorkflowRow } from '@sema-agent/sdk';
2
- import { type FleetTaskView, type FleetWorkflowView, type ProjectTasksOptions } from './fleetProjection.js';
1
+ import type { AgentEvent, FleetFrame, FleetTaskRow, FleetWorkflowRow } from '@sema-agent/sdk';
2
+ import { type FleetLedgerTaskView, type FleetWorkflowView, type ProjectTasksOptions } from './fleetProjection.js';
3
3
  export declare const TERMINAL_RETAIN_MS = 60000;
4
+ export declare const RESUMABLE_IDLE_FALLBACK_MS = 360000;
4
5
  export interface EngineActiveBgTask {
5
6
  id: string;
6
7
  name?: string;
@@ -21,6 +22,11 @@ export declare function clearAllRetainedFleetRows(): void;
21
22
  export declare function clearAllRetainedFleetRowsFor(sessionKey: string): void;
22
23
  export declare function forgetSettledTerminalRow(id: string): boolean;
23
24
  export declare function forgetSettledTerminalRowFor(sessionKey: string, id: string): boolean;
25
+ export type FleetTaskNotificationWire = Pick<Extract<AgentEvent, {
26
+ type: 'task_notification';
27
+ }>, 'task_id' | 'task_type' | 'status' | 'seq' | 'resumable'>;
28
+ export declare function noteFleetTaskNotification(n: FleetTaskNotificationWire): void;
29
+ export declare function noteFleetTaskNotificationFor(sessionKey: string, n: FleetTaskNotificationWire): void;
24
30
  export declare function __resetFleetLedgerRegistryForTests(): void;
25
31
  export declare function fleetStreamOptions(): {
26
32
  session?: string;
@@ -95,7 +101,7 @@ export interface FleetLedger {
95
101
  project(nowMs: number, opts: ProjectTasksOptions & {
96
102
  viewingTaskId?: string | undefined;
97
103
  }): {
98
- tasks: FleetTaskView[];
104
+ tasks: FleetLedgerTaskView[];
99
105
  workflows: FleetWorkflowView[];
100
106
  };
101
107
  status(): FleetLedgerStatus;
@@ -8,12 +8,13 @@ import { rowIdTail } from '../workflow.js';
8
8
  import { recordEngineTranscriptId } from '../subagent/engineDelegatedPrompt.js';
9
9
  import { recordFleetRowAgentType } from './fleetRowAgentType.js';
10
10
  import { DEFAULT_SESSION_KEY } from '../sessionSlot.js';
11
- import { isSdkFleetTaskTerminal, isSdkFleetTaskTerminalNotSuccess } from '../terminalStatuses.js';
12
- import { projectTasks, projectWorkflows, wireCycleSeq, wireParentId, } from './fleetProjection.js';
11
+ import { isCoreTaskNotificationTerminal, isSdkFleetTaskTerminal, isSdkFleetTaskTerminalNotSuccess } from '../terminalStatuses.js';
12
+ import { projectTasks, projectWorkflows, wireCycleSeq, wireParentId, wireResumable, } from './fleetProjection.js';
13
13
  import { isInStreamDrainingEnd } from '../wireErrorTriage.js';
14
14
  import { cycleSeqOrder, cycleSeqValue } from '../cycleOrder.js';
15
15
  import { guardedRead, ownSnapshotRecord, readOwnDataValue } from '../ownKey.js';
16
16
  export const TERMINAL_RETAIN_MS = 60_000;
17
+ export const RESUMABLE_IDLE_FALLBACK_MS = 360_000;
17
18
  function hasRowId(row) {
18
19
  const id = row?.id;
19
20
  return typeof id === 'string' && id.length > 0;
@@ -77,6 +78,36 @@ export function forgetSettledTerminalRowFor(sessionKey, id) {
77
78
  forgotten = true;
78
79
  return forgotten;
79
80
  }
81
+ function turnTaskEndFactOf(raw) {
82
+ const at = (key) => guardedRead(() => (Array.isArray(raw) ? undefined : readOwnDataValue(raw, key)));
83
+ const taskType = at('task_type');
84
+ if (typeof taskType !== 'string' || taskType === 'external')
85
+ return undefined;
86
+ const status = at('status');
87
+ if (!isCoreTaskNotificationTerminal(status))
88
+ return undefined;
89
+ const id = at('task_id');
90
+ if (typeof id !== 'string' || id.length === 0)
91
+ return undefined;
92
+ const cycle = cycleSeqValue(at('seq'));
93
+ if (cycle === undefined)
94
+ return undefined;
95
+ const tail = rowIdTail(id);
96
+ if (tail.length === 0)
97
+ return undefined;
98
+ const resumable = at('resumable');
99
+ return { tail, cycle, completed: status === 'completed', resumable: typeof resumable === 'boolean' ? resumable : undefined };
100
+ }
101
+ export function noteFleetTaskNotification(n) {
102
+ noteFleetTaskNotificationFor(DEFAULT_SESSION_KEY, n);
103
+ }
104
+ export function noteFleetTaskNotificationFor(sessionKey, n) {
105
+ const fact = turnTaskEndFactOf(n);
106
+ if (fact === undefined)
107
+ return;
108
+ for (const view of viewsFor(sessionKey))
109
+ view.noteTaskNotification(fact);
110
+ }
80
111
  export function __resetFleetLedgerRegistryForTests() {
81
112
  liveBgViews.clear();
82
113
  }
@@ -118,7 +149,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
118
149
  break;
119
150
  }
120
151
  }
121
- const rec = prior ?? { gen: undefined, settled: undefined };
152
+ const rec = prior ?? { gen: undefined, settled: undefined, ended: undefined };
122
153
  tails.set(tail, rec);
123
154
  return rec;
124
155
  }
@@ -136,13 +167,46 @@ export function createFleetLedger(hooks = {}, opts = {}) {
136
167
  function noteSettledTerminal(tail, row) {
137
168
  if (tail.length === 0 || typeof row.status !== 'string')
138
169
  return;
139
- touchTail(tail).settled = { status: row.status, cycleSeq: wireCycleSeq(row) };
170
+ touchTail(tail).settled = wireCycleSeq(row) === undefined ? { status: row.status } : undefined;
140
171
  }
141
172
  function withdrawSettledTerminal(tail) {
142
173
  const rec = tails.get(tail);
143
174
  if (rec !== undefined)
144
175
  rec.settled = undefined;
145
176
  }
177
+ function mergeEndFact(tail, cycle, completed, resumable, atMs) {
178
+ if (cycle === undefined || tail.length === 0 || isFormerGeneration(tail, cycle))
179
+ return;
180
+ const rec = touchTail(tail);
181
+ const prior = rec.ended;
182
+ const order = prior === undefined ? 'before' : cycleSeqOrder(prior.cycle, cycle);
183
+ if (prior === undefined || order === 'before') {
184
+ rec.ended = { cycle, atMs, completed, resumable, voided: false };
185
+ return;
186
+ }
187
+ if (order !== 'same')
188
+ return;
189
+ prior.completed = prior.completed && completed;
190
+ prior.resumable = prior.resumable === false || resumable === false ? false : prior.resumable === true || resumable === true ? true : undefined;
191
+ }
192
+ function voidListedEnds() {
193
+ retained.clear();
194
+ for (const rec of tails.values())
195
+ if (rec.ended !== undefined)
196
+ rec.ended.voided = true;
197
+ }
198
+ function terminalEvidenceOf(tail) {
199
+ const rec = tails.get(tail);
200
+ if (rec === undefined)
201
+ return [];
202
+ const out = [];
203
+ const e = rec.ended;
204
+ if (e !== undefined && cycleSeqOrder(e.cycle, rec.gen) !== 'before')
205
+ out.push({ notSuccess: !e.completed, cycleSeq: e.cycle });
206
+ if (rec.settled !== undefined)
207
+ out.push({ notSuccess: isSdkFleetTaskTerminalNotSuccess(rec.settled.status), cycleSeq: undefined });
208
+ return out;
209
+ }
146
210
  let droppedMalformed = 0;
147
211
  let droppedUnknownFrame = 0;
148
212
  let droppedForeignBgNotification = 0;
@@ -178,13 +242,15 @@ export function createFleetLedger(hooks = {}, opts = {}) {
178
242
  function settleTerminalRow(row, tail, receivedAtMs) {
179
243
  taskMap.delete(row.id);
180
244
  rowMeta.delete(row.id);
245
+ const cycle = wireCycleSeq(row);
246
+ mergeEndFact(tail, cycle, row.status === 'completed', wireResumable(row), receivedAtMs);
181
247
  for (const id of taskMap.keys()) {
182
248
  if (rowIdTail(id) === tail) {
183
249
  debug(`[fleet-frame] terminal row not retained: tail still in flight on an alias(row=${row.id} alias=${id})`);
184
250
  return;
185
251
  }
186
252
  }
187
- retained.set(row.id, { row, expireAt: receivedAtMs + TERMINAL_RETAIN_MS });
253
+ retained.set(row.id, { row, expireAt: receivedAtMs + TERMINAL_RETAIN_MS, ...(cycle !== undefined ? { endCycle: cycle } : {}) });
188
254
  for (const id of [...retained.keys()]) {
189
255
  if (id !== row.id && rowIdTail(id) === tail)
190
256
  retained.delete(id);
@@ -246,9 +312,15 @@ export function createFleetLedger(hooks = {}, opts = {}) {
246
312
  },
247
313
  sessionScoped: () => sessionScopedMeta,
248
314
  clearRetained: () => {
249
- retained.clear();
315
+ voidListedEnds();
250
316
  },
251
317
  forgetSettledTerminal: id => forgetSettledTerminalRowOf(id),
318
+ noteTaskNotification: fact => {
319
+ if (disposed || isFormerGeneration(fact.tail, fact.cycle))
320
+ return;
321
+ raiseTailGen(fact.tail, fact.cycle);
322
+ mergeEndFact(fact.tail, fact.cycle, fact.completed, fact.resumable, nowFn());
323
+ },
252
324
  };
253
325
  {
254
326
  let views = liveBgViews.get(sessionKey);
@@ -461,6 +533,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
461
533
  debug(`[fleet-frame] bg_notification stale-cycle ignored(tail-cycle=${tails.get(notifTail)?.gen} notif-seq=${notifSeq})`);
462
534
  }
463
535
  if (!staleBlocked) {
536
+ mergeEndFact(notifTail, notifSeq, n.status === 'completed', typeof n.resumable === 'boolean' ? n.resumable : undefined, nowMs);
464
537
  for (const id of rowIds) {
465
538
  const known = taskMap.get(id);
466
539
  if (!known)
@@ -482,6 +555,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
482
555
  },
483
556
  expireAt: nowMs + TERMINAL_RETAIN_MS,
484
557
  retiredByNotification: true,
558
+ ...(notifSeq !== undefined ? { endCycle: notifSeq } : {}),
485
559
  });
486
560
  writtenIds.add(id);
487
561
  applied = true;
@@ -494,6 +568,8 @@ export function createFleetLedger(hooks = {}, opts = {}) {
494
568
  const prior = entry.row.status ?? '';
495
569
  if (prior === n.status) {
496
570
  applied = true;
571
+ if (notifSeq !== undefined && cycleSeqOrder(entry.endCycle, notifSeq) !== 'same')
572
+ retained.set(id, { ...entry, endCycle: notifSeq });
497
573
  continue;
498
574
  }
499
575
  if (isWashGreen(prior, n.status)) {
@@ -505,6 +581,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
505
581
  row: { ...entry.row, status: coerced },
506
582
  expireAt: nowMs + TERMINAL_RETAIN_MS,
507
583
  ...(entry.retiredByNotification === true ? { retiredByNotification: true } : {}),
584
+ ...(notifSeq !== undefined ? { endCycle: notifSeq } : {}),
508
585
  });
509
586
  applied = true;
510
587
  }
@@ -513,15 +590,18 @@ export function createFleetLedger(hooks = {}, opts = {}) {
513
590
  (applied ||
514
591
  (!washBlocked &&
515
592
  (() => {
516
- for (const prior of [getBgTerminalFacts(n.taskId), tails.get(notifTail)?.settled]) {
517
- if (prior === undefined)
518
- continue;
593
+ const facts = getBgTerminalFacts(n.taskId);
594
+ const priors = [
595
+ ...(facts !== undefined ? [{ notSuccess: isSdkFleetTaskTerminalNotSuccess(facts.status), cycleSeq: facts.cycleSeq }] : []),
596
+ ...terminalEvidenceOf(notifTail),
597
+ ];
598
+ for (const prior of priors) {
519
599
  const order = cycleSeqOrder(notifSeq, prior.cycleSeq);
520
600
  if (order === 'before')
521
601
  return false;
522
602
  if (order === 'after')
523
603
  continue;
524
- if (isWashGreen(prior.status, n.status))
604
+ if (n.status === 'completed' && prior.notSuccess)
525
605
  return false;
526
606
  }
527
607
  return true;
@@ -559,6 +639,17 @@ export function createFleetLedger(hooks = {}, opts = {}) {
559
639
  ...(typeof n.completionId === 'string' && n.completionId.length > 0 ? { completionId: n.completionId } : {}),
560
640
  });
561
641
  }
642
+ function listsAsIdle(id, entry, nowMs) {
643
+ const rec = tails.get(rowIdTail(id));
644
+ const e = rec?.ended;
645
+ return (rec !== undefined &&
646
+ e !== undefined &&
647
+ !e.voided &&
648
+ cycleSeqOrder(entry.endCycle, e.cycle) === 'same' &&
649
+ e.completed &&
650
+ e.resumable === true &&
651
+ nowMs < e.atMs + RESUMABLE_IDLE_FALLBACK_MS);
652
+ }
562
653
  function project(nowMs, opts) {
563
654
  const viewingTaskId = opts.viewingTaskId;
564
655
  const liveRows = [...taskMap.values()].map(r => {
@@ -575,14 +666,18 @@ export function createFleetLedger(hooks = {}, opts = {}) {
575
666
  return idle > 0 ? { ...r, elapsedMs: r.elapsedMs + idle } : r;
576
667
  });
577
668
  const retainedRows = [];
669
+ const idleIds = new Set();
578
670
  for (const [id, r] of retained) {
579
671
  if (taskMap.has(id))
580
672
  continue;
581
673
  const isViewed = viewingTaskId !== undefined && rowIdTail(id) === viewingTaskId;
582
- if (nowMs > r.expireAt && !isViewed) {
674
+ const isIdle = listsAsIdle(id, r, nowMs);
675
+ if (nowMs > r.expireAt && !isViewed && !isIdle) {
583
676
  retained.delete(id);
584
677
  continue;
585
678
  }
679
+ if (isIdle)
680
+ idleIds.add(id);
586
681
  retainedRows.push(r.row);
587
682
  }
588
683
  const allRowsById = new Map();
@@ -605,11 +700,12 @@ export function createFleetLedger(hooks = {}, opts = {}) {
605
700
  return false;
606
701
  };
607
702
  const ownWorkflowRow = (r) => sessionScopedMeta === true || isOwnWorkflowRun(r.id);
703
+ const tasks = projectTasks([...liveRows, ...retainedRows].filter(ownTaskRow), {
704
+ rowFilter: opts.rowFilter,
705
+ ...(viewingTaskId !== undefined ? { viewingTaskId } : {}),
706
+ });
608
707
  return {
609
- tasks: projectTasks([...liveRows, ...retainedRows].filter(ownTaskRow), {
610
- rowFilter: opts.rowFilter,
611
- ...(viewingTaskId !== undefined ? { viewingTaskId } : {}),
612
- }),
708
+ tasks: idleIds.size === 0 ? tasks : tasks.map(t => (idleIds.has(t.id) ? { ...t, idleUntil: null } : t)),
613
709
  workflows: projectWorkflows([...wfMap.values()].filter(ownWorkflowRow)),
614
710
  };
615
711
  }
@@ -657,7 +753,7 @@ export function createFleetLedger(hooks = {}, opts = {}) {
657
753
  taskMap.clear();
658
754
  wfMap.clear();
659
755
  rowMeta.clear();
660
- retained.clear();
756
+ voidListedEnds();
661
757
  for (const rec of tails.values())
662
758
  rec.settled = undefined;
663
759
  contentAnchorSet = false;
@@ -32,6 +32,9 @@ export interface FleetTaskView {
32
32
  retiredBy?: string;
33
33
  workspaceSnapshot?: 'failed';
34
34
  }
35
+ export type FleetLedgerTaskView = FleetTaskView & {
36
+ readonly idleUntil?: null;
37
+ };
35
38
  export interface FleetRowUsage {
36
39
  totalTokens?: number;
37
40
  toolUses?: number;
@@ -53,6 +56,7 @@ export interface FleetWorkflowView {
53
56
  startedCount?: number;
54
57
  }
55
58
  export declare function coerceTaskStatus(s: string): FleetTaskStatus;
59
+ export declare function fleetTaskShownStatus(view: FleetLedgerTaskView): FleetTaskStatus;
56
60
  export declare function coerceWorkflowStatus(s: string | undefined): FleetTaskStatus;
57
61
  export declare function workflowRunDisplayStatusOf(run: object | null | undefined): FleetTaskStatus;
58
62
  export declare const CONTROL_TOOL_VERBS: ReadonlySet<string>;
@@ -68,6 +72,7 @@ export interface ProjectTasksOptions {
68
72
  export declare function projectTasks(allRows: readonly FleetTaskRow[], opts: ProjectTasksOptions): FleetTaskView[];
69
73
  export declare function projectWorkflows(rows: readonly FleetWorkflowRow[]): FleetWorkflowView[];
70
74
  export declare const FLEET_TASK_VIEW_KEYS: readonly string[];
75
+ export declare const FLEET_LEDGER_TASK_VIEW_EXTRA_KEYS: readonly string[];
71
76
  export declare const FLEET_WORKFLOW_VIEW_KEYS: readonly string[];
72
77
  export declare const FLEET_TASK_ROW_WIRE_KEYS: readonly string[];
73
78
  export declare const FLEET_WORKFLOW_ROW_WIRE_KEYS: readonly string[];
@@ -28,6 +28,12 @@ export function coerceTaskStatus(s) {
28
28
  }
29
29
  }
30
30
  }
31
+ export function fleetTaskShownStatus(view) {
32
+ if (guardedRead(() => readOwnDataValue(view, 'idleUntil')) === null)
33
+ return 'idle';
34
+ const status = guardedRead(() => readOwnDataValue(view, 'status'));
35
+ return typeof status === 'string' ? coerceTaskStatus(status) : 'idle';
36
+ }
31
37
  export function coerceWorkflowStatus(s) {
32
38
  return workflowRunDisplayStatusOf({ status: s });
33
39
  }
@@ -290,6 +296,8 @@ const FLEET_TASK_VIEW_KEY_TUPLE = [
290
296
  'workspaceSnapshot',
291
297
  ];
292
298
  export const FLEET_TASK_VIEW_KEYS = FLEET_TASK_VIEW_KEY_TUPLE;
299
+ const FLEET_LEDGER_TASK_VIEW_EXTRA_KEY_TUPLE = ['idleUntil'];
300
+ export const FLEET_LEDGER_TASK_VIEW_EXTRA_KEYS = FLEET_LEDGER_TASK_VIEW_EXTRA_KEY_TUPLE;
293
301
  const FLEET_WORKFLOW_VIEW_KEY_TUPLE = [
294
302
  'id',
295
303
  'name',
@@ -393,5 +401,5 @@ export const FLEET_WORKFLOW_ROW_KEYS_NOT_PROJECTED = {
393
401
  reason: '折进 status:恒字面 true 的「有 agent 停在审批门上」位 ⇒ 显示状态 awaiting approval(workflowRunDisplayStatusOf 唯一读处);缺席不读作「没停」以外的任何事。',
394
402
  },
395
403
  };
396
- const _fleetViewKeyPins = [true, true, true, true, true, true, true, true];
404
+ const _fleetViewKeyPins = [true, true, true, true, true, true, true, true, true, true];
397
405
  void _fleetViewKeyPins;
@@ -11,6 +11,13 @@ export declare function isGateDeniedByWord(v: unknown): v is GateDeniedByWord;
11
11
  export declare function gateDeniedByDetail(deniedBy: unknown, lang?: UiLanguage): string;
12
12
  export declare function askOriginDetail(origin: unknown, lang?: UiLanguage): string;
13
13
  export declare function askOriginNoteOf(origin: string | undefined, lang?: UiLanguage): string | undefined;
14
+ export declare const APPROVAL_CARD_HEADLINE_SOURCES: readonly ["origin"];
15
+ export type ApprovalCardHeadlineSource = (typeof APPROVAL_CARD_HEADLINE_SOURCES)[number];
16
+ export interface ApprovalCardHeadline {
17
+ readonly source: ApprovalCardHeadlineSource;
18
+ readonly text: string;
19
+ }
20
+ export declare function approvalCardHeadlineOf(card: object | null | undefined, lang?: UiLanguage): Readonly<ApprovalCardHeadline> | undefined;
14
21
  export declare function ruleOffersAbsenceDetail(reason: string | undefined, opts?: {
15
22
  readonly deploymentListSessionRow?: boolean;
16
23
  }, lang?: UiLanguage): string | undefined;