@tokenfactory/acc-runner 0.12.0 → 0.16.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.
Files changed (51) hide show
  1. package/README.md +54 -0
  2. package/dist/cli.js +5 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/doctor.d.ts +19 -0
  5. package/dist/doctor.d.ts.map +1 -1
  6. package/dist/doctor.js +126 -3
  7. package/dist/doctor.js.map +1 -1
  8. package/dist/failure-classifier.d.ts +58 -7
  9. package/dist/failure-classifier.d.ts.map +1 -1
  10. package/dist/failure-classifier.js +172 -6
  11. package/dist/failure-classifier.js.map +1 -1
  12. package/dist/git.d.ts +15 -0
  13. package/dist/git.d.ts.map +1 -1
  14. package/dist/git.js +19 -0
  15. package/dist/git.js.map +1 -1
  16. package/dist/github-client.d.ts +133 -0
  17. package/dist/github-client.d.ts.map +1 -0
  18. package/dist/github-client.js +234 -0
  19. package/dist/github-client.js.map +1 -0
  20. package/dist/runtime/cache-dir.d.ts +2 -0
  21. package/dist/runtime/cache-dir.d.ts.map +1 -0
  22. package/dist/runtime/cache-dir.js +15 -0
  23. package/dist/runtime/cache-dir.js.map +1 -0
  24. package/dist/runtime/quarantine.d.ts +10 -0
  25. package/dist/runtime/quarantine.d.ts.map +1 -1
  26. package/dist/runtime/quarantine.js.map +1 -1
  27. package/dist/runtime/resolution-integrity.d.ts +86 -0
  28. package/dist/runtime/resolution-integrity.d.ts.map +1 -0
  29. package/dist/runtime/resolution-integrity.js +248 -0
  30. package/dist/runtime/resolution-integrity.js.map +1 -0
  31. package/dist/runtime/reviewer.d.ts +8 -1
  32. package/dist/runtime/reviewer.d.ts.map +1 -1
  33. package/dist/runtime/reviewer.js +18 -0
  34. package/dist/runtime/reviewer.js.map +1 -1
  35. package/dist/runtime/singleton.d.ts +47 -0
  36. package/dist/runtime/singleton.d.ts.map +1 -0
  37. package/dist/runtime/singleton.js +200 -0
  38. package/dist/runtime/singleton.js.map +1 -0
  39. package/dist/runtime/version-drift.d.ts +31 -0
  40. package/dist/runtime/version-drift.d.ts.map +1 -0
  41. package/dist/runtime/version-drift.js +114 -0
  42. package/dist/runtime/version-drift.js.map +1 -0
  43. package/dist/task-runner.d.ts +40 -2
  44. package/dist/task-runner.d.ts.map +1 -1
  45. package/dist/task-runner.js +290 -50
  46. package/dist/task-runner.js.map +1 -1
  47. package/dist/watch.d.ts +19 -0
  48. package/dist/watch.d.ts.map +1 -1
  49. package/dist/watch.js +254 -6
  50. package/dist/watch.js.map +1 -1
  51. package/package.json +1 -1
