@sema-agent/core 5.11.0 → 5.13.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 (41) hide show
  1. package/CHANGELOG.md +52 -0
  2. package/dist/agents/subagent.js +4 -2
  3. package/dist/core/auto-compaction.d.ts +12 -1
  4. package/dist/core/auto-compaction.js +3 -1
  5. package/dist/core/checkpoint-store.d.ts +8 -1
  6. package/dist/core/checkpoint-store.js +3 -1
  7. package/dist/core/compliance.d.ts +11 -0
  8. package/dist/core/compliance.js +34 -0
  9. package/dist/core/exec-gate.js +12 -1
  10. package/dist/core/governance-codes.d.ts +12 -0
  11. package/dist/core/governance-codes.js +24 -0
  12. package/dist/core/locked-config.d.ts +27 -0
  13. package/dist/core/locked-config.js +42 -0
  14. package/dist/core/memory-admission.d.ts +47 -0
  15. package/dist/core/memory-admission.js +156 -0
  16. package/dist/core/memory.d.ts +2 -0
  17. package/dist/core/memory.js +3 -2
  18. package/dist/core/retention.d.ts +36 -0
  19. package/dist/core/retention.js +31 -0
  20. package/dist/core/runner/assemble-result.js +1 -1
  21. package/dist/core/runner/compaction-call-options.d.ts +13 -1
  22. package/dist/core/runner/compaction-call-options.js +85 -0
  23. package/dist/core/runner/prepare-memory.d.ts +9 -0
  24. package/dist/core/runner/prepare-memory.js +28 -2
  25. package/dist/core/runner/prepare-task.d.ts +4 -0
  26. package/dist/core/runner/prepare-task.js +183 -26
  27. package/dist/core/runner/runtask.js +101 -47
  28. package/dist/core/runner/turn-attachments.js +3 -1
  29. package/dist/core/session-store.d.ts +1 -0
  30. package/dist/core/session-store.js +1 -0
  31. package/dist/core/tool-result-store.d.ts +2 -0
  32. package/dist/core/tool-result-store.js +1 -0
  33. package/dist/core/types.d.ts +12 -2
  34. package/dist/engine/compaction/compaction.d.ts +11 -2
  35. package/dist/engine/compaction/compaction.js +87 -9
  36. package/dist/engine/harness/agent-harness.js +11 -1
  37. package/dist/engine/llm/validation.js +11 -1
  38. package/dist/index.d.ts +7 -2
  39. package/dist/index.js +6 -1
  40. package/dist/prompt-assembly/event-registry.js +1 -1
  41. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { resolve as resolveFsPath } from "node:path";
4
4
  import { AgentHarness, DEFAULT_CHARS_PER_TOKEN, DEFAULT_CLAMP_TOLERANCE, DEFAULT_COMPACTION_SETTINGS, estimateContextTokens, readCompactionActiveTools, summaryOutputBudgetTokens } from "../../internal/harness.js";
5
5
  const PROMPT_HASH_SALT = randomBytes(16);
6
6
  import { sanitizeCompactionSettings } from "../auto-compaction.js";
7
+ import { projectStaleToolResults, resolveStaleToolResultOffload } from "./compaction-call-options.js";
7
8
  import { createAutoModeDecider } from "../auto-mode.js";
8
9
  import { buildAutoModePrompt, renderAutoModeAction, renderAutoModeWindow } from "../auto-mode-prompt.js";
9
10
  import { resolveModel, resolveTaskModel, roleModelIfSet } from "../roles.js";
@@ -37,6 +38,10 @@ import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, S
37
38
  import { compileOutputSchema } from "./strict-output-schema.js";
38
39
  import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, } from "./tool-disclosure.js";
39
40
  import { composeMemoryBlock } from "../memory.js";
41
+ import { preflightLockedConfig } from "../locked-config.js";
42
+ import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
43
+ import { assertRetentionCapability } from "../retention.js";
44
+ import { foldAdmissionFreeze } from "../memory-admission.js";
40
45
  import { prepareMemory } from "./prepare-memory.js";
