@sema-agent/core 5.17.0 → 5.18.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 (56) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/agents/subagent.js +24 -0
  3. package/dist/core/auto-compaction.d.ts +6 -0
  4. package/dist/core/auto-compaction.js +15 -1
  5. package/dist/core/checkpoint-store.d.ts +1 -0
  6. package/dist/core/governance-codes.js +1 -0
  7. package/dist/core/hooks.d.ts +8 -0
  8. package/dist/core/hooks.js +17 -0
  9. package/dist/core/mcp.js +3 -0
  10. package/dist/core/memory-engine/content-origin.d.ts +27 -0
  11. package/dist/core/memory-engine/content-origin.js +38 -0
  12. package/dist/core/memory-engine/engine.d.ts +12 -2
  13. package/dist/core/memory-engine/engine.js +172 -12
  14. package/dist/core/memory-engine/file-backend.d.ts +4 -0
  15. package/dist/core/memory-engine/file-backend.js +25 -3
  16. package/dist/core/memory-engine/index.d.ts +2 -1
  17. package/dist/core/memory-engine/index.js +2 -1
  18. package/dist/core/memory-engine/layout.d.ts +16 -0
  19. package/dist/core/memory-engine/layout.js +90 -2
  20. package/dist/core/memory-engine/sync-client.d.ts +1 -0
  21. package/dist/core/memory-engine/sync-client.js +23 -5
  22. package/dist/core/memory-engine/tools.d.ts +55 -0
  23. package/dist/core/memory-engine/tools.js +307 -0
  24. package/dist/core/memory-engine/types.d.ts +1 -1
  25. package/dist/core/memory.d.ts +4 -0
  26. package/dist/core/memory.js +15 -2
  27. package/dist/core/permission-rule-consent.d.ts +131 -0
  28. package/dist/core/permission-rule-consent.js +307 -0
  29. package/dist/core/permission-rule-model.d.ts +66 -0
  30. package/dist/core/permission-rule-model.js +135 -0
  31. package/dist/core/permission-rule-store.d.ts +89 -0
  32. package/dist/core/permission-rule-store.js +145 -0
  33. package/dist/core/permission-rules.d.ts +3 -2
  34. package/dist/core/permission-rules.js +9 -4
  35. package/dist/core/runner/prepare-memory.d.ts +3 -1
  36. package/dist/core/runner/prepare-memory.js +54 -14
  37. package/dist/core/runner/prepare-task.d.ts +11 -0
  38. package/dist/core/runner/prepare-task.js +192 -10
  39. package/dist/core/runner/runtask.js +24 -0
  40. package/dist/core/runner/tool-output-projection.js +1 -1
  41. package/dist/core/tool-policy.d.ts +8 -1
  42. package/dist/core/tool-policy.js +36 -0
  43. package/dist/core/tools.js +1 -0
  44. package/dist/core/trace.d.ts +20 -0
  45. package/dist/core/types.d.ts +14 -0
  46. package/dist/core/wiring-manifest.d.ts +5 -1
  47. package/dist/core/wiring-manifest.js +2 -0
  48. package/dist/index.d.ts +6 -2
  49. package/dist/index.js +5 -1
  50. package/dist/stores/file/permission-rule-store.d.ts +32 -0
  51. package/dist/stores/file/permission-rule-store.js +213 -0
  52. package/dist/tools/fs/fs-bash.js +12 -5
  53. package/dist/tools/fs/fs-shared.d.ts +12 -0
  54. package/dist/tools/fs/fs-shared.js +65 -1
  55. package/dist/tools/web.js +2 -0
  56. package/package.json +1 -1
@@ -21,6 +21,8 @@ import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../age
21
21
  import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
22
22
  import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
23
23
  import { askApproverIdentity, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
24
+ const PERSISTED_RULE_TOOL = "Bash";
25
+ import { findAdmittingRule, suggestRulesForCommand } from "../permission-rule-model.js";
24
26
  import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
25
27
  import { CHANGED_FILES_MTIME_EPS_MS, fenceMcpServerInstructions, renderAgentListingDelta } from "./turn-attachments.js";
26
28
  import { inlineUntrusted } from "../untrusted-text.js";
@@ -41,6 +43,9 @@ import { compileOutputSchema } from "./strict-output-schema.js";
41
43
  import { TOOL_SEARCH_NAME, buildDeferredRegistry, classifyDeferred, extractDiscoveredToolNames, createPlaceholderTool, createToolSearchTool, staticSchemaRenderable, } from "./tool-disclosure.js";
42
44
  import { createSharedMemoryTools } from "../shared-memory/tools.js";
43
45
  import { SHARED_MEMORY_TOOL_NAMES } from "../shared-memory/types.js";
46
+ import { MEMORY_ENGINE_TOOL_NAMES } from "../memory-engine/tools.js";
47
+ import { MEMORY_RECALL_DISCIPLINE } from "../memory-engine/engine.js";
48
+ import { classifyToolContentOrigin, contentOriginPollutes, delegationCallIsExternal } from "../memory-engine/content-origin.js";
44
49
  import { composeMemoryBlock } from "../memory.js";
45
50
  import { preflightLockedConfig } from "../locked-config.js";
46
51
  import { COMPLIANCE_CAPABILITIES, WEB_FETCH_TOOL_NAME, complianceCallDenial, resolveComplianceDenies } from "../compliance.js";
@@ -1389,6 +1394,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1389
1394
  }