@@ -1,19 +1,46 @@
1
1
  /**
2
- * v0.48: classify a non-zero claude CLI exit into one of four categories
2
+ * v0.48: classify a non-zero claude CLI exit into one of five categories
3
3
  * so the watch loop can distinguish machine-level failures (which should
4
4
  * quarantine the runner) from genuine task errors (which count toward the
5
- * task's retry budget).
5
+ * task's retry budget) and from a spent capacity window (which should
6
+ * PAUSE, not quarantine).
6
7
  *
7
- * The four classes:
8
- * usage_limit — Claude/Anthropic plan limit or rate limit reached.
8
+ * The five classes:
9
+ * capacity_exhausted — Claude/Anthropic session or usage limit reached
10
+ * (api_error_status 429 + a "resets <time>" message).
11
+ * A NEW v0.56 (T-56-1) retryable-infra class: capacity is
12
+ * a resource, not a failure. NEVER task_error and NEVER
13
+ * quarantine — the runner pauses claiming until the stated
14
+ * reset time (or a default backoff) then resumes with a
15
+ * single probe. Root cause of the 2026-06-05 brownout.
16
+ * usage_limit — A hard Claude/Anthropic plan limit that will NOT reset
17
+ * on its own soon (out of credits, monthly spend cap,
18
+ * quota exhausted). Quarantine is still the right response
19
+ * — an operator has to top up or wait out the billing cycle.
9
20
  * auth_expired — API key invalid or session expired on the user's machine.
10
21
  * env_broken — Host-side environment failure (dyld, missing binary, OOM).
11
22
  * Root cause of the pd36 claim-and-fail storm (INCIDENT_2026-06-02).
12
23
  * task_error — Everything else; genuine task-level failure.
13
24
  *
14
- * Only task_error counts toward a task's retry budget. The other three
15
- * trigger runner quarantine in watch.ts.
25
+ * Only task_error counts toward a task's retry budget. usage_limit /
26
+ * auth_expired / env_broken trigger runner quarantine in watch.ts;
27
+ * capacity_exhausted triggers the pause/resume path instead.
16
28
  */
