@hizliemre/horse-code 0.1.2 → 0.3.0
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/{app-LAJN3TWC.js → app-5FXHE7GX.js} +354 -350
- package/dist/{chunk-2SVAHH5N.js → chunk-63E73TGI.js} +2 -4
- package/dist/{chunk-DTWKSZXY.js → chunk-6OSEQOYY.js} +2 -2
- package/dist/{chunk-SSDLHWSF.js → chunk-6W4UH2BQ.js} +33 -1
- package/dist/{chunk-VPAWRRHL.js → chunk-AE36LLL2.js} +31 -21
- package/dist/{chunk-4EWK7HWQ.js → chunk-EAF22QIG.js} +32 -0
- package/dist/chunk-G45RWL7S.js +289 -0
- package/dist/{chunk-FFYBY2NA.js → chunk-KAGKX2YT.js} +2 -4
- package/dist/{chunk-DRZSUQ7Q.js → chunk-LLL7QWXB.js} +18 -7
- package/dist/{chunk-WYQBRCKY.js → chunk-LNW557IO.js} +2 -2
- package/dist/{chunk-IW2KBAVZ.js → chunk-LPQU436C.js} +12 -1
- package/dist/chunk-M2RKCIGV.js +11 -0
- package/dist/{chunk-NNTIACT4.js → chunk-MRZVA5JB.js} +4 -4
- package/dist/{chunk-RPVAIS3P.js → chunk-UEWVVN5L.js} +1511 -305
- package/dist/{chunk-EQX7BQYN.js → chunk-UGESK765.js} +1 -1
- package/dist/{chunk-PGOYDOI4.js → chunk-XEGQT5EN.js} +3 -5
- package/dist/{chunk-K2VERI5Q.js → chunk-XYZVZPAY.js} +365 -666
- package/dist/{chunk-KOWMHL23.js → chunk-YPZP7LYL.js} +2 -2
- package/dist/{chunk-FGVJFMK5.js → chunk-ZSQ24YDJ.js} +1 -1
- package/dist/cli.js +675 -95
- package/dist/{discover-5URG7C4J.js → discover-G2Z6XC3O.js} +9 -3
- package/dist/fix-ONLA45HD.js +34 -0
- package/dist/git-QZTDZSJY.js +8 -0
- package/dist/{ongoing-XP6WXNI7.js → ongoing-WHYXPW24.js} +4 -4
- package/dist/{project-graph-IOPCSZUA.js → project-graph-5HNPRFQG.js} +2 -3
- package/dist/{run-LQOZ5I7Z.js → run-P6ZYL5JL.js} +2 -3
- package/dist/{save-skills-NSLBU33X.js → save-skills-ZW5GY6KV.js} +2 -1
- package/dist/{trace-ZMB7LT7W.js → trace-X6TU3AG6.js} +2 -3
- package/dist/{trace-adopt-C6TUWFJL.js → trace-adopt-URECQWJV.js} +2 -3
- package/dist/{trace-run-F23MFTY4.js → trace-run-7U4WJZ3V.js} +6 -7
- package/dist/{triage-ES5OHZOS.js → triage-FCYHD2AQ.js} +8 -9
- package/dist/{verify-3R7DGUSI.js → verify-LC57A6H2.js} +19 -19
- package/package.json +1 -1
- package/dist/chunk-3UYA3KUG.js +0 -212
- package/dist/chunk-B67BK5GQ.js +0 -34
- package/dist/chunk-O74BDQKS.js +0 -28
- package/dist/fix-CMARU6JR.js +0 -34
- package/dist/git-VTSZALSR.js +0 -6
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
hashContent,
|
|
3
3
|
traceDir
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-KAGKX2YT.js";
|
|
5
5
|
|
|
6
6
|
// src/engine/project-brief.ts
|
|
7
7
|
import { readFile, writeFile, mkdir } from "fs/promises";
|
|
@@ -70,7 +70,7 @@ async function gatherBriefInput(cwd, files) {
|
|
|
70
70
|
async function ourOwnOutput(cwd) {
|
|
71
71
|
const out = /* @__PURE__ */ new Set();
|
|
72
72
|
try {
|
|
73
|
-
const { loadTraceIndex, traceRootRel } = await import("./trace-
|
|
73
|
+
const { loadTraceIndex, traceRootRel } = await import("./trace-X6TU3AG6.js");
|
|
74
74
|
const root = traceRootRel().replace(/\\/g, "/");
|
|
75
75
|
out.add(`${root}/PROJECT.md`);
|
|
76
76
|
out.add(`${root}/PROJECT.json`);
|
|
@@ -27,9 +27,41 @@ function inLinkedWorktree(cwd, run) {
|
|
|
27
27
|
return resolve(dir) !== resolve(common);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
// src/session/atomic.ts
|
|
31
|
+
import { rename, rm, writeFile } from "fs/promises";
|
|
32
|
+
import { renameSync, rmSync, writeFileSync } from "fs";
|
|
33
|
+
var seq = 0;
|
|
34
|
+
var tmpName = (path) => `${path}.${process.pid}.${seq++}.tmp`;
|
|
35
|
+
async function writeAtomic(path, data) {
|
|
36
|
+
const tmp = tmpName(path);
|
|
37
|
+
try {
|
|
38
|
+
await writeFile(tmp, data, "utf8");
|
|
39
|
+
await rename(tmp, path);
|
|
40
|
+
} catch (e) {
|
|
41
|
+
await rm(tmp, { force: true }).catch(() => {
|
|
42
|
+
});
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function writeAtomicSync(path, data) {
|
|
47
|
+
const tmp = tmpName(path);
|
|
48
|
+
try {
|
|
49
|
+
writeFileSync(tmp, data, "utf8");
|
|
50
|
+
renameSync(tmp, path);
|
|
51
|
+
} catch (e) {
|
|
52
|
+
try {
|
|
53
|
+
rmSync(tmp, { force: true });
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
30
60
|
export {
|
|
31
61
|
sessionBase,
|
|
32
62
|
stateRoot,
|
|
33
63
|
writableStateRoot,
|
|
34
|
-
inLinkedWorktree
|
|
64
|
+
inLinkedWorktree,
|
|
65
|
+
writeAtomic,
|
|
66
|
+
writeAtomicSync
|
|
35
67
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
sessionBase
|
|
3
|
+
} from "./chunk-6W4UH2BQ.js";
|
|
4
|
+
|
|
1
5
|
// src/tui/format.ts
|
|
2
6
|
function fmtTokens(n) {
|
|
3
7
|
if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
|
|
@@ -443,22 +447,6 @@ function recallNote(tool, subject, turn, authored = false) {
|
|
|
443
447
|
}
|
|
444
448
|
|
|
445
449
|
// src/core/surrogates.ts
|
|
446
|
-
var LONE_SURROGATE = /[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g;
|
|
447
|
-
var REPLACEMENT = "\uFFFD";
|
|
448
|
-
function stripLoneSurrogates(text) {
|
|
449
|
-
LONE_SURROGATE.lastIndex = 0;
|
|
450
|
-
return LONE_SURROGATE.test(text) ? text.replace(LONE_SURROGATE, REPLACEMENT) : text;
|
|
451
|
-
}
|
|
452
|
-
function sanitizeForJson(value) {
|
|
453
|
-
if (typeof value === "string") return stripLoneSurrogates(value);
|
|
454
|
-
if (Array.isArray(value)) return value.map((v) => sanitizeForJson(v));
|
|
455
|
-
if (value && typeof value === "object") {
|
|
456
|
-
const out = {};
|
|
457
|
-
for (const [k, v] of Object.entries(value)) out[k] = sanitizeForJson(v);
|
|
458
|
-
return out;
|
|
459
|
-
}
|
|
460
|
-
return value;
|
|
461
|
-
}
|
|
462
450
|
function truncateSafe(text, max) {
|
|
463
451
|
if (text.length <= max || max <= 0) return text;
|
|
464
452
|
const code = text.charCodeAt(max - 1);
|
|
@@ -983,15 +971,19 @@ function shieldToolOutput(text) {
|
|
|
983
971
|
}
|
|
984
972
|
|
|
985
973
|
// src/agent/loop.ts
|
|
974
|
+
var DELEGATED_TOOL_NAMES = { file_change: "Write", command_execution: "Bash" };
|
|
986
975
|
function fmtChars(n) {
|
|
987
976
|
return n >= 1e3 ? `${(n / 1e3).toFixed(1)}k chars` : `${n} chars`;
|
|
988
977
|
}
|
|
989
978
|
function workingDirectoryNote(cwd) {
|
|
990
|
-
|
|
979
|
+
const here = `
|
|
991
980
|
|
|
992
981
|
# Working directory
|
|
993
982
|
|
|
994
983
|
You are already in \`${cwd}\`. Every relative path resolves from here, and every tool runs here \u2014 do not \`cd\` elsewhere, and do not go looking for the repository.`;
|
|
984
|
+
return sessionBase(cwd) === void 0 ? here : `${here}
|
|
985
|
+
|
|
986
|
+
This is a fresh git worktree, so everything git ignores is absent: no \`node_modules\`, no build output, no package caches. Source, tests and configuration are all here and current.`;
|
|
995
987
|
}
|
|
996
988
|
var COMPACT_KEYS_LOGGED = 24;
|
|
997
989
|
var agentSeq = 0;
|
|
@@ -1067,6 +1059,16 @@ async function* runRoleAgent(opts) {
|
|
|
1067
1059
|
toolCalls.push(ev.toolCall);
|
|
1068
1060
|
} else if (ev.type === "tool-progress") {
|
|
1069
1061
|
if (ev.path) opts.onLiveActivity?.(`writing ${ev.path.split("/").pop()} \xB7 ${fmtChars(ev.chars)}`);
|
|
1062
|
+
} else if (ev.type === "activity") {
|
|
1063
|
+
const what = ev.target ? ev.target.split("/").pop() ?? ev.target : "";
|
|
1064
|
+
opts.onActivity?.({
|
|
1065
|
+
tool: DELEGATED_TOOL_NAMES[ev.tool] ?? ev.tool,
|
|
1066
|
+
target: what,
|
|
1067
|
+
lines: 0,
|
|
1068
|
+
// Only a failure is worth a suffix. "done" on every row is the bullet said twice.
|
|
1069
|
+
summary: ev.ok === false ? "failed" : "",
|
|
1070
|
+
ok: ev.ok !== false
|
|
1071
|
+
});
|
|
1070
1072
|
} else if (ev.type === "usage") {
|
|
1071
1073
|
yield { type: "usage", promptTokens: ev.promptTokens, completionTokens: ev.completionTokens };
|
|
1072
1074
|
opts.onUsage?.({ promptTokens: ev.promptTokens, completionTokens: ev.completionTokens, model: activeModel });
|
|
@@ -1082,7 +1084,7 @@ async function* runRoleAgent(opts) {
|
|
|
1082
1084
|
break;
|
|
1083
1085
|
}
|
|
1084
1086
|
if (errored.retryable && !errored.capability && !errored.noBench) opts.onExhausted?.(activeModel, errored.message);
|
|
1085
|
-
if (errored.retryable &&
|
|
1087
|
+
if (errored.retryable && chainIdx < chain.length - 1) {
|
|
1086
1088
|
const next = chain[chainIdx + 1];
|
|
1087
1089
|
opts.onFallback?.(activeModel, next, errored.message);
|
|
1088
1090
|
chainIdx++;
|
|
@@ -1240,6 +1242,10 @@ var ToolRegistry = class {
|
|
|
1240
1242
|
}
|
|
1241
1243
|
};
|
|
1242
1244
|
|
|
1245
|
+
// src/core/types.ts
|
|
1246
|
+
var DEADLINE_MESSAGE = "the model did not answer within its deadline";
|
|
1247
|
+
var CHAIN_BUDGET_MESSAGE = "the chain's total budget ran out before this model was given a fair turn";
|
|
1248
|
+
|
|
1243
1249
|
// src/agent/structured.ts
|
|
1244
1250
|
function valueAt(args, path) {
|
|
1245
1251
|
let cur = args;
|
|
@@ -1255,6 +1261,11 @@ function whatWasWrong(issues, args) {
|
|
|
1255
1261
|
const got = valueAt(args, i.path);
|
|
1256
1262
|
const shown = got === void 0 ? "nothing" : JSON.stringify(got);
|
|
1257
1263
|
const head = where ? `${where}: ${i.message}` : i.message;
|
|
1264
|
+
if (got === void 0 && /received\s+(undefined|null|nothing)/i.test(i.message)) {
|
|
1265
|
+
const parent = i.path.length > 1 ? valueAt(args, i.path.slice(0, -1)) : args;
|
|
1266
|
+
const sent = parent && typeof parent === "object" ? Object.keys(parent) : [];
|
|
1267
|
+
return sent.length ? `${head} \u2014 you sent only ${sent.map((k) => `\`${k}\``).join(", ")}` : head;
|
|
1268
|
+
}
|
|
1258
1269
|
return `${head} \u2014 got ${shown.length > 120 ? `${shown.slice(0, 120)}\u2026` : shown}`;
|
|
1259
1270
|
}).join("; ");
|
|
1260
1271
|
}
|
|
@@ -1323,7 +1334,7 @@ async function runStructuredRole(opts, schema, maxAttempts = 2) {
|
|
|
1323
1334
|
}
|
|
1324
1335
|
if (ev.type === "abort") {
|
|
1325
1336
|
if (opts.signal.aborted) throw new Error("cancelled");
|
|
1326
|
-
errored = total?.aborted ?
|
|
1337
|
+
errored = total?.aborted ? CHAIN_BUDGET_MESSAGE : DEADLINE_MESSAGE;
|
|
1327
1338
|
break;
|
|
1328
1339
|
}
|
|
1329
1340
|
if (ev.type === "message.done") lastText = ev.message.content ?? lastText;
|
|
@@ -1356,13 +1367,12 @@ async function runStructuredRole(opts, schema, maxAttempts = 2) {
|
|
|
1356
1367
|
}
|
|
1357
1368
|
|
|
1358
1369
|
export {
|
|
1359
|
-
sanitizeForJson,
|
|
1360
|
-
truncateSafe,
|
|
1361
1370
|
fmtTokens,
|
|
1362
1371
|
fmtDuration,
|
|
1363
1372
|
relTime,
|
|
1364
1373
|
stripThinking,
|
|
1365
1374
|
handedOver,
|
|
1375
|
+
truncateSafe,
|
|
1366
1376
|
Telemetry,
|
|
1367
1377
|
sampleMemory,
|
|
1368
1378
|
estimateFreezeSeconds,
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CLI_KINDS
|
|
3
|
+
} from "./chunk-G45RWL7S.js";
|
|
1
4
|
import {
|
|
2
5
|
arrayField,
|
|
3
6
|
objectField,
|
|
@@ -18,6 +21,7 @@ var DEFAULT_CONFIG = {
|
|
|
18
21
|
specKit: { version: "v0.13.2" },
|
|
19
22
|
mcp: {},
|
|
20
23
|
modelSources: [],
|
|
24
|
+
accounts: [],
|
|
21
25
|
traceDir: "",
|
|
22
26
|
/**
|
|
23
27
|
* Shipped as a REFERENCE, not a copy, for the reasons skills/README.md gives for exactly this shape: it is
|
|
@@ -60,6 +64,33 @@ var fileSchema = z.object({
|
|
|
60
64
|
council: z.object({ members: z.array(reviewerSchema) }).optional(),
|
|
61
65
|
specKit: z.object({ version: z.string() }).optional(),
|
|
62
66
|
modelSources: z.array(z.string()).optional(),
|
|
67
|
+
/**
|
|
68
|
+
* Logged-in profile directories, in spill order. A path each, never a credential.
|
|
69
|
+
*
|
|
70
|
+
* Two things here were wrong in a way that could not be seen from this file, and both were found by
|
|
71
|
+
* connecting a real account.
|
|
72
|
+
*
|
|
73
|
+
* The kinds were spelled out — `["claude", "codex"]` — and every entry of a kind added since was
|
|
74
|
+
* rejected. `CLI_KINDS` is where they are declared, so this reads them rather than repeating them, and a
|
|
75
|
+
* fifth subscription cannot be half-added again.
|
|
76
|
+
*
|
|
77
|
+
* `configDir` was REQUIRED, and the signed-in default is precisely the entry that has none: `withAmbient`
|
|
78
|
+
* records it without a directory, deliberately, because Claude Code keeps that session in the Keychain
|
|
79
|
+
* and naming a directory switches it to file credentials. So the one entry written to stop a second
|
|
80
|
+
* account quietly retiring the first was itself unloadable.
|
|
81
|
+
*
|
|
82
|
+
* `.catch([])` bounds what a bad row can cost. The loader reads `parsed.success ? parsed.data : {}`, so
|
|
83
|
+
* a single rejected entry did not merely drop that account — it discarded the WHOLE global config.
|
|
84
|
+
* Measured on a live one: the file held 64 role chains and an API key, and with one z.ai entry present
|
|
85
|
+
* `loadConfig` returned zero roles and no key, silently, for every session since it was connected.
|
|
86
|
+
*/
|
|
87
|
+
accounts: z.array(z.object({
|
|
88
|
+
kind: z.enum(CLI_KINDS),
|
|
89
|
+
name: z.string(),
|
|
90
|
+
configDir: z.string().optional(),
|
|
91
|
+
email: z.string().optional(),
|
|
92
|
+
plan: z.string().optional()
|
|
93
|
+
})).catch([]).optional(),
|
|
63
94
|
traceDir: z.string().optional(),
|
|
64
95
|
// where /graph trace writes; empty = .horsecode/traces
|
|
65
96
|
mainBranch: z.string().optional(),
|
|
@@ -103,6 +134,7 @@ function loadConfig(opts) {
|
|
|
103
134
|
merged.roles = { ...global.roles ?? {}, ...projectSafe.roles ?? {} };
|
|
104
135
|
merged.mcp = { ...global.mcp ?? {}, ...projectSafe.mcp ?? {} };
|
|
105
136
|
merged.modelSources = projectSafe.modelSources ?? global.modelSources ?? [];
|
|
137
|
+
merged.accounts = global.accounts ?? [];
|
|
106
138
|
merged.maxParallel = projectSafe.maxParallel ?? global.maxParallel ?? DEFAULT_CONFIG.maxParallel;
|
|
107
139
|
merged.telemetry = projectSafe.telemetry ?? global.telemetry ?? DEFAULT_CONFIG.telemetry;
|
|
108
140
|
const spoken = global.skillSources !== void 0 || projectSafe.skillSources !== void 0;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
// src/agents/cli-auth.ts
|
|
2
|
+
import { spawnSync } from "child_process";
|
|
3
|
+
|
|
4
|
+
// src/agents/cli-agent.ts
|
|
5
|
+
import { spawn } from "child_process";
|
|
6
|
+
var CLI_KINDS = ["claude", "codex", "grok", "zai"];
|
|
7
|
+
function cliBinary(kind) {
|
|
8
|
+
return kind === "zai" ? "claude" : kind;
|
|
9
|
+
}
|
|
10
|
+
var CLI_ERROR_UNSPOKEN = "the CLI reported an error";
|
|
11
|
+
function cliArgs(kind, prompt, extra = []) {
|
|
12
|
+
if (kind === "claude" || kind === "zai") {
|
|
13
|
+
return ["--output-format", "stream-json", "--verbose", ...extra, "-p", "--", prompt];
|
|
14
|
+
}
|
|
15
|
+
if (kind === "codex") {
|
|
16
|
+
return ["exec", "--json", "--skip-git-repo-check", ...extra, "--", prompt];
|
|
17
|
+
}
|
|
18
|
+
return ["--output-format", "streaming-messages-json", ...extra, `--single=${prompt}`];
|
|
19
|
+
}
|
|
20
|
+
function decodeClaudeEvent(line) {
|
|
21
|
+
let e;
|
|
22
|
+
try {
|
|
23
|
+
e = JSON.parse(line);
|
|
24
|
+
} catch {
|
|
25
|
+
return void 0;
|
|
26
|
+
}
|
|
27
|
+
const type = e.type;
|
|
28
|
+
if (type === "rate_limit_event") {
|
|
29
|
+
const info = e.rate_limit_info ?? {};
|
|
30
|
+
const status = String(info.status ?? "unknown");
|
|
31
|
+
const raw = info.unifiedWindows ?? {};
|
|
32
|
+
const windows = {};
|
|
33
|
+
for (const [name, w] of Object.entries(raw)) windows[name] = w?.utilization ?? 0;
|
|
34
|
+
const quota = {
|
|
35
|
+
status,
|
|
36
|
+
windows,
|
|
37
|
+
...typeof info.resetsAt === "number" ? { resetsAt: info.resetsAt } : {}
|
|
38
|
+
};
|
|
39
|
+
return status.startsWith("allowed") ? { quota } : {
|
|
40
|
+
quota,
|
|
41
|
+
/**
|
|
42
|
+
* The reset time rides along, as an ISO instant rather than prose.
|
|
43
|
+
*
|
|
44
|
+
* A spent five-hour window reopens; without saying when, the only safe bench is "the rest of the
|
|
45
|
+
* run", which on a ten-hour board writes off a subscription for hours after it recovered. The
|
|
46
|
+
* gateway's wordings said "reset after 4h" and nothing ever parsed them — see `quotaResetAt`.
|
|
47
|
+
*/
|
|
48
|
+
rateLimited: `${status} \u2014 ${describeWindows(windows)}` + (quota.resetsAt ? ` (resets ${new Date(quota.resetsAt * 1e3).toISOString()})` : "")
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (type === "assistant") {
|
|
52
|
+
const msg = e.message;
|
|
53
|
+
const parts = Array.isArray(msg?.content) ? msg.content : [];
|
|
54
|
+
const text = parts.filter((b) => typeof b === "object" && b !== null && b.type === "text").map((b) => b.text).join("");
|
|
55
|
+
const tool = parts.find((b) => typeof b === "object" && b !== null && b.type === "tool_use");
|
|
56
|
+
return {
|
|
57
|
+
...text ? { text } : {},
|
|
58
|
+
...msg?.model ? { served: msg.model } : {},
|
|
59
|
+
...tool ? { tool: { name: tool.name, ...targetOf(tool.input) ? { target: targetOf(tool.input) } : {} } } : {}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (type === "user") {
|
|
63
|
+
const parts = e.message?.content;
|
|
64
|
+
const failed = (Array.isArray(parts) ? parts : []).find(
|
|
65
|
+
(b) => typeof b === "object" && b !== null && b.type === "tool_result" && b.is_error === true
|
|
66
|
+
);
|
|
67
|
+
return failed ? { tool: { name: "tool", ok: false } } : void 0;
|
|
68
|
+
}
|
|
69
|
+
if (type === "result") {
|
|
70
|
+
const u = e.usage ?? {};
|
|
71
|
+
const cost = e.total_cost_usd;
|
|
72
|
+
return {
|
|
73
|
+
usage: {
|
|
74
|
+
freshTokens: u.input_tokens ?? 0,
|
|
75
|
+
cachedTokens: u.cache_read_input_tokens ?? 0,
|
|
76
|
+
cacheWriteTokens: u.cache_creation_input_tokens ?? 0,
|
|
77
|
+
outputTokens: u.output_tokens ?? 0,
|
|
78
|
+
...cost !== void 0 ? { costUsd: cost } : {}
|
|
79
|
+
},
|
|
80
|
+
...e.subtype === "error_during_execution" ? { error: String(e.result ?? CLI_ERROR_UNSPOKEN) } : {}
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return void 0;
|
|
84
|
+
}
|
|
85
|
+
function decodeCodexEvent(line) {
|
|
86
|
+
let e;
|
|
87
|
+
try {
|
|
88
|
+
e = JSON.parse(line);
|
|
89
|
+
} catch {
|
|
90
|
+
return void 0;
|
|
91
|
+
}
|
|
92
|
+
const type = String(e.type ?? "");
|
|
93
|
+
if (/rate.?limit/i.test(type)) return { rateLimited: String(e.message ?? "rate limited by the CLI") };
|
|
94
|
+
if (type === "item.completed") {
|
|
95
|
+
const item = e.item;
|
|
96
|
+
if (item?.type === "agent_message" && item.text) return { text: item.text };
|
|
97
|
+
if (item?.type && item.type !== "agent_message") {
|
|
98
|
+
const changes = item.changes;
|
|
99
|
+
const first = Array.isArray(changes) ? changes.find((c) => typeof c?.path === "string")?.path : void 0;
|
|
100
|
+
const more = Array.isArray(changes) && changes.length > 1 ? ` +${changes.length - 1}` : "";
|
|
101
|
+
return {
|
|
102
|
+
tool: {
|
|
103
|
+
name: item.name ?? item.type,
|
|
104
|
+
...first ? { target: `${first}${more}` } : {}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
if (type === "turn.completed") {
|
|
111
|
+
const u = e.usage ?? {};
|
|
112
|
+
return {
|
|
113
|
+
usage: {
|
|
114
|
+
freshTokens: u.input_tokens ?? 0,
|
|
115
|
+
cachedTokens: u.cached_input_tokens ?? 0,
|
|
116
|
+
cacheWriteTokens: u.cache_write_input_tokens ?? 0,
|
|
117
|
+
outputTokens: u.output_tokens ?? 0
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (type === "turn.failed" || type === "error") {
|
|
122
|
+
return { error: String(e.message ?? "codex reported an error") };
|
|
123
|
+
}
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
function describeWindows(windows) {
|
|
127
|
+
const parts = Object.entries(windows).map(([k, v]) => `${k} ${Math.round(v * 100)}%`);
|
|
128
|
+
return parts.length ? parts.join(", ") : "no window reported";
|
|
129
|
+
}
|
|
130
|
+
var SYNTHETIC = "<synthetic>";
|
|
131
|
+
function targetOf(input) {
|
|
132
|
+
for (const k of ["file_path", "path", "filePath", "notebook_path"]) {
|
|
133
|
+
const v = input?.[k];
|
|
134
|
+
if (typeof v === "string" && v) return v;
|
|
135
|
+
}
|
|
136
|
+
return void 0;
|
|
137
|
+
}
|
|
138
|
+
function makeStreamReader(decode, onEvent) {
|
|
139
|
+
let pending = "";
|
|
140
|
+
const drain = (upToNewline) => {
|
|
141
|
+
const lines = pending.split("\n");
|
|
142
|
+
pending = upToNewline ? lines.pop() ?? "" : "";
|
|
143
|
+
for (const line of lines) {
|
|
144
|
+
if (!line.trim()) continue;
|
|
145
|
+
const ev = decode(line);
|
|
146
|
+
if (ev) onEvent(ev);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
return {
|
|
150
|
+
push(chunk) {
|
|
151
|
+
pending += chunk;
|
|
152
|
+
drain(true);
|
|
153
|
+
},
|
|
154
|
+
end() {
|
|
155
|
+
if (pending.trim()) drain(false);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function reportedError(decoded, stderr, exitCode) {
|
|
160
|
+
if (decoded && decoded !== CLI_ERROR_UNSPOKEN) return decoded;
|
|
161
|
+
const spoken = exitCode !== 0 ? stderr.trim().slice(0, 500) : "";
|
|
162
|
+
return spoken || decoded || void 0;
|
|
163
|
+
}
|
|
164
|
+
async function runCliAgent(run) {
|
|
165
|
+
const decode = run.kind === "codex" ? decodeCodexEvent : decodeClaudeEvent;
|
|
166
|
+
const args = cliArgs(run.kind, run.prompt, run.args ?? []);
|
|
167
|
+
return new Promise((resolve) => {
|
|
168
|
+
let child;
|
|
169
|
+
try {
|
|
170
|
+
child = spawn(cliBinary(run.kind), args, {
|
|
171
|
+
cwd: run.cwd,
|
|
172
|
+
signal: run.signal,
|
|
173
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
174
|
+
...run.configDir ? { env: { ...process.env, ...profileEnv(run.kind, run.configDir) } } : {}
|
|
175
|
+
});
|
|
176
|
+
} catch (e) {
|
|
177
|
+
resolve({ text: "", error: e instanceof Error ? e.message : String(e), exitCode: -1 });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
let text = "";
|
|
181
|
+
let usage;
|
|
182
|
+
let rateLimited;
|
|
183
|
+
let served;
|
|
184
|
+
let quota;
|
|
185
|
+
let error;
|
|
186
|
+
let stderr = "";
|
|
187
|
+
const reader = makeStreamReader(decode, (ev) => {
|
|
188
|
+
if (ev.text) text += ev.text;
|
|
189
|
+
if (ev.usage) usage = ev.usage;
|
|
190
|
+
if (ev.rateLimited) rateLimited = ev.rateLimited;
|
|
191
|
+
if (ev.served) served = ev.served;
|
|
192
|
+
if (ev.quota) quota = ev.quota;
|
|
193
|
+
if (ev.error) error = ev.error;
|
|
194
|
+
run.onEvent?.(ev);
|
|
195
|
+
});
|
|
196
|
+
child.stdout?.on("data", (d) => reader.push(d.toString()));
|
|
197
|
+
child.stderr?.on("data", (d) => {
|
|
198
|
+
stderr += d.toString();
|
|
199
|
+
});
|
|
200
|
+
child.on("error", (e) => resolve({ text, ...usage ? { usage } : {}, error: e.message, exitCode: -1 }));
|
|
201
|
+
child.on("close", (code) => {
|
|
202
|
+
reader.end();
|
|
203
|
+
const reported = reportedError(error, stderr, code ?? -1);
|
|
204
|
+
resolve({
|
|
205
|
+
text,
|
|
206
|
+
...usage ? { usage } : {},
|
|
207
|
+
...rateLimited ? { rateLimited } : {},
|
|
208
|
+
...quota ? { quota } : {},
|
|
209
|
+
...served ? { served } : {},
|
|
210
|
+
...reported ? { error: reported } : {},
|
|
211
|
+
exitCode: code ?? -1
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// src/agents/cli-auth.ts
|
|
218
|
+
function profileEnv(kind, configDir) {
|
|
219
|
+
if (!configDir) return {};
|
|
220
|
+
if (kind === "claude" || kind === "zai") return { CLAUDE_CONFIG_DIR: configDir };
|
|
221
|
+
if (kind === "codex") return { CODEX_HOME: configDir };
|
|
222
|
+
return { GROK_HOME: configDir };
|
|
223
|
+
}
|
|
224
|
+
function readAuthStatus(kind, out) {
|
|
225
|
+
if (kind === "grok") {
|
|
226
|
+
const m = /you are logged in with\s+(.+)/i.exec(out);
|
|
227
|
+
if (!m) return { loggedIn: false };
|
|
228
|
+
const plan = m[1].trim().replace(/\.$/, "");
|
|
229
|
+
return { loggedIn: true, ...plan ? { plan } : {} };
|
|
230
|
+
}
|
|
231
|
+
if (kind === "codex") {
|
|
232
|
+
const m = /logged in(?: using (.+))?/i.exec(out);
|
|
233
|
+
if (!m || /not logged in/i.test(out)) return { loggedIn: false };
|
|
234
|
+
const plan = m[1]?.trim();
|
|
235
|
+
return { loggedIn: true, ...plan ? { plan } : {} };
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const j = JSON.parse(out);
|
|
239
|
+
if (!j.loggedIn) return { loggedIn: false };
|
|
240
|
+
return {
|
|
241
|
+
loggedIn: true,
|
|
242
|
+
...j.email ? { email: j.email } : {},
|
|
243
|
+
...j.subscriptionType ? { plan: j.subscriptionType } : {}
|
|
244
|
+
};
|
|
245
|
+
} catch {
|
|
246
|
+
return { loggedIn: false };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function statusArgs(kind) {
|
|
250
|
+
if (kind === "claude" || kind === "zai") return ["auth", "status"];
|
|
251
|
+
if (kind === "codex") return ["login", "status"];
|
|
252
|
+
return ["models"];
|
|
253
|
+
}
|
|
254
|
+
function loginArgs(kind) {
|
|
255
|
+
return kind === "claude" ? ["auth", "login"] : ["login"];
|
|
256
|
+
}
|
|
257
|
+
function checkProfile(kind, configDir) {
|
|
258
|
+
if (kind === "zai" && !configDir) return { loggedIn: false };
|
|
259
|
+
const r = spawnSync(cliBinary(kind), statusArgs(kind), {
|
|
260
|
+
env: { ...process.env, ...profileEnv(kind, configDir) },
|
|
261
|
+
encoding: "utf8",
|
|
262
|
+
// A status check that hangs must not hang the startup summary with it.
|
|
263
|
+
timeout: 2e4
|
|
264
|
+
});
|
|
265
|
+
if (r.error) return { loggedIn: false };
|
|
266
|
+
return readAuthStatus(kind, `${r.stdout ?? ""}${r.stderr ?? ""}`);
|
|
267
|
+
}
|
|
268
|
+
function runLogin(kind, configDir) {
|
|
269
|
+
if (kind === "zai") {
|
|
270
|
+
return { ok: false, error: "z.ai has no sign-in \u2014 a profile is connected by writing its settings file" };
|
|
271
|
+
}
|
|
272
|
+
const r = spawnSync(cliBinary(kind), loginArgs(kind), {
|
|
273
|
+
env: { ...process.env, ...profileEnv(kind, configDir) },
|
|
274
|
+
stdio: "inherit"
|
|
275
|
+
});
|
|
276
|
+
if (r.error) {
|
|
277
|
+
const e = r.error;
|
|
278
|
+
return e.code === "ENOENT" ? { ok: false, error: `\`${kind}\` is not installed, or not on PATH` } : { ok: false, error: e.message };
|
|
279
|
+
}
|
|
280
|
+
return { ok: r.status === 0 };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export {
|
|
284
|
+
checkProfile,
|
|
285
|
+
runLogin,
|
|
286
|
+
CLI_KINDS,
|
|
287
|
+
SYNTHETIC,
|
|
288
|
+
runCliAgent
|
|
289
|
+
};
|
|
@@ -2,21 +2,21 @@ import {
|
|
|
2
2
|
isMigrated,
|
|
3
3
|
loadMigratedSync,
|
|
4
4
|
migratedNotice
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-63E73TGI.js";
|
|
6
6
|
import {
|
|
7
7
|
telemetry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-AE36LLL2.js";
|
|
9
9
|
import {
|
|
10
10
|
readBriefSync
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-6OSEQOYY.js";
|
|
12
12
|
import {
|
|
13
13
|
everTraceable,
|
|
14
14
|
readTraceSync
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-KAGKX2YT.js";
|
|
16
16
|
import {
|
|
17
17
|
areaOf,
|
|
18
18
|
loadGraphSync
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-XEGQT5EN.js";
|
|
20
20
|
|
|
21
21
|
// src/tools/read.ts
|
|
22
22
|
import { readFile } from "fs/promises";
|
|
@@ -948,9 +948,20 @@ ${core.join("\n")}`;
|
|
|
948
948
|
);
|
|
949
949
|
var graphTraceTool = {
|
|
950
950
|
name: "graph_trace",
|
|
951
|
-
|
|
951
|
+
/**
|
|
952
|
+
* The limit is stated HERE because this is the only place an agent reads before calling.
|
|
953
|
+
*
|
|
954
|
+
* Measured live in one run: four different lenses — `risk-judge`, `plan-observability`,
|
|
955
|
+
* `plan-architecture` and one more — each asked for a trace of `spec.md` or `plan.md`. The error they got
|
|
956
|
+
* back is a good one and says the failure is permanent, so none of them asked twice; but each learned it
|
|
957
|
+
* separately, at a turn apiece, because a lens has its own memo and cannot be told by the last one. An
|
|
958
|
+
* error teaches one agent after the fact. A description tells every agent before.
|
|
959
|
+
*/
|
|
960
|
+
description: `What a source file is responsible for and what to be careful of when changing it, in the product's terms. Far cheaper than reading the file. Use it to orient before opening unfamiliar code. SOURCE CODE ONLY (.ts, .cs, .py, .go, \u2026): a .md, .json or .txt path has no trace and never will \u2014 read documents with read_file. Pass "project" instead of a path to get the project brief: what the product is, its domain vocabulary, and the business rules the code must not violate. Read that FIRST in an unfamiliar codebase.`,
|
|
952
961
|
permissionLevel: "safe",
|
|
953
|
-
parameters: z4.object({
|
|
962
|
+
parameters: z4.object({
|
|
963
|
+
file: z4.string().describe('Repo-relative path to SOURCE code, e.g. "src/config/config.ts". Not a document.')
|
|
964
|
+
}),
|
|
954
965
|
describe: (args) => ({ allowKey: "graph:trace", preview: `graph_trace ${JSON.stringify(args)}`.slice(0, 120) }),
|
|
955
966
|
async run(args, ctx) {
|
|
956
967
|
const file = String(args.file ?? "");
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
memoryHints,
|
|
6
6
|
readFileTool,
|
|
7
7
|
reinforceUsed
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LLL7QWXB.js";
|
|
9
9
|
import {
|
|
10
10
|
ToolRegistry,
|
|
11
11
|
runStructuredRole
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-AE36LLL2.js";
|
|
13
13
|
|
|
14
14
|
// src/engine/triage.ts
|
|
15
15
|
import { z } from "zod";
|
|
@@ -15,7 +15,18 @@ var defaultGitRunner = (args, cwd) => new Promise((resolve) => {
|
|
|
15
15
|
child.on("error", (e) => resolve({ stdout, stderr: stderr + e.message, code: -1 }));
|
|
16
16
|
child.on("close", (code) => resolve({ stdout, stderr, code: code ?? -1 }));
|
|
17
17
|
});
|
|
18
|
+
var TAKES_A_VALUE = /* @__PURE__ */ new Set(["-c", "-C", "--git-dir", "--work-tree", "--namespace", "--exec-path", "--config-env"]);
|
|
19
|
+
function gitVerb(args) {
|
|
20
|
+
for (let i = 0; i < args.length; i++) {
|
|
21
|
+
const a = args[i];
|
|
22
|
+
if (a === void 0) continue;
|
|
23
|
+
if (!a.startsWith("-")) return a;
|
|
24
|
+
if (TAKES_A_VALUE.has(a)) i++;
|
|
25
|
+
}
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
18
28
|
|
|
19
29
|
export {
|
|
20
|
-
defaultGitRunner
|
|
30
|
+
defaultGitRunner,
|
|
31
|
+
gitVerb
|
|
21
32
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/engine/language.ts
|
|
2
|
+
function respondIn(language) {
|
|
3
|
+
if (!language || /^english$/i.test(language)) return "";
|
|
4
|
+
return `
|
|
5
|
+
|
|
6
|
+
Respond to the user in ${language}: everything you say to them, and every question you ask, is in ${language}. Code, identifiers, logs and commit messages keep whatever language the project uses.`;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
respondIn
|
|
11
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
defaultGitRunner
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LPQU436C.js";
|
|
4
4
|
import {
|
|
5
5
|
briefForPrompt,
|
|
6
6
|
briefPrompt,
|
|
7
7
|
briefStatus,
|
|
8
8
|
gatherBriefInput,
|
|
9
9
|
saveBrief
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-6OSEQOYY.js";
|
|
11
11
|
import {
|
|
12
12
|
ensureGitignore,
|
|
13
13
|
loadTraceIndex,
|
|
@@ -19,10 +19,10 @@ import {
|
|
|
19
19
|
tracePrompt,
|
|
20
20
|
traceRootRel,
|
|
21
21
|
traceable
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-KAGKX2YT.js";
|
|
23
23
|
import {
|
|
24
24
|
loadGraph
|
|
25
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-XEGQT5EN.js";
|
|
26
26
|
|
|
27
27
|
// src/engine/trace-run.ts
|
|
28
28
|
async function traceableFiles(cwd, opts) {
|