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.
- package/README.md +35 -193
- package/dist/application/use-cases/check-claimed-tests.js +81 -0
- package/dist/application/use-cases/detect-test-weakening.js +237 -0
- package/dist/application/use-cases/rerun-tests.js +110 -7
- package/dist/index.js +38 -1212
- package/dist/infrastructure/orchestration/artifacts-extractor.js +2 -323
- package/dist/presentation/cli/commands/check-command.js +178 -23
- package/dist/presentation/cli/commands/help-command.js +21 -285
- package/dist/ui.js +5 -236
- package/dist/utils/version.js +0 -6
- package/package.json +9 -38
- package/dist/application/services/verdict-service.js +0 -154
- package/dist/application/use-cases/assess-cross-vendor.js +0 -126
- package/dist/application/use-cases/build-cross-vendor-verdict.js +0 -118
- package/dist/application/use-cases/domain-trust.js +0 -110
- package/dist/application/use-cases/export-aat.js +0 -107
- package/dist/application/use-cases/generate-acceptance-criteria.js +0 -223
- package/dist/application/use-cases/generate-final-report.js +0 -259
- package/dist/application/use-cases/generate-narration.js +0 -130
- package/dist/application/use-cases/generate-thread-title.js +0 -181
- package/dist/application/use-cases/narrator-prompt-template.js +0 -158
- package/dist/application/use-cases/plan-auto-review.js +0 -57
- package/dist/application/use-cases/reconcile-audit.js +0 -100
- package/dist/application/use-cases/search-sessions.js +0 -70
- package/dist/application/use-cases/should-auto-review.js +0 -71
- package/dist/application/use-cases/thread-export.js +0 -245
- package/dist/application/use-cases/verify-claim.js +0 -456
- package/dist/application/use-cases/write-pass.js +0 -277
- package/dist/domain/audit.js +0 -11
- package/dist/domain/services/codebase-detector.js +0 -85
- package/dist/domain/task.js +0 -91
- package/dist/error-handler.js +0 -139
- package/dist/infrastructure/completion/bash-completion.js +0 -29
- package/dist/infrastructure/completion/fish-completion.js +0 -17
- package/dist/infrastructure/completion/zsh-completion.js +0 -25
- package/dist/infrastructure/config/config-manager.js +0 -174
- package/dist/infrastructure/config/product-context.js +0 -49
- package/dist/infrastructure/context/adapters/drawio-source.js +0 -157
- package/dist/infrastructure/context/adapters/figma-source.js +0 -198
- package/dist/infrastructure/context/adapters/github-issue-source.js +0 -160
- package/dist/infrastructure/context/adapters/linear-source.js +0 -190
- package/dist/infrastructure/context/context-resolver.js +0 -144
- package/dist/infrastructure/context/context-source.js +0 -114
- package/dist/infrastructure/context/github-api.js +0 -110
- package/dist/infrastructure/context/github-device-flow.js +0 -65
- package/dist/infrastructure/context/oauth-flow.js +0 -207
- package/dist/infrastructure/context/oauth-store.js +0 -122
- package/dist/infrastructure/context/registry-bootstrap.js +0 -95
- package/dist/infrastructure/context/write-pass-resolver.js +0 -106
- package/dist/infrastructure/cost/budget-config.js +0 -114
- package/dist/infrastructure/cost/cost-router.js +0 -97
- package/dist/infrastructure/cost/spend-aggregator.js +0 -222
- package/dist/infrastructure/cost/team-ledger.js +0 -355
- package/dist/infrastructure/discovery/discovery-log.js +0 -85
- package/dist/infrastructure/hooks/audit-tip-signature.js +0 -336
- package/dist/infrastructure/hooks/claude-sdk-hook.js +0 -1018
- package/dist/infrastructure/hooks/hook-mode.js +0 -87
- package/dist/infrastructure/hooks/mirror-tool-call-to-audit.js +0 -49
- package/dist/infrastructure/hooks/pre-prompt-scan.js +0 -122
- package/dist/infrastructure/hooks/session-analyzer.js +0 -177
- package/dist/infrastructure/observability/execution-tracer.js +0 -227
- package/dist/infrastructure/orchestration/agent-adapter.js +0 -13
- package/dist/infrastructure/orchestration/agent-installer.js +0 -101
- package/dist/infrastructure/orchestration/agents-config-seed.js +0 -68
- package/dist/infrastructure/orchestration/aider-adapter.js +0 -611
- package/dist/infrastructure/orchestration/cassette-recorder.js +0 -120
- package/dist/infrastructure/orchestration/claude-code-adapter.js +0 -592
- package/dist/infrastructure/orchestration/codex-adapter.js +0 -574
- package/dist/infrastructure/orchestration/conductor-adapter.js +0 -416
- package/dist/infrastructure/orchestration/context-handoff.js +0 -162
- package/dist/infrastructure/orchestration/control/codex-control-surface.js +0 -99
- package/dist/infrastructure/orchestration/control/control-surface.js +0 -15
- package/dist/infrastructure/orchestration/detect-ollama.js +0 -75
- package/dist/infrastructure/orchestration/failover-classifier.js +0 -151
- package/dist/infrastructure/orchestration/failover-config.js +0 -67
- package/dist/infrastructure/orchestration/failover-policy.js +0 -106
- package/dist/infrastructure/orchestration/gemini-adapter.js +0 -663
- package/dist/infrastructure/orchestration/generic-cli-adapter.js +0 -689
- package/dist/infrastructure/orchestration/graphify-context.js +0 -209
- package/dist/infrastructure/orchestration/memory-injector.js +0 -380
- package/dist/infrastructure/orchestration/mocks/mock-agent-repl.js +0 -388
- package/dist/infrastructure/orchestration/mocks/mock-codex-app-server.js +0 -73
- package/dist/infrastructure/orchestration/mocks/mock-synthesis.js +0 -83
- package/dist/infrastructure/orchestration/mocks/resolve-agent-spawn.js +0 -37
- package/dist/infrastructure/orchestration/opencode-adapter.js +0 -580
- package/dist/infrastructure/orchestration/progress-aggregator.js +0 -906
- package/dist/infrastructure/orchestration/readiness.js +0 -103
- package/dist/infrastructure/orchestration/session-manager.js +0 -1590
- package/dist/infrastructure/orchestration/session-persistence.js +0 -394
- package/dist/infrastructure/orchestration/thread-key-store.js +0 -127
- package/dist/infrastructure/orchestration/write-pass-store.js +0 -38
- package/dist/infrastructure/protocols/cloud-sync.js +0 -207
- package/dist/infrastructure/protocols/mcp-helpers.js +0 -25
- package/dist/infrastructure/protocols/mcp-server-impl.js +0 -3935
- package/dist/infrastructure/protocols/mcp-tools.js +0 -401
- package/dist/infrastructure/protocols/probe-port.js +0 -36
- package/dist/infrastructure/protocols/thread-phase.js +0 -105
- package/dist/infrastructure/protocols/ui/app-script-artifacts.js +0 -121
- package/dist/infrastructure/protocols/ui/app-script-audit-footer.js +0 -44
- package/dist/infrastructure/protocols/ui/app-script-audit-verify.js +0 -56
- package/dist/infrastructure/protocols/ui/app-script-composer-cost.js +0 -588
- package/dist/infrastructure/protocols/ui/app-script-context-sources.js +0 -168
- package/dist/infrastructure/protocols/ui/app-script-conversation.js +0 -67
- package/dist/infrastructure/protocols/ui/app-script-drawer.js +0 -115
- package/dist/infrastructure/protocols/ui/app-script-failover.js +0 -133
- package/dist/infrastructure/protocols/ui/app-script-github.js +0 -113
- package/dist/infrastructure/protocols/ui/app-script-observer.js +0 -104
- package/dist/infrastructure/protocols/ui/app-script-onboarding.js +0 -80
- package/dist/infrastructure/protocols/ui/app-script-overlay.js +0 -203
- package/dist/infrastructure/protocols/ui/app-script-recovery.js +0 -136
- package/dist/infrastructure/protocols/ui/app-script-render.js +0 -166
- package/dist/infrastructure/protocols/ui/app-script-share.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-sidebar.js +0 -194
- package/dist/infrastructure/protocols/ui/app-script-tour.js +0 -78
- package/dist/infrastructure/protocols/ui/app-script-wpass.js +0 -312
- package/dist/infrastructure/protocols/ui/app-script.js +0 -2998
- package/dist/infrastructure/protocols/ui/font-faces.js +0 -7
- package/dist/infrastructure/protocols/ui/styles.js +0 -2147
- package/dist/infrastructure/protocols/ui/tokens.js +0 -96
- package/dist/infrastructure/protocols/ui-page-v2.js +0 -320
- package/dist/infrastructure/protocols/verify-page.js +0 -1677
- package/dist/infrastructure/security/file-collection.js +0 -118
- package/dist/infrastructure/security/fix-code.js +0 -144
- package/dist/infrastructure/security/fix-secret.js +0 -89
- package/dist/infrastructure/security/permissions-config.js +0 -235
- package/dist/infrastructure/security/scan-types.js +0 -10
- package/dist/infrastructure/security/ship-scan.js +0 -195
- package/dist/infrastructure/security/trust-scorer.js +0 -724
- package/dist/infrastructure/security/vibe-checks.js +0 -99
- package/dist/infrastructure/storage/atomic-write.js +0 -86
- package/dist/infrastructure/storage/cross-tool-agents.js +0 -268
- package/dist/infrastructure/storage/project-thread-migration.js +0 -237
- package/dist/infrastructure/storage/project-thread-mirror.js +0 -671
- package/dist/infrastructure/storage/project-thread-reader.js +0 -409
- package/dist/infrastructure/storage/project-thread-title-backfill.js +0 -214
- package/dist/monitoring.js +0 -212
- package/dist/presentation/cli/commands/audit-command.js +0 -201
- package/dist/presentation/cli/commands/budget-command.js +0 -79
- package/dist/presentation/cli/commands/config-command.js +0 -125
- package/dist/presentation/cli/commands/context-source-command.js +0 -217
- package/dist/presentation/cli/commands/hook-command.js +0 -265
- package/dist/presentation/cli/commands/init-command.js +0 -590
- package/dist/presentation/cli/commands/memory-command.js +0 -1031
- package/dist/presentation/cli/commands/narrator-command.js +0 -101
- package/dist/presentation/cli/commands/permission-command.js +0 -105
- package/dist/presentation/cli/commands/protocols-builder.js +0 -254
- package/dist/presentation/cli/commands/protocols-command.js +0 -84
- package/dist/presentation/cli/commands/repo-map-builder.js +0 -247
- package/dist/presentation/cli/commands/report-command.js +0 -403
- package/dist/presentation/cli/commands/scan-command.js +0 -52
- package/dist/presentation/cli/commands/scan-tool-command.js +0 -110
- package/dist/presentation/cli/commands/search-command.js +0 -51
- package/dist/presentation/cli/commands/setup-command.js +0 -330
- package/dist/presentation/cli/commands/status-command.js +0 -614
- package/dist/presentation/cli/commands/team-command.js +0 -256
- package/dist/presentation/cli/commands/thread-command.js +0 -370
- package/dist/presentation/cli/commands/trust-command.js +0 -328
- package/dist/presentation/cli/commands/verify-command.js +0 -298
- package/dist/presentation/cli/error-display.js +0 -328
- package/dist/presentation/cli/repl.js +0 -805
- package/dist/presentation/cli/task-deliverable.js +0 -153
- package/dist/testing/cassette.js +0 -125
- 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
|