@aiden-ade/sandbox-agent 0.1.1 → 0.1.4
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/index.cjs +1086 -408
- package/package.json +5 -3
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 spawn2 = 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 = spawn2(process.argv[0], ["-e", execString]);
|
|
336
336
|
var statusText;
|
|
337
337
|
while (fs.existsSync(syncFile)) {
|
|
338
338
|
}
|
|
@@ -921,21 +921,19 @@ var require_supports_color = __commonJS({
|
|
|
921
921
|
var tty = require("tty");
|
|
922
922
|
var hasFlag = require_has_flag();
|
|
923
923
|
var { env } = process;
|
|
924
|
-
var
|
|
924
|
+
var forceColor;
|
|
925
925
|
if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) {
|
|
926
|
-
|
|
926
|
+
forceColor = 0;
|
|
927
927
|
} else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) {
|
|
928
|
-
|
|
928
|
+
forceColor = 1;
|
|
929
929
|
}
|
|
930
|
-
|
|
931
|
-
if (
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
}
|
|
938
|
-
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
930
|
+
if ("FORCE_COLOR" in env) {
|
|
931
|
+
if (env.FORCE_COLOR === "true") {
|
|
932
|
+
forceColor = 1;
|
|
933
|
+
} else if (env.FORCE_COLOR === "false") {
|
|
934
|
+
forceColor = 0;
|
|
935
|
+
} else {
|
|
936
|
+
forceColor = env.FORCE_COLOR.length === 0 ? 1 : Math.min(parseInt(env.FORCE_COLOR, 10), 3);
|
|
939
937
|
}
|
|
940
938
|
}
|
|
941
939
|
function translateLevel(level) {
|
|
@@ -949,22 +947,15 @@ var require_supports_color = __commonJS({
|
|
|
949
947
|
has16m: level >= 3
|
|
950
948
|
};
|
|
951
949
|
}
|
|
952
|
-
function supportsColor(haveStream,
|
|
953
|
-
const noFlagForceColor = envForceColor();
|
|
954
|
-
if (noFlagForceColor !== void 0) {
|
|
955
|
-
flagForceColor = noFlagForceColor;
|
|
956
|
-
}
|
|
957
|
-
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
950
|
+
function supportsColor(haveStream, streamIsTTY) {
|
|
958
951
|
if (forceColor === 0) {
|
|
959
952
|
return 0;
|
|
960
953
|
}
|
|
961
|
-
if (
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
return 2;
|
|
967
|
-
}
|
|
954
|
+
if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) {
|
|
955
|
+
return 3;
|
|
956
|
+
}
|
|
957
|
+
if (hasFlag("color=256")) {
|
|
958
|
+
return 2;
|
|
968
959
|
}
|
|
969
960
|
if (haveStream && !streamIsTTY && forceColor === void 0) {
|
|
970
961
|
return 0;
|
|
@@ -981,7 +972,7 @@ var require_supports_color = __commonJS({
|
|
|
981
972
|
return 1;
|
|
982
973
|
}
|
|
983
974
|
if ("CI" in env) {
|
|
984
|
-
if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"
|
|
975
|
+
if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE"].some((sign) => sign in env) || env.CI_NAME === "codeship") {
|
|
985
976
|
return 1;
|
|
986
977
|
}
|
|
987
978
|
return min;
|
|
@@ -993,7 +984,7 @@ var require_supports_color = __commonJS({
|
|
|
993
984
|
return 3;
|
|
994
985
|
}
|
|
995
986
|
if ("TERM_PROGRAM" in env) {
|
|
996
|
-
const version =
|
|
987
|
+
const version = parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10);
|
|
997
988
|
switch (env.TERM_PROGRAM) {
|
|
998
989
|
case "iTerm.app":
|
|
999
990
|
return version >= 3 ? 3 : 2;
|
|
@@ -1012,17 +1003,14 @@ var require_supports_color = __commonJS({
|
|
|
1012
1003
|
}
|
|
1013
1004
|
return min;
|
|
1014
1005
|
}
|
|
1015
|
-
function getSupportLevel(stream
|
|
1016
|
-
const level = supportsColor(stream,
|
|
1017
|
-
streamIsTTY: stream && stream.isTTY,
|
|
1018
|
-
...options
|
|
1019
|
-
});
|
|
1006
|
+
function getSupportLevel(stream) {
|
|
1007
|
+
const level = supportsColor(stream, stream && stream.isTTY);
|
|
1020
1008
|
return translateLevel(level);
|
|
1021
1009
|
}
|
|
1022
1010
|
module2.exports = {
|
|
1023
1011
|
supportsColor: getSupportLevel,
|
|
1024
|
-
stdout:
|
|
1025
|
-
stderr:
|
|
1012
|
+
stdout: translateLevel(supportsColor(true, tty.isatty(1))),
|
|
1013
|
+
stderr: translateLevel(supportsColor(true, tty.isatty(2)))
|
|
1026
1014
|
};
|
|
1027
1015
|
}
|
|
1028
1016
|
});
|
|
@@ -4822,14 +4810,76 @@ var require_websocket_server = __commonJS({
|
|
|
4822
4810
|
}
|
|
4823
4811
|
});
|
|
4824
4812
|
|
|
4825
|
-
//
|
|
4826
|
-
var
|
|
4827
|
-
var
|
|
4828
|
-
var
|
|
4813
|
+
// ../agent-core/dist/index.js
|
|
4814
|
+
var import_child_process = require("child_process");
|
|
4815
|
+
var import_fs = require("fs");
|
|
4816
|
+
var import_os = require("os");
|
|
4817
|
+
var import_path = require("path");
|
|
4818
|
+
var import_readline = require("readline");
|
|
4819
|
+
|
|
4820
|
+
// ../shared/dist/constants/agent.js
|
|
4821
|
+
var INTERACTIVE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
4822
|
+
"askuserquestion",
|
|
4823
|
+
"exitplanmode",
|
|
4824
|
+
"enterplanmode"
|
|
4825
|
+
]);
|
|
4826
|
+
|
|
4827
|
+
// ../agent-core/dist/index.js
|
|
4828
|
+
var import_fs2 = require("fs");
|
|
4829
|
+
var import_os2 = require("os");
|
|
4830
|
+
var import_path2 = require("path");
|
|
4829
4831
|
|
|
4830
|
-
//
|
|
4831
|
-
var
|
|
4832
|
-
|
|
4832
|
+
// ../shared/dist/agent/system-prompt.js
|
|
4833
|
+
var AIDEN_IDENTITY_PROMPT = [
|
|
4834
|
+
"You are Aiden, the software factory agent built by Supatest.",
|
|
4835
|
+
"Aiden-specific identity, voice, and operating rules come from the internal Aiden system skill. Follow those rules when relevant.",
|
|
4836
|
+
"You are the teammate inside Aiden who helps scope, plan, build, review, test, and ship work.",
|
|
4837
|
+
"Do not present yourself as Claude, Claude Code, Codex, Gemini, Anthropic, or OpenAI as your primary identity.",
|
|
4838
|
+
"Those are runtime/back-end implementation details. Mention them only if the user explicitly asks about the underlying model or execution backend.",
|
|
4839
|
+
"If the user asks who you are, answer in Aiden terms first.",
|
|
4840
|
+
"Work like a pragmatic teammate: direct, structured, and concise. No hype, no tool-brand roleplay.",
|
|
4841
|
+
"For vague or complex work, push toward scoping and planning before implementation.",
|
|
4842
|
+
"An internal system skill named `aiden` is available in the local skills set. Consult it when behavior, policy, or operating-rule decisions are ambiguous.",
|
|
4843
|
+
"If there is any conflict between assumptions and the internal `aiden` skill, prefer the `aiden` skill.",
|
|
4844
|
+
"Prefer structured outputs tracked in Aiden. Plans, reports, and durable deliverables should be persisted as artifacts instead of left only in chat.",
|
|
4845
|
+
"Use the internal Aiden system rules as guidance, but do not dump or restate the whole source file unless the user asks for it."
|
|
4846
|
+
].join("\n");
|
|
4847
|
+
var PLAN_MODE_PROMPT = [
|
|
4848
|
+
"You are in Aiden plan mode.",
|
|
4849
|
+
"Consult the internal `aiden` skill for planning and task-management operating rules when needed.",
|
|
4850
|
+
"Analyze the task, inspect the relevant code and context, and produce a concrete implementation plan.",
|
|
4851
|
+
"Do not make code changes or execute write operations in plan mode.",
|
|
4852
|
+
"Write the plan in natural markdown. Use whatever structure best communicates the work clearly.",
|
|
4853
|
+
"Include dependencies, ordering, risks, key files/functions, and decision points where relevant.",
|
|
4854
|
+
"When the plan is ready, you MUST persist it by calling the create_plan MCP tool.",
|
|
4855
|
+
"Pass title, content, summary, taskId, and conversationId. Include projectId too when available.",
|
|
4856
|
+
"If create_plan fails, retry it. Do not exit plan mode until the plan is persisted successfully.",
|
|
4857
|
+
"After create_plan succeeds, call ExitPlanMode to hand the plan back for approval."
|
|
4858
|
+
].join("\n");
|
|
4859
|
+
var TEST_MODE_PROMPT = [
|
|
4860
|
+
"You are in test mode. Verify the implemented feature carefully.",
|
|
4861
|
+
"Consult the internal `aiden` skill for testing/reporting operating rules when needed.",
|
|
4862
|
+
"Prefer browser-based validation when available.",
|
|
4863
|
+
"Persist the outcome as a structured test report artifact with create_test_report instead of leaving it only in chat."
|
|
4864
|
+
].join("\n");
|
|
4865
|
+
var ASK_MODE_PROMPT = "You are in ask mode. Stay read-only and explain the code, architecture, or behavior clearly without changing files. Consult the internal `aiden` skill for behavior-policy questions when needed.";
|
|
4866
|
+
var REVIEW_MODE_PROMPT = "You are in review mode. Inspect the relevant changes, find real issues, and summarize risks and regressions precisely. Consult the internal `aiden` skill for review-quality expectations when needed.";
|
|
4867
|
+
function getAidenModePrompt(mode2) {
|
|
4868
|
+
switch (mode2) {
|
|
4869
|
+
case "plan":
|
|
4870
|
+
return PLAN_MODE_PROMPT;
|
|
4871
|
+
case "test":
|
|
4872
|
+
return TEST_MODE_PROMPT;
|
|
4873
|
+
case "ask":
|
|
4874
|
+
return ASK_MODE_PROMPT;
|
|
4875
|
+
case "review":
|
|
4876
|
+
return REVIEW_MODE_PROMPT;
|
|
4877
|
+
default:
|
|
4878
|
+
return void 0;
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
|
|
4882
|
+
// ../agent-core/dist/index.js
|
|
4833
4883
|
function formatCliSpawnError(command, error) {
|
|
4834
4884
|
if (error.code === "ENOENT") {
|
|
4835
4885
|
return new Error(
|
|
@@ -4838,6 +4888,32 @@ function formatCliSpawnError(command, error) {
|
|
|
4838
4888
|
}
|
|
4839
4889
|
return error;
|
|
4840
4890
|
}
|
|
4891
|
+
function isLikelyProviderAuthError(stderr) {
|
|
4892
|
+
const lower = stderr.toLowerCase();
|
|
4893
|
+
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");
|
|
4894
|
+
}
|
|
4895
|
+
function classifyCliError(stderr, exitCode) {
|
|
4896
|
+
const lower = stderr.toLowerCase();
|
|
4897
|
+
if (lower.includes("rate_limit") || lower.includes("rate limit") || lower.includes("429"))
|
|
4898
|
+
return "Rate limited by API. Try again shortly.";
|
|
4899
|
+
if (isLikelyProviderAuthError(stderr))
|
|
4900
|
+
return "API key invalid or expired. Check your provider settings.";
|
|
4901
|
+
if (lower.includes("overloaded") || lower.includes("503") || lower.includes("service unavailable"))
|
|
4902
|
+
return "API is overloaded. Will retry automatically.";
|
|
4903
|
+
if (lower.includes("context_length") || lower.includes("too long") || lower.includes("max tokens") || lower.includes("context window"))
|
|
4904
|
+
return "Context window exceeded. Try a shorter prompt or start a new session.";
|
|
4905
|
+
if (lower.includes("econnrefused") || lower.includes("network") || lower.includes("enotfound") || lower.includes("timeout") || lower.includes("etimedout"))
|
|
4906
|
+
return "Network connection failed. Check your internet connection.";
|
|
4907
|
+
if (lower.includes("permission denied") || lower.includes("eacces"))
|
|
4908
|
+
return "Permission denied. Check file or command permissions.";
|
|
4909
|
+
if (lower.includes("out of memory") || lower.includes("enomem"))
|
|
4910
|
+
return "Out of memory. Close other applications and try again.";
|
|
4911
|
+
return stderr.trim() || `Process exited with code ${exitCode}`;
|
|
4912
|
+
}
|
|
4913
|
+
function isTransientError(errorMessage) {
|
|
4914
|
+
const lower = errorMessage.toLowerCase();
|
|
4915
|
+
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");
|
|
4916
|
+
}
|
|
4841
4917
|
function buildPromptWithSystem(config, promptText) {
|
|
4842
4918
|
const parts2 = [
|
|
4843
4919
|
config.systemPrompt?.trim(),
|
|
@@ -4856,13 +4932,16 @@ function buildPlanModePrefix(promptText) {
|
|
|
4856
4932
|
].join("\n");
|
|
4857
4933
|
}
|
|
4858
4934
|
function spawnCli(command, args, context) {
|
|
4859
|
-
const
|
|
4860
|
-
return (0,
|
|
4935
|
+
const isWindows = process.platform === "win32";
|
|
4936
|
+
return (0, import_child_process.spawn)(command, args, {
|
|
4861
4937
|
cwd: context.cwd,
|
|
4862
4938
|
env: context.env,
|
|
4863
4939
|
stdio: ["pipe", "pipe", "pipe"],
|
|
4864
|
-
shell:
|
|
4865
|
-
|
|
4940
|
+
shell: isWindows,
|
|
4941
|
+
// Detached on Unix creates a new process group so we can kill the entire tree
|
|
4942
|
+
// (CLI tools spawn child processes for tool execution that would otherwise be orphaned).
|
|
4943
|
+
// On Windows, detached opens a new console — shell mode handles grouping instead.
|
|
4944
|
+
detached: !isWindows
|
|
4866
4945
|
});
|
|
4867
4946
|
}
|
|
4868
4947
|
function createGenericCliBackend(options) {
|
|
@@ -4874,30 +4953,39 @@ function createGenericCliBackend(options) {
|
|
|
4874
4953
|
const state = {
|
|
4875
4954
|
process: null,
|
|
4876
4955
|
iterations: 0,
|
|
4877
|
-
summary: ""
|
|
4956
|
+
summary: "",
|
|
4957
|
+
allowContinuation: options.allowContinuation ?? false
|
|
4878
4958
|
};
|
|
4879
4959
|
const prompt = options.augmentPrompt?.(context) ?? (context.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(context.config, context.promptText)) : buildPromptWithSystem(context.config, context.promptText));
|
|
4880
4960
|
const args = options.buildArgs?.(context, prompt) ?? options.args.map((arg) => arg === "{{prompt}}" ? prompt : arg);
|
|
4881
4961
|
const child = spawnCli(options.command, args, context);
|
|
4882
4962
|
state.process = child;
|
|
4963
|
+
context.onProcessSpawned?.(child);
|
|
4883
4964
|
const safeArgs = args.map(
|
|
4884
|
-
(a, i) =>
|
|
4885
|
-
// Redact API keys but show everything else for debugging
|
|
4886
|
-
i > 0 && args[i - 1] === "--system-prompt" ? `"<system-prompt ${a.length} chars>"` : a
|
|
4887
|
-
)
|
|
4965
|
+
(a, i) => i > 0 && args[i - 1] === "--system-prompt" ? `"<system-prompt ${a.length} chars>"` : a
|
|
4888
4966
|
);
|
|
4889
4967
|
console.info(`[${options.kind}] Spawning: ${options.command} ${safeArgs.join(" ")}`);
|
|
4890
4968
|
presenter.recordRawTranscript?.("system", `${options.command} ${args.join(" ")}`, {
|
|
4891
4969
|
backendKind: options.kind
|
|
4892
4970
|
});
|
|
4893
|
-
|
|
4971
|
+
child.stdin.on("error", (err) => {
|
|
4972
|
+
if (err.code !== "EPIPE") {
|
|
4973
|
+
console.error(`[${options.kind}] stdin error:`, err);
|
|
4974
|
+
}
|
|
4975
|
+
});
|
|
4976
|
+
if (options.stdinWriter) {
|
|
4977
|
+
options.stdinWriter(child.stdin, context);
|
|
4978
|
+
} else if (options.promptViaStdin) {
|
|
4894
4979
|
child.stdin.write(prompt);
|
|
4895
4980
|
child.stdin.end();
|
|
4896
4981
|
} else {
|
|
4897
4982
|
child.stdin.end();
|
|
4898
4983
|
}
|
|
4899
|
-
|
|
4900
|
-
|
|
4984
|
+
if (options.keepStdinOpen) {
|
|
4985
|
+
state.stdin = child.stdin;
|
|
4986
|
+
}
|
|
4987
|
+
const stdoutRl = (0, import_readline.createInterface)({ input: child.stdout });
|
|
4988
|
+
const stderrRl = (0, import_readline.createInterface)({ input: child.stderr });
|
|
4901
4989
|
const stderrLines = [];
|
|
4902
4990
|
stdoutRl.on("line", (line) => {
|
|
4903
4991
|
presenter.recordRawTranscript?.("stdout", line);
|
|
@@ -4919,12 +5007,108 @@ function createGenericCliBackend(options) {
|
|
|
4919
5007
|
void presenter.onLog(`[${options.kind}] ${line}`);
|
|
4920
5008
|
}
|
|
4921
5009
|
});
|
|
4922
|
-
|
|
4923
|
-
|
|
4924
|
-
|
|
5010
|
+
let exitCode;
|
|
5011
|
+
try {
|
|
5012
|
+
const exitPromise = new Promise((resolve2, reject) => {
|
|
5013
|
+
child.on("error", (error) => {
|
|
5014
|
+
reject(formatCliSpawnError(options.command, error));
|
|
5015
|
+
});
|
|
5016
|
+
child.on("exit", (code) => resolve2(code ?? 0));
|
|
5017
|
+
const onAbort = () => {
|
|
5018
|
+
const pid = child.pid;
|
|
5019
|
+
if (!pid) return;
|
|
5020
|
+
try {
|
|
5021
|
+
process.kill(-pid, "SIGTERM");
|
|
5022
|
+
} catch {
|
|
5023
|
+
try {
|
|
5024
|
+
child.kill("SIGTERM");
|
|
5025
|
+
} catch {
|
|
5026
|
+
}
|
|
5027
|
+
}
|
|
5028
|
+
};
|
|
5029
|
+
context.abortController.signal.addEventListener("abort", onAbort, { once: true });
|
|
4925
5030
|
});
|
|
4926
|
-
|
|
4927
|
-
|
|
5031
|
+
if (options.keepStdinOpen) {
|
|
5032
|
+
if (options.allowContinuation) {
|
|
5033
|
+
const IDLE_MS = options.continuationIdleMs ?? 10 * 60 * 1e3;
|
|
5034
|
+
let idleTimer = null;
|
|
5035
|
+
let heartbeatInterval = null;
|
|
5036
|
+
const resetIdleTimer = () => {
|
|
5037
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
5038
|
+
idleTimer = setTimeout(() => {
|
|
5039
|
+
if (state.stdin && !state.stdin.destroyed) {
|
|
5040
|
+
state.stdin.end();
|
|
5041
|
+
console.info(`[${options.kind}] Continuation idle timeout (${IDLE_MS}ms) \u2014 closing stdin`);
|
|
5042
|
+
}
|
|
5043
|
+
}, IDLE_MS);
|
|
5044
|
+
};
|
|
5045
|
+
state.onNewOutput = resetIdleTimer;
|
|
5046
|
+
state.stopHeartbeat = () => {
|
|
5047
|
+
if (heartbeatInterval) {
|
|
5048
|
+
clearInterval(heartbeatInterval);
|
|
5049
|
+
heartbeatInterval = null;
|
|
5050
|
+
}
|
|
5051
|
+
};
|
|
5052
|
+
state.startHeartbeat = () => {
|
|
5053
|
+
state.stopHeartbeat?.();
|
|
5054
|
+
heartbeatInterval = setInterval(() => {
|
|
5055
|
+
void presenter.onActivity?.("background_work");
|
|
5056
|
+
}, 3e4);
|
|
5057
|
+
};
|
|
5058
|
+
resetIdleTimer();
|
|
5059
|
+
exitCode = await exitPromise;
|
|
5060
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
5061
|
+
state.stopHeartbeat?.();
|
|
5062
|
+
} else {
|
|
5063
|
+
const resultPromise = new Promise((resolve2) => {
|
|
5064
|
+
state.onResultReceived = () => resolve2("result_received");
|
|
5065
|
+
if (state.resultReceived) resolve2("result_received");
|
|
5066
|
+
});
|
|
5067
|
+
const raceResult = await Promise.race([exitPromise, resultPromise]);
|
|
5068
|
+
if (raceResult === "result_received") {
|
|
5069
|
+
const GRACE_MS = 5e3;
|
|
5070
|
+
const gracedExit = await Promise.race([
|
|
5071
|
+
exitPromise,
|
|
5072
|
+
new Promise((resolve2) => setTimeout(() => resolve2(0), GRACE_MS))
|
|
5073
|
+
]);
|
|
5074
|
+
exitCode = typeof gracedExit === "number" ? gracedExit : 0;
|
|
5075
|
+
if (child.exitCode === null && !child.killed) {
|
|
5076
|
+
console.warn(`[${options.kind}] Process still alive ${GRACE_MS}ms after result event, killing`);
|
|
5077
|
+
try {
|
|
5078
|
+
if (process.platform === "win32") {
|
|
5079
|
+
child.kill("SIGKILL");
|
|
5080
|
+
} else if (child.pid) {
|
|
5081
|
+
process.kill(-child.pid, "SIGKILL");
|
|
5082
|
+
}
|
|
5083
|
+
} catch {
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
} else {
|
|
5087
|
+
exitCode = raceResult;
|
|
5088
|
+
}
|
|
5089
|
+
}
|
|
5090
|
+
} else {
|
|
5091
|
+
exitCode = await exitPromise;
|
|
5092
|
+
}
|
|
5093
|
+
} finally {
|
|
5094
|
+
stdoutRl.close();
|
|
5095
|
+
stderrRl.close();
|
|
5096
|
+
}
|
|
5097
|
+
if (context.abortController.signal.aborted) {
|
|
5098
|
+
return {
|
|
5099
|
+
success: false,
|
|
5100
|
+
summary: "Interrupted by user",
|
|
5101
|
+
filesModified: [],
|
|
5102
|
+
planFilesCreated: [],
|
|
5103
|
+
iterations: Math.max(state.iterations, 1),
|
|
5104
|
+
error: "Interrupted by user",
|
|
5105
|
+
providerSessionId: state.runtimeSessionId,
|
|
5106
|
+
runtimeSessionId: state.runtimeSessionId,
|
|
5107
|
+
backendKind: options.kind,
|
|
5108
|
+
supportTier: options.supportTier,
|
|
5109
|
+
usage: state.usage
|
|
5110
|
+
};
|
|
5111
|
+
}
|
|
4928
5112
|
if (exitCode !== 0) {
|
|
4929
5113
|
console.error(`[${options.kind}] Process exited with code ${exitCode}`, {
|
|
4930
5114
|
stderrLines: stderrLines.slice(-10),
|
|
@@ -4937,14 +5121,17 @@ function createGenericCliBackend(options) {
|
|
|
4937
5121
|
if (hasRenderableTurn) {
|
|
4938
5122
|
await presenter.onTurnComplete([]);
|
|
4939
5123
|
}
|
|
4940
|
-
const
|
|
5124
|
+
const stderrText = stderrLines.join("\n").trim();
|
|
5125
|
+
const hasStructuredError = exitCode === 0 && !!state.error?.trim();
|
|
5126
|
+
const failed = exitCode !== 0 || hasStructuredError;
|
|
5127
|
+
const summary = state.summary.trim() || state.error?.trim() || (failed ? "Task failed" : "Task completed");
|
|
4941
5128
|
return {
|
|
4942
|
-
success:
|
|
5129
|
+
success: !failed,
|
|
4943
5130
|
summary,
|
|
4944
5131
|
filesModified: [],
|
|
4945
5132
|
planFilesCreated: [],
|
|
4946
5133
|
iterations: Math.max(state.iterations, 1),
|
|
4947
|
-
error:
|
|
5134
|
+
error: failed ? classifyCliError(state.error?.trim() || stderrText || "", exitCode) : void 0,
|
|
4948
5135
|
providerSessionId: state.runtimeSessionId,
|
|
4949
5136
|
runtimeSessionId: state.runtimeSessionId,
|
|
4950
5137
|
backendKind: options.kind,
|
|
@@ -4955,12 +5142,8 @@ function createGenericCliBackend(options) {
|
|
|
4955
5142
|
};
|
|
4956
5143
|
}
|
|
4957
5144
|
function extractToolResultContent(content) {
|
|
4958
|
-
if (typeof content === "string")
|
|
4959
|
-
|
|
4960
|
-
}
|
|
4961
|
-
if (!Array.isArray(content)) {
|
|
4962
|
-
return "";
|
|
4963
|
-
}
|
|
5145
|
+
if (typeof content === "string") return content;
|
|
5146
|
+
if (!Array.isArray(content)) return "";
|
|
4964
5147
|
return content.map((block) => {
|
|
4965
5148
|
if (typeof block === "object" && block !== null && "type" in block && block.type === "text" && "text" in block && typeof block.text === "string") {
|
|
4966
5149
|
return block.text;
|
|
@@ -4974,10 +5157,7 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
4974
5157
|
try {
|
|
4975
5158
|
parsed = JSON.parse(line);
|
|
4976
5159
|
} catch {
|
|
4977
|
-
|
|
4978
|
-
`;
|
|
4979
|
-
void presenter.onAssistantText(`${line}
|
|
4980
|
-
`);
|
|
5160
|
+
void presenter.onLog(`[claude_cli] ${line}`);
|
|
4981
5161
|
return;
|
|
4982
5162
|
}
|
|
4983
5163
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
@@ -4994,13 +5174,22 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
4994
5174
|
break;
|
|
4995
5175
|
}
|
|
4996
5176
|
case "assistant": {
|
|
5177
|
+
if (presenter.pendingAskUserToolIds?.size) {
|
|
5178
|
+
console.info(`[claude_cli] Suppressing fallback assistant turn \u2014 ${presenter.pendingAskUserToolIds.size} AskUserQuestion(s) still pending`);
|
|
5179
|
+
break;
|
|
5180
|
+
}
|
|
5181
|
+
if (state.resultReceived && state.allowContinuation) {
|
|
5182
|
+
console.info("[claude_cli] Continuation turn detected \u2014 restarting presenter");
|
|
5183
|
+
state.resultReceived = false;
|
|
5184
|
+
state.stopHeartbeat?.();
|
|
5185
|
+
void presenter.onStart(context.config);
|
|
5186
|
+
}
|
|
5187
|
+
state.onNewOutput?.();
|
|
4997
5188
|
state.iterations += 1;
|
|
4998
5189
|
const message = typeof parsed.message === "object" && parsed.message !== null ? parsed.message : null;
|
|
4999
5190
|
const content = Array.isArray(message?.content) ? message.content : [];
|
|
5000
5191
|
for (const block of content) {
|
|
5001
|
-
if (typeof block !== "object" || block === null || !("type" in block))
|
|
5002
|
-
continue;
|
|
5003
|
-
}
|
|
5192
|
+
if (typeof block !== "object" || block === null || !("type" in block)) continue;
|
|
5004
5193
|
if (block.type === "text" && typeof block.text === "string") {
|
|
5005
5194
|
const text = block.text;
|
|
5006
5195
|
state.summary += `${text}
|
|
@@ -5015,6 +5204,25 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
5015
5204
|
if (block.type === "tool_use" && typeof block.id === "string" && typeof block.name === "string") {
|
|
5016
5205
|
const toolBlock = block;
|
|
5017
5206
|
void presenter.onToolUse(toolBlock.name, toolBlock.input ?? {}, toolBlock.id);
|
|
5207
|
+
const lowerName = toolBlock.name.toLowerCase();
|
|
5208
|
+
if (INTERACTIVE_TOOL_NAMES.has(lowerName)) {
|
|
5209
|
+
if (!state.suppressedToolResultIds) state.suppressedToolResultIds = /* @__PURE__ */ new Set();
|
|
5210
|
+
state.suppressedToolResultIds.add(toolBlock.id);
|
|
5211
|
+
}
|
|
5212
|
+
const toolInput = toolBlock.input ?? {};
|
|
5213
|
+
if (lowerName === "askuserquestion") {
|
|
5214
|
+
if (!presenter.pendingAskUserToolIds) presenter.pendingAskUserToolIds = /* @__PURE__ */ new Set();
|
|
5215
|
+
presenter.pendingAskUserToolIds.add(toolBlock.id);
|
|
5216
|
+
console.info(`[claude_cli] AskUserQuestion tool_use detected \u2014 id=${toolBlock.id}, pendingSize=${presenter.pendingAskUserToolIds.size}, presenterHasProp=${Object.prototype.hasOwnProperty.call(presenter, "pendingAskUserToolIds")}`);
|
|
5217
|
+
const questions = Array.isArray(toolInput.questions) ? toolInput.questions : [];
|
|
5218
|
+
void presenter.onAskUser?.(toolBlock.id, questions);
|
|
5219
|
+
console.info(`[claude_cli] After onAskUser \u2014 pendingSize=${presenter.pendingAskUserToolIds?.size}`);
|
|
5220
|
+
} else if (lowerName === "exitplanmode") {
|
|
5221
|
+
void presenter.onExitPlanMode?.(toolBlock.id, toolInput);
|
|
5222
|
+
} else if (lowerName === "todowrite") {
|
|
5223
|
+
const todos = Array.isArray(toolInput.todos) ? toolInput.todos : [];
|
|
5224
|
+
void presenter.onTodoWrite?.(todos);
|
|
5225
|
+
}
|
|
5018
5226
|
}
|
|
5019
5227
|
}
|
|
5020
5228
|
break;
|
|
@@ -5024,19 +5232,26 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
5024
5232
|
const content = Array.isArray(message?.content) ? message.content : [];
|
|
5025
5233
|
for (const block of content) {
|
|
5026
5234
|
if (typeof block === "object" && block !== null && "type" in block && block.type === "tool_result" && "tool_use_id" in block && typeof block.tool_use_id === "string") {
|
|
5235
|
+
if (state.suppressedToolResultIds?.has(block.tool_use_id)) {
|
|
5236
|
+
console.info(`[claude_cli] Suppressed auto-generated tool_result for ${block.tool_use_id}`);
|
|
5237
|
+
continue;
|
|
5238
|
+
}
|
|
5239
|
+
const isError = "is_error" in block && block.is_error === true ? true : void 0;
|
|
5027
5240
|
void presenter.onToolResult?.(
|
|
5028
5241
|
block.tool_use_id,
|
|
5029
|
-
extractToolResultContent(block.content)
|
|
5242
|
+
extractToolResultContent(block.content),
|
|
5243
|
+
isError
|
|
5030
5244
|
);
|
|
5031
5245
|
}
|
|
5032
5246
|
}
|
|
5033
5247
|
break;
|
|
5034
5248
|
}
|
|
5035
5249
|
case "result": {
|
|
5036
|
-
const
|
|
5037
|
-
const
|
|
5038
|
-
const
|
|
5039
|
-
const
|
|
5250
|
+
const usage = parsed.usage;
|
|
5251
|
+
const inputTokens = typeof usage?.input_tokens === "number" ? usage.input_tokens : 0;
|
|
5252
|
+
const outputTokens = typeof usage?.output_tokens === "number" ? usage.output_tokens : 0;
|
|
5253
|
+
const cacheReadTokens = typeof usage?.cache_read_input_tokens === "number" ? usage.cache_read_input_tokens : 0;
|
|
5254
|
+
const cacheCreationTokens = typeof usage?.cache_creation_input_tokens === "number" ? usage.cache_creation_input_tokens : 0;
|
|
5040
5255
|
state.usage = {
|
|
5041
5256
|
model: typeof parsed.model === "string" ? parsed.model : typeof context.config.selectedModel === "string" ? context.config.selectedModel : "claude",
|
|
5042
5257
|
numTurns: typeof parsed.num_turns === "number" ? parsed.num_turns : Math.max(state.iterations, 1),
|
|
@@ -5065,17 +5280,46 @@ function parseClaudeStructuredLine(line, context, state) {
|
|
|
5065
5280
|
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
5066
5281
|
const errMsg = typeof parsed.error === "string" && parsed.error.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || typeof parsed.message === "string" && parsed.message.trim() || typeof parsed.result === "string" && parsed.result.trim() || subtype;
|
|
5067
5282
|
state.error = errMsg;
|
|
5068
|
-
|
|
5283
|
+
console.error("[claude_cli] result subtype=", subtype, "error fields:", JSON.stringify({
|
|
5069
5284
|
error: parsed.error,
|
|
5070
5285
|
message: parsed.message,
|
|
5071
5286
|
result: typeof parsed.result === "string" ? parsed.result.slice(0, 200) : parsed.result,
|
|
5072
5287
|
session_id: parsed.session_id,
|
|
5073
5288
|
model: parsed.model,
|
|
5074
|
-
num_turns: parsed.num_turns
|
|
5075
|
-
|
|
5289
|
+
num_turns: parsed.num_turns
|
|
5290
|
+
}));
|
|
5291
|
+
}
|
|
5292
|
+
console.info(`[claude_cli] result event \u2014 pendingAskUserToolIds size=${presenter.pendingAskUserToolIds?.size ?? "undefined"}, hasOwnProp=${Object.prototype.hasOwnProperty.call(presenter, "pendingAskUserToolIds")}`);
|
|
5293
|
+
if (presenter.pendingAskUserToolIds?.size) {
|
|
5294
|
+
console.info(`[claude_cli] Keeping stdin open \u2014 ${presenter.pendingAskUserToolIds.size} AskUserQuestion(s) awaiting user response`);
|
|
5295
|
+
state.onNewOutput?.();
|
|
5296
|
+
break;
|
|
5297
|
+
}
|
|
5298
|
+
state.resultReceived = true;
|
|
5299
|
+
if (state.allowContinuation) {
|
|
5300
|
+
const continuationResult = {
|
|
5301
|
+
success: !state.error,
|
|
5302
|
+
summary: state.summary || "",
|
|
5303
|
+
filesModified: [],
|
|
5304
|
+
planFilesCreated: [],
|
|
5305
|
+
iterations: Math.max(state.iterations, 1),
|
|
5306
|
+
error: state.error,
|
|
5307
|
+
providerSessionId: state.runtimeSessionId,
|
|
5308
|
+
runtimeSessionId: state.runtimeSessionId,
|
|
5309
|
+
backendKind: context.config.backendKind,
|
|
5310
|
+
supportTier: context.config.supportTier,
|
|
5311
|
+
usage: state.usage
|
|
5076
5312
|
};
|
|
5077
|
-
|
|
5078
|
-
|
|
5313
|
+
void presenter.onComplete(continuationResult);
|
|
5314
|
+
state.onNewOutput?.();
|
|
5315
|
+
state.startHeartbeat?.();
|
|
5316
|
+
console.info("[claude_cli] Result received in continuation mode \u2014 keeping stdin open for background work");
|
|
5317
|
+
} else {
|
|
5318
|
+
state.onResultReceived?.();
|
|
5319
|
+
if (state.stdin && !state.stdin.destroyed) {
|
|
5320
|
+
state.stdin.end();
|
|
5321
|
+
console.info("[claude_cli] Closed stdin after result event to signal CLI exit");
|
|
5322
|
+
}
|
|
5079
5323
|
}
|
|
5080
5324
|
break;
|
|
5081
5325
|
}
|
|
@@ -5089,10 +5333,7 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5089
5333
|
try {
|
|
5090
5334
|
parsed = JSON.parse(line);
|
|
5091
5335
|
} catch {
|
|
5092
|
-
|
|
5093
|
-
`;
|
|
5094
|
-
void presenter.onAssistantText(`${line}
|
|
5095
|
-
`);
|
|
5336
|
+
void presenter.onLog(`[codex] ${line}`);
|
|
5096
5337
|
return;
|
|
5097
5338
|
}
|
|
5098
5339
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
@@ -5101,13 +5342,9 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5101
5342
|
state.runtimeSessionId = parsed.thread_id;
|
|
5102
5343
|
}
|
|
5103
5344
|
const extractCodexErrorMessage = (value2) => {
|
|
5104
|
-
if (typeof value2 !== "string")
|
|
5105
|
-
return "";
|
|
5106
|
-
}
|
|
5345
|
+
if (typeof value2 !== "string") return "";
|
|
5107
5346
|
const trimmed = value2.trim();
|
|
5108
|
-
if (!trimmed)
|
|
5109
|
-
return "";
|
|
5110
|
-
}
|
|
5347
|
+
if (!trimmed) return "";
|
|
5111
5348
|
try {
|
|
5112
5349
|
const parsedValue = JSON.parse(trimmed);
|
|
5113
5350
|
if (typeof parsedValue.detail === "string" && parsedValue.detail.trim().length > 0) {
|
|
@@ -5136,18 +5373,14 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5136
5373
|
break;
|
|
5137
5374
|
case "item.started": {
|
|
5138
5375
|
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
5139
|
-
if (!item || item.type !== "command_execution" || typeof item.id !== "string")
|
|
5140
|
-
break;
|
|
5141
|
-
}
|
|
5376
|
+
if (!item || item.type !== "command_execution" || typeof item.id !== "string") break;
|
|
5142
5377
|
const command = typeof item.command === "string" ? item.command : "";
|
|
5143
5378
|
void presenter.onToolUse("Bash", { command, cwd: context.cwd }, item.id);
|
|
5144
5379
|
break;
|
|
5145
5380
|
}
|
|
5146
5381
|
case "item.completed": {
|
|
5147
5382
|
const item = typeof parsed.item === "object" && parsed.item !== null ? parsed.item : null;
|
|
5148
|
-
if (!item || typeof item.type !== "string")
|
|
5149
|
-
break;
|
|
5150
|
-
}
|
|
5383
|
+
if (!item || typeof item.type !== "string") break;
|
|
5151
5384
|
if (item.type === "reasoning" && typeof item.text === "string") {
|
|
5152
5385
|
void presenter.onThinking(item.text);
|
|
5153
5386
|
break;
|
|
@@ -5160,8 +5393,8 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5160
5393
|
}
|
|
5161
5394
|
if (item.type === "command_execution" && typeof item.id === "string") {
|
|
5162
5395
|
const output = typeof item.aggregated_output === "string" ? item.aggregated_output : "";
|
|
5163
|
-
const
|
|
5164
|
-
const resultText = output.trim().length > 0 ? output :
|
|
5396
|
+
const itemExitCode = typeof item.exit_code === "number" ? item.exit_code : null;
|
|
5397
|
+
const resultText = output.trim().length > 0 ? output : itemExitCode === null ? "" : `Exit code: ${itemExitCode}`;
|
|
5165
5398
|
void presenter.onToolResult?.(item.id, resultText);
|
|
5166
5399
|
break;
|
|
5167
5400
|
}
|
|
@@ -5205,20 +5438,17 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5205
5438
|
}
|
|
5206
5439
|
case "mcp_tool_call_end": {
|
|
5207
5440
|
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `mcp-${Date.now()}`;
|
|
5208
|
-
|
|
5209
|
-
void presenter.onToolResult?.(toolId, JSON.stringify(result ?? {}, null, 2));
|
|
5441
|
+
void presenter.onToolResult?.(toolId, JSON.stringify(parsed.result ?? {}, null, 2));
|
|
5210
5442
|
break;
|
|
5211
5443
|
}
|
|
5212
5444
|
case "token_count": {
|
|
5213
5445
|
const info = typeof parsed.info === "object" && parsed.info !== null ? parsed.info : {};
|
|
5214
|
-
const inputTokens = typeof info.input_tokens === "number" ? info.input_tokens : 0;
|
|
5215
|
-
const outputTokens = typeof info.output_tokens === "number" ? info.output_tokens : 0;
|
|
5216
5446
|
state.usage = {
|
|
5217
5447
|
model: typeof info.model === "string" ? info.model : "codex",
|
|
5218
5448
|
numTurns: Math.max(state.iterations, 1),
|
|
5219
5449
|
durationMs: 0,
|
|
5220
|
-
inputTokens,
|
|
5221
|
-
outputTokens,
|
|
5450
|
+
inputTokens: typeof info.input_tokens === "number" ? info.input_tokens : 0,
|
|
5451
|
+
outputTokens: typeof info.output_tokens === "number" ? info.output_tokens : 0,
|
|
5222
5452
|
cacheReadTokens: typeof info.cached_input_tokens === "number" ? info.cached_input_tokens : 0,
|
|
5223
5453
|
cacheCreationTokens: 0,
|
|
5224
5454
|
costUsd: typeof info.total_cost_usd === "number" ? info.total_cost_usd : 0
|
|
@@ -5234,23 +5464,18 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5234
5464
|
}
|
|
5235
5465
|
case "task_complete": {
|
|
5236
5466
|
const lastMessage = typeof parsed.last_agent_message === "string" ? parsed.last_agent_message : "";
|
|
5237
|
-
if (lastMessage.length > 0)
|
|
5238
|
-
state.summary = lastMessage;
|
|
5239
|
-
}
|
|
5467
|
+
if (lastMessage.length > 0) state.summary = lastMessage;
|
|
5240
5468
|
break;
|
|
5241
5469
|
}
|
|
5242
5470
|
case "turn.completed": {
|
|
5243
5471
|
const usage = typeof parsed.usage === "object" && parsed.usage !== null ? parsed.usage : {};
|
|
5244
|
-
const inputTokens = typeof usage.input_tokens === "number" ? usage.input_tokens : 0;
|
|
5245
|
-
const outputTokens = typeof usage.output_tokens === "number" ? usage.output_tokens : 0;
|
|
5246
|
-
const cacheReadTokens = typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : 0;
|
|
5247
5472
|
state.usage = {
|
|
5248
5473
|
model: typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "codex",
|
|
5249
5474
|
numTurns: Math.max(state.iterations, 1),
|
|
5250
5475
|
durationMs: 0,
|
|
5251
|
-
inputTokens,
|
|
5252
|
-
outputTokens,
|
|
5253
|
-
cacheReadTokens,
|
|
5476
|
+
inputTokens: typeof usage.input_tokens === "number" ? usage.input_tokens : 0,
|
|
5477
|
+
outputTokens: typeof usage.output_tokens === "number" ? usage.output_tokens : 0,
|
|
5478
|
+
cacheReadTokens: typeof usage.cached_input_tokens === "number" ? usage.cached_input_tokens : 0,
|
|
5254
5479
|
cacheCreationTokens: 0,
|
|
5255
5480
|
costUsd: 0
|
|
5256
5481
|
};
|
|
@@ -5264,17 +5489,128 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5264
5489
|
break;
|
|
5265
5490
|
}
|
|
5266
5491
|
case "error": {
|
|
5267
|
-
const message = extractCodexErrorMessage(parsed.message);
|
|
5268
|
-
if (message)
|
|
5269
|
-
state.error = message;
|
|
5270
|
-
}
|
|
5492
|
+
const message = extractCodexErrorMessage(parsed.message) || extractCodexErrorMessage(parsed.error) || typeof parsed.detail === "string" && parsed.detail.trim() || "";
|
|
5493
|
+
if (message) state.error = message;
|
|
5271
5494
|
break;
|
|
5272
5495
|
}
|
|
5273
5496
|
case "turn.failed": {
|
|
5274
5497
|
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : {};
|
|
5275
|
-
const message = extractCodexErrorMessage(error.message);
|
|
5276
|
-
if (message)
|
|
5277
|
-
|
|
5498
|
+
const message = extractCodexErrorMessage(error.message) || extractCodexErrorMessage(error.detail) || typeof parsed.message === "string" && parsed.message.trim() || "";
|
|
5499
|
+
if (message) state.error = message;
|
|
5500
|
+
break;
|
|
5501
|
+
}
|
|
5502
|
+
default:
|
|
5503
|
+
break;
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
function extractCursorToolEntry(toolCall) {
|
|
5507
|
+
const [rawName, rawPayload] = Object.entries(toolCall)[0] ?? [];
|
|
5508
|
+
if (!rawName || typeof rawPayload !== "object" || rawPayload === null) return null;
|
|
5509
|
+
return { rawName, payload: rawPayload };
|
|
5510
|
+
}
|
|
5511
|
+
function formatCursorToolName(rawName) {
|
|
5512
|
+
if (rawName === "terminalToolCall") return "Bash";
|
|
5513
|
+
const trimmed = rawName.replace(/ToolCall$/, "");
|
|
5514
|
+
if (!trimmed) return "Cursor Tool";
|
|
5515
|
+
return trimmed.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^[a-z]/, (char) => char.toUpperCase());
|
|
5516
|
+
}
|
|
5517
|
+
function buildCursorToolResultText(result) {
|
|
5518
|
+
if (typeof result === "string") return result;
|
|
5519
|
+
if (!result || typeof result !== "object") return JSON.stringify(result ?? {});
|
|
5520
|
+
const resultRecord = result;
|
|
5521
|
+
const success = typeof resultRecord.success === "object" && resultRecord.success !== null ? resultRecord.success : null;
|
|
5522
|
+
if (success) {
|
|
5523
|
+
if (typeof success.content === "string" && success.content.trim().length > 0) return success.content;
|
|
5524
|
+
if (typeof success.output === "string" && success.output.trim().length > 0) return success.output;
|
|
5525
|
+
const stdout = typeof success.stdout === "string" ? success.stdout.trim() : "";
|
|
5526
|
+
const stderr = typeof success.stderr === "string" ? success.stderr.trim() : "";
|
|
5527
|
+
if (stdout || stderr) return [stdout, stderr].filter(Boolean).join("\n\n");
|
|
5528
|
+
}
|
|
5529
|
+
const error = typeof resultRecord.error === "object" && resultRecord.error !== null ? resultRecord.error : null;
|
|
5530
|
+
if (error) {
|
|
5531
|
+
if (typeof error.message === "string" && error.message.trim().length > 0) return error.message;
|
|
5532
|
+
if (typeof error.content === "string" && error.content.trim().length > 0) return error.content;
|
|
5533
|
+
}
|
|
5534
|
+
return JSON.stringify(result, null, 2);
|
|
5535
|
+
}
|
|
5536
|
+
function parseCursorStructuredLine(line, context, state) {
|
|
5537
|
+
const presenter = context.presenter;
|
|
5538
|
+
let parsed = null;
|
|
5539
|
+
try {
|
|
5540
|
+
parsed = JSON.parse(line);
|
|
5541
|
+
} catch {
|
|
5542
|
+
if (line.trim().length > 0) void presenter.onLog(`[cursor_agent_cli] ${line}`);
|
|
5543
|
+
return;
|
|
5544
|
+
}
|
|
5545
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
5546
|
+
if (!type) return;
|
|
5547
|
+
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
5548
|
+
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
5549
|
+
switch (type) {
|
|
5550
|
+
case "system":
|
|
5551
|
+
break;
|
|
5552
|
+
case "assistant": {
|
|
5553
|
+
const message = typeof parsed.message === "object" && parsed.message !== null ? parsed.message : null;
|
|
5554
|
+
const content = Array.isArray(message?.content) ? message.content : [];
|
|
5555
|
+
for (const block of content) {
|
|
5556
|
+
if (!block || typeof block !== "object") continue;
|
|
5557
|
+
const part = block;
|
|
5558
|
+
const blockType = typeof part.type === "string" ? part.type : "";
|
|
5559
|
+
const text = typeof part.text === "string" ? part.text : "";
|
|
5560
|
+
if (!text) continue;
|
|
5561
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5562
|
+
if (blockType === "thinking" || blockType === "reasoning") {
|
|
5563
|
+
void presenter.onThinking(text);
|
|
5564
|
+
continue;
|
|
5565
|
+
}
|
|
5566
|
+
state.summary += text;
|
|
5567
|
+
void presenter.onAssistantText(text);
|
|
5568
|
+
}
|
|
5569
|
+
break;
|
|
5570
|
+
}
|
|
5571
|
+
case "tool_call": {
|
|
5572
|
+
const subtype = typeof parsed.subtype === "string" ? parsed.subtype : "";
|
|
5573
|
+
const toolId = typeof parsed.call_id === "string" ? parsed.call_id : `cursor-tool-${Date.now()}`;
|
|
5574
|
+
const toolCall = typeof parsed.tool_call === "object" && parsed.tool_call !== null ? parsed.tool_call : null;
|
|
5575
|
+
const entry = toolCall ? extractCursorToolEntry(toolCall) : null;
|
|
5576
|
+
if (!entry) break;
|
|
5577
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5578
|
+
const toolName = formatCursorToolName(entry.rawName);
|
|
5579
|
+
const toolArgs = typeof entry.payload.args === "object" && entry.payload.args !== null ? entry.payload.args : entry.payload;
|
|
5580
|
+
if (subtype === "started") {
|
|
5581
|
+
void presenter.onToolUse(toolName, toolArgs, toolId);
|
|
5582
|
+
break;
|
|
5583
|
+
}
|
|
5584
|
+
if (subtype === "completed") {
|
|
5585
|
+
void presenter.onToolResult?.(toolId, buildCursorToolResultText(entry.payload.result));
|
|
5586
|
+
}
|
|
5587
|
+
break;
|
|
5588
|
+
}
|
|
5589
|
+
case "result": {
|
|
5590
|
+
const subtype = typeof parsed.subtype === "string" ? parsed.subtype : "";
|
|
5591
|
+
const durationMs = typeof parsed.duration_ms === "number" ? parsed.duration_ms : 0;
|
|
5592
|
+
const resultText = typeof parsed.result === "string" ? parsed.result : "";
|
|
5593
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5594
|
+
state.usage = {
|
|
5595
|
+
model: state.activeModel || (typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "cursor-agent"),
|
|
5596
|
+
numTurns: Math.max(state.iterations, 1),
|
|
5597
|
+
durationMs,
|
|
5598
|
+
inputTokens: 0,
|
|
5599
|
+
outputTokens: 0,
|
|
5600
|
+
cacheReadTokens: 0,
|
|
5601
|
+
cacheCreationTokens: 0,
|
|
5602
|
+
costUsd: 0
|
|
5603
|
+
};
|
|
5604
|
+
void presenter.onUsageUpdate?.({
|
|
5605
|
+
model: state.usage.model,
|
|
5606
|
+
inputTokens: 0,
|
|
5607
|
+
outputTokens: 0,
|
|
5608
|
+
cacheReadTokens: 0,
|
|
5609
|
+
cacheCreationTokens: 0
|
|
5610
|
+
});
|
|
5611
|
+
if (resultText.trim().length > 0) state.summary = resultText;
|
|
5612
|
+
if ((parsed.is_error === true || subtype === "error") && !state.error) {
|
|
5613
|
+
state.error = resultText.trim() || "Cursor Agent run failed";
|
|
5278
5614
|
}
|
|
5279
5615
|
break;
|
|
5280
5616
|
}
|
|
@@ -5284,18 +5620,12 @@ function parseCodexStructuredLine(line, context, state) {
|
|
|
5284
5620
|
}
|
|
5285
5621
|
function buildGeminiToolResultText(parsed) {
|
|
5286
5622
|
const output = typeof parsed.output === "string" ? parsed.output : "";
|
|
5287
|
-
if (output.trim().length > 0)
|
|
5288
|
-
return output;
|
|
5289
|
-
}
|
|
5623
|
+
if (output.trim().length > 0) return output;
|
|
5290
5624
|
const error = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
5291
5625
|
const message = typeof error?.message === "string" ? error.message.trim() : "";
|
|
5292
5626
|
const errorType = typeof error?.type === "string" ? error.type.trim() : "";
|
|
5293
|
-
if (message && errorType) {
|
|
5294
|
-
|
|
5295
|
-
}
|
|
5296
|
-
if (message) {
|
|
5297
|
-
return message;
|
|
5298
|
-
}
|
|
5627
|
+
if (message && errorType) return `${errorType}: ${message}`;
|
|
5628
|
+
if (message) return message;
|
|
5299
5629
|
const status = typeof parsed.status === "string" ? parsed.status.trim() : "";
|
|
5300
5630
|
return status || "Tool completed";
|
|
5301
5631
|
}
|
|
@@ -5305,22 +5635,13 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
5305
5635
|
try {
|
|
5306
5636
|
parsed = JSON.parse(line);
|
|
5307
5637
|
} catch {
|
|
5308
|
-
|
|
5309
|
-
`;
|
|
5310
|
-
void presenter.onAssistantText(`${line}
|
|
5311
|
-
`);
|
|
5638
|
+
void presenter.onLog(`[gemini_cli] ${line}`);
|
|
5312
5639
|
return;
|
|
5313
5640
|
}
|
|
5314
5641
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
5315
|
-
if (!type)
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
if (typeof parsed.session_id === "string") {
|
|
5319
|
-
state.runtimeSessionId = parsed.session_id;
|
|
5320
|
-
}
|
|
5321
|
-
if (typeof parsed.model === "string") {
|
|
5322
|
-
state.activeModel = parsed.model;
|
|
5323
|
-
}
|
|
5642
|
+
if (!type) return;
|
|
5643
|
+
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
5644
|
+
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
5324
5645
|
switch (type) {
|
|
5325
5646
|
case "init": {
|
|
5326
5647
|
const model2 = state.activeModel || context.config.selectedModel || "gemini";
|
|
@@ -5328,14 +5649,9 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
5328
5649
|
break;
|
|
5329
5650
|
}
|
|
5330
5651
|
case "message": {
|
|
5331
|
-
|
|
5332
|
-
if (role !== "assistant") {
|
|
5333
|
-
break;
|
|
5334
|
-
}
|
|
5652
|
+
if (typeof parsed.role !== "string" || parsed.role !== "assistant") break;
|
|
5335
5653
|
const content = typeof parsed.content === "string" ? parsed.content : "";
|
|
5336
|
-
if (!content)
|
|
5337
|
-
break;
|
|
5338
|
-
}
|
|
5654
|
+
if (!content) break;
|
|
5339
5655
|
state.iterations = Math.max(state.iterations, 1);
|
|
5340
5656
|
state.summary += content;
|
|
5341
5657
|
void presenter.onAssistantText(content);
|
|
@@ -5355,13 +5671,12 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
5355
5671
|
break;
|
|
5356
5672
|
}
|
|
5357
5673
|
case "error": {
|
|
5358
|
-
const
|
|
5674
|
+
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
5675
|
+
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() || "";
|
|
5359
5676
|
const severity = typeof parsed.severity === "string" ? parsed.severity : "error";
|
|
5360
5677
|
if (message) {
|
|
5361
5678
|
void presenter.onLog(`[gemini_cli/${severity}] ${message}`);
|
|
5362
|
-
if (severity === "error")
|
|
5363
|
-
state.error = message;
|
|
5364
|
-
}
|
|
5679
|
+
if (severity === "error") state.error = message;
|
|
5365
5680
|
}
|
|
5366
5681
|
break;
|
|
5367
5682
|
}
|
|
@@ -5386,7 +5701,14 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
5386
5701
|
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
5387
5702
|
});
|
|
5388
5703
|
if (typeof parsed.status === "string" && parsed.status !== "success" && !state.error) {
|
|
5389
|
-
|
|
5704
|
+
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
5705
|
+
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;
|
|
5706
|
+
state.error = errMsg;
|
|
5707
|
+
console.error("[gemini_cli] result status=", parsed.status, "error:", JSON.stringify({
|
|
5708
|
+
error: parsed.error,
|
|
5709
|
+
error_message: parsed.error_message,
|
|
5710
|
+
message: parsed.message
|
|
5711
|
+
}));
|
|
5390
5712
|
}
|
|
5391
5713
|
break;
|
|
5392
5714
|
}
|
|
@@ -5394,8 +5716,8 @@ function parseGeminiStructuredLine(line, context, state) {
|
|
|
5394
5716
|
break;
|
|
5395
5717
|
}
|
|
5396
5718
|
}
|
|
5397
|
-
function getClaudePermissionMode(
|
|
5398
|
-
switch (
|
|
5719
|
+
function getClaudePermissionMode(mode2) {
|
|
5720
|
+
switch (mode2) {
|
|
5399
5721
|
case "plan":
|
|
5400
5722
|
case "ask":
|
|
5401
5723
|
case "review":
|
|
@@ -5404,40 +5726,108 @@ function getClaudePermissionMode(mode) {
|
|
|
5404
5726
|
return "bypassPermissions";
|
|
5405
5727
|
}
|
|
5406
5728
|
}
|
|
5729
|
+
function isCodexReadOnlyMode(mode2) {
|
|
5730
|
+
return mode2 === "plan" || mode2 === "ask" || mode2 === "review";
|
|
5731
|
+
}
|
|
5732
|
+
function shouldForceCursorAgent(mode2) {
|
|
5733
|
+
switch (mode2) {
|
|
5734
|
+
case "plan":
|
|
5735
|
+
case "ask":
|
|
5736
|
+
case "review":
|
|
5737
|
+
return false;
|
|
5738
|
+
default:
|
|
5739
|
+
return true;
|
|
5740
|
+
}
|
|
5741
|
+
}
|
|
5742
|
+
function writeImagesToTempFiles(images) {
|
|
5743
|
+
if (!images?.length) return { paths: [], cleanup: () => {
|
|
5744
|
+
} };
|
|
5745
|
+
const paths = [];
|
|
5746
|
+
for (const img of images) {
|
|
5747
|
+
const ext = img.mimeType.split("/")[1]?.replace("jpeg", "jpg") ?? "png";
|
|
5748
|
+
const filePath = (0, import_path.join)((0, import_os.tmpdir)(), `aiden-img-${img.id}.${ext}`);
|
|
5749
|
+
(0, import_fs.writeFileSync)(filePath, Buffer.from(img.data, "base64"));
|
|
5750
|
+
paths.push(filePath);
|
|
5751
|
+
}
|
|
5752
|
+
return {
|
|
5753
|
+
paths,
|
|
5754
|
+
cleanup: () => {
|
|
5755
|
+
for (const p of paths) {
|
|
5756
|
+
try {
|
|
5757
|
+
(0, import_fs.rmSync)(p, { force: true });
|
|
5758
|
+
} catch {
|
|
5759
|
+
}
|
|
5760
|
+
}
|
|
5761
|
+
}
|
|
5762
|
+
};
|
|
5763
|
+
}
|
|
5407
5764
|
function createClaudeCliBackend(command = "claude", defaultArgs = []) {
|
|
5408
5765
|
return {
|
|
5409
5766
|
kind: "claude_cli",
|
|
5410
5767
|
supportTier: "structured",
|
|
5411
5768
|
async run(context) {
|
|
5412
5769
|
const args = [
|
|
5413
|
-
"--print",
|
|
5414
5770
|
"--verbose",
|
|
5415
5771
|
"--output-format",
|
|
5416
5772
|
"stream-json",
|
|
5773
|
+
"--input-format",
|
|
5774
|
+
"stream-json",
|
|
5417
5775
|
"--permission-mode",
|
|
5418
5776
|
getClaudePermissionMode(context.config.mode)
|
|
5419
5777
|
];
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5778
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
5779
|
+
if (!resumeId) {
|
|
5780
|
+
if (context.config.systemPrompt?.trim()) {
|
|
5781
|
+
args.push("--system-prompt", context.config.systemPrompt.trim());
|
|
5782
|
+
}
|
|
5783
|
+
if (context.config.systemPromptAppend?.trim()) {
|
|
5784
|
+
args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
|
|
5785
|
+
}
|
|
5425
5786
|
}
|
|
5426
5787
|
if (context.config.selectedModel?.trim()) {
|
|
5427
5788
|
args.push("--model", context.config.selectedModel.trim());
|
|
5428
5789
|
}
|
|
5429
|
-
|
|
5790
|
+
if (context.config.mcpConfigPath?.trim()) {
|
|
5791
|
+
args.push("--mcp-config", context.config.mcpConfigPath.trim());
|
|
5792
|
+
}
|
|
5430
5793
|
if (resumeId) {
|
|
5431
5794
|
args.push("--resume", resumeId);
|
|
5432
5795
|
console.info("[claude_cli] Resuming session", { resumeId });
|
|
5433
5796
|
}
|
|
5434
|
-
args.push(...defaultArgs
|
|
5797
|
+
args.push(...defaultArgs);
|
|
5435
5798
|
return createGenericCliBackend({
|
|
5436
5799
|
kind: "claude_cli",
|
|
5437
5800
|
supportTier: "structured",
|
|
5438
5801
|
command,
|
|
5439
5802
|
args,
|
|
5440
|
-
parseStructuredLine: parseClaudeStructuredLine
|
|
5803
|
+
parseStructuredLine: parseClaudeStructuredLine,
|
|
5804
|
+
stdinWriter: (stdin, ctx) => {
|
|
5805
|
+
const contentBlocks = [];
|
|
5806
|
+
if (ctx.config.images?.length) {
|
|
5807
|
+
for (const img of ctx.config.images) {
|
|
5808
|
+
contentBlocks.push({
|
|
5809
|
+
type: "image",
|
|
5810
|
+
source: { type: "base64", media_type: img.mimeType, data: img.data }
|
|
5811
|
+
});
|
|
5812
|
+
}
|
|
5813
|
+
}
|
|
5814
|
+
const promptText = ctx.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(ctx.config, ctx.promptText)) : buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
5815
|
+
if (promptText.trim()) {
|
|
5816
|
+
contentBlocks.push({ type: "text", text: promptText });
|
|
5817
|
+
}
|
|
5818
|
+
console.info(`[claude_cli] stdinWriter: ${contentBlocks.length} content blocks (${ctx.config.images?.length ?? 0} images)`);
|
|
5819
|
+
const message = JSON.stringify({
|
|
5820
|
+
type: "user",
|
|
5821
|
+
message: { role: "user", content: contentBlocks },
|
|
5822
|
+
session_id: "default",
|
|
5823
|
+
parent_tool_use_id: null
|
|
5824
|
+
});
|
|
5825
|
+
stdin.write(message + "\n");
|
|
5826
|
+
},
|
|
5827
|
+
keepStdinOpen: true,
|
|
5828
|
+
allowContinuation: true,
|
|
5829
|
+
continuationIdleMs: 10 * 60 * 1e3
|
|
5830
|
+
// 10 minutes
|
|
5441
5831
|
}).run(context);
|
|
5442
5832
|
}
|
|
5443
5833
|
};
|
|
@@ -5453,45 +5843,168 @@ function createGeminiCliBackend(command = "gemini", defaultArgs = []) {
|
|
|
5453
5843
|
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
5454
5844
|
const approvalMode = context.config.mode === "plan" || context.config.mode === "ask" || context.config.mode === "review" ? "plan" : "yolo";
|
|
5455
5845
|
args.push("--approval-mode", approvalMode);
|
|
5456
|
-
if (resumeId)
|
|
5457
|
-
|
|
5458
|
-
}
|
|
5459
|
-
if (context.config.selectedModel?.trim()) {
|
|
5460
|
-
args.push("--model", context.config.selectedModel.trim());
|
|
5461
|
-
}
|
|
5846
|
+
if (resumeId) args.push("--resume", resumeId);
|
|
5847
|
+
if (context.config.selectedModel?.trim()) args.push("--model", context.config.selectedModel.trim());
|
|
5462
5848
|
return [...args, ...defaultArgs];
|
|
5463
5849
|
},
|
|
5464
5850
|
promptViaStdin: true,
|
|
5465
5851
|
augmentPrompt: (context) => {
|
|
5466
5852
|
const basePrompt = buildPromptWithSystem(context.config, context.promptText);
|
|
5467
|
-
|
|
5468
|
-
return buildPlanModePrefix(basePrompt);
|
|
5469
|
-
}
|
|
5470
|
-
return basePrompt;
|
|
5853
|
+
return context.config.mode === "plan" ? buildPlanModePrefix(basePrompt) : basePrompt;
|
|
5471
5854
|
},
|
|
5472
5855
|
parseStructuredLine: parseGeminiStructuredLine
|
|
5473
5856
|
});
|
|
5474
5857
|
}
|
|
5475
|
-
function
|
|
5858
|
+
function isCursorAgentCliModelId(model2) {
|
|
5859
|
+
return model2 === "auto" || /^composer-/.test(model2) || /^gpt-\d/.test(model2) || /^claude-\d/.test(model2) || /^gemini-\d/.test(model2) || /^grok-\d/.test(model2) || /^kimi-/.test(model2);
|
|
5860
|
+
}
|
|
5861
|
+
function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = []) {
|
|
5862
|
+
return createGenericCliBackend({
|
|
5863
|
+
kind: "cursor_agent_cli",
|
|
5864
|
+
supportTier: "structured",
|
|
5865
|
+
command,
|
|
5866
|
+
args: [],
|
|
5867
|
+
buildArgs: (context, prompt) => {
|
|
5868
|
+
const args = ["--print", "--output-format", "stream-json"];
|
|
5869
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
5870
|
+
if (shouldForceCursorAgent(context.config.mode)) args.push("--force");
|
|
5871
|
+
if (resumeId) args.push("--resume", resumeId);
|
|
5872
|
+
const model2 = context.config.selectedModel?.trim();
|
|
5873
|
+
if (model2 && isCursorAgentCliModelId(model2)) args.push("--model", model2);
|
|
5874
|
+
return [...args, ...defaultArgs, prompt];
|
|
5875
|
+
},
|
|
5876
|
+
augmentPrompt: (context) => {
|
|
5877
|
+
const base = buildPromptWithSystem(context.config, context.promptText);
|
|
5878
|
+
return context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
5879
|
+
},
|
|
5880
|
+
parseStructuredLine: parseCursorStructuredLine
|
|
5881
|
+
});
|
|
5882
|
+
}
|
|
5883
|
+
function droidExecPermissionArgs(mode2) {
|
|
5884
|
+
if (mode2 === "plan" || mode2 === "ask" || mode2 === "review") return [];
|
|
5885
|
+
return ["--auto", "high"];
|
|
5886
|
+
}
|
|
5887
|
+
function parseDroidStructuredLine(line, context, state) {
|
|
5476
5888
|
const presenter = context.presenter;
|
|
5477
5889
|
let parsed = null;
|
|
5478
5890
|
try {
|
|
5479
5891
|
parsed = JSON.parse(line);
|
|
5480
5892
|
} catch {
|
|
5481
|
-
if (line.trim().length > 0) {
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5893
|
+
if (line.trim().length > 0) void presenter.onLog(`[droid_cli] ${line}`);
|
|
5894
|
+
return;
|
|
5895
|
+
}
|
|
5896
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
5897
|
+
if (!type) return;
|
|
5898
|
+
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
5899
|
+
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
5900
|
+
switch (type) {
|
|
5901
|
+
case "system": {
|
|
5902
|
+
const subtype = typeof parsed.subtype === "string" ? parsed.subtype : "";
|
|
5903
|
+
if (subtype === "init") {
|
|
5904
|
+
if (typeof parsed.session_id === "string") state.runtimeSessionId = parsed.session_id;
|
|
5905
|
+
if (typeof parsed.model === "string") state.activeModel = parsed.model;
|
|
5906
|
+
}
|
|
5907
|
+
break;
|
|
5908
|
+
}
|
|
5909
|
+
case "message": {
|
|
5910
|
+
const role = typeof parsed.role === "string" ? parsed.role : "";
|
|
5911
|
+
if (role !== "assistant") break;
|
|
5912
|
+
const text = typeof parsed.text === "string" ? parsed.text : "";
|
|
5913
|
+
if (!text) break;
|
|
5914
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5915
|
+
state.summary += text;
|
|
5916
|
+
void presenter.onAssistantText(text);
|
|
5917
|
+
break;
|
|
5918
|
+
}
|
|
5919
|
+
case "tool_call": {
|
|
5920
|
+
const toolId = typeof parsed.id === "string" ? parsed.id : `droid-tool-${Date.now()}`;
|
|
5921
|
+
const toolName = typeof parsed.toolName === "string" ? parsed.toolName : "Tool";
|
|
5922
|
+
const parameters = typeof parsed.parameters === "object" && parsed.parameters !== null ? parsed.parameters : {};
|
|
5923
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
5924
|
+
void presenter.onToolUse(toolName, parameters, toolId);
|
|
5925
|
+
break;
|
|
5926
|
+
}
|
|
5927
|
+
case "tool_result": {
|
|
5928
|
+
const toolId = typeof parsed.id === "string" ? parsed.id : `droid-tool-${Date.now()}`;
|
|
5929
|
+
const value2 = parsed.value;
|
|
5930
|
+
const content = typeof value2 === "string" ? value2 : value2 !== void 0 ? JSON.stringify(value2) : "";
|
|
5931
|
+
const isError = parsed.isError === true;
|
|
5932
|
+
void presenter.onToolResult?.(toolId, content, isError);
|
|
5933
|
+
break;
|
|
5934
|
+
}
|
|
5935
|
+
case "completion": {
|
|
5936
|
+
const finalText = typeof parsed.finalText === "string" ? parsed.finalText : "";
|
|
5937
|
+
const numTurns = typeof parsed.numTurns === "number" ? parsed.numTurns : 1;
|
|
5938
|
+
const durationMs = typeof parsed.durationMs === "number" ? parsed.durationMs : 0;
|
|
5939
|
+
state.iterations = Math.max(state.iterations, numTurns, finalText ? 1 : 0);
|
|
5940
|
+
if (finalText.trim() && !state.summary.trim()) {
|
|
5941
|
+
state.summary = finalText;
|
|
5942
|
+
void presenter.onAssistantText(finalText);
|
|
5943
|
+
}
|
|
5944
|
+
state.usage = {
|
|
5945
|
+
model: state.activeModel || (typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel : "droid"),
|
|
5946
|
+
numTurns: Math.max(numTurns, 1),
|
|
5947
|
+
durationMs,
|
|
5948
|
+
inputTokens: 0,
|
|
5949
|
+
outputTokens: 0,
|
|
5950
|
+
cacheReadTokens: 0,
|
|
5951
|
+
cacheCreationTokens: 0,
|
|
5952
|
+
costUsd: 0
|
|
5953
|
+
};
|
|
5954
|
+
void presenter.onUsageUpdate?.({
|
|
5955
|
+
model: state.usage.model,
|
|
5956
|
+
inputTokens: 0,
|
|
5957
|
+
outputTokens: 0,
|
|
5958
|
+
cacheReadTokens: 0,
|
|
5959
|
+
cacheCreationTokens: 0
|
|
5960
|
+
});
|
|
5961
|
+
break;
|
|
5486
5962
|
}
|
|
5963
|
+
default:
|
|
5964
|
+
break;
|
|
5965
|
+
}
|
|
5966
|
+
}
|
|
5967
|
+
function createDroidCliBackend(command = "droid", defaultArgs = []) {
|
|
5968
|
+
return createGenericCliBackend({
|
|
5969
|
+
kind: "droid_cli",
|
|
5970
|
+
supportTier: "structured",
|
|
5971
|
+
command,
|
|
5972
|
+
args: [],
|
|
5973
|
+
buildArgs: (context, prompt) => {
|
|
5974
|
+
const args = [
|
|
5975
|
+
"exec",
|
|
5976
|
+
"--output-format",
|
|
5977
|
+
"stream-json",
|
|
5978
|
+
...droidExecPermissionArgs(context.config.mode),
|
|
5979
|
+
...defaultArgs
|
|
5980
|
+
];
|
|
5981
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
5982
|
+
if (resumeId) args.push("--session-id", resumeId);
|
|
5983
|
+
const model2 = context.config.selectedModel?.trim();
|
|
5984
|
+
if (model2) args.push("-m", model2);
|
|
5985
|
+
args.push(prompt);
|
|
5986
|
+
return args;
|
|
5987
|
+
},
|
|
5988
|
+
augmentPrompt: (context) => {
|
|
5989
|
+
const base = buildPromptWithSystem(context.config, context.promptText);
|
|
5990
|
+
return context.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
5991
|
+
},
|
|
5992
|
+
parseStructuredLine: parseDroidStructuredLine
|
|
5993
|
+
});
|
|
5994
|
+
}
|
|
5995
|
+
function parseOpencodeStructuredLine(line, context, state) {
|
|
5996
|
+
const presenter = context.presenter;
|
|
5997
|
+
let parsed = null;
|
|
5998
|
+
try {
|
|
5999
|
+
parsed = JSON.parse(line);
|
|
6000
|
+
} catch {
|
|
6001
|
+
if (line.trim().length > 0) void presenter.onLog(`[opencode] ${line}`);
|
|
5487
6002
|
return;
|
|
5488
6003
|
}
|
|
5489
6004
|
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
5490
6005
|
if (!type) return;
|
|
5491
6006
|
const sessionID = typeof parsed.sessionID === "string" ? parsed.sessionID : void 0;
|
|
5492
|
-
if (sessionID)
|
|
5493
|
-
state.runtimeSessionId = sessionID;
|
|
5494
|
-
}
|
|
6007
|
+
if (sessionID) state.runtimeSessionId = sessionID;
|
|
5495
6008
|
const part = typeof parsed.part === "object" && parsed.part !== null ? parsed.part : null;
|
|
5496
6009
|
switch (type) {
|
|
5497
6010
|
case "step_start": {
|
|
@@ -5525,19 +6038,44 @@ function parseOpencodeStructuredLine(line, context, state) {
|
|
|
5525
6038
|
if (part && typeof part.tokens === "object" && part.tokens !== null) {
|
|
5526
6039
|
const tokens = part.tokens;
|
|
5527
6040
|
state.usage = {
|
|
6041
|
+
model: typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() || "opencode",
|
|
6042
|
+
numTurns: Math.max(state.iterations, 1),
|
|
6043
|
+
durationMs: 0,
|
|
5528
6044
|
inputTokens: tokens.input ?? 0,
|
|
5529
|
-
outputTokens: tokens.output ?? 0
|
|
6045
|
+
outputTokens: tokens.output ?? 0,
|
|
6046
|
+
cacheReadTokens: tokens.cache_read ?? 0,
|
|
6047
|
+
cacheCreationTokens: tokens.cache_creation ?? 0,
|
|
6048
|
+
costUsd: 0
|
|
5530
6049
|
};
|
|
6050
|
+
void presenter.onUsageUpdate?.({
|
|
6051
|
+
model: state.usage.model,
|
|
6052
|
+
inputTokens: state.usage.inputTokens,
|
|
6053
|
+
outputTokens: state.usage.outputTokens,
|
|
6054
|
+
cacheReadTokens: state.usage.cacheReadTokens,
|
|
6055
|
+
cacheCreationTokens: state.usage.cacheCreationTokens
|
|
6056
|
+
});
|
|
5531
6057
|
}
|
|
5532
6058
|
break;
|
|
5533
6059
|
}
|
|
5534
6060
|
case "session.error":
|
|
5535
6061
|
case "error": {
|
|
5536
|
-
const
|
|
6062
|
+
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
6063
|
+
const message = typeof parsed.message === "string" && parsed.message.trim() || part && typeof part.message === "string" && part.message.trim() || typeof parsed.error === "string" && parsed.error.trim() || errObj && typeof errObj.message === "string" && errObj.message.trim() || part && typeof part.error === "string" && part.error.trim() || "";
|
|
5537
6064
|
if (message) {
|
|
5538
6065
|
void presenter.onLog(`[opencode/error] ${message}`);
|
|
5539
6066
|
state.error = message;
|
|
5540
6067
|
}
|
|
6068
|
+
const pid = state.process?.pid;
|
|
6069
|
+
if (pid) {
|
|
6070
|
+
try {
|
|
6071
|
+
process.kill(-pid, "SIGTERM");
|
|
6072
|
+
} catch {
|
|
6073
|
+
try {
|
|
6074
|
+
state.process?.kill("SIGTERM");
|
|
6075
|
+
} catch {
|
|
6076
|
+
}
|
|
6077
|
+
}
|
|
6078
|
+
}
|
|
5541
6079
|
break;
|
|
5542
6080
|
}
|
|
5543
6081
|
default:
|
|
@@ -5545,25 +6083,40 @@ function parseOpencodeStructuredLine(line, context, state) {
|
|
|
5545
6083
|
}
|
|
5546
6084
|
}
|
|
5547
6085
|
function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
5548
|
-
return
|
|
6086
|
+
return {
|
|
5549
6087
|
kind: "opencode_cli",
|
|
5550
6088
|
supportTier: "structured",
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
6089
|
+
async run(context) {
|
|
6090
|
+
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images);
|
|
6091
|
+
try {
|
|
6092
|
+
return await createGenericCliBackend({
|
|
6093
|
+
kind: "opencode_cli",
|
|
6094
|
+
supportTier: "structured",
|
|
6095
|
+
command,
|
|
6096
|
+
args: [],
|
|
6097
|
+
buildArgs: (ctx, prompt) => {
|
|
6098
|
+
const args = ["run", "--format", "json"];
|
|
6099
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6100
|
+
if (resumeId) args.push("--session", resumeId);
|
|
6101
|
+
const model2 = ctx.config.selectedModel?.trim();
|
|
6102
|
+
if (model2 && model2.includes("/")) args.push("--model", model2);
|
|
6103
|
+
const fileArgs = imagePaths.flatMap((p) => ["--file", p]);
|
|
6104
|
+
if (fileArgs.length > 0) {
|
|
6105
|
+
return [...args, ...fileArgs, ...defaultArgs, "--", prompt];
|
|
6106
|
+
}
|
|
6107
|
+
return [...args, ...defaultArgs, prompt];
|
|
6108
|
+
},
|
|
6109
|
+
augmentPrompt: (ctx) => {
|
|
6110
|
+
const base = buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6111
|
+
return ctx.config.mode === "plan" ? buildPlanModePrefix(base) : base;
|
|
6112
|
+
},
|
|
6113
|
+
parseStructuredLine: parseOpencodeStructuredLine
|
|
6114
|
+
}).run(context);
|
|
6115
|
+
} finally {
|
|
6116
|
+
cleanup();
|
|
6117
|
+
}
|
|
6118
|
+
}
|
|
6119
|
+
};
|
|
5567
6120
|
}
|
|
5568
6121
|
function createGenericCliPassthroughBackend(command, defaultArgs = []) {
|
|
5569
6122
|
return createGenericCliBackend({
|
|
@@ -5575,95 +6128,121 @@ function createGenericCliPassthroughBackend(command, defaultArgs = []) {
|
|
|
5575
6128
|
});
|
|
5576
6129
|
}
|
|
5577
6130
|
function createCodexRuntimeBackend(command = "codex", defaultArgs = []) {
|
|
5578
|
-
return
|
|
6131
|
+
return {
|
|
5579
6132
|
kind: "codex_app_server",
|
|
5580
6133
|
supportTier: "structured",
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
6134
|
+
async run(context) {
|
|
6135
|
+
const { paths: imagePaths, cleanup } = writeImagesToTempFiles(context.config.images);
|
|
6136
|
+
try {
|
|
6137
|
+
return await createGenericCliBackend({
|
|
6138
|
+
kind: "codex_app_server",
|
|
6139
|
+
supportTier: "structured",
|
|
6140
|
+
command,
|
|
6141
|
+
args: [],
|
|
6142
|
+
buildArgs: (ctx) => {
|
|
6143
|
+
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
6144
|
+
const modelArgs = ctx.config.selectedModel?.trim() ? ["--model", ctx.config.selectedModel.trim()] : [];
|
|
6145
|
+
const permissionArgs = isCodexReadOnlyMode(ctx.config.mode) ? ["--ask-for-approval", "never", "--sandbox", "read-only"] : ["--dangerously-bypass-approvals-and-sandbox"];
|
|
6146
|
+
const imageArgs = imagePaths.flatMap((p) => ["--image", p]);
|
|
6147
|
+
const baseArgs = resumeId ? [...permissionArgs, "exec", "resume", "--json", "--skip-git-repo-check", ...modelArgs, ...imageArgs, resumeId, "-"] : [...permissionArgs, "exec", "--json", "--skip-git-repo-check", ...modelArgs, ...imageArgs, "-"];
|
|
6148
|
+
return [...baseArgs, ...defaultArgs];
|
|
6149
|
+
},
|
|
6150
|
+
promptViaStdin: true,
|
|
6151
|
+
augmentPrompt: (ctx) => {
|
|
6152
|
+
const basePrompt = buildPromptWithSystem(ctx.config, ctx.promptText);
|
|
6153
|
+
return ctx.config.mode === "plan" ? buildPlanModePrefix(basePrompt) : basePrompt;
|
|
6154
|
+
},
|
|
6155
|
+
parseStructuredLine: parseCodexStructuredLine
|
|
6156
|
+
}).run(context);
|
|
6157
|
+
} finally {
|
|
6158
|
+
cleanup();
|
|
5593
6159
|
}
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
parseStructuredLine: parseCodexStructuredLine
|
|
5597
|
-
});
|
|
6160
|
+
}
|
|
6161
|
+
};
|
|
5598
6162
|
}
|
|
5599
|
-
|
|
5600
|
-
// src/core-agent.ts
|
|
5601
|
-
var MODE_CONFIG = {
|
|
5602
|
-
agent: {},
|
|
5603
|
-
plan: {
|
|
5604
|
-
systemPromptHint: "You are in plan mode. Focus on analysis, sequencing, and implementation planning rather than making edits."
|
|
5605
|
-
},
|
|
5606
|
-
debug: {
|
|
5607
|
-
systemPromptHint: "You are in debug mode. Prioritize reproducing the issue, isolating the cause, and validating the fix."
|
|
5608
|
-
},
|
|
5609
|
-
ask: {
|
|
5610
|
-
systemPromptHint: "You are in ask mode. Stay read-only and explain the code or architecture clearly without changing files."
|
|
5611
|
-
},
|
|
5612
|
-
review: {
|
|
5613
|
-
systemPromptHint: "You are in review mode. Inspect the changes, identify real issues, and summarize risks and regressions."
|
|
5614
|
-
}
|
|
5615
|
-
};
|
|
5616
6163
|
var DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
6164
|
+
var DROID_DEFAULT_MODEL = "claude-opus-4-7";
|
|
5617
6165
|
function canonicalizePath(inputPath) {
|
|
5618
6166
|
try {
|
|
5619
|
-
const resolved = (0,
|
|
5620
|
-
return (0,
|
|
5621
|
-
} catch
|
|
5622
|
-
console.warn("CoreAgent", "Failed to canonicalize path", { inputPath, error });
|
|
6167
|
+
const resolved = (0, import_path2.resolve)(inputPath);
|
|
6168
|
+
return (0, import_fs2.existsSync)(resolved) ? (0, import_fs2.realpathSync)(resolved) : resolved;
|
|
6169
|
+
} catch {
|
|
5623
6170
|
return null;
|
|
5624
6171
|
}
|
|
5625
6172
|
}
|
|
5626
6173
|
function buildPromptWithFiles(task2, files) {
|
|
5627
|
-
if (!files || files.length === 0)
|
|
5628
|
-
return task2;
|
|
5629
|
-
}
|
|
6174
|
+
if (!files || files.length === 0) return task2;
|
|
5630
6175
|
const fileList = files.map((file) => `- ${file.filename} (${file.label}): ${file.tempPath}`).join("\n");
|
|
5631
6176
|
return `${task2}
|
|
5632
6177
|
|
|
5633
6178
|
The user has attached the following files. You can read them using your available tools:
|
|
5634
6179
|
${fileList}`;
|
|
5635
6180
|
}
|
|
5636
|
-
var
|
|
6181
|
+
var BaseMachineAgent = class _BaseMachineAgent {
|
|
6182
|
+
static MAX_RETRIES = 2;
|
|
6183
|
+
static RETRY_BASE_DELAY_MS = 3e3;
|
|
5637
6184
|
presenter;
|
|
5638
|
-
abortController = null;
|
|
5639
6185
|
runtime;
|
|
5640
|
-
|
|
5641
|
-
pendingPlanApprovalResolvers = /* @__PURE__ */ new Map();
|
|
6186
|
+
abortController = null;
|
|
5642
6187
|
constructor(presenter, runtime) {
|
|
5643
6188
|
this.presenter = presenter;
|
|
5644
6189
|
this.runtime = runtime;
|
|
5645
6190
|
}
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
6191
|
+
// ── Overridable hooks (defaults match sandbox behaviour) ───────────────
|
|
6192
|
+
/**
|
|
6193
|
+
* Extra system-prompt sections appended after identity + mode hint.
|
|
6194
|
+
* Sandbox: artifact IDs from env vars.
|
|
6195
|
+
* Desktop: taskMeta block + artifact IDs from AgentConfig fields.
|
|
6196
|
+
*/
|
|
6197
|
+
buildExtraSystemPromptParts(_config) {
|
|
6198
|
+
return [];
|
|
5652
6199
|
}
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
6200
|
+
/**
|
|
6201
|
+
* Return a callback to be notified when the CLI child process spawns.
|
|
6202
|
+
* Desktop returns a closure that registers the process for kill tracking.
|
|
6203
|
+
* Sandbox returns undefined (no process management needed).
|
|
6204
|
+
*/
|
|
6205
|
+
getOnProcessSpawned() {
|
|
6206
|
+
return void 0;
|
|
5659
6207
|
}
|
|
6208
|
+
/**
|
|
6209
|
+
* Whether to skip calling presenter.onComplete() after runCliBackend.
|
|
6210
|
+
* Desktop returns true when the agent was externally killed to avoid
|
|
6211
|
+
* a second onComplete firing on top of the "Interrupted by user" event.
|
|
6212
|
+
*/
|
|
6213
|
+
shouldSkipOnComplete() {
|
|
6214
|
+
return false;
|
|
6215
|
+
}
|
|
6216
|
+
/**
|
|
6217
|
+
* Called when a session-resume attempt fails with a non-transient error.
|
|
6218
|
+
*
|
|
6219
|
+
* - Return null → retry immediately without the session ID (sandbox default).
|
|
6220
|
+
* - Return AgentResult → stop and surface this result, no retry (desktop behaviour).
|
|
6221
|
+
*/
|
|
6222
|
+
async onSessionResumeFailure(_runtimeConfig, _lastResult) {
|
|
6223
|
+
this.presenter.onLog("Session resume failed, starting fresh session...");
|
|
6224
|
+
return null;
|
|
6225
|
+
}
|
|
6226
|
+
/**
|
|
6227
|
+
* Called before sleeping between retry attempts.
|
|
6228
|
+
* Desktop uses this to wait for the previous child process to exit.
|
|
6229
|
+
*/
|
|
6230
|
+
async beforeRetry() {
|
|
6231
|
+
}
|
|
6232
|
+
/**
|
|
6233
|
+
* Build the prompt text sent to the CLI backend.
|
|
6234
|
+
* Default appends file attachment paths (sandbox uses this).
|
|
6235
|
+
* Desktop overrides to return config.task unchanged.
|
|
6236
|
+
*/
|
|
6237
|
+
buildPromptText(config) {
|
|
6238
|
+
return buildPromptWithFiles(config.task, config.files);
|
|
6239
|
+
}
|
|
6240
|
+
// ── Shared helpers ─────────────────────────────────────────────────────
|
|
5660
6241
|
abort() {
|
|
5661
6242
|
this.abortController?.abort();
|
|
5662
6243
|
}
|
|
5663
6244
|
isAbortError(error) {
|
|
5664
|
-
if (error instanceof Error && error.name === "AbortError")
|
|
5665
|
-
return true;
|
|
5666
|
-
}
|
|
6245
|
+
if (error instanceof Error && error.name === "AbortError") return true;
|
|
5667
6246
|
const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
|
|
5668
6247
|
return message.includes("aborted") || message.includes("interrupted") || message.includes("cancelled") || message.includes("canceled");
|
|
5669
6248
|
}
|
|
@@ -5674,6 +6253,8 @@ var CoreAgent = class {
|
|
|
5674
6253
|
switch (backendKind2) {
|
|
5675
6254
|
case "claude_cli":
|
|
5676
6255
|
case "codex_app_server":
|
|
6256
|
+
case "cursor_agent_cli":
|
|
6257
|
+
case "droid_cli":
|
|
5677
6258
|
case "gemini_cli":
|
|
5678
6259
|
case "opencode_cli":
|
|
5679
6260
|
return "structured";
|
|
@@ -5682,77 +6263,112 @@ var CoreAgent = class {
|
|
|
5682
6263
|
return "text";
|
|
5683
6264
|
}
|
|
5684
6265
|
}
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
const env = {};
|
|
5688
|
-
for (const [key, value2] of Object.entries(loginEnv)) {
|
|
5689
|
-
if (value2 !== void 0) {
|
|
5690
|
-
env[key] = value2;
|
|
5691
|
-
}
|
|
5692
|
-
}
|
|
5693
|
-
env.PATH = `/home/user/.local/node/bin:${env.PATH ?? ""}`;
|
|
5694
|
-
return env;
|
|
5695
|
-
}
|
|
5696
|
-
async buildRuntimeConfig(config, safeProjectPath) {
|
|
6266
|
+
// ── Core lifecycle ─────────────────────────────────────────────────────
|
|
6267
|
+
buildRuntimeConfig(config, safeProjectPath) {
|
|
5697
6268
|
const backendKind2 = this.resolveBackendKind(config);
|
|
5698
6269
|
const supportTier = config.supportTier || this.getDefaultSupportTier(backendKind2);
|
|
5699
|
-
const systemPromptParts = [config.systemPrompt?.trim()].filter(
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
6270
|
+
const systemPromptParts = [AIDEN_IDENTITY_PROMPT, config.systemPrompt?.trim()].filter(
|
|
6271
|
+
Boolean
|
|
6272
|
+
);
|
|
6273
|
+
const modeHint = getAidenModePrompt(config.mode);
|
|
6274
|
+
if (modeHint) systemPromptParts.push(modeHint);
|
|
6275
|
+
const workingDir = config.worktreePath || safeProjectPath;
|
|
6276
|
+
if (workingDir) {
|
|
5705
6277
|
systemPromptParts.push(
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
"Prefer relative paths and keep your work scoped to this project."
|
|
5709
|
-
].join("\n")
|
|
5710
|
-
);
|
|
5711
|
-
}
|
|
5712
|
-
if (config.images?.length) {
|
|
5713
|
-
this.presenter.onLog(
|
|
5714
|
-
`Image attachments are not yet passed directly to the CLI agent. Attached image count: ${config.images.length}.`
|
|
6278
|
+
`You are working inside: ${workingDir}
|
|
6279
|
+
Prefer relative paths and keep your work scoped to this project.`
|
|
5715
6280
|
);
|
|
5716
6281
|
}
|
|
6282
|
+
const extra = this.buildExtraSystemPromptParts(config);
|
|
6283
|
+
systemPromptParts.push(...extra);
|
|
5717
6284
|
return {
|
|
5718
6285
|
...config,
|
|
5719
6286
|
backendKind: backendKind2,
|
|
5720
6287
|
supportTier,
|
|
5721
6288
|
runtimeCommand: config.runtimeCommand || this.runtime.runtimeCommand,
|
|
5722
6289
|
runtimeArgs: config.runtimeArgs || this.runtime.runtimeArgs,
|
|
5723
|
-
selectedModel: config.selectedModel || DEFAULT_MODEL,
|
|
6290
|
+
selectedModel: config.selectedModel || (backendKind2 === "droid_cli" ? DROID_DEFAULT_MODEL : DEFAULT_MODEL),
|
|
5724
6291
|
systemPrompt: systemPromptParts.join("\n\n") || void 0
|
|
5725
6292
|
};
|
|
5726
6293
|
}
|
|
5727
6294
|
async runCliBackend(config, safeProjectPath) {
|
|
5728
6295
|
const backendKind2 = this.resolveBackendKind(config);
|
|
5729
|
-
const defaultCwd = (0,
|
|
5730
|
-
if (!(0,
|
|
5731
|
-
(0,
|
|
6296
|
+
const defaultCwd = (0, import_path2.join)((0, import_os2.homedir)(), "Documents", "aiden");
|
|
6297
|
+
if (!(0, import_fs2.existsSync)(defaultCwd)) {
|
|
6298
|
+
(0, import_fs2.mkdirSync)(defaultCwd, { recursive: true });
|
|
5732
6299
|
}
|
|
5733
|
-
|
|
6300
|
+
let runtimeConfig = this.buildRuntimeConfig(config, safeProjectPath);
|
|
5734
6301
|
const cwd = runtimeConfig.worktreePath || safeProjectPath || runtimeConfig.cwd || defaultCwd;
|
|
5735
|
-
const promptText = buildPromptWithFiles(runtimeConfig.task, runtimeConfig.files);
|
|
5736
6302
|
const env = this.buildCliEnvironment();
|
|
6303
|
+
const promptText = this.buildPromptText(runtimeConfig);
|
|
5737
6304
|
const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
|
|
5738
6305
|
const runtimeArgs = runtimeConfig.runtimeArgs || this.runtime.runtimeArgs || [];
|
|
5739
|
-
const backend = backendKind2 === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind2 === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind2 === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "gemini", runtimeArgs) : backendKind2 === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : createGenericCliPassthroughBackend(runtimeCommand || "generic-cli", runtimeArgs);
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
|
|
5743
|
-
|
|
5744
|
-
|
|
5745
|
-
|
|
5746
|
-
|
|
5747
|
-
|
|
5748
|
-
|
|
5749
|
-
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
|
|
5754
|
-
|
|
5755
|
-
|
|
6306
|
+
const backend = backendKind2 === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind2 === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind2 === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind2 === "gemini_cli" ? createGeminiCliBackend(runtimeCommand || "gemini", runtimeArgs) : backendKind2 === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind2 === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : createGenericCliPassthroughBackend(runtimeCommand || "generic-cli", runtimeArgs);
|
|
6307
|
+
let lastResult = null;
|
|
6308
|
+
for (let attempt = 0; attempt <= _BaseMachineAgent.MAX_RETRIES; attempt++) {
|
|
6309
|
+
if (this.abortController?.signal.aborted) break;
|
|
6310
|
+
const result = await backend.run({
|
|
6311
|
+
presenter: this.presenter,
|
|
6312
|
+
config: runtimeConfig,
|
|
6313
|
+
abortController: this.abortController,
|
|
6314
|
+
cwd,
|
|
6315
|
+
env,
|
|
6316
|
+
promptText,
|
|
6317
|
+
onProcessSpawned: this.getOnProcessSpawned()
|
|
6318
|
+
});
|
|
6319
|
+
lastResult = {
|
|
6320
|
+
...result,
|
|
6321
|
+
backendKind: backend.kind,
|
|
6322
|
+
supportTier: backend.supportTier,
|
|
6323
|
+
runtimeSessionId: result.runtimeSessionId || result.providerSessionId,
|
|
6324
|
+
providerSessionId: result.providerSessionId || result.runtimeSessionId
|
|
6325
|
+
};
|
|
6326
|
+
if (lastResult.success || !lastResult.error) break;
|
|
6327
|
+
const hadResumeId = runtimeConfig.runtimeSessionId || runtimeConfig.providerSessionId;
|
|
6328
|
+
if (hadResumeId && !isTransientError(lastResult.error)) {
|
|
6329
|
+
const overrideResult = await this.onSessionResumeFailure(runtimeConfig, lastResult);
|
|
6330
|
+
if (overrideResult !== null) {
|
|
6331
|
+
lastResult = overrideResult;
|
|
6332
|
+
break;
|
|
6333
|
+
}
|
|
6334
|
+
console.warn(
|
|
6335
|
+
"[base-machine-agent] Resume failed, retrying without session ID:",
|
|
6336
|
+
lastResult.error
|
|
6337
|
+
);
|
|
6338
|
+
runtimeConfig = { ...runtimeConfig, runtimeSessionId: void 0, providerSessionId: void 0 };
|
|
6339
|
+
const retryResult = await backend.run({
|
|
6340
|
+
presenter: this.presenter,
|
|
6341
|
+
config: runtimeConfig,
|
|
6342
|
+
abortController: this.abortController,
|
|
6343
|
+
cwd,
|
|
6344
|
+
env,
|
|
6345
|
+
promptText,
|
|
6346
|
+
onProcessSpawned: this.getOnProcessSpawned()
|
|
6347
|
+
});
|
|
6348
|
+
lastResult = {
|
|
6349
|
+
...retryResult,
|
|
6350
|
+
backendKind: backend.kind,
|
|
6351
|
+
supportTier: backend.supportTier,
|
|
6352
|
+
runtimeSessionId: retryResult.runtimeSessionId || retryResult.providerSessionId,
|
|
6353
|
+
providerSessionId: retryResult.providerSessionId || retryResult.runtimeSessionId
|
|
6354
|
+
};
|
|
6355
|
+
break;
|
|
6356
|
+
}
|
|
6357
|
+
if (!isTransientError(lastResult.error)) break;
|
|
6358
|
+
if (attempt < _BaseMachineAgent.MAX_RETRIES) {
|
|
6359
|
+
await this.beforeRetry();
|
|
6360
|
+
const delayMs = _BaseMachineAgent.RETRY_BASE_DELAY_MS * Math.pow(2, attempt);
|
|
6361
|
+
console.warn(
|
|
6362
|
+
`[base-machine-agent] Transient error (attempt ${attempt + 1}/${_BaseMachineAgent.MAX_RETRIES + 1}), retrying in ${delayMs}ms: ${lastResult.error}`
|
|
6363
|
+
);
|
|
6364
|
+
void this.presenter.onLog(`Transient error, retrying in ${Math.round(delayMs / 1e3)}s...`);
|
|
6365
|
+
await new Promise((resolve2) => setTimeout(resolve2, delayMs));
|
|
6366
|
+
}
|
|
6367
|
+
}
|
|
6368
|
+
const normalizedResult = lastResult;
|
|
6369
|
+
if (!this.shouldSkipOnComplete()) {
|
|
6370
|
+
this.presenter.onComplete(normalizedResult);
|
|
6371
|
+
}
|
|
5756
6372
|
return normalizedResult;
|
|
5757
6373
|
}
|
|
5758
6374
|
async run(config) {
|
|
@@ -5770,11 +6386,9 @@ var CoreAgent = class {
|
|
|
5770
6386
|
let safeProjectPath = initialConfig.projectPath;
|
|
5771
6387
|
if (initialConfig.projectPath) {
|
|
5772
6388
|
const canonicalized = canonicalizePath(initialConfig.projectPath);
|
|
5773
|
-
if (canonicalized)
|
|
5774
|
-
safeProjectPath = canonicalized;
|
|
5775
|
-
}
|
|
6389
|
+
if (canonicalized) safeProjectPath = canonicalized;
|
|
5776
6390
|
}
|
|
5777
|
-
if (safeProjectPath && !(0,
|
|
6391
|
+
if (safeProjectPath && !(0, import_fs2.existsSync)(safeProjectPath)) {
|
|
5778
6392
|
const message = `Project path no longer exists: "${safeProjectPath}". Please update the project's local path in project settings before running the agent.`;
|
|
5779
6393
|
this.presenter.onError(message, true);
|
|
5780
6394
|
const result = {
|
|
@@ -5796,7 +6410,7 @@ var CoreAgent = class {
|
|
|
5796
6410
|
const interrupted = this.isAbortError(error);
|
|
5797
6411
|
const message = interrupted ? "Interrupted by user" : error instanceof Error ? error.message : String(error);
|
|
5798
6412
|
if (!interrupted) {
|
|
5799
|
-
console.error("
|
|
6413
|
+
console.error("[base-machine-agent] CLI runtime failed", error);
|
|
5800
6414
|
this.presenter.onError(message, true);
|
|
5801
6415
|
}
|
|
5802
6416
|
const result = {
|
|
@@ -5815,6 +6429,79 @@ var CoreAgent = class {
|
|
|
5815
6429
|
}
|
|
5816
6430
|
};
|
|
5817
6431
|
|
|
6432
|
+
// src/core-agent.ts
|
|
6433
|
+
var CoreAgent = class extends BaseMachineAgent {
|
|
6434
|
+
childProcess = null;
|
|
6435
|
+
constructor(presenter, runtime) {
|
|
6436
|
+
super(presenter, runtime);
|
|
6437
|
+
}
|
|
6438
|
+
// ── Interactive tool response (WS relay → stdin) ───────────────────────
|
|
6439
|
+
/**
|
|
6440
|
+
* Send a tool_result to the CLI's stdin (stream-json format).
|
|
6441
|
+
* Used for interactive tools like AskUserQuestion.
|
|
6442
|
+
*/
|
|
6443
|
+
sendToolResponse(toolId, response) {
|
|
6444
|
+
if (!this.childProcess) return false;
|
|
6445
|
+
const stdin = this.childProcess.stdin;
|
|
6446
|
+
if (!stdin || stdin.destroyed) return false;
|
|
6447
|
+
try {
|
|
6448
|
+
const message = JSON.stringify({
|
|
6449
|
+
type: "user",
|
|
6450
|
+
message: {
|
|
6451
|
+
role: "user",
|
|
6452
|
+
content: [
|
|
6453
|
+
{
|
|
6454
|
+
type: "tool_result",
|
|
6455
|
+
tool_use_id: toolId,
|
|
6456
|
+
content: response
|
|
6457
|
+
}
|
|
6458
|
+
]
|
|
6459
|
+
},
|
|
6460
|
+
session_id: "default",
|
|
6461
|
+
parent_tool_use_id: null
|
|
6462
|
+
});
|
|
6463
|
+
stdin.write(message + "\n");
|
|
6464
|
+
console.info(`[CoreAgent] Sent tool_response for tool ${toolId}`);
|
|
6465
|
+
return true;
|
|
6466
|
+
} catch (err) {
|
|
6467
|
+
console.error("[CoreAgent] Failed to write tool_response to stdin", err);
|
|
6468
|
+
return false;
|
|
6469
|
+
}
|
|
6470
|
+
}
|
|
6471
|
+
// ── BaseMachineAgent hooks ─────────────────────────────────────────────
|
|
6472
|
+
getOnProcessSpawned() {
|
|
6473
|
+
return (child) => {
|
|
6474
|
+
this.childProcess = child;
|
|
6475
|
+
child.on("exit", () => {
|
|
6476
|
+
this.childProcess = null;
|
|
6477
|
+
});
|
|
6478
|
+
};
|
|
6479
|
+
}
|
|
6480
|
+
buildCliEnvironment() {
|
|
6481
|
+
const loginEnv = process.env;
|
|
6482
|
+
const env = {};
|
|
6483
|
+
for (const [key, value2] of Object.entries(loginEnv)) {
|
|
6484
|
+
if (value2 !== void 0) env[key] = value2;
|
|
6485
|
+
}
|
|
6486
|
+
env.PATH = `/home/user/.local/node/bin:${env.PATH ?? ""}`;
|
|
6487
|
+
return env;
|
|
6488
|
+
}
|
|
6489
|
+
buildExtraSystemPromptParts(_config) {
|
|
6490
|
+
const artifactIds = [];
|
|
6491
|
+
const envTaskId = process.env.AIDEN_TASK_ID;
|
|
6492
|
+
const envSessionId = process.env.AIDEN_SESSION_ID;
|
|
6493
|
+
if (envTaskId) artifactIds.push(`- taskId: "${envTaskId}"`);
|
|
6494
|
+
if (envSessionId) artifactIds.push(`- conversationId: "${envSessionId}"`);
|
|
6495
|
+
if (artifactIds.length === 0) return [];
|
|
6496
|
+
return [
|
|
6497
|
+
[
|
|
6498
|
+
"When creating artifacts using MCP tools (create_plan, create_document_artifact, create_test_report), always pass these IDs:",
|
|
6499
|
+
...artifactIds
|
|
6500
|
+
].join("\n")
|
|
6501
|
+
];
|
|
6502
|
+
}
|
|
6503
|
+
};
|
|
6504
|
+
|
|
5818
6505
|
// src/web-presenter.ts
|
|
5819
6506
|
var WebPresenter = class {
|
|
5820
6507
|
wsClient;
|
|
@@ -5842,8 +6529,14 @@ var WebPresenter = class {
|
|
|
5842
6529
|
onToolUse(tool, input, toolId) {
|
|
5843
6530
|
this.wsClient.emitEvent({ type: "tool_use", id: toolId, name: tool, input, ts: Date.now() });
|
|
5844
6531
|
}
|
|
5845
|
-
onToolResult(toolId, content) {
|
|
5846
|
-
this.wsClient.emitEvent({
|
|
6532
|
+
onToolResult(toolId, content, isError) {
|
|
6533
|
+
this.wsClient.emitEvent({
|
|
6534
|
+
type: "tool_result",
|
|
6535
|
+
id: toolId,
|
|
6536
|
+
content,
|
|
6537
|
+
ts: Date.now(),
|
|
6538
|
+
...isError ? { is_error: true } : {}
|
|
6539
|
+
});
|
|
5847
6540
|
}
|
|
5848
6541
|
onTurnComplete(content) {
|
|
5849
6542
|
this.wsClient.emitEvent({ type: "turn_complete", content, ts: Date.now() });
|
|
@@ -5851,6 +6544,26 @@ var WebPresenter = class {
|
|
|
5851
6544
|
onUsageUpdate(usage) {
|
|
5852
6545
|
this.wsClient.emitEvent({ type: "usage", usage });
|
|
5853
6546
|
}
|
|
6547
|
+
onAskUser(id, questions) {
|
|
6548
|
+
this.wsClient.emitEvent({ type: "ask_user", id, questions });
|
|
6549
|
+
}
|
|
6550
|
+
onTodoWrite(todos) {
|
|
6551
|
+
this.wsClient.emitEvent({ type: "todo_write", todos });
|
|
6552
|
+
}
|
|
6553
|
+
onExitPlanMode(id, input, planFilePath) {
|
|
6554
|
+
this.wsClient.emitEvent({ type: "exit_plan_mode", id, input, ...planFilePath ? { planFilePath } : {} });
|
|
6555
|
+
}
|
|
6556
|
+
onCheckpoint(id) {
|
|
6557
|
+
this.wsClient.emitEvent({ type: "checkpoint", id });
|
|
6558
|
+
}
|
|
6559
|
+
onActivity(status) {
|
|
6560
|
+
this.wsClient.emitEvent({ type: "activity", status, ts: Date.now() });
|
|
6561
|
+
}
|
|
6562
|
+
onOpenBrowserTab(browserId, url2) {
|
|
6563
|
+
this.wsClient.emitEvent({ type: "open_browser_tab", browserId, ...url2 ? { url: url2 } : {} });
|
|
6564
|
+
}
|
|
6565
|
+
recordRawTranscript() {
|
|
6566
|
+
}
|
|
5854
6567
|
onError(message, _isAgentError) {
|
|
5855
6568
|
this.wsClient.emitEvent({ type: "error", message });
|
|
5856
6569
|
if (!this.suppressSessionLifecycle) {
|
|
@@ -9452,10 +10165,6 @@ var WSClient = class {
|
|
|
9452
10165
|
emitEvent(event) {
|
|
9453
10166
|
this.socket.emit("agent_event", event);
|
|
9454
10167
|
}
|
|
9455
|
-
/** Signal the WS relay that this agent is about to update (not crashing). */
|
|
9456
|
-
emitUpdating() {
|
|
9457
|
-
this.socket.emit("agent_updating");
|
|
9458
|
-
}
|
|
9459
10168
|
waitForConnection(timeoutMs = 15e3) {
|
|
9460
10169
|
return new Promise((resolve2, reject) => {
|
|
9461
10170
|
if (this.socket.connected) {
|
|
@@ -9491,15 +10200,24 @@ var WSClient = class {
|
|
|
9491
10200
|
this.socket.on("connect_error", (error) => {
|
|
9492
10201
|
console.warn(`[WSClient] Connection error: ${error.message}`);
|
|
9493
10202
|
});
|
|
9494
|
-
this.socket.on("user_message", (data) => {
|
|
9495
|
-
|
|
10203
|
+
this.socket.on("user_message", (data, ack) => {
|
|
10204
|
+
ack?.({ ok: true });
|
|
10205
|
+
callbacks.onUserMessage({
|
|
10206
|
+
text: data.text,
|
|
10207
|
+
images: data.images,
|
|
10208
|
+
providerSessionId: data.providerSessionId,
|
|
10209
|
+
backendKind: data.backendKind,
|
|
10210
|
+
selectedModel: data.selectedModel,
|
|
10211
|
+
mode: data.mode
|
|
10212
|
+
});
|
|
9496
10213
|
});
|
|
9497
10214
|
this.socket.on("stop", () => {
|
|
9498
10215
|
callbacks.onStop();
|
|
9499
10216
|
});
|
|
9500
|
-
this.socket.on("
|
|
9501
|
-
|
|
9502
|
-
|
|
10217
|
+
this.socket.on("tool_response", (data) => {
|
|
10218
|
+
if (data?.toolId && typeof data.response === "string") {
|
|
10219
|
+
callbacks.onToolResponse?.(data.toolId, data.response);
|
|
10220
|
+
}
|
|
9503
10221
|
});
|
|
9504
10222
|
this.socket.on("error", (data) => {
|
|
9505
10223
|
console.error(`[WSClient] Server error: ${data.message} (${data.code})`);
|
|
@@ -9508,52 +10226,15 @@ var WSClient = class {
|
|
|
9508
10226
|
};
|
|
9509
10227
|
|
|
9510
10228
|
// src/version.ts
|
|
9511
|
-
var AGENT_VERSION = "0.1.
|
|
9512
|
-
|
|
9513
|
-
// src/updater.ts
|
|
9514
|
-
var import_node_child_process2 = require("child_process");
|
|
9515
|
-
var DEFAULT_PATH = "/home/user/.local/node/bin:/home/user/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
9516
|
-
async function installUpdate(payload) {
|
|
9517
|
-
const version = payload.version ?? "latest";
|
|
9518
|
-
try {
|
|
9519
|
-
console.info(`[updater] Installing @aiden-ade/sandbox-agent@${version} via npm`);
|
|
9520
|
-
(0, import_node_child_process2.execSync)(`npm install -g @aiden-ade/sandbox-agent@${version}`, {
|
|
9521
|
-
timeout: 6e4,
|
|
9522
|
-
env: { ...process.env, PATH: DEFAULT_PATH },
|
|
9523
|
-
stdio: "pipe"
|
|
9524
|
-
});
|
|
9525
|
-
console.info("[updater] npm install complete");
|
|
9526
|
-
return true;
|
|
9527
|
-
} catch (error) {
|
|
9528
|
-
console.error("[updater] npm install failed:", error.message);
|
|
9529
|
-
return false;
|
|
9530
|
-
}
|
|
9531
|
-
}
|
|
9532
|
-
function respawnAgent(lastProviderSessionId) {
|
|
9533
|
-
const env = { ...process.env };
|
|
9534
|
-
if (lastProviderSessionId) {
|
|
9535
|
-
env.AIDEN_PROVIDER_SESSION_ID = lastProviderSessionId;
|
|
9536
|
-
}
|
|
9537
|
-
console.info("[updater] Spawning new agent process");
|
|
9538
|
-
const child = (0, import_node_child_process2.spawn)("aiden-agent", [], {
|
|
9539
|
-
detached: true,
|
|
9540
|
-
stdio: "ignore",
|
|
9541
|
-
env: { ...env, PATH: DEFAULT_PATH }
|
|
9542
|
-
});
|
|
9543
|
-
child.unref();
|
|
9544
|
-
setTimeout(() => {
|
|
9545
|
-
console.info("[updater] Old agent exiting after respawn");
|
|
9546
|
-
process.exit(0);
|
|
9547
|
-
}, 500);
|
|
9548
|
-
}
|
|
10229
|
+
var AGENT_VERSION = "0.1.4";
|
|
9549
10230
|
|
|
9550
10231
|
// src/sandbox.ts
|
|
9551
10232
|
async function runSandbox(config) {
|
|
9552
10233
|
let currentTask = config.task;
|
|
10234
|
+
let currentImages = [];
|
|
9553
10235
|
let stopped = false;
|
|
9554
10236
|
let pendingMessageResolve = null;
|
|
9555
10237
|
let currentAgent = null;
|
|
9556
|
-
let pendingUpdate = null;
|
|
9557
10238
|
console.info("[sandbox] Connecting to WS server", { wsUrl: config.wsUrl, sessionId: config.sessionId });
|
|
9558
10239
|
const wsClient = new WSClient(
|
|
9559
10240
|
config.wsUrl,
|
|
@@ -9574,13 +10255,14 @@ async function runSandbox(config) {
|
|
|
9574
10255
|
console.info("[sandbox] Stop signal received");
|
|
9575
10256
|
if (currentAgent) currentAgent.abort();
|
|
9576
10257
|
},
|
|
9577
|
-
|
|
9578
|
-
|
|
9579
|
-
|
|
9580
|
-
|
|
9581
|
-
|
|
9582
|
-
|
|
9583
|
-
|
|
10258
|
+
onToolResponse: (toolId, response) => {
|
|
10259
|
+
if (currentAgent) {
|
|
10260
|
+
const sent = currentAgent.sendToolResponse(toolId, response);
|
|
10261
|
+
if (!sent) {
|
|
10262
|
+
console.warn("[sandbox] Failed to deliver tool_response to agent stdin", { toolId });
|
|
10263
|
+
}
|
|
10264
|
+
} else {
|
|
10265
|
+
console.warn("[sandbox] No active agent for tool_response", { toolId });
|
|
9584
10266
|
}
|
|
9585
10267
|
},
|
|
9586
10268
|
onConnect: () => console.info("[sandbox] WS connected"),
|
|
@@ -9599,11 +10281,8 @@ async function runSandbox(config) {
|
|
|
9599
10281
|
let lastProviderSessionId = config.providerSessionId;
|
|
9600
10282
|
let activeBackendKind = config.backendKind || "claude_cli";
|
|
9601
10283
|
let activeModel = config.model;
|
|
10284
|
+
let activeMode = config.mode || "agent";
|
|
9602
10285
|
while (!stopped) {
|
|
9603
|
-
if (pendingUpdate) {
|
|
9604
|
-
await performUpdate(wsClient, pendingUpdate, lastProviderSessionId);
|
|
9605
|
-
pendingUpdate = null;
|
|
9606
|
-
}
|
|
9607
10286
|
if (currentTask?.trim()) {
|
|
9608
10287
|
const agent = new CoreAgent(presenter, {
|
|
9609
10288
|
backendKind: activeBackendKind
|
|
@@ -9617,8 +10296,17 @@ async function runSandbox(config) {
|
|
|
9617
10296
|
projectPath: config.projectPath,
|
|
9618
10297
|
cwd: config.projectPath,
|
|
9619
10298
|
backendKind: activeBackendKind,
|
|
10299
|
+
mode: activeMode,
|
|
9620
10300
|
selectedModel: activeModel,
|
|
9621
|
-
providerSessionId: lastProviderSessionId ?? void 0
|
|
10301
|
+
providerSessionId: lastProviderSessionId ?? void 0,
|
|
10302
|
+
images: currentImages.length > 0 ? currentImages.map((img) => ({
|
|
10303
|
+
id: img.id,
|
|
10304
|
+
data: img.data,
|
|
10305
|
+
mimeType: img.mimeType,
|
|
10306
|
+
filename: img.filename,
|
|
10307
|
+
width: 0,
|
|
10308
|
+
height: 0
|
|
10309
|
+
})) : void 0
|
|
9622
10310
|
});
|
|
9623
10311
|
} catch (error) {
|
|
9624
10312
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -9630,22 +10318,15 @@ async function runSandbox(config) {
|
|
|
9630
10318
|
lastProviderSessionId = result.providerSessionId;
|
|
9631
10319
|
}
|
|
9632
10320
|
if (stopped) break;
|
|
9633
|
-
if (pendingUpdate) {
|
|
9634
|
-
await performUpdate(wsClient, pendingUpdate, lastProviderSessionId);
|
|
9635
|
-
pendingUpdate = null;
|
|
9636
|
-
}
|
|
9637
10321
|
}
|
|
9638
10322
|
const nextPayload = await new Promise((resolve2) => {
|
|
9639
10323
|
pendingMessageResolve = resolve2;
|
|
9640
10324
|
});
|
|
9641
10325
|
if (nextPayload === null || stopped) {
|
|
9642
|
-
if (pendingUpdate) {
|
|
9643
|
-
await performUpdate(wsClient, pendingUpdate, lastProviderSessionId);
|
|
9644
|
-
pendingUpdate = null;
|
|
9645
|
-
}
|
|
9646
10326
|
break;
|
|
9647
10327
|
}
|
|
9648
10328
|
currentTask = nextPayload.text;
|
|
10329
|
+
currentImages = nextPayload.images ?? [];
|
|
9649
10330
|
if (nextPayload.providerSessionId) {
|
|
9650
10331
|
lastProviderSessionId = nextPayload.providerSessionId;
|
|
9651
10332
|
}
|
|
@@ -9655,25 +10336,20 @@ async function runSandbox(config) {
|
|
|
9655
10336
|
if (nextPayload.selectedModel) {
|
|
9656
10337
|
activeModel = nextPayload.selectedModel;
|
|
9657
10338
|
}
|
|
10339
|
+
if (nextPayload.mode) {
|
|
10340
|
+
activeMode = nextPayload.mode;
|
|
10341
|
+
}
|
|
9658
10342
|
}
|
|
9659
10343
|
presenter.setSuppressSessionLifecycle(false);
|
|
9660
10344
|
wsClient.close();
|
|
9661
10345
|
}
|
|
9662
|
-
async function performUpdate(wsClient, payload, lastProviderSessionId) {
|
|
9663
|
-
console.info("[sandbox] Performing self-update", payload);
|
|
9664
|
-
wsClient.emitUpdating();
|
|
9665
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
9666
|
-
wsClient.close();
|
|
9667
|
-
const success = await installUpdate(payload);
|
|
9668
|
-
if (success) {
|
|
9669
|
-
respawnAgent(lastProviderSessionId);
|
|
9670
|
-
await new Promise((r) => setTimeout(r, 2e3));
|
|
9671
|
-
} else {
|
|
9672
|
-
console.error("[sandbox] Update failed \u2014 continuing with current version");
|
|
9673
|
-
}
|
|
9674
|
-
}
|
|
9675
10346
|
|
|
9676
10347
|
// src/index.ts
|
|
10348
|
+
if (process.argv.includes("--version")) {
|
|
10349
|
+
process.stdout.write(`${AGENT_VERSION}
|
|
10350
|
+
`);
|
|
10351
|
+
process.exit(0);
|
|
10352
|
+
}
|
|
9677
10353
|
var wsUrl = process.env.AIDEN_WS_URL;
|
|
9678
10354
|
var sessionId = process.env.AIDEN_SESSION_ID;
|
|
9679
10355
|
var taskId = process.env.AIDEN_TASK_ID;
|
|
@@ -9681,13 +10357,14 @@ var sessionToken = process.env.AIDEN_SESSION_TOKEN;
|
|
|
9681
10357
|
var task = process.env.AIDEN_TASK;
|
|
9682
10358
|
var projectPath = process.env.AIDEN_PROJECT_PATH || "/home/user/workspace";
|
|
9683
10359
|
var backendKind = process.env.AIDEN_BACKEND_KIND || "claude_cli";
|
|
10360
|
+
var mode = process.env.AIDEN_MODE || "agent";
|
|
9684
10361
|
var model = process.env.AIDEN_MODEL;
|
|
9685
10362
|
var providerSessionId = process.env.AIDEN_PROVIDER_SESSION_ID || void 0;
|
|
9686
10363
|
if (!wsUrl || !sessionId || !taskId || !sessionToken || task == null) {
|
|
9687
10364
|
console.error("[aiden-agent] Missing required env vars: AIDEN_WS_URL, AIDEN_SESSION_ID, AIDEN_TASK_ID, AIDEN_SESSION_TOKEN, AIDEN_TASK");
|
|
9688
10365
|
process.exit(1);
|
|
9689
10366
|
}
|
|
9690
|
-
console.info("[aiden-agent] Starting", { sessionId, taskId, projectPath, backendKind, model, version: AGENT_VERSION });
|
|
10367
|
+
console.info("[aiden-agent] Starting", { sessionId, taskId, projectPath, backendKind, mode, model, version: AGENT_VERSION });
|
|
9691
10368
|
runSandbox({
|
|
9692
10369
|
wsUrl,
|
|
9693
10370
|
sessionId,
|
|
@@ -9696,6 +10373,7 @@ runSandbox({
|
|
|
9696
10373
|
task,
|
|
9697
10374
|
projectPath,
|
|
9698
10375
|
backendKind,
|
|
10376
|
+
mode,
|
|
9699
10377
|
model,
|
|
9700
10378
|
providerSessionId,
|
|
9701
10379
|
maxIterations: 50
|