@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
@@ -1,15 +1,16 @@
1
- import { engineCapNestedTrue, engineCapState, engineCapString, engineCapValue } from './engineCapsCache.js';
2
- import { hooksReaderNotReadKeys } from './hooksReaderGenerations.js';
1
+ import { engineCapNestedTrue, engineCapNestedValue, engineCapState, engineCapString, engineCapValue } from './engineCapsCache.js';
2
+ import { compareReleaseVersions } from './supportFloor.js';
3
+ import { hooksEntryKeyReaderOf } from './hooksReaderGenerations.js';
3
4
  import { msg, renderMessage } from './i18n/translate.js';
4
5
  import { capForDisplay } from './fleetTaskDesc.js';
5
6
  import { hostLog } from './host.js';
6
7
  import { faultText } from './hostCallbackGuard.js';
7
- import { HostSettingsSourceShapeError, MANAGED_READING_DISPOSITIONS, mergedBooleanOf, nonManagedLayerDown, readHostSettingsOnce } from './hostSettingsSource.js';
8
+ import { HostSettingsSourceShapeError, MANAGED_READING_DISPOSITIONS, mergedBooleanOf, nonManagedLayersDown, readHostSettingsOnce } from './hostSettingsSource.js';
8
9
  import { admittedLayers, layerAdmissionWord } from './settingsLayerAdmission.js';
9
10
  import { guardedRead, readOwnDataValue } from './ownKey.js';
10
- import { HOOK_SETTINGS_SOURCE_TEXT, hooksSalvageMaskOf, salvageSettingsHooksEntries, settingsHooksSalvageNotice } from './settingsHooksSalvage.js';
11
+ import { HOOK_SETTINGS_SOURCE_TEXT, hooksSalvageMaskOf, isGuardHookEvent, salvageSettingsHooksEntries, settingsHooksSalvageNotice } from './settingsHooksSalvage.js';
11
12
  import { submissionOriginOf } from './request/submissionIdentity.js';
12
- import { createSettingsHookJudge, decidePluginHooks, decideSessionHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, hooksWirePlanShapeOf, hookEntryTypeWordOf, isEngineFiredHookEvent, occupancyOf, } from './pluginHooksWire.js';
13
+ import { createSettingsHookJudge, decidePluginHooks, decideSessionHooks, dedupeSettingsHookGroups, filterSettingsHookGroups, hooksWirePlanShapeOf, hookEntryTypeWordOf, isEngineFiredHookEvent, SUPPORT_FLOOR_SETTINGS_CONTRACT, occupancyOf, } from './pluginHooksWire.js';
13
14
  import { MAX_HOOK_NOTICE_TEXT_CHARS } from './notifications.js';
14
15
  import { applyHttpHookPolicy, httpHookAdmittedSources, readHttpHookPolicy, wireHasHttpEntry } from './httpHookPolicy.js';
15
16
  import { goalStopHookMatcher, __resetGoalStopHookForTests } from './goalStopHook.js';
@@ -164,12 +165,15 @@ export function hooksWirePlan(opts) {
164
165
  for (const line of read.debug)
165
166
  hostLog('debug', line);
166
167
  const owned = o.engineOwnedByThisShell;
168
+ const reader = hooksEntryKeyReaderOf(engineCapValue(o.baseUrl, 'settingsContract'), engineCapString(o.baseUrl, 'version'));
167
169
  const engine = {
168
170
  ownedByThisShell: owned === true ? true : owned === false ? false : null,
169
171
  pluginHooksCapable: engineCapState(o.baseUrl, 'taskSettings') === 'unprobed'
170
172
  ? null
171
173
  : engineCapNestedTrue(o.baseUrl, 'taskSettings', 'pluginHooks'),
172
174
  hooksCapable: taskHooksCapability(o.baseUrl),
175
+ settingsContract: peerSettingsContractOf(o.baseUrl),
176
+ hookEntryFields: reader.fields,
173
177
  };
174
178
  const shellEvents = new Set();
175
179
  if (Array.isArray(o.shellHookEvents)) {
@@ -186,8 +190,19 @@ export function hooksWirePlan(opts) {
186
190
  const types = typesByEvent.get(event);
187
191
  return types === undefined || (typeof type === 'string' && types.has(type));
188
192
  };
189
- const readerNotRead = hooksReaderNotReadKeys(engineCapString(o.baseUrl, 'version'));
190
- return buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUserOf(o), suppressedLocallyOf(o), readerNotRead, compactionOnShellOf(o));
193
+ return buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUserOf(o), suppressedLocallyOf(o), reader, compactionOnShellOf(o));
194
+ }
195
+ function peerSettingsContractOf(baseUrl) {
196
+ const floor = (basis) => basis === 'absent' ? SUPPORT_FLOOR_SETTINGS_CONTRACT : Object.freeze({ version: SUPPORT_FLOOR_SETTINGS_CONTRACT.version, basis });
197
+ const group = engineCapValue(baseUrl, 'settingsContract');
198
+ if (group.kind === 'unobserved' || group.kind === 'not_reported')
199
+ return floor('absent');
200
+ if (group.kind !== 'value' || typeof group.value !== 'object' || group.value === null || Array.isArray(group.value))
201
+ return floor('unreadable');
202
+ const v = engineCapNestedValue(baseUrl, 'settingsContract', 'version');
203
+ if (v.kind !== 'value' || typeof v.value !== 'string' || compareReleaseVersions(v.value, v.value) === undefined)
204
+ return floor('unreadable');
205
+ return Object.freeze({ version: v.value, basis: 'reported' });
191
206
  }
