@sema-agent/core 5.18.0 → 5.18.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.18.1 — 2026-08-10
4
+
5
+ > The 5.18.0 post-release verification round, folded back in. Every surface below was
6
+ > two-way-verified against the 5.18.0 artifact by the evaluation line before this release
7
+ > (pre-release criteria [3095], green-lit [3103]).
8
+
9
+ ### BREAKING (vs 5.18.0; both APIs are new in 5.18.0 and cli has not consumed them yet)
10
+
11
+ - **`confirmRuleApproval` returns `{ ok: true } | { ok: false, reason }`** instead of a bare
12
+ boolean — seven-member closed reason set (`record_not_found` deliberately covers the
13
+ foreign-principal arm so record existence does not leak, `selection_missing`,
14
+ `selection_invalid`, `selection_mismatch`, `batch_takes_no_selection`, `not_pending`,
15
+ `conflict`). `selectedCandidate` is an integer index.
16
+ - **`prepareCardApproval` refuses caller-supplied `candidates` loudly**
17
+ (`config.invalid_argument`) instead of silently ignoring them — card options are
18
+ engine-minted, and a dropped supply attempt is the same silent-narrowing shape this
19
+ release family removes elsewhere.
20
+
21
+ ### Added
22
+
23
+ - **`settledBy` on approval settlements** (`"human" | "timeout" | "aborted"`): an optional
24
+ field on the durable resume outcome's `policy_ask` arm, carried onto the settled call's
25
+ `tool_end` (top-level key). Out-of-vocabulary values are refused pre-CAS
26
+ (`checkpoint.invalid_outcome`, `detail.field: "settledBy"` — the row stays pending);
27
+ `decision: "allow"` accepts only `"human"` or omission (a contradictory record must not
28
+ become an execution). Core's own arms fill it (elapsed window = `timeout`; abort /
29
+ unclonable / out-of-contract = `aborted`; human answers = `"human"`); posture arms
30
+ (headless auto-deny, blanket modes) leave it absent. Absence = an older caller — consumers
31
+ must not infer semantics from a missing key. New exports: `ApprovalSettledBy`,
32
+ `APPROVAL_SETTLED_BY_VALUES`, `isApprovalSettledBy`.
33
+
34
+ ### Fixed
35
+
36
+ - **An elapsed approval window says nobody answered** — the live-deadline refusal no longer
37
+ borrows the sentence a person's refusal uses; human-refusal and no-provenance wordings are
38
+ byte-for-byte unchanged (269 existing approval strings verified untouched).
39
+ - **`createAllowDenyPolicy` screens malformed `mcp__` names** (empty segment: `mcp__`,
40
+ `mcp__a__`) alongside the content form; `mcp__server` / `mcp__server__tool` and the
41
+ conditionally-retired `Task` spelling stay accepted. The constructor-level retired-name
42
+ screen added briefly after 5.18.0 was withdrawn — the roster-aware prepare-time audit
43
+ (`config.legacy_tool_name`, all three kinds) already owns that refusal.
44
+
3
45
  ## 5.18.0 — 2026-08-10
4
46
 
