@gethmy/agent 1.30.0 → 1.31.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/cli.js +283 -130
- package/dist/index.js +283 -130
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -834,7 +834,7 @@ function isDaemonAuthoredComment(comment, identity) {
|
|
|
834
834
|
return true;
|
|
835
835
|
}
|
|
836
836
|
// ../harmony-shared/dist/agentStaleness.js
|
|
837
|
-
var AGENT_HEARTBEAT_LIVENESS_MS, AGENT_MILESTONE_LIVENESS_MS, AGENT_SWEEP_DAEMON_MS, AGENT_SWEEP_INTERACTIVE_MS, AGENT_SWEEP_PAUSED_MS, SWEPT_SESSION_WRITE_GRACE_MS, ACTIVE_STATUSES;
|
|
837
|
+
var AGENT_HEARTBEAT_LIVENESS_MS, AGENT_MILESTONE_LIVENESS_MS, AGENT_SWEEP_DAEMON_MS, AGENT_SWEEP_INTERACTIVE_MS, AGENT_SWEEP_PAUSED_MS, SWEPT_SESSION_WRITE_GRACE_MS, ACTIVE_STATUSES, NOTICE_DRIVER = "notice";
|
|
838
838
|
var init_agentStaleness = __esm(() => {
|
|
839
839
|
AGENT_HEARTBEAT_LIVENESS_MS = 5 * 60 * 1000;
|
|
840
840
|
AGENT_MILESTONE_LIVENESS_MS = 30 * 60 * 1000;
|
|
@@ -893,6 +893,37 @@ function recordsPushedWorkOn(description, branchName) {
|
|
|
893
893
|
}
|
|
894
894
|
return false;
|
|
895
895
|
}
|
|
896
|
+
function recordedBranchForCard(description, shortId, branchPrefixes) {
|
|
897
|
+
if (!description || !Number.isFinite(shortId))
|
|
898
|
+
return null;
|
|
899
|
+
const anchors = [
|
|
900
|
+
...new Set(branchPrefixes.filter((p) => p.length > 0)),
|
|
901
|
+
""
|
|
902
|
+
];
|
|
903
|
+
let recorded = null;
|
|
904
|
+
for (const match of description.matchAll(DAEMON_BRANCH_LINE_PATTERN)) {
|
|
905
|
+
const ref = match[1];
|
|
906
|
+
if (!ref || !SAFE_GIT_REF_PATTERN.test(ref))
|
|
907
|
+
continue;
|
|
908
|
+
if (anchors.some((p) => ref.startsWith(`${p}${shortId}-`)))
|
|
909
|
+
recorded = ref;
|
|
910
|
+
}
|
|
911
|
+
return recorded;
|
|
912
|
+
}
|
|
913
|
+
function rewriteDaemonBranchLines(description, fromRef, toRef2) {
|
|
914
|
+
if (!description || !fromRef || !toRef2)
|
|
915
|
+
return null;
|
|
916
|
+
if (!SAFE_GIT_REF_PATTERN.test(toRef2))
|
|
917
|
+
return null;
|
|
918
|
+
let matched = false;
|
|
919
|
+
const rewritten = description.replace(DAEMON_BRANCH_LINE_PATTERN, (line, ref) => {
|
|
920
|
+
if (ref !== fromRef)
|
|
921
|
+
return line;
|
|
922
|
+
matched = true;
|
|
923
|
+
return line.replace(`\`${ref}\``, `\`${toRef2}\``);
|
|
924
|
+
});
|
|
925
|
+
return matched ? rewritten : null;
|
|
926
|
+
}
|
|
896
927
|
var BRANCH_REF_PATTERN, DAEMON_BRANCH_LINE_PATTERN, SAFE_GIT_REF_PATTERN, PR_LINK_PATTERN;
|
|
897
928
|
var init_branchRef = __esm(() => {
|
|
898
929
|
BRANCH_REF_PATTERN = /Branch:\s*`([^`]+)`/g;
|
|
@@ -1641,6 +1672,18 @@ function entryActionAllowlist(entryAction) {
|
|
|
1641
1672
|
function stageDisallowedTools() {
|
|
1642
1673
|
return STAGE_DAEMON_OWNED_TOOLS.length > 0 ? STAGE_DAEMON_OWNED_TOOLS.join(",") : null;
|
|
1643
1674
|
}
|
|
1675
|
+
function toolsCanCommit(allowedTools) {
|
|
1676
|
+
return allowedTools.split(",").map((t) => t.trim().replace(/\(.*$/, "")).some((t) => COMMIT_CAPABLE_TOOLS.includes(t));
|
|
1677
|
+
}
|
|
1678
|
+
function stageRunExpectsCommit(stage, allowedTools) {
|
|
1679
|
+
if (!toolsCanCommit(allowedTools))
|
|
1680
|
+
return false;
|
|
1681
|
+
const artifact = stage.artifact_type;
|
|
1682
|
+
if (artifact !== null && artifact !== undefined && NON_DIFF_ARTIFACTS.includes(artifact)) {
|
|
1683
|
+
return false;
|
|
1684
|
+
}
|
|
1685
|
+
return true;
|
|
1686
|
+
}
|
|
1644
1687
|
function customGateMetric(gate) {
|
|
1645
1688
|
if (gate === null || typeof gate !== "object" || Array.isArray(gate)) {
|
|
1646
1689
|
return null;
|
|
@@ -1677,7 +1720,7 @@ function referencedGateMetrics(def) {
|
|
|
1677
1720
|
}
|
|
1678
1721
|
return out;
|
|
1679
1722
|
}
|
|
1680
|
-
var DEFAULT_LOOP_MAX_ITERATIONS = 5, DEFAULT_LOOP_CONCURRENCY = 1, DEFAULT_ON_ITEM_FAIL = "continue", PLAYBOOK_STAGE_ROLES, SKILL_TOOL_ALLOWLIST, HARMONY_TOOL_RE, STAGE_DAEMON_OWNED_TOOLS;
|
|
1723
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5, DEFAULT_LOOP_CONCURRENCY = 1, DEFAULT_ON_ITEM_FAIL = "continue", PLAYBOOK_STAGE_ROLES, SKILL_TOOL_ALLOWLIST, HARMONY_TOOL_RE, STAGE_DAEMON_OWNED_TOOLS, COMMIT_CAPABLE_TOOLS, NON_DIFF_ARTIFACTS;
|
|
1681
1724
|
var init_playbookStage = __esm(() => {
|
|
1682
1725
|
PLAYBOOK_STAGE_ROLES = [
|
|
1683
1726
|
"author",
|
|
@@ -1698,11 +1741,20 @@ var init_playbookStage = __esm(() => {
|
|
|
1698
1741
|
"mcp__harmony__harmony_start_agent_session",
|
|
1699
1742
|
"mcp__harmony__harmony_move_card"
|
|
1700
1743
|
];
|
|
1744
|
+
COMMIT_CAPABLE_TOOLS = ["Bash", "Write", "Edit"];
|
|
1745
|
+
NON_DIFF_ARTIFACTS = ["plan", "review", "document", "decision"];
|
|
1701
1746
|
});
|
|
1702
1747
|
|
|
1703
1748
|
// ../harmony-shared/dist/projectTemplates.js
|
|
1704
1749
|
var init_projectTemplates = () => {};
|
|
1705
1750
|
|
|
1751
|
+
// ../harmony-shared/dist/realtimeChannel.js
|
|
1752
|
+
var inFlightDetach;
|
|
1753
|
+
var init_realtimeChannel = __esm(() => {
|
|
1754
|
+
init_logger();
|
|
1755
|
+
inFlightDetach = new WeakMap;
|
|
1756
|
+
});
|
|
1757
|
+
|
|
1706
1758
|
// ../harmony-shared/dist/reviewMethodology.js
|
|
1707
1759
|
var REVIEW_SYSTEM_PROMPT = `You are a senior code reviewer. Follow this two-pass methodology strictly.
|
|
1708
1760
|
Report findings; do NOT fix them. This is a read-only review.
|
|
@@ -2023,6 +2075,7 @@ var init_dist = __esm(() => {
|
|
|
2023
2075
|
init_playbookCatalog();
|
|
2024
2076
|
init_playbookStage();
|
|
2025
2077
|
init_projectTemplates();
|
|
2078
|
+
init_realtimeChannel();
|
|
2026
2079
|
init_reviewTools();
|
|
2027
2080
|
init_stageHandoff();
|
|
2028
2081
|
init_types();
|
|
@@ -2331,12 +2384,12 @@ var init_plan_phase = __esm(() => {
|
|
|
2331
2384
|
|
|
2332
2385
|
// src/types.ts
|
|
2333
2386
|
function agentIdentifier(workerId) {
|
|
2334
|
-
return
|
|
2387
|
+
return `${DAEMON_IDENTIFIER_BASE}-${workerId}`;
|
|
2335
2388
|
}
|
|
2336
2389
|
function endStatusForCancel(reason) {
|
|
2337
2390
|
return reason === "human_stop" ? "cancelled" : "paused";
|
|
2338
2391
|
}
|
|
2339
|
-
var DEFAULT_AGENT_CONFIG, IN_PROGRESS_COLUMN = "In Progress", NEED_REVIEW_LABEL = "Need Review", NEED_REVIEW_LABEL_COLOR = "#f59e0b", AGENT_NAME = "Harmony Agent";
|
|
2392
|
+
var DEFAULT_AGENT_CONFIG, IN_PROGRESS_COLUMN = "In Progress", NEED_REVIEW_LABEL = "Need Review", NEED_REVIEW_LABEL_COLOR = "#f59e0b", AGENT_NAME = "Harmony Agent", DAEMON_IDENTIFIER_BASE = "harmony-daemon", NOTICE_IDENTIFIER;
|
|
2340
2393
|
var init_types2 = __esm(() => {
|
|
2341
2394
|
init_board_review();
|
|
2342
2395
|
init_contract_phase();
|
|
@@ -2468,6 +2521,7 @@ var init_types2 = __esm(() => {
|
|
|
2468
2521
|
maxCardsPerSweep: 10
|
|
2469
2522
|
}
|
|
2470
2523
|
};
|
|
2524
|
+
NOTICE_IDENTIFIER = `${DAEMON_IDENTIFIER_BASE}-notice`;
|
|
2471
2525
|
});
|
|
2472
2526
|
|
|
2473
2527
|
// src/config.ts
|
|
@@ -5103,6 +5157,7 @@ import {
|
|
|
5103
5157
|
captureDiffStat,
|
|
5104
5158
|
createPullRequest,
|
|
5105
5159
|
detectGitProvider as detectGitProvider3,
|
|
5160
|
+
extractPrUrl as extractPrUrl2,
|
|
5106
5161
|
getBranchWebUrl,
|
|
5107
5162
|
log as log16,
|
|
5108
5163
|
pushBranch,
|
|
@@ -5141,7 +5196,7 @@ function buildTokenPayload(stats) {
|
|
|
5141
5196
|
numTurns: stats.cost.numTurns
|
|
5142
5197
|
};
|
|
5143
5198
|
}
|
|
5144
|
-
async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionIdentifier, agentId, sessionStats, workspaceId, agentSessionId, stateStore, onMovedToCompletion, onBeforeWorktreeCleanup, runBaselineSha, effectiveMaxTurns) {
|
|
5199
|
+
async function runCompletion(client, card, branchName, worktreePath, config, workerId, sessionIdentifier, agentId, sessionStats, workspaceId, agentSessionId, stateStore, onMovedToCompletion, onBeforeWorktreeCleanup, runBaselineSha, effectiveMaxTurns, expectsCommits) {
|
|
5145
5200
|
let verificationResult = {
|
|
5146
5201
|
passed: true,
|
|
5147
5202
|
buildErrors: [],
|
|
@@ -5161,6 +5216,20 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
5161
5216
|
log16.warn(TAG15, `No commits on branch ${branchName} — ${failureSummary}; parking for a decision`);
|
|
5162
5217
|
return "park";
|
|
5163
5218
|
}
|
|
5219
|
+
if (expectsCommits === false) {
|
|
5220
|
+
log16.info(TAG15, `No commits on branch ${branchName} — this stage's deliverable is not a diff; its gate decides the run`);
|
|
5221
|
+
if (onBeforeWorktreeCleanup) {
|
|
5222
|
+
try {
|
|
5223
|
+
await onBeforeWorktreeCleanup(worktreePath);
|
|
5224
|
+
} catch (err) {
|
|
5225
|
+
log16.warn(TAG15, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
5226
|
+
}
|
|
5227
|
+
} else {
|
|
5228
|
+
await endRunSession({ client, tag: TAG15 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
|
|
5229
|
+
}
|
|
5230
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
5231
|
+
return true;
|
|
5232
|
+
}
|
|
5164
5233
|
log16.warn(TAG15, `No commits on branch ${branchName} — ${failureSummary}; counting as a failed attempt`);
|
|
5165
5234
|
const noCommitHandback = await guardedHandback(client, card.id, {
|
|
5166
5235
|
agentId,
|
|
@@ -5187,6 +5256,9 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
5187
5256
|
log16.error(TAG15, `pre-verify push failed for ${branchName}: ${err instanceof Error ? err.message : err}`);
|
|
5188
5257
|
}
|
|
5189
5258
|
const recoveryUrl = lastPushedSha ? getBranchWebUrl(branchName, worktreePath) : null;
|
|
5259
|
+
if (lastPushedSha) {
|
|
5260
|
+
await recordBranchProvenance(client, card, branchName);
|
|
5261
|
+
}
|
|
5190
5262
|
if (config.verification.enabled) {
|
|
5191
5263
|
await client.updateAgentProgress(card.id, {
|
|
5192
5264
|
agentIdentifier: sessionIdentifier,
|
|
@@ -5283,17 +5355,16 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
5283
5355
|
if (config.completion.postSummary) {
|
|
5284
5356
|
await postSummary(client, card, branchName, worktreePath, prUrl, config.worktree.baseBranch, sessionStats);
|
|
5285
5357
|
}
|
|
5286
|
-
let endDisposition = { status: "completed" };
|
|
5287
5358
|
if (onBeforeWorktreeCleanup) {
|
|
5288
5359
|
try {
|
|
5289
|
-
|
|
5290
|
-
if (disposition)
|
|
5291
|
-
endDisposition = disposition;
|
|
5360
|
+
await onBeforeWorktreeCleanup(worktreePath);
|
|
5292
5361
|
} catch (err) {
|
|
5293
5362
|
log16.warn(TAG15, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
5294
5363
|
}
|
|
5295
5364
|
}
|
|
5296
|
-
|
|
5365
|
+
if (!onBeforeWorktreeCleanup) {
|
|
5366
|
+
await endRunSession({ client, tag: TAG15 }, card, { status: "completed" }, buildTokenPayload(sessionStats), "throw");
|
|
5367
|
+
}
|
|
5297
5368
|
if (workspaceId) {
|
|
5298
5369
|
const diffStat = captureDiffStat(worktreePath, config.worktree.baseBranch);
|
|
5299
5370
|
const changedFiles = diffStat && diffStat.files.length > 0 ? diffStat.files : sessionStats?.filesEditedPaths ?? [];
|
|
@@ -5397,18 +5468,47 @@ function checkHasCommits(worktreePath, baseBranch, baselineSha, gitImpl = (args,
|
|
|
5397
5468
|
return false;
|
|
5398
5469
|
}
|
|
5399
5470
|
}
|
|
5471
|
+
function stripDaemonBlocks(description) {
|
|
5472
|
+
const indices = [SUMMARY_MARKER, BRANCH_PROVENANCE_MARKER].map((marker) => description.indexOf(marker)).filter((index) => index >= 0);
|
|
5473
|
+
if (indices.length === 0)
|
|
5474
|
+
return description;
|
|
5475
|
+
return description.slice(0, Math.min(...indices)).trimEnd();
|
|
5476
|
+
}
|
|
5477
|
+
async function recordBranchProvenance(client, card, branchName) {
|
|
5478
|
+
try {
|
|
5479
|
+
let description = card.description || "";
|
|
5480
|
+
try {
|
|
5481
|
+
const { card: latest } = await client.getCard(card.id);
|
|
5482
|
+
description = latest.description ?? description;
|
|
5483
|
+
} catch {}
|
|
5484
|
+
if (recordsPushedWorkOn(description, branchName))
|
|
5485
|
+
return;
|
|
5486
|
+
const block = `
|
|
5487
|
+
|
|
5488
|
+
${BRANCH_PROVENANCE_MARKER} (pushed — a later run continues it)
|
|
5489
|
+
Branch: \`${branchName}\``;
|
|
5490
|
+
await client.updateCard(card.id, { description: description + block });
|
|
5491
|
+
log16.info(TAG15, `Recorded branch provenance on #${card.short_id}`);
|
|
5492
|
+
} catch (err) {
|
|
5493
|
+
log16.warn(TAG15, `Failed to record branch provenance on #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
5494
|
+
}
|
|
5495
|
+
}
|
|
5400
5496
|
async function postSummary(client, card, branchName, worktreePath, prUrl, baseBranch, sessionStats) {
|
|
5401
5497
|
let commitLog = "";
|
|
5402
5498
|
try {
|
|
5403
5499
|
commitLog = execFileSync5("git", ["log", "--oneline", `origin/${baseBranch}..HEAD`], { cwd: worktreePath, encoding: "utf-8" }).trim();
|
|
5404
5500
|
} catch {}
|
|
5405
|
-
|
|
5406
|
-
|
|
5501
|
+
let existingDesc = card.description || "";
|
|
5502
|
+
try {
|
|
5503
|
+
const { card: latest } = await client.getCard(card.id);
|
|
5504
|
+
existingDesc = latest.description ?? existingDesc;
|
|
5505
|
+
} catch {}
|
|
5506
|
+
const carriedPrUrl = prUrl ?? extractPrUrl2(existingDesc);
|
|
5407
5507
|
const parts = [`
|
|
5408
5508
|
|
|
5409
5509
|
${SUMMARY_MARKER}`];
|
|
5410
|
-
if (
|
|
5411
|
-
parts.push(`PR: ${
|
|
5510
|
+
if (carriedPrUrl) {
|
|
5511
|
+
parts.push(`PR: ${carriedPrUrl}`);
|
|
5412
5512
|
}
|
|
5413
5513
|
parts.push(`Branch: \`${branchName}\``);
|
|
5414
5514
|
if (sessionStats) {
|
|
@@ -5439,8 +5539,7 @@ ${commitLog}
|
|
|
5439
5539
|
\`\`\``);
|
|
5440
5540
|
}
|
|
5441
5541
|
try {
|
|
5442
|
-
const
|
|
5443
|
-
const baseDesc = existingDesc.includes(SUMMARY_MARKER) ? existingDesc.slice(0, existingDesc.indexOf(SUMMARY_MARKER)).trimEnd() : existingDesc;
|
|
5542
|
+
const baseDesc = stripDaemonBlocks(existingDesc);
|
|
5444
5543
|
await client.updateCard(card.id, {
|
|
5445
5544
|
description: baseDesc + parts.join(`
|
|
5446
5545
|
`)
|
|
@@ -5450,8 +5549,11 @@ ${commitLog}
|
|
|
5450
5549
|
log16.error(TAG15, `Failed to post summary: ${err instanceof Error ? err.message : err}`);
|
|
5451
5550
|
}
|
|
5452
5551
|
}
|
|
5453
|
-
var TAG15 = "completion"
|
|
5552
|
+
var TAG15 = "completion", SUMMARY_MARKER = `---
|
|
5553
|
+
**Agent completed**`, BRANCH_PROVENANCE_MARKER = `---
|
|
5554
|
+
**Agent branch**`;
|
|
5454
5555
|
var init_completion = __esm(() => {
|
|
5556
|
+
init_dist();
|
|
5455
5557
|
init_board_helpers();
|
|
5456
5558
|
init_episode_writer();
|
|
5457
5559
|
init_handback();
|
|
@@ -5838,6 +5940,9 @@ var init_progress_tracker = __esm(() => {
|
|
|
5838
5940
|
|
|
5839
5941
|
// src/prompt.ts
|
|
5840
5942
|
import { log as log18 } from "@gethmy/harness";
|
|
5943
|
+
import {
|
|
5944
|
+
buildMemoryQuery
|
|
5945
|
+
} from "@gethmy/mcp/src/api-client.js";
|
|
5841
5946
|
function buildSteeringPrompt(messages) {
|
|
5842
5947
|
if (messages.length === 1)
|
|
5843
5948
|
return messages[0];
|
|
@@ -5870,11 +5975,11 @@ function renderPreviousAttemptsSection(failures) {
|
|
|
5870
5975
|
].join(`
|
|
5871
5976
|
`);
|
|
5872
5977
|
}
|
|
5873
|
-
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId) {
|
|
5978
|
+
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId, onRecallOutcome) {
|
|
5874
5979
|
const { card } = enriched;
|
|
5875
5980
|
const [pastEpisodesSection, referenceSection] = await Promise.all([
|
|
5876
|
-
renderPastEpisodesSection(client, card.title, card.description ?? "", workspaceId, projectId),
|
|
5877
|
-
renderReferenceSection(client, card.title, card.description ?? "", workspaceId, projectId)
|
|
5981
|
+
renderPastEpisodesSection(client, card.title, card.description ?? "", workspaceId, projectId, onRecallOutcome),
|
|
5982
|
+
renderReferenceSection(client, card.title, card.description ?? "", workspaceId, projectId, onRecallOutcome)
|
|
5878
5983
|
]);
|
|
5879
5984
|
try {
|
|
5880
5985
|
const result = await client.generateCardPrompt({
|
|
@@ -5915,12 +6020,11 @@ ${section}` : "";
|
|
|
5915
6020
|
return "";
|
|
5916
6021
|
}
|
|
5917
6022
|
}
|
|
5918
|
-
async function renderPastEpisodesSection(client, title, description, workspaceId, projectId) {
|
|
6023
|
+
async function renderPastEpisodesSection(client, title, description, workspaceId, projectId, onOutcome) {
|
|
5919
6024
|
if (!projectId)
|
|
5920
6025
|
return "";
|
|
5921
6026
|
try {
|
|
5922
|
-
const query =
|
|
5923
|
-
${description}`.trim();
|
|
6027
|
+
const query = buildMemoryQuery(title, description);
|
|
5924
6028
|
const { entities } = await client.harmonyRecall({
|
|
5925
6029
|
workspaceId,
|
|
5926
6030
|
projectId,
|
|
@@ -5932,6 +6036,7 @@ ${description}`.trim();
|
|
|
5932
6036
|
includeEpisodes: true,
|
|
5933
6037
|
consumer: "agent-prompt"
|
|
5934
6038
|
});
|
|
6039
|
+
onOutcome?.({ flavour: "past_episodes", count: entities.length });
|
|
5935
6040
|
if (entities.length === 0)
|
|
5936
6041
|
return "";
|
|
5937
6042
|
const bullets = entities.map((entity) => {
|
|
@@ -5960,17 +6065,18 @@ ${description}`.trim();
|
|
|
5960
6065
|
## Similar past tasks
|
|
5961
6066
|
${bullets}`;
|
|
5962
6067
|
} catch (err) {
|
|
6068
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
5963
6069
|
log18.warn(TAG17, "past-episodes recall failed", {
|
|
5964
6070
|
event: "episode_recall_failed",
|
|
5965
|
-
error
|
|
6071
|
+
error
|
|
5966
6072
|
});
|
|
6073
|
+
onOutcome?.({ flavour: "past_episodes", count: 0, failure: error });
|
|
5967
6074
|
return "";
|
|
5968
6075
|
}
|
|
5969
6076
|
}
|
|
5970
|
-
async function renderReferenceSection(client, title, description, workspaceId, projectId) {
|
|
6077
|
+
async function renderReferenceSection(client, title, description, workspaceId, projectId, onOutcome) {
|
|
5971
6078
|
try {
|
|
5972
|
-
const query =
|
|
5973
|
-
${description}`.trim();
|
|
6079
|
+
const query = buildMemoryQuery(title, description);
|
|
5974
6080
|
const { entities } = await client.harmonyRecall({
|
|
5975
6081
|
workspaceId,
|
|
5976
6082
|
projectId,
|
|
@@ -5979,6 +6085,7 @@ ${description}`.trim();
|
|
|
5979
6085
|
topK: 5,
|
|
5980
6086
|
consumer: "agent-prompt"
|
|
5981
6087
|
});
|
|
6088
|
+
onOutcome?.({ flavour: "reference", count: entities.length });
|
|
5982
6089
|
if (entities.length === 0)
|
|
5983
6090
|
return "";
|
|
5984
6091
|
const bullets = entities.map((entity) => {
|
|
@@ -5993,10 +6100,12 @@ ${description}`.trim();
|
|
|
5993
6100
|
## How we work here
|
|
5994
6101
|
${bullets}`;
|
|
5995
6102
|
} catch (err) {
|
|
6103
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
5996
6104
|
log18.warn(TAG17, "reference recall failed", {
|
|
5997
6105
|
event: "reference_recall_failed",
|
|
5998
|
-
error
|
|
6106
|
+
error
|
|
5999
6107
|
});
|
|
6108
|
+
onOutcome?.({ flavour: "reference", count: 0, failure: error });
|
|
6000
6109
|
return "";
|
|
6001
6110
|
}
|
|
6002
6111
|
}
|
|
@@ -6056,7 +6165,7 @@ import {
|
|
|
6056
6165
|
cleanupWorktree as cleanupWorktree3,
|
|
6057
6166
|
createPullRequest as createPullRequest2,
|
|
6058
6167
|
detectGitProvider as detectGitProvider4,
|
|
6059
|
-
extractPrUrl as
|
|
6168
|
+
extractPrUrl as extractPrUrl3,
|
|
6060
6169
|
getBranchWebUrl as getBranchWebUrl2,
|
|
6061
6170
|
getHeadSha,
|
|
6062
6171
|
log as log19,
|
|
@@ -6354,19 +6463,31 @@ ${runLogTail}
|
|
|
6354
6463
|
}
|
|
6355
6464
|
}
|
|
6356
6465
|
await addLabelByName(client, card, config.review.approvedLabel, config.review.approvedLabelColor);
|
|
6357
|
-
|
|
6466
|
+
const renamedFrom = branchName && approvedBranch && approvedBranch !== branchName ? branchName : null;
|
|
6467
|
+
const renamedTo = renamedFrom ? approvedBranch : null;
|
|
6468
|
+
if (prUrl || renamedFrom) {
|
|
6358
6469
|
try {
|
|
6359
6470
|
const { card: latest } = await client.getCard(card.id);
|
|
6360
|
-
|
|
6361
|
-
|
|
6471
|
+
let desc = latest.description || "";
|
|
6472
|
+
let changed = false;
|
|
6473
|
+
if (renamedFrom && renamedTo) {
|
|
6474
|
+
const rewritten = rewriteDaemonBranchLines(desc, renamedFrom, renamedTo);
|
|
6475
|
+
if (rewritten !== null) {
|
|
6476
|
+
desc = rewritten;
|
|
6477
|
+
changed = true;
|
|
6478
|
+
}
|
|
6479
|
+
}
|
|
6480
|
+
if (prUrl && !extractPrUrl3(desc)) {
|
|
6362
6481
|
const separator = desc ? `
|
|
6363
6482
|
` : "";
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6483
|
+
desc = `${desc}${separator}PR: ${prUrl}`;
|
|
6484
|
+
changed = true;
|
|
6485
|
+
}
|
|
6486
|
+
if (changed) {
|
|
6487
|
+
await client.updateCard(card.id, { description: desc });
|
|
6367
6488
|
}
|
|
6368
6489
|
} catch (err) {
|
|
6369
|
-
log19.warn(TAG18, `Failed to persist PR URL to #${card.short_id} description: ${err instanceof Error ? err.message : err}`);
|
|
6490
|
+
log19.warn(TAG18, `Failed to persist PR URL / branch rename to #${card.short_id} description: ${err instanceof Error ? err.message : err}`);
|
|
6370
6491
|
}
|
|
6371
6492
|
}
|
|
6372
6493
|
if (branchName) {
|
|
@@ -6543,6 +6664,7 @@ ${runLogTail}
|
|
|
6543
6664
|
var TAG18 = "review-completion", MAX_FINDINGS = 10, MAX_SUBTASK_TITLE = 120, COMMENT_BODY_BUDGET = 9500, REVIEW_MARKER = `---
|
|
6544
6665
|
**Review:`, RUN_LOG_TAIL_BYTES = 2048;
|
|
6545
6666
|
var init_review_completion = __esm(() => {
|
|
6667
|
+
init_dist();
|
|
6546
6668
|
init_board_helpers();
|
|
6547
6669
|
init_completion();
|
|
6548
6670
|
init_episode_writer();
|
|
@@ -9086,6 +9208,9 @@ var init_motor_driver = () => {};
|
|
|
9086
9208
|
|
|
9087
9209
|
// src/stage-advance.ts
|
|
9088
9210
|
import { gateConfigErrorReason, log as log29 } from "@gethmy/harness";
|
|
9211
|
+
function endDispositionFor(outcome) {
|
|
9212
|
+
return "endDisposition" in outcome && outcome.endDisposition ? outcome.endDisposition : { status: "completed" };
|
|
9213
|
+
}
|
|
9089
9214
|
function handoffText(stage) {
|
|
9090
9215
|
if (stage.handoff && typeof stage.handoff === "object") {
|
|
9091
9216
|
const summary = stage.handoff.summary ?? stage.handoff.description;
|
|
@@ -9094,16 +9219,6 @@ function handoffText(stage) {
|
|
|
9094
9219
|
}
|
|
9095
9220
|
return stage.name;
|
|
9096
9221
|
}
|
|
9097
|
-
function stageEndDisposition(evaluation, _stage, stageIndex, def) {
|
|
9098
|
-
if (!evaluation?.passed)
|
|
9099
|
-
return { status: "completed" };
|
|
9100
|
-
const next = nextStageAfter(def, stageIndex);
|
|
9101
|
-
if (next.kind === "next" && next.stage.owner === "human") {
|
|
9102
|
-
const reason = `Stage "${next.stage.name}" is yours: ${handoffText(next.stage)}`;
|
|
9103
|
-
return { status: "blocked", blockers: [reason] };
|
|
9104
|
-
}
|
|
9105
|
-
return { status: "completed" };
|
|
9106
|
-
}
|
|
9107
9222
|
function gateKindOf(stage) {
|
|
9108
9223
|
return stage.gate && typeof stage.gate === "object" ? String(stage.gate.kind ?? "gate") : "gate";
|
|
9109
9224
|
}
|
|
@@ -9161,9 +9276,9 @@ async function holdGateMisconfigured(card, stage, detail, deps) {
|
|
|
9161
9276
|
currentTask: reason
|
|
9162
9277
|
});
|
|
9163
9278
|
} catch {}
|
|
9164
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9279
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9165
9280
|
log29.info(TAG27, `#${card.short_id} GateMisconfigured: ${reason}`);
|
|
9166
|
-
return { kind: "held_misconfigured", reason };
|
|
9281
|
+
return { kind: "held_misconfigured", reason, endDisposition };
|
|
9167
9282
|
}
|
|
9168
9283
|
function firstErrorMessage(evaluation) {
|
|
9169
9284
|
const e = evaluation?.findings.find((f) => f.level === "error");
|
|
@@ -9232,13 +9347,9 @@ async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loo
|
|
|
9232
9347
|
currentTask: reason
|
|
9233
9348
|
});
|
|
9234
9349
|
} catch {}
|
|
9235
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, {
|
|
9236
|
-
keepAttempts: true,
|
|
9237
|
-
endStatus: "blocked",
|
|
9238
|
-
blockers: [reason]
|
|
9239
|
-
});
|
|
9350
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, { keepAttempts: true, endStatus: "blocked", blockers: [reason] });
|
|
9240
9351
|
log29.info(TAG27, `#${card.short_id} LoopExhausted: ${reason}`);
|
|
9241
|
-
return { kind: "held_gate_unmet", reason };
|
|
9352
|
+
return { kind: "held_gate_unmet", reason, endDisposition };
|
|
9242
9353
|
}
|
|
9243
9354
|
const guard = await guardStageReclaim(card, `converge-loop iteration of "${stage.name}"`, deps);
|
|
9244
9355
|
if (!guard.proceed) {
|
|
@@ -9311,14 +9422,14 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
|
|
|
9311
9422
|
}
|
|
9312
9423
|
if (next.kind === "out_of_range") {
|
|
9313
9424
|
const reason = `Stage advancement aborted: stage index ${stageIndex} is out of range for the pinned playbook version — holding for a human.`;
|
|
9314
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9315
|
-
return { kind: "held_misconfigured", reason };
|
|
9425
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9426
|
+
return { kind: "held_misconfigured", reason, endDisposition };
|
|
9316
9427
|
}
|
|
9317
9428
|
const toColumn = await resolveStageColumnName(deps.client, card, next.stage);
|
|
9318
9429
|
if (!toColumn) {
|
|
9319
9430
|
const reason = `Stage "${stage.name}" passed but the next stage "${next.stage.name}" has no resolvable board column — holding for a human (never moving to an undefined column).`;
|
|
9320
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9321
|
-
return { kind: "held_misconfigured", reason };
|
|
9431
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore);
|
|
9432
|
+
return { kind: "held_misconfigured", reason, endDisposition };
|
|
9322
9433
|
}
|
|
9323
9434
|
await persistStagePointer(deps.client, card, {
|
|
9324
9435
|
currentStage: next.stage.id,
|
|
@@ -9341,11 +9452,13 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
|
|
|
9341
9452
|
log29.info(TAG27, `#${card.short_id} advanced "${stage.name}" → "${next.stage.name}" (column "${toColumn}")`);
|
|
9342
9453
|
if (next.stage.owner === "human") {
|
|
9343
9454
|
const reason = `Stage "${next.stage.name}" is yours: ${handoffText(next.stage)}`;
|
|
9344
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, {
|
|
9345
|
-
|
|
9346
|
-
|
|
9347
|
-
|
|
9348
|
-
|
|
9455
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, { keepAttempts: true, endStatus: "blocked", blockers: [reason] });
|
|
9456
|
+
return {
|
|
9457
|
+
kind: "advanced",
|
|
9458
|
+
toStageId: next.stage.id,
|
|
9459
|
+
toColumn,
|
|
9460
|
+
endDisposition
|
|
9461
|
+
};
|
|
9349
9462
|
}
|
|
9350
9463
|
return { kind: "advanced", toStageId: next.stage.id, toColumn };
|
|
9351
9464
|
}
|
|
@@ -9362,13 +9475,9 @@ async function handleGateUnmet(card, stage, summary, deps) {
|
|
|
9362
9475
|
currentTask: reason
|
|
9363
9476
|
});
|
|
9364
9477
|
} catch {}
|
|
9365
|
-
await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, {
|
|
9366
|
-
keepAttempts: true,
|
|
9367
|
-
endStatus: "blocked",
|
|
9368
|
-
blockers: [reason]
|
|
9369
|
-
});
|
|
9478
|
+
const endDisposition = await holdForHuman(deps.client, card, reason, deps.runId, deps.stateStore, { keepAttempts: true, endStatus: "blocked", blockers: [reason] });
|
|
9370
9479
|
log29.info(TAG27, `#${card.short_id} GateUnmetExhausted: ${reason}`);
|
|
9371
|
-
return { kind: "held_gate_unmet", reason };
|
|
9480
|
+
return { kind: "held_gate_unmet", reason, endDisposition };
|
|
9372
9481
|
}
|
|
9373
9482
|
const guard = await guardStageReclaim(card, `gate-unmet re-run of "${stage.name}"`, deps);
|
|
9374
9483
|
if (!guard.proceed) {
|
|
@@ -9430,21 +9539,16 @@ async function holdForHuman(client, card, reason, runId, stateStore, opts = {})
|
|
|
9430
9539
|
await client.addComment(card.id, reason, { commentType: "blocker" });
|
|
9431
9540
|
} catch {}
|
|
9432
9541
|
try {
|
|
9433
|
-
|
|
9434
|
-
removeLabels: [AGENT_LABEL],
|
|
9435
|
-
endSession: {
|
|
9436
|
-
status: opts.endStatus ?? "paused",
|
|
9437
|
-
blockers: opts.blockers,
|
|
9438
|
-
failureReason: "other",
|
|
9439
|
-
failureSummary: reason.slice(0, 300)
|
|
9440
|
-
}
|
|
9441
|
-
}, { store: stateStore, runId });
|
|
9442
|
-
if (opts.endStatus === "blocked" && result.endSession?.ended === false) {
|
|
9443
|
-
log29.warn(TAG27, `#${card.short_id} hold intended to end the session BLOCKED, but it was already ended (${result.endSession.reason ?? "unknown reason"}) — no agent_blocked push fired from this write.`);
|
|
9444
|
-
}
|
|
9542
|
+
await runTransition(client, card, { removeLabels: [AGENT_LABEL] }, { store: stateStore, runId });
|
|
9445
9543
|
} catch (err) {
|
|
9446
9544
|
log29.warn(TAG27, `hold transition failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
9447
9545
|
}
|
|
9546
|
+
return {
|
|
9547
|
+
status: opts.endStatus ?? "paused",
|
|
9548
|
+
blockers: opts.blockers,
|
|
9549
|
+
failureReason: "other",
|
|
9550
|
+
failureSummary: reason.slice(0, 300)
|
|
9551
|
+
};
|
|
9448
9552
|
}
|
|
9449
9553
|
var TAG27 = "stage-advance", AGENT_LABEL = "agent";
|
|
9450
9554
|
var init_stage_advance = __esm(() => {
|
|
@@ -9464,12 +9568,14 @@ import {
|
|
|
9464
9568
|
collectGateEvidence as collectGateEvidence2,
|
|
9465
9569
|
createWorktree,
|
|
9466
9570
|
describeApiError as describeApiError2,
|
|
9571
|
+
fetchExistingBranch,
|
|
9467
9572
|
log as log30,
|
|
9468
9573
|
makeBranchName,
|
|
9469
9574
|
normalizeGateSpec,
|
|
9470
9575
|
pushBranch as pushBranch3,
|
|
9471
9576
|
readWorktreeHead,
|
|
9472
9577
|
reapGroup as reapGroup2,
|
|
9578
|
+
resolveContinuationTarget,
|
|
9473
9579
|
SdkAgentRunner as SdkAgentRunner2,
|
|
9474
9580
|
signalGroup as signalGroup2,
|
|
9475
9581
|
sizeRun,
|
|
@@ -9755,12 +9861,18 @@ class Worker {
|
|
|
9755
9861
|
log30.info(this.tag, resuming ? `Resuming #${card.short_id} "${card.title}" with ${this.grantedTurns ?? "the default"} more turns` : `Preparing #${card.short_id} "${card.title}"`);
|
|
9756
9862
|
const attemptCount = await this.stateStore.incrementAttempt(card.id);
|
|
9757
9863
|
const isRework = attemptCount > 1;
|
|
9758
|
-
const recordedBranch =
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9864
|
+
const recordedBranch = recordedBranchForCard(card.description, card.short_id, [
|
|
9865
|
+
this.config.worktree.failedBranchPrefix,
|
|
9866
|
+
this.config.worktree.approvedBranchPrefix
|
|
9867
|
+
]);
|
|
9868
|
+
const continuesPushedWork = isRework || recordedBranch !== null;
|
|
9869
|
+
if (!resuming && recordedBranch) {
|
|
9870
|
+
if (recordedBranch !== this.branchName) {
|
|
9871
|
+
log30.info(this.tag, `Card records pushed work on ${recordedBranch} — continuing that branch instead of ${this.branchName}`);
|
|
9872
|
+
this.branchName = recordedBranch;
|
|
9873
|
+
} else if (!isRework) {
|
|
9874
|
+
log30.info(this.tag, `Card records pushed work on ${this.branchName} — continuing that branch instead of rebuilding from ${this.config.worktree.baseBranch}`);
|
|
9875
|
+
}
|
|
9764
9876
|
}
|
|
9765
9877
|
this.startHeartbeat();
|
|
9766
9878
|
this.sizingOutcome = await this.sizeThisRun(card);
|
|
@@ -9850,8 +9962,20 @@ class Worker {
|
|
|
9850
9962
|
return;
|
|
9851
9963
|
}
|
|
9852
9964
|
if (!resuming) {
|
|
9965
|
+
const continueRequested = stageCtx.kind === "run" || stageCtx.kind === "motor" || continuesPushedWork;
|
|
9966
|
+
const repoRoot = execFileSync7("git", ["rev-parse", "--show-toplevel"], {
|
|
9967
|
+
encoding: "utf-8"
|
|
9968
|
+
}).trim();
|
|
9969
|
+
const target = resolveContinuationTarget(this.branchName, continueRequested, this.config.worktree.failedBranchPrefix, this.config.worktree.approvedBranchPrefix, (ref) => fetchExistingBranch(repoRoot, ref));
|
|
9970
|
+
if (target.branchName !== this.branchName) {
|
|
9971
|
+
log30.info(this.tag, `Branch ${this.branchName} is gone from origin but its approved rename ${target.branchName} exists — continuing that branch`);
|
|
9972
|
+
this.branchName = target.branchName;
|
|
9973
|
+
} else if (target.reason === "exists_on_origin") {
|
|
9974
|
+
log30.info(this.tag, `Branch ${this.branchName} exists on origin with no card record — continuing it rather than resetting it`);
|
|
9975
|
+
}
|
|
9853
9976
|
this.worktreePath = createWorktree(this.config.worktree.basePath, this.config.worktree.baseBranch, this.branchName, {
|
|
9854
|
-
continueExisting:
|
|
9977
|
+
continueExisting: target.continueExisting,
|
|
9978
|
+
branchExistsOnOrigin: target.existsOnOrigin
|
|
9855
9979
|
});
|
|
9856
9980
|
this.runBaselineSha = readWorktreeHead(this.worktreePath);
|
|
9857
9981
|
}
|
|
@@ -9941,9 +10065,8 @@ class Worker {
|
|
|
9941
10065
|
const stageRun = stageCtx.kind === "run" ? stageCtx : null;
|
|
9942
10066
|
const onBeforeWorktreeCleanup = stageRun ? async (worktreePath) => {
|
|
9943
10067
|
stageGateEvaluation = await this.collectStageGateEvidence(card, stageRun.stage, worktreePath, subtasks);
|
|
9944
|
-
return stageEndDisposition(stageGateEvaluation, stageRun.stage, stageRun.index, stageRun.def);
|
|
9945
10068
|
} : undefined;
|
|
9946
|
-
const completed = await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id, this.sessionIdentifier, this.identity.agentId, this.lastSessionStats, this.workspaceId, this.sessionId, this.stateStore, this.onCardCompleted, onBeforeWorktreeCleanup, this.runBaselineSha, this.effectiveMaxTurns);
|
|
10069
|
+
const completed = await runCompletion(this.client, card, this.branchName, this.worktreePath, this.config, this.id, this.sessionIdentifier, this.identity.agentId, this.lastSessionStats, this.workspaceId, this.sessionId, this.stateStore, this.onCardCompleted, onBeforeWorktreeCleanup, this.runBaselineSha, this.effectiveMaxTurns, stageRun ? stageRunExpectsCommit(stageRun.stage, stageRun.allowedTools) : true);
|
|
9947
10070
|
if (completed === "park") {
|
|
9948
10071
|
await this.parkForDecision(card, "max_turns");
|
|
9949
10072
|
return;
|
|
@@ -9951,24 +10074,29 @@ class Worker {
|
|
|
9951
10074
|
this.worktreePath = null;
|
|
9952
10075
|
this.verificationFailed = !completed;
|
|
9953
10076
|
if (completed && stageRun) {
|
|
9954
|
-
|
|
9955
|
-
|
|
9956
|
-
|
|
9957
|
-
|
|
9958
|
-
|
|
9959
|
-
|
|
9960
|
-
|
|
9961
|
-
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
|
|
9969
|
-
|
|
9970
|
-
|
|
10077
|
+
let outcome = { kind: "no_advance" };
|
|
10078
|
+
try {
|
|
10079
|
+
outcome = await this.advanceFromGateEvaluation(card, stageRun.stage, stageRun.index, stageRun.def, stageGateEvaluation);
|
|
10080
|
+
switch (outcome.kind) {
|
|
10081
|
+
case "requeued_gate_unmet":
|
|
10082
|
+
case "held_gate_unmet":
|
|
10083
|
+
case "held_misconfigured":
|
|
10084
|
+
this.held = true;
|
|
10085
|
+
break;
|
|
10086
|
+
case "reclaim_refused":
|
|
10087
|
+
this.held = true;
|
|
10088
|
+
log30.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
|
|
10089
|
+
break;
|
|
10090
|
+
case "advanced":
|
|
10091
|
+
case "completed_terminal":
|
|
10092
|
+
case "no_advance":
|
|
10093
|
+
break;
|
|
10094
|
+
default: {
|
|
10095
|
+
const _exhaustive = outcome;
|
|
10096
|
+
}
|
|
9971
10097
|
}
|
|
10098
|
+
} finally {
|
|
10099
|
+
await this.endStageRunSession(card, outcome);
|
|
9972
10100
|
}
|
|
9973
10101
|
}
|
|
9974
10102
|
} catch (err) {
|
|
@@ -10627,28 +10755,33 @@ class Worker {
|
|
|
10627
10755
|
this.completionStarted = true;
|
|
10628
10756
|
await this.recordPhase("completing");
|
|
10629
10757
|
const evaluation = verdict ? evaluationFromVerdict(verdict, result.structured) : null;
|
|
10630
|
-
await this.finishMotorStageRun(card, ctx
|
|
10631
|
-
|
|
10632
|
-
|
|
10633
|
-
|
|
10634
|
-
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
|
|
10640
|
-
|
|
10641
|
-
|
|
10642
|
-
|
|
10643
|
-
|
|
10644
|
-
|
|
10645
|
-
|
|
10646
|
-
|
|
10647
|
-
|
|
10758
|
+
await this.finishMotorStageRun(card, ctx);
|
|
10759
|
+
let outcome = { kind: "no_advance" };
|
|
10760
|
+
try {
|
|
10761
|
+
outcome = await this.advanceFromGateEvaluation(card, ctx.stage, ctx.index, ctx.def, evaluation);
|
|
10762
|
+
switch (outcome.kind) {
|
|
10763
|
+
case "requeued_gate_unmet":
|
|
10764
|
+
case "held_gate_unmet":
|
|
10765
|
+
case "held_misconfigured":
|
|
10766
|
+
this.held = true;
|
|
10767
|
+
break;
|
|
10768
|
+
case "reclaim_refused":
|
|
10769
|
+
this.held = true;
|
|
10770
|
+
log30.info(this.tag, `#${card.short_id} stage reclaim refused (${outcome.reason})${outcome.released ? " — released the daemon's claim" : ""}`);
|
|
10771
|
+
break;
|
|
10772
|
+
case "advanced":
|
|
10773
|
+
case "completed_terminal":
|
|
10774
|
+
case "no_advance":
|
|
10775
|
+
break;
|
|
10776
|
+
default: {
|
|
10777
|
+
const _exhaustive = outcome;
|
|
10778
|
+
}
|
|
10648
10779
|
}
|
|
10780
|
+
} finally {
|
|
10781
|
+
await this.endStageRunSession(card, outcome);
|
|
10649
10782
|
}
|
|
10650
10783
|
}
|
|
10651
|
-
async finishMotorStageRun(card, ctx
|
|
10784
|
+
async finishMotorStageRun(card, ctx) {
|
|
10652
10785
|
const worktreePath = this.worktreePath;
|
|
10653
10786
|
if (worktreePath) {
|
|
10654
10787
|
commitUncommittedChanges(worktreePath, card);
|
|
@@ -10666,7 +10799,6 @@ class Worker {
|
|
|
10666
10799
|
} else {
|
|
10667
10800
|
log30.warn(this.tag, `completion.moveToColumn is empty — #${card.short_id} stays in its current column after the motor stage run`);
|
|
10668
10801
|
}
|
|
10669
|
-
await endRunSession({ client: this.client, tag: this.tag }, card, disposition, {}, "log");
|
|
10670
10802
|
await this.closeoutMotorWorktree(card);
|
|
10671
10803
|
}
|
|
10672
10804
|
async closeoutMotorWorktree(card) {
|
|
@@ -10681,7 +10813,9 @@ class Worker {
|
|
|
10681
10813
|
this.worktreePath = null;
|
|
10682
10814
|
}
|
|
10683
10815
|
async buildFreshRunPrompt(enriched, card, stageCtx, continuesPushedWork, resuming) {
|
|
10684
|
-
const
|
|
10816
|
+
const recallOutcomes = [];
|
|
10817
|
+
const basePrompt = await buildPrompt(enriched, this.branchName, this.worktreePath, this.client, this.workspaceId, this.projectId, (outcome) => recallOutcomes.push(outcome));
|
|
10818
|
+
this.recordPromptAssembled(recallOutcomes);
|
|
10685
10819
|
let prompt = basePrompt;
|
|
10686
10820
|
if (stageCtx.kind === "run") {
|
|
10687
10821
|
const loop = getStageLoop(stageCtx.stage);
|
|
@@ -10820,6 +10954,9 @@ ${prompt}`;
|
|
|
10820
10954
|
return { kind: "no_advance" };
|
|
10821
10955
|
}
|
|
10822
10956
|
}
|
|
10957
|
+
async endStageRunSession(card, outcome) {
|
|
10958
|
+
await endRunSession({ client: this.client, tag: this.tag }, card, endDispositionFor(outcome), buildTokenPayload(this.lastSessionStats), "log");
|
|
10959
|
+
}
|
|
10823
10960
|
selectImplementModel(card) {
|
|
10824
10961
|
const attempts = this.stateStore.getCard(card.id)?.attempts ?? 1;
|
|
10825
10962
|
const choice = chooseImplementModel(this.config.claude, card, attempts, this.sizing ?? undefined);
|
|
@@ -10859,6 +10996,19 @@ ${prompt}`;
|
|
|
10859
10996
|
}
|
|
10860
10997
|
return outcome;
|
|
10861
10998
|
}
|
|
10999
|
+
recordPromptAssembled(outcomes) {
|
|
11000
|
+
const countOf = (flavour) => outcomes.find((o) => o.flavour === flavour)?.count ?? 0;
|
|
11001
|
+
const failures = outcomes.flatMap((o) => o.failure ? [{ flavour: o.flavour, message: o.failure.slice(0, 300) }] : []);
|
|
11002
|
+
this.cliRunner?.record({
|
|
11003
|
+
kind: "prompt_assembled",
|
|
11004
|
+
source: "system",
|
|
11005
|
+
payload: {
|
|
11006
|
+
episodeCount: countOf("past_episodes"),
|
|
11007
|
+
referenceCount: countOf("reference"),
|
|
11008
|
+
...failures.length > 0 ? { recallFailures: failures } : {}
|
|
11009
|
+
}
|
|
11010
|
+
});
|
|
11011
|
+
}
|
|
10862
11012
|
recordRunSized() {
|
|
10863
11013
|
if (!this.modelChoice)
|
|
10864
11014
|
return;
|
|
@@ -11680,10 +11830,11 @@ class Pool {
|
|
|
11680
11830
|
return;
|
|
11681
11831
|
try {
|
|
11682
11832
|
await this.client.updateAgentProgress(cardId, {
|
|
11683
|
-
agentIdentifier:
|
|
11833
|
+
agentIdentifier: NOTICE_IDENTIFIER,
|
|
11684
11834
|
agentName: AGENT_NAME,
|
|
11685
11835
|
status: "waiting",
|
|
11686
|
-
currentTask
|
|
11836
|
+
currentTask,
|
|
11837
|
+
driver: NOTICE_DRIVER
|
|
11687
11838
|
});
|
|
11688
11839
|
this.lastWaitingEmit.set(cardId, currentTask);
|
|
11689
11840
|
} catch (err) {
|
|
@@ -12219,6 +12370,7 @@ Reassign the card — that clears the turn count and starts it fresh — or rais
|
|
|
12219
12370
|
}
|
|
12220
12371
|
var TAG29 = "pool";
|
|
12221
12372
|
var init_pool = __esm(() => {
|
|
12373
|
+
init_dist();
|
|
12222
12374
|
init_board_helpers();
|
|
12223
12375
|
init_budget_pause();
|
|
12224
12376
|
init_handback();
|
|
@@ -14520,6 +14672,7 @@ async function main() {
|
|
|
14520
14672
|
};
|
|
14521
14673
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
|
14522
14674
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
|
14675
|
+
process.on("SIGHUP", () => shutdown("SIGHUP"));
|
|
14523
14676
|
process.on("uncaughtException", (err) => {
|
|
14524
14677
|
log42.error(TAG40, `Uncaught exception: ${err.message}`);
|
|
14525
14678
|
exitCode = 1;
|