@gethmy/agent 1.11.2 → 1.12.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 +80 -32
- package/dist/index.js +80 -32
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -390,7 +390,7 @@ var init_types = __esm(() => {
|
|
|
390
390
|
pickupColumns: ["To Do"],
|
|
391
391
|
priorityLabels: { urgent: 100, critical: 90, bug: 50 },
|
|
392
392
|
columnBoost: true,
|
|
393
|
-
runner: "
|
|
393
|
+
runner: "sdk",
|
|
394
394
|
completion: {
|
|
395
395
|
createPR: false,
|
|
396
396
|
moveToColumn: "Review",
|
|
@@ -565,7 +565,7 @@ function loadDaemonConfig() {
|
|
|
565
565
|
}
|
|
566
566
|
};
|
|
567
567
|
if (agent.runner !== "cli" && agent.runner !== "sdk") {
|
|
568
|
-
agent.runner =
|
|
568
|
+
agent.runner = DEFAULT_AGENT_CONFIG.runner;
|
|
569
569
|
}
|
|
570
570
|
return {
|
|
571
571
|
apiKey,
|
|
@@ -6227,6 +6227,15 @@ class Worker {
|
|
|
6227
6227
|
...this.runLedger()
|
|
6228
6228
|
});
|
|
6229
6229
|
} catch {}
|
|
6230
|
+
const stopCost = this.lastSessionStats?.cost;
|
|
6231
|
+
if (stopCost) {
|
|
6232
|
+
const cents = Math.round(stopCost.totalCostUsd * 100);
|
|
6233
|
+
if (cents > 0) {
|
|
6234
|
+
try {
|
|
6235
|
+
await this.stateStore.addCost(card.id, cents);
|
|
6236
|
+
} catch {}
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6230
6239
|
} else if (this.runId && this.verificationFailed) {
|
|
6231
6240
|
try {
|
|
6232
6241
|
await this.stateStore.endRun(this.runId, "paused", {
|
|
@@ -6709,7 +6718,7 @@ class Worker {
|
|
|
6709
6718
|
clearTimeout(this.timeoutTimer);
|
|
6710
6719
|
this.timeoutTimer = null;
|
|
6711
6720
|
}
|
|
6712
|
-
if (this.worktreePath && (this.state === "error" || this.timedOut)) {
|
|
6721
|
+
if (this.worktreePath && (this.state === "error" || this.timedOut || this.aborted)) {
|
|
6713
6722
|
try {
|
|
6714
6723
|
cleanupWorktree(this.worktreePath, this.branchName ?? undefined);
|
|
6715
6724
|
} catch {
|
|
@@ -7681,6 +7690,10 @@ class Watcher {
|
|
|
7681
7690
|
readyPromise = new Promise((resolve3) => {
|
|
7682
7691
|
this.readyResolve = resolve3;
|
|
7683
7692
|
});
|
|
7693
|
+
stopping = false;
|
|
7694
|
+
reconnectTimer = null;
|
|
7695
|
+
reconnectAttempts = 0;
|
|
7696
|
+
broadcastGen = 0;
|
|
7684
7697
|
get isConnected() {
|
|
7685
7698
|
return this.connected;
|
|
7686
7699
|
}
|
|
@@ -7709,7 +7722,34 @@ class Watcher {
|
|
|
7709
7722
|
}
|
|
7710
7723
|
this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
|
|
7711
7724
|
const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
|
|
7712
|
-
|
|
7725
|
+
this.subscribeBroadcast();
|
|
7726
|
+
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
7727
|
+
log.debug(TAG32, "Presence sync");
|
|
7728
|
+
}).subscribe(async (status) => {
|
|
7729
|
+
if (status === "SUBSCRIBED") {
|
|
7730
|
+
await presenceChannel.track({
|
|
7731
|
+
daemonId: this.daemonId,
|
|
7732
|
+
startedAt: new Date().toISOString(),
|
|
7733
|
+
userId: this.identity.userId,
|
|
7734
|
+
agentId: this.identity.agentId,
|
|
7735
|
+
userEmail: this.identity.userEmail,
|
|
7736
|
+
agentIdentifier: this.identity.agentIdentifier,
|
|
7737
|
+
agentName: this.identity.agentName
|
|
7738
|
+
});
|
|
7739
|
+
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7740
|
+
log.info(TAG32, "Presence tracked on board-presence channel");
|
|
7741
|
+
}
|
|
7742
|
+
this.presenceTracked = true;
|
|
7743
|
+
this.maybeResolveReady();
|
|
7744
|
+
}
|
|
7745
|
+
});
|
|
7746
|
+
this.presenceChannel = presenceChannel;
|
|
7747
|
+
}
|
|
7748
|
+
subscribeBroadcast() {
|
|
7749
|
+
if (!this.supabase)
|
|
7750
|
+
return;
|
|
7751
|
+
const gen = ++this.broadcastGen;
|
|
7752
|
+
this.channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
|
|
7713
7753
|
log.debug(TAG32, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
|
|
7714
7754
|
this.onCardBroadcast({
|
|
7715
7755
|
event: "card_update",
|
|
@@ -7730,46 +7770,54 @@ class Watcher {
|
|
|
7730
7770
|
this.onAgentCommand?.({ cardId, command });
|
|
7731
7771
|
}
|
|
7732
7772
|
}).subscribe((status) => {
|
|
7773
|
+
if (gen !== this.broadcastGen)
|
|
7774
|
+
return;
|
|
7733
7775
|
if (status === "SUBSCRIBED") {
|
|
7734
7776
|
this.connected = true;
|
|
7777
|
+
this.reconnectAttempts = 0;
|
|
7735
7778
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7736
7779
|
log.info(TAG32, "Broadcast subscription active");
|
|
7737
7780
|
}
|
|
7738
7781
|
this.maybeResolveReady();
|
|
7739
|
-
} else if (status === "CHANNEL_ERROR") {
|
|
7740
|
-
this.connected = false;
|
|
7741
|
-
log.error(TAG32, "Broadcast channel error — will rely on reconciliation");
|
|
7742
|
-
} else if (status === "TIMED_OUT") {
|
|
7743
|
-
this.connected = false;
|
|
7744
|
-
log.warn(TAG32, "Broadcast subscription timed out — retrying...");
|
|
7745
|
-
} else if (status === "CLOSED") {
|
|
7782
|
+
} else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
|
|
7746
7783
|
this.connected = false;
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
7751
|
-
log.debug(TAG32, "Presence sync");
|
|
7752
|
-
}).subscribe(async (status) => {
|
|
7753
|
-
if (status === "SUBSCRIBED") {
|
|
7754
|
-
await presenceChannel.track({
|
|
7755
|
-
daemonId: this.daemonId,
|
|
7756
|
-
startedAt: new Date().toISOString(),
|
|
7757
|
-
userId: this.identity.userId,
|
|
7758
|
-
agentId: this.identity.agentId,
|
|
7759
|
-
userEmail: this.identity.userEmail,
|
|
7760
|
-
agentIdentifier: this.identity.agentIdentifier,
|
|
7761
|
-
agentName: this.identity.agentName
|
|
7762
|
-
});
|
|
7763
|
-
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7764
|
-
log.info(TAG32, "Presence tracked on board-presence channel");
|
|
7784
|
+
if (!this.stopping) {
|
|
7785
|
+
log.warn(TAG32, `Broadcast subscription ${status} — scheduling reconnect`);
|
|
7786
|
+
this.scheduleReconnect();
|
|
7765
7787
|
}
|
|
7766
|
-
this.presenceTracked = true;
|
|
7767
|
-
this.maybeResolveReady();
|
|
7768
7788
|
}
|
|
7769
7789
|
});
|
|
7770
|
-
|
|
7790
|
+
}
|
|
7791
|
+
scheduleReconnect() {
|
|
7792
|
+
if (this.stopping || this.reconnectTimer)
|
|
7793
|
+
return;
|
|
7794
|
+
const delay = Math.min(30000, 1000 * 2 ** this.reconnectAttempts);
|
|
7795
|
+
this.reconnectAttempts++;
|
|
7796
|
+
this.reconnectTimer = setTimeout(() => {
|
|
7797
|
+
this.reconnectTimer = null;
|
|
7798
|
+
this.reconnectBroadcast();
|
|
7799
|
+
}, delay);
|
|
7800
|
+
}
|
|
7801
|
+
async reconnectBroadcast() {
|
|
7802
|
+
if (this.stopping || !this.supabase)
|
|
7803
|
+
return;
|
|
7804
|
+
log.warn(TAG32, `Reconnecting broadcast subscription (attempt ${this.reconnectAttempts})`);
|
|
7805
|
+
if (this.channel) {
|
|
7806
|
+
const old = this.channel;
|
|
7807
|
+
this.channel = null;
|
|
7808
|
+
this.broadcastGen++;
|
|
7809
|
+
try {
|
|
7810
|
+
await this.supabase.removeChannel(old);
|
|
7811
|
+
} catch {}
|
|
7812
|
+
}
|
|
7813
|
+
this.subscribeBroadcast();
|
|
7771
7814
|
}
|
|
7772
7815
|
async stop() {
|
|
7816
|
+
this.stopping = true;
|
|
7817
|
+
if (this.reconnectTimer) {
|
|
7818
|
+
clearTimeout(this.reconnectTimer);
|
|
7819
|
+
this.reconnectTimer = null;
|
|
7820
|
+
}
|
|
7773
7821
|
if (this.presenceChannel) {
|
|
7774
7822
|
await this.supabase?.removeChannel(this.presenceChannel);
|
|
7775
7823
|
this.presenceChannel = null;
|
package/dist/index.js
CHANGED
|
@@ -389,7 +389,7 @@ var init_types = __esm(() => {
|
|
|
389
389
|
pickupColumns: ["To Do"],
|
|
390
390
|
priorityLabels: { urgent: 100, critical: 90, bug: 50 },
|
|
391
391
|
columnBoost: true,
|
|
392
|
-
runner: "
|
|
392
|
+
runner: "sdk",
|
|
393
393
|
completion: {
|
|
394
394
|
createPR: false,
|
|
395
395
|
moveToColumn: "Review",
|
|
@@ -564,7 +564,7 @@ function loadDaemonConfig() {
|
|
|
564
564
|
}
|
|
565
565
|
};
|
|
566
566
|
if (agent.runner !== "cli" && agent.runner !== "sdk") {
|
|
567
|
-
agent.runner =
|
|
567
|
+
agent.runner = DEFAULT_AGENT_CONFIG.runner;
|
|
568
568
|
}
|
|
569
569
|
return {
|
|
570
570
|
apiKey,
|
|
@@ -6226,6 +6226,15 @@ class Worker {
|
|
|
6226
6226
|
...this.runLedger()
|
|
6227
6227
|
});
|
|
6228
6228
|
} catch {}
|
|
6229
|
+
const stopCost = this.lastSessionStats?.cost;
|
|
6230
|
+
if (stopCost) {
|
|
6231
|
+
const cents = Math.round(stopCost.totalCostUsd * 100);
|
|
6232
|
+
if (cents > 0) {
|
|
6233
|
+
try {
|
|
6234
|
+
await this.stateStore.addCost(card.id, cents);
|
|
6235
|
+
} catch {}
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6229
6238
|
} else if (this.runId && this.verificationFailed) {
|
|
6230
6239
|
try {
|
|
6231
6240
|
await this.stateStore.endRun(this.runId, "paused", {
|
|
@@ -6708,7 +6717,7 @@ class Worker {
|
|
|
6708
6717
|
clearTimeout(this.timeoutTimer);
|
|
6709
6718
|
this.timeoutTimer = null;
|
|
6710
6719
|
}
|
|
6711
|
-
if (this.worktreePath && (this.state === "error" || this.timedOut)) {
|
|
6720
|
+
if (this.worktreePath && (this.state === "error" || this.timedOut || this.aborted)) {
|
|
6712
6721
|
try {
|
|
6713
6722
|
cleanupWorktree(this.worktreePath, this.branchName ?? undefined);
|
|
6714
6723
|
} catch {
|
|
@@ -7680,6 +7689,10 @@ class Watcher {
|
|
|
7680
7689
|
readyPromise = new Promise((resolve3) => {
|
|
7681
7690
|
this.readyResolve = resolve3;
|
|
7682
7691
|
});
|
|
7692
|
+
stopping = false;
|
|
7693
|
+
reconnectTimer = null;
|
|
7694
|
+
reconnectAttempts = 0;
|
|
7695
|
+
broadcastGen = 0;
|
|
7683
7696
|
get isConnected() {
|
|
7684
7697
|
return this.connected;
|
|
7685
7698
|
}
|
|
@@ -7708,7 +7721,34 @@ class Watcher {
|
|
|
7708
7721
|
}
|
|
7709
7722
|
this.supabase = createClient(this.credentials.supabaseUrl, this.credentials.supabaseAnonKey);
|
|
7710
7723
|
const presenceChannel = this.supabase.channel(`board-presence-${this.projectId}`);
|
|
7711
|
-
|
|
7724
|
+
this.subscribeBroadcast();
|
|
7725
|
+
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
7726
|
+
log.debug(TAG32, "Presence sync");
|
|
7727
|
+
}).subscribe(async (status) => {
|
|
7728
|
+
if (status === "SUBSCRIBED") {
|
|
7729
|
+
await presenceChannel.track({
|
|
7730
|
+
daemonId: this.daemonId,
|
|
7731
|
+
startedAt: new Date().toISOString(),
|
|
7732
|
+
userId: this.identity.userId,
|
|
7733
|
+
agentId: this.identity.agentId,
|
|
7734
|
+
userEmail: this.identity.userEmail,
|
|
7735
|
+
agentIdentifier: this.identity.agentIdentifier,
|
|
7736
|
+
agentName: this.identity.agentName
|
|
7737
|
+
});
|
|
7738
|
+
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7739
|
+
log.info(TAG32, "Presence tracked on board-presence channel");
|
|
7740
|
+
}
|
|
7741
|
+
this.presenceTracked = true;
|
|
7742
|
+
this.maybeResolveReady();
|
|
7743
|
+
}
|
|
7744
|
+
});
|
|
7745
|
+
this.presenceChannel = presenceChannel;
|
|
7746
|
+
}
|
|
7747
|
+
subscribeBroadcast() {
|
|
7748
|
+
if (!this.supabase)
|
|
7749
|
+
return;
|
|
7750
|
+
const gen = ++this.broadcastGen;
|
|
7751
|
+
this.channel = this.supabase.channel(`board-${this.projectId}`).on("broadcast", { event: "card_update" }, (msg) => {
|
|
7712
7752
|
log.debug(TAG32, `Broadcast: card_update ${JSON.stringify(msg.payload)}`);
|
|
7713
7753
|
this.onCardBroadcast({
|
|
7714
7754
|
event: "card_update",
|
|
@@ -7729,46 +7769,54 @@ class Watcher {
|
|
|
7729
7769
|
this.onAgentCommand?.({ cardId, command });
|
|
7730
7770
|
}
|
|
7731
7771
|
}).subscribe((status) => {
|
|
7772
|
+
if (gen !== this.broadcastGen)
|
|
7773
|
+
return;
|
|
7732
7774
|
if (status === "SUBSCRIBED") {
|
|
7733
7775
|
this.connected = true;
|
|
7776
|
+
this.reconnectAttempts = 0;
|
|
7734
7777
|
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7735
7778
|
log.info(TAG32, "Broadcast subscription active");
|
|
7736
7779
|
}
|
|
7737
7780
|
this.maybeResolveReady();
|
|
7738
|
-
} else if (status === "CHANNEL_ERROR") {
|
|
7739
|
-
this.connected = false;
|
|
7740
|
-
log.error(TAG32, "Broadcast channel error — will rely on reconciliation");
|
|
7741
|
-
} else if (status === "TIMED_OUT") {
|
|
7742
|
-
this.connected = false;
|
|
7743
|
-
log.warn(TAG32, "Broadcast subscription timed out — retrying...");
|
|
7744
|
-
} else if (status === "CLOSED") {
|
|
7781
|
+
} else if (status === "CHANNEL_ERROR" || status === "TIMED_OUT" || status === "CLOSED") {
|
|
7745
7782
|
this.connected = false;
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
presenceChannel.on("presence", { event: "sync" }, () => {
|
|
7750
|
-
log.debug(TAG32, "Presence sync");
|
|
7751
|
-
}).subscribe(async (status) => {
|
|
7752
|
-
if (status === "SUBSCRIBED") {
|
|
7753
|
-
await presenceChannel.track({
|
|
7754
|
-
daemonId: this.daemonId,
|
|
7755
|
-
startedAt: new Date().toISOString(),
|
|
7756
|
-
userId: this.identity.userId,
|
|
7757
|
-
agentId: this.identity.agentId,
|
|
7758
|
-
userEmail: this.identity.userEmail,
|
|
7759
|
-
agentIdentifier: this.identity.agentIdentifier,
|
|
7760
|
-
agentName: this.identity.agentName
|
|
7761
|
-
});
|
|
7762
|
-
if (!isPretty() || !this.suppressStartupLogs) {
|
|
7763
|
-
log.info(TAG32, "Presence tracked on board-presence channel");
|
|
7783
|
+
if (!this.stopping) {
|
|
7784
|
+
log.warn(TAG32, `Broadcast subscription ${status} — scheduling reconnect`);
|
|
7785
|
+
this.scheduleReconnect();
|
|
7764
7786
|
}
|
|
7765
|
-
this.presenceTracked = true;
|
|
7766
|
-
this.maybeResolveReady();
|
|
7767
7787
|
}
|
|
7768
7788
|
});
|
|
7769
|
-
|
|
7789
|
+
}
|
|
7790
|
+
scheduleReconnect() {
|
|
7791
|
+
if (this.stopping || this.reconnectTimer)
|
|
7792
|
+
return;
|
|
7793
|
+
const delay = Math.min(30000, 1000 * 2 ** this.reconnectAttempts);
|
|
7794
|
+
this.reconnectAttempts++;
|
|
7795
|
+
this.reconnectTimer = setTimeout(() => {
|
|
7796
|
+
this.reconnectTimer = null;
|
|
7797
|
+
this.reconnectBroadcast();
|
|
7798
|
+
}, delay);
|
|
7799
|
+
}
|
|
7800
|
+
async reconnectBroadcast() {
|
|
7801
|
+
if (this.stopping || !this.supabase)
|
|
7802
|
+
return;
|
|
7803
|
+
log.warn(TAG32, `Reconnecting broadcast subscription (attempt ${this.reconnectAttempts})`);
|
|
7804
|
+
if (this.channel) {
|
|
7805
|
+
const old = this.channel;
|
|
7806
|
+
this.channel = null;
|
|
7807
|
+
this.broadcastGen++;
|
|
7808
|
+
try {
|
|
7809
|
+
await this.supabase.removeChannel(old);
|
|
7810
|
+
} catch {}
|
|
7811
|
+
}
|
|
7812
|
+
this.subscribeBroadcast();
|
|
7770
7813
|
}
|
|
7771
7814
|
async stop() {
|
|
7815
|
+
this.stopping = true;
|
|
7816
|
+
if (this.reconnectTimer) {
|
|
7817
|
+
clearTimeout(this.reconnectTimer);
|
|
7818
|
+
this.reconnectTimer = null;
|
|
7819
|
+
}
|
|
7772
7820
|
if (this.presenceChannel) {
|
|
7773
7821
|
await this.supabase?.removeChannel(this.presenceChannel);
|
|
7774
7822
|
this.presenceChannel = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/agent",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.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.11.1"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@harmony/shared": "workspace:*",
|