Skip to content

Commit c9707ab

Browse files
authored
fix(exec): rebuild command authorization on the Tree-sitter command planner
Replace the exec approval parser/planner path with Tree-sitter-backed authorization planning, carrying planner decisions through node and gateway execution. This keeps unpersistable shell shapes one-shot, adds typed `unavailableDecisions` for approval prompts, and refreshes coverage for allowlist matching, command rendering, durable allow-always persistence, and host approval paths. Verification: - GitHub PR checks for ce23811: CLEAN, 142 success, 32 skipped, 0 failed, 0 pending. - /Users/jmerhi/.nvm/versions/node/v24.12.0/bin/node scripts/plugin-sdk-surface-report.mjs --check - /Users/jmerhi/.nvm/versions/node/v24.12.0/bin/node scripts/run-vitest.mjs test/scripts/plugin-sdk-surface-report.test.ts --reporter=verbose - Focused exec approval suite: 13 files, 467 tests.
1 parent 79c7468 commit c9707ab

52 files changed

Lines changed: 5115 additions & 3154 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6579,6 +6579,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable {
65796579
public let security: AnyCodable?
65806580
public let ask: AnyCodable?
65816581
public let warningtext: AnyCodable?
6582+
public let unavailabledecisions: [String]?
65826583
public let commandspans: [[String: AnyCodable]]?
65836584
public let agentid: AnyCodable?
65846585
public let resolvedpath: AnyCodable?
@@ -6604,6 +6605,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable {
66046605
security: AnyCodable?,
66056606
ask: AnyCodable?,
66066607
warningtext: AnyCodable?,
6608+
unavailabledecisions: [String]?,
66076609
commandspans: [[String: AnyCodable]]?,
66086610
agentid: AnyCodable? = nil,
66096611
resolvedpath: AnyCodable?,
@@ -6628,6 +6630,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable {
66286630
self.security = security
66296631
self.ask = ask
66306632
self.warningtext = warningtext
6633+
self.unavailabledecisions = unavailabledecisions
66316634
self.commandspans = commandspans
66326635
self.agentid = agentid
66336636
self.resolvedpath = resolvedpath
@@ -6654,6 +6657,7 @@ public struct ExecApprovalRequestParams: Codable, Sendable {
66546657
case security
66556658
case ask
66566659
case warningtext = "warningText"
6660+
case unavailabledecisions = "unavailableDecisions"
66576661
case commandspans = "commandSpans"
66586662
case agentid = "agentId"
66596663
case resolvedpath = "resolvedPath"

packages/gateway-protocol/src/exec-approvals-validators.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,4 +106,27 @@ describe("exec approvals protocol validators", () => {
106106
}),
107107
).toBe(false);
108108
});
109+
110+
it("accepts only optional unavailable approval decisions", () => {
111+
expect(
112+
validateExecApprovalRequestParams({
113+
command: "echo hi",
114+
unavailableDecisions: ["allow-always"],
115+
}),
116+
).toBe(true);
117+
118+
for (const unavailableDecisions of [
119+
[],
120+
["allow-always", "allow-always"],
121+
["allow-once"],
122+
["deny"],
123+
]) {
124+
expect(
125+
validateExecApprovalRequestParams({
126+
command: "echo hi",
127+
unavailableDecisions,
128+
}),
129+
).toBe(false);
130+
}
131+
});
109132
});

packages/gateway-protocol/src/schema/exec-approvals.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,12 @@ export const ExecApprovalRequestParamsSchema = Type.Object(
151151
security: Type.Optional(Type.Union([Type.String(), Type.Null()])),
152152
ask: Type.Optional(Type.Union([Type.String(), Type.Null()])),
153153
warningText: Type.Optional(Type.Union([Type.String(), Type.Null()])),
154+
unavailableDecisions: Type.Optional(
155+
Type.Array(Type.String({ enum: ["allow-always"] }), {
156+
minItems: 1,
157+
maxItems: 1,
158+
}),
159+
),
154160
commandSpans: Type.Optional(
155161
Type.Array(
156162
Type.Object(

scripts/plugin-sdk-surface-report.mjs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ const defaultPublicDeprecatedExportsByEntrypointBudget = Object.freeze({
8787
"outbound-send-deps": 4,
8888
"outbound-runtime": 16,
8989
"file-access-runtime": 2,
90-
"infra-runtime": 577,
90+
"infra-runtime": 584,
9191
"ssrf-policy": 1,
9292
"ssrf-runtime": 1,
9393
"media-runtime": 2,
@@ -161,11 +161,11 @@ let publicDeprecatedExportsByEntrypointBudget;
161161
try {
162162
budgets = {
163163
publicEntrypoints: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_ENTRYPOINTS", 319),
164-
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10284),
165-
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5163),
164+
publicExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_EXPORTS", 10291),
165+
publicFunctionExports: readBudgetEnv("OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_FUNCTION_EXPORTS", 5166),
166166
publicDeprecatedExports: readBudgetEnv(
167167
"OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_DEPRECATED_EXPORTS",
168-
3237,
168+
3244,
169169
),
170170
publicWildcardReexports: readBudgetEnv(
171171
"OPENCLAW_PLUGIN_SDK_MAX_PUBLIC_WILDCARD_REEXPORTS",

src/agents/bash-tools.exec-approval-request.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
} from "@openclaw/normalization-core/string-coerce";
1414
import type {
1515
ExecApprovalCommandSpan,
16+
ExecApprovalUnavailableDecision,
1617
ExecAsk,
1718
ExecSecurity,
1819
SystemRunApprovalPlan,
@@ -55,6 +56,7 @@ type RequestExecApprovalDecisionParams = {
5556
ask: ExecAsk;
5657
warningText?: string;
5758
commandSpans?: ExecApprovalCommandSpan[];
59+
unavailableDecisions?: readonly ExecApprovalUnavailableDecision[];
5860
agentId?: string;
5961
resolvedPath?: string;
6062
sessionKey?: string;
@@ -87,6 +89,9 @@ function buildExecApprovalRequestToolParams(
8789
ask: params.ask,
8890
warningText: params.warningText,
8991
commandSpans: params.commandSpans,
92+
...(params.unavailableDecisions?.length
93+
? { unavailableDecisions: params.unavailableDecisions }
94+
: {}),
9095
agentId: params.agentId,
9196
resolvedPath: params.resolvedPath,
9297
sessionKey: params.sessionKey,
@@ -207,6 +212,7 @@ type HostExecApprovalParams = {
207212
ask: ExecAsk;
208213
warningText?: string;
209214
commandSpans?: ExecApprovalCommandSpan[];
215+
unavailableDecisions?: readonly ExecApprovalUnavailableDecision[];
210216
commandHighlighting?: boolean;
211217
agentId?: string;
212218
resolvedPath?: string;
@@ -315,6 +321,7 @@ async function buildHostApprovalDecisionParams(
315321
ask: params.ask,
316322
warningText: params.warningText,
317323
commandSpans,
324+
unavailableDecisions: params.unavailableDecisions,
318325
...buildExecApprovalRequesterContext({
319326
agentId: params.agentId,
320327
sessionKey: params.sessionKey,

0 commit comments

Comments
 (0)