@buildaharness/aielia 0.3.3 → 0.3.5

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.
Files changed (111) hide show
  1. package/README.md +63 -20
  2. package/dist/PlanGraphPane.d.ts +19 -0
  3. package/dist/_memory-core-generated.d.ts +1764 -0
  4. package/dist/agent-loop.d.ts +31 -8
  5. package/dist/ambiguity-guard-flag.d.ts +12 -0
  6. package/dist/ambiguity-guard.d.ts +20 -0
  7. package/dist/answer-claim.d.ts +13 -1
  8. package/dist/ask-clarification-service.d.ts +12 -1
  9. package/dist/ask-response-format.d.ts +6 -0
  10. package/dist/assistant-session.d.ts +16 -5
  11. package/dist/assistant-source.d.ts +7 -0
  12. package/dist/assistant-types.d.ts +25 -1
  13. package/dist/assistant.d.ts +166 -5
  14. package/dist/benchmark-injected-failure.d.ts +6 -0
  15. package/dist/bin-CjKpxDxl.js +4836 -0
  16. package/dist/bin-CjKpxDxl.js.map +1 -0
  17. package/dist/cli-session.d.ts +26 -1
  18. package/dist/cli.d.ts +20 -2
  19. package/dist/cli.js +1 -1
  20. package/dist/code-plan-templates.d.ts +21 -0
  21. package/dist/config.d.ts +51 -0
  22. package/dist/constraint-check.d.ts +35 -0
  23. package/dist/contradiction-checker.d.ts +7 -0
  24. package/dist/decomposed-answer.d.ts +13 -0
  25. package/dist/decomposition-classifier.d.ts +2 -0
  26. package/dist/doctor-checks.d.ts +3 -0
  27. package/dist/episodic-digest.d.ts +89 -0
  28. package/dist/fact-extraction.d.ts +39 -0
  29. package/dist/file-tools-mcp-server.mjs +138 -12
  30. package/dist/file-tools.d.ts +4 -0
  31. package/dist/first-run.d.ts +3 -2
  32. package/dist/goal-graph-flag.d.ts +44 -0
  33. package/dist/goal-graph-reconcile.d.ts +55 -0
  34. package/dist/goal-graph-service.d.ts +48 -0
  35. package/dist/goal-graph-store.d.ts +485 -0
  36. package/dist/goal-graph-suggest-flag.d.ts +34 -0
  37. package/dist/goal-identity-matcher.d.ts +21 -0
  38. package/dist/goal-thread-identity.d.ts +32 -0
  39. package/dist/goal-thread-scheduler.d.ts +78 -0
  40. package/dist/grounding-check.d.ts +33 -0
  41. package/dist/harness-bridge.d.ts +75 -2
  42. package/dist/harness-token-budget.d.ts +9 -0
  43. package/dist/index.d.ts +30 -1
  44. package/dist/index.js +166 -80
  45. package/dist/index.js.map +1 -1
  46. package/dist/layer-policy-flag.d.ts +13 -0
  47. package/dist/layer-policy-wiring.d.ts +77 -0
  48. package/dist/layer-settings.d.ts +49 -0
  49. package/dist/layer-telemetry.d.ts +2 -0
  50. package/dist/lexical/lexical-mode.d.ts +29 -0
  51. package/dist/live-steering-channel.d.ts +31 -0
  52. package/dist/memory-consolidation.d.ts +80 -0
  53. package/dist/memory-governance.d.ts +30 -0
  54. package/dist/memory-reviewer.d.ts +114 -0
  55. package/dist/memory-service.d.ts +348 -9
  56. package/dist/model-json.d.ts +17 -0
  57. package/dist/next-step-context.d.ts +51 -0
  58. package/dist/next-step-proposer.d.ts +53 -0
  59. package/dist/plan-approval-service.d.ts +1 -1
  60. package/dist/plan-drafting-service.d.ts +1 -1
  61. package/dist/plan-graph-flag.d.ts +20 -0
  62. package/dist/plan-question.d.ts +60 -0
  63. package/dist/plan-service.d.ts +6 -2
  64. package/dist/plan-step-prompt.d.ts +15 -0
  65. package/dist/plan-store.d.ts +7 -1
  66. package/dist/plan-viz/normalize.d.ts +14 -0
  67. package/dist/plan-viz/plan-detail-lines.d.ts +3 -0
  68. package/dist/plan-viz/plan-layout.d.ts +48 -0
  69. package/dist/plan-viz/plan-nav.d.ts +29 -0
  70. package/dist/plan-viz/plan-raster.d.ts +36 -0
  71. package/dist/plan-viz/plan-routes.d.ts +6 -0
  72. package/dist/plan-viz/plan-snapshot.d.ts +73 -0
  73. package/dist/plan-viz/plan-viewport.d.ts +29 -0
  74. package/dist/plan-viz/plan-width.d.ts +15 -0
  75. package/dist/plan-viz/testkit/display-width.d.ts +12 -0
  76. package/dist/plan-viz/testkit/generators.d.ts +61 -0
  77. package/dist/plan-viz/testkit/grid-oracles.d.ts +38 -0
  78. package/dist/plan-viz/testkit/nav-oracles.d.ts +79 -0
  79. package/dist/plan-viz/testkit/types.d.ts +8 -0
  80. package/dist/plan-viz/types.d.ts +34 -0
  81. package/dist/provider-setup-LFsaTkpf.js +16606 -0
  82. package/dist/provider-setup-LFsaTkpf.js.map +1 -0
  83. package/dist/provider-setup.d.ts +51 -0
  84. package/dist/recall-tool.d.ts +43 -0
  85. package/dist/recovery-note.d.ts +22 -0
  86. package/dist/response-service.d.ts +28 -2
  87. package/dist/review-checker.d.ts +10 -4
  88. package/dist/reviewer-revision.d.ts +23 -0
  89. package/dist/risk-classifier.d.ts +7 -0
  90. package/dist/scope-urgency-classifier.d.ts +29 -0
  91. package/dist/scripted-llm-client.d.ts +26 -1
  92. package/dist/semantic-compaction.d.ts +15 -0
  93. package/dist/semantic-hypotheses.d.ts +40 -0
  94. package/dist/source-reliability.d.ts +71 -0
  95. package/dist/supervisor-flag.d.ts +20 -0
  96. package/dist/task-completion-check.d.ts +24 -0
  97. package/dist/tool-control-plane.d.ts +33 -1
  98. package/dist/tool-effect-class.d.ts +9 -0
  99. package/dist/transcript-compaction.d.ts +9 -0
  100. package/dist/{tui-app-CFfOP2DC.js → tui-app-BLR-PRFp.js} +321 -7
  101. package/dist/tui-app-BLR-PRFp.js.map +1 -0
  102. package/dist/tui-app.d.ts +38 -0
  103. package/dist/turn-intent-classifier.d.ts +75 -16
  104. package/dist/turn-interpreter.d.ts +25 -3
  105. package/dist/version.d.ts +1 -1
  106. package/package.json +7 -7
  107. package/dist/bin-Btzhiyzi.js +0 -2108
  108. package/dist/bin-Btzhiyzi.js.map +0 -1
  109. package/dist/cli-session-DnSc6Hhx.js +0 -8343
  110. package/dist/cli-session-DnSc6Hhx.js.map +0 -1
  111. package/dist/tui-app-CFfOP2DC.js.map +0 -1
