agent-dealer 1.2.1 → 1.2.3

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 (57) hide show
  1. package/bundle/server/dist/adapters/linear-inbox.js +9 -5
  2. package/bundle/server/dist/capacity/adapter.js +2 -0
  3. package/bundle/server/dist/capacity/claude-events.js +45 -10
  4. package/bundle/server/dist/capacity/claude-events.test.js +7 -0
  5. package/bundle/server/dist/capacity/claude-local-cache.js +788 -0
  6. package/bundle/server/dist/capacity/claude-local-cache.test.js +520 -0
  7. package/bundle/server/dist/capacity/codex-app-server.js +100 -10
  8. package/bundle/server/dist/capacity/codex-app-server.test.js +195 -2
  9. package/bundle/server/dist/capacity/cursor-individual-credentials.js +425 -0
  10. package/bundle/server/dist/capacity/cursor-individual-credentials.test.js +369 -0
  11. package/bundle/server/dist/capacity/cursor-individual.js +1037 -0
  12. package/bundle/server/dist/capacity/cursor-individual.test.js +1081 -0
  13. package/bundle/server/dist/capacity/muse-host.js +748 -0
  14. package/bundle/server/dist/capacity/muse-host.test.js +480 -0
  15. package/bundle/server/dist/capacity/muse-lifecycle.test.js +97 -0
  16. package/bundle/server/dist/capacity/muse.js +389 -91
  17. package/bundle/server/dist/capacity/muse.test.js +100 -61
  18. package/bundle/server/dist/capacity/service.js +1 -0
  19. package/bundle/server/dist/coordinator/muse-spawn.js +143 -1
  20. package/bundle/server/dist/db/index.js +30 -0
  21. package/bundle/server/dist/db/schema.sql +27 -0
  22. package/bundle/server/dist/index.js +17 -2
  23. package/bundle/server/dist/repository/cursor-individual-billing.js +84 -0
  24. package/bundle/server/dist/repository/repository-mappings.js +46 -0
  25. package/bundle/server/dist/repository/repository-mappings.test.js +83 -0
  26. package/bundle/server/dist/repository/runtime-capacity.js +8 -3
  27. package/bundle/server/dist/repository/runtime-capacity.test.js +4 -0
  28. package/bundle/server/dist/routes/cursor-individual-billing.test.js +80 -0
  29. package/bundle/server/dist/routes/index.js +58 -9
  30. package/bundle/server/dist/routes/intake-linear.test.js +55 -1
  31. package/bundle/server/dist/routes/repository-mappings.js +22 -0
  32. package/bundle/server/dist/routes/repository-mappings.test.js +110 -0
  33. package/bundle/server/dist/routes/runtime-capacity.test.js +84 -8
  34. package/bundle/server/dist/runners/muse-code-jsonl.js +7 -1
  35. package/bundle/server/dist/runners/muse-serve-session.js +313 -0
  36. package/bundle/server/dist/runners/muse-serve-session.test.js +325 -0
  37. package/bundle/server/package.json +2 -2
  38. package/bundle/server/static-ui/assets/codex-YZSpL-SB.png +0 -0
  39. package/bundle/server/static-ui/assets/index-CYqRh_-S.css +1 -0
  40. package/bundle/server/static-ui/assets/index-UO4lHZw4.js +60 -0
  41. package/bundle/server/static-ui/assets/muse-CtdcC6ve.svg +33 -0
  42. package/bundle/server/static-ui/index.html +2 -2
  43. package/bundle/shared/dist/index.d.ts +6 -6
  44. package/bundle/shared/dist/linear-intake.d.ts +71 -0
  45. package/bundle/shared/dist/linear-intake.js +90 -0
  46. package/bundle/shared/dist/linear-intake.test.js +80 -1
  47. package/bundle/shared/dist/runtime-capacity.d.ts +89 -0
  48. package/bundle/shared/dist/runtime-capacity.js +60 -0
  49. package/bundle/shared/dist/runtime-capacity.test.js +1 -0
  50. package/bundle/shared/package.json +1 -1
  51. package/dist/doctor.d.ts +57 -0
  52. package/dist/doctor.js +211 -0
  53. package/dist/doctor.test.d.ts +1 -0
  54. package/dist/doctor.test.js +246 -0
  55. package/package.json +1 -1
  56. package/bundle/server/static-ui/assets/index-ChnI0F02.css +0 -1
  57. package/bundle/server/static-ui/assets/index-CyB1573Q.js +0 -60
