@aiden-ade/sandbox-agent 0.1.43 → 0.1.45
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/__tests__/cli-executable.test.d.ts +2 -0
- package/dist/__tests__/cli-executable.test.d.ts.map +1 -0
- package/dist/__tests__/cli-executable.test.js +83 -0
- package/dist/__tests__/cli-executable.test.js.map +1 -0
- package/dist/__tests__/cli-help.test.d.ts +2 -0
- package/dist/__tests__/cli-help.test.d.ts.map +1 -0
- package/dist/__tests__/cli-help.test.js +33 -0
- package/dist/__tests__/cli-help.test.js.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/core-agent-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/core-agent-workflow-context.test.js +50 -0
- package/dist/__tests__/core-agent-workflow-context.test.js.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts +2 -0
- package/dist/__tests__/daemon-socket.integration.test.d.ts.map +1 -0
- package/dist/__tests__/daemon-socket.integration.test.js +156 -0
- package/dist/__tests__/daemon-socket.integration.test.js.map +1 -0
- package/dist/__tests__/daemon.test.d.ts +2 -0
- package/dist/__tests__/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/daemon.test.js +1014 -0
- package/dist/__tests__/daemon.test.js.map +1 -0
- package/dist/__tests__/generated-images.test.d.ts +2 -0
- package/dist/__tests__/generated-images.test.d.ts.map +1 -0
- package/dist/__tests__/generated-images.test.js +50 -0
- package/dist/__tests__/generated-images.test.js.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts +2 -0
- package/dist/__tests__/sandbox-workflow-context.test.d.ts.map +1 -0
- package/dist/__tests__/sandbox-workflow-context.test.js +66 -0
- package/dist/__tests__/sandbox-workflow-context.test.js.map +1 -0
- package/dist/aiden-system-prompt.d.ts +6 -0
- package/dist/aiden-system-prompt.d.ts.map +1 -0
- package/dist/aiden-system-prompt.js +6 -0
- package/dist/aiden-system-prompt.js.map +1 -0
- package/dist/cli-executable.d.ts +58 -0
- package/dist/cli-executable.d.ts.map +1 -0
- package/dist/cli-executable.js +301 -0
- package/dist/cli-executable.js.map +1 -0
- package/dist/cli-help.d.ts +7 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +45 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/core-agent.d.ts +37 -0
- package/dist/core-agent.d.ts.map +1 -0
- package/dist/core-agent.js +337 -0
- package/dist/core-agent.js.map +1 -0
- package/dist/daemon.d.ts +46 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +876 -0
- package/dist/daemon.js.map +1 -0
- package/dist/generated-images.d.ts +4 -0
- package/dist/generated-images.d.ts.map +1 -0
- package/dist/generated-images.js +142 -0
- package/dist/generated-images.js.map +1 -0
- package/dist/index.cjs +737 -130
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +118 -0
- package/dist/index.js.map +1 -0
- package/dist/runtime-backends.d.ts +2 -0
- package/dist/runtime-backends.d.ts.map +1 -0
- package/dist/runtime-backends.js +2 -0
- package/dist/runtime-backends.js.map +1 -0
- package/dist/sandbox.d.ts +31 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +215 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/dist/updater.d.ts +15 -0
- package/dist/updater.d.ts.map +1 -0
- package/dist/updater.js +50 -0
- package/dist/updater.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/dist/web-presenter.d.ts +36 -0
- package/dist/web-presenter.d.ts.map +1 -0
- package/dist/web-presenter.js +125 -0
- package/dist/web-presenter.js.map +1 -0
- package/dist/ws-client.d.ts +114 -0
- package/dist/ws-client.d.ts.map +1 -0
- package/dist/ws-client.js +97 -0
- package/dist/ws-client.js.map +1 -0
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -14479,13 +14479,7 @@ function alanMcpConfigFilesForBackend(backendKind, mcpServers, home) {
|
|
|
14479
14479
|
var SESSION_RESUME_FAILED_MESSAGE = "Session resume failed. The previous session may have expired or is no longer available. Please start a new session.";
|
|
14480
14480
|
|
|
14481
14481
|
// ../shared/dist/agent/system-prompt.js
|
|
14482
|
-
var ALAN_IDENTITY_PROMPT = `You are Alan, the software factory agent
|
|
14483
|
-
|
|
14484
|
-
Do not present yourself as a runtime model, CLI, or backend vendor as your primary identity \u2014 those are runtime details. Mention them only if the user explicitly asks about the underlying model or backend. If the user asks who you are, answer in Alan terms first, and identify by your current agent role (described after this block).
|
|
14485
|
-
|
|
14486
|
-
## Voice
|
|
14487
|
-
|
|
14488
|
-
Direct. Structured. Concise. No hype, no "Great question!", no tool-brand roleplay. Speak like a teammate, not a data dump. Precise and decisive when working a task; listen, think, then act.
|
|
14482
|
+
var ALAN_IDENTITY_PROMPT = `You are Alan, the software factory agent. Alan ships software end to end \u2014 plan, write, review, test, deploy.
|
|
14489
14483
|
|
|
14490
14484
|
## How Alan is structured
|
|
14491
14485
|
|
|
@@ -14503,10 +14497,6 @@ Team
|
|
|
14503
14497
|
- **Document vs Artifact**: Document = standalone page in a team. Artifact = output from work on a task + conversation, tied to that context. Belongs to a task \u2192 artifact. Doesn't \u2192 document.
|
|
14504
14498
|
- **IDs are not interchangeable**: teamId, taskId, conversationId identify different things. Substituting one for another orphans records. Use the IDs the system provides; never invent or guess.
|
|
14505
14499
|
|
|
14506
|
-
## Structure over chat
|
|
14507
|
-
|
|
14508
|
-
Deliverables persist as Alan artifacts and documents \u2014 not as chat. Plans, PRDs, specs, test reports, and reviews live as structured outputs tied to team, task, and conversation. If a skill exists for the workflow you're in, use it instead of improvising in chat.
|
|
14509
|
-
|
|
14510
14500
|
## Universal teammate principles
|
|
14511
14501
|
|
|
14512
14502
|
- Listen first. Don't jump to solutions. One question at a time, not a list.
|
|
@@ -14545,7 +14535,6 @@ Examples: "where is X configured", "which file handles Y", "how does Z work", "f
|
|
|
14545
14535
|
- Do NOT start with \`grep\`, \`rg\`, \`find\`, or broad Bash filesystem search for codebase discovery or implementation-answer questions when \`search_code_context\` is available.
|
|
14546
14536
|
- Do NOT repeat or rephrase index searches after the first result identifies useful paths. Continue locally instead.
|
|
14547
14537
|
- Do NOT claim current behavior from an index excerpt without reading the cited local file.
|
|
14548
|
-
- Do NOT use graphify or local graph tools for the initial semantic "where is this implemented" pass.
|
|
14549
14538
|
|
|
14550
14539
|
### Local follow-ups
|
|
14551
14540
|
- Import/caller structure: use local \`code_graph_query\` or narrow symbol search after reading the cited files.
|
|
@@ -15296,6 +15285,8 @@ var import_path5 = require("path");
|
|
|
15296
15285
|
var import_fs6 = require("fs");
|
|
15297
15286
|
var import_os6 = require("os");
|
|
15298
15287
|
var import_path6 = require("path");
|
|
15288
|
+
var import_readline2 = require("readline");
|
|
15289
|
+
var import_url2 = require("url");
|
|
15299
15290
|
var import_fs7 = require("fs");
|
|
15300
15291
|
var import_os7 = require("os");
|
|
15301
15292
|
var import_path7 = require("path");
|
|
@@ -15333,7 +15324,7 @@ function writeFileSafe(path, content) {
|
|
|
15333
15324
|
(0, import_fs2.rmSync)(pendingPath, { force: true });
|
|
15334
15325
|
}
|
|
15335
15326
|
}
|
|
15336
|
-
function acquireConfigLock(path) {
|
|
15327
|
+
async function acquireConfigLock(path) {
|
|
15337
15328
|
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(path), { recursive: true });
|
|
15338
15329
|
const lockPath = `${path}.alan-lock`;
|
|
15339
15330
|
const startedAt = Date.now();
|
|
@@ -15355,7 +15346,7 @@ function acquireConfigLock(path) {
|
|
|
15355
15346
|
if (Date.now() - startedAt >= 2e3) {
|
|
15356
15347
|
throw new Error(`Alan could not safely update ${path}`);
|
|
15357
15348
|
}
|
|
15358
|
-
|
|
15349
|
+
await new Promise((resolve22) => setTimeout(resolve22, 20));
|
|
15359
15350
|
}
|
|
15360
15351
|
}
|
|
15361
15352
|
}
|
|
@@ -15619,13 +15610,13 @@ function buildJsonTargets(home) {
|
|
|
15619
15610
|
}
|
|
15620
15611
|
];
|
|
15621
15612
|
}
|
|
15622
|
-
function syncAlanMcpSessionHeaders(input) {
|
|
15613
|
+
async function syncAlanMcpSessionHeaders(input) {
|
|
15623
15614
|
const home = input.homeDir ?? (0, import_os2.homedir)();
|
|
15624
15615
|
const sessionHeaders = buildSessionHeaders(input);
|
|
15625
15616
|
const written = [];
|
|
15626
15617
|
for (const target of buildCodexTomlTargets(home)) {
|
|
15627
15618
|
if (!(0, import_fs2.existsSync)(target.guardDir)) continue;
|
|
15628
|
-
const releaseLock = acquireConfigLock(target.configPath);
|
|
15619
|
+
const releaseLock = await acquireConfigLock(target.configPath);
|
|
15629
15620
|
try {
|
|
15630
15621
|
if (patchCodexTomlMcpTarget(target, sessionHeaders)) {
|
|
15631
15622
|
written.push(target.cli);
|
|
@@ -15637,7 +15628,7 @@ function syncAlanMcpSessionHeaders(input) {
|
|
|
15637
15628
|
}
|
|
15638
15629
|
for (const target of buildJsonTargets(home)) {
|
|
15639
15630
|
if (!(0, import_fs2.existsSync)(target.guardDir)) continue;
|
|
15640
|
-
const releaseLock = acquireConfigLock(target.configPath);
|
|
15631
|
+
const releaseLock = await acquireConfigLock(target.configPath);
|
|
15641
15632
|
try {
|
|
15642
15633
|
if (patchJsonMcpTarget(target, sessionHeaders)) {
|
|
15643
15634
|
written.push(target.cli);
|
|
@@ -19157,7 +19148,7 @@ function createGrokAcpDriver(imageFiles) {
|
|
|
19157
19148
|
}
|
|
19158
19149
|
newSession(context);
|
|
19159
19150
|
}
|
|
19160
|
-
function
|
|
19151
|
+
function sendPrompt2(context, state) {
|
|
19161
19152
|
const text = buildPromptWithImagePathReferences(context, imageFiles);
|
|
19162
19153
|
isPromptActive = true;
|
|
19163
19154
|
promptId = request("session/prompt", {
|
|
@@ -19200,7 +19191,7 @@ function createGrokAcpDriver(imageFiles) {
|
|
|
19200
19191
|
const sessionId = asString(result?.sessionId);
|
|
19201
19192
|
if (sessionId) state.runtimeSessionId = sessionId;
|
|
19202
19193
|
state.iterations = Math.max(state.iterations, 1);
|
|
19203
|
-
|
|
19194
|
+
sendPrompt2(context, state);
|
|
19204
19195
|
return;
|
|
19205
19196
|
}
|
|
19206
19197
|
if (message.id === promptId) {
|
|
@@ -19546,23 +19537,59 @@ function createKimiCliBackend(command = "kimi-cli", defaultArgs = []) {
|
|
|
19546
19537
|
}
|
|
19547
19538
|
};
|
|
19548
19539
|
}
|
|
19540
|
+
var AUTONOMOUS_OPENCODE_CONFIG_CONTENT = JSON.stringify({
|
|
19541
|
+
$schema: "https://opencode.ai/config.json",
|
|
19542
|
+
permission: "allow"
|
|
19543
|
+
});
|
|
19544
|
+
function buildAutonomousOpenCodeConfigContent(existing) {
|
|
19545
|
+
if (!existing?.trim()) return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
|
|
19546
|
+
try {
|
|
19547
|
+
const parsed = JSON.parse(existing);
|
|
19548
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19549
|
+
return JSON.stringify({
|
|
19550
|
+
...parsed,
|
|
19551
|
+
permission: "allow"
|
|
19552
|
+
});
|
|
19553
|
+
}
|
|
19554
|
+
} catch {
|
|
19555
|
+
}
|
|
19556
|
+
return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
|
|
19557
|
+
}
|
|
19558
|
+
function withAutonomousOpenCodePermissions(context) {
|
|
19559
|
+
if (shouldUseReadOnlyRuntimePermissions(context.config)) return context;
|
|
19560
|
+
return {
|
|
19561
|
+
...context,
|
|
19562
|
+
env: {
|
|
19563
|
+
...context.env,
|
|
19564
|
+
OPENCODE_CONFIG_CONTENT: buildAutonomousOpenCodeConfigContent(
|
|
19565
|
+
context.env.OPENCODE_CONFIG_CONTENT
|
|
19566
|
+
)
|
|
19567
|
+
}
|
|
19568
|
+
};
|
|
19569
|
+
}
|
|
19549
19570
|
function getRecord(value2) {
|
|
19550
19571
|
return typeof value2 === "object" && value2 !== null ? value2 : null;
|
|
19551
19572
|
}
|
|
19552
19573
|
function getString(value2) {
|
|
19553
19574
|
return typeof value2 === "string" ? value2.trim() : "";
|
|
19554
19575
|
}
|
|
19555
|
-
function getOpenCodeToolError(
|
|
19556
|
-
const state = getRecord(part
|
|
19557
|
-
const error = getRecord(part
|
|
19558
|
-
return getString(part
|
|
19576
|
+
function getOpenCodeToolError(part) {
|
|
19577
|
+
const state = getRecord(part.state);
|
|
19578
|
+
const error = getRecord(part.error) ?? getRecord(state?.error);
|
|
19579
|
+
return getString(part.error) || getString(state?.error) || getString(error?.message);
|
|
19559
19580
|
}
|
|
19560
|
-
function isOpenCodeToolError(
|
|
19561
|
-
const state = getRecord(part
|
|
19562
|
-
return part
|
|
19581
|
+
function isOpenCodeToolError(part) {
|
|
19582
|
+
const state = getRecord(part.state);
|
|
19583
|
+
return part.is_error === true || part.isError === true || state?.status === "error" || Boolean(getOpenCodeToolError(part));
|
|
19563
19584
|
}
|
|
19564
|
-
function getOpenCodePartId(
|
|
19565
|
-
return getString(part
|
|
19585
|
+
function getOpenCodePartId(part) {
|
|
19586
|
+
return getString(part.id) || getString(part.partID);
|
|
19587
|
+
}
|
|
19588
|
+
function getOpenCodeToolId(part) {
|
|
19589
|
+
return getString(part.callID) || getString(part.id) || `opencode-tool-${Date.now()}`;
|
|
19590
|
+
}
|
|
19591
|
+
function getOpenCodeToolName(part) {
|
|
19592
|
+
return getString(part.tool) || (typeof part.name === "string" ? part.name.trim() : "") || "Tool";
|
|
19566
19593
|
}
|
|
19567
19594
|
function resolveOpenCodePartSnapshotDelta(text, partId, snapshots) {
|
|
19568
19595
|
if (!partId) return text;
|
|
@@ -19579,93 +19606,92 @@ function resolveOpenCodeReasoningDelta(text, partId, state) {
|
|
|
19579
19606
|
state.opencodeReasoningByPartId ??= /* @__PURE__ */ new Map();
|
|
19580
19607
|
return resolveOpenCodePartSnapshotDelta(text, partId, state.opencodeReasoningByPartId);
|
|
19581
19608
|
}
|
|
19582
|
-
|
|
19609
|
+
var TASK_TOOL_NAME = "task";
|
|
19610
|
+
var TASK_RESULT_TAG_PATTERN = /<task_result>\n?([\s\S]*?)\n?<\/task_result>/;
|
|
19611
|
+
var TASK_ERROR_TAG_PATTERN = /<task_error>\n?([\s\S]*?)\n?<\/task_error>/;
|
|
19612
|
+
function unwrapOpenCodeTaskOutput(output) {
|
|
19613
|
+
const result = output.match(TASK_RESULT_TAG_PATTERN);
|
|
19614
|
+
if (result?.[1] !== void 0) return result[1].trim();
|
|
19615
|
+
const error = output.match(TASK_ERROR_TAG_PATTERN);
|
|
19616
|
+
if (error?.[1] !== void 0) return error[1].trim();
|
|
19617
|
+
return output;
|
|
19618
|
+
}
|
|
19619
|
+
function emitOpenCodeToolPart(presenter, state, part, opts) {
|
|
19620
|
+
const stateRecord = getRecord(part.state);
|
|
19621
|
+
const status = getString(stateRecord?.status);
|
|
19622
|
+
if (status === "pending") return;
|
|
19623
|
+
const toolName = getOpenCodeToolName(part);
|
|
19624
|
+
const toolId = getOpenCodeToolId(part);
|
|
19625
|
+
const isTask = toolName.toLowerCase() === TASK_TOOL_NAME;
|
|
19626
|
+
const parentToolUseId = opts.parentToolUseId ?? void 0;
|
|
19627
|
+
state.opencodeToolUseIds ??= /* @__PURE__ */ new Set();
|
|
19628
|
+
if (!state.opencodeToolUseIds.has(toolId)) {
|
|
19629
|
+
state.opencodeToolUseIds.add(toolId);
|
|
19630
|
+
void presenter.onToolUse(toolName, stateRecord?.input ?? part, toolId, parentToolUseId);
|
|
19631
|
+
if (isTask) registerBackgroundLauncher(state, toolId);
|
|
19632
|
+
}
|
|
19633
|
+
if (status !== "completed" && status !== "error") return;
|
|
19634
|
+
const toolError = isOpenCodeToolError(part);
|
|
19635
|
+
const rawOutput = getString(stateRecord?.output) || getString(stateRecord?.error) || JSON.stringify(stateRecord ?? part);
|
|
19636
|
+
const output = isTask ? unwrapOpenCodeTaskOutput(rawOutput) : rawOutput;
|
|
19637
|
+
void presenter.onToolResult?.(toolId, output, toolError, parentToolUseId);
|
|
19638
|
+
if (isTask) clearBackgroundTaskIds(state, [toolId]);
|
|
19639
|
+
if (toolError && !state.error) {
|
|
19640
|
+
const message = getOpenCodeToolError(part) || "OpenCode tool call failed.";
|
|
19641
|
+
state.error = message;
|
|
19642
|
+
void presenter.onError(message);
|
|
19643
|
+
}
|
|
19644
|
+
}
|
|
19645
|
+
function mapOpencodePart(part, context, state, opts = {}) {
|
|
19583
19646
|
const presenter = context.presenter;
|
|
19584
|
-
const type =
|
|
19585
|
-
if (!type) return false;
|
|
19586
|
-
const sessionID = typeof parsed.sessionID === "string" ? parsed.sessionID : void 0;
|
|
19587
|
-
if (sessionID) state.runtimeSessionId = sessionID;
|
|
19588
|
-
const part = typeof parsed.part === "object" && parsed.part !== null ? parsed.part : null;
|
|
19647
|
+
const type = getString(part.type);
|
|
19589
19648
|
switch (type) {
|
|
19590
|
-
case "
|
|
19649
|
+
case "step-start": {
|
|
19591
19650
|
state.iterations += 1;
|
|
19592
19651
|
void presenter.onLog(`[opencode] Step ${state.iterations} started`);
|
|
19593
19652
|
return true;
|
|
19594
19653
|
}
|
|
19595
19654
|
case "text": {
|
|
19596
|
-
const text =
|
|
19655
|
+
const text = typeof part.text === "string" ? part.text : "";
|
|
19597
19656
|
if (text) {
|
|
19598
|
-
const delta = resolveOpenCodeTextDelta(text, getOpenCodePartId(
|
|
19599
|
-
if (
|
|
19600
|
-
|
|
19601
|
-
|
|
19657
|
+
const delta = resolveOpenCodeTextDelta(text, getOpenCodePartId(part), state);
|
|
19658
|
+
if (delta) {
|
|
19659
|
+
state.summary += delta;
|
|
19660
|
+
void presenter.onAssistantText(delta);
|
|
19661
|
+
}
|
|
19602
19662
|
}
|
|
19603
19663
|
return true;
|
|
19604
19664
|
}
|
|
19665
|
+
// Real Part schema only ever uses "reasoning"; "thinking" is a defensive alias in
|
|
19666
|
+
// case a CLI dialect ever names the part itself that way.
|
|
19605
19667
|
case "reasoning":
|
|
19606
19668
|
case "thinking": {
|
|
19607
|
-
const text =
|
|
19669
|
+
const text = typeof part.text === "string" ? part.text : "";
|
|
19608
19670
|
if (!text) return true;
|
|
19609
|
-
const delta = resolveOpenCodeReasoningDelta(text, getOpenCodePartId(
|
|
19671
|
+
const delta = resolveOpenCodeReasoningDelta(text, getOpenCodePartId(part), state);
|
|
19610
19672
|
if (!delta) return true;
|
|
19611
19673
|
state.iterations = Math.max(state.iterations, 1);
|
|
19612
19674
|
void presenter.onThinking(delta);
|
|
19613
19675
|
return true;
|
|
19614
19676
|
}
|
|
19615
|
-
case "tool_use":
|
|
19616
|
-
case "tool.execute": {
|
|
19617
|
-
const toolName = getString(parsed.tool) || (part && typeof part.name === "string" ? part.name : "") || getString(parsed.name) || (part && typeof part.tool === "string" ? part.tool : "") || "Tool";
|
|
19618
|
-
const toolId = part && typeof part.id === "string" ? part.id : `opencode-tool-${Date.now()}`;
|
|
19619
|
-
void presenter.onToolUse(toolName, part ?? {}, toolId);
|
|
19620
|
-
return true;
|
|
19621
|
-
}
|
|
19622
19677
|
case "tool": {
|
|
19623
|
-
|
|
19624
|
-
const toolName = getString(parsed.tool) || (part && typeof part.name === "string" ? part.name : "") || getString(parsed.name) || (part && typeof part.tool === "string" ? part.tool : "") || "Tool";
|
|
19625
|
-
const toolId = getString(parsed.id) || getString(parsed.callID) || `opencode-tool-${Date.now()}`;
|
|
19626
|
-
state.opencodeToolUseIds ??= /* @__PURE__ */ new Set();
|
|
19627
|
-
if (!state.opencodeToolUseIds.has(toolId)) {
|
|
19628
|
-
state.opencodeToolUseIds.add(toolId);
|
|
19629
|
-
void presenter.onToolUse(toolName, stateRecord?.input ?? part ?? parsed, toolId);
|
|
19630
|
-
}
|
|
19631
|
-
const status = getString(stateRecord?.status);
|
|
19632
|
-
if (status === "completed" || status === "error") {
|
|
19633
|
-
const toolError = isOpenCodeToolError(parsed, part);
|
|
19634
|
-
const output = getString(stateRecord?.output) || getString(stateRecord?.error) || JSON.stringify(stateRecord ?? part ?? parsed);
|
|
19635
|
-
void presenter.onToolResult?.(toolId, output, toolError);
|
|
19636
|
-
if (toolError && !state.error) {
|
|
19637
|
-
const message = getOpenCodeToolError(parsed, part) || "OpenCode tool call failed.";
|
|
19638
|
-
state.error = message;
|
|
19639
|
-
void presenter.onError(message);
|
|
19640
|
-
}
|
|
19641
|
-
}
|
|
19678
|
+
emitOpenCodeToolPart(presenter, state, part, opts);
|
|
19642
19679
|
return true;
|
|
19643
19680
|
}
|
|
19644
|
-
case "
|
|
19645
|
-
|
|
19646
|
-
|
|
19647
|
-
|
|
19648
|
-
|
|
19649
|
-
void presenter.onToolResult?.(toolId, output, toolError);
|
|
19650
|
-
if (toolError && !state.error) {
|
|
19651
|
-
const message = getOpenCodeToolError(parsed, part) || "OpenCode tool call failed.";
|
|
19652
|
-
state.error = message;
|
|
19653
|
-
void presenter.onError(message);
|
|
19654
|
-
}
|
|
19655
|
-
return true;
|
|
19656
|
-
}
|
|
19657
|
-
case "step_finish": {
|
|
19658
|
-
if (part && typeof part.tokens === "object" && part.tokens !== null) {
|
|
19659
|
-
const tokens = part.tokens;
|
|
19681
|
+
case "step-finish": {
|
|
19682
|
+
const tokens = getRecord(part.tokens);
|
|
19683
|
+
if (tokens) {
|
|
19684
|
+
const cache2 = getRecord(tokens.cache);
|
|
19685
|
+
const cost = typeof part.cost === "number" ? part.cost : 0;
|
|
19660
19686
|
state.usage = {
|
|
19661
19687
|
model: typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() || "opencode",
|
|
19662
19688
|
numTurns: Math.max(state.iterations, 1),
|
|
19663
19689
|
durationMs: 0,
|
|
19664
|
-
inputTokens: tokens.input
|
|
19665
|
-
outputTokens: tokens.output
|
|
19666
|
-
cacheReadTokens:
|
|
19667
|
-
cacheCreationTokens:
|
|
19668
|
-
costUsd:
|
|
19690
|
+
inputTokens: Number(tokens.input) || 0,
|
|
19691
|
+
outputTokens: Number(tokens.output) || 0,
|
|
19692
|
+
cacheReadTokens: Number(cache2?.read) || 0,
|
|
19693
|
+
cacheCreationTokens: Number(cache2?.write) || 0,
|
|
19694
|
+
costUsd: cost
|
|
19669
19695
|
};
|
|
19670
19696
|
void presenter.onUsageUpdate?.({
|
|
19671
19697
|
model: state.usage.model,
|
|
@@ -19677,6 +19703,57 @@ function handleOpencodeStructuredEvent(parsed, context, state) {
|
|
|
19677
19703
|
}
|
|
19678
19704
|
return true;
|
|
19679
19705
|
}
|
|
19706
|
+
default:
|
|
19707
|
+
return false;
|
|
19708
|
+
}
|
|
19709
|
+
}
|
|
19710
|
+
function normalizeOpenCodePart(parsed, part, partType) {
|
|
19711
|
+
return { ...parsed, ...part ?? {}, type: partType };
|
|
19712
|
+
}
|
|
19713
|
+
function handleOpencodeStructuredEvent(parsed, context, state) {
|
|
19714
|
+
const presenter = context.presenter;
|
|
19715
|
+
const type = typeof parsed.type === "string" ? parsed.type : "";
|
|
19716
|
+
if (!type) return false;
|
|
19717
|
+
const sessionID = typeof parsed.sessionID === "string" ? parsed.sessionID : void 0;
|
|
19718
|
+
if (sessionID) state.runtimeSessionId = sessionID;
|
|
19719
|
+
const part = typeof parsed.part === "object" && parsed.part !== null ? parsed.part : null;
|
|
19720
|
+
switch (type) {
|
|
19721
|
+
case "step_start":
|
|
19722
|
+
return mapOpencodePart(normalizeOpenCodePart(parsed, part, "step-start"), context, state);
|
|
19723
|
+
case "text":
|
|
19724
|
+
return mapOpencodePart(normalizeOpenCodePart(parsed, part, "text"), context, state);
|
|
19725
|
+
case "reasoning":
|
|
19726
|
+
case "thinking":
|
|
19727
|
+
return mapOpencodePart(normalizeOpenCodePart(parsed, part, "reasoning"), context, state);
|
|
19728
|
+
// Real event: `opencode run --format json` emits exactly this shape — a single
|
|
19729
|
+
// terminal event per tool call, carrying part.state.{input,output|error}. See
|
|
19730
|
+
// notes/opencode-cli-reverse-engineering.md for the live-captured schema.
|
|
19731
|
+
case "tool_use":
|
|
19732
|
+
case "tool.execute":
|
|
19733
|
+
case "tool":
|
|
19734
|
+
return mapOpencodePart(normalizeOpenCodePart(parsed, part, "tool"), context, state);
|
|
19735
|
+
case "step_finish":
|
|
19736
|
+
return mapOpencodePart(normalizeOpenCodePart(parsed, part, "step-finish"), context, state);
|
|
19737
|
+
// Defensive fallback only: no observed OpenCode version emits a separate
|
|
19738
|
+
// tool_result/tool.result event in `--format json` mode (tool state always
|
|
19739
|
+
// arrives via `tool_use` above). Kept in case a future/older CLI build splits
|
|
19740
|
+
// the terminal tool event into a distinct result message. Deliberately NOT routed
|
|
19741
|
+
// through mapOpencodePart — this shape has no part.state wrapper at all (output
|
|
19742
|
+
// sits directly on the part), so it isn't a real Part-schema shape.
|
|
19743
|
+
case "tool_result":
|
|
19744
|
+
case "tool.result": {
|
|
19745
|
+
const toolId = getString(part?.callID) || getString(parsed.callID) || getString(part?.id) || getString(parsed.id) || `opencode-tool-${Date.now()}`;
|
|
19746
|
+
const output = part && typeof part.output === "string" ? part.output : JSON.stringify(part);
|
|
19747
|
+
const stateRecord = getRecord(part?.state) ?? getRecord(parsed.state);
|
|
19748
|
+
const toolError = part?.is_error === true || part?.isError === true || stateRecord?.status === "error" || Boolean(getString(part?.error) || getString(stateRecord?.error));
|
|
19749
|
+
void presenter.onToolResult?.(toolId, output, toolError);
|
|
19750
|
+
if (toolError && !state.error) {
|
|
19751
|
+
const message = getString(part?.error) || getString(stateRecord?.error) || "OpenCode tool call failed.";
|
|
19752
|
+
state.error = message;
|
|
19753
|
+
void presenter.onError(message);
|
|
19754
|
+
}
|
|
19755
|
+
return true;
|
|
19756
|
+
}
|
|
19680
19757
|
case "session.error":
|
|
19681
19758
|
case "error": {
|
|
19682
19759
|
const errObj = typeof parsed.error === "object" && parsed.error !== null ? parsed.error : null;
|
|
@@ -19701,35 +19778,10 @@ var parseOpencodeStructuredLine = createStructuredLineParser(
|
|
|
19701
19778
|
"opencode_cli",
|
|
19702
19779
|
handleOpencodeStructuredEvent
|
|
19703
19780
|
);
|
|
19704
|
-
|
|
19705
|
-
|
|
19706
|
-
|
|
19707
|
-
|
|
19708
|
-
function buildAutonomousOpenCodeConfigContent(existing) {
|
|
19709
|
-
if (!existing?.trim()) return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
|
|
19710
|
-
try {
|
|
19711
|
-
const parsed = JSON.parse(existing);
|
|
19712
|
-
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
19713
|
-
return JSON.stringify({
|
|
19714
|
-
...parsed,
|
|
19715
|
-
permission: "allow"
|
|
19716
|
-
});
|
|
19717
|
-
}
|
|
19718
|
-
} catch {
|
|
19719
|
-
}
|
|
19720
|
-
return AUTONOMOUS_OPENCODE_CONFIG_CONTENT;
|
|
19721
|
-
}
|
|
19722
|
-
function withAutonomousOpenCodePermissions(context) {
|
|
19723
|
-
if (shouldUseReadOnlyRuntimePermissions(context.config)) return context;
|
|
19724
|
-
return {
|
|
19725
|
-
...context,
|
|
19726
|
-
env: {
|
|
19727
|
-
...context.env,
|
|
19728
|
-
OPENCODE_CONFIG_CONTENT: buildAutonomousOpenCodeConfigContent(
|
|
19729
|
-
context.env.OPENCODE_CONFIG_CONTENT
|
|
19730
|
-
)
|
|
19731
|
-
}
|
|
19732
|
-
};
|
|
19781
|
+
function buildOpencodeEffortArgs(selectedEffortLevel) {
|
|
19782
|
+
const trimmed = selectedEffortLevel?.trim().toLowerCase();
|
|
19783
|
+
if (!trimmed || !isEffortLevel(trimmed)) return [];
|
|
19784
|
+
return ["--variant", trimmed];
|
|
19733
19785
|
}
|
|
19734
19786
|
function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
19735
19787
|
return {
|
|
@@ -19752,7 +19804,7 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
|
19752
19804
|
command,
|
|
19753
19805
|
args: [],
|
|
19754
19806
|
buildArgs: (ctx, prompt) => {
|
|
19755
|
-
const args = ["run", "--format", "json"];
|
|
19807
|
+
const args = ["run", "--format", "json", "--thinking"];
|
|
19756
19808
|
if (!shouldUseReadOnlyRuntimePermissions(ctx.config)) {
|
|
19757
19809
|
args.push("--dangerously-skip-permissions");
|
|
19758
19810
|
}
|
|
@@ -19760,6 +19812,7 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
|
19760
19812
|
if (resumeId) args.push("--session", resumeId);
|
|
19761
19813
|
const model = ctx.config.selectedModel?.trim();
|
|
19762
19814
|
if (model?.includes("/")) args.push("--model", model);
|
|
19815
|
+
args.push(...buildOpencodeEffortArgs(ctx.config.selectedEffortLevel));
|
|
19763
19816
|
const attachmentPaths = [...imagePaths, ...filePaths];
|
|
19764
19817
|
const fileArgs = attachmentPaths.flatMap((p) => ["--file", p]);
|
|
19765
19818
|
if (fileArgs.length > 0) {
|
|
@@ -19780,6 +19833,395 @@ function createOpencodeCliBackend(command = "opencode", defaultArgs = []) {
|
|
|
19780
19833
|
}
|
|
19781
19834
|
};
|
|
19782
19835
|
}
|
|
19836
|
+
function parseSseEventBlock(block) {
|
|
19837
|
+
const dataLines = block.split("\n").map((line) => line.endsWith("\r") ? line.slice(0, -1) : line).filter((line) => line.startsWith("data:")).map((line) => line.slice("data:".length).replace(/^ /, ""));
|
|
19838
|
+
if (dataLines.length === 0) return null;
|
|
19839
|
+
try {
|
|
19840
|
+
const parsed = JSON.parse(dataLines.join("\n"));
|
|
19841
|
+
if (parsed && typeof parsed === "object" && typeof parsed.type === "string") {
|
|
19842
|
+
const record = parsed;
|
|
19843
|
+
return {
|
|
19844
|
+
id: typeof record.id === "string" ? record.id : void 0,
|
|
19845
|
+
type: record.type,
|
|
19846
|
+
properties: typeof record.properties === "object" && record.properties !== null ? record.properties : {}
|
|
19847
|
+
};
|
|
19848
|
+
}
|
|
19849
|
+
} catch {
|
|
19850
|
+
}
|
|
19851
|
+
return null;
|
|
19852
|
+
}
|
|
19853
|
+
async function connectOpenCodeSse(url2, signal) {
|
|
19854
|
+
const response = await fetch(url2, {
|
|
19855
|
+
signal,
|
|
19856
|
+
headers: { Accept: "text/event-stream" }
|
|
19857
|
+
});
|
|
19858
|
+
if (!response.ok || !response.body) {
|
|
19859
|
+
throw new Error(`OpenCode SSE connection failed: HTTP ${response.status}`);
|
|
19860
|
+
}
|
|
19861
|
+
const reader = response.body.getReader();
|
|
19862
|
+
const decoder = new TextDecoder();
|
|
19863
|
+
let closed = false;
|
|
19864
|
+
async function* generate() {
|
|
19865
|
+
let buffer = "";
|
|
19866
|
+
try {
|
|
19867
|
+
while (!closed) {
|
|
19868
|
+
const { value: value2, done } = await reader.read();
|
|
19869
|
+
if (done) return;
|
|
19870
|
+
buffer += decoder.decode(value2, { stream: true });
|
|
19871
|
+
let separatorIndex = buffer.indexOf("\n\n");
|
|
19872
|
+
while (separatorIndex !== -1) {
|
|
19873
|
+
const block = buffer.slice(0, separatorIndex);
|
|
19874
|
+
buffer = buffer.slice(separatorIndex + 2);
|
|
19875
|
+
const event = parseSseEventBlock(block);
|
|
19876
|
+
if (event) yield event;
|
|
19877
|
+
separatorIndex = buffer.indexOf("\n\n");
|
|
19878
|
+
}
|
|
19879
|
+
}
|
|
19880
|
+
} finally {
|
|
19881
|
+
try {
|
|
19882
|
+
reader.releaseLock();
|
|
19883
|
+
} catch {
|
|
19884
|
+
}
|
|
19885
|
+
}
|
|
19886
|
+
}
|
|
19887
|
+
return {
|
|
19888
|
+
events: generate(),
|
|
19889
|
+
close: () => {
|
|
19890
|
+
if (closed) return;
|
|
19891
|
+
closed = true;
|
|
19892
|
+
reader.cancel().catch(() => {
|
|
19893
|
+
});
|
|
19894
|
+
}
|
|
19895
|
+
};
|
|
19896
|
+
}
|
|
19897
|
+
var SERVER_LISTENING_PATTERN = /listening on (https?:\/\/\S+)/i;
|
|
19898
|
+
var STARTUP_TIMEOUT_MS = 2e4;
|
|
19899
|
+
var HEALTH_POLL_INTERVAL_MS = 200;
|
|
19900
|
+
var HEALTH_POLL_MAX_ATTEMPTS = 50;
|
|
19901
|
+
function startOpenCodeServer(command, context) {
|
|
19902
|
+
const child = spawnCli(command, ["serve", "--port", "0", "--hostname", "127.0.0.1"], context);
|
|
19903
|
+
const ready = new Promise((resolve22, reject) => {
|
|
19904
|
+
let settled = false;
|
|
19905
|
+
const timeout = setTimeout(() => {
|
|
19906
|
+
if (settled) return;
|
|
19907
|
+
settled = true;
|
|
19908
|
+
reject(
|
|
19909
|
+
new Error(`opencode serve did not report a listening URL within ${STARTUP_TIMEOUT_MS}ms`)
|
|
19910
|
+
);
|
|
19911
|
+
}, STARTUP_TIMEOUT_MS);
|
|
19912
|
+
const stdoutRl = (0, import_readline2.createInterface)({ input: child.stdout });
|
|
19913
|
+
const stderrRl = (0, import_readline2.createInterface)({ input: child.stderr });
|
|
19914
|
+
const stderrLines = [];
|
|
19915
|
+
const checkLine = (line) => {
|
|
19916
|
+
if (settled) return;
|
|
19917
|
+
const match = line.match(SERVER_LISTENING_PATTERN);
|
|
19918
|
+
if (!match) return;
|
|
19919
|
+
settled = true;
|
|
19920
|
+
clearTimeout(timeout);
|
|
19921
|
+
resolve22({ baseUrl: match[1].replace(/\/$/, "") });
|
|
19922
|
+
};
|
|
19923
|
+
stdoutRl.on("line", checkLine);
|
|
19924
|
+
stderrRl.on("line", (line) => {
|
|
19925
|
+
stderrLines.push(line);
|
|
19926
|
+
checkLine(line);
|
|
19927
|
+
});
|
|
19928
|
+
child.once("error", (error) => {
|
|
19929
|
+
if (settled) return;
|
|
19930
|
+
settled = true;
|
|
19931
|
+
clearTimeout(timeout);
|
|
19932
|
+
reject(error);
|
|
19933
|
+
});
|
|
19934
|
+
child.once("exit", (code) => {
|
|
19935
|
+
if (settled) return;
|
|
19936
|
+
settled = true;
|
|
19937
|
+
clearTimeout(timeout);
|
|
19938
|
+
reject(
|
|
19939
|
+
new Error(
|
|
19940
|
+
`opencode serve exited (code ${code}) before reporting a listening URL. stderr: ${stderrLines.slice(-5).join("\n")}`
|
|
19941
|
+
)
|
|
19942
|
+
);
|
|
19943
|
+
});
|
|
19944
|
+
});
|
|
19945
|
+
return { child, ready };
|
|
19946
|
+
}
|
|
19947
|
+
async function waitForHealthy(baseUrl, signal) {
|
|
19948
|
+
for (let attempt = 0; attempt < HEALTH_POLL_MAX_ATTEMPTS; attempt++) {
|
|
19949
|
+
if (signal.aborted)
|
|
19950
|
+
throw new Error("Aborted while waiting for opencode server to become healthy");
|
|
19951
|
+
try {
|
|
19952
|
+
const response = await fetch(`${baseUrl}/global/health`, { signal });
|
|
19953
|
+
if (response.ok) {
|
|
19954
|
+
const body = await response.json().catch(() => null);
|
|
19955
|
+
if (body?.healthy) return;
|
|
19956
|
+
}
|
|
19957
|
+
} catch {
|
|
19958
|
+
}
|
|
19959
|
+
await new Promise((resolve22) => setTimeout(resolve22, HEALTH_POLL_INTERVAL_MS));
|
|
19960
|
+
}
|
|
19961
|
+
throw new Error(`opencode server at ${baseUrl} did not become healthy in time`);
|
|
19962
|
+
}
|
|
19963
|
+
var NON_INTERACTIVE_DENY_RULES = [
|
|
19964
|
+
{ permission: "question", pattern: "*", action: "deny" },
|
|
19965
|
+
{ permission: "plan_enter", pattern: "*", action: "deny" },
|
|
19966
|
+
{ permission: "plan_exit", pattern: "*", action: "deny" }
|
|
19967
|
+
];
|
|
19968
|
+
async function resolveSession(baseUrl, context, signal) {
|
|
19969
|
+
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
19970
|
+
if (resumeId) {
|
|
19971
|
+
const existing = await fetch(`${baseUrl}/session/${encodeURIComponent(resumeId)}`, {
|
|
19972
|
+
signal
|
|
19973
|
+
}).catch(() => null);
|
|
19974
|
+
if (existing?.ok) return { id: resumeId };
|
|
19975
|
+
}
|
|
19976
|
+
const title = context.promptText.slice(0, 50) + (context.promptText.length > 50 ? "..." : "");
|
|
19977
|
+
const response = await fetch(`${baseUrl}/session`, {
|
|
19978
|
+
method: "POST",
|
|
19979
|
+
signal,
|
|
19980
|
+
headers: { "Content-Type": "application/json" },
|
|
19981
|
+
body: JSON.stringify({ title, permission: NON_INTERACTIVE_DENY_RULES })
|
|
19982
|
+
});
|
|
19983
|
+
if (!response.ok) {
|
|
19984
|
+
throw new Error(`Failed to create OpenCode session: HTTP ${response.status}`);
|
|
19985
|
+
}
|
|
19986
|
+
const info = await response.json();
|
|
19987
|
+
if (!info.id) throw new Error("OpenCode session create response had no id");
|
|
19988
|
+
return { id: info.id };
|
|
19989
|
+
}
|
|
19990
|
+
function buildPromptParts(promptText, imageFiles, attachmentFiles) {
|
|
19991
|
+
const attachments = [...imageFiles, ...attachmentFiles].map(
|
|
19992
|
+
(file) => ({
|
|
19993
|
+
type: "file",
|
|
19994
|
+
mime: file.mimeType,
|
|
19995
|
+
filename: file.filename,
|
|
19996
|
+
url: (0, import_url2.pathToFileURL)(file.path).href
|
|
19997
|
+
})
|
|
19998
|
+
);
|
|
19999
|
+
return [...attachments, { type: "text", text: promptText }];
|
|
20000
|
+
}
|
|
20001
|
+
async function sendPrompt(baseUrl, sessionId, context, parts2, signal) {
|
|
20002
|
+
const model = context.config.selectedModel?.trim();
|
|
20003
|
+
const modelBody = model && model.includes("/") ? (() => {
|
|
20004
|
+
const slashIndex = model.indexOf("/");
|
|
20005
|
+
return { providerID: model.slice(0, slashIndex), modelID: model.slice(slashIndex + 1) };
|
|
20006
|
+
})() : void 0;
|
|
20007
|
+
const variant = context.config.selectedEffortLevel?.trim().toLowerCase();
|
|
20008
|
+
const response = await fetch(`${baseUrl}/session/${encodeURIComponent(sessionId)}/message`, {
|
|
20009
|
+
method: "POST",
|
|
20010
|
+
signal,
|
|
20011
|
+
headers: { "Content-Type": "application/json" },
|
|
20012
|
+
body: JSON.stringify({
|
|
20013
|
+
parts: parts2,
|
|
20014
|
+
...modelBody ? { model: modelBody } : {},
|
|
20015
|
+
...variant && isEffortLevel(variant) ? { variant } : {}
|
|
20016
|
+
})
|
|
20017
|
+
});
|
|
20018
|
+
if (!response.ok) {
|
|
20019
|
+
const text = await response.text().catch(() => "");
|
|
20020
|
+
throw new Error(`OpenCode prompt request failed: HTTP ${response.status} ${text}`.trim());
|
|
20021
|
+
}
|
|
20022
|
+
}
|
|
20023
|
+
async function replyToPermission(baseUrl, requestId, reply, signal) {
|
|
20024
|
+
await fetch(`${baseUrl}/permission/${encodeURIComponent(requestId)}/reply`, {
|
|
20025
|
+
method: "POST",
|
|
20026
|
+
signal,
|
|
20027
|
+
headers: { "Content-Type": "application/json" },
|
|
20028
|
+
body: JSON.stringify({ reply })
|
|
20029
|
+
}).catch((error) => {
|
|
20030
|
+
console.warn("[opencode_serve] Failed to reply to permission request", requestId, error);
|
|
20031
|
+
});
|
|
20032
|
+
}
|
|
20033
|
+
function buildAgentResult(state, sessionId, aborted2) {
|
|
20034
|
+
if (aborted2) {
|
|
20035
|
+
return {
|
|
20036
|
+
success: false,
|
|
20037
|
+
summary: "Interrupted by user",
|
|
20038
|
+
filesModified: [],
|
|
20039
|
+
planFilesCreated: [],
|
|
20040
|
+
iterations: Math.max(state.iterations, 1),
|
|
20041
|
+
error: "Interrupted by user",
|
|
20042
|
+
providerSessionId: sessionId,
|
|
20043
|
+
runtimeSessionId: sessionId,
|
|
20044
|
+
backendKind: "opencode_serve",
|
|
20045
|
+
supportTier: "structured",
|
|
20046
|
+
usage: state.usage
|
|
20047
|
+
};
|
|
20048
|
+
}
|
|
20049
|
+
const failed = Boolean(state.error?.trim());
|
|
20050
|
+
const summary = state.summary.trim() || state.error?.trim() || (failed ? "Task failed" : "Task completed");
|
|
20051
|
+
return {
|
|
20052
|
+
success: !failed,
|
|
20053
|
+
summary,
|
|
20054
|
+
filesModified: [],
|
|
20055
|
+
planFilesCreated: [],
|
|
20056
|
+
iterations: Math.max(state.iterations, 1),
|
|
20057
|
+
...state.error?.trim() ? { error: state.error.trim() } : {},
|
|
20058
|
+
providerSessionId: sessionId,
|
|
20059
|
+
runtimeSessionId: sessionId,
|
|
20060
|
+
backendKind: "opencode_serve",
|
|
20061
|
+
supportTier: "structured",
|
|
20062
|
+
usage: state.usage
|
|
20063
|
+
};
|
|
20064
|
+
}
|
|
20065
|
+
function createOpencodeServeBackend(command = "opencode") {
|
|
20066
|
+
return {
|
|
20067
|
+
kind: "opencode_serve",
|
|
20068
|
+
supportTier: "structured",
|
|
20069
|
+
async run(context) {
|
|
20070
|
+
const { files: imageFiles, cleanup: cleanupImages } = writeImagesToTempFiles(
|
|
20071
|
+
context.config.images,
|
|
20072
|
+
context.cwd
|
|
20073
|
+
);
|
|
20074
|
+
const { files: attachmentFiles, cleanup: cleanupFiles } = writeFilesToTempFiles(
|
|
20075
|
+
context.config.files,
|
|
20076
|
+
context.cwd
|
|
20077
|
+
);
|
|
20078
|
+
const runContext = withAutonomousOpenCodePermissions(context);
|
|
20079
|
+
const readOnly = shouldUseReadOnlyRuntimePermissions(context.config);
|
|
20080
|
+
const { child, ready } = startOpenCodeServer(command, runContext);
|
|
20081
|
+
const state = { process: child, iterations: 0, summary: "" };
|
|
20082
|
+
context.onProcessSpawned?.(child);
|
|
20083
|
+
context.registerLivenessProbe?.(() => ({
|
|
20084
|
+
providerAlive: child.exitCode === null,
|
|
20085
|
+
lastRawOutputAgoMs: typeof state.lastRawOutputAtMs === "number" ? Date.now() - state.lastRawOutputAtMs : null
|
|
20086
|
+
}));
|
|
20087
|
+
let sseClose = null;
|
|
20088
|
+
let sessionId = "";
|
|
20089
|
+
let aborted2 = false;
|
|
20090
|
+
const onAbort = () => {
|
|
20091
|
+
aborted2 = true;
|
|
20092
|
+
sseClose?.();
|
|
20093
|
+
killProcessTree(child.pid, { signal: "SIGTERM", child });
|
|
20094
|
+
};
|
|
20095
|
+
context.abortController.signal.addEventListener("abort", onAbort, { once: true });
|
|
20096
|
+
try {
|
|
20097
|
+
const server = await ready;
|
|
20098
|
+
state.lastRawOutputAtMs = Date.now();
|
|
20099
|
+
await waitForHealthy(server.baseUrl, context.abortController.signal);
|
|
20100
|
+
const session = await resolveSession(
|
|
20101
|
+
server.baseUrl,
|
|
20102
|
+
runContext,
|
|
20103
|
+
context.abortController.signal
|
|
20104
|
+
);
|
|
20105
|
+
sessionId = session.id;
|
|
20106
|
+
state.runtimeSessionId = sessionId;
|
|
20107
|
+
const prompt = context.config.mode === "plan" ? buildPlanModePrefix(buildPromptWithSystem(context.config, context.promptText)) : buildPromptWithSystem(context.config, context.promptText);
|
|
20108
|
+
const parts2 = buildPromptParts(prompt, imageFiles, attachmentFiles);
|
|
20109
|
+
const connection = await connectOpenCodeSse(
|
|
20110
|
+
`${server.baseUrl}/event`,
|
|
20111
|
+
context.abortController.signal
|
|
20112
|
+
);
|
|
20113
|
+
sseClose = connection.close;
|
|
20114
|
+
const promptError = await sendPrompt(
|
|
20115
|
+
server.baseUrl,
|
|
20116
|
+
sessionId,
|
|
20117
|
+
runContext,
|
|
20118
|
+
parts2,
|
|
20119
|
+
context.abortController.signal
|
|
20120
|
+
).then(() => null).catch((error) => ({
|
|
20121
|
+
message: error instanceof Error ? error.message : String(error)
|
|
20122
|
+
}));
|
|
20123
|
+
if (context.abortController.signal.aborted) {
|
|
20124
|
+
} else if (promptError) {
|
|
20125
|
+
state.error = promptError.message;
|
|
20126
|
+
void context.presenter.onError(promptError.message);
|
|
20127
|
+
} else {
|
|
20128
|
+
for await (const event of connection.events) {
|
|
20129
|
+
if (aborted2) break;
|
|
20130
|
+
const done = handleOpenCodeServeEvent(
|
|
20131
|
+
event,
|
|
20132
|
+
sessionId,
|
|
20133
|
+
runContext,
|
|
20134
|
+
state,
|
|
20135
|
+
readOnly,
|
|
20136
|
+
server.baseUrl
|
|
20137
|
+
);
|
|
20138
|
+
if (done) break;
|
|
20139
|
+
}
|
|
20140
|
+
}
|
|
20141
|
+
return buildAgentResult(state, sessionId, aborted2);
|
|
20142
|
+
} catch (error) {
|
|
20143
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
20144
|
+
if (!aborted2 && !context.abortController.signal.aborted) {
|
|
20145
|
+
console.error("[opencode_serve] Run failed:", message);
|
|
20146
|
+
state.error = message;
|
|
20147
|
+
void context.presenter.onError(message);
|
|
20148
|
+
}
|
|
20149
|
+
return buildAgentResult(
|
|
20150
|
+
state,
|
|
20151
|
+
sessionId,
|
|
20152
|
+
aborted2 || context.abortController.signal.aborted
|
|
20153
|
+
);
|
|
20154
|
+
} finally {
|
|
20155
|
+
context.abortController.signal.removeEventListener("abort", onAbort);
|
|
20156
|
+
sseClose?.();
|
|
20157
|
+
killProcessTree(child.pid, { signal: "SIGTERM", child });
|
|
20158
|
+
cleanupImages();
|
|
20159
|
+
cleanupFiles();
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
};
|
|
20163
|
+
}
|
|
20164
|
+
function handleOpenCodeServeEvent(event, sessionId, context, state, readOnly, baseUrl) {
|
|
20165
|
+
const properties = event.properties;
|
|
20166
|
+
const eventSessionId = getString(properties.sessionID);
|
|
20167
|
+
if (eventSessionId && eventSessionId !== sessionId) return false;
|
|
20168
|
+
switch (event.type) {
|
|
20169
|
+
// Live-verified: the SSE bus emits a message.updated (and matching
|
|
20170
|
+
// message.part.updated) for the USER's own message too, not just the
|
|
20171
|
+
// assistant's — track user message ids so their echoed text is never
|
|
20172
|
+
// mistaken for assistant output below.
|
|
20173
|
+
case "message.updated": {
|
|
20174
|
+
const info = getRecord(properties.info);
|
|
20175
|
+
if (getString(info?.role) === "user") {
|
|
20176
|
+
const messageId = getString(info?.id);
|
|
20177
|
+
if (messageId) {
|
|
20178
|
+
state.opencodeUserMessageIds ??= /* @__PURE__ */ new Set();
|
|
20179
|
+
state.opencodeUserMessageIds.add(messageId);
|
|
20180
|
+
}
|
|
20181
|
+
}
|
|
20182
|
+
return false;
|
|
20183
|
+
}
|
|
20184
|
+
case "message.part.updated": {
|
|
20185
|
+
const part = getRecord(properties.part);
|
|
20186
|
+
if (!part) return false;
|
|
20187
|
+
const messageId = getString(part.messageID);
|
|
20188
|
+
if (messageId && state.opencodeUserMessageIds?.has(messageId)) return false;
|
|
20189
|
+
mapOpencodePart(part, context, state);
|
|
20190
|
+
return false;
|
|
20191
|
+
}
|
|
20192
|
+
// Live token-by-token streaming is explicitly out of scope — only the
|
|
20193
|
+
// start/complete snapshots from message.part.updated are consumed.
|
|
20194
|
+
case "message.part.delta":
|
|
20195
|
+
return false;
|
|
20196
|
+
case "permission.asked": {
|
|
20197
|
+
const requestId = getString(properties.id);
|
|
20198
|
+
if (requestId) {
|
|
20199
|
+
void replyToPermission(
|
|
20200
|
+
baseUrl,
|
|
20201
|
+
requestId,
|
|
20202
|
+
readOnly ? "reject" : "once",
|
|
20203
|
+
context.abortController.signal
|
|
20204
|
+
);
|
|
20205
|
+
}
|
|
20206
|
+
return false;
|
|
20207
|
+
}
|
|
20208
|
+
case "session.error": {
|
|
20209
|
+
const errorRecord = getRecord(properties.error);
|
|
20210
|
+
const message = getString(errorRecord?.message) || getString(properties.error) || "OpenCode session error.";
|
|
20211
|
+
if (!state.error) {
|
|
20212
|
+
state.error = message;
|
|
20213
|
+
void context.presenter.onError(message);
|
|
20214
|
+
}
|
|
20215
|
+
return false;
|
|
20216
|
+
}
|
|
20217
|
+
case "session.status": {
|
|
20218
|
+
const status = getRecord(properties.status);
|
|
20219
|
+
return getString(status?.type) === "idle";
|
|
20220
|
+
}
|
|
20221
|
+
default:
|
|
20222
|
+
return false;
|
|
20223
|
+
}
|
|
20224
|
+
}
|
|
19783
20225
|
function createGenericCliPassthroughBackend(command, defaultArgs = []) {
|
|
19784
20226
|
return {
|
|
19785
20227
|
kind: "generic_cli",
|
|
@@ -20008,6 +20450,7 @@ function defaultSelectedModelForBackend(backendKind) {
|
|
|
20008
20450
|
case "grok_cli":
|
|
20009
20451
|
return firstCatalogModelId(backendKind) ?? "grok-4.5";
|
|
20010
20452
|
case "opencode_cli":
|
|
20453
|
+
case "opencode_serve":
|
|
20011
20454
|
return void 0;
|
|
20012
20455
|
case "antigravity_cli":
|
|
20013
20456
|
return void 0;
|
|
@@ -20149,7 +20592,7 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
20149
20592
|
};
|
|
20150
20593
|
const releaseGate = await acquireMcpConfigWriteGate();
|
|
20151
20594
|
try {
|
|
20152
|
-
const writtenClis = syncAlanMcpSessionHeaders({
|
|
20595
|
+
const writtenClis = await syncAlanMcpSessionHeaders({
|
|
20153
20596
|
conversationId,
|
|
20154
20597
|
teamId: config.teamId,
|
|
20155
20598
|
runHeaders: config.alanMcp?.headers,
|
|
@@ -20224,6 +20667,7 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
20224
20667
|
case "cursor_agent_cli":
|
|
20225
20668
|
case "droid_cli":
|
|
20226
20669
|
case "opencode_cli":
|
|
20670
|
+
case "opencode_serve":
|
|
20227
20671
|
case "copilot_cli":
|
|
20228
20672
|
case "supatest_cli":
|
|
20229
20673
|
case "kimi_cli":
|
|
@@ -20291,7 +20735,7 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
20291
20735
|
const promptText = this.buildPromptText(runtimeConfig);
|
|
20292
20736
|
const runtimeCommand = runtimeConfig.runtimeCommand || this.runtime.runtimeCommand;
|
|
20293
20737
|
const runtimeArgs = runtimeConfig.runtimeArgs || this.runtime.runtimeArgs || [];
|
|
20294
|
-
const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "antigravity_cli" ? createAntigravityCliBackend(runtimeCommand || "agy", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi-cli", runtimeArgs) : backendKind === "grok_cli" ? createGrokCliBackend(runtimeCommand || "grok", runtimeArgs) : backendKind === "supatest_cli" ? createSupatestCliBackend(runtimeCommand || "supatest", runtimeArgs) : createGenericCliPassthroughBackend(
|
|
20738
|
+
const backend = backendKind === "claude_cli" ? createClaudeCliBackend(runtimeCommand || "claude", runtimeArgs) : backendKind === "codex_app_server" ? createCodexRuntimeBackend(runtimeCommand || "codex", runtimeArgs) : backendKind === "copilot_cli" ? createCopilotCliBackend(runtimeCommand || "copilot", runtimeArgs) : backendKind === "cursor_agent_cli" ? createCursorAgentCliBackend(runtimeCommand || "cursor-agent", runtimeArgs) : backendKind === "antigravity_cli" ? createAntigravityCliBackend(runtimeCommand || "agy", runtimeArgs) : backendKind === "droid_cli" ? createDroidCliBackend(runtimeCommand || "droid", runtimeArgs) : backendKind === "opencode_cli" ? createOpencodeCliBackend(runtimeCommand || "opencode", runtimeArgs) : backendKind === "opencode_serve" ? createOpencodeServeBackend(runtimeCommand || "opencode") : backendKind === "kimi_cli" ? createKimiCliBackend(runtimeCommand || "kimi-cli", runtimeArgs) : backendKind === "grok_cli" ? createGrokCliBackend(runtimeCommand || "grok", runtimeArgs) : backendKind === "supatest_cli" ? createSupatestCliBackend(runtimeCommand || "supatest", runtimeArgs) : createGenericCliPassthroughBackend(
|
|
20295
20739
|
runtimeCommand || "generic-cli",
|
|
20296
20740
|
runtimeArgs
|
|
20297
20741
|
);
|
|
@@ -21004,13 +21448,20 @@ ${meta.description}`);
|
|
|
21004
21448
|
}
|
|
21005
21449
|
if (conversationId) artifactIds.push(`- conversationId: "${conversationId}"`);
|
|
21006
21450
|
if (artifactIds.length > 0) {
|
|
21451
|
+
const artifactTools = ["create_plan", "create_document_artifact", "create_test_report"];
|
|
21452
|
+
if (taskId || isWorkflowSession) artifactTools.push("upsert_prototype_version");
|
|
21007
21453
|
parts2.push(
|
|
21008
21454
|
[
|
|
21009
|
-
|
|
21455
|
+
`When creating artifacts using MCP tools (${artifactTools.join(", ")}), always pass these IDs:`,
|
|
21010
21456
|
...artifactIds
|
|
21011
21457
|
].join("\n")
|
|
21012
21458
|
);
|
|
21013
21459
|
}
|
|
21460
|
+
if (teamId && conversationId && !taskId && !isWorkflowSession) {
|
|
21461
|
+
parts2.push(
|
|
21462
|
+
"This is a standalone chat. For create_plan, create_document_artifact, and create_test_report, omit taskId and workflowId; conversationId is the artifact owner. Do not use create_document unless the user explicitly asks for a standalone team Document."
|
|
21463
|
+
);
|
|
21464
|
+
}
|
|
21014
21465
|
if (workflowId && conversationId && teamId) {
|
|
21015
21466
|
parts2.push(
|
|
21016
21467
|
[
|
|
@@ -22274,9 +22725,7 @@ var CODEX_ALAN_TOML_SECTION = "mcp_servers.alan";
|
|
|
22274
22725
|
function isCodexAlanTomlSection(section) {
|
|
22275
22726
|
return section === CODEX_ALAN_TOML_SECTION || section.startsWith(`${CODEX_ALAN_TOML_SECTION}.`);
|
|
22276
22727
|
}
|
|
22277
|
-
function
|
|
22278
|
-
if (existingContent === null || existingContent.trim() === "") return desiredContent;
|
|
22279
|
-
assertValidExistingToml(path, existingContent);
|
|
22728
|
+
function stripCodexAlanSection(existingContent) {
|
|
22280
22729
|
const lines = existingContent.split("\n");
|
|
22281
22730
|
const retained = [];
|
|
22282
22731
|
let insideAlanSection = false;
|
|
@@ -22287,7 +22736,12 @@ function mergeCodexAlanSection(path, existingContent, desiredContent) {
|
|
|
22287
22736
|
}
|
|
22288
22737
|
if (!insideAlanSection) retained.push(line);
|
|
22289
22738
|
}
|
|
22290
|
-
|
|
22739
|
+
return retained.join("\n").trimEnd();
|
|
22740
|
+
}
|
|
22741
|
+
function mergeCodexAlanSection(path, existingContent, desiredContent) {
|
|
22742
|
+
if (existingContent === null || existingContent.trim() === "") return desiredContent;
|
|
22743
|
+
assertValidExistingToml(path, existingContent);
|
|
22744
|
+
const prefix = stripCodexAlanSection(existingContent);
|
|
22291
22745
|
const merged = `${prefix}${prefix ? "\n\n" : ""}${desiredContent.trim()}
|
|
22292
22746
|
`;
|
|
22293
22747
|
try {
|
|
@@ -22373,6 +22827,45 @@ function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os6.homedir
|
|
|
22373
22827
|
verification: inspection.verification
|
|
22374
22828
|
};
|
|
22375
22829
|
}
|
|
22830
|
+
function removeAlanFromJsonConfig(path, existingContent) {
|
|
22831
|
+
const config = parseJsonObject2(path, existingContent);
|
|
22832
|
+
for (const containerKey of ["mcpServers", "mcp"]) {
|
|
22833
|
+
const container = config[containerKey];
|
|
22834
|
+
if (container && typeof container === "object" && !Array.isArray(container)) {
|
|
22835
|
+
delete container.alan;
|
|
22836
|
+
}
|
|
22837
|
+
}
|
|
22838
|
+
return JSON.stringify(config);
|
|
22839
|
+
}
|
|
22840
|
+
function unregisterAlanMcp(backendKind, home = (0, import_node_os6.homedir)()) {
|
|
22841
|
+
const removedPaths = [];
|
|
22842
|
+
const errors = [];
|
|
22843
|
+
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
22844
|
+
for (const file of files) {
|
|
22845
|
+
if (!(0, import_node_fs7.existsSync)(file.path)) continue;
|
|
22846
|
+
const releaseLock = waitForConfigLock(`${file.path}.alan-lock`);
|
|
22847
|
+
try {
|
|
22848
|
+
const current = (0, import_node_fs7.readFileSync)(file.path, "utf8");
|
|
22849
|
+
let next;
|
|
22850
|
+
if (file.path.endsWith(".toml")) {
|
|
22851
|
+
const stripped = stripCodexAlanSection(current);
|
|
22852
|
+
next = stripped ? `${stripped}
|
|
22853
|
+
` : "";
|
|
22854
|
+
} else {
|
|
22855
|
+
next = removeAlanFromJsonConfig(file.path, current);
|
|
22856
|
+
}
|
|
22857
|
+
if (next !== current) {
|
|
22858
|
+
atomicWriteManagedConfig(file.path, next);
|
|
22859
|
+
removedPaths.push(file.path);
|
|
22860
|
+
}
|
|
22861
|
+
} catch (err) {
|
|
22862
|
+
errors.push(err instanceof Error ? err.message : String(err));
|
|
22863
|
+
} finally {
|
|
22864
|
+
releaseLock();
|
|
22865
|
+
}
|
|
22866
|
+
}
|
|
22867
|
+
return { removedPaths, errors };
|
|
22868
|
+
}
|
|
22376
22869
|
function isHttpUrl(value2) {
|
|
22377
22870
|
if (typeof value2 !== "string") return false;
|
|
22378
22871
|
try {
|
|
@@ -22719,6 +23212,88 @@ function extractCodexDiscoveredModelIdsFromDebugJson(value2) {
|
|
|
22719
23212
|
}
|
|
22720
23213
|
return [...new Set(ids)];
|
|
22721
23214
|
}
|
|
23215
|
+
var OPENCODE_MODEL_ID_LINE_PATTERN = /^[a-z0-9][\w.-]*\/[\w.-]+$/i;
|
|
23216
|
+
var OPENCODE_KNOWN_VARIANT_KEYS = /* @__PURE__ */ new Set([
|
|
23217
|
+
"minimal",
|
|
23218
|
+
"none",
|
|
23219
|
+
"low",
|
|
23220
|
+
"medium",
|
|
23221
|
+
"high",
|
|
23222
|
+
"xhigh",
|
|
23223
|
+
"extra-high",
|
|
23224
|
+
"max",
|
|
23225
|
+
"ultra"
|
|
23226
|
+
]);
|
|
23227
|
+
function parseOpenCodeVerboseModelOutput(text) {
|
|
23228
|
+
const entries = [];
|
|
23229
|
+
const lines = text.split(/\r?\n/);
|
|
23230
|
+
let i = 0;
|
|
23231
|
+
while (i < lines.length) {
|
|
23232
|
+
const line = (lines[i] ?? "").trim();
|
|
23233
|
+
i += 1;
|
|
23234
|
+
if (!OPENCODE_MODEL_ID_LINE_PATTERN.test(line)) continue;
|
|
23235
|
+
let jsonStart = i;
|
|
23236
|
+
while (jsonStart < lines.length && (lines[jsonStart] ?? "").trim() === "") jsonStart += 1;
|
|
23237
|
+
if (jsonStart >= lines.length || !(lines[jsonStart] ?? "").trim().startsWith("{")) continue;
|
|
23238
|
+
let depth = 0;
|
|
23239
|
+
let inString = false;
|
|
23240
|
+
let escaped = false;
|
|
23241
|
+
let endLineIdx = -1;
|
|
23242
|
+
let buffer = "";
|
|
23243
|
+
for (let j = jsonStart; j < lines.length; j += 1) {
|
|
23244
|
+
const raw = lines[j] ?? "";
|
|
23245
|
+
buffer += (buffer ? "\n" : "") + raw;
|
|
23246
|
+
for (const ch of raw) {
|
|
23247
|
+
if (escaped) {
|
|
23248
|
+
escaped = false;
|
|
23249
|
+
continue;
|
|
23250
|
+
}
|
|
23251
|
+
if (ch === "\\" && inString) {
|
|
23252
|
+
escaped = true;
|
|
23253
|
+
continue;
|
|
23254
|
+
}
|
|
23255
|
+
if (ch === '"') {
|
|
23256
|
+
inString = !inString;
|
|
23257
|
+
continue;
|
|
23258
|
+
}
|
|
23259
|
+
if (inString) continue;
|
|
23260
|
+
if (ch === "{") depth += 1;
|
|
23261
|
+
else if (ch === "}") depth -= 1;
|
|
23262
|
+
}
|
|
23263
|
+
if (depth === 0) {
|
|
23264
|
+
endLineIdx = j;
|
|
23265
|
+
break;
|
|
23266
|
+
}
|
|
23267
|
+
}
|
|
23268
|
+
if (endLineIdx === -1) break;
|
|
23269
|
+
try {
|
|
23270
|
+
const obj = JSON.parse(buffer);
|
|
23271
|
+
entries.push({
|
|
23272
|
+
id: line,
|
|
23273
|
+
reasoning: obj.capabilities?.reasoning === true,
|
|
23274
|
+
variants: obj.variants ? Object.keys(obj.variants) : []
|
|
23275
|
+
});
|
|
23276
|
+
} catch {
|
|
23277
|
+
}
|
|
23278
|
+
i = endLineIdx + 1;
|
|
23279
|
+
}
|
|
23280
|
+
return entries;
|
|
23281
|
+
}
|
|
23282
|
+
function extractOpenCodeDiscoveredModelIdsFromVerboseOutput(text) {
|
|
23283
|
+
const entries = parseOpenCodeVerboseModelOutput(text);
|
|
23284
|
+
const ids = [];
|
|
23285
|
+
for (const entry of entries) {
|
|
23286
|
+
const knownVariants = entry.reasoning ? entry.variants.filter((variant) => OPENCODE_KNOWN_VARIANT_KEYS.has(variant)) : [];
|
|
23287
|
+
if (knownVariants.length === 0) {
|
|
23288
|
+
ids.push(entry.id);
|
|
23289
|
+
continue;
|
|
23290
|
+
}
|
|
23291
|
+
for (const variant of knownVariants) {
|
|
23292
|
+
ids.push(`${entry.id}-${variant}`);
|
|
23293
|
+
}
|
|
23294
|
+
}
|
|
23295
|
+
return [...new Set(ids)];
|
|
23296
|
+
}
|
|
22722
23297
|
function extractModelIdsFromText(value2) {
|
|
22723
23298
|
const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
22724
23299
|
const ignoredLinePattern = /^(available models|models|usage|error|flag provided|try |agy\b|warning:)/i;
|
|
@@ -22829,12 +23404,24 @@ async function probeJsonOrTextModels(executable, env, argSets, options) {
|
|
|
22829
23404
|
return [];
|
|
22830
23405
|
}
|
|
22831
23406
|
async function discoverOpenCodeModels(executable, env) {
|
|
22832
|
-
const
|
|
23407
|
+
const verbose = await runCliProbeAsync(
|
|
23408
|
+
executable,
|
|
23409
|
+
withModelProbeEnv(env),
|
|
23410
|
+
["models", "--verbose"],
|
|
23411
|
+
MODEL_DISCOVERY_TIMEOUT_MS
|
|
23412
|
+
);
|
|
23413
|
+
if (verbose && !verbose.error && verbose.status === 0 && verbose.stdout.trim()) {
|
|
23414
|
+
const withEffort = extractOpenCodeDiscoveredModelIdsFromVerboseOutput(verbose.stdout);
|
|
23415
|
+
if (withEffort.length > 0) return withEffort;
|
|
23416
|
+
}
|
|
23417
|
+
const plain = await probeJsonOrTextModels(executable, env, [["models"]]);
|
|
23418
|
+
if (plain.length > 0) return plain;
|
|
23419
|
+
const legacy = await probeJsonOrTextModels(executable, env, [
|
|
22833
23420
|
["models", "--json"],
|
|
22834
23421
|
["models", "list", "--json"],
|
|
22835
23422
|
["model", "list", "--json"]
|
|
22836
23423
|
]);
|
|
22837
|
-
return
|
|
23424
|
+
return legacy.length > 0 ? legacy : OPENCODE_ZEN_MODEL_IDS;
|
|
22838
23425
|
}
|
|
22839
23426
|
async function discoverAntigravityModels(executable, env) {
|
|
22840
23427
|
return probeJsonOrTextModels(executable, env, [["models"]], {
|
|
@@ -23080,7 +23667,7 @@ var RunStartGate = class {
|
|
|
23080
23667
|
};
|
|
23081
23668
|
|
|
23082
23669
|
// src/version.ts
|
|
23083
|
-
var AGENT_VERSION = "0.1.
|
|
23670
|
+
var AGENT_VERSION = "0.1.45";
|
|
23084
23671
|
|
|
23085
23672
|
// src/workspace-relocation.ts
|
|
23086
23673
|
var import_node_child_process3 = require("child_process");
|
|
@@ -25224,6 +25811,21 @@ async function startDaemon(args) {
|
|
|
25224
25811
|
recentLogs.push(`${(/* @__PURE__ */ new Date()).toISOString()} ${message}`);
|
|
25225
25812
|
if (recentLogs.length > 200) recentLogs.splice(0, recentLogs.length - 200);
|
|
25226
25813
|
};
|
|
25814
|
+
const alanMcpRegisteredRuns = /* @__PURE__ */ new Set();
|
|
25815
|
+
const alanMcpRegisteredBackends = /* @__PURE__ */ new Set();
|
|
25816
|
+
const finalizeAlanMcpForRun = (runId) => {
|
|
25817
|
+
if (!alanMcpRegisteredRuns.delete(runId)) return;
|
|
25818
|
+
if (alanMcpRegisteredRuns.size > 0) return;
|
|
25819
|
+
for (const backend of alanMcpRegisteredBackends) {
|
|
25820
|
+
try {
|
|
25821
|
+
const { removedPaths } = unregisterAlanMcp(backend);
|
|
25822
|
+
if (removedPaths.length > 0) pushLog(`mcp-unregistered backend=${backend} run=${runId}`);
|
|
25823
|
+
} catch (err) {
|
|
25824
|
+
pushLog(`mcp-unregister-failed backend=${backend} ${err.message}`);
|
|
25825
|
+
}
|
|
25826
|
+
}
|
|
25827
|
+
alanMcpRegisteredBackends.clear();
|
|
25828
|
+
};
|
|
25227
25829
|
const workspaceAccessTracker = new WorkspaceAccessTracker();
|
|
25228
25830
|
const recheckWorkspaceIssue = () => {
|
|
25229
25831
|
const previousPath = workspaceAccessTracker.recoveryPath();
|
|
@@ -26006,6 +26608,10 @@ async function startDaemon(args) {
|
|
|
26006
26608
|
workspaceLease: workspaceLeaseCapture.lease
|
|
26007
26609
|
});
|
|
26008
26610
|
pendingRunStarts.delete(payload.runId);
|
|
26611
|
+
if (payload.alanMcp && backendKind) {
|
|
26612
|
+
alanMcpRegisteredRuns.add(payload.runId);
|
|
26613
|
+
alanMcpRegisteredBackends.add(backendKind);
|
|
26614
|
+
}
|
|
26009
26615
|
void agent.run({
|
|
26010
26616
|
task: payload.content,
|
|
26011
26617
|
maxIterations: payload.maxIterations ?? 50,
|
|
@@ -26051,6 +26657,7 @@ async function startDaemon(args) {
|
|
|
26051
26657
|
} catch {
|
|
26052
26658
|
pushLog(`run-journal-remove-failed run=${payload.runId}`);
|
|
26053
26659
|
}
|
|
26660
|
+
finalizeAlanMcpForRun(payload.runId);
|
|
26054
26661
|
releaseActiveRun();
|
|
26055
26662
|
});
|
|
26056
26663
|
});
|
|
@@ -28037,7 +28644,7 @@ async function runSessionFromEnv() {
|
|
|
28037
28644
|
const task = process.env.ALAN_TASK;
|
|
28038
28645
|
const teamId = process.env.ALAN_TEAM_ID || void 0;
|
|
28039
28646
|
const projectPath = process.env.ALAN_PROJECT_PATH || "/home/user/workspace";
|
|
28040
|
-
const backendKind = process.env.ALAN_BACKEND_KIND || "
|
|
28647
|
+
const backendKind = process.env.ALAN_BACKEND_KIND || "codex_app_server";
|
|
28041
28648
|
const agentId = process.env.ALAN_AGENT_ID || void 0;
|
|
28042
28649
|
const agentPrompt = process.env.ALAN_AGENT_PROMPT || void 0;
|
|
28043
28650
|
const mode = process.env.ALAN_MODE || "agent";
|