@@ -0,0 +1,4836 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { createInterface } from "node:readline";
5
+ import { rename, stat, realpath, readdir, mkdir, unlink, writeFile, readFile } from "node:fs/promises";
6
+ import { existsSync, mkdirSync, writeFileSync, renameSync, rmSync, createWriteStream, chmodSync, readFileSync, realpathSync } from "node:fs";
7
+ import { homedir, tmpdir } from "node:os";
8
+ import { join, dirname, resolve } from "node:path";
9
+ import { fileURLToPath, pathToFileURL } from "node:url";
10
+ import { OPENROUTER_DEFAULT_MODEL, OPENAI_DEFAULT_MODEL, ANTHROPIC_DEFAULT_MODEL, InMemoryReminderStore, FileSystemAdapter, FileSystemExperienceStore, LLMClient, OpenAICompatibleLLMClient, OPENROUTER_EXTRA_HEADERS, OPENROUTER_BASE_URL, OPENAI_BASE_URL, AnthropicLLMClient } from "@buildaharness/runtime";
11
+ import { X as buildClaudePrompt, bH as stripJsonCodeFence, bm as recallToolEnabled, aX as lexicalOffEnvValue, a as ALREADY_STAGED_ACTION_TOOL, bG as stagedActionInput, bg as parseClaudeCliOutput, bI as stripMcpToolPrefix, b3 as memoryStatusChecks, bf as normalizePlanNodes, P as PROVIDER_SETUP, a3 as cleanApiKey, Z as checkApiKeyFormat, af as envOverridesFromProcessEnv, bp as resolveConfig, bE as shouldLaunchTuiApp, Q as applyLayerSettings, bC as sanitizeLayerChoices, bL as testApiKey, bO as validateConfig, c as ConfigValidationError, aF as formatSpendCapStatus, aS as isPlanGraphEnabled, z as PersonalAssistant, aP as isGoalGraphSuggestEnabled, aO as isGoalGraphEnabled, bl as planToSnapshot, V as braveSearch, a7 as createSmtpSender, a6 as createResendSender, aU as isQuitCommand, x as LiveSteeringChannel, aC as formatNextSteps, _ as classifyError, b6 as nodeToLayer, L as LAYER_DISPLAY_NAME, b5 as nodeDisplayName, ah as estimateCostUsd, ao as formatConfigListing, aN as isConfigKey, C as CONFIG_KEYS, E as ENV_VAR_FOR_CONFIG_KEY, bh as parseConfigValue, d as ConfigValueParseError, aq as formatDoctorReport, ap as formatCostSummary, as as formatGoalGraphState, aE as formatSearchResults, ax as formatMemoryHistory, aA as formatMemoryStatus, az as formatMemoryPendingOutcome, av as formatMemoryArchive, aI as formatUndoLogListing, aa as defaultExportFilename, aH as formatTranscriptMarkdown, aG as formatStatus, at as formatHelp, q as LAYER_ORDER, au as formatLayerListing, bP as withLayerChoice, w as LayerSettingError, ay as formatMemoryInjection, Y as buildWhyChain, s as LAYER_SHORT_CODE, ab as defaultMemoryExportFilename, aw as formatMemoryExport, b2 as memoryAuditLogEnabled, aB as formatMemorySummary } from "./provider-setup-LFsaTkpf.js";
12
+ import { spawn } from "node:child_process";
13
+ import { createServer, connect } from "node:net";
14
+ import { createRequire } from "node:module";
15
+ import { randomBytes, createHash } from "node:crypto";
16
+ import { Readable } from "node:stream";
17
+ import { pipeline } from "node:stream/promises";
18
+ function isEnoent$1(err) {
19
+ return (err == null ? void 0 : err.code) === "ENOENT";
20
+ }
21
+ function createNodeFsBackend() {
22
+ return {
23
+ async readTextFile(path) {
24
+ try {
25
+ return await readFile(path, "utf-8");
26
+ } catch (err) {
27
+ if (isEnoent$1(err)) return void 0;
28
+ throw err;
29
+ }
30
+ },
31
+ async writeTextFile(path, contents) {
32
+ await writeFile(path, contents, "utf-8");
33
+ },
34
+ async removeFile(path) {
35
+ try {
36
+ await unlink(path);
37
+ } catch (err) {
38
+ if (!isEnoent$1(err)) throw err;
39
+ }
40
+ },
41
+ async mkdir(path) {
42
+ await mkdir(path, { recursive: true });
43
+ },
44
+ async readDir(path) {
45
+ try {
46
+ return await readdir(path);
47
+ } catch (err) {
48
+ if (isEnoent$1(err)) return [];
49
+ throw err;
50
+ }
51
+ },
52
+ async realpath(path) {
53
+ return realpath(path);
54
+ },
55
+ async stat(path) {
56
+ try {
57
+ const info = await stat(path);
58
+ return { isDirectory: info.isDirectory(), size: info.size };
59
+ } catch (err) {
60
+ if (isEnoent$1(err)) return void 0;
61
+ throw err;
62
+ }
63
+ },
64
+ async rename(from, to) {
65
+ await rename(from, to);
66
+ }
67
+ };
68
+ }
69
+ const CLI_VERSION = "0.3.5";
70
+ function seaCacheDir(version, home = homedir()) {
71
+ return join(home, ".buildaharness", "personal-assistant", "sea-cache", version);
72
+ }
73
+ function extractAssetOnce(dir, fileName, read) {
74
+ const target = join(dir, fileName);
75
+ if (existsSync(target)) return target;
76
+ mkdirSync(dir, { recursive: true });
77
+ const tmp = `${target}.${process.pid}.tmp`;
78
+ const data = read();
79
+ writeFileSync(tmp, typeof data === "string" ? data : new Uint8Array(data));
80
+ renameSync(tmp, target);
81
+ return target;
82
+ }
83
+ const MCP_SERVER_FILE_NAME = "file-tools-mcp-server.mjs";
84
+ const MCP_SERVER_PATTERN_FILES = ["injection-patterns.json", "fact-markers.json", "risk-patterns.json"];
85
+ function loadSea() {
86
+ try {
87
+ const sea = createRequire(import.meta.url)("node:sea");
88
+ return sea.isSea() ? sea : null;
89
+ } catch {
90
+ return null;
91
+ }
92
+ }
93
+ function resolveMcpServerPath(seaOverride, home) {
94
+ const sea = loadSea();
95
+ if (sea) {
96
+ const dir = seaCacheDir(CLI_VERSION, home);
97
+ for (const name of MCP_SERVER_PATTERN_FILES) {
98
+ const key = `lexical/patterns/${name}`;
99
+ extractAssetOnce(join(dir, "lexical", "patterns"), name, () => sea.getRawAsset(key));
100
+ }
101
+ return extractAssetOnce(dir, MCP_SERVER_FILE_NAME, () => sea.getRawAsset(MCP_SERVER_FILE_NAME));
102
+ }
103
+ return fileURLToPath(new URL(MCP_SERVER_FILE_NAME, import.meta.url));
104
+ }
105
+ const EMPTY_MCP_CONFIG = JSON.stringify({ mcpServers: {} });
106
+ function invokeClaude(claudePath, args) {
107
+ return new Promise((resolvePromise, reject) => {
108
+ const proc = spawn(claudePath, args, { cwd: tmpdir(), stdio: ["ignore", "pipe", "pipe"] });
109
+ let stdout = "";
110
+ let stderr = "";
111
+ proc.stdout.on("data", (chunk) => {
112
+ stdout += chunk;
113
+ });
114
+ proc.stderr.on("data", (chunk) => {
115
+ stderr += chunk;
116
+ });
117
+ proc.on("error", reject);
118
+ proc.on("close", (code) => {
119
+ if (code !== 0) {
120
+ reject(new Error(stderr.trim() || `claude exited with code ${code}`));
121
+ return;
122
+ }
123
+ resolvePromise(parseClaudeCliOutput(stdout));
124
+ });
125
+ });
126
+ }
127
+ function invokeClaudeStreaming(claudePath, args, onToolStep) {
128
+ return new Promise((resolvePromise, reject) => {
129
+ const proc = spawn(claudePath, args, { cwd: tmpdir(), stdio: ["ignore", "pipe", "pipe"] });
130
+ let buffer = "";
131
+ let stderr = "";
132
+ let finalResultLine;
133
+ proc.stdout.on("data", (chunk) => {
134
+ var _a;
135
+ buffer += chunk.toString("utf-8");
136
+ let newlineIndex;
137
+ while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
138
+ const line = buffer.slice(0, newlineIndex);
139
+ buffer = buffer.slice(newlineIndex + 1);
140
+ if (!line.trim()) continue;
141
+ let event;
142
+ try {
143
+ event = JSON.parse(line);
144
+ } catch {
145
+ continue;
146
+ }
147
+ if (event.type === "assistant" && onToolStep) {
148
+ for (const block of ((_a = event.message) == null ? void 0 : _a.content) ?? []) {
149
+ if (block.type !== "tool_use") continue;
150
+ const toolUse = block;
151
+ onToolStep({ tool: stripMcpToolPrefix(toolUse.name), input: toolUse.input ?? {} });
152
+ }
153
+ } else if (event.type === "result") {
154
+ finalResultLine = line;
155
+ }
156
+ }
157
+ });
158
+ proc.stderr.on("data", (chunk) => {
159
+ stderr += chunk;
160
+ });
161
+ proc.on("error", reject);
162
+ proc.on("close", (code) => {
163
+ if (code !== 0) {
164
+ reject(new Error(stderr.trim() || `claude exited with code ${code}`));
165
+ return;
166
+ }
167
+ resolvePromise(parseClaudeCliOutput(finalResultLine ?? ""));
168
+ });
169
+ });
170
+ }
171
+ function startToolGateServer(onToolProposal, onToolResult, onToolExecute) {
172
+ return new Promise((resolvePromise, reject) => {
173
+ const server = createServer((socket) => {
174
+ let buffer = "";
175
+ socket.on("data", (chunk) => {
176
+ buffer += chunk.toString("utf-8");
177
+ let newlineIndex;
178
+ while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
179
+ const line = buffer.slice(0, newlineIndex);
180
+ buffer = buffer.slice(newlineIndex + 1);
181
+ if (!line.trim()) continue;
182
+ void (async (requestLine) => {
183
+ let request;
184
+ try {
185
+ request = JSON.parse(requestLine);
186
+ } catch {
187
+ socket.write(`${JSON.stringify({ decision: "allow" })}
188
+ `);
189
+ return;
190
+ }
191
+ if (request.kind === "execute") {
192
+ let reply = { handled: false };
193
+ try {
194
+ if (onToolExecute && typeof request.tool === "string") {
195
+ const text = await onToolExecute(request.tool, request.input ?? {});
196
+ if (text !== void 0) reply = { handled: true, text };
197
+ }
198
+ } catch (err) {
199
+ reply = { handled: true, error: err instanceof Error ? err.message : String(err) };
200
+ }
201
+ socket.write(`${JSON.stringify(reply)}
202
+ `);
203
+ return;
204
+ }
205
+ if (request.kind === "result") {
206
+ try {
207
+ if (onToolResult && typeof request.tool === "string" && typeof request.text === "string") {
208
+ await onToolResult(request.tool, request.input ?? {}, request.text, request.ok !== false, request.notFound === true);
209
+ }
210
+ } catch (err) {
211
+ console.error("claude-cli tool gate: onToolResult threw — ignoring:", err);
212
+ }
213
+ socket.write(`${JSON.stringify({ decision: "allow" })}
214
+ `);
215
+ return;
216
+ }
217
+ let decision;
218
+ try {
219
+ decision = onToolProposal && typeof request.tool === "string" ? await onToolProposal(request.tool, request.input ?? {}) : { decision: "allow" };
220
+ } catch (err) {
221
+ console.error("claude-cli tool gate: onToolProposal threw — failing open:", err);
222
+ decision = { decision: "allow" };
223
+ }
224
+ socket.write(`${JSON.stringify(decision)}
225
+ `);
226
+ })(line);
227
+ }
228
+ });
229
+ });
230
+ server.on("error", reject);
231
+ server.listen(0, "127.0.0.1", () => {
232
+ const address = server.address();
233
+ if (address === null || typeof address === "string") {
234
+ reject(new Error("tool gate server failed to bind to a loopback TCP port"));
235
+ return;
236
+ }
237
+ resolvePromise({ server, port: address.port });
238
+ });
239
+ });
240
+ }
241
+ class ClaudeCliLLMClient {
242
+ constructor(options = {}) {
243
+ __publicField(this, "claudePath");
244
+ __publicField(this, "fileTools");
245
+ __publicField(this, "remindersFile");
246
+ __publicField(this, "shellTools");
247
+ __publicField(this, "webTools");
248
+ __publicField(this, "actionTools");
249
+ __publicField(this, "model");
250
+ /**
251
+ * The concrete model id the last `claude -p` call actually reported running against (read from
252
+ * the response's own `model` field, not the CLI alias). `undefined` until a call resolves one.
253
+ * Plan A1 records this into the benchmark report so a published table can name the model.
254
+ */
255
+ __publicField(this, "resolvedModelId");
256
+ this.claudePath = options.claudePath ?? process.env.CLAUDE_PATH ?? "claude";
257
+ this.fileTools = options.fileTools;
258
+ this.remindersFile = options.remindersFile;
259
+ this.shellTools = options.shellTools;
260
+ this.webTools = options.webTools;
261
+ this.actionTools = options.actionTools;
262
+ this.model = options.model;
263
+ }
264
+ async *callChat(messages, options = {}) {
265
+ yield await this.callChatSync(messages, options);
266
+ }
267
+ async callChatSync(messages, options = {}) {
268
+ var _a;
269
+ const { systemPrompt, prompt } = buildClaudePrompt(messages);
270
+ const args = [
271
+ "--print",
272
+ "--output-format",
273
+ "json",
274
+ "--tools",
275
+ "",
276
+ "--no-session-persistence",
277
+ "--system-prompt",
278
+ systemPrompt,
279
+ "--mcp-config",
280
+ EMPTY_MCP_CONFIG,
281
+ "--strict-mcp-config"
282
+ // ignore any ambient project/user MCP config — see EMPTY_MCP_CONFIG's doc comment
283
+ ];
284
+ const model = options.model ?? this.model;
285
+ if (model) args.push("--model", model);
286
+ args.push(prompt);
287
+ const { reply, usage, model: resolved } = await invokeClaude(this.claudePath, args);
288
+ if (resolved) this.resolvedModelId = resolved;
289
+ if (usage) (_a = options.onUsage) == null ? void 0 : _a.call(options, usage);
290
+ return reply;
291
+ }
292
+ /**
293
+ * When `tools` is non-empty and `fileTools`/`shellTools` was configured, wires the
294
+ * file-tools MCP server into a single `claude -p` call and lets Claude Code's own
295
+ * agentic loop call read_file/list_directory/write_file/fetch_url/create_reminder/
296
+ * list_reminders/run_shell_command autonomously — we don't get to intercept each
297
+ * call the way the proxy backend's manual tool loop does (see
298
+ * the internal plan, T6). fetch_url is always
299
+ * registered on that server; create_reminder/list_reminders only when
300
+ * `remindersFile` is set; run_shell_command only when `shellTools` is set;
301
+ * web_search only when `webTools` is set (Brave Search — see the `webTools`
302
+ * option). Three
303
+ * possible outcomes: a final text reply (no tool call this backend needs to
304
+ * surface), a write or shell command staged by the MCP server mid-call (surfaced
305
+ * as a synthetic `__staged_action` tool call so assistant.ts's tool loop treats it
306
+ * the same as a manually staged action without staging it a second time), or —
307
+ * for fetch_url/create_reminder/list_reminders — the tool's result text folded
308
+ * directly into Claude Code's own reply, since (unlike the proxy backend's
309
+ * `executeToolCall`) there's no outer loop here to intercept each call and
310
+ * re-apply trust-tagging itself; the MCP server tags fetch_url's result before
311
+ * Claude Code ever sees it instead (see file-tools-mcp-server.mjs).
312
+ */
313
+ async callChatStructured(messages, tools, options = {}) {
314
+ var _a, _b, _c, _d, _e2, _f;
315
+ if (!tools || tools.length === 0) {
316
+ const content = await this.callChatSync(messages, options);
317
+ return { content: options.structuredOutput ? stripJsonCodeFence(content) : content };
318
+ }
319
+ if (!this.fileTools && !this.shellTools && !this.webTools && !this.actionTools && !recallToolEnabled()) {
320
+ throw new Error(
321
+ "ClaudeCliLLMClient does not support tool calls unless constructed with fileTools, shellTools, webTools, or actionTools configured"
322
+ );
323
+ }
324
+ const { systemPrompt, prompt } = buildClaudePrompt(messages);
325
+ const workspaceRoot = ((_a = this.fileTools) == null ? void 0 : _a.workspaceRoot) ?? ((_b = this.shellTools) == null ? void 0 : _b.workspaceRoot) ?? ((_c = this.actionTools) == null ? void 0 : _c.workspaceRoot) ?? tmpdir();
326
+ const lastUserMessage = ((_d = [...messages].reverse().find((m) => m.role === "user")) == null ? void 0 : _d.content) ?? "";
327
+ const mcpServerPath = resolveMcpServerPath();
328
+ const gate = await startToolGateServer(options.onToolProposal, options.onToolResult, options.onToolExecute);
329
+ try {
330
+ const mcpConfig = JSON.stringify({
331
+ mcpServers: {
332
+ "file-tools": {
333
+ command: "node",
334
+ args: [mcpServerPath],
335
+ env: {
336
+ WORKSPACE_ROOT: workspaceRoot,
337
+ TOOL_GATE_PORT: String(gate.port),
338
+ ...this.remindersFile ? { REMINDERS_FILE: this.remindersFile, CURRENT_USER_MESSAGE: lastUserMessage } : {},
339
+ ...this.shellTools ? { ENABLE_SHELL_TOOLS: "1" } : {},
340
+ ...((_e2 = this.webTools) == null ? void 0 : _e2.braveApiKey) ? { BRAVE_SEARCH_API_KEY: this.webTools.braveApiKey } : {},
341
+ ...this.actionTools ? { ENABLE_EMAIL_TOOL: "1" } : {},
342
+ // M3: recall_memory is registered (gated, then served by the parent via onToolExecute) only under AUDIT_RECALL_TOOL.
343
+ ...recallToolEnabled() ? { ENABLE_RECALL_TOOL: "1" } : {},
344
+ // Always passed (even empty): the server reads an unset value as the default, every family off.
345
+ ASSISTANT_LEXICAL_RESOLVED_OFF: lexicalOffEnvValue()
346
+ }
347
+ }
348
+ }
349
+ });
350
+ const args = [
351
+ "--print",
352
+ "--output-format",
353
+ "stream-json",
354
+ // streamed (not the single-object 'json') so tool_use events can be reported live via onToolStep — see invokeClaudeStreaming
355
+ "--verbose",
356
+ // required by --print when --output-format is stream-json
357
+ "--tools",
358
+ "",
359
+ // still disable Claude Code's own built-in Read/Write/Bash tools — Bash is never added, regardless of shellTools
360
+ "--no-session-persistence",
361
+ "--system-prompt",
362
+ systemPrompt,
363
+ "--mcp-config",
364
+ mcpConfig,
365
+ "--strict-mcp-config",
366
+ // ignore any ambient project .mcp.json — the tool surface must be exactly this plan's tools
367
+ "--dangerously-skip-permissions"
368
+ // headless -p mode has no way to answer an interactive tool-permission prompt
369
+ ];
370
+ const model = options.model ?? this.model;
371
+ if (model) args.push("--model", model);
372
+ args.push(prompt);
373
+ const callStartedAt = Date.now();
374
+ const { reply, usage, model: resolved } = await invokeClaudeStreaming(this.claudePath, args, options.onToolStep);
375
+ if (resolved) this.resolvedModelId = resolved;
376
+ if (usage) (_f = options.onUsage) == null ? void 0 : _f.call(options, usage);
377
+ const staged = await this.findPendingActionStagedSince(workspaceRoot, callStartedAt);
378
+ if (staged) {
379
+ return {
380
+ content: "",
381
+ toolCalls: [{ id: `cli-staged-${staged.id}`, name: ALREADY_STAGED_ACTION_TOOL, input: stagedActionInput(staged) }]
382
+ };
383
+ }
384
+ return { content: reply };
385
+ } finally {
386
+ gate.server.close();
387
+ }
388
+ }
389
+ /** Diffs .pending-actions/ against the call's start time to detect a write or shell command the MCP server staged during this subprocess call. */
390
+ /**
391
+ * When the MCP server staged more than one action this call (e.g. "run X AND write Y" —
392
+ * see file-tools-mcp-server.mjs's stagePendingAction doc comment), every one of them lands
393
+ * in `.pending-actions/` with an mtime after `startTimeMs`, in `readdir`-arbitrary order —
394
+ * not necessarily staging order. Must return the *chain head* (earliest `stagedAt`)
395
+ * specifically, since resolvePendingAction (assistant.ts) walks `nextPendingActionId`
396
+ * forward from whichever record it's handed; returning a later link here would surface the
397
+ * chain out of order and orphan the earlier, still-unresolved action.
398
+ */
399
+ async findPendingActionStagedSince(workspaceRoot, startTimeMs) {
400
+ const dir = `${workspaceRoot}/.pending-actions`;
401
+ let names;
402
+ try {
403
+ names = await readdir(dir);
404
+ } catch {
405
+ return void 0;
406
+ }
407
+ let earliest;
408
+ for (const name of names) {
409
+ if (!name.endsWith(".json")) continue;
410
+ const filePath = `${dir}/${name}`;
411
+ const stats = await stat(filePath);
412
+ if (stats.mtimeMs < startTimeMs - 1e3) continue;
413
+ const record = JSON.parse(await readFile(filePath, "utf-8"));
414
+ if (!earliest || record.stagedAt < earliest.stagedAt) earliest = record;
415
+ }
416
+ return earliest;
417
+ }
418
+ }
419
+ const MAX_HEADER_BYTES = 16384;
420
+ function hostAllowed(host, allowlist) {
421
+ const normalized = host.toLowerCase();
422
+ return allowlist.some((entry) => {
423
+ const allowed = entry.toLowerCase();
424
+ return normalized === allowed || normalized.endsWith(`.${allowed}`);
425
+ });
426
+ }
427
+ function parseConnectTarget(requestLine) {
428
+ const match = /^CONNECT\s+([^:\s]+):(\d+)\s+HTTP/i.exec(requestLine);
429
+ return match ? { host: match[1], port: Number(match[2]) } : null;
430
+ }
431
+ function parsePlainHttpTarget(requestLine, headerText) {
432
+ const absoluteUri = /^[A-Z]+\s+https?:\/\/([^/:\s]+)(?::(\d+))?/i.exec(requestLine);
433
+ if (absoluteUri) return { host: absoluteUri[1], port: Number(absoluteUri[2] ?? 80) };
434
+ const hostHeader = /^host:\s*([^\s:]+)(?::(\d+))?/im.exec(headerText);
435
+ return hostHeader ? { host: hostHeader[1], port: Number(hostHeader[2] ?? 80) } : null;
436
+ }
437
+ function handleConnection(clientSocket, allowlist) {
438
+ let buffered = Buffer.alloc(0);
439
+ const onData = (chunk) => {
440
+ buffered = Buffer.concat([buffered, chunk]);
441
+ const headerEnd = buffered.indexOf("\r\n\r\n");
442
+ if (headerEnd === -1) {
443
+ if (buffered.length > MAX_HEADER_BYTES) clientSocket.destroy();
444
+ return;
445
+ }
446
+ clientSocket.removeListener("data", onData);
447
+ const headerText = buffered.slice(0, headerEnd).toString("utf-8");
448
+ const requestLine = headerText.split("\r\n")[0] ?? "";
449
+ const isConnect = /^CONNECT\s/i.test(requestLine);
450
+ const target = isConnect ? parseConnectTarget(requestLine) : parsePlainHttpTarget(requestLine, headerText);
451
+ if (!target || !hostAllowed(target.host, allowlist)) {
452
+ clientSocket.end("HTTP/1.1 403 Forbidden\r\nContent-Length: 0\r\n\r\n");
453
+ return;
454
+ }
455
+ const upstream = connect(target.port, target.host, () => {
456
+ if (isConnect) {
457
+ clientSocket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
458
+ const tunnelBytes = buffered.slice(headerEnd + 4);
459
+ if (tunnelBytes.length > 0) upstream.write(tunnelBytes);
460
+ } else {
461
+ upstream.write(buffered);
462
+ }
463
+ clientSocket.pipe(upstream);
464
+ upstream.pipe(clientSocket);
465
+ });
466
+ upstream.on("error", () => clientSocket.destroy());
467
+ };
468
+ clientSocket.on("data", onData);
469
+ clientSocket.on("error", () => clientSocket.destroy());
470
+ }
471
+ function startProxyServer(allowlist) {
472
+ return new Promise((resolveStart, rejectStart) => {
473
+ const server = createServer((socket) => handleConnection(socket, allowlist));
474
+ server.on("error", rejectStart);
475
+ server.listen(0, "127.0.0.1", () => {
476
+ const address = server.address();
477
+ if (address === null || typeof address === "string") {
478
+ rejectStart(new Error("Failed to bind network containment proxy to a loopback port"));
479
+ return;
480
+ }
481
+ resolveStart({
482
+ port: address.port,
483
+ close: () => new Promise((resolveClose) => server.close(() => resolveClose()))
484
+ });
485
+ });
486
+ });
487
+ }
488
+ const proxyCache = /* @__PURE__ */ new Map();
489
+ function getNetworkContainmentProxy(allowlist) {
490
+ const key = JSON.stringify([...allowlist].map((host) => host.toLowerCase()).sort());
491
+ let proxy = proxyCache.get(key);
492
+ if (!proxy) {
493
+ proxy = startProxyServer(allowlist);
494
+ proxyCache.set(key, proxy);
495
+ }
496
+ return proxy;
497
+ }
498
+ const DEFAULT_TIMEOUT_MS = 3e4;
499
+ const DEFAULT_MAX_OUTPUT_BYTES = 2e4;
500
+ const ALLOWED_ENV_VARS = ["PATH", "HOME", "LANG"];
501
+ function allowlistedEnv() {
502
+ const env = {};
503
+ for (const key of ALLOWED_ENV_VARS) {
504
+ const value = process.env[key];
505
+ if (value !== void 0) env[key] = value;
506
+ }
507
+ return env;
508
+ }
509
+ async function networkContainmentEnv(networkAllowlist) {
510
+ const proxy = await getNetworkContainmentProxy(networkAllowlist);
511
+ const proxyUrl = `http://127.0.0.1:${proxy.port}`;
512
+ return {
513
+ HTTP_PROXY: proxyUrl,
514
+ http_proxy: proxyUrl,
515
+ HTTPS_PROXY: proxyUrl,
516
+ https_proxy: proxyUrl
517
+ };
518
+ }
519
+ function truncateOutput(text, maxBytes) {
520
+ const encoded = new TextEncoder().encode(text);
521
+ if (encoded.length <= maxBytes) return text;
522
+ const truncated = new TextDecoder("utf-8", { fatal: false }).decode(encoded.slice(0, maxBytes));
523
+ return `${truncated}
524
+ … (truncated)`;
525
+ }
526
+ const runApprovedShellCommand = async (command, cwd, options = {}) => {
527
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
528
+ const maxOutputBytes = options.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
529
+ const containmentEnv = await networkContainmentEnv(options.networkAllowlist ?? []);
530
+ return new Promise((resolvePromise, reject) => {
531
+ var _a, _b;
532
+ const proc = spawn(command, {
533
+ shell: true,
534
+ cwd,
535
+ env: { ...allowlistedEnv(), ...containmentEnv },
536
+ detached: true,
537
+ stdio: ["ignore", "pipe", "pipe"]
538
+ });
539
+ let output = "";
540
+ let timedOut = false;
541
+ let settled = false;
542
+ const timer = setTimeout(() => {
543
+ timedOut = true;
544
+ try {
545
+ if (proc.pid) process.kill(-proc.pid, "SIGKILL");
546
+ else proc.kill("SIGKILL");
547
+ } catch {
548
+ proc.kill("SIGKILL");
549
+ }
550
+ }, timeoutMs);
551
+ (_a = proc.stdout) == null ? void 0 : _a.on("data", (chunk) => {
552
+ output += chunk.toString("utf-8");
553
+ });
554
+ (_b = proc.stderr) == null ? void 0 : _b.on("data", (chunk) => {
555
+ output += chunk.toString("utf-8");
556
+ });
557
+ proc.on("error", (err) => {
558
+ if (settled) return;
559
+ settled = true;
560
+ clearTimeout(timer);
561
+ reject(err);
562
+ });
563
+ proc.on("close", (exitCode) => {
564
+ if (settled) return;
565
+ settled = true;
566
+ clearTimeout(timer);
567
+ resolvePromise({
568
+ output: truncateOutput(output, maxOutputBytes),
569
+ exitCode: timedOut ? null : exitCode,
570
+ timedOut
571
+ });
572
+ });
573
+ });
574
+ };
575
+ function isEnoent(err) {
576
+ return (err == null ? void 0 : err.code) === "ENOENT";
577
+ }
578
+ class NodeConfigStore {
579
+ constructor(path) {
580
+ __publicField(this, "queue", Promise.resolve());
581
+ this.path = path;
582
+ }
583
+ async load() {
584
+ let raw;
585
+ try {
586
+ raw = await readFile(this.path, "utf-8");
587
+ } catch (err) {
588
+ if (isEnoent(err)) return {};
589
+ throw err;
590
+ }
591
+ try {
592
+ return JSON.parse(raw);
593
+ } catch {
594
+ console.error(`Warning: ${this.path} is not valid JSON — ignoring it and falling back to defaults.`);
595
+ return {};
596
+ }
597
+ }
598
+ save(patch) {
599
+ const task = this.queue.then(() => this.writePatch(patch));
600
+ this.queue = task.catch(() => {
601
+ });
602
+ return task;
603
+ }
604
+ async writePatch(patch) {
605
+ const existing = await this.load();
606
+ const merged = { ...existing, ...patch };
607
+ await mkdir(dirname(this.path), { recursive: true });
608
+ const tempPath = `${this.path}.tmp-${process.pid}-${randomBytes(6).toString("hex")}`;
609
+ await writeFile(tempPath, JSON.stringify(merged, null, 2), "utf-8");
610
+ await rename(tempPath, this.path);
611
+ }
612
+ }
613
+ const DOCTOR_CHECK_TIMEOUT_MS = 3e3;
614
+ async function checkProxyHealth(proxyUrl) {
615
+ const label = `proxy reachable (${proxyUrl}/health)`;
616
+ const controller = new AbortController();
617
+ const timeout = setTimeout(() => controller.abort(), DOCTOR_CHECK_TIMEOUT_MS);
618
+ try {
619
+ const res = await fetch(`${proxyUrl}/health`, { signal: controller.signal });
620
+ if (!res.ok) return { label, ok: false, detail: `HTTP ${res.status}` };
621
+ const body = await res.json().catch(() => void 0);
622
+ return (body == null ? void 0 : body.status) === "ok" ? { label, ok: true } : { label, ok: false, detail: "unexpected response body" };
623
+ } catch (err) {
624
+ const timedOut = err instanceof Error && err.name === "AbortError";
625
+ return { label, ok: false, detail: timedOut ? "timed out" : err instanceof Error ? err.message : String(err) };
626
+ } finally {
627
+ clearTimeout(timeout);
628
+ }
629
+ }
630
+ function checkClaudeCli(claudePath) {
631
+ const label = `claude binary (${claudePath})`;
632
+ return new Promise((resolvePromise) => {
633
+ let settled = false;
634
+ const proc = spawn(claudePath, ["--version"], { stdio: ["ignore", "ignore", "ignore"] });
635
+ const timeout = setTimeout(() => {
636
+ if (settled) return;
637
+ settled = true;
638
+ proc.kill();
639
+ resolvePromise({ label, ok: false, detail: "timed out" });
640
+ }, DOCTOR_CHECK_TIMEOUT_MS);
641
+ proc.on("error", () => {
642
+ if (settled) return;
643
+ settled = true;
644
+ clearTimeout(timeout);
645
+ resolvePromise({ label, ok: false, detail: "not found" });
646
+ });
647
+ proc.on("close", (code) => {
648
+ if (settled) return;
649
+ settled = true;
650
+ clearTimeout(timeout);
651
+ resolvePromise({ label, ok: code === 0, detail: code === 0 ? void 0 : `exited with code ${code}` });
652
+ });
653
+ });
654
+ }
655
+ function checkMemoryHealth(status) {
656
+ return memoryStatusChecks(status);
657
+ }
658
+ async function checkWorkspaceRoot(workspaceRoot) {
659
+ const label = `workspace root exists (${workspaceRoot})`;
660
+ try {
661
+ const info = await stat(workspaceRoot);
662
+ return info.isDirectory() ? { label, ok: true } : { label, ok: false, detail: "not a directory" };
663
+ } catch {
664
+ return { label, ok: false, detail: "not found" };
665
+ }
666
+ }
667
+ async function checkDataDirWritable(backend, dataDir) {
668
+ const label = `data dir writable (${dataDir})`;
669
+ const probePath = join(dataDir, ".doctor-check");
670
+ try {
671
+ await backend.mkdir(dataDir);
672
+ await backend.writeTextFile(probePath, "ok");
673
+ await backend.removeFile(probePath);
674
+ return { label, ok: true };
675
+ } catch (err) {
676
+ return { label, ok: false, detail: err instanceof Error ? err.message : String(err) };
677
+ }
678
+ }
679
+ function resolveNonInteractiveApprovalMode(env) {
680
+ const raw = env.ASSISTANT_NON_INTERACTIVE_APPROVAL;
681
+ if (raw === void 0) return void 0;
682
+ if (raw === "decline" || raw === "require-tty") return raw;
683
+ console.error(`[warning] ASSISTANT_NON_INTERACTIVE_APPROVAL="${raw}" is not "decline" or "require-tty" — ignoring.`);
684
+ return void 0;
685
+ }
686
+ var Te = Object.defineProperty;
687
+ var In = (e, n, t) => n in e ? Te(e, n, { enumerable: true, configurable: true, writable: true, value: t }) : e[n] = t;
688
+ var Sn = (e, n) => {
689
+ for (var t in n) Te(e, t, { get: n[t], enumerable: true });
690
+ };
691
+ var je = (e, n, t) => In(e, n + "", t);
692
+ var ie = {};
693
+ Sn(ie, { Graph: () => T, alg: () => H });
694
+ var Mn = Object.defineProperty, Se = (e, n) => {
695
+ for (var t in n) Mn(e, t, { get: n[t], enumerable: true });
696
+ }, Q = class {
697
+ constructor(e) {
698
+ this._isDirected = true, this._isMultigraph = false, this._isCompound = false, this._nodes = {}, this._in = {}, this._preds = {}, this._out = {}, this._sucs = {}, this._edgeObjs = {}, this._edgeLabels = {}, this._nodeCount = 0, this._edgeCount = 0, this._defaultNodeLabelFn = () => {
699
+ }, this._defaultEdgeLabelFn = () => {
700
+ }, e && (this._isDirected = "directed" in e ? e.directed : true, this._isMultigraph = "multigraph" in e ? e.multigraph : false, this._isCompound = "compound" in e ? e.compound : false), this._isCompound && (this._parent = {}, this._children = {}, this._children["\0"] = {});
701
+ }
702
+ isDirected() {
703
+ return this._isDirected;
704
+ }
705
+ isMultigraph() {
706
+ return this._isMultigraph;
707
+ }
708
+ isCompound() {
709
+ return this._isCompound;
710
+ }
711
+ setGraph(e) {
712
+ return this._label = e, this;
713
+ }
714
+ graph() {
715
+ return this._label;
716
+ }
717
+ setDefaultNodeLabel(e) {
718
+ return typeof e != "function" ? this._defaultNodeLabelFn = () => e : this._defaultNodeLabelFn = e, this;
719
+ }
720
+ nodeCount() {
721
+ return this._nodeCount;
722
+ }
723
+ nodes() {
724
+ return Object.keys(this._nodes);
725
+ }
726
+ sources() {
727
+ return this.nodes().filter((e) => Object.keys(this._in[e]).length === 0);
728
+ }
729
+ sinks() {
730
+ return this.nodes().filter((e) => Object.keys(this._out[e]).length === 0);
731
+ }
732
+ setNodes(e, n) {
733
+ return e.forEach((t) => {
734
+ n !== void 0 ? this.setNode(t, n) : this.setNode(t);
735
+ }), this;
736
+ }
737
+ setNode(e, n) {
738
+ return e in this._nodes ? (arguments.length > 1 && (this._nodes[e] = n), this) : (this._nodes[e] = arguments.length > 1 ? n : this._defaultNodeLabelFn(e), this._isCompound && (this._parent[e] = "\0", this._children[e] = {}, this._children["\0"][e] = true), this._in[e] = {}, this._preds[e] = {}, this._out[e] = {}, this._sucs[e] = {}, ++this._nodeCount, this);
739
+ }
740
+ node(e) {
741
+ return this._nodes[e];
742
+ }
743
+ hasNode(e) {
744
+ return e in this._nodes;
745
+ }
746
+ removeNode(e) {
747
+ if (e in this._nodes) {
748
+ let n = (t) => this.removeEdge(this._edgeObjs[t]);
749
+ delete this._nodes[e], this._isCompound && (this._removeFromParentsChildList(e), delete this._parent[e], this.children(e).forEach((t) => {
750
+ this.setParent(t);
751
+ }), delete this._children[e]), Object.keys(this._in[e]).forEach(n), delete this._in[e], delete this._preds[e], Object.keys(this._out[e]).forEach(n), delete this._out[e], delete this._sucs[e], --this._nodeCount;
752
+ }
753
+ return this;
754
+ }
755
+ setParent(e, n) {
756
+ if (!this._isCompound) throw new Error("Cannot set parent in a non-compound graph");
757
+ if (n === void 0) n = "\0";
758
+ else {
759
+ n += "";
760
+ for (let t = n; t !== void 0; t = this.parent(t)) if (t === e) throw new Error("Setting " + n + " as parent of " + e + " would create a cycle");
761
+ this.setNode(n);
762
+ }
763
+ return this.setNode(e), this._removeFromParentsChildList(e), this._parent[e] = n, this._children[n][e] = true, this;
764
+ }
765
+ parent(e) {
766
+ if (this._isCompound) {
767
+ let n = this._parent[e];
768
+ if (n !== "\0") return n;
769
+ }
770
+ }
771
+ children(e = "\0") {
772
+ if (this._isCompound) {
773
+ let n = this._children[e];
774
+ if (n) return Object.keys(n);
775
+ } else {
776
+ if (e === "\0") return this.nodes();
777
+ if (this.hasNode(e)) return [];
778
+ }
779
+ return [];
780
+ }
781
+ predecessors(e) {
782
+ let n = this._preds[e];
783
+ if (n) return Object.keys(n);
784
+ }
785
+ successors(e) {
786
+ let n = this._sucs[e];
787
+ if (n) return Object.keys(n);
788
+ }
789
+ neighbors(e) {
790
+ let n = this.predecessors(e);
791
+ if (n) {
792
+ let t = new Set(n), r = this.successors(e);
793
+ if (r) for (let o of r) t.add(o);
794
+ return Array.from(t.values());
795
+ }
796
+ }
797
+ isLeaf(e) {
798
+ var n;
799
+ let t;
800
+ return this.isDirected() ? t = this.successors(e) : t = this.neighbors(e), ((n = t == null ? void 0 : t.length) != null ? n : 0) === 0;
801
+ }
802
+ filterNodes(e) {
803
+ let n = new this.constructor({ directed: this._isDirected, multigraph: this._isMultigraph, compound: this._isCompound });
804
+ n.setGraph(this.graph()), Object.entries(this._nodes).forEach(([o, i]) => {
805
+ e(o) && n.setNode(o, i);
806
+ }), Object.values(this._edgeObjs).forEach((o) => {
807
+ n.hasNode(o.v) && n.hasNode(o.w) && n.setEdge(o, this.edge(o));
808
+ });
809
+ let t = {}, r = (o) => {
810
+ let i = this.parent(o);
811
+ return !i || n.hasNode(i) ? (t[o] = i, i) : i in t ? t[i] : r(i);
812
+ };
813
+ return this._isCompound && n.nodes().forEach((o) => n.setParent(o, r(o))), n;
814
+ }
815
+ setDefaultEdgeLabel(e) {
816
+ return typeof e != "function" ? this._defaultEdgeLabelFn = () => e : this._defaultEdgeLabelFn = e, this;
817
+ }
818
+ edgeCount() {
819
+ return this._edgeCount;
820
+ }
821
+ edges() {
822
+ return Object.values(this._edgeObjs);
823
+ }
824
+ setPath(e, n) {
825
+ return e.reduce((t, r) => (n !== void 0 ? this.setEdge(t, r, n) : this.setEdge(t, r), r)), this;
826
+ }
827
+ setEdge(e, n, t, r) {
828
+ let o, i, s, a, l = false;
829
+ typeof e == "object" && e !== null && "v" in e ? (o = e.v, i = e.w, s = e.name, arguments.length === 2 && (a = n, l = true)) : (o = e, i = n, s = r, arguments.length > 2 && (a = t, l = true)), o = "" + o, i = "" + i, s !== void 0 && (s = "" + s);
830
+ let u = z(this._isDirected, o, i, s);
831
+ if (u in this._edgeLabels) return l && (this._edgeLabels[u] = a), this;
832
+ if (s !== void 0 && !this._isMultigraph) throw new Error("Cannot set a named edge when isMultigraph = false");
833
+ this.setNode(o), this.setNode(i), this._edgeLabels[u] = l ? a : this._defaultEdgeLabelFn(o, i, s);
834
+ let d = Pn(this._isDirected, o, i, s);
835
+ return o = d.v, i = d.w, Object.freeze(d), this._edgeObjs[u] = d, Re(this._preds[i], o), Re(this._sucs[o], i), this._in[i][u] = d, this._out[o][u] = d, this._edgeCount++, this;
836
+ }
837
+ edge(e, n, t) {
838
+ let r = arguments.length === 1 ? oe(this._isDirected, e) : z(this._isDirected, e, n, t);
839
+ return this._edgeLabels[r];
840
+ }
841
+ edgeAsObj(e, n, t) {
842
+ let r = arguments.length === 1 ? this.edge(e) : this.edge(e, n, t);
843
+ return typeof r != "object" || r === null ? { label: r } : r;
844
+ }
845
+ hasEdge(e, n, t) {
846
+ return (arguments.length === 1 ? oe(this._isDirected, e) : z(this._isDirected, e, n, t)) in this._edgeLabels;
847
+ }
848
+ removeEdge(e, n, t) {
849
+ let r = arguments.length === 1 ? oe(this._isDirected, e) : z(this._isDirected, e, n, t), o = this._edgeObjs[r];
850
+ if (o) {
851
+ let i = o.v, s = o.w;
852
+ delete this._edgeLabels[r], delete this._edgeObjs[r], Ie(this._preds[s], i), Ie(this._sucs[i], s), delete this._in[s][r], delete this._out[i][r], this._edgeCount--;
853
+ }
854
+ return this;
855
+ }
856
+ inEdges(e, n) {
857
+ return this.isDirected() ? this.filterEdges(this._in[e], e, n) : this.nodeEdges(e, n);
858
+ }
859
+ outEdges(e, n) {
860
+ return this.isDirected() ? this.filterEdges(this._out[e], e, n) : this.nodeEdges(e, n);
861
+ }
862
+ nodeEdges(e, n) {
863
+ if (e in this._nodes) return this.filterEdges({ ...this._in[e], ...this._out[e] }, e, n);
864
+ }
865
+ _removeFromParentsChildList(e) {
866
+ delete this._children[this._parent[e]][e];
867
+ }
868
+ filterEdges(e, n, t) {
869
+ if (!e) return;
870
+ let r = Object.values(e);
871
+ return t ? r.filter((o) => o.v === n && o.w === t || o.v === t && o.w === n) : r;
872
+ }
873
+ };
874
+ function Re(e, n) {
875
+ e[n] ? e[n]++ : e[n] = 1;
876
+ }
877
+ function Ie(e, n) {
878
+ e[n] !== void 0 && !--e[n] && delete e[n];
879
+ }
880
+ function z(e, n, t, r) {
881
+ let o = "" + n, i = "" + t;
882
+ if (!e && o > i) {
883
+ let s = o;
884
+ o = i, i = s;
885
+ }
886
+ return o + "" + i + "" + (r === void 0 ? "\0" : r);
887
+ }
888
+ function Pn(e, n, t, r) {
889
+ let o = "" + n, i = "" + t;
890
+ if (!e && o > i) {
891
+ let a = o;
892
+ o = i, i = a;
893
+ }
894
+ let s = { v: o, w: i };
895
+ return r && (s.name = r), s;
896
+ }
897
+ function oe(e, n) {
898
+ return z(e, n.v, n.w, n.name);
899
+ }
900
+ var Fn = {};
901
+ Se(Fn, { read: () => Yn, write: () => An });
902
+ function An(e) {
903
+ let n = { options: { directed: e.isDirected(), multigraph: e.isMultigraph(), compound: e.isCompound() }, nodes: Vn(e), edges: Dn(e) }, t = e.graph();
904
+ return t !== void 0 && (n.value = structuredClone(t)), n;
905
+ }
906
+ function Vn(e) {
907
+ return e.nodes().map((n) => {
908
+ let t = e.node(n), r = e.parent(n), o = { v: n };
909
+ return t !== void 0 && (o.value = t), r !== void 0 && (o.parent = r), o;
910
+ });
911
+ }
912
+ function Dn(e) {
913
+ return e.edges().map((n) => {
914
+ let t = e.edge(n), r = { v: n.v, w: n.w };
915
+ return n.name !== void 0 && (r.name = n.name), t !== void 0 && (r.value = t), r;
916
+ });
917
+ }
918
+ function Yn(e) {
919
+ let n = new Q(e.options);
920
+ return e.value !== void 0 && n.setGraph(e.value), e.nodes.forEach((t) => {
921
+ n.setNode(t.v, t.value), t.parent && n.setParent(t.v, t.parent);
922
+ }), e.edges.forEach((t) => {
923
+ n.setEdge({ v: t.v, w: t.w, name: t.name }, t.value);
924
+ }), n;
925
+ }
926
+ var H = {};
927
+ Se(H, { CycleException: () => K, bellmanFord: () => Me, components: () => Xn, dijkstra: () => J, dijkstraAll: () => qn, findCycles: () => $n, floydWarshall: () => Jn, isAcyclic: () => Qn, postorder: () => et, preorder: () => nt, prim: () => tt, shortestPaths: () => rt, tarjan: () => Fe, topsort: () => Ae });
928
+ var Wn = () => 1;
929
+ function Me(e, n, t, r) {
930
+ return Bn(e, String(n), t || Wn, r || function(o) {
931
+ var i;
932
+ return (i = e.outEdges(o)) != null ? i : [];
933
+ });
934
+ }
935
+ function Bn(e, n, t, r) {
936
+ let o = {}, i, s = 0, a = e.nodes(), l = function(c) {
937
+ let f = o[c.v], h = o[c.w];
938
+ if (!f || !h) return;
939
+ let p = t(c);
940
+ f.distance + p < h.distance && (o[c.w] = { distance: f.distance + p, predecessor: c.v }, i = true);
941
+ }, u = function() {
942
+ a.forEach(function(c) {
943
+ r(c).forEach(function(f) {
944
+ let h = f.v === c ? f.v : f.w, p = h === f.v ? f.w : f.v;
945
+ l({ v: h, w: p });
946
+ });
947
+ });
948
+ };
949
+ a.forEach(function(c) {
950
+ let f = c === n ? 0 : Number.POSITIVE_INFINITY;
951
+ o[c] = { distance: f, predecessor: "" };
952
+ });
953
+ let d = a.length;
954
+ for (let c = 1; c < d && (i = false, s++, u(), !!i); c++) ;
955
+ if (s === d - 1 && (i = false, u(), i)) throw new Error("The graph contains a negative weight cycle");
956
+ return o;
957
+ }
958
+ function Xn(e) {
959
+ let n = {}, t = [], r;
960
+ function o(i) {
961
+ var s, a;
962
+ i in n || (n[i] = true, r.push(i), (s = e.successors(i)) == null || s.forEach(o), (a = e.predecessors(i)) == null || a.forEach(o));
963
+ }
964
+ return e.nodes().forEach(function(i) {
965
+ r = [], o(i), r.length && t.push(r);
966
+ }), t;
967
+ }
968
+ var Pe = class {
969
+ constructor() {
970
+ this._arr = [], this._keyIndices = {};
971
+ }
972
+ size() {
973
+ return this._arr.length;
974
+ }
975
+ keys() {
976
+ return this._arr.map((e) => e.key);
977
+ }
978
+ has(e) {
979
+ return e in this._keyIndices;
980
+ }
981
+ priority(e) {
982
+ let n = this._keyIndices[e];
983
+ if (n !== void 0) return this._arr[n].priority;
984
+ }
985
+ min() {
986
+ if (this.size() === 0) throw new Error("Queue underflow");
987
+ return this._arr[0].key;
988
+ }
989
+ add(e, n) {
990
+ let t = this._keyIndices, r = String(e);
991
+ if (!(r in t)) {
992
+ let o = this._arr, i = o.length;
993
+ return t[r] = i, o.push({ key: r, priority: n }), this._decrease(i), true;
994
+ }
995
+ return false;
996
+ }
997
+ removeMin() {
998
+ if (this.size() === 0) throw new Error("Queue underflow");
999
+ this._swap(0, this._arr.length - 1);
1000
+ let e = this._arr.pop();
1001
+ return delete this._keyIndices[e.key], this._heapify(0), e.key;
1002
+ }
1003
+ decrease(e, n) {
1004
+ let t = this._keyIndices[e];
1005
+ if (t === void 0) throw new Error(`Key not found: ${e}`);
1006
+ let r = this._arr[t].priority;
1007
+ if (n > r) throw new Error(`New priority is greater than current priority. Key: ${e} Old: ${r} New: ${n}`);
1008
+ this._arr[t].priority = n, this._decrease(t);
1009
+ }
1010
+ _heapify(e) {
1011
+ let n = this._arr, t = 2 * e, r = t + 1, o = e;
1012
+ t < n.length && (o = n[t].priority < n[o].priority ? t : o, r < n.length && (o = n[r].priority < n[o].priority ? r : o), o !== e && (this._swap(e, o), this._heapify(o)));
1013
+ }
1014
+ _decrease(e) {
1015
+ let n = this._arr, t = n[e].priority, r;
1016
+ for (; e !== 0 && (r = e >> 1, !(n[r].priority < t)); ) this._swap(e, r), e = r;
1017
+ }
1018
+ _swap(e, n) {
1019
+ let t = this._arr, r = this._keyIndices, o = t[e], i = t[n];
1020
+ t[e] = i, t[n] = o, r[i.key] = e, r[o.key] = n;
1021
+ }
1022
+ }, zn = () => 1;
1023
+ function J(e, n, t, r) {
1024
+ let o = function(i) {
1025
+ var s;
1026
+ return (s = e.outEdges(i)) != null ? s : [];
1027
+ };
1028
+ return Hn(e, String(n), t || zn, r || o);
1029
+ }
1030
+ function Hn(e, n, t, r) {
1031
+ let o = {}, i = new Pe(), s, a, l = function(u) {
1032
+ let d = u.v !== s ? u.v : u.w, c = o[d];
1033
+ if (!c) return;
1034
+ let f = t(u), h = a.distance + f;
1035
+ if (f < 0) throw new Error("dijkstra does not allow negative edge weights. Bad edge: " + u + " Weight: " + f);
1036
+ h < c.distance && (c.distance = h, c.predecessor = s, i.decrease(d, h));
1037
+ };
1038
+ for (e.nodes().forEach(function(u) {
1039
+ let d = u === n ? 0 : Number.POSITIVE_INFINITY;
1040
+ o[u] = { distance: d, predecessor: "" }, i.add(u, d);
1041
+ }); i.size() > 0; ) {
1042
+ s = i.removeMin();
1043
+ let u = o[s];
1044
+ if (!u || u.distance === Number.POSITIVE_INFINITY) break;
1045
+ a = u, r(s).forEach(l);
1046
+ }
1047
+ return o;
1048
+ }
1049
+ function qn(e, n, t) {
1050
+ return e.nodes().reduce(function(r, o) {
1051
+ return r[o] = J(e, o, n, t), r;
1052
+ }, {});
1053
+ }
1054
+ function Fe(e) {
1055
+ let n = 0, t = [], r = {}, o = [];
1056
+ function i(s) {
1057
+ var a;
1058
+ let l = r[s] = { onStack: true, lowlink: n, index: n++ };
1059
+ if (t.push(s), (a = e.successors(s)) == null || a.forEach(function(u) {
1060
+ if (u in r) {
1061
+ let d = r[u];
1062
+ d != null && d.onStack && (l.lowlink = Math.min(l.lowlink, d.index));
1063
+ } else {
1064
+ i(u);
1065
+ let d = r[u];
1066
+ d && (l.lowlink = Math.min(l.lowlink, d.lowlink));
1067
+ }
1068
+ }), l.lowlink === l.index) {
1069
+ let u = [], d;
1070
+ do {
1071
+ d = t.pop();
1072
+ let c = r[d];
1073
+ c && (c.onStack = false), u.push(d);
1074
+ } while (s !== d);
1075
+ o.push(u);
1076
+ }
1077
+ }
1078
+ return e.nodes().forEach(function(s) {
1079
+ s in r || i(s);
1080
+ }), o;
1081
+ }
1082
+ function $n(e) {
1083
+ return Fe(e).filter(function(n) {
1084
+ var t;
1085
+ let r = n[0];
1086
+ return r ? n.length > 1 || n.length === 1 && ((t = e.outEdges(r, r)) != null ? t : []).length > 0 : false;
1087
+ });
1088
+ }
1089
+ var Un = () => 1;
1090
+ function Jn(e, n, t) {
1091
+ return Kn(e, n || Un, t || function(r) {
1092
+ var o;
1093
+ return (o = e.outEdges(r)) != null ? o : [];
1094
+ });
1095
+ }
1096
+ function Kn(e, n, t) {
1097
+ let r = {}, o = e.nodes();
1098
+ return o.forEach(function(i) {
1099
+ let s = {};
1100
+ r[i] = s, s[i] = { distance: 0, predecessor: "" }, o.forEach(function(a) {
1101
+ i !== a && (s[a] = { distance: Number.POSITIVE_INFINITY, predecessor: "" });
1102
+ }), t(i).forEach(function(a) {
1103
+ let l = a.v === i ? a.w : a.v, u = n(a);
1104
+ s[l] = { distance: u, predecessor: i };
1105
+ });
1106
+ }), o.forEach(function(i) {
1107
+ let s = r[i];
1108
+ s && o.forEach(function(a) {
1109
+ let l = r[a];
1110
+ l && o.forEach(function(u) {
1111
+ let d = l[i], c = s[u], f = l[u];
1112
+ if (d && c && f) {
1113
+ let h = d.distance + c.distance;
1114
+ h < f.distance && (f.distance = h, f.predecessor = c.predecessor);
1115
+ }
1116
+ });
1117
+ });
1118
+ }), r;
1119
+ }
1120
+ var K = class extends Error {
1121
+ constructor(e) {
1122
+ super(e), this.name = "CycleException";
1123
+ }
1124
+ };
1125
+ function Ae(e) {
1126
+ let n = {}, t = {}, r = [];
1127
+ function o(i) {
1128
+ var s;
1129
+ if (i in t) throw new K();
1130
+ i in n || (t[i] = true, n[i] = true, (s = e.predecessors(i)) == null || s.forEach(o), delete t[i], r.push(i));
1131
+ }
1132
+ if (e.sinks().forEach(o), Object.keys(n).length !== e.nodeCount()) throw new K();
1133
+ return r;
1134
+ }
1135
+ function Qn(e) {
1136
+ try {
1137
+ Ae(e);
1138
+ } catch (n) {
1139
+ if (n instanceof K) return false;
1140
+ throw n;
1141
+ }
1142
+ return true;
1143
+ }
1144
+ function Zn(e, n, t, r, o) {
1145
+ Array.isArray(n) || (n = [n]);
1146
+ let i = (a) => {
1147
+ var l;
1148
+ return (l = e.isDirected() ? e.successors(a) : e.neighbors(a)) != null ? l : [];
1149
+ }, s = {};
1150
+ return n.forEach(function(a) {
1151
+ if (!e.hasNode(a)) throw new Error("Graph does not have node: " + a);
1152
+ o = Ve(e, a, t === "post", s, i, r, o);
1153
+ }), o;
1154
+ }
1155
+ function Ve(e, n, t, r, o, i, s) {
1156
+ return n in r || (r[n] = true, t || (s = i(s, n)), o(n).forEach(function(a) {
1157
+ s = Ve(e, a, t, r, o, i, s);
1158
+ }), t && (s = i(s, n))), s;
1159
+ }
1160
+ function De(e, n, t) {
1161
+ return Zn(e, n, t, function(r, o) {
1162
+ return r.push(o), r;
1163
+ }, []);
1164
+ }
1165
+ function et(e, n) {
1166
+ return De(e, n, "post");
1167
+ }
1168
+ function nt(e, n) {
1169
+ return De(e, n, "pre");
1170
+ }
1171
+ function tt(e, n) {
1172
+ var t;
1173
+ let r = new Q(), o = {}, i = new Pe(), s;
1174
+ function a(d) {
1175
+ let c = d.v === s ? d.w : d.v, f = i.priority(c);
1176
+ if (f !== void 0) {
1177
+ let h = n(d);
1178
+ h < f && (o[c] = s, i.decrease(c, h));
1179
+ }
1180
+ }
1181
+ if (e.nodeCount() === 0) return r;
1182
+ e.nodes().forEach(function(d) {
1183
+ i.add(d, Number.POSITIVE_INFINITY), r.setNode(d);
1184
+ });
1185
+ let l = e.nodes()[0];
1186
+ l !== void 0 && i.decrease(l, 0);
1187
+ let u = false;
1188
+ for (; i.size() > 0; ) {
1189
+ if (s = i.removeMin(), s in o) r.setEdge(s, o[s]);
1190
+ else {
1191
+ if (u) throw new Error("Input graph is not connected: " + e);
1192
+ u = true;
1193
+ }
1194
+ (t = e.nodeEdges(s)) == null || t.forEach(a);
1195
+ }
1196
+ return r;
1197
+ }
1198
+ function rt(e, n, t, r) {
1199
+ return ot(e, n, t, r != null ? r : (o) => {
1200
+ var i;
1201
+ return (i = e.outEdges(o)) != null ? i : [];
1202
+ });
1203
+ }
1204
+ function ot(e, n, t, r) {
1205
+ if (t === void 0) return J(e, n, t, r);
1206
+ let o = false, i = e.nodes();
1207
+ for (let s = 0; s < i.length; s++) {
1208
+ let a = i[s];
1209
+ if (a === void 0) continue;
1210
+ let l = r(a);
1211
+ for (let u = 0; u < l.length; u++) {
1212
+ let d = l[u];
1213
+ if (!d) continue;
1214
+ let c = d.v === a ? d.v : d.w, f = c === d.v ? d.w : d.v;
1215
+ t({ v: c, w: f }) < 0 && (o = true);
1216
+ }
1217
+ if (o) return Me(e, n, t, r);
1218
+ }
1219
+ return J(e, n, t, r);
1220
+ }
1221
+ var T = Q;
1222
+ function M(e, n, t, r) {
1223
+ let o = r;
1224
+ for (; e.hasNode(o); ) o = $(r);
1225
+ return t.dummy = n, e.setNode(o, t), o;
1226
+ }
1227
+ function Ye(e) {
1228
+ let n = new T().setGraph(e.graph());
1229
+ return e.nodes().forEach((t) => n.setNode(t, e.node(t))), e.edges().forEach((t) => {
1230
+ let r = n.edge(t.v, t.w) || { weight: 0, minlen: 1 }, o = e.edge(t);
1231
+ n.setEdge(t.v, t.w, { weight: r.weight + o.weight, minlen: Math.max(r.minlen, o.minlen) });
1232
+ }), n;
1233
+ }
1234
+ function Z(e) {
1235
+ let n = new T({ multigraph: e.isMultigraph() }).setGraph(e.graph());
1236
+ return e.nodes().forEach((t) => {
1237
+ e.children(t).length || n.setNode(t, e.node(t));
1238
+ }), e.edges().forEach((t) => {
1239
+ n.setEdge(t, e.edge(t));
1240
+ }), n;
1241
+ }
1242
+ function se(e, n) {
1243
+ let t = e.x, r = e.y, o = n.x - t, i = n.y - r, s = e.width / 2, a = e.height / 2;
1244
+ if (!o && !i) throw new Error("Not possible to find intersection inside of the rectangle");
1245
+ let l, u;
1246
+ return Math.abs(i) * s > Math.abs(o) * a ? (i < 0 && (a = -a), l = a * o / i, u = a) : (o < 0 && (s = -s), l = s, u = s * i / o), { x: t + l, y: r + u };
1247
+ }
1248
+ function P(e) {
1249
+ let n = A(de(e) + 1).map(() => []);
1250
+ return e.nodes().forEach((t) => {
1251
+ let r = e.node(t), o = r.rank;
1252
+ o !== void 0 && (n[o] || (n[o] = []), n[o][r.order] = t);
1253
+ }), n;
1254
+ }
1255
+ function We(e) {
1256
+ let n = e.nodes().map((r) => {
1257
+ let o = e.node(r).rank;
1258
+ return o === void 0 ? Number.MAX_VALUE : o;
1259
+ }), t = R$1(Math.min, n);
1260
+ e.nodes().forEach((r) => {
1261
+ let o = e.node(r);
1262
+ Object.hasOwn(o, "rank") && (o.rank -= t);
1263
+ });
1264
+ }
1265
+ function Be(e) {
1266
+ let n = e.nodes().map((s) => e.node(s).rank).filter((s) => s !== void 0), t = R$1(Math.min, n), r = [];
1267
+ e.nodes().forEach((s) => {
1268
+ let a = e.node(s).rank - t;
1269
+ r[a] || (r[a] = []), r[a].push(s);
1270
+ });
1271
+ let o = 0, i = e.graph().nodeRankFactor;
1272
+ Array.from(r).forEach((s, a) => {
1273
+ s === void 0 && a % i !== 0 ? --o : s !== void 0 && o && s.forEach((l) => e.node(l).rank += o);
1274
+ });
1275
+ }
1276
+ function ae(e, n, t, r) {
1277
+ let o = { width: 0, height: 0 };
1278
+ return arguments.length >= 4 && (o.rank = t, o.order = r), M(e, "border", o, n);
1279
+ }
1280
+ function it(e, n = Xe) {
1281
+ let t = [];
1282
+ for (let r = 0; r < e.length; r += n) {
1283
+ let o = e.slice(r, r + n);
1284
+ t.push(o);
1285
+ }
1286
+ return t;
1287
+ }
1288
+ var Xe = 65535;
1289
+ function R$1(e, n) {
1290
+ if (n.length > Xe) {
1291
+ let t = it(n);
1292
+ return e(...t.map((r) => e(...r)));
1293
+ } else return e(...n);
1294
+ }
1295
+ function de(e) {
1296
+ let t = e.nodes().map((r) => {
1297
+ let o = e.node(r).rank;
1298
+ return o === void 0 ? Number.MIN_VALUE : o;
1299
+ });
1300
+ return R$1(Math.max, t);
1301
+ }
1302
+ function ze(e, n) {
1303
+ let t = { lhs: [], rhs: [] };
1304
+ return e.forEach((r) => {
1305
+ n(r) ? t.lhs.push(r) : t.rhs.push(r);
1306
+ }), t;
1307
+ }
1308
+ function le(e, n) {
1309
+ let t = Date.now();
1310
+ try {
1311
+ return n();
1312
+ } finally {
1313
+ console.log(e + " time: " + (Date.now() - t) + "ms");
1314
+ }
1315
+ }
1316
+ function q(e, n) {
1317
+ return n();
1318
+ }
1319
+ var st = 0;
1320
+ function $(e) {
1321
+ let n = ++st;
1322
+ return e + ("" + n);
1323
+ }
1324
+ function A(e, n, t = 1) {
1325
+ n == null && (n = e, e = 0);
1326
+ let r = (i) => i < n;
1327
+ t < 0 && (r = (i) => n < i);
1328
+ let o = [];
1329
+ for (let i = e; r(i); i += t) o.push(i);
1330
+ return o;
1331
+ }
1332
+ function B(e, n) {
1333
+ let t = {};
1334
+ for (let r of n) e[r] !== void 0 && (t[r] = e[r]);
1335
+ return t;
1336
+ }
1337
+ function X(e, n) {
1338
+ let t;
1339
+ return typeof n == "string" ? t = (r) => r[n] : t = n, Object.entries(e).reduce((r, [o, i]) => (r[o] = t(i, o), r), {});
1340
+ }
1341
+ function He(e, n) {
1342
+ return e.reduce((t, r, o) => (t[r] = n[o], t), {});
1343
+ }
1344
+ var D$1 = "\0";
1345
+ function ee(e, n, t) {
1346
+ var u, d, c, f, h, p;
1347
+ if (!(e && n && t && n.dummy === "edge" && t.dummy === "edge" && n.edgeObj && t.edgeObj && e[n.edgeObj.v] && e[t.edgeObj.v] && e[n.edgeObj.w] && e[t.edgeObj.w])) return 0;
1348
+ let r = true;
1349
+ n.edgeObj.w === t.edgeObj.w && (r = false);
1350
+ let o = r ? (d = (u = e[n.edgeObj.v]) == null ? void 0 : u.rank) != null ? d : NaN + 1 : (f = (c = e[n.edgeObj.w]) == null ? void 0 : c.rank) != null ? f : NaN - 1, i = Object.entries(e).find((E) => {
1351
+ var y, L2;
1352
+ return ((y = E[1].edgeObj) == null ? void 0 : y.v) === n.edgeObj.v && ((L2 = E[1].edgeObj) == null ? void 0 : L2.w) === n.edgeObj.w && E[1].rank === o;
1353
+ }), s = Object.entries(e).find((E) => {
1354
+ var y, L2;
1355
+ return ((y = E[1].edgeObj) == null ? void 0 : y.v) === t.edgeObj.v && ((L2 = E[1].edgeObj) == null ? void 0 : L2.w) === t.edgeObj.w && E[1].rank === o;
1356
+ });
1357
+ if (!i || !s) return 0;
1358
+ let a = (h = i[1].order) != null ? h : NaN, l = (p = s[1].order) != null ? p : NaN;
1359
+ return isNaN(a - l) ? 0 : a - l;
1360
+ }
1361
+ var ue = "3.1.1";
1362
+ var ce = class {
1363
+ constructor() {
1364
+ je(this, "_sentinel");
1365
+ let n = {};
1366
+ n._next = n._prev = n, this._sentinel = n;
1367
+ }
1368
+ dequeue() {
1369
+ let n = this._sentinel, t = n._prev;
1370
+ if (t !== n) return qe(t), t;
1371
+ }
1372
+ enqueue(n) {
1373
+ let t = this._sentinel;
1374
+ n._prev && n._next && qe(n), n._next = t._next, t._next._prev = n, t._next = n, n._prev = t;
1375
+ }
1376
+ toString() {
1377
+ let n = [], t = this._sentinel, r = t._prev;
1378
+ for (; r !== t; ) n.push(JSON.stringify(r, at)), r = r._prev;
1379
+ return "[" + n.join(", ") + "]";
1380
+ }
1381
+ };
1382
+ function qe(e) {
1383
+ e._prev._next = e._next, e._next._prev = e._prev, delete e._next, delete e._prev;
1384
+ }
1385
+ function at(e, n) {
1386
+ if (e !== "_next" && e !== "_prev") return n;
1387
+ }
1388
+ var $e = ce;
1389
+ var dt = () => 1;
1390
+ function be(e, n) {
1391
+ if (e.nodeCount() <= 1) return [];
1392
+ let t = ut(e, n || dt);
1393
+ return lt(t.graph, t.buckets, t.zeroIdx).flatMap((o) => e.outEdges(o.v, o.w) || []);
1394
+ }
1395
+ function lt(e, n, t) {
1396
+ var a;
1397
+ let r = [], o = n[n.length - 1], i = n[0], s;
1398
+ for (; e.nodeCount(); ) {
1399
+ for (; s = i.dequeue(); ) fe(e, n, t, s);
1400
+ for (; s = o.dequeue(); ) fe(e, n, t, s);
1401
+ if (e.nodeCount()) {
1402
+ for (let l = n.length - 2; l > 0; --l) if (s = (a = n[l]) == null ? void 0 : a.dequeue(), s) {
1403
+ r = r.concat(fe(e, n, t, s, true) || []);
1404
+ break;
1405
+ }
1406
+ }
1407
+ }
1408
+ return r;
1409
+ }
1410
+ function fe(e, n, t, r, o) {
1411
+ let i = [], s = o ? i : void 0;
1412
+ return (e.inEdges(r.v) || []).forEach((a) => {
1413
+ let l = e.edge(a), u = e.node(a.v);
1414
+ o && i.push({ v: a.v, w: a.w }), u.out -= l, he(n, t, u);
1415
+ }), (e.outEdges(r.v) || []).forEach((a) => {
1416
+ let l = e.edge(a), u = a.w, d = e.node(u);
1417
+ d.in -= l, he(n, t, d);
1418
+ }), e.removeNode(r.v), s;
1419
+ }
1420
+ function ut(e, n) {
1421
+ let t = new T(), r = 0, o = 0;
1422
+ e.nodes().forEach((a) => {
1423
+ t.setNode(a, { v: a, in: 0, out: 0 });
1424
+ }), e.edges().forEach((a) => {
1425
+ let l = t.edge(a.v, a.w) || 0, u = n(a), d = l + u;
1426
+ t.setEdge(a.v, a.w, d);
1427
+ let c = t.node(a.v), f = t.node(a.w);
1428
+ o = Math.max(o, c.out += u), r = Math.max(r, f.in += u);
1429
+ });
1430
+ let i = ct(o + r + 3).map(() => new $e()), s = r + 1;
1431
+ return t.nodes().forEach((a) => {
1432
+ he(i, s, t.node(a));
1433
+ }), { graph: t, buckets: i, zeroIdx: s };
1434
+ }
1435
+ function he(e, n, t) {
1436
+ var r, o, i;
1437
+ t.out ? t.in ? (i = e[t.out - t.in + n]) == null || i.enqueue(t) : (o = e[e.length - 1]) == null || o.enqueue(t) : (r = e[0]) == null || r.enqueue(t);
1438
+ }
1439
+ function ct(e) {
1440
+ let n = [];
1441
+ for (let t = 0; t < e; t++) n.push(t);
1442
+ return n;
1443
+ }
1444
+ function Ue(e, n) {
1445
+ (e.graph().acyclicer === "greedy" ? be(e, r(e)) : ft(e, n != null ? n : null)).forEach((o) => {
1446
+ let i = e.edge(o);
1447
+ e.removeEdge(o), i.forwardName = o.name, i.reversed = true, e.setEdge(o.w, o.v, i, $("rev"));
1448
+ });
1449
+ function r(o) {
1450
+ return (i) => o.edge(i).weight;
1451
+ }
1452
+ }
1453
+ function ft(e, n) {
1454
+ let t = [], r = {}, o = {};
1455
+ function i(l) {
1456
+ Object.hasOwn(o, l) || (o[l] = true, r[l] = true, e.outEdges(l).forEach((u) => {
1457
+ Object.hasOwn(r, u.w) ? t.push(u) : i(u.w);
1458
+ }), delete r[l]);
1459
+ }
1460
+ function s(l) {
1461
+ var u;
1462
+ Object.hasOwn(o, l) || (o[l] = true, r[l] = true, (u = e.outEdges(l)) == null || u.forEach((d) => {
1463
+ var c, f;
1464
+ Object.hasOwn(r, d.w) || ((c = n.node(l)) == null ? void 0 : c.rank) > ((f = n.node(d.w)) == null ? void 0 : f.rank) && ht(e, d.w, d) ? t.push(d) : s(d.w);
1465
+ }), delete r[l]);
1466
+ }
1467
+ let a = i;
1468
+ return n && typeof n.node == "function" && (a = s), e.sources().forEach(a), e.nodes().forEach(a), t;
1469
+ }
1470
+ function Je(e) {
1471
+ e.edges().forEach((n) => {
1472
+ let t = e.edge(n);
1473
+ if (t.reversed) {
1474
+ e.removeEdge(n);
1475
+ let r = t.forwardName;
1476
+ delete t.reversed, delete t.forwardName, e.setEdge(n.w, n.v, t, r);
1477
+ }
1478
+ });
1479
+ }
1480
+ function ht(e, n, t) {
1481
+ let r = /* @__PURE__ */ new Set();
1482
+ function o(i) {
1483
+ var s;
1484
+ if (e.sources().includes(i)) return true;
1485
+ r.add(i);
1486
+ for (let a of (s = e.inEdges(i)) != null ? s : []) if (!(a.v === t.v && a.w === t.w) && !r.has(a.v) && o(a.v)) return true;
1487
+ return false;
1488
+ }
1489
+ return o(n);
1490
+ }
1491
+ function Ke(e) {
1492
+ e.graph().dummyChains = [], e.edges().forEach((n) => gt(e, n));
1493
+ }
1494
+ function gt(e, n) {
1495
+ let t = n.v, r = e.node(t).rank, o = n.w, i = e.node(o).rank, s = n.name, a = e.edge(n), l = a.labelRank;
1496
+ if (i === r + 1) return;
1497
+ e.removeEdge(n);
1498
+ let u, d, c;
1499
+ for (c = 0, ++r; r < i; ++c, ++r) a.points = [], d = { width: 0, height: 0, edgeLabel: a, edgeObj: n, rank: r }, u = M(e, "edge", d, "_d"), r === l && (d.width = a.width, d.height = a.height, d.dummy = "edge-label", d.labelpos = a.labelpos), e.setEdge(t, u, { weight: a.weight }, s), c === 0 && e.graph().dummyChains.push(u), t = u;
1500
+ e.setEdge(t, o, { weight: a.weight }, s);
1501
+ }
1502
+ function Qe(e) {
1503
+ e.graph().dummyChains.forEach((n) => {
1504
+ let t = e.node(n), r = t.edgeLabel, o;
1505
+ for (e.setEdge(t.edgeObj, r); t.dummy; ) o = e.successors(n)[0], e.removeNode(n), r.points.push({ x: t.x, y: t.y }), t.dummy === "edge-label" && (r.x = t.x, r.y = t.y, r.width = t.width, r.height = t.height), n = o, t = e.node(n);
1506
+ });
1507
+ }
1508
+ function U$1(e) {
1509
+ let n = {};
1510
+ function t(r) {
1511
+ let o = e.node(r);
1512
+ if (Object.hasOwn(n, r)) return o.rank;
1513
+ n[r] = true;
1514
+ let i = e.outEdges(r), s = i ? i.map((l) => l == null ? Number.POSITIVE_INFINITY : t(l.w) - e.edge(l).minlen) : [], a = R$1(Math.min, s);
1515
+ return a === Number.POSITIVE_INFINITY && (a = 0), o.rank = a;
1516
+ }
1517
+ e.sources().forEach(t);
1518
+ }
1519
+ function V(e, n) {
1520
+ return e.node(n.w).rank - e.node(n.v).rank - e.edge(n).minlen;
1521
+ }
1522
+ var ne = mt;
1523
+ function mt(e) {
1524
+ let n = new T({ directed: false }), t = e.nodes();
1525
+ if (t.length === 0) throw new Error("Graph must have at least one node");
1526
+ let r = t[0], o = e.nodeCount();
1527
+ n.setNode(r, {});
1528
+ let i, s;
1529
+ for (; Et(n, e) < o && (i = Lt(n, e), !!i); ) s = n.hasNode(i.v) ? V(e, i) : -V(e, i), yt(n, e, s);
1530
+ return n;
1531
+ }
1532
+ function Et(e, n) {
1533
+ function t(r) {
1534
+ let o = n.nodeEdges(r);
1535
+ o && o.forEach((i) => {
1536
+ let s = i.v, a = r === s ? i.w : s;
1537
+ !e.hasNode(a) && !V(n, i) && (e.setNode(a, {}), e.setEdge(r, a, {}), t(a));
1538
+ });
1539
+ }
1540
+ return e.nodes().forEach(t), e.nodeCount();
1541
+ }
1542
+ function Lt(e, n) {
1543
+ return n.edges().reduce((r, o) => {
1544
+ let i = Number.POSITIVE_INFINITY;
1545
+ return e.hasNode(o.v) !== e.hasNode(o.w) && (i = V(n, o)), i < r[0] ? [i, o] : r;
1546
+ }, [Number.POSITIVE_INFINITY, null])[1];
1547
+ }
1548
+ function yt(e, n, t) {
1549
+ e.nodes().forEach((r) => n.node(r).rank += t);
1550
+ }
1551
+ var { preorder: wt, postorder: Nt } = H, en = Y;
1552
+ Y.initLowLimValues = pe;
1553
+ Y.initCutValues = ge;
1554
+ Y.calcCutValue = nn;
1555
+ Y.leaveEdge = rn;
1556
+ Y.enterEdge = on;
1557
+ Y.exchangeEdges = sn;
1558
+ function Y(e) {
1559
+ e = Ye(e), U$1(e);
1560
+ let n = ne(e);
1561
+ pe(n), ge(n, e);
1562
+ let t, r;
1563
+ for (; t = rn(n); ) r = on(n, e, t), sn(n, e, t, r);
1564
+ }
1565
+ function ge(e, n) {
1566
+ let t = Nt(e, e.nodes());
1567
+ t = t.slice(0, t.length - 1), t.forEach((r) => Gt(e, n, r));
1568
+ }
1569
+ function Gt(e, n, t) {
1570
+ let o = e.node(t).parent, i = e.edge(t, o);
1571
+ i.cutvalue = nn(e, n, t);
1572
+ }
1573
+ function nn(e, n, t) {
1574
+ let o = e.node(t).parent, i = true, s = n.edge(t, o), a = 0;
1575
+ s || (i = false, s = n.edge(o, t)), a = s.weight;
1576
+ let l = n.nodeEdges(t);
1577
+ return l && l.forEach((u) => {
1578
+ let d = u.v === t, c = d ? u.w : u.v;
1579
+ if (c !== o) {
1580
+ let f = d === i, h = n.edge(u).weight;
1581
+ if (a += f ? h : -h, kt(e, t, c)) {
1582
+ let E = e.edge(t, c).cutvalue;
1583
+ a += f ? -E : E;
1584
+ }
1585
+ }
1586
+ }), a;
1587
+ }
1588
+ function pe(e, n) {
1589
+ arguments.length < 2 && (n = e.nodes()[0]), tn(e, {}, 1, n);
1590
+ }
1591
+ function tn(e, n, t, r, o) {
1592
+ let i = t, s = e.node(r);
1593
+ n[r] = true;
1594
+ let a = e.neighbors(r);
1595
+ return a && a.forEach((l) => {
1596
+ Object.hasOwn(n, l) || (t = tn(e, n, t, l, r));
1597
+ }), s.low = i, s.lim = t++, o ? s.parent = o : delete s.parent, t;
1598
+ }
1599
+ function rn(e) {
1600
+ return e.edges().find((n) => e.edge(n).cutvalue < 0);
1601
+ }
1602
+ function on(e, n, t) {
1603
+ let r = t.v, o = t.w;
1604
+ n.hasEdge(r, o) || (r = t.w, o = t.v);
1605
+ let i = e.node(r), s = e.node(o), a = i, l = false;
1606
+ return i.lim > s.lim && (a = s, l = true), n.edges().filter((d) => l === Ze(e, e.node(d.v), a) && l !== Ze(e, e.node(d.w), a)).reduce((d, c) => V(n, c) < V(n, d) ? c : d);
1607
+ }
1608
+ function sn(e, n, t, r) {
1609
+ let o = t.v, i = t.w;
1610
+ e.removeEdge(o, i), e.setEdge(r.v, r.w, {}), pe(e), ge(e, n), vt(e, n);
1611
+ }
1612
+ function vt(e, n) {
1613
+ let t = e.nodes().find((o) => !e.node(o).parent);
1614
+ if (!t) return;
1615
+ let r = wt(e, [t]);
1616
+ r = r.slice(1), r.forEach((o) => {
1617
+ let s = e.node(o).parent, a = n.edge(o, s), l = false;
1618
+ a || (a = n.edge(s, o), l = true), n.node(o).rank = n.node(s).rank + (l ? a.minlen : -a.minlen);
1619
+ });
1620
+ }
1621
+ function kt(e, n, t) {
1622
+ return e.hasEdge(n, t);
1623
+ }
1624
+ function Ze(e, n, t) {
1625
+ return t.low <= n.lim && n.lim <= t.lim;
1626
+ }
1627
+ var dn = xt;
1628
+ function xt(e) {
1629
+ let n = e.graph().ranker;
1630
+ if (typeof n == "function") return n(e);
1631
+ switch (n) {
1632
+ case "network-simplex":
1633
+ an(e);
1634
+ break;
1635
+ case "tight-tree":
1636
+ Ot(e);
1637
+ break;
1638
+ case "longest-path":
1639
+ _t(e);
1640
+ break;
1641
+ case "none":
1642
+ break;
1643
+ default:
1644
+ an(e);
1645
+ }
1646
+ }
1647
+ var _t = U$1;
1648
+ function Ot(e) {
1649
+ U$1(e), ne(e);
1650
+ }
1651
+ function an(e) {
1652
+ en(e);
1653
+ }
1654
+ var ln = Ct;
1655
+ function Ct(e) {
1656
+ let n = jt(e), t = e.graph();
1657
+ if (!Array.isArray(t.dummyChains)) return;
1658
+ t.dummyChains.forEach((o) => {
1659
+ let i = e.node(o), s = i.edgeObj, a = Tt(e, n, s.v, s.w), l = a.path, u = a.lca, d = 0, c = l[d], f = true;
1660
+ for (; o !== s.w; ) {
1661
+ if (i = e.node(o), f) {
1662
+ for (; (c = l[d]) !== u && e.node(c).maxRank < i.rank; ) d++;
1663
+ c === u && (f = false);
1664
+ }
1665
+ if (!f) {
1666
+ for (; d < l.length - 1 && e.node(l[d + 1]).minRank <= i.rank; ) d++;
1667
+ c = l[d];
1668
+ }
1669
+ c !== void 0 && e.setParent(o, c), o = e.successors(o)[0];
1670
+ }
1671
+ });
1672
+ }
1673
+ function Tt(e, n, t, r) {
1674
+ let o = [], i = [], s = Math.min(n[t].low, n[r].low), a = Math.max(n[t].lim, n[r].lim), l;
1675
+ l = t;
1676
+ do
1677
+ l = e.parent(l), o.push(l);
1678
+ while (l && (n[l].low > s || a > n[l].lim));
1679
+ let u = l, d = r;
1680
+ for (; (d = e.parent(d)) !== u; ) i.push(d);
1681
+ return { path: o.concat(i.reverse()), lca: u };
1682
+ }
1683
+ function jt(e) {
1684
+ let n = {}, t = 0;
1685
+ function r(o) {
1686
+ let i = t;
1687
+ e.children(o).forEach(r), n[o] = { low: i, lim: t++ };
1688
+ }
1689
+ return e.children(D$1).forEach(r), n;
1690
+ }
1691
+ function un(e) {
1692
+ let n = M(e, "root", {}, "_root"), t = Rt(e), r = Object.values(t), o = R$1(Math.max, r) - 1, i = 2 * o + 1;
1693
+ e.graph().nestingRoot = n, e.edges().forEach((a) => e.edge(a).minlen *= i);
1694
+ let s = It(e) + 1;
1695
+ e.children(D$1).forEach((a) => {
1696
+ cn(e, n, i, s, o, t, a);
1697
+ }), e.graph().nodeRankFactor = i;
1698
+ }
1699
+ function cn(e, n, t, r, o, i, s) {
1700
+ var c;
1701
+ let a = e.children(s);
1702
+ if (!a.length) {
1703
+ s !== n && e.setEdge(n, s, { weight: 0, minlen: t });
1704
+ return;
1705
+ }
1706
+ let l = ae(e, "_bt"), u = ae(e, "_bb"), d = e.node(s);
1707
+ e.setParent(l, s), d.borderTop = l, e.setParent(u, s), d.borderBottom = u, a.forEach((f) => {
1708
+ var b;
1709
+ cn(e, n, t, r, o, i, f);
1710
+ let h = e.node(f), p = h.borderTop ? h.borderTop : f, E = h.borderBottom ? h.borderBottom : f, y = h.borderTop ? r : 2 * r, L2 = p !== E ? 1 : o - ((b = i[s]) != null ? b : 0) + 1;
1711
+ e.setEdge(l, p, { weight: y, minlen: L2, nestingEdge: true }), e.setEdge(E, u, { weight: y, minlen: L2, nestingEdge: true });
1712
+ }), e.parent(s) || e.setEdge(n, l, { weight: 0, minlen: o + ((c = i[s]) != null ? c : 0) });
1713
+ }
1714
+ function Rt(e) {
1715
+ let n = {};
1716
+ function t(r, o) {
1717
+ let i = e.children(r);
1718
+ i && i.length && i.forEach((s) => t(s, o + 1)), n[r] = o;
1719
+ }
1720
+ return e.children(D$1).forEach((r) => t(r, 1)), n;
1721
+ }
1722
+ function It(e) {
1723
+ return e.edges().reduce((n, t) => n + e.edge(t).weight, 0);
1724
+ }
1725
+ function fn(e) {
1726
+ let n = e.graph();
1727
+ e.removeNode(n.nestingRoot), delete n.nestingRoot, e.edges().forEach((t) => {
1728
+ e.edge(t).nestingEdge && e.removeEdge(t);
1729
+ });
1730
+ }
1731
+ var bn = Mt;
1732
+ function Mt(e) {
1733
+ function n(t) {
1734
+ let r = e.children(t), o = e.node(t);
1735
+ if (r.length && r.forEach(n), o && Object.hasOwn(o, "minRank")) {
1736
+ o.borderLeft = [], o.borderRight = [];
1737
+ for (let i = o.minRank, s = o.maxRank + 1; i < s; ++i) hn(e, "borderLeft", "_bl", t, o, i), hn(e, "borderRight", "_br", t, o, i);
1738
+ }
1739
+ }
1740
+ e.children(D$1).forEach(n);
1741
+ }
1742
+ function hn(e, n, t, r, o, i) {
1743
+ let s = { width: 0, height: 0, rank: i, borderType: n }, a = o[n][i - 1], l = M(e, "border", s, t);
1744
+ o[n][i] = l, e.setParent(l, r), a && e.setEdge(a, l, { weight: 1 });
1745
+ }
1746
+ function pn(e) {
1747
+ var t;
1748
+ let n = (t = e.graph().rankdir) == null ? void 0 : t.toLowerCase();
1749
+ (n === "lr" || n === "rl") && En(e);
1750
+ }
1751
+ function mn(e) {
1752
+ var t;
1753
+ let n = (t = e.graph().rankdir) == null ? void 0 : t.toLowerCase();
1754
+ (n === "bt" || n === "rl") && Pt(e), (n === "lr" || n === "rl") && (Ft(e), En(e));
1755
+ }
1756
+ function En(e) {
1757
+ e.nodes().forEach((n) => gn(e.node(n))), e.edges().forEach((n) => gn(e.edge(n)));
1758
+ }
1759
+ function gn(e) {
1760
+ let n = e.width;
1761
+ e.width = e.height, e.height = n;
1762
+ }
1763
+ function Pt(e) {
1764
+ e.nodes().forEach((n) => me(e.node(n))), e.edges().forEach((n) => {
1765
+ var r;
1766
+ let t = e.edge(n);
1767
+ (r = t.points) == null || r.forEach(me), Object.hasOwn(t, "y") && me(t);
1768
+ });
1769
+ }
1770
+ function me(e) {
1771
+ e.y = -e.y;
1772
+ }
1773
+ function Ft(e) {
1774
+ e.nodes().forEach((n) => Ee(e.node(n))), e.edges().forEach((n) => {
1775
+ var r;
1776
+ let t = e.edge(n);
1777
+ (r = t.points) == null || r.forEach(Ee), Object.hasOwn(t, "x") && Ee(t);
1778
+ });
1779
+ }
1780
+ function Ee(e) {
1781
+ let n = e.x;
1782
+ e.x = e.y, e.y = n;
1783
+ }
1784
+ function Le(e, n = null) {
1785
+ let t = {}, r = e.nodes().filter((d) => !e.children(d).length), o = r.map((d) => e.node(d).rank), i = R$1(Math.max, o), s = A(i + 1).map(() => []);
1786
+ function a(d) {
1787
+ if (t[d]) return;
1788
+ t[d] = true;
1789
+ let c = e.node(d);
1790
+ s[c.rank].push(d);
1791
+ let f = e.successors(d);
1792
+ f && [...f].sort((p, E) => u(p, E)).forEach(a);
1793
+ }
1794
+ r.sort((d, c) => e.node(d).rank - e.node(c).rank).forEach(a);
1795
+ function u(d, c) {
1796
+ let f = e.node(d), h = e.node(c);
1797
+ return ee(n, f, h);
1798
+ }
1799
+ return s;
1800
+ }
1801
+ function ye(e, n) {
1802
+ let t = 0;
1803
+ for (let r = 1; r < n.length; ++r) t += Vt(e, n[r - 1], n[r]);
1804
+ return t;
1805
+ }
1806
+ function Vt(e, n, t) {
1807
+ let r = He(t, t.map((u, d) => d)), o = n.flatMap((u) => {
1808
+ let d = e.outEdges(u);
1809
+ return d ? d.map((c) => ({ pos: r[c.w], weight: e.edge(c).weight })).sort((c, f) => c.pos - f.pos) : [];
1810
+ }), i = 1;
1811
+ for (; i < t.length; ) i <<= 1;
1812
+ let s = 2 * i - 1;
1813
+ i -= 1;
1814
+ let a = new Array(s).fill(0), l = 0;
1815
+ return o.forEach((u) => {
1816
+ let d = u.pos + i;
1817
+ a[d] += u.weight;
1818
+ let c = 0;
1819
+ for (; d > 0; ) d % 2 && (c += a[d + 1]), d = d - 1 >> 1, a[d] += u.weight;
1820
+ l += u.weight * c;
1821
+ }), l;
1822
+ }
1823
+ function we(e, n = []) {
1824
+ return n.map((t) => {
1825
+ let r = e.inEdges(t);
1826
+ if (!r || !r.length) return { v: t };
1827
+ {
1828
+ let o = r.reduce((i, s) => {
1829
+ let a = e.edge(s), l = e.node(s.v);
1830
+ return { sum: i.sum + a.weight * l.order, weight: i.weight + a.weight };
1831
+ }, { sum: 0, weight: 0 });
1832
+ return { v: t, barycenter: o.sum / o.weight, weight: o.weight };
1833
+ }
1834
+ });
1835
+ }
1836
+ function Ne(e, n) {
1837
+ let t = {};
1838
+ e.forEach((o, i) => {
1839
+ let s = { indegree: 0, in: [], out: [], vs: [o.v], i };
1840
+ o.barycenter !== void 0 && (s.barycenter = o.barycenter, s.weight = o.weight), t[o.v] = s;
1841
+ }), n.edges().forEach((o) => {
1842
+ let i = t[o.v], s = t[o.w];
1843
+ i !== void 0 && s !== void 0 && (s.indegree++, i.out.push(s));
1844
+ });
1845
+ let r = Object.values(t).filter((o) => !o.indegree);
1846
+ return Dt(r);
1847
+ }
1848
+ function Dt(e) {
1849
+ let n = [];
1850
+ function t(o) {
1851
+ return (i) => {
1852
+ i.merged || (i.barycenter === void 0 || o.barycenter === void 0 || i.barycenter >= o.barycenter) && Yt(o, i);
1853
+ };
1854
+ }
1855
+ function r(o) {
1856
+ return (i) => {
1857
+ i.in.push(o), --i.indegree === 0 && e.push(i);
1858
+ };
1859
+ }
1860
+ for (; e.length; ) {
1861
+ let o = e.pop();
1862
+ n.push(o), o.in.reverse().forEach(t(o)), o.out.forEach(r(o));
1863
+ }
1864
+ return n.filter((o) => !o.merged).map((o) => B(o, ["vs", "i", "barycenter", "weight"]));
1865
+ }
1866
+ function Yt(e, n) {
1867
+ let t = 0, r = 0;
1868
+ e.weight && (t += e.barycenter * e.weight, r += e.weight), n.weight && (t += n.barycenter * n.weight, r += n.weight), e.vs = n.vs.concat(e.vs), e.barycenter = t / r, e.weight = r, e.i = Math.min(n.i, e.i), n.merged = true;
1869
+ }
1870
+ function Ge(e, n, t, r, o) {
1871
+ let i = {}, s = null, a = null, l = o;
1872
+ typeof n == "boolean" ? (l = n, i = {}) : n && (i = n, s = t != null ? t : null, a = r != null ? r : null);
1873
+ let u = ze(e, (L2) => Object.hasOwn(L2, "barycenter")), d = u.lhs, c = u.rhs.sort((L2, b) => b.i - L2.i), f = [], h = 0, p = 0, E = 0;
1874
+ d.sort(Wt(a, s, !!l));
1875
+ for (let [L2, b] of Object.entries(i)) {
1876
+ let g = d.findIndex((m) => m.vs[0] === L2);
1877
+ d.splice(g + 1, 0, b);
1878
+ }
1879
+ E = Ln(f, c, E), d.forEach((L2) => {
1880
+ E += L2.vs.length, f.push(L2.vs), h += L2.barycenter * L2.weight, p += L2.weight, E = Ln(f, c, E);
1881
+ });
1882
+ let y = { vs: f.flat(1) };
1883
+ return p && (y.barycenter = h / p, y.weight = p), y;
1884
+ }
1885
+ function Ln(e, n, t) {
1886
+ let r;
1887
+ for (; n.length && (r = n[n.length - 1]).i <= t; ) n.pop(), e.push(r.vs), t++;
1888
+ return t;
1889
+ }
1890
+ function Wt(e, n, t) {
1891
+ return (r, o) => {
1892
+ if (r.barycenter < o.barycenter) return -1;
1893
+ if (r.barycenter > o.barycenter) return 1;
1894
+ if (e && (typeof r.vs[0] == "string" || typeof o.vs[0] == "string")) {
1895
+ let i = e.node(r.vs[0]), s = e.node(o.vs[0]), a = ee(n, i, s);
1896
+ if (a !== 0) return a;
1897
+ }
1898
+ return t ? o.i - r.i : r.i - o.i;
1899
+ };
1900
+ }
1901
+ function te(e, n, t, r, o) {
1902
+ var L2, b, g, m, w, k, _, C, j, I, S;
1903
+ let i = null, s = o;
1904
+ typeof r == "boolean" ? (s = r, i = null) : r !== void 0 && (i = r);
1905
+ let a = e.children(n), l = e.node(n), u = l ? l.borderLeft : void 0, d = l ? l.borderRight : void 0, c = {};
1906
+ u && (a = a.filter((G) => G !== u && G !== d));
1907
+ let f = we(e, a);
1908
+ f.forEach((G) => {
1909
+ if (e.children(G.v).length) {
1910
+ let { result: x } = te(e, G.v, t, i, s);
1911
+ c[G.v] = x, Object.hasOwn(x, "barycenter") && Xt(G, x);
1912
+ }
1913
+ });
1914
+ let h = Ne(f, t);
1915
+ Bt(h, c);
1916
+ let p = {}, E = false;
1917
+ for (let G = 0; G < h.length; G++) for (let x = G + 1; x < h.length; x++) if (!(!h[G] || !h[x] || !((L2 = h[G]) != null && L2.barycenter) || !((b = h[x]) != null && b.barycenter)) && ((g = h[G]) == null ? void 0 : g.barycenter) === h[x].barycenter) {
1918
+ let v = (w = (m = h[G]) == null ? void 0 : m.vs[0]) != null ? w : "", N = (_ = (k = h[x]) == null ? void 0 : k.vs[0]) != null ? _ : "", O = e.node(v), W = e.node(N);
1919
+ if (O.dummy === "edge" && W.dummy === "edge" && ((C = O.edgeObj) == null ? void 0 : C.v) === ((j = W.edgeObj) == null ? void 0 : j.v) && ((I = O.edgeObj) == null ? void 0 : I.w) === ((S = W.edgeObj) == null ? void 0 : S.w)) if (O.edgeLabel.reversed) {
1920
+ p[N] = h[G], h.splice(G, 1), G--;
1921
+ break;
1922
+ } else p[v] = h[x], h.splice(x, 1), x--;
1923
+ else E = true;
1924
+ }
1925
+ let y = Ge(h, p, i, e, s);
1926
+ if (u && d) {
1927
+ y.vs = [u, y.vs, d].flat(1);
1928
+ let G = e.predecessors(u);
1929
+ if (G && G.length) {
1930
+ let x = e.node(G[0]), v = e.predecessors(d), N = e.node(v[0]);
1931
+ Object.hasOwn(y, "barycenter") || (y.barycenter = 0, y.weight = 0), y.barycenter = (y.barycenter * y.weight + x.order + N.order) / (y.weight + 2), y.weight += 2;
1932
+ }
1933
+ }
1934
+ return Object.defineProperty(y, "result", { value: y, enumerable: false, configurable: true, writable: true }), Object.defineProperty(y, "usedBias", { value: E, enumerable: false, configurable: true, writable: true }), y;
1935
+ }
1936
+ function Bt(e, n) {
1937
+ e.forEach((t) => {
1938
+ t.vs = t.vs.flatMap((r) => n[r] ? n[r].vs : r);
1939
+ });
1940
+ }
1941
+ function Xt(e, n) {
1942
+ e.barycenter !== void 0 ? (e.barycenter = (e.barycenter * e.weight + n.barycenter * n.weight) / (e.weight + n.weight), e.weight += n.weight) : (e.barycenter = n.barycenter, e.weight = n.weight);
1943
+ }
1944
+ function ve(e, n, t, r) {
1945
+ r || (r = e.nodes());
1946
+ let o = zt(e), i = new T({ compound: true }).setGraph({ root: o }).setDefaultNodeLabel((s) => e.node(s));
1947
+ return r.forEach((s) => {
1948
+ let a = e.node(s), l = e.parent(s);
1949
+ if (a.rank === n || a.minRank <= n && n <= a.maxRank) {
1950
+ i.setNode(s), i.setParent(s, l || o);
1951
+ let u = e[t](s);
1952
+ u && u.forEach((d) => {
1953
+ let c = d.v === s ? d.w : d.v, f = i.edge(c, s), h = f !== void 0 ? f.weight : 0;
1954
+ i.setEdge(c, s, { weight: e.edge(d).weight + h });
1955
+ }), Object.hasOwn(a, "minRank") && i.setNode(s, { borderLeft: a.borderLeft[n], borderRight: a.borderRight[n] });
1956
+ }
1957
+ }), i;
1958
+ }
1959
+ function zt(e) {
1960
+ let n;
1961
+ for (; e.hasNode(n = $("_root")); ) ;
1962
+ return n;
1963
+ }
1964
+ function ke(e, n, t) {
1965
+ let r = {}, o;
1966
+ t.forEach((i) => {
1967
+ let s = e.parent(i), a, l;
1968
+ for (; s; ) {
1969
+ if (a = e.parent(s), a ? (l = r[a], r[a] = s) : (l = o, o = s), l && l !== s) {
1970
+ n.setEdge(l, s);
1971
+ return;
1972
+ }
1973
+ s = a;
1974
+ }
1975
+ });
1976
+ }
1977
+ function re(e, n = {}, t = null) {
1978
+ if (typeof n.customOrder == "function") {
1979
+ n.customOrder(e, re);
1980
+ return;
1981
+ }
1982
+ let r = de(e), o = yn(e, A(1, r + 1), "inEdges"), i = yn(e, A(r - 1, -1, -1), "outEdges"), s = Le(e, t);
1983
+ if (wn(e, s), n.disableOptimalOrderHeuristic) return;
1984
+ let a = Number.POSITIVE_INFINITY, l, u = n.constraints || [];
1985
+ for (let d = 0, c = 0; c < 4; ++d, ++c) {
1986
+ Ht(d % 2 ? o : i, d % 4 >= 2, u, t), s = P(e);
1987
+ let f = ye(e, s);
1988
+ f < a ? (c = 0, l = Object.assign({}, s), a = f) : f === a && (l = structuredClone(s));
1989
+ }
1990
+ wn(e, l);
1991
+ }
1992
+ function yn(e, n, t) {
1993
+ let r = /* @__PURE__ */ new Map(), o = (i, s) => {
1994
+ r.has(i) || r.set(i, []), r.get(i).push(s);
1995
+ };
1996
+ for (let i of e.nodes()) {
1997
+ let s = e.node(i);
1998
+ if (typeof s.rank == "number" && o(s.rank, i), typeof s.minRank == "number" && typeof s.maxRank == "number") for (let a = s.minRank; a <= s.maxRank; a++) a !== s.rank && o(a, i);
1999
+ }
2000
+ return n.map(function(i) {
2001
+ return ve(e, i, t, r.get(i) || []);
2002
+ });
2003
+ }
2004
+ function Ht(e, n, t, r) {
2005
+ let o = true, i = new T();
2006
+ e.forEach(function(s) {
2007
+ t.forEach((d) => i.setEdge(d.left, d.right));
2008
+ let a = s.graph().root, { result: l, usedBias: u } = te(s, a, i, r, o);
2009
+ n && u && (o = !o), l.vs.forEach((d, c) => s.node(d).order = c), ke(s, i, l.vs);
2010
+ });
2011
+ }
2012
+ function wn(e, n) {
2013
+ Object.values(n).forEach((t) => t.forEach((r, o) => e.node(r).order = o));
2014
+ }
2015
+ function qt(e, n) {
2016
+ let t = {};
2017
+ function r(o, i) {
2018
+ let s = 0, a = 0, l = o.length, u = i[i.length - 1];
2019
+ return i.forEach((d, c) => {
2020
+ let f = Ut(e, d), h = f ? e.node(f).order : l;
2021
+ (f || d === u) && (i.slice(a, c + 1).forEach((p) => {
2022
+ let E = e.predecessors(p);
2023
+ E && E.forEach((y) => {
2024
+ let L2 = e.node(y), b = L2.order;
2025
+ (b < s || h < b) && !(L2.dummy && e.node(p).dummy) && Gn(t, y, p);
2026
+ });
2027
+ }), a = c + 1, s = h);
2028
+ }), i;
2029
+ }
2030
+ return n.length && n.reduce(r), t;
2031
+ }
2032
+ function $t(e, n) {
2033
+ let t = {};
2034
+ function r(i, s, a, l, u) {
2035
+ A(s, a).forEach((d) => {
2036
+ let c = i[d];
2037
+ if (c !== void 0 && e.node(c).dummy) {
2038
+ let f = e.predecessors(c);
2039
+ f && f.forEach((h) => {
2040
+ if (h === void 0) return;
2041
+ let p = e.node(h);
2042
+ p.dummy && (p.order < l || p.order > u) && Gn(t, h, c);
2043
+ });
2044
+ }
2045
+ });
2046
+ }
2047
+ function o(i, s) {
2048
+ let a = -1, l = -1, u = 0;
2049
+ return s.forEach((d, c) => {
2050
+ if (e.node(d).dummy === "border") {
2051
+ let f = e.predecessors(d);
2052
+ if (f && f.length) {
2053
+ let h = f[0];
2054
+ if (h === void 0) return;
2055
+ l = e.node(h).order, r(s, u, c, a, l), u = c, a = l;
2056
+ }
2057
+ }
2058
+ r(s, u, s.length, l, i.length);
2059
+ }), s;
2060
+ }
2061
+ return n.length && n.reduce(o), t;
2062
+ }
2063
+ function Ut(e, n) {
2064
+ if (e.node(n).dummy) {
2065
+ let t = e.predecessors(n);
2066
+ if (t) return t.find((r) => e.node(r).dummy);
2067
+ }
2068
+ }
2069
+ function Gn(e, n, t) {
2070
+ if (n > t) {
2071
+ let o = n;
2072
+ n = t, t = o;
2073
+ }
2074
+ let r = e[n];
2075
+ r || (e[n] = r = {}), r[t] = true;
2076
+ }
2077
+ function Jt(e, n, t) {
2078
+ if (n > t) {
2079
+ let o = n;
2080
+ n = t, t = o;
2081
+ }
2082
+ let r = e[n];
2083
+ return r !== void 0 && Object.hasOwn(r, t);
2084
+ }
2085
+ function Kt(e, n, t, r, o) {
2086
+ let i = {}, s = {}, a = {};
2087
+ return n.forEach((l) => {
2088
+ l.forEach((u, d) => {
2089
+ i[u] = u, s[u] = u, a[u] = d;
2090
+ });
2091
+ }), n.forEach((l) => {
2092
+ let u = -1, d = -1, c = false, f = l, h = l.findIndex((p) => (o == null ? void 0 : o.includes(p)) || Nn(p, e, o));
2093
+ h > 0 && (f = [l[h], ...l.slice(0, h), ...l.slice(h + 1)], c = true), f.forEach((p) => {
2094
+ var y;
2095
+ let E = r(p);
2096
+ if (E && E.length) {
2097
+ o != null && o.includes(p) && (E = E.filter((g) => Nn(g, e, o)));
2098
+ let L2 = E.sort((g, m) => {
2099
+ let w = a[g], k = a[m];
2100
+ return (w !== void 0 ? w : 0) - (k !== void 0 ? k : 0);
2101
+ }), b = (L2.length - 1) / 2;
2102
+ for (let g = Math.floor(b), m = Math.ceil(b); g <= m; ++g) {
2103
+ let w = L2[g];
2104
+ if (w === void 0) continue;
2105
+ let k = a[w];
2106
+ if (k !== void 0 && s[p] === p && u < k && a[w] !== d && !Jt(t, p, w)) {
2107
+ let _ = i[w];
2108
+ _ !== void 0 && (s[w] = p, s[p] = i[p] = _, u = k, c && (u = -1, d = (y = a[w]) != null ? y : -1, c = false));
2109
+ }
2110
+ }
2111
+ }
2112
+ });
2113
+ }), { root: i, align: s };
2114
+ }
2115
+ function Qt(e, n, t, r, o = false) {
2116
+ let i = {}, s = Zt(e, n, t, o), a = o ? "borderLeft" : "borderRight";
2117
+ function l(h, p) {
2118
+ let E = s.nodes().slice(), y = {}, L2 = E.pop();
2119
+ for (; L2; ) {
2120
+ if (y[L2]) h(L2);
2121
+ else {
2122
+ y[L2] = true, E.push(L2);
2123
+ for (let b of p(L2)) E.push(b);
2124
+ }
2125
+ L2 = E.pop();
2126
+ }
2127
+ }
2128
+ function u(h) {
2129
+ let p = s.inEdges(h);
2130
+ p ? i[h] = p.reduce((E, y) => {
2131
+ var g;
2132
+ let L2 = (g = i[y.v]) != null ? g : 0, b = s.edge(y);
2133
+ return Math.max(E, L2 + (b !== void 0 ? b : 0));
2134
+ }, 0) : i[h] = 0;
2135
+ }
2136
+ function d(h) {
2137
+ let p = s.outEdges(h), E = Number.POSITIVE_INFINITY;
2138
+ p && (E = p.reduce((L2, b) => {
2139
+ let g = i[b.w], m = s.edge(b);
2140
+ return Math.min(L2, (g !== void 0 ? g : 0) - (m !== void 0 ? m : 0));
2141
+ }, Number.POSITIVE_INFINITY));
2142
+ let y = e.node(h);
2143
+ E !== Number.POSITIVE_INFINITY && y.borderType !== a && (i[h] = Math.max(i[h] !== void 0 ? i[h] : 0, E));
2144
+ }
2145
+ function c(h) {
2146
+ return s.predecessors(h) || [];
2147
+ }
2148
+ function f(h) {
2149
+ return s.successors(h) || [];
2150
+ }
2151
+ return l(u, c), l(d, f), Object.keys(r).forEach((h) => {
2152
+ var E;
2153
+ let p = t[h];
2154
+ p !== void 0 && (i[h] = (E = i[p]) != null ? E : 0);
2155
+ }), i;
2156
+ }
2157
+ function Zt(e, n, t, r) {
2158
+ let o = new T(), i = e.graph(), s = rr(i.nodesep, i.edgesep, r);
2159
+ return n.forEach((a) => {
2160
+ let l;
2161
+ a.forEach((u) => {
2162
+ let d = t[u];
2163
+ if (d !== void 0) {
2164
+ if (o.setNode(d), l !== void 0) {
2165
+ let c = t[l];
2166
+ if (c !== void 0) {
2167
+ let f = o.edge(c, d);
2168
+ o.setEdge(c, d, Math.max(s(e, u, l), f || 0));
2169
+ }
2170
+ }
2171
+ l = u;
2172
+ }
2173
+ });
2174
+ }), o;
2175
+ }
2176
+ function er(e, n) {
2177
+ return Object.values(n).reduce((t, r) => {
2178
+ let o = Number.NEGATIVE_INFINITY, i = Number.POSITIVE_INFINITY;
2179
+ Object.entries(r).forEach(([a, l]) => {
2180
+ let u = or(e, a) / 2;
2181
+ o = Math.max(l + u, o), i = Math.min(l - u, i);
2182
+ });
2183
+ let s = o - i;
2184
+ return s < t[0] && (t = [s, r]), t;
2185
+ }, [Number.POSITIVE_INFINITY, null])[1];
2186
+ }
2187
+ function nr(e, n) {
2188
+ let t = Object.values(n), r = R$1(Math.min, t), o = R$1(Math.max, t);
2189
+ ["u", "d"].forEach((i) => {
2190
+ ["l", "r"].forEach((s) => {
2191
+ let a = i + s, l = e[a];
2192
+ if (!l || l === n) return;
2193
+ let u = Object.values(l), d = r - R$1(Math.min, u);
2194
+ s !== "l" && (d = o - R$1(Math.max, u)), d && (e[a] = X(l, (c) => c + d));
2195
+ });
2196
+ });
2197
+ }
2198
+ function tr(e, n = void 0) {
2199
+ let t = e.ul;
2200
+ return t ? X(t, (r, o) => {
2201
+ var s, a;
2202
+ if (n) {
2203
+ let l = n.toLowerCase(), u = e[l];
2204
+ if (u && u[o] !== void 0) return u[o];
2205
+ }
2206
+ let i = Object.values(e).map((l) => {
2207
+ let u = l[o];
2208
+ return u !== void 0 ? u : 0;
2209
+ }).sort((l, u) => l - u);
2210
+ return (((s = i[1]) != null ? s : 0) + ((a = i[2]) != null ? a : 0)) / 2;
2211
+ }) : {};
2212
+ }
2213
+ function vn(e, n) {
2214
+ let t = P(e), r = Object.assign(qt(e, t), $t(e, t)), o = {}, i;
2215
+ ["u", "d"].forEach((a) => {
2216
+ i = a === "u" ? t : Object.values(t).reverse(), ["l", "r"].forEach((l) => {
2217
+ l === "r" && (i = i.map((f) => Object.values(f).reverse()));
2218
+ let d = Kt(e, i, r, (f) => (a === "u" ? e.predecessors(f) : e.successors(f)) || [], n), c = Qt(e, i, d.root, d.align, l === "r");
2219
+ l === "r" && (c = X(c, (f) => -f)), o[a + l] = c;
2220
+ });
2221
+ });
2222
+ let s = er(e, o);
2223
+ return nr(o, s), tr(o, e.graph().align);
2224
+ }
2225
+ function rr(e, n, t) {
2226
+ return (r, o, i) => {
2227
+ let s = r.node(o), a = r.node(i), l = 0, u;
2228
+ if (l += s.width / 2, Object.hasOwn(s, "labelpos")) switch (s.labelpos.toLowerCase()) {
2229
+ case "l":
2230
+ u = -s.width / 2;
2231
+ break;
2232
+ case "r":
2233
+ u = s.width / 2;
2234
+ break;
2235
+ }
2236
+ if (u && (l += t ? u : -u), u = void 0, l += (s.dummy ? n : e) / 2, l += (a.dummy ? n : e) / 2, l += a.width / 2, Object.hasOwn(a, "labelpos")) switch (a.labelpos.toLowerCase()) {
2237
+ case "l":
2238
+ u = a.width / 2;
2239
+ break;
2240
+ case "r":
2241
+ u = -a.width / 2;
2242
+ break;
2243
+ }
2244
+ return u && (l += t ? u : -u), l;
2245
+ };
2246
+ }
2247
+ function or(e, n) {
2248
+ return e.node(n).width;
2249
+ }
2250
+ function Nn(e, n, t) {
2251
+ var s;
2252
+ if (!t) return false;
2253
+ let r = (s = n.node(e)) == null ? void 0 : s.edgeObj;
2254
+ if (!r || n.node(e).edgeLabel.reversed) return false;
2255
+ let o = t.indexOf(r == null ? void 0 : r.v), i = t.indexOf(r == null ? void 0 : r.w);
2256
+ return o !== -1 && i !== -1 && o === (i + 1) % t.length || o === (i - 1) % t.length;
2257
+ }
2258
+ function kn(e, n) {
2259
+ e = Z(e), ir(e), Object.entries(vn(e, n)).forEach(([t, r]) => e.node(t).x = r);
2260
+ }
2261
+ function ir(e) {
2262
+ let n = P(e), t = e.graph(), r = t.ranksep, o = t.rankalign, i = 0;
2263
+ n.forEach((s) => {
2264
+ let a = s.reduce((l, u) => {
2265
+ var c;
2266
+ let d = (c = e.node(u).height) != null ? c : 0;
2267
+ return l > d ? l : d;
2268
+ }, 0);
2269
+ s.forEach((l) => {
2270
+ let u = e.node(l);
2271
+ o === "top" ? u.y = i + u.height / 2 : o === "bottom" ? u.y = i + a - u.height / 2 : u.y = i + a / 2;
2272
+ }), i += a + r;
2273
+ });
2274
+ }
2275
+ var xn = /* @__PURE__ */ new WeakMap();
2276
+ function Oe(e, n = {}) {
2277
+ return Rn(e, q, n), e;
2278
+ }
2279
+ function _n(e, n, t) {
2280
+ let r = n;
2281
+ for (; r !== void 0; ) {
2282
+ let o = e.parent(r);
2283
+ if (o === t) return r;
2284
+ r = o;
2285
+ }
2286
+ }
2287
+ function Rn(e, n, t) {
2288
+ var L2;
2289
+ let r = e.nodes().filter((b) => e.children(b).length), o = {};
2290
+ r.forEach((b) => {
2291
+ let g = e.node(b);
2292
+ if (g && g.rankdir) {
2293
+ let m = new T({ multigraph: true, compound: true });
2294
+ m.setGraph({ rankdir: g.rankdir });
2295
+ let w = e.children(b);
2296
+ w.forEach((v) => {
2297
+ let N = { ...e.node(v) };
2298
+ m.setNode(v, N);
2299
+ let O = e.parent(v);
2300
+ O && O !== b && w.includes(O) && m.setParent(v, O);
2301
+ });
2302
+ let k = /* @__PURE__ */ new Set();
2303
+ e.edges().forEach((v) => {
2304
+ let N = _n(e, v.v, b), O = _n(e, v.w, b);
2305
+ if (N && O && N !== O) {
2306
+ let W = `${N}\0${O}`;
2307
+ k.has(W) || (k.add(W), m.setEdge(N, O, { ...e.edge(v) }));
2308
+ }
2309
+ }), Rn(m, n, t);
2310
+ let _ = jn(m);
2311
+ On(_, n, t, null), Cn(m, _);
2312
+ let C = 1 / 0, j = 1 / 0, I = -1 / 0, S = -1 / 0;
2313
+ m.nodes().forEach((v) => {
2314
+ if (v === b) return;
2315
+ let N = m.node(v);
2316
+ N && typeof N.x == "number" && typeof N.y == "number" && typeof N.width == "number" && typeof N.height == "number" && (C = Math.min(C, N.x - N.width / 2), I = Math.max(I, N.x + N.width / 2), j = Math.min(j, N.y - N.height / 2), S = Math.max(S, N.y + N.height / 2));
2317
+ }), (!isFinite(C) || !isFinite(j) || !isFinite(I) || !isFinite(S)) && (C = j = 0, I = S = 0);
2318
+ let G = I - C, x = S - j;
2319
+ o[b] = { minX: C, minY: j, maxX: I, maxY: S, width: G, height: x, offsetX: C, offsetY: j }, g._dagreClusterSubgraph = m;
2320
+ }
2321
+ });
2322
+ let i = [], s = (b) => {
2323
+ let g = [], m = (e.children(b) || []).filter((w) => w !== b);
2324
+ for (; m.length > 0; ) {
2325
+ let w = m.shift();
2326
+ g.push(w), (e.children(w) || []).filter((k) => k !== w).forEach((k) => m.push(k));
2327
+ }
2328
+ return g;
2329
+ }, a = /* @__PURE__ */ new Map();
2330
+ r.forEach((b) => {
2331
+ let g = e.node(b);
2332
+ g && g.rankdir && o[b] && a.set(b, (e.children(b) || []).filter((m) => m !== b));
2333
+ });
2334
+ let l = new Set([...a.values()].flat()), u = /* @__PURE__ */ new Map();
2335
+ a.forEach((b, g) => {
2336
+ l.has(g) || u.set(g, s(g));
2337
+ });
2338
+ let d = new Set([...u.values()].flat()), c = (b) => {
2339
+ for (let [g, m] of u) if (m.includes(b)) return g;
2340
+ return b;
2341
+ }, f = [];
2342
+ e.edges().forEach((b) => {
2343
+ (d.has(b.v) || d.has(b.w)) && f.push({ edge: b, label: e.edge(b) });
2344
+ });
2345
+ let h = /* @__PURE__ */ new Map();
2346
+ d.forEach((b) => {
2347
+ let g = e.parent(b);
2348
+ h.set(b, typeof g == "string" ? g : void 0);
2349
+ }), u.forEach((b, g) => {
2350
+ let m = e.node(g), w = [];
2351
+ b.forEach((C) => {
2352
+ let j = e.node(C);
2353
+ j && (w.push({ id: C, node: j, parent: h.get(C) }), e.removeNode(C));
2354
+ });
2355
+ let k = f.filter(({ edge: C }) => b.includes(C.v) || b.includes(C.w)), _ = o[g];
2356
+ m && (i.push({ clusterId: g, subgraph: m._dagreClusterSubgraph, bounds: _, children: b, removedNodes: w, removedEdges: k }), m.width = _.width, m.height = _.height);
2357
+ });
2358
+ let p = /* @__PURE__ */ new Set();
2359
+ f.forEach(({ edge: b, label: g }) => {
2360
+ let m = c(b.v), w = c(b.w);
2361
+ if (m !== w && e.hasNode(m) && e.hasNode(w)) {
2362
+ let k = `${m}\0${w}`;
2363
+ p.has(k) || (p.add(k), e.setEdge(m, w, { ...g, width: 0, height: 0 }));
2364
+ }
2365
+ });
2366
+ let E = jn(e), y = On(E, n, t, (L2 = xn.get(e)) != null ? L2 : null);
2367
+ xn.set(e, y), Cn(e, E), p.forEach((b) => {
2368
+ let g = b.indexOf("\0"), m = b.slice(0, g), w = b.slice(g + 1);
2369
+ e.hasEdge(m, w) && e.removeEdge(m, w);
2370
+ }), i.forEach(({ clusterId: b, subgraph: g, bounds: m, removedNodes: w, removedEdges: k }) => {
2371
+ var G, x;
2372
+ let _ = e.node(b), C = (G = _ == null ? void 0 : _.x) != null ? G : 0, j = (x = _ == null ? void 0 : _.y) != null ? x : 0, I = (m.minX + m.maxX) / 2, S = (m.minY + m.maxY) / 2;
2373
+ w.forEach(({ id: v, node: N, parent: O }) => {
2374
+ e.setNode(v, N), O !== void 0 && e.setParent(v, O);
2375
+ }), k.forEach(({ edge: v, label: N }) => {
2376
+ e.setEdge(v, N);
2377
+ }), g.nodes().forEach((v) => {
2378
+ if (v === b) return;
2379
+ let N = g.node(v), O = e.node(v);
2380
+ O && N && typeof N.x == "number" && typeof N.y == "number" && (O.x = C + (N.x - I), O.y = j + (N.y - S));
2381
+ }), delete _._dagreClusterSubgraph;
2382
+ }), r.forEach((b) => {
2383
+ var w, k;
2384
+ let g = e.node(b), m = o[b];
2385
+ if (g && g.rankdir && g._dagreClusterSubgraph && m) {
2386
+ let _ = g._dagreClusterSubgraph, C = (w = g.x) != null ? w : 0, j = (k = g.y) != null ? k : 0, I = (m.minX + m.maxX) / 2, S = (m.minY + m.maxY) / 2;
2387
+ _.nodes().forEach((G) => {
2388
+ if (G === b) return;
2389
+ let x = _.node(G), v = e.node(G);
2390
+ if (v && x && typeof x.x == "number" && typeof x.y == "number") {
2391
+ let N = x.x - I, O = x.y - S;
2392
+ v.x = C + N, v.y = j + O;
2393
+ }
2394
+ }), delete g._dagreClusterSubgraph;
2395
+ }
2396
+ });
2397
+ }
2398
+ function On(e, n, t, r = null) {
2399
+ var l, u;
2400
+ let o = (t == null ? void 0 : t.useDynamic) !== false, i = o && (l = r == null ? void 0 : r.graph) != null ? l : null, s = o && (u = r == null ? void 0 : r.rawNodes) != null ? u : null;
2401
+ n(" makeSpaceForEdgeLabels", () => hr(e)), n(" removeSelfEdges", () => Nr(e)), n(" acyclic", () => Ue(e, i)), n(" nestingGraph.run", () => un(e)), n(" rank", () => dn(Z(e))), n(" injectEdgeLabelProxies", () => br(e)), n(" removeEmptyRanks", () => Be(e)), n(" nestingGraph.cleanup", () => fn(e)), n(" normalizeRanks", () => We(e)), n(" assignRankMinMax", () => gr(e)), n(" removeEdgeLabelProxies", () => pr(e)), n(" normalize.run", () => Ke(e)), n(" parentDummyChains", () => ln(e)), n(" addBorderSegments", () => bn(e)), n(" order", () => re(e, t, s)), n(" insertSelfEdges", () => Gr(e)), n(" adjustCoordinateSystem", () => pn(e)), n(" position", () => kn(e, t.corePath)), n(" positionSelfEdges", () => vr(e));
2402
+ let a = JSON.parse(JSON.stringify(e._nodes));
2403
+ return n(" removeBorderNodes", () => wr(e)), n(" normalize.undo", () => Qe(e)), n(" fixupEdgeLabelCoords", () => Lr(e)), n(" undoCoordinateSystem", () => mn(e)), n(" translateGraph", () => mr(e)), n(" assignNodeIntersects", () => Er(e)), n(" reversePoints", () => yr(e)), n(" acyclic.undo", () => Je(e)), { graph: e, rawNodes: a };
2404
+ }
2405
+ function Cn(e, n) {
2406
+ e.nodes().forEach((t) => {
2407
+ let r = e.node(t), o = n.node(t);
2408
+ r && (r.x = o.x, r.y = o.y, r.order = o.order, r.rank = o.rank, n.children(t).length && (r.width = o.width, r.height = o.height));
2409
+ }), e.edges().forEach((t) => {
2410
+ let r = e.edge(t), o = n.edge(t);
2411
+ r.points = o.points, Object.hasOwn(o, "x") && (r.x = o.x, r.y = o.y);
2412
+ }), e.graph().width = n.graph().width, e.graph().height = n.graph().height;
2413
+ }
2414
+ var sr = ["nodesep", "edgesep", "ranksep", "marginx", "marginy"], ar = { ranksep: 50, edgesep: 20, nodesep: 50, rankdir: "TB", rankalign: "center" }, dr = ["acyclicer", "ranker", "rankdir", "align", "rankalign"], lr = ["width", "height", "rank"], Tn = { width: 0, height: 0 }, ur = ["minlen", "weight", "width", "height", "labeloffset"], cr = { minlen: 1, weight: 1, width: 0, height: 0, labeloffset: 10, labelpos: "r" }, fr = ["labelpos"];
2415
+ function jn(e) {
2416
+ let n = new T({ multigraph: true, compound: true }), t = _e(e.graph());
2417
+ return n.setGraph(Object.assign({}, ar, xe(t, sr), B(t, dr))), e.nodes().forEach((r) => {
2418
+ let o = _e(e.node(r)), i = xe(o, lr);
2419
+ Object.keys(Tn).forEach((a) => {
2420
+ i[a] === void 0 && (i[a] = Tn[a]);
2421
+ }), n.setNode(r, i);
2422
+ let s = e.parent(r);
2423
+ s !== void 0 && n.setParent(r, s);
2424
+ }), e.edges().forEach((r) => {
2425
+ let o = _e(e.edge(r));
2426
+ n.setEdge(r, Object.assign({}, cr, xe(o, ur), B(o, fr)));
2427
+ }), n;
2428
+ }
2429
+ function hr(e) {
2430
+ let n = e.graph();
2431
+ n.ranksep /= 2, e.edges().forEach((t) => {
2432
+ var o;
2433
+ let r = e.edge(t);
2434
+ r.minlen *= 2, ((o = r.labelpos) != null ? o : "r").toLowerCase() !== "c" && (n.rankdir === "TB" || n.rankdir === "BT" ? r.width += r.labeloffset : r.height += r.labeloffset);
2435
+ });
2436
+ }
2437
+ function br(e) {
2438
+ e.edges().forEach((n) => {
2439
+ let t = e.edge(n);
2440
+ if (t.width && t.height) {
2441
+ let r = e.node(n.v), i = { rank: (e.node(n.w).rank - r.rank) / 2 + r.rank, e: n };
2442
+ M(e, "edge-proxy", i, "_ep");
2443
+ }
2444
+ });
2445
+ }
2446
+ function gr(e) {
2447
+ let n = 0;
2448
+ e.nodes().forEach((t) => {
2449
+ let r = e.node(t);
2450
+ r.borderTop && (r.minRank = e.node(r.borderTop).rank, r.maxRank = e.node(r.borderBottom).rank, n = Math.max(n, r.maxRank));
2451
+ }), e.graph().maxRank = n;
2452
+ }
2453
+ function pr(e) {
2454
+ e.nodes().forEach((n) => {
2455
+ let t = e.node(n);
2456
+ if (t.dummy === "edge-proxy") {
2457
+ let r = t;
2458
+ e.edge(r.e).labelRank = t.rank, e.removeNode(n);
2459
+ }
2460
+ });
2461
+ }
2462
+ function mr(e) {
2463
+ let n = Number.POSITIVE_INFINITY, t = 0, r = Number.POSITIVE_INFINITY, o = 0, i = e.graph(), s = i.marginx || 0, a = i.marginy || 0;
2464
+ function l(u) {
2465
+ let d = u.x, c = u.y, f = u.width, h = u.height;
2466
+ n = Math.min(n, d - f / 2), t = Math.max(t, d + f / 2), r = Math.min(r, c - h / 2), o = Math.max(o, c + h / 2);
2467
+ }
2468
+ e.nodes().forEach((u) => l(e.node(u))), e.edges().forEach((u) => {
2469
+ let d = e.edge(u);
2470
+ Object.hasOwn(d, "x") && l(d);
2471
+ }), n -= s, r -= a, e.nodes().forEach((u) => {
2472
+ let d = e.node(u);
2473
+ d.x -= n, d.y -= r;
2474
+ }), e.edges().forEach((u) => {
2475
+ let d = e.edge(u);
2476
+ d.points.forEach((c) => {
2477
+ c.x -= n, c.y -= r;
2478
+ }), Object.hasOwn(d, "x") && (d.x -= n), Object.hasOwn(d, "y") && (d.y -= r);
2479
+ }), i.width = t - n + s, i.height = o - r + a;
2480
+ }
2481
+ function Er(e) {
2482
+ e.edges().forEach((n) => {
2483
+ if (n.v === n.w) return;
2484
+ let t = e.edge(n), r = e.node(n.v), o = e.node(n.w), i, s;
2485
+ t.points ? (i = t.points[0], s = t.points[t.points.length - 1]) : (t.points = [], i = o, s = r), t.points.unshift(se(r, i)), t.points.push(se(o, s));
2486
+ });
2487
+ }
2488
+ function Lr(e) {
2489
+ e.edges().forEach((n) => {
2490
+ let t = e.edge(n);
2491
+ if (Object.hasOwn(t, "x")) switch ((t.labelpos === "l" || t.labelpos === "r") && (t.width -= t.labeloffset), t.labelpos) {
2492
+ case "l":
2493
+ t.x -= t.width / 2 + t.labeloffset;
2494
+ break;
2495
+ case "r":
2496
+ t.x += t.width / 2 + t.labeloffset;
2497
+ break;
2498
+ }
2499
+ });
2500
+ }
2501
+ function yr(e) {
2502
+ e.edges().forEach((n) => {
2503
+ let t = e.edge(n);
2504
+ t.reversed && t.points.reverse();
2505
+ });
2506
+ }
2507
+ function wr(e) {
2508
+ e.nodes().forEach((n) => {
2509
+ if (e.children(n).length) {
2510
+ let t = e.node(n), r = e.node(t.borderTop), o = e.node(t.borderBottom), i = e.node(t.borderLeft[t.borderLeft.length - 1]), s = e.node(t.borderRight[t.borderRight.length - 1]);
2511
+ t.width = Math.abs(s.x - i.x), t.height = Math.abs(o.y - r.y), t.x = i.x + t.width / 2, t.y = r.y + t.height / 2;
2512
+ }
2513
+ }), e.nodes().forEach((n) => {
2514
+ e.node(n).dummy === "border" && e.removeNode(n);
2515
+ });
2516
+ }
2517
+ function Nr(e) {
2518
+ e.edges().forEach((n) => {
2519
+ if (n.v === n.w) {
2520
+ let t = e.node(n.v);
2521
+ t.selfEdges || (t.selfEdges = []), t.selfEdges.push({ e: n, label: e.edge(n) }), e.removeEdge(n);
2522
+ }
2523
+ });
2524
+ }
2525
+ function Gr(e) {
2526
+ P(e).forEach((t) => {
2527
+ let r = 0;
2528
+ t.forEach((o, i) => {
2529
+ let s = e.node(o);
2530
+ typeof s.rank != "number" && (s.rank = 0), s.order = i + r, (s.selfEdges || []).forEach((a) => {
2531
+ M(e, "selfedge", { width: a.label.width, height: a.label.height, rank: s.rank, order: i + ++r, e: a.e, edgeLabel: a.label }, "_se"), (!Array.isArray(a.label.points) || a.label.points.length !== 7) && (a.label.points = [{ x: 0, y: -10 }, { x: 0, y: -10 }, { x: 0, y: 0 }, { x: 0, y: 10 }, { x: 0, y: 10 }, { x: 0, y: 0 }, { x: 0, y: 0 }]);
2532
+ }), delete s.selfEdges;
2533
+ });
2534
+ });
2535
+ }
2536
+ function vr(e) {
2537
+ e.nodes().forEach((n) => {
2538
+ let t = e.node(n), r = (o) => typeof o == "number" && isFinite(o);
2539
+ if (t.dummy === "selfedge") {
2540
+ let o = t, i = e.node(o.e.v), s = r(i == null ? void 0 : i.x) ? i.x : 0, a = r(i == null ? void 0 : i.y) ? i.y : 0, l = r(i == null ? void 0 : i.width) ? i.width : 0, u = r(i == null ? void 0 : i.height) ? i.height : 0, d = r(t.x) ? t.x : s, c = r(t.y) ? t.y : a, f = l / 2, h = u / 2;
2541
+ o.edgeLabel.points = [{ x: d + f, y: c - h }, { x: d + f, y: c - h }, { x: d, y: c }, { x: d - f, y: c + h }, { x: d - f, y: c + h }, { x: d, y: c }, { x: d, y: c }], o.edgeLabel.x = d, o.edgeLabel.y = c, e.setEdge(o.e, o.edgeLabel), e.removeNode(n);
2542
+ } else t && Array.isArray(t.selfEdges) && t.selfEdges.forEach((o) => {
2543
+ if (!Array.isArray(o.label.points) || o.label.points.length !== 7) {
2544
+ let i = r(t.x) ? t.x : 0, s = r(t.y) ? t.y : 0, a = r(t.width) ? t.width : 0, l = r(t.height) ? t.height : 0, u = a / 2, d = l / 2;
2545
+ o.label.points = [{ x: i + u, y: s - d }, { x: i + u, y: s - d }, { x: i, y: s }, { x: i - u, y: s + d }, { x: i - u, y: s + d }, { x: i, y: s }, { x: i, y: s }];
2546
+ }
2547
+ });
2548
+ });
2549
+ }
2550
+ function xe(e, n) {
2551
+ return X(B(e, n), Number);
2552
+ }
2553
+ function _e(e) {
2554
+ let n = {};
2555
+ return e && Object.entries(e).forEach(([t, r]) => {
2556
+ typeof t == "string" && (t = t.toLowerCase()), n[t] = r;
2557
+ }), n;
2558
+ }
2559
+ function Ce(e) {
2560
+ let n = P(e), t = new T({ compound: true, multigraph: true }).setGraph({});
2561
+ return e.nodes().forEach((r) => {
2562
+ t.setNode(r, { label: r }), t.setParent(r, "layer" + e.node(r).rank);
2563
+ }), e.edges().forEach((r) => t.setEdge(r.v, r.w, {}, r.name)), n.forEach((r, o) => {
2564
+ let i = "layer" + o;
2565
+ t.setNode(i, { rank: "same" }), r.reduce((s, a) => (t.setEdge(s, a, { style: "invis" }), a));
2566
+ }), t;
2567
+ }
2568
+ var kr = { graphlib: ie, version: ue, layout: Oe, debug: Ce, util: { time: le, notime: q } }, $o = kr;
2569
+ /*! For license information please see dagre.esm.js.LEGAL.txt */
2570
+ const WIDE_RANGES = [
2571
+ [4352, 4447],
2572
+ [11904, 42191],
2573
+ [44032, 55203],
2574
+ [63744, 64255],
2575
+ [65072, 65135],
2576
+ [65280, 65376],
2577
+ [65504, 65510],
2578
+ [127744, 128591],
2579
+ [128640, 128767],
2580
+ [128992, 129003],
2581
+ [129280, 129535],
2582
+ [131072, 262141],
2583
+ // single-codepoint emoji that are East Asian Wide (✅ ❌ ⚡ ⭐ ❓ ➕): drawn two cells wide
2584
+ [9989, 9989],
2585
+ [10060, 10060],
2586
+ [9889, 9889],
2587
+ [11088, 11088],
2588
+ [10067, 10069],
2589
+ [10133, 10135]
2590
+ ];
2591
+ const ZERO_WIDTH_RANGES = [[768, 879], [8203, 8205], [65024, 65039]];
2592
+ function cellWidth(ch) {
2593
+ const cp = ch.codePointAt(0) ?? 0;
2594
+ if (ZERO_WIDTH_RANGES.some(([a, b]) => cp >= a && cp <= b)) return 0;
2595
+ if (WIDE_RANGES.some(([a, b]) => cp >= a && cp <= b)) return 2;
2596
+ return 1;
2597
+ }
2598
+ function displayWidth(s) {
2599
+ let w = 0;
2600
+ for (const ch of s) w += cellWidth(ch);
2601
+ return w;
2602
+ }
2603
+ const WIDE_TAIL = "\0";
2604
+ const ZERO_WIDTH = /[\u0300-\u036f\u200b-\u200d\ufe00-\ufe0f]/g;
2605
+ const CONTROL = /[\u0000-\u001f\u007f-\u009f؜‎‏‪-‮⁦-⁩]/g;
2606
+ function sanitizeLabel(raw) {
2607
+ return String(raw ?? "").normalize("NFC").replace(/[\n\r\t]+/g, " ").replace(CONTROL, "").replace(ZERO_WIDTH, "").replace(/ {2,}/g, " ").trim();
2608
+ }
2609
+ function fitLabel(s, max) {
2610
+ if (displayWidth(s) <= max) return s;
2611
+ let out = "";
2612
+ let w = 0;
2613
+ for (const ch of s) {
2614
+ const cw = cellWidth(ch);
2615
+ if (w + cw > max - 1) break;
2616
+ out += ch;
2617
+ w += cw;
2618
+ }
2619
+ return out + "…";
2620
+ }
2621
+ const MAX_PLAN_NODES = 150;
2622
+ const MAX_PLAN_EDGES = 600;
2623
+ const BOX_HEIGHT = 3;
2624
+ const BOX_CHROME = 6;
2625
+ const MIN_LABEL = 8;
2626
+ const GAP_BOX = 3;
2627
+ const GAP_OTHER = 2;
2628
+ const LABEL_FLOOR = MIN_LABEL;
2629
+ function layoutPlan(input, labelMax) {
2630
+ const { nodes, warnings } = normalizePlanNodes(input);
2631
+ if (!nodes.length) return { ok: false, reason: "empty", message: "the plan has no tasks" };
2632
+ if (nodes.length > MAX_PLAN_NODES) return { ok: false, reason: "too_large", message: `the plan has ${nodes.length} tasks (the graph shows at most ${MAX_PLAN_NODES})` };
2633
+ const edgeCount = nodes.reduce((a, n) => a + n.deps.length, 0);
2634
+ if (edgeCount > MAX_PLAN_EDGES) return { ok: false, reason: "too_large", message: `the plan has ${edgeCount} dependencies (the graph shows at most ${MAX_PLAN_EDGES})` };
2635
+ const items = nodes.map((node, i) => {
2636
+ const label = fitLabel(sanitizeLabel(node.label) || sanitizeLabel(node.id) || "?", Math.max(MIN_LABEL, labelMax));
2637
+ return { uid: i, rank: 0, x: 0, w: Math.max(displayWidth(label), 1) + BOX_CHROME, virtual: false, node, label };
2638
+ });
2639
+ const byId = new Map(items.map((it2) => [it2.node.id, it2]));
2640
+ const g = new $o.graphlib.Graph();
2641
+ g.setGraph({ rankdir: "TB", nodesep: GAP_BOX + 1, edgesep: GAP_OTHER + 1, ranksep: 4, marginx: 0, marginy: 0 });
2642
+ g.setDefaultEdgeLabel(() => ({}));
2643
+ items.forEach((it2, i) => g.setNode(`n${i}`, { width: it2.w, height: BOX_HEIGHT }));
2644
+ const idx = new Map(nodes.map((n, i) => [n.id, i]));
2645
+ for (const n of nodes) for (const d of n.deps) g.setEdge(`n${idx.get(d)}`, `n${idx.get(n.id)}`);
2646
+ $o.layout(g);
2647
+ const ys = [...new Set(items.map((_, i) => Math.round(g.node(`n${i}`).y)))].sort((a, b) => a - b);
2648
+ items.forEach((it2, i) => {
2649
+ const p = g.node(`n${i}`);
2650
+ it2.rank = ys.indexOf(Math.round(p.y));
2651
+ it2.x = Math.round(p.x - it2.w / 2);
2652
+ });
2653
+ const ranks = ys.map(() => []);
2654
+ items.forEach((it2) => ranks[it2.rank].push(it2));
2655
+ const chains = [];
2656
+ let uid = items.length;
2657
+ for (const n of nodes) for (const d of n.deps) {
2658
+ const a = items[idx.get(d)];
2659
+ const b = items[idx.get(n.id)];
2660
+ const span = b.rank - a.rank;
2661
+ if (span < 1) return { ok: false, reason: "cannot_draw", message: `${n.id} is not ranked below ${d}` };
2662
+ const pts = g.edge(`n${idx.get(d)}`, `n${idx.get(n.id)}`).points ?? [];
2663
+ const chain = [a];
2664
+ for (let k = 1; k < span; k++) {
2665
+ const px = pts.length === span + 1 ? pts[k].x : a.x + a.w / 2 + (b.x + b.w / 2 - (a.x + a.w / 2)) * (k / span);
2666
+ const v = { uid: uid++, rank: a.rank + k, x: Math.round(px), w: 1, virtual: true, chain: chains.length };
2667
+ ranks[v.rank].push(v);
2668
+ chain.push(v);
2669
+ }
2670
+ chain.push(b);
2671
+ chains.push({ from: d, to: n.id, items: chain });
2672
+ }
2673
+ const legalize = (row) => {
2674
+ row.sort((p, q2) => p.x - q2.x || p.uid - q2.uid);
2675
+ for (let i = 1; i < row.length; i++) {
2676
+ const gap = row[i - 1].virtual || row[i].virtual ? GAP_OTHER : GAP_BOX;
2677
+ row[i].x = Math.max(row[i].x, row[i - 1].x + row[i - 1].w + gap);
2678
+ }
2679
+ };
2680
+ ranks.forEach(legalize);
2681
+ const minX = Math.min(...ranks.flat().map((it2) => it2.x));
2682
+ if (minX !== 0) ranks.flat().forEach((it2) => {
2683
+ it2.x -= minX;
2684
+ });
2685
+ const clashes = (v) => [v.rank - 1, v.rank + 1].some((r) => (ranks[r] ?? []).some((o) => o.virtual && o.x === v.x && o.chain !== v.chain));
2686
+ for (let pass = 0; pass < 40; pass++) {
2687
+ let changed = false;
2688
+ for (const row of ranks) for (const v of row) if (v.virtual && clashes(v)) {
2689
+ v.x += 1;
2690
+ legalize(row);
2691
+ changed = true;
2692
+ }
2693
+ if (!changed) break;
2694
+ }
2695
+ if (ranks.flat().some((v) => v.virtual && clashes(v))) return { ok: false, reason: "cannot_draw", message: "edge waypoints could not be separated" };
2696
+ const width = Math.max(...ranks.flat().map((it2) => it2.x + it2.w));
2697
+ return { ok: true, nodes, ranks, chains, byId, width, warnings };
2698
+ }
2699
+ const GLYPHS = { done: "✓", running: "▶", failed: "✗", pending: "○", ready: "◔", awaiting_user: "~", awaiting_input: "⏸", cancelled: "⊘" };
2700
+ const ASCII_GLYPHS = { done: "+", running: ">", failed: "x", pending: "o", ready: "*", awaiting_user: "~", awaiting_input: "=", cancelled: "/" };
2701
+ const STATUS_SGR = { done: "32", running: "33", failed: "31", pending: "2", ready: "36", awaiting_user: "35", awaiting_input: "35", cancelled: "2" };
2702
+ const U = 1;
2703
+ const D = 2;
2704
+ const L = 4;
2705
+ const R = 8;
2706
+ const UNICODE_GLYPH = {
2707
+ [U]: "│",
2708
+ [D]: "│",
2709
+ [U | D]: "│",
2710
+ [L]: "─",
2711
+ [R]: "─",
2712
+ [L | R]: "─",
2713
+ [D | R]: "┌",
2714
+ [D | L]: "┐",
2715
+ [U | R]: "└",
2716
+ [U | L]: "┘",
2717
+ [U | D | R]: "├",
2718
+ [U | D | L]: "┤",
2719
+ [L | R | D]: "┬",
2720
+ [L | R | U]: "┴",
2721
+ [U | D | L | R]: "┼"
2722
+ };
2723
+ const LABEL_STEPS = [40, 30, 24, 20, 16, 13, 10, LABEL_FLOOR];
2724
+ class CannotDraw extends Error {
2725
+ }
2726
+ const cV = (col, r1, r2) => Array.from({ length: r2 - r1 + 1 }, (_, i) => [r1 + i, col]);
2727
+ const cH = (row, c1, c2) => {
2728
+ const lo = Math.min(c1, c2);
2729
+ return Array.from({ length: Math.abs(c2 - c1) + 1 }, (_, i) => [row, lo + i]);
2730
+ };
2731
+ const portRange = (it2) => it2.virtual ? [it2.x, it2.x] : [it2.x + 1, it2.x + it2.w - 2];
2732
+ function planGap(layout, g) {
2733
+ const top = layout.ranks[g];
2734
+ const bot = layout.ranks[g + 1];
2735
+ const segs = [];
2736
+ layout.chains.forEach((c, ci) => {
2737
+ for (let k = 0; k + 1 < c.items.length; k++) if (c.items[k].rank === g) segs.push({ a: c.items[k], b: c.items[k + 1], chain: ci });
2738
+ });
2739
+ const outN = /* @__PURE__ */ new Map();
2740
+ const inN = /* @__PURE__ */ new Map();
2741
+ for (const s of segs) {
2742
+ outN.set(s.a.uid, (outN.get(s.a.uid) ?? 0) + 1);
2743
+ inN.set(s.b.uid, (inN.get(s.b.uid) ?? 0) + 1);
2744
+ }
2745
+ const limit = layout.width + 24;
2746
+ const used = /* @__PURE__ */ new Set();
2747
+ for (const it2 of [...top, ...bot]) if (it2.virtual) used.add(it2.x);
2748
+ const pickFree = (lo, hi, pref) => {
2749
+ const p = Math.min(hi, Math.max(lo, pref));
2750
+ for (let d = 0; d <= hi - lo; d++) for (const c of d === 0 ? [p] : [p - d, p + d]) if (c >= lo && c <= hi && !used.has(c)) return c;
2751
+ return void 0;
2752
+ };
2753
+ const sc = /* @__PURE__ */ new Map();
2754
+ const tc = /* @__PURE__ */ new Map();
2755
+ for (const it2 of top) if (it2.virtual) sc.set(it2.uid, it2.x);
2756
+ for (const it2 of bot) if (it2.virtual) tc.set(it2.uid, it2.x);
2757
+ const straight = /* @__PURE__ */ new Set();
2758
+ for (const s of segs) {
2759
+ if (outN.get(s.a.uid) !== 1 || inN.get(s.b.uid) !== 1) continue;
2760
+ const [alo, ahi] = portRange(s.a);
2761
+ const [blo, bhi] = portRange(s.b);
2762
+ const lo = Math.max(alo, blo);
2763
+ const hi = Math.min(ahi, bhi);
2764
+ if (lo > hi) continue;
2765
+ let c;
2766
+ if (s.a.virtual || s.b.virtual) c = s.a.virtual ? s.a.x : s.b.x;
2767
+ else c = pickFree(lo, hi, Math.round((s.a.x + s.a.w / 2 + s.b.x + s.b.w / 2) / 2));
2768
+ if (c === void 0 || c < lo || c > hi) continue;
2769
+ used.add(c);
2770
+ sc.set(s.a.uid, c);
2771
+ tc.set(s.b.uid, c);
2772
+ straight.add(s);
2773
+ }
2774
+ for (const [items, ports, counts] of [[top, sc, outN], [bot, tc, inN]]) {
2775
+ for (const it2 of items) {
2776
+ if (!counts.get(it2.uid) || ports.has(it2.uid)) continue;
2777
+ const [lo, hi] = portRange(it2);
2778
+ const c = pickFree(lo, hi, Math.round(it2.x + it2.w / 2));
2779
+ if (c === void 0) throw new CannotDraw("no free port column");
2780
+ used.add(c);
2781
+ ports.set(it2.uid, c);
2782
+ }
2783
+ }
2784
+ const dropOf = /* @__PURE__ */ new Map();
2785
+ const needSrc = /* @__PURE__ */ new Set();
2786
+ const needTgt = /* @__PURE__ */ new Set();
2787
+ for (const s of segs) {
2788
+ if (straight.has(s)) continue;
2789
+ const srcSingle = outN.get(s.a.uid) === 1;
2790
+ const tgtSingle = inN.get(s.b.uid) === 1;
2791
+ if (srcSingle && !tgtSingle) {
2792
+ dropOf.set(s, sc.get(s.a.uid));
2793
+ needTgt.add(s.b.uid);
2794
+ } else if (tgtSingle) {
2795
+ dropOf.set(s, tc.get(s.b.uid));
2796
+ needSrc.add(s.a.uid);
2797
+ }
2798
+ }
2799
+ for (const s of segs) {
2800
+ if (straight.has(s) || dropOf.has(s)) continue;
2801
+ const c = pickFree(0, limit, Math.round((sc.get(s.a.uid) + tc.get(s.b.uid)) / 2));
2802
+ if (c === void 0) throw new CannotDraw("no free drop column");
2803
+ used.add(c);
2804
+ dropOf.set(s, c);
2805
+ needSrc.add(s.a.uid);
2806
+ needTgt.add(s.b.uid);
2807
+ }
2808
+ const srcRow = /* @__PURE__ */ new Map();
2809
+ const tgtRow = /* @__PURE__ */ new Map();
2810
+ top.filter((it2) => needSrc.has(it2.uid)).forEach((it2, i) => srcRow.set(it2.uid, i));
2811
+ bot.filter((it2) => needTgt.has(it2.uid)).forEach((it2, i) => tgtRow.set(it2.uid, srcRow.size + i));
2812
+ const rows = Math.max(srcRow.size + tgtRow.size + 1, 2);
2813
+ const last = rows - 1;
2814
+ const plan = { rows, vlines: [], hlines: [], arrows: [], routes: [], maxCol: Math.max(0, ...used) };
2815
+ const arrive = (b, col) => {
2816
+ if (!b.virtual) plan.arrows.push({ row: last, col });
2817
+ };
2818
+ for (const s of segs) {
2819
+ const a = sc.get(s.a.uid);
2820
+ const t = tc.get(s.b.uid);
2821
+ const botOpen = s.b.virtual;
2822
+ const cells = [];
2823
+ if (straight.has(s)) {
2824
+ plan.vlines.push({ col: a, r1: 0, r2: last, top: true, bot: botOpen });
2825
+ arrive(s.b, a);
2826
+ cells.push(...cV(a, 0, last));
2827
+ } else {
2828
+ const d = dropOf.get(s);
2829
+ const sr2 = srcRow.get(s.a.uid);
2830
+ const tr2 = tgtRow.get(s.b.uid);
2831
+ const haveSrc = sr2 !== void 0 && needSrc.has(s.a.uid) && d !== a;
2832
+ const haveTgt = tr2 !== void 0 && needTgt.has(s.b.uid) && d !== t;
2833
+ if (haveSrc) {
2834
+ plan.vlines.push({ col: a, r1: 0, r2: sr2, top: true, bot: false });
2835
+ plan.hlines.push({ row: sr2, c1: Math.min(a, d), c2: Math.max(a, d) });
2836
+ cells.push(...cV(a, 0, sr2), ...cH(sr2, a, d));
2837
+ }
2838
+ const y1 = haveSrc ? sr2 : 0;
2839
+ const y2 = haveTgt ? tr2 : last;
2840
+ plan.vlines.push({ col: d, r1: y1, r2: y2, top: !haveSrc, bot: !haveTgt && botOpen });
2841
+ cells.push(...cV(d, y1, y2));
2842
+ if (haveTgt) {
2843
+ plan.hlines.push({ row: tr2, c1: Math.min(d, t), c2: Math.max(d, t) });
2844
+ plan.vlines.push({ col: t, r1: tr2, r2: last, top: false, bot: botOpen });
2845
+ cells.push(...cH(tr2, d, t), ...cV(t, tr2, last));
2846
+ arrive(s.b, t);
2847
+ } else arrive(s.b, d);
2848
+ }
2849
+ plan.routes.push({ chain: s.chain, cells });
2850
+ plan.maxCol = Math.max(plan.maxCol, ...cells.map((c) => c[1]));
2851
+ }
2852
+ return plan;
2853
+ }
2854
+ function drawOnce(layout, opts) {
2855
+ const ascii = opts.ascii ?? false;
2856
+ const gaps = layout.ranks.slice(0, -1).map((_, g) => planGap(layout, g));
2857
+ const rankY = [0];
2858
+ gaps.forEach((gp, g) => rankY.push(rankY[g] + BOX_HEIGHT + gp.rows));
2859
+ const height = rankY[rankY.length - 1] + BOX_HEIGHT;
2860
+ const width = Math.max(layout.width, ...gaps.map((gp) => gp.maxCol + 1));
2861
+ const bits = Array.from({ length: height }, () => new Uint8Array(width));
2862
+ const arrows = [];
2863
+ const parts = layout.chains.map(() => []);
2864
+ gaps.forEach((gp, g) => {
2865
+ const y0 = rankY[g] + BOX_HEIGHT;
2866
+ for (const v of gp.vlines) for (let r = v.r1; r <= v.r2; r++) bits[y0 + r][v.col] |= (r > v.r1 || v.top ? U : 0) | (r < v.r2 || v.bot ? D : 0);
2867
+ for (const h of gp.hlines) for (let c = h.c1; c <= h.c2; c++) bits[y0 + h.row][c] |= (c > h.c1 ? L : 0) | (c < h.c2 ? R : 0);
2868
+ for (const a of gp.arrows) arrows.push([y0 + a.row, a.col]);
2869
+ for (const rt2 of gp.routes) parts[rt2.chain].push({ at: g * 2 + 1, cells: rt2.cells.map(([r, c]) => [y0 + r, c]) });
2870
+ });
2871
+ layout.chains.forEach((ch, ci) => {
2872
+ for (const it2 of ch.items) if (it2.virtual) {
2873
+ for (let r = 0; r < BOX_HEIGHT; r++) bits[rankY[it2.rank] + r][it2.x] |= U | D;
2874
+ parts[ci].push({ at: it2.rank * 2, cells: [0, 1, 2].map((r) => [rankY[it2.rank] + r, it2.x]) });
2875
+ }
2876
+ });
2877
+ const routes = Object.fromEntries(layout.chains.map((c, ci) => [`${c.from}>${c.to}`, parts[ci].sort((p, q2) => p.at - q2.at).flatMap((p) => p.cells)]));
2878
+ const cells = bits.map((row) => Array.from(row, (b) => b ? ascii ? b & (L | R) && b & (U | D) ? "+" : b & (L | R) ? "-" : "|" : UNICODE_GLYPH[b] : " "));
2879
+ for (const [r, c] of arrows) cells[r][c] = ascii ? "v" : "▼";
2880
+ const styles = cells.map((row) => row.map(() => ""));
2881
+ const glyphs = ascii ? ASCII_GLYPHS : GLYPHS;
2882
+ const boxes = [];
2883
+ const labels = {};
2884
+ const put = (r, c, ch, style = "") => {
2885
+ const w = cellWidth(ch);
2886
+ if (w === 0) return 0;
2887
+ cells[r][c] = ch;
2888
+ styles[r][c] = style;
2889
+ if (w === 2) {
2890
+ cells[r][c + 1] = WIDE_TAIL;
2891
+ styles[r][c + 1] = style;
2892
+ }
2893
+ return w;
2894
+ };
2895
+ const color = opts.color ?? false;
2896
+ for (const node of layout.nodes) {
2897
+ const it2 = layout.byId.get(node.id);
2898
+ const y = rankY[it2.rank];
2899
+ const { x, w } = it2;
2900
+ boxes.push({ id: node.id, x, y, w, h: BOX_HEIGHT });
2901
+ labels[node.id] = it2.label;
2902
+ const selected = opts.selectedId === node.id;
2903
+ const [tl, tr2, bl, br2, h, v] = ascii ? ["+", "+", "+", "+", "-", "|"] : ["┌", "┐", "└", "┘", "─", "│"];
2904
+ for (let c2 = x; c2 < x + w; c2++) {
2905
+ put(y, c2, c2 === x ? tl : c2 === x + w - 1 ? tr2 : h);
2906
+ put(y + 2, c2, c2 === x ? bl : c2 === x + w - 1 ? br2 : h);
2907
+ }
2908
+ for (let c2 = x; c2 < x + w; c2++) put(y + 1, c2, " ", color && selected ? "7" : "");
2909
+ put(y + 1, x, v);
2910
+ put(y + 1, x + w - 1, v);
2911
+ const mid = color && selected ? "7" : "";
2912
+ if (selected && !color) put(y + 1, x + 1, ">");
2913
+ put(y + 1, x + 2, glyphs[node.status] ?? "?", color ? (selected ? "7;" : "") + STATUS_SGR[node.status] : "");
2914
+ let c = x + 4;
2915
+ for (const ch of it2.label) c += put(y + 1, c, ch, mid);
2916
+ }
2917
+ if (color && opts.selectedId) {
2918
+ for (const [key, route] of Object.entries(routes)) {
2919
+ const e = layout.chains.find((ch) => `${ch.from}>${ch.to}` === key);
2920
+ if (e.from === opts.selectedId || e.to === opts.selectedId) {
2921
+ for (const [r, c] of route) if (!styles[r][c]) styles[r][c] = "1;36";
2922
+ }
2923
+ }
2924
+ }
2925
+ const edges = layout.chains.map((c) => ({ from: c.from, to: c.to }));
2926
+ return { ok: true, lines: toLines(cells, styles), boxes, edges, width, height, fits: true, labels, routes, cells, styles, warnings: layout.warnings };
2927
+ }
2928
+ function toLines(cells, styles, rows, cols) {
2929
+ const [r0, r1] = rows ?? [0, cells.length];
2930
+ const out = [];
2931
+ for (let r = r0; r < r1; r++) {
2932
+ const row = cells[r];
2933
+ const [c0, c1] = cols ?? [0, row.length];
2934
+ let end = c1;
2935
+ while (end > c0 && (row[end - 1] === " " && !styles[r][end - 1])) end--;
2936
+ let line = "";
2937
+ let cur = "";
2938
+ for (let c = c0; c < end; c++) {
2939
+ if (row[c] === WIDE_TAIL) continue;
2940
+ if (styles[r][c] !== cur) {
2941
+ line += cur ? "\x1B[0m" : "";
2942
+ cur = styles[r][c];
2943
+ line += cur ? `\x1B[${cur}m` : "";
2944
+ }
2945
+ line += row[c];
2946
+ }
2947
+ out.push(line + (cur ? "\x1B[0m" : ""));
2948
+ }
2949
+ return out;
2950
+ }
2951
+ function renderPlan(nodes, opts = {}) {
2952
+ try {
2953
+ let result;
2954
+ for (const labelMax of LABEL_STEPS) {
2955
+ const layout = layoutPlan(nodes, labelMax);
2956
+ if (!layout.ok) return layout;
2957
+ result = drawOnce(layout, opts);
2958
+ if (opts.maxCols === void 0 || result.width <= opts.maxCols) return result;
2959
+ }
2960
+ return { ...result, fits: false };
2961
+ } catch (err) {
2962
+ return { ok: false, reason: "cannot_draw", message: err instanceof Error ? err.message : String(err) };
2963
+ }
2964
+ }
2965
+ const MAX_KEY_ATTEMPTS = 3;
2966
+ function alreadyConfigured(deps) {
2967
+ const { persisted, overriddenKeys } = deps;
2968
+ if (persisted.llmBackend !== void 0) return true;
2969
+ if (persisted.apiKey !== void 0 || persisted.authToken !== void 0) return true;
2970
+ for (const key of ["llmBackend", "apiKey", "authToken", "proxyUrl"]) {
2971
+ if (overriddenKeys.has(key)) return true;
2972
+ }
2973
+ return false;
2974
+ }
2975
+ async function maybeRunFirstRunSetup(deps) {
2976
+ const { configStore, persisted, isInteractive, ask, log, testKey } = deps;
2977
+ if (alreadyConfigured(deps)) return persisted;
2978
+ if (!isInteractive) return persisted;
2979
+ log("");
2980
+ log("Welcome to Aielia! 👋 Let’s get you set up — it takes about a minute.");
2981
+ log("");
2982
+ log("Aielia needs an AI model to think with. Pick where that comes from below.");
2983
+ log("(You can change this any time with /config, or press Ctrl+C to skip.)");
2984
+ log("");
2985
+ const patch = {};
2986
+ log("Which AI provider do you have (or want to use)?");
2987
+ log("");
2988
+ PROVIDER_SETUP.forEach((p, i) => {
2989
+ log(` ${i + 1}) ${p.name}`);
2990
+ log(` ${p.blurb}`);
2991
+ });
2992
+ log("");
2993
+ const choice = (await ask(`Type 1-${PROVIDER_SETUP.length} and press Enter (or just Enter to skip): `)).trim();
2994
+ const picked = PROVIDER_SETUP[Number(choice) - 1];
2995
+ if (!picked) {
2996
+ log("");
2997
+ log('Skipped. Until you set a provider, Aielia will try the "proxy" backend (needs');
2998
+ log("@buildaharness/proxy running on :8787). To set one up later, run:");
2999
+ log(" /config set llmBackend <anthropic|openai|openrouter>");
3000
+ log(" /config set apiKey <your key>");
3001
+ log("");
3002
+ return persisted;
3003
+ }
3004
+ log("");
3005
+ log(`To connect ${picked.name}, you need an “API key” — a password-like code that lets Aielia use your account.`);
3006
+ log("Here’s how to get one:");
3007
+ log("");
3008
+ picked.steps.forEach((step, i) => log(` ${i + 1}. ${step}`));
3009
+ if (picked.safety) {
3010
+ log("");
3011
+ log(` ${picked.safety.title}:`);
3012
+ picked.safety.steps.forEach((step, i) => log(` ${i + 1}. ${step}`));
3013
+ log(` (${picked.safety.urlLabel})`);
3014
+ }
3015
+ log("");
3016
+ log(`The key starts with "${picked.keyPrefix}". Paste it below when you have it.`);
3017
+ log("");
3018
+ let key = "";
3019
+ for (let attempt = 1; attempt <= MAX_KEY_ATTEMPTS; attempt++) {
3020
+ const candidate = cleanApiKey(await ask("Paste your API key (or just Enter to skip): "));
3021
+ if (candidate === "") break;
3022
+ const formatProblem = checkApiKeyFormat(picked.backend, candidate);
3023
+ if (formatProblem) {
3024
+ log(` ✗ ${formatProblem}`);
3025
+ continue;
3026
+ }
3027
+ if (testKey) {
3028
+ log(" Checking your key…");
3029
+ const result = await testKey(picked.backend, candidate);
3030
+ if (result.status === "invalid") {
3031
+ log(` ✗ ${result.message}`);
3032
+ continue;
3033
+ }
3034
+ if (result.status === "unverified") log(` ! ${result.message}`);
3035
+ }
3036
+ key = candidate;
3037
+ break;
3038
+ }
3039
+ if (!key) {
3040
+ log("");
3041
+ log("No working key entered, so nothing was saved. Run Aielia again to retry, or use");
3042
+ log("/config set llmBackend " + picked.backend + " then /config set apiKey <key>");
3043
+ log("");
3044
+ return persisted;
3045
+ }
3046
+ patch.llmBackend = picked.backend;
3047
+ patch.apiKey = key;
3048
+ await configStore.save(patch);
3049
+ log("");
3050
+ log(`✓ You’re all set — using ${picked.name}. Say hello!`);
3051
+ log(" Your key is saved on this computer in ~/.buildaharness/personal-assistant/config.json");
3052
+ log(" (plain text, like a .env file) — don’t share that file.");
3053
+ log("");
3054
+ return { ...persisted, ...patch };
3055
+ }
3056
+ const ICONS = {
3057
+ /** A routine, no-approval-needed tool step (read_file, fetch_url, web_search, list_reminders, …). */
3058
+ toolStep: "⚙",
3059
+ /** A tool step that is itself the proposal of a state-changing action (write_file, run_shell_command) — printed on the same in-flight step line, before the separate "[needs approval — …]" prompt appears. */
3060
+ proposalStep: "⚠",
3061
+ /** A read-only tool call `tool-policy.ts` denied before it executed — deliberately not `✗` (cli.ts already uses that, unprefixed, as the leading character of several unrelated config/undo-action error lines; reusing it here would misclassify those as `'tool'`-kind in tui-app.tsx's `TOOL_STEP_PREFIXES` match). */
3062
+ deniedStep: "⛔"
3063
+ };
3064
+ const PLAN_LINE_PREFIX = "▤PLAN▤";
3065
+ const PROPOSAL_TOOLS = /* @__PURE__ */ new Set(["write_file", "run_shell_command"]);
3066
+ function toolStepIcon(tool) {
3067
+ return PROPOSAL_TOOLS.has(tool) ? ICONS.proposalStep : ICONS.toolStep;
3068
+ }
3069
+ function parseCliArgs(argv) {
3070
+ const first = argv[0];
3071
+ if (first === "--version" || first === "-v" || first === "version") return { command: "version" };
3072
+ if (first === "--help" || first === "-h" || first === "help") return { command: "help" };
3073
+ if (first === "update") return { command: "update", dryRun: argv.slice(1).includes("--dry-run") };
3074
+ return { command: "repl" };
3075
+ }
3076
+ function cliHelpText(version) {
3077
+ return [
3078
+ `aielia ${version} — an agent you can hand real work to, held in bounds by a harness.`,
3079
+ "",
3080
+ "Usage:",
3081
+ " aielia start the interactive assistant",
3082
+ " aielia update update to the latest release (standalone binary installs)",
3083
+ " aielia update --dry-run check for an update without installing it",
3084
+ " aielia --version print the version",
3085
+ " aielia --help show this help",
3086
+ "",
3087
+ "https://myaielia.com"
3088
+ ].join("\n");
3089
+ }
3090
+ const MANIFEST_URL = "https://myaielia.com/aielia-latest.json";
3091
+ const RELEASES_API_URL = "https://api.github.com/repos/3IVIS/buildaharness/releases?per_page=50";
3092
+ const RELEASE_TAG_PREFIX = "aielia-v";
3093
+ const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
3094
+ const REQUEST_TIMEOUT_MS = 5e3;
3095
+ const DOWNLOAD_TIMEOUT_MS = 10 * 60 * 1e3;
3096
+ const NPM_UPDATE_HINT = "npm update -g @buildaharness/aielia";
3097
+ const defaultDataDir$1 = () => join(homedir(), ".buildaharness", "personal-assistant");
3098
+ const cachePath = (dataDir) => join(dataDir, "update-check.json");
3099
+ function compareVersions(a, b) {
3100
+ const parse = (v) => {
3101
+ const [core, pre] = v.replace(/^v/, "").split("-", 2);
3102
+ const nums = core.split(".").map((n) => Number.parseInt(n, 10) || 0);
3103
+ while (nums.length < 3) nums.push(0);
3104
+ return { nums, pre: pre !== void 0 };
3105
+ };
3106
+ const pa = parse(a);
3107
+ const pb = parse(b);
3108
+ for (let i = 0; i < 3; i++) if (pa.nums[i] !== pb.nums[i]) return pa.nums[i] - pb.nums[i];
3109
+ if (pa.pre !== pb.pre) return pa.pre ? -1 : 1;
3110
+ return 0;
3111
+ }
3112
+ function assertHttps(url, allowInsecureHttp = false) {
3113
+ let protocol;
3114
+ try {
3115
+ protocol = new URL(url).protocol;
3116
+ } catch {
3117
+ throw new Error(`Refusing malformed URL: ${url}`);
3118
+ }
3119
+ if (protocol === "https:") return;
3120
+ if (protocol === "http:" && allowInsecureHttp) return;
3121
+ throw new Error(`Refusing non-HTTPS URL: ${url}`);
3122
+ }
3123
+ function shouldRunPassiveUpdateCheck(input) {
3124
+ if (input.updateCheck === "disabled") return false;
3125
+ if (!input.stdinIsTty) return false;
3126
+ if (input.env.ASSISTANT_NON_INTERACTIVE_APPROVAL !== void 0) return false;
3127
+ return true;
3128
+ }
3129
+ function platformKey(o) {
3130
+ return `${o.platform ?? process.platform}-${o.arch ?? process.arch}`;
3131
+ }
3132
+ function readCache(dataDir) {
3133
+ try {
3134
+ const parsed = JSON.parse(readFileSync(cachePath(dataDir), "utf8"));
3135
+ if (typeof parsed.checkedAt !== "number") return null;
3136
+ return parsed;
3137
+ } catch {
3138
+ return null;
3139
+ }
3140
+ }
3141
+ function writeCache(dataDir, cache) {
3142
+ try {
3143
+ mkdirSync(dataDir, { recursive: true });
3144
+ writeFileSync(cachePath(dataDir), JSON.stringify(cache));
3145
+ } catch {
3146
+ }
3147
+ }
3148
+ function parseManifest(json, allowInsecureHttp) {
3149
+ const m = json;
3150
+ if (typeof (m == null ? void 0 : m.tag) !== "string" || typeof m.version !== "string" || typeof m.assets !== "object" || m.assets === null) {
3151
+ throw new Error("Malformed update manifest");
3152
+ }
3153
+ const assets = {};
3154
+ for (const [key, raw] of Object.entries(m.assets)) {
3155
+ if (typeof (raw == null ? void 0 : raw.url) !== "string") continue;
3156
+ assertHttps(raw.url, allowInsecureHttp);
3157
+ assets[key] = {
3158
+ url: raw.url,
3159
+ sha256: typeof raw.sha256 === "string" ? raw.sha256.toLowerCase() : void 0,
3160
+ size: typeof raw.size === "number" ? raw.size : void 0
3161
+ };
3162
+ }
3163
+ return { tag: m.tag, version: m.version, assets, source: "manifest" };
3164
+ }
3165
+ function assetKeyFromName(name) {
3166
+ const m = /^aielia-([a-z0-9]+-[a-z0-9]+?)(?:\.exe)?$/.exec(name);
3167
+ return m ? m[1] : null;
3168
+ }
3169
+ function parseGithubReleases(releases, allowInsecureHttp) {
3170
+ let best = null;
3171
+ for (const r of releases) {
3172
+ if (r.draft || r.prerelease || !r.tag_name.startsWith(RELEASE_TAG_PREFIX)) continue;
3173
+ const version = r.tag_name.slice(RELEASE_TAG_PREFIX.length);
3174
+ if (best && compareVersions(version, best.version) <= 0) continue;
3175
+ const assets = {};
3176
+ for (const a of r.assets ?? []) {
3177
+ const key = assetKeyFromName(a.name);
3178
+ if (!key) continue;
3179
+ assertHttps(a.browser_download_url, allowInsecureHttp);
3180
+ assets[key] = { url: a.browser_download_url, size: a.size };
3181
+ }
3182
+ best = { tag: r.tag_name, version, assets, source: "github" };
3183
+ }
3184
+ return best;
3185
+ }
3186
+ async function getJson(fetchFn, url, headers = {}) {
3187
+ return fetchFn(url, { headers, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), redirect: "follow" });
3188
+ }
3189
+ async function fetchLatestRelease(options, etag) {
3190
+ const fetchFn = options.fetchFn ?? fetch;
3191
+ const insecure = options.allowInsecureHttp ?? false;
3192
+ const manifestUrl = options.manifestUrl ?? MANIFEST_URL;
3193
+ const apiUrl = options.releasesApiUrl ?? RELEASES_API_URL;
3194
+ assertHttps(manifestUrl, insecure);
3195
+ assertHttps(apiUrl, insecure);
3196
+ let manifestError;
3197
+ try {
3198
+ const res2 = await getJson(fetchFn, manifestUrl);
3199
+ if (res2.ok) return { release: parseManifest(await res2.json(), insecure) };
3200
+ manifestError = new Error(`manifest HTTP ${res2.status}`);
3201
+ } catch (err) {
3202
+ manifestError = err;
3203
+ }
3204
+ const headers = { Accept: "application/vnd.github+json" };
3205
+ if (etag) headers["If-None-Match"] = etag;
3206
+ const res = await getJson(fetchFn, apiUrl, headers);
3207
+ if (res.status === 304 && etag) return { notModified: true };
3208
+ if (!res.ok) {
3209
+ throw new Error(`Could not check for updates (manifest: ${(manifestError == null ? void 0 : manifestError.message) ?? manifestError}; GitHub: HTTP ${res.status})`);
3210
+ }
3211
+ const release = parseGithubReleases(await res.json(), insecure);
3212
+ if (!release) throw new Error(`No ${RELEASE_TAG_PREFIX}* release found`);
3213
+ return { release, etag: res.headers.get("etag") ?? void 0 };
3214
+ }
3215
+ async function checkForUpdate(options = {}) {
3216
+ const now = (options.now ?? Date.now)();
3217
+ const dataDir = options.dataDir ?? defaultDataDir$1();
3218
+ const current = options.currentVersion ?? CLI_VERSION;
3219
+ const cached = readCache(dataDir);
3220
+ try {
3221
+ if ((cached == null ? void 0 : cached.latestVersion) && now - cached.checkedAt < CHECK_INTERVAL_MS) {
3222
+ return { latestVersion: cached.latestVersion, latestTag: cached.latestTag, updateAvailable: compareVersions(cached.latestVersion, current) > 0 };
3223
+ }
3224
+ const result = await fetchLatestRelease(options, cached == null ? void 0 : cached.etag);
3225
+ if ("notModified" in result) {
3226
+ writeCache(dataDir, { ...cached, checkedAt: now });
3227
+ const v = cached.latestVersion;
3228
+ return { latestVersion: v, latestTag: cached.latestTag, updateAvailable: compareVersions(v, current) > 0 };
3229
+ }
3230
+ writeCache(dataDir, { checkedAt: now, latestVersion: result.release.version, latestTag: result.release.tag, etag: result.etag });
3231
+ return { latestVersion: result.release.version, latestTag: result.release.tag, updateAvailable: compareVersions(result.release.version, current) > 0 };
3232
+ } catch {
3233
+ return null;
3234
+ }
3235
+ }
3236
+ function updateAvailableNotice(latestVersion, currentVersion, isSea) {
3237
+ const how = isSea ? "run `aielia update`" : `run \`${NPM_UPDATE_HINT}\``;
3238
+ return `A newer aielia is available (${currentVersion} → ${latestVersion}) — ${how}.`;
3239
+ }
3240
+ function isRunningAsSea() {
3241
+ return loadSea() !== null;
3242
+ }
3243
+ function cleanupStaleUpdateFiles(execPath = process.execPath) {
3244
+ for (const stale of [`${execPath}.old`, `${execPath}.update.tmp`]) {
3245
+ try {
3246
+ if (existsSync(stale)) rmSync(stale, { force: true });
3247
+ } catch {
3248
+ }
3249
+ }
3250
+ }
3251
+ function replaceBinary(execPath, newFile, platform = process.platform) {
3252
+ if (platform === "win32") {
3253
+ const aside = `${execPath}.old`;
3254
+ rmSync(aside, { force: true });
3255
+ renameSync(execPath, aside);
3256
+ try {
3257
+ renameSync(newFile, execPath);
3258
+ } catch (err) {
3259
+ renameSync(aside, execPath);
3260
+ throw err;
3261
+ }
3262
+ return;
3263
+ }
3264
+ chmodSync(newFile, 493);
3265
+ renameSync(newFile, execPath);
3266
+ }
3267
+ async function downloadTo(fetchFn, url, dest) {
3268
+ const res = await fetchFn(url, { signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS), redirect: "follow" });
3269
+ if (!res.ok || !res.body) throw new Error(`Download failed: HTTP ${res.status} for ${url}`);
3270
+ const hash = createHash("sha256");
3271
+ await pipeline(
3272
+ Readable.fromWeb(res.body),
3273
+ async function* (source) {
3274
+ for await (const chunk of source) {
3275
+ hash.update(chunk);
3276
+ yield chunk;
3277
+ }
3278
+ },
3279
+ createWriteStream(dest)
3280
+ );
3281
+ return hash.digest("hex");
3282
+ }
3283
+ async function fetchSidecarHash(fetchFn, assetUrl) {
3284
+ try {
3285
+ const res = await getJson(fetchFn, `${assetUrl}.sha256`);
3286
+ if (!res.ok) return null;
3287
+ const m = /[0-9a-fA-F]{64}/.exec(await res.text());
3288
+ return m ? m[0].toLowerCase() : null;
3289
+ } catch {
3290
+ return null;
3291
+ }
3292
+ }
3293
+ async function runUpdateCommand(input = {}) {
3294
+ const log = input.log ?? ((line) => console.log(line));
3295
+ const isSea = input.isSea ?? isRunningAsSea();
3296
+ const current = input.currentVersion ?? CLI_VERSION;
3297
+ const dataDir = input.dataDir ?? defaultDataDir$1();
3298
+ const fetchFn = input.fetchFn ?? fetch;
3299
+ const insecure = input.allowInsecureHttp ?? false;
3300
+ if (!isSea) {
3301
+ log(`aielia update only replaces the standalone binary. This copy was installed with npm — run: ${NPM_UPDATE_HINT}`);
3302
+ return 1;
3303
+ }
3304
+ let release;
3305
+ try {
3306
+ const result = await fetchLatestRelease(input);
3307
+ if ("notModified" in result) throw new Error("unexpected 304");
3308
+ release = result.release;
3309
+ writeCache(dataDir, { checkedAt: (input.now ?? Date.now)(), latestVersion: release.version, latestTag: release.tag, etag: result.etag });
3310
+ } catch (err) {
3311
+ log(`Could not check for updates: ${err.message}`);
3312
+ return 1;
3313
+ }
3314
+ if (compareVersions(release.version, current) <= 0) {
3315
+ log(`aielia ${current} is up to date.`);
3316
+ return 0;
3317
+ }
3318
+ const key = platformKey(input);
3319
+ const asset = release.assets[key];
3320
+ if (!asset) {
3321
+ log(`Release ${release.tag} has no build for ${key}.`);
3322
+ return 1;
3323
+ }
3324
+ if (input.dryRun) {
3325
+ log(`Update available: ${current} → ${release.version} (${release.tag}). Would download ${asset.url}`);
3326
+ return 0;
3327
+ }
3328
+ const execPath = input.execPath ?? process.execPath;
3329
+ const tmp = `${execPath}.update.tmp`;
3330
+ try {
3331
+ assertHttps(asset.url, insecure);
3332
+ log(`Downloading aielia ${release.version} (${key})…`);
3333
+ const actual = await downloadTo(fetchFn, asset.url, tmp);
3334
+ const sidecar = await fetchSidecarHash(fetchFn, asset.url);
3335
+ const expected = asset.sha256 ?? sidecar;
3336
+ if (!expected) throw new Error("No checksum available for this release — refusing to install an unverified binary");
3337
+ if (actual !== expected) throw new Error(`Checksum mismatch (expected ${expected}, got ${actual}) — download discarded`);
3338
+ if (asset.sha256 && sidecar && sidecar !== asset.sha256) {
3339
+ throw new Error("Checksum sidecar disagrees with the release manifest — download discarded");
3340
+ }
3341
+ mkdirSync(dirname(execPath), { recursive: true });
3342
+ replaceBinary(execPath, tmp, input.platform);
3343
+ log(`Updated aielia ${current} → ${release.version}. Restart to use the new version.`);
3344
+ return 0;
3345
+ } catch (err) {
3346
+ rmSync(tmp, { force: true });
3347
+ log(`Update failed: ${err.message}`);
3348
+ return 1;
3349
+ }
3350
+ }
3351
+ const defaultDataDir = join(homedir(), ".buildaharness", "personal-assistant");
3352
+ const defaultConfigStore = new NodeConfigStore(join(defaultDataDir, "config.json"));
3353
+ const defaultRemindersFile = join(defaultDataDir, "reminders", "reminders.json");
3354
+ const defaultEnvOverrides = envOverridesFromProcessEnv(process.env);
3355
+ const defaultNonInteractiveApprovalMode = resolveNonInteractiveApprovalMode(process.env);
3356
+ const defaultBackend = createNodeFsBackend();
3357
+ function buildEmailSender(config) {
3358
+ if (!config.enableEmail || !config.emailFrom) return void 0;
3359
+ if (config.emailProvider === "smtp" && config.smtpHost && config.smtpPort) {
3360
+ return createSmtpSender({
3361
+ host: config.smtpHost,
3362
+ port: config.smtpPort,
3363
+ auth: config.smtpUser && config.smtpPass ? { user: config.smtpUser, pass: config.smtpPass } : void 0,
3364
+ from: config.emailFrom
3365
+ });
3366
+ }
3367
+ if (config.emailProvider === "resend" && config.resendApiKey) {
3368
+ return createResendSender({ apiKey: config.resendApiKey, from: config.emailFrom });
3369
+ }
3370
+ return void 0;
3371
+ }
3372
+ function buildLlmClient(config, workspaceRoot, remindersFile) {
3373
+ switch (config.llmBackend) {
3374
+ case "claude-cli":
3375
+ return new ClaudeCliLLMClient({
3376
+ fileTools: { workspaceRoot },
3377
+ remindersFile,
3378
+ shellTools: config.enableShell ? { workspaceRoot } : void 0,
3379
+ webTools: config.enableWeb ? { braveApiKey: config.braveApiKey } : void 0,
3380
+ actionTools: config.enableEmail ? { workspaceRoot } : void 0
3381
+ });
3382
+ case "anthropic":
3383
+ return new AnthropicLLMClient({ apiKey: config.apiKey ?? "" });
3384
+ case "openai":
3385
+ return new OpenAICompatibleLLMClient({ apiKey: config.apiKey ?? "", baseUrl: OPENAI_BASE_URL, defaultModel: OPENAI_DEFAULT_MODEL });
3386
+ case "openrouter":
3387
+ return new OpenAICompatibleLLMClient({
3388
+ apiKey: config.apiKey ?? "",
3389
+ baseUrl: OPENROUTER_BASE_URL,
3390
+ defaultModel: OPENROUTER_DEFAULT_MODEL,
3391
+ extraHeaders: OPENROUTER_EXTRA_HEADERS
3392
+ });
3393
+ case "proxy":
3394
+ return new LLMClient({ proxyUrl: config.proxyUrl, authToken: config.authToken });
3395
+ }
3396
+ }
3397
+ async function buildAssistant(config, { backend, dataDir, remindersFile }) {
3398
+ const workspaceRoot = config.workspaceRoot ?? process.cwd();
3399
+ const search = (query) => braveSearch(query, config.braveApiKey);
3400
+ const llmClient = buildLlmClient(config, workspaceRoot, remindersFile);
3401
+ return PersonalAssistant.create({
3402
+ llmClient,
3403
+ model: config.model,
3404
+ // config.activeProject (set via /project <name>) overrides the default of "the workspace
3405
+ // I'm running in" — see PersonalAssistantOptions.activeProject's doc comment.
3406
+ activeProject: config.activeProject || workspaceRoot,
3407
+ memoryBudgetChars: config.memoryBudgetChars,
3408
+ memoryWriteMode: config.memoryWriteMode,
3409
+ // M5: consolidation proposals are staged for /memory consolidate; the CLI has the controls, so it may raise them on /new.
3410
+ consolidateOnNewSession: true,
3411
+ memory: new FileSystemAdapter({ backend, baseDir: dataDir, namespace: "transcripts" }),
3412
+ experienceStore: await FileSystemExperienceStore.create({ backend, baseDir: dataDir, namespace: "experience" }),
3413
+ checkpointStore: new FileSystemAdapter({ backend, baseDir: dataDir, namespace: "checkpoints" }),
3414
+ reminderStore: new InMemoryReminderStore(new FileSystemAdapter({ backend, baseDir: dataDir, namespace: "reminders" })),
3415
+ fileTools: { backend, workspaceRoot },
3416
+ webTools: config.enableWeb ? { search } : void 0,
3417
+ // executeCommand is the real child_process.spawn-based implementation (shell-executor.ts) —
3418
+ // wired in here, not inside assistant.ts, so the browser build never needs node:child_process.
3419
+ shellTools: config.enableShell ? { backend, workspaceRoot, timeoutMs: config.shellTimeoutMs, networkAllowlist: config.shellNetworkAllowlist, executeCommand: runApprovedShellCommand } : void 0,
3420
+ // The Resend/SMTP transport is wired in here, not inside assistant.ts, so the browser build
3421
+ // never needs nodemailer or a mail server — same split as shellTools' executeCommand.
3422
+ actionTools: (() => {
3423
+ const sendEmail = buildEmailSender(config);
3424
+ return sendEmail ? { backend, workspaceRoot, sendEmail } : void 0;
3425
+ })(),
3426
+ dangerouslySkipPermissions: config.dangerouslySkipPermissions,
3427
+ spendCap: config.sessionCostLimitUsd !== void 0 || config.sessionCallLimit !== void 0 ? { sessionCostLimitUsd: config.sessionCostLimitUsd, sessionCallLimit: config.sessionCallLimit } : void 0,
3428
+ // Resolved through the shared config seam (cli-config.ts's envOverridesFromProcessEnv reads
3429
+ // ASSISTANT_ONE_LOOP into config.oneLoopMode; /config set oneLoopMode persists it) so the CLI,
3430
+ // chat-ui, and the Tauri desktop build all decide this the same way. Undefined here means
3431
+ // PersonalAssistant falls back to DEFAULT_ONE_LOOP_MODE.
3432
+ oneLoopMode: config.oneLoopMode,
3433
+ layerPolicyMode: config.layerPolicyMode,
3434
+ // R7: turn-end next-step options and DONE-thread suggestions. Undefined config means the
3435
+ // package default (enabled); PersonalAssistant itself defaults to disabled, so this is where
3436
+ // the front end opts in.
3437
+ goalGraphSuggestMode: isGoalGraphSuggestEnabled(config.goalGraphSuggestMode) ? "enabled" : "disabled",
3438
+ // P11 of the internal plan — resolved through the same shared config
3439
+ // seam as oneLoopMode above (ASSISTANT_PLAN_MODE / `/config set planMode`). Undefined here
3440
+ // means PersonalAssistant falls back to DEFAULT_PLAN_MODE ('legacy').
3441
+ planMode: config.planMode,
3442
+ ambiguityGuardMode: config.ambiguityGuardMode
3443
+ });
3444
+ }
3445
+ async function runCli(options = {}) {
3446
+ console.log("Aielia is alpha software — expect rough edges and breaking changes. It uses AI models and can make mistakes; verify anything important.\n");
3447
+ const dataDir = options.dataDir ?? defaultDataDir;
3448
+ const configStore = options.configStore ?? defaultConfigStore;
3449
+ const backend = options.backend ?? defaultBackend;
3450
+ const remindersFile = options.remindersFile ?? defaultRemindersFile;
3451
+ const envOverrides = options.envOverrides ?? defaultEnvOverrides;
3452
+ const nonInteractiveApprovalMode = options.nonInteractiveApprovalMode ?? defaultNonInteractiveApprovalMode;
3453
+ const inputStream = options.input ?? process.stdin;
3454
+ const outputStream = options.output ?? process.stdout;
3455
+ let persisted = await configStore.load();
3456
+ let { config, overriddenKeys } = resolveConfig(persisted, envOverrides);
3457
+ let layerPins = applyLayerSettings(sanitizeLayerChoices(config.layers), process.env).pinned;
3458
+ if (!options.skipFirstRunSetup && !options.assistant) {
3459
+ const isInteractive = inputStream.isTTY === true;
3460
+ let setupRl;
3461
+ const firstRunAsk = options.firstRunAsk ?? ((question) => {
3462
+ setupRl ?? (setupRl = createInterface({ input: inputStream, output: outputStream }));
3463
+ return new Promise((resolvePrompt) => {
3464
+ setupRl.question(question, (answer) => resolvePrompt(answer.trim()));
3465
+ });
3466
+ });
3467
+ try {
3468
+ persisted = await maybeRunFirstRunSetup({
3469
+ configStore,
3470
+ persisted,
3471
+ overriddenKeys,
3472
+ isInteractive,
3473
+ ask: firstRunAsk,
3474
+ log: (line) => outputStream.write(`${line}
3475
+ `),
3476
+ testKey: options.firstRunTestKey ?? ((backend2, key) => testApiKey(backend2, key))
3477
+ });
3478
+ } finally {
3479
+ setupRl == null ? void 0 : setupRl.close();
3480
+ }
3481
+ ({ config, overriddenKeys } = resolveConfig(persisted, envOverrides));
3482
+ layerPins = applyLayerSettings(sanitizeLayerChoices(config.layers), process.env).pinned;
3483
+ }
3484
+ try {
3485
+ validateConfig({}, config);
3486
+ } catch (err) {
3487
+ if (!(err instanceof ConfigValidationError)) throw err;
3488
+ console.error(err.message);
3489
+ process.exit(1);
3490
+ }
3491
+ if (nonInteractiveApprovalMode === "require-tty" && !process.stdin.isTTY) {
3492
+ console.error(
3493
+ `ASSISTANT_NON_INTERACTIVE_APPROVAL=require-tty is set, but stdin is not a real TTY (piped/scripted input). Refusing to start rather than fail confusingly at the first approval prompt — see README.md's "Non-interactive / scripted use" section for the alternative (ASSISTANT_NON_INTERACTIVE_APPROVAL=decline).`
3494
+ );
3495
+ process.exit(1);
3496
+ }
3497
+ let assistant = options.assistant ?? await buildAssistant(config, { dataDir, backend, remindersFile });
3498
+ const rl = createInterface({ input: options.input ?? process.stdin, output: options.output ?? process.stdout, prompt: "you> " });
3499
+ rl.pause();
3500
+ rl.on("SIGINT", () => {
3501
+ console.log("\nExiting.");
3502
+ process.exit(0);
3503
+ });
3504
+ const backendDisplayModel = {
3505
+ proxy: ANTHROPIC_DEFAULT_MODEL,
3506
+ "claude-cli": "(your Claude Code default)",
3507
+ anthropic: ANTHROPIC_DEFAULT_MODEL,
3508
+ openai: OPENAI_DEFAULT_MODEL,
3509
+ openrouter: OPENROUTER_DEFAULT_MODEL
3510
+ };
3511
+ console.log(`backend: ${config.llmBackend} (${config.model ?? backendDisplayModel[config.llmBackend]})`);
3512
+ const enabledCapabilities = [];
3513
+ if (config.enableWeb) enabledCapabilities.push("web search/fetch (brave)");
3514
+ if (config.enableShell) enabledCapabilities.push(`shell commands (${config.dangerouslySkipPermissions ? "NOT approval-gated" : "approval-gated"})`);
3515
+ const capabilitySuffix = enabledCapabilities.length > 0 ? ` — enabled: ${enabledCapabilities.join(", ")}` : "";
3516
+ const dangerBanner = config.dangerouslySkipPermissions ? "\n⚠ dangerouslySkipPermissions is ON — every approval prompt (risky messages, file writes, shell commands) is skipped automatically.\n" : "";
3517
+ const nonInteractiveBanner = nonInteractiveApprovalMode === "decline" ? "\nASSISTANT_NON_INTERACTIVE_APPROVAL=decline is set — every approval prompt auto-declines.\n" : "";
3518
+ const startupUndoLogEntries = await assistant.listUndoLogEntries();
3519
+ const undoableFromBefore = startupUndoLogEntries.filter((e) => e.undoable).length;
3520
+ const undoBanner = undoableFromBefore > 0 ? `
3521
+ ${undoableFromBefore} action${undoableFromBefore === 1 ? "" : "s"} from earlier sessions ${undoableFromBefore === 1 ? "is" : "are"} still revertible — see /undo-action.
3522
+ ` : "";
3523
+ console.log(`Aielia — your personal assistant on the 11-layer harness, one turn at a time. Ctrl+C to exit.${capabilitySuffix}
3524
+ ${dangerBanner}${nonInteractiveBanner}${undoBanner}`);
3525
+ console.log("Type /help to see all commands, /config to view settings.\n");
3526
+ void assistant.proposeMemoryConsolidation("cli").catch(() => void 0);
3527
+ rl.prompt();
3528
+ let lastTrace;
3529
+ let lastSources;
3530
+ let lastPlanStatus;
3531
+ let pendingPlanApproval;
3532
+ let lastTurnUsage;
3533
+ let lastTurnToolSteps = [];
3534
+ let lastNoTraceReason;
3535
+ const rememberedActionKinds = /* @__PURE__ */ new Set();
3536
+ const rememberedRiskLevels = /* @__PURE__ */ new Set();
3537
+ const APPROVAL_OPTIONS = [
3538
+ { key: "y", label: "Yes" },
3539
+ { key: "a", label: "Yes, don't ask again this session" },
3540
+ { key: "n", label: "No" }
3541
+ ];
3542
+ function withCostEstimate(usage) {
3543
+ if (config.llmBackend === "claude-cli" || usage.costUsd !== void 0) return usage;
3544
+ const estimated = estimateCostUsd(config.model ?? ANTHROPIC_DEFAULT_MODEL, usage);
3545
+ return estimated !== void 0 ? { ...usage, costUsd: estimated } : usage;
3546
+ }
3547
+ const PLAN_TASK_STATUS_ICON = {
3548
+ PENDING: "○",
3549
+ RUNNING: "▶",
3550
+ COMPLETE: "✓",
3551
+ FAILED: "✗",
3552
+ BLOCKED: "✗",
3553
+ HUMAN_REQUIRED: "~"
3554
+ };
3555
+ function printPlan() {
3556
+ if (!lastPlanStatus) {
3557
+ console.log("\nNo active plan for this session.\n");
3558
+ return;
3559
+ }
3560
+ const lines = [
3561
+ PLAN_LINE_PREFIX,
3562
+ `Plan: ${lastPlanStatus.templateName ?? "custom plan"} (${lastPlanStatus.completionPct.toFixed(1)}% complete)`,
3563
+ ...lastPlanStatus.tasks.flatMap((task) => [
3564
+ ` ${PLAN_TASK_STATUS_ICON[task.status] ?? "?"} [${task.status}] ${task.id} — ${task.description}`,
3565
+ ...task.note ? [` not accepted as done: ${task.note}`] : []
3566
+ ]),
3567
+ `Success criteria: ${lastPlanStatus.successCriteria}`
3568
+ ];
3569
+ console.log(`
3570
+ ${lines.join("\n")}
3571
+ `);
3572
+ }
3573
+ async function handlePlanSketch(args) {
3574
+ const request = args.join(" ").trim();
3575
+ if (!request) {
3576
+ console.log("\nUsage: /plan sketch <request>\n");
3577
+ return;
3578
+ }
3579
+ console.log("\nSketching a plan (read-only grounding only, nothing staged)...\n");
3580
+ const result = await assistant.sketchPlan("cli", request);
3581
+ console.log(`Aielia> ${result.reply ?? "(no reply)"}
3582
+ `);
3583
+ if (result.usage) lastTurnUsage = withCostEstimate(result.usage);
3584
+ }
3585
+ const PLAN_APPROVAL_OPTIONS = [
3586
+ { key: "y", label: "Approve" },
3587
+ { key: "e", label: "Approve with edits" },
3588
+ { key: "t", label: "Approve & trust this plan (its write/shell steps stop re-prompting)" },
3589
+ { key: "d", label: "Decide later — /plan approve|edit|decline" },
3590
+ { key: "n", label: "Decline" }
3591
+ ];
3592
+ function printPlanApproval(snapshot) {
3593
+ const lines = [
3594
+ PLAN_LINE_PREFIX,
3595
+ `Plan ready for approval${snapshot.templateName ? `: ${snapshot.templateName}` : " (custom plan)"}`,
3596
+ `Success criteria: ${snapshot.successCriteria}`,
3597
+ ...snapshot.rationale ? [`Why: ${snapshot.rationale}`] : [],
3598
+ ...snapshot.tasks.map((task) => ` ${task.riskLevel === "HIGH" ? "!" : task.riskLevel === "MEDIUM" ? "·" : "○"} [${task.riskLevel ?? "LOW"}] ${task.id} — ${task.description}`),
3599
+ ...snapshot.reviewNotes && snapshot.reviewNotes.length > 0 ? ["Review notes:", ...snapshot.reviewNotes.map((note) => ` - ${note}`)] : []
3600
+ ];
3601
+ console.log(`
3602
+ ${lines.join("\n")}
3603
+ `);
3604
+ }
3605
+ async function promptPlanEdits(snapshot) {
3606
+ const knownIds = new Set(snapshot.tasks.map((task) => task.id));
3607
+ const cancelRaw = await askLine("Task ids to cancel (comma-separated, blank for none): ");
3608
+ const editRaw = await askLine('Edits as "id=new description" pairs separated by ";", e.g. t2=Reword this. (blank for none): ');
3609
+ const cancelTaskIds = cancelRaw.split(/[\s,]+/).map((s) => s.trim()).filter(Boolean);
3610
+ const parsedEdits = editRaw.split(";").map((pair) => pair.trim()).filter(Boolean).map((pair) => {
3611
+ const sep = pair.indexOf("=");
3612
+ return sep >= 0 ? { id: pair.slice(0, sep).trim(), description: pair.slice(sep + 1).trim() } : { id: pair, description: "" };
3613
+ });
3614
+ const validCancels = cancelTaskIds.filter((id) => knownIds.has(id));
3615
+ const validEdits = parsedEdits.filter((edit) => knownIds.has(edit.id) && edit.description);
3616
+ const unknown = [...cancelTaskIds.filter((id) => !knownIds.has(id)), ...parsedEdits.filter((e) => !knownIds.has(e.id)).map((e) => e.id)];
3617
+ if (unknown.length > 0) console.log(` [ignoring unknown task id(s): ${[...new Set(unknown)].join(", ")}]`);
3618
+ return {
3619
+ ...validCancels.length > 0 ? { cancelTaskIds: validCancels } : {},
3620
+ ...validEdits.length > 0 ? { editedTasks: validEdits } : {}
3621
+ };
3622
+ }
3623
+ async function handlePlanApprovalCommand(sub) {
3624
+ if (!pendingPlanApproval) {
3625
+ console.log("\nNo plan is awaiting approval.\n");
3626
+ return;
3627
+ }
3628
+ const { id, message, snapshot } = pendingPlanApproval;
3629
+ let decision;
3630
+ let edits;
3631
+ if (sub === "approve") {
3632
+ decision = "approve";
3633
+ } else if (sub === "trust") {
3634
+ decision = "approve_trusted";
3635
+ } else if (sub === "edit") {
3636
+ if (!snapshot) {
3637
+ console.log("\n[tasks unavailable for editing — /plan approve or /plan decline]\n");
3638
+ return;
3639
+ }
3640
+ printPlanApproval(snapshot);
3641
+ edits = await promptPlanEdits(snapshot);
3642
+ decision = "approve_with_edits";
3643
+ } else {
3644
+ decision = "decline";
3645
+ }
3646
+ pendingPlanApproval = void 0;
3647
+ lastTrace = void 0;
3648
+ lastNoTraceReason = `No harness trace — the last turn staged a plan that was ${decision === "decline" ? "declined" : "approved"} before the harness ran.`;
3649
+ await handleTurn(message, false, void 0, void 0, void 0, id, decision, edits);
3650
+ }
3651
+ async function loadPlanGraphNodes(live, threadId) {
3652
+ const record = await assistant.getPlanGraph("cli");
3653
+ if (!record) return void 0;
3654
+ let nodes = planToSnapshot(record, live ?? []).nodes;
3655
+ if (threadId) nodes = nodes.filter((n) => n.id.startsWith(`${threadId}__`));
3656
+ return nodes.length ? nodes : void 0;
3657
+ }
3658
+ async function handlePlanGraph(args) {
3659
+ const nodes = await loadPlanGraphNodes(void 0, args[0]);
3660
+ if (!nodes) {
3661
+ console.log(args[0] ? `
3662
+ No plan graph for thread "${args[0]}".
3663
+ ` : "\nNo active plan for this session.\n");
3664
+ return;
3665
+ }
3666
+ if (options.openPlanGraph) {
3667
+ const probe = renderPlan(nodes, { maxCols: 80 });
3668
+ if (probe.ok) {
3669
+ options.openPlanGraph(nodes);
3670
+ return;
3671
+ }
3672
+ console.log(`
3673
+ [plan graph unavailable: ${probe.message}] showing the checklist instead.`);
3674
+ printPlan();
3675
+ return;
3676
+ }
3677
+ const cols = process.stdout.columns || 80;
3678
+ const color = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
3679
+ const result = renderPlan(nodes, { maxCols: cols, color });
3680
+ if (!result.ok) {
3681
+ console.log(`
3682
+ [plan graph unavailable: ${result.message}] showing the checklist instead.`);
3683
+ printPlan();
3684
+ return;
3685
+ }
3686
+ if (!result.fits) {
3687
+ console.log(`
3688
+ [plan graph is wider than ${cols} columns] dependency list:
3689
+ `);
3690
+ for (const n of nodes) console.log(` [${n.status}] ${n.id} — ${n.label}${n.deps.length ? ` (after: ${n.deps.join(", ")})` : ""}`);
3691
+ console.log("");
3692
+ return;
3693
+ }
3694
+ console.log(`
3695
+ ${result.lines.join("\n")}
3696
+ `);
3697
+ }
3698
+ async function handlePlan(args) {
3699
+ const sub = args[0];
3700
+ if (sub === "graph" && isPlanGraphEnabled(config.planGraphMode)) {
3701
+ await handlePlanGraph(args.slice(1));
3702
+ return;
3703
+ }
3704
+ if (sub === "sketch") {
3705
+ await handlePlanSketch(args.slice(1));
3706
+ return;
3707
+ }
3708
+ if (sub === "approve" || sub === "trust" || sub === "edit" || sub === "decline") {
3709
+ await handlePlanApprovalCommand(sub);
3710
+ return;
3711
+ }
3712
+ if (pendingPlanApproval == null ? void 0 : pendingPlanApproval.snapshot) {
3713
+ printPlanApproval(pendingPlanApproval.snapshot);
3714
+ return;
3715
+ }
3716
+ printPlan();
3717
+ }
3718
+ function verificationHealthLabel({ strength, feasibility }) {
3719
+ const confidence = Math.min(strength, feasibility);
3720
+ if (confidence >= 0.7) return "High confidence";
3721
+ if (confidence >= 0.4) return "Reasonably confident";
3722
+ return "Worth double-checking";
3723
+ }
3724
+ function batchBudgetSummaryLine(batchBudget) {
3725
+ const found = batchBudget.perItemOutcomes.filter((o) => o.status === "found").length;
3726
+ const notFound = batchBudget.perItemOutcomes.filter((o) => o.status === "not_found").length;
3727
+ const truncated = batchBudget.perItemOutcomes.filter((o) => o.status === "truncated_while_productive").length;
3728
+ return `Batch: ${batchBudget.itemCount} items — ${found} found, ${notFound} not found, ${truncated} truncated (${batchBudget.totalCallsUsed} calls used, projected ~${Math.ceil(batchBudget.projectedTotal)})`;
3729
+ }
3730
+ function printWhy() {
3731
+ const memoryLine = formatMemoryInjection(assistant.getLastMemoryInjection());
3732
+ if (!lastTrace) {
3733
+ console.log(`
3734
+ ${lastNoTraceReason ?? "No harness trace for the last turn (nothing to explain yet, or it took the fast path)."}
3735
+ ${memoryLine}
3736
+ `);
3737
+ return;
3738
+ }
3739
+ console.log(`
3740
+ ${verificationHealthLabel(lastTrace.verificationHealth)}`);
3741
+ const chain = buildWhyChain(lastTrace.layerActivity);
3742
+ if (chain.length > 0) {
3743
+ console.log(" " + chain.map((item) => `${LAYER_SHORT_CODE[item.layer]} (${item.reason})`).join(" > "));
3744
+ }
3745
+ if (lastTrace.batchBudget) {
3746
+ console.log(" " + batchBudgetSummaryLine(lastTrace.batchBudget));
3747
+ }
3748
+ console.log(memoryLine);
3749
+ console.log("");
3750
+ }
3751
+ function printLayers() {
3752
+ if (!lastTrace) {
3753
+ console.log(`
3754
+ ${lastNoTraceReason ?? "No harness trace for the last turn (nothing to explain yet, or it took the fast path)."}
3755
+ `);
3756
+ return;
3757
+ }
3758
+ console.log("");
3759
+ const byLayer = new Map(lastTrace.layerActivity.map((e) => [e.layer, e]));
3760
+ for (const layer of LAYER_ORDER) {
3761
+ const e = byLayer.get(layer);
3762
+ const mark = (e == null ? void 0 : e.fired) ? "✓" : "·";
3763
+ const reason = (e == null ? void 0 : e.reason) ?? "not evaluated this turn";
3764
+ console.log(` [${mark}] ${LAYER_DISPLAY_NAME[layer].padEnd(22)} ${reason}`);
3765
+ }
3766
+ if (lastTrace.batchBudget) {
3767
+ console.log("");
3768
+ console.log(` ${batchBudgetSummaryLine(lastTrace.batchBudget)}`);
3769
+ const STATUS_MARK = {
3770
+ found: "✓",
3771
+ not_found: "✗",
3772
+ truncated_while_productive: "~"
3773
+ };
3774
+ for (const outcome of lastTrace.batchBudget.perItemOutcomes) {
3775
+ console.log(` [${STATUS_MARK[outcome.status]}] ${outcome.item.padEnd(30)} ${outcome.status} (${outcome.callsUsed} calls)`);
3776
+ }
3777
+ }
3778
+ console.log("");
3779
+ }
3780
+ const SOURCE_TOOL_LABEL = {
3781
+ read_file: "Read",
3782
+ list_directory: "Listed",
3783
+ web_search: "Searched",
3784
+ fetch_url: "Fetched"
3785
+ };
3786
+ function printSources() {
3787
+ if (!lastSources || lastSources.length === 0) {
3788
+ console.log("\nNo sources for the last turn (it used no file/web tool calls).\n");
3789
+ return;
3790
+ }
3791
+ console.log("");
3792
+ for (const source of lastSources) {
3793
+ console.log(` - ${SOURCE_TOOL_LABEL[source.tool]} ${source.path}`);
3794
+ }
3795
+ console.log("");
3796
+ }
3797
+ function printHelp() {
3798
+ console.log(`
3799
+ ${formatHelp()}
3800
+ `);
3801
+ }
3802
+ async function handleClear() {
3803
+ await assistant.clearSession("cli");
3804
+ lastTrace = void 0;
3805
+ lastSources = void 0;
3806
+ lastPlanStatus = void 0;
3807
+ pendingPlanApproval = void 0;
3808
+ lastTurnUsage = void 0;
3809
+ lastNoTraceReason = void 0;
3810
+ console.log("\n✓ Started a fresh conversation.\n");
3811
+ }
3812
+ async function handleCheckpoint(args) {
3813
+ if (args[0] === "clear") {
3814
+ const result = await assistant.clearCheckpoint("cli");
3815
+ console.log(
3816
+ result.cleared ? `
3817
+ ✓ Cleared the stuck checkpoint (was at step ${result.stepsUsed}, node "${result.currentNode}"). Conversation history is untouched — your next message starts a fresh harness run.
3818
+ ` : "\nNo checkpoint to clear — the last turn either completed normally or there was nothing in progress.\n"
3819
+ );
3820
+ return;
3821
+ }
3822
+ const status = await assistant.getCheckpointStatus("cli");
3823
+ if (!status.present) {
3824
+ console.log("\nNo checkpoint present — the last turn completed normally or there was nothing in progress.\n");
3825
+ return;
3826
+ }
3827
+ const attemptsNote = status.failedResumeAttempts > 0 ? ` — failed to resume ${status.failedResumeAttempts} time${status.failedResumeAttempts === 1 ? "" : "s"} in a row so far` : "";
3828
+ console.log(
3829
+ `
3830
+ A checkpoint is present: step ${status.stepsUsed}, last node "${status.currentNode}"${attemptsNote}.
3831
+ Your next message will try to resume it automatically. Run /checkpoint clear to discard it and start fresh instead.
3832
+ `
3833
+ );
3834
+ }
3835
+ async function printStatus() {
3836
+ const transcript = await assistant.getTranscript("cli");
3837
+ const undoLogEntries = await assistant.listUndoLogEntries();
3838
+ const spendCapLine = await spendCapStatusLine();
3839
+ console.log(
3840
+ `
3841
+ ${formatStatus({ config, overriddenKeys, transcriptLength: transcript.length, planActive: lastPlanStatus !== void 0, undoLogEntries, spendCapLine })}
3842
+ `
3843
+ );
3844
+ }
3845
+ async function spendCapStatusLine() {
3846
+ if (config.sessionCostLimitUsd === void 0 && config.sessionCallLimit === void 0) return void 0;
3847
+ const state = await assistant.getSpendState("cli");
3848
+ return formatSpendCapStatus(state, { sessionCostLimitUsd: config.sessionCostLimitUsd, sessionCallLimit: config.sessionCallLimit });
3849
+ }
3850
+ async function handleExport(args) {
3851
+ const transcript = await assistant.getTranscript("cli");
3852
+ if (transcript.length === 0) {
3853
+ console.log("\nNothing to export yet.\n");
3854
+ return;
3855
+ }
3856
+ const filename = resolve(process.cwd(), args[0] ?? defaultExportFilename());
3857
+ try {
3858
+ await writeFile(filename, formatTranscriptMarkdown(transcript), "utf-8");
3859
+ console.log(`
3860
+ ✓ Exported ${transcript.length} message${transcript.length === 1 ? "" : "s"} to ${filename}
3861
+ `);
3862
+ } catch (err) {
3863
+ const { message } = classifyError(err);
3864
+ console.log(`
3865
+ [error] ${message}
3866
+ `);
3867
+ }
3868
+ }
3869
+ async function handleUndo() {
3870
+ const transcriptBefore = await assistant.getTranscript("cli");
3871
+ if (transcriptBefore.length === 0) {
3872
+ console.log("\nNothing to undo.\n");
3873
+ return;
3874
+ }
3875
+ const wasPendingApproval = transcriptBefore[transcriptBefore.length - 1].role !== "assistant";
3876
+ const undoneReminders = lastTurnToolSteps.filter((s) => s.tool === "create_reminder");
3877
+ const result = await assistant.undoLastTurn("cli");
3878
+ if (!result.undone) {
3879
+ console.log("\nNothing to undo.\n");
3880
+ return;
3881
+ }
3882
+ lastTrace = void 0;
3883
+ lastSources = void 0;
3884
+ lastPlanStatus = void 0;
3885
+ lastTurnToolSteps = [];
3886
+ const caveat = undoneReminders.length > 0 ? ` Note: ${undoneReminders.length === 1 ? "a reminder" : `${undoneReminders.length} reminders`} created in that exchange (${undoneReminders.map((s) => `"${s.input.text}"`).join(", ")}) ${undoneReminders.length === 1 ? "is" : "are"} still active — /undo only removes chat history, not that side effect.` : "";
3887
+ console.log(
3888
+ `
3889
+ ✓ Removed ${wasPendingApproval ? "the pending message awaiting approval" : "the last exchange (1 user message, 1 assistant reply)"}.${caveat}
3890
+ `
3891
+ );
3892
+ }
3893
+ async function handleUndoAction(args) {
3894
+ if (args.length === 0) {
3895
+ const entries = await assistant.listUndoLogEntries();
3896
+ console.log(`
3897
+ ${formatUndoLogListing(entries)}
3898
+ `);
3899
+ return;
3900
+ }
3901
+ const staged = await assistant.stageUndoAction(args[0]);
3902
+ if (staged.status === "error") {
3903
+ console.log(`
3904
+ ✗ ${staged.message}
3905
+ `);
3906
+ return;
3907
+ }
3908
+ console.log(`
3909
+ [needs approval — revert] ${staged.reason}`);
3910
+ const confirmed = await askYesNo("Apply this revert? (y/N) ");
3911
+ lastTrace = void 0;
3912
+ lastNoTraceReason = `No harness trace — the last turn was a staged revert that was ${confirmed ? "approved" : "declined"} before the harness ran.`;
3913
+ await handleTurn("", confirmed, staged.pendingActionId);
3914
+ }
3915
+ async function printMemory() {
3916
+ const summary = await assistant.getMemorySummary("cli");
3917
+ const digests = await assistant.listSessionDigests();
3918
+ const digestLine = digests.length > 0 ? `
3919
+ Session digests stored: ${digests.length} (/memory forget digest [id] erases them; /memory export includes them).
3920
+ ` : "";
3921
+ console.log(`
3922
+ ${formatMemoryStatus(await assistant.getMemoryStatus("cli"))}
3923
+
3924
+ ${formatMemorySummary(summary)}
3925
+ ${digestLine}`);
3926
+ }
3927
+ async function handleMemoryExport(args) {
3928
+ const data = await assistant.exportMemory("cli");
3929
+ const filename = resolve(process.cwd(), args[0] ?? defaultMemoryExportFilename());
3930
+ try {
3931
+ await writeFile(filename, formatMemoryExport(data), "utf-8");
3932
+ console.log(`
3933
+ ✓ Exported learned memory to ${filename}
3934
+ `);
3935
+ } catch (err) {
3936
+ const { message } = classifyError(err);
3937
+ console.log(`
3938
+ [error] ${message}
3939
+ `);
3940
+ }
3941
+ }
3942
+ async function handleMemoryPending(action, args) {
3943
+ const selector = args[0];
3944
+ if (!selector) {
3945
+ console.log(`
3946
+ Usage: /memory ${action} <n> or /memory ${action} <category>
3947
+ `);
3948
+ return;
3949
+ }
3950
+ const outcome = action === "confirm" ? await assistant.confirmPendingFact(selector) : await assistant.rejectPendingFact(selector);
3951
+ console.log(`
3952
+ ${formatMemoryPendingOutcome(action === "confirm" ? "confirmed" : "rejected", outcome)}
3953
+ `);
3954
+ }
3955
+ async function handleMemoryForget(args) {
3956
+ const selector = args[0];
3957
+ if (!selector) {
3958
+ console.log("\nUsage: /memory forget <n> [erase]\n");
3959
+ return;
3960
+ }
3961
+ const erase = args[1] === "erase";
3962
+ const outcome = await assistant.forgetFact(selector, "cli", erase);
3963
+ const note = outcome.ok && !erase && memoryAuditLogEnabled() ? " The audit log still holds its text (see /memory history); use `/memory forget <n> erase` to remove that too." : "";
3964
+ console.log(`
3965
+ ${formatMemoryPendingOutcome("forgotten", outcome)}${note}
3966
+ `);
3967
+ }
3968
+ async function handleMemory(args) {
3969
+ if (args[0] === "export") {
3970
+ await handleMemoryExport(args.slice(1));
3971
+ return;
3972
+ }
3973
+ if (args[0] === "confirm" || args[0] === "reject") {
3974
+ await handleMemoryPending(args[0], args.slice(1));
3975
+ return;
3976
+ }
3977
+ if (args[0] === "forget" && (args[1] === "digest" || args[1] === "digests")) {
3978
+ const removed = await assistant.forgetDigests(args[2]);
3979
+ console.log(`
3980
+ Forgot ${removed} session digest${removed === 1 ? "" : "s"}.
3981
+ `);
3982
+ return;
3983
+ }
3984
+ if (args[0] === "forget") {
3985
+ await handleMemoryForget(args.slice(1));
3986
+ return;
3987
+ }
3988
+ if (args[0] === "history") {
3989
+ console.log(`
3990
+ ${formatMemoryHistory(await assistant.memoryHistory())}
3991
+ `);
3992
+ return;
3993
+ }
3994
+ if (args[0] === "status") {
3995
+ console.log(`
3996
+ ${formatMemoryStatus(await assistant.getMemoryStatus("cli"))}
3997
+ `);
3998
+ return;
3999
+ }
4000
+ if (args[0] === "archive") {
4001
+ if (args[1] === "forget") {
4002
+ const outcome = await assistant.forgetArchivedFact(args[2] ?? "");
4003
+ console.log(`
4004
+ ${formatMemoryPendingOutcome("forgotten", outcome)}
4005
+ `);
4006
+ return;
4007
+ }
4008
+ if (args[1] === "restore") {
4009
+ const outcome = await assistant.restoreArchivedMemory(args[2] ?? "", "cli");
4010
+ console.log(`
4011
+ ${outcome.message}
4012
+ `);
4013
+ return;
4014
+ }
4015
+ console.log(`
4016
+ ${formatMemoryArchive(await assistant.listArchivedFacts(), await assistant.restorableArchiveCount())}
4017
+ `);
4018
+ return;
4019
+ }
4020
+ if (args[0] === "off" || args[0] === "on") {
4021
+ await assistant.setMemoryEnabled(args[0] === "on");
4022
+ console.log(args[0] === "off" ? "\nMemory writes are OFF for this install: nothing new will be saved (facts, pending guesses, digests). What is already stored stays readable; /memory forget and /memory reject still work. /memory on resumes.\n" : "\nMemory writes are on.\n");
4023
+ return;
4024
+ }
4025
+ if (args[0] === "undo") {
4026
+ const outcome = await assistant.undoMemoryChange(args[1] ?? "", "cli");
4027
+ console.log(`
4028
+ ${outcome.message}
4029
+ `);
4030
+ return;
4031
+ }
4032
+ if (args[0] === "consolidate") {
4033
+ await handleMemoryConsolidate(args.slice(1));
4034
+ return;
4035
+ }
4036
+ await printMemory();
4037
+ }
4038
+ async function handleMemoryConsolidate(args) {
4039
+ if (args[0] === "accept" || args[0] === "dismiss") {
4040
+ const outcome2 = args[0] === "accept" ? await assistant.acceptMemoryProposal(args[1] ?? "", "cli") : await assistant.dismissMemoryProposal(args[1] ?? "");
4041
+ console.log(`
4042
+ ${outcome2.message}
4043
+ `);
4044
+ return;
4045
+ }
4046
+ const outcome = await assistant.consolidateMemory("cli");
4047
+ const proposals = await assistant.memoryProposals();
4048
+ if (proposals.length === 0) {
4049
+ console.log(`
4050
+ ${outcome.message}
4051
+ `);
4052
+ return;
4053
+ }
4054
+ console.log(`
4055
+ ${outcome.status === "done" ? "" : `${outcome.message}
4056
+ `}Proposed changes (nothing applied yet):
4057
+ ${proposals.map((p, i) => ` ${i + 1}. [${p.kind}]${p.touchesUserAsserted ? " (touches something you said yourself)" : ""} ${p.text ? `-> "${p.text}" ` : ""}${p.reason}`).join("\n")}
4058
+ Use /memory consolidate accept <n> or dismiss <n>.
4059
+ `);
4060
+ }
4061
+ async function handleSearch(args) {
4062
+ const query = args.join(" ");
4063
+ if (!query.trim()) {
4064
+ console.log("\nUsage: /search <query>\n");
4065
+ return;
4066
+ }
4067
+ const hits = await assistant.searchTranscript(query);
4068
+ console.log(`
4069
+ ${formatSearchResults(hits, query)}
4070
+ `);
4071
+ }
4072
+ async function handleGoals() {
4073
+ const state = await assistant.getGoalGraphState("cli");
4074
+ console.log(`
4075
+ ${formatGoalGraphState(state)}
4076
+ `);
4077
+ }
4078
+ async function printCost() {
4079
+ const spendCapLine = await spendCapStatusLine();
4080
+ const spendState = await assistant.getSpendState("cli");
4081
+ const session = { inputTokens: spendState.cumulativeInputTokens, outputTokens: spendState.cumulativeOutputTokens, costUsd: spendState.cumulativeCostUsd };
4082
+ console.log(`
4083
+ ${formatCostSummary({ lastTurn: lastTurnUsage, session, backend: config.llmBackend, spendCapLine })}
4084
+ `);
4085
+ }
4086
+ async function handleDoctor() {
4087
+ const workspaceRoot = config.workspaceRoot ?? process.cwd();
4088
+ const checks = await Promise.all([
4089
+ // Backend-specific checks only run for the backend actually in use — skipped, not
4090
+ // reported as failing, for the one that's inactive.
4091
+ ...config.llmBackend === "proxy" ? [checkProxyHealth(config.proxyUrl)] : [],
4092
+ ...config.llmBackend === "claude-cli" ? [checkClaudeCli(process.env.CLAUDE_PATH ?? "claude")] : [],
4093
+ checkWorkspaceRoot(workspaceRoot),
4094
+ checkDataDirWritable(backend, dataDir),
4095
+ ...checkMemoryHealth(await assistant.getMemoryStatus("cli")),
4096
+ // Informational only (always ok:true) — surfaces the active backend/flags so a user has
4097
+ // one command to confirm config, rather than none (see file's own config summary gap).
4098
+ { label: `llmBackend: ${config.llmBackend}`, ok: true },
4099
+ { label: `enableShell: ${config.enableShell ?? false}, enableWeb: ${config.enableWeb ?? false}`, ok: true },
4100
+ // Provider API keys are stored plaintext in config.json, not an OS keychain — confirming
4101
+ // that explicitly here rather than leaving it unstated (no keychain integration exists).
4102
+ { label: "provider keys stored: plaintext in config.json (no OS keychain integration)", ok: true }
4103
+ ]);
4104
+ console.log(`
4105
+ ${formatDoctorReport(checks)}
4106
+ `);
4107
+ }
4108
+ let lastProgressLineLength = 0;
4109
+ function writeProgress(progress) {
4110
+ const layer = nodeToLayer(progress.currentNode);
4111
+ const label = layer ? LAYER_DISPLAY_NAME[layer] : nodeDisplayName(progress.currentNode);
4112
+ const prefix = progress.planPosition ? `[${progress.planPosition.templateName ?? "custom plan"} — step ${progress.planPosition.stepIndex}/${progress.planPosition.stepCount} (${progress.planPosition.completionPct.toFixed(0)}%)]` : `[step ${progress.stepsUsed}/${progress.maxSteps}]`;
4113
+ const line = `${prefix}${label ? ` ${label}…` : ""}`;
4114
+ process.stdout.write(`\r${line.padEnd(lastProgressLineLength)}`);
4115
+ lastProgressLineLength = line.length;
4116
+ }
4117
+ function clearProgress() {
4118
+ if (lastProgressLineLength === 0) return;
4119
+ process.stdout.write(`\r${" ".repeat(lastProgressLineLength)}\r`);
4120
+ lastProgressLineLength = 0;
4121
+ }
4122
+ function writeToolStep(step) {
4123
+ clearProgress();
4124
+ if (step.deniedReason) {
4125
+ console.log(` ${ICONS.deniedStep} Denied: ${step.summary} — ${step.deniedReason}`);
4126
+ } else {
4127
+ console.log(` ${toolStepIcon(step.tool)} ${step.summary}`);
4128
+ }
4129
+ lastTurnToolSteps.push(step);
4130
+ }
4131
+ function emptyClarificationDraft() {
4132
+ return { selectedLabels: [], editText: "", freeText: "" };
4133
+ }
4134
+ function orderedAskOptions(question) {
4135
+ const options2 = question.options ?? [];
4136
+ return options2.map((option, index) => ({ option, index })).sort((a, b) => Number(b.option.recommended ?? false) - Number(a.option.recommended ?? false) || a.index - b.index).map(({ option }) => option);
4137
+ }
4138
+ function clarificationDraftToAnswer(questionId, draft) {
4139
+ if (draft.selectedLabels.length > 0) {
4140
+ return draft.editText.trim() ? { questionId, kind: "selected_with_edit", selectedLabels: draft.selectedLabels, editText: draft.editText.trim() } : { questionId, kind: "selected", selectedLabels: draft.selectedLabels };
4141
+ }
4142
+ if (draft.freeText.trim()) {
4143
+ return { questionId, kind: "free_text", freeText: draft.freeText.trim() };
4144
+ }
4145
+ return null;
4146
+ }
4147
+ function printClarificationQuestion(question, draft, index, total) {
4148
+ const options2 = orderedAskOptions(question);
4149
+ const allowFreeText = question.allowFreeText !== false;
4150
+ console.log("");
4151
+ if (question.header) console.log(`[${question.header}]`);
4152
+ if (total > 1) console.log(`Question ${index + 1} of ${total}`);
4153
+ console.log(question.question);
4154
+ options2.forEach((option, i) => {
4155
+ const n = i + 1;
4156
+ const picked = draft.selectedLabels.includes(option.label) ? "✓" : " ";
4157
+ const recommended = option.recommended ? " (recommended)" : "";
4158
+ console.log(` [${picked}] ${n}) ${option.label}${recommended}`);
4159
+ if (option.description) console.log(` ${option.description}`);
4160
+ if (option.preview) console.log(` preview: ${option.preview}`);
4161
+ });
4162
+ if (draft.editText) console.log(` note: ${draft.editText}`);
4163
+ if (draft.freeText) console.log(` other: ${draft.freeText}`);
4164
+ const commands2 = [
4165
+ options2.length > 0 ? question.allowMultiple ? "<n>/t<n> toggle option" : "<n> select option" : void 0,
4166
+ options2.length > 0 ? "e<n> add a note to a selected option" : void 0,
4167
+ allowFreeText ? "o other (free text)" : void 0,
4168
+ total > 1 ? "b back, n next" : void 0,
4169
+ "s submit"
4170
+ ].filter((c) => c !== void 0);
4171
+ console.log(` (${commands2.join(" | ")})`);
4172
+ }
4173
+ async function handleClarification(result) {
4174
+ const questions = result.questions ?? [];
4175
+ const pendingClarificationId = result.pendingClarificationId;
4176
+ if (questions.length === 0 || !pendingClarificationId) {
4177
+ lastTrace = void 0;
4178
+ lastNoTraceReason = `No harness trace — the last turn needed clarification (${result.reason ?? "no further detail"}) but had no answerable questions.`;
4179
+ console.log(`
4180
+ [needs clarification] ${result.reason ?? "This request needs clarification."}
4181
+ `);
4182
+ return;
4183
+ }
4184
+ if (nonInteractiveApprovalMode === "decline") {
4185
+ console.log(`
4186
+ [non-interactive mode: auto-declining — ASSISTANT_NON_INTERACTIVE_APPROVAL=decline]`);
4187
+ console.log(`
4188
+ [needs clarification] ${result.reason ?? "This request needs clarification."}`);
4189
+ for (const q2 of questions) console.log(` - ${q2.question}`);
4190
+ console.log("");
4191
+ lastTrace = void 0;
4192
+ lastNoTraceReason = "No harness trace — the last turn needed clarification and was auto-declined (non-interactive mode) before the harness resumed.";
4193
+ return;
4194
+ }
4195
+ console.log(`
4196
+ [needs clarification] ${questions.length} question${questions.length === 1 ? "" : "s"} to answer.`);
4197
+ const drafts = new Map(questions.map((q2) => [q2.id, emptyClarificationDraft()]));
4198
+ let index = 0;
4199
+ while (true) {
4200
+ const question = questions[index];
4201
+ const draft = drafts.get(question.id);
4202
+ printClarificationQuestion(question, draft, index, questions.length);
4203
+ const token = (await askLine("clarify> ")).trim();
4204
+ const lower = token.toLowerCase();
4205
+ if (lower === "s" || lower === "submit") {
4206
+ const answers = questions.map((q2) => clarificationDraftToAnswer(q2.id, drafts.get(q2.id)));
4207
+ const missing = questions.filter((q2, i) => answers[i] === null);
4208
+ if (missing.length > 0) {
4209
+ console.log(`
4210
+ Still need an answer for: ${missing.map((q2) => q2.question).join("; ")}
4211
+ `);
4212
+ continue;
4213
+ }
4214
+ const response = { answers };
4215
+ await handleTurn("", false, void 0, pendingClarificationId, response);
4216
+ return;
4217
+ }
4218
+ if (lower === "b" && questions.length > 1) {
4219
+ index = Math.max(0, index - 1);
4220
+ continue;
4221
+ }
4222
+ if (lower === "n" && questions.length > 1) {
4223
+ index = Math.min(questions.length - 1, index + 1);
4224
+ continue;
4225
+ }
4226
+ if (lower === "o") {
4227
+ if (question.allowFreeText === false) {
4228
+ console.log("\nThis question does not accept a free-text answer.\n");
4229
+ continue;
4230
+ }
4231
+ const text = await askLine("Other — type your own answer: ");
4232
+ if (!text.trim()) {
4233
+ console.log("\nEmpty answer ignored.\n");
4234
+ continue;
4235
+ }
4236
+ drafts.set(question.id, { selectedLabels: [], editText: "", freeText: text.trim() });
4237
+ continue;
4238
+ }
4239
+ const editMatch = /^e(\d+)$/i.exec(token);
4240
+ if (editMatch) {
4241
+ const options2 = orderedAskOptions(question);
4242
+ const option = options2[Number(editMatch[1]) - 1];
4243
+ if (!option) {
4244
+ console.log(`
4245
+ No option ${editMatch[1]}.
4246
+ `);
4247
+ continue;
4248
+ }
4249
+ if (!draft.selectedLabels.includes(option.label)) {
4250
+ console.log(`
4251
+ Select option ${editMatch[1]} first, then add a note with e${editMatch[1]}.
4252
+ `);
4253
+ continue;
4254
+ }
4255
+ const note = await askLine("Note: ");
4256
+ drafts.set(question.id, { ...draft, editText: note.trim() });
4257
+ continue;
4258
+ }
4259
+ const toggleMatch = /^t(\d+)$/i.exec(token) ?? /^(\d+)$/.exec(token);
4260
+ if (toggleMatch) {
4261
+ const options2 = orderedAskOptions(question);
4262
+ const option = options2[Number(toggleMatch[1]) - 1];
4263
+ if (!option) {
4264
+ console.log(`
4265
+ No option ${toggleMatch[1]}.
4266
+ `);
4267
+ continue;
4268
+ }
4269
+ if (question.allowMultiple) {
4270
+ const already = draft.selectedLabels.includes(option.label);
4271
+ drafts.set(question.id, {
4272
+ ...draft,
4273
+ selectedLabels: already ? draft.selectedLabels.filter((l) => l !== option.label) : [...draft.selectedLabels, option.label],
4274
+ freeText: ""
4275
+ });
4276
+ } else {
4277
+ drafts.set(question.id, { ...draft, selectedLabels: [option.label], freeText: "" });
4278
+ }
4279
+ continue;
4280
+ }
4281
+ console.log(`
4282
+ Unrecognized input "${token}".
4283
+ `);
4284
+ }
4285
+ }
4286
+ async function handleTurn(message, approved = false, pendingActionId, pendingClarificationId, clarificationAnswer, planApprovalId, planDecision, planEdits) {
4287
+ lastTurnToolSteps = [];
4288
+ let streamedAnyTokens = false;
4289
+ function writeToken(token) {
4290
+ if (!streamedAnyTokens) {
4291
+ process.stdout.write("\nAielia> ");
4292
+ streamedAnyTokens = true;
4293
+ }
4294
+ process.stdout.write(token);
4295
+ }
4296
+ try {
4297
+ const result = await assistant.turn(message, {
4298
+ sessionId: "cli",
4299
+ approved,
4300
+ pendingActionId,
4301
+ pendingClarificationId,
4302
+ clarificationAnswer,
4303
+ planApprovalId,
4304
+ planDecision,
4305
+ planEdits,
4306
+ // Gated on streamedAnyTokens: onProgress keeps firing for layers (Memory,
4307
+ // Verification) that run after the LLM call, i.e. after writeToken has already put
4308
+ // the reply on the current line with no trailing newline. writeProgress's \r-based
4309
+ // overwrite doesn't know that — on a real TTY it wrote the "[step N/5] ..." text
4310
+ // over the tail of the just-streamed reply, and the following clearProgress() then
4311
+ // blanked that same line, so the end of the assistant's answer visibly vanished
4312
+ // right after it finished streaming. Once streaming has started this turn, further
4313
+ // progress has nothing safe to overwrite, so it's dropped instead.
4314
+ onProgress: (progress) => {
4315
+ var _a;
4316
+ if (progress.planTasks) (_a = options.onPlanProgress) == null ? void 0 : _a.call(options, progress.planTasks);
4317
+ if (!streamedAnyTokens) writeProgress(progress);
4318
+ },
4319
+ onToken: writeToken,
4320
+ onToolStep: writeToolStep,
4321
+ // Phase 4 — real intra-turn absorption via checkCallerUpdates, replacing Phase 3's
4322
+ // drain-as-follow-up-turns fallback in dispatchOne's `finally` below (kept there, but
4323
+ // only as a backstop: turn() re-enqueues anything it didn't get around to classifying
4324
+ // this turn — e.g. a trivial turn that never calls harnessBridge.run() at all — back onto
4325
+ // this same channel, see TurnOptions.steeringChannel's doc comment). Passed unconditionally:
4326
+ // with goalGraphMode off, routeMessage (below) never enqueues into steeringChannel, so it's
4327
+ // always empty and this resolves to exactly today's behavior (INV-43).
4328
+ steeringChannel
4329
+ });
4330
+ if (!streamedAnyTokens) clearProgress();
4331
+ if (result.status === "needs_approval" && result.pendingActionId) {
4332
+ const kindLabel = result.pendingActionKind === "shell" ? "shell command" : result.pendingActionKind === "email" ? "send email" : result.pendingActionKind === "batch" ? "batch research" : "write";
4333
+ const promptText = result.pendingActionKind === "shell" ? "Run this command?" : result.pendingActionKind === "email" ? "Send this email?" : result.pendingActionKind === "batch" ? "Continue?" : "Apply this write?";
4334
+ console.log(`
4335
+ [needs approval — ${kindLabel}] ${result.reason}`);
4336
+ let confirmed;
4337
+ if (result.pendingActionKind && rememberedActionKinds.has(result.pendingActionKind)) {
4338
+ console.log(`["don't ask again" active this session for ${kindLabel} — auto-approved]`);
4339
+ confirmed = true;
4340
+ } else {
4341
+ const decision = await askSelect(promptText, APPROVAL_OPTIONS);
4342
+ confirmed = decision !== "n";
4343
+ if (decision === "a" && result.pendingActionKind) rememberedActionKinds.add(result.pendingActionKind);
4344
+ }
4345
+ lastTrace = void 0;
4346
+ lastNoTraceReason = `No harness trace — the last turn was a staged ${kindLabel} that was ${confirmed ? "approved" : "declined"} before the harness ran.`;
4347
+ await handleTurn(message, confirmed, result.pendingActionId);
4348
+ return;
4349
+ }
4350
+ if (result.status === "needs_approval") {
4351
+ console.log(`
4352
+ [needs approval — ${result.riskLevel}] ${result.reason}`);
4353
+ console.log(` "${message}"`);
4354
+ let confirmed;
4355
+ if (result.riskLevel && rememberedRiskLevels.has(result.riskLevel)) {
4356
+ console.log(`["don't ask again" active this session for risk level ${result.riskLevel} — auto-approved]`);
4357
+ confirmed = true;
4358
+ } else {
4359
+ const decision = await askSelect("Proceed?", APPROVAL_OPTIONS);
4360
+ confirmed = decision !== "n";
4361
+ if (decision === "a" && result.riskLevel) rememberedRiskLevels.add(result.riskLevel);
4362
+ }
4363
+ if (confirmed) {
4364
+ await handleTurn(message, true);
4365
+ } else {
4366
+ lastTrace = void 0;
4367
+ lastNoTraceReason = "No harness trace — the last turn was blocked on an approval gate and declined before the harness ran.";
4368
+ await assistant.recordDeclinedRequest("cli", message, result.reason ?? "This request needed approval.");
4369
+ console.log("Cancelled.\n");
4370
+ }
4371
+ return;
4372
+ }
4373
+ if (result.status === "needs_clarification") {
4374
+ await handleClarification(result);
4375
+ return;
4376
+ }
4377
+ if (result.status === "needs_plan_approval") {
4378
+ if (!result.planApprovalId) {
4379
+ console.log(`
4380
+ [needs plan approval] ${result.reason ?? "A plan is awaiting approval, but its approval id is missing."}
4381
+ `);
4382
+ return;
4383
+ }
4384
+ const id = result.planApprovalId;
4385
+ const snapshot = result.planApproval;
4386
+ if (result.reason) console.log(`
4387
+ ${result.reason}`);
4388
+ if (snapshot) printPlanApproval(snapshot);
4389
+ pendingPlanApproval = { id, message, snapshot };
4390
+ lastTrace = void 0;
4391
+ lastNoTraceReason = "No harness trace — the last turn staged a plan for approval (pending /plan approve|decline|edit).";
4392
+ if (!snapshot) {
4393
+ console.log("Decide with /plan approve or /plan decline.\n");
4394
+ return;
4395
+ }
4396
+ const choice = await askSelect("Approve this plan?", PLAN_APPROVAL_OPTIONS);
4397
+ if (choice === "d") {
4398
+ console.log("Left pending — /plan to review, /plan approve|edit|decline to decide.\n");
4399
+ return;
4400
+ }
4401
+ pendingPlanApproval = void 0;
4402
+ let decision;
4403
+ let edits;
4404
+ if (choice === "t") {
4405
+ decision = "approve_trusted";
4406
+ } else if (choice === "e") {
4407
+ edits = await promptPlanEdits(snapshot);
4408
+ decision = "approve_with_edits";
4409
+ } else if (choice === "y") {
4410
+ decision = "approve";
4411
+ } else {
4412
+ decision = "decline";
4413
+ }
4414
+ lastNoTraceReason = `No harness trace — the last turn staged a plan that was ${decision === "decline" ? "declined" : "approved"} before the harness ran.`;
4415
+ await handleTurn(message, false, void 0, void 0, void 0, id, decision, edits);
4416
+ return;
4417
+ }
4418
+ if (result.status === "escalated") {
4419
+ lastTrace = void 0;
4420
+ lastNoTraceReason = `No harness trace — the last turn escalated (${result.reason}) before completing.`;
4421
+ console.log(`
4422
+ [escalated] ${result.reason}
4423
+ `);
4424
+ return;
4425
+ }
4426
+ if (!pendingActionId) {
4427
+ lastTrace = result.harnessSkipped ? void 0 : result.trace;
4428
+ lastNoTraceReason = result.harnessSkipped ? "No harness trace — the last turn was a simple, self-contained question answered directly without activating the harness (fast path)." : void 0;
4429
+ }
4430
+ lastSources = result.sources;
4431
+ if (!pendingActionId && (!result.harnessSkipped || result.planStatus)) lastPlanStatus = result.planStatus;
4432
+ lastTurnUsage = result.usage ? withCostEstimate(result.usage) : void 0;
4433
+ const riskSuffix = result.riskLevel && result.riskLevel !== "LOW" ? ` [risk: ${result.riskLevel}]` : "";
4434
+ const sourcesHint = result.sources && result.sources.length > 0 ? ` (${result.sources.length} source${result.sources.length > 1 ? "s" : ""} — /sources)` : "";
4435
+ const planHint = result.planStatus ? ` (plan: ${result.planStatus.completionPct.toFixed(0)}% — /plan)` : "";
4436
+ const contradictionNotice = result.contradictionNotice ? `
4437
+
4438
+ ${result.contradictionNotice}` : "";
4439
+ const reviewNotice = result.reviewNotice ? `
4440
+
4441
+ ${result.reviewNotice}` : "";
4442
+ lastNextSteps = result.nextSteps && result.nextSteps.length > 0 ? result.nextSteps : void 0;
4443
+ const nextStepsBlock = lastNextSteps ? `
4444
+
4445
+ ${formatNextSteps(lastNextSteps)}` : "";
4446
+ if (streamedAnyTokens) {
4447
+ const pausedNoteText = result.pausedNote ? `
4448
+
4449
+ ${result.pausedNote}` : "";
4450
+ process.stdout.write(`${pausedNoteText}${riskSuffix}${sourcesHint}${planHint}${contradictionNotice}${reviewNotice}${nextStepsBlock}
4451
+
4452
+ `);
4453
+ } else {
4454
+ console.log(`
4455
+ Aielia>${riskSuffix} ${result.reply}${sourcesHint}${planHint}${contradictionNotice}${reviewNotice}${nextStepsBlock}
4456
+ `);
4457
+ }
4458
+ } catch (err) {
4459
+ clearProgress();
4460
+ const { message: errorMessage, retryable } = classifyError(err, config.llmBackend);
4461
+ console.log(`
4462
+ [error] ${errorMessage}${retryable ? " Type the message again to retry." : ""}
4463
+ `);
4464
+ }
4465
+ }
4466
+ function askYesNo(question) {
4467
+ if (options.askYesNo) return options.askYesNo(question);
4468
+ if (nonInteractiveApprovalMode === "decline") {
4469
+ console.log(`
4470
+ [non-interactive mode: auto-declining — ASSISTANT_NON_INTERACTIVE_APPROVAL=decline]`);
4471
+ return Promise.resolve(false);
4472
+ }
4473
+ return new Promise((resolve2) => {
4474
+ try {
4475
+ rl.question(question, (answer) => resolve2(answer.trim().toLowerCase().startsWith("y")));
4476
+ } catch {
4477
+ console.log(`
4478
+ [could not read a response — treating as declined]`);
4479
+ resolve2(false);
4480
+ }
4481
+ });
4482
+ }
4483
+ function askLine(question) {
4484
+ if (options.askLine) return options.askLine(question);
4485
+ return new Promise((resolve2) => {
4486
+ try {
4487
+ rl.question(question, (answer) => resolve2(answer.trim()));
4488
+ } catch {
4489
+ console.log(`
4490
+ [could not read a response — treating as blank]`);
4491
+ resolve2("");
4492
+ }
4493
+ });
4494
+ }
4495
+ function askSelect(question, selectOptions) {
4496
+ if (options.askSelect) return options.askSelect(question, selectOptions);
4497
+ const fallbackKey = selectOptions[selectOptions.length - 1].key;
4498
+ if (nonInteractiveApprovalMode === "decline") {
4499
+ console.log(`
4500
+ [non-interactive mode: auto-declining — ASSISTANT_NON_INTERACTIVE_APPROVAL=decline]`);
4501
+ return Promise.resolve(fallbackKey);
4502
+ }
4503
+ const optionLines = selectOptions.map((option, i) => ` ${i + 1}) [${option.key}] ${option.label}`).join("\n");
4504
+ return new Promise((resolve2) => {
4505
+ try {
4506
+ rl.question(`${question}
4507
+ ${optionLines}
4508
+ > `, (answer) => {
4509
+ var _a;
4510
+ const trimmed = answer.trim().toLowerCase();
4511
+ const byKey = selectOptions.find((option) => option.key.toLowerCase() === trimmed);
4512
+ const byIndex = /^\d+$/.test(trimmed) ? selectOptions[Number(trimmed) - 1] : void 0;
4513
+ resolve2(((_a = byKey ?? byIndex) == null ? void 0 : _a.key) ?? fallbackKey);
4514
+ });
4515
+ } catch {
4516
+ console.log(`
4517
+ [could not read a response — treating as declined]`);
4518
+ resolve2(fallbackKey);
4519
+ }
4520
+ });
4521
+ }
4522
+ async function reloadAssistant() {
4523
+ const nextPersisted = await configStore.load();
4524
+ ({ config, overriddenKeys } = resolveConfig(nextPersisted, envOverrides));
4525
+ layerPins = applyLayerSettings(sanitizeLayerChoices(config.layers), process.env).pinned;
4526
+ assistant = options.assistant ?? await buildAssistant(config, { dataDir, backend, remindersFile });
4527
+ }
4528
+ async function handleConfigCommand(args) {
4529
+ if (args.length === 0) {
4530
+ console.log(`
4531
+ ${formatConfigListing(config, overriddenKeys)}
4532
+ `);
4533
+ return;
4534
+ }
4535
+ if (args[0] === "set") {
4536
+ const key = args[1];
4537
+ const raw = args.slice(2).join(" ");
4538
+ if (!key || !isConfigKey(key)) {
4539
+ console.log(`
4540
+ ✗ Unknown config key "${key ?? ""}". Known keys: ${CONFIG_KEYS.join(", ")}
4541
+ `);
4542
+ return;
4543
+ }
4544
+ if (!raw) {
4545
+ console.log(`
4546
+ Usage: /config set ${key} <value>
4547
+ `);
4548
+ return;
4549
+ }
4550
+ if (overriddenKeys.has(key)) {
4551
+ console.log(`
4552
+ ✗ "${key}" is pinned by ${ENV_VAR_FOR_CONFIG_KEY[key]} — unset that env var to change it here.
4553
+ `);
4554
+ return;
4555
+ }
4556
+ let value;
4557
+ try {
4558
+ value = parseConfigValue(key, raw);
4559
+ } catch (err) {
4560
+ if (!(err instanceof ConfigValueParseError)) throw err;
4561
+ console.log(`
4562
+ ✗ ${err.message}
4563
+ `);
4564
+ return;
4565
+ }
4566
+ const patch = { [key]: value };
4567
+ try {
4568
+ validateConfig(patch, config);
4569
+ } catch (err) {
4570
+ if (!(err instanceof ConfigValidationError)) throw err;
4571
+ console.log(`
4572
+ ✗ ${err.message}
4573
+ `);
4574
+ return;
4575
+ }
4576
+ await configStore.save(patch);
4577
+ await reloadAssistant();
4578
+ console.log(`
4579
+ ✓ ${key} updated (took effect immediately, no restart needed)
4580
+ `);
4581
+ return;
4582
+ }
4583
+ if (args[0] === "reset") {
4584
+ const key = args[1];
4585
+ if (key && !isConfigKey(key)) {
4586
+ console.log(`
4587
+ ✗ Unknown config key "${key}". Known keys: ${CONFIG_KEYS.join(", ")}
4588
+ `);
4589
+ return;
4590
+ }
4591
+ const target = key ? `"${key}"` : "ALL settings";
4592
+ const confirmed = await askYesNo(`
4593
+ Reset ${target} to default? This takes effect immediately. (y/N) `);
4594
+ if (!confirmed) {
4595
+ console.log("\nCancelled — nothing was reset.\n");
4596
+ return;
4597
+ }
4598
+ const clearPatch = key ? { [key]: void 0 } : Object.fromEntries(CONFIG_KEYS.map((k) => [k, void 0]));
4599
+ await configStore.save(clearPatch);
4600
+ await reloadAssistant();
4601
+ console.log(`
4602
+ ✓ Reset ${key ?? "all settings"} to default
4603
+ `);
4604
+ return;
4605
+ }
4606
+ console.log("\nUsage: /config | /config set <key> <value> | /config reset [key]\n");
4607
+ }
4608
+ async function handleLayers(args) {
4609
+ const [sub, id] = args;
4610
+ if (sub === "settings" || sub === "list") {
4611
+ console.log(`
4612
+ ${formatLayerListing(sanitizeLayerChoices(config.layers), layerPins, process.env)}
4613
+ `);
4614
+ return;
4615
+ }
4616
+ try {
4617
+ if (sub === "on" || sub === "off") {
4618
+ if (!id) {
4619
+ console.log(`
4620
+ Usage: /layers ${sub} <id>
4621
+ `);
4622
+ return;
4623
+ }
4624
+ const layers = withLayerChoice(sanitizeLayerChoices(config.layers), id, sub === "on");
4625
+ await configStore.save({ layers });
4626
+ } else if (sub === "reset") {
4627
+ const layers = id ? withLayerChoice(sanitizeLayerChoices(config.layers), id, void 0) : {};
4628
+ await configStore.save({ layers });
4629
+ } else {
4630
+ console.log("\nUsage: /layers settings | /layers on <id> | /layers off <id> | /layers reset [id]\n");
4631
+ return;
4632
+ }
4633
+ } catch (err) {
4634
+ if (!(err instanceof LayerSettingError)) throw err;
4635
+ console.log(`
4636
+ ✗ ${err.message}
4637
+ `);
4638
+ return;
4639
+ }
4640
+ await reloadAssistant();
4641
+ const pinned = id !== void 0 && layerPins.has(id);
4642
+ console.log(pinned ? `
4643
+ ✗ Saved, but "${id}" is pinned by its AUDIT_* env flag, so the saved choice has no effect until that is unset.
4644
+ ` : `
4645
+ ✓ Layers updated (took effect immediately, no restart needed)
4646
+ `);
4647
+ }
4648
+ async function handleModel(args) {
4649
+ if (args.length === 0) {
4650
+ console.log(`
4651
+ ${config.model ?? "(using each backend's default)"}
4652
+ `);
4653
+ return;
4654
+ }
4655
+ await handleConfigCommand(["set", "model", args.join(" ")]);
4656
+ }
4657
+ async function handleProject(args) {
4658
+ if (args.length === 0) {
4659
+ console.log(`
4660
+ ${assistant.getActiveProject() || "(none)"}
4661
+ `);
4662
+ return;
4663
+ }
4664
+ if (args[0] === "clear") {
4665
+ await handleConfigCommand(["reset", "activeProject"]);
4666
+ return;
4667
+ }
4668
+ await handleConfigCommand(["set", "activeProject", args.join(" ")]);
4669
+ }
4670
+ let turnInProgress = false;
4671
+ let lastNextSteps;
4672
+ const steeringChannel = new LiveSteeringChannel();
4673
+ const commands = {
4674
+ "/why": () => printWhy(),
4675
+ // Bare /layers is the last-turn fired/skipped view; its subcommands (settings/on/off/reset) edit the on/off choices.
4676
+ "/layers": (args) => args.length === 0 ? printLayers() : handleLayers(args),
4677
+ "/sources": () => printSources(),
4678
+ "/plan": (args) => handlePlan(args),
4679
+ "/help": () => printHelp(),
4680
+ "/clear": () => handleClear(),
4681
+ "/new": () => handleClear(),
4682
+ "/status": () => printStatus(),
4683
+ "/export": (args) => handleExport(args),
4684
+ "/undo": () => handleUndo(),
4685
+ "/undo-action": (args) => handleUndoAction(args),
4686
+ "/memory": (args) => handleMemory(args),
4687
+ "/search": (args) => handleSearch(args),
4688
+ "/goals": () => handleGoals(),
4689
+ "/model": (args) => handleModel(args),
4690
+ "/project": (args) => handleProject(args),
4691
+ "/cost": () => printCost(),
4692
+ "/doctor": () => handleDoctor(),
4693
+ "/config": (args) => handleConfigCommand(args),
4694
+ "/checkpoint": (args) => handleCheckpoint(args)
4695
+ };
4696
+ async function dispatchOne(message) {
4697
+ if (quitting) return;
4698
+ if (isQuitCommand(message)) {
4699
+ quitting = true;
4700
+ try {
4701
+ await assistant.endSession("cli");
4702
+ } catch {
4703
+ }
4704
+ console.log("Exiting.");
4705
+ rl.close();
4706
+ return;
4707
+ }
4708
+ const pick = /^[1-3]$/.test(message) && lastNextSteps ? lastNextSteps[Number(message) - 1] : void 0;
4709
+ lastNextSteps = void 0;
4710
+ if (pick) {
4711
+ console.log(` → ${pick.description}`);
4712
+ message = pick.description;
4713
+ }
4714
+ const [token, ...args] = message.split(/\s+/);
4715
+ const handler = commands[token];
4716
+ if (handler) {
4717
+ await handler(args);
4718
+ return;
4719
+ }
4720
+ turnInProgress = true;
4721
+ try {
4722
+ await handleTurn(message);
4723
+ } finally {
4724
+ turnInProgress = false;
4725
+ if (isGoalGraphEnabled(config.goalGraphMode)) {
4726
+ for (const event of steeringChannel.poll()) {
4727
+ void enqueue(event.message);
4728
+ }
4729
+ }
4730
+ }
4731
+ }
4732
+ let quitting = false;
4733
+ let dispatchQueue = Promise.resolve();
4734
+ function enqueue(message) {
4735
+ const result = dispatchQueue.then(() => dispatchOne(message)).catch((err) => {
4736
+ console.error("[unexpected error]", err);
4737
+ });
4738
+ dispatchQueue = result;
4739
+ return result;
4740
+ }
4741
+ function isKnownCommand(message) {
4742
+ const [token] = message.split(/\s+/);
4743
+ return token in commands || isQuitCommand(message);
4744
+ }
4745
+ function routeMessage(message) {
4746
+ if (isGoalGraphEnabled(config.goalGraphMode) && turnInProgress && !isKnownCommand(message)) {
4747
+ steeringChannel.enqueue(message);
4748
+ console.log("\n[queued — the current turn is still running; this will be taken into account once it finishes]\n");
4749
+ return Promise.resolve();
4750
+ }
4751
+ return enqueue(message);
4752
+ }
4753
+ rl.on("line", (line) => {
4754
+ const message = line.trim();
4755
+ if (!message) {
4756
+ rl.prompt();
4757
+ return;
4758
+ }
4759
+ void routeMessage(message).finally(() => rl.prompt());
4760
+ });
4761
+ rl.resume();
4762
+ return {
4763
+ dispatchLine: async (line) => {
4764
+ const message = line.trim();
4765
+ if (!message) return;
4766
+ await routeMessage(message);
4767
+ },
4768
+ close: () => rl.close(),
4769
+ getPlanGraphNodes: async (live) => isPlanGraphEnabled(config.planGraphMode) ? loadPlanGraphNodes(live) : void 0,
4770
+ getStatusIndicators: async () => {
4771
+ const indicators = [];
4772
+ indicators.push(`Workspace: ${config.workspaceRoot ?? process.cwd()}`);
4773
+ if (config.dangerouslySkipPermissions) indicators.push("⚠ Permissions: auto-approved (dangerouslySkipPermissions)");
4774
+ if (lastPlanStatus !== void 0) indicators.push("Plan mode: active");
4775
+ indicators.push(`Model: ${config.model ?? backendDisplayModel[config.llmBackend]}`);
4776
+ const spendState = await assistant.getSpendState("cli");
4777
+ indicators.push(
4778
+ `↑${spendState.cumulativeInputTokens.toLocaleString()} ↓${spendState.cumulativeOutputTokens.toLocaleString()} tokens (~$${spendState.cumulativeCostUsd.toFixed(4)})`
4779
+ );
4780
+ const spendCapLine = formatSpendCapStatus(spendState, { sessionCostLimitUsd: config.sessionCostLimitUsd, sessionCallLimit: config.sessionCallLimit });
4781
+ if (spendCapLine) indicators.push(spendCapLine);
4782
+ return indicators;
4783
+ }
4784
+ };
4785
+ }
4786
+ async function main(argv = process.argv.slice(2)) {
4787
+ const parsed = parseCliArgs(argv);
4788
+ if (parsed.command === "version") {
4789
+ console.log(CLI_VERSION);
4790
+ return;
4791
+ }
4792
+ if (parsed.command === "help") {
4793
+ console.log(cliHelpText(CLI_VERSION));
4794
+ return;
4795
+ }
4796
+ if (parsed.command === "update") {
4797
+ process.exitCode = await runUpdateCommand({ dryRun: parsed.dryRun });
4798
+ return;
4799
+ }
4800
+ const persisted = await defaultConfigStore.load();
4801
+ const { config } = resolveConfig(persisted, defaultEnvOverrides);
4802
+ const isSea = isRunningAsSea();
4803
+ if (isSea) cleanupStaleUpdateFiles();
4804
+ if (shouldRunPassiveUpdateCheck({ updateCheck: config.updateCheck, env: process.env, stdinIsTty: Boolean(process.stdin.isTTY) })) {
4805
+ void checkForUpdate().then((result) => {
4806
+ if (result == null ? void 0 : result.updateAvailable) console.log(updateAvailableNotice(result.latestVersion, CLI_VERSION, isSea));
4807
+ });
4808
+ }
4809
+ if (shouldLaunchTuiApp(config.tuiMode ?? "disabled", Boolean(process.stdout.isTTY), Boolean(process.stdin.isTTY))) {
4810
+ const { runTuiApp } = await import("./tui-app-BLR-PRFp.js");
4811
+ await runTuiApp();
4812
+ return;
4813
+ }
4814
+ await runCli();
4815
+ }
4816
+ function entryArgMatchesModule(entryArg, moduleHref, resolveReal = realpathSync) {
4817
+ if (entryArg === void 0) return false;
4818
+ try {
4819
+ return moduleHref === pathToFileURL(resolveReal(entryArg)).href;
4820
+ } catch {
4821
+ return false;
4822
+ }
4823
+ }
4824
+ function isEntryModule() {
4825
+ return entryArgMatchesModule(process.argv[1], import.meta.url);
4826
+ }
4827
+ if (isEntryModule()) void main();
4828
+ void main();
4829
+ export {
4830
+ ICONS as I,
4831
+ PLAN_LINE_PREFIX as P,
4832
+ runCli as a,
4833
+ renderPlan as r,
4834
+ toLines as t
4835
+ };
4836
+ //# sourceMappingURL=bin-CjKpxDxl.js.map