41
46
  import { defaultPromptProvider, buildEnvironmentContext, buildGitSnapshot, formatLocalDate, isValidTimeZone, PROJECT_CONTEXT_FRAMING } from "../../prompts/default.js";
42
47
  import { assemblePrompt } from "../../prompt-assembly/assemble.js";
@@ -68,7 +73,7 @@ import { resolveWorkflowSizeGuideline } from "../../orchestration/workflow-size-
68
73
  import { createLspTool, gitCheckIgnoreFilter, resolveLspPath } from "../lsp.js";
69
74
  import { resolveKey } from "../../tools/fs/safety.js";
70
75
  import { wholeFileRecordsFromTranscript } from "./session-file-state-replay.js";
71
- import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
76
+ import { BINDING_CHECKPOINT_VERSION, mintCheckpointToken, ORG_ADMISSION_CHECKPOINT_VERSION, RESOURCE_CHECKPOINT_VERSION, TOKEN_CHECKPOINT_VERSION, buildRiskDescriptor, debitLedger, remainingBudgetMicroUsd, } from "../checkpoint-store.js";
72
77
  import { boundInputHashOf } from "../canonical-json.js";
73
78
  import { GLOBAL_USAGE_KEY, resolveUsageWindows, usageRetryAfterMs } from "../usage-window-store.js";
74
79
  const RECONCILE_MAX_RETRIES = 3;
@@ -336,6 +341,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
336
341
  e.code = "config.empty_objective";
337
342
  throw e;
338
343
  }
344
+ const lockedPreflight = preflightLockedConfig(spec, deps);
345
+ assertRetentionCapability({
346
+ policy: deps.retentionPolicy,
347
+ locked: lockedPreflight.lockedKeys.has("retentionPolicy"),
348
+ stores: [
349
+ { name: "sessionStore", store: sessions },
350
+ { name: "checkpointStore", store: resolveCheckpointStore(spec, deps) },
351
+ { name: "toolResultStore", store: deps.toolResultStore },
352
+ ],
353
+ });
339
354
  resolveTaskLimits(spec.limits);
340
355
  if (spec.resourceSuspend !== undefined) {
341
356
  const rsus = spec.resourceSuspend;
@@ -939,9 +954,24 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
939
954
  ? liveShellGate
940
955
  : seedShellGate;
941
956
  const inheritedParentConstraints = liveInheritedGate?.parentConstraints;
957
+ const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
958
+ const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
959
+ const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
960
+ const inheritedOrgGoverned = liveInheritedGate?.orgAdmissionGoverned === true || seedInheritedGate?.orgAdmissionGoverned === true;
961
+ const priorOwnOrgVerdict = resume?.seed.inheritedGate?.ownAdmittedOrgScopes !== undefined
962
+ ? { scopes: resume.seed.inheritedGate.ownAdmittedOrgScopes, writeScope: resume.seed.inheritedGate.ownAdmittedOrgWriteScope ?? null }
963
+ : undefined;
964
+ const orgGovernedProvenance = deps.memoryScopeAdmission !== undefined ||
965
+ (deps.deploymentMemoryScopes !== undefined && deps.deploymentMemoryScopes.length > 0) ||
966
+ deps.compliancePostureResolver !== undefined ||
967
+ inheritedOrgGoverned ||
968
+ priorOwnOrgVerdict !== undefined;
942
969
  const specShellGate = spec.shellGate ?? "off";
943
970
  const effectiveShellGate = inheritedShellGate !== undefined && shellGateRank[inheritedShellGate] > shellGateRank[specShellGate] ? inheritedShellGate : specShellGate;
944
971
  const ownSessionRulesRef = {};
972
+ const memoryAdmittedOrgScopesRef = { current: [] };
973
+ const ownOrgVerdictRef = { current: undefined };
974
+ const orgAdmissionCheckpointState = () => inheritedAdmittedOrgScopes !== undefined || ownOrgVerdictRef.current !== undefined || orgGovernedProvenance;
945
975
  const frozenOnAsk = spec.onAsk ?? deps.onAsk;
946
976
  const frozenOnQuestion = spec.onQuestion ?? deps.onQuestion;
947
977
  const inheritedGateForChildren = () => {
@@ -949,7 +979,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
949
979
  ...(inheritedAncestorRules ?? []),
950
980
  ...(ownSessionRulesRef.current !== undefined ? [ownSessionRulesRef.current] : []),
951
981
  ];
952
- const ownCallerPolicy = spec.toolPolicy ?? deps.toolPolicy;
982
+ const ownCallerPolicy = lockedPreflight.toolPolicy;
953
983
  const durableMandate = runtimeCaps?.forceDurableGate === true || (spec.durableApproval !== undefined && frozenOnAsk === undefined);
954
984
  const parentConstraints = [
955
985
  ...(inheritedParentConstraints ?? []),
@@ -967,6 +997,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
967
997
  ...(ancestorRules.length > 0 ? { ancestorRules } : {}),
968
998
  ...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
969
999
  ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
1000
+ admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
1001
+ ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
970
1002
  };
971
1003
  };
