@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 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 to free up context |
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
- if (estimateTokens(messages) <= maxTokens) {
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) <= maxTokens) {
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 = z.object({
1274
- command: z.string().describe("Shell command to execute"),
1275
- timeout: z.number().positive().max(MAX_TIMEOUT).optional().describe("Timeout in seconds (default 120, max 600)"),
1276
- description: z.string().optional().describe("Short description of what the command does"),
1277
- run_in_background: z.boolean().optional().describe(
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 z2 } from "zod";
1412
- var schema2 = z2.object({
1413
- path: z2.string().describe("File path, absolute or relative to the working directory"),
1414
- old_string: z2.string().min(1).describe("Exact text to replace"),
1415
- new_string: z2.string().describe("Replacement text"),
1416
- replace_all: z2.boolean().optional().describe("Replace every occurrence instead of requiring a unique match")
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 z3 } from "zod";
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 = z3.object({
1712
- pattern: z3.string().describe(
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: z3.string().optional().describe("Directory to search, defaults to the working directory")
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 z4 } from "zod";
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 = z4.object({
1748
- pattern: z4.string().describe("Regular expression to search for"),
1749
- path: z4.string().optional().describe("File or directory to search, defaults to the working directory"),
1750
- glob: z4.string().optional().describe("Glob pattern to filter which files are searched, e.g. '*.ts'"),
1751
- ignoreCase: z4.boolean().optional().describe("Case-insensitive matching")
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 z5 } from "zod";
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 = z5.object({
1877
- path: z5.string().describe("File path, absolute or relative to the working directory"),
1878
- offset: z5.number().int().positive().optional().describe("1-based line number to start reading from"),
1879
- limit: z5.number().int().positive().optional().describe("Maximum number of lines to read (default 2000)")
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 z6 } from "zod";
1953
- var schema6 = z6.object({
1954
- path: z6.string().describe("File path, absolute or relative to the working directory"),
1955
- content: z6.string().describe("Full content to write to the file")
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 z7 } from "zod";
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 = z7.object({
2135
- id: z7.string().describe("Task id, e.g. task-1 or agent-1")
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: z7.object({}),
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 z8 } from "zod";
2214
- var todoItemSchema = z8.object({
2215
- id: z8.number().int(),
2216
- title: z8.string(),
2217
- status: z8.enum(["pending", "in_progress", "done"])
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 = z8.object({
2220
- todos: z8.array(todoItemSchema).describe("The full todo list, replacing the current one")
2351
+ var writeSchema = z9.object({
2352
+ todos: z9.array(todoItemSchema).describe("The full todo list, replacing the current one")
2221
2353
  });
2222
- var readSchema = z8.object({});
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 z9 } from "zod";
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 = z9.object({
2343
- url: z9.string().describe("The http(s) URL to fetch"),
2344
- maxChars: z9.number().int().positive().optional().describe("Maximum characters of text to return (default 20000)")
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 z10 } from "zod";
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 = z10.object({
2485
- query: z10.string().min(1).describe("The search query"),
2486
- maxResults: z10.number().int().min(1).max(MAX_RESULTS_LIMIT).optional().describe(
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 z11 } from "zod";
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: z11.object({
2753
- name: z11.string().describe("Name of the skill to load, as listed in the system prompt.")
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 z12 } from "zod";
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-TQ7GFTTE.js");
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: z12.object({
2854
- prompt: z12.string().describe("Complete, self-contained instructions for the subagent."),
2855
- description: z12.string().optional().describe("Short label for the subtask, shown in the result header."),
2856
- run_in_background: z12.boolean().optional().describe(
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 z13 } from "zod";
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: z13.object({
2930
- text: z13.string().max(REMEMBER_TEXT_MAX_CHARS).describe("The note to save, as a single concise sentence.")
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([...this.messages], maxTokens);
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-3L6QKJM4.js.map
3955
+ //# sourceMappingURL=chunk-K3R42LOX.js.map