cruo-agent 0.1.14 → 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 +287 -81
- 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;
|
|
@@ -46535,8 +46628,14 @@ function refusalIn(stdout) {
|
|
|
46535
46628
|
}
|
|
46536
46629
|
return null;
|
|
46537
46630
|
}
|
|
46631
|
+
function costlessInstant(run) {
|
|
46632
|
+
if (!run.usage || run.durationMs >= 1e4) return false;
|
|
46633
|
+
const tokens = (run.usage.inputTokens ?? 0) + (run.usage.outputTokens ?? 0);
|
|
46634
|
+
const cost = run.usage.costUsd ?? 0;
|
|
46635
|
+
return tokens === 0 && cost === 0;
|
|
46636
|
+
}
|
|
46538
46637
|
function neverRan(run) {
|
|
46539
|
-
return run.refusal !== null || run.stdoutBytes === 0;
|
|
46638
|
+
return run.refusal !== null || run.stdoutBytes === 0 || costlessInstant(run);
|
|
46540
46639
|
}
|
|
46541
46640
|
function usageIn(text) {
|
|
46542
46641
|
const trimmed = text.trimEnd();
|
|
@@ -46641,13 +46740,25 @@ var init_harness_signal = __esm({
|
|
|
46641
46740
|
// literal string in the 2.x binary. That one word cost 8,239 runs (CRA-89):
|
|
46642
46741
|
// the refusal went unrecognised, so `neverRan` was false, so the backoff
|
|
46643
46742
|
// never engaged and every card was charged for an outage.
|
|
46743
|
+
//
|
|
46744
|
+
// The NOUN is a family, not a list to extend one incident at a time. This
|
|
46745
|
+
// pattern has now been widened three times — `usage` (CRA-75), `reached
|
|
46746
|
+
// your` (CRA-89), and `session` (CRA-125, after "You've hit your session
|
|
46747
|
+
// limit · resets 2:30pm" set two cards aside inside sixty seconds). Each
|
|
46748
|
+
// miss costs the same way: the refusal is unrecognised, `neverRan` is false,
|
|
46749
|
+
// and an account outage is charged to whatever cards the agent was holding.
|
|
46750
|
+
// The word `limit` is still required, so a model writing about limits in a
|
|
46751
|
+
// card is not matched.
|
|
46644
46752
|
[
|
|
46645
|
-
/(hit|reached) your (monthly |weekly |daily )?(spend|usage) limit/i,
|
|
46753
|
+
/(hit|reached) your (monthly |weekly |daily |hourly )?(spend|usage|session|message|rate) limit/i,
|
|
46646
46754
|
"the account's usage limit is reached"
|
|
46647
46755
|
],
|
|
46648
46756
|
// The passive voice of the same thing, which is what the harness prints when
|
|
46649
46757
|
// it is reporting rather than addressing you.
|
|
46650
|
-
[
|
|
46758
|
+
[
|
|
46759
|
+
/(usage|spend|usage credit|session|message|rate) limit reached/i,
|
|
46760
|
+
"the account's usage limit is reached"
|
|
46761
|
+
],
|
|
46651
46762
|
// `is` optional: the binary carries BOTH "credit balance is too low" and
|
|
46652
46763
|
// "credit balance too low", and the pattern only knew the longer one.
|
|
46653
46764
|
[
|
|
@@ -46707,6 +46818,48 @@ var init_pacing = __esm({
|
|
|
46707
46818
|
}
|
|
46708
46819
|
});
|
|
46709
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
|
+
|
|
46710
46863
|
// src/queries.ts
|
|
46711
46864
|
function blocksAgentPickup(state) {
|
|
46712
46865
|
return state.requires_human === true;
|
|
@@ -47024,15 +47177,19 @@ async function pollMentions(ctx, identity) {
|
|
|
47024
47177
|
}
|
|
47025
47178
|
async function claim(ctx, hit) {
|
|
47026
47179
|
const ttlMs = options.harnessTimeoutMs + 3e4;
|
|
47027
|
-
const { data, error: error51 } = await
|
|
47028
|
-
|
|
47029
|
-
|
|
47030
|
-
|
|
47031
|
-
|
|
47032
|
-
|
|
47033
|
-
|
|
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`);
|
|
47034
47191
|
}
|
|
47035
|
-
return
|
|
47192
|
+
return outcome;
|
|
47036
47193
|
}
|
|
47037
47194
|
async function claimedElsewhere(ctx, hits) {
|
|
47038
47195
|
if (hits.length === 0) return /* @__PURE__ */ new Set();
|
|
@@ -47044,12 +47201,20 @@ async function claimedElsewhere(ctx, hits) {
|
|
|
47044
47201
|
return new Set((data ?? []).map((r) => r.issue_id));
|
|
47045
47202
|
}
|
|
47046
47203
|
async function release(ctx, hit) {
|
|
47047
|
-
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
|
+
);
|
|
47048
47209
|
if (error51) log(` could not release ${hit.ref}: ${error51.message}`);
|
|
47049
47210
|
}
|
|
47050
47211
|
async function markRead(ctx, notificationIds) {
|
|
47051
47212
|
if (notificationIds.length === 0) return;
|
|
47052
|
-
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
|
+
);
|
|
47053
47218
|
if (error51) log(` could not mark ${notificationIds.length} mention(s) read: ${error51.message}`);
|
|
47054
47219
|
}
|
|
47055
47220
|
async function writeMcpConfig() {
|
|
@@ -47333,14 +47498,29 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47333
47498
|
});
|
|
47334
47499
|
child.stderr?.on("data", (c) => process.stderr.write(c));
|
|
47335
47500
|
let killed = false;
|
|
47336
|
-
const
|
|
47337
|
-
|
|
47338
|
-
|
|
47339
|
-
|
|
47340
|
-
|
|
47341
|
-
|
|
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?.();
|
|
47342
47522
|
child.on("error", (e) => {
|
|
47343
|
-
|
|
47523
|
+
clearInterval(wakeTimer);
|
|
47344
47524
|
log(` harness failed to start: ${e.message}`);
|
|
47345
47525
|
resolve2({
|
|
47346
47526
|
code: 127,
|
|
@@ -47349,6 +47529,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47349
47529
|
durationMs: Date.now() - startedAtMs,
|
|
47350
47530
|
usage: null,
|
|
47351
47531
|
killed,
|
|
47532
|
+
sawSleep: wakeClock.sawSleep,
|
|
47352
47533
|
stdoutHead: headFor(head2),
|
|
47353
47534
|
openingContext: null,
|
|
47354
47535
|
systemPromptChars: systemPromptText.length,
|
|
@@ -47356,7 +47537,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47356
47537
|
});
|
|
47357
47538
|
});
|
|
47358
47539
|
child.on("close", (code) => {
|
|
47359
|
-
|
|
47540
|
+
clearInterval(wakeTimer);
|
|
47360
47541
|
if (streaming && pending.trim() !== "") {
|
|
47361
47542
|
tail = pending;
|
|
47362
47543
|
if (openingContext === null) openingContext = turnUsageIn(pending);
|
|
@@ -47372,6 +47553,7 @@ async function invokeHarness(ctx, identity, hit, worktree) {
|
|
|
47372
47553
|
durationMs: Date.now() - startedAtMs,
|
|
47373
47554
|
usage: usageIn(tail),
|
|
47374
47555
|
killed,
|
|
47556
|
+
sawSleep: wakeClock.sawSleep,
|
|
47375
47557
|
stdoutHead: headFor(head2),
|
|
47376
47558
|
openingContext,
|
|
47377
47559
|
systemPromptChars: systemPromptText.length,
|
|
@@ -47396,25 +47578,33 @@ async function loadAttempts(ctx, hits) {
|
|
|
47396
47578
|
}
|
|
47397
47579
|
async function recordAttempt(ctx, hit, outcome, previous) {
|
|
47398
47580
|
const attempts = previous + 1;
|
|
47399
|
-
const { error: error51 } = await
|
|
47400
|
-
|
|
47401
|
-
|
|
47402
|
-
|
|
47403
|
-
|
|
47404
|
-
|
|
47405
|
-
|
|
47406
|
-
|
|
47407
|
-
|
|
47408
|
-
|
|
47409
|
-
|
|
47410
|
-
|
|
47411
|
-
|
|
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
|
|
47412
47598
|
);
|
|
47413
47599
|
if (error51) log(` could not record the attempt on ${hit.ref}: ${error51.message}`);
|
|
47414
47600
|
return attempts;
|
|
47415
47601
|
}
|
|
47416
47602
|
async function clearAttempts(ctx, hit) {
|
|
47417
|
-
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
|
+
);
|
|
47418
47608
|
if (error51) log(` could not clear attempts on ${hit.ref}: ${error51.message}`);
|
|
47419
47609
|
}
|
|
47420
47610
|
async function repliedSince(ctx, hit, since2) {
|
|
@@ -47446,20 +47636,24 @@ async function publish(ctx, hit, worktree) {
|
|
|
47446
47636
|
}
|
|
47447
47637
|
}
|
|
47448
47638
|
async function beat(ctx, fields) {
|
|
47449
|
-
const { error: error51 } = await
|
|
47450
|
-
|
|
47451
|
-
|
|
47452
|
-
|
|
47453
|
-
|
|
47454
|
-
|
|
47455
|
-
|
|
47456
|
-
|
|
47457
|
-
|
|
47458
|
-
|
|
47459
|
-
|
|
47460
|
-
|
|
47461
|
-
|
|
47462
|
-
|
|
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
|
|
47463
47657
|
);
|
|
47464
47658
|
if (error51) log(` could not record a heartbeat: ${error51.message}`);
|
|
47465
47659
|
lastHolding = fields.holding;
|
|
@@ -47488,28 +47682,32 @@ function beatWhileBusy(ctx, deadTicks) {
|
|
|
47488
47682
|
return () => clearInterval(timer);
|
|
47489
47683
|
}
|
|
47490
47684
|
async function recordRun(ctx, hit, run, outcome) {
|
|
47491
|
-
const { error: error51 } = await
|
|
47492
|
-
|
|
47493
|
-
|
|
47494
|
-
|
|
47495
|
-
|
|
47496
|
-
|
|
47497
|
-
|
|
47498
|
-
|
|
47499
|
-
|
|
47500
|
-
|
|
47501
|
-
|
|
47502
|
-
|
|
47503
|
-
|
|
47504
|
-
|
|
47505
|
-
|
|
47506
|
-
|
|
47507
|
-
|
|
47508
|
-
|
|
47509
|
-
|
|
47510
|
-
|
|
47511
|
-
|
|
47512
|
-
|
|
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
|
+
);
|
|
47513
47711
|
if (error51 && !warnedAboutSpendTable) {
|
|
47514
47712
|
warnedAboutSpendTable = true;
|
|
47515
47713
|
log(` (not recording run costs: ${error51.message})`);
|
|
@@ -47579,8 +47777,9 @@ async function tick(ctx, identity, deadTicks, allowance) {
|
|
|
47579
47777
|
invoked += 1;
|
|
47580
47778
|
const before = { status: hit.issue.status_id, assignee: hit.issue.assignee_id };
|
|
47581
47779
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
47582
|
-
|
|
47583
|
-
|
|
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`);
|
|
47584
47783
|
continue;
|
|
47585
47784
|
}
|
|
47586
47785
|
let worktree = null;
|
|
@@ -47661,6 +47860,9 @@ async function tick(ctx, identity, deadTicks, allowance) {
|
|
|
47661
47860
|
} else {
|
|
47662
47861
|
log(` ${hit.ref} handed on (harness exit ${code})`);
|
|
47663
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`);
|
|
47664
47866
|
} else {
|
|
47665
47867
|
const what = hit.reason === "mention" ? "no reply written" : "unchanged";
|
|
47666
47868
|
const note = salvaged ? `${what} (exit ${code}); cut short, work saved as ${salvaged.slice(0, 8)}` : `${what} (exit ${code})`;
|
|
@@ -47892,7 +48094,7 @@ to catch: a second supervisor nothing could see or stop.
|
|
|
47892
48094
|
if (stopping) return;
|
|
47893
48095
|
}
|
|
47894
48096
|
}
|
|
47895
|
-
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;
|
|
47896
48098
|
var init_supervisor = __esm({
|
|
47897
48099
|
"src/supervisor.ts"() {
|
|
47898
48100
|
"use strict";
|
|
@@ -47900,10 +48102,13 @@ var init_supervisor = __esm({
|
|
|
47900
48102
|
init_dist7();
|
|
47901
48103
|
init_env2();
|
|
47902
48104
|
init_auth();
|
|
48105
|
+
init_session_renewal();
|
|
48106
|
+
init_claim();
|
|
47903
48107
|
init_options();
|
|
47904
48108
|
init_harness_signal();
|
|
47905
48109
|
init_process();
|
|
47906
48110
|
init_pacing();
|
|
48111
|
+
init_wake_clock();
|
|
47907
48112
|
init_queries();
|
|
47908
48113
|
init_worktree();
|
|
47909
48114
|
argv = process.argv.slice(2);
|
|
@@ -48061,6 +48266,7 @@ cruo-supervisor: ${error51.message}
|
|
|
48061
48266
|
}
|
|
48062
48267
|
})();
|
|
48063
48268
|
log = (...parts) => console.log(`[${(/* @__PURE__ */ new Date()).toLocaleTimeString("en-GB", { hour12: false })}]`, ...parts);
|
|
48269
|
+
sessionRenewal = { renew: refreshContext, log };
|
|
48064
48270
|
worktreeConfig = null;
|
|
48065
48271
|
lastRefusal = null;
|
|
48066
48272
|
CONFIG_DIR_PREFIX = "cruo-sup-";
|