clauderipple 0.8.9 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.es.md +0 -4
  3. package/README.ja.md +0 -4
  4. package/README.ko.md +5 -1
  5. package/README.md +5 -1
  6. package/README.pt-BR.md +0 -4
  7. package/README.zh-CN.md +0 -4
  8. package/dist/app/dist/main.js +23 -3
  9. package/dist/app/package.json +1 -1
  10. package/dist/cli/src/codex.js +3 -1
  11. package/dist/cli/src/index.js +1 -1
  12. package/dist/cli/src/settings.js +5 -2
  13. package/dist/cli/src/tray.js +41 -5
  14. package/dist/mod/.claude-plugin/plugin.json +9 -1
  15. package/dist/mod/hooks/cache.ts +60 -0
  16. package/dist/mod/hooks/compact.ts +40 -0
  17. package/dist/mod/hooks/handoff.ts +43 -0
  18. package/dist/mod/hooks/i18n.ts +2716 -0
  19. package/dist/mod/hooks/judge.ts +226 -0
  20. package/dist/mod/hooks/ladder.js +179 -0
  21. package/dist/mod/hooks/ladder.ts +174 -0
  22. package/dist/mod/hooks/modes.ts +93 -0
  23. package/dist/mod/hooks/register.tsx +1458 -256
  24. package/dist/mod/hooks/session.ts +62 -0
  25. package/dist/mod/hooks/settings.js +342 -0
  26. package/dist/mod/hooks/settings.ts +321 -0
  27. package/dist/mod/hooks/stats.ts +59 -0
  28. package/dist/mod/hooks/ui/agents.tsx +138 -0
  29. package/dist/mod/hooks/ui/alerts.tsx +370 -0
  30. package/dist/mod/hooks/ui/art.ts +628 -0
  31. package/dist/mod/hooks/ui/band.tsx +836 -0
  32. package/dist/mod/hooks/ui/kit.tsx +258 -0
  33. package/dist/mod/hooks/ui/log.tsx +109 -0
  34. package/dist/mod/hooks/ui/parts.tsx +217 -0
  35. package/dist/mod/hooks/ui/ripple.ts +614 -0
  36. package/dist/mod/hooks/ui/settings.tsx +376 -0
  37. package/dist/mod/hooks/ui/setup.tsx +148 -0
  38. package/dist/mod/hooks/ui/theme.ts +217 -0
  39. package/dist/mod/hooks/ui/view.ts +371 -0
  40. package/dist/mod/hooks/worker.ts +1 -1
  41. package/dist/mod/hooks/workers.ts +217 -0
  42. package/dist/mod/tests/register.test.tsx +1730 -0
  43. package/dist/mod/tests/ui.test.tsx +853 -0
  44. package/dist/mod/tsconfig.json +2 -1
  45. package/dist/mod/types/index.d.ts +320 -1
  46. package/dist/router/src/admin.js +195 -53
  47. package/dist/router/src/agents.js +24 -5
  48. package/dist/router/src/bootstrap.js +4 -2
  49. package/dist/router/src/compat.js +10 -2
  50. package/dist/router/src/config.js +3 -0
  51. package/dist/router/src/health.js +33 -0
  52. package/dist/router/src/index.js +1 -0
  53. package/dist/router/src/ingress/models.js +4 -1
  54. package/dist/router/src/mod-settings.js +119 -0
  55. package/dist/router/src/model-policy.js +6 -0
  56. package/dist/router/src/model-scores.js +159 -0
  57. package/dist/router/src/picker.js +7 -0
  58. package/dist/router/src/providers/anthropic-account-pool.js +10 -4
  59. package/dist/router/src/providers/anthropic-accounts.js +26 -1
  60. package/dist/router/src/providers/chatgpt/catalog.js +45 -14
  61. package/dist/router/src/providers/chatgpt/index.js +9 -3
  62. package/dist/router/src/providers/chatgpt/translate.js +9 -5
  63. package/dist/router/src/providers/google/index.js +6 -2
  64. package/dist/router/src/providers/openai/index.js +4 -1
  65. package/dist/router/src/providers/openai/translate.js +1 -1
  66. package/dist/router/src/proxy.js +14 -3
  67. package/dist/router/src/version.js +1 -1
  68. package/dist/ui/app.js +932 -209
  69. package/dist/ui/favicon-32.png +0 -0
  70. package/dist/ui/favicon.svg +12 -0
  71. package/dist/ui/i18n.js +923 -237
  72. package/dist/ui/index.html +159 -87
  73. package/dist/ui/style.css +789 -408
  74. package/docs/ARCHITECTURE.md +134 -2
  75. package/package.json +7 -3
@@ -18,7 +18,7 @@ import https from "node:https";
18
18
  import net from "node:net";
19
19
  import tls from "node:tls";
20
20
  import zlib from "node:zlib";
21
- import { UpstreamHealth } from "./health.js";
21
+ import { ProviderHealth, providerExhaustion, UpstreamHealth } from "./health.js";
22
22
  import { BOOTSTRAP_PATH, injectBootstrap } from "./bootstrap.js";
23
23
  import { THREAD_UNSUPPORTED, effortOf, resolve, rewriteBody, stripThreadFields, threadDecision, unroutableReason } from "./routing.js";
24
24
  import { defaultAgentDir, withAgentAliases } from "./agents.js";
