@sema-agent/core 1.293.0 → 1.295.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 (96) hide show
  1. package/dist/agents/subagent.d.ts.map +1 -1
  2. package/dist/agents/subagent.js +25 -3
  3. package/dist/agents/subagent.js.map +1 -1
  4. package/dist/bin/sema-tb.d.ts.map +1 -1
  5. package/dist/bin/sema-tb.js +7 -2
  6. package/dist/bin/sema-tb.js.map +1 -1
  7. package/dist/brain/anthropic.d.ts.map +1 -1
  8. package/dist/brain/anthropic.js +9 -3
  9. package/dist/brain/anthropic.js.map +1 -1
  10. package/dist/brain/openai.d.ts +1 -0
  11. package/dist/brain/openai.d.ts.map +1 -1
  12. package/dist/brain/openai.js +320 -298
  13. package/dist/brain/openai.js.map +1 -1
  14. package/dist/core/checkpoint-store.d.ts +14 -2
  15. package/dist/core/checkpoint-store.d.ts.map +1 -1
  16. package/dist/core/checkpoint-store.js.map +1 -1
  17. package/dist/core/fs-write-gate-policy.d.ts +1 -0
  18. package/dist/core/fs-write-gate-policy.d.ts.map +1 -1
  19. package/dist/core/fs-write-gate-policy.js +16 -0
  20. package/dist/core/fs-write-gate-policy.js.map +1 -1
  21. package/dist/core/hooks.d.ts +3 -3
  22. package/dist/core/hooks.d.ts.map +1 -1
  23. package/dist/core/hooks.js +11 -4
  24. package/dist/core/hooks.js.map +1 -1
  25. package/dist/core/present-plan-tool.d.ts.map +1 -1
  26. package/dist/core/present-plan-tool.js +12 -13
  27. package/dist/core/present-plan-tool.js.map +1 -1
  28. package/dist/core/runner/prepare-task.d.ts +17 -1
  29. package/dist/core/runner/prepare-task.d.ts.map +1 -1
  30. package/dist/core/runner/prepare-task.js +302 -20
  31. package/dist/core/runner/prepare-task.js.map +1 -1
  32. package/dist/core/runner/runtask.d.ts +2 -0
  33. package/dist/core/runner/runtask.d.ts.map +1 -1
  34. package/dist/core/runner/runtask.js +68 -10
  35. package/dist/core/runner/runtask.js.map +1 -1
  36. package/dist/core/sensitive-path-policy.d.ts +2 -1
  37. package/dist/core/sensitive-path-policy.d.ts.map +1 -1
  38. package/dist/core/sensitive-path-policy.js +22 -0
  39. package/dist/core/sensitive-path-policy.js.map +1 -1
  40. package/dist/core/task-registry.d.ts +4 -0
  41. package/dist/core/task-registry.d.ts.map +1 -1
  42. package/dist/core/task-registry.js +83 -15
  43. package/dist/core/task-registry.js.map +1 -1
  44. package/dist/core/tool-policy.d.ts +5 -2
  45. package/dist/core/tool-policy.d.ts.map +1 -1
  46. package/dist/core/tool-policy.js +9 -0
  47. package/dist/core/tool-policy.js.map +1 -1
  48. package/dist/core/types.d.ts +5 -0
  49. package/dist/core/types.d.ts.map +1 -1
  50. package/dist/engine/compaction/utils.d.ts.map +1 -1
  51. package/dist/engine/compaction/utils.js +3 -1
  52. package/dist/engine/compaction/utils.js.map +1 -1
  53. package/dist/engine/harness/messages.d.ts +2 -0
  54. package/dist/engine/harness/messages.d.ts.map +1 -1
  55. package/dist/engine/harness/messages.js +52 -3
  56. package/dist/engine/harness/messages.js.map +1 -1
  57. package/dist/engine/llm/types.d.ts.map +1 -1
  58. package/dist/engine/session/import-validate.d.ts.map +1 -1
  59. package/dist/engine/session/import-validate.js +64 -0
  60. package/dist/engine/session/import-validate.js.map +1 -1
  61. package/dist/index.d.ts +2 -2
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/orchestration/run-workflow-tool.d.ts +1 -0
  66. package/dist/orchestration/run-workflow-tool.d.ts.map +1 -1
  67. package/dist/orchestration/run-workflow-tool.js +3 -0
  68. package/dist/orchestration/run-workflow-tool.js.map +1 -1
  69. package/dist/orchestration/workflow.d.ts +1 -0
  70. package/dist/orchestration/workflow.d.ts.map +1 -1
  71. package/dist/orchestration/workflow.js +1 -0
  72. package/dist/orchestration/workflow.js.map +1 -1
  73. package/dist/scenarios/full-body.d.ts.map +1 -1
  74. package/dist/scenarios/full-body.js +3 -2
  75. package/dist/scenarios/full-body.js.map +1 -1
  76. package/dist/tools/fs/index.d.ts +4 -1
  77. package/dist/tools/fs/index.d.ts.map +1 -1
  78. package/dist/tools/fs/index.js +59 -20
  79. package/dist/tools/fs/index.js.map +1 -1
  80. package/dist/tools/fs/safety.d.ts +1 -1
  81. package/dist/tools/fs/safety.d.ts.map +1 -1
  82. package/dist/tools/fs/safety.js +3 -1
  83. package/dist/tools/fs/safety.js.map +1 -1
  84. package/dist/tools/fs/search.d.ts +7 -2
  85. package/dist/tools/fs/search.d.ts.map +1 -1
  86. package/dist/tools/fs/search.js +465 -57
  87. package/dist/tools/fs/search.js.map +1 -1
  88. package/dist/tools/monitor.d.ts.map +1 -1
  89. package/dist/tools/monitor.js +12 -1
  90. package/dist/tools/monitor.js.map +1 -1
  91. package/dist/tools/task-list.d.ts.map +1 -1
  92. package/dist/tools/task-list.js.map +1 -1
  93. package/dist/tools/worktree.d.ts.map +1 -1
  94. package/dist/tools/worktree.js +15 -2
  95. package/dist/tools/worktree.js.map +1 -1
  96. package/package.json +1 -1
