@cjhyy/code-shell 0.1.0-alpha.0 → 0.1.0-alpha.1
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/CHANGELOG.md +39 -0
- package/dist/{chunk-6JOOLG6F.js → chunk-AP5F6VYE.js} +5 -2
- package/dist/{chunk-BHGIJ2DJ.js → chunk-FECTK5KJ.js} +6 -3
- package/dist/{chunk-7RMAR2Y3.js → chunk-JUHRV2XF.js} +278 -57
- package/dist/{chunk-R32DJSJE.cjs → chunk-MOILS4WE.cjs} +8 -5
- package/dist/{chunk-QHDYFC5U.cjs → chunk-Q6BYFYGD.cjs} +406 -185
- package/dist/{chunk-WFQF3RT4.cjs → chunk-WYKNZ54E.cjs} +11 -8
- package/dist/cli/main.cjs +20 -18
- package/dist/cli/main.js +5 -3
- package/dist/engine/engine.d.ts +12 -0
- package/dist/engine/turn-loop.d.ts +6 -4
- package/dist/index.cjs +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/product/index.cjs +3 -3
- package/dist/product/index.js +2 -2
- package/dist/product/types.d.ts +2 -7
- package/dist/run/EngineRunner.d.ts +3 -1
- package/dist/run/RunManager.d.ts +6 -0
- package/dist/run/factory.d.ts +7 -1
- package/dist/run/index.cjs +3 -3
- package/dist/run/index.js +2 -2
- package/dist/tool-system/builtin/agent-registry.d.ts +36 -0
- package/dist/tool-system/builtin/agent.d.ts +4 -0
- package/dist/tool-system/registry.d.ts +6 -0
- package/dist/types.d.ts +6 -0
- package/dist/{updater-URVNPDPW.cjs → updater-2N6745WR.cjs} +1 -1
- package/dist/{updater-AVD4LKTQ.js → updater-PV7FKSUI.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,45 @@ breaking.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.1.0-alpha.1] - 2026-04-30
|
|
12
|
+
|
|
13
|
+
### Changed (breaking)
|
|
14
|
+
- **Tool timeout system rewritten.** Removed the hardcoded
|
|
15
|
+
`LEGACY_LONG_TIMEOUT_TOOLS = {Agent, Arena}` whitelist in
|
|
16
|
+
`tool-system/registry.ts`. Tools now declare their own timeout via
|
|
17
|
+
`RegisteredTool.timeoutMs` at registration time. Precedence:
|
|
18
|
+
`executeTool(opts.timeoutMs)` > `tool.timeoutMs` > `DEFAULT_TOOL_TIMEOUT_MS`
|
|
19
|
+
(120s). Custom long-running tools registered via `engine.registerCustomTool`
|
|
20
|
+
can now set a higher timeout instead of being silently capped at 120s.
|
|
21
|
+
- **Bash internal 600s cap removed.** `bash.ts` no longer clamps the
|
|
22
|
+
user-supplied `timeout` argument. The outer registry caps via the tool's
|
|
23
|
+
declared `timeoutMs` (default 1h for Bash). Long-running shell loops
|
|
24
|
+
(`until cond; do ...; done`) are now feasible.
|
|
25
|
+
- **Agent `max_turns` upper bound (30) removed.** Sub-agents that need to do
|
|
26
|
+
more turns (deep research, large refactors) are no longer artificially
|
|
27
|
+
capped. Default remains 15.
|
|
28
|
+
- **Sub-agent LLM call timeout (60s) removed.** `agent.ts` no longer clamps
|
|
29
|
+
`subAgentConfig.llm.timeout` to 60s, which was breaking slow models
|
|
30
|
+
(e.g. extended thinking).
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **`Agent(run_in_background: true)`** — fire-and-forget sub-agents. Returns
|
|
34
|
+
an `agent_id` immediately instead of blocking the parent turn. The agent
|
|
35
|
+
runs detached in the same process; restarting loses its state.
|
|
36
|
+
- **`AgentStatus(agent_id?)`** — query background agent state
|
|
37
|
+
(running / completed / failed / cancelled), or list all when `agent_id` is
|
|
38
|
+
omitted. Returns the result text once completed.
|
|
39
|
+
- **`AgentCancel(agent_id)`** — abort a running background agent.
|
|
40
|
+
- New module `src/tool-system/builtin/agent-registry.ts` — in-process
|
|
41
|
+
registry for async agent handles.
|
|
42
|
+
|
|
43
|
+
### Notes
|
|
44
|
+
- Cross-process / restart-survivable long tasks still belong to `RunManager`
|
|
45
|
+
in `@cjhyy/code-shell/run`, not to `Agent(run_in_background)`. The split
|
|
46
|
+
mirrors Claude Code's REPL/Agent-tool/Routines architecture.
|
|
47
|
+
|
|
48
|
+
## [0.1.0-alpha.0] - 2026-04-28
|
|
49
|
+
|
|
11
50
|
### Added
|
|
12
51
|
- **`IterativeArena` — multi-model authoring loop.** Pipeline: tournament v1
|
|
13
52
|
(every participant writes a draft, the author merges anonymized drafts into
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
NoopEvaluator,
|
|
5
5
|
RunManager,
|
|
6
6
|
registerPreset
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JUHRV2XF.js";
|
|
8
8
|
import {
|
|
9
9
|
init_esm_shims
|
|
10
10
|
} from "./chunk-DI7RDLOS.js";
|
|
@@ -90,11 +90,14 @@ function defineProduct(definition, runtime) {
|
|
|
90
90
|
disabledBuiltinTools: adapter?.disableTools,
|
|
91
91
|
customSystemPrompt: presetDef.customPrompt,
|
|
92
92
|
appendSystemPrompt: presetDef.appendPrompt,
|
|
93
|
+
hooks: adapter?.hooks,
|
|
93
94
|
customTools: customToolEntries.length > 0 ? customToolEntries : void 0
|
|
94
95
|
},
|
|
95
96
|
concurrency: contract?.concurrency ?? 1,
|
|
96
97
|
runsDir,
|
|
97
|
-
evaluator
|
|
98
|
+
evaluator,
|
|
99
|
+
defaultTags: contract?.defaultTags,
|
|
100
|
+
defaultMetadata: contract?.defaultMetadata
|
|
98
101
|
});
|
|
99
102
|
return { manager, preset: agentPreset, customTools };
|
|
100
103
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
FileRunStore,
|
|
3
3
|
RunManager
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JUHRV2XF.js";
|
|
5
5
|
import {
|
|
6
6
|
init_esm_shims
|
|
7
7
|
} from "./chunk-DI7RDLOS.js";
|
|
@@ -28,11 +28,14 @@ function createRunManager(options) {
|
|
|
28
28
|
enabledBuiltinTools: options.enabledBuiltinTools,
|
|
29
29
|
disabledBuiltinTools: options.disabledBuiltinTools,
|
|
30
30
|
customSystemPrompt: options.customSystemPrompt,
|
|
31
|
-
appendSystemPrompt: options.appendSystemPrompt
|
|
31
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
32
|
+
hooks: options.hooks
|
|
32
33
|
},
|
|
33
34
|
concurrency: options.concurrency ?? 1,
|
|
34
35
|
runsDir,
|
|
35
|
-
evaluator: options.evaluator
|
|
36
|
+
evaluator: options.evaluator,
|
|
37
|
+
defaultTags: options.defaultTags,
|
|
38
|
+
defaultMetadata: options.defaultMetadata
|
|
36
39
|
});
|
|
37
40
|
}
|
|
38
41
|
|
|
@@ -814,7 +814,7 @@ var bashToolDef = {
|
|
|
814
814
|
command: { type: "string", description: "The shell command to execute" },
|
|
815
815
|
timeout: {
|
|
816
816
|
type: "number",
|
|
817
|
-
description: "Timeout in milliseconds (default: 120000
|
|
817
|
+
description: "Timeout in milliseconds (default: 120000). Outer registry caps at 1h."
|
|
818
818
|
},
|
|
819
819
|
description: {
|
|
820
820
|
type: "string",
|
|
@@ -828,7 +828,7 @@ var MAX_OUTPUT = 1e5;
|
|
|
828
828
|
async function bashTool(args) {
|
|
829
829
|
const command = args.command;
|
|
830
830
|
if (!command) return "Error: command is required";
|
|
831
|
-
const timeout =
|
|
831
|
+
const timeout = args.timeout || 12e4;
|
|
832
832
|
try {
|
|
833
833
|
const { stdout, stderr } = await execAsync(command, {
|
|
834
834
|
timeout,
|
|
@@ -903,6 +903,63 @@ async function askUserTool(args) {
|
|
|
903
903
|
// src/tool-system/builtin/agent.ts
|
|
904
904
|
init_esm_shims();
|
|
905
905
|
init_plan();
|
|
906
|
+
|
|
907
|
+
// src/tool-system/builtin/agent-registry.ts
|
|
908
|
+
init_esm_shims();
|
|
909
|
+
var AsyncAgentRegistry = class {
|
|
910
|
+
agents = /* @__PURE__ */ new Map();
|
|
911
|
+
register(entry) {
|
|
912
|
+
this.agents.set(entry.agentId, entry);
|
|
913
|
+
}
|
|
914
|
+
get(agentId) {
|
|
915
|
+
return this.agents.get(agentId);
|
|
916
|
+
}
|
|
917
|
+
list() {
|
|
918
|
+
return [...this.agents.values()];
|
|
919
|
+
}
|
|
920
|
+
markCompleted(agentId, result) {
|
|
921
|
+
const e = this.agents.get(agentId);
|
|
922
|
+
if (!e) return;
|
|
923
|
+
if (e.status !== "running") return;
|
|
924
|
+
e.status = "completed";
|
|
925
|
+
e.result = result;
|
|
926
|
+
e.finishedAt = Date.now();
|
|
927
|
+
}
|
|
928
|
+
markFailed(agentId, error) {
|
|
929
|
+
const e = this.agents.get(agentId);
|
|
930
|
+
if (!e) return;
|
|
931
|
+
if (e.status !== "running") return;
|
|
932
|
+
e.status = "failed";
|
|
933
|
+
e.error = error;
|
|
934
|
+
e.finishedAt = Date.now();
|
|
935
|
+
}
|
|
936
|
+
cancel(agentId) {
|
|
937
|
+
const e = this.agents.get(agentId);
|
|
938
|
+
if (!e) return false;
|
|
939
|
+
if (e.status !== "running") return false;
|
|
940
|
+
try {
|
|
941
|
+
e.abort();
|
|
942
|
+
} catch {
|
|
943
|
+
}
|
|
944
|
+
e.status = "cancelled";
|
|
945
|
+
e.finishedAt = Date.now();
|
|
946
|
+
return true;
|
|
947
|
+
}
|
|
948
|
+
reset() {
|
|
949
|
+
for (const e of this.agents.values()) {
|
|
950
|
+
if (e.status === "running") {
|
|
951
|
+
try {
|
|
952
|
+
e.abort();
|
|
953
|
+
} catch {
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
this.agents.clear();
|
|
958
|
+
}
|
|
959
|
+
};
|
|
960
|
+
var asyncAgentRegistry = new AsyncAgentRegistry();
|
|
961
|
+
|
|
962
|
+
// src/tool-system/builtin/agent.ts
|
|
906
963
|
import { nanoid } from "nanoid";
|
|
907
964
|
var agentToolDef = {
|
|
908
965
|
name: "Agent",
|
|
@@ -921,6 +978,10 @@ var agentToolDef = {
|
|
|
921
978
|
max_turns: {
|
|
922
979
|
type: "number",
|
|
923
980
|
description: "Maximum turns for the sub-agent (default: 15)"
|
|
981
|
+
},
|
|
982
|
+
run_in_background: {
|
|
983
|
+
type: "boolean",
|
|
984
|
+
description: "If true, launch the sub-agent in the background and return an agent_id immediately instead of waiting for it to finish. Use AgentStatus(agent_id) to check progress and AgentCancel(agent_id) to stop it. The agent runs in this process; restarting loses its state. Default: false (synchronous wait)."
|
|
924
985
|
}
|
|
925
986
|
},
|
|
926
987
|
required: ["description", "prompt"]
|
|
@@ -930,35 +991,21 @@ var _subAgentConfig;
|
|
|
930
991
|
function setSubAgentConfig(config) {
|
|
931
992
|
_subAgentConfig = config;
|
|
932
993
|
}
|
|
933
|
-
async function
|
|
934
|
-
|
|
935
|
-
const
|
|
936
|
-
if (!prompt) return "Error: prompt is required";
|
|
937
|
-
if (!_subAgentConfig) {
|
|
938
|
-
return "Error: Agent tool is not configured.";
|
|
939
|
-
}
|
|
940
|
-
const signal = args.__signal;
|
|
941
|
-
if (signal?.aborted) {
|
|
942
|
-
return "Agent aborted before starting.";
|
|
943
|
-
}
|
|
944
|
-
const maxTurns = Math.min(args.max_turns || 15, 30);
|
|
945
|
-
const agentId = nanoid(8);
|
|
946
|
-
const parentStream = _subAgentConfig.onStream;
|
|
994
|
+
async function runSubAgent(opts) {
|
|
995
|
+
if (!_subAgentConfig) throw new Error("Agent tool is not configured.");
|
|
996
|
+
const { agentId, description, prompt, maxTurns, signal, parentStream } = opts;
|
|
947
997
|
parentStream?.({ type: "agent_start", agentId, description });
|
|
948
998
|
const childStream = (event) => {
|
|
949
999
|
if (!parentStream) return;
|
|
950
1000
|
const tagged = { ...event, agentId };
|
|
951
1001
|
parentStream(tagged);
|
|
952
1002
|
};
|
|
1003
|
+
const parentWasInPlanMode = isInPlanMode();
|
|
1004
|
+
if (parentWasInPlanMode) resetPlanMode();
|
|
953
1005
|
try {
|
|
954
|
-
const parentWasInPlanMode = isInPlanMode();
|
|
955
|
-
if (parentWasInPlanMode) {
|
|
956
|
-
resetPlanMode();
|
|
957
|
-
}
|
|
958
1006
|
const engine = _subAgentConfig.createEngine({
|
|
959
1007
|
llm: {
|
|
960
1008
|
..._subAgentConfig.llm,
|
|
961
|
-
timeout: Math.min(_subAgentConfig.llm.timeout ?? 12e4, 6e4),
|
|
962
1009
|
retryMaxAttempts: 2
|
|
963
1010
|
},
|
|
964
1011
|
cwd: _subAgentConfig.cwd,
|
|
@@ -972,27 +1019,142 @@ async function agentTool(args) {
|
|
|
972
1019
|
maxContextTokens: _subAgentConfig.maxContextTokens,
|
|
973
1020
|
sessionStorageDir: _subAgentConfig.sessionStorageDir
|
|
974
1021
|
});
|
|
975
|
-
|
|
976
|
-
try {
|
|
977
|
-
result = await engine.run(prompt, { signal, onStream: childStream });
|
|
978
|
-
} finally {
|
|
979
|
-
if (parentWasInPlanMode) {
|
|
980
|
-
restorePlanMode();
|
|
981
|
-
}
|
|
982
|
-
}
|
|
1022
|
+
const result = await engine.run(prompt, { signal, onStream: childStream });
|
|
983
1023
|
parentStream?.({ type: "agent_end", agentId, description });
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1024
|
+
return result.text || `Agent completed (${result.reason}) but produced no text output.`;
|
|
1025
|
+
} finally {
|
|
1026
|
+
if (parentWasInPlanMode) restorePlanMode();
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
async function agentTool(args) {
|
|
1030
|
+
const prompt = args.prompt;
|
|
1031
|
+
const description = args.description || "sub-agent";
|
|
1032
|
+
if (!prompt) return "Error: prompt is required";
|
|
1033
|
+
if (!_subAgentConfig) {
|
|
1034
|
+
return "Error: Agent tool is not configured.";
|
|
1035
|
+
}
|
|
1036
|
+
const parentSignal = args.__signal;
|
|
1037
|
+
if (parentSignal?.aborted) {
|
|
1038
|
+
return "Agent aborted before starting.";
|
|
1039
|
+
}
|
|
1040
|
+
const maxTurns = args.max_turns || 15;
|
|
1041
|
+
const runInBackground = args.run_in_background === true;
|
|
1042
|
+
const agentId = nanoid(8);
|
|
1043
|
+
const parentStream = _subAgentConfig.onStream;
|
|
1044
|
+
if (runInBackground) {
|
|
1045
|
+
const controller = new AbortController();
|
|
1046
|
+
asyncAgentRegistry.register({
|
|
1047
|
+
agentId,
|
|
1048
|
+
description,
|
|
1049
|
+
status: "running",
|
|
1050
|
+
startedAt: Date.now(),
|
|
1051
|
+
abort: () => controller.abort()
|
|
1052
|
+
});
|
|
1053
|
+
void runSubAgent({
|
|
1054
|
+
agentId,
|
|
1055
|
+
description,
|
|
1056
|
+
prompt,
|
|
1057
|
+
maxTurns,
|
|
1058
|
+
signal: controller.signal,
|
|
1059
|
+
parentStream
|
|
1060
|
+
}).then((text) => asyncAgentRegistry.markCompleted(agentId, text)).catch((err) => {
|
|
1061
|
+
if (controller.signal.aborted) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
asyncAgentRegistry.markFailed(agentId, err.message);
|
|
1065
|
+
});
|
|
1066
|
+
return [
|
|
1067
|
+
`Agent launched in background.`,
|
|
1068
|
+
`agent_id: ${agentId}`,
|
|
1069
|
+
`description: ${description}`,
|
|
1070
|
+
``,
|
|
1071
|
+
`Use AgentStatus(agent_id="${agentId}") to check progress or fetch the result.`,
|
|
1072
|
+
`Use AgentCancel(agent_id="${agentId}") to stop it.`
|
|
1073
|
+
].join("\n");
|
|
1074
|
+
}
|
|
1075
|
+
try {
|
|
1076
|
+
return await runSubAgent({
|
|
1077
|
+
agentId,
|
|
1078
|
+
description,
|
|
1079
|
+
prompt,
|
|
1080
|
+
maxTurns,
|
|
1081
|
+
signal: parentSignal ?? new AbortController().signal,
|
|
1082
|
+
parentStream
|
|
1083
|
+
});
|
|
988
1084
|
} catch (err) {
|
|
989
1085
|
parentStream?.({ type: "agent_end", agentId, description, error: err.message });
|
|
990
|
-
if (
|
|
1086
|
+
if (parentSignal?.aborted) {
|
|
991
1087
|
return "Agent was aborted.";
|
|
992
1088
|
}
|
|
993
1089
|
return `Agent error: ${err.message}`;
|
|
994
1090
|
}
|
|
995
1091
|
}
|
|
1092
|
+
var agentStatusToolDef = {
|
|
1093
|
+
name: "AgentStatus",
|
|
1094
|
+
description: "Check the status of a background agent launched with Agent(run_in_background=true). Returns running / completed / failed / cancelled, plus the result text once finished. Omit agent_id to list all background agents in this process.",
|
|
1095
|
+
inputSchema: {
|
|
1096
|
+
type: "object",
|
|
1097
|
+
properties: {
|
|
1098
|
+
agent_id: {
|
|
1099
|
+
type: "string",
|
|
1100
|
+
description: "The agent_id returned by Agent(run_in_background=true). Omit to list all."
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
1105
|
+
async function agentStatusTool(args) {
|
|
1106
|
+
const agentId = args.agent_id;
|
|
1107
|
+
if (!agentId) {
|
|
1108
|
+
const all = asyncAgentRegistry.list();
|
|
1109
|
+
if (all.length === 0) return "No background agents in this process.";
|
|
1110
|
+
return all.map((e2) => {
|
|
1111
|
+
const dur2 = ((e2.finishedAt ?? Date.now()) - e2.startedAt) / 1e3;
|
|
1112
|
+
return `${e2.agentId} [${e2.status}] ${e2.description} (${dur2.toFixed(1)}s)`;
|
|
1113
|
+
}).join("\n");
|
|
1114
|
+
}
|
|
1115
|
+
const e = asyncAgentRegistry.get(agentId);
|
|
1116
|
+
if (!e) return `Error: agent_id "${agentId}" not found.`;
|
|
1117
|
+
const dur = ((e.finishedAt ?? Date.now()) - e.startedAt) / 1e3;
|
|
1118
|
+
const lines = [
|
|
1119
|
+
`agent_id: ${e.agentId}`,
|
|
1120
|
+
`status: ${e.status}`,
|
|
1121
|
+
`description: ${e.description}`,
|
|
1122
|
+
`duration: ${dur.toFixed(1)}s`
|
|
1123
|
+
];
|
|
1124
|
+
if (e.status === "completed" && e.result) {
|
|
1125
|
+
lines.push("", "\u2500\u2500 result \u2500\u2500", e.result);
|
|
1126
|
+
} else if (e.status === "failed" && e.error) {
|
|
1127
|
+
lines.push("", "\u2500\u2500 error \u2500\u2500", e.error);
|
|
1128
|
+
} else if (e.status === "running") {
|
|
1129
|
+
lines.push("", "(still running \u2014 call AgentStatus again later)");
|
|
1130
|
+
}
|
|
1131
|
+
return lines.join("\n");
|
|
1132
|
+
}
|
|
1133
|
+
var agentCancelToolDef = {
|
|
1134
|
+
name: "AgentCancel",
|
|
1135
|
+
description: "Cancel a background agent launched with Agent(run_in_background=true). The agent's current LLM call and any in-flight tools will be aborted.",
|
|
1136
|
+
inputSchema: {
|
|
1137
|
+
type: "object",
|
|
1138
|
+
properties: {
|
|
1139
|
+
agent_id: {
|
|
1140
|
+
type: "string",
|
|
1141
|
+
description: "The agent_id to cancel."
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
required: ["agent_id"]
|
|
1145
|
+
}
|
|
1146
|
+
};
|
|
1147
|
+
async function agentCancelTool(args) {
|
|
1148
|
+
const agentId = args.agent_id;
|
|
1149
|
+
if (!agentId) return "Error: agent_id is required.";
|
|
1150
|
+
const e = asyncAgentRegistry.get(agentId);
|
|
1151
|
+
if (!e) return `Error: agent_id "${agentId}" not found.`;
|
|
1152
|
+
if (e.status !== "running") {
|
|
1153
|
+
return `Agent ${agentId} is already ${e.status}; nothing to cancel.`;
|
|
1154
|
+
}
|
|
1155
|
+
const ok = asyncAgentRegistry.cancel(agentId);
|
|
1156
|
+
return ok ? `Agent ${agentId} cancelled.` : `Failed to cancel agent ${agentId}.`;
|
|
1157
|
+
}
|
|
996
1158
|
|
|
997
1159
|
// src/tool-system/builtin/index.ts
|
|
998
1160
|
init_plan();
|
|
@@ -2321,7 +2483,9 @@ var BUILTIN_TOOLS = [
|
|
|
2321
2483
|
source: "builtin",
|
|
2322
2484
|
permissionDefault: "ask",
|
|
2323
2485
|
isReadOnly: false,
|
|
2324
|
-
isConcurrencySafe: false
|
|
2486
|
+
isConcurrencySafe: false,
|
|
2487
|
+
timeoutMs: 36e5
|
|
2488
|
+
// 1h — supports long-running shell loops (e.g. `until` polling)
|
|
2325
2489
|
},
|
|
2326
2490
|
execute: bashTool
|
|
2327
2491
|
},
|
|
@@ -2361,10 +2525,32 @@ var BUILTIN_TOOLS = [
|
|
|
2361
2525
|
source: "builtin",
|
|
2362
2526
|
permissionDefault: "allow",
|
|
2363
2527
|
isReadOnly: true,
|
|
2364
|
-
isConcurrencySafe: true
|
|
2528
|
+
isConcurrencySafe: true,
|
|
2529
|
+
timeoutMs: 18e5
|
|
2530
|
+
// 30min — sub-agent runs may execute many tool calls
|
|
2365
2531
|
},
|
|
2366
2532
|
execute: agentTool
|
|
2367
2533
|
},
|
|
2534
|
+
{
|
|
2535
|
+
definition: {
|
|
2536
|
+
...agentStatusToolDef,
|
|
2537
|
+
source: "builtin",
|
|
2538
|
+
permissionDefault: "allow",
|
|
2539
|
+
isReadOnly: true,
|
|
2540
|
+
isConcurrencySafe: true
|
|
2541
|
+
},
|
|
2542
|
+
execute: agentStatusTool
|
|
2543
|
+
},
|
|
2544
|
+
{
|
|
2545
|
+
definition: {
|
|
2546
|
+
...agentCancelToolDef,
|
|
2547
|
+
source: "builtin",
|
|
2548
|
+
permissionDefault: "allow",
|
|
2549
|
+
isReadOnly: false,
|
|
2550
|
+
isConcurrencySafe: false
|
|
2551
|
+
},
|
|
2552
|
+
execute: agentCancelTool
|
|
2553
|
+
},
|
|
2368
2554
|
{
|
|
2369
2555
|
definition: {
|
|
2370
2556
|
...enterPlanModeToolDef,
|
|
@@ -2647,7 +2833,9 @@ var BUILTIN_TOOLS = [
|
|
|
2647
2833
|
source: "builtin",
|
|
2648
2834
|
permissionDefault: "ask",
|
|
2649
2835
|
isReadOnly: true,
|
|
2650
|
-
isConcurrencySafe: false
|
|
2836
|
+
isConcurrencySafe: false,
|
|
2837
|
+
timeoutMs: 18e5
|
|
2838
|
+
// 30min — multi-model debate rounds take time
|
|
2651
2839
|
},
|
|
2652
2840
|
execute: arenaTool
|
|
2653
2841
|
}
|
|
@@ -2655,6 +2843,7 @@ var BUILTIN_TOOLS = [
|
|
|
2655
2843
|
|
|
2656
2844
|
// src/tool-system/registry.ts
|
|
2657
2845
|
init_esm_shims();
|
|
2846
|
+
var DEFAULT_TOOL_TIMEOUT_MS = 12e4;
|
|
2658
2847
|
var ToolRegistry = class {
|
|
2659
2848
|
tools = /* @__PURE__ */ new Map();
|
|
2660
2849
|
builtinExecutors = /* @__PURE__ */ new Map();
|
|
@@ -2709,10 +2898,7 @@ var ToolRegistry = class {
|
|
|
2709
2898
|
if (!executor) {
|
|
2710
2899
|
throw new ToolExecutionError(name, "No executor registered for this tool");
|
|
2711
2900
|
}
|
|
2712
|
-
const
|
|
2713
|
-
const isLongRunning = LONG_TIMEOUT_TOOLS.has(name);
|
|
2714
|
-
const defaultTimeout = isLongRunning ? 18e5 : 12e4;
|
|
2715
|
-
const timeout = options?.timeoutMs ?? defaultTimeout;
|
|
2901
|
+
const timeout = options?.timeoutMs ?? tool.timeoutMs ?? DEFAULT_TOOL_TIMEOUT_MS;
|
|
2716
2902
|
const parentSignal = options?.signal;
|
|
2717
2903
|
const id = `tool_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
|
|
2718
2904
|
if (parentSignal?.aborted) {
|
|
@@ -3126,7 +3312,7 @@ var InteractiveApprovalBackend = class {
|
|
|
3126
3312
|
if (toolRule === "allow") return { approved: true };
|
|
3127
3313
|
if (toolRule === "deny") return { approved: false };
|
|
3128
3314
|
if (!this.promptFn) {
|
|
3129
|
-
return { approved:
|
|
3315
|
+
return { approved: false, reason: "interactive approval backend has no prompt function" };
|
|
3130
3316
|
}
|
|
3131
3317
|
const result = await this.promptFn(req);
|
|
3132
3318
|
if (result.always && result.approved) {
|
|
@@ -3870,6 +4056,8 @@ var GENERAL_BUILTIN_TOOLS = [
|
|
|
3870
4056
|
"WebFetch",
|
|
3871
4057
|
"AskUserQuestion",
|
|
3872
4058
|
"Agent",
|
|
4059
|
+
"AgentStatus",
|
|
4060
|
+
"AgentCancel",
|
|
3873
4061
|
"EnterPlanMode",
|
|
3874
4062
|
"ExitPlanMode",
|
|
3875
4063
|
"ToolSearch",
|
|
@@ -3909,6 +4097,8 @@ var GENERAL_PERMISSION_RULES = [
|
|
|
3909
4097
|
{ tool: "WebFetch", decision: "allow" },
|
|
3910
4098
|
{ tool: "AskUserQuestion", decision: "allow" },
|
|
3911
4099
|
{ tool: "Agent", decision: "allow" },
|
|
4100
|
+
{ tool: "AgentStatus", decision: "allow" },
|
|
4101
|
+
{ tool: "AgentCancel", decision: "allow" },
|
|
3912
4102
|
{ tool: "EnterPlanMode", decision: "allow" },
|
|
3913
4103
|
{ tool: "ExitPlanMode", decision: "allow" },
|
|
3914
4104
|
{ tool: "ToolSearch", decision: "allow" },
|
|
@@ -4433,19 +4623,19 @@ var TurnLoop = class {
|
|
|
4433
4623
|
} catch (retryErr) {
|
|
4434
4624
|
if (!(retryErr instanceof ContextLimitError)) {
|
|
4435
4625
|
this.config.onStream?.({ type: "error", error: retryErr.message });
|
|
4436
|
-
return { text: finalText, reason: "model_error" };
|
|
4626
|
+
return { text: finalText, reason: "model_error", messages };
|
|
4437
4627
|
}
|
|
4438
4628
|
}
|
|
4439
4629
|
}
|
|
4440
4630
|
if (!recovered) {
|
|
4441
4631
|
this.patchOrphanedToolUses(messages);
|
|
4442
4632
|
this.config.onStream?.({ type: "error", error: "Context limit exceeded after 3 recovery attempts" });
|
|
4443
|
-
return { text: finalText, reason: "prompt_too_long" };
|
|
4633
|
+
return { text: finalText, reason: "prompt_too_long", messages };
|
|
4444
4634
|
}
|
|
4445
4635
|
} else {
|
|
4446
4636
|
this.patchOrphanedToolUses(messages);
|
|
4447
4637
|
this.config.onStream?.({ type: "error", error: err.message });
|
|
4448
|
-
return { text: finalText, reason: "model_error" };
|
|
4638
|
+
return { text: finalText, reason: "model_error", messages };
|
|
4449
4639
|
}
|
|
4450
4640
|
}
|
|
4451
4641
|
if (response.usage?.promptTokens !== void 0) {
|
|
@@ -4483,7 +4673,7 @@ var TurnLoop = class {
|
|
|
4483
4673
|
response = { ...response, text: combinedText };
|
|
4484
4674
|
}
|
|
4485
4675
|
if (this.config.signal?.aborted) {
|
|
4486
|
-
return { text: finalText, reason: "aborted_streaming" };
|
|
4676
|
+
return { text: finalText, reason: "aborted_streaming", messages };
|
|
4487
4677
|
}
|
|
4488
4678
|
if (response.text) {
|
|
4489
4679
|
finalText = response.text;
|
|
@@ -4502,7 +4692,8 @@ var TurnLoop = class {
|
|
|
4502
4692
|
turnNumber: this.turnCount,
|
|
4503
4693
|
hasToolUse: false
|
|
4504
4694
|
});
|
|
4505
|
-
|
|
4695
|
+
messages.push({ role: "assistant", content: finalText });
|
|
4696
|
+
return { text: finalText, reason: "completed", messages };
|
|
4506
4697
|
}
|
|
4507
4698
|
logger.info("turn.tool_use", { turn: this.turnCount, tools: response.toolCalls.map((t) => t.toolName) });
|
|
4508
4699
|
const toolCalls = response.toolCalls.slice(0, this.config.maxToolCallsPerTurn);
|
|
@@ -4566,7 +4757,8 @@ var TurnLoop = class {
|
|
|
4566
4757
|
type: "assistant_message",
|
|
4567
4758
|
message: { role: "assistant", content: finalText }
|
|
4568
4759
|
});
|
|
4569
|
-
|
|
4760
|
+
messages.push({ role: "assistant", content: finalText });
|
|
4761
|
+
return { text: finalText, reason: "completed", messages };
|
|
4570
4762
|
}
|
|
4571
4763
|
if (budgetDecision === "nudge") {
|
|
4572
4764
|
messages.push({
|
|
@@ -4607,9 +4799,10 @@ var TurnLoop = class {
|
|
|
4607
4799
|
type: "assistant_message",
|
|
4608
4800
|
message: { role: "assistant", content: finalText }
|
|
4609
4801
|
});
|
|
4802
|
+
messages.push({ role: "assistant", content: finalText });
|
|
4610
4803
|
}
|
|
4611
4804
|
this.config.onStream?.({ type: "turn_complete", reason: "max_turns" });
|
|
4612
|
-
return { text: finalText, reason: "max_turns" };
|
|
4805
|
+
return { text: finalText, reason: "max_turns", messages };
|
|
4613
4806
|
}
|
|
4614
4807
|
/**
|
|
4615
4808
|
* Call model with streaming fallback.
|
|
@@ -4750,6 +4943,9 @@ var Engine = class _Engine {
|
|
|
4750
4943
|
})
|
|
4751
4944
|
});
|
|
4752
4945
|
this.hooks = new HookRegistry();
|
|
4946
|
+
for (const hook of config.hooks ?? []) {
|
|
4947
|
+
this.hooks.register(hook.event, hook.handler, hook.priority, hook.name);
|
|
4948
|
+
}
|
|
4753
4949
|
this.sessionManager = new SessionManager(config.sessionStorageDir);
|
|
4754
4950
|
this.modelPool = new ModelPool();
|
|
4755
4951
|
try {
|
|
@@ -4819,7 +5015,7 @@ var Engine = class _Engine {
|
|
|
4819
5015
|
let messages;
|
|
4820
5016
|
if (options?.sessionId) {
|
|
4821
5017
|
session = this.sessionManager.resume(options.sessionId);
|
|
4822
|
-
messages = session.transcript.toMessages();
|
|
5018
|
+
messages = this.compactedMessagesBySession.get(options.sessionId) ? [...this.compactedMessagesBySession.get(options.sessionId)] : session.transcript.toMessages();
|
|
4823
5019
|
if (session.state.costState && this.config.costStore) {
|
|
4824
5020
|
this.config.costStore.restore(session.state.costState);
|
|
4825
5021
|
}
|
|
@@ -4860,7 +5056,7 @@ var Engine = class _Engine {
|
|
|
4860
5056
|
approvalBackend = new AutoApprovalBackend();
|
|
4861
5057
|
} else {
|
|
4862
5058
|
approvalBackend = new HeadlessApprovalBackend(
|
|
4863
|
-
mode === "bypassPermissions" ? "approve-all" : mode === "dontAsk" ? "deny-all" : "
|
|
5059
|
+
mode === "bypassPermissions" ? "approve-all" : mode === "dontAsk" ? "deny-all" : "deny-all"
|
|
4864
5060
|
);
|
|
4865
5061
|
}
|
|
4866
5062
|
const permission = new PermissionClassifier(defaultRules, mode, approvalBackend);
|
|
@@ -4912,6 +5108,8 @@ var Engine = class _Engine {
|
|
|
4912
5108
|
if (userContextMsg) {
|
|
4913
5109
|
messages.unshift(userContextMsg);
|
|
4914
5110
|
}
|
|
5111
|
+
this.lastSessionId = session.state.sessionId;
|
|
5112
|
+
this.lastMessages = messages;
|
|
4915
5113
|
contextManager.setTranscriptPath(session.transcript.getFilePath());
|
|
4916
5114
|
contextManager.setSummarizeFn(async (prompt) => {
|
|
4917
5115
|
const summaryResponse = await llmClient.createMessage({
|
|
@@ -4977,6 +5175,11 @@ var Engine = class _Engine {
|
|
|
4977
5175
|
}
|
|
4978
5176
|
);
|
|
4979
5177
|
const result = await turnLoop.run(messages);
|
|
5178
|
+
this.lastMessages = result.messages;
|
|
5179
|
+
this.compactedMessagesBySession.set(
|
|
5180
|
+
session.state.sessionId,
|
|
5181
|
+
this.stripUserContextMessage(result.messages, userContextMsg)
|
|
5182
|
+
);
|
|
4980
5183
|
logger.info("engine.done", {
|
|
4981
5184
|
sessionId: session.state.sessionId,
|
|
4982
5185
|
reason: result.reason,
|
|
@@ -5059,19 +5262,29 @@ var Engine = class _Engine {
|
|
|
5059
5262
|
* Returns token stats before/after.
|
|
5060
5263
|
*/
|
|
5061
5264
|
forceCompact() {
|
|
5062
|
-
|
|
5265
|
+
const sessionId = this.lastSessionId;
|
|
5266
|
+
if (!this.lastContextManager || !sessionId) {
|
|
5063
5267
|
return { before: 0, after: 0, strategy: "none (no active session)" };
|
|
5064
5268
|
}
|
|
5065
5269
|
const { estimateTokens: estimateTokens2 } = (init_compaction(), __toCommonJS(compaction_exports));
|
|
5066
|
-
const
|
|
5067
|
-
|
|
5068
|
-
const
|
|
5270
|
+
const sourceMessages = this.compactedMessagesBySession.get(sessionId) ?? this.sessionManager.resume(sessionId).transcript.toMessages();
|
|
5271
|
+
const before = estimateTokens2(sourceMessages);
|
|
5272
|
+
const compacted = this.lastContextManager.manage(sourceMessages);
|
|
5273
|
+
const after = estimateTokens2(compacted);
|
|
5274
|
+
this.compactedMessagesBySession.set(sessionId, compacted);
|
|
5275
|
+
this.lastMessages = compacted;
|
|
5069
5276
|
return {
|
|
5070
5277
|
before,
|
|
5071
5278
|
after,
|
|
5072
5279
|
strategy: before === after ? "no compaction needed" : "compacted"
|
|
5073
5280
|
};
|
|
5074
5281
|
}
|
|
5282
|
+
stripUserContextMessage(messages, userContextMsg) {
|
|
5283
|
+
if (!userContextMsg || messages[0] !== userContextMsg) {
|
|
5284
|
+
return [...messages];
|
|
5285
|
+
}
|
|
5286
|
+
return messages.slice(1);
|
|
5287
|
+
}
|
|
5075
5288
|
/**
|
|
5076
5289
|
* Update a config setting at runtime.
|
|
5077
5290
|
*/
|
|
@@ -5090,6 +5303,8 @@ var Engine = class _Engine {
|
|
|
5090
5303
|
/** Track last context manager and messages for /compact support. */
|
|
5091
5304
|
lastContextManager;
|
|
5092
5305
|
lastMessages;
|
|
5306
|
+
lastSessionId;
|
|
5307
|
+
compactedMessagesBySession = /* @__PURE__ */ new Map();
|
|
5093
5308
|
};
|
|
5094
5309
|
|
|
5095
5310
|
// src/run/types.ts
|
|
@@ -5921,6 +6136,7 @@ var EngineRunner = class {
|
|
|
5921
6136
|
appendSystemPrompt: this.config.appendSystemPrompt,
|
|
5922
6137
|
sessionStorageDir: this.config.sessionStorageDir,
|
|
5923
6138
|
mcpServers: this.config.mcpServers,
|
|
6139
|
+
hooks: this.config.hooks,
|
|
5924
6140
|
approvalBackend,
|
|
5925
6141
|
askUser: askUserFn,
|
|
5926
6142
|
...context.engineConfigOverrides
|
|
@@ -5961,6 +6177,8 @@ var RunManager = class {
|
|
|
5961
6177
|
lock;
|
|
5962
6178
|
heartbeat;
|
|
5963
6179
|
evaluator;
|
|
6180
|
+
defaultTags;
|
|
6181
|
+
defaultMetadata;
|
|
5964
6182
|
subscribers = /* @__PURE__ */ new Map();
|
|
5965
6183
|
abortControllers = /* @__PURE__ */ new Map();
|
|
5966
6184
|
/** Active execution handles — used to resolve pending approvals/input while Engine is suspended */
|
|
@@ -5978,6 +6196,8 @@ var RunManager = class {
|
|
|
5978
6196
|
intervalMs: config.heartbeatIntervalMs
|
|
5979
6197
|
});
|
|
5980
6198
|
this.evaluator = config.evaluator ?? new NoopEvaluator();
|
|
6199
|
+
this.defaultTags = config.defaultTags ?? [];
|
|
6200
|
+
this.defaultMetadata = config.defaultMetadata ?? {};
|
|
5981
6201
|
this.queue.setExecutor((runId) => this.executeRun(runId));
|
|
5982
6202
|
}
|
|
5983
6203
|
// ─── Submit ────────────────────────────────────────────────────
|
|
@@ -6002,8 +6222,8 @@ var RunManager = class {
|
|
|
6002
6222
|
latestApprovalId: null,
|
|
6003
6223
|
summary: null,
|
|
6004
6224
|
error: null,
|
|
6005
|
-
tags: input.tags ?? [],
|
|
6006
|
-
metadata: input.metadata ?? {}
|
|
6225
|
+
tags: [.../* @__PURE__ */ new Set([...this.defaultTags, ...input.tags ?? []])],
|
|
6226
|
+
metadata: { ...this.defaultMetadata, ...input.metadata ?? {} }
|
|
6007
6227
|
};
|
|
6008
6228
|
await this.store.create(snapshot);
|
|
6009
6229
|
await this.emitRunEvent(runId, "run_created", { objective: input.objective });
|
|
@@ -6471,6 +6691,7 @@ export {
|
|
|
6471
6691
|
ToolExecutor,
|
|
6472
6692
|
HeadlessApprovalBackend,
|
|
6473
6693
|
AutoApprovalBackend,
|
|
6694
|
+
getInteractiveApprovalBackend,
|
|
6474
6695
|
setInteractiveApprovalFn,
|
|
6475
6696
|
setRuntimeBypass,
|
|
6476
6697
|
PermissionClassifier,
|