@gethmy/agent 1.14.2 → 1.14.4
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 +37 -4
- package/dist/index.js +37 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5851,6 +5851,11 @@ async function runTransition(client, card, plan, opts = {}) {
|
|
|
5851
5851
|
await withRetry("endSession", shortId, () => client.endAgentSession(card.id, plan.endSession), attempts, backoffMs);
|
|
5852
5852
|
log.info(TAG22, `#${shortId} session ended (${plan.endSession.status})`);
|
|
5853
5853
|
}
|
|
5854
|
+
if (plan.assignAgent !== undefined) {
|
|
5855
|
+
const assignedAgentId = plan.assignAgent;
|
|
5856
|
+
await withRetry("assignAgent", shortId, () => client.updateCard(card.id, { assignedAgentId }), attempts, backoffMs);
|
|
5857
|
+
log.info(TAG22, assignedAgentId ? `#${shortId} assigned → agent ${assignedAgentId}` : `#${shortId} unassigned`);
|
|
5858
|
+
}
|
|
5854
5859
|
if (opts.store && opts.runId) {
|
|
5855
5860
|
try {
|
|
5856
5861
|
await opts.store.heartbeat(opts.runId);
|
|
@@ -6986,7 +6991,8 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
|
|
|
6986
6991
|
});
|
|
6987
6992
|
await runTransition(deps.client, card, {
|
|
6988
6993
|
move: { columnName: toColumn },
|
|
6989
|
-
addLabels: [{ name: AGENT_LABEL }]
|
|
6994
|
+
addLabels: [{ name: AGENT_LABEL }],
|
|
6995
|
+
...isAgentRunnableOwner(next.stage.owner) ? { assignAgent: deps.agentId } : {}
|
|
6990
6996
|
}, { store: deps.stateStore, runId: deps.runId });
|
|
6991
6997
|
deps.stateStore.recordOutcome(card.id, "success").catch(() => {});
|
|
6992
6998
|
log.info(TAG28, `#${card.short_id} advanced "${stage.name}" → "${next.stage.name}" (column "${toColumn}")`);
|
|
@@ -7015,7 +7021,11 @@ async function handleGateUnmet(card, stage, summary, deps) {
|
|
|
7015
7021
|
try {
|
|
7016
7022
|
await deps.client.addComment(card.id, `Stage gate unmet — re-running "${stage.name}". ${summary}.`, { commentType: "progress" });
|
|
7017
7023
|
} catch {}
|
|
7018
|
-
await runTransition(deps.client, card, {
|
|
7024
|
+
await runTransition(deps.client, card, {
|
|
7025
|
+
move: { columnName: toColumn },
|
|
7026
|
+
addLabels: [{ name: AGENT_LABEL }],
|
|
7027
|
+
...isAgentRunnableOwner(stage.owner) ? { assignAgent: deps.agentId } : {}
|
|
7028
|
+
}, { store: deps.stateStore, runId: deps.runId });
|
|
7019
7029
|
log.info(TAG28, `#${card.short_id} gate unmet for "${stage.name}" — requeued to "${toColumn}" for re-run (attempt ${attempts}/${deps.maxAttempts})`);
|
|
7020
7030
|
return { kind: "requeued_gate_unmet", toColumn };
|
|
7021
7031
|
}
|
|
@@ -7078,6 +7088,9 @@ function buildStagePreamble(stage) {
|
|
|
7078
7088
|
}
|
|
7079
7089
|
}
|
|
7080
7090
|
lines.push("Do only this stage's work. When the stage's handoff is met, end your session — advancement to the next stage is handled by the board.");
|
|
7091
|
+
if (normalizeGateSpec(stage.gate)?.kind === "review_passed") {
|
|
7092
|
+
lines.push("", "**This stage's gate is `review_passed` — your deliverable is a review verdict, not a prose handoff.** Do NOT end the session until you have actually reviewed the change and emitted EXACTLY one JSON block as the LAST thing you output (nothing after it):", "```json", REVIEW_VERDICT_SCHEMA, "```", "Decision rules:", REVIEW_DECISION_RULES, "The board reads this verdict to decide advancement: `approved` advances the card, `rejected` sends it back. A missing or unparseable verdict blocks the card. Do NOT modify any code — this is a read-only review.");
|
|
7093
|
+
}
|
|
7081
7094
|
return lines.filter(Boolean).join(`
|
|
7082
7095
|
`);
|
|
7083
7096
|
}
|
|
@@ -7115,12 +7128,14 @@ class Worker {
|
|
|
7115
7128
|
timedOut = false;
|
|
7116
7129
|
verificationFailed = false;
|
|
7117
7130
|
held = false;
|
|
7131
|
+
completionStarted = false;
|
|
7118
7132
|
sessionId = null;
|
|
7119
7133
|
runId = null;
|
|
7120
7134
|
cliSessionId = null;
|
|
7121
7135
|
lastDrainedSeq = 0;
|
|
7122
7136
|
runCostCents = 0;
|
|
7123
7137
|
runTurns = 0;
|
|
7138
|
+
lastRunText = "";
|
|
7124
7139
|
constructor(id, config, client, agentId, onDone, workspaceId, projectId, stateStore, onCardCompleted, onApiError) {
|
|
7125
7140
|
this.config = config;
|
|
7126
7141
|
this.client = client;
|
|
@@ -7186,8 +7201,10 @@ class Worker {
|
|
|
7186
7201
|
this.timedOut = false;
|
|
7187
7202
|
this.verificationFailed = false;
|
|
7188
7203
|
this.held = false;
|
|
7204
|
+
this.completionStarted = false;
|
|
7189
7205
|
this.runCostCents = 0;
|
|
7190
7206
|
this.runTurns = 0;
|
|
7207
|
+
this.lastRunText = "";
|
|
7191
7208
|
this.cliSessionId = null;
|
|
7192
7209
|
this.lastDrainedSeq = 0;
|
|
7193
7210
|
this.cardId = card.id;
|
|
@@ -7325,6 +7342,7 @@ class Worker {
|
|
|
7325
7342
|
progressPercent: 75
|
|
7326
7343
|
});
|
|
7327
7344
|
this.state = "completing";
|
|
7345
|
+
this.completionStarted = true;
|
|
7328
7346
|
await this.recordPhase("completing");
|
|
7329
7347
|
let stageGateEvaluation = null;
|
|
7330
7348
|
const stageRun = stageCtx.kind === "run" ? stageCtx : null;
|
|
@@ -7639,6 +7657,10 @@ class Worker {
|
|
|
7639
7657
|
log.info(this.tag, `Stage "${stage.name}" gate "${gate.kind}" is advisory — skipping enforcement`);
|
|
7640
7658
|
return null;
|
|
7641
7659
|
}
|
|
7660
|
+
const review = gate.kind === "review_passed" ? parseReviewOutput(this.lastRunText) : undefined;
|
|
7661
|
+
if (review) {
|
|
7662
|
+
log.info(this.tag, `Review-gated stage "${stage.name}" verdict: ${review.verdict} (${review.findings.length} finding(s))`);
|
|
7663
|
+
}
|
|
7642
7664
|
const registry = buildGateCollectorRegistry({
|
|
7643
7665
|
build: {
|
|
7644
7666
|
worktreePath,
|
|
@@ -7649,7 +7671,8 @@ class Worker {
|
|
|
7649
7671
|
worktreePath,
|
|
7650
7672
|
artifactType: stage.artifact_type
|
|
7651
7673
|
},
|
|
7652
|
-
checklist: { subtasks, cardDone: card.done ?? false }
|
|
7674
|
+
checklist: { subtasks, cardDone: card.done ?? false },
|
|
7675
|
+
...review ? { review } : {}
|
|
7653
7676
|
});
|
|
7654
7677
|
const context = {
|
|
7655
7678
|
cardId: card.id,
|
|
@@ -7676,6 +7699,7 @@ class Worker {
|
|
|
7676
7699
|
return await advanceStageOnGate(card, stage, stageIndex, def, evaluation, {
|
|
7677
7700
|
client: this.client,
|
|
7678
7701
|
stateStore: this.stateStore,
|
|
7702
|
+
agentId: this.agentId,
|
|
7679
7703
|
maxAttempts: this.config.budget.maxAttemptsPerCard,
|
|
7680
7704
|
fallbackColumn: this.config.pickupColumns[0] ?? "To Do",
|
|
7681
7705
|
sink: this.cliRunner,
|
|
@@ -7778,7 +7802,7 @@ class Worker {
|
|
|
7778
7802
|
sigtermTimeoutMs: CANCEL_SIGTERM_TIMEOUT2
|
|
7779
7803
|
});
|
|
7780
7804
|
}
|
|
7781
|
-
if (this.cardId && !this.timedOut) {
|
|
7805
|
+
if (this.cardId && !this.timedOut && !this.completionStarted) {
|
|
7782
7806
|
try {
|
|
7783
7807
|
const stats = this.lastSessionStats ?? this.progressTracker?.stats;
|
|
7784
7808
|
await this.client.endAgentSession(this.cardId, {
|
|
@@ -7986,6 +8010,9 @@ class Worker {
|
|
|
7986
8010
|
});
|
|
7987
8011
|
}
|
|
7988
8012
|
}
|
|
8013
|
+
parser.on("text", (content) => {
|
|
8014
|
+
this.lastRunText += content;
|
|
8015
|
+
});
|
|
7989
8016
|
parser.on("parse_error", (msg) => {
|
|
7990
8017
|
log.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
|
|
7991
8018
|
runLog?.stream.write(`
|
|
@@ -8079,6 +8106,10 @@ class Worker {
|
|
|
8079
8106
|
try {
|
|
8080
8107
|
for await (const ev of stream) {
|
|
8081
8108
|
this.progressTracker?.ingest(ev);
|
|
8109
|
+
if (ev.kind === "assistant_text") {
|
|
8110
|
+
this.lastRunText += `${ev.payload.text}
|
|
8111
|
+
`;
|
|
8112
|
+
}
|
|
8082
8113
|
if (ev.source === "agent")
|
|
8083
8114
|
this.cliRunner?.record(ev);
|
|
8084
8115
|
if (ev.kind === "error") {
|
|
@@ -8166,6 +8197,8 @@ var init_worker = __esm(() => {
|
|
|
8166
8197
|
init_process_group();
|
|
8167
8198
|
init_progress_tracker();
|
|
8168
8199
|
init_prompt();
|
|
8200
|
+
init_review_completion();
|
|
8201
|
+
init_review_knowledge();
|
|
8169
8202
|
init_run_log();
|
|
8170
8203
|
init_sdk_agent_runner();
|
|
8171
8204
|
init_stage_advance();
|
package/dist/index.js
CHANGED
|
@@ -5850,6 +5850,11 @@ async function runTransition(client, card, plan, opts = {}) {
|
|
|
5850
5850
|
await withRetry("endSession", shortId, () => client.endAgentSession(card.id, plan.endSession), attempts, backoffMs);
|
|
5851
5851
|
log.info(TAG22, `#${shortId} session ended (${plan.endSession.status})`);
|
|
5852
5852
|
}
|
|
5853
|
+
if (plan.assignAgent !== undefined) {
|
|
5854
|
+
const assignedAgentId = plan.assignAgent;
|
|
5855
|
+
await withRetry("assignAgent", shortId, () => client.updateCard(card.id, { assignedAgentId }), attempts, backoffMs);
|
|
5856
|
+
log.info(TAG22, assignedAgentId ? `#${shortId} assigned → agent ${assignedAgentId}` : `#${shortId} unassigned`);
|
|
5857
|
+
}
|
|
5853
5858
|
if (opts.store && opts.runId) {
|
|
5854
5859
|
try {
|
|
5855
5860
|
await opts.store.heartbeat(opts.runId);
|
|
@@ -6985,7 +6990,8 @@ async function advanceStageOnGate(card, stage, stageIndex, def, evaluation, deps
|
|
|
6985
6990
|
});
|
|
6986
6991
|
await runTransition(deps.client, card, {
|
|
6987
6992
|
move: { columnName: toColumn },
|
|
6988
|
-
addLabels: [{ name: AGENT_LABEL }]
|
|
6993
|
+
addLabels: [{ name: AGENT_LABEL }],
|
|
6994
|
+
...isAgentRunnableOwner(next.stage.owner) ? { assignAgent: deps.agentId } : {}
|
|
6989
6995
|
}, { store: deps.stateStore, runId: deps.runId });
|
|
6990
6996
|
deps.stateStore.recordOutcome(card.id, "success").catch(() => {});
|
|
6991
6997
|
log.info(TAG28, `#${card.short_id} advanced "${stage.name}" → "${next.stage.name}" (column "${toColumn}")`);
|
|
@@ -7014,7 +7020,11 @@ async function handleGateUnmet(card, stage, summary, deps) {
|
|
|
7014
7020
|
try {
|
|
7015
7021
|
await deps.client.addComment(card.id, `Stage gate unmet — re-running "${stage.name}". ${summary}.`, { commentType: "progress" });
|
|
7016
7022
|
} catch {}
|
|
7017
|
-
await runTransition(deps.client, card, {
|
|
7023
|
+
await runTransition(deps.client, card, {
|
|
7024
|
+
move: { columnName: toColumn },
|
|
7025
|
+
addLabels: [{ name: AGENT_LABEL }],
|
|
7026
|
+
...isAgentRunnableOwner(stage.owner) ? { assignAgent: deps.agentId } : {}
|
|
7027
|
+
}, { store: deps.stateStore, runId: deps.runId });
|
|
7018
7028
|
log.info(TAG28, `#${card.short_id} gate unmet for "${stage.name}" — requeued to "${toColumn}" for re-run (attempt ${attempts}/${deps.maxAttempts})`);
|
|
7019
7029
|
return { kind: "requeued_gate_unmet", toColumn };
|
|
7020
7030
|
}
|
|
@@ -7077,6 +7087,9 @@ function buildStagePreamble(stage) {
|
|
|
7077
7087
|
}
|
|
7078
7088
|
}
|
|
7079
7089
|
lines.push("Do only this stage's work. When the stage's handoff is met, end your session — advancement to the next stage is handled by the board.");
|
|
7090
|
+
if (normalizeGateSpec(stage.gate)?.kind === "review_passed") {
|
|
7091
|
+
lines.push("", "**This stage's gate is `review_passed` — your deliverable is a review verdict, not a prose handoff.** Do NOT end the session until you have actually reviewed the change and emitted EXACTLY one JSON block as the LAST thing you output (nothing after it):", "```json", REVIEW_VERDICT_SCHEMA, "```", "Decision rules:", REVIEW_DECISION_RULES, "The board reads this verdict to decide advancement: `approved` advances the card, `rejected` sends it back. A missing or unparseable verdict blocks the card. Do NOT modify any code — this is a read-only review.");
|
|
7092
|
+
}
|
|
7080
7093
|
return lines.filter(Boolean).join(`
|
|
7081
7094
|
`);
|
|
7082
7095
|
}
|
|
@@ -7114,12 +7127,14 @@ class Worker {
|
|
|
7114
7127
|
timedOut = false;
|
|
7115
7128
|
verificationFailed = false;
|
|
7116
7129
|
held = false;
|
|
7130
|
+
completionStarted = false;
|
|
7117
7131
|
sessionId = null;
|
|
7118
7132
|
runId = null;
|
|
7119
7133
|
cliSessionId = null;
|
|
7120
7134
|
lastDrainedSeq = 0;
|
|
7121
7135
|
runCostCents = 0;
|
|
7122
7136
|
runTurns = 0;
|
|
7137
|
+
lastRunText = "";
|
|
7123
7138
|
constructor(id, config, client, agentId, onDone, workspaceId, projectId, stateStore, onCardCompleted, onApiError) {
|
|
7124
7139
|
this.config = config;
|
|
7125
7140
|
this.client = client;
|
|
@@ -7185,8 +7200,10 @@ class Worker {
|
|
|
7185
7200
|
this.timedOut = false;
|
|
7186
7201
|
this.verificationFailed = false;
|
|
7187
7202
|
this.held = false;
|
|
7203
|
+
this.completionStarted = false;
|
|
7188
7204
|
this.runCostCents = 0;
|
|
7189
7205
|
this.runTurns = 0;
|
|
7206
|
+
this.lastRunText = "";
|
|
7190
7207
|
this.cliSessionId = null;
|
|
7191
7208
|
this.lastDrainedSeq = 0;
|
|
7192
7209
|
this.cardId = card.id;
|
|
@@ -7324,6 +7341,7 @@ class Worker {
|
|
|
7324
7341
|
progressPercent: 75
|
|
7325
7342
|
});
|
|
7326
7343
|
this.state = "completing";
|
|
7344
|
+
this.completionStarted = true;
|
|
7327
7345
|
await this.recordPhase("completing");
|
|
7328
7346
|
let stageGateEvaluation = null;
|
|
7329
7347
|
const stageRun = stageCtx.kind === "run" ? stageCtx : null;
|
|
@@ -7638,6 +7656,10 @@ class Worker {
|
|
|
7638
7656
|
log.info(this.tag, `Stage "${stage.name}" gate "${gate.kind}" is advisory — skipping enforcement`);
|
|
7639
7657
|
return null;
|
|
7640
7658
|
}
|
|
7659
|
+
const review = gate.kind === "review_passed" ? parseReviewOutput(this.lastRunText) : undefined;
|
|
7660
|
+
if (review) {
|
|
7661
|
+
log.info(this.tag, `Review-gated stage "${stage.name}" verdict: ${review.verdict} (${review.findings.length} finding(s))`);
|
|
7662
|
+
}
|
|
7641
7663
|
const registry = buildGateCollectorRegistry({
|
|
7642
7664
|
build: {
|
|
7643
7665
|
worktreePath,
|
|
@@ -7648,7 +7670,8 @@ class Worker {
|
|
|
7648
7670
|
worktreePath,
|
|
7649
7671
|
artifactType: stage.artifact_type
|
|
7650
7672
|
},
|
|
7651
|
-
checklist: { subtasks, cardDone: card.done ?? false }
|
|
7673
|
+
checklist: { subtasks, cardDone: card.done ?? false },
|
|
7674
|
+
...review ? { review } : {}
|
|
7652
7675
|
});
|
|
7653
7676
|
const context = {
|
|
7654
7677
|
cardId: card.id,
|
|
@@ -7675,6 +7698,7 @@ class Worker {
|
|
|
7675
7698
|
return await advanceStageOnGate(card, stage, stageIndex, def, evaluation, {
|
|
7676
7699
|
client: this.client,
|
|
7677
7700
|
stateStore: this.stateStore,
|
|
7701
|
+
agentId: this.agentId,
|
|
7678
7702
|
maxAttempts: this.config.budget.maxAttemptsPerCard,
|
|
7679
7703
|
fallbackColumn: this.config.pickupColumns[0] ?? "To Do",
|
|
7680
7704
|
sink: this.cliRunner,
|
|
@@ -7777,7 +7801,7 @@ class Worker {
|
|
|
7777
7801
|
sigtermTimeoutMs: CANCEL_SIGTERM_TIMEOUT2
|
|
7778
7802
|
});
|
|
7779
7803
|
}
|
|
7780
|
-
if (this.cardId && !this.timedOut) {
|
|
7804
|
+
if (this.cardId && !this.timedOut && !this.completionStarted) {
|
|
7781
7805
|
try {
|
|
7782
7806
|
const stats = this.lastSessionStats ?? this.progressTracker?.stats;
|
|
7783
7807
|
await this.client.endAgentSession(this.cardId, {
|
|
@@ -7985,6 +8009,9 @@ class Worker {
|
|
|
7985
8009
|
});
|
|
7986
8010
|
}
|
|
7987
8011
|
}
|
|
8012
|
+
parser.on("text", (content) => {
|
|
8013
|
+
this.lastRunText += content;
|
|
8014
|
+
});
|
|
7988
8015
|
parser.on("parse_error", (msg) => {
|
|
7989
8016
|
log.debug(this.tag, `Stream parse error (non-fatal): ${msg}`);
|
|
7990
8017
|
runLog?.stream.write(`
|
|
@@ -8078,6 +8105,10 @@ class Worker {
|
|
|
8078
8105
|
try {
|
|
8079
8106
|
for await (const ev of stream) {
|
|
8080
8107
|
this.progressTracker?.ingest(ev);
|
|
8108
|
+
if (ev.kind === "assistant_text") {
|
|
8109
|
+
this.lastRunText += `${ev.payload.text}
|
|
8110
|
+
`;
|
|
8111
|
+
}
|
|
8081
8112
|
if (ev.source === "agent")
|
|
8082
8113
|
this.cliRunner?.record(ev);
|
|
8083
8114
|
if (ev.kind === "error") {
|
|
@@ -8165,6 +8196,8 @@ var init_worker = __esm(() => {
|
|
|
8165
8196
|
init_process_group();
|
|
8166
8197
|
init_progress_tracker();
|
|
8167
8198
|
init_prompt();
|
|
8199
|
+
init_review_completion();
|
|
8200
|
+
init_review_knowledge();
|
|
8168
8201
|
init_run_log();
|
|
8169
8202
|
init_sdk_agent_runner();
|
|
8170
8203
|
init_stage_advance();
|