codehere 1.0.1 → 1.0.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 (163) hide show
  1. package/README.md +35 -193
  2. package/dist/application/use-cases/check-claimed-tests.js +81 -0
  3. package/dist/application/use-cases/detect-test-weakening.js +237 -0
  4. package/dist/application/use-cases/rerun-tests.js +110 -7
  5. package/dist/index.js +38 -1212
  6. package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
  7. package/dist/presentation/cli/commands/check-command.js +178 -23
  8. package/dist/presentation/cli/commands/help-command.js +21 -285
  9. package/dist/ui.js +5 -236
  10. package/dist/utils/version.js +0 -6
  11. package/package.json +9 -38
  12. package/dist/application/services/verdict-service.js +0 -154
  13. package/dist/application/use-cases/assess-cross-vendor.js +0 -126
  14. package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
  15. package/dist/application/use-cases/domain-trust.js +0 -110
  16. package/dist/application/use-cases/export-aat.js +0 -107
  17. package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
  18. package/dist/application/use-cases/generate-final-report.js +0 -259
  19. package/dist/application/use-cases/generate-narration.js +0 -130
  20. package/dist/application/use-cases/generate-thread-title.js +0 -181
  21. package/dist/application/use-cases/narrator-prompt-template.js +0 -158
  22. package/dist/application/use-cases/plan-auto-review.js +0 -57
  23. package/dist/application/use-cases/reconcile-audit.js +0 -100
  24. package/dist/application/use-cases/search-sessions.js +0 -70
  25. package/dist/application/use-cases/should-auto-review.js +0 -71
  26. package/dist/application/use-cases/thread-export.js +0 -245
  27. package/dist/application/use-cases/verify-claim.js +0 -456
  28. package/dist/application/use-cases/write-pass.js +0 -277
  29. package/dist/domain/audit.js +0 -11
  30. package/dist/domain/services/codebase-detector.js +0 -85
  31. package/dist/domain/task.js +0 -91
  32. package/dist/error-handler.js +0 -139
  33. package/dist/infrastructure/completion/bash-completion.js +0 -29
  34. package/dist/infrastructure/completion/fish-completion.js +0 -17
  35. package/dist/infrastructure/completion/zsh-completion.js +0 -25
  36. package/dist/infrastructure/config/config-manager.js +0 -174
  37. package/dist/infrastructure/config/product-context.js +0 -49
  38. package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
  39. package/dist/infrastructure/context/adapters/figma-source.js +0 -198
  40. package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
  41. package/dist/infrastructure/context/adapters/linear-source.js +0 -190
  42. package/dist/infrastructure/context/context-resolver.js +0 -144
  43. package/dist/infrastructure/context/context-source.js +0 -114
  44. package/dist/infrastructure/context/github-api.js +0 -110
  45. package/dist/infrastructure/context/github-device-flow.js +0 -65
  46. package/dist/infrastructure/context/oauth-flow.js +0 -207
  47. package/dist/infrastructure/context/oauth-store.js +0 -122
  48. package/dist/infrastructure/context/registry-bootstrap.js +0 -95
  49. package/dist/infrastructure/context/write-pass-resolver.js +0 -106
  50. package/dist/infrastructure/cost/budget-config.js +0 -114
  51. package/dist/infrastructure/cost/cost-router.js +0 -97
  52. package/dist/infrastructure/cost/spend-aggregator.js +0 -222
  53. package/dist/infrastructure/cost/team-ledger.js +0 -355
  54. package/dist/infrastructure/discovery/discovery-log.js +0 -85
  55. package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
  56. package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
  57. package/dist/infrastructure/hooks/hook-mode.js +0 -87
  58. package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
  59. package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
  60. package/dist/infrastructure/hooks/session-analyzer.js +0 -177
  61. package/dist/infrastructure/observability/execution-tracer.js +0 -227
  62. package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
  63. package/dist/infrastructure/orchestration/agent-installer.js +0 -101
  64. package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
  65. package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
  66. package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
  67. package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
  68. package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
  69. package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
  70. package/dist/infrastructure/orchestration/context-handoff.js +0 -162
  71. package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
  72. package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
  73. package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
  74. package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
  75. package/dist/infrastructure/orchestration/failover-config.js +0 -67
  76. package/dist/infrastructure/orchestration/failover-policy.js +0 -106
  77. package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
  78. package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
  79. package/dist/infrastructure/orchestration/graphify-context.js +0 -209
  80. package/dist/infrastructure/orchestration/memory-injector.js +0 -380
  81. package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
  82. package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
  83. package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
  84. package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
  85. package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
  86. package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
  87. package/dist/infrastructure/orchestration/readiness.js +0 -103
  88. package/dist/infrastructure/orchestration/session-manager.js +0 -1590
  89. package/dist/infrastructure/orchestration/session-persistence.js +0 -394
  90. package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
  91. package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
  92. package/dist/infrastructure/protocols/cloud-sync.js +0 -207
  93. package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
  94. package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
  95. package/dist/infrastructure/protocols/mcp-tools.js +0 -401
  96. package/dist/infrastructure/protocols/probe-port.js +0 -36
  97. package/dist/infrastructure/protocols/thread-phase.js +0 -105
  98. package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
  99. package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
  100. package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
  101. package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
  102. package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
  103. package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
  104. package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
  105. package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
  106. package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
  107. package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
  108. package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
  109. package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
  110. package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
  111. package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
  112. package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
  113. package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
  114. package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
  115. package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
  116. package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
  117. package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
  118. package/dist/infrastructure/protocols/ui/styles.js +0 -2147
  119. package/dist/infrastructure/protocols/ui/tokens.js +0 -96
  120. package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
  121. package/dist/infrastructure/protocols/verify-page.js +0 -1677
  122. package/dist/infrastructure/security/file-collection.js +0 -118
  123. package/dist/infrastructure/security/fix-code.js +0 -144
  124. package/dist/infrastructure/security/fix-secret.js +0 -89
  125. package/dist/infrastructure/security/permissions-config.js +0 -235
  126. package/dist/infrastructure/security/scan-types.js +0 -10
  127. package/dist/infrastructure/security/ship-scan.js +0 -195
  128. package/dist/infrastructure/security/trust-scorer.js +0 -724
  129. package/dist/infrastructure/security/vibe-checks.js +0 -99
  130. package/dist/infrastructure/storage/atomic-write.js +0 -86
  131. package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
  132. package/dist/infrastructure/storage/project-thread-migration.js +0 -237
  133. package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
  134. package/dist/infrastructure/storage/project-thread-reader.js +0 -409
  135. package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
  136. package/dist/monitoring.js +0 -212
  137. package/dist/presentation/cli/commands/audit-command.js +0 -201
  138. package/dist/presentation/cli/commands/budget-command.js +0 -79
  139. package/dist/presentation/cli/commands/config-command.js +0 -125
  140. package/dist/presentation/cli/commands/context-source-command.js +0 -217
  141. package/dist/presentation/cli/commands/hook-command.js +0 -265
  142. package/dist/presentation/cli/commands/init-command.js +0 -590
  143. package/dist/presentation/cli/commands/memory-command.js +0 -1031
  144. package/dist/presentation/cli/commands/narrator-command.js +0 -101
  145. package/dist/presentation/cli/commands/permission-command.js +0 -105
  146. package/dist/presentation/cli/commands/protocols-builder.js +0 -254
  147. package/dist/presentation/cli/commands/protocols-command.js +0 -84
  148. package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
  149. package/dist/presentation/cli/commands/report-command.js +0 -403
  150. package/dist/presentation/cli/commands/scan-command.js +0 -52
  151. package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
  152. package/dist/presentation/cli/commands/search-command.js +0 -51
  153. package/dist/presentation/cli/commands/setup-command.js +0 -330
  154. package/dist/presentation/cli/commands/status-command.js +0 -614
  155. package/dist/presentation/cli/commands/team-command.js +0 -256
  156. package/dist/presentation/cli/commands/thread-command.js +0 -370
  157. package/dist/presentation/cli/commands/trust-command.js +0 -328
  158. package/dist/presentation/cli/commands/verify-command.js +0 -298
  159. package/dist/presentation/cli/error-display.js +0 -328
  160. package/dist/presentation/cli/repl.js +0 -805
  161. package/dist/presentation/cli/task-deliverable.js +0 -153
  162. package/dist/testing/cassette.js +0 -125
  163. package/dist/testing/replay-adapter.js +0 -147
