Skip to content

Commit 460562c

Browse files
authored
test: narrow SDK package runner cwd
1 parent f7bd125 commit 460562c

12 files changed

Lines changed: 110 additions & 269 deletions

scripts/check-release-metadata-only.mjs

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,28 @@ function readRefOptionValue(argv, index, optionName) {
2929
}
3030

3131
export function parseArgs(argv) {
32+
const separatorIndex = argv.indexOf("--");
33+
const flagArgv = separatorIndex === -1 ? argv : argv.slice(0, separatorIndex);
34+
const explicitPaths =
35+
separatorIndex === -1 ? [] : argv.slice(separatorIndex + 1).map(normalizePath);
3236
const args = { staged: false, base: "origin/main", head: "HEAD", paths: [] };
33-
for (let index = 0; index < argv.length; index += 1) {
34-
const arg = argv[index];
35-
if (arg === "--") {
36-
continue;
37-
} else if (arg === "--staged") {
37+
for (let index = 0; index < flagArgv.length; index += 1) {
38+
const arg = flagArgv[index];
39+
if (arg === "--staged") {
3840
args.staged = true;
3941
} else if (arg === "--base") {
40-
args.base = readRefOptionValue(argv, index, arg);
42+
args.base = readRefOptionValue(flagArgv, index, arg);
4143
index += 1;
4244
} else if (arg === "--head") {
43-
args.head = readRefOptionValue(argv, index, arg);
45+
args.head = readRefOptionValue(flagArgv, index, arg);
4446
index += 1;
47+
} else if (arg.startsWith("-")) {
48+
throw new Error(`Unknown option: ${arg}`);
4549
} else {
4650
args.paths.push(normalizePath(arg));
4751
}
4852
}
53+
args.paths.push(...explicitPaths);
4954
return args;
5055
}
5156

@@ -164,5 +169,10 @@ export function main(argv = process.argv.slice(2)) {
164169
}
165170

166171
if (process.argv[1] && path.resolve(process.argv[1]) === path.resolve(import.meta.filename)) {
167-
main();
172+
try {
173+
main();
174+
} catch (error) {
175+
console.error(error instanceof Error ? error.message : String(error));
176+
process.exit(1);
177+
}
168178
}

src/agents/embedded-agent-runner/run/attempt-bootstrap-routing.ts

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -33,23 +33,6 @@ type AttemptWorkspaceBootstrapRoutingInput = Omit<
3333
bootstrapFiles?: readonly WorkspaceBootstrapFile[];
3434
};
3535

36-
/**
37-
* Maps a resolved bootstrap mode to concrete prompt destinations. Today only
38-
* full bootstrap enters system context; limited/none intentionally avoid
39-
* runtime-context injection until that path has a separate contract.
40-
*/
41-
export function resolveBootstrapContextTargets(params: {
42-
bootstrapMode: BootstrapMode;
43-
}): Pick<
44-
AttemptBootstrapRouting,
45-
"includeBootstrapInSystemContext" | "includeBootstrapInRuntimeContext"
46-
> {
47-
return {
48-
includeBootstrapInSystemContext: params.bootstrapMode === "full",
49-
includeBootstrapInRuntimeContext: false,
50-
};
51-
}
52-
5336
function resolveAttemptBootstrapRouting(
5437
params: AttemptBootstrapRoutingInput,
5538
): AttemptBootstrapRouting {
@@ -66,22 +49,11 @@ function resolveAttemptBootstrapRouting(
6649

6750
return {
6851
bootstrapMode,
69-
...resolveBootstrapContextTargets({ bootstrapMode }),
52+
includeBootstrapInSystemContext: bootstrapMode === "full",
53+
includeBootstrapInRuntimeContext: false,
7054
};
7155
}
7256

73-
export function hasBootstrapFileContent(files?: readonly WorkspaceBootstrapFile[]): boolean {
74-
return (
75-
files?.some(
76-
(file) =>
77-
file.name === DEFAULT_BOOTSTRAP_FILENAME &&
78-
!file.missing &&
79-
typeof file.content === "string" &&
80-
file.content.trim().length > 0,
81-
) ?? false
82-
);
83-
}
84-
8557
/**
8658
* Resolves workspace bootstrap routing after checking pending state and
8759
* hook-provided bootstrap files. Hook content counts as both pending bootstrap
@@ -94,7 +66,14 @@ export async function resolveAttemptWorkspaceBootstrapRouting(
9466
const workspaceBootstrapPending = await params.isWorkspaceBootstrapPending(
9567
params.resolvedWorkspace,
9668
);
97-
const hasHookBootstrapContent = hasBootstrapFileContent(params.bootstrapFiles);
69+
const hasHookBootstrapContent =
70+
params.bootstrapFiles?.some(
71+
(file) =>
72+
file.name === DEFAULT_BOOTSTRAP_FILENAME &&
73+
!file.missing &&
74+
typeof file.content === "string" &&
75+
file.content.trim().length > 0,
76+
) ?? false;
9877
return resolveAttemptBootstrapRouting({
9978
...params,
10079
workspaceBootstrapPending: workspaceBootstrapPending || hasHookBootstrapContent,

src/agents/embedded-agent-runner/run/attempt.spawn-workspace.bootstrap-routing.test.ts

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
// Coverage for bootstrap routing across canonical and effective workspaces.
22
import { describe, expect, it, vi } from "vitest";
3-
import {
4-
hasBootstrapFileContent,
5-
resolveBootstrapContextTargets,
6-
resolveAttemptWorkspaceBootstrapRouting,
7-
} from "./attempt-bootstrap-routing.js";
3+
import { resolveAttemptWorkspaceBootstrapRouting } from "./attempt-bootstrap-routing.js";
84

95
describe("runEmbeddedAttempt bootstrap routing", () => {
106
it("resolves bootstrap pending from the canonical workspace instead of a copied sandbox", async () => {
@@ -100,34 +96,27 @@ describe("runEmbeddedAttempt bootstrap routing", () => {
10096
expect(routing.includeBootstrapInRuntimeContext).toBe(false);
10197
});
10298

103-
it("does not treat empty hook-provided BOOTSTRAP.md as pending bootstrap context", () => {
104-
expect(
105-
hasBootstrapFileContent([
99+
it("does not treat empty hook-provided BOOTSTRAP.md as pending bootstrap context", async () => {
100+
const routing = await resolveAttemptWorkspaceBootstrapRouting({
101+
isWorkspaceBootstrapPending: vi.fn(async () => false),
102+
bootstrapFiles: [
106103
{
107104
name: "BOOTSTRAP.md",
108105
path: "/tmp/openclaw-workspace/BOOTSTRAP.md",
109106
content: " ",
110107
missing: false,
111108
},
112-
]),
113-
).toBe(false);
114-
});
115-
116-
it("keeps BOOTSTRAP.md in Project Context for full bootstrap turns", () => {
117-
expect(resolveBootstrapContextTargets({ bootstrapMode: "full" })).toEqual({
118-
includeBootstrapInSystemContext: true,
119-
includeBootstrapInRuntimeContext: false,
109+
],
110+
trigger: "user",
111+
isPrimaryRun: true,
112+
isCanonicalWorkspace: true,
113+
effectiveWorkspace: "/tmp/openclaw-workspace",
114+
resolvedWorkspace: "/tmp/openclaw-workspace",
115+
hasBootstrapFileAccess: true,
120116
});
121-
});
122117

123-
it("excludes BOOTSTRAP.md from every context outside full bootstrap turns", () => {
124-
expect(resolveBootstrapContextTargets({ bootstrapMode: "limited" })).toEqual({
125-
includeBootstrapInSystemContext: false,
126-
includeBootstrapInRuntimeContext: false,
127-
});
128-
expect(resolveBootstrapContextTargets({ bootstrapMode: "none" })).toEqual({
129-
includeBootstrapInSystemContext: false,
130-
includeBootstrapInRuntimeContext: false,
131-
});
118+
expect(routing.bootstrapMode).toBe("none");
119+
expect(routing.includeBootstrapInSystemContext).toBe(false);
120+
expect(routing.includeBootstrapInRuntimeContext).toBe(false);
132121
});
133122
});

src/agents/embedded-agent-runner/run/attempt.test.ts

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import {
1616
resolveEmbeddedAgentBaseStreamFn,
1717
resolveEmbeddedAgentStreamFn,
1818
} from "../stream-resolution.js";
19-
import { resolveBootstrapContextTargets } from "./attempt-bootstrap-routing.js";
2019
import { buildContextEnginePromptCacheInfo } from "./attempt.context-engine-helpers.js";
2120
import {
2221
buildAfterTurnRuntimeContext,
@@ -332,23 +331,6 @@ describe("resolvePromptModeForSession", () => {
332331
});
333332
});
334333

335-
describe("resolveBootstrapContextTargets", () => {
336-
it("keeps BOOTSTRAP.md in system Project Context only for full bootstrap turns", () => {
337-
expect(resolveBootstrapContextTargets({ bootstrapMode: "full" })).toEqual({
338-
includeBootstrapInSystemContext: true,
339-
includeBootstrapInRuntimeContext: false,
340-
});
341-
expect(resolveBootstrapContextTargets({ bootstrapMode: "limited" })).toEqual({
342-
includeBootstrapInSystemContext: false,
343-
includeBootstrapInRuntimeContext: false,
344-
});
345-
expect(resolveBootstrapContextTargets({ bootstrapMode: "none" })).toEqual({
346-
includeBootstrapInSystemContext: false,
347-
includeBootstrapInRuntimeContext: false,
348-
});
349-
});
350-
});
351-
352334
describe("shouldWarnOnOrphanedUserRepair", () => {
353335
it("warns for user and manual runs", () => {
354336
expect(shouldWarnOnOrphanedUserRepair("user")).toBe(true);

src/agents/embedded-agent-runner/run/attempt.tool-search-run-plan.test.ts

Lines changed: 14 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,7 @@
11
// Coverage for Tool Search control planning and allowlist accounting.
22
import { describe, expect, it } from "vitest";
33
import { setPluginToolMeta } from "../../../plugins/tools.js";
4-
import {
5-
buildAutoAddedToolSearchControlNamesForAllowlistCheck,
6-
buildCallableToolNamesForEmptyAllowlistCheck,
7-
buildToolSearchRunPlan,
8-
} from "./attempt.tool-search-run-plan.js";
9-
10-
describe("buildCallableToolNamesForEmptyAllowlistCheck", () => {
11-
it("ignores auto-added Tool Search controls so bad allowlists still fail", () => {
12-
// Auto-added controls are not real callable tools when the backing catalog
13-
// is empty.
14-
expect(
15-
buildCallableToolNamesForEmptyAllowlistCheck({
16-
effectiveToolNames: ["tool_search_code"],
17-
autoAddedToolSearchControlNames: new Set(["tool_search_code"]),
18-
toolSearchCatalogToolCount: 0,
19-
}),
20-
).toEqual([]);
21-
});
22-
23-
it("counts cataloged tools hidden behind auto-added Tool Search controls", () => {
24-
expect(
25-
buildCallableToolNamesForEmptyAllowlistCheck({
26-
effectiveToolNames: ["tool_search_code"],
27-
autoAddedToolSearchControlNames: new Set(["tool_search_code"]),
28-
toolSearchCatalogToolCount: 1,
29-
}),
30-
).toEqual(["tool-search:0"]);
31-
});
32-
33-
it("keeps explicitly requested Tool Search controls callable", () => {
34-
expect(
35-
buildCallableToolNamesForEmptyAllowlistCheck({
36-
effectiveToolNames: ["tool_search_code"],
37-
autoAddedToolSearchControlNames: new Set(),
38-
toolSearchCatalogToolCount: 0,
39-
}),
40-
).toEqual(["tool_search_code"]);
41-
});
42-
});
43-
44-
describe("buildAutoAddedToolSearchControlNamesForAllowlistCheck", () => {
45-
it("treats controls as auto-added unless any explicit allowlist requested them", () => {
46-
expect(
47-
buildAutoAddedToolSearchControlNamesForAllowlistCheck({
48-
toolSearchControlsEnabled: true,
49-
explicitAllowlistSources: [{ entries: ["missing_tool"] }],
50-
controlNames: ["tool_search_code", "tool_search"],
51-
}),
52-
).toEqual(new Set(["tool_search_code", "tool_search"]));
53-
54-
expect(
55-
buildAutoAddedToolSearchControlNamesForAllowlistCheck({
56-
toolSearchControlsEnabled: true,
57-
explicitAllowlistSources: [{ entries: ["tool_search_code"] }],
58-
controlNames: ["tool_search_code", "tool_search"],
59-
}),
60-
).toEqual(new Set(["tool_search"]));
61-
});
62-
});
4+
import { buildToolSearchRunPlan } from "./attempt.tool-search-run-plan.js";
635

646
describe("buildToolSearchRunPlan", () => {
657
it("keeps compact visible names separate from replay-safe names", () => {
@@ -161,6 +103,19 @@ describe("buildToolSearchRunPlan", () => {
161103
expect(plan.emptyAllowlistCallableNames).toEqual([]);
162104
});
163105

106+
it("keeps explicitly requested Tool Search controls callable", () => {
107+
const plan = buildToolSearchRunPlan({
108+
visibleTools: [{ name: "tool_search_code" }] as never,
109+
uncompactedTools: [{ name: "tool_search_code" }] as never,
110+
clientToolsCataloged: true,
111+
catalogToolCount: 0,
112+
controlsEnabled: true,
113+
explicitAllowlistSources: [{ entries: ["tool_search_code"] }],
114+
});
115+
116+
expect(plan.emptyAllowlistCallableNames).toEqual(["tool_search_code"]);
117+
});
118+
164119
it("keeps uncataloged directory-mode client tools visible", () => {
165120
const plan = buildToolSearchRunPlan({
166121
visibleTools: [

src/agents/embedded-agent-runner/run/attempt.tool-search-run-plan.ts

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,56 +29,9 @@ type ToolSearchRunPlan = {
2929
replayAllowedToolNames: Set<string>;
3030
liveAllowedToolNames: Set<string>;
3131
capabilityToolNames: Set<string>;
32-
autoAddedControlNames?: Set<string>;
3332
emptyAllowlistCallableNames: string[];
3433
};
3534

36-
/**
37-
* Builds the callable-name list used to decide whether an allowlist is empty.
38-
* Auto-added tool-search controls are excluded so they do not make an otherwise
39-
* empty user/tool allowlist look populated.
40-
*/
41-
export function buildCallableToolNamesForEmptyAllowlistCheck(params: {
42-
effectiveToolNames: string[];
43-
autoAddedToolSearchControlNames?: Set<string>;
44-
toolSearchCatalogToolCount: number;
45-
}): string[] {
46-
return [
47-
...params.effectiveToolNames.filter(
48-
(toolName) => !params.autoAddedToolSearchControlNames?.has(toolName),
49-
),
50-
...Array.from(
51-
{ length: params.toolSearchCatalogToolCount },
52-
(_, index) => `tool-search:${index}`,
53-
),
54-
];
55-
}
56-
57-
/**
58-
* Identifies tool-search control names that were added by policy rather than
59-
* explicitly allowed by the user. Explicit controls stay visible to empty
60-
* allowlist checks because the user selected them.
61-
*/
62-
export function buildAutoAddedToolSearchControlNamesForAllowlistCheck(params: {
63-
toolSearchControlsEnabled: boolean;
64-
explicitAllowlistSources: Array<{ entries: string[] }>;
65-
controlNames?: readonly string[];
66-
}): Set<string> | undefined {
67-
if (!params.toolSearchControlsEnabled) {
68-
return undefined;
69-
}
70-
const explicitlyAllowed = new Set(
71-
params.explicitAllowlistSources.flatMap((source) =>
72-
source.entries.map((entry) => normalizeToolName(entry)),
73-
),
74-
);
75-
return new Set(
76-
(params.controlNames ?? TOOL_SEARCH_CONTROL_ALLOWLIST_NAMES).filter(
77-
(controlName) => !explicitlyAllowed.has(normalizeToolName(controlName)),
78-
),
79-
);
80-
}
81-
8235
function collectExplicitlyAllowedClientToolNames(params: {
8336
clientTools?: CollectAllowedToolNamesParams["clientTools"];
8437
explicitAllowlistSources: Array<{ entries: string[] }>;
@@ -153,11 +106,17 @@ export function buildToolSearchRunPlan(params: {
153106
liveAllowedToolNames.add(visibleName);
154107
}
155108
}
156-
const autoAddedControlNames = buildAutoAddedToolSearchControlNamesForAllowlistCheck({
157-
toolSearchControlsEnabled: params.controlsEnabled,
158-
explicitAllowlistSources: params.explicitAllowlistSources,
159-
controlNames: params.controlNames,
160-
});
109+
const explicitControlAllowlistNames = new Set(
110+
params.explicitAllowlistSources.flatMap((source) =>
111+
source.entries.map((entry) => normalizeToolName(entry)),
112+
),
113+
);
114+
const autoAddedControlNames = new Set(
115+
(params.controlsEnabled
116+
? (params.controlNames ?? TOOL_SEARCH_CONTROL_ALLOWLIST_NAMES)
117+
: []
118+
).filter((controlName) => !explicitControlAllowlistNames.has(normalizeToolName(controlName))),
119+
);
161120
const explicitlyAllowedClientToolNames = collectExplicitlyAllowedClientToolNames({
162121
clientTools: params.clientTools,
163122
explicitAllowlistSources: params.explicitAllowlistSources,
@@ -175,13 +134,11 @@ export function buildToolSearchRunPlan(params: {
175134
replayAllowedToolNames,
176135
liveAllowedToolNames,
177136
capabilityToolNames,
178-
autoAddedControlNames,
179137
emptyAllowlistCallableNames: [
180-
...buildCallableToolNamesForEmptyAllowlistCheck({
181-
effectiveToolNames: [...emptyAllowlistVisibleToolNames],
182-
autoAddedToolSearchControlNames: autoAddedControlNames,
183-
toolSearchCatalogToolCount: params.catalogToolCount,
184-
}),
138+
...[...emptyAllowlistVisibleToolNames].filter(
139+
(toolName) => !autoAddedControlNames.has(toolName),
140+
),
141+
...Array.from({ length: params.catalogToolCount }, (_, index) => `tool-search:${index}`),
185142
...explicitClientCallableNames,
186143
],
187144
};

0 commit comments

Comments
 (0)