1390
1395
  tools.push(...mcp.tools.map((t) => remoteToolOffload(t)));
1391
1396
  const rebuildHarnessToolsRef = {};
1397
+ const contentOriginWrapRef = {};
1392
1398
  const toolCallGateArmedRef = { armed: false };
1393
1399
  if (lockedPreflight.mcp?.length) {
1394
1400
  tools.push({
@@ -1868,13 +1874,23 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1868
1874
  .catch(() => undefined);
1869
1875
  }
1870
1876
  : undefined;
1871
- const { memoryEngineSession, memoryBlock: memoryBlockFromEngine, seedFiles: memorySeedFiles, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, } = await prepareMemory({
1877
+ const memoryPairNameDomain = new Set();
1878
+ for (const t of tools) {
1879
+ memoryPairNameDomain.add(t.name);
1880
+ for (const alias of t.aliases ?? [])
1881
+ memoryPairNameDomain.add(alias);
1882
+ }
1883
+ const memoryPairExcludedName = MEMORY_ENGINE_TOOL_NAMES.find((n) => (toolFaceSnapshot.exclude ?? []).includes(n));
1884
+ const memoryPairOccupiedName = MEMORY_ENGINE_TOOL_NAMES.find((n) => memoryPairNameDomain.has(n));
1885
+ const memorySearchToolsPlanned = memoryPairExcludedName === undefined && memoryPairOccupiedName === undefined;
1886
+ const { memoryEngineSession, memoryBlock: memoryBlockFromEngine, memoryTools, seedFiles: memorySeedFiles, admittedOrgScopes: memoryAdmittedOrgScopes, ownOrgVerdict: memoryOwnOrgVerdict, } = await prepareMemory({
1872
1887
  spec,
1873
1888
  deps,
1874
1889
  sessionId,
1875
1890
  taskRootPath,
1876
1891
  memoryWriteGateRef,
1877
1892
  writeToolsMounted: tools.some((t) => t.name === "Write") && !(toolFaceSnapshot.exclude?.includes("Write") ?? false),
1893
+ memorySearchToolsPlanned,
1878
1894
  admissionCtx: {
1879
1895
  orgMemoryDenied: complianceDenies.has("org_memory_mount"),
1880
1896
  complianceDegraded,
@@ -2012,6 +2028,18 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2012
2028
  sharedMemoryPairMounted = true;
2013
2029
  }
2014
2030
  }
2031
+ let memoryEnginePairMounted = false;
2032
+ if (memoryTools !== undefined && memoryTools.length > 0) {
2033
+ tools.push(...memoryTools.map((s) => defineTool(s)));
2034
+ memoryEnginePairMounted = true;
2035
+ }
2036
+ else if (memoryEngineSession !== undefined && !memorySearchToolsPlanned) {
2037
+ deps.onError?.(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: ` +
2038
+ (memoryPairExcludedName !== undefined
2039
+ ? `excludeTools removes "${memoryPairExcludedName}"`
2040
+ : `this task already declares a tool named "${memoryPairOccupiedName}" (the built-in yields to it)`) +
2041
+ " — the pair mounts together or not at all."), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
2042
+ }
2015
2043
  const skillsListing = skillSpecs.length > 0
2016
2044
  ? {
2017
2045
  entries: skillSpecs.map((s) => ({
@@ -2216,22 +2244,39 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2216
2244
  }
2217
2245
  return deferredSet;
2218
2246
  };
2219
- const sharedMemoryPair = SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n));
2247
+ const sharedMemoryPair = sharedMemoryPairMounted ? SHARED_MEMORY_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
2248
+ const memoryEnginePair = memoryEnginePairMounted ? MEMORY_ENGINE_TOOL_NAMES.filter((n) => tools.some((t) => t.name === n)) : [];
2249
+ const builtinDeferPairNames = [...sharedMemoryPair, ...memoryEnginePair];
2220
2250
  let deferred;
2221
- if (sharedMemoryPairMounted && sharedMemoryPair.length > 0) {
2222
- const withoutPair = tools.filter((t) => !sharedMemoryPair.some((n) => n === t.name));
2223
- const d1 = classifyDeferredOverFace(tools, sharedMemoryPair);
2224
- const d0 = classifyDeferredOverFace(withoutPair, []);
2251
+ if (builtinDeferPairNames.length > 0) {
2252
+ const withoutPairs = tools.filter((t) => !builtinDeferPairNames.some((n) => n === t.name));
2253
+ const d1 = classifyDeferredOverFace(tools, builtinDeferPairNames);
2254
+ const d0 = classifyDeferredOverFace(withoutPairs, []);
2225
2255
  const soleCause = d0.size === 0 && d1.size > 0;
2226
2256
  const supportNameTaken = tools.some((t) => t.name === TOOL_SEARCH_NAME || (t.aliases ?? []).includes(TOOL_SEARCH_NAME));
2227
2257
  if (soleCause && supportNameTaken) {
2228
2258
  for (let i = tools.length - 1; i >= 0; i--) {
2229
- if (sharedMemoryPair.some((n) => n === tools[i].name))
2259
+ if (builtinDeferPairNames.some((n) => n === tools[i].name))
2230
2260
  tools.splice(i, 1);
2231
2261
  }
2232
- sharedMemoryPairMounted = false;
2233
- deps.onError?.(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
2234
- `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
2262
+ if (sharedMemoryPair.length > 0) {
2263
+ sharedMemoryPairMounted = false;
2264
+ deps.onError?.(new Error(`Shared memory tools ${SHARED_MEMORY_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
2265
+ `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "shared-memory-not-mounted" });
2266
+ }
2267
+ if (memoryEnginePair.length > 0) {
2268
+ memoryEnginePairMounted = false;
2269
+ if (memoryBlock !== undefined) {
2270
+ if (memoryBlock === MEMORY_RECALL_DISCIPLINE)
2271
+ memoryBlock = undefined;
2272
+ else if (memoryBlock.includes(`\n\n${MEMORY_RECALL_DISCIPLINE}`))
2273
+ memoryBlock = memoryBlock.replace(`\n\n${MEMORY_RECALL_DISCIPLINE}`, "");
2274
+ else if (memoryBlock.startsWith(`${MEMORY_RECALL_DISCIPLINE}\n\n`))
2275
+ memoryBlock = memoryBlock.slice(MEMORY_RECALL_DISCIPLINE.length + 2);
2276
+ }
2277
+ deps.onError?.(new Error(`Memory tools ${MEMORY_ENGINE_TOOL_NAMES.join("/")} were NOT mounted: mounting them would inject ` +
2278
+ `the "${TOOL_SEARCH_NAME}" tool, whose name this task already declares — the pair mounts together or not at all.`), { phase: "config", sessionId, classification: "memory-tools-not-mounted" });
2279
+ }
2235
2280
  deferred = d0;
2236
2281
  }
2237
2282
  else {
@@ -2241,6 +2286,78 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2241
2286
  else {
2242
2287
  deferred = classifyDeferredOverFace(tools, []);
2243
2288
  }
2289
+ if (memoryEngineSession !== undefined) {
2290
+ const { pollution, contentSafety } = memoryEngineSession;
2291
+ const callerToolNames = new Set((spec.tools ?? []).map((t) => t.name));
2292
+ const contentOriginWrapped = new WeakSet();
2293
+ const classOf = (t) => classifyToolContentOrigin({
2294
+ declared: t.contentOrigin,
2295
+ isProtocolTool: protocolOf(t.name) !== undefined,
2296
+ isCallerTool: callerToolNames.has(t.name),
2297
+ trusted: contentSafety.trustedTools.has(t.name),
2298
+ });
2299
+ const delegationByName = new Map();
2300
+ for (const t of spec.tools ?? []) {
2301
+ const withFaces = t;
2302
+ if (withFaces.agentToolFaces === undefined && t.agentListing === undefined)
2303
+ continue;
2304
+ delegationByName.set(t.name, { faces: withFaces.agentToolFaces, pool: withFaces.agentToolPool });
2305
+ }
2306
+ const poolToolPollutes = (t) => contentOriginPollutes(classifyToolContentOrigin({
2307
+ declared: t.contentOrigin,
2308
+ isProtocolTool: protocolOf(t.name) !== undefined,
2309
+ isCallerTool: true,
2310
+ trusted: contentSafety.trustedTools.has(t.name),
2311
+ }), contentSafety.execIsExternalContent);
2312
+ contentOriginWrapRef.current = () => {
2313
+ for (let i = 0; i < tools.length; i++) {
2314
+ const t = tools[i];
2315
+ if (contentOriginWrapped.has(t))
2316
+ continue;
2317
+ const origin = classOf(t);
2318
+ const delegation = delegationByName.get(t.name);
2319
+ const isDelegation = delegation !== undefined;
2320
+ const pollutes = contentOriginPollutes(origin, contentSafety.execIsExternalContent);
2321
+ if (!pollutes && !isDelegation) {
2322
+ contentOriginWrapped.add(t);
2323
+ continue;
2324
+ }
2325
+ const inner = t.execute.bind(t);
2326
+ const mark = (reason) => {
2327
+ try {
2328
+ pollution.markPolluted(reason);
2329
+ }
2330
+ catch {
2331
+ }
2332
+ };
2333
+ const wrapped = {
2334
+ ...t,
2335
+ execute: async (toolCallId, params, signal, onUpdate) => {
2336
+ if (pollutes)
2337
+ mark(`tool "${t.name}" (${origin} content class) was invoked in this session`);
2338
+ if (!isDelegation)
2339
+ return inner(toolCallId, params, signal, onUpdate);
2340
+ const requested = params?.subagent_type;
2341
+ const external = delegationCallIsExternal({
2342
+ requestedType: typeof requested === "string" ? requested : undefined,
2343
+ faces: delegation.faces,
2344
+ pool: delegation.pool,
2345
+ isPolluting: poolToolPollutes,
2346
+ });
2347
+ const result = await inner(toolCallId, params, signal, onUpdate);
2348
+ if (external) {
2349
+ mark(`tool "${t.name}" returned the output of a delegated agent whose tool face can reach external content` +
2350
+ (typeof requested === "string" ? ` (subagent_type "${requested}")` : " (no subagent_type named — classified fail-closed)"));
2351
+ }
2352
+ return result;
2353
+ },
2354
+ };
2355
+ contentOriginWrapped.add(wrapped);
2356
+ tools[i] = wrapped;
2357
+ }
2358
+ };
2359
+ contentOriginWrapRef.current();
2360
+ }
2244
2361
  let centerAdoption;
2245
2362
  let centerAdoptionFresh = false;
2246
2363
  {
@@ -2485,6 +2602,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2485
2602
  const withdrawnTools = new Set();
2486
2603
  const deltaRef = toolsDeltaRef;
2487
2604
  const rematerialize = async (active) => {
2605
+ contentOriginWrapRef.current?.();
2488
2606
  const list = buildToolList(active);
2489
2607
  await harnessRef.current.setTools(list, list.map((t) => t.name));
2490
2608
  if (fpRef.current)
@@ -2528,6 +2646,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2528
2646
  }
2529
2647
  else {
2530
2648
  rebuildHarnessToolsRef.current = async () => {
2649
+ contentOriginWrapRef.current?.();
2531
2650
  const list = [...tools];
2532
2651
  await harnessRef.current.setTools(list, list.map((t) => t.name));
2533
2652
  if (fpRef.current)
@@ -2864,6 +2983,47 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2864
2983
  return {};
2865
2984
  return { riskAxes: { ...(irreversible !== undefined ? { irreversible } : {}), ...(egress !== undefined ? { egress } : {}) } };
2866
2985
  };
2986
+ const permissionRuleLane = (() => {
2987
+ const provider = deps.permissionRuleStore;
2988
+ if (provider === undefined)
2989
+ return undefined;
2990
+ const root = taskRootPath;
2991
+ return {
2992
+ admits: async (req) => {
2993
+ if (spec.principal === undefined || spec.principal === "")
2994
+ return undefined;
2995
+ if (req.toolName !== PERSISTED_RULE_TOOL)
2996
+ return undefined;
2997
+ const command = req.args?.command;
2998
+ if (typeof command !== "string")
2999
+ return undefined;
3000
+ let listed;
3001
+ try {
3002
+ listed = await provider.forPrincipal(spec.principal).list();
3003
+ }
3004
+ catch (err) {
3005
+ emitTrace(deps.tracer, () => ({
3006
+ kind: "permission.rule_store_unreadable",
3007
+ version: 1,
3008
+ taskId: spec.taskId ?? sessionId,
3009
+ message: err instanceof Error ? err.message : String(err),
3010
+ ts: Date.now(),
3011
+ }));
3012
+ return undefined;
3013
+ }
3014
+ return findAdmittingRule(listed.rules, { tool: req.toolName, command, cwd: root })?.rule;
3015
+ },
3016
+ };
3017
+ })();
3018
+ const ruleSuggestionsOf = (toolName, args) => {
3019
+ if (permissionRuleLane === undefined || toolName !== PERSISTED_RULE_TOOL)
3020
+ return {};
3021
+ const command = args?.command;
3022
+ if (typeof command !== "string")
3023
+ return {};
3024
+ const suggestions = suggestRulesForCommand(command);
3025
+ return suggestions.length > 0 ? { ruleSuggestions: suggestions } : {};
3026
+ };
2867
3027
  const recheckApprovedEdit = async (pol, onAskOf, creq, edit, csignal) => {
2868
3028
  let editArgs = edit;
2869
3029
  for (let round = 0;; round++) {
@@ -2894,6 +3054,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2894
3054
  toolName: creq.toolName,
2895
3055
  toolCallId: creq.toolCallId,
2896
3056
  args: editArgs,
3057
+ ...ruleSuggestionsOf(creq.toolName, editArgs),
2897
3058
  message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
2898
3059
  ...askSourceIdentity(),
2899
3060
  ...riskAxesOf(creq.toolName),
@@ -2942,6 +3103,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
2942
3103
  toolName: creq.toolName,
2943
3104
  toolCallId: creq.toolCallId,
2944
3105
  args: presentedArgs,
3106
+ ...ruleSuggestionsOf(creq.toolName, presentedArgs),
2945
3107
  message: first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
2946
3108
  ...askSourceIdentity(),
2947
3109
  ...riskAxesOf(creq.toolName),
@@ -3000,6 +3162,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3000
3162
  toolName: creq.toolName,
3001
3163
  toolCallId: creq.toolCallId,
3002
3164
  args: presentedArgs,
3165
+ ...ruleSuggestionsOf(creq.toolName, presentedArgs),
3003
3166
  message: decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
3004
3167
  ...askSourceIdentity(),
3005
3168
  ...riskAxesOf(creq.toolName),
@@ -3215,6 +3378,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3215
3378
  ...(checkpointStore !== undefined ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
3216
3379
  sessionDurability: resolveDeclaredDurability(sessions, "sessionStore"),
3217
3380
  backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
3381
+ permissionRuleStoreWired: deps.permissionRuleStore !== undefined,
3218
3382
  hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
3219
3383
  lockedConfigWired: deps.lockedConfig !== undefined,
3220
3384
  complianceWired: deps.compliancePostureResolver !== undefined,
@@ -3303,6 +3467,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3303
3467
  const preview = approvalPreviewOf(req.toolName, req.args);
3304
3468
  return preview !== undefined ? { preview } : {};
3305
3469
  })(),
3470
+ ...ruleSuggestionsOf(req.toolName, req.args),
3306
3471
  message: decision.message ?? `approval required for "${req.toolName}"`,
3307
3472
  ...askSourceIdentity(),
3308
3473
  ...riskAxesOf(req.toolName),
@@ -3854,6 +4019,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3854
4019
  const preview = approvalPreviewOf(req.toolName, parkedArgs);
3855
4020
  return preview !== undefined ? { preview } : {};
3856
4021
  })(),
4022
+ ...ruleSuggestionsOf(req.toolName, parkedArgs),
3857
4023
  boundInputHash: boundInputHashOf(parkedArgs),
3858
4024
  batchToolCallIds,
3859
4025
  completedCallIds,
@@ -3961,6 +4127,22 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3961
4127
  onHookError: notifyHookError,
3962
4128
  shellGated: (e.toolName === "Bash" && shellGatedBash) || (e.toolName === "Monitor" && shellGatedMonitor),
3963
4129
  ...(autoModeDecider ? { autoMode: { decider: autoModeDecider } } : {}),
4130
+ ...(permissionRuleLane
4131
+ ? {
4132
+ persistedRules: {
4133
+ admits: permissionRuleLane.admits,
4134
+ onResolved: (info) => emitTrace(deps.tracer, () => ({
4135
+ kind: "permission.persisted_rule_allowed",
4136
+ version: 1,
4137
+ taskId: spec.taskId ?? sessionId,
4138
+ toolName: info.toolName,
4139
+ toolCallId: info.toolCallId,
4140
+ rule: info.rule,
4141
+ ts: Date.now(),
4142
+ })),
4143
+ },
4144
+ }
4145
+ : {}),
3964
4146
  isMarkedUnresolvable: (toolCallId) => inheritedUnavailableAsks.has(toolCallId),
3965
4147
  });
3966
4148
  }
@@ -328,6 +328,24 @@ function platformLimitTerminal(reason, retryAfterMs, moment = "turn_boundary") {
328
328
  e.retryAfterMs = retryAfterMs;
329
329
  return e;
330
330
  }
331
+ const disclosedUnevaluableWindow = new WeakSet();
332
+ function discloseUnevaluableWindow(u, prepared, tracer, taskId, onError) {
333
+ if (disclosedUnevaluableWindow.has(prepared))
334
+ return;
335
+ disclosedUnevaluableWindow.add(prepared);
336
+ emitTrace(tracer, () => ({ kind: "compaction.unevaluable", version: 1, taskId, estTokens: u.estTokens, ts: Date.now() }));
337
+ const modelClause = u.modelId !== undefined ? ` "${u.modelId}"` : "";
338
+ const cause = u.windowValue !== undefined
339
+ ? `its \`${u.windowField}\` is ${u.windowValue}, which is not a positive number of tokens — that field takes precedence over the ` +
340
+ `others, so fixing or removing it is what restores the threshold`
341
+ : `it declares no context window at all (\`autoCompactTokens\` / \`contextTokens\` / \`contextWindow\` are all absent) — declare one to restore the threshold`;
342
+ try {
343
+ onError?.(new Error(`auto-compaction cannot run for model${modelClause}: ${cause}. Until then there is no threshold to compare against, so the ` +
344
+ `context will grow until the provider refuses the request. The conversation is already about ${u.estTokens} estimated tokens.`), { phase: "config", sessionId: prepared.sessionId });
345
+ }
346
+ catch {
347
+ }
348
+ }
331
349
  function makeTurnBoundary(prepared, stats, rs, deps) {
332
350
  const { spec, queue, manualCompactRef, todoToolMounted, taskToolsMounted, walltimeMonotonicDeadline, timeout, ident, postToolBatchHook, batchArgs, compactionBrain, withinTaskCompaction, compactionBreaker, windowSafetyOptions, rapidRefill, drainManualCompact, runnerHooks } = deps;
333
351
  let finalVerifySeenAtLastBoundary = 0;
@@ -853,6 +871,9 @@ function makeTurnBoundary(prepared, stats, rs, deps) {
853
871
  if (comp.contextUsage) {
854
872
  queue.push({ type: "context_usage", ...comp.contextUsage, ...ident() });
855
873
  }
874
+ if (comp.unevaluableWindow) {
875
+ discloseUnevaluableWindow(comp.unevaluableWindow, prepared, rs.telemetry.tracer, rs.telemetry.taskId, runnerHooks.onError);
876
+ }
856
877
  if (comp.suppressedByFloor) {
857
878
  const s = comp.suppressedByFloor;
858
879
  emitTrace(rs.telemetry.tracer, () => ({
@@ -3969,6 +3990,9 @@ export class Runner {
3969
3990
  ...this.compactionHookOptions(spec, prepared.sessionId, "auto"),
3970
3991
  });
3971
3992
  this.recordCompactionReuse(prepared, finishComp);
3993
+ if (finishComp.unevaluableWindow) {
3994
+ discloseUnevaluableWindow(finishComp.unevaluableWindow, prepared, spec.tracer ?? this.deps.tracer, spec.taskId ?? prepared.sessionId, this.deps.onError);
3995
+ }
3972
3996
  comp = finishComp;
3973
3997
  }
3974
3998
  catch (err) {
@@ -62,7 +62,7 @@ const CC_DETAIL_TYPES = new Set([
62
62
  "agent", "task", "task-list", "task-output", "workflow-run",
63
63
  "web-fetch", "web-search", "todo", "cron-create", "cron-delete", "cron-list", "image",
64
64
  "task-stop", "tool-search", "repo-map", "fork", "enter-plan-mode", "exit-plan-mode",
65
- "monitor-start", "path_not_in_root", "readonly_out_of_root",
65
+ "monitor-start", "path_not_in_root", "readonly_out_of_root", "bash_invalid_timeout",
66
66
  "report-findings", "schedule-wakeup", "send-message", "agent-transcript", "a2a", "document",
67
67
  ]);
68
68
  export const structuredFrom = (result) => {
@@ -8,7 +8,7 @@ export interface ToolCallRequest {
8
8
  suspendCount: number;
9
9
  };
10
10
  }
11
- export type DecisionReason = "rule" | "mode" | "hook" | "safety" | "classifier";
11
+ export type DecisionReason = "rule" | "mode" | "hook" | "safety" | "classifier" | "persisted_rule";
12
12
  export type PermissionResult = {
13
13
  action: "allow";
14
14
  updatedInput?: unknown;
@@ -43,6 +43,12 @@ export declare function toolPolicyNameSets(p: ToolPolicy | undefined): readonly
43
43
  export declare function createAllowDenyPolicy(opts: {
44
44
  allow?: string[];
45
45
  deny?: string[];
46
+ onInvalidName?: "throw" | "skip";
47
+ onInvalidNameIssue?: (issue: {
48
+ entry: string;
49
+ list: "allow" | "deny";
50
+ message: string;
51
+ }) => void;
46
52
  }): NamedToolPolicy;
47
53
  export declare function createApprovalPolicy(opts: {
48
54
  requireApproval: string[];
@@ -81,6 +87,7 @@ export interface AskRequest {
81
87
  args: unknown;
82
88
  readonly preview?: unknown;
83
89
  readonly boundInputHash?: string;
90
+ readonly ruleSuggestions?: readonly import("./permission-rule-model.js").RuleSuggestion[];
84
91
  message: string;
85
92
  readonly principal?: string;
86
93
  readonly sourceTaskId?: string;
@@ -3,6 +3,7 @@ import { isAbsolute, join, normalize as normalizePath, sep } from "node:path";
3
3
  import { BASH_READONLY_DEFAULT_ALLOW, parseLeadingCommandName } from "../tools/fs/index.js";
4
4
  import { boundInputHashOf } from "./canonical-json.js";
5
5
  import { inlineUntrusted } from "./untrusted-text.js";
6
+ import { parsePermissionRule } from "./permission-rules.js";
6
7
  import { writeTargetPath } from "../tools/fs/safety.js";
7
8
  export function decisionText(d) {
8
9
  return d.message;
@@ -44,6 +45,41 @@ export function toolPolicyNameSets(p) {
44
45
  return Array.isArray(sets) ? sets : [];
45
46
  }
46
47
  export function createAllowDenyPolicy(opts) {
48
+ const invalid = [];
49
+ const screen = (entries, list) => {
50
+ if (entries === undefined)
51
+ return undefined;
52
+ const kept = [];
53
+ for (const entry of entries) {
54
+ if (parsePermissionRule(entry).ruleContent === undefined) {
55
+ kept.push(entry);
56
+ continue;
57
+ }
58
+ invalid.push({
59
+ entry,
60
+ list,
61
+ message: `"${entry}" is a rule CONTENT form, not a tool name — a name set matches raw tool names, so this entry ` +
62
+ `can never match any mounted tool (in an allow list it removes the tool entirely). Use the tool NAME here, ` +
63
+ `and route the narrowing through the lane that speaks this grammar: parameter rules via ` +
64
+ `createPermissionRulePolicy, Bash command prefixes via the persisted allow-rule lane.`,
65
+ });
66
+ }
67
+ return kept;
68
+ };
69
+ const screenedAllow = screen(opts.allow, "allow");
70
+ const screenedDeny = screen(opts.deny, "deny");
71
+ if (invalid.length > 0) {
72
+ if ((opts.onInvalidName ?? "throw") === "throw") {
73
+ const e = new Error(`createAllowDenyPolicy: ${invalid.length} entr${invalid.length === 1 ? "y is" : "ies are"} not tool name(s):\n` +
74
+ invalid.map((i) => ` [${i.list}] ${i.message}`).join("\n"));
75
+ e.code = "config.invalid_tool_name_set";
76
+ e.issues = invalid;
77
+ throw e;
78
+ }
79
+ for (const issue of invalid)
80
+ opts.onInvalidNameIssue?.(issue);
81
+ }
82
+ opts = { ...opts, ...(screenedAllow ? { allow: screenedAllow } : {}), ...(screenedDeny ? { deny: screenedDeny } : {}) };
47
83
  const allow = opts.allow ? new Set(opts.allow) : undefined;
48
84
  const deny = new Set(opts.deny ?? []);
49
85
  return {
@@ -45,6 +45,7 @@ export function defineTool(spec, options) {
45
45
  executionMode,
46
46
  ...(spec.isConcurrencySafe ? { isConcurrencySafe: spec.isConcurrencySafe } : {}),
47
47
  ...(spec.effect ? { effect: spec.effect } : {}),
48
+ ...(spec.contentOrigin ? { contentOrigin: spec.contentOrigin } : {}),
48
49
  ...(spec.prepareArguments ? { prepareArguments: spec.prepareArguments } : {}),
49
50
  ...(spec.approvalPreview ? { approvalPreview: spec.approvalPreview } : {}),
50
51
  execute: async (toolCallId, rawParams, signal) => {
@@ -181,6 +181,20 @@ export type TraceEvent = {
181
181
  site: string;
182
182
  message: string;
183
183
  ts: number;
184
+ } | {
185
+ kind: "permission.persisted_rule_allowed";
186
+ version: 1;
187
+ taskId: string;
188
+ toolName: string;
189
+ toolCallId: string;
190
+ rule: string;
191
+ ts: number;
192
+ } | {
193
+ kind: "permission.rule_store_unreadable";
194
+ version: 1;
195
+ taskId: string;
196
+ message: string;
197
+ ts: number;
184
198
  } | {
185
199
  kind: "brain.failover";
186
200
  version: 1;
@@ -217,6 +231,12 @@ export type TraceEvent = {
217
231
  estTokens: number;
218
232
  floor: number;
219
233
  ts: number;
234
+ } | {
235
+ kind: "compaction.unevaluable";
236
+ version: 1;
237
+ taskId: string;
238
+ estTokens: number;
239
+ ts: number;
220
240
  } | {
221
241
  kind: "compaction.blocked";
222
242
  version: 1;
@@ -20,6 +20,7 @@ export interface Brain {
20
20
  complete?: CompleteSimpleFn;
21
21
  }
22
22
  export type ToolEffect = "read" | "write" | "idempotent";
23
+ export type ToolContentOrigin = "external" | "execution" | "local";
23
24
  export interface ToolSpec<TParams extends TSchema = TSchema> {
24
25
  name: string;
25
26
  aliases?: string[];
@@ -30,6 +31,17 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
30
31
  name: string;
31
32
  description: string;
32
33
  }>;
34
+ agentToolFaces?: ReadonlyArray<{
35
+ name: string;
36
+ allowTools?: readonly string[];
37
+ denyTools?: readonly string[];
38
+ canRedelegate?: boolean;
39
+ }>;
40
+ agentToolPool?: ReadonlyArray<{
41
+ name: string;
42
+ aliases?: readonly string[];
43
+ contentOrigin?: ToolContentOrigin;
44
+ }>;
33
45
  approvalPreview?: (args: unknown) => unknown;
34
46
  agentModels?: readonly string[];
35
47
  withAgents?: (agents: ReadonlyArray<AgentDefinition>) => ToolSpec;
@@ -42,6 +54,7 @@ export interface ToolSpec<TParams extends TSchema = TSchema> {
42
54
  } | Promise<{
43
55
  reversible: boolean;
44
56
  }>;
57
+ contentOrigin?: ToolContentOrigin;
45
58
  executionMode?: "sequential" | "parallel";
46
59
  isConcurrencySafe?: (args: unknown) => boolean;
47
60
  parameters: TParams;
@@ -772,6 +785,7 @@ export interface RunnerDeps {
772
785
  checkpointStore?: import("./checkpoint-store.js").CheckpointStore;
773
786
  fileSnapshotStore?: import("./file-snapshot-store.js").FileSnapshotStore;
774
787
  sessionPolicyStore?: import("./session-policy-store.js").SessionPolicyStore;
788
+ permissionRuleStore?: import("./permission-rule-store.js").PermissionRuleStoreProvider;
775
789
  runtimeCapsResolver?: (principal: string | undefined) => RuntimeCaps | undefined | Promise<RuntimeCaps | undefined>;
776
790
  lockedConfig?: import("./locked-config.js").LockedConfig;
777
791
  compliancePostureResolver?: (principal: string | undefined) => import("./compliance.js").CompliancePosture | undefined | Promise<import("./compliance.js").CompliancePosture | undefined>;
@@ -43,6 +43,9 @@ export interface WiringManifest {
43
43
  backgroundAgentStore: boolean;
44
44
  hostChildEventSink: boolean;
45
45
  };
46
+ permissionRules: {
47
+ storeWired: boolean;
48
+ };
46
49
  governance: {
47
50
  audience: "operator";
48
51
  lockedConfig: boolean;
@@ -71,13 +74,14 @@ export interface WiringFacts {
71
74
  checkpointDurability?: StoreDurability;
72
75
  sessionDurability: StoreDurability;
73
76
  backgroundAgentStoreWired: boolean;
77
+ permissionRuleStoreWired: boolean;
74
78
  hostChildEventSinkWired: boolean;
75
79
  lockedConfigWired: boolean;
76
80
  complianceWired: boolean;
77
81
  memoryAdmissionWired: boolean;
78
82
  retentionPolicyWired: boolean;
79
83
  }
80
- export type StaticWiringDeps = Pick<RunnerDeps, "onAsk" | "onQuestion" | "interactionPosture" | "onElicit" | "checkpointStore" | "sessionStore" | "backgroundAgentStore" | "onBackgroundChildEvent" | "lockedConfig" | "compliancePostureResolver" | "memoryScopeAdmission" | "retentionPolicy">;
84
+ export type StaticWiringDeps = Pick<RunnerDeps, "onAsk" | "onQuestion" | "interactionPosture" | "onElicit" | "checkpointStore" | "sessionStore" | "backgroundAgentStore" | "onBackgroundChildEvent" | "lockedConfig" | "compliancePostureResolver" | "memoryScopeAdmission" | "retentionPolicy" | "permissionRuleStore">;
81
85
  export type StaticWiringSpec = Pick<TaskSpec, "onAsk" | "onQuestion" | "checkpointStore" | "durableApproval" | "mcp" | "interactiveTools" | "interactionPosture">;
82
86
  export declare function resolveDeclaredDurability(store: {
83
87
  readonly durability?: StoreDurability;
@@ -86,6 +86,7 @@ export function deriveWiringManifest(facts) {
86
86
  parkLane,
87
87
  session: { store: manifestDurabilityOf(facts.sessionDurability) },
88
88
  fleet: { backgroundAgentStore: facts.backgroundAgentStoreWired, hostChildEventSink: facts.hostChildEventSinkWired },
89
+ permissionRules: { storeWired: facts.permissionRuleStoreWired },
89
90
  governance: {
90
91
  audience: "operator",
91
92
  lockedConfig: facts.lockedConfigWired,
@@ -177,6 +178,7 @@ export function describeStaticWiring(deps, spec = {}) {
177
178
  ...(capable ? { checkpointDurability: resolveDeclaredDurability(checkpointStore, "checkpointStore") } : {}),
178
179
  sessionDurability: resolveDeclaredDurability(deps.sessionStore, "sessionStore"),
179
180
  backgroundAgentStoreWired: deps.backgroundAgentStore !== undefined,
181
+ permissionRuleStoreWired: deps.permissionRuleStore !== undefined,
180
182
  hostChildEventSinkWired: deps.onBackgroundChildEvent !== undefined,
181
183
  lockedConfigWired: deps.lockedConfig !== undefined,
182
184
  complianceWired: deps.compliancePostureResolver !== undefined,
package/dist/index.d.ts CHANGED
@@ -127,9 +127,13 @@ export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, typ
127
127
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, type AutoModeRules, type BuildAutoModePromptOptions, type AutoModeWindowOptions, } from "./core/auto-mode-prompt.js";
128
128
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
129
129
  export { createPermissionRulePolicy, validatePermissionRules, parsePermissionRule, wildcardMatch, type PermissionRule, type ParsedPermissionRule, type PermissionRuleIssue, type PermissionRuleCaps, type PermissionRulePolicyOptions, } from "./core/permission-rules.js";
130
+ export { parseAllowRuleText, formatAllowRuleText, ruleAdmitsCommand, findAdmittingRule, suggestRulesForCommand, scopeCoversCwd, pathWithinRoot, isRuleLive, BARE_INTERPRETER_NAMES, MAX_RULE_TEXT_CHARS, type PersistedAllowRule, type RuleTombstone, type RuleScope, type RuleDot, type RuleAdd, type RuleAddOrigin, type RuleSuggestion, type RuleReject, type RuleRejectCode, type ParsedAllowRule, type PersistedRuleTool, type PersistedRuleMatch, } from "./core/permission-rule-model.js";
131
+ export { removePersistedRule, applyTombstones, sameScope, InMemoryPermissionRuleStore, EMPTY_RULE_STORE, type PermissionRuleStore, type PermissionRuleStoreProvider, type StoredAllowRules, type RemoveResult, type PutResult, } from "./core/permission-rule-store.js";
132
+ export { prepareCardApproval, confirmRuleApproval, redeemRuleTicket, redeemRuleBatch, prepareCcImport, prepareStarterBatch, mintRuleTicket, STARTER_RULES, InMemoryRuleApprovalRecordStore, type RuleTicket, type RuleCandidate, type RuleApprovalKind, type RuleApprovalRecord, type RuleApprovalRecordStore, type RuleConsentDeps, type RedeemResult, type CcImportLayer, type ImportedSettingsLayer, type ImportPreview, type ImportResult, } from "./core/permission-rule-consent.js";
133
+ export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-store.js";
130
134
  export { formatHookFeedback, runToolGate, createHookEnvCapabilities, type Hooks, type HookToolContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
131
135
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
132
- export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
136
+ export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
133
137
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, type SharedMemoryStoreProvider, type SharedMemoryStoreReader, type SharedMemoryStoreInfo, type SharedMemoryDocumentEntry, type SharedMemorySnapshot, type SharedMemoryRequestContext, type MemoryListDetails, type MemoryReadDetails, } from "./core/shared-memory/types.js";
134
138
  export { sharedMemoryStoreContract, type SharedMemoryFixture, type SharedMemoryStoreContractHooks, } from "./core/shared-memory/contract.js";
135
139
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
@@ -218,7 +222,7 @@ export { retryBackoffMs, parseRetryAfter } from "./brain/retry.js";
218
222
  export { type BrainTimeoutConfig } from "./brain/timeout.js";
219
223
  export { createAssistantMessageEventStream } from "./internal/llm.js";
220
224
  export type { AssistantMessage, AssistantMessageEvent, CompleteSimpleFn, Context, DocumentContent, ImageContent, Message, StopReason, StreamFn, TextContent, ThinkingContent, ToolCall, ToolResultMessage, Usage, UserMessage, } from "./internal/llm.js";
221
- export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, RuntimeCaps, BackgroundChildEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolEffect, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
225
+ export type { AgentDefinition, BeforeWriteHook, BeforeWriteRequest, BeforeWriteResult, HandsBandOptions, Brain, BrainStatus, BrainStatusPhase, ImageInput, McpElicitRequest, McpElicitResponse, McpServerSpec, A2aServerSpec, OnElicit, Model, ModelRef, ProjectMemoryLoad, RunnerDeps, RuntimeCaps, BackgroundChildEvent, SkillManifest, SkillSpec, TaskEvent, TaskEventIdentity, ToolActivity, TaskLimits, StaleToolResultOffloadOptions, TaskResult, RemoteEnvFailureNote, TaskSpec, TaskStatus, TaskStream, CompactOutcome, ThinkingLevel, ToolExecuteContext, ToolReturn, ToolSpec, ToolEffect, ToolContentOrigin, WorkflowGovernanceBaseline, DelegationTaskType, } from "./core/types.js";
222
226
  export { Type } from "typebox";
223
227
  export type { TSchema, Static } from "typebox";
224
228
  export { explainPromptAssembly, describeDefaultPack, type DefaultPackDescription, type ExplainInput } from "./prompt-assembly/explain.js";