972
1004
  let agentForkDenial;
@@ -1071,6 +1103,39 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1071
1103
  runtimeCaps = { allowWorkflows: false, allowFork: false };
1072
1104
  }
1073
1105
  }
1106
+ let complianceDenies = new Set();
1107
+ let complianceDegraded = false;
1108
+ if (deps.compliancePostureResolver) {
1109
+ try {
1110
+ const posture = (await deps.compliancePostureResolver(spec.principal)) ?? undefined;
1111
+ if (posture !== undefined)
1112
+ complianceDenies = resolveComplianceDenies(posture);
1113
+ }
1114
+ catch (err) {
1115
+ deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId });
1116
+ complianceDenies = new Set(COMPLIANCE_CAPABILITIES);
1117
+ complianceDegraded = true;
1118
+ }
1119
+ const complianceRefuse = (capability, requested) => {
1120
+ const e = new Error(`${requested} is denied for this principal by the compliance posture (capability "${capability}"` +
1121
+ `${complianceDegraded ? "; the posture resolver is currently failing, so every managed capability is denied fail-closed" : ""}) — ` +
1122
+ `the task is refused rather than silently narrowed.`);
1123
+ e.code = complianceDegraded ? "config.compliance_required" : "config.compliance_denied";
1124
+ throw e;
1125
+ };
1126
+ if (complianceDenies.has("mcp_servers") && lockedPreflight.mcp?.length) {
1127
+ complianceRefuse("mcp_servers", "TaskSpec.mcp (MCP server materialization)");
1128
+ }
1129
+ if (complianceDenies.has("workflows") && spec.selfOrchestration === true) {
1130
+ complianceRefuse("workflows", "TaskSpec.selfOrchestration (workflow self-orchestration)");
1131
+ }
1132
+ if (complianceDenies.has("web_fetch")) {
1133
+ const webTool = (spec.tools ?? []).find((t) => t.name === WEB_FETCH_TOOL_NAME || (t.aliases ?? []).includes(WEB_FETCH_TOOL_NAME));
1134
+ if (webTool !== undefined) {
1135
+ complianceRefuse("web_fetch", `TaskSpec.tools["${webTool.name}"] (the WebFetch tool face)`);
1136
+ }
1137
+ }
1138
+ }
1074
1139
  agentForkDenial = forkGovernanceDenial(spec.enableFork, runtimeCaps?.allowFork);
1075
1140
  observersActive = runtimeCaps?.allowObservers === true;
1076
1141
  if (runtimeCaps?.autoMode === true && deps.autoMode !== undefined) {
@@ -1219,8 +1284,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1219
1284
  }
1220
1285
  tools.push(createOutputTool(outputRef, spec.outputSchema, compiled.strict ? compiled.modelSchema : undefined));
1221
1286
  }
1222
- mcp = spec.mcp?.length
1223
- ? await materializeMcpTools(spec.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer)
1287
+ mcp = lockedPreflight.mcp?.length
1288
+ ? await materializeMcpTools(lockedPreflight.mcp, spec.principal, deps.onElicit, deps.mcpImageResizer)
1224
1289
  : { tools: [], toolAxes: [], warnings: [], serverInstructions: [], instructionsDelta: { pendingAdds: [], pendingRemovals: [] }, droppedTools: [], statuses: [], refresh: async () => [], dispose: async () => { } };