@@ -28,6 +28,7 @@ import { reconcileInterruptedSession } from "../session-reconcile.js";
28
28
  import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-detector.js";
29
29
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
30
30
  import { defineTool } from "../tools.js";
31
+ import { canonicalToolName } from "../tool-name-aliases.js";
31
32
  import { pathToUri } from "../lsp-protocol.js";
32
33
  import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, makeReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
33
34
  import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, buildSkillsBlock, makeOutputTool, makeReportBlockedTool, makeReportFindingsTool, makeSkillTool, normalizeSkills } from "./synthetic-tools.js";
@@ -153,6 +154,16 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
153
154
  throw e;
154
155
  }
155
156
  }
157
+ if (spec.agents !== undefined && spec.agents.length > 0) {
158
+ const pool = spec.tools ?? [];
159
+ if (!pool.some((t) => typeof t.withAgents === "function")) {
160
+ const e = new Error(`TaskSpec.agents was provided but no delegation tool (createSubagentTool) is mounted in spec.tools — the per-task agents could never be offered. Mount the delegation tool, or drop spec.agents.`);
161
+ e.code = "config.agents.no_delegation_tool";
162
+ throw e;
163
+ }
164
+ const perTask = spec.agents;
165
+ spec = { ...spec, tools: pool.map((t) => (typeof t.withAgents === "function" ? t.withAgents(perTask) : t)) };
166
+ }
156
167
  const resolvedRole = resolveTaskModel(spec, deps);
157
168
  const model = resolvedRole.model;
158
169
  const thinking = spec.thinking ?? resolvedRole.thinking ?? model.defaultThinking;
@@ -524,6 +535,62 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
524
535
  };
525
536
  const subagentRetain = spec.retainSubagentSessions ? new SubagentRetainLedger(spec.retainSubagentSessions) : undefined;
526
537
  const worktreeIsolation = spec.handsReadOnly !== true ? createSubagentWorktreeHelper(executionEnv, taskRootPath) : undefined;
