Files
Termix/plugins/ai/tests/backend/agents/install.test.ts
T
ZacharyZcR bf817d8bd1 feat(ai): add remote coding agent sessions for SSH hosts (#1545)
* feat(ai): add host coding agent sessions with configured providers

* Wait for agent initialization and shutdown before resuming sessions

* Keep Pi transcripts outside its configuration migration directory

* Cover Pi session directory separation

* Update core permission catalog expectations for remote agents

* Install remote agent runtimes from verified official sources

* Wait for the identity panel credentials request in its test

* Add scoped SSH forwarding setup for remote agents

* Verify forwarding setup through a fresh SSH connection

* Bound the SSH forwarding verification timeout

* Return the forwarding verification callback result

* fix(ai): support agent send and interrupt keyboard shortcuts

* feat(ai): add agent queues attachments worktrees and session recovery

* fix(ai): handle deleted workspace paths and duplicate status events

* fix(ai): accept file references in root workspaces

* fix(ai): refresh paused queues after agent errors

* fix(ai): store agent sessions in plugin tables and lock down OpenCode

Agent sessions lived in a single ctx.kv value holding up to 2000 events,
which passed the 256 KB kv limit within minutes of a normal run. Every
save after that failed, so history, nativeId, queue and archive changes
were lost and every event wrote a kv audit row.

Sessions, events and queued prompts now have their own tables. A running
agent appends events in batches at most once a second with a lazy save;
session and queue changes save immediately. Prompts, drafts and stored
event text are capped in UTF-8 bytes to fit MySQL TEXT. kv:own is no
longer requested.

The OpenCode server now requires a per-session password, so other local
accounts on the host cannot drive the agent through its loopback port.

* test(upgrade): include agent session tables in the reviewed upgrade SQL
2026-10-03 16:01:47 -05:00

75 lines
2.2 KiB
TypeScript

import { expect, it } from "vitest";
import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { execFileSync } from "node:child_process";
import { installScript } from "../../../src/backend/agents/install.js";
it("refuses corrupted Node.js downloads before extracting or installing", async () => {
const dir = await mkdtemp(join(tmpdir(), "termix-install-"));
try {
const bin = join(dir, "bin");
await mkdir(bin);
await writeFile(
join(bin, "curl"),
`#!/bin/sh
for arg do previous="$last"; last="$arg"; done
case "$last" in
*SHASUMS256.txt) printf '%064d node-v24.1.0-linux-x64.tar.gz\\n' 0 > "$last";;
*) echo corrupted > "$last";;
esac
`,
{ mode: 0o700 },
);
await writeFile(
join(bin, "uname"),
'#!/bin/sh\nif [ "$1" = -s ]; then echo Linux; else echo x86_64; fi\n',
{ mode: 0o700 },
);
let output = "";
try {
execFileSync("sh", ["-c", installScript("pi")], {
env: { ...process.env, HOME: dir, PATH: `${bin}:${process.env.PATH}` },
stdio: "pipe",
});
} catch (error) {
output = String((error as { stdout: Buffer }).stdout);
}
expect(output).toContain("Node.js SHA-256 verification failed");
await expect(
readFile(join(dir, ".local/share/termix-agent-runtime/node/bin/node")),
).rejects.toThrow();
} finally {
await rm(dir, { recursive: true, force: true });
}
});
it("rejects a mirror dependency before npm ci", async () => {
const dir = await mkdtemp(join(tmpdir(), "termix-registry-"));
try {
const script = installScript("pi")
.split("node - <<'VERIFY'\n")[1]
.split("\nVERIFY")[0];
await writeFile(
join(dir, "package-lock.json"),
JSON.stringify({
packages: {
"node_modules/injected": {
version: "1.0.0",
resolved: "https://mirror.invalid/pkg.tgz",
integrity: "sha512-fake",
},
},
}),
);
expect(() =>
execFileSync(process.execPath, ["-e", script], {
cwd: dir,
stdio: "pipe",
}),
).toThrow();
} finally {
await rm(dir, { recursive: true, force: true });
}
});