feat(ai-tools): omp shared + OpenCode/omp sessions, tokens, subagents; Grok hooks; resume by id

- omp-usage.js / opencode-usage.js: poll each CLI's own session store into
  sessions (tool omp/opencode), token_usage per model and subagent agents;
  transcript routes read them (omp journals, OpenCode's opencode.db).
- Grok runs the Claude hooks in ~/.claude/settings.json: its hooks are now
  recognised (hookEventName + workspaceRoot), tagged tool=grok, and their
  transcript_path dropped instead of being parsed as a Claude transcript.
- Catalog `resumeById`: omp/Grok/Cursor `--resume <id>`, OpenCode
  `--session <id>`; session ids may carry `_`. The client offers Resume for
  any tool that resumes by id.
- Kimi Code and GitHub Copilot leave the catalog (their session stores could
  not be verified).
This commit is contained in:
2026-10-19 16:49:59 +07:00
parent 0434ffc750
commit f8d07dab61
33 changed files with 2007 additions and 103 deletions
+5 -5
View File
@@ -190,16 +190,16 @@ describe("install / update / icons", () => {
it("starts an install job and reports it", async () => {
jobs.__reset();
assert.equal((await request("GET", "/api/ai-tools/job")).body, null);
const r = await request("POST", "/api/ai-tools/copilot/install");
const r = await request("POST", "/api/ai-tools/codex/install");
assert.equal(r.status, 202);
assert.equal(r.body.job.tool, "copilot");
assert.equal(r.body.job.tool, "codex");
const done = await settle();
assert.equal(done.status, "ok");
assert.deepEqual(spawned.at(-1), [
path.join(BIN, "npm"),
"install",
"-g",
"@github/copilot@latest",
"@openai/codex@latest",
]);
});
@@ -216,7 +216,7 @@ describe("install / update / icons", () => {
const notNpm = await request("POST", "/api/ai-tools/opencode/update");
assert.equal(notNpm.status, 409);
assert.equal(notNpm.body.error.code, "ENOTNPM");
const cross = await request("POST", "/api/ai-tools/copilot/install", undefined, {
const cross = await request("POST", "/api/ai-tools/codex/install", undefined, {
Origin: "https://evil.test",
});
assert.equal(cross.status, 403);
@@ -225,7 +225,7 @@ describe("install / update / icons", () => {
it("answers EBUSY while a job runs", async () => {
jobs.__reset({ timeoutMs: 50 });
hang = true;
assert.equal((await request("POST", "/api/ai-tools/copilot/install")).status, 202);
assert.equal((await request("POST", "/api/ai-tools/codex/install")).status, 202);
const busy = await request("POST", "/api/ai-tools/amp/install");
assert.equal(busy.status, 409);
assert.equal(busy.body.error.code, "EBUSY");