@aiden-ade/sandbox-agent 0.1.14 → 0.1.16
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/dist/aiden-system-prompt.d.ts +6 -0
- package/dist/aiden-system-prompt.d.ts.map +1 -0
- package/dist/aiden-system-prompt.js +6 -0
- package/dist/aiden-system-prompt.js.map +1 -0
- package/dist/index.cjs +1594 -910
- package/dist/updater.d.ts.map +1 -1
- package/dist/updater.js +0 -4
- package/dist/updater.js.map +1 -1
- package/package.json +11 -12
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
36
36
|
"use strict";
|
|
37
37
|
var fs = require("fs");
|
|
38
38
|
var Url = require("url");
|
|
39
|
-
var
|
|
39
|
+
var spawn3 = require("child_process").spawn;
|
|
40
40
|
module2.exports = XMLHttpRequest3;
|
|
41
41
|
XMLHttpRequest3.XMLHttpRequest = XMLHttpRequest3;
|
|
42
42
|
function XMLHttpRequest3(opts) {
|
|
@@ -332,7 +332,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
332
332
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
333
333
|
fs.writeFileSync(syncFile, "", "utf8");
|
|
334
334
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
335
|
-
var syncProc =
|
|
335
|
+
var syncProc = spawn3(process.argv[0], ["-e", execString]);
|
|
336
336
|
var statusText;
|
|
337
337
|
while (fs.existsSync(syncFile)) {
|
|
338
338
|
}
|
|
@@ -645,8 +645,8 @@ var require_common = __commonJS({
|
|
|
645
645
|
}
|
|
646
646
|
return debug12;
|
|
647
647
|
}
|
|
648
|
-
function extend(namespace,
|
|
649
|
-
const newDebug = createDebug(this.namespace + (typeof
|
|
648
|
+
function extend(namespace, delimiter2) {
|
|
649
|
+
const newDebug = createDebug(this.namespace + (typeof delimiter2 === "undefined" ? ":" : delimiter2) + namespace);
|
|
650
650
|
newDebug.log = this.log;
|
|
651
651
|
return newDebug;
|
|
652
652
|
}
|
|
@@ -4812,11 +4812,6 @@ var require_websocket_server = __commonJS({
|
|
|
4812
4812
|
|
|
4813
4813
|
// ../agent-core/dist/index.js
|
|
4814
4814
|
var import_child_process = require("child_process");
|
|
4815
|
-
var import_crypto = require("crypto");
|
|
4816
|
-
var import_fs = require("fs");
|
|
4817
|
-
var import_os = require("os");
|
|
4818
|
-
var import_path = require("path");
|
|
4819
|
-
var import_readline = require("readline");
|
|
4820
4815
|
|
|
4821
4816
|
// ../shared/dist/agent/agent-definitions.js
|
|
4822
4817
|
var COORDINATOR_PROMPT = `## You are the Coordinator
|
|
@@ -4940,7 +4935,7 @@ var GENERAL_PROMPT = `## You are the General Agent
|
|
|
4940
4935
|
|
|
4941
4936
|
You are a flexible general-purpose agent (an Aiden agent profile) for work that does not need a specialist profile. Wear whatever hat the moment demands \u2014 product thinker, planner, programmer, debugger, tester. Read the room and pick the hat.
|
|
4942
4937
|
|
|
4943
|
-
|
|
4938
|
+
Your name is Aiden, General Agent.
|
|
4944
4939
|
|
|
4945
4940
|
Clarify the goal only when required, inspect the relevant context first, then complete the requested work directly. Keep changes focused, avoid unrelated refactors, and validate the result before reporting back.
|
|
4946
4941
|
|
|
@@ -5175,8 +5170,8 @@ var PLAN_MODE_PROMPT = [
|
|
|
5175
5170
|
"Include dependencies, ordering, risks, key files/functions, and decision points where relevant.",
|
|
5176
5171
|
"When the plan is ready, you MUST persist it by calling the create_plan MCP tool.",
|
|
5177
5172
|
"Pass title, content, summary, taskId, and conversationId. Include teamId too when available.",
|
|
5178
|
-
"If create_plan fails, retry it. Do not
|
|
5179
|
-
"After create_plan succeeds,
|
|
5173
|
+
"If create_plan fails, retry it. Do not finish plan mode until the plan is persisted successfully.",
|
|
5174
|
+
"After create_plan succeeds, present the plan title and artifact link to the user for approval."
|
|
5180
5175
|
].join("\n");
|
|
5181
5176
|
var TEST_MODE_PROMPT = [
|
|
5182
5177
|
"You are in test mode. Verify the implemented feature carefully.",
|
|
@@ -5206,12 +5201,25 @@ function getAidenAgentPrompt(agentId) {
|
|
|
5206
5201
|
// ../shared/dist/agent/session-result.js
|
|
5207
5202
|
var SESSION_RESUME_FAILED_MESSAGE = "Session resume failed. The previous session may have expired or is no longer available. Please start a new session.";
|
|
5208
5203
|
|
|
5204
|
+
// ../agent-core/dist/index.js
|
|
5205
|
+
var import_fs = require("fs");
|
|
5206
|
+
var import_os = require("os");
|
|
5207
|
+
var import_path = require("path");
|
|
5208
|
+
var import_crypto = require("crypto");
|
|
5209
|
+
var import_fs2 = require("fs");
|
|
5210
|
+
var import_fs3 = require("fs");
|
|
5211
|
+
var import_os2 = require("os");
|
|
5212
|
+
var import_path2 = require("path");
|
|
5213
|
+
var import_readline = require("readline");
|
|
5214
|
+
|
|
5209
5215
|
// ../shared/dist/constants/agent.js
|
|
5210
5216
|
var INTERACTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
5211
5217
|
"askuserquestion",
|
|
5212
5218
|
"exitplanmode",
|
|
5213
5219
|
"enterplanmode"
|
|
5214
5220
|
]);
|
|
5221
|
+
var AIDEN_AGENT_WORKOS_USER_ID_PREFIX = "aiden-agent:";
|
|
5222
|
+
var AIDEN_AGENT_WORKOS_USER_ID_LIKE_PATTERN = `${AIDEN_AGENT_WORKOS_USER_ID_PREFIX}%`;
|
|
5215
5223
|
|
|
5216
5224
|
// ../shared/dist/constants/tasks.js
|
|
5217
5225
|
var STATUS_OPTIONS = [
|
|
@@ -5232,17 +5240,9 @@ var DEFAULT_TEAM_STATUSES = STATUS_OPTIONS.map((o) => ({
|
|
|
5232
5240
|
}));
|
|
5233
5241
|
|
|
5234
5242
|
// ../agent-core/dist/index.js
|
|
5235
|
-
var
|
|
5236
|
-
var
|
|
5237
|
-
var
|
|
5238
|
-
function formatCliSpawnError(command, error) {
|
|
5239
|
-
if (error.code === "ENOENT") {
|
|
5240
|
-
return new Error(
|
|
5241
|
-
`CLI command not found: ${command}. Install it or configure this provider with the full executable path.`
|
|
5242
|
-
);
|
|
5243
|
-
}
|
|
5244
|
-
return error;
|
|
5245
|
-
}
|
|
5243
|
+
var import_fs4 = require("fs");
|
|
5244
|
+
var import_os3 = require("os");
|
|
5245
|
+
var import_path3 = require("path");
|
|
5246
5246
|
function isLikelyProviderAuthError(stderr) {
|
|
5247
5247
|
const lower = stderr.toLowerCase();
|
|
5248
5248
|
return lower.includes("api key") || lower.includes("api_key") || lower.includes("invalid api key") || lower.includes("api key invalid") || lower.includes("unauthorized") || lower.includes("authentication failed") || lower.includes("authentication error") || lower.includes("provider settings") || lower.includes("invalid token") || lower.includes("token expired");
|
|
@@ -5287,23 +5287,6 @@ function isTransientError(errorMessage) {
|
|
|
5287
5287
|
const lower = errorMessage.toLowerCase();
|
|
5288
5288
|
return lower.includes("rate limit") || lower.includes("overloaded") || lower.includes("503") || lower.includes("429") || lower.includes("service unavailable") || lower.includes("etimedout") || lower.includes("econnreset") || lower.includes("econnrefused") || lower.includes("epipe");
|
|
5289
5289
|
}
|
|
5290
|
-
function buildPromptWithSystem(config, promptText) {
|
|
5291
|
-
const parts2 = [
|
|
5292
|
-
config.systemPrompt?.trim(),
|
|
5293
|
-
config.systemPromptAppend?.trim(),
|
|
5294
|
-
promptText.trim()
|
|
5295
|
-
].filter((value2) => Boolean(value2 && value2.length > 0));
|
|
5296
|
-
return parts2.join("\n\n");
|
|
5297
|
-
}
|
|
5298
|
-
function buildPlanModePrefix(promptText) {
|
|
5299
|
-
return [
|
|
5300
|
-
"You are in plan-only mode.",
|
|
5301
|
-
"Do not make code changes or execute write operations.",
|
|
5302
|
-
"Focus on analysis, planning, and explaining the next steps.",
|
|
5303
|
-
"",
|
|
5304
|
-
promptText
|
|
5305
|
-
].join("\n");
|
|
5306
|
-
}
|
|
5307
5290
|
function planCliSpawn(command, args, env = process.env) {
|
|
5308
5291
|
const isWindows = process.platform === "win32";
|
|
5309
5292
|
const comSpec = env.ComSpec ?? process.env.ComSpec ?? "cmd.exe";
|
|
@@ -5339,48 +5322,334 @@ function spawnCli(command, args, context) {
|
|
|
5339
5322
|
detached: process.platform !== "win32"
|
|
5340
5323
|
});
|
|
5341
5324
|
}
|
|
5342
|
-
function
|
|
5343
|
-
return
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
5354
|
-
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5325
|
+
function shouldUseReadOnlyRuntimePermissions(config) {
|
|
5326
|
+
if (isReadOnlyAgent(config.agentId)) return true;
|
|
5327
|
+
switch (config.mode) {
|
|
5328
|
+
case "plan":
|
|
5329
|
+
case "ask":
|
|
5330
|
+
case "review":
|
|
5331
|
+
return true;
|
|
5332
|
+
default:
|
|
5333
|
+
return false;
|
|
5334
|
+
}
|
|
5335
|
+
}
|
|
5336
|
+
function getClaudePermissionMode(config) {
|
|
5337
|
+
switch (config.mode) {
|
|
5338
|
+
case "plan":
|
|
5339
|
+
case "ask":
|
|
5340
|
+
case "review":
|
|
5341
|
+
return "plan";
|
|
5342
|
+
default:
|
|
5343
|
+
return "bypassPermissions";
|
|
5344
|
+
}
|
|
5345
|
+
}
|
|
5346
|
+
function getClaudeDisallowedTools(config) {
|
|
5347
|
+
return isReadOnlyAgent(config.agentId) ? ["Edit", "Write", "MultiEdit", "NotebookEdit"] : [];
|
|
5348
|
+
}
|
|
5349
|
+
function parseJsonObject(value2) {
|
|
5350
|
+
try {
|
|
5351
|
+
const parsed = JSON.parse(value2);
|
|
5352
|
+
return typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
5353
|
+
} catch {
|
|
5354
|
+
return null;
|
|
5355
|
+
}
|
|
5356
|
+
}
|
|
5357
|
+
function parseMaybeJson(value2) {
|
|
5358
|
+
if (typeof value2 !== "string") return value2;
|
|
5359
|
+
return parseJsonObject(value2) ?? value2;
|
|
5360
|
+
}
|
|
5361
|
+
function normalizeCodexMcpToolResult(output) {
|
|
5362
|
+
if (typeof output !== "string") return JSON.stringify(output ?? {}, null, 2);
|
|
5363
|
+
const outputMarker = "\nOutput:\n";
|
|
5364
|
+
const raw = output.includes(outputMarker) ? output.slice(output.indexOf(outputMarker) + outputMarker.length).trim() : output.trim();
|
|
5365
|
+
let parsed;
|
|
5366
|
+
try {
|
|
5367
|
+
parsed = JSON.parse(raw);
|
|
5368
|
+
} catch {
|
|
5369
|
+
return raw;
|
|
5370
|
+
}
|
|
5371
|
+
if (Array.isArray(parsed)) {
|
|
5372
|
+
const textBlocks = parsed.filter(
|
|
5373
|
+
(entry) => typeof entry === "object" && entry !== null && "text" in entry
|
|
5374
|
+
).map((entry) => typeof entry.text === "string" ? entry.text : "").filter(Boolean);
|
|
5375
|
+
if (textBlocks.length > 0) return textBlocks.join("\n");
|
|
5376
|
+
}
|
|
5377
|
+
return raw;
|
|
5378
|
+
}
|
|
5379
|
+
function findCodexSessionLog(runtimeSessionId, codexHome) {
|
|
5380
|
+
if (!runtimeSessionId || !(0, import_fs.existsSync)(codexHome)) return null;
|
|
5381
|
+
const root = (0, import_path.join)(codexHome, "sessions");
|
|
5382
|
+
if (!(0, import_fs.existsSync)(root)) return null;
|
|
5383
|
+
const matches = [];
|
|
5384
|
+
const stack = [root];
|
|
5385
|
+
while (stack.length > 0) {
|
|
5386
|
+
const dir = stack.pop();
|
|
5387
|
+
let entries;
|
|
5388
|
+
try {
|
|
5389
|
+
entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
|
|
5390
|
+
} catch {
|
|
5391
|
+
continue;
|
|
5392
|
+
}
|
|
5393
|
+
for (const entry of entries) {
|
|
5394
|
+
const path = (0, import_path.join)(dir, entry.name);
|
|
5395
|
+
if (entry.isDirectory()) {
|
|
5396
|
+
stack.push(path);
|
|
5397
|
+
} else if (entry.isFile() && entry.name.includes(runtimeSessionId) && entry.name.endsWith(".jsonl")) {
|
|
5398
|
+
try {
|
|
5399
|
+
matches.push({ path, mtimeMs: (0, import_fs.statSync)(path).mtimeMs });
|
|
5400
|
+
} catch {
|
|
5401
|
+
matches.push({ path, mtimeMs: 0 });
|
|
5369
5402
|
}
|
|
5370
|
-
});
|
|
5371
|
-
if (options.stdinWriter) {
|
|
5372
|
-
options.stdinWriter(child.stdin, context);
|
|
5373
|
-
} else if (options.promptViaStdin) {
|
|
5374
|
-
child.stdin.write(prompt);
|
|
5375
|
-
child.stdin.end();
|
|
5376
|
-
} else {
|
|
5377
|
-
child.stdin.end();
|
|
5378
5403
|
}
|
|
5379
|
-
|
|
5380
|
-
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
5407
|
+
return matches[0]?.path ?? null;
|
|
5408
|
+
}
|
|
5409
|
+
async function replayCodexMcpToolEventsFromSessionLog(context, state) {
|
|
5410
|
+
const runtimeSessionId = state.runtimeSessionId;
|
|
5411
|
+
if (!runtimeSessionId) return;
|
|
5412
|
+
const codexHome = context.env.CODEX_HOME || process.env.CODEX_HOME || (0, import_path.join)((0, import_os.homedir)(), ".codex");
|
|
5413
|
+
const logPath = findCodexSessionLog(runtimeSessionId, codexHome);
|
|
5414
|
+
if (!logPath) return;
|
|
5415
|
+
const emittedToolIds = /* @__PURE__ */ new Set();
|
|
5416
|
+
const lines = (0, import_fs.readFileSync)(logPath, "utf8").split(/\r?\n/).filter(Boolean);
|
|
5417
|
+
for (const line of lines) {
|
|
5418
|
+
const entry = parseJsonObject(line);
|
|
5419
|
+
const payload = entry && typeof entry.payload === "object" && entry.payload !== null ? entry.payload : null;
|
|
5420
|
+
if (!payload) continue;
|
|
5421
|
+
if (payload.type === "function_call") {
|
|
5422
|
+
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5423
|
+
const name = typeof payload.name === "string" ? payload.name : "";
|
|
5424
|
+
const namespace = typeof payload.namespace === "string" ? payload.namespace : "";
|
|
5425
|
+
if (!callId || !name || !namespace.startsWith("mcp__")) continue;
|
|
5426
|
+
const toolName = `${namespace}${name}`;
|
|
5427
|
+
context.presenter.onToolUse(toolName, parseMaybeJson(payload.arguments), callId);
|
|
5428
|
+
emittedToolIds.add(callId);
|
|
5429
|
+
continue;
|
|
5430
|
+
}
|
|
5431
|
+
if (payload.type === "tool_search_call") {
|
|
5432
|
+
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5433
|
+
if (!callId) continue;
|
|
5434
|
+
context.presenter.onToolUse("tool_search", payload.arguments ?? {}, callId);
|
|
5435
|
+
emittedToolIds.add(callId);
|
|
5436
|
+
}
|
|
5437
|
+
}
|
|
5438
|
+
if (emittedToolIds.size === 0) return;
|
|
5439
|
+
for (const line of lines) {
|
|
5440
|
+
const entry = parseJsonObject(line);
|
|
5441
|
+
const payload = entry && typeof entry.payload === "object" && entry.payload !== null ? entry.payload : null;
|
|
5442
|
+
if (!payload || payload.type !== "function_call_output" && payload.type !== "tool_search_output") continue;
|
|
5443
|
+
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5444
|
+
if (!emittedToolIds.has(callId)) continue;
|
|
5445
|
+
try {
|
|
5446
|
+
const result = payload.type === "tool_search_output" ? JSON.stringify(payload.tools ?? [], null, 2) : normalizeCodexMcpToolResult(payload.output);
|
|
5447
|
+
context.presenter.onToolResult?.(callId, result);
|
|
5448
|
+
} catch {
|
|
5449
|
+
context.presenter.onToolResult?.(callId, String(payload.output ?? ""));
|
|
5450
|
+
}
|
|
5451
|
+
}
|
|
5452
|
+
}
|
|
5453
|
+
function kimiSessionId(cwd) {
|
|
5454
|
+
let resolved = cwd;
|
|
5455
|
+
try {
|
|
5456
|
+
resolved = (0, import_fs2.realpathSync)(cwd);
|
|
5457
|
+
} catch {
|
|
5458
|
+
}
|
|
5459
|
+
return (0, import_crypto.createHash)("md5").update(resolved).digest("hex");
|
|
5460
|
+
}
|
|
5461
|
+
function parseKimiStructuredLine(line, context, state) {
|
|
5462
|
+
const presenter = context.presenter;
|
|
5463
|
+
if (!state.runtimeSessionId) {
|
|
5464
|
+
state.runtimeSessionId = kimiSessionId(context.cwd);
|
|
5465
|
+
}
|
|
5466
|
+
let parsed;
|
|
5467
|
+
try {
|
|
5468
|
+
parsed = JSON.parse(line);
|
|
5469
|
+
} catch {
|
|
5470
|
+
const text = line.trim();
|
|
5471
|
+
if (text.length > 0) {
|
|
5472
|
+
if (text.toLowerCase().includes("llm not set")) state.error = text;
|
|
5473
|
+
void presenter.onLog(`[kimi_cli] ${line}`);
|
|
5474
|
+
}
|
|
5475
|
+
return;
|
|
5476
|
+
}
|
|
5477
|
+
const role = typeof parsed.role === "string" ? parsed.role : "";
|
|
5478
|
+
const contentBlocks = Array.isArray(parsed.content) ? parsed.content : [];
|
|
5479
|
+
const toolCalls = Array.isArray(parsed.tool_calls) ? parsed.tool_calls : [];
|
|
5480
|
+
if (role === "assistant") {
|
|
5481
|
+
const thinking = contentBlocks.filter((b) => b.type === "think" && typeof b.think === "string").map((b) => b.think).join("");
|
|
5482
|
+
if (thinking) {
|
|
5483
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5484
|
+
void presenter.onThinking(thinking);
|
|
5485
|
+
}
|
|
5486
|
+
if (toolCalls.length > 0) {
|
|
5487
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5488
|
+
for (const tc of toolCalls) {
|
|
5489
|
+
const fn = typeof tc.function === "object" && tc.function !== null ? tc.function : {};
|
|
5490
|
+
const toolId = typeof tc.id === "string" ? tc.id : `kimi-tool-${Date.now()}`;
|
|
5491
|
+
const toolName = typeof fn.name === "string" ? fn.name : "Tool";
|
|
5492
|
+
let args = {};
|
|
5493
|
+
if (typeof fn.arguments === "string") {
|
|
5494
|
+
try {
|
|
5495
|
+
args = JSON.parse(fn.arguments);
|
|
5496
|
+
} catch {
|
|
5497
|
+
}
|
|
5498
|
+
} else if (typeof fn.arguments === "object" && fn.arguments !== null) {
|
|
5499
|
+
args = fn.arguments;
|
|
5500
|
+
}
|
|
5501
|
+
void presenter.onToolUse(toolName, args, toolId);
|
|
5381
5502
|
}
|
|
5382
|
-
|
|
5383
|
-
const
|
|
5503
|
+
} else {
|
|
5504
|
+
const text = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
5505
|
+
if (text) {
|
|
5506
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5507
|
+
state.summary += text;
|
|
5508
|
+
void presenter.onAssistantText(text);
|
|
5509
|
+
}
|
|
5510
|
+
}
|
|
5511
|
+
return;
|
|
5512
|
+
}
|
|
5513
|
+
if (role === "tool") {
|
|
5514
|
+
const toolCallId = typeof parsed.tool_call_id === "string" ? parsed.tool_call_id : `kimi-tool-${Date.now()}`;
|
|
5515
|
+
const allText = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text);
|
|
5516
|
+
const userText = allText.filter((t) => !t.startsWith("<system>")).join("\n");
|
|
5517
|
+
void presenter.onToolResult?.(toolCallId, userText || allText.join("\n"));
|
|
5518
|
+
return;
|
|
5519
|
+
}
|
|
5520
|
+
}
|
|
5521
|
+
function buildPromptWithSystem(config, promptText) {
|
|
5522
|
+
const parts2 = [
|
|
5523
|
+
config.systemPrompt?.trim(),
|
|
5524
|
+
config.systemPromptAppend?.trim(),
|
|
5525
|
+
promptText.trim()
|
|
5526
|
+
].filter((value2) => Boolean(value2 && value2.length > 0));
|
|
5527
|
+
return parts2.join("\n\n");
|
|
5528
|
+
}
|
|
5529
|
+
function buildPlanModePrefix(promptText) {
|
|
5530
|
+
return [
|
|
5531
|
+
"You are in plan-only mode.",
|
|
5532
|
+
"Do not make code changes or execute write operations.",
|
|
5533
|
+
"Focus on analysis, planning, and explaining the next steps.",
|
|
5534
|
+
"",
|
|
5535
|
+
promptText
|
|
5536
|
+
].join("\n");
|
|
5537
|
+
}
|
|
5538
|
+
function imageFileExtension(mimeType) {
|
|
5539
|
+
const subtype = mimeType.split("/")[1]?.split(/[+;]/)[0]?.trim();
|
|
5540
|
+
if (!subtype) return "png";
|
|
5541
|
+
return subtype === "jpeg" ? "jpg" : subtype;
|
|
5542
|
+
}
|
|
5543
|
+
function createImageTempDirectory(cwd) {
|
|
5544
|
+
const candidates = [
|
|
5545
|
+
cwd ? (0, import_path2.join)(cwd, ".aiden-images-") : null,
|
|
5546
|
+
(0, import_path2.join)((0, import_os2.tmpdir)(), "aiden-images-")
|
|
5547
|
+
].filter((value2) => Boolean(value2));
|
|
5548
|
+
for (const candidate of candidates) {
|
|
5549
|
+
try {
|
|
5550
|
+
return (0, import_fs3.mkdtempSync)(candidate);
|
|
5551
|
+
} catch {
|
|
5552
|
+
}
|
|
5553
|
+
}
|
|
5554
|
+
return (0, import_fs3.mkdtempSync)((0, import_path2.join)((0, import_os2.tmpdir)(), "aiden-images-"));
|
|
5555
|
+
}
|
|
5556
|
+
function writeImagesToTempFiles(images, cwd) {
|
|
5557
|
+
if (!images?.length) return { paths: [], files: [], cleanup: () => {
|
|
5558
|
+
} };
|
|
5559
|
+
const directory = createImageTempDirectory(cwd);
|
|
5560
|
+
const paths = [];
|
|
5561
|
+
const files = [];
|
|
5562
|
+
for (const img of images) {
|
|
5563
|
+
const ext = imageFileExtension(img.mimeType);
|
|
5564
|
+
const filePath = (0, import_path2.join)(directory, `aiden-img-${img.id}.${ext}`);
|
|
5565
|
+
(0, import_fs3.writeFileSync)(filePath, Buffer.from(img.data, "base64"));
|
|
5566
|
+
paths.push(filePath);
|
|
5567
|
+
files.push({
|
|
5568
|
+
filename: img.filename,
|
|
5569
|
+
mimeType: img.mimeType,
|
|
5570
|
+
width: img.width,
|
|
5571
|
+
height: img.height,
|
|
5572
|
+
path: filePath
|
|
5573
|
+
});
|
|
5574
|
+
}
|
|
5575
|
+
return {
|
|
5576
|
+
paths,
|
|
5577
|
+
files,
|
|
5578
|
+
cleanup: () => {
|
|
5579
|
+
try {
|
|
5580
|
+
(0, import_fs3.rmSync)(directory, { recursive: true, force: true });
|
|
5581
|
+
} catch {
|
|
5582
|
+
}
|
|
5583
|
+
}
|
|
5584
|
+
};
|
|
5585
|
+
}
|
|
5586
|
+
function appendImagePathReferences(prompt, images) {
|
|
5587
|
+
if (images.length === 0) return prompt;
|
|
5588
|
+
const imageLines = images.map((img) => {
|
|
5589
|
+
const dimensions = img.width > 0 && img.height > 0 ? `, ${img.width}x${img.height}` : "";
|
|
5590
|
+
return `- ${img.filename} (${img.mimeType}${dimensions}): @${img.path} (path: ${img.path})`;
|
|
5591
|
+
});
|
|
5592
|
+
return [
|
|
5593
|
+
prompt,
|
|
5594
|
+
"Attached images are available as local files. Use the @path references below when this CLI supports file references, or read the plain paths with filesystem tools:",
|
|
5595
|
+
...imageLines
|
|
5596
|
+
].join("\n\n");
|
|
5597
|
+
}
|
|
5598
|
+
function buildPromptWithImagePathReferences(context, images) {
|
|
5599
|
+
const base = buildPromptWithSystem(context.config, context.promptText);
|
|
5600
|
+
const prompt = context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
5601
|
+
return appendImagePathReferences(prompt, images);
|
|
5602
|
+
}
|
|
5603
|
+
function formatCliSpawnError(command, error) {
|
|
5604
|
+
if (error.code === "ENOENT") {
|
|
5605
|
+
return new Error(
|
|
5606
|
+
`CLI command not found: ${command}. Install it or configure this provider with the full executable path.`
|
|
5607
|
+
);
|
|
5608
|
+
}
|
|
5609
|
+
return error;
|
|
5610
|
+
}
|
|
5611
|
+
function createGenericCliBackend(options) {
|
|
5612
|
+
return {
|
|
5613
|
+
kind: options.kind,
|
|
5614
|
+
supportTier: options.supportTier,
|
|
5615
|
+
async run(context) {
|
|
5616
|
+
const presenter = context.presenter;
|
|
5617
|
+
const state = {
|
|
5618
|
+
process: null,
|
|
5619
|
+
iterations: 0,
|
|
5620
|
+
summary: "",
|
|
5621
|
+
allowContinuation: options.allowContinuation ?? false
|
|
5622
|
+
};
|
|
5623
|
+
const prompt = options.augmentPrompt?.(context) ?? (context.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(context.config, context.promptText)) : buildPromptWithSystem(context.config, context.promptText));
|
|
5624
|
+
const args = options.buildArgs?.(context, prompt) ?? options.args.map((arg) => arg === "{{prompt}}" ? prompt : arg);
|
|
5625
|
+
const child = spawnCli(options.command, args, context);
|
|
5626
|
+
state.process = child;
|
|
5627
|
+
context.onProcessSpawned?.(child);
|
|
5628
|
+
const safeArgs = args.map(
|
|
5629
|
+
(a, i) => i > 0 && args[i - 1] === "--system-prompt" ? `"<system-prompt ${a.length} chars>"` : a
|
|
5630
|
+
);
|
|
5631
|
+
console.info(`[${options.kind}] Spawning: ${options.command} ${safeArgs.join(" ")}`);
|
|
5632
|
+
presenter.recordRawTranscript?.("system", `${options.command} ${args.join(" ")}`, {
|
|
5633
|
+
backendKind: options.kind
|
|
5634
|
+
});
|
|
5635
|
+
child.stdin.on("error", (err) => {
|
|
5636
|
+
if (err.code !== "EPIPE") {
|
|
5637
|
+
console.error(`[${options.kind}] stdin error:`, err);
|
|
5638
|
+
}
|
|
5639
|
+
});
|
|
5640
|
+
if (options.stdinWriter) {
|
|
5641
|
+
options.stdinWriter(child.stdin, context);
|
|
5642
|
+
} else if (options.promptViaStdin) {
|
|
5643
|
+
child.stdin.write(prompt);
|
|
5644
|
+
if (!options.keepStdinOpen) child.stdin.end();
|
|
5645
|
+
} else if (!options.keepStdinOpen) {
|
|
5646
|
+
child.stdin.end();
|
|
5647
|
+
}
|
|
5648
|
+
if (options.keepStdinOpen) {
|
|
5649
|
+
state.stdin = child.stdin;
|
|
5650
|
+
}
|
|
5651
|
+
const stdoutRl = (0, import_readline.createInterface)({ input: child.stdout });
|
|
5652
|
+
const stderrRl = (0, import_readline.createInterface)({ input: child.stderr });
|
|
5384
5653
|
const stderrLines = [];
|
|
5385
5654
|
stdoutRl.on("line", (line) => {
|
|
5386
5655
|
presenter.recordRawTranscript?.("stdout", line);
|
|
@@ -5708,155 +5977,281 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
5708
5977
|
break;
|
|
5709
5978
|
}
|
|
5710
5979
|
}
|
|
5711
|
-
function
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
return parsedValue.detail.trim();
|
|
5733
|
-
}
|
|
5734
|
-
if (typeof parsedValue.message === "string" && parsedValue.message.trim().length > 0) {
|
|
5735
|
-
return parsedValue.message.trim();
|
|
5980
|
+
function buildClaudeModelArg(baseModel, selectedContextWindow) {
|
|
5981
|
+
if (selectedContextWindow === "1m") return `${baseModel}[1m]`;
|
|
5982
|
+
return baseModel;
|
|
5983
|
+
}
|
|
5984
|
+
function createClaudeCliBackend(command = "claude", defaultArgs = []) {
|
|
5985
|
+
return {
|
|
5986
|
+
kind: "claude_cli",
|
|
5987
|
+
supportTier: "structured",
|
|
5988
|
+
async run(context) {
|
|
5989
|
+
const args = [
|
|
5990
|
+
"--verbose",
|
|
5991
|
+
"--output-format",
|
|
5992
|
+
"stream-json",
|
|
5993
|
+
"--input-format",
|
|
5994
|
+
"stream-json",
|
|
5995
|
+
"--permission-mode",
|
|
5996
|
+
getClaudePermissionMode(context.config)
|
|
5997
|
+
];
|
|
5998
|
+
const disallowedTools = getClaudeDisallowedTools(context.config);
|
|
5999
|
+
if (disallowedTools.length > 0) {
|
|
6000
|
+
args.push("--disallowedTools", disallowedTools.join(","));
|
|
5736
6001
|
}
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
state.iterations += 1;
|
|
5746
|
-
break;
|
|
5747
|
-
case "session_configured":
|
|
5748
|
-
if (typeof parsed.session_id === "string") {
|
|
5749
|
-
state.runtimeSessionId = parsed.session_id;
|
|
6002
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
6003
|
+
if (!resumeId) {
|
|
6004
|
+
if (context.config.systemPrompt?.trim()) {
|
|
6005
|
+
args.push("--system-prompt", context.config.systemPrompt.trim());
|
|
6006
|
+
}
|
|
6007
|
+
if (context.config.systemPromptAppend?.trim()) {
|
|
6008
|
+
args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
|
|
6009
|
+
}
|
|
5750
6010
|
}
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
break;
|
|
5755
|
-
case "item.started": {
|
|
5756
|
-
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
5757
|
-
if (!item || item.type !== "command_execution" || typeof item.id !== "string") break;
|
|
5758
|
-
const command = typeof item.command === "string" ? item.command : "";
|
|
5759
|
-
void presenter.onToolUse("Bash", { command, cwd: context.cwd }, item.id);
|
|
5760
|
-
break;
|
|
5761
|
-
}
|
|
5762
|
-
case "item.completed": {
|
|
5763
|
-
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
5764
|
-
if (!item || typeof item.type !== "string") break;
|
|
5765
|
-
if (item.type === "reasoning" && typeof item.text === "string") {
|
|
5766
|
-
void presenter.onThinking(item.text);
|
|
5767
|
-
break;
|
|
6011
|
+
const baseModel = context.config.selectedModel?.trim();
|
|
6012
|
+
if (baseModel) {
|
|
6013
|
+
args.push("--model", buildClaudeModelArg(baseModel, context.config.selectedContextWindow));
|
|
5768
6014
|
}
|
|
5769
|
-
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
void presenter.onAssistantText(item.text);
|
|
5773
|
-
break;
|
|
6015
|
+
const claudeEffort = context.config.selectedEffortLevel?.trim();
|
|
6016
|
+
if (claudeEffort) {
|
|
6017
|
+
args.push("--effort", claudeEffort);
|
|
5774
6018
|
}
|
|
5775
|
-
if (
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
const resultText = output.trim().length > 0 ? output : itemExitCode === null ? "" : `Exit code: ${itemExitCode}`;
|
|
5779
|
-
void presenter.onToolResult?.(item.id, resultText);
|
|
5780
|
-
break;
|
|
6019
|
+
if (resumeId) {
|
|
6020
|
+
args.push("--resume", resumeId);
|
|
6021
|
+
console.info("[claude_cli] Resuming session", { resumeId });
|
|
5781
6022
|
}
|
|
5782
|
-
|
|
6023
|
+
args.push("--chrome");
|
|
6024
|
+
console.info("[claude_cli] Chrome flag added \u2014 final args:", args.join(" "));
|
|
6025
|
+
args.push(...defaultArgs);
|
|
6026
|
+
return createGenericCliBackend({
|
|
6027
|
+
kind: "claude_cli",
|
|
6028
|
+
supportTier: "structured",
|
|
6029
|
+
command,
|
|
6030
|
+
args,
|
|
6031
|
+
parseStructuredLine: parseClaudeStructuredLine,
|
|
6032
|
+
stdinWriter: (stdin, ctx) => {
|
|
6033
|
+
const contentBlocks = [];
|
|
6034
|
+
if (ctx.config.images?.length) {
|
|
6035
|
+
for (const img of ctx.config.images) {
|
|
6036
|
+
contentBlocks.push({
|
|
6037
|
+
type: "image",
|
|
6038
|
+
source: { type: "base64", media_type: img.mimeType, data: img.data }
|
|
6039
|
+
});
|
|
6040
|
+
}
|
|
6041
|
+
}
|
|
6042
|
+
const promptText = ctx.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(ctx.config, ctx.promptText)) : buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6043
|
+
if (promptText.trim()) {
|
|
6044
|
+
contentBlocks.push({ type: "text", text: promptText });
|
|
6045
|
+
}
|
|
6046
|
+
console.info(`[claude_cli] stdinWriter: ${contentBlocks.length} content blocks (${ctx.config.images?.length ?? 0} images)`);
|
|
6047
|
+
const message = JSON.stringify({
|
|
6048
|
+
type: "user",
|
|
6049
|
+
message: { role: "user", content: contentBlocks },
|
|
6050
|
+
session_id: "default",
|
|
6051
|
+
parent_tool_use_id: null
|
|
6052
|
+
});
|
|
6053
|
+
stdin.write(message + "\n");
|
|
6054
|
+
},
|
|
6055
|
+
keepStdinOpen: true,
|
|
6056
|
+
allowContinuation: true,
|
|
6057
|
+
continuationIdleMs: 10 * 60 * 1e3
|
|
6058
|
+
// 10 minutes
|
|
6059
|
+
}).run(context);
|
|
5783
6060
|
}
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
6061
|
+
};
|
|
6062
|
+
}
|
|
6063
|
+
function createSupatestCliBackend(command = "supatest", defaultArgs = []) {
|
|
6064
|
+
return {
|
|
6065
|
+
kind: "supatest_cli",
|
|
6066
|
+
supportTier: "structured",
|
|
6067
|
+
async run(context) {
|
|
6068
|
+
const args = [
|
|
6069
|
+
"--cwd",
|
|
6070
|
+
context.cwd,
|
|
6071
|
+
"--output-format",
|
|
6072
|
+
"stream-json",
|
|
6073
|
+
"--input-format",
|
|
6074
|
+
"stream-json",
|
|
6075
|
+
"--permission-mode",
|
|
6076
|
+
getClaudePermissionMode(context.config)
|
|
6077
|
+
];
|
|
6078
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
6079
|
+
if (!resumeId) {
|
|
6080
|
+
if (context.config.systemPrompt?.trim()) {
|
|
6081
|
+
args.push("--system-prompt", context.config.systemPrompt.trim());
|
|
6082
|
+
}
|
|
6083
|
+
if (context.config.systemPromptAppend?.trim()) {
|
|
6084
|
+
args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
const baseModel = context.config.selectedModel?.trim();
|
|
6088
|
+
if (baseModel) {
|
|
6089
|
+
args.push("--model", buildClaudeModelArg(baseModel, context.config.selectedContextWindow));
|
|
6090
|
+
}
|
|
6091
|
+
if (resumeId) {
|
|
6092
|
+
args.push("--resume", resumeId);
|
|
6093
|
+
console.info("[supatest_cli] Resuming session", { resumeId });
|
|
6094
|
+
}
|
|
6095
|
+
args.push(...defaultArgs);
|
|
6096
|
+
return createGenericCliBackend({
|
|
6097
|
+
kind: "supatest_cli",
|
|
6098
|
+
supportTier: "structured",
|
|
6099
|
+
command,
|
|
6100
|
+
args,
|
|
6101
|
+
parseStructuredLine: parseClaudeStructuredLine,
|
|
6102
|
+
stdinWriter: (stdin, ctx) => {
|
|
6103
|
+
const contentBlocks = [];
|
|
6104
|
+
if (ctx.config.images?.length) {
|
|
6105
|
+
for (const img of ctx.config.images) {
|
|
6106
|
+
contentBlocks.push({
|
|
6107
|
+
type: "image",
|
|
6108
|
+
source: { type: "base64", media_type: img.mimeType, data: img.data }
|
|
6109
|
+
});
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
const promptText = ctx.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(ctx.config, ctx.promptText)) : buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6113
|
+
if (promptText.trim()) {
|
|
6114
|
+
contentBlocks.push({ type: "text", text: promptText });
|
|
6115
|
+
}
|
|
6116
|
+
const message = JSON.stringify({
|
|
6117
|
+
type: "user",
|
|
6118
|
+
message: { role: "user", content: contentBlocks },
|
|
6119
|
+
session_id: "default",
|
|
6120
|
+
parent_tool_use_id: null
|
|
6121
|
+
});
|
|
6122
|
+
stdin.write(message + "\n");
|
|
6123
|
+
},
|
|
6124
|
+
keepStdinOpen: true
|
|
6125
|
+
}).run(context);
|
|
5799
6126
|
}
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
6127
|
+
};
|
|
6128
|
+
}
|
|
6129
|
+
function shouldEnableKimiCliThinking(model) {
|
|
6130
|
+
return model?.trim() === "kimi-k2.6-thinking";
|
|
6131
|
+
}
|
|
6132
|
+
function resolveKimiCliModelArg(model) {
|
|
6133
|
+
const trimmed = model?.trim();
|
|
6134
|
+
if (!trimmed) return void 0;
|
|
6135
|
+
if (trimmed === "kimi-latest" || trimmed === "kimi-k2.5" || trimmed === "kimi-k2.6" || shouldEnableKimiCliThinking(trimmed)) {
|
|
6136
|
+
return void 0;
|
|
6137
|
+
}
|
|
6138
|
+
if (trimmed.startsWith("claude-")) return void 0;
|
|
6139
|
+
return trimmed;
|
|
6140
|
+
}
|
|
6141
|
+
function createKimiCliBackend(command = "kimi", defaultArgs = []) {
|
|
6142
|
+
return {
|
|
6143
|
+
kind: "kimi_cli",
|
|
6144
|
+
supportTier: "structured",
|
|
6145
|
+
async run(context) {
|
|
6146
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6147
|
+
try {
|
|
6148
|
+
return await createGenericCliBackend({
|
|
6149
|
+
kind: "kimi_cli",
|
|
6150
|
+
supportTier: "structured",
|
|
6151
|
+
command,
|
|
6152
|
+
args: [],
|
|
6153
|
+
buildArgs: (ctx, prompt) => {
|
|
6154
|
+
const args = [
|
|
6155
|
+
"--print",
|
|
6156
|
+
"--output-format",
|
|
6157
|
+
"stream-json",
|
|
6158
|
+
"--work-dir",
|
|
6159
|
+
ctx.cwd
|
|
6160
|
+
];
|
|
6161
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6162
|
+
if (resumeId) args.push("--session", resumeId);
|
|
6163
|
+
if (shouldEnableKimiCliThinking(ctx.config.selectedModel)) {
|
|
6164
|
+
args.push("--thinking");
|
|
6165
|
+
}
|
|
6166
|
+
const model = resolveKimiCliModelArg(ctx.config.selectedModel);
|
|
6167
|
+
if (model) args.push("--model", model);
|
|
6168
|
+
return [...args, ...defaultArgs, "--prompt", prompt];
|
|
6169
|
+
},
|
|
6170
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
6171
|
+
parseStructuredLine: parseKimiStructuredLine
|
|
6172
|
+
}).run(context);
|
|
6173
|
+
} finally {
|
|
6174
|
+
cleanup();
|
|
6175
|
+
}
|
|
5806
6176
|
}
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
6177
|
+
};
|
|
6178
|
+
}
|
|
6179
|
+
function buildGeminiToolResultText(parsed) {
|
|
6180
|
+
const output = typeof parsed.output === "string" ? parsed.output : "";
|
|
6181
|
+
if (output.trim().length > 0) return output;
|
|
6182
|
+
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6183
|
+
const message = typeof error?.message === "string" ? error.message.trim() : "";
|
|
6184
|
+
const errorType = typeof error?.type === "string" ? error.type.trim() : "";
|
|
6185
|
+
if (message && errorType) return `${errorType}: ${message}`;
|
|
6186
|
+
if (message) return message;
|
|
6187
|
+
const status = typeof parsed.status === "string" ? parsed.status.trim() : "";
|
|
6188
|
+
return status || "Tool completed";
|
|
6189
|
+
}
|
|
6190
|
+
function parseGeminiStructuredLine(line, context, state) {
|
|
6191
|
+
const presenter = context.presenter;
|
|
6192
|
+
let parsed = null;
|
|
6193
|
+
try {
|
|
6194
|
+
parsed = JSON.parse(line);
|
|
6195
|
+
} catch {
|
|
6196
|
+
void presenter.onLog(`[gemini_cli] ${line}`);
|
|
6197
|
+
return;
|
|
6198
|
+
}
|
|
6199
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
6200
|
+
if (!type) return;
|
|
6201
|
+
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
6202
|
+
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
6203
|
+
switch (type) {
|
|
6204
|
+
case "init": {
|
|
6205
|
+
const model = state.activeModel || context.config.selectedModel || "gemini";
|
|
6206
|
+
void presenter.onLog(`[gemini_cli] Session initialized with ${model}`);
|
|
5811
6207
|
break;
|
|
5812
6208
|
}
|
|
5813
|
-
case "
|
|
5814
|
-
|
|
5815
|
-
const
|
|
5816
|
-
|
|
5817
|
-
|
|
6209
|
+
case "message": {
|
|
6210
|
+
if (typeof parsed.role !== "string" || parsed.role !== "assistant") break;
|
|
6211
|
+
const content = typeof parsed.content === "string" ? parsed.content : "";
|
|
6212
|
+
if (!content) break;
|
|
6213
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
6214
|
+
state.summary += content;
|
|
6215
|
+
void presenter.onAssistantText(content);
|
|
5818
6216
|
break;
|
|
5819
6217
|
}
|
|
5820
|
-
case "
|
|
5821
|
-
const
|
|
5822
|
-
|
|
6218
|
+
case "tool_use": {
|
|
6219
|
+
const toolName = typeof parsed.tool_name === "string" ? parsed.tool_name : "Gemini Tool";
|
|
6220
|
+
const toolId = typeof parsed.tool_id === "string" ? parsed.tool_id : `gemini-tool-${Date.now()}`;
|
|
6221
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
6222
|
+
void presenter.onToolUse(toolName, parsed.parameters ?? {}, toolId);
|
|
5823
6223
|
break;
|
|
5824
6224
|
}
|
|
5825
|
-
case "
|
|
5826
|
-
const
|
|
5827
|
-
state.
|
|
5828
|
-
|
|
5829
|
-
numTurns: Math.max(state.iterations, 1),
|
|
5830
|
-
durationMs: 0,
|
|
5831
|
-
inputTokens: typeof info.input_tokens === "number" ? info.input_tokens : 0,
|
|
5832
|
-
outputTokens: typeof info.output_tokens === "number" ? info.output_tokens : 0,
|
|
5833
|
-
cacheReadTokens: typeof info.cached_input_tokens === "number" ? info.cached_input_tokens : 0,
|
|
5834
|
-
cacheCreationTokens: 0,
|
|
5835
|
-
costUsd: typeof info.total_cost_usd === "number" ? info.total_cost_usd : 0
|
|
5836
|
-
};
|
|
5837
|
-
void presenter.onUsageUpdate?.({
|
|
5838
|
-
model: state.usage.model,
|
|
5839
|
-
inputTokens: state.usage.inputTokens,
|
|
5840
|
-
outputTokens: state.usage.outputTokens,
|
|
5841
|
-
cacheReadTokens: state.usage.cacheReadTokens,
|
|
5842
|
-
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
5843
|
-
});
|
|
6225
|
+
case "tool_result": {
|
|
6226
|
+
const toolId = typeof parsed.tool_id === "string" ? parsed.tool_id : `gemini-tool-${Date.now()}`;
|
|
6227
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
6228
|
+
void presenter.onToolResult?.(toolId, buildGeminiToolResultText(parsed));
|
|
5844
6229
|
break;
|
|
5845
6230
|
}
|
|
5846
|
-
case "
|
|
5847
|
-
const
|
|
5848
|
-
|
|
6231
|
+
case "error": {
|
|
6232
|
+
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6233
|
+
const message = typeof parsed.message === "string" && parsed.message.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || typeof parsed.error === "string" && parsed.error.trim() || typeof parsed.error_message === "string" && parsed.error_message.trim() || "";
|
|
6234
|
+
const severity = typeof parsed.severity === "string" ? parsed.severity : "error";
|
|
6235
|
+
if (message) {
|
|
6236
|
+
if (severity === "error") {
|
|
6237
|
+
void presenter.onError(message);
|
|
6238
|
+
state.error = message;
|
|
6239
|
+
} else {
|
|
6240
|
+
void presenter.onLog(`[gemini_cli/${severity}] ${message}`);
|
|
6241
|
+
}
|
|
6242
|
+
}
|
|
5849
6243
|
break;
|
|
5850
6244
|
}
|
|
5851
|
-
case "
|
|
5852
|
-
const
|
|
6245
|
+
case "result": {
|
|
6246
|
+
const stats = typeof parsed.stats === "object" && parsed.stats !== null ? parsed.stats : {};
|
|
6247
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5853
6248
|
state.usage = {
|
|
5854
|
-
model: typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "
|
|
6249
|
+
model: state.activeModel || (typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "gemini"),
|
|
5855
6250
|
numTurns: Math.max(state.iterations, 1),
|
|
5856
|
-
durationMs: 0,
|
|
5857
|
-
inputTokens: typeof
|
|
5858
|
-
outputTokens: typeof
|
|
5859
|
-
cacheReadTokens: typeof
|
|
6251
|
+
durationMs: typeof stats.duration_ms === "number" ? stats.duration_ms : 0,
|
|
6252
|
+
inputTokens: typeof stats.input_tokens === "number" ? stats.input_tokens : typeof stats.input === "number" ? stats.input : 0,
|
|
6253
|
+
outputTokens: typeof stats.output_tokens === "number" ? stats.output_tokens : 0,
|
|
6254
|
+
cacheReadTokens: typeof stats.cached === "number" ? stats.cached : 0,
|
|
5860
6255
|
cacheCreationTokens: 0,
|
|
5861
6256
|
costUsd: 0
|
|
5862
6257
|
};
|
|
@@ -5867,24 +6262,15 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5867
6262
|
cacheReadTokens: state.usage.cacheReadTokens,
|
|
5868
6263
|
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
5869
6264
|
});
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
|
|
5873
|
-
|
|
5874
|
-
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5878
|
-
|
|
5879
|
-
break;
|
|
5880
|
-
}
|
|
5881
|
-
case "turn.failed": {
|
|
5882
|
-
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : {};
|
|
5883
|
-
const message = extractCodexErrorMessage(error.message) || extractCodexErrorMessage(error.detail) || typeof parsed.message === "string" && parsed.message.trim() || "";
|
|
5884
|
-
if (message) {
|
|
5885
|
-
const normalizedMessage = normalizeCodexCliErrorMessage(message);
|
|
5886
|
-
void presenter.onError(normalizedMessage);
|
|
5887
|
-
state.error = normalizedMessage;
|
|
6265
|
+
if (typeof parsed.status === "string" && parsed.status !== "success" && !state.error) {
|
|
6266
|
+
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6267
|
+
const errMsg = typeof parsed.error === "string" && parsed.error.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || typeof parsed.error_message === "string" && parsed.error_message.trim() || typeof parsed.message === "string" && parsed.message.trim() || typeof parsed.result === "string" && parsed.result.trim() || parsed.status;
|
|
6268
|
+
state.error = errMsg;
|
|
6269
|
+
console.error("[gemini_cli] result status=", parsed.status, "error:", JSON.stringify({
|
|
6270
|
+
error: parsed.error,
|
|
6271
|
+
error_message: parsed.error_message,
|
|
6272
|
+
message: parsed.message
|
|
6273
|
+
}));
|
|
5888
6274
|
}
|
|
5889
6275
|
break;
|
|
5890
6276
|
}
|
|
@@ -5892,109 +6278,36 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5892
6278
|
break;
|
|
5893
6279
|
}
|
|
5894
6280
|
}
|
|
5895
|
-
function
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
5902
|
-
|
|
5903
|
-
|
|
5904
|
-
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
|
|
5919
|
-
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
}
|
|
5923
|
-
return raw;
|
|
5924
|
-
}
|
|
5925
|
-
function findCodexSessionLog(runtimeSessionId, codexHome) {
|
|
5926
|
-
if (!runtimeSessionId || !(0, import_fs.existsSync)(codexHome)) return null;
|
|
5927
|
-
const root = (0, import_path.join)(codexHome, "sessions");
|
|
5928
|
-
if (!(0, import_fs.existsSync)(root)) return null;
|
|
5929
|
-
const matches = [];
|
|
5930
|
-
const stack = [root];
|
|
5931
|
-
while (stack.length > 0) {
|
|
5932
|
-
const dir = stack.pop();
|
|
5933
|
-
let entries;
|
|
5934
|
-
try {
|
|
5935
|
-
entries = (0, import_fs.readdirSync)(dir, { withFileTypes: true });
|
|
5936
|
-
} catch {
|
|
5937
|
-
continue;
|
|
5938
|
-
}
|
|
5939
|
-
for (const entry of entries) {
|
|
5940
|
-
const path = (0, import_path.join)(dir, entry.name);
|
|
5941
|
-
if (entry.isDirectory()) {
|
|
5942
|
-
stack.push(path);
|
|
5943
|
-
} else if (entry.isFile() && entry.name.includes(runtimeSessionId) && entry.name.endsWith(".jsonl")) {
|
|
5944
|
-
try {
|
|
5945
|
-
matches.push({ path, mtimeMs: (0, import_fs.statSync)(path).mtimeMs });
|
|
5946
|
-
} catch {
|
|
5947
|
-
matches.push({ path, mtimeMs: 0 });
|
|
5948
|
-
}
|
|
6281
|
+
function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
|
|
6282
|
+
return {
|
|
6283
|
+
kind: "gemini_cli",
|
|
6284
|
+
supportTier: "structured",
|
|
6285
|
+
async run(context) {
|
|
6286
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6287
|
+
try {
|
|
6288
|
+
return await createGenericCliBackend({
|
|
6289
|
+
kind: "gemini_cli",
|
|
6290
|
+
supportTier: "structured",
|
|
6291
|
+
command,
|
|
6292
|
+
args: [],
|
|
6293
|
+
buildArgs: (ctx) => {
|
|
6294
|
+
const args = ["-p", "", "--output-format", "stream-json"];
|
|
6295
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6296
|
+
const approvalMode = ctx.config.mode === "plan" || ctx.config.mode === "ask" || ctx.config.mode === "review" ? "plan" : "yolo";
|
|
6297
|
+
args.push("--approval-mode", approvalMode);
|
|
6298
|
+
if (resumeId) args.push("--resume", resumeId);
|
|
6299
|
+
if (ctx.config.selectedModel?.trim()) args.push("--model", ctx.config.selectedModel.trim());
|
|
6300
|
+
return [...args, ...defaultArgs];
|
|
6301
|
+
},
|
|
6302
|
+
promptViaStdin: true,
|
|
6303
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
6304
|
+
parseStructuredLine: parseGeminiStructuredLine
|
|
6305
|
+
}).run(context);
|
|
6306
|
+
} finally {
|
|
6307
|
+
cleanup();
|
|
5949
6308
|
}
|
|
5950
6309
|
}
|
|
5951
|
-
}
|
|
5952
|
-
matches.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
5953
|
-
return matches[0]?.path ?? null;
|
|
5954
|
-
}
|
|
5955
|
-
async function replayCodexMcpToolEventsFromSessionLog(context, state) {
|
|
5956
|
-
const runtimeSessionId = state.runtimeSessionId;
|
|
5957
|
-
if (!runtimeSessionId) return;
|
|
5958
|
-
const codexHome = context.env.CODEX_HOME || process.env.CODEX_HOME || (0, import_path.join)((0, import_os.homedir)(), ".codex");
|
|
5959
|
-
const logPath = findCodexSessionLog(runtimeSessionId, codexHome);
|
|
5960
|
-
if (!logPath) return;
|
|
5961
|
-
const emittedToolIds = /* @__PURE__ */ new Set();
|
|
5962
|
-
const lines = (0, import_fs.readFileSync)(logPath, "utf8").split(/\r?\n/).filter(Boolean);
|
|
5963
|
-
for (const line of lines) {
|
|
5964
|
-
const entry = parseJsonObject(line);
|
|
5965
|
-
const payload = entry && typeof entry.payload === "object" && entry.payload !== null ? entry.payload : null;
|
|
5966
|
-
if (!payload) continue;
|
|
5967
|
-
if (payload.type === "function_call") {
|
|
5968
|
-
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5969
|
-
const name = typeof payload.name === "string" ? payload.name : "";
|
|
5970
|
-
const namespace = typeof payload.namespace === "string" ? payload.namespace : "";
|
|
5971
|
-
if (!callId || !name || !namespace.startsWith("mcp__")) continue;
|
|
5972
|
-
const toolName = `${namespace}${name}`;
|
|
5973
|
-
context.presenter.onToolUse(toolName, parseMaybeJson(payload.arguments), callId);
|
|
5974
|
-
emittedToolIds.add(callId);
|
|
5975
|
-
continue;
|
|
5976
|
-
}
|
|
5977
|
-
if (payload.type === "tool_search_call") {
|
|
5978
|
-
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5979
|
-
if (!callId) continue;
|
|
5980
|
-
context.presenter.onToolUse("tool_search", payload.arguments ?? {}, callId);
|
|
5981
|
-
emittedToolIds.add(callId);
|
|
5982
|
-
}
|
|
5983
|
-
}
|
|
5984
|
-
if (emittedToolIds.size === 0) return;
|
|
5985
|
-
for (const line of lines) {
|
|
5986
|
-
const entry = parseJsonObject(line);
|
|
5987
|
-
const payload = entry && typeof entry.payload === "object" && entry.payload !== null ? entry.payload : null;
|
|
5988
|
-
if (!payload || payload.type !== "function_call_output" && payload.type !== "tool_search_output") continue;
|
|
5989
|
-
const callId = typeof payload.call_id === "string" ? payload.call_id : "";
|
|
5990
|
-
if (!emittedToolIds.has(callId)) continue;
|
|
5991
|
-
try {
|
|
5992
|
-
const result = payload.type === "tool_search_output" ? JSON.stringify(payload.tools ?? [], null, 2) : normalizeCodexMcpToolResult(payload.output);
|
|
5993
|
-
context.presenter.onToolResult?.(callId, result);
|
|
5994
|
-
} catch {
|
|
5995
|
-
context.presenter.onToolResult?.(callId, String(payload.output ?? ""));
|
|
5996
|
-
}
|
|
5997
|
-
}
|
|
6310
|
+
};
|
|
5998
6311
|
}
|
|
5999
6312
|
function extractCursorToolEntry(toolCall) {
|
|
6000
6313
|
const [rawName, rawPayload] = Object.entries(toolCall)[0] ?? [];
|
|
@@ -6106,107 +6419,10 @@ function parseCursorStructuredLine(line, context, state) {
|
|
|
6106
6419
|
state.error = resultText.trim() || "Cursor Agent run failed";
|
|
6107
6420
|
void presenter.onError(state.error);
|
|
6108
6421
|
}
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
}
|
|
6114
|
-
}
|
|
6115
|
-
function buildGeminiToolResultText(parsed) {
|
|
6116
|
-
const output = typeof parsed.output === "string" ? parsed.output : "";
|
|
6117
|
-
if (output.trim().length > 0) return output;
|
|
6118
|
-
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6119
|
-
const message = typeof error?.message === "string" ? error.message.trim() : "";
|
|
6120
|
-
const errorType = typeof error?.type === "string" ? error.type.trim() : "";
|
|
6121
|
-
if (message && errorType) return `${errorType}: ${message}`;
|
|
6122
|
-
if (message) return message;
|
|
6123
|
-
const status = typeof parsed.status === "string" ? parsed.status.trim() : "";
|
|
6124
|
-
return status || "Tool completed";
|
|
6125
|
-
}
|
|
6126
|
-
function parseGeminiStructuredLine(line, context, state) {
|
|
6127
|
-
const presenter = context.presenter;
|
|
6128
|
-
let parsed = null;
|
|
6129
|
-
try {
|
|
6130
|
-
parsed = JSON.parse(line);
|
|
6131
|
-
} catch {
|
|
6132
|
-
void presenter.onLog(`[gemini_cli] ${line}`);
|
|
6133
|
-
return;
|
|
6134
|
-
}
|
|
6135
|
-
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
6136
|
-
if (!type) return;
|
|
6137
|
-
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
6138
|
-
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
6139
|
-
switch (type) {
|
|
6140
|
-
case "init": {
|
|
6141
|
-
const model = state.activeModel || context.config.selectedModel || "gemini";
|
|
6142
|
-
void presenter.onLog(`[gemini_cli] Session initialized with ${model}`);
|
|
6143
|
-
break;
|
|
6144
|
-
}
|
|
6145
|
-
case "message": {
|
|
6146
|
-
if (typeof parsed.role !== "string" || parsed.role !== "assistant") break;
|
|
6147
|
-
const content = typeof parsed.content === "string" ? parsed.content : "";
|
|
6148
|
-
if (!content) break;
|
|
6149
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6150
|
-
state.summary += content;
|
|
6151
|
-
void presenter.onAssistantText(content);
|
|
6152
|
-
break;
|
|
6153
|
-
}
|
|
6154
|
-
case "tool_use": {
|
|
6155
|
-
const toolName = typeof parsed.tool_name === "string" ? parsed.tool_name : "Gemini Tool";
|
|
6156
|
-
const toolId = typeof parsed.tool_id === "string" ? parsed.tool_id : `gemini-tool-${Date.now()}`;
|
|
6157
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6158
|
-
void presenter.onToolUse(toolName, parsed.parameters ?? {}, toolId);
|
|
6159
|
-
break;
|
|
6160
|
-
}
|
|
6161
|
-
case "tool_result": {
|
|
6162
|
-
const toolId = typeof parsed.tool_id === "string" ? parsed.tool_id : `gemini-tool-${Date.now()}`;
|
|
6163
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6164
|
-
void presenter.onToolResult?.(toolId, buildGeminiToolResultText(parsed));
|
|
6165
|
-
break;
|
|
6166
|
-
}
|
|
6167
|
-
case "error": {
|
|
6168
|
-
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6169
|
-
const message = typeof parsed.message === "string" && parsed.message.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || typeof parsed.error === "string" && parsed.error.trim() || typeof parsed.error_message === "string" && parsed.error_message.trim() || "";
|
|
6170
|
-
const severity = typeof parsed.severity === "string" ? parsed.severity : "error";
|
|
6171
|
-
if (message) {
|
|
6172
|
-
if (severity === "error") {
|
|
6173
|
-
void presenter.onError(message);
|
|
6174
|
-
state.error = message;
|
|
6175
|
-
} else {
|
|
6176
|
-
void presenter.onLog(`[gemini_cli/${severity}] ${message}`);
|
|
6177
|
-
}
|
|
6178
|
-
}
|
|
6179
|
-
break;
|
|
6180
|
-
}
|
|
6181
|
-
case "result": {
|
|
6182
|
-
const stats = typeof parsed.stats === "object" && parsed.stats !== null ? parsed.stats : {};
|
|
6183
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6184
|
-
state.usage = {
|
|
6185
|
-
model: state.activeModel || (typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "gemini"),
|
|
6186
|
-
numTurns: Math.max(state.iterations, 1),
|
|
6187
|
-
durationMs: typeof stats.duration_ms === "number" ? stats.duration_ms : 0,
|
|
6188
|
-
inputTokens: typeof stats.input_tokens === "number" ? stats.input_tokens : typeof stats.input === "number" ? stats.input : 0,
|
|
6189
|
-
outputTokens: typeof stats.output_tokens === "number" ? stats.output_tokens : 0,
|
|
6190
|
-
cacheReadTokens: typeof stats.cached === "number" ? stats.cached : 0,
|
|
6191
|
-
cacheCreationTokens: 0,
|
|
6192
|
-
costUsd: 0
|
|
6193
|
-
};
|
|
6194
|
-
void presenter.onUsageUpdate?.({
|
|
6195
|
-
model: state.usage.model,
|
|
6196
|
-
inputTokens: state.usage.inputTokens,
|
|
6197
|
-
outputTokens: state.usage.outputTokens,
|
|
6198
|
-
cacheReadTokens: state.usage.cacheReadTokens,
|
|
6199
|
-
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
6200
|
-
});
|
|
6201
|
-
if (typeof parsed.status === "string" && parsed.status !== "success" && !state.error) {
|
|
6202
|
-
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6203
|
-
const errMsg = typeof parsed.error === "string" && parsed.error.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || typeof parsed.error_message === "string" && parsed.error_message.trim() || typeof parsed.message === "string" && parsed.message.trim() || typeof parsed.result === "string" && parsed.result.trim() || parsed.status;
|
|
6204
|
-
state.error = errMsg;
|
|
6205
|
-
console.error("[gemini_cli] result status=", parsed.status, "error:", JSON.stringify({
|
|
6206
|
-
error: parsed.error,
|
|
6207
|
-
error_message: parsed.error_message,
|
|
6208
|
-
message: parsed.message
|
|
6209
|
-
}));
|
|
6422
|
+
state.resultReceived = true;
|
|
6423
|
+
state.onResultReceived?.();
|
|
6424
|
+
if (state.stdin && !state.stdin.destroyed) {
|
|
6425
|
+
state.stdin.end();
|
|
6210
6426
|
}
|
|
6211
6427
|
break;
|
|
6212
6428
|
}
|
|
@@ -6214,29 +6430,8 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
6214
6430
|
break;
|
|
6215
6431
|
}
|
|
6216
6432
|
}
|
|
6217
|
-
function
|
|
6218
|
-
|
|
6219
|
-
switch (config.mode) {
|
|
6220
|
-
case "plan":
|
|
6221
|
-
case "ask":
|
|
6222
|
-
case "review":
|
|
6223
|
-
return true;
|
|
6224
|
-
default:
|
|
6225
|
-
return false;
|
|
6226
|
-
}
|
|
6227
|
-
}
|
|
6228
|
-
function getClaudePermissionMode(config) {
|
|
6229
|
-
switch (config.mode) {
|
|
6230
|
-
case "plan":
|
|
6231
|
-
case "ask":
|
|
6232
|
-
case "review":
|
|
6233
|
-
return "plan";
|
|
6234
|
-
default:
|
|
6235
|
-
return "bypassPermissions";
|
|
6236
|
-
}
|
|
6237
|
-
}
|
|
6238
|
-
function getClaudeDisallowedTools(config) {
|
|
6239
|
-
return isReadOnlyAgent(config.agentId) ? ["Edit", "Write", "MultiEdit", "NotebookEdit"] : [];
|
|
6433
|
+
function isCursorAgentCliModelId(model) {
|
|
6434
|
+
return model === "auto" || /^composer-/.test(model) || /^gpt-\d/.test(model) || /^claude-\d/.test(model) || /^gemini-\d/.test(model) || /^grok-\d/.test(model) || /^kimi-/.test(model);
|
|
6240
6435
|
}
|
|
6241
6436
|
function shouldForceCursorAgent(config) {
|
|
6242
6437
|
if (shouldUseReadOnlyRuntimePermissions(config)) return false;
|
|
@@ -6249,272 +6444,48 @@ function shouldForceCursorAgent(config) {
|
|
|
6249
6444
|
return true;
|
|
6250
6445
|
}
|
|
6251
6446
|
}
|
|
6252
|
-
function
|
|
6253
|
-
if (!images?.length) return { paths: [], cleanup: () => {
|
|
6254
|
-
} };
|
|
6255
|
-
const paths = [];
|
|
6256
|
-
for (const img of images) {
|
|
6257
|
-
const ext = img.mimeType.split("/")[1]?.replace("jpeg", "jpg") ?? "png";
|
|
6258
|
-
const filePath = (0, import_path.join)((0, import_os.tmpdir)(), `aiden-img-${img.id}.${ext}`);
|
|
6259
|
-
(0, import_fs.writeFileSync)(filePath, Buffer.from(img.data, "base64"));
|
|
6260
|
-
paths.push(filePath);
|
|
6261
|
-
}
|
|
6262
|
-
return {
|
|
6263
|
-
paths,
|
|
6264
|
-
cleanup: () => {
|
|
6265
|
-
for (const p of paths) {
|
|
6266
|
-
try {
|
|
6267
|
-
(0, import_fs.rmSync)(p, { force: true });
|
|
6268
|
-
} catch {
|
|
6269
|
-
}
|
|
6270
|
-
}
|
|
6271
|
-
}
|
|
6272
|
-
};
|
|
6273
|
-
}
|
|
6274
|
-
function buildClaudeModelArg(baseModel, selectedContextWindow) {
|
|
6275
|
-
if (selectedContextWindow === "1m") return `${baseModel}[1m]`;
|
|
6276
|
-
return baseModel;
|
|
6277
|
-
}
|
|
6278
|
-
function createClaudeCliBackend(command = "claude", defaultArgs = []) {
|
|
6447
|
+
function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = []) {
|
|
6279
6448
|
return {
|
|
6280
|
-
kind: "
|
|
6449
|
+
kind: "cursor_agent_cli",
|
|
6281
6450
|
supportTier: "structured",
|
|
6282
6451
|
async run(context) {
|
|
6283
|
-
const
|
|
6284
|
-
|
|
6285
|
-
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
const baseModel = context.config.selectedModel?.trim();
|
|
6306
|
-
if (baseModel) {
|
|
6307
|
-
args.push("--model", buildClaudeModelArg(baseModel, context.config.selectedContextWindow));
|
|
6308
|
-
}
|
|
6309
|
-
const claudeEffort = context.config.selectedEffortLevel?.trim();
|
|
6310
|
-
if (claudeEffort) {
|
|
6311
|
-
args.push("--effort", claudeEffort);
|
|
6312
|
-
}
|
|
6313
|
-
if (resumeId) {
|
|
6314
|
-
args.push("--resume", resumeId);
|
|
6315
|
-
console.info("[claude_cli] Resuming session", { resumeId });
|
|
6316
|
-
}
|
|
6317
|
-
args.push("--chrome");
|
|
6318
|
-
console.info("[claude_cli] Chrome flag added \u2014 final args:", args.join(" "));
|
|
6319
|
-
args.push(...defaultArgs);
|
|
6320
|
-
return createGenericCliBackend({
|
|
6321
|
-
kind: "claude_cli",
|
|
6322
|
-
supportTier: "structured",
|
|
6323
|
-
command,
|
|
6324
|
-
args,
|
|
6325
|
-
parseStructuredLine: parseClaudeStructuredLine,
|
|
6326
|
-
stdinWriter: (stdin, ctx) => {
|
|
6327
|
-
const contentBlocks = [];
|
|
6328
|
-
if (ctx.config.images?.length) {
|
|
6329
|
-
for (const img of ctx.config.images) {
|
|
6330
|
-
contentBlocks.push({
|
|
6331
|
-
type: "image",
|
|
6332
|
-
source: { type: "base64", media_type: img.mimeType, data: img.data }
|
|
6333
|
-
});
|
|
6452
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6453
|
+
try {
|
|
6454
|
+
return await createGenericCliBackend({
|
|
6455
|
+
kind: "cursor_agent_cli",
|
|
6456
|
+
supportTier: "structured",
|
|
6457
|
+
command,
|
|
6458
|
+
args: [],
|
|
6459
|
+
buildArgs: (ctx, prompt) => {
|
|
6460
|
+
const args = [
|
|
6461
|
+
"--print",
|
|
6462
|
+
"--output-format",
|
|
6463
|
+
"stream-json",
|
|
6464
|
+
"--trust",
|
|
6465
|
+
"--workspace",
|
|
6466
|
+
ctx.cwd
|
|
6467
|
+
];
|
|
6468
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6469
|
+
if (shouldForceCursorAgent(ctx.config)) args.push("--force");
|
|
6470
|
+
if (ctx.config.mode === "plan" || ctx.config.mode === "review") {
|
|
6471
|
+
args.push("--mode", "plan");
|
|
6472
|
+
} else if (ctx.config.mode === "ask") {
|
|
6473
|
+
args.push("--mode", "ask");
|
|
6334
6474
|
}
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
}
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
keepStdinOpen: true,
|
|
6350
|
-
allowContinuation: true,
|
|
6351
|
-
continuationIdleMs: 10 * 60 * 1e3
|
|
6352
|
-
// 10 minutes
|
|
6353
|
-
}).run(context);
|
|
6354
|
-
}
|
|
6355
|
-
};
|
|
6356
|
-
}
|
|
6357
|
-
function kimiSessionId(cwd) {
|
|
6358
|
-
let resolved = cwd;
|
|
6359
|
-
try {
|
|
6360
|
-
resolved = (0, import_fs.realpathSync)(cwd);
|
|
6361
|
-
} catch {
|
|
6362
|
-
}
|
|
6363
|
-
return (0, import_crypto.createHash)("md5").update(resolved).digest("hex");
|
|
6364
|
-
}
|
|
6365
|
-
function shouldEnableKimiCliThinking(model) {
|
|
6366
|
-
return model?.trim() === "kimi-k2.6-thinking";
|
|
6367
|
-
}
|
|
6368
|
-
function resolveKimiCliModelArg(model) {
|
|
6369
|
-
const trimmed = model?.trim();
|
|
6370
|
-
if (!trimmed) return void 0;
|
|
6371
|
-
if (trimmed === "kimi-latest" || trimmed === "kimi-k2.5" || trimmed === "kimi-k2.6" || shouldEnableKimiCliThinking(trimmed)) {
|
|
6372
|
-
return void 0;
|
|
6373
|
-
}
|
|
6374
|
-
if (trimmed.startsWith("claude-")) return void 0;
|
|
6375
|
-
return trimmed;
|
|
6376
|
-
}
|
|
6377
|
-
function parseKimiStructuredLine(line, context, state) {
|
|
6378
|
-
const presenter = context.presenter;
|
|
6379
|
-
if (!state.runtimeSessionId) {
|
|
6380
|
-
state.runtimeSessionId = kimiSessionId(context.cwd);
|
|
6381
|
-
}
|
|
6382
|
-
let parsed;
|
|
6383
|
-
try {
|
|
6384
|
-
parsed = JSON.parse(line);
|
|
6385
|
-
} catch {
|
|
6386
|
-
const text = line.trim();
|
|
6387
|
-
if (text.length > 0) {
|
|
6388
|
-
if (text.toLowerCase().includes("llm not set")) state.error = text;
|
|
6389
|
-
void presenter.onLog(`[kimi_cli] ${line}`);
|
|
6390
|
-
}
|
|
6391
|
-
return;
|
|
6392
|
-
}
|
|
6393
|
-
const role = typeof parsed.role === "string" ? parsed.role : "";
|
|
6394
|
-
const contentBlocks = Array.isArray(parsed.content) ? parsed.content : [];
|
|
6395
|
-
const toolCalls = Array.isArray(parsed.tool_calls) ? parsed.tool_calls : [];
|
|
6396
|
-
if (role === "assistant") {
|
|
6397
|
-
const thinking = contentBlocks.filter((b) => b.type === "think" && typeof b.think === "string").map((b) => b.think).join("");
|
|
6398
|
-
if (thinking) {
|
|
6399
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6400
|
-
void presenter.onThinking(thinking);
|
|
6401
|
-
}
|
|
6402
|
-
if (toolCalls.length > 0) {
|
|
6403
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6404
|
-
for (const tc of toolCalls) {
|
|
6405
|
-
const fn = typeof tc.function === "object" && tc.function !== null ? tc.function : {};
|
|
6406
|
-
const toolId = typeof tc.id === "string" ? tc.id : `kimi-tool-${Date.now()}`;
|
|
6407
|
-
const toolName = typeof fn.name === "string" ? fn.name : "Tool";
|
|
6408
|
-
let args = {};
|
|
6409
|
-
if (typeof fn.arguments === "string") {
|
|
6410
|
-
try {
|
|
6411
|
-
args = JSON.parse(fn.arguments);
|
|
6412
|
-
} catch {
|
|
6413
|
-
}
|
|
6414
|
-
} else if (typeof fn.arguments === "object" && fn.arguments !== null) {
|
|
6415
|
-
args = fn.arguments;
|
|
6416
|
-
}
|
|
6417
|
-
void presenter.onToolUse(toolName, args, toolId);
|
|
6418
|
-
}
|
|
6419
|
-
} else {
|
|
6420
|
-
const text = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text).join("");
|
|
6421
|
-
if (text) {
|
|
6422
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
6423
|
-
state.summary += text;
|
|
6424
|
-
void presenter.onAssistantText(text);
|
|
6425
|
-
}
|
|
6426
|
-
}
|
|
6427
|
-
return;
|
|
6428
|
-
}
|
|
6429
|
-
if (role === "tool") {
|
|
6430
|
-
const toolCallId = typeof parsed.tool_call_id === "string" ? parsed.tool_call_id : `kimi-tool-${Date.now()}`;
|
|
6431
|
-
const allText = contentBlocks.filter((b) => b.type === "text" && typeof b.text === "string").map((b) => b.text);
|
|
6432
|
-
const userText = allText.filter((t) => !t.startsWith("<system>")).join("\n");
|
|
6433
|
-
void presenter.onToolResult?.(toolCallId, userText || allText.join("\n"));
|
|
6434
|
-
return;
|
|
6435
|
-
}
|
|
6436
|
-
}
|
|
6437
|
-
function createKimiCliBackend(command = "kimi", defaultArgs = []) {
|
|
6438
|
-
return createGenericCliBackend({
|
|
6439
|
-
kind: "kimi_cli",
|
|
6440
|
-
supportTier: "structured",
|
|
6441
|
-
command,
|
|
6442
|
-
args: [],
|
|
6443
|
-
buildArgs: (context, prompt) => {
|
|
6444
|
-
const args = [
|
|
6445
|
-
"--print",
|
|
6446
|
-
"--output-format",
|
|
6447
|
-
"stream-json",
|
|
6448
|
-
"--work-dir",
|
|
6449
|
-
context.cwd
|
|
6450
|
-
];
|
|
6451
|
-
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
6452
|
-
if (resumeId) args.push("--session", resumeId);
|
|
6453
|
-
if (shouldEnableKimiCliThinking(context.config.selectedModel)) {
|
|
6454
|
-
args.push("--thinking");
|
|
6455
|
-
}
|
|
6456
|
-
const model = resolveKimiCliModelArg(context.config.selectedModel);
|
|
6457
|
-
if (model) args.push("--model", model);
|
|
6458
|
-
return [...args, ...defaultArgs, "--prompt", prompt];
|
|
6459
|
-
},
|
|
6460
|
-
augmentPrompt: (context) => {
|
|
6461
|
-
const base = buildPromptWithSystem(context.config, context.promptText);
|
|
6462
|
-
return context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
6463
|
-
},
|
|
6464
|
-
parseStructuredLine: parseKimiStructuredLine
|
|
6465
|
-
});
|
|
6466
|
-
}
|
|
6467
|
-
function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
|
|
6468
|
-
return createGenericCliBackend({
|
|
6469
|
-
kind: "gemini_cli",
|
|
6470
|
-
supportTier: "structured",
|
|
6471
|
-
command,
|
|
6472
|
-
args: [],
|
|
6473
|
-
buildArgs: (context) => {
|
|
6474
|
-
const args = ["-p", "", "--output-format", "stream-json"];
|
|
6475
|
-
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
6476
|
-
const approvalMode = context.config.mode === "plan" || context.config.mode === "ask" || context.config.mode === "review" ? "plan" : "yolo";
|
|
6477
|
-
args.push("--approval-mode", approvalMode);
|
|
6478
|
-
if (resumeId) args.push("--resume", resumeId);
|
|
6479
|
-
if (context.config.selectedModel?.trim()) args.push("--model", context.config.selectedModel.trim());
|
|
6480
|
-
return [...args, ...defaultArgs];
|
|
6481
|
-
},
|
|
6482
|
-
promptViaStdin: true,
|
|
6483
|
-
augmentPrompt: (context) => {
|
|
6484
|
-
const basePrompt = buildPromptWithSystem(context.config, context.promptText);
|
|
6485
|
-
return context.config.mode === "plan" ? buildPlanModePrefix(basePrompt) : basePrompt;
|
|
6486
|
-
},
|
|
6487
|
-
parseStructuredLine: parseGeminiStructuredLine
|
|
6488
|
-
});
|
|
6489
|
-
}
|
|
6490
|
-
function isCursorAgentCliModelId(model) {
|
|
6491
|
-
return model === "auto" || /^composer-/.test(model) || /^gpt-\d/.test(model) || /^claude-\d/.test(model) || /^gemini-\d/.test(model) || /^grok-\d/.test(model) || /^kimi-/.test(model);
|
|
6492
|
-
}
|
|
6493
|
-
function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = []) {
|
|
6494
|
-
return createGenericCliBackend({
|
|
6495
|
-
kind: "cursor_agent_cli",
|
|
6496
|
-
supportTier: "structured",
|
|
6497
|
-
command,
|
|
6498
|
-
args: [],
|
|
6499
|
-
buildArgs: (context, prompt) => {
|
|
6500
|
-
const args = ["--print", "--output-format", "stream-json"];
|
|
6501
|
-
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
6502
|
-
if (shouldForceCursorAgent(context.config)) args.push("--force");
|
|
6503
|
-
if (resumeId) args.push("--resume", resumeId);
|
|
6504
|
-
const model = context.config.selectedModel?.trim();
|
|
6505
|
-
if (model && isCursorAgentCliModelId(model)) args.push("--model", model);
|
|
6506
|
-
return [...args, ...defaultArgs, prompt];
|
|
6507
|
-
},
|
|
6508
|
-
augmentPrompt: (context) => {
|
|
6509
|
-
const base = buildPromptWithSystem(context.config, context.promptText);
|
|
6510
|
-
return context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
6511
|
-
},
|
|
6512
|
-
parseStructuredLine: parseCursorStructuredLine
|
|
6513
|
-
});
|
|
6514
|
-
}
|
|
6515
|
-
function droidExecPermissionArgs(mode) {
|
|
6516
|
-
if (mode === "plan" || mode === "ask" || mode === "review") return [];
|
|
6517
|
-
return ["--auto", "high"];
|
|
6475
|
+
if (resumeId) args.push("--resume", resumeId);
|
|
6476
|
+
const model = ctx.config.selectedModel?.trim();
|
|
6477
|
+
if (model && isCursorAgentCliModelId(model)) args.push("--model", model);
|
|
6478
|
+
return [...args, ...defaultArgs, prompt];
|
|
6479
|
+
},
|
|
6480
|
+
keepStdinOpen: true,
|
|
6481
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
6482
|
+
parseStructuredLine: parseCursorStructuredLine
|
|
6483
|
+
}).run(context);
|
|
6484
|
+
} finally {
|
|
6485
|
+
cleanup();
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
};
|
|
6518
6489
|
}
|
|
6519
6490
|
function parseDroidStructuredLine(line, context, state) {
|
|
6520
6491
|
const presenter = context.presenter;
|
|
@@ -6596,33 +6567,45 @@ function parseDroidStructuredLine(line, context, state) {
|
|
|
6596
6567
|
break;
|
|
6597
6568
|
}
|
|
6598
6569
|
}
|
|
6570
|
+
function droidExecPermissionArgs(mode) {
|
|
6571
|
+
if (mode === "plan" || mode === "ask" || mode === "review") return [];
|
|
6572
|
+
return ["--auto", "high"];
|
|
6573
|
+
}
|
|
6599
6574
|
function createDroidCliBackend(command = "droid", defaultArgs = []) {
|
|
6600
|
-
return
|
|
6575
|
+
return {
|
|
6601
6576
|
kind: "droid_cli",
|
|
6602
6577
|
supportTier: "structured",
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6578
|
+
async run(context) {
|
|
6579
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6580
|
+
try {
|
|
6581
|
+
return await createGenericCliBackend({
|
|
6582
|
+
kind: "droid_cli",
|
|
6583
|
+
supportTier: "structured",
|
|
6584
|
+
command,
|
|
6585
|
+
args: [],
|
|
6586
|
+
buildArgs: (ctx, prompt) => {
|
|
6587
|
+
const args = [
|
|
6588
|
+
"exec",
|
|
6589
|
+
"--output-format",
|
|
6590
|
+
"stream-json",
|
|
6591
|
+
...droidExecPermissionArgs(ctx.config.mode),
|
|
6592
|
+
...defaultArgs
|
|
6593
|
+
];
|
|
6594
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6595
|
+
if (resumeId) args.push("--session-id", resumeId);
|
|
6596
|
+
const model = ctx.config.selectedModel?.trim();
|
|
6597
|
+
if (model) args.push("-m", model);
|
|
6598
|
+
args.push(prompt);
|
|
6599
|
+
return args;
|
|
6600
|
+
},
|
|
6601
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
6602
|
+
parseStructuredLine: parseDroidStructuredLine
|
|
6603
|
+
}).run(context);
|
|
6604
|
+
} finally {
|
|
6605
|
+
cleanup();
|
|
6606
|
+
}
|
|
6607
|
+
}
|
|
6608
|
+
};
|
|
6626
6609
|
}
|
|
6627
6610
|
function parseOpencodeStructuredLine(line, context, state) {
|
|
6628
6611
|
const presenter = context.presenter;
|
|
@@ -6719,51 +6702,247 @@ function parseOpencodeStructuredLine(line, context, state) {
|
|
|
6719
6702
|
break;
|
|
6720
6703
|
}
|
|
6721
6704
|
}
|
|
6722
|
-
function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
6723
|
-
return {
|
|
6724
|
-
kind: "opencode_cli",
|
|
6725
|
-
supportTier: "structured",
|
|
6726
|
-
async run(context) {
|
|
6727
|
-
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images);
|
|
6728
|
-
try {
|
|
6729
|
-
return await createGenericCliBackend({
|
|
6730
|
-
kind: "opencode_cli",
|
|
6731
|
-
supportTier: "structured",
|
|
6732
|
-
command,
|
|
6733
|
-
args: [],
|
|
6734
|
-
buildArgs: (ctx, prompt) => {
|
|
6735
|
-
const args = ["run", "--format", "json"];
|
|
6736
|
-
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6737
|
-
if (resumeId) args.push("--session", resumeId);
|
|
6738
|
-
const model = ctx.config.selectedModel?.trim();
|
|
6739
|
-
if (model && model.includes("/")) args.push("--model", model);
|
|
6740
|
-
const fileArgs = imagePaths.flatMap((p) => ["--file", p]);
|
|
6741
|
-
if (fileArgs.length > 0) {
|
|
6742
|
-
return [...args, ...fileArgs, ...defaultArgs, "--", prompt];
|
|
6743
|
-
}
|
|
6744
|
-
return [...args, ...defaultArgs, prompt];
|
|
6745
|
-
},
|
|
6746
|
-
augmentPrompt: (ctx) => {
|
|
6747
|
-
const base = buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6748
|
-
return ctx.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
6749
|
-
},
|
|
6750
|
-
parseStructuredLine: parseOpencodeStructuredLine
|
|
6751
|
-
}).run(context);
|
|
6752
|
-
} finally {
|
|
6753
|
-
cleanup();
|
|
6754
|
-
}
|
|
6755
|
-
}
|
|
6756
|
-
};
|
|
6757
|
-
}
|
|
6758
|
-
function createGenericCliPassthroughBackend(command, defaultArgs = []) {
|
|
6759
|
-
return
|
|
6760
|
-
kind: "generic_cli",
|
|
6761
|
-
supportTier: "text",
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6705
|
+
function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
6706
|
+
return {
|
|
6707
|
+
kind: "opencode_cli",
|
|
6708
|
+
supportTier: "structured",
|
|
6709
|
+
async run(context) {
|
|
6710
|
+
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6711
|
+
try {
|
|
6712
|
+
return await createGenericCliBackend({
|
|
6713
|
+
kind: "opencode_cli",
|
|
6714
|
+
supportTier: "structured",
|
|
6715
|
+
command,
|
|
6716
|
+
args: [],
|
|
6717
|
+
buildArgs: (ctx, prompt) => {
|
|
6718
|
+
const args = ["run", "--format", "json"];
|
|
6719
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6720
|
+
if (resumeId) args.push("--session", resumeId);
|
|
6721
|
+
const model = ctx.config.selectedModel?.trim();
|
|
6722
|
+
if (model && model.includes("/")) args.push("--model", model);
|
|
6723
|
+
const fileArgs = imagePaths.flatMap((p) => ["--file", p]);
|
|
6724
|
+
if (fileArgs.length > 0) {
|
|
6725
|
+
return [...args, ...fileArgs, ...defaultArgs, "--", prompt];
|
|
6726
|
+
}
|
|
6727
|
+
return [...args, ...defaultArgs, prompt];
|
|
6728
|
+
},
|
|
6729
|
+
augmentPrompt: (ctx) => {
|
|
6730
|
+
const base = buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6731
|
+
return ctx.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
6732
|
+
},
|
|
6733
|
+
parseStructuredLine: parseOpencodeStructuredLine
|
|
6734
|
+
}).run(context);
|
|
6735
|
+
} finally {
|
|
6736
|
+
cleanup();
|
|
6737
|
+
}
|
|
6738
|
+
}
|
|
6739
|
+
};
|
|
6740
|
+
}
|
|
6741
|
+
function createGenericCliPassthroughBackend(command, defaultArgs = []) {
|
|
6742
|
+
return {
|
|
6743
|
+
kind: "generic_cli",
|
|
6744
|
+
supportTier: "text",
|
|
6745
|
+
async run(context) {
|
|
6746
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6747
|
+
try {
|
|
6748
|
+
return await createGenericCliBackend({
|
|
6749
|
+
kind: "generic_cli",
|
|
6750
|
+
supportTier: "text",
|
|
6751
|
+
command,
|
|
6752
|
+
args: defaultArgs,
|
|
6753
|
+
promptViaStdin: true,
|
|
6754
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles)
|
|
6755
|
+
}).run(context);
|
|
6756
|
+
} finally {
|
|
6757
|
+
cleanup();
|
|
6758
|
+
}
|
|
6759
|
+
}
|
|
6760
|
+
};
|
|
6761
|
+
}
|
|
6762
|
+
function parseCodexStructuredLine(line, context, state) {
|
|
6763
|
+
const presenter = context.presenter;
|
|
6764
|
+
let parsed = null;
|
|
6765
|
+
try {
|
|
6766
|
+
parsed = JSON.parse(line);
|
|
6767
|
+
} catch {
|
|
6768
|
+
void presenter.onLog(`[codex] ${line}`);
|
|
6769
|
+
return;
|
|
6770
|
+
}
|
|
6771
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
6772
|
+
if (!type) return;
|
|
6773
|
+
if (typeof parsed.thread_id === "string") {
|
|
6774
|
+
state.runtimeSessionId = parsed.thread_id;
|
|
6775
|
+
}
|
|
6776
|
+
const extractCodexErrorMessage = (value2) => {
|
|
6777
|
+
if (typeof value2 !== "string") return "";
|
|
6778
|
+
const trimmed = value2.trim();
|
|
6779
|
+
if (!trimmed) return "";
|
|
6780
|
+
try {
|
|
6781
|
+
const parsedValue = JSON.parse(trimmed);
|
|
6782
|
+
if (typeof parsedValue.detail === "string" && parsedValue.detail.trim().length > 0) {
|
|
6783
|
+
return parsedValue.detail.trim();
|
|
6784
|
+
}
|
|
6785
|
+
if (typeof parsedValue.message === "string" && parsedValue.message.trim().length > 0) {
|
|
6786
|
+
return parsedValue.message.trim();
|
|
6787
|
+
}
|
|
6788
|
+
} catch {
|
|
6789
|
+
}
|
|
6790
|
+
return trimmed;
|
|
6791
|
+
};
|
|
6792
|
+
switch (type) {
|
|
6793
|
+
case "thread.started":
|
|
6794
|
+
break;
|
|
6795
|
+
case "turn.started":
|
|
6796
|
+
state.iterations += 1;
|
|
6797
|
+
break;
|
|
6798
|
+
case "session_configured":
|
|
6799
|
+
if (typeof parsed.session_id === "string") {
|
|
6800
|
+
state.runtimeSessionId = parsed.session_id;
|
|
6801
|
+
}
|
|
6802
|
+
break;
|
|
6803
|
+
case "task_started":
|
|
6804
|
+
state.iterations += 1;
|
|
6805
|
+
break;
|
|
6806
|
+
case "item.started": {
|
|
6807
|
+
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
6808
|
+
if (!item || item.type !== "command_execution" || typeof item.id !== "string") break;
|
|
6809
|
+
const command = typeof item.command === "string" ? item.command : "";
|
|
6810
|
+
void presenter.onToolUse("Bash", { command, cwd: context.cwd }, item.id);
|
|
6811
|
+
break;
|
|
6812
|
+
}
|
|
6813
|
+
case "item.completed": {
|
|
6814
|
+
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
6815
|
+
if (!item || typeof item.type !== "string") break;
|
|
6816
|
+
if (item.type === "reasoning" && typeof item.text === "string") {
|
|
6817
|
+
void presenter.onThinking(item.text);
|
|
6818
|
+
break;
|
|
6819
|
+
}
|
|
6820
|
+
if (item.type === "agent_message" && typeof item.text === "string") {
|
|
6821
|
+
state.summary += `${item.text}
|
|
6822
|
+
`;
|
|
6823
|
+
void presenter.onAssistantText(item.text);
|
|
6824
|
+
break;
|
|
6825
|
+
}
|
|
6826
|
+
if (item.type === "command_execution" && typeof item.id === "string") {
|
|
6827
|
+
const output = typeof item.aggregated_output === "string" ? item.aggregated_output : "";
|
|
6828
|
+
const itemExitCode = typeof item.exit_code === "number" ? item.exit_code : null;
|
|
6829
|
+
const resultText = output.trim().length > 0 ? output : itemExitCode === null ? "" : `Exit code: ${itemExitCode}`;
|
|
6830
|
+
void presenter.onToolResult?.(item.id, resultText);
|
|
6831
|
+
break;
|
|
6832
|
+
}
|
|
6833
|
+
break;
|
|
6834
|
+
}
|
|
6835
|
+
case "agent_message_delta":
|
|
6836
|
+
case "agent_message_content_delta": {
|
|
6837
|
+
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
6838
|
+
if (!delta) return;
|
|
6839
|
+
state.summary += delta;
|
|
6840
|
+
void presenter.onAssistantText(delta);
|
|
6841
|
+
break;
|
|
6842
|
+
}
|
|
6843
|
+
case "agent_reasoning_delta":
|
|
6844
|
+
case "reasoning_content_delta":
|
|
6845
|
+
case "agent_reasoning_raw_content_delta": {
|
|
6846
|
+
const delta = typeof parsed.delta === "string" ? parsed.delta : "";
|
|
6847
|
+
if (!delta) return;
|
|
6848
|
+
void presenter.onThinking(delta);
|
|
6849
|
+
break;
|
|
6850
|
+
}
|
|
6851
|
+
case "exec_command_begin": {
|
|
6852
|
+
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `exec-${Date.now()}`;
|
|
6853
|
+
const command = Array.isArray(parsed.command) ? parsed.command.join(" ") : "";
|
|
6854
|
+
const cwd = typeof parsed.cwd === "string" ? parsed.cwd : context.cwd;
|
|
6855
|
+
void presenter.onToolUse("Bash", { command, cwd }, toolId);
|
|
6856
|
+
break;
|
|
6857
|
+
}
|
|
6858
|
+
case "exec_command_end": {
|
|
6859
|
+
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `exec-${Date.now()}`;
|
|
6860
|
+
const output = typeof parsed.formatted_output === "string" ? parsed.formatted_output : typeof parsed.aggregated_output === "string" ? parsed.aggregated_output : "";
|
|
6861
|
+
void presenter.onToolResult?.(toolId, output);
|
|
6862
|
+
break;
|
|
6863
|
+
}
|
|
6864
|
+
case "mcp_tool_call_begin": {
|
|
6865
|
+
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `mcp-${Date.now()}`;
|
|
6866
|
+
const invocation = typeof parsed.invocation === "object" && parsed.invocation !== null ? parsed.invocation : {};
|
|
6867
|
+
const tool = typeof invocation.tool_name === "string" ? invocation.tool_name : typeof invocation.tool === "string" ? invocation.tool : "MCP Tool";
|
|
6868
|
+
void presenter.onToolUse(tool, invocation, toolId);
|
|
6869
|
+
break;
|
|
6870
|
+
}
|
|
6871
|
+
case "mcp_tool_call_end": {
|
|
6872
|
+
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `mcp-${Date.now()}`;
|
|
6873
|
+
void presenter.onToolResult?.(toolId, JSON.stringify(parsed.result ?? {}, null, 2));
|
|
6874
|
+
break;
|
|
6875
|
+
}
|
|
6876
|
+
case "token_count": {
|
|
6877
|
+
const info = typeof parsed.info === "object" && parsed.info !== null ? parsed.info : {};
|
|
6878
|
+
state.usage = {
|
|
6879
|
+
model: typeof info.model === "string" ? info.model : "codex",
|
|
6880
|
+
numTurns: Math.max(state.iterations, 1),
|
|
6881
|
+
durationMs: 0,
|
|
6882
|
+
inputTokens: typeof info.input_tokens === "number" ? info.input_tokens : 0,
|
|
6883
|
+
outputTokens: typeof info.output_tokens === "number" ? info.output_tokens : 0,
|
|
6884
|
+
cacheReadTokens: typeof info.cached_input_tokens === "number" ? info.cached_input_tokens : 0,
|
|
6885
|
+
cacheCreationTokens: 0,
|
|
6886
|
+
costUsd: typeof info.total_cost_usd === "number" ? info.total_cost_usd : 0
|
|
6887
|
+
};
|
|
6888
|
+
void presenter.onUsageUpdate?.({
|
|
6889
|
+
model: state.usage.model,
|
|
6890
|
+
inputTokens: state.usage.inputTokens,
|
|
6891
|
+
outputTokens: state.usage.outputTokens,
|
|
6892
|
+
cacheReadTokens: state.usage.cacheReadTokens,
|
|
6893
|
+
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
6894
|
+
});
|
|
6895
|
+
break;
|
|
6896
|
+
}
|
|
6897
|
+
case "task_complete": {
|
|
6898
|
+
const lastMessage = typeof parsed.last_agent_message === "string" ? parsed.last_agent_message : "";
|
|
6899
|
+
if (lastMessage.length > 0) state.summary = lastMessage;
|
|
6900
|
+
break;
|
|
6901
|
+
}
|
|
6902
|
+
case "turn.completed": {
|
|
6903
|
+
const usage = typeof parsed.usage === "object" && parsed.usage !== null ? parsed.usage : {};
|
|
6904
|
+
state.usage = {
|
|
6905
|
+
model: typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "codex",
|
|
6906
|
+
numTurns: Math.max(state.iterations, 1),
|
|
6907
|
+
durationMs: 0,
|
|
6908
|
+
inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
6909
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
|
|
6910
|
+
cacheReadTokens: typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : 0,
|
|
6911
|
+
cacheCreationTokens: 0,
|
|
6912
|
+
costUsd: 0
|
|
6913
|
+
};
|
|
6914
|
+
void presenter.onUsageUpdate?.({
|
|
6915
|
+
model: state.usage.model,
|
|
6916
|
+
inputTokens: state.usage.inputTokens,
|
|
6917
|
+
outputTokens: state.usage.outputTokens,
|
|
6918
|
+
cacheReadTokens: state.usage.cacheReadTokens,
|
|
6919
|
+
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
6920
|
+
});
|
|
6921
|
+
break;
|
|
6922
|
+
}
|
|
6923
|
+
case "error": {
|
|
6924
|
+
const message = extractCodexErrorMessage(parsed.message) || extractCodexErrorMessage(parsed.error) || typeof parsed.detail === "string" && parsed.detail.trim() || "";
|
|
6925
|
+
if (message) {
|
|
6926
|
+
const normalizedMessage = normalizeCodexCliErrorMessage(message);
|
|
6927
|
+
void presenter.onError(normalizedMessage);
|
|
6928
|
+
state.error = normalizedMessage;
|
|
6929
|
+
}
|
|
6930
|
+
break;
|
|
6931
|
+
}
|
|
6932
|
+
case "turn.failed": {
|
|
6933
|
+
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : {};
|
|
6934
|
+
const message = extractCodexErrorMessage(error.message) || extractCodexErrorMessage(error.detail) || typeof parsed.message === "string" && parsed.message.trim() || "";
|
|
6935
|
+
if (message) {
|
|
6936
|
+
const normalizedMessage = normalizeCodexCliErrorMessage(message);
|
|
6937
|
+
void presenter.onError(normalizedMessage);
|
|
6938
|
+
state.error = normalizedMessage;
|
|
6939
|
+
}
|
|
6940
|
+
break;
|
|
6941
|
+
}
|
|
6942
|
+
default:
|
|
6943
|
+
break;
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6767
6946
|
function buildCodexEffortArgs(selectedEffortLevel) {
|
|
6768
6947
|
const level = selectedEffortLevel?.trim();
|
|
6769
6948
|
if (!level) return [];
|
|
@@ -6774,7 +6953,7 @@ function createCodexRuntimeBackend(command = "codex", defaultArgs = []) {
|
|
|
6774
6953
|
kind: "codex_app_server",
|
|
6775
6954
|
supportTier: "structured",
|
|
6776
6955
|
async run(context) {
|
|
6777
|
-
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images);
|
|
6956
|
+
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
6778
6957
|
try {
|
|
6779
6958
|
return await createGenericCliBackend({
|
|
6780
6959
|
kind: "codex_app_server",
|
|
@@ -6785,7 +6964,7 @@ function createCodexRuntimeBackend(command = "codex", defaultArgs = []) {
|
|
|
6785
6964
|
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6786
6965
|
const modelArgs = ctx.config.selectedModel?.trim() ? ["--model", ctx.config.selectedModel.trim()] : [];
|
|
6787
6966
|
const effortArgs = buildCodexEffortArgs(ctx.config.selectedEffortLevel);
|
|
6788
|
-
const permissionArgs =
|
|
6967
|
+
const permissionArgs = ["--dangerously-bypass-approvals-and-sandbox"];
|
|
6789
6968
|
const imageArgs = imagePaths.flatMap((p) => ["--image", p]);
|
|
6790
6969
|
const baseArgs = resumeId ? [...permissionArgs, "exec", "resume", "--json", "--skip-git-repo-check", ...modelArgs, ...effortArgs, ...imageArgs, resumeId, "-"] : [...permissionArgs, "exec", "--json", "--skip-git-repo-check", ...modelArgs, ...effortArgs, ...imageArgs, "-"];
|
|
6791
6970
|
return [...baseArgs, ...defaultArgs];
|
|
@@ -6881,51 +7060,67 @@ function parseCopilotStructuredLine(line, context, state) {
|
|
|
6881
7060
|
}
|
|
6882
7061
|
}
|
|
6883
7062
|
function createCopilotCliBackend(command = "copilot", defaultArgs = []) {
|
|
6884
|
-
return
|
|
7063
|
+
return {
|
|
6885
7064
|
kind: "copilot_cli",
|
|
6886
7065
|
supportTier: "structured",
|
|
6887
|
-
|
|
6888
|
-
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
7066
|
+
async run(context) {
|
|
7067
|
+
const { files: imageFiles, cleanup } = writeImagesToTempFiles(context.config.images, context.cwd);
|
|
7068
|
+
try {
|
|
7069
|
+
return await createGenericCliBackend({
|
|
7070
|
+
kind: "copilot_cli",
|
|
7071
|
+
supportTier: "structured",
|
|
7072
|
+
command,
|
|
7073
|
+
args: [],
|
|
7074
|
+
buildArgs: (ctx, prompt) => {
|
|
7075
|
+
const args = [
|
|
7076
|
+
"--autopilot",
|
|
7077
|
+
"--yolo",
|
|
7078
|
+
"--max-autopilot-continues",
|
|
7079
|
+
"20",
|
|
7080
|
+
"-s",
|
|
7081
|
+
"--stream",
|
|
7082
|
+
"on",
|
|
7083
|
+
"--output-format",
|
|
7084
|
+
"json",
|
|
7085
|
+
"-p",
|
|
7086
|
+
prompt
|
|
7087
|
+
];
|
|
7088
|
+
if (ctx.config.selectedModel?.trim()) {
|
|
7089
|
+
args.push("--model", ctx.config.selectedModel.trim());
|
|
7090
|
+
}
|
|
7091
|
+
return [...args, ...defaultArgs];
|
|
7092
|
+
},
|
|
7093
|
+
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
7094
|
+
parseStructuredLine: parseCopilotStructuredLine
|
|
7095
|
+
}).run(context);
|
|
7096
|
+
} finally {
|
|
7097
|
+
cleanup();
|
|
6905
7098
|
}
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
parseStructuredLine: parseCopilotStructuredLine
|
|
6909
|
-
});
|
|
7099
|
+
}
|
|
7100
|
+
};
|
|
6910
7101
|
}
|
|
6911
7102
|
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
6912
7103
|
var DROID_DEFAULT_MODEL = "claude-opus-4-7";
|
|
6913
7104
|
function defaultSelectedModelForBackend(backendKind) {
|
|
6914
7105
|
switch (backendKind) {
|
|
7106
|
+
case "claude_cli":
|
|
7107
|
+
return void 0;
|
|
6915
7108
|
case "droid_cli":
|
|
6916
7109
|
return DROID_DEFAULT_MODEL;
|
|
6917
7110
|
case "copilot_cli":
|
|
6918
7111
|
return "claude-sonnet-4.5";
|
|
6919
7112
|
case "kimi_cli":
|
|
6920
7113
|
return void 0;
|
|
7114
|
+
case "supatest_cli":
|
|
7115
|
+
return "medium";
|
|
6921
7116
|
default:
|
|
6922
7117
|
return DEFAULT_MODEL;
|
|
6923
7118
|
}
|
|
6924
7119
|
}
|
|
6925
7120
|
function canonicalizePath(inputPath) {
|
|
6926
7121
|
try {
|
|
6927
|
-
const resolved = (0,
|
|
6928
|
-
return (0,
|
|
7122
|
+
const resolved = (0, import_path3.resolve)(inputPath);
|
|
7123
|
+
return (0, import_fs4.existsSync)(resolved) ? (0, import_fs4.realpathSync)(resolved) : resolved;
|
|
6929
7124
|
} catch {
|
|
6930
7125
|
return null;
|
|
6931
7126
|
}
|
|
@@ -6938,6 +7133,28 @@ function buildPromptWithFiles(task, files) {
|
|
|
6938
7133
|
The user has attached the following files. You can read them using your available tools:
|
|
6939
7134
|
${fileList}`;
|
|
6940
7135
|
}
|
|
7136
|
+
function buildTaskLifecyclePrompt(config) {
|
|
7137
|
+
if (config.mode === "plan" || config.mode === "ask" || config.mode === "review") {
|
|
7138
|
+
return void 0;
|
|
7139
|
+
}
|
|
7140
|
+
const taskId = config.taskId ?? config.taskMeta?.id;
|
|
7141
|
+
if (!taskId) return void 0;
|
|
7142
|
+
const statusLines = config.taskMeta?.statusOptions?.length ? [
|
|
7143
|
+
"",
|
|
7144
|
+
"Available task statuses for this task, in board order:",
|
|
7145
|
+
...config.taskMeta.statusOptions.map(
|
|
7146
|
+
(status) => `- ${status.id}: ${status.label} (${status.group})`
|
|
7147
|
+
)
|
|
7148
|
+
] : [];
|
|
7149
|
+
return [
|
|
7150
|
+
"## Task Lifecycle",
|
|
7151
|
+
`This session is attached to taskId "${taskId}".`,
|
|
7152
|
+
`Do not change the task status on your own. The user manages this task's status. Only change it by calling the Aiden MCP update_task tool when the user explicitly asks you to (for example: "move this to review" or "mark it done"). Do not update the status proactively as you move through research, implementation, testing, or review phases.`,
|
|
7153
|
+
...statusLines,
|
|
7154
|
+
"",
|
|
7155
|
+
"When the user asks you to change the status, use exact status IDs from the available statuses. Do not invent status IDs. If none of the available statuses clearly matches the request, ask which one they mean instead of guessing."
|
|
7156
|
+
].join("\n");
|
|
7157
|
+
}
|
|
6941
7158
|
var BaseMachineAgent = class _BaseMachineAgent {
|
|
6942
7159
|
static MAX_RETRIES = 2;
|
|
6943
7160
|
static RETRY_BASE_DELAY_MS = 3e3;
|
|
@@ -7018,6 +7235,8 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
7018
7235
|
case "gemini_cli":
|
|
7019
7236
|
case "opencode_cli":
|
|
7020
7237
|
case "copilot_cli":
|
|
7238
|
+
case "supatest_cli":
|
|
7239
|
+
case "kimi_cli":
|
|
7021
7240
|
return "structured";
|
|
7022
7241
|
case "generic_cli":
|
|
7023
7242
|
default:
|
|
@@ -7042,6 +7261,8 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
7042
7261
|
Prefer relative paths and keep your work scoped to this project.`
|
|
7043
7262
|
);
|
|
7044
7263
|
}
|
|
7264
|
+
const lifecyclePrompt = buildTaskLifecyclePrompt(config);
|
|
7265
|
+
if (lifecyclePrompt) systemPromptParts.push(lifecyclePrompt);
|
|
7045
7266
|
const extra = this.buildExtraSystemPromptParts(config);
|
|
7046
7267
|
systemPromptParts.push(...extra);
|
|
7047
7268
|
return {
|
|
@@ -7056,9 +7277,9 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
7056
7277
|
}
|
|
7057
7278
|
async runCliBackend(config, safeProjectPath) {
|
|
7058
7279
|
const backendKind = this.resolveBackendKind(config);
|
|
7059
|
-
const defaultCwd = (0,
|
|
7060
|
-
if (!(0,
|
|
7061
|
-
(0,
|
|
7280
|
+
const defaultCwd = (0, import_path3.join)((0, import_os3.homedir)(), "Documents", "aiden");
|
|
7281
|
+
if (!(0, import_fs4.existsSync)(defaultCwd)) {
|
|
7282
|
+
(0, import_fs4.mkdirSync)(defaultCwd, { recursive: true });
|
|
7062
7283
|
}
|
|
7063
7284
|
let runtimeConfig = this.buildRuntimeConfig(config, safeProjectPath);
|
|
7064
7285
|
const cwd = runtimeConfig.worktreePath || safeProjectPath || runtimeConfig.cwd || defaultCwd;
|
|
@@ -7066,7 +7287,7 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
7066
7287
|
const promptText = this.buildPromptText(runtimeConfig);
|
|
7067
7288
|
const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
|
|
7068
7289
|
const runtimeArgs = runtimeConfig.runtimeArgs || this.runtime.runtimeArgs || [];
|
|
7069
|
-
const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "gemini", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi", runtimeArgs) : createGenericCliPassthroughBackend(runtimeCommand || "generic-cli", runtimeArgs);
|
|
7290
|
+
const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "gemini", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi", runtimeArgs) : backendKind === "supatest_cli" ? createSupatestCliBackend(runtimeCommand || "supatest", runtimeArgs) : createGenericCliPassthroughBackend(runtimeCommand || "generic-cli", runtimeArgs);
|
|
7070
7291
|
let lastResult = null;
|
|
7071
7292
|
for (let attempt = 0; attempt <= _BaseMachineAgent.MAX_RETRIES; attempt++) {
|
|
7072
7293
|
if (this.abortController?.signal.aborted) break;
|
|
@@ -7162,7 +7383,7 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
7162
7383
|
const canonicalized = canonicalizePath(initialConfig.teamPath);
|
|
7163
7384
|
if (canonicalized) safeProjectPath = canonicalized;
|
|
7164
7385
|
}
|
|
7165
|
-
if (safeProjectPath && !(0,
|
|
7386
|
+
if (safeProjectPath && !(0, import_fs4.existsSync)(safeProjectPath)) {
|
|
7166
7387
|
const message = `Local path no longer exists: "${safeProjectPath}". Please update the team's local path in team settings before running the agent.`;
|
|
7167
7388
|
this.presenter.onError(message, true);
|
|
7168
7389
|
const result = {
|
|
@@ -7207,8 +7428,9 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
7207
7428
|
var import_node_os2 = require("os");
|
|
7208
7429
|
|
|
7209
7430
|
// src/cli-executable.ts
|
|
7210
|
-
var import_node_fs = require("fs");
|
|
7211
7431
|
var import_node_child_process = require("child_process");
|
|
7432
|
+
var import_node_fs = require("fs");
|
|
7433
|
+
var import_node_child_process2 = require("child_process");
|
|
7212
7434
|
var import_node_os = require("os");
|
|
7213
7435
|
var import_node_path = require("path");
|
|
7214
7436
|
var PROVIDER_CLI_COMMANDS = {
|
|
@@ -7219,7 +7441,8 @@ var PROVIDER_CLI_COMMANDS = {
|
|
|
7219
7441
|
gemini_cli: { provider: "gemini_cli", command: "gemini", envVar: "AIDEN_GEMINI_PATH" },
|
|
7220
7442
|
kimi_cli: { provider: "kimi_cli", command: "kimi", envVar: "AIDEN_KIMI_PATH" },
|
|
7221
7443
|
opencode_cli: { provider: "opencode_cli", command: "opencode", envVar: "AIDEN_OPENCODE_PATH" },
|
|
7222
|
-
droid_cli: { provider: "droid_cli", command: "droid", envVar: "AIDEN_DROID_PATH" }
|
|
7444
|
+
droid_cli: { provider: "droid_cli", command: "droid", envVar: "AIDEN_DROID_PATH" },
|
|
7445
|
+
supatest_cli: { provider: "supatest_cli", command: "supatest", envVar: "AIDEN_SUPATEST_PATH" }
|
|
7223
7446
|
};
|
|
7224
7447
|
var DISCOVERABLE_PROVIDER_KINDS = Object.keys(PROVIDER_CLI_COMMANDS);
|
|
7225
7448
|
var PROVIDER_ALIASES = {
|
|
@@ -7236,11 +7459,50 @@ var BACKEND_CLI_COMMANDS = {
|
|
|
7236
7459
|
gemini: "gemini",
|
|
7237
7460
|
kimi_cli: "kimi",
|
|
7238
7461
|
opencode_cli: "opencode",
|
|
7239
|
-
droid_cli: "droid"
|
|
7462
|
+
droid_cli: "droid",
|
|
7463
|
+
supatest_cli: "supatest"
|
|
7464
|
+
};
|
|
7465
|
+
var CLI_COMMAND_ALIASES = {
|
|
7466
|
+
kimi: ["kimi-cli"]
|
|
7240
7467
|
};
|
|
7241
7468
|
var resolvedCliCache = /* @__PURE__ */ new Map();
|
|
7469
|
+
var cachedLoginShellEnv = null;
|
|
7470
|
+
function parseEnvOutput(output) {
|
|
7471
|
+
const loginEnv = {};
|
|
7472
|
+
for (const line of output.split("\n")) {
|
|
7473
|
+
if (!line.trim()) continue;
|
|
7474
|
+
const eqIdx = line.indexOf("=");
|
|
7475
|
+
if (eqIdx <= 0) continue;
|
|
7476
|
+
const key = line.slice(0, eqIdx);
|
|
7477
|
+
if (/\s/.test(key)) continue;
|
|
7478
|
+
loginEnv[key] = line.slice(eqIdx + 1);
|
|
7479
|
+
}
|
|
7480
|
+
if (!loginEnv.HOME) loginEnv.HOME = process.env.HOME ?? (0, import_node_os.homedir)();
|
|
7481
|
+
if (!loginEnv.SHELL) loginEnv.SHELL = process.env.SHELL ?? "/bin/zsh";
|
|
7482
|
+
return loginEnv;
|
|
7483
|
+
}
|
|
7484
|
+
function loadLoginShellEnvironment() {
|
|
7485
|
+
if (cachedLoginShellEnv) return cachedLoginShellEnv;
|
|
7486
|
+
if ((0, import_node_os.platform)() === "win32") {
|
|
7487
|
+
cachedLoginShellEnv = process.env;
|
|
7488
|
+
return cachedLoginShellEnv;
|
|
7489
|
+
}
|
|
7490
|
+
try {
|
|
7491
|
+
const shell = (process.env.SHELL || "/bin/bash").replace(/'/g, "'\\''");
|
|
7492
|
+
const envOutput = (0, import_node_child_process.execSync)(`'${shell}' -ilc 'env'`, {
|
|
7493
|
+
encoding: "utf8",
|
|
7494
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
7495
|
+
timeout: 5e3
|
|
7496
|
+
});
|
|
7497
|
+
cachedLoginShellEnv = parseEnvOutput(envOutput);
|
|
7498
|
+
} catch {
|
|
7499
|
+
cachedLoginShellEnv = process.env;
|
|
7500
|
+
}
|
|
7501
|
+
return cachedLoginShellEnv;
|
|
7502
|
+
}
|
|
7242
7503
|
function getDaemonCliEnvironment() {
|
|
7243
|
-
|
|
7504
|
+
const base = (0, import_node_os.platform)() === "win32" ? process.env : loadLoginShellEnvironment();
|
|
7505
|
+
return augmentCliPath(base);
|
|
7244
7506
|
}
|
|
7245
7507
|
function cacheResolvedCli(command, resolvedPath) {
|
|
7246
7508
|
resolvedCliCache.set(command, resolvedPath);
|
|
@@ -7252,7 +7514,7 @@ function resolveViaWhere(command, env) {
|
|
|
7252
7514
|
if ((0, import_node_os.platform)() !== "win32") return null;
|
|
7253
7515
|
const whereExe = (0, import_node_path.join)(env.SystemRoot ?? process.env.SystemRoot ?? "C:\\Windows", "System32", "where.exe");
|
|
7254
7516
|
if (!(0, import_node_fs.existsSync)(whereExe)) return null;
|
|
7255
|
-
const result = (0,
|
|
7517
|
+
const result = (0, import_node_child_process2.spawnSync)(whereExe, [command], {
|
|
7256
7518
|
encoding: "utf8",
|
|
7257
7519
|
env,
|
|
7258
7520
|
windowsHide: true,
|
|
@@ -7266,26 +7528,6 @@ function resolveViaWhere(command, env) {
|
|
|
7266
7528
|
}
|
|
7267
7529
|
return null;
|
|
7268
7530
|
}
|
|
7269
|
-
function runCliVersionProbe(executable, env, args = ["--version"]) {
|
|
7270
|
-
const isWin = (0, import_node_os.platform)() === "win32";
|
|
7271
|
-
if (isWin && /\.(cmd|bat)$/i.test(executable)) {
|
|
7272
|
-
const comSpec = env.ComSpec ?? process.env.ComSpec ?? "cmd.exe";
|
|
7273
|
-
return (0, import_node_child_process.spawnSync)(comSpec, ["/d", "/s", "/c", executable, ...args], {
|
|
7274
|
-
encoding: "utf8",
|
|
7275
|
-
timeout: 3e3,
|
|
7276
|
-
env,
|
|
7277
|
-
windowsHide: true
|
|
7278
|
-
});
|
|
7279
|
-
}
|
|
7280
|
-
const useShell = isWin && !/[\\/]/.test(executable);
|
|
7281
|
-
return (0, import_node_child_process.spawnSync)(executable, args, {
|
|
7282
|
-
encoding: "utf8",
|
|
7283
|
-
timeout: 3e3,
|
|
7284
|
-
env,
|
|
7285
|
-
shell: useShell,
|
|
7286
|
-
windowsHide: isWin ? true : void 0
|
|
7287
|
-
});
|
|
7288
|
-
}
|
|
7289
7531
|
function pathSeparator() {
|
|
7290
7532
|
return (0, import_node_os.platform)() === "win32" ? ";" : ":";
|
|
7291
7533
|
}
|
|
@@ -7350,7 +7592,8 @@ function resolveCliExecutable(command, env = getDaemonCliEnvironment()) {
|
|
|
7350
7592
|
const home = enriched.HOME || enriched.USERPROFILE || (0, import_node_os.homedir)();
|
|
7351
7593
|
const isWin = (0, import_node_os.platform)() === "win32";
|
|
7352
7594
|
const pathExt = enriched.PATHEXT ?? (isWin ? ".EXE;.CMD;.BAT;.COM" : "");
|
|
7353
|
-
const
|
|
7595
|
+
const commandNames = [trimmed, ...CLI_COMMAND_ALIASES[trimmed] ?? []];
|
|
7596
|
+
const names = commandNames.flatMap((name) => isWin ? windowsCommandCandidates(name, pathExt) : [name]);
|
|
7354
7597
|
const candidates = [];
|
|
7355
7598
|
for (const name of names) {
|
|
7356
7599
|
if (name.includes("/") || name.includes("\\")) {
|
|
@@ -7425,10 +7668,12 @@ function resolveBackendRuntimeCommand(backendKind, env = getDaemonCliEnvironment
|
|
|
7425
7668
|
var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
7426
7669
|
childProcess = null;
|
|
7427
7670
|
_aborted = false;
|
|
7671
|
+
providerApiKey;
|
|
7428
7672
|
/** How long to wait after SIGTERM before escalating to SIGKILL. */
|
|
7429
7673
|
static SIGKILL_DELAY_MS = 3e3;
|
|
7430
7674
|
constructor(presenter, runtime) {
|
|
7431
7675
|
super(presenter, runtime);
|
|
7676
|
+
this.providerApiKey = runtime.providerApiKey;
|
|
7432
7677
|
}
|
|
7433
7678
|
abort() {
|
|
7434
7679
|
this._aborted = true;
|
|
@@ -7553,6 +7798,9 @@ var CoreAgent = class _CoreAgent extends BaseMachineAgent {
|
|
|
7553
7798
|
if (value2 !== void 0) env[key] = value2;
|
|
7554
7799
|
}
|
|
7555
7800
|
const augmented = getDaemonCliEnvironment();
|
|
7801
|
+
if (this.runtime.backendKind === "supatest_cli" && this.providerApiKey) {
|
|
7802
|
+
augmented.SUPATEST_API_KEY = this.providerApiKey;
|
|
7803
|
+
}
|
|
7556
7804
|
if ((0, import_node_os2.platform)() !== "win32") {
|
|
7557
7805
|
const home = augmented.HOME ?? "/home/user";
|
|
7558
7806
|
const extraPaths = [`${home}/.local/node/bin`, `${home}/.local/bin`];
|
|
@@ -11387,7 +11635,7 @@ var WSClient = class {
|
|
|
11387
11635
|
};
|
|
11388
11636
|
|
|
11389
11637
|
// src/version.ts
|
|
11390
|
-
var AGENT_VERSION = "0.1.
|
|
11638
|
+
var AGENT_VERSION = "0.1.16";
|
|
11391
11639
|
|
|
11392
11640
|
// src/sandbox.ts
|
|
11393
11641
|
async function runSandbox(config) {
|
|
@@ -11403,6 +11651,8 @@ async function runSandbox(config) {
|
|
|
11403
11651
|
let currentConversationId = config.sessionId;
|
|
11404
11652
|
let currentTeamId = config.teamId;
|
|
11405
11653
|
let currentUser = config.currentUser;
|
|
11654
|
+
let currentTerminalContextRefs;
|
|
11655
|
+
let currentTerminalSnapshots;
|
|
11406
11656
|
console.info("[sandbox] Connecting to WS server", { wsUrl: config.wsUrl, sessionId: config.sessionId });
|
|
11407
11657
|
const wsClient = new WSClient(
|
|
11408
11658
|
config.wsUrl,
|
|
@@ -11484,6 +11734,8 @@ async function runSandbox(config) {
|
|
|
11484
11734
|
teamId: currentTeamId,
|
|
11485
11735
|
currentUser,
|
|
11486
11736
|
workflowTools: currentWorkflowTools,
|
|
11737
|
+
terminalContextRefs: currentTerminalContextRefs,
|
|
11738
|
+
terminalSnapshots: currentTerminalSnapshots,
|
|
11487
11739
|
images: currentImages.length > 0 ? currentImages.map((img) => ({
|
|
11488
11740
|
id: img.id,
|
|
11489
11741
|
data: img.data,
|
|
@@ -11524,8 +11776,8 @@ async function runSandbox(config) {
|
|
|
11524
11776
|
if (nextPayload.agentPrompt !== void 0) {
|
|
11525
11777
|
activeAgentPrompt = nextPayload.agentPrompt;
|
|
11526
11778
|
}
|
|
11527
|
-
if (nextPayload.selectedModel) {
|
|
11528
|
-
activeModel = nextPayload.selectedModel;
|
|
11779
|
+
if (nextPayload.selectedModel !== void 0) {
|
|
11780
|
+
activeModel = nextPayload.selectedModel.trim() || void 0;
|
|
11529
11781
|
}
|
|
11530
11782
|
if (nextPayload.selectedContextWindow !== void 0) {
|
|
11531
11783
|
activeContextWindow = nextPayload.selectedContextWindow;
|
|
@@ -11557,49 +11809,349 @@ async function runSandbox(config) {
|
|
|
11557
11809
|
if (nextPayload.teamId !== void 0) {
|
|
11558
11810
|
currentTeamId = nextPayload.teamId;
|
|
11559
11811
|
}
|
|
11812
|
+
if (nextPayload.terminalContextRefs !== void 0) {
|
|
11813
|
+
currentTerminalContextRefs = nextPayload.terminalContextRefs;
|
|
11814
|
+
}
|
|
11815
|
+
if (nextPayload.terminalSnapshots !== void 0) {
|
|
11816
|
+
currentTerminalSnapshots = nextPayload.terminalSnapshots;
|
|
11817
|
+
}
|
|
11560
11818
|
}
|
|
11561
11819
|
presenter.setSuppressSessionLifecycle(false);
|
|
11562
11820
|
wsClient.close();
|
|
11563
11821
|
}
|
|
11564
11822
|
|
|
11565
11823
|
// src/daemon.ts
|
|
11566
|
-
var
|
|
11824
|
+
var import_node_fs3 = require("fs");
|
|
11567
11825
|
var import_node_http = __toESM(require("http"), 1);
|
|
11568
11826
|
var import_node_crypto = require("crypto");
|
|
11827
|
+
var import_node_os4 = require("os");
|
|
11828
|
+
var import_node_path3 = require("path");
|
|
11829
|
+
|
|
11830
|
+
// ../shared/dist/node/provider-limits.js
|
|
11831
|
+
var import_node_child_process3 = require("child_process");
|
|
11832
|
+
var import_node_fs2 = require("fs");
|
|
11569
11833
|
var import_node_os3 = require("os");
|
|
11570
11834
|
var import_node_path2 = require("path");
|
|
11835
|
+
var CACHE_TTL_MS = 6e4;
|
|
11836
|
+
var cachedLimits = null;
|
|
11837
|
+
function numericValue(value2) {
|
|
11838
|
+
if (typeof value2 === "number" && Number.isFinite(value2))
|
|
11839
|
+
return value2;
|
|
11840
|
+
if (typeof value2 === "string" && value2.trim() && Number.isFinite(Number(value2))) {
|
|
11841
|
+
return Number(value2);
|
|
11842
|
+
}
|
|
11843
|
+
return null;
|
|
11844
|
+
}
|
|
11845
|
+
function splitPath2(value2) {
|
|
11846
|
+
return (value2 ?? "").split(import_node_path2.delimiter).filter(Boolean);
|
|
11847
|
+
}
|
|
11848
|
+
function isExecutableFile(path) {
|
|
11849
|
+
try {
|
|
11850
|
+
(0, import_node_fs2.accessSync)(path, import_node_fs2.constants.X_OK);
|
|
11851
|
+
return true;
|
|
11852
|
+
} catch {
|
|
11853
|
+
return false;
|
|
11854
|
+
}
|
|
11855
|
+
}
|
|
11856
|
+
function augmentCliPath2(env) {
|
|
11857
|
+
const home = env.HOME || (0, import_node_os3.homedir)();
|
|
11858
|
+
const extraPaths = [
|
|
11859
|
+
(0, import_node_path2.join)(home, ".local", "bin"),
|
|
11860
|
+
(0, import_node_path2.join)(home, ".local", "node", "bin"),
|
|
11861
|
+
(0, import_node_path2.join)(home, ".bun", "bin"),
|
|
11862
|
+
(0, import_node_path2.join)(home, ".cargo", "bin"),
|
|
11863
|
+
"/opt/homebrew/bin",
|
|
11864
|
+
"/usr/local/bin"
|
|
11865
|
+
];
|
|
11866
|
+
const existing = new Set(splitPath2(env.PATH));
|
|
11867
|
+
const missing = extraPaths.filter((path) => !existing.has(path));
|
|
11868
|
+
if (missing.length === 0)
|
|
11869
|
+
return env;
|
|
11870
|
+
return {
|
|
11871
|
+
...env,
|
|
11872
|
+
PATH: `${missing.join(import_node_path2.delimiter)}${import_node_path2.delimiter}${env.PATH ?? ""}`
|
|
11873
|
+
};
|
|
11874
|
+
}
|
|
11875
|
+
function resolveExecutable(command, env, overrideEnvVar) {
|
|
11876
|
+
const override = env[overrideEnvVar]?.trim();
|
|
11877
|
+
if (override && isExecutableFile(override))
|
|
11878
|
+
return override;
|
|
11879
|
+
for (const dir of splitPath2(env.PATH)) {
|
|
11880
|
+
const candidate = (0, import_node_path2.join)(dir, command);
|
|
11881
|
+
if (isExecutableFile(candidate))
|
|
11882
|
+
return candidate;
|
|
11883
|
+
}
|
|
11884
|
+
return null;
|
|
11885
|
+
}
|
|
11886
|
+
function limitLabel(windowMinutes, fallback) {
|
|
11887
|
+
if (windowMinutes === 5 * 60)
|
|
11888
|
+
return "5h";
|
|
11889
|
+
if (windowMinutes === 7 * 24 * 60)
|
|
11890
|
+
return "Weekly";
|
|
11891
|
+
return fallback;
|
|
11892
|
+
}
|
|
11893
|
+
function windowFromUsedPercent(params) {
|
|
11894
|
+
const usedPercent = Math.max(0, Math.min(100, params.usedPercent));
|
|
11895
|
+
return {
|
|
11896
|
+
label: limitLabel(params.windowMinutes, params.fallbackLabel),
|
|
11897
|
+
remaining: Math.max(0, 100 - usedPercent),
|
|
11898
|
+
limit: 100,
|
|
11899
|
+
usedPercent,
|
|
11900
|
+
...params.windowMinutes !== null ? { windowMinutes: params.windowMinutes } : {},
|
|
11901
|
+
...params.resetAt ? { resetAt: params.resetAt } : {},
|
|
11902
|
+
source: params.source
|
|
11903
|
+
};
|
|
11904
|
+
}
|
|
11905
|
+
function codexWindow(raw, fallbackLabel) {
|
|
11906
|
+
if (!raw)
|
|
11907
|
+
return null;
|
|
11908
|
+
const usedPercent = numericValue(raw.usedPercent);
|
|
11909
|
+
if (usedPercent === null)
|
|
11910
|
+
return null;
|
|
11911
|
+
const windowMinutes = numericValue(raw.windowDurationMins);
|
|
11912
|
+
const resetSeconds = numericValue(raw.resetsAt);
|
|
11913
|
+
const resetAt = resetSeconds !== null ? new Date(resetSeconds * 1e3).toISOString() : null;
|
|
11914
|
+
return windowFromUsedPercent({
|
|
11915
|
+
usedPercent,
|
|
11916
|
+
windowMinutes,
|
|
11917
|
+
resetAt,
|
|
11918
|
+
fallbackLabel,
|
|
11919
|
+
source: "codex-cli-rpc"
|
|
11920
|
+
});
|
|
11921
|
+
}
|
|
11922
|
+
function claudeWindow(raw, label, windowMinutes) {
|
|
11923
|
+
if (!raw)
|
|
11924
|
+
return null;
|
|
11925
|
+
const usedPercent = numericValue(raw.utilization);
|
|
11926
|
+
if (usedPercent === null)
|
|
11927
|
+
return null;
|
|
11928
|
+
const resetAt = typeof raw.resets_at === "string" && raw.resets_at.trim() ? raw.resets_at.trim() : null;
|
|
11929
|
+
return windowFromUsedPercent({
|
|
11930
|
+
usedPercent,
|
|
11931
|
+
windowMinutes,
|
|
11932
|
+
resetAt,
|
|
11933
|
+
fallbackLabel: label,
|
|
11934
|
+
source: "claude-oauth"
|
|
11935
|
+
});
|
|
11936
|
+
}
|
|
11937
|
+
async function withTimeout(promise, timeoutMs, fallback) {
|
|
11938
|
+
let timer;
|
|
11939
|
+
try {
|
|
11940
|
+
return await Promise.race([
|
|
11941
|
+
promise,
|
|
11942
|
+
new Promise((resolve2) => {
|
|
11943
|
+
timer = setTimeout(() => resolve2(fallback), timeoutMs);
|
|
11944
|
+
})
|
|
11945
|
+
]);
|
|
11946
|
+
} finally {
|
|
11947
|
+
if (timer)
|
|
11948
|
+
clearTimeout(timer);
|
|
11949
|
+
}
|
|
11950
|
+
}
|
|
11951
|
+
async function collectCodexLimits(env) {
|
|
11952
|
+
const runtimeEnv = augmentCliPath2(env);
|
|
11953
|
+
const codexCommand = resolveExecutable("codex", runtimeEnv, "AIDEN_CODEX_PATH");
|
|
11954
|
+
if (!codexCommand)
|
|
11955
|
+
return [];
|
|
11956
|
+
return withTimeout(new Promise((resolve2) => {
|
|
11957
|
+
const child = (0, import_node_child_process3.spawn)(codexCommand, ["-s", "read-only", "-a", "untrusted", "app-server"], {
|
|
11958
|
+
env: runtimeEnv,
|
|
11959
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
11960
|
+
});
|
|
11961
|
+
let nextId = 1;
|
|
11962
|
+
let buffer = "";
|
|
11963
|
+
const pending = /* @__PURE__ */ new Map();
|
|
11964
|
+
const finish = (windows = []) => {
|
|
11965
|
+
pending.clear();
|
|
11966
|
+
if (!child.killed)
|
|
11967
|
+
child.kill();
|
|
11968
|
+
resolve2(windows);
|
|
11969
|
+
};
|
|
11970
|
+
const request = (method, params = {}) => {
|
|
11971
|
+
const id = nextId++;
|
|
11972
|
+
child.stdin.write(`${JSON.stringify({ id, method, params })}
|
|
11973
|
+
`);
|
|
11974
|
+
return new Promise((requestResolve) => {
|
|
11975
|
+
pending.set(id, requestResolve);
|
|
11976
|
+
});
|
|
11977
|
+
};
|
|
11978
|
+
child.on("error", () => finish());
|
|
11979
|
+
child.on("exit", () => finish());
|
|
11980
|
+
child.stdout.on("data", (chunk) => {
|
|
11981
|
+
buffer += chunk.toString("utf8");
|
|
11982
|
+
const lines = buffer.split("\n");
|
|
11983
|
+
buffer = lines.pop() ?? "";
|
|
11984
|
+
for (const line of lines) {
|
|
11985
|
+
const trimmed = line.trim();
|
|
11986
|
+
if (!trimmed)
|
|
11987
|
+
continue;
|
|
11988
|
+
let message;
|
|
11989
|
+
try {
|
|
11990
|
+
message = JSON.parse(trimmed);
|
|
11991
|
+
} catch {
|
|
11992
|
+
continue;
|
|
11993
|
+
}
|
|
11994
|
+
const id = numericValue(message.id);
|
|
11995
|
+
if (id === null)
|
|
11996
|
+
continue;
|
|
11997
|
+
const resolver = pending.get(id);
|
|
11998
|
+
if (!resolver)
|
|
11999
|
+
continue;
|
|
12000
|
+
pending.delete(id);
|
|
12001
|
+
resolver(message.error ? null : message.result);
|
|
12002
|
+
}
|
|
12003
|
+
});
|
|
12004
|
+
void (async () => {
|
|
12005
|
+
try {
|
|
12006
|
+
await request("initialize", {
|
|
12007
|
+
clientInfo: { name: "aiden", version: "runtime" }
|
|
12008
|
+
});
|
|
12009
|
+
child.stdin.write(`${JSON.stringify({ method: "initialized", params: {} })}
|
|
12010
|
+
`);
|
|
12011
|
+
const result = await request("account/rateLimits/read");
|
|
12012
|
+
const windows = [
|
|
12013
|
+
codexWindow(result?.rateLimits?.primary, "Primary"),
|
|
12014
|
+
codexWindow(result?.rateLimits?.secondary, "Weekly")
|
|
12015
|
+
].filter((window2) => Boolean(window2));
|
|
12016
|
+
finish(windows);
|
|
12017
|
+
} catch {
|
|
12018
|
+
finish();
|
|
12019
|
+
}
|
|
12020
|
+
})();
|
|
12021
|
+
}), 4e3, []);
|
|
12022
|
+
}
|
|
12023
|
+
function readClaudeCredentialsPayload() {
|
|
12024
|
+
const path = (0, import_node_path2.join)((0, import_node_os3.homedir)(), ".claude", ".credentials.json");
|
|
12025
|
+
if ((0, import_node_fs2.existsSync)(path)) {
|
|
12026
|
+
try {
|
|
12027
|
+
return JSON.parse((0, import_node_fs2.readFileSync)(path, "utf8"));
|
|
12028
|
+
} catch {
|
|
12029
|
+
return null;
|
|
12030
|
+
}
|
|
12031
|
+
}
|
|
12032
|
+
if (process.platform !== "darwin")
|
|
12033
|
+
return null;
|
|
12034
|
+
try {
|
|
12035
|
+
const raw = (0, import_node_child_process3.execFileSync)("security", ["find-generic-password", "-s", "Claude Code-credentials", "-w"], { encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
12036
|
+
if (!raw)
|
|
12037
|
+
return null;
|
|
12038
|
+
return JSON.parse(raw);
|
|
12039
|
+
} catch {
|
|
12040
|
+
return null;
|
|
12041
|
+
}
|
|
12042
|
+
}
|
|
12043
|
+
function readClaudeAccessToken() {
|
|
12044
|
+
const parsed = readClaudeCredentialsPayload();
|
|
12045
|
+
if (!parsed)
|
|
12046
|
+
return null;
|
|
12047
|
+
try {
|
|
12048
|
+
const oauth = parsed.claudeAiOauth;
|
|
12049
|
+
if (!oauth || typeof oauth.accessToken !== "string" || !oauth.accessToken.trim())
|
|
12050
|
+
return null;
|
|
12051
|
+
const expiresAt = numericValue(oauth.expiresAt);
|
|
12052
|
+
if (expiresAt !== null && Date.now() >= expiresAt)
|
|
12053
|
+
return null;
|
|
12054
|
+
return oauth.accessToken.trim();
|
|
12055
|
+
} catch {
|
|
12056
|
+
return null;
|
|
12057
|
+
}
|
|
12058
|
+
}
|
|
12059
|
+
async function collectClaudeLimits() {
|
|
12060
|
+
const token = readClaudeAccessToken();
|
|
12061
|
+
if (!token)
|
|
12062
|
+
return [];
|
|
12063
|
+
return withTimeout((async () => {
|
|
12064
|
+
const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
12065
|
+
headers: {
|
|
12066
|
+
accept: "application/json",
|
|
12067
|
+
authorization: `Bearer ${token}`,
|
|
12068
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
12069
|
+
"user-agent": "claude-code/2.1.0"
|
|
12070
|
+
}
|
|
12071
|
+
});
|
|
12072
|
+
if (!response.ok)
|
|
12073
|
+
return [];
|
|
12074
|
+
const body = await response.json();
|
|
12075
|
+
return [
|
|
12076
|
+
claudeWindow(body.five_hour, "5h", 5 * 60),
|
|
12077
|
+
claudeWindow(body.seven_day, "Weekly", 7 * 24 * 60)
|
|
12078
|
+
].filter((window2) => Boolean(window2));
|
|
12079
|
+
})().catch(() => []), 4e3, []);
|
|
12080
|
+
}
|
|
12081
|
+
async function collectLocalAgentProviderLimits(env = process.env) {
|
|
12082
|
+
if (cachedLimits && Object.keys(cachedLimits.value).length > 0 && Date.now() - cachedLimits.collectedAt < CACHE_TTL_MS) {
|
|
12083
|
+
return cachedLimits.value;
|
|
12084
|
+
}
|
|
12085
|
+
const [codex, claude] = await Promise.all([
|
|
12086
|
+
collectCodexLimits(env),
|
|
12087
|
+
collectClaudeLimits()
|
|
12088
|
+
]);
|
|
12089
|
+
const limits = {};
|
|
12090
|
+
if (codex.length > 0)
|
|
12091
|
+
limits.codex_app_server = codex;
|
|
12092
|
+
if (claude.length > 0)
|
|
12093
|
+
limits.claude_cli = claude;
|
|
12094
|
+
if (Object.keys(limits).length > 0) {
|
|
12095
|
+
cachedLimits = { value: limits, collectedAt: Date.now() };
|
|
12096
|
+
} else {
|
|
12097
|
+
cachedLimits = null;
|
|
12098
|
+
}
|
|
12099
|
+
return limits;
|
|
12100
|
+
}
|
|
12101
|
+
|
|
12102
|
+
// src/daemon.ts
|
|
11571
12103
|
var PRODUCTION_API_URL = "https://api.aiden-platform.com";
|
|
11572
12104
|
var PRODUCTION_WS_URL = "wss://ws.aiden-platform.com";
|
|
11573
12105
|
var LOCAL_API_URL = "http://localhost:8400";
|
|
11574
12106
|
var LOCAL_WS_URL = "ws://localhost:8401";
|
|
12107
|
+
async function probeLocalDaemonEndpoint(port, token, timeoutMs = 750) {
|
|
12108
|
+
try {
|
|
12109
|
+
const headers = {};
|
|
12110
|
+
if (token) headers.authorization = `Bearer ${token}`;
|
|
12111
|
+
const response = await fetch(`http://127.0.0.1:${port}/status`, {
|
|
12112
|
+
headers,
|
|
12113
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
12114
|
+
});
|
|
12115
|
+
if (response.status === 401) return { state: "listening_unauthorized" };
|
|
12116
|
+
if (!response.ok) return { state: "absent" };
|
|
12117
|
+
const body = await response.json();
|
|
12118
|
+
if (!body.runtimeId) return { state: "absent" };
|
|
12119
|
+
return { state: "running", status: body };
|
|
12120
|
+
} catch {
|
|
12121
|
+
return { state: "absent" };
|
|
12122
|
+
}
|
|
12123
|
+
}
|
|
12124
|
+
function describeOccupiedLocalDaemon(port) {
|
|
12125
|
+
return `local daemon API port ${port} is already in use. If Aiden desktop is open, it manages the daemon automatically. Quit Aiden desktop or stop the existing daemon before starting another.`;
|
|
12126
|
+
}
|
|
11575
12127
|
function getConfigPath() {
|
|
11576
|
-
return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0,
|
|
12128
|
+
return process.env.AIDEN_AGENT_CONFIG_PATH ?? (0, import_node_path3.join)((0, import_node_os4.homedir)(), ".aiden", "agent", "config.json");
|
|
11577
12129
|
}
|
|
11578
12130
|
function getEndpointDefaultsPath() {
|
|
11579
|
-
return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0,
|
|
12131
|
+
return process.env.AIDEN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path3.join)((0, import_node_path3.dirname)(getConfigPath()), "endpoints.json");
|
|
11580
12132
|
}
|
|
11581
12133
|
function readConfig() {
|
|
11582
12134
|
const configPath = getConfigPath();
|
|
11583
|
-
if (!(0,
|
|
11584
|
-
return JSON.parse((0,
|
|
12135
|
+
if (!(0, import_node_fs3.existsSync)(configPath)) return {};
|
|
12136
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(configPath, "utf8"));
|
|
11585
12137
|
}
|
|
11586
12138
|
function readEndpointDefaults() {
|
|
11587
12139
|
const endpointsPath = getEndpointDefaultsPath();
|
|
11588
|
-
if (!(0,
|
|
12140
|
+
if (!(0, import_node_fs3.existsSync)(endpointsPath)) return {};
|
|
11589
12141
|
try {
|
|
11590
|
-
return JSON.parse((0,
|
|
12142
|
+
return JSON.parse((0, import_node_fs3.readFileSync)(endpointsPath, "utf8"));
|
|
11591
12143
|
} catch {
|
|
11592
12144
|
return {};
|
|
11593
12145
|
}
|
|
11594
12146
|
}
|
|
11595
12147
|
function writeConfig(config) {
|
|
11596
12148
|
const configPath = getConfigPath();
|
|
11597
|
-
(0,
|
|
11598
|
-
(0,
|
|
12149
|
+
(0, import_node_fs3.mkdirSync)((0, import_node_path3.dirname)(configPath), { recursive: true, mode: 448 });
|
|
12150
|
+
(0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
11599
12151
|
}
|
|
11600
12152
|
function removeConfig() {
|
|
11601
12153
|
const configPath = getConfigPath();
|
|
11602
|
-
if ((0,
|
|
12154
|
+
if ((0, import_node_fs3.existsSync)(configPath)) (0, import_node_fs3.rmSync)(configPath, { force: true });
|
|
11603
12155
|
}
|
|
11604
12156
|
function argValue(args, name) {
|
|
11605
12157
|
const index = args.indexOf(name);
|
|
@@ -11629,50 +12181,81 @@ function isLocalEndpoint(url2) {
|
|
|
11629
12181
|
function getLocalApiPort(args, stored) {
|
|
11630
12182
|
const raw = argValue(args, "--local-api-port") ?? process.env.AIDEN_AGENT_LOCAL_API_PORT;
|
|
11631
12183
|
if (raw) return Number.parseInt(raw, 10);
|
|
11632
|
-
|
|
12184
|
+
const storedPort = stored.localApiPort;
|
|
12185
|
+
if (storedPort && storedPort > 0) return storedPort;
|
|
12186
|
+
return 47831;
|
|
11633
12187
|
}
|
|
11634
12188
|
function sleep(ms) {
|
|
11635
12189
|
if (ms <= 0) return Promise.resolve();
|
|
11636
12190
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
11637
12191
|
}
|
|
11638
|
-
function commandVersion(command, args = ["--version"]) {
|
|
11639
|
-
const env = getDaemonCliEnvironment();
|
|
11640
|
-
const executable = resolveCliExecutable(command, env);
|
|
11641
|
-
if (!executable) return void 0;
|
|
11642
|
-
const result = runCliVersionProbe(executable, env, args);
|
|
11643
|
-
if (result.error || result.status !== 0) return void 0;
|
|
11644
|
-
return (result.stdout || result.stderr).split("\n")[0]?.trim() || void 0;
|
|
11645
|
-
}
|
|
11646
|
-
function commandVersionForProvider(provider) {
|
|
11647
|
-
const env = getDaemonCliEnvironment();
|
|
11648
|
-
const executable = resolveProviderCliCommand(provider, env);
|
|
11649
|
-
if (!executable) return void 0;
|
|
11650
|
-
const result = runCliVersionProbe(executable, env, ["--version"]);
|
|
11651
|
-
if (result.error || result.status !== 0) return void 0;
|
|
11652
|
-
return (result.stdout || result.stderr).split("\n")[0]?.trim() || void 0;
|
|
11653
|
-
}
|
|
11654
12192
|
function normalizeBackendKind(backendKind) {
|
|
11655
12193
|
if (backendKind === "codex") return "codex_app_server";
|
|
11656
12194
|
return backendKind;
|
|
11657
12195
|
}
|
|
11658
12196
|
function discoverCapabilities() {
|
|
11659
12197
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
12198
|
+
const env = getDaemonCliEnvironment();
|
|
11660
12199
|
return {
|
|
11661
12200
|
agents: DISCOVERABLE_PROVIDER_KINDS.map((provider) => {
|
|
11662
|
-
const
|
|
12201
|
+
const executable = resolveProviderCliCommand(provider, env);
|
|
11663
12202
|
return {
|
|
11664
12203
|
provider,
|
|
11665
|
-
available: Boolean(
|
|
11666
|
-
...version ? { version } : {},
|
|
12204
|
+
available: Boolean(executable),
|
|
11667
12205
|
models: [],
|
|
11668
12206
|
lastCheckedAt: now
|
|
11669
12207
|
};
|
|
11670
12208
|
}),
|
|
11671
|
-
hasGit: Boolean(
|
|
12209
|
+
hasGit: Boolean(resolveCliExecutable("git", env)),
|
|
11672
12210
|
hasTerminal: true,
|
|
11673
12211
|
supportsFilesystem: true
|
|
11674
12212
|
};
|
|
11675
12213
|
}
|
|
12214
|
+
var USER_INTERRUPTED_RESULT = {
|
|
12215
|
+
success: false,
|
|
12216
|
+
summary: "Interrupted by user",
|
|
12217
|
+
filesModified: [],
|
|
12218
|
+
planFilesCreated: [],
|
|
12219
|
+
iterations: 0,
|
|
12220
|
+
error: "Interrupted by user"
|
|
12221
|
+
};
|
|
12222
|
+
function abortActiveAgent(entry) {
|
|
12223
|
+
entry.presenter.onComplete(USER_INTERRUPTED_RESULT);
|
|
12224
|
+
entry.agent.kill();
|
|
12225
|
+
}
|
|
12226
|
+
function collectRuntimeMetadata() {
|
|
12227
|
+
const cpuList = (0, import_node_os4.cpus)();
|
|
12228
|
+
const metadata = {
|
|
12229
|
+
platform: (0, import_node_os4.platform)(),
|
|
12230
|
+
arch: (0, import_node_os4.arch)(),
|
|
12231
|
+
osVersion: (0, import_node_os4.release)(),
|
|
12232
|
+
agentVersion: AGENT_VERSION,
|
|
12233
|
+
memoryBytes: (0, import_node_os4.totalmem)(),
|
|
12234
|
+
memoryFreeBytes: (0, import_node_os4.freemem)()
|
|
12235
|
+
};
|
|
12236
|
+
if (cpuList.length > 0) {
|
|
12237
|
+
metadata.cpuModel = cpuList[0]?.model;
|
|
12238
|
+
metadata.cpuCores = cpuList.length;
|
|
12239
|
+
}
|
|
12240
|
+
try {
|
|
12241
|
+
const disk = (0, import_node_fs3.statfsSync)((0, import_node_os4.homedir)());
|
|
12242
|
+
metadata.diskFreeBytes = disk.bavail * disk.bsize;
|
|
12243
|
+
metadata.diskTotalBytes = disk.blocks * disk.bsize;
|
|
12244
|
+
} catch {
|
|
12245
|
+
}
|
|
12246
|
+
return metadata;
|
|
12247
|
+
}
|
|
12248
|
+
async function collectRuntimeMetadataWithProviderLimits() {
|
|
12249
|
+
const metadata = collectRuntimeMetadata();
|
|
12250
|
+
try {
|
|
12251
|
+
const agentProviderLimits = await collectLocalAgentProviderLimits(getDaemonCliEnvironment());
|
|
12252
|
+
if (Object.keys(agentProviderLimits).length > 0) {
|
|
12253
|
+
metadata.agentProviderLimits = agentProviderLimits;
|
|
12254
|
+
}
|
|
12255
|
+
} catch {
|
|
12256
|
+
}
|
|
12257
|
+
return metadata;
|
|
12258
|
+
}
|
|
11676
12259
|
var RuntimePresenter = class {
|
|
11677
12260
|
constructor(socket, conversationId, runId) {
|
|
11678
12261
|
this.socket = socket;
|
|
@@ -11748,7 +12331,7 @@ async function setupDaemon(args) {
|
|
|
11748
12331
|
const teamId = argValue(args, "--team") ?? process.env.AIDEN_TEAM_ID;
|
|
11749
12332
|
const setupToken = argValue(args, "--setup-token") ?? process.env.AIDEN_RUNTIME_SETUP_TOKEN;
|
|
11750
12333
|
const token = argValue(args, "--token") ?? process.env.AIDEN_SETUP_TOKEN;
|
|
11751
|
-
const displayName = argValue(args, "--name") ?? (0,
|
|
12334
|
+
const displayName = argValue(args, "--name") ?? (0, import_node_os4.hostname)();
|
|
11752
12335
|
const legacyLocalMachineId = argValue(args, "--legacy-local-machine-id") ?? process.env.AIDEN_LEGACY_LOCAL_MACHINE_ID;
|
|
11753
12336
|
const scope = argValue(args, "--scope") ?? process.env.AIDEN_RUNTIME_SCOPE ?? "team";
|
|
11754
12337
|
if (scope !== "team" && scope !== "user") {
|
|
@@ -11769,7 +12352,7 @@ async function setupDaemon(args) {
|
|
|
11769
12352
|
...teamId ? { teamId } : {},
|
|
11770
12353
|
...setupToken ? { setupToken } : {},
|
|
11771
12354
|
displayName,
|
|
11772
|
-
hostname: (0,
|
|
12355
|
+
hostname: (0, import_node_os4.hostname)(),
|
|
11773
12356
|
...legacyLocalMachineId ? { legacyLocalMachineId } : {},
|
|
11774
12357
|
runtimeKind: "machine",
|
|
11775
12358
|
managementKind: "user_managed",
|
|
@@ -11778,11 +12361,7 @@ async function setupDaemon(args) {
|
|
|
11778
12361
|
ownerType: scope,
|
|
11779
12362
|
visibility: scope,
|
|
11780
12363
|
capabilities: discoverCapabilities(),
|
|
11781
|
-
metadata:
|
|
11782
|
-
platform: (0, import_node_os3.platform)(),
|
|
11783
|
-
arch: (0, import_node_os3.arch)(),
|
|
11784
|
-
agentVersion: AGENT_VERSION
|
|
11785
|
-
}
|
|
12364
|
+
metadata: await collectRuntimeMetadataWithProviderLimits()
|
|
11786
12365
|
})
|
|
11787
12366
|
});
|
|
11788
12367
|
if (!response.ok) {
|
|
@@ -11804,20 +12383,16 @@ async function setupDaemon(args) {
|
|
|
11804
12383
|
}
|
|
11805
12384
|
async function loginWithDeviceCode(args) {
|
|
11806
12385
|
const { apiUrl, wsUrl, endpointProfile } = resolveEndpoints(args);
|
|
11807
|
-
const displayName = argValue(args, "--name") ?? (0,
|
|
12386
|
+
const displayName = argValue(args, "--name") ?? (0, import_node_os4.hostname)();
|
|
11808
12387
|
const maxPolls = Number.parseInt(argValue(args, "--max-polls") ?? "300", 10);
|
|
11809
12388
|
const start = await fetch(`${apiUrl}/public/runtimes/device-authorizations`, {
|
|
11810
12389
|
method: "POST",
|
|
11811
12390
|
headers: { "content-type": "application/json" },
|
|
11812
12391
|
body: JSON.stringify({
|
|
11813
12392
|
displayName,
|
|
11814
|
-
hostname: (0,
|
|
12393
|
+
hostname: (0, import_node_os4.hostname)(),
|
|
11815
12394
|
capabilities: discoverCapabilities(),
|
|
11816
|
-
metadata:
|
|
11817
|
-
platform: (0, import_node_os3.platform)(),
|
|
11818
|
-
arch: (0, import_node_os3.arch)(),
|
|
11819
|
-
agentVersion: AGENT_VERSION
|
|
11820
|
-
}
|
|
12395
|
+
metadata: await collectRuntimeMetadataWithProviderLimits()
|
|
11821
12396
|
})
|
|
11822
12397
|
});
|
|
11823
12398
|
if (!start.ok) {
|
|
@@ -11865,8 +12440,9 @@ async function startDaemon(args) {
|
|
|
11865
12440
|
if (!runtimeId || !runtimeToken || !wsUrl) {
|
|
11866
12441
|
throw new Error("daemon requires runtimeId, runtime token, and wsUrl. Run setup first or pass --runtime-id/--token/--ws-url.");
|
|
11867
12442
|
}
|
|
11868
|
-
const capabilities = discoverCapabilities();
|
|
11869
12443
|
const activeAgents = /* @__PURE__ */ new Map();
|
|
12444
|
+
const currentCapabilities = () => discoverCapabilities();
|
|
12445
|
+
const currentMetadata = () => collectRuntimeMetadataWithProviderLimits();
|
|
11870
12446
|
const recentLogs = [];
|
|
11871
12447
|
const pushLog = (message) => {
|
|
11872
12448
|
recentLogs.push(`${(/* @__PURE__ */ new Date()).toISOString()} ${message}`);
|
|
@@ -11888,16 +12464,26 @@ async function startDaemon(args) {
|
|
|
11888
12464
|
extraHeaders: { "ngrok-skip-browser-warning": "1" }
|
|
11889
12465
|
});
|
|
11890
12466
|
const heartbeat = setInterval(() => {
|
|
11891
|
-
if (socket.connected)
|
|
12467
|
+
if (!socket.connected) return;
|
|
12468
|
+
void currentMetadata().then((metadata) => {
|
|
12469
|
+
socket.emit("runtime.heartbeat", { capabilities: currentCapabilities(), metadata });
|
|
12470
|
+
});
|
|
11892
12471
|
}, 15e3);
|
|
11893
12472
|
socket.on("connect", () => {
|
|
11894
12473
|
pushLog(`connected runtime=${runtimeId}`);
|
|
11895
12474
|
console.info("[aiden-agent] Daemon connected", { runtimeId, wsUrl, version: AGENT_VERSION });
|
|
11896
|
-
|
|
12475
|
+
void currentMetadata().then((metadata) => {
|
|
12476
|
+
socket.emit("runtime.hello", { capabilities: currentCapabilities(), metadata });
|
|
12477
|
+
});
|
|
11897
12478
|
});
|
|
12479
|
+
let lastConnectErrorLogAt = 0;
|
|
11898
12480
|
socket.on("connect_error", (error) => {
|
|
11899
12481
|
pushLog(`connect_error ${error.message}`);
|
|
11900
|
-
|
|
12482
|
+
const now = Date.now();
|
|
12483
|
+
if (now - lastConnectErrorLogAt < 5e3) return;
|
|
12484
|
+
lastConnectErrorLogAt = now;
|
|
12485
|
+
const wsHint = isLocalEndpoint(wsUrl) ? ` Nothing is listening at ${wsUrl}. For local dev run setup with --profile local and start the stack (WS on ws://localhost:8401).` : ` Verify wsUrl in ${getConfigPath()} and that you can reach ${wsUrl}.`;
|
|
12486
|
+
console.warn("[aiden-agent] Daemon connection error:", `${error.message}.${wsHint}`);
|
|
11901
12487
|
});
|
|
11902
12488
|
socket.on("disconnect", (reason) => {
|
|
11903
12489
|
pushLog(`disconnected ${reason}`);
|
|
@@ -11925,10 +12511,11 @@ async function startDaemon(args) {
|
|
|
11925
12511
|
const presenter = new RuntimePresenter(socket, payload.conversationId, payload.runId);
|
|
11926
12512
|
const agent = new CoreAgent(presenter, {
|
|
11927
12513
|
backendKind,
|
|
11928
|
-
runtimeCommand
|
|
12514
|
+
runtimeCommand,
|
|
12515
|
+
providerApiKey: payload.providerApiKey
|
|
11929
12516
|
});
|
|
11930
|
-
activeAgents.set(payload.runId, agent);
|
|
11931
12517
|
const cwd = payload.projectPath ?? process.cwd();
|
|
12518
|
+
activeAgents.set(payload.runId, { agent, presenter });
|
|
11932
12519
|
void agent.run({
|
|
11933
12520
|
task: payload.content,
|
|
11934
12521
|
maxIterations: payload.maxIterations ?? 50,
|
|
@@ -11964,21 +12551,21 @@ async function startDaemon(args) {
|
|
|
11964
12551
|
});
|
|
11965
12552
|
socket.on("agent.abort", (payload) => {
|
|
11966
12553
|
if (!payload?.runId) return;
|
|
11967
|
-
const
|
|
11968
|
-
if (!
|
|
11969
|
-
|
|
12554
|
+
const entry = activeAgents.get(payload.runId);
|
|
12555
|
+
if (!entry) return;
|
|
12556
|
+
abortActiveAgent(entry);
|
|
11970
12557
|
activeAgents.delete(payload.runId);
|
|
11971
12558
|
pushLog(`aborted run=${payload.runId}`);
|
|
11972
12559
|
});
|
|
11973
12560
|
socket.on("agent.tool_response", (payload) => {
|
|
11974
12561
|
if (!payload?.toolId || typeof payload.response !== "string") return;
|
|
11975
|
-
const agents = payload.runId ? [activeAgents.get(payload.runId)] : [...activeAgents.values()];
|
|
12562
|
+
const agents = payload.runId ? [activeAgents.get(payload.runId)?.agent] : [...activeAgents.values()].map((entry) => entry.agent);
|
|
11976
12563
|
const delivered = agents.some((agent) => agent?.sendToolResponse(payload.toolId, payload.response) === true);
|
|
11977
12564
|
if (!delivered) pushLog(`tool_response missed tool=${payload.toolId}`);
|
|
11978
12565
|
});
|
|
11979
12566
|
socket.on("agent.append_message", (payload) => {
|
|
11980
12567
|
if (!payload?.runId || typeof payload.text !== "string") return;
|
|
11981
|
-
const agent = activeAgents.get(payload.runId);
|
|
12568
|
+
const agent = activeAgents.get(payload.runId)?.agent;
|
|
11982
12569
|
const delivered = agent?.sendUserMessage(payload.text) === true;
|
|
11983
12570
|
socket.emit(delivered ? "agent.accepted" : "agent.rejected", {
|
|
11984
12571
|
runId: payload.runId,
|
|
@@ -12012,21 +12599,47 @@ async function startDaemon(args) {
|
|
|
12012
12599
|
return;
|
|
12013
12600
|
}
|
|
12014
12601
|
if (req.method === "POST" && req.url === "/stop") {
|
|
12015
|
-
for (const
|
|
12602
|
+
for (const entry of activeAgents.values()) abortActiveAgent(entry);
|
|
12016
12603
|
activeAgents.clear();
|
|
12017
12604
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
12018
12605
|
return;
|
|
12019
12606
|
}
|
|
12607
|
+
if (req.method === "POST" && req.url === "/shutdown") {
|
|
12608
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
12609
|
+
setImmediate(queueFullShutdown);
|
|
12610
|
+
return;
|
|
12611
|
+
}
|
|
12020
12612
|
res.writeHead(404, { "content-type": "application/json" }).end(JSON.stringify({ error: "Not found" }));
|
|
12021
12613
|
});
|
|
12614
|
+
const occupiedProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
12615
|
+
if (occupiedProbe.state === "running") {
|
|
12616
|
+
clearInterval(heartbeat);
|
|
12617
|
+
socket.disconnect();
|
|
12618
|
+
throw new Error(
|
|
12619
|
+
`${describeOccupiedLocalDaemon(localApiPort)} (runtime ${occupiedProbe.status.runtimeId})`
|
|
12620
|
+
);
|
|
12621
|
+
}
|
|
12622
|
+
if (occupiedProbe.state === "listening_unauthorized") {
|
|
12623
|
+
clearInterval(heartbeat);
|
|
12624
|
+
socket.disconnect();
|
|
12625
|
+
throw new Error(
|
|
12626
|
+
`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Aiden desktop or run setup again.`
|
|
12627
|
+
);
|
|
12628
|
+
}
|
|
12022
12629
|
await new Promise((resolve2, reject) => {
|
|
12023
12630
|
const onError = (error) => {
|
|
12024
12631
|
clearInterval(heartbeat);
|
|
12025
12632
|
socket.disconnect();
|
|
12026
12633
|
if (error.code === "EADDRINUSE") {
|
|
12027
|
-
|
|
12028
|
-
|
|
12029
|
-
|
|
12634
|
+
void probeLocalDaemonEndpoint(localApiPort).then((probe) => {
|
|
12635
|
+
if (probe.state !== "absent") {
|
|
12636
|
+
reject(new Error(describeOccupiedLocalDaemon(localApiPort)));
|
|
12637
|
+
return;
|
|
12638
|
+
}
|
|
12639
|
+
reject(new Error(
|
|
12640
|
+
`local daemon API port ${localApiPort} is already in use by another process. Otherwise free the port or pass \`--local-api-port\` with a different value.`
|
|
12641
|
+
));
|
|
12642
|
+
});
|
|
12030
12643
|
return;
|
|
12031
12644
|
}
|
|
12032
12645
|
reject(error);
|
|
@@ -12046,8 +12659,8 @@ async function startDaemon(args) {
|
|
|
12046
12659
|
if (stopped) return;
|
|
12047
12660
|
stopped = true;
|
|
12048
12661
|
clearInterval(heartbeat);
|
|
12049
|
-
for (const
|
|
12050
|
-
|
|
12662
|
+
for (const entry of activeAgents.values()) {
|
|
12663
|
+
abortActiveAgent(entry);
|
|
12051
12664
|
}
|
|
12052
12665
|
activeAgents.clear();
|
|
12053
12666
|
socket.disconnect();
|
|
@@ -12055,13 +12668,74 @@ async function startDaemon(args) {
|
|
|
12055
12668
|
localServer.close(() => resolve2());
|
|
12056
12669
|
});
|
|
12057
12670
|
};
|
|
12058
|
-
|
|
12671
|
+
const controller = {
|
|
12672
|
+
runtimeId,
|
|
12673
|
+
wsUrl,
|
|
12674
|
+
localApiPort,
|
|
12675
|
+
localApiToken,
|
|
12676
|
+
stop
|
|
12677
|
+
};
|
|
12678
|
+
const queueFullShutdown = () => {
|
|
12679
|
+
void stop().then(() => controller.whenExternallyStopped?.());
|
|
12680
|
+
};
|
|
12681
|
+
return controller;
|
|
12682
|
+
}
|
|
12683
|
+
async function stopDaemon(args) {
|
|
12684
|
+
const stored = readConfig();
|
|
12685
|
+
const localApiPort = getLocalApiPort(args, stored);
|
|
12686
|
+
const localApiToken = stored.localApiToken;
|
|
12687
|
+
if (!localApiToken) {
|
|
12688
|
+
throw new Error("No local API token in config. Run setup or login first.");
|
|
12689
|
+
}
|
|
12690
|
+
const probe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
12691
|
+
if (probe.state === "listening_unauthorized") {
|
|
12692
|
+
throw new Error(
|
|
12693
|
+
`Daemon is listening on port ${localApiPort} but rejected this config token. Quit Aiden desktop or rerun setup/login to refresh config.`
|
|
12694
|
+
);
|
|
12695
|
+
}
|
|
12696
|
+
if (probe.state === "absent") {
|
|
12697
|
+
console.info("[aiden-agent] No daemon listening", { port: localApiPort });
|
|
12698
|
+
return;
|
|
12699
|
+
}
|
|
12700
|
+
const response = await fetch(`http://127.0.0.1:${localApiPort}/shutdown`, {
|
|
12701
|
+
method: "POST",
|
|
12702
|
+
headers: { authorization: `Bearer ${localApiToken}` },
|
|
12703
|
+
signal: AbortSignal.timeout(5e3)
|
|
12704
|
+
});
|
|
12705
|
+
if (!response.ok) {
|
|
12706
|
+
throw new Error(`Failed to stop daemon: HTTP ${response.status}`);
|
|
12707
|
+
}
|
|
12708
|
+
console.info("[aiden-agent] Daemon stopped", {
|
|
12709
|
+
port: localApiPort,
|
|
12710
|
+
runtimeId: probe.status.runtimeId
|
|
12711
|
+
});
|
|
12059
12712
|
}
|
|
12060
12713
|
async function runDaemon(args) {
|
|
12714
|
+
const stored = readConfig();
|
|
12715
|
+
const localApiPort = getLocalApiPort(args, stored);
|
|
12716
|
+
const localApiToken = stored.localApiToken;
|
|
12717
|
+
const existingProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
12718
|
+
if (existingProbe.state === "running") {
|
|
12719
|
+
console.info("[aiden-agent] Daemon already running", {
|
|
12720
|
+
port: localApiPort,
|
|
12721
|
+
runtimeId: existingProbe.status.runtimeId,
|
|
12722
|
+
connected: existingProbe.status.connected,
|
|
12723
|
+
hint: "If Aiden desktop is open, it manages the daemon automatically."
|
|
12724
|
+
});
|
|
12725
|
+
return;
|
|
12726
|
+
}
|
|
12727
|
+
if (existingProbe.state === "listening_unauthorized") {
|
|
12728
|
+
console.info("[aiden-agent] Daemon already running", {
|
|
12729
|
+
port: localApiPort,
|
|
12730
|
+
hint: "Aiden desktop is using a different local API token than config.json. Quit the desktop app or rerun setup if you need a fresh daemon."
|
|
12731
|
+
});
|
|
12732
|
+
return;
|
|
12733
|
+
}
|
|
12061
12734
|
const controller = await startDaemon(args);
|
|
12062
12735
|
const shutdown = () => {
|
|
12063
12736
|
void controller.stop().finally(() => process.exit(0));
|
|
12064
12737
|
};
|
|
12738
|
+
controller.whenExternallyStopped = shutdown;
|
|
12065
12739
|
process.once("SIGINT", shutdown);
|
|
12066
12740
|
process.once("SIGTERM", shutdown);
|
|
12067
12741
|
await new Promise(() => {
|
|
@@ -12108,13 +12782,16 @@ function logoutDaemon() {
|
|
|
12108
12782
|
removeConfig();
|
|
12109
12783
|
console.info("[aiden-agent] Runtime config removed", { configPath: getConfigPath() });
|
|
12110
12784
|
}
|
|
12111
|
-
function printStatus() {
|
|
12785
|
+
async function printStatus() {
|
|
12112
12786
|
const config = readConfig();
|
|
12113
12787
|
const configured = Boolean(config.runtimeId && config.runtimeToken && config.wsUrl);
|
|
12114
12788
|
const warnings = [];
|
|
12115
12789
|
if ((isLocalEndpoint(config.apiUrl) || isLocalEndpoint(config.wsUrl)) && config.endpointProfile !== "local") {
|
|
12116
12790
|
warnings.push("Configured endpoint points at localhost. Use --profile local only for local development.");
|
|
12117
12791
|
}
|
|
12792
|
+
const localApiPort = config.localApiPort && config.localApiPort > 0 ? config.localApiPort : getLocalApiPort([], config);
|
|
12793
|
+
const localDaemonProbe = config.localApiToken ? await probeLocalDaemonEndpoint(localApiPort, config.localApiToken) : { state: "absent" };
|
|
12794
|
+
const localDaemonRunning = localDaemonProbe.state === "running";
|
|
12118
12795
|
console.info(JSON.stringify({
|
|
12119
12796
|
mode: "runtime",
|
|
12120
12797
|
configured,
|
|
@@ -12123,9 +12800,10 @@ function printStatus() {
|
|
|
12123
12800
|
wsUrl: config.wsUrl ?? null,
|
|
12124
12801
|
endpointProfile: config.endpointProfile ?? null,
|
|
12125
12802
|
localApiPort: config.localApiPort ?? null,
|
|
12803
|
+
localDaemonRunning,
|
|
12126
12804
|
hasLocalApiToken: Boolean(config.localApiToken),
|
|
12127
12805
|
configPath: getConfigPath(),
|
|
12128
|
-
note: configured ? "Durable runtime config is present. Start it with: aiden-agent daemon" : "No durable runtime configured. Cloud sandbox sessions are launched by Aiden with session env vars and do not appear in this local config.",
|
|
12806
|
+
note: configured ? localDaemonRunning ? "Local daemon is running. Stop it with: aiden-agent stop" : "Durable runtime config is present. Start it with: aiden-agent daemon" : "No durable runtime configured. Cloud sandbox sessions are launched by Aiden with session env vars and do not appear in this local config.",
|
|
12129
12807
|
warnings
|
|
12130
12808
|
}, null, 2));
|
|
12131
12809
|
}
|
|
@@ -12144,7 +12822,7 @@ async function printDoctor(args = []) {
|
|
|
12144
12822
|
authorization: `Bearer ${config.runtimeToken}`,
|
|
12145
12823
|
"content-type": "application/json"
|
|
12146
12824
|
},
|
|
12147
|
-
body: JSON.stringify({ capabilities })
|
|
12825
|
+
body: JSON.stringify({ capabilities, metadata: await collectRuntimeMetadataWithProviderLimits() })
|
|
12148
12826
|
});
|
|
12149
12827
|
if (!response.ok) {
|
|
12150
12828
|
syncError = `${response.status} ${await response.text()}`;
|
|
@@ -12177,6 +12855,7 @@ Commands:
|
|
|
12177
12855
|
setup Register this computer with a setup token from Aiden
|
|
12178
12856
|
login Register this computer through browser authorization
|
|
12179
12857
|
daemon Start the durable runtime daemon
|
|
12858
|
+
stop Stop the local daemon (or active runs only via the local API /stop)
|
|
12180
12859
|
status Show local runtime configuration without secrets
|
|
12181
12860
|
doctor Check local CLI capabilities
|
|
12182
12861
|
logout Remove local runtime configuration
|
|
@@ -12188,6 +12867,7 @@ Common flows:
|
|
|
12188
12867
|
aiden-agent setup --setup-token <token>
|
|
12189
12868
|
aiden-agent setup --setup-token <token> --scope user
|
|
12190
12869
|
aiden-agent daemon
|
|
12870
|
+
aiden-agent stop
|
|
12191
12871
|
|
|
12192
12872
|
Local development:
|
|
12193
12873
|
aiden-agent login --profile local
|
|
@@ -12274,8 +12954,12 @@ async function main() {
|
|
|
12274
12954
|
await runDaemon(commandArgs);
|
|
12275
12955
|
return;
|
|
12276
12956
|
}
|
|
12957
|
+
if (command === "stop") {
|
|
12958
|
+
await stopDaemon(commandArgs);
|
|
12959
|
+
return;
|
|
12960
|
+
}
|
|
12277
12961
|
if (command === "status") {
|
|
12278
|
-
printStatus();
|
|
12962
|
+
await printStatus();
|
|
12279
12963
|
return;
|
|
12280
12964
|
}
|
|
12281
12965
|
if (command === "doctor") {
|