@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
@@ -2,7 +2,7 @@ import { requestCoordinateText } from './requestCoordinate.js';
2
2
  import { displayUntrusted } from './displayUntrusted.js';
3
3
  import { msg, renderMessage } from './i18n/translate.js';
4
4
  import { appendOwnElement } from './ownKey.js';
5
- import { SETTINGS_CLASSIFY_HOOKS_CONFIG, } from './generated/settingsSchemaFns.js';
5
+ import { SETTINGS_CLASSIFY_HOOKS_CONFIG, SETTINGS_HOOK_EVENTS, } from './generated/settingsSchemaFns.js';
6
6
  const _tuplePins = [true, true, true, true, true, true];
7
7
  void _tuplePins;
8
8
  const _classifierPins = [true, true, true, true, true, true];
@@ -113,13 +113,13 @@ export function salvageSettingsHooksEntries(doc) {
113
113
  });
114
114
  }
115
115
  const READER_GROUP_WIRE_KEYS = ['source'];
116
- const READER_COMMAND_WIRE_KEYS = ['plugin', 'args'];
117
- function unreadOf(raw, carried, wireKeys, notRead) {
116
+ const READER_COMMAND_WIRE_KEYS = ['plugin'];
117
+ function unreadOf(raw, carried, wireKeys, reads) {
118
118
  const out = [];
119
119
  for (const k of Object.keys(raw)) {
120
120
  if (own(raw, k) === undefined || wireKeys.includes(k))
121
121
  continue;
122
- if (Object.hasOwn(carried, k) && !notRead.includes(k))
122
+ if (reads(k, Object.hasOwn(carried, k)))
123
123
  continue;
124
124
  appendOwnElement(out, k);
125
125
  }
@@ -136,7 +136,7 @@ function carriedGroupOf(event, group) {
136
136
  export function hooksGroupUnreadKeys(event, group) {
137
137
  const raw = group;
138
138
  const carried = carriedGroupOf(event, { ...raw, hooks: [] });
139
- return carried === undefined ? undefined : unreadOf(raw, carried, READER_GROUP_WIRE_KEYS, []);
139
+ return carried === undefined ? undefined : unreadOf(raw, carried, READER_GROUP_WIRE_KEYS, (_key, carriedByCopy) => carriedByCopy);
140
140
  }
141
141
  export function hooksGroupAuthoredSource(group) {
142
142
  try {
@@ -153,14 +153,27 @@ export function hooksGroupAuthoredSource(group) {
153
153
  return 'unreadable';
154
154
  }
155
155
  }
156
- export function hooksEntryUnreadKeys(event, entry, notRead) {
157
- const raw = entry;
158
- const g = carriedGroupOf(event, { hooks: [raw] });
156
+ export function hooksEntryContractView(event, entry) {
157
+ const g = carriedGroupOf(event, { hooks: [entry] });
159
158
  const entries = g === undefined ? undefined : own(g, 'hooks');
160
159
  const carried = Array.isArray(entries) ? entries[0] : undefined;
160
+ return isRecord(carried) ? carried : undefined;
161
+ }
162
+ export function hooksEntryUnreadKeys(event, entry, reader) {
163
+ const raw = entry;
164
+ const carried = hooksEntryContractView(event, raw);
161
165
  if (!isRecord(carried))
162
166
  return undefined;
163
- return unreadOf(raw, carried, own(carried, 'type') === 'command' ? READER_COMMAND_WIRE_KEYS : [], notRead);
167
+ const type = own(carried, 'type');
168
+ const entryType = typeof type === 'string' ? type : '';
169
+ return unreadOf(raw, carried, entryType === 'command' ? READER_COMMAND_WIRE_KEYS : [], (key, carriedByCopy) => reader.reads(entryType, key, carriedByCopy));
170
+ }
171
+ const GUARD_HOOK_EVENTS = new Set(SETTINGS_HOOK_EVENTS.filter((event) => {
172
+ const r = SETTINGS_CLASSIFY_HOOKS_CONFIG({ hooks: { [event]: 1 } });
173
+ return r.outcome === 'rejected' && r.rejections.some((x) => x.code === 'guard-event-not-array');
174
+ }));
175
+ export function isGuardHookEvent(event) {
176
+ return typeof event === 'string' && GUARD_HOOK_EVENTS.has(event);
164
177
  }
165
178
  export function hooksKeyCoordinateText(path) {
166
179
  return requestCoordinateText('settings', path, (seg) => labelOf(seg));
@@ -47,6 +47,7 @@ export interface SkillOverrideLock {
47
47
  readonly source: SkillOverrideLockSource;
48
48
  readonly value: SkillOverrideState;
49
49
  }
50
+ export declare const LIST_BUDGET = 4096;
50
51
  export interface ShapedSkillSubject {
51
52
  readonly name: string;
52
53
  readonly origin: SkillOrigin;
@@ -75,6 +76,8 @@ export declare function secondaryNamesOf(s: ShapedSkillSubject): string[];
75
76
  export declare function effectiveOfShaped(L: ShapedSkillLayers, s: ShapedSkillSubject): SkillOverrideEffective;
76
77
  export declare function lockOfShaped(L: ShapedSkillLayers, s: ShapedSkillSubject): SkillOverrideLock | undefined;
77
78
  export declare function skillOverrideEffectiveOf(layers: SkillOverrideLayers, skill: SkillOverrideSubject): SkillOverrideEffective | undefined;
79
+ export type SkillOverrideTightening = 'none' | 'plain' | 'managed';
80
+ export declare function skillOverrideTighteningOf(layers: SkillOverrideLayers): SkillOverrideTightening;
78
81
  export declare function skillOverrideLockOf(layers: SkillOverrideLayers, skill: SkillOverrideSubject): SkillOverrideLock | undefined;
79
82
  export declare function ownValueIn(ov: object | undefined, s: ShapedSkillSubject): SkillOverrideState | undefined;
80
83
  export declare function stateAt(ov: object | undefined, key: string | undefined): SkillOverrideState | undefined;
@@ -7,9 +7,8 @@ export const SKILL_OVERRIDE_DECIDERS = Object.freeze([...SKILL_OVERRIDE_LAYERS,
7
7
  export const SKILL_OVERRIDE_LOCK_SOURCES = Object.freeze(['policy', 'flag', 'plugin', 'skill-author', 'reserved-name']);
8
8
  const MANAGED = ['policySettings', 'flagSettings'];
9
9
  const PLAIN = ['localSettings', 'projectSettings', 'userSettings'];
10
- const MERGED = ['policySettings', 'flagSettings', 'localSettings', 'projectSettings', 'userSettings'];
11
10
  const RANK = { on: 0, 'name-only': 1, 'user-invocable-only': 2, off: 3 };
12
- const LIST_BUDGET = 4096;
11
+ export const LIST_BUDGET = 4096;
13
12
  function own(v, key) {
14
13
  if (typeof v !== 'object' || v === null)
15
14
  return undefined;
@@ -91,7 +90,7 @@ export function shapeSkillLayers(v) {
91
90
  const session = own(v, 'session');
92
91
  const enabled = (layer) => layer === 'policySettings' || layer === 'flagSettings' || !disabled.has(layer);
93
92
  let fromSettings = false;
94
- for (const l of MERGED) {
93
+ for (const l of SKILL_OVERRIDE_LAYERS) {
95
94
  if (!enabled(l))
96
95
  continue;
97
96
  const b = own(own(v, l), 'disableBundledSkills');
@@ -151,7 +150,7 @@ function reservedTighten(ov, s, by) {
151
150
  return r === undefined || r.value === 'on' ? undefined : r;
152
151
  }
153
152
  function mergedAt(L, key) {
154
- for (const l of MERGED) {
153
+ for (const l of SKILL_OVERRIDE_LAYERS) {
155
154
  if (!L.enabled(l))
156
155
  continue;
157
156
  const h = hitAt(L.docs[l], key, l);
@@ -324,6 +323,34 @@ export function skillOverrideEffectiveOf(layers, skill) {
324
323
  return undefined;
325
324
  }
326
325
  }
326
+ function recordTightens(rec) {
327
+ if (rec === undefined)
328
+ return false;
329
+ const keys = Reflect.ownKeys(rec);
330
+ if (keys.length > LIST_BUDGET)
331
+ return true;
332
+ for (const k of keys) {
333
+ if (typeof k !== 'string')
334
+ continue;
335
+ const v = own(rec, k);
336
+ if (isState(v) && v !== 'on')
337
+ return true;
338
+ }
339
+ return false;
340
+ }
341
+ export function skillOverrideTighteningOf(layers) {
342
+ try {
343
+ const L = shapeSkillLayers(layers);
344
+ if (MANAGED.some((l) => recordTightens(L.docs[l])))
345
+ return 'managed';
346
+ if (PLAIN.some((l) => L.enabled(l) && recordTightens(L.docs[l])) || recordTightens(L.session))
347
+ return 'plain';
348
+ return 'none';
349
+ }
350
+ catch {
351
+ return 'managed';
352
+ }
353
+ }
327
354
  export function skillOverrideLockOf(layers, skill) {
328
355
  try {
329
356
  const s = shapeSkillSubject(skill);
@@ -1,6 +1,6 @@
1
1
  import type { SkillSpec } from '@sema-agent/sdk';
2
2
  import { type SkillToolListsReading } from './skillToolListsCapability.js';
3
- import { type SkillOverrideLayers, type SkillOverrideSubject } from './skillOverrideChain.js';
3
+ import { type SkillOrigin, type SkillOverrideLayers, type SkillOverrideSubject } from './skillOverrideChain.js';
4
4
  export interface SkillCmdLike {
5
5
  type?: string;
6
6
  name?: string;
@@ -23,6 +23,17 @@ export interface SkillSpecsOptions {
23
23
  skillToolLists?: SkillToolListsReading;
24
24
  skillOverrides?: SkillOverrideLayers;
25
25
  }
26
+ export type SkillCmdWithSubject = SkillCmdLike & {
27
+ skillOverrideSubject: SkillOverrideSubject;
28
+ };
29
+ export declare const SKILL_WITHHELD_REASONS: readonly ["identity_unreadable"];
30
+ export type SkillWithheldReason = (typeof SKILL_WITHHELD_REASONS)[number];
31
+ export interface SkillWithheldRow {
32
+ readonly name: string;
33
+ readonly origin: SkillOrigin;
34
+ readonly reason: SkillWithheldReason;
35
+ }
36
+ export declare function skillCommandsWithheldOf(cmds: readonly SkillCmdLike[], opts?: SkillSpecsOptions): readonly SkillWithheldRow[];
26
37
  export declare function skillBaseDirOf(skillRoot: string | undefined, platform?: 'posix' | 'win32'): string | undefined;
27
38
  export type SkillToolListKey = 'allowedTools' | 'disallowedTools';
28
39
  export type SkillToolListsVerdict = {
@@ -38,4 +49,7 @@ export type SkillToolListsVerdict = {
38
49
  readonly detail: string;
39
50
  };
40
51
  export declare function skillToolListsVerdict(source: object, reading: SkillToolListsReading | undefined): SkillToolListsVerdict;
41
- export declare function skillCommandsToSpecs(cmds: SkillCmdLike[], resolveBody: (cmd: SkillCmdLike) => string | null, opts?: SkillSpecsOptions): SkillSpecOnWire[];
52
+ export declare function skillCommandsToSpecs(cmds: SkillCmdLike[], resolveBody: (cmd: SkillCmdLike) => string | null, opts?: SkillSpecsOptions & {
53
+ skillOverrides?: undefined;
54
+ }): SkillSpecOnWire[];
55
+ export declare function skillCommandsToSpecs(cmds: SkillCmdWithSubject[], resolveBody: (cmd: SkillCmdWithSubject) => string | null, opts: SkillSpecsOptions): SkillSpecOnWire[];
@@ -1,7 +1,8 @@
1
1
  import { skillToolListsAvailable } from './skillToolListsCapability.js';
2
2
  import { msg, renderMessage } from './i18n/translate.js';
3
3
  import { appendOwnElement } from './ownKey.js';
4
- import { skillOverrideEffectiveOf } from './skillOverrideChain.js';
4
+ import { pathShapeOf, PATH_LENGTH_LIMIT } from './pathShape.js';
5
+ import { skillOverrideEffectiveOf, skillOverrideTighteningOf, } from './skillOverrideChain.js';
5
6
  export function toCappedSpec(name, description, whenToUse, body) {
6
7
  const n = name.trim();
7
8
  const c = body.trim();
@@ -13,29 +14,54 @@ export function toCappedSpec(name, description, whenToUse, body) {
13
14
  return { name: n, description: desc, content: c };
14
15
  }
15
16
  const ORIGIN_OF_SOURCE = { plugin: 'plugin', builtin: 'builtin', bundled: 'bundled', mcp: 'mcp' };
17
+ function originOfCommand(cmd) {
18
+ const src = typeof cmd.source === 'string' && Object.hasOwn(ORIGIN_OF_SOURCE, cmd.source) ? ORIGIN_OF_SOURCE[cmd.source] : undefined;
19
+ return src ?? 'skills';
20
+ }
16
21
  function overrideSubjectOf(cmd, name) {
17
22
  if (cmd.skillOverrideSubject !== undefined)
18
23
  return cmd.skillOverrideSubject;
19
- const src = typeof cmd.source === 'string' && Object.hasOwn(ORIGIN_OF_SOURCE, cmd.source) ? ORIGIN_OF_SOURCE[cmd.source] : undefined;
20
- return { name, origin: src ?? 'skills' };
24
+ return { name, origin: originOfCommand(cmd) };
25
+ }
26
+ export const SKILL_WITHHELD_REASONS = Object.freeze(['identity_unreadable']);
27
+ function identityNeedsSubject(cmd, name) {
28
+ if (name.includes(':'))
29
+ return true;
30
+ const s = cmd.source;
31
+ return s === 'mcp' || s === 'plugin' || s === 'commands';
32
+ }
33
+ function readOverrideIdentity(cmd, name, layers, tightening) {
34
+ if (cmd.skillOverrideSubject === undefined) {
35
+ if (tightening === 'managed' || (tightening === 'plain' && identityNeedsSubject(cmd, name)))
36
+ return { kind: 'withheld', reason: 'identity_unreadable' };
37
+ }
38
+ const effective = skillOverrideEffectiveOf(layers, overrideSubjectOf(cmd, name));
39
+ return effective === undefined ? { kind: 'withheld', reason: 'identity_unreadable' } : { kind: 'read', effective };
40
+ }
41
+ export function skillCommandsWithheldOf(cmds, opts) {
42
+ const rows = [];
43
+ const layers = opts?.skillOverrides;
44
+ if (layers === undefined)
45
+ return Object.freeze(rows);
46
+ const tightening = skillOverrideTighteningOf(layers);
47
+ for (const cmd of cmds) {
48
+ if (cmd.type !== 'prompt' || cmd.disableModelInvocation)
49
+ continue;
50
+ const name = (cmd.name ?? '').trim();
51
+ const read = readOverrideIdentity(cmd, name, layers, tightening);
52
+ if (read.kind === 'withheld')
53
+ appendOwnElement(rows, Object.freeze({ name, origin: originOfCommand(cmd), reason: read.reason }));
54
+ }
55
+ return Object.freeze(rows);
21
56
  }
22
57
  export function skillBaseDirOf(skillRoot, platform = 'posix') {
23
- if (skillRoot === undefined || skillRoot.length === 0 || skillRoot.length > 4096)
24
- return undefined;
25
- if (skillRoot.includes('\u0000'))
26
- return undefined;
27
- if (platform === 'win32') {
28
- if (!(/^[A-Za-z]:[\\/]/.test(skillRoot) || /^\\\\[^\\/]+[\\/][^\\/]+/.test(skillRoot)))
29
- return undefined;
30
- if (skillRoot.split(/[\\/]/).includes('..'))
31
- return undefined;
32
- return skillRoot;
33
- }
34
- if (!skillRoot.startsWith('/'))
58
+ if (skillRoot === undefined || skillRoot.length > PATH_LENGTH_LIMIT)
35
59
  return undefined;
36
- if (skillRoot.split(/[\\/]/).includes('..'))
60
+ const shape = pathShapeOf(skillRoot);
61
+ if (shape === undefined || shape.hasParentSegment)
37
62
  return undefined;
38
- return skillRoot;
63
+ const rooted = platform === 'win32' ? shape.kind === 'drive' || shape.kind === 'unc' : shape.kind === 'posix';
64
+ return rooted ? skillRoot : undefined;
39
65
  }
40
66
  const SKILL_TOOL_LIST_READ_BUDGET = 100_000;
41
67
  function readSkillToolList(key, source) {
@@ -123,6 +149,7 @@ export function skillToolListsVerdict(source, reading) {
123
149
  }
124
150
  export function skillCommandsToSpecs(cmds, resolveBody, opts) {
125
151
  const specs = [];
152
+ const tightening = opts?.skillOverrides !== undefined ? skillOverrideTighteningOf(opts.skillOverrides) : 'none';
126
153
  const seen = new Set();
127
154
  const reading = opts?.skillToolLists;
128
155
  for (const cmd of cmds) {
@@ -130,10 +157,10 @@ export function skillCommandsToSpecs(cmds, resolveBody, opts) {
130
157
  continue;
131
158
  let nameOnly = false;
132
159
  if (opts?.skillOverrides !== undefined) {
133
- const eff = skillOverrideEffectiveOf(opts.skillOverrides, overrideSubjectOf(cmd, (cmd.name ?? '').trim()));
134
- if (eff === undefined || eff.value === 'off' || eff.value === 'user-invocable-only')
160
+ const read = readOverrideIdentity(cmd, (cmd.name ?? '').trim(), opts.skillOverrides, tightening);
161
+ if (read.kind === 'withheld' || read.effective.value === 'off' || read.effective.value === 'user-invocable-only')
135
162
  continue;
136
- nameOnly = eff.value === 'name-only';
163
+ nameOnly = read.effective.value === 'name-only';
137
164
  }
138
165
  const body = resolveBody(cmd);
139
166
  if (!body)
@@ -12,6 +12,16 @@ function own(v, key) {
12
12
  }
13
13
  const finite = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : undefined);
14
14
  const word = (v) => (typeof v === 'string' && v.trim() !== '' ? displayUntrusted(v, { max: 64 }) : undefined);
15
+ function ownSlotAbsent(v, key) {
16
+ if (typeof v !== 'object' || v === null || Array.isArray(v))
17
+ return true;
18
+ try {
19
+ return Object.getOwnPropertyDescriptor(v, key) === undefined;
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
15
25
  function span(ms) {
16
26
  const s = Math.floor(ms / 1000);
17
27
  if (s < 60)
@@ -29,13 +39,27 @@ export function subagentStateDetail(carrier) {
29
39
  const readAt = finite(own(st, 'readAt'));
30
40
  const last = finite(own(st, 'lastActivityAt'));
31
41
  const activity = readAt !== undefined && last !== undefined && last <= readAt ? `last activity ${span(readAt - last)} ago` : 'last activity unknown';
32
- const approval = own(st, 'approval');
33
- const approvalState = word(own(approval, 'state'));
34
- let approvalPart = '';
35
- if (approvalState !== undefined) {
36
- const deadline = finite(own(approval, 'deadline'));
37
- const when = deadline === undefined || readAt === undefined ? '' : deadline > readAt ? ` (deadline in ${span(deadline - readAt)})` : ' (deadline passed)';
38
- approvalPart = ` · approval ${approvalState}${when}`;
42
+ const deadlineClause = (holder) => {
43
+ const deadline = finite(own(holder, 'deadline'));
44
+ return deadline === undefined || readAt === undefined ? '' : deadline > readAt ? ` (deadline in ${span(deadline - readAt)})` : ' (deadline passed)';
45
+ };
46
+ let parkPart = '';
47
+ if (!ownSlotAbsent(st, 'park')) {
48
+ const park = own(st, 'park');
49
+ const parkState = word(own(park, 'state'));
50
+ if (parkState !== undefined) {
51
+ const gate = own(park, 'gate');
52
+ const gateKind = word(own(gate, 'kind'));
53
+ const limitReason = gateKind === 'resource_limit' ? word(own(gate, 'reason')) : undefined;
54
+ const on = gateKind === undefined ? '' : ` on ${gateKind}${limitReason !== undefined ? `: ${limitReason}` : ''}`;
55
+ parkPart = ` · park ${parkState}${on}${deadlineClause(park)}`;
56
+ }
57
+ }
58
+ else {
59
+ const approval = own(st, 'approval');
60
+ const approvalState = word(own(approval, 'state'));
61
+ if (approvalState !== undefined)
62
+ parkPart = ` · approval ${approvalState}${deadlineClause(approval)}`;
39
63
  }
40
- return `Subagent state: ${status} · ${activity}${approvalPart}`;
64
+ return `Subagent state: ${status} · ${activity}${parkPart}`;
41
65
  }
@@ -12,10 +12,16 @@ function usableCompactClient(client) {
12
12
  return clientHasVerbs(client, [['capabilities'], ['runs', 'compact']]);
13
13
  }
14
14
  const COMPACT_PROBE_SETTLE_MS = 5000;
15
- const pendingCompactKeys = new Set();
16
- const pendingCompactSinks = new Map();
15
+ const pendingCompactRequests = new Map();
16
+ function armCompactRequests(sessionKey, requests) {
17
+ const queued = pendingCompactRequests.get(sessionKey);
18
+ if (queued === undefined)
19
+ pendingCompactRequests.set(sessionKey, [...requests]);
20
+ else
21
+ queued.push(...requests);
22
+ }
17
23
  export function isEngineCompactPending(sessionKey = DEFAULT_SESSION_KEY) {
18
- return pendingCompactKeys.has(sessionKey);
24
+ return pendingCompactRequests.has(sessionKey);
19
25
  }
20
26
  function probeManualCompactCapability(wire) {
21
27
  const base = wire.kind === 'injected' ? wire.capsBaseUrl : wire.target.baseUrl;
@@ -32,8 +38,7 @@ function probeManualCompactCapability(wire) {
32
38
  kickEngineCapsProbe(base, () => client.capabilities());
33
39
  }
34
40
  export function __resetEngineCompactArmForTests() {
35
- pendingCompactKeys.clear();
36
- pendingCompactSinks.clear();
41
+ pendingCompactRequests.clear();
37
42
  }
38
43
  function deferFire(ms, fn, why, sessionKey) {
39
44
  const timers = hostTimersFor(sessionKey);
@@ -73,13 +78,23 @@ function emitCompactOutcome(sink, outcome) {
73
78
  function callSinkIsolated(sink, outcome) {
74
79
  invokeHostCallbackIsolated('engineCompactWire: onSettled sink', () => sink(outcome));
75
80
  }
76
- function fanOutCompactSinks(sinks) {
77
- if (sinks === undefined || sinks.length === 0)
78
- return undefined;
79
- return (outcome) => {
80
- for (const s of sinks)
81
- callSinkIsolated(s, outcome);
82
- };
81
+ function settleCompactRequests(requests, outcome) {
82
+ for (const r of requests)
83
+ emitCompactOutcome(r.sink, outcome);
84
+ }
85
+ function settleNotRunning(sessionKey, requests, outcome) {
86
+ const renewed = [];
87
+ for (const r of requests) {
88
+ if (r.continued)
89
+ emitCompactOutcome(r.sink, outcome);
90
+ else
91
+ renewed.push({ sink: r.sink, continued: true });
92
+ }
93
+ if (renewed.length > 0) {
94
+ armCompactRequests(sessionKey, renewed);
95
+ hostLog('debug', `engineCompactWire: 409 compact.not_running —— ${renewed.length} 只请求记回本槽待发(下一条 run 续一次)`);
96
+ }
97
+ surfaceCompactNotRunning(sessionKey, renewed.length > 0 ? 'will_retry' : 'final');
83
98
  }
84
99
  function compactFailureOutcomeOf(e) {
85
100
  const shape = wireFailureShapeOf(e);
@@ -98,10 +113,12 @@ function compactFailureOutcomeOf(e) {
98
113
  return { kind: 'failed', detail: noAnswer, unconfirmed: true };
99
114
  return { kind: 'failed', detail: wireFailureTextsOf(e).reason };
100
115
  }
101
- async function postCompact(shot, sink, retriesLeft = 1) {
116
+ const COMPACT_404_RETRIES = 5;
117
+ const COMPACT_404_FIRST_RETRY_MS = 100;
118
+ async function postCompact(shot, requests, retriesUsed = 0) {
102
119
  const client = shot.client();
103
120
  if (!client) {
104
- emitCompactOutcome(sink, { kind: 'offline' });
121
+ settleCompactRequests(requests, { kind: 'offline' });
105
122
  return;
106
123
  }
107
124
  const taskId = shot.taskId;
@@ -109,7 +126,7 @@ async function postCompact(shot, sink, retriesLeft = 1) {
109
126
  const fire = (signal) => client.runs.compact(taskId, { ...(shot.session !== undefined ? { session: shot.session } : {}), ...(signal ? { signal } : {}) });
110
127
  const ack = shot.injected ? await settleInjected(INJECTED_WIRE_SETTLE_MS, undefined, fire) : await fire(undefined);
111
128
  hostLog('debug', `engineCompactWire: POST compact ${taskId} → ok${ack?.status ? ` (${ack.status})` : ''}`);
112
- emitCompactOutcome(sink, { kind: 'accepted' });
129
+ settleCompactRequests(requests, { kind: 'accepted' });
113
130
  }
114
131
  catch (e) {
115
132
  const outcome = compactFailureOutcomeOf(e);
@@ -119,13 +136,16 @@ async function postCompact(shot, sink, retriesLeft = 1) {
119
136
  else {
120
137
  hostLog('debug', `engineCompactWire: compact fire failed (best-effort): ${wireFailureTextsOf(e).log}`);
121
138
  }
122
- if (outcome.kind === 'refused' && outcome.status === 404 && retriesLeft > 0) {
123
- if (deferFire(2000, () => void postCompact(shot, sink, retriesLeft - 1), '404 单次重试', shot.sessionKey))
139
+ if (outcome.kind === 'refused' && outcome.status === 404 && retriesUsed < COMPACT_404_RETRIES) {
140
+ const waitMs = COMPACT_404_FIRST_RETRY_MS * 2 ** retriesUsed;
141
+ if (deferFire(waitMs, () => void postCompact(shot, requests, retriesUsed + 1), '404 短梯重试', shot.sessionKey))
124
142
  return;
125
143
  }
126
- if (outcome.kind === 'refused' && outcome.errorCode === COMPACT_NOT_RUNNING)
127
- surfaceCompactNotRunning(shot.sessionKey);
128
- emitCompactOutcome(sink, outcome);
144
+ if (outcome.kind === 'refused' && outcome.errorCode === COMPACT_NOT_RUNNING) {
145
+ settleNotRunning(shot.sessionKey, requests, outcome);
146
+ return;
147
+ }
148
+ settleCompactRequests(requests, outcome);
129
149
  }
130
150
  }
131
151
  export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY, opts) {
@@ -141,37 +161,29 @@ export function requestEngineCompact(sessionKey = DEFAULT_SESSION_KEY, opts) {
141
161
  const taskId = activeEngineRunIdFor(sessionKey);
142
162
  const rawSink = opts?.onSettled;
143
163
  const sink = typeof rawSink === 'function' ? rawSink : undefined;
164
+ const request = { sink, continued: false };
144
165
  if (taskId) {
145
166
  const shot = freezeCompactShot(taskId, sessionKey, wire);
146
167
  if (shot !== null)
147
- void postCompact(shot, sink);
168
+ void postCompact(shot, [request]);
148
169
  else
149
170
  emitCompactOutcome(sink, { kind: 'offline' });
150
171
  return 'fired';
151
172
  }
152
- pendingCompactKeys.add(sessionKey);
153
- if (sink !== undefined) {
154
- const sinks = pendingCompactSinks.get(sessionKey);
155
- if (sinks === undefined)
156
- pendingCompactSinks.set(sessionKey, [sink]);
157
- else
158
- sinks.push(sink);
159
- }
173
+ armCompactRequests(sessionKey, [request]);
160
174
  return 'armed';
161
175
  }
162
- const BOUND_FIRE_DELAY_MS = 1500;
163
176
  export function onEngineTaskBound(taskId, sessionKey = DEFAULT_SESSION_KEY, opts) {
164
177
  const wire = engineWireFor(opts?.wire, sessionKey, usableCompactClient);
165
178
  if (wire.kind !== 'no-target' && wire.kind !== 'injected-unusable')
166
179
  probeManualCompactCapability(wire);
167
- if (!pendingCompactKeys.has(sessionKey))
180
+ const requests = pendingCompactRequests.get(sessionKey);
181
+ if (requests === undefined)
168
182
  return;
169
183
  const shot = freezeCompactShot(taskId, sessionKey, wire);
170
184
  if (shot === null)
171
185
  return;
172
- const sink = fanOutCompactSinks(pendingCompactSinks.get(sessionKey));
173
- if (deferFire(BOUND_FIRE_DELAY_MS, () => void postCompact(shot, sink), 'bind 后发射', sessionKey)) {
174
- pendingCompactKeys.delete(sessionKey);
175
- pendingCompactSinks.delete(sessionKey);
176
- }
186
+ const batch = [...requests];
187
+ if (deferFire(0, () => void postCompact(shot, batch), 'bind 后发射', sessionKey))
188
+ pendingCompactRequests.delete(sessionKey);
177
189
  }
@@ -12,13 +12,14 @@ export declare function delegatedPromptText(content: unknown): string | undefine
12
12
  export declare function pickDelegatedPrompt(messages: readonly unknown[]): string | undefined;
13
13
  type TranscriptWindowReader = (transcriptId: string) => Promise<readonly unknown[] | null>;
14
14
  export declare function __setTranscriptWindowReaderForTests(r: TranscriptWindowReader | null): void;
15
- export declare const DELEGATED_PROMPT_ABSENCE_CAUSES: readonly ["not_owned_after_restart", "not_in_store", "forbidden", "prompt_absent", "unknown"];
15
+ export declare const DELEGATED_PROMPT_ABSENCE_CAUSES: readonly ["anchor_unreported", "not_in_store", "forbidden", "prompt_absent", "unknown"];
16
16
  export type DelegatedPromptAbsenceCause = (typeof DELEGATED_PROMPT_ABSENCE_CAUSES)[number];
17
17
  export interface DelegatedPromptAbsence {
18
18
  readonly cause: DelegatedPromptAbsenceCause;
19
19
  readonly text: string;
20
20
  }
21
21
  export declare function delegatedPromptAbsenceSentence(cause: DelegatedPromptAbsenceCause | string, lang?: UiLanguage): string;
22
+ export declare function standingDelegatedPromptAbsence(taskId: string, lang?: UiLanguage): DelegatedPromptAbsence | undefined;
22
23
  export interface DelegatedPromptFetch {
23
24
  prompt?: string;
24
25
  outcome: 'hit' | 'skipped' | 'unreadable' | 'absent';
@@ -26,5 +27,5 @@ export interface DelegatedPromptFetch {
26
27
  }
27
28
  export declare function fetchDelegatedPrompt(taskId: string, opts?: {
28
29
  wire?: DelegatedPromptWire;
29
- }): Promise<DelegatedPromptFetch>;
30
+ }, lang?: UiLanguage): Promise<DelegatedPromptFetch>;
30
31
  export {};
@@ -115,9 +115,9 @@ async function readTranscriptWindow(transcriptId, sessionKey, injectedWire) {
115
115
  const msgs = rec.messages;
116
116
  return Array.isArray(msgs) ? msgs : null;
117
117
  }
118
- export const DELEGATED_PROMPT_ABSENCE_CAUSES = Object.freeze(['not_owned_after_restart', 'not_in_store', 'forbidden', 'prompt_absent', 'unknown']);
118
+ export const DELEGATED_PROMPT_ABSENCE_CAUSES = Object.freeze(['anchor_unreported', 'not_in_store', 'forbidden', 'prompt_absent', 'unknown']);
119
119
  const ABSENCE_KEY = {
120
- not_owned_after_restart: 'session.subagent.delegatedPrompt.absence.notOwnedAfterRestart',
120
+ anchor_unreported: 'session.subagent.delegatedPrompt.absence.anchorUnreported',
121
121
  not_in_store: 'session.subagent.delegatedPrompt.absence.notInStore',
122
122
  forbidden: 'session.subagent.delegatedPrompt.absence.forbidden',
123
123
  prompt_absent: 'session.subagent.delegatedPrompt.absence.promptAbsent',
@@ -127,46 +127,83 @@ export function delegatedPromptAbsenceSentence(cause, lang) {
127
127
  const known = typeof cause === 'string' && Object.hasOwn(ABSENCE_KEY, cause);
128
128
  return renderMessage(msg(known ? ABSENCE_KEY[cause] : ABSENCE_KEY.unknown), lang);
129
129
  }
130
- function absenceOf(cause) {
131
- return Object.freeze({ cause, text: delegatedPromptAbsenceSentence(cause) });
130
+ function absenceOf(cause, lang) {
131
+ return Object.freeze({ cause, text: delegatedPromptAbsenceSentence(cause, lang) });
132
132
  }
133
- function absenceOfThrown(e) {
133
+ function causeOfThrown(e) {
134
134
  const status = readThrownSlot(e, 'status');
135
135
  if (status === 404)
136
- return absenceOf('not_in_store');
136
+ return 'not_in_store';
137
137
  if (status === 401 || status === 403)
138
- return absenceOf('forbidden');
139
- return absenceOf('unknown');
138
+ return 'forbidden';
139
+ return 'unknown';
140
140
  }
141
- export async function fetchDelegatedPrompt(taskId, opts) {
142
- if (promptHydrateInFlight.has(taskId)) {
143
- const settled = promptHydrateInFlight.get(taskId);
144
- return settled ? { outcome: 'skipped', absence: settled } : { outcome: 'skipped' };
145
- }
146
- const transcriptId = getEngineTranscriptId(taskId);
147
- if (!isSessionIdPathSegment(transcriptId))
148
- return { outcome: 'skipped', absence: absenceOf(transcriptId === undefined ? 'not_owned_after_restart' : 'unknown') };
149
- promptHydrateInFlight.set(taskId, null);
141
+ function anchorlessCause(anchor) {
142
+ return anchor === undefined ? 'anchor_unreported' : 'unknown';
143
+ }
144
+ export function standingDelegatedPromptAbsence(taskId, lang) {
145
+ if (typeof taskId !== 'string' || taskId === '')
146
+ return undefined;
147
+ const anchor = getEngineTranscriptId(taskId);
148
+ if (!isSessionIdPathSegment(anchor))
149
+ return absenceOf(anchorlessCause(anchor), lang);
150
+ const mark = promptHydrateInFlight.get(taskId);
151
+ return mark !== undefined && mark.anchor === anchor && (mark.phase === 'absent' || mark.phase === 'failed') ? absenceOf(mark.cause, lang) : undefined;
152
+ }
153
+ async function readDelegatedPrompt(taskId, transcriptId, wire, lang) {
150
154
  let msgs = null;
155
+ let prompt;
151
156
  try {
152
- msgs = await readTranscriptWindow(transcriptId, resolveOwnerContext(taskId).key, opts?.wire);
157
+ msgs = await readTranscriptWindow(transcriptId, resolveOwnerContext(taskId).key, wire);
158
+ prompt = msgs ? pickDelegatedPrompt(msgs) : undefined;
153
159
  }
154
160
  catch (e) {
155
- promptHydrateInFlight.delete(taskId);
156
161
  hostLog('debug', `[delegated-prompt] ${taskId} transcript ${transcriptId} read failed: ${String(e).slice(0, 160)}`);
157
- return { outcome: 'unreadable', absence: absenceOfThrown(e) };
162
+ return { outcome: 'unreadable', absence: absenceOf(causeOfThrown(e), lang) };
158
163
  }
159
- const prompt = msgs ? pickDelegatedPrompt(msgs) : undefined;
160
164
  if (!prompt) {
161
- if (msgs === null)
162
- promptHydrateInFlight.delete(taskId);
163
165
  hostLog('debug', `[delegated-prompt] ${taskId} transcript ${transcriptId}: ${msgs === null ? 'transcript unreadable (retryable)' : 'no delegated user message (final)'}`);
164
166
  if (msgs === null)
165
- return { outcome: 'unreadable', absence: absenceOf('unknown') };
166
- const settled = absenceOf('prompt_absent');
167
- promptHydrateInFlight.set(taskId, settled);
168
- return { outcome: 'absent', absence: settled };
167
+ return { outcome: 'unreadable', absence: absenceOf('unknown', lang) };
168
+ return { outcome: 'absent', absence: absenceOf('prompt_absent', lang) };
169
169
  }
170
170
  hostLog('debug', `[delegated-prompt] ${taskId} <- transcript ${transcriptId} (${prompt.length} chars) head=${JSON.stringify(prompt.slice(0, 60))}`);
171
- return { prompt, outcome: 'hit' };
171
+ return { outcome: 'hit', prompt };
172
+ }
173
+ function markOf(anchor, r) {
174
+ if (r.outcome === 'hit')
175
+ return { anchor, phase: 'hit' };
176
+ return { anchor, phase: r.outcome === 'absent' ? 'absent' : 'failed', cause: r.absence.cause };
177
+ }
178
+ function landed(taskId, mine, next) {
179
+ if (promptHydrateInFlight.get(taskId) !== mine)
180
+ return false;
181
+ if (getEngineTranscriptId(taskId) !== mine.anchor) {
182
+ promptHydrateInFlight.delete(taskId);
183
+ return false;
184
+ }
185
+ promptHydrateInFlight.set(taskId, next);
186
+ return true;
187
+ }
188
+ export async function fetchDelegatedPrompt(taskId, opts, lang) {
189
+ const transcriptId = getEngineTranscriptId(taskId);
190
+ const held = promptHydrateInFlight.get(taskId);
191
+ if (held !== undefined) {
192
+ if (held.anchor !== transcriptId)
193
+ promptHydrateInFlight.delete(taskId);
194
+ else if (held.phase === 'flying' || held.phase === 'hit')
195
+ return { outcome: 'skipped' };
196
+ else if (held.phase === 'absent')
197
+ return { outcome: 'skipped', absence: absenceOf(held.cause, lang) };
198
+ }
199
+ if (!isSessionIdPathSegment(transcriptId))
200
+ return { outcome: 'skipped', absence: absenceOf(anchorlessCause(transcriptId), lang) };
201
+ const mine = { anchor: transcriptId, phase: 'flying' };
202
+ promptHydrateInFlight.set(taskId, mine);
203
+ const read = await readDelegatedPrompt(taskId, transcriptId, opts?.wire, lang);
204
+ if (!landed(taskId, mine, markOf(transcriptId, read))) {
205
+ hostLog('debug', `[delegated-prompt] ${taskId} dropped=${transcriptId}`);
206
+ return { outcome: 'skipped' };
207
+ }
208
+ return read;
172
209
  }
@@ -0,0 +1,9 @@
1
+ import type { UiLanguage } from './env/uiLanguage.js';
2
+ export interface SubagentEditOutcomes {
3
+ readonly attempted: number;
4
+ readonly succeeded: number;
5
+ readonly refused: number;
6
+ readonly failed: number;
7
+ }
8
+ export declare function subagentEditOutcomesOf(toolStats: {} | null | undefined): SubagentEditOutcomes | undefined;
9
+ export declare function subagentEditOutcomesSentence(outcomes: SubagentEditOutcomes | null | undefined, lang?: UiLanguage): string | undefined;