ai-whisper 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/dist/bin/broker-daemon.js +225 -21
- package/dist/bin/companion-agent.js +779 -51
- package/dist/bin/relay-monitor.js +3 -3
- package/dist/bin/turn-event-shim.js +18 -6
- package/dist/bin/whisper.js +1184 -163
- package/package.json +6 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ai-whisper
|
|
2
2
|
|
|
3
|
-
ai-whisper pairs two coding agents — mount any two of Claude, Codex, and
|
|
3
|
+
ai-whisper pairs two coding agents — mount any two of Claude, Codex, ezio, and agy — into a terminal-native pair that hand work back and forth under a single baton, so one agent implements while the other reviews, and a structured workflow drives the loop to a finished, reviewed deliverable without a human babysitting every round.
|
|
4
4
|
|
|
5
5
|
## Magic moment
|
|
6
6
|
|
|
@@ -52,13 +52,14 @@ It is **not** for:
|
|
|
52
52
|
|
|
53
53
|
## Prerequisites
|
|
54
54
|
|
|
55
|
-
You pair any two of
|
|
55
|
+
You pair any two of four agents — `claude`, `codex`, `ezio`, and `agy`. ai-whisper drives the *real* Claude, Codex, and Antigravity CLIs, so install and authenticate whichever of those you plan to mount first; `ezio` is protocol-native and ships with ai-whisper.
|
|
56
56
|
|
|
57
57
|
- **[Claude Code CLI](https://claude.com/claude-code)** — the `claude` command, signed in.
|
|
58
58
|
- **[Codex CLI](https://github.com/openai/codex)** — the `codex` command, signed in.
|
|
59
59
|
- **ezio** *(optional)* — bundled with ai-whisper; mount it with `whisper collab mount ezio`, no separate install.
|
|
60
|
+
- **agy** *(optional)* — the `agy` command (Antigravity CLI), signed in. Mount it with `whisper collab mount agy` (manual-mount parity; no auto-launch via `collab start`).
|
|
60
61
|
- **Node.js 22+**.
|
|
61
|
-
- **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
62
|
+
- **An LLM evaluator with credentials** — workflows are gated by it and refuse to start without it. It supports four providers: **Anthropic** (default), a local **Ollama** model, **OpenAI** (and any OpenAI-compatible backend via a `baseURL` — Azure, OpenRouter, vLLM, LM Studio, …), or reusing an **already-mounted agent CLI** (`claude`/`codex`/`agy`) in non-interactive mode (no separate key — it reuses that CLI's own auth). See [Evaluator configuration](docs/evaluator-configuration.md).
|
|
62
63
|
- **tmux** *(optional)* — only for `whisper collab start`, which auto-arranges both agents into panes. The mount flow below does not need it.
|
|
63
64
|
|
|
64
65
|
> **Platform support:** ai-whisper is terminal-native and Unix-oriented — it drives interactive PTY sessions, so it runs on **macOS and Linux**. It is **not supported natively on Windows**: `whisper collab mount` / `reconnect` require a Unix tty-backed shell and will exit with an error pointing here. On Windows, run ai-whisper inside **[WSL2](https://learn.microsoft.com/windows/wsl/install)** — install Node, your agent CLI, and ai-whisper inside the WSL2 distro and run the commands there, where everything works as-is.
|
|
@@ -120,9 +121,9 @@ A run that stops short usually **escalates** — it does not crash. When the eva
|
|
|
120
121
|
|
|
121
122
|
## Core concepts
|
|
122
123
|
|
|
123
|
-
ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* agent sessions in your terminal — Claude or Codex CLIs, or
|
|
124
|
+
ai-whisper is **not a swarm**. The agents never type at once — work moves by a single baton, one owner at a time. Mounted sessions are *real* agent sessions in your terminal — Claude or Codex CLIs, ezio, or agy — and those sessions are the source of truth. Autonomy is supervised: every handoff, verdict, and round is inspectable, and runs are resumable rather than fire-and-forget. Work is organized as structured workflows — explicit loops and state transitions, not a free-form chat.
|
|
124
125
|
|
|
125
|
-
Claude, Codex, and
|
|
126
|
+
Claude, Codex, ezio, and agy are supported today — you mount any two of them; the architecture is provider-agnostic by design, so other coding-agent CLIs can be added behind the same relay.
|
|
126
127
|
|
|
127
128
|
For the full mental model, read [Concepts](docs/concepts.md).
|
|
128
129
|
|
|
@@ -63,7 +63,7 @@ var threadStates = [
|
|
|
63
63
|
"failed"
|
|
64
64
|
];
|
|
65
65
|
var collabStates = ["active", "stopped"];
|
|
66
|
-
var agentTypes = ["codex", "claude", "ezio"];
|
|
66
|
+
var agentTypes = ["codex", "claude", "ezio", "agy"];
|
|
67
67
|
var sessionRegistrationStates = ["registered"];
|
|
68
68
|
var workItemStates = [
|
|
69
69
|
"queued",
|
|
@@ -332,7 +332,7 @@ var mockProviderReplySchema = z14.object({
|
|
|
332
332
|
|
|
333
333
|
// ../shared/dist/relay-host.js
|
|
334
334
|
import { z as z15 } from "zod";
|
|
335
|
-
var relayTargets = ["codex", "claude", "ezio", "pull"];
|
|
335
|
+
var relayTargets = ["codex", "claude", "ezio", "agy", "pull"];
|
|
336
336
|
var relayDirectiveSchema = z15.object({
|
|
337
337
|
raw: z15.string().min(1),
|
|
338
338
|
target: z15.enum(relayTargets),
|
|
@@ -5918,7 +5918,45 @@ function createWorkflowEventBridge(input) {
|
|
|
5918
5918
|
import * as crypto3 from "node:crypto";
|
|
5919
5919
|
import Anthropic from "@anthropic-ai/sdk";
|
|
5920
5920
|
import { Ollama } from "ollama";
|
|
5921
|
+
import OpenAI from "openai";
|
|
5921
5922
|
import { z as z18 } from "zod";
|
|
5923
|
+
|
|
5924
|
+
// src/runtime/agent-cli-presets.ts
|
|
5925
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
5926
|
+
import { accessSync, constants, statSync as statSync3 } from "node:fs";
|
|
5927
|
+
import { delimiter, join as join6 } from "node:path";
|
|
5928
|
+
var defaultSpawn = (command, args, options) => nodeSpawn(command, [...args], options);
|
|
5929
|
+
var PRESETS = {
|
|
5930
|
+
claude: { executable: "claude", execArgs: ["-p"], promptVia: "arg" },
|
|
5931
|
+
codex: { executable: "codex", execArgs: ["exec"], promptVia: "arg" },
|
|
5932
|
+
agy: { executable: "agy", execArgs: ["-p"], promptVia: "arg" }
|
|
5933
|
+
};
|
|
5934
|
+
function resolveAgentCliInvocation(input) {
|
|
5935
|
+
const preset = PRESETS[input.agent];
|
|
5936
|
+
return {
|
|
5937
|
+
executable: input.executable ?? preset.executable,
|
|
5938
|
+
execArgs: input.execArgs ?? preset.execArgs,
|
|
5939
|
+
promptVia: input.promptVia ?? preset.promptVia
|
|
5940
|
+
};
|
|
5941
|
+
}
|
|
5942
|
+
function isExecutableOnPath(executable) {
|
|
5943
|
+
const isExecFile = (p) => {
|
|
5944
|
+
try {
|
|
5945
|
+
if (!statSync3(p).isFile()) return false;
|
|
5946
|
+
accessSync(p, constants.X_OK);
|
|
5947
|
+
return true;
|
|
5948
|
+
} catch {
|
|
5949
|
+
return false;
|
|
5950
|
+
}
|
|
5951
|
+
};
|
|
5952
|
+
if (executable.includes("/") || executable.includes("\\")) return isExecFile(executable);
|
|
5953
|
+
const dirs = (process.env.PATH ?? "").split(delimiter).filter(Boolean);
|
|
5954
|
+
const exts = process.platform === "win32" ? ["", ".exe", ".cmd", ".bat"] : [""];
|
|
5955
|
+
return dirs.some((dir) => exts.some((ext) => isExecFile(join6(dir, executable + ext))));
|
|
5956
|
+
}
|
|
5957
|
+
|
|
5958
|
+
// src/runtime/relay-orchestrator-evaluator.ts
|
|
5959
|
+
var defaultOpenAIFactory = (opts) => new OpenAI(opts);
|
|
5922
5960
|
var baseFields = {
|
|
5923
5961
|
confidence: z18.number().min(0).max(1),
|
|
5924
5962
|
reason: z18.string()
|
|
@@ -6158,7 +6196,10 @@ function selectBranch(payload) {
|
|
|
6158
6196
|
}
|
|
6159
6197
|
return legacyBranch;
|
|
6160
6198
|
}
|
|
6199
|
+
var ProviderUnavailableError = class extends Error {
|
|
6200
|
+
};
|
|
6161
6201
|
function isProviderUnavailableError(error) {
|
|
6202
|
+
if (error instanceof ProviderUnavailableError) return true;
|
|
6162
6203
|
if (!(error instanceof Error)) return false;
|
|
6163
6204
|
const code = error.code;
|
|
6164
6205
|
if (code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "ETIMEDOUT" || code === "ECONNRESET")
|
|
@@ -6222,6 +6263,77 @@ function buildOllamaCaller(config) {
|
|
|
6222
6263
|
return { raw: response.message.content };
|
|
6223
6264
|
};
|
|
6224
6265
|
}
|
|
6266
|
+
function buildOpenAICaller(config) {
|
|
6267
|
+
const client = (config.createClient ?? defaultOpenAIFactory)({
|
|
6268
|
+
apiKey: config.apiKey,
|
|
6269
|
+
...config.baseURL ? { baseURL: config.baseURL } : {}
|
|
6270
|
+
});
|
|
6271
|
+
const model = config.model;
|
|
6272
|
+
return async function(systemPrompt, payload, jsonSchema) {
|
|
6273
|
+
const response = await client.chat.completions.create({
|
|
6274
|
+
model,
|
|
6275
|
+
messages: [
|
|
6276
|
+
{ role: "system", content: systemPrompt },
|
|
6277
|
+
{ role: "user", content: JSON.stringify(payload) }
|
|
6278
|
+
],
|
|
6279
|
+
// Non-strict json_schema: schema-shaped guidance only; branch.parse() is authoritative.
|
|
6280
|
+
// `strict` is intentionally omitted (see spec §5.2.1) — do not set it true.
|
|
6281
|
+
response_format: { type: "json_schema", json_schema: { name: "verdict", schema: jsonSchema } },
|
|
6282
|
+
temperature: 0.3
|
|
6283
|
+
});
|
|
6284
|
+
const raw = response.choices[0]?.message.content ?? "";
|
|
6285
|
+
return {
|
|
6286
|
+
raw,
|
|
6287
|
+
...typeof response.usage?.prompt_tokens === "number" ? { inputTokens: response.usage.prompt_tokens } : {},
|
|
6288
|
+
...typeof response.usage?.completion_tokens === "number" ? { outputTokens: response.usage.completion_tokens } : {}
|
|
6289
|
+
};
|
|
6290
|
+
};
|
|
6291
|
+
}
|
|
6292
|
+
function buildAgentCliCaller(config) {
|
|
6293
|
+
const { executable, execArgs, promptVia } = resolveAgentCliInvocation(config);
|
|
6294
|
+
const spawnImpl = config.spawnImpl ?? defaultSpawn;
|
|
6295
|
+
return function(systemPrompt, payload) {
|
|
6296
|
+
const prompt = `${systemPrompt}
|
|
6297
|
+
|
|
6298
|
+
=== INPUT (JSON) ===
|
|
6299
|
+
${JSON.stringify(payload)}
|
|
6300
|
+
=== END INPUT ===
|
|
6301
|
+
|
|
6302
|
+
Reply with ONLY the JSON object described above \u2014 no prose, no code fence.`;
|
|
6303
|
+
return new Promise((resolve2, reject) => {
|
|
6304
|
+
const child = promptVia === "arg" ? spawnImpl(executable, [...execArgs, prompt], { stdio: ["ignore", "pipe", "pipe"] }) : spawnImpl(executable, execArgs, { stdio: ["pipe", "pipe", "pipe"] });
|
|
6305
|
+
let stdout = "";
|
|
6306
|
+
let stderr = "";
|
|
6307
|
+
let settled = false;
|
|
6308
|
+
child.stdout.on("data", (chunk) => {
|
|
6309
|
+
stdout += String(chunk);
|
|
6310
|
+
});
|
|
6311
|
+
child.stderr.on("data", (chunk) => {
|
|
6312
|
+
stderr += String(chunk);
|
|
6313
|
+
});
|
|
6314
|
+
child.on("error", (err) => {
|
|
6315
|
+
if (settled) return;
|
|
6316
|
+
settled = true;
|
|
6317
|
+
reject(new ProviderUnavailableError(`agent-cli spawn failed (${executable}): ${err.message}`));
|
|
6318
|
+
});
|
|
6319
|
+
child.on("close", (code) => {
|
|
6320
|
+
if (settled) return;
|
|
6321
|
+
settled = true;
|
|
6322
|
+
if (code !== 0) {
|
|
6323
|
+
reject(new ProviderUnavailableError(`agent-cli ${executable} exited with code ${code}: ${stderr.trim()}`));
|
|
6324
|
+
return;
|
|
6325
|
+
}
|
|
6326
|
+
resolve2({ raw: stdout });
|
|
6327
|
+
});
|
|
6328
|
+
if (promptVia === "stdin") {
|
|
6329
|
+
child.stdin.on("error", () => {
|
|
6330
|
+
});
|
|
6331
|
+
child.stdin.write(prompt);
|
|
6332
|
+
child.stdin.end();
|
|
6333
|
+
}
|
|
6334
|
+
});
|
|
6335
|
+
};
|
|
6336
|
+
}
|
|
6225
6337
|
function buildSingleProviderCaller(config) {
|
|
6226
6338
|
if (config.provider === "anthropic") {
|
|
6227
6339
|
const call2 = buildAnthropicCaller(config);
|
|
@@ -6264,6 +6376,46 @@ function buildSingleProviderCaller(config) {
|
|
|
6264
6376
|
}
|
|
6265
6377
|
};
|
|
6266
6378
|
}
|
|
6379
|
+
if (config.provider === "openai") {
|
|
6380
|
+
const call2 = buildOpenAICaller(config);
|
|
6381
|
+
return async function(payload, branch) {
|
|
6382
|
+
const started = Date.now();
|
|
6383
|
+
let providerResult;
|
|
6384
|
+
try {
|
|
6385
|
+
providerResult = await call2(branch.systemPrompt, payload, branch.jsonSchema);
|
|
6386
|
+
} catch (callErr) {
|
|
6387
|
+
const providerLatencyMs2 = Date.now() - started;
|
|
6388
|
+
return { ok: false, error: callErr instanceof Error ? callErr : new Error(String(callErr)), raw: null, inputTokens: null, outputTokens: null, providerLatencyMs: providerLatencyMs2 };
|
|
6389
|
+
}
|
|
6390
|
+
const providerLatencyMs = Date.now() - started;
|
|
6391
|
+
try {
|
|
6392
|
+
const verdict = branch.parse(providerResult.raw);
|
|
6393
|
+
return { ok: true, verdict, raw: providerResult.raw, inputTokens: providerResult.inputTokens ?? null, outputTokens: providerResult.outputTokens ?? null, providerLatencyMs };
|
|
6394
|
+
} catch (parseErr) {
|
|
6395
|
+
return { ok: false, error: parseErr instanceof Error ? parseErr : new Error(String(parseErr)), raw: providerResult.raw, inputTokens: providerResult.inputTokens ?? null, outputTokens: providerResult.outputTokens ?? null, providerLatencyMs };
|
|
6396
|
+
}
|
|
6397
|
+
};
|
|
6398
|
+
}
|
|
6399
|
+
if (config.provider === "agent-cli") {
|
|
6400
|
+
const call2 = buildAgentCliCaller(config);
|
|
6401
|
+
return async function(payload, branch) {
|
|
6402
|
+
const started = Date.now();
|
|
6403
|
+
let raw;
|
|
6404
|
+
try {
|
|
6405
|
+
({ raw } = await call2(branch.systemPrompt, payload));
|
|
6406
|
+
} catch (callErr) {
|
|
6407
|
+
const providerLatencyMs2 = Date.now() - started;
|
|
6408
|
+
return { ok: false, error: callErr instanceof Error ? callErr : new Error(String(callErr)), raw: null, inputTokens: null, outputTokens: null, providerLatencyMs: providerLatencyMs2 };
|
|
6409
|
+
}
|
|
6410
|
+
const providerLatencyMs = Date.now() - started;
|
|
6411
|
+
try {
|
|
6412
|
+
const verdict = branch.parse(raw);
|
|
6413
|
+
return { ok: true, verdict, raw, inputTokens: null, outputTokens: null, providerLatencyMs };
|
|
6414
|
+
} catch (parseErr) {
|
|
6415
|
+
return { ok: false, error: parseErr instanceof Error ? parseErr : new Error(String(parseErr)), raw, inputTokens: null, outputTokens: null, providerLatencyMs };
|
|
6416
|
+
}
|
|
6417
|
+
};
|
|
6418
|
+
}
|
|
6267
6419
|
const call = buildOllamaCaller(config);
|
|
6268
6420
|
return async function(payload, branch) {
|
|
6269
6421
|
const started = Date.now();
|
|
@@ -6710,8 +6862,8 @@ function writeOwnPidToBrokerDaemon(db, input) {
|
|
|
6710
6862
|
}
|
|
6711
6863
|
|
|
6712
6864
|
// src/runtime/evaluator-config.ts
|
|
6713
|
-
import { readFileSync as readFileSync2, statSync as
|
|
6714
|
-
import { join as
|
|
6865
|
+
import { readFileSync as readFileSync2, statSync as statSync4 } from "node:fs";
|
|
6866
|
+
import { join as join7 } from "node:path";
|
|
6715
6867
|
|
|
6716
6868
|
// src/runtime/state-root.ts
|
|
6717
6869
|
import os from "node:os";
|
|
@@ -6745,7 +6897,7 @@ function warnIfLoosePerms(path2) {
|
|
|
6745
6897
|
if (process.platform === "win32") return;
|
|
6746
6898
|
let mode;
|
|
6747
6899
|
try {
|
|
6748
|
-
mode =
|
|
6900
|
+
mode = statSync4(path2).mode;
|
|
6749
6901
|
} catch {
|
|
6750
6902
|
return;
|
|
6751
6903
|
}
|
|
@@ -6773,31 +6925,46 @@ function loadEvaluatorConfig() {
|
|
|
6773
6925
|
const root = getStateRoot();
|
|
6774
6926
|
let dotenv = {};
|
|
6775
6927
|
try {
|
|
6776
|
-
dotenv = parseDotEnv(readFileSync2(
|
|
6928
|
+
dotenv = parseDotEnv(readFileSync2(join7(root, ".env"), "utf8"));
|
|
6777
6929
|
} catch (err) {
|
|
6778
6930
|
if (err.code !== "ENOENT") throw err;
|
|
6779
6931
|
}
|
|
6780
6932
|
const envGet = (k) => process.env[k] ?? dotenv[k];
|
|
6781
|
-
const authPath =
|
|
6933
|
+
const authPath = join7(root, "auth.json");
|
|
6782
6934
|
warnIfLoosePerms(authPath);
|
|
6783
6935
|
const auth = readJsonFile(authPath, "auth.json");
|
|
6784
|
-
const config = readJsonFile(
|
|
6936
|
+
const config = readJsonFile(join7(root, "config.json"), "config.json");
|
|
6785
6937
|
const evalCfg = config?.evaluator ?? {};
|
|
6786
6938
|
const evalOllama = evalCfg.ollama ?? {};
|
|
6787
|
-
const provider =
|
|
6939
|
+
const provider = envGet("AI_WHISPER_EVALUATOR_PROVIDER") ?? evalCfg.provider;
|
|
6940
|
+
const resolvedProvider = provider === "ollama" || provider === "openai" || provider === "agent-cli" ? provider : "anthropic";
|
|
6788
6941
|
const rawFallback = envGet("AI_WHISPER_EVALUATOR_FALLBACK") ?? evalCfg.fallback;
|
|
6789
|
-
const fallback = rawFallback === "anthropic" || rawFallback === "ollama" ? rawFallback : null;
|
|
6942
|
+
const fallback = rawFallback === "anthropic" || rawFallback === "ollama" || rawFallback === "openai" || rawFallback === "agent-cli" ? rawFallback : null;
|
|
6790
6943
|
const apiKey = envGet("ANTHROPIC_API_KEY") ?? auth?.ANTHROPIC_API_KEY ?? null;
|
|
6944
|
+
const evalOpenai = evalCfg.openai ?? {};
|
|
6945
|
+
const openaiKey = envGet("OPENAI_API_KEY") ?? auth?.OPENAI_API_KEY ?? null;
|
|
6946
|
+
const evalAgentCli = evalCfg.agentCli ?? {};
|
|
6947
|
+
const rawAgent = envGet("AI_WHISPER_EVALUATOR_AGENT_CLI_AGENT") ?? evalAgentCli.agent;
|
|
6948
|
+
const agent = rawAgent === "claude" || rawAgent === "codex" || rawAgent === "agy" ? rawAgent : null;
|
|
6791
6949
|
return {
|
|
6792
|
-
provider,
|
|
6950
|
+
provider: resolvedProvider,
|
|
6793
6951
|
fallback,
|
|
6794
|
-
anthropic: {
|
|
6795
|
-
apiKey: apiKey && apiKey.length > 0 ? apiKey : null,
|
|
6796
|
-
model: evalCfg.anthropicModel ?? null
|
|
6797
|
-
},
|
|
6952
|
+
anthropic: { apiKey: apiKey && apiKey.length > 0 ? apiKey : null, model: evalCfg.anthropicModel ?? null },
|
|
6798
6953
|
ollama: {
|
|
6799
6954
|
host: envGet("AI_WHISPER_EVALUATOR_OLLAMA_HOST") ?? evalOllama.host ?? null,
|
|
6800
6955
|
model: envGet("AI_WHISPER_EVALUATOR_OLLAMA_MODEL") ?? evalOllama.model ?? null
|
|
6956
|
+
},
|
|
6957
|
+
openai: {
|
|
6958
|
+
apiKey: openaiKey && openaiKey.length > 0 ? openaiKey : null,
|
|
6959
|
+
model: envGet("AI_WHISPER_EVALUATOR_OPENAI_MODEL") ?? evalOpenai.model ?? null,
|
|
6960
|
+
baseURL: envGet("AI_WHISPER_EVALUATOR_OPENAI_BASE_URL") ?? evalOpenai.baseURL ?? null
|
|
6961
|
+
},
|
|
6962
|
+
agentCli: {
|
|
6963
|
+
agent,
|
|
6964
|
+
executable: evalAgentCli.executable ?? null,
|
|
6965
|
+
execArgs: Array.isArray(evalAgentCli.execArgs) ? evalAgentCli.execArgs : null,
|
|
6966
|
+
promptVia: evalAgentCli.promptVia === "arg" || evalAgentCli.promptVia === "stdin" ? evalAgentCli.promptVia : null,
|
|
6967
|
+
model: evalAgentCli.model ?? null
|
|
6801
6968
|
}
|
|
6802
6969
|
};
|
|
6803
6970
|
}
|
|
@@ -6807,6 +6974,21 @@ function computeEvaluatorStatus(cfg, ctx) {
|
|
|
6807
6974
|
if (cfg.provider === "anthropic" && cfg.anthropic.apiKey === null) {
|
|
6808
6975
|
return "missing_anthropic_key";
|
|
6809
6976
|
}
|
|
6977
|
+
if (cfg.provider === "openai") {
|
|
6978
|
+
if (cfg.openai.apiKey === null) return "missing_openai_key";
|
|
6979
|
+
if (cfg.openai.model === null) return "invalid_config";
|
|
6980
|
+
}
|
|
6981
|
+
if (cfg.provider === "agent-cli") {
|
|
6982
|
+
if (cfg.agentCli.agent === null) return "invalid_config";
|
|
6983
|
+
const { executable } = resolveAgentCliInvocation({
|
|
6984
|
+
agent: cfg.agentCli.agent,
|
|
6985
|
+
executable: cfg.agentCli.executable,
|
|
6986
|
+
execArgs: cfg.agentCli.execArgs,
|
|
6987
|
+
promptVia: cfg.agentCli.promptVia
|
|
6988
|
+
});
|
|
6989
|
+
const exists = (ctx.executableExists ?? isExecutableOnPath)(executable);
|
|
6990
|
+
if (!exists) return "agent_cli_unavailable";
|
|
6991
|
+
}
|
|
6810
6992
|
return "ready";
|
|
6811
6993
|
}
|
|
6812
6994
|
|
|
@@ -6815,7 +6997,9 @@ var NOT_CONFIGURED = {
|
|
|
6815
6997
|
provider: "anthropic",
|
|
6816
6998
|
fallback: null,
|
|
6817
6999
|
anthropic: { apiKey: null, model: null },
|
|
6818
|
-
ollama: { host: null, model: null }
|
|
7000
|
+
ollama: { host: null, model: null },
|
|
7001
|
+
openai: { apiKey: null, model: null, baseURL: null },
|
|
7002
|
+
agentCli: { agent: null, executable: null, execArgs: null, promptVia: null, model: null }
|
|
6819
7003
|
};
|
|
6820
7004
|
function recordEvaluatorStatus(db, input) {
|
|
6821
7005
|
const status = computeEvaluatorStatus(input.resolved ?? NOT_CONFIGURED, {
|
|
@@ -6828,7 +7012,7 @@ function recordEvaluatorStatus(db, input) {
|
|
|
6828
7012
|
|
|
6829
7013
|
// src/bin/broker-daemon.ts
|
|
6830
7014
|
import { execFile as execFile2 } from "node:child_process";
|
|
6831
|
-
import { join as
|
|
7015
|
+
import { join as join8 } from "node:path";
|
|
6832
7016
|
|
|
6833
7017
|
// src/runtime/cli-package-info.ts
|
|
6834
7018
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
@@ -6878,7 +7062,7 @@ if (collabId) {
|
|
|
6878
7062
|
});
|
|
6879
7063
|
workflowEventBridge.start();
|
|
6880
7064
|
eventSocket = await createEventSocketServer({
|
|
6881
|
-
socketPath:
|
|
7065
|
+
socketPath: join8(getStateSocketsDir(), `events-${collabId}.sock`),
|
|
6882
7066
|
events: [broker.events, externalEvents],
|
|
6883
7067
|
engineVersion: resolveCliVersion()
|
|
6884
7068
|
});
|
|
@@ -6905,10 +7089,30 @@ function providerConfigFrom(kind) {
|
|
|
6905
7089
|
if (!resolved || resolved.anthropic.apiKey === null) return null;
|
|
6906
7090
|
return { provider: "anthropic", apiKey: resolved.anthropic.apiKey };
|
|
6907
7091
|
}
|
|
7092
|
+
if (kind === "openai") {
|
|
7093
|
+
if (!resolved || resolved.openai.apiKey === null || resolved.openai.model === null) return null;
|
|
7094
|
+
return {
|
|
7095
|
+
provider: "openai",
|
|
7096
|
+
apiKey: resolved.openai.apiKey,
|
|
7097
|
+
model: resolved.openai.model,
|
|
7098
|
+
...resolved.openai.baseURL ? { baseURL: resolved.openai.baseURL } : {}
|
|
7099
|
+
};
|
|
7100
|
+
}
|
|
7101
|
+
if (kind === "ollama") {
|
|
7102
|
+
return {
|
|
7103
|
+
provider: "ollama",
|
|
7104
|
+
...resolved?.ollama.host ? { host: resolved.ollama.host } : {},
|
|
7105
|
+
...resolved?.ollama.model ? { model: resolved.ollama.model } : {}
|
|
7106
|
+
};
|
|
7107
|
+
}
|
|
7108
|
+
if (!resolved || resolved.agentCli.agent === null) return null;
|
|
6908
7109
|
return {
|
|
6909
|
-
provider: "
|
|
6910
|
-
|
|
6911
|
-
...resolved
|
|
7110
|
+
provider: "agent-cli",
|
|
7111
|
+
agent: resolved.agentCli.agent,
|
|
7112
|
+
...resolved.agentCli.executable ? { executable: resolved.agentCli.executable } : {},
|
|
7113
|
+
...resolved.agentCli.execArgs ? { execArgs: resolved.agentCli.execArgs } : {},
|
|
7114
|
+
...resolved.agentCli.promptVia ? { promptVia: resolved.agentCli.promptVia } : {},
|
|
7115
|
+
...resolved.agentCli.model ? { model: resolved.agentCli.model } : {}
|
|
6912
7116
|
};
|
|
6913
7117
|
}
|
|
6914
7118
|
var collab = broker.control.getCollab(collabId);
|