@cryer/star-cli 0.3.3 → 0.3.4
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 +3 -1
- package/README.zh-CN.md +3 -1
- package/dist/{chunk-3L6QKJM4.js → chunk-K3R42LOX.js} +207 -69
- package/dist/chunk-K3R42LOX.js.map +1 -0
- package/dist/{loop-TQ7GFTTE.js → loop-SDZVEQND.js} +2 -2
- package/dist/main.js +28 -126
- package/dist/main.js.map +1 -1
- package/package.json +25 -16
- package/dist/chunk-3L6QKJM4.js.map +0 -1
- /package/dist/{loop-TQ7GFTTE.js.map → loop-SDZVEQND.js.map} +0 -0
package/README.md
CHANGED
|
@@ -52,6 +52,8 @@ Config file: `~/.star-cli/config.toml` (project-level override: `.star/config.to
|
|
|
52
52
|
defaultModel = "gpt"
|
|
53
53
|
permissionMode = "ask" # ask | auto | readonly | yolo | plan
|
|
54
54
|
contextMaxTokens = 100000
|
|
55
|
+
# compactThresholdTokens = 80000 # optional auto-compaction trigger in tokens; defaults to the
|
|
56
|
+
# context window. /compact always compacts, regardless of this.
|
|
55
57
|
contextCompaction = "summary" # summary | truncate — how over-budget history is compacted
|
|
56
58
|
# Seconds with no stream output before a stalled response is ended gracefully
|
|
57
59
|
# (some relays never close the stream). The first token gets a fixed 120s allowance.
|
|
@@ -148,7 +150,7 @@ star --clear-sessions all delete every stored session
|
|
|
148
150
|
| `/connect` | interactive provider onboarding wizard: pick a preset or custom endpoint, paste the API key (masked), name a model — appends `[[providers]]`/`[[models]]` to the config, stores the key in `~/.star-cli/.env` (never in `config.toml`), and can set the new model as default + switch to it immediately |
|
|
149
151
|
| `/plan` | toggle plan mode: read-only research, then approve the generated plan before it executes (session-only) |
|
|
150
152
|
| `/memory [add <text>]` | show the long-term memory file (`~/.star-cli/MEMORY.md`, injected into every session's system prompt), or append a line to it — see [Long-term memory](#long-term-memory) |
|
|
151
|
-
| `/compact` | compact conversation history
|
|
153
|
+
| `/compact` | compact conversation history now — always runs, even below the auto-compact threshold |
|
|
152
154
|
| `/export [path]` | export the current session to a Markdown file |
|
|
153
155
|
| `/undo` | undo the last conversation turn: revert its file changes (write_file/edit_file) and retract its messages — earlier turns are never touched (previews the message count and per-file revert diffs, then asks for confirmation) |
|
|
154
156
|
| `/rewind [n]` | list file-change checkpoints, or rewind to just before checkpoint `n`: restore every file changed since then and retract the matching conversation messages (asks for confirmation first) |
|
package/README.zh-CN.md
CHANGED
|
@@ -52,6 +52,8 @@ star -p "hi" # non-interactive print mode
|
|
|
52
52
|
defaultModel = "gpt"
|
|
53
53
|
permissionMode = "ask" # ask | auto | readonly | yolo | plan
|
|
54
54
|
contextMaxTokens = 100000
|
|
55
|
+
# compactThresholdTokens = 80000 # optional auto-compaction trigger in tokens; defaults to the
|
|
56
|
+
# context window. /compact always compacts, regardless of this.
|
|
55
57
|
contextCompaction = "summary" # summary | truncate — how over-budget history is compacted
|
|
56
58
|
# Seconds with no stream output before a stalled response is ended gracefully
|
|
57
59
|
# (some relays never close the stream). The first token gets a fixed 120s allowance.
|
|
@@ -148,7 +150,7 @@ star --clear-sessions all delete every stored session
|
|
|
148
150
|
| `/connect` | 交互式服务商接入向导:选择预设或自定义端点,粘贴 API 密钥(掩码显示),命名模型——把 `[[providers]]`/`[[models]]` 追加到配置,密钥存入 `~/.star-cli/.env`(绝不写入 `config.toml`),还可将新模型设为默认并立即切换 |
|
|
149
151
|
| `/plan` | 切换计划模式:先只读调研,再批准生成的计划后才执行(仅会话内有效) |
|
|
150
152
|
| `/memory [add <text>]` | 查看长期记忆文件(`~/.star-cli/MEMORY.md`,注入每个会话的系统提示词),或向其中追加一行——见[长期记忆](#长期记忆) |
|
|
151
|
-
| `/compact` |
|
|
153
|
+
| `/compact` | 立即压缩对话历史——低于自动压缩阈值时也会执行 |
|
|
152
154
|
| `/export [path]` | 将当前会话导出为 Markdown 文件 |
|
|
153
155
|
| `/undo` | 撤销上一轮对话:还原其文件改动(write_file/edit_file)并撤回其消息——更早的回合不受影响(先预览消息数量与逐文件还原 diff,再要求确认) |
|
|
154
156
|
| `/rewind [n]` | 列出文件改动检查点,或回滚到检查点 `n` 之前:还原此后改动的所有文件并撤回对应的对话消息(先要求确认) |
|
|
@@ -12,6 +12,123 @@ import {
|
|
|
12
12
|
// src/agent/loop.ts
|
|
13
13
|
import { tool as aiTool, generateText as generateText3 } from "ai";
|
|
14
14
|
|
|
15
|
+
// src/config/schema.ts
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var ProviderConfigSchema = z.object({
|
|
18
|
+
name: z.string(),
|
|
19
|
+
// openai-responses talks to the Responses API (/responses) instead of chat
|
|
20
|
+
// completions — the wire format codex uses, and the native API of
|
|
21
|
+
// reasoning-model relays that otherwise hide reasoning behind empty chat
|
|
22
|
+
// completions. streamChat sends strictSchemas:false + store:false with it.
|
|
23
|
+
protocol: z.enum(["openai-compatible", "anthropic", "openai-responses"]).default("openai-compatible"),
|
|
24
|
+
baseURL: z.string(),
|
|
25
|
+
apiKeyEnv: z.string().optional(),
|
|
26
|
+
apiKey: z.string().optional(),
|
|
27
|
+
headers: z.record(z.string()).optional()
|
|
28
|
+
});
|
|
29
|
+
var ModelConfigSchema = z.object({
|
|
30
|
+
name: z.string(),
|
|
31
|
+
provider: z.string(),
|
|
32
|
+
model: z.string(),
|
|
33
|
+
maxTokens: z.number().int().positive().optional(),
|
|
34
|
+
// Per-model context window; overrides the top-level contextMaxTokens for
|
|
35
|
+
// compaction and the status bar when set.
|
|
36
|
+
contextMaxTokens: z.number().int().positive().optional(),
|
|
37
|
+
// Cost estimation needs BOTH prices (USD per 1M tokens); with only one set
|
|
38
|
+
// the model is treated as unpriced.
|
|
39
|
+
promptPrice: z.number().nonnegative().optional(),
|
|
40
|
+
completionPrice: z.number().nonnegative().optional(),
|
|
41
|
+
// Optional price (USD per 1M tokens) for cache-read prompt tokens. When
|
|
42
|
+
// unset, OpenAI-style cached tokens bill at promptPrice and Anthropic-style
|
|
43
|
+
// cache reads are not billed — set this for accurate cache pricing.
|
|
44
|
+
cacheReadPrice: z.number().nonnegative().optional()
|
|
45
|
+
});
|
|
46
|
+
var PermissionsConfigSchema = z.object({
|
|
47
|
+
allow: z.array(z.string()).default([]),
|
|
48
|
+
deny: z.array(z.string()).default([])
|
|
49
|
+
});
|
|
50
|
+
var HookConfigSchema = z.object({
|
|
51
|
+
event: z.enum(["PreToolUse", "PostToolUse", "Stop"]),
|
|
52
|
+
matcher: z.string().refine(
|
|
53
|
+
(pattern) => {
|
|
54
|
+
try {
|
|
55
|
+
new RegExp(pattern);
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{ message: "invalid regular expression" }
|
|
62
|
+
).optional(),
|
|
63
|
+
command: z.string().min(1),
|
|
64
|
+
timeoutSec: z.number().positive().default(30)
|
|
65
|
+
});
|
|
66
|
+
var ConfigSchema = z.object({
|
|
67
|
+
defaultModel: z.string().default(""),
|
|
68
|
+
permissionMode: z.enum(["auto", "ask", "readonly", "yolo", "plan"]).default("ask"),
|
|
69
|
+
providers: z.array(ProviderConfigSchema).default([]),
|
|
70
|
+
models: z.array(ModelConfigSchema).default([]),
|
|
71
|
+
maxSteps: z.number().int().positive().default(50),
|
|
72
|
+
contextMaxTokens: z.number().int().positive().default(1e5),
|
|
73
|
+
// Token threshold at which the agent loop auto-compacts the history.
|
|
74
|
+
// Defaults to the effective context window (compact only when full); set a
|
|
75
|
+
// lower value to compact earlier and keep headroom. Manual /compact ignores
|
|
76
|
+
// this — it always compacts.
|
|
77
|
+
compactThresholdTokens: z.number().int().positive().optional(),
|
|
78
|
+
// Seconds without any stream part before a stalled response is ended
|
|
79
|
+
// gracefully (some relays never send the terminal chunks). Applies once
|
|
80
|
+
// streaming has started; the first part gets a longer, fixed allowance.
|
|
81
|
+
streamIdleTimeoutSec: z.number().positive().default(20),
|
|
82
|
+
// Seconds to wait for the very first stream part before giving up; slow
|
|
83
|
+
// thinking-model relays can buffer for minutes before sending anything.
|
|
84
|
+
streamFirstChunkTimeoutSec: z.number().positive().default(300),
|
|
85
|
+
// Extra attempts per model request when a stream fails transiently
|
|
86
|
+
// (network error, 429/5xx, idle watchdog cutoff) or comes back empty, so a
|
|
87
|
+
// relay hiccup does not silently end a half-finished turn. 0 disables.
|
|
88
|
+
// The wait between attempts honors Retry-After headers and otherwise backs
|
|
89
|
+
// off exponentially with jitter (llm/retry.ts); retries after a
|
|
90
|
+
// timeout/empty failure also scale both stream timeouts up (attempt number
|
|
91
|
+
// ×, capped at 3x) since the relay is likely overloaded. Default 5 matches
|
|
92
|
+
// opencode/Codex retry budgets and rides out flaky relays (中转站) better
|
|
93
|
+
// than a minimal budget.
|
|
94
|
+
streamMaxRetries: z.number().int().min(0).default(5),
|
|
95
|
+
// How many consecutive unproductive replies may be nudged before the turn
|
|
96
|
+
// is handed back: a text-only reply that announces pending work ("我将…",
|
|
97
|
+
// "I will…") or answers a nudge with more words, or an empty reply that
|
|
98
|
+
// survived the stream retry budget (steered with a nudge — a changed
|
|
99
|
+
// request — since identical resends already failed). Tool calls reset the
|
|
100
|
+
// count, so a productive task keeps going; exhaustion surfaces a notice
|
|
101
|
+
// instead of stopping silently. Never fires in plan mode. 0 disables.
|
|
102
|
+
maxAutoContinues: z.number().int().min(0).default(2),
|
|
103
|
+
contextCompaction: z.enum(["summary", "truncate"]).default("summary"),
|
|
104
|
+
// Optional per-session dollar budget; when set, the REPL stops the session
|
|
105
|
+
// once the estimated cost for the session reaches this amount.
|
|
106
|
+
sessionBudgetUsd: z.number().positive().optional(),
|
|
107
|
+
// Terminal bell (REPL only): ring when a turn takes longer than
|
|
108
|
+
// notifyBellThresholdSec, and when a background task finishes.
|
|
109
|
+
// STAR_NO_NOTIFY=1 disables without touching the config.
|
|
110
|
+
notifyBell: z.boolean().default(true),
|
|
111
|
+
notifyBellThresholdSec: z.number().positive().default(10),
|
|
112
|
+
permissions: PermissionsConfigSchema.default({ allow: [], deny: [] }),
|
|
113
|
+
hooks: z.array(HookConfigSchema).default([]),
|
|
114
|
+
// Consecutive identical tool calls (same tool + same arguments) allowed
|
|
115
|
+
// before the loop refuses to execute the repeat and tells the model to
|
|
116
|
+
// change approach — a guard against agents stuck retrying a failing call
|
|
117
|
+
// (opencode's doom_loop). 0 disables.
|
|
118
|
+
doomLoopThreshold: z.number().int().min(0).default(3),
|
|
119
|
+
// Track the whole working tree with an internal git repo (outside the
|
|
120
|
+
// user's .git) at every turn boundary, so /undo and /redo can restore
|
|
121
|
+
// changes made by ANY tool (bash included), not just write_file/edit_file.
|
|
122
|
+
// Falls back silently to per-file snapshots when git is unavailable.
|
|
123
|
+
gitSnapshots: z.boolean().default(true)
|
|
124
|
+
});
|
|
125
|
+
function contextWindowTokens(config, modelName) {
|
|
126
|
+
return config.models.find((m) => m.name === modelName)?.contextMaxTokens ?? config.contextMaxTokens;
|
|
127
|
+
}
|
|
128
|
+
function resolveCompactThreshold(config, windowTokens) {
|
|
129
|
+
return Math.min(config.compactThresholdTokens ?? windowTokens, windowTokens);
|
|
130
|
+
}
|
|
131
|
+
|
|
15
132
|
// src/context/compaction.ts
|
|
16
133
|
import { generateText } from "ai";
|
|
17
134
|
|
|
@@ -106,8 +223,9 @@ ${transcript}`,
|
|
|
106
223
|
});
|
|
107
224
|
return text.trim();
|
|
108
225
|
}
|
|
109
|
-
function compactMessages(messages, maxTokens) {
|
|
110
|
-
|
|
226
|
+
function compactMessages(messages, maxTokens, opts = {}) {
|
|
227
|
+
const limit = opts.force ? 0 : maxTokens;
|
|
228
|
+
if (estimateTokens(messages) <= limit) {
|
|
111
229
|
return { messages, compacted: false, droppedCount: 0 };
|
|
112
230
|
}
|
|
113
231
|
const first = messages[0];
|
|
@@ -133,7 +251,7 @@ function compactMessages(messages, maxTokens) {
|
|
|
133
251
|
droppedCount += turn.length;
|
|
134
252
|
turnIndex += 1;
|
|
135
253
|
const candidate = [...head, placeholderMessage(droppedCount), ...rest.slice(droppedCount)];
|
|
136
|
-
if (estimateTokens(candidate) <=
|
|
254
|
+
if (estimateTokens(candidate) <= limit) {
|
|
137
255
|
break;
|
|
138
256
|
}
|
|
139
257
|
}
|
|
@@ -1091,7 +1209,7 @@ async function rewindToSnapshot(id, includeSubagent = false) {
|
|
|
1091
1209
|
import { spawn as spawn3 } from "child_process";
|
|
1092
1210
|
import { existsSync as existsSync2 } from "fs";
|
|
1093
1211
|
import path4 from "path";
|
|
1094
|
-
import { z } from "zod";
|
|
1212
|
+
import { z as z2 } from "zod";
|
|
1095
1213
|
|
|
1096
1214
|
// src/tasks/manager.ts
|
|
1097
1215
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -1270,11 +1388,11 @@ function resolveShell() {
|
|
|
1270
1388
|
}
|
|
1271
1389
|
return cachedSpec;
|
|
1272
1390
|
}
|
|
1273
|
-
var schema =
|
|
1274
|
-
command:
|
|
1275
|
-
timeout:
|
|
1276
|
-
description:
|
|
1277
|
-
run_in_background:
|
|
1391
|
+
var schema = z2.object({
|
|
1392
|
+
command: z2.string().describe("Shell command to execute"),
|
|
1393
|
+
timeout: z2.number().positive().max(MAX_TIMEOUT).optional().describe("Timeout in seconds (default 120, max 600)"),
|
|
1394
|
+
description: z2.string().optional().describe("Short description of what the command does"),
|
|
1395
|
+
run_in_background: z2.boolean().optional().describe(
|
|
1278
1396
|
"Run the command in the background and return a task id immediately (default false). Use task_output/task_kill to inspect or stop it."
|
|
1279
1397
|
)
|
|
1280
1398
|
});
|
|
@@ -1408,12 +1526,12 @@ Command timed out after ${timeoutSeconds}s`,
|
|
|
1408
1526
|
// src/tools/fs/edit.ts
|
|
1409
1527
|
import { readFile as readFile2, stat as stat2, writeFile as writeFile2 } from "fs/promises";
|
|
1410
1528
|
import path5 from "path";
|
|
1411
|
-
import { z as
|
|
1412
|
-
var schema2 =
|
|
1413
|
-
path:
|
|
1414
|
-
old_string:
|
|
1415
|
-
new_string:
|
|
1416
|
-
replace_all:
|
|
1529
|
+
import { z as z3 } from "zod";
|
|
1530
|
+
var schema2 = z3.object({
|
|
1531
|
+
path: z3.string().describe("File path, absolute or relative to the working directory"),
|
|
1532
|
+
old_string: z3.string().min(1).describe("Exact text to replace"),
|
|
1533
|
+
new_string: z3.string().describe("Replacement text"),
|
|
1534
|
+
replace_all: z3.boolean().optional().describe("Replace every occurrence instead of requiring a unique match")
|
|
1417
1535
|
});
|
|
1418
1536
|
var editFileTool = {
|
|
1419
1537
|
name: "edit_file",
|
|
@@ -1477,7 +1595,7 @@ var editFileTool = {
|
|
|
1477
1595
|
|
|
1478
1596
|
// src/tools/fs/glob.ts
|
|
1479
1597
|
import path7 from "path";
|
|
1480
|
-
import { z as
|
|
1598
|
+
import { z as z4 } from "zod";
|
|
1481
1599
|
|
|
1482
1600
|
// src/tools/fs/util.ts
|
|
1483
1601
|
import { readFileSync, statSync } from "fs";
|
|
@@ -1708,11 +1826,11 @@ function literalDirPrefix(pattern) {
|
|
|
1708
1826
|
}
|
|
1709
1827
|
return out;
|
|
1710
1828
|
}
|
|
1711
|
-
var schema3 =
|
|
1712
|
-
pattern:
|
|
1829
|
+
var schema3 = z4.object({
|
|
1830
|
+
pattern: z4.string().describe(
|
|
1713
1831
|
"Glob pattern, e.g. 'src/**/*.ts'. Supports **, *, ?, {a,b} braces and [abc] character classes. A bare pattern like '*.ts' matches basenames recursively."
|
|
1714
1832
|
),
|
|
1715
|
-
path:
|
|
1833
|
+
path: z4.string().optional().describe("Directory to search, defaults to the working directory")
|
|
1716
1834
|
});
|
|
1717
1835
|
var globTool = {
|
|
1718
1836
|
name: "glob",
|
|
@@ -1736,7 +1854,7 @@ var globTool = {
|
|
|
1736
1854
|
// src/tools/fs/grep.ts
|
|
1737
1855
|
import { readFile as readFile3, stat as stat4 } from "fs/promises";
|
|
1738
1856
|
import path8 from "path";
|
|
1739
|
-
import { z as
|
|
1857
|
+
import { z as z5 } from "zod";
|
|
1740
1858
|
var MAX_MATCHES = 250;
|
|
1741
1859
|
var MAX_LINE_CHARS = 500;
|
|
1742
1860
|
var MAX_PATTERN_CHARS = 200;
|
|
@@ -1744,11 +1862,11 @@ var MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
|
1744
1862
|
function isTooComplexPattern(pattern) {
|
|
1745
1863
|
return pattern.length > MAX_PATTERN_CHARS || /\)[+*]/.test(pattern) || /\)\{\d+(,\d*)?\}/.test(pattern);
|
|
1746
1864
|
}
|
|
1747
|
-
var schema4 =
|
|
1748
|
-
pattern:
|
|
1749
|
-
path:
|
|
1750
|
-
glob:
|
|
1751
|
-
ignoreCase:
|
|
1865
|
+
var schema4 = z5.object({
|
|
1866
|
+
pattern: z5.string().describe("Regular expression to search for"),
|
|
1867
|
+
path: z5.string().optional().describe("File or directory to search, defaults to the working directory"),
|
|
1868
|
+
glob: z5.string().optional().describe("Glob pattern to filter which files are searched, e.g. '*.ts'"),
|
|
1869
|
+
ignoreCase: z5.boolean().optional().describe("Case-insensitive matching")
|
|
1752
1870
|
});
|
|
1753
1871
|
var grepTool = {
|
|
1754
1872
|
name: "grep",
|
|
@@ -1847,7 +1965,7 @@ var grepTool = {
|
|
|
1847
1965
|
// src/tools/fs/read.ts
|
|
1848
1966
|
import { open, stat as stat5 } from "fs/promises";
|
|
1849
1967
|
import path9 from "path";
|
|
1850
|
-
import { z as
|
|
1968
|
+
import { z as z6 } from "zod";
|
|
1851
1969
|
var DEFAULT_LIMIT = 2e3;
|
|
1852
1970
|
var MAX_CHARS = 100 * 1024;
|
|
1853
1971
|
var MAX_READ_BYTES = 1024 * 1024;
|
|
@@ -1873,10 +1991,10 @@ function decodeTextBuffer(buf) {
|
|
|
1873
1991
|
}
|
|
1874
1992
|
return { text: buf.toString("utf8") };
|
|
1875
1993
|
}
|
|
1876
|
-
var schema5 =
|
|
1877
|
-
path:
|
|
1878
|
-
offset:
|
|
1879
|
-
limit:
|
|
1994
|
+
var schema5 = z6.object({
|
|
1995
|
+
path: z6.string().describe("File path, absolute or relative to the working directory"),
|
|
1996
|
+
offset: z6.number().int().positive().optional().describe("1-based line number to start reading from"),
|
|
1997
|
+
limit: z6.number().int().positive().optional().describe("Maximum number of lines to read (default 2000)")
|
|
1880
1998
|
});
|
|
1881
1999
|
var readFileTool = {
|
|
1882
2000
|
name: "read_file",
|
|
@@ -1949,10 +2067,10 @@ var readFileTool = {
|
|
|
1949
2067
|
// src/tools/fs/write.ts
|
|
1950
2068
|
import { mkdir as mkdir3, writeFile as writeFile3 } from "fs/promises";
|
|
1951
2069
|
import path10 from "path";
|
|
1952
|
-
import { z as
|
|
1953
|
-
var schema6 =
|
|
1954
|
-
path:
|
|
1955
|
-
content:
|
|
2070
|
+
import { z as z7 } from "zod";
|
|
2071
|
+
var schema6 = z7.object({
|
|
2072
|
+
path: z7.string().describe("File path, absolute or relative to the working directory"),
|
|
2073
|
+
content: z7.string().describe("Full content to write to the file")
|
|
1956
2074
|
});
|
|
1957
2075
|
var writeFileTool = {
|
|
1958
2076
|
name: "write_file",
|
|
@@ -1974,7 +2092,7 @@ var writeFileTool = {
|
|
|
1974
2092
|
};
|
|
1975
2093
|
|
|
1976
2094
|
// src/tools/tasks.ts
|
|
1977
|
-
import { z as
|
|
2095
|
+
import { z as z8 } from "zod";
|
|
1978
2096
|
|
|
1979
2097
|
// src/agent/agent-tasks.ts
|
|
1980
2098
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
@@ -2086,6 +2204,20 @@ var AgentTaskManager = class extends EventEmitter2 {
|
|
|
2086
2204
|
}
|
|
2087
2205
|
};
|
|
2088
2206
|
var defaultAgentTasks = new AgentTaskManager();
|
|
2207
|
+
function formatAgentTaskUpdate(task) {
|
|
2208
|
+
const label = task.description ? `: ${task.description}` : "";
|
|
2209
|
+
if (task.status === "running") {
|
|
2210
|
+
return `Background subagent ${task.id} started${label}`;
|
|
2211
|
+
}
|
|
2212
|
+
let text = `Background subagent ${task.id} ${task.status}${label}`;
|
|
2213
|
+
if (task.status === "failed") {
|
|
2214
|
+
const reason = (task.result.split("\n")[0] ?? "").replace(/^Subagent failed:\s*/, "");
|
|
2215
|
+
if (reason) {
|
|
2216
|
+
text += ` \u2014 ${reason.length > 140 ? `${reason.slice(0, 140)}\u2026` : reason}`;
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
return text;
|
|
2220
|
+
}
|
|
2089
2221
|
|
|
2090
2222
|
// src/tasks/format.ts
|
|
2091
2223
|
function formatDuration(startedAt, endedAt) {
|
|
@@ -2131,8 +2263,8 @@ function formatTaskFinished(task) {
|
|
|
2131
2263
|
}
|
|
2132
2264
|
|
|
2133
2265
|
// src/tools/tasks.ts
|
|
2134
|
-
var idSchema =
|
|
2135
|
-
id:
|
|
2266
|
+
var idSchema = z8.object({
|
|
2267
|
+
id: z8.string().describe("Task id, e.g. task-1 or agent-1")
|
|
2136
2268
|
});
|
|
2137
2269
|
function truncateOutput(s) {
|
|
2138
2270
|
if (s.length <= MAX_OUTPUT) return s;
|
|
@@ -2149,7 +2281,7 @@ var taskListTool = {
|
|
|
2149
2281
|
name: "task_list",
|
|
2150
2282
|
description: "List background tasks (shell commands and subagents) with status and runtime.",
|
|
2151
2283
|
permission: "read",
|
|
2152
|
-
parameters:
|
|
2284
|
+
parameters: z8.object({}),
|
|
2153
2285
|
execute() {
|
|
2154
2286
|
const shell = formatTaskList(defaultTaskManager.list());
|
|
2155
2287
|
const agents = defaultAgentTasks.list();
|
|
@@ -2210,16 +2342,16 @@ var taskKillTool = {
|
|
|
2210
2342
|
// src/tools/todo.ts
|
|
2211
2343
|
import { mkdir as mkdir4, readFile as readFile4, rename, writeFile as writeFile4 } from "fs/promises";
|
|
2212
2344
|
import path11 from "path";
|
|
2213
|
-
import { z as
|
|
2214
|
-
var todoItemSchema =
|
|
2215
|
-
id:
|
|
2216
|
-
title:
|
|
2217
|
-
status:
|
|
2345
|
+
import { z as z9 } from "zod";
|
|
2346
|
+
var todoItemSchema = z9.object({
|
|
2347
|
+
id: z9.number().int(),
|
|
2348
|
+
title: z9.string(),
|
|
2349
|
+
status: z9.enum(["pending", "in_progress", "done"])
|
|
2218
2350
|
});
|
|
2219
|
-
var writeSchema =
|
|
2220
|
-
todos:
|
|
2351
|
+
var writeSchema = z9.object({
|
|
2352
|
+
todos: z9.array(todoItemSchema).describe("The full todo list, replacing the current one")
|
|
2221
2353
|
});
|
|
2222
|
-
var readSchema =
|
|
2354
|
+
var readSchema = z9.object({});
|
|
2223
2355
|
function fileFor(cwd) {
|
|
2224
2356
|
return path11.join(cwd, ".star", "todos.json");
|
|
2225
2357
|
}
|
|
@@ -2335,13 +2467,13 @@ function createTodoTools(store = defaultStore) {
|
|
|
2335
2467
|
}
|
|
2336
2468
|
|
|
2337
2469
|
// src/tools/web/fetch.ts
|
|
2338
|
-
import { z as
|
|
2470
|
+
import { z as z10 } from "zod";
|
|
2339
2471
|
var DEFAULT_MAX_CHARS = 2e4;
|
|
2340
2472
|
var MAX_BYTES = 2 * 1024 * 1024;
|
|
2341
2473
|
var TIMEOUT_MS = 3e4;
|
|
2342
|
-
var schema7 =
|
|
2343
|
-
url:
|
|
2344
|
-
maxChars:
|
|
2474
|
+
var schema7 = z10.object({
|
|
2475
|
+
url: z10.string().describe("The http(s) URL to fetch"),
|
|
2476
|
+
maxChars: z10.number().int().positive().optional().describe("Maximum characters of text to return (default 20000)")
|
|
2345
2477
|
});
|
|
2346
2478
|
function safeCodePoint(n, raw) {
|
|
2347
2479
|
if (!Number.isInteger(n) || n < 0 || n > 1114111) {
|
|
@@ -2475,15 +2607,15 @@ var webFetchTool = {
|
|
|
2475
2607
|
};
|
|
2476
2608
|
|
|
2477
2609
|
// src/tools/web/search.ts
|
|
2478
|
-
import { z as
|
|
2610
|
+
import { z as z11 } from "zod";
|
|
2479
2611
|
var TIMEOUT_MS2 = 15e3;
|
|
2480
2612
|
var DEFAULT_MAX_RESULTS = 5;
|
|
2481
2613
|
var MAX_RESULTS_LIMIT = 10;
|
|
2482
2614
|
var SEARCH_URL = "https://html.duckduckgo.com/html/?q=";
|
|
2483
2615
|
var USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
|
|
2484
|
-
var schema8 =
|
|
2485
|
-
query:
|
|
2486
|
-
maxResults:
|
|
2616
|
+
var schema8 = z11.object({
|
|
2617
|
+
query: z11.string().min(1).describe("The search query"),
|
|
2618
|
+
maxResults: z11.number().int().min(1).max(MAX_RESULTS_LIMIT).optional().describe(
|
|
2487
2619
|
`Maximum number of results to return (default ${DEFAULT_MAX_RESULTS}, max ${MAX_RESULTS_LIMIT})`
|
|
2488
2620
|
)
|
|
2489
2621
|
});
|
|
@@ -2664,7 +2796,7 @@ ${body}`;
|
|
|
2664
2796
|
// src/agent/skills.ts
|
|
2665
2797
|
import { readFileSync as readFileSync3, readdirSync, statSync as statSync3 } from "fs";
|
|
2666
2798
|
import path13 from "path";
|
|
2667
|
-
import { z as
|
|
2799
|
+
import { z as z12 } from "zod";
|
|
2668
2800
|
var SKILL_FILE = "SKILL.md";
|
|
2669
2801
|
var SKILL_MAX_CHARS = 32 * 1024;
|
|
2670
2802
|
var NAME_PATTERN = /^[a-z0-9-]+$/;
|
|
@@ -2749,8 +2881,8 @@ function createSkillTool(deps) {
|
|
|
2749
2881
|
name: "skill",
|
|
2750
2882
|
description: "Load the full instructions of a skill by name. Available skills are listed in the system prompt when present; call this before starting work that matches one of them.",
|
|
2751
2883
|
permission: "read",
|
|
2752
|
-
parameters:
|
|
2753
|
-
name:
|
|
2884
|
+
parameters: z12.object({
|
|
2885
|
+
name: z12.string().describe("Name of the skill to load, as listed in the system prompt.")
|
|
2754
2886
|
}),
|
|
2755
2887
|
async execute(args) {
|
|
2756
2888
|
const skills = discoverSkills(deps.cwd, deps.home);
|
|
@@ -2780,7 +2912,7 @@ function createSkillTool(deps) {
|
|
|
2780
2912
|
}
|
|
2781
2913
|
|
|
2782
2914
|
// src/agent/subagent.ts
|
|
2783
|
-
import { z as
|
|
2915
|
+
import { z as z13 } from "zod";
|
|
2784
2916
|
|
|
2785
2917
|
// src/tools/index.ts
|
|
2786
2918
|
function createDefaultRegistry(todoStore) {
|
|
@@ -2792,7 +2924,7 @@ var MAX_SUBAGENT_DEPTH = 1;
|
|
|
2792
2924
|
var MAX_REPORT_CHARS = 2e4;
|
|
2793
2925
|
var SUBAGENT_PROMPT = "You are a subagent spawned to handle a focused subtask on behalf of the main agent. Work autonomously with the tools available to you, then finish with a concise report of what you found or did. Your final text is returned to the main agent as the tool result \u2014 make it self-contained.";
|
|
2794
2926
|
async function runSubagent(deps, args, cwd, signal) {
|
|
2795
|
-
const { AgentLoop: AgentLoop2 } = await import("./loop-
|
|
2927
|
+
const { AgentLoop: AgentLoop2 } = await import("./loop-SDZVEQND.js");
|
|
2796
2928
|
const child = new AgentLoop2({
|
|
2797
2929
|
model: deps.model,
|
|
2798
2930
|
registry: createDefaultRegistry(),
|
|
@@ -2850,10 +2982,10 @@ function createSubagentTool(deps) {
|
|
|
2850
2982
|
name: "subagent",
|
|
2851
2983
|
description: "Spawn a subagent with its own agent loop to handle a focused subtask (research, exploration, or an isolated change). The subagent has the same tools but cannot spawn further subagents. Returns the subagent's final report. Prefer this for self-contained work that would otherwise clutter the main conversation. Set run_in_background to start it without blocking: independent subtasks then run concurrently and their reports are delivered to you automatically when they finish (also inspectable via task_list/task_output/task_kill).",
|
|
2852
2984
|
permission: "exec",
|
|
2853
|
-
parameters:
|
|
2854
|
-
prompt:
|
|
2855
|
-
description:
|
|
2856
|
-
run_in_background:
|
|
2985
|
+
parameters: z13.object({
|
|
2986
|
+
prompt: z13.string().describe("Complete, self-contained instructions for the subagent."),
|
|
2987
|
+
description: z13.string().optional().describe("Short label for the subtask, shown in the result header."),
|
|
2988
|
+
run_in_background: z13.boolean().optional().describe(
|
|
2857
2989
|
"Run concurrently in the background and return a task id immediately (default false). Use for independent subtasks that can run in parallel."
|
|
2858
2990
|
)
|
|
2859
2991
|
}),
|
|
@@ -2881,7 +3013,7 @@ function createSubagentTool(deps) {
|
|
|
2881
3013
|
// src/agent/user-memory.ts
|
|
2882
3014
|
import { appendFileSync, mkdirSync, readFileSync as readFileSync4, statSync as statSync4 } from "fs";
|
|
2883
3015
|
import path14 from "path";
|
|
2884
|
-
import { z as
|
|
3016
|
+
import { z as z14 } from "zod";
|
|
2885
3017
|
var USER_MEMORY_MAX_CHARS = 32 * 1024;
|
|
2886
3018
|
var REMEMBER_TEXT_MAX_CHARS = 500;
|
|
2887
3019
|
var cache3 = /* @__PURE__ */ new Map();
|
|
@@ -2926,8 +3058,8 @@ function createRememberTool(deps = {}) {
|
|
|
2926
3058
|
name: "remember",
|
|
2927
3059
|
description: `Save a note to the user's long-term memory (${userMemoryPath()}), injected into every future session. ONLY use when the user explicitly asks to remember or save something. Never use autonomously.`,
|
|
2928
3060
|
permission: "write",
|
|
2929
|
-
parameters:
|
|
2930
|
-
text:
|
|
3061
|
+
parameters: z14.object({
|
|
3062
|
+
text: z14.string().max(REMEMBER_TEXT_MAX_CHARS).describe("The note to save, as a single concise sentence.")
|
|
2931
3063
|
}),
|
|
2932
3064
|
async execute(args) {
|
|
2933
3065
|
appendUserMemory(args.text, deps.home);
|
|
@@ -3357,7 +3489,10 @@ ${finished.result}`
|
|
|
3357
3489
|
}
|
|
3358
3490
|
}
|
|
3359
3491
|
const maxTokens = this.opts.contextMaxTokens ?? config.contextMaxTokens;
|
|
3360
|
-
const compacted = compactMessages(
|
|
3492
|
+
const compacted = compactMessages(
|
|
3493
|
+
[...this.messages],
|
|
3494
|
+
resolveCompactThreshold(config, maxTokens)
|
|
3495
|
+
);
|
|
3361
3496
|
if (compacted.compacted) {
|
|
3362
3497
|
this.messages = await this.applyCompactionSummary(compacted, signal);
|
|
3363
3498
|
this.turnMarkers = [];
|
|
@@ -3425,7 +3560,7 @@ ${finished.result}`
|
|
|
3425
3560
|
if (attempt >= maxRetries) break;
|
|
3426
3561
|
if (!failure && text.length === 0 && toolCalls.length === 0 && attempt >= 1 && (isFilteredEmptyFinish(lastFinishReason) || sawReasoning || lastUsage !== void 0 && lastUsage.completionTokens > 0))
|
|
3427
3562
|
break;
|
|
3428
|
-
const delayMs = computeRetryDelayMs(attempt, baseDelay, failure);
|
|
3563
|
+
const delayMs = computeRetryDelayMs(attempt, failure ? baseDelay : baseDelay * 2, failure);
|
|
3429
3564
|
yield {
|
|
3430
3565
|
type: "retry",
|
|
3431
3566
|
attempt: attempt + 2,
|
|
@@ -3772,6 +3907,9 @@ ${summary}`
|
|
|
3772
3907
|
|
|
3773
3908
|
export {
|
|
3774
3909
|
defaultAgentTasks,
|
|
3910
|
+
formatAgentTaskUpdate,
|
|
3911
|
+
ConfigSchema,
|
|
3912
|
+
contextWindowTokens,
|
|
3775
3913
|
estimateTokens,
|
|
3776
3914
|
summarizeMessages,
|
|
3777
3915
|
compactMessages,
|
|
@@ -3814,4 +3952,4 @@ export {
|
|
|
3814
3952
|
PLAN_MODE_PROMPT,
|
|
3815
3953
|
AgentLoop
|
|
3816
3954
|
};
|
|
3817
|
-
//# sourceMappingURL=chunk-
|
|
3955
|
+
//# sourceMappingURL=chunk-K3R42LOX.js.map
|