coterie 0.1.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/README.md +137 -0
- package/dist/adapters/base.d.ts +56 -0
- package/dist/adapters/base.d.ts.map +1 -0
- package/dist/adapters/base.js +176 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/claudeCode.d.ts +11 -0
- package/dist/adapters/claudeCode.d.ts.map +1 -0
- package/dist/adapters/claudeCode.js +65 -0
- package/dist/adapters/claudeCode.js.map +1 -0
- package/dist/adapters/codex.d.ts +10 -0
- package/dist/adapters/codex.d.ts.map +1 -0
- package/dist/adapters/codex.js +51 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/cursor.d.ts +16 -0
- package/dist/adapters/cursor.d.ts.map +1 -0
- package/dist/adapters/cursor.js +47 -0
- package/dist/adapters/cursor.js.map +1 -0
- package/dist/adapters/fake.d.ts +19 -0
- package/dist/adapters/fake.d.ts.map +1 -0
- package/dist/adapters/fake.js +41 -0
- package/dist/adapters/fake.js.map +1 -0
- package/dist/adapters/index.d.ts +11 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +11 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/stream.d.ts +6 -0
- package/dist/adapters/stream.d.ts.map +1 -0
- package/dist/adapters/stream.js +20 -0
- package/dist/adapters/stream.js.map +1 -0
- package/dist/chat/configs.d.ts +18 -0
- package/dist/chat/configs.d.ts.map +1 -0
- package/dist/chat/configs.js +71 -0
- package/dist/chat/configs.js.map +1 -0
- package/dist/chat/doctor.d.ts +16 -0
- package/dist/chat/doctor.d.ts.map +1 -0
- package/dist/chat/doctor.js +43 -0
- package/dist/chat/doctor.js.map +1 -0
- package/dist/chat/finalizer.d.ts +25 -0
- package/dist/chat/finalizer.d.ts.map +1 -0
- package/dist/chat/finalizer.js +52 -0
- package/dist/chat/finalizer.js.map +1 -0
- package/dist/chat/preflight.d.ts +37 -0
- package/dist/chat/preflight.d.ts.map +1 -0
- package/dist/chat/preflight.js +115 -0
- package/dist/chat/preflight.js.map +1 -0
- package/dist/chat/render.d.ts +21 -0
- package/dist/chat/render.d.ts.map +1 -0
- package/dist/chat/render.js +113 -0
- package/dist/chat/render.js.map +1 -0
- package/dist/chat/repl.d.ts +9 -0
- package/dist/chat/repl.d.ts.map +1 -0
- package/dist/chat/repl.js +275 -0
- package/dist/chat/repl.js.map +1 -0
- package/dist/chat/trace.d.ts +29 -0
- package/dist/chat/trace.d.ts.map +1 -0
- package/dist/chat/trace.js +132 -0
- package/dist/chat/trace.js.map +1 -0
- package/dist/chat/transcript.d.ts +15 -0
- package/dist/chat/transcript.d.ts.map +1 -0
- package/dist/chat/transcript.js +39 -0
- package/dist/chat/transcript.js.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +134 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +52 -0
- package/dist/config.js.map +1 -0
- package/dist/core/annotation.d.ts +22 -0
- package/dist/core/annotation.d.ts.map +1 -0
- package/dist/core/annotation.js +45 -0
- package/dist/core/annotation.js.map +1 -0
- package/dist/core/compile.d.ts +3 -0
- package/dist/core/compile.d.ts.map +1 -0
- package/dist/core/compile.js +9 -0
- package/dist/core/compile.js.map +1 -0
- package/dist/core/executor.d.ts +32 -0
- package/dist/core/executor.d.ts.map +1 -0
- package/dist/core/executor.js +73 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/json.d.ts +9 -0
- package/dist/core/json.d.ts.map +1 -0
- package/dist/core/json.js +49 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/llm/base.d.ts +9 -0
- package/dist/core/llm/base.d.ts.map +1 -0
- package/dist/core/llm/base.js +3 -0
- package/dist/core/llm/base.js.map +1 -0
- package/dist/core/llm/build.d.ts +16 -0
- package/dist/core/llm/build.d.ts.map +1 -0
- package/dist/core/llm/build.js +34 -0
- package/dist/core/llm/build.js.map +1 -0
- package/dist/core/llm/claudeCli.d.ts +20 -0
- package/dist/core/llm/claudeCli.d.ts.map +1 -0
- package/dist/core/llm/claudeCli.js +57 -0
- package/dist/core/llm/claudeCli.js.map +1 -0
- package/dist/core/llm/codexCli.d.ts +13 -0
- package/dist/core/llm/codexCli.d.ts.map +1 -0
- package/dist/core/llm/codexCli.js +30 -0
- package/dist/core/llm/codexCli.js.map +1 -0
- package/dist/core/llm/cursorCli.d.ts +13 -0
- package/dist/core/llm/cursorCli.d.ts.map +1 -0
- package/dist/core/llm/cursorCli.js +27 -0
- package/dist/core/llm/cursorCli.js.map +1 -0
- package/dist/core/llm/index.d.ts +5 -0
- package/dist/core/llm/index.d.ts.map +1 -0
- package/dist/core/llm/index.js +4 -0
- package/dist/core/llm/index.js.map +1 -0
- package/dist/core/llm/scripted.d.ts +16 -0
- package/dist/core/llm/scripted.d.ts.map +1 -0
- package/dist/core/llm/scripted.js +29 -0
- package/dist/core/llm/scripted.js.map +1 -0
- package/dist/core/progress.d.ts +29 -0
- package/dist/core/progress.d.ts.map +1 -0
- package/dist/core/progress.js +22 -0
- package/dist/core/progress.js.map +1 -0
- package/dist/core/registry.d.ts +29 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +63 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/spawn.d.ts +19 -0
- package/dist/core/spawn.d.ts.map +1 -0
- package/dist/core/spawn.js +58 -0
- package/dist/core/spawn.js.map +1 -0
- package/dist/core/state.d.ts +58 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +3 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/timeout.d.ts +13 -0
- package/dist/core/timeout.d.ts.map +1 -0
- package/dist/core/timeout.js +33 -0
- package/dist/core/timeout.js.map +1 -0
- package/dist/core/types.d.ts +15 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validate.d.ts +7 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +47 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/graph.d.ts +4 -0
- package/dist/graph.d.ts.map +1 -0
- package/dist/graph.js +14 -0
- package/dist/graph.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/modes/adversarial.d.ts +3 -0
- package/dist/modes/adversarial.d.ts.map +1 -0
- package/dist/modes/adversarial.js +42 -0
- package/dist/modes/adversarial.js.map +1 -0
- package/dist/modes/consensus.d.ts +3 -0
- package/dist/modes/consensus.d.ts.map +1 -0
- package/dist/modes/consensus.js +47 -0
- package/dist/modes/consensus.js.map +1 -0
- package/dist/modes/debate.d.ts +3 -0
- package/dist/modes/debate.d.ts.map +1 -0
- package/dist/modes/debate.js +76 -0
- package/dist/modes/debate.js.map +1 -0
- package/dist/modes/index.d.ts +7 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +7 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/single.d.ts +3 -0
- package/dist/modes/single.d.ts.map +1 -0
- package/dist/modes/single.js +42 -0
- package/dist/modes/single.js.map +1 -0
- package/dist/modes/tournament.d.ts +3 -0
- package/dist/modes/tournament.d.ts.map +1 -0
- package/dist/modes/tournament.js +48 -0
- package/dist/modes/tournament.js.map +1 -0
- package/dist/nodes/agentRunner.d.ts +15 -0
- package/dist/nodes/agentRunner.d.ts.map +1 -0
- package/dist/nodes/agentRunner.js +106 -0
- package/dist/nodes/agentRunner.js.map +1 -0
- package/dist/nodes/auditor.d.ts +38 -0
- package/dist/nodes/auditor.d.ts.map +1 -0
- package/dist/nodes/auditor.js +179 -0
- package/dist/nodes/auditor.js.map +1 -0
- package/dist/nodes/bracket.d.ts +19 -0
- package/dist/nodes/bracket.d.ts.map +1 -0
- package/dist/nodes/bracket.js +90 -0
- package/dist/nodes/bracket.js.map +1 -0
- package/dist/nodes/consensusEngine.d.ts +18 -0
- package/dist/nodes/consensusEngine.d.ts.map +1 -0
- package/dist/nodes/consensusEngine.js +94 -0
- package/dist/nodes/consensusEngine.js.map +1 -0
- package/dist/nodes/moderator.d.ts +20 -0
- package/dist/nodes/moderator.d.ts.map +1 -0
- package/dist/nodes/moderator.js +83 -0
- package/dist/nodes/moderator.js.map +1 -0
- package/dist/nodes/planner.d.ts +17 -0
- package/dist/nodes/planner.d.ts.map +1 -0
- package/dist/nodes/planner.js +40 -0
- package/dist/nodes/planner.js.map +1 -0
- package/dist/nodes/supervisor.d.ts +28 -0
- package/dist/nodes/supervisor.d.ts.map +1 -0
- package/dist/nodes/supervisor.js +75 -0
- package/dist/nodes/supervisor.js.map +1 -0
- package/package.json +66 -0
- package/schemas/coterie.config.schema.json +204 -0
package/README.md
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# coterie
|
|
2
|
+
|
|
3
|
+
A conversational meta-agent for your terminal. You chat like you're talking to a
|
|
4
|
+
single coding assistant, but **every turn runs through a multi-agent coordination
|
|
5
|
+
round** (debate, adversarial review, tournament, consensus, or single) behind the
|
|
6
|
+
scenes — raising the reliability and quality of each response.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
coterie chat # in any repo — each prompt runs a multi-agent round
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
▲ coterie chat
|
|
14
|
+
mode=adversarial · workdir=. · agents: claude-code, codex, cursor · $0 metered
|
|
15
|
+
modes: single, adversarial, debate, tournament, consensus
|
|
16
|
+
Each turn: agents deliberate, then one finalizer applies edits + replies. /mode to change modes. /help for commands.
|
|
17
|
+
|
|
18
|
+
coterie(adversarial)› add a retry decorator to http.py and cover it with tests
|
|
19
|
+
· implementer (claude-code)
|
|
20
|
+
· auditor (codex)
|
|
21
|
+
· judge → claude-code: tests pass, edge cases covered
|
|
22
|
+
|
|
23
|
+
Added @retry to http.py with exponential backoff + tests in test_http.py.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Setup (one-time)
|
|
27
|
+
|
|
28
|
+
Install from npm — puts the `coterie` command on your PATH:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install -g coterie
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
<details>
|
|
35
|
+
<summary>Or install from source</summary>
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/cxk280/coterie
|
|
39
|
+
cd coterie/packages/coterie-js
|
|
40
|
+
npm install
|
|
41
|
+
npm run build # compile TypeScript → dist/
|
|
42
|
+
npm link # puts `coterie` on your PATH globally
|
|
43
|
+
```
|
|
44
|
+
</details>
|
|
45
|
+
|
|
46
|
+
Sign in to **at least two** agent CLIs — Coterie coordinates multiple agents, so
|
|
47
|
+
it needs two to deliberate (any pair), and uses all you have. They run on your
|
|
48
|
+
**subscriptions**, no API keys:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
claude # sign in to Claude Max, then exit
|
|
52
|
+
codex # sign in with your ChatGPT account
|
|
53
|
+
cursor-agent login # sign in to Cursor
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Run `coterie doctor` to see which are ready:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
coterie doctor # ✓/✗ per agent; exits non-zero until at least two are ready
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Coterie keeps no config of binary paths or credentials — it finds each CLI on
|
|
63
|
+
your `PATH` and lets it read its own creds, so it authenticates exactly as your
|
|
64
|
+
shell does. `coterie chat` also runs this check on startup.
|
|
65
|
+
|
|
66
|
+
Then, in any repo you want it to work in:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
cd ~/my-project
|
|
70
|
+
coterie chat # runs entirely on your subscriptions — $0 metered
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## How it works
|
|
74
|
+
|
|
75
|
+
Each prompt becomes a one-turn coordination round over the **real coding-agent
|
|
76
|
+
CLIs you already have** — any two or more of Claude Code, Codex, and Cursor. The
|
|
77
|
+
mode decides how they cooperate; the synthesized result (and any file edits) is
|
|
78
|
+
the reply. Switch strategies per prompt with `/mode <name>`.
|
|
79
|
+
|
|
80
|
+
Each turn has two phases: the agents **deliberate** in throwaway worktrees (they
|
|
81
|
+
never touch your files), then one **finalizer** agent runs in your workdir,
|
|
82
|
+
applies the edits, and writes the plain-prose reply. So file edits land in *every*
|
|
83
|
+
mode, and you never get raw findings JSON back. The mode shapes the deliberation:
|
|
84
|
+
|
|
85
|
+
| Mode | What the agents do | Best for |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| `single` | a router picks one agent to attempt it | quick edits, simple asks |
|
|
88
|
+
| `adversarial` | implementer + auditor + judge, with refinement | reliable code changes |
|
|
89
|
+
| `debate` | two agents argue, a moderator + judge decide | decisions, tradeoffs |
|
|
90
|
+
| `tournament` | N agents compete, a bracket judge ranks | best-of-N for critical work |
|
|
91
|
+
| `consensus` | agents review independently, an engine merges agreement | reviews, audits |
|
|
92
|
+
|
|
93
|
+
## Runs on your subscriptions ($0 metered)
|
|
94
|
+
|
|
95
|
+
Everything runs on your existing logins — the agents on **Claude Max**, **ChatGPT**
|
|
96
|
+
(Codex), and **Cursor Pro**, and the behind-the-scenes coordination (routing /
|
|
97
|
+
judging / moderating) on your **Claude subscription** via `claude -p`. So a full
|
|
98
|
+
turn is **$0 metered** — no setup, no flags. On startup `coterie chat` checks each
|
|
99
|
+
required CLI is installed and signed in, and tells you exactly what to do if not.
|
|
100
|
+
|
|
101
|
+
> **No pay-as-you-go API backend (yet).** Coordination is subscription-only by
|
|
102
|
+
> design, so a session can never run up a surprise metered bill. A pluggable API
|
|
103
|
+
> provider (Anthropic / OpenAI-compatible) may be added later for environments
|
|
104
|
+
> without the subscription CLIs — it's intentionally not wired in today.
|
|
105
|
+
|
|
106
|
+
> **Grok is deferred** for the same reason: unlike Claude / Codex / Cursor it has
|
|
107
|
+
> no subscription-backed headless coding CLI (only the pay-as-you-go xAI API), so
|
|
108
|
+
> it can't join the $0-metered lineup yet.
|
|
109
|
+
|
|
110
|
+
## Commands
|
|
111
|
+
|
|
112
|
+
`/mode <name>` · `/show` `/hide` (live agent exchanges) · `/clear` · `/help` · `/exit`
|
|
113
|
+
|
|
114
|
+
The agent exchanges stream live by default — each agent's contribution, the
|
|
115
|
+
judge's verdict, what the finalizer changed. `/hide` (or `--quiet`) shows only the
|
|
116
|
+
reply.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
coterie chat --mode debate --workdir ~/proj # start in a mode, against a repo
|
|
120
|
+
coterie chat --quiet # hide the agent exchanges
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## One-shot (non-conversational)
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
coterie run "find every bug in src/auth.ts" --config examples/consensus.coterie.yaml
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Test
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
npm test # fast, offline, deterministic — the CI gate
|
|
133
|
+
npm run test:e2e # real-agent end-to-end (spends subscription calls; needs claude + codex signed in)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
See the [top-level README](../../README.md) for architecture, the five modes in
|
|
137
|
+
depth, and the full breakdown of the three test layers.
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** CLIAdapter contract. Mirrors Python's `adapters/base.py`. */
|
|
2
|
+
export interface AdapterResult {
|
|
3
|
+
stdout: string;
|
|
4
|
+
stderr: string;
|
|
5
|
+
exit_code: number;
|
|
6
|
+
files_changed: string[];
|
|
7
|
+
duration_s: number;
|
|
8
|
+
cost_estimate_usd: number | null;
|
|
9
|
+
}
|
|
10
|
+
export interface CLIAdapterCtor {
|
|
11
|
+
adapterName: string;
|
|
12
|
+
new (agent_id: string, opts?: {
|
|
13
|
+
model?: string;
|
|
14
|
+
}): CLIAdapter;
|
|
15
|
+
}
|
|
16
|
+
/** A DOMException-style abort error so callers can detect cancellation via
|
|
17
|
+
* `err.name === "AbortError"` (matches what `fetch`/AbortSignal users expect). */
|
|
18
|
+
export declare function abortError(): Error;
|
|
19
|
+
export declare abstract class CLIAdapter {
|
|
20
|
+
readonly agent_id: string;
|
|
21
|
+
readonly opts: {
|
|
22
|
+
model?: string;
|
|
23
|
+
};
|
|
24
|
+
static readonly adapterName: string;
|
|
25
|
+
/**
|
|
26
|
+
* Env vars stripped from the agent subprocess. Lets an adapter force its CLI
|
|
27
|
+
* onto its own auth (e.g. a subscription session) instead of an API key that
|
|
28
|
+
* Coterie's coordination LLMs need in-process.
|
|
29
|
+
*/
|
|
30
|
+
static readonly stripEnv: readonly string[];
|
|
31
|
+
constructor(agent_id: string, opts?: {
|
|
32
|
+
model?: string;
|
|
33
|
+
});
|
|
34
|
+
get model(): string | undefined;
|
|
35
|
+
private subprocessEnv;
|
|
36
|
+
abstract buildCommand(prompt: string, workdir: string, extra: Record<string, unknown>): string[];
|
|
37
|
+
abstract parseResult(stdout: string, stderr: string, exitCode: number): AdapterResult;
|
|
38
|
+
/** Turn one line of the CLI's streaming (NDJSON) output into a short, human
|
|
39
|
+
* -readable progress note (e.g. "→ Read foo.ts", "→ $ npm test"), or null to
|
|
40
|
+
* ignore it. Adapters that run in a streaming format override this; the
|
|
41
|
+
* default (no streaming) yields nothing. */
|
|
42
|
+
streamEvent(_line: string): string | null;
|
|
43
|
+
/** Run the agent CLI asynchronously. Async (vs the old `spawnSync`) is what
|
|
44
|
+
* keeps Node's event loop free during a turn — so the chat trace can stream
|
|
45
|
+
* live, SIGINT is handled, and fan-out agents run concurrently. Honors a
|
|
46
|
+
* timeout and an optional AbortSignal (both kill the child). */
|
|
47
|
+
run(prompt: string, workdir: string, opts?: {
|
|
48
|
+
timeoutMs?: number;
|
|
49
|
+
extra?: Record<string, unknown>;
|
|
50
|
+
signal?: AbortSignal;
|
|
51
|
+
onStream?: (text: string) => void;
|
|
52
|
+
}): Promise<AdapterResult>;
|
|
53
|
+
private spawnCollect;
|
|
54
|
+
protected gitChangedFiles(workdir: string): string[];
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAMhE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,CAAC;CAC/D;AAED;mFACmF;AACnF,wBAAgB,UAAU,IAAI,KAAK,CAIlC;AAED,8BAAsB,UAAU;aAWZ,QAAQ,EAAE,MAAM;aAChB,IAAI,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE;IAX1C,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAEpC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAM;gBAG/B,QAAQ,EAAE,MAAM,EAChB,IAAI,GAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAO;IAG/C,IAAI,KAAK,IAAI,MAAM,GAAG,SAAS,CAE9B;IAED,OAAO,CAAC,aAAa;IAQrB,QAAQ,CAAC,YAAY,CACnB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,MAAM,EAAE;IAEX,QAAQ,CAAC,WAAW,CAClB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,GACf,aAAa;IAEhB;;;iDAG6C;IAC7C,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAIzC;;;qEAGiE;IAC3D,GAAG,CACP,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,IAAI,GAAE;QACJ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,MAAM,CAAC,EAAE,WAAW,CAAC;QACrB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;KAC9B,GACL,OAAO,CAAC,aAAa,CAAC;IAczB,OAAO,CAAC,YAAY;IAuGpB,SAAS,CAAC,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;CAWrD"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/** CLIAdapter contract. Mirrors Python's `adapters/base.py`. */
|
|
2
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
3
|
+
import { sleepAwareTimeout } from "../core/timeout.js";
|
|
4
|
+
/** A DOMException-style abort error so callers can detect cancellation via
|
|
5
|
+
* `err.name === "AbortError"` (matches what `fetch`/AbortSignal users expect). */
|
|
6
|
+
export function abortError() {
|
|
7
|
+
const e = new Error("The operation was aborted");
|
|
8
|
+
e.name = "AbortError";
|
|
9
|
+
return e;
|
|
10
|
+
}
|
|
11
|
+
export class CLIAdapter {
|
|
12
|
+
agent_id;
|
|
13
|
+
opts;
|
|
14
|
+
static adapterName;
|
|
15
|
+
/**
|
|
16
|
+
* Env vars stripped from the agent subprocess. Lets an adapter force its CLI
|
|
17
|
+
* onto its own auth (e.g. a subscription session) instead of an API key that
|
|
18
|
+
* Coterie's coordination LLMs need in-process.
|
|
19
|
+
*/
|
|
20
|
+
static stripEnv = [];
|
|
21
|
+
constructor(agent_id, opts = {}) {
|
|
22
|
+
this.agent_id = agent_id;
|
|
23
|
+
this.opts = opts;
|
|
24
|
+
}
|
|
25
|
+
get model() {
|
|
26
|
+
return this.opts.model;
|
|
27
|
+
}
|
|
28
|
+
subprocessEnv() {
|
|
29
|
+
const strip = this.constructor.stripEnv;
|
|
30
|
+
if (!strip.length)
|
|
31
|
+
return undefined; // inherit process.env
|
|
32
|
+
const env = { ...process.env };
|
|
33
|
+
for (const key of strip)
|
|
34
|
+
delete env[key];
|
|
35
|
+
return env;
|
|
36
|
+
}
|
|
37
|
+
/** Turn one line of the CLI's streaming (NDJSON) output into a short, human
|
|
38
|
+
* -readable progress note (e.g. "→ Read foo.ts", "→ $ npm test"), or null to
|
|
39
|
+
* ignore it. Adapters that run in a streaming format override this; the
|
|
40
|
+
* default (no streaming) yields nothing. */
|
|
41
|
+
streamEvent(_line) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/** Run the agent CLI asynchronously. Async (vs the old `spawnSync`) is what
|
|
45
|
+
* keeps Node's event loop free during a turn — so the chat trace can stream
|
|
46
|
+
* live, SIGINT is handled, and fan-out agents run concurrently. Honors a
|
|
47
|
+
* timeout and an optional AbortSignal (both kill the child). */
|
|
48
|
+
async run(prompt, workdir, opts = {}) {
|
|
49
|
+
const argv = this.buildCommand(prompt, workdir, opts.extra ?? {});
|
|
50
|
+
const [cmd, ...args] = argv;
|
|
51
|
+
if (!cmd)
|
|
52
|
+
throw new Error(`Adapter ${this.agent_id} produced an empty command`);
|
|
53
|
+
const t0 = Date.now();
|
|
54
|
+
const { stdout, stderr, code } = await this.spawnCollect(cmd, args, workdir, opts);
|
|
55
|
+
const result = this.parseResult(stdout, stderr, code);
|
|
56
|
+
result.duration_s = (Date.now() - t0) / 1000;
|
|
57
|
+
// Report which files the agent actually touched (so the trace can show
|
|
58
|
+
// "edited X, Y") unless the adapter already computed it.
|
|
59
|
+
if (!result.files_changed?.length)
|
|
60
|
+
result.files_changed = this.gitChangedFiles(workdir);
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
spawnCollect(cmd, args, workdir, opts) {
|
|
64
|
+
return new Promise((resolve, reject) => {
|
|
65
|
+
if (opts.signal?.aborted)
|
|
66
|
+
return reject(abortError());
|
|
67
|
+
// stdin = /dev/null so the child gets an immediate EOF. Headless agent CLIs
|
|
68
|
+
// (notably `codex exec`) otherwise block forever waiting on stdin — async
|
|
69
|
+
// spawn leaves the stdin pipe open, unlike the old spawnSync which closed it.
|
|
70
|
+
const child = spawn(cmd, args, {
|
|
71
|
+
cwd: workdir,
|
|
72
|
+
env: this.subprocessEnv(),
|
|
73
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
74
|
+
});
|
|
75
|
+
let stdout = "";
|
|
76
|
+
let stderr = "";
|
|
77
|
+
const cap = 32 * 1024 * 1024;
|
|
78
|
+
// Buffer stdout into whole lines so each NDJSON event can be turned into a
|
|
79
|
+
// live progress note as it streams (without blocking — the loop is free).
|
|
80
|
+
const lineCap = 1024 * 1024;
|
|
81
|
+
let lineBuf = "";
|
|
82
|
+
// True while we're discarding an over-cap, not-yet-terminated line; reset
|
|
83
|
+
// at the next newline so streaming resyncs on the following whole line.
|
|
84
|
+
let lineOverflow = false;
|
|
85
|
+
child.stdout?.on("data", (d) => {
|
|
86
|
+
const s = d.toString();
|
|
87
|
+
if (stdout.length < cap)
|
|
88
|
+
stdout += s;
|
|
89
|
+
if (!opts.onStream)
|
|
90
|
+
return;
|
|
91
|
+
lineBuf += s;
|
|
92
|
+
let nl;
|
|
93
|
+
while ((nl = lineBuf.indexOf("\n")) >= 0) {
|
|
94
|
+
const line = lineBuf.slice(0, nl);
|
|
95
|
+
lineBuf = lineBuf.slice(nl + 1);
|
|
96
|
+
if (lineOverflow) {
|
|
97
|
+
// This newline closes the dropped line; resume on the next one.
|
|
98
|
+
lineOverflow = false;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const note = this.streamEvent(line);
|
|
103
|
+
if (note)
|
|
104
|
+
opts.onStream(note);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// a malformed line must never break the run
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Bound the partial-line buffer: a producer that emits a multi-megabyte
|
|
111
|
+
// single line (or never a newline) must not grow it without limit — the
|
|
112
|
+
// authoritative `stdout` is capped, and progress notes are best-effort,
|
|
113
|
+
// so drop the partial line and resync at the next newline.
|
|
114
|
+
if (lineBuf.length > lineCap) {
|
|
115
|
+
lineBuf = "";
|
|
116
|
+
lineOverflow = true;
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
child.stderr?.on("data", (d) => {
|
|
120
|
+
if (stderr.length < cap)
|
|
121
|
+
stderr += d.toString();
|
|
122
|
+
});
|
|
123
|
+
// Graceful kill: SIGTERM, then SIGKILL if it doesn't exit in time.
|
|
124
|
+
const hardKill = () => {
|
|
125
|
+
if (!child.killed)
|
|
126
|
+
child.kill("SIGKILL");
|
|
127
|
+
};
|
|
128
|
+
const kill = () => {
|
|
129
|
+
child.kill("SIGTERM");
|
|
130
|
+
setTimeout(hardKill, 2_000).unref();
|
|
131
|
+
};
|
|
132
|
+
const timeoutMs = opts.timeoutMs ?? 600_000;
|
|
133
|
+
let timedOut = false;
|
|
134
|
+
// Sleep-aware: a wall-clock timer would kill a merely-suspended child the
|
|
135
|
+
// instant the machine wakes. This only counts active time.
|
|
136
|
+
const clearTimer = sleepAwareTimeout(timeoutMs, () => {
|
|
137
|
+
timedOut = true;
|
|
138
|
+
kill();
|
|
139
|
+
});
|
|
140
|
+
const onAbort = () => {
|
|
141
|
+
kill();
|
|
142
|
+
cleanup();
|
|
143
|
+
reject(abortError());
|
|
144
|
+
};
|
|
145
|
+
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
146
|
+
const cleanup = () => {
|
|
147
|
+
clearTimer();
|
|
148
|
+
opts.signal?.removeEventListener("abort", onAbort);
|
|
149
|
+
};
|
|
150
|
+
child.on("error", (err) => {
|
|
151
|
+
cleanup();
|
|
152
|
+
reject(err);
|
|
153
|
+
});
|
|
154
|
+
child.on("close", (code) => {
|
|
155
|
+
cleanup();
|
|
156
|
+
if (timedOut)
|
|
157
|
+
reject(new Error(`timed out after ${Math.round(timeoutMs / 1000)}s`));
|
|
158
|
+
else
|
|
159
|
+
resolve({ stdout, stderr, code: code ?? 1 });
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
gitChangedFiles(workdir) {
|
|
164
|
+
const proc = spawnSync("git", ["status", "--porcelain"], {
|
|
165
|
+
cwd: workdir,
|
|
166
|
+
encoding: "utf8",
|
|
167
|
+
});
|
|
168
|
+
if (proc.status !== 0)
|
|
169
|
+
return [];
|
|
170
|
+
return (proc.stdout ?? "")
|
|
171
|
+
.split("\n")
|
|
172
|
+
.filter((l) => l.trim())
|
|
173
|
+
.map((l) => l.slice(3));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=base.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/adapters/base.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CLIAdapter, type AdapterResult } from "./base.js";
|
|
2
|
+
export declare class ClaudeCodeAdapter extends CLIAdapter {
|
|
3
|
+
static readonly adapterName = "claude-code";
|
|
4
|
+
static readonly stripEnv: string[];
|
|
5
|
+
buildCommand(prompt: string, _workdir: string, extra?: Record<string, unknown>): string[];
|
|
6
|
+
/** Emit a note for each tool the agent invokes; skip thinking/text/bookkeeping
|
|
7
|
+
* (the final answer is rendered when the run completes). */
|
|
8
|
+
streamEvent(line: string): string | null;
|
|
9
|
+
parseResult(stdout: string, stderr: string, exitCode: number): AdapterResult;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=claudeCode.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeCode.d.ts","sourceRoot":"","sources":["../../src/adapters/claudeCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAK3D,qBAAa,iBAAkB,SAAQ,UAAU;IAC/C,MAAM,CAAC,QAAQ,CAAC,WAAW,iBAAiB;IAI5C,MAAM,CAAC,QAAQ,CAAC,QAAQ,WAAiD;IAEzE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,EAAE;IAU7F;iEAC6D;IACpD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IASjD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa;CA8B7E"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { CLIAdapter } from "./base.js";
|
|
2
|
+
import { parseJsonLoose, parseNdjson } from "../core/json.js";
|
|
3
|
+
import { toolHint } from "./stream.js";
|
|
4
|
+
import { registerAdapter } from "../core/registry.js";
|
|
5
|
+
export class ClaudeCodeAdapter extends CLIAdapter {
|
|
6
|
+
static adapterName = "claude-code";
|
|
7
|
+
// Run on the user's Claude subscription (OAuth session), not a pay-per-token
|
|
8
|
+
// API key — coordination LLMs use ANTHROPIC_API_KEY in-process, but the agent
|
|
9
|
+
// CLI should bill against the subscription.
|
|
10
|
+
static stripEnv = ["ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN"];
|
|
11
|
+
buildCommand(prompt, _workdir, extra = {}) {
|
|
12
|
+
// acceptEdits lets the agent apply file edits headlessly while still gating
|
|
13
|
+
// riskier tools — safe against an isolated workdir. stream-json (which needs
|
|
14
|
+
// --verbose) emits one NDJSON event per step so we can show live progress.
|
|
15
|
+
const permissionMode = extra.permissionMode ?? "acceptEdits";
|
|
16
|
+
const cmd = ["claude", "-p", prompt, "--output-format", "stream-json", "--verbose", "--permission-mode", permissionMode];
|
|
17
|
+
if (this.model)
|
|
18
|
+
cmd.push("--model", this.model);
|
|
19
|
+
return cmd;
|
|
20
|
+
}
|
|
21
|
+
/** Emit a note for each tool the agent invokes; skip thinking/text/bookkeeping
|
|
22
|
+
* (the final answer is rendered when the run completes). */
|
|
23
|
+
streamEvent(line) {
|
|
24
|
+
const ev = parseJsonLoose(line);
|
|
25
|
+
if (ev?.type !== "assistant")
|
|
26
|
+
return null;
|
|
27
|
+
for (const c of ev.message?.content ?? []) {
|
|
28
|
+
if (c?.type === "tool_use")
|
|
29
|
+
return `→ ${c.name}${toolHint(c.input)}`;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
parseResult(stdout, stderr, exitCode) {
|
|
34
|
+
// stdout is an NDJSON stream; the final `result` event carries the answer + cost.
|
|
35
|
+
const events = parseNdjson(stdout);
|
|
36
|
+
const result = events.find((e) => e?.type === "result");
|
|
37
|
+
if (result) {
|
|
38
|
+
const files = events
|
|
39
|
+
.flatMap((e) => (e?.type === "assistant" ? (e.message?.content ?? []) : []))
|
|
40
|
+
.filter((c) => c?.type === "tool_use" && /^(Edit|Write|NotebookEdit)$/.test(c.name))
|
|
41
|
+
.map((c) => c.input?.file_path)
|
|
42
|
+
.filter(Boolean);
|
|
43
|
+
return {
|
|
44
|
+
stdout: result.result ?? stdout,
|
|
45
|
+
stderr,
|
|
46
|
+
exit_code: exitCode,
|
|
47
|
+
files_changed: [...new Set(files)],
|
|
48
|
+
duration_s: 0,
|
|
49
|
+
cost_estimate_usd: result.total_cost_usd ?? null,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
// Fallback: not the expected stream (older CLI / plain JSON / prose).
|
|
53
|
+
const loose = parseJsonLoose(stdout);
|
|
54
|
+
return {
|
|
55
|
+
stdout: loose?.result ?? stdout,
|
|
56
|
+
stderr,
|
|
57
|
+
exit_code: exitCode,
|
|
58
|
+
files_changed: [],
|
|
59
|
+
duration_s: 0,
|
|
60
|
+
cost_estimate_usd: loose?.total_cost_usd ?? null,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
registerAdapter(ClaudeCodeAdapter);
|
|
65
|
+
//# sourceMappingURL=claudeCode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claudeCode.js","sourceRoot":"","sources":["../../src/adapters/claudeCode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,OAAO,iBAAkB,SAAQ,UAAU;IAC/C,MAAM,CAAU,WAAW,GAAG,aAAa,CAAC;IAC5C,6EAA6E;IAC7E,8EAA8E;IAC9E,4CAA4C;IAC5C,MAAM,CAAU,QAAQ,GAAG,CAAC,mBAAmB,EAAE,sBAAsB,CAAC,CAAC;IAEzE,YAAY,CAAC,MAAc,EAAE,QAAgB,EAAE,QAAiC,EAAE;QAChF,4EAA4E;QAC5E,6EAA6E;QAC7E,2EAA2E;QAC3E,MAAM,cAAc,GAAI,KAAK,CAAC,cAAyB,IAAI,aAAa,CAAC;QACzE,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,cAAc,CAAC,CAAC;QACzH,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,OAAO,GAAG,CAAC;IACb,CAAC;IAED;iEAC6D;IACpD,WAAW,CAAC,IAAY;QAC/B,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,EAAE,EAAE,IAAI,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,EAAE,IAAI,KAAK,UAAU;gBAAE,OAAO,KAAK,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;QACvE,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,WAAW,CAAC,MAAc,EAAE,MAAc,EAAE,QAAgB;QAC1D,kFAAkF;QAClF,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC;QACxD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,KAAK,GAAG,MAAM;iBACjB,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;iBAC3E,MAAM,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,UAAU,IAAI,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;iBACxF,GAAG,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,SAAS,CAAC;iBACnC,MAAM,CAAC,OAAO,CAAC,CAAC;YACnB,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,MAAM;gBAC/B,MAAM;gBACN,SAAS,EAAE,QAAQ;gBACnB,aAAa,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,CAAa;gBAC9C,UAAU,EAAE,CAAC;gBACb,iBAAiB,EAAE,MAAM,CAAC,cAAc,IAAI,IAAI;aACjD,CAAC;QACJ,CAAC;QACD,sEAAsE;QACtE,MAAM,KAAK,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;QACrC,OAAO;YACL,MAAM,EAAE,KAAK,EAAE,MAAM,IAAI,MAAM;YAC/B,MAAM;YACN,SAAS,EAAE,QAAQ;YACnB,aAAa,EAAE,EAAE;YACjB,UAAU,EAAE,CAAC;YACb,iBAAiB,EAAE,KAAK,EAAE,cAAc,IAAI,IAAI;SACjD,CAAC;IACJ,CAAC;;AAGH,eAAe,CAAC,iBAAiB,CAAC,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CLIAdapter, type AdapterResult } from "./base.js";
|
|
2
|
+
export declare class CodexAdapter extends CLIAdapter {
|
|
3
|
+
static readonly adapterName = "codex";
|
|
4
|
+
buildCommand(prompt: string, _workdir: string, extra?: Record<string, unknown>): string[];
|
|
5
|
+
/** Each non-message item (a command, a file change, reasoning) is live activity;
|
|
6
|
+
* the final agent_message is the answer, surfaced on completion. */
|
|
7
|
+
streamEvent(line: string): string | null;
|
|
8
|
+
parseResult(stdout: string, stderr: string, exit_code: number): AdapterResult;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=codex.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAI3D,qBAAa,YAAa,SAAQ,UAAU;IAC1C,MAAM,CAAC,QAAQ,CAAC,WAAW,WAAW;IAEtC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,EAAE;IAU7F;yEACqE;IAC5D,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAajD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa;CAgB9E"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CLIAdapter } from "./base.js";
|
|
2
|
+
import { parseJsonLoose, parseNdjson } from "../core/json.js";
|
|
3
|
+
import { registerAdapter } from "../core/registry.js";
|
|
4
|
+
export class CodexAdapter extends CLIAdapter {
|
|
5
|
+
static adapterName = "codex";
|
|
6
|
+
buildCommand(prompt, _workdir, extra = {}) {
|
|
7
|
+
// workspace-write sandboxes edits to the cwd; --skip-git-repo-check lets exec
|
|
8
|
+
// run in a plain (non-git) workdir; --json streams JSONL events for live progress.
|
|
9
|
+
const sandbox = extra.sandbox ?? "workspace-write";
|
|
10
|
+
const cmd = ["codex", "exec", "--skip-git-repo-check", "--json", "-s", sandbox];
|
|
11
|
+
if (this.model)
|
|
12
|
+
cmd.push("--model", this.model);
|
|
13
|
+
cmd.push(prompt);
|
|
14
|
+
return cmd;
|
|
15
|
+
}
|
|
16
|
+
/** Each non-message item (a command, a file change, reasoning) is live activity;
|
|
17
|
+
* the final agent_message is the answer, surfaced on completion. */
|
|
18
|
+
streamEvent(line) {
|
|
19
|
+
const ev = parseJsonLoose(line);
|
|
20
|
+
if (ev?.type !== "item.completed")
|
|
21
|
+
return null;
|
|
22
|
+
const it = ev.item ?? {};
|
|
23
|
+
if (it.type === "agent_message" || it.type === "reasoning")
|
|
24
|
+
return null;
|
|
25
|
+
if (it.command)
|
|
26
|
+
return `→ $ ${String(it.command).replace(/\s+/g, " ").slice(0, 80)}`;
|
|
27
|
+
if (it.type === "file_change" || it.type === "patch") {
|
|
28
|
+
const f = it.path ?? (Array.isArray(it.changes) ? it.changes.map((c) => c.path).join(", ") : "");
|
|
29
|
+
return `→ edit ${String(f).slice(0, 80)}`.trimEnd();
|
|
30
|
+
}
|
|
31
|
+
return `→ ${String(it.type).replace(/_/g, " ")}`;
|
|
32
|
+
}
|
|
33
|
+
parseResult(stdout, stderr, exit_code) {
|
|
34
|
+
// stdout is JSONL; the answer is the last `agent_message` item.
|
|
35
|
+
const events = parseNdjson(stdout);
|
|
36
|
+
const messages = events
|
|
37
|
+
.filter((e) => e?.type === "item.completed" && e.item?.type === "agent_message")
|
|
38
|
+
.map((e) => e.item.text)
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
return {
|
|
41
|
+
stdout: messages.length ? messages[messages.length - 1] : stdout,
|
|
42
|
+
stderr,
|
|
43
|
+
exit_code,
|
|
44
|
+
files_changed: [],
|
|
45
|
+
duration_s: 0,
|
|
46
|
+
cost_estimate_usd: null,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
registerAdapter(CodexAdapter);
|
|
51
|
+
//# sourceMappingURL=codex.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex.js","sourceRoot":"","sources":["../../src/adapters/codex.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { CLIAdapter, type AdapterResult } from "./base.js";
|
|
2
|
+
/**
|
|
3
|
+
* Cursor's headless agent CLI (`cursor-agent`), run on a Cursor Pro subscription.
|
|
4
|
+
* NB: `cursor-agent` is a separate install from the `cursor` editor launcher; the
|
|
5
|
+
* chat preflight checks for it. Flags verified against cursor-agent's print mode:
|
|
6
|
+
* `-p` non-interactive, `--output-format stream-json` (NDJSON events for live
|
|
7
|
+
* progress), `--force` to apply edits without a confirmation/trust prompt.
|
|
8
|
+
*/
|
|
9
|
+
export declare class CursorAdapter extends CLIAdapter {
|
|
10
|
+
static readonly adapterName = "cursor";
|
|
11
|
+
buildCommand(prompt: string, _workdir: string, _extra?: Record<string, unknown>): string[];
|
|
12
|
+
/** Surface tool calls as live activity; skip thinking deltas + the final text. */
|
|
13
|
+
streamEvent(line: string): string | null;
|
|
14
|
+
parseResult(stdout: string, stderr: string, exitCode: number): AdapterResult;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=cursor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../../src/adapters/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAK3D;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,UAAU;IAC3C,MAAM,CAAC,QAAQ,CAAC,WAAW,YAAY;IAEvC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,MAAM,EAAE;IAM9F,kFAAkF;IACzE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAWjD,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa;CAa7E"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CLIAdapter } from "./base.js";
|
|
2
|
+
import { parseJsonLoose, parseNdjson } from "../core/json.js";
|
|
3
|
+
import { toolHint } from "./stream.js";
|
|
4
|
+
import { registerAdapter } from "../core/registry.js";
|
|
5
|
+
/**
|
|
6
|
+
* Cursor's headless agent CLI (`cursor-agent`), run on a Cursor Pro subscription.
|
|
7
|
+
* NB: `cursor-agent` is a separate install from the `cursor` editor launcher; the
|
|
8
|
+
* chat preflight checks for it. Flags verified against cursor-agent's print mode:
|
|
9
|
+
* `-p` non-interactive, `--output-format stream-json` (NDJSON events for live
|
|
10
|
+
* progress), `--force` to apply edits without a confirmation/trust prompt.
|
|
11
|
+
*/
|
|
12
|
+
export class CursorAdapter extends CLIAdapter {
|
|
13
|
+
static adapterName = "cursor";
|
|
14
|
+
buildCommand(prompt, _workdir, _extra = {}) {
|
|
15
|
+
const cmd = ["cursor-agent", "-p", prompt, "--output-format", "stream-json", "--force"];
|
|
16
|
+
if (this.model)
|
|
17
|
+
cmd.push("--model", this.model);
|
|
18
|
+
return cmd;
|
|
19
|
+
}
|
|
20
|
+
/** Surface tool calls as live activity; skip thinking deltas + the final text. */
|
|
21
|
+
streamEvent(line) {
|
|
22
|
+
const ev = parseJsonLoose(line);
|
|
23
|
+
if (ev?.type !== "assistant")
|
|
24
|
+
return null;
|
|
25
|
+
for (const c of ev.message?.content ?? []) {
|
|
26
|
+
if (c?.type === "tool_call" || c?.type === "tool_use") {
|
|
27
|
+
return `→ ${c.name ?? "tool"}${toolHint(c.input ?? c.args)}`;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
parseResult(stdout, stderr, exitCode) {
|
|
33
|
+
// stdout is an NDJSON stream; the `result` event carries the final answer.
|
|
34
|
+
const events = parseNdjson(stdout);
|
|
35
|
+
const result = events.find((e) => e?.type === "result");
|
|
36
|
+
return {
|
|
37
|
+
stdout: result?.result ?? stdout,
|
|
38
|
+
stderr,
|
|
39
|
+
exit_code: exitCode,
|
|
40
|
+
files_changed: [],
|
|
41
|
+
duration_s: 0,
|
|
42
|
+
cost_estimate_usd: null,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
registerAdapter(CursorAdapter);
|
|
47
|
+
//# sourceMappingURL=cursor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/adapters/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAsB,MAAM,WAAW,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD;;;;;;GAMG;AACH,MAAM,OAAO,aAAc,SAAQ,UAAU;IAC3C,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC;IAEvC,YAAY,CAAC,MAAc,EAAE,QAAgB,EAAE,SAAkC,EAAE;QACjF,MAAM,GAAG,GAAG,CAAC,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QACxF,IAAI,IAAI,CAAC,KAAK;YAAE,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAChD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,kFAAkF;IACzE,WAAW,CAAC,IAAY;QAC/B,MAAM,EAAE,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QAChC,IAAI,EAAE,EAAE,IAAI,KAAK,WAAW;YAAE,OAAO,IAAI,CAAC;QAC1C,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,CAAC;YAC1C,IAAI,CAAC,EAAE,IAAI,KAAK,WAAW,IAAI,CAAC,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;gBACtD,OAAO,KAAK,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/D,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,WAAW,CAAC,MAAc,EAAE,MAAc,EAAE,QAAgB;QAC1D,2EAA2E;QAC3E,MAAM,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC;QACxD,OAAO;YACL,MAAM,EAAE,MAAM,EAAE,MAAM,IAAI,MAAM;YAChC,MAAM;YACN,SAAS,EAAE,QAAQ;YACnB,aAAa,EAAE,EAAE;YACjB,UAAU,EAAE,CAAC;YACb,iBAAiB,EAAE,IAAI;SACxB,CAAC;IACJ,CAAC;;AAGH,eAAe,CAAC,aAAa,CAAC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** In-process FakeAdapter for tests / offline demos. */
|
|
2
|
+
import { CLIAdapter, type AdapterResult } from "./base.js";
|
|
3
|
+
export declare class FakeAdapterError extends Error {
|
|
4
|
+
}
|
|
5
|
+
export declare class FakeAdapter extends CLIAdapter {
|
|
6
|
+
static readonly adapterName = "fake";
|
|
7
|
+
private static scripts;
|
|
8
|
+
private static invocationsByAgent;
|
|
9
|
+
static script(agent_id: string, results: AdapterResult[]): void;
|
|
10
|
+
static invocationsFor(agent_id: string): Array<{
|
|
11
|
+
prompt: string;
|
|
12
|
+
workdir: string;
|
|
13
|
+
}>;
|
|
14
|
+
static resetAll(): void;
|
|
15
|
+
buildCommand(): string[];
|
|
16
|
+
parseResult(stdout: string, stderr: string, exit_code: number): AdapterResult;
|
|
17
|
+
run(prompt: string, workdir: string): Promise<AdapterResult>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=fake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake.d.ts","sourceRoot":"","sources":["../../src/adapters/fake.ts"],"names":[],"mappings":"AAAA,wDAAwD;AAExD,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAG3D,qBAAa,gBAAiB,SAAQ,KAAK;CAAG;AAE9C,qBAAa,WAAY,SAAQ,UAAU;IACzC,MAAM,CAAC,QAAQ,CAAC,WAAW,UAAU;IAErC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAsC;IAC5D,OAAO,CAAC,MAAM,CAAC,kBAAkB,CAAiE;IAElG,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,IAAI;IAO/D,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;IAInF,MAAM,CAAC,QAAQ,IAAI,IAAI;IAKvB,YAAY,IAAI,MAAM,EAAE;IAIxB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,aAAa;IAI9D,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;CAa5E"}
|