@indigoai-us/hq-cli 5.178.0 → 5.179.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/CHANGELOG.md
CHANGED
|
@@ -2,10 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.179.0] — 2026-09-24
|
|
6
|
+
|
|
5
7
|
## [5.178.0] — 2026-09-24
|
|
6
8
|
|
|
7
9
|
### Fixed
|
|
8
10
|
|
|
11
|
+
- Work Mesh transcript-watch still tracks hook-covered sessions (interactive Claude/Codex) and enqueues `session_end` after 10 minutes of quiet. Hooks keep owning start/turn events; the quiet `session_end` is the channel work-report trigger those sessions were missing. Resume (transcript mtime or a later hook event) resets the timer. A SessionEnd hook within 60s of that emit is not treated as resume, so `/exit` does not double-fire.
|
|
9
12
|
- Reloading a macOS LaunchAgent (`hq mesh daemon install`, including `--if-stale` and the post-update restart, `hq daemon install`, `hq agent kit` services, and bot agents) now waits for `launchctl bootout` to finish before bootstrapping. Before, bootstrap could race the old job's teardown, fail with "Bootstrap failed: 5: Input/output error", and leave the service unloaded with nothing running. Bootstrap now polls `launchctl print` for up to 10 seconds, retries EIO with backoff (0.5s to 8s), and on final failure either restarts the still-loaded previous job or reports that the service is NOT loaded with the `launchctl bootstrap` command to fix it.
|
|
10
13
|
|
|
11
14
|
## [5.177.0] — 2026-09-24
|
|
@@ -52,7 +55,6 @@
|
|
|
52
55
|
- Bot LaunchAgents and systemd units, the Work Mesh daemon unit, the `hq daemon` unit, and the lanes and agent-kit units now name node by its upgrade-stable path (`/opt/homebrew/bin/node` or `/opt/homebrew/opt/<formula>/bin/node`, or nvm's `current` link) instead of the versioned `/opt/homebrew/Cellar/node/<version>/bin/node`. Before, `brew upgrade node` deleted that path and launchd could no longer start the bots. The stable path is used only when it resolves to the same node binary; otherwise the unit keeps the resolved path as before.
|
|
53
56
|
- Installed bot LaunchAgents that still name a versioned or missing node are repaired: `hq bot start` rewrites and reloads the agent, a bot started by launchd rewrites its own agent and reloads it from a detached shell before it takes any message, and `hq doctor` reports it (`bots.<name>.daemon-node`), with `hq doctor --fix` rewriting only the plist file and not reloading launchd. `hq bot list` and `hq bot status` flag a missing node binary. `hq mesh daemon install --if-stale` also reinstalls a same-version unit whose node path is missing or versioned.
|
|
54
57
|
|
|
55
|
-
|
|
56
58
|
## [5.172.0] — 2026-09-24
|
|
57
59
|
|
|
58
60
|
### Changed
|
|
@@ -89,7 +91,6 @@
|
|
|
89
91
|
|
|
90
92
|
- Switching the HQ session company (including `hq-session.sh set company_slug`) clears the Work Mesh project and task, posts `session_end` to the previous company, and holds a personal target as `needs_company`.
|
|
91
93
|
|
|
92
|
-
|
|
93
94
|
## [5.169.0] — 2026-09-24
|
|
94
95
|
|
|
95
96
|
### Fixed
|
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
* Network: harness claude-desktop | codex, source "transcript" on every event.
|
|
25
25
|
* Local state keeps transcriptMarker for channel-card / Atlas before network sync;
|
|
26
26
|
* localHarness is no longer written (harness carries claude-desktop on the wire).
|
|
27
|
+
*
|
|
28
|
+
* Hook-covered sessions (durable hook-written state, or a hook event within
|
|
29
|
+
* TRANSCRIPT_HOOK_COVER_MS) do not get synthesized session_start / turn_end —
|
|
30
|
+
* hooks own those. The watcher still tracks last activity (transcript mtime or
|
|
31
|
+
* hook events) and enqueues session_end after TRANSCRIPT_SESSION_END_MS of
|
|
32
|
+
* quiet so the channel work report has a trigger (US-050b). Resume resets the
|
|
33
|
+
* timer. A hook event within TRANSCRIPT_HOOK_COVER_MS of our own emit is the
|
|
34
|
+
* SessionEnd hook catching up and is not treated as resume.
|
|
27
35
|
*/
|
|
28
36
|
import * as fs from "node:fs";
|
|
29
37
|
import type { MembershipValidationResult } from "../../../work-context/config.js";
|
|
@@ -260,6 +268,14 @@ export declare class TranscriptWatcher {
|
|
|
260
268
|
/** Record that a hook path emitted an event for sessionId (daemon flush). */
|
|
261
269
|
noteHookEvent(sessionId: string, atMs?: number): void;
|
|
262
270
|
tick(): Promise<TranscriptWatchTickResult>;
|
|
271
|
+
/**
|
|
272
|
+
* Hook-covered sessions already emit start/turns. Still track last activity
|
|
273
|
+
* and enqueue session_end after TRANSCRIPT_SESSION_END_MS of quiet so the
|
|
274
|
+
* channel work report has a trigger (US-050b). Resume (mtime or a later hook
|
|
275
|
+
* event) resets the timer. A hook event shortly after our own emit is the
|
|
276
|
+
* SessionEnd hook catching up — do not treat it as resume.
|
|
277
|
+
*/
|
|
278
|
+
private tickHookCoveredQuietEnd;
|
|
263
279
|
/**
|
|
264
280
|
* Heavy ticks double the poll interval up to MAX; a quiet tick resets to base.
|
|
265
281
|
*/
|
|
@@ -24,6 +24,14 @@
|
|
|
24
24
|
* Network: harness claude-desktop | codex, source "transcript" on every event.
|
|
25
25
|
* Local state keeps transcriptMarker for channel-card / Atlas before network sync;
|
|
26
26
|
* localHarness is no longer written (harness carries claude-desktop on the wire).
|
|
27
|
+
*
|
|
28
|
+
* Hook-covered sessions (durable hook-written state, or a hook event within
|
|
29
|
+
* TRANSCRIPT_HOOK_COVER_MS) do not get synthesized session_start / turn_end —
|
|
30
|
+
* hooks own those. The watcher still tracks last activity (transcript mtime or
|
|
31
|
+
* hook events) and enqueues session_end after TRANSCRIPT_SESSION_END_MS of
|
|
32
|
+
* quiet so the channel work report has a trigger (US-050b). Resume resets the
|
|
33
|
+
* timer. A hook event within TRANSCRIPT_HOOK_COVER_MS of our own emit is the
|
|
34
|
+
* SessionEnd hook catching up and is not treated as resume.
|
|
27
35
|
*/
|
|
28
36
|
import * as fs from "node:fs";
|
|
29
37
|
import * as os from "node:os";
|
|
@@ -632,8 +640,7 @@ export class TranscriptWatcher {
|
|
|
632
640
|
lastHookEventAt: this.deps.lastHookEventAt,
|
|
633
641
|
readState: this.readState,
|
|
634
642
|
})) {
|
|
635
|
-
|
|
636
|
-
this.tracked.delete(d.path);
|
|
643
|
+
this.tickHookCoveredQuietEnd(d, nowMs, result);
|
|
637
644
|
continue;
|
|
638
645
|
}
|
|
639
646
|
let track = this.tracked.get(d.path);
|
|
@@ -719,6 +726,63 @@ export class TranscriptWatcher {
|
|
|
719
726
|
this.tickInFlight = false;
|
|
720
727
|
}
|
|
721
728
|
}
|
|
729
|
+
/**
|
|
730
|
+
* Hook-covered sessions already emit start/turns. Still track last activity
|
|
731
|
+
* and enqueue session_end after TRANSCRIPT_SESSION_END_MS of quiet so the
|
|
732
|
+
* channel work report has a trigger (US-050b). Resume (mtime or a later hook
|
|
733
|
+
* event) resets the timer. A hook event shortly after our own emit is the
|
|
734
|
+
* SessionEnd hook catching up — do not treat it as resume.
|
|
735
|
+
*/
|
|
736
|
+
tickHookCoveredQuietEnd(d, nowMs, result) {
|
|
737
|
+
result.skippedHookCovered += 1;
|
|
738
|
+
const hookAt = this.deps.lastHookEventAt?.get(d.sessionId);
|
|
739
|
+
const lastActivity = Math.max(d.mtimeMs, hookAt ?? 0);
|
|
740
|
+
let track = this.tracked.get(d.path);
|
|
741
|
+
if (!track) {
|
|
742
|
+
if (nowMs - lastActivity > TRANSCRIPT_FRESH_MS)
|
|
743
|
+
return;
|
|
744
|
+
track = {
|
|
745
|
+
path: d.path,
|
|
746
|
+
sessionId: d.sessionId,
|
|
747
|
+
kind: d.kind,
|
|
748
|
+
cwd: d.cwd,
|
|
749
|
+
harness: d.harness,
|
|
750
|
+
lastMtimeMs: d.mtimeMs,
|
|
751
|
+
lastChangeAtMs: lastActivity,
|
|
752
|
+
started: true,
|
|
753
|
+
ended: false,
|
|
754
|
+
pendingTurn: false,
|
|
755
|
+
seq: 0,
|
|
756
|
+
startRetryAttempts: 0,
|
|
757
|
+
nextStartRetryAtMs: 0,
|
|
758
|
+
};
|
|
759
|
+
this.tracked.set(d.path, track);
|
|
760
|
+
}
|
|
761
|
+
if (d.mtimeMs > track.lastMtimeMs) {
|
|
762
|
+
track.lastMtimeMs = d.mtimeMs;
|
|
763
|
+
track.lastChangeAtMs = nowMs;
|
|
764
|
+
if (track.ended)
|
|
765
|
+
track.ended = false;
|
|
766
|
+
}
|
|
767
|
+
if (hookAt !== undefined) {
|
|
768
|
+
const resumeFromHook = !track.ended ||
|
|
769
|
+
track.endedAtMs === undefined ||
|
|
770
|
+
hookAt - track.endedAtMs >= TRANSCRIPT_HOOK_COVER_MS;
|
|
771
|
+
if (resumeFromHook && hookAt > track.lastChangeAtMs) {
|
|
772
|
+
track.lastChangeAtMs = hookAt;
|
|
773
|
+
if (track.ended)
|
|
774
|
+
track.ended = false;
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (track.started &&
|
|
778
|
+
!track.ended &&
|
|
779
|
+
nowMs - track.lastChangeAtMs >= TRANSCRIPT_SESSION_END_MS) {
|
|
780
|
+
this.emitKind(track, "session_end", nowMs);
|
|
781
|
+
track.ended = true;
|
|
782
|
+
track.endedAtMs = nowMs;
|
|
783
|
+
result.ended += 1;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
722
786
|
/**
|
|
723
787
|
* Heavy ticks double the poll interval up to MAX; a quiet tick resets to base.
|
|
724
788
|
*/
|