@ai-sdlc/orchestrator 0.6.0 → 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.
- package/dist/admission-composite.d.ts +67 -0
- package/dist/admission-composite.js +158 -0
- package/dist/admission-enrichment.d.ts +142 -0
- package/dist/admission-enrichment.js +331 -0
- package/dist/admission-hc.d.ts +62 -0
- package/dist/admission-hc.js +83 -0
- package/dist/admission-score.d.ts +95 -5
- package/dist/admission-score.js +84 -11
- package/dist/analysis/analyzer.js +3 -2
- package/dist/analysis/convention-detector.d.ts +85 -2
- package/dist/analysis/convention-detector.js +375 -70
- package/dist/analysis/diff-analyzer.d.ts +33 -0
- package/dist/analysis/diff-analyzer.js +122 -0
- package/dist/analysis/hotspot-analyzer.js +3 -1
- package/dist/analysis/index.d.ts +2 -1
- package/dist/analysis/index.js +2 -1
- package/dist/artifacts/index.d.ts +65 -0
- package/dist/artifacts/index.js +142 -0
- package/dist/backlog-adapter.d.ts +121 -0
- package/dist/backlog-adapter.js +438 -0
- package/dist/calibration.d.ts +43 -0
- package/dist/calibration.js +76 -0
- package/dist/cli/commands/agents.d.ts +24 -0
- package/dist/cli/commands/agents.js +66 -1
- package/dist/cli/commands/git-remote.d.ts +49 -0
- package/dist/cli/commands/git-remote.js +91 -0
- package/dist/cli/commands/health.d.ts +4 -0
- package/dist/cli/commands/health.js +13 -1
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +164 -22
- package/dist/cli/commands/mcp-setup.d.ts +31 -0
- package/dist/cli/commands/mcp-setup.js +78 -8
- package/dist/cli/formatters/table.js +15 -2
- package/dist/cli/index.d.ts +14 -1
- package/dist/cli/index.js +81 -20
- package/dist/cli/versions.d.ts +57 -0
- package/dist/cli/versions.js +128 -0
- package/dist/code-area-classifier.d.ts +21 -0
- package/dist/code-area-classifier.js +48 -0
- package/dist/config.d.ts +33 -1
- package/dist/config.js +78 -8
- package/dist/database/adapters/external.d.ts +24 -0
- package/dist/database/adapters/external.js +80 -0
- package/dist/database/adapters/neon.d.ts +41 -0
- package/dist/database/adapters/neon.js +98 -0
- package/dist/database/adapters/pg-snapshot-restore.d.ts +28 -0
- package/dist/database/adapters/pg-snapshot-restore.js +68 -0
- package/dist/database/adapters/sqlite-copy.d.ts +32 -0
- package/dist/database/adapters/sqlite-copy.js +145 -0
- package/dist/database/connection-injection.d.ts +35 -0
- package/dist/database/connection-injection.js +93 -0
- package/dist/database/index.d.ts +12 -0
- package/dist/database/index.js +23 -0
- package/dist/database/registry.d.ts +13 -0
- package/dist/database/registry.js +27 -0
- package/dist/database/topology.d.ts +52 -0
- package/dist/database/topology.js +44 -0
- package/dist/database/types.d.ts +89 -0
- package/dist/database/types.js +26 -0
- package/dist/design-authority.d.ts +40 -0
- package/dist/design-authority.js +71 -0
- package/dist/design-lookahead.d.ts +64 -0
- package/dist/design-lookahead.js +86 -0
- package/dist/design-quality-trend.d.ts +87 -0
- package/dist/design-quality-trend.js +190 -0
- package/dist/design-system-context.d.ts +46 -0
- package/dist/design-system-context.js +80 -0
- package/dist/design-system-correction-loop.d.ts +64 -0
- package/dist/design-system-correction-loop.js +128 -0
- package/dist/design-system-metrics.d.ts +61 -0
- package/dist/design-system-metrics.js +104 -0
- package/dist/design-system-stewardship.d.ts +22 -0
- package/dist/design-system-stewardship.js +85 -0
- package/dist/design-system-validation.d.ts +37 -0
- package/dist/design-system-validation.js +88 -0
- package/dist/dispatch/index.d.ts +4 -0
- package/dist/dispatch/index.js +4 -0
- package/dist/dispatch/merge-gate.d.ts +46 -0
- package/dist/dispatch/merge-gate.js +90 -0
- package/dist/dispatch/requeue.d.ts +57 -0
- package/dist/dispatch/requeue.js +131 -0
- package/dist/dispatch/worker-pool.d.ts +62 -0
- package/dist/dispatch/worker-pool.js +60 -0
- package/dist/execute.d.ts +40 -0
- package/dist/execute.js +185 -15
- package/dist/fix-ci.js +8 -1
- package/dist/fix-review.js +8 -1
- package/dist/harness/adapters/claude-code.d.ts +29 -0
- package/dist/harness/adapters/claude-code.js +191 -0
- package/dist/harness/adapters/codex.d.ts +25 -0
- package/dist/harness/adapters/codex.js +61 -0
- package/dist/harness/independence.d.ts +51 -0
- package/dist/harness/independence.js +67 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +20 -0
- package/dist/harness/registry.d.ts +17 -0
- package/dist/harness/registry.js +31 -0
- package/dist/harness/types.d.ts +123 -0
- package/dist/harness/types.js +8 -0
- package/dist/harness/version-probe.d.ts +14 -0
- package/dist/harness/version-probe.js +123 -0
- package/dist/index.d.ts +30 -4
- package/dist/index.js +35 -2
- package/dist/models/classifier.d.ts +76 -0
- package/dist/models/classifier.js +221 -0
- package/dist/models/index.d.ts +3 -0
- package/dist/models/index.js +3 -0
- package/dist/models/registry.d.ts +97 -0
- package/dist/models/registry.js +173 -0
- package/dist/pillar-breakdown.d.ts +63 -0
- package/dist/pillar-breakdown.js +153 -0
- package/dist/priority.d.ts +5 -0
- package/dist/priority.js +18 -5
- package/dist/review-meta.d.ts +65 -0
- package/dist/review-meta.js +149 -0
- package/dist/review.d.ts +4 -0
- package/dist/review.js +12 -2
- package/dist/runners/claude-code-sdk.d.ts +31 -0
- package/dist/runners/claude-code-sdk.js +238 -0
- package/dist/runners/claude-code.d.ts +3 -0
- package/dist/runners/claude-code.js +30 -66
- package/dist/runners/codex.js +4 -1
- package/dist/runners/copilot.js +4 -1
- package/dist/runners/cursor.js +4 -1
- package/dist/runners/git-utils.d.ts +81 -0
- package/dist/runners/git-utils.js +201 -0
- package/dist/runners/index.d.ts +3 -0
- package/dist/runners/index.js +3 -0
- package/dist/runners/review-agent.d.ts +20 -0
- package/dist/runners/review-agent.js +171 -41
- package/dist/runners/runner-registry.js +10 -0
- package/dist/runners/sdk-review-runner.d.ts +65 -0
- package/dist/runners/sdk-review-runner.js +185 -0
- package/dist/runners/security-triage.d.ts +20 -4
- package/dist/runners/security-triage.js +39 -15
- package/dist/runners/types.d.ts +6 -0
- package/dist/runtime/attestations.d.ts +274 -0
- package/dist/runtime/attestations.js +460 -0
- package/dist/runtime/git-env.d.ts +53 -0
- package/dist/runtime/git-env.js +60 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/parallelism-flag.d.ts +10 -0
- package/dist/runtime/parallelism-flag.js +18 -0
- package/dist/runtime/port-allocator.d.ts +21 -0
- package/dist/runtime/port-allocator.js +66 -0
- package/dist/runtime/worktree-pool.d.ts +86 -0
- package/dist/runtime/worktree-pool.js +204 -0
- package/dist/runtime/worktree.d.ts +25 -0
- package/dist/runtime/worktree.js +111 -0
- package/dist/sa-scoring/auto-calibrate.d.ts +69 -0
- package/dist/sa-scoring/auto-calibrate.js +107 -0
- package/dist/sa-scoring/c1-sa2-computable.d.ts +42 -0
- package/dist/sa-scoring/c1-sa2-computable.js +59 -0
- package/dist/sa-scoring/composite.d.ts +107 -0
- package/dist/sa-scoring/composite.js +139 -0
- package/dist/sa-scoring/depparse-client.d.ts +79 -0
- package/dist/sa-scoring/depparse-client.js +187 -0
- package/dist/sa-scoring/did-compiler.d.ts +122 -0
- package/dist/sa-scoring/did-compiler.js +286 -0
- package/dist/sa-scoring/drift-monitor.d.ts +84 -0
- package/dist/sa-scoring/drift-monitor.js +186 -0
- package/dist/sa-scoring/exemplar-bank.d.ts +78 -0
- package/dist/sa-scoring/exemplar-bank.js +154 -0
- package/dist/sa-scoring/feedback-store.d.ts +100 -0
- package/dist/sa-scoring/feedback-store.js +156 -0
- package/dist/sa-scoring/index.d.ts +71 -0
- package/dist/sa-scoring/index.js +158 -0
- package/dist/sa-scoring/layer1-deterministic.d.ts +115 -0
- package/dist/sa-scoring/layer1-deterministic.js +298 -0
- package/dist/sa-scoring/layer2-structural.d.ts +71 -0
- package/dist/sa-scoring/layer2-structural.js +151 -0
- package/dist/sa-scoring/layer3-llm.d.ts +86 -0
- package/dist/sa-scoring/layer3-llm.js +282 -0
- package/dist/sa-scoring/rescore-orchestrator.d.ts +52 -0
- package/dist/sa-scoring/rescore-orchestrator.js +47 -0
- package/dist/scheduling/burn-down.d.ts +27 -0
- package/dist/scheduling/burn-down.js +43 -0
- package/dist/scheduling/calibration.d.ts +42 -0
- package/dist/scheduling/calibration.js +150 -0
- package/dist/scheduling/index.d.ts +8 -0
- package/dist/scheduling/index.js +8 -0
- package/dist/scheduling/ledger.d.ts +59 -0
- package/dist/scheduling/ledger.js +216 -0
- package/dist/scheduling/off-peak.d.ts +27 -0
- package/dist/scheduling/off-peak.js +112 -0
- package/dist/scheduling/schedule-decision.d.ts +39 -0
- package/dist/scheduling/schedule-decision.js +89 -0
- package/dist/scheduling/tier-analysis.d.ts +47 -0
- package/dist/scheduling/tier-analysis.js +81 -0
- package/dist/scheduling/types.d.ts +140 -0
- package/dist/scheduling/types.js +11 -0
- package/dist/shared.d.ts +13 -0
- package/dist/shared.js +32 -0
- package/dist/state/schema.d.ts +5 -1
- package/dist/state/schema.js +223 -1
- package/dist/state/store.d.ts +55 -1
- package/dist/state/store.js +342 -7
- package/dist/state/types.d.ts +139 -0
- package/dist/types.d.ts +1 -1
- package/dist/validate-agent-output.js +4 -1
- package/dist/watch.js +6 -0
- package/package.json +2 -2
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ClaudeCodeAdapter — wraps Claude Code CLI behind the HarnessAdapter contract.
|
|
3
|
+
* Uses the local `claude` CLI in `-p --output-format stream-json` mode, authenticating
|
|
4
|
+
* via whatever credential the CLI is logged into (Pro/Max subscription or API key).
|
|
5
|
+
*/
|
|
6
|
+
import { spawn } from 'node:child_process';
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { probeVersion } from '../version-probe.js';
|
|
9
|
+
const DEFAULT_AVAILABLE_MODELS = [
|
|
10
|
+
'claude-haiku-4-5-20251001',
|
|
11
|
+
'claude-sonnet-4-6',
|
|
12
|
+
'claude-opus-4-7',
|
|
13
|
+
'claude-opus-4-7[1m]',
|
|
14
|
+
];
|
|
15
|
+
const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
|
|
16
|
+
const HEARTBEAT_INTERVAL_MS = 30_000;
|
|
17
|
+
export class ClaudeCodeAdapter {
|
|
18
|
+
deps;
|
|
19
|
+
name = 'claude-code';
|
|
20
|
+
capabilities = {
|
|
21
|
+
freshContext: true,
|
|
22
|
+
customTools: true,
|
|
23
|
+
streaming: true,
|
|
24
|
+
worktreeAwareCwd: true,
|
|
25
|
+
skills: true,
|
|
26
|
+
artifactWrites: true,
|
|
27
|
+
maxContextTokens: 1_000_000,
|
|
28
|
+
};
|
|
29
|
+
requires = {
|
|
30
|
+
binary: 'claude',
|
|
31
|
+
versionRange: '>=2.0.0',
|
|
32
|
+
versionProbe: {
|
|
33
|
+
args: ['--version'],
|
|
34
|
+
parse: (stdout) => stdout.match(/(\d+\.\d+\.\d+)/)?.[1] ?? '',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
cachedAvailability = null;
|
|
38
|
+
constructor(deps = {}) {
|
|
39
|
+
this.deps = deps;
|
|
40
|
+
}
|
|
41
|
+
async getAccountId() {
|
|
42
|
+
const env = this.deps.env ?? process.env;
|
|
43
|
+
const tokenSources = [env.ANTHROPIC_API_KEY, env.CLAUDE_CODE_API_KEY, env.ANTHROPIC_AUTH_TOKEN];
|
|
44
|
+
for (const source of tokenSources) {
|
|
45
|
+
if (source && source.length > 0) {
|
|
46
|
+
return createHash('sha256').update(`claude-code:${source}`).digest('hex').slice(0, 16);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
async isAvailable() {
|
|
52
|
+
if (this.cachedAvailability)
|
|
53
|
+
return this.cachedAvailability;
|
|
54
|
+
const result = this.deps.probe ? await this.deps.probe() : await probeVersion(this.requires);
|
|
55
|
+
this.cachedAvailability = result;
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
async invoke(input, onEvent) {
|
|
59
|
+
if (this.deps.invoke)
|
|
60
|
+
return this.deps.invoke(input, onEvent);
|
|
61
|
+
return runClaudeCli(input, this.deps.spawn ?? spawn, onEvent);
|
|
62
|
+
}
|
|
63
|
+
async availableModels() {
|
|
64
|
+
return DEFAULT_AVAILABLE_MODELS;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function runClaudeCli(input, spawnFn, onEvent) {
|
|
68
|
+
const timeoutMs = parseDurationMs(input.timeout) ?? DEFAULT_TIMEOUT_MS;
|
|
69
|
+
const args = ['-p', '--output-format', 'stream-json', '--verbose', '--model', input.model];
|
|
70
|
+
return new Promise((resolve) => {
|
|
71
|
+
const startTs = Date.now();
|
|
72
|
+
onEvent?.({ type: 'started', timestamp: new Date().toISOString() });
|
|
73
|
+
let child;
|
|
74
|
+
try {
|
|
75
|
+
child = spawnFn('claude', args, {
|
|
76
|
+
cwd: input.cwd,
|
|
77
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
78
|
+
env: process.env,
|
|
79
|
+
timeout: timeoutMs,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
resolve({
|
|
84
|
+
status: 'unavailable',
|
|
85
|
+
exitCode: -1,
|
|
86
|
+
costUsd: 0,
|
|
87
|
+
inputTokens: 0,
|
|
88
|
+
outputTokens: 0,
|
|
89
|
+
artifactPaths: [],
|
|
90
|
+
errorDetail: `failed to spawn 'claude': ${err.message}`,
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
let resultText = '';
|
|
95
|
+
let inputTokens = 0;
|
|
96
|
+
let outputTokens = 0;
|
|
97
|
+
let costUsd = 0;
|
|
98
|
+
let lineBuf = '';
|
|
99
|
+
const errBuf = [];
|
|
100
|
+
const heartbeat = setInterval(() => {
|
|
101
|
+
onEvent?.({
|
|
102
|
+
type: 'heartbeat',
|
|
103
|
+
timestamp: new Date().toISOString(),
|
|
104
|
+
message: `${Math.round((Date.now() - startTs) / 1000)}s elapsed`,
|
|
105
|
+
});
|
|
106
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
107
|
+
child.stdout?.on('data', (chunk) => {
|
|
108
|
+
lineBuf += chunk.toString('utf-8');
|
|
109
|
+
const lines = lineBuf.split('\n');
|
|
110
|
+
lineBuf = lines.pop() ?? '';
|
|
111
|
+
for (const line of lines) {
|
|
112
|
+
const trimmed = line.trim();
|
|
113
|
+
if (!trimmed)
|
|
114
|
+
continue;
|
|
115
|
+
try {
|
|
116
|
+
const ev = JSON.parse(trimmed);
|
|
117
|
+
if (ev.type === 'result') {
|
|
118
|
+
resultText = ev.result ?? '';
|
|
119
|
+
costUsd = ev.total_cost_usd ?? 0;
|
|
120
|
+
const modelUsage = ev.modelUsage;
|
|
121
|
+
if (modelUsage) {
|
|
122
|
+
const firstModel = Object.keys(modelUsage)[0];
|
|
123
|
+
if (firstModel) {
|
|
124
|
+
const usage = modelUsage[firstModel];
|
|
125
|
+
inputTokens = usage.inputTokens ?? 0;
|
|
126
|
+
outputTokens = usage.outputTokens ?? 0;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// Non-JSON line (claude occasionally emits status text); ignore.
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
child.stderr?.on('data', (chunk) => {
|
|
137
|
+
errBuf.push(chunk);
|
|
138
|
+
});
|
|
139
|
+
child.on('error', (err) => {
|
|
140
|
+
clearInterval(heartbeat);
|
|
141
|
+
resolve({
|
|
142
|
+
status: 'unavailable',
|
|
143
|
+
exitCode: -1,
|
|
144
|
+
costUsd,
|
|
145
|
+
inputTokens,
|
|
146
|
+
outputTokens,
|
|
147
|
+
artifactPaths: [],
|
|
148
|
+
errorDetail: `claude spawn error: ${err.message}`,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
child.on('close', (code, signal) => {
|
|
152
|
+
clearInterval(heartbeat);
|
|
153
|
+
const stderr = Buffer.concat(errBuf).toString('utf-8');
|
|
154
|
+
const status = code === 0
|
|
155
|
+
? 'success'
|
|
156
|
+
: signal === 'SIGTERM' || signal === 'SIGKILL'
|
|
157
|
+
? 'timeout'
|
|
158
|
+
: 'failure';
|
|
159
|
+
onEvent?.({
|
|
160
|
+
type: 'completed',
|
|
161
|
+
timestamp: new Date().toISOString(),
|
|
162
|
+
status,
|
|
163
|
+
});
|
|
164
|
+
resolve({
|
|
165
|
+
status,
|
|
166
|
+
exitCode: code ?? -1,
|
|
167
|
+
costUsd,
|
|
168
|
+
inputTokens,
|
|
169
|
+
outputTokens,
|
|
170
|
+
artifactPaths: [],
|
|
171
|
+
outputText: resultText,
|
|
172
|
+
errorDetail: status === 'success' ? undefined : stderr.slice(-500) || undefined,
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
child.stdin?.write(input.prompt);
|
|
176
|
+
child.stdin?.end();
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function parseDurationMs(duration) {
|
|
180
|
+
if (!duration)
|
|
181
|
+
return null;
|
|
182
|
+
const m = duration.match(/^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/);
|
|
183
|
+
if (!m)
|
|
184
|
+
return null;
|
|
185
|
+
const days = Number.parseInt(m[1] ?? '0', 10);
|
|
186
|
+
const hours = Number.parseInt(m[2] ?? '0', 10);
|
|
187
|
+
const minutes = Number.parseInt(m[3] ?? '0', 10);
|
|
188
|
+
const seconds = Number.parseInt(m[4] ?? '0', 10);
|
|
189
|
+
return (((days * 24 + hours) * 60 + minutes) * 60 + seconds) * 1000;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=claude-code.js.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodexAdapter — wraps OpenAI Codex CLI behind the HarnessAdapter contract.
|
|
3
|
+
* Phase 2.7 ships the adapter shell; the actual end-to-end Codex invocation against a
|
|
4
|
+
* fixture worktree is deferred to Phase 3 dispatcher integration. The adapter's
|
|
5
|
+
* capabilities, version probe, and account-id derivation are wired and tested here.
|
|
6
|
+
*/
|
|
7
|
+
import type { HarnessAdapter, HarnessAvailability, HarnessCapabilities, HarnessEvent, HarnessInput, HarnessName, HarnessRequires, HarnessResult } from '../types.js';
|
|
8
|
+
export interface CodexAdapterDeps {
|
|
9
|
+
env?: NodeJS.ProcessEnv;
|
|
10
|
+
invoke?: (input: HarnessInput, onEvent?: (e: HarnessEvent) => void) => Promise<HarnessResult>;
|
|
11
|
+
probe?: () => Promise<HarnessAvailability>;
|
|
12
|
+
}
|
|
13
|
+
export declare class CodexAdapter implements HarnessAdapter {
|
|
14
|
+
private readonly deps;
|
|
15
|
+
readonly name: HarnessName;
|
|
16
|
+
readonly capabilities: HarnessCapabilities;
|
|
17
|
+
readonly requires: HarnessRequires;
|
|
18
|
+
private cachedAvailability;
|
|
19
|
+
constructor(deps?: CodexAdapterDeps);
|
|
20
|
+
getAccountId(): Promise<string | null>;
|
|
21
|
+
isAvailable(): Promise<HarnessAvailability>;
|
|
22
|
+
invoke(input: HarnessInput, onEvent?: (e: HarnessEvent) => void): Promise<HarnessResult>;
|
|
23
|
+
availableModels(): Promise<string[]>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodexAdapter — wraps OpenAI Codex CLI behind the HarnessAdapter contract.
|
|
3
|
+
* Phase 2.7 ships the adapter shell; the actual end-to-end Codex invocation against a
|
|
4
|
+
* fixture worktree is deferred to Phase 3 dispatcher integration. The adapter's
|
|
5
|
+
* capabilities, version probe, and account-id derivation are wired and tested here.
|
|
6
|
+
*/
|
|
7
|
+
import { createHash } from 'node:crypto';
|
|
8
|
+
import { probeVersion } from '../version-probe.js';
|
|
9
|
+
const DEFAULT_AVAILABLE_MODELS = ['gpt-5', 'gpt-5-mini', 'o3', 'o3-mini'];
|
|
10
|
+
export class CodexAdapter {
|
|
11
|
+
deps;
|
|
12
|
+
name = 'codex';
|
|
13
|
+
capabilities = {
|
|
14
|
+
freshContext: true,
|
|
15
|
+
customTools: false, // Phase 2.7: partial MCP support; track via capability declaration.
|
|
16
|
+
streaming: true,
|
|
17
|
+
worktreeAwareCwd: true,
|
|
18
|
+
skills: false,
|
|
19
|
+
artifactWrites: true,
|
|
20
|
+
maxContextTokens: 200_000,
|
|
21
|
+
};
|
|
22
|
+
requires = {
|
|
23
|
+
binary: 'codex',
|
|
24
|
+
versionRange: '>=0.1.0',
|
|
25
|
+
versionProbe: {
|
|
26
|
+
args: ['--version'],
|
|
27
|
+
parse: (stdout) => stdout.match(/(\d+\.\d+\.\d+)/)?.[1] ?? '',
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
cachedAvailability = null;
|
|
31
|
+
constructor(deps = {}) {
|
|
32
|
+
this.deps = deps;
|
|
33
|
+
}
|
|
34
|
+
async getAccountId() {
|
|
35
|
+
const env = this.deps.env ?? process.env;
|
|
36
|
+
const tokenSources = [env.OPENAI_API_KEY, env.CODEX_API_KEY];
|
|
37
|
+
for (const source of tokenSources) {
|
|
38
|
+
if (source && source.length > 0) {
|
|
39
|
+
return createHash('sha256').update(`codex:${source}`).digest('hex').slice(0, 16);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
async isAvailable() {
|
|
45
|
+
if (this.cachedAvailability)
|
|
46
|
+
return this.cachedAvailability;
|
|
47
|
+
const result = this.deps.probe ? await this.deps.probe() : await probeVersion(this.requires);
|
|
48
|
+
this.cachedAvailability = result;
|
|
49
|
+
return result;
|
|
50
|
+
}
|
|
51
|
+
async invoke(input, onEvent) {
|
|
52
|
+
if (this.deps.invoke)
|
|
53
|
+
return this.deps.invoke(input, onEvent);
|
|
54
|
+
throw new Error('CodexAdapter.invoke is not wired into dispatch yet (Phase 3 work). ' +
|
|
55
|
+
'Tests should inject deps.invoke; production code should not call this method directly until Phase 3.');
|
|
56
|
+
}
|
|
57
|
+
async availableModels() {
|
|
58
|
+
return DEFAULT_AVAILABLE_MODELS;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness independence enforcement per RFC §13.10 (Q8 resolution).
|
|
3
|
+
*
|
|
4
|
+
* When a stage declares requiresIndependentHarnessFrom: [stageA, stageB], the orchestrator
|
|
5
|
+
* filters that stage's harness chain to exclude harnesses that actually ran the named
|
|
6
|
+
* upstream stages (which may differ from declared if a fallback was used).
|
|
7
|
+
*
|
|
8
|
+
* This is the load-bearing safety property for cross-harness review (RFC §13.6) — without
|
|
9
|
+
* it, fallback can silently collapse review onto the same harness as the implementer.
|
|
10
|
+
*/
|
|
11
|
+
import type { HarnessName } from './types.js';
|
|
12
|
+
export interface UpstreamRun {
|
|
13
|
+
stage: string;
|
|
14
|
+
resolvedHarness: HarnessName;
|
|
15
|
+
}
|
|
16
|
+
export interface IndependenceResult {
|
|
17
|
+
effectiveChain: HarnessName[];
|
|
18
|
+
removed: HarnessName[];
|
|
19
|
+
forbidden: HarnessName[];
|
|
20
|
+
/** True when the filtered chain is empty — caller must apply onFailure. */
|
|
21
|
+
violated: boolean;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Filter a stage's candidate harness chain to preserve independence from named upstreams.
|
|
25
|
+
*/
|
|
26
|
+
export declare function enforceIndependence(candidateChain: HarnessName[], requiresIndependentFrom: string[], upstreamRuns: UpstreamRun[]): IndependenceResult;
|
|
27
|
+
/**
|
|
28
|
+
* Validate that requiresIndependentHarnessFrom declarations form a DAG.
|
|
29
|
+
* Returns the set of cyclic edges (empty array means valid).
|
|
30
|
+
*
|
|
31
|
+
* Each stage is identified by name; references must point at stages that appear *earlier*
|
|
32
|
+
* in the pipeline order. References to downstream or self → cycle.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateIndependenceGraph(stages: ReadonlyArray<{
|
|
35
|
+
name: string;
|
|
36
|
+
requiresIndependentHarnessFrom?: string[];
|
|
37
|
+
}>): Array<{
|
|
38
|
+
stage: string;
|
|
39
|
+
references: string;
|
|
40
|
+
}>;
|
|
41
|
+
export declare class CyclicIndependenceConstraintError extends Error {
|
|
42
|
+
readonly cycles: Array<{
|
|
43
|
+
stage: string;
|
|
44
|
+
references: string;
|
|
45
|
+
}>;
|
|
46
|
+
constructor(cycles: Array<{
|
|
47
|
+
stage: string;
|
|
48
|
+
references: string;
|
|
49
|
+
}>);
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=independence.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness independence enforcement per RFC §13.10 (Q8 resolution).
|
|
3
|
+
*
|
|
4
|
+
* When a stage declares requiresIndependentHarnessFrom: [stageA, stageB], the orchestrator
|
|
5
|
+
* filters that stage's harness chain to exclude harnesses that actually ran the named
|
|
6
|
+
* upstream stages (which may differ from declared if a fallback was used).
|
|
7
|
+
*
|
|
8
|
+
* This is the load-bearing safety property for cross-harness review (RFC §13.6) — without
|
|
9
|
+
* it, fallback can silently collapse review onto the same harness as the implementer.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Filter a stage's candidate harness chain to preserve independence from named upstreams.
|
|
13
|
+
*/
|
|
14
|
+
export function enforceIndependence(candidateChain, requiresIndependentFrom, upstreamRuns) {
|
|
15
|
+
const upstreamMap = new Map(upstreamRuns.map((r) => [r.stage, r.resolvedHarness]));
|
|
16
|
+
const forbidden = new Set();
|
|
17
|
+
for (const stage of requiresIndependentFrom) {
|
|
18
|
+
const h = upstreamMap.get(stage);
|
|
19
|
+
if (h)
|
|
20
|
+
forbidden.add(h);
|
|
21
|
+
}
|
|
22
|
+
const effectiveChain = candidateChain.filter((h) => !forbidden.has(h));
|
|
23
|
+
const removed = candidateChain.filter((h) => forbidden.has(h));
|
|
24
|
+
return {
|
|
25
|
+
effectiveChain,
|
|
26
|
+
removed,
|
|
27
|
+
forbidden: Array.from(forbidden),
|
|
28
|
+
violated: effectiveChain.length === 0,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Validate that requiresIndependentHarnessFrom declarations form a DAG.
|
|
33
|
+
* Returns the set of cyclic edges (empty array means valid).
|
|
34
|
+
*
|
|
35
|
+
* Each stage is identified by name; references must point at stages that appear *earlier*
|
|
36
|
+
* in the pipeline order. References to downstream or self → cycle.
|
|
37
|
+
*/
|
|
38
|
+
export function validateIndependenceGraph(stages) {
|
|
39
|
+
const stageOrder = new Map(stages.map((s, i) => [s.name, i]));
|
|
40
|
+
const cycles = [];
|
|
41
|
+
for (const [i, s] of stages.entries()) {
|
|
42
|
+
const refs = s.requiresIndependentHarnessFrom ?? [];
|
|
43
|
+
for (const ref of refs) {
|
|
44
|
+
const refIndex = stageOrder.get(ref);
|
|
45
|
+
if (refIndex === undefined) {
|
|
46
|
+
cycles.push({ stage: s.name, references: `unknown stage '${ref}'` });
|
|
47
|
+
}
|
|
48
|
+
else if (refIndex >= i) {
|
|
49
|
+
cycles.push({
|
|
50
|
+
stage: s.name,
|
|
51
|
+
references: `'${ref}' is not strictly upstream (cycle / self-reference)`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return cycles;
|
|
57
|
+
}
|
|
58
|
+
export class CyclicIndependenceConstraintError extends Error {
|
|
59
|
+
cycles;
|
|
60
|
+
constructor(cycles) {
|
|
61
|
+
const summary = cycles.map((c) => `${c.stage} → ${c.references}`).join('; ');
|
|
62
|
+
super(`requiresIndependentHarnessFrom forms invalid graph: ${summary}`);
|
|
63
|
+
this.cycles = cycles;
|
|
64
|
+
this.name = 'CyclicIndependenceConstraintError';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=independence.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { type HarnessAdapter, type HarnessAvailability, type HarnessCapabilities, type HarnessEvent, type HarnessInput, type HarnessName, type HarnessRequires, type HarnessResult, type HarnessResultStatus, type ToolDefinition, } from './types.js';
|
|
2
|
+
export { HarnessRegistry, UnknownHarnessError } from './registry.js';
|
|
3
|
+
export { probeVersion, matchesRange } from './version-probe.js';
|
|
4
|
+
export { enforceIndependence, validateIndependenceGraph, CyclicIndependenceConstraintError, type IndependenceResult, type UpstreamRun, } from './independence.js';
|
|
5
|
+
export { ClaudeCodeAdapter, type ClaudeCodeAdapterDeps } from './adapters/claude-code.js';
|
|
6
|
+
export { CodexAdapter, type CodexAdapterDeps } from './adapters/codex.js';
|
|
7
|
+
import { HarnessRegistry } from './registry.js';
|
|
8
|
+
/**
|
|
9
|
+
* Create a registry pre-populated with the v1 adapters (claude-code, codex).
|
|
10
|
+
* Future adapters (gemini-cli, opencode, aider, generic-api) register themselves
|
|
11
|
+
* the same way once their adapter implementations land.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createDefaultHarnessRegistry(): HarnessRegistry;
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export { HarnessRegistry, UnknownHarnessError } from './registry.js';
|
|
2
|
+
export { probeVersion, matchesRange } from './version-probe.js';
|
|
3
|
+
export { enforceIndependence, validateIndependenceGraph, CyclicIndependenceConstraintError, } from './independence.js';
|
|
4
|
+
export { ClaudeCodeAdapter } from './adapters/claude-code.js';
|
|
5
|
+
export { CodexAdapter } from './adapters/codex.js';
|
|
6
|
+
import { HarnessRegistry } from './registry.js';
|
|
7
|
+
import { ClaudeCodeAdapter } from './adapters/claude-code.js';
|
|
8
|
+
import { CodexAdapter } from './adapters/codex.js';
|
|
9
|
+
/**
|
|
10
|
+
* Create a registry pre-populated with the v1 adapters (claude-code, codex).
|
|
11
|
+
* Future adapters (gemini-cli, opencode, aider, generic-api) register themselves
|
|
12
|
+
* the same way once their adapter implementations land.
|
|
13
|
+
*/
|
|
14
|
+
export function createDefaultHarnessRegistry() {
|
|
15
|
+
const reg = new HarnessRegistry();
|
|
16
|
+
reg.register(new ClaudeCodeAdapter());
|
|
17
|
+
reg.register(new CodexAdapter());
|
|
18
|
+
return reg;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness adapter registry per RFC §13.2. Resolves harness names to adapter implementations.
|
|
3
|
+
* Pipeline-load fails with UnknownHarness when a stage names an unregistered harness.
|
|
4
|
+
*/
|
|
5
|
+
import type { HarnessAdapter, HarnessName } from './types.js';
|
|
6
|
+
export declare class UnknownHarnessError extends Error {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
constructor(name: string);
|
|
9
|
+
}
|
|
10
|
+
export declare class HarnessRegistry {
|
|
11
|
+
private readonly adapters;
|
|
12
|
+
register(adapter: HarnessAdapter): void;
|
|
13
|
+
get(name: string): HarnessAdapter;
|
|
14
|
+
has(name: string): boolean;
|
|
15
|
+
list(): HarnessName[];
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness adapter registry per RFC §13.2. Resolves harness names to adapter implementations.
|
|
3
|
+
* Pipeline-load fails with UnknownHarness when a stage names an unregistered harness.
|
|
4
|
+
*/
|
|
5
|
+
export class UnknownHarnessError extends Error {
|
|
6
|
+
name;
|
|
7
|
+
constructor(name) {
|
|
8
|
+
super(`Unknown harness: ${name}`);
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.name = 'UnknownHarnessError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class HarnessRegistry {
|
|
14
|
+
adapters = new Map();
|
|
15
|
+
register(adapter) {
|
|
16
|
+
this.adapters.set(adapter.name, adapter);
|
|
17
|
+
}
|
|
18
|
+
get(name) {
|
|
19
|
+
const a = this.adapters.get(name);
|
|
20
|
+
if (!a)
|
|
21
|
+
throw new UnknownHarnessError(name);
|
|
22
|
+
return a;
|
|
23
|
+
}
|
|
24
|
+
has(name) {
|
|
25
|
+
return this.adapters.has(name);
|
|
26
|
+
}
|
|
27
|
+
list() {
|
|
28
|
+
return Array.from(this.adapters.keys());
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HarnessAdapter framework per RFC-0010 §13. Decouples the orchestrator from any single
|
|
3
|
+
* coding-agent runtime. Each adapter declares static capabilities + binary requirements;
|
|
4
|
+
* the orchestrator validates at pipeline-load and dispatches at runtime, with the
|
|
5
|
+
* fallback chain handling availability failures.
|
|
6
|
+
*/
|
|
7
|
+
export type HarnessName = 'claude-code' | 'codex' | 'gemini-cli' | 'opencode' | 'aider' | 'generic-api';
|
|
8
|
+
export interface HarnessCapabilities {
|
|
9
|
+
/** Can spawn a clean session per invocation (no leaked context). */
|
|
10
|
+
freshContext: boolean;
|
|
11
|
+
/** Supports MCP tools / custom tool definitions. */
|
|
12
|
+
customTools: boolean;
|
|
13
|
+
/** Emits incremental output (for heartbeats). */
|
|
14
|
+
streaming: boolean;
|
|
15
|
+
/** Honors a per-invocation cwd. */
|
|
16
|
+
worktreeAwareCwd: boolean;
|
|
17
|
+
/** Supports loadable skills / system prompts. */
|
|
18
|
+
skills: boolean;
|
|
19
|
+
/** Can write files to $ARTIFACTS_DIR mid-stage. */
|
|
20
|
+
artifactWrites: boolean;
|
|
21
|
+
/** Largest context window across this harness's models. */
|
|
22
|
+
maxContextTokens: number;
|
|
23
|
+
}
|
|
24
|
+
export interface HarnessRequires {
|
|
25
|
+
/** Executable name resolved against PATH. */
|
|
26
|
+
binary: string;
|
|
27
|
+
/** semver range. Open-ended upper bound by default (RFC §13.8). */
|
|
28
|
+
versionRange: string;
|
|
29
|
+
/** Probe configuration: how to extract the installed version. */
|
|
30
|
+
versionProbe: {
|
|
31
|
+
args: string[];
|
|
32
|
+
parse: (stdout: string) => string;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export interface HarnessAvailability {
|
|
36
|
+
available: boolean;
|
|
37
|
+
reason?: 'binary-missing' | 'version-out-of-range' | 'probe-failed' | 'health-check-failed';
|
|
38
|
+
detail?: string;
|
|
39
|
+
installedVersion?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface HarnessInput {
|
|
42
|
+
prompt: string;
|
|
43
|
+
/** Worktree path (cwd for the agent invocation). */
|
|
44
|
+
cwd: string;
|
|
45
|
+
/** Resolved physical model ID, not alias. */
|
|
46
|
+
model: string;
|
|
47
|
+
/** Allocated dev-server port (optional). */
|
|
48
|
+
port?: number;
|
|
49
|
+
/** $ARTIFACTS_DIR/<issue-id>/ for the run. */
|
|
50
|
+
artifactsDir: string;
|
|
51
|
+
/** Optional MCP tools / tool definitions. */
|
|
52
|
+
tools?: ToolDefinition[];
|
|
53
|
+
/** Optional skill names to load. */
|
|
54
|
+
skills?: string[];
|
|
55
|
+
/** ISO 8601 duration. */
|
|
56
|
+
timeout?: string;
|
|
57
|
+
/** Per-stage cost ceiling. */
|
|
58
|
+
maxBudgetUsd?: number;
|
|
59
|
+
}
|
|
60
|
+
export interface ToolDefinition {
|
|
61
|
+
name: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
inputSchema?: Record<string, unknown>;
|
|
64
|
+
}
|
|
65
|
+
export type HarnessResultStatus = 'success' | 'failure' | 'timeout' | 'budget-exceeded' | 'unavailable';
|
|
66
|
+
export interface HarnessResult {
|
|
67
|
+
status: HarnessResultStatus;
|
|
68
|
+
exitCode: number;
|
|
69
|
+
costUsd: number;
|
|
70
|
+
inputTokens: number;
|
|
71
|
+
outputTokens: number;
|
|
72
|
+
/** Files the harness wrote to $ARTIFACTS_DIR. */
|
|
73
|
+
artifactPaths: string[];
|
|
74
|
+
/**
|
|
75
|
+
* Final assistant text response, when the stage produced one (read-only stages like
|
|
76
|
+
* triage/review return their verdict here; code-generation stages typically write to
|
|
77
|
+
* artifacts and leave this empty or set it to a brief summary).
|
|
78
|
+
*/
|
|
79
|
+
outputText?: string;
|
|
80
|
+
errorDetail?: string;
|
|
81
|
+
}
|
|
82
|
+
export type HarnessEvent = {
|
|
83
|
+
type: 'started';
|
|
84
|
+
timestamp: string;
|
|
85
|
+
} | {
|
|
86
|
+
type: 'heartbeat';
|
|
87
|
+
timestamp: string;
|
|
88
|
+
message?: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'progress';
|
|
91
|
+
timestamp: string;
|
|
92
|
+
tokensConsumed: number;
|
|
93
|
+
} | {
|
|
94
|
+
type: 'completed';
|
|
95
|
+
timestamp: string;
|
|
96
|
+
status: HarnessResultStatus;
|
|
97
|
+
};
|
|
98
|
+
export interface HarnessAdapter {
|
|
99
|
+
readonly name: HarnessName;
|
|
100
|
+
readonly capabilities: HarnessCapabilities;
|
|
101
|
+
readonly requires: HarnessRequires;
|
|
102
|
+
/**
|
|
103
|
+
* Stable identifier for the credential / account in scope. Used as the SubscriptionLedger
|
|
104
|
+
* key per RFC §14.12. MUST be a one-way derivation (e.g., SHA-256 of the API key).
|
|
105
|
+
* Returns null when the harness cannot derive an account identity (e.g., generic-api
|
|
106
|
+
* with no auth scheme).
|
|
107
|
+
*/
|
|
108
|
+
getAccountId(): Promise<string | null>;
|
|
109
|
+
/**
|
|
110
|
+
* Cheap liveness probe used at pipeline-load and by the fallback chain. Combines binary
|
|
111
|
+
* presence + version-range check + adapter-specific health. May be cached for the
|
|
112
|
+
* orchestrator's lifetime; operator restart picks up newly-installed binaries.
|
|
113
|
+
*/
|
|
114
|
+
isAvailable(): Promise<HarnessAvailability>;
|
|
115
|
+
/**
|
|
116
|
+
* Execute one stage end-to-end. Streams progress via onEvent. Adapters MUST validate
|
|
117
|
+
* any schema-conformant artifact they produce per RFC §13.9.
|
|
118
|
+
*/
|
|
119
|
+
invoke(input: HarnessInput, onEvent?: (e: HarnessEvent) => void): Promise<HarnessResult>;
|
|
120
|
+
/** List models the harness can drive (after env-var introspection). */
|
|
121
|
+
availableModels(): Promise<string[]>;
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HarnessAdapter framework per RFC-0010 §13. Decouples the orchestrator from any single
|
|
3
|
+
* coding-agent runtime. Each adapter declares static capabilities + binary requirements;
|
|
4
|
+
* the orchestrator validates at pipeline-load and dispatches at runtime, with the
|
|
5
|
+
* fallback chain handling availability failures.
|
|
6
|
+
*/
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { HarnessAvailability, HarnessRequires } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Run a harness's version probe and compare against its declared semver range.
|
|
4
|
+
* Per RFC §13.8: parse failures fall through to "available" (warning only) so undocumented
|
|
5
|
+
* vendor changes to --version output don't break every pipeline.
|
|
6
|
+
*/
|
|
7
|
+
export declare function probeVersion(requires: HarnessRequires): Promise<HarnessAvailability>;
|
|
8
|
+
/**
|
|
9
|
+
* Minimal semver-range matcher supporting `>=X.Y.Z` and `>=X.Y.Z <A.B.C`.
|
|
10
|
+
* v1 ships only the cases the RFC's default policy uses (open-ended upper bound by
|
|
11
|
+
* default; explicit upper bound only when a known-incompatible upstream version exists).
|
|
12
|
+
*/
|
|
13
|
+
export declare function matchesRange(version: string, range: string): boolean;
|
|
14
|
+
//# sourceMappingURL=version-probe.d.ts.map
|