1225
1290
  for (const w of mcp.warnings)
1226
1291
  deps.onError?.(w, { phase: "mcp", sessionId });
@@ -1240,7 +1305,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1240
1305
  tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
1241
1306
  const rebuildHarnessToolsRef = {};
1242
1307
  const toolCallGateArmedRef = { armed: false };
1243
- if (spec.mcp?.length) {
1308
+ if (lockedPreflight.mcp?.length) {
1244
1309
  tools.push({
1245
1310
  name: "RefreshMcpTools",
1246
1311
  label: "RefreshMcpTools",
@@ -1631,7 +1696,31 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1631
1696
  .catch(() => undefined);
1632
1697
  }
1633
1698
  : undefined;
1634
- const { memoryEngineSession, memoryBlock: memoryBlockFromEngine, seedFiles: memorySeedFiles } = await prepareMemory({ spec, deps, sessionId, taskRootPath, memoryWriteGateRef });
1699
+ const { memoryEngineSession, memoryBlock: memoryBlockFromEngine, seedFiles: memorySeedFiles, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, } = await prepareMemory({
1700
+ spec,
1701
+ deps,
1702
+ sessionId,
1703
+ taskRootPath,
1704
+ memoryWriteGateRef,
1705
+ admissionCtx: {
1706
+ orgMemoryDenied: complianceDenies.has("org_memory_mount"),
1707
+ complianceDegraded,
1708
+ parentAdmittedOrgScopes: foldAdmissionFreeze({
1709
+ delegated: internals?.isDelegatedChild === true ||
1710
+ internals?.inheritedGate !== undefined ||
1711
+ (resume?.seed.inheritedGate !== undefined &&
1712
+ (resume.seed.inheritedGate.ancestorRules !== undefined ||
1713
+ resume.seed.inheritedGate.shellGate !== undefined ||
1714
+ resume.seed.inheritedGate.admittedOrgScopes !== undefined ||
1715
+ resume.seed.inheritedGate.requiresParentConstraint)),
1716
+ inherited: inheritedAdmittedOrgScopes,
1717
+ }),
1718
+ priorOwnVerdict: priorOwnOrgVerdict,
1719
+ governedProvenance: orgGovernedProvenance,
1720
+ },
1721
+ });
1722
+ memoryAdmittedOrgScopesRef.current = memoryAdmittedOrgScopes;
1723
+ ownOrgVerdictRef.current = memoryOwnOrgVerdict ?? priorOwnOrgVerdict;
1635
1724
  let memoryBlock = memoryBlockFromEngine;
1636
1725
  if (memorySeedFiles?.length && seedContextFiles) {
1637
1726
  try {
@@ -1736,7 +1825,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1736
1825
  { entries: [], seedAnnounced: true }
1737
1826
  : undefined;
1738
1827
  const provider = spec.promptProvider ?? deps.promptProvider ?? defaultPromptProvider;
1739
- const promptPolicyEnabled = Boolean(spec.toolPolicy ?? deps.toolPolicy);
1828
+ const promptPolicyEnabled = Boolean(lockedPreflight.toolPolicy);
1740
1829
  const promptHooks = spec.hooks ?? deps.hooks;
1741
1830
  const promptHooksEnabled = Boolean(promptHooks?.preToolUse || promptHooks?.postToolUse);
1742
1831
  const failClosedReason = selfOrchestrationFailClosedReason(spec, deps);
@@ -2275,6 +2364,34 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2275
2364
  const dateChange = renderWithDate
2276
2365
  ? { legDate: envFacts.date, today: () => formatLocalDate(new Date(), tzValid ? userTz : undefined) }
2277
2366
  : undefined;
2367
+ const lastBrainContextRef = {};
2368
+ const requestLossyRef = { current: false };
2369
+ const staleOffloadWrittenRefs = new Set();
2370
+ const lastBrainContext = () => lastBrainContextRef.current;
2371
+ const recordBrainContext = (servedModelId, c) => {
2372
+ if (requestLossyRef.current) {
2373
+ lastBrainContextRef.current = undefined;
2374
+ return;
2375
+ }
2376
+ lastBrainContextRef.current = {
2377
+ ...(c.systemPrompt !== undefined ? { systemPrompt: c.systemPrompt } : {}),
2378
+ ...(c.systemBlocks !== undefined ? { systemBlocks: [...c.systemBlocks] } : {}),
2379
+ messages: [...c.messages],
2380
+ ...(c.tools !== undefined ? { tools: [...c.tools] } : {}),
2381
+ modelId: servedModelId,
2382
+ };
2383
+ };
2384
+ const staleOffloadCfg = resolveStaleToolResultOffload(spec.compaction?.staleToolResultOffload);
2385
+ if (staleOffloadCfg !== undefined && offloadStore === undefined) {
2386
+ deps.onError?.(new Error("compaction.staleToolResultOffload is set but the tool-result offload store is disabled (toolResultThresholdChars ≤ 0/∞) — the knob is inert this run; re-enable offloading or drop the knob"), { phase: "config", sessionId });
2387
+ }
2388
+ const staleOffload = staleOffloadCfg !== undefined && offloadStore !== undefined ? { cfg: staleOffloadCfg, store: offloadStore } : undefined;
2389
+ const guardedBrain = brainCallGuardrailMs === undefined
2390
+ ? deps.brain
2391
+ : {
2392
+ ...deps.brain,
2393
+ stream: withBrainCallGuardrail((m, c, o) => deps.brain.stream(m, c, o), brainCallGuardrailMs, brainCallGuardrailRef),
2394
+ };
2278
2395
  const harness = new AgentHarness({
2279
2396
  abortResultDetails: () => suspendRef.token !== undefined || reviewRef.token !== undefined ? { code: "gate.parked" } : undefined,
2280
2397
  ...(spec.limits?.maxOutputTokens !== undefined && spec.limits.maxOutputTokens > 0
@@ -2299,7 +2416,6 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2299
2416
  }));
2300
2417
  },
2301
2418
  ...(spec.streamingToolExecution === true ? { streamingToolExecution: true } : {}),
2302
- followUpMode: "all",
2303
2419
  env: executionEnv,
2304
2420
  session,
2305
2421
  tools: harnessTools,
@@ -2308,12 +2424,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2308
2424
  systemPrompt,
2309
2425
  ...(assembled.systemBlocks ? { systemBlocks: assembled.systemBlocks } : {}),
2310
2426
  getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
2311
- runtime: brainToRuntime(brainCallGuardrailMs === undefined
2312
- ? deps.brain
2313
- : {
2314
- ...deps.brain,
2315
- stream: withBrainCallGuardrail((m, c, o) => deps.brain.stream(m, c, o), brainCallGuardrailMs, brainCallGuardrailRef),
2316
- }),
2427
+ runtime: brainToRuntime({
2428
+ ...guardedBrain,
2429
+ stream: (m, c, o) => {
2430
+ if (staleOffload === undefined) {
2431
+ recordBrainContext(m.id, c);
2432
+ return guardedBrain.stream(m, c, o);
2433
+ }
2434
+ return (async () => {
2435
+ const projected = await projectStaleToolResults(c, staleOffload.cfg, staleOffload.store, sessionId, staleOffloadWrittenRefs);
2436
+ recordBrainContext(m.id, projected);
2437
+ return await guardedBrain.stream(m, projected, o);
2438
+ })();
2439
+ },
2440
+ }),
2317
2441
  });
2318
2442
  harnessRef.current = harness;
2319
2443
  let releaseSignal = () => undefined;
@@ -2331,7 +2455,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2331
2455
  releaseSignal = () => sig.removeEventListener("abort", onExternalAbort);
2332
2456
  }
2333
2457
  }
