@sema-agent/core 5.0.0 → 5.1.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.
- package/CHANGELOG.md +50 -0
- package/dist/agents/repair-loop.js +9 -1
- package/dist/agents/roster-store.d.ts +9 -2
- package/dist/agents/roster-store.js +26 -5
- package/dist/agents/send-message-tool.js +1 -1
- package/dist/agents/subagent.js +15 -5
- package/dist/bin/sema-tb.d.ts +1 -2
- package/dist/bin/sema-tb.js +13 -25
- package/dist/brain/anthropic.js +2 -3
- package/dist/brain/degrading.d.ts +2 -8
- package/dist/brain/degrading.js +3 -3
- package/dist/brain/terminal-cause.js +2 -8
- package/dist/core/hooks.js +4 -4
- package/dist/core/lsp.js +2 -3
- package/dist/core/mcp.d.ts +1 -1
- package/dist/core/mcp.js +2 -1
- package/dist/core/memory-engine/engine.js +2 -2
- package/dist/core/memory-engine/file-backend.js +3 -1
- package/dist/core/memory-engine/layout.d.ts +3 -0
- package/dist/core/memory-engine/layout.js +51 -0
- package/dist/core/memory.d.ts +0 -1
- package/dist/core/memory.js +1 -1
- package/dist/core/permission-rules.d.ts +2 -2
- package/dist/core/permission-rules.js +18 -7
- package/dist/core/protocol-table.d.ts +14 -0
- package/dist/core/protocol-table.js +23 -0
- package/dist/core/runner/active-skill-scope.js +7 -7
- package/dist/core/runner/prepare-memory.js +4 -1
- package/dist/core/runner/prepare-task.js +38 -25
- package/dist/core/runner/runtask.js +9 -3
- package/dist/core/runner/session-rule-policy.d.ts +2 -2
- package/dist/core/runner/session-rule-policy.js +2 -1
- package/dist/core/runner/synthetic-tools.js +2 -2
- package/dist/core/runner/tool-disclosure.d.ts +0 -1
- package/dist/core/runner/tool-disclosure.js +0 -2
- package/dist/core/runner/turn-attachments.js +2 -1
- package/dist/core/sensitive-path-policy.js +2 -2
- package/dist/core/tool-policy.d.ts +0 -3
- package/dist/core/tool-policy.js +29 -15
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/orchestration/run-spec.js +1 -1
- package/dist/orchestration/run-workflow-tool.js +2 -2
- package/dist/orchestration/workflow-script-store.d.ts +1 -1
- package/dist/orchestration/workflow-script-store.js +1 -1
- package/dist/prompt-assembly/assemble.js +0 -1
- package/dist/prompt-assembly/tool-catalog.d.ts +2 -1
- package/dist/prompts/default.d.ts +0 -2
- package/dist/prompts/default.js +1 -5
- package/dist/stores/cc/mailbox-store.d.ts +4 -0
- package/dist/stores/cc/mailbox-store.js +16 -2
- package/dist/stores/file/session-policy-store.d.ts +10 -1
- package/dist/stores/file/session-policy-store.js +20 -4
- package/dist/tools/fs/fs-search-tools.js +1 -2
- package/dist/tools/fs/fs-shared.d.ts +0 -1
- package/dist/tools/fs/fs-shared.js +0 -1
- package/dist/tools/todo.js +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NamedToolPolicy } from "./tool-policy.js";
|
|
2
2
|
export declare const BASH_GENERIC_PARAMS: ReadonlySet<string>;
|
|
3
3
|
export interface PermissionRule {
|
|
4
4
|
rule: string;
|
|
@@ -32,4 +32,4 @@ export declare function parsePermissionRule(rule: string): ParsedPermissionRule;
|
|
|
32
32
|
export declare function wildcardMatch(pattern: string, value: string): boolean;
|
|
33
33
|
export declare const fileToolParamVocabulary: (canonicalTool: string) => ReadonlySet<string> | undefined;
|
|
34
34
|
export declare function validatePermissionRules(rules: PermissionRule[], opts?: Pick<PermissionRulePolicyOptions, "primaryFieldGeneric" | "caps">): PermissionRuleIssue[];
|
|
35
|
-
export declare function createPermissionRulePolicy(rules: PermissionRule[], opts?: PermissionRulePolicyOptions):
|
|
35
|
+
export declare function createPermissionRulePolicy(rules: PermissionRule[], opts?: PermissionRulePolicyOptions): NamedToolPolicy;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { MCP_NAMESPACE } from "./protocol-table.js";
|
|
1
2
|
export const BASH_GENERIC_PARAMS = new Set(["command", "timeout", "description", "run_in_background"]);
|
|
2
3
|
const DEFAULT_CAPS = {
|
|
3
4
|
maxRules: 256,
|
|
@@ -95,7 +96,7 @@ const FILE_TOOL_PARAMS = {
|
|
|
95
96
|
Write: new Set(["file_path", "path", "content"]),
|
|
96
97
|
NotebookEdit: new Set(["notebook_path", "path", "cell_id", "cell_type", "edit_mode", "new_source"]),
|
|
97
98
|
Grep: new Set([
|
|
98
|
-
"pattern", "path", "glob", "type", "output_mode", "head_limit", "
|
|
99
|
+
"pattern", "path", "glob", "type", "output_mode", "head_limit", "offset",
|
|
99
100
|
"multiline", "context", "ignore_case", "-i", "-n", "-o", "-A", "-B", "-C",
|
|
100
101
|
]),
|
|
101
102
|
Glob: new Set(["pattern", "path", "max_results"]),
|
|
@@ -142,7 +143,7 @@ function compile(rules, caps, primaryFieldGeneric) {
|
|
|
142
143
|
}
|
|
143
144
|
}
|
|
144
145
|
const toolName = parsed.toolName;
|
|
145
|
-
if (toolName.startsWith(
|
|
146
|
+
if (toolName.startsWith(MCP_NAMESPACE.prefix) && (parsed.ruleContent !== undefined || indexOfUnescaped(text, "(") !== -1)) {
|
|
146
147
|
bad(text, "unsupported.mcp_paren", "MCP rules do not support patterns in parentheses (CC parity); use the toolAxes system");
|
|
147
148
|
continue;
|
|
148
149
|
}
|
|
@@ -259,23 +260,33 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
259
260
|
}
|
|
260
261
|
}
|
|
261
262
|
const defaultAction = opts?.defaultAction ?? "allow";
|
|
263
|
+
const nameSets = { deny: [], ask: [], allow: [] };
|
|
264
|
+
for (const [toolName, entry] of byTool) {
|
|
265
|
+
if (entry.bare.deny !== undefined || entry.param.deny.length > 0)
|
|
266
|
+
nameSets.deny.push(toolName);
|
|
267
|
+
if (entry.bare.ask !== undefined || entry.param.ask.length > 0)
|
|
268
|
+
nameSets.ask.push(toolName);
|
|
269
|
+
if (entry.bare.allow !== undefined)
|
|
270
|
+
nameSets.allow.push(toolName);
|
|
271
|
+
}
|
|
262
272
|
return {
|
|
273
|
+
nameSets: [nameSets],
|
|
263
274
|
check(req) {
|
|
264
275
|
const entry = byTool.get(req.toolName);
|
|
265
276
|
if (entry) {
|
|
266
277
|
if (entry.bare.deny) {
|
|
267
|
-
return { action: "deny",
|
|
278
|
+
return { action: "deny", message: ruleMessage("denied", entry.bare.deny.ruleText, entry.bare.deny.source) };
|
|
268
279
|
}
|
|
269
280
|
const paramDeny = matchParamRules(entry.param.deny, req.args, caps.maxScalarValueChars);
|
|
270
281
|
if (paramDeny) {
|
|
271
|
-
return { action: "deny",
|
|
282
|
+
return { action: "deny", message: ruleMessage("denied", paramDeny.ruleText, paramDeny.source) };
|
|
272
283
|
}
|
|
273
284
|
if (entry.bare.ask) {
|
|
274
|
-
return { action: "ask",
|
|
285
|
+
return { action: "ask", message: ruleMessage("flagged", entry.bare.ask.ruleText, entry.bare.ask.source) };
|
|
275
286
|
}
|
|
276
287
|
const paramAsk = matchParamRules(entry.param.ask, req.args, caps.maxScalarValueChars);
|
|
277
288
|
if (paramAsk) {
|
|
278
|
-
return { action: "ask",
|
|
289
|
+
return { action: "ask", message: ruleMessage("flagged", paramAsk.ruleText, paramAsk.source) };
|
|
279
290
|
}
|
|
280
291
|
if (entry.bare.allow) {
|
|
281
292
|
return { action: "allow" };
|
|
@@ -283,7 +294,7 @@ export function createPermissionRulePolicy(rules, opts) {
|
|
|
283
294
|
}
|
|
284
295
|
if (defaultAction === "allow")
|
|
285
296
|
return { action: "allow" };
|
|
286
|
-
return { action: defaultAction,
|
|
297
|
+
return { action: defaultAction, message: `no permission rule matched "${req.toolName}" (default-${defaultAction} rule set)` };
|
|
287
298
|
},
|
|
288
299
|
};
|
|
289
300
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ProtocolId = "mcp";
|
|
2
|
+
export interface ProtocolNamespace {
|
|
3
|
+
readonly id: ProtocolId;
|
|
4
|
+
readonly prefix: string;
|
|
5
|
+
makeName(peer: string, tool: string): string;
|
|
6
|
+
parse(name: string): {
|
|
7
|
+
peer: string;
|
|
8
|
+
tool: string;
|
|
9
|
+
} | undefined;
|
|
10
|
+
displayGroupKey(name: string): string;
|
|
11
|
+
}
|
|
12
|
+
export declare const MCP_NAMESPACE: ProtocolNamespace;
|
|
13
|
+
export declare const PROTOCOL_TABLE: readonly ProtocolNamespace[];
|
|
14
|
+
export declare function protocolOf(name: string): ProtocolNamespace | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const NAME_SEP = "__";
|
|
2
|
+
export const MCP_NAMESPACE = {
|
|
3
|
+
id: "mcp",
|
|
4
|
+
prefix: `mcp${NAME_SEP}`,
|
|
5
|
+
makeName: (peer, tool) => `mcp${NAME_SEP}${peer}${NAME_SEP}${tool}`,
|
|
6
|
+
parse(name) {
|
|
7
|
+
if (!name.startsWith(this.prefix))
|
|
8
|
+
return undefined;
|
|
9
|
+
const rest = name.slice(this.prefix.length);
|
|
10
|
+
const sep = rest.indexOf(NAME_SEP);
|
|
11
|
+
if (sep <= 0)
|
|
12
|
+
return undefined;
|
|
13
|
+
return { peer: rest.slice(0, sep), tool: rest.slice(sep + NAME_SEP.length) };
|
|
14
|
+
},
|
|
15
|
+
displayGroupKey(name) {
|
|
16
|
+
const p = this.parse(name);
|
|
17
|
+
return p === undefined ? name : `${this.prefix}${p.peer}${NAME_SEP}*`;
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
export const PROTOCOL_TABLE = [MCP_NAMESPACE];
|
|
21
|
+
export function protocolOf(name) {
|
|
22
|
+
return PROTOCOL_TABLE.find((ns) => name.startsWith(ns.prefix));
|
|
23
|
+
}
|
|
@@ -28,7 +28,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
28
28
|
if (unresolved && unresolved.kind === "unresolved") {
|
|
29
29
|
return {
|
|
30
30
|
action: "deny",
|
|
31
|
-
|
|
31
|
+
message: `tool "${req.toolName}" denied: active skill artifact "${unresolved.lineageId}" manifest could not be resolved (${unresolved.reason}) — fail-closed deny-all`,
|
|
32
32
|
decisionReason: "safety",
|
|
33
33
|
};
|
|
34
34
|
}
|
|
@@ -49,7 +49,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
49
49
|
const ids = manifests.map((m) => m.lineageId).join(", ");
|
|
50
50
|
return {
|
|
51
51
|
action: "deny",
|
|
52
|
-
|
|
52
|
+
message: `tool "${req.toolName}" is not in the active skill manifest allowlist (artifact(s): ${ids})`,
|
|
53
53
|
decisionReason: "safety",
|
|
54
54
|
};
|
|
55
55
|
}
|
|
@@ -68,7 +68,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
68
68
|
if (!admitted) {
|
|
69
69
|
return {
|
|
70
70
|
action: "deny",
|
|
71
|
-
|
|
71
|
+
message: `tool "${req.toolName}" is narrowed by skill manifest "${frame.manifest.lineageId}": ${rejections.join("; ")}`,
|
|
72
72
|
decisionReason: "safety",
|
|
73
73
|
};
|
|
74
74
|
}
|
|
@@ -80,7 +80,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
80
80
|
const ids = manifests.filter((m) => m.allowPaths?.length).map((m) => m.lineageId).join(", ");
|
|
81
81
|
return {
|
|
82
82
|
action: "deny",
|
|
83
|
-
|
|
83
|
+
message: `write-capable tool "${req.toolName}" denied: active skill manifest constrains writes to allowPaths but this tool's target cannot be path-confined (only ${[...PATH_WRITE_TOOLS].join("/")} are) — fail-closed (artifact(s): ${ids})`,
|
|
84
84
|
decisionReason: "safety",
|
|
85
85
|
};
|
|
86
86
|
}
|
|
@@ -92,7 +92,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
92
92
|
if (typeof path !== "string" || path.length === 0) {
|
|
93
93
|
return {
|
|
94
94
|
action: "deny",
|
|
95
|
-
|
|
95
|
+
message: `write tool "${req.toolName}" denied: active skill manifest constrains writes to allowPaths but the call has no resolvable path`,
|
|
96
96
|
decisionReason: "safety",
|
|
97
97
|
};
|
|
98
98
|
}
|
|
@@ -100,7 +100,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
100
100
|
if (!canon.ok) {
|
|
101
101
|
return {
|
|
102
102
|
action: "deny",
|
|
103
|
-
|
|
103
|
+
message: `write to "${path}" denied: its real target could not be resolved against the active skill manifest allowPaths`,
|
|
104
104
|
decisionReason: "safety",
|
|
105
105
|
};
|
|
106
106
|
}
|
|
@@ -115,7 +115,7 @@ export function createActiveSkillScopePolicy(opts) {
|
|
|
115
115
|
if (!inside) {
|
|
116
116
|
return {
|
|
117
117
|
action: "deny",
|
|
118
|
-
|
|
118
|
+
message: `write to "${path}" is outside the paths allowed by skill manifest "${m.lineageId}" (allowPaths: ${(m.allowPaths ?? []).join(", ")})`,
|
|
119
119
|
decisionReason: "safety",
|
|
120
120
|
};
|
|
121
121
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { deriveControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
1
|
+
import { adoptLegacyRepoDirs, deriveControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
|
|
2
2
|
import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
|
|
3
3
|
import { normalizeMemorySpec } from "../memory.js";
|
|
4
4
|
import { MemoryEngine } from "../memory-engine/engine.js";
|
|
@@ -15,6 +15,9 @@ export async function prepareMemory(input) {
|
|
|
15
15
|
const pinned = backend.directoryRoot;
|
|
16
16
|
const engineRoot = resolveMemoryEngineRoot(deps.memoryEngineDir);
|
|
17
17
|
const repoRoot = deps.rootPath ?? taskRootPath;
|
|
18
|
+
if (repoRoot) {
|
|
19
|
+
adoptLegacyRepoDirs(engineRoot, repoRoot, (err) => deps.onError?.(err, { phase: "memory", sessionId }));
|
|
20
|
+
}
|
|
18
21
|
let identityKey;
|
|
19
22
|
if (memorySpec.scopeContract === "v2" && repoRoot) {
|
|
20
23
|
const consumesProjectIdentity = [...memorySpec.scopes, ...(memorySpec.writeScope !== null ? [memorySpec.writeScope] : [])]
|
|
@@ -30,6 +30,7 @@ import { CacheBreakDetector, toolsToFingerprintInputs } from "../cache-break-det
|
|
|
30
30
|
import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
|
|
31
31
|
import { defineTool, isDefineToolProduct } from "../tools.js";
|
|
32
32
|
import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
|
|
33
|
+
import { protocolOf } from "../protocol-table.js";
|
|
33
34
|
import { pathToUri } from "../lsp-protocol.js";
|
|
34
35
|
import { DEFAULT_TOOL_RESULT_THRESHOLD_CHARS, createOffloadPersist, firstPartyOffloadPolicy, InMemoryToolResultStore, RunnerSharedToolResultStore, ScopedToolResultStore, isVolatileOffloadStore, OFFLOAD_TOOL_NAME, createReadToolResultTool, withToolResultOffload, } from "../tool-result-store.js";
|
|
35
36
|
import { OUTPUT_TOOL_NAME, REPORT_FINDINGS_TOOL_NAME, SKILL_CONTENT_MAX_CHARS, SKILL_TOOL_NAME, createOutputTool, createReportBlockedTool, createReportFindingsTool, createSkillTool, normalizeSkills } from "./synthetic-tools.js";
|
|
@@ -1378,6 +1379,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1378
1379
|
...(deps.backgroundAgentStore !== undefined ? { agentStore: deps.backgroundAgentStore } : {}),
|
|
1379
1380
|
...(deps.mailboxStore !== undefined ? { mailbox: deps.mailboxStore } : {}),
|
|
1380
1381
|
...(reviveSpawn !== undefined ? { reviveSpawn } : {}),
|
|
1382
|
+
onNotifyError: (f) => emitTrace(deps.tracer, () => ({ kind: "observer.notify_failed", version: 1, taskId: hostTaskId, site: f.site, message: f.error.message, ts: Date.now() })),
|
|
1381
1383
|
})));
|
|
1382
1384
|
if (!(spec.tools ?? []).some((t) => t.name === AGENT_TRANSCRIPT_TOOL_NAME)) {
|
|
1383
1385
|
tools.push(firstPartyOffload(createAgentTranscriptTool({
|
|
@@ -1834,7 +1836,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1834
1836
|
...(assembled.sections
|
|
1835
1837
|
? { sections: assembled.sections.map((s) => ({ id: s.id, slot: s.slot, carrier: s.carrier, cadence: s.cadence, cacheClass: s.cacheClass, chars: s.text.length, hash: saltedHash(s.text), ...(s.declaredContentHash !== undefined ? { contentHash: s.declaredContentHash } : {}) })) }
|
|
1836
1838
|
: {}),
|
|
1837
|
-
tools: projectToolManifest(tools, (t) => (t.name
|
|
1839
|
+
tools: projectToolManifest(tools, (t) => protocolOf(t.name)?.id ?? "caller"),
|
|
1838
1840
|
};
|
|
1839
1841
|
const epochDeclaredSections = (assembled.sections ?? [])
|
|
1840
1842
|
.filter((s) => s.origin === "provider")
|
|
@@ -2190,8 +2192,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2190
2192
|
}
|
|
2191
2193
|
}
|
|
2192
2194
|
const policy = spec.toolPolicy ?? deps.toolPolicy;
|
|
2193
|
-
{
|
|
2194
|
-
const nameGroups = toolPolicyNameSets(
|
|
2195
|
+
const auditPolicyNames = (auditee) => {
|
|
2196
|
+
const nameGroups = toolPolicyNameSets(auditee);
|
|
2195
2197
|
if (nameGroups.length > 0) {
|
|
2196
2198
|
const known = new Set(tools.flatMap((t) => [t.name, ...(t.aliases ?? [])]));
|
|
2197
2199
|
for (const t of harnessTools)
|
|
@@ -2208,6 +2210,11 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2208
2210
|
err.code = "config.legacy_tool_name";
|
|
2209
2211
|
throw err;
|
|
2210
2212
|
}
|
|
2213
|
+
if (n.includes("__") && protocolOf(n) === undefined) {
|
|
2214
|
+
const err = new Error(`tool policy ${kind}-list entry "${n}" is a pre-prefix MCP tool name and matches nothing in this run's roster — MCP tools are named "mcp__<server>__<tool>" and legacy-name normalization was removed (RB-476-A), so this entry would silently guard nothing. Prefix the entry with "mcp__".`);
|
|
2215
|
+
err.code = "config.legacy_tool_name";
|
|
2216
|
+
throw err;
|
|
2217
|
+
}
|
|
2211
2218
|
unmatched.add(`"${n}" (${kind})`);
|
|
2212
2219
|
}
|
|
2213
2220
|
}
|
|
@@ -2231,7 +2238,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2231
2238
|
}
|
|
2232
2239
|
}
|
|
2233
2240
|
}
|
|
2234
|
-
}
|
|
2241
|
+
};
|
|
2242
|
+
auditPolicyNames(policy);
|
|
2235
2243
|
let sessionRulePolicy;
|
|
2236
2244
|
if (deps.sessionPolicyStore && spec.sessionId) {
|
|
2237
2245
|
const rules = await deps.sessionPolicyStore.getRules(spec.sessionId, spec.principal);
|
|
@@ -2268,6 +2276,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2268
2276
|
})
|
|
2269
2277
|
: undefined;
|
|
2270
2278
|
const narrowingLayers = [sessionRulePolicy, ...ancestorRulePolicies, skillScopePolicy].filter((p) => p !== undefined);
|
|
2279
|
+
for (const layer of narrowingLayers)
|
|
2280
|
+
auditPolicyNames(layer);
|
|
2271
2281
|
const denyNarrowingPolicy = narrowingLayers.length === 0 ? undefined : narrowingLayers.length === 1 ? narrowingLayers[0] : combinePolicies(...narrowingLayers);
|
|
2272
2282
|
const basePolicyForResumeEdit = spec.toolPolicy ?? deps.toolPolicy;
|
|
2273
2283
|
const sameInstanceAncestorCount = policy === undefined ? 0 : (inheritedParentConstraints ?? []).reduce((n, pc) => (pc.policy === policy ? n + 1 : n), 0);
|
|
@@ -2330,7 +2340,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2330
2340
|
if (round >= 3) {
|
|
2331
2341
|
return {
|
|
2332
2342
|
action: "deny",
|
|
2333
|
-
|
|
2343
|
+
message: `the approval-edit chain for "${creq.toolName}" exceeded the re-approval cap (inherited parent policy) — denied fail-closed; re-submit the edited action directly`,
|
|
2334
2344
|
};
|
|
2335
2345
|
}
|
|
2336
2346
|
let re;
|
|
@@ -2340,7 +2350,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2340
2350
|
catch (err) {
|
|
2341
2351
|
return {
|
|
2342
2352
|
action: "deny",
|
|
2343
|
-
|
|
2353
|
+
message: `inherited parent policy re-check of the approved edit errored for "${creq.toolName}": ${err instanceof Error ? err.message : String(err)}`,
|
|
2344
2354
|
};
|
|
2345
2355
|
}
|
|
2346
2356
|
if (re.action === "deny")
|
|
@@ -2354,7 +2364,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2354
2364
|
toolName: creq.toolName,
|
|
2355
2365
|
toolCallId: creq.toolCallId,
|
|
2356
2366
|
args: editArgs,
|
|
2357
|
-
message: re.message ?? re.
|
|
2367
|
+
message: re.message ?? re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2358
2368
|
...askSourceIdentity(),
|
|
2359
2369
|
...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2360
2370
|
}, onAskOf, csignal ?? abortController.signal);
|
|
@@ -2372,7 +2382,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2372
2382
|
if (first === undefined) {
|
|
2373
2383
|
return {
|
|
2374
2384
|
action: "deny",
|
|
2375
|
-
|
|
2385
|
+
message: `inherited parent policy could not be arbitrated for "${creq.toolName}" ` +
|
|
2376
2386
|
`(shared-instance first decision unavailable); denied fail-closed`,
|
|
2377
2387
|
};
|
|
2378
2388
|
}
|
|
@@ -2390,7 +2400,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2390
2400
|
}
|
|
2391
2401
|
return {
|
|
2392
2402
|
action: "deny",
|
|
2393
|
-
|
|
2403
|
+
message: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
|
|
2394
2404
|
`ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
|
|
2395
2405
|
};
|
|
2396
2406
|
}
|
|
@@ -2400,7 +2410,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2400
2410
|
toolName: creq.toolName,
|
|
2401
2411
|
toolCallId: creq.toolCallId,
|
|
2402
2412
|
args: presentedArgs,
|
|
2403
|
-
message: first.message ?? first.
|
|
2413
|
+
message: first.message ?? first.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2404
2414
|
...askSourceIdentity(),
|
|
2405
2415
|
...(first.action === "ask" && first.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2406
2416
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
@@ -2410,7 +2420,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2410
2420
|
return first;
|
|
2411
2421
|
return {
|
|
2412
2422
|
action: "deny",
|
|
2413
|
-
|
|
2423
|
+
message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed`,
|
|
2414
2424
|
};
|
|
2415
2425
|
}
|
|
2416
2426
|
if (resolved.action !== "allow")
|
|
@@ -2431,7 +2441,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2431
2441
|
catch (err) {
|
|
2432
2442
|
return {
|
|
2433
2443
|
action: "deny",
|
|
2434
|
-
|
|
2444
|
+
message: `inherited parent policy errored for "${creq.toolName}": ${err instanceof Error ? err.message : String(err)}`,
|
|
2435
2445
|
};
|
|
2436
2446
|
}
|
|
2437
2447
|
if (decision.action !== "ask")
|
|
@@ -2446,7 +2456,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2446
2456
|
}
|
|
2447
2457
|
return {
|
|
2448
2458
|
action: "deny",
|
|
2449
|
-
|
|
2459
|
+
message: `inherited parent policy requires durable approval for "${creq.toolName}" — the parent's durable ` +
|
|
2450
2460
|
`ask cannot be reconstructed in a delegated child; denied fail-closed (tighten-only)`,
|
|
2451
2461
|
};
|
|
2452
2462
|
}
|
|
@@ -2456,7 +2466,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2456
2466
|
toolName: creq.toolName,
|
|
2457
2467
|
toolCallId: creq.toolCallId,
|
|
2458
2468
|
args: presentedArgs,
|
|
2459
|
-
message: decision.message ?? decision.
|
|
2469
|
+
message: decision.message ?? decision.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
|
|
2460
2470
|
...askSourceIdentity(),
|
|
2461
2471
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2462
2472
|
}, pc.onAsk, csignal ?? abortController.signal);
|
|
@@ -2466,7 +2476,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2466
2476
|
return decision;
|
|
2467
2477
|
return {
|
|
2468
2478
|
action: "deny",
|
|
2469
|
-
|
|
2479
|
+
message: `no approver is reachable for the inherited approval of "${creq.toolName}" and the marker channel is at capacity — denied fail-closed`,
|
|
2470
2480
|
};
|
|
2471
2481
|
}
|
|
2472
2482
|
if (resolved.action !== "allow")
|
|
@@ -2621,7 +2631,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2621
2631
|
const adjudicate = effectivePolicy
|
|
2622
2632
|
? (req) => raceAbort(Promise.resolve(effectivePolicy.check({ ...req, budget: budgetSnapshot }, abortController.signal)), abortController.signal, () => ({
|
|
2623
2633
|
action: "deny",
|
|
2624
|
-
|
|
2634
|
+
message: "policy check aborted (task timed out or cancelled)",
|
|
2625
2635
|
}))
|
|
2626
2636
|
: undefined;
|
|
2627
2637
|
const sanitizePreview = (node, depth = 0) => {
|
|
@@ -2663,7 +2673,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2663
2673
|
if (inheritedUnavailableAsks.delete(req.toolCallId)) {
|
|
2664
2674
|
return {
|
|
2665
2675
|
action: "deny",
|
|
2666
|
-
|
|
2676
|
+
message: `approval for "${req.toolName}" requires an ancestor task's approval that cannot be resolved here ` +
|
|
2667
2677
|
`(no live approver reachable, or a durable-park mandate applies), and no durable approval park is ` +
|
|
2668
2678
|
`available — denied fail-closed (the inherited constraint stands).`,
|
|
2669
2679
|
decisionReason: "mode",
|
|
@@ -2696,7 +2706,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2696
2706
|
const preview = approvalPreviewOf(req.toolName, req.args);
|
|
2697
2707
|
return preview !== undefined ? { preview } : {};
|
|
2698
2708
|
})(),
|
|
2699
|
-
message: decision.message ?? decision.
|
|
2709
|
+
message: decision.message ?? decision.message ?? `approval required for "${req.toolName}"`,
|
|
2700
2710
|
...askSourceIdentity(),
|
|
2701
2711
|
...(decision.action === "ask" && decision.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
|
|
2702
2712
|
}, onAsk, abortController.signal);
|
|
@@ -2878,6 +2888,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2878
2888
|
const token = mintCheckpointToken();
|
|
2879
2889
|
const gate = { kind: "resource_limit", reason };
|
|
2880
2890
|
const resourceLedgerOut = debitLedger(priorLedger, sliceSpend, resourceTotal);
|
|
2891
|
+
const mintedAt = Date.now();
|
|
2881
2892
|
const cp = {
|
|
2882
2893
|
token,
|
|
2883
2894
|
version: resourceLedgerOut.totalWalltimeSec !== undefined ? WALLTIME_CHECKPOINT_VERSION : RESOURCE_CHECKPOINT_VERSION,
|
|
@@ -2888,8 +2899,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2888
2899
|
pendingAction: { kind: "resource_limit", reason },
|
|
2889
2900
|
state: serializeCheckpointState(remoteHandle),
|
|
2890
2901
|
status: "pending",
|
|
2891
|
-
createdAt:
|
|
2892
|
-
deadline:
|
|
2902
|
+
createdAt: mintedAt,
|
|
2903
|
+
deadline: mintedAt + (sanitizedTtlMs(rs.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS),
|
|
2893
2904
|
suspendCount: priorSuspendCount,
|
|
2894
2905
|
humanReview: humanReviewRef.count > 0
|
|
2895
2906
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
@@ -2958,6 +2969,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2958
2969
|
const scope = checkpointScopeOf(spec);
|
|
2959
2970
|
const gate = { kind: "plan_review", reason: reason ?? `plan review requested` };
|
|
2960
2971
|
const reviewLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
|
|
2972
|
+
const mintedAt = Date.now();
|
|
2961
2973
|
const cp = {
|
|
2962
2974
|
token,
|
|
2963
2975
|
version: reviewLedger.totalWalltimeSec !== undefined ? WALLTIME_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
@@ -2968,9 +2980,9 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
2968
2980
|
pendingAction: { kind: "plan_review" },
|
|
2969
2981
|
state: serializeCheckpointState(remoteHandle),
|
|
2970
2982
|
status: "pending",
|
|
2971
|
-
createdAt:
|
|
2983
|
+
createdAt: mintedAt,
|
|
2972
2984
|
suspendedAt: now(),
|
|
2973
|
-
deadline:
|
|
2985
|
+
deadline: mintedAt + (sanitizedTtlMs(spec.durableApproval?.ttlMs) ?? DEFAULT_RESOURCE_TTL_MS),
|
|
2974
2986
|
suspendCount: priorSuspendCount + 1,
|
|
2975
2987
|
humanReview: humanReviewRef.count > 0
|
|
2976
2988
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
@@ -3082,6 +3094,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3082
3094
|
const ttlMs = sanitizedTtlMs(durableApproval?.ttlMs);
|
|
3083
3095
|
const checkpointState = serializeCheckpointState(remoteHandle);
|
|
3084
3096
|
const approvalLedger = debitLedger(priorLedger, liveSpendRef.get?.() ?? { costMicroUsd: 0, tokens: 0, turns: 0, walltimeMs: 0 }, resourceTotal, { countSlice: false });
|
|
3097
|
+
const mintedAt = Date.now();
|
|
3085
3098
|
cp = {
|
|
3086
3099
|
token,
|
|
3087
3100
|
version: approvalLedger.totalWalltimeSec !== undefined ? WALLTIME_CHECKPOINT_VERSION : BINDING_CHECKPOINT_VERSION,
|
|
@@ -3104,12 +3117,12 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
3104
3117
|
},
|
|
3105
3118
|
state: checkpointState,
|
|
3106
3119
|
deadline: ttlMs !== undefined
|
|
3107
|
-
?
|
|
3120
|
+
? mintedAt + ttlMs
|
|
3108
3121
|
: durableApproval !== undefined
|
|
3109
3122
|
? undefined
|
|
3110
|
-
:
|
|
3123
|
+
: mintedAt + DEFAULT_UNATTENDED_APPROVAL_TTL_MS,
|
|
3111
3124
|
status: "pending",
|
|
3112
|
-
createdAt:
|
|
3125
|
+
createdAt: mintedAt,
|
|
3113
3126
|
suspendedAt: now(),
|
|
3114
3127
|
humanReview: humanReviewRef.count > 0
|
|
3115
3128
|
? { count: humanReviewRef.count, totalWaitMs: humanReviewRef.totalWaitMs, gates: [...humanReviewRef.gates] }
|
|
@@ -1674,7 +1674,13 @@ export class Runner {
|
|
|
1674
1674
|
}
|
|
1675
1675
|
rs.telemetry.taskStart = Date.now();
|
|
1676
1676
|
rs.telemetry.taskStartMonotonic = performance.now();
|
|
1677
|
-
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId)).catch(() => {
|
|
1677
|
+
void Promise.resolve(this.sessions.noteTaskRun?.(prepared.sessionId, rs.telemetry.taskId)).catch((err) => {
|
|
1678
|
+
try {
|
|
1679
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "config", sessionId: prepared.sessionId });
|
|
1680
|
+
}
|
|
1681
|
+
catch {
|
|
1682
|
+
}
|
|
1683
|
+
});
|
|
1678
1684
|
rs.degrade.recordDegraded = (info, toModel) => {
|
|
1679
1685
|
if (rs.degrade.degraded !== undefined)
|
|
1680
1686
|
return;
|
|
@@ -3344,7 +3350,7 @@ export class Runner {
|
|
|
3344
3350
|
const rechecked = await prepared.basePolicyForResumeEdit.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal);
|
|
3345
3351
|
if (rechecked.action === "deny") {
|
|
3346
3352
|
emitEnd(true);
|
|
3347
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approver EDITED this call's input; the edited call is denied by the deployment's tool policy and was not executed${rechecked.
|
|
3353
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approver EDITED this call's input; the edited call is denied by the deployment's tool policy and was not executed${rechecked.message ? `: ${rechecked.message}` : ""}.`), true));
|
|
3348
3354
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3349
3355
|
return;
|
|
3350
3356
|
}
|
|
@@ -3353,7 +3359,7 @@ export class Runner {
|
|
|
3353
3359
|
const narrowed = await prepared.denyNarrowingPolicy.check({ toolName: pendingAction.toolName, args: resolvedArgs, toolCallId: pendingAction.toolCallId }, prepared.abortController.signal);
|
|
3354
3360
|
if (narrowed.action === "deny") {
|
|
3355
3361
|
emitEnd(true);
|
|
3356
|
-
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.
|
|
3362
|
+
const eid = await prepared.session.appendMessage(toolResultMsg(pendingAction.toolCallId, pendingAction.toolName, formatHookFeedback(`The approved tool call "${pendingAction.toolName}" is now denied by a session rule and was not executed${narrowed.message ? `: ${narrowed.message}` : ""}.`), true));
|
|
3357
3363
|
emitCommitted(eid, "toolResult", pendingAction.toolCallId);
|
|
3358
3364
|
return;
|
|
3359
3365
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExecutionEnv } from "../../internal/harness-types.js";
|
|
2
2
|
import type { ToolEffect } from "../types.js";
|
|
3
|
-
import { type
|
|
3
|
+
import { type NamedToolPolicy } from "../tool-policy.js";
|
|
4
4
|
import type { SessionPermissionRules } from "../session-policy-store.js";
|
|
5
5
|
export declare const PATH_WRITE_TOOLS: ReadonlySet<string>;
|
|
6
6
|
export declare function isWithin(root: string, p: string): boolean;
|
|
@@ -8,4 +8,4 @@ export declare function createSessionRulePolicy(rules: SessionPermissionRules, o
|
|
|
8
8
|
env: ExecutionEnv;
|
|
9
9
|
rootPath?: string;
|
|
10
10
|
toolEffects?: ReadonlyMap<string, ToolEffect>;
|
|
11
|
-
}):
|
|
11
|
+
}): NamedToolPolicy;
|
|
@@ -18,7 +18,7 @@ export function isWithin(root, p) {
|
|
|
18
18
|
const base = r.endsWith("/") ? r : r + "/";
|
|
19
19
|
return c.startsWith(base);
|
|
20
20
|
}
|
|
21
|
-
const deny = (
|
|
21
|
+
const deny = (message) => ({ action: "deny", message, decisionReason: "rule" });
|
|
22
22
|
export function createSessionRulePolicy(rules, opts) {
|
|
23
23
|
const { env, rootPath, toolEffects } = opts;
|
|
24
24
|
const toolDeny = new Set(rules.toolDeny ?? []);
|
|
@@ -32,6 +32,7 @@ export function createSessionRulePolicy(rules, opts) {
|
|
|
32
32
|
: undefined;
|
|
33
33
|
const allowDirs = rules.allowDirs && rules.allowDirs.length > 0 ? rules.allowDirs : undefined;
|
|
34
34
|
return {
|
|
35
|
+
nameSets: [{ ...(rules.toolDeny?.length ? { deny: [...rules.toolDeny] } : {}), ...(rules.toolAllow?.length ? { allow: [...rules.toolAllow] } : {}) }],
|
|
35
36
|
async check(req, signal) {
|
|
36
37
|
const toolName = req.toolName;
|
|
37
38
|
if (toolDeny.has(toolName))
|
|
@@ -187,13 +187,13 @@ export function createSkillTool(skills, scope) {
|
|
|
187
187
|
`Do not invoke a skill that is already running. ` +
|
|
188
188
|
`If a \`<command-name>\` block is already present this turn, the skill is loaded — follow it directly rather than calling again.`,
|
|
189
189
|
parameters: Type.Object({
|
|
190
|
-
skill: Type.
|
|
190
|
+
skill: Type.String({ description: 'The skill name. E.g., "commit", "review-pr", or "pdf"' }),
|
|
191
191
|
args: Type.Optional(Type.String({ description: "Optional arguments for the skill" })),
|
|
192
192
|
}),
|
|
193
193
|
effect: "read",
|
|
194
194
|
execute: (rawArgs) => {
|
|
195
195
|
const a = (rawArgs ?? {});
|
|
196
|
-
const name = String(a.skill ??
|
|
196
|
+
const name = String(a.skill ?? "");
|
|
197
197
|
const skill = byName.get(name);
|
|
198
198
|
if (!skill) {
|
|
199
199
|
throw new Error(`Unknown skill "${name}". Available skills: ${names}.`);
|
|
@@ -39,7 +39,6 @@ export declare function scoreToolMatch(query: string, info: DeferredToolInfo): n
|
|
|
39
39
|
export interface ToolSearchArgs {
|
|
40
40
|
query?: string;
|
|
41
41
|
max_results?: number;
|
|
42
|
-
select?: string[];
|
|
43
42
|
}
|
|
44
43
|
export declare function resolveToolSearchDetailed(args: ToolSearchArgs, registry: ReadonlyMap<string, DeferredToolInfo>): {
|
|
45
44
|
matched: string[];
|
|
@@ -130,8 +130,6 @@ export function resolveToolSearchDetailed(args, registry) {
|
|
|
130
130
|
out.push(name);
|
|
131
131
|
}
|
|
132
132
|
};
|
|
133
|
-
for (const name of args.select ?? [])
|
|
134
|
-
add(name);
|
|
135
133
|
const q = typeof args.query === "string" ? args.query.trim() : "";
|
|
136
134
|
if (q !== "") {
|
|
137
135
|
const selectMatch = /^select:(.+)$/i.exec(q);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { PRESENT_PLAN_TOOL_NAME } from "../present-plan-tool.js";
|
|
2
2
|
import { defuseFenceMarkers, delimitUntrusted, sanitizeUntrustedText } from "../untrusted-text.js";
|
|
3
|
+
import { protocolOf } from "../protocol-table.js";
|
|
3
4
|
import { buildSkillsBlock, skillListingLine } from "./synthetic-tools.js";
|
|
4
5
|
import { TOOL_SEARCH_NAME as TOOL_SEARCH_TOOL_NAME } from "./tool-disclosure.js";
|
|
5
6
|
export const TODO_REMINDER_CONFIG = {
|
|
@@ -384,7 +385,7 @@ export const TOOLS_DELTA_LIST_MAX = 30;
|
|
|
384
385
|
function groupByMcpServer(names) {
|
|
385
386
|
const counts = new Map();
|
|
386
387
|
for (const n of names) {
|
|
387
|
-
const key =
|
|
388
|
+
const key = protocolOf(n)?.displayGroupKey(n) ?? n;
|
|
388
389
|
counts.set(key, (counts.get(key) ?? 0) + 1);
|
|
389
390
|
}
|
|
390
391
|
return [...counts.entries()]
|
|
@@ -69,7 +69,7 @@ export function createSensitivePathPolicy(opts) {
|
|
|
69
69
|
if (canon.unresolvedSymlink) {
|
|
70
70
|
return {
|
|
71
71
|
action: "deny",
|
|
72
|
-
|
|
72
|
+
message: `write to "${path}" is blocked: it is a symlink whose real target could not be resolved (it could point onto a guarded sensitive path)`,
|
|
73
73
|
decisionReason: "safety",
|
|
74
74
|
};
|
|
75
75
|
}
|
|
@@ -79,7 +79,7 @@ export function createSensitivePathPolicy(opts) {
|
|
|
79
79
|
if (hit) {
|
|
80
80
|
return {
|
|
81
81
|
action: "deny",
|
|
82
|
-
|
|
82
|
+
message: `write to "${path}" is blocked: its real target resolves onto the guarded sensitive path "${hit}"`,
|
|
83
83
|
decisionReason: "safety",
|
|
84
84
|
};
|
|
85
85
|
}
|
|
@@ -13,20 +13,17 @@ export type PermissionResult = {
|
|
|
13
13
|
action: "allow";
|
|
14
14
|
updatedInput?: unknown;
|
|
15
15
|
message?: string;
|
|
16
|
-
reason?: string;
|
|
17
16
|
decisionReason?: DecisionReason;
|
|
18
17
|
} | {
|
|
19
18
|
action: "ask";
|
|
20
19
|
updatedInput?: unknown;
|
|
21
20
|
message?: string;
|
|
22
|
-
reason?: string;
|
|
23
21
|
decisionReason?: DecisionReason;
|
|
24
22
|
requiresRealApproval?: boolean;
|
|
25
23
|
} | {
|
|
26
24
|
action: "deny";
|
|
27
25
|
updatedInput?: unknown;
|
|
28
26
|
message?: string;
|
|
29
|
-
reason?: string;
|
|
30
27
|
decisionReason?: DecisionReason;
|
|
31
28
|
};
|
|
32
29
|
export declare function decisionText(d: PermissionResult): string | undefined;
|