0agent 1.0.77 → 1.0.81

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 (2) hide show
  1. package/dist/daemon.mjs +1331 -303
  2. package/package.json +1 -1
package/dist/daemon.mjs CHANGED
@@ -1507,7 +1507,7 @@ var init_EdgeWeightUpdater = __esm({
1507
1507
  this.weightLog.append(event);
1508
1508
  }
1509
1509
  sleep(ms) {
1510
- return new Promise((resolve17) => setTimeout(resolve17, ms));
1510
+ return new Promise((resolve19) => setTimeout(resolve19, ms));
1511
1511
  }
1512
1512
  };
1513
1513
  }
@@ -1969,15 +1969,22 @@ var init_src = __esm({
1969
1969
  }
1970
1970
  });
1971
1971
 
1972
- // packages/daemon/src/LLMExecutor.ts
1972
+ // packages/daemon/src/services/LLMExecutor.ts
1973
1973
  var LLMExecutor;
1974
1974
  var init_LLMExecutor = __esm({
1975
- "packages/daemon/src/LLMExecutor.ts"() {
1975
+ "packages/daemon/src/services/LLMExecutor.ts"() {
1976
1976
  "use strict";
1977
1977
  LLMExecutor = class _LLMExecutor {
1978
1978
  constructor(config) {
1979
1979
  this.config = config;
1980
1980
  }
1981
+ /**
1982
+ * Create a new LLMExecutor with a different model but same provider/key.
1983
+ * Used by SmartModelRouter to switch to a fast model for simple messages.
1984
+ */
1985
+ withModel(model) {
1986
+ return new _LLMExecutor({ ...this.config, model });
1987
+ }
1981
1988
  get isConfigured() {
1982
1989
  if (this.config.provider === "ollama") return true;
1983
1990
  return !!this.config.api_key?.trim();
@@ -2015,10 +2022,10 @@ var init_LLMExecutor = __esm({
2015
2022
  return { content: res.content, tokens_used: res.tokens_used, model: res.model };
2016
2023
  }
2017
2024
  // ─── Tool-calling completion with optional streaming ─────────────────────
2018
- async completeWithTools(messages, tools, system, onToken, signal) {
2025
+ async completeWithTools(messages, tools, system, onToken, signal, onToolUseBlock) {
2019
2026
  switch (this.config.provider) {
2020
2027
  case "anthropic":
2021
- return this.anthropic(messages, tools, system, onToken, signal);
2028
+ return this.anthropic(messages, tools, system, onToken, signal, onToolUseBlock);
2022
2029
  case "openai":
2023
2030
  return this.openai(messages, tools, system, onToken, void 0, signal);
2024
2031
  case "xai":
@@ -2032,7 +2039,7 @@ var init_LLMExecutor = __esm({
2032
2039
  }
2033
2040
  }
2034
2041
  // ─── Anthropic ───────────────────────────────────────────────────────────
2035
- async anthropic(messages, tools, system, onToken, signal) {
2042
+ async anthropic(messages, tools, system, onToken, signal, onToolUseBlock) {
2036
2043
  const sysContent = system ?? messages.find((m) => m.role === "system")?.content;
2037
2044
  const filtered = messages.filter((m) => m.role !== "system");
2038
2045
  const anthropicMsgs = filtered.map((m) => {
@@ -2146,6 +2153,9 @@ var init_LLMExecutor = __esm({
2146
2153
  tc.input = JSON.parse(toolInputBuffers[currentToolId]);
2147
2154
  } catch {
2148
2155
  }
2156
+ if (onToolUseBlock && tc.input && Object.keys(tc.input).length > 0) {
2157
+ onToolUseBlock(tc);
2158
+ }
2149
2159
  }
2150
2160
  }
2151
2161
  } else if (type === "message_delta") {
@@ -2320,11 +2330,11 @@ var init_LLMExecutor = __esm({
2320
2330
  }
2321
2331
  });
2322
2332
 
2323
- // packages/daemon/src/capabilities/WebSearchCapability.ts
2333
+ // packages/daemon/src/tools/WebSearchCapability.ts
2324
2334
  import { execSync, spawnSync } from "node:child_process";
2325
2335
  var WebSearchCapability;
2326
2336
  var init_WebSearchCapability = __esm({
2327
- "packages/daemon/src/capabilities/WebSearchCapability.ts"() {
2337
+ "packages/daemon/src/tools/WebSearchCapability.ts"() {
2328
2338
  "use strict";
2329
2339
  WebSearchCapability = class {
2330
2340
  name = "web_search";
@@ -2467,7 +2477,7 @@ var init_WebSearchCapability = __esm({
2467
2477
  }
2468
2478
  });
2469
2479
 
2470
- // packages/daemon/src/capabilities/BrowserCapability.ts
2480
+ // packages/daemon/src/tools/BrowserCapability.ts
2471
2481
  import { spawnSync as spawnSync2, execSync as execSync2, spawn } from "node:child_process";
2472
2482
  import { platform } from "node:os";
2473
2483
  function findSystemChrome() {
@@ -2488,7 +2498,7 @@ function findSystemChrome() {
2488
2498
  }
2489
2499
  var BrowserCapability;
2490
2500
  var init_BrowserCapability = __esm({
2491
- "packages/daemon/src/capabilities/BrowserCapability.ts"() {
2501
+ "packages/daemon/src/tools/BrowserCapability.ts"() {
2492
2502
  "use strict";
2493
2503
  BrowserCapability = class {
2494
2504
  name = "browser_open";
@@ -2603,11 +2613,11 @@ var init_BrowserCapability = __esm({
2603
2613
  }
2604
2614
  });
2605
2615
 
2606
- // packages/daemon/src/capabilities/ScraperCapability.ts
2616
+ // packages/daemon/src/tools/ScraperCapability.ts
2607
2617
  import { spawnSync as spawnSync3 } from "node:child_process";
2608
2618
  var ScraperCapability;
2609
2619
  var init_ScraperCapability = __esm({
2610
- "packages/daemon/src/capabilities/ScraperCapability.ts"() {
2620
+ "packages/daemon/src/tools/ScraperCapability.ts"() {
2611
2621
  "use strict";
2612
2622
  init_BrowserCapability();
2613
2623
  ScraperCapability = class {
@@ -2699,11 +2709,11 @@ else:
2699
2709
  }
2700
2710
  });
2701
2711
 
2702
- // packages/daemon/src/capabilities/ShellCapability.ts
2712
+ // packages/daemon/src/tools/ShellCapability.ts
2703
2713
  import { spawn as spawn2 } from "node:child_process";
2704
2714
  var ShellCapability;
2705
2715
  var init_ShellCapability = __esm({
2706
- "packages/daemon/src/capabilities/ShellCapability.ts"() {
2716
+ "packages/daemon/src/tools/ShellCapability.ts"() {
2707
2717
  "use strict";
2708
2718
  ShellCapability = class _ShellCapability {
2709
2719
  name = "shell_exec";
@@ -2816,12 +2826,12 @@ var init_ShellCapability = __esm({
2816
2826
  }
2817
2827
  });
2818
2828
 
2819
- // packages/daemon/src/capabilities/FileCapability.ts
2829
+ // packages/daemon/src/tools/FileCapability.ts
2820
2830
  import { readFileSync as readFileSync2, writeFileSync, readdirSync, mkdirSync, existsSync as existsSync2 } from "node:fs";
2821
2831
  import { resolve as resolve2, dirname } from "node:path";
2822
2832
  var FileCapability;
2823
2833
  var init_FileCapability = __esm({
2824
- "packages/daemon/src/capabilities/FileCapability.ts"() {
2834
+ "packages/daemon/src/tools/FileCapability.ts"() {
2825
2835
  "use strict";
2826
2836
  FileCapability = class {
2827
2837
  name = "file_op";
@@ -2906,10 +2916,10 @@ var init_FileCapability = __esm({
2906
2916
  }
2907
2917
  });
2908
2918
 
2909
- // packages/daemon/src/capabilities/MemoryCapability.ts
2919
+ // packages/daemon/src/tools/MemoryCapability.ts
2910
2920
  var MemoryCapability;
2911
2921
  var init_MemoryCapability = __esm({
2912
- "packages/daemon/src/capabilities/MemoryCapability.ts"() {
2922
+ "packages/daemon/src/tools/MemoryCapability.ts"() {
2913
2923
  "use strict";
2914
2924
  init_src();
2915
2925
  MemoryCapability = class {
@@ -3007,14 +3017,14 @@ var init_MemoryCapability = __esm({
3007
3017
  }
3008
3018
  });
3009
3019
 
3010
- // packages/daemon/src/capabilities/GUICapability.ts
3020
+ // packages/daemon/src/tools/GUICapability.ts
3011
3021
  import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
3012
3022
  import { writeFileSync as writeFileSync2, unlinkSync } from "node:fs";
3013
3023
  import { resolve as resolve3 } from "node:path";
3014
3024
  import { tmpdir, platform as platform2 } from "node:os";
3015
3025
  var GUICapability;
3016
3026
  var init_GUICapability = __esm({
3017
- "packages/daemon/src/capabilities/GUICapability.ts"() {
3027
+ "packages/daemon/src/tools/GUICapability.ts"() {
3018
3028
  "use strict";
3019
3029
  GUICapability = class {
3020
3030
  name = "gui_automation";
@@ -4005,14 +4015,14 @@ else:
4005
4015
  }
4006
4016
  });
4007
4017
 
4008
- // packages/daemon/src/capabilities/OpenInterpreterCapability.ts
4018
+ // packages/daemon/src/tools/OpenInterpreterCapability.ts
4009
4019
  import { spawn as spawn4 } from "node:child_process";
4010
4020
  import { writeFileSync as writeFileSync3, unlinkSync as unlinkSync2 } from "node:fs";
4011
4021
  import { resolve as resolve4 } from "node:path";
4012
4022
  import { tmpdir as tmpdir2 } from "node:os";
4013
4023
  var OI_SCRIPT, OpenInterpreterCapability;
4014
4024
  var init_OpenInterpreterCapability = __esm({
4015
- "packages/daemon/src/capabilities/OpenInterpreterCapability.ts"() {
4025
+ "packages/daemon/src/tools/OpenInterpreterCapability.ts"() {
4016
4026
  "use strict";
4017
4027
  OI_SCRIPT = `
4018
4028
  import sys
@@ -4145,7 +4155,7 @@ Run manually: pip3 install open-interpreter`,
4145
4155
  }
4146
4156
  /** Async pip install — never blocks the event loop (unlike spawnSync). */
4147
4157
  _pipInstall(pkg, signal) {
4148
- return new Promise((resolve17) => {
4158
+ return new Promise((resolve19) => {
4149
4159
  const proc = spawn4("pip3", ["install", pkg, "-q"], {
4150
4160
  env: process.env,
4151
4161
  stdio: "ignore"
@@ -4156,7 +4166,7 @@ Run manually: pip3 install open-interpreter`,
4156
4166
  settled = true;
4157
4167
  signal?.removeEventListener("abort", onAbort);
4158
4168
  clearTimeout(timer);
4159
- resolve17(ok);
4169
+ resolve19(ok);
4160
4170
  };
4161
4171
  const onAbort = () => {
4162
4172
  try {
@@ -4178,7 +4188,7 @@ Run manually: pip3 install open-interpreter`,
4178
4188
  });
4179
4189
  }
4180
4190
  _runScript(scriptPath, stdinData, signal) {
4181
- return new Promise((resolve17) => {
4191
+ return new Promise((resolve19) => {
4182
4192
  const proc = spawn4("python3", [scriptPath], {
4183
4193
  env: process.env,
4184
4194
  stdio: ["pipe", "pipe", "pipe"]
@@ -4191,7 +4201,7 @@ Run manually: pip3 install open-interpreter`,
4191
4201
  settled = true;
4192
4202
  signal?.removeEventListener("abort", onAbort);
4193
4203
  clearTimeout(timer);
4194
- resolve17({ stdout: out.join(""), stderr: err.join(""), code });
4204
+ resolve19({ stdout: out.join(""), stderr: err.join(""), code });
4195
4205
  };
4196
4206
  const onAbort = () => {
4197
4207
  try {
@@ -4220,13 +4230,13 @@ Run manually: pip3 install open-interpreter`,
4220
4230
  }
4221
4231
  });
4222
4232
 
4223
- // packages/daemon/src/capabilities/SurgeCapability.ts
4233
+ // packages/daemon/src/tools/SurgeCapability.ts
4224
4234
  import { execSync as execSync3 } from "node:child_process";
4225
4235
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, copyFileSync, statSync } from "node:fs";
4226
4236
  import { join } from "node:path";
4227
4237
  var SurgeCapability;
4228
4238
  var init_SurgeCapability = __esm({
4229
- "packages/daemon/src/capabilities/SurgeCapability.ts"() {
4239
+ "packages/daemon/src/tools/SurgeCapability.ts"() {
4230
4240
  "use strict";
4231
4241
  SurgeCapability = class {
4232
4242
  name = "surge_publish";
@@ -4300,10 +4310,10 @@ ${out}`,
4300
4310
  }
4301
4311
  });
4302
4312
 
4303
- // packages/daemon/src/capabilities/BrowserExecuteCapability.ts
4313
+ // packages/daemon/src/tools/BrowserExecuteCapability.ts
4304
4314
  var BrowserExecuteCapability;
4305
4315
  var init_BrowserExecuteCapability = __esm({
4306
- "packages/daemon/src/capabilities/BrowserExecuteCapability.ts"() {
4316
+ "packages/daemon/src/tools/BrowserExecuteCapability.ts"() {
4307
4317
  "use strict";
4308
4318
  BrowserExecuteCapability = class {
4309
4319
  name = "browser_execute";
@@ -4577,11 +4587,11 @@ var init_BrowserExecuteCapability = __esm({
4577
4587
  }
4578
4588
  });
4579
4589
 
4580
- // packages/daemon/src/capabilities/OCRExtractCapability.ts
4590
+ // packages/daemon/src/tools/OCRExtractCapability.ts
4581
4591
  import { readFileSync as readFileSync3, existsSync as existsSync4 } from "node:fs";
4582
4592
  var OCRExtractCapability;
4583
4593
  var init_OCRExtractCapability = __esm({
4584
- "packages/daemon/src/capabilities/OCRExtractCapability.ts"() {
4594
+ "packages/daemon/src/tools/OCRExtractCapability.ts"() {
4585
4595
  "use strict";
4586
4596
  OCRExtractCapability = class {
4587
4597
  name = "ocr_extract";
@@ -4740,11 +4750,11 @@ ${JSON.stringify(result.fields, null, 2)}`,
4740
4750
  }
4741
4751
  });
4742
4752
 
4743
- // packages/daemon/src/capabilities/CredentialVaultCapability.ts
4753
+ // packages/daemon/src/tools/CredentialVaultCapability.ts
4744
4754
  import { randomBytes, createCipheriv, createDecipheriv } from "node:crypto";
4745
4755
  var sessionKeys, credStore, CredentialVaultCapability;
4746
4756
  var init_CredentialVaultCapability = __esm({
4747
- "packages/daemon/src/capabilities/CredentialVaultCapability.ts"() {
4757
+ "packages/daemon/src/tools/CredentialVaultCapability.ts"() {
4748
4758
  "use strict";
4749
4759
  sessionKeys = /* @__PURE__ */ new Map();
4750
4760
  credStore = /* @__PURE__ */ new Map();
@@ -4855,10 +4865,10 @@ var init_CredentialVaultCapability = __esm({
4855
4865
  }
4856
4866
  });
4857
4867
 
4858
- // packages/daemon/src/capabilities/MonitorWatchCapability.ts
4868
+ // packages/daemon/src/tools/MonitorWatchCapability.ts
4859
4869
  var DAEMON_URL, MonitorWatchCapability;
4860
4870
  var init_MonitorWatchCapability = __esm({
4861
- "packages/daemon/src/capabilities/MonitorWatchCapability.ts"() {
4871
+ "packages/daemon/src/tools/MonitorWatchCapability.ts"() {
4862
4872
  "use strict";
4863
4873
  DAEMON_URL = "http://localhost:4200";
4864
4874
  MonitorWatchCapability = class {
@@ -5043,14 +5053,425 @@ ${lines.join("\n")}`,
5043
5053
  }
5044
5054
  });
5045
5055
 
5046
- // packages/daemon/src/capabilities/CodespaceBrowserCapability.ts
5056
+ // packages/daemon/src/tools/SessionSearchCapability.ts
5057
+ var SessionSearchCapability;
5058
+ var init_SessionSearchCapability = __esm({
5059
+ "packages/daemon/src/tools/SessionSearchCapability.ts"() {
5060
+ "use strict";
5061
+ SessionSearchCapability = class {
5062
+ name = "session_search";
5063
+ description = "Search across past conversation history to recall previous interactions and decisions.";
5064
+ toolDefinition = {
5065
+ name: "session_search",
5066
+ description: "Search across all past conversations for relevant context. Use this when the user references something from a previous session, or when you need to recall past decisions, outcomes, or context. Returns matching conversation excerpts with timestamps.",
5067
+ input_schema: {
5068
+ type: "object",
5069
+ properties: {
5070
+ query: { type: "string", description: "Search query \u2014 keywords, phrases, or natural language question" },
5071
+ max_results: { type: "string", description: "Maximum results to return (default 5)" }
5072
+ },
5073
+ required: ["query"]
5074
+ }
5075
+ };
5076
+ getDbPath;
5077
+ constructor(getDbPath) {
5078
+ this.getDbPath = getDbPath;
5079
+ }
5080
+ async execute(input) {
5081
+ const start = Date.now();
5082
+ const query = String(input.query ?? "").trim();
5083
+ const maxResults = Number(input.max_results ?? 5);
5084
+ if (!query) return { success: false, output: "query is required", duration_ms: 0 };
5085
+ try {
5086
+ const Database2 = (await import("better-sqlite3")).default;
5087
+ const db = new Database2(this.getDbPath());
5088
+ const keywords = query.split(/\s+/).filter((w) => w.length > 2);
5089
+ const likeClause = keywords.map(() => `content LIKE ?`).join(" OR ");
5090
+ const likeParams = keywords.map((k) => `%${k}%`);
5091
+ const rows = db.prepare(`
5092
+ SELECT session_id, role, content, created_at
5093
+ FROM conversations
5094
+ WHERE ${likeClause || "1=1"}
5095
+ ORDER BY created_at DESC
5096
+ LIMIT ?
5097
+ `).all(...likeParams, maxResults * 3);
5098
+ db.close();
5099
+ if (!rows.length) {
5100
+ return {
5101
+ success: true,
5102
+ output: `No past conversations found matching "${query}".`,
5103
+ duration_ms: Date.now() - start
5104
+ };
5105
+ }
5106
+ const sessions = /* @__PURE__ */ new Map();
5107
+ for (const row of rows) {
5108
+ if (!sessions.has(row.session_id)) sessions.set(row.session_id, []);
5109
+ sessions.get(row.session_id).push(row);
5110
+ }
5111
+ const results = [];
5112
+ let count = 0;
5113
+ for (const [sessionId, msgs] of sessions) {
5114
+ if (count >= maxResults) break;
5115
+ const date = new Date(msgs[0].created_at).toISOString().split("T")[0];
5116
+ const excerpt = msgs.slice(0, 4).map((m) => ` ${m.role}: ${m.content.slice(0, 200)}${m.content.length > 200 ? "\u2026" : ""}`).join("\n");
5117
+ results.push(`[${date}] Session ${sessionId.slice(0, 8)}:
5118
+ ${excerpt}`);
5119
+ count++;
5120
+ }
5121
+ return {
5122
+ success: true,
5123
+ output: `Found ${sessions.size} matching session(s):
5124
+
5125
+ ${results.join("\n\n")}`,
5126
+ structured: { sessions: sessions.size, total_messages: rows.length },
5127
+ duration_ms: Date.now() - start
5128
+ };
5129
+ } catch (err) {
5130
+ const msg = err instanceof Error ? err.message : String(err);
5131
+ return { success: false, output: `Session search failed: ${msg}`, duration_ms: Date.now() - start, error: msg };
5132
+ }
5133
+ }
5134
+ };
5135
+ }
5136
+ });
5137
+
5138
+ // packages/daemon/src/tools/WorktreeCapability.ts
5139
+ import { execSync as execSync4 } from "node:child_process";
5140
+ import { existsSync as existsSync5 } from "node:fs";
5141
+ import { resolve as resolve5, basename } from "node:path";
5142
+ var activeWorktrees, WorktreeCapability;
5143
+ var init_WorktreeCapability = __esm({
5144
+ "packages/daemon/src/tools/WorktreeCapability.ts"() {
5145
+ "use strict";
5146
+ activeWorktrees = /* @__PURE__ */ new Map();
5147
+ WorktreeCapability = class {
5148
+ name = "worktree";
5149
+ description = "Create isolated git worktrees for safe parallel branch work.";
5150
+ toolDefinition = {
5151
+ name: "worktree",
5152
+ description: 'Manage git worktrees for isolated parallel development. Operations: "enter" (create worktree + branch), "exit" (remove/keep), "list" (show active). Use this when you need to work on a separate branch without affecting the current working tree.',
5153
+ input_schema: {
5154
+ type: "object",
5155
+ properties: {
5156
+ op: { type: "string", description: "Operation: enter, exit, list" },
5157
+ branch: { type: "string", description: "Branch name for new worktree (enter only). Auto-generated if omitted." },
5158
+ keep: { type: "string", description: 'Set to "true" to keep worktree on exit (default: remove)' },
5159
+ id: { type: "string", description: 'Worktree ID for exit (if multiple active). Use "list" to see IDs.' }
5160
+ },
5161
+ required: ["op"]
5162
+ }
5163
+ };
5164
+ async execute(input, cwd) {
5165
+ const start = Date.now();
5166
+ const op = String(input.op ?? "");
5167
+ switch (op) {
5168
+ case "enter":
5169
+ return this._enter(input, cwd, start);
5170
+ case "exit":
5171
+ return this._exit(input, cwd, start);
5172
+ case "list":
5173
+ return this._list(start);
5174
+ default:
5175
+ return { success: false, output: `Unknown op: ${op}. Use enter, exit, or list.`, duration_ms: 0 };
5176
+ }
5177
+ }
5178
+ _enter(input, cwd, start) {
5179
+ try {
5180
+ execSync4("git rev-parse --git-dir", { cwd, stdio: "pipe" });
5181
+ } catch {
5182
+ return { success: false, output: "Not in a git repository.", duration_ms: 0 };
5183
+ }
5184
+ const branch = input.branch ? String(input.branch).replace(/[^a-zA-Z0-9\-_\/]/g, "-") : `0agent-wt-${Date.now().toString(36)}`;
5185
+ const repoRoot = execSync4("git rev-parse --show-toplevel", { cwd, encoding: "utf8" }).trim();
5186
+ const worktreePath = resolve5(repoRoot, "..", `${basename(repoRoot)}-${branch}`);
5187
+ if (existsSync5(worktreePath)) {
5188
+ return { success: false, output: `Worktree path already exists: ${worktreePath}`, duration_ms: 0 };
5189
+ }
5190
+ try {
5191
+ execSync4(`git worktree add -b "${branch}" "${worktreePath}"`, { cwd, stdio: "pipe" });
5192
+ const id = `wt-${Date.now().toString(36)}`;
5193
+ activeWorktrees.set(id, { originalCwd: cwd, worktreePath, branch });
5194
+ return {
5195
+ success: true,
5196
+ output: [
5197
+ `Worktree created:`,
5198
+ ` ID: ${id}`,
5199
+ ` Branch: ${branch}`,
5200
+ ` Path: ${worktreePath}`,
5201
+ ` Original cwd: ${cwd}`,
5202
+ ``,
5203
+ `Switch to the worktree by running commands in: ${worktreePath}`,
5204
+ `When done, call worktree(op:"exit", id:"${id}") to clean up.`
5205
+ ].join("\n"),
5206
+ structured: { id, branch, worktreePath, originalCwd: cwd },
5207
+ duration_ms: Date.now() - start
5208
+ };
5209
+ } catch (err) {
5210
+ const msg = err instanceof Error ? err.message : String(err);
5211
+ return { success: false, output: `Failed to create worktree: ${msg}`, duration_ms: Date.now() - start };
5212
+ }
5213
+ }
5214
+ _exit(input, _cwd, start) {
5215
+ const id = String(input.id ?? "");
5216
+ const keep = String(input.keep ?? "") === "true";
5217
+ let entry;
5218
+ let entryId = id;
5219
+ if (id) {
5220
+ entry = activeWorktrees.get(id);
5221
+ } else {
5222
+ const keys = [...activeWorktrees.keys()];
5223
+ if (keys.length > 0) {
5224
+ entryId = keys[keys.length - 1];
5225
+ entry = activeWorktrees.get(entryId);
5226
+ }
5227
+ }
5228
+ if (!entry) {
5229
+ return { success: false, output: id ? `No worktree with ID: ${id}` : "No active worktrees.", duration_ms: 0 };
5230
+ }
5231
+ if (keep) {
5232
+ activeWorktrees.delete(entryId);
5233
+ return {
5234
+ success: true,
5235
+ output: `Worktree kept at: ${entry.worktreePath} (branch: ${entry.branch}). Original cwd: ${entry.originalCwd}`,
5236
+ duration_ms: Date.now() - start
5237
+ };
5238
+ }
5239
+ try {
5240
+ execSync4(`git worktree remove "${entry.worktreePath}" --force`, {
5241
+ cwd: entry.originalCwd,
5242
+ stdio: "pipe"
5243
+ });
5244
+ try {
5245
+ execSync4(`git branch -D "${entry.branch}"`, { cwd: entry.originalCwd, stdio: "pipe" });
5246
+ } catch {
5247
+ }
5248
+ activeWorktrees.delete(entryId);
5249
+ return {
5250
+ success: true,
5251
+ output: `Worktree removed. Branch "${entry.branch}" deleted. Restored to: ${entry.originalCwd}`,
5252
+ duration_ms: Date.now() - start
5253
+ };
5254
+ } catch (err) {
5255
+ const msg = err instanceof Error ? err.message : String(err);
5256
+ return { success: false, output: `Failed to remove worktree: ${msg}`, duration_ms: Date.now() - start };
5257
+ }
5258
+ }
5259
+ _list(start) {
5260
+ if (activeWorktrees.size === 0) {
5261
+ return { success: true, output: "No active worktrees.", duration_ms: Date.now() - start };
5262
+ }
5263
+ const lines = [...activeWorktrees.entries()].map(
5264
+ ([id, wt]) => ` ${id}: ${wt.branch} \u2192 ${wt.worktreePath}`
5265
+ );
5266
+ return {
5267
+ success: true,
5268
+ output: `Active worktrees:
5269
+ ${lines.join("\n")}`,
5270
+ structured: [...activeWorktrees.entries()].map(([id, wt]) => ({ id, ...wt })),
5271
+ duration_ms: Date.now() - start
5272
+ };
5273
+ }
5274
+ };
5275
+ }
5276
+ });
5277
+
5278
+ // packages/daemon/src/tools/LSPCapability.ts
5279
+ import { execSync as execSync5 } from "node:child_process";
5280
+ import { readFileSync as readFileSync4, existsSync as existsSync6 } from "node:fs";
5281
+ import { resolve as resolve6, extname } from "node:path";
5282
+ var LSPCapability;
5283
+ var init_LSPCapability = __esm({
5284
+ "packages/daemon/src/tools/LSPCapability.ts"() {
5285
+ "use strict";
5286
+ LSPCapability = class {
5287
+ name = "lsp";
5288
+ description = "Code intelligence: go-to-definition, find-references, hover, symbols via LSP.";
5289
+ toolDefinition = {
5290
+ name: "lsp",
5291
+ description: 'Language Server Protocol code intelligence. Operations: "definition" (go to definition of symbol at position), "references" (find all references to symbol), "hover" (get type info and docs for symbol), "symbols" (list all symbols in a file). Requires a language server for the file type (auto-detected).',
5292
+ input_schema: {
5293
+ type: "object",
5294
+ properties: {
5295
+ op: { type: "string", description: "Operation: definition, references, hover, symbols" },
5296
+ file: { type: "string", description: "Absolute or relative file path" },
5297
+ line: { type: "string", description: "Line number (1-based) for definition/references/hover" },
5298
+ column: { type: "string", description: "Column number (1-based) for definition/references/hover" },
5299
+ symbol: { type: "string", description: "Symbol name to search for (alternative to line+column \u2014 uses grep to find position)" }
5300
+ },
5301
+ required: ["op", "file"]
5302
+ }
5303
+ };
5304
+ async execute(input, cwd) {
5305
+ const start = Date.now();
5306
+ const op = String(input.op ?? "");
5307
+ let file = String(input.file ?? "");
5308
+ const line = Number(input.line ?? 1) - 1;
5309
+ const column = Number(input.column ?? 1) - 1;
5310
+ const symbol = input.symbol ? String(input.symbol) : void 0;
5311
+ if (!file) return { success: false, output: "file is required", duration_ms: 0 };
5312
+ if (!file.startsWith("/")) file = resolve6(cwd, file);
5313
+ if (!existsSync6(file)) return { success: false, output: `File not found: ${file}`, duration_ms: 0 };
5314
+ let actualLine = line;
5315
+ let actualCol = column;
5316
+ if (symbol) {
5317
+ const pos = this._findSymbolPosition(file, symbol);
5318
+ if (pos) {
5319
+ actualLine = pos.line;
5320
+ actualCol = pos.column;
5321
+ } else {
5322
+ return { success: false, output: `Symbol "${symbol}" not found in ${file}`, duration_ms: Date.now() - start };
5323
+ }
5324
+ }
5325
+ const ext = extname(file).toLowerCase();
5326
+ if ([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"].includes(ext)) {
5327
+ return this._tsOperation(op, file, actualLine, actualCol, cwd, start);
5328
+ }
5329
+ return this._grepFallback(op, file, actualLine, actualCol, symbol, cwd, start);
5330
+ }
5331
+ /**
5332
+ * TypeScript/JavaScript operations using tsserver protocol.
5333
+ */
5334
+ _tsOperation(op, file, line, column, cwd, start) {
5335
+ try {
5336
+ switch (op) {
5337
+ case "definition": {
5338
+ const content = readFileSync4(file, "utf8");
5339
+ const lines = content.split("\n");
5340
+ const targetLine = lines[line] ?? "";
5341
+ const word = this._getWordAt(targetLine, column);
5342
+ if (!word) return { success: false, output: "No symbol at that position", duration_ms: Date.now() - start };
5343
+ const defPatterns = [
5344
+ `(function|const|let|var|class|interface|type|enum)\\s+${word}\\b`,
5345
+ `${word}\\s*[:=]\\s*(function|\\()`,
5346
+ `export\\s+(default\\s+)?(function|class|const|let|var|type|interface)\\s+${word}`
5347
+ ];
5348
+ const results = [];
5349
+ for (const pattern of defPatterns) {
5350
+ try {
5351
+ const grep = execSync5(
5352
+ `rg -n "${pattern}" --type ts --type js "${cwd}" 2>/dev/null | head -10`,
5353
+ { encoding: "utf8", timeout: 5e3 }
5354
+ ).trim();
5355
+ if (grep) results.push(grep);
5356
+ } catch {
5357
+ }
5358
+ }
5359
+ if (!results.length) {
5360
+ return { success: true, output: `No definition found for "${word}"`, duration_ms: Date.now() - start };
5361
+ }
5362
+ return {
5363
+ success: true,
5364
+ output: `Definition of "${word}":
5365
+ ${results.join("\n")}`,
5366
+ duration_ms: Date.now() - start
5367
+ };
5368
+ }
5369
+ case "references": {
5370
+ const content = readFileSync4(file, "utf8");
5371
+ const lines = content.split("\n");
5372
+ const word = this._getWordAt(lines[line] ?? "", column);
5373
+ if (!word) return { success: false, output: "No symbol at that position", duration_ms: Date.now() - start };
5374
+ try {
5375
+ const grep = execSync5(
5376
+ `rg -n "\\b${word}\\b" --type ts --type js "${cwd}" 2>/dev/null | head -20`,
5377
+ { encoding: "utf8", timeout: 5e3 }
5378
+ ).trim();
5379
+ const refCount = grep.split("\n").filter(Boolean).length;
5380
+ return {
5381
+ success: true,
5382
+ output: `References to "${word}" (${refCount} found):
5383
+ ${grep}`,
5384
+ duration_ms: Date.now() - start
5385
+ };
5386
+ } catch {
5387
+ return { success: true, output: `No references found for "${word}"`, duration_ms: Date.now() - start };
5388
+ }
5389
+ }
5390
+ case "hover": {
5391
+ const content = readFileSync4(file, "utf8");
5392
+ const lines = content.split("\n");
5393
+ const word = this._getWordAt(lines[line] ?? "", column);
5394
+ if (!word) return { success: false, output: "No symbol at that position", duration_ms: Date.now() - start };
5395
+ const surroundingLines = lines.slice(Math.max(0, line - 3), line + 4);
5396
+ return {
5397
+ success: true,
5398
+ output: `Symbol: ${word}
5399
+ Context (${file}:${line + 1}:${column + 1}):
5400
+ ${surroundingLines.join("\n")}`,
5401
+ duration_ms: Date.now() - start
5402
+ };
5403
+ }
5404
+ case "symbols": {
5405
+ try {
5406
+ const grep = execSync5(
5407
+ `rg -n "^\\s*(export\\s+)?(function|class|interface|type|enum|const|let|var)\\s+\\w+" "${file}" 2>/dev/null`,
5408
+ { encoding: "utf8", timeout: 5e3 }
5409
+ ).trim();
5410
+ return {
5411
+ success: true,
5412
+ output: `Symbols in ${file}:
5413
+ ${grep || "(no symbols found)"}`,
5414
+ duration_ms: Date.now() - start
5415
+ };
5416
+ } catch {
5417
+ return { success: true, output: `No symbols found in ${file}`, duration_ms: Date.now() - start };
5418
+ }
5419
+ }
5420
+ default:
5421
+ return { success: false, output: `Unknown op: ${op}`, duration_ms: 0 };
5422
+ }
5423
+ } catch (err) {
5424
+ const msg = err instanceof Error ? err.message : String(err);
5425
+ return { success: false, output: `LSP operation failed: ${msg}`, duration_ms: Date.now() - start };
5426
+ }
5427
+ }
5428
+ _grepFallback(op, file, line, column, symbol, cwd, start) {
5429
+ const content = readFileSync4(file, "utf8");
5430
+ const lines = content.split("\n");
5431
+ const word = symbol || this._getWordAt(lines[line] ?? "", column);
5432
+ if (!word) return { success: false, output: "No symbol to search for", duration_ms: 0 };
5433
+ try {
5434
+ const grep = execSync5(
5435
+ `rg -n "\\b${word}\\b" "${cwd}" 2>/dev/null | head -20`,
5436
+ { encoding: "utf8", timeout: 5e3 }
5437
+ ).trim();
5438
+ return {
5439
+ success: true,
5440
+ output: `${op} for "${word}" (grep fallback):
5441
+ ${grep || "(no results)"}`,
5442
+ duration_ms: Date.now() - start
5443
+ };
5444
+ } catch {
5445
+ return { success: true, output: `No results for "${word}"`, duration_ms: Date.now() - start };
5446
+ }
5447
+ }
5448
+ _findSymbolPosition(file, symbol) {
5449
+ const content = readFileSync4(file, "utf8");
5450
+ const lines = content.split("\n");
5451
+ for (let i = 0; i < lines.length; i++) {
5452
+ const col = lines[i].indexOf(symbol);
5453
+ if (col !== -1) return { line: i, column: col };
5454
+ }
5455
+ return null;
5456
+ }
5457
+ _getWordAt(line, col) {
5458
+ const before = line.slice(0, col + 1).match(/[\w$]+$/)?.[0] ?? "";
5459
+ const after = line.slice(col + 1).match(/^[\w$]*/)?.[0] ?? "";
5460
+ const word = before + after;
5461
+ return word.length > 0 ? word : null;
5462
+ }
5463
+ };
5464
+ }
5465
+ });
5466
+
5467
+ // packages/daemon/src/tools/CodespaceBrowserCapability.ts
5047
5468
  var CodespaceBrowserCapability_exports = {};
5048
5469
  __export(CodespaceBrowserCapability_exports, {
5049
5470
  CodespaceBrowserCapability: () => CodespaceBrowserCapability
5050
5471
  });
5051
5472
  var CodespaceBrowserCapability;
5052
5473
  var init_CodespaceBrowserCapability = __esm({
5053
- "packages/daemon/src/capabilities/CodespaceBrowserCapability.ts"() {
5474
+ "packages/daemon/src/tools/CodespaceBrowserCapability.ts"() {
5054
5475
  "use strict";
5055
5476
  init_BrowserCapability();
5056
5477
  CodespaceBrowserCapability = class {
@@ -5117,10 +5538,10 @@ var init_CodespaceBrowserCapability = __esm({
5117
5538
  }
5118
5539
  });
5119
5540
 
5120
- // packages/daemon/src/capabilities/CapabilityRegistry.ts
5541
+ // packages/daemon/src/tools/CapabilityRegistry.ts
5121
5542
  var CapabilityRegistry;
5122
5543
  var init_CapabilityRegistry = __esm({
5123
- "packages/daemon/src/capabilities/CapabilityRegistry.ts"() {
5544
+ "packages/daemon/src/tools/CapabilityRegistry.ts"() {
5124
5545
  "use strict";
5125
5546
  init_WebSearchCapability();
5126
5547
  init_BrowserCapability();
@@ -5135,6 +5556,9 @@ var init_CapabilityRegistry = __esm({
5135
5556
  init_OCRExtractCapability();
5136
5557
  init_CredentialVaultCapability();
5137
5558
  init_MonitorWatchCapability();
5559
+ init_SessionSearchCapability();
5560
+ init_WorktreeCapability();
5561
+ init_LSPCapability();
5138
5562
  CapabilityRegistry = class {
5139
5563
  capabilities = /* @__PURE__ */ new Map();
5140
5564
  /**
@@ -5147,7 +5571,7 @@ var init_CapabilityRegistry = __esm({
5147
5571
  * task_type: browser_task). The main agent does NOT have direct access
5148
5572
  * to browser_open without going through a subagent spawn.
5149
5573
  */
5150
- constructor(codespaceManager, graph, onMemoryWrite) {
5574
+ constructor(codespaceManager, graph, onMemoryWrite, dbPath) {
5151
5575
  this.register(new WebSearchCapability());
5152
5576
  if (codespaceManager) {
5153
5577
  try {
@@ -5169,6 +5593,11 @@ var init_CapabilityRegistry = __esm({
5169
5593
  this.register(new OCRExtractCapability());
5170
5594
  this.register(new CredentialVaultCapability());
5171
5595
  this.register(new MonitorWatchCapability());
5596
+ if (dbPath) {
5597
+ this.register(new SessionSearchCapability(() => dbPath));
5598
+ }
5599
+ this.register(new WorktreeCapability());
5600
+ this.register(new LSPCapability());
5172
5601
  if (graph) {
5173
5602
  this.register(new MemoryCapability(graph, onMemoryWrite));
5174
5603
  }
@@ -5199,6 +5628,7 @@ var init_CapabilityRegistry = __esm({
5199
5628
  const lower = task.toLowerCase();
5200
5629
  const active = /* @__PURE__ */ new Set(["shell_exec", "file_op", "surge_publish"]);
5201
5630
  if (this.capabilities.has("memory_write")) active.add("memory_write");
5631
+ if (this.capabilities.has("session_search")) active.add("session_search");
5202
5632
  if (/search|web|browse|scrape|research|website|url|http|google|fetch|crawl|find.*online/i.test(lower)) {
5203
5633
  active.add("web_search");
5204
5634
  active.add("scrape_url");
@@ -5208,6 +5638,10 @@ var init_CapabilityRegistry = __esm({
5208
5638
  active.add("computer_use");
5209
5639
  active.add("gui_automation");
5210
5640
  }
5641
+ if (/implement|build|write|fix|refactor|debug|test|definition|reference|hover|symbol|branch|worktree|isolat|parallel.*work/i.test(lower)) {
5642
+ active.add("lsp");
5643
+ active.add("worktree");
5644
+ }
5211
5645
  if (/book|file|itr|tax|irctc|train|ticket|flight|passport|appointment|login|portal|form.*fill|ocr|extract|document|pan.*card|aadhaar|credential|password|otp|monitor|watch|alert|notify.*when|price.*drop|slot.*available|justdo/i.test(lower)) {
5212
5646
  active.add("browser_execute");
5213
5647
  active.add("ocr_extract");
@@ -5238,9 +5672,9 @@ var init_CapabilityRegistry = __esm({
5238
5672
  }
5239
5673
  });
5240
5674
 
5241
- // packages/daemon/src/capabilities/index.ts
5242
- var init_capabilities = __esm({
5243
- "packages/daemon/src/capabilities/index.ts"() {
5675
+ // packages/daemon/src/tools/index.ts
5676
+ var init_tools = __esm({
5677
+ "packages/daemon/src/tools/index.ts"() {
5244
5678
  "use strict";
5245
5679
  init_CapabilityRegistry();
5246
5680
  init_WebSearchCapability();
@@ -5254,20 +5688,309 @@ var init_capabilities = __esm({
5254
5688
  init_OCRExtractCapability();
5255
5689
  init_CredentialVaultCapability();
5256
5690
  init_MonitorWatchCapability();
5691
+ init_SessionSearchCapability();
5692
+ init_WorktreeCapability();
5693
+ init_LSPCapability();
5694
+ }
5695
+ });
5696
+
5697
+ // packages/daemon/src/utils/IterationBudget.ts
5698
+ var IterationBudget;
5699
+ var init_IterationBudget = __esm({
5700
+ "packages/daemon/src/utils/IterationBudget.ts"() {
5701
+ "use strict";
5702
+ IterationBudget = class _IterationBudget {
5703
+ constructor(maxIterations, parent, childAllocation) {
5704
+ this.maxIterations = maxIterations;
5705
+ this.parent = parent ?? null;
5706
+ this.childAllocation = childAllocation ?? maxIterations;
5707
+ }
5708
+ used = 0;
5709
+ parent;
5710
+ childAllocation;
5711
+ /**
5712
+ * Consume N iterations. Returns true if budget remains, false if exhausted.
5713
+ * Also consumes from parent budget if this is a child.
5714
+ */
5715
+ consume(n = 1) {
5716
+ this.used += n;
5717
+ if (this.parent) {
5718
+ return this.parent.consume(n) && this.used <= this.maxIterations;
5719
+ }
5720
+ return this.used <= this.maxIterations;
5721
+ }
5722
+ /**
5723
+ * Refund N iterations (e.g., for execute_code calls that don't count).
5724
+ */
5725
+ refund(n = 1) {
5726
+ this.used = Math.max(0, this.used - n);
5727
+ this.parent?.refund(n);
5728
+ }
5729
+ remaining() {
5730
+ const local = this.maxIterations - this.used;
5731
+ if (this.parent) {
5732
+ return Math.min(local, this.parent.remaining());
5733
+ }
5734
+ return local;
5735
+ }
5736
+ exhausted() {
5737
+ return this.remaining() <= 0;
5738
+ }
5739
+ /**
5740
+ * Fork a child budget that draws from this parent's remaining pool.
5741
+ * The child has its own max but also decrements the parent on each consume.
5742
+ */
5743
+ fork(childMax) {
5744
+ const max = childMax ?? Math.min(30, this.remaining());
5745
+ return new _IterationBudget(max, this, max);
5746
+ }
5747
+ /**
5748
+ * Get a budget pressure message if running low.
5749
+ * Returns null if budget is healthy.
5750
+ */
5751
+ pressureWarning() {
5752
+ const rem = this.remaining();
5753
+ const pct = rem / this.maxIterations;
5754
+ if (pct <= 0) {
5755
+ return "BUDGET EXHAUSTED: You have used all available iterations. Wrap up immediately.";
5756
+ }
5757
+ if (pct <= 0.1) {
5758
+ return `BUDGET CRITICAL: Only ${rem} iteration(s) remaining. Finish the current task NOW.`;
5759
+ }
5760
+ if (pct <= 0.25) {
5761
+ return `Budget warning: ${rem} iterations remaining (${Math.round(pct * 100)}%). Start wrapping up.`;
5762
+ }
5763
+ return null;
5764
+ }
5765
+ stats() {
5766
+ return {
5767
+ used: this.used,
5768
+ max: this.maxIterations,
5769
+ remaining: this.maxIterations - this.used,
5770
+ ...this.parent ? { parentRemaining: this.parent.remaining() } : {}
5771
+ };
5772
+ }
5773
+ };
5774
+ }
5775
+ });
5776
+
5777
+ // packages/daemon/src/utils/PromptInjectionScanner.ts
5778
+ function scanForInjection(content, source) {
5779
+ const warnings = [];
5780
+ let sanitized = content;
5781
+ const invisibleMatches = content.match(INVISIBLE_CHARS);
5782
+ if (invisibleMatches && invisibleMatches.length > 0) {
5783
+ warnings.push(`${source || "content"}: ${invisibleMatches.length} invisible Unicode chars detected and stripped`);
5784
+ sanitized = sanitized.replace(INVISIBLE_CHARS, "");
5785
+ }
5786
+ let hasBlock = false;
5787
+ for (const { pattern, severity, label } of INJECTION_PATTERNS) {
5788
+ if (pattern.test(content)) {
5789
+ const msg = `${source || "content"}: ${label} detected (${severity})`;
5790
+ warnings.push(msg);
5791
+ if (severity === "block") hasBlock = true;
5792
+ }
5793
+ }
5794
+ const lines = content.split("\n");
5795
+ const uniqueLines = new Set(lines.map((l) => l.trim()).filter((l) => l.length > 10));
5796
+ if (lines.length > 20 && uniqueLines.size < lines.length * 0.3) {
5797
+ warnings.push(`${source || "content"}: suspicious repetition (${uniqueLines.size} unique of ${lines.length} lines)`);
5798
+ }
5799
+ return {
5800
+ safe: !hasBlock,
5801
+ warnings,
5802
+ sanitized: hasBlock ? `[BLOCKED: Prompt injection detected in ${source || "content"}. Content excluded for safety.]` : sanitized
5803
+ };
5804
+ }
5805
+ function sanitizeContextFile(content, filePath, log) {
5806
+ const result = scanForInjection(content, filePath);
5807
+ for (const w of result.warnings) {
5808
+ log?.(`[injection-scan] ${w}`);
5809
+ }
5810
+ return result.sanitized;
5811
+ }
5812
+ var INJECTION_PATTERNS, INVISIBLE_CHARS;
5813
+ var init_PromptInjectionScanner = __esm({
5814
+ "packages/daemon/src/utils/PromptInjectionScanner.ts"() {
5815
+ "use strict";
5816
+ INJECTION_PATTERNS = [
5817
+ // Direct instruction override
5818
+ { pattern: /ignore\s+(all\s+)?(previous|prior|above|earlier)\s+(instructions?|context|rules?|prompts?)/i, severity: "block", label: "instruction override" },
5819
+ { pattern: /disregard\s+(all\s+)?(previous|prior|above)\s+(instructions?|context)/i, severity: "block", label: "instruction override" },
5820
+ { pattern: /forget\s+(everything|all|what)\s+(you|I)\s+(told|said|instructed)/i, severity: "block", label: "instruction override" },
5821
+ { pattern: /new\s+instructions?\s*:/i, severity: "block", label: "instruction injection" },
5822
+ { pattern: /\bsystem\s*:\s*you\s+are\b/i, severity: "block", label: "role hijack" },
5823
+ // Role impersonation
5824
+ { pattern: /you\s+are\s+now\s+(a|an|the)\s+/i, severity: "warn", label: "role impersonation" },
5825
+ { pattern: /act\s+as\s+(if|though)\s+you\s+are/i, severity: "warn", label: "role impersonation" },
5826
+ { pattern: /pretend\s+(you'?re?|to\s+be)\s+/i, severity: "warn", label: "role impersonation" },
5827
+ // Credential/data exfiltration
5828
+ { pattern: /send\s+(the|your|all)\s+(api\s*key|token|password|secret|credential)/i, severity: "block", label: "credential exfil" },
5829
+ { pattern: /output\s+(the|your)\s+system\s+prompt/i, severity: "block", label: "prompt extraction" },
5830
+ { pattern: /reveal\s+(the|your)\s+(system|initial)\s+(prompt|instructions)/i, severity: "block", label: "prompt extraction" },
5831
+ { pattern: /what\s+(is|are)\s+your\s+(system\s+)?instructions/i, severity: "warn", label: "prompt extraction attempt" },
5832
+ // Encoded/obfuscated injection
5833
+ { pattern: /base64\s*decode|atob\s*\(/i, severity: "warn", label: "encoded payload" },
5834
+ { pattern: /eval\s*\(|Function\s*\(/i, severity: "warn", label: "code execution attempt" }
5835
+ ];
5836
+ INVISIBLE_CHARS = /[\u200B\u200C\u200D\u200E\u200F\u202A-\u202E\u2060\u2061\u2062\u2063\u2064\u2066-\u2069\uFEFF\u00AD]/g;
5837
+ }
5838
+ });
5839
+
5840
+ // packages/daemon/src/utils/ContextCollapse.ts
5841
+ function collapseContext(messages, tailKeep = 10) {
5842
+ if (messages.length <= tailKeep) return messages;
5843
+ const cutIndex = messages.length - tailKeep;
5844
+ const prefix = messages.slice(0, cutIndex);
5845
+ const tail = messages.slice(cutIndex);
5846
+ const collapsed = [];
5847
+ for (const msg of prefix) {
5848
+ if (msg.role === "tool") {
5849
+ collapsed.push({
5850
+ ...msg,
5851
+ content: collapseToolResult(msg.content, msg.tool_call_id)
5852
+ });
5853
+ } else if (msg.role === "assistant" && msg.tool_calls) {
5854
+ collapsed.push({
5855
+ ...msg,
5856
+ content: msg.content.length > 200 ? msg.content.slice(0, 200) + "\u2026" : msg.content
5857
+ });
5858
+ } else {
5859
+ collapsed.push(msg);
5860
+ }
5861
+ }
5862
+ return [...collapsed, ...tail];
5863
+ }
5864
+ function collapseToolResult(content, toolCallId) {
5865
+ const lineCount = content.split("\n").length;
5866
+ const charCount = content.length;
5867
+ if (charCount <= 200) return content;
5868
+ if (/^(1\t| 1\t|\d+\|)/.test(content)) {
5869
+ const lastLine = content.trim().split("\n").pop() ?? "";
5870
+ const lineNum = lastLine.match(/^(\d+)/)?.[1] ?? String(lineCount);
5871
+ return `[file content: ${lineNum} lines, ${charCount} chars \u2014 collapsed]`;
5872
+ }
5873
+ if (content.startsWith("(no output)") || content.startsWith("exit ")) {
5874
+ return content.slice(0, 100);
5875
+ }
5876
+ if (/^(Error|FAIL|Traceback|SyntaxError|TypeError|ReferenceError)/i.test(content)) {
5877
+ const lines = content.split("\n");
5878
+ return lines.slice(0, 2).join("\n") + (lines.length > 2 ? `
5879
+ [...${lines.length - 2} more lines]` : "");
5880
+ }
5881
+ const fileMatches = content.match(/^[^\n]+:\d+:/gm);
5882
+ if (fileMatches) {
5883
+ const uniqueFiles = new Set(fileMatches.map((m) => m.split(":")[0]));
5884
+ return `[search: ${fileMatches.length} matches in ${uniqueFiles.size} files \u2014 collapsed]`;
5885
+ }
5886
+ if (content.startsWith("{") || content.startsWith("[")) {
5887
+ return `[JSON output: ${charCount} chars \u2014 collapsed]`;
5888
+ }
5889
+ const urlMatch = content.match(/https?:\/\/[^\s]+\.surge\.sh/);
5890
+ if (urlMatch) {
5891
+ return `[published: ${urlMatch[0]}]`;
5892
+ }
5893
+ const firstLine = content.split("\n")[0].slice(0, 100);
5894
+ return `${firstLine}\u2026 [${lineCount} lines, ${charCount} chars \u2014 collapsed]`;
5895
+ }
5896
+ var init_ContextCollapse = __esm({
5897
+ "packages/daemon/src/utils/ContextCollapse.ts"() {
5898
+ "use strict";
5899
+ }
5900
+ });
5901
+
5902
+ // packages/daemon/src/services/StreamingToolExecutor.ts
5903
+ function isPreExecSafe(tc) {
5904
+ if (PRE_EXEC_SAFE.has(tc.name)) return true;
5905
+ if (tc.name === "file_op" && tc.input.op === "read") return true;
5906
+ if (tc.name === "file_op" && tc.input.op === "list") return true;
5907
+ if (tc.name === "ocr_extract") return true;
5908
+ return false;
5909
+ }
5910
+ var PRE_EXEC_SAFE, StreamingToolExecutor;
5911
+ var init_StreamingToolExecutor = __esm({
5912
+ "packages/daemon/src/services/StreamingToolExecutor.ts"() {
5913
+ "use strict";
5914
+ PRE_EXEC_SAFE = /* @__PURE__ */ new Set([
5915
+ "web_search",
5916
+ "scrape_url",
5917
+ "session_search",
5918
+ "lsp"
5919
+ ]);
5920
+ StreamingToolExecutor = class {
5921
+ pending = /* @__PURE__ */ new Map();
5922
+ registry;
5923
+ cwd;
5924
+ signal;
5925
+ constructor(registry, cwd, signal) {
5926
+ this.registry = registry;
5927
+ this.cwd = cwd;
5928
+ this.signal = signal;
5929
+ }
5930
+ /**
5931
+ * Called by LLMExecutor's onToolUseBlock callback during streaming.
5932
+ * Starts executing the tool immediately if it's safe to pre-execute.
5933
+ */
5934
+ onToolBlock(tc) {
5935
+ if (!isPreExecSafe(tc)) return;
5936
+ const promise = this.registry.execute(tc.name, tc.input, this.cwd, this.signal);
5937
+ this.pending.set(tc.id, { promise, toolCall: tc, started: Date.now() });
5938
+ }
5939
+ /**
5940
+ * Get a pre-executed result if available, or execute now.
5941
+ * Returns the result string (same contract as _executeSingleTool).
5942
+ */
5943
+ async getOrExecute(tc, fallbackExecute) {
5944
+ const pending = this.pending.get(tc.id);
5945
+ if (pending) {
5946
+ try {
5947
+ const capResult = await pending.promise;
5948
+ const MAX_TOOL_OUTPUT = 4e3;
5949
+ let result = capResult.output;
5950
+ if (result.length > MAX_TOOL_OUTPUT) {
5951
+ result = result.slice(0, MAX_TOOL_OUTPUT) + `
5952
+ [...${result.length - MAX_TOOL_OUTPUT} chars truncated]`;
5953
+ }
5954
+ return { result, preExecuted: true };
5955
+ } catch (err) {
5956
+ return { result: `Error: ${err instanceof Error ? err.message : String(err)}`, preExecuted: true };
5957
+ } finally {
5958
+ this.pending.delete(tc.id);
5959
+ }
5960
+ }
5961
+ return { result: await fallbackExecute(), preExecuted: false };
5962
+ }
5963
+ /**
5964
+ * Number of tools currently pre-executing.
5965
+ */
5966
+ get activeCount() {
5967
+ return this.pending.size;
5968
+ }
5969
+ /**
5970
+ * Cancel all pending pre-executions.
5971
+ */
5972
+ clear() {
5973
+ this.pending.clear();
5974
+ }
5975
+ };
5257
5976
  }
5258
5977
  });
5259
5978
 
5260
5979
  // packages/daemon/src/AgentExecutor.ts
5261
- import { spawn as spawn5 } from "node:child_process";
5262
- import { writeFileSync as writeFileSync4, readFileSync as readFileSync4, readdirSync as readdirSync2, mkdirSync as mkdirSync3, existsSync as existsSync5 } from "node:fs";
5263
- import { resolve as resolve5, dirname as dirname2, relative } from "node:path";
5980
+ import { spawn as spawn6 } from "node:child_process";
5981
+ import { writeFileSync as writeFileSync4, readFileSync as readFileSync5, readdirSync as readdirSync2, mkdirSync as mkdirSync3, existsSync as existsSync7 } from "node:fs";
5982
+ import { resolve as resolve7, dirname as dirname2, relative } from "node:path";
5264
5983
  import { homedir as homedir2 } from "node:os";
5265
5984
  var SELF_MOD_PATTERN, AgentExecutor;
5266
5985
  var init_AgentExecutor = __esm({
5267
5986
  "packages/daemon/src/AgentExecutor.ts"() {
5268
5987
  "use strict";
5269
5988
  init_LLMExecutor();
5270
- init_capabilities();
5989
+ init_tools();
5990
+ init_IterationBudget();
5991
+ init_PromptInjectionScanner();
5992
+ init_ContextCollapse();
5993
+ init_StreamingToolExecutor();
5271
5994
  SELF_MOD_PATTERN = /\b(yourself|the agent|this agent|this cli|0agent|your code|your source|agent cli|improve.*agent|update.*agent|add.*to.*agent|fix.*agent|self.?improv)\b/i;
5272
5995
  AgentExecutor = class {
5273
5996
  constructor(llm, config, onStep, onToken) {
@@ -5279,7 +6002,7 @@ var init_AgentExecutor = __esm({
5279
6002
  this.maxIterations = config.max_iterations ?? 50;
5280
6003
  this.maxCommandMs = config.max_command_ms ?? 3e4;
5281
6004
  this.agentRoot = config.agent_root;
5282
- this.registry = new CapabilityRegistry(void 0, config.graph, config.onMemoryWrite);
6005
+ this.registry = new CapabilityRegistry(void 0, config.graph, config.onMemoryWrite, config.dbPath);
5283
6006
  if (config.entityNodeId) {
5284
6007
  this.registry.setEntityNodeId(config.entityNodeId);
5285
6008
  }
@@ -5299,6 +6022,7 @@ var init_AgentExecutor = __esm({
5299
6022
  const systemPrompt = this.buildSystemPrompt(systemContext, task);
5300
6023
  const activeTools = this.registry.getToolDefinitionsFor(task);
5301
6024
  let toolSet = activeTools;
6025
+ const budget = new IterationBudget(this.maxIterations);
5302
6026
  const messages = [
5303
6027
  { role: "user", content: task }
5304
6028
  ];
@@ -5320,14 +6044,37 @@ var init_AgentExecutor = __esm({
5320
6044
  finalOutput = "Cancelled.";
5321
6045
  break;
5322
6046
  }
6047
+ if (budget.exhausted()) {
6048
+ this.onStep("Iteration budget exhausted \u2014 wrapping up.");
6049
+ break;
6050
+ }
6051
+ budget.consume(1);
6052
+ const pressureMsg = budget.pressureWarning();
6053
+ if (pressureMsg && messages.length > 2) {
6054
+ messages.push({ role: "user", content: `[SYSTEM] ${pressureMsg}` });
6055
+ }
5323
6056
  this.onStep(i === 0 ? "Thinking\u2026" : "Continuing\u2026");
5324
6057
  const estimatedTokens = this._estimateTokens(messages);
5325
- if (estimatedTokens > contextLimit - 16384) {
5326
- this.onStep(`Compacting context (${Math.round(estimatedTokens / 1e3)}k tokens)\u2026`);
6058
+ if (estimatedTokens > contextLimit * 0.6) {
6059
+ const before = messages.length;
6060
+ const collapsed = collapseContext(messages, 12);
6061
+ if (collapsed !== messages) {
6062
+ messages.length = 0;
6063
+ messages.push(...collapsed);
6064
+ const afterTokens = this._estimateTokens(messages);
6065
+ if (afterTokens < estimatedTokens * 0.8) {
6066
+ this.onStep(`Context collapsed (${Math.round(estimatedTokens / 1e3)}k \u2192 ${Math.round(afterTokens / 1e3)}k tokens)`);
6067
+ }
6068
+ }
6069
+ }
6070
+ const tokensAfterCollapse = this._estimateTokens(messages);
6071
+ if (tokensAfterCollapse > contextLimit - 16384) {
6072
+ this.onStep(`Compacting context (${Math.round(tokensAfterCollapse / 1e3)}k tokens)\u2026`);
5327
6073
  this._compactHistory(messages);
5328
6074
  }
5329
6075
  let response;
5330
6076
  let llmFailed = false;
6077
+ const streamExec = new StreamingToolExecutor(this.registry, this.cwd, signal);
5331
6078
  {
5332
6079
  let llmRetry = 0;
5333
6080
  while (true) {
@@ -5341,7 +6088,9 @@ var init_AgentExecutor = __esm({
5341
6088
  this.onToken(token);
5342
6089
  finalOutput += token;
5343
6090
  },
5344
- signal
6091
+ signal,
6092
+ // Streaming tool execution: start safe tools as their blocks complete
6093
+ (tc) => streamExec.onToolBlock(tc)
5345
6094
  );
5346
6095
  break;
5347
6096
  } catch (err) {
@@ -5377,6 +6126,12 @@ var init_AgentExecutor = __esm({
5377
6126
  totalTokens += response.tokens_used;
5378
6127
  totalCost += response.cost_usd;
5379
6128
  modelName = response.model;
6129
+ const maxBudget = this.config.max_budget_usd ?? Infinity;
6130
+ if (totalCost >= maxBudget) {
6131
+ this.onStep(`Budget limit reached: $${totalCost.toFixed(4)} \u2265 $${maxBudget} \u2014 stopping session.`);
6132
+ finalOutput = `Session stopped: cost budget of $${maxBudget} exceeded ($${totalCost.toFixed(4)} used).`;
6133
+ break;
6134
+ }
5380
6135
  if (response.tool_calls?.some((tc) => !toolSet.find((t) => t.name === tc.name))) {
5381
6136
  toolSet = this.registry.getToolDefinitions();
5382
6137
  }
@@ -5390,35 +6145,31 @@ var init_AgentExecutor = __esm({
5390
6145
  content: response.content,
5391
6146
  tool_calls: response.tool_calls
5392
6147
  });
5393
- for (const tc of response.tool_calls) {
5394
- this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})`);
5395
- let result;
5396
- try {
5397
- const capResult = await this.registry.execute(tc.name, tc.input, this.cwd, signal);
5398
- result = capResult.output;
5399
- const MAX_TOOL_OUTPUT = 4e3;
5400
- if (result.length > MAX_TOOL_OUTPUT) {
5401
- result = result.slice(0, MAX_TOOL_OUTPUT) + `
5402
- [...${result.length - MAX_TOOL_OUTPUT} chars truncated]`;
5403
- }
5404
- if (capResult.fallback_used) {
5405
- this.onStep(` (used fallback: ${capResult.fallback_used})`);
5406
- }
5407
- if (tc.name === "file_op" && tc.input.op === "write" && tc.input.path) {
5408
- filesWritten.push(String(tc.input.path));
5409
- }
5410
- if (tc.name === "shell_exec" && tc.input.command) {
5411
- commandsRun.push(String(tc.input.command));
5412
- }
5413
- } catch (err) {
5414
- result = `Error: ${err instanceof Error ? err.message : String(err)}`;
6148
+ const toolCalls = response.tool_calls;
6149
+ const { parallel, serial } = this._partitionToolCalls(toolCalls);
6150
+ if (parallel.length > 0) {
6151
+ const results = await Promise.all(parallel.map(async (tc) => {
6152
+ const { result, preExecuted } = await streamExec.getOrExecute(
6153
+ tc,
6154
+ () => this._executeSingleTool(tc, signal, filesWritten, commandsRun)
6155
+ );
6156
+ const tag = preExecuted ? " [pre-exec]" : " [parallel]";
6157
+ this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})${tag}`);
6158
+ return { tc, result };
6159
+ }));
6160
+ for (const { tc, result } of results) {
6161
+ this.onStep(` \u21B3 ${result.slice(0, 120)}${result.length > 120 ? "\u2026" : ""}`);
6162
+ messages.push({ role: "tool", content: result, tool_call_id: tc.id });
5415
6163
  }
6164
+ }
6165
+ for (const tc of serial) {
6166
+ const { result, preExecuted } = await streamExec.getOrExecute(
6167
+ tc,
6168
+ () => this._executeSingleTool(tc, signal, filesWritten, commandsRun)
6169
+ );
6170
+ this.onStep(`\u25B6 ${tc.name}(${this.summariseInput(tc.name, tc.input)})${preExecuted ? " [pre-exec]" : ""}`);
5416
6171
  this.onStep(` \u21B3 ${result.slice(0, 120)}${result.length > 120 ? "\u2026" : ""}`);
5417
- messages.push({
5418
- role: "tool",
5419
- content: result,
5420
- tool_call_id: tc.id
5421
- });
6172
+ messages.push({ role: "tool", content: result, tool_call_id: tc.id });
5422
6173
  }
5423
6174
  }
5424
6175
  return {
@@ -5462,9 +6213,9 @@ var init_AgentExecutor = __esm({
5462
6213
  }
5463
6214
  }
5464
6215
  shellExec(command, timeoutMs) {
5465
- return new Promise((resolve17) => {
6216
+ return new Promise((resolve19) => {
5466
6217
  const chunks = [];
5467
- const proc = spawn5("bash", ["-c", command], {
6218
+ const proc = spawn6("bash", ["-c", command], {
5468
6219
  cwd: this.cwd,
5469
6220
  env: { ...process.env, TERM: "dumb" },
5470
6221
  timeout: timeoutMs
@@ -5473,10 +6224,10 @@ var init_AgentExecutor = __esm({
5473
6224
  proc.stderr.on("data", (d) => chunks.push(d.toString()));
5474
6225
  proc.on("close", (code) => {
5475
6226
  const output = chunks.join("").trim();
5476
- resolve17(output || (code === 0 ? "(command completed, no output)" : `exit code ${code}`));
6227
+ resolve19(output || (code === 0 ? "(command completed, no output)" : `exit code ${code}`));
5477
6228
  });
5478
6229
  proc.on("error", (err) => {
5479
- resolve17(`Error: ${err.message}`);
6230
+ resolve19(`Error: ${err.message}`);
5480
6231
  });
5481
6232
  });
5482
6233
  }
@@ -5491,8 +6242,8 @@ var init_AgentExecutor = __esm({
5491
6242
  readFile(filePath) {
5492
6243
  const safe = this.safePath(filePath);
5493
6244
  if (!safe) return "Error: path outside working directory";
5494
- if (!existsSync5(safe)) return `File not found: ${filePath}`;
5495
- const content = readFileSync4(safe, "utf8");
6245
+ if (!existsSync7(safe)) return `File not found: ${filePath}`;
6246
+ const content = readFileSync5(safe, "utf8");
5496
6247
  return content.length > 8e3 ? content.slice(0, 8e3) + `
5497
6248
  \u2026[truncated, ${content.length} total bytes]` : content;
5498
6249
  }
@@ -5583,7 +6334,7 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
5583
6334
  listDir(dirPath) {
5584
6335
  const safe = this.safePath(dirPath ?? ".");
5585
6336
  if (!safe) return "Error: path outside working directory";
5586
- if (!existsSync5(safe)) return `Directory not found: ${dirPath}`;
6337
+ if (!existsSync7(safe)) return `Directory not found: ${dirPath}`;
5587
6338
  try {
5588
6339
  const entries = readdirSync2(safe, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && e.name !== "node_modules").map((e) => `${e.isDirectory() ? "d" : "f"} ${e.name}`).join("\n");
5589
6340
  return entries || "(empty directory)";
@@ -5593,7 +6344,7 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
5593
6344
  }
5594
6345
  // ─── Helpers ───────────────────────────────────────────────────────────────
5595
6346
  safePath(p) {
5596
- const resolved = resolve5(this.cwd, p);
6347
+ const resolved = resolve7(this.cwd, p);
5597
6348
  return resolved.startsWith(this.cwd) ? resolved : null;
5598
6349
  }
5599
6350
  buildSystemPrompt(extra, task) {
@@ -5724,17 +6475,18 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
5724
6475
  );
5725
6476
  }
5726
6477
  const agentsFiles = [
5727
- resolve5(this.cwd, "AGENTS.md"),
5728
- resolve5(this.cwd, ".0agent", "AGENTS.md"),
5729
- resolve5(this.cwd, "CLAUDE.md"),
5730
- resolve5(homedir2(), ".0agent", "AGENTS.md")
6478
+ resolve7(this.cwd, "AGENTS.md"),
6479
+ resolve7(this.cwd, ".0agent", "AGENTS.md"),
6480
+ resolve7(this.cwd, "CLAUDE.md"),
6481
+ resolve7(homedir2(), ".0agent", "AGENTS.md")
5731
6482
  ];
5732
6483
  for (const f of agentsFiles) {
5733
6484
  try {
5734
- if (existsSync5(f)) {
5735
- const content = readFileSync4(f, "utf8").trim();
6485
+ if (existsSync7(f)) {
6486
+ const content = readFileSync5(f, "utf8").trim();
5736
6487
  if (content && content.length < 4e3) {
5737
- lines.push(``, `Project instructions:`, content);
6488
+ const sanitized = sanitizeContextFile(content, f);
6489
+ lines.push(``, `Project instructions:`, sanitized);
5738
6490
  break;
5739
6491
  }
5740
6492
  }
@@ -5839,13 +6591,82 @@ content = element.text if element else page.get_all_text()` : `content = page.ge
5839
6591
  if (toolName === "scrape_url") return `"${String(input.url ?? "").slice(0, 60)}" mode=${input.mode ?? "text"}`;
5840
6592
  return JSON.stringify(input).slice(0, 60);
5841
6593
  }
6594
+ /**
6595
+ * Execute a single tool call. Returns the result string.
6596
+ */
6597
+ async _executeSingleTool(tc, signal, filesWritten, commandsRun) {
6598
+ let result;
6599
+ try {
6600
+ const capResult = await this.registry.execute(tc.name, tc.input, this.cwd, signal);
6601
+ result = capResult.output;
6602
+ const MAX_TOOL_OUTPUT = 4e3;
6603
+ if (result.length > MAX_TOOL_OUTPUT) {
6604
+ result = result.slice(0, MAX_TOOL_OUTPUT) + `
6605
+ [...${result.length - MAX_TOOL_OUTPUT} chars truncated]`;
6606
+ }
6607
+ if (capResult.fallback_used) {
6608
+ this.onStep(` (used fallback: ${capResult.fallback_used})`);
6609
+ }
6610
+ if (tc.name === "file_op" && tc.input.op === "write" && tc.input.path) {
6611
+ filesWritten.push(String(tc.input.path));
6612
+ }
6613
+ if (tc.name === "shell_exec" && tc.input.command) {
6614
+ commandsRun.push(String(tc.input.command));
6615
+ }
6616
+ } catch (err) {
6617
+ result = `Error: ${err instanceof Error ? err.message : String(err)}`;
6618
+ }
6619
+ return result;
6620
+ }
6621
+ /**
6622
+ * Partition tool calls into parallelisable and serial groups.
6623
+ * Inspired by Hermes Agent's path-overlap analysis.
6624
+ *
6625
+ * Tools are safe to parallelize when they don't share file paths
6626
+ * and aren't in the never-parallel set (shell_exec, browser_execute).
6627
+ */
6628
+ _partitionToolCalls(calls) {
6629
+ if (calls.length <= 1) return { parallel: [], serial: calls };
6630
+ const NEVER_PARALLEL = /* @__PURE__ */ new Set(["shell_exec", "browser_execute", "credential_vault", "monitor_watch"]);
6631
+ const parallel = [];
6632
+ const serial = [];
6633
+ const usedPaths = /* @__PURE__ */ new Set();
6634
+ for (const tc of calls) {
6635
+ if (NEVER_PARALLEL.has(tc.name)) {
6636
+ serial.push(tc);
6637
+ continue;
6638
+ }
6639
+ const paths = this._extractPaths(tc);
6640
+ let hasOverlap = false;
6641
+ for (const p of paths) {
6642
+ if (usedPaths.has(p)) {
6643
+ hasOverlap = true;
6644
+ break;
6645
+ }
6646
+ }
6647
+ if (hasOverlap) {
6648
+ serial.push(tc);
6649
+ } else {
6650
+ parallel.push(tc);
6651
+ for (const p of paths) usedPaths.add(p);
6652
+ }
6653
+ }
6654
+ return { parallel, serial };
6655
+ }
6656
+ _extractPaths(tc) {
6657
+ const paths = [];
6658
+ if (tc.input.path) paths.push(String(tc.input.path));
6659
+ if (tc.input.image_path) paths.push(String(tc.input.image_path));
6660
+ if (tc.input.url) paths.push(String(tc.input.url));
6661
+ return paths;
6662
+ }
5842
6663
  };
5843
6664
  }
5844
6665
  });
5845
6666
 
5846
6667
  // packages/daemon/src/ExecutionVerifier.ts
5847
- import { existsSync as existsSync7 } from "node:fs";
5848
- import { resolve as resolve6 } from "node:path";
6668
+ import { existsSync as existsSync9 } from "node:fs";
6669
+ import { resolve as resolve8 } from "node:path";
5849
6670
  var ExecutionVerifier;
5850
6671
  var init_ExecutionVerifier = __esm({
5851
6672
  "packages/daemon/src/ExecutionVerifier.ts"() {
@@ -5882,8 +6703,8 @@ var init_ExecutionVerifier = __esm({
5882
6703
  };
5883
6704
  }
5884
6705
  if (files.length > 0) {
5885
- const lastFile = resolve6(this.cwd, files[files.length - 1]);
5886
- const exists = existsSync7(lastFile);
6706
+ const lastFile = resolve8(this.cwd, files[files.length - 1]);
6707
+ const exists = existsSync9(lastFile);
5887
6708
  return {
5888
6709
  success: exists,
5889
6710
  method: "file_exists",
@@ -5922,10 +6743,10 @@ var init_ExecutionVerifier = __esm({
5922
6743
  });
5923
6744
 
5924
6745
  // packages/daemon/src/RuntimeSelfHeal.ts
5925
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, existsSync as existsSync8 } from "node:fs";
5926
- import { resolve as resolve7, dirname as dirname3 } from "node:path";
6746
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, existsSync as existsSync10 } from "node:fs";
6747
+ import { resolve as resolve9, dirname as dirname3 } from "node:path";
5927
6748
  import { fileURLToPath } from "node:url";
5928
- import { execSync as execSync5, spawn as spawn6 } from "node:child_process";
6749
+ import { execSync as execSync7, spawn as spawn7 } from "node:child_process";
5929
6750
  function isRuntimeBug(error) {
5930
6751
  if (TASK_FAILURE_PATTERNS.some((p) => p.test(error))) return false;
5931
6752
  return RUNTIME_BUG_PATTERNS.some((p) => p.test(error));
@@ -5949,8 +6770,8 @@ function parseStackTrace(stack) {
5949
6770
  }
5950
6771
  function extractContext(filePath, errorLine, contextLines = 30) {
5951
6772
  try {
5952
- if (!existsSync8(filePath)) return null;
5953
- const content = readFileSync6(filePath, "utf8");
6773
+ if (!existsSync10(filePath)) return null;
6774
+ const content = readFileSync7(filePath, "utf8");
5954
6775
  const lines = content.split("\n");
5955
6776
  const start = Math.max(0, errorLine - contextLines);
5956
6777
  const end = Math.min(lines.length, errorLine + contextLines);
@@ -5995,8 +6816,8 @@ var init_RuntimeSelfHeal = __esm({
5995
6816
  this.llm = llm;
5996
6817
  this.eventBus = eventBus;
5997
6818
  let dir = dirname3(fileURLToPath(import.meta.url));
5998
- while (dir !== "/" && !existsSync8(resolve7(dir, "package.json"))) {
5999
- dir = resolve7(dir, "..");
6819
+ while (dir !== "/" && !existsSync10(resolve9(dir, "package.json"))) {
6820
+ dir = resolve9(dir, "..");
6000
6821
  }
6001
6822
  this.projectRoot = dir;
6002
6823
  }
@@ -6032,7 +6853,7 @@ var init_RuntimeSelfHeal = __esm({
6032
6853
  */
6033
6854
  async applyPatch(proposal) {
6034
6855
  const tsPath = this.findSourceFile(proposal.location);
6035
- if (!tsPath || !existsSync8(tsPath)) {
6856
+ if (!tsPath || !existsSync10(tsPath)) {
6036
6857
  return {
6037
6858
  applied: false,
6038
6859
  restarted: false,
@@ -6040,7 +6861,7 @@ var init_RuntimeSelfHeal = __esm({
6040
6861
  };
6041
6862
  }
6042
6863
  try {
6043
- const original = readFileSync6(tsPath, "utf8");
6864
+ const original = readFileSync7(tsPath, "utf8");
6044
6865
  const backup = tsPath + ".bak";
6045
6866
  writeFileSync5(backup, original, "utf8");
6046
6867
  if (!original.includes(proposal.original_code.trim())) {
@@ -6052,10 +6873,10 @@ var init_RuntimeSelfHeal = __esm({
6052
6873
  }
6053
6874
  const patched = original.replace(proposal.original_code, proposal.proposed_code);
6054
6875
  writeFileSync5(tsPath, patched, "utf8");
6055
- const bundleScript = resolve7(this.projectRoot, "scripts", "bundle.mjs");
6056
- if (existsSync8(bundleScript)) {
6876
+ const bundleScript = resolve9(this.projectRoot, "scripts", "bundle.mjs");
6877
+ if (existsSync10(bundleScript)) {
6057
6878
  try {
6058
- execSync5(`node "${bundleScript}"`, {
6879
+ execSync7(`node "${bundleScript}"`, {
6059
6880
  cwd: this.projectRoot,
6060
6881
  timeout: 6e4,
6061
6882
  stdio: "ignore"
@@ -6086,14 +6907,14 @@ var init_RuntimeSelfHeal = __esm({
6086
6907
  // ─── Private helpers ───────────────────────────────────────────────────────
6087
6908
  findSourceFile(location) {
6088
6909
  const candidates = [
6089
- resolve7(this.projectRoot, location.relPath),
6910
+ resolve9(this.projectRoot, location.relPath),
6090
6911
  // If relPath starts with dist/, look in src/
6091
- resolve7(this.projectRoot, location.relPath.replace(/^dist\//, "src/").replace(/\.js$/, ".ts")),
6092
- resolve7(this.projectRoot, "packages", "daemon", "src", location.relPath.replace(/.*src\//, "")),
6093
- resolve7(this.projectRoot, "packages", "core", "src", location.relPath.replace(/.*src\//, ""))
6912
+ resolve9(this.projectRoot, location.relPath.replace(/^dist\//, "src/").replace(/\.js$/, ".ts")),
6913
+ resolve9(this.projectRoot, "packages", "daemon", "src", location.relPath.replace(/.*src\//, "")),
6914
+ resolve9(this.projectRoot, "packages", "core", "src", location.relPath.replace(/.*src\//, ""))
6094
6915
  ];
6095
6916
  for (const p of candidates) {
6096
- if (existsSync8(p)) return p;
6917
+ if (existsSync10(p)) return p;
6097
6918
  }
6098
6919
  return null;
6099
6920
  }
@@ -6158,9 +6979,9 @@ Rules:
6158
6979
  }
6159
6980
  }
6160
6981
  restartDaemon() {
6161
- const bundlePath = resolve7(this.projectRoot, "dist", "daemon.mjs");
6162
- if (existsSync8(bundlePath)) {
6163
- const child = spawn6(process.execPath, [bundlePath], {
6982
+ const bundlePath = resolve9(this.projectRoot, "dist", "daemon.mjs");
6983
+ if (existsSync10(bundlePath)) {
6984
+ const child = spawn7(process.execPath, [bundlePath], {
6164
6985
  detached: true,
6165
6986
  stdio: "ignore",
6166
6987
  env: process.env
@@ -6258,14 +7079,14 @@ var init_SelfHealLoop = __esm({
6258
7079
  }
6259
7080
  });
6260
7081
 
6261
- // packages/daemon/src/ProactiveSurface.ts
7082
+ // packages/daemon/src/tasks/ProactiveSurface.ts
6262
7083
  var ProactiveSurface_exports = {};
6263
7084
  __export(ProactiveSurface_exports, {
6264
7085
  ProactiveSurface: () => ProactiveSurface
6265
7086
  });
6266
- import { execSync as execSync8 } from "node:child_process";
6267
- import { existsSync as existsSync18, readFileSync as readFileSync15, statSync as statSync2, readdirSync as readdirSync5 } from "node:fs";
6268
- import { resolve as resolve14, join as join7 } from "node:path";
7087
+ import { execSync as execSync10 } from "node:child_process";
7088
+ import { existsSync as existsSync20, readFileSync as readFileSync16, statSync as statSync2, readdirSync as readdirSync5 } from "node:fs";
7089
+ import { resolve as resolve16, join as join7 } from "node:path";
6269
7090
  function readdirSafe(dir) {
6270
7091
  try {
6271
7092
  return readdirSync5(dir);
@@ -6275,7 +7096,7 @@ function readdirSafe(dir) {
6275
7096
  }
6276
7097
  var ProactiveSurface;
6277
7098
  var init_ProactiveSurface = __esm({
6278
- "packages/daemon/src/ProactiveSurface.ts"() {
7099
+ "packages/daemon/src/tasks/ProactiveSurface.ts"() {
6279
7100
  "use strict";
6280
7101
  init_src();
6281
7102
  ProactiveSurface = class {
@@ -6314,7 +7135,7 @@ var init_ProactiveSurface = __esm({
6314
7135
  return [...this.insights];
6315
7136
  }
6316
7137
  async poll() {
6317
- if (!existsSync18(resolve14(this.cwd, ".git"))) return;
7138
+ if (!existsSync20(resolve16(this.cwd, ".git"))) return;
6318
7139
  const newInsights = [];
6319
7140
  const gitInsight = this.checkGitActivity();
6320
7141
  if (gitInsight) newInsights.push(gitInsight);
@@ -6332,7 +7153,7 @@ var init_ProactiveSurface = __esm({
6332
7153
  try {
6333
7154
  const currentHead = this.getGitHead();
6334
7155
  if (!currentHead || currentHead === this.lastKnownHead) return null;
6335
- const log = execSync8(
7156
+ const log = execSync10(
6336
7157
  `git log ${this.lastKnownHead}..${currentHead} --oneline --stat`,
6337
7158
  { cwd: this.cwd, timeout: 3e3, encoding: "utf8" }
6338
7159
  ).trim();
@@ -6358,13 +7179,13 @@ var init_ProactiveSurface = __esm({
6358
7179
  ];
6359
7180
  for (const dir of outputPaths) {
6360
7181
  try {
6361
- if (!existsSync18(dir)) continue;
7182
+ if (!existsSync20(dir)) continue;
6362
7183
  const xmlFiles = readdirSafe(dir).filter((f) => f.endsWith(".xml"));
6363
7184
  for (const xml of xmlFiles) {
6364
7185
  const path = join7(dir, xml);
6365
7186
  const stat = statSync2(path);
6366
7187
  if (stat.mtimeMs < this.lastPollAt) continue;
6367
- const content = readFileSync15(path, "utf8");
7188
+ const content = readFileSync16(path, "utf8");
6368
7189
  const failures = [...content.matchAll(/<failure[^>]*message="([^"]+)"/g)].length;
6369
7190
  if (failures > 0) {
6370
7191
  return this.makeInsight(
@@ -6408,7 +7229,7 @@ var init_ProactiveSurface = __esm({
6408
7229
  }
6409
7230
  getGitHead() {
6410
7231
  try {
6411
- return execSync8("git rev-parse HEAD", { cwd: this.cwd, timeout: 1e3, encoding: "utf8" }).trim();
7232
+ return execSync10("git rev-parse HEAD", { cwd: this.cwd, timeout: 1e3, encoding: "utf8" }).trim();
6412
7233
  } catch {
6413
7234
  return "";
6414
7235
  }
@@ -6419,8 +7240,8 @@ var init_ProactiveSurface = __esm({
6419
7240
 
6420
7241
  // packages/daemon/src/ZeroAgentDaemon.ts
6421
7242
  init_src();
6422
- import { writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, existsSync as existsSync19, mkdirSync as mkdirSync10, readFileSync as readFileSync16 } from "node:fs";
6423
- import { resolve as resolve15 } from "node:path";
7243
+ import { writeFileSync as writeFileSync12, unlinkSync as unlinkSync4, existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync17 } from "node:fs";
7244
+ import { resolve as resolve17 } from "node:path";
6424
7245
  import { homedir as homedir9 } from "node:os";
6425
7246
 
6426
7247
  // packages/daemon/src/config/DaemonConfig.ts
@@ -6618,7 +7439,7 @@ ${issues}`);
6618
7439
  // packages/daemon/src/SessionManager.ts
6619
7440
  init_src();
6620
7441
 
6621
- // packages/daemon/src/EntityScopedContext.ts
7442
+ // packages/daemon/src/utils/EntityScopedContext.ts
6622
7443
  init_src();
6623
7444
  var EntityScopedContextLoader = class {
6624
7445
  constructor(graph) {
@@ -6695,7 +7516,7 @@ var EntityScopedContextLoader = class {
6695
7516
  init_LLMExecutor();
6696
7517
  init_AgentExecutor();
6697
7518
 
6698
- // packages/daemon/src/AnthropicSkillFetcher.ts
7519
+ // packages/daemon/src/services/AnthropicSkillFetcher.ts
6699
7520
  var BASE_URL = "https://raw.githubusercontent.com/anthropics/skills/main";
6700
7521
  var CACHE_TTL_MS = 60 * 60 * 1e3;
6701
7522
  var ANTHROPIC_SKILLS = [
@@ -6814,9 +7635,9 @@ var AnthropicSkillFetcher = class {
6814
7635
  }
6815
7636
  };
6816
7637
 
6817
- // packages/daemon/src/ProjectScanner.ts
6818
- import { execSync as execSync4 } from "node:child_process";
6819
- import { readFileSync as readFileSync5, existsSync as existsSync6 } from "node:fs";
7638
+ // packages/daemon/src/utils/ProjectScanner.ts
7639
+ import { execSync as execSync6 } from "node:child_process";
7640
+ import { readFileSync as readFileSync6, existsSync as existsSync8 } from "node:fs";
6820
7641
  import { join as join2 } from "node:path";
6821
7642
  import { createServer } from "node:net";
6822
7643
  var PORTS_TO_CHECK = [3e3, 3001, 4e3, 4200, 5e3, 5173, 8e3, 8080, 8888];
@@ -6865,13 +7686,13 @@ var ProjectScanner = class {
6865
7686
  const stack = [];
6866
7687
  let name = "";
6867
7688
  const pkgPath = join2(this.cwd, "package.json");
6868
- if (existsSync6(pkgPath)) {
7689
+ if (existsSync8(pkgPath)) {
6869
7690
  try {
6870
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf8"));
7691
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf8"));
6871
7692
  name = pkg.name ?? "";
6872
7693
  const deps = { ...pkg.dependencies, ...pkg.devDependencies };
6873
7694
  stack.push("node");
6874
- if (deps.typescript || existsSync6(join2(this.cwd, "tsconfig.json"))) stack.push("typescript");
7695
+ if (deps.typescript || existsSync8(join2(this.cwd, "tsconfig.json"))) stack.push("typescript");
6875
7696
  if (deps.react) stack.push("react");
6876
7697
  if (deps.vue) stack.push("vue");
6877
7698
  if (deps.svelte) stack.push("svelte");
@@ -6880,24 +7701,24 @@ var ProjectScanner = class {
6880
7701
  } catch {
6881
7702
  }
6882
7703
  }
6883
- if (existsSync6(join2(this.cwd, "Cargo.toml"))) {
7704
+ if (existsSync8(join2(this.cwd, "Cargo.toml"))) {
6884
7705
  stack.push("rust");
6885
7706
  try {
6886
- const cargo = readFileSync5(join2(this.cwd, "Cargo.toml"), "utf8");
7707
+ const cargo = readFileSync6(join2(this.cwd, "Cargo.toml"), "utf8");
6887
7708
  const nameMatch = cargo.match(/^name\s*=\s*"([^"]+)"/m);
6888
7709
  if (nameMatch && !name) name = nameMatch[1];
6889
7710
  } catch {
6890
7711
  }
6891
7712
  }
6892
- if (existsSync6(join2(this.cwd, "pyproject.toml")) || existsSync6(join2(this.cwd, "requirements.txt"))) {
7713
+ if (existsSync8(join2(this.cwd, "pyproject.toml")) || existsSync8(join2(this.cwd, "requirements.txt"))) {
6893
7714
  stack.push("python");
6894
7715
  }
6895
- if (existsSync6(join2(this.cwd, "go.mod"))) stack.push("go");
7716
+ if (existsSync8(join2(this.cwd, "go.mod"))) stack.push("go");
6896
7717
  return [stack, name];
6897
7718
  }
6898
7719
  getRecentCommits() {
6899
7720
  try {
6900
- const out = execSync4("git log --oneline -5 2>/dev/null", {
7721
+ const out = execSync6("git log --oneline -5 2>/dev/null", {
6901
7722
  cwd: this.cwd,
6902
7723
  timeout: 3e3,
6903
7724
  encoding: "utf8"
@@ -6909,7 +7730,7 @@ var ProjectScanner = class {
6909
7730
  }
6910
7731
  getDirtyFiles() {
6911
7732
  try {
6912
- const out = execSync4("git status --short 2>/dev/null", {
7733
+ const out = execSync6("git status --short 2>/dev/null", {
6913
7734
  cwd: this.cwd,
6914
7735
  timeout: 3e3,
6915
7736
  encoding: "utf8"
@@ -6922,19 +7743,19 @@ var ProjectScanner = class {
6922
7743
  async getRunningPorts() {
6923
7744
  const open = [];
6924
7745
  await Promise.all(PORTS_TO_CHECK.map(
6925
- (port) => new Promise((resolve17) => {
7746
+ (port) => new Promise((resolve19) => {
6926
7747
  const s = createServer();
6927
7748
  s.listen(port, "127.0.0.1", () => {
6928
7749
  s.close();
6929
- resolve17();
7750
+ resolve19();
6930
7751
  });
6931
7752
  s.on("error", () => {
6932
7753
  open.push(port);
6933
- resolve17();
7754
+ resolve19();
6934
7755
  });
6935
7756
  setTimeout(() => {
6936
7757
  s.close();
6937
- resolve17();
7758
+ resolve19();
6938
7759
  }, 200);
6939
7760
  })
6940
7761
  ));
@@ -6943,9 +7764,9 @@ var ProjectScanner = class {
6943
7764
  getReadmeSummary() {
6944
7765
  for (const name of ["README.md", "readme.md", "README.txt", "README"]) {
6945
7766
  const p = join2(this.cwd, name);
6946
- if (existsSync6(p)) {
7767
+ if (existsSync8(p)) {
6947
7768
  try {
6948
- return readFileSync5(p, "utf8").slice(0, 300).replace(/\n+/g, " ").trim();
7769
+ return readFileSync6(p, "utf8").slice(0, 300).replace(/\n+/g, " ").trim();
6949
7770
  } catch {
6950
7771
  }
6951
7772
  }
@@ -6954,7 +7775,7 @@ var ProjectScanner = class {
6954
7775
  }
6955
7776
  };
6956
7777
 
6957
- // packages/daemon/src/ConversationStore.ts
7778
+ // packages/daemon/src/services/ConversationStore.ts
6958
7779
  var CREATE_TABLE = `
6959
7780
  CREATE TABLE IF NOT EXISTS conversations (
6960
7781
  id TEXT PRIMARY KEY,
@@ -7009,9 +7830,200 @@ var ConversationStore = class {
7009
7830
  }
7010
7831
  };
7011
7832
 
7833
+ // packages/daemon/src/services/SmartModelRouter.ts
7834
+ var COMPLEX_PATTERNS = [
7835
+ // Code-related
7836
+ /\b(implement|build|write|fix|refactor|debug|test|deploy|compile|lint|bundle|migrate)\b/i,
7837
+ /\b(function|class|interface|module|component|endpoint|schema|type|hook|middleware)\b/i,
7838
+ /\b(error|bug|crash|fail|broken|issue|stack\s*trace|exception|undefined|null)\b/i,
7839
+ // Tool-heavy tasks
7840
+ /\b(search|scrape|browse|download|install|create\s+file|delete|execute|run|shell|command)\b/i,
7841
+ /\b(git|npm|pip|docker|kubectl|terraform|aws|gcp|azure)\b/i,
7842
+ // Justdo / web tasks
7843
+ /\b(book|file.*itr|irctc|train|ticket|flight|passport|appointment|monitor|watch|login|portal)\b/i,
7844
+ // Long or detailed requests
7845
+ /\b(explain|analyze|review|compare|design|plan|architect|optimise|optimize|improve)\b/i,
7846
+ // URLs, code blocks, file paths
7847
+ /https?:\/\//,
7848
+ /```/,
7849
+ /\/[\w\-]+\.[\w]+/,
7850
+ // file paths like /foo/bar.ts
7851
+ // Self-modification
7852
+ /\b(yourself|agent|0agent|daemon|capability|skill)\b/i
7853
+ // Multi-step or long messages (>200 chars likely complex)
7854
+ ];
7855
+ var SIMPLE_PATTERNS = [
7856
+ /^(hey|hi|hello|sup|yo|hola|namaste|what'?s?\s*up|how\s+are\s+you)[!?.\s,]*$/i,
7857
+ /^(thanks|thank\s+you|thx|ok|okay|cool|great|nice|perfect|got\s+it|sure|yep|yeah|yes|no|nah)[!?.\s,]*$/i,
7858
+ /^(bye|goodbye|see\s+ya|later|good\s*(morning|evening|afternoon|night))[!?.\s,]*$/i,
7859
+ /^(what\s+is|who\s+is|when\s+was|where\s+is|how\s+do\s+you|what\s+does|can\s+you)\b.{0,80}$/i,
7860
+ /^(tell\s+me\s+about|summarize|summarise|tldr|tl;dr)\b.{0,80}$/i
7861
+ ];
7862
+ function routeMessage(task) {
7863
+ const trimmed = task.trim();
7864
+ if (trimmed.length < 5) {
7865
+ return { decision: "skip", reason: "too short, likely noise" };
7866
+ }
7867
+ for (const p of SIMPLE_PATTERNS) {
7868
+ if (p.test(trimmed)) {
7869
+ return { decision: "fast", reason: `matches simple pattern: ${p.source.slice(0, 30)}` };
7870
+ }
7871
+ }
7872
+ for (const p of COMPLEX_PATTERNS) {
7873
+ if (p.test(trimmed)) {
7874
+ return { decision: "primary", reason: `matches complex pattern: ${p.source.slice(0, 30)}` };
7875
+ }
7876
+ }
7877
+ if (trimmed.length > 200) {
7878
+ return { decision: "primary", reason: "long message (>200 chars)" };
7879
+ }
7880
+ const sentences = trimmed.split(/[.!?]+/).filter((s) => s.trim().length > 0);
7881
+ if (sentences.length > 2) {
7882
+ return { decision: "primary", reason: "multi-sentence request" };
7883
+ }
7884
+ return { decision: "fast", reason: "short message, no complex indicators" };
7885
+ }
7886
+ function getFastModelId(provider, _currentModel) {
7887
+ switch (provider) {
7888
+ case "anthropic":
7889
+ return "claude-haiku-4-5-20251001";
7890
+ case "openai":
7891
+ return "gpt-4o-mini";
7892
+ case "gemini":
7893
+ return "gemini-2.0-flash";
7894
+ case "xai":
7895
+ return "grok-3-mini";
7896
+ case "groq":
7897
+ return "llama-3.1-8b-instant";
7898
+ default:
7899
+ return null;
7900
+ }
7901
+ }
7902
+
7903
+ // packages/daemon/src/services/AutoMemoryExtractor.ts
7904
+ init_src();
7905
+ var state = {
7906
+ lastMessageIndex: 0,
7907
+ inProgress: false,
7908
+ pendingRun: false,
7909
+ turnsSinceExtraction: 0
7910
+ };
7911
+ var MIN_TURNS_BETWEEN = 3;
7912
+ var MAX_MESSAGES_PER_RUN = 20;
7913
+ var AutoMemoryExtractor = class {
7914
+ constructor(llm, graph, entityNodeId) {
7915
+ this.llm = llm;
7916
+ this.graph = graph;
7917
+ this.entityNodeId = entityNodeId;
7918
+ }
7919
+ /**
7920
+ * Called after each session turn. Decides whether to extract memories
7921
+ * and runs extraction in the background if needed.
7922
+ *
7923
+ * Fire-and-forget — never blocks the main session.
7924
+ */
7925
+ async maybeExtract(messages, memoryWritesSinceLast) {
7926
+ state.turnsSinceExtraction++;
7927
+ if (memoryWritesSinceLast) {
7928
+ state.turnsSinceExtraction = 0;
7929
+ return;
7930
+ }
7931
+ if (state.turnsSinceExtraction < MIN_TURNS_BETWEEN) return;
7932
+ if (messages.length <= state.lastMessageIndex) return;
7933
+ if (state.inProgress) {
7934
+ state.pendingRun = true;
7935
+ return;
7936
+ }
7937
+ await this._runExtraction(messages);
7938
+ if (state.pendingRun) {
7939
+ state.pendingRun = false;
7940
+ await this._runExtraction(messages);
7941
+ }
7942
+ }
7943
+ async _runExtraction(messages) {
7944
+ if (!this.llm?.isConfigured) return;
7945
+ state.inProgress = true;
7946
+ try {
7947
+ const newMessages = messages.slice(
7948
+ state.lastMessageIndex,
7949
+ state.lastMessageIndex + MAX_MESSAGES_PER_RUN
7950
+ );
7951
+ if (newMessages.length === 0) return;
7952
+ const conversationText = newMessages.filter((m) => m.role === "user" || m.role === "assistant" && !m.tool_calls).map((m) => `${m.role}: ${m.content.slice(0, 500)}`).join("\n");
7953
+ if (conversationText.length < 50) return;
7954
+ const prompt = [
7955
+ "Extract any durable facts from this conversation that are worth remembering across sessions.",
7956
+ "Focus on: user preferences, project decisions, tool configurations, recurring patterns, names, roles.",
7957
+ "Do NOT extract: greetings, transient task details, things already obvious from the code.",
7958
+ "",
7959
+ 'Return a JSON array of facts. Each fact: {"label":"snake_case_key","content":"value","type":"identity|tech|preference|project|outcome"}',
7960
+ "Return [] if nothing worth extracting.",
7961
+ "",
7962
+ "Conversation:",
7963
+ conversationText
7964
+ ].join("\n");
7965
+ const resp = await this.llm.complete(
7966
+ [{ role: "user", content: prompt }],
7967
+ "You extract durable facts from conversations. Return ONLY a JSON array. No explanation."
7968
+ );
7969
+ const text = resp.content.trim();
7970
+ const jsonMatch = text.match(/\[[\s\S]*\]/);
7971
+ if (!jsonMatch) return;
7972
+ let facts;
7973
+ try {
7974
+ facts = JSON.parse(jsonMatch[0]);
7975
+ } catch {
7976
+ return;
7977
+ }
7978
+ if (!Array.isArray(facts) || facts.length === 0) return;
7979
+ for (const fact of facts.slice(0, 5)) {
7980
+ if (!fact.label || !fact.content) continue;
7981
+ const nodeId = `memory:${fact.label}`;
7982
+ const existing = this.graph.getNode(nodeId);
7983
+ if (existing) {
7984
+ this.graph.updateNode(nodeId, {
7985
+ metadata: {
7986
+ ...existing.metadata,
7987
+ content: fact.content,
7988
+ type: fact.type || "note",
7989
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
7990
+ }
7991
+ });
7992
+ } else {
7993
+ const node = createNode({
7994
+ id: nodeId,
7995
+ graph_id: "root",
7996
+ label: fact.label,
7997
+ type: "context" /* CONTEXT */,
7998
+ metadata: {
7999
+ content: fact.content,
8000
+ type: fact.type || "note",
8001
+ saved_at: (/* @__PURE__ */ new Date()).toISOString(),
8002
+ source: "auto_extract"
8003
+ }
8004
+ });
8005
+ this.graph.addNode(node);
8006
+ }
8007
+ }
8008
+ state.lastMessageIndex = messages.length;
8009
+ state.turnsSinceExtraction = 0;
8010
+ } catch {
8011
+ } finally {
8012
+ state.inProgress = false;
8013
+ }
8014
+ }
8015
+ /** Reset state (e.g., on new session). */
8016
+ reset() {
8017
+ state.lastMessageIndex = 0;
8018
+ state.inProgress = false;
8019
+ state.pendingRun = false;
8020
+ state.turnsSinceExtraction = 0;
8021
+ }
8022
+ };
8023
+
7012
8024
  // packages/daemon/src/SessionManager.ts
7013
- import { readFileSync as readFileSync7, existsSync as existsSync9 } from "node:fs";
7014
- import { resolve as resolve8 } from "node:path";
8025
+ import { readFileSync as readFileSync8, existsSync as existsSync11 } from "node:fs";
8026
+ import { resolve as resolve10 } from "node:path";
7015
8027
  import { homedir as homedir3 } from "node:os";
7016
8028
  import YAML2 from "yaml";
7017
8029
  var SessionManager = class {
@@ -7248,14 +8260,21 @@ var SessionManager = class {
7248
8260
  const activeLLM = this.getFreshLLM();
7249
8261
  if (activeLLM?.isConfigured) {
7250
8262
  const userEntityId = enrichedReq.entity_id ?? this.identity?.entity_node_id;
7251
- const isConversational = /^(hey|hi|hello|sup|yo|what'?s up|how are you|thanks|ok|cool|bye|good\s+(morning|evening|afternoon)|lol|nice)[!?.\s,]*$/i.test(enrichedReq.task.trim());
7252
- if (isConversational) {
7253
- const resp = await activeLLM.complete(
8263
+ const routing = routeMessage(enrichedReq.task);
8264
+ if (routing.decision === "skip") {
8265
+ this.completeSession(sessionId, { output: "", files_written: [], commands_run: [], tokens_used: 0, model: "skip" });
8266
+ return this.sessions.get(sessionId);
8267
+ }
8268
+ if (routing.decision === "fast") {
8269
+ const provider = activeLLM["config"]?.provider ?? "anthropic";
8270
+ const fastModel = getFastModelId(provider);
8271
+ const llmToUse = fastModel ? activeLLM.withModel(fastModel) : activeLLM;
8272
+ const resp = await llmToUse.complete(
7254
8273
  [{ role: "user", content: enrichedReq.task }],
7255
- "You are a helpful assistant."
8274
+ this.identity ? `You are 0agent, a helpful AI assistant. You are talking to ${this.identity.name}. Be concise and friendly.` : "You are 0agent, a helpful AI assistant. Be concise and friendly."
7256
8275
  );
7257
8276
  this.emit({ type: "session.token", session_id: sessionId, token: resp.content });
7258
- this.addStep(sessionId, `Done (${resp.tokens_used} tokens, 1 LLM turns)`);
8277
+ this.addStep(sessionId, `Done (${resp.tokens_used} tokens, fast model, reason: ${routing.reason})`);
7259
8278
  this.completeSession(sessionId, {
7260
8279
  output: resp.content,
7261
8280
  files_written: [],
@@ -7397,6 +8416,15 @@ Current task:`;
7397
8416
  this._extractAndPersistFacts(enrichedReq.task, agentResult.output, activeLLM, userEntityId).catch((err) => {
7398
8417
  console.warn("[0agent] Memory extraction outer error:", err instanceof Error ? err.message : err);
7399
8418
  });
8419
+ if (this.graph && activeLLM) {
8420
+ const extractor = new AutoMemoryExtractor(activeLLM, this.graph, userEntityId);
8421
+ extractor.maybeExtract(
8422
+ [{ role: "user", content: enrichedReq.task }, { role: "assistant", content: agentResult.output }],
8423
+ false
8424
+ // memoryWritesSinceLast — checked inside extractor
8425
+ ).catch(() => {
8426
+ });
8427
+ }
7400
8428
  this.completeSession(sessionId, {
7401
8429
  output: agentResult.output,
7402
8430
  files_written: agentResult.files_written,
@@ -7405,7 +8433,7 @@ Current task:`;
7405
8433
  model: agentResult.model
7406
8434
  });
7407
8435
  } else {
7408
- const cfgPath = resolve8(homedir3(), ".0agent", "config.yaml");
8436
+ const cfgPath = resolve10(homedir3(), ".0agent", "config.yaml");
7409
8437
  const output = `No LLM API key found. Add one to ${cfgPath} or run: 0agent init`;
7410
8438
  this.addStep(sessionId, "\u26A0 No LLM API key configured \u2014 run: 0agent init");
7411
8439
  this.completeSession(sessionId, { output });
@@ -7448,9 +8476,9 @@ Current task:`;
7448
8476
  */
7449
8477
  getFreshLLM() {
7450
8478
  try {
7451
- const configPath = resolve8(homedir3(), ".0agent", "config.yaml");
7452
- if (!existsSync9(configPath)) return this.llm;
7453
- const raw = readFileSync7(configPath, "utf8");
8479
+ const configPath = resolve10(homedir3(), ".0agent", "config.yaml");
8480
+ if (!existsSync11(configPath)) return this.llm;
8481
+ const raw = readFileSync8(configPath, "utf8");
7454
8482
  const cfg = YAML2.parse(raw);
7455
8483
  const providers = cfg.llm_providers;
7456
8484
  if (!providers?.length) return this.llm;
@@ -7476,9 +8504,9 @@ Current task:`;
7476
8504
  if (!this.graph) return;
7477
8505
  let extractLLM;
7478
8506
  try {
7479
- const cfgPath = resolve8(homedir3(), ".0agent", "config.yaml");
7480
- if (existsSync9(cfgPath)) {
7481
- const raw = readFileSync7(cfgPath, "utf8");
8507
+ const cfgPath = resolve10(homedir3(), ".0agent", "config.yaml");
8508
+ if (existsSync11(cfgPath)) {
8509
+ const raw = readFileSync8(cfgPath, "utf8");
7482
8510
  const cfg = YAML2.parse(raw);
7483
8511
  const prov = cfg.llm_providers?.find((p) => p.is_default) ?? cfg.llm_providers?.[0];
7484
8512
  if (prov?.api_key && prov.provider === "anthropic") {
@@ -7716,7 +8744,7 @@ var WebSocketEventBus = class {
7716
8744
  }
7717
8745
  };
7718
8746
 
7719
- // packages/daemon/src/BackgroundWorkers.ts
8747
+ // packages/daemon/src/tasks/BackgroundWorkers.ts
7720
8748
  var DEFAULT_CONFIG2 = {
7721
8749
  decay_interval_ms: 6 * 60 * 60 * 1e3,
7722
8750
  // 6 hours
@@ -7840,8 +8868,8 @@ var BackgroundWorkers = class {
7840
8868
  }
7841
8869
  };
7842
8870
 
7843
- // packages/daemon/src/SkillRegistry.ts
7844
- import { readFileSync as readFileSync8, readdirSync as readdirSync3, existsSync as existsSync10, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, mkdirSync as mkdirSync4 } from "node:fs";
8871
+ // packages/daemon/src/skills/SkillRegistry.ts
8872
+ import { readFileSync as readFileSync9, readdirSync as readdirSync3, existsSync as existsSync12, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3, mkdirSync as mkdirSync4 } from "node:fs";
7845
8873
  import { join as join3 } from "node:path";
7846
8874
  import { homedir as homedir4 } from "node:os";
7847
8875
  import YAML3 from "yaml";
@@ -7864,11 +8892,11 @@ var SkillRegistry = class {
7864
8892
  this.loadFromDir(this.customDir, false);
7865
8893
  }
7866
8894
  loadFromDir(dir, isBuiltin) {
7867
- if (!existsSync10(dir)) return;
8895
+ if (!existsSync12(dir)) return;
7868
8896
  const files = readdirSync3(dir).filter((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
7869
8897
  for (const file of files) {
7870
8898
  try {
7871
- const raw = readFileSync8(join3(dir, file), "utf8");
8899
+ const raw = readFileSync9(join3(dir, file), "utf8");
7872
8900
  const skill = YAML3.parse(raw);
7873
8901
  if (skill.name) {
7874
8902
  this.skills.set(skill.name, skill);
@@ -7918,7 +8946,7 @@ var SkillRegistry = class {
7918
8946
  throw new Error(`Cannot delete built-in skill: ${name}`);
7919
8947
  }
7920
8948
  const filePath = join3(this.customDir, `${name}.yaml`);
7921
- if (existsSync10(filePath)) {
8949
+ if (existsSync12(filePath)) {
7922
8950
  unlinkSync3(filePath);
7923
8951
  }
7924
8952
  this.skills.delete(name);
@@ -7931,8 +8959,8 @@ var SkillRegistry = class {
7931
8959
  // packages/daemon/src/HTTPServer.ts
7932
8960
  import { Hono as Hono14 } from "hono";
7933
8961
  import { serve } from "@hono/node-server";
7934
- import { readFileSync as readFileSync10 } from "node:fs";
7935
- import { resolve as resolve10, dirname as dirname4 } from "node:path";
8962
+ import { readFileSync as readFileSync11 } from "node:fs";
8963
+ import { resolve as resolve12, dirname as dirname4 } from "node:path";
7936
8964
  import { fileURLToPath as fileURLToPath2 } from "node:url";
7937
8965
 
7938
8966
  // packages/daemon/src/routes/health.ts
@@ -8224,8 +9252,8 @@ function memoryRoutes(deps) {
8224
9252
  // packages/daemon/src/routes/llm.ts
8225
9253
  init_LLMExecutor();
8226
9254
  import { Hono as Hono10 } from "hono";
8227
- import { readFileSync as readFileSync9, existsSync as existsSync11 } from "node:fs";
8228
- import { resolve as resolve9 } from "node:path";
9255
+ import { readFileSync as readFileSync10, existsSync as existsSync13 } from "node:fs";
9256
+ import { resolve as resolve11 } from "node:path";
8229
9257
  import { homedir as homedir5 } from "node:os";
8230
9258
  import YAML4 from "yaml";
8231
9259
  function llmRoutes() {
@@ -8233,11 +9261,11 @@ function llmRoutes() {
8233
9261
  app.post("/ping", async (c) => {
8234
9262
  const start = Date.now();
8235
9263
  try {
8236
- const configPath = resolve9(homedir5(), ".0agent", "config.yaml");
8237
- if (!existsSync11(configPath)) {
9264
+ const configPath = resolve11(homedir5(), ".0agent", "config.yaml");
9265
+ if (!existsSync13(configPath)) {
8238
9266
  return c.json({ ok: false, error: "Config not found. Run: 0agent init" });
8239
9267
  }
8240
- const cfg = YAML4.parse(readFileSync9(configPath, "utf8"));
9268
+ const cfg = YAML4.parse(readFileSync10(configPath, "utf8"));
8241
9269
  const providers = cfg.llm_providers;
8242
9270
  const def = providers?.find((p) => p.is_default) ?? providers?.[0];
8243
9271
  if (!def) {
@@ -8322,7 +9350,7 @@ function codespaceRoutes(deps) {
8322
9350
  // packages/daemon/src/routes/schedule.ts
8323
9351
  import { Hono as Hono12 } from "hono";
8324
9352
 
8325
- // packages/daemon/src/SchedulerManager.ts
9353
+ // packages/daemon/src/tasks/SchedulerManager.ts
8326
9354
  var DAYS = {
8327
9355
  sunday: 0,
8328
9356
  sun: 0,
@@ -8752,15 +9780,15 @@ function runtimeRoutes(deps) {
8752
9780
  // packages/daemon/src/HTTPServer.ts
8753
9781
  function findGraphHtml() {
8754
9782
  const candidates = [
8755
- resolve10(dirname4(fileURLToPath2(import.meta.url)), "graph.html"),
9783
+ resolve12(dirname4(fileURLToPath2(import.meta.url)), "graph.html"),
8756
9784
  // dev (src/)
8757
- resolve10(dirname4(fileURLToPath2(import.meta.url)), "..", "graph.html"),
9785
+ resolve12(dirname4(fileURLToPath2(import.meta.url)), "..", "graph.html"),
8758
9786
  // bundled (dist/../)
8759
- resolve10(dirname4(fileURLToPath2(import.meta.url)), "..", "dist", "graph.html")
9787
+ resolve12(dirname4(fileURLToPath2(import.meta.url)), "..", "dist", "graph.html")
8760
9788
  ];
8761
9789
  for (const p of candidates) {
8762
9790
  try {
8763
- readFileSync10(p);
9791
+ readFileSync11(p);
8764
9792
  return p;
8765
9793
  } catch {
8766
9794
  }
@@ -8796,7 +9824,7 @@ var HTTPServer = class {
8796
9824
  }
8797
9825
  const serveGraph = (c) => {
8798
9826
  try {
8799
- const html = readFileSync10(GRAPH_HTML_PATH, "utf8");
9827
+ const html = readFileSync11(GRAPH_HTML_PATH, "utf8");
8800
9828
  return c.html(html);
8801
9829
  } catch {
8802
9830
  return c.html("<p>Graph UI not found. Run: pnpm build</p>");
@@ -8806,7 +9834,7 @@ var HTTPServer = class {
8806
9834
  this.app.get("/graph", serveGraph);
8807
9835
  }
8808
9836
  start() {
8809
- return new Promise((resolve17) => {
9837
+ return new Promise((resolve19) => {
8810
9838
  this.server = serve(
8811
9839
  {
8812
9840
  fetch: this.app.fetch,
@@ -8814,20 +9842,20 @@ var HTTPServer = class {
8814
9842
  hostname: this.deps.host
8815
9843
  },
8816
9844
  () => {
8817
- resolve17();
9845
+ resolve19();
8818
9846
  }
8819
9847
  );
8820
9848
  });
8821
9849
  }
8822
9850
  stop() {
8823
- return new Promise((resolve17, reject) => {
9851
+ return new Promise((resolve19, reject) => {
8824
9852
  if (!this.server) {
8825
- resolve17();
9853
+ resolve19();
8826
9854
  return;
8827
9855
  }
8828
9856
  this.server.close((err) => {
8829
9857
  if (err) reject(err);
8830
- else resolve17();
9858
+ else resolve19();
8831
9859
  });
8832
9860
  });
8833
9861
  }
@@ -8839,13 +9867,13 @@ var HTTPServer = class {
8839
9867
  // packages/daemon/src/ZeroAgentDaemon.ts
8840
9868
  init_LLMExecutor();
8841
9869
 
8842
- // packages/daemon/src/IdentityManager.ts
9870
+ // packages/daemon/src/utils/IdentityManager.ts
8843
9871
  init_src();
8844
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
8845
- import { resolve as resolve11, dirname as dirname5 } from "node:path";
9872
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync14, mkdirSync as mkdirSync5 } from "node:fs";
9873
+ import { resolve as resolve13, dirname as dirname5 } from "node:path";
8846
9874
  import { homedir as homedir6, hostname } from "node:os";
8847
9875
  import YAML5 from "yaml";
8848
- var IDENTITY_PATH = resolve11(homedir6(), ".0agent", "identity.yaml");
9876
+ var IDENTITY_PATH = resolve13(homedir6(), ".0agent", "identity.yaml");
8849
9877
  var DEFAULT_IDENTITY = {
8850
9878
  name: "User",
8851
9879
  device_id: `unknown-device`,
@@ -8861,8 +9889,8 @@ var IdentityManager = class {
8861
9889
  * Load or create identity. Call once at daemon startup.
8862
9890
  */
8863
9891
  async init() {
8864
- if (existsSync12(IDENTITY_PATH)) {
8865
- const raw = readFileSync11(IDENTITY_PATH, "utf8");
9892
+ if (existsSync14(IDENTITY_PATH)) {
9893
+ const raw = readFileSync12(IDENTITY_PATH, "utf8");
8866
9894
  this.identity = YAML5.parse(raw);
8867
9895
  } else {
8868
9896
  this.identity = {
@@ -8914,24 +9942,24 @@ var IdentityManager = class {
8914
9942
  }
8915
9943
  save() {
8916
9944
  const dir = dirname5(IDENTITY_PATH);
8917
- if (!existsSync12(dir)) {
9945
+ if (!existsSync14(dir)) {
8918
9946
  mkdirSync5(dir, { recursive: true });
8919
9947
  }
8920
9948
  writeFileSync7(IDENTITY_PATH, YAML5.stringify(this.identity), "utf8");
8921
9949
  }
8922
9950
  };
8923
9951
 
8924
- // packages/daemon/src/TeamManager.ts
8925
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "node:fs";
8926
- import { resolve as resolve12 } from "node:path";
9952
+ // packages/daemon/src/services/TeamManager.ts
9953
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync15, mkdirSync as mkdirSync6 } from "node:fs";
9954
+ import { resolve as resolve14 } from "node:path";
8927
9955
  import { homedir as homedir7 } from "node:os";
8928
9956
  import YAML6 from "yaml";
8929
- var TEAMS_PATH = resolve12(homedir7(), ".0agent", "teams.yaml");
9957
+ var TEAMS_PATH = resolve14(homedir7(), ".0agent", "teams.yaml");
8930
9958
  var TeamManager = class {
8931
9959
  config;
8932
9960
  constructor() {
8933
- if (existsSync13(TEAMS_PATH)) {
8934
- this.config = YAML6.parse(readFileSync12(TEAMS_PATH, "utf8"));
9961
+ if (existsSync15(TEAMS_PATH)) {
9962
+ this.config = YAML6.parse(readFileSync13(TEAMS_PATH, "utf8"));
8935
9963
  } else {
8936
9964
  this.config = { memberships: [] };
8937
9965
  }
@@ -8986,12 +10014,12 @@ var TeamManager = class {
8986
10014
  }
8987
10015
  }
8988
10016
  save() {
8989
- mkdirSync6(resolve12(homedir7(), ".0agent"), { recursive: true });
10017
+ mkdirSync6(resolve14(homedir7(), ".0agent"), { recursive: true });
8990
10018
  writeFileSync8(TEAMS_PATH, YAML6.stringify(this.config), "utf8");
8991
10019
  }
8992
10020
  };
8993
10021
 
8994
- // packages/daemon/src/TeamSync.ts
10022
+ // packages/daemon/src/services/TeamSync.ts
8995
10023
  var TeamSync = class {
8996
10024
  constructor(teamManager, adapter, entityNodeId) {
8997
10025
  this.teamManager = teamManager;
@@ -9069,9 +10097,9 @@ var TeamSync = class {
9069
10097
  }
9070
10098
  };
9071
10099
 
9072
- // packages/daemon/src/GitHubMemorySync.ts
9073
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9, existsSync as existsSync14, readdirSync as readdirSync4 } from "node:fs";
9074
- import { resolve as resolve13 } from "node:path";
10100
+ // packages/daemon/src/services/GitHubMemorySync.ts
10101
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, existsSync as existsSync16, readdirSync as readdirSync4 } from "node:fs";
10102
+ import { resolve as resolve15 } from "node:path";
9075
10103
  import { homedir as homedir8 } from "node:os";
9076
10104
  var GITHUB_API = "https://api.github.com";
9077
10105
  async function ghFetch(path, token, opts) {
@@ -9191,10 +10219,10 @@ var GitHubMemorySync = class {
9191
10219
  )
9192
10220
  );
9193
10221
  }
9194
- const customSkillsDir = resolve13(homedir8(), ".0agent", "skills", "custom");
9195
- if (existsSync14(customSkillsDir)) {
10222
+ const customSkillsDir = resolve15(homedir8(), ".0agent", "skills", "custom");
10223
+ if (existsSync16(customSkillsDir)) {
9196
10224
  for (const file of readdirSync4(customSkillsDir).filter((f) => f.endsWith(".yaml"))) {
9197
- const content = readFileSync13(resolve13(customSkillsDir, file), "utf8");
10225
+ const content = readFileSync14(resolve15(customSkillsDir, file), "utf8");
9198
10226
  pushes.push(putFile(token, owner, repo, `skills/custom/${file}`, content, commitMsg));
9199
10227
  }
9200
10228
  }
@@ -9380,7 +10408,7 @@ var GitHubMemorySync = class {
9380
10408
  }
9381
10409
  async pullCustomSkills() {
9382
10410
  const { token, owner, repo } = this.config;
9383
- const dir = resolve13(homedir8(), ".0agent", "skills", "custom");
10411
+ const dir = resolve15(homedir8(), ".0agent", "skills", "custom");
9384
10412
  try {
9385
10413
  const res = await ghFetch(`/repos/${owner}/${repo}/contents/skills/custom`, token);
9386
10414
  if (!res.ok) return;
@@ -9390,7 +10418,7 @@ var GitHubMemorySync = class {
9390
10418
  if (content) {
9391
10419
  const { mkdirSync: mkdirSync11 } = await import("node:fs");
9392
10420
  mkdirSync11(dir, { recursive: true });
9393
- writeFileSync9(resolve13(dir, file.name), content, "utf8");
10421
+ writeFileSync9(resolve15(dir, file.name), content, "utf8");
9394
10422
  }
9395
10423
  }
9396
10424
  } catch {
@@ -9466,8 +10494,8 @@ git checkout <commit> graph/ # restore graph files
9466
10494
  }
9467
10495
  };
9468
10496
 
9469
- // packages/daemon/src/CodespaceManager.ts
9470
- import { execSync as execSync6, spawn as spawn7 } from "node:child_process";
10497
+ // packages/daemon/src/services/CodespaceManager.ts
10498
+ import { execSync as execSync8, spawn as spawn8 } from "node:child_process";
9471
10499
  var BROWSER_PORT_REMOTE = 3e3;
9472
10500
  var BROWSER_PORT_LOCAL = 3001;
9473
10501
  var DISPLAY_NAME = "0agent-browser";
@@ -9509,7 +10537,7 @@ var CodespaceManager = class {
9509
10537
  console.log(`[Codespace] Creating browser codespace from ${this.memoryRepo}...`);
9510
10538
  console.log("[Codespace] First time: ~2-3 minutes. Subsequent starts: ~30 seconds.");
9511
10539
  try {
9512
- const result = execSync6(
10540
+ const result = execSync8(
9513
10541
  `gh codespace create --repo "${this.memoryRepo}" --machine basicLinux32gb --display-name "${DISPLAY_NAME}" --json name`,
9514
10542
  { encoding: "utf8", timeout: 3e5 }
9515
10543
  );
@@ -9523,7 +10551,7 @@ var CodespaceManager = class {
9523
10551
  /** Find the 0agent-browser codespace by display name. */
9524
10552
  findExisting() {
9525
10553
  try {
9526
- const out = execSync6("gh codespace list --json name,state,displayName,repository", {
10554
+ const out = execSync8("gh codespace list --json name,state,displayName,repository", {
9527
10555
  encoding: "utf8",
9528
10556
  timeout: 1e4
9529
10557
  });
@@ -9539,7 +10567,7 @@ var CodespaceManager = class {
9539
10567
  const info = this.findExisting();
9540
10568
  if (info?.state === "Shutdown") {
9541
10569
  console.log("[Codespace] Starting stopped codespace (~30s)...");
9542
- execSync6(`gh codespace start --codespace "${name}"`, { timeout: 12e4 });
10570
+ execSync8(`gh codespace start --codespace "${name}"`, { timeout: 12e4 });
9543
10571
  await this.waitForState(name, "Available", 60);
9544
10572
  console.log("[Codespace] Codespace is running");
9545
10573
  } else if (info?.state === "Starting") {
@@ -9551,7 +10579,7 @@ var CodespaceManager = class {
9551
10579
  /** Start the browser server inside the codespace (idempotent). */
9552
10580
  async startBrowserServer(name) {
9553
10581
  try {
9554
- execSync6(
10582
+ execSync8(
9555
10583
  `gh codespace exec --codespace "${name}" -- bash -c "pgrep -f 'node server.js' > /dev/null 2>&1 || (cd /workspaces && nohup node server.js > /tmp/browser-server.log 2>&1 &)"`,
9556
10584
  { timeout: 3e4 }
9557
10585
  );
@@ -9562,7 +10590,7 @@ var CodespaceManager = class {
9562
10590
  async openTunnel(name) {
9563
10591
  this.closeTunnel();
9564
10592
  console.log(`[Codespace] Opening tunnel port ${BROWSER_PORT_REMOTE} \u2192 localhost:${BROWSER_PORT_LOCAL}...`);
9565
- this.forwardProcess = spawn7(
10593
+ this.forwardProcess = spawn8(
9566
10594
  "gh",
9567
10595
  ["codespace", "ports", "forward", `${BROWSER_PORT_REMOTE}:${BROWSER_PORT_LOCAL}`, "--codespace", name],
9568
10596
  { stdio: ["ignore", "ignore", "ignore"] }
@@ -9606,7 +10634,7 @@ var CodespaceManager = class {
9606
10634
  this.closeTunnel();
9607
10635
  const info = this.findExisting();
9608
10636
  if (info?.state === "Available") {
9609
- execSync6(`gh codespace stop --codespace "${info.name}"`, { timeout: 3e4 });
10637
+ execSync8(`gh codespace stop --codespace "${info.name}"`, { timeout: 3e4 });
9610
10638
  console.log("[Codespace] Stopped (state preserved, restarts in 30s when needed)");
9611
10639
  }
9612
10640
  }
@@ -9615,7 +10643,7 @@ var CodespaceManager = class {
9615
10643
  this.closeTunnel();
9616
10644
  const info = this.findExisting();
9617
10645
  if (info) {
9618
- execSync6(`gh codespace delete --codespace "${info.name}" --force`, { timeout: 3e4 });
10646
+ execSync8(`gh codespace delete --codespace "${info.name}" --force`, { timeout: 3e4 });
9619
10647
  console.log("[Codespace] Deleted");
9620
10648
  }
9621
10649
  }
@@ -9641,7 +10669,7 @@ var CodespaceManager = class {
9641
10669
  /** Check if gh CLI is installed and authenticated. */
9642
10670
  static isAvailable() {
9643
10671
  try {
9644
- execSync6("gh auth status", { stdio: "ignore", timeout: 5e3 });
10672
+ execSync8("gh auth status", { stdio: "ignore", timeout: 5e3 });
9645
10673
  return true;
9646
10674
  } catch {
9647
10675
  return false;
@@ -9652,7 +10680,7 @@ var CodespaceManager = class {
9652
10680
  // packages/daemon/src/ZeroAgentDaemon.ts
9653
10681
  init_RuntimeSelfHeal();
9654
10682
 
9655
- // packages/daemon/src/TelegramBridge.ts
10683
+ // packages/daemon/src/services/TelegramBridge.ts
9656
10684
  var TelegramBridge = class {
9657
10685
  constructor(config, sessions, eventBus) {
9658
10686
  this.config = config;
@@ -9951,52 +10979,52 @@ var SurfaceRouter = class {
9951
10979
  }
9952
10980
  _handleDaemonEvent(event) {
9953
10981
  const sessionId = String(event.session_id ?? "");
9954
- const state = this.activeSessions.get(sessionId);
9955
- if (!state) return;
9956
- const adapter = this.adapters.get(state.surface);
10982
+ const state2 = this.activeSessions.get(sessionId);
10983
+ if (!state2) return;
10984
+ const adapter = this.adapters.get(state2.surface);
9957
10985
  if (!adapter) return;
9958
10986
  if (event.type === "session.token") {
9959
- state.tokenBuffer += String(event.token ?? "");
9960
- if (state.streamTimer) clearTimeout(state.streamTimer);
9961
- state.streamTimer = setTimeout(() => {
9962
- if (!state.tokenBuffer) return;
10987
+ state2.tokenBuffer += String(event.token ?? "");
10988
+ if (state2.streamTimer) clearTimeout(state2.streamTimer);
10989
+ state2.streamTimer = setTimeout(() => {
10990
+ if (!state2.tokenBuffer) return;
9963
10991
  adapter.send({
9964
- surface_channel_id: state.channelId,
9965
- text: state.tokenBuffer,
10992
+ surface_channel_id: state2.channelId,
10993
+ text: state2.tokenBuffer,
9966
10994
  format: "markdown",
9967
10995
  is_progress: true,
9968
- thread_id: state.threadId
10996
+ thread_id: state2.threadId
9969
10997
  }).catch(() => {
9970
10998
  });
9971
10999
  }, 400);
9972
11000
  } else if (event.type === "session.completed") {
9973
- if (state.streamTimer) {
9974
- clearTimeout(state.streamTimer);
9975
- state.streamTimer = null;
11001
+ if (state2.streamTimer) {
11002
+ clearTimeout(state2.streamTimer);
11003
+ state2.streamTimer = null;
9976
11004
  }
9977
11005
  const result = event.result;
9978
11006
  const output = String(result?.output ?? "").trim();
9979
11007
  if (output && output !== "(no output)") {
9980
11008
  adapter.send({
9981
- surface_channel_id: state.channelId,
11009
+ surface_channel_id: state2.channelId,
9982
11010
  text: output,
9983
11011
  format: "markdown",
9984
11012
  is_progress: false,
9985
- thread_id: state.threadId
11013
+ thread_id: state2.threadId
9986
11014
  }).catch(() => {
9987
11015
  });
9988
11016
  }
9989
11017
  this.activeSessions.delete(sessionId);
9990
11018
  } else if (event.type === "session.failed") {
9991
- if (state.streamTimer) {
9992
- clearTimeout(state.streamTimer);
9993
- state.streamTimer = null;
11019
+ if (state2.streamTimer) {
11020
+ clearTimeout(state2.streamTimer);
11021
+ state2.streamTimer = null;
9994
11022
  }
9995
11023
  adapter.send({
9996
- surface_channel_id: state.channelId,
11024
+ surface_channel_id: state2.channelId,
9997
11025
  text: `\u26A0\uFE0F ${String(event.error ?? "Task failed")}`,
9998
11026
  format: "prose",
9999
- thread_id: state.threadId
11027
+ thread_id: state2.threadId
10000
11028
  }).catch(() => {
10001
11029
  });
10002
11030
  this.activeSessions.delete(sessionId);
@@ -10011,7 +11039,7 @@ var SurfaceRouter = class {
10011
11039
  };
10012
11040
 
10013
11041
  // packages/daemon/src/surfaces/TelegramAdapter.ts
10014
- import { existsSync as existsSync15, mkdirSync as mkdirSync7 } from "node:fs";
11042
+ import { existsSync as existsSync17, mkdirSync as mkdirSync7 } from "node:fs";
10015
11043
  import { tmpdir as tmpdir3 } from "node:os";
10016
11044
  import { join as join4 } from "node:path";
10017
11045
  var TelegramAdapter = class {
@@ -10061,13 +11089,13 @@ var TelegramAdapter = class {
10061
11089
  async send(msg) {
10062
11090
  const chatId = Number(msg.surface_channel_id);
10063
11091
  if (!chatId) return;
10064
- const state = this.streamingState.get(chatId);
10065
- if (msg.is_progress && state) {
10066
- state.accumulatedText = msg.text;
10067
- await this._editMessage(chatId, state.workingMsgId, `\u23F3 ${this._truncate(msg.text, 3800)}`);
11092
+ const state2 = this.streamingState.get(chatId);
11093
+ if (msg.is_progress && state2) {
11094
+ state2.accumulatedText = msg.text;
11095
+ await this._editMessage(chatId, state2.workingMsgId, `\u23F3 ${this._truncate(msg.text, 3800)}`);
10068
11096
  } else {
10069
- if (state) {
10070
- await this._editMessage(chatId, state.workingMsgId, msg.text);
11097
+ if (state2) {
11098
+ await this._editMessage(chatId, state2.workingMsgId, msg.text);
10071
11099
  this.streamingState.delete(chatId);
10072
11100
  } else {
10073
11101
  await this._sendMessage(chatId, msg.text);
@@ -10218,7 +11246,7 @@ Sessions: ${h.active_sessions} active`
10218
11246
  const fileUrl = await this._getFileUrl(fileId);
10219
11247
  if (!fileUrl) return null;
10220
11248
  const tmpDir = join4(tmpdir3(), "0agent-voice");
10221
- if (!existsSync15(tmpDir)) mkdirSync7(tmpDir, { recursive: true });
11249
+ if (!existsSync17(tmpDir)) mkdirSync7(tmpDir, { recursive: true });
10222
11250
  const tmpPath = join4(tmpDir, `${fileId}.ogg`);
10223
11251
  const wavPath = join4(tmpDir, `${fileId}.wav`);
10224
11252
  const res = await fetch(fileUrl);
@@ -10226,20 +11254,20 @@ Sessions: ${h.active_sessions} active`
10226
11254
  const buf = await res.arrayBuffer();
10227
11255
  const { writeFileSync: writeFileSync13 } = await import("node:fs");
10228
11256
  writeFileSync13(tmpPath, Buffer.from(buf));
10229
- const { execSync: execSync9 } = await import("node:child_process");
11257
+ const { execSync: execSync11 } = await import("node:child_process");
10230
11258
  try {
10231
- execSync9(`ffmpeg -y -i "${tmpPath}" -ar 16000 -ac 1 "${wavPath}" 2>/dev/null`, { timeout: 3e4 });
11259
+ execSync11(`ffmpeg -y -i "${tmpPath}" -ar 16000 -ac 1 "${wavPath}" 2>/dev/null`, { timeout: 3e4 });
10232
11260
  } catch {
10233
11261
  }
10234
- const inputFile = existsSync15(wavPath) ? wavPath : tmpPath;
10235
- const whisperOut = execSync9(
11262
+ const inputFile = existsSync17(wavPath) ? wavPath : tmpPath;
11263
+ const whisperOut = execSync11(
10236
11264
  `whisper "${inputFile}" --model ${this.whisperModel} --output_format txt --output_dir "${tmpDir}" --fp16 False 2>/dev/null`,
10237
11265
  { timeout: 12e4, encoding: "utf8" }
10238
11266
  );
10239
11267
  const txtPath = inputFile.replace(/\.(ogg|wav)$/, ".txt");
10240
- if (existsSync15(txtPath)) {
10241
- const { readFileSync: readFileSync17 } = await import("node:fs");
10242
- return readFileSync17(txtPath, "utf8").trim();
11268
+ if (existsSync17(txtPath)) {
11269
+ const { readFileSync: readFileSync18 } = await import("node:fs");
11270
+ return readFileSync18(txtPath, "utf8").trim();
10243
11271
  }
10244
11272
  return whisperOut?.trim() || null;
10245
11273
  } catch {
@@ -10405,24 +11433,24 @@ var SlackAdapter = class {
10405
11433
  if (!this.app) return;
10406
11434
  const client = this.app["client"];
10407
11435
  const stateKey = `${msg.surface_channel_id}:${msg.thread_id ?? ""}`;
10408
- const state = this.streamingState.get(stateKey);
10409
- if (msg.is_progress && state) {
11436
+ const state2 = this.streamingState.get(stateKey);
11437
+ if (msg.is_progress && state2) {
10410
11438
  try {
10411
11439
  await client["chat.update"]({
10412
- channel: state.channelId,
10413
- ts: state.ts,
11440
+ channel: state2.channelId,
11441
+ ts: state2.ts,
10414
11442
  text: `\u23F3 ${this._truncate(msg.text, 3e3)}`
10415
11443
  });
10416
11444
  } catch {
10417
11445
  }
10418
11446
  } else {
10419
- if (state) {
11447
+ if (state2) {
10420
11448
  try {
10421
11449
  await client["chat.update"]({
10422
- channel: state.channelId,
10423
- ts: state.ts,
11450
+ channel: state2.channelId,
11451
+ ts: state2.ts,
10424
11452
  text: msg.text,
10425
- thread_ts: state.threadTs || void 0
11453
+ thread_ts: state2.threadTs || void 0
10426
11454
  });
10427
11455
  } catch {
10428
11456
  await this._postMessage(client, msg.surface_channel_id, msg.text, msg.thread_id);
@@ -10681,10 +11709,10 @@ var WhatsAppAdapter = class {
10681
11709
  import * as readline from "node:readline";
10682
11710
 
10683
11711
  // packages/daemon/src/surfaces/WhisperSTT.ts
10684
- import { execSync as execSync7, spawnSync as spawnSync5 } from "node:child_process";
10685
- import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync14 } from "node:fs";
11712
+ import { execSync as execSync9, spawnSync as spawnSync5 } from "node:child_process";
11713
+ import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync15 } from "node:fs";
10686
11714
  import { tmpdir as tmpdir4 } from "node:os";
10687
- import { join as join5, basename } from "node:path";
11715
+ import { join as join5, basename as basename2 } from "node:path";
10688
11716
  var WhisperSTT = class _WhisperSTT {
10689
11717
  model;
10690
11718
  language;
@@ -10700,20 +11728,20 @@ var WhisperSTT = class _WhisperSTT {
10700
11728
  console.warn("[WhisperSTT] No Whisper binary found. Install: pip install openai-whisper");
10701
11729
  return null;
10702
11730
  }
10703
- if (!existsSync16(audioPath)) {
11731
+ if (!existsSync18(audioPath)) {
10704
11732
  console.warn(`[WhisperSTT] Audio file not found: ${audioPath}`);
10705
11733
  return null;
10706
11734
  }
10707
11735
  const outDir = join5(tmpdir4(), "0agent-whisper");
10708
- if (!existsSync16(outDir)) mkdirSync8(outDir, { recursive: true });
11736
+ if (!existsSync18(outDir)) mkdirSync8(outDir, { recursive: true });
10709
11737
  try {
10710
11738
  const langFlag = this.language ? `--language ${this.language}` : "";
10711
11739
  const cmd = this.binary === "faster-whisper" ? `faster-whisper "${audioPath}" --model ${this.model} ${langFlag} --output_format txt --output_dir "${outDir}"` : `whisper "${audioPath}" --model ${this.model} ${langFlag} --output_format txt --output_dir "${outDir}" --fp16 False`;
10712
- execSync7(cmd, { timeout: 18e4, stdio: "pipe" });
10713
- const baseName = basename(audioPath).replace(/\.[^.]+$/, "");
11740
+ execSync9(cmd, { timeout: 18e4, stdio: "pipe" });
11741
+ const baseName = basename2(audioPath).replace(/\.[^.]+$/, "");
10714
11742
  const txtPath = join5(outDir, `${baseName}.txt`);
10715
- if (existsSync16(txtPath)) {
10716
- return readFileSync14(txtPath, "utf8").trim();
11743
+ if (existsSync18(txtPath)) {
11744
+ return readFileSync15(txtPath, "utf8").trim();
10717
11745
  }
10718
11746
  return null;
10719
11747
  } catch (err) {
@@ -10738,14 +11766,14 @@ var WhisperSTT = class _WhisperSTT {
10738
11766
  };
10739
11767
  async function recordAudio(durationSeconds) {
10740
11768
  const outDir = join5(tmpdir4(), "0agent-voice");
10741
- if (!existsSync16(outDir)) mkdirSync8(outDir, { recursive: true });
11769
+ if (!existsSync18(outDir)) mkdirSync8(outDir, { recursive: true });
10742
11770
  const outPath = join5(outDir, `recording-${Date.now()}.wav`);
10743
11771
  const soxResult = spawnSync5(
10744
11772
  "sox",
10745
11773
  ["-d", "-r", "16000", "-c", "1", "-b", "16", outPath, "trim", "0", String(durationSeconds)],
10746
11774
  { timeout: (durationSeconds + 5) * 1e3, stdio: "pipe" }
10747
11775
  );
10748
- if (soxResult.status === 0 && existsSync16(outPath)) return outPath;
11776
+ if (soxResult.status === 0 && existsSync18(outPath)) return outPath;
10749
11777
  const platform3 = process.platform;
10750
11778
  let ffmpegDevice;
10751
11779
  if (platform3 === "darwin") {
@@ -10760,11 +11788,11 @@ async function recordAudio(durationSeconds) {
10760
11788
  ["-y", ...ffmpegDevice, "-ar", "16000", "-ac", "1", "-t", String(durationSeconds), outPath],
10761
11789
  { timeout: (durationSeconds + 5) * 1e3, stdio: "pipe" }
10762
11790
  );
10763
- return ffmpegResult.status === 0 && existsSync16(outPath) ? outPath : null;
11791
+ return ffmpegResult.status === 0 && existsSync18(outPath) ? outPath : null;
10764
11792
  }
10765
11793
 
10766
11794
  // packages/daemon/src/surfaces/NativeTTS.ts
10767
- import { spawnSync as spawnSync6, spawn as spawn8 } from "node:child_process";
11795
+ import { spawnSync as spawnSync6, spawn as spawn9 } from "node:child_process";
10768
11796
  var NativeTTS = class _NativeTTS {
10769
11797
  engine;
10770
11798
  voice;
@@ -10788,11 +11816,11 @@ var NativeTTS = class _NativeTTS {
10788
11816
  if (!this.resolvedEngine) return;
10789
11817
  const cleaned = this._clean(text);
10790
11818
  if (!cleaned) return;
10791
- return new Promise((resolve17) => {
11819
+ return new Promise((resolve19) => {
10792
11820
  const args = this._buildArgs(this.resolvedEngine, cleaned);
10793
- const proc = spawn8(this.resolvedEngine, args, { stdio: "ignore" });
10794
- proc.on("close", () => resolve17());
10795
- proc.on("error", () => resolve17());
11821
+ const proc = spawn9(this.resolvedEngine, args, { stdio: "ignore" });
11822
+ proc.on("close", () => resolve19());
11823
+ proc.on("error", () => resolve19());
10796
11824
  });
10797
11825
  }
10798
11826
  /** Check if any TTS engine is available */
@@ -10850,7 +11878,7 @@ var NativeTTS = class _NativeTTS {
10850
11878
  }
10851
11879
  _speakWith(engine, text) {
10852
11880
  const args = this._buildArgs(engine, text);
10853
- const proc = spawn8(engine, args, { stdio: "ignore", detached: true });
11881
+ const proc = spawn9(engine, args, { stdio: "ignore", detached: true });
10854
11882
  proc.unref();
10855
11883
  }
10856
11884
  /** Remove markdown/ANSI and control chars before speaking */
@@ -10975,10 +12003,10 @@ var VoiceAdapter = class {
10975
12003
  };
10976
12004
 
10977
12005
  // packages/daemon/src/surfaces/MeetingAdapter.ts
10978
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "node:fs";
12006
+ import { existsSync as existsSync19, mkdirSync as mkdirSync9, writeFileSync as writeFileSync11 } from "node:fs";
10979
12007
  import { tmpdir as tmpdir5 } from "node:os";
10980
12008
  import { join as join6 } from "node:path";
10981
- import { spawn as spawn9 } from "node:child_process";
12009
+ import { spawn as spawn10 } from "node:child_process";
10982
12010
  var MeetingAdapter = class {
10983
12011
  name = "meeting";
10984
12012
  messageHandler = null;
@@ -11003,7 +12031,7 @@ var MeetingAdapter = class {
11003
12031
  this.triggerPhrases = config.trigger_phrases ?? ["agent,", "hey agent", "ok agent"];
11004
12032
  this.contextWindowSeconds = config.context_window_seconds ?? 120;
11005
12033
  this.tmpDir = join6(tmpdir5(), "0agent-meeting");
11006
- if (!existsSync17(this.tmpDir)) mkdirSync9(this.tmpDir, { recursive: true });
12034
+ if (!existsSync19(this.tmpDir)) mkdirSync9(this.tmpDir, { recursive: true });
11007
12035
  this.stt = new WhisperSTT({ model: config.whisper_model ?? "base" });
11008
12036
  }
11009
12037
  onMessage(handler) {
@@ -11088,7 +12116,7 @@ ${msg.text}
11088
12116
  async _captureAndTranscribeChunk(channelId) {
11089
12117
  const chunkPath = join6(this.tmpDir, `chunk-${Date.now()}.wav`);
11090
12118
  const captured = await this._captureSystemAudio(chunkPath, this.chunkSeconds);
11091
- if (!captured || !existsSync17(chunkPath)) return;
12119
+ if (!captured || !existsSync19(chunkPath)) return;
11092
12120
  const text = await this.stt.transcribe(chunkPath);
11093
12121
  if (!text || text.trim().length < 3) return;
11094
12122
  const segment = { text: text.trim(), timestamp: Date.now() };
@@ -11109,7 +12137,7 @@ ${msg.text}
11109
12137
  }
11110
12138
  }
11111
12139
  async _captureSystemAudio(outPath, seconds) {
11112
- return new Promise((resolve17) => {
12140
+ return new Promise((resolve19) => {
11113
12141
  const platform3 = process.platform;
11114
12142
  let args;
11115
12143
  if (platform3 === "darwin") {
@@ -11117,18 +12145,18 @@ ${msg.text}
11117
12145
  } else if (platform3 === "linux") {
11118
12146
  args = ["-y", "-f", "pulse", "-i", "default.monitor", "-ar", "16000", "-ac", "1", "-t", String(seconds), outPath];
11119
12147
  } else {
11120
- resolve17(false);
12148
+ resolve19(false);
11121
12149
  return;
11122
12150
  }
11123
- const proc = spawn9("ffmpeg", args, { stdio: "pipe" });
12151
+ const proc = spawn10("ffmpeg", args, { stdio: "pipe" });
11124
12152
  this.ffmpegProcess = proc;
11125
12153
  proc.on("close", (code) => {
11126
12154
  this.ffmpegProcess = null;
11127
- resolve17(code === 0);
12155
+ resolve19(code === 0);
11128
12156
  });
11129
12157
  proc.on("error", () => {
11130
12158
  this.ffmpegProcess = null;
11131
- resolve17(false);
12159
+ resolve19(false);
11132
12160
  });
11133
12161
  });
11134
12162
  }
@@ -11226,12 +12254,12 @@ var ZeroAgentDaemon = class {
11226
12254
  startedAt = 0;
11227
12255
  pidFilePath;
11228
12256
  constructor() {
11229
- this.pidFilePath = resolve15(homedir9(), ".0agent", "daemon.pid");
12257
+ this.pidFilePath = resolve17(homedir9(), ".0agent", "daemon.pid");
11230
12258
  }
11231
12259
  async start(opts) {
11232
12260
  this.config = await loadConfig(opts?.config_path);
11233
- const dotDir = resolve15(homedir9(), ".0agent");
11234
- if (!existsSync19(dotDir)) {
12261
+ const dotDir = resolve17(homedir9(), ".0agent");
12262
+ if (!existsSync21(dotDir)) {
11235
12263
  mkdirSync10(dotDir, { recursive: true });
11236
12264
  }
11237
12265
  this.adapter = new SQLiteAdapter({ db_path: this.config.graph.db_path });
@@ -11305,10 +12333,10 @@ var ZeroAgentDaemon = class {
11305
12333
  console.log(`[0agent] Teams: ${teams.map((t) => t.team_name).join(", ")}`);
11306
12334
  }
11307
12335
  const _daemonFile = fileURLToPath3(import.meta.url);
11308
- const _agentRoot = resolve15(dirname7(_daemonFile), "..");
12336
+ const _agentRoot = resolve17(dirname7(_daemonFile), "..");
11309
12337
  let agentRoot;
11310
12338
  try {
11311
- const _pkg = JSON.parse(readFileSync16(resolve15(_agentRoot, "package.json"), "utf8"));
12339
+ const _pkg = JSON.parse(readFileSync17(resolve17(_agentRoot, "package.json"), "utf8"));
11312
12340
  if (_pkg.name === "0agent") agentRoot = _agentRoot;
11313
12341
  } catch {
11314
12342
  }
@@ -11512,7 +12540,7 @@ var ZeroAgentDaemon = class {
11512
12540
  this.graph = null;
11513
12541
  }
11514
12542
  this.adapter = null;
11515
- if (existsSync19(this.pidFilePath)) {
12543
+ if (existsSync21(this.pidFilePath)) {
11516
12544
  try {
11517
12545
  unlinkSync4(this.pidFilePath);
11518
12546
  } catch {
@@ -11542,11 +12570,11 @@ var ZeroAgentDaemon = class {
11542
12570
  };
11543
12571
 
11544
12572
  // packages/daemon/src/start.ts
11545
- import { resolve as resolve16 } from "node:path";
12573
+ import { resolve as resolve18 } from "node:path";
11546
12574
  import { homedir as homedir10 } from "node:os";
11547
- import { existsSync as existsSync20 } from "node:fs";
11548
- var CONFIG_PATH = process.env["ZEROAGENT_CONFIG"] ?? resolve16(homedir10(), ".0agent", "config.yaml");
11549
- if (!existsSync20(CONFIG_PATH)) {
12575
+ import { existsSync as existsSync22 } from "node:fs";
12576
+ var CONFIG_PATH = process.env["ZEROAGENT_CONFIG"] ?? resolve18(homedir10(), ".0agent", "config.yaml");
12577
+ if (!existsSync22(CONFIG_PATH)) {
11550
12578
  console.error(`
11551
12579
  0agent is not initialised.
11552
12580