cruo-agent 0.1.15 → 0.1.16
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/VERSION +1 -1
- package/dist/cli.js +266 -78
- package/dist/index.js +38253 -2877
- package/package.json +1 -1
package/dist/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.1.
|
|
1
|
+
0.1.16
|
package/dist/cli.js
CHANGED
|
@@ -15233,6 +15233,15 @@ var init_identity = __esm({
|
|
|
15233
15233
|
}
|
|
15234
15234
|
});
|
|
15235
15235
|
|
|
15236
|
+
// ../../packages/core/dist/attachments.js
|
|
15237
|
+
var MAX_ATTACHMENT_BYTES;
|
|
15238
|
+
var init_attachments = __esm({
|
|
15239
|
+
"../../packages/core/dist/attachments.js"() {
|
|
15240
|
+
"use strict";
|
|
15241
|
+
MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
15242
|
+
}
|
|
15243
|
+
});
|
|
15244
|
+
|
|
15236
15245
|
// ../../packages/core/dist/effort.js
|
|
15237
15246
|
var init_effort = __esm({
|
|
15238
15247
|
"../../packages/core/dist/effort.js"() {
|
|
@@ -15298,6 +15307,7 @@ var init_dist = __esm({
|
|
|
15298
15307
|
init_schemas3();
|
|
15299
15308
|
init_rank();
|
|
15300
15309
|
init_identity();
|
|
15310
|
+
init_attachments();
|
|
15301
15311
|
init_effort();
|
|
15302
15312
|
init_plans();
|
|
15303
15313
|
init_reports();
|
|
@@ -36793,16 +36803,41 @@ var init_dist6 = __esm({
|
|
|
36793
36803
|
});
|
|
36794
36804
|
|
|
36795
36805
|
// ../../packages/mcp-kit/dist/context.js
|
|
36806
|
+
function decodeJwtExpiry(token) {
|
|
36807
|
+
const parts = token.split(".");
|
|
36808
|
+
if (parts.length !== 3)
|
|
36809
|
+
return null;
|
|
36810
|
+
try {
|
|
36811
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
36812
|
+
return typeof payload.exp === "number" ? payload.exp * 1e3 : null;
|
|
36813
|
+
} catch {
|
|
36814
|
+
return null;
|
|
36815
|
+
}
|
|
36816
|
+
}
|
|
36817
|
+
function isExpiredSessionError(error51) {
|
|
36818
|
+
if (!error51)
|
|
36819
|
+
return false;
|
|
36820
|
+
if (error51.code === "PGRST301" || error51.code === "PGRST303")
|
|
36821
|
+
return true;
|
|
36822
|
+
const message = (error51.message ?? "").toLowerCase();
|
|
36823
|
+
return message.includes("jwt expired") || message.includes("jwt is expired") || message.includes("invalid jwt") || message.includes("jws error") || message.includes("token is expired");
|
|
36824
|
+
}
|
|
36825
|
+
async function resolveContext(options2) {
|
|
36826
|
+
return resolve(options2);
|
|
36827
|
+
}
|
|
36828
|
+
function refreshBy(ctx) {
|
|
36829
|
+
return ctx.expiresAt != null ? ctx.expiresAt - 6e4 : Date.now() + ASSUMED_SESSION_LIFETIME_MS;
|
|
36830
|
+
}
|
|
36796
36831
|
function createContextResolver(options2) {
|
|
36797
36832
|
let cached2 = null;
|
|
36798
36833
|
let inflight = null;
|
|
36799
36834
|
return async function getContext2() {
|
|
36800
|
-
if (cached2 && cached2.
|
|
36835
|
+
if (cached2 && cached2.refreshBy > Date.now())
|
|
36801
36836
|
return cached2.ctx;
|
|
36802
36837
|
if (inflight)
|
|
36803
36838
|
return inflight;
|
|
36804
36839
|
const attempt = resolve(options2).then((ctx) => {
|
|
36805
|
-
cached2 = { ctx,
|
|
36840
|
+
cached2 = { ctx, refreshBy: refreshBy(ctx) };
|
|
36806
36841
|
return ctx;
|
|
36807
36842
|
});
|
|
36808
36843
|
inflight = attempt;
|
|
@@ -36928,13 +36963,13 @@ function createRemoteContextResolver(options2) {
|
|
|
36928
36963
|
let cached2 = null;
|
|
36929
36964
|
let inflight = null;
|
|
36930
36965
|
return async function getContext2() {
|
|
36931
|
-
if (cached2 && cached2.
|
|
36966
|
+
if (cached2 && cached2.refreshBy > Date.now())
|
|
36932
36967
|
return cached2.ctx;
|
|
36933
36968
|
if (inflight)
|
|
36934
36969
|
return inflight;
|
|
36935
36970
|
const attempt = fetchDelegatedSession(options2.endpoint, options2.token).then((session) => {
|
|
36936
36971
|
const ctx = contextFromSession(options2.schema, session);
|
|
36937
|
-
cached2 = { ctx,
|
|
36972
|
+
cached2 = { ctx, refreshBy: refreshBy(ctx) };
|
|
36938
36973
|
return ctx;
|
|
36939
36974
|
});
|
|
36940
36975
|
inflight = attempt;
|
|
@@ -36974,7 +37009,8 @@ function contextFromSession(schema, session) {
|
|
|
36974
37009
|
userId: session.userId,
|
|
36975
37010
|
email: session.email,
|
|
36976
37011
|
workspaceId: session.workspaceId,
|
|
36977
|
-
workspaceName: session.workspaceName
|
|
37012
|
+
workspaceName: session.workspaceName,
|
|
37013
|
+
expiresAt: decodeJwtExpiry(session.accessToken)
|
|
36978
37014
|
};
|
|
36979
37015
|
}
|
|
36980
37016
|
async function mintContext(options2) {
|
|
@@ -36985,7 +37021,8 @@ async function mintContext(options2) {
|
|
|
36985
37021
|
userId: principal.userId,
|
|
36986
37022
|
email: principal.email,
|
|
36987
37023
|
workspaceId: principal.workspaceId,
|
|
36988
|
-
workspaceName: principal.workspaceName
|
|
37024
|
+
workspaceName: principal.workspaceName,
|
|
37025
|
+
expiresAt: decodeJwtExpiry(accessToken)
|
|
36989
37026
|
};
|
|
36990
37027
|
}
|
|
36991
37028
|
async function resolve(options2) {
|
|
@@ -36993,7 +37030,7 @@ async function resolve(options2) {
|
|
|
36993
37030
|
const principal = await authenticate({ config: config3, token, product, mintedAt });
|
|
36994
37031
|
return mintContext({ config: config3, principal, schema });
|
|
36995
37032
|
}
|
|
36996
|
-
var TokenRefused, sessionInFlight, MINT_ATTEMPTS, DELEGATED_SESSION_ENV;
|
|
37033
|
+
var TokenRefused, ASSUMED_SESSION_LIFETIME_MS, sessionInFlight, MINT_ATTEMPTS, DELEGATED_SESSION_ENV;
|
|
36997
37034
|
var init_context = __esm({
|
|
36998
37035
|
"../../packages/mcp-kit/dist/context.js"() {
|
|
36999
37036
|
"use strict";
|
|
@@ -37005,6 +37042,7 @@ var init_context = __esm({
|
|
|
37005
37042
|
this.name = "TokenRefused";
|
|
37006
37043
|
}
|
|
37007
37044
|
};
|
|
37045
|
+
ASSUMED_SESSION_LIFETIME_MS = 50 * 6e4;
|
|
37008
37046
|
sessionInFlight = /* @__PURE__ */ new Map();
|
|
37009
37047
|
MINT_ATTEMPTS = 3;
|
|
37010
37048
|
DELEGATED_SESSION_ENV = "CRUO_SESSION";
|
|
@@ -46270,6 +46308,11 @@ function getContext() {
|
|
|
46270
46308
|
});
|
|
46271
46309
|
return stdioResolver();
|
|
46272
46310
|
}
|
|
46311
|
+
async function refreshContext() {
|
|
46312
|
+
const token = readToken({ mintedAt: MINTED_AT });
|
|
46313
|
+
const endpoint = sessionEndpoint();
|
|
46314
|
+
return endpoint ? contextFromSession("pm", await fetchDelegatedSession(endpoint, token)) : resolveContext({ config: config2, token, product: "projects", schema: "pm", mintedAt: MINTED_AT });
|
|
46315
|
+
}
|
|
46273
46316
|
function sessionEndpoint() {
|
|
46274
46317
|
const raw = process.env.CRUO_SESSION_URL;
|
|
46275
46318
|
if (raw && raw.trim()) return raw.trim();
|
|
@@ -46527,6 +46570,56 @@ var init_usage = __esm({
|
|
|
46527
46570
|
}
|
|
46528
46571
|
});
|
|
46529
46572
|
|
|
46573
|
+
// src/session-renewal.ts
|
|
46574
|
+
async function withFreshSession(ctx, attempt, renewal) {
|
|
46575
|
+
const first = await attempt();
|
|
46576
|
+
if (!first.error || !isExpiredSessionError(first.error)) {
|
|
46577
|
+
warnedThisOutage = false;
|
|
46578
|
+
return first;
|
|
46579
|
+
}
|
|
46580
|
+
try {
|
|
46581
|
+
const fresh = await renewal.renew();
|
|
46582
|
+
ctx.client = fresh.client;
|
|
46583
|
+
ctx.userId = fresh.userId;
|
|
46584
|
+
ctx.email = fresh.email;
|
|
46585
|
+
ctx.workspaceId = fresh.workspaceId;
|
|
46586
|
+
ctx.workspaceName = fresh.workspaceName;
|
|
46587
|
+
ctx.expiresAt = fresh.expiresAt;
|
|
46588
|
+
warnedThisOutage = false;
|
|
46589
|
+
return await attempt();
|
|
46590
|
+
} catch (error51) {
|
|
46591
|
+
if (!warnedThisOutage) {
|
|
46592
|
+
warnedThisOutage = true;
|
|
46593
|
+
renewal.log(
|
|
46594
|
+
`! this agent's session expired and could not be renewed: ${error51 instanceof Error ? error51.message : String(error51)}`
|
|
46595
|
+
);
|
|
46596
|
+
renewal.log(
|
|
46597
|
+
` board writes will keep failing until this is fixed \u2014 the run in progress is not killed for it.`
|
|
46598
|
+
);
|
|
46599
|
+
}
|
|
46600
|
+
return first;
|
|
46601
|
+
}
|
|
46602
|
+
}
|
|
46603
|
+
var warnedThisOutage;
|
|
46604
|
+
var init_session_renewal = __esm({
|
|
46605
|
+
"src/session-renewal.ts"() {
|
|
46606
|
+
"use strict";
|
|
46607
|
+
init_dist7();
|
|
46608
|
+
warnedThisOutage = false;
|
|
46609
|
+
}
|
|
46610
|
+
});
|
|
46611
|
+
|
|
46612
|
+
// src/claim.ts
|
|
46613
|
+
function interpretClaim(data, error51) {
|
|
46614
|
+
if (error51) return { taken: false, heldElsewhere: false, message: error51.message };
|
|
46615
|
+
return data === true ? { taken: true } : { taken: false, heldElsewhere: true };
|
|
46616
|
+
}
|
|
46617
|
+
var init_claim = __esm({
|
|
46618
|
+
"src/claim.ts"() {
|
|
46619
|
+
"use strict";
|
|
46620
|
+
}
|
|
46621
|
+
});
|
|
46622
|
+
|
|
46530
46623
|
// src/harness-signal.ts
|
|
46531
46624
|
function refusalIn(stdout) {
|
|
46532
46625
|
if (!stdout.trim()) return null;
|
|
@@ -46725,6 +46818,48 @@ var init_pacing = __esm({
|
|
|
46725
46818
|
}
|
|
46726
46819
|
});
|
|
46727
46820
|
|
|
46821
|
+
// src/wake-clock.ts
|
|
46822
|
+
function tickWakeClock(clock, now, tickMs, timeoutMs) {
|
|
46823
|
+
const elapsed = now - clock.lastTickAt;
|
|
46824
|
+
const sleepThresholdMs = tickMs * 4;
|
|
46825
|
+
const asleep = elapsed > sleepThresholdMs;
|
|
46826
|
+
const creditedMs = asleep ? tickMs : Math.max(elapsed, 0);
|
|
46827
|
+
const awakeMs = clock.awakeMs + creditedMs;
|
|
46828
|
+
const wokeAfterMs = asleep ? elapsed - tickMs : null;
|
|
46829
|
+
return {
|
|
46830
|
+
next: {
|
|
46831
|
+
awakeMs,
|
|
46832
|
+
lastTickAt: now,
|
|
46833
|
+
sawSleep: clock.sawSleep || asleep
|
|
46834
|
+
},
|
|
46835
|
+
wokeAfterMs,
|
|
46836
|
+
timedOut: awakeMs >= timeoutMs
|
|
46837
|
+
};
|
|
46838
|
+
}
|
|
46839
|
+
function killSaysNothingAboutCard(run) {
|
|
46840
|
+
return run.killed && run.sawSleep;
|
|
46841
|
+
}
|
|
46842
|
+
function humanDuration(ms2) {
|
|
46843
|
+
const totalSeconds = Math.max(0, Math.round(ms2 / 1e3));
|
|
46844
|
+
const h = Math.floor(totalSeconds / 3600);
|
|
46845
|
+
const m = Math.floor(totalSeconds % 3600 / 60);
|
|
46846
|
+
const s = totalSeconds % 60;
|
|
46847
|
+
if (h > 0) return `${h}h${m}m`;
|
|
46848
|
+
if (m > 0) return `${m}m${s}s`;
|
|
46849
|
+
return `${s}s`;
|
|
46850
|
+
}
|
|
46851
|
+
var freshWakeClock;
|
|
46852
|
+
var init_wake_clock = __esm({
|
|
46853
|
+
"src/wake-clock.ts"() {
|
|
46854
|
+
"use strict";
|
|
46855
|
+
freshWakeClock = (now) => ({
|
|
46856
|
+
awakeMs: 0,
|
|
46857
|
+
lastTickAt: now,
|
|
46858
|
+
sawSleep: false
|
|
46859
|
+
});
|
|
46860
|
+
}
|
|
46861
|
+
});
|
|
46862
|
+
|
|
46728
46863
|
// src/queries.ts
|
|
46729
46864
|
function blocksAgentPickup(state) {
|
|
46730
46865
|
return state.requires_human === true;
|
|
@@ -47042,15 +47177,19 @@ async function pollMentions(ctx, identity) {
|
|
|
47042
47177
|
}
|
|
47043
47178
|
async function claim(ctx, hit) {
|
|
47044
47179
|
const ttlMs = options.harnessTimeoutMs + 3e4;
|
|
47045
|
-
const { data, error: error51 } = await
|
|
47046
|
-
|
|
47047
|
-
|
|
47048
|
-
|
|
47049
|
-
|
|
47050
|
-
|
|
47051
|
-
|
|
47180
|
+
const { data, error: error51 } = await withFreshSession(
|
|
47181
|
+
ctx,
|
|
47182
|
+
() => ctx.client.schema("public").rpc("claim_issue", {
|
|
47183
|
+
issue: hit.issue.id,
|
|
47184
|
+
ttl_ms: ttlMs
|
|
47185
|
+
}),
|
|
47186
|
+
sessionRenewal
|
|
47187
|
+
);
|
|
47188
|
+
const outcome = interpretClaim(data, error51);
|
|
47189
|
+
if (!outcome.taken && !outcome.heldElsewhere) {
|
|
47190
|
+
log(` could not claim ${hit.ref}: ${outcome.message} \u2014 leaving it`);
|
|
47052
47191
|
}
|
|
47053
|
-
return
|
|
47192
|
+
return outcome;
|
|
47054
47193
|
}
|
|
47055
47194
|
async function claimedElsewhere(ctx, hits) {
|
|
47056
47195
|
if (hits.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -47062,12 +47201,20 @@ async function claimedElsewhere(ctx, hits) {
|
|
|
47062
47201
|
return new Set((data ?? []).map((r) => r.issue_id));
|
|
47063
47202
|
}
|
|
47064
47203
|
async function release(ctx, hit) {
|
|
47065
|
-
const { error: error51 } = await
|
|
47204
|
+
const { error: error51 } = await withFreshSession(
|
|
47205
|
+
ctx,
|
|
47206
|
+
() => ctx.client.schema("pm").from("issue_claims").delete().eq("issue_id", hit.issue.id).eq("claimed_by", ctx.userId),
|
|
47207
|
+
sessionRenewal
|
|
47208
|
+
);
|
|
47066
47209
|
if (error51) log(` could not release ${hit.ref}: ${error51.message}`);
|
|
47067
47210
|
}
|
|
47068
47211
|
async function markRead(ctx, notificationIds) {
|
|
47069
47212
|
if (notificationIds.length === 0) return;
|
|
47070
|
-
const { error: error51 } = await
|
|
47213
|
+
const { error: error51 } = await withFreshSession(
|
|
47214
|
+
ctx,
|
|
47215
|
+
() => ctx.client.schema("public").from("notifications").update({ read_at: (/* @__PURE__ */ new Date()).toISOString() }).in("id", notificationIds),
|
|
47216
|
+
sessionRenewal
|
|
47217
|
+
);
|
|
47071
47218
|
if (error51) log(` could not mark ${notificationIds.length} mention(s) read: ${error51.message}`);
|
|
47072
47219
|
}
|
|
47073
47220
|
async function writeMcpConfig() {
|
|
@@ -47351,14 +47498,29 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47351
47498
|
});
|
|
47352
47499
|
child.stderr?.on("data", (c) => process.stderr.write(c));
|
|
47353
47500
|
let killed = false;
|
|
47354
|
-
const
|
|
47355
|
-
|
|
47356
|
-
|
|
47357
|
-
|
|
47358
|
-
|
|
47359
|
-
|
|
47501
|
+
const WAKE_TICK_MS = 5e3;
|
|
47502
|
+
let wakeClock = freshWakeClock(Date.now());
|
|
47503
|
+
const wakeTimer = setInterval(() => {
|
|
47504
|
+
const step = tickWakeClock(wakeClock, Date.now(), WAKE_TICK_MS, options.harnessTimeoutMs);
|
|
47505
|
+
wakeClock = step.next;
|
|
47506
|
+
if (step.wokeAfterMs !== null) {
|
|
47507
|
+
log(` machine slept for ${humanDuration(step.wokeAfterMs)} \u2014 not counted against ${hit.ref}`);
|
|
47508
|
+
void claim(ctx, hit).catch(() => {
|
|
47509
|
+
});
|
|
47510
|
+
}
|
|
47511
|
+
if (step.timedOut && !killed) {
|
|
47512
|
+
killed = true;
|
|
47513
|
+
clearInterval(wakeTimer);
|
|
47514
|
+
log(
|
|
47515
|
+
` harness exceeded ${options.harnessTimeoutMs / 1e3}s awake` + (wakeClock.sawSleep ? " (excluding sleep) \u2014 killing it" : " \u2014 killing it")
|
|
47516
|
+
);
|
|
47517
|
+
child.kill("SIGTERM");
|
|
47518
|
+
setTimeout(() => child.kill("SIGKILL"), 1e4).unref();
|
|
47519
|
+
}
|
|
47520
|
+
}, WAKE_TICK_MS);
|
|
47521
|
+
wakeTimer.unref?.();
|
|
47360
47522
|
child.on("error", (e) => {
|
|
47361
|
-
|
|
47523
|
+
clearInterval(wakeTimer);
|
|
47362
47524
|
log(` harness failed to start: ${e.message}`);
|
|
47363
47525
|
resolve2({
|
|
47364
47526
|
code: 127,
|
|
@@ -47367,6 +47529,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47367
47529
|
durationMs: Date.now() - startedAtMs,
|
|
47368
47530
|
usage: null,
|
|
47369
47531
|
killed,
|
|
47532
|
+
sawSleep: wakeClock.sawSleep,
|
|
47370
47533
|
stdoutHead: headFor(head2),
|
|
47371
47534
|
openingContext: null,
|
|
47372
47535
|
systemPromptChars: systemPromptText.length,
|
|
@@ -47374,7 +47537,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47374
47537
|
});
|
|
47375
47538
|
});
|
|
47376
47539
|
child.on("close", (code) => {
|
|
47377
|
-
|
|
47540
|
+
clearInterval(wakeTimer);
|
|
47378
47541
|
if (streaming && pending.trim() !== "") {
|
|
47379
47542
|
tail = pending;
|
|
47380
47543
|
if (openingContext === null) openingContext = turnUsageIn(pending);
|
|
@@ -47390,6 +47553,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47390
47553
|
durationMs: Date.now() - startedAtMs,
|
|
47391
47554
|
usage: usageIn(tail),
|
|
47392
47555
|
killed,
|
|
47556
|
+
sawSleep: wakeClock.sawSleep,
|
|
47393
47557
|
stdoutHead: headFor(head2),
|
|
47394
47558
|
openingContext,
|
|
47395
47559
|
systemPromptChars: systemPromptText.length,
|
|
@@ -47414,25 +47578,33 @@ async function loadAttempts(ctx, hits) {
|
|
|
47414
47578
|
}
|
|
47415
47579
|
async function recordAttempt(ctx, hit, outcome, previous) {
|
|
47416
47580
|
const attempts = previous + 1;
|
|
47417
|
-
const { error: error51 } = await
|
|
47418
|
-
|
|
47419
|
-
|
|
47420
|
-
|
|
47421
|
-
|
|
47422
|
-
|
|
47423
|
-
|
|
47424
|
-
|
|
47425
|
-
|
|
47426
|
-
|
|
47427
|
-
|
|
47428
|
-
|
|
47429
|
-
|
|
47581
|
+
const { error: error51 } = await withFreshSession(
|
|
47582
|
+
ctx,
|
|
47583
|
+
() => ctx.client.from("issue_attempts").upsert(
|
|
47584
|
+
{
|
|
47585
|
+
issue_id: hit.issue.id,
|
|
47586
|
+
agent_id: ctx.userId,
|
|
47587
|
+
reason: hit.reason,
|
|
47588
|
+
attempts,
|
|
47589
|
+
last_tried_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47590
|
+
last_outcome: outcome,
|
|
47591
|
+
// Stamped at the moment the budget runs out, so the board can say when
|
|
47592
|
+
// the automation gave up rather than only that it did.
|
|
47593
|
+
set_aside_at: attempts >= options.maxAttempts ? (/* @__PURE__ */ new Date()).toISOString() : null
|
|
47594
|
+
},
|
|
47595
|
+
{ onConflict: "issue_id,agent_id,reason" }
|
|
47596
|
+
),
|
|
47597
|
+
sessionRenewal
|
|
47430
47598
|
);
|
|
47431
47599
|
if (error51) log(` could not record the attempt on ${hit.ref}: ${error51.message}`);
|
|
47432
47600
|
return attempts;
|
|
47433
47601
|
}
|
|
47434
47602
|
async function clearAttempts(ctx, hit) {
|
|
47435
|
-
const { error: error51 } = await
|
|
47603
|
+
const { error: error51 } = await withFreshSession(
|
|
47604
|
+
ctx,
|
|
47605
|
+
() => ctx.client.from("issue_attempts").delete().eq("issue_id", hit.issue.id).eq("agent_id", ctx.userId).eq("reason", hit.reason),
|
|
47606
|
+
sessionRenewal
|
|
47607
|
+
);
|
|
47436
47608
|
if (error51) log(` could not clear attempts on ${hit.ref}: ${error51.message}`);
|
|
47437
47609
|
}
|
|
47438
47610
|
async function repliedSince(ctx, hit, since2) {
|
|
@@ -47464,20 +47636,24 @@ async function publish(ctx, hit, worktree) {
|
|
|
47464
47636
|
}
|
|
47465
47637
|
}
|
|
47466
47638
|
async function beat(ctx, fields) {
|
|
47467
|
-
const { error: error51 } = await
|
|
47468
|
-
|
|
47469
|
-
|
|
47470
|
-
|
|
47471
|
-
|
|
47472
|
-
|
|
47473
|
-
|
|
47474
|
-
|
|
47475
|
-
|
|
47476
|
-
|
|
47477
|
-
|
|
47478
|
-
|
|
47479
|
-
|
|
47480
|
-
|
|
47639
|
+
const { error: error51 } = await withFreshSession(
|
|
47640
|
+
ctx,
|
|
47641
|
+
() => ctx.client.from("agent_heartbeats").upsert(
|
|
47642
|
+
{
|
|
47643
|
+
user_id: ctx.userId,
|
|
47644
|
+
workspace_id: ctx.workspaceId,
|
|
47645
|
+
last_seen_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47646
|
+
holding: fields.holding,
|
|
47647
|
+
dead_ticks: fields.deadTicks,
|
|
47648
|
+
poll_interval_ms: fields.intervalMs,
|
|
47649
|
+
// CRA-102. Without this the board cannot tell an agent someone paused
|
|
47650
|
+
// from one that is wading through a backlog, nor one stopped on purpose
|
|
47651
|
+
// from one whose laptop shut its lid — all of them simply went quiet.
|
|
47652
|
+
status: fields.status ?? (paused ? "paused" : "running")
|
|
47653
|
+
},
|
|
47654
|
+
{ onConflict: "user_id,workspace_id" }
|
|
47655
|
+
),
|
|
47656
|
+
sessionRenewal
|
|
47481
47657
|
);
|
|
47482
47658
|
if (error51) log(` could not record a heartbeat: ${error51.message}`);
|
|
47483
47659
|
lastHolding = fields.holding;
|
|
@@ -47506,28 +47682,32 @@ function beatWhileBusy(ctx, deadTicks) {
|
|
|
47506
47682
|
return () => clearInterval(timer);
|
|
47507
47683
|
}
|
|
47508
47684
|
async function recordRun(ctx, hit, run, outcome) {
|
|
47509
|
-
const { error: error51 } = await
|
|
47510
|
-
|
|
47511
|
-
|
|
47512
|
-
|
|
47513
|
-
|
|
47514
|
-
|
|
47515
|
-
|
|
47516
|
-
|
|
47517
|
-
|
|
47518
|
-
|
|
47519
|
-
|
|
47520
|
-
|
|
47521
|
-
|
|
47522
|
-
|
|
47523
|
-
|
|
47524
|
-
|
|
47525
|
-
|
|
47526
|
-
|
|
47527
|
-
|
|
47528
|
-
|
|
47529
|
-
|
|
47530
|
-
|
|
47685
|
+
const { error: error51 } = await withFreshSession(
|
|
47686
|
+
ctx,
|
|
47687
|
+
() => ctx.client.from("harness_runs").insert({
|
|
47688
|
+
agent_id: ctx.userId,
|
|
47689
|
+
workspace_id: ctx.workspaceId,
|
|
47690
|
+
issue_id: hit.issue.id,
|
|
47691
|
+
reason: hit.reason,
|
|
47692
|
+
duration_ms: run.durationMs,
|
|
47693
|
+
exit_code: run.code,
|
|
47694
|
+
stdout_bytes: run.stdoutBytes,
|
|
47695
|
+
stdout_head: run.stdoutHead,
|
|
47696
|
+
outcome,
|
|
47697
|
+
model: run.usage?.model ?? null,
|
|
47698
|
+
input_tokens: run.usage?.inputTokens ?? null,
|
|
47699
|
+
output_tokens: run.usage?.outputTokens ?? null,
|
|
47700
|
+
cache_read_tokens: run.usage?.cacheReadTokens ?? null,
|
|
47701
|
+
cache_write_tokens: run.usage?.cacheWriteTokens ?? null,
|
|
47702
|
+
cost_usd: run.usage?.costUsd ?? null,
|
|
47703
|
+
opening_context_input_tokens: run.openingContext?.inputTokens ?? null,
|
|
47704
|
+
opening_context_cache_read_tokens: run.openingContext?.cacheReadTokens ?? null,
|
|
47705
|
+
opening_context_cache_write_tokens: run.openingContext?.cacheWriteTokens ?? null,
|
|
47706
|
+
system_prompt_chars: run.systemPromptChars,
|
|
47707
|
+
user_prompt_chars: run.userPromptChars
|
|
47708
|
+
}),
|
|
47709
|
+
sessionRenewal
|
|
47710
|
+
);
|
|
47531
47711
|
if (error51 && !warnedAboutSpendTable) {
|
|
47532
47712
|
warnedAboutSpendTable = true;
|
|
47533
47713
|
log(` (not recording run costs: ${error51.message})`);
|
|
@@ -47597,8 +47777,9 @@ async function tick(ctx, identity, deadTicks, allowance) {
|
|
|
47597
47777
|
invoked += 1;
|
|
47598
47778
|
const before = { status: hit.issue.status_id, assignee: hit.issue.assignee_id };
|
|
47599
47779
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
47600
|
-
|
|
47601
|
-
|
|
47780
|
+
const claimed = await claim(ctx, hit);
|
|
47781
|
+
if (!claimed.taken) {
|
|
47782
|
+
if (claimed.heldElsewhere) log(` ${hit.ref} is being worked by another agent \u2014 skipping`);
|
|
47602
47783
|
continue;
|
|
47603
47784
|
}
|
|
47604
47785
|
let worktree = null;
|
|
@@ -47679,6 +47860,9 @@ async function tick(ctx, identity, deadTicks, allowance) {
|
|
|
47679
47860
|
} else {
|
|
47680
47861
|
log(` ${hit.ref} handed on (harness exit ${code})`);
|
|
47681
47862
|
}
|
|
47863
|
+
} else if (killSaysNothingAboutCard(run)) {
|
|
47864
|
+
const note = salvaged ? `killed after the machine slept mid-run (exit ${code}); cut short, work saved as ${salvaged.slice(0, 8)}` : `killed after the machine slept mid-run (exit ${code})`;
|
|
47865
|
+
log(` ${hit.ref} ${note} \u2014 not counted against this issue`);
|
|
47682
47866
|
} else {
|
|
47683
47867
|
const what = hit.reason === "mention" ? "no reply written" : "unchanged";
|
|
47684
47868
|
const note = salvaged ? `${what} (exit ${code}); cut short, work saved as ${salvaged.slice(0, 8)}` : `${what} (exit ${code})`;
|
|
@@ -47910,7 +48094,7 @@ to catch: a second supervisor nothing could see or stop.
|
|
|
47910
48094
|
if (stopping) return;
|
|
47911
48095
|
}
|
|
47912
48096
|
}
|
|
47913
|
-
var argv, flag, opt, num2, ms, readOptions, options, log, worktreeConfig, lastRefusal, CONFIG_DIR_PREFIX, CRUO_OWNED_ENV, HEAD_STORED, lastHolding, warnedAboutSpendTable, stopping, paused, stopRequestedAt, currentRun, pendingWait;
|
|
48097
|
+
var argv, flag, opt, num2, ms, readOptions, options, log, sessionRenewal, worktreeConfig, lastRefusal, CONFIG_DIR_PREFIX, CRUO_OWNED_ENV, HEAD_STORED, lastHolding, warnedAboutSpendTable, stopping, paused, stopRequestedAt, currentRun, pendingWait;
|
|
47914
48098
|
var init_supervisor = __esm({
|
|
47915
48099
|
"src/supervisor.ts"() {
|
|
47916
48100
|
"use strict";
|
|
@@ -47918,10 +48102,13 @@ var init_supervisor = __esm({
|
|
|
47918
48102
|
init_dist7();
|
|
47919
48103
|
init_env2();
|
|
47920
48104
|
init_auth();
|
|
48105
|
+
init_session_renewal();
|
|
48106
|
+
init_claim();
|
|
47921
48107
|
init_options();
|
|
47922
48108
|
init_harness_signal();
|
|
47923
48109
|
init_process();
|
|
47924
48110
|
init_pacing();
|
|
48111
|
+
init_wake_clock();
|
|
47925
48112
|
init_queries();
|
|
47926
48113
|
init_worktree();
|
|
47927
48114
|
argv = process.argv.slice(2);
|
|
@@ -48079,6 +48266,7 @@ cruo-supervisor: ${error51.message}
|
|
|
48079
48266
|
}
|
|
48080
48267
|
})();
|
|
48081
48268
|
log = (...parts) => console.log(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false })}]`, ...parts);
|
|
48269
|
+
sessionRenewal = { renew: refreshContext, log };
|
|
48082
48270
|
worktreeConfig = null;
|
|
48083
48271
|
lastRefusal = null;
|
|
48084
48272
|
CONFIG_DIR_PREFIX = "cruo-sup-";
|