@pitboss/cli 0.4.9 → 0.4.10
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/cli.js +169 -54
- package/dist/event-worker.js +156 -49
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -21060,6 +21060,9 @@ var USAGE_NUMBER_KEYS = [
|
|
|
21060
21060
|
"cache_read_input_tokens",
|
|
21061
21061
|
"cache_creation_input_tokens"
|
|
21062
21062
|
];
|
|
21063
|
+
function notReportedUsage(reason) {
|
|
21064
|
+
return { notReported: true, reason };
|
|
21065
|
+
}
|
|
21063
21066
|
function finiteNumber(value) {
|
|
21064
21067
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
21065
21068
|
}
|
|
@@ -21127,14 +21130,32 @@ async function getTaskStatus(conn, id, logPrefix) {
|
|
|
21127
21130
|
return void 0;
|
|
21128
21131
|
}
|
|
21129
21132
|
}
|
|
21130
|
-
async function
|
|
21133
|
+
async function listUpdatedSince(conn, since, logPrefix) {
|
|
21134
|
+
try {
|
|
21135
|
+
const res = await fetch(
|
|
21136
|
+
`${conn.apiUrl}/tasks?repoId=${conn.repoId}&assignedTo=${conn.agentId}&updatedAfter=${encodeURIComponent(since)}`,
|
|
21137
|
+
{ headers: { Authorization: `Bearer ${conn.apiSecret}` } }
|
|
21138
|
+
);
|
|
21139
|
+
if (!res.ok) throw new Error(`task list failed (${res.status} ${res.statusText})`);
|
|
21140
|
+
const { data } = await res.json();
|
|
21141
|
+
return (data ?? []).filter((row) => typeof row.id === "string" && row.id.length > 0).filter((row) => row.status !== "assigned").map((row) => row.id);
|
|
21142
|
+
} catch (err) {
|
|
21143
|
+
console.error(
|
|
21144
|
+
`${logPrefix} Could not list tasks updated since the session started:`,
|
|
21145
|
+
err instanceof Error ? err.message : String(err)
|
|
21146
|
+
);
|
|
21147
|
+
return [];
|
|
21148
|
+
}
|
|
21149
|
+
}
|
|
21150
|
+
async function sessionTouchedTaskIds(conn, candidates, logPrefix, since) {
|
|
21131
21151
|
const touched = [];
|
|
21132
21152
|
for (const id of candidates) {
|
|
21133
21153
|
const status = await getTaskStatus(conn, id, logPrefix);
|
|
21134
21154
|
if (status !== void 0 && status !== "assigned") touched.push(id);
|
|
21135
21155
|
}
|
|
21136
21156
|
const inProgressAtClose = await listSessionTaskIds(conn, logPrefix, "in_progress");
|
|
21137
|
-
|
|
21157
|
+
const updated = since ? await listUpdatedSince(conn, since, logPrefix) : [];
|
|
21158
|
+
return unionTaskIds(touched, inProgressAtClose, updated);
|
|
21138
21159
|
}
|
|
21139
21160
|
function unionTaskIds(...lists) {
|
|
21140
21161
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -21148,34 +21169,87 @@ function unionTaskIds(...lists) {
|
|
|
21148
21169
|
}
|
|
21149
21170
|
return out;
|
|
21150
21171
|
}
|
|
21151
|
-
async function writeSessionUsage(conn, taskIds, usage, logPrefix) {
|
|
21152
|
-
const sessionUsage = {
|
|
21153
|
-
...usage,
|
|
21154
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
21155
|
-
sessionTaskIds: [...taskIds]
|
|
21156
|
-
};
|
|
21172
|
+
async function writeSessionUsage(conn, taskIds, usage, logPrefix, opts = {}) {
|
|
21157
21173
|
const headers = { "Content-Type": "application/json", Authorization: `Bearer ${conn.apiSecret}` };
|
|
21158
21174
|
for (const id of taskIds) {
|
|
21175
|
+
const slice = taskIds.length > 1 && !usage.notReported ? opts.slices?.[id] : void 0;
|
|
21176
|
+
const share = usage.notReported ? void 0 : slice || taskIds.length === 1 ? "task" : "session";
|
|
21177
|
+
const body = {
|
|
21178
|
+
usage: slice ?? usage,
|
|
21179
|
+
sessionTaskIds: taskIds,
|
|
21180
|
+
...opts.model ? { model: opts.model } : {},
|
|
21181
|
+
...share ? { share } : {}
|
|
21182
|
+
};
|
|
21159
21183
|
try {
|
|
21160
|
-
const
|
|
21161
|
-
method: "
|
|
21184
|
+
const res = await fetch(`${conn.apiUrl}/tasks/${id}/usage-report`, {
|
|
21185
|
+
method: "POST",
|
|
21162
21186
|
headers,
|
|
21163
|
-
body: JSON.stringify(
|
|
21187
|
+
body: JSON.stringify(body)
|
|
21164
21188
|
});
|
|
21165
|
-
if (!
|
|
21166
|
-
console.error(`${logPrefix} Could not
|
|
21189
|
+
if (!res.ok) {
|
|
21190
|
+
console.error(`${logPrefix} Could not report usage on ${id} (${res.status} ${res.statusText})`);
|
|
21167
21191
|
}
|
|
21168
21192
|
} catch (err) {
|
|
21169
21193
|
console.error(
|
|
21170
|
-
`${logPrefix} Could not
|
|
21194
|
+
`${logPrefix} Could not report usage on ${id}:`,
|
|
21171
21195
|
err instanceof Error ? err.message : String(err)
|
|
21172
21196
|
);
|
|
21173
21197
|
}
|
|
21174
21198
|
}
|
|
21175
21199
|
}
|
|
21176
|
-
async function recordSessionUsage(conn, startIds, usage, logPrefix) {
|
|
21200
|
+
async function recordSessionUsage(conn, startIds, usage, logPrefix, opts = {}) {
|
|
21177
21201
|
const start = await startIds;
|
|
21178
|
-
|
|
21202
|
+
const { since, ...rest } = opts;
|
|
21203
|
+
await writeSessionUsage(conn, await sessionTouchedTaskIds(conn, start, logPrefix, since), usage, logPrefix, rest);
|
|
21204
|
+
}
|
|
21205
|
+
var BOUNDARY_STATUSES = /* @__PURE__ */ new Set(["completed", "blocked", "pending_verification"]);
|
|
21206
|
+
function addUsage(into, from) {
|
|
21207
|
+
into.num_turns = (into.num_turns ?? 0) + (from.num_turns ?? 0);
|
|
21208
|
+
const total = into.usage ?? {};
|
|
21209
|
+
for (const key of USAGE_NUMBER_KEYS) {
|
|
21210
|
+
const n = from.usage?.[key];
|
|
21211
|
+
if (n !== void 0) total[key] = (total[key] ?? 0) + n;
|
|
21212
|
+
}
|
|
21213
|
+
into.usage = total;
|
|
21214
|
+
}
|
|
21215
|
+
function createUsageTracker() {
|
|
21216
|
+
let model;
|
|
21217
|
+
let pending = /* @__PURE__ */ new Map();
|
|
21218
|
+
const slices = {};
|
|
21219
|
+
let anon = 0;
|
|
21220
|
+
function cut(taskId) {
|
|
21221
|
+
const slice = slices[taskId] ?? {};
|
|
21222
|
+
for (const messageUsage of pending.values()) addUsage(slice, messageUsage);
|
|
21223
|
+
pending = /* @__PURE__ */ new Map();
|
|
21224
|
+
if (slice.usage) slices[taskId] = slice;
|
|
21225
|
+
}
|
|
21226
|
+
return {
|
|
21227
|
+
observe(event) {
|
|
21228
|
+
if (event.type === "system" && event.subtype === "init") {
|
|
21229
|
+
if (typeof event.model === "string" && event.model) model = event.model;
|
|
21230
|
+
return;
|
|
21231
|
+
}
|
|
21232
|
+
if (event.type !== "assistant") return;
|
|
21233
|
+
const message = event.message ?? {};
|
|
21234
|
+
const raw = message.usage;
|
|
21235
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
21236
|
+
const usage = pickNumbers(raw, USAGE_NUMBER_KEYS);
|
|
21237
|
+
if (Object.keys(usage).length > 0) {
|
|
21238
|
+
pending.set(typeof message.id === "string" ? message.id : `anon_${anon++}`, { num_turns: 1, usage });
|
|
21239
|
+
}
|
|
21240
|
+
}
|
|
21241
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
21242
|
+
for (const block of content) {
|
|
21243
|
+
if (block.type !== "tool_use" || typeof block.name !== "string" || !block.name.endsWith("update_task_status")) continue;
|
|
21244
|
+
const input = block.input ?? {};
|
|
21245
|
+
if (typeof input.taskId === "string" && typeof input.status === "string" && BOUNDARY_STATUSES.has(input.status)) {
|
|
21246
|
+
cut(input.taskId);
|
|
21247
|
+
}
|
|
21248
|
+
}
|
|
21249
|
+
},
|
|
21250
|
+
model: () => model,
|
|
21251
|
+
opts: () => ({ ...model ? { model } : {}, slices })
|
|
21252
|
+
};
|
|
21179
21253
|
}
|
|
21180
21254
|
|
|
21181
21255
|
// ../claude-worker/src/model.ts
|
|
@@ -21222,26 +21296,36 @@ function writeMcpConfig(config, prefix = "claude-worker") {
|
|
|
21222
21296
|
writeFileSync6(path3, JSON.stringify(mcpConfig), { mode: 384 });
|
|
21223
21297
|
return path3;
|
|
21224
21298
|
}
|
|
21299
|
+
function claudeArgs(opts) {
|
|
21300
|
+
return [
|
|
21301
|
+
"--print",
|
|
21302
|
+
"--mcp-config",
|
|
21303
|
+
opts.mcpConfigPath,
|
|
21304
|
+
"--no-session-persistence",
|
|
21305
|
+
"--dangerously-skip-permissions",
|
|
21306
|
+
"--output-format",
|
|
21307
|
+
"stream-json",
|
|
21308
|
+
"--verbose",
|
|
21309
|
+
"--model",
|
|
21310
|
+
opts.model,
|
|
21311
|
+
"--add-dir",
|
|
21312
|
+
opts.repoPath,
|
|
21313
|
+
...opts.isolateUserConfig ? ["--setting-sources", "project,local"] : []
|
|
21314
|
+
];
|
|
21315
|
+
}
|
|
21225
21316
|
async function runClaudeSession(config) {
|
|
21226
21317
|
const prompt = buildPrompt(config);
|
|
21227
21318
|
const mcpConfigPath = writeMcpConfig(config);
|
|
21319
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21228
21320
|
const taskIdsPromise = listSessionTaskIds(config, "[claude-worker]");
|
|
21229
21321
|
try {
|
|
21230
21322
|
return await new Promise((resolve9, reject) => {
|
|
21231
|
-
const args =
|
|
21232
|
-
"--print",
|
|
21233
|
-
"--mcp-config",
|
|
21323
|
+
const args = claudeArgs({
|
|
21234
21324
|
mcpConfigPath,
|
|
21235
|
-
|
|
21236
|
-
|
|
21237
|
-
"
|
|
21238
|
-
|
|
21239
|
-
"--verbose",
|
|
21240
|
-
"--model",
|
|
21241
|
-
pinnedModel() ?? config.model,
|
|
21242
|
-
"--add-dir",
|
|
21243
|
-
config.repoPath
|
|
21244
|
-
];
|
|
21325
|
+
model: pinnedModel() ?? config.model,
|
|
21326
|
+
repoPath: config.repoPath,
|
|
21327
|
+
isolateUserConfig: process.env.PITBOSS_ISOLATE_USER_CONFIG === "1"
|
|
21328
|
+
});
|
|
21245
21329
|
console.log(`[claude-worker] Spawning: ${config.claudeBin} ${args.slice(0, 4).join(" ")} ...`);
|
|
21246
21330
|
console.log(`[claude-worker] cwd: ${config.repoPath}`);
|
|
21247
21331
|
const { ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ...workerEnv } = process.env;
|
|
@@ -21257,6 +21341,8 @@ async function runClaudeSession(config) {
|
|
|
21257
21341
|
let buffer = "";
|
|
21258
21342
|
let resultError = "";
|
|
21259
21343
|
let sessionUsage;
|
|
21344
|
+
let sawResult = false;
|
|
21345
|
+
const tracker = createUsageTracker();
|
|
21260
21346
|
proc.stdout.on("data", (chunk) => {
|
|
21261
21347
|
buffer += chunk.toString();
|
|
21262
21348
|
const lines = buffer.split("\n");
|
|
@@ -21265,6 +21351,7 @@ async function runClaudeSession(config) {
|
|
|
21265
21351
|
if (!line.trim()) continue;
|
|
21266
21352
|
try {
|
|
21267
21353
|
const event = JSON.parse(line);
|
|
21354
|
+
tracker.observe(event);
|
|
21268
21355
|
if (event.type === "assistant") {
|
|
21269
21356
|
const content = event.message?.content ?? [];
|
|
21270
21357
|
for (const block of content) {
|
|
@@ -21278,6 +21365,7 @@ async function runClaudeSession(config) {
|
|
|
21278
21365
|
}
|
|
21279
21366
|
}
|
|
21280
21367
|
} else if (event.type === "result") {
|
|
21368
|
+
sawResult = true;
|
|
21281
21369
|
const parsed = parseResultUsage(event);
|
|
21282
21370
|
if (parsed) sessionUsage = parsed;
|
|
21283
21371
|
if (event.is_error) {
|
|
@@ -21299,9 +21387,9 @@ async function runClaudeSession(config) {
|
|
|
21299
21387
|
proc.on("close", (code, signal) => {
|
|
21300
21388
|
const unique = [...new Set(toolsUsed)];
|
|
21301
21389
|
console.log(`[claude-worker] Done \u2014 tools used: ${unique.length ? unique.join(", ") : "none"}`);
|
|
21302
|
-
const usage = sessionUsage;
|
|
21303
|
-
if (
|
|
21304
|
-
const finish =
|
|
21390
|
+
const usage = sessionUsage ?? notReportedUsage(sawResult ? "adapter" : "killed");
|
|
21391
|
+
if (sessionUsage) console.log(formatSessionUsageLine("[claude-worker]", sessionUsage));
|
|
21392
|
+
const finish = recordSessionUsage(config, taskIdsPromise, usage, "[claude-worker]", { since: startedAt, ...tracker.opts() });
|
|
21305
21393
|
const settle = () => {
|
|
21306
21394
|
if (signal || code !== 0 && code !== null) {
|
|
21307
21395
|
const detail = (resultError || stderrOutput || "").trim();
|
|
@@ -21369,6 +21457,9 @@ function readCursorEvent(line) {
|
|
|
21369
21457
|
} catch {
|
|
21370
21458
|
return {};
|
|
21371
21459
|
}
|
|
21460
|
+
if (event.type === "system" && event.subtype === "init") {
|
|
21461
|
+
return typeof event.model === "string" && event.model ? { model: event.model } : {};
|
|
21462
|
+
}
|
|
21372
21463
|
if (event.type === "tool_call" && event.subtype === "started") {
|
|
21373
21464
|
const call = event.tool_call ?? {};
|
|
21374
21465
|
const [kind] = Object.keys(call);
|
|
@@ -21386,6 +21477,7 @@ function readCursorEvent(line) {
|
|
|
21386
21477
|
async function runCursorSession(config) {
|
|
21387
21478
|
const args = cursorArgs(config.repoPath, buildPrompt(config), pinnedModel());
|
|
21388
21479
|
console.log(`[cursor-worker] Spawning: ${bin()} ${args.slice(0, 5).join(" ")} ...`);
|
|
21480
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21389
21481
|
const taskIdsPromise = listSessionTaskIds(config, "[cursor-worker]");
|
|
21390
21482
|
return new Promise((resolve9, reject) => {
|
|
21391
21483
|
const proc = spawn3(bin(), args, { cwd: config.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -21395,12 +21487,14 @@ async function runCursorSession(config) {
|
|
|
21395
21487
|
let resultError = "";
|
|
21396
21488
|
let stderrOutput = "";
|
|
21397
21489
|
let sessionUsage;
|
|
21490
|
+
let model;
|
|
21398
21491
|
proc.stdout.on("data", (chunk) => {
|
|
21399
21492
|
buffer += chunk.toString();
|
|
21400
21493
|
const lines = buffer.split("\n");
|
|
21401
21494
|
buffer = lines.pop() ?? "";
|
|
21402
21495
|
for (const line of lines) {
|
|
21403
|
-
const { tool, error, usage } = readCursorEvent(line);
|
|
21496
|
+
const { tool, error, usage, model: initModel } = readCursorEvent(line);
|
|
21497
|
+
if (initModel) model = initModel;
|
|
21404
21498
|
if (tool) {
|
|
21405
21499
|
toolsUsed.push(tool);
|
|
21406
21500
|
process.stdout.write(` \u2699 ${tool}
|
|
@@ -21418,9 +21512,9 @@ async function runCursorSession(config) {
|
|
|
21418
21512
|
});
|
|
21419
21513
|
proc.on("close", (code, signal) => {
|
|
21420
21514
|
console.log(`[cursor-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
21421
|
-
const usage = sessionUsage;
|
|
21422
|
-
if (
|
|
21423
|
-
const finish =
|
|
21515
|
+
const usage = sessionUsage ?? notReportedUsage("killed");
|
|
21516
|
+
if (sessionUsage) console.log(formatSessionUsageLine("[cursor-worker]", sessionUsage));
|
|
21517
|
+
const finish = recordSessionUsage(config, taskIdsPromise, usage, "[cursor-worker]", { since: startedAt, ...model ? { model } : {} });
|
|
21424
21518
|
const settle = () => {
|
|
21425
21519
|
if (signal || code !== 0 && code !== null) {
|
|
21426
21520
|
const detail = (resultError || stderrOutput).trim();
|
|
@@ -21491,6 +21585,7 @@ function readCopilotEvent(line) {
|
|
|
21491
21585
|
const type = typeof event.type === "string" ? event.type : "";
|
|
21492
21586
|
const data = event.data ?? {};
|
|
21493
21587
|
const message = typeof data.message === "string" ? data.message : "";
|
|
21588
|
+
if (type === "session.tools_updated" && typeof data.model === "string" && data.model) return { model: data.model };
|
|
21494
21589
|
if (/^tool\..*start/.test(type) && typeof data.toolName === "string") return { tool: data.toolName };
|
|
21495
21590
|
if (type === "session.error") return { error: message || "copilot session error" };
|
|
21496
21591
|
if (type === "result" && typeof event.exitCode === "number" && event.exitCode !== 0) return { error: `copilot finished with exit code ${event.exitCode}` };
|
|
@@ -21500,6 +21595,8 @@ function readCopilotEvent(line) {
|
|
|
21500
21595
|
async function runCopilotSession(config) {
|
|
21501
21596
|
const mcpConfigPath = writeMcpConfig(config, "copilot-worker");
|
|
21502
21597
|
const args = copilotArgs(config.repoPath, buildPrompt(config), mcpConfigPath, pinnedModel());
|
|
21598
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21599
|
+
const taskIdsPromise = listSessionTaskIds(config, "[copilot-worker]");
|
|
21503
21600
|
console.log(`[copilot-worker] Spawning: ${bin2()} -p <prompt> ${args.slice(2, 6).join(" ")} ...`);
|
|
21504
21601
|
return new Promise((resolve9, reject) => {
|
|
21505
21602
|
const proc = spawn4(bin2(), args, { cwd: config.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -21508,12 +21605,14 @@ async function runCopilotSession(config) {
|
|
|
21508
21605
|
let buffer = "";
|
|
21509
21606
|
let sessionError = "";
|
|
21510
21607
|
let stderrOutput = "";
|
|
21608
|
+
let model;
|
|
21511
21609
|
proc.stdout.on("data", (chunk) => {
|
|
21512
21610
|
buffer += chunk.toString();
|
|
21513
21611
|
const lines = buffer.split("\n");
|
|
21514
21612
|
buffer = lines.pop() ?? "";
|
|
21515
21613
|
for (const line of lines) {
|
|
21516
|
-
const { tool, error } = readCopilotEvent(line);
|
|
21614
|
+
const { tool, error, model: initModel } = readCopilotEvent(line);
|
|
21615
|
+
if (initModel) model = initModel;
|
|
21517
21616
|
if (tool) {
|
|
21518
21617
|
toolsUsed.push(tool);
|
|
21519
21618
|
process.stdout.write(` \u2699 ${tool}
|
|
@@ -21534,12 +21633,15 @@ async function runCopilotSession(config) {
|
|
|
21534
21633
|
} catch {
|
|
21535
21634
|
}
|
|
21536
21635
|
console.log(`[copilot-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
21537
|
-
|
|
21538
|
-
|
|
21539
|
-
|
|
21540
|
-
|
|
21541
|
-
|
|
21542
|
-
|
|
21636
|
+
const settle = () => {
|
|
21637
|
+
if (sessionError || signal || code !== 0 && code !== null) {
|
|
21638
|
+
const detail = (sessionError || stderrOutput).trim();
|
|
21639
|
+
reject(new Error(`copilot exited with ${signal ?? `code ${code}`}${detail ? `: ${detail}` : ""}`));
|
|
21640
|
+
} else {
|
|
21641
|
+
resolve9([...new Set(toolsUsed)]);
|
|
21642
|
+
}
|
|
21643
|
+
};
|
|
21644
|
+
recordSessionUsage(config, taskIdsPromise, notReportedUsage("adapter"), "[copilot-worker]", { since: startedAt, ...model ? { model } : {} }).then(settle, settle);
|
|
21543
21645
|
});
|
|
21544
21646
|
proc.on("error", (err) => {
|
|
21545
21647
|
try {
|
|
@@ -21633,6 +21735,8 @@ async function runCodexSession(config) {
|
|
|
21633
21735
|
}
|
|
21634
21736
|
};
|
|
21635
21737
|
const args = codexArgs(config.repoPath, buildPrompt(config), profile, pinnedModel());
|
|
21738
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
21739
|
+
const taskIdsPromise = listSessionTaskIds(config, "[codex-worker]");
|
|
21636
21740
|
console.log(`[codex-worker] Spawning: ${bin3()} ${args.slice(0, 6).join(" ")} ...`);
|
|
21637
21741
|
return new Promise((resolve9, reject) => {
|
|
21638
21742
|
const proc = spawn5(bin3(), args, { cwd: config.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -21664,12 +21768,15 @@ async function runCodexSession(config) {
|
|
|
21664
21768
|
proc.on("close", (code, signal) => {
|
|
21665
21769
|
cleanup();
|
|
21666
21770
|
console.log(`[codex-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
21667
|
-
|
|
21668
|
-
|
|
21669
|
-
|
|
21670
|
-
|
|
21671
|
-
|
|
21672
|
-
|
|
21771
|
+
const settle = () => {
|
|
21772
|
+
if (sessionError || signal || code !== 0 && code !== null) {
|
|
21773
|
+
const detail = (sessionError || stderrOutput).trim();
|
|
21774
|
+
reject(new Error(`codex exited with ${signal ?? `code ${code}`}${detail ? `: ${detail}` : ""}`));
|
|
21775
|
+
} else {
|
|
21776
|
+
resolve9([...new Set(toolsUsed)]);
|
|
21777
|
+
}
|
|
21778
|
+
};
|
|
21779
|
+
recordSessionUsage(config, taskIdsPromise, notReportedUsage("adapter"), "[codex-worker]", { since: startedAt }).then(settle, settle);
|
|
21673
21780
|
});
|
|
21674
21781
|
proc.on("error", (err) => {
|
|
21675
21782
|
cleanup();
|
|
@@ -21781,7 +21888,8 @@ function poolMembers(pool) {
|
|
|
21781
21888
|
worktree: join16(pool.worktreeRoot, name),
|
|
21782
21889
|
specialization: pool.specialization,
|
|
21783
21890
|
...pool.tier ? { tier: pool.tier } : {},
|
|
21784
|
-
...pool.model ? { model: pool.model } : {}
|
|
21891
|
+
...pool.model ? { model: pool.model } : {},
|
|
21892
|
+
...pool.isolateUserConfig ? { isolateUserConfig: true } : {}
|
|
21785
21893
|
};
|
|
21786
21894
|
});
|
|
21787
21895
|
}
|
|
@@ -21811,6 +21919,12 @@ function modelOf(a, i) {
|
|
|
21811
21919
|
}
|
|
21812
21920
|
return { model: a.model };
|
|
21813
21921
|
}
|
|
21922
|
+
function isolateOf(a, i, runtime) {
|
|
21923
|
+
if (a.isolateUserConfig === void 0 || a.isolateUserConfig === false) return {};
|
|
21924
|
+
if (a.isolateUserConfig !== true) throw new Error(`agents[${i}].isolateUserConfig must be true or false`);
|
|
21925
|
+
if (runtime !== "claude") throw new Error(`agents[${i}]: isolateUserConfig only applies to the claude runtime`);
|
|
21926
|
+
return { isolateUserConfig: true };
|
|
21927
|
+
}
|
|
21814
21928
|
function parsePool(a, i, specDir) {
|
|
21815
21929
|
if (a.name !== void 0 || a.worktree !== void 0) throw new Error(`agents[${i}] must have either name or pool, not both`);
|
|
21816
21930
|
if (typeof a.pool !== "string" || !POOL_NAME.test(a.pool)) throw new Error(`agents[${i}].pool must be letters, digits, '.', '_' or '-' (at most 60)`);
|
|
@@ -21829,7 +21943,7 @@ function parsePool(a, i, specDir) {
|
|
|
21829
21943
|
const base2 = resolve5(specDir, a.base);
|
|
21830
21944
|
const worktreeRoot = resolve5(specDir, a.worktreeRoot);
|
|
21831
21945
|
if (isWithin(worktreeRoot, base2)) throw new Error(`agents[${i}].worktreeRoot ${worktreeRoot} is inside base ${base2}; pool worktrees never go inside your checkout`);
|
|
21832
|
-
return { pool: a.pool, runtime, base: base2, worktreeRoot, specialization, ...tierOf(a, i, specialization), ...modelOf(a, i), min, max };
|
|
21946
|
+
return { pool: a.pool, runtime, base: base2, worktreeRoot, specialization, ...tierOf(a, i, specialization), ...modelOf(a, i), ...isolateOf(a, i, runtime), min, max };
|
|
21833
21947
|
}
|
|
21834
21948
|
function parseSpec(text, specDir) {
|
|
21835
21949
|
let raw;
|
|
@@ -21856,7 +21970,7 @@ function parseSpec(text, specDir) {
|
|
|
21856
21970
|
if (typeof a.worktree !== "string" || !a.worktree) throw new Error(`agents[${i}].worktree is required`);
|
|
21857
21971
|
const runtime = runtimeOf(a, i);
|
|
21858
21972
|
const specialization = specializationOf(a, i);
|
|
21859
|
-
agents.push({ name: a.name, runtime, worktree: resolve5(specDir, a.worktree), specialization, ...tierOf(a, i, specialization), ...modelOf(a, i) });
|
|
21973
|
+
agents.push({ name: a.name, runtime, worktree: resolve5(specDir, a.worktree), specialization, ...tierOf(a, i, specialization), ...modelOf(a, i), ...isolateOf(a, i, runtime) });
|
|
21860
21974
|
});
|
|
21861
21975
|
for (const p of pools) {
|
|
21862
21976
|
for (const a of agents) {
|
|
@@ -22731,7 +22845,7 @@ function inspectProcess(pid) {
|
|
|
22731
22845
|
}
|
|
22732
22846
|
}
|
|
22733
22847
|
function childSpec(agent, creds, worker, onDemand = false) {
|
|
22734
|
-
const { ANTHROPIC_API_KEY: _key, ANTHROPIC_AUTH_TOKEN: _tok, ...inherited } = process.env;
|
|
22848
|
+
const { ANTHROPIC_API_KEY: _key, ANTHROPIC_AUTH_TOKEN: _tok, PITBOSS_ISOLATE_USER_CONFIG: _iso, ...inherited } = process.env;
|
|
22735
22849
|
return {
|
|
22736
22850
|
name: agent.name,
|
|
22737
22851
|
command: worker.command,
|
|
@@ -22749,7 +22863,8 @@ function childSpec(agent, creds, worker, onDemand = false) {
|
|
|
22749
22863
|
SPECIALIZATION: agent.specialization,
|
|
22750
22864
|
WORKER_RUNTIME: agent.runtime,
|
|
22751
22865
|
PITBOSS_ACTIVITY_FILE: activityPath(agent.name),
|
|
22752
|
-
PITBOSS_MODEL_FILE: resolve8(modelPath(agent.name))
|
|
22866
|
+
PITBOSS_MODEL_FILE: resolve8(modelPath(agent.name)),
|
|
22867
|
+
...agent.isolateUserConfig ? { PITBOSS_ISOLATE_USER_CONFIG: "1" } : {}
|
|
22753
22868
|
}
|
|
22754
22869
|
};
|
|
22755
22870
|
}
|
package/dist/event-worker.js
CHANGED
|
@@ -449,6 +449,9 @@ var USAGE_NUMBER_KEYS = [
|
|
|
449
449
|
"cache_read_input_tokens",
|
|
450
450
|
"cache_creation_input_tokens"
|
|
451
451
|
];
|
|
452
|
+
function notReportedUsage(reason) {
|
|
453
|
+
return { notReported: true, reason };
|
|
454
|
+
}
|
|
452
455
|
function finiteNumber(value) {
|
|
453
456
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
454
457
|
}
|
|
@@ -516,14 +519,32 @@ async function getTaskStatus(conn, id, logPrefix) {
|
|
|
516
519
|
return void 0;
|
|
517
520
|
}
|
|
518
521
|
}
|
|
519
|
-
async function
|
|
522
|
+
async function listUpdatedSince(conn, since, logPrefix) {
|
|
523
|
+
try {
|
|
524
|
+
const res = await fetch(
|
|
525
|
+
`${conn.apiUrl}/tasks?repoId=${conn.repoId}&assignedTo=${conn.agentId}&updatedAfter=${encodeURIComponent(since)}`,
|
|
526
|
+
{ headers: { Authorization: `Bearer ${conn.apiSecret}` } }
|
|
527
|
+
);
|
|
528
|
+
if (!res.ok) throw new Error(`task list failed (${res.status} ${res.statusText})`);
|
|
529
|
+
const { data } = await res.json();
|
|
530
|
+
return (data ?? []).filter((row) => typeof row.id === "string" && row.id.length > 0).filter((row) => row.status !== "assigned").map((row) => row.id);
|
|
531
|
+
} catch (err) {
|
|
532
|
+
console.error(
|
|
533
|
+
`${logPrefix} Could not list tasks updated since the session started:`,
|
|
534
|
+
err instanceof Error ? err.message : String(err)
|
|
535
|
+
);
|
|
536
|
+
return [];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
async function sessionTouchedTaskIds(conn, candidates, logPrefix, since) {
|
|
520
540
|
const touched = [];
|
|
521
541
|
for (const id of candidates) {
|
|
522
542
|
const status = await getTaskStatus(conn, id, logPrefix);
|
|
523
543
|
if (status !== void 0 && status !== "assigned") touched.push(id);
|
|
524
544
|
}
|
|
525
545
|
const inProgressAtClose = await listSessionTaskIds(conn, logPrefix, "in_progress");
|
|
526
|
-
|
|
546
|
+
const updated = since ? await listUpdatedSince(conn, since, logPrefix) : [];
|
|
547
|
+
return unionTaskIds(touched, inProgressAtClose, updated);
|
|
527
548
|
}
|
|
528
549
|
function unionTaskIds(...lists) {
|
|
529
550
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -537,34 +558,87 @@ function unionTaskIds(...lists) {
|
|
|
537
558
|
}
|
|
538
559
|
return out;
|
|
539
560
|
}
|
|
540
|
-
async function writeSessionUsage(conn, taskIds, usage, logPrefix) {
|
|
541
|
-
const sessionUsage = {
|
|
542
|
-
...usage,
|
|
543
|
-
at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
544
|
-
sessionTaskIds: [...taskIds]
|
|
545
|
-
};
|
|
561
|
+
async function writeSessionUsage(conn, taskIds, usage, logPrefix, opts = {}) {
|
|
546
562
|
const headers = { "Content-Type": "application/json", Authorization: `Bearer ${conn.apiSecret}` };
|
|
547
563
|
for (const id of taskIds) {
|
|
564
|
+
const slice = taskIds.length > 1 && !usage.notReported ? opts.slices?.[id] : void 0;
|
|
565
|
+
const share = usage.notReported ? void 0 : slice || taskIds.length === 1 ? "task" : "session";
|
|
566
|
+
const body = {
|
|
567
|
+
usage: slice ?? usage,
|
|
568
|
+
sessionTaskIds: taskIds,
|
|
569
|
+
...opts.model ? { model: opts.model } : {},
|
|
570
|
+
...share ? { share } : {}
|
|
571
|
+
};
|
|
548
572
|
try {
|
|
549
|
-
const
|
|
550
|
-
method: "
|
|
573
|
+
const res = await fetch(`${conn.apiUrl}/tasks/${id}/usage-report`, {
|
|
574
|
+
method: "POST",
|
|
551
575
|
headers,
|
|
552
|
-
body: JSON.stringify(
|
|
576
|
+
body: JSON.stringify(body)
|
|
553
577
|
});
|
|
554
|
-
if (!
|
|
555
|
-
console.error(`${logPrefix} Could not
|
|
578
|
+
if (!res.ok) {
|
|
579
|
+
console.error(`${logPrefix} Could not report usage on ${id} (${res.status} ${res.statusText})`);
|
|
556
580
|
}
|
|
557
581
|
} catch (err) {
|
|
558
582
|
console.error(
|
|
559
|
-
`${logPrefix} Could not
|
|
583
|
+
`${logPrefix} Could not report usage on ${id}:`,
|
|
560
584
|
err instanceof Error ? err.message : String(err)
|
|
561
585
|
);
|
|
562
586
|
}
|
|
563
587
|
}
|
|
564
588
|
}
|
|
565
|
-
async function recordSessionUsage(conn, startIds, usage, logPrefix) {
|
|
589
|
+
async function recordSessionUsage(conn, startIds, usage, logPrefix, opts = {}) {
|
|
566
590
|
const start = await startIds;
|
|
567
|
-
|
|
591
|
+
const { since, ...rest } = opts;
|
|
592
|
+
await writeSessionUsage(conn, await sessionTouchedTaskIds(conn, start, logPrefix, since), usage, logPrefix, rest);
|
|
593
|
+
}
|
|
594
|
+
var BOUNDARY_STATUSES = /* @__PURE__ */ new Set(["completed", "blocked", "pending_verification"]);
|
|
595
|
+
function addUsage(into, from) {
|
|
596
|
+
into.num_turns = (into.num_turns ?? 0) + (from.num_turns ?? 0);
|
|
597
|
+
const total = into.usage ?? {};
|
|
598
|
+
for (const key of USAGE_NUMBER_KEYS) {
|
|
599
|
+
const n = from.usage?.[key];
|
|
600
|
+
if (n !== void 0) total[key] = (total[key] ?? 0) + n;
|
|
601
|
+
}
|
|
602
|
+
into.usage = total;
|
|
603
|
+
}
|
|
604
|
+
function createUsageTracker() {
|
|
605
|
+
let model;
|
|
606
|
+
let pending = /* @__PURE__ */ new Map();
|
|
607
|
+
const slices = {};
|
|
608
|
+
let anon = 0;
|
|
609
|
+
function cut(taskId) {
|
|
610
|
+
const slice = slices[taskId] ?? {};
|
|
611
|
+
for (const messageUsage of pending.values()) addUsage(slice, messageUsage);
|
|
612
|
+
pending = /* @__PURE__ */ new Map();
|
|
613
|
+
if (slice.usage) slices[taskId] = slice;
|
|
614
|
+
}
|
|
615
|
+
return {
|
|
616
|
+
observe(event) {
|
|
617
|
+
if (event.type === "system" && event.subtype === "init") {
|
|
618
|
+
if (typeof event.model === "string" && event.model) model = event.model;
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
if (event.type !== "assistant") return;
|
|
622
|
+
const message = event.message ?? {};
|
|
623
|
+
const raw = message.usage;
|
|
624
|
+
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
625
|
+
const usage = pickNumbers(raw, USAGE_NUMBER_KEYS);
|
|
626
|
+
if (Object.keys(usage).length > 0) {
|
|
627
|
+
pending.set(typeof message.id === "string" ? message.id : `anon_${anon++}`, { num_turns: 1, usage });
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
631
|
+
for (const block of content) {
|
|
632
|
+
if (block.type !== "tool_use" || typeof block.name !== "string" || !block.name.endsWith("update_task_status")) continue;
|
|
633
|
+
const input = block.input ?? {};
|
|
634
|
+
if (typeof input.taskId === "string" && typeof input.status === "string" && BOUNDARY_STATUSES.has(input.status)) {
|
|
635
|
+
cut(input.taskId);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
},
|
|
639
|
+
model: () => model,
|
|
640
|
+
opts: () => ({ ...model ? { model } : {}, slices })
|
|
641
|
+
};
|
|
568
642
|
}
|
|
569
643
|
|
|
570
644
|
// ../claude-worker/src/model.ts
|
|
@@ -611,26 +685,36 @@ function writeMcpConfig(config2, prefix = "claude-worker") {
|
|
|
611
685
|
writeFileSync(path, JSON.stringify(mcpConfig), { mode: 384 });
|
|
612
686
|
return path;
|
|
613
687
|
}
|
|
688
|
+
function claudeArgs(opts) {
|
|
689
|
+
return [
|
|
690
|
+
"--print",
|
|
691
|
+
"--mcp-config",
|
|
692
|
+
opts.mcpConfigPath,
|
|
693
|
+
"--no-session-persistence",
|
|
694
|
+
"--dangerously-skip-permissions",
|
|
695
|
+
"--output-format",
|
|
696
|
+
"stream-json",
|
|
697
|
+
"--verbose",
|
|
698
|
+
"--model",
|
|
699
|
+
opts.model,
|
|
700
|
+
"--add-dir",
|
|
701
|
+
opts.repoPath,
|
|
702
|
+
...opts.isolateUserConfig ? ["--setting-sources", "project,local"] : []
|
|
703
|
+
];
|
|
704
|
+
}
|
|
614
705
|
async function runClaudeSession(config2) {
|
|
615
706
|
const prompt = buildPrompt(config2);
|
|
616
707
|
const mcpConfigPath = writeMcpConfig(config2);
|
|
708
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
617
709
|
const taskIdsPromise = listSessionTaskIds(config2, "[claude-worker]");
|
|
618
710
|
try {
|
|
619
711
|
return await new Promise((resolve, reject) => {
|
|
620
|
-
const args =
|
|
621
|
-
"--print",
|
|
622
|
-
"--mcp-config",
|
|
712
|
+
const args = claudeArgs({
|
|
623
713
|
mcpConfigPath,
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
"
|
|
627
|
-
|
|
628
|
-
"--verbose",
|
|
629
|
-
"--model",
|
|
630
|
-
pinnedModel() ?? config2.model,
|
|
631
|
-
"--add-dir",
|
|
632
|
-
config2.repoPath
|
|
633
|
-
];
|
|
714
|
+
model: pinnedModel() ?? config2.model,
|
|
715
|
+
repoPath: config2.repoPath,
|
|
716
|
+
isolateUserConfig: process.env.PITBOSS_ISOLATE_USER_CONFIG === "1"
|
|
717
|
+
});
|
|
634
718
|
console.log(`[claude-worker] Spawning: ${config2.claudeBin} ${args.slice(0, 4).join(" ")} ...`);
|
|
635
719
|
console.log(`[claude-worker] cwd: ${config2.repoPath}`);
|
|
636
720
|
const { ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, ...workerEnv } = process.env;
|
|
@@ -646,6 +730,8 @@ async function runClaudeSession(config2) {
|
|
|
646
730
|
let buffer = "";
|
|
647
731
|
let resultError = "";
|
|
648
732
|
let sessionUsage;
|
|
733
|
+
let sawResult = false;
|
|
734
|
+
const tracker = createUsageTracker();
|
|
649
735
|
proc.stdout.on("data", (chunk) => {
|
|
650
736
|
buffer += chunk.toString();
|
|
651
737
|
const lines = buffer.split("\n");
|
|
@@ -654,6 +740,7 @@ async function runClaudeSession(config2) {
|
|
|
654
740
|
if (!line.trim()) continue;
|
|
655
741
|
try {
|
|
656
742
|
const event = JSON.parse(line);
|
|
743
|
+
tracker.observe(event);
|
|
657
744
|
if (event.type === "assistant") {
|
|
658
745
|
const content = event.message?.content ?? [];
|
|
659
746
|
for (const block of content) {
|
|
@@ -667,6 +754,7 @@ async function runClaudeSession(config2) {
|
|
|
667
754
|
}
|
|
668
755
|
}
|
|
669
756
|
} else if (event.type === "result") {
|
|
757
|
+
sawResult = true;
|
|
670
758
|
const parsed = parseResultUsage(event);
|
|
671
759
|
if (parsed) sessionUsage = parsed;
|
|
672
760
|
if (event.is_error) {
|
|
@@ -688,9 +776,9 @@ async function runClaudeSession(config2) {
|
|
|
688
776
|
proc.on("close", (code, signal) => {
|
|
689
777
|
const unique = [...new Set(toolsUsed)];
|
|
690
778
|
console.log(`[claude-worker] Done \u2014 tools used: ${unique.length ? unique.join(", ") : "none"}`);
|
|
691
|
-
const usage = sessionUsage;
|
|
692
|
-
if (
|
|
693
|
-
const finish =
|
|
779
|
+
const usage = sessionUsage ?? notReportedUsage(sawResult ? "adapter" : "killed");
|
|
780
|
+
if (sessionUsage) console.log(formatSessionUsageLine("[claude-worker]", sessionUsage));
|
|
781
|
+
const finish = recordSessionUsage(config2, taskIdsPromise, usage, "[claude-worker]", { since: startedAt, ...tracker.opts() });
|
|
694
782
|
const settle = () => {
|
|
695
783
|
if (signal || code !== 0 && code !== null) {
|
|
696
784
|
const detail = (resultError || stderrOutput || "").trim();
|
|
@@ -758,6 +846,9 @@ function readCursorEvent(line) {
|
|
|
758
846
|
} catch {
|
|
759
847
|
return {};
|
|
760
848
|
}
|
|
849
|
+
if (event.type === "system" && event.subtype === "init") {
|
|
850
|
+
return typeof event.model === "string" && event.model ? { model: event.model } : {};
|
|
851
|
+
}
|
|
761
852
|
if (event.type === "tool_call" && event.subtype === "started") {
|
|
762
853
|
const call = event.tool_call ?? {};
|
|
763
854
|
const [kind] = Object.keys(call);
|
|
@@ -775,6 +866,7 @@ function readCursorEvent(line) {
|
|
|
775
866
|
async function runCursorSession(config2) {
|
|
776
867
|
const args = cursorArgs(config2.repoPath, buildPrompt(config2), pinnedModel());
|
|
777
868
|
console.log(`[cursor-worker] Spawning: ${bin()} ${args.slice(0, 5).join(" ")} ...`);
|
|
869
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
778
870
|
const taskIdsPromise = listSessionTaskIds(config2, "[cursor-worker]");
|
|
779
871
|
return new Promise((resolve, reject) => {
|
|
780
872
|
const proc = spawn2(bin(), args, { cwd: config2.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -784,12 +876,14 @@ async function runCursorSession(config2) {
|
|
|
784
876
|
let resultError = "";
|
|
785
877
|
let stderrOutput = "";
|
|
786
878
|
let sessionUsage;
|
|
879
|
+
let model;
|
|
787
880
|
proc.stdout.on("data", (chunk) => {
|
|
788
881
|
buffer += chunk.toString();
|
|
789
882
|
const lines = buffer.split("\n");
|
|
790
883
|
buffer = lines.pop() ?? "";
|
|
791
884
|
for (const line of lines) {
|
|
792
|
-
const { tool, error, usage } = readCursorEvent(line);
|
|
885
|
+
const { tool, error, usage, model: initModel } = readCursorEvent(line);
|
|
886
|
+
if (initModel) model = initModel;
|
|
793
887
|
if (tool) {
|
|
794
888
|
toolsUsed.push(tool);
|
|
795
889
|
process.stdout.write(` \u2699 ${tool}
|
|
@@ -807,9 +901,9 @@ async function runCursorSession(config2) {
|
|
|
807
901
|
});
|
|
808
902
|
proc.on("close", (code, signal) => {
|
|
809
903
|
console.log(`[cursor-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
810
|
-
const usage = sessionUsage;
|
|
811
|
-
if (
|
|
812
|
-
const finish =
|
|
904
|
+
const usage = sessionUsage ?? notReportedUsage("killed");
|
|
905
|
+
if (sessionUsage) console.log(formatSessionUsageLine("[cursor-worker]", sessionUsage));
|
|
906
|
+
const finish = recordSessionUsage(config2, taskIdsPromise, usage, "[cursor-worker]", { since: startedAt, ...model ? { model } : {} });
|
|
813
907
|
const settle = () => {
|
|
814
908
|
if (signal || code !== 0 && code !== null) {
|
|
815
909
|
const detail = (resultError || stderrOutput).trim();
|
|
@@ -880,6 +974,7 @@ function readCopilotEvent(line) {
|
|
|
880
974
|
const type = typeof event.type === "string" ? event.type : "";
|
|
881
975
|
const data = event.data ?? {};
|
|
882
976
|
const message = typeof data.message === "string" ? data.message : "";
|
|
977
|
+
if (type === "session.tools_updated" && typeof data.model === "string" && data.model) return { model: data.model };
|
|
883
978
|
if (/^tool\..*start/.test(type) && typeof data.toolName === "string") return { tool: data.toolName };
|
|
884
979
|
if (type === "session.error") return { error: message || "copilot session error" };
|
|
885
980
|
if (type === "result" && typeof event.exitCode === "number" && event.exitCode !== 0) return { error: `copilot finished with exit code ${event.exitCode}` };
|
|
@@ -889,6 +984,8 @@ function readCopilotEvent(line) {
|
|
|
889
984
|
async function runCopilotSession(config2) {
|
|
890
985
|
const mcpConfigPath = writeMcpConfig(config2, "copilot-worker");
|
|
891
986
|
const args = copilotArgs(config2.repoPath, buildPrompt(config2), mcpConfigPath, pinnedModel());
|
|
987
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
988
|
+
const taskIdsPromise = listSessionTaskIds(config2, "[copilot-worker]");
|
|
892
989
|
console.log(`[copilot-worker] Spawning: ${bin2()} -p <prompt> ${args.slice(2, 6).join(" ")} ...`);
|
|
893
990
|
return new Promise((resolve, reject) => {
|
|
894
991
|
const proc = spawn3(bin2(), args, { cwd: config2.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -897,12 +994,14 @@ async function runCopilotSession(config2) {
|
|
|
897
994
|
let buffer = "";
|
|
898
995
|
let sessionError = "";
|
|
899
996
|
let stderrOutput = "";
|
|
997
|
+
let model;
|
|
900
998
|
proc.stdout.on("data", (chunk) => {
|
|
901
999
|
buffer += chunk.toString();
|
|
902
1000
|
const lines = buffer.split("\n");
|
|
903
1001
|
buffer = lines.pop() ?? "";
|
|
904
1002
|
for (const line of lines) {
|
|
905
|
-
const { tool, error } = readCopilotEvent(line);
|
|
1003
|
+
const { tool, error, model: initModel } = readCopilotEvent(line);
|
|
1004
|
+
if (initModel) model = initModel;
|
|
906
1005
|
if (tool) {
|
|
907
1006
|
toolsUsed.push(tool);
|
|
908
1007
|
process.stdout.write(` \u2699 ${tool}
|
|
@@ -923,12 +1022,15 @@ async function runCopilotSession(config2) {
|
|
|
923
1022
|
} catch {
|
|
924
1023
|
}
|
|
925
1024
|
console.log(`[copilot-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
1025
|
+
const settle = () => {
|
|
1026
|
+
if (sessionError || signal || code !== 0 && code !== null) {
|
|
1027
|
+
const detail = (sessionError || stderrOutput).trim();
|
|
1028
|
+
reject(new Error(`copilot exited with ${signal ?? `code ${code}`}${detail ? `: ${detail}` : ""}`));
|
|
1029
|
+
} else {
|
|
1030
|
+
resolve([...new Set(toolsUsed)]);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
recordSessionUsage(config2, taskIdsPromise, notReportedUsage("adapter"), "[copilot-worker]", { since: startedAt, ...model ? { model } : {} }).then(settle, settle);
|
|
932
1034
|
});
|
|
933
1035
|
proc.on("error", (err) => {
|
|
934
1036
|
try {
|
|
@@ -1022,6 +1124,8 @@ async function runCodexSession(config2) {
|
|
|
1022
1124
|
}
|
|
1023
1125
|
};
|
|
1024
1126
|
const args = codexArgs(config2.repoPath, buildPrompt(config2), profile, pinnedModel());
|
|
1127
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1128
|
+
const taskIdsPromise = listSessionTaskIds(config2, "[codex-worker]");
|
|
1025
1129
|
console.log(`[codex-worker] Spawning: ${bin3()} ${args.slice(0, 6).join(" ")} ...`);
|
|
1026
1130
|
return new Promise((resolve, reject) => {
|
|
1027
1131
|
const proc = spawn4(bin3(), args, { cwd: config2.repoPath, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
@@ -1053,12 +1157,15 @@ async function runCodexSession(config2) {
|
|
|
1053
1157
|
proc.on("close", (code, signal) => {
|
|
1054
1158
|
cleanup();
|
|
1055
1159
|
console.log(`[codex-worker] Done \u2014 tools used: ${toolsUsed.length ? [...new Set(toolsUsed)].join(", ") : "none"}`);
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1160
|
+
const settle = () => {
|
|
1161
|
+
if (sessionError || signal || code !== 0 && code !== null) {
|
|
1162
|
+
const detail = (sessionError || stderrOutput).trim();
|
|
1163
|
+
reject(new Error(`codex exited with ${signal ?? `code ${code}`}${detail ? `: ${detail}` : ""}`));
|
|
1164
|
+
} else {
|
|
1165
|
+
resolve([...new Set(toolsUsed)]);
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
recordSessionUsage(config2, taskIdsPromise, notReportedUsage("adapter"), "[codex-worker]", { since: startedAt }).then(settle, settle);
|
|
1062
1169
|
});
|
|
1063
1170
|
proc.on("error", (err) => {
|
|
1064
1171
|
cleanup();
|