@pythonluvr/openwar 0.4.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 +21 -0
- package/README.md +297 -0
- package/bin/openwar +45 -0
- package/dist/adapters/anthropic.d.ts +15 -0
- package/dist/adapters/anthropic.d.ts.map +1 -0
- package/dist/adapters/anthropic.js +179 -0
- package/dist/adapters/anthropic.js.map +1 -0
- package/dist/adapters/gemini.d.ts +15 -0
- package/dist/adapters/gemini.d.ts.map +1 -0
- package/dist/adapters/gemini.js +141 -0
- package/dist/adapters/gemini.js.map +1 -0
- package/dist/adapters/grok.d.ts +6 -0
- package/dist/adapters/grok.d.ts.map +1 -0
- package/dist/adapters/grok.js +15 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +16 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +35 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/mock.d.ts +17 -0
- package/dist/adapters/mock.d.ts.map +1 -0
- package/dist/adapters/mock.js +33 -0
- package/dist/adapters/mock.js.map +1 -0
- package/dist/adapters/openai-compat.d.ts +6 -0
- package/dist/adapters/openai-compat.d.ts.map +1 -0
- package/dist/adapters/openai-compat.js +19 -0
- package/dist/adapters/openai-compat.js.map +1 -0
- package/dist/adapters/openai.d.ts +23 -0
- package/dist/adapters/openai.d.ts.map +1 -0
- package/dist/adapters/openai.js +176 -0
- package/dist/adapters/openai.js.map +1 -0
- package/dist/adapters/sse.d.ts +6 -0
- package/dist/adapters/sse.d.ts.map +1 -0
- package/dist/adapters/sse.js +45 -0
- package/dist/adapters/sse.js.map +1 -0
- package/dist/adapters/types.d.ts +7 -0
- package/dist/adapters/types.d.ts.map +1 -0
- package/dist/adapters/types.js +2 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/auth/categories.d.ts +13 -0
- package/dist/auth/categories.d.ts.map +1 -0
- package/dist/auth/categories.js +47 -0
- package/dist/auth/categories.js.map +1 -0
- package/dist/auth/check.d.ts +31 -0
- package/dist/auth/check.d.ts.map +1 -0
- package/dist/auth/check.js +59 -0
- package/dist/auth/check.js.map +1 -0
- package/dist/auth/role-scope.d.ts +18 -0
- package/dist/auth/role-scope.d.ts.map +1 -0
- package/dist/auth/role-scope.js +62 -0
- package/dist/auth/role-scope.js.map +1 -0
- package/dist/auth/wildcards.d.ts +5 -0
- package/dist/auth/wildcards.d.ts.map +1 -0
- package/dist/auth/wildcards.js +36 -0
- package/dist/auth/wildcards.js.map +1 -0
- package/dist/brief.d.ts +9 -0
- package/dist/brief.d.ts.map +1 -0
- package/dist/brief.js +514 -0
- package/dist/brief.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +585 -0
- package/dist/cli.js.map +1 -0
- package/dist/coordinator/cost-tracker.d.ts +14 -0
- package/dist/coordinator/cost-tracker.d.ts.map +1 -0
- package/dist/coordinator/cost-tracker.js +63 -0
- package/dist/coordinator/cost-tracker.js.map +1 -0
- package/dist/coordinator/driver.d.ts +37 -0
- package/dist/coordinator/driver.d.ts.map +1 -0
- package/dist/coordinator/driver.js +644 -0
- package/dist/coordinator/driver.js.map +1 -0
- package/dist/coordinator/index.d.ts +14 -0
- package/dist/coordinator/index.d.ts.map +1 -0
- package/dist/coordinator/index.js +8 -0
- package/dist/coordinator/index.js.map +1 -0
- package/dist/coordinator/plan-parser.d.ts +17 -0
- package/dist/coordinator/plan-parser.d.ts.map +1 -0
- package/dist/coordinator/plan-parser.js +44 -0
- package/dist/coordinator/plan-parser.js.map +1 -0
- package/dist/coordinator/result-aggregator.d.ts +21 -0
- package/dist/coordinator/result-aggregator.d.ts.map +1 -0
- package/dist/coordinator/result-aggregator.js +58 -0
- package/dist/coordinator/result-aggregator.js.map +1 -0
- package/dist/coordinator/retry-policy.d.ts +7 -0
- package/dist/coordinator/retry-policy.d.ts.map +1 -0
- package/dist/coordinator/retry-policy.js +17 -0
- package/dist/coordinator/retry-policy.js.map +1 -0
- package/dist/coordinator/state-machine.d.ts +63 -0
- package/dist/coordinator/state-machine.d.ts.map +1 -0
- package/dist/coordinator/state-machine.js +242 -0
- package/dist/coordinator/state-machine.js.map +1 -0
- package/dist/coordinator/types.d.ts +3 -0
- package/dist/coordinator/types.d.ts.map +1 -0
- package/dist/coordinator/types.js +5 -0
- package/dist/coordinator/types.js.map +1 -0
- package/dist/detectors/banned-phrases.d.ts +3 -0
- package/dist/detectors/banned-phrases.d.ts.map +1 -0
- package/dist/detectors/banned-phrases.js +33 -0
- package/dist/detectors/banned-phrases.js.map +1 -0
- package/dist/detectors/blocker.d.ts +3 -0
- package/dist/detectors/blocker.d.ts.map +1 -0
- package/dist/detectors/blocker.js +62 -0
- package/dist/detectors/blocker.js.map +1 -0
- package/dist/detectors/completion.d.ts +3 -0
- package/dist/detectors/completion.d.ts.map +1 -0
- package/dist/detectors/completion.js +17 -0
- package/dist/detectors/completion.js.map +1 -0
- package/dist/detectors/confirmation-summary.d.ts +3 -0
- package/dist/detectors/confirmation-summary.d.ts.map +1 -0
- package/dist/detectors/confirmation-summary.js +76 -0
- package/dist/detectors/confirmation-summary.js.map +1 -0
- package/dist/detectors/destructive.d.ts +3 -0
- package/dist/detectors/destructive.d.ts.map +1 -0
- package/dist/detectors/destructive.js +96 -0
- package/dist/detectors/destructive.js.map +1 -0
- package/dist/detectors/index.d.ts +12 -0
- package/dist/detectors/index.d.ts.map +1 -0
- package/dist/detectors/index.js +19 -0
- package/dist/detectors/index.js.map +1 -0
- package/dist/detectors/phase-marker.d.ts +3 -0
- package/dist/detectors/phase-marker.d.ts.map +1 -0
- package/dist/detectors/phase-marker.js +25 -0
- package/dist/detectors/phase-marker.js.map +1 -0
- package/dist/framework.d.ts +2 -0
- package/dist/framework.d.ts.map +1 -0
- package/dist/framework.js +34 -0
- package/dist/framework.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +25 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +83 -0
- package/dist/io.js.map +1 -0
- package/dist/mcp/client.d.ts +22 -0
- package/dist/mcp/client.d.ts.map +1 -0
- package/dist/mcp/client.js +44 -0
- package/dist/mcp/client.js.map +1 -0
- package/dist/mcp/index.d.ts +5 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +6 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/registry.d.ts +16 -0
- package/dist/mcp/registry.d.ts.map +1 -0
- package/dist/mcp/registry.js +53 -0
- package/dist/mcp/registry.js.map +1 -0
- package/dist/mcp/transport-stdio.d.ts +26 -0
- package/dist/mcp/transport-stdio.d.ts.map +1 -0
- package/dist/mcp/transport-stdio.js +138 -0
- package/dist/mcp/transport-stdio.js.map +1 -0
- package/dist/mcp/types.d.ts +90 -0
- package/dist/mcp/types.d.ts.map +1 -0
- package/dist/mcp/types.js +23 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/orchestration/handoff.d.ts +27 -0
- package/dist/orchestration/handoff.d.ts.map +1 -0
- package/dist/orchestration/handoff.js +322 -0
- package/dist/orchestration/handoff.js.map +1 -0
- package/dist/phases/blocker.d.ts +8 -0
- package/dist/phases/blocker.d.ts.map +1 -0
- package/dist/phases/blocker.js +11 -0
- package/dist/phases/blocker.js.map +1 -0
- package/dist/phases/completion.d.ts +10 -0
- package/dist/phases/completion.d.ts.map +1 -0
- package/dist/phases/completion.js +47 -0
- package/dist/phases/completion.js.map +1 -0
- package/dist/phases/destructive.d.ts +10 -0
- package/dist/phases/destructive.d.ts.map +1 -0
- package/dist/phases/destructive.js +31 -0
- package/dist/phases/destructive.js.map +1 -0
- package/dist/phases/execute.d.ts +30 -0
- package/dist/phases/execute.d.ts.map +1 -0
- package/dist/phases/execute.js +222 -0
- package/dist/phases/execute.js.map +1 -0
- package/dist/phases/intake.d.ts +16 -0
- package/dist/phases/intake.d.ts.map +1 -0
- package/dist/phases/intake.js +105 -0
- package/dist/phases/intake.js.map +1 -0
- package/dist/roles/critic.d.ts +3 -0
- package/dist/roles/critic.d.ts.map +1 -0
- package/dist/roles/critic.js +35 -0
- package/dist/roles/critic.js.map +1 -0
- package/dist/roles/executor.d.ts +3 -0
- package/dist/roles/executor.d.ts.map +1 -0
- package/dist/roles/executor.js +46 -0
- package/dist/roles/executor.js.map +1 -0
- package/dist/roles/index.d.ts +8 -0
- package/dist/roles/index.d.ts.map +1 -0
- package/dist/roles/index.js +8 -0
- package/dist/roles/index.js.map +1 -0
- package/dist/roles/planner.d.ts +3 -0
- package/dist/roles/planner.d.ts.map +1 -0
- package/dist/roles/planner.js +50 -0
- package/dist/roles/planner.js.map +1 -0
- package/dist/roles/prompt-overlay.d.ts +9 -0
- package/dist/roles/prompt-overlay.d.ts.map +1 -0
- package/dist/roles/prompt-overlay.js +25 -0
- package/dist/roles/prompt-overlay.js.map +1 -0
- package/dist/roles/registry.d.ts +8 -0
- package/dist/roles/registry.d.ts.map +1 -0
- package/dist/roles/registry.js +45 -0
- package/dist/roles/registry.js.map +1 -0
- package/dist/roles/reviewer.d.ts +3 -0
- package/dist/roles/reviewer.d.ts.map +1 -0
- package/dist/roles/reviewer.js +46 -0
- package/dist/roles/reviewer.js.map +1 -0
- package/dist/roles/types.d.ts +2 -0
- package/dist/roles/types.d.ts.map +1 -0
- package/dist/roles/types.js +4 -0
- package/dist/roles/types.js.map +1 -0
- package/dist/runner.d.ts +3 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +473 -0
- package/dist/runner.js.map +1 -0
- package/dist/sandbox/host-allowlist.d.ts +13 -0
- package/dist/sandbox/host-allowlist.d.ts.map +1 -0
- package/dist/sandbox/host-allowlist.js +85 -0
- package/dist/sandbox/host-allowlist.js.map +1 -0
- package/dist/sandbox/output-cap.d.ts +9 -0
- package/dist/sandbox/output-cap.d.ts.map +1 -0
- package/dist/sandbox/output-cap.js +66 -0
- package/dist/sandbox/output-cap.js.map +1 -0
- package/dist/sandbox/timeout.d.ts +7 -0
- package/dist/sandbox/timeout.d.ts.map +1 -0
- package/dist/sandbox/timeout.js +52 -0
- package/dist/sandbox/timeout.js.map +1 -0
- package/dist/sandbox/types.d.ts +18 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +27 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/sandbox/workdir.d.ts +9 -0
- package/dist/sandbox/workdir.d.ts.map +1 -0
- package/dist/sandbox/workdir.js +83 -0
- package/dist/sandbox/workdir.js.map +1 -0
- package/dist/state/index.d.ts +4 -0
- package/dist/state/index.d.ts.map +1 -0
- package/dist/state/index.js +4 -0
- package/dist/state/index.js.map +1 -0
- package/dist/state/paths.d.ts +5 -0
- package/dist/state/paths.d.ts.map +1 -0
- package/dist/state/paths.js +18 -0
- package/dist/state/paths.js.map +1 -0
- package/dist/state/persist.d.ts +14 -0
- package/dist/state/persist.d.ts.map +1 -0
- package/dist/state/persist.js +146 -0
- package/dist/state/persist.js.map +1 -0
- package/dist/state/transcript.d.ts +9 -0
- package/dist/state/transcript.d.ts.map +1 -0
- package/dist/state/transcript.js +34 -0
- package/dist/state/transcript.js.map +1 -0
- package/dist/tools/native/apply_patch.d.ts +18 -0
- package/dist/tools/native/apply_patch.d.ts.map +1 -0
- package/dist/tools/native/apply_patch.js +245 -0
- package/dist/tools/native/apply_patch.js.map +1 -0
- package/dist/tools/native/http_fetch.d.ts +6 -0
- package/dist/tools/native/http_fetch.d.ts.map +1 -0
- package/dist/tools/native/http_fetch.js +168 -0
- package/dist/tools/native/http_fetch.js.map +1 -0
- package/dist/tools/native/index.d.ts +9 -0
- package/dist/tools/native/index.d.ts.map +1 -0
- package/dist/tools/native/index.js +22 -0
- package/dist/tools/native/index.js.map +1 -0
- package/dist/tools/native/list_dir.d.ts +4 -0
- package/dist/tools/native/list_dir.d.ts.map +1 -0
- package/dist/tools/native/list_dir.js +175 -0
- package/dist/tools/native/list_dir.js.map +1 -0
- package/dist/tools/native/read_file.d.ts +4 -0
- package/dist/tools/native/read_file.d.ts.map +1 -0
- package/dist/tools/native/read_file.js +91 -0
- package/dist/tools/native/read_file.js.map +1 -0
- package/dist/tools/native/shell_exec.d.ts +4 -0
- package/dist/tools/native/shell_exec.d.ts.map +1 -0
- package/dist/tools/native/shell_exec.js +180 -0
- package/dist/tools/native/shell_exec.js.map +1 -0
- package/dist/tools/native/write_file.d.ts +4 -0
- package/dist/tools/native/write_file.d.ts.map +1 -0
- package/dist/tools/native/write_file.js +101 -0
- package/dist/tools/native/write_file.js.map +1 -0
- package/dist/tools/types.d.ts +48 -0
- package/dist/tools/types.d.ts.map +1 -0
- package/dist/tools/types.js +10 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +385 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/examples/README.md +73 -0
- package/examples/creative-brief.md +34 -0
- package/examples/critic-disagreement-brief.md +42 -0
- package/examples/engineering-brief.md +35 -0
- package/examples/file-editing-brief.md +33 -0
- package/examples/mcp-brief.md +34 -0
- package/examples/multi-agent-brief.md +43 -0
- package/examples/research-brief.md +35 -0
- package/openwar.md +248 -0
- package/package.json +76 -0
- package/templates/brief.md +62 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenWar contributors
|
|
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,297 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="openwar-logo.png" alt="OpenWar" width="160" height="160" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">OpenWar</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><strong>A framework and a runtime for agent behavior that doesn't go off the rails.</strong></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<a href="https://github.com/pythonluvr/openwar/releases"><img src="https://img.shields.io/github/v/release/pythonluvr/openwar?display_name=tag&sort=semver" alt="Latest release"></a>
|
|
11
|
+
<a href="https://github.com/pythonluvr/openwar/actions"><img src="https://img.shields.io/github/actions/workflow/status/pythonluvr/openwar/test.yml?branch=main" alt="Tests"></a>
|
|
12
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
OpenWar replaces eager-customer-service-rep defaults with the behavior of a senior peer. It confirms briefs before acting, breaks work into phases, asks before doing anything destructive, and writes like an adult who's busy.
|
|
16
|
+
|
|
17
|
+
This is WarBit. He runs OpenWar. He does not "happily help you with that."
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="branding/warbit-story/warbit-04-chaos.png" alt="WarBit buried in TODOs and merge conflicts" width="280" />
|
|
21
|
+
<br />
|
|
22
|
+
<em>Default agent behavior. Sycophantic, eager, drowning in half-finished context.</em>
|
|
23
|
+
</p>
|
|
24
|
+
|
|
25
|
+
You can use OpenWar two ways:
|
|
26
|
+
|
|
27
|
+
1. **As a runtime** (new in v0.2, expanded in v0.3 with tool calling). Install the package, point it at a brief, watch the phase machine enforce the framework against any BYOK LLM. The runtime is opinionated: no flag to disable Phase 3, no way to skip the Confirmation Summary.
|
|
28
|
+
2. **As a system prompt** (v0.1, still supported). Paste [`openwar.md`](./openwar.md) into Claude Code's CLAUDE.md, Cursor's rules, Hermes, OpenClaw, or anywhere else. The agent's behavior changes; nothing else does.
|
|
29
|
+
|
|
30
|
+
The framework doc and the runtime are the same source of truth. The doc tells the model what to do. The runtime makes sure it actually does it.
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<img src="branding/warbit-story/warbit-02-daily-intel.png" alt="WarBit at a 'Daily Intel' bulletin board" width="280" />
|
|
34
|
+
<br />
|
|
35
|
+
<em>Phase 0 in one image. What are you shipping today, ops?</em>
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
## Quick start (runtime)
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npx @pythonluvr/openwar run examples/creative-brief.md --adapter anthropic
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Or install:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g @pythonluvr/openwar
|
|
48
|
+
export ANTHROPIC_API_KEY=...
|
|
49
|
+
openwar run examples/engineering-brief.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Provide an API key for whichever adapter you pick:
|
|
53
|
+
|
|
54
|
+
| Adapter | Env var | Default model |
|
|
55
|
+
|-----------------|--------------------------|-----------------------|
|
|
56
|
+
| `anthropic` | `ANTHROPIC_API_KEY` | `claude-sonnet-4-6` |
|
|
57
|
+
| `openai` | `OPENAI_API_KEY` | `gpt-4o` |
|
|
58
|
+
| `gemini` | `GEMINI_API_KEY` | `gemini-2.0-flash` |
|
|
59
|
+
| `grok` | `XAI_API_KEY` | `grok-2-latest` |
|
|
60
|
+
| `openai-compat` | `OPENAI_COMPAT_API_KEY` | (specify with `--base-url`) |
|
|
61
|
+
|
|
62
|
+
`openai-compat` covers OpenRouter, Groq, Together, Ollama, vLLM, llama.cpp, and anything else speaking OpenAI's chat-completions protocol.
|
|
63
|
+
|
|
64
|
+
## What the runtime enforces
|
|
65
|
+
|
|
66
|
+
<p align="center">
|
|
67
|
+
<img src="branding/warbit-story/warbit-05-cockpit.png" alt="WarBit watching a wall of monitors" width="280" />
|
|
68
|
+
<br />
|
|
69
|
+
<em>Every turn passes through deterministic detectors. No second LLM, no judging.</em>
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
| Phase | What happens | What blocks |
|
|
73
|
+
|---------|--------------------------------------------------------------------------------------------------|-------------|
|
|
74
|
+
| Phase 0 | Agent must produce a Confirmation Summary with Objective / Deliverables / Constraints / Tools / Unknowns. | No execution until the operator accepts. |
|
|
75
|
+
| Phase 1 | Agent executes step by step. Gated mode pauses between steps; auto-pilot runs through clean ones. | Banned phrases warn. |
|
|
76
|
+
| Phase 2 | If the agent declares it's blocked, the runtime halts the session and persists state. | Resume with `openwar resume <brief_id>`. |
|
|
77
|
+
| Phase 3 | If the agent announces intent to do something destructive or out-of-directive, the runtime stops and asks for explicit yes. | Authorization can be pre-approved per category in the brief's `authorized_costs`. |
|
|
78
|
+
| Phase 4 | Agent produces a concise completion report. | None. |
|
|
79
|
+
|
|
80
|
+
If the agent skips the Confirmation Summary, the runtime asks it to restate before letting execution start.
|
|
81
|
+
|
|
82
|
+
## Tools (new in v0.3)
|
|
83
|
+
|
|
84
|
+
<p align="center">
|
|
85
|
+
<img src="branding/warbit-story/warbit-06-new-tools.png" alt="WarBit opening a glowing 'New Tools' chest" width="280" />
|
|
86
|
+
<br />
|
|
87
|
+
<em>v0.3 turned OpenWar from "constrained chat wrapper" into "real agent." The runtime now calls tools.</em>
|
|
88
|
+
</p>
|
|
89
|
+
|
|
90
|
+
The runtime ships six native tools plus a hand-rolled MCP client. Every tool call goes through:
|
|
91
|
+
|
|
92
|
+
1. Schema translation in the adapter to the provider's native function-calling format.
|
|
93
|
+
2. Authorization check against the brief's `authorized_costs` and any session-approved categories.
|
|
94
|
+
3. Sandbox execution: workdir-bounded paths, timeout enforcement, output caps, HTTP host allowlist.
|
|
95
|
+
4. Result fed back to the LLM for the next round.
|
|
96
|
+
|
|
97
|
+
If a tool requires an unauthorized category, the runtime halts into Phase 3 and prompts for `y` / `Y` / `n` (one-shot, session-wide, deny).
|
|
98
|
+
|
|
99
|
+
### Native tools
|
|
100
|
+
|
|
101
|
+
| Name | Categories required | Notes |
|
|
102
|
+
|---------------|---------------------|-------|
|
|
103
|
+
| `read_file` | `filesystem_read` | Default-allowed. Caps at `max_bytes` (1 MB default). |
|
|
104
|
+
| `write_file` | `filesystem_write` | Atomic via tmp+rename. Creates parent dirs. |
|
|
105
|
+
| `list_dir` | `filesystem_read` | Skips `.git`, `node_modules`, etc. Honors `.openwarignore`. |
|
|
106
|
+
| `shell_exec` | `shell_exec` | SIGTERM then SIGKILL on timeout. `--no-shell` disables entirely. |
|
|
107
|
+
| `http_fetch` | `http_fetch` | HTTPS only by default. Optional `~/.openwar/http-allow.json` host allowlist. |
|
|
108
|
+
| `apply_patch` | `filesystem_write` | Unified-diff applier. Rolls back on hunk failure. |
|
|
109
|
+
|
|
110
|
+
List them with `openwar tools`.
|
|
111
|
+
|
|
112
|
+
### MCP servers
|
|
113
|
+
|
|
114
|
+
Configure once with `openwar mcp add <name> <command...>` (writes to `~/.openwar/mcp.json`) or per-brief:
|
|
115
|
+
|
|
116
|
+
```yaml
|
|
117
|
+
mcp_servers:
|
|
118
|
+
- filesystem=npx -y @modelcontextprotocol/server-filesystem /allowed/dir
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Each MCP server's tools auto-register under `<name>:<tool>` and require `mcp_tool:<name>:<tool>` to call. Use `mcp_tool:<name>:*` in `authorized_costs` to pre-approve everything a server exposes.
|
|
122
|
+
|
|
123
|
+
Test a server before relying on it:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
openwar mcp test filesystem
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Multi-agent orchestration (new in v0.4)
|
|
130
|
+
|
|
131
|
+
When a brief opts into multi-agent by setting `roles:` in its frontmatter, the runtime stops running one agent against the whole brief and instead drives a small team:
|
|
132
|
+
|
|
133
|
+
- **planner** decomposes the brief into linear sub-tasks with acceptance criteria.
|
|
134
|
+
- **executor** runs each sub-task with the full v0.3 tool layer, gated by the brief's `authorized_costs`.
|
|
135
|
+
- **reviewer** evaluates the executor's output against the sub-task's acceptance criteria. Read-only file access for verification.
|
|
136
|
+
- **critic** (optional fourth role) gives an independent second-opinion review. Disagreement with the reviewer halts the run for an operator decision.
|
|
137
|
+
|
|
138
|
+
The framework applies recursively. Every role's output passes through the same detectors as a single-agent run. Every sub-task gets its own Phase 0. Phase 2 / Phase 3 fire inside the role that triggered them.
|
|
139
|
+
|
|
140
|
+
Try it without spending execution tokens first:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
openwar plan examples/multi-agent-brief.md --adapter anthropic
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Full run:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
openwar run examples/multi-agent-brief.md --adapter anthropic
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Single-agent mode (omitting `roles:` or passing `--single`) keeps the v0.3 behavior. Sessions from v0.3 resume cleanly under v0.4; the schema migration is automatic.
|
|
153
|
+
|
|
154
|
+
### Budgets
|
|
155
|
+
|
|
156
|
+
Briefs may declare cost ceilings in `budgets:`. Hitting any ceiling halts the coordinator cleanly with state persisted; the operator can extend and resume.
|
|
157
|
+
|
|
158
|
+
```yaml
|
|
159
|
+
budgets:
|
|
160
|
+
max_tokens: 80000
|
|
161
|
+
max_wall_clock_minutes: 25
|
|
162
|
+
max_tool_calls_per_subtask: 12
|
|
163
|
+
max_retries_per_subtask: 3
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Defaults if omitted: 50k tokens, 20 minutes, 15 tool calls per sub-task, 3 retries per sub-task.
|
|
167
|
+
|
|
168
|
+
## CLI
|
|
169
|
+
|
|
170
|
+
```text
|
|
171
|
+
openwar run <brief.md> [--adapter <id>] [--model <name>] [--mode gated|auto]
|
|
172
|
+
[--workdir <path>] [--no-shell]
|
|
173
|
+
[--mcp-server name=command] [--resume] [--ephemeral]
|
|
174
|
+
[--roles planner,executor,reviewer[,critic]]
|
|
175
|
+
[--max-tokens N] [--max-minutes N] [--single]
|
|
176
|
+
openwar plan <brief.md> # planner dry-run; no execution
|
|
177
|
+
openwar resume <brief_id> # auto-detects single-agent vs multi-agent
|
|
178
|
+
openwar list
|
|
179
|
+
openwar inspect <brief_id> [--transcript]
|
|
180
|
+
openwar validate <brief.md>
|
|
181
|
+
openwar roles # list registered roles
|
|
182
|
+
openwar adapters
|
|
183
|
+
openwar tools
|
|
184
|
+
openwar mcp list | add <name> <cmd...> | remove <name> | test <name>
|
|
185
|
+
openwar version
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Sessions persist to `~/.openwar/sessions/` as JSON + JSONL transcript. Resume with `openwar resume <brief_id>`. `--ephemeral` skips persistence (used by tests and integrators).
|
|
189
|
+
|
|
190
|
+
## Use as a library
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import { run, MockAdapter, AnthropicAdapter } from "openwar";
|
|
194
|
+
|
|
195
|
+
const adapter = new AnthropicAdapter({ id: "anthropic", model: "claude-sonnet-4-6" });
|
|
196
|
+
const result = await run({ briefPath: "./brief.md", adapter });
|
|
197
|
+
|
|
198
|
+
if (!result.completed) {
|
|
199
|
+
console.error(`Halted at ${result.final_phase}: ${result.halt_reason}`);
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
War Room and other integrators consume OpenWar this way; the runtime is the framework's enforcement surface.
|
|
204
|
+
|
|
205
|
+
## Brief format
|
|
206
|
+
|
|
207
|
+
Briefs are markdown with required frontmatter. A reference template ships at [`templates/brief.md`](./templates/brief.md).
|
|
208
|
+
|
|
209
|
+
```yaml
|
|
210
|
+
---
|
|
211
|
+
project: <slug> # required, kebab-case
|
|
212
|
+
brief_id: YYYY-MM-DD-NNN # optional; auto-generated if absent
|
|
213
|
+
deadline: YYYY-MM-DD # optional
|
|
214
|
+
scope_locked: true|false # if true, refuse out-of-scope additions
|
|
215
|
+
mode: gated|auto # optional override of per-step-vs-auto
|
|
216
|
+
workdir: ./relative-or-absolute # optional. All filesystem tools sandboxed here.
|
|
217
|
+
authorized_costs: # pre-approves these destructive categories
|
|
218
|
+
- filesystem_write
|
|
219
|
+
- shell_exec
|
|
220
|
+
- http_fetch
|
|
221
|
+
- mcp_tool:filesystem
|
|
222
|
+
mcp_servers: # optional. name=command, one per entry.
|
|
223
|
+
- filesystem=npx -y @modelcontextprotocol/server-filesystem /allowed/dir
|
|
224
|
+
---
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Recognized `authorized_costs` categories: `filesystem_read` (default-allowed), `filesystem_write`, `filesystem_delete`, `shell_exec`, `http_fetch`, `paid_api_call`, `git_write`, `git_push`, `deploy`, `external_message`, plus `mcp_tool:<server>` and `mcp_tool:<server>:<tool>`. Wildcards: `*` matches every category, `mcp_tool:*` matches any MCP tool, `mcp_tool:server:*` matches any tool from a server. `*` triggers a brief-lint warning; almost always you want specific entries.
|
|
228
|
+
|
|
229
|
+
Body sections: **Objective**, **Deliverables**, **Constraints**, **Tools required**, **Notes / unknowns**.
|
|
230
|
+
|
|
231
|
+
Validate a brief without running it:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
openwar validate brief.md
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Still want just the system prompt?
|
|
238
|
+
|
|
239
|
+
Paste [`openwar.md`](./openwar.md) into your agent's system prompt. The framework remains a single markdown file. You lose enforcement (the model is on the honor system), but you keep the behavior overlay.
|
|
240
|
+
|
|
241
|
+
### Claude Code
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
mkdir -p ~/.claude
|
|
245
|
+
curl -fsSL https://raw.githubusercontent.com/pythonluvr/openwar/main/openwar.md >> ~/.claude/CLAUDE.md
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Cursor
|
|
249
|
+
|
|
250
|
+
Settings → Rules for AI → paste the contents of `openwar.md`.
|
|
251
|
+
|
|
252
|
+
### War Room
|
|
253
|
+
|
|
254
|
+
War Room ships OpenWar as its default framework. v0.7+ adopts the runtime; earlier versions used the system prompt only.
|
|
255
|
+
|
|
256
|
+
## Why both
|
|
257
|
+
|
|
258
|
+
Behavioral overlays are easy to ignore. A model that's been told "always produce a Confirmation Summary" will sometimes skip it under context pressure or specific phrasing. The runtime catches the skip and asks the model to restate.
|
|
259
|
+
|
|
260
|
+
System prompts cost nothing to install and work with any runtime. The runtime is heavier, but it actually enforces the rules.
|
|
261
|
+
|
|
262
|
+
<p align="center">
|
|
263
|
+
<img src="branding/warbit-story/warbit-03-success.png" alt="WarBit celebrating in front of a 'SUCCESS' screen" width="280" />
|
|
264
|
+
<br />
|
|
265
|
+
<em>Phase 4 completion. WarBit ships.</em>
|
|
266
|
+
</p>
|
|
267
|
+
|
|
268
|
+
## Versioning
|
|
269
|
+
|
|
270
|
+
Current: **v0.4.0**.
|
|
271
|
+
|
|
272
|
+
- v0.1: framework doc only (single markdown file).
|
|
273
|
+
- v0.2: runtime, CLI, BYOK adapters for Anthropic, OpenAI, Gemini, Grok, OpenAI-compat.
|
|
274
|
+
- v0.3: six native tools (read_file, write_file, list_dir, shell_exec, http_fetch, apply_patch), hand-rolled MCP client, per-adapter tool-call translation, Phase 3 destructive flag for unauthorized tool calls.
|
|
275
|
+
- v0.4: multi-agent orchestration. Coordinator FSM, planner / executor / reviewer / critic roles, typed handoffs, per-role tool scoping, budgets, schema v3 with v2 migration, `openwar plan` and `openwar roles` subcommands.
|
|
276
|
+
- v0.5: persistent project memory across briefs.
|
|
277
|
+
- v0.6: observability dashboards / tracing UI.
|
|
278
|
+
|
|
279
|
+
Framework doc is versioned with the package. Drop-in upgrades preserve compatibility within a major version; major bumps may rename phases or change the brief format.
|
|
280
|
+
|
|
281
|
+
## Community
|
|
282
|
+
|
|
283
|
+
Questions, bug reports, framework discussion: [Discord](https://discord.gg/ku6GJS92V2). Issues and PRs welcome on this repo too.
|
|
284
|
+
|
|
285
|
+
## License
|
|
286
|
+
|
|
287
|
+
[MIT](./LICENSE). Use it, modify it, fork it, ship your own variants, paste it into commercial products. No obligations beyond keeping the copyright notice.
|
|
288
|
+
|
|
289
|
+
## Authorship
|
|
290
|
+
|
|
291
|
+
OpenWar is the framework that ships inside [War Room](https://github.com/pythonluvr/war-room), authored across many iterations of running real agent work. This standalone repo exists so people who don't use War Room can still adopt the framework.
|
|
292
|
+
|
|
293
|
+
<p align="center">
|
|
294
|
+
<img src="branding/warbit-story/warbit-01-sunset.png" alt="WarBit sitting in front of a sunset over the city" width="280" />
|
|
295
|
+
<br />
|
|
296
|
+
<em>Issues and PRs welcome. WarBit will read them in the morning.</em>
|
|
297
|
+
</p>
|
package/bin/openwar
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Resolves whether to run compiled dist (preferred) or tsx-compile src on the
|
|
3
|
+
// fly (for development inside the repo before `npm run build` has run).
|
|
4
|
+
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
7
|
+
import { dirname, join, resolve } from "node:path";
|
|
8
|
+
|
|
9
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
const repoRoot = resolve(here, "..");
|
|
11
|
+
const dist = join(repoRoot, "dist", "cli.js");
|
|
12
|
+
const srcCli = join(repoRoot, "src", "cli.ts");
|
|
13
|
+
|
|
14
|
+
async function loadCli() {
|
|
15
|
+
if (existsSync(dist)) {
|
|
16
|
+
return await import(pathToFileURL(dist).href);
|
|
17
|
+
}
|
|
18
|
+
if (existsSync(srcCli)) {
|
|
19
|
+
try {
|
|
20
|
+
const tsx = await import("tsx/esm/api");
|
|
21
|
+
tsx.register();
|
|
22
|
+
return await import(pathToFileURL(srcCli).href);
|
|
23
|
+
} catch {
|
|
24
|
+
process.stderr.write(
|
|
25
|
+
"openwar: dist/cli.js not found and tsx is not installed. Run `npm install` and `npm run build`.\n",
|
|
26
|
+
);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
process.stderr.write("openwar: cannot locate cli (no dist/cli.js or src/cli.ts).\n");
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const mod = await loadCli();
|
|
35
|
+
if (typeof mod.main !== "function") {
|
|
36
|
+
process.stderr.write("openwar: cli module did not export main().\n");
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
try {
|
|
40
|
+
const code = await mod.main(process.argv.slice(2));
|
|
41
|
+
process.exit(code ?? 0);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
process.stderr.write(`openwar: ${(err && err.message) || err}\n`);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentAdapter, SendMessageOptions, StreamEvent, AdapterConfig, ToolDefinition, ToolResultForRound } from "../types.js";
|
|
2
|
+
export declare function formatToolsForAnthropic(tools: ToolDefinition[]): unknown[];
|
|
3
|
+
export declare function formatMessagesForAnthropic(opts: SendMessageOptions): unknown[];
|
|
4
|
+
export declare function formatToolResultForAnthropicMessage(r: ToolResultForRound): unknown;
|
|
5
|
+
export declare class AnthropicAdapter implements AgentAdapter {
|
|
6
|
+
readonly id = "anthropic";
|
|
7
|
+
readonly name = "Anthropic (Claude)";
|
|
8
|
+
readonly model: string;
|
|
9
|
+
private readonly apiKey;
|
|
10
|
+
private readonly baseUrl;
|
|
11
|
+
constructor(config?: AdapterConfig);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
sendMessage(opts: SendMessageOptions): AsyncIterable<StreamEvent>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,cAAc,EAEd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AASrB,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,EAAE,CAM1E;AAGD,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAyB9E;AAED,wBAAgB,mCAAmC,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAOlF;AASD,qBAAa,gBAAiB,YAAW,YAAY;IACnD,QAAQ,CAAC,EAAE,eAAe;IAC1B,QAAQ,CAAC,IAAI,wBAAwB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,GAAE,aAAmC;IAMvD,YAAY,IAAI,OAAO;IAIhB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,WAAW,CAAC;CAqHzE"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { parseSseStream } from "./sse.js";
|
|
2
|
+
const DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
3
|
+
const DEFAULT_BASE_URL = "https://api.anthropic.com";
|
|
4
|
+
const ANTHROPIC_VERSION = "2023-06-01";
|
|
5
|
+
// Translate OpenWar ToolDefinition[] into Anthropic's "tools" request shape.
|
|
6
|
+
// https://docs.anthropic.com/claude/docs/tool-use
|
|
7
|
+
export function formatToolsForAnthropic(tools) {
|
|
8
|
+
return tools.map(t => ({
|
|
9
|
+
name: t.name,
|
|
10
|
+
description: t.description,
|
|
11
|
+
input_schema: t.input_schema,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
// Build the messages array including prior tool_use / tool_result blocks.
|
|
15
|
+
export function formatMessagesForAnthropic(opts) {
|
|
16
|
+
const out = [];
|
|
17
|
+
for (const m of opts.messages) {
|
|
18
|
+
if (m.role === "system")
|
|
19
|
+
continue;
|
|
20
|
+
out.push({ role: m.role, content: m.content });
|
|
21
|
+
}
|
|
22
|
+
if (opts.prior_tool_calls && opts.prior_tool_calls.length > 0) {
|
|
23
|
+
const blocks = opts.prior_tool_calls.map(c => ({
|
|
24
|
+
type: "tool_use",
|
|
25
|
+
id: c.id,
|
|
26
|
+
name: c.name,
|
|
27
|
+
input: c.arguments ?? {},
|
|
28
|
+
}));
|
|
29
|
+
out.push({ role: "assistant", content: blocks });
|
|
30
|
+
}
|
|
31
|
+
if (opts.prior_tool_results && opts.prior_tool_results.length > 0) {
|
|
32
|
+
const blocks = opts.prior_tool_results.map(r => ({
|
|
33
|
+
type: "tool_result",
|
|
34
|
+
tool_use_id: r.call_id,
|
|
35
|
+
content: r.content,
|
|
36
|
+
...(r.is_error && { is_error: true }),
|
|
37
|
+
}));
|
|
38
|
+
out.push({ role: "user", content: blocks });
|
|
39
|
+
}
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
export function formatToolResultForAnthropicMessage(r) {
|
|
43
|
+
return {
|
|
44
|
+
type: "tool_result",
|
|
45
|
+
tool_use_id: r.call_id,
|
|
46
|
+
content: r.content,
|
|
47
|
+
...(r.is_error && { is_error: true }),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export class AnthropicAdapter {
|
|
51
|
+
id = "anthropic";
|
|
52
|
+
name = "Anthropic (Claude)";
|
|
53
|
+
model;
|
|
54
|
+
apiKey;
|
|
55
|
+
baseUrl;
|
|
56
|
+
constructor(config = { id: "anthropic" }) {
|
|
57
|
+
this.model = config.model ?? DEFAULT_MODEL;
|
|
58
|
+
this.apiKey = config.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
59
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
60
|
+
}
|
|
61
|
+
isConfigured() {
|
|
62
|
+
return Boolean(this.apiKey);
|
|
63
|
+
}
|
|
64
|
+
async *sendMessage(opts) {
|
|
65
|
+
if (!this.apiKey) {
|
|
66
|
+
yield { type: "error", error: new Error("ANTHROPIC_API_KEY not set") };
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const body = {
|
|
70
|
+
model: this.model,
|
|
71
|
+
max_tokens: 4096,
|
|
72
|
+
system: opts.system,
|
|
73
|
+
stream: true,
|
|
74
|
+
messages: formatMessagesForAnthropic(opts),
|
|
75
|
+
};
|
|
76
|
+
if (opts.tools && opts.tools.length > 0) {
|
|
77
|
+
body.tools = formatToolsForAnthropic(opts.tools);
|
|
78
|
+
}
|
|
79
|
+
let res;
|
|
80
|
+
try {
|
|
81
|
+
res = await fetch(`${this.baseUrl}/v1/messages`, {
|
|
82
|
+
method: "POST",
|
|
83
|
+
headers: {
|
|
84
|
+
"content-type": "application/json",
|
|
85
|
+
"anthropic-version": ANTHROPIC_VERSION,
|
|
86
|
+
"x-api-key": this.apiKey,
|
|
87
|
+
},
|
|
88
|
+
body: JSON.stringify(body),
|
|
89
|
+
signal: opts.signal,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
yield { type: "error", error: err instanceof Error ? err : new Error(String(err)) };
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (!res.ok || !res.body) {
|
|
97
|
+
const txt = await res.text().catch(() => "");
|
|
98
|
+
yield {
|
|
99
|
+
type: "error",
|
|
100
|
+
error: new Error(`Anthropic ${res.status}: ${txt.slice(0, 500)}`),
|
|
101
|
+
};
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let assembled = "";
|
|
105
|
+
const toolCallsByIndex = new Map();
|
|
106
|
+
const completedCalls = [];
|
|
107
|
+
try {
|
|
108
|
+
for await (const ev of parseSseStream(res.body)) {
|
|
109
|
+
if (!ev.data || ev.data === "[DONE]")
|
|
110
|
+
continue;
|
|
111
|
+
let parsed;
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(ev.data);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
if (parsed.type === "content_block_start" && parsed.content_block?.type === "tool_use") {
|
|
119
|
+
const idx = parsed.index ?? 0;
|
|
120
|
+
toolCallsByIndex.set(idx, {
|
|
121
|
+
index: idx,
|
|
122
|
+
id: parsed.content_block.id ?? `call_${idx}`,
|
|
123
|
+
name: parsed.content_block.name ?? "",
|
|
124
|
+
argsJson: "",
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
else if (parsed.type === "content_block_delta" &&
|
|
128
|
+
parsed.delta?.type === "input_json_delta") {
|
|
129
|
+
const idx = parsed.index ?? 0;
|
|
130
|
+
const slot = toolCallsByIndex.get(idx);
|
|
131
|
+
if (slot) {
|
|
132
|
+
const fragment = parsed.delta.partial_json ?? "";
|
|
133
|
+
slot.argsJson += fragment;
|
|
134
|
+
yield {
|
|
135
|
+
type: "tool_call_arg_delta",
|
|
136
|
+
tool_call_id: slot.id,
|
|
137
|
+
name: slot.name,
|
|
138
|
+
arg_delta: fragment,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else if (parsed.type === "content_block_delta" &&
|
|
143
|
+
parsed.delta?.type === "text_delta") {
|
|
144
|
+
const delta = parsed.delta.text ?? "";
|
|
145
|
+
if (delta) {
|
|
146
|
+
assembled += delta;
|
|
147
|
+
yield { type: "text_delta", delta };
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else if (parsed.type === "content_block_stop") {
|
|
151
|
+
const idx = parsed.index ?? 0;
|
|
152
|
+
const slot = toolCallsByIndex.get(idx);
|
|
153
|
+
if (slot) {
|
|
154
|
+
let args = {};
|
|
155
|
+
try {
|
|
156
|
+
args = slot.argsJson.length > 0 ? JSON.parse(slot.argsJson) : {};
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
args = { __parse_error: slot.argsJson };
|
|
160
|
+
}
|
|
161
|
+
const call = { id: slot.id, name: slot.name, arguments: args };
|
|
162
|
+
completedCalls.push(call);
|
|
163
|
+
yield { type: "tool_call_complete", call };
|
|
164
|
+
toolCallsByIndex.delete(idx);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (parsed.type === "message_stop") {
|
|
168
|
+
yield { type: "done", message: assembled, tool_calls: completedCalls };
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
yield { type: "done", message: assembled, tool_calls: completedCalls };
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
yield { type: "error", error: err instanceof Error ? err : new Error(String(err)) };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=anthropic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/adapters/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AgentAdapter, SendMessageOptions, StreamEvent, AdapterConfig, ToolDefinition, ToolResultForRound } from "../types.js";
|
|
2
|
+
export declare function formatToolsForGemini(tools: ToolDefinition[]): unknown;
|
|
3
|
+
export declare function formatMessagesForGemini(opts: SendMessageOptions): unknown[];
|
|
4
|
+
export declare function formatToolResultForGeminiMessage(r: ToolResultForRound): unknown;
|
|
5
|
+
export declare class GeminiAdapter implements AgentAdapter {
|
|
6
|
+
readonly id = "gemini";
|
|
7
|
+
readonly name = "Google Gemini";
|
|
8
|
+
readonly model: string;
|
|
9
|
+
private readonly apiKey;
|
|
10
|
+
private readonly baseUrl;
|
|
11
|
+
constructor(config?: AdapterConfig);
|
|
12
|
+
isConfigured(): boolean;
|
|
13
|
+
sendMessage(opts: SendMessageOptions): AsyncIterable<StreamEvent>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=gemini.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/adapters/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EAClB,WAAW,EACX,aAAa,EACb,cAAc,EAEd,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAOrB,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,OAAO,CAUrE;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,EAAE,CA6B3E;AAED,wBAAgB,gCAAgC,CAAC,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAO/E;AAED,qBAAa,aAAc,YAAW,YAAY;IAChD,QAAQ,CAAC,EAAE,YAAY;IACvB,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAqB;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,MAAM,GAAE,aAAgC;IAMpD,YAAY,IAAI,OAAO;IAIhB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,aAAa,CAAC,WAAW,CAAC;CA6EzE"}
|