claude-code-infinite 1.0.21 → 1.0.22
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/README.md +74 -24
- package/dist/claude-env.d.ts +14 -1
- package/dist/claude-env.d.ts.map +1 -1
- package/dist/claude-env.js +17 -1
- package/dist/claude-env.js.map +1 -1
- package/dist/cli-lifecycle.d.ts +4 -0
- package/dist/cli-lifecycle.d.ts.map +1 -1
- package/dist/cli-lifecycle.js +18 -0
- package/dist/cli-lifecycle.js.map +1 -1
- package/dist/cli-main.js +118 -18
- package/dist/cli-main.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/count-tokens.d.ts.map +1 -1
- package/dist/count-tokens.js +7 -1
- package/dist/count-tokens.js.map +1 -1
- package/dist/diagnostics-schema.d.ts +44 -0
- package/dist/diagnostics-schema.d.ts.map +1 -0
- package/dist/diagnostics-schema.js +162 -0
- package/dist/diagnostics-schema.js.map +1 -0
- package/dist/diagnostics.d.ts +116 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +316 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/first-tree.d.ts +11 -0
- package/dist/first-tree.d.ts.map +1 -0
- package/dist/first-tree.js +45 -0
- package/dist/first-tree.js.map +1 -0
- package/dist/hooks.d.ts +37 -0
- package/dist/hooks.d.ts.map +1 -1
- package/dist/hooks.js +69 -9
- package/dist/hooks.js.map +1 -1
- package/dist/memtree-links.d.ts +2 -0
- package/dist/memtree-links.d.ts.map +1 -1
- package/dist/memtree-links.js +4 -0
- package/dist/memtree-links.js.map +1 -1
- package/dist/memtree.d.ts +7 -0
- package/dist/memtree.d.ts.map +1 -1
- package/dist/memtree.js +8 -0
- package/dist/memtree.js.map +1 -1
- package/dist/prefix-store.js +3 -0
- package/dist/prefix-store.js.map +1 -1
- package/dist/proxy.d.ts +28 -2
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +63 -7
- package/dist/proxy.js.map +1 -1
- package/dist/reqlog.d.ts +17 -1
- package/dist/reqlog.d.ts.map +1 -1
- package/dist/reqlog.js.map +1 -1
- package/dist/resume-hint.d.ts +25 -0
- package/dist/resume-hint.d.ts.map +1 -0
- package/dist/resume-hint.js +36 -0
- package/dist/resume-hint.js.map +1 -0
- package/package.json +3 -3
package/dist/proxy.js
CHANGED
|
@@ -59,6 +59,7 @@ import { hyperlink } from "./payment-gate.js";
|
|
|
59
59
|
import { PaymentNoticeThrottle, paymentNoticeText } from "./payment-notice.js";
|
|
60
60
|
import { MEMTREE_LINKS_MAX_SESSIONS } from "./memtree-links.js";
|
|
61
61
|
import { describeClaudeCodeRequest, inspectMonitorTranscript, knownSideRequestKind, transcriptWarning, isClaudeCodeSideRequest, memtreeClientMeta, sessionTag, } from "./cc-request.js";
|
|
62
|
+
import { isResumableSessionId } from "./resume-hint.js";
|
|
62
63
|
import { conservativeBodyTokens } from "./token-estimate.js";
|
|
63
64
|
import { approxTokensFromBytes, mergeUsageFromJsonBody, mergeUsageFromSseEvent, } from "./reqlog.js";
|
|
64
65
|
const DEFAULT_UPSTREAM = "https://api.anthropic.com";
|
|
@@ -92,10 +93,23 @@ const SKIP_RESPONSE_HEADERS = new Set([
|
|
|
92
93
|
]);
|
|
93
94
|
/**
|
|
94
95
|
* Budget fallback until the server reports `model_budget_tokens`: this share
|
|
95
|
-
* of the model's context window
|
|
96
|
-
*
|
|
96
|
+
* of the model's context window. 0.8 by default; NEAR_WINDOW_FALLBACK_RATIO
|
|
97
|
+
* (900k of 1M) for the 1M-window models the server budgets at 900k for ccc
|
|
98
|
+
* (NEAR_WINDOW_MODEL_RE). Plain Claude Code 2.1.302 auto-compacts a 1M window
|
|
99
|
+
* at 967k, so MemTree compresses no earlier than ~70k before it would; ccc
|
|
100
|
+
* lifts Claude Code's own 977k refusal (claude-env.ts) so a tool turn that
|
|
101
|
+
* jumps past the budget reaches the proxy and is compressed here.
|
|
97
102
|
*/
|
|
98
103
|
export const FALLBACK_BUDGET_WINDOW_RATIO = 0.8;
|
|
104
|
+
export const NEAR_WINDOW_FALLBACK_RATIO = 0.9;
|
|
105
|
+
/** Opus 5.5, Opus 5, Opus 4.8 and Fable 5 / 5.1 (polychat LARGE_CONTEXT_MODELS). */
|
|
106
|
+
const NEAR_WINDOW_MODEL_RE = /claude-(?:opus-(?:4-8|5-5|5)|fable-5(?:-1)?)(?![0-9])/i;
|
|
107
|
+
const ONE_MILLION_TOKENS = 1_000_000;
|
|
108
|
+
/** The fallback share of the window for `model` (see FALLBACK_BUDGET_WINDOW_RATIO). */
|
|
109
|
+
export function fallbackBudgetRatio(model, modelContextLimit) {
|
|
110
|
+
return typeof model === "string" && modelContextLimit >= ONE_MILLION_TOKENS &&
|
|
111
|
+
NEAR_WINDOW_MODEL_RE.test(model) ? NEAR_WINDOW_FALLBACK_RATIO : FALLBACK_BUDGET_WINDOW_RATIO;
|
|
112
|
+
}
|
|
99
113
|
/**
|
|
100
114
|
* An explicit target is capped at this share of the budget: a compaction to
|
|
101
115
|
* the budget itself would be over budget again on the next turn and
|
|
@@ -269,6 +283,7 @@ export function startProxy(opts) {
|
|
|
269
283
|
unpaidDetail: () => opts.memtree.paymentRequiredDetail,
|
|
270
284
|
usageUrl: opts.memtree.usageDashboardUrl,
|
|
271
285
|
sessionStartNotice: opts.sessionStartNotice,
|
|
286
|
+
diagnostics: opts.diagnostics,
|
|
272
287
|
notices: new NoticeDeliveryQueue(),
|
|
273
288
|
mainNoticePending: false,
|
|
274
289
|
mainPromptGeneration: 0,
|
|
@@ -305,6 +320,7 @@ export function startProxy(opts) {
|
|
|
305
320
|
laneSizes: new Map(),
|
|
306
321
|
};
|
|
307
322
|
installMemtreeLink(state, state.memtreeLinkPlacement);
|
|
323
|
+
state.notices.setFirstTreeIntro(opts.firstTreeIntro ?? null);
|
|
308
324
|
const server = http.createServer((req, res) => {
|
|
309
325
|
const accepted = { req, res };
|
|
310
326
|
acceptedRequests.add(accepted);
|
|
@@ -387,6 +403,7 @@ export function startProxy(opts) {
|
|
|
387
403
|
void beginClose();
|
|
388
404
|
},
|
|
389
405
|
drain,
|
|
406
|
+
mainSessionId: () => state.resumableMainSession,
|
|
390
407
|
});
|
|
391
408
|
});
|
|
392
409
|
});
|
|
@@ -617,6 +634,30 @@ export function memtreePageId(pageUrl) {
|
|
|
617
634
|
return undefined;
|
|
618
635
|
}
|
|
619
636
|
}
|
|
637
|
+
const SESSION_START_SOURCES = new Set(["startup", "resume", "clear", "compact"]);
|
|
638
|
+
/** A main-agent SessionStart, for diagnostics; never fails the hook. */
|
|
639
|
+
function recordSessionStart(state, source) {
|
|
640
|
+
try {
|
|
641
|
+
const known = source !== undefined && SESSION_START_SOURCES.has(source);
|
|
642
|
+
state.diagnostics?.record("session_start", { source: known ? source : "other" });
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
// Diagnostics are best effort.
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* A resumed or forked session can be resumed again at once; a fresh or
|
|
650
|
+
* cleared one has nothing to resume until its first turn. Compaction keeps
|
|
651
|
+
* the same session.
|
|
652
|
+
*/
|
|
653
|
+
function noteMainSessionStart(state, source, sessionId) {
|
|
654
|
+
if (source === "resume" || source === "fork") {
|
|
655
|
+
state.resumableMainSession = isResumableSessionId(sessionId) ? sessionId : undefined;
|
|
656
|
+
}
|
|
657
|
+
else if (source === "startup" || source === "clear") {
|
|
658
|
+
state.resumableMainSession = undefined;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
620
661
|
/** Serve only validated Claude hook POSTs on the randomized localhost path. */
|
|
621
662
|
async function handleNoticeHook(req, res, state, reqlog,
|
|
622
663
|
/** A main-lane turn ended: the session may be over (its final index). */
|
|
@@ -655,6 +696,10 @@ onMainStop) {
|
|
|
655
696
|
return;
|
|
656
697
|
}
|
|
657
698
|
if (parsed.hook_event_name === "SessionStart") {
|
|
699
|
+
if (parsed.agent_id === undefined) {
|
|
700
|
+
recordSessionStart(state, parsed.source);
|
|
701
|
+
noteMainSessionStart(state, parsed.source, parsed.session_id);
|
|
702
|
+
}
|
|
658
703
|
// A resume waits briefly for the newest tree; a fresh session has none to wait for.
|
|
659
704
|
if (parsed.agent_id === undefined && parsed.source === "resume") {
|
|
660
705
|
await state.newestTrees?.settle(parsed.session_id);
|
|
@@ -1055,6 +1100,8 @@ async function handleMessages(req, res, opts, upstream, state) {
|
|
|
1055
1100
|
// reply the session ends with (scheduleFinalIndex, on Stop).
|
|
1056
1101
|
const mainSessionId = isMainRequest ? requestSessionId(req) : undefined;
|
|
1057
1102
|
if (mainSessionId !== undefined) {
|
|
1103
|
+
if (isResumableSessionId(mainSessionId))
|
|
1104
|
+
state.resumableMainSession = mainSessionId;
|
|
1058
1105
|
opts.memtree.noteMainConversation(mainSessionId, {
|
|
1059
1106
|
messages: msgsForMemtree,
|
|
1060
1107
|
modelContextLimit,
|
|
@@ -1648,7 +1695,10 @@ async function handleMessages(req, res, opts, upstream, state) {
|
|
|
1648
1695
|
const paymentDetail = opts.memtree.paymentRequiredDetail;
|
|
1649
1696
|
const mayQueueNotice = displayForThisTurn && state.mainPromptGeneration === noticePromptGeneration;
|
|
1650
1697
|
if (mayQueueNotice && paymentDetail !== null && state.paymentNotice.due()) {
|
|
1651
|
-
state.notices.queueSuffix(paymentNoticeText(paymentDetail, recapLink(state, sessionId)?.link), () =>
|
|
1698
|
+
state.notices.queueSuffix(paymentNoticeText(paymentDetail, recapLink(state, sessionId)?.link), () => {
|
|
1699
|
+
state.paymentNotice.markShown();
|
|
1700
|
+
state.diagnostics?.record("paused_notice", { trigger: "turn" });
|
|
1701
|
+
}, noticePromptId);
|
|
1652
1702
|
}
|
|
1653
1703
|
else if (mayQueueNotice && paymentDetail === null) {
|
|
1654
1704
|
state.notices.queueSuffix(DEGRADED_NOTICE, undefined, noticePromptId);
|
|
@@ -1882,6 +1932,7 @@ function resumePaymentNotice(state, input) {
|
|
|
1882
1932
|
if (detail === null)
|
|
1883
1933
|
return undefined;
|
|
1884
1934
|
state.paymentNotice.markShown();
|
|
1935
|
+
state.diagnostics?.record("paused_notice", { trigger: "resume" });
|
|
1885
1936
|
return state.notices.warningText(paymentNoticeText(detail));
|
|
1886
1937
|
}
|
|
1887
1938
|
/** The caller's extra SessionStart line; a throwing provider shows nothing. */
|
|
@@ -2399,7 +2450,7 @@ function serverBudgetKey(model, modelContextLimit) {
|
|
|
2399
2450
|
/**
|
|
2400
2451
|
* The session's whole-request budget: CCC_BUDGET_TOKENS when set (tests),
|
|
2401
2452
|
* else the model budget the server last reported for this model and window,
|
|
2402
|
-
* else the window times
|
|
2453
|
+
* else the window times fallbackBudgetRatio (servers that predate
|
|
2403
2454
|
* `model_budget_tokens`, and the first request before any response).
|
|
2404
2455
|
*/
|
|
2405
2456
|
function resolveBudget(opts, state, model, modelContextLimit) {
|
|
@@ -2410,7 +2461,7 @@ function resolveBudget(opts, state, model, modelContextLimit) {
|
|
|
2410
2461
|
if (reported !== undefined)
|
|
2411
2462
|
return { tokens: reported, source: "server" };
|
|
2412
2463
|
return {
|
|
2413
|
-
tokens: Math.floor(modelContextLimit *
|
|
2464
|
+
tokens: Math.floor(modelContextLimit * fallbackBudgetRatio(model, modelContextLimit)),
|
|
2414
2465
|
source: "window-ratio",
|
|
2415
2466
|
};
|
|
2416
2467
|
}
|
|
@@ -3863,12 +3914,17 @@ function refuseWholeRequest(args, inputTokens, budgetTokens) {
|
|
|
3863
3914
|
const failure = args.state.fallbackFailures.fail(args.routeKey, { awaitingIndex: args.rec.indexBehind === true });
|
|
3864
3915
|
args.rec.forwardedBytes = 0;
|
|
3865
3916
|
args.rec.approxInputTokens = 0;
|
|
3866
|
-
|
|
3917
|
+
args.state.diagnostics?.record("refusal", {
|
|
3918
|
+
status: failure.status, attempt: failure.attempt,
|
|
3919
|
+
input_ktokens: Math.round(inputTokens / 1000), budget_ktokens: Math.round(budgetTokens / 1000),
|
|
3920
|
+
});
|
|
3921
|
+
// To requests.jsonl, never the console: Claude Code's TUI shows stderr inline.
|
|
3922
|
+
args.rec.refusal = {
|
|
3867
3923
|
lane: args.routeKey, attempt: failure.attempt, status: failure.status,
|
|
3868
3924
|
inputTokens, outputTokens: args.body.max_tokens ?? 0,
|
|
3869
3925
|
outputReservation: bodyOutputReservation(args.body), indexBehind: args.rec.indexBehind === true,
|
|
3870
3926
|
budgetTokens, modelContextLimit: args.modelContextLimit,
|
|
3871
|
-
}
|
|
3927
|
+
};
|
|
3872
3928
|
if (args.res.headersSent || args.res.destroyed)
|
|
3873
3929
|
return false;
|
|
3874
3930
|
args.res.writeHead(failure.status, { "content-type": "application/json", ...failure.headers });
|