@zhijiewang/openharness 2.32.0 → 2.34.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 +12 -1
- package/README.zh-CN.md +12 -1
- package/dist/agents/roles.js +31 -2
- package/dist/harness/config.d.ts +28 -0
- package/dist/harness/sandbox-runtime.d.ts +47 -0
- package/dist/harness/sandbox-runtime.js +100 -0
- package/dist/providers/router.js +7 -0
- package/dist/tools/BashTool/index.js +31 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -542,7 +542,8 @@ Dispatch specialized sub-agents for focused tasks:
|
|
|
542
542
|
| `security-auditor` | OWASP, injection, secrets, CVE scanning | Read-only + Bash |
|
|
543
543
|
| `evaluator` | Evaluate code quality and run tests (read-only) | Read-only + Bash + Diagnostics |
|
|
544
544
|
| `planner` | Design step-by-step implementation plans | Read-only + Bash |
|
|
545
|
-
| `architect` | Analyze architecture and design structural changes | Read-only |
|
|
545
|
+
| `architect` | Analyze architecture and design structural changes (hands off to editor) | Read-only |
|
|
546
|
+
| `editor` | Apply an architect's plan as code edits, no re-planning | Read + Edit + Write + MultiEdit + Bash |
|
|
546
547
|
| `migrator` | Systematic codebase migrations and upgrades | All file tools + Bash |
|
|
547
548
|
|
|
548
549
|
Each role restricts the sub-agent to only its suggested tools. You can also pass `allowed_tools` explicitly:
|
|
@@ -552,6 +553,16 @@ Agent({ subagent_type: 'evaluator', prompt: 'Run all tests and report results' }
|
|
|
552
553
|
Agent({ allowed_tools: ['Read', 'Grep'], prompt: 'Search for all TODO comments' })
|
|
553
554
|
```
|
|
554
555
|
|
|
556
|
+
### Architect → Editor (cost-saving multi-file edits)
|
|
557
|
+
|
|
558
|
+
For larger changes that span multiple files, dispatch a two-pass `architect` → `editor` workflow. The architect (powerful model) reads the codebase and outputs a structured plan; the editor (fast model) applies it mechanically without re-planning. When `modelRouter` is configured, OH automatically routes the `architect` role to your `powerful` tier and the `editor` role to your `fast` tier — typical cost reduction is 30-50% on multi-file edits versus running both passes on the powerful model.
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
Agent({ subagent_type: 'architect', prompt: 'Plan a migration from option A to option B across src/' })
|
|
562
|
+
# Hand the resulting plan to:
|
|
563
|
+
Agent({ subagent_type: 'editor', prompt: '<paste plan>' })
|
|
564
|
+
```
|
|
565
|
+
|
|
555
566
|
## Headless Mode
|
|
556
567
|
|
|
557
568
|
Run a single prompt without interactive UI — perfect for CI/CD and scripting:
|
package/README.zh-CN.md
CHANGED
|
@@ -542,7 +542,8 @@ Cron 执行器每 60 秒检查一次到期任务,并通过子查询运行。
|
|
|
542
542
|
| `security-auditor` | OWASP、注入、密钥、CVE 扫描 | 只读 + Bash |
|
|
543
543
|
| `evaluator` | 评估代码质量并运行测试(只读) | 只读 + Bash + Diagnostics |
|
|
544
544
|
| `planner` | 设计分步实现计划 | 只读 + Bash |
|
|
545
|
-
| `architect` |
|
|
545
|
+
| `architect` | 分析架构、设计结构性变更(移交给 editor 落地) | 只读 |
|
|
546
|
+
| `editor` | 按 architect 给出的方案应用代码改动,不再重新规划 | Read + Edit + Write + MultiEdit + Bash |
|
|
546
547
|
| `migrator` | 系统化的代码库迁移与升级 | 全部文件工具 + Bash |
|
|
547
548
|
|
|
548
549
|
每个角色只会让子代理使用其推荐的工具。你也可以显式传入 `allowed_tools`:
|
|
@@ -552,6 +553,16 @@ Agent({ subagent_type: 'evaluator', prompt: 'Run all tests and report results' }
|
|
|
552
553
|
Agent({ allowed_tools: ['Read', 'Grep'], prompt: 'Search for all TODO comments' })
|
|
553
554
|
```
|
|
554
555
|
|
|
556
|
+
### Architect → Editor(多文件改动的省钱模式)
|
|
557
|
+
|
|
558
|
+
对于跨多个文件的较大改动,使用 `architect` → `editor` 两遍式工作流:architect(强模型)读懂代码并产出结构化方案;editor(轻量模型)按方案机械落地,不再重新规划。当配置了 `modelRouter` 时,OH 会自动把 `architect` 角色路由到 `powerful` 档、把 `editor` 角色路由到 `fast` 档 —— 相比两遍都跑强模型,多文件改动通常省 30-50% 成本。
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
Agent({ subagent_type: 'architect', prompt: 'Plan a migration from option A to option B across src/' })
|
|
562
|
+
# 把得到的方案再交给 editor:
|
|
563
|
+
Agent({ subagent_type: 'editor', prompt: '<paste plan>' })
|
|
564
|
+
```
|
|
565
|
+
|
|
555
566
|
## 无头模式
|
|
556
567
|
|
|
557
568
|
跑一次提示词,不走交互 UI —— 适合 CI/CD 和脚本化:
|
package/dist/agents/roles.js
CHANGED
|
@@ -131,7 +131,7 @@ Do NOT implement anything. Your output is a plan document, not code. Read widely
|
|
|
131
131
|
{
|
|
132
132
|
id: "architect",
|
|
133
133
|
name: "Architect",
|
|
134
|
-
description: "Analyzes system architecture and designs structural changes",
|
|
134
|
+
description: "Analyzes system architecture and designs structural changes (hands off to editor)",
|
|
135
135
|
systemPromptSupplement: `You are an architecture agent. Your job is to:
|
|
136
136
|
- Map the current system architecture (modules, dependencies, data flow)
|
|
137
137
|
- Identify architectural patterns and conventions in use
|
|
@@ -139,9 +139,38 @@ Do NOT implement anything. Your output is a plan document, not code. Read widely
|
|
|
139
139
|
- Evaluate trade-offs between approaches (performance, maintainability, complexity)
|
|
140
140
|
- Document interfaces, contracts, and integration points
|
|
141
141
|
|
|
142
|
-
Focus on the big picture: module boundaries, data flow, dependency graphs.
|
|
142
|
+
Focus on the big picture: module boundaries, data flow, dependency graphs. Do NOT apply edits — leave implementation to the editor agent.
|
|
143
|
+
|
|
144
|
+
When you've finished planning, output a structured "Plan" the editor can apply mechanically:
|
|
145
|
+
|
|
146
|
+
## Plan
|
|
147
|
+
1. **<file:line>** — <change description>
|
|
148
|
+
- **Before**: <current state, 1-line>
|
|
149
|
+
- **After**: <target state, 1-line>
|
|
150
|
+
- **Why**: <one-sentence rationale>
|
|
151
|
+
2. <next change…>
|
|
152
|
+
|
|
153
|
+
Keep each step small enough that an editor (a cheaper model) can apply it without re-deriving your reasoning. Group related edits together; surface dependencies between steps.`,
|
|
143
154
|
suggestedTools: ["Read", "Glob", "Grep", "LS"],
|
|
144
155
|
},
|
|
156
|
+
{
|
|
157
|
+
id: "editor",
|
|
158
|
+
name: "Editor",
|
|
159
|
+
description: "Applies an architect's plan as code edits — does not re-plan, no discovery",
|
|
160
|
+
systemPromptSupplement: `You are a code editor. You receive a plan from an architect agent and apply it as file edits.
|
|
161
|
+
|
|
162
|
+
Rules:
|
|
163
|
+
- DO apply the changes exactly as described in the plan.
|
|
164
|
+
- DO read each file before editing it (Read → Edit), so your edit anchors land correctly.
|
|
165
|
+
- DO run tests after each batch if test commands are available.
|
|
166
|
+
- DO NOT re-plan, second-guess the architect, or expand scope. If the plan is ambiguous on a specific edit, follow the most literal interpretation; surface the ambiguity in your final summary, don't try to resolve it yourself.
|
|
167
|
+
- DO NOT add features, refactor adjacent code, or change tests beyond what the plan specifies.
|
|
168
|
+
|
|
169
|
+
This role is intentionally tuned for a cheaper/faster model — the architect already did the reasoning. Your job is mechanical accuracy.
|
|
170
|
+
|
|
171
|
+
If the plan can't be applied (e.g. a referenced file doesn't exist, an anchor doesn't match the current code), STOP and report which step failed and why. Do not improvise.`,
|
|
172
|
+
suggestedTools: ["Read", "Edit", "Write", "MultiEdit", "Bash"],
|
|
173
|
+
},
|
|
145
174
|
{
|
|
146
175
|
id: "migrator",
|
|
147
176
|
name: "Migrator",
|
package/dist/harness/config.d.ts
CHANGED
|
@@ -214,6 +214,34 @@ export type OhConfig = {
|
|
|
214
214
|
rateLimit?: number;
|
|
215
215
|
allowedTools?: string[];
|
|
216
216
|
};
|
|
217
|
+
/**
|
|
218
|
+
* Opt-in OS-level sandbox via `@anthropic-ai/sandbox-runtime` (an optional
|
|
219
|
+
* dependency). When `enabled: true`, BashTool wraps every command in
|
|
220
|
+
* bubblewrap (Linux) or sandbox-exec (macOS) plus a domain-allowlist
|
|
221
|
+
* network proxy. Windows isn't supported by the package — the wrap is a
|
|
222
|
+
* silent passthrough there. Off by default; users opt in via config or the
|
|
223
|
+
* `--sandbox` CLI flag.
|
|
224
|
+
*
|
|
225
|
+
* `network.allowedDomains` is the proxy allowlist (e.g. `["github.com",
|
|
226
|
+
* "registry.npmjs.org"]`); `deniedDomains` blocks specific hosts before
|
|
227
|
+
* the allowlist applies. `filesystem.allowWrite` defaults to `[cwd]` —
|
|
228
|
+
* the sandbox can write to the project tree but nowhere else.
|
|
229
|
+
*
|
|
230
|
+
* See `src/harness/sandbox-runtime.ts` and SECURITY.md for the full
|
|
231
|
+
* threat-model boundary.
|
|
232
|
+
*/
|
|
233
|
+
sandbox?: {
|
|
234
|
+
enabled?: boolean;
|
|
235
|
+
network?: {
|
|
236
|
+
allowedDomains?: string[];
|
|
237
|
+
deniedDomains?: string[];
|
|
238
|
+
};
|
|
239
|
+
filesystem?: {
|
|
240
|
+
allowWrite?: string[];
|
|
241
|
+
denyWrite?: string[];
|
|
242
|
+
denyRead?: string[];
|
|
243
|
+
};
|
|
244
|
+
};
|
|
217
245
|
/**
|
|
218
246
|
* Environment variables injected into child processes spawned by the harness —
|
|
219
247
|
* Bash/Monitor/PowerShell tool executions and MCP server subprocesses. Useful
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS-level sandbox integration via the optional `@anthropic-ai/sandbox-runtime`
|
|
3
|
+
* package. The package wraps a shell command in bubblewrap (Linux) or
|
|
4
|
+
* sandbox-exec (macOS) plus a network proxy that filters by domain allowlist.
|
|
5
|
+
*
|
|
6
|
+
* Boundaries:
|
|
7
|
+
* - **Linux + macOS**: real sandboxing via the package's static API.
|
|
8
|
+
* - **Windows**: not supported by the package — every wrap call returns null
|
|
9
|
+
* (graceful passthrough; tools spawn unsandboxed). Documented in SECURITY.md.
|
|
10
|
+
* - **Package not installed**: same passthrough behavior — installs cleanly
|
|
11
|
+
* without the optional dep on any platform.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle:
|
|
14
|
+
* - Initialized once per process on the first wrap request.
|
|
15
|
+
* - One `SandboxManager.initialize` covers all subsequent wrap calls.
|
|
16
|
+
* - No reset — the package documents auto-cleanup on process exit.
|
|
17
|
+
*
|
|
18
|
+
* Opt-in: callers pass `{ enabled: true }` (typically derived from
|
|
19
|
+
* `OhConfig.sandbox.enabled` or the `--sandbox` CLI flag). The default is
|
|
20
|
+
* off so existing users see no behavior change.
|
|
21
|
+
*/
|
|
22
|
+
import type { OhConfig } from "./config.js";
|
|
23
|
+
export type SandboxConfig = NonNullable<OhConfig["sandbox"]>;
|
|
24
|
+
/**
|
|
25
|
+
* Returns true on Linux/macOS where sandboxing is supported. Windows is
|
|
26
|
+
* unsupported by the underlying package, so we short-circuit there to avoid
|
|
27
|
+
* a misleading "tried to load and failed" log.
|
|
28
|
+
*/
|
|
29
|
+
export declare function isSandboxAvailable(): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Wrap a shell command for sandboxed execution.
|
|
32
|
+
*
|
|
33
|
+
* Returns the wrapped command (a single shell string suitable for
|
|
34
|
+
* `spawn(cmd, { shell: "/bin/bash" })`) when sandboxing is enabled and
|
|
35
|
+
* available. Returns null in every other case — Windows, missing package,
|
|
36
|
+
* disabled config, init failure — so the caller falls through to the
|
|
37
|
+
* unsandboxed code path unchanged.
|
|
38
|
+
*/
|
|
39
|
+
export declare function wrapForSandbox(command: string, config: SandboxConfig): Promise<string | null>;
|
|
40
|
+
/**
|
|
41
|
+
* Test-only: reset the cached init promise so unit tests can re-init with
|
|
42
|
+
* different configs.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export declare function _resetSandboxForTest(): void;
|
|
47
|
+
//# sourceMappingURL=sandbox-runtime.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS-level sandbox integration via the optional `@anthropic-ai/sandbox-runtime`
|
|
3
|
+
* package. The package wraps a shell command in bubblewrap (Linux) or
|
|
4
|
+
* sandbox-exec (macOS) plus a network proxy that filters by domain allowlist.
|
|
5
|
+
*
|
|
6
|
+
* Boundaries:
|
|
7
|
+
* - **Linux + macOS**: real sandboxing via the package's static API.
|
|
8
|
+
* - **Windows**: not supported by the package — every wrap call returns null
|
|
9
|
+
* (graceful passthrough; tools spawn unsandboxed). Documented in SECURITY.md.
|
|
10
|
+
* - **Package not installed**: same passthrough behavior — installs cleanly
|
|
11
|
+
* without the optional dep on any platform.
|
|
12
|
+
*
|
|
13
|
+
* Lifecycle:
|
|
14
|
+
* - Initialized once per process on the first wrap request.
|
|
15
|
+
* - One `SandboxManager.initialize` covers all subsequent wrap calls.
|
|
16
|
+
* - No reset — the package documents auto-cleanup on process exit.
|
|
17
|
+
*
|
|
18
|
+
* Opt-in: callers pass `{ enabled: true }` (typically derived from
|
|
19
|
+
* `OhConfig.sandbox.enabled` or the `--sandbox` CLI flag). The default is
|
|
20
|
+
* off so existing users see no behavior change.
|
|
21
|
+
*/
|
|
22
|
+
// Cached, lazy-initialized handle. We deliberately don't expose this — callers
|
|
23
|
+
// only see `wrapForSandbox` / `isSandboxAvailable` / `resetSandboxForTest`.
|
|
24
|
+
let _initPromise = null;
|
|
25
|
+
/**
|
|
26
|
+
* Returns true on Linux/macOS where sandboxing is supported. Windows is
|
|
27
|
+
* unsupported by the underlying package, so we short-circuit there to avoid
|
|
28
|
+
* a misleading "tried to load and failed" log.
|
|
29
|
+
*/
|
|
30
|
+
export function isSandboxAvailable() {
|
|
31
|
+
return process.platform === "linux" || process.platform === "darwin";
|
|
32
|
+
}
|
|
33
|
+
async function loadAndInitialize(config) {
|
|
34
|
+
if (!isSandboxAvailable())
|
|
35
|
+
return null;
|
|
36
|
+
let mod;
|
|
37
|
+
try {
|
|
38
|
+
mod = (await import("@anthropic-ai/sandbox-runtime"));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Optional dep not installed — graceful passthrough.
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
await mod.SandboxManager.initialize({
|
|
46
|
+
network: {
|
|
47
|
+
allowedDomains: config.network?.allowedDomains ?? [],
|
|
48
|
+
deniedDomains: config.network?.deniedDomains ?? [],
|
|
49
|
+
},
|
|
50
|
+
filesystem: {
|
|
51
|
+
allowWrite: config.filesystem?.allowWrite ?? [process.cwd()],
|
|
52
|
+
denyWrite: config.filesystem?.denyWrite ?? [],
|
|
53
|
+
denyRead: config.filesystem?.denyRead ?? [],
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// Init can fail when bubblewrap / sandbox-exec aren't installed, or when
|
|
59
|
+
// the user's profile rejects the proxy ports. Falling back to passthrough
|
|
60
|
+
// is correct — opting in promised "use sandbox if you can," not "fail
|
|
61
|
+
// closed" — that's a separate `requireSandbox` mode for a future revision.
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return mod;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Wrap a shell command for sandboxed execution.
|
|
68
|
+
*
|
|
69
|
+
* Returns the wrapped command (a single shell string suitable for
|
|
70
|
+
* `spawn(cmd, { shell: "/bin/bash" })`) when sandboxing is enabled and
|
|
71
|
+
* available. Returns null in every other case — Windows, missing package,
|
|
72
|
+
* disabled config, init failure — so the caller falls through to the
|
|
73
|
+
* unsandboxed code path unchanged.
|
|
74
|
+
*/
|
|
75
|
+
export async function wrapForSandbox(command, config) {
|
|
76
|
+
if (!config.enabled)
|
|
77
|
+
return null;
|
|
78
|
+
if (!_initPromise) {
|
|
79
|
+
_initPromise = loadAndInitialize(config);
|
|
80
|
+
}
|
|
81
|
+
const mod = await _initPromise;
|
|
82
|
+
if (!mod)
|
|
83
|
+
return null;
|
|
84
|
+
try {
|
|
85
|
+
return await mod.SandboxManager.wrapWithSandbox(command);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Test-only: reset the cached init promise so unit tests can re-init with
|
|
93
|
+
* different configs.
|
|
94
|
+
*
|
|
95
|
+
* @internal
|
|
96
|
+
*/
|
|
97
|
+
export function _resetSandboxForTest() {
|
|
98
|
+
_initPromise = null;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=sandbox-runtime.js.map
|
package/dist/providers/router.js
CHANGED
|
@@ -26,6 +26,13 @@ export class ModelRouter {
|
|
|
26
26
|
if (context.role && powerfulRoles.includes(context.role)) {
|
|
27
27
|
return this.route("powerful", `role: ${context.role}`);
|
|
28
28
|
}
|
|
29
|
+
// Roles that apply mechanical changes per a prior plan → fast (cost
|
|
30
|
+
// optimization for the architect → editor pattern; the planner has
|
|
31
|
+
// already done the reasoning, so the editor doesn't need a powerful model)
|
|
32
|
+
const cheapRoles = ["editor"];
|
|
33
|
+
if (context.role && cheapRoles.includes(context.role)) {
|
|
34
|
+
return this.route("fast", `role: ${context.role}`);
|
|
35
|
+
}
|
|
29
36
|
// Early exploration turns (1-2) → fast
|
|
30
37
|
if (context.turn <= 2 && context.hadToolCalls) {
|
|
31
38
|
return this.route("fast", "early exploration");
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { readOhConfig } from "../../harness/config.js";
|
|
4
|
+
import { wrapForSandbox } from "../../harness/sandbox-runtime.js";
|
|
3
5
|
import { safeEnv } from "../../utils/safe-env.js";
|
|
4
6
|
const inputSchema = z.object({
|
|
5
7
|
command: z.string(),
|
|
@@ -21,12 +23,30 @@ export const BashTool = {
|
|
|
21
23
|
isConcurrencySafe() {
|
|
22
24
|
return false;
|
|
23
25
|
},
|
|
24
|
-
call(input, context) {
|
|
26
|
+
async call(input, context) {
|
|
25
27
|
// input.timeout is in seconds; convert to ms. Default 120s.
|
|
26
28
|
const timeoutMs = Math.min((input.timeout ?? 120) * 1000, MAX_TIMEOUT);
|
|
27
29
|
const isWin = process.platform === "win32";
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
// Optional OS-level sandbox via @anthropic-ai/sandbox-runtime. Returns null
|
|
31
|
+
// when disabled / on Windows / when the optional dep isn't installed —
|
|
32
|
+
// caller falls back to the existing unsandboxed spawn unchanged.
|
|
33
|
+
const sandboxCfg = readOhConfig()?.sandbox;
|
|
34
|
+
const wrappedCommand = sandboxCfg ? await wrapForSandbox(input.command, sandboxCfg) : null;
|
|
35
|
+
let shell;
|
|
36
|
+
let shellArgs;
|
|
37
|
+
let extraSpawnOpts = {};
|
|
38
|
+
if (wrappedCommand) {
|
|
39
|
+
// sandbox-runtime returns a shell-string. Pin the shell to /bin/bash so
|
|
40
|
+
// the surrounding command syntax (heredocs, $((...)) etc.) keeps working
|
|
41
|
+
// — `shell: true` would default to /bin/sh on Linux.
|
|
42
|
+
shell = wrappedCommand;
|
|
43
|
+
shellArgs = [];
|
|
44
|
+
extraSpawnOpts = { shell: "/bin/bash" };
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
shell = isWin ? "cmd.exe" : "/bin/bash";
|
|
48
|
+
shellArgs = isWin ? ["/c", input.command] : ["-c", input.command];
|
|
49
|
+
}
|
|
30
50
|
// Background execution: spawn and return immediately
|
|
31
51
|
if (input.run_in_background) {
|
|
32
52
|
const bgId = Date.now().toString(36) + Math.random().toString(36).slice(2, 6);
|
|
@@ -35,9 +55,13 @@ export const BashTool = {
|
|
|
35
55
|
env: safeEnv(),
|
|
36
56
|
stdio: ["ignore", "pipe", "pipe"],
|
|
37
57
|
detached: false,
|
|
58
|
+
...extraSpawnOpts,
|
|
38
59
|
});
|
|
39
60
|
let stdout = "";
|
|
40
61
|
let stderr = "";
|
|
62
|
+
// stdio is fixed to ["ignore", "pipe", "pipe"] above, so stdout/stderr
|
|
63
|
+
// are always streams. Adding `...extraSpawnOpts` widens the spawn
|
|
64
|
+
// overload's return type to potentially-null pipes; assert non-null.
|
|
41
65
|
proc.stdout.on("data", (chunk) => {
|
|
42
66
|
stdout += chunk.toString();
|
|
43
67
|
});
|
|
@@ -76,11 +100,15 @@ export const BashTool = {
|
|
|
76
100
|
cwd: context.workingDir,
|
|
77
101
|
env: safeEnv(),
|
|
78
102
|
stdio: ["ignore", "pipe", "pipe"],
|
|
103
|
+
...extraSpawnOpts,
|
|
79
104
|
});
|
|
80
105
|
const timer = setTimeout(() => {
|
|
81
106
|
killed = true;
|
|
82
107
|
proc.kill("SIGTERM");
|
|
83
108
|
}, timeoutMs);
|
|
109
|
+
// stdio: ["ignore", "pipe", "pipe"] is set above — pipes are always
|
|
110
|
+
// present here; the spread of extraSpawnOpts just widens the return
|
|
111
|
+
// type. Non-null asserts are safe.
|
|
84
112
|
proc.stdout.on("data", (chunk) => {
|
|
85
113
|
const text = chunk.toString();
|
|
86
114
|
stdout += text;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhijiewang/openharness",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.34.0",
|
|
4
4
|
"description": "Open-source terminal coding agent. Works with any LLM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
},
|
|
92
92
|
"homepage": "https://github.com/zhijiewong/openharness#readme",
|
|
93
93
|
"optionalDependencies": {
|
|
94
|
+
"@anthropic-ai/sandbox-runtime": "^0.0.49",
|
|
94
95
|
"@napi-rs/keyring": "^1.2.0",
|
|
95
96
|
"sharp": "^0.34.5"
|
|
96
97
|
}
|