@@ -0,0 +1,33 @@
1
+ <svg viewBox="0 0 15.184 10.109" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
2
+ <!-- Exact Meta infinity-mark paths extracted from Meta's first-party wordmark SVG. -->
3
+ <path d="M4.40165 0.0546875C4.39678 0.0546875 4.39194 0.0547047 4.38708 0.0547298L4.36719 1.69809C4.37174 1.69804 4.37629 1.698 4.38085 1.698H4.38086C5.46253 1.698 6.30154 2.55086 8.12513 5.62254L8.2363 5.80954L8.24357 5.82178L9.2644 4.29012L9.25733 4.27829C9.01709 3.88756 8.78629 3.52806 8.56494 3.1998C8.30824 2.81945 8.06274 2.47967 7.8245 2.17631C6.61813 0.640297 5.60899 0.0546875 4.40165 0.0546875Z" fill="url(#meta-0)"/>
4
+ <path d="M4.38698 0.0547298C3.17378 0.0609714 2.10123 0.845474 1.32675 2.04593C1.32448 2.04944 1.32221 2.05296 1.31995 2.05649L2.74159 2.83027C2.7439 2.82677 2.74621 2.82327 2.74853 2.81978C3.20078 2.13886 3.76359 1.70456 4.36709 1.69809C4.37164 1.69804 4.37619 1.698 4.38075 1.698L4.40155 0.0546875C4.39668 0.0546875 4.39184 0.0547047 4.38698 0.0547298Z" fill="url(#meta-1)"/>
5
+ <path d="M1.32666 2.04688C1.3244 2.05039 1.32213 2.05391 1.31986 2.05744C0.810944 2.84965 0.431562 3.8218 0.226532 4.87056C0.225644 4.8751 0.224759 4.87964 0.223877 4.88419L1.82103 5.26104C1.82187 5.25649 1.82271 5.25193 1.82355 5.24738C1.99427 4.32569 2.31929 3.47086 2.7415 2.83122C2.7438 2.82772 2.74612 2.82422 2.74844 2.82073L1.32666 2.04688Z" fill="url(#meta-2)"/>
6
+ <path d="M1.82364 5.24791L0.226615 4.87109C0.225727 4.87563 0.224842 4.88018 0.22396 4.88472C0.11216 5.4615 0.0552821 6.04757 0.0540887 6.63508C0.0540793 6.63983 0.0540757 6.64458 0.0540777 6.64932L1.69203 6.79586C1.6919 6.79109 1.69176 6.78638 1.69165 6.7816C1.69084 6.74752 1.69043 6.71299 1.69042 6.67799C1.69124 6.20287 1.73498 5.72881 1.82112 5.26157C1.82195 5.25702 1.82279 5.25247 1.82364 5.24791Z" fill="url(#meta-3)"/>
7
+ <path d="M1.74184 7.30462C1.71246 7.13577 1.69581 6.96494 1.69203 6.79359C1.6919 6.78883 1.69176 6.78412 1.69165 6.77933L0.0540887 6.63281C0.0540793 6.63756 0.0540757 6.64231 0.0540777 6.64706V6.64798C0.0540777 7.01363 0.0845699 7.35598 0.143831 7.67225C0.144688 7.67682 0.145531 7.68131 0.1464 7.68588L1.74432 7.3183C1.74348 7.31376 1.74266 7.30919 1.74184 7.30462Z" fill="url(#meta-4)"/>
8
+ <path d="M2.11498 8.1544C1.93675 7.95985 1.81061 7.67922 1.74429 7.32032C1.74345 7.31578 1.74264 7.31121 1.74182 7.30664L0.143799 7.67426C0.144655 7.67884 0.145498 7.68333 0.146368 7.6879C0.267152 8.32205 0.504031 8.85057 0.843261 9.2504C0.846277 9.25395 0.849304 9.2575 0.852342 9.26105L2.12451 8.16464C2.12133 8.16125 2.11813 8.15783 2.11498 8.1544Z" fill="url(#meta-5)"/>
9
+ <path d="M6.84924 3.60742C5.88628 5.08446 5.30292 6.01088 5.30292 6.01088C4.02016 8.02169 3.57639 8.47237 2.86221 8.47237C2.5642 8.47237 2.31524 8.36626 2.12451 8.16343C2.12133 8.16005 2.11814 8.15662 2.11499 8.15319L0.843262 9.24919C0.846278 9.25274 0.849305 9.25629 0.852343 9.25984C1.32074 9.80583 1.98189 10.1088 2.79981 10.1088C4.03726 10.1088 4.92725 9.52537 6.5094 6.75973C6.5094 6.75973 7.16894 5.59504 7.62266 4.79276C7.34161 4.33898 7.08584 3.94614 6.84924 3.60742Z" fill="#0082FB"/>
10
+ <path d="M8.56569 1.27734C8.56244 1.28079 8.55913 1.28431 8.55589 1.28777C8.30301 1.55719 8.06032 1.85807 7.82422 2.1773C8.06247 2.48067 8.30842 2.82111 8.56513 3.20145C8.86769 2.73447 9.15011 2.35626 9.42691 2.06637C9.4302 2.06294 9.43341 2.0596 9.43669 2.05618L8.56569 1.27734Z" fill="url(#meta-6)"/>
11
+ <path d="M13.2408 1.13125C12.5692 0.452783 11.7685 0.0546875 10.9125 0.0546875C10.0098 0.0546875 9.25054 0.549336 8.56571 1.27604C8.56246 1.27948 8.55915 1.283 8.55591 1.28646L9.42694 2.06506C9.43022 2.06163 9.43343 2.05829 9.43672 2.05488C9.88786 1.58561 10.3245 1.35131 10.8085 1.35131H10.8084C11.3294 1.35131 11.8171 1.59654 12.2394 2.02645C12.2427 2.02982 12.246 2.03316 12.2493 2.03655L13.2508 1.14135C13.2475 1.13796 13.2441 1.13462 13.2408 1.13125Z" fill="#0082FB"/>
12
+ <path d="M15.1822 6.41693C15.1446 4.23851 14.3823 2.29103 13.2508 1.14096C13.2474 1.13757 13.2441 1.13423 13.2407 1.13086L12.2394 2.02606C12.2427 2.02943 12.246 2.03277 12.2493 2.03616C13.1005 2.91055 13.6844 4.53694 13.7375 6.41646C13.7376 6.42119 13.7377 6.42592 13.7379 6.43065L15.1824 6.43111C15.1824 6.42639 15.1823 6.42166 15.1822 6.41693Z" fill="url(#meta-7)"/>
13
+ <path d="M15.1824 6.43263C15.1824 6.4279 15.1823 6.42317 15.1822 6.41843L13.7375 6.41797C13.7376 6.4227 13.7377 6.42743 13.7379 6.43216C13.7402 6.52038 13.7414 6.60913 13.7414 6.69841C13.7414 7.21086 13.6649 7.6251 13.5092 7.92417C13.5069 7.92861 13.5045 7.93306 13.5022 7.93744L14.5793 9.05771C14.582 9.05364 14.5846 9.0496 14.5873 9.04551C14.9783 8.44213 15.1836 7.60392 15.1836 6.58747C15.1836 6.53573 15.1832 6.48411 15.1824 6.43263Z" fill="url(#meta-8)"/>
14
+ <path d="M13.5093 7.92188C13.507 7.92631 13.5046 7.93076 13.5023 7.93515C13.3676 8.18724 13.1753 8.35538 12.924 8.4289L13.415 9.97644C13.48 9.95436 13.5433 9.92957 13.6049 9.90207C13.6233 9.89388 13.6416 9.88545 13.6597 9.87678C13.6701 9.87178 13.6805 9.86671 13.6908 9.86157C14.0155 9.69974 14.2899 9.46106 14.5118 9.15354C14.5254 9.13476 14.5388 9.11582 14.5519 9.09654C14.5612 9.08295 14.5704 9.06925 14.5794 9.05541C14.5821 9.05135 14.5847 9.0473 14.5874 9.04322L13.5093 7.92188Z" fill="url(#meta-9)"/>
15
+ <path d="M12.6111 8.47085C12.4465 8.47085 12.3012 8.44634 12.159 8.38281L11.6562 9.96718C11.9388 10.0637 12.2403 10.1072 12.5765 10.1072C12.8862 10.1072 13.1705 10.0609 13.4283 9.97153L12.9374 8.42452C12.8316 8.45623 12.7216 8.47184 12.6111 8.47085Z" fill="url(#meta-10)"/>
16
+ <path d="M11.6051 7.93071C11.602 7.92714 11.5989 7.92355 11.5957 7.91992L10.4388 9.12313C10.4421 9.12659 10.4453 9.13004 10.4485 9.13348C10.8505 9.56188 11.2344 9.82762 11.6697 9.97347L12.1721 8.39027C11.9887 8.31148 11.8112 8.16864 11.6051 7.93071Z" fill="url(#meta-11)"/>
17
+ <path d="M11.5958 7.91873C11.2488 7.51518 10.8195 6.84328 10.1443 5.75677L9.26431 4.28917L9.25724 4.27734L8.23621 5.80859L8.24348 5.82083L8.86699 6.86972C9.47138 7.88111 9.96376 8.61279 10.4389 9.12193C10.4422 9.1254 10.4454 9.12885 10.4486 9.13229L11.6051 7.92952C11.602 7.92596 11.5989 7.92236 11.5958 7.91873Z" fill="url(#meta-12)"/>
18
+ <defs>
19
+ <linearGradient id="meta-0" x1="8.58586" y1="5.1989" x2="5.19669" y2="0.757905" gradientUnits="userSpaceOnUse"><stop offset="0.0006" stop-color="#0867DF"/><stop offset="0.4539" stop-color="#0668E1"/><stop offset="0.8591" stop-color="#0064E0"/></linearGradient>
20
+ <linearGradient id="meta-1" x1="1.98772" y1="2.32206" x2="4.31121" y2="0.558086" gradientUnits="userSpaceOnUse"><stop offset="0.1323" stop-color="#0064DF"/><stop offset="0.9988" stop-color="#0064E0"/></linearGradient>
21
+ <linearGradient id="meta-2" x1="1.0028" y1="4.91157" x2="1.93484" y2="2.56535" gradientUnits="userSpaceOnUse"><stop offset="0.0147" stop-color="#0072EC"/><stop offset="0.6881" stop-color="#0064DF"/></linearGradient>
22
+ <linearGradient id="meta-3" x1="0.876315" y1="6.60704" x2="0.984158" y2="5.17414" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007CF6"/><stop offset="0.9943" stop-color="#0072EC"/></linearGradient>
23
+ <linearGradient id="meta-4" x1="0.935623" y1="7.38311" x2="0.858481" y2="6.80902" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"/><stop offset="1" stop-color="#007CF6"/></linearGradient>
24
+ <linearGradient id="meta-5" x1="0.89031" y1="7.53593" x2="1.37106" y2="8.55559" gradientUnits="userSpaceOnUse"><stop offset="0.0731" stop-color="#007FF9"/><stop offset="1" stop-color="#0082FB"/></linearGradient>
25
+ <linearGradient id="meta-6" x1="8.27941" y1="2.60226" x2="8.91497" y2="1.72324" gradientUnits="userSpaceOnUse"><stop offset="0.2799" stop-color="#007FF8"/><stop offset="0.9141" stop-color="#0082FB"/></linearGradient>
26
+ <linearGradient id="meta-7" x1="13.1116" y1="1.46132" x2="14.4412" y2="6.35237" gradientUnits="userSpaceOnUse"><stop stop-color="#0082FB"/><stop offset="0.9995" stop-color="#0081FA"/></linearGradient>
27
+ <linearGradient id="meta-8" x1="14.7623" y1="6.541" x2="13.9208" y2="8.24143" gradientUnits="userSpaceOnUse"><stop offset="0.0619" stop-color="#0081FA"/><stop offset="1" stop-color="#0080F9"/></linearGradient>
28
+ <linearGradient id="meta-9" x1="13.2521" y1="9.14064" x2="14.0387" y2="8.6049" gradientUnits="userSpaceOnUse"><stop stop-color="#027AF3"/><stop offset="1" stop-color="#0080F9"/></linearGradient>
29
+ <linearGradient id="meta-10" x1="12.0183" y1="9.24502" x2="13.1113" y2="9.24502" gradientUnits="userSpaceOnUse"><stop stop-color="#0377EF"/><stop offset="0.9994" stop-color="#0279F1"/></linearGradient>
30
+ <linearGradient id="meta-11" x1="11.068" y1="8.64479" x2="11.8538" y2="9.1071" gradientUnits="userSpaceOnUse"><stop offset="0.0019" stop-color="#0471E9"/><stop offset="1" stop-color="#0377EF"/></linearGradient>
31
+ <linearGradient id="meta-12" x1="8.67106" y1="5.23467" x2="11.1883" y2="8.3996" gradientUnits="userSpaceOnUse"><stop offset="0.2765" stop-color="#0867DF"/><stop offset="1" stop-color="#0471E9"/></linearGradient>
32
+ </defs>
33
+ </svg>
@@ -8,8 +8,8 @@
8
8
  <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