538
+ const liveInheritedGate = internals?.inheritedGate;
539
+ const seedInheritedGate = resume?.seed.inheritedGate;
540
+ const inheritedAncestorRules = (() => {
541
+ const seedRules = seedInheritedGate?.ancestorRules;
542
+ const liveRules = liveInheritedGate?.ancestorRules;
543
+ if (seedRules === undefined || seedRules.length === 0)
544
+ return liveRules ?? seedRules;
545
+ if (liveRules === undefined || liveRules.length === 0)
546
+ return seedRules;
547
+ const merged = seedRules.map((s) => {
548
+ const live = liveRules.find((l) => l.sessionId === s.sessionId && l.principal === s.principal);
549
+ return live !== undefined && live.rev >= s.rev ? live : s;
550
+ });
551
+ const extras = liveRules.filter((l) => !seedRules.some((s) => s.sessionId === l.sessionId && s.principal === l.principal));
552
+ return [...merged, ...extras];
553
+ })();
554
+ const shellGateRank = { off: 0, classify: 1, always: 2 };
555
+ const liveShellGate = liveInheritedGate?.shellGate;
556
+ const seedShellGate = seedInheritedGate?.shellGate;
557
+ const inheritedShellGate = liveShellGate === undefined
558
+ ? seedShellGate
559
+ : seedShellGate === undefined
560
+ ? liveShellGate
561
+ : shellGateRank[liveShellGate] >= shellGateRank[seedShellGate]
562
+ ? liveShellGate
563
+ : seedShellGate;
564
+ const inheritedParentConstraints = liveInheritedGate?.parentConstraints;
565
+ const specShellGate = spec.shellGate ?? "off";
566
+ const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
567
+ const ownSessionRulesRef = {};
568
+ const frozenOnAsk = spec.onAsk ?? deps.onAsk;
569
+ const inheritedGateForChildren = () => {
570
+ const ancestorRules = [
571
+ ...(inheritedAncestorRules ?? []),
572
+ ...(ownSessionRulesRef.current !== undefined ? [ownSessionRulesRef.current] : []),
573
+ ];
574
+ const ownCallerPolicy = spec.toolPolicy ?? deps.toolPolicy;
575
+ const durableMandate = runtimeCaps?.forceDurableGate === true || (spec.durableApproval !== undefined && frozenOnAsk === undefined);
576
+ const parentConstraints = [
577
+ ...(inheritedParentConstraints ?? []),
578
+ ...(ownCallerPolicy !== undefined
579
+ ? [
580
+ {
581
+ policy: ownCallerPolicy,
582
+ ...(frozenOnAsk !== undefined ? { onAsk: frozenOnAsk } : {}),
583
+ ...(durableMandate ? { durableMandate: true } : {}),
584
+ },
585
+ ]
586
+ : []),
587
+ ];
588
+ return {
589
+ ...(ancestorRules.length > 0 ? { ancestorRules } : {}),
590
+ ...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
591
+ ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
592
+ };
593
+ };
527
594
  let agentForkDenial;
528
595
  let observersActive = false;
529
596
  let autoModeDecider;
@@ -538,6 +605,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
538
605
  clientContext: spec.clientContext,
539
606
  parentCwd: taskRootPath,
540
607
  activeSkillScope: () => skillScope.active(),
608
+ inheritedGateForChildren,
541
609
  taskId: hostTaskId,
542
610
  sessionId,
543
611
  ...(spec.backgroundScope !== undefined ? { backgroundScope: spec.backgroundScope } : {}),
@@ -642,6 +710,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
642
710
  parentCwd: taskRootPath,
643
711
  parentThinking: () => harnessRef.current?.getThinkingLevel() ?? thinking,
644
712
  ...(forwardEvent ? { forwardEvent } : {}),
713
+ inheritedGateForChildren,
645
714
  }));
646
715
  }
647
716
  const suspendRef = {};
@@ -830,7 +899,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
830
899
  }
831
900
  }
832
901
  tools.push(...band.map((t) => firstPartyOffload(t)));
833
- const shellGate = spec.shellGate ?? "off";
902
+ const shellGate = effectiveShellGate;
834
903
  if (shellGate !== "off" && !(executionEnv instanceof StubExecutionEnv) && spec.handsReadOnly !== true) {
835
904
  shellGatedBash = !egressTools.has("Bash") && !irreversibilityTier.has("Bash");
836
905
  irreversibilityTier.set("Bash", shellGate === "always" ? "always" : "maybe");
@@ -1424,6 +1493,20 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1424
1493
  toolSearch = makeToolSearchTool({ registry, active: activeTools, rematerialize });
1425
1494
  harnessTools = buildToolList(activeTools);
1426
1495
  }