192
207
  function taskHooksCapability(baseUrl) {
193
208
  const v = engineCapValue(baseUrl, 'taskSettings');
@@ -300,7 +315,7 @@ function withheldRowsOf(candidates, trust, layers) {
300
315
  }
301
316
  return rows;
302
317
  }
303
- function closedDropRowsOf(source, doc, reason) {
318
+ function layerDropRowsOf(source, doc, reason) {
304
319
  const rows = [];
305
320
  try {
306
321
  const hooks = readOwnDataValue(doc, 'hooks');
@@ -317,7 +332,7 @@ function closedDropRowsOf(source, doc, reason) {
317
332
  if (!Array.isArray(entries))
318
333
  return;
319
334
  entries.forEach((entry, hookIndex) => {
320
- rows.push({ source, event, groupIndex, hookIndex, ...hookEntryTypeWordOf(entry), reason, notRun: true });
335
+ rows.push({ source, event, groupIndex, hookIndex, ...hookEntryTypeWordOf(entry), reason, notRun: true, guard: isGuardHookEvent(event) });
321
336
  });
322
337
  });
323
338
  }
@@ -326,7 +341,7 @@ function closedDropRowsOf(source, doc, reason) {
326
341
  }
327
342
  return rows;
328
343
  }
329
- function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppressedLocally, readerNotRead, compactionOnShell = false) {
344
+ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppressedLocally, reader, compactionOnShell = false) {
330
345
  const settingsDropped = [];
331
346
  const settingsVerdicts = [];
332
347
  const settingsExcluded = [];
@@ -360,7 +375,7 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
360
375
  const closedPlan = (reason) => {
361
376
  closedBy = reason;
362
377
  for (const source of ALL_HOOK_SOURCES)
363
- settingsDropped.push(...closedDropRowsOf(source, read.layers[source].doc, reason));
378
+ settingsDropped.push(...layerDropRowsOf(source, read.layers[source].doc, reason));
364
379
  sessionReader = read.sessionHooks.state;
365
380
  const pr = read.plugins;
366
381
  if (pr.reading === undefined)
@@ -374,7 +389,7 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
374
389
  shellRuns,
375
390
  promptAuthoredByUser,
376
391
  occupied: new Map(),
377
- readerNotRead,
392
+ reader,
378
393
  }));
379
394
  if (d === undefined)
380
395
  return planOf('failed', undefined);
@@ -436,7 +451,9 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
436
451
  if (managedReading.hooksRestriction === 'withhold') {
437
452
  hostLog('debug', `hooksWireCaps: managed policy layer reported ${read.managed} — only policySettings hooks projected to the engine`);
438
453
  }
439
- const mergedDisable = () => mergedBooleanOf(read, 'disableAllHooks') === true || (read.current !== undefined && mergedBooleanOf(read.current, 'disableAllHooks') === true) || nonManagedLayerDown(read);
454
+ const explicitMergedDisable = () => mergedBooleanOf(read, 'disableAllHooks') === true || (read.current !== undefined && mergedBooleanOf(read.current, 'disableAllHooks') === true);
455
+ const layersDown = nonManagedLayersDown(read);
456
+ const mergedDisable = () => explicitMergedDisable() || layersDown.length > 0;
440
457
  const disabledByNonManaged = !managedByPolicy && mergedDisable();
441
458
  if (disabledByNonManaged) {
442
459
  hostLog('debug', 'hooksWireCaps: disableAllHooks (merged, non-managed) — only policySettings hooks projected to the engine');
@@ -479,6 +496,14 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
479
496
  const notAdmitted = ALL_HOOK_SOURCES.filter((s) => !hookAdmitted.includes(s) && read.layers[s].doc !== null);
480
497
  if (sources === hookAdmitted)
481
498
  settingsWithheldNow = Object.freeze(withheldRowsOf(notAdmitted, read.trust, read.layers));
499
+ if (disabledByNonManaged && startupMode === undefined && !explicitMergedDisable() && layersDown.length > 0) {
500
+ settingsWithheldNow = Object.freeze(layersDown.map((source) => Object.freeze({ source, cause: 'layer_degraded', notice: renderMessage(msg('config.hooks.layerWithheld.degraded', { layer: HOOK_SETTINGS_SOURCE_TEXT[source] })) })));
501
+ for (const source of hookAdmitted) {
502
+ if (source === 'policySettings' || layersDown.includes(source))
503
+ continue;
504
+ settingsDropped.push(...layerDropRowsOf(source, read.layers[source].doc, 'layer_degraded'));
505
+ }
506
+ }
482
507
  if (sources !== hookAdmitted ? false : notAdmitted.length > 0) {
483
508
  hostLog('debug', `hooksWireCaps: workspace trust reading ${read.trust} — hooks from ${notAdmitted.join(', ')} are not admitted (repository-controlled layers need an accepted workspace)`);
484
509
  }
@@ -496,11 +521,11 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
496
521
  continue;
497
522
  if (!Array.isArray(groups) || groups.length === 0)
498
523
  continue;
499
- const kept = filterSettingsHookGroups(source, event, groups, engine.pluginHooksCapable === true, (type) => shellRuns(event, type), judge, readerNotRead, layer.mask);
524
+ const kept = filterSettingsHookGroups(source, event, groups, engine.pluginHooksCapable === true, (type) => shellRuns(event, type), judge, reader, engine.settingsContract, layer.mask);
500
525
  settingsDropped.push(...kept.drops);
501
526
  settingsVerdicts.push(...kept.verdicts);
502
527
  settingsExcluded.push(...kept.excluded);
503
- if (!isEngineFiredHookEvent(event))
528
+ if (!isEngineFiredHookEvent(event, engine.settingsContract))
504
529
  continue;
505
530
  settingsStripped += kept.stripped;
506
531
  if (kept.groups.length === 0)
@@ -520,7 +545,7 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
520
545
  merged[event] = d.groups;
521
546
  settingsDeduped += d.removed;
522
547
  }
