@dmrlzz/davecode 0.6.0 → 0.7.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/{ChatApp-RQZ2IZFQ.js → ChatApp-C23NVTME.js} +6 -6
- package/dist/{account-FY6THFLW.js → account-GG7UFSNY.js} +3 -3
- package/dist/{accounts-ZV3KMLC5.js → accounts-IPOGUMHF.js} +5 -5
- package/dist/{budget-OUOEMMOM.js → budget-7QJGPYPF.js} +4 -4
- package/dist/{chat-OHVP4BS3.js → chat-3K2KXS3E.js} +8 -8
- package/dist/{chunk-FFB2QA5I.js → chunk-3BTE76UD.js} +2 -2
- package/dist/{chunk-L7NEDOA3.js → chunk-4FE2YV5U.js} +2 -2
- package/dist/{chunk-TP3ORSHY.js → chunk-FKGZERYB.js} +157 -9
- package/dist/chunk-FKGZERYB.js.map +1 -0
- package/dist/{chunk-2MM5WT5J.js → chunk-JBX6J74G.js} +3 -3
- package/dist/{chunk-2P5DJWIJ.js → chunk-KEURMO75.js} +4 -4
- package/dist/{chunk-NKRI6SYR.js → chunk-LK2K73DC.js} +2 -2
- package/dist/{chunk-2XGDILCW.js → chunk-NBQIMB43.js} +2422 -1599
- package/dist/chunk-NBQIMB43.js.map +1 -0
- package/dist/{chunk-HL4RIXPM.js → chunk-PGZPSJS7.js} +3 -3
- package/dist/{chunk-U5CI774J.js → chunk-U42YONUN.js} +3 -3
- package/dist/{config-ZLO3YOUX.js → config-BFEQFGAR.js} +3 -3
- package/dist/dashboard/assets/Accounts-CUMFzGls.js +1 -0
- package/dist/dashboard/assets/{Callout-CqdcH6dC.js → Callout-DDtTG_BA.js} +1 -1
- package/dist/dashboard/assets/Goals-CBGiTkcP.js +1 -0
- package/dist/dashboard/assets/{Library-DnCIb76j.js → Library-CKWa3h0E.js} +1 -1
- package/dist/dashboard/assets/{Panes-DBqKYuaE.js → Panes-ziAz-CZa.js} +4 -4
- package/dist/dashboard/assets/RoutesPage-D2iW-KhM.js +4 -0
- package/dist/dashboard/assets/Runner-4e79FB1J.js +1 -0
- package/dist/dashboard/assets/Settings-CSae84S0.js +1 -0
- package/dist/dashboard/assets/Tasks-CnbZ5PvX.js +2 -0
- package/dist/dashboard/assets/Traffic-B12xKOUm.js +1 -0
- package/dist/dashboard/assets/Workspace-CMPHlw4f.js +1 -0
- package/dist/dashboard/assets/chevron-down-DT349Fth.js +1 -0
- package/dist/dashboard/assets/external-link-DCIUScea.js +1 -0
- package/dist/dashboard/assets/format-C7Q-B9fI.js +112 -0
- package/dist/dashboard/assets/index-4RIBvhr9.css +2 -0
- package/dist/dashboard/assets/index-z8TlFwRe.js +50 -0
- package/dist/dashboard/assets/{task-errors-DK4-900l.js → task-errors-3F3vbQ7k.js} +1 -1
- package/dist/dashboard/assets/x-UBQsC1rW.js +1 -0
- package/dist/dashboard/index.html +4 -3
- package/dist/{dist-56V3DLJ4.js → dist-VTC5E7Z5.js} +34 -2
- package/dist/{doctor-DUOKUSJL.js → doctor-JLFYQ3EP.js} +3 -3
- package/dist/{goal-WG37XHUY.js → goal-JM7QYTL2.js} +4 -4
- package/dist/index.js +32 -32
- package/dist/{init-WW4XPFMM.js → init-6K4EGMTV.js} +3 -3
- package/dist/{orchestrate-7YQDBS3E.js → orchestrate-QC5P6IUZ.js} +4 -4
- package/dist/{run-QB4SGPEC.js → run-ABQNQ722.js} +3 -3
- package/dist/runtime.js +2 -2
- package/dist/{start-U4RL377T.js → start-MSO7TGBX.js} +3 -3
- package/dist/{status-GHVMGZI5.js → status-U6C4LUCM.js} +4 -4
- package/dist/{tasks-VTBJ4B2G.js → tasks-HEVT67YM.js} +4 -4
- package/package.json +4 -4
- package/dist/chunk-2XGDILCW.js.map +0 -1
- package/dist/chunk-TP3ORSHY.js.map +0 -1
- package/dist/dashboard/assets/Accounts-CIhOt9FH.js +0 -1
- package/dist/dashboard/assets/Goals-CITP2GqG.js +0 -1
- package/dist/dashboard/assets/RoutesPage-BNVmKhyU.js +0 -4
- package/dist/dashboard/assets/Runner-KR35ZWUE.js +0 -1
- package/dist/dashboard/assets/Settings-B5qE-a5O.js +0 -1
- package/dist/dashboard/assets/Tasks-bJLfNJmS.js +0 -2
- package/dist/dashboard/assets/Traffic-CGfilrRx.js +0 -1
- package/dist/dashboard/assets/Workspace-Dc3cvct_.js +0 -1
- package/dist/dashboard/assets/format-328qIBHX.js +0 -111
- package/dist/dashboard/assets/index-C-bwzlsl.js +0 -50
- package/dist/dashboard/assets/index-D9FCfPmM.css +0 -2
- /package/dist/{ChatApp-RQZ2IZFQ.js.map → ChatApp-C23NVTME.js.map} +0 -0
- /package/dist/{account-FY6THFLW.js.map → account-GG7UFSNY.js.map} +0 -0
- /package/dist/{accounts-ZV3KMLC5.js.map → accounts-IPOGUMHF.js.map} +0 -0
- /package/dist/{budget-OUOEMMOM.js.map → budget-7QJGPYPF.js.map} +0 -0
- /package/dist/{chat-OHVP4BS3.js.map → chat-3K2KXS3E.js.map} +0 -0
- /package/dist/{chunk-FFB2QA5I.js.map → chunk-3BTE76UD.js.map} +0 -0
- /package/dist/{chunk-L7NEDOA3.js.map → chunk-4FE2YV5U.js.map} +0 -0
- /package/dist/{chunk-2MM5WT5J.js.map → chunk-JBX6J74G.js.map} +0 -0
- /package/dist/{chunk-2P5DJWIJ.js.map → chunk-KEURMO75.js.map} +0 -0
- /package/dist/{chunk-NKRI6SYR.js.map → chunk-LK2K73DC.js.map} +0 -0
- /package/dist/{chunk-HL4RIXPM.js.map → chunk-PGZPSJS7.js.map} +0 -0
- /package/dist/{chunk-U5CI774J.js.map → chunk-U42YONUN.js.map} +0 -0
- /package/dist/{config-ZLO3YOUX.js.map → config-BFEQFGAR.js.map} +0 -0
- /package/dist/{dist-56V3DLJ4.js.map → dist-VTC5E7Z5.js.map} +0 -0
- /package/dist/{doctor-DUOKUSJL.js.map → doctor-JLFYQ3EP.js.map} +0 -0
- /package/dist/{goal-WG37XHUY.js.map → goal-JM7QYTL2.js.map} +0 -0
- /package/dist/{init-WW4XPFMM.js.map → init-6K4EGMTV.js.map} +0 -0
- /package/dist/{orchestrate-7YQDBS3E.js.map → orchestrate-QC5P6IUZ.js.map} +0 -0
- /package/dist/{run-QB4SGPEC.js.map → run-ABQNQ722.js.map} +0 -0
- /package/dist/{start-U4RL377T.js.map → start-MSO7TGBX.js.map} +0 -0
- /package/dist/{status-GHVMGZI5.js.map → status-U6C4LUCM.js.map} +0 -0
- /package/dist/{tasks-VTBJ4B2G.js.map → tasks-HEVT67YM.js.map} +0 -0
|
@@ -120,21 +120,37 @@ import { mkdirSync as mkdirSync7, readFileSync as readFileSync3, rmSync as rmSyn
|
|
|
120
120
|
import { dirname as dirname6, join as join6 } from "path";
|
|
121
121
|
import { z as z9 } from "zod";
|
|
122
122
|
import { z as z10 } from "zod";
|
|
123
|
-
import {
|
|
124
|
-
import {
|
|
125
|
-
import {
|
|
126
|
-
|
|
123
|
+
import { execFile as execFile2 } from "child_process";
|
|
124
|
+
import { createHash as createHash3, randomBytes as randomBytes9 } from "crypto";
|
|
125
|
+
import {
|
|
126
|
+
existsSync as existsSync5,
|
|
127
|
+
mkdirSync as mkdirSync8,
|
|
128
|
+
readdirSync,
|
|
129
|
+
readFileSync as readFileSync4,
|
|
130
|
+
realpathSync as realpathSync2,
|
|
131
|
+
rmSync as rmSync5,
|
|
132
|
+
statSync as statSync4,
|
|
133
|
+
writeFileSync as writeFileSync4
|
|
134
|
+
} from "fs";
|
|
135
|
+
import path9 from "path";
|
|
136
|
+
import { z as z13 } from "zod";
|
|
127
137
|
import { existsSync as existsSync4, mkdtempSync, realpathSync, rmSync as rmSync4, statSync as statSync3 } from "fs";
|
|
128
138
|
import { tmpdir } from "os";
|
|
129
139
|
import path8 from "path";
|
|
130
|
-
import {
|
|
131
|
-
import { z as
|
|
132
|
-
import { createHash as createHash3 } from "crypto";
|
|
133
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
134
|
-
import { join as join8, resolve as resolve5 } from "path";
|
|
140
|
+
import { z as z11 } from "zod";
|
|
141
|
+
import { z as z12 } from "zod";
|
|
135
142
|
import { z as z14 } from "zod";
|
|
136
|
-
import {
|
|
137
|
-
import
|
|
143
|
+
import { readdir as readdir3 } from "fs/promises";
|
|
144
|
+
import { join as join7, relative as relative2 } from "path";
|
|
145
|
+
import { z as z15 } from "zod";
|
|
146
|
+
import { randomBytes as randomBytes10 } from "crypto";
|
|
147
|
+
import { z as z16 } from "zod";
|
|
148
|
+
import { createHash as createHash4 } from "crypto";
|
|
149
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
150
|
+
import { join as join8, resolve as resolve5 } from "path";
|
|
151
|
+
import { z as z17 } from "zod";
|
|
152
|
+
import { existsSync as existsSync6, realpathSync as realpathSync3, rmSync as rmSync6 } from "fs";
|
|
153
|
+
import path10 from "path";
|
|
138
154
|
var credentials = z.object({ clientId: z.string().min(1), clientSecret: z.string().min(1) });
|
|
139
155
|
var optionsSchema = z.object({
|
|
140
156
|
origin: z.string().url().refine((value) => {
|
|
@@ -587,14 +603,14 @@ function projectPaths(root) {
|
|
|
587
603
|
}
|
|
588
604
|
var sleep = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
589
605
|
var RETRYABLE_RENAME_CODES = /* @__PURE__ */ new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
590
|
-
async function atomicWriteFile(
|
|
591
|
-
await mkdir(dirname(
|
|
592
|
-
const tmp = `${
|
|
606
|
+
async function atomicWriteFile(path11, content) {
|
|
607
|
+
await mkdir(dirname(path11), { recursive: true });
|
|
608
|
+
const tmp = `${path11}.${process.pid}.${randomBytes2(4).toString("hex")}.tmp`;
|
|
593
609
|
try {
|
|
594
610
|
await writeFile(tmp, content, "utf8");
|
|
595
611
|
for (let attempt = 0; ; attempt++) {
|
|
596
612
|
try {
|
|
597
|
-
await rename(tmp,
|
|
613
|
+
await rename(tmp, path11);
|
|
598
614
|
return;
|
|
599
615
|
} catch (err) {
|
|
600
616
|
const code = err.code;
|
|
@@ -607,9 +623,9 @@ async function atomicWriteFile(path10, content) {
|
|
|
607
623
|
throw err;
|
|
608
624
|
}
|
|
609
625
|
}
|
|
610
|
-
async function readFileIfExists(
|
|
626
|
+
async function readFileIfExists(path11) {
|
|
611
627
|
try {
|
|
612
|
-
return await readFile(
|
|
628
|
+
return await readFile(path11, "utf8");
|
|
613
629
|
} catch (err) {
|
|
614
630
|
if (err.code === "ENOENT") return void 0;
|
|
615
631
|
throw err;
|
|
@@ -1041,9 +1057,9 @@ var LockTimeoutError = class extends Error {
|
|
|
1041
1057
|
};
|
|
1042
1058
|
var TRANSIENT_OPEN_ERRORS = ["EPERM", "EACCES", "EBUSY"];
|
|
1043
1059
|
var sleep2 = (ms) => new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
1044
|
-
async function readLockInfo(
|
|
1060
|
+
async function readLockInfo(path11) {
|
|
1045
1061
|
try {
|
|
1046
|
-
const parsed = JSON.parse(await readFile2(
|
|
1062
|
+
const parsed = JSON.parse(await readFile2(path11, "utf8"));
|
|
1047
1063
|
if (typeof parsed === "object" && parsed !== null && typeof parsed.ts === "number" && typeof parsed.pid === "number") {
|
|
1048
1064
|
return parsed;
|
|
1049
1065
|
}
|
|
@@ -1051,11 +1067,11 @@ async function readLockInfo(path10) {
|
|
|
1051
1067
|
}
|
|
1052
1068
|
return void 0;
|
|
1053
1069
|
}
|
|
1054
|
-
async function lockAge(
|
|
1055
|
-
const info = await readLockInfo(
|
|
1070
|
+
async function lockAge(path11) {
|
|
1071
|
+
const info = await readLockInfo(path11);
|
|
1056
1072
|
if (info) return Date.now() - info.ts;
|
|
1057
1073
|
try {
|
|
1058
|
-
return Date.now() - (await stat2(
|
|
1074
|
+
return Date.now() - (await stat2(path11)).mtimeMs;
|
|
1059
1075
|
} catch {
|
|
1060
1076
|
return void 0;
|
|
1061
1077
|
}
|
|
@@ -1102,9 +1118,9 @@ async function withFileLock(lockPath, fn2, opts = {}) {
|
|
|
1102
1118
|
}
|
|
1103
1119
|
}
|
|
1104
1120
|
}
|
|
1105
|
-
async function exists(
|
|
1121
|
+
async function exists(path11) {
|
|
1106
1122
|
try {
|
|
1107
|
-
await access(
|
|
1123
|
+
await access(path11);
|
|
1108
1124
|
return true;
|
|
1109
1125
|
} catch {
|
|
1110
1126
|
return false;
|
|
@@ -1163,8 +1179,8 @@ var MissingBrainError = class extends Error {
|
|
|
1163
1179
|
/** HTTP status hint and machine-readable code for the gateway's error handler. */
|
|
1164
1180
|
statusCode = 409;
|
|
1165
1181
|
code = "no_brain";
|
|
1166
|
-
constructor(
|
|
1167
|
-
super(`Missing project brain file: ${
|
|
1182
|
+
constructor(path11) {
|
|
1183
|
+
super(`Missing project brain file: ${path11} (run ProjectBrain.init first)`);
|
|
1168
1184
|
this.name = "MissingBrainError";
|
|
1169
1185
|
}
|
|
1170
1186
|
};
|
|
@@ -1202,8 +1218,8 @@ var ProjectBrain = class _ProjectBrain {
|
|
|
1202
1218
|
[brain.paths.architecture, architectureTemplate(name)],
|
|
1203
1219
|
[brain.paths.taskGraph, serializeGraph(taskGraphTemplate())]
|
|
1204
1220
|
];
|
|
1205
|
-
for (const [
|
|
1206
|
-
if (!await exists(
|
|
1221
|
+
for (const [path11, content] of files) {
|
|
1222
|
+
if (!await exists(path11)) await atomicWriteFile(path11, content);
|
|
1207
1223
|
}
|
|
1208
1224
|
return brain;
|
|
1209
1225
|
}
|
|
@@ -1315,10 +1331,10 @@ ${line}` : line);
|
|
|
1315
1331
|
this.events?.emit({ type: "task.updated", task });
|
|
1316
1332
|
return task;
|
|
1317
1333
|
}
|
|
1318
|
-
async readText(
|
|
1319
|
-
const content = await readFileIfExists(
|
|
1334
|
+
async readText(path11) {
|
|
1335
|
+
const content = await readFileIfExists(path11);
|
|
1320
1336
|
if (content === void 0) {
|
|
1321
|
-
throw new MissingBrainError(
|
|
1337
|
+
throw new MissingBrainError(path11);
|
|
1322
1338
|
}
|
|
1323
1339
|
return content;
|
|
1324
1340
|
}
|
|
@@ -1532,14 +1548,14 @@ function filterProjectLayer(layer) {
|
|
|
1532
1548
|
const out = {};
|
|
1533
1549
|
for (const [key, v] of Object.entries(value)) {
|
|
1534
1550
|
const rule = Object.hasOwn(tree, key) ? tree[key] : void 0;
|
|
1535
|
-
const
|
|
1551
|
+
const path11 = prefix ? `${prefix}.${key}` : key;
|
|
1536
1552
|
if (rule === true) {
|
|
1537
1553
|
out[key] = v;
|
|
1538
1554
|
} else if (rule && isPlainObject(v)) {
|
|
1539
|
-
const inner = walk(v, rule,
|
|
1555
|
+
const inner = walk(v, rule, path11);
|
|
1540
1556
|
if (Object.keys(inner).length > 0) out[key] = inner;
|
|
1541
1557
|
} else {
|
|
1542
|
-
dropped.push(
|
|
1558
|
+
dropped.push(path11);
|
|
1543
1559
|
}
|
|
1544
1560
|
}
|
|
1545
1561
|
return out;
|
|
@@ -1564,21 +1580,21 @@ function deepMerge(base, override) {
|
|
|
1564
1580
|
}
|
|
1565
1581
|
return out;
|
|
1566
1582
|
}
|
|
1567
|
-
function readLayer(
|
|
1583
|
+
function readLayer(path11, project) {
|
|
1568
1584
|
let raw;
|
|
1569
1585
|
try {
|
|
1570
|
-
raw = readFileSync(
|
|
1586
|
+
raw = readFileSync(path11, "utf8");
|
|
1571
1587
|
} catch (err) {
|
|
1572
1588
|
if (err.code === "ENOENT") return void 0;
|
|
1573
|
-
throw new ConfigError(
|
|
1589
|
+
throw new ConfigError(path11, `cannot read file: ${err.message}`, { cause: err });
|
|
1574
1590
|
}
|
|
1575
1591
|
let parsed;
|
|
1576
1592
|
try {
|
|
1577
1593
|
parsed = JSON.parse(raw);
|
|
1578
1594
|
} catch (err) {
|
|
1579
|
-
throw new ConfigError(
|
|
1595
|
+
throw new ConfigError(path11, `malformed JSON: ${err.message}`, { cause: err });
|
|
1580
1596
|
}
|
|
1581
|
-
if (!isPlainObject(parsed)) throw new ConfigError(
|
|
1597
|
+
if (!isPlainObject(parsed)) throw new ConfigError(path11, "expected a JSON object");
|
|
1582
1598
|
let layer = parsed;
|
|
1583
1599
|
if (project) {
|
|
1584
1600
|
const { allowed, dropped } = filterProjectLayer(parsed);
|
|
@@ -1586,7 +1602,7 @@ function readLayer(path10, project) {
|
|
|
1586
1602
|
layer = allowed;
|
|
1587
1603
|
}
|
|
1588
1604
|
const result = configSchema.safeParse(layer);
|
|
1589
|
-
if (!result.success) throw new ConfigError(
|
|
1605
|
+
if (!result.success) throw new ConfigError(path11, z4.prettifyError(result.error));
|
|
1590
1606
|
return layer;
|
|
1591
1607
|
}
|
|
1592
1608
|
var TRUE_VALUES = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
|
|
@@ -1665,19 +1681,19 @@ function loadConfig(options = {}) {
|
|
|
1665
1681
|
}
|
|
1666
1682
|
var isPlainObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1667
1683
|
async function writeGlobalRouting(home, update, lock = {}) {
|
|
1668
|
-
const
|
|
1684
|
+
const path11 = globalPaths(home).config;
|
|
1669
1685
|
return withFileLock(
|
|
1670
|
-
`${
|
|
1686
|
+
`${path11}.lock`,
|
|
1671
1687
|
async () => {
|
|
1672
1688
|
let current = {};
|
|
1673
1689
|
try {
|
|
1674
|
-
const parsed = JSON.parse(await readFile3(
|
|
1675
|
-
if (!isPlainObject2(parsed)) throw new ConfigError(
|
|
1690
|
+
const parsed = JSON.parse(await readFile3(path11, "utf8"));
|
|
1691
|
+
if (!isPlainObject2(parsed)) throw new ConfigError(path11, "expected a JSON object");
|
|
1676
1692
|
current = parsed;
|
|
1677
1693
|
} catch (err) {
|
|
1678
1694
|
if (err instanceof ConfigError) throw err;
|
|
1679
1695
|
if (!isNodeError(err, "ENOENT")) {
|
|
1680
|
-
throw new ConfigError(
|
|
1696
|
+
throw new ConfigError(path11, `cannot update this file: ${err.message}`, {
|
|
1681
1697
|
cause: err
|
|
1682
1698
|
});
|
|
1683
1699
|
}
|
|
@@ -1692,11 +1708,11 @@ async function writeGlobalRouting(home, update, lock = {}) {
|
|
|
1692
1708
|
}
|
|
1693
1709
|
};
|
|
1694
1710
|
const checked = configSchema.safeParse(next);
|
|
1695
|
-
if (!checked.success) throw new ConfigError(
|
|
1696
|
-
await atomicWriteFile(
|
|
1711
|
+
if (!checked.success) throw new ConfigError(path11, z5.prettifyError(checked.error));
|
|
1712
|
+
await atomicWriteFile(path11, `${JSON.stringify(next, null, 2)}
|
|
1697
1713
|
`);
|
|
1698
1714
|
return {
|
|
1699
|
-
path:
|
|
1715
|
+
path: path11,
|
|
1700
1716
|
routes: checked.data.routing.routes,
|
|
1701
1717
|
defaultRoute: checked.data.routing.defaultRoute
|
|
1702
1718
|
};
|
|
@@ -1784,8 +1800,8 @@ function makeUsage(prompt, completion) {
|
|
|
1784
1800
|
function stripProviderPrefix(model, provider) {
|
|
1785
1801
|
return model.startsWith(`${provider}/`) ? model.slice(provider.length + 1) : model;
|
|
1786
1802
|
}
|
|
1787
|
-
function joinUrl(base,
|
|
1788
|
-
return `${base.replace(/\/+$/, "")}/${
|
|
1803
|
+
function joinUrl(base, path11) {
|
|
1804
|
+
return `${base.replace(/\/+$/, "")}/${path11.replace(/^\/+/, "")}`;
|
|
1789
1805
|
}
|
|
1790
1806
|
function configString(config, key) {
|
|
1791
1807
|
const value = config[key];
|
|
@@ -2002,7 +2018,7 @@ function fraction(d) {
|
|
|
2002
2018
|
}
|
|
2003
2019
|
function limitsFromHeaders(headers, now = Date.now()) {
|
|
2004
2020
|
const pick = (a, b) => a.remaining !== void 0 ? a : b;
|
|
2005
|
-
const
|
|
2021
|
+
const tokens2 = pick(
|
|
2006
2022
|
pick(
|
|
2007
2023
|
dimension(
|
|
2008
2024
|
headers,
|
|
@@ -2049,16 +2065,16 @@ function limitsFromHeaders(headers, now = Date.now()) {
|
|
|
2049
2065
|
now
|
|
2050
2066
|
)
|
|
2051
2067
|
);
|
|
2052
|
-
if (
|
|
2068
|
+
if (tokens2.remaining === void 0 && requests.remaining === void 0) return void 0;
|
|
2053
2069
|
const out = {};
|
|
2054
|
-
if (
|
|
2055
|
-
if (
|
|
2070
|
+
if (tokens2.remaining !== void 0) out.tokensRemaining = tokens2.remaining;
|
|
2071
|
+
if (tokens2.limit !== void 0) out.tokensLimit = tokens2.limit;
|
|
2056
2072
|
if (requests.remaining !== void 0) out.requestsRemaining = requests.remaining;
|
|
2057
2073
|
if (requests.limit !== void 0) out.requestsLimit = requests.limit;
|
|
2058
|
-
const tighter = (fraction(
|
|
2059
|
-
const resetAt = tighter.resetAt ??
|
|
2074
|
+
const tighter = (fraction(tokens2) ?? 1) <= (fraction(requests) ?? 1) ? tokens2 : requests;
|
|
2075
|
+
const resetAt = tighter.resetAt ?? tokens2.resetAt ?? requests.resetAt;
|
|
2060
2076
|
if (resetAt !== void 0) out.resetAt = resetAt;
|
|
2061
|
-
if (
|
|
2077
|
+
if (tokens2.remaining === 0 || requests.remaining === 0) out.exhausted = true;
|
|
2062
2078
|
return out;
|
|
2063
2079
|
}
|
|
2064
2080
|
function reportedFraction(limits) {
|
|
@@ -2602,8 +2618,8 @@ var SHELL_SAFE = /^[\w.:=@+\-/\\[\]]+$/;
|
|
|
2602
2618
|
function isShellSafeArg(arg) {
|
|
2603
2619
|
return SHELL_SAFE.test(arg);
|
|
2604
2620
|
}
|
|
2605
|
-
function quoteCommand(
|
|
2606
|
-
return isShellSafeArg(
|
|
2621
|
+
function quoteCommand(path11) {
|
|
2622
|
+
return isShellSafeArg(path11) ? path11 : `"${path11}"`;
|
|
2607
2623
|
}
|
|
2608
2624
|
function killTree(child) {
|
|
2609
2625
|
if (child.pid === void 0 || child.exitCode !== null) return;
|
|
@@ -4047,7 +4063,7 @@ var SUBSCRIPTION_PROVIDER_KINDS = [
|
|
|
4047
4063
|
"antigravity-cli",
|
|
4048
4064
|
"gemini-web"
|
|
4049
4065
|
];
|
|
4050
|
-
var VERSION = "0.
|
|
4066
|
+
var VERSION = "0.7.0";
|
|
4051
4067
|
var QUOTA_WINDOWS = ["1m", "5h", "24h"];
|
|
4052
4068
|
var WINDOW_MS = {
|
|
4053
4069
|
"1m": 6e4,
|
|
@@ -4059,19 +4075,19 @@ var WINDOW_COUNT = QUOTA_WINDOWS.length;
|
|
|
4059
4075
|
function limitsFor(window, limits) {
|
|
4060
4076
|
const pick = (v) => v !== void 0 && v > 0 ? v : void 0;
|
|
4061
4077
|
const out = {};
|
|
4062
|
-
let
|
|
4078
|
+
let tokens2;
|
|
4063
4079
|
let requests;
|
|
4064
4080
|
if (window === "1m") {
|
|
4065
|
-
|
|
4081
|
+
tokens2 = pick(limits.tpm);
|
|
4066
4082
|
requests = pick(limits.rpm);
|
|
4067
4083
|
} else if (window === "5h") {
|
|
4068
|
-
|
|
4084
|
+
tokens2 = pick(limits.tokens5h);
|
|
4069
4085
|
requests = pick(limits.requests5h);
|
|
4070
4086
|
} else {
|
|
4071
|
-
|
|
4087
|
+
tokens2 = pick(limits.tokensDaily);
|
|
4072
4088
|
requests = pick(limits.requestsDaily);
|
|
4073
4089
|
}
|
|
4074
|
-
if (
|
|
4090
|
+
if (tokens2 !== void 0) out.tokenLimit = tokens2;
|
|
4075
4091
|
if (requests !== void 0) out.requestLimit = requests;
|
|
4076
4092
|
return out;
|
|
4077
4093
|
}
|
|
@@ -4083,26 +4099,26 @@ var AccountSeries = class {
|
|
|
4083
4099
|
heads = new Array(WINDOW_COUNT).fill(0);
|
|
4084
4100
|
tokenSums = new Array(WINDOW_COUNT).fill(0);
|
|
4085
4101
|
requestSums = new Array(WINDOW_COUNT).fill(0);
|
|
4086
|
-
add(ts,
|
|
4102
|
+
add(ts, tokens2, requests) {
|
|
4087
4103
|
const last = this.slotTs.length - 1;
|
|
4088
4104
|
let slot = Math.floor(ts / SLOT_MS) * SLOT_MS;
|
|
4089
4105
|
if (last >= 0 && slot < this.slotTs[last]) slot = this.slotTs[last];
|
|
4090
4106
|
if (last >= 0 && this.slotTs[last] === slot) {
|
|
4091
|
-
this.slotTokens[last] +=
|
|
4107
|
+
this.slotTokens[last] += tokens2;
|
|
4092
4108
|
this.slotRequests[last] += requests;
|
|
4093
4109
|
for (let w = 0; w < WINDOW_COUNT; w++) {
|
|
4094
4110
|
if (this.heads[w] <= last) {
|
|
4095
|
-
this.tokenSums[w] +=
|
|
4111
|
+
this.tokenSums[w] += tokens2;
|
|
4096
4112
|
this.requestSums[w] += requests;
|
|
4097
4113
|
}
|
|
4098
4114
|
}
|
|
4099
4115
|
return;
|
|
4100
4116
|
}
|
|
4101
4117
|
this.slotTs.push(slot);
|
|
4102
|
-
this.slotTokens.push(
|
|
4118
|
+
this.slotTokens.push(tokens2);
|
|
4103
4119
|
this.slotRequests.push(requests);
|
|
4104
4120
|
for (let w = 0; w < WINDOW_COUNT; w++) {
|
|
4105
|
-
this.tokenSums[w] +=
|
|
4121
|
+
this.tokenSums[w] += tokens2;
|
|
4106
4122
|
this.requestSums[w] += requests;
|
|
4107
4123
|
}
|
|
4108
4124
|
}
|
|
@@ -4152,13 +4168,13 @@ var QuotaEngine = class {
|
|
|
4152
4168
|
constructor(options = {}) {
|
|
4153
4169
|
this.clock = options.clock ?? Date.now;
|
|
4154
4170
|
}
|
|
4155
|
-
record(accountId,
|
|
4171
|
+
record(accountId, tokens2, options = {}) {
|
|
4156
4172
|
let series = this.series.get(accountId);
|
|
4157
4173
|
if (!series) {
|
|
4158
4174
|
series = new AccountSeries();
|
|
4159
4175
|
this.series.set(accountId, series);
|
|
4160
4176
|
}
|
|
4161
|
-
series.add(options.ts ?? this.clock(), Math.max(0,
|
|
4177
|
+
series.add(options.ts ?? this.clock(), Math.max(0, tokens2), Math.max(0, options.requests ?? 1));
|
|
4162
4178
|
series.advance(this.clock());
|
|
4163
4179
|
}
|
|
4164
4180
|
totals(accountId) {
|
|
@@ -4173,11 +4189,11 @@ var QuotaEngine = class {
|
|
|
4173
4189
|
const totals = this.totals(account.id);
|
|
4174
4190
|
const windows = {};
|
|
4175
4191
|
QUOTA_WINDOWS.forEach((window, i) => {
|
|
4176
|
-
const { tokens, requests } = totals[i];
|
|
4192
|
+
const { tokens: tokens2, requests } = totals[i];
|
|
4177
4193
|
const limits = limitsFor(window, account.limits);
|
|
4178
|
-
const usage = { window, tokens, requests, utilization: 0, ...limits };
|
|
4194
|
+
const usage = { window, tokens: tokens2, requests, utilization: 0, ...limits };
|
|
4179
4195
|
usage.utilization = Math.max(
|
|
4180
|
-
limits.tokenLimit ?
|
|
4196
|
+
limits.tokenLimit ? tokens2 / limits.tokenLimit : 0,
|
|
4181
4197
|
limits.requestLimit ? requests / limits.requestLimit : 0
|
|
4182
4198
|
);
|
|
4183
4199
|
windows[window] = usage;
|
|
@@ -4193,9 +4209,9 @@ var QuotaEngine = class {
|
|
|
4193
4209
|
wouldExceed(account, estimatedTokens) {
|
|
4194
4210
|
const totals = this.totals(account.id);
|
|
4195
4211
|
return QUOTA_WINDOWS.some((window, i) => {
|
|
4196
|
-
const { tokens, requests } = totals[i];
|
|
4212
|
+
const { tokens: tokens2, requests } = totals[i];
|
|
4197
4213
|
const { tokenLimit, requestLimit } = limitsFor(window, account.limits);
|
|
4198
|
-
if (tokenLimit !== void 0 &&
|
|
4214
|
+
if (tokenLimit !== void 0 && tokens2 + estimatedTokens > tokenLimit) return true;
|
|
4199
4215
|
if (requestLimit !== void 0 && requests + 1 > requestLimit) return true;
|
|
4200
4216
|
return false;
|
|
4201
4217
|
});
|
|
@@ -5193,19 +5209,19 @@ var Router = class {
|
|
|
5193
5209
|
});
|
|
5194
5210
|
}
|
|
5195
5211
|
/** The caller aborted: record the attempt as cancelled without touching cooldowns. */
|
|
5196
|
-
onCancelled(candidate, requestId, startedAt,
|
|
5212
|
+
onCancelled(candidate, requestId, startedAt, tokens2 = { prompt: 0, completion: 0 }) {
|
|
5197
5213
|
const latencyMs = Math.max(0, this.clock() - startedAt);
|
|
5198
5214
|
this.breaker.onCancel(candidate.account.id);
|
|
5199
5215
|
this.recordUsage(
|
|
5200
5216
|
candidate,
|
|
5201
5217
|
requestId,
|
|
5202
5218
|
"cancelled",
|
|
5203
|
-
|
|
5204
|
-
|
|
5219
|
+
tokens2.prompt,
|
|
5220
|
+
tokens2.completion,
|
|
5205
5221
|
latencyMs
|
|
5206
5222
|
);
|
|
5207
5223
|
}
|
|
5208
|
-
onFailure(candidate, requestId, error, startedAt,
|
|
5224
|
+
onFailure(candidate, requestId, error, startedAt, tokens2 = { prompt: 0, completion: 0 }) {
|
|
5209
5225
|
const { account } = candidate;
|
|
5210
5226
|
const latencyMs = Math.max(0, this.clock() - startedAt);
|
|
5211
5227
|
const rateLimited = error.kind === "rate_limit" || error.kind === "quota_exhausted";
|
|
@@ -5213,8 +5229,8 @@ var Router = class {
|
|
|
5213
5229
|
candidate,
|
|
5214
5230
|
requestId,
|
|
5215
5231
|
rateLimited ? "rate_limited" : "error",
|
|
5216
|
-
|
|
5217
|
-
|
|
5232
|
+
tokens2.prompt,
|
|
5233
|
+
tokens2.completion,
|
|
5218
5234
|
latencyMs,
|
|
5219
5235
|
error.kind
|
|
5220
5236
|
);
|
|
@@ -5383,7 +5399,7 @@ var Router = class {
|
|
|
5383
5399
|
if (chunk.usage) upstreamUsage = chunk.usage;
|
|
5384
5400
|
for (const choice of chunk.choices) text += messageText(choice.delta);
|
|
5385
5401
|
};
|
|
5386
|
-
const
|
|
5402
|
+
const tokens2 = () => {
|
|
5387
5403
|
const has = upstreamUsage && (upstreamUsage.prompt_tokens > 0 || upstreamUsage.completion_tokens > 0);
|
|
5388
5404
|
return has && upstreamUsage ? { prompt: upstreamUsage.prompt_tokens, completion: upstreamUsage.completion_tokens } : { prompt: estimate, completion: countTextTokens(text) };
|
|
5389
5405
|
};
|
|
@@ -5405,9 +5421,9 @@ var Router = class {
|
|
|
5405
5421
|
failed = true;
|
|
5406
5422
|
const error = toProviderError2(err, candidate.account);
|
|
5407
5423
|
if (signal?.aborted) {
|
|
5408
|
-
this.onCancelled(candidate, meta.requestId, startedAt,
|
|
5424
|
+
this.onCancelled(candidate, meta.requestId, startedAt, tokens2());
|
|
5409
5425
|
} else {
|
|
5410
|
-
this.onFailure(candidate, meta.requestId, error, startedAt,
|
|
5426
|
+
this.onFailure(candidate, meta.requestId, error, startedAt, tokens2());
|
|
5411
5427
|
}
|
|
5412
5428
|
throw error;
|
|
5413
5429
|
} finally {
|
|
@@ -5418,7 +5434,7 @@ var Router = class {
|
|
|
5418
5434
|
}
|
|
5419
5435
|
}
|
|
5420
5436
|
if (!failed) {
|
|
5421
|
-
const { prompt, completion } =
|
|
5437
|
+
const { prompt, completion } = tokens2();
|
|
5422
5438
|
this.onSuccess(candidate, meta.requestId, prompt, completion, startedAt);
|
|
5423
5439
|
}
|
|
5424
5440
|
}
|
|
@@ -5695,9 +5711,9 @@ function migrate(db, migrations = MIGRATIONS) {
|
|
|
5695
5711
|
}
|
|
5696
5712
|
return version;
|
|
5697
5713
|
}
|
|
5698
|
-
function openDatabase(
|
|
5699
|
-
if (
|
|
5700
|
-
const db = new BetterSqlite3(
|
|
5714
|
+
function openDatabase(path11, options = {}) {
|
|
5715
|
+
if (path11 !== ":memory:") mkdirSync5(dirname5(path11), { recursive: true });
|
|
5716
|
+
const db = new BetterSqlite3(path11);
|
|
5701
5717
|
try {
|
|
5702
5718
|
db.pragma("journal_mode = WAL");
|
|
5703
5719
|
db.pragma("foreign_keys = ON");
|
|
@@ -6899,7 +6915,7 @@ ${repairPrompt(runOpts.cycle, runOpts.failureReport)}`;
|
|
|
6899
6915
|
}
|
|
6900
6916
|
let resultText;
|
|
6901
6917
|
let isError = false;
|
|
6902
|
-
let
|
|
6918
|
+
let tokens2 = 0;
|
|
6903
6919
|
let turns = 0;
|
|
6904
6920
|
try {
|
|
6905
6921
|
for await (const raw of cli.events) {
|
|
@@ -6919,7 +6935,7 @@ ${repairPrompt(runOpts.cycle, runOpts.failureReport)}`;
|
|
|
6919
6935
|
resultText = asString(raw.result) ?? "";
|
|
6920
6936
|
isError = raw.is_error === true || (asString(raw.subtype) ?? "success") !== "success";
|
|
6921
6937
|
const usage = isRecord(raw.usage) ? raw.usage : {};
|
|
6922
|
-
|
|
6938
|
+
tokens2 = (asNumber(usage.input_tokens) ?? 0) + (asNumber(usage.output_tokens) ?? 0) + (asNumber(usage.cache_creation_input_tokens) ?? 0);
|
|
6923
6939
|
}
|
|
6924
6940
|
}
|
|
6925
6941
|
} catch (err) {
|
|
@@ -6938,7 +6954,7 @@ ${repairPrompt(runOpts.cycle, runOpts.failureReport)}`;
|
|
|
6938
6954
|
`Claude Code CLI timed out after ${opts.timeoutMs ?? 18e5} ms`
|
|
6939
6955
|
);
|
|
6940
6956
|
}
|
|
6941
|
-
totalTokens +=
|
|
6957
|
+
totalTokens += tokens2;
|
|
6942
6958
|
if (exit.code !== 0 || isError || resultText === void 0) {
|
|
6943
6959
|
const detail = redact(
|
|
6944
6960
|
(isError && resultText ? resultText : exit.stderr || resultText || "").trim()
|
|
@@ -6951,7 +6967,7 @@ ${repairPrompt(runOpts.cycle, runOpts.failureReport)}`;
|
|
|
6951
6967
|
summary: resultText.trim() || "Implemented by Claude Code.",
|
|
6952
6968
|
stopReason: "completed",
|
|
6953
6969
|
iterations: Math.max(1, turns),
|
|
6954
|
-
tokens,
|
|
6970
|
+
tokens: tokens2,
|
|
6955
6971
|
totalTokens,
|
|
6956
6972
|
changedFiles: []
|
|
6957
6973
|
};
|
|
@@ -8302,8 +8318,8 @@ var AccountClient = class {
|
|
|
8302
8318
|
return url.toString();
|
|
8303
8319
|
}
|
|
8304
8320
|
/** A page of the hosted service, such as `/login/done`. */
|
|
8305
|
-
pageUrl(
|
|
8306
|
-
return `${this.service.url}${
|
|
8321
|
+
pageUrl(path11) {
|
|
8322
|
+
return `${this.service.url}${path11}`;
|
|
8307
8323
|
}
|
|
8308
8324
|
/** Browser URL that starts Google or GitHub sign-in and returns to `redirectTo` with `?code=`. */
|
|
8309
8325
|
authorizeUrl(provider, redirectTo, challenge) {
|
|
@@ -8356,13 +8372,13 @@ var AccountClient = class {
|
|
|
8356
8372
|
signal: AbortSignal.timeout(1e4)
|
|
8357
8373
|
}).catch(() => void 0);
|
|
8358
8374
|
}
|
|
8359
|
-
async session(
|
|
8360
|
-
return toSession(await this.post(
|
|
8375
|
+
async session(path11, body) {
|
|
8376
|
+
return toSession(await this.post(path11, body), this.now());
|
|
8361
8377
|
}
|
|
8362
|
-
async post(
|
|
8378
|
+
async post(path11, body) {
|
|
8363
8379
|
let response;
|
|
8364
8380
|
try {
|
|
8365
|
-
response = await this.fetch(`${this.service.url}/auth/v1/${
|
|
8381
|
+
response = await this.fetch(`${this.service.url}/auth/v1/${path11}`, {
|
|
8366
8382
|
method: "POST",
|
|
8367
8383
|
headers: { apikey: this.service.publishableKey, "Content-Type": "application/json" },
|
|
8368
8384
|
body: JSON.stringify(body),
|
|
@@ -8582,16 +8598,16 @@ var AccountStore = class {
|
|
|
8582
8598
|
* refresh goes ahead without it (the service tolerates two refreshes racing).
|
|
8583
8599
|
*/
|
|
8584
8600
|
async lock() {
|
|
8585
|
-
const
|
|
8601
|
+
const path11 = `${this.path}.lock`;
|
|
8586
8602
|
const owner = `${process.pid}:${randomBytes8(8).toString("hex")}`;
|
|
8587
|
-
mkdirSync7(dirname6(
|
|
8603
|
+
mkdirSync7(dirname6(path11), { recursive: true });
|
|
8588
8604
|
const deadline = Date.now() + LOCK_WAIT_MS;
|
|
8589
8605
|
for (; ; ) {
|
|
8590
8606
|
try {
|
|
8591
|
-
writeFileSync3(
|
|
8607
|
+
writeFileSync3(path11, owner, { flag: "wx", mode: 384 });
|
|
8592
8608
|
return () => {
|
|
8593
8609
|
try {
|
|
8594
|
-
if (readFileSync3(
|
|
8610
|
+
if (readFileSync3(path11, "utf8") === owner) rmSync3(path11, { force: true });
|
|
8595
8611
|
} catch {
|
|
8596
8612
|
}
|
|
8597
8613
|
};
|
|
@@ -8600,7 +8616,7 @@ var AccountStore = class {
|
|
|
8600
8616
|
return () => void 0;
|
|
8601
8617
|
}
|
|
8602
8618
|
try {
|
|
8603
|
-
if (Date.now() - statSync2(
|
|
8619
|
+
if (Date.now() - statSync2(path11).mtimeMs > LOCK_STALE_MS) rmSync3(path11, { force: true });
|
|
8604
8620
|
} catch {
|
|
8605
8621
|
}
|
|
8606
8622
|
if (Date.now() >= deadline) return () => void 0;
|
|
@@ -8699,1548 +8715,2339 @@ function brandLogo(unicode = true) {
|
|
|
8699
8715
|
(line) => line.replaceAll("\u2588", "#").replaceAll("\u2580", '"').replaceAll("\u2584", ".")
|
|
8700
8716
|
);
|
|
8701
8717
|
}
|
|
8702
|
-
var
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8707
|
-
|
|
8708
|
-
|
|
8709
|
-
|
|
8710
|
-
|
|
8711
|
-
|
|
8712
|
-
|
|
8713
|
-
|
|
8714
|
-
|
|
8715
|
-
|
|
8716
|
-
|
|
8718
|
+
var CHANGES_LIMITS = {
|
|
8719
|
+
/** Most isolated workspaces per project. */
|
|
8720
|
+
maxWorkspaces: 12,
|
|
8721
|
+
labelMaxLength: 60,
|
|
8722
|
+
messageMaxLength: 5e3,
|
|
8723
|
+
titleMaxLength: 200,
|
|
8724
|
+
bodyMaxLength: 2e4,
|
|
8725
|
+
/** Largest patch returned by `diff` (characters); longer patches are cut and flagged. */
|
|
8726
|
+
diffMaxLength: 1e6,
|
|
8727
|
+
/** Largest untracked file rendered as a patch (bytes); bigger ones are listed as binary. */
|
|
8728
|
+
untrackedMaxBytes: 512 * 1024,
|
|
8729
|
+
/** Largest patch handed to an agent to write the commit message (characters). */
|
|
8730
|
+
suggestDiffMaxLength: 4e4,
|
|
8731
|
+
/** Hard timeout for an agent writing the commit message. */
|
|
8732
|
+
suggestTimeoutMs: 3 * 6e4
|
|
8733
|
+
};
|
|
8734
|
+
var MAIN_WORKSPACE_ID = "main";
|
|
8735
|
+
var WORKSPACE_ID_PATTERN = /^[a-z0-9]{4,24}$/;
|
|
8736
|
+
var PANE_BRANCH_PREFIX = "davecode/pane-";
|
|
8737
|
+
var workspaceIdSchema = z10.string().refine((id) => id === MAIN_WORKSPACE_ID || WORKSPACE_ID_PATTERN.test(id), "invalid workspace");
|
|
8738
|
+
var safeRef = z10.string().regex(/^(?!.*\.\.)(?!.*\/\/)(?!\/)(?!.*\/$)(?!.*\.lock$)(?!-)[A-Za-z0-9._/-]{1,200}$/);
|
|
8739
|
+
var createWorkspaceSchema = z10.object({
|
|
8740
|
+
label: z10.string().trim().min(1).max(CHANGES_LIMITS.labelMaxLength).optional()
|
|
8741
|
+
});
|
|
8742
|
+
var suggestSchema = z10.object({
|
|
8743
|
+
/** Agent that writes the text; without one, DaveCode derives it from the file list. */
|
|
8744
|
+
agent: z10.enum(["claude", "codex", "agy"]).optional()
|
|
8745
|
+
});
|
|
8746
|
+
var shipSchema = z10.object({
|
|
8747
|
+
message: z10.string().trim().min(1).max(CHANGES_LIMITS.messageMaxLength).optional(),
|
|
8748
|
+
push: z10.boolean().default(false),
|
|
8749
|
+
pr: z10.object({
|
|
8750
|
+
title: z10.string().trim().min(1).max(CHANGES_LIMITS.titleMaxLength),
|
|
8751
|
+
body: z10.string().max(CHANGES_LIMITS.bodyMaxLength).default(""),
|
|
8752
|
+
draft: z10.boolean().default(false),
|
|
8753
|
+
/** Default: `baseBranch` for isolated workspaces, the remote's default branch otherwise. */
|
|
8754
|
+
base: safeRef.optional()
|
|
8755
|
+
}).optional()
|
|
8756
|
+
});
|
|
8757
|
+
var mergeSchema = z10.object({
|
|
8758
|
+
/** Commits uncommitted work in the isolated workspace with this message before merging. */
|
|
8759
|
+
message: z10.string().trim().min(1).max(CHANGES_LIMITS.messageMaxLength).optional()
|
|
8760
|
+
});
|
|
8761
|
+
var removeSchema = z10.object({
|
|
8762
|
+
/** Also delete the workspace's branch. By default it stays, so no work is lost. */
|
|
8763
|
+
deleteBranch: z10.boolean().default(false)
|
|
8764
|
+
});
|
|
8765
|
+
var ChangesError = class extends Error {
|
|
8766
|
+
constructor(code, message) {
|
|
8767
|
+
super(message);
|
|
8768
|
+
this.code = code;
|
|
8769
|
+
this.name = "ChangesError";
|
|
8770
|
+
}
|
|
8771
|
+
code;
|
|
8772
|
+
};
|
|
8773
|
+
var ORCHESTRATOR_AGENTS = ["claude", "codex", "agy"];
|
|
8774
|
+
var ORCHESTRATOR_LIMITS = {
|
|
8775
|
+
objectiveMaxLength: 4e3,
|
|
8776
|
+
/** Most tasks a plan may contain. */
|
|
8777
|
+
maxTasks: 8,
|
|
8778
|
+
/** Default when the request does not say. */
|
|
8779
|
+
defaultMaxTasks: 5,
|
|
8780
|
+
/** Most tasks running at the same time. */
|
|
8781
|
+
maxParallel: 3,
|
|
8782
|
+
/** Hard timeout for one headless agent run. */
|
|
8783
|
+
taskTimeoutMs: 30 * 6e4,
|
|
8784
|
+
/** Hard timeout for the planning run. */
|
|
8785
|
+
planTimeoutMs: 5 * 6e4,
|
|
8786
|
+
/** Longest summary kept per task (characters, head kept). */
|
|
8787
|
+
summaryMaxLength: 4e3
|
|
8788
|
+
};
|
|
8789
|
+
var startRunSchema = z11.object({
|
|
8790
|
+
objective: z11.string().trim().min(1).max(ORCHESTRATOR_LIMITS.objectiveMaxLength),
|
|
8791
|
+
/** Defaults to every available agent. */
|
|
8792
|
+
agents: z11.array(z11.enum(ORCHESTRATOR_AGENTS)).min(1).max(3).optional(),
|
|
8793
|
+
maxTasks: z11.number().int().min(1).max(ORCHESTRATOR_LIMITS.maxTasks).optional(),
|
|
8794
|
+
/** The agent that splits the objective. It must be installed, but need not take tasks.
|
|
8795
|
+
* Defaults to the first allowed agent in the order claude, codex, agy. */
|
|
8796
|
+
planner: z11.enum(ORCHESTRATOR_AGENTS).optional()
|
|
8797
|
+
});
|
|
8798
|
+
var OrchestratorError = class extends Error {
|
|
8799
|
+
constructor(code, message) {
|
|
8800
|
+
super(message);
|
|
8801
|
+
this.code = code;
|
|
8802
|
+
this.name = "OrchestratorError";
|
|
8803
|
+
}
|
|
8804
|
+
code;
|
|
8805
|
+
};
|
|
8806
|
+
var MAX_TEXT_CHUNK = 2e3;
|
|
8807
|
+
var MAX_TOOL_LINE = 200;
|
|
8808
|
+
var MAX_ERROR = 300;
|
|
8809
|
+
var AGENTS = {
|
|
8810
|
+
claude: {
|
|
8811
|
+
label: "Claude Code",
|
|
8812
|
+
binary: "claude",
|
|
8813
|
+
provider: "claude-cli",
|
|
8814
|
+
signIn: "run `claude` in a terminal and sign in"
|
|
8717
8815
|
},
|
|
8718
|
-
{
|
|
8719
|
-
|
|
8720
|
-
|
|
8721
|
-
|
|
8816
|
+
codex: {
|
|
8817
|
+
label: "Codex",
|
|
8818
|
+
binary: "codex",
|
|
8819
|
+
provider: "codex-cli",
|
|
8820
|
+
signIn: "run `codex login` in a terminal"
|
|
8722
8821
|
},
|
|
8723
|
-
{
|
|
8724
|
-
|
|
8725
|
-
|
|
8726
|
-
|
|
8822
|
+
agy: {
|
|
8823
|
+
label: "Antigravity",
|
|
8824
|
+
binary: "agy",
|
|
8825
|
+
provider: "antigravity-cli",
|
|
8826
|
+
signIn: "run `agy` in a terminal and sign in"
|
|
8727
8827
|
}
|
|
8728
|
-
|
|
8729
|
-
|
|
8730
|
-
|
|
8731
|
-
|
|
8732
|
-
|
|
8733
|
-
|
|
8734
|
-
|
|
8735
|
-
|
|
8736
|
-
|
|
8737
|
-
|
|
8738
|
-
|
|
8739
|
-
|
|
8740
|
-
|
|
8741
|
-
|
|
8828
|
+
};
|
|
8829
|
+
var AGY_PLAN_NOTE = "Note: you are running in an empty scratch directory, not inside the project, so you cannot see the repository's files. Answer from the information in this message only and do not create files.";
|
|
8830
|
+
function agentArgs(agent, mode) {
|
|
8831
|
+
switch (agent) {
|
|
8832
|
+
case "claude":
|
|
8833
|
+
return mode === "plan" ? [
|
|
8834
|
+
"-p",
|
|
8835
|
+
"--output-format",
|
|
8836
|
+
"stream-json",
|
|
8837
|
+
"--verbose",
|
|
8838
|
+
"--permission-mode",
|
|
8839
|
+
"plan",
|
|
8840
|
+
"--tools",
|
|
8841
|
+
"Read",
|
|
8842
|
+
"Glob",
|
|
8843
|
+
"Grep",
|
|
8844
|
+
"--strict-mcp-config"
|
|
8845
|
+
] : ["-p", "--output-format", "stream-json", "--verbose", "--permission-mode", "acceptEdits"];
|
|
8846
|
+
case "codex":
|
|
8847
|
+
return [
|
|
8848
|
+
"exec",
|
|
8849
|
+
"--json",
|
|
8850
|
+
"--skip-git-repo-check",
|
|
8851
|
+
"--sandbox",
|
|
8852
|
+
mode === "plan" ? "read-only" : "workspace-write",
|
|
8853
|
+
"-c",
|
|
8854
|
+
"approval_policy=never",
|
|
8855
|
+
"-"
|
|
8856
|
+
];
|
|
8857
|
+
case "agy":
|
|
8858
|
+
return [
|
|
8859
|
+
"--input-format",
|
|
8860
|
+
"stream-json",
|
|
8861
|
+
"--output-format",
|
|
8862
|
+
"stream-json",
|
|
8863
|
+
"--sandbox",
|
|
8864
|
+
"--disable-slash-commands",
|
|
8865
|
+
"--print="
|
|
8866
|
+
];
|
|
8742
8867
|
}
|
|
8743
|
-
return [...roles.values()];
|
|
8744
8868
|
}
|
|
8745
|
-
function
|
|
8746
|
-
|
|
8747
|
-
(
|
|
8748
|
-
|
|
8869
|
+
function realPath(p) {
|
|
8870
|
+
try {
|
|
8871
|
+
return realpathSync.native(p);
|
|
8872
|
+
} catch {
|
|
8873
|
+
return path8.resolve(p);
|
|
8874
|
+
}
|
|
8749
8875
|
}
|
|
8750
|
-
|
|
8751
|
-
|
|
8752
|
-
|
|
8753
|
-
|
|
8754
|
-
|
|
8755
|
-
|
|
8756
|
-
|
|
8757
|
-
];
|
|
8758
|
-
var OPERATIONAL = [
|
|
8759
|
-
/\b(deploy|release|publish)\b[^.\n]*\b(production|prod|npm|app store|play store|live)\b/i,
|
|
8760
|
-
/\b(go live|pay|purchase|buy|checkout)\b/i,
|
|
8761
|
-
/\b(send|email|e-mail|message)\b[^.\n]*\b(customers?|users?|leads?|prospects?|subscribers?|mailing list|newsletter)\b/i,
|
|
8762
|
-
/\b(post|tweet|publish)\b[^.\n]*\b(twitter|x\.com|linkedin|facebook|instagram|reddit|hacker news|product hunt)\b/i
|
|
8763
|
-
];
|
|
8764
|
-
function firstMatch(patterns, text) {
|
|
8765
|
-
for (const pattern of patterns) {
|
|
8766
|
-
const match = pattern.exec(text);
|
|
8767
|
-
if (match) return match[0];
|
|
8876
|
+
function agentStdin(agent, mode, prompt) {
|
|
8877
|
+
if (agent === "agy") {
|
|
8878
|
+
const content = mode === "plan" ? `${AGY_PLAN_NOTE}
|
|
8879
|
+
|
|
8880
|
+
${prompt}` : prompt;
|
|
8881
|
+
return `${JSON.stringify({ event: "user", message: { content } })}
|
|
8882
|
+
`;
|
|
8768
8883
|
}
|
|
8769
|
-
return
|
|
8884
|
+
if (agent === "claude" && prompt.trimStart().startsWith("/")) return `Task:
|
|
8885
|
+
${prompt}`;
|
|
8886
|
+
return prompt;
|
|
8770
8887
|
}
|
|
8771
|
-
function
|
|
8772
|
-
|
|
8888
|
+
function abortError() {
|
|
8889
|
+
const err = new Error("The agent run was aborted");
|
|
8890
|
+
err.name = "AbortError";
|
|
8891
|
+
return err;
|
|
8773
8892
|
}
|
|
8774
|
-
var
|
|
8775
|
-
|
|
8776
|
-
|
|
8777
|
-
|
|
8778
|
-
|
|
8779
|
-
|
|
8780
|
-
|
|
8781
|
-
|
|
8782
|
-
|
|
8893
|
+
var SECRET_PATTERNS = [
|
|
8894
|
+
[/\b(?:sk|rk|pk)-[A-Za-z0-9_-]{8,}/g, "[redacted]"],
|
|
8895
|
+
[/\b(?:ghp|gho|ghs|ghu|github_pat)_[A-Za-z0-9_]{8,}/g, "[redacted]"],
|
|
8896
|
+
[/\bAIza[A-Za-z0-9_-]{20,}/g, "[redacted]"],
|
|
8897
|
+
[/\bya29\.[A-Za-z0-9._-]{10,}/g, "[redacted]"],
|
|
8898
|
+
[/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]*/g, "[redacted]"],
|
|
8899
|
+
[/\b(bearer)\s+[^\s"']+/gi, "$1 [redacted]"],
|
|
8900
|
+
[
|
|
8901
|
+
/\b(api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password|authorization)(["']?\s*[:=]\s*["']?)[^\s"',}]+/gi,
|
|
8902
|
+
"$1$2[redacted]"
|
|
8903
|
+
]
|
|
8904
|
+
];
|
|
8905
|
+
function scrubSecrets(text) {
|
|
8906
|
+
let out = redact(text);
|
|
8907
|
+
for (const [pattern, replacement] of SECRET_PATTERNS) out = out.replace(pattern, replacement);
|
|
8908
|
+
return out.replace(/\s+/g, " ").trim();
|
|
8783
8909
|
}
|
|
8784
|
-
|
|
8785
|
-
|
|
8786
|
-
title: z10.string().trim().min(1).max(300),
|
|
8787
|
-
description: z10.string().max(8e3).optional(),
|
|
8788
|
-
role: z10.string().min(1).max(40),
|
|
8789
|
-
kind: z10.enum(["code", "artifact"]).optional(),
|
|
8790
|
-
dependsOn: z10.array(z10.string().min(1)).max(50).default([]),
|
|
8791
|
-
acceptance: z10.array(z10.string().min(1).max(1e3)).max(20).optional()
|
|
8792
|
-
});
|
|
8793
|
-
var planSchema = z10.object({
|
|
8794
|
-
summary: z10.string().max(4e3).optional(),
|
|
8795
|
-
tasks: z10.array(plannedTaskSchema).min(1).max(100)
|
|
8796
|
-
});
|
|
8797
|
-
var reviewSchema = z10.object({
|
|
8798
|
-
/** The objective is achieved as far as DaveCode can take it. */
|
|
8799
|
-
done: z10.boolean(),
|
|
8800
|
-
summary: z10.string().max(8e3),
|
|
8801
|
-
/** Ids of FAILED tasks worth another attempt. */
|
|
8802
|
-
retry: z10.array(z10.string()).max(50).default([]),
|
|
8803
|
-
/** New tasks for the next round. */
|
|
8804
|
-
tasks: z10.array(plannedTaskSchema).max(50).default([])
|
|
8805
|
-
});
|
|
8806
|
-
var TASK_ID = /^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/;
|
|
8807
|
-
function sanitizeTaskId(raw) {
|
|
8808
|
-
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
8809
|
-
return TASK_ID.test(id) ? id : "task";
|
|
8910
|
+
function clip(text, max) {
|
|
8911
|
+
return text.length <= max ? text : `${text.slice(0, max - 1)}\u2026`;
|
|
8810
8912
|
}
|
|
8811
|
-
function
|
|
8812
|
-
|
|
8813
|
-
const taken = new Set(opts.existing.map((t) => t.id));
|
|
8814
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
8815
|
-
const out = [];
|
|
8816
|
-
for (const p of planned.slice(0, Math.max(0, opts.room))) {
|
|
8817
|
-
let id = sanitizeTaskId(p.id);
|
|
8818
|
-
for (let n = 2; taken.has(id); n++) id = `${sanitizeTaskId(p.id)}-${n}`;
|
|
8819
|
-
taken.add(id);
|
|
8820
|
-
idMap.set(p.id, id);
|
|
8821
|
-
let role = roleById.get(p.role.toLowerCase());
|
|
8822
|
-
const requested = p.kind ?? role?.kind ?? "artifact";
|
|
8823
|
-
if (!role)
|
|
8824
|
-
role = roleById.get(requested === "code" ? "implementer" : "writer") ?? opts.roles[0];
|
|
8825
|
-
const kind = requested === "code" && opts.codeTasks ? "code" : "artifact";
|
|
8826
|
-
const task = {
|
|
8827
|
-
id,
|
|
8828
|
-
title: p.title,
|
|
8829
|
-
role: role?.id ?? "writer",
|
|
8830
|
-
kind,
|
|
8831
|
-
status: "PENDING",
|
|
8832
|
-
dependsOn: p.dependsOn,
|
|
8833
|
-
round: opts.round,
|
|
8834
|
-
attempts: 0
|
|
8835
|
-
};
|
|
8836
|
-
const description = requested === "code" && kind === "artifact" ? [
|
|
8837
|
-
p.description,
|
|
8838
|
-
"Code tasks cannot run here: write a precise implementation plan instead."
|
|
8839
|
-
].filter(Boolean).join("\n\n") : p.description;
|
|
8840
|
-
if (description) task.description = description;
|
|
8841
|
-
if (p.acceptance?.length) task.acceptance = p.acceptance;
|
|
8842
|
-
out.push(applySafetyGuard(task));
|
|
8843
|
-
}
|
|
8844
|
-
const known = /* @__PURE__ */ new Set([...opts.existing.map((t) => t.id), ...out.map((t) => t.id)]);
|
|
8845
|
-
for (const task of out) {
|
|
8846
|
-
task.dependsOn = [
|
|
8847
|
-
...new Set(task.dependsOn.map((d) => idMap.get(d) ?? sanitizeTaskId(d)))
|
|
8848
|
-
].filter((d) => d !== task.id && known.has(d));
|
|
8849
|
-
}
|
|
8850
|
-
for (; ; ) {
|
|
8851
|
-
const graph = {
|
|
8852
|
-
version: 1,
|
|
8853
|
-
tasks: [...opts.existing, ...out].map((t) => ({
|
|
8854
|
-
id: t.id,
|
|
8855
|
-
title: t.title,
|
|
8856
|
-
status: t.status,
|
|
8857
|
-
dependsOn: t.dependsOn
|
|
8858
|
-
}))
|
|
8859
|
-
};
|
|
8860
|
-
const cycle = findCycles(graph)[0];
|
|
8861
|
-
if (!cycle) break;
|
|
8862
|
-
const from = cycle[cycle.length - 2];
|
|
8863
|
-
const to = cycle[cycle.length - 1];
|
|
8864
|
-
const task = out.find((t) => t.id === from);
|
|
8865
|
-
if (!task) break;
|
|
8866
|
-
task.dependsOn = task.dependsOn.filter((d) => d !== to);
|
|
8867
|
-
}
|
|
8868
|
-
return out;
|
|
8869
|
-
}
|
|
8870
|
-
var SAFETY_RULES = `Hard rules (non-negotiable):
|
|
8871
|
-
- You run inside DaveCode with no internet access and no ability to contact anyone.
|
|
8872
|
-
- Never send emails or messages, post or publish anything, make payments or purchases, sign up for services, or deploy. When such a step is useful, produce a DRAFT for a human to review and execute.
|
|
8873
|
-
- Never invent credentials, personal data or facts presented as verified; label assumptions and estimates clearly.
|
|
8874
|
-
- Stay within the objective; prefer concrete, actionable outputs.`;
|
|
8875
|
-
function roleList(roles) {
|
|
8876
|
-
return roles.map((r) => `- ${r.id} (${r.kind}): ${r.description}`).join("\n");
|
|
8913
|
+
function shortError(text) {
|
|
8914
|
+
return clip(scrubSecrets(text), MAX_ERROR);
|
|
8877
8915
|
}
|
|
8878
|
-
function
|
|
8879
|
-
|
|
8880
|
-
|
|
8881
|
-
Roles:
|
|
8882
|
-
${roleList(roles)}
|
|
8883
|
-
|
|
8884
|
-
Task kinds:
|
|
8885
|
-
- "artifact": the role writes one markdown document (analysis, plan, copy, checklist, draft).
|
|
8886
|
-
- "code": the implementer changes the repository; an autonomous runner branches, runs the quality gates and merges.${codeTasks ? "" : "\n Code tasks are NOT available in this workspace: plan artifact tasks only."}
|
|
8887
|
-
|
|
8888
|
-
${SAFETY_RULES}
|
|
8889
|
-
|
|
8890
|
-
Plan at most the number of tasks you are allowed, smallest useful set first. Ids are short lowercase kebab-case. Use dependsOn so later tasks build on earlier outputs.
|
|
8891
|
-
|
|
8892
|
-
Respond with ONLY a JSON object, no prose and no code fences:
|
|
8893
|
-
{"summary": "one paragraph plan", "tasks": [{"id": "market-research", "title": "...", "description": "...", "role": "researcher", "kind": "artifact", "dependsOn": [], "acceptance": ["..."]}]}`;
|
|
8916
|
+
function formatDuration(ms) {
|
|
8917
|
+
if (ms >= 6e4) return `${Math.round(ms / 6e4)} min`;
|
|
8918
|
+
return `${Math.max(1, Math.ceil(ms / 1e3))} s`;
|
|
8894
8919
|
}
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
Decide:
|
|
8900
|
-
- "done": true when the objective is achieved as far as an offline team can take it (remaining steps are outbound actions a human must do from the drafts).
|
|
8901
|
-
- otherwise propose the next round: "retry" lists FAILED task ids worth another attempt, "tasks" lists new tasks (same shape as the planner's, dependsOn may reference existing ids).
|
|
8902
|
-
|
|
8903
|
-
Respond with ONLY a JSON object, no prose and no code fences:
|
|
8904
|
-
{"done": false, "summary": "progress, gaps and what a human must do next", "retry": [], "tasks": []}`;
|
|
8905
|
-
function roleSystemPrompt(role) {
|
|
8906
|
-
return [
|
|
8907
|
-
`You are the ${role.id} of a small team of AI agents. ${role.description}`,
|
|
8908
|
-
SAFETY_RULES,
|
|
8909
|
-
"Write your deliverable as a single, well-structured markdown document. Do not wrap it in code fences. Start with a level-1 heading.",
|
|
8910
|
-
role.instructions?.trim()
|
|
8911
|
-
].filter(Boolean).join("\n\n");
|
|
8920
|
+
function tail(text, max = 400) {
|
|
8921
|
+
const t = text.trim();
|
|
8922
|
+
return t.length <= max ? t : t.slice(t.length - max);
|
|
8912
8923
|
}
|
|
8913
|
-
|
|
8914
|
-
|
|
8915
|
-
|
|
8916
|
-
|
|
8917
|
-
|
|
8918
|
-
|
|
8919
|
-
|
|
8920
|
-
|
|
8921
|
-
|
|
8922
|
-
|
|
8923
|
-
|
|
8924
|
-
|
|
8925
|
-
|
|
8926
|
-
|
|
8927
|
-
|
|
8928
|
-
|
|
8924
|
+
function toolLine(name, input, cwd) {
|
|
8925
|
+
let hint;
|
|
8926
|
+
if (isRecord(input)) {
|
|
8927
|
+
const lower = new Map(Object.entries(input).map(([k, v]) => [k.toLowerCase(), v]));
|
|
8928
|
+
for (const key of [
|
|
8929
|
+
"file_path",
|
|
8930
|
+
"filepath",
|
|
8931
|
+
"absolutepath",
|
|
8932
|
+
"path",
|
|
8933
|
+
"targetfile",
|
|
8934
|
+
"command",
|
|
8935
|
+
"commandline",
|
|
8936
|
+
"cmd",
|
|
8937
|
+
"pattern",
|
|
8938
|
+
"query",
|
|
8939
|
+
"url",
|
|
8940
|
+
"description"
|
|
8941
|
+
]) {
|
|
8942
|
+
const value = lower.get(key);
|
|
8943
|
+
const str2 = typeof value === "string" ? value : Array.isArray(value) ? value.join(" ") : "";
|
|
8944
|
+
if (str2.trim()) {
|
|
8945
|
+
hint = str2;
|
|
8946
|
+
break;
|
|
8929
8947
|
}
|
|
8930
|
-
}
|
|
8948
|
+
}
|
|
8931
8949
|
}
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8950
|
+
if (hint && path8.isAbsolute(hint)) {
|
|
8951
|
+
for (const base of /* @__PURE__ */ new Set([cwd, realPath(cwd)])) {
|
|
8952
|
+
const rel = path8.relative(base, hint);
|
|
8953
|
+
if (rel && !rel.startsWith("..") && !path8.isAbsolute(rel)) {
|
|
8954
|
+
hint = rel.split(path8.sep).join("/");
|
|
8955
|
+
break;
|
|
8956
|
+
}
|
|
8957
|
+
}
|
|
8938
8958
|
}
|
|
8939
|
-
|
|
8940
|
-
|
|
8941
|
-
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8959
|
+
return clip(scrubSecrets(hint ? `${name} ${hint}` : name), MAX_TOOL_LINE);
|
|
8960
|
+
}
|
|
8961
|
+
function claudeParser(emit, cwd) {
|
|
8962
|
+
const state = { text: "", completed: false };
|
|
8963
|
+
let lastAssistant = "";
|
|
8964
|
+
return {
|
|
8965
|
+
state,
|
|
8966
|
+
flush() {
|
|
8967
|
+
},
|
|
8968
|
+
handle(raw) {
|
|
8969
|
+
if (!isRecord(raw)) return;
|
|
8970
|
+
const type = asString(raw.type);
|
|
8971
|
+
if (type === "system" && raw.subtype === "init") {
|
|
8972
|
+
const model = asString(raw.model);
|
|
8973
|
+
emit({
|
|
8974
|
+
kind: "status",
|
|
8975
|
+
text: model ? `Claude Code started (${model})` : "Claude Code started"
|
|
8976
|
+
});
|
|
8977
|
+
return;
|
|
8978
|
+
}
|
|
8979
|
+
if (type === "assistant" && raw.parent_tool_use_id == null && isRecord(raw.message)) {
|
|
8980
|
+
const texts = [];
|
|
8981
|
+
for (const block of asArray(raw.message.content)) {
|
|
8982
|
+
if (!isRecord(block)) continue;
|
|
8983
|
+
if (block.type === "text") {
|
|
8984
|
+
const t = asString(block.text)?.trim();
|
|
8985
|
+
if (t) texts.push(t);
|
|
8986
|
+
} else if (block.type === "tool_use") {
|
|
8987
|
+
emit({
|
|
8988
|
+
kind: "tool",
|
|
8989
|
+
text: toolLine(asString(block.name) ?? "tool", block.input, cwd)
|
|
8990
|
+
});
|
|
8991
|
+
}
|
|
8992
|
+
}
|
|
8993
|
+
if (texts.length > 0) {
|
|
8994
|
+
lastAssistant = texts.join("\n");
|
|
8995
|
+
emit({ kind: "text", text: clip(lastAssistant, MAX_TEXT_CHUNK) });
|
|
8996
|
+
}
|
|
8997
|
+
return;
|
|
8998
|
+
}
|
|
8999
|
+
if (type === "result") {
|
|
9000
|
+
const result = asString(raw.result) ?? "";
|
|
9001
|
+
const subtype = asString(raw.subtype) ?? "success";
|
|
9002
|
+
const u = isRecord(raw.usage) ? raw.usage : void 0;
|
|
9003
|
+
if (u) {
|
|
9004
|
+
state.tokens = (asNumber(u.input_tokens) ?? 0) + (asNumber(u.cache_creation_input_tokens) ?? 0) + (asNumber(u.cache_read_input_tokens) ?? 0) + (asNumber(u.output_tokens) ?? 0);
|
|
9005
|
+
}
|
|
9006
|
+
if (raw.is_error === true || subtype !== "success") {
|
|
9007
|
+
state.failure = result || `Claude Code reported ${subtype}`;
|
|
9008
|
+
return;
|
|
9009
|
+
}
|
|
9010
|
+
state.completed = true;
|
|
9011
|
+
state.text = result || lastAssistant;
|
|
9012
|
+
}
|
|
8948
9013
|
}
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
9014
|
+
};
|
|
9015
|
+
}
|
|
9016
|
+
function codexErrorText(value) {
|
|
9017
|
+
if (typeof value === "string") return value;
|
|
9018
|
+
if (isRecord(value)) return asString(value.message) ?? JSON.stringify(value);
|
|
9019
|
+
return void 0;
|
|
9020
|
+
}
|
|
9021
|
+
function codexItemLine(item, cwd) {
|
|
9022
|
+
const type = asString(item.type) ?? "item";
|
|
9023
|
+
switch (type) {
|
|
9024
|
+
case "reasoning":
|
|
9025
|
+
case "todo_list":
|
|
9026
|
+
return void 0;
|
|
9027
|
+
case "command_execution":
|
|
9028
|
+
return toolLine("Run", { command: asString(item.command) ?? "" }, cwd);
|
|
9029
|
+
case "file_change": {
|
|
9030
|
+
const files = asArray(item.changes).flatMap((c) => isRecord(c) && asString(c.path) ? [asString(c.path)] : []).map((f) => toolLine("", { path: f }, cwd).trim());
|
|
9031
|
+
return clip(`Edit ${files.join(", ")}`.trim(), MAX_TOOL_LINE);
|
|
8952
9032
|
}
|
|
8953
|
-
|
|
8954
|
-
|
|
9033
|
+
case "mcp_tool_call": {
|
|
9034
|
+
const name = [asString(item.server), asString(item.tool)].filter(Boolean).join(".");
|
|
9035
|
+
return toolLine(name || "MCP tool", void 0, cwd);
|
|
8955
9036
|
}
|
|
8956
|
-
|
|
9037
|
+
case "web_search":
|
|
9038
|
+
return toolLine("Search", { query: asString(item.query) ?? "" }, cwd);
|
|
9039
|
+
default:
|
|
9040
|
+
return toolLine(type, item, cwd);
|
|
8957
9041
|
}
|
|
8958
|
-
async run(task, opts) {
|
|
8959
|
-
const { brain, engine } = this.options;
|
|
8960
|
-
const runnerTaskId = sanitizeTaskId(`goal-${opts.goal.id.slice(-6)}-${task.id}`);
|
|
8961
|
-
const graph = await brain.readGraph();
|
|
8962
|
-
const existing = graph.tasks.find((t) => t.id === runnerTaskId);
|
|
8963
|
-
if (existing?.status === "SUCCESS") {
|
|
8964
|
-
return { ok: true, summary: existing.notes ?? "already done", tokens: 0, runnerTaskId };
|
|
8965
|
-
}
|
|
8966
|
-
if (!existing) {
|
|
8967
|
-
await brain.createTask({
|
|
8968
|
-
id: runnerTaskId,
|
|
8969
|
-
title: task.title,
|
|
8970
|
-
description: [
|
|
8971
|
-
task.description,
|
|
8972
|
-
`Part of DaveCode goal ${opts.goal.id}: ${opts.goal.objective}`
|
|
8973
|
-
].filter(Boolean).join("\n\n"),
|
|
8974
|
-
...task.acceptance?.length ? { acceptance: task.acceptance } : {}
|
|
8975
|
-
});
|
|
8976
|
-
} else if (existing.status !== "PENDING") {
|
|
8977
|
-
await brain.setTaskStatus(runnerTaskId, "PENDING", { notes: "retried by goal mode" });
|
|
8978
|
-
}
|
|
8979
|
-
const base = engine.config.runner;
|
|
8980
|
-
const config = {
|
|
8981
|
-
...base,
|
|
8982
|
-
maxTaskTokens: Math.max(MIN_TASK_TOKENS, Math.min(base.maxTaskTokens, opts.maxTokens))
|
|
8983
|
-
};
|
|
8984
|
-
const metered = new MeteredExecutor(this.options.executor ?? createExecutor(engine, config));
|
|
8985
|
-
const runner = this.options.makeRunner?.(config, metered) ?? createRunner(engine, {
|
|
8986
|
-
brain,
|
|
8987
|
-
config,
|
|
8988
|
-
executor: metered,
|
|
8989
|
-
lock: { lock: this.options.lock, owner: this.owner },
|
|
8990
|
-
...this.options.contextExtras ? { contextExtras: this.options.contextExtras } : {}
|
|
8991
|
-
});
|
|
8992
|
-
const onAbort = () => void runner.stop();
|
|
8993
|
-
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
8994
|
-
try {
|
|
8995
|
-
const result = await runner.runOnce({ taskId: runnerTaskId });
|
|
8996
|
-
return {
|
|
8997
|
-
ok: result.outcome === "success",
|
|
8998
|
-
summary: result.summary ?? result.error ?? result.outcome,
|
|
8999
|
-
tokens: metered.tokens,
|
|
9000
|
-
runnerTaskId,
|
|
9001
|
-
...result.outcome === "stopped" ? { stopped: true } : {}
|
|
9002
|
-
};
|
|
9003
|
-
} catch (err) {
|
|
9004
|
-
if (!(err instanceof RunnerError)) throw err;
|
|
9005
|
-
return { ok: false, summary: err.message, tokens: metered.tokens, runnerTaskId };
|
|
9006
|
-
} finally {
|
|
9007
|
-
opts.signal.removeEventListener("abort", onAbort);
|
|
9008
|
-
}
|
|
9009
|
-
}
|
|
9010
|
-
};
|
|
9011
|
-
var TERMINAL = /* @__PURE__ */ new Set([
|
|
9012
|
-
"planned",
|
|
9013
|
-
"succeeded",
|
|
9014
|
-
"failed",
|
|
9015
|
-
"cancelled",
|
|
9016
|
-
"budget_exhausted"
|
|
9017
|
-
]);
|
|
9018
|
-
function isTerminalGoal(goal) {
|
|
9019
|
-
return TERMINAL.has(goal.status);
|
|
9020
9042
|
}
|
|
9021
|
-
|
|
9022
|
-
};
|
|
9023
|
-
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9028
|
-
|
|
9029
|
-
|
|
9030
|
-
|
|
9031
|
-
|
|
9032
|
-
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9037
|
-
tools;
|
|
9038
|
-
/** A fresh goal (not yet persisted) with limits clamped to the config. */
|
|
9039
|
-
create(input) {
|
|
9040
|
-
const cfg = this.options.config;
|
|
9041
|
-
const stamp = this.now().toISOString();
|
|
9042
|
-
const goal = {
|
|
9043
|
-
id: newId2("goal", 6),
|
|
9044
|
-
objective: input.objective.trim(),
|
|
9045
|
-
status: "planning",
|
|
9046
|
-
round: 0,
|
|
9047
|
-
limits: {
|
|
9048
|
-
maxRounds: Math.min(input.maxRounds ?? cfg.maxRounds, cfg.maxRounds),
|
|
9049
|
-
maxTasks: Math.min(input.maxTasks ?? cfg.maxTasks, cfg.maxTasks),
|
|
9050
|
-
maxTokens: Math.min(input.maxTokens ?? cfg.maxTokens, cfg.maxTokens),
|
|
9051
|
-
tokensUsed: 0
|
|
9052
|
-
},
|
|
9053
|
-
roles: publicRoles(resolveRoles(cfg)),
|
|
9054
|
-
tasks: [],
|
|
9055
|
-
createdAt: stamp,
|
|
9056
|
-
updatedAt: stamp
|
|
9057
|
-
};
|
|
9058
|
-
if (input.dryRun) goal.dryRun = true;
|
|
9059
|
-
return goal;
|
|
9060
|
-
}
|
|
9061
|
-
/** Runs `goal` to a terminal status. Never throws: failures end up in `status`/`lastError`. */
|
|
9062
|
-
async run(goal, signal) {
|
|
9063
|
-
const cfg = this.options.config;
|
|
9064
|
-
const roles = resolveRoles(cfg);
|
|
9065
|
-
try {
|
|
9066
|
-
const code = await this.codeAvailability();
|
|
9067
|
-
if (!code.ok) this.log(goal, "info", `code tasks disabled: ${code.reason}`);
|
|
9068
|
-
if (goal.round === 0 || goal.tasks.length === 0) {
|
|
9069
|
-
goal.round = 1;
|
|
9070
|
-
await this.update(goal, { status: "planning" });
|
|
9071
|
-
this.log(goal, "info", `planning: ${goal.objective}`, "planner");
|
|
9072
|
-
const plan = await this.plan(goal, roles, code.ok, signal);
|
|
9073
|
-
goal.tasks = normalizeTasks(plan.tasks, {
|
|
9074
|
-
existing: [],
|
|
9075
|
-
roles,
|
|
9076
|
-
round: 1,
|
|
9077
|
-
room: goal.limits.maxTasks,
|
|
9078
|
-
codeTasks: code.ok
|
|
9079
|
-
});
|
|
9080
|
-
if (plan.summary) goal.summary = plan.summary;
|
|
9081
|
-
this.log(goal, "info", `planned ${goal.tasks.length} task(s)`, "planner");
|
|
9082
|
-
if (goal.dryRun) return await this.finish(goal, "planned");
|
|
9083
|
-
} else {
|
|
9084
|
-
this.log(goal, "info", `executing planned goal (${goal.tasks.length} task(s))`, "planner");
|
|
9043
|
+
function codexParser(emit, cwd) {
|
|
9044
|
+
const state = { text: "", completed: false };
|
|
9045
|
+
const announced = /* @__PURE__ */ new Set();
|
|
9046
|
+
let lastError;
|
|
9047
|
+
return {
|
|
9048
|
+
state,
|
|
9049
|
+
flush() {
|
|
9050
|
+
if (!state.completed && !state.failure && lastError) state.failure = lastError;
|
|
9051
|
+
},
|
|
9052
|
+
handle(raw) {
|
|
9053
|
+
if (!isRecord(raw)) return;
|
|
9054
|
+
const ev = isRecord(raw.msg) ? raw.msg : raw;
|
|
9055
|
+
const type = asString(ev.type) ?? "";
|
|
9056
|
+
if (type === "thread.started") {
|
|
9057
|
+
emit({ kind: "status", text: "Codex started" });
|
|
9058
|
+
return;
|
|
9085
9059
|
}
|
|
9086
|
-
|
|
9087
|
-
|
|
9088
|
-
|
|
9089
|
-
|
|
9090
|
-
|
|
9091
|
-
|
|
9092
|
-
|
|
9093
|
-
|
|
9094
|
-
|
|
9095
|
-
|
|
9096
|
-
return
|
|
9060
|
+
if ((type === "item.started" || type === "item.completed") && isRecord(ev.item)) {
|
|
9061
|
+
const item = ev.item;
|
|
9062
|
+
const itemType = asString(item.type);
|
|
9063
|
+
if (itemType === "agent_message" || itemType === "assistant_message") {
|
|
9064
|
+
if (type !== "item.completed") return;
|
|
9065
|
+
const text = (asString(item.text) ?? asString(item.message) ?? "").trim();
|
|
9066
|
+
if (text) {
|
|
9067
|
+
state.text = text;
|
|
9068
|
+
emit({ kind: "text", text: clip(text, MAX_TEXT_CHUNK) });
|
|
9069
|
+
}
|
|
9070
|
+
return;
|
|
9097
9071
|
}
|
|
9098
|
-
|
|
9099
|
-
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
room: goal.limits.maxTasks - goal.tasks.length,
|
|
9104
|
-
codeTasks: code.ok
|
|
9105
|
-
});
|
|
9106
|
-
if (retried === 0 && added.length === 0) {
|
|
9107
|
-
goal.lastError = goal.tasks.length >= goal.limits.maxTasks ? `reached the cap of ${goal.limits.maxTasks} task(s) without completing the objective` : "the reviewer proposed no further work but the objective is not done";
|
|
9108
|
-
return await this.finish(goal, "failed");
|
|
9072
|
+
if (itemType === "error") {
|
|
9073
|
+
const message = asString(item.message);
|
|
9074
|
+
if (message && type === "item.completed")
|
|
9075
|
+
emit({ kind: "error", text: shortError(message) });
|
|
9076
|
+
return;
|
|
9109
9077
|
}
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
);
|
|
9078
|
+
const id = asString(item.id) ?? `${itemType}-${announced.size}`;
|
|
9079
|
+
if (itemType === "file_change" && type !== "item.completed") return;
|
|
9080
|
+
if (announced.has(id)) return;
|
|
9081
|
+
announced.add(id);
|
|
9082
|
+
const line = codexItemLine(item, cwd);
|
|
9083
|
+
if (line) emit({ kind: "tool", text: line });
|
|
9084
|
+
return;
|
|
9118
9085
|
}
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
return
|
|
9086
|
+
if (type === "turn.completed" || type === "task_complete") {
|
|
9087
|
+
state.completed = true;
|
|
9088
|
+
const last = asString(ev.last_agent_message);
|
|
9089
|
+
if (!state.text && last) state.text = last;
|
|
9090
|
+
const u = isRecord(ev.usage) ? ev.usage : void 0;
|
|
9091
|
+
if (u) state.tokens = (asNumber(u.input_tokens) ?? 0) + (asNumber(u.output_tokens) ?? 0);
|
|
9092
|
+
return;
|
|
9126
9093
|
}
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
|
|
9130
|
-
|
|
9131
|
-
|
|
9132
|
-
|
|
9133
|
-
|
|
9134
|
-
|
|
9135
|
-
|
|
9136
|
-
|
|
9137
|
-
|
|
9138
|
-
|
|
9139
|
-
|
|
9140
|
-
|
|
9141
|
-
|
|
9142
|
-
async plan(goal, roles, codeTasks, signal) {
|
|
9143
|
-
const user = [
|
|
9144
|
-
`OBJECTIVE:
|
|
9145
|
-
${goal.objective}`,
|
|
9146
|
-
`You may plan at most ${goal.limits.maxTasks} task(s) in total; the team has ${goal.limits.maxRounds} round(s) of plan \u2192 execute \u2192 review.`,
|
|
9147
|
-
await this.sharedContext()
|
|
9148
|
-
].filter(Boolean).join("\n\n");
|
|
9149
|
-
return this.askJson(
|
|
9150
|
-
planSchema,
|
|
9151
|
-
goal,
|
|
9152
|
-
this.roleFor(roles, "planner"),
|
|
9153
|
-
plannerPrompt(roles, codeTasks),
|
|
9154
|
-
user,
|
|
9155
|
-
signal
|
|
9156
|
-
);
|
|
9157
|
-
}
|
|
9158
|
-
async review(goal, roles, signal) {
|
|
9159
|
-
const lines2 = [];
|
|
9160
|
-
for (const task of goal.tasks) {
|
|
9161
|
-
lines2.push(
|
|
9162
|
-
`- [${task.status}] ${task.id} (${task.role}, ${task.kind}, round ${task.round}, ${task.attempts} attempt(s)): ${task.title}`
|
|
9163
|
-
);
|
|
9164
|
-
if (task.notes) lines2.push(` notes: ${truncateHead(task.notes, 500).replace(/\n/g, " ")}`);
|
|
9165
|
-
if (task.artifact) {
|
|
9166
|
-
const text = await this.options.store.readArtifact(goal.id, task.id);
|
|
9167
|
-
if (text)
|
|
9168
|
-
lines2.push(` output excerpt:
|
|
9169
|
-
${indent(truncateHead(text, MAX_REVIEW_EXCERPT))}`);
|
|
9094
|
+
if (type === "turn.failed") {
|
|
9095
|
+
state.failure = codexErrorText(ev.error) ?? lastError ?? "Codex reported an error";
|
|
9096
|
+
return;
|
|
9097
|
+
}
|
|
9098
|
+
if (type === "error") {
|
|
9099
|
+
lastError = codexErrorText(ev.message) ?? codexErrorText(ev.error) ?? lastError;
|
|
9100
|
+
if (lastError) emit({ kind: "error", text: shortError(lastError) });
|
|
9101
|
+
return;
|
|
9102
|
+
}
|
|
9103
|
+
if (type === "agent_message") {
|
|
9104
|
+
const text = (asString(ev.message) ?? "").trim();
|
|
9105
|
+
if (text) {
|
|
9106
|
+
state.text = text;
|
|
9107
|
+
emit({ kind: "text", text: clip(text, MAX_TEXT_CHUNK) });
|
|
9108
|
+
}
|
|
9170
9109
|
}
|
|
9171
9110
|
}
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
if (
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
} catch (err) {
|
|
9211
|
-
if (err instanceof Cancelled || err instanceof OutOfBudget || signal.aborted) throw err;
|
|
9212
|
-
if (err instanceof RouterError && (err.code === "no_capacity" || err.code === "rate_limited")) {
|
|
9213
|
-
throw new OutOfBudget(`no account has capacity left (${err.message})`);
|
|
9111
|
+
};
|
|
9112
|
+
}
|
|
9113
|
+
function agyParser(emit, cwd) {
|
|
9114
|
+
const state = { text: "", completed: false };
|
|
9115
|
+
const announced = /* @__PURE__ */ new Set();
|
|
9116
|
+
let streamed = "";
|
|
9117
|
+
let buffer = "";
|
|
9118
|
+
let bufferStep;
|
|
9119
|
+
const flushText = () => {
|
|
9120
|
+
const text = buffer.trim();
|
|
9121
|
+
buffer = "";
|
|
9122
|
+
bufferStep = void 0;
|
|
9123
|
+
if (text) emit({ kind: "text", text: clip(text, MAX_TEXT_CHUNK) });
|
|
9124
|
+
};
|
|
9125
|
+
return {
|
|
9126
|
+
state,
|
|
9127
|
+
flush: flushText,
|
|
9128
|
+
handle(raw) {
|
|
9129
|
+
if (!isRecord(raw)) return;
|
|
9130
|
+
const event = asString(raw.event);
|
|
9131
|
+
if (event === "init") {
|
|
9132
|
+
emit({ kind: "status", text: "Antigravity started" });
|
|
9133
|
+
return;
|
|
9134
|
+
}
|
|
9135
|
+
if (event === "step_update" && isRecord(raw.step_update)) {
|
|
9136
|
+
const step = raw.step_update;
|
|
9137
|
+
const stepType = asString(step.step_type);
|
|
9138
|
+
const index = String(asNumber(step.step_index) ?? asString(step.step_index) ?? "");
|
|
9139
|
+
if (stepType === "agent_response") {
|
|
9140
|
+
if (bufferStep !== void 0 && bufferStep !== index) flushText();
|
|
9141
|
+
const delta = asString(step.text_delta);
|
|
9142
|
+
if (delta) {
|
|
9143
|
+
bufferStep = index;
|
|
9144
|
+
buffer += delta;
|
|
9145
|
+
streamed += delta;
|
|
9146
|
+
}
|
|
9147
|
+
if (asString(step.state) === "DONE") flushText();
|
|
9148
|
+
return;
|
|
9214
9149
|
}
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9150
|
+
if (stepType === "tool") {
|
|
9151
|
+
flushText();
|
|
9152
|
+
if (announced.has(index)) return;
|
|
9153
|
+
announced.add(index);
|
|
9154
|
+
const info = isRecord(step.tool_info) ? step.tool_info : {};
|
|
9155
|
+
const name = asString(step.tool_name) ?? asString(info.name) ?? "tool";
|
|
9156
|
+
emit({ kind: "tool", text: toolLine(name, info.parameters, cwd) });
|
|
9157
|
+
}
|
|
9158
|
+
return;
|
|
9218
9159
|
}
|
|
9219
|
-
if (
|
|
9220
|
-
|
|
9160
|
+
if (event === "result" && isRecord(raw.result)) {
|
|
9161
|
+
flushText();
|
|
9162
|
+
const result = raw.result;
|
|
9163
|
+
const u = isRecord(result.usage) ? result.usage : void 0;
|
|
9164
|
+
if (u) {
|
|
9165
|
+
state.tokens = (asNumber(u.input_tokens) ?? 0) + (asNumber(u.output_tokens) ?? 0) + (asNumber(u.thinking_tokens) ?? 0);
|
|
9166
|
+
}
|
|
9167
|
+
const status = asString(result.status);
|
|
9168
|
+
if (status !== "SUCCESS") {
|
|
9169
|
+
state.failure = asString(result.error) ?? `Antigravity ended with ${status ?? "an error"}`;
|
|
9170
|
+
return;
|
|
9171
|
+
}
|
|
9172
|
+
state.completed = true;
|
|
9173
|
+
state.text = (asString(result.response) ?? streamed).trim();
|
|
9221
9174
|
}
|
|
9222
|
-
delete goal.activeTaskId;
|
|
9223
|
-
await this.update(goal, {});
|
|
9224
9175
|
}
|
|
9225
|
-
}
|
|
9226
|
-
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9176
|
+
};
|
|
9177
|
+
}
|
|
9178
|
+
var PARSERS = {
|
|
9179
|
+
claude: claudeParser,
|
|
9180
|
+
codex: codexParser,
|
|
9181
|
+
agy: agyParser
|
|
9182
|
+
};
|
|
9183
|
+
function failureMessage(info, detail, evidence, code) {
|
|
9184
|
+
const { kind } = classifyCliText(evidence);
|
|
9185
|
+
if (kind === "auth") return `${info.label} is not signed in: ${info.signIn}`;
|
|
9186
|
+
const clean = scrubSecrets(detail);
|
|
9187
|
+
if (kind === "quota_exhausted") return shortError(`${info.label} usage limit reached: ${clean}`);
|
|
9188
|
+
if (kind === "rate_limit") return shortError(`${info.label} is rate limited: ${clean}`);
|
|
9189
|
+
const exit = code !== null && code !== 0 ? ` (exit ${code})` : "";
|
|
9190
|
+
return shortError(`${info.label} failed${exit}${clean ? `: ${clean}` : ""}`);
|
|
9191
|
+
}
|
|
9192
|
+
function createCliAgentRunner(options = {}) {
|
|
9193
|
+
const env = options.env ?? process.env;
|
|
9194
|
+
const childEnv = {};
|
|
9195
|
+
for (const [k, v] of Object.entries(env)) if (typeof v === "string") childEnv[k] = v;
|
|
9196
|
+
const resolveBinary = (agent) => {
|
|
9197
|
+
const override = options.binaries?.[agent];
|
|
9198
|
+
const command = override?.command ?? AGENTS[agent].binary;
|
|
9199
|
+
return { command: resolveOnPath(command, env), prefix: override?.args ?? [] };
|
|
9200
|
+
};
|
|
9201
|
+
const isAvailable = (agent) => {
|
|
9202
|
+
const { command } = resolveBinary(agent);
|
|
9203
|
+
if (!path8.isAbsolute(command)) return false;
|
|
9204
|
+
try {
|
|
9205
|
+
return existsSync4(command) && statSync3(command).isFile();
|
|
9206
|
+
} catch {
|
|
9207
|
+
return false;
|
|
9233
9208
|
}
|
|
9234
|
-
|
|
9235
|
-
|
|
9236
|
-
|
|
9237
|
-
|
|
9238
|
-
|
|
9239
|
-
|
|
9240
|
-
|
|
9241
|
-
|
|
9242
|
-
parts.push(`OUTPUT OF ${dep.id} (${dep.role}): ${dep.title}
|
|
9243
|
-
${excerpt}`);
|
|
9209
|
+
};
|
|
9210
|
+
async function run(request) {
|
|
9211
|
+
if (request.signal.aborted) throw abortError();
|
|
9212
|
+
const info = AGENTS[request.agent];
|
|
9213
|
+
const { command, prefix } = resolveBinary(request.agent);
|
|
9214
|
+
const args = agentArgs(request.agent, request.mode);
|
|
9215
|
+
if (needsShell(command) && ![...prefix, ...args].every(isShellSafeArg)) {
|
|
9216
|
+
return { ok: false, text: "", error: `${info.label}: unsafe argument for the Windows shell` };
|
|
9244
9217
|
}
|
|
9245
|
-
const
|
|
9246
|
-
|
|
9247
|
-
|
|
9248
|
-
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
const result = await runner.run(task, {
|
|
9257
|
-
goal,
|
|
9258
|
-
maxTokens: goal.limits.maxTokens - goal.limits.tokensUsed,
|
|
9259
|
-
signal
|
|
9260
|
-
});
|
|
9261
|
-
this.addTokens(goal, result.tokens);
|
|
9262
|
-
if (result.runnerTaskId) task.runnerTaskId = result.runnerTaskId;
|
|
9263
|
-
if (result.stopped || signal.aborted) throw new Cancelled();
|
|
9264
|
-
task.status = result.ok ? "SUCCESS" : "FAILED";
|
|
9265
|
-
task.notes = truncateHead(result.summary || (result.ok ? "done" : "failed"), 2e3);
|
|
9266
|
-
}
|
|
9267
|
-
applyRetries(goal, ids) {
|
|
9268
|
-
let count = 0;
|
|
9269
|
-
for (const id of new Set(ids)) {
|
|
9270
|
-
const task = goal.tasks.find((t) => t.id === id);
|
|
9271
|
-
if (task?.status !== "FAILED") continue;
|
|
9272
|
-
task.status = "PENDING";
|
|
9273
|
-
task.attempts = 0;
|
|
9274
|
-
count++;
|
|
9218
|
+
const emit = (chunk) => {
|
|
9219
|
+
try {
|
|
9220
|
+
request.onOutput(chunk);
|
|
9221
|
+
} catch {
|
|
9222
|
+
}
|
|
9223
|
+
};
|
|
9224
|
+
let scratch;
|
|
9225
|
+
let cwd = request.cwd;
|
|
9226
|
+
if (request.agent === "agy" && request.mode === "plan") {
|
|
9227
|
+
scratch = mkdtempSync(path8.join(options.scratchDir ?? tmpdir(), "davecode-agy-plan-"));
|
|
9228
|
+
cwd = scratch;
|
|
9275
9229
|
}
|
|
9276
|
-
|
|
9277
|
-
|
|
9278
|
-
|
|
9279
|
-
|
|
9280
|
-
|
|
9281
|
-
|
|
9282
|
-
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
{ role: "system", content: system },
|
|
9286
|
-
{ role: "user", content: user }
|
|
9287
|
-
];
|
|
9288
|
-
let attachTools = this.options.readTools === true;
|
|
9289
|
-
for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
|
|
9290
|
-
this.checkpoint(goal, signal);
|
|
9291
|
-
const allowTools = attachTools && round < MAX_TOOL_ROUNDS;
|
|
9292
|
-
let completion;
|
|
9230
|
+
const timeout = AbortSignal.timeout(request.timeoutMs);
|
|
9231
|
+
const signal = AbortSignal.any([request.signal, timeout]);
|
|
9232
|
+
const timedOut = () => ({
|
|
9233
|
+
ok: false,
|
|
9234
|
+
text: "",
|
|
9235
|
+
error: `${info.label} timed out after ${formatDuration(request.timeoutMs)}`
|
|
9236
|
+
});
|
|
9237
|
+
try {
|
|
9238
|
+
let cli;
|
|
9293
9239
|
try {
|
|
9294
|
-
|
|
9295
|
-
|
|
9296
|
-
|
|
9297
|
-
|
|
9298
|
-
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
|
|
9240
|
+
cli = runCli({
|
|
9241
|
+
provider: info.provider,
|
|
9242
|
+
accountId: "orchestrator",
|
|
9243
|
+
settings: { command, prefixArgs: prefix },
|
|
9244
|
+
args,
|
|
9245
|
+
env: childEnv,
|
|
9246
|
+
cwd,
|
|
9247
|
+
stdin: agentStdin(request.agent, request.mode, request.prompt),
|
|
9248
|
+
signal
|
|
9249
|
+
});
|
|
9302
9250
|
} catch (err) {
|
|
9303
|
-
if (
|
|
9304
|
-
|
|
9305
|
-
|
|
9306
|
-
} else {
|
|
9307
|
-
throw err;
|
|
9308
|
-
}
|
|
9251
|
+
if (request.signal.aborted) throw abortError();
|
|
9252
|
+
if (timeout.aborted) return timedOut();
|
|
9253
|
+
return { ok: false, text: "", error: shortError(errText(err)) };
|
|
9309
9254
|
}
|
|
9310
|
-
const
|
|
9311
|
-
|
|
9312
|
-
const text = typeof content === "string" ? content : "";
|
|
9313
|
-
const usage = completion.usage;
|
|
9314
|
-
this.addTokens(
|
|
9315
|
-
goal,
|
|
9316
|
-
usage && usage.prompt_tokens + usage.completion_tokens > 0 ? usage.prompt_tokens + usage.completion_tokens : countTextTokens(system) + countTextTokens(user) + countTextTokens(text)
|
|
9317
|
-
);
|
|
9318
|
-
const calls = message?.tool_calls ?? [];
|
|
9319
|
-
if (calls.length === 0 || round === MAX_TOOL_ROUNDS) return text;
|
|
9320
|
-
messages.push({ role: "assistant", content: content ?? null, tool_calls: calls });
|
|
9321
|
-
for (const call of calls) {
|
|
9322
|
-
this.log(goal, "debug", `tool ${call.function.name}`, role.id, goal.activeTaskId);
|
|
9323
|
-
const outcome = await this.tools.call(call.function.name, call.function.arguments, signal);
|
|
9324
|
-
messages.push({
|
|
9325
|
-
role: "tool",
|
|
9326
|
-
tool_call_id: call.id,
|
|
9327
|
-
name: call.function.name,
|
|
9328
|
-
content: outcome.output
|
|
9329
|
-
});
|
|
9330
|
-
}
|
|
9331
|
-
}
|
|
9332
|
-
return "";
|
|
9333
|
-
}
|
|
9334
|
-
/** Asks for a JSON reply matching `schema`; one corrective retry on malformed output. */
|
|
9335
|
-
async askJson(schema, goal, role, system, user, signal) {
|
|
9336
|
-
let problem = "";
|
|
9337
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
9338
|
-
const prompt = attempt === 0 ? user : `${user}
|
|
9339
|
-
|
|
9340
|
-
Your previous reply was rejected (${problem}). Respond with ONLY the JSON object in the required shape.`;
|
|
9341
|
-
const reply = await this.ask(goal, role, system, prompt, signal);
|
|
9255
|
+
const parser = PARSERS[request.agent](emit, cwd);
|
|
9256
|
+
let streamError;
|
|
9342
9257
|
try {
|
|
9343
|
-
const
|
|
9344
|
-
if (parsed.success) return parsed.data;
|
|
9345
|
-
problem = parsed.error.issues.slice(0, 3).map((i) => `${i.path.join(".") || "reply"}: ${i.message}`).join("; ");
|
|
9258
|
+
for await (const event of cli.events) parser.handle(event);
|
|
9346
9259
|
} catch (err) {
|
|
9347
|
-
|
|
9260
|
+
streamError = errText(err);
|
|
9261
|
+
}
|
|
9262
|
+
parser.flush();
|
|
9263
|
+
let exit;
|
|
9264
|
+
try {
|
|
9265
|
+
exit = await cli.exit;
|
|
9266
|
+
} catch {
|
|
9267
|
+
if (request.signal.aborted) throw abortError();
|
|
9268
|
+
return {
|
|
9269
|
+
ok: false,
|
|
9270
|
+
text: "",
|
|
9271
|
+
error: `${info.label} could not be started: is \`${info.binary}\` installed and on PATH?`
|
|
9272
|
+
};
|
|
9273
|
+
}
|
|
9274
|
+
if (request.signal.aborted) throw abortError();
|
|
9275
|
+
if (exit.aborted || timeout.aborted) return { ...timedOut(), text: parser.state.text };
|
|
9276
|
+
const { state } = parser;
|
|
9277
|
+
if (state.completed && !state.failure && exit.code === 0) {
|
|
9278
|
+
return {
|
|
9279
|
+
ok: true,
|
|
9280
|
+
text: state.text,
|
|
9281
|
+
...state.tokens !== void 0 ? { tokens: state.tokens } : {}
|
|
9282
|
+
};
|
|
9283
|
+
}
|
|
9284
|
+
const detail = state.failure ?? streamError ?? (tail(exit.stderr) || (exit.code === 0 ? "the CLI ended without a final result" : "no output"));
|
|
9285
|
+
return {
|
|
9286
|
+
ok: false,
|
|
9287
|
+
text: state.text,
|
|
9288
|
+
error: failureMessage(info, detail, `${detail}
|
|
9289
|
+
${tail(exit.stderr)}`, exit.code),
|
|
9290
|
+
...state.tokens !== void 0 ? { tokens: state.tokens } : {}
|
|
9291
|
+
};
|
|
9292
|
+
} finally {
|
|
9293
|
+
if (scratch) {
|
|
9294
|
+
try {
|
|
9295
|
+
rmSync4(scratch, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
9296
|
+
} catch {
|
|
9297
|
+
}
|
|
9348
9298
|
}
|
|
9349
|
-
this.log(goal, "warn", `invalid JSON from the ${role.id}: ${problem}`, role.id);
|
|
9350
|
-
}
|
|
9351
|
-
throw new Error(`the ${role.id} did not return valid JSON (${problem})`);
|
|
9352
|
-
}
|
|
9353
|
-
async sharedContext(includeProject = true) {
|
|
9354
|
-
const parts = [];
|
|
9355
|
-
if (includeProject && this.options.projectContext) {
|
|
9356
|
-
const project = (await this.options.projectContext()).trim();
|
|
9357
|
-
if (project) parts.push(`PROJECT CONTEXT:
|
|
9358
|
-
${project}`);
|
|
9359
|
-
}
|
|
9360
|
-
if (this.options.budgetContext) {
|
|
9361
|
-
const budget = (await this.options.budgetContext()).trim();
|
|
9362
|
-
if (budget) parts.push(budget);
|
|
9363
9299
|
}
|
|
9364
|
-
return parts.join("\n\n");
|
|
9365
9300
|
}
|
|
9366
|
-
|
|
9367
|
-
|
|
9368
|
-
|
|
9369
|
-
|
|
9370
|
-
|
|
9371
|
-
|
|
9301
|
+
return {
|
|
9302
|
+
async availability() {
|
|
9303
|
+
return ORCHESTRATOR_AGENTS.map((agent) => ({ agent, available: isAvailable(agent) }));
|
|
9304
|
+
},
|
|
9305
|
+
run
|
|
9306
|
+
};
|
|
9307
|
+
}
|
|
9308
|
+
function errText(err) {
|
|
9309
|
+
return err instanceof Error ? err.message : String(err);
|
|
9310
|
+
}
|
|
9311
|
+
function tokens(out) {
|
|
9312
|
+
const parts = out.split("\0");
|
|
9313
|
+
if (parts.at(-1) === "") parts.pop();
|
|
9314
|
+
return parts;
|
|
9315
|
+
}
|
|
9316
|
+
function statusOf(letter) {
|
|
9317
|
+
switch (letter[0]) {
|
|
9318
|
+
case "A":
|
|
9319
|
+
return "added";
|
|
9320
|
+
case "D":
|
|
9321
|
+
return "deleted";
|
|
9322
|
+
case "R":
|
|
9323
|
+
return "renamed";
|
|
9324
|
+
case "C":
|
|
9325
|
+
return "added";
|
|
9326
|
+
default:
|
|
9327
|
+
return "modified";
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
function parseNameStatus(out) {
|
|
9331
|
+
const parts = tokens(out);
|
|
9332
|
+
const files = [];
|
|
9333
|
+
for (let i = 0; i < parts.length; ) {
|
|
9334
|
+
const code = parts[i++] ?? "";
|
|
9335
|
+
if (!code) continue;
|
|
9336
|
+
if (code[0] === "R" || code[0] === "C") {
|
|
9337
|
+
const oldPath = parts[i++] ?? "";
|
|
9338
|
+
const path11 = parts[i++] ?? "";
|
|
9339
|
+
files.push(
|
|
9340
|
+
code[0] === "R" ? { path: path11, oldPath, status: "renamed" } : { path: path11, status: "added" }
|
|
9372
9341
|
);
|
|
9342
|
+
} else {
|
|
9343
|
+
files.push({ path: parts[i++] ?? "", status: statusOf(code) });
|
|
9373
9344
|
}
|
|
9374
9345
|
}
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9384
|
-
|
|
9385
|
-
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
log(goal, level, message, role, taskId) {
|
|
9396
|
-
this.options.events.emit({
|
|
9397
|
-
type: "goal.log",
|
|
9398
|
-
goalId: goal.id,
|
|
9399
|
-
level,
|
|
9400
|
-
message,
|
|
9401
|
-
...role ? { role } : {},
|
|
9402
|
-
...taskId ? { taskId } : {}
|
|
9346
|
+
return files.filter((f) => f.path.length > 0);
|
|
9347
|
+
}
|
|
9348
|
+
function parseNumStat(out) {
|
|
9349
|
+
const parts = tokens(out);
|
|
9350
|
+
const stats = /* @__PURE__ */ new Map();
|
|
9351
|
+
for (let i = 0; i < parts.length; ) {
|
|
9352
|
+
const head = parts[i++] ?? "";
|
|
9353
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.*)$/s.exec(head);
|
|
9354
|
+
if (!match) continue;
|
|
9355
|
+
const [, added = "0", deleted = "0", inline = ""] = match;
|
|
9356
|
+
let path11 = inline;
|
|
9357
|
+
if (inline === "") {
|
|
9358
|
+
i++;
|
|
9359
|
+
path11 = parts[i++] ?? "";
|
|
9360
|
+
}
|
|
9361
|
+
const binary = added === "-" || deleted === "-";
|
|
9362
|
+
stats.set(path11, {
|
|
9363
|
+
additions: binary ? 0 : Number(added),
|
|
9364
|
+
deletions: binary ? 0 : Number(deleted),
|
|
9365
|
+
binary
|
|
9403
9366
|
});
|
|
9404
9367
|
}
|
|
9405
|
-
|
|
9406
|
-
function readyGoalTasks(goal) {
|
|
9407
|
-
const status = new Map(goal.tasks.map((t) => [t.id, t.status]));
|
|
9408
|
-
return goal.tasks.filter(
|
|
9409
|
-
(t) => t.status === "PENDING" && t.dependsOn.every((d) => status.get(d) === "SUCCESS")
|
|
9410
|
-
);
|
|
9368
|
+
return stats;
|
|
9411
9369
|
}
|
|
9412
|
-
function
|
|
9413
|
-
|
|
9370
|
+
function mergeFileLists(tracked, stats, untracked) {
|
|
9371
|
+
const files = tracked.map((file) => {
|
|
9372
|
+
const stat4 = stats.get(file.path) ?? { additions: 0, deletions: 0, binary: false };
|
|
9373
|
+
return { ...file, ...stat4 };
|
|
9374
|
+
});
|
|
9375
|
+
const seen = new Set(files.map((f) => f.path));
|
|
9376
|
+
for (const file of untracked) if (!seen.has(file.path)) files.push(file);
|
|
9377
|
+
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
9414
9378
|
}
|
|
9415
|
-
function
|
|
9416
|
-
|
|
9417
|
-
return match?.[1] ?? text;
|
|
9379
|
+
function looksBinary(content) {
|
|
9380
|
+
return content.subarray(0, 8e3).includes(0);
|
|
9418
9381
|
}
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9382
|
+
function countLines(text) {
|
|
9383
|
+
if (text.length === 0) return 0;
|
|
9384
|
+
const breaks = text.split("\n").length - 1;
|
|
9385
|
+
return text.endsWith("\n") ? breaks : breaks + 1;
|
|
9386
|
+
}
|
|
9387
|
+
function newFilePatch(path11, content) {
|
|
9388
|
+
const header = `diff --git a/${path11} b/${path11}
|
|
9389
|
+
new file mode 100644
|
|
9390
|
+
`;
|
|
9391
|
+
if (!content || looksBinary(content)) {
|
|
9392
|
+
return `${header}Binary files /dev/null and b/${path11} differ
|
|
9393
|
+
`;
|
|
9425
9394
|
}
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
|
|
9429
|
-
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9395
|
+
const text = content.toString("utf8");
|
|
9396
|
+
if (text.length === 0) return header;
|
|
9397
|
+
const lines2 = text.split("\n");
|
|
9398
|
+
const trailingNewline = text.endsWith("\n");
|
|
9399
|
+
if (trailingNewline) lines2.pop();
|
|
9400
|
+
const body = lines2.map((line) => `+${line}`).join("\n");
|
|
9401
|
+
const hunk = `@@ -0,0 +1,${lines2.length} @@
|
|
9402
|
+
${body}
|
|
9403
|
+
`;
|
|
9404
|
+
return `${header}--- /dev/null
|
|
9405
|
+
+++ b/${path11}
|
|
9406
|
+
${hunk}${trailingNewline ? "" : "\\n"}`;
|
|
9407
|
+
}
|
|
9408
|
+
function slugify(text, max = 40) {
|
|
9409
|
+
const slug = text.toLowerCase().normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/^[a-z]+(\([^)]*\))?!?:\s*/, "").replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, max).replace(/-+$/g, "");
|
|
9410
|
+
return slug || "changes";
|
|
9411
|
+
}
|
|
9412
|
+
function scrubUrls(text) {
|
|
9413
|
+
return text.replace(/(\b[a-z][a-z0-9+.-]*:\/\/)[^/@\s]+@/gi, "$1***@");
|
|
9414
|
+
}
|
|
9415
|
+
var DOC = /(^|\/)docs?\/|\.(md|mdx|txt|rst)$/i;
|
|
9416
|
+
var TEST = /(^|\/)(__tests__|tests?)\/|\.(test|spec)\.[a-z0-9]+$/i;
|
|
9417
|
+
var PACKAGE = /^(?:packages|apps|libs|services)\/([^/]+)\//;
|
|
9418
|
+
var MAX_LISTED = 30;
|
|
9419
|
+
function basename3(path11) {
|
|
9420
|
+
return path11.split("/").at(-1) ?? path11;
|
|
9421
|
+
}
|
|
9422
|
+
function commitType(files) {
|
|
9423
|
+
if (files.every((f) => DOC.test(f.path))) return "docs";
|
|
9424
|
+
if (files.every((f) => TEST.test(f.path))) return "test";
|
|
9425
|
+
return "chore";
|
|
9426
|
+
}
|
|
9427
|
+
function commitScope(files) {
|
|
9428
|
+
const scopes = new Set(files.map((f) => PACKAGE.exec(f.path)?.[1]));
|
|
9429
|
+
if (scopes.size !== 1) return void 0;
|
|
9430
|
+
const [scope] = scopes;
|
|
9431
|
+
return scope;
|
|
9432
|
+
}
|
|
9433
|
+
function subject(files) {
|
|
9434
|
+
if (files.length === 1) {
|
|
9435
|
+
const [file] = files;
|
|
9436
|
+
const verb = file.status === "added" || file.status === "untracked" ? "add" : file.status === "deleted" ? "remove" : file.status === "renamed" ? "rename" : "update";
|
|
9437
|
+
return `${verb} ${basename3(file.path)}`;
|
|
9438
|
+
}
|
|
9439
|
+
return `update ${files.length} files`;
|
|
9440
|
+
}
|
|
9441
|
+
function fileLine(file) {
|
|
9442
|
+
const counts = file.binary ? "binary" : `+${file.additions} \u2212${file.deletions}`;
|
|
9443
|
+
const from = file.oldPath ? ` (from \`${file.oldPath}\`)` : "";
|
|
9444
|
+
return `- \`${file.path}\`${from} \u2014 ${counts}`;
|
|
9445
|
+
}
|
|
9446
|
+
function fallbackSuggestion(files) {
|
|
9447
|
+
if (files.length === 0) {
|
|
9448
|
+
return { message: "chore: update", title: "chore: update", body: "", by: "davecode" };
|
|
9449
|
+
}
|
|
9450
|
+
const scope = commitScope(files);
|
|
9451
|
+
const message = `${commitType(files)}${scope ? `(${scope})` : ""}: ${subject(files)}`;
|
|
9452
|
+
const listed = files.slice(0, MAX_LISTED).map(fileLine);
|
|
9453
|
+
if (files.length > MAX_LISTED) listed.push(`- \u2026and ${files.length - MAX_LISTED} more`);
|
|
9454
|
+
const body = `## Changes
|
|
9455
|
+
|
|
9456
|
+
${listed.join("\n")}
|
|
9457
|
+
`;
|
|
9458
|
+
return { message, title: message, body, by: "davecode" };
|
|
9459
|
+
}
|
|
9460
|
+
function agentPrompt(input) {
|
|
9461
|
+
const max = CHANGES_LIMITS.suggestDiffMaxLength;
|
|
9462
|
+
const patch = input.patch.length > max ? `${input.patch.slice(0, max)}
|
|
9463
|
+
[patch truncated]` : input.patch;
|
|
9464
|
+
const files = input.files.map(
|
|
9465
|
+
(f) => `${f.status} ${f.path}${f.binary ? " (binary)" : ` (+${f.additions} -${f.deletions})`}`
|
|
9466
|
+
).join("\n");
|
|
9467
|
+
return [
|
|
9468
|
+
"Write the git commit message and pull request text for the changes below.",
|
|
9469
|
+
"Do not modify any file and do not run any command: only read the patch.",
|
|
9470
|
+
"Reply with a single JSON object and nothing else (no code fence):",
|
|
9471
|
+
'{"message": "<Conventional Commits subject, imperative mood, at most 72 characters>",',
|
|
9472
|
+
' "title": "<pull request title, at most 72 characters>",',
|
|
9473
|
+
' "body": "<pull request description in Markdown: one short paragraph, then a bullet list of the main changes>"}',
|
|
9474
|
+
"Write in the same language as the code comments; default to English.",
|
|
9475
|
+
"",
|
|
9476
|
+
`Branch: ${input.branch ?? "(detached)"}`,
|
|
9477
|
+
"Changed files:",
|
|
9478
|
+
files,
|
|
9479
|
+
"",
|
|
9480
|
+
"Patch:",
|
|
9481
|
+
patch
|
|
9482
|
+
].join("\n");
|
|
9483
|
+
}
|
|
9484
|
+
var agentReplySchema = z12.object({
|
|
9485
|
+
message: z12.string().trim().min(1).max(CHANGES_LIMITS.messageMaxLength),
|
|
9486
|
+
title: z12.string().trim().min(1).max(CHANGES_LIMITS.titleMaxLength),
|
|
9487
|
+
body: z12.string().max(CHANGES_LIMITS.bodyMaxLength).default("")
|
|
9488
|
+
});
|
|
9489
|
+
function parseAgentSuggestion(text) {
|
|
9490
|
+
const start = text.indexOf("{");
|
|
9491
|
+
const end = text.lastIndexOf("}");
|
|
9492
|
+
if (start < 0 || end <= start) return void 0;
|
|
9493
|
+
let json;
|
|
9494
|
+
try {
|
|
9495
|
+
json = JSON.parse(text.slice(start, end + 1));
|
|
9496
|
+
} catch {
|
|
9497
|
+
return void 0;
|
|
9435
9498
|
}
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9441
|
-
|
|
9499
|
+
const parsed = agentReplySchema.safeParse(json);
|
|
9500
|
+
return parsed.success ? parsed.data : void 0;
|
|
9501
|
+
}
|
|
9502
|
+
var EMPTY_TREE = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
9503
|
+
var FALLBACK_IDENTITY = ["-c", "user.name=DaveCode", "-c", "user.email=davecode@localhost"];
|
|
9504
|
+
var SLOW_MS = 10 * 6e4;
|
|
9505
|
+
function paneWorktreesDir(home, projectDir) {
|
|
9506
|
+
const key = createHash3("sha256").update(path9.resolve(projectDir)).digest("hex").slice(0, 16);
|
|
9507
|
+
return path9.join(home, "panes", key);
|
|
9508
|
+
}
|
|
9509
|
+
function paneWorkspacePath(home, projectDir, id) {
|
|
9510
|
+
if (!WORKSPACE_ID_PATTERN.test(id)) return void 0;
|
|
9511
|
+
return path9.join(paneWorktreesDir(home, projectDir), id);
|
|
9512
|
+
}
|
|
9513
|
+
var metaSchema = z13.object({
|
|
9514
|
+
id: z13.string().regex(WORKSPACE_ID_PATTERN),
|
|
9515
|
+
label: z13.string(),
|
|
9516
|
+
baseBranch: z13.string().optional(),
|
|
9517
|
+
baseCommit: z13.string(),
|
|
9518
|
+
createdAt: z13.string()
|
|
9519
|
+
});
|
|
9520
|
+
function short(err) {
|
|
9521
|
+
const raw = err instanceof GitError ? err.stderr || err.message : err instanceof Error ? err.message : String(err);
|
|
9522
|
+
const text = scrubSecrets(scrubUrls(raw));
|
|
9523
|
+
return text.length <= 300 ? text : `${text.slice(0, 299)}\u2026`;
|
|
9524
|
+
}
|
|
9525
|
+
function nul(out) {
|
|
9526
|
+
return out.split("\0").filter((f) => f.length > 0);
|
|
9527
|
+
}
|
|
9528
|
+
function samePath(a, b) {
|
|
9529
|
+
const real = (p) => {
|
|
9530
|
+
try {
|
|
9531
|
+
return realpathSync2.native(p);
|
|
9532
|
+
} catch {
|
|
9533
|
+
return path9.resolve(p);
|
|
9534
|
+
}
|
|
9535
|
+
};
|
|
9536
|
+
const [x, y] = [real(a), real(b)];
|
|
9537
|
+
return process.platform === "win32" ? x.toLowerCase() === y.toLowerCase() : x === y;
|
|
9538
|
+
}
|
|
9539
|
+
function createChangesService(options) {
|
|
9540
|
+
const projectDir = path9.resolve(options.projectDir);
|
|
9541
|
+
const panesDir = paneWorktreesDir(options.home, projectDir);
|
|
9542
|
+
const env = options.env ?? process.env;
|
|
9543
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
9544
|
+
const gitAt = (cwd, slow = false) => new Git(cwd, { env, ...slow ? { timeoutMs: SLOW_MS } : {} });
|
|
9545
|
+
const repo = gitAt(projectDir);
|
|
9546
|
+
let queue = Promise.resolve();
|
|
9547
|
+
const serial = (fn2) => {
|
|
9548
|
+
const next = queue.then(fn2, fn2);
|
|
9549
|
+
queue = next.catch(() => void 0);
|
|
9550
|
+
return next;
|
|
9551
|
+
};
|
|
9552
|
+
const identity = /* @__PURE__ */ new Map();
|
|
9553
|
+
async function identityArgs(g) {
|
|
9554
|
+
if (identity.get(g.cwd)) return [];
|
|
9555
|
+
try {
|
|
9556
|
+
await g.run(["var", "GIT_AUTHOR_IDENT"]);
|
|
9557
|
+
await g.run(["var", "GIT_COMMITTER_IDENT"]);
|
|
9558
|
+
identity.set(g.cwd, true);
|
|
9559
|
+
return [];
|
|
9560
|
+
} catch {
|
|
9561
|
+
return FALLBACK_IDENTITY;
|
|
9562
|
+
}
|
|
9442
9563
|
}
|
|
9443
|
-
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
if (this.active) {
|
|
9447
|
-
throw new GoalError("busy", `goal ${this.active.goal.id} is still running; cancel it first`);
|
|
9564
|
+
async function assertRepo() {
|
|
9565
|
+
if (!await repo.isRepo()) {
|
|
9566
|
+
throw new ChangesError("not_a_repository", "This folder is not a git repository.");
|
|
9448
9567
|
}
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
`the working tree is busy (${lock.holder ?? "another task"} is running)`
|
|
9454
|
-
);
|
|
9568
|
+
try {
|
|
9569
|
+
return (await repo.run(["rev-parse", "--verify", "--quiet", "HEAD^{commit}"])).trim();
|
|
9570
|
+
} catch {
|
|
9571
|
+
throw new ChangesError("not_a_repository", "This repository has no commits yet.");
|
|
9455
9572
|
}
|
|
9573
|
+
}
|
|
9574
|
+
const metaFile = (id) => path9.join(panesDir, `${id}.json`);
|
|
9575
|
+
function readMeta(id) {
|
|
9456
9576
|
try {
|
|
9457
|
-
const
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
return
|
|
9461
|
-
} catch (err) {
|
|
9462
|
-
lock?.release(this.owner);
|
|
9463
|
-
throw err;
|
|
9577
|
+
const parsed = metaSchema.safeParse(JSON.parse(readFileSync4(metaFile(id), "utf8")));
|
|
9578
|
+
return parsed.success && parsed.data.id === id ? parsed.data : void 0;
|
|
9579
|
+
} catch {
|
|
9580
|
+
return void 0;
|
|
9464
9581
|
}
|
|
9465
9582
|
}
|
|
9466
|
-
|
|
9467
|
-
|
|
9468
|
-
|
|
9469
|
-
|
|
9470
|
-
|
|
9471
|
-
if (
|
|
9472
|
-
|
|
9583
|
+
function isolatedIds() {
|
|
9584
|
+
if (!existsSync5(panesDir)) return [];
|
|
9585
|
+
return readdirSync(panesDir).filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -5)).filter((id) => WORKSPACE_ID_PATTERN.test(id) && existsSync5(path9.join(panesDir, id)));
|
|
9586
|
+
}
|
|
9587
|
+
function target(id) {
|
|
9588
|
+
if (id === MAIN_WORKSPACE_ID) {
|
|
9589
|
+
return { id, label: path9.basename(projectDir), path: projectDir, isolated: false };
|
|
9473
9590
|
}
|
|
9474
|
-
const
|
|
9475
|
-
|
|
9476
|
-
if (
|
|
9477
|
-
|
|
9478
|
-
|
|
9479
|
-
|
|
9480
|
-
|
|
9481
|
-
|
|
9482
|
-
|
|
9483
|
-
|
|
9484
|
-
|
|
9485
|
-
"busy",
|
|
9486
|
-
`the working tree is busy (${lock.holder ?? "another task"} is running)`
|
|
9487
|
-
);
|
|
9488
|
-
}
|
|
9489
|
-
try {
|
|
9490
|
-
delete goal.dryRun;
|
|
9491
|
-
delete goal.finishedAt;
|
|
9492
|
-
delete goal.lastError;
|
|
9493
|
-
goal.status = "running";
|
|
9494
|
-
goal.updatedAt = this.now().toISOString();
|
|
9495
|
-
await this.options.store.save(goal);
|
|
9496
|
-
this.launch(goal, lock);
|
|
9497
|
-
return structuredClone(goal);
|
|
9498
|
-
} catch (err) {
|
|
9499
|
-
lock?.release(this.owner);
|
|
9500
|
-
throw err;
|
|
9591
|
+
const meta = WORKSPACE_ID_PATTERN.test(id) ? readMeta(id) : void 0;
|
|
9592
|
+
const dir = path9.join(panesDir, id);
|
|
9593
|
+
if (!meta || !existsSync5(dir)) throw new ChangesError("not_found", `unknown workspace "${id}"`);
|
|
9594
|
+
return { id, label: meta.label, path: dir, isolated: true, meta };
|
|
9595
|
+
}
|
|
9596
|
+
async function optional(g, args) {
|
|
9597
|
+
try {
|
|
9598
|
+
const out = (await g.run(args)).trim();
|
|
9599
|
+
return out || void 0;
|
|
9600
|
+
} catch {
|
|
9601
|
+
return void 0;
|
|
9501
9602
|
}
|
|
9502
9603
|
}
|
|
9503
|
-
|
|
9504
|
-
|
|
9505
|
-
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
|
|
9513
|
-
|
|
9604
|
+
async function count(g, range) {
|
|
9605
|
+
const out = await optional(g, ["rev-list", "--count", ...range]);
|
|
9606
|
+
return out ? Number(out) || 0 : 0;
|
|
9607
|
+
}
|
|
9608
|
+
async function diffBase(g, t) {
|
|
9609
|
+
const head = await optional(g, ["rev-parse", "--verify", "--quiet", "HEAD^{commit}"]);
|
|
9610
|
+
if (!head) return EMPTY_TREE;
|
|
9611
|
+
if (!t.isolated || !t.meta) return head;
|
|
9612
|
+
if (t.meta.baseBranch) {
|
|
9613
|
+
const base = await optional(g, ["merge-base", `refs/heads/${t.meta.baseBranch}`, "HEAD"]);
|
|
9614
|
+
if (base) return base;
|
|
9514
9615
|
}
|
|
9515
|
-
return
|
|
9616
|
+
return await optional(g, ["merge-base", t.meta.baseCommit, "HEAD"]) ?? t.meta.baseCommit;
|
|
9516
9617
|
}
|
|
9517
|
-
|
|
9518
|
-
const
|
|
9519
|
-
|
|
9520
|
-
|
|
9521
|
-
|
|
9522
|
-
return
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9618
|
+
async function untrackedFiles(g, root) {
|
|
9619
|
+
const paths = nul(await g.run(["ls-files", "--others", "--exclude-standard", "-z"]));
|
|
9620
|
+
return paths.map((rel) => {
|
|
9621
|
+
const content = readUntracked(root, rel);
|
|
9622
|
+
const binary = !content || looksBinary(content);
|
|
9623
|
+
return {
|
|
9624
|
+
path: rel,
|
|
9625
|
+
status: "untracked",
|
|
9626
|
+
additions: binary ? 0 : countLines(content.toString("utf8")),
|
|
9627
|
+
deletions: 0,
|
|
9628
|
+
binary
|
|
9629
|
+
};
|
|
9526
9630
|
});
|
|
9527
|
-
this.active = { goal, controller, done };
|
|
9528
9631
|
}
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9632
|
+
function readUntracked(root, rel) {
|
|
9633
|
+
const file = path9.join(root, rel);
|
|
9634
|
+
try {
|
|
9635
|
+
if (statSync4(file).size > CHANGES_LIMITS.untrackedMaxBytes) return void 0;
|
|
9636
|
+
return readFileSync4(file);
|
|
9637
|
+
} catch {
|
|
9638
|
+
return void 0;
|
|
9639
|
+
}
|
|
9532
9640
|
}
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
const
|
|
9536
|
-
const
|
|
9537
|
-
|
|
9538
|
-
|
|
9539
|
-
|
|
9641
|
+
async function changedFiles(g, t) {
|
|
9642
|
+
const base = await diffBase(g, t);
|
|
9643
|
+
const common = ["-c", "core.quotepath=false", "diff", "--no-ext-diff", "-M", "-z"];
|
|
9644
|
+
const [names, stats, untracked] = await Promise.all([
|
|
9645
|
+
g.run([...common, "--name-status", base]),
|
|
9646
|
+
g.run([...common, "--numstat", base]),
|
|
9647
|
+
untrackedFiles(g, t.path)
|
|
9648
|
+
]);
|
|
9649
|
+
return { base, files: mergeFileLists(parseNameStatus(names), parseNumStat(stats), untracked) };
|
|
9650
|
+
}
|
|
9651
|
+
async function describe(t) {
|
|
9652
|
+
const g = gitAt(t.path);
|
|
9653
|
+
const [branch, { files }, porcelain, hasRemote] = await Promise.all([
|
|
9654
|
+
optional(g, ["symbolic-ref", "--quiet", "--short", "HEAD"]),
|
|
9655
|
+
changedFiles(g, t),
|
|
9656
|
+
g.run(["status", "--porcelain", "-z", "--untracked-files=all"]),
|
|
9657
|
+
g.hasRemote()
|
|
9658
|
+
]);
|
|
9659
|
+
const upstream = branch ? await optional(g, ["rev-parse", "--abbrev-ref", "--symbolic-full-name", "@{u}"]) : void 0;
|
|
9660
|
+
let ahead = 0;
|
|
9661
|
+
let unpushed = 0;
|
|
9662
|
+
if (t.isolated && t.meta) {
|
|
9663
|
+
const base = t.meta.baseBranch ? `refs/heads/${t.meta.baseBranch}` : t.meta.baseCommit;
|
|
9664
|
+
ahead = await count(g, [`${base}..HEAD`]);
|
|
9665
|
+
}
|
|
9666
|
+
if (upstream) unpushed = await count(g, ["@{u}..HEAD"]);
|
|
9667
|
+
else if (t.isolated) unpushed = ahead;
|
|
9668
|
+
else if (hasRemote) unpushed = await count(g, ["HEAD", "--not", "--remotes"]);
|
|
9669
|
+
if (!t.isolated) ahead = unpushed;
|
|
9670
|
+
return {
|
|
9671
|
+
id: t.id,
|
|
9672
|
+
label: t.label,
|
|
9673
|
+
isolated: t.isolated,
|
|
9674
|
+
path: t.path,
|
|
9675
|
+
...branch ? { branch } : {},
|
|
9676
|
+
...t.meta?.baseBranch ? { baseBranch: t.meta.baseBranch } : {},
|
|
9677
|
+
files,
|
|
9678
|
+
additions: files.reduce((sum, f) => sum + f.additions, 0),
|
|
9679
|
+
deletions: files.reduce((sum, f) => sum + f.deletions, 0),
|
|
9680
|
+
dirty: porcelain.length > 0,
|
|
9681
|
+
ahead,
|
|
9682
|
+
...upstream ? { upstream } : {},
|
|
9683
|
+
unpushed,
|
|
9684
|
+
hasRemote,
|
|
9685
|
+
...t.meta ? { createdAt: t.meta.createdAt } : {}
|
|
9686
|
+
};
|
|
9540
9687
|
}
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
async cancel(id) {
|
|
9546
|
-
if (this.active?.goal.id === id) {
|
|
9547
|
-
this.active.controller.abort();
|
|
9548
|
-
return structuredClone(await this.active.done);
|
|
9688
|
+
async function uniqueBranch(g, wanted) {
|
|
9689
|
+
for (let n = 1; n < 100; n++) {
|
|
9690
|
+
const name = n === 1 ? wanted : `${wanted}-${n}`;
|
|
9691
|
+
if (!await g.branchExists(name)) return name;
|
|
9549
9692
|
}
|
|
9550
|
-
|
|
9551
|
-
if (!goal) throw new GoalError("not_found", `unknown goal "${id}"`);
|
|
9552
|
-
if (isTerminalGoal(goal)) return goal;
|
|
9553
|
-
const stamp = this.now().toISOString();
|
|
9554
|
-
goal.status = "cancelled";
|
|
9555
|
-
goal.updatedAt = stamp;
|
|
9556
|
-
goal.finishedAt = stamp;
|
|
9557
|
-
delete goal.activeTaskId;
|
|
9558
|
-
await this.options.store.save(goal);
|
|
9559
|
-
return goal;
|
|
9693
|
+
return `${wanted}-${randomBytes9(3).toString("hex")}`;
|
|
9560
9694
|
}
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
9571
|
-
description: z11.string().optional(),
|
|
9572
|
-
role: z11.string(),
|
|
9573
|
-
kind: z11.enum(["code", "artifact"]),
|
|
9574
|
-
status: taskStatusSchema,
|
|
9575
|
-
dependsOn: z11.array(z11.string()),
|
|
9576
|
-
acceptance: z11.array(z11.string()).optional(),
|
|
9577
|
-
round: z11.number().int().min(1),
|
|
9578
|
-
attempts: z11.number().int().min(0),
|
|
9579
|
-
artifact: z11.string().optional(),
|
|
9580
|
-
runnerTaskId: z11.string().optional(),
|
|
9581
|
-
notes: z11.string().optional()
|
|
9582
|
-
});
|
|
9583
|
-
var goalSchema = z11.object({
|
|
9584
|
-
id: z11.string().regex(GOAL_ID),
|
|
9585
|
-
objective: z11.string().min(1),
|
|
9586
|
-
status: z11.enum([
|
|
9587
|
-
"planning",
|
|
9588
|
-
"running",
|
|
9589
|
-
"reviewing",
|
|
9590
|
-
"planned",
|
|
9591
|
-
"succeeded",
|
|
9592
|
-
"failed",
|
|
9593
|
-
"cancelled",
|
|
9594
|
-
"budget_exhausted"
|
|
9595
|
-
]),
|
|
9596
|
-
round: z11.number().int().min(0),
|
|
9597
|
-
limits: z11.object({
|
|
9598
|
-
maxRounds: z11.number().int().min(1),
|
|
9599
|
-
maxTasks: z11.number().int().min(1),
|
|
9600
|
-
maxTokens: z11.number().int().min(1),
|
|
9601
|
-
tokensUsed: z11.number().int().min(0)
|
|
9602
|
-
}),
|
|
9603
|
-
roles: z11.array(
|
|
9604
|
-
z11.object({ id: z11.string(), description: z11.string(), route: z11.string().optional() })
|
|
9605
|
-
),
|
|
9606
|
-
tasks: z11.array(goalTaskSchema),
|
|
9607
|
-
activeTaskId: z11.string().optional(),
|
|
9608
|
-
summary: z11.string().optional(),
|
|
9609
|
-
lastError: z11.string().optional(),
|
|
9610
|
-
dryRun: z11.boolean().optional(),
|
|
9611
|
-
createdAt: z11.string(),
|
|
9612
|
-
updatedAt: z11.string(),
|
|
9613
|
-
finishedAt: z11.string().optional()
|
|
9614
|
-
});
|
|
9615
|
-
function isGoalId(id) {
|
|
9616
|
-
return GOAL_ID.test(id);
|
|
9617
|
-
}
|
|
9618
|
-
var GoalStore = class {
|
|
9619
|
-
constructor(root) {
|
|
9620
|
-
this.root = root;
|
|
9621
|
-
this.dir = join7(projectPaths(root).dir, "goals");
|
|
9695
|
+
async function defaultBranch(g) {
|
|
9696
|
+
const head = await optional(g, [
|
|
9697
|
+
"symbolic-ref",
|
|
9698
|
+
"--quiet",
|
|
9699
|
+
"--short",
|
|
9700
|
+
"refs/remotes/origin/HEAD"
|
|
9701
|
+
]);
|
|
9702
|
+
if (head?.startsWith("origin/")) return head.slice("origin/".length);
|
|
9703
|
+
for (const name of ["main", "master"]) if (await g.branchExists(name)) return name;
|
|
9704
|
+
return void 0;
|
|
9622
9705
|
}
|
|
9623
|
-
|
|
9624
|
-
|
|
9625
|
-
|
|
9626
|
-
if (
|
|
9627
|
-
|
|
9706
|
+
function runGh(cwd, args) {
|
|
9707
|
+
if (options.runGh) return options.runGh(cwd, args);
|
|
9708
|
+
const gh = resolveOnPath(options.gh ?? "gh", env);
|
|
9709
|
+
if (/\.(cmd|bat)$/i.test(gh)) {
|
|
9710
|
+
return Promise.reject(new Error("gh is a shell shim; DaveCode will not pass text to it"));
|
|
9711
|
+
}
|
|
9712
|
+
return new Promise((resolve6, reject) => {
|
|
9713
|
+
execFile2(
|
|
9714
|
+
gh,
|
|
9715
|
+
args,
|
|
9716
|
+
{
|
|
9717
|
+
cwd,
|
|
9718
|
+
env: { ...env, GH_PROMPT_DISABLED: "1" },
|
|
9719
|
+
timeout: SLOW_MS,
|
|
9720
|
+
windowsHide: true,
|
|
9721
|
+
encoding: "utf8"
|
|
9722
|
+
},
|
|
9723
|
+
(err, stdout, stderr) => {
|
|
9724
|
+
if (err) {
|
|
9725
|
+
const missing = err.code === "ENOENT";
|
|
9726
|
+
reject(
|
|
9727
|
+
new Error(
|
|
9728
|
+
missing ? "GitHub CLI (gh) is not installed. Install it and run `gh auth login`." : (stderr || err.message).trim()
|
|
9729
|
+
)
|
|
9730
|
+
);
|
|
9731
|
+
} else resolve6(stdout.trim());
|
|
9732
|
+
}
|
|
9733
|
+
);
|
|
9734
|
+
});
|
|
9628
9735
|
}
|
|
9629
|
-
async
|
|
9630
|
-
await
|
|
9631
|
-
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
);
|
|
9736
|
+
async function commitAll(g, message) {
|
|
9737
|
+
await g.run(["add", "-A"]);
|
|
9738
|
+
if (!await g.hasStagedChanges()) return void 0;
|
|
9739
|
+
await g.run([...await identityArgs(g), "commit", "--quiet", "-m", message]);
|
|
9740
|
+
return (await g.run(["rev-parse", "--short", "HEAD"])).trim();
|
|
9635
9741
|
}
|
|
9636
|
-
|
|
9637
|
-
|
|
9638
|
-
|
|
9639
|
-
|
|
9640
|
-
if (text === void 0) return void 0;
|
|
9641
|
-
return goalSchema.parse(JSON.parse(text));
|
|
9742
|
+
function writeMeta(meta) {
|
|
9743
|
+
mkdirSync8(panesDir, { recursive: true });
|
|
9744
|
+
writeFileSync4(metaFile(meta.id), `${JSON.stringify(meta, null, 2)}
|
|
9745
|
+
`);
|
|
9642
9746
|
}
|
|
9643
|
-
|
|
9644
|
-
async list() {
|
|
9645
|
-
let names;
|
|
9747
|
+
async function removeWorktree(dir) {
|
|
9646
9748
|
try {
|
|
9647
|
-
|
|
9648
|
-
} catch
|
|
9649
|
-
if (isNodeError(err, "ENOENT")) return [];
|
|
9650
|
-
throw err;
|
|
9749
|
+
await repo.run(["worktree", "remove", "--force", dir]);
|
|
9750
|
+
} catch {
|
|
9651
9751
|
}
|
|
9652
|
-
|
|
9653
|
-
|
|
9654
|
-
|
|
9655
|
-
|
|
9656
|
-
|
|
9657
|
-
} catch {
|
|
9658
|
-
}
|
|
9752
|
+
if (existsSync5(dir))
|
|
9753
|
+
rmSync5(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
9754
|
+
try {
|
|
9755
|
+
await repo.run(["worktree", "prune"]);
|
|
9756
|
+
} catch {
|
|
9659
9757
|
}
|
|
9660
|
-
return goals.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
9661
|
-
}
|
|
9662
|
-
/** Writes a task's markdown artifact and returns its repository-relative path (`/`-separated). */
|
|
9663
|
-
async writeArtifact(goalId, taskId, markdown) {
|
|
9664
|
-
const path10 = join7(this.goalDir(goalId), `${taskId}.md`);
|
|
9665
|
-
await atomicWriteFile(path10, markdown.endsWith("\n") ? markdown : `${markdown}
|
|
9666
|
-
`);
|
|
9667
|
-
return relative2(this.root, path10).split("\\").join("/");
|
|
9668
|
-
}
|
|
9669
|
-
async readArtifact(goalId, taskId) {
|
|
9670
|
-
if (!isGoalId(goalId) || !/^[-a-zA-Z0-9_]{1,64}$/.test(taskId)) return void 0;
|
|
9671
|
-
return readFileIfExists(join7(this.goalDir(goalId), `${taskId}.md`));
|
|
9672
9758
|
}
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9676
|
-
|
|
9677
|
-
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
|
|
9689
|
-
|
|
9690
|
-
|
|
9691
|
-
|
|
9692
|
-
|
|
9693
|
-
|
|
9694
|
-
|
|
9759
|
+
const service = {
|
|
9760
|
+
async list() {
|
|
9761
|
+
await assertRepo();
|
|
9762
|
+
const isolated = isolatedIds().map((id) => target(id)).sort((a, b) => (a.meta?.createdAt ?? "").localeCompare(b.meta?.createdAt ?? ""));
|
|
9763
|
+
return Promise.all([target(MAIN_WORKSPACE_ID), ...isolated].map(describe));
|
|
9764
|
+
},
|
|
9765
|
+
async get(id) {
|
|
9766
|
+
await assertRepo();
|
|
9767
|
+
return describe(target(id));
|
|
9768
|
+
},
|
|
9769
|
+
create(input = {}) {
|
|
9770
|
+
return serial(async () => {
|
|
9771
|
+
const head = await assertRepo();
|
|
9772
|
+
if (isolatedIds().length >= CHANGES_LIMITS.maxWorkspaces) {
|
|
9773
|
+
throw new ChangesError(
|
|
9774
|
+
"limit",
|
|
9775
|
+
`At most ${CHANGES_LIMITS.maxWorkspaces} isolated workspaces per project. Remove one first.`
|
|
9776
|
+
);
|
|
9777
|
+
}
|
|
9778
|
+
const id = randomBytes9(4).toString("hex").slice(0, 6);
|
|
9779
|
+
const dir = path9.join(panesDir, id);
|
|
9780
|
+
const baseBranch = await optional(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
9781
|
+
const branch = await uniqueBranch(repo, `${PANE_BRANCH_PREFIX}${id}`);
|
|
9782
|
+
mkdirSync8(panesDir, { recursive: true });
|
|
9783
|
+
await repo.run(["worktree", "add", "-b", assertSafeRef(branch), dir, head]);
|
|
9784
|
+
writeMeta({
|
|
9785
|
+
id,
|
|
9786
|
+
label: input.label ?? "Isolated",
|
|
9787
|
+
...baseBranch ? { baseBranch } : {},
|
|
9788
|
+
baseCommit: head,
|
|
9789
|
+
createdAt: now().toISOString()
|
|
9790
|
+
});
|
|
9791
|
+
return describe(target(id));
|
|
9792
|
+
});
|
|
9793
|
+
},
|
|
9794
|
+
async diff(id, file) {
|
|
9795
|
+
await assertRepo();
|
|
9796
|
+
const t = target(id);
|
|
9797
|
+
const g = gitAt(t.path);
|
|
9798
|
+
const { base, files } = await changedFiles(g, t);
|
|
9799
|
+
let selected = files;
|
|
9800
|
+
if (file !== void 0) {
|
|
9801
|
+
selected = files.filter((f) => f.path === file || f.oldPath === file);
|
|
9802
|
+
if (selected.length === 0) throw new ChangesError("not_found", `"${file}" has no changes`);
|
|
9803
|
+
}
|
|
9804
|
+
const tracked = selected.filter((f) => f.status !== "untracked");
|
|
9805
|
+
let patch = "";
|
|
9806
|
+
if (tracked.length > 0) {
|
|
9807
|
+
const paths = tracked.flatMap((f) => f.oldPath ? [f.oldPath, f.path] : [f.path]);
|
|
9808
|
+
patch = await g.run([
|
|
9809
|
+
"-c",
|
|
9810
|
+
"core.quotepath=false",
|
|
9811
|
+
"diff",
|
|
9812
|
+
"--no-ext-diff",
|
|
9813
|
+
"--no-color",
|
|
9814
|
+
"-M",
|
|
9815
|
+
base,
|
|
9816
|
+
"--",
|
|
9817
|
+
...file === void 0 ? [] : paths
|
|
9818
|
+
]);
|
|
9819
|
+
}
|
|
9820
|
+
for (const f of selected) {
|
|
9821
|
+
if (f.status === "untracked") patch += newFilePatch(f.path, readUntracked(t.path, f.path));
|
|
9822
|
+
}
|
|
9823
|
+
const truncated = patch.length > CHANGES_LIMITS.diffMaxLength;
|
|
9824
|
+
return { patch: truncated ? patch.slice(0, CHANGES_LIMITS.diffMaxLength) : patch, truncated };
|
|
9825
|
+
},
|
|
9826
|
+
async suggest(id, input = {}) {
|
|
9827
|
+
const { agent } = suggestSchema.parse(input);
|
|
9828
|
+
await assertRepo();
|
|
9829
|
+
const t = target(id);
|
|
9830
|
+
const workspace = await describe(t);
|
|
9831
|
+
if (workspace.files.length === 0) {
|
|
9832
|
+
throw new ChangesError("nothing_to_commit", "There are no changes to describe.");
|
|
9833
|
+
}
|
|
9834
|
+
const fallback = fallbackSuggestion(workspace.files);
|
|
9835
|
+
if (!agent) return fallback;
|
|
9836
|
+
if (!options.writer) {
|
|
9837
|
+
throw new ChangesError("unavailable", "No agent can write the message on this gateway.");
|
|
9838
|
+
}
|
|
9839
|
+
const { patch } = await service.diff(id);
|
|
9840
|
+
const controller = new AbortController();
|
|
9841
|
+
const result = await options.writer.run({
|
|
9842
|
+
agent,
|
|
9843
|
+
cwd: t.path,
|
|
9844
|
+
prompt: agentPrompt({
|
|
9845
|
+
...workspace.branch ? { branch: workspace.branch } : {},
|
|
9846
|
+
files: workspace.files,
|
|
9847
|
+
patch
|
|
9848
|
+
}),
|
|
9849
|
+
mode: "plan",
|
|
9850
|
+
signal: controller.signal,
|
|
9851
|
+
timeoutMs: CHANGES_LIMITS.suggestTimeoutMs,
|
|
9852
|
+
onOutput: () => {
|
|
9853
|
+
}
|
|
9854
|
+
});
|
|
9855
|
+
const parsed = result.ok ? parseAgentSuggestion(result.text) : void 0;
|
|
9856
|
+
if (!parsed) {
|
|
9857
|
+
const why = result.ok ? "it did not reply with the expected JSON" : result.error ?? "it failed";
|
|
9858
|
+
throw new ChangesError("unavailable", `${agent} could not write the message: ${why}`);
|
|
9859
|
+
}
|
|
9860
|
+
return { ...parsed, by: agent };
|
|
9861
|
+
},
|
|
9862
|
+
ship(id, input) {
|
|
9863
|
+
const request = shipSchema.parse(input);
|
|
9864
|
+
return serial(async () => {
|
|
9865
|
+
await assertRepo();
|
|
9866
|
+
const t = target(id);
|
|
9867
|
+
const g = gitAt(t.path, true);
|
|
9868
|
+
const steps = [];
|
|
9869
|
+
let branch = await optional(g, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
9870
|
+
let commit;
|
|
9871
|
+
let prUrl;
|
|
9872
|
+
const wantPush = request.push || request.pr !== void 0;
|
|
9873
|
+
if (!branch && (wantPush || request.pr)) {
|
|
9874
|
+
throw new ChangesError("invalid", "HEAD is detached. Check out a branch first.");
|
|
9875
|
+
}
|
|
9876
|
+
const finish = async (ok) => ({
|
|
9877
|
+
ok,
|
|
9878
|
+
steps,
|
|
9879
|
+
...commit ? { commit } : {},
|
|
9880
|
+
...branch ? { branch } : {},
|
|
9881
|
+
...prUrl ? { prUrl } : {},
|
|
9882
|
+
workspace: await describe(t)
|
|
9883
|
+
});
|
|
9884
|
+
const step = async (phase, work) => {
|
|
9885
|
+
try {
|
|
9886
|
+
steps.push({ phase, ok: true, detail: await work() });
|
|
9887
|
+
return true;
|
|
9888
|
+
} catch (err) {
|
|
9889
|
+
steps.push({ phase, ok: false, detail: short(err) });
|
|
9890
|
+
return false;
|
|
9891
|
+
}
|
|
9892
|
+
};
|
|
9893
|
+
const before = await describe(t);
|
|
9894
|
+
if (request.message && !before.dirty && !wantPush) {
|
|
9895
|
+
throw new ChangesError("nothing_to_commit", "There is nothing to commit.");
|
|
9896
|
+
}
|
|
9897
|
+
if (!request.message && !wantPush) {
|
|
9898
|
+
throw new ChangesError("invalid", "Give a commit message, push or a pull request.");
|
|
9899
|
+
}
|
|
9900
|
+
const base = request.pr?.base ?? (t.isolated ? t.meta?.baseBranch : void 0) ?? (request.pr ? await defaultBranch(g) : void 0);
|
|
9901
|
+
if (request.pr && branch) {
|
|
9902
|
+
const current = branch;
|
|
9903
|
+
const slug = slugify(request.pr.title);
|
|
9904
|
+
if (!t.isolated && current === base) {
|
|
9905
|
+
const ok = await step("branch", async () => {
|
|
9906
|
+
const name = await uniqueBranch(g, `davecode/${slug}`);
|
|
9907
|
+
await g.run(["switch", "--quiet", "-c", assertSafeRef(name)]);
|
|
9908
|
+
branch = name;
|
|
9909
|
+
return `Created ${name} from ${current}`;
|
|
9910
|
+
});
|
|
9911
|
+
if (!ok) return finish(false);
|
|
9912
|
+
} else if (t.isolated && current.startsWith(PANE_BRANCH_PREFIX) && !before.upstream) {
|
|
9913
|
+
const ok = await step("branch", async () => {
|
|
9914
|
+
const name = await uniqueBranch(g, `davecode/${slug}`);
|
|
9915
|
+
await g.run(["branch", "-m", assertSafeRef(current), assertSafeRef(name)]);
|
|
9916
|
+
branch = name;
|
|
9917
|
+
return `Renamed ${current} to ${name}`;
|
|
9918
|
+
});
|
|
9919
|
+
if (!ok) return finish(false);
|
|
9920
|
+
}
|
|
9921
|
+
}
|
|
9922
|
+
if (request.message && before.dirty) {
|
|
9923
|
+
const message = request.message;
|
|
9924
|
+
const ok = await step("commit", async () => {
|
|
9925
|
+
commit = await commitAll(g, message);
|
|
9926
|
+
if (!commit) throw new Error("Nothing was staged.");
|
|
9927
|
+
return `Committed ${commit}`;
|
|
9928
|
+
});
|
|
9929
|
+
if (!ok) return finish(false);
|
|
9930
|
+
}
|
|
9931
|
+
if (wantPush && branch) {
|
|
9932
|
+
const name = branch;
|
|
9933
|
+
const ok = await step("push", async () => {
|
|
9934
|
+
if (!await g.hasRemote()) throw new Error('This repository has no "origin" remote.');
|
|
9935
|
+
await g.run(["push", "--quiet", "--set-upstream", "origin", assertSafeRef(name)]);
|
|
9936
|
+
return `Pushed ${name} to origin`;
|
|
9937
|
+
});
|
|
9938
|
+
if (!ok) return finish(false);
|
|
9939
|
+
}
|
|
9940
|
+
if (request.pr && branch) {
|
|
9941
|
+
const pr = request.pr;
|
|
9942
|
+
const head = branch;
|
|
9943
|
+
const ok = await step("pr", async () => {
|
|
9944
|
+
if (!base) throw new Error("Could not tell which branch the pull request targets.");
|
|
9945
|
+
if (base === head) throw new Error(`The pull request would merge ${head} into itself.`);
|
|
9946
|
+
const args = [
|
|
9947
|
+
"pr",
|
|
9948
|
+
"create",
|
|
9949
|
+
"--base",
|
|
9950
|
+
assertSafeRef(base),
|
|
9951
|
+
"--head",
|
|
9952
|
+
assertSafeRef(head)
|
|
9953
|
+
];
|
|
9954
|
+
args.push("--title", pr.title, "--body", pr.body || pr.title);
|
|
9955
|
+
if (pr.draft) args.push("--draft");
|
|
9956
|
+
try {
|
|
9957
|
+
const out = await runGh(t.path, args);
|
|
9958
|
+
prUrl = out.split("\n").at(-1)?.trim() || out;
|
|
9959
|
+
} catch (err) {
|
|
9960
|
+
const text = err instanceof Error ? err.message : String(err);
|
|
9961
|
+
if (!/already exists/i.test(text)) throw err;
|
|
9962
|
+
prUrl = await runGh(t.path, ["pr", "view", head, "--json", "url", "--jq", ".url"]);
|
|
9963
|
+
}
|
|
9964
|
+
return prUrl;
|
|
9965
|
+
});
|
|
9966
|
+
if (!ok) return finish(false);
|
|
9967
|
+
}
|
|
9968
|
+
return finish(true);
|
|
9969
|
+
});
|
|
9970
|
+
},
|
|
9971
|
+
merge(id, input = {}) {
|
|
9972
|
+
const { message } = mergeSchema.parse(input);
|
|
9973
|
+
return serial(async () => {
|
|
9974
|
+
await assertRepo();
|
|
9975
|
+
const t = target(id);
|
|
9976
|
+
if (!t.isolated)
|
|
9977
|
+
throw new ChangesError("invalid", "Only an isolated workspace can be merged.");
|
|
9978
|
+
const g = gitAt(t.path, true);
|
|
9979
|
+
const branch = await optional(g, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
9980
|
+
if (!branch) throw new ChangesError("invalid", "The workspace has no branch checked out.");
|
|
9981
|
+
let state = await describe(t);
|
|
9982
|
+
if (state.dirty) {
|
|
9983
|
+
if (!message) {
|
|
9984
|
+
throw new ChangesError(
|
|
9985
|
+
"dirty",
|
|
9986
|
+
"The workspace has uncommitted changes. Commit them first."
|
|
9987
|
+
);
|
|
9988
|
+
}
|
|
9989
|
+
await commitAll(g, message);
|
|
9990
|
+
state = await describe(t);
|
|
9991
|
+
}
|
|
9992
|
+
if (state.ahead === 0)
|
|
9993
|
+
throw new ChangesError("nothing_to_commit", "There is nothing to merge.");
|
|
9994
|
+
const into = await optional(repo, ["symbolic-ref", "--quiet", "--short", "HEAD"]);
|
|
9995
|
+
if (!into) throw new ChangesError("invalid", "The project checkout is on a detached HEAD.");
|
|
9996
|
+
if (samePath(t.path, projectDir))
|
|
9997
|
+
throw new ChangesError("invalid", "Cannot merge into itself.");
|
|
9998
|
+
const main = gitAt(projectDir, true);
|
|
9999
|
+
try {
|
|
10000
|
+
await main.run([
|
|
10001
|
+
...await identityArgs(main),
|
|
10002
|
+
"merge",
|
|
10003
|
+
"--no-ff",
|
|
10004
|
+
"--no-edit",
|
|
10005
|
+
"-m",
|
|
10006
|
+
`Merge ${t.label} (${branch}) into ${into}`,
|
|
10007
|
+
assertSafeRef(branch)
|
|
10008
|
+
]);
|
|
10009
|
+
} catch (err) {
|
|
10010
|
+
const conflicts = nul(
|
|
10011
|
+
await optional(main, ["diff", "--name-only", "-z", "--diff-filter=U"]) ?? ""
|
|
10012
|
+
);
|
|
10013
|
+
await main.abortMerge();
|
|
10014
|
+
throw new ChangesError(
|
|
10015
|
+
"conflict",
|
|
10016
|
+
conflicts.length > 0 ? `The merge conflicts in ${conflicts.join(", ")}. Nothing was changed.` : `The merge failed: ${short(err)}`
|
|
10017
|
+
);
|
|
10018
|
+
}
|
|
10019
|
+
return describe(target(MAIN_WORKSPACE_ID));
|
|
10020
|
+
});
|
|
10021
|
+
},
|
|
10022
|
+
remove(id, input = {}) {
|
|
10023
|
+
const { deleteBranch } = removeSchema.parse(input);
|
|
10024
|
+
return serial(async () => {
|
|
10025
|
+
const t = target(id);
|
|
10026
|
+
if (!t.isolated)
|
|
10027
|
+
throw new ChangesError("invalid", "The project checkout cannot be removed.");
|
|
10028
|
+
const branch = await optional(gitAt(t.path), [
|
|
10029
|
+
"symbolic-ref",
|
|
10030
|
+
"--quiet",
|
|
10031
|
+
"--short",
|
|
10032
|
+
"HEAD"
|
|
10033
|
+
]);
|
|
10034
|
+
await removeWorktree(t.path);
|
|
10035
|
+
rmSync5(metaFile(id), { force: true });
|
|
10036
|
+
if (deleteBranch && branch) {
|
|
10037
|
+
try {
|
|
10038
|
+
await repo.run(["branch", "-D", assertSafeRef(branch)]);
|
|
10039
|
+
} catch {
|
|
10040
|
+
}
|
|
10041
|
+
}
|
|
10042
|
+
});
|
|
10043
|
+
}
|
|
9695
10044
|
};
|
|
9696
|
-
|
|
9697
|
-
router: engine.router,
|
|
9698
|
-
store,
|
|
9699
|
-
events: engine.events,
|
|
9700
|
-
config: engine.config.goal,
|
|
9701
|
-
codeRunner,
|
|
9702
|
-
projectContext,
|
|
9703
|
-
...opts.budgetContext ? { budgetContext: opts.budgetContext } : {}
|
|
9704
|
-
});
|
|
9705
|
-
return new GoalService({ orchestrator, store, lock });
|
|
10045
|
+
return service;
|
|
9706
10046
|
}
|
|
9707
|
-
var
|
|
9708
|
-
|
|
9709
|
-
|
|
9710
|
-
|
|
9711
|
-
|
|
9712
|
-
/** Default when the request does not say. */
|
|
9713
|
-
defaultMaxTasks: 5,
|
|
9714
|
-
/** Most tasks running at the same time. */
|
|
9715
|
-
maxParallel: 3,
|
|
9716
|
-
/** Hard timeout for one headless agent run. */
|
|
9717
|
-
taskTimeoutMs: 30 * 6e4,
|
|
9718
|
-
/** Hard timeout for the planning run. */
|
|
9719
|
-
planTimeoutMs: 5 * 6e4,
|
|
9720
|
-
/** Longest summary kept per task (characters, head kept). */
|
|
9721
|
-
summaryMaxLength: 4e3
|
|
9722
|
-
};
|
|
9723
|
-
var startRunSchema = z12.object({
|
|
9724
|
-
objective: z12.string().trim().min(1).max(ORCHESTRATOR_LIMITS.objectiveMaxLength),
|
|
9725
|
-
/** Defaults to every available agent. */
|
|
9726
|
-
agents: z12.array(z12.enum(ORCHESTRATOR_AGENTS)).min(1).max(3).optional(),
|
|
9727
|
-
maxTasks: z12.number().int().min(1).max(ORCHESTRATOR_LIMITS.maxTasks).optional(),
|
|
9728
|
-
/** The agent that splits the objective. It must be installed, but need not take tasks.
|
|
9729
|
-
* Defaults to the first allowed agent in the order claude, codex, agy. */
|
|
9730
|
-
planner: z12.enum(ORCHESTRATOR_AGENTS).optional()
|
|
9731
|
-
});
|
|
9732
|
-
var OrchestratorError = class extends Error {
|
|
9733
|
-
constructor(code, message) {
|
|
9734
|
-
super(message);
|
|
9735
|
-
this.code = code;
|
|
9736
|
-
this.name = "OrchestratorError";
|
|
9737
|
-
}
|
|
9738
|
-
code;
|
|
9739
|
-
};
|
|
9740
|
-
var MAX_TEXT_CHUNK = 2e3;
|
|
9741
|
-
var MAX_TOOL_LINE = 200;
|
|
9742
|
-
var MAX_ERROR = 300;
|
|
9743
|
-
var AGENTS = {
|
|
9744
|
-
claude: {
|
|
9745
|
-
label: "Claude Code",
|
|
9746
|
-
binary: "claude",
|
|
9747
|
-
provider: "claude-cli",
|
|
9748
|
-
signIn: "run `claude` in a terminal and sign in"
|
|
10047
|
+
var DEFAULT_ROLES = [
|
|
10048
|
+
{
|
|
10049
|
+
id: "planner",
|
|
10050
|
+
description: "Breaks the objective into a plan, defines milestones and success criteria.",
|
|
10051
|
+
kind: "artifact"
|
|
9749
10052
|
},
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
signIn: "run `codex login` in a terminal"
|
|
10053
|
+
{
|
|
10054
|
+
id: "researcher",
|
|
10055
|
+
description: "Analyses markets, users, competitors and options from its own knowledge and the workspace; cannot browse the web.",
|
|
10056
|
+
kind: "artifact"
|
|
9755
10057
|
},
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
"stream-json",
|
|
9771
|
-
"--verbose",
|
|
9772
|
-
"--permission-mode",
|
|
9773
|
-
"plan",
|
|
9774
|
-
"--tools",
|
|
9775
|
-
"Read",
|
|
9776
|
-
"Glob",
|
|
9777
|
-
"Grep",
|
|
9778
|
-
"--strict-mcp-config"
|
|
9779
|
-
] : ["-p", "--output-format", "stream-json", "--verbose", "--permission-mode", "acceptEdits"];
|
|
9780
|
-
case "codex":
|
|
9781
|
-
return [
|
|
9782
|
-
"exec",
|
|
9783
|
-
"--json",
|
|
9784
|
-
"--skip-git-repo-check",
|
|
9785
|
-
"--sandbox",
|
|
9786
|
-
mode === "plan" ? "read-only" : "workspace-write",
|
|
9787
|
-
"-c",
|
|
9788
|
-
"approval_policy=never",
|
|
9789
|
-
"-"
|
|
9790
|
-
];
|
|
9791
|
-
case "agy":
|
|
9792
|
-
return [
|
|
9793
|
-
"--input-format",
|
|
9794
|
-
"stream-json",
|
|
9795
|
-
"--output-format",
|
|
9796
|
-
"stream-json",
|
|
9797
|
-
"--sandbox",
|
|
9798
|
-
"--disable-slash-commands",
|
|
9799
|
-
"--print="
|
|
9800
|
-
];
|
|
9801
|
-
}
|
|
9802
|
-
}
|
|
9803
|
-
function realPath(p) {
|
|
9804
|
-
try {
|
|
9805
|
-
return realpathSync.native(p);
|
|
9806
|
-
} catch {
|
|
9807
|
-
return path8.resolve(p);
|
|
10058
|
+
{
|
|
10059
|
+
id: "implementer",
|
|
10060
|
+
description: "Writes and changes code in the repository (handed to the autonomous runner).",
|
|
10061
|
+
kind: "code"
|
|
10062
|
+
},
|
|
10063
|
+
{
|
|
10064
|
+
id: "reviewer",
|
|
10065
|
+
description: "Critically reviews other outputs for gaps, risks and quality.",
|
|
10066
|
+
kind: "artifact"
|
|
10067
|
+
},
|
|
10068
|
+
{
|
|
10069
|
+
id: "writer",
|
|
10070
|
+
description: "Writes positioning, marketing copy, landing-page text, documentation and drafts of messages.",
|
|
10071
|
+
kind: "artifact"
|
|
9808
10072
|
}
|
|
9809
|
-
|
|
9810
|
-
function
|
|
9811
|
-
|
|
9812
|
-
|
|
9813
|
-
|
|
9814
|
-
|
|
9815
|
-
|
|
9816
|
-
|
|
10073
|
+
];
|
|
10074
|
+
function resolveRoles(config) {
|
|
10075
|
+
const roles = new Map(DEFAULT_ROLES.map((r) => [r.id, { ...r }]));
|
|
10076
|
+
for (const [id, override] of Object.entries(config.roles)) {
|
|
10077
|
+
const base = roles.get(id);
|
|
10078
|
+
const role = {
|
|
10079
|
+
id,
|
|
10080
|
+
description: override.description ?? base?.description ?? id,
|
|
10081
|
+
kind: override.kind ?? base?.kind ?? "artifact"
|
|
10082
|
+
};
|
|
10083
|
+
if (override.route) role.route = override.route;
|
|
10084
|
+
const instructions = override.instructions ?? base?.instructions;
|
|
10085
|
+
if (instructions) role.instructions = instructions;
|
|
10086
|
+
roles.set(id, role);
|
|
9817
10087
|
}
|
|
9818
|
-
|
|
9819
|
-
${prompt}`;
|
|
9820
|
-
return prompt;
|
|
10088
|
+
return [...roles.values()];
|
|
9821
10089
|
}
|
|
9822
|
-
function
|
|
9823
|
-
|
|
9824
|
-
|
|
9825
|
-
|
|
10090
|
+
function publicRoles(roles) {
|
|
10091
|
+
return roles.map(
|
|
10092
|
+
({ id, description, route }) => route ? { id, description, route } : { id, description }
|
|
10093
|
+
);
|
|
9826
10094
|
}
|
|
9827
|
-
var
|
|
9828
|
-
|
|
9829
|
-
|
|
9830
|
-
|
|
9831
|
-
|
|
9832
|
-
|
|
9833
|
-
|
|
9834
|
-
[
|
|
9835
|
-
/\b(api[_-]?key|access[_-]?token|refresh[_-]?token|token|secret|password|authorization)(["']?\s*[:=]\s*["']?)[^\s"',}]+/gi,
|
|
9836
|
-
"$1$2[redacted]"
|
|
9837
|
-
]
|
|
10095
|
+
var OUTBOUND = [
|
|
10096
|
+
/\b(send|sending|email|e-mail|emailing|mail out)\b/i,
|
|
10097
|
+
/\b(tweet|dm|direct message|share on)\b/i,
|
|
10098
|
+
/\b(post|publish)\b[^.\n]{0,60}\b(on|to)\b/i,
|
|
10099
|
+
/\b(pay|purchase|buy|checkout|subscribe to|sign up|signup|register (for|on|at))\b/i,
|
|
10100
|
+
/\b(outreach|cold[- ]?(call|email)|contact (the|our|potential|prospects?|customers?|leads?))\b/i,
|
|
10101
|
+
/\b(launch (the )?ads?|run (the )?ads?|ad campaign|deploy to production|go live)\b/i
|
|
9838
10102
|
];
|
|
9839
|
-
|
|
9840
|
-
|
|
9841
|
-
|
|
9842
|
-
|
|
9843
|
-
|
|
9844
|
-
|
|
9845
|
-
|
|
10103
|
+
var OPERATIONAL = [
|
|
10104
|
+
/\b(deploy|release|publish)\b[^.\n]*\b(production|prod|npm|app store|play store|live)\b/i,
|
|
10105
|
+
/\b(go live|pay|purchase|buy|checkout)\b/i,
|
|
10106
|
+
/\b(send|email|e-mail|message)\b[^.\n]*\b(customers?|users?|leads?|prospects?|subscribers?|mailing list|newsletter)\b/i,
|
|
10107
|
+
/\b(post|tweet|publish)\b[^.\n]*\b(twitter|x\.com|linkedin|facebook|instagram|reddit|hacker news|product hunt)\b/i
|
|
10108
|
+
];
|
|
10109
|
+
function firstMatch(patterns, text) {
|
|
10110
|
+
for (const pattern of patterns) {
|
|
10111
|
+
const match = pattern.exec(text);
|
|
10112
|
+
if (match) return match[0];
|
|
10113
|
+
}
|
|
10114
|
+
return void 0;
|
|
9846
10115
|
}
|
|
9847
|
-
function
|
|
9848
|
-
return
|
|
10116
|
+
function outboundAction(text) {
|
|
10117
|
+
return firstMatch(OUTBOUND, text);
|
|
9849
10118
|
}
|
|
9850
|
-
|
|
9851
|
-
|
|
9852
|
-
|
|
10119
|
+
var DRAFT_NOTICE = "DaveCode never performs outbound actions (emails, posts, payments, sign-ups, deployments). Produce a ready-to-use DRAFT for a human to review and execute.";
|
|
10120
|
+
function applySafetyGuard(task) {
|
|
10121
|
+
const text = `${task.title}
|
|
10122
|
+
${task.description ?? ""}`;
|
|
10123
|
+
const action = task.kind === "code" ? firstMatch(OPERATIONAL, text) : firstMatch(OUTBOUND, text);
|
|
10124
|
+
if (!action) return task;
|
|
10125
|
+
const title = /^draft\b/i.test(task.title) ? task.title : `Draft: ${task.title}`;
|
|
10126
|
+
const description = [task.description?.trim(), DRAFT_NOTICE].filter(Boolean).join("\n\n");
|
|
10127
|
+
return { ...task, kind: "artifact", title: title.slice(0, 300), description };
|
|
9853
10128
|
}
|
|
9854
|
-
|
|
9855
|
-
|
|
9856
|
-
|
|
10129
|
+
var plannedTaskSchema = z14.object({
|
|
10130
|
+
id: z14.string().min(1).max(80),
|
|
10131
|
+
title: z14.string().trim().min(1).max(300),
|
|
10132
|
+
description: z14.string().max(8e3).optional(),
|
|
10133
|
+
role: z14.string().min(1).max(40),
|
|
10134
|
+
kind: z14.enum(["code", "artifact"]).optional(),
|
|
10135
|
+
dependsOn: z14.array(z14.string().min(1)).max(50).default([]),
|
|
10136
|
+
acceptance: z14.array(z14.string().min(1).max(1e3)).max(20).optional()
|
|
10137
|
+
});
|
|
10138
|
+
var planSchema = z14.object({
|
|
10139
|
+
summary: z14.string().max(4e3).optional(),
|
|
10140
|
+
tasks: z14.array(plannedTaskSchema).min(1).max(100)
|
|
10141
|
+
});
|
|
10142
|
+
var reviewSchema = z14.object({
|
|
10143
|
+
/** The objective is achieved as far as DaveCode can take it. */
|
|
10144
|
+
done: z14.boolean(),
|
|
10145
|
+
summary: z14.string().max(8e3),
|
|
10146
|
+
/** Ids of FAILED tasks worth another attempt. */
|
|
10147
|
+
retry: z14.array(z14.string()).max(50).default([]),
|
|
10148
|
+
/** New tasks for the next round. */
|
|
10149
|
+
tasks: z14.array(plannedTaskSchema).max(50).default([])
|
|
10150
|
+
});
|
|
10151
|
+
var TASK_ID = /^[a-z0-9]+(?:[-_.][a-z0-9]+)*$/;
|
|
10152
|
+
function sanitizeTaskId(raw) {
|
|
10153
|
+
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60).replace(/-+$/g, "");
|
|
10154
|
+
return TASK_ID.test(id) ? id : "task";
|
|
9857
10155
|
}
|
|
9858
|
-
function
|
|
9859
|
-
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9866
|
-
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
"
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
9877
|
-
|
|
9878
|
-
|
|
9879
|
-
|
|
9880
|
-
|
|
9881
|
-
|
|
9882
|
-
}
|
|
9883
|
-
|
|
9884
|
-
|
|
9885
|
-
|
|
9886
|
-
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
|
|
9890
|
-
}
|
|
9891
|
-
}
|
|
10156
|
+
function normalizeTasks(planned, opts) {
|
|
10157
|
+
const roleById = new Map(opts.roles.map((r) => [r.id, r]));
|
|
10158
|
+
const taken = new Set(opts.existing.map((t) => t.id));
|
|
10159
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
10160
|
+
const out = [];
|
|
10161
|
+
for (const p of planned.slice(0, Math.max(0, opts.room))) {
|
|
10162
|
+
let id = sanitizeTaskId(p.id);
|
|
10163
|
+
for (let n = 2; taken.has(id); n++) id = `${sanitizeTaskId(p.id)}-${n}`;
|
|
10164
|
+
taken.add(id);
|
|
10165
|
+
idMap.set(p.id, id);
|
|
10166
|
+
let role = roleById.get(p.role.toLowerCase());
|
|
10167
|
+
const requested = p.kind ?? role?.kind ?? "artifact";
|
|
10168
|
+
if (!role)
|
|
10169
|
+
role = roleById.get(requested === "code" ? "implementer" : "writer") ?? opts.roles[0];
|
|
10170
|
+
const kind = requested === "code" && opts.codeTasks ? "code" : "artifact";
|
|
10171
|
+
const task = {
|
|
10172
|
+
id,
|
|
10173
|
+
title: p.title,
|
|
10174
|
+
role: role?.id ?? "writer",
|
|
10175
|
+
kind,
|
|
10176
|
+
status: "PENDING",
|
|
10177
|
+
dependsOn: p.dependsOn,
|
|
10178
|
+
round: opts.round,
|
|
10179
|
+
attempts: 0
|
|
10180
|
+
};
|
|
10181
|
+
const description = requested === "code" && kind === "artifact" ? [
|
|
10182
|
+
p.description,
|
|
10183
|
+
"Code tasks cannot run here: write a precise implementation plan instead."
|
|
10184
|
+
].filter(Boolean).join("\n\n") : p.description;
|
|
10185
|
+
if (description) task.description = description;
|
|
10186
|
+
if (p.acceptance?.length) task.acceptance = p.acceptance;
|
|
10187
|
+
out.push(applySafetyGuard(task));
|
|
9892
10188
|
}
|
|
9893
|
-
|
|
10189
|
+
const known = /* @__PURE__ */ new Set([...opts.existing.map((t) => t.id), ...out.map((t) => t.id)]);
|
|
10190
|
+
for (const task of out) {
|
|
10191
|
+
task.dependsOn = [
|
|
10192
|
+
...new Set(task.dependsOn.map((d) => idMap.get(d) ?? sanitizeTaskId(d)))
|
|
10193
|
+
].filter((d) => d !== task.id && known.has(d));
|
|
10194
|
+
}
|
|
10195
|
+
for (; ; ) {
|
|
10196
|
+
const graph = {
|
|
10197
|
+
version: 1,
|
|
10198
|
+
tasks: [...opts.existing, ...out].map((t) => ({
|
|
10199
|
+
id: t.id,
|
|
10200
|
+
title: t.title,
|
|
10201
|
+
status: t.status,
|
|
10202
|
+
dependsOn: t.dependsOn
|
|
10203
|
+
}))
|
|
10204
|
+
};
|
|
10205
|
+
const cycle = findCycles(graph)[0];
|
|
10206
|
+
if (!cycle) break;
|
|
10207
|
+
const from = cycle[cycle.length - 2];
|
|
10208
|
+
const to = cycle[cycle.length - 1];
|
|
10209
|
+
const task = out.find((t) => t.id === from);
|
|
10210
|
+
if (!task) break;
|
|
10211
|
+
task.dependsOn = task.dependsOn.filter((d) => d !== to);
|
|
10212
|
+
}
|
|
10213
|
+
return out;
|
|
9894
10214
|
}
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
handle(raw) {
|
|
9903
|
-
if (!isRecord(raw)) return;
|
|
9904
|
-
const type = asString(raw.type);
|
|
9905
|
-
if (type === "system" && raw.subtype === "init") {
|
|
9906
|
-
const model = asString(raw.model);
|
|
9907
|
-
emit({
|
|
9908
|
-
kind: "status",
|
|
9909
|
-
text: model ? `Claude Code started (${model})` : "Claude Code started"
|
|
9910
|
-
});
|
|
9911
|
-
return;
|
|
9912
|
-
}
|
|
9913
|
-
if (type === "assistant" && raw.parent_tool_use_id == null && isRecord(raw.message)) {
|
|
9914
|
-
const texts = [];
|
|
9915
|
-
for (const block of asArray(raw.message.content)) {
|
|
9916
|
-
if (!isRecord(block)) continue;
|
|
9917
|
-
if (block.type === "text") {
|
|
9918
|
-
const t = asString(block.text)?.trim();
|
|
9919
|
-
if (t) texts.push(t);
|
|
9920
|
-
} else if (block.type === "tool_use") {
|
|
9921
|
-
emit({
|
|
9922
|
-
kind: "tool",
|
|
9923
|
-
text: toolLine(asString(block.name) ?? "tool", block.input, cwd)
|
|
9924
|
-
});
|
|
9925
|
-
}
|
|
9926
|
-
}
|
|
9927
|
-
if (texts.length > 0) {
|
|
9928
|
-
lastAssistant = texts.join("\n");
|
|
9929
|
-
emit({ kind: "text", text: clip(lastAssistant, MAX_TEXT_CHUNK) });
|
|
9930
|
-
}
|
|
9931
|
-
return;
|
|
9932
|
-
}
|
|
9933
|
-
if (type === "result") {
|
|
9934
|
-
const result = asString(raw.result) ?? "";
|
|
9935
|
-
const subtype = asString(raw.subtype) ?? "success";
|
|
9936
|
-
const u = isRecord(raw.usage) ? raw.usage : void 0;
|
|
9937
|
-
if (u) {
|
|
9938
|
-
state.tokens = (asNumber(u.input_tokens) ?? 0) + (asNumber(u.cache_creation_input_tokens) ?? 0) + (asNumber(u.cache_read_input_tokens) ?? 0) + (asNumber(u.output_tokens) ?? 0);
|
|
9939
|
-
}
|
|
9940
|
-
if (raw.is_error === true || subtype !== "success") {
|
|
9941
|
-
state.failure = result || `Claude Code reported ${subtype}`;
|
|
9942
|
-
return;
|
|
9943
|
-
}
|
|
9944
|
-
state.completed = true;
|
|
9945
|
-
state.text = result || lastAssistant;
|
|
9946
|
-
}
|
|
9947
|
-
}
|
|
9948
|
-
};
|
|
10215
|
+
var SAFETY_RULES = `Hard rules (non-negotiable):
|
|
10216
|
+
- You run inside DaveCode with no internet access and no ability to contact anyone.
|
|
10217
|
+
- Never send emails or messages, post or publish anything, make payments or purchases, sign up for services, or deploy. When such a step is useful, produce a DRAFT for a human to review and execute.
|
|
10218
|
+
- Never invent credentials, personal data or facts presented as verified; label assumptions and estimates clearly.
|
|
10219
|
+
- Stay within the objective; prefer concrete, actionable outputs.`;
|
|
10220
|
+
function roleList(roles) {
|
|
10221
|
+
return roles.map((r) => `- ${r.id} (${r.kind}): ${r.description}`).join("\n");
|
|
9949
10222
|
}
|
|
9950
|
-
function
|
|
9951
|
-
|
|
9952
|
-
|
|
9953
|
-
|
|
10223
|
+
function plannerPrompt(roles, codeTasks) {
|
|
10224
|
+
return `You are the planner of a small team of AI agents working toward one objective. Decompose the objective into a dependency graph of concrete tasks and assign each task to one role.
|
|
10225
|
+
|
|
10226
|
+
Roles:
|
|
10227
|
+
${roleList(roles)}
|
|
10228
|
+
|
|
10229
|
+
Task kinds:
|
|
10230
|
+
- "artifact": the role writes one markdown document (analysis, plan, copy, checklist, draft).
|
|
10231
|
+
- "code": the implementer changes the repository; an autonomous runner branches, runs the quality gates and merges.${codeTasks ? "" : "\n Code tasks are NOT available in this workspace: plan artifact tasks only."}
|
|
10232
|
+
|
|
10233
|
+
${SAFETY_RULES}
|
|
10234
|
+
|
|
10235
|
+
Plan at most the number of tasks you are allowed, smallest useful set first. Ids are short lowercase kebab-case. Use dependsOn so later tasks build on earlier outputs.
|
|
10236
|
+
|
|
10237
|
+
Respond with ONLY a JSON object, no prose and no code fences:
|
|
10238
|
+
{"summary": "one paragraph plan", "tasks": [{"id": "market-research", "title": "...", "description": "...", "role": "researcher", "kind": "artifact", "dependsOn": [], "acceptance": ["..."]}]}`;
|
|
9954
10239
|
}
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
10240
|
+
var REVIEWER_PROMPT = `You are the reviewer of a team of AI agents working toward one objective. Judge the results so far against the objective.
|
|
10241
|
+
|
|
10242
|
+
${SAFETY_RULES}
|
|
10243
|
+
|
|
10244
|
+
Decide:
|
|
10245
|
+
- "done": true when the objective is achieved as far as an offline team can take it (remaining steps are outbound actions a human must do from the drafts).
|
|
10246
|
+
- otherwise propose the next round: "retry" lists FAILED task ids worth another attempt, "tasks" lists new tasks (same shape as the planner's, dependsOn may reference existing ids).
|
|
10247
|
+
|
|
10248
|
+
Respond with ONLY a JSON object, no prose and no code fences:
|
|
10249
|
+
{"done": false, "summary": "progress, gaps and what a human must do next", "retry": [], "tasks": []}`;
|
|
10250
|
+
function roleSystemPrompt(role) {
|
|
10251
|
+
return [
|
|
10252
|
+
`You are the ${role.id} of a small team of AI agents. ${role.description}`,
|
|
10253
|
+
SAFETY_RULES,
|
|
10254
|
+
"Write your deliverable as a single, well-structured markdown document. Do not wrap it in code fences. Start with a level-1 heading.",
|
|
10255
|
+
role.instructions?.trim()
|
|
10256
|
+
].filter(Boolean).join("\n\n");
|
|
10257
|
+
}
|
|
10258
|
+
var MeteredExecutor = class {
|
|
10259
|
+
constructor(inner) {
|
|
10260
|
+
this.inner = inner;
|
|
10261
|
+
}
|
|
10262
|
+
inner;
|
|
10263
|
+
tokens = 0;
|
|
10264
|
+
get name() {
|
|
10265
|
+
return this.inner.name;
|
|
10266
|
+
}
|
|
10267
|
+
createSession(task) {
|
|
10268
|
+
const session = this.inner.createSession(task);
|
|
10269
|
+
return {
|
|
10270
|
+
run: async (opts) => {
|
|
10271
|
+
const result = await session.run(opts);
|
|
10272
|
+
this.tokens += Math.max(0, result.tokens);
|
|
10273
|
+
return result;
|
|
10274
|
+
}
|
|
10275
|
+
};
|
|
10276
|
+
}
|
|
10277
|
+
};
|
|
10278
|
+
var MIN_TASK_TOKENS = 1e4;
|
|
10279
|
+
var RunnerCodeTasks = class {
|
|
10280
|
+
constructor(options) {
|
|
10281
|
+
this.options = options;
|
|
10282
|
+
this.owner = options.owner ?? "goal";
|
|
10283
|
+
}
|
|
10284
|
+
options;
|
|
10285
|
+
owner;
|
|
10286
|
+
async available() {
|
|
10287
|
+
const { brain, engine } = this.options;
|
|
10288
|
+
const git = new Git(brain.root);
|
|
10289
|
+
if (!await git.isRepo())
|
|
10290
|
+
return { ok: false, reason: `${brain.root} is not a git repository` };
|
|
10291
|
+
if (!await brain.isInitialised()) {
|
|
10292
|
+
return { ok: false, reason: "the project brain is missing (run davecode init)" };
|
|
9966
10293
|
}
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
return
|
|
10294
|
+
const base = engine.config.runner.baseBranch;
|
|
10295
|
+
if (!await git.branchExists(base)) {
|
|
10296
|
+
return { ok: false, reason: `base branch "${base}" does not exist` };
|
|
9970
10297
|
}
|
|
9971
|
-
|
|
9972
|
-
return
|
|
9973
|
-
|
|
9974
|
-
|
|
10298
|
+
if ((await git.status({ ignoreBrain: true })).length > 0) {
|
|
10299
|
+
return { ok: false, reason: "the working tree has uncommitted changes" };
|
|
10300
|
+
}
|
|
10301
|
+
return { ok: true };
|
|
9975
10302
|
}
|
|
9976
|
-
|
|
9977
|
-
|
|
9978
|
-
|
|
9979
|
-
|
|
9980
|
-
|
|
9981
|
-
|
|
9982
|
-
|
|
9983
|
-
flush() {
|
|
9984
|
-
if (!state.completed && !state.failure && lastError) state.failure = lastError;
|
|
9985
|
-
},
|
|
9986
|
-
handle(raw) {
|
|
9987
|
-
if (!isRecord(raw)) return;
|
|
9988
|
-
const ev = isRecord(raw.msg) ? raw.msg : raw;
|
|
9989
|
-
const type = asString(ev.type) ?? "";
|
|
9990
|
-
if (type === "thread.started") {
|
|
9991
|
-
emit({ kind: "status", text: "Codex started" });
|
|
9992
|
-
return;
|
|
9993
|
-
}
|
|
9994
|
-
if ((type === "item.started" || type === "item.completed") && isRecord(ev.item)) {
|
|
9995
|
-
const item = ev.item;
|
|
9996
|
-
const itemType = asString(item.type);
|
|
9997
|
-
if (itemType === "agent_message" || itemType === "assistant_message") {
|
|
9998
|
-
if (type !== "item.completed") return;
|
|
9999
|
-
const text = (asString(item.text) ?? asString(item.message) ?? "").trim();
|
|
10000
|
-
if (text) {
|
|
10001
|
-
state.text = text;
|
|
10002
|
-
emit({ kind: "text", text: clip(text, MAX_TEXT_CHUNK) });
|
|
10003
|
-
}
|
|
10004
|
-
return;
|
|
10005
|
-
}
|
|
10006
|
-
if (itemType === "error") {
|
|
10007
|
-
const message = asString(item.message);
|
|
10008
|
-
if (message && type === "item.completed")
|
|
10009
|
-
emit({ kind: "error", text: shortError(message) });
|
|
10010
|
-
return;
|
|
10011
|
-
}
|
|
10012
|
-
const id = asString(item.id) ?? `${itemType}-${announced.size}`;
|
|
10013
|
-
if (itemType === "file_change" && type !== "item.completed") return;
|
|
10014
|
-
if (announced.has(id)) return;
|
|
10015
|
-
announced.add(id);
|
|
10016
|
-
const line = codexItemLine(item, cwd);
|
|
10017
|
-
if (line) emit({ kind: "tool", text: line });
|
|
10018
|
-
return;
|
|
10019
|
-
}
|
|
10020
|
-
if (type === "turn.completed" || type === "task_complete") {
|
|
10021
|
-
state.completed = true;
|
|
10022
|
-
const last = asString(ev.last_agent_message);
|
|
10023
|
-
if (!state.text && last) state.text = last;
|
|
10024
|
-
const u = isRecord(ev.usage) ? ev.usage : void 0;
|
|
10025
|
-
if (u) state.tokens = (asNumber(u.input_tokens) ?? 0) + (asNumber(u.output_tokens) ?? 0);
|
|
10026
|
-
return;
|
|
10027
|
-
}
|
|
10028
|
-
if (type === "turn.failed") {
|
|
10029
|
-
state.failure = codexErrorText(ev.error) ?? lastError ?? "Codex reported an error";
|
|
10030
|
-
return;
|
|
10031
|
-
}
|
|
10032
|
-
if (type === "error") {
|
|
10033
|
-
lastError = codexErrorText(ev.message) ?? codexErrorText(ev.error) ?? lastError;
|
|
10034
|
-
if (lastError) emit({ kind: "error", text: shortError(lastError) });
|
|
10035
|
-
return;
|
|
10036
|
-
}
|
|
10037
|
-
if (type === "agent_message") {
|
|
10038
|
-
const text = (asString(ev.message) ?? "").trim();
|
|
10039
|
-
if (text) {
|
|
10040
|
-
state.text = text;
|
|
10041
|
-
emit({ kind: "text", text: clip(text, MAX_TEXT_CHUNK) });
|
|
10042
|
-
}
|
|
10043
|
-
}
|
|
10303
|
+
async run(task, opts) {
|
|
10304
|
+
const { brain, engine } = this.options;
|
|
10305
|
+
const runnerTaskId = sanitizeTaskId(`goal-${opts.goal.id.slice(-6)}-${task.id}`);
|
|
10306
|
+
const graph = await brain.readGraph();
|
|
10307
|
+
const existing = graph.tasks.find((t) => t.id === runnerTaskId);
|
|
10308
|
+
if (existing?.status === "SUCCESS") {
|
|
10309
|
+
return { ok: true, summary: existing.notes ?? "already done", tokens: 0, runnerTaskId };
|
|
10044
10310
|
}
|
|
10045
|
-
|
|
10311
|
+
if (!existing) {
|
|
10312
|
+
await brain.createTask({
|
|
10313
|
+
id: runnerTaskId,
|
|
10314
|
+
title: task.title,
|
|
10315
|
+
description: [
|
|
10316
|
+
task.description,
|
|
10317
|
+
`Part of DaveCode goal ${opts.goal.id}: ${opts.goal.objective}`
|
|
10318
|
+
].filter(Boolean).join("\n\n"),
|
|
10319
|
+
...task.acceptance?.length ? { acceptance: task.acceptance } : {}
|
|
10320
|
+
});
|
|
10321
|
+
} else if (existing.status !== "PENDING") {
|
|
10322
|
+
await brain.setTaskStatus(runnerTaskId, "PENDING", { notes: "retried by goal mode" });
|
|
10323
|
+
}
|
|
10324
|
+
const base = engine.config.runner;
|
|
10325
|
+
const config = {
|
|
10326
|
+
...base,
|
|
10327
|
+
maxTaskTokens: Math.max(MIN_TASK_TOKENS, Math.min(base.maxTaskTokens, opts.maxTokens))
|
|
10328
|
+
};
|
|
10329
|
+
const metered = new MeteredExecutor(this.options.executor ?? createExecutor(engine, config));
|
|
10330
|
+
const runner = this.options.makeRunner?.(config, metered) ?? createRunner(engine, {
|
|
10331
|
+
brain,
|
|
10332
|
+
config,
|
|
10333
|
+
executor: metered,
|
|
10334
|
+
lock: { lock: this.options.lock, owner: this.owner },
|
|
10335
|
+
...this.options.contextExtras ? { contextExtras: this.options.contextExtras } : {}
|
|
10336
|
+
});
|
|
10337
|
+
const onAbort = () => void runner.stop();
|
|
10338
|
+
opts.signal.addEventListener("abort", onAbort, { once: true });
|
|
10339
|
+
try {
|
|
10340
|
+
const result = await runner.runOnce({ taskId: runnerTaskId });
|
|
10341
|
+
return {
|
|
10342
|
+
ok: result.outcome === "success",
|
|
10343
|
+
summary: result.summary ?? result.error ?? result.outcome,
|
|
10344
|
+
tokens: metered.tokens,
|
|
10345
|
+
runnerTaskId,
|
|
10346
|
+
...result.outcome === "stopped" ? { stopped: true } : {}
|
|
10347
|
+
};
|
|
10348
|
+
} catch (err) {
|
|
10349
|
+
if (!(err instanceof RunnerError)) throw err;
|
|
10350
|
+
return { ok: false, summary: err.message, tokens: metered.tokens, runnerTaskId };
|
|
10351
|
+
} finally {
|
|
10352
|
+
opts.signal.removeEventListener("abort", onAbort);
|
|
10353
|
+
}
|
|
10354
|
+
}
|
|
10355
|
+
};
|
|
10356
|
+
var TERMINAL = /* @__PURE__ */ new Set([
|
|
10357
|
+
"planned",
|
|
10358
|
+
"succeeded",
|
|
10359
|
+
"failed",
|
|
10360
|
+
"cancelled",
|
|
10361
|
+
"budget_exhausted"
|
|
10362
|
+
]);
|
|
10363
|
+
function isTerminalGoal(goal) {
|
|
10364
|
+
return TERMINAL.has(goal.status);
|
|
10046
10365
|
}
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10056
|
-
|
|
10057
|
-
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10064
|
-
|
|
10065
|
-
|
|
10066
|
-
|
|
10067
|
-
|
|
10366
|
+
var Cancelled = class extends Error {
|
|
10367
|
+
};
|
|
10368
|
+
var OutOfBudget = class extends Error {
|
|
10369
|
+
};
|
|
10370
|
+
var MAX_DEP_CHARS = 6e3;
|
|
10371
|
+
var MAX_DEPS_TOTAL = 2e4;
|
|
10372
|
+
var MAX_REVIEW_EXCERPT = 1500;
|
|
10373
|
+
var MAX_TOOL_ROUNDS = 6;
|
|
10374
|
+
var GoalOrchestrator = class {
|
|
10375
|
+
constructor(options) {
|
|
10376
|
+
this.options = options;
|
|
10377
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
10378
|
+
this.tools = new WorkspaceTools({ root: options.store.root, readOnly: true });
|
|
10379
|
+
}
|
|
10380
|
+
options;
|
|
10381
|
+
now;
|
|
10382
|
+
tools;
|
|
10383
|
+
/** A fresh goal (not yet persisted) with limits clamped to the config. */
|
|
10384
|
+
create(input) {
|
|
10385
|
+
const cfg = this.options.config;
|
|
10386
|
+
const stamp = this.now().toISOString();
|
|
10387
|
+
const goal = {
|
|
10388
|
+
id: newId2("goal", 6),
|
|
10389
|
+
objective: input.objective.trim(),
|
|
10390
|
+
status: "planning",
|
|
10391
|
+
round: 0,
|
|
10392
|
+
limits: {
|
|
10393
|
+
maxRounds: Math.min(input.maxRounds ?? cfg.maxRounds, cfg.maxRounds),
|
|
10394
|
+
maxTasks: Math.min(input.maxTasks ?? cfg.maxTasks, cfg.maxTasks),
|
|
10395
|
+
maxTokens: Math.min(input.maxTokens ?? cfg.maxTokens, cfg.maxTokens),
|
|
10396
|
+
tokensUsed: 0
|
|
10397
|
+
},
|
|
10398
|
+
roles: publicRoles(resolveRoles(cfg)),
|
|
10399
|
+
tasks: [],
|
|
10400
|
+
createdAt: stamp,
|
|
10401
|
+
updatedAt: stamp
|
|
10402
|
+
};
|
|
10403
|
+
if (input.dryRun) goal.dryRun = true;
|
|
10404
|
+
return goal;
|
|
10405
|
+
}
|
|
10406
|
+
/** Runs `goal` to a terminal status. Never throws: failures end up in `status`/`lastError`. */
|
|
10407
|
+
async run(goal, signal) {
|
|
10408
|
+
const cfg = this.options.config;
|
|
10409
|
+
const roles = resolveRoles(cfg);
|
|
10410
|
+
try {
|
|
10411
|
+
const code = await this.codeAvailability();
|
|
10412
|
+
if (!code.ok) this.log(goal, "info", `code tasks disabled: ${code.reason}`);
|
|
10413
|
+
if (goal.round === 0 || goal.tasks.length === 0) {
|
|
10414
|
+
goal.round = 1;
|
|
10415
|
+
await this.update(goal, { status: "planning" });
|
|
10416
|
+
this.log(goal, "info", `planning: ${goal.objective}`, "planner");
|
|
10417
|
+
const plan = await this.plan(goal, roles, code.ok, signal);
|
|
10418
|
+
goal.tasks = normalizeTasks(plan.tasks, {
|
|
10419
|
+
existing: [],
|
|
10420
|
+
roles,
|
|
10421
|
+
round: 1,
|
|
10422
|
+
room: goal.limits.maxTasks,
|
|
10423
|
+
codeTasks: code.ok
|
|
10424
|
+
});
|
|
10425
|
+
if (plan.summary) goal.summary = plan.summary;
|
|
10426
|
+
this.log(goal, "info", `planned ${goal.tasks.length} task(s)`, "planner");
|
|
10427
|
+
if (goal.dryRun) return await this.finish(goal, "planned");
|
|
10428
|
+
} else {
|
|
10429
|
+
this.log(goal, "info", `executing planned goal (${goal.tasks.length} task(s))`, "planner");
|
|
10068
10430
|
}
|
|
10069
|
-
|
|
10070
|
-
|
|
10071
|
-
|
|
10072
|
-
|
|
10073
|
-
|
|
10074
|
-
|
|
10075
|
-
|
|
10076
|
-
|
|
10077
|
-
|
|
10078
|
-
|
|
10079
|
-
|
|
10080
|
-
}
|
|
10081
|
-
if (asString(step.state) === "DONE") flushText();
|
|
10082
|
-
return;
|
|
10431
|
+
for (; ; ) {
|
|
10432
|
+
await this.update(goal, { status: "running" });
|
|
10433
|
+
await this.executeReady(goal, roles, signal);
|
|
10434
|
+
await this.update(goal, { status: "reviewing" });
|
|
10435
|
+
this.log(goal, "info", `reviewing round ${goal.round}`, "reviewer");
|
|
10436
|
+
const review = await this.review(goal, roles, signal);
|
|
10437
|
+
goal.summary = review.summary;
|
|
10438
|
+
if (review.done) return await this.finish(goal, "succeeded");
|
|
10439
|
+
if (goal.round >= goal.limits.maxRounds) {
|
|
10440
|
+
goal.lastError = `reached ${goal.limits.maxRounds} round(s) without completing the objective`;
|
|
10441
|
+
return await this.finish(goal, "failed");
|
|
10083
10442
|
}
|
|
10084
|
-
|
|
10085
|
-
|
|
10086
|
-
|
|
10087
|
-
|
|
10088
|
-
|
|
10089
|
-
|
|
10090
|
-
|
|
10443
|
+
const retried = this.applyRetries(goal, review.retry);
|
|
10444
|
+
const added = normalizeTasks(review.tasks, {
|
|
10445
|
+
existing: goal.tasks,
|
|
10446
|
+
roles,
|
|
10447
|
+
round: goal.round + 1,
|
|
10448
|
+
room: goal.limits.maxTasks - goal.tasks.length,
|
|
10449
|
+
codeTasks: code.ok
|
|
10450
|
+
});
|
|
10451
|
+
if (retried === 0 && added.length === 0) {
|
|
10452
|
+
goal.lastError = goal.tasks.length >= goal.limits.maxTasks ? `reached the cap of ${goal.limits.maxTasks} task(s) without completing the objective` : "the reviewer proposed no further work but the objective is not done";
|
|
10453
|
+
return await this.finish(goal, "failed");
|
|
10091
10454
|
}
|
|
10092
|
-
|
|
10455
|
+
goal.tasks.push(...added);
|
|
10456
|
+
goal.round++;
|
|
10457
|
+
this.log(
|
|
10458
|
+
goal,
|
|
10459
|
+
"info",
|
|
10460
|
+
`round ${goal.round}: ${added.length} new task(s), ${retried} retried`,
|
|
10461
|
+
"reviewer"
|
|
10462
|
+
);
|
|
10093
10463
|
}
|
|
10094
|
-
|
|
10095
|
-
|
|
10096
|
-
|
|
10097
|
-
|
|
10098
|
-
|
|
10099
|
-
|
|
10100
|
-
|
|
10101
|
-
const status = asString(result.status);
|
|
10102
|
-
if (status !== "SUCCESS") {
|
|
10103
|
-
state.failure = asString(result.error) ?? `Antigravity ended with ${status ?? "an error"}`;
|
|
10104
|
-
return;
|
|
10105
|
-
}
|
|
10106
|
-
state.completed = true;
|
|
10107
|
-
state.text = (asString(result.response) ?? streamed).trim();
|
|
10464
|
+
} catch (err) {
|
|
10465
|
+
for (const task of goal.tasks) if (task.status === "IN_PROGRESS") task.status = "PENDING";
|
|
10466
|
+
delete goal.activeTaskId;
|
|
10467
|
+
if (err instanceof Cancelled || signal.aborted) return await this.finish(goal, "cancelled");
|
|
10468
|
+
if (err instanceof OutOfBudget) {
|
|
10469
|
+
goal.lastError = err.message;
|
|
10470
|
+
return await this.finish(goal, "budget_exhausted");
|
|
10108
10471
|
}
|
|
10472
|
+
goal.lastError = err instanceof Error ? err.message : String(err);
|
|
10473
|
+
this.log(goal, "error", goal.lastError);
|
|
10474
|
+
return await this.finish(goal, "failed");
|
|
10475
|
+
}
|
|
10476
|
+
}
|
|
10477
|
+
// -- phases -----------------------------------------------------------------
|
|
10478
|
+
async codeAvailability() {
|
|
10479
|
+
if (!this.options.config.codeTasks) return { ok: false, reason: "goal.codeTasks is off" };
|
|
10480
|
+
if (!this.options.codeRunner) return { ok: false, reason: "no project runner available" };
|
|
10481
|
+
try {
|
|
10482
|
+
return await this.options.codeRunner.available();
|
|
10483
|
+
} catch (err) {
|
|
10484
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
10485
|
+
}
|
|
10486
|
+
}
|
|
10487
|
+
async plan(goal, roles, codeTasks, signal) {
|
|
10488
|
+
const user = [
|
|
10489
|
+
`OBJECTIVE:
|
|
10490
|
+
${goal.objective}`,
|
|
10491
|
+
`You may plan at most ${goal.limits.maxTasks} task(s) in total; the team has ${goal.limits.maxRounds} round(s) of plan \u2192 execute \u2192 review.`,
|
|
10492
|
+
await this.sharedContext()
|
|
10493
|
+
].filter(Boolean).join("\n\n");
|
|
10494
|
+
return this.askJson(
|
|
10495
|
+
planSchema,
|
|
10496
|
+
goal,
|
|
10497
|
+
this.roleFor(roles, "planner"),
|
|
10498
|
+
plannerPrompt(roles, codeTasks),
|
|
10499
|
+
user,
|
|
10500
|
+
signal
|
|
10501
|
+
);
|
|
10502
|
+
}
|
|
10503
|
+
async review(goal, roles, signal) {
|
|
10504
|
+
const lines2 = [];
|
|
10505
|
+
for (const task of goal.tasks) {
|
|
10506
|
+
lines2.push(
|
|
10507
|
+
`- [${task.status}] ${task.id} (${task.role}, ${task.kind}, round ${task.round}, ${task.attempts} attempt(s)): ${task.title}`
|
|
10508
|
+
);
|
|
10509
|
+
if (task.notes) lines2.push(` notes: ${truncateHead(task.notes, 500).replace(/\n/g, " ")}`);
|
|
10510
|
+
if (task.artifact) {
|
|
10511
|
+
const text = await this.options.store.readArtifact(goal.id, task.id);
|
|
10512
|
+
if (text)
|
|
10513
|
+
lines2.push(` output excerpt:
|
|
10514
|
+
${indent(truncateHead(text, MAX_REVIEW_EXCERPT))}`);
|
|
10515
|
+
}
|
|
10516
|
+
}
|
|
10517
|
+
const user = [
|
|
10518
|
+
`OBJECTIVE:
|
|
10519
|
+
${goal.objective}`,
|
|
10520
|
+
`Round ${goal.round} of ${goal.limits.maxRounds}. Room for ${Math.max(0, goal.limits.maxTasks - goal.tasks.length)} more task(s).`,
|
|
10521
|
+
`TASKS:
|
|
10522
|
+
${lines2.join("\n")}`,
|
|
10523
|
+
await this.sharedContext()
|
|
10524
|
+
].filter(Boolean).join("\n\n");
|
|
10525
|
+
return this.askJson(
|
|
10526
|
+
reviewSchema,
|
|
10527
|
+
goal,
|
|
10528
|
+
this.roleFor(roles, "reviewer"),
|
|
10529
|
+
REVIEWER_PROMPT,
|
|
10530
|
+
user,
|
|
10531
|
+
signal
|
|
10532
|
+
);
|
|
10533
|
+
}
|
|
10534
|
+
/** Runs ready tasks until none is left; failed tasks are retried up to `maxTaskAttempts`. */
|
|
10535
|
+
async executeReady(goal, roles, signal) {
|
|
10536
|
+
for (; ; ) {
|
|
10537
|
+
const task = readyGoalTasks(goal)[0];
|
|
10538
|
+
if (!task) return;
|
|
10539
|
+
this.checkpoint(goal, signal);
|
|
10540
|
+
task.attempts++;
|
|
10541
|
+
task.status = "IN_PROGRESS";
|
|
10542
|
+
await this.update(goal, { activeTaskId: task.id });
|
|
10543
|
+
this.log(goal, "info", `${task.title} (attempt ${task.attempts})`, task.role, task.id);
|
|
10544
|
+
try {
|
|
10545
|
+
if (task.kind === "code") await this.runCodeTask(goal, task, signal);
|
|
10546
|
+
else await this.runArtifactTask(goal, task, this.roleFor(roles, task.role), signal);
|
|
10547
|
+
const outcome = task.status;
|
|
10548
|
+
this.log(
|
|
10549
|
+
goal,
|
|
10550
|
+
outcome === "SUCCESS" ? "info" : "warn",
|
|
10551
|
+
`${task.id}: ${outcome}`,
|
|
10552
|
+
task.role,
|
|
10553
|
+
task.id
|
|
10554
|
+
);
|
|
10555
|
+
} catch (err) {
|
|
10556
|
+
if (err instanceof Cancelled || err instanceof OutOfBudget || signal.aborted) throw err;
|
|
10557
|
+
if (err instanceof RouterError && (err.code === "no_capacity" || err.code === "rate_limited")) {
|
|
10558
|
+
throw new OutOfBudget(`no account has capacity left (${err.message})`);
|
|
10559
|
+
}
|
|
10560
|
+
task.status = "FAILED";
|
|
10561
|
+
task.notes = truncateHead(err instanceof Error ? err.message : String(err), 2e3);
|
|
10562
|
+
this.log(goal, "warn", `${task.id} failed: ${task.notes}`, task.role, task.id);
|
|
10563
|
+
}
|
|
10564
|
+
if (task.status === "FAILED" && task.attempts < this.options.config.maxTaskAttempts) {
|
|
10565
|
+
task.status = "PENDING";
|
|
10566
|
+
}
|
|
10567
|
+
delete goal.activeTaskId;
|
|
10568
|
+
await this.update(goal, {});
|
|
10569
|
+
}
|
|
10570
|
+
}
|
|
10571
|
+
async runArtifactTask(goal, task, role, signal) {
|
|
10572
|
+
const parts = [`OBJECTIVE OF THE TEAM:
|
|
10573
|
+
${goal.objective}`, `YOUR TASK: ${task.title}`];
|
|
10574
|
+
if (task.description) parts.push(task.description);
|
|
10575
|
+
if (task.acceptance?.length) {
|
|
10576
|
+
parts.push(`Acceptance criteria:
|
|
10577
|
+
${task.acceptance.map((c) => `- ${c}`).join("\n")}`);
|
|
10578
|
+
}
|
|
10579
|
+
let budget = MAX_DEPS_TOTAL;
|
|
10580
|
+
for (const depId of task.dependsOn) {
|
|
10581
|
+
const dep = goal.tasks.find((t) => t.id === depId);
|
|
10582
|
+
if (!dep || budget <= 0) continue;
|
|
10583
|
+
const text = dep.artifact ? await this.options.store.readArtifact(goal.id, dep.id) : dep.notes;
|
|
10584
|
+
if (!text) continue;
|
|
10585
|
+
const excerpt = truncateHead(text, Math.min(MAX_DEP_CHARS, budget));
|
|
10586
|
+
budget -= excerpt.length;
|
|
10587
|
+
parts.push(`OUTPUT OF ${dep.id} (${dep.role}): ${dep.title}
|
|
10588
|
+
${excerpt}`);
|
|
10589
|
+
}
|
|
10590
|
+
const shared = await this.sharedContext(false);
|
|
10591
|
+
if (shared) parts.push(shared);
|
|
10592
|
+
const reply = (await this.ask(goal, role, roleSystemPrompt(role), parts.join("\n\n"), signal)).trim();
|
|
10593
|
+
if (!reply) throw new Error("the agent returned an empty reply");
|
|
10594
|
+
task.artifact = await this.options.store.writeArtifact(goal.id, task.id, stripFence(reply));
|
|
10595
|
+
task.status = "SUCCESS";
|
|
10596
|
+
task.notes = `Wrote ${task.artifact}`;
|
|
10597
|
+
}
|
|
10598
|
+
async runCodeTask(goal, task, signal) {
|
|
10599
|
+
const runner = this.options.codeRunner;
|
|
10600
|
+
if (!runner) throw new Error("no project runner available for code tasks");
|
|
10601
|
+
const result = await runner.run(task, {
|
|
10602
|
+
goal,
|
|
10603
|
+
maxTokens: goal.limits.maxTokens - goal.limits.tokensUsed,
|
|
10604
|
+
signal
|
|
10605
|
+
});
|
|
10606
|
+
this.addTokens(goal, result.tokens);
|
|
10607
|
+
if (result.runnerTaskId) task.runnerTaskId = result.runnerTaskId;
|
|
10608
|
+
if (result.stopped || signal.aborted) throw new Cancelled();
|
|
10609
|
+
task.status = result.ok ? "SUCCESS" : "FAILED";
|
|
10610
|
+
task.notes = truncateHead(result.summary || (result.ok ? "done" : "failed"), 2e3);
|
|
10611
|
+
}
|
|
10612
|
+
applyRetries(goal, ids) {
|
|
10613
|
+
let count = 0;
|
|
10614
|
+
for (const id of new Set(ids)) {
|
|
10615
|
+
const task = goal.tasks.find((t) => t.id === id);
|
|
10616
|
+
if (task?.status !== "FAILED") continue;
|
|
10617
|
+
task.status = "PENDING";
|
|
10618
|
+
task.attempts = 0;
|
|
10619
|
+
count++;
|
|
10620
|
+
}
|
|
10621
|
+
return count;
|
|
10622
|
+
}
|
|
10623
|
+
// -- model calls ------------------------------------------------------------
|
|
10624
|
+
roleFor(roles, id) {
|
|
10625
|
+
return roles.find((r) => r.id === id) ?? { id, description: id, kind: "artifact" };
|
|
10626
|
+
}
|
|
10627
|
+
async ask(goal, role, system, user, signal) {
|
|
10628
|
+
const model = routeModel(role.route ?? this.options.config.route);
|
|
10629
|
+
const messages = [
|
|
10630
|
+
{ role: "system", content: system },
|
|
10631
|
+
{ role: "user", content: user }
|
|
10632
|
+
];
|
|
10633
|
+
let attachTools = this.options.readTools === true;
|
|
10634
|
+
for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
|
|
10635
|
+
this.checkpoint(goal, signal);
|
|
10636
|
+
const allowTools = attachTools && round < MAX_TOOL_ROUNDS;
|
|
10637
|
+
let completion;
|
|
10638
|
+
try {
|
|
10639
|
+
({ completion } = await this.options.router.complete(
|
|
10640
|
+
{
|
|
10641
|
+
model,
|
|
10642
|
+
messages,
|
|
10643
|
+
...allowTools ? { tools: [...READ_ONLY_TOOL_DEFINITIONS] } : {}
|
|
10644
|
+
},
|
|
10645
|
+
{ signal }
|
|
10646
|
+
));
|
|
10647
|
+
} catch (err) {
|
|
10648
|
+
if (allowTools && err instanceof RouterError && err.code === "no_available_account") {
|
|
10649
|
+
attachTools = false;
|
|
10650
|
+
({ completion } = await this.options.router.complete({ model, messages }, { signal }));
|
|
10651
|
+
} else {
|
|
10652
|
+
throw err;
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
const message = completion.choices[0]?.message;
|
|
10656
|
+
const content = message?.content;
|
|
10657
|
+
const text = typeof content === "string" ? content : "";
|
|
10658
|
+
const usage = completion.usage;
|
|
10659
|
+
this.addTokens(
|
|
10660
|
+
goal,
|
|
10661
|
+
usage && usage.prompt_tokens + usage.completion_tokens > 0 ? usage.prompt_tokens + usage.completion_tokens : countTextTokens(system) + countTextTokens(user) + countTextTokens(text)
|
|
10662
|
+
);
|
|
10663
|
+
const calls = message?.tool_calls ?? [];
|
|
10664
|
+
if (calls.length === 0 || round === MAX_TOOL_ROUNDS) return text;
|
|
10665
|
+
messages.push({ role: "assistant", content: content ?? null, tool_calls: calls });
|
|
10666
|
+
for (const call of calls) {
|
|
10667
|
+
this.log(goal, "debug", `tool ${call.function.name}`, role.id, goal.activeTaskId);
|
|
10668
|
+
const outcome = await this.tools.call(call.function.name, call.function.arguments, signal);
|
|
10669
|
+
messages.push({
|
|
10670
|
+
role: "tool",
|
|
10671
|
+
tool_call_id: call.id,
|
|
10672
|
+
name: call.function.name,
|
|
10673
|
+
content: outcome.output
|
|
10674
|
+
});
|
|
10675
|
+
}
|
|
10676
|
+
}
|
|
10677
|
+
return "";
|
|
10678
|
+
}
|
|
10679
|
+
/** Asks for a JSON reply matching `schema`; one corrective retry on malformed output. */
|
|
10680
|
+
async askJson(schema, goal, role, system, user, signal) {
|
|
10681
|
+
let problem = "";
|
|
10682
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
10683
|
+
const prompt = attempt === 0 ? user : `${user}
|
|
10684
|
+
|
|
10685
|
+
Your previous reply was rejected (${problem}). Respond with ONLY the JSON object in the required shape.`;
|
|
10686
|
+
const reply = await this.ask(goal, role, system, prompt, signal);
|
|
10687
|
+
try {
|
|
10688
|
+
const parsed = schema.safeParse(extractJsonObject(reply));
|
|
10689
|
+
if (parsed.success) return parsed.data;
|
|
10690
|
+
problem = parsed.error.issues.slice(0, 3).map((i) => `${i.path.join(".") || "reply"}: ${i.message}`).join("; ");
|
|
10691
|
+
} catch (err) {
|
|
10692
|
+
problem = err instanceof Error ? err.message : String(err);
|
|
10693
|
+
}
|
|
10694
|
+
this.log(goal, "warn", `invalid JSON from the ${role.id}: ${problem}`, role.id);
|
|
10695
|
+
}
|
|
10696
|
+
throw new Error(`the ${role.id} did not return valid JSON (${problem})`);
|
|
10697
|
+
}
|
|
10698
|
+
async sharedContext(includeProject = true) {
|
|
10699
|
+
const parts = [];
|
|
10700
|
+
if (includeProject && this.options.projectContext) {
|
|
10701
|
+
const project = (await this.options.projectContext()).trim();
|
|
10702
|
+
if (project) parts.push(`PROJECT CONTEXT:
|
|
10703
|
+
${project}`);
|
|
10704
|
+
}
|
|
10705
|
+
if (this.options.budgetContext) {
|
|
10706
|
+
const budget = (await this.options.budgetContext()).trim();
|
|
10707
|
+
if (budget) parts.push(budget);
|
|
10708
|
+
}
|
|
10709
|
+
return parts.join("\n\n");
|
|
10710
|
+
}
|
|
10711
|
+
// -- bookkeeping ------------------------------------------------------------
|
|
10712
|
+
checkpoint(goal, signal) {
|
|
10713
|
+
if (signal.aborted) throw new Cancelled();
|
|
10714
|
+
if (goal.limits.tokensUsed >= goal.limits.maxTokens) {
|
|
10715
|
+
throw new OutOfBudget(
|
|
10716
|
+
`token budget exhausted (${goal.limits.tokensUsed}/${goal.limits.maxTokens})`
|
|
10717
|
+
);
|
|
10718
|
+
}
|
|
10719
|
+
}
|
|
10720
|
+
addTokens(goal, tokens2) {
|
|
10721
|
+
if (Number.isFinite(tokens2) && tokens2 > 0) goal.limits.tokensUsed += Math.round(tokens2);
|
|
10722
|
+
}
|
|
10723
|
+
async update(goal, patch) {
|
|
10724
|
+
Object.assign(goal, patch);
|
|
10725
|
+
goal.updatedAt = this.now().toISOString();
|
|
10726
|
+
await this.options.store.save(goal);
|
|
10727
|
+
this.options.events.emit({ type: "goal.updated", goal: structuredClone(goal) });
|
|
10728
|
+
}
|
|
10729
|
+
async finish(goal, status) {
|
|
10730
|
+
delete goal.activeTaskId;
|
|
10731
|
+
goal.finishedAt = this.now().toISOString();
|
|
10732
|
+
await this.update(goal, { status });
|
|
10733
|
+
this.log(
|
|
10734
|
+
goal,
|
|
10735
|
+
status === "succeeded" || status === "planned" ? "info" : "warn",
|
|
10736
|
+
`goal ${status}${goal.lastError ? `: ${goal.lastError}` : ""} (${goal.limits.tokensUsed} tokens)`
|
|
10737
|
+
);
|
|
10738
|
+
return goal;
|
|
10739
|
+
}
|
|
10740
|
+
log(goal, level, message, role, taskId) {
|
|
10741
|
+
this.options.events.emit({
|
|
10742
|
+
type: "goal.log",
|
|
10743
|
+
goalId: goal.id,
|
|
10744
|
+
level,
|
|
10745
|
+
message,
|
|
10746
|
+
...role ? { role } : {},
|
|
10747
|
+
...taskId ? { taskId } : {}
|
|
10748
|
+
});
|
|
10749
|
+
}
|
|
10750
|
+
};
|
|
10751
|
+
function readyGoalTasks(goal) {
|
|
10752
|
+
const status = new Map(goal.tasks.map((t) => [t.id, t.status]));
|
|
10753
|
+
return goal.tasks.filter(
|
|
10754
|
+
(t) => t.status === "PENDING" && t.dependsOn.every((d) => status.get(d) === "SUCCESS")
|
|
10755
|
+
);
|
|
10756
|
+
}
|
|
10757
|
+
function indent(text) {
|
|
10758
|
+
return text.split("\n").map((l) => ` ${l}`).join("\n");
|
|
10759
|
+
}
|
|
10760
|
+
function stripFence(text) {
|
|
10761
|
+
const match = /^```(?:markdown|md)?\s*\n([\s\S]*?)\n```\s*$/i.exec(text);
|
|
10762
|
+
return match?.[1] ?? text;
|
|
10763
|
+
}
|
|
10764
|
+
var GoalError = class extends Error {
|
|
10765
|
+
constructor(code, message) {
|
|
10766
|
+
super(message);
|
|
10767
|
+
this.code = code;
|
|
10768
|
+
this.name = "GoalError";
|
|
10769
|
+
this.statusCode = code === "not_found" ? 404 : code === "invalid" ? 400 : 409;
|
|
10770
|
+
}
|
|
10771
|
+
code;
|
|
10772
|
+
/** HTTP status hint for the gateway. */
|
|
10773
|
+
statusCode;
|
|
10774
|
+
};
|
|
10775
|
+
var GoalService = class {
|
|
10776
|
+
constructor(options) {
|
|
10777
|
+
this.options = options;
|
|
10778
|
+
this.owner = options.owner ?? "goal";
|
|
10779
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
10780
|
+
}
|
|
10781
|
+
options;
|
|
10782
|
+
active;
|
|
10783
|
+
owner;
|
|
10784
|
+
now;
|
|
10785
|
+
get activeGoalId() {
|
|
10786
|
+
return this.active?.goal.id;
|
|
10787
|
+
}
|
|
10788
|
+
/** Validates, persists and starts a goal in the background. Resolves with its initial state. */
|
|
10789
|
+
async start(input) {
|
|
10790
|
+
if (!input.objective.trim()) throw new GoalError("invalid", "the objective is empty");
|
|
10791
|
+
if (this.active) {
|
|
10792
|
+
throw new GoalError("busy", `goal ${this.active.goal.id} is still running; cancel it first`);
|
|
10793
|
+
}
|
|
10794
|
+
const lock = input.dryRun ? void 0 : this.options.lock;
|
|
10795
|
+
if (lock && !lock.acquire(this.owner)) {
|
|
10796
|
+
throw new GoalError(
|
|
10797
|
+
"busy",
|
|
10798
|
+
`the working tree is busy (${lock.holder ?? "another task"} is running)`
|
|
10799
|
+
);
|
|
10800
|
+
}
|
|
10801
|
+
try {
|
|
10802
|
+
const goal = this.options.orchestrator.create(input);
|
|
10803
|
+
await this.options.store.save(goal);
|
|
10804
|
+
this.launch(goal, lock);
|
|
10805
|
+
return structuredClone(goal);
|
|
10806
|
+
} catch (err) {
|
|
10807
|
+
lock?.release(this.owner);
|
|
10808
|
+
throw err;
|
|
10809
|
+
}
|
|
10810
|
+
}
|
|
10811
|
+
/**
|
|
10812
|
+
* Executes a `planned` (dry-run) goal in the background without re-planning round 1.
|
|
10813
|
+
* Resolves with the updated goal (`status: 'running'`).
|
|
10814
|
+
*/
|
|
10815
|
+
async execute(id) {
|
|
10816
|
+
if (this.active) {
|
|
10817
|
+
throw new GoalError("busy", `goal ${this.active.goal.id} is still running; cancel it first`);
|
|
10818
|
+
}
|
|
10819
|
+
const goal = await this.options.store.load(id);
|
|
10820
|
+
if (!goal) throw new GoalError("not_found", `unknown goal "${id}"`);
|
|
10821
|
+
if (goal.status !== "planned") {
|
|
10822
|
+
throw new GoalError(
|
|
10823
|
+
"invalid",
|
|
10824
|
+
`goal "${id}" is ${goal.status}; only a planned goal can be executed`
|
|
10825
|
+
);
|
|
10826
|
+
}
|
|
10827
|
+
const lock = this.options.lock;
|
|
10828
|
+
if (lock && !lock.acquire(this.owner)) {
|
|
10829
|
+
throw new GoalError(
|
|
10830
|
+
"busy",
|
|
10831
|
+
`the working tree is busy (${lock.holder ?? "another task"} is running)`
|
|
10832
|
+
);
|
|
10833
|
+
}
|
|
10834
|
+
try {
|
|
10835
|
+
delete goal.dryRun;
|
|
10836
|
+
delete goal.finishedAt;
|
|
10837
|
+
delete goal.lastError;
|
|
10838
|
+
goal.status = "running";
|
|
10839
|
+
goal.updatedAt = this.now().toISOString();
|
|
10840
|
+
await this.options.store.save(goal);
|
|
10841
|
+
this.launch(goal, lock);
|
|
10842
|
+
return structuredClone(goal);
|
|
10843
|
+
} catch (err) {
|
|
10844
|
+
lock?.release(this.owner);
|
|
10845
|
+
throw err;
|
|
10846
|
+
}
|
|
10847
|
+
}
|
|
10848
|
+
/** Reads the markdown artifact produced by `taskId` in goal `id`. */
|
|
10849
|
+
async readArtifact(id, taskId) {
|
|
10850
|
+
const goal = await this.get(id);
|
|
10851
|
+
if (!goal) throw new GoalError("not_found", `unknown goal "${id}"`);
|
|
10852
|
+
const task = goal.tasks.find((t) => t.id === taskId);
|
|
10853
|
+
if (!task?.artifact) {
|
|
10854
|
+
throw new GoalError("not_found", `task "${taskId}" has no artifact in goal "${id}"`);
|
|
10855
|
+
}
|
|
10856
|
+
const content = await this.options.store.readArtifact(id, taskId);
|
|
10857
|
+
if (content === void 0) {
|
|
10858
|
+
throw new GoalError("not_found", `artifact file for task "${taskId}" was not found`);
|
|
10859
|
+
}
|
|
10860
|
+
return { taskId: task.id, path: task.artifact, content };
|
|
10861
|
+
}
|
|
10862
|
+
launch(goal, lock) {
|
|
10863
|
+
const controller = new AbortController();
|
|
10864
|
+
const done = this.options.orchestrator.run(goal, controller.signal).catch((err) => {
|
|
10865
|
+
goal.status = "failed";
|
|
10866
|
+
goal.lastError = err instanceof Error ? err.message : String(err);
|
|
10867
|
+
return goal;
|
|
10868
|
+
}).finally(() => {
|
|
10869
|
+
if (this.active?.goal === goal) this.active = void 0;
|
|
10870
|
+
lock?.release(this.owner);
|
|
10871
|
+
});
|
|
10872
|
+
this.active = { goal, controller, done };
|
|
10873
|
+
}
|
|
10874
|
+
async get(id) {
|
|
10875
|
+
if (this.active?.goal.id === id) return structuredClone(this.active.goal);
|
|
10876
|
+
return this.options.store.load(id);
|
|
10877
|
+
}
|
|
10878
|
+
/** Every goal of the workspace, newest first. */
|
|
10879
|
+
async list() {
|
|
10880
|
+
const stored = await this.options.store.list();
|
|
10881
|
+
const active = this.active?.goal;
|
|
10882
|
+
if (!active) return stored;
|
|
10883
|
+
const rest = stored.filter((g) => g.id !== active.id);
|
|
10884
|
+
return [structuredClone(active), ...rest];
|
|
10885
|
+
}
|
|
10886
|
+
/**
|
|
10887
|
+
* Cancels a running goal and resolves once it has stopped. A goal left unfinished by an earlier
|
|
10888
|
+
* process is marked cancelled; a finished goal is returned unchanged.
|
|
10889
|
+
*/
|
|
10890
|
+
async cancel(id) {
|
|
10891
|
+
if (this.active?.goal.id === id) {
|
|
10892
|
+
this.active.controller.abort();
|
|
10893
|
+
return structuredClone(await this.active.done);
|
|
10109
10894
|
}
|
|
10110
|
-
|
|
10111
|
-
}
|
|
10112
|
-
|
|
10113
|
-
|
|
10114
|
-
|
|
10115
|
-
|
|
10895
|
+
const goal = await this.options.store.load(id);
|
|
10896
|
+
if (!goal) throw new GoalError("not_found", `unknown goal "${id}"`);
|
|
10897
|
+
if (isTerminalGoal(goal)) return goal;
|
|
10898
|
+
const stamp = this.now().toISOString();
|
|
10899
|
+
goal.status = "cancelled";
|
|
10900
|
+
goal.updatedAt = stamp;
|
|
10901
|
+
goal.finishedAt = stamp;
|
|
10902
|
+
delete goal.activeTaskId;
|
|
10903
|
+
await this.options.store.save(goal);
|
|
10904
|
+
return goal;
|
|
10905
|
+
}
|
|
10906
|
+
/** Resolves when the goal is finished (immediately for stored goals). */
|
|
10907
|
+
async wait(id) {
|
|
10908
|
+
if (this.active?.goal.id === id) return structuredClone(await this.active.done);
|
|
10909
|
+
return this.options.store.load(id);
|
|
10910
|
+
}
|
|
10116
10911
|
};
|
|
10117
|
-
|
|
10118
|
-
|
|
10119
|
-
|
|
10120
|
-
|
|
10121
|
-
|
|
10122
|
-
|
|
10123
|
-
|
|
10124
|
-
|
|
10912
|
+
var GOAL_ID = /^goal_[a-z0-9]{6,32}$/;
|
|
10913
|
+
var goalTaskSchema = z15.object({
|
|
10914
|
+
id: z15.string().min(1),
|
|
10915
|
+
title: z15.string(),
|
|
10916
|
+
description: z15.string().optional(),
|
|
10917
|
+
role: z15.string(),
|
|
10918
|
+
kind: z15.enum(["code", "artifact"]),
|
|
10919
|
+
status: taskStatusSchema,
|
|
10920
|
+
dependsOn: z15.array(z15.string()),
|
|
10921
|
+
acceptance: z15.array(z15.string()).optional(),
|
|
10922
|
+
round: z15.number().int().min(1),
|
|
10923
|
+
attempts: z15.number().int().min(0),
|
|
10924
|
+
artifact: z15.string().optional(),
|
|
10925
|
+
runnerTaskId: z15.string().optional(),
|
|
10926
|
+
notes: z15.string().optional()
|
|
10927
|
+
});
|
|
10928
|
+
var goalSchema = z15.object({
|
|
10929
|
+
id: z15.string().regex(GOAL_ID),
|
|
10930
|
+
objective: z15.string().min(1),
|
|
10931
|
+
status: z15.enum([
|
|
10932
|
+
"planning",
|
|
10933
|
+
"running",
|
|
10934
|
+
"reviewing",
|
|
10935
|
+
"planned",
|
|
10936
|
+
"succeeded",
|
|
10937
|
+
"failed",
|
|
10938
|
+
"cancelled",
|
|
10939
|
+
"budget_exhausted"
|
|
10940
|
+
]),
|
|
10941
|
+
round: z15.number().int().min(0),
|
|
10942
|
+
limits: z15.object({
|
|
10943
|
+
maxRounds: z15.number().int().min(1),
|
|
10944
|
+
maxTasks: z15.number().int().min(1),
|
|
10945
|
+
maxTokens: z15.number().int().min(1),
|
|
10946
|
+
tokensUsed: z15.number().int().min(0)
|
|
10947
|
+
}),
|
|
10948
|
+
roles: z15.array(
|
|
10949
|
+
z15.object({ id: z15.string(), description: z15.string(), route: z15.string().optional() })
|
|
10950
|
+
),
|
|
10951
|
+
tasks: z15.array(goalTaskSchema),
|
|
10952
|
+
activeTaskId: z15.string().optional(),
|
|
10953
|
+
summary: z15.string().optional(),
|
|
10954
|
+
lastError: z15.string().optional(),
|
|
10955
|
+
dryRun: z15.boolean().optional(),
|
|
10956
|
+
createdAt: z15.string(),
|
|
10957
|
+
updatedAt: z15.string(),
|
|
10958
|
+
finishedAt: z15.string().optional()
|
|
10959
|
+
});
|
|
10960
|
+
function isGoalId(id) {
|
|
10961
|
+
return GOAL_ID.test(id);
|
|
10125
10962
|
}
|
|
10126
|
-
|
|
10127
|
-
|
|
10128
|
-
|
|
10129
|
-
|
|
10130
|
-
|
|
10131
|
-
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
|
|
10137
|
-
|
|
10963
|
+
var GoalStore = class {
|
|
10964
|
+
constructor(root) {
|
|
10965
|
+
this.root = root;
|
|
10966
|
+
this.dir = join7(projectPaths(root).dir, "goals");
|
|
10967
|
+
}
|
|
10968
|
+
root;
|
|
10969
|
+
dir;
|
|
10970
|
+
goalDir(id) {
|
|
10971
|
+
if (!isGoalId(id)) throw new Error(`invalid goal id: ${id}`);
|
|
10972
|
+
return join7(this.dir, id);
|
|
10973
|
+
}
|
|
10974
|
+
async save(goal) {
|
|
10975
|
+
await atomicWriteFile(
|
|
10976
|
+
join7(this.goalDir(goal.id), "goal.json"),
|
|
10977
|
+
`${JSON.stringify(goal, null, 2)}
|
|
10978
|
+
`
|
|
10979
|
+
);
|
|
10980
|
+
}
|
|
10981
|
+
/** The goal, or `undefined` when unknown. Throws when `goal.json` is corrupt. */
|
|
10982
|
+
async load(id) {
|
|
10983
|
+
if (!isGoalId(id)) return void 0;
|
|
10984
|
+
const text = await readFileIfExists(join7(this.goalDir(id), "goal.json"));
|
|
10985
|
+
if (text === void 0) return void 0;
|
|
10986
|
+
return goalSchema.parse(JSON.parse(text));
|
|
10987
|
+
}
|
|
10988
|
+
/** Every readable goal, newest first. Corrupt entries are skipped. */
|
|
10989
|
+
async list() {
|
|
10990
|
+
let names;
|
|
10138
10991
|
try {
|
|
10139
|
-
|
|
10140
|
-
} catch {
|
|
10141
|
-
return
|
|
10142
|
-
|
|
10143
|
-
};
|
|
10144
|
-
async function run(request) {
|
|
10145
|
-
if (request.signal.aborted) throw abortError();
|
|
10146
|
-
const info = AGENTS[request.agent];
|
|
10147
|
-
const { command, prefix } = resolveBinary(request.agent);
|
|
10148
|
-
const args = agentArgs(request.agent, request.mode);
|
|
10149
|
-
if (needsShell(command) && ![...prefix, ...args].every(isShellSafeArg)) {
|
|
10150
|
-
return { ok: false, text: "", error: `${info.label}: unsafe argument for the Windows shell` };
|
|
10151
|
-
}
|
|
10152
|
-
const emit = (chunk) => {
|
|
10153
|
-
try {
|
|
10154
|
-
request.onOutput(chunk);
|
|
10155
|
-
} catch {
|
|
10156
|
-
}
|
|
10157
|
-
};
|
|
10158
|
-
let scratch;
|
|
10159
|
-
let cwd = request.cwd;
|
|
10160
|
-
if (request.agent === "agy" && request.mode === "plan") {
|
|
10161
|
-
scratch = mkdtempSync(path8.join(options.scratchDir ?? tmpdir(), "davecode-agy-plan-"));
|
|
10162
|
-
cwd = scratch;
|
|
10992
|
+
names = await readdir3(this.dir);
|
|
10993
|
+
} catch (err) {
|
|
10994
|
+
if (isNodeError(err, "ENOENT")) return [];
|
|
10995
|
+
throw err;
|
|
10163
10996
|
}
|
|
10164
|
-
const
|
|
10165
|
-
const
|
|
10166
|
-
const timedOut = () => ({
|
|
10167
|
-
ok: false,
|
|
10168
|
-
text: "",
|
|
10169
|
-
error: `${info.label} timed out after ${formatDuration(request.timeoutMs)}`
|
|
10170
|
-
});
|
|
10171
|
-
try {
|
|
10172
|
-
let cli;
|
|
10173
|
-
try {
|
|
10174
|
-
cli = runCli({
|
|
10175
|
-
provider: info.provider,
|
|
10176
|
-
accountId: "orchestrator",
|
|
10177
|
-
settings: { command, prefixArgs: prefix },
|
|
10178
|
-
args,
|
|
10179
|
-
env: childEnv,
|
|
10180
|
-
cwd,
|
|
10181
|
-
stdin: agentStdin(request.agent, request.mode, request.prompt),
|
|
10182
|
-
signal
|
|
10183
|
-
});
|
|
10184
|
-
} catch (err) {
|
|
10185
|
-
if (request.signal.aborted) throw abortError();
|
|
10186
|
-
if (timeout.aborted) return timedOut();
|
|
10187
|
-
return { ok: false, text: "", error: shortError(errText(err)) };
|
|
10188
|
-
}
|
|
10189
|
-
const parser = PARSERS[request.agent](emit, cwd);
|
|
10190
|
-
let streamError;
|
|
10191
|
-
try {
|
|
10192
|
-
for await (const event of cli.events) parser.handle(event);
|
|
10193
|
-
} catch (err) {
|
|
10194
|
-
streamError = errText(err);
|
|
10195
|
-
}
|
|
10196
|
-
parser.flush();
|
|
10197
|
-
let exit;
|
|
10997
|
+
const goals = [];
|
|
10998
|
+
for (const name of names.filter(isGoalId)) {
|
|
10198
10999
|
try {
|
|
10199
|
-
|
|
11000
|
+
const goal = await this.load(name);
|
|
11001
|
+
if (goal) goals.push(goal);
|
|
10200
11002
|
} catch {
|
|
10201
|
-
if (request.signal.aborted) throw abortError();
|
|
10202
|
-
return {
|
|
10203
|
-
ok: false,
|
|
10204
|
-
text: "",
|
|
10205
|
-
error: `${info.label} could not be started: is \`${info.binary}\` installed and on PATH?`
|
|
10206
|
-
};
|
|
10207
|
-
}
|
|
10208
|
-
if (request.signal.aborted) throw abortError();
|
|
10209
|
-
if (exit.aborted || timeout.aborted) return { ...timedOut(), text: parser.state.text };
|
|
10210
|
-
const { state } = parser;
|
|
10211
|
-
if (state.completed && !state.failure && exit.code === 0) {
|
|
10212
|
-
return {
|
|
10213
|
-
ok: true,
|
|
10214
|
-
text: state.text,
|
|
10215
|
-
...state.tokens !== void 0 ? { tokens: state.tokens } : {}
|
|
10216
|
-
};
|
|
10217
|
-
}
|
|
10218
|
-
const detail = state.failure ?? streamError ?? (tail(exit.stderr) || (exit.code === 0 ? "the CLI ended without a final result" : "no output"));
|
|
10219
|
-
return {
|
|
10220
|
-
ok: false,
|
|
10221
|
-
text: state.text,
|
|
10222
|
-
error: failureMessage(info, detail, `${detail}
|
|
10223
|
-
${tail(exit.stderr)}`, exit.code),
|
|
10224
|
-
...state.tokens !== void 0 ? { tokens: state.tokens } : {}
|
|
10225
|
-
};
|
|
10226
|
-
} finally {
|
|
10227
|
-
if (scratch) {
|
|
10228
|
-
try {
|
|
10229
|
-
rmSync4(scratch, { recursive: true, force: true, maxRetries: 3, retryDelay: 100 });
|
|
10230
|
-
} catch {
|
|
10231
|
-
}
|
|
10232
11003
|
}
|
|
10233
11004
|
}
|
|
11005
|
+
return goals.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
10234
11006
|
}
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
}
|
|
10239
|
-
|
|
11007
|
+
/** Writes a task's markdown artifact and returns its repository-relative path (`/`-separated). */
|
|
11008
|
+
async writeArtifact(goalId, taskId, markdown) {
|
|
11009
|
+
const path11 = join7(this.goalDir(goalId), `${taskId}.md`);
|
|
11010
|
+
await atomicWriteFile(path11, markdown.endsWith("\n") ? markdown : `${markdown}
|
|
11011
|
+
`);
|
|
11012
|
+
return relative2(this.root, path11).split("\\").join("/");
|
|
11013
|
+
}
|
|
11014
|
+
async readArtifact(goalId, taskId) {
|
|
11015
|
+
if (!isGoalId(goalId) || !/^[-a-zA-Z0-9_]{1,64}$/.test(taskId)) return void 0;
|
|
11016
|
+
return readFileIfExists(join7(this.goalDir(goalId), `${taskId}.md`));
|
|
11017
|
+
}
|
|
11018
|
+
};
|
|
11019
|
+
var PROJECT_CONTEXT_CHARS = 3e3;
|
|
11020
|
+
function createGoalService(engine, opts) {
|
|
11021
|
+
const { brain } = opts;
|
|
11022
|
+
const lock = opts.lock ?? new WorkspaceLock();
|
|
11023
|
+
const store = new GoalStore(brain.root);
|
|
11024
|
+
const codeRunner = new RunnerCodeTasks({
|
|
11025
|
+
engine,
|
|
11026
|
+
brain,
|
|
11027
|
+
lock,
|
|
11028
|
+
...opts.budgetContext ? { contextExtras: opts.budgetContext } : {},
|
|
11029
|
+
...opts.executor ? { executor: opts.executor } : {}
|
|
11030
|
+
});
|
|
11031
|
+
const projectContext = async () => {
|
|
11032
|
+
if (!await brain.isInitialised()) return "";
|
|
11033
|
+
const [architecture, state] = await Promise.all([brain.readArchitecture(), brain.readState()]);
|
|
11034
|
+
return [
|
|
11035
|
+
`ARCHITECTURE.md:
|
|
11036
|
+
${truncateHead(architecture.trim(), PROJECT_CONTEXT_CHARS)}`,
|
|
11037
|
+
`STATE.md:
|
|
11038
|
+
${truncateHead(state.trim(), PROJECT_CONTEXT_CHARS)}`
|
|
11039
|
+
].join("\n\n");
|
|
10240
11040
|
};
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
11041
|
+
const orchestrator = new GoalOrchestrator({
|
|
11042
|
+
router: engine.router,
|
|
11043
|
+
store,
|
|
11044
|
+
events: engine.events,
|
|
11045
|
+
config: engine.config.goal,
|
|
11046
|
+
codeRunner,
|
|
11047
|
+
projectContext,
|
|
11048
|
+
...opts.budgetContext ? { budgetContext: opts.budgetContext } : {}
|
|
11049
|
+
});
|
|
11050
|
+
return new GoalService({ orchestrator, store, lock });
|
|
10244
11051
|
}
|
|
10245
11052
|
var AGENT_DESCRIPTIONS = {
|
|
10246
11053
|
claude: "Claude Code (Anthropic): careful multi-file changes, refactors and reasoning about existing code.",
|
|
@@ -10330,15 +11137,15 @@ function buildTaskPrompt(input) {
|
|
|
10330
11137
|
);
|
|
10331
11138
|
return lines2.join("\n");
|
|
10332
11139
|
}
|
|
10333
|
-
var dependencySchema =
|
|
10334
|
-
var plannedTaskSchema2 =
|
|
10335
|
-
title:
|
|
10336
|
-
agent:
|
|
10337
|
-
prompt:
|
|
10338
|
-
dependsOn:
|
|
11140
|
+
var dependencySchema = z16.union([z16.number(), z16.string()]);
|
|
11141
|
+
var plannedTaskSchema2 = z16.object({
|
|
11142
|
+
title: z16.string().trim().min(1),
|
|
11143
|
+
agent: z16.string().optional(),
|
|
11144
|
+
prompt: z16.string().trim().min(1),
|
|
11145
|
+
dependsOn: z16.array(dependencySchema).nullish()
|
|
10339
11146
|
});
|
|
10340
|
-
var planReplySchema =
|
|
10341
|
-
tasks:
|
|
11147
|
+
var planReplySchema = z16.object({
|
|
11148
|
+
tasks: z16.array(plannedTaskSchema2).min(1, "the plan has no tasks")
|
|
10342
11149
|
});
|
|
10343
11150
|
function dependencyIndex(value) {
|
|
10344
11151
|
if (typeof value === "number") return Number.isInteger(value) ? value : void 0;
|
|
@@ -10351,8 +11158,8 @@ function clip2(text, max) {
|
|
|
10351
11158
|
function describeZodError(error) {
|
|
10352
11159
|
const issue = error.issues[0];
|
|
10353
11160
|
if (!issue) return "the reply does not match the expected shape";
|
|
10354
|
-
const
|
|
10355
|
-
return `${
|
|
11161
|
+
const path11 = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
11162
|
+
return `${path11}${issue.message}`;
|
|
10356
11163
|
}
|
|
10357
11164
|
function findCycle(tasks) {
|
|
10358
11165
|
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
@@ -10420,45 +11227,45 @@ function parsePlan(text, options) {
|
|
|
10420
11227
|
return { ok: true, tasks };
|
|
10421
11228
|
}
|
|
10422
11229
|
var ORCHESTRATOR_RUNS_KEPT = 20;
|
|
10423
|
-
var agentSchema =
|
|
10424
|
-
var taskSchema =
|
|
10425
|
-
id:
|
|
10426
|
-
title:
|
|
11230
|
+
var agentSchema = z17.enum(ORCHESTRATOR_AGENTS);
|
|
11231
|
+
var taskSchema = z17.object({
|
|
11232
|
+
id: z17.string().min(1),
|
|
11233
|
+
title: z17.string(),
|
|
10427
11234
|
agent: agentSchema,
|
|
10428
|
-
prompt:
|
|
10429
|
-
dependsOn:
|
|
10430
|
-
status:
|
|
10431
|
-
branch:
|
|
10432
|
-
summary:
|
|
10433
|
-
changedFiles:
|
|
10434
|
-
error:
|
|
10435
|
-
startedAt:
|
|
10436
|
-
finishedAt:
|
|
11235
|
+
prompt: z17.string(),
|
|
11236
|
+
dependsOn: z17.array(z17.string()),
|
|
11237
|
+
status: z17.enum(["pending", "running", "done", "failed", "conflict", "cancelled", "skipped"]),
|
|
11238
|
+
branch: z17.string().optional(),
|
|
11239
|
+
summary: z17.string().optional(),
|
|
11240
|
+
changedFiles: z17.array(z17.string()).optional(),
|
|
11241
|
+
error: z17.string().optional(),
|
|
11242
|
+
startedAt: z17.string().optional(),
|
|
11243
|
+
finishedAt: z17.string().optional()
|
|
10437
11244
|
});
|
|
10438
|
-
var orchestratorRunSchema =
|
|
10439
|
-
id:
|
|
10440
|
-
objective:
|
|
10441
|
-
status:
|
|
10442
|
-
agents:
|
|
11245
|
+
var orchestratorRunSchema = z17.object({
|
|
11246
|
+
id: z17.string().min(1),
|
|
11247
|
+
objective: z17.string(),
|
|
11248
|
+
status: z17.enum(["planning", "running", "done", "failed", "cancelled"]),
|
|
11249
|
+
agents: z17.array(agentSchema),
|
|
10443
11250
|
planner: agentSchema.optional(),
|
|
10444
|
-
baseBranch:
|
|
10445
|
-
baseCommit:
|
|
10446
|
-
integrationBranch:
|
|
10447
|
-
tasks:
|
|
10448
|
-
summary:
|
|
10449
|
-
error:
|
|
10450
|
-
appliedAt:
|
|
10451
|
-
createdAt:
|
|
10452
|
-
updatedAt:
|
|
11251
|
+
baseBranch: z17.string().optional(),
|
|
11252
|
+
baseCommit: z17.string().optional(),
|
|
11253
|
+
integrationBranch: z17.string().optional(),
|
|
11254
|
+
tasks: z17.array(taskSchema),
|
|
11255
|
+
summary: z17.string().optional(),
|
|
11256
|
+
error: z17.string().optional(),
|
|
11257
|
+
appliedAt: z17.string().optional(),
|
|
11258
|
+
createdAt: z17.string(),
|
|
11259
|
+
updatedAt: z17.string()
|
|
10453
11260
|
});
|
|
10454
|
-
var fileSchema2 =
|
|
10455
|
-
version:
|
|
10456
|
-
runs:
|
|
11261
|
+
var fileSchema2 = z17.object({
|
|
11262
|
+
version: z17.literal(1),
|
|
11263
|
+
runs: z17.array(z17.unknown())
|
|
10457
11264
|
});
|
|
10458
11265
|
function orchestratorRunsPath(home, projectDir) {
|
|
10459
11266
|
let key = resolve5(projectDir);
|
|
10460
11267
|
if (process.platform === "win32") key = key.toLowerCase();
|
|
10461
|
-
const hash =
|
|
11268
|
+
const hash = createHash4("sha256").update(key).digest("hex").slice(0, 16);
|
|
10462
11269
|
return join8(home, "orchestrator", hash, "runs.json");
|
|
10463
11270
|
}
|
|
10464
11271
|
function newestFirst(runs) {
|
|
@@ -10478,7 +11285,7 @@ var OrchestratorStore = class {
|
|
|
10478
11285
|
load() {
|
|
10479
11286
|
let text;
|
|
10480
11287
|
try {
|
|
10481
|
-
text =
|
|
11288
|
+
text = readFileSync5(this.path, "utf8");
|
|
10482
11289
|
} catch {
|
|
10483
11290
|
return [];
|
|
10484
11291
|
}
|
|
@@ -10561,7 +11368,7 @@ var OrchestratorEngine = class {
|
|
|
10561
11368
|
constructor(options) {
|
|
10562
11369
|
this.options = options;
|
|
10563
11370
|
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
10564
|
-
this.newId = options.newId ?? (() =>
|
|
11371
|
+
this.newId = options.newId ?? (() => randomBytes10(6).toString("hex"));
|
|
10565
11372
|
this.store = new OrchestratorStore(options.home, options.projectDir);
|
|
10566
11373
|
this.runs = this.store.load();
|
|
10567
11374
|
this.recoverInterrupted();
|
|
@@ -10987,7 +11794,7 @@ function describeOutcome(task) {
|
|
|
10987
11794
|
var SAFE_ID2 = /^[A-Za-z0-9_-]+$/;
|
|
10988
11795
|
var INTEGRATION_DIR = "integration";
|
|
10989
11796
|
var BRAIN_DIR2 = ".davecode";
|
|
10990
|
-
var
|
|
11797
|
+
var FALLBACK_IDENTITY2 = ["-c", "user.name=DaveCode", "-c", "user.email=davecode@localhost"];
|
|
10991
11798
|
function assertId(kind, value) {
|
|
10992
11799
|
if (!SAFE_ID2.test(value)) {
|
|
10993
11800
|
throw new GitError(`Invalid ${kind}: ${JSON.stringify(value)}`, [], null, "");
|
|
@@ -11007,27 +11814,27 @@ function errMessage(err) {
|
|
|
11007
11814
|
}
|
|
11008
11815
|
function realPath2(p) {
|
|
11009
11816
|
try {
|
|
11010
|
-
return
|
|
11817
|
+
return realpathSync3.native(p);
|
|
11011
11818
|
} catch {
|
|
11012
|
-
return
|
|
11819
|
+
return path10.resolve(p);
|
|
11013
11820
|
}
|
|
11014
11821
|
}
|
|
11015
|
-
function
|
|
11822
|
+
function samePath2(a, b) {
|
|
11016
11823
|
const na = realPath2(a);
|
|
11017
11824
|
const nb = realPath2(b);
|
|
11018
11825
|
return process.platform === "win32" ? na.toLowerCase() === nb.toLowerCase() : na === nb;
|
|
11019
11826
|
}
|
|
11020
11827
|
function isInside2(child, parent) {
|
|
11021
|
-
const rel =
|
|
11022
|
-
return rel === "" || !rel.startsWith("..") && !
|
|
11828
|
+
const rel = path10.relative(realPath2(parent), realPath2(child));
|
|
11829
|
+
return rel === "" || !rel.startsWith("..") && !path10.isAbsolute(rel);
|
|
11023
11830
|
}
|
|
11024
11831
|
function createGitWorkspace(options) {
|
|
11025
|
-
const projectDir =
|
|
11026
|
-
const worktreesDir =
|
|
11832
|
+
const projectDir = path10.resolve(options.projectDir);
|
|
11833
|
+
const worktreesDir = path10.resolve(options.worktreesDir);
|
|
11027
11834
|
const gitAt = (cwd) => new Git(cwd, { env: options.env });
|
|
11028
11835
|
const repo = gitAt(projectDir);
|
|
11029
|
-
const runDir = (runId) =>
|
|
11030
|
-
const integrationPath = (runId) =>
|
|
11836
|
+
const runDir = (runId) => path10.join(worktreesDir, assertId("run id", runId));
|
|
11837
|
+
const integrationPath = (runId) => path10.join(runDir(runId), INTEGRATION_DIR);
|
|
11031
11838
|
const integrationBranch = (runId) => `davecode/run-${assertId("run id", runId)}`;
|
|
11032
11839
|
const taskBases = /* @__PURE__ */ new Map();
|
|
11033
11840
|
let queue = Promise.resolve();
|
|
@@ -11045,7 +11852,7 @@ function createGitWorkspace(options) {
|
|
|
11045
11852
|
hasIdentity = true;
|
|
11046
11853
|
return [];
|
|
11047
11854
|
} catch {
|
|
11048
|
-
return
|
|
11855
|
+
return FALLBACK_IDENTITY2;
|
|
11049
11856
|
}
|
|
11050
11857
|
}
|
|
11051
11858
|
async function removeWorktree(target) {
|
|
@@ -11053,8 +11860,8 @@ function createGitWorkspace(options) {
|
|
|
11053
11860
|
await repo.run(["worktree", "remove", "--force", target]);
|
|
11054
11861
|
} catch {
|
|
11055
11862
|
}
|
|
11056
|
-
if (
|
|
11057
|
-
|
|
11863
|
+
if (existsSync6(target)) {
|
|
11864
|
+
rmSync6(target, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
11058
11865
|
}
|
|
11059
11866
|
try {
|
|
11060
11867
|
await repo.run(["worktree", "prune"]);
|
|
@@ -11130,10 +11937,10 @@ function createGitWorkspace(options) {
|
|
|
11130
11937
|
throw new GitError(`Invalid task id: ${JSON.stringify(taskId)}`, [], null, "");
|
|
11131
11938
|
}
|
|
11132
11939
|
const branch = `${integrationBranch(runId)}-${taskId}`;
|
|
11133
|
-
const target =
|
|
11940
|
+
const target = path10.join(runDir(runId), taskId);
|
|
11134
11941
|
const baseCommit = (await repo.run(["rev-parse", "--verify", "--quiet", `${assertSafeRef(base)}^{commit}`])).trim();
|
|
11135
11942
|
await addWorktree(target, branch, baseCommit);
|
|
11136
|
-
taskBases.set(
|
|
11943
|
+
taskBases.set(path10.resolve(target), baseCommit);
|
|
11137
11944
|
return { path: target, branch };
|
|
11138
11945
|
});
|
|
11139
11946
|
},
|
|
@@ -11153,7 +11960,7 @@ function createGitWorkspace(options) {
|
|
|
11153
11960
|
message
|
|
11154
11961
|
]);
|
|
11155
11962
|
}
|
|
11156
|
-
const base = taskBases.get(
|
|
11963
|
+
const base = taskBases.get(path10.resolve(workspace.path));
|
|
11157
11964
|
if (base) return nulList(await g.run(["diff", "--name-only", "-z", base, "HEAD"]));
|
|
11158
11965
|
if (!committed) return [];
|
|
11159
11966
|
return nulList(await g.run(["diff", "--name-only", "-z", "HEAD~1", "HEAD"]));
|
|
@@ -11162,7 +11969,7 @@ function createGitWorkspace(options) {
|
|
|
11162
11969
|
mergeIntoIntegration(runId, branch, message) {
|
|
11163
11970
|
return serial(async () => {
|
|
11164
11971
|
const target = integrationPath(runId);
|
|
11165
|
-
if (!
|
|
11972
|
+
if (!existsSync6(target)) {
|
|
11166
11973
|
return { ok: false, reason: `The integration worktree for run ${runId} is missing` };
|
|
11167
11974
|
}
|
|
11168
11975
|
return merge(gitAt(target), branch, message, true);
|
|
@@ -11207,18 +12014,18 @@ function createGitWorkspace(options) {
|
|
|
11207
12014
|
return;
|
|
11208
12015
|
}
|
|
11209
12016
|
try {
|
|
11210
|
-
const listed = lines(await repo.run(["worktree", "list", "--porcelain"])).filter((l) => l.startsWith("worktree ")).map((l) => l.slice("worktree ".length)).filter((p) => isInside2(p, dir) && !
|
|
12017
|
+
const listed = lines(await repo.run(["worktree", "list", "--porcelain"])).filter((l) => l.startsWith("worktree ")).map((l) => l.slice("worktree ".length)).filter((p) => isInside2(p, dir) && !samePath2(p, projectDir));
|
|
11211
12018
|
for (const target of listed) {
|
|
11212
12019
|
try {
|
|
11213
12020
|
await repo.run(["worktree", "remove", "--force", target]);
|
|
11214
12021
|
} catch {
|
|
11215
12022
|
}
|
|
11216
|
-
taskBases.delete(
|
|
12023
|
+
taskBases.delete(path10.resolve(target));
|
|
11217
12024
|
}
|
|
11218
12025
|
} catch {
|
|
11219
12026
|
}
|
|
11220
12027
|
try {
|
|
11221
|
-
|
|
12028
|
+
rmSync6(dir, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
11222
12029
|
} catch {
|
|
11223
12030
|
}
|
|
11224
12031
|
try {
|
|
@@ -11412,6 +12219,27 @@ export {
|
|
|
11412
12219
|
createAccountControl,
|
|
11413
12220
|
BRAND_MARK_SVG,
|
|
11414
12221
|
brandLogo,
|
|
12222
|
+
CHANGES_LIMITS,
|
|
12223
|
+
MAIN_WORKSPACE_ID,
|
|
12224
|
+
WORKSPACE_ID_PATTERN,
|
|
12225
|
+
PANE_BRANCH_PREFIX,
|
|
12226
|
+
workspaceIdSchema,
|
|
12227
|
+
createWorkspaceSchema,
|
|
12228
|
+
suggestSchema,
|
|
12229
|
+
shipSchema,
|
|
12230
|
+
mergeSchema,
|
|
12231
|
+
removeSchema,
|
|
12232
|
+
ChangesError,
|
|
12233
|
+
ORCHESTRATOR_AGENTS,
|
|
12234
|
+
ORCHESTRATOR_LIMITS,
|
|
12235
|
+
startRunSchema,
|
|
12236
|
+
OrchestratorError,
|
|
12237
|
+
createCliAgentRunner,
|
|
12238
|
+
fallbackSuggestion,
|
|
12239
|
+
parseAgentSuggestion,
|
|
12240
|
+
paneWorktreesDir,
|
|
12241
|
+
paneWorkspacePath,
|
|
12242
|
+
createChangesService,
|
|
11415
12243
|
DEFAULT_ROLES,
|
|
11416
12244
|
resolveRoles,
|
|
11417
12245
|
outboundAction,
|
|
@@ -11427,12 +12255,7 @@ export {
|
|
|
11427
12255
|
isGoalId,
|
|
11428
12256
|
GoalStore,
|
|
11429
12257
|
createGoalService,
|
|
11430
|
-
ORCHESTRATOR_AGENTS,
|
|
11431
|
-
ORCHESTRATOR_LIMITS,
|
|
11432
|
-
startRunSchema,
|
|
11433
|
-
OrchestratorError,
|
|
11434
|
-
createCliAgentRunner,
|
|
11435
12258
|
createOrchestratorService,
|
|
11436
12259
|
createGitWorkspace
|
|
11437
12260
|
};
|
|
11438
|
-
//# sourceMappingURL=chunk-
|
|
12261
|
+
//# sourceMappingURL=chunk-NBQIMB43.js.map
|