automata-cli 0.12.0-develop.554 → 0.12.0-develop.562
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/index.js +313 -96
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -5435,6 +5435,44 @@ async function scrubExcerpt(execution, excerpt, timeoutMs = SECOND_OPINION_TIMEO
|
|
|
5435
5435
|
return result.ok ? interpretScrubOutput(result.stdout, excerpt) : result;
|
|
5436
5436
|
}
|
|
5437
5437
|
|
|
5438
|
+
// src/run/claudeAuth.ts
|
|
5439
|
+
var CLAUDE_AUTH_PATTERNS = [
|
|
5440
|
+
/Failed to authenticate/i,
|
|
5441
|
+
/OAuth (?:session|token) (?:has )?expired/i,
|
|
5442
|
+
/Invalid API key\s*[·\-:]\s*Please run \/login/i,
|
|
5443
|
+
/Please run \/login/i
|
|
5444
|
+
];
|
|
5445
|
+
var CLAUDE_AUTH_MARKER = "<!-- automata:claude-auth -->";
|
|
5446
|
+
function detectClaudeAuthFailure(text) {
|
|
5447
|
+
return CLAUDE_AUTH_PATTERNS.some((pattern) => pattern.test(text));
|
|
5448
|
+
}
|
|
5449
|
+
var CLAUDE_AUTH_TAIL_MAX = 8192;
|
|
5450
|
+
var ClaudeAuthTail = class {
|
|
5451
|
+
text = "";
|
|
5452
|
+
output(_stream, chunk) {
|
|
5453
|
+
this.text = (this.text + chunk).slice(-CLAUDE_AUTH_TAIL_MAX);
|
|
5454
|
+
}
|
|
5455
|
+
exited() {
|
|
5456
|
+
}
|
|
5457
|
+
get tail() {
|
|
5458
|
+
return this.text;
|
|
5459
|
+
}
|
|
5460
|
+
};
|
|
5461
|
+
var CLAUDE_AUTH_REASON = "Claude Code is not signed in (its login expired and could not be refreshed)";
|
|
5462
|
+
var CLAUDE_AUTH_REMEDY = "Run `claude /login` again on the machine that runs automata (inside the devcontainer, if it runs there), then reply to have another attempt made.";
|
|
5463
|
+
function renderClaudeAuthComment(surface, excerpt) {
|
|
5464
|
+
const lines = [
|
|
5465
|
+
`automata do-work: the agent run stopped before it posted an answer on ${surface}. ${CLAUDE_AUTH_REASON}.`,
|
|
5466
|
+
CLAUDE_AUTH_REMEDY,
|
|
5467
|
+
"This comment is posted once. Automata does not post it again while the problem remains."
|
|
5468
|
+
];
|
|
5469
|
+
if (excerpt.trim() !== "") {
|
|
5470
|
+
lines.push("", "Last lines of the agent's output:", "```text", excerpt.replaceAll("```", "'''"), "```");
|
|
5471
|
+
}
|
|
5472
|
+
return `${lines.join("\n")}
|
|
5473
|
+
${CLAUDE_AUTH_MARKER}`;
|
|
5474
|
+
}
|
|
5475
|
+
|
|
5438
5476
|
// src/run/answerRecovery.ts
|
|
5439
5477
|
import { closeSync, fstatSync, openSync, readSync } from "fs";
|
|
5440
5478
|
var RECOVERY_TIMEOUT_MS = 3e5;
|
|
@@ -6259,6 +6297,14 @@ function describeTickHistory(ticks, cadence, now, logPath, lines, problems) {
|
|
|
6259
6297
|
)
|
|
6260
6298
|
);
|
|
6261
6299
|
}
|
|
6300
|
+
if (ticks[0].note === "claude-auth") {
|
|
6301
|
+
problems.push(
|
|
6302
|
+
finding(
|
|
6303
|
+
`${CLAUDE_AUTH_REASON}, so the last tick stopped before it answered; ${CLAUDE_AUTH_REMEDY}`,
|
|
6304
|
+
"claude /login"
|
|
6305
|
+
)
|
|
6306
|
+
);
|
|
6307
|
+
}
|
|
6262
6308
|
const lockHeld = ticks.filter((tick) => tick.note === "lock-held").length;
|
|
6263
6309
|
const withRuns = ticks.filter((tick) => tick.runs > 0).length;
|
|
6264
6310
|
lines.push(
|
|
@@ -6638,14 +6684,20 @@ function requireParticipants(config) {
|
|
|
6638
6684
|
);
|
|
6639
6685
|
}
|
|
6640
6686
|
if (!config.issueDiscoveryTechnique) {
|
|
6641
|
-
failSettings(
|
|
6687
|
+
failSettings(
|
|
6688
|
+
"No issue discovery technique configured. Run `automata config set issue-discovery-technique <value>`."
|
|
6689
|
+
);
|
|
6642
6690
|
}
|
|
6643
6691
|
if (!config.issueDiscoveryValue) {
|
|
6644
|
-
failSettings(
|
|
6692
|
+
failSettings(
|
|
6693
|
+
"No issue discovery value configured. Run `automata config set issue-discovery-value <value>`."
|
|
6694
|
+
);
|
|
6645
6695
|
}
|
|
6646
6696
|
const allowedUsers = (config.allowedUsers ?? []).filter((user) => user.trim().length > 0);
|
|
6647
6697
|
if (allowedUsers.length === 0) {
|
|
6648
|
-
failSettings(
|
|
6698
|
+
failSettings(
|
|
6699
|
+
"No allowed users configured. Run `automata config set allowed-users <user1,user2>`."
|
|
6700
|
+
);
|
|
6649
6701
|
}
|
|
6650
6702
|
const agentUser = (config.agentUser ?? "").trim();
|
|
6651
6703
|
if (agentUser.length === 0) {
|
|
@@ -6864,8 +6916,20 @@ function validateDoWorkConfig(section2) {
|
|
|
6864
6916
|
failSettings(`doWork.executor must be 'claude' or 'codex', got ${JSON.stringify(executor)}.`);
|
|
6865
6917
|
}
|
|
6866
6918
|
validateOptionalString(section2, "baseBranch", "doWork.baseBranch");
|
|
6867
|
-
validateOptionalInt(
|
|
6868
|
-
|
|
6919
|
+
validateOptionalInt(
|
|
6920
|
+
section2,
|
|
6921
|
+
"maxRunsPerTick",
|
|
6922
|
+
"doWork.maxRunsPerTick",
|
|
6923
|
+
0,
|
|
6924
|
+
"a non-negative integer (0 = unlimited)"
|
|
6925
|
+
);
|
|
6926
|
+
validateOptionalInt(
|
|
6927
|
+
section2,
|
|
6928
|
+
"lockStaleMinutes",
|
|
6929
|
+
"doWork.lockStaleMinutes",
|
|
6930
|
+
1,
|
|
6931
|
+
"a positive integer"
|
|
6932
|
+
);
|
|
6869
6933
|
validateOptionalBoolean(section2, "dumpOnBlock", "doWork.dumpOnBlock");
|
|
6870
6934
|
validateOptionalBoolean(section2, "postRunLog", "doWork.postRunLog");
|
|
6871
6935
|
validateProtectedBranches(section2["protectedBranches"]);
|
|
@@ -6897,7 +6961,9 @@ function validateSettingContainer(value, container, keys) {
|
|
|
6897
6961
|
}
|
|
6898
6962
|
for (const key2 of Object.keys(value)) {
|
|
6899
6963
|
if (!keys.includes(key2)) {
|
|
6900
|
-
failSettings(
|
|
6964
|
+
failSettings(
|
|
6965
|
+
`doWork.${container}.${key2} is not a recognised setting; expected one of: ${keys.join(", ")}.`
|
|
6966
|
+
);
|
|
6901
6967
|
}
|
|
6902
6968
|
}
|
|
6903
6969
|
}
|
|
@@ -6909,7 +6975,11 @@ function orphanPassEnabled(settings) {
|
|
|
6909
6975
|
}
|
|
6910
6976
|
function discoverIssues(settings) {
|
|
6911
6977
|
if (!issuePassEnabled(settings)) return [];
|
|
6912
|
-
const candidates = listCandidateIssues(
|
|
6978
|
+
const candidates = listCandidateIssues(
|
|
6979
|
+
settings.technique,
|
|
6980
|
+
settings.discoveryValue,
|
|
6981
|
+
settings.limit
|
|
6982
|
+
);
|
|
6913
6983
|
if (settings.onlyIssue === void 0) {
|
|
6914
6984
|
if (candidates.length === settings.limit) {
|
|
6915
6985
|
progress(
|
|
@@ -7006,11 +7076,15 @@ function claimIssue(item, settings) {
|
|
|
7006
7076
|
if (!item.needsAssignment || item.issue === null) return;
|
|
7007
7077
|
try {
|
|
7008
7078
|
assignIssueToAgent(item.issue.number, settings.participants.agentUser);
|
|
7009
|
-
progress(
|
|
7010
|
-
`)
|
|
7079
|
+
progress(
|
|
7080
|
+
` assigned issue #${String(item.issue.number)} to ${settings.participants.agentUser}.
|
|
7081
|
+
`
|
|
7082
|
+
);
|
|
7011
7083
|
} catch (err) {
|
|
7012
|
-
progress(
|
|
7013
|
-
`)
|
|
7084
|
+
progress(
|
|
7085
|
+
` warning: could not assign issue #${String(item.issue.number)}: ${err.message}
|
|
7086
|
+
`
|
|
7087
|
+
);
|
|
7014
7088
|
}
|
|
7015
7089
|
}
|
|
7016
7090
|
function claimPr(prNumber, agentUser) {
|
|
@@ -7019,8 +7093,10 @@ function claimPr(prNumber, agentUser) {
|
|
|
7019
7093
|
progress(` assigned pull request #${String(prNumber)} to ${agentUser}.
|
|
7020
7094
|
`);
|
|
7021
7095
|
} catch (err) {
|
|
7022
|
-
progress(
|
|
7023
|
-
`)
|
|
7096
|
+
progress(
|
|
7097
|
+
` warning: could not assign pull request #${String(prNumber)}: ${err.message}
|
|
7098
|
+
`
|
|
7099
|
+
);
|
|
7024
7100
|
}
|
|
7025
7101
|
}
|
|
7026
7102
|
function notePickupOnIssue(item, marker) {
|
|
@@ -7033,13 +7109,17 @@ function notePickupOnIssue(item, marker) {
|
|
|
7033
7109
|
`automata do-work: picked this up on pull request #${String(item.pr.number)} \u2014 ${item.pr.url}`
|
|
7034
7110
|
);
|
|
7035
7111
|
} catch (err) {
|
|
7036
|
-
progress(
|
|
7037
|
-
`)
|
|
7112
|
+
progress(
|
|
7113
|
+
` skipped: could not note the pickup on issue #${String(item.issue.number)} \u2014 ${err.message}
|
|
7114
|
+
`
|
|
7115
|
+
);
|
|
7038
7116
|
try {
|
|
7039
7117
|
deleteMarker(marker);
|
|
7040
7118
|
} catch (deleteErr) {
|
|
7041
|
-
progress(
|
|
7042
|
-
`
|
|
7119
|
+
progress(
|
|
7120
|
+
` warning: could not withdraw the working marker: ${deleteErr.message}
|
|
7121
|
+
`
|
|
7122
|
+
);
|
|
7043
7123
|
}
|
|
7044
7124
|
return false;
|
|
7045
7125
|
}
|
|
@@ -7052,8 +7132,10 @@ function reportIssueMessagesBuriedByNote(item, note, participants) {
|
|
|
7052
7132
|
const messages = getIssueSurface(item.issue.number).messages;
|
|
7053
7133
|
buried = messagesBetween(messages, participants, watermark, note.createdAt);
|
|
7054
7134
|
} catch (err) {
|
|
7055
|
-
progress(
|
|
7056
|
-
`)
|
|
7135
|
+
progress(
|
|
7136
|
+
` warning: could not re-read issue #${String(item.issue.number)}: ${err.message}
|
|
7137
|
+
`
|
|
7138
|
+
);
|
|
7057
7139
|
return 0;
|
|
7058
7140
|
}
|
|
7059
7141
|
if (buried.length === 0) return 0;
|
|
@@ -7065,8 +7147,10 @@ function reportIssueMessagesBuriedByNote(item, note, participants) {
|
|
|
7065
7147
|
`automata do-work: ${authors} posted here while this issue was being picked up, so ${buried.length === 1 ? "that message was" : "those messages were"} not included in the run. Please post again to have them acted on.`
|
|
7066
7148
|
);
|
|
7067
7149
|
} catch (err) {
|
|
7068
|
-
progress(
|
|
7069
|
-
`)
|
|
7150
|
+
progress(
|
|
7151
|
+
` warning: could not report the buried issue message(s): ${err.message}
|
|
7152
|
+
`
|
|
7153
|
+
);
|
|
7070
7154
|
}
|
|
7071
7155
|
return buried.length;
|
|
7072
7156
|
}
|
|
@@ -7084,7 +7168,9 @@ function refreshOrphanItem(item, linkMap, settings, policy) {
|
|
|
7084
7168
|
const number = item.pr?.number ?? 0;
|
|
7085
7169
|
const stillOrphan = linkMap.orphans.some((candidate) => candidate.pr.number === number);
|
|
7086
7170
|
if (!stillOrphan) {
|
|
7087
|
-
const linkedTo = issuesClosedBy(linkMap, number).map(
|
|
7171
|
+
const linkedTo = issuesClosedBy(linkMap, number).map(
|
|
7172
|
+
(issueNumber) => `#${String(issueNumber)}`
|
|
7173
|
+
);
|
|
7088
7174
|
return {
|
|
7089
7175
|
kind: "skip",
|
|
7090
7176
|
issue: null,
|
|
@@ -7126,7 +7212,8 @@ function reportOvertakenMessages(item, analysis) {
|
|
|
7126
7212
|
`);
|
|
7127
7213
|
}
|
|
7128
7214
|
}
|
|
7129
|
-
async function reconcileMarker(item, marker, participants, watermark,
|
|
7215
|
+
async function reconcileMarker(item, marker, participants, watermark, run5) {
|
|
7216
|
+
const { runError, diagnostics, recovery, claudeAuth } = run5;
|
|
7130
7217
|
let analysis;
|
|
7131
7218
|
try {
|
|
7132
7219
|
analysis = analyseAnswer(readAnsweringSurface(item), participants, marker, watermark);
|
|
@@ -7139,7 +7226,8 @@ async function reconcileMarker(item, marker, participants, watermark, runError,
|
|
|
7139
7226
|
}
|
|
7140
7227
|
let recoveryNote = null;
|
|
7141
7228
|
let recovered = false;
|
|
7142
|
-
|
|
7229
|
+
const canRecover = runError === null && recovery !== null && claudeAuth === null;
|
|
7230
|
+
if (analysis.answeredAt === null && canRecover) {
|
|
7143
7231
|
const attempt = await attemptRecovery(item, marker, participants, watermark, recovery);
|
|
7144
7232
|
if ("analysis" in attempt) {
|
|
7145
7233
|
analysis = attempt.analysis;
|
|
@@ -7149,31 +7237,74 @@ async function reconcileMarker(item, marker, participants, watermark, runError,
|
|
|
7149
7237
|
}
|
|
7150
7238
|
}
|
|
7151
7239
|
if (analysis.answeredAt !== null) {
|
|
7152
|
-
|
|
7153
|
-
try {
|
|
7154
|
-
deleteMarker(marker);
|
|
7155
|
-
} catch (err) {
|
|
7156
|
-
progress(` warning: could not delete the marker comment: ${err.message}
|
|
7157
|
-
`);
|
|
7158
|
-
}
|
|
7159
|
-
if (analysis.missed.length > 0) {
|
|
7160
|
-
return {
|
|
7161
|
-
outcome: "answered-no-reply",
|
|
7162
|
-
detail: `answered (${String(analysis.toReport.length)} message(s) arrived mid-run and were flagged)`,
|
|
7163
|
-
reason: "flagged"
|
|
7164
|
-
};
|
|
7165
|
-
}
|
|
7166
|
-
if (recovered) {
|
|
7167
|
-
return { outcome: "answered", detail: "answered (recovered from the run transcript)", reason: "answered" };
|
|
7168
|
-
}
|
|
7169
|
-
return runError === null ? { outcome: "answered", detail: "answered", reason: "answered" } : { outcome: "answered", detail: `answered, but the run reported: ${runError.message}`, reason: "answered" };
|
|
7240
|
+
return reportAnswered(item, marker, analysis, runError, recovered);
|
|
7170
7241
|
}
|
|
7242
|
+
if (claudeAuth !== null) return reportClaudeAuth(item, marker, participants, claudeAuth);
|
|
7171
7243
|
return reportNoAnswer(item, marker, runError, diagnostics, recoveryNote);
|
|
7172
7244
|
}
|
|
7245
|
+
function reportAnswered(item, marker, analysis, runError, recovered) {
|
|
7246
|
+
reportOvertakenMessages(item, analysis);
|
|
7247
|
+
try {
|
|
7248
|
+
deleteMarker(marker);
|
|
7249
|
+
} catch (err) {
|
|
7250
|
+
progress(` warning: could not delete the marker comment: ${err.message}
|
|
7251
|
+
`);
|
|
7252
|
+
}
|
|
7253
|
+
if (analysis.missed.length > 0) {
|
|
7254
|
+
return {
|
|
7255
|
+
outcome: "answered-no-reply",
|
|
7256
|
+
detail: `answered (${String(analysis.toReport.length)} message(s) arrived mid-run and were flagged)`,
|
|
7257
|
+
reason: "flagged"
|
|
7258
|
+
};
|
|
7259
|
+
}
|
|
7260
|
+
if (recovered) {
|
|
7261
|
+
return {
|
|
7262
|
+
outcome: "answered",
|
|
7263
|
+
detail: "answered (recovered from the run transcript)",
|
|
7264
|
+
reason: "answered"
|
|
7265
|
+
};
|
|
7266
|
+
}
|
|
7267
|
+
return runError === null ? { outcome: "answered", detail: "answered", reason: "answered" } : {
|
|
7268
|
+
outcome: "answered",
|
|
7269
|
+
detail: `answered, but the run reported: ${runError.message}`,
|
|
7270
|
+
reason: "answered"
|
|
7271
|
+
};
|
|
7272
|
+
}
|
|
7273
|
+
function hasClaudeAuthComment(item, marker, participants) {
|
|
7274
|
+
try {
|
|
7275
|
+
const before = new Date(marker.createdAt).getTime();
|
|
7276
|
+
const agent = participants.agentUser.toLowerCase();
|
|
7277
|
+
const last = readAnsweringSurface(item).findLast(
|
|
7278
|
+
(message) => message.author.toLowerCase() === agent && new Date(message.createdAt).getTime() < before
|
|
7279
|
+
);
|
|
7280
|
+
return last?.body.includes(CLAUDE_AUTH_MARKER) === true;
|
|
7281
|
+
} catch {
|
|
7282
|
+
return false;
|
|
7283
|
+
}
|
|
7284
|
+
}
|
|
7285
|
+
function reportClaudeAuth(item, marker, participants, excerpt) {
|
|
7286
|
+
const surface = markerSurfaceLabel(item);
|
|
7287
|
+
const result = {
|
|
7288
|
+
outcome: "failed",
|
|
7289
|
+
detail: `${CLAUDE_AUTH_REASON}. ${CLAUDE_AUTH_REMEDY}`,
|
|
7290
|
+
reason: "no-answer",
|
|
7291
|
+
claudeAuth: true
|
|
7292
|
+
};
|
|
7293
|
+
const alreadyTold = hasClaudeAuthComment(item, marker, participants);
|
|
7294
|
+
try {
|
|
7295
|
+
if (alreadyTold) deleteMarker(marker);
|
|
7296
|
+
else updateMarker(marker, renderClaudeAuthComment(surface, excerpt));
|
|
7297
|
+
} catch (err) {
|
|
7298
|
+
warnMarkerNotUpdated(surface, err, "that Claude Code is not signed in");
|
|
7299
|
+
}
|
|
7300
|
+
return result;
|
|
7301
|
+
}
|
|
7173
7302
|
async function attemptRecovery(item, marker, participants, watermark, recovery) {
|
|
7174
7303
|
const surface = markerSurfaceLabel(item);
|
|
7175
|
-
progress(
|
|
7176
|
-
`
|
|
7304
|
+
progress(
|
|
7305
|
+
` recovery: the run posted no answer on ${surface}; asking the model to write it from the transcript.
|
|
7306
|
+
`
|
|
7307
|
+
);
|
|
7177
7308
|
const reread = () => analyseAnswer(readAnsweringSurface(item), participants, marker, watermark);
|
|
7178
7309
|
const fail3 = (reason) => {
|
|
7179
7310
|
progress(` recovery: failed \u2014 ${reason}
|
|
@@ -7205,12 +7336,15 @@ async function attemptRecovery(item, marker, participants, watermark, recovery)
|
|
|
7205
7336
|
}
|
|
7206
7337
|
try {
|
|
7207
7338
|
const verified2 = reread();
|
|
7208
|
-
if (verified2.answeredAt === null)
|
|
7339
|
+
if (verified2.answeredAt === null)
|
|
7340
|
+
return fail3(`the recovered answer is not visible on ${surface} after posting`);
|
|
7209
7341
|
progress(` recovery: posted the answer recovered from the run transcript on ${surface}.
|
|
7210
7342
|
`);
|
|
7211
7343
|
return { analysis: verified2, posted: true };
|
|
7212
7344
|
} catch (err) {
|
|
7213
|
-
return fail3(
|
|
7345
|
+
return fail3(
|
|
7346
|
+
`could not re-read ${surface} to verify the recovered answer: ${err.message}`
|
|
7347
|
+
);
|
|
7214
7348
|
}
|
|
7215
7349
|
}
|
|
7216
7350
|
function reportUnverified(item, marker) {
|
|
@@ -7230,6 +7364,13 @@ function reportUnverified(item, marker) {
|
|
|
7230
7364
|
reason: "unverified"
|
|
7231
7365
|
};
|
|
7232
7366
|
}
|
|
7367
|
+
function warnMarkerNotUpdated(surface, err, what) {
|
|
7368
|
+
progress(
|
|
7369
|
+
` warning: could not update the marker comment on ${surface}: ${err.message}
|
|
7370
|
+
the humans have not been told ${what}.
|
|
7371
|
+
`
|
|
7372
|
+
);
|
|
7373
|
+
}
|
|
7233
7374
|
async function reportNoAnswer(item, marker, runError, diagnostics, recoveryNote = null) {
|
|
7234
7375
|
const surface = markerSurfaceLabel(item);
|
|
7235
7376
|
const sideEffects = item.turn === "issue-discuss" ? "It may still have created a branch or opened a pull request \u2014 check before assuming otherwise." : `It may still have changed the branch \`${item.branch}\` \u2014 check it before assuming otherwise.`;
|
|
@@ -7238,13 +7379,13 @@ async function reportNoAnswer(item, marker, runError, diagnostics, recoveryNote
|
|
|
7238
7379
|
try {
|
|
7239
7380
|
updateMarker(marker, explanation + recovery + (await diagnostics?.() ?? ""));
|
|
7240
7381
|
} catch (err) {
|
|
7241
|
-
|
|
7242
|
-
` warning: could not update the marker comment on ${surface}: ${err.message}
|
|
7243
|
-
the humans have not been told that this run produced no answer.
|
|
7244
|
-
`
|
|
7245
|
-
);
|
|
7382
|
+
warnMarkerNotUpdated(surface, err, "that this run produced no answer");
|
|
7246
7383
|
}
|
|
7247
|
-
return runError === null ? {
|
|
7384
|
+
return runError === null ? {
|
|
7385
|
+
outcome: "answered-no-reply",
|
|
7386
|
+
detail: "run finished but posted no answer",
|
|
7387
|
+
reason: "no-answer"
|
|
7388
|
+
} : { outcome: "failed", detail: `run failed: ${runError.message}`, reason: "no-answer" };
|
|
7248
7389
|
}
|
|
7249
7390
|
async function invokeExecutor(prompt, execution, silent, sink2) {
|
|
7250
7391
|
beat({
|
|
@@ -7256,7 +7397,12 @@ async function invokeExecutor(prompt, execution, silent, sink2) {
|
|
|
7256
7397
|
await runCodex(prompt, { model: execution.model, effort: execution.effort, sink: sink2 });
|
|
7257
7398
|
return;
|
|
7258
7399
|
}
|
|
7259
|
-
await runClaude(prompt, {
|
|
7400
|
+
await runClaude(prompt, {
|
|
7401
|
+
model: execution.model,
|
|
7402
|
+
effort: execution.effort,
|
|
7403
|
+
printSteps: !silent,
|
|
7404
|
+
sink: sink2
|
|
7405
|
+
});
|
|
7260
7406
|
}
|
|
7261
7407
|
function repairIssueLink(item, baseBranch, agentUser) {
|
|
7262
7408
|
const issue = item.issue;
|
|
@@ -7264,8 +7410,10 @@ function repairIssueLink(item, baseBranch, agentUser) {
|
|
|
7264
7410
|
try {
|
|
7265
7411
|
const branch = getCurrentBranch();
|
|
7266
7412
|
if (branch === baseBranch) {
|
|
7267
|
-
progress(
|
|
7268
|
-
`)
|
|
7413
|
+
progress(
|
|
7414
|
+
` issue #${String(issue.number)} is still in discussion (no branch was created).
|
|
7415
|
+
`
|
|
7416
|
+
);
|
|
7269
7417
|
return false;
|
|
7270
7418
|
}
|
|
7271
7419
|
const pr = getCurrentBranchPr();
|
|
@@ -7277,10 +7425,15 @@ function repairIssueLink(item, baseBranch, agentUser) {
|
|
|
7277
7425
|
if (pr.assignees.length === 0) {
|
|
7278
7426
|
claimPr(pr.number, agentUser);
|
|
7279
7427
|
}
|
|
7280
|
-
const closesRef = new RegExp(
|
|
7428
|
+
const closesRef = new RegExp(
|
|
7429
|
+
String.raw`\bcloses\s+#` + String(issue.number) + String.raw`\b`,
|
|
7430
|
+
"i"
|
|
7431
|
+
);
|
|
7281
7432
|
if (closesRef.test(pr.body)) {
|
|
7282
|
-
progress(
|
|
7283
|
-
`)
|
|
7433
|
+
progress(
|
|
7434
|
+
` pull request #${String(pr.number)} already closes issue #${String(issue.number)}.
|
|
7435
|
+
`
|
|
7436
|
+
);
|
|
7284
7437
|
return true;
|
|
7285
7438
|
}
|
|
7286
7439
|
addClosesRefToPr(pr.number, issue.number);
|
|
@@ -7288,8 +7441,10 @@ function repairIssueLink(item, baseBranch, agentUser) {
|
|
|
7288
7441
|
`);
|
|
7289
7442
|
return true;
|
|
7290
7443
|
} catch (err) {
|
|
7291
|
-
progress(
|
|
7292
|
-
`)
|
|
7444
|
+
progress(
|
|
7445
|
+
` warning: could not link a pull request to issue #${String(issue.number)}: ${err.message}
|
|
7446
|
+
`
|
|
7447
|
+
);
|
|
7293
7448
|
return false;
|
|
7294
7449
|
}
|
|
7295
7450
|
}
|
|
@@ -7378,8 +7533,10 @@ ${itemLabel(planned)} ${planned.turn}: ${planned.reason}
|
|
|
7378
7533
|
}
|
|
7379
7534
|
let buriedByNote = 0;
|
|
7380
7535
|
if (note !== null) {
|
|
7381
|
-
progress(
|
|
7382
|
-
`)
|
|
7536
|
+
progress(
|
|
7537
|
+
` noted on issue #${String(item.issue?.number ?? 0)} that the work is on pull request #${String(item.pr?.number ?? 0)}.
|
|
7538
|
+
`
|
|
7539
|
+
);
|
|
7383
7540
|
buriedByNote = reportIssueMessagesBuriedByNote(item, note, settings.participants);
|
|
7384
7541
|
}
|
|
7385
7542
|
const watermark = promptWatermark([
|
|
@@ -7414,43 +7571,64 @@ ${itemLabel(planned)} ${planned.turn}: ${planned.reason}
|
|
|
7414
7571
|
);
|
|
7415
7572
|
}
|
|
7416
7573
|
const execution = toExecution(resolved);
|
|
7417
|
-
const { runError, diagnostics, recovery } = await runRecorded(
|
|
7574
|
+
const { runError, diagnostics, recovery, claudeAuth } = await runRecorded(
|
|
7575
|
+
item,
|
|
7576
|
+
prompt,
|
|
7577
|
+
execution,
|
|
7578
|
+
settings,
|
|
7579
|
+
silent
|
|
7580
|
+
);
|
|
7418
7581
|
const ranExecutor = true;
|
|
7419
7582
|
let reconciled;
|
|
7420
7583
|
try {
|
|
7421
|
-
reconciled = await reconcileMarker(
|
|
7422
|
-
item,
|
|
7423
|
-
marker,
|
|
7424
|
-
settings.participants,
|
|
7425
|
-
watermark,
|
|
7584
|
+
reconciled = await reconcileMarker(item, marker, settings.participants, watermark, {
|
|
7426
7585
|
runError,
|
|
7427
7586
|
diagnostics,
|
|
7428
|
-
recovery
|
|
7429
|
-
|
|
7587
|
+
recovery,
|
|
7588
|
+
claudeAuth
|
|
7589
|
+
});
|
|
7430
7590
|
} finally {
|
|
7431
7591
|
inFlightMarker = null;
|
|
7432
7592
|
}
|
|
7433
7593
|
progress(` ${reconciled.detail}
|
|
7434
7594
|
`);
|
|
7435
7595
|
const outcome = adjustOutcome(reconciled, item, settings, buriedByNote);
|
|
7436
|
-
return {
|
|
7596
|
+
return {
|
|
7597
|
+
...base,
|
|
7598
|
+
outcome: outcome.outcome,
|
|
7599
|
+
detail: outcome.detail,
|
|
7600
|
+
ranExecutor,
|
|
7601
|
+
execution,
|
|
7602
|
+
claudeAuth: outcome.claudeAuth
|
|
7603
|
+
};
|
|
7437
7604
|
}
|
|
7438
7605
|
async function runRecorded(item, prompt, execution, settings, silent) {
|
|
7439
7606
|
const transcript = settings.postRunLog ? new RunTranscript(transcriptLabel(item)) : null;
|
|
7440
7607
|
const before = transcript === null ? null : snapshotCheckout();
|
|
7608
|
+
const authTail = transcript === null && execution.executor === "claude" ? new ClaudeAuthTail() : null;
|
|
7441
7609
|
let runError = null;
|
|
7442
7610
|
try {
|
|
7443
|
-
await invokeExecutor(prompt, execution, silent, transcript ?? void 0);
|
|
7611
|
+
await invokeExecutor(prompt, execution, silent, transcript ?? authTail ?? void 0);
|
|
7444
7612
|
} catch (err) {
|
|
7445
7613
|
runError = err;
|
|
7446
7614
|
}
|
|
7447
|
-
if (transcript === null)
|
|
7615
|
+
if (transcript === null) {
|
|
7616
|
+
const claudeAuth2 = runError !== null && authTail !== null && detectClaudeAuthFailure(authTail.tail) ? "" : null;
|
|
7617
|
+
return { runError, diagnostics: null, recovery: null, claudeAuth: claudeAuth2 };
|
|
7618
|
+
}
|
|
7619
|
+
const tail = transcript.excerpt();
|
|
7620
|
+
const claudeAuth = runError !== null && execution.executor === "claude" && detectClaudeAuthFailure(tail) ? tail : null;
|
|
7448
7621
|
return {
|
|
7449
7622
|
runError,
|
|
7623
|
+
claudeAuth,
|
|
7450
7624
|
recovery: async () => {
|
|
7451
7625
|
const text = (transcript.saved ? readTranscriptTail(transcript.path) : null) ?? transcript.excerpt();
|
|
7452
7626
|
if (text.trim() === "") return { ok: false, reason: "the run transcript is empty" };
|
|
7453
|
-
return recoverAnswer(execution, {
|
|
7627
|
+
return recoverAnswer(execution, {
|
|
7628
|
+
subject: markerSurfaceLabel(item),
|
|
7629
|
+
transcript: text,
|
|
7630
|
+
conversation: prompt
|
|
7631
|
+
});
|
|
7454
7632
|
},
|
|
7455
7633
|
diagnostics: async () => {
|
|
7456
7634
|
const excerpt = transcript.excerpt();
|
|
@@ -7530,8 +7708,10 @@ function summarize(reports) {
|
|
|
7530
7708
|
}
|
|
7531
7709
|
for (const report of reports) {
|
|
7532
7710
|
const ran = report.execution === void 0 ? "" : ` \xB7 ${describeExecution(report.execution)}`;
|
|
7533
|
-
out(
|
|
7534
|
-
`)
|
|
7711
|
+
out(
|
|
7712
|
+
` ${reportLabel(report)} ${report.turn ?? "-"} ${report.outcome} \u2014 ${report.detail}${ran}
|
|
7713
|
+
`
|
|
7714
|
+
);
|
|
7535
7715
|
}
|
|
7536
7716
|
}
|
|
7537
7717
|
function toItemJson(report) {
|
|
@@ -7553,7 +7733,13 @@ function toItemJson(report) {
|
|
|
7553
7733
|
}
|
|
7554
7734
|
var doWorkCommand = new Command6("do-work").description(
|
|
7555
7735
|
"Run one tick of the autonomous loop: answer the issues whose newest authorized message the agent has not answered, then the open pull requests that close no issue of this repository and have one"
|
|
7556
|
-
).option(
|
|
7736
|
+
).option(
|
|
7737
|
+
"--with <executor>",
|
|
7738
|
+
"Executor to use: claude or codex (default: from config, else claude)"
|
|
7739
|
+
).option(
|
|
7740
|
+
"--model <string>",
|
|
7741
|
+
"Model identifier to pass to the executor, overriding the configured default for it"
|
|
7742
|
+
).option(
|
|
7557
7743
|
"--effort <level>",
|
|
7558
7744
|
"Reasoning effort to pass to the executor, overriding the configured default for it"
|
|
7559
7745
|
).option("--issue <number>", "Restrict the tick to a single issue").option(
|
|
@@ -7619,7 +7805,12 @@ async function runDoWorkTraced(options) {
|
|
|
7619
7805
|
return exitCode2;
|
|
7620
7806
|
}
|
|
7621
7807
|
const { exitCode, reports } = await runTickUnderLock(lock.handle, settings, options);
|
|
7622
|
-
logTick(
|
|
7808
|
+
logTick(
|
|
7809
|
+
reports,
|
|
7810
|
+
exitCode,
|
|
7811
|
+
startedAt,
|
|
7812
|
+
reports.some((report) => report.claudeAuth === true) ? "claude-auth" : void 0
|
|
7813
|
+
);
|
|
7623
7814
|
return exitCode;
|
|
7624
7815
|
}
|
|
7625
7816
|
async function runTickUnderLock(handle, settings, options) {
|
|
@@ -7685,6 +7876,20 @@ function buildSelectionEvidence(settings, issues, linkMap, decisions, verbose) {
|
|
|
7685
7876
|
})) : null
|
|
7686
7877
|
};
|
|
7687
7878
|
}
|
|
7879
|
+
function decideAll(settings, issues, linkMap, policy) {
|
|
7880
|
+
return skipDuplicateHeadBranches([
|
|
7881
|
+
...issues.map(
|
|
7882
|
+
(issue) => decideWork(buildIssueState(issue, linkMap), settings.participants, policy)
|
|
7883
|
+
),
|
|
7884
|
+
...discoverOrphanPrs(settings, linkMap).map(
|
|
7885
|
+
(candidate) => decideOrphanPrWork(
|
|
7886
|
+
{ prSurface: getPrSurface(candidate.pr.number) },
|
|
7887
|
+
settings.participants,
|
|
7888
|
+
policy
|
|
7889
|
+
)
|
|
7890
|
+
)
|
|
7891
|
+
]);
|
|
7892
|
+
}
|
|
7688
7893
|
function collectSelection(settings, verbose) {
|
|
7689
7894
|
const issues = discoverIssues(settings);
|
|
7690
7895
|
const linkMap = getOpenPrLinkMap();
|
|
@@ -7693,12 +7898,7 @@ function collectSelection(settings, verbose) {
|
|
|
7693
7898
|
defaultBranch: linkMap.defaultBranch,
|
|
7694
7899
|
protectedBranches: settings.protectedBranches
|
|
7695
7900
|
};
|
|
7696
|
-
const decisions =
|
|
7697
|
-
...issues.map((issue) => decideWork(buildIssueState(issue, linkMap), settings.participants, policy)),
|
|
7698
|
-
...discoverOrphanPrs(settings, linkMap).map(
|
|
7699
|
-
(candidate) => decideOrphanPrWork({ prSurface: getPrSurface(candidate.pr.number) }, settings.participants, policy)
|
|
7700
|
-
)
|
|
7701
|
-
]);
|
|
7901
|
+
const decisions = decideAll(settings, issues, linkMap, policy);
|
|
7702
7902
|
return buildSelectionEvidence(settings, issues, linkMap, decisions, verbose);
|
|
7703
7903
|
}
|
|
7704
7904
|
function makeSelectionSection(lines, problems, data) {
|
|
@@ -7730,7 +7930,12 @@ function describeIssueEvidence(settings, evidence) {
|
|
|
7730
7930
|
`discovery query: ${settings.technique} = ${settings.discoveryValue}, limit ${String(settings.limit)}${restriction}`
|
|
7731
7931
|
];
|
|
7732
7932
|
if (evidence.issues === null) return lines;
|
|
7733
|
-
lines.push(
|
|
7933
|
+
lines.push(
|
|
7934
|
+
heading(
|
|
7935
|
+
`discovery returned ${String(evidence.issues.length)} issue(s)`,
|
|
7936
|
+
evidence.issues.length
|
|
7937
|
+
)
|
|
7938
|
+
);
|
|
7734
7939
|
for (const issue of evidence.issues) {
|
|
7735
7940
|
lines.push(` #${String(issue.number)} ${issue.title}`);
|
|
7736
7941
|
}
|
|
@@ -7812,11 +8017,11 @@ function selectionData(evidence) {
|
|
|
7812
8017
|
}
|
|
7813
8018
|
function selectionSectionFromEvidence(settings, evidence, verbose) {
|
|
7814
8019
|
if (evidence === null) {
|
|
7815
|
-
return makeSelectionSection(
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7819
|
-
);
|
|
8020
|
+
return makeSelectionSection(["not run: the tick was blocked before discovery"], [], {
|
|
8021
|
+
ran: false,
|
|
8022
|
+
detail: "blocked before discovery",
|
|
8023
|
+
plan: []
|
|
8024
|
+
});
|
|
7820
8025
|
}
|
|
7821
8026
|
const lines = [];
|
|
7822
8027
|
if (verbose && settings !== null) describeSelectionEvidence(settings, evidence, lines);
|
|
@@ -8051,6 +8256,9 @@ function dumpBlocked(options, context) {
|
|
|
8051
8256
|
}
|
|
8052
8257
|
}
|
|
8053
8258
|
function classifyBlocked(hygiene, decisions, items, reports) {
|
|
8259
|
+
if (reports.some((report) => report.claudeAuth === true)) {
|
|
8260
|
+
return { reason: "claude-auth", trigger: `${CLAUDE_AUTH_REASON}; run \`claude /login\` again` };
|
|
8261
|
+
}
|
|
8054
8262
|
const ran = reports.some((report) => report.ranExecutor === true);
|
|
8055
8263
|
if (ran) return null;
|
|
8056
8264
|
if (hygiene.degraded) {
|
|
@@ -8168,12 +8376,7 @@ async function runTick(settings, options) {
|
|
|
8168
8376
|
defaultBranch: linkMap.defaultBranch,
|
|
8169
8377
|
protectedBranches: settings.protectedBranches
|
|
8170
8378
|
};
|
|
8171
|
-
const decisions =
|
|
8172
|
-
...issues.map((issue) => decideWork(buildIssueState(issue, linkMap), settings.participants, policy)),
|
|
8173
|
-
...discoverOrphanPrs(settings, linkMap).map(
|
|
8174
|
-
(candidate) => decideOrphanPrWork({ prSurface: getPrSurface(candidate.pr.number) }, settings.participants, policy)
|
|
8175
|
-
)
|
|
8176
|
-
]);
|
|
8379
|
+
const decisions = decideAll(settings, issues, linkMap, policy);
|
|
8177
8380
|
const items = decisions.flatMap((decision) => decision.kind === "work" ? [decision.item] : []);
|
|
8178
8381
|
const planText = `Work plan (${String(items.length)} of ${String(decisions.length)} candidates need an answer):
|
|
8179
8382
|
${describePlan(decisions)}`;
|
|
@@ -8222,9 +8425,23 @@ async function processItems(items, settings, options, hygiene) {
|
|
|
8222
8425
|
const report = await reportForItem(item, settings, options, preflightCauses);
|
|
8223
8426
|
if (report.ranExecutor === true) runsUsed++;
|
|
8224
8427
|
reports.push(report);
|
|
8428
|
+
if (report.claudeAuth === true) {
|
|
8429
|
+
reports.push(...items.slice(index + 1).map((rest) => skipForClaudeAuth(rest)));
|
|
8430
|
+
break;
|
|
8431
|
+
}
|
|
8225
8432
|
}
|
|
8226
8433
|
return { reports, deferred };
|
|
8227
8434
|
}
|
|
8435
|
+
function skipForClaudeAuth(item) {
|
|
8436
|
+
return {
|
|
8437
|
+
issue: item.issue?.number ?? null,
|
|
8438
|
+
pr: item.pr?.number ?? null,
|
|
8439
|
+
title: itemTitle(item),
|
|
8440
|
+
turn: item.turn,
|
|
8441
|
+
outcome: "skipped",
|
|
8442
|
+
detail: `${CLAUDE_AUTH_REASON}; run \`claude /login\` again`
|
|
8443
|
+
};
|
|
8444
|
+
}
|
|
8228
8445
|
async function reportForItem(item, settings, options, preflightCauses) {
|
|
8229
8446
|
try {
|
|
8230
8447
|
return await processItem(item, settings, options.silent === true, preflightCauses);
|