29
+ // v0.56 (T-56-1): signatures of a SPENT-BUT-RECOVERABLE capacity window —
30
+ // the session/usage limit Claude resets on a clock (vs. usage_limit below,
31
+ // which is a hard billing wall). These must stay disjoint from the
32
+ // usage_limit patterns so the live 2026-06-05 signature
33
+ // ("You've hit your session limit - resets 3:20pm (Asia/Calcutta)")
34
+ // routes to pause, while "out of credits" / "monthly spend limit" still
35
+ // route to quarantine. The authoritative signal is api_error_status 429 in
36
+ // the result JSON; these message patterns are the fallback when the runner
37
+ // only has plain text (older claude, or the JSON envelope was not emitted).
38
+ const CAPACITY_PATTERNS = [
39
+ /hit your (?:session|usage|account) limit/i,
40
+ /session limit/i,
41
+ /(?:session|usage|account|limit).*resets?\b/i,
42
+ /resets?\s+(?:at\s+)?\d{1,2}(?::\d{2})?\s*(?:am|pm)/i,
43
+ ];
17
44
  const USAGE_LIMIT_PATTERNS = [
18
45
  /usage limit/i,
19
46
  /you have reached the maximum number of tokens/i,
@@ -65,6 +92,129 @@ const FAST_EXIT_MAX_OUTPUT_BYTES = 100;
65
92
  function matchesAny(text, patterns) {
66
93
  return patterns.some((p) => p.test(text));
67
94
  }
95
+ /**
96
+ * v0.56 (T-56-1): pull `api_error_status` out of claude's
97
+ * `--output-format=json` envelope. On a session/usage limit the CLI exits
98
+ * 1 with a JSON object carrying `{ "is_error": true, "api_error_status":
99
+ * 429, "result": "You've hit your session limit - resets 3:20pm
100
+ * (Asia/Calcutta)" }`. Returns the numeric status when present, else null.
101
+ * Kept self-contained (a local parse, no cost-pricing import) so the
102
+ * classifier stays a pure (strings) → class function for unit tests.
103
+ */
104
+ export function parseApiErrorStatus(stdout) {
105
+ const trimmed = stdout.trim();
106
+ if (!trimmed.startsWith("{"))
107
+ return null;
108
+ try {
109
+ const parsed = JSON.parse(trimmed);
110
+ const status = parsed?.api_error_status;
111
+ if (typeof status === "number")
112
+ return status;
113
+ if (typeof status === "string" && /^\d+$/.test(status))
114
+ return Number(status);
115
+ return null;
116
+ }
117
+ catch {
118
+ return null;
119
+ }
120
+ }
121
+ const RESET_TIME_RE = /resets?\s+(?:at\s+|by\s+)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*(?:\(([^)]+)\))?/i;
122
+ function tzDateParts(instant, tz) {
123
+ const dtf = new Intl.DateTimeFormat("en-US", {
124
+ timeZone: tz,
125
+ year: "numeric",
126
+ month: "2-digit",
127
+ day: "2-digit",
128
+ hour: "2-digit",
129
+ minute: "2-digit",
130
+ second: "2-digit",
131
+ hour12: false,
132
+ });
133
+ const map = {};
134
+ for (const p of dtf.formatToParts(instant)) {
135
+ if (p.type !== "literal")
136
+ map[p.type] = p.value;
137
+ }
138
+ // Intl can emit "24" for midnight in hour12:false mode; normalise to 0.
139
+ const hour = Number(map.hour) === 24 ? 0 : Number(map.hour);
140
+ return {
141
+ year: Number(map.year),
142
+ month: Number(map.month),
143
+ day: Number(map.day),
144
+ hour,
145
+ minute: Number(map.minute),
146
+ second: Number(map.second),
147
+ };
148
+ }
149
+ /** Offset (ms) of `tz` relative to UTC at the given instant (east = positive). */
150
+ function zoneOffsetMs(instant, tz) {
151
+ const p = tzDateParts(instant, tz);
152
+ const asUtc = Date.UTC(p.year, p.month - 1, p.day, p.hour, p.minute, p.second);
153
+ return asUtc - instant;
154
+ }
155
+ /** Resolve a wall-clock Y-M-D H:M in `tz` to a UTC epoch (one DST refinement). */
156
+ function wallClockToUtc(year, month, day, hour, minute, tz) {
157
+ const guess = Date.UTC(year, month - 1, day, hour, minute, 0);
158
+ let instant = guess - zoneOffsetMs(guess, tz);
159
+ const off2 = zoneOffsetMs(instant, tz);
160
+ const refined = guess - off2;
161
+ if (refined !== instant)
162
+ instant = refined;
163
+ return instant;
164
+ }
165
+ /**
166
+ * v0.56 (T-56-1): parse the reset time Claude states when it pauses a
167
+ * session ("...resets 3:20pm (Asia/Calcutta)") into an absolute epoch
168
+ * (ms). The runner schedules its capacity resume off this so it wakes
169
+ * exactly when the window reopens rather than guessing a fixed backoff.
170
+ *
171
+ * - 12h ("3:20pm") and 24h ("15:20") clock forms are both accepted.
172
+ * - A parenthetical IANA zone ("(Asia/Calcutta)") is honoured; without
173
+ * one the time is read in the runner's local zone.
174
+ * - The soonest FUTURE occurrence wins: a stated time already past today
175
+ * rolls to tomorrow.
176
+ *
177
+ * Returns null when no reset time is present or the zone is unparseable —
178
+ * the caller then falls back to a default backoff.
179
+ */
180
+ export function extractResetTime(text, now = Date.now()) {
181
+ if (!text)
182
+ return null;
183
+ const m = RESET_TIME_RE.exec(text);
184
+ if (!m)
185
+ return null;
186
+ let hour = Number(m[1]);
187
+ const minute = m[2] ? Number(m[2]) : 0;
188
+ const ampm = m[3]?.toLowerCase();
189
+ const tz = m[4]?.trim();
190
+ if (Number.isNaN(hour) || hour > 23 || minute > 59)
191
+ return null;
192
+ if (ampm === "pm" && hour < 12)
193
+ hour += 12;
194
+ if (ampm === "am" && hour === 12)
195
+ hour = 0;
196
+ try {
197
+ if (tz) {
198
+ const today = tzDateParts(now, tz);
199
+ let instant = wallClockToUtc(today.year, today.month, today.day, hour, minute, tz);
200
+ if (instant <= now) {
201
+ const tomorrow = tzDateParts(now + 24 * 3_600_000, tz);
202
+ instant = wallClockToUtc(tomorrow.year, tomorrow.month, tomorrow.day, hour, minute, tz);
203
+ }
204
+ return instant;
205
+ }
206
+ const local = new Date(now);
207
+ local.setHours(hour, minute, 0, 0);
208
+ let instant = local.getTime();
209
+ if (instant <= now)
210
+ instant += 24 * 3_600_000;
211
+ return instant;
212
+ }
213
+ catch {
214
+ // Unknown/garbled IANA zone — let the caller apply its default backoff.
215
+ return null;
216
+ }
217
+ }
68
218
  /**
69
219
  * Classify a non-zero claude exit.
70
220
  *
@@ -84,6 +234,18 @@ export function classifyClaudeExit(exitCode, stderr, stdout, durationMs) {
84
234
  };
85
235
  }
86
236
  const combined = `${stderr}\n${stdout}`;
237
+ // v0.56 (T-56-1): capacity check FIRST so the recoverable session/usage
238
+ // window routes to pause, not quarantine. Authoritative signal is the
239
+ // result JSON's api_error_status 429; the message patterns are the
240
+ // plain-text fallback. Disjoint from USAGE_LIMIT_PATTERNS below, which
241
+ // stay quarantine-worthy (hard billing walls).
242
+ if (parseApiErrorStatus(stdout) === 429 || matchesAny(combined, CAPACITY_PATTERNS)) {
243
+ return {
244
+ exitCode,
245
+ class: "capacity_exhausted",
246
+ detail: "Claude session/usage capacity reached (429) — runner will pause until reset",
247
+ };
248
+ }
87
249
  if (matchesAny(combined, USAGE_LIMIT_PATTERNS)) {
88
250
  return {
89
251
  exitCode,
@@ -117,6 +279,10 @@ export function classifyClaudeExit(exitCode, stderr, stdout, durationMs) {
117
279
  exitCode,
118
280
  class: "env_broken",
119
281
  detail: `claude exited ${exitCode ?? "signal"} in ${durationMs}ms with empty output (env_broken heuristic)`,
282
+ // v0.53 T-53-4: flag the heuristic origin so the task-runner applies
283
+ // the "retry once, quarantine only on 2 consecutive" tune rather than
284
+ // benching the machine on a single instant-empty blip.
285
+ heuristic: true,
120
286
  };
121
287
  }
122
288
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"failure-classifier.js","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAcH,MAAM,oBAAoB,GAAa;IACrC,cAAc;IACd,gDAAgD;IAChD,sBAAsB;IACtB,iBAAiB;IACjB,sBAAsB;IACtB,aAAa;IACb,iBAAiB;IACjB,sBAAsB;IACtB,oCAAoC;CACrC,CAAC;AAEF,MAAM,qBAAqB,GAAa;IACtC,kBAAkB;IAClB,yBAAyB;IACzB,oBAAoB;IACpB,eAAe;IACf,0CAA0C;IAC1C,mBAAmB;IACnB,yCAAyC;IACzC,oBAAoB;IACpB,uBAAuB;IACvB,gBAAgB;CACjB,CAAC;AAEF,yEAAyE;AACzE,4EAA4E;AAC5E,2EAA2E;AAC3E,MAAM,mBAAmB,GAAa;IACpC,8BAA8B;IAC9B,qBAAqB;IACrB,kBAAkB;IAClB,oCAAoC;IACpC,QAAQ;IACR,aAAa;IACb,qBAAqB;IACrB,SAAS;IACT,eAAe;IACf,oBAAoB;IACpB,4BAA4B;IAC5B,0BAA0B;IAC1B,WAAW;IACX,SAAS;IACT,6BAA6B;CAC9B,CAAC;AAEF,kEAAkE;AAClE,sEAAsE;AACtE,4EAA4E;AAC5E,MAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAEvC,SAAS,UAAU,CAAC,IAAY,EAAE,QAAkB;IAClD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAuB,EACvB,MAAc,EACd,MAAc,EACd,UAAmB;IAEnB,mEAAmE;IACnE,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACrB,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,4CAA4C;SACrD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,GAAG,MAAM,KAAK,MAAM,EAAE,CAAC;IAExC,IAAI,UAAU,CAAC,QAAQ,EAAE,oBAAoB,CAAC,EAAE,CAAC;QAC/C,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,aAAa;YACpB,MAAM,EAAE,wCAAwC;SACjD,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,EAAE,qBAAqB,CAAC,EAAE,CAAC;QAChD,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,cAAc;YACrB,MAAM,EAAE,6DAA6D;SACtE,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,EAAE,mBAAmB,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,mDAAmD;SAC5D,CAAC;IACJ,CAAC;IAED,kEAAkE;IAClE,wEAAwE;IACxE,qEAAqE;IACrE,qEAAqE;IACrE,IACE,UAAU,KAAK,SAAS;QACxB,UAAU,GAAG,YAAY;QACzB,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAC1B,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,0BAA0B,EACjD,CAAC;QACD,OAAO;YACL,QAAQ;YACR,KAAK,EAAE,YAAY;YACnB,MAAM,EAAE,iBAAiB,QAAQ,IAAI,QAAQ,OAAO,UAAU,6CAA6C;SAC5G,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,KAAK,EAAE,YAAY;QACnB,MAAM,EAAE,iBAAiB,QAAQ,IAAI,QAAQ,uBAAuB;KACrE,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"failure-classifier.js","sourceRoot":"","sources":["../src/failure-classifier.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts CHANGED
@@ -6,6 +6,21 @@ export interface GitRunner {
6
6
  push(repoPath: string, branch: string): Promise<void>;
7
7
  /** True when the working tree has no uncommitted changes. */
