@clipboard-health/groundcrew 4.45.6 → 4.45.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD,UAAU,cAAc;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,+FAA+F;QAC/F,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAyOjE;AA2BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/commands/dispatcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EACL,KAAK,UAAU,EAGf,KAAK,KAAK,EAEX,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAGpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAWzD,UAAU,cAAc;IACtB,MAAM,EAAE,cAAc,CAAC;IACvB,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,UAAU,EAAE;QACpB,KAAK,EAAE,UAAU,CAAC;QAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;QAC1C,+FAA+F;QAC/F,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;QACvD,MAAM,EAAE,OAAO,CAAC;QAChB,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB;;;;WAIG;QACH,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAiCD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,cAAc,GAAG,UAAU,CAyOjE;AA8BD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAQrE"}
@@ -239,6 +239,9 @@ function hasRecoverableCandidate(issues, worktreeEntries) {
239
239
  });
240
240
  }
241
241
  function formatUsageExhaustion(exhaustion) {
242
+ if (exhaustion.kind === "unavailable") {
243
+ return `${exhaustion.agent} usage unavailable: ${exhaustion.reason} — skipping its tasks`;
244
+ }
242
245
  if (exhaustion.kind === "session") {
243
246
  const mins = exhaustion.resetMinutes ?? "?";
244
247
  return `${exhaustion.agent} session at ${exhaustion.usedPercentage.toFixed(0)}% (> ${exhaustion.limitPercentage}%), resets in ${mins}m — skipping its tasks`;
@@ -38,6 +38,10 @@ export interface SkipVerdict {
38
38
  }
39
39
  type Verdict = StartVerdict | SkipVerdict;
40
40
  export type AgentUsageExhaustion = {
41
+ kind: "unavailable";
42
+ agent: string;
43
+ reason: string;
44
+ } | {
41
45
  kind: "session";
42
46
  agent: string;
43
47
  usedPercentage: number;
@@ -1 +1 @@
1
- {"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,KAAK,UAAU,GACX,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,4BAA4B,GAC5B,mBAAmB,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,EAAE,UAAU,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1C,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,MAAM,GAAG,SAAS,CAepB;AAaD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,GAClB,oBAAoB,EAAE,CAmCxB;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,qBAAqB,CAYxF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAgE5E"}
1
+ {"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,KAAK,UAAU,GACX,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,4BAA4B,GAC5B,mBAAmB,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,EAAE,UAAU,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1C,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,GACD;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,MAAM,GAAG,SAAS,CAepB;AAaD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,GAClB,oBAAoB,EAAE,CA2CxB;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,qBAAqB,CAYxF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAgE5E"}
@@ -73,6 +73,14 @@ export function classifyUsageExhaustion(config, usage) {
73
73
  const exhausted = [];
74
74
  const sessionLimit = config.orchestrator.sessionLimitPercentage;
75
75
  for (const [agent, snapshot] of Object.entries(usage)) {
76
+ if (snapshot.unavailableReason !== undefined) {
77
+ exhausted.push({
78
+ kind: "unavailable",
79
+ agent,
80
+ reason: snapshot.unavailableReason,
81
+ });
82
+ continue;
83
+ }
76
84
  if (snapshot.session !== null && snapshot.session * PERCENT_FRACTION_DIVISOR > sessionLimit) {
77
85
  exhausted.push({
78
86
  kind: "session",
@@ -1 +1 @@
1
- {"version":3,"file":"cmuxAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/cmuxAdapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,WAAW,EAAE,OAqFzB,CAAC"}
1
+ {"version":3,"file":"cmuxAdapter.d.ts","sourceRoot":"","sources":["../../src/lib/cmuxAdapter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,KAAK,OAAO,EAKb,MAAM,uBAAuB,CAAC;AAG/B,eAAO,MAAM,WAAW,EAAE,OA+EzB,CAAC"}
@@ -4,7 +4,7 @@
4
4
  * per-workspace status pill, which `open` applies best-effort.
5
5
  */
6
6
  import { isSignalAborted, runWorkspaceCommand, } from "./workspaceAdapter.js";
7
- import { debug, errorMessage, log } from "./util.js";
7
+ import { debug, errorMessage, log, logEvent } from "./util.js";
8
8
  export const cmuxAdapter = {
9
9
  async open(spec, signal) {
10
10
  let output;
@@ -58,28 +58,21 @@ export const cmuxAdapter = {
58
58
  debug(`cmux close-workspace skipped for ${name}: list-workspaces failed, no usable id`);
59
59
  return { kind: "unavailable" };
60
60
  }
61
- const match = raw.find((ws) => cmuxTaskId(ws) === name);
62
- if (match === undefined) {
61
+ const matches = raw.filter((ws) => cmuxTaskId(ws) === name);
62
+ if (matches.length === 0) {
63
63
  return { kind: "missing" };
64
64
  }
65
- try {
66
- await closeCmuxWorkspace(match.id, signal);
67
- return { kind: "closed" };
68
- }
69
- catch (error) {
70
- if (isSignalAborted(signal)) {
71
- throw error;
72
- }
73
- const remaining = await listCmuxRaw(signal);
74
- if (remaining === undefined) {
75
- return { kind: "unavailable", error };
76
- }
77
- const isStillPresent = remaining.some((ws) => cmuxTaskId(ws) === name);
78
- if (!isStillPresent) {
79
- return { kind: "closed" };
80
- }
81
- throw error;
65
+ if (matches.length > 1) {
66
+ // A single task id maps to many cmux workspaces only after a leaked launch
67
+ // or session-restore duplication. Surface the leak so it's observable
68
+ // rather than silently reconciled away.
69
+ logEvent("cmux_close", {
70
+ outcome: "duplicate_markers",
71
+ task: name,
72
+ duplicates: matches.length,
73
+ });
82
74
  }
75
+ return await closeAllCmuxMatches(matches, signal);
83
76
  },
84
77
  accessHint(_name) {
85
78
  // cmux is a TUI; users surface workspaces by launching the cmux app,
@@ -184,6 +177,59 @@ async function applyCmuxStatus(workspaceId, status, signal) {
184
177
  arguments_.push("--workspace", workspaceId);
185
178
  await runWorkspaceCommand("cmux", arguments_, signal);
186
179
  }
180
+ /**
181
+ * Closes every workspace sharing the requested marker. Closes run sequentially:
182
+ * a failure path re-lists cmux to confirm the workspace is gone, and firing N
183
+ * mutations plus N confirmation lists concurrently would race on that shared
184
+ * state. Each match is attempted even when an earlier one fails, so a single
185
+ * stuck duplicate can't orphan the rest; a confirmed-still-present failure is
186
+ * collected and rethrown only after the loop (preserving single-match
187
+ * semantics). A failure that cannot be confirmed yields `unavailable`; `closed`
188
+ * wins only when every match is gone.
189
+ */
190
+ async function closeAllCmuxMatches(matches, signal) {
191
+ let unavailable;
192
+ const errors = [];
193
+ for (const match of matches) {
194
+ try {
195
+ // oxlint-disable-next-line no-await-in-loop -- sequential by design; failure re-lists shared cmux state
196
+ const result = await closeCmuxMatch(match, signal);
197
+ if (result.kind === "unavailable") {
198
+ unavailable = result;
199
+ }
200
+ }
201
+ catch (error) {
202
+ if (isSignalAborted(signal)) {
203
+ throw error;
204
+ }
205
+ errors.push(error);
206
+ }
207
+ }
208
+ if (errors.length > 0) {
209
+ throw errors.length === 1 ? errors[0] : new AggregateError(errors);
210
+ }
211
+ return unavailable ?? { kind: "closed" };
212
+ }
213
+ async function closeCmuxMatch(match, signal) {
214
+ try {
215
+ await closeCmuxWorkspace(match.id, signal);
216
+ return { kind: "closed" };
217
+ }
218
+ catch (error) {
219
+ if (isSignalAborted(signal)) {
220
+ throw error;
221
+ }
222
+ const remaining = await listCmuxRaw(signal);
223
+ if (remaining === undefined) {
224
+ return { kind: "unavailable", error };
225
+ }
226
+ const isStillPresent = remaining.some((ws) => ws.id === match.id);
227
+ if (!isStillPresent) {
228
+ return { kind: "closed" };
229
+ }
230
+ throw error;
231
+ }
232
+ }
187
233
  async function closeCmuxWorkspace(workspaceId, signal) {
188
234
  await runWorkspaceCommand("cmux", ["close-workspace", "--workspace", workspaceId], signal);
189
235
  }
@@ -11,6 +11,8 @@ interface NormalizedUsage {
11
11
  sessionEndDuration: number | null;
12
12
  weekly: number | null;
13
13
  weekEndDuration: number | null;
14
+ /** Set when the snapshot is fail-closed because codexbar could not be read. */
15
+ unavailableReason?: string;
14
16
  }
15
17
  export type UsageByAgent = Record<string, NormalizedUsage>;
16
18
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/lib/usage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,aAAa,CAAC;AA+BnE,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,eAK7B,CAAC;AA8JF,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAI5D;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CA+BvB"}
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/lib/usage.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,aAAa,CAAC;AA+BnE,UAAU,eAAe;IACvB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAE3D;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,EAAE,eAK7B,CAAC;AAgKF,wBAAgB,WAAW,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,EAAE,CAI5D;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,YAAY,CAAC,CAgCvB"}
package/dist/lib/usage.js CHANGED
@@ -109,7 +109,7 @@ async function codexbarUsage(definition, signal) {
109
109
  // exhausted entry; surface codexbar's error message so the operator
110
110
  // can fix the underlying CLI.
111
111
  const detail = match.error?.message ?? "no usage data";
112
- throw new Error(`codexbar returned no usage for provider=${provider}: ${detail}`);
112
+ throw new Error(`codexbar returned no usage for provider=${provider}, source=${source}: ${detail}`);
113
113
  }
114
114
  return match.usage;
115
115
  }
@@ -196,8 +196,9 @@ export async function getUsageByAgent(config, signal) {
196
196
  // --verbose) so operators can fix the underlying CLI, and return a
197
197
  // fully-exhausted snapshot so the dispatcher gates the agent. The
198
198
  // gate itself surfaces a visible skip line via formatUsageExhaustion.
199
- debug(`Usage check failed for ${agent} (treating as exhausted): ${errorMessage(error)}`);
200
- out[agent] = EXHAUSTED_USAGE;
199
+ const reason = errorMessage(error);
200
+ debug(`Usage check failed for ${agent} (treating as exhausted): ${reason}`);
201
+ out[agent] = { ...EXHAUSTED_USAGE, unavailableReason: reason };
201
202
  }
202
203
  }
203
204
  return out;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clipboard-health/groundcrew",
3
- "version": "4.45.6",
3
+ "version": "4.45.8",
4
4
  "description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
5
5
  "keywords": [
6
6
  "agent",
@@ -88,7 +88,7 @@
88
88
  "cspell": "10.0.1",
89
89
  "dependency-cruiser": "17.4.3",
90
90
  "husky": "9.1.7",
91
- "jscpd": "5.0.10",
91
+ "jscpd": "5.0.11",
92
92
  "knip": "6.16.1",
93
93
  "lint-staged": "17.0.7",
94
94
  "markdownlint-cli2": "0.22.1",