1496
+ if (spec.agents !== undefined && spec.agents.length > 0) {
1497
+ const known = new Set(tools.flatMap((t) => [canonicalToolName(t.name), ...(t.aliases ?? []).map((a) => canonicalToolName(a))]));
1498
+ for (const def of spec.agents) {
1499
+ for (const n of def.allowTools ?? []) {
1500
+ if (n === "*")
1501
+ continue;
1502
+ if (!known.has(canonicalToolName(n))) {
1503
+ const e = new Error(`TaskSpec.agents: agent "${def.name}" declares tool "${n}" in its allowed tools, but no such tool exists in this deployment — fix the agent's tools list or mount the tool.`);
1504
+ e.code = "config.agent.unknown_tool";
1505
+ throw e;
1506
+ }
1507
+ }
1508
+ }
1509
+ }
1427
1510
  const agentListingSpec = (spec.tools ?? []).find((t) => t.agentListing !== undefined && t.agentListing.length > 0);
1428
1511
  const agentListing = agentListingSpec
1429
1512
  ? { entries: agentListingSpec.agentListing, toolName: agentListingSpec.name, seedAnnounced: resume !== undefined }
@@ -1522,8 +1605,28 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1522
1605
  const rules = await deps.sessionPolicyStore.getRules(spec.sessionId, spec.principal);
1523
1606
  if (rules) {
1524
1607
  sessionRulePolicy = createSessionRulePolicy(rules, { env: executionEnv, rootPath: taskRootPath, toolEffects });
1608
+ ownSessionRulesRef.current = {
1609
+ sessionId: spec.sessionId,
1610
+ ...(spec.principal !== undefined ? { principal: spec.principal } : {}),
1611
+ rev: rules.rev,
1612
+ rules,
1613
+ };
1525
1614
  }
1526
1615
  }
1616
+ const ancestorRulePolicies = [];
1617
+ for (const snap of inheritedAncestorRules ?? []) {
1618
+ let ancestorRules = snap.rules;
1619
+ if (deps.sessionPolicyStore) {
1620
+ try {
1621
+ const live = await deps.sessionPolicyStore.getRules(snap.sessionId, snap.principal);
1622
+ if (live !== null && live.rev >= snap.rev)
1623
+ ancestorRules = live;
1624
+ }
1625
+ catch {
1626
+ }
1627
+ }
1628
+ ancestorRulePolicies.push(createSessionRulePolicy(ancestorRules, { env: executionEnv, rootPath: taskRootPath, toolEffects }));
1629
+ }
1527
1630
  const skillScopePolicy = hasSkillManifest
1528
1631
  ? createActiveSkillScopePolicy({
1529
1632
  scope: skillScope,
@@ -1532,12 +1635,177 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1532
1635
  toolEffects,
1533
1636
  })
1534
1637
  : undefined;
1535
- const narrowingLayers = [sessionRulePolicy, skillScopePolicy].filter((p) => p !== undefined);
1638
+ const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, skillScopePolicy].filter((p) => p !== undefined);
1536
1639
  const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