9
9
  <meta name="theme-color" content="#0a0e14" />
10
10
  <link rel="preload" href="/fonts/Monaco.ttf" as="font" type="font/ttf" crossorigin />
11
- <script type="module" crossorigin src="/assets/index-CyB1573Q.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-ChnI0F02.css">
11
+ <script type="module" crossorigin src="/assets/index-UO4lHZw4.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-CYqRh_-S.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -213,6 +213,7 @@ export declare const UsageLineItem: z.ZodObject<{
213
213
  maxTurns: z.ZodOptional<z.ZodNumber>;
214
214
  maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
215
215
  }, "strip", z.ZodTypeAny, {
216
+ label: string;
216
217
  usage: {
217
218
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
218
219
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -227,10 +228,10 @@ export declare const UsageLineItem: z.ZodObject<{
227
228
  cacheWriteTokens?: number | undefined;
228
229
  numTurns?: number | undefined;
229
230
  };
230
- label: string;
231
231
  maxTurns?: number | undefined;
232
232
  maxBudgetUsd?: number | undefined;
233
233
  }, {
234
+ label: string;
234
235
  usage: {
235
236
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
236
237
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -245,7 +246,6 @@ export declare const UsageLineItem: z.ZodObject<{
245
246
  cacheWriteTokens?: number | undefined;
246
247
  numTurns?: number | undefined;
247
248
  };
248
- label: string;
249
249
  maxTurns?: number | undefined;
250
250
  maxBudgetUsd?: number | undefined;
251
251
  }>;
@@ -299,6 +299,7 @@ export declare const UsageSummary: z.ZodObject<{
299
299
  maxTurns: z.ZodOptional<z.ZodNumber>;
300
300
  maxBudgetUsd: z.ZodOptional<z.ZodNumber>;
301
301
  }, "strip", z.ZodTypeAny, {
302
+ label: string;
302
303
  usage: {
303
304
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
304
305
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -313,10 +314,10 @@ export declare const UsageSummary: z.ZodObject<{
313
314
  cacheWriteTokens?: number | undefined;
314
315
  numTurns?: number | undefined;
315
316
  };
316
- label: string;
317
317
  maxTurns?: number | undefined;
318
318
  maxBudgetUsd?: number | undefined;
319
319
  }, {
320
+ label: string;
320
321
  usage: {
321
322
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
322
323
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -331,7 +332,6 @@ export declare const UsageSummary: z.ZodObject<{
331
332
  cacheWriteTokens?: number | undefined;
332
333
  numTurns?: number | undefined;
333
334
  };
334
- label: string;
335
335
  maxTurns?: number | undefined;
336
336
  maxBudgetUsd?: number | undefined;
337
337
  }>, "many">;
@@ -363,6 +363,7 @@ export declare const UsageSummary: z.ZodObject<{
363
363
  numTurns: number;
364
364
  };
365
365
  lines: {
366
+ label: string;
366
367
  usage: {
367
368
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
368
369
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -377,7 +378,6 @@ export declare const UsageSummary: z.ZodObject<{
377
378
  cacheWriteTokens?: number | undefined;
378
379
  numTurns?: number | undefined;
379
380
  };
380
- label: string;
381
381
  maxTurns?: number | undefined;
382
382
  maxBudgetUsd?: number | undefined;
383
383
  }[];
@@ -390,6 +390,7 @@ export declare const UsageSummary: z.ZodObject<{
390
390
  numTurns: number;
391
391
  };
392
392
  lines: {
393
+ label: string;
393
394
  usage: {
394
395
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
395
396
  phase: "plan" | "execute" | "reflect" | "qa";
@@ -404,7 +405,6 @@ export declare const UsageSummary: z.ZodObject<{
404
405
  cacheWriteTokens?: number | undefined;
405
406
  numTurns?: number | undefined;
406
407
  };
407
- label: string;
408
408
  maxTurns?: number | undefined;
409
409
  maxBudgetUsd?: number | undefined;
410
410
  }[];
@@ -42,6 +42,77 @@ export declare const LinearRepoResolution: z.ZodObject<{
42
42
  export type LinearRepoResolution = z.infer<typeof LinearRepoResolution>;
43
43
  /** Raw labels that claim to name a repository — prefix match is case-insensitive. */
44
44
  export declare function extractRepoLabels(labels: readonly string[] | undefined): string[];
45
+ /**
46
+ * NOT-260: configurable Linear label → repository mappings.
47
+ *
48
+ * A normalized Linear label (trimmed, 1–100 chars) is a unique key pointing
49
+ * at exactly one normalized `github.com/<owner>/<repo>` identity. Different
50
+ * labels may point at the same repository.
51
+ */
52
+ export declare const MAX_REPOSITORY_MAPPINGS = 100;
53
+ export declare const LinearRepositoryMapping: z.ZodObject<{
54
+ /** Normalized on write: trimmed, 1–100 characters. */
55
+ label: z.ZodString;
56
+ /** Normalized on write with `parseGitHubRepoInput` to `github.com/<owner>/<repo>`. */
57
+ repository: z.ZodString;
58
+ }, "strip", z.ZodTypeAny, {
59
+ repository: string;
60
+ label: string;
61
+ }, {
62
+ repository: string;
63
+ label: string;
64
+ }>;
65
+ export type LinearRepositoryMapping = z.infer<typeof LinearRepositoryMapping>;
66
+ export declare const LinearRepositoryMappingsInput: z.ZodObject<{
67
+ mappings: z.ZodArray<z.ZodObject<{
68
+ /** Normalized on write: trimmed, 1–100 characters. */
69
+ label: z.ZodString;
70
+ /** Normalized on write with `parseGitHubRepoInput` to `github.com/<owner>/<repo>`. */
71
+ repository: z.ZodString;
72
+ }, "strip", z.ZodTypeAny, {
73
+ repository: string;
74
+ label: string;
75
+ }, {
76
+ repository: string;
77
+ label: string;
78
+ }>, "many">;
79
+ }, "strip", z.ZodTypeAny, {
80
+ mappings: {
81
+ repository: string;
82
+ label: string;
83
+ }[];
84
+ }, {
85
+ mappings: {
86
+ repository: string;
87
+ label: string;
88
+ }[];
89
+ }>;
90
+ export type LinearRepositoryMappingsInput = z.infer<typeof LinearRepositoryMappingsInput>;
91
+ /** Trim + lowercase identity used for duplicate detection and matching. */
92
+ export declare function normalizeMappingLabel(label: string): string;
93
+ /**
94
+ * Validate and normalize a raw mappings payload. Returns normalized
95
+ * `{ label, repository }` rows (label trimmed, repository canonicalized).
96
+ * Throws a readable Error on empty labels, overlong labels, invalid
97
+ * repositories, duplicate normalized labels, or more than 100 rows —
98
+ * callers persist only on success so failures stay atomic.
99
+ */
100
+ export declare function normalizeRepositoryMappings(input: {
101
+ mappings: Array<{
102
+ label: string;
103
+ repository: string;
104
+ }>;
105
+ }): LinearRepositoryMapping[];
106
+ /**
107
+ * NOT-260 mapped resolution over a candidate's raw labels:
108
+ * 1. Match configured mappings by trimmed, case-insensitive label equality.
109
+ * 2. No match: fall back to the existing `repo:` resolver.
110
+ * 3. One distinct mapped repository: `resolved` with the matched Linear label
111
+ * as `sourceLabel`.
112
+ * 4. Multiple matched labels targeting different repositories: `conflict`.
113
+ * 5. Multiple matched labels targeting the same repository resolve.
114
+ */
115
+ export declare function resolveLinearRepoWithMappings(labels: readonly string[] | undefined, mappings: readonly LinearRepositoryMapping[] | undefined): LinearRepoResolution;
45
116
  /**
46
117
  * Deterministic resolution over a candidate's raw labels. Repository parsing
47
118
  * reuses the shared `parseGitHubRepoInput` contract, so a label-derived
@@ -38,6 +38,96 @@ export function extractRepoLabels(labels) {
38
38
  return [];
39
39
  return labels.filter((l) => l.toLowerCase().startsWith(REPO_LABEL_PREFIX));
40
40
  }
41
+ /**
42
+ * NOT-260: configurable Linear label → repository mappings.
43
+ *
44
+ * A normalized Linear label (trimmed, 1–100 chars) is a unique key pointing
45
+ * at exactly one normalized `github.com/<owner>/<repo>` identity. Different
46
+ * labels may point at the same repository.
47
+ */
48
+ export const MAX_REPOSITORY_MAPPINGS = 100;
49
+ export const LinearRepositoryMapping = z.object({
50
+ /** Normalized on write: trimmed, 1–100 characters. */
51
+ label: z.string(),
52
+ /** Normalized on write with `parseGitHubRepoInput` to `github.com/<owner>/<repo>`. */
53
+ repository: z.string(),
54
+ });
55
+ export const LinearRepositoryMappingsInput = z.object({
56
+ mappings: z.array(LinearRepositoryMapping).max(MAX_REPOSITORY_MAPPINGS + 1),
57
+ });
58
+ /** Trim + lowercase identity used for duplicate detection and matching. */
59
+ export function normalizeMappingLabel(label) {
60
+ return label.trim().toLowerCase();
61
+ }
62
+ /**
63
+ * Validate and normalize a raw mappings payload. Returns normalized
64
+ * `{ label, repository }` rows (label trimmed, repository canonicalized).
65
+ * Throws a readable Error on empty labels, overlong labels, invalid
66
+ * repositories, duplicate normalized labels, or more than 100 rows —
67
+ * callers persist only on success so failures stay atomic.
68
+ */
69
+ export function normalizeRepositoryMappings(input) {
70
+ const rows = input.mappings ?? [];
71
+ if (rows.length > MAX_REPOSITORY_MAPPINGS) {
72
+ throw new Error(`At most ${MAX_REPOSITORY_MAPPINGS} repository mappings are allowed`);
73
+ }
74
+ const seen = new Set();
75
+ const out = [];
76
+ for (const row of rows) {
77
+ const label = (row.label ?? "").trim();
78
+ if (!label)
79
+ throw new Error("Mapping label must not be empty");
80
+ if (label.length > 100)
81
+ throw new Error(`Mapping label must be 1–100 characters (got "${label}")`);
82
+ const key = label.toLowerCase();
83
+ if (seen.has(key))
84
+ throw new Error(`Duplicate repository mapping for label "${label}"`);
85
+ seen.add(key);
86
+ let repository;
87
+ try {
88
+ repository = parseGitHubRepoInput(row.repository ?? "").identity;
89
+ }
90
+ catch (err) {
91
+ throw new Error(`Invalid repository for label "${label}": ${err.message}`);
92
+ }
93
+ out.push({ label, repository });
94
+ }
95
+ return out;
96
+ }
97
+ /**
98
+ * NOT-260 mapped resolution over a candidate's raw labels:
99
+ * 1. Match configured mappings by trimmed, case-insensitive label equality.
100
+ * 2. No match: fall back to the existing `repo:` resolver.
101
+ * 3. One distinct mapped repository: `resolved` with the matched Linear label
102
+ * as `sourceLabel`.
103
+ * 4. Multiple matched labels targeting different repositories: `conflict`.
104
+ * 5. Multiple matched labels targeting the same repository resolve.
105
+ */
106
+ export function resolveLinearRepoWithMappings(labels, mappings) {
107
+ const saved = mappings ?? [];
108
+ if (saved.length > 0 && labels && labels.length > 0) {
109
+ const byKey = new Map();
110
+ for (const m of saved)
111
+ byKey.set(normalizeMappingLabel(m.label), m.repository);
112
+ const matchedRepos = [];
113
+ const matchedLabels = [];
114
+ for (const raw of labels) {
115
+ const repo = byKey.get(raw.trim().toLowerCase());
116
+ if (repo !== undefined) {
117
+ matchedRepos.push(repo);
118
+ matchedLabels.push(raw);
119
+ }
120
+ }
121
+ if (matchedLabels.length > 0) {
122
+ const distinct = [...new Set(matchedRepos)];
123
+ if (distinct.length === 1) {
124
+ return { status: "resolved", repository: distinct[0], sourceLabel: matchedLabels[0], labels: matchedLabels };
125
+ }
126
+ return { status: "conflict", labels: matchedLabels };
127
+ }
128
+ }
129
+ return resolveLinearRepoLabels(labels);
130
+ }
41
131
  /**
42
132
  * Deterministic resolution over a candidate's raw labels. Repository parsing
43
133
  * reuses the shared `parseGitHubRepoInput` contract, so a label-derived
@@ -2,7 +2,7 @@
2
2
  import { test } from "node:test";
3
3
  import assert from "node:assert/strict";
4
4
  import { LinearCandidate } from "./index.js";
5
- import { LinearRepoResolution, extractRepoLabels, resolveLinearRepoLabels, } from "./linear-intake.js";
5
+ import { LinearRepoResolution, extractRepoLabels, normalizeMappingLabel, normalizeRepositoryMappings, resolveLinearRepoLabels, resolveLinearRepoWithMappings, } from "./linear-intake.js";
6
6
  test("one valid repo: label resolves to the canonical identity", () => {
7
7
  const r = resolveLinearRepoLabels(["agent-dealer", "repo:github.com/not-so-fat/agent-dealer"]);
8
8
  assert.equal(r.status, "resolved");
@@ -77,6 +77,85 @@ test("LinearCandidate keeps raw labels and accepts the resolved hint", () => {
77
77
  });
78
78
  assert.equal(bare.repoResolution, undefined);
79
79
  });
80
+ test("mapping labels are unique after trim + lowercase normalization", () => {
81
+ assert.equal(normalizeMappingLabel(" Agent-Dealer "), "agent-dealer");
82
+ assert.throws(() => normalizeRepositoryMappings({
83
+ mappings: [
84
+ { label: "agent-dealer", repository: "not-so-fat/agent-dealer" },
85
+ { label: " AGENT-DEALER ", repository: "not-so-fat/other" },
86
+ ],
87
+ }), /Duplicate repository mapping/);
88
+ // Different labels may point at the same repository.
89
+ const same = normalizeRepositoryMappings({
90
+ mappings: [
91
+ { label: "agent-dealer", repository: "not-so-fat/agent-dealer" },
92
+ { label: "dealer", repository: "not-so-fat/agent-dealer" },
93
+ ],
94
+ });
95
+ assert.equal(same.length, 2);
96
+ });
97
+ test("mapping normalization trims labels and canonicalizes repositories", () => {
98
+ const [row] = normalizeRepositoryMappings({
99
+ mappings: [{ label: " agent-dealer ", repository: "not-so-fat/agent-dealer" }],
100
+ });
101
+ assert.equal(row.label, "agent-dealer");
102
+ assert.equal(row.repository, "github.com/not-so-fat/agent-dealer");
103
+ // Full URL and .git forms land on the same identity.
104
+ const [url] = normalizeRepositoryMappings({
105
+ mappings: [{ label: "x", repository: "https://github.com/not-so-fat/agent-dealer.git" }],
106
+ });
107
+ assert.equal(url.repository, "github.com/not-so-fat/agent-dealer");
108
+ });
109
+ test("mapping validation rejects empty labels, bad repos, and 101 rows", () => {
110
+ assert.throws(() => normalizeRepositoryMappings({ mappings: [{ label: " ", repository: "a/b" }] }), /must not be empty/);
111
+ assert.throws(() => normalizeRepositoryMappings({ mappings: [{ label: "x".repeat(101), repository: "a/b" }] }), /1–100/);
112
+ assert.throws(() => normalizeRepositoryMappings({ mappings: [{ label: "ok", repository: "not a repo!!" }] }), /Invalid repository/);
113
+ const many = Array.from({ length: 101 }, (_, i) => ({ label: `l${i}`, repository: "a/b" }));
114
+ assert.throws(() => normalizeRepositoryMappings({ mappings: many }), /At most 100/);
115
+ // Exactly 100 rows pass.
116
+ const hundred = Array.from({ length: 100 }, (_, i) => ({ label: `l${i}`, repository: "a/b" }));
117
+ assert.equal(normalizeRepositoryMappings({ mappings: hundred }).length, 100);
118
+ });
119
+ test("a mapped label resolves with the matched Linear label as sourceLabel", () => {
120
+ const r = resolveLinearRepoWithMappings(["backend", "Agent-Dealer"], [
121
+ { label: "agent-dealer", repository: "github.com/not-so-fat/agent-dealer" },
122
+ ]);
123
+ assert.equal(r.status, "resolved");
124
+ assert.equal(r.repository, "github.com/not-so-fat/agent-dealer");
125
+ assert.equal(r.sourceLabel, "Agent-Dealer");
126
+ });
127
+ test("multiple matched labels on the same repository resolve", () => {
128
+ const r = resolveLinearRepoWithMappings(["agent-dealer", "dealer"], [
129
+ { label: "agent-dealer", repository: "github.com/not-so-fat/agent-dealer" },
130
+ { label: "dealer", repository: "github.com/not-so-fat/agent-dealer" },
131
+ ]);
132
+ assert.equal(r.status, "resolved");
133
+ assert.equal(r.repository, "github.com/not-so-fat/agent-dealer");
134
+ });
135
+ test("multiple matched labels on different repositories conflict — never first", () => {
136
+ const r = resolveLinearRepoWithMappings(["agent-dealer", "dealer"], [
137
+ { label: "agent-dealer", repository: "github.com/not-so-fat/agent-dealer" },
138
+ { label: "dealer", repository: "github.com/not-so-fat/other" },
139
+ ]);
140
+ assert.equal(r.status, "conflict");
141
+ assert.equal(r.repository, undefined);
142
+ assert.deepEqual(r.labels, ["agent-dealer", "dealer"]);
143
+ });
144
+ test("no configured match falls back to the legacy repo: resolver", () => {
145
+ const mappings = [{ label: "agent-dealer", repository: "github.com/not-so-fat/agent-dealer" }];
146
+ const legacy = resolveLinearRepoWithMappings(["repo:github.com/a/one"], mappings);
147
+ assert.equal(legacy.status, "resolved");
148
+ assert.equal(legacy.repository, "github.com/a/one");
149
+ assert.equal(legacy.sourceLabel, "repo:github.com/a/one");
150
+ const none = resolveLinearRepoWithMappings(["backend"], mappings);
151
+ assert.equal(none.status, "unresolved");
152
+ const bare = resolveLinearRepoWithMappings(["backend"], []);
153
+ assert.equal(bare.status, "unresolved");
154
+ // Mapping wins over a repo: label when both are present.
155
+ const both = resolveLinearRepoWithMappings(["agent-dealer", "repo:github.com/a/one"], mappings);
156
+ assert.equal(both.status, "resolved");
157
+ assert.equal(both.repository, "github.com/not-so-fat/agent-dealer");
158
+ });
80
159
  test("LinearRepoResolution schema round-trips every state", () => {
81
160
  for (const status of ["resolved", "unresolved", "conflict", "invalid"]) {
82
161
  const parsed = LinearRepoResolution.parse({ status });
@@ -14,6 +14,17 @@ export type CapacitySource = z.infer<typeof CapacitySource>;
14
14
  */
15
15
  export declare const CapacityUnavailableReason: z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>;
16
16
  export type CapacityUnavailableReason = z.infer<typeof CapacityUnavailableReason>;
17
+ /**
18
+ * Which account-wide critical window this is, set once by the adapter that
19
+ * knows the provider's own identity for it (e.g. Codex's `rateLimits`
20
+ * aggregate, or whichever detailed limit pair it collapsed into) — never
21
+ * re-derived downstream from a window key, label, or duration, none of
22
+ * which can disambiguate multiple same-duration buckets a provider reports.
23
+ * Null means "not the account-wide pair" (a non-critical extra bucket, or a
24
+ * provider/window this contract has no opinion on).
25
+ */
26
+ export declare const CapacityCriticalRole: z.ZodEnum<["five_hour", "weekly"]>;
27
+ export type CapacityCriticalRole = z.infer<typeof CapacityCriticalRole>;
17
28
  export declare const CapacityWindowSnapshot: z.ZodObject<{
18
29
  /** Stable per-runtime key for this window (e.g. `weekly_all_models`). */
19
30
  windowKey: z.ZodString;
@@ -38,6 +49,7 @@ export declare const CapacityWindowSnapshot: z.ZodObject<{
38
49
  expiresAt: z.ZodNullable<z.ZodString>;
39
50
  source: z.ZodEnum<["supported_protocol", "observed_event", "experimental_api", "unavailable"]>;
40
51
  unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
52
+ criticalRole: z.ZodNullable<z.ZodEnum<["five_hour", "weekly"]>>;
41
53
  }, "strip", z.ZodTypeAny, {
42
54
  observedAt: string;
43
55
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -52,6 +64,7 @@ export declare const CapacityWindowSnapshot: z.ZodObject<{
52
64
  freshUntil: string | null;
53
65
  expiresAt: string | null;
54
66
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
67
+ criticalRole: "five_hour" | "weekly" | null;
55
68
  }, {
56
69
  observedAt: string;
57
70
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -66,6 +79,7 @@ export declare const CapacityWindowSnapshot: z.ZodObject<{
66
79
  freshUntil: string | null;
67
80
  expiresAt: string | null;
68
81
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
82
+ criticalRole: "five_hour" | "weekly" | null;
69
83
  }>;
70
84
  export type CapacityWindowSnapshot = z.infer<typeof CapacityWindowSnapshot>;
71
85
  export declare const RuntimeCapacityEntry: z.ZodObject<{
@@ -95,6 +109,7 @@ export declare const RuntimeCapacityEntry: z.ZodObject<{
95
109
  expiresAt: z.ZodNullable<z.ZodString>;
96
110
  source: z.ZodEnum<["supported_protocol", "observed_event", "experimental_api", "unavailable"]>;
97
111
  unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
112
+ criticalRole: z.ZodNullable<z.ZodEnum<["five_hour", "weekly"]>>;
98
113
  }, "strip", z.ZodTypeAny, {
99
114
  observedAt: string;
100
115
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -109,6 +124,7 @@ export declare const RuntimeCapacityEntry: z.ZodObject<{
109
124
  freshUntil: string | null;
110
125
  expiresAt: string | null;
111
126
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
127
+ criticalRole: "five_hour" | "weekly" | null;
112
128
  }, {
113
129
  observedAt: string;
114
130
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -123,6 +139,7 @@ export declare const RuntimeCapacityEntry: z.ZodObject<{
123
139
  freshUntil: string | null;
124
140
  expiresAt: string | null;
125
141
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
142
+ criticalRole: "five_hour" | "weekly" | null;
126
143
  }>, "many">;
127
144
  /** Whole-entry fallback reason when no window carries a current value. */
128
145
  unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
@@ -143,6 +160,7 @@ export declare const RuntimeCapacityEntry: z.ZodObject<{
143
160
  freshUntil: string | null;
144
161
  expiresAt: string | null;
145
162
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
163
+ criticalRole: "five_hour" | "weekly" | null;
146
164
  }[];
147
165
  }, {
148
166
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
@@ -161,6 +179,7 @@ export declare const RuntimeCapacityEntry: z.ZodObject<{
161
179
  freshUntil: string | null;
162
180
  expiresAt: string | null;
163
181
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
182
+ criticalRole: "five_hour" | "weekly" | null;
164
183
  }[];
165
184
  }>;
166
185
  export type RuntimeCapacityEntry = z.infer<typeof RuntimeCapacityEntry>;
@@ -192,6 +211,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
192
211
  expiresAt: z.ZodNullable<z.ZodString>;
193
212
  source: z.ZodEnum<["supported_protocol", "observed_event", "experimental_api", "unavailable"]>;
194
213
  unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
214
+ criticalRole: z.ZodNullable<z.ZodEnum<["five_hour", "weekly"]>>;
195
215
  }, "strip", z.ZodTypeAny, {
196
216
  observedAt: string;
197
217
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -206,6 +226,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
206
226
  freshUntil: string | null;
207
227
  expiresAt: string | null;
208
228
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
229
+ criticalRole: "five_hour" | "weekly" | null;
209
230
  }, {
210
231
  observedAt: string;
211
232
  source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
@@ -220,6 +241,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
220
241
  freshUntil: string | null;
221
242
  expiresAt: string | null;
222
243
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
244
+ criticalRole: "five_hour" | "weekly" | null;
223
245
  }>, "many">;
224
246
  /** Whole-entry fallback reason when no window carries a current value. */
225
247
  unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
@@ -240,6 +262,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
240
262
  freshUntil: string | null;
241
263
  expiresAt: string | null;
242
264
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
265
+ criticalRole: "five_hour" | "weekly" | null;
243
266
  }[];
244
267
  }, {
245
268
  runtime: "claude_code" | "cursor_local" | "codex_local" | "muse_code";
@@ -258,6 +281,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
258
281
  freshUntil: string | null;
259
282
  expiresAt: string | null;
260
283
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
284
+ criticalRole: "five_hour" | "weekly" | null;
261
285
  }[];
262
286
  }>, "many">;
263
287
  generatedAt: z.ZodString;
@@ -280,6 +304,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
280
304
  freshUntil: string | null;
281
305
  expiresAt: string | null;
282
306
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
307
+ criticalRole: "five_hour" | "weekly" | null;
283
308
  }[];
284
309
  }[];
285
310
  }, {
@@ -301,6 +326,7 @@ export declare const RuntimeCapacityResponse: z.ZodObject<{
301
326
  freshUntil: string | null;
302
327
  expiresAt: string | null;
303
328
  unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
329
+ criticalRole: "five_hour" | "weekly" | null;
304
330
  }[];
305
331
  }[];
306
332
  }>;
@@ -415,5 +441,68 @@ export declare const CursorTeamBilling: z.ZodObject<{
415
441
  export type CursorTeamBilling = z.infer<typeof CursorTeamBilling>;
416
442
  /** True when the billing snapshot carries current, renderable values. */
417
443
  export declare function isTeamBillingKnown(b: CursorTeamBilling, nowMs?: number): boolean;
444
+ /**
445
+ * NOT-250: monthly/billing-cycle capacity for Cursor Individual accounts via
446
+ * an opt-in *experimental* dashboard adapter. Cursor documents monthly
447
+ * individual usage (Spending dashboard) but exposes no supported usage API,
448
+ * so this surface reads authenticated dashboard endpoints using the existing
449
+ * local Cursor login. Those endpoints and credential formats are not a
450
+ * supported public contract and may change without notice — hence disabled
451
+ * by default, `experimental_api` source, and N/A degradation on any drift.
452
+ * Billing-cycle values only (label/start/end, reported usage, remaining
453
+ * percent): no 5H/1W-style windows are invented, and money is never rendered
454
+ * as a token percentage. N/A reasons reuse CapacityUnavailableReason.
455
+ */
456
+ export declare const CursorIndividualBilling: z.ZodObject<{
457
+ /** True only with explicit opt-in (`AGENT_DEALER_CURSOR_INDIVIDUAL_CAPACITY=experimental`). */
458
+ enabled: z.ZodBoolean;
459
+ /** True only when opted in AND a usable local credential was found. */
460
+ configured: z.ZodBoolean;
461
+ /** Billing-cycle label exactly as reported (e.g. `September 2026`), never guessed. */
462
+ cycleLabel: z.ZodNullable<z.ZodString>;
463
+ /** Billing-cycle start as reported (ISO-8601, nullable). */
464
+ cycleStart: z.ZodNullable<z.ZodString>;
465
+ /** Billing-cycle reset/end as reported (ISO-8601, nullable). */
466
+ cycleEnd: z.ZodNullable<z.ZodString>;
467
+ /** Reported usage value/unit as reported (never normalized away). */
468
+ usageValue: z.ZodNullable<z.ZodNumber>;
469
+ usageUnit: z.ZodNullable<z.ZodString>;
470
+ /** Normalized remaining percent 0–100 when derivable; null renders N/A. */
471
+ remainingPercent: z.ZodNullable<z.ZodNumber>;
472
+ source: z.ZodEnum<["supported_protocol", "observed_event", "experimental_api", "unavailable"]>;
473
+ unavailableReason: z.ZodNullable<z.ZodEnum<["unsupported", "missing", "expired", "unparsable", "stale"]>>;
474
+ /** When the backing dashboard read was observed (ISO-8601, nullable). */
475
+ observedAt: z.ZodNullable<z.ZodString>;
476
+ generatedAt: z.ZodString;
477
+ }, "strip", z.ZodTypeAny, {
478
+ observedAt: string | null;
479
+ generatedAt: string;
480
+ source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
481
+ remainingPercent: number | null;
482
+ unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
483
+ configured: boolean;
484
+ cycleStart: string | null;
485
+ cycleEnd: string | null;
486
+ enabled: boolean;
487
+ cycleLabel: string | null;
488
+ usageValue: number | null;
489
+ usageUnit: string | null;
490
+ }, {
491
+ observedAt: string | null;
492
+ generatedAt: string;
493
+ source: "unavailable" | "supported_protocol" | "observed_event" | "experimental_api";
494
+ remainingPercent: number | null;
495
+ unavailableReason: "unsupported" | "missing" | "expired" | "unparsable" | "stale" | null;
496
+ configured: boolean;
497
+ cycleStart: string | null;
498
+ cycleEnd: string | null;
499
+ enabled: boolean;
500
+ cycleLabel: string | null;
501
+ usageValue: number | null;
502
+ usageUnit: string | null;
503
+ }>;
504
+ export type CursorIndividualBilling = z.infer<typeof CursorIndividualBilling>;
505
+ /** True when the individual billing snapshot carries current, renderable values. */
506
+ export declare function isIndividualBillingKnown(b: CursorIndividualBilling, nowMs?: number): boolean;
418
507
  /** True when the window carries a current, renderable remaining value. */
419
508
  export declare function isWindowKnown(w: CapacityWindowSnapshot, nowMs?: number): boolean;