5
47
  > Two mainline features land together: design/178 personal-assistant memory v1 (the retrieval pair
@@ -95,6 +95,7 @@ export type ResumeOutcome = {
95
95
  updatedInput?: unknown;
96
96
  reason?: string;
97
97
  answer?: QuestionAnswer;
98
+ settledBy?: import("./tool-policy.js").ApprovalSettledBy;
98
99
  } | {
99
100
  gate: "resource_limit";
100
101
  decision: "continue";
@@ -277,10 +278,10 @@ export declare function summarizeCheckpoint(cp: Checkpoint): CheckpointSummary;
277
278
  export declare class CheckpointError extends Error {
278
279
  readonly code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.walltime_axis_retired" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "steering.queue_full" | "steering.duplicate_input_id" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch";
279
280
  readonly detail?: {
280
- field?: "boundCallId" | "boundInputHash" | "answer";
281
+ field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy";
281
282
  } | undefined;
282
283
  constructor(code: "checkpoint.already_exists" | "checkpoint.already_resolved" | "checkpoint.not_found" | "checkpoint.gate_mismatch" | "checkpoint.resume_aborted" | "checkpoint.invalid_outcome" | "checkpoint.unsupported_version" | "checkpoint.walltime_axis_retired" | "checkpoint.reopen_revote" | "checkpoint.reopened_concurrently" | "checkpoint.reopen_failed" | "steering.invalid_content" | "steering.queue_full" | "steering.duplicate_input_id" | "wake.gate_pending" | "wake.nothing_to_deliver" | "resume.parent_constraint_missing" | "resume.parent_constraint_mismatch", message: string, detail?: {
283
- field?: "boundCallId" | "boundInputHash" | "answer";
284
+ field?: "boundCallId" | "boundInputHash" | "answer" | "settledBy";
284
285
  } | undefined);
285
286
  }
286
287
  export type StoreDurability = "durable" | "process-local";
@@ -104,6 +104,7 @@ export declare function formatHookFeedback(text: string): string;
104
104
  export interface ToolGateResult {
105
105
  block?: boolean;
106
106
  reason?: string;
107
+ settledBy?: import("./tool-policy.js").ApprovalSettledBy;
107
108
  updatedInput?: unknown;
108
109
  suspend?: {
109
110
  token: import("./checkpoint-store.js").CheckpointToken;
@@ -411,9 +411,11 @@ export async function runToolGate(input) {
411
411
  if (input.permissionDenied) {
412
412
  await notifier.notifyAsync(() => input.permissionDenied?.({ toolName, input: cloneObserverInput(currentInput), toolCallId, reason: denyReason, source: denySource }), "toolGate.permissionDenied");
413
413
  }
414
+ const denySettledBy = decision.settledBy;
414
415
  return {
415
416
  block: true,
416
417
  reason: formatHookFeedback(denyReason),
418
+ ...(denySettledBy !== undefined ? { settledBy: denySettledBy } : {}),
417
419
  preToolContext,
418
420
  };
419
421
  }
@@ -422,8 +424,10 @@ export async function runToolGate(input) {
422
424
  if (rw !== undefined)
423
425
  currentInput = rw;
424
426
  }
427
+ const allowSettledBy = decision.action === "allow" ? decision.settledBy : undefined;
425
428
  return {
426
429
  updatedInput: currentInput === event.input ? undefined : currentInput,
430
+ ...(allowSettledBy !== undefined ? { settledBy: allowSettledBy } : {}),
427
431
  preToolContext,
428
432
  };
429
433
  }
@@ -56,7 +56,14 @@ export declare function confirmRuleApproval(opts: {
56
56
  principal: string;
57
57
  selectedCandidate?: number;
58
58
  deps: RuleConsentDeps;
59
- }): Promise<boolean>;
59
+ }): Promise<ConfirmResult>;
60
+ export type ConfirmRefusalReason = "record_not_found" | "selection_missing" | "selection_invalid" | "selection_mismatch" | "batch_takes_no_selection" | "not_pending" | "conflict";
61
+ export type ConfirmResult = {
62
+ ok: true;
63
+ } | {
64
+ ok: false;
65
+ reason: ConfirmRefusalReason;
66
+ };
60
67
  export type RedeemResult = {
61
68
  status: "redeemed";
62
69
  rule: string;
@@ -47,6 +47,11 @@ function parseRuleTicket(ticket) {
47
47
  }
48
48
  export async function prepareCardApproval(opts) {
49
49
  const principal = requirePrincipal(opts.principal, "prepareCardApproval");
50
+ if ("candidates" in opts) {
51
+ const e = new Error("prepareCardApproval does not accept caller candidates — card options are minted by the engine from the adjudicated command");
52
+ e.code = "config.invalid_argument";
53
+ throw e;
54
+ }
50
55
  if (opts.toolName !== CARD_RULE_TOOL)
51
56
  return undefined;
52
57
  const scope = opts.scope ?? { kind: "global" };
@@ -70,24 +75,30 @@ export async function prepareCardApproval(opts) {
70
75
  const CARD_RULE_TOOL = "Bash";
71
76
  export async function confirmRuleApproval(opts) {
72
77
  const principal = requirePrincipal(opts.principal, "confirmRuleApproval");
78
+ const no = (reason) => ({ ok: false, reason });
73
79
  const rec = await opts.deps.approvals.get(opts.approvalId);
74
80
  if (rec === undefined || rec.principal !== principal)
75
- return false;
81
+ return no("record_not_found");
76
82
  if (rec.kind === "card") {
77
83
  const chosen = opts.selectedCandidate;
78
- if (chosen === undefined || !Number.isInteger(chosen) || rec.candidates[chosen] === undefined)
79
- return false;
80
- if (rec.state === "approved" || rec.state === "redeemed")
81
- return rec.selectedCandidate === chosen;
82
- return await opts.deps.approvals.cas(rec.id, rec.rev, { ...rec, rev: rec.rev + 1, state: "approved", selectedCandidate: chosen });
84
+ if (chosen === undefined)
85
+ return no("selection_missing");
86
+ if (!Number.isInteger(chosen) || rec.candidates[chosen] === undefined)
87
+ return no("selection_invalid");
88
+ if (rec.state === "approved" || rec.state === "redeemed") {
89
+ return rec.selectedCandidate === chosen ? { ok: true } : no("selection_mismatch");
90
+ }
91
+ const won = await opts.deps.approvals.cas(rec.id, rec.rev, { ...rec, rev: rec.rev + 1, state: "approved", selectedCandidate: chosen });
92
+ return won ? { ok: true } : no("conflict");
83
93
  }
84
94
  if (opts.selectedCandidate !== undefined)
85
- return false;
95
+ return no("batch_takes_no_selection");
86
96
  if (rec.state === "approved")
87
- return true;
97
+ return { ok: true };
88
98
  if (rec.state !== "pending")
89
- return false;
90
- return await opts.deps.approvals.cas(rec.id, rec.rev, { ...rec, rev: rec.rev + 1, state: "approved" });
99
+ return no("not_pending");
100
+ const won = await opts.deps.approvals.cas(rec.id, rec.rev, { ...rec, rev: rec.rev + 1, state: "approved" });
101
+ return won ? { ok: true } : no("conflict");
91
102
  }
92
103
  export async function redeemRuleTicket(opts) {
93
104
  const principal = requirePrincipal(opts.principal, "redeemRuleTicket");
@@ -64,6 +64,7 @@ export interface Prepared {
64
64
  hit: boolean;
65
65
  };
66
66
  blockedToolCalls: Set<string>;
67
+ approvalSettledBy: Map<string, import("../tool-policy.js").ApprovalSettledBy>;
67
68
  nestedStats: NestedUsageAccum;
68
69
  rewindNotes?: NonNullable<TaskResult["rewindNotes"]>;
69
70
  cwdRef?: CwdRef;
@@ -20,7 +20,7 @@ import { createSubagentWorktreeHelper, forkGovernanceDenial } from "../../agents
20
20
  import { createAgentTranscriptTool, AGENT_TRANSCRIPT_TOOL_NAME } from "../../agents/agent-transcript-tool.js";
21
21
  import { createSendMessageTool, SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
22
22
  import { SubagentRetainLedger } from "../../agents/retain-ledger.js";
23
- import { askApproverIdentity, combinePolicies, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
23
+ import { askApproverIdentity, combinePolicies, isApprovalSettledBy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, describeThrown, refuseOutOfContractDecision, resolveAsk, toolPolicyNameSets, tryCloneArgs } from "../tool-policy.js";
24
24
  const PERSISTED_RULE_TOOL = "Bash";
25
25
  import { findAdmittingRule, suggestRulesForCommand } from "../permission-rule-model.js";
26
26
  import { ActiveSkillScope, createActiveSkillScopePolicy } from "./active-skill-scope.js";
@@ -3304,6 +3304,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
3304
3304
  }
3305
3305
  const preToolContexts = new Map();
3306
3306
  const blockedToolCalls = new Set();
3307
+ const approvalSettledBy = new Map();
3307
3308
  const blockedTracked = Boolean(hooks?.postToolUse || hooks?.preToolUse || hooks?.postToolUseFailure || hooks?.postToolBatch);
3308
3309
  const restoreSurfaceGap = ownedEnv !== undefined && isRemoteExecutionEnv(ownedEnv) && ownedEnv.capabilities.suspendable ? missingRestoreSurface(ownedEnv) : [];
3309
3310
  const incompleteSuspendAdapter = restoreSurfaceGap.length > 0 ? restoreSurfaceGap : undefined;
@@ -4156,6 +4157,17 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4156
4157
  if (blockedTracked && (result.block || result.suspend)) {
4157
4158
  blockedToolCalls.add(e.toolCallId);
4158
4159
  }
4160
+ const reported = result.settledBy;
4161
+ if (reported !== undefined) {
4162
+ const settling = result.block === true;
4163
+ if (!isApprovalSettledBy(reported) || (!settling && reported !== "human")) {
4164
+ deps.onError?.(new Error(`a tool-gate settlement reported settledBy "${String(reported)}" on ${settling ? "a blocked" : "an executing"} call — ` +
4165
+ `it is one of "human" / "timeout" / "aborted", and only "human" can be the source of a call that runs; the frame carries no source`), { phase: "config", sessionId });
4166
+ }
4167
+ else {
4168
+ approvalSettledBy.set(e.toolCallId, reported);
4169
+ }
4170
+ }
4159
4171
  return result.block
4160
4172
  ? { block: true, reason: result.reason }
4161
4173
  : result.updatedInput !== undefined
@@ -4444,7 +4456,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
4444
4456
  : undefined;
4445
4457
  overheadState.promptChars = systemPrompt.length;
4446
4458
  const preparedHolder = {};
4447
- 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, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), 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 } : {}) });
4459
+ const buildPrepared = () => ({ harness, session, sessionId, taskRootPath, model, thinking, compModel, mcp: mcp, ...(a2a !== undefined && a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, approvalSettledBy, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), cwdRef: handsCwdRef, ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), denyNarrowingPolicy, ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), 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 } : {}) });
4448
4460
  const prepared = buildPrepared();
