@aiden-ade/sandbox-agent 0.1.32 → 0.1.35
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 +1056 -428
- 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 +9 -5
package/dist/index.cjs
CHANGED
|
@@ -4810,14 +4810,23 @@ var require_websocket_server = __commonJS({
|
|
|
4810
4810
|
}
|
|
4811
4811
|
});
|
|
4812
4812
|
|
|
4813
|
+
// src/legacy-env.ts
|
|
4814
|
+
for (const [key, value2] of Object.entries(process.env)) {
|
|
4815
|
+
if (!key.startsWith("ALAN_") || value2 === void 0) continue;
|
|
4816
|
+
const alanKey = `ALAN_${key.slice("ALAN_".length)}`;
|
|
4817
|
+
if (process.env[alanKey] === void 0 || process.env[alanKey] === "") {
|
|
4818
|
+
process.env[alanKey] = value2;
|
|
4819
|
+
}
|
|
4820
|
+
}
|
|
4821
|
+
|
|
4813
4822
|
// src/cli-help.ts
|
|
4814
|
-
var HELP_TEXT = `
|
|
4823
|
+
var HELP_TEXT = `Alan agent CLI
|
|
4815
4824
|
|
|
4816
4825
|
Usage:
|
|
4817
|
-
|
|
4826
|
+
alan-agent <command> [options]
|
|
4818
4827
|
|
|
4819
4828
|
Commands:
|
|
4820
|
-
setup Register this computer with a setup token from
|
|
4829
|
+
setup Register this computer with a setup token from Alan
|
|
4821
4830
|
login Register this computer through browser authorization
|
|
4822
4831
|
daemon Start the durable runtime daemon
|
|
4823
4832
|
stop Stop the local daemon (or active runs only via the local API /stop)
|
|
@@ -4826,22 +4835,22 @@ Commands:
|
|
|
4826
4835
|
scan-models Probe installed agent CLIs and print discovered models
|
|
4827
4836
|
logout Remove local runtime configuration
|
|
4828
4837
|
token Rotate or revoke the configured runtime token
|
|
4829
|
-
run-session Internal: run one ephemeral sandbox/session from
|
|
4838
|
+
run-session Internal: run one ephemeral sandbox/session from ALAN_* env vars
|
|
4830
4839
|
|
|
4831
4840
|
Common flows:
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4841
|
+
alan-agent login
|
|
4842
|
+
alan-agent setup --setup-token <token>
|
|
4843
|
+
alan-agent setup --setup-token <token> --scope user
|
|
4844
|
+
alan-agent daemon
|
|
4845
|
+
alan-agent stop
|
|
4837
4846
|
|
|
4838
4847
|
Local development:
|
|
4839
|
-
|
|
4840
|
-
|
|
4848
|
+
alan-agent login --profile local
|
|
4849
|
+
alan-agent setup --profile local --setup-token <token>
|
|
4841
4850
|
`;
|
|
4842
4851
|
function hasRunSessionEnv(env) {
|
|
4843
4852
|
return Boolean(
|
|
4844
|
-
env.
|
|
4853
|
+
env.ALAN_WS_URL && env.ALAN_SESSION_ID && env.ALAN_TASK_ID && env.ALAN_SESSION_TOKEN && env.ALAN_TASK !== void 0
|
|
4845
4854
|
);
|
|
4846
4855
|
}
|
|
4847
4856
|
function resolveCommand(args, env) {
|
|
@@ -4986,7 +4995,7 @@ async function withTimeout(promise, timeoutMs, fallback) {
|
|
|
4986
4995
|
}
|
|
4987
4996
|
async function collectCodexLimits(env) {
|
|
4988
4997
|
const runtimeEnv = augmentCliPath(env);
|
|
4989
|
-
const codexCommand = resolveExecutable("codex", runtimeEnv, "
|
|
4998
|
+
const codexCommand = resolveExecutable("codex", runtimeEnv, "ALAN_CODEX_PATH");
|
|
4990
4999
|
if (!codexCommand)
|
|
4991
5000
|
return [];
|
|
4992
5001
|
return withTimeout(new Promise((resolve3) => {
|
|
@@ -5040,7 +5049,7 @@ async function collectCodexLimits(env) {
|
|
|
5040
5049
|
void (async () => {
|
|
5041
5050
|
try {
|
|
5042
5051
|
await request("initialize", {
|
|
5043
|
-
clientInfo: { name: "
|
|
5052
|
+
clientInfo: { name: "alan", version: "runtime" }
|
|
5044
5053
|
});
|
|
5045
5054
|
child.stdin.write(`${JSON.stringify({ method: "initialized", params: {} })}
|
|
5046
5055
|
`);
|
|
@@ -9255,7 +9264,7 @@ function resolveAgentConfigFileName(profile) {
|
|
|
9255
9264
|
case "production":
|
|
9256
9265
|
return "config.json";
|
|
9257
9266
|
case "local":
|
|
9258
|
-
return "config-
|
|
9267
|
+
return "config-local.json";
|
|
9259
9268
|
case "staging":
|
|
9260
9269
|
return "config-staging.json";
|
|
9261
9270
|
case "custom":
|
|
@@ -12870,28 +12879,28 @@ var import_node_fs2 = require("fs");
|
|
|
12870
12879
|
var import_node_os2 = require("os");
|
|
12871
12880
|
var import_node_path2 = require("path");
|
|
12872
12881
|
var PROVIDER_CLI_COMMANDS = {
|
|
12873
|
-
claude_cli: { provider: "claude_cli", command: "claude", envVars: ["
|
|
12882
|
+
claude_cli: { provider: "claude_cli", command: "claude", envVars: ["ALAN_CLAUDE_PATH"] },
|
|
12874
12883
|
codex_app_server: {
|
|
12875
12884
|
provider: "codex_app_server",
|
|
12876
12885
|
command: "codex",
|
|
12877
|
-
envVars: ["
|
|
12886
|
+
envVars: ["ALAN_CODEX_PATH"]
|
|
12878
12887
|
},
|
|
12879
|
-
copilot_cli: { provider: "copilot_cli", command: "copilot", envVars: ["
|
|
12888
|
+
copilot_cli: { provider: "copilot_cli", command: "copilot", envVars: ["ALAN_COPILOT_PATH"] },
|
|
12880
12889
|
cursor_agent_cli: {
|
|
12881
12890
|
provider: "cursor_agent_cli",
|
|
12882
12891
|
command: "cursor-agent",
|
|
12883
|
-
envVars: ["
|
|
12892
|
+
envVars: ["ALAN_CURSOR_AGENT_PATH"]
|
|
12884
12893
|
},
|
|
12885
12894
|
antigravity_cli: {
|
|
12886
12895
|
provider: "antigravity_cli",
|
|
12887
12896
|
command: "agy",
|
|
12888
|
-
envVars: ["
|
|
12897
|
+
envVars: ["ALAN_ANTIGRAVITY_PATH"]
|
|
12889
12898
|
},
|
|
12890
|
-
kimi_cli: { provider: "kimi_cli", command: "kimi-cli", envVars: ["
|
|
12891
|
-
grok_cli: { provider: "grok_cli", command: "grok", envVars: ["
|
|
12892
|
-
opencode_cli: { provider: "opencode_cli", command: "opencode", envVars: ["
|
|
12893
|
-
droid_cli: { provider: "droid_cli", command: "droid", envVars: ["
|
|
12894
|
-
supatest_cli: { provider: "supatest_cli", command: "supatest", envVars: ["
|
|
12899
|
+
kimi_cli: { provider: "kimi_cli", command: "kimi-cli", envVars: ["ALAN_KIMI_PATH"] },
|
|
12900
|
+
grok_cli: { provider: "grok_cli", command: "grok", envVars: ["ALAN_GROK_PATH"] },
|
|
12901
|
+
opencode_cli: { provider: "opencode_cli", command: "opencode", envVars: ["ALAN_OPENCODE_PATH"] },
|
|
12902
|
+
droid_cli: { provider: "droid_cli", command: "droid", envVars: ["ALAN_DROID_PATH"] },
|
|
12903
|
+
supatest_cli: { provider: "supatest_cli", command: "supatest", envVars: ["ALAN_SUPATEST_PATH"] }
|
|
12895
12904
|
};
|
|
12896
12905
|
var DISCOVERABLE_PROVIDER_KINDS = Object.keys(PROVIDER_CLI_COMMANDS);
|
|
12897
12906
|
var PROVIDER_ALIASES = {
|
|
@@ -13177,7 +13186,7 @@ var import_node_fs4 = require("fs");
|
|
|
13177
13186
|
var import_node_fs3 = require("fs");
|
|
13178
13187
|
var import_node_os3 = require("os");
|
|
13179
13188
|
var import_node_path3 = require("path");
|
|
13180
|
-
var DEFAULT_AGENT_DIR = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".
|
|
13189
|
+
var DEFAULT_AGENT_DIR = (0, import_node_path3.join)((0, import_node_os3.homedir)(), ".alan", "agent");
|
|
13181
13190
|
function resolveAgentConfigPath(input) {
|
|
13182
13191
|
if (input?.explicitPath?.trim()) return input.explicitPath.trim();
|
|
13183
13192
|
const profile = normalizeAgentEndpointProfile(
|
|
@@ -13191,15 +13200,15 @@ function configExistsAtPath(configPath) {
|
|
|
13191
13200
|
}
|
|
13192
13201
|
function resolveEndpointProfileFromArgs(args) {
|
|
13193
13202
|
const index = args.indexOf("--profile");
|
|
13194
|
-
const raw = index === -1 ? process.env.
|
|
13203
|
+
const raw = index === -1 ? process.env.ALAN_AGENT_PROFILE : args[index + 1];
|
|
13195
13204
|
return normalizeAgentEndpointProfile(raw) ?? "production";
|
|
13196
13205
|
}
|
|
13197
13206
|
function bindAgentConfigPathFromArgs(args) {
|
|
13198
|
-
const explicit = process.env.
|
|
13207
|
+
const explicit = process.env.ALAN_AGENT_CONFIG_PATH?.trim();
|
|
13199
13208
|
if (explicit) return explicit;
|
|
13200
13209
|
const profile = resolveEndpointProfileFromArgs(args);
|
|
13201
13210
|
const configPath = resolveAgentConfigPath({ profile });
|
|
13202
|
-
process.env.
|
|
13211
|
+
process.env.ALAN_AGENT_CONFIG_PATH = configPath;
|
|
13203
13212
|
return configPath;
|
|
13204
13213
|
}
|
|
13205
13214
|
|
|
@@ -13270,7 +13279,7 @@ function buildComputerReadiness(input) {
|
|
|
13270
13279
|
check(
|
|
13271
13280
|
"ws_connected",
|
|
13272
13281
|
input.connected ? "pass" : "fail",
|
|
13273
|
-
input.connected ? "Daemon is connected to
|
|
13282
|
+
input.connected ? "Daemon is connected to Alan backend." : "Daemon is disconnected from Alan backend.",
|
|
13274
13283
|
{
|
|
13275
13284
|
nowMs,
|
|
13276
13285
|
code: input.connected ? void 0 : "ws_disconnected",
|
|
@@ -13329,7 +13338,7 @@ Before create_task:
|
|
|
13329
13338
|
// ../shared/dist/agent/agent-definitions.js
|
|
13330
13339
|
var COORDINATOR_PROMPT = `## You are the Orchestrator
|
|
13331
13340
|
|
|
13332
|
-
You are the **Orchestrator** for this task. When asked "who are you?", "what agent are you?", "are you the coordinator?", "are you the orchestrator?", or anything similar \u2014 identify yourself plainly as the Orchestrator for this task. Do not say "I'm
|
|
13341
|
+
You are the **Orchestrator** for this task. When asked "who are you?", "what agent are you?", "are you the coordinator?", "are you the orchestrator?", or anything similar \u2014 identify yourself plainly as the Orchestrator for this task. Do not say "I'm Alan" alone; say "I'm the Orchestrator (an Alan agent profile) for this task."
|
|
13333
13342
|
|
|
13334
13343
|
Your primary job is orchestration: understand the task, choose the next unit of work, keep context current, and decide whether to act directly or delegate to a specialist.
|
|
13335
13344
|
|
|
@@ -13345,7 +13354,7 @@ Use these tools, in roughly this order of frequency:
|
|
|
13345
13354
|
5. \`get_session_context\` \u2014 check what a worker accomplished
|
|
13346
13355
|
6. \`update_task\` \u2014 keep status, priority, labels current
|
|
13347
13356
|
7. \`send_session_message\` / \`start_task_session\` \u2014 continue a specific session or force a new session only when needed
|
|
13348
|
-
8. \`list_sessions\` / \`search\` / \`Read\` / \`Glob\` / \`Grep\` \u2014 situational awareness
|
|
13357
|
+
8. \`list_sessions\` / \`search\` / \`search_code_context\` / \`Read\` / \`Glob\` / \`Grep\` \u2014 situational awareness (\`search_code_context\` before grep for codebase discovery or implementation-answer questions; read exact cited files before claiming behavior)
|
|
13349
13358
|
9. \`get_task_memory\` / \`update_task_memory\` \u2014 compatibility memory tools for older task summaries and session outcomes
|
|
13350
13359
|
10. \`create_plan\` \u2014 document implementation plans and durable specs as artifacts
|
|
13351
13360
|
11. \`add_task_comment\` \u2014 human-facing collaboration: answer questions, post explicit status updates, escalate blockers, or leave comments the user asked you to post. Do not use comments as running notes or memory.
|
|
@@ -13390,7 +13399,7 @@ You are coordination-first, not read-only. Use judgment: act directly for focuse
|
|
|
13390
13399
|
${TASK_CREATE_FIND_BEFORE_CREATE_INSTRUCTIONS}`;
|
|
13391
13400
|
var PM_PROMPT = `## You are the PM Agent
|
|
13392
13401
|
|
|
13393
|
-
You are the **PM Agent** (Product Manager) for this task. When identifying yourself, say "I'm the PM Agent (an
|
|
13402
|
+
You are the **PM Agent** (Product Manager) for this task. When identifying yourself, say "I'm the PM Agent (an Alan agent profile)."
|
|
13394
13403
|
|
|
13395
13404
|
Your job is product thinking, not implementation. You own requirements, scope, and prioritization. You do not write code or edit files.
|
|
13396
13405
|
|
|
@@ -13448,11 +13457,11 @@ Every \`start_task_session\` message must include all four of these:
|
|
|
13448
13457
|
5. Track results and update task status as work completes.`;
|
|
13449
13458
|
var GENERAL_PROMPT = `## You are the General Agent
|
|
13450
13459
|
|
|
13451
|
-
You are a flexible general-purpose agent (an
|
|
13460
|
+
You are a flexible general-purpose agent (an Alan agent profile) for work that does not need a specialist profile. Wear whatever hat the moment demands \u2014 product thinker, planner, programmer, debugger, tester. Read the room and pick the hat.
|
|
13452
13461
|
|
|
13453
|
-
Your name is
|
|
13462
|
+
Your name is Alan, General Agent.
|
|
13454
13463
|
|
|
13455
|
-
Clarify the goal only when required, inspect the relevant context first, then complete the requested work directly. Keep changes focused, avoid unrelated refactors, and validate the result before reporting back.
|
|
13464
|
+
Clarify the goal only when required, inspect the relevant context first, then complete the requested work directly. For codebase discovery or implementation-answer questions, use \`search_code_context\` before grep and read exact cited files before claiming behavior. Keep changes focused, avoid unrelated refactors, and validate the result before reporting back.
|
|
13456
13465
|
|
|
13457
13466
|
Non-trivial implementations include tests. Break and plan before building when scope is wide.
|
|
13458
13467
|
|
|
@@ -13468,17 +13477,25 @@ ${TASK_CREATE_FIND_BEFORE_CREATE_INSTRUCTIONS}
|
|
|
13468
13477
|
- Any residual risk or open question`;
|
|
13469
13478
|
var BUDDY_PROMPT = `## You are Buddy
|
|
13470
13479
|
|
|
13471
|
-
You are **Buddy** (an
|
|
13480
|
+
You are **Buddy** (an Alan agent profile) for direct team chat. When identifying yourself, say "I'm Buddy, the direct chat agent in Alan."
|
|
13472
13481
|
|
|
13473
13482
|
Your job is to be the user's always-available teammate: answer questions, inspect context, help shape ambiguous ideas, and take focused implementation or coordination action when the user asks.
|
|
13474
13483
|
|
|
13475
13484
|
## How to work
|
|
13476
13485
|
|
|
13477
13486
|
1. Start from the current team/chat context before asking for details.
|
|
13478
|
-
2.
|
|
13479
|
-
3.
|
|
13480
|
-
4.
|
|
13481
|
-
5. For
|
|
13487
|
+
2. For codebase location, behavior, or implementation-answer questions, call \`search_code_context\` via Alan MCP before grep or filesystem search, then read the exact cited files before claiming behavior.
|
|
13488
|
+
3. Clarify only when the request has multiple plausible meanings.
|
|
13489
|
+
4. Keep responses concise, concrete, and tied to the user's next step.
|
|
13490
|
+
5. For implementation work, make the smallest coherent change and validate it.
|
|
13491
|
+
6. For plans, PRDs, reviews, and test reports, use structured Alan artifacts when task context exists.
|
|
13492
|
+
|
|
13493
|
+
## Primary tools (code discovery)
|
|
13494
|
+
|
|
13495
|
+
- \`mcp__alan__search_code_context\` / \`search_code_context\` \u2014 find where code lives or how behavior is implemented (**use this first**)
|
|
13496
|
+
- \`mcp__alan__code_context_status\` \u2014 confirm the team index is ready
|
|
13497
|
+
- \`Read\`, \`Grep\` \u2014 only after you have paths from the index, or for line-level edits in known files; read exact cited files before answering exact questions
|
|
13498
|
+
- Do **not** run repo-wide \`grep\` / \`rg\` / Bash find for "where is X" or "how does this work" questions when MCP code-context tools are available
|
|
13482
13499
|
|
|
13483
13500
|
## Rules
|
|
13484
13501
|
|
|
@@ -13490,7 +13507,7 @@ Your job is to be the user's always-available teammate: answer questions, inspec
|
|
|
13490
13507
|
${TASK_CREATE_FIND_BEFORE_CREATE_INSTRUCTIONS}`;
|
|
13491
13508
|
var REVIEW_PROMPT = `## You are the Review Agent
|
|
13492
13509
|
|
|
13493
|
-
You are the **Review Agent** (an
|
|
13510
|
+
You are the **Review Agent** (an Alan agent profile) for pull request review sessions. When identifying yourself, say "I'm the Review Agent."
|
|
13494
13511
|
|
|
13495
13512
|
Your job is to inspect the pull request context, reason about correctness and risk, and help the user review the change. Prefer concrete findings tied to files, lines, tests, and behavior. Do not send a review verdict until you have enough evidence.
|
|
13496
13513
|
|
|
@@ -13511,7 +13528,7 @@ Your job is to inspect the pull request context, reason about correctness and ri
|
|
|
13511
13528
|
- Ask one targeted question only when evidence is missing and blocks a useful review.`;
|
|
13512
13529
|
var IMPLEMENTOR_PROMPT = `## You are the Implementor Agent
|
|
13513
13530
|
|
|
13514
|
-
You are a senior implementation agent responsible for end-to-end code delivery. When identifying yourself, say "I'm the Implementor Agent (an
|
|
13531
|
+
You are a senior implementation agent responsible for end-to-end code delivery. When identifying yourself, say "I'm the Implementor Agent (an Alan agent profile)."
|
|
13515
13532
|
|
|
13516
13533
|
Workflow: Listen \u2192 Understand \u2192 Break down \u2192 Plan \u2192 Build \u2192 Test \u2192 Ship.
|
|
13517
13534
|
|
|
@@ -13524,7 +13541,8 @@ Workflow: Listen \u2192 Understand \u2192 Break down \u2192 Plan \u2192 Build \u
|
|
|
13524
13541
|
|
|
13525
13542
|
## Primary tools
|
|
13526
13543
|
|
|
13527
|
-
- \`
|
|
13544
|
+
- \`mcp__alan__search_code_context\` / \`search_code_context\` \u2014 find where code lives or how behavior is implemented (use BEFORE grep for discovery and implementation-answer questions)
|
|
13545
|
+
- \`Read\`, \`Glob\`, \`Grep\` \u2014 read exact cited files or narrow search after you have paths from the index
|
|
13528
13546
|
- \`Edit\`, \`Write\` \u2014 make changes; prefer \`Edit\` over \`Write\` for existing files
|
|
13529
13547
|
- \`Bash\` \u2014 run type-checks, tests, and read-only commands; never skip validation
|
|
13530
13548
|
- \`get_session_context\` \u2014 check what prior sessions already did before redoing work
|
|
@@ -13544,7 +13562,7 @@ Workflow: Listen \u2192 Understand \u2192 Break down \u2192 Plan \u2192 Build \u
|
|
|
13544
13562
|
- **Residual risk**: anything that could break or needs follow-up`;
|
|
13545
13563
|
var DESIGNER_PROMPT = `## You are the Designer Agent
|
|
13546
13564
|
|
|
13547
|
-
You are a product UI designer and frontend interaction specialist. When identifying yourself, say "I'm the Designer Agent (an
|
|
13565
|
+
You are a product UI designer and frontend interaction specialist. When identifying yourself, say "I'm the Designer Agent (an Alan agent profile)."
|
|
13548
13566
|
|
|
13549
13567
|
## Taste Skill (premium frontends)
|
|
13550
13568
|
|
|
@@ -13778,9 +13796,9 @@ function isBrowserRecordingToolName(toolName) {
|
|
|
13778
13796
|
}
|
|
13779
13797
|
|
|
13780
13798
|
// ../shared/dist/agent/mcp-config.js
|
|
13781
|
-
function
|
|
13799
|
+
function buildAlanMcpServers(registration) {
|
|
13782
13800
|
return {
|
|
13783
|
-
|
|
13801
|
+
alan: {
|
|
13784
13802
|
type: "http",
|
|
13785
13803
|
url: registration.url,
|
|
13786
13804
|
...Object.keys(registration.headers).length > 0 ? { headers: registration.headers } : {}
|
|
@@ -13895,7 +13913,7 @@ function toCodexMcpToml(src) {
|
|
|
13895
13913
|
return `${lines.join("\n").trim()}
|
|
13896
13914
|
`;
|
|
13897
13915
|
}
|
|
13898
|
-
function
|
|
13916
|
+
function renderAlanMcpConfigFiles(mcpServers, home) {
|
|
13899
13917
|
return [
|
|
13900
13918
|
{
|
|
13901
13919
|
path: `${home}/.claude.json`,
|
|
@@ -13922,8 +13940,8 @@ function renderAidenMcpConfigFiles(mcpServers, home) {
|
|
|
13922
13940
|
}
|
|
13923
13941
|
];
|
|
13924
13942
|
}
|
|
13925
|
-
function
|
|
13926
|
-
const all =
|
|
13943
|
+
function alanMcpConfigFilesForBackend(backendKind, mcpServers, home) {
|
|
13944
|
+
const all = renderAlanMcpConfigFiles(mcpServers, home);
|
|
13927
13945
|
const byPathSuffix = (...suffixes) => all.filter((f) => suffixes.some((s) => f.path.endsWith(s)));
|
|
13928
13946
|
switch (backendKind) {
|
|
13929
13947
|
case "claude_cli":
|
|
@@ -13948,15 +13966,15 @@ function aidenMcpConfigFilesForBackend(backendKind, mcpServers, home) {
|
|
|
13948
13966
|
var SESSION_RESUME_FAILED_MESSAGE = "Session resume failed. The previous session may have expired or is no longer available. Please start a new session.";
|
|
13949
13967
|
|
|
13950
13968
|
// ../shared/dist/agent/system-prompt.js
|
|
13951
|
-
var
|
|
13969
|
+
var ALAN_IDENTITY_PROMPT = `You are Alan, the software factory agent built by Supatest in Bangalore. Alan ships software end to end \u2014 plan, write, review, test, deploy.
|
|
13952
13970
|
|
|
13953
|
-
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
|
|
13971
|
+
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).
|
|
13954
13972
|
|
|
13955
13973
|
## Voice
|
|
13956
13974
|
|
|
13957
13975
|
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.
|
|
13958
13976
|
|
|
13959
|
-
## How
|
|
13977
|
+
## How Alan is structured
|
|
13960
13978
|
|
|
13961
13979
|
\`\`\`
|
|
13962
13980
|
Team
|
|
@@ -13974,7 +13992,7 @@ Team
|
|
|
13974
13992
|
|
|
13975
13993
|
## Structure over chat
|
|
13976
13994
|
|
|
13977
|
-
Deliverables persist as
|
|
13995
|
+
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.
|
|
13978
13996
|
|
|
13979
13997
|
## Universal teammate principles
|
|
13980
13998
|
|
|
@@ -13990,13 +14008,13 @@ Deliverables persist as Aiden artifacts and documents \u2014 not as chat. Plans,
|
|
|
13990
14008
|
For non-trivial tasks, log high-signal events as they happen: decisions, changed assumptions, validation results, known-good IDs, blockers, risks, and direction changes. Keep one current task-state summary updated with current state, known-good IDs, open risks, and next action. Do not log routine commands or file reads. Before stopping, ensure the timeline and current state are enough for a human to understand progress and for another agent to resume. If a prior decision is stale, write a newer event that supersedes it.
|
|
13991
14009
|
|
|
13992
14010
|
The specific work you do \u2014 implementation, planning, review, coordination, verification \u2014 is determined by your agent role, described next.`;
|
|
13993
|
-
var
|
|
14011
|
+
var ALAN_ASK_USER_PROMPT = `## Asking the user
|
|
13994
14012
|
|
|
13995
|
-
When you need a decision or information only the user can provide, do not guess \u2014 ask with an interactive ask tool so
|
|
14013
|
+
When you need a decision or information only the user can provide, do not guess \u2014 ask with an interactive ask tool so Alan can render clickable question cards.
|
|
13996
14014
|
|
|
13997
14015
|
### Tool preference (required)
|
|
13998
14016
|
1. Prefer the **native** ask tool when it appears in your tool list (\`AskUserQuestion\`, or the backend's equivalent built-in ask tool).
|
|
13999
|
-
2. Only if native ask is missing, call \`
|
|
14017
|
+
2. Only if native ask is missing, call \`mcp__alan__ask_user_question\` (server tool name: \`ask_user_question\`).
|
|
14000
14018
|
|
|
14001
14019
|
Required arguments (both native and MCP forms):
|
|
14002
14020
|
- \`questions\`: non-empty array. Each item needs a clear \`question\` string.
|
|
@@ -14015,8 +14033,49 @@ If neither native ask nor the MCP ask tool is available, ask ONE short plain-tex
|
|
|
14015
14033
|
|
|
14016
14034
|
### After the tool call
|
|
14017
14035
|
The ask tool returns an empty/ack result and does NOT wait. Immediately end your turn. At most one short line like "Please pick an option above." \u2014 no restating the full questions in markdown. The user's answer (or skip) arrives as the next user message; continue from there.`;
|
|
14036
|
+
function buildAlanTeamScopePrompt(teamId) {
|
|
14037
|
+
return [
|
|
14038
|
+
`You are operating within team \`${teamId}\`.`,
|
|
14039
|
+
"Use this teamId for MCP tools that accept it (search, list_tasks, search_code_context, etc.).",
|
|
14040
|
+
"On code-context tools, teamId auto-resolves from the Alan session when omitted \u2014 never invent or guess a teamId.",
|
|
14041
|
+
"Do not call list_teams."
|
|
14042
|
+
].join(" ");
|
|
14043
|
+
}
|
|
14044
|
+
var ALAN_CODE_CONTEXT_PROMPT = `## Codebase index (required for code discovery)
|
|
14045
|
+
|
|
14046
|
+
This team has an indexed codebase. For questions about where code lives, how something works, how to answer from the codebase, or which files to read, use Alan MCP code-context tools BEFORE local grep/find/bash search.
|
|
14047
|
+
|
|
14048
|
+
### Required path for codebase questions
|
|
14049
|
+
Examples: "where is X configured", "which file handles Y", "how does Z work", "find code that \u2026", "why does this behavior happen?"
|
|
14050
|
+
1. Call \`mcp__alan__search_code_context\` (MCP tool: \`search_code_context\`) with hybrid mode and topK 5.
|
|
14051
|
+
2. Optionally call \`mcp__alan__code_context_status\` first when unsure indexing is ready.
|
|
14052
|
+
3. Use the result citations to choose exact paths, then \`Read\` those files before making claims, proposing edits, or changing code.
|
|
14053
|
+
4. For exact symbols, constants, routes, schema fields, or line-level behavior, verify in the cited file with \`Read\` or narrow \`Grep\` before answering.
|
|
14054
|
+
|
|
14055
|
+
### Do NOT do this first
|
|
14056
|
+
- 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.
|
|
14057
|
+
- Do NOT use graphify or local graph tools for semantic "where is this implemented" questions \u2014 use \`search_code_context\`.
|
|
14058
|
+
- \`Read\` / \`Grep\` / \`Glob\` are for after you have specific paths from the index, or for line-level work in files already identified.
|
|
14059
|
+
|
|
14060
|
+
### Follow-ups
|
|
14061
|
+
- Import/caller structure: \`code_graph_query\` after search when available.
|
|
14062
|
+
- Current breakage: \`code_lsp_diagnostics\` when available.
|
|
14063
|
+
|
|
14064
|
+
If search returns no strong matches, say so and try one targeted local search in the most likely package \u2014 not repo-wide grep.`;
|
|
14065
|
+
function buildAlanTeamCodeContextOverlay(teamId) {
|
|
14066
|
+
return [buildAlanTeamScopePrompt(teamId), ALAN_CODE_CONTEXT_PROMPT].join("\n\n");
|
|
14067
|
+
}
|
|
14068
|
+
function buildCodeDiscoveryEnforcementTail() {
|
|
14069
|
+
return [
|
|
14070
|
+
"## MANDATORY \u2014 team code index (do this before Grep/graphify)",
|
|
14071
|
+
"1. For codebase discovery or implementation-answer questions, call `mcp__alan__search_code_context` (hybrid, topK 5). Omit teamId \u2014 Alan session headers resolve it.",
|
|
14072
|
+
"2. Optionally `mcp__alan__code_context_status` if indexing readiness is unclear.",
|
|
14073
|
+
"3. Read the exact cited files before answering, claiming behavior, or editing code; use narrow `Grep` only inside known paths.",
|
|
14074
|
+
"Repo-wide grep, graphify, or Bash find **before** MCP on discovery/implementation-answer questions fails the task."
|
|
14075
|
+
].join("\n");
|
|
14076
|
+
}
|
|
14018
14077
|
var PLAN_MODE_PROMPT = [
|
|
14019
|
-
"You are in
|
|
14078
|
+
"You are in Alan plan mode.",
|
|
14020
14079
|
"Analyze the task, inspect the relevant code and context, and produce a concrete implementation plan.",
|
|
14021
14080
|
"Do not make code changes or execute write operations in plan mode.",
|
|
14022
14081
|
"Write the plan in natural markdown. Use whatever structure best communicates the work clearly.",
|
|
@@ -14033,7 +14092,7 @@ var TEST_MODE_PROMPT = [
|
|
|
14033
14092
|
].join("\n");
|
|
14034
14093
|
var ASK_MODE_PROMPT = "You are in ask mode. Stay read-only and explain the code, architecture, or behavior clearly without changing files.";
|
|
14035
14094
|
var REVIEW_MODE_PROMPT = "You are in review mode. Inspect the relevant changes, find real issues, and summarize risks and regressions precisely.";
|
|
14036
|
-
function
|
|
14095
|
+
function getAlanModePrompt(mode) {
|
|
14037
14096
|
switch (mode) {
|
|
14038
14097
|
case "plan":
|
|
14039
14098
|
return PLAN_MODE_PROMPT;
|
|
@@ -14047,11 +14106,12 @@ function getAidenModePrompt(mode) {
|
|
|
14047
14106
|
return void 0;
|
|
14048
14107
|
}
|
|
14049
14108
|
}
|
|
14050
|
-
function
|
|
14109
|
+
function getAlanAgentPrompt(agentId) {
|
|
14051
14110
|
return getAgentSystemPrompt(agentId);
|
|
14052
14111
|
}
|
|
14053
14112
|
|
|
14054
14113
|
// ../shared/dist/types/frontend.js
|
|
14114
|
+
var EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
|
|
14055
14115
|
var CLAUDE_48_EFFORT_LEVELS = [
|
|
14056
14116
|
"minimal",
|
|
14057
14117
|
"low",
|
|
@@ -14338,9 +14398,18 @@ var CLAUDE_5_EFFORT_LEVELS2 = [
|
|
|
14338
14398
|
var CODEX_5X_EFFORT_LEVELS2 = ["low", "medium", "high", "xhigh"];
|
|
14339
14399
|
var GPT_5_5_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
14340
14400
|
var GPT_5_4_EFFORT_LEVELS = ["low", "medium", "high"];
|
|
14401
|
+
var GPT_5_6_EFFORT_LEVELS = [
|
|
14402
|
+
"low",
|
|
14403
|
+
"medium",
|
|
14404
|
+
"high",
|
|
14405
|
+
"xhigh",
|
|
14406
|
+
"max",
|
|
14407
|
+
"ultra"
|
|
14408
|
+
];
|
|
14341
14409
|
var CURSOR_EFFORT_SUFFIXES = [
|
|
14342
14410
|
{ suffix: "-extra-high", level: "xhigh" },
|
|
14343
14411
|
{ suffix: "-xhigh", level: "xhigh" },
|
|
14412
|
+
{ suffix: "-ultra", level: "ultra" },
|
|
14344
14413
|
{ suffix: "-max", level: "max" },
|
|
14345
14414
|
{ suffix: "-high", level: "high" },
|
|
14346
14415
|
{ suffix: "-medium", level: "medium" },
|
|
@@ -14436,7 +14505,11 @@ function mergeEffortLevels(existing, discovered) {
|
|
|
14436
14505
|
const merged = new Set(existing ?? []);
|
|
14437
14506
|
for (const level of discovered)
|
|
14438
14507
|
merged.add(level);
|
|
14439
|
-
return
|
|
14508
|
+
return orderEffortLevels(merged);
|
|
14509
|
+
}
|
|
14510
|
+
function orderEffortLevels(levels) {
|
|
14511
|
+
const set = levels instanceof Set ? levels : new Set(levels);
|
|
14512
|
+
return EFFORT_LEVELS.filter((level) => set.has(level));
|
|
14440
14513
|
}
|
|
14441
14514
|
function lookupKnownModelCapabilities(modelId) {
|
|
14442
14515
|
for (const catalog of [AVAILABLE_MODELS, CURSOR_AGENT_MODELS]) {
|
|
@@ -14460,6 +14533,13 @@ function inferSlugModelCapabilities(providerKind, modelId) {
|
|
|
14460
14533
|
if (id === "gpt-5.4" || id === "gpt-5.4-mini") {
|
|
14461
14534
|
return { contextWindows: [], effortLevels: GPT_5_4_EFFORT_LEVELS };
|
|
14462
14535
|
}
|
|
14536
|
+
if (/^gpt-5\.6-/.test(id)) {
|
|
14537
|
+
const hasUltra = id.includes("-sol") || id.includes("-terra");
|
|
14538
|
+
return {
|
|
14539
|
+
contextWindows: [],
|
|
14540
|
+
effortLevels: hasUltra ? GPT_5_6_EFFORT_LEVELS : GPT_5_6_EFFORT_LEVELS.filter((level) => level !== "ultra")
|
|
14541
|
+
};
|
|
14542
|
+
}
|
|
14463
14543
|
if (id.includes("codex") || /^gpt-5\.[0-3]/.test(id)) {
|
|
14464
14544
|
return { contextWindows: [], effortLevels: CODEX_5X_EFFORT_LEVELS2 };
|
|
14465
14545
|
}
|
|
@@ -14618,6 +14698,7 @@ function baseCatalogForProvider(providerKind) {
|
|
|
14618
14698
|
}
|
|
14619
14699
|
return staticCatalog;
|
|
14620
14700
|
}
|
|
14701
|
+
var PROVIDER_MODEL_INFERENCE_IDS = Object.keys(PROVIDER_MODELS).filter((providerId) => providerId !== "antigravity_cli");
|
|
14621
14702
|
function catalogModelsForProvider(providerKind, modelsFromConfig) {
|
|
14622
14703
|
const catalog = baseCatalogForProvider(providerKind);
|
|
14623
14704
|
if (!modelsFromConfig)
|
|
@@ -14630,13 +14711,18 @@ function catalogModelsForProvider(providerKind, modelsFromConfig) {
|
|
|
14630
14711
|
}
|
|
14631
14712
|
|
|
14632
14713
|
// ../agent-core/dist/index.js
|
|
14633
|
-
var import_child_process = require("child_process");
|
|
14634
14714
|
var import_fs2 = require("fs");
|
|
14635
14715
|
var import_os2 = require("os");
|
|
14636
14716
|
var import_path2 = require("path");
|
|
14717
|
+
var import_child_process = require("child_process");
|
|
14718
|
+
var import_fs3 = require("fs");
|
|
14719
|
+
var import_os3 = require("os");
|
|
14720
|
+
var import_path3 = require("path");
|
|
14637
14721
|
var import_readline = require("readline");
|
|
14638
14722
|
var import_child_process2 = require("child_process");
|
|
14639
14723
|
var import_crypto = require("crypto");
|
|
14724
|
+
var import_child_process3 = require("child_process");
|
|
14725
|
+
var import_util10 = require("util");
|
|
14640
14726
|
|
|
14641
14727
|
// ../shared/dist/constants/agent.js
|
|
14642
14728
|
var INTERACTIVE_TOOL_NAMES = /* @__PURE__ */ new Set(["askuserquestion", "exitplanmode", "enterplanmode"]);
|
|
@@ -14645,8 +14731,11 @@ function normalizeToolName2(name) {
|
|
|
14645
14731
|
const mcp = lower.match(/^mcp__[a-z0-9-]+__(.+)$/);
|
|
14646
14732
|
let bare = mcp ? mcp[1] : lower;
|
|
14647
14733
|
bare = bare.replace(/[_\s-]/g, "");
|
|
14648
|
-
|
|
14649
|
-
bare
|
|
14734
|
+
for (const vendor of ["alan", "alan"]) {
|
|
14735
|
+
if (bare.startsWith(vendor) && bare.length > vendor.length) {
|
|
14736
|
+
bare = bare.slice(vendor.length);
|
|
14737
|
+
break;
|
|
14738
|
+
}
|
|
14650
14739
|
}
|
|
14651
14740
|
return bare;
|
|
14652
14741
|
}
|
|
@@ -14685,8 +14774,8 @@ function isInteractiveToolName(name, input) {
|
|
|
14685
14774
|
return true;
|
|
14686
14775
|
return isAskUserQuestionTool(name, input);
|
|
14687
14776
|
}
|
|
14688
|
-
var
|
|
14689
|
-
var
|
|
14777
|
+
var ALAN_AGENT_WORKOS_USER_ID_PREFIX = "alan-agent:";
|
|
14778
|
+
var ALAN_AGENT_WORKOS_USER_ID_LIKE_PATTERN = `${ALAN_AGENT_WORKOS_USER_ID_PREFIX}%`;
|
|
14690
14779
|
|
|
14691
14780
|
// ../shared/dist/constants/colors.js
|
|
14692
14781
|
var CATEGORICAL_COLORS = {
|
|
@@ -14925,18 +15014,289 @@ var PRIORITY_OPTIONS = [
|
|
|
14925
15014
|
];
|
|
14926
15015
|
|
|
14927
15016
|
// ../agent-core/dist/index.js
|
|
14928
|
-
var import_fs3 = require("fs");
|
|
14929
|
-
var import_os3 = require("os");
|
|
14930
|
-
var import_path3 = require("path");
|
|
14931
15017
|
var import_fs4 = require("fs");
|
|
14932
15018
|
var import_os4 = require("os");
|
|
14933
15019
|
var import_path4 = require("path");
|
|
14934
15020
|
var import_fs5 = require("fs");
|
|
14935
15021
|
var import_os5 = require("os");
|
|
14936
15022
|
var import_path5 = require("path");
|
|
14937
|
-
var import_crypto2 = require("crypto");
|
|
14938
15023
|
var import_fs6 = require("fs");
|
|
15024
|
+
var import_os6 = require("os");
|
|
14939
15025
|
var import_path6 = require("path");
|
|
15026
|
+
var import_crypto2 = require("crypto");
|
|
15027
|
+
var import_fs7 = require("fs");
|
|
15028
|
+
var import_path7 = require("path");
|
|
15029
|
+
var MANAGED_ALAN_MCP_SERVER_NAMES = [
|
|
15030
|
+
"alan",
|
|
15031
|
+
"alan-prod",
|
|
15032
|
+
"alan-staging",
|
|
15033
|
+
"alan-local"
|
|
15034
|
+
];
|
|
15035
|
+
function parseJsonSafe(content) {
|
|
15036
|
+
if (!content) return {};
|
|
15037
|
+
try {
|
|
15038
|
+
return JSON.parse(content);
|
|
15039
|
+
} catch {
|
|
15040
|
+
return {};
|
|
15041
|
+
}
|
|
15042
|
+
}
|
|
15043
|
+
function readFileOrNull(path) {
|
|
15044
|
+
try {
|
|
15045
|
+
if ((0, import_fs2.existsSync)(path)) return (0, import_fs2.readFileSync)(path, "utf-8");
|
|
15046
|
+
} catch {
|
|
15047
|
+
}
|
|
15048
|
+
return null;
|
|
15049
|
+
}
|
|
15050
|
+
function writeFileSafe(path, content) {
|
|
15051
|
+
(0, import_fs2.mkdirSync)((0, import_path2.dirname)(path), { recursive: true });
|
|
15052
|
+
(0, import_fs2.writeFileSync)(path, content, "utf-8");
|
|
15053
|
+
}
|
|
15054
|
+
function formatTomlKey2(key) {
|
|
15055
|
+
return /^[A-Za-z0-9_-]+$/.test(key) ? key : JSON.stringify(key);
|
|
15056
|
+
}
|
|
15057
|
+
function parseJsonStringOrNull(raw) {
|
|
15058
|
+
try {
|
|
15059
|
+
const value2 = JSON.parse(raw);
|
|
15060
|
+
return typeof value2 === "string" ? value2 : null;
|
|
15061
|
+
} catch {
|
|
15062
|
+
return null;
|
|
15063
|
+
}
|
|
15064
|
+
}
|
|
15065
|
+
function splitTomlInlineEntries(body) {
|
|
15066
|
+
const entries = [];
|
|
15067
|
+
let current = "";
|
|
15068
|
+
let inString = false;
|
|
15069
|
+
let escaping = false;
|
|
15070
|
+
for (const char of body) {
|
|
15071
|
+
if (escaping) {
|
|
15072
|
+
current += char;
|
|
15073
|
+
escaping = false;
|
|
15074
|
+
continue;
|
|
15075
|
+
}
|
|
15076
|
+
if (char === "\\") {
|
|
15077
|
+
current += char;
|
|
15078
|
+
escaping = inString;
|
|
15079
|
+
continue;
|
|
15080
|
+
}
|
|
15081
|
+
if (char === '"') inString = !inString;
|
|
15082
|
+
if (char === "," && !inString) {
|
|
15083
|
+
if (current.trim()) entries.push(current.trim());
|
|
15084
|
+
current = "";
|
|
15085
|
+
continue;
|
|
15086
|
+
}
|
|
15087
|
+
current += char;
|
|
15088
|
+
}
|
|
15089
|
+
if (current.trim()) entries.push(current.trim());
|
|
15090
|
+
return entries;
|
|
15091
|
+
}
|
|
15092
|
+
function parseTomlInlineStringTable(raw) {
|
|
15093
|
+
const trimmed = raw.trim();
|
|
15094
|
+
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) return {};
|
|
15095
|
+
const body = trimmed.slice(1, -1).trim();
|
|
15096
|
+
if (!body) return {};
|
|
15097
|
+
const headers = {};
|
|
15098
|
+
for (const entry of splitTomlInlineEntries(body)) {
|
|
15099
|
+
const equalsIndex = entry.indexOf("=");
|
|
15100
|
+
if (equalsIndex < 0) continue;
|
|
15101
|
+
const rawKey = entry.slice(0, equalsIndex).trim();
|
|
15102
|
+
const rawValue = entry.slice(equalsIndex + 1).trim();
|
|
15103
|
+
const key = rawKey.startsWith('"') ? parseJsonStringOrNull(rawKey) : rawKey;
|
|
15104
|
+
const value2 = rawValue.startsWith('"') ? parseJsonStringOrNull(rawValue) : rawValue;
|
|
15105
|
+
if (key && value2 !== null) headers[key] = value2;
|
|
15106
|
+
}
|
|
15107
|
+
return headers;
|
|
15108
|
+
}
|
|
15109
|
+
function buildTomlInlineStringTable(record) {
|
|
15110
|
+
return `{ ${Object.entries(record).map(([key, value2]) => `${formatTomlKey2(key)} = ${JSON.stringify(value2)}`).join(", ")} }`;
|
|
15111
|
+
}
|
|
15112
|
+
function findManagedAlanServer(servers) {
|
|
15113
|
+
for (const name of MANAGED_ALAN_MCP_SERVER_NAMES) {
|
|
15114
|
+
const server = servers[name];
|
|
15115
|
+
if (server && typeof server === "object") {
|
|
15116
|
+
return { name, server };
|
|
15117
|
+
}
|
|
15118
|
+
}
|
|
15119
|
+
return null;
|
|
15120
|
+
}
|
|
15121
|
+
function buildSessionHeaders(input) {
|
|
15122
|
+
const headers = {
|
|
15123
|
+
"x-conversation-id": input.conversationId
|
|
15124
|
+
};
|
|
15125
|
+
if (input.teamId) headers["x-team-id"] = input.teamId;
|
|
15126
|
+
if (input.allowedTools?.length) {
|
|
15127
|
+
headers["x-allowed-tools"] = input.allowedTools.join(",");
|
|
15128
|
+
}
|
|
15129
|
+
return headers;
|
|
15130
|
+
}
|
|
15131
|
+
function getTomlSectionName(line) {
|
|
15132
|
+
const match = line.match(/^\s*\[mcp_servers\.(?:"([^"]+)"|([A-Za-z0-9_-]+))\]\s*$/);
|
|
15133
|
+
return match?.[1] ?? match?.[2] ?? null;
|
|
15134
|
+
}
|
|
15135
|
+
function patchCodexTomlMcpConfig(content, sessionHeaders) {
|
|
15136
|
+
const newline = content.includes("\r\n") ? "\r\n" : "\n";
|
|
15137
|
+
const lines = content.split(/\r?\n/);
|
|
15138
|
+
const sectionStart = lines.findIndex((line) => {
|
|
15139
|
+
const name = getTomlSectionName(line);
|
|
15140
|
+
return Boolean(name && MANAGED_ALAN_MCP_SERVER_NAMES.includes(name));
|
|
15141
|
+
});
|
|
15142
|
+
if (sectionStart < 0) return null;
|
|
15143
|
+
let sectionEnd = lines.findIndex((line, index) => index > sectionStart && /^\s*\[/.test(line));
|
|
15144
|
+
if (sectionEnd < 0) sectionEnd = lines.length;
|
|
15145
|
+
const headerLineIndex = lines.findIndex(
|
|
15146
|
+
(line, index) => index > sectionStart && index < sectionEnd && /^\s*http_headers\s*=/.test(line)
|
|
15147
|
+
);
|
|
15148
|
+
if (headerLineIndex >= 0) {
|
|
15149
|
+
const rawInlineTable = lines[headerLineIndex]?.replace(/^\s*http_headers\s*=\s*/, "") ?? "";
|
|
15150
|
+
const currentHeaders = parseTomlInlineStringTable(rawInlineTable);
|
|
15151
|
+
lines[headerLineIndex] = `http_headers = ${buildTomlInlineStringTable({
|
|
15152
|
+
...currentHeaders,
|
|
15153
|
+
...sessionHeaders
|
|
15154
|
+
})}`;
|
|
15155
|
+
} else {
|
|
15156
|
+
const insertAfterUrl = lines.findIndex(
|
|
15157
|
+
(line, index) => index > sectionStart && index < sectionEnd && /^\s*url\s*=/.test(line)
|
|
15158
|
+
);
|
|
15159
|
+
lines.splice(
|
|
15160
|
+
insertAfterUrl >= 0 ? insertAfterUrl + 1 : sectionStart + 1,
|
|
15161
|
+
0,
|
|
15162
|
+
`http_headers = ${buildTomlInlineStringTable(sessionHeaders)}`
|
|
15163
|
+
);
|
|
15164
|
+
}
|
|
15165
|
+
const updated = lines.join(newline);
|
|
15166
|
+
return updated.endsWith(newline) ? updated : `${updated}${newline}`;
|
|
15167
|
+
}
|
|
15168
|
+
function patchCodexTomlMcpTarget(target, sessionHeaders) {
|
|
15169
|
+
if (!(0, import_fs2.existsSync)(target.guardDir)) return false;
|
|
15170
|
+
const existing = readFileOrNull(target.configPath);
|
|
15171
|
+
if (existing === null) return false;
|
|
15172
|
+
const updated = patchCodexTomlMcpConfig(existing, sessionHeaders);
|
|
15173
|
+
if (updated === null || updated === existing) return false;
|
|
15174
|
+
writeFileSafe(target.configPath, updated);
|
|
15175
|
+
return true;
|
|
15176
|
+
}
|
|
15177
|
+
function patchJsonMcpTarget(target, sessionHeaders) {
|
|
15178
|
+
if (!(0, import_fs2.existsSync)(target.guardDir)) return false;
|
|
15179
|
+
const existing = readFileOrNull(target.configPath);
|
|
15180
|
+
const config = parseJsonSafe(existing);
|
|
15181
|
+
const servers = target.readServers(config);
|
|
15182
|
+
const match = findManagedAlanServer(servers);
|
|
15183
|
+
if (!match) return false;
|
|
15184
|
+
const currentHeaders = target.readHeaders(match.server) ?? {};
|
|
15185
|
+
target.writeHeaders(match.server, { ...currentHeaders, ...sessionHeaders });
|
|
15186
|
+
servers[match.name] = match.server;
|
|
15187
|
+
target.writeServers(config, servers);
|
|
15188
|
+
writeFileSafe(target.configPath, `${JSON.stringify(config, null, 2)}
|
|
15189
|
+
`);
|
|
15190
|
+
return true;
|
|
15191
|
+
}
|
|
15192
|
+
function jsonMcpServersTarget(cli, home, configRelativePath, guardRelativePath) {
|
|
15193
|
+
const configPath = (0, import_path2.join)(home, ...configRelativePath);
|
|
15194
|
+
const guardDir = guardRelativePath.length > 0 ? (0, import_path2.join)(home, ...guardRelativePath) : home;
|
|
15195
|
+
return {
|
|
15196
|
+
cli,
|
|
15197
|
+
configPath,
|
|
15198
|
+
guardDir,
|
|
15199
|
+
readServers: (config) => config.mcpServers ?? {},
|
|
15200
|
+
writeServers: (config, servers) => {
|
|
15201
|
+
config.mcpServers = servers;
|
|
15202
|
+
},
|
|
15203
|
+
readHeaders: (server) => server.headers,
|
|
15204
|
+
writeHeaders: (server, headers) => {
|
|
15205
|
+
server.headers = headers;
|
|
15206
|
+
}
|
|
15207
|
+
};
|
|
15208
|
+
}
|
|
15209
|
+
function buildCodexTomlTargets(home) {
|
|
15210
|
+
return [
|
|
15211
|
+
{
|
|
15212
|
+
cli: "Codex",
|
|
15213
|
+
configPath: (0, import_path2.join)(home, ".codex", "config.toml"),
|
|
15214
|
+
guardDir: (0, import_path2.join)(home, ".codex")
|
|
15215
|
+
}
|
|
15216
|
+
];
|
|
15217
|
+
}
|
|
15218
|
+
function buildJsonTargets(home) {
|
|
15219
|
+
return [
|
|
15220
|
+
jsonMcpServersTarget("Cursor", home, [".cursor", "mcp.json"], [".cursor"]),
|
|
15221
|
+
jsonMcpServersTarget("Claude Code", home, [".claude.json"], []),
|
|
15222
|
+
jsonMcpServersTarget("Kimi CLI", home, [".kimi", "mcp.json"], [".kimi"]),
|
|
15223
|
+
jsonMcpServersTarget("Factory Droid", home, [".factory", "mcp.json"], [".factory"]),
|
|
15224
|
+
{
|
|
15225
|
+
cli: "OpenCode",
|
|
15226
|
+
configPath: (0, import_path2.join)(home, ".config", "opencode", "opencode.json"),
|
|
15227
|
+
guardDir: (0, import_path2.join)(home, ".config", "opencode"),
|
|
15228
|
+
readServers: (config) => config.mcp ?? {},
|
|
15229
|
+
writeServers: (config, servers) => {
|
|
15230
|
+
config.mcp = servers;
|
|
15231
|
+
},
|
|
15232
|
+
readHeaders: (server) => server.headers,
|
|
15233
|
+
writeHeaders: (server, headers) => {
|
|
15234
|
+
server.headers = headers;
|
|
15235
|
+
}
|
|
15236
|
+
},
|
|
15237
|
+
{
|
|
15238
|
+
cli: "Amp",
|
|
15239
|
+
configPath: (0, import_path2.join)(home, ".config", "amp", "settings.json"),
|
|
15240
|
+
guardDir: (0, import_path2.join)(home, ".config", "amp"),
|
|
15241
|
+
readServers: (config) => config["amp.mcpServers"] ?? {},
|
|
15242
|
+
writeServers: (config, servers) => {
|
|
15243
|
+
config["amp.mcpServers"] = servers;
|
|
15244
|
+
},
|
|
15245
|
+
readHeaders: (server) => server.headers,
|
|
15246
|
+
writeHeaders: (server, headers) => {
|
|
15247
|
+
server.headers = headers;
|
|
15248
|
+
}
|
|
15249
|
+
},
|
|
15250
|
+
{
|
|
15251
|
+
cli: "Antigravity",
|
|
15252
|
+
configPath: (0, import_path2.join)(home, ".gemini", "settings.json"),
|
|
15253
|
+
guardDir: (0, import_path2.join)(home, ".gemini"),
|
|
15254
|
+
readServers: (config) => config.mcpServers ?? {},
|
|
15255
|
+
writeServers: (config, servers) => {
|
|
15256
|
+
config.mcpServers = servers;
|
|
15257
|
+
},
|
|
15258
|
+
readHeaders: (server) => server.headers,
|
|
15259
|
+
writeHeaders: (server, headers) => {
|
|
15260
|
+
server.headers = headers;
|
|
15261
|
+
}
|
|
15262
|
+
},
|
|
15263
|
+
{
|
|
15264
|
+
cli: "Antigravity CLI",
|
|
15265
|
+
configPath: (0, import_path2.join)(home, ".gemini", "antigravity-cli", "settings.json"),
|
|
15266
|
+
guardDir: (0, import_path2.join)(home, ".gemini"),
|
|
15267
|
+
readServers: (config) => config.mcpServers ?? {},
|
|
15268
|
+
writeServers: (config, servers) => {
|
|
15269
|
+
config.mcpServers = servers;
|
|
15270
|
+
},
|
|
15271
|
+
readHeaders: (server) => server.headers,
|
|
15272
|
+
writeHeaders: (server, headers) => {
|
|
15273
|
+
server.headers = headers;
|
|
15274
|
+
}
|
|
15275
|
+
}
|
|
15276
|
+
];
|
|
15277
|
+
}
|
|
15278
|
+
function syncAlanMcpSessionHeaders(input) {
|
|
15279
|
+
const home = input.homeDir ?? (0, import_os2.homedir)();
|
|
15280
|
+
const sessionHeaders = buildSessionHeaders(input);
|
|
15281
|
+
const written = [];
|
|
15282
|
+
for (const target of buildCodexTomlTargets(home)) {
|
|
15283
|
+
try {
|
|
15284
|
+
if (patchCodexTomlMcpTarget(target, sessionHeaders)) {
|
|
15285
|
+
written.push(target.cli);
|
|
15286
|
+
}
|
|
15287
|
+
} catch {
|
|
15288
|
+
}
|
|
15289
|
+
}
|
|
15290
|
+
for (const target of buildJsonTargets(home)) {
|
|
15291
|
+
try {
|
|
15292
|
+
if (patchJsonMcpTarget(target, sessionHeaders)) {
|
|
15293
|
+
written.push(target.cli);
|
|
15294
|
+
}
|
|
15295
|
+
} catch {
|
|
15296
|
+
}
|
|
15297
|
+
}
|
|
15298
|
+
return written;
|
|
15299
|
+
}
|
|
14940
15300
|
function buildPromptWithSystem(config, promptText) {
|
|
14941
15301
|
const parts2 = [
|
|
14942
15302
|
config.systemPrompt?.trim(),
|
|
@@ -14961,16 +15321,16 @@ function imageFileExtension(mimeType) {
|
|
|
14961
15321
|
}
|
|
14962
15322
|
function createImageTempDirectory(cwd) {
|
|
14963
15323
|
const candidates = [
|
|
14964
|
-
cwd ? (0,
|
|
14965
|
-
(0,
|
|
15324
|
+
cwd ? (0, import_path3.join)(cwd, ".alan-images-") : null,
|
|
15325
|
+
(0, import_path3.join)((0, import_os3.tmpdir)(), "alan-images-")
|
|
14966
15326
|
].filter((value2) => Boolean(value2));
|
|
14967
15327
|
for (const candidate of candidates) {
|
|
14968
15328
|
try {
|
|
14969
|
-
return (0,
|
|
15329
|
+
return (0, import_fs3.mkdtempSync)(candidate);
|
|
14970
15330
|
} catch {
|
|
14971
15331
|
}
|
|
14972
15332
|
}
|
|
14973
|
-
return (0,
|
|
15333
|
+
return (0, import_fs3.mkdtempSync)((0, import_path3.join)((0, import_os3.tmpdir)(), "alan-images-"));
|
|
14974
15334
|
}
|
|
14975
15335
|
function safeFilename(filename) {
|
|
14976
15336
|
const safe = filename.replace(/[^a-zA-Z0-9._-]/g, "_").replace(/^_+/, "");
|
|
@@ -14984,8 +15344,8 @@ function writeImagesToTempFiles(images, cwd) {
|
|
|
14984
15344
|
const files = [];
|
|
14985
15345
|
for (const img of images) {
|
|
14986
15346
|
const ext = imageFileExtension(img.mimeType);
|
|
14987
|
-
const filePath = (0,
|
|
14988
|
-
(0,
|
|
15347
|
+
const filePath = (0, import_path3.join)(directory, `alan-img-${img.id}.${ext}`);
|
|
15348
|
+
(0, import_fs3.writeFileSync)(filePath, Buffer.from(img.data, "base64"));
|
|
14989
15349
|
paths.push(filePath);
|
|
14990
15350
|
files.push({
|
|
14991
15351
|
filename: img.filename,
|
|
@@ -15000,7 +15360,7 @@ function writeImagesToTempFiles(images, cwd) {
|
|
|
15000
15360
|
files,
|
|
15001
15361
|
cleanup: () => {
|
|
15002
15362
|
try {
|
|
15003
|
-
(0,
|
|
15363
|
+
(0, import_fs3.rmSync)(directory, { recursive: true, force: true });
|
|
15004
15364
|
} catch {
|
|
15005
15365
|
}
|
|
15006
15366
|
}
|
|
@@ -15026,8 +15386,8 @@ function writeFilesToTempFiles(files, cwd) {
|
|
|
15026
15386
|
}
|
|
15027
15387
|
for (const file of dataFiles) {
|
|
15028
15388
|
if (!directory || file.data === void 0) continue;
|
|
15029
|
-
const filePath = (0,
|
|
15030
|
-
(0,
|
|
15389
|
+
const filePath = (0, import_path3.join)(directory, `${file.id}-${safeFilename(file.filename)}`);
|
|
15390
|
+
(0, import_fs3.writeFileSync)(filePath, Buffer.from(file.data, "base64"));
|
|
15031
15391
|
paths.push(filePath);
|
|
15032
15392
|
tempFiles.push({
|
|
15033
15393
|
filename: file.filename,
|
|
@@ -15042,7 +15402,7 @@ function writeFilesToTempFiles(files, cwd) {
|
|
|
15042
15402
|
cleanup: () => {
|
|
15043
15403
|
if (!directory) return;
|
|
15044
15404
|
try {
|
|
15045
|
-
(0,
|
|
15405
|
+
(0, import_fs3.rmSync)(directory, { recursive: true, force: true });
|
|
15046
15406
|
} catch {
|
|
15047
15407
|
}
|
|
15048
15408
|
}
|
|
@@ -15964,6 +16324,30 @@ function createAntigravityCliBackend(command = "agy", defaultArgs = []) {
|
|
|
15964
16324
|
}
|
|
15965
16325
|
};
|
|
15966
16326
|
}
|
|
16327
|
+
var execFileAsync = (0, import_util10.promisify)(import_child_process3.execFile);
|
|
16328
|
+
var flagSupportCache = /* @__PURE__ */ new Map();
|
|
16329
|
+
function helpAdvertisesFlag(helpText, flag) {
|
|
16330
|
+
return helpText.includes(flag);
|
|
16331
|
+
}
|
|
16332
|
+
async function cliSupportsFlag(command, flag, env = {}) {
|
|
16333
|
+
const key = `${command}::${flag}`;
|
|
16334
|
+
const cached = flagSupportCache.get(key);
|
|
16335
|
+
if (cached !== void 0) return cached;
|
|
16336
|
+
let supported = false;
|
|
16337
|
+
try {
|
|
16338
|
+
const { stdout, stderr } = await execFileAsync(command, ["--help"], {
|
|
16339
|
+
env: { ...process.env, ...env },
|
|
16340
|
+
timeout: 5e3,
|
|
16341
|
+
maxBuffer: 4 * 1024 * 1024
|
|
16342
|
+
});
|
|
16343
|
+
supported = helpAdvertisesFlag(`${stdout ?? ""}${stderr ?? ""}`, flag);
|
|
16344
|
+
} catch (error) {
|
|
16345
|
+
const withOutput = error;
|
|
16346
|
+
supported = helpAdvertisesFlag(`${withOutput.stdout ?? ""}${withOutput.stderr ?? ""}`, flag);
|
|
16347
|
+
}
|
|
16348
|
+
flagSupportCache.set(key, supported);
|
|
16349
|
+
return supported;
|
|
16350
|
+
}
|
|
15967
16351
|
function stringField(record, key) {
|
|
15968
16352
|
const value2 = record[key];
|
|
15969
16353
|
return typeof value2 === "string" && value2.trim().length > 0 ? value2 : void 0;
|
|
@@ -16070,7 +16454,7 @@ function maybeFinalizeDeferredResult(kind, context, state) {
|
|
|
16070
16454
|
state.resultDeferredOnBackgroundWork = false;
|
|
16071
16455
|
finalizeStructuredResult(kind, state);
|
|
16072
16456
|
}
|
|
16073
|
-
var DEBUG_ASK = process.env.
|
|
16457
|
+
var DEBUG_ASK = process.env.ALAN_DEBUG_ASK === "1" || process.env.ALAN_DEBUG === "1";
|
|
16074
16458
|
function debugAskLog(message) {
|
|
16075
16459
|
if (DEBUG_ASK) console.info(message);
|
|
16076
16460
|
}
|
|
@@ -16333,13 +16717,13 @@ function createClaudeCliBackend(command = "claude", defaultArgs = []) {
|
|
|
16333
16717
|
args.push("--disallowedTools", disallowedTools.join(","));
|
|
16334
16718
|
}
|
|
16335
16719
|
const resumeId = context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim();
|
|
16336
|
-
if (!resumeId) {
|
|
16337
|
-
|
|
16338
|
-
|
|
16339
|
-
|
|
16340
|
-
|
|
16341
|
-
|
|
16342
|
-
|
|
16720
|
+
if (!resumeId && context.config.systemPrompt?.trim()) {
|
|
16721
|
+
args.push("--system-prompt", context.config.systemPrompt.trim());
|
|
16722
|
+
} else if (resumeId && context.config.systemPromptAppend?.trim()) {
|
|
16723
|
+
args.push("--append-system-prompt", context.config.systemPromptAppend.trim());
|
|
16724
|
+
}
|
|
16725
|
+
if (context.config.teamId?.trim() && await cliSupportsFlag(command, "--approve-mcps", context.env)) {
|
|
16726
|
+
args.push("--approve-mcps");
|
|
16343
16727
|
}
|
|
16344
16728
|
const baseModel = context.config.selectedModel?.trim();
|
|
16345
16729
|
if (baseModel) {
|
|
@@ -16424,26 +16808,26 @@ function normalizeCodexMcpToolResult(output) {
|
|
|
16424
16808
|
return raw;
|
|
16425
16809
|
}
|
|
16426
16810
|
function findCodexSessionLog(runtimeSessionId, codexHome) {
|
|
16427
|
-
if (!runtimeSessionId || !(0,
|
|
16428
|
-
const root = (0,
|
|
16429
|
-
if (!(0,
|
|
16811
|
+
if (!runtimeSessionId || !(0, import_fs4.existsSync)(codexHome)) return null;
|
|
16812
|
+
const root = (0, import_path4.join)(codexHome, "sessions");
|
|
16813
|
+
if (!(0, import_fs4.existsSync)(root)) return null;
|
|
16430
16814
|
const matches = [];
|
|
16431
16815
|
const stack = [root];
|
|
16432
16816
|
while (stack.length > 0) {
|
|
16433
16817
|
const dir = stack.pop();
|
|
16434
16818
|
let entries;
|
|
16435
16819
|
try {
|
|
16436
|
-
entries = (0,
|
|
16820
|
+
entries = (0, import_fs4.readdirSync)(dir, { withFileTypes: true });
|
|
16437
16821
|
} catch {
|
|
16438
16822
|
continue;
|
|
16439
16823
|
}
|
|
16440
16824
|
for (const entry of entries) {
|
|
16441
|
-
const path = (0,
|
|
16825
|
+
const path = (0, import_path4.join)(dir, entry.name);
|
|
16442
16826
|
if (entry.isDirectory()) {
|
|
16443
16827
|
stack.push(path);
|
|
16444
16828
|
} else if (entry.isFile() && entry.name.includes(runtimeSessionId) && entry.name.endsWith(".jsonl")) {
|
|
16445
16829
|
try {
|
|
16446
|
-
matches.push({ path, mtimeMs: (0,
|
|
16830
|
+
matches.push({ path, mtimeMs: (0, import_fs4.statSync)(path).mtimeMs });
|
|
16447
16831
|
} catch {
|
|
16448
16832
|
matches.push({ path, mtimeMs: 0 });
|
|
16449
16833
|
}
|
|
@@ -16476,10 +16860,10 @@ function latestUserMessageLineIndex(lines) {
|
|
|
16476
16860
|
async function replayCodexMcpToolEventsFromSessionLog(context, state) {
|
|
16477
16861
|
const runtimeSessionId = state.runtimeSessionId;
|
|
16478
16862
|
if (!runtimeSessionId) return;
|
|
16479
|
-
const codexHome = context.env.CODEX_HOME || process.env.CODEX_HOME || (0,
|
|
16863
|
+
const codexHome = context.env.CODEX_HOME || process.env.CODEX_HOME || (0, import_path4.join)((0, import_os4.homedir)(), ".codex");
|
|
16480
16864
|
const logPath = findCodexSessionLog(runtimeSessionId, codexHome);
|
|
16481
16865
|
if (!logPath) return;
|
|
16482
|
-
const allLines = (0,
|
|
16866
|
+
const allLines = (0, import_fs4.readFileSync)(logPath, "utf8").split(/\r?\n/).filter(Boolean);
|
|
16483
16867
|
const latestUserLineIndex = latestUserMessageLineIndex(allLines);
|
|
16484
16868
|
const lines = latestUserLineIndex >= 0 ? allLines.slice(latestUserLineIndex + 1) : allLines;
|
|
16485
16869
|
const emittedToolIds = /* @__PURE__ */ new Set();
|
|
@@ -17117,6 +17501,7 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
|
|
|
17117
17501
|
context.config.images,
|
|
17118
17502
|
context.cwd
|
|
17119
17503
|
);
|
|
17504
|
+
const approveMcps = context.config.teamId?.trim() ? await cliSupportsFlag(command, "--approve-mcps", context.env) : false;
|
|
17120
17505
|
try {
|
|
17121
17506
|
return await createGenericCliBackend({
|
|
17122
17507
|
kind: "cursor_agent_cli",
|
|
@@ -17129,10 +17514,12 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
|
|
|
17129
17514
|
"--output-format",
|
|
17130
17515
|
"stream-json",
|
|
17131
17516
|
"--trust",
|
|
17132
|
-
"--approve-mcps",
|
|
17133
17517
|
"--workspace",
|
|
17134
17518
|
ctx.cwd
|
|
17135
17519
|
];
|
|
17520
|
+
if (approveMcps) {
|
|
17521
|
+
args.push("--approve-mcps");
|
|
17522
|
+
}
|
|
17136
17523
|
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
17137
17524
|
if (shouldForceCursorAgent(ctx.config)) args.push("--force");
|
|
17138
17525
|
if (ctx.config.mode === "plan" || ctx.config.mode === "review") {
|
|
@@ -17167,7 +17554,12 @@ function createCursorAgentCliBackend(command = "cursor-agent", defaultArgs = [])
|
|
|
17167
17554
|
// with zero output; without this watchdog only the 30-min server reaper
|
|
17168
17555
|
// would end the run.
|
|
17169
17556
|
startupTimeoutMs: 18e4,
|
|
17170
|
-
augmentPrompt: (ctx) =>
|
|
17557
|
+
augmentPrompt: (ctx) => {
|
|
17558
|
+
const prompt = buildPromptWithImagePathReferences(ctx, imageFiles);
|
|
17559
|
+
return ctx.config.teamId?.trim() ? `${prompt}
|
|
17560
|
+
|
|
17561
|
+
${buildCodeDiscoveryEnforcementTail()}` : prompt;
|
|
17562
|
+
},
|
|
17171
17563
|
parseStructuredLine: parseCursorStructuredLine
|
|
17172
17564
|
}).run(context);
|
|
17173
17565
|
} finally {
|
|
@@ -17305,174 +17697,259 @@ function createDroidCliBackend(command = "droid", defaultArgs = []) {
|
|
|
17305
17697
|
}
|
|
17306
17698
|
};
|
|
17307
17699
|
}
|
|
17700
|
+
var ACP_PROTOCOL_VERSION = 1;
|
|
17308
17701
|
function asRecord(value2) {
|
|
17309
|
-
return value2 && typeof value2 === "object" ? value2 : null;
|
|
17702
|
+
return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
17310
17703
|
}
|
|
17311
|
-
function
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17704
|
+
function asString(value2) {
|
|
17705
|
+
return typeof value2 === "string" ? value2 : "";
|
|
17706
|
+
}
|
|
17707
|
+
function resumeSessionId(context) {
|
|
17708
|
+
return context.config.runtimeSessionId?.trim() || context.config.providerSessionId?.trim() || "";
|
|
17709
|
+
}
|
|
17710
|
+
function chunkText(content) {
|
|
17711
|
+
const record = asRecord(content);
|
|
17712
|
+
if (!record) return "";
|
|
17713
|
+
if (typeof record.text === "string") return record.text;
|
|
17714
|
+
return chunkText(record.content);
|
|
17715
|
+
}
|
|
17716
|
+
function toolResultText(update) {
|
|
17717
|
+
const content = update.content;
|
|
17718
|
+
if (Array.isArray(content)) {
|
|
17719
|
+
const parts2 = content.map((item) => chunkText(item)).filter((text) => text.length > 0);
|
|
17720
|
+
if (parts2.length) return parts2.join("\n");
|
|
17315
17721
|
}
|
|
17316
|
-
const
|
|
17317
|
-
if (
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17722
|
+
const rawOutput = update.rawOutput;
|
|
17723
|
+
if (rawOutput !== void 0) return JSON.stringify(rawOutput, null, 2);
|
|
17724
|
+
return "";
|
|
17725
|
+
}
|
|
17726
|
+
function toolMetaName(update) {
|
|
17727
|
+
const meta = asRecord(update._meta);
|
|
17728
|
+
const tool = asRecord(meta?.["x.ai/tool"]);
|
|
17729
|
+
return asString(tool?.name);
|
|
17730
|
+
}
|
|
17731
|
+
function resolvePermission(params, context) {
|
|
17732
|
+
const record = asRecord(params);
|
|
17733
|
+
const options = Array.isArray(record?.options) ? record?.options : [];
|
|
17734
|
+
const toolCall = asRecord(record?.toolCall);
|
|
17735
|
+
const meta = asRecord(toolCall?._meta);
|
|
17736
|
+
const tool = asRecord(meta?.["x.ai/tool"]);
|
|
17737
|
+
const isReadOnlyTool = tool?.read_only === true || asString(tool?.kind) === "read";
|
|
17738
|
+
const allow = !shouldUseReadOnlyRuntimePermissions(context.config) || isReadOnlyTool;
|
|
17739
|
+
const allowKinds = ["allow_always", "allow_once", "allow"];
|
|
17740
|
+
const rejectKinds = ["reject_always", "reject_once", "reject", "deny"];
|
|
17741
|
+
const wanted = allow ? allowKinds : rejectKinds;
|
|
17742
|
+
const chosen = options.find((option) => wanted.includes(asString(asRecord(option)?.kind))) ?? (allow ? options[0] : options[options.length - 1]);
|
|
17743
|
+
const optionId = asString(asRecord(chosen)?.optionId);
|
|
17744
|
+
if (!optionId) return { outcome: { outcome: "cancelled" } };
|
|
17745
|
+
return { outcome: { outcome: "selected", optionId } };
|
|
17746
|
+
}
|
|
17747
|
+
function createGrokAcpDriver(imageFiles) {
|
|
17748
|
+
let stdin = null;
|
|
17749
|
+
let nextId = 0;
|
|
17750
|
+
let initId = -1;
|
|
17751
|
+
let sessionOpId = -1;
|
|
17752
|
+
let promptId = -1;
|
|
17753
|
+
let attemptedNewSession = false;
|
|
17754
|
+
let finalized = false;
|
|
17755
|
+
function write(message) {
|
|
17756
|
+
try {
|
|
17757
|
+
stdin?.write(`${JSON.stringify(message)}
|
|
17758
|
+
`);
|
|
17759
|
+
} catch {
|
|
17321
17760
|
}
|
|
17322
17761
|
}
|
|
17323
|
-
|
|
17324
|
-
|
|
17325
|
-
|
|
17326
|
-
|
|
17327
|
-
const value2 = parsed[key];
|
|
17328
|
-
if (typeof value2 === "string" && value2.trim()) return value2.trim();
|
|
17762
|
+
function request(method, params) {
|
|
17763
|
+
const id = ++nextId;
|
|
17764
|
+
write({ jsonrpc: "2.0", id, method, params });
|
|
17765
|
+
return id;
|
|
17329
17766
|
}
|
|
17330
|
-
|
|
17331
|
-
|
|
17332
|
-
for (const key of ["id", "toolCallId", "callId"]) {
|
|
17333
|
-
const value2 = toolCall[key];
|
|
17334
|
-
if (typeof value2 === "string" && value2.trim()) return value2.trim();
|
|
17335
|
-
}
|
|
17767
|
+
function respond(id, result) {
|
|
17768
|
+
write({ jsonrpc: "2.0", id, result });
|
|
17336
17769
|
}
|
|
17337
|
-
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
for (const key of ["args", "arguments", "input", "params"]) {
|
|
17341
|
-
if (parsed[key] !== void 0) return parsed[key];
|
|
17770
|
+
function newSession(context) {
|
|
17771
|
+
attemptedNewSession = true;
|
|
17772
|
+
sessionOpId = request("session/new", { cwd: context.cwd, mcpServers: [] });
|
|
17342
17773
|
}
|
|
17343
|
-
|
|
17344
|
-
|
|
17345
|
-
|
|
17346
|
-
|
|
17774
|
+
function beginSession(context, state) {
|
|
17775
|
+
const resume = resumeSessionId(context);
|
|
17776
|
+
if (resume) {
|
|
17777
|
+
state.runtimeSessionId = resume;
|
|
17778
|
+
sessionOpId = request("session/load", {
|
|
17779
|
+
sessionId: resume,
|
|
17780
|
+
cwd: context.cwd,
|
|
17781
|
+
mcpServers: []
|
|
17782
|
+
});
|
|
17783
|
+
return;
|
|
17347
17784
|
}
|
|
17785
|
+
newSession(context);
|
|
17348
17786
|
}
|
|
17349
|
-
|
|
17350
|
-
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17787
|
+
function sendPrompt(context, state) {
|
|
17788
|
+
const text = buildPromptWithImagePathReferences(context, imageFiles);
|
|
17789
|
+
promptId = request("session/prompt", {
|
|
17790
|
+
sessionId: state.runtimeSessionId,
|
|
17791
|
+
prompt: [{ type: "text", text }]
|
|
17792
|
+
});
|
|
17355
17793
|
}
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
if (typeof value2 === "string" && value2.trim()) {
|
|
17365
|
-
state.runtimeSessionId = value2.trim();
|
|
17366
|
-
break;
|
|
17794
|
+
function finalize(state) {
|
|
17795
|
+
if (finalized) return;
|
|
17796
|
+
finalized = true;
|
|
17797
|
+
state.resultReceived = true;
|
|
17798
|
+
state.onResultReceived?.();
|
|
17799
|
+
try {
|
|
17800
|
+
stdin?.end();
|
|
17801
|
+
} catch {
|
|
17367
17802
|
}
|
|
17368
17803
|
}
|
|
17369
|
-
|
|
17370
|
-
|
|
17371
|
-
|
|
17372
|
-
|
|
17373
|
-
case "thought":
|
|
17374
|
-
case "thinking":
|
|
17375
|
-
case "reasoning": {
|
|
17376
|
-
const text = typeof parsed.data === "string" ? parsed.data : "";
|
|
17377
|
-
if (!text) return true;
|
|
17378
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
17379
|
-
void presenter.onThinking(text);
|
|
17380
|
-
return true;
|
|
17804
|
+
function handleResponse(message, context, state) {
|
|
17805
|
+
if (message.error && message.id === sessionOpId && !attemptedNewSession) {
|
|
17806
|
+
newSession(context);
|
|
17807
|
+
return;
|
|
17381
17808
|
}
|
|
17382
|
-
|
|
17383
|
-
|
|
17384
|
-
|
|
17385
|
-
|
|
17386
|
-
|
|
17387
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
17388
|
-
state.summary += text;
|
|
17389
|
-
void presenter.onAssistantText(text);
|
|
17390
|
-
return true;
|
|
17809
|
+
if (message.error) {
|
|
17810
|
+
state.error = message.error.message?.trim() || "Grok agent error";
|
|
17811
|
+
void context.presenter.onError(state.error);
|
|
17812
|
+
finalize(state);
|
|
17813
|
+
return;
|
|
17391
17814
|
}
|
|
17392
|
-
|
|
17393
|
-
|
|
17394
|
-
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17398
|
-
|
|
17399
|
-
const subtype = typeof parsed.subtype === "string" ? parsed.subtype : "";
|
|
17400
|
-
const status = typeof parsed.status === "string" ? parsed.status : "";
|
|
17401
|
-
state.iterations = Math.max(state.iterations, 1);
|
|
17402
|
-
if (subtype === "completed" || subtype === "result" || status === "completed" || status === "done" || parsed.result !== void 0 || parsed.output !== void 0) {
|
|
17403
|
-
void presenter.onToolResult?.(toolId, extractToolResultText(parsed));
|
|
17404
|
-
return true;
|
|
17405
|
-
}
|
|
17406
|
-
void presenter.onToolUse(toolName, toolArgs, toolId);
|
|
17407
|
-
return true;
|
|
17815
|
+
const result = asRecord(message.result);
|
|
17816
|
+
if (message.id === initId) {
|
|
17817
|
+
const modelState = asRecord(asRecord(result?._meta)?.modelState);
|
|
17818
|
+
const model = asString(modelState?.currentModelId);
|
|
17819
|
+
if (model) state.activeModel = model;
|
|
17820
|
+
beginSession(context, state);
|
|
17821
|
+
return;
|
|
17408
17822
|
}
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17823
|
+
if (message.id === sessionOpId) {
|
|
17824
|
+
const sessionId = asString(result?.sessionId);
|
|
17825
|
+
if (sessionId) state.runtimeSessionId = sessionId;
|
|
17412
17826
|
state.iterations = Math.max(state.iterations, 1);
|
|
17413
|
-
|
|
17414
|
-
return
|
|
17415
|
-
}
|
|
17416
|
-
case "error": {
|
|
17417
|
-
const message = typeof parsed.message === "string" && parsed.message.trim() ? parsed.message.trim() : typeof parsed.data === "string" && parsed.data.trim() ? parsed.data.trim() : "Grok Build run failed";
|
|
17418
|
-
state.error = message;
|
|
17419
|
-
void presenter.onError(message);
|
|
17420
|
-
state.resultReceived = true;
|
|
17421
|
-
state.onResultReceived?.();
|
|
17422
|
-
return true;
|
|
17827
|
+
sendPrompt(context, state);
|
|
17828
|
+
return;
|
|
17423
17829
|
}
|
|
17424
|
-
|
|
17425
|
-
|
|
17426
|
-
const
|
|
17830
|
+
if (message.id === promptId) {
|
|
17831
|
+
const meta = asRecord(result?._meta);
|
|
17832
|
+
const model = asString(meta?.modelId) || state.activeModel || (typeof context.config.selectedModel === "string" && context.config.selectedModel.trim() ? context.config.selectedModel.trim() : "grok");
|
|
17833
|
+
const inputTokens = typeof meta?.inputTokens === "number" ? meta.inputTokens : 0;
|
|
17834
|
+
const outputTokens = typeof meta?.outputTokens === "number" ? meta.outputTokens : 0;
|
|
17835
|
+
const cacheReadTokens = typeof meta?.cachedReadTokens === "number" ? meta.cachedReadTokens : 0;
|
|
17427
17836
|
state.iterations = Math.max(state.iterations, 1);
|
|
17428
17837
|
state.usage = {
|
|
17429
|
-
model
|
|
17838
|
+
model,
|
|
17430
17839
|
numTurns: Math.max(state.iterations, 1),
|
|
17431
|
-
durationMs:
|
|
17432
|
-
inputTokens
|
|
17433
|
-
outputTokens
|
|
17434
|
-
cacheReadTokens
|
|
17840
|
+
durationMs: 0,
|
|
17841
|
+
inputTokens,
|
|
17842
|
+
outputTokens,
|
|
17843
|
+
cacheReadTokens,
|
|
17435
17844
|
cacheCreationTokens: 0,
|
|
17436
17845
|
costUsd: 0
|
|
17437
17846
|
};
|
|
17438
|
-
void presenter.onUsageUpdate?.({
|
|
17439
|
-
model
|
|
17440
|
-
inputTokens
|
|
17441
|
-
outputTokens
|
|
17442
|
-
cacheReadTokens
|
|
17847
|
+
void context.presenter.onUsageUpdate?.({
|
|
17848
|
+
model,
|
|
17849
|
+
inputTokens,
|
|
17850
|
+
outputTokens,
|
|
17851
|
+
cacheReadTokens,
|
|
17443
17852
|
cacheCreationTokens: 0
|
|
17444
17853
|
});
|
|
17445
|
-
|
|
17854
|
+
const stopReason = asString(result?.stopReason).toLowerCase();
|
|
17855
|
+
if (stopReason.includes("error") && !state.error) {
|
|
17446
17856
|
state.error = "Grok Build run failed";
|
|
17447
|
-
void presenter.onError(state.error);
|
|
17857
|
+
void context.presenter.onError(state.error);
|
|
17448
17858
|
}
|
|
17449
|
-
state
|
|
17450
|
-
state.onResultReceived?.();
|
|
17451
|
-
return true;
|
|
17859
|
+
finalize(state);
|
|
17452
17860
|
}
|
|
17453
|
-
default:
|
|
17454
|
-
return false;
|
|
17455
17861
|
}
|
|
17862
|
+
function handleServerRequest(message, context, _state) {
|
|
17863
|
+
const id = message.id;
|
|
17864
|
+
switch (message.method) {
|
|
17865
|
+
case "session/request_permission":
|
|
17866
|
+
respond(id, resolvePermission(message.params, context));
|
|
17867
|
+
return;
|
|
17868
|
+
case "fs/read_text_file":
|
|
17869
|
+
respond(id, { content: "" });
|
|
17870
|
+
return;
|
|
17871
|
+
default:
|
|
17872
|
+
respond(id, null);
|
|
17873
|
+
}
|
|
17874
|
+
}
|
|
17875
|
+
function handleNotification(message, context, state) {
|
|
17876
|
+
const params = asRecord(message.params);
|
|
17877
|
+
const update = asRecord(params?.update);
|
|
17878
|
+
if (!update) return;
|
|
17879
|
+
const presenter = context.presenter;
|
|
17880
|
+
switch (asString(update.sessionUpdate)) {
|
|
17881
|
+
case "agent_thought_chunk": {
|
|
17882
|
+
const text = chunkText(update.content);
|
|
17883
|
+
if (!text) return;
|
|
17884
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
17885
|
+
void presenter.onThinking(text);
|
|
17886
|
+
return;
|
|
17887
|
+
}
|
|
17888
|
+
case "agent_message_chunk": {
|
|
17889
|
+
const text = chunkText(update.content);
|
|
17890
|
+
if (!text) return;
|
|
17891
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
17892
|
+
state.summary += text;
|
|
17893
|
+
void presenter.onAssistantText(text);
|
|
17894
|
+
return;
|
|
17895
|
+
}
|
|
17896
|
+
case "tool_call": {
|
|
17897
|
+
const toolId = asString(update.toolCallId);
|
|
17898
|
+
const toolName = asString(update.title) || toolMetaName(update) || "Grok Tool";
|
|
17899
|
+
const input = update.rawInput ?? {};
|
|
17900
|
+
state.iterations = Math.max(state.iterations, 1);
|
|
17901
|
+
void presenter.onToolUse(toolName, input, toolId);
|
|
17902
|
+
return;
|
|
17903
|
+
}
|
|
17904
|
+
case "tool_call_update": {
|
|
17905
|
+
const status = asString(update.status).toLowerCase();
|
|
17906
|
+
if (status !== "completed" && status !== "failed" && status !== "error") return;
|
|
17907
|
+
const toolId = asString(update.toolCallId);
|
|
17908
|
+
void presenter.onToolResult?.(toolId, toolResultText(update));
|
|
17909
|
+
return;
|
|
17910
|
+
}
|
|
17911
|
+
default:
|
|
17912
|
+
return;
|
|
17913
|
+
}
|
|
17914
|
+
}
|
|
17915
|
+
function handleLine(line, context, state) {
|
|
17916
|
+
if (!stdin && state.stdin) stdin = state.stdin;
|
|
17917
|
+
let parsed;
|
|
17918
|
+
try {
|
|
17919
|
+
parsed = JSON.parse(line);
|
|
17920
|
+
} catch {
|
|
17921
|
+
if (line.trim().length > 0) void context.presenter.onLog(`[grok_cli] ${line}`);
|
|
17922
|
+
return;
|
|
17923
|
+
}
|
|
17924
|
+
const message = asRecord(parsed);
|
|
17925
|
+
if (!message) return;
|
|
17926
|
+
if (message.id !== void 0 && message.method === void 0) {
|
|
17927
|
+
handleResponse(message, context, state);
|
|
17928
|
+
return;
|
|
17929
|
+
}
|
|
17930
|
+
if (message.method && message.id !== void 0) {
|
|
17931
|
+
handleServerRequest(message, context, state);
|
|
17932
|
+
return;
|
|
17933
|
+
}
|
|
17934
|
+
if (message.method) {
|
|
17935
|
+
handleNotification(message, context, state);
|
|
17936
|
+
}
|
|
17937
|
+
}
|
|
17938
|
+
function start(stdinStream, _context) {
|
|
17939
|
+
stdin = stdinStream;
|
|
17940
|
+
initId = request("initialize", {
|
|
17941
|
+
protocolVersion: ACP_PROTOCOL_VERSION,
|
|
17942
|
+
clientCapabilities: {
|
|
17943
|
+
fs: { readTextFile: false, writeTextFile: false },
|
|
17944
|
+
terminal: false
|
|
17945
|
+
}
|
|
17946
|
+
});
|
|
17947
|
+
}
|
|
17948
|
+
return { start, handleLine };
|
|
17456
17949
|
}
|
|
17457
|
-
var parseGrokStructuredLine = createStructuredLineParser(
|
|
17458
|
-
"grok_cli",
|
|
17459
|
-
handleGrokStructuredEvent
|
|
17460
|
-
);
|
|
17461
17950
|
function isGrokCliModelId(model) {
|
|
17462
17951
|
return model === "auto" || /^grok-/.test(model) || /^grok_/.test(model) || model.startsWith("grok-composer-");
|
|
17463
17952
|
}
|
|
17464
|
-
function resolveGrokPermissionMode(config) {
|
|
17465
|
-
if (shouldUseReadOnlyRuntimePermissions(config)) return "plan";
|
|
17466
|
-
switch (config.mode) {
|
|
17467
|
-
case "plan":
|
|
17468
|
-
case "review":
|
|
17469
|
-
return "plan";
|
|
17470
|
-
case "ask":
|
|
17471
|
-
return "dontAsk";
|
|
17472
|
-
default:
|
|
17473
|
-
return "bypassPermissions";
|
|
17474
|
-
}
|
|
17475
|
-
}
|
|
17476
17953
|
function createGrokCliBackend(command = "grok", defaultArgs = []) {
|
|
17477
17954
|
return {
|
|
17478
17955
|
kind: "grok_cli",
|
|
@@ -17482,34 +17959,17 @@ function createGrokCliBackend(command = "grok", defaultArgs = []) {
|
|
|
17482
17959
|
context.config.images,
|
|
17483
17960
|
context.cwd
|
|
17484
17961
|
);
|
|
17962
|
+
const driver = createGrokAcpDriver(imageFiles);
|
|
17485
17963
|
try {
|
|
17486
17964
|
return await createGenericCliBackend({
|
|
17487
17965
|
kind: "grok_cli",
|
|
17488
17966
|
supportTier: "structured",
|
|
17489
17967
|
command,
|
|
17490
17968
|
args: [],
|
|
17491
|
-
buildArgs: (ctx
|
|
17492
|
-
|
|
17493
|
-
|
|
17494
|
-
|
|
17495
|
-
"--output-format",
|
|
17496
|
-
"streaming-json",
|
|
17497
|
-
"--cwd",
|
|
17498
|
-
ctx.cwd,
|
|
17499
|
-
"--permission-mode",
|
|
17500
|
-
resolveGrokPermissionMode(ctx.config)
|
|
17501
|
-
];
|
|
17502
|
-
if (!shouldUseReadOnlyRuntimePermissions(ctx.config)) {
|
|
17503
|
-
args.push("--always-approve");
|
|
17504
|
-
}
|
|
17505
|
-
const resumeId = ctx.config.runtimeSessionId?.trim() || ctx.config.providerSessionId?.trim();
|
|
17506
|
-
if (resumeId) args.push("--resume", resumeId);
|
|
17507
|
-
const model = ctx.config.selectedModel?.trim();
|
|
17508
|
-
if (model && isGrokCliModelId(model)) args.push("--model", model);
|
|
17509
|
-
return [...args, ...defaultArgs];
|
|
17510
|
-
},
|
|
17511
|
-
augmentPrompt: (ctx) => buildPromptWithImagePathReferences(ctx, imageFiles),
|
|
17512
|
-
parseStructuredLine: parseGrokStructuredLine
|
|
17969
|
+
buildArgs: (ctx) => buildGrokAgentArgs(ctx.config, defaultArgs),
|
|
17970
|
+
keepStdinOpen: true,
|
|
17971
|
+
stdinWriter: (stdin, ctx) => driver.start(stdin, ctx),
|
|
17972
|
+
parseStructuredLine: driver.handleLine
|
|
17513
17973
|
}).run(context);
|
|
17514
17974
|
} finally {
|
|
17515
17975
|
cleanup();
|
|
@@ -17517,10 +17977,20 @@ function createGrokCliBackend(command = "grok", defaultArgs = []) {
|
|
|
17517
17977
|
}
|
|
17518
17978
|
};
|
|
17519
17979
|
}
|
|
17980
|
+
function buildGrokAgentArgs(config, defaultArgs = []) {
|
|
17981
|
+
const args = ["agent"];
|
|
17982
|
+
if (!shouldUseReadOnlyRuntimePermissions(config)) {
|
|
17983
|
+
args.push("--always-approve");
|
|
17984
|
+
}
|
|
17985
|
+
const model = config.selectedModel?.trim();
|
|
17986
|
+
if (model && isGrokCliModelId(model)) args.push("--model", model);
|
|
17987
|
+
args.push(...defaultArgs, "stdio");
|
|
17988
|
+
return args;
|
|
17989
|
+
}
|
|
17520
17990
|
var DEFAULT_SKILL_ROOTS = [
|
|
17521
|
-
(0,
|
|
17522
|
-
(0,
|
|
17523
|
-
(0,
|
|
17991
|
+
(0, import_path5.join)((0, import_os5.homedir)(), ".agents", "skills"),
|
|
17992
|
+
(0, import_path5.join)((0, import_os5.homedir)(), ".claude", "skills"),
|
|
17993
|
+
(0, import_path5.join)((0, import_os5.homedir)(), ".config", "opencode", "skills")
|
|
17524
17994
|
];
|
|
17525
17995
|
function parseFrontmatter(content) {
|
|
17526
17996
|
if (!content.startsWith("---\n")) return {};
|
|
@@ -17536,12 +18006,12 @@ function parseFrontmatter(content) {
|
|
|
17536
18006
|
return result;
|
|
17537
18007
|
}
|
|
17538
18008
|
function listSkillFiles(root) {
|
|
17539
|
-
if (!(0,
|
|
18009
|
+
if (!(0, import_fs5.existsSync)(root)) return [];
|
|
17540
18010
|
const files = [];
|
|
17541
|
-
for (const entry of (0,
|
|
18011
|
+
for (const entry of (0, import_fs5.readdirSync)(root, { withFileTypes: true })) {
|
|
17542
18012
|
if (!entry.isDirectory()) continue;
|
|
17543
|
-
const skillPath = (0,
|
|
17544
|
-
if ((0,
|
|
18013
|
+
const skillPath = (0, import_path5.join)(root, entry.name, "SKILL.md");
|
|
18014
|
+
if ((0, import_fs5.existsSync)(skillPath)) files.push(skillPath);
|
|
17545
18015
|
}
|
|
17546
18016
|
return files;
|
|
17547
18017
|
}
|
|
@@ -17550,7 +18020,7 @@ function loadInstalledSkills(roots = DEFAULT_SKILL_ROOTS) {
|
|
|
17550
18020
|
for (const root of roots) {
|
|
17551
18021
|
for (const skillPath of listSkillFiles(root)) {
|
|
17552
18022
|
try {
|
|
17553
|
-
const frontmatter = parseFrontmatter((0,
|
|
18023
|
+
const frontmatter = parseFrontmatter((0, import_fs5.readFileSync)(skillPath, "utf8"));
|
|
17554
18024
|
const name = frontmatter.name;
|
|
17555
18025
|
const description = frontmatter.description;
|
|
17556
18026
|
if (!name || !description || skills.has(name)) continue;
|
|
@@ -18047,40 +18517,40 @@ function parseSkillInvocation(text) {
|
|
|
18047
18517
|
function candidateRoots(cwd) {
|
|
18048
18518
|
const roots = [];
|
|
18049
18519
|
if (cwd) {
|
|
18050
|
-
roots.push((0,
|
|
18051
|
-
roots.push((0,
|
|
18052
|
-
roots.push((0,
|
|
18053
|
-
if ((0,
|
|
18520
|
+
roots.push((0, import_path6.join)(cwd, ".agents", "skills"));
|
|
18521
|
+
roots.push((0, import_path6.join)(cwd, ".claude", "skills"));
|
|
18522
|
+
roots.push((0, import_path6.join)(cwd, ".codex", "skills"));
|
|
18523
|
+
if ((0, import_fs6.existsSync)(cwd)) {
|
|
18054
18524
|
let entries = [];
|
|
18055
18525
|
try {
|
|
18056
|
-
entries = (0,
|
|
18526
|
+
entries = (0, import_fs6.readdirSync)(cwd, { withFileTypes: true });
|
|
18057
18527
|
} catch {
|
|
18058
18528
|
}
|
|
18059
18529
|
for (const entry of entries) {
|
|
18060
18530
|
if (!entry.isDirectory()) continue;
|
|
18061
18531
|
if (entry.name.startsWith(".")) continue;
|
|
18062
|
-
const child = (0,
|
|
18063
|
-
roots.push((0,
|
|
18064
|
-
roots.push((0,
|
|
18065
|
-
roots.push((0,
|
|
18532
|
+
const child = (0, import_path6.join)(cwd, entry.name);
|
|
18533
|
+
roots.push((0, import_path6.join)(child, ".agents", "skills"));
|
|
18534
|
+
roots.push((0, import_path6.join)(child, ".claude", "skills"));
|
|
18535
|
+
roots.push((0, import_path6.join)(child, ".codex", "skills"));
|
|
18066
18536
|
}
|
|
18067
18537
|
}
|
|
18068
18538
|
}
|
|
18069
|
-
const home = (0,
|
|
18070
|
-
roots.push((0,
|
|
18071
|
-
roots.push((0,
|
|
18072
|
-
roots.push((0,
|
|
18539
|
+
const home = (0, import_os6.homedir)();
|
|
18540
|
+
roots.push((0, import_path6.join)(home, ".agents", "skills"));
|
|
18541
|
+
roots.push((0, import_path6.join)(home, ".claude", "skills"));
|
|
18542
|
+
roots.push((0, import_path6.join)(home, ".codex", "skills"));
|
|
18073
18543
|
return Array.from(new Set(roots));
|
|
18074
18544
|
}
|
|
18075
18545
|
function resolveSkillInvocation(text, cwd) {
|
|
18076
18546
|
const parsed = parseSkillInvocation(text);
|
|
18077
18547
|
if (!parsed) return null;
|
|
18078
18548
|
for (const root of candidateRoots(cwd)) {
|
|
18079
|
-
const skillPath = (0,
|
|
18080
|
-
if (!(0,
|
|
18549
|
+
const skillPath = (0, import_path6.join)(root, parsed.command, "SKILL.md");
|
|
18550
|
+
if (!(0, import_fs6.existsSync)(skillPath)) continue;
|
|
18081
18551
|
let content;
|
|
18082
18552
|
try {
|
|
18083
|
-
content = (0,
|
|
18553
|
+
content = (0, import_fs6.readFileSync)(skillPath, "utf-8");
|
|
18084
18554
|
} catch {
|
|
18085
18555
|
continue;
|
|
18086
18556
|
}
|
|
@@ -18190,7 +18660,7 @@ function buildTaskLifecyclePrompt(config) {
|
|
|
18190
18660
|
return [
|
|
18191
18661
|
"## Task Lifecycle",
|
|
18192
18662
|
`This session is attached to taskId "${taskId}".`,
|
|
18193
|
-
`Do not change the task status on your own. The user manages this task's status. Only change it by calling the
|
|
18663
|
+
`Do not change the task status on your own. The user manages this task's status. Only change it by calling the Alan MCP update_task tool when the user explicitly asks you to (for example: "move this to review" or "mark it done"). Do not update the status proactively as you move through research, implementation, testing, or review phases.`,
|
|
18194
18664
|
...statusLines,
|
|
18195
18665
|
"",
|
|
18196
18666
|
"When the user asks you to change the status, use exact status IDs from the available statuses. Do not invent status IDs. If none of the available statuses clearly matches the request, ask which one they mean instead of guessing."
|
|
@@ -18309,12 +18779,12 @@ var BaseMachineAgent = class _BaseMachineAgent {
|
|
|
18309
18779
|
buildRuntimeConfig(config, safeProjectPath) {
|
|
18310
18780
|
const backendKind = this.resolveBackendKind(config);
|
|
18311
18781
|
const supportTier = config.supportTier || this.getDefaultSupportTier(backendKind);
|
|
18312
|
-
const systemPromptParts = [
|
|
18313
|
-
const agentPrompt = config.agentPrompt?.trim() ||
|
|
18782
|
+
const systemPromptParts = [ALAN_IDENTITY_PROMPT];
|
|
18783
|
+
const agentPrompt = config.agentPrompt?.trim() || getAlanAgentPrompt(config.agentId);
|
|
18314
18784
|
if (agentPrompt) systemPromptParts.push(agentPrompt);
|
|
18315
|
-
const modeHint =
|
|
18785
|
+
const modeHint = getAlanModePrompt(config.mode);
|
|
18316
18786
|
if (modeHint) systemPromptParts.push(modeHint);
|
|
18317
|
-
systemPromptParts.push(
|
|
18787
|
+
systemPromptParts.push(ALAN_ASK_USER_PROMPT);
|
|
18318
18788
|
const operatorSystemPrompt = config.systemPrompt?.trim();
|
|
18319
18789
|
if (operatorSystemPrompt) systemPromptParts.push(operatorSystemPrompt);
|
|
18320
18790
|
const workingDir = config.worktreePath || safeProjectPath;
|
|
@@ -18326,8 +18796,14 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
18326
18796
|
}
|
|
18327
18797
|
const lifecyclePrompt = buildTaskLifecyclePrompt(config);
|
|
18328
18798
|
if (lifecyclePrompt) systemPromptParts.push(lifecyclePrompt);
|
|
18799
|
+
const teamId = config.teamId;
|
|
18800
|
+
const teamCodeOverlay = teamId ? buildAlanTeamCodeContextOverlay(teamId) : void 0;
|
|
18801
|
+
if (teamCodeOverlay) {
|
|
18802
|
+
systemPromptParts.push(teamCodeOverlay);
|
|
18803
|
+
}
|
|
18329
18804
|
const extra = this.buildExtraSystemPromptParts(config);
|
|
18330
18805
|
systemPromptParts.push(...extra);
|
|
18806
|
+
const mergedAppend = [config.systemPromptAppend?.trim(), teamCodeOverlay].filter((value2) => Boolean(value2 && value2.length > 0)).join("\n\n");
|
|
18331
18807
|
return {
|
|
18332
18808
|
...config,
|
|
18333
18809
|
backendKind,
|
|
@@ -18335,12 +18811,13 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
18335
18811
|
runtimeCommand: config.runtimeCommand || this.runtime.runtimeCommand,
|
|
18336
18812
|
runtimeArgs: config.runtimeArgs || this.runtime.runtimeArgs,
|
|
18337
18813
|
selectedModel: config.selectedModel || defaultSelectedModelForBackend(backendKind),
|
|
18338
|
-
systemPrompt: systemPromptParts.join("\n\n") || void 0
|
|
18814
|
+
systemPrompt: systemPromptParts.join("\n\n") || void 0,
|
|
18815
|
+
systemPromptAppend: mergedAppend || void 0
|
|
18339
18816
|
};
|
|
18340
18817
|
}
|
|
18341
18818
|
async runCliBackend(config, safeProjectPath) {
|
|
18342
18819
|
const backendKind = this.resolveBackendKind(config);
|
|
18343
|
-
const defaultCwd = (0, import_path.join)((0, import_os.homedir)(), "Documents", "
|
|
18820
|
+
const defaultCwd = (0, import_path.join)((0, import_os.homedir)(), "Documents", "alan");
|
|
18344
18821
|
if (!(0, import_fs.existsSync)(defaultCwd)) {
|
|
18345
18822
|
(0, import_fs.mkdirSync)(defaultCwd, { recursive: true });
|
|
18346
18823
|
}
|
|
@@ -18479,6 +18956,19 @@ Prefer relative paths and keep your work scoped to this project.`
|
|
|
18479
18956
|
this.presenter.onComplete(result);
|
|
18480
18957
|
return result;
|
|
18481
18958
|
}
|
|
18959
|
+
if (initialConfig.conversationId) {
|
|
18960
|
+
try {
|
|
18961
|
+
const writtenClis = syncAlanMcpSessionHeaders({
|
|
18962
|
+
conversationId: initialConfig.conversationId,
|
|
18963
|
+
teamId: initialConfig.teamId
|
|
18964
|
+
});
|
|
18965
|
+
if (writtenClis.length > 0) {
|
|
18966
|
+
void this.presenter.onLog(`Synced Alan MCP session headers to ${writtenClis.join(", ")}`);
|
|
18967
|
+
}
|
|
18968
|
+
} catch (error) {
|
|
18969
|
+
console.warn("[base-machine-agent] Failed to sync MCP session headers", error);
|
|
18970
|
+
}
|
|
18971
|
+
}
|
|
18482
18972
|
try {
|
|
18483
18973
|
return await this.runCliBackend(initialConfig, safeProjectPath);
|
|
18484
18974
|
} catch (error) {
|
|
@@ -18520,8 +19010,8 @@ var MAX_SESSION_VIDEO_BYTES = 100 * 1024 * 1024;
|
|
|
18520
19010
|
function isRecord(value2) {
|
|
18521
19011
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
18522
19012
|
}
|
|
18523
|
-
function shouldExtractGeneratedImagesFromToolResult(toolName,
|
|
18524
|
-
return isGeneratedImageToolName(toolName)
|
|
19013
|
+
function shouldExtractGeneratedImagesFromToolResult(toolName, _content, _options = {}) {
|
|
19014
|
+
return isGeneratedImageToolName(toolName);
|
|
18525
19015
|
}
|
|
18526
19016
|
function collectStringValues(value2, out) {
|
|
18527
19017
|
if (typeof value2 === "string") {
|
|
@@ -18585,14 +19075,14 @@ function normalizePath(candidate, cwd) {
|
|
|
18585
19075
|
} catch {
|
|
18586
19076
|
}
|
|
18587
19077
|
if (path.startsWith("~/")) return null;
|
|
18588
|
-
if ((0,
|
|
18589
|
-
return cwd ? (0,
|
|
19078
|
+
if ((0, import_path7.isAbsolute)(path)) return path;
|
|
19079
|
+
return cwd ? (0, import_path7.join)(cwd, path) : null;
|
|
18590
19080
|
}
|
|
18591
19081
|
function resolveToolCwd(cwd, toolInput) {
|
|
18592
19082
|
if (!isRecord(toolInput)) return cwd;
|
|
18593
19083
|
const raw = typeof toolInput.cwd === "string" ? toolInput.cwd : typeof toolInput.workdir === "string" ? toolInput.workdir : typeof toolInput.workingDirectory === "string" ? toolInput.workingDirectory : null;
|
|
18594
19084
|
if (!raw) return cwd;
|
|
18595
|
-
return (0,
|
|
19085
|
+
return (0, import_path7.isAbsolute)(raw) || !cwd ? raw : (0, import_path7.join)(cwd, raw);
|
|
18596
19086
|
}
|
|
18597
19087
|
function extractBrowserMediaPathHints(content, cwd, toolName, options = {}, kind = "video") {
|
|
18598
19088
|
if (!isAgentBrowserMediaInvocation(toolName, content, options.toolInput, kind)) return [];
|
|
@@ -18635,19 +19125,19 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
|
|
|
18635
19125
|
const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
|
|
18636
19126
|
for (const candidate of extractPathCandidates(content, options)) {
|
|
18637
19127
|
const path = normalizePath(candidate, effectiveCwd);
|
|
18638
|
-
if (!path || seen.has(path) || !(0,
|
|
19128
|
+
if (!path || seen.has(path) || !(0, import_fs7.existsSync)(path)) continue;
|
|
18639
19129
|
seen.add(path);
|
|
18640
|
-
const ext = (0,
|
|
19130
|
+
const ext = (0, import_path7.extname)(path).toLowerCase();
|
|
18641
19131
|
const mimeType = IMAGE_MIME_BY_EXT[ext];
|
|
18642
19132
|
if (!mimeType) continue;
|
|
18643
|
-
const stat = (0,
|
|
19133
|
+
const stat = (0, import_fs7.statSync)(path);
|
|
18644
19134
|
if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_GENERATED_IMAGE_BYTES) continue;
|
|
18645
|
-
const buffer = (0,
|
|
19135
|
+
const buffer = (0, import_fs7.readFileSync)(path);
|
|
18646
19136
|
const { width, height } = readImageDimensions2(buffer, mimeType);
|
|
18647
19137
|
images.push({
|
|
18648
19138
|
type: "generated_image",
|
|
18649
19139
|
id: (0, import_crypto2.randomUUID)(),
|
|
18650
|
-
filename: (0,
|
|
19140
|
+
filename: (0, import_path7.basename)(path),
|
|
18651
19141
|
mimeType,
|
|
18652
19142
|
size: buffer.length,
|
|
18653
19143
|
width,
|
|
@@ -18662,27 +19152,36 @@ function extractGeneratedImagesFromToolResult(toolId, content, cwd, toolName, op
|
|
|
18662
19152
|
return images;
|
|
18663
19153
|
}
|
|
18664
19154
|
function extractSessionMediaFromToolResult(toolId, content, cwd, toolName, options = {}) {
|
|
18665
|
-
|
|
19155
|
+
const isImage = isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "image");
|
|
19156
|
+
const isVideo = isAgentBrowserMediaInvocation(toolName, content, options.toolInput, "video");
|
|
19157
|
+
if (!isImage && !isVideo) return [];
|
|
18666
19158
|
const media = [];
|
|
18667
19159
|
const seen = /* @__PURE__ */ new Set();
|
|
18668
19160
|
const effectiveCwd = resolveToolCwd(cwd, options.toolInput);
|
|
18669
|
-
|
|
19161
|
+
const kind = isVideo ? "video" : "image";
|
|
19162
|
+
const extensionsPattern = kind === "video" ? "mp4|webm|mov" : "png|jpe?g|gif|webp";
|
|
19163
|
+
const maxBytes = kind === "video" ? MAX_SESSION_VIDEO_BYTES : MAX_GENERATED_IMAGE_BYTES;
|
|
19164
|
+
const mimeByExt = kind === "video" ? VIDEO_MIME_BY_EXT : IMAGE_MIME_BY_EXT;
|
|
19165
|
+
for (const candidate of extractPathCandidates(content, options, extensionsPattern)) {
|
|
18670
19166
|
const path = normalizePath(candidate, effectiveCwd);
|
|
18671
|
-
if (!path || seen.has(path) || !(0,
|
|
19167
|
+
if (!path || seen.has(path) || !(0, import_fs7.existsSync)(path)) continue;
|
|
18672
19168
|
seen.add(path);
|
|
18673
|
-
const ext = (0,
|
|
18674
|
-
const mimeType =
|
|
19169
|
+
const ext = (0, import_path7.extname)(path).toLowerCase();
|
|
19170
|
+
const mimeType = mimeByExt[ext];
|
|
18675
19171
|
if (!mimeType) continue;
|
|
18676
|
-
const stat = (0,
|
|
18677
|
-
if (!stat.isFile() || stat.size <= 0 || stat.size >
|
|
18678
|
-
const buffer = (0,
|
|
19172
|
+
const stat = (0, import_fs7.statSync)(path);
|
|
19173
|
+
if (!stat.isFile() || stat.size <= 0 || stat.size > maxBytes) continue;
|
|
19174
|
+
const buffer = (0, import_fs7.readFileSync)(path);
|
|
19175
|
+
const dimensions = kind === "image" ? readImageDimensions2(buffer, mimeType) : null;
|
|
18679
19176
|
media.push({
|
|
18680
19177
|
type: "session_media",
|
|
18681
|
-
kind
|
|
19178
|
+
kind,
|
|
18682
19179
|
id: (0, import_crypto2.randomUUID)(),
|
|
18683
|
-
filename: (0,
|
|
19180
|
+
filename: (0, import_path7.basename)(path),
|
|
18684
19181
|
mimeType,
|
|
18685
19182
|
size: buffer.length,
|
|
19183
|
+
width: dimensions?.width,
|
|
19184
|
+
height: dimensions?.height,
|
|
18686
19185
|
data: buffer.toString("base64"),
|
|
18687
19186
|
sourcePath: path,
|
|
18688
19187
|
sourceHash: (0, import_crypto2.createHash)("sha256").update(buffer).digest("hex"),
|
|
@@ -18928,11 +19427,11 @@ ${meta.description}`);
|
|
|
18928
19427
|
}
|
|
18929
19428
|
parts2.push(lines.join("\n"));
|
|
18930
19429
|
}
|
|
18931
|
-
const taskId = config.taskId ?? process.env.
|
|
18932
|
-
const workflowId = config.workflowId ?? process.env.
|
|
18933
|
-
const workflowExecutionId = config.workflowExecutionId ?? process.env.
|
|
18934
|
-
const conversationId = config.conversationId ?? process.env.
|
|
18935
|
-
const teamId = config.teamId;
|
|
19430
|
+
const taskId = config.taskId ?? process.env.ALAN_TASK_ID;
|
|
19431
|
+
const workflowId = config.workflowId ?? process.env.ALAN_WORKFLOW_ID;
|
|
19432
|
+
const workflowExecutionId = config.workflowExecutionId ?? process.env.ALAN_WORKFLOW_EXECUTION_ID;
|
|
19433
|
+
const conversationId = config.conversationId ?? process.env.ALAN_SESSION_ID;
|
|
19434
|
+
const teamId = config.teamId ?? process.env.ALAN_TEAM_ID;
|
|
18936
19435
|
const artifactIds = [];
|
|
18937
19436
|
if (teamId) artifactIds.push(`- teamId: "${teamId}"`);
|
|
18938
19437
|
const isWorkflowSession = !!(workflowId || workflowExecutionId);
|
|
@@ -18956,7 +19455,7 @@ ${meta.description}`);
|
|
|
18956
19455
|
[
|
|
18957
19456
|
"## Prototype Artifacts",
|
|
18958
19457
|
"When the user asks to create, build, modify, or iterate on a prototype, do not create local prototype files such as prototype/index.html.",
|
|
18959
|
-
"Instead, generate a Vite React TypeScript source snapshot and call the
|
|
19458
|
+
"Instead, generate a Vite React TypeScript source snapshot and call the Alan MCP tool upsert_prototype_version with teamId, workflowId, workflowExecutionId, conversationId, title, sourceSnapshot, and packageJson.",
|
|
18960
19459
|
"For follow-up changes to the same prototype, call list_prototypes (with workflowId) or get_prototype if needed, then call upsert_prototype_version again with the existing prototypeId and parentVersionId."
|
|
18961
19460
|
].join("\n")
|
|
18962
19461
|
);
|
|
@@ -18965,16 +19464,11 @@ ${meta.description}`);
|
|
|
18965
19464
|
[
|
|
18966
19465
|
"## Prototype Artifacts",
|
|
18967
19466
|
"When the user asks to create, build, modify, or iterate on a prototype, do not create local prototype files such as prototype/index.html.",
|
|
18968
|
-
"Instead, generate a Vite React TypeScript source snapshot and call the
|
|
19467
|
+
"Instead, generate a Vite React TypeScript source snapshot and call the Alan MCP tool upsert_prototype_version with teamId, taskId, conversationId, title, sourceSnapshot, and packageJson.",
|
|
18969
19468
|
"For follow-up changes to the same prototype, call list_prototypes or get_prototype if needed, then call upsert_prototype_version again with the existing prototypeId and parentVersionId so the task Prototypes panel renders the new version."
|
|
18970
19469
|
].join("\n")
|
|
18971
19470
|
);
|
|
18972
19471
|
}
|
|
18973
|
-
if (teamId) {
|
|
18974
|
-
parts2.push(
|
|
18975
|
-
`You are operating within team \`${teamId}\`. Always use this as the \`teamId\` for all MCP tool calls that accept it (list_tasks, search, start_task_session, etc.). Do not call list_teams.`
|
|
18976
|
-
);
|
|
18977
|
-
}
|
|
18978
19472
|
return parts2;
|
|
18979
19473
|
}
|
|
18980
19474
|
};
|
|
@@ -18983,10 +19477,10 @@ ${meta.description}`);
|
|
|
18983
19477
|
var import_node_fs5 = require("fs");
|
|
18984
19478
|
var import_node_os5 = require("os");
|
|
18985
19479
|
var import_node_path4 = require("path");
|
|
18986
|
-
function
|
|
19480
|
+
function ensureAlanMcpRegistered(backendKind, registration, home = (0, import_node_os5.homedir)()) {
|
|
18987
19481
|
try {
|
|
18988
|
-
const mcpServers =
|
|
18989
|
-
const files =
|
|
19482
|
+
const mcpServers = buildAlanMcpServers(registration);
|
|
19483
|
+
const files = alanMcpConfigFilesForBackend(backendKind, mcpServers, home);
|
|
18990
19484
|
const paths = [];
|
|
18991
19485
|
for (const file of files) {
|
|
18992
19486
|
(0, import_node_fs5.mkdirSync)((0, import_node_path4.dirname)(file.path), { recursive: true });
|
|
@@ -19010,8 +19504,8 @@ function ensureAidenMcpRegistered(backendKind, registration, home = (0, import_n
|
|
|
19010
19504
|
return { ok: false, paths: [], error: err instanceof Error ? err.message : String(err) };
|
|
19011
19505
|
}
|
|
19012
19506
|
}
|
|
19013
|
-
function
|
|
19014
|
-
const files =
|
|
19507
|
+
function verifyAlanMcpRegistered(backendKind, home = (0, import_node_os5.homedir)()) {
|
|
19508
|
+
const files = alanMcpConfigFilesForBackend(backendKind, {}, home);
|
|
19015
19509
|
const missing = files.map((f) => f.path).filter((p) => !(0, import_node_fs5.existsSync)(p));
|
|
19016
19510
|
return { ok: missing.length === 0, missing };
|
|
19017
19511
|
}
|
|
@@ -19163,20 +19657,40 @@ function extractModelIdsFromJson(value2) {
|
|
|
19163
19657
|
}
|
|
19164
19658
|
return [];
|
|
19165
19659
|
}
|
|
19166
|
-
function
|
|
19660
|
+
function normalizeCodexReasoningEffort(effort) {
|
|
19661
|
+
const normalized = effort.trim().toLowerCase();
|
|
19662
|
+
if (!normalized) return null;
|
|
19663
|
+
if (normalized === "none") return "minimal";
|
|
19664
|
+
if (normalized === "minimal" || normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "xhigh" || normalized === "max" || normalized === "ultra") {
|
|
19665
|
+
return normalized;
|
|
19666
|
+
}
|
|
19667
|
+
return null;
|
|
19668
|
+
}
|
|
19669
|
+
function extractCodexDiscoveredModelIdsFromDebugJson(value2) {
|
|
19167
19670
|
if (!value2 || typeof value2 !== "object") return [];
|
|
19168
19671
|
const models = value2.models;
|
|
19169
19672
|
if (!Array.isArray(models)) return [];
|
|
19170
|
-
const
|
|
19673
|
+
const ids = [];
|
|
19171
19674
|
for (const entry of models) {
|
|
19172
19675
|
if (!entry || typeof entry !== "object") continue;
|
|
19173
19676
|
const record = entry;
|
|
19174
19677
|
const visibility = typeof record.visibility === "string" ? record.visibility.toLowerCase() : "list";
|
|
19175
19678
|
if (visibility === "hidden" || visibility === "hide") continue;
|
|
19176
19679
|
const slug = readModelId(record.slug);
|
|
19177
|
-
if (slug)
|
|
19680
|
+
if (!slug) continue;
|
|
19681
|
+
const reasoningLevels = record.supported_reasoning_levels;
|
|
19682
|
+
if (Array.isArray(reasoningLevels) && reasoningLevels.length > 0) {
|
|
19683
|
+
for (const levelEntry of reasoningLevels) {
|
|
19684
|
+
if (!levelEntry || typeof levelEntry !== "object") continue;
|
|
19685
|
+
const effort = typeof levelEntry.effort === "string" ? levelEntry.effort : null;
|
|
19686
|
+
const normalized = effort ? normalizeCodexReasoningEffort(effort) : null;
|
|
19687
|
+
if (normalized) ids.push(`${slug}-${normalized}`);
|
|
19688
|
+
}
|
|
19689
|
+
continue;
|
|
19690
|
+
}
|
|
19691
|
+
ids.push(slug);
|
|
19178
19692
|
}
|
|
19179
|
-
return [...new Set(
|
|
19693
|
+
return [...new Set(ids)];
|
|
19180
19694
|
}
|
|
19181
19695
|
function extractModelIdsFromText(value2) {
|
|
19182
19696
|
const ansiPattern = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
@@ -19267,8 +19781,8 @@ async function probeJsonOrTextModels(executable, env, argSets, options) {
|
|
|
19267
19781
|
if (!result || result.error || result.status !== 0 || !result.stdout.trim()) continue;
|
|
19268
19782
|
try {
|
|
19269
19783
|
const parsed = JSON.parse(result.stdout);
|
|
19270
|
-
const
|
|
19271
|
-
if (
|
|
19784
|
+
const codexModels = extractCodexDiscoveredModelIdsFromDebugJson(parsed);
|
|
19785
|
+
if (codexModels.length > 0) return codexModels;
|
|
19272
19786
|
const models = extractModelIdsFromJson(parsed);
|
|
19273
19787
|
if (models.length > 0) return models;
|
|
19274
19788
|
} catch {
|
|
@@ -19349,13 +19863,13 @@ async function discoverProviderModels(provider, executable, env) {
|
|
|
19349
19863
|
}
|
|
19350
19864
|
|
|
19351
19865
|
// src/version.ts
|
|
19352
|
-
var AGENT_VERSION = "0.1.
|
|
19866
|
+
var AGENT_VERSION = "0.1.35";
|
|
19353
19867
|
|
|
19354
19868
|
// src/daemon.ts
|
|
19355
|
-
var STAGING_API_URL = process.env.
|
|
19356
|
-
var STAGING_WS_URL = process.env.
|
|
19357
|
-
var PRODUCTION_API_URL = "https://api.
|
|
19358
|
-
var PRODUCTION_WS_URL = "wss://ws.
|
|
19869
|
+
var STAGING_API_URL = process.env.ALAN_STAGING_API_URL ?? "https://staging-api.tryalan.ai";
|
|
19870
|
+
var STAGING_WS_URL = process.env.ALAN_STAGING_WS_URL ?? "wss://alan-staging-ws.tryalan.ai";
|
|
19871
|
+
var PRODUCTION_API_URL = "https://api.tryalan.ai";
|
|
19872
|
+
var PRODUCTION_WS_URL = "wss://ws.tryalan.ai";
|
|
19359
19873
|
var LOCAL_API_URL = "http://localhost:8400";
|
|
19360
19874
|
var LOCAL_WS_URL = "ws://localhost:8401";
|
|
19361
19875
|
function requiredString(params, key) {
|
|
@@ -19405,10 +19919,10 @@ function gitSafe(cwd, args) {
|
|
|
19405
19919
|
}
|
|
19406
19920
|
function worktreeDir(projectPath) {
|
|
19407
19921
|
const resolved = (0, import_node_path5.resolve)(projectPath);
|
|
19408
|
-
return (0, import_node_path5.join)((0, import_node_path5.dirname)(resolved), `${(0, import_node_path5.basename)(resolved)}-
|
|
19922
|
+
return (0, import_node_path5.join)((0, import_node_path5.dirname)(resolved), `${(0, import_node_path5.basename)(resolved)}-alan-worktrees`);
|
|
19409
19923
|
}
|
|
19410
19924
|
function pathSegment(value2) {
|
|
19411
|
-
return value2.toLowerCase().replace(/^
|
|
19925
|
+
return value2.toLowerCase().replace(/^alan\//, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "task";
|
|
19412
19926
|
}
|
|
19413
19927
|
function repoName(repoUrl, localPath) {
|
|
19414
19928
|
return repoUrl.replace(/\.git$/, "").split("/").pop() || (0, import_node_path5.basename)(localPath) || "repo";
|
|
@@ -19579,7 +20093,7 @@ async function probeLocalDaemonEndpoint(port, token, timeoutMs = 750) {
|
|
|
19579
20093
|
}
|
|
19580
20094
|
}
|
|
19581
20095
|
function describeOccupiedLocalDaemon(port) {
|
|
19582
|
-
return `local daemon API port ${port} is already in use. If
|
|
20096
|
+
return `local daemon API port ${port} is already in use. If Alan desktop is open, it manages the daemon automatically. Quit Alan desktop or stop the existing daemon before starting another.`;
|
|
19583
20097
|
}
|
|
19584
20098
|
var boundAgentConfigPath = null;
|
|
19585
20099
|
function bindConfigPath(args) {
|
|
@@ -19587,10 +20101,10 @@ function bindConfigPath(args) {
|
|
|
19587
20101
|
return boundAgentConfigPath;
|
|
19588
20102
|
}
|
|
19589
20103
|
function getConfigPath() {
|
|
19590
|
-
return process.env.
|
|
20104
|
+
return process.env.ALAN_AGENT_CONFIG_PATH ?? boundAgentConfigPath ?? resolveAgentConfigPath({ profile: "production" });
|
|
19591
20105
|
}
|
|
19592
20106
|
function getEndpointDefaultsPath() {
|
|
19593
|
-
return process.env.
|
|
20107
|
+
return process.env.ALAN_AGENT_ENDPOINTS_PATH ?? (0, import_node_path5.join)((0, import_node_path5.dirname)(getConfigPath()), "endpoints.json");
|
|
19594
20108
|
}
|
|
19595
20109
|
function readConfig() {
|
|
19596
20110
|
const configPath = getConfigPath();
|
|
@@ -19609,7 +20123,14 @@ function readEndpointDefaults() {
|
|
|
19609
20123
|
function writeConfig(config) {
|
|
19610
20124
|
const configPath = getConfigPath();
|
|
19611
20125
|
(0, import_node_fs7.mkdirSync)((0, import_node_path5.dirname)(configPath), { recursive: true, mode: 448 });
|
|
19612
|
-
(0,
|
|
20126
|
+
const pendingPath = `${configPath}.pending-${process.pid}-${(0, import_node_crypto.randomBytes)(6).toString("hex")}`;
|
|
20127
|
+
try {
|
|
20128
|
+
(0, import_node_fs7.writeFileSync)(pendingPath, JSON.stringify(config, null, 2), { mode: 384 });
|
|
20129
|
+
(0, import_node_fs7.chmodSync)(pendingPath, 384);
|
|
20130
|
+
(0, import_node_fs7.renameSync)(pendingPath, configPath);
|
|
20131
|
+
} finally {
|
|
20132
|
+
(0, import_node_fs7.rmSync)(pendingPath, { force: true });
|
|
20133
|
+
}
|
|
19613
20134
|
}
|
|
19614
20135
|
function removeConfig() {
|
|
19615
20136
|
const configPath = getConfigPath();
|
|
@@ -19629,8 +20150,8 @@ function resolveEndpoints(args, stored = {}) {
|
|
|
19629
20150
|
const profileApiUrl = requestedProfile === "local" ? LOCAL_API_URL : requestedProfile === "staging" ? STAGING_API_URL : PRODUCTION_API_URL;
|
|
19630
20151
|
const profileWsUrl = requestedProfile === "local" ? LOCAL_WS_URL : requestedProfile === "staging" ? STAGING_WS_URL : PRODUCTION_WS_URL;
|
|
19631
20152
|
const endpointDefaults = readEndpointDefaults();
|
|
19632
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.
|
|
19633
|
-
const wsUrl = argValue(args, "--ws-url") ?? process.env.
|
|
20153
|
+
const apiUrl = argValue(args, "--api-url") ?? process.env.ALAN_API_URL ?? stored.apiUrl ?? endpointDefaults.apiUrl ?? profileApiUrl;
|
|
20154
|
+
const wsUrl = argValue(args, "--ws-url") ?? process.env.ALAN_WS_URL ?? stored.wsUrl ?? endpointDefaults.wsUrl ?? profileWsUrl;
|
|
19634
20155
|
const endpointProfile = apiUrl === profileApiUrl && wsUrl === profileWsUrl ? requestedProfile : apiUrl === endpointDefaults.apiUrl && wsUrl === endpointDefaults.wsUrl && endpointDefaults.endpointProfile ? endpointDefaults.endpointProfile : "custom";
|
|
19635
20156
|
return { apiUrl, wsUrl, endpointProfile };
|
|
19636
20157
|
}
|
|
@@ -19640,9 +20161,12 @@ function isLocalEndpoint(url2) {
|
|
|
19640
20161
|
function checkStoredEndpointConsistency(stored, endpointDefaults) {
|
|
19641
20162
|
const profile = stored.endpointProfile;
|
|
19642
20163
|
if (profile !== "production" && profile !== "staging") return null;
|
|
20164
|
+
const builtInApiUrl = profile === "production" ? PRODUCTION_API_URL : STAGING_API_URL;
|
|
20165
|
+
const builtInWsUrl = profile === "production" ? PRODUCTION_WS_URL : STAGING_WS_URL;
|
|
20166
|
+
if (stored.apiUrl === builtInApiUrl && stored.wsUrl === builtInWsUrl) return null;
|
|
19643
20167
|
const defaultsMatchProfile = endpointDefaults.endpointProfile === profile;
|
|
19644
|
-
const expectedApiUrl = (defaultsMatchProfile ? endpointDefaults.apiUrl : void 0) ??
|
|
19645
|
-
const expectedWsUrl = (defaultsMatchProfile ? endpointDefaults.wsUrl : void 0) ??
|
|
20168
|
+
const expectedApiUrl = (defaultsMatchProfile ? endpointDefaults.apiUrl : void 0) ?? builtInApiUrl;
|
|
20169
|
+
const expectedWsUrl = (defaultsMatchProfile ? endpointDefaults.wsUrl : void 0) ?? builtInWsUrl;
|
|
19646
20170
|
const issues = [];
|
|
19647
20171
|
if (stored.wsUrl && stored.wsUrl !== expectedWsUrl) {
|
|
19648
20172
|
issues.push(`wsUrl ${stored.wsUrl} (expected ${expectedWsUrl})`);
|
|
@@ -19658,7 +20182,7 @@ function checkStoredEndpointConsistency(stored, endpointDefaults) {
|
|
|
19658
20182
|
};
|
|
19659
20183
|
}
|
|
19660
20184
|
function getLocalApiPort(args, stored) {
|
|
19661
|
-
const raw = argValue(args, "--local-api-port") ?? process.env.
|
|
20185
|
+
const raw = argValue(args, "--local-api-port") ?? process.env.ALAN_AGENT_LOCAL_API_PORT;
|
|
19662
20186
|
if (raw) return Number.parseInt(raw, 10);
|
|
19663
20187
|
const storedPort = stored.localApiPort;
|
|
19664
20188
|
if (storedPort && storedPort > 0) return storedPort;
|
|
@@ -20189,17 +20713,17 @@ var RuntimePresenter = class {
|
|
|
20189
20713
|
async function setupDaemon(args) {
|
|
20190
20714
|
bindConfigPath(args);
|
|
20191
20715
|
const { apiUrl, wsUrl, endpointProfile } = resolveEndpoints(args);
|
|
20192
|
-
const teamId = argValue(args, "--team") ?? process.env.
|
|
20193
|
-
const setupToken = argValue(args, "--setup-token") ?? process.env.
|
|
20194
|
-
const token = argValue(args, "--token") ?? process.env.
|
|
20716
|
+
const teamId = argValue(args, "--team") ?? process.env.ALAN_TEAM_ID;
|
|
20717
|
+
const setupToken = argValue(args, "--setup-token") ?? process.env.ALAN_RUNTIME_SETUP_TOKEN;
|
|
20718
|
+
const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
|
|
20195
20719
|
const displayName = argValue(args, "--name") ?? (0, import_node_os6.hostname)();
|
|
20196
|
-
const scope = argValue(args, "--scope") ?? process.env.
|
|
20720
|
+
const scope = argValue(args, "--scope") ?? process.env.ALAN_RUNTIME_SCOPE ?? "team";
|
|
20197
20721
|
if (scope !== "team" && scope !== "user") {
|
|
20198
20722
|
throw new Error("setup --scope must be either 'team' or 'user'");
|
|
20199
20723
|
}
|
|
20200
20724
|
if (!setupToken && (!teamId || !token)) {
|
|
20201
20725
|
throw new Error(
|
|
20202
|
-
"setup requires --setup-token <token> or --team <team-id> and --token <
|
|
20726
|
+
"setup requires --setup-token <token> or --team <team-id> and --token <alan PAT>.\nFor normal setup, copy the setup token from Alan and run: alan-agent setup --setup-token <token>\nFor browser authorization, run: alan-agent login"
|
|
20203
20727
|
);
|
|
20204
20728
|
}
|
|
20205
20729
|
const response = await fetch(
|
|
@@ -20241,7 +20765,7 @@ async function setupDaemon(args) {
|
|
|
20241
20765
|
localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
|
|
20242
20766
|
displayName: body.runtime.displayName ?? displayName
|
|
20243
20767
|
});
|
|
20244
|
-
console.info("[
|
|
20768
|
+
console.info("[alan-agent] Runtime registered", {
|
|
20245
20769
|
runtimeId: body.runtime.id,
|
|
20246
20770
|
configPath: getConfigPath()
|
|
20247
20771
|
});
|
|
@@ -20266,8 +20790,8 @@ async function loginWithDeviceCode(args) {
|
|
|
20266
20790
|
}
|
|
20267
20791
|
const authorization = await start.json();
|
|
20268
20792
|
const intervalMs = Math.max(0, (authorization.intervalSeconds ?? 2) * 1e3);
|
|
20269
|
-
console.info(`[
|
|
20270
|
-
console.info(`[
|
|
20793
|
+
console.info(`[alan-agent] Open ${authorization.verificationUri}`);
|
|
20794
|
+
console.info(`[alan-agent] Enter code ${authorization.userCode}`);
|
|
20271
20795
|
for (let attempt = 0; attempt < maxPolls; attempt += 1) {
|
|
20272
20796
|
if (attempt > 0) await sleep(intervalMs);
|
|
20273
20797
|
const token = await fetch(`${apiUrl}/public/runtimes/device-authorizations/token`, {
|
|
@@ -20293,7 +20817,7 @@ async function loginWithDeviceCode(args) {
|
|
|
20293
20817
|
localApiToken: (0, import_node_crypto.randomBytes)(24).toString("hex"),
|
|
20294
20818
|
displayName: body.runtime.displayName ?? displayName
|
|
20295
20819
|
});
|
|
20296
|
-
console.info("[
|
|
20820
|
+
console.info("[alan-agent] Runtime registered", {
|
|
20297
20821
|
runtimeId: body.runtime.id,
|
|
20298
20822
|
configPath: getConfigPath()
|
|
20299
20823
|
});
|
|
@@ -20308,14 +20832,14 @@ async function startDaemon(args) {
|
|
|
20308
20832
|
const endpointProfile = stored.endpointProfile ?? resolveEndpointProfile(args);
|
|
20309
20833
|
const daemonSessionId = (0, import_node_crypto.randomUUID)();
|
|
20310
20834
|
let heartbeatSeq = 0;
|
|
20311
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.
|
|
20312
|
-
const runtimeToken = argValue(args, "--token") ?? process.env.
|
|
20313
|
-
const wsUrlOverride = argValue(args, "--ws-url") ?? process.env.
|
|
20835
|
+
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
20836
|
+
const runtimeToken = argValue(args, "--token") ?? process.env.ALAN_RUNTIME_TOKEN ?? stored.runtimeToken;
|
|
20837
|
+
const wsUrlOverride = argValue(args, "--ws-url") ?? process.env.ALAN_WS_URL;
|
|
20314
20838
|
const endpointMismatch = wsUrlOverride ? null : checkStoredEndpointConsistency(stored, readEndpointDefaults());
|
|
20315
20839
|
let wsUrl = wsUrlOverride ?? stored.wsUrl;
|
|
20316
20840
|
if (endpointMismatch && stored.wsUrl && stored.wsUrl !== endpointMismatch.expectedWsUrl) {
|
|
20317
20841
|
console.warn(
|
|
20318
|
-
"[
|
|
20842
|
+
"[alan-agent] Stored config does not match its endpoint profile \u2014 connecting with the profile endpoints instead. Re-run setup to repair the stored config.",
|
|
20319
20843
|
{ configPath: getConfigPath(), detail: endpointMismatch.detail }
|
|
20320
20844
|
);
|
|
20321
20845
|
wsUrl = endpointMismatch.expectedWsUrl;
|
|
@@ -20329,6 +20853,8 @@ async function startDaemon(args) {
|
|
|
20329
20853
|
}
|
|
20330
20854
|
const activeAgents = /* @__PURE__ */ new Map();
|
|
20331
20855
|
let disconnectedSinceMs = null;
|
|
20856
|
+
let lastConnectError = null;
|
|
20857
|
+
let lastConnectErrorCode = null;
|
|
20332
20858
|
const pendingDeliveryLost = [];
|
|
20333
20859
|
const currentCapabilities = () => discoverCapabilities();
|
|
20334
20860
|
const currentMetadata = () => collectRuntimeMetadataWithProviderLimits();
|
|
@@ -20397,9 +20923,11 @@ async function startDaemon(args) {
|
|
|
20397
20923
|
}, VERSION_REFRESH_INTERVAL_MS);
|
|
20398
20924
|
socket.on("connect", () => {
|
|
20399
20925
|
disconnectedSinceMs = null;
|
|
20926
|
+
lastConnectError = null;
|
|
20927
|
+
lastConnectErrorCode = null;
|
|
20400
20928
|
setConnectedRuntimeSocket(socket);
|
|
20401
20929
|
pushLog(`connected runtime=${runtimeId}`);
|
|
20402
|
-
console.info("[
|
|
20930
|
+
console.info("[alan-agent] Daemon connected", { runtimeId, wsUrl, version: AGENT_VERSION });
|
|
20403
20931
|
flushPendingDeliveryLostEvents({
|
|
20404
20932
|
socket,
|
|
20405
20933
|
activeAgents,
|
|
@@ -20434,18 +20962,23 @@ async function startDaemon(args) {
|
|
|
20434
20962
|
let lastConnectErrorLogAt = 0;
|
|
20435
20963
|
socket.on("connect_error", (error) => {
|
|
20436
20964
|
if (!socket.connected && disconnectedSinceMs === null) disconnectedSinceMs = Date.now();
|
|
20437
|
-
|
|
20965
|
+
lastConnectError = error.message;
|
|
20966
|
+
const errorCode = error?.data?.code;
|
|
20967
|
+
lastConnectErrorCode = typeof errorCode === "string" ? errorCode : null;
|
|
20968
|
+
pushLog(
|
|
20969
|
+
`connect_error ${error.message}${lastConnectErrorCode ? ` code=${lastConnectErrorCode}` : ""}`
|
|
20970
|
+
);
|
|
20438
20971
|
const now = Date.now();
|
|
20439
20972
|
if (now - lastConnectErrorLogAt < 5e3) return;
|
|
20440
20973
|
lastConnectErrorLogAt = now;
|
|
20441
20974
|
const wsHint = isLocalEndpoint(wsUrl) ? ` Nothing is listening at ${wsUrl}. For local dev run setup with --profile local and start the stack (WS on ws://localhost:8401).` : ` Verify wsUrl in ${getConfigPath()} and that you can reach ${wsUrl}.`;
|
|
20442
|
-
console.warn("[
|
|
20975
|
+
console.warn("[alan-agent] Daemon connection error:", `${error.message}.${wsHint}`);
|
|
20443
20976
|
});
|
|
20444
20977
|
socket.on("disconnect", (reason) => {
|
|
20445
20978
|
setConnectedRuntimeSocket(null);
|
|
20446
20979
|
if (disconnectedSinceMs === null) disconnectedSinceMs = Date.now();
|
|
20447
20980
|
pushLog(`disconnected ${reason}`);
|
|
20448
|
-
console.info("[
|
|
20981
|
+
console.info("[alan-agent] Daemon disconnected:", reason);
|
|
20449
20982
|
for (const [runId, entry] of activeAgents.entries()) {
|
|
20450
20983
|
if (entry.stage === "completed" || entry.stage === "failed" || entry.stage === "aborted" || entry.stage === "delivery_lost") {
|
|
20451
20984
|
continue;
|
|
@@ -20548,7 +21081,7 @@ async function startDaemon(args) {
|
|
|
20548
21081
|
agentVersion: AGENT_VERSION
|
|
20549
21082
|
});
|
|
20550
21083
|
pushLog(`accepted run=${payload.runId}`);
|
|
20551
|
-
console.info("[
|
|
21084
|
+
console.info("[alan-agent] Agent run accepted", correlation);
|
|
20552
21085
|
if (!payload.worktreeInfo?.worktreePath && payload.sharedCheckout?.branchName) {
|
|
20553
21086
|
const repoTargets = resolveSharedCheckoutTargets({
|
|
20554
21087
|
cwd,
|
|
@@ -20578,14 +21111,14 @@ async function startDaemon(args) {
|
|
|
20578
21111
|
};
|
|
20579
21112
|
const releaseActiveRun = () => {
|
|
20580
21113
|
if (!activeAgents.delete(payload.runId)) return;
|
|
20581
|
-
console.info("[
|
|
21114
|
+
console.info("[alan-agent] Agent run finished", correlation);
|
|
20582
21115
|
};
|
|
20583
21116
|
if (backendKind) {
|
|
20584
21117
|
if (payload.aidenMcp) {
|
|
20585
|
-
const registration =
|
|
21118
|
+
const registration = ensureAlanMcpRegistered(backendKind, payload.aidenMcp);
|
|
20586
21119
|
if (registration.ok) {
|
|
20587
21120
|
pushLog(`mcp-registered run=${payload.runId} url=${payload.aidenMcp.url}`);
|
|
20588
|
-
console.info("[
|
|
21121
|
+
console.info("[alan-agent] Alan MCP registered for run", {
|
|
20589
21122
|
...correlation,
|
|
20590
21123
|
mcpUrl: payload.aidenMcp.url,
|
|
20591
21124
|
paths: registration.paths
|
|
@@ -20593,16 +21126,16 @@ async function startDaemon(args) {
|
|
|
20593
21126
|
} else {
|
|
20594
21127
|
pushLog(`mcp-registration-failed run=${payload.runId} ${registration.error}`);
|
|
20595
21128
|
console.warn(
|
|
20596
|
-
"[
|
|
21129
|
+
"[alan-agent] Alan MCP registration failed \u2014 Alan tools may be unavailable this run",
|
|
20597
21130
|
{ ...correlation, error: registration.error }
|
|
20598
21131
|
);
|
|
20599
21132
|
}
|
|
20600
21133
|
} else {
|
|
20601
|
-
const check2 =
|
|
21134
|
+
const check2 = verifyAlanMcpRegistered(backendKind);
|
|
20602
21135
|
if (!check2.ok) {
|
|
20603
21136
|
pushLog(`mcp-config-missing run=${payload.runId}`);
|
|
20604
21137
|
console.warn(
|
|
20605
|
-
"[
|
|
21138
|
+
"[alan-agent] Alan MCP config missing \u2014 Alan tools may be unavailable this run",
|
|
20606
21139
|
{ ...correlation, missing: check2.missing }
|
|
20607
21140
|
);
|
|
20608
21141
|
}
|
|
@@ -20668,7 +21201,7 @@ async function startDaemon(args) {
|
|
|
20668
21201
|
}).catch((error) => {
|
|
20669
21202
|
const message = error instanceof Error ? error.message : String(error);
|
|
20670
21203
|
pushLog(`failed run=${payload.runId} ${message}`);
|
|
20671
|
-
console.error("[
|
|
21204
|
+
console.error("[alan-agent] Agent run failed", {
|
|
20672
21205
|
...correlation,
|
|
20673
21206
|
error: message
|
|
20674
21207
|
});
|
|
@@ -20695,7 +21228,7 @@ async function startDaemon(args) {
|
|
|
20695
21228
|
abortActiveAgent(entry);
|
|
20696
21229
|
activeAgents.delete(payload.runId);
|
|
20697
21230
|
pushLog(`aborted run=${payload.runId}`);
|
|
20698
|
-
console.info("[
|
|
21231
|
+
console.info("[alan-agent] Agent run aborted", {
|
|
20699
21232
|
runId: payload.runId,
|
|
20700
21233
|
agentVersion: AGENT_VERSION
|
|
20701
21234
|
});
|
|
@@ -20801,6 +21334,8 @@ async function startDaemon(args) {
|
|
|
20801
21334
|
staleRunCount: runnerSummary.staleRunCount,
|
|
20802
21335
|
lastRunActivityAtMs: runnerSummary.lastRunActivityAtMs,
|
|
20803
21336
|
disconnectedSinceMs: runnerSummary.disconnectedSinceMs,
|
|
21337
|
+
lastConnectError,
|
|
21338
|
+
lastConnectErrorCode,
|
|
20804
21339
|
daemonSessionId,
|
|
20805
21340
|
heartbeatSeq,
|
|
20806
21341
|
endpointProfile,
|
|
@@ -20827,6 +21362,15 @@ async function startDaemon(args) {
|
|
|
20827
21362
|
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
20828
21363
|
return;
|
|
20829
21364
|
}
|
|
21365
|
+
if (req.method === "POST" && req.url === "/reconnect") {
|
|
21366
|
+
pushLog("reconnect requested via local API");
|
|
21367
|
+
lastConnectError = null;
|
|
21368
|
+
lastConnectErrorCode = null;
|
|
21369
|
+
socket.disconnect();
|
|
21370
|
+
socket.connect();
|
|
21371
|
+
res.writeHead(200, { "content-type": "application/json" }).end(JSON.stringify({ ok: true }));
|
|
21372
|
+
return;
|
|
21373
|
+
}
|
|
20830
21374
|
if (req.method === "POST" && req.url?.startsWith("/stop_run")) {
|
|
20831
21375
|
const runId = new URL(req.url, "http://127.0.0.1").searchParams.get("runId");
|
|
20832
21376
|
if (!runId) {
|
|
@@ -20881,7 +21425,7 @@ async function startDaemon(args) {
|
|
|
20881
21425
|
clearInterval(versionRefresh);
|
|
20882
21426
|
socket.disconnect();
|
|
20883
21427
|
throw new Error(
|
|
20884
|
-
`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit
|
|
21428
|
+
`${describeOccupiedLocalDaemon(localApiPort)} The token in ${getConfigPath()} no longer matches the running daemon; quit Alan desktop or run setup again.`
|
|
20885
21429
|
);
|
|
20886
21430
|
}
|
|
20887
21431
|
await new Promise((resolve3, reject) => {
|
|
@@ -20911,7 +21455,7 @@ async function startDaemon(args) {
|
|
|
20911
21455
|
const address = localServer.address();
|
|
20912
21456
|
if (address && typeof address === "object") localApiPort = address.port;
|
|
20913
21457
|
pushLog(`local_api listening port=${localApiPort}`);
|
|
20914
|
-
console.info("[
|
|
21458
|
+
console.info("[alan-agent] Local daemon API listening", { port: localApiPort });
|
|
20915
21459
|
resolve3();
|
|
20916
21460
|
});
|
|
20917
21461
|
});
|
|
@@ -20953,11 +21497,11 @@ async function stopDaemon(args) {
|
|
|
20953
21497
|
const probe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
20954
21498
|
if (probe.state === "listening_unauthorized") {
|
|
20955
21499
|
throw new Error(
|
|
20956
|
-
`Daemon is listening on port ${localApiPort} but rejected this config token. Quit
|
|
21500
|
+
`Daemon is listening on port ${localApiPort} but rejected this config token. Quit Alan desktop or rerun setup/login to refresh config.`
|
|
20957
21501
|
);
|
|
20958
21502
|
}
|
|
20959
21503
|
if (probe.state === "absent") {
|
|
20960
|
-
console.info("[
|
|
21504
|
+
console.info("[alan-agent] No daemon listening", { port: localApiPort });
|
|
20961
21505
|
return;
|
|
20962
21506
|
}
|
|
20963
21507
|
const response = await fetch(`http://127.0.0.1:${localApiPort}/shutdown`, {
|
|
@@ -20968,7 +21512,7 @@ async function stopDaemon(args) {
|
|
|
20968
21512
|
if (!response.ok) {
|
|
20969
21513
|
throw new Error(`Failed to stop daemon: HTTP ${response.status}`);
|
|
20970
21514
|
}
|
|
20971
|
-
console.info("[
|
|
21515
|
+
console.info("[alan-agent] Daemon stopped", {
|
|
20972
21516
|
port: localApiPort,
|
|
20973
21517
|
runtimeId: probe.status.runtimeId
|
|
20974
21518
|
});
|
|
@@ -20980,18 +21524,18 @@ async function runDaemon(args) {
|
|
|
20980
21524
|
const localApiToken = stored.localApiToken;
|
|
20981
21525
|
const existingProbe = await probeLocalDaemonEndpoint(localApiPort, localApiToken);
|
|
20982
21526
|
if (existingProbe.state === "running") {
|
|
20983
|
-
console.info("[
|
|
21527
|
+
console.info("[alan-agent] Daemon already running", {
|
|
20984
21528
|
port: localApiPort,
|
|
20985
21529
|
runtimeId: existingProbe.status.runtimeId,
|
|
20986
21530
|
connected: existingProbe.status.connected,
|
|
20987
|
-
hint: "If
|
|
21531
|
+
hint: "If Alan desktop is open, it manages the daemon automatically."
|
|
20988
21532
|
});
|
|
20989
21533
|
return;
|
|
20990
21534
|
}
|
|
20991
21535
|
if (existingProbe.state === "listening_unauthorized") {
|
|
20992
|
-
console.info("[
|
|
21536
|
+
console.info("[alan-agent] Daemon already running", {
|
|
20993
21537
|
port: localApiPort,
|
|
20994
|
-
hint: `
|
|
21538
|
+
hint: `Alan desktop is using a different local API token than ${getConfigPath()}. Quit the desktop app or rerun setup if you need a fresh daemon.`
|
|
20995
21539
|
});
|
|
20996
21540
|
return;
|
|
20997
21541
|
}
|
|
@@ -21008,11 +21552,11 @@ async function runDaemon(args) {
|
|
|
21008
21552
|
async function rotateRuntimeToken(args) {
|
|
21009
21553
|
bindConfigPath(args);
|
|
21010
21554
|
const stored = readConfig();
|
|
21011
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.
|
|
21012
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.
|
|
21013
|
-
const token = argValue(args, "--token") ?? process.env.
|
|
21555
|
+
const apiUrl = argValue(args, "--api-url") ?? process.env.ALAN_API_URL ?? stored.apiUrl;
|
|
21556
|
+
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
21557
|
+
const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
|
|
21014
21558
|
if (!apiUrl || !runtimeId || !token) {
|
|
21015
|
-
throw new Error("token rotate requires configured api/runtime plus --token <
|
|
21559
|
+
throw new Error("token rotate requires configured api/runtime plus --token <alan PAT>");
|
|
21016
21560
|
}
|
|
21017
21561
|
const response = await fetch(`${apiUrl}/public/runtimes/${runtimeId}/tokens/rotate`, {
|
|
21018
21562
|
method: "POST",
|
|
@@ -21023,16 +21567,16 @@ async function rotateRuntimeToken(args) {
|
|
|
21023
21567
|
}
|
|
21024
21568
|
const body = await response.json();
|
|
21025
21569
|
writeConfig({ ...stored, apiUrl, runtimeId, runtimeToken: body.runtimeToken });
|
|
21026
|
-
console.info("[
|
|
21570
|
+
console.info("[alan-agent] Runtime token rotated", { runtimeId });
|
|
21027
21571
|
}
|
|
21028
21572
|
async function revokeRuntimeToken(args) {
|
|
21029
21573
|
bindConfigPath(args);
|
|
21030
21574
|
const stored = readConfig();
|
|
21031
|
-
const apiUrl = argValue(args, "--api-url") ?? process.env.
|
|
21032
|
-
const runtimeId = argValue(args, "--runtime-id") ?? process.env.
|
|
21033
|
-
const token = argValue(args, "--token") ?? process.env.
|
|
21575
|
+
const apiUrl = argValue(args, "--api-url") ?? process.env.ALAN_API_URL ?? stored.apiUrl;
|
|
21576
|
+
const runtimeId = argValue(args, "--runtime-id") ?? process.env.ALAN_RUNTIME_ID ?? stored.runtimeId;
|
|
21577
|
+
const token = argValue(args, "--token") ?? process.env.ALAN_SETUP_TOKEN;
|
|
21034
21578
|
if (!apiUrl || !runtimeId || !token) {
|
|
21035
|
-
throw new Error("token revoke requires configured api/runtime plus --token <
|
|
21579
|
+
throw new Error("token revoke requires configured api/runtime plus --token <alan PAT>");
|
|
21036
21580
|
}
|
|
21037
21581
|
const response = await fetch(`${apiUrl}/public/runtimes/${runtimeId}/tokens/revoke`, {
|
|
21038
21582
|
method: "POST",
|
|
@@ -21042,12 +21586,12 @@ async function revokeRuntimeToken(args) {
|
|
|
21042
21586
|
throw new Error(`runtime token revoke failed: ${response.status} ${await response.text()}`);
|
|
21043
21587
|
}
|
|
21044
21588
|
removeConfig();
|
|
21045
|
-
console.info("[
|
|
21589
|
+
console.info("[alan-agent] Runtime token revoked", { runtimeId });
|
|
21046
21590
|
}
|
|
21047
21591
|
function logoutDaemon(args = []) {
|
|
21048
21592
|
bindConfigPath(args);
|
|
21049
21593
|
removeConfig();
|
|
21050
|
-
console.info("[
|
|
21594
|
+
console.info("[alan-agent] Runtime config removed", { configPath: getConfigPath() });
|
|
21051
21595
|
}
|
|
21052
21596
|
async function printStatus(args = []) {
|
|
21053
21597
|
bindConfigPath(args);
|
|
@@ -21075,7 +21619,7 @@ async function printStatus(args = []) {
|
|
|
21075
21619
|
localDaemonRunning,
|
|
21076
21620
|
hasLocalApiToken: Boolean(config.localApiToken),
|
|
21077
21621
|
configPath: getConfigPath(),
|
|
21078
|
-
note: configured ? localDaemonRunning ? "Local daemon is running. Stop it with:
|
|
21622
|
+
note: configured ? localDaemonRunning ? "Local daemon is running. Stop it with: alan-agent stop" : "Durable runtime config is present. Start it with: alan-agent daemon" : "No durable runtime configured. Cloud sandbox sessions are launched by Alan with session env vars and do not appear in this local config.",
|
|
21079
21623
|
warnings
|
|
21080
21624
|
},
|
|
21081
21625
|
null,
|
|
@@ -21166,7 +21710,7 @@ var AgentLifecycleLogger = class _AgentLifecycleLogger {
|
|
|
21166
21710
|
const record = omitUndefined({
|
|
21167
21711
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21168
21712
|
level,
|
|
21169
|
-
component: "
|
|
21713
|
+
component: "agent-manager",
|
|
21170
21714
|
event,
|
|
21171
21715
|
...this.context,
|
|
21172
21716
|
...normalizeFields(fields)
|
|
@@ -21176,12 +21720,12 @@ var AgentLifecycleLogger = class _AgentLifecycleLogger {
|
|
|
21176
21720
|
};
|
|
21177
21721
|
function lifecycleContextFromEnv(env) {
|
|
21178
21722
|
return normalizeContext({
|
|
21179
|
-
taskId: env.
|
|
21180
|
-
conversationId: env.
|
|
21181
|
-
runId: env.
|
|
21182
|
-
sandboxId: env.
|
|
21183
|
-
provider: env.
|
|
21184
|
-
backendKind: env.
|
|
21723
|
+
taskId: env.ALAN_TASK_ID,
|
|
21724
|
+
conversationId: env.ALAN_SESSION_ID,
|
|
21725
|
+
runId: env.ALAN_RUN_ID,
|
|
21726
|
+
sandboxId: env.ALAN_SANDBOX_ID,
|
|
21727
|
+
provider: env.ALAN_SANDBOX_PROVIDER,
|
|
21728
|
+
backendKind: env.ALAN_BACKEND_KIND,
|
|
21185
21729
|
agentVersion: AGENT_VERSION
|
|
21186
21730
|
});
|
|
21187
21731
|
}
|
|
@@ -21370,11 +21914,41 @@ var WebPresenter = class {
|
|
|
21370
21914
|
}
|
|
21371
21915
|
};
|
|
21372
21916
|
|
|
21917
|
+
// src/ws-client.ts
|
|
21918
|
+
var import_node_crypto2 = require("crypto");
|
|
21919
|
+
|
|
21920
|
+
// ../shared/dist/agent-liveness.js
|
|
21921
|
+
var AGENT_LIVENESS_PROTOCOL_VERSION = 1;
|
|
21922
|
+
var AGENT_HEARTBEAT_INTERVAL_MS = 15e3;
|
|
21923
|
+
var agentRunStateSchema = external_exports.object({
|
|
21924
|
+
/** True when no run is currently executing in this agent process. */
|
|
21925
|
+
idle: external_exports.boolean(),
|
|
21926
|
+
/** Run ids currently executing (cloud sandbox agent runs one at a time, but keep it a list). */
|
|
21927
|
+
activeRunIds: external_exports.array(external_exports.string()).default([])
|
|
21928
|
+
});
|
|
21929
|
+
var agentHeartbeatSchema = external_exports.object({
|
|
21930
|
+
/** Stable id for this agent OS process; changes only on a fresh agent start. */
|
|
21931
|
+
agentSessionId: external_exports.string().min(1),
|
|
21932
|
+
/** Monotonic per-process sequence number; advances on every heartbeat. */
|
|
21933
|
+
seq: external_exports.number().int().nonnegative(),
|
|
21934
|
+
/** Capability marker — presence is what distinguishes a new agent from a legacy one. */
|
|
21935
|
+
protocolVersion: external_exports.number().int().positive(),
|
|
21936
|
+
runState: agentRunStateSchema,
|
|
21937
|
+
agentVersion: external_exports.string().optional()
|
|
21938
|
+
});
|
|
21939
|
+
var agentProbeAckSchema = external_exports.object({
|
|
21940
|
+
agentSessionId: external_exports.string().min(1),
|
|
21941
|
+
seq: external_exports.number().int().nonnegative().optional(),
|
|
21942
|
+
protocolVersion: external_exports.number().int().positive().optional()
|
|
21943
|
+
});
|
|
21944
|
+
|
|
21373
21945
|
// src/ws-client.ts
|
|
21374
21946
|
var WSClient = class {
|
|
21375
21947
|
constructor(wsUrl, sessionId, taskId, token, callbacks, agentVersion, lifecycle) {
|
|
21376
21948
|
this.lifecycle = lifecycle;
|
|
21377
21949
|
this.lifecycle?.info("sandbox_agent_ws_socket_create", { wsUrl });
|
|
21950
|
+
this.agentVersion = agentVersion ?? "";
|
|
21951
|
+
this.getRunState = callbacks.getRunState;
|
|
21378
21952
|
this.socket = lookup(wsUrl, {
|
|
21379
21953
|
query: { type: "agent", sessionId, taskId, agentVersion: agentVersion ?? "" },
|
|
21380
21954
|
auth: { token },
|
|
@@ -21390,6 +21964,48 @@ var WSClient = class {
|
|
|
21390
21964
|
this.setupListeners(callbacks);
|
|
21391
21965
|
}
|
|
21392
21966
|
socket;
|
|
21967
|
+
/** Stable id for this agent process; fences stale heartbeats server-side. */
|
|
21968
|
+
agentSessionId = (0, import_node_crypto2.randomUUID)();
|
|
21969
|
+
/** Monotonic heartbeat sequence — advances on every hello + heartbeat. */
|
|
21970
|
+
heartbeatSeq = 0;
|
|
21971
|
+
heartbeatTimer = null;
|
|
21972
|
+
agentVersion;
|
|
21973
|
+
getRunState;
|
|
21974
|
+
currentRunState() {
|
|
21975
|
+
try {
|
|
21976
|
+
return this.getRunState?.() ?? { idle: true, activeRunIds: [] };
|
|
21977
|
+
} catch {
|
|
21978
|
+
return { idle: true, activeRunIds: [] };
|
|
21979
|
+
}
|
|
21980
|
+
}
|
|
21981
|
+
buildHeartbeat() {
|
|
21982
|
+
this.heartbeatSeq += 1;
|
|
21983
|
+
return {
|
|
21984
|
+
agentSessionId: this.agentSessionId,
|
|
21985
|
+
seq: this.heartbeatSeq,
|
|
21986
|
+
protocolVersion: AGENT_LIVENESS_PROTOCOL_VERSION,
|
|
21987
|
+
runState: this.currentRunState(),
|
|
21988
|
+
agentVersion: this.agentVersion || void 0
|
|
21989
|
+
};
|
|
21990
|
+
}
|
|
21991
|
+
/** Emit `agent.hello` on (re)connect and (re)start the heartbeat interval. */
|
|
21992
|
+
startHeartbeat() {
|
|
21993
|
+
if (this.socket.connected) {
|
|
21994
|
+
this.socket.emit("agent.hello", this.buildHeartbeat());
|
|
21995
|
+
}
|
|
21996
|
+
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
|
|
21997
|
+
this.heartbeatTimer = setInterval(() => {
|
|
21998
|
+
if (!this.socket.connected) return;
|
|
21999
|
+
this.socket.emit("agent.heartbeat", this.buildHeartbeat());
|
|
22000
|
+
}, AGENT_HEARTBEAT_INTERVAL_MS);
|
|
22001
|
+
this.heartbeatTimer.unref?.();
|
|
22002
|
+
}
|
|
22003
|
+
stopHeartbeat() {
|
|
22004
|
+
if (this.heartbeatTimer) {
|
|
22005
|
+
clearInterval(this.heartbeatTimer);
|
|
22006
|
+
this.heartbeatTimer = null;
|
|
22007
|
+
}
|
|
22008
|
+
}
|
|
21393
22009
|
emitEvent(event) {
|
|
21394
22010
|
this.socket.emit("agent_event", event);
|
|
21395
22011
|
}
|
|
@@ -21421,17 +22037,27 @@ var WSClient = class {
|
|
|
21421
22037
|
}
|
|
21422
22038
|
close() {
|
|
21423
22039
|
this.lifecycle?.info("sandbox_agent_ws_close_requested");
|
|
22040
|
+
this.stopHeartbeat();
|
|
21424
22041
|
this.socket.disconnect();
|
|
21425
22042
|
}
|
|
21426
22043
|
setupListeners(callbacks) {
|
|
21427
22044
|
this.socket.on("connect", () => {
|
|
21428
22045
|
this.lifecycle?.info("sandbox_agent_ws_socket_connected", { socketId: this.socket.id });
|
|
22046
|
+
this.startHeartbeat();
|
|
21429
22047
|
callbacks.onConnect?.();
|
|
21430
22048
|
});
|
|
21431
22049
|
this.socket.on("disconnect", (reason) => {
|
|
21432
22050
|
this.lifecycle?.info("sandbox_agent_ws_socket_disconnected", { reason });
|
|
22051
|
+
this.stopHeartbeat();
|
|
21433
22052
|
callbacks.onDisconnect?.(reason);
|
|
21434
22053
|
});
|
|
22054
|
+
this.socket.on("agent.probe", (_data, ack) => {
|
|
22055
|
+
ack?.({
|
|
22056
|
+
agentSessionId: this.agentSessionId,
|
|
22057
|
+
seq: this.heartbeatSeq,
|
|
22058
|
+
protocolVersion: AGENT_LIVENESS_PROTOCOL_VERSION
|
|
22059
|
+
});
|
|
22060
|
+
});
|
|
21435
22061
|
this.socket.on("connect_error", (error) => {
|
|
21436
22062
|
this.lifecycle?.warn("sandbox_agent_ws_socket_connect_error", {
|
|
21437
22063
|
error: error instanceof Error ? error : new Error(String(error))
|
|
@@ -21596,7 +22222,9 @@ async function runSandbox(config) {
|
|
|
21596
22222
|
}
|
|
21597
22223
|
},
|
|
21598
22224
|
onConnect: () => lifecycle.info("sandbox_agent_ws_connected"),
|
|
21599
|
-
onDisconnect: (reason) => lifecycle.info("sandbox_agent_ws_disconnected", { reason })
|
|
22225
|
+
onDisconnect: (reason) => lifecycle.info("sandbox_agent_ws_disconnected", { reason }),
|
|
22226
|
+
// Liveness heartbeat run state: a run is active while CoreAgent is executing.
|
|
22227
|
+
getRunState: () => currentAgent ? { idle: false, activeRunIds: currentRunId ? [currentRunId] : [] } : { idle: true, activeRunIds: [] }
|
|
21600
22228
|
},
|
|
21601
22229
|
AGENT_VERSION,
|
|
21602
22230
|
lifecycle
|
|
@@ -21619,7 +22247,7 @@ async function runSandbox(config) {
|
|
|
21619
22247
|
let activeContextWindow = config.contextWindow;
|
|
21620
22248
|
let activeEffortLevel = config.effortLevel;
|
|
21621
22249
|
let activeMode = config.mode || "agent";
|
|
21622
|
-
let
|
|
22250
|
+
let currentAlanMcp;
|
|
21623
22251
|
while (!stopped) {
|
|
21624
22252
|
if (currentTask?.trim()) {
|
|
21625
22253
|
const agent = new CoreAgent(presenter, {
|
|
@@ -21653,8 +22281,8 @@ async function runSandbox(config) {
|
|
|
21653
22281
|
model: activeModel,
|
|
21654
22282
|
maxIterations: currentMaxIterations
|
|
21655
22283
|
});
|
|
21656
|
-
if (
|
|
21657
|
-
const registration =
|
|
22284
|
+
if (currentAlanMcp) {
|
|
22285
|
+
const registration = ensureAlanMcpRegistered(activeBackendKind, currentAlanMcp);
|
|
21658
22286
|
if (registration.ok) {
|
|
21659
22287
|
lifecycle.info("sandbox_agent_mcp_registered", {
|
|
21660
22288
|
backendKind: activeBackendKind,
|
|
@@ -21666,18 +22294,18 @@ async function runSandbox(config) {
|
|
|
21666
22294
|
error: registration.error
|
|
21667
22295
|
});
|
|
21668
22296
|
presenter.onLog(
|
|
21669
|
-
`[sandbox]
|
|
22297
|
+
`[sandbox] Alan MCP registration failed \u2014 Alan tools may be unavailable this run: ${registration.error}`
|
|
21670
22298
|
);
|
|
21671
22299
|
}
|
|
21672
22300
|
} else {
|
|
21673
|
-
const check2 =
|
|
22301
|
+
const check2 = verifyAlanMcpRegistered(activeBackendKind);
|
|
21674
22302
|
if (!check2.ok) {
|
|
21675
22303
|
lifecycle.warn("sandbox_agent_mcp_config_missing", {
|
|
21676
22304
|
backendKind: activeBackendKind,
|
|
21677
22305
|
missing: check2.missing
|
|
21678
22306
|
});
|
|
21679
22307
|
presenter.onLog(
|
|
21680
|
-
`[sandbox]
|
|
22308
|
+
`[sandbox] Alan MCP config missing for ${activeBackendKind} \u2014 Alan tools may be unavailable this run`
|
|
21681
22309
|
);
|
|
21682
22310
|
}
|
|
21683
22311
|
}
|
|
@@ -21789,8 +22417,8 @@ async function runSandbox(config) {
|
|
|
21789
22417
|
if (nextPayload.mode) {
|
|
21790
22418
|
activeMode = nextPayload.mode;
|
|
21791
22419
|
}
|
|
21792
|
-
if (nextPayload.
|
|
21793
|
-
|
|
22420
|
+
if (nextPayload.alanMcp !== void 0) {
|
|
22421
|
+
currentAlanMcp = nextPayload.alanMcp;
|
|
21794
22422
|
}
|
|
21795
22423
|
if (nextPayload.taskMeta !== void 0) {
|
|
21796
22424
|
currentTaskMeta = nextPayload.taskMeta;
|
|
@@ -21839,26 +22467,26 @@ async function runSandbox(config) {
|
|
|
21839
22467
|
|
|
21840
22468
|
// src/index.ts
|
|
21841
22469
|
async function runSessionFromEnv() {
|
|
21842
|
-
const wsUrl = process.env.
|
|
21843
|
-
const sessionId = process.env.
|
|
21844
|
-
const taskId = process.env.
|
|
21845
|
-
const sessionToken = process.env.
|
|
21846
|
-
const task = process.env.
|
|
21847
|
-
const teamId = process.env.
|
|
21848
|
-
const projectPath = process.env.
|
|
21849
|
-
const backendKind = process.env.
|
|
21850
|
-
const agentId = process.env.
|
|
21851
|
-
const agentPrompt = process.env.
|
|
21852
|
-
const mode = process.env.
|
|
21853
|
-
const model = process.env.
|
|
21854
|
-
const contextWindow = process.env.
|
|
21855
|
-
const effortLevel = process.env.
|
|
21856
|
-
const providerSessionId = process.env.
|
|
21857
|
-
const workflowId = process.env.
|
|
21858
|
-
const workflowExecutionId = process.env.
|
|
21859
|
-
const sandboxId = process.env.
|
|
21860
|
-
const provider = process.env.
|
|
21861
|
-
const runId = process.env.
|
|
22470
|
+
const wsUrl = process.env.ALAN_WS_URL;
|
|
22471
|
+
const sessionId = process.env.ALAN_SESSION_ID;
|
|
22472
|
+
const taskId = process.env.ALAN_TASK_ID;
|
|
22473
|
+
const sessionToken = process.env.ALAN_SESSION_TOKEN;
|
|
22474
|
+
const task = process.env.ALAN_TASK;
|
|
22475
|
+
const teamId = process.env.ALAN_TEAM_ID || void 0;
|
|
22476
|
+
const projectPath = process.env.ALAN_PROJECT_PATH || "/home/user/workspace";
|
|
22477
|
+
const backendKind = process.env.ALAN_BACKEND_KIND || "claude_cli";
|
|
22478
|
+
const agentId = process.env.ALAN_AGENT_ID || void 0;
|
|
22479
|
+
const agentPrompt = process.env.ALAN_AGENT_PROMPT || void 0;
|
|
22480
|
+
const mode = process.env.ALAN_MODE || "agent";
|
|
22481
|
+
const model = process.env.ALAN_MODEL;
|
|
22482
|
+
const contextWindow = process.env.ALAN_CONTEXT_WINDOW || void 0;
|
|
22483
|
+
const effortLevel = process.env.ALAN_EFFORT_LEVEL || void 0;
|
|
22484
|
+
const providerSessionId = process.env.ALAN_PROVIDER_SESSION_ID || void 0;
|
|
22485
|
+
const workflowId = process.env.ALAN_WORKFLOW_ID || void 0;
|
|
22486
|
+
const workflowExecutionId = process.env.ALAN_WORKFLOW_EXECUTION_ID || void 0;
|
|
22487
|
+
const sandboxId = process.env.ALAN_SANDBOX_ID || void 0;
|
|
22488
|
+
const provider = process.env.ALAN_SANDBOX_PROVIDER || void 0;
|
|
22489
|
+
const runId = process.env.ALAN_RUN_ID || void 0;
|
|
21862
22490
|
const lifecycle = new AgentLifecycleLogger(lifecycleContextFromEnv(process.env));
|
|
21863
22491
|
if (!wsUrl || !sessionId || !taskId || !sessionToken || task == null) {
|
|
21864
22492
|
lifecycle.error("sandbox_agent_bootstrap_missing_env", {
|
|
@@ -21869,7 +22497,7 @@ async function runSessionFromEnv() {
|
|
|
21869
22497
|
hasTask: task != null
|
|
21870
22498
|
});
|
|
21871
22499
|
throw new Error(
|
|
21872
|
-
"run-session is an internal
|
|
22500
|
+
"run-session is an internal Alan sandbox/session command and must be launched by Alan with session env vars. Missing required env vars: ALAN_WS_URL, ALAN_SESSION_ID, ALAN_TASK_ID, ALAN_SESSION_TOKEN, ALAN_TASK"
|
|
21873
22501
|
);
|
|
21874
22502
|
}
|
|
21875
22503
|
lifecycle.info("sandbox_agent_process_start", {
|