claude-code-infinite 1.0.26 → 1.0.28

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 (79) hide show
  1. package/README.md +44 -16
  2. package/dist/claude-auth.d.ts +9 -1
  3. package/dist/claude-auth.d.ts.map +1 -1
  4. package/dist/claude-auth.js +26 -4
  5. package/dist/claude-auth.js.map +1 -1
  6. package/dist/claude-env.d.ts +11 -4
  7. package/dist/claude-env.d.ts.map +1 -1
  8. package/dist/claude-env.js +11 -4
  9. package/dist/claude-env.js.map +1 -1
  10. package/dist/claude-locate.d.ts +18 -0
  11. package/dist/claude-locate.d.ts.map +1 -1
  12. package/dist/claude-locate.js +82 -3
  13. package/dist/claude-locate.js.map +1 -1
  14. package/dist/claude-plan-warning.d.ts +55 -0
  15. package/dist/claude-plan-warning.d.ts.map +1 -0
  16. package/dist/claude-plan-warning.js +95 -0
  17. package/dist/claude-plan-warning.js.map +1 -0
  18. package/dist/cli-args.d.ts +5 -0
  19. package/dist/cli-args.d.ts.map +1 -1
  20. package/dist/cli-args.js +9 -0
  21. package/dist/cli-args.js.map +1 -1
  22. package/dist/cli-lifecycle.d.ts +5 -0
  23. package/dist/cli-lifecycle.d.ts.map +1 -1
  24. package/dist/cli-lifecycle.js +13 -0
  25. package/dist/cli-lifecycle.js.map +1 -1
  26. package/dist/cli-main.js +170 -87
  27. package/dist/cli-main.js.map +1 -1
  28. package/dist/diagnostics-schema.d.ts +2 -0
  29. package/dist/diagnostics-schema.d.ts.map +1 -1
  30. package/dist/diagnostics-schema.js +13 -2
  31. package/dist/diagnostics-schema.js.map +1 -1
  32. package/dist/diagnostics.d.ts +8 -3
  33. package/dist/diagnostics.d.ts.map +1 -1
  34. package/dist/diagnostics.js +12 -3
  35. package/dist/diagnostics.js.map +1 -1
  36. package/dist/hooks.d.ts +8 -0
  37. package/dist/hooks.d.ts.map +1 -1
  38. package/dist/hooks.js +57 -25
  39. package/dist/hooks.js.map +1 -1
  40. package/dist/key-page.d.ts +17 -0
  41. package/dist/key-page.d.ts.map +1 -0
  42. package/dist/key-page.js +32 -0
  43. package/dist/key-page.js.map +1 -0
  44. package/dist/key-status.d.ts +79 -0
  45. package/dist/key-status.d.ts.map +1 -0
  46. package/dist/key-status.js +143 -0
  47. package/dist/key-status.js.map +1 -0
  48. package/dist/memtree.d.ts +23 -0
  49. package/dist/memtree.d.ts.map +1 -1
  50. package/dist/memtree.js +39 -3
  51. package/dist/memtree.js.map +1 -1
  52. package/dist/notices.d.ts +5 -0
  53. package/dist/notices.d.ts.map +1 -1
  54. package/dist/notices.js +13 -0
  55. package/dist/notices.js.map +1 -1
  56. package/dist/paused-session.d.ts +29 -0
  57. package/dist/paused-session.d.ts.map +1 -0
  58. package/dist/paused-session.js +148 -0
  59. package/dist/paused-session.js.map +1 -0
  60. package/dist/payment-gate.d.ts +44 -13
  61. package/dist/payment-gate.d.ts.map +1 -1
  62. package/dist/payment-gate.js +82 -23
  63. package/dist/payment-gate.js.map +1 -1
  64. package/dist/payment-link.d.ts +68 -0
  65. package/dist/payment-link.d.ts.map +1 -0
  66. package/dist/payment-link.js +123 -0
  67. package/dist/payment-link.js.map +1 -0
  68. package/dist/project-trees.d.ts +100 -0
  69. package/dist/project-trees.d.ts.map +1 -0
  70. package/dist/project-trees.js +196 -0
  71. package/dist/project-trees.js.map +1 -0
  72. package/dist/proxy.d.ts +13 -0
  73. package/dist/proxy.d.ts.map +1 -1
  74. package/dist/proxy.js +70 -8
  75. package/dist/proxy.js.map +1 -1
  76. package/dist/reqlog.d.ts +2 -0
  77. package/dist/reqlog.d.ts.map +1 -1
  78. package/dist/reqlog.js.map +1 -1
  79. package/package.json +1 -1