@@ -1,15 +0,0 @@
1
- /**
2
- * ControlSurface · the vendor-neutral "drive another agent" abstraction.
3
- *
4
- * The POC (agent/test/evidence/poc/control-channel/) proved the cross-agent
5
- * finding: each agent exposes a DIFFERENT control surface (codex app-server
6
- * turn/steer · claude --channels · etc.), so codehere's control layer is one
7
- * interface implemented per-adapter — exactly the adapter pattern codehere
8
- * already uses for spawning.
9
- *
10
- * `inject(message)` is the core primitive: push a message INTO a running agent
11
- * session (the remote-control headline). Today: codex app-server. Next: claude
12
- * channel.
13
- */
14
- export {};
15
- //# sourceMappingURL=control-surface.js.map
@@ -1,75 +0,0 @@
1
- /**
2
- * Detect a local Ollama install with at least one model — so codehere can offer
3
- * a RELIABLE, FREE, LOCAL second vendor (aider → ollama) instead of depending on
4
- * flaky free tiers or the user's cloud billing. This is the unlock for the
5
- * cross-vendor wedge (V2) on a machine that has Ollama: a local model that
6
- * completes real coding tasks with no rate limits and no key.
7
- *
8
- * Cheap + cached: `ollama list` is shelled at most once per TTL window; if Ollama
9
- * isn't installed/running the call fails fast and we cache "not available", so the
10
- * common no-Ollama path costs ~one bounded spawn every 30s, never per task.
11
- */
12
- import { execFileSync } from 'node:child_process';
13
- let cache;
14
- let cachedAt = 0;
15
- const TTL_MS = 30_000;
16
- /** Embedding / non-chat models aider can't drive as a coder. Routing to one
17
- * would fail silently, so they're never auto-picked (self-audit 2026-06-09). */
18
- const NON_CHAT = /embed|rerank|bge|nomic|minilm|e5-|gte-|clip|whisper|moondream|llava-phi/i;
19
- /** Pick the model to run: an explicit CODEHERE_OLLAMA_MODEL override if it's
20
- * actually installed, else the first GENERATIVE model. We don't guess a "best"
21
- * coder (a /coder/ bias picked an 18GB model — slow); we only EXCLUDE models
22
- * aider can't use. The user knows their hardware — CODEHERE_OLLAMA_MODEL is the
23
- * knob (refuter + self-audit 2026-06-09). */
24
- function pickModel(models) {
25
- if (models.length === 0)
26
- return undefined;
27
- const override = process.env.CODEHERE_OLLAMA_MODEL;
28
- if (override && models.includes(override))
29
- return override; // explicit wins, even if "non-chat"
30
- // :cloud tags are ollama's CLOUD-proxied models — no local weights, billed
31
- // account required. Auto-picking one breaks this module's whole promise
32
- // (reliable, free, local) with "couldn't reach the model provider" — hit
33
- // live 2026-07-02 when glm-5.2:cloud was first in `ollama list`. Explicit
34
- // override above still allows them; auto-pick never does.
35
- return models.find((m) => !NON_CHAT.test(m) && !/:cloud$/i.test(m)) ?? undefined;
36
- }
37
- /** Detect a usable local Ollama. Never throws. */
38
- export function detectOllama() {
39
- const now = Date.now();
40
- if (cache && now - cachedAt < TTL_MS)
41
- return cache;
42
- let info = { available: false };
43
- try {
44
- // Short timeout: this is a SYNC call and readiness runs it on the daemon's
45
- // event loop (the UI polls readiness), so a HUNG ollama must not block the
46
- // daemon for long. 700ms once per TTL window is a tolerable worst case; the
47
- // common (fast or absent) paths return in ~20ms (refuter 2026-06-09).
48
- const out = execFileSync('ollama', ['list'], {
49
- timeout: 700,
50
- encoding: 'utf8',
51
- stdio: ['ignore', 'pipe', 'ignore'],
52
- });
53
- // `ollama list` prints a header row then "NAME ID SIZE MODIFIED" rows.
54
- const models = out
55
- .split('\n')
56
- .slice(1)
57
- .map((l) => l.trim().split(/\s+/)[0])
58
- .filter((m) => m && m !== 'NAME');
59
- const model = pickModel(models);
60
- if (model)
61
- info = { available: true, model };
62
- }
63
- catch {
64
- /* ollama not installed / not serving → not available */
65
- }
66
- cache = info;
67
- cachedAt = now;
68
- return info;
69
- }
70
- /** @internal test seam — clear the memoized detection. */
71
- export function __resetOllamaCache() {
72
- cache = undefined;
73
- cachedAt = 0;
74
- }
75
- //# sourceMappingURL=detect-ollama.js.map
@@ -1,151 +0,0 @@
1
- /**
2
- * Failover classifier — v0.6.x rate-limit fail-over wedge (Slice A).
3
- *
4
- * Interprets adapter error codes to decide whether fail-over to a different
5
- * adapter should be offered to the user. Pure function — no I/O, no adapter
6
- * spawning. Adapters already detect rate-limit / quota errors via their
7
- * ERROR_PATTERNS arrays and emit `codehere:error` events with codes:
8
- * - 'rate_limit' — Anthropic / OpenAI / Google 429s
9
- * - 'quota_exceeded' — credit balance / quota exhausted
10
- * - 'auth_invalid' — NOT failover-eligible (different fix)
11
- * - other codes — NOT failover-eligible (specific to provider)
12
- *
13
- * This module's job: take an emitted error code, return either a FailoverSignal
14
- * (caller should ask user "switch to Codex?") or null (don't offer failover).
15
- *
16
- * Per `feedback_research_sharpens_not_pivots.md` — this sharpens what already
17
- * ships (per-adapter error patterns) rather than introducing a new detection
18
- * mechanism. Per claim_market_position v4 — the fail-over capability is the
19
- * v0.6 outward proof point that demonstrates codehere's vendor-neutral value
20
- * (only a vendor-neutral layer can switch you off Claude when Claude refuses).
21
- *
22
- * Slice A scope (this commit): the pure classifier + retry-after parser +
23
- * tests. Slices B-F wire policy resolution, user consent, mid-task hand-off,
24
- * UI surfacing, and end-to-end integration.
25
- */
26
- /**
27
- * Error codes that should trigger a fail-over offer. Keep tight — false
28
- * positives waste user attention with "switch?" prompts they don't want.
29
- *
30
- * Verified against current adapter ERROR_PATTERNS (May 2026):
31
- * - 'rate_limit' — emitted by all 5 adapters on 429s
32
- * - 'quota_exceeded' — emitted by codex-adapter, gemini-adapter
33
- * - 'credit_low' — emitted by claude-code-adapter, aider-adapter
34
- * when API credit balance is exhausted
35
- *
36
- * NOT included (and why):
37
- * - 'auth_invalid' — fixing the wrong adapter doesn't help; switching to
38
- * a different agent that ALSO needs auth doesn't fix anything
39
- * - generic errors — no signal that another adapter would do better
40
- * - timeouts — could be transient on either side
41
- */
42
- const FAILOVER_TRIGGER_CODES = new Set(['rate_limit', 'quota_exceeded', 'credit_low']);
43
- /**
44
- * Returns a FailoverSignal if the error code suggests we should offer
45
- * fail-over to a different adapter, null otherwise. Pure function — no
46
- * side effects. Caller decides how to surface the offer (CLI prompt,
47
- * web UI banner, programmatic auto-failover).
48
- *
49
- * @param fromAgent Adapter id of the agent that hit the error
50
- * @param errorCode Code from the adapter's ERROR_PATTERNS classification
51
- * @param errorMessage Original error text (used to extract retry-after)
52
- */
53
- export function classifyFailoverEligibility(fromAgent, errorCode, errorMessage) {
54
- if (!FAILOVER_TRIGGER_CODES.has(errorCode))
55
- return null;
56
- if (!fromAgent)
57
- return null;
58
- const retryAfterMs = parseRetryAfterMs(errorMessage);
59
- const signal = {
60
- fromAgent,
61
- errorCode,
62
- reason: errorMessage || `${fromAgent} hit ${errorCode}`,
63
- };
64
- if (retryAfterMs !== undefined)
65
- signal.retryAfterMs = retryAfterMs;
66
- return signal;
67
- }
68
- /**
69
- * Best-effort extraction of retry-after from common error message shapes.
70
- * Returns milliseconds or undefined. Conservative — only matches patterns
71
- * we've seen in real provider responses; unknown shapes return undefined
72
- * (caller treats as "unknown — show indefinite wait").
73
- *
74
- * Patterns recognized:
75
- * - "Retry-After: 60"
76
- * - "retry after 60s"
77
- * - "retry after 60 seconds"
78
- * - "try again in 2 minutes"
79
- * - "wait 5 min"
80
- * - "wait 90 seconds"
81
- * - "in 2m 30s" (rare, treat as 2 minutes)
82
- *
83
- * NOT matched (intentionally — too ambiguous):
84
- * - bare numbers ("60") — could be anything
85
- * - "wait a moment" — no quantity
86
- */
87
- export function parseRetryAfterMs(message) {
88
- if (!message)
89
- return undefined;
90
- // "Retry-After: <N> <unit>" — HTTP header style with EXPLICIT unit.
91
- // Per RFC 7231, default unit is seconds; we also accept explicit unit
92
- // suffix (some providers add one for clarity, e.g. "Retry-After: 5 minutes").
93
- // Must NOT match HTTP-date forms ("Retry-After: Wed, 21 Oct ...").
94
- const headerWithUnitMatch = message.match(/retry-?after[:\s]+(\d+)\s*(seconds?|secs?|s\b|minutes?|mins?|m\b|hours?|hrs?|h\b)/i);
95
- if (headerWithUnitMatch) {
96
- const n = Number(headerWithUnitMatch[1]);
97
- const unit = headerWithUnitMatch[2].toLowerCase();
98
- if (Number.isFinite(n) && n > 0)
99
- return convertToMs(n, unit);
100
- }
101
- // "Retry-After: 60" — bare header with no unit, defaults to seconds per RFC.
102
- // Negative lookahead rejects HTTP-date forms (alphabetic chars after the digits).
103
- const headerBareMatch = message.match(/retry-?after[:\s]+(\d+)(?!\s*[A-Za-z])/i);
104
- if (headerBareMatch) {
105
- const sec = Number(headerBareMatch[1]);
106
- if (Number.isFinite(sec) && sec > 0)
107
- return sec * 1000;
108
- }
109
- // Order matters: try LARGER units first so compound durations like
110
- // "2m 30s" or "1h 45m" don't get misread as the smaller unit. Without
111
- // this, "try again in 2m 30s" would match the seconds pattern on "30s"
112
- // and return 30 seconds instead of ~2 minutes.
113
- //
114
- // Each pattern accepts a fractional value (e.g. OpenAI's "try again in 1.23s")
115
- // — bare-int regex would have matched "23s" and missed the "1." prefix,
116
- // returning 23 seconds instead of ~1.23 seconds. Capture group is "\d+(?:\.\d+)?".
117
- // "in 2 hours" / "try again in 1 hour" — common for daily / weekly limits
118
- const naturalHoursMatch = message.match(/(?:retry|try again|in|wait|reset).*?(\d+(?:\.\d+)?)\s*hours?\b/i);
119
- if (naturalHoursMatch) {
120
- const hr = Number(naturalHoursMatch[1]);
121
- if (Number.isFinite(hr) && hr > 0)
122
- return Math.round(hr * 60 * 60 * 1000);
123
- }
124
- // "retry after 2 minutes" / "try again in 5 min" / "wait 5 min" / "in 2m 30s"
125
- const naturalMinutesMatch = message.match(/(?:retry|try again|wait).*?(\d+(?:\.\d+)?)\s*(?:minutes?|mins?|m\b)/i);
126
- if (naturalMinutesMatch) {
127
- const min = Number(naturalMinutesMatch[1]);
128
- if (Number.isFinite(min) && min > 0)
129
- return Math.round(min * 60 * 1000);
130
- }
131
- // "retry after 60s" / "try again in 60s" / "wait 60s" / "try again in 1.23s"
132
- // (OpenAI returns fractional seconds for sub-2-second rate-limit windows).
133
- // Last because compound durations like "2m 30s" should be read as minutes.
134
- const naturalSecondsMatch = message.match(/(?:retry|try again|wait).*?(\d+(?:\.\d+)?)\s*(?:seconds?|secs?|s\b)/i);
135
- if (naturalSecondsMatch) {
136
- const sec = Number(naturalSecondsMatch[1]);
137
- if (Number.isFinite(sec) && sec > 0)
138
- return Math.round(sec * 1000);
139
- }
140
- return undefined;
141
- }
142
- /** Convert a number + unit string to milliseconds. Unit is one of the regex
143
- * alternatives in the header-with-unit pattern above. */
144
- function convertToMs(n, unit) {
145
- if (/^h/i.test(unit))
146
- return n * 60 * 60 * 1000;
147
- if (/^m/i.test(unit))
148
- return n * 60 * 1000;
149
- return n * 1000; // seconds (default)
150
- }
151
- //# sourceMappingURL=failover-classifier.js.map
@@ -1,67 +0,0 @@
1
- /**
2
- * Failover config loader — v0.6.x rate-limit fail-over wedge (Slice C).
3
- *
4
- * Reads `failoverChain` from `~/.codehere/config.json` and validates it
5
- * against the known agent ids. Returns the validated chain or undefined
6
- * if the user has not configured one (caller falls back to the default).
7
- *
8
- * Config shape (~/.codehere/config.json):
9
- * {
10
- * "version": "0.6.0",
11
- * "failover": {
12
- * "chain": ["codex", "claude-code", "aider"]
13
- * }
14
- * }
15
- *
16
- * If `failover.chain` is missing, empty, or invalid, returns undefined
17
- * so the caller uses DEFAULT_FAILOVER_CHAIN from failover-policy.ts.
18
- *
19
- * Slice C scope: config read + validation. Slice D wires it into
20
- * SessionManager so the resolved chain flows into the resolver.
21
- */
22
- import { readFileSync, existsSync } from 'fs';
23
- import { homedir } from 'os';
24
- import { join } from 'path';
25
- import { validateFailoverChain } from './failover-policy.js';
26
- const GLOBAL_CONFIG_PATH = join(process.env.CODEHERE_HOME || homedir(), '.codehere', 'config.json');
27
- /**
28
- * Load failover configuration from ~/.codehere/config.json.
29
- * Returns undefined if the config file does not exist or has no failover section.
30
- *
31
- * Safe to call at any time — no side effects, no caching, no I/O errors
32
- * propagated (returns undefined on any read failure).
33
- */
34
- export function loadFailoverConfig() {
35
- if (!existsSync(GLOBAL_CONFIG_PATH))
36
- return undefined;
37
- try {
38
- const raw = JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, 'utf-8'));
39
- if (!raw || typeof raw !== 'object')
40
- return undefined;
41
- const failover = raw.failover;
42
- if (!failover || typeof failover !== 'object')
43
- return undefined;
44
- const config = {};
45
- // Validate chain if present
46
- if (Array.isArray(failover.chain)) {
47
- try {
48
- config.chain = validateFailoverChain(failover.chain);
49
- }
50
- catch {
51
- // Invalid chain — skip it, caller uses default
52
- config.chain = undefined;
53
- }
54
- }
55
- // autoFailover — boolean, defaults to false
56
- if (typeof failover.autoFailover === 'boolean') {
57
- config.autoFailover = failover.autoFailover;
58
- }
59
- return config;
60
- }
61
- catch {
62
- // Config file exists but is malformed — return undefined so caller
63
- // falls back to defaults without crashing.
64
- return undefined;
65
- }
66
- }
67
- //# sourceMappingURL=failover-config.js.map
@@ -1,106 +0,0 @@
1
- /**
2
- * Failover policy — v0.6.x rate-limit fail-over wedge (Slice B).
3
- *
4
- * Given: an adapter that hit a fail-over-eligible error (per Slice A's
5
- * `classifyFailoverEligibility`), a user-configured preference chain, and
6
- * the set of currently-available adapters on this machine.
7
- * Returns: the next adapter to try, or null if no fail-over target is
8
- * available.
9
- *
10
- * Pure function — no I/O, no spawning. Caller (session-manager, Slice D)
11
- * uses the resolver output to drive the user-consent flow (Slice C) and
12
- * the mid-task hand-off.
13
- *
14
- * Default chain rationale:
15
- * claude-code → codex → aider → opencode → gemini
16
- *
17
- * The default is ordered by capability+reliability for general coding work
18
- * as of May 2026:
19
- * - claude-code: strongest at multi-file reasoning + tool use
20
- * - codex: strongest at independent review + adversarial mode
21
- * - aider: most reliable for surgical single-file edits
22
- * - opencode: cross-vendor flexibility within itself
23
- * - gemini: longest context window, but free-tier rate limits hit fast
24
- *
25
- * Users can override via `~/.codehere/config.json` `failoverChain` array.
26
- * Per-task override also supported via SpawnOptions.failoverChain (Slice D
27
- * wires this).
28
- *
29
- * Slice B scope (this commit): pure resolver + config schema + tests.
30
- * Slices C-F wire user consent, mid-task hand-off, UI surfacing, e2e.
31
- */
32
- /** Adapter ids in the order codehere supports. Keep in sync with adapter
33
- * registry. Used as the strict ordering for chain validation. */
34
- export const KNOWN_AGENT_IDS = ['claude-code', 'codex', 'aider', 'opencode', 'gemini'];
35
- /** Default fail-over chain. See module docblock for ordering rationale.
36
- * Each entry is the agent codehere will TRY in order when the previous
37
- * one is unavailable or has already been tried this hand-off. */
38
- export const DEFAULT_FAILOVER_CHAIN = ['claude-code', 'codex', 'aider', 'opencode', 'gemini'];
39
- /**
40
- * Resolve the next fail-over target. Returns the adapter id to try, or
41
- * null if no eligible target exists.
42
- *
43
- * Selection rules (in order):
44
- * 1. Use preferredChain if provided, else DEFAULT_FAILOVER_CHAIN
45
- * 2. Skip the failed agent itself
46
- * 3. Skip any adapter in alreadyTried
47
- * 4. Skip adapters not present in available[] OR with ready=false
48
- * 5. Return the FIRST remaining adapter in chain order
49
- * 6. If nothing remains, return null
50
- *
51
- * Pure — no spawn, no config read. Caller assembles the inputs.
52
- */
53
- export function resolveFailoverTarget(opts) {
54
- const chain = opts.preferredChain && opts.preferredChain.length > 0 ? opts.preferredChain : DEFAULT_FAILOVER_CHAIN;
55
- const tried = new Set([opts.failedAgent, ...(opts.alreadyTried ?? [])]);
56
- const readyById = new Map();
57
- for (const a of opts.available)
58
- readyById.set(a.id, a.ready);
59
- for (const candidate of chain) {
60
- if (tried.has(candidate))
61
- continue;
62
- if (readyById.get(candidate) !== true)
63
- continue;
64
- return candidate;
65
- }
66
- return null;
67
- }
68
- /**
69
- * Validate a user-supplied chain (e.g. read from config.json). Returns the
70
- * chain if every entry is a known agent id; throws otherwise. Caller
71
- * (config-loader, Slice C) uses this to give a helpful error if the user
72
- * mistypes an adapter name.
73
- */
74
- export function validateFailoverChain(chain) {
75
- if (!Array.isArray(chain)) {
76
- throw new Error('failoverChain must be an array of agent ids');
77
- }
78
- const validated = [];
79
- for (const item of chain) {
80
- if (typeof item !== 'string') {
81
- throw new Error(`failoverChain entries must be strings, got ${typeof item}`);
82
- }
83
- if (!isKnownAgentId(item)) {
84
- throw new Error(`Unknown agent id in failoverChain: "${item}". Valid ids: ${KNOWN_AGENT_IDS.join(', ')}`);
85
- }
86
- if (validated.includes(item)) {
87
- throw new Error(`Duplicate agent id in failoverChain: "${item}"`);
88
- }
89
- validated.push(item);
90
- }
91
- return validated;
92
- }
93
- function isKnownAgentId(id) {
94
- return KNOWN_AGENT_IDS.includes(id);
95
- }
96
- /**
97
- * Helper: convert an array of AgentAdapter (typically from an adapter
98
- * registry) to the AdapterAvailability shape this module expects, given
99
- * a readiness probe function. Used by callers who already have the
100
- * adapter instances handy. Caller passes a probe like
101
- * `adapter.checkReadiness().state === 'ready'`.
102
- */
103
- export function toAvailability(adapters, isReady) {
104
- return adapters.map((a) => ({ id: a.id, ready: isReady(a) }));
105
- }
106
- //# sourceMappingURL=failover-policy.js.map