@rallycry/conveyor-agent 9.0.3 → 10.0.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/{chunk-EYIGNRRW.js → chunk-34NA4BCK.js} +175 -154
- package/dist/chunk-34NA4BCK.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +9 -27
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EYIGNRRW.js.map +0 -1
|
@@ -378,6 +378,14 @@ var AgentConnection = class _AgentConnection {
|
|
|
378
378
|
static RECONNECT_MAX_DELAY_MS = 6e4;
|
|
379
379
|
static RECONNECT_STATUS_EVERY_N = 3;
|
|
380
380
|
isReconnecting = false;
|
|
381
|
+
/**
|
|
382
|
+
* Invoked after every successful session reconnect (the `connectAgent` RPC
|
|
383
|
+
* re-established the session room). The runner uses this to force a TUI
|
|
384
|
+
* repaint: the reconnect may have landed on a different/restarted API
|
|
385
|
+
* process whose PTY scrollback ring is empty, and a quiet terminal would
|
|
386
|
+
* otherwise never re-seed it.
|
|
387
|
+
*/
|
|
388
|
+
onReconnected;
|
|
381
389
|
async reconnectToSession() {
|
|
382
390
|
if (this.isReconnecting) return;
|
|
383
391
|
this.isReconnecting = true;
|
|
@@ -409,6 +417,10 @@ var AgentConnection = class _AgentConnection {
|
|
|
409
417
|
reason: "reconnected",
|
|
410
418
|
attempts: attempt
|
|
411
419
|
});
|
|
420
|
+
try {
|
|
421
|
+
this.onReconnected?.();
|
|
422
|
+
} catch {
|
|
423
|
+
}
|
|
412
424
|
return;
|
|
413
425
|
} catch (err) {
|
|
414
426
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -541,24 +553,23 @@ var AgentConnection = class _AgentConnection {
|
|
|
541
553
|
};
|
|
542
554
|
}
|
|
543
555
|
// ── Convenience methods (thin wrappers around call / emit) ─────────
|
|
544
|
-
async emitStatus(status) {
|
|
556
|
+
async emitStatus(status, reason) {
|
|
545
557
|
this.lastEmittedStatus = status;
|
|
546
558
|
await this.flushEvents();
|
|
559
|
+
const payload = {
|
|
560
|
+
sessionId: this.config.sessionId,
|
|
561
|
+
status,
|
|
562
|
+
...reason ? { reason } : {}
|
|
563
|
+
};
|
|
547
564
|
const AWAIT_STATUSES = ["idle", "waiting_for_input", "connected"];
|
|
548
565
|
if (AWAIT_STATUSES.includes(status)) {
|
|
549
566
|
try {
|
|
550
|
-
await this.call("reportAgentStatus",
|
|
551
|
-
sessionId: this.config.sessionId,
|
|
552
|
-
status
|
|
553
|
-
});
|
|
567
|
+
await this.call("reportAgentStatus", payload);
|
|
554
568
|
this.lastReportedStatus = status;
|
|
555
569
|
} catch {
|
|
556
570
|
}
|
|
557
571
|
} else {
|
|
558
|
-
void this.call("reportAgentStatus", {
|
|
559
|
-
sessionId: this.config.sessionId,
|
|
560
|
-
status
|
|
561
|
-
}).then(() => {
|
|
572
|
+
void this.call("reportAgentStatus", payload).then(() => {
|
|
562
573
|
this.lastReportedStatus = status;
|
|
563
574
|
}).catch(() => {
|
|
564
575
|
});
|
|
@@ -668,17 +679,6 @@ var AgentConnection = class _AgentConnection {
|
|
|
668
679
|
this.sendEvent({ type: "agent_typing_stop" });
|
|
669
680
|
}
|
|
670
681
|
// ── RPC convenience wrappers (v6 compat, will migrate to call()) ───
|
|
671
|
-
trackSpending(params) {
|
|
672
|
-
if (!this.socket) return;
|
|
673
|
-
void this.call("trackSpending", {
|
|
674
|
-
sessionId: this.config.sessionId,
|
|
675
|
-
inputTokens: params.modelUsage?.reduce((s, m) => s + (m.inputTokens ?? 0), 0) ?? 0,
|
|
676
|
-
outputTokens: params.modelUsage?.reduce((s, m) => s + (m.outputTokens ?? 0), 0) ?? 0,
|
|
677
|
-
costUsd: params.totalCostUsd,
|
|
678
|
-
model: params.modelUsage?.[0]?.model ?? "unknown"
|
|
679
|
-
}).catch(() => {
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
682
|
emitRateLimitPause(resetsAt) {
|
|
683
683
|
this.sendEvent({ type: "rate_limit_update", resetsAt });
|
|
684
684
|
}
|
|
@@ -723,9 +723,6 @@ ${q.question}${q.options.length ? "\n" + q.options.map((o) => `- ${o.label}: ${o
|
|
|
723
723
|
triggerIdentification() {
|
|
724
724
|
return this.call("triggerIdentification", { sessionId: this.config.sessionId });
|
|
725
725
|
}
|
|
726
|
-
getCumulativeSpending() {
|
|
727
|
-
return this.call("getCumulativeSpending", { sessionId: this.config.sessionId });
|
|
728
|
-
}
|
|
729
726
|
async refreshAuthToken() {
|
|
730
727
|
const result = await this.refreshFromBootstrap();
|
|
731
728
|
return result.refreshedClaude;
|
|
@@ -1287,6 +1284,7 @@ const net = require("node:net");
|
|
|
1287
1284
|
const crypto = require("node:crypto");
|
|
1288
1285
|
|
|
1289
1286
|
const PRE_TOOL_USE_TIMEOUT_MS = 110000;
|
|
1287
|
+
const ASK_USER_QUESTION_TIMEOUT_MS = 5000;
|
|
1290
1288
|
|
|
1291
1289
|
let raw = "";
|
|
1292
1290
|
process.stdin.setEncoding("utf8");
|
|
@@ -1358,17 +1356,26 @@ function preToolUse(payload) {
|
|
|
1358
1356
|
);
|
|
1359
1357
|
process.exit(0);
|
|
1360
1358
|
};
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1359
|
+
// AskUserQuestion is observe-only (status reporting) \u2014 fail OPEN so a
|
|
1360
|
+
// missing/slow socket never denies the questionnaire. Everything else
|
|
1361
|
+
// (ExitPlanMode) is a Conveyor gate \u2014 fail CLOSED.
|
|
1362
|
+
const failOpen = payload.tool_name === "AskUserQuestion";
|
|
1363
|
+
const respondUnavailable = () =>
|
|
1364
|
+
failOpen
|
|
1365
|
+
? respond("allow")
|
|
1366
|
+
: respond(
|
|
1367
|
+
"deny",
|
|
1368
|
+
"Conveyor validation is unavailable right now. Wait a moment and call the tool again.",
|
|
1369
|
+
);
|
|
1366
1370
|
if (!socketPath) {
|
|
1367
|
-
|
|
1371
|
+
respondUnavailable();
|
|
1368
1372
|
return;
|
|
1369
1373
|
}
|
|
1370
1374
|
const id = crypto.randomBytes(8).toString("hex");
|
|
1371
|
-
const timer = setTimeout(
|
|
1375
|
+
const timer = setTimeout(
|
|
1376
|
+
respondUnavailable,
|
|
1377
|
+
failOpen ? ASK_USER_QUESTION_TIMEOUT_MS : PRE_TOOL_USE_TIMEOUT_MS,
|
|
1378
|
+
);
|
|
1372
1379
|
const client = net.connect(socketPath, () => {
|
|
1373
1380
|
client.write(
|
|
1374
1381
|
JSON.stringify({
|
|
@@ -1403,11 +1410,11 @@ function preToolUse(payload) {
|
|
|
1403
1410
|
});
|
|
1404
1411
|
client.on("error", () => {
|
|
1405
1412
|
clearTimeout(timer);
|
|
1406
|
-
|
|
1413
|
+
respondUnavailable();
|
|
1407
1414
|
});
|
|
1408
1415
|
client.on("close", () => {
|
|
1409
1416
|
clearTimeout(timer);
|
|
1410
|
-
|
|
1417
|
+
respondUnavailable();
|
|
1411
1418
|
});
|
|
1412
1419
|
}
|
|
1413
1420
|
`;
|
|
@@ -1445,6 +1452,13 @@ async function writeHookSettings(dir) {
|
|
|
1445
1452
|
{
|
|
1446
1453
|
matcher: "ExitPlanMode",
|
|
1447
1454
|
hooks: [{ type: "command", command: `node ${JSON.stringify(helperPath)}`, timeout: 120 }]
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
// Observe-only: lets the session report waiting_for_input the moment
|
|
1458
|
+
// the planning questionnaire renders. The helper fails open for this
|
|
1459
|
+
// tool, so the questionnaire is never blocked by Conveyor.
|
|
1460
|
+
matcher: "AskUserQuestion",
|
|
1461
|
+
hooks: [{ type: "command", command: `node ${JSON.stringify(helperPath)}`, timeout: 30 }]
|
|
1448
1462
|
}
|
|
1449
1463
|
],
|
|
1450
1464
|
PostToolUse: [
|
|
@@ -1682,7 +1696,9 @@ var ConnectAgentRequestSchema = z.object({
|
|
|
1682
1696
|
});
|
|
1683
1697
|
var ReportAgentStatusRequestSchema = z.object({
|
|
1684
1698
|
sessionId: z.string(),
|
|
1685
|
-
status: z.string()
|
|
1699
|
+
status: z.string(),
|
|
1700
|
+
/** Why the agent reports this status (e.g. "user_question" while an AskUserQuestion questionnaire is pending in the TUI). */
|
|
1701
|
+
reason: z.string().optional()
|
|
1686
1702
|
});
|
|
1687
1703
|
var NotifyAgentVersionRequestSchema = z.object({
|
|
1688
1704
|
sessionId: z.string(),
|
|
@@ -1997,6 +2013,20 @@ var GetProjectTaskSessionsRequestSchema = z2.object({
|
|
|
1997
2013
|
projectId: z2.string(),
|
|
1998
2014
|
taskId: z2.string()
|
|
1999
2015
|
});
|
|
2016
|
+
var QueryProjectGcpLogsRequestSchema = z2.object({
|
|
2017
|
+
projectId: z2.string(),
|
|
2018
|
+
env: z2.enum(["prod", "dev", "claudespace"]).optional(),
|
|
2019
|
+
severity: z2.enum(["DEBUG", "INFO", "NOTICE", "WARNING", "ERROR", "CRITICAL", "ALERT", "EMERGENCY"]).optional(),
|
|
2020
|
+
services: z2.array(z2.string().min(1).max(200)).max(25).optional(),
|
|
2021
|
+
sqlInstances: z2.array(z2.string().min(1).max(200)).max(25).optional(),
|
|
2022
|
+
allServices: z2.boolean().optional(),
|
|
2023
|
+
search: z2.string().max(256).optional(),
|
|
2024
|
+
filter: z2.string().max(1e3).optional(),
|
|
2025
|
+
startTime: z2.string().optional(),
|
|
2026
|
+
endTime: z2.string().optional(),
|
|
2027
|
+
limit: z2.number().int().min(1).max(200).optional().default(50),
|
|
2028
|
+
pageToken: z2.string().max(4096).optional()
|
|
2029
|
+
});
|
|
2000
2030
|
var StartProjectBuildRequestSchema = z2.object({
|
|
2001
2031
|
projectId: z2.string(),
|
|
2002
2032
|
taskId: z2.string(),
|
|
@@ -2175,6 +2205,7 @@ var CreateProjectSuggestionRequestSchema = z2.object({
|
|
|
2175
2205
|
tagNames: z2.array(z2.string()).optional(),
|
|
2176
2206
|
requestingUserId: z2.string().optional()
|
|
2177
2207
|
});
|
|
2208
|
+
var AGENT_STATUS_REASON_USER_QUESTION = "user_question";
|
|
2178
2209
|
var TASK_CHAT_HISTORY_LIMIT = 20;
|
|
2179
2210
|
var PM_CHAT_HISTORY_LIMIT = 40;
|
|
2180
2211
|
var AGENT_CHAT_HISTORY_FETCH_LIMIT = Math.max(TASK_CHAT_HISTORY_LIMIT, PM_CHAT_HISTORY_LIMIT) + 10;
|
|
@@ -3107,6 +3138,25 @@ async function transcriptSize(path) {
|
|
|
3107
3138
|
return 0;
|
|
3108
3139
|
}
|
|
3109
3140
|
}
|
|
3141
|
+
function parseUserQuestions(input) {
|
|
3142
|
+
if (!Array.isArray(input.questions)) return [];
|
|
3143
|
+
const questions = [];
|
|
3144
|
+
for (const entry of input.questions) {
|
|
3145
|
+
if (!isRecord2(entry)) continue;
|
|
3146
|
+
if (typeof entry.question !== "string") continue;
|
|
3147
|
+
const options = Array.isArray(entry.options) ? entry.options.filter(isRecord2).filter((o) => typeof o.label === "string").map((o) => ({
|
|
3148
|
+
label: o.label,
|
|
3149
|
+
description: typeof o.description === "string" ? o.description : ""
|
|
3150
|
+
})) : [];
|
|
3151
|
+
questions.push({
|
|
3152
|
+
question: entry.question,
|
|
3153
|
+
header: typeof entry.header === "string" ? entry.header : "",
|
|
3154
|
+
options,
|
|
3155
|
+
...typeof entry.multiSelect === "boolean" ? { multiSelect: entry.multiSelect } : {}
|
|
3156
|
+
});
|
|
3157
|
+
}
|
|
3158
|
+
return questions;
|
|
3159
|
+
}
|
|
3110
3160
|
var PtySession = class {
|
|
3111
3161
|
constructor(prompt, options, resume, bridge) {
|
|
3112
3162
|
this.prompt = prompt;
|
|
@@ -3222,6 +3272,21 @@ var PtySession = class {
|
|
|
3222
3272
|
} catch {
|
|
3223
3273
|
}
|
|
3224
3274
|
}
|
|
3275
|
+
/**
|
|
3276
|
+
* Force the CLI to repaint its full screen by wiggling the pty size
|
|
3277
|
+
* (SIGWINCH). Used after a socket reconnect: the server's scrollback ring is
|
|
3278
|
+
* per-process and starts empty on a new instance, so without a repaint a
|
|
3279
|
+
* quiet TUI would leave the Connected-TUI terminal blank (and hidden) until
|
|
3280
|
+
* the CLI next draws on its own.
|
|
3281
|
+
*/
|
|
3282
|
+
forceRepaint() {
|
|
3283
|
+
if (!this.pty) return;
|
|
3284
|
+
try {
|
|
3285
|
+
this.pty.resize(this.cols, Math.max(1, this.rows - 1));
|
|
3286
|
+
this.pty.resize(this.cols, this.rows);
|
|
3287
|
+
} catch {
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3225
3290
|
async teardown() {
|
|
3226
3291
|
if (this._toreDown) return;
|
|
3227
3292
|
this._toreDown = true;
|
|
@@ -3384,11 +3449,17 @@ var PtySession = class {
|
|
|
3384
3449
|
}
|
|
3385
3450
|
/**
|
|
3386
3451
|
* PreToolUse request from the hook helper → the runner's `canUseTool`
|
|
3387
|
-
* verdict.
|
|
3388
|
-
*
|
|
3389
|
-
*
|
|
3452
|
+
* verdict. ExitPlanMode is the Conveyor plan-exit gate (validation +
|
|
3453
|
+
* identification trigger run inside the callback); AskUserQuestion is
|
|
3454
|
+
* observed and always allowed. Absent callback → allow (mirrors the SDK
|
|
3455
|
+
* default).
|
|
3390
3456
|
*/
|
|
3391
3457
|
async handlePreToolUse(request) {
|
|
3458
|
+
if (request.tool_name === "AskUserQuestion") {
|
|
3459
|
+
this.disarmSubmitNudge();
|
|
3460
|
+
this.queue.push({ type: "user_question", questions: parseUserQuestions(request.tool_input) });
|
|
3461
|
+
return { decision: "allow" };
|
|
3462
|
+
}
|
|
3392
3463
|
const canUseTool = this.options.canUseTool;
|
|
3393
3464
|
let verdict;
|
|
3394
3465
|
if (canUseTool) {
|
|
@@ -3635,6 +3706,17 @@ var PtyHarness = class {
|
|
|
3635
3706
|
this.bridge = bridge;
|
|
3636
3707
|
}
|
|
3637
3708
|
bridge;
|
|
3709
|
+
/** The session currently streaming events, if any — repaint target. */
|
|
3710
|
+
activeSession = null;
|
|
3711
|
+
/**
|
|
3712
|
+
* Wiggle the live pty's size so the CLI repaints its whole screen. No-op
|
|
3713
|
+
* between queries or on the SDK harness. Called after a socket reconnect so
|
|
3714
|
+
* the server's (per-process, possibly brand-new) scrollback ring re-seeds
|
|
3715
|
+
* and the Connected-TUI terminal reappears.
|
|
3716
|
+
*/
|
|
3717
|
+
forceRepaint() {
|
|
3718
|
+
this.activeSession?.forceRepaint();
|
|
3719
|
+
}
|
|
3638
3720
|
async *executeQuery(opts) {
|
|
3639
3721
|
const session = new PtySession(
|
|
3640
3722
|
opts.prompt,
|
|
@@ -3645,11 +3727,13 @@ var PtyHarness = class {
|
|
|
3645
3727
|
await ensureClaudeCredentials();
|
|
3646
3728
|
await ensureClaudeOnboarding(process.env, opts.options.cwd);
|
|
3647
3729
|
await session.start();
|
|
3730
|
+
this.activeSession = session;
|
|
3648
3731
|
try {
|
|
3649
3732
|
for await (const event of session.events()) {
|
|
3650
3733
|
yield event;
|
|
3651
3734
|
}
|
|
3652
3735
|
} finally {
|
|
3736
|
+
if (this.activeSession === session) this.activeSession = null;
|
|
3653
3737
|
await session.teardown();
|
|
3654
3738
|
}
|
|
3655
3739
|
}
|
|
@@ -5004,7 +5088,7 @@ var cliEventFormatters = {
|
|
|
5004
5088
|
tool_result: (e) => `${e.tool} \u2192 ${e.output?.slice(0, 500) ?? ""}${e.isError ? " [ERROR]" : ""}`,
|
|
5005
5089
|
message: (e) => e.content ?? "",
|
|
5006
5090
|
error: (e) => `ERROR: ${e.message ?? ""}`,
|
|
5007
|
-
completed: (e) => `Completed: ${e.summary ?? ""} (
|
|
5091
|
+
completed: (e) => `Completed: ${e.summary ?? ""} (duration: ${e.durationMs ?? "?"}ms)`,
|
|
5008
5092
|
setup_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
|
|
5009
5093
|
start_command_output: (e) => `[${e.stream ?? "stdout"}] ${e.data ?? ""}`,
|
|
5010
5094
|
turn_end: (e) => `Turn complete (${e.toolCalls?.length ?? 0} tool calls)`
|
|
@@ -6268,35 +6352,16 @@ function emitContextUpdate(modelUsage, host, context, lastAssistantUsage) {
|
|
|
6268
6352
|
});
|
|
6269
6353
|
}
|
|
6270
6354
|
}
|
|
6271
|
-
function trackCostSpending(host, context, cumulativeTotal) {
|
|
6272
|
-
if (cumulativeTotal > 0 && context.agentId && context._runnerSessionId) {
|
|
6273
|
-
const breakdown = host.costTracker.modelBreakdown;
|
|
6274
|
-
host.connection.trackSpending({
|
|
6275
|
-
agentId: context.agentId,
|
|
6276
|
-
sessionId: context._runnerSessionId,
|
|
6277
|
-
totalCostUsd: cumulativeTotal,
|
|
6278
|
-
onSubscription: host.config.mode === "pm" || !!process.env.CLAUDE_CODE_OAUTH_TOKEN,
|
|
6279
|
-
modelUsage: breakdown.length > 0 ? breakdown : void 0
|
|
6280
|
-
});
|
|
6281
|
-
}
|
|
6282
|
-
}
|
|
6283
6355
|
function handleSuccessResult(event, host, context, startTime, lastAssistantUsage) {
|
|
6284
6356
|
const durationMs = Date.now() - startTime;
|
|
6285
6357
|
const summary = event.result || "Task completed.";
|
|
6286
6358
|
const retriable = isRetriableMessage(summary);
|
|
6287
|
-
|
|
6359
|
+
host.connection.sendEvent({ type: "completed", summary, durationMs });
|
|
6288
6360
|
const { modelUsage } = event;
|
|
6289
|
-
if (modelUsage && typeof modelUsage === "object") {
|
|
6290
|
-
host.costTracker.addModelUsage(
|
|
6291
|
-
modelUsage
|
|
6292
|
-
);
|
|
6293
|
-
}
|
|
6294
|
-
host.connection.sendEvent({ type: "completed", summary, costUsd: cumulativeTotal, durationMs });
|
|
6295
6361
|
if (modelUsage && typeof modelUsage === "object") {
|
|
6296
6362
|
emitContextUpdate(modelUsage, host, context, lastAssistantUsage);
|
|
6297
6363
|
}
|
|
6298
|
-
|
|
6299
|
-
return { totalCostUsd: cumulativeTotal, retriable };
|
|
6364
|
+
return { retriable };
|
|
6300
6365
|
}
|
|
6301
6366
|
function handleErrorResult(event, host) {
|
|
6302
6367
|
const errorMsg = event.errors.length > 0 ? event.errors.join(", ") : `Agent stopped: ${event.subtype}`;
|
|
@@ -6325,7 +6390,7 @@ function handleResultEvent(event, host, context, startTime, lastAssistantUsage)
|
|
|
6325
6390
|
return { ...result2, resultSummary };
|
|
6326
6391
|
}
|
|
6327
6392
|
const result = handleErrorResult(event, host);
|
|
6328
|
-
return {
|
|
6393
|
+
return { ...result, resultSummary };
|
|
6329
6394
|
}
|
|
6330
6395
|
async function emitResultEvent(event, host, context, startTime, lastAssistantUsage) {
|
|
6331
6396
|
const result = handleResultEvent(event, host, context, startTime, lastAssistantUsage);
|
|
@@ -6336,7 +6401,6 @@ async function emitResultEvent(event, host, context, startTime, lastAssistantUsa
|
|
|
6336
6401
|
await host.callbacks.onEvent({
|
|
6337
6402
|
type: "completed",
|
|
6338
6403
|
summary,
|
|
6339
|
-
costUsd: result.totalCostUsd,
|
|
6340
6404
|
durationMs
|
|
6341
6405
|
});
|
|
6342
6406
|
} else if (!result.staleSession) {
|
|
@@ -6456,6 +6520,44 @@ async function handleResultCase(event, host, context, startTime, isTyping, lastA
|
|
|
6456
6520
|
}
|
|
6457
6521
|
|
|
6458
6522
|
// src/execution/event-processor.ts
|
|
6523
|
+
function hasAskUserQuestionBlock(event) {
|
|
6524
|
+
return event.message.content.some((b) => b.type === "tool_use" && b.name === "AskUserQuestion");
|
|
6525
|
+
}
|
|
6526
|
+
async function armQuestionPending(host, state) {
|
|
6527
|
+
state.questionPending = true;
|
|
6528
|
+
await host.connection.emitStatus("waiting_for_input", AGENT_STATUS_REASON_USER_QUESTION);
|
|
6529
|
+
await host.callbacks.onStatusChange("waiting_for_input");
|
|
6530
|
+
}
|
|
6531
|
+
async function clearQuestionPending(host, state, options) {
|
|
6532
|
+
if (!state.questionPending) return;
|
|
6533
|
+
state.questionPending = false;
|
|
6534
|
+
if (options.emitRunning) {
|
|
6535
|
+
await host.connection.emitStatus("running");
|
|
6536
|
+
await host.callbacks.onStatusChange("running");
|
|
6537
|
+
}
|
|
6538
|
+
}
|
|
6539
|
+
async function applyQuestionTransitions(event, host, state) {
|
|
6540
|
+
switch (event.type) {
|
|
6541
|
+
case "user_question":
|
|
6542
|
+
await armQuestionPending(host, state);
|
|
6543
|
+
return;
|
|
6544
|
+
case "assistant":
|
|
6545
|
+
if (!hasAskUserQuestionBlock(event)) {
|
|
6546
|
+
await clearQuestionPending(host, state, { emitRunning: true });
|
|
6547
|
+
}
|
|
6548
|
+
return;
|
|
6549
|
+
case "tool_progress":
|
|
6550
|
+
if (event.tool_name === "AskUserQuestion") {
|
|
6551
|
+
await clearQuestionPending(host, state, { emitRunning: true });
|
|
6552
|
+
}
|
|
6553
|
+
return;
|
|
6554
|
+
case "result":
|
|
6555
|
+
await clearQuestionPending(host, state, { emitRunning: false });
|
|
6556
|
+
return;
|
|
6557
|
+
default:
|
|
6558
|
+
break;
|
|
6559
|
+
}
|
|
6560
|
+
}
|
|
6459
6561
|
function stopTypingIfNeeded(host, isTyping) {
|
|
6460
6562
|
if (isTyping) host.connection.sendTypingStop();
|
|
6461
6563
|
}
|
|
@@ -6535,13 +6637,14 @@ async function processEvents(events, context, host) {
|
|
|
6535
6637
|
staleSession: void 0,
|
|
6536
6638
|
authError: void 0,
|
|
6537
6639
|
lastAssistantUsage: void 0,
|
|
6538
|
-
turnToolCalls: []
|
|
6640
|
+
turnToolCalls: [],
|
|
6641
|
+
questionPending: false
|
|
6539
6642
|
};
|
|
6540
6643
|
for await (const event of events) {
|
|
6541
6644
|
if (host.isStopped()) break;
|
|
6542
6645
|
flushPendingToolCalls(host, state.turnToolCalls);
|
|
6543
6646
|
const now = Date.now();
|
|
6544
|
-
if (now - lastStatusEmit >= STATUS_REEMIT_INTERVAL_MS) {
|
|
6647
|
+
if (now - lastStatusEmit >= STATUS_REEMIT_INTERVAL_MS && !state.questionPending) {
|
|
6545
6648
|
host.connection.emitStatus("running");
|
|
6546
6649
|
lastStatusEmit = now;
|
|
6547
6650
|
}
|
|
@@ -6549,6 +6652,7 @@ async function processEvents(events, context, host) {
|
|
|
6549
6652
|
stopTypingIfNeeded(host, state.isTyping);
|
|
6550
6653
|
return { retriable: false, modeRestart: true };
|
|
6551
6654
|
}
|
|
6655
|
+
await applyQuestionTransitions(event, host, state);
|
|
6552
6656
|
switch (event.type) {
|
|
6553
6657
|
case "system":
|
|
6554
6658
|
await processSystemCase(event, host, context, state);
|
|
@@ -7051,7 +7155,6 @@ function buildQueryOptions(host, context) {
|
|
|
7051
7155
|
effort: settings.effort,
|
|
7052
7156
|
thinking: settings.thinking,
|
|
7053
7157
|
betas: settings.betas,
|
|
7054
|
-
maxBudgetUsd: settings.maxBudgetUsd ?? 50,
|
|
7055
7158
|
abortController: host.abortController ?? void 0,
|
|
7056
7159
|
disallowedTools: buildDisallowedTools(settings, mode, host.hasExitedPlanMode),
|
|
7057
7160
|
enableFileCheckpointing: settings.enableFileCheckpointing,
|
|
@@ -7450,58 +7553,6 @@ async function runWithRetry(initialQuery, context, host, options) {
|
|
|
7450
7553
|
}
|
|
7451
7554
|
}
|
|
7452
7555
|
|
|
7453
|
-
// src/execution/cost-tracker.ts
|
|
7454
|
-
var CostTracker = class {
|
|
7455
|
-
cumulativeCostUsd = 0;
|
|
7456
|
-
modelUsage = /* @__PURE__ */ new Map();
|
|
7457
|
-
seeded = false;
|
|
7458
|
-
/**
|
|
7459
|
-
* Rehydrate cumulative spend from the server so `maxBudgetUsd` enforcement
|
|
7460
|
-
* accounts for costs incurred by prior agent runs on the same task. Must be
|
|
7461
|
-
* called before any `addQueryCost` / `addModelUsage` — re-seeding after
|
|
7462
|
-
* costs have accumulated would clobber in-process totals.
|
|
7463
|
-
*/
|
|
7464
|
-
seed(totalCostUsd, modelUsage) {
|
|
7465
|
-
if (this.seeded) return;
|
|
7466
|
-
if (this.cumulativeCostUsd > 0 || this.modelUsage.size > 0) return;
|
|
7467
|
-
this.cumulativeCostUsd = totalCostUsd;
|
|
7468
|
-
for (const entry of modelUsage) {
|
|
7469
|
-
this.modelUsage.set(entry.model, { ...entry });
|
|
7470
|
-
}
|
|
7471
|
-
this.seeded = true;
|
|
7472
|
-
}
|
|
7473
|
-
/** Add cost from a completed query and return the running total */
|
|
7474
|
-
addQueryCost(queryCostUsd) {
|
|
7475
|
-
this.cumulativeCostUsd += queryCostUsd;
|
|
7476
|
-
return this.cumulativeCostUsd;
|
|
7477
|
-
}
|
|
7478
|
-
/** Merge per-model usage from a completed query */
|
|
7479
|
-
addModelUsage(usage) {
|
|
7480
|
-
for (const [model, data] of Object.entries(usage)) {
|
|
7481
|
-
const existing = this.modelUsage.get(model) ?? {
|
|
7482
|
-
model,
|
|
7483
|
-
inputTokens: 0,
|
|
7484
|
-
outputTokens: 0,
|
|
7485
|
-
cacheReadInputTokens: 0,
|
|
7486
|
-
cacheCreationInputTokens: 0,
|
|
7487
|
-
costUSD: 0
|
|
7488
|
-
};
|
|
7489
|
-
existing.inputTokens += data.inputTokens ?? 0;
|
|
7490
|
-
existing.outputTokens += data.outputTokens ?? 0;
|
|
7491
|
-
existing.cacheReadInputTokens += data.cacheReadInputTokens ?? 0;
|
|
7492
|
-
existing.cacheCreationInputTokens += data.cacheCreationInputTokens ?? 0;
|
|
7493
|
-
existing.costUSD += data.costUSD ?? 0;
|
|
7494
|
-
this.modelUsage.set(model, existing);
|
|
7495
|
-
}
|
|
7496
|
-
}
|
|
7497
|
-
get totalCostUsd() {
|
|
7498
|
-
return this.cumulativeCostUsd;
|
|
7499
|
-
}
|
|
7500
|
-
get modelBreakdown() {
|
|
7501
|
-
return [...this.modelUsage.values()];
|
|
7502
|
-
}
|
|
7503
|
-
};
|
|
7504
|
-
|
|
7505
7556
|
// src/execution/exploration-tracker.ts
|
|
7506
7557
|
var FILE_REREAD_NUDGE_L1 = 3;
|
|
7507
7558
|
var FILE_REREAD_NUDGE_L2 = 5;
|
|
@@ -7628,7 +7679,6 @@ var QueryBridge = class {
|
|
|
7628
7679
|
harnessKind,
|
|
7629
7680
|
harnessKind === "pty" ? buildPtyBridge(connection) : void 0
|
|
7630
7681
|
);
|
|
7631
|
-
this.costTracker = new CostTracker();
|
|
7632
7682
|
this.planSync = new PlanSync(workspaceDir, connection);
|
|
7633
7683
|
}
|
|
7634
7684
|
connection;
|
|
@@ -7639,7 +7689,6 @@ var QueryBridge = class {
|
|
|
7639
7689
|
/** Which harness drives this bridge ("pty" task chat; "sdk" only under the
|
|
7640
7690
|
* CONVEYOR_FORCE_SDK_CARDS maintainer override). */
|
|
7641
7691
|
harnessKind;
|
|
7642
|
-
costTracker;
|
|
7643
7692
|
planSync;
|
|
7644
7693
|
sessionIds = /* @__PURE__ */ new Map();
|
|
7645
7694
|
activeQuery = null;
|
|
@@ -7671,6 +7720,13 @@ var QueryBridge = class {
|
|
|
7671
7720
|
get wasRateLimited() {
|
|
7672
7721
|
return this._wasRateLimited;
|
|
7673
7722
|
}
|
|
7723
|
+
/**
|
|
7724
|
+
* Ask the live terminal (PTY harness only) to redraw its full screen.
|
|
7725
|
+
* Safe no-op on the SDK harness or between queries.
|
|
7726
|
+
*/
|
|
7727
|
+
forceRepaint() {
|
|
7728
|
+
this.harness.forceRepaint?.();
|
|
7729
|
+
}
|
|
7674
7730
|
stop() {
|
|
7675
7731
|
this._stopped = true;
|
|
7676
7732
|
this._abortController?.abort();
|
|
@@ -7678,10 +7734,6 @@ var QueryBridge = class {
|
|
|
7678
7734
|
resume() {
|
|
7679
7735
|
this._stopped = false;
|
|
7680
7736
|
}
|
|
7681
|
-
/** Rehydrate CostTracker from server-side cumulative spend on agent boot. */
|
|
7682
|
-
seedCostTracker(totalCostUsd, modelUsage) {
|
|
7683
|
-
this.costTracker.seed(totalCostUsd, modelUsage);
|
|
7684
|
-
}
|
|
7685
7737
|
/**
|
|
7686
7738
|
* How the INITIAL instructions for this task would be delivered — "prefill"
|
|
7687
7739
|
* means the TUI input is pre-filled but unsubmitted, awaiting the human.
|
|
@@ -7740,7 +7792,6 @@ var QueryBridge = class {
|
|
|
7740
7792
|
harness: this.harness,
|
|
7741
7793
|
harnessKind: this.harnessKind,
|
|
7742
7794
|
setupLog: [],
|
|
7743
|
-
costTracker: this.costTracker,
|
|
7744
7795
|
explorationTracker: bridge.mode.effectiveMode === "discovery" || bridge.mode.effectiveMode === "auto" && !bridge.mode.hasExitedPlanMode ? new ExplorationTracker(bridge._isParentTask) : null,
|
|
7745
7796
|
sessionIds: this.sessionIds,
|
|
7746
7797
|
pendingToolOutputs: this.pendingToolOutputs,
|
|
@@ -8050,7 +8101,6 @@ var SessionRunner = class _SessionRunner {
|
|
|
8050
8101
|
});
|
|
8051
8102
|
}
|
|
8052
8103
|
this.queryBridge = this.createQueryBridge();
|
|
8053
|
-
await this.seedCostTrackerFromServer();
|
|
8054
8104
|
this.logInitialization();
|
|
8055
8105
|
const staleBatch = [...this.pendingMessages];
|
|
8056
8106
|
const didExecuteInitialQuery = await this.executeInitialMode();
|
|
@@ -8602,6 +8652,7 @@ var SessionRunner = class _SessionRunner {
|
|
|
8602
8652
|
this.connection.onMessage((msg) => this.injectMessage(msg));
|
|
8603
8653
|
this.connection.onStop(() => this.stop());
|
|
8604
8654
|
this.connection.onSoftStop(() => this.softStop());
|
|
8655
|
+
this.connection.onReconnected = () => this.queryBridge?.forceRepaint();
|
|
8605
8656
|
this.connection.onModeChange((data) => {
|
|
8606
8657
|
const action = this.mode.handleModeChange(data.agentMode);
|
|
8607
8658
|
if (action.type === "start_auto") {
|
|
@@ -8634,36 +8685,6 @@ var SessionRunner = class _SessionRunner {
|
|
|
8634
8685
|
handlePullBranch(this.config.workspaceDir, branch);
|
|
8635
8686
|
});
|
|
8636
8687
|
}
|
|
8637
|
-
/**
|
|
8638
|
-
* Rehydrate CostTracker from server. Caps at 3s so a slow API call never
|
|
8639
|
-
* blocks agent startup — on timeout/error we fall through with a $0 tracker
|
|
8640
|
-
* (the existing behavior before this rehydration path was added).
|
|
8641
|
-
*/
|
|
8642
|
-
async seedCostTrackerFromServer() {
|
|
8643
|
-
if (!this.queryBridge) return;
|
|
8644
|
-
const TIMEOUT_MS = 3e3;
|
|
8645
|
-
try {
|
|
8646
|
-
const timeout = new Promise((resolve) => {
|
|
8647
|
-
setTimeout(() => resolve(null), TIMEOUT_MS);
|
|
8648
|
-
});
|
|
8649
|
-
const fetched = await Promise.race([this.connection.getCumulativeSpending(), timeout]);
|
|
8650
|
-
if (fetched) {
|
|
8651
|
-
this.queryBridge.seedCostTracker(fetched.totalCostUsd, fetched.modelUsage);
|
|
8652
|
-
process.stderr.write(
|
|
8653
|
-
`[conveyor-agent] CostTracker seeded: $${fetched.totalCostUsd.toFixed(4)} across ${fetched.modelUsage.length} model(s)
|
|
8654
|
-
`
|
|
8655
|
-
);
|
|
8656
|
-
} else {
|
|
8657
|
-
process.stderr.write(
|
|
8658
|
-
"[conveyor-agent] CostTracker seed timed out after 3s \u2014 starting at $0\n"
|
|
8659
|
-
);
|
|
8660
|
-
}
|
|
8661
|
-
} catch (err) {
|
|
8662
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
8663
|
-
process.stderr.write(`[conveyor-agent] CostTracker seed failed: ${msg} \u2014 starting at $0
|
|
8664
|
-
`);
|
|
8665
|
-
}
|
|
8666
|
-
}
|
|
8667
8688
|
/** Proactively refresh the GitHub token before the 1-hour expiry. */
|
|
8668
8689
|
async refreshGithubToken() {
|
|
8669
8690
|
try {
|
|
@@ -8893,4 +8914,4 @@ export {
|
|
|
8893
8914
|
runStartCommand,
|
|
8894
8915
|
unshallowRepo
|
|
8895
8916
|
};
|
|
8896
|
-
//# sourceMappingURL=chunk-
|
|
8917
|
+
//# sourceMappingURL=chunk-34NA4BCK.js.map
|