523
- const goalMatcher = goalStopHookMatcher(managedOnly);
548
+ const goalMatcher = isEngineFiredHookEvent('Stop', engine.settingsContract) ? goalStopHookMatcher(managedOnly) : null;
524
549
  if (goalMatcher !== null) {
525
550
  merged['Stop'] = [...(merged['Stop'] ?? []), goalMatcher];
526
551
  }
@@ -530,7 +555,7 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
530
555
  }
531
556
  if (settingsDeduped > 0) {
532
557
  hostLog('debug', `hooksWireCaps: ${settingsDeduped} settings hook entr${settingsDeduped === 1 ? 'y appears' : 'ies appear'} more than once ` +
533
- '(same event, matcher group and hook) — sent once, at the first position, with the fields of the highest-precedence source');
558
+ '(same event, matcher group and identical hook content) — sent once, at the first position');
534
559
  }
535
560
  if (settingsStripped > 0) {
536
561
  hostLog('debug', `hooksWireCaps: ${settingsStripped} settings hook entr${settingsStripped === 1 ? 'y' : 'ies'} carried a plugin key — ` +
@@ -564,7 +589,7 @@ function buildHooksWirePlan(read, engine, shellRuns, promptAuthoredByUser, suppr
564
589
  promptAuthoredByUser,
565
590
  occupied,
566
591
  ...(suppressedLocally !== undefined ? { suppressedLocally } : {}),
567
- readerNotRead,
592
+ reader,
568
593
  });
569
594
  });
570
595
  if (d === undefined)
@@ -73,7 +73,7 @@ export declare function mergedBooleanOf(read: HostSettingsRead, key: string): bo
73
73
  export declare const CC_MERGE_ORDER: readonly ["userSettings", "projectSettings", "localSettings", "flagSettings", "policySettings"];
74
74
  export type TightenOnlyHostSettingsLayer = Exclude<HostSettingsLayer, (typeof CC_MERGE_ORDER)[number]>;
75
75
  export declare const TIGHTEN_ONLY_HOST_SETTINGS_LAYERS: readonly TightenOnlyHostSettingsLayer[];
76
- export declare function nonManagedLayerDown(read: HostSettingsRead): boolean;
76
+ export declare function nonManagedLayersDown(read: HostSettingsRead): readonly (typeof CC_MERGE_ORDER)[number][];
77
77
  export declare const ADOPTED_LAYERS: readonly ["userSettings", "projectSettings", "localSettings", "flagSettings"];
78
78
  export type AdoptedLayer = (typeof ADOPTED_LAYERS)[number];