package/dist/proxy.js CHANGED
@@ -56,6 +56,7 @@ import { COMPRESSED_NOTICE, compressedTotalsText, DEGRADED_NOTICE, NOT_COMPRESSE
56
56
  import { NoticeDeliveryQueue, MEMTREE_COMPACT_COMMAND, MEMTREE_HELP_COMMAND, TRAILER_LABEL, LINK_LABEL, linkLines, urlLine, terminalSupportsHyperlinks, isMemtreeViewCommand, sessionCommandArgs, parseNoticeHookInput, } from "./hooks.js";
57
57
  import { USAGE_LABEL, USAGE_SIGN_IN_NOTE } from "./usage-link.js";
58
58
  import { hyperlink } from "./payment-gate.js";
59
+ import { keyInvalidNoticeText, keyInvalidRefusalMessage } from "./key-status.js";
59
60
  import { PaymentNoticeThrottle, pausedCompactionText, pausedRefusalMessage, paymentNoticeText, subscribeUrlFromDetail } from "./payment-notice.js";
60
61
  import { MEMTREE_LINKS_MAX_SESSIONS } from "./memtree-links.js";
61
62
  import { describeClaudeCodeRequest, inspectMonitorTranscript, knownSideRequestKind, transcriptWarning, isClaudeCodeSideRequest, memtreeClientMeta, sessionTag, } from "./cc-request.js";
@@ -304,11 +305,15 @@ export function startProxy(opts) {
304
305
  const shutdownAbort = new AbortController();
305
306
  const state = {
306
307
  paymentNotice: new PaymentNoticeThrottle(opts.paymentNoticeIntervalMs),
308
+ keyNotice: new PaymentNoticeThrottle(opts.paymentNoticeIntervalMs),
309
+ keyInvalid: () => opts.memtree.keyInvalid,
310
+ keyPageUrl: opts.memtree.keyPageUrl,
307
311
  pausedNearWindowWarned: new Set(),
308
312
  unpaidDetail: () => opts.memtree.paymentRequiredDetail,
309
313
  pausedDetail: () => opts.memtree.pausedDetail,
310
314
  usageUrl: opts.memtree.usageDashboardUrl,
311
315
  sessionStartNotice: opts.sessionStartNotice,
316
+ projectTrees: opts.projectTrees,
312
317
  diagnostics: opts.diagnostics,
313
318
  notices: new NoticeDeliveryQueue(),
314
319
  mainNoticePending: false,
@@ -726,17 +731,20 @@ onMainStop) {
726
731
  recordSessionStart(state, parsed.source);
727
732
  noteMainSessionStart(state, parsed.source, parsed.session_id);
728
733
  }
734
+ // Earlier trees in this project: asked now, so its wait overlaps the resume's.
735
+ const contextPromise = projectTreesContext(state.projectTrees, parsed);
729
736
  // A resume waits briefly for the newest tree; a fresh session has none to wait for.
730
737
  if (parsed.agent_id === undefined && parsed.source === "resume") {
731
738
  await state.newestTrees?.settle(parsed.session_id);
732
739
  }
733
740
  const line = joinNoticeLines(resumeLinkLine(state, parsed), resumePaymentNotice(state, parsed), extraSessionStartLine(state.sessionStartNotice, parsed));
734
- if (!line) {
741
+ const context = await contextPromise;
742
+ if (!line && !context) {
735
743
  res.writeHead(204);
736
744
  res.end();
737
745
  return;
738
746
  }
739
- const body = Buffer.from(JSON.stringify({ systemMessage: line }), "utf-8");
747
+ const body = Buffer.from(JSON.stringify(sessionStartOutput(line, context)), "utf-8");
740
748
  res.writeHead(200, {
741
749
  "content-type": "application/json",
742
750
  "content-length": String(body.length),
@@ -1697,6 +1705,13 @@ async function handleMessages(req, res, opts, upstream, state) {
1697
1705
  // it pending for the next turn.
1698
1706
  if (result && sessionId !== undefined)
1699
1707
  state.compactNow.delete(sessionId);
1708
+ // A first 402 starts fetching the short checkout link (memtree.ts): give the
1709
+ // paused notice below a moment to show it instead of the long one. Only a turn
1710
+ // that can show the notice waits, and only while that fetch is in flight.
1711
+ if (!result && displayForThisTurn && state.mainPromptGeneration === noticePromptGeneration &&
1712
+ state.paymentNotice.due() && opts.memtree.paymentRequiredDetail !== null) {
1713
+ await opts.memtree.paymentLinkSettled?.(PAYMENT_LINK_NOTICE_WAIT_MS);
1714
+ }
1700
1715
  if (downstreamClosedDuringCompression ||
1701
1716
  (res.destroyed && !res.writableFinished)) {
1702
1717
  releaseUncommittedRouteDecision();
@@ -1724,7 +1739,15 @@ async function handleMessages(req, res, opts, upstream, state) {
1724
1739
  const paymentDetail = opts.memtree.paymentRequiredDetail;
1725
1740
  const mayQueueNotice = displayForThisTurn && state.mainPromptGeneration === noticePromptGeneration;
1726
1741
  const overBudget = reachedBudget(rec.compaction);
1727
- if (mayQueueNotice && paymentDetail !== null &&
1742
+ if (state.keyInvalid()) {
1743
+ // A refused key (401/403) says why MemTree is off and how to fix it;
1744
+ // throttled like the payment notice, and quiet in between.
1745
+ if (mayQueueNotice && state.keyNotice.due()) {
1746
+ // A refusal of this very request says the same and withdraws it.
1747
+ pausedNotices.set(rec, state.notices.queueSuffix(keyInvalidNoticeText(state.keyPageUrl), () => state.keyNotice.markShown(), noticePromptId));
1748
+ }
1749
+ }
1750
+ else if (mayQueueNotice && paymentDetail !== null &&
1728
1751
  (state.paymentNotice.due() || (overBudget && !nearWindowWarned(state, sessionId)))) {
1729
1752
  // A paused refusal of this very request withdraws it (refuseWholeRequest),
1730
1753
  // and any later one withdraws its near-window line.
@@ -1984,11 +2007,39 @@ function extraSessionStartLine(provider, input) {
1984
2007
  return undefined;
1985
2008
  }
1986
2009
  }
2010
+ /**
2011
+ * Earlier sessions in this project (project-trees.ts) for a main session's
2012
+ * start: fresh, resumed, cleared or forked, never a compaction. Never throws.
2013
+ */
2014
+ async function projectTreesContext(hint, input) {
2015
+ if (!hint || input.agent_id !== undefined || input.source === "compact")
2016
+ return undefined;
2017
+ try {
2018
+ return await hint.contextFor(input.session_id);
2019
+ }
2020
+ catch {
2021
+ return undefined;
2022
+ }
2023
+ }
2024
+ /**
2025
+ * The SessionStart hook's answer: the banner line for the user
2026
+ * (`systemMessage`), model context (`additionalContext`), or both.
2027
+ */
2028
+ export function sessionStartOutput(line, context) {
2029
+ return {
2030
+ ...(line ? { systemMessage: line } : {}),
2031
+ ...(context
2032
+ ? { hookSpecificOutput: { hookEventName: "SessionStart", additionalContext: context } }
2033
+ : {}),
2034
+ };
2035
+ }
1987
2036
  function joinNoticeLines(...parts) {
1988
2037
  const present = parts.filter((part) => !!part);
1989
2038
  return present.length ? present.join("\n") : undefined;
1990
2039
  }
1991
2040
  export const MEMTREE_COMPACT_MIN_TOKENS = 20_000;
2041
+ /** How long a paused turn waits for the short checkout link before its notice (once). */
2042
+ export const PAYMENT_LINK_NOTICE_WAIT_MS = 1_000;
1992
2043
  /** The reply to a ccc slash command, or undefined for an ordinary prompt. */
1993
2044
  function sessionCommandReply(state, sessionId, prompt) {
1994
2045
  const unpaid = state.unpaidDetail();
@@ -3554,7 +3605,9 @@ async function recoverToolRouteMiss(args) {
3554
3605
  // a later turn. An unpaid key (402, possibly set by this very compress)
3555
3606
  // or a shutting-down proxy gets no retry.
3556
3607
  if (!state.shutdownSignal.aborted &&
3557
- opts.memtree.paymentRequiredDetail === null) {
3608
+ opts.memtree.paymentRequiredDetail === null &&
3609
+ !opts.memtree.keyInvalid // a refused key refuses the index too
3610
+ ) {
3558
3611
  opts.memtree.indexInBackground(hash, msgsForMemtree, modelContextLimit, requestSessionId(req), args.clientMeta, transcriptTimesFor(opts, requestSessionId(req), agentAttributionId(req)));
3559
3612
  }
3560
3613
  return forwardOriginal();
@@ -3962,9 +4015,12 @@ async function wholeRequestRefused(sizing, candidate, limits) {
3962
4015
  }
3963
4016
  /** Write the bounded fallback refusal (route-fallback.ts); always false. */
3964
4017
  function refuseWholeRequest(args, inputTokens, budgetTokens) {
3965
- const unpaid = args.state.pausedDetail();
3966
- const failure = unpaid !== null ? pausedRefusal(pausedRefusalMessage(unpaid))
3967
- : args.state.fallbackFailures.fail(args.routeKey, { awaitingIndex: args.rec.indexBehind === true });
4018
+ // A refused key or a paused one: a retry cannot fix either, so it is final at once.
4019
+ const keyInvalid = args.state.keyInvalid();
4020
+ const unpaid = keyInvalid ? null : args.state.pausedDetail();
4021
+ const failure = keyInvalid ? pausedRefusal(keyInvalidRefusalMessage(args.state.keyPageUrl))
4022
+ : unpaid !== null ? pausedRefusal(pausedRefusalMessage(unpaid))
4023
+ : args.state.fallbackFailures.fail(args.routeKey, { awaitingIndex: args.rec.indexBehind === true });
3968
4024
  args.rec.forwardedBytes = 0;
3969
4025
  args.rec.approxInputTokens = 0;
3970
4026
  args.state.diagnostics?.record("refusal", {
@@ -3977,8 +4033,9 @@ function refuseWholeRequest(args, inputTokens, budgetTokens) {
3977
4033
  inputTokens, outputTokens: args.body.max_tokens ?? 0,
3978
4034
  outputReservation: bodyOutputReservation(args.body), indexBehind: args.rec.indexBehind === true,
3979
4035
  budgetTokens, modelContextLimit: args.modelContextLimit, ...(unpaid !== null ? { paused: true } : {}),
4036
+ ...(keyInvalid ? { keyInvalid: true } : {}),
3980
4037
  };
3981
- if (unpaid !== null)
4038
+ if (unpaid !== null || keyInvalid)
3982
4039
  cancelPausedNotice(args.state, args.rec);
3983
4040
  if (args.res.headersSent || args.res.destroyed)
3984
4041
  return false;
@@ -3987,6 +4044,9 @@ function refuseWholeRequest(args, inputTokens, budgetTokens) {
3987
4044
  if (unpaid !== null) {
3988
4045
  notePausedRefusal(args.state, args.rec, args.routeKey, requestSessionId(args.req));
3989
4046
  }
4047
+ // The refusal named the key: it counts as the key notice on the user's screen.
4048
+ if (keyInvalid && args.rec.routeLane === "main")
4049
+ args.state.keyNotice.markShown();
3990
4050
  return false;
3991
4051
  }
3992
4052
  /**
@@ -4025,6 +4085,8 @@ function cancelPausedNotice(state, rec) {
4025
4085
  * already queued the paused notice carries the line itself.
4026
4086
  */
4027
4087
  function warnPausedNearWindow(state, rec, sessionId) {
4088
+ if (state.keyInvalid())
4089
+ return; // a refused key, not a pause: the key notice says why
4028
4090
  const detail = state.unpaidDetail();
4029
4091
  // Paid again: a later pause (next month's limit) warns afresh.
4030
4092
  if (detail === null)