4449
4461
  preparedHolder.current = prepared;
4450
4462
  return prepared;
@@ -5,6 +5,7 @@ import { type TaskOutcome } from "../task-outcome.js";
5
5
  import { type SideQuerySpec, type SideQueryResult } from "../side-query.js";
6
6
  import type { SessionStore } from "../session.js";
7
7
  import { type RecoveredOrphan } from "../session-reconcile.js";
8
+ import { type ApprovalSettledBy } from "../tool-policy.js";
8
9
  import type { AgentDefinition, RunnerDeps, TaskEvent, TaskResult, TaskSpec, TaskStream } from "../types.js";
9
10
  export type ResumeTaskConfig = Omit<TaskSpec, "objective" | "sessionId">;
10
11
  interface ResumeRun {
@@ -17,12 +18,13 @@ interface ResumeRun {
17
18
  onEnvRestoreFailed?: (reason: ReopenReason) => Promise<void>;
18
19
  decisionDelivered?: boolean;
19
20
  }
20
- declare function toolEndBodyFrom(result: unknown, isError: boolean): {
21
+ declare function toolEndBodyFrom(result: unknown, isError: boolean, settledBy?: ApprovalSettledBy): {
21
22
  output?: unknown;
22
23
  truncated?: boolean;
23
24
  totalChars?: number;
24
25
  structured?: unknown;
25
26
  errorCode?: string;
27
+ settledBy?: ApprovalSettledBy;
26
28
  };
27
29
  export declare function reconciledToolEndBody(orphan: Pick<RecoveredOrphan, "text" | "errorKind">): ReturnType<typeof toolEndBodyFrom>;
28
30
  export declare const GOVERNANCE_READ_STALLED: unique symbol;
@@ -38,7 +38,7 @@ import { delimitUntrusted, inlineUntrusted, sanitizeUntrustedText } from "../unt
38
38
  import { reconcileInterruptedSession } from "../session-reconcile.js";
39
39
  import { RunnerSharedToolResultStore } from "../tool-result-store.js";
40
40
  import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
41
- import { refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
41
+ import { isApprovalSettledBy, refuseOutOfContractDecision, toolPolicyNameSets } from "../tool-policy.js";
42
42
  import { defaultTaskRegistry } from "../task-registry.js";
43
43
  import { discloseDroppedPending, isDelegatedAgentTerminal, PendingSessionNotifications, renderTaskNotificationXml, SystemInjectionQueue, taskNotificationDedupKey } from "../task-notification.js";
44
44
  import { ToolDetachHub } from "../tool-detach.js";
@@ -126,7 +126,7 @@ function resumeDecisionWasNegative(resume) {
126
126
  }
127
127
  const DEFERRED_REISSUE = "[DEFERRED] This tool call shared a batch with a call that suspended for durable approval, so it was " +
128
128
  "NOT executed on resume. If you still need it, issue it again now.";
129
- function toolEndBodyFrom(result, isError) {
129
+ function toolEndBodyFrom(result, isError, settledBy) {
130
130
  const o = toolOutputFrom(result);
131
131
  const st = structuredFrom(result);
132
132
  const code = isError ? result?.details?.code : undefined;
@@ -134,6 +134,7 @@ function toolEndBodyFrom(result, isError) {
134
134
  ...(o !== undefined ? { output: o.output, ...(o.truncated ? { truncated: true } : {}), ...(o.totalChars !== undefined ? { totalChars: o.totalChars } : {}) } : {}),
135
135
  ...(st !== undefined ? { structured: st } : {}),
136
136
  ...(typeof code === "string" ? { errorCode: code } : {}),
137
+ ...(settledBy !== undefined ? { settledBy } : {}),
137
138
  };
138
139
  }
139
140
  export function reconciledToolEndBody(orphan) {
@@ -1202,13 +1203,16 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
1202
1203
  prepared.lspDiagnostics.nudge(p);
1203
1204
  }
1204
1205
  }
1206
+ const settledBy = prepared.approvalSettledBy.get(event.toolCallId);
1207
+ if (settledBy !== undefined)
1208
+ prepared.approvalSettledBy.delete(event.toolCallId);
1205
1209
  pushContent({
1206
1210
  type: "tool_end",
1207
1211
  toolCallId: event.toolCallId,
1208
1212
  toolName: event.toolName,
1209
1213
  ...(toolLabels.get(event.toolName) !== undefined ? { label: toolLabels.get(event.toolName) } : {}),
1210
1214
  isError: event.isError,
1211
- ...toolEndBodyFrom(event.result, event.isError),
1215
+ ...toolEndBodyFrom(event.result, event.isError, settledBy),
1212
1216
  ...ident(),
1213
1217
  });
1214
1218
  announceWorkspaceMove();
@@ -3617,6 +3621,14 @@ export class Runner {
3617
3621
  if (decision !== "allow" && decision !== "deny") {
3618
3622
  throw new CheckpointError("checkpoint.invalid_outcome", `resume decision "${describeSuppliedValue(decision)}" is outside the policy_ask domain — a decide is exactly "allow" or "deny"; refusing pre-CAS, the checkpoint stays pending`);
3619
3623
  }
3624
+ const settledBy = decide.settledBy;
3625
+ if (settledBy !== undefined && !isApprovalSettledBy(settledBy)) {
3626
+ throw new CheckpointError("checkpoint.invalid_outcome", `resume settledBy "${describeSuppliedValue(settledBy)}" is outside the settlement vocabulary — it is exactly "human", "timeout" or "aborted" (or omitted); refusing pre-CAS, the checkpoint stays pending`, { field: "settledBy" });
3627
+ }
3628
+ if (settledBy !== undefined && settledBy !== "human" && decision === "allow") {
3629
+ throw new CheckpointError("checkpoint.invalid_outcome", `resume carries decision "allow" with settledBy "${settledBy}" — a non-human settlement is a fail-closed end (nobody answered), so it cannot be the source of an approval that EXECUTES; ` +
3630
+ "supply \"human\", or omit the field if the allow came from configuration rather than a person; refusing pre-CAS, the checkpoint stays pending", { field: "settledBy" });
3631
+ }
3620
3632
  const reason = decide.reason;
3621
3633
  assertOutcomeText(reason, "reason");
3622
3634
  plainPolicyOutcome = {
@@ -3627,6 +3639,7 @@ export class Runner {
3627
3639
  ...(updatedInput !== undefined ? { updatedInput } : {}),
3628
3640
  ...(reason !== undefined ? { reason } : {}),
3629
3641
  ...(redeemedAnswer !== undefined ? { answer: redeemedAnswer } : {}),
3642
+ ...(settledBy !== undefined ? { settledBy } : {}),
3630
3643
  };
3631
3644
  if (plainPolicyOutcome.decision === "deny" && plainPolicyOutcome.reason && sanitizeUntrustedText(plainPolicyOutcome.reason) !== plainPolicyOutcome.reason) {
3632
3645
  throw new CheckpointError("checkpoint.invalid_outcome", "resume deny/reject reason must not contain a </system-reminder> tag");
@@ -3823,9 +3836,14 @@ export class Runner {
3823
3836
  const resolvedArgs = pendingAction.toolName === ASK_USER_QUESTION_TOOL_NAME ? pendingAction.args : (outcome.updatedInput !== undefined ? outcome.updatedInput : pendingAction.args);
3824
3837
  const pendingLabel = (() => { const l = prepared.tools.find((t) => t.name === pendingAction.toolName)?.label; return l !== undefined && l !== pendingAction.toolName ? { label: l } : {}; })();
3825
3838
  emit({ type: "tool_start", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, args: resolvedArgs });
3826
- const emitEnd = (isError, result) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError) });
3839
+ const emitEnd = (isError, result) => emit({ type: "tool_end", toolCallId: pendingAction.toolCallId, toolName: pendingAction.toolName, ...pendingLabel, isError, ...toolEndBodyFrom(result, isError, outcome.settledBy) });
3827
3840
  if (outcome.decision === "deny") {
3828
- const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason) : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
3841
+ const defaultDenial = outcome.settledBy === "timeout"
3842
+ ? `No one answered the approval request for the pending tool call "${pendingAction.toolName}" — the approval window elapsed with no answer, so it was not executed.`
3843
+ : outcome.settledBy === "aborted"
3844
+ ? `The approval for the pending tool call "${pendingAction.toolName}" ended without anyone deciding it (it was cancelled or could not be delivered), so it was not executed.`
3845
+ : `The pending tool call "${pendingAction.toolName}" was denied by an approver.`;
3846
+ const reason = outcome.reason ? delimitUntrusted("reviewer note", outcome.reason) : defaultDenial;
3829
3847
  emitEnd(true, { content: formatHookFeedback(reason) });
3830
3848
  const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(reason), true));
3831
3849
  emitCommitted(eid, "toolResult", pendingAction.toolCallId);
@@ -9,11 +9,15 @@ export interface ToolCallRequest {
9
9
  };
10
10
  }
11
11
  export type DecisionReason = "rule" | "mode" | "hook" | "safety" | "classifier" | "persisted_rule";
12
+ export type ApprovalSettledBy = "human" | "timeout" | "aborted";
13
+ export declare const APPROVAL_SETTLED_BY_VALUES: readonly ApprovalSettledBy[];
14
+ export declare function isApprovalSettledBy(v: unknown): v is ApprovalSettledBy;
12
15
  export type PermissionResult = {
13
16
  action: "allow";
14
17
  updatedInput?: unknown;
15
18
  message?: string;
16
19
  decisionReason?: DecisionReason;
20
+ settledBy?: Extract<ApprovalSettledBy, "human">;
17
21
  } | {
18
22
  action: "ask";
19
23
  updatedInput?: unknown;
@@ -25,6 +29,7 @@ export type PermissionResult = {
25
29
  updatedInput?: unknown;
26
30
  message?: string;
27
31
  decisionReason?: DecisionReason;
32
+ settledBy?: ApprovalSettledBy;
28
33
  };
29
34
  export declare function decisionText(d: PermissionResult): string | undefined;
30
35
  export interface ToolPolicy {
@@ -5,6 +5,10 @@ import { boundInputHashOf } from "./canonical-json.js";
5
5
  import { inlineUntrusted } from "./untrusted-text.js";
6
6
  import { parsePermissionRule } from "./permission-rules.js";
7
7
  import { writeTargetPath } from "../tools/fs/safety.js";
8
+ export const APPROVAL_SETTLED_BY_VALUES = ["human", "timeout", "aborted"];
9
+ export function isApprovalSettledBy(v) {
10
+ return typeof v === "string" && APPROVAL_SETTLED_BY_VALUES.includes(v);
11
+ }
8
12
  export function decisionText(d) {
9
13
  return d.message;
10
14
  }
@@ -19,6 +23,7 @@ export function refuseOutOfContractDecision(d) {
19
23
  return d;
20
24
  return { action: "deny", message: RETIRED_TEXT_FIELD_DENY_MESSAGE, decisionReason: "rule" };
21
25
  }
26
+ const DEADLINE_ELAPSED = Symbol("approval.deadline_elapsed");
22
27
  function withTimeout(p, ms, onTimeout) {
23
28
  if (ms === undefined) {
24
29
  return p;
@@ -51,6 +56,18 @@ export function createAllowDenyPolicy(opts) {
51
56
  return undefined;
52
57
  const kept = [];
53
58
  for (const entry of entries) {
59
+ if (entry.startsWith("mcp__")) {
60
+ const segments = entry.slice("mcp__".length).split("__");
61
+ if (segments.some((seg) => seg.length === 0)) {
62
+ invalid.push({
63
+ entry,
64
+ list,
65
+ message: `"${entry}" is a malformed MCP tool name (empty segment) — it can never match any mounted tool. ` +
66
+ `Use \`mcp__<server>\` for every tool of a server, or \`mcp__<server>__<tool>\` for one tool.`,
67
+ });
68
+ continue;
69
+ }
70
+ }
54
71
  if (parsePermissionRule(entry).ruleContent === undefined) {
55
72
  kept.push(entry);
56
73
  continue;
@@ -118,25 +135,37 @@ export function createApprovalPolicy(opts) {
118
135
  }
119
136
  if (need.has(toolName)) {
120
137
  if (signal?.aborted) {
121
- return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)` };
138
+ return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)`, settledBy: "aborted" };
122
139
  }
123
140
  let ok;
124
141
  try {
125
- ok = await withTimeout(Promise.resolve(opts.approve(req, signal)), opts.approvalTimeoutMs, () => false);
142
+ ok = await withTimeout(Promise.resolve(opts.approve(req, signal)), opts.approvalTimeoutMs, () => DEADLINE_ELAPSED);
126
143
  }
127
144
  catch (err) {
128
145
  return {
129
146
  action: "deny",
130
147
  message: `approval errored for "${req.toolName}": ${describeThrown(err)}`,
148
+ settledBy: "aborted",
149
+ };
150
+ }
151
+ if (ok === DEADLINE_ELAPSED) {
152
+ return {
153
+ action: "deny",
154
+ message: `no one answered the approval request for "${req.toolName}" — the approval window elapsed ` +
155
+ `(${opts.approvalTimeoutMs}ms) with no answer; denied fail-closed`,
156
+ settledBy: "timeout",
131
157
  };
132
158
  }
159
+ if (signal?.aborted) {
160
+ return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)`, settledBy: "aborted" };
161
+ }
133
162
  const okRaw = ok;
134
163
  if (okRaw === true)
135
- return ALLOW;
164
+ return { action: "allow", settledBy: "human" };
136
165
  if (okRaw !== false) {
137
- return { action: "deny", message: `approval callback for "${req.toolName}" returned an out-of-contract value (${typeof ok}) — denied fail-closed (this policy's \`approve\` returns a boolean: return true or false)` };
166
+ return { action: "deny", message: `approval callback for "${req.toolName}" returned an out-of-contract value (${typeof ok}) — denied fail-closed (this policy's \`approve\` returns a boolean: return true or false)`, settledBy: "aborted" };
138
167
  }
139
- return { action: "deny", message: `approval denied for "${req.toolName}"` };
168
+ return { action: "deny", message: `approval denied for "${req.toolName}"`, settledBy: "human" };
140
169
  }
141
170
  if (opts.denyByDefault && !auto.has(toolName)) {
142
171
  return { action: "deny", message: `tool "${req.toolName}" requires explicit allow` };
@@ -153,6 +182,7 @@ export function combinePolicies(...policies) {
153
182
  let asked;
154
183
  let current = req;
155
184
  let rewrite;
185
+ let settled;
156
186
  for (const p of policies) {
157
187
  const d = refuseOutOfContractDecision(await p.check(current, signal));
158
188
  if (d.action === "deny") {
@@ -162,6 +192,11 @@ export function combinePolicies(...policies) {
162
192
  current = { ...current, args: d.updatedInput };
163
193
  rewrite = d;
164
194
  }
195
+ if (d.action === "allow") {
196
+ const supplied = d.settledBy;
197
+ if (supplied !== undefined)
198
+ settled = supplied;
199
+ }
165
200
  if (d.action === "ask" && (asked === undefined || (d.requiresRealApproval === true && asked.requiresRealApproval !== true))) {
166
201
  asked = d;
167
202
  }
@@ -170,7 +205,8 @@ export function combinePolicies(...policies) {
170
205
  const merged = rewrite?.updatedInput;
171
206
  return merged !== undefined ? { ...asked, updatedInput: merged } : asked;
172
207
  }
173
- return rewrite ?? ALLOW;
208
+ const allowed = rewrite ?? ALLOW;
209
+ return settled !== undefined ? { ...allowed, settledBy: settled } : allowed;
174
210
  },
175
211
  };
176
212
  }
@@ -643,7 +679,7 @@ export async function resolveAsk(req, onAsk, signal) {
643
679
  };
644
680
  }
645
681
  if (signal?.aborted) {
646
- return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)`, decisionReason: "mode" };
682
+ return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)`, decisionReason: "mode", settledBy: "aborted" };
647
683
  }
648
684
  const presented = tryCloneArgs(req.args);
649
685
  if (!presented.ok) {
@@ -651,6 +687,7 @@ export async function resolveAsk(req, onAsk, signal) {
651
687
  action: "deny",
652
688
  message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${presented.reason}) — denied fail-closed`,
653
689
  decisionReason: "mode",
690
+ settledBy: "aborted",
654
691
  };
655
692
  }
656
693
  let ok;
@@ -661,6 +698,7 @@ export async function resolveAsk(req, onAsk, signal) {
661
698
  action: "deny",
662
699
  message: `approval for "${req.toolName}" could not present the args safely (unclonable value: ${approverView.reason}) — denied fail-closed`,
663
700
  decisionReason: "mode",
701
+ settledBy: "aborted",
664
702
  };
665
703
  }
666
704
  ok = await onAsk({ ...req, boundInputHash: boundInputHashOf(presented.value), args: approverView.value }, signal);
@@ -670,42 +708,49 @@ export async function resolveAsk(req, onAsk, signal) {
670
708
  action: "deny",
671
709
  message: `approval errored for "${req.toolName}": ${describeThrown(err)}`,
672
710
  decisionReason: "mode",
711
+ settledBy: "aborted",
673
712
  };
674
713
  }
714
+ if (signal?.aborted) {
715
+ return { action: "deny", message: `approval aborted for "${req.toolName}" (task ended)`, decisionReason: "mode", settledBy: "aborted" };
716
+ }
675
717
  if (ok === "unavailable") {
676
718
  return {
677
719
  action: "deny",
678
720
  message: `approval required for "${req.toolName}" but the approver reported unavailable (no reachable ` +
679
721
  `operator for this ask) and no durable approval gate is armed — denied fail-closed: ${req.message}`,
680
722
  decisionReason: "mode",
723
+ settledBy: "aborted",
681
724
  approverUnavailable: true,
682
725
  };
683
726
  }
684
727
  if (typeof ok === "object" && ok !== null) {
685
728
  if (ok.allow !== true) {
686
- return { action: "deny", message: `approval denied for "${req.toolName}": ${req.message}`, decisionReason: "mode" };
729
+ return { action: "deny", message: `approval denied for "${req.toolName}": ${req.message}`, decisionReason: "mode", settledBy: "human" };
687
730
  }
688
731
  if (ok.updatedInput === undefined)
689
- return { action: "allow", decisionReason: "mode", presentedInput: presented.value };
732
+ return { action: "allow", decisionReason: "mode", presentedInput: presented.value, settledBy: "human" };
690
733
  const edit = tryCloneArgs(ok.updatedInput);
691
734
  if (!edit.ok) {
692
735
  return {
693
736
  action: "deny",
694
737
  message: `the approved edit for "${req.toolName}" is not safely clonable (${edit.reason}) — denied fail-closed`,
695
738
  decisionReason: "mode",
739
+ settledBy: "aborted",
696
740
  };
697
741
  }
698
- return { action: "allow", updatedInput: edit.value, decisionReason: "mode" };
742
+ return { action: "allow", updatedInput: edit.value, decisionReason: "mode", settledBy: "human" };
699
743
  }
700
744
  const okRaw = ok;
701
745
  if (okRaw === true)
702
- return { action: "allow", decisionReason: "mode", presentedInput: presented.value };
746
+ return { action: "allow", decisionReason: "mode", presentedInput: presented.value, settledBy: "human" };
703
747
  if (okRaw !== false) {
704
748
  return {
705
749
  action: "deny",
706
750
  message: `the approver for "${req.toolName}" returned an out-of-contract value (${typeof ok}) — denied fail-closed (return true, false, "unavailable", or the {allow} object)`,
707
751
  decisionReason: "mode",
752
+ settledBy: "aborted",
708
753
  };
709
754
  }
710
- return { action: "deny", message: `approval denied for "${req.toolName}": ${req.message}`, decisionReason: "mode" };
755
+ return { action: "deny", message: `approval denied for "${req.toolName}": ${req.message}`, decisionReason: "mode", settledBy: "human" };
711
756
  }
@@ -568,6 +568,7 @@ export type TaskEvent = ({
568
568
  output?: unknown;
569
569
  structured?: unknown;
570
570
  errorCode?: string;
571
+ settledBy?: import("./tool-policy.js").ApprovalSettledBy;
571
572
  truncated?: boolean;
572
573
  totalChars?: number;
573
574
  } & TaskEventIdentity) | ({
package/dist/index.d.ts CHANGED
@@ -122,14 +122,14 @@ export type { SchedulerCapability, SchedulerErrorCode, ScheduledIntent, Schedule
122
122
  export { createSchedulerTools, type SchedulerToolContext, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
123
123
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, type AutonomousLoopPromptOptions, } from "./tools/loop-tick.js";
124
124
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
125
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, } from "./core/tool-policy.js";
125
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, type ToolPolicyNameSets, type NamedToolPolicy, type ToolPolicy, type ToolCallRequest, type PermissionResult, type DecisionReason, type ApprovalSettledBy, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, type OnAsk, type AskOutcome, type ResolvedAsk, type AskRequest, type AskDelegationProvenance, } from "./core/tool-policy.js";
126
126
  export { parseAutoModeResponse, createAutoModeDecider, type AutoModeVerdict, type AutoModeDecider, type AutoModeDeciderOptions, type AutoModeClassifyFn, type AutoModeClassifyInput, } from "./core/auto-mode.js";
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
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
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";
132
+ export { prepareCardApproval, confirmRuleApproval, type ConfirmResult, type ConfirmRefusalReason, 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
133
  export { FilePermissionRuleStoreProvider } from "./stores/file/permission-rule-store.js";
134
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";
135
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";
package/dist/index.js CHANGED
@@ -107,7 +107,7 @@ export { hasScheduler, isValidCronExpr, SchedulerError } from "./core/scheduler.
107
107
  export { createSchedulerTools, SCHEDULE_WAKEUP_TOOL_NAME, AUTONOMOUS_LOOP_SENTINEL, AUTONOMOUS_LOOP_DYNAMIC_SENTINEL, } from "./tools/scheduler-tools.js";
108
108
  export { resolveAutonomousLoopPrompt, AUTONOMOUS_LOOP_PREAMBLE, AUTONOMOUS_LOOP_PREAMBLE_PERSISTENT, } from "./tools/loop-tick.js";
109
109
  export { tightenTaskSpec, TaskSpecTightenError } from "./core/tighten-task-spec.js";
110
- export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, } from "./core/tool-policy.js";
110
+ export { createAllowDenyPolicy, createApprovalPolicy, COARSE_SHELL_TOOLS, createCoarseCommandNamePolicy, createTranscriptIntegrityPolicy, createUnverifiableDeletePolicy, findUnverifiableRecursiveDelete, combinePolicies, decisionText, resolveAsk, toolPolicyNameSets, APPROVAL_SETTLED_BY_VALUES, isApprovalSettledBy, } from "./core/tool-policy.js";
111
111
  export { parseAutoModeResponse, createAutoModeDecider, } from "./core/auto-mode.js";
112
112
  export { buildAutoModePrompt, renderAutoModeWindow, renderAutoModeAction, AUTO_MODE_DEFAULTS_SENTINEL, } from "./core/auto-mode-prompt.js";
113
113
  export { AUTO_MODE_BASE_PROMPT, AUTO_MODE_PERMISSIONS_EXTERNAL } from "./core/auto-mode-prompt-assets.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sema-agent/core",
3
- "version": "5.18.0",
3
+ "version": "5.18.1",
4
4
  "description": "Stateless, task-oriented AI agent core",
5
5
  "type": "module",
6
6
  "license": "BUSL-1.1",