@qadanm/opencode-smart-router 0.0.0-stage → 0.4.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 (65) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/CONTRIBUTING.md +96 -0
  3. package/LICENSE +21 -0
  4. package/README.md +341 -2
  5. package/SECURITY.md +174 -0
  6. package/config.example.json +108 -0
  7. package/dist/allowance.js +487 -0
  8. package/dist/gui-experimental.cjs +1 -0
  9. package/dist/index.js +11111 -0
  10. package/dist/npm/index.js +40 -0
  11. package/dist/tui.js +587 -0
  12. package/docs/ARCHITECTURE.md +104 -0
  13. package/docs/DEBUGGING-GUI.md +70 -0
  14. package/docs/INSTALL.md +120 -0
  15. package/docs/LIMITATIONS.md +213 -0
  16. package/docs/MODELS.md +124 -0
  17. package/docs/OPENDESIGN-INTEGRATION.md +635 -0
  18. package/docs/QA.md +142 -0
  19. package/docs/SHARED-CORE.md +72 -0
  20. package/install +75 -0
  21. package/package.json +92 -4
  22. package/scripts/install.sh +324 -0
  23. package/scripts/uninstall.sh +91 -0
  24. package/src/allowance.ts +135 -0
  25. package/src/core/classify/classifier.ts +305 -0
  26. package/src/core/config-writer.ts +167 -0
  27. package/src/core/config.ts +1030 -0
  28. package/src/core/context/strategy.ts +188 -0
  29. package/src/core/context-health.ts +39 -0
  30. package/src/core/failures.ts +398 -0
  31. package/src/core/history/opencode.ts +173 -0
  32. package/src/core/models/protocol.ts +22 -0
  33. package/src/core/models/registry.ts +203 -0
  34. package/src/core/models/seed.ts +771 -0
  35. package/src/core/offline/mode.ts +321 -0
  36. package/src/core/plan-page.ts +390 -0
  37. package/src/core/plans/catalog.ts +481 -0
  38. package/src/core/policy/calibration.ts +259 -0
  39. package/src/core/policy/eligibility.ts +578 -0
  40. package/src/core/providers/enablement.ts +143 -0
  41. package/src/core/providers/opendesign-credits.ts +206 -0
  42. package/src/core/providers/opendesign-models.ts +628 -0
  43. package/src/core/providers/opendesign.ts +474 -0
  44. package/src/core/quota/calibration.ts +166 -0
  45. package/src/core/quota/commandcode.ts +471 -0
  46. package/src/core/quota/credentials.ts +490 -0
  47. package/src/core/quota/ledger.ts +1273 -0
  48. package/src/core/quota/opencode-go.ts +148 -0
  49. package/src/core/quota/opendesign.ts +1043 -0
  50. package/src/core/quota/reconcile.ts +155 -0
  51. package/src/core/quota/report.ts +261 -0
  52. package/src/core/quota/service.ts +273 -0
  53. package/src/core/reprompt.ts +48 -0
  54. package/src/core/routing/engine.ts +919 -0
  55. package/src/core/types.ts +825 -0
  56. package/src/core/ui.ts +236 -0
  57. package/src/core/util.ts +114 -0
  58. package/src/gui/index.tsx +1670 -0
  59. package/src/gui/viewmodel.ts +456 -0
  60. package/src/index.ts +48 -0
  61. package/src/rpc.ts +204 -0
  62. package/src/server/controller.ts +3281 -0
  63. package/src/server/route-tracker.ts +60 -0
  64. package/src/server/shared-core.ts +671 -0
  65. package/src/tui/index.tsx +168 -0
@@ -0,0 +1,60 @@
1
+ import type { RoutingDecision, TaskClass } from "../core/types.js"
2
+
3
+ /**
4
+ * What the router chose for the current prompt of a session, kept so the
5
+ * retry hook can re-route the same task when a provider deterministically
6
+ * rejects the request. The retry event carries no prompt linkage of its own,
7
+ * so the prompt hook records the pairing here.
8
+ *
9
+ * Sized like the other per-session caches (the `expectedSwitch` pattern):
10
+ * bounded, oldest entries evicted, never persisted to disk.
11
+ */
12
+ export interface PromptRouteEntry {
13
+ /** The user prompt the routing belongs to (when the host reports one). */
14
+ readonly messageID?: string
15
+ readonly task: TaskClass
16
+ readonly decision: RoutingDecision
17
+ readonly at: number
18
+ /** Set once a prompt-rejection failover for this prompt has run. */
19
+ healedAt?: number
20
+ }
21
+
22
+ const LIMIT = 500
23
+
24
+ export class PromptRouteTracker {
25
+ private readonly entries = new Map<string, PromptRouteEntry>()
26
+
27
+ set(sessionID: string, entry: PromptRouteEntry): void {
28
+ if (this.entries.size >= LIMIT && !this.entries.has(sessionID)) {
29
+ this.evict()
30
+ }
31
+ this.entries.set(sessionID, entry)
32
+ }
33
+
34
+ get(sessionID: string): PromptRouteEntry | undefined {
35
+ return this.entries.get(sessionID)
36
+ }
37
+
38
+ clear(sessionID: string): void {
39
+ this.entries.delete(sessionID)
40
+ }
41
+
42
+ clearAll(): void {
43
+ this.entries.clear()
44
+ }
45
+
46
+ size(): number {
47
+ return this.entries.size
48
+ }
49
+
50
+ private evict(): void {
51
+ const cutoff = Date.now() - 60_000
52
+ for (const [sessionID, entry] of this.entries) {
53
+ if (entry.at < cutoff) this.entries.delete(sessionID)
54
+ }
55
+ if (this.entries.size >= LIMIT) {
56
+ const oldest = [...this.entries.entries()].sort((a, b) => a[1].at - b[1].at)[0]
57
+ if (oldest) this.entries.delete(oldest[0])
58
+ }
59
+ }
60
+ }