@gethmy/agent 1.15.0 → 1.16.1
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 +103 -18
- package/dist/index.js +103 -18
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -496,6 +496,7 @@ import {
|
|
|
496
496
|
getApiUrl,
|
|
497
497
|
getUserEmail
|
|
498
498
|
} from "@gethmy/mcp/src/config.js";
|
|
499
|
+
import { refreshOAuthToken } from "@gethmy/mcp/src/oauth-refresh.js";
|
|
499
500
|
function getRepoRoot() {
|
|
500
501
|
return execSync("git rev-parse --show-toplevel", {
|
|
501
502
|
encoding: "utf-8"
|
|
@@ -602,7 +603,11 @@ async function fetchRealtimeCredentials(client) {
|
|
|
602
603
|
return result;
|
|
603
604
|
}
|
|
604
605
|
function createApiClient(config) {
|
|
605
|
-
return new HarmonyApiClient({
|
|
606
|
+
return new HarmonyApiClient({
|
|
607
|
+
apiKey: config.apiKey,
|
|
608
|
+
apiUrl: config.apiUrl,
|
|
609
|
+
refreshCredential: refreshOAuthToken
|
|
610
|
+
});
|
|
606
611
|
}
|
|
607
612
|
var init_config = __esm(() => {
|
|
608
613
|
init_types();
|
|
@@ -1289,6 +1294,8 @@ function serializeCommentThread(comments, options = {}) {
|
|
|
1289
1294
|
const tags = [];
|
|
1290
1295
|
if (c.edited_at)
|
|
1291
1296
|
tags.push("edited");
|
|
1297
|
+
if (c.reply_to_id)
|
|
1298
|
+
tags.push(`reply to ${ref(c.reply_to_id)}`);
|
|
1292
1299
|
if (c.supersedes_id)
|
|
1293
1300
|
tags.push(`supersedes ${ref(c.supersedes_id)}`);
|
|
1294
1301
|
if (c.confirms_id)
|
|
@@ -1666,6 +1673,9 @@ function getStageLoop(stage) {
|
|
|
1666
1673
|
function isConvergeLoop(loop) {
|
|
1667
1674
|
return loop !== null && loop.mode === "converge";
|
|
1668
1675
|
}
|
|
1676
|
+
function resolveLoopExitGate(stage, loop) {
|
|
1677
|
+
return loop.exit_gate ?? stage.gate ?? null;
|
|
1678
|
+
}
|
|
1669
1679
|
function decideLoopContinuation(args) {
|
|
1670
1680
|
const { loop, gatePassed, hasExitGate, completedIterations } = args;
|
|
1671
1681
|
const max = Math.max(1, Math.floor(loop.max_iterations) || 1);
|
|
@@ -1714,7 +1724,10 @@ function entryActionAllowlist(entryAction) {
|
|
|
1714
1724
|
return `mcp__${entryAction}`;
|
|
1715
1725
|
return null;
|
|
1716
1726
|
}
|
|
1717
|
-
|
|
1727
|
+
function stageDisallowedTools() {
|
|
1728
|
+
return STAGE_DAEMON_OWNED_TOOLS.length > 0 ? STAGE_DAEMON_OWNED_TOOLS.join(",") : null;
|
|
1729
|
+
}
|
|
1730
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5, SKILL_TOOL_ALLOWLIST, HARMONY_TOOL_RE, STAGE_DAEMON_OWNED_TOOLS;
|
|
1718
1731
|
var init_playbookStage = __esm(() => {
|
|
1719
1732
|
SKILL_TOOL_ALLOWLIST = {
|
|
1720
1733
|
hmy: "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*",
|
|
@@ -1725,6 +1738,11 @@ var init_playbookStage = __esm(() => {
|
|
|
1725
1738
|
"hmy-standup": "Read,Grep,Glob,mcp__harmony__*"
|
|
1726
1739
|
};
|
|
1727
1740
|
HARMONY_TOOL_RE = /^harmony_[a-z_]+$/;
|
|
1741
|
+
STAGE_DAEMON_OWNED_TOOLS = [
|
|
1742
|
+
"mcp__harmony__harmony_end_agent_session",
|
|
1743
|
+
"mcp__harmony__harmony_start_agent_session",
|
|
1744
|
+
"mcp__harmony__harmony_move_card"
|
|
1745
|
+
];
|
|
1728
1746
|
});
|
|
1729
1747
|
|
|
1730
1748
|
// ../harmony-shared/dist/projectTemplates.js
|
|
@@ -2159,6 +2177,58 @@ function cleanupWorktree(worktreePath, branchName) {
|
|
|
2159
2177
|
} catch {}
|
|
2160
2178
|
}
|
|
2161
2179
|
}
|
|
2180
|
+
function resolveRepoRoot() {
|
|
2181
|
+
return execFileSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
2182
|
+
encoding: "utf-8"
|
|
2183
|
+
}).trim();
|
|
2184
|
+
}
|
|
2185
|
+
function branchAheadOfItsRemote(branchName, repoRoot = resolveRepoRoot()) {
|
|
2186
|
+
try {
|
|
2187
|
+
const out = execFileSync3("git", ["rev-list", branchName, "--not", "--remotes=origin"], { cwd: repoRoot, encoding: "utf-8" }).trim();
|
|
2188
|
+
return out.length > 0;
|
|
2189
|
+
} catch {
|
|
2190
|
+
return false;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
async function rescueUnpushedBranch(client, cardId, branchName, repoRoot = resolveRepoRoot()) {
|
|
2194
|
+
const { getBranchWebUrl: getBranchWebUrl2, pushBranch: pushBranch2 } = await Promise.resolve().then(() => (init_git_pr(), exports_git_pr));
|
|
2195
|
+
try {
|
|
2196
|
+
pushBranch2(branchName, repoRoot);
|
|
2197
|
+
} catch (err) {
|
|
2198
|
+
log.error(TAG5, `push-rescue failed for ${branchName} — leaving local branch ref intact (recoverable via git reflog / the local branch): ${err instanceof Error ? err.message : err}`);
|
|
2199
|
+
return false;
|
|
2200
|
+
}
|
|
2201
|
+
log.warn(TAG5, `push-rescued unpushed branch ${branchName} to origin before teardown`);
|
|
2202
|
+
try {
|
|
2203
|
+
const url = getBranchWebUrl2(branchName, repoRoot);
|
|
2204
|
+
const recover = url ? `View it at ${url} or recover locally: \`git fetch && git checkout ${branchName}\`` : `Recover it locally: \`git fetch && git checkout ${branchName}\``;
|
|
2205
|
+
const body = `⚠ Run ended before completion. Committed work was push-rescued to ` + `\`origin/${branchName}\` so it isn't lost. ${recover}`;
|
|
2206
|
+
await client.addComment(cardId, body, { commentType: "message" });
|
|
2207
|
+
} catch (err) {
|
|
2208
|
+
log.warn(TAG5, `push-rescue comment failed for ${branchName} (work is still safe on origin): ${err instanceof Error ? err.message : err}`);
|
|
2209
|
+
}
|
|
2210
|
+
return true;
|
|
2211
|
+
}
|
|
2212
|
+
async function teardownWorktree(client, cardId, worktreePath, branchName) {
|
|
2213
|
+
let skipBranchDelete = false;
|
|
2214
|
+
if (branchName && cardId) {
|
|
2215
|
+
let repoRoot;
|
|
2216
|
+
try {
|
|
2217
|
+
repoRoot = resolveRepoRoot();
|
|
2218
|
+
} catch {
|
|
2219
|
+
cleanupWorktree(worktreePath, branchName);
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
if (branchAheadOfItsRemote(branchName, repoRoot)) {
|
|
2223
|
+
const ok = await rescueUnpushedBranch(client, cardId, branchName, repoRoot);
|
|
2224
|
+
if (!ok) {
|
|
2225
|
+
skipBranchDelete = true;
|
|
2226
|
+
log.error(TAG5, `Keeping local branch ${branchName} (push-rescue failed) to avoid orphaning its commit`);
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
cleanupWorktree(worktreePath, skipBranchDelete ? undefined : branchName);
|
|
2231
|
+
}
|
|
2162
2232
|
function makeBranchName(shortId, title, prefix = "agent-attempts/") {
|
|
2163
2233
|
const slug = title.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2164
2234
|
return `${prefix}${shortId}-${slug || "task"}`;
|
|
@@ -3509,7 +3579,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3509
3579
|
failureSummary,
|
|
3510
3580
|
...buildTokenPayload(sessionStats)
|
|
3511
3581
|
});
|
|
3512
|
-
|
|
3582
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3513
3583
|
return false;
|
|
3514
3584
|
}
|
|
3515
3585
|
log.info(TAG14, `Pushing branch ${branchName} (pre-verify)...`);
|
|
@@ -3591,7 +3661,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3591
3661
|
recoveryBranch: branchName,
|
|
3592
3662
|
...buildTokenPayload(sessionStats)
|
|
3593
3663
|
});
|
|
3594
|
-
|
|
3664
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3595
3665
|
return false;
|
|
3596
3666
|
}
|
|
3597
3667
|
log.info(TAG14, `Verification passed for #${card.short_id}`);
|
|
@@ -3647,7 +3717,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3647
3717
|
log.warn(TAG14, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
3648
3718
|
}
|
|
3649
3719
|
}
|
|
3650
|
-
|
|
3720
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3651
3721
|
log.info(TAG14, `Completion done for #${card.short_id}${prUrl ? ` — PR: ${prUrl}` : ""}`);
|
|
3652
3722
|
return true;
|
|
3653
3723
|
}
|
|
@@ -3967,6 +4037,7 @@ class SdkAgentRunner {
|
|
|
3967
4037
|
cwd: input.cwd,
|
|
3968
4038
|
model: input.model ?? this.cfg.model,
|
|
3969
4039
|
allowedTools: allowed,
|
|
4040
|
+
...this.cfg.disallowedTools && this.cfg.disallowedTools.length > 0 ? { disallowedTools: this.cfg.disallowedTools } : {},
|
|
3970
4041
|
tools: builtinTools,
|
|
3971
4042
|
permissionMode: "dontAsk",
|
|
3972
4043
|
maxTurns: this.cfg.maxTurns,
|
|
@@ -6918,7 +6989,7 @@ async function buildPrompt(enriched, branchName, worktreePath, client, workspace
|
|
|
6918
6989
|
variant: "execute",
|
|
6919
6990
|
customConstraints: `You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
6920
6991
|
Do NOT push to main. All your work stays on \`${branchName}\`.
|
|
6921
|
-
|
|
6992
|
+
The daemon owns the run lifecycle: once your work is committed it ends the agent session, pushes the branch, and moves the card to Review for you. Do NOT call harmony_end_agent_session, do NOT start a new session, and do NOT move the card or change its column yourself. If the skill driving this work tells you to move the card or end the session as a final step, SKIP it — it is handled for you (those tools are disabled for this run). Finish the implementation, commit, and stop.`
|
|
6922
6993
|
});
|
|
6923
6994
|
log.info(TAG27, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
|
|
6924
6995
|
return result.prompt + pastEpisodesSection;
|
|
@@ -7028,7 +7099,7 @@ ${subtaskStr}
|
|
|
7028
7099
|
Include a brief currentTask description.
|
|
7029
7100
|
3. Implement the changes on branch \`${branchName}\`
|
|
7030
7101
|
4. Commit your work with clear, descriptive commit messages
|
|
7031
|
-
5. When
|
|
7102
|
+
5. When the work is committed, STOP. The daemon owns the run lifecycle: it ends the agent session, pushes the branch, and moves the card to Review for you. Do NOT call harmony_end_agent_session, do NOT start a new session, and do NOT move the card or change its column yourself — those tools are disabled for this run.
|
|
7032
7103
|
|
|
7033
7104
|
You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
7034
7105
|
Do NOT push to main. All your work stays on \`${branchName}\`.`;
|
|
@@ -7077,7 +7148,7 @@ function firstErrorMessage(evaluation) {
|
|
|
7077
7148
|
return e ? e.message : null;
|
|
7078
7149
|
}
|
|
7079
7150
|
async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loop, deps) {
|
|
7080
|
-
const hasExitGate = loop
|
|
7151
|
+
const hasExitGate = resolveLoopExitGate(stage, loop) != null;
|
|
7081
7152
|
const gatePassed = hasExitGate ? evaluation?.passed ?? false : false;
|
|
7082
7153
|
const gateResult = !hasExitGate ? "skipped" : gatePassed ? "passed" : "failed";
|
|
7083
7154
|
const maxIterations = Math.max(1, Math.floor(loop.max_iterations) || 1);
|
|
@@ -7330,7 +7401,7 @@ function buildStagePreamble(stage) {
|
|
|
7330
7401
|
lines.push(`Hand off when done: ${summary.trim()}`);
|
|
7331
7402
|
}
|
|
7332
7403
|
}
|
|
7333
|
-
lines.push("Do only this stage's work.
|
|
7404
|
+
lines.push("Do only this stage's work, then stop. The daemon owns the run lifecycle here: it ends the agent session and performs every card move and stage advancement automatically once your stage work is done. Do NOT call `harmony_end_agent_session`, do NOT start a new session, and do NOT move the card or change its column yourself. If the skill driving this stage tells you to move the card (e.g. to Review) or end your session as a final step, SKIP that step — it is handled for you. Those tools are disabled for this run, so attempting them only wastes turns. Finish the stage's work and stop.");
|
|
7334
7405
|
if (normalizeGateSpec(stage.gate)?.kind === "review_passed") {
|
|
7335
7406
|
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.");
|
|
7336
7407
|
}
|
|
@@ -7343,6 +7414,13 @@ function buildSteeringPrompt(messages) {
|
|
|
7343
7414
|
return messages.map((m, i) => `${i + 1}. ${m}`).join(`
|
|
7344
7415
|
`);
|
|
7345
7416
|
}
|
|
7417
|
+
function computeRunSpawnGating(stageAllowedTools) {
|
|
7418
|
+
const denylist = stageDisallowedTools();
|
|
7419
|
+
return {
|
|
7420
|
+
...stageAllowedTools ? { allowedTools: stageAllowedTools } : {},
|
|
7421
|
+
...denylist ? { disallowedTools: denylist } : {}
|
|
7422
|
+
};
|
|
7423
|
+
}
|
|
7346
7424
|
|
|
7347
7425
|
class Worker {
|
|
7348
7426
|
config;
|
|
@@ -7371,6 +7449,7 @@ class Worker {
|
|
|
7371
7449
|
timedOut = false;
|
|
7372
7450
|
verificationFailed = false;
|
|
7373
7451
|
held = false;
|
|
7452
|
+
activeRunSpawnOpts = null;
|
|
7374
7453
|
completionStarted = false;
|
|
7375
7454
|
sessionId = null;
|
|
7376
7455
|
runId = null;
|
|
@@ -7450,6 +7529,7 @@ class Worker {
|
|
|
7450
7529
|
this.lastRunText = "";
|
|
7451
7530
|
this.cliSessionId = null;
|
|
7452
7531
|
this.lastDrainedSeq = 0;
|
|
7532
|
+
this.activeRunSpawnOpts = null;
|
|
7453
7533
|
this.cardId = card.id;
|
|
7454
7534
|
this.startedAt = Date.now();
|
|
7455
7535
|
this.runId = newRunId();
|
|
@@ -7570,9 +7650,10 @@ class Worker {
|
|
|
7570
7650
|
this.timedOut = true;
|
|
7571
7651
|
this.cancel();
|
|
7572
7652
|
}, this.config.maxTimeout);
|
|
7653
|
+
this.activeRunSpawnOpts = computeRunSpawnGating(stageCtx.kind === "run" ? stageCtx.allowedTools : null);
|
|
7573
7654
|
await this.spawnClaude(prompt, card, subtasks, {
|
|
7574
7655
|
model: this.selectImplementModel(card),
|
|
7575
|
-
...
|
|
7656
|
+
...this.activeRunSpawnOpts ?? {}
|
|
7576
7657
|
});
|
|
7577
7658
|
if (this.aborted)
|
|
7578
7659
|
return;
|
|
@@ -7647,7 +7728,7 @@ class Worker {
|
|
|
7647
7728
|
}
|
|
7648
7729
|
if (this.worktreePath) {
|
|
7649
7730
|
try {
|
|
7650
|
-
|
|
7731
|
+
await teardownWorktree(this.client, card.id, this.worktreePath, this.branchName ?? undefined);
|
|
7651
7732
|
} catch {
|
|
7652
7733
|
log.warn(this.tag, "Failed to cleanup worktree before requeue");
|
|
7653
7734
|
}
|
|
@@ -7691,7 +7772,7 @@ class Worker {
|
|
|
7691
7772
|
} else if (this.runId && this.timedOut) {
|
|
7692
7773
|
if (this.worktreePath) {
|
|
7693
7774
|
try {
|
|
7694
|
-
|
|
7775
|
+
await teardownWorktree(this.client, card.id, this.worktreePath, this.branchName ?? undefined);
|
|
7695
7776
|
} catch {
|
|
7696
7777
|
log.warn(this.tag, "Failed to cleanup worktree before requeue");
|
|
7697
7778
|
}
|
|
@@ -7781,7 +7862,7 @@ class Worker {
|
|
|
7781
7862
|
await this.cliRunner.flushFinal();
|
|
7782
7863
|
} catch {}
|
|
7783
7864
|
}
|
|
7784
|
-
this.cleanup();
|
|
7865
|
+
await this.cleanup();
|
|
7785
7866
|
this.state = "idle";
|
|
7786
7867
|
this.onDone(this);
|
|
7787
7868
|
}
|
|
@@ -7907,7 +7988,7 @@ class Worker {
|
|
|
7907
7988
|
async collectStageGateEvidence(card, stage, worktreePath, subtasks) {
|
|
7908
7989
|
try {
|
|
7909
7990
|
const loop = getStageLoop(stage);
|
|
7910
|
-
const gateSource = isConvergeLoop(loop)
|
|
7991
|
+
const gateSource = isConvergeLoop(loop) && loop ? resolveLoopExitGate(stage, loop) : stage.gate;
|
|
7911
7992
|
const gate = normalizeGateSpec(gateSource);
|
|
7912
7993
|
if (!gate) {
|
|
7913
7994
|
return null;
|
|
@@ -8205,7 +8286,8 @@ class Worker {
|
|
|
8205
8286
|
await this.spawnClaude(buildSteeringPrompt(messages.map((m) => m.text)), card, subtasks, {
|
|
8206
8287
|
model: this.selectImplementModel(card),
|
|
8207
8288
|
maxTurns: STEERING_MAX_TURNS,
|
|
8208
|
-
resumeSessionId: this.cliSessionId
|
|
8289
|
+
resumeSessionId: this.cliSessionId,
|
|
8290
|
+
...this.activeRunSpawnOpts ?? {}
|
|
8209
8291
|
});
|
|
8210
8292
|
} catch (err) {
|
|
8211
8293
|
log.warn(this.tag, `Steering resume failed (non-fatal): ${err instanceof Error ? err.message : err}`);
|
|
@@ -8233,6 +8315,7 @@ class Worker {
|
|
|
8233
8315
|
String(maxTurns),
|
|
8234
8316
|
"--allowedTools",
|
|
8235
8317
|
allowedTools,
|
|
8318
|
+
...opts.disallowedTools ? ["--disallowedTools", opts.disallowedTools] : [],
|
|
8236
8319
|
...opts.resumeSessionId ? ["--resume", opts.resumeSessionId] : [],
|
|
8237
8320
|
...this.config.claude.additionalArgs,
|
|
8238
8321
|
"--",
|
|
@@ -8320,6 +8403,7 @@ class Worker {
|
|
|
8320
8403
|
const model = opts.model ?? this.config.claude.model;
|
|
8321
8404
|
const maxTurns = opts.maxTurns ?? this.config.claude.maxTurns;
|
|
8322
8405
|
const allowedTools = (opts.allowedTools ?? IMPLEMENT_ALLOWED_TOOLS).split(",").map((t) => t.trim()).filter(Boolean);
|
|
8406
|
+
const disallowedTools = opts.disallowedTools ? opts.disallowedTools.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
8323
8407
|
const initialPhase = opts.initialPhase ?? "exploring";
|
|
8324
8408
|
const sdkCfg = this.config.sdk;
|
|
8325
8409
|
log.info(this.tag, `Spawning Agent SDK runner (model=${model}, maxTurns=${maxTurns}${opts.resumeSessionId ? ", resume" : ""})`);
|
|
@@ -8339,6 +8423,7 @@ class Worker {
|
|
|
8339
8423
|
model,
|
|
8340
8424
|
maxTurns,
|
|
8341
8425
|
allowedTools,
|
|
8426
|
+
...disallowedTools ? { disallowedTools } : {},
|
|
8342
8427
|
maxBudgetUsd: sdkCfg?.maxBudgetUsd,
|
|
8343
8428
|
settingSources: sdkCfg?.settingSources,
|
|
8344
8429
|
mcpServers: sdkCfg?.mcpServers,
|
|
@@ -8405,7 +8490,7 @@ class Worker {
|
|
|
8405
8490
|
throw err;
|
|
8406
8491
|
}
|
|
8407
8492
|
}
|
|
8408
|
-
cleanup() {
|
|
8493
|
+
async cleanup() {
|
|
8409
8494
|
if (this.progressTracker) {
|
|
8410
8495
|
this.progressTracker.stop();
|
|
8411
8496
|
this.progressTracker = null;
|
|
@@ -8423,7 +8508,7 @@ class Worker {
|
|
|
8423
8508
|
}
|
|
8424
8509
|
if (this.worktreePath && (this.state === "error" || this.timedOut || this.aborted)) {
|
|
8425
8510
|
try {
|
|
8426
|
-
|
|
8511
|
+
await teardownWorktree(this.client, this.cardId, this.worktreePath, this.branchName ?? undefined);
|
|
8427
8512
|
} catch {
|
|
8428
8513
|
log.warn(this.tag, "Failed to cleanup worktree");
|
|
8429
8514
|
}
|
|
@@ -8904,7 +8989,7 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
8904
8989
|
}
|
|
8905
8990
|
if (run.worktreePath) {
|
|
8906
8991
|
try {
|
|
8907
|
-
|
|
8992
|
+
await teardownWorktree(client, run.cardId, run.worktreePath, run.branchName ?? undefined);
|
|
8908
8993
|
outcome.actions.push("cleaned up worktree");
|
|
8909
8994
|
} catch (err) {
|
|
8910
8995
|
const msg = err instanceof Error ? err.message : String(err);
|
package/dist/index.js
CHANGED
|
@@ -495,6 +495,7 @@ import {
|
|
|
495
495
|
getApiUrl,
|
|
496
496
|
getUserEmail
|
|
497
497
|
} from "@gethmy/mcp/src/config.js";
|
|
498
|
+
import { refreshOAuthToken } from "@gethmy/mcp/src/oauth-refresh.js";
|
|
498
499
|
function getRepoRoot() {
|
|
499
500
|
return execSync("git rev-parse --show-toplevel", {
|
|
500
501
|
encoding: "utf-8"
|
|
@@ -601,7 +602,11 @@ async function fetchRealtimeCredentials(client) {
|
|
|
601
602
|
return result;
|
|
602
603
|
}
|
|
603
604
|
function createApiClient(config) {
|
|
604
|
-
return new HarmonyApiClient({
|
|
605
|
+
return new HarmonyApiClient({
|
|
606
|
+
apiKey: config.apiKey,
|
|
607
|
+
apiUrl: config.apiUrl,
|
|
608
|
+
refreshCredential: refreshOAuthToken
|
|
609
|
+
});
|
|
605
610
|
}
|
|
606
611
|
var init_config = __esm(() => {
|
|
607
612
|
init_types();
|
|
@@ -1288,6 +1293,8 @@ function serializeCommentThread(comments, options = {}) {
|
|
|
1288
1293
|
const tags = [];
|
|
1289
1294
|
if (c.edited_at)
|
|
1290
1295
|
tags.push("edited");
|
|
1296
|
+
if (c.reply_to_id)
|
|
1297
|
+
tags.push(`reply to ${ref(c.reply_to_id)}`);
|
|
1291
1298
|
if (c.supersedes_id)
|
|
1292
1299
|
tags.push(`supersedes ${ref(c.supersedes_id)}`);
|
|
1293
1300
|
if (c.confirms_id)
|
|
@@ -1665,6 +1672,9 @@ function getStageLoop(stage) {
|
|
|
1665
1672
|
function isConvergeLoop(loop) {
|
|
1666
1673
|
return loop !== null && loop.mode === "converge";
|
|
1667
1674
|
}
|
|
1675
|
+
function resolveLoopExitGate(stage, loop) {
|
|
1676
|
+
return loop.exit_gate ?? stage.gate ?? null;
|
|
1677
|
+
}
|
|
1668
1678
|
function decideLoopContinuation(args) {
|
|
1669
1679
|
const { loop, gatePassed, hasExitGate, completedIterations } = args;
|
|
1670
1680
|
const max = Math.max(1, Math.floor(loop.max_iterations) || 1);
|
|
@@ -1713,7 +1723,10 @@ function entryActionAllowlist(entryAction) {
|
|
|
1713
1723
|
return `mcp__${entryAction}`;
|
|
1714
1724
|
return null;
|
|
1715
1725
|
}
|
|
1716
|
-
|
|
1726
|
+
function stageDisallowedTools() {
|
|
1727
|
+
return STAGE_DAEMON_OWNED_TOOLS.length > 0 ? STAGE_DAEMON_OWNED_TOOLS.join(",") : null;
|
|
1728
|
+
}
|
|
1729
|
+
var DEFAULT_LOOP_MAX_ITERATIONS = 5, SKILL_TOOL_ALLOWLIST, HARMONY_TOOL_RE, STAGE_DAEMON_OWNED_TOOLS;
|
|
1717
1730
|
var init_playbookStage = __esm(() => {
|
|
1718
1731
|
SKILL_TOOL_ALLOWLIST = {
|
|
1719
1732
|
hmy: "Bash,Read,Write,Edit,Glob,Grep,Agent,mcp__harmony__*",
|
|
@@ -1724,6 +1737,11 @@ var init_playbookStage = __esm(() => {
|
|
|
1724
1737
|
"hmy-standup": "Read,Grep,Glob,mcp__harmony__*"
|
|
1725
1738
|
};
|
|
1726
1739
|
HARMONY_TOOL_RE = /^harmony_[a-z_]+$/;
|
|
1740
|
+
STAGE_DAEMON_OWNED_TOOLS = [
|
|
1741
|
+
"mcp__harmony__harmony_end_agent_session",
|
|
1742
|
+
"mcp__harmony__harmony_start_agent_session",
|
|
1743
|
+
"mcp__harmony__harmony_move_card"
|
|
1744
|
+
];
|
|
1727
1745
|
});
|
|
1728
1746
|
|
|
1729
1747
|
// ../harmony-shared/dist/projectTemplates.js
|
|
@@ -2158,6 +2176,58 @@ function cleanupWorktree(worktreePath, branchName) {
|
|
|
2158
2176
|
} catch {}
|
|
2159
2177
|
}
|
|
2160
2178
|
}
|
|
2179
|
+
function resolveRepoRoot() {
|
|
2180
|
+
return execFileSync3("git", ["rev-parse", "--show-toplevel"], {
|
|
2181
|
+
encoding: "utf-8"
|
|
2182
|
+
}).trim();
|
|
2183
|
+
}
|
|
2184
|
+
function branchAheadOfItsRemote(branchName, repoRoot = resolveRepoRoot()) {
|
|
2185
|
+
try {
|
|
2186
|
+
const out = execFileSync3("git", ["rev-list", branchName, "--not", "--remotes=origin"], { cwd: repoRoot, encoding: "utf-8" }).trim();
|
|
2187
|
+
return out.length > 0;
|
|
2188
|
+
} catch {
|
|
2189
|
+
return false;
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
async function rescueUnpushedBranch(client, cardId, branchName, repoRoot = resolveRepoRoot()) {
|
|
2193
|
+
const { getBranchWebUrl: getBranchWebUrl2, pushBranch: pushBranch2 } = await Promise.resolve().then(() => (init_git_pr(), exports_git_pr));
|
|
2194
|
+
try {
|
|
2195
|
+
pushBranch2(branchName, repoRoot);
|
|
2196
|
+
} catch (err) {
|
|
2197
|
+
log.error(TAG5, `push-rescue failed for ${branchName} — leaving local branch ref intact (recoverable via git reflog / the local branch): ${err instanceof Error ? err.message : err}`);
|
|
2198
|
+
return false;
|
|
2199
|
+
}
|
|
2200
|
+
log.warn(TAG5, `push-rescued unpushed branch ${branchName} to origin before teardown`);
|
|
2201
|
+
try {
|
|
2202
|
+
const url = getBranchWebUrl2(branchName, repoRoot);
|
|
2203
|
+
const recover = url ? `View it at ${url} or recover locally: \`git fetch && git checkout ${branchName}\`` : `Recover it locally: \`git fetch && git checkout ${branchName}\``;
|
|
2204
|
+
const body = `⚠ Run ended before completion. Committed work was push-rescued to ` + `\`origin/${branchName}\` so it isn't lost. ${recover}`;
|
|
2205
|
+
await client.addComment(cardId, body, { commentType: "message" });
|
|
2206
|
+
} catch (err) {
|
|
2207
|
+
log.warn(TAG5, `push-rescue comment failed for ${branchName} (work is still safe on origin): ${err instanceof Error ? err.message : err}`);
|
|
2208
|
+
}
|
|
2209
|
+
return true;
|
|
2210
|
+
}
|
|
2211
|
+
async function teardownWorktree(client, cardId, worktreePath, branchName) {
|
|
2212
|
+
let skipBranchDelete = false;
|
|
2213
|
+
if (branchName && cardId) {
|
|
2214
|
+
let repoRoot;
|
|
2215
|
+
try {
|
|
2216
|
+
repoRoot = resolveRepoRoot();
|
|
2217
|
+
} catch {
|
|
2218
|
+
cleanupWorktree(worktreePath, branchName);
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
if (branchAheadOfItsRemote(branchName, repoRoot)) {
|
|
2222
|
+
const ok = await rescueUnpushedBranch(client, cardId, branchName, repoRoot);
|
|
2223
|
+
if (!ok) {
|
|
2224
|
+
skipBranchDelete = true;
|
|
2225
|
+
log.error(TAG5, `Keeping local branch ${branchName} (push-rescue failed) to avoid orphaning its commit`);
|
|
2226
|
+
}
|
|
2227
|
+
}
|
|
2228
|
+
}
|
|
2229
|
+
cleanupWorktree(worktreePath, skipBranchDelete ? undefined : branchName);
|
|
2230
|
+
}
|
|
2161
2231
|
function makeBranchName(shortId, title, prefix = "agent-attempts/") {
|
|
2162
2232
|
const slug = title.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
2163
2233
|
return `${prefix}${shortId}-${slug || "task"}`;
|
|
@@ -3508,7 +3578,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3508
3578
|
failureSummary,
|
|
3509
3579
|
...buildTokenPayload(sessionStats)
|
|
3510
3580
|
});
|
|
3511
|
-
|
|
3581
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3512
3582
|
return false;
|
|
3513
3583
|
}
|
|
3514
3584
|
log.info(TAG14, `Pushing branch ${branchName} (pre-verify)...`);
|
|
@@ -3590,7 +3660,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3590
3660
|
recoveryBranch: branchName,
|
|
3591
3661
|
...buildTokenPayload(sessionStats)
|
|
3592
3662
|
});
|
|
3593
|
-
|
|
3663
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3594
3664
|
return false;
|
|
3595
3665
|
}
|
|
3596
3666
|
log.info(TAG14, `Verification passed for #${card.short_id}`);
|
|
@@ -3646,7 +3716,7 @@ async function runCompletion(client, card, branchName, worktreePath, config, wor
|
|
|
3646
3716
|
log.warn(TAG14, `onBeforeWorktreeCleanup hook failed for #${card.short_id}: ${err instanceof Error ? err.message : err}`);
|
|
3647
3717
|
}
|
|
3648
3718
|
}
|
|
3649
|
-
|
|
3719
|
+
await teardownWorktree(client, card.id, worktreePath, branchName);
|
|
3650
3720
|
log.info(TAG14, `Completion done for #${card.short_id}${prUrl ? ` — PR: ${prUrl}` : ""}`);
|
|
3651
3721
|
return true;
|
|
3652
3722
|
}
|
|
@@ -3966,6 +4036,7 @@ class SdkAgentRunner {
|
|
|
3966
4036
|
cwd: input.cwd,
|
|
3967
4037
|
model: input.model ?? this.cfg.model,
|
|
3968
4038
|
allowedTools: allowed,
|
|
4039
|
+
...this.cfg.disallowedTools && this.cfg.disallowedTools.length > 0 ? { disallowedTools: this.cfg.disallowedTools } : {},
|
|
3969
4040
|
tools: builtinTools,
|
|
3970
4041
|
permissionMode: "dontAsk",
|
|
3971
4042
|
maxTurns: this.cfg.maxTurns,
|
|
@@ -6917,7 +6988,7 @@ async function buildPrompt(enriched, branchName, worktreePath, client, workspace
|
|
|
6917
6988
|
variant: "execute",
|
|
6918
6989
|
customConstraints: `You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
6919
6990
|
Do NOT push to main. All your work stays on \`${branchName}\`.
|
|
6920
|
-
|
|
6991
|
+
The daemon owns the run lifecycle: once your work is committed it ends the agent session, pushes the branch, and moves the card to Review for you. Do NOT call harmony_end_agent_session, do NOT start a new session, and do NOT move the card or change its column yourself. If the skill driving this work tells you to move the card or end the session as a final step, SKIP it — it is handled for you (those tools are disabled for this run). Finish the implementation, commit, and stop.`
|
|
6921
6992
|
});
|
|
6922
6993
|
log.info(TAG27, `Generated prompt for #${card.short_id} — ${result.contextSummary.memoryCount} memories, ${result.tokenEstimate} tokens`);
|
|
6923
6994
|
return result.prompt + pastEpisodesSection;
|
|
@@ -7027,7 +7098,7 @@ ${subtaskStr}
|
|
|
7027
7098
|
Include a brief currentTask description.
|
|
7028
7099
|
3. Implement the changes on branch \`${branchName}\`
|
|
7029
7100
|
4. Commit your work with clear, descriptive commit messages
|
|
7030
|
-
5. When
|
|
7101
|
+
5. When the work is committed, STOP. The daemon owns the run lifecycle: it ends the agent session, pushes the branch, and moves the card to Review for you. Do NOT call harmony_end_agent_session, do NOT start a new session, and do NOT move the card or change its column yourself — those tools are disabled for this run.
|
|
7031
7102
|
|
|
7032
7103
|
You are working in a git worktree at \`${worktreePath}\` on branch \`${branchName}\`.
|
|
7033
7104
|
Do NOT push to main. All your work stays on \`${branchName}\`.`;
|
|
@@ -7076,7 +7147,7 @@ function firstErrorMessage(evaluation) {
|
|
|
7076
7147
|
return e ? e.message : null;
|
|
7077
7148
|
}
|
|
7078
7149
|
async function advanceConvergeLoop(card, stage, stageIndex, def, evaluation, loop, deps) {
|
|
7079
|
-
const hasExitGate = loop
|
|
7150
|
+
const hasExitGate = resolveLoopExitGate(stage, loop) != null;
|
|
7080
7151
|
const gatePassed = hasExitGate ? evaluation?.passed ?? false : false;
|
|
7081
7152
|
const gateResult = !hasExitGate ? "skipped" : gatePassed ? "passed" : "failed";
|
|
7082
7153
|
const maxIterations = Math.max(1, Math.floor(loop.max_iterations) || 1);
|
|
@@ -7329,7 +7400,7 @@ function buildStagePreamble(stage) {
|
|
|
7329
7400
|
lines.push(`Hand off when done: ${summary.trim()}`);
|
|
7330
7401
|
}
|
|
7331
7402
|
}
|
|
7332
|
-
lines.push("Do only this stage's work.
|
|
7403
|
+
lines.push("Do only this stage's work, then stop. The daemon owns the run lifecycle here: it ends the agent session and performs every card move and stage advancement automatically once your stage work is done. Do NOT call `harmony_end_agent_session`, do NOT start a new session, and do NOT move the card or change its column yourself. If the skill driving this stage tells you to move the card (e.g. to Review) or end your session as a final step, SKIP that step — it is handled for you. Those tools are disabled for this run, so attempting them only wastes turns. Finish the stage's work and stop.");
|
|
7333
7404
|
if (normalizeGateSpec(stage.gate)?.kind === "review_passed") {
|
|
7334
7405
|
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.");
|
|
7335
7406
|
}
|
|
@@ -7342,6 +7413,13 @@ function buildSteeringPrompt(messages) {
|
|
|
7342
7413
|
return messages.map((m, i) => `${i + 1}. ${m}`).join(`
|
|
7343
7414
|
`);
|
|
7344
7415
|
}
|
|
7416
|
+
function computeRunSpawnGating(stageAllowedTools) {
|
|
7417
|
+
const denylist = stageDisallowedTools();
|
|
7418
|
+
return {
|
|
7419
|
+
...stageAllowedTools ? { allowedTools: stageAllowedTools } : {},
|
|
7420
|
+
...denylist ? { disallowedTools: denylist } : {}
|
|
7421
|
+
};
|
|
7422
|
+
}
|
|
7345
7423
|
|
|
7346
7424
|
class Worker {
|
|
7347
7425
|
config;
|
|
@@ -7370,6 +7448,7 @@ class Worker {
|
|
|
7370
7448
|
timedOut = false;
|
|
7371
7449
|
verificationFailed = false;
|
|
7372
7450
|
held = false;
|
|
7451
|
+
activeRunSpawnOpts = null;
|
|
7373
7452
|
completionStarted = false;
|
|
7374
7453
|
sessionId = null;
|
|
7375
7454
|
runId = null;
|
|
@@ -7449,6 +7528,7 @@ class Worker {
|
|
|
7449
7528
|
this.lastRunText = "";
|
|
7450
7529
|
this.cliSessionId = null;
|
|
7451
7530
|
this.lastDrainedSeq = 0;
|
|
7531
|
+
this.activeRunSpawnOpts = null;
|
|
7452
7532
|
this.cardId = card.id;
|
|
7453
7533
|
this.startedAt = Date.now();
|
|
7454
7534
|
this.runId = newRunId();
|
|
@@ -7569,9 +7649,10 @@ class Worker {
|
|
|
7569
7649
|
this.timedOut = true;
|
|
7570
7650
|
this.cancel();
|
|
7571
7651
|
}, this.config.maxTimeout);
|
|
7652
|
+
this.activeRunSpawnOpts = computeRunSpawnGating(stageCtx.kind === "run" ? stageCtx.allowedTools : null);
|
|
7572
7653
|
await this.spawnClaude(prompt, card, subtasks, {
|
|
7573
7654
|
model: this.selectImplementModel(card),
|
|
7574
|
-
...
|
|
7655
|
+
...this.activeRunSpawnOpts ?? {}
|
|
7575
7656
|
});
|
|
7576
7657
|
if (this.aborted)
|
|
7577
7658
|
return;
|
|
@@ -7646,7 +7727,7 @@ class Worker {
|
|
|
7646
7727
|
}
|
|
7647
7728
|
if (this.worktreePath) {
|
|
7648
7729
|
try {
|
|
7649
|
-
|
|
7730
|
+
await teardownWorktree(this.client, card.id, this.worktreePath, this.branchName ?? undefined);
|
|
7650
7731
|
} catch {
|
|
7651
7732
|
log.warn(this.tag, "Failed to cleanup worktree before requeue");
|
|
7652
7733
|
}
|
|
@@ -7690,7 +7771,7 @@ class Worker {
|
|
|
7690
7771
|
} else if (this.runId && this.timedOut) {
|
|
7691
7772
|
if (this.worktreePath) {
|
|
7692
7773
|
try {
|
|
7693
|
-
|
|
7774
|
+
await teardownWorktree(this.client, card.id, this.worktreePath, this.branchName ?? undefined);
|
|
7694
7775
|
} catch {
|
|
7695
7776
|
log.warn(this.tag, "Failed to cleanup worktree before requeue");
|
|
7696
7777
|
}
|
|
@@ -7780,7 +7861,7 @@ class Worker {
|
|
|
7780
7861
|
await this.cliRunner.flushFinal();
|
|
7781
7862
|
} catch {}
|
|
7782
7863
|
}
|
|
7783
|
-
this.cleanup();
|
|
7864
|
+
await this.cleanup();
|
|
7784
7865
|
this.state = "idle";
|
|
7785
7866
|
this.onDone(this);
|
|
7786
7867
|
}
|
|
@@ -7906,7 +7987,7 @@ class Worker {
|
|
|
7906
7987
|
async collectStageGateEvidence(card, stage, worktreePath, subtasks) {
|
|
7907
7988
|
try {
|
|
7908
7989
|
const loop = getStageLoop(stage);
|
|
7909
|
-
const gateSource = isConvergeLoop(loop)
|
|
7990
|
+
const gateSource = isConvergeLoop(loop) && loop ? resolveLoopExitGate(stage, loop) : stage.gate;
|
|
7910
7991
|
const gate = normalizeGateSpec(gateSource);
|
|
7911
7992
|
if (!gate) {
|
|
7912
7993
|
return null;
|
|
@@ -8204,7 +8285,8 @@ class Worker {
|
|
|
8204
8285
|
await this.spawnClaude(buildSteeringPrompt(messages.map((m) => m.text)), card, subtasks, {
|
|
8205
8286
|
model: this.selectImplementModel(card),
|
|
8206
8287
|
maxTurns: STEERING_MAX_TURNS,
|
|
8207
|
-
resumeSessionId: this.cliSessionId
|
|
8288
|
+
resumeSessionId: this.cliSessionId,
|
|
8289
|
+
...this.activeRunSpawnOpts ?? {}
|
|
8208
8290
|
});
|
|
8209
8291
|
} catch (err) {
|
|
8210
8292
|
log.warn(this.tag, `Steering resume failed (non-fatal): ${err instanceof Error ? err.message : err}`);
|
|
@@ -8232,6 +8314,7 @@ class Worker {
|
|
|
8232
8314
|
String(maxTurns),
|
|
8233
8315
|
"--allowedTools",
|
|
8234
8316
|
allowedTools,
|
|
8317
|
+
...opts.disallowedTools ? ["--disallowedTools", opts.disallowedTools] : [],
|
|
8235
8318
|
...opts.resumeSessionId ? ["--resume", opts.resumeSessionId] : [],
|
|
8236
8319
|
...this.config.claude.additionalArgs,
|
|
8237
8320
|
"--",
|
|
@@ -8319,6 +8402,7 @@ class Worker {
|
|
|
8319
8402
|
const model = opts.model ?? this.config.claude.model;
|
|
8320
8403
|
const maxTurns = opts.maxTurns ?? this.config.claude.maxTurns;
|
|
8321
8404
|
const allowedTools = (opts.allowedTools ?? IMPLEMENT_ALLOWED_TOOLS).split(",").map((t) => t.trim()).filter(Boolean);
|
|
8405
|
+
const disallowedTools = opts.disallowedTools ? opts.disallowedTools.split(",").map((t) => t.trim()).filter(Boolean) : undefined;
|
|
8322
8406
|
const initialPhase = opts.initialPhase ?? "exploring";
|
|
8323
8407
|
const sdkCfg = this.config.sdk;
|
|
8324
8408
|
log.info(this.tag, `Spawning Agent SDK runner (model=${model}, maxTurns=${maxTurns}${opts.resumeSessionId ? ", resume" : ""})`);
|
|
@@ -8338,6 +8422,7 @@ class Worker {
|
|
|
8338
8422
|
model,
|
|
8339
8423
|
maxTurns,
|
|
8340
8424
|
allowedTools,
|
|
8425
|
+
...disallowedTools ? { disallowedTools } : {},
|
|
8341
8426
|
maxBudgetUsd: sdkCfg?.maxBudgetUsd,
|
|
8342
8427
|
settingSources: sdkCfg?.settingSources,
|
|
8343
8428
|
mcpServers: sdkCfg?.mcpServers,
|
|
@@ -8404,7 +8489,7 @@ class Worker {
|
|
|
8404
8489
|
throw err;
|
|
8405
8490
|
}
|
|
8406
8491
|
}
|
|
8407
|
-
cleanup() {
|
|
8492
|
+
async cleanup() {
|
|
8408
8493
|
if (this.progressTracker) {
|
|
8409
8494
|
this.progressTracker.stop();
|
|
8410
8495
|
this.progressTracker = null;
|
|
@@ -8422,7 +8507,7 @@ class Worker {
|
|
|
8422
8507
|
}
|
|
8423
8508
|
if (this.worktreePath && (this.state === "error" || this.timedOut || this.aborted)) {
|
|
8424
8509
|
try {
|
|
8425
|
-
|
|
8510
|
+
await teardownWorktree(this.client, this.cardId, this.worktreePath, this.branchName ?? undefined);
|
|
8426
8511
|
} catch {
|
|
8427
8512
|
log.warn(this.tag, "Failed to cleanup worktree");
|
|
8428
8513
|
}
|
|
@@ -8903,7 +8988,7 @@ async function recoverRun(run, store, client, config, outcome) {
|
|
|
8903
8988
|
}
|
|
8904
8989
|
if (run.worktreePath) {
|
|
8905
8990
|
try {
|
|
8906
|
-
|
|
8991
|
+
await teardownWorktree(client, run.cardId, run.worktreePath, run.branchName ?? undefined);
|
|
8907
8992
|
outcome.actions.push("cleaned up worktree");
|
|
8908
8993
|
} catch (err) {
|
|
8909
8994
|
const msg = err instanceof Error ? err.message : String(err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.16.1",
|
|
4
4
|
"description": "Push-based agent daemon for Harmony — watches board assignments and spawns Claude CLI workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@anthropic-ai/claude-agent-sdk": "^0.3.178",
|
|
48
48
|
"@supabase/supabase-js": "2.95.3",
|
|
49
|
-
"@gethmy/mcp": "2.
|
|
49
|
+
"@gethmy/mcp": "^2.14.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@harmony/shared": "workspace:*",
|