Files
wehub-resource-sync 426e9eeabd
Benchmark Bridge Tests / benchmark (bunx @biomejs/biome check packages/lifeops-bench/src, benchmark-lint) (push) Waiting to run
Benchmark Bridge Tests / benchmark (bunx vitest run --config packages/lifeops-bench/vitest.config.ts --root packages/lifeops-bench --passWithNoTests, benchmark-tests) (push) Waiting to run
Build Agent Image / build-and-push (push) Waiting to run
Chat shell gestures / Chat shell gesture + parity e2e (push) Waiting to run
Cloud Gateway Discord / Test (push) Waiting to run
Cloud Gateway Webhook / Test (push) Waiting to run
Cloud Tests / lint-and-types (push) Waiting to run
Cloud Tests / unit-tests (push) Waiting to run
Cloud Tests / integration-tests (push) Waiting to run
Cloud Tests / e2e-tests (push) Blocked by required conditions
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
Deploy Apps Worker (Product 2) / Determine environment (push) Waiting to run
Deploy Apps Worker (Product 2) / Deploy apps worker to apps-control host (${{ needs.determine-env.outputs.environment }}) (push) Blocked by required conditions
Deploy Eliza Provisioning Worker / Determine environment (push) Waiting to run
Deploy Eliza Provisioning Worker / Deploy worker to Hetzner host (${{ needs.determine-env.outputs.environment }} @ ${{ needs.determine-env.outputs.deployment_sha }}) (push) Blocked by required conditions
Dev Smoke / Classify changed paths (push) Waiting to run
Dev Smoke / bun run dev onboarding chat (push) Blocked by required conditions
Dev Smoke / Vite HMR dependency-level smoke (push) Blocked by required conditions
Electrobun Submodule Guard / electrobun gitlink is fetchable (push) Waiting to run
Publish @elizaos/example-code / check_npm (push) Waiting to run
Publish @elizaos/example-code / publish_npm (push) Blocked by required conditions
Publish @elizaos/plugin-elizacloud / verify_version (push) Waiting to run
Publish @elizaos/plugin-elizacloud / publish_npm (push) Blocked by required conditions
Sandbox Live Smoke / Sandbox live smoke (push) Waiting to run
Snap Build & Test / Build Snap (amd64) (push) Waiting to run
Snap Build & Test / Build Snap (arm64) (push) Waiting to run
supply-chain / sbom (push) Waiting to run
supply-chain / vulnerability-scan (push) Waiting to run
Build, Push & Deploy to Phala Cloud / build-and-push (push) Waiting to run
Test Packaging / Validate Packaging Configs (push) Waiting to run
Test Packaging / PyPI on Python ${{ matrix.python }} (push) Waiting to run
Test Packaging / Pack & Test JS Tarballs (push) Waiting to run
Test Packaging / elizaos CLI global-install smoke (node + bun) (push) Waiting to run
UI Fixture E2E / ui-fixture-e2e (push) Waiting to run
UI Fixture E2E / fixture-e2e (push) Waiting to run
UI Story Gate / story-gate (push) Waiting to run
vault-ci / test (macos-latest) (push) Waiting to run
vault-ci / test (ubuntu-latest) (push) Waiting to run
vault-ci / test (windows-latest) (push) Waiting to run
vault-ci / app-core wiring tests (push) Waiting to run
verify-patches / verify patches/CHECKSUMS.sha256 (push) Waiting to run
Voice Benchmark Smoke / voice-emotion fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voiceagentbench fixture smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench-quality unit smoke (push) Waiting to run
Voice Benchmark Smoke / voicebench TypeScript unit (no audio) (push) Waiting to run
Voice Benchmark Smoke / voice bench smoke summary (push) Blocked by required conditions
Windows CI / windows ([bun run --cwd packages/app-core test bun run --cwd packages/elizaos test bun run --cwd packages/cloud/shared test], app-and-cli) (push) Waiting to run
Windows CI / windows ([bun run --cwd packages/scenario-runner test bun run --cwd packages/vault test bun run --cwd packages/security test bun run --cwd plugins/plugin-coding-tools test], framework-packages) (push) Waiting to run
Windows CI / windows ([bun run --cwd plugins/plugin-elizacloud test bun run --cwd plugins/plugin-discord test bun run --cwd plugins/plugin-anthropic test bun run --cwd plugins/plugin-openai test bun run --cwd plugins/plugin-app-control test bun run --cwd plugins/pl… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run build --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/agent --concurrency=4 node packages/scripts/run-bash-linux-only.mjs scripts/verify-riscv64-buildpaths.sh node packages/scripts/run… (push) Waiting to run
Windows CI / windows ([node packages/scripts/run-turbo.mjs run typecheck --filter=@elizaos/core --filter=@elizaos/shared --filter=@elizaos/cloud-shared --concurrency=4 bun run --cwd packages/core test bun run --cwd packages/shared test], core-runtime, 75) (push) Waiting to run
Test Packaging / Build & Test PyPI Package (push) Waiting to run
Voice Workbench / headless workbench (mocked backends) (push) Has been cancelled
Voice Workbench / real acoustic lane (nightly, provisioned only) (push) Has been cancelled
ci / test (push) Has been cancelled
ci / lint-and-format (push) Has been cancelled
ci / build (push) Has been cancelled
ci / dev-startup (push) Has been cancelled
gitleaks / gitleaks (push) Has been cancelled
Markdown Links / Relative Markdown Links (push) Has been cancelled
Quality (Extended) / Homepage Build (PR smoke) (push) Has been cancelled
Quality (Extended) / Comment-only diff guard (push) Has been cancelled
Quality (Extended) / Format + Type Safety Ratchet (push) Has been cancelled
Quality (Extended) / Develop Gate (secret scan + UI determinism) (push) Has been cancelled
Quality (Extended) / Develop Gate (lint) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:43:05 +08:00

270 lines
9.2 KiB
TypeScript

/**
* Integration tests for the assembled `codingToolsPlugin` — service registration,
* action wiring, and auto-enable gating — exercised in-process against the real
* filesystem.
*/
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
import type { IAgentRuntime, Memory, Service, UUID } from "@elizaos/core";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import * as pluginModule from "../src/index.js";
import codingToolsPlugin, {
availableToolsProvider,
CODING_TOOLS_CONTEXTS,
FILE_STATE_SERVICE,
FileStateService,
RIPGREP_SERVICE,
RipgrepService,
SANDBOX_SERVICE,
SandboxService,
SESSION_CWD_SERVICE,
SessionCwdService,
} from "../src/index.js";
const EXPECTED_ACTIONS = ["FILE", "SHELL", "WORKTREE"];
describe("@elizaos/plugin-coding-tools — plugin export shape", () => {
it("exports a Plugin with the expected name", () => {
expect(codingToolsPlugin.name).toBe("coding-tools");
expect(codingToolsPlugin.description).toBeTruthy();
});
it("registers the consolidated top-level coding actions", () => {
const actions = codingToolsPlugin.actions ?? [];
const names = actions.map((a) => a.name).sort();
expect(names).toEqual([...EXPECTED_ACTIONS].sort());
});
it("does not register legacy leaf actions as planner-facing actions", () => {
const names = new Set((codingToolsPlugin.actions ?? []).map((a) => a.name));
for (const legacyName of [
"READ",
"WRITE",
"EDIT",
"BASH",
"GREP",
"GLOB",
"LS",
"WEB_FETCH",
"ASK_USER_QUESTION",
"ENTER_WORKTREE",
"EXIT_WORKTREE",
]) {
expect(names.has(legacyName), legacyName).toBe(false);
}
});
it("FILE exposes only canonical file umbrella similes", () => {
const fileAction = (codingToolsPlugin.actions ?? []).find(
(action) => action.name === "FILE",
);
expect(fileAction?.similes).toEqual(["FILE_OPERATION", "FILE_IO"]);
});
it("each action has the required fields", () => {
for (const action of codingToolsPlugin.actions ?? []) {
expect(action.name, action.name).toBeTruthy();
expect(action.description, action.name).toBeTruthy();
expect(action.handler, action.name).toBeTypeOf("function");
expect(action.validate, action.name).toBeTypeOf("function");
}
});
it("action names are unique", () => {
const names = (codingToolsPlugin.actions ?? []).map((a) => a.name);
expect(new Set(names).size).toBe(names.length);
});
it("exports the 4 active services", () => {
const services = codingToolsPlugin.services ?? [];
expect(services).toContain(FileStateService);
expect(services).toContain(SandboxService);
expect(services).toContain(SessionCwdService);
expect(services).toContain(RipgrepService);
expect(services.length).toBe(4);
});
it("does not export removed actions or service constants", () => {
expect("bashAction" in pluginModule).toBe(false);
expect("BASH_AST_SERVICE" in pluginModule).toBe(false);
expect("OS_SANDBOX_SERVICE" in pluginModule).toBe(false);
expect("SHELL_TASK_SERVICE" in pluginModule).toBe(false);
expect("ShellTaskService" in pluginModule).toBe(false);
expect("notebookEditAction" in pluginModule).toBe(false);
expect("taskOutputAction" in pluginModule).toBe(false);
expect("taskStopAction" in pluginModule).toBe(false);
expect("todoWriteAction" in pluginModule).toBe(false);
});
it("exports the available-tools provider at position -10 with code/terminal/automation gates", () => {
expect(codingToolsPlugin.providers ?? []).toContain(availableToolsProvider);
expect(availableToolsProvider.position).toBe(-10);
expect(availableToolsProvider.contexts).toEqual([...CODING_TOOLS_CONTEXTS]);
});
it("validate ignores CODING_TOOLS_DISABLE — kill switch was removed", async () => {
const runtime = {
agentId: "00000000-0000-0000-0000-000000000000",
getSetting: (key: string) =>
key === "CODING_TOOLS_DISABLE" ? true : undefined,
getService: () => null,
} as IAgentRuntime;
const message = { roomId: "r" } as Memory;
for (const action of codingToolsPlugin.actions ?? []) {
const ok = await action.validate?.(runtime, message);
expect(ok, action.name).toBe(true);
}
});
});
describe("@elizaos/plugin-coding-tools — end-to-end smoke", () => {
let tmpDir: string;
let runtime: IAgentRuntime;
let services: Map<string, Service>;
const cleanup: Array<() => Promise<void>> = [];
beforeAll(async () => {
tmpDir = await fs.realpath(
await fs.mkdtemp(path.join(os.tmpdir(), "ct-integ-")),
);
await fs.writeFile(
path.join(tmpDir, "needle.txt"),
"this file contains the NEEDLE word\n",
"utf8",
);
await fs.writeFile(
path.join(tmpDir, "other.md"),
"# heading\n\nbody text\n",
"utf8",
);
services = new Map();
runtime = {
agentId: "00000000-0000-0000-0000-000000000000" as UUID,
getSetting: (_key: string) => undefined,
getService: (key: string) => services.get(key) ?? null,
} as IAgentRuntime;
const fileState = await FileStateService.start(runtime);
const sandbox = await SandboxService.start(runtime);
const session = await SessionCwdService.start(runtime);
const rg = await RipgrepService.start(runtime);
services.set(FILE_STATE_SERVICE, fileState);
services.set(SANDBOX_SERVICE, sandbox);
services.set(SESSION_CWD_SERVICE, session);
services.set(RIPGREP_SERVICE, rg);
cleanup.push(() => fileState.stop());
cleanup.push(() => sandbox.stop());
cleanup.push(() => session.stop());
cleanup.push(() => rg.stop());
session.setCwd("smoke-room", tmpDir);
});
afterAll(async () => {
for (const fn of cleanup) {
try {
await fn();
} catch {
// ignore
}
}
await fs.rm(tmpDir, { recursive: true, force: true });
});
function findAction(name: string) {
const actions = codingToolsPlugin.actions ?? [];
const a = actions.find((x) => x.name === name);
if (!a) throw new Error(`action ${name} not found`);
return a;
}
function makeMessage(): Memory {
return { roomId: "smoke-room" } as Memory;
}
it("FILE action=read returns a known file's contents", async () => {
const action = findAction("FILE");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: {
action: "read",
file_path: path.join(tmpDir, "needle.txt"),
},
});
expect(result.success).toBe(true);
expect(result.text).toContain("NEEDLE");
});
it("FILE action=write creates a new file", async () => {
const action = findAction("FILE");
const target = path.join(tmpDir, "smoke-out.txt");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { action: "write", file_path: target, content: "smoke ok" },
});
expect(result.success).toBe(true);
const written = await fs.readFile(target, "utf8");
expect(written).toBe("smoke ok");
});
it("SHELL echo hello", async () => {
const action = findAction("SHELL");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { command: "echo smoke-bash-ok", cwd: tmpDir },
});
expect(result.success).toBe(true);
expect(result.text).toContain("smoke-bash-ok");
expect(result.text).toContain("[exit 0]");
});
it("FILE action=glob lists *.txt files", async () => {
const action = findAction("FILE");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { action: "glob", pattern: "*.txt", path: tmpDir },
});
expect(result.success).toBe(true);
expect(result.text).toContain("needle.txt");
});
it("FILE action=ls shows fixture entries", async () => {
const action = findAction("FILE");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { action: "ls", path: tmpDir },
});
expect(result.success).toBe(true);
expect(result.text).toContain("needle.txt");
expect(result.text).toContain("other.md");
});
it("GREP finds the NEEDLE token (skip when ripgrep absent)", async () => {
const rg = services.get(RIPGREP_SERVICE) as RipgrepService | undefined;
if (!rg) return;
const fs2 = await import("node:fs");
const initial = rg.binary();
if (!fs2.existsSync(initial)) {
const candidates = [
"/opt/homebrew/bin/rg",
"/usr/local/bin/rg",
"/usr/bin/rg",
];
const sys = candidates.find((p) => fs2.existsSync(p));
if (!sys) return;
(rg as { rgPath: string }).rgPath = sys;
}
const action = findAction("FILE");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { action: "grep", pattern: "NEEDLE", path: tmpDir },
});
expect(result.success).toBe(true);
expect(result.text).toContain("needle.txt");
});
it("WORKTREE action=enter in a non-git dir fails cleanly", async () => {
const action = findAction("WORKTREE");
const result = await action.handler?.(runtime, makeMessage(), undefined, {
parameters: { action: "enter" },
});
expect(result.success).toBe(false);
expect(result.text).toContain("io_error");
});
});