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.
- package/dist/daemon.mjs +1331 -303
- 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((
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
2919
|
+
// packages/daemon/src/tools/MemoryCapability.ts
|
|
2910
2920
|
var MemoryCapability;
|
|
2911
2921
|
var init_MemoryCapability = __esm({
|
|
2912
|
-
"packages/daemon/src/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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((
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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/
|
|
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/
|
|
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/
|
|
4313
|
+
// packages/daemon/src/tools/BrowserExecuteCapability.ts
|
|
4304
4314
|
var BrowserExecuteCapability;
|
|
4305
4315
|
var init_BrowserExecuteCapability = __esm({
|
|
4306
|
-
"packages/daemon/src/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
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/
|
|
4868
|
+
// packages/daemon/src/tools/MonitorWatchCapability.ts
|
|
4859
4869
|
var DAEMON_URL, MonitorWatchCapability;
|
|
4860
4870
|
var init_MonitorWatchCapability = __esm({
|
|
4861
|
-
"packages/daemon/src/
|
|
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/
|
|
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/
|
|
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/
|
|
5541
|
+
// packages/daemon/src/tools/CapabilityRegistry.ts
|
|
5121
5542
|
var CapabilityRegistry;
|
|
5122
5543
|
var init_CapabilityRegistry = __esm({
|
|
5123
|
-
"packages/daemon/src/
|
|
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/
|
|
5242
|
-
var
|
|
5243
|
-
"packages/daemon/src/
|
|
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
|
|
5262
|
-
import { writeFileSync as writeFileSync4, readFileSync as
|
|
5263
|
-
import { resolve as
|
|
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
|
-
|
|
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
|
|
5326
|
-
|
|
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
|
-
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
const
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
}
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
}
|
|
5407
|
-
|
|
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((
|
|
6216
|
+
return new Promise((resolve19) => {
|
|
5466
6217
|
const chunks = [];
|
|
5467
|
-
const proc =
|
|
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
|
-
|
|
6227
|
+
resolve19(output || (code === 0 ? "(command completed, no output)" : `exit code ${code}`));
|
|
5477
6228
|
});
|
|
5478
6229
|
proc.on("error", (err) => {
|
|
5479
|
-
|
|
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 (!
|
|
5495
|
-
const content =
|
|
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 (!
|
|
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 =
|
|
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
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
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 (
|
|
5735
|
-
const content =
|
|
6485
|
+
if (existsSync7(f)) {
|
|
6486
|
+
const content = readFileSync5(f, "utf8").trim();
|
|
5736
6487
|
if (content && content.length < 4e3) {
|
|
5737
|
-
|
|
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
|
|
5848
|
-
import { resolve as
|
|
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 =
|
|
5886
|
-
const exists =
|
|
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
|
|
5926
|
-
import { resolve as
|
|
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
|
|
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 (!
|
|
5953
|
-
const content =
|
|
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 !== "/" && !
|
|
5999
|
-
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 || !
|
|
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 =
|
|
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 =
|
|
6056
|
-
if (
|
|
6876
|
+
const bundleScript = resolve9(this.projectRoot, "scripts", "bundle.mjs");
|
|
6877
|
+
if (existsSync10(bundleScript)) {
|
|
6057
6878
|
try {
|
|
6058
|
-
|
|
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
|
-
|
|
6910
|
+
resolve9(this.projectRoot, location.relPath),
|
|
6090
6911
|
// If relPath starts with dist/, look in src/
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
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 (
|
|
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 =
|
|
6162
|
-
if (
|
|
6163
|
-
const child =
|
|
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
|
|
6267
|
-
import { existsSync as
|
|
6268
|
-
import { resolve as
|
|
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 (!
|
|
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 =
|
|
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 (!
|
|
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 =
|
|
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
|
|
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
|
|
6423
|
-
import { resolve as
|
|
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
|
|
6819
|
-
import { readFileSync as
|
|
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 (
|
|
7689
|
+
if (existsSync8(pkgPath)) {
|
|
6869
7690
|
try {
|
|
6870
|
-
const pkg = JSON.parse(
|
|
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 ||
|
|
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 (
|
|
7704
|
+
if (existsSync8(join2(this.cwd, "Cargo.toml"))) {
|
|
6884
7705
|
stack.push("rust");
|
|
6885
7706
|
try {
|
|
6886
|
-
const cargo =
|
|
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 (
|
|
7713
|
+
if (existsSync8(join2(this.cwd, "pyproject.toml")) || existsSync8(join2(this.cwd, "requirements.txt"))) {
|
|
6893
7714
|
stack.push("python");
|
|
6894
7715
|
}
|
|
6895
|
-
if (
|
|
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 =
|
|
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 =
|
|
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((
|
|
7746
|
+
(port) => new Promise((resolve19) => {
|
|
6926
7747
|
const s = createServer();
|
|
6927
7748
|
s.listen(port, "127.0.0.1", () => {
|
|
6928
7749
|
s.close();
|
|
6929
|
-
|
|
7750
|
+
resolve19();
|
|
6930
7751
|
});
|
|
6931
7752
|
s.on("error", () => {
|
|
6932
7753
|
open.push(port);
|
|
6933
|
-
|
|
7754
|
+
resolve19();
|
|
6934
7755
|
});
|
|
6935
7756
|
setTimeout(() => {
|
|
6936
7757
|
s.close();
|
|
6937
|
-
|
|
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 (
|
|
7767
|
+
if (existsSync8(p)) {
|
|
6947
7768
|
try {
|
|
6948
|
-
return
|
|
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
|
|
7014
|
-
import { resolve as
|
|
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
|
|
7252
|
-
if (
|
|
7253
|
-
|
|
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,
|
|
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 =
|
|
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 =
|
|
7452
|
-
if (!
|
|
7453
|
-
const raw =
|
|
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 =
|
|
7480
|
-
if (
|
|
7481
|
-
const raw =
|
|
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
|
|
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 (!
|
|
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 =
|
|
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 (
|
|
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
|
|
7935
|
-
import { resolve as
|
|
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
|
|
8228
|
-
import { resolve as
|
|
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 =
|
|
8237
|
-
if (!
|
|
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(
|
|
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
|
-
|
|
9783
|
+
resolve12(dirname4(fileURLToPath2(import.meta.url)), "graph.html"),
|
|
8756
9784
|
// dev (src/)
|
|
8757
|
-
|
|
9785
|
+
resolve12(dirname4(fileURLToPath2(import.meta.url)), "..", "graph.html"),
|
|
8758
9786
|
// bundled (dist/../)
|
|
8759
|
-
|
|
9787
|
+
resolve12(dirname4(fileURLToPath2(import.meta.url)), "..", "dist", "graph.html")
|
|
8760
9788
|
];
|
|
8761
9789
|
for (const p of candidates) {
|
|
8762
9790
|
try {
|
|
8763
|
-
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
9845
|
+
resolve19();
|
|
8818
9846
|
}
|
|
8819
9847
|
);
|
|
8820
9848
|
});
|
|
8821
9849
|
}
|
|
8822
9850
|
stop() {
|
|
8823
|
-
return new Promise((
|
|
9851
|
+
return new Promise((resolve19, reject) => {
|
|
8824
9852
|
if (!this.server) {
|
|
8825
|
-
|
|
9853
|
+
resolve19();
|
|
8826
9854
|
return;
|
|
8827
9855
|
}
|
|
8828
9856
|
this.server.close((err) => {
|
|
8829
9857
|
if (err) reject(err);
|
|
8830
|
-
else
|
|
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
|
|
8845
|
-
import { resolve as
|
|
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 =
|
|
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 (
|
|
8865
|
-
const raw =
|
|
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 (!
|
|
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
|
|
8926
|
-
import { resolve as
|
|
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 =
|
|
9957
|
+
var TEAMS_PATH = resolve14(homedir7(), ".0agent", "teams.yaml");
|
|
8930
9958
|
var TeamManager = class {
|
|
8931
9959
|
config;
|
|
8932
9960
|
constructor() {
|
|
8933
|
-
if (
|
|
8934
|
-
this.config = YAML6.parse(
|
|
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(
|
|
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
|
|
9074
|
-
import { resolve as
|
|
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 =
|
|
9195
|
-
if (
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
9955
|
-
if (!
|
|
9956
|
-
const adapter = this.adapters.get(
|
|
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
|
-
|
|
9960
|
-
if (
|
|
9961
|
-
|
|
9962
|
-
if (!
|
|
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:
|
|
9965
|
-
text:
|
|
10992
|
+
surface_channel_id: state2.channelId,
|
|
10993
|
+
text: state2.tokenBuffer,
|
|
9966
10994
|
format: "markdown",
|
|
9967
10995
|
is_progress: true,
|
|
9968
|
-
thread_id:
|
|
10996
|
+
thread_id: state2.threadId
|
|
9969
10997
|
}).catch(() => {
|
|
9970
10998
|
});
|
|
9971
10999
|
}, 400);
|
|
9972
11000
|
} else if (event.type === "session.completed") {
|
|
9973
|
-
if (
|
|
9974
|
-
clearTimeout(
|
|
9975
|
-
|
|
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:
|
|
11009
|
+
surface_channel_id: state2.channelId,
|
|
9982
11010
|
text: output,
|
|
9983
11011
|
format: "markdown",
|
|
9984
11012
|
is_progress: false,
|
|
9985
|
-
thread_id:
|
|
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 (
|
|
9992
|
-
clearTimeout(
|
|
9993
|
-
|
|
11019
|
+
if (state2.streamTimer) {
|
|
11020
|
+
clearTimeout(state2.streamTimer);
|
|
11021
|
+
state2.streamTimer = null;
|
|
9994
11022
|
}
|
|
9995
11023
|
adapter.send({
|
|
9996
|
-
surface_channel_id:
|
|
11024
|
+
surface_channel_id: state2.channelId,
|
|
9997
11025
|
text: `\u26A0\uFE0F ${String(event.error ?? "Task failed")}`,
|
|
9998
11026
|
format: "prose",
|
|
9999
|
-
thread_id:
|
|
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
|
|
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
|
|
10065
|
-
if (msg.is_progress &&
|
|
10066
|
-
|
|
10067
|
-
await this._editMessage(chatId,
|
|
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 (
|
|
10070
|
-
await this._editMessage(chatId,
|
|
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 (!
|
|
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:
|
|
11257
|
+
const { execSync: execSync11 } = await import("node:child_process");
|
|
10230
11258
|
try {
|
|
10231
|
-
|
|
11259
|
+
execSync11(`ffmpeg -y -i "${tmpPath}" -ar 16000 -ac 1 "${wavPath}" 2>/dev/null`, { timeout: 3e4 });
|
|
10232
11260
|
} catch {
|
|
10233
11261
|
}
|
|
10234
|
-
const inputFile =
|
|
10235
|
-
const whisperOut =
|
|
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 (
|
|
10241
|
-
const { readFileSync:
|
|
10242
|
-
return
|
|
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
|
|
10409
|
-
if (msg.is_progress &&
|
|
11436
|
+
const state2 = this.streamingState.get(stateKey);
|
|
11437
|
+
if (msg.is_progress && state2) {
|
|
10410
11438
|
try {
|
|
10411
11439
|
await client["chat.update"]({
|
|
10412
|
-
channel:
|
|
10413
|
-
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 (
|
|
11447
|
+
if (state2) {
|
|
10420
11448
|
try {
|
|
10421
11449
|
await client["chat.update"]({
|
|
10422
|
-
channel:
|
|
10423
|
-
ts:
|
|
11450
|
+
channel: state2.channelId,
|
|
11451
|
+
ts: state2.ts,
|
|
10424
11452
|
text: msg.text,
|
|
10425
|
-
thread_ts:
|
|
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
|
|
10685
|
-
import { existsSync as
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
10713
|
-
const baseName =
|
|
11740
|
+
execSync9(cmd, { timeout: 18e4, stdio: "pipe" });
|
|
11741
|
+
const baseName = basename2(audioPath).replace(/\.[^.]+$/, "");
|
|
10714
11742
|
const txtPath = join5(outDir, `${baseName}.txt`);
|
|
10715
|
-
if (
|
|
10716
|
-
return
|
|
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 (!
|
|
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 &&
|
|
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 &&
|
|
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
|
|
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((
|
|
11819
|
+
return new Promise((resolve19) => {
|
|
10792
11820
|
const args = this._buildArgs(this.resolvedEngine, cleaned);
|
|
10793
|
-
const proc =
|
|
10794
|
-
proc.on("close", () =>
|
|
10795
|
-
proc.on("error", () =>
|
|
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 =
|
|
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
|
|
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
|
|
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 (!
|
|
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 || !
|
|
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((
|
|
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
|
-
|
|
12148
|
+
resolve19(false);
|
|
11121
12149
|
return;
|
|
11122
12150
|
}
|
|
11123
|
-
const proc =
|
|
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
|
-
|
|
12155
|
+
resolve19(code === 0);
|
|
11128
12156
|
});
|
|
11129
12157
|
proc.on("error", () => {
|
|
11130
12158
|
this.ffmpegProcess = null;
|
|
11131
|
-
|
|
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 =
|
|
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 =
|
|
11234
|
-
if (!
|
|
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 =
|
|
12336
|
+
const _agentRoot = resolve17(dirname7(_daemonFile), "..");
|
|
11309
12337
|
let agentRoot;
|
|
11310
12338
|
try {
|
|
11311
|
-
const _pkg = JSON.parse(
|
|
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 (
|
|
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
|
|
12573
|
+
import { resolve as resolve18 } from "node:path";
|
|
11546
12574
|
import { homedir as homedir10 } from "node:os";
|
|
11547
|
-
import { existsSync as
|
|
11548
|
-
var CONFIG_PATH = process.env["ZEROAGENT_CONFIG"] ??
|
|
11549
|
-
if (!
|
|
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
|
|