1537
- const policyLayers = policy !== undefined ? [...narrowingLayers, policy, ...narrowingLayers] : narrowingLayers;
1640
+ const sameInstanceAncestorCount = policy === undefined ? 0 : (inheritedParentConstraints ?? []).reduce((n, pc) => (pc.policy === policy ? n + 1 : n), 0);
1641
+ const sharedFirstDecision = new Map();
1642
+ const SHARED_FIRST_DECISION_CAP = 256;
1643
+ const callerPolicyLayer = policy !== undefined && sameInstanceAncestorCount > 0
1644
+ ? {
1645
+ check: async (creq, csignal) => {
1646
+ const decision = await policy.check(creq, csignal ?? abortController.signal);
1647
+ sharedFirstDecision.set(creq.toolCallId, decision);
1648
+ if (sharedFirstDecision.size > SHARED_FIRST_DECISION_CAP) {
1649
+ const oldest = sharedFirstDecision.keys().next().value;
1650
+ if (oldest !== undefined)
1651
+ sharedFirstDecision.delete(oldest);
1652
+ }
1653
+ return decision;
1654
+ },
1655
+ }
1656
+ : policy;
1657
+ const parentConstraintWrappers = (inheritedParentConstraints ?? []).map((pc) => policy !== undefined && pc.policy === policy
1658
+ ? {
1659
+ check: async (creq, csignal) => {
1660
+ const first = sharedFirstDecision.get(creq.toolCallId);
1661
+ if (first === undefined) {
1662
+ return {
1663
+ action: "deny",
1664
+ reason: `inherited parent policy could not be arbitrated for "${creq.toolName}" ` +
1665
+ `(shared-instance first decision unavailable); denied fail-closed`,
1666
+ };
1667
+ }
1668
+ if (first.action === "deny")
1669
+ return first;
1670
+ if (first.action === "allow")
1671
+ return { action: "allow" };
1672
+ if (pc.durableMandate === true) {
1673
+ return {
1674
+ action: "deny",
1675
+ reason: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
1676
+ `ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
1677
+ };
1678
+ }
1679
+ const presentedArgs = creq.args;
1680
+ const resolved = await resolveAsk({
1681
+ toolName: creq.toolName,
1682
+ toolCallId: creq.toolCallId,
1683
+ args: presentedArgs,
1684
+ message: first.message ?? first.reason ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
1685
+ principal: spec.principal,
1686
+ sourceTaskId: sessionId,
1687
+ }, pc.onAsk, csignal ?? abortController.signal);
1688
+ return resolved.action === "allow" ? { action: "allow" } : resolved;
1689
+ },
1690
+ }
1691
+ : {
1692
+ check: async (creq, csignal) => {
1693
+ let decision;
1694
+ try {
1695
+ decision = await pc.policy.check(creq, csignal ?? abortController.signal);
1696
+ }
1697
+ catch (err) {
1698
+ return {
1699
+ action: "deny",
1700
+ reason: `inherited parent policy errored for "${creq.toolName}": ${err instanceof Error ? err.message : String(err)}`,
1701
+ };
1702
+ }
1703
+ if (decision.action !== "ask")
1704
+ return decision;
1705
+ if (pc.durableMandate === true) {
1706
+ return {
1707
+ action: "deny",
1708
+ reason: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
1709
+ `ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
1710
+ };
1711
+ }
1712
+ const presentedArgs = decision.updatedInput !== undefined ? decision.updatedInput : creq.args;
1713
+ const resolved = await resolveAsk({
1714
+ toolName: creq.toolName,
1715
+ toolCallId: creq.toolCallId,
1716
+ args: presentedArgs,
1717
+ message: decision.message ?? decision.reason ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
1718
+ principal: spec.principal,
1719
+ sourceTaskId: sessionId,
1720
+ }, pc.onAsk, csignal ?? abortController.signal);
1721
+ return resolved.action === "allow" && decision.updatedInput !== undefined
1722
+ ? { ...resolved, updatedInput: decision.updatedInput }
1723
+ : resolved;
1724
+ },
1725
+ });
1726
+ const rewriteCapableLayers = policy !== undefined || parentConstraintWrappers.length > 0;
1727
+ const rewriteEmitterIndex = new Map();
1728
+ const rewriteCapableChain = [
1729
+ ...(callerPolicyLayer !== undefined ? [callerPolicyLayer] : []),
1730
+ ...parentConstraintWrappers,
1731
+ ];
1732
+ const callerSlotOffset = callerPolicyLayer !== undefined ? 1 : 0;
1733
+ const sameInstanceAdapterIndices = new Set();
1734
+ (inheritedParentConstraints ?? []).forEach((pc, j) => {
1735
+ if (policy !== undefined && pc.policy === policy)
1736
+ sameInstanceAdapterIndices.add(j + callerSlotOffset);
1737
+ });
1738
+ const instrumentedChain = rewriteCapableChain.map((layer, idx) => ({
1739
+ check: async (creq, csignal) => {
1740
+ const d = await layer.check(creq, csignal);
1741
+ if (d.updatedInput !== undefined)
1742
+ rewriteEmitterIndex.set(creq.toolCallId, idx);
1743
+ return d;
1744
+ },
1745
+ }));
1746
+ const policyLayers = [
1747
+ ...narrowingLayers,
1748
+ ...instrumentedChain,
1749
+ ...(rewriteCapableLayers ? narrowingLayers : []),
1750
+ ];
1538
1751
  const integrityGuardLayers = [createTranscriptIntegrityPolicy(), createUnverifiableDeletePolicy()];
