instar 1.3.430 → 1.3.432
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/dashboard/subscriptions.js +21 -0
- package/dist/core/CompletionEvaluator.d.ts +12 -5
- package/dist/core/CompletionEvaluator.d.ts.map +1 -1
- package/dist/core/CompletionEvaluator.js +34 -15
- package/dist/core/CompletionEvaluator.js.map +1 -1
- package/dist/core/OAuthRefresher.d.ts +112 -0
- package/dist/core/OAuthRefresher.d.ts.map +1 -0
- package/dist/core/OAuthRefresher.js +222 -0
- package/dist/core/OAuthRefresher.js.map +1 -0
- package/dist/core/QuotaPoller.d.ts +33 -6
- package/dist/core/QuotaPoller.d.ts.map +1 -1
- package/dist/core/QuotaPoller.js +73 -66
- package/dist/core/QuotaPoller.js.map +1 -1
- package/dist/core/SubscriptionPool.d.ts +8 -0
- package/dist/core/SubscriptionPool.d.ts.map +1 -1
- package/dist/core/SubscriptionPool.js +3 -0
- package/dist/core/SubscriptionPool.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.431.md +23 -0
- package/upgrades/1.3.432.md +27 -0
- package/upgrades/side-effects/p13-stop-rationale-peer-pursuit.md +69 -0
- package/upgrades/side-effects/subscription-token-autorefresh.md +29 -0
|
@@ -90,6 +90,20 @@ export function countdown(iso, now = Date.now(), { expiredWord = 'expired' } = {
|
|
|
90
90
|
return `${sec}s`;
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
/** A coarse "N ago" for a PAST ISO timestamp (token-refresh recency). '' if invalid. */
|
|
94
|
+
export function relativeAge(iso, now = Date.now()) {
|
|
95
|
+
const t = typeof iso === 'string' ? Date.parse(iso) : NaN;
|
|
96
|
+
if (Number.isNaN(t)) return '';
|
|
97
|
+
const sec = Math.floor((now - t) / 1000);
|
|
98
|
+
if (sec < 0) return 'just now';
|
|
99
|
+
if (sec < 60) return 'just now';
|
|
100
|
+
const min = Math.floor(sec / 60);
|
|
101
|
+
if (min < 60) return `${min}m ago`;
|
|
102
|
+
const hr = Math.floor(min / 60);
|
|
103
|
+
if (hr < 24) return `${hr}h ago`;
|
|
104
|
+
return `${Math.floor(hr / 24)}d ago`;
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
// ── DOM helpers (textContent ONLY — never innerHTML) ────────────────────────
|
|
94
108
|
function el(doc, tag, cls, text) {
|
|
95
109
|
const node = doc.createElement(tag);
|
|
@@ -142,6 +156,13 @@ export function renderAccounts(doc, target, accounts, now = Date.now()) {
|
|
|
142
156
|
} else {
|
|
143
157
|
card.appendChild(el(doc, 'div', 'sub-account-noquota', 'No quota reading yet.'));
|
|
144
158
|
}
|
|
159
|
+
// Token health: when the poller silently refreshed the access token from the
|
|
160
|
+
// refresh token, show it — so a routine access-token expiry reads as healthy
|
|
161
|
+
// (auto-handled) rather than looking like a re-auth event.
|
|
162
|
+
const refAge = a && a.lastRefreshAt ? relativeAge(a.lastRefreshAt, now) : null;
|
|
163
|
+
if (refAge) {
|
|
164
|
+
card.appendChild(el(doc, 'div', 'sub-account-refresh', `Token auto-refreshed ${refAge}`));
|
|
165
|
+
}
|
|
145
166
|
target.appendChild(card);
|
|
146
167
|
}
|
|
147
168
|
}
|
|
@@ -56,11 +56,18 @@ export declare class CompletionEvaluator {
|
|
|
56
56
|
private parse;
|
|
57
57
|
/**
|
|
58
58
|
* P13 "The Stop Reason Is the Work" guard. Given the recent transcript, decide
|
|
59
|
-
* whether an autonomous stop-attempt is EARNED, or whether it rests on
|
|
60
|
-
* anti-pattern
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
59
|
+
* whether an autonomous stop-attempt is EARNED, or whether it rests on a P13
|
|
60
|
+
* anti-pattern — ending the run citing any of:
|
|
61
|
+
* - "needs a judgment call" / "needs real engineering" (a derivable standard
|
|
62
|
+
* or buildable engineering dressed up as a stop reason);
|
|
63
|
+
* - "blocked on / waiting on another agent (or the operator)" (a peer
|
|
64
|
+
* dependency is NOT terminal — the agent must keep pursuing it: re-ping,
|
|
65
|
+
* periodic re-check, alternate path, or advance other work);
|
|
66
|
+
* - "a waiting / polling loop burns resources / spins the box" (waiting on a
|
|
67
|
+
* peer is not a CPU burn; a cheap periodic check is the right strategy).
|
|
68
|
+
* ...WITHOUT showing a derived standard, a built artifact, or a genuinely
|
|
69
|
+
* operator-only residual it has already pursued. Returns stopAllowed:false +
|
|
70
|
+
* guidance when the anti-pattern is detected; otherwise stopAllowed:true.
|
|
64
71
|
*
|
|
65
72
|
* Fails OPEN (stopAllowed:true) on error or ambiguity: this is a SECONDARY guard
|
|
66
73
|
* on top of the completion check, so an evaluator hiccup must never TRAP a
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompletionEvaluator.d.ts","sourceRoot":"","sources":["../../src/core/CompletionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,GAAG,EAAE,OAAO,CAAC;IACb,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,oBAAoB,CAAC;IACnC,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CAC7C;AAID,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAEhD,IAAI,EAAE,uBAAuB;IAKzC;;;;;OAKG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiBrF,OAAO,CAAC,WAAW;IAiBnB,gFAAgF;IAChF,OAAO,CAAC,KAAK;IAcb
|
|
1
|
+
{"version":3,"file":"CompletionEvaluator.d.ts","sourceRoot":"","sources":["../../src/core/CompletionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,GAAG,EAAE,OAAO,CAAC;IACb,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACnC,uEAAuE;IACvE,WAAW,EAAE,OAAO,CAAC;IACrB,0FAA0F;IAC1F,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,oBAAoB,CAAC;IACnC,mFAAmF;IACnF,SAAS,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAC;CAC7C;AAID,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAuB;IACpD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;gBAEhD,IAAI,EAAE,uBAAuB;IAKzC;;;;;OAKG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAiBrF,OAAO,CAAC,WAAW;IAiBnB,gFAAgF;IAChF,OAAO,CAAC,KAAK;IAcb;;;;;;;;;;;;;;;;;;OAkBG;IACG,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAkBlF,OAAO,CAAC,wBAAwB;IAyChC,2FAA2F;IAC3F,OAAO,CAAC,kBAAkB;IAoB1B,IAAI,aAAa,IAAI,MAAM,CAE1B;CACF"}
|
|
@@ -81,11 +81,18 @@ export class CompletionEvaluator {
|
|
|
81
81
|
}
|
|
82
82
|
/**
|
|
83
83
|
* P13 "The Stop Reason Is the Work" guard. Given the recent transcript, decide
|
|
84
|
-
* whether an autonomous stop-attempt is EARNED, or whether it rests on
|
|
85
|
-
* anti-pattern
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
84
|
+
* whether an autonomous stop-attempt is EARNED, or whether it rests on a P13
|
|
85
|
+
* anti-pattern — ending the run citing any of:
|
|
86
|
+
* - "needs a judgment call" / "needs real engineering" (a derivable standard
|
|
87
|
+
* or buildable engineering dressed up as a stop reason);
|
|
88
|
+
* - "blocked on / waiting on another agent (or the operator)" (a peer
|
|
89
|
+
* dependency is NOT terminal — the agent must keep pursuing it: re-ping,
|
|
90
|
+
* periodic re-check, alternate path, or advance other work);
|
|
91
|
+
* - "a waiting / polling loop burns resources / spins the box" (waiting on a
|
|
92
|
+
* peer is not a CPU burn; a cheap periodic check is the right strategy).
|
|
93
|
+
* ...WITHOUT showing a derived standard, a built artifact, or a genuinely
|
|
94
|
+
* operator-only residual it has already pursued. Returns stopAllowed:false +
|
|
95
|
+
* guidance when the anti-pattern is detected; otherwise stopAllowed:true.
|
|
89
96
|
*
|
|
90
97
|
* Fails OPEN (stopAllowed:true) on error or ambiguity: this is a SECONDARY guard
|
|
91
98
|
* on top of the completion check, so an evaluator hiccup must never TRAP a
|
|
@@ -115,11 +122,18 @@ export class CompletionEvaluator {
|
|
|
115
122
|
'standard P13 "The Stop Reason Is the Work." Judge ONLY the transcript below.',
|
|
116
123
|
'',
|
|
117
124
|
'Decide whether the agent is ENDING / STOPPING its autonomous run, and if so,',
|
|
118
|
-
'WHY. The stop is NOT earned (BLOCK it) when the stated reason is
|
|
119
|
-
'"I need a judgment call from the user" or "this needs real engineering / a',
|
|
120
|
-
'careful build / reverse-engineering" —
|
|
121
|
-
'
|
|
122
|
-
'
|
|
125
|
+
'WHY. The stop is NOT earned (BLOCK it) when the stated reason is any of:',
|
|
126
|
+
'- "I need a judgment call from the user" or "this needs real engineering / a',
|
|
127
|
+
' careful build / reverse-engineering" — a judgment gap (a DERIVABLE standard)',
|
|
128
|
+
' or buildable engineering (which the agent can DO) dressed up as a stop reason;',
|
|
129
|
+
'- "I am blocked / waiting on another agent (or on the operator) to respond or',
|
|
130
|
+
' act" — a dependency on a peer is NOT a terminal blocker. It is the agent\'s',
|
|
131
|
+
' job to keep PURSUING it: re-ping the peer, check for a reply on a cadence,',
|
|
132
|
+
' find an alternate path, or advance other open work — NOT to end the run;',
|
|
133
|
+
'- "an idle / waiting / polling loop burns resources / spins the box / wastes',
|
|
134
|
+
' tokens or CPU" — waiting on a peer is NOT a resource burn, and a cheap',
|
|
135
|
+
' periodic check is the correct strategy; resource cost is not a reason to',
|
|
136
|
+
' stop while real work still remains.',
|
|
123
137
|
'',
|
|
124
138
|
'The stop IS earned (ALLOW it) when ANY of these is shown in the transcript:',
|
|
125
139
|
'- a DERIVED STANDARD the agent reasoned out and is proceeding under (even if',
|
|
@@ -127,11 +141,14 @@ export class CompletionEvaluator {
|
|
|
127
141
|
'- a BUILT ARTIFACT produced this run (a PR/commit, a spec/file written, a test',
|
|
128
142
|
' result, a converged artifact handed over for review);',
|
|
129
143
|
"- a genuinely OPERATOR-ONLY residual (a credential/account the user holds, a",
|
|
130
|
-
" real value/priority/risk judgment that is the user's, a required approval,",
|
|
131
|
-
'
|
|
144
|
+
" real value/priority/risk judgment that is the user's, a required approval, a",
|
|
145
|
+
' legal/billing/payment action) that the agent has ALREADY actively pursued',
|
|
146
|
+
' (asked / queued the request) AND with no other open work it could advance',
|
|
147
|
+
' in the meantime;',
|
|
132
148
|
'- a DURATION limit reached or an EMERGENCY stop;',
|
|
133
149
|
'- the agent is NOT actually stopping (it is continuing / re-scoping / moving',
|
|
134
|
-
' to another topic
|
|
150
|
+
' to another topic, or periodically checking for a peer reply while it keeps',
|
|
151
|
+
' advancing other work).',
|
|
135
152
|
'',
|
|
136
153
|
`RECENT TRANSCRIPT (most recent last):\n${transcriptTail}`,
|
|
137
154
|
'',
|
|
@@ -146,12 +163,14 @@ export class CompletionEvaluator {
|
|
|
146
163
|
return { stopAllowed: true, guidance: '' };
|
|
147
164
|
const lines = text.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
148
165
|
const first = (lines[0] || '').toUpperCase();
|
|
149
|
-
|
|
166
|
+
// Reason is the SECOND line only — a bare verdict (just "STOP_BLOCKED") must
|
|
167
|
+
// fall through to the rich default guidance below, never echo the verdict token.
|
|
168
|
+
const reason = (lines[1] || '').slice(0, 300);
|
|
150
169
|
// Check BLOCKED before OK (explicit; they share no substring but keep the order clear).
|
|
151
170
|
if (/\bSTOP[_ ]?BLOCKED\b/.test(first)) {
|
|
152
171
|
return {
|
|
153
172
|
stopAllowed: false,
|
|
154
|
-
guidance: reason || 'P13:
|
|
173
|
+
guidance: reason || 'P13: a stop is not earned by a judgment-call / needs-engineering reason, by "blocked on another agent" (a peer dependency is not terminal — keep pursuing: re-ping + check on a cadence + advance other work), or by "a waiting/polling loop burns resources". Derive+document the standard and proceed, build the artifact and hand it over, or keep actively pursuing the dependency; reserve the stop for a genuinely operator-only residual you have already pursued with no other work to advance.',
|
|
155
174
|
};
|
|
156
175
|
}
|
|
157
176
|
if (/\bSTOP[_ ]?OK\b/.test(first))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CompletionEvaluator.js","sourceRoot":"","sources":["../../src/core/CompletionEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA+BH,MAAM,cAAc,GAAG,oBAAoB,CAAC;AAE5C,MAAM,OAAO,mBAAmB;IACb,YAAY,CAAuB;IACnC,SAAS,CAAkC;IAE5D,YAAY,IAA6B;QACvC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC;QACtC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,MAAM,CAAC;IAC5C,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,QAAQ,CAAC,SAAiB,EAAE,cAAsB;QACtD,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;QAC3D,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAC7C,KAAK,EAAE,IAAI,CAAC,SAAS;gBACrB,WAAW,EAAE,CAAC;gBACd,SAAS,EAAE,GAAG;gBACd,SAAS,EAAE,MAAM;gBACjB,WAAW,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE;aAClD,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACvH,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;IAEO,WAAW,CAAC,SAAiB,EAAE,cAAsB;QAC3D,OAAO;YACL,2EAA2E;YAC3E,8EAA8E;YAC9E,6EAA6E;YAC7E,8EAA8E;YAC9E,oEAAoE;YACpE,EAAE;YACF,0BAA0B,SAAS,EAAE;YACrC,EAAE;YACF,0CAA0C,cAAc,EAAE;YAC1D,EAAE;YACF,6EAA6E;YAC7E,2CAA2C;SAC5C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IAED,gFAAgF;IACxE,KAAK,CAAC,GAAW;QACvB,MAAM,IAAI,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,IAAI;YAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,yCAAyC,EAAE,CAAC;QACpF,4CAA4C;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7C,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1D,gEAAgE;QAChE,IAAI,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,IAAI,uBAAuB,EAAE,CAAC;QACpG,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,IAAI,eAAe,EAAE,CAAC;QACnF,6CAA6C;QAC7C,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,0CAA0C,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC;IAChG,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"CompletionEvaluator.js","sourceRoot":"","sources":["../../src/core/CompletionEvaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuthRefresher — mint a fresh Claude Code OAuth access token from its stored
|
|
3
|
+
* refresh token, so the QuotaPoller never falsely flags a still-valid login as
|
|
4
|
+
* `needs-reauth` (P1.2 hardening of the Subscription & Auth Standard).
|
|
5
|
+
*
|
|
6
|
+
* ── Why this exists (the bug it fixes) ──
|
|
7
|
+
* A Claude Code login holds TWO tokens in its config home's credential store:
|
|
8
|
+
* - a short-lived ACCESS token (`sk-ant-oat…`, ~8–12h), and
|
|
9
|
+
* - a long-lived REFRESH token (`sk-ant-ort…`, weeks→months).
|
|
10
|
+
* The `claude` client silently exchanges the refresh token for a new access
|
|
11
|
+
* token on every real use. The QuotaPoller reads the access token out-of-band
|
|
12
|
+
* and calls the usage endpoint directly — so when the access token has expired
|
|
13
|
+
* (which is routine, daily) but the refresh token is still perfectly valid, the
|
|
14
|
+
* usage read returns 401 and the poller wrongly marks the account `needs-reauth`.
|
|
15
|
+
* That cried wolf: the login is intact, only the access token lapsed. This module
|
|
16
|
+
* performs the same refresh-token exchange the client does, so a routine expiry
|
|
17
|
+
* recovers silently and `needs-reauth` is reserved for a genuinely dead login
|
|
18
|
+
* (refresh token revoked / password change) — exactly what SubscriptionPool's
|
|
19
|
+
* status comment already promises.
|
|
20
|
+
*
|
|
21
|
+
* ── Corruption safety (the load-bearing invariant) ──
|
|
22
|
+
* The ONLY way this module could harm a working login is by writing a bad
|
|
23
|
+
* credential back. So the write is gated three ways:
|
|
24
|
+
* 1. it happens ONLY on a fully-validated 200 response (new access token shaped
|
|
25
|
+
* `sk-ant-oat…`, a positive numeric `expires_in`);
|
|
26
|
+
* 2. it is a READ-MERGE-WRITE — the existing credential JSON is re-read and only
|
|
27
|
+
* the access token / refresh token / expiry are overwritten, so scopes,
|
|
28
|
+
* subscriptionType, rateLimitTier and any unknown fields are preserved; and
|
|
29
|
+
* 3. if the server rotates the refresh token, the NEW one is persisted; if the
|
|
30
|
+
* response omits a refresh token (non-rotating server), the existing one is
|
|
31
|
+
* kept — never dropped.
|
|
32
|
+
* A wrong endpoint / client id / network failure can therefore only ever make the
|
|
33
|
+
* exchange FAIL (→ the caller's existing `needs-reauth` path), never corrupt.
|
|
34
|
+
*
|
|
35
|
+
* Token values are NEVER logged or returned to any persisted surface. The OAuth
|
|
36
|
+
* token endpoint + client id are the public Claude Code values, extracted from
|
|
37
|
+
* the official client binary (verified 2026-06-08).
|
|
38
|
+
*
|
|
39
|
+
* Testability: the credential store, the fetch surface, and the clock are all
|
|
40
|
+
* injectable, so the whole refresh runs hermetically with zero keychain, zero
|
|
41
|
+
* network, and a deterministic clock in tests.
|
|
42
|
+
*/
|
|
43
|
+
/** Public Claude Code OAuth token endpoint (from the official client binary). */
|
|
44
|
+
export declare const CLAUDE_TOKEN_URL = "https://platform.claude.com/v1/oauth/token";
|
|
45
|
+
/** Public Claude Code OAuth client id (from the official client binary). */
|
|
46
|
+
export declare const CLAUDE_CODE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
47
|
+
export type RefreshFailReason = 'unsupported-account' | 'read-failed' | 'no-refresh-token' | 'exchange-failed' | 'malformed-response' | 'write-failed';
|
|
48
|
+
export type RefreshResult = {
|
|
49
|
+
ok: true;
|
|
50
|
+
accessToken: string;
|
|
51
|
+
expiresAt: number;
|
|
52
|
+
rotated: boolean;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
reason: RefreshFailReason;
|
|
56
|
+
status?: number;
|
|
57
|
+
};
|
|
58
|
+
/** The OAuth access + refresh tokens parsed out of a credential store entry. */
|
|
59
|
+
export interface ClaudeOauth {
|
|
60
|
+
accessToken?: string;
|
|
61
|
+
refreshToken?: string;
|
|
62
|
+
expiresAt?: number;
|
|
63
|
+
[k: string]: unknown;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A credential store for one config home. `read` returns the RAW JSON string of
|
|
67
|
+
* the stored entry (so the refresher can merge-preserve every field); `write`
|
|
68
|
+
* persists a replacement raw JSON string. Injectable so tests use a fake.
|
|
69
|
+
*/
|
|
70
|
+
export interface CredentialStore {
|
|
71
|
+
read(configHome: string): string | null;
|
|
72
|
+
write(configHome: string, rawJson: string): boolean;
|
|
73
|
+
}
|
|
74
|
+
/** POST-capable fetch surface (distinct from QuotaPoller's GET-only FetchImpl). */
|
|
75
|
+
export type RefreshFetch = (url: string, init: {
|
|
76
|
+
method: string;
|
|
77
|
+
headers: Record<string, string>;
|
|
78
|
+
body: string;
|
|
79
|
+
}) => Promise<{
|
|
80
|
+
ok: boolean;
|
|
81
|
+
status: number;
|
|
82
|
+
json: () => Promise<unknown>;
|
|
83
|
+
}>;
|
|
84
|
+
export interface RefreshDeps {
|
|
85
|
+
store?: CredentialStore;
|
|
86
|
+
fetchImpl?: RefreshFetch;
|
|
87
|
+
now?: () => number;
|
|
88
|
+
tokenUrl?: string;
|
|
89
|
+
clientId?: string;
|
|
90
|
+
}
|
|
91
|
+
export declare function expandHome(p: string): string;
|
|
92
|
+
/**
|
|
93
|
+
* macOS keychain service name for a config home's Claude Code credentials.
|
|
94
|
+
* The default home (`~/.claude`) has no hash suffix; every other config home is
|
|
95
|
+
* suffixed with the first 8 hex of sha256(configHome) — verified empirically and
|
|
96
|
+
* matched by the official client.
|
|
97
|
+
*/
|
|
98
|
+
export declare function claudeCredentialService(configHome: string): string;
|
|
99
|
+
/** Non-darwin credential file for a config home. */
|
|
100
|
+
export declare function claudeCredentialFilePath(configHome: string): string;
|
|
101
|
+
/** Default store: macOS keychain, else a per-config-home credentials file. */
|
|
102
|
+
export declare const defaultCredentialStore: CredentialStore;
|
|
103
|
+
/** Parse the `claudeAiOauth` block out of a config home's credential store. */
|
|
104
|
+
export declare function readClaudeOauth(configHome: string, store?: CredentialStore): ClaudeOauth | null;
|
|
105
|
+
/**
|
|
106
|
+
* Refresh a config home's Claude Code access token from its stored refresh token.
|
|
107
|
+
* Returns the new access token + expiry on success; otherwise a typed failure
|
|
108
|
+
* reason the caller maps to its `needs-reauth` decision. Writes NOTHING on any
|
|
109
|
+
* failure — a working login is never put at risk by an unsuccessful refresh.
|
|
110
|
+
*/
|
|
111
|
+
export declare function refreshClaudeToken(configHome: string, deps?: RefreshDeps): Promise<RefreshResult>;
|
|
112
|
+
//# sourceMappingURL=OAuthRefresher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthRefresher.d.ts","sourceRoot":"","sources":["../../src/core/OAuthRefresher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAQH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,+CAA+C,CAAC;AAC7E,4EAA4E;AAC5E,eAAO,MAAM,qBAAqB,yCAAyC,CAAC;AAK5E,MAAM,MAAM,iBAAiB,GACzB,qBAAqB,GACrB,aAAa,GACb,kBAAkB,GAClB,iBAAiB,GACjB,oBAAoB,GACpB,cAAc,CAAC;AAEnB,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACtE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACxC,KAAK,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACrD;AAED,mFAAmF;AACnF,MAAM,MAAM,YAAY,GAAG,CACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,KACpE,OAAO,CAAC;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,EAAE,eAAe,CAAC;IACxB,SAAS,CAAC,EAAE,YAAY,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAK5C;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED,oDAAoD;AACpD,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,EAAE,eAoDpC,CAAC;AAEF,+EAA+E;AAC/E,wBAAgB,eAAe,CAC7B,UAAU,EAAE,MAAM,EAClB,KAAK,GAAE,eAAwC,GAC9C,WAAW,GAAG,IAAI,CAUpB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,MAAM,EAClB,IAAI,GAAE,WAAgB,GACrB,OAAO,CAAC,aAAa,CAAC,CA+ExB"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuthRefresher — mint a fresh Claude Code OAuth access token from its stored
|
|
3
|
+
* refresh token, so the QuotaPoller never falsely flags a still-valid login as
|
|
4
|
+
* `needs-reauth` (P1.2 hardening of the Subscription & Auth Standard).
|
|
5
|
+
*
|
|
6
|
+
* ── Why this exists (the bug it fixes) ──
|
|
7
|
+
* A Claude Code login holds TWO tokens in its config home's credential store:
|
|
8
|
+
* - a short-lived ACCESS token (`sk-ant-oat…`, ~8–12h), and
|
|
9
|
+
* - a long-lived REFRESH token (`sk-ant-ort…`, weeks→months).
|
|
10
|
+
* The `claude` client silently exchanges the refresh token for a new access
|
|
11
|
+
* token on every real use. The QuotaPoller reads the access token out-of-band
|
|
12
|
+
* and calls the usage endpoint directly — so when the access token has expired
|
|
13
|
+
* (which is routine, daily) but the refresh token is still perfectly valid, the
|
|
14
|
+
* usage read returns 401 and the poller wrongly marks the account `needs-reauth`.
|
|
15
|
+
* That cried wolf: the login is intact, only the access token lapsed. This module
|
|
16
|
+
* performs the same refresh-token exchange the client does, so a routine expiry
|
|
17
|
+
* recovers silently and `needs-reauth` is reserved for a genuinely dead login
|
|
18
|
+
* (refresh token revoked / password change) — exactly what SubscriptionPool's
|
|
19
|
+
* status comment already promises.
|
|
20
|
+
*
|
|
21
|
+
* ── Corruption safety (the load-bearing invariant) ──
|
|
22
|
+
* The ONLY way this module could harm a working login is by writing a bad
|
|
23
|
+
* credential back. So the write is gated three ways:
|
|
24
|
+
* 1. it happens ONLY on a fully-validated 200 response (new access token shaped
|
|
25
|
+
* `sk-ant-oat…`, a positive numeric `expires_in`);
|
|
26
|
+
* 2. it is a READ-MERGE-WRITE — the existing credential JSON is re-read and only
|
|
27
|
+
* the access token / refresh token / expiry are overwritten, so scopes,
|
|
28
|
+
* subscriptionType, rateLimitTier and any unknown fields are preserved; and
|
|
29
|
+
* 3. if the server rotates the refresh token, the NEW one is persisted; if the
|
|
30
|
+
* response omits a refresh token (non-rotating server), the existing one is
|
|
31
|
+
* kept — never dropped.
|
|
32
|
+
* A wrong endpoint / client id / network failure can therefore only ever make the
|
|
33
|
+
* exchange FAIL (→ the caller's existing `needs-reauth` path), never corrupt.
|
|
34
|
+
*
|
|
35
|
+
* Token values are NEVER logged or returned to any persisted surface. The OAuth
|
|
36
|
+
* token endpoint + client id are the public Claude Code values, extracted from
|
|
37
|
+
* the official client binary (verified 2026-06-08).
|
|
38
|
+
*
|
|
39
|
+
* Testability: the credential store, the fetch surface, and the clock are all
|
|
40
|
+
* injectable, so the whole refresh runs hermetically with zero keychain, zero
|
|
41
|
+
* network, and a deterministic clock in tests.
|
|
42
|
+
*/
|
|
43
|
+
import { execFileSync } from 'node:child_process';
|
|
44
|
+
import crypto from 'node:crypto';
|
|
45
|
+
import fs from 'node:fs';
|
|
46
|
+
import os from 'node:os';
|
|
47
|
+
import path from 'node:path';
|
|
48
|
+
/** Public Claude Code OAuth token endpoint (from the official client binary). */
|
|
49
|
+
export const CLAUDE_TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
|
|
50
|
+
/** Public Claude Code OAuth client id (from the official client binary). */
|
|
51
|
+
export const CLAUDE_CODE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
52
|
+
const ACCESS_PREFIX = 'sk-ant-oat';
|
|
53
|
+
const REFRESH_PREFIX = 'sk-ant-ort';
|
|
54
|
+
export function expandHome(p) {
|
|
55
|
+
if (p === '~' || p.startsWith('~/')) {
|
|
56
|
+
return path.join(process.env.HOME ?? '', p.slice(1));
|
|
57
|
+
}
|
|
58
|
+
return p;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* macOS keychain service name for a config home's Claude Code credentials.
|
|
62
|
+
* The default home (`~/.claude`) has no hash suffix; every other config home is
|
|
63
|
+
* suffixed with the first 8 hex of sha256(configHome) — verified empirically and
|
|
64
|
+
* matched by the official client.
|
|
65
|
+
*/
|
|
66
|
+
export function claudeCredentialService(configHome) {
|
|
67
|
+
const home = expandHome(configHome);
|
|
68
|
+
const defaultHome = expandHome('~/.claude');
|
|
69
|
+
return home === defaultHome
|
|
70
|
+
? 'Claude Code-credentials'
|
|
71
|
+
: `Claude Code-credentials-${crypto.createHash('sha256').update(home).digest('hex').slice(0, 8)}`;
|
|
72
|
+
}
|
|
73
|
+
/** Non-darwin credential file for a config home. */
|
|
74
|
+
export function claudeCredentialFilePath(configHome) {
|
|
75
|
+
return path.join(expandHome(configHome), '.credentials.json');
|
|
76
|
+
}
|
|
77
|
+
/** Default store: macOS keychain, else a per-config-home credentials file. */
|
|
78
|
+
export const defaultCredentialStore = {
|
|
79
|
+
read(configHome) {
|
|
80
|
+
if (process.platform === 'darwin') {
|
|
81
|
+
try {
|
|
82
|
+
const raw = execFileSync('security', ['find-generic-password', '-s', claudeCredentialService(configHome), '-w'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
83
|
+
return raw || null;
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return null; // @silent-fallback-ok: no keychain entry → unreadable
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
const p = claudeCredentialFilePath(configHome);
|
|
91
|
+
return fs.existsSync(p) ? fs.readFileSync(p, 'utf-8') : null;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null; // @silent-fallback-ok: missing/unreadable creds file
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
write(configHome, rawJson) {
|
|
98
|
+
if (process.platform === 'darwin') {
|
|
99
|
+
try {
|
|
100
|
+
execFileSync('security', [
|
|
101
|
+
'add-generic-password',
|
|
102
|
+
'-U', // update the existing entry in place
|
|
103
|
+
'-a',
|
|
104
|
+
os.userInfo().username,
|
|
105
|
+
'-s',
|
|
106
|
+
claudeCredentialService(configHome),
|
|
107
|
+
'-w',
|
|
108
|
+
rawJson,
|
|
109
|
+
], { stdio: ['ignore', 'ignore', 'ignore'] });
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return false; // @silent-fallback-ok: keychain write failed → caller falls to needs-reauth
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const p = claudeCredentialFilePath(configHome);
|
|
118
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
119
|
+
fs.writeFileSync(p, rawJson, { mode: 0o600 });
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return false; // @silent-fallback-ok: file write failed
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
/** Parse the `claudeAiOauth` block out of a config home's credential store. */
|
|
128
|
+
export function readClaudeOauth(configHome, store = defaultCredentialStore) {
|
|
129
|
+
const raw = store.read(configHome);
|
|
130
|
+
if (!raw)
|
|
131
|
+
return null;
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(raw);
|
|
134
|
+
const oauth = parsed?.claudeAiOauth;
|
|
135
|
+
return oauth && typeof oauth === 'object' ? oauth : null;
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return null; // @silent-fallback-ok: unparseable entry
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Refresh a config home's Claude Code access token from its stored refresh token.
|
|
143
|
+
* Returns the new access token + expiry on success; otherwise a typed failure
|
|
144
|
+
* reason the caller maps to its `needs-reauth` decision. Writes NOTHING on any
|
|
145
|
+
* failure — a working login is never put at risk by an unsuccessful refresh.
|
|
146
|
+
*/
|
|
147
|
+
export async function refreshClaudeToken(configHome, deps = {}) {
|
|
148
|
+
const store = deps.store ?? defaultCredentialStore;
|
|
149
|
+
const fetchImpl = deps.fetchImpl ??
|
|
150
|
+
((url, init) => fetch(url, init));
|
|
151
|
+
const now = deps.now ?? (() => Date.now());
|
|
152
|
+
const tokenUrl = deps.tokenUrl ?? CLAUDE_TOKEN_URL;
|
|
153
|
+
const clientId = deps.clientId ?? CLAUDE_CODE_CLIENT_ID;
|
|
154
|
+
const raw = store.read(configHome);
|
|
155
|
+
if (!raw)
|
|
156
|
+
return { ok: false, reason: 'read-failed' };
|
|
157
|
+
let parsed;
|
|
158
|
+
try {
|
|
159
|
+
parsed = JSON.parse(raw);
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return { ok: false, reason: 'read-failed' };
|
|
163
|
+
}
|
|
164
|
+
const oauth = (parsed?.claudeAiOauth ?? null);
|
|
165
|
+
const refreshToken = oauth?.refreshToken;
|
|
166
|
+
if (typeof refreshToken !== 'string' || !refreshToken) {
|
|
167
|
+
return { ok: false, reason: 'no-refresh-token' };
|
|
168
|
+
}
|
|
169
|
+
let res;
|
|
170
|
+
try {
|
|
171
|
+
res = await fetchImpl(tokenUrl, {
|
|
172
|
+
method: 'POST',
|
|
173
|
+
headers: { 'Content-Type': 'application/json' },
|
|
174
|
+
body: JSON.stringify({
|
|
175
|
+
grant_type: 'refresh_token',
|
|
176
|
+
refresh_token: refreshToken,
|
|
177
|
+
client_id: clientId,
|
|
178
|
+
}),
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return { ok: false, reason: 'exchange-failed' };
|
|
183
|
+
}
|
|
184
|
+
if (!res.ok)
|
|
185
|
+
return { ok: false, reason: 'exchange-failed', status: res.status };
|
|
186
|
+
let data;
|
|
187
|
+
try {
|
|
188
|
+
data = (await res.json());
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return { ok: false, reason: 'malformed-response' };
|
|
192
|
+
}
|
|
193
|
+
const newAccess = data?.access_token;
|
|
194
|
+
const expiresIn = data?.expires_in;
|
|
195
|
+
if (typeof newAccess !== 'string' || !newAccess.startsWith(ACCESS_PREFIX)) {
|
|
196
|
+
return { ok: false, reason: 'malformed-response' };
|
|
197
|
+
}
|
|
198
|
+
if (typeof expiresIn !== 'number' || !Number.isFinite(expiresIn) || expiresIn <= 0) {
|
|
199
|
+
return { ok: false, reason: 'malformed-response' };
|
|
200
|
+
}
|
|
201
|
+
const newRefresh = data?.refresh_token;
|
|
202
|
+
const rotated = typeof newRefresh === 'string' &&
|
|
203
|
+
newRefresh.startsWith(REFRESH_PREFIX) &&
|
|
204
|
+
newRefresh !== refreshToken;
|
|
205
|
+
const expiresAt = now() + expiresIn * 1000;
|
|
206
|
+
// READ-MERGE-WRITE: preserve every existing field, overwrite only the tokens
|
|
207
|
+
// + expiry. Keep the old refresh token if the server didn't rotate.
|
|
208
|
+
const updatedOauth = {
|
|
209
|
+
...oauth,
|
|
210
|
+
accessToken: newAccess,
|
|
211
|
+
refreshToken: typeof newRefresh === 'string' && newRefresh.startsWith(REFRESH_PREFIX)
|
|
212
|
+
? newRefresh
|
|
213
|
+
: refreshToken,
|
|
214
|
+
expiresAt,
|
|
215
|
+
};
|
|
216
|
+
const updatedRaw = { ...parsed, claudeAiOauth: updatedOauth };
|
|
217
|
+
if (!store.write(configHome, JSON.stringify(updatedRaw))) {
|
|
218
|
+
return { ok: false, reason: 'write-failed' };
|
|
219
|
+
}
|
|
220
|
+
return { ok: true, accessToken: newAccess, expiresAt, rotated };
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=OAuthRefresher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OAuthRefresher.js","sourceRoot":"","sources":["../../src/core/OAuthRefresher.ts"],"names":[],"mappings":"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"}
|
|
@@ -32,8 +32,15 @@
|
|
|
32
32
|
* poller runs hermetically with zero credentials and zero network in tests.
|
|
33
33
|
*/
|
|
34
34
|
import type { SubscriptionPool, SubscriptionAccount, AccountQuotaSnapshot } from './SubscriptionPool.js';
|
|
35
|
+
import { type RefreshResult } from './OAuthRefresher.js';
|
|
35
36
|
/** Injectable token resolver — returns an account's OAuth access token or null. */
|
|
36
37
|
export type TokenResolver = (account: SubscriptionAccount) => string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Injectable account refresher — exchanges a config home's stored refresh token
|
|
40
|
+
* for a fresh access token (see OAuthRefresher). Defaults to the real keychain/
|
|
41
|
+
* file-backed refresh; tests inject a stub so the poller runs hermetically.
|
|
42
|
+
*/
|
|
43
|
+
export type AccountRefresher = (account: SubscriptionAccount) => Promise<RefreshResult>;
|
|
37
44
|
/** Minimal fetch surface so tests inject a stub (no global fetch dependency). */
|
|
38
45
|
export type FetchImpl = (url: string, init: {
|
|
39
46
|
headers: Record<string, string>;
|
|
@@ -50,6 +57,12 @@ export interface QuotaPollerConfig {
|
|
|
50
57
|
fetchImpl?: FetchImpl;
|
|
51
58
|
/** Injected for tests; defaults to the config-home credential resolver. */
|
|
52
59
|
tokenResolver?: TokenResolver;
|
|
60
|
+
/**
|
|
61
|
+
* Injected for tests; defaults to the real OAuth refresh-token exchange. On a
|
|
62
|
+
* usage-read auth failure the poller calls this BEFORE declaring needs-reauth,
|
|
63
|
+
* so a routine access-token expiry recovers silently instead of crying wolf.
|
|
64
|
+
*/
|
|
65
|
+
refresher?: AccountRefresher;
|
|
53
66
|
/** Logger (defaults to console). */
|
|
54
67
|
logger?: {
|
|
55
68
|
log: (m: string) => void;
|
|
@@ -66,10 +79,12 @@ export interface BurnRate {
|
|
|
66
79
|
}
|
|
67
80
|
/**
|
|
68
81
|
* Resolve a claude-code account's OAuth access token from its config home,
|
|
69
|
-
* TRANSIENTLY. Never persisted, never logged.
|
|
70
|
-
* keychain
|
|
71
|
-
*
|
|
72
|
-
*
|
|
82
|
+
* TRANSIENTLY. Never persisted, never logged. Reads via the shared OAuthRefresher
|
|
83
|
+
* locator (macOS keychain `Claude Code-credentials-<sha256(configHome)[0:8]>`,
|
|
84
|
+
* else `<configHome>/.credentials.json`) so the resolver and the refresher always
|
|
85
|
+
* agree on WHERE a config home's credentials live. NOTE: an EXPIRED access token
|
|
86
|
+
* is still returned here (it's a valid string) — expiry is detected by the usage
|
|
87
|
+
* read's 401 and recovered by the refresher, not by this resolver.
|
|
73
88
|
*/
|
|
74
89
|
export declare function defaultTokenResolver(account: SubscriptionAccount): string | null;
|
|
75
90
|
/**
|
|
@@ -92,6 +107,7 @@ export declare class QuotaPoller {
|
|
|
92
107
|
private readonly pollIntervalMs;
|
|
93
108
|
private readonly fetchImpl;
|
|
94
109
|
private readonly tokenResolver;
|
|
110
|
+
private readonly refresher;
|
|
95
111
|
private readonly logger;
|
|
96
112
|
private interval;
|
|
97
113
|
/** Most-recent snapshot per account id. */
|
|
@@ -101,10 +117,21 @@ export declare class QuotaPoller {
|
|
|
101
117
|
constructor(config: QuotaPollerConfig);
|
|
102
118
|
start(): void;
|
|
103
119
|
stop(): void;
|
|
120
|
+
/**
|
|
121
|
+
* One usage read. Returns the parsed body (or null body on a non-auth non-ok),
|
|
122
|
+
* an auth-failed marker (401/403), or null on a network failure. NEVER logs the
|
|
123
|
+
* token.
|
|
124
|
+
*/
|
|
125
|
+
private readUsage;
|
|
126
|
+
private markNeedsReauth;
|
|
104
127
|
/**
|
|
105
128
|
* Poll one account: resolve token transiently, read usage, map to snapshot.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
129
|
+
* On a usage-read auth failure (401/403) the access token may simply have
|
|
130
|
+
* EXPIRED while the refresh token is still valid — so the poller attempts a
|
|
131
|
+
* refresh-token exchange and ONE retry BEFORE declaring needs-reauth. Only a
|
|
132
|
+
* genuinely dead login (no refresh token / refresh rejected / still 401 after
|
|
133
|
+
* a fresh token) yields needs-reauth. Returns null when the token is
|
|
134
|
+
* unresolvable or the read fails. NEVER logs or returns the token.
|
|
108
135
|
*/
|
|
109
136
|
pollAccount(account: SubscriptionAccount): Promise<AccountQuotaSnapshot | null>;
|
|
110
137
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaPoller.d.ts","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;
|
|
1
|
+
{"version":3,"file":"QuotaPoller.d.ts","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"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"}
|
package/dist/core/QuotaPoller.js
CHANGED
|
@@ -31,61 +31,26 @@
|
|
|
31
31
|
* Testability: `fetchImpl` and `tokenResolver` are injectable so the whole
|
|
32
32
|
* poller runs hermetically with zero credentials and zero network in tests.
|
|
33
33
|
*/
|
|
34
|
-
import { execFileSync } from 'node:child_process';
|
|
35
|
-
import crypto from 'node:crypto';
|
|
36
34
|
import fs from 'node:fs';
|
|
37
35
|
import path from 'node:path';
|
|
36
|
+
import { readClaudeOauth, refreshClaudeToken, expandHome, } from './OAuthRefresher.js';
|
|
38
37
|
const USAGE_URL = 'https://api.anthropic.com/api/oauth/usage';
|
|
39
38
|
/**
|
|
40
39
|
* Resolve a claude-code account's OAuth access token from its config home,
|
|
41
|
-
* TRANSIENTLY. Never persisted, never logged.
|
|
42
|
-
* keychain
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* TRANSIENTLY. Never persisted, never logged. Reads via the shared OAuthRefresher
|
|
41
|
+
* locator (macOS keychain `Claude Code-credentials-<sha256(configHome)[0:8]>`,
|
|
42
|
+
* else `<configHome>/.credentials.json`) so the resolver and the refresher always
|
|
43
|
+
* agree on WHERE a config home's credentials live. NOTE: an EXPIRED access token
|
|
44
|
+
* is still returned here (it's a valid string) — expiry is detected by the usage
|
|
45
|
+
* read's 401 and recovered by the refresher, not by this resolver.
|
|
45
46
|
*/
|
|
46
47
|
export function defaultTokenResolver(account) {
|
|
47
48
|
if (account.provider !== 'anthropic' || account.framework !== 'claude-code') {
|
|
48
49
|
return null;
|
|
49
50
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const credPath = path.join(configHome, '.credentials.json');
|
|
55
|
-
if (fs.existsSync(credPath)) {
|
|
56
|
-
const data = JSON.parse(fs.readFileSync(credPath, 'utf-8'));
|
|
57
|
-
const tok = data?.claudeAiOauth?.accessToken;
|
|
58
|
-
return typeof tok === 'string' && tok.startsWith('sk-ant-oat') ? tok : null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
// @silent-fallback-ok: missing/unreadable creds → unresolvable token (null)
|
|
63
|
-
}
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
// macOS: keychain service name, hash-suffixed by config-home path.
|
|
67
|
-
const defaultHome = expandHome('~/.claude');
|
|
68
|
-
const service = configHome === defaultHome
|
|
69
|
-
? 'Claude Code-credentials'
|
|
70
|
-
: `Claude Code-credentials-${crypto.createHash('sha256').update(configHome).digest('hex').slice(0, 8)}`;
|
|
71
|
-
try {
|
|
72
|
-
const raw = execFileSync('security', ['find-generic-password', '-s', service, '-w'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'] }).trim();
|
|
73
|
-
if (!raw)
|
|
74
|
-
return null;
|
|
75
|
-
const data = JSON.parse(raw);
|
|
76
|
-
const tok = data?.claudeAiOauth?.accessToken;
|
|
77
|
-
return typeof tok === 'string' && tok.startsWith('sk-ant-oat') ? tok : null;
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
// @silent-fallback-ok: no keychain entry for this config home → null
|
|
81
|
-
return null;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
function expandHome(p) {
|
|
85
|
-
if (p === '~' || p.startsWith('~/')) {
|
|
86
|
-
return path.join(process.env.HOME ?? '', p.slice(1));
|
|
87
|
-
}
|
|
88
|
-
return p;
|
|
51
|
+
const oauth = readClaudeOauth(account.configHome);
|
|
52
|
+
const tok = oauth?.accessToken;
|
|
53
|
+
return typeof tok === 'string' && tok.startsWith('sk-ant-oat') ? tok : null;
|
|
89
54
|
}
|
|
90
55
|
/**
|
|
91
56
|
* Read the account email (`oauthAccount.emailAddress`) Claude Code records for a
|
|
@@ -169,6 +134,7 @@ export class QuotaPoller {
|
|
|
169
134
|
pollIntervalMs;
|
|
170
135
|
fetchImpl;
|
|
171
136
|
tokenResolver;
|
|
137
|
+
refresher;
|
|
172
138
|
logger;
|
|
173
139
|
interval = null;
|
|
174
140
|
/** Most-recent snapshot per account id. */
|
|
@@ -182,6 +148,8 @@ export class QuotaPoller {
|
|
|
182
148
|
config.fetchImpl ??
|
|
183
149
|
((url, init) => fetch(url, init));
|
|
184
150
|
this.tokenResolver = config.tokenResolver ?? defaultTokenResolver;
|
|
151
|
+
this.refresher =
|
|
152
|
+
config.refresher ?? ((account) => refreshClaudeToken(expandHome(account.configHome)));
|
|
185
153
|
this.logger = config.logger ?? { log: () => { }, warn: () => { } };
|
|
186
154
|
}
|
|
187
155
|
start() {
|
|
@@ -199,18 +167,11 @@ export class QuotaPoller {
|
|
|
199
167
|
}
|
|
200
168
|
}
|
|
201
169
|
/**
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
170
|
+
* One usage read. Returns the parsed body (or null body on a non-auth non-ok),
|
|
171
|
+
* an auth-failed marker (401/403), or null on a network failure. NEVER logs the
|
|
172
|
+
* token.
|
|
205
173
|
*/
|
|
206
|
-
async
|
|
207
|
-
const token = this.tokenResolver(account);
|
|
208
|
-
if (!token) {
|
|
209
|
-
this.logger.warn(`[QuotaPoller] no resolvable token for account ${account.id} — skipping`);
|
|
210
|
-
return null;
|
|
211
|
-
}
|
|
212
|
-
let body = null;
|
|
213
|
-
let authFailed = false;
|
|
174
|
+
async readUsage(token) {
|
|
214
175
|
try {
|
|
215
176
|
const res = await this.fetchImpl(USAGE_URL, {
|
|
216
177
|
headers: {
|
|
@@ -220,26 +181,72 @@ export class QuotaPoller {
|
|
|
220
181
|
},
|
|
221
182
|
});
|
|
222
183
|
if (res.ok) {
|
|
223
|
-
body
|
|
224
|
-
}
|
|
225
|
-
else if (res.status === 401 || res.status === 403) {
|
|
226
|
-
authFailed = true;
|
|
184
|
+
return { authFailed: false, body: (await res.json()) };
|
|
227
185
|
}
|
|
186
|
+
if (res.status === 401 || res.status === 403)
|
|
187
|
+
return { authFailed: true };
|
|
188
|
+
return { authFailed: false, body: null }; // 5xx etc. → no snapshot, not auth
|
|
228
189
|
}
|
|
229
190
|
catch {
|
|
230
191
|
// @silent-fallback-ok: network failure → no snapshot this cycle (retry next)
|
|
231
192
|
return null;
|
|
232
193
|
}
|
|
233
|
-
|
|
234
|
-
|
|
194
|
+
}
|
|
195
|
+
markNeedsReauth(account, reason) {
|
|
196
|
+
try {
|
|
197
|
+
this.pool.update(account.id, { status: 'needs-reauth' });
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
// @silent-fallback-ok: pool update best-effort; status reflects next read
|
|
201
|
+
}
|
|
202
|
+
this.logger.warn(`[QuotaPoller] account ${account.id} → needs-reauth (${reason})`);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Poll one account: resolve token transiently, read usage, map to snapshot.
|
|
206
|
+
* On a usage-read auth failure (401/403) the access token may simply have
|
|
207
|
+
* EXPIRED while the refresh token is still valid — so the poller attempts a
|
|
208
|
+
* refresh-token exchange and ONE retry BEFORE declaring needs-reauth. Only a
|
|
209
|
+
* genuinely dead login (no refresh token / refresh rejected / still 401 after
|
|
210
|
+
* a fresh token) yields needs-reauth. Returns null when the token is
|
|
211
|
+
* unresolvable or the read fails. NEVER logs or returns the token.
|
|
212
|
+
*/
|
|
213
|
+
async pollAccount(account) {
|
|
214
|
+
const token = this.tokenResolver(account);
|
|
215
|
+
if (!token) {
|
|
216
|
+
this.logger.warn(`[QuotaPoller] no resolvable token for account ${account.id} — skipping`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
const read = await this.readUsage(token);
|
|
220
|
+
if (read === null)
|
|
221
|
+
return null; // network failure
|
|
222
|
+
let body;
|
|
223
|
+
if (read.authFailed) {
|
|
224
|
+
const refreshed = await this.refresher(account);
|
|
225
|
+
if (!refreshed.ok) {
|
|
226
|
+
// No refresh token, or the exchange was rejected — genuine re-auth.
|
|
227
|
+
this.markNeedsReauth(account, refreshed.reason);
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const retry = await this.readUsage(refreshed.accessToken);
|
|
231
|
+
if (retry === null)
|
|
232
|
+
return null; // network blip on the retry → next cycle
|
|
233
|
+
if (retry.authFailed) {
|
|
234
|
+
// Fresh token still rejected — treat as genuinely failed.
|
|
235
|
+
this.markNeedsReauth(account, 'usage still auth-failed after refresh');
|
|
236
|
+
return null;
|
|
237
|
+
}
|
|
238
|
+
// Recovered silently — no operator action needed. Record for visibility.
|
|
235
239
|
try {
|
|
236
|
-
this.pool.update(account.id, {
|
|
240
|
+
this.pool.update(account.id, { lastRefreshAt: new Date().toISOString() });
|
|
237
241
|
}
|
|
238
242
|
catch {
|
|
239
|
-
// @silent-fallback-ok:
|
|
243
|
+
// @silent-fallback-ok: visibility-only write
|
|
240
244
|
}
|
|
241
|
-
this.logger.
|
|
242
|
-
|
|
245
|
+
this.logger.log(`[QuotaPoller] account ${account.id} access token refreshed silently (no re-auth needed)`);
|
|
246
|
+
body = retry.body;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
body = read.body;
|
|
243
250
|
}
|
|
244
251
|
if (!body)
|
|
245
252
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"QuotaPoller.js","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"QuotaPoller.js","sourceRoot":"","sources":["../../src/core/QuotaPoller.ts"],"names":[],"mappings":"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"}
|
|
@@ -92,6 +92,13 @@ export interface SubscriptionAccount {
|
|
|
92
92
|
enrolledAt: string;
|
|
93
93
|
/** ISO timestamp the account was last selected for a session. */
|
|
94
94
|
lastUsedAt?: string;
|
|
95
|
+
/**
|
|
96
|
+
* ISO timestamp the poller last silently refreshed this account's access token
|
|
97
|
+
* from its refresh token (P1.2 hardening). Visibility only — lets the dashboard
|
|
98
|
+
* show "token auto-refreshed" so a routine access-token expiry reads as healthy
|
|
99
|
+
* rather than a re-auth event.
|
|
100
|
+
*/
|
|
101
|
+
lastRefreshAt?: string;
|
|
95
102
|
/** Monotonic version for optimistic CAS in update(). */
|
|
96
103
|
version: number;
|
|
97
104
|
}
|
|
@@ -117,6 +124,7 @@ export interface UpdateAccountInput {
|
|
|
117
124
|
status?: SubscriptionAccountStatus;
|
|
118
125
|
lastQuota?: AccountQuotaSnapshot | null;
|
|
119
126
|
lastUsedAt?: string;
|
|
127
|
+
lastRefreshAt?: string;
|
|
120
128
|
email?: string;
|
|
121
129
|
}
|
|
122
130
|
export declare class ValidationError extends Error {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionPool.d.ts","sourceRoot":"","sources":["../../src/core/SubscriptionPool.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"SubscriptionPool.d.ts","sourceRoot":"","sources":["../../src/core/SubscriptionPool.ts"],"names":[],"mappings":"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"}
|
|
@@ -186,6 +186,9 @@ export class SubscriptionPool {
|
|
|
186
186
|
if (patch.lastUsedAt !== undefined) {
|
|
187
187
|
acct.lastUsedAt = patch.lastUsedAt;
|
|
188
188
|
}
|
|
189
|
+
if (patch.lastRefreshAt !== undefined) {
|
|
190
|
+
acct.lastRefreshAt = patch.lastRefreshAt;
|
|
191
|
+
}
|
|
189
192
|
if (patch.email !== undefined) {
|
|
190
193
|
const em = patch.email.trim();
|
|
191
194
|
acct.email = em || undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SubscriptionPool.js","sourceRoot":"","sources":["../../src/core/SubscriptionPool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"SubscriptionPool.js","sourceRoot":"","sources":["../../src/core/SubscriptionPool.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-08T20:59:17.649Z",
|
|
5
|
+
"instarVersion": "1.3.432",
|
|
6
6
|
"entryCount": 199,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Extended the P13 "Stop Reason Is the Work" guard (`CompletionEvaluator.evaluateStopRationale`, wired at `/autonomous/evaluate-stop`) to BLOCK two more autonomous-stop rationales: "I'm blocked / waiting on another agent (or the operator)" and "an idle/polling/waiting loop burns the box / wastes resources." A peer dependency is not a terminal blocker — the agent must keep pursuing it (re-ping, check for a reply on a cadence, find an alternate path, or advance other open work); waiting on a peer is not a CPU burn. Also tightened the operator-only ALLOW exception to "already pursued AND no other work to advance," and fixed a latent parse bug where a bare `STOP_BLOCKED` verdict echoed the token as guidance instead of the rich default steering.
|
|
9
|
+
|
|
10
|
+
## What to Tell Your User
|
|
11
|
+
|
|
12
|
+
Internal autonomy-robustness change — nothing to configure. If you run autonomous sessions, the agent is now structurally prevented from ending a run by claiming it's "blocked on another agent" or that "an idle loop wastes resources" — it keeps pursuing the dependency (and periodically checking for a reply) instead of stopping. This closes a real failure mode where an autonomous run declared itself complete while work remained — and a peer had in fact already replied.
|
|
13
|
+
|
|
14
|
+
## Summary of New Capabilities
|
|
15
|
+
|
|
16
|
+
| Capability | How to use |
|
|
17
|
+
|-----------|-----------|
|
|
18
|
+
| P13 guard blocks "blocked-on-peer" stops | Automatic in autonomous mode — no config |
|
|
19
|
+
| P13 guard blocks "resource-burn" stops | Automatic in autonomous mode — no config |
|
|
20
|
+
|
|
21
|
+
## Evidence
|
|
22
|
+
|
|
23
|
+
`CompletionEvaluator` unit tests 15/15 green — including 2 new cases: the independent judge's prompt now names both new BLOCK rationales, and a bare-verdict stop falls through to pursuit-steering guidance. `tsc --noEmit` clean. The P13 standard doc (`docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md`) gains the 2026-06-08 extension + earned-from note. Tier-1 change; side-effects review at `upgrades/side-effects/p13-stop-rationale-peer-pursuit.md`. Earned from topic 12476 (operator correction: a peer dependency is not a blocker; waiting is not a resource burn).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
The subscription-pool quota poller now auto-refreshes an expired OAuth access token from its stored refresh token before declaring an account `needs-reauth`. Previously any 401/403 from the usage endpoint was treated as a dead login — but a Claude Code access token expires routinely (~8–12h) while its refresh token stays valid for weeks, so the poller was wrongly flagging healthy accounts as needing re-authentication every time the short-lived token lapsed.
|
|
9
|
+
|
|
10
|
+
New `OAuthRefresher` performs the `refresh_token` grant against the public Claude Code OAuth token endpoint and writes the rotated credential back corruption-safely (validated-200 only, read-merge-write preserving all fields, fail-closed on any error). `QuotaPoller` calls it on a 401 and retries once; only a genuinely dead refresh token now yields `needs-reauth`. A new optional `lastRefreshAt` field plus a dashboard "Token auto-refreshed" line make the silent refresh visible.
|
|
11
|
+
|
|
12
|
+
## What to Tell Your User
|
|
13
|
+
|
|
14
|
+
Your accounts no longer ask you to sign in again just because a day went by. A Claude login actually holds two keys — a short-lived one that turns over every several hours, and a long-lived one that lasts weeks. I now renew the short-lived key automatically using the long-lived one, exactly the way the Claude app does, so a routine daily expiry is invisible to you. I'll only ask you to sign in again when the real login is genuinely gone — a password change, a sign-out, or a new login elsewhere. The dashboard also shows a small "token auto-refreshed" note so you can see I'm handling it.
|
|
15
|
+
|
|
16
|
+
## Summary of New Capabilities
|
|
17
|
+
|
|
18
|
+
| Capability | How to Use |
|
|
19
|
+
|-----------|-----------|
|
|
20
|
+
| Auto-refresh of expired access tokens | automatic (subscription-pool quota poller) |
|
|
21
|
+
| Token-health visibility | Subscriptions dashboard tab — "Token auto-refreshed" line |
|
|
22
|
+
|
|
23
|
+
## Evidence
|
|
24
|
+
|
|
25
|
+
Reproduction (live, 2026-06-08, echo's two enrolled max accounts): both showed `status: needs-reauth` and a fresh `POST /subscription-pool/poll` failed 2/2. Inspecting the macOS keychain entries (`Claude Code-credentials-5939f6c8` / `-63465191`) showed `hasRefreshToken: true` with the access token `expiresAt` ~11–12h in the past — i.e. only the short-lived token had lapsed; the login was intact. The old code path mapped that 401 straight to `needs-reauth`.
|
|
26
|
+
|
|
27
|
+
After the fix: the unit/integration/e2e suites reproduce the exact sequence (usage read 401 → refresh-token exchange → retry 200) and assert the account stays `active` with a `lastRefreshAt` stamp rather than flipping to `needs-reauth`; the dead-refresh-token case still flips to `needs-reauth`. All three tiers green (`tests/unit/oauth-refresher.test.ts`, `tests/unit/quota-poller.test.ts`, `tests/integration/subscription-quota-routes.test.ts`, `tests/e2e/subscription-quota-lifecycle.test.ts`), tsc + repo lint clean.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Side-Effects Review — P13 guard blocks "blocked-on-peer" + "resource-burn" stop rationales
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `p13-stop-rationale-peer-pursuit`
|
|
4
|
+
**Date:** `2026-06-08`
|
|
5
|
+
**Author:** `Echo (instar-dev)`
|
|
6
|
+
**Second-pass reviewer:** `not required (Tier 1)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Extends `CompletionEvaluator.evaluateStopRationale` — the P13 "Stop Reason Is the Work" guard already wired into the autonomous stop path at `/autonomous/evaluate-stop` — so the independent judge also BLOCKS two additional stop rationales: (a) "I'm blocked / waiting on another agent (or the operator)" and (b) "an idle/polling/waiting loop burns the box / wastes resources." Both are added to `buildStopRationalePrompt`'s BLOCK list with steering toward active pursuit + periodic re-check; the ALLOW list's operator-only exception is tightened to "already actively pursued AND no other work to advance." Also fixes a latent parse bug in `parseStopRationale`: a bare `STOP_BLOCKED` verdict (no reason line) echoed the verdict token as guidance instead of falling through to the rich default. Files: `src/core/CompletionEvaluator.ts`, `tests/unit/CompletionEvaluator.test.ts`, `docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md` (P13 section).
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- `CompletionEvaluator.evaluateStopRationale` (P13 stop guard, `/autonomous/evaluate-stop`) — **modify** — adds two BLOCK rationales + tightens the operator-only ALLOW; the verdict mechanism (STOP_OK/STOP_BLOCKED, fail-open) is unchanged.
|
|
15
|
+
- `CompletionEvaluator.parseStopRationale` — **modify (bugfix)** — reason now reads the second line only, so a bare verdict reaches the default guidance.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1. Over-block
|
|
20
|
+
|
|
21
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
22
|
+
|
|
23
|
+
The guard governs whether an autonomous *stop attempt* is allowed; "over-block" here = blocking a legitimate stop. Risk: a genuinely operator-only residual (a credential/approval only the user can give) where the agent truly has no other work could now be nudged to "keep pursuing." Mitigated three ways: (1) the ALLOW list still explicitly permits an operator-only residual the agent has already pursued with no other work; (2) duration/emergency stops remain allowed; (3) the guard is SECONDARY and fails OPEN — the primary completion authority still governs, and any ambiguity returns `stopAllowed:true`. Net: it can only *delay* a genuine operator-only stop by re-injecting "did you pursue it + is there other work?" guidance, never trap it.
|
|
24
|
+
|
|
25
|
+
## 2. Under-block
|
|
26
|
+
|
|
27
|
+
**What failure modes does this still miss?**
|
|
28
|
+
|
|
29
|
+
It relies on an LLM judge reading the transcript, so a stop whose blocked-on-peer/resource-burn reasoning is implicit (never stated) can still pass as `STOP_OK`. It does not detect a *silent* stop that emits no rationale text (that surface is the completion evaluator + the stop-hook re-injection, not this guard). It cannot force the agent to actually pursue — it only refuses the stop and re-injects guidance; pursuit is the agent's next-turn behavior.
|
|
30
|
+
|
|
31
|
+
## 3. Level-of-abstraction fit
|
|
32
|
+
|
|
33
|
+
This is an **authority** (a context-rich LLM judgment), correctly so — it extends the existing P13 authority rather than adding a parallel brittle detector. It feeds the existing `/autonomous/evaluate-stop` surface (no new route, no new component). It re-uses the established STOP_OK/STOP_BLOCKED contract and fail-open posture. No lower-level primitive is re-implemented.
|
|
34
|
+
|
|
35
|
+
## 4. Signal vs authority compliance
|
|
36
|
+
|
|
37
|
+
**Does this change hold blocking authority with brittle logic?** No. The blocking authority is the LLM judge (full transcript context), not a keyword filter — consistent with `docs/signal-vs-authority.md`. The only string logic is the verdict parse (STOP_OK/STOP_BLOCKED), unchanged in posture, and it fails OPEN. The new content is *prompt guidance* the judge reasons over, not a regex that blocks.
|
|
38
|
+
|
|
39
|
+
## 5. Interactions
|
|
40
|
+
|
|
41
|
+
- **Shadowing:** runs as the SECONDARY guard after the PRIMARY completion evaluator; it does not shadow it — completion authority governs first. No change to ordering.
|
|
42
|
+
- **Double-fire:** none — single call site (`routes.ts` `/autonomous/evaluate-stop`); no new caller.
|
|
43
|
+
- **Races:** none — pure function over an injected transcript string; no shared mutable state.
|
|
44
|
+
- **Feedback loops:** when it blocks, it re-injects guidance as the next-turn prompt — intended P13 behavior; bounded by the loop's existing duration/emergency stops.
|
|
45
|
+
|
|
46
|
+
## 6. External surfaces
|
|
47
|
+
|
|
48
|
+
No new routes, config, or user-visible output. Behavior change is internal to autonomous-loop stopping. The deployed behavior reaches existing agents on package update (CompletionEvaluator ships in `dist` — no migration needed; it is code, not an installed agent file). The P13 standard doc is updated for the `/spec-converge` reviewer.
|
|
49
|
+
|
|
50
|
+
## 7. Rollback cost
|
|
51
|
+
|
|
52
|
+
- **Hot-fix release:** revert the code change; ship as next patch. Fully reversible.
|
|
53
|
+
- **Data migration:** none — no persistent state.
|
|
54
|
+
- **Agent state repair:** none — no agent files touched; behavior updates with the package.
|
|
55
|
+
- **User visibility:** none — internal autonomous-loop behavior; worst case is the guard allows a stop it previously blocked (or vice-versa), both within the existing fail-open envelope.
|
|
56
|
+
|
|
57
|
+
## Conclusion
|
|
58
|
+
|
|
59
|
+
Tier-1, safety-improving extension of an existing ratified standard (P13) via its existing guard. Adds BLOCK cases only (more conservative — the safe direction), fails open, no new surface, no migration. Tests 15/15 green, `tsc` clean.
|
|
60
|
+
|
|
61
|
+
## Second-pass review (if required)
|
|
62
|
+
|
|
63
|
+
Not required (Tier 1).
|
|
64
|
+
|
|
65
|
+
## Evidence pointers
|
|
66
|
+
|
|
67
|
+
- `tests/unit/CompletionEvaluator.test.ts` — 15/15 pass (2 new cases: judge prompt names the new BLOCK rationales; default guidance steers to pursuit).
|
|
68
|
+
- `docs/INSTAR-DESIGN-PRINCIPLES-AND-LESSONS.md` — P13 "Extension (2026-06-08)" + "Extended from" note.
|
|
69
|
+
- Earned from topic 12476, 2026-06-08 (operator correction: a peer dependency is not a blocker; waiting is not a resource burn).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Side-effects review — subscription-pool OAuth access-token auto-refresh
|
|
2
|
+
|
|
3
|
+
## What changed
|
|
4
|
+
- New `src/core/OAuthRefresher.ts`: a corruption-safe refresh-token→access-token exchange for a config home's Claude Code credential (keychain on macOS, `<configHome>/.credentials.json` elsewhere). Endpoint + client id are the public Claude Code values extracted from the official client binary.
|
|
5
|
+
- `src/core/QuotaPoller.ts`: on a usage-read 401/403 the poller now attempts a refresh + one retry BEFORE marking the account `needs-reauth`. `defaultTokenResolver` refactored to share the OAuthRefresher locator (single source of truth for where a config home's credential lives).
|
|
6
|
+
- `src/core/SubscriptionPool.ts`: new optional `lastRefreshAt` field (visibility only).
|
|
7
|
+
- `dashboard/subscriptions.js`: a "Token auto-refreshed <ago>" line + `relativeAge()` helper.
|
|
8
|
+
|
|
9
|
+
## Blast radius / who is affected
|
|
10
|
+
- Only anthropic/claude-code accounts in a SubscriptionPool. A pool of zero accounts (single-account agents) is entirely unaffected — the poller only runs when accounts are enrolled.
|
|
11
|
+
- Behavior change is strictly a NARROWING of when `needs-reauth` fires: it no longer fires on a recoverable access-token expiry. Genuinely dead logins still flip to `needs-reauth` exactly as before.
|
|
12
|
+
|
|
13
|
+
## Corruption safety (the load-bearing concern)
|
|
14
|
+
- The ONLY mutation is the credential write-back. It is gated three ways: (1) only on a fully-validated 200 (new access token shaped `sk-ant-oat…`, positive numeric `expires_in`); (2) read-merge-write that preserves every existing field (scopes, subscriptionType, rateLimitTier, unknown fields); (3) refresh-token rotation persisted when present, old token kept when the server doesn't rotate — never dropped.
|
|
15
|
+
- Any failure (wrong endpoint, wrong client id, network error, malformed body, write failure) writes NOTHING and returns a typed failure → the caller's existing `needs-reauth` path. A misconfiguration can only ever fail to improve, never corrupt a working login. This is fail-CLOSED, not silent-degrade-to-heuristic (consistent with the no-silent-llm-fallback standard, though this path is auth not inference).
|
|
16
|
+
|
|
17
|
+
## Secrets handling
|
|
18
|
+
- Token values are never logged and never returned to any persisted surface. The pool registry's existing `assertNoCredentialFields` guard still rejects any credential-bearing field name; `lastRefreshAt` is a timestamp, not a secret.
|
|
19
|
+
|
|
20
|
+
## Framework generality
|
|
21
|
+
- Scoped to claude-code OAuth specifically (the refresh-token grant is provider/framework-specific). It does NOT touch the session launch/inject abstraction (frameworkSessionLaunch / MessageDelivery), so it cannot regress codex-cli / gemini-cli / pi-cli. Non-claude-code accounts are skipped by the same provider/framework guard the poller already applies.
|
|
22
|
+
|
|
23
|
+
## Migration parity
|
|
24
|
+
- No agent-installed files change (no settings.json hooks, no config defaults, no CLAUDE.md template, no hook scripts, no skills). This is an in-process monitoring component; existing agents pick it up on the next server update. The new `lastRefreshAt` field is optional and back-compatible with existing `subscription-pool.json` on disk.
|
|
25
|
+
|
|
26
|
+
## Tests
|
|
27
|
+
- Tier 1: `tests/unit/oauth-refresher.test.ts` (rotation, no-rotation, no-refresh-token, exchange-failed, malformed, read-failed, write-failed, locator, request shape) + extended `tests/unit/quota-poller.test.ts` (refresh-recovers, still-401-after-refresh, dead-refresh-token) + `tests/unit/subscriptions-render.test.ts` (token-health line + relativeAge).
|
|
28
|
+
- Tier 2: `tests/integration/subscription-quota-routes.test.ts` — recovery + dead-login through the real `/subscription-pool/poll` HTTP route.
|
|
29
|
+
- Tier 3: `tests/e2e/subscription-quota-lifecycle.test.ts` — expired token auto-refreshes end-to-end, account stays active, stamped to the on-disk registry.
|