From b5f75c6c659d03b56c73d1bd93e66c8a1835e760 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Sun, 28 Jun 2026 00:29:59 +0200 Subject: [PATCH] =?UTF-8?q?fix(update):=20harden=20auto-update=20=E2=80=94?= =?UTF-8?q?=20fire=20`agenthub=20update`=20directly=20on=20tick?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old auto path checked `HEAD..origin/branch` right after kicking off a background fetch, so it read a stale origin ref and only fired on the SECOND invocation (and never if the agent sat in one long-running `work`). Now: when AGENTHUB_AUTO_UPDATE is set, a throttled tick (≤1h) spawns `agenthub update` directly in the background. update does its OWN fresh fetch + reset + build and no-ops when current — so it self-heals on the FIRST invocation, no stale-ref lag. Notify-only path (no flag) unchanged. Removed the now-unused cooldown const. 122/122 green. Bump 0.6.0 -> 0.6.1. Co-Authored-By: Claude Opus 4.8 --- package.json | 2 +- src/cli/commands/update.ts | 41 ++++++++++++++++++++------------------ src/cli/index.ts | 2 +- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/package.json b/package.json index 1862365..ab6ea34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "agenthub", - "version": "0.6.0", + "version": "0.6.1", "description": "Local coordination layer for AI coding agents", "type": "module", "main": "./dist/index.js", diff --git a/src/cli/commands/update.ts b/src/cli/commands/update.ts index bd49dfe..6f43737 100644 --- a/src/cli/commands/update.ts +++ b/src/cli/commands/update.ts @@ -5,11 +5,10 @@ import { dirname, join, parse } from 'node:path'; import { homedir } from 'node:os'; const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; -// In auto-update mode we refresh more often so a freshly pushed version is -// picked up within the hour, and we cool down between background updates so we -// never spawn a second reset+build while one is still running. +// In auto-update mode we run an actual background `agenthub update` at most this +// often (it self-no-ops when current), so a freshly pushed version is picked up +// within the hour without spawning competing reset+builds. const AUTO_CHECK_INTERVAL_MS = 60 * 60 * 1000; -const AUTO_UPDATE_COOLDOWN_MS = 10 * 60 * 1000; /** Opt-in: set AGENTHUB_AUTO_UPDATE=1 to self-update in the background. */ function autoUpdateEnabled(): boolean { @@ -180,11 +179,26 @@ export function maybeNotifyUpdate(rootOverride?: string): void { if (!root || !existsSync(join(root, '.git'))) return; const branch = currentBranch(root); - const auto = autoUpdateEnabled(); + // Auto-update: on a throttled tick, fire `agenthub update` directly in the + // background. It does its OWN fresh fetch + reset + build and no-ops when + // already current — so it self-heals on the FIRST invocation. (The old + // path checked a possibly-stale origin ref, so it lagged a fetch cycle and + // took two invocations to ever fire.) + if (autoUpdateEnabled()) { + const stamp = stampPath('last-auto-update'); + if (Date.now() - readStamp(stamp) > AUTO_CHECK_INTERVAL_MS) { + writeStamp(stamp); + process.stderr.write('\n🔄 AgentHub: checking for updates in the background…\n\n'); + spawnBackgroundUpdate(root); + } + return; + } + + // Notify-only: throttled background fetch keeps the behind-count fresh, + // then a one-line hint if the install is behind. const stamp = stampPath('last-update-check'); - const interval = auto ? AUTO_CHECK_INTERVAL_MS : CHECK_INTERVAL_MS; - if (Date.now() - readStamp(stamp) > interval) { + if (Date.now() - readStamp(stamp) > CHECK_INTERVAL_MS) { writeStamp(stamp); try { spawn('git', ['fetch', '--quiet', 'origin', branch], { cwd: root, detached: true, stdio: 'ignore' }).unref(); @@ -199,18 +213,7 @@ export function maybeNotifyUpdate(rootOverride?: string): void { } catch { return; } - if (behind === '0') return; - - if (auto) { - // Only one background update per cooldown window — a second command while - // the reset+build is in flight must not spawn a competing update. - const autoStamp = stampPath('last-auto-update'); - if (Date.now() - readStamp(autoStamp) > AUTO_UPDATE_COOLDOWN_MS) { - writeStamp(autoStamp); - process.stderr.write(`\n🔄 AgentHub: updating in background (${behind} commit(s) behind)…\n\n`); - spawnBackgroundUpdate(root); - } - } else { + if (behind !== '0') { process.stderr.write(`\n🔄 A newer AgentHub is available (${behind} commit(s) behind). Run \`agenthub update\`.\n\n`); } } catch { diff --git a/src/cli/index.ts b/src/cli/index.ts index 3b5b4b9..ba5f692 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -114,7 +114,7 @@ async function runRemote(serverUrl: string, fn: () => Promise): Promise', 'AgentHub server URL (env: AGENTHUB_SERVER)'); program