@zhijiewang/openharness 2.37.0 → 2.38.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.
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import type { Provider } from "../providers/base.js";
|
|
9
9
|
import type { Tools } from "../Tool.js";
|
|
10
10
|
import type { StreamEvent, ToolCallComplete, ToolCallEnd, ToolCallStart, ToolOutputDelta } from "../types/events.js";
|
|
11
|
-
import type
|
|
11
|
+
import { type PermissionMode } from "../types/permissions.js";
|
|
12
12
|
/**
|
|
13
13
|
* Forward inner-loop tool events to the outer stream, stamping parentCallId.
|
|
14
14
|
* Exported for direct unit testing.
|
|
@@ -20,6 +20,15 @@ export type AgentTask = {
|
|
|
20
20
|
description?: string;
|
|
21
21
|
blockedBy?: string[];
|
|
22
22
|
allowedTools?: string[];
|
|
23
|
+
/**
|
|
24
|
+
* Per-task permission mode override — narrowing-only, same contract as
|
|
25
|
+
* AgentTool's `permission_mode` (v2.36). When set, the task's effective
|
|
26
|
+
* mode is `clampSubagentPermissionMode(dispatcher.permissionMode, task.permissionMode)`,
|
|
27
|
+
* so a task can be the same strictness as the outer call or stricter,
|
|
28
|
+
* never looser. Use to mark specific tasks in a parallel batch as
|
|
29
|
+
* read-only review/audit while letting siblings keep full write access.
|
|
30
|
+
*/
|
|
31
|
+
permissionMode?: PermissionMode;
|
|
23
32
|
};
|
|
24
33
|
export type AgentTaskResult = {
|
|
25
34
|
id: string;
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* and triggers dependent tasks when their blockers complete.
|
|
7
7
|
*/
|
|
8
8
|
import { createWorktree, isGitRepo, removeWorktree } from "../git/index.js";
|
|
9
|
+
import { clampSubagentPermissionMode } from "../types/permissions.js";
|
|
9
10
|
/**
|
|
10
11
|
* Forward inner-loop tool events to the outer stream, stamping parentCallId.
|
|
11
12
|
* Exported for direct unit testing.
|
|
@@ -168,11 +169,15 @@ export class AgentDispatcher {
|
|
|
168
169
|
// matching `process.chdir(originalCwd)` in `finally` — but since
|
|
169
170
|
// `process.cwd()` is process-wide, two concurrent tasks would clobber
|
|
170
171
|
// each other's directory mid-execution.
|
|
172
|
+
// Per-task permission mode — narrowing-only clamp applied so a task
|
|
173
|
+
// can override only to a same-or-stricter mode than the dispatcher's
|
|
174
|
+
// outer mode (#115 contract).
|
|
175
|
+
const taskPermissionMode = clampSubagentPermissionMode(this.permissionMode, task.permissionMode);
|
|
171
176
|
const config = {
|
|
172
177
|
provider: this.provider,
|
|
173
178
|
tools: taskTools,
|
|
174
179
|
systemPrompt: this.systemPrompt,
|
|
175
|
-
permissionMode:
|
|
180
|
+
permissionMode: taskPermissionMode,
|
|
176
181
|
model: this.model,
|
|
177
182
|
maxTurns: 20,
|
|
178
183
|
abortSignal: this.abortSignal,
|
|
@@ -6,15 +6,21 @@ declare const inputSchema: z.ZodObject<{
|
|
|
6
6
|
prompt: z.ZodString;
|
|
7
7
|
description: z.ZodOptional<z.ZodString>;
|
|
8
8
|
blockedBy: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
9
|
+
allowed_tools: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
10
|
+
permission_mode: z.ZodOptional<z.ZodEnum<["ask", "trust", "deny", "acceptEdits", "plan", "auto", "bypassPermissions"]>>;
|
|
9
11
|
}, "strip", z.ZodTypeAny, {
|
|
10
12
|
id: string;
|
|
11
13
|
prompt: string;
|
|
12
14
|
description?: string | undefined;
|
|
15
|
+
allowed_tools?: string[] | undefined;
|
|
16
|
+
permission_mode?: "ask" | "deny" | "trust" | "acceptEdits" | "plan" | "auto" | "bypassPermissions" | undefined;
|
|
13
17
|
blockedBy?: string[] | undefined;
|
|
14
18
|
}, {
|
|
15
19
|
id: string;
|
|
16
20
|
prompt: string;
|
|
17
21
|
description?: string | undefined;
|
|
22
|
+
allowed_tools?: string[] | undefined;
|
|
23
|
+
permission_mode?: "ask" | "deny" | "trust" | "acceptEdits" | "plan" | "auto" | "bypassPermissions" | undefined;
|
|
18
24
|
blockedBy?: string[] | undefined;
|
|
19
25
|
}>, "many">;
|
|
20
26
|
}, "strip", z.ZodTypeAny, {
|
|
@@ -22,6 +28,8 @@ declare const inputSchema: z.ZodObject<{
|
|
|
22
28
|
id: string;
|
|
23
29
|
prompt: string;
|
|
24
30
|
description?: string | undefined;
|
|
31
|
+
allowed_tools?: string[] | undefined;
|
|
32
|
+
permission_mode?: "ask" | "deny" | "trust" | "acceptEdits" | "plan" | "auto" | "bypassPermissions" | undefined;
|
|
25
33
|
blockedBy?: string[] | undefined;
|
|
26
34
|
}[];
|
|
27
35
|
}, {
|
|
@@ -29,6 +37,8 @@ declare const inputSchema: z.ZodObject<{
|
|
|
29
37
|
id: string;
|
|
30
38
|
prompt: string;
|
|
31
39
|
description?: string | undefined;
|
|
40
|
+
allowed_tools?: string[] | undefined;
|
|
41
|
+
permission_mode?: "ask" | "deny" | "trust" | "acceptEdits" | "plan" | "auto" | "bypassPermissions" | undefined;
|
|
32
42
|
blockedBy?: string[] | undefined;
|
|
33
43
|
}[];
|
|
34
44
|
}>;
|
|
@@ -5,6 +5,11 @@ const taskSchema = z.object({
|
|
|
5
5
|
prompt: z.string(),
|
|
6
6
|
description: z.string().optional(),
|
|
7
7
|
blockedBy: z.array(z.string()).optional(),
|
|
8
|
+
allowed_tools: z.array(z.string()).optional(),
|
|
9
|
+
permission_mode: z
|
|
10
|
+
.enum(["ask", "trust", "deny", "acceptEdits", "plan", "auto", "bypassPermissions"])
|
|
11
|
+
.optional()
|
|
12
|
+
.describe("Restrict THIS task's permission mode. Narrowing-only — clamps to the outer mode if a less-restrictive value is requested. Use to mark a single task as read-only review/audit while sibling tasks keep full write access."),
|
|
8
13
|
});
|
|
9
14
|
const inputSchema = z.object({
|
|
10
15
|
tasks: z.array(taskSchema).min(1),
|
|
@@ -27,7 +32,18 @@ export const ParallelAgentTool = {
|
|
|
27
32
|
const systemPrompt = context.systemPrompt ?? "You are a sub-agent. Complete the delegated task concisely.";
|
|
28
33
|
const dispatcher = new AgentDispatcher(context.provider, context.tools, systemPrompt, context.permissionMode ?? "trust", context.model, context.workingDir, context.abortSignal, 4, // maxConcurrency default
|
|
29
34
|
context.callId, context.emitChildEvent);
|
|
30
|
-
|
|
35
|
+
// Map snake_case input fields to the AgentTask camelCase shape — the
|
|
36
|
+
// input schema uses `allowed_tools` / `permission_mode` to stay
|
|
37
|
+
// consistent with AgentTool, but the dispatcher's task type uses
|
|
38
|
+
// `allowedTools` / `permissionMode`.
|
|
39
|
+
dispatcher.addTasks(input.tasks.map((t) => ({
|
|
40
|
+
id: t.id,
|
|
41
|
+
prompt: t.prompt,
|
|
42
|
+
description: t.description,
|
|
43
|
+
blockedBy: t.blockedBy,
|
|
44
|
+
allowedTools: t.allowed_tools,
|
|
45
|
+
permissionMode: t.permission_mode,
|
|
46
|
+
})));
|
|
31
47
|
const results = await dispatcher.execute();
|
|
32
48
|
const output = results
|
|
33
49
|
.map((r) => {
|
|
@@ -48,12 +64,13 @@ Parameters:
|
|
|
48
64
|
- prompt (string): Instructions for the sub-agent
|
|
49
65
|
- description (string, optional): Short label
|
|
50
66
|
- blockedBy (string[], optional): IDs of tasks that must complete first
|
|
67
|
+
- allowed_tools (string[], optional): Restrict THIS task's agent to specific tools
|
|
68
|
+
- permission_mode (string, optional): Override THIS task's permission mode. Narrowing-only — a less-restrictive value clamps to the outer mode. Useful for marking review/audit tasks as "plan" or "deny" while sibling tasks keep full write access.
|
|
51
69
|
|
|
52
|
-
Example:
|
|
70
|
+
Example: parallel test-write + read-only review:
|
|
53
71
|
tasks: [
|
|
54
|
-
{ id: "
|
|
55
|
-
{ id: "
|
|
56
|
-
{ id: "c", prompt: "...", blockedBy: ["a", "b"] }
|
|
72
|
+
{ id: "tests", prompt: "Add tests for the new auth module" },
|
|
73
|
+
{ id: "review", prompt: "Audit the new auth module for security issues", permission_mode: "plan" }
|
|
57
74
|
]`;
|
|
58
75
|
},
|
|
59
76
|
};
|