@zhijiewang/openharness 2.12.0 → 2.14.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 +2 -0
- package/dist/commands/info.js +20 -0
- package/dist/commands/session.js +39 -12
- package/dist/harness/config.d.ts +7 -0
- package/dist/harness/hooks.d.ts +35 -1
- package/dist/harness/hooks.js +204 -35
- package/dist/harness/session.d.ts +4 -0
- package/dist/harness/session.js +2 -0
- package/dist/harness/submit-handler.js +36 -2
- package/dist/main.js +48 -24
- package/dist/mcp/oauth-keychain.d.ts +16 -0
- package/dist/mcp/oauth-keychain.js +70 -0
- package/dist/mcp/oauth-storage-fs.d.ts +23 -0
- package/dist/mcp/oauth-storage-fs.js +58 -0
- package/dist/mcp/oauth-storage.d.ts +24 -19
- package/dist/mcp/oauth-storage.js +46 -49
- package/dist/providers/fallback.js +19 -7
- package/dist/providers/index.js +18 -2
- package/dist/providers/router.d.ts +4 -0
- package/dist/providers/router.js +19 -0
- package/dist/query/index.js +33 -1
- package/dist/query/tools.js +49 -11
- package/dist/query/types.d.ts +6 -0
- package/dist/tools/AgentTool/index.js +2 -2
- package/dist/tools/ScheduleWakeupTool/index.d.ts +2 -2
- package/package.json +5 -2
package/dist/query/tools.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Tool execution — permission checking, batching, output capping.
|
|
3
3
|
*/
|
|
4
4
|
import { createCheckpoint, getAffectedFiles } from "../harness/checkpoints.js";
|
|
5
|
-
import { emitHook } from "../harness/hooks.js";
|
|
5
|
+
import { emitHook, emitHookWithOutcome } from "../harness/hooks.js";
|
|
6
6
|
import { findToolByName } from "../Tool.js";
|
|
7
7
|
import { createToolResultMessage } from "../types/message.js";
|
|
8
8
|
import { checkPermission } from "../types/permissions.js";
|
|
@@ -45,9 +45,28 @@ export async function executeSingleTool(toolCall, tools, context, permissionMode
|
|
|
45
45
|
if (perm.reason === "needs-approval" && askUser) {
|
|
46
46
|
const { formatToolArgs } = await import("../utils/tool-summary.js");
|
|
47
47
|
const description = formatToolArgs(tool.name, toolCall.arguments);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
// Hook: permissionRequest — fires between preToolUse and the interactive askUser prompt.
|
|
49
|
+
// Only fires when checkPermission says "needs-approval" AND askUser is provided.
|
|
50
|
+
const hookOutcome = await emitHookWithOutcome("permissionRequest", {
|
|
51
|
+
toolName: tool.name,
|
|
52
|
+
toolArgs: JSON.stringify(toolCall.arguments).slice(0, 1000),
|
|
53
|
+
toolInputJson: JSON.stringify(parsed.data).slice(0, 1000),
|
|
54
|
+
permissionMode,
|
|
55
|
+
permissionAction: "ask",
|
|
56
|
+
});
|
|
57
|
+
if (hookOutcome.permissionDecision === "allow") {
|
|
58
|
+
// Hook granted permission — skip interactive prompt and proceed to execution.
|
|
59
|
+
}
|
|
60
|
+
else if (hookOutcome.permissionDecision === "deny" || !hookOutcome.allowed) {
|
|
61
|
+
const reason = hookOutcome.reason ? `: ${hookOutcome.reason}` : "";
|
|
62
|
+
return { output: `Permission denied by hook${reason}`, isError: true };
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
// "ask" or no decision → fall through to interactive prompt
|
|
66
|
+
const allowed = await askUser(tool.name, description, tool.riskLevel);
|
|
67
|
+
if (!allowed) {
|
|
68
|
+
return { output: "Permission denied by user.", isError: true };
|
|
69
|
+
}
|
|
51
70
|
}
|
|
52
71
|
}
|
|
53
72
|
else {
|
|
@@ -79,12 +98,23 @@ export async function executeSingleTool(toolCall, tools, context, permissionMode
|
|
|
79
98
|
toolAbort.addEventListener("abort", () => reject(new Error(`Tool '${tool.name}' timed out after ${TOOL_TIMEOUT_MS / 1000}s`)));
|
|
80
99
|
}),
|
|
81
100
|
]);
|
|
82
|
-
// Hook: postToolUse
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
// Hook: postToolUse / postToolUseFailure (mutually exclusive — strict CC parity)
|
|
102
|
+
if (result.isError) {
|
|
103
|
+
emitHook("postToolUseFailure", {
|
|
104
|
+
toolName: tool.name,
|
|
105
|
+
toolArgs: JSON.stringify(toolCall.arguments).slice(0, 1000),
|
|
106
|
+
toolOutput: result.output.slice(0, 1000),
|
|
107
|
+
toolError: "ReportedError",
|
|
108
|
+
errorMessage: result.output.slice(0, 1000),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
emitHook("postToolUse", {
|
|
113
|
+
toolName: tool.name,
|
|
114
|
+
toolArgs: JSON.stringify(toolCall.arguments).slice(0, 1000),
|
|
115
|
+
toolOutput: result.output.slice(0, 1000),
|
|
116
|
+
});
|
|
117
|
+
}
|
|
88
118
|
// Emit fileChanged hook for file-modifying tools
|
|
89
119
|
if (!result.isError && ["Edit", "Write", "MultiEdit"].includes(tool.name)) {
|
|
90
120
|
const filePaths = getAffectedFiles(tool.name, parsed.data);
|
|
@@ -141,7 +171,15 @@ export async function executeSingleTool(toolCall, tools, context, permissionMode
|
|
|
141
171
|
return { output, isError: result.isError };
|
|
142
172
|
}
|
|
143
173
|
catch (err) {
|
|
144
|
-
|
|
174
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
175
|
+
const errName = err instanceof Error ? err.name : "ExecutionError";
|
|
176
|
+
emitHook("postToolUseFailure", {
|
|
177
|
+
toolName: tool.name,
|
|
178
|
+
toolArgs: JSON.stringify(toolCall.arguments).slice(0, 1000),
|
|
179
|
+
errorMessage: errMsg,
|
|
180
|
+
toolError: errName,
|
|
181
|
+
});
|
|
182
|
+
return { output: `Tool error: ${errMsg}`, isError: true };
|
|
145
183
|
}
|
|
146
184
|
}
|
|
147
185
|
export async function* executeToolCalls(toolCalls, tools, context, permissionMode, askUser, state) {
|
package/dist/query/types.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export type QueryConfig = {
|
|
|
20
20
|
workingDir?: string;
|
|
21
21
|
/** Auto-commit after each file-modifying tool */
|
|
22
22
|
gitCommitPerTool?: boolean;
|
|
23
|
+
/** For sub-agent invocations: the agent role name (feeds into the model router). */
|
|
24
|
+
role?: string;
|
|
23
25
|
};
|
|
24
26
|
export type TransitionReason = "next_turn" | "retry_network" | "retry_prompt_too_long" | "retry_max_output_tokens";
|
|
25
27
|
export type QueryLoopState = {
|
|
@@ -33,5 +35,9 @@ export type QueryLoopState = {
|
|
|
33
35
|
promptTooLongRetries?: number;
|
|
34
36
|
/** Track consecutive compression failures for circuit breaker */
|
|
35
37
|
compressionFailures?: number;
|
|
38
|
+
/** Whether the previous turn made any tool calls (feeds ModelRouter) */
|
|
39
|
+
lastTurnHadTools?: boolean;
|
|
40
|
+
/** Number of tool calls in the previous turn (feeds ModelRouter) */
|
|
41
|
+
lastTurnToolCount?: number;
|
|
36
42
|
};
|
|
37
43
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -99,7 +99,7 @@ export const AgentTool = {
|
|
|
99
99
|
const runAgent = async () => {
|
|
100
100
|
let finalText = "";
|
|
101
101
|
try {
|
|
102
|
-
for await (const event of query(input.prompt, config)) {
|
|
102
|
+
for await (const event of query(input.prompt, { ...config, role: role?.id })) {
|
|
103
103
|
if (event.type === "text_delta")
|
|
104
104
|
finalText += event.content;
|
|
105
105
|
}
|
|
@@ -137,7 +137,7 @@ export const AgentTool = {
|
|
|
137
137
|
let finalText = "";
|
|
138
138
|
try {
|
|
139
139
|
try {
|
|
140
|
-
for await (const event of query(input.prompt, config)) {
|
|
140
|
+
for await (const event of query(input.prompt, { ...config, role: role?.id })) {
|
|
141
141
|
if (event.type === "text_delta") {
|
|
142
142
|
finalText += event.content;
|
|
143
143
|
}
|
|
@@ -5,12 +5,12 @@ declare const inputSchema: z.ZodObject<{
|
|
|
5
5
|
reason: z.ZodString;
|
|
6
6
|
prompt: z.ZodString;
|
|
7
7
|
}, "strip", z.ZodTypeAny, {
|
|
8
|
-
prompt: string;
|
|
9
8
|
reason: string;
|
|
9
|
+
prompt: string;
|
|
10
10
|
delaySeconds: number;
|
|
11
11
|
}, {
|
|
12
|
-
prompt: string;
|
|
13
12
|
reason: string;
|
|
13
|
+
prompt: string;
|
|
14
14
|
delaySeconds: number;
|
|
15
15
|
}>;
|
|
16
16
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhijiewang/openharness",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.14.0",
|
|
4
4
|
"description": "Open-source terminal coding agent. Works with any LLM.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -84,5 +84,8 @@
|
|
|
84
84
|
"bugs": {
|
|
85
85
|
"url": "https://github.com/zhijiewong/openharness/issues"
|
|
86
86
|
},
|
|
87
|
-
"homepage": "https://github.com/zhijiewong/openharness#readme"
|
|
87
|
+
"homepage": "https://github.com/zhijiewong/openharness#readme",
|
|
88
|
+
"optionalDependencies": {
|
|
89
|
+
"@napi-rs/keyring": "^1.2.0"
|
|
90
|
+
}
|
|
88
91
|
}
|