@@ -32,7 +32,7 @@ import { OpenAiCompatibleAdapter } from "./providers/openai/index.js";
32
32
  import { GoogleAdapter } from "./providers/google/index.js";
33
33
  import { conversationKey } from "./providers/chatgpt/translate.js";
34
34
  import { providerFor, searchProviderFor, terminateHosts } from "./config.js";
35
- import { injectPickerModels, isBootstrapPath } from "./picker.js";
35
+ import { chatPickerModels, injectPickerModels, isBootstrapPath } from "./picker.js";
36
36
  import { effortLevelsForModel } from "./admin.js";
37
37
  import { ResponseUsageTap } from "./requestlog.js";
38
38
  import { credentialHeaderValues, redactErrorText, redactHeaders } from "./redact.js";
@@ -198,6 +198,7 @@ export class Proxy {
198
198
  * its quarantine again on the first request.
199
199
  */
200
200
  pool = new CredentialPool();
201
+ providerHealth = new ProviderHealth();
201
202
  /** Latest rate-limit snapshot reported by any chatgpt provider (for the admin GUI). */
202
203
  get chatgptRateLimits() {
203
204
  const out = {};
@@ -784,6 +785,8 @@ export class Proxy {
784
785
  // The adapter reports each account's outcome to the shared pool itself, so a slot pointing
785
786
  // here fails over once every account is out (chooseTarget asks the adapter).
786
787
  const o = await this.chatgpt(route.provider, provider).handle(req, res, path, json, route.model, effortOf(json));
788
+ if (!path.startsWith("/v1/messages/count_tokens"))
789
+ this.providerHealth.observe(route.provider, o.status, o.exhaustion);
787
790
  finish(String(o.status), o.bytes, o.note, o.status >= 400, { ...(o.usage ? { usage: o.usage } : {}), ...(o.stopReason ? { stopReason: o.stopReason } : {}) });
788
791
  }
789
792
  catch (e) {
@@ -813,6 +816,8 @@ export class Proxy {
813
816
  try {
814
817
  const o = await this.openai(route.provider, provider).handle(req, res, path, json, route.model, routeEffort);
815
818
  this.recordOutcome(route.provider, o.status);
819
+ if (!path.startsWith("/v1/messages/count_tokens"))
820
+ this.providerHealth.observe(route.provider, o.status, o.exhaustion);
816
821
  finish(String(o.status), o.bytes, o.note, o.status >= 400, { ...(o.usage ? { usage: o.usage } : {}), ...(o.stopReason ? { stopReason: o.stopReason } : {}) });
817
822
  }
818
823
  catch (e) {
@@ -845,6 +850,8 @@ export class Proxy {
845
850
  try {
846
851
  const o = await this.google(route.provider, provider).handle(req, res, path, json, route.model, routeEffort);
847
852
  this.recordOutcome(route.provider, o.status);
853
+ if (!path.startsWith("/v1/messages/count_tokens"))
854
+ this.providerHealth.observe(route.provider, o.status, o.exhaustion);
848
855
  finish(String(o.status), o.bytes, o.note, o.status >= 400, { ...(o.usage ? { usage: o.usage } : {}), ...(o.stopReason ? { stopReason: o.stopReason } : {}) });
849
856
  }
850
857
  catch (e) {
@@ -1274,7 +1281,7 @@ export class Proxy {
1274
1281
  else if (status === 200 && isPickerBootstrap) {
1275
1282
  try {
1276
1283
  const j = JSON.parse(out.toString("utf8"));
1277
- const r = injectPickerModels(j, cfg.cli.extraModels, cfg.cli.autoCompactWindow, (model) => effortLevelsForModel(cfg, model));
1284
+ const r = injectPickerModels(j, chatPickerModels(cfg), cfg.cli.autoCompactWindow, (model) => effortLevelsForModel(cfg, model));
1278
1285
  if (r.surfaces.length > 0) {
1279
1286
  this.lastPickerInjection = { at: new Date().toISOString(), ...r };
1280
1287
  out = Buffer.from(JSON.stringify(j));
@@ -1337,6 +1344,10 @@ export class Proxy {
1337
1344
  observedUsage = observed.usage;
1338
1345
  observedStopReason = observed.stopReason;
1339
1346
  res.end();
1347
+ const errorBody = status >= 400 ? decodeBodyToText(Buffer.concat(errorHead), upRes.headers["content-encoding"]) : "";
1348
+ if (route && path.startsWith("/v1/messages") && !path.startsWith("/v1/messages/count_tokens")) {
1349
+ this.providerHealth.observe(route.provider, status, providerExhaustion(status, errorBody, retryAfterMs(upRes.headers)));
1350
+ }
1340
1351
  const note = status >= 400 ? `upstream ${status}: ${errorSnippet(Buffer.concat(errorHead), upRes.headers["content-encoding"], [...errorSecrets], upRes.headers["content-type"])}` : undefined;
1341
1352
  // Anthropic forgets a conversation's server-side thread after a few idle minutes, and the CLI
1342
1353
  // answers the 404 by replaying the whole conversation, which hits the prompt cache. From CLI
@@ -5,4 +5,4 @@
5
5
  // from the manifests. Until 0.1.1 the router and the CLI each carried their own literal, and both
6
6
  // still said "0.1.0" in the 0.1.1 release: after an update nobody could tell which router was
7
7
  // running, and the tray app had nothing to compare (2026-09-15, reported from a Windows install).
8
- export const VERSION = "0.8.9";
8
+ export const VERSION = "0.9.0";