8
8
  isClean(repoPath: string): Promise<boolean>;
9
+ /**
10
+ * v0.53 T-53-4: the EARLIEST commit on `branch` that is not reachable
11
+ * from `baseRef` — i.e. the first commit the task produced on its branch
12
+ * — as `{ sha, ts }` where `ts` is the committer date (ISO-8601), or
13
+ * null when the branch has no commits ahead of the base. Powers the
14
+ * `task.first_commit` SLO signal (when coding actually started).
15
+ *
16
+ * Optional on the interface so existing GitRunner test stubs that omit
17
+ * it keep compiling; the default implementation below provides it and
18
+ * the task-runner guards the call with `?.`.
19
+ */
20
+ firstCommit?(repoPath: string, baseRef: string, branch: string): Promise<{
21
+ sha: string;
22
+ ts: string;
23
+ } | null>;
9
24
  }
10
25
  export type PushErrorKind = "transient" | "terminal";
11
26
  /**
package/dist/git.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,kBAAkB,QAAsC,CAAC;AAEtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7C;AAMD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CA0B7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,wCAAwC;IACxC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAQD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC3B,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,eAAO,MAAM,GAAG,EAAE,SAiBjB,CAAC"}
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,kBAAkB,QAAsC,CAAC;AAEtE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEpE;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C;;;;;;;;;;OAUG;IACH,WAAW,CAAC,CACV,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;CAChD;AAMD,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,CAAC;AAErD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,CA0B7D;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,wCAAwC;IACxC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC;AAQD;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC3B,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CAoBf;AAED,eAAO,MAAM,GAAG,EAAE,SAkCjB,CAAC"}
package/dist/git.js CHANGED
@@ -104,5 +104,24 @@ export const git = {
104
104
  const { stdout } = await run(repoPath, ["status", "--porcelain"]);
105
105
  return stdout.trim().length === 0;
106
106
  },
107
+ async firstCommit(repoPath, baseRef, branch) {
108
+ // `--reverse` lists oldest→newest so the first line is the earliest
109
+ // commit on `branch` not reachable from `baseRef`. %H = full sha,
110
+ // %cI = committer date in strict ISO-8601. A tab separator survives
111
+ // both fields cleanly (commit dates never contain tabs).
112
+ const { stdout } = await run(repoPath, [
113
+ "log",
114
+ "--reverse",
115
+ "--format=%H%x09%cI",
116
+ `${baseRef}..${branch}`,
117
+ ]);
118
+ const firstLine = stdout.split(/\r?\n/).find((l) => l.trim().length > 0);
119
+ if (!firstLine)
120
+ return null;
121
+ const [sha, ts] = firstLine.split("\t");
122
+ if (!sha)
123
+ return null;
124
+ return { sha: sha.trim(), ts: (ts ?? "").trim() };
125
+ },
107
126
  };
108
127
  //# sourceMappingURL=git.js.map
package/dist/git.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,KAAK,EAA2C,MAAM,OAAO,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,uEAAuE;AACvE,wEAAwE;AACxE,qEAAqE;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,mCAAmC,CAAC;AAEtE,MAAM,UAAU,WAAW,CAAC,IAA+B;IACzD,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC;AAUD,KAAK,UAAU,GAAG,CAAC,QAAgB,EAAE,IAAc;IACjD,OAAO,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;AAChE,CAAC;AAID;;;;;;;;GAQG;AACH,MAAM,UAAU,iBAAiB,CAAC,GAAY;IAC5C,MAAM,CAAC,GAAG,GAAoE,CAAC;IAC/E,MAAM,MAAM,GAAG,OAAO,CAAC,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,QAAQ,GAAG,GAAG,MAAM,KAAK,OAAO,EAAE,CAAC,WAAW,EAAE,CAAC;IAEvD,iEAAiE;IACjE,+BAA+B;IAC/B,MAAM,gBAAgB,GAAG;QACvB,uBAAuB;QACvB,mBAAmB;QACnB,oBAAoB;QACpB,yBAAyB;QACzB,yBAAyB;QACzB,eAAe;QACf,kBAAkB;QAClB,kBAAkB;QAClB,eAAe;QACf,iDAAiD;KAClD,CAAC;IACF,IAAI,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC;IAE1E,uEAAuE;IACvE,uEAAuE;IACvE,cAAc;IACd,OAAO,WAAW,CAAC;AACrB,CAAC;AAUD,MAAM,kBAAkB,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAE1D,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA2B,EAC3B,UAA4B,EAAE;IAE9B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;IACvC,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAC;IACxD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,YAAY,CAAC;IAE5C,IAAI,SAAS,GAAY,IAAI,CAAC;IAC9B,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,QAAQ,EAAE,OAAO,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC;YACH,MAAM,MAAM,EAAE,CAAC;YACf,OAAO;QACT,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,SAAS,GAAG,GAAG,CAAC;YAChB,MAAM,IAAI,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACpC,IAAI,IAAI,KAAK,UAAU;gBAAE,MAAM,GAAG,CAAC;YACnC,IAAI,OAAO,KAAK,QAAQ;gBAAE,MAAM;YAChC,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;YACtE,IAAI,IAAI,GAAG,CAAC;gBAAE,MAAM,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IACD,MAAM,SAAkB,CAAC;AAC3B,CAAC;AAED,MAAM,CAAC,MAAM,GAAG,GAAc;IAC5B,KAAK,CAAC,KAAK,CAAC,QAAQ;QAClB,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IACrD,CAAC;IACD,KAAK,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM;QAC7B,qEAAqE;QACrE,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM;QACzB,MAAM,aAAa,CAAC,KAAK,IAAI,EAAE;YAC7B,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;QACxD,CAAC,CAAC,CAAC;IACL,CAAC;IACD,KAAK,CAAC,OAAO,CAAC,QAAQ;QACpB,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC;QAClE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,CAAC;IACpC,CAAC;CACF,CAAC"}
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * v0.53 T-53-4 — runner-side, budget-aware GitHub REST client.
3
+ *
4
+ * A port of the api/ side's `api/_lib/github-client.ts` (T-52-4) into the
5
+ * runner package. The runner CANNOT import api/ code (separate build, no
6
+ * shared module graph), so the pattern is reproduced here. The three
7
+ * behaviours match the server client 1:1:
8
+ *
9
+ * 1. Quota tracking — x-ratelimit-remaining / x-ratelimit-reset per
10
+ * resource (core, search, graphql, …) from every response, surfaced
11
+ * via getQuotaSnapshot() for `acc-runner doctor`.
12
+ *
13
+ * 2. Secondary-limit backoff — a 429, or a 403 carrying retry-after /
14
+ * x-ratelimit-remaining: 0, is a rate-limit response, not an answer.
15
+ * The client sleeps once (retry-after if given, else an exponential
16
+ * base, both jittered, capped at MAX_BACKOFF_MS) and retries once. A
17
+ * second rate-limit response — or a retry-after beyond the cap —
18
+ * surfaces as a typed RateLimitedError.
19
+ *
20
+ * 3. Per-task-execution soft budget — runTask arms a budget at the top
21
+ * of a task (startTaskGithubBudget) and disarms it in its finally
22
+ * (endTaskGithubBudget). Once the budget is spent, every further
23
+ * githubFetch throws GithubBudgetExceededError WITHOUT hitting the
24
+ * network. The caller treats this as a CLEAN, RETRYABLE infra failure
25
+ * — NOT a code failure: it never reaches classifyClaudeExit, so the
26
+ * runner is never quarantined for exhausting its GitHub budget.
27
+ *
28
+ * ── Coverage note (READ THIS) ──────────────────────────────────────────
29
+ * As of v0.53 the runner makes ALL of its GitHub calls through the `gh`
30
+ * CLI (see gh.ts, runtime/conflict-resolver.ts, runtime/rework.ts,
31
+ * runtime/reviewer.ts). Those `gh`/`gh api` invocations are SEPARATE
32
+ * processes with their own rate-limit handling and are out of scope for
33
+ * this client — they remain UNCOVERED by the budget/backoff here. This
34
+ * module is the sanctioned wrapper for any future *direct* REST call the
35
+ * runner adds (`fetch` against api.github.com); routing such calls through
36
+ * githubFetch is what keeps them budget- and rate-limit-aware.
37
+ *
38
+ * Reference: api/_lib/github-client.ts, planning/ACC_WORKFLOW_GAP_ANALYSIS.md §G13.
39
+ */
40
+ /** Soft per-task-execution call budget. */
41
+ export declare const DEFAULT_TASK_GITHUB_BUDGET = 50;
42
+ /**
43
+ * Longest single backoff sleep. A secondary-limit retry-after can be 60s+;
44
+ * sleeping that long inside a task run wastes wall-clock the operator is
45
+ * paying for, so anything above the cap skips the retry and throws
46
+ * RateLimitedError instead.
47
+ */
48
+ export declare const MAX_BACKOFF_MS = 15000;
49
+ /** Base for the exponential backoff when GitHub sends no retry-after. */
50
+ export declare const BASE_BACKOFF_MS = 2000;
51
+ /**
52
+ * Typed rate-limit failure: GitHub said "back off" twice in a row (or
53
+ * asked for a longer wait than we can afford). A rate-limit storm is then
54
+ * diagnosable instead of looking like random github_http_403 noise. This
55
+ * is a RETRYABLE INFRA failure — see isRetryableGithubError.
56
+ */
57
+ export declare class RateLimitedError extends Error {
58
+ readonly status: number;
59
+ readonly resource: string | null;
60
+ /** ISO timestamp the quota window resets at, when GitHub said. */
61
+ readonly reset_at: string | null;
62
+ readonly retry_after_s: number | null;
63
+ constructor(opts: {
64
+ status: number;
65
+ resource: string | null;
66
+ reset_at: string | null;
67
+ retry_after_s: number | null;
68
+ });
69
+ }
70
+ /**
71
+ * Soft-budget exhaustion. NOT a code failure: the task simply did as much
72
+ * direct GitHub work as one task execution is allowed. The caller should
73
+ * surface it as a clean, RETRYABLE infra failure and NEVER feed it to the
74
+ * claude-exit classifier (which would quarantine the runner).
75
+ */
76
+ export declare class GithubBudgetExceededError extends Error {
77
+ readonly used: number;
78
+ readonly budget: number;
79
+ constructor(used: number, budget: number);
80
+ }
81
+ /**
82
+ * v0.53 T-53-4: both client failure modes are retryable infra conditions,
83
+ * not task/code failures. The contract the runner relies on: an error for
84
+ * which this returns true must surface as `phase: "github"` / retryable and
85
+ * MUST NOT set `quarantine_cause` — i.e. it is classifier-neutral and never
86
+ * benches the runner.
87
+ */
88
+ export declare function isRetryableGithubError(err: unknown): boolean;
89
+ interface ResourceQuota {
90
+ remaining: number | null;
91
+ /** ISO timestamp. */
92
+ reset_at: string | null;
93
+ }
94
+ export interface GithubQuotaSnapshot {
95
+ /** Lowest remaining across observed resources (the binding constraint). */
96
+ remaining: number | null;
97
+ /** Reset time of that lowest-remaining resource. */
98
+ reset_at: string | null;
99
+ /** Direct REST calls made since the last startTaskGithubBudget(). */
100
+ used_this_task: number;
101
+ /** Per-resource view (core, search, …) as reported by GitHub headers. */
102
+ resources: Record<string, ResourceQuota>;
103
+ }
104
+ /**
105
+ * Begin a budgeted task execution: resets the per-task counter and arms the
106
+ * soft budget. runTask calls this at the top of a task; the explicit reset
107
+ * also re-baselines a long-lived watch process between tasks.
108
+ */
109
+ export declare function startTaskGithubBudget(budget?: number): void;
110
+ /**
111
+ * End a budgeted task execution: disarms the soft budget so any direct REST
112
+ * call made OUTSIDE a task run (e.g. doctor) is unbudgeted. Idempotent.
113
+ */
114
+ export declare function endTaskGithubBudget(): void;
115
+ export declare function getQuotaSnapshot(): GithubQuotaSnapshot;
116
+ /** Test seam: wipe quota/budget state between unit tests. */
117
+ export declare function resetGithubClientStateForTests(): void;
118
+ /** Injection seams for unit tests; production callers never pass this. */
119
+ export interface GithubFetchDeps {
120
+ fetchImpl?: typeof fetch;
121
+ sleep?: (ms: number) => Promise<void>;
122
+ /** Returns [0, 1); defaults to Math.random. Drives backoff jitter. */
123
+ random?: () => number;
124
+ }
125
+ /**
126
+ * Authenticated GitHub REST fetch. The caller supplies whatever token is
127
+ * appropriate (the runner uses the operator's own gh/git credentials for
128
+ * CLI calls; a direct REST consumer would pass a token it already holds).
129
+ * An empty token sends an anonymous request rather than `Bearer <empty>`.
130
+ */
131
+ export declare function githubFetch(url: string, token: string, init?: RequestInit, deps?: GithubFetchDeps): Promise<Response>;
132
+ export {};
133
+ //# sourceMappingURL=github-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-client.d.ts","sourceRoot":"","sources":["../src/github-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,2CAA2C;AAC3C,eAAO,MAAM,0BAA0B,KAAK,CAAC;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,cAAc,QAAS,CAAC;AAErC,yEAAyE;AACzE,eAAO,MAAM,eAAe,OAAQ,CAAC;AAErC;;;;;GAKG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;IACzC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;gBAE1B,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B;CAYF;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBAEZ,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAMzC;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,UAAU,aAAa;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,qBAAqB;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;IACvB,yEAAyE;IACzE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;CAC1C;AAOD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,GAAE,MAAmC,GAAG,IAAI,CAGvF;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAED,wBAAgB,gBAAgB,IAAI,mBAAmB,CAkBtD;AAED,6DAA6D;AAC7D,wBAAgB,8BAA8B,IAAI,IAAI,CAIrD;AA6CD,0EAA0E;AAC1E,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC;CACvB;AAMD;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,IAAI,GAAE,WAAgB,EACtB,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,QAAQ,CAAC,CAwCnB"}