1539
- const guardedPolicyLayers = policy !== undefined ? [...integrityGuardLayers, ...policyLayers, ...integrityGuardLayers] : [...integrityGuardLayers, ...policyLayers];
1540
- const effectivePolicy = guardedPolicyLayers.length === 1 ? guardedPolicyLayers[0] : combinePolicies(...guardedPolicyLayers);
1752
+ const guardedPolicyLayers = rewriteCapableLayers
1753
+ ? [...integrityGuardLayers, ...policyLayers, ...integrityGuardLayers]
1754
+ : [...integrityGuardLayers, ...policyLayers];
1755
+ const foldedPolicy = guardedPolicyLayers.length === 1 ? guardedPolicyLayers[0] : combinePolicies(...guardedPolicyLayers);
1756
+ const effectivePolicy = foldedPolicy !== undefined && rewriteCapableLayers
1757
+ ? {
1758
+ check: async (req, signal) => {
1759
+ let decision;
1760
+ try {
1761
+ decision = await foldedPolicy.check(req, signal);
1762
+ }
1763
+ catch (err) {
1764
+ rewriteEmitterIndex.delete(req.toolCallId);
1765
+ throw err;
1766
+ }
1767
+ const emitterIdx = rewriteEmitterIndex.get(req.toolCallId);
1768
+ rewriteEmitterIndex.delete(req.toolCallId);
1769
+ if (decision.action === "deny" || decision.updatedInput === undefined)
1770
+ return decision;
1771
+ const recheckPrefix = rewriteCapableChain.slice(0, emitterIdx ?? rewriteCapableChain.length);
1772
+ const postEmitterSameInstanceAdapters = [];
1773
+ if (emitterIdx !== undefined && emitterIdx > 0) {
1774
+ for (let i = emitterIdx + 1; i < rewriteCapableChain.length; i++) {
1775
+ if (sameInstanceAdapterIndices.has(i))
1776
+ postEmitterSameInstanceAdapters.push(rewriteCapableChain[i]);
1777
+ }
1778
+ }
1779
+ const recheckLayers = [...recheckPrefix, ...postEmitterSameInstanceAdapters];
1780
+ const finalReq = { ...req, args: decision.updatedInput };
1781
+ let asked;
1782
+ try {
1783
+ for (const layer of recheckLayers) {
1784
+ const d = await layer.check(finalReq, signal);
1785
+ if (d.action === "deny") {
1786
+ return { ...d, updatedInput: decision.updatedInput };
1787
+ }
1788
+ if (d.updatedInput !== undefined) {
1789
+ return {
1790
+ action: "deny",
1791
+ message: "policy required a rewrite during final-form re-check — the arguments that would execute were not accepted as-is (fail-closed; rewrite chains are not iterated to a fixpoint)",
1792
+ updatedInput: decision.updatedInput,
1793
+ };
1794
+ }
1795
+ if (d.action === "ask" && asked === undefined)
1796
+ asked = d;
1797
+ }
1798
+ }
1799
+ finally {
1800
+ rewriteEmitterIndex.delete(req.toolCallId);
1801
+ }
1802
+ if (asked !== undefined && decision.action === "allow") {
1803
+ return { ...asked, updatedInput: decision.updatedInput };
1804
+ }
1805
+ return decision;
1806
+ },
1807
+ }
1808
+ : foldedPolicy;
1541
1809
  const hooks = spec.hooks ?? deps.hooks;
1542
1810
  const onAsk = spec.onAsk ?? deps.onAsk;
1543
1811
  const handWriteTools = handsEnabled
@@ -1607,16 +1875,18 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1607
1875
  sourceTaskId: sessionId,
1608
1876
  }, onAsk, abortController.signal);
1609
1877
  const waitMs = Math.max(0, now() - t0);