2334
- const policy = spec.toolPolicy ?? deps.toolPolicy;
2458
+ const policy = lockedPreflight.toolPolicy;
2335
2459
  const auditPolicyNames = (auditee) => {
2336
2460
  const nameGroups = toolPolicyNameSets(auditee);
2337
2461
  if (nameGroups.length > 0) {
@@ -2419,7 +2543,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2419
2543
  for (const layer of narrowingLayers)
2420
2544
  auditPolicyNames(layer);
2421
2545
  const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
2422
- const basePolicyForResumeEdit = spec.toolPolicy ?? deps.toolPolicy;
2546
+ const basePolicyForResumeEdit = lockedPreflight.toolPolicy;
2423
2547
  const sameInstanceAncestorCount = policy === undefined ? 0 : (inheritedParentConstraints ?? []).reduce((n, pc) => (pc.policy === policy ? n + 1 : n), 0);
2424
2548
  const sharedFirstDecision = new Map();
2425
2549
  const SHARED_FIRST_DECISION_CAP = 256;
@@ -2957,10 +3081,20 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2957
3081
  inheritedGate: (() => {
2958
3082
  const requiresParentConstraint = (inheritedParentConstraints?.length ?? 0) > 0 || seedInheritedGate?.requiresParentConstraint === true;
2959
3083
  const parentConstraintCount = (inheritedParentConstraints?.length ?? 0) > 0 ? inheritedParentConstraints.length : seedInheritedGate?.parentConstraintCount;
2960
- return inheritedAncestorRules !== undefined || inheritedShellGate !== undefined || requiresParentConstraint
3084
+ return inheritedAncestorRules !== undefined ||
3085
+ inheritedShellGate !== undefined ||
3086
+ inheritedAdmittedOrgScopes !== undefined ||
3087
+ ownOrgVerdictRef.current !== undefined ||
3088
+ orgGovernedProvenance ||
3089
+ requiresParentConstraint
2961
3090
  ? {
2962
3091
  ...(inheritedAncestorRules !== undefined ? { ancestorRules: structuredClone(inheritedAncestorRules) } : {}),
2963
3092
  ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
3093
+ ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
3094
+ ...(ownOrgVerdictRef.current !== undefined
3095
+ ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
3096
+ : {}),
3097
+ ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
2964
3098
  requiresParentConstraint,
2965
3099
  ...(requiresParentConstraint && parentConstraintCount !== undefined ? { parentConstraintCount } : {}),
2966
3100
  }
@@ -3075,7 +3209,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3075
3209
  const mintedAt = Date.now();
3076
3210
  const cp = {
3077
3211
  token,
3078
- version: resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
3212
+ version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : resourceLedgerOut.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
3079
3213
  scope,
3080
3214
  sessionId,
3081
3215
  leafId,
@@ -3157,7 +3291,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3157
3291
  const mintedAt = Date.now();
3158
3292
  const cp = {
3159
3293
  token,
3160
- version: reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
3294
+ version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : reviewLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
3161
3295
  scope,
3162
3296
  sessionId,
3163
3297
  leafId,
@@ -3193,8 +3327,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3193
3327
  : undefined;
3194
3328
  const suspendAsk = checkpointStore && (durableApproval || irreversibleTools.size > 0 || egressTools.size > 0)
3195
3329
  ? async (req, postHookArgs, safety, approverUnavailable) => {
3196
- if (safety === undefined &&
3197
- onAsk !== undefined &&
3330
+ if (onAsk !== undefined &&
3198
3331
  runtimeCaps?.forceDurableGate !== true &&
3199
3332
  approverUnavailable !== true &&
3200
3333
  req.toolName !== ASK_USER_QUESTION_TOOL_NAME &&
@@ -3283,7 +3416,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3283
3416
  const mintedAt = Date.now();
3284
3417
  cp = {
3285
3418
  token,
3286
- version: approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
3419
+ version: orgAdmissionCheckpointState() ? ORG_ADMISSION_CHECKPOINT_VERSION : approvalLedger.totalTokens !== undefined ? TOKEN_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
3287
3420
  scope,
3288
3421
  sessionId,
3289
3422
  leafId,
@@ -3344,11 +3477,28 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3344
3477
  if (eff !== undefined && !toolEffects.has(t.name))
3345
3478
  toolEffects.set(t.name, eff);
3346
3479
  }
3347
- toolCallGateArmedRef.armed = effectivePolicy !== undefined || hooks?.preToolUse !== undefined || egressTools.size > 0 || irreversibleTools.size > 0 || spec.enablePlanMode === true;
3480
+ toolCallGateArmedRef.armed =
3481
+ effectivePolicy !== undefined ||
3482
+ hooks?.preToolUse !== undefined ||
3483
+ egressTools.size > 0 ||
3484
+ irreversibleTools.size > 0 ||
3485
+ spec.enablePlanMode === true ||
3486
+ complianceDenies.has("web_fetch");
3348
3487
  if (toolCallGateArmedRef.armed) {
3349
3488
  harness.on("tool_call", async (e) => {
3350
3489
  blockedToolCalls.delete(e.toolCallId);
3351
3490
  inheritedAskGrants.delete(e.toolCallId);
3491
+ {
3492
+ const complianceDeny = complianceCallDenial(complianceDenies, e.toolName);
3493
+ if (complianceDeny !== undefined) {
3494
+ if (blockedTracked)
3495
+ blockedToolCalls.add(e.toolCallId);
3496
+ if (notifyPermissionDenied) {
3497
+ await notifyPermissionDenied({ toolName: e.toolName, input: cloneObserverInput(e.input), toolCallId: e.toolCallId, reason: complianceDeny, source: "safety" });
3498
+ }
3499
+ return { block: true, reason: formatHookFeedback(complianceDeny), preToolContext: [] };
3500
+ }
3501
+ }
3352
3502
  if (planModeRef.active && (toolEffects.get(e.toolName) ?? "write") !== "read") {
3353
3503
  if (blockedTracked)
3354
3504
  blockedToolCalls.add(e.toolCallId);
@@ -3486,7 +3636,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3486
3636
  : {}),
3487
3637
  });
3488
3638
  let trimmed = trimToBudget(edited, guardAt, estimateContextTokens(edited, charsPerToken).tokens, charsPerToken);
3489
- if (trimmed.length < edited.length) {
3639
+ const trimDroppedMessages = trimmed.length < edited.length;
3640
+ if (trimDroppedMessages) {
3490
3641
  trimPressureRef.droppedMessages = true;
3491
3642
  const droppedCount = edited.length - trimmed.length;
3492
3643
  trimmed = insertTrimNotice(trimmed);
@@ -3509,6 +3660,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3509
3660
  catch {
3510
3661
  }
3511
3662
  }
3663
+ requestLossyRef.current =
3664
+ capped !== healed ||
3665
+ mediaCapped !== capped ||
3666
+ edited !== mediaCapped ||
3667
+ trimDroppedMessages ||
3668
+ swept.dropped.length > 0;
3512
3669
  return { messages: swept.messages };
3513
3670
  });
3514
3671
  const cacheBreakDetector = deps.cacheBreakDetection === false ? undefined : new CacheBreakDetector();
@@ -3622,7 +3779,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3622
3779
  : undefined;
3623
3780
  const listBackgroundTasks = () => defaultTaskRegistry
3624
3781
  .list({ owner: hostTaskId, scope: taskScope, sessionId })
3625
- .filter((t) => t.status === "pending" || t.status === "running")
3782
+ .filter((t) => t.status === "pending" || t.status === "running" || t.status === "parked")
3626
3783
  .map((t) => ({ id: t.task_id, ...(t.description !== undefined ? { description: t.description } : {}), status: t.status }));
3627
3784
  const overheadState = { promptChars: 0 };
3628
3785
  const centerCompactionCandidate = deps.promptSource !== undefined && centerAdoption !== undefined
@@ -3674,7 +3831,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3674
3831
  : undefined;
3675
3832
  overheadState.promptChars = systemPrompt.length;
3676
3833
  const preparedHolder = {};
3677
- const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
3834
+ const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, cacheBreakDetector, cacheFingerprint, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, ownedEnv, suspendRef, suspendProgressRef, reviewRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, brainCallGuardrailRef, suspendForReview, resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, listBackgroundTasks, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}) });
3678
3835
  const prepared = buildPrepared();
3679
3836
  preparedHolder.current = prepared;
3680
3837
  return prepared;