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