@gethmy/agent 1.42.0 → 1.44.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 +62 -7
- package/dist/index.js +62 -7
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -7361,7 +7361,7 @@ function renderPreviousAttemptsSection(failures) {
|
|
|
7361
7361
|
].join(`
|
|
7362
7362
|
`);
|
|
7363
7363
|
}
|
|
7364
|
-
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId, onRecallOutcome) {
|
|
7364
|
+
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId, onRecallOutcome, onPromptFallback) {
|
|
7365
7365
|
const { card } = enriched;
|
|
7366
7366
|
const referenceSection = await renderReferenceSection(client, card.title, card.description ?? "", workspaceId, projectId, onRecallOutcome);
|
|
7367
7367
|
try {
|
|
@@ -7379,6 +7379,7 @@ The daemon owns the run lifecycle: once your work is committed it ends the agent
|
|
|
7379
7379
|
} catch (err) {
|
|
7380
7380
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7381
7381
|
log22.warn(TAG20, `Failed to generate prompt via API, using fallback: ${msg}`);
|
|
7382
|
+
onPromptFallback?.();
|
|
7382
7383
|
const commentsSection = await renderCommentsSection(client, card.id);
|
|
7383
7384
|
return buildFallbackPrompt(enriched, branchName, worktreePath) + commentsSection + referenceSection;
|
|
7384
7385
|
}
|
|
@@ -7411,9 +7412,14 @@ async function renderReferenceSection(client, title, description, workspaceId, p
|
|
|
7411
7412
|
const query = buildMemoryQuery(title, description);
|
|
7412
7413
|
const { entities } = await client.searchMemoryEntities(workspaceId, query, {
|
|
7413
7414
|
project_id: projectId,
|
|
7414
|
-
limit: 5
|
|
7415
|
+
limit: 5,
|
|
7416
|
+
consumer: "agent-reference"
|
|
7417
|
+
});
|
|
7418
|
+
onOutcome?.({
|
|
7419
|
+
flavour: "reference",
|
|
7420
|
+
count: entities.length,
|
|
7421
|
+
candidateCount: entities.length
|
|
7415
7422
|
});
|
|
7416
|
-
onOutcome?.({ flavour: "reference", count: entities.length });
|
|
7417
7423
|
if (entities.length === 0)
|
|
7418
7424
|
return "";
|
|
7419
7425
|
const bullets = entities.map((entity) => {
|
|
@@ -8612,6 +8618,14 @@ class StateStore {
|
|
|
8612
8618
|
getCard(cardId) {
|
|
8613
8619
|
return this.state.cards.find((c) => c.cardId === cardId) ?? null;
|
|
8614
8620
|
}
|
|
8621
|
+
async forgetCard(cardId) {
|
|
8622
|
+
const before = this.state.cards.length;
|
|
8623
|
+
this.state.cards = this.state.cards.filter((c) => c.cardId !== cardId);
|
|
8624
|
+
if (this.state.cards.length === before)
|
|
8625
|
+
return false;
|
|
8626
|
+
await this.persist();
|
|
8627
|
+
return true;
|
|
8628
|
+
}
|
|
8615
8629
|
async incrementAttempt(cardId) {
|
|
8616
8630
|
const rec = this.ensureCard(cardId);
|
|
8617
8631
|
rec.attempts += 1;
|
|
@@ -12671,11 +12685,16 @@ class Worker {
|
|
|
12671
12685
|
if (worktreePath) {
|
|
12672
12686
|
commitUncommittedChanges(worktreePath, card);
|
|
12673
12687
|
if (this.branchName) {
|
|
12688
|
+
let pushed = false;
|
|
12674
12689
|
try {
|
|
12675
12690
|
pushBranch3(this.branchName, worktreePath);
|
|
12691
|
+
pushed = true;
|
|
12676
12692
|
} catch (err) {
|
|
12677
12693
|
log34.error(this.tag, `push after the motor stage "${ctx.stage.name}" failed for ${this.branchName}: ${err instanceof Error ? err.message : err}`);
|
|
12678
12694
|
}
|
|
12695
|
+
if (pushed) {
|
|
12696
|
+
await recordBranchProvenance(this.client, card, this.branchName);
|
|
12697
|
+
}
|
|
12679
12698
|
}
|
|
12680
12699
|
}
|
|
12681
12700
|
const completionColumn = this.config.completion.moveToColumn;
|
|
@@ -12699,8 +12718,11 @@ class Worker {
|
|
|
12699
12718
|
}
|
|
12700
12719
|
async buildFreshRunPrompt(enriched, card, stageCtx, continuesPushedWork, resuming) {
|
|
12701
12720
|
const recallOutcomes = [];
|
|
12702
|
-
|
|
12703
|
-
this.
|
|
12721
|
+
let promptFallback = false;
|
|
12722
|
+
const basePrompt = await buildPrompt(enriched, this.branchName, this.worktreePath, this.client, this.workspaceId, this.projectId, (outcome) => recallOutcomes.push(outcome), () => {
|
|
12723
|
+
promptFallback = true;
|
|
12724
|
+
});
|
|
12725
|
+
this.recordPromptAssembled(recallOutcomes, promptFallback);
|
|
12704
12726
|
let prompt = basePrompt;
|
|
12705
12727
|
if (stageCtx.kind === "run") {
|
|
12706
12728
|
const loop = getStageLoop(stageCtx.stage);
|
|
@@ -12910,16 +12932,20 @@ ${prompt}`;
|
|
|
12910
12932
|
}
|
|
12911
12933
|
return outcome;
|
|
12912
12934
|
}
|
|
12913
|
-
recordPromptAssembled(outcomes) {
|
|
12914
|
-
const
|
|
12935
|
+
recordPromptAssembled(outcomes, promptFallback = false) {
|
|
12936
|
+
const outcomeOf = (flavour) => outcomes.find((o) => o.flavour === flavour);
|
|
12937
|
+
const countOf = (flavour) => outcomeOf(flavour)?.count;
|
|
12915
12938
|
const failures = outcomes.flatMap((o) => o.failure ? [{ flavour: o.flavour, message: o.failure.slice(0, 300) }] : []);
|
|
12916
12939
|
const episodeCount = countOf("past_episodes");
|
|
12940
|
+
const candidateCount = outcomeOf("reference")?.candidateCount;
|
|
12917
12941
|
this.cliRunner?.record({
|
|
12918
12942
|
kind: "prompt_assembled",
|
|
12919
12943
|
source: "system",
|
|
12920
12944
|
payload: {
|
|
12921
12945
|
...episodeCount === undefined ? {} : { episodeCount },
|
|
12922
12946
|
referenceCount: countOf("reference") ?? 0,
|
|
12947
|
+
...candidateCount === undefined ? {} : { candidateCount },
|
|
12948
|
+
...promptFallback ? { promptFallback: true } : {},
|
|
12923
12949
|
...failures.length > 0 ? { recallFailures: failures } : {}
|
|
12924
12950
|
}
|
|
12925
12951
|
});
|
|
@@ -13701,6 +13727,12 @@ import {
|
|
|
13701
13727
|
describeApiError as describeApiError3,
|
|
13702
13728
|
log as log36
|
|
13703
13729
|
} from "@gethmy/harness";
|
|
13730
|
+
function httpStatusOf(err) {
|
|
13731
|
+
if (typeof err !== "object" || err === null)
|
|
13732
|
+
return;
|
|
13733
|
+
const status = own(err, "status");
|
|
13734
|
+
return typeof status === "number" ? status : undefined;
|
|
13735
|
+
}
|
|
13704
13736
|
async function routeBudgetDecision(d, run, actions, cardId) {
|
|
13705
13737
|
if (!run) {
|
|
13706
13738
|
if (!cardId)
|
|
@@ -14081,6 +14113,10 @@ class Pool {
|
|
|
14081
14113
|
try {
|
|
14082
14114
|
({ decisions } = await this.client.getBudgetDecisions(cardId, new Date(sinceMs).toISOString()));
|
|
14083
14115
|
} catch (err) {
|
|
14116
|
+
if (httpStatusOf(err) === 404) {
|
|
14117
|
+
await this.forgetDeletedCard(cardId, run);
|
|
14118
|
+
return;
|
|
14119
|
+
}
|
|
14084
14120
|
log36.warn(TAG32, `getBudgetDecisions failed for ${cardId}: ${err}`);
|
|
14085
14121
|
return;
|
|
14086
14122
|
}
|
|
@@ -14105,6 +14141,25 @@ class Pool {
|
|
|
14105
14141
|
this.draining.delete(cardId);
|
|
14106
14142
|
}
|
|
14107
14143
|
}
|
|
14144
|
+
async forgetDeletedCard(cardId, run) {
|
|
14145
|
+
if (run) {
|
|
14146
|
+
try {
|
|
14147
|
+
await this.stateStore.endRun(run.runId, "failed", {
|
|
14148
|
+
errorMessage: "card deleted"
|
|
14149
|
+
});
|
|
14150
|
+
} catch (err) {
|
|
14151
|
+
log36.warn(TAG32, `Failed to end the parked run for the deleted card ${cardId}: ${err}`);
|
|
14152
|
+
}
|
|
14153
|
+
}
|
|
14154
|
+
try {
|
|
14155
|
+
const dropped = await this.stateStore.forgetCard(cardId);
|
|
14156
|
+
if (dropped) {
|
|
14157
|
+
log36.info(TAG32, `Card ${cardId} no longer exists on the server — dropped from the local ledger; its budget question can never be answered`);
|
|
14158
|
+
}
|
|
14159
|
+
} catch (err) {
|
|
14160
|
+
log36.warn(TAG32, `Failed to drop the deleted card ${cardId}: ${err}`);
|
|
14161
|
+
}
|
|
14162
|
+
}
|
|
14108
14163
|
async releaseExpiredPark(run) {
|
|
14109
14164
|
try {
|
|
14110
14165
|
await this.client.addComment(run.cardId, formatExpiredParkComment({
|
package/dist/index.js
CHANGED
|
@@ -7360,7 +7360,7 @@ function renderPreviousAttemptsSection(failures) {
|
|
|
7360
7360
|
].join(`
|
|
7361
7361
|
`);
|
|
7362
7362
|
}
|
|
7363
|
-
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId, onRecallOutcome) {
|
|
7363
|
+
async function buildPrompt(enriched, branchName, worktreePath, client, workspaceId, projectId, onRecallOutcome, onPromptFallback) {
|
|
7364
7364
|
const { card } = enriched;
|
|
7365
7365
|
const referenceSection = await renderReferenceSection(client, card.title, card.description ?? "", workspaceId, projectId, onRecallOutcome);
|
|
7366
7366
|
try {
|
|
@@ -7378,6 +7378,7 @@ The daemon owns the run lifecycle: once your work is committed it ends the agent
|
|
|
7378
7378
|
} catch (err) {
|
|
7379
7379
|
const msg = err instanceof Error ? err.message : String(err);
|
|
7380
7380
|
log22.warn(TAG20, `Failed to generate prompt via API, using fallback: ${msg}`);
|
|
7381
|
+
onPromptFallback?.();
|
|
7381
7382
|
const commentsSection = await renderCommentsSection(client, card.id);
|
|
7382
7383
|
return buildFallbackPrompt(enriched, branchName, worktreePath) + commentsSection + referenceSection;
|
|
7383
7384
|
}
|
|
@@ -7410,9 +7411,14 @@ async function renderReferenceSection(client, title, description, workspaceId, p
|
|
|
7410
7411
|
const query = buildMemoryQuery(title, description);
|
|
7411
7412
|
const { entities } = await client.searchMemoryEntities(workspaceId, query, {
|
|
7412
7413
|
project_id: projectId,
|
|
7413
|
-
limit: 5
|
|
7414
|
+
limit: 5,
|
|
7415
|
+
consumer: "agent-reference"
|
|
7416
|
+
});
|
|
7417
|
+
onOutcome?.({
|
|
7418
|
+
flavour: "reference",
|
|
7419
|
+
count: entities.length,
|
|
7420
|
+
candidateCount: entities.length
|
|
7414
7421
|
});
|
|
7415
|
-
onOutcome?.({ flavour: "reference", count: entities.length });
|
|
7416
7422
|
if (entities.length === 0)
|
|
7417
7423
|
return "";
|
|
7418
7424
|
const bullets = entities.map((entity) => {
|
|
@@ -8611,6 +8617,14 @@ class StateStore {
|
|
|
8611
8617
|
getCard(cardId) {
|
|
8612
8618
|
return this.state.cards.find((c) => c.cardId === cardId) ?? null;
|
|
8613
8619
|
}
|
|
8620
|
+
async forgetCard(cardId) {
|
|
8621
|
+
const before = this.state.cards.length;
|
|
8622
|
+
this.state.cards = this.state.cards.filter((c) => c.cardId !== cardId);
|
|
8623
|
+
if (this.state.cards.length === before)
|
|
8624
|
+
return false;
|
|
8625
|
+
await this.persist();
|
|
8626
|
+
return true;
|
|
8627
|
+
}
|
|
8614
8628
|
async incrementAttempt(cardId) {
|
|
8615
8629
|
const rec = this.ensureCard(cardId);
|
|
8616
8630
|
rec.attempts += 1;
|
|
@@ -12670,11 +12684,16 @@ class Worker {
|
|
|
12670
12684
|
if (worktreePath) {
|
|
12671
12685
|
commitUncommittedChanges(worktreePath, card);
|
|
12672
12686
|
if (this.branchName) {
|
|
12687
|
+
let pushed = false;
|
|
12673
12688
|
try {
|
|
12674
12689
|
pushBranch3(this.branchName, worktreePath);
|
|
12690
|
+
pushed = true;
|
|
12675
12691
|
} catch (err) {
|
|
12676
12692
|
log34.error(this.tag, `push after the motor stage "${ctx.stage.name}" failed for ${this.branchName}: ${err instanceof Error ? err.message : err}`);
|
|
12677
12693
|
}
|
|
12694
|
+
if (pushed) {
|
|
12695
|
+
await recordBranchProvenance(this.client, card, this.branchName);
|
|
12696
|
+
}
|
|
12678
12697
|
}
|
|
12679
12698
|
}
|
|
12680
12699
|
const completionColumn = this.config.completion.moveToColumn;
|
|
@@ -12698,8 +12717,11 @@ class Worker {
|
|
|
12698
12717
|
}
|
|
12699
12718
|
async buildFreshRunPrompt(enriched, card, stageCtx, continuesPushedWork, resuming) {
|
|
12700
12719
|
const recallOutcomes = [];
|
|
12701
|
-
|
|
12702
|
-
this.
|
|
12720
|
+
let promptFallback = false;
|
|
12721
|
+
const basePrompt = await buildPrompt(enriched, this.branchName, this.worktreePath, this.client, this.workspaceId, this.projectId, (outcome) => recallOutcomes.push(outcome), () => {
|
|
12722
|
+
promptFallback = true;
|
|
12723
|
+
});
|
|
12724
|
+
this.recordPromptAssembled(recallOutcomes, promptFallback);
|
|
12703
12725
|
let prompt = basePrompt;
|
|
12704
12726
|
if (stageCtx.kind === "run") {
|
|
12705
12727
|
const loop = getStageLoop(stageCtx.stage);
|
|
@@ -12909,16 +12931,20 @@ ${prompt}`;
|
|
|
12909
12931
|
}
|
|
12910
12932
|
return outcome;
|
|
12911
12933
|
}
|
|
12912
|
-
recordPromptAssembled(outcomes) {
|
|
12913
|
-
const
|
|
12934
|
+
recordPromptAssembled(outcomes, promptFallback = false) {
|
|
12935
|
+
const outcomeOf = (flavour) => outcomes.find((o) => o.flavour === flavour);
|
|
12936
|
+
const countOf = (flavour) => outcomeOf(flavour)?.count;
|
|
12914
12937
|
const failures = outcomes.flatMap((o) => o.failure ? [{ flavour: o.flavour, message: o.failure.slice(0, 300) }] : []);
|
|
12915
12938
|
const episodeCount = countOf("past_episodes");
|
|
12939
|
+
const candidateCount = outcomeOf("reference")?.candidateCount;
|
|
12916
12940
|
this.cliRunner?.record({
|
|
12917
12941
|
kind: "prompt_assembled",
|
|
12918
12942
|
source: "system",
|
|
12919
12943
|
payload: {
|
|
12920
12944
|
...episodeCount === undefined ? {} : { episodeCount },
|
|
12921
12945
|
referenceCount: countOf("reference") ?? 0,
|
|
12946
|
+
...candidateCount === undefined ? {} : { candidateCount },
|
|
12947
|
+
...promptFallback ? { promptFallback: true } : {},
|
|
12922
12948
|
...failures.length > 0 ? { recallFailures: failures } : {}
|
|
12923
12949
|
}
|
|
12924
12950
|
});
|
|
@@ -13700,6 +13726,12 @@ import {
|
|
|
13700
13726
|
describeApiError as describeApiError3,
|
|
13701
13727
|
log as log36
|
|
13702
13728
|
} from "@gethmy/harness";
|
|
13729
|
+
function httpStatusOf(err) {
|
|
13730
|
+
if (typeof err !== "object" || err === null)
|
|
13731
|
+
return;
|
|
13732
|
+
const status = own(err, "status");
|
|
13733
|
+
return typeof status === "number" ? status : undefined;
|
|
13734
|
+
}
|
|
13703
13735
|
async function routeBudgetDecision(d, run, actions, cardId) {
|
|
13704
13736
|
if (!run) {
|
|
13705
13737
|
if (!cardId)
|
|
@@ -14080,6 +14112,10 @@ class Pool {
|
|
|
14080
14112
|
try {
|
|
14081
14113
|
({ decisions } = await this.client.getBudgetDecisions(cardId, new Date(sinceMs).toISOString()));
|
|
14082
14114
|
} catch (err) {
|
|
14115
|
+
if (httpStatusOf(err) === 404) {
|
|
14116
|
+
await this.forgetDeletedCard(cardId, run);
|
|
14117
|
+
return;
|
|
14118
|
+
}
|
|
14083
14119
|
log36.warn(TAG32, `getBudgetDecisions failed for ${cardId}: ${err}`);
|
|
14084
14120
|
return;
|
|
14085
14121
|
}
|
|
@@ -14104,6 +14140,25 @@ class Pool {
|
|
|
14104
14140
|
this.draining.delete(cardId);
|
|
14105
14141
|
}
|
|
14106
14142
|
}
|
|
14143
|
+
async forgetDeletedCard(cardId, run) {
|
|
14144
|
+
if (run) {
|
|
14145
|
+
try {
|
|
14146
|
+
await this.stateStore.endRun(run.runId, "failed", {
|
|
14147
|
+
errorMessage: "card deleted"
|
|
14148
|
+
});
|
|
14149
|
+
} catch (err) {
|
|
14150
|
+
log36.warn(TAG32, `Failed to end the parked run for the deleted card ${cardId}: ${err}`);
|
|
14151
|
+
}
|
|
14152
|
+
}
|
|
14153
|
+
try {
|
|
14154
|
+
const dropped = await this.stateStore.forgetCard(cardId);
|
|
14155
|
+
if (dropped) {
|
|
14156
|
+
log36.info(TAG32, `Card ${cardId} no longer exists on the server — dropped from the local ledger; its budget question can never be answered`);
|
|
14157
|
+
}
|
|
14158
|
+
} catch (err) {
|
|
14159
|
+
log36.warn(TAG32, `Failed to drop the deleted card ${cardId}: ${err}`);
|
|
14160
|
+
}
|
|
14161
|
+
}
|
|
14107
14162
|
async releaseExpiredPark(run) {
|
|
14108
14163
|
try {
|
|
14109
14164
|
await this.client.addComment(run.cardId, formatExpiredParkComment({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.44.0",
|
|
4
4
|
"description": "Push-based agent daemon for Harmony — picks up assigned cards, builds them in isolated git worktrees, and opens PRs. It runs where you put it.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@anthropic-ai/claude-agent-sdk": "^0.3.178",
|
|
48
|
-
"@gethmy/harness": "1.
|
|
49
|
-
"@gethmy/mcp": "3.
|
|
48
|
+
"@gethmy/harness": "1.17.0",
|
|
49
|
+
"@gethmy/mcp": "3.13.0",
|
|
50
50
|
"@supabase/supabase-js": "2.95.3"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|