diff --git a/src/agents/bash-tools.exec.pty.test.ts b/src/agents/bash-tools.exec.pty.test.ts index 88238ae1592..cab80619a98 100644 --- a/src/agents/bash-tools.exec.pty.test.ts +++ b/src/agents/bash-tools.exec.pty.test.ts @@ -83,28 +83,20 @@ async function waitForSessionCompletion(params: { .toBe(true); } -test("exec supports pty output", async () => { +test("exec supports pty output and OPENCLAW_SHELL", async () => { const result = await runPtyCommand( - currentNodeEvalCommand("process.stdout.write(String.fromCharCode(111,107))"), + currentNodeEvalCommand('process.stdout.write(`ok:${process.env.OPENCLAW_SHELL || ""}`)'), ); expect(result.status).toBe("completed"); expect(result.aggregated).toContain("ok"); -}); - -test("exec sets OPENCLAW_SHELL in pty mode", async () => { - const result = await runPtyCommand( - currentNodeEvalCommand('process.stdout.write(process.env.OPENCLAW_SHELL || "")'), - ); - - expect(result.status).toBe("completed"); expect(result.aggregated).toContain("exec"); }); -test("process send-keys encodes Enter for pty sessions", async () => { +test("process send-keys and submit send Enter for pty sessions", async () => { const { processTool, sessionId } = await startPtySession( currentNodeEvalCommand( - "const dataEvent=String.fromCharCode(100,97,116,97);process.stdin.on(dataEvent,d=>{process.stdout.write(d);if(d.includes(10)||d.includes(13))process.exit(0);});", + "const dataEvent=String.fromCharCode(100,97,116,97);const submitted=String.fromCharCode(115,117,98,109,105,116,116,101,100);let first=false;process.stdin.on(dataEvent,d=>{process.stdout.write(d);if(d.includes(10)||d.includes(13)){if(first){process.stdout.write(submitted);process.exit(0);}first=true;}});", ), ); @@ -114,16 +106,6 @@ test("process send-keys encodes Enter for pty sessions", async () => { keys: ["h", "i", "Enter"], }); - await waitForSessionCompletion({ processTool, sessionId, expectedText: "hi" }); -}); - -test("process submit sends Enter for pty sessions", async () => { - const { processTool, sessionId } = await startPtySession( - currentNodeEvalCommand( - "const dataEvent=String.fromCharCode(100,97,116,97);const submitted=String.fromCharCode(115,117,98,109,105,116,116,101,100);process.stdin.on(dataEvent,d=>{if(d.includes(10)||d.includes(13)){process.stdout.write(submitted);process.exit(0);}});", - ), - ); - await processTool.execute("toolcall", { action: "submit", sessionId, diff --git a/src/agents/bundle-mcp-config.test.ts b/src/agents/bundle-mcp-config.test.ts index b05cb4f08dd..cc1e8de3c56 100644 --- a/src/agents/bundle-mcp-config.test.ts +++ b/src/agents/bundle-mcp-config.test.ts @@ -1,50 +1,49 @@ -import { afterEach, describe, expect, it } from "vitest"; -import { - createBundleMcpTempHarness, - createBundleProbePlugin, - withBundleHomeEnv, -} from "../plugins/bundle-mcp.test-support.js"; +import { describe, expect, it, vi } from "vitest"; import { loadMergedBundleMcpConfig, toCliBundleMcpServerConfig } from "./bundle-mcp-config.js"; -const tempHarness = createBundleMcpTempHarness(); +const mocks = vi.hoisted(() => ({ + bundleMcp: { + config: { + mcpServers: { + bundleProbe: { + command: "node", + args: ["./servers/probe.mjs"], + }, + }, + }, + diagnostics: [], + }, +})); -afterEach(async () => { - await tempHarness.cleanup(); -}); +vi.mock("../plugins/bundle-mcp.js", () => ({ + loadEnabledBundleMcpConfig: () => mocks.bundleMcp, +})); describe("loadMergedBundleMcpConfig", () => { - it("lets OpenClaw mcp.servers override bundle defaults while preserving raw transport shape", async () => { - await withBundleHomeEnv( - tempHarness, - "openclaw-bundle-mcp-config", - async ({ homeDir, workspaceDir }) => { - await createBundleProbePlugin(homeDir); - - const merged = loadMergedBundleMcpConfig({ - workspaceDir, - cfg: { - plugins: { - entries: { - "bundle-probe": { enabled: true }, - }, - }, - mcp: { - servers: { - bundleProbe: { - transport: "streamable-http", - url: "https://mcp.example.com/mcp", - }, - }, + it("lets OpenClaw mcp.servers override bundle defaults while preserving raw transport shape", () => { + const merged = loadMergedBundleMcpConfig({ + workspaceDir: "/workspace", + cfg: { + plugins: { + entries: { + "bundle-probe": { enabled: true }, + }, + }, + mcp: { + servers: { + bundleProbe: { + transport: "streamable-http", + url: "https://mcp.example.com/mcp", }, }, - }); - - expect(merged.config.mcpServers.bundleProbe).toEqual({ - transport: "streamable-http", - url: "https://mcp.example.com/mcp", - }); + }, }, - ); + }); + + expect(merged.config.mcpServers.bundleProbe).toEqual({ + transport: "streamable-http", + url: "https://mcp.example.com/mcp", + }); }); it("maps OpenClaw transports to downstream CLI types when requested", () => { diff --git a/src/agents/lanes.ts b/src/agents/lanes.ts index adb4e0a9393..c9d56a48073 100644 --- a/src/agents/lanes.ts +++ b/src/agents/lanes.ts @@ -3,6 +3,7 @@ import { CommandLane } from "../process/lanes.js"; export const AGENT_LANE_NESTED = CommandLane.Nested; export const AGENT_LANE_CRON_NESTED = CommandLane.CronNested; export const AGENT_LANE_SUBAGENT = CommandLane.Subagent; +const AGENT_LANE_CRON: string = CommandLane.Cron; const NESTED_LANE = "nested"; const NESTED_LANE_PREFIX = `${NESTED_LANE}:`; @@ -18,7 +19,7 @@ export function resolveCronAgentLane(lane?: string): string { const trimmed = lane?.trim(); // Cron jobs already occupy the outer cron lane, so inner agent work needs // its own lane to avoid self-deadlock without widening shared nested flows. - if (!trimmed || trimmed === CommandLane.Cron) { + if (!trimmed || trimmed === AGENT_LANE_CRON) { return AGENT_LANE_CRON_NESTED; } return trimmed; diff --git a/test/scripts/ts-topology.test.ts b/test/scripts/ts-topology.test.ts index fb07f1ac84a..70dfa32d266 100644 --- a/test/scripts/ts-topology.test.ts +++ b/test/scripts/ts-topology.test.ts @@ -1,6 +1,6 @@ import path from "node:path"; import { describe, expect, it } from "vitest"; -import { analyzeTopology } from "../../scripts/lib/ts-topology/analyze.js"; +import { analyzeTopology, filterRecordsForReport } from "../../scripts/lib/ts-topology/analyze.js"; import { renderTextReport } from "../../scripts/lib/ts-topology/reports.js"; import { createFilesystemPublicSurfaceScope } from "../../scripts/lib/ts-topology/scope.js"; import { main } from "../../scripts/ts-topology.ts"; @@ -22,16 +22,17 @@ const publicSurfaceEnvelope = analyzeTopology({ scope: fixtureScope, report: "public-surface-usage", }); -const singleOwnerEnvelope = analyzeTopology({ - repoRoot, - scope: fixtureScope, - report: "single-owner-shared", -}); -const unusedEnvelope = analyzeTopology({ - repoRoot, - scope: fixtureScope, - report: "unused-public-surface", -}); + +function deriveReportEnvelope(report: Parameters[1]) { + return { + ...publicSurfaceEnvelope, + report, + records: filterRecordsForReport(publicSurfaceEnvelope.records, report), + }; +} + +const singleOwnerEnvelope = deriveReportEnvelope("single-owner-shared"); +const unusedEnvelope = deriveReportEnvelope("unused-public-surface"); describe("ts-topology", () => { it("collapses canonical symbols exported by multiple public subpaths", () => { @@ -100,7 +101,7 @@ describe("ts-topology", () => { `); }); - it("emits stable JSON and filtered report output through the CLI", async () => { + it("emits stable JSON through the CLI and filtered report output", async () => { const captured = createCapturedIo(); const jsonExit = await main( [ @@ -121,27 +122,13 @@ describe("ts-topology", () => { payload.records.map((record: { exportNames: string[] }) => record.exportNames[0]), ).toEqual(["aliasedThing", "singleOwnerHelper"]); - const textCaptured = createCapturedIo(); - const textExit = await main( - [ - "--scope=custom", - "--entrypoint-root=src/public", - "--import-prefix=fixture-sdk", - "--repo-root=test/fixtures/ts-topology/basic", - "--report=consumer-topology", - "--limit=2", - ], - textCaptured.io, - ); - expect(textExit).toBe(0); - expect(textCaptured.readStdout()).toMatchInlineSnapshot(` + expect(renderTextReport(deriveReportEnvelope("consumer-topology"), 2)).toMatchInlineSnapshot(` "Scope: custom Records with consumers: 5 Top 2 consumer-topology records: - fixture-sdk:sharedThing prod=3 test=0 internal=0 - - fixture-sdk:SharedType prod=2 test=0 internal=0 - " + - fixture-sdk:SharedType prod=2 test=0 internal=0" `); }); });