qantara 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ahmed Gamil (ahmed.gamil.codes@gmail.com)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,272 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/ahmeedgamil/qantara/main/assets/qantara_logo.png" alt="Qantara" width="300">
3
+ <br>
4
+ <em>An arched bridge between your coding agents.</em>
5
+ </p>
6
+
7
+ <p align="center">
8
+ <!-- After `npm publish`, replace this static badge with the dynamic one:
9
+ <a href="https://www.npmjs.com/package/qantara"><img alt="npm" src="https://img.shields.io/npm/v/qantara"></a> -->
10
+ <img alt="version 0.1.0" src="https://img.shields.io/badge/version-0.1.0-blue">
11
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-green.svg"></a>
12
+ <img alt="Node.js >= 18" src="https://img.shields.io/badge/node-%E2%89%A518-339933?logo=node.js&logoColor=white">
13
+ <img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-5.x-3178C6?logo=typescript&logoColor=white">
14
+ <img alt="MCP server" src="https://img.shields.io/badge/MCP-server-8A2BE2">
15
+ <img alt="Bridges Claude, Codex, Gemini" src="https://img.shields.io/badge/bridges-Claude%20%C2%B7%20Codex%20%C2%B7%20Gemini-FF6B35">
16
+ </p>
17
+
18
+ ---
19
+
20
+ An MCP server that lets coding agents **delegate tasks to each other**. Claude Code can
21
+ hand a task to Codex or Gemini, Codex can hand a task to Claude, and the design extends
22
+ to any other agent (Aider, …) by adding a small adapter.
23
+
24
+ It works by shelling out to each tool's **headless CLI** (`claude -p`, `codex exec`,
25
+ `gemini -p`), so it **reuses your existing CLI logins** — no extra API keys, no separate
26
+ per-token billing.
27
+
28
+ ```
29
+ Claude Code ──(MCP tool: ask_codex)──► qantara ──► codex exec --json ──► Codex
30
+ Codex ──(MCP tool: ask_claude)─► qantara ──► claude -p --json ──► Claude Code
31
+ Gemini ──(MCP tool: ask_codex)──► qantara ──► ...
32
+ ```
33
+
34
+ ## Setup (one command)
35
+
36
+ ```powershell
37
+ npm i -g qantara # or run from a clone: npm install && npm run build
38
+ qantara setup # detects claude/codex/gemini, registers the bridge in each
39
+ ```
40
+
41
+ `setup` detects which agent CLIs are installed, registers qantara into each host
42
+ (exposing the *other* agents as tools), pre-approves the tools so nothing prompts,
43
+ and forwards your proxy env to Codex if you have one. It backs up every file it
44
+ touches (`*.qantara.bak`), is idempotent, and `qantara setup --dry-run` shows the
45
+ plan without writing. Restart your hosts afterwards — MCP servers load at session
46
+ start. The sections below describe what it writes, for manual setup or auditing.
47
+
48
+ ## The control model
49
+
50
+ There is no central controller — control follows whoever you talk to:
51
+
52
+ ```
53
+ You (the human)
54
+ └── Host agent — whichever one you opened (Claude Code OR Codex)
55
+ └── qantara (dumb pipe, controls nothing)
56
+ └── Delegated agent (contractor: gets a brief, works, reports, exits)
57
+ └── can delegate again, down to a depth limit
58
+ ```
59
+
60
+ - **You** own all policy: which agents exist, what they may do, every limit. Policy is
61
+ written in config files *before* anything runs; there is no API for an agent to change
62
+ it at runtime.
63
+ - **The host agent** is the brain for the session: it decides when to delegate, writes the
64
+ brief, and judges the result. The roles are symmetric — open Claude Code and Claude
65
+ commands Codex; open Codex and Codex commands Claude.
66
+ - **The bridge makes no decisions** about the work. It translates one MCP tool call into
67
+ one CLI invocation and enforces mechanical guards (depth, timeout, output cap). All
68
+ intelligence and all safety policy live in the agents and in your config.
69
+ - **The delegated agent** is autonomous *within* its task, but its powers (sandbox,
70
+ permission mode) were fixed by your config before it started — not by the caller.
71
+
72
+ Delegation is a *letter, not a phone call*: the delegated agent gets only the task text,
73
+ none of the caller's conversation. Write briefs accordingly (file paths, constraints,
74
+ acceptance criteria). By default the bridge tells delegated agents to **reply with
75
+ questions instead of guessing** when a task is ambiguous; answer them with a
76
+ `continue_session: true` follow-up call.
77
+
78
+ ## Prerequisites
79
+
80
+ - Node.js 18+
81
+ - The agent CLIs you want to bridge, installed and logged in:
82
+ - `npm i -g @anthropic-ai/claude-code` (`claude`)
83
+ - `npm i -g @openai/codex` (`codex`)
84
+ - `npm i -g @google/gemini-cli` (`gemini`) — log in by running `gemini` once
85
+ interactively (Google account), or set `GEMINI_API_KEY`
86
+
87
+ ## Build
88
+
89
+ ```powershell
90
+ npm install
91
+ npm run build
92
+ ```
93
+
94
+ ## Manual registration (what `setup` writes)
95
+
96
+ ### Into Claude Code (gives Claude `ask_codex` / `ask_gemini`)
97
+
98
+ ```powershell
99
+ claude mcp add -s user qantara -e BRIDGE_EXPOSE=codex,gemini -- node "<path-to>/qantara/dist/server.js"
100
+ ```
101
+
102
+ To skip the per-session permission prompt, allowlist the server in
103
+ `~/.claude/settings.json` (one entry covers all its tools):
104
+
105
+ ```json
106
+ {
107
+ "permissions": {
108
+ "allow": ["mcp__qantara"]
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Into Codex (gives Codex `ask_claude` / `ask_gemini`)
114
+
115
+ Add to `~/.codex/config.toml`:
116
+
117
+ ```toml
118
+ [mcp_servers.qantara]
119
+ command = "node"
120
+ args = ['<path-to>/qantara/dist/server.js']
121
+ enabled = true
122
+ startup_timeout_sec = 120
123
+ tool_timeout_sec = 600
124
+ # Required for headless `codex exec`: without this, Codex auto-cancels the
125
+ # MCP tool call ("user cancelled MCP tool call") because there is no human
126
+ # to approve it. Also removes the prompt in interactive Codex.
127
+ # Available since Codex 0.122.0.
128
+ default_tools_approval_mode = "approve"
129
+
130
+ [mcp_servers.qantara.env]
131
+ BRIDGE_EXPOSE = "claude"
132
+ # Codex spawns MCP servers with a stripped environment (a fixed whitelist of
133
+ # core OS vars). If your `claude` login needs a proxy (HTTP_PROXY/HTTPS_PROXY),
134
+ # you MUST re-declare it here or the spawned claude gets
135
+ # "API Error: 403 Request not allowed".
136
+ # HTTP_PROXY = "http://user:pass@host:port"
137
+ # HTTPS_PROXY = "http://user:pass@host:port"
138
+ # NO_PROXY = "localhost,127.0.0.1,::1"
139
+ ```
140
+
141
+ > The general rule: **the caller's side pre-approves the tool call** (Claude's
142
+ > `permissions.allow`, Codex's `default_tools_approval_mode`); **the delegated agent never
143
+ > prompts** — its behavior is governed by the `BRIDGE_*` policy below. If you expose both
144
+ > agents in one host (`BRIDGE_EXPOSE = "all"`), allowlist both `ask_*` tool names.
145
+
146
+ ## Tools
147
+
148
+ ### `ask_<agent>` — delegate a task
149
+
150
+ | Param | Type | Notes |
151
+ | ------------------ | ----------------------------------------------- | ------------------------------------------------ |
152
+ | `task` | string (required) | The work to delegate. Include all context — the agent sees nothing else. |
153
+ | `cwd` | string | Working directory for the agent (set it to your project to work on the same files). |
154
+ | `continue_session` | boolean | Resume the last session with this agent (sequential shorthand). |
155
+ | `session_id` | string | Resume an exact session (id from a result's footer). Takes precedence over `continue_session`; use with parallel sessions. |
156
+ | `background` | boolean | Don't block: returns a job id immediately. Poll with `check_job`. For long tasks. |
157
+ | `model` | string | Override the model (e.g. `gpt-5.5`, `opus`). |
158
+ | `reasoning` | `low` \| `medium` \| `high` | Maps to Codex `model_reasoning_effort` / Claude `--effort`. |
159
+ | `thinking` | `off` \| `think` \| `think_hard` \| `ultrathink` | **Claude only** — extended thinking. |
160
+
161
+ Blocking calls time out after `BRIDGE_TIMEOUT_MS` (10 min default). For anything that
162
+ might run longer, use `background: true` — background jobs get `BRIDGE_JOB_TIMEOUT_MS`
163
+ (1 h default) and the caller keeps working while they run.
164
+
165
+ ### `check_job` / `cancel_job` — manage background jobs
166
+
167
+ - `check_job` with a `job_id`: status, and the agent's full result once finished.
168
+ - `check_job` without arguments: list all jobs of the session.
169
+ - `cancel_job`: kill a running job.
170
+
171
+ Jobs live in the bridge process's memory: they last for your host session and are gone
172
+ after a restart. There is no push notification (MCP is request/response) — the caller
173
+ polls between its own steps.
174
+
175
+ ## Working on the same project
176
+
177
+ Pass your project path as `cwd` and both agents edit the same real files. Within one
178
+ delegation there is no write conflict — the caller is paused (or, for background jobs,
179
+ should avoid editing the same areas). Two habits make this reliable:
180
+
181
+ 1. **Re-read changed files after a delegation returns.** The caller's in-context copy is
182
+ stale the moment the child edits the file.
183
+ 2. **One writer per area at a time.** There is no locking and no git isolation — the
184
+ working tree and the git index are shared. (Worktree isolation is on the roadmap.)
185
+
186
+ ## Configuration (environment variables)
187
+
188
+ Set these in the host's MCP registration (see above). They are read once at startup —
189
+ **callers cannot change policy at runtime.**
190
+
191
+ | Variable | Default | Purpose |
192
+ | ------------------------- | ----------------- | ---------------------------------------------------- |
193
+ | `BRIDGE_EXPOSE` | `all` | Comma list of agents to expose (`codex`, `claude`, `gemini`). |
194
+ | `BRIDGE_MAX_DEPTH` | `3` | Max agent→agent recursion depth (loop guard). |
195
+ | `BRIDGE_TIMEOUT_MS` | `600000` | Timeout per blocking call (10 min). |
196
+ | `BRIDGE_JOB_TIMEOUT_MS` | `3600000` | Timeout per background job (1 h). |
197
+ | `BRIDGE_MAX_OUTPUT_BYTES` | `1000000` | Output cap (protects the caller's context). |
198
+ | `BRIDGE_CLARIFY` | `1` | Tell delegated agents to ask instead of guess. `0` to disable. |
199
+ | `BRIDGE_SANDBOX` | `workspace-write` | Codex sandbox (`read-only`/`workspace-write`/`danger-full-access`). |
200
+ | `BRIDGE_CLAUDE_PERMISSION`| `acceptEdits` | Claude permission mode (`bypassPermissions` for full autonomy). |
201
+ | `BRIDGE_GEMINI_APPROVAL` | `auto_edit` | Gemini approval mode (`default`/`auto_edit`/`yolo`/`plan`). |
202
+ | `BRIDGE_GEMINI_MODEL` | (CLI default) | Default Gemini model. |
203
+ | `BRIDGE_CODEX_MODEL` | (CLI default) | Default Codex model. |
204
+ | `BRIDGE_CODEX_REASONING` | (CLI default) | Default Codex reasoning effort. |
205
+ | `BRIDGE_CLAUDE_MODEL` | (CLI default) | Default Claude model. |
206
+ | `BRIDGE_DEBUG` | (off) | Path of a file to append diagnostic logs to. |
207
+
208
+ ## Security notes
209
+
210
+ - **The caller's sandbox does not propagate.** Hosts run MCP servers *outside* their own
211
+ sandbox, so a tightly sandboxed Codex session can still delegate to a Claude that runs
212
+ with normal user privileges (and vice versa). A delegated agent's limits come entirely
213
+ from the `BRIDGE_*` policy you configured — never from the caller's restrictions. Choose
214
+ `BRIDGE_SANDBOX` / `BRIDGE_CLAUDE_PERMISSION` as if the delegated agent were launched
215
+ directly by you, because effectively it is.
216
+ - **Codex delegate:** headless Codex never prompts — actions outside its sandbox simply
217
+ fail. Note that `workspace-write` blocks network for shell commands by default (e.g.
218
+ `npm install` inside a task may fail).
219
+ - **Claude delegate:** `acceptEdits` auto-approves file edits but refuses Bash commands
220
+ outside your allowlist (headless mode cannot prompt). `bypassPermissions` removes all
221
+ gates — use deliberately.
222
+ - **Gemini delegate:** `auto_edit` auto-approves edits; shell commands are denied (no
223
+ prompt is possible headless). `yolo` approves everything. The bridge passes
224
+ `--skip-trust` so gemini's folder-trust feature doesn't silently downgrade the
225
+ approval mode — your `BRIDGE_GEMINI_APPROVAL` is the single source of policy.
226
+ - **Loop guard:** `BRIDGE_DEPTH` is threaded into each spawned child and incremented; at
227
+ `BRIDGE_MAX_DEPTH` further `ask_*` calls are refused, so A→B→A→B recursion cannot burn
228
+ your subscriptions. There is no per-session call-count or cost budget yet — the host
229
+ agent (and your subscription limits) govern how many delegations happen.
230
+
231
+ ## Troubleshooting
232
+
233
+ | Symptom | Cause / fix |
234
+ | --- | --- |
235
+ | Codex replies "user cancelled MCP tool call" | Headless Codex auto-cancels MCP approval prompts. Set `default_tools_approval_mode = "approve"` on the server entry in `~/.codex/config.toml`. |
236
+ | Delegated `claude` fails: `API Error: 403 Request not allowed` | Your Anthropic traffic needs a proxy, and Codex stripped `HTTP_PROXY`/`HTTPS_PROXY` from the bridge's env. Re-declare them in `[mcp_servers.qantara.env]`. |
237
+ | `codex exec` hangs on "Reading additional input from stdin..." | When scripting Codex, close stdin (`$null | codex exec …` in PowerShell, `codex exec … < /dev/null` in sh). |
238
+ | `continue_session` doesn't resume across separate `codex exec` runs | Session ids live in the bridge process's memory; each headless run spawns a fresh bridge. Works as expected in interactive hosts. |
239
+ | A blocking call dies at 10 minutes | Use `background: true` (1 h budget), or raise `BRIDGE_TIMEOUT_MS`. |
240
+ | `ask_gemini` fails with exit code 41 / "set an Auth method" | The gemini CLI isn't logged in. Run `gemini` once interactively, or set `GEMINI_API_KEY` in the bridge's env block. |
241
+ | Gemini `continue_session` doesn't find the session | Gemini sessions are project-scoped (keyed by cwd) — resume only works with the same `cwd` as the original call. |
242
+
243
+ ## Adding another agent
244
+
245
+ 1. Create `src/runners/<name>.ts` implementing the `AgentRunner` interface from
246
+ [`src/registry.ts`](src/registry.ts).
247
+ 2. `register(<name>Runner)` in [`src/server.ts`](src/server.ts).
248
+
249
+ No changes to the MCP layer or core are needed — an `ask_<name>` tool appears automatically.
250
+
251
+ ## Smoke tests
252
+
253
+ ```powershell
254
+ node smoke.mjs # calls each runner directly with a trivial task
255
+ node mcp-test.mjs # connects an MCP client, lists tools, calls one
256
+ node mcp-test-claude.mjs # drives the claude runner through MCP
257
+ node mcp-test-gemini.mjs # drives the gemini runner through MCP
258
+ node mcp-test-jobs.mjs # background jobs: start, poll, result, cancel, list
259
+ node mcp-test-session.mjs # explicit resume: teach a word, resume by session_id
260
+ ```
261
+
262
+ ## Roadmap
263
+
264
+ - **Git worktree isolation** — run each delegation in a temp worktree and return a
265
+ reviewable diff instead of editing the shared tree.
266
+ - Live progress for background jobs (parse the child's JSON stream incrementally).
267
+ - Per-session call-count / cost budget guard.
268
+ - More adapters (Aider, …). Gemini CLI is already included.
269
+
270
+ ## License
271
+
272
+ MIT
package/dist/cli.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * qantara CLI entry point.
4
+ * qantara → run the MCP server on stdio (what hosts invoke)
5
+ * qantara setup → detect installed agent CLIs and register the bridge
6
+ * into each host's config (--dry-run to preview)
7
+ * qantara --version → print the package version
8
+ */
9
+ import { readFileSync } from "node:fs";
10
+ import { dirname, join } from "node:path";
11
+ import { fileURLToPath } from "node:url";
12
+ const cmd = process.argv[2];
13
+ if (cmd === "--version" || cmd === "-v" || cmd === "version") {
14
+ const pkg = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "package.json"), "utf8"));
15
+ process.stdout.write(`qantara ${pkg.version}\n`);
16
+ }
17
+ else if (cmd === "setup") {
18
+ const { runSetup } = await import("./setup.js");
19
+ await runSetup(process.argv.slice(3));
20
+ }
21
+ else if (cmd === "--help" || cmd === "-h" || cmd === "help") {
22
+ process.stdout.write("qantara — an MCP bridge that lets coding agents delegate to each other\n\n" +
23
+ "Usage:\n" +
24
+ " qantara Run the MCP server on stdio (hosts invoke this)\n" +
25
+ " qantara setup Register the bridge into Claude Code / Codex / Gemini\n" +
26
+ " qantara setup --dry-run Show what setup would change, without writing\n" +
27
+ " qantara --version Print the package version\n");
28
+ }
29
+ else {
30
+ await import("./server.js");
31
+ }
32
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;GAMG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE5B,IAAI,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CACV,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,EACnE,MAAM,CACP,CACF,CAAC;IACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,GAAG,CAAC,OAAO,IAAI,CAAC,CAAC;AACnD,CAAC;KAAM,IAAI,GAAG,KAAK,OAAO,EAAE,CAAC;IAC3B,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACxC,CAAC;KAAM,IAAI,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,MAAM,EAAE,CAAC;IAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,4EAA4E;QAC1E,UAAU;QACV,wEAAwE;QACxE,8EAA8E;QAC9E,6EAA6E;QAC7E,kDAAkD,CACrD,CAAC;AACJ,CAAC;KAAM,CAAC;IACN,MAAM,MAAM,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Central, env-driven configuration. All knobs are environment variables so the
3
+ * same binary can be registered into different hosts with different scoping
4
+ * (e.g. expose only `codex` inside Claude Code, only `claude` inside Codex).
5
+ */
6
+ function num(name, fallback) {
7
+ const raw = process.env[name];
8
+ if (!raw)
9
+ return fallback;
10
+ const n = Number(raw);
11
+ return Number.isFinite(n) ? n : fallback;
12
+ }
13
+ function list(name) {
14
+ const raw = process.env[name];
15
+ if (!raw)
16
+ return [];
17
+ return raw
18
+ .split(",")
19
+ .map((s) => s.trim())
20
+ .filter(Boolean);
21
+ }
22
+ export const config = {
23
+ /** Hard timeout per blocking agent invocation. */
24
+ timeoutMs: num("BRIDGE_TIMEOUT_MS", 600_000),
25
+ /** Hard timeout per background job (longer: jobs don't block the caller). */
26
+ jobTimeoutMs: num("BRIDGE_JOB_TIMEOUT_MS", 3_600_000),
27
+ /** Max bytes of captured output, to protect the calling model's context. */
28
+ maxOutputBytes: num("BRIDGE_MAX_OUTPUT_BYTES", 1_000_000),
29
+ /** Which agents to expose as tools (empty => all registered). */
30
+ expose: list("BRIDGE_EXPOSE"),
31
+ /**
32
+ * Tell delegated agents that a supervising agent can answer follow-up
33
+ * questions (via session resume), so they ask instead of guessing when a
34
+ * task is ambiguous. Disable with BRIDGE_CLARIFY=0.
35
+ */
36
+ clarify: process.env.BRIDGE_CLARIFY !== "0",
37
+ /** Recursion / fan-out guard. */
38
+ maxDepth: num("BRIDGE_MAX_DEPTH", 3),
39
+ /** Current depth, threaded in from a parent bridge invocation (0 at the top). */
40
+ depth: num("BRIDGE_DEPTH", 0),
41
+ /** Codex execution settings. */
42
+ codex: {
43
+ sandbox: process.env.BRIDGE_SANDBOX ?? "workspace-write",
44
+ model: process.env.BRIDGE_CODEX_MODEL, // undefined => CLI default
45
+ reasoning: process.env.BRIDGE_CODEX_REASONING, // undefined => CLI default
46
+ },
47
+ /** Claude execution settings. */
48
+ claude: {
49
+ permissionMode: process.env.BRIDGE_CLAUDE_PERMISSION ?? "acceptEdits",
50
+ model: process.env.BRIDGE_CLAUDE_MODEL, // undefined => CLI default
51
+ },
52
+ /** Gemini execution settings. */
53
+ gemini: {
54
+ // default | auto_edit | yolo | plan — auto_edit mirrors Claude's acceptEdits.
55
+ approvalMode: process.env.BRIDGE_GEMINI_APPROVAL ?? "auto_edit",
56
+ model: process.env.BRIDGE_GEMINI_MODEL, // undefined => CLI default
57
+ },
58
+ };
59
+ /** Env to pass to a spawned child agent so depth increments and propagates. */
60
+ export function childEnv(currentDepth) {
61
+ return { BRIDGE_DEPTH: String(currentDepth + 1) };
62
+ }
63
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,SAAS,GAAG,CAAC,IAAY,EAAE,QAAgB;IACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,QAAQ,CAAC;IAC1B,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtB,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AAC3C,CAAC;AAED,SAAS,IAAI,CAAC,IAAY;IACxB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,GAAG;SACP,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;SACpB,MAAM,CAAC,OAAO,CAAC,CAAC;AACrB,CAAC;AAED,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,kDAAkD;IAClD,SAAS,EAAE,GAAG,CAAC,mBAAmB,EAAE,OAAO,CAAC;IAC5C,6EAA6E;IAC7E,YAAY,EAAE,GAAG,CAAC,uBAAuB,EAAE,SAAS,CAAC;IACrD,4EAA4E;IAC5E,cAAc,EAAE,GAAG,CAAC,yBAAyB,EAAE,SAAS,CAAC;IAEzD,iEAAiE;IACjE,MAAM,EAAE,IAAI,CAAC,eAAe,CAAC;IAE7B;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,KAAK,GAAG;IAE3C,iCAAiC;IACjC,QAAQ,EAAE,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC;IACpC,iFAAiF;IACjF,KAAK,EAAE,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC;IAE7B,gCAAgC;IAChC,KAAK,EAAE;QACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,iBAAiB;QACxD,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,2BAA2B;QAClE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,2BAA2B;KAC3E;IAED,iCAAiC;IACjC,MAAM,EAAE;QACN,cAAc,EAAE,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,aAAa;QACrE,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,2BAA2B;KACpE;IAED,iCAAiC;IACjC,MAAM,EAAE;QACN,8EAA8E;QAC9E,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,WAAW;QAC/D,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB,EAAE,2BAA2B;KACpE;CACO,CAAC;AAEX,+EAA+E;AAC/E,MAAM,UAAU,QAAQ,CAAC,YAAoB;IAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC"}
package/dist/debug.js ADDED
@@ -0,0 +1,25 @@
1
+ import { appendFileSync } from "node:fs";
2
+ const target = process.env.BRIDGE_DEBUG;
3
+ // Startup diagnostic. Hosts may launch the bridge with a stripped environment
4
+ // (Codex passes only a whitelist of core OS vars), so logging the received env
5
+ // keys up front makes those problems visible without a debugger.
6
+ debug(`bridge process started | ` +
7
+ `BRIDGE_EXPOSE=${process.env.BRIDGE_EXPOSE ?? "(unset)"} | ` +
8
+ `BRIDGE_DEPTH=${process.env.BRIDGE_DEPTH ?? "(unset)"} | ` +
9
+ `cwd=${process.cwd()} | ` +
10
+ `envKeys=${Object.keys(process.env).sort().join(",")}`);
11
+ /**
12
+ * Appends a timestamped line to the file named in BRIDGE_DEBUG (if set).
13
+ * Used to diagnose runs where stderr is not visible (e.g. spawned by a host).
14
+ */
15
+ export function debug(msg) {
16
+ if (!target)
17
+ return;
18
+ try {
19
+ appendFileSync(target, `[${new Date().toISOString()}] ${msg}\n`);
20
+ }
21
+ catch {
22
+ // best-effort logging only
23
+ }
24
+ }
25
+ //# sourceMappingURL=debug.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug.js","sourceRoot":"","sources":["../src/debug.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAEzC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;AAExC,8EAA8E;AAC9E,+EAA+E;AAC/E,iEAAiE;AACjE,KAAK,CACH,2BAA2B;IACzB,iBAAiB,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS,KAAK;IAC5D,gBAAgB,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,SAAS,KAAK;IAC1D,OAAO,OAAO,CAAC,GAAG,EAAE,KAAK;IACzB,WAAW,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CACzD,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,KAAK,CAAC,GAAW;IAC/B,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC;QACH,cAAc,CAAC,MAAM,EAAE,IAAI,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;AACH,CAAC"}
package/dist/exec.js ADDED
@@ -0,0 +1,105 @@
1
+ import { spawn } from "node:child_process";
2
+ import { debug } from "./debug.js";
3
+ export class ExecError extends Error {
4
+ result;
5
+ constructor(message, result) {
6
+ super(message);
7
+ this.result = result;
8
+ this.name = "ExecError";
9
+ }
10
+ }
11
+ /**
12
+ * Spawn a command with an args array (never a shell string) so arbitrary task
13
+ * text cannot be interpreted by the shell. Enforces a timeout and an output cap.
14
+ *
15
+ * On Windows, `.cmd` shims (npm-installed CLIs) are not directly executable by
16
+ * spawn without a shell, so we resolve the actual command up front via PATHEXT.
17
+ */
18
+ export function runCommand(command, args, opts) {
19
+ return new Promise((resolve, reject) => {
20
+ // On Windows, .cmd/.bat shims require shell:true to be invoked. We keep the
21
+ // args array form so values are still passed as discrete argv entries.
22
+ const isWindows = process.platform === "win32";
23
+ debug(`spawn command=${command} args=${JSON.stringify(args)} cwd=${opts.cwd ?? "(inherit)"} ` +
24
+ `shell=${isWindows} PATH_present=${Boolean(process.env.PATH || process.env.Path)}`);
25
+ const child = spawn(command, args, {
26
+ cwd: opts.cwd,
27
+ env: { ...process.env, ...opts.env },
28
+ shell: isWindows, // needed for npm .cmd shims on Windows
29
+ windowsHide: true,
30
+ });
31
+ let stdout = "";
32
+ let stderr = "";
33
+ let truncated = false;
34
+ let timedOut = false;
35
+ let cancelled = false;
36
+ let settled = false;
37
+ const cap = opts.maxOutputBytes;
38
+ const timer = setTimeout(() => {
39
+ timedOut = true;
40
+ child.kill("SIGKILL");
41
+ }, opts.timeoutMs);
42
+ const onAbort = () => {
43
+ cancelled = true;
44
+ child.kill("SIGKILL");
45
+ };
46
+ if (opts.signal) {
47
+ if (opts.signal.aborted)
48
+ onAbort();
49
+ else
50
+ opts.signal.addEventListener("abort", onAbort, { once: true });
51
+ }
52
+ if (opts.stdin !== undefined) {
53
+ child.stdin.write(opts.stdin);
54
+ child.stdin.end();
55
+ }
56
+ child.stdout.on("data", (chunk) => {
57
+ if (stdout.length < cap) {
58
+ stdout += chunk.toString("utf8");
59
+ if (stdout.length >= cap) {
60
+ stdout = stdout.slice(0, cap);
61
+ truncated = true;
62
+ }
63
+ }
64
+ });
65
+ child.stderr.on("data", (chunk) => {
66
+ if (stderr.length < cap) {
67
+ stderr += chunk.toString("utf8");
68
+ if (stderr.length >= cap)
69
+ stderr = stderr.slice(0, cap);
70
+ }
71
+ });
72
+ child.on("error", (err) => {
73
+ if (settled)
74
+ return;
75
+ settled = true;
76
+ clearTimeout(timer);
77
+ debug(`child error for ${command}: ${err.message}`);
78
+ // ENOENT => binary missing; surface a clear, actionable message.
79
+ const hint = err.code === "ENOENT"
80
+ ? ` (is "${command}" installed and on PATH?)`
81
+ : "";
82
+ reject(new ExecError(`Failed to launch "${command}"${hint}: ${err.message}`));
83
+ });
84
+ child.on("close", (code) => {
85
+ if (settled)
86
+ return;
87
+ settled = true;
88
+ clearTimeout(timer);
89
+ debug(`child close ${command} code=${code} stdoutLen=${stdout.length} ` +
90
+ `stderrTail=${JSON.stringify(stderr.slice(-300))}`);
91
+ opts.signal?.removeEventListener("abort", onAbort);
92
+ const result = { code, stdout, stderr, timedOut, truncated };
93
+ if (cancelled) {
94
+ reject(new ExecError(`"${command}" was cancelled`, result));
95
+ return;
96
+ }
97
+ if (timedOut) {
98
+ reject(new ExecError(`"${command}" timed out after ${opts.timeoutMs}ms`, result));
99
+ return;
100
+ }
101
+ resolve(result);
102
+ });
103
+ });
104
+ }
105
+ //# sourceMappingURL=exec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exec.js","sourceRoot":"","sources":["../src/exec.ts"],"names":[],"mappings":"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"}
package/dist/jobs.js ADDED
@@ -0,0 +1,22 @@
1
+ const jobs = new Map();
2
+ let counter = 0;
3
+ export function createJob(agent, task) {
4
+ counter += 1;
5
+ const job = {
6
+ id: `${agent}-${counter}`,
7
+ agent,
8
+ taskSummary: task.split("\n")[0].slice(0, 120),
9
+ status: "running",
10
+ startedAt: Date.now(),
11
+ abort: new AbortController(),
12
+ };
13
+ jobs.set(job.id, job);
14
+ return job;
15
+ }
16
+ export function getJob(id) {
17
+ return jobs.get(id);
18
+ }
19
+ export function listJobs() {
20
+ return [...jobs.values()];
21
+ }
22
+ //# sourceMappingURL=jobs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../src/jobs.ts"],"names":[],"mappings":"AAsBA,MAAM,IAAI,GAAG,IAAI,GAAG,EAAe,CAAC;AACpC,IAAI,OAAO,GAAG,CAAC,CAAC;AAEhB,MAAM,UAAU,SAAS,CAAC,KAAa,EAAE,IAAY;IACnD,OAAO,IAAI,CAAC,CAAC;IACb,MAAM,GAAG,GAAQ;QACf,EAAE,EAAE,GAAG,KAAK,IAAI,OAAO,EAAE;QACzB,KAAK;QACL,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC;QAC9C,MAAM,EAAE,SAAS;QACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,KAAK,EAAE,IAAI,eAAe,EAAE;KAC7B,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;IACtB,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,MAAM,CAAC,EAAU;IAC/B,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The single abstraction the whole bridge is built on. Every backend agent
3
+ * (Codex, Claude, and later Gemini / Antigravity / Aider / ...) is just an
4
+ * adapter implementing this interface. Adding a new agent = one new adapter
5
+ * file + one registry entry, with no changes to the MCP server or core.
6
+ */
7
+ const registry = new Map();
8
+ export function register(runner) {
9
+ registry.set(runner.name, runner);
10
+ }
11
+ export function getRunner(name) {
12
+ return registry.get(name);
13
+ }
14
+ /**
15
+ * Returns the runners that should be exposed as tools, filtered by the
16
+ * `expose` allow-list (empty/`all` => every registered runner).
17
+ */
18
+ export function exposedRunners(expose) {
19
+ const all = [...registry.values()];
20
+ if (expose.length === 0 || expose.includes("all"))
21
+ return all;
22
+ return all.filter((r) => expose.includes(r.name));
23
+ }
24
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../src/registry.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAmDH,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;AAEhD,MAAM,UAAU,QAAQ,CAAC,MAAmB;IAC1C,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,MAAgB;IAC7C,MAAM,GAAG,GAAG,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACnC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC;IAC9D,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AACpD,CAAC"}