79
79
  export interface HostLayerRule {
@@ -395,8 +395,8 @@ export function mergedBooleanOf(read, key) {
395
395
  }
396
396
  export const CC_MERGE_ORDER = Object.freeze(['userSettings', 'projectSettings', 'localSettings', 'flagSettings', 'policySettings']);
397
397
  export const TIGHTEN_ONLY_HOST_SETTINGS_LAYERS = Object.freeze(HOST_SETTINGS_LAYERS.filter((l) => !CC_MERGE_ORDER.includes(l)));
398
- export function nonManagedLayerDown(read) {
399
- return CC_MERGE_ORDER.some((l) => {
398
+ export function nonManagedLayersDown(read) {
399
+ return CC_MERGE_ORDER.filter((l) => {
400
400
  if (l === 'policySettings')
401
401
  return false;
402
402
  const snap = read.layers[l];
@@ -3,7 +3,7 @@ import type { WireHooksConfig } from './finalVerifyWire.js';
3
3
  import type { memoryOffDeclared as MemoryOffDeclared } from './request/taskRequest.js';
4
4
  import type { EnvLike } from './hostEnv.js';
5
5
  import { type SettingsDocument } from './host.js';
6
- import { type HooksWirePlan, type HooksWirePlanOptions } from './pluginHooksWire.js';
6
+ import { type HooksWirePlan, type HooksWirePlanOptions, type HooksWireSettingsContract } from './pluginHooksWire.js';
7
7
  import { type HostSettingsLayer, type HostSettingsSession, type HostSettingsSource } from './hostSettingsSource.js';
8
8
  import { type SubmissionIdentity } from './request/submissionIdentity.js';
9
9
  import { type WebSearchApiKeyFor } from './webSearchWireCaps.js';
@@ -114,7 +114,7 @@ export interface HostSettingsHeld {
114
114
  };
115
115
  readonly version: string;
116
116
  }
117
- export declare function hookEnvNamesOf(hooks: unknown): readonly string[];
117
+ export declare function hookEnvNamesOf(hooks: unknown, contract: HooksWireSettingsContract | undefined): readonly string[];
118
118
  export interface HostTurnSettingsJudges {
119
119
  readonly materialize: HostResolvedMaterializer;
120
120
  readonly memoryOffDeclared: typeof MemoryOffDeclared;
@@ -1,6 +1,6 @@
1
1
  import { SETTINGS_CLASSIFY_HOOKS_CONFIG } from './generated/settingsSchemaFns.js';
2
2
  import { hostLog } from './host.js';
3
- import { isEngineFiredHookEvent } from './pluginHooksWire.js';
3
+ import { engineLaneOf, planSettingsContractOf } from './pluginHooksWire.js';
4
4
  import { layerNotAppliedRefusesTurn, salvageSettingsHooksEntries } from './settingsHooksSalvage.js';
5
5
  import { ADOPTED_LAYERS, HOST_SETTINGS_LAYERS, HostSettingsSourceShapeError, TIGHTEN_ONLY_HOST_SETTINGS_LAYERS, permissionBucketOf, permissionRulesOf, MANAGED_READING_DISPOSITIONS, adoptHostSettingsRead, mintHostSettingsSession, readHostSettingsOnce, remintSameContent, sessionSnapshotOf, settingsValueOf, sourceOfRead, } from './hostSettingsSource.js';
6
6
  import { hooksWirePlan } from './hooksWireCaps.js';
@@ -14,7 +14,7 @@ import { assemblyRefusalOf } from './wireFailureShape.js';
14
14
  import { permissionRuleLayersOn } from './permissionsAllowGrantCapability.js';
15
15
  import { layerAdmissionReading, layerAdmitted } from './settingsLayerAdmission.js';
16
16
  import { SETTINGS_BUILD_LAYER_PARTS_UNREAD_NOTICE } from './generated/settingsSchemaFns.js';
17
- import { appendOwnElement, ownDataField, readOwnDataValue } from './ownKey.js';
17
+ import { appendOwnElement, ownDataField, readOwnDataValue, sameJson } from './ownKey.js';
18
18
  export const HOST_SUPPLIED_KEYS = Object.freeze(['settings.hooks', 'settings.resolved', 'settings.webSearch', 'settings.outputStyle', 'memory', 'fileHistory', 'settings.env']);
19
19
  export const HOST_SETTING_CAUSES = Object.freeze([
20
20
  'carried',
@@ -219,12 +219,14 @@ function ownElementsOf(v) {
219
219
  appendOwnElement(out, readOwnDataValue(v, String(i)));
220
220
  return out;
221
221
  }
222
- export function hookEnvNamesOf(hooks) {
222
+ export function hookEnvNamesOf(hooks, contract) {
223
223
  const out = [];
224
224
  if (!isRecordShape(hooks))
225
225
  return out;
226
+ let engineEvent;
226
227
  for (const event of Object.keys(hooks)) {
227
- if (!isEngineFiredHookEvent(event))
228
+ engineEvent ??= engineLaneOf(contract);
229
+ if (!engineEvent(event))
228
230
  continue;
229
231
  for (const group of ownElementsOf(readOwnDataValue(hooks, event))) {
230
232
  if (!isRecordShape(group))
@@ -241,6 +243,9 @@ export function hookEnvNamesOf(hooks) {
241
243
  }
242
244
  return out;
243
245
  }
246
+ function objectOrUndefined(v) {
247
+ return typeof v === 'object' && v !== null ? v : undefined;
248
+ }
244
249
  function hookEnvLayerOf(names, env) {
245
250
  const layer = {};
246
251
  let n = 0;
@@ -253,7 +258,7 @@ function hookEnvLayerOf(names, env) {
253
258
  }
254
259
  return n > 0 ? freeze(layer) : undefined;
255
260
  }
256
- function hookEnvConsistent(env, hooks) {
261
+ function hookEnvConsistent(env, hooks, contract) {
257
262
  if (env === undefined)
258
263
  return true;
259
264
  try {
@@ -262,14 +267,14 @@ function hookEnvConsistent(env, hooks) {
262
267
  const keys = Reflect.ownKeys(env);
263
268
  if (keys.length === 0)
264
269
  return false;
265
- const names = hookEnvNamesOf(hooks);
270
+ const names = hookEnvNamesOf(hooks, contract);
266
271
  return keys.every((k) => typeof k === 'string' && names.includes(k) && typeof readOwnDataValue(env, k) === 'string');
267
272
  }
268
273
  catch {
269
274
  return false;
270
275
  }
271
276
  }
272
- function envArm(src, hooksCarried) {
277
+ function envArm(src, hooksCarried, contract) {
273
278
  const key = 'settings.env';
274
279
  if (src.kind === 'missing')
275
280
  return missingArm(key);
@@ -293,7 +298,7 @@ function envArm(src, hooksCarried) {
293
298
  throw badSource(key, '{ env } where env is the environment record');
294
299
  let layer;
295
300
  try {
296
- layer = hookEnvLayerOf(hookEnvNamesOf(hooksCarried), env);
301
+ layer = hookEnvLayerOf(hookEnvNamesOf(hooksCarried, contract), env);
297
302
  }
298
303
  catch {
299
304
  return unreadableArm(key);
@@ -864,7 +869,7 @@ export function buildHostTurnSettings(input, judges) {
864
869
  }
865
870
  const hooksFinal = armsByKey[0];
866
871
  const hooksToCarry = hooksFinal.row.disposition === 'carry' || hooksFinal.row.disposition === 'carry_partial' || hooksFinal.row.disposition === 'hold_for_consent' ? hooksFinal.carried : undefined;
867
- const env = envArm(envSource, hooksToCarry);
872
+ const env = envArm(envSource, hooksToCarry, planSettingsContractOf(hooks.plan));
868
873
  const arms = [...armsByKey, env];
869
874
  const rows = freeze(arms.map((a) => a.row));
870
875
  const resolvedOut = arms[1]?.row.disposition === 'carry' || arms[1]?.row.disposition === 'carry_partial' || arms[1]?.row.disposition === 'hold_for_consent' ? resolvedCarried : undefined;
@@ -1089,7 +1094,7 @@ export function hostTurnSettingsOf(v, depth = 'full') {
1089
1094
  return undefined;
1090
1095
  if (Object.hasOwn(carried, 'hooks') && carried.hooks !== undefined && !hooksPassJudge(carried.hooks))
1091
1096
  return undefined;
1092
- if (!hookEnvConsistent(Object.hasOwn(carried, 'env') ? carried.env : undefined, Object.hasOwn(carried, 'hooks') ? carried.hooks : undefined))
1097
+ if (!hookEnvConsistent(Object.hasOwn(carried, 'env') ? carried.env : undefined, Object.hasOwn(carried, 'hooks') ? carried.hooks : undefined, planSettingsContractOf(objectOrUndefined(readOwnDataValue(shaped, 'hooks')))))
1093
1098
  return undefined;
1094
1099
  return shaped;
1095
1100
  }
@@ -1101,22 +1106,6 @@ function wireFormOf(v) {
1101
1106
  const text = JSON.stringify(v);
1102
1107
  return text === undefined ? undefined : JSON.parse(text);
1103
1108
  }
1104
- function sameJson(a, b) {
1105
- if (a === b)
1106
- return true;
1107
- if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
1108
- return false;
1109
- if (Array.isArray(a) !== Array.isArray(b))
1110
- return false;
1111
- if (Array.isArray(a) && Array.isArray(b))
1112
- return a.length === b.length && a.every((x, i) => sameJson(x, b[i]));
1113
- const ra = a;
1114
- const rb = b;
1115
- const ka = Object.keys(ra);
1116
- if (ka.length !== Object.keys(rb).length)
1117
- return false;
1118
- return ka.every((k) => Object.hasOwn(rb, k) && sameJson(ra[k], rb[k]));
1119
- }
1120
1109
  function groupHead(g) {
1121
1110
  if (!isRecordShape(g))
1122
1111
  return undefined;
@@ -1126,6 +1115,26 @@ function groupHead(g) {
1126
1115
  head[k] = g[k];
1127
1116
  return head;
1128
1117
  }
1118
+ function httpAllowedEnvNames(entry) {
1119
+ if (!isRecordShape(entry) || !Object.hasOwn(entry, 'type') || entry.type !== 'http')
1120
+ return undefined;
1121
+ if (!Object.hasOwn(entry, 'allowedEnvVars'))
1122
+ return undefined;
1123
+ const names = entry.allowedEnvVars;
1124
+ return Array.isArray(names) && names.every((n) => typeof n === 'string') ? names : undefined;
1125
+ }
1126
+ function withoutAllowedEnvVars(entry) {
1127
+ return Object.fromEntries(Object.entries(entry).filter(([k]) => k !== 'allowedEnvVars'));
1128
+ }
1129
+ function isSubsetWitness(entry, witness) {
1130
+ if (!isRecordShape(entry) || !isRecordShape(witness))
1131
+ return false;
1132
+ const want = httpAllowedEnvNames(entry);
1133
+ const have = httpAllowedEnvNames(witness);
1134
+ if (want === undefined || have === undefined)
1135
+ return false;
1136
+ return want.every((n) => have.includes(n)) && sameJson(withoutAllowedEnvVars(entry), withoutAllowedEnvVars(witness));
1137
+ }
1129
1138
  export function intersectHostHooks(sent, now) {
1130
1139
  try {
1131
1140
  return intersectHostHooksUnguarded(sent, now);
@@ -1156,38 +1165,68 @@ function intersectHostHooksUnguarded(sentIn, nowIn) {
1156
1165
  return { hooks: undefined, dropped: 0, judged: true };
1157
1166
  if (!isRecordShape(now))
1158
1167
  return { hooks: undefined, dropped: total, judged: true };
1159
- const out = {};
1160
- let kept = 0;
1161
- for (const ev of Object.keys(sent)) {
1162
- const groups = sent[ev];
1163
- const nowGroups = now[ev];
1168
+ const events = [];
1169
+ for (const event of Object.keys(sent)) {
1170
+ const groups = sent[event];
1171
+ const nowGroups = now[event];
1164
1172
  if (!Array.isArray(groups) || !Array.isArray(nowGroups))
1165
1173
  continue;
1166
1174
  const pool = nowGroups.map((g) => ({ head: groupHead(g), left: isRecordShape(g) && Array.isArray(g.hooks) ? [...g.hooks] : [] }));
1167
- const evOut = [];
1175
+ const sentGroups = [];
1168
1176
  for (const g of groups) {
1169
1177
  const head = groupHead(g);
1170
1178
  if (head === undefined || !isRecordShape(g) || !Array.isArray(g.hooks))
1171
1179
  continue;
1172
- const keep = [];
1173
- for (const entry of g.hooks) {
1174
- for (const slot of pool) {
1175
- if (slot.head === undefined || !sameJson(slot.head, head))
1176
- continue;
1177
- const at = slot.left.findIndex((x) => sameJson(x, entry));
1178
- if (at < 0)
1179
- continue;
1180
- slot.left.splice(at, 1);
1181
- appendOwnElement(keep, entry);
1182
- break;
1183
- }
1180
+ const claims = [];
1181
+ for (const entry of g.hooks)
1182
+ appendOwnElement(claims, { entry, claimed: false });
1183
+ appendOwnElement(sentGroups, { head, claims });
1184
+ }
1185
+ appendOwnElement(events, { event, groups: sentGroups, pool });
1186
+ }
1187
+ const takeWitness = (ev, g, matches) => {
1188
+ for (const slot of ev.pool) {
1189
+ if (slot.head === undefined || !sameJson(slot.head, g.head))
1190
+ continue;
1191
+ const at = slot.left.findIndex(matches);
1192
+ if (at < 0)
1193
+ continue;
1194
+ const [witness] = slot.left.splice(at, 1);
1195
+ return { witness };
1196
+ }
1197
+ return undefined;
1198
+ };
1199
+ for (const ev of events)
1200
+ for (const g of ev.groups)
1201
+ for (const c of g.claims)
1202
+ if (takeWitness(ev, g, (w) => sameJson(w, c.entry)) !== undefined)
1203
+ c.claimed = true;
1204
+ for (const ev of events) {
1205
+ for (const g of ev.groups) {
1206
+ for (const c of g.claims) {
1207
+ if (c.claimed)
1208
+ continue;
1209
+ const hit = takeWitness(ev, g, (w) => isSubsetWitness(c.entry, w));
1210
+ if (hit !== undefined)
1211
+ c.claimed = true;
1184
1212
  }
1213
+ }
1214
+ }
1215
+ const out = {};
1216
+ let kept = 0;
1217
+ for (const ev of events) {
1218
+ const evOut = [];
1219
+ for (const g of ev.groups) {
1220
+ const keep = [];
1221
+ for (const c of g.claims)
1222
+ if (c.claimed)
1223
+ appendOwnElement(keep, c.entry);
1185
1224
  if (keep.length > 0)
1186
- appendOwnElement(evOut, { ...head, hooks: keep });
1225
+ appendOwnElement(evOut, { ...g.head, hooks: keep });
1187
1226
  kept += keep.length;
1188
1227
  }
1189
1228
  if (evOut.length > 0)
1190
- out[ev] = evOut;
1229
+ out[ev.event] = evOut;
1191
1230
  }
1192
1231
  const hooks = Object.keys(out).length > 0 ? out : undefined;
1193
1232
  return { hooks, dropped: total - kept, judged: hooks === undefined || hooksPassJudge(hooks) };
@@ -1363,7 +1402,8 @@ function intakeSnapshotUnguarded(product, onResolved) {
1363
1402
  return undefined;
1364
1403
  if (values.hooks !== undefined && !hooksPassJudge(values.hooks))
1365
1404
  return undefined;
1366
- if (!hookEnvConsistent(values.env, values.hooks))
1405
+ const planR = ownDataOf(product, 'hooks');
1406
+ if (!hookEnvConsistent(values.env, values.hooks, planR.ok ? planSettingsContractOf(objectOrUndefined(planR.value)) : undefined))
1367
1407
  return undefined;
1368
1408
  const v = ownDataOf(product, 'verdict');
1369
1409
  if (!v.ok)
@@ -38,6 +38,7 @@ export interface HumanTurnItem {
38
38
  export interface HumanTurnCaps {
39
39
  readonly vision?: ImageVision;
40
40
  readonly userAttachments?: UserAttachmentSeatReading;
41
+ readonly scenario?: {} | null;
41
42
  }
42
43
  export interface HumanTurnInput {
43
44
  readonly items: readonly HumanTurnItem[];
@@ -45,7 +46,7 @@ export interface HumanTurnInput {
45
46
  readonly caps: HumanTurnCaps;
46
47
  readonly otherBodyBytes?: number;
47
48
  }
48
- type EncoderShortfall = 'body_budget' | 'steer_admission' | 'vision_no' | 'image_too_large' | 'seat_count' | 'seat_total' | 'lane_has_no_image_seat' | 'lane_has_no_attachment_id_seat';
49
+ type EncoderShortfall = 'body_budget' | 'steer_admission' | 'vision_no' | 'image_too_large' | 'seat_count' | 'seat_total' | 'lane_has_no_image_seat' | 'lane_has_no_attachment_id_seat' | 'lane_has_no_path_seat' | 'path_not_admitted' | 'path_seat_count';
49
50
  export interface HumanTurnSharePart {
50
51
  readonly kind: 'text' | 'image' | 'attachment_ref' | 'attachment_id' | 'rider_text' | 'non_text';
51
52
  readonly ref?: string | number;
package/dist/humanTurn.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CORE_IMAGE_ADMISSION_HARD_IMAGE_BASE64_CHARS, CORE_MAX_PENDING_STEER_CHARS, CORE_USER_ATTACHMENT_LABEL_MAX } from './generated/engineFactTables.js';
2
- import { mintEnvelopeOf, mintReferenceOf, seatTextOf } from './userAttachmentEnvelope.js';
2
+ import { mintEnvelopeOf, mintPathPointerOf, mintReferenceOf, seatTextOf } from './userAttachmentEnvelope.js';
3
3
  import { readEncodingShares, readHumanTurnInput } from './humanTurnRead.js';
4
4
  import { putOwnKey } from './ownKey.js';
5
5
  import { msg, renderMessage } from './i18n/translate.js';
@@ -76,11 +76,11 @@ function steerRefusal(text) {
76
76
  return 'too_long';
77
77
  return undefined;
78
78
  }
79
- function textOf(p) {
79
+ function textOf(p, pointers) {
80
80
  if (p.form === 'reference')
81
81
  return mintReferenceOf(p.a.d);
82
82
  if (p.form === 'seat')
83
- return '';
83
+ return pointers && p.a.d.kind === 'path' ? mintPathPointerOf(p.a.d) : '';
84
84
  return mintEnvelopeOf(p.a.d, p.seated ? 'attached' : 'not-attached');
85
85
  }
86
86
  const digits = (n) => String(n).length;
@@ -100,10 +100,14 @@ function seatEntryOf(a, maxRenderedTextChars) {
100
100
  return undefined;
101
101
  return a.d.kind === 'text' ? { name, text, startLine } : { name, text };
102
102
  }
103
- function compose(items, placements) {
104
- const perItem = items.map((it, i) => [...placements.filter((p) => p.item === i).map(textOf), it.text].filter((s) => s.length > 0).join('\n\n'));
103
+ function compose(items, placements, pointers = false) {
104
+ const perItem = items.map((it, i) => [...placements.filter((p) => p.item === i).map((p) => textOf(p, pointers)), it.text].filter((s) => s.length > 0).join('\n\n'));
105
105
  return perItem.filter((s) => s.length > 0).join('\n');
106
106
  }
107
+ function composeObjective(items, placements) {
108
+ const text = compose(items, placements);
109
+ return text.trim() === '' && placements.some((p) => p.form === 'seat' && p.a.d.kind === 'path') ? compose(items, placements, true) : text;
110
+ }
107
111
  function seatedImages(items, pastedWhy, placements) {
108
112
  const out = [];
109
113
  items.forEach((it, i) => {
@@ -127,6 +131,9 @@ const ENCODER_WHY_LIST = [
127
131
  'seat_total',
128
132
  'lane_has_no_image_seat',
129
133
  'lane_has_no_attachment_id_seat',
134
+ 'lane_has_no_path_seat',
135
+ 'path_not_admitted',
136
+ 'path_seat_count',
130
137
  ];
131
138
  const encoderWhysExhaustive = true;
132
139
  void encoderWhysExhaustive;
@@ -146,7 +153,7 @@ function records(lane, items, pastedWhy, placements) {
146
153
  for (const p of placements) {
147
154
  if (p.item !== i)
148
155
  continue;
149
- push(i, p.form !== 'reference' ? { kind: 'attachment_ref', ref: p.a.uuid, status: 'full' } : { kind: 'attachment_ref', ref: p.a.uuid, status: 'none', why: lane === 'objective' ? 'body_budget' : 'steer_admission' });
156
+ push(i, p.form !== 'reference' ? { kind: 'attachment_ref', ref: p.a.uuid, status: 'full' } : { kind: 'attachment_ref', ref: p.a.uuid, status: 'none', why: p.refWhy ?? (lane === 'objective' ? 'body_budget' : 'steer_admission') });
150
157
  if (p.a.pixels !== undefined) {
151
158
  if (lane === 'steer')
152
159
  push(i, { kind: 'image', ref: p.a.uuid, status: 'none', why: 'lane_has_no_image_seat' });
@@ -238,7 +245,7 @@ function encodeObjective(v) {
238
245
  const placements = [];
239
246
  items.forEach((it, i) => {
240
247
  for (const a of it.attachments) {
241
- const p = { item: i, a, form: 'content', seated: false, seatWhy: undefined, entry: undefined };
248
+ const p = { item: i, a, form: 'content', seated: false, seatWhy: undefined, entry: undefined, refWhy: undefined };
242
249
  if (a.pixels !== undefined) {
243
250
  if (v.vision === 'no')
244
251
  p.seatWhy = 'vision_no';
@@ -250,10 +257,29 @@ function encodeObjective(v) {
250
257
  placements.push(p);
251
258
  }
252
259
  });
260
+ let pathSeated = 0;
261
+ for (const p of placements) {
262
+ const d = p.a.d;
263
+ if (d.kind !== 'path')
264
+ continue;
265
+ if (v.pathSeats === undefined) {
266
+ p.form = 'reference';
267
+ p.refWhy = 'path_not_admitted';
268
+ }
269
+ else if (pathSeated >= v.pathSeats) {
270
+ p.form = 'reference';
271
+ p.refWhy = 'path_seat_count';
272
+ }
273
+ else {
274
+ p.form = 'seat';
275
+ p.entry = d.entry;
276
+ pathSeated += 1;
277
+ }
278
+ }
253
279
  if (v.seat !== undefined) {
254
280
  let n = 0;
255
281
  for (const p of placements) {
256
- if (n >= v.seat.maxEntries)
282
+ if (pathSeated + n >= v.seat.maxEntries)
257
283
  break;
258
284
  const entry = seatEntryOf(p.a, v.seat.maxRenderedTextChars);
259
285
  if (entry === undefined)
@@ -264,7 +290,7 @@ function encodeObjective(v) {
264
290
  }
265
291
  if (n > 0 && compose(items, placements).trim() === '') {
266
292
  for (const p of placements) {
267
- if (p.form === 'seat') {
293
+ if (p.form === 'seat' && p.a.d.kind !== 'path') {
268
294
  p.form = 'content';
269
295
  p.entry = undefined;
270
296
  }
@@ -273,7 +299,7 @@ function encodeObjective(v) {
273
299
  }
274
300
  const ids = items.flatMap((it) => it.attachmentIds);
275
301
  for (;;) {
276
- const text = compose(items, placements);
302
+ const text = composeObjective(items, placements);
277
303
  const images = seatedImages(items, pastedWhy, placements);
278
304
  const seated = placements.filter((p) => p.form === 'seat');
279
305
  const entries = seated.map((p) => p.entry);
@@ -298,6 +324,8 @@ function encodeObjective(v) {
298
324
  placements.forEach((p, k) => {
299
325
  if (p.form === 'reference')
300
326
  return;
327
+ if (p.a.d.kind === 'path')
328
+ return;
301
329
  if (p.form === 'seat') {
302
330
  const cost = jsonBytes(p.entry) + 1;
303
331
  if (cost >= pickCost) {
@@ -306,7 +334,7 @@ function encodeObjective(v) {
306
334
  }
307
335
  return;
308
336
  }
309
- const cost = jsonBytes(textOf(p)) + (p.seated && p.a.pixels !== undefined ? jsonBytes({ data: p.a.pixels.data, mimeType: p.a.pixels.mimeType }) : 0);
337
+ const cost = jsonBytes(textOf(p, false)) + (p.seated && p.a.pixels !== undefined ? jsonBytes({ data: p.a.pixels.data, mimeType: p.a.pixels.mimeType }) : 0);
310
338
  if (cost >= pickCost) {
311
339
  pick = k;
312
340
  pickCost = cost;
@@ -321,8 +349,11 @@ function encodeSteer(v) {
321
349
  const { items } = v;
322
350
  const placements = [];
323
351
  items.forEach((it, i) => {
324
- for (const a of it.attachments)
325
- placements.push({ item: i, a, form: 'content', seated: false, seatWhy: undefined, entry: undefined });
352
+ for (const a of it.attachments) {
353
+ placements.push(a.d.kind === 'path'
354
+ ? { item: i, a, form: 'reference', seated: false, seatWhy: undefined, entry: undefined, refWhy: 'lane_has_no_path_seat' }
355
+ : { item: i, a, form: 'content', seated: false, seatWhy: undefined, entry: undefined, refWhy: undefined });
356
+ }
326
357
  });
327
358
  let text = compose(items, placements);
328
359
  let refusal = steerRefusal(text);
@@ -3,6 +3,7 @@ import type { UserAttachmentDescriptor, UserAttachmentMcpResourcePart, UserAttac
3
3
  import type { HumanTurnAttachment, HumanTurnEncoding, HumanTurnInput, HumanTurnMissing } from './humanTurn.js';
4
4
  import type { ImageVision } from './liveModelCatalog.js';
5
5
  import { type UserAttachmentSeatLimits } from './userAttachmentSeat.js';
6
+ import { type UserAttachmentPathEntry } from './attachmentPathPlan.js';
6
7
  export interface CellView {
7
8
  cellType: string | undefined;
8
9
  source: string | undefined;
@@ -70,6 +71,11 @@ export type DescriptorView = {
70
71
  name: string;
71
72
  parts: UserAttachmentMcpResourcePart[];
72
73
  truncated: boolean;
74
+ } | {
75
+ kind: 'path';
76
+ name: string;
77
+ path: string;
78
+ entry: UserAttachmentPathEntry;
73
79
  };
74
80
  export declare function readDescriptor(x: UserAttachmentDescriptor | HumanTurnAttachment['descriptor'], imageDelivery: 'required' | 'ignored'): DescriptorView | undefined;
75
81
  type Reason = Exclude<Extract<HumanTurnEncoding, {
@@ -106,6 +112,7 @@ export interface InputView {
106
112
  lane: 'objective' | 'steer';
107
113
  vision: ImageVision;
108
114
  seat: UserAttachmentSeatLimits | undefined;
115
+ pathSeats: number | undefined;
109
116
  otherBodyBytes: number;
110
117
  items: ItemView[];
111
118
  }