1610
- humanReviewRef.count += 1;
1611
- humanReviewRef.totalWaitMs += waitMs;
1612
- const toolArg = primaryActivityArg(req.args);
1613
- humanReviewRef.gates.push({
1614
- kind: "human",
1615
- waitMs,
1616
- decision: resolved.action === "allow" ? "allow" : "deny",
1617
- toolName: req.toolName,
1618
- ...(toolArg !== undefined ? { toolArg } : {}),
1619
- });
1878
+ if (resolved.approverUnavailable !== true) {
1879
+ humanReviewRef.count += 1;
1880
+ humanReviewRef.totalWaitMs += waitMs;
1881
+ const toolArg = primaryActivityArg(req.args);
1882
+ humanReviewRef.gates.push({
1883
+ kind: "human",
1884
+ waitMs,
1885
+ decision: resolved.action === "allow" ? "allow" : "deny",
1886
+ toolName: req.toolName,
1887
+ ...(toolArg !== undefined ? { toolArg } : {}),
1888
+ });
1889
+ }
1620
1890
  return resolved;
1621
1891
  };
1622
1892
  const hooksWithPermissionDenied = hooks?.permissionDenied ? hooks : undefined;
@@ -1659,6 +1929,18 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1659
1929
  return live.length > 0 ? live : undefined;
1660
1930
  })(),
1661
1931
  pendingSteer: undefined,
1932
+ inheritedGate: (() => {
1933
+ const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
1934
+ const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
1935
+ return inheritedAncestorRules !== undefined || inheritedShellGate !== undefined || requiresParentConstraint
1936
+ ? {
1937
+ ...(inheritedAncestorRules !== undefined ? { ancestorRules: structuredClone(inheritedAncestorRules) } : {}),
1938
+ ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
1939
+ requiresParentConstraint,
1940
+ ...(requiresParentConstraint && parentConstraintCount !== undefined ? { parentConstraintCount } : {}),
1941
+ }
1942
+ : undefined;
1943
+ })(),
1662
1944
  });
1663
1945
  const commitSuspendSaga = async (token, cp, remoteEnv, remoteHandle) => {
1664
1946
  if (!checkpointStore)
@@ -1740,7 +2022,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1740
2022
  remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
1741
2023
  }
1742
2024
  else if (parkOnlyRemoteEnv !== undefined) {
1743
- if (hasBackgroundShell(parkOnlyRemoteEnv))
2025
+ if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
1744
2026
  await parkOnlyRemoteEnv.disposeBackgroundShells();
1745
2027
  remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
1746
2028
  }
@@ -1808,7 +2090,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1808
2090
  remoteHandle = { ...suspendableEnv.workspaceHandle(), snapshotId: snap.value };
1809
2091
  }
1810
2092
  else if (parkOnlyRemoteEnv !== undefined) {
1811
- if (hasBackgroundShell(parkOnlyRemoteEnv))
2093
+ if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
1812
2094
  await parkOnlyRemoteEnv.disposeBackgroundShells();
1813
2095
  remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
1814
2096
  }
@@ -1851,8 +2133,8 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1851
2133
  }
1852
2134
  : undefined;
1853
2135
  const suspendAsk = checkpointStore && (durableApproval || irreversibleTools.size > 0 || egressTools.size > 0)
1854
- ? async (req, postHookArgs, safety) => {
1855
- if (safety === undefined && durableApproval === undefined && onAsk !== undefined) {
2136
+ ? async (req, postHookArgs, safety, approverUnavailable) => {
2137
+ if (safety === undefined && onAsk !== undefined && runtimeCaps?.forceDurableGate !== true && approverUnavailable !== true) {
1856
2138
  return undefined;
1857
2139
  }
1858
2140
  let token;
@@ -1893,7 +2175,7 @@ export async function prepareTask(spec, deps, sessions, resume, _memorySelector,
1893
2175
  remoteHandle = { ...remoteEnv.workspaceHandle(), snapshotId: snap.value };
1894
2176
  }
1895
2177
  else if (parkOnlyRemoteEnv !== undefined) {
1896
- if (hasBackgroundShell(parkOnlyRemoteEnv))
2178
+ if (hasBackgroundShell(parkOnlyRemoteEnv) && spec.retainBackgroundProcesses !== true)
1897
2179
  await parkOnlyRemoteEnv.disposeBackgroundShells();
1898
2180
  remoteHandle = parkOnlyHandle(parkOnlyRemoteEnv);
1899
2181
  }