omnexx 0.10.0 → 0.11.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.
@@ -34,7 +34,7 @@ function text(c, key) {
34
34
  return v;
35
35
  }
36
36
  async function serveChat(io, runCli, opts) {
37
- const { Session } = await import("./session-7Y5VFFFM.js");
37
+ const { Session } = await import("./session-A75AZD4D.js");
38
38
  const session = new Session(io, runCli);
39
39
  const write = (msg) => {
40
40
  println(io.stdout, JSON.stringify(msg));
@@ -299,4 +299,4 @@ async function serveChat(io, runCli, opts) {
299
299
  export {
300
300
  serveChat
301
301
  };
302
- //# sourceMappingURL=chat-server-RZKFHVDX.js.map
302
+ //# sourceMappingURL=chat-server-KRKEX6ID.js.map
@@ -22,10 +22,11 @@ import {
22
22
  runAgentLoop,
23
23
  runGates,
24
24
  scrubEnv,
25
+ thinkingGist,
25
26
  toolSpec,
26
27
  transcriptFor,
27
28
  workerTools
28
- } from "./chunk-AGALUEDT.js";
29
+ } from "./chunk-CP53ICXW.js";
29
30
  import {
30
31
  RunStore,
31
32
  git,
@@ -265,7 +266,6 @@ var CHAT_SYSTEM = `You are Omnexx in chat mode: a coding agent working turn by t
265
266
  var PLAN_MODE = `[Plan mode] Don't change anything yet: no edits and no commands. Investigate with the read-only tools, then reply with a concrete step-by-step plan: the files to change and how, the approach, how you will test it, and any risks or questions for me. Put the steps in the todo list. Stop after the plan; I'll approve it or ask for changes.`;
266
267
  var CHAT_FILE = "chat.json";
267
268
  var CHAT_DENIED = /* @__PURE__ */ new Set(["task"]);
268
- var firstLine = (t) => t.split("\n").find((l) => l.trim())?.trim() ?? "";
269
269
  function errorLine(t) {
270
270
  const lines = t.split("\n").map((l) => l.trim()).filter(Boolean);
271
271
  const telling = lines.find(
@@ -314,6 +314,8 @@ var CodeChat = class _CodeChat {
314
314
  ctx;
315
315
  events;
316
316
  store;
317
+ /** The last thinking line shown, so an identical one isn't printed again. */
318
+ lastThought = "";
317
319
  history = [];
318
320
  /** Set by open(); where repo memory lives. */
319
321
  paths;
@@ -513,9 +515,13 @@ ${renderNotes(known)}` }] : [],
513
515
  if (e.isError)
514
516
  v.line({ kind: "err", text: ` \u2717 ${errorLine(str(e.error)).slice(0, 160)}` });
515
517
  return;
516
- case "agent.thinking":
517
- v.line({ kind: "think", text: firstLine(str(e.text)).slice(0, 200) });
518
+ case "agent.thinking": {
519
+ const gist = thinkingGist(str(e.text), this.lastThought);
520
+ if (!gist) return;
521
+ this.lastThought = gist;
522
+ v.line({ kind: "think", text: gist });
518
523
  return;
524
+ }
519
525
  case "todo.update":
520
526
  v.todo(Array.isArray(e.items) ? e.items : []);
521
527
  return;
@@ -787,4 +793,4 @@ export {
787
793
  resolveModelLenient,
788
794
  CodeChat
789
795
  };
790
- //# sourceMappingURL=chunk-B4UJJZKS.js.map
796
+ //# sourceMappingURL=chunk-32JVRGO6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tui/attach.ts","../src/tui/code-chat.ts","../src/tui/undo.ts","../src/instructions/load.ts","../src/providers/profiles.ts"],"sourcesContent":["import { readFile, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, isAbsolute, resolve } from 'node:path';\nimport type { ContentBlock, ImageMediaType } from '../providers/types.js';\n\nconst MEDIA: Record<string, ImageMediaType> = {\n png: 'image/png',\n jpg: 'image/jpeg',\n jpeg: 'image/jpeg',\n gif: 'image/gif',\n webp: 'image/webp',\n};\nconst MAX_IMAGE_BYTES = 8 * 1024 * 1024;\n\n/**\n * Image paths in a message: plain, quoted, or as a terminal pastes a dragged file (spaces escaped\n * with backslashes). Paths that don't exist are ignored, so ordinary text never attaches anything.\n */\nexport function imagePathsIn(text: string): string[] {\n const re =\n /'([^']+\\.(?:png|jpe?g|gif|webp))'|\"([^\"]+\\.(?:png|jpe?g|gif|webp))\"|((?:\\\\ |[^\\s'\"])+\\.(?:png|jpe?g|gif|webp))/gi;\n const out: string[] = [];\n for (const m of text.matchAll(re)) {\n const p = (m[1] ?? m[2] ?? m[3] ?? '').replace(/\\\\ /g, ' ');\n if (p) out.push(p);\n }\n return out;\n}\n\n/** Read the images a message points at into content blocks; notes say what was skipped and why. */\nexport async function attachImages(\n text: string,\n cwd: string,\n): Promise<{ blocks: ContentBlock[]; attached: string[]; skipped: string[] }> {\n const blocks: ContentBlock[] = [];\n const attached: string[] = [];\n const skipped: string[] = [];\n for (const raw of imagePathsIn(text)) {\n const p = raw.startsWith('~/')\n ? resolve(homedir(), raw.slice(2))\n : isAbsolute(raw)\n ? raw\n : resolve(cwd, raw);\n const info = await stat(p).catch(() => undefined);\n if (!info?.isFile()) continue;\n if (info.size > MAX_IMAGE_BYTES) {\n skipped.push(`${basename(p)} (over 8 MB)`);\n continue;\n }\n const ext = p.split('.').pop()?.toLowerCase() ?? '';\n const mediaType = MEDIA[ext];\n if (!mediaType) continue;\n blocks.push({ type: 'image', mediaType, data: (await readFile(p)).toString('base64') });\n attached.push(basename(p));\n }\n return { blocks, attached, skipped };\n}\n","import { thinkingGist } from '../telemetry/narrate.js';\nimport { InCycleWatch } from '../guard/stuck.js';\nimport { orderForStrategy, strategyFor } from '../config/routing.js';\nimport { randomBytes } from 'node:crypto';\nimport { mkdir, readdir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { buildCodemap, renderCodemap } from '../agent/codemap.js';\nimport {\n clearOldToolResults,\n contextTokens,\n pruneSuperseded,\n transcriptFor,\n} from '../agent/compaction.js';\nimport { runAgentLoop } from '../agent/loop.js';\nimport type { CliIO } from '../cli/io.js';\nimport { resolveRunDeps } from '../cli/run-deps.js';\nimport type { OmnexxConfig } from '../config/schema.js';\nimport { loadConfig } from '../config/load.js';\nimport { parseDuration } from '../config/duration.js';\nimport { EventLog, type OmnexxEvent } from '../core/events.js';\nimport { RunStore } from '../core/run-store.js';\nimport { RunningContext } from '../core/running-context.js';\nimport { readTextOr, writeJsonAtomic } from '../core/atomic.js';\nimport { runsDir } from '../core/paths.js';\nimport { QuotaLedger } from '../providers/quota-ledger.js';\nimport { restoreTree, snapshotTree } from './undo.js';\nimport { renderNotes } from '../core/notes.js';\nimport type { OmnexxPaths } from '../core/paths.js';\nimport { readRepoNotes, saveToRepoMemory } from '../core/repo-memory.js';\nimport { realClock, type Clock } from '../core/clock.js';\nimport { estimateTokens } from '../core/tokens.js';\nimport { loadInstructions, renderInstructions } from '../instructions/load.js';\nimport type { ResolvedModel } from '../providers/pricing.js';\nimport { resolveModelLenient } from '../providers/profiles.js';\nimport type { ContentBlock, Message, Provider } from '../providers/types.js';\nimport { scrubEnv } from '../security/env-scrub.js';\nimport { PathJail } from '../security/paths.js';\nimport { Redactor } from '../security/redact.js';\nimport { readOnlyTools, workerTools, toolSpec } from '../tools/registry.js';\nimport type { ToolWhere } from '../tools/extra/types.js';\nimport { helperRunner } from '../agent/subagent.js';\nimport type { TodoItem } from '../tools/todo.js';\nimport type { Tool, ToolContext } from '../tools/types.js';\nimport { runGates } from '../verify/gates.js';\nimport { emptyTelemetry, fold, type Telemetry } from '../telemetry/aggregate.js';\n\n/** Questions read without editing; only this many turns in a row without an edit is circling. */\nconst CHAT_NO_EDIT_TURNS = 25;\n\nexport const CHAT_SYSTEM = `You are Omnexx in chat mode: a coding agent working turn by turn with a person at the keyboard, directly in their repository (your cwd).\n\n- Do what they ask, then stop and reply briefly: what you changed and what you checked.\n- For anything with more than two steps, write a todo list first with the todo tool and keep it current: one item in_progress at a time, done as you finish.\n- Use the context tool to pull in wider context (general, repo, heat, recent) only when it helps; save what you learn about the codebase with context update.\n- Before each tool call, say in one short sentence what you are about to do and why.\n- Explore with the codebase map, outline, search and ranged reads. Edit with str_replace / multi_edit; write_file for new or small files.\n- Run the relevant tests with bash before you finish when you changed code.\n- The person may send messages while you work; they arrive marked as such. Read them and adjust at once.\n- Some actions need their OK (paths outside the repo, commands the policy refuses); they are asked for you. If they say no, find another way or explain.\n- Ask a question instead of guessing when the request is ambiguous and the choice matters.`;\n\n/** Prepended to a message in plan mode. */\nconst PLAN_MODE = `[Plan mode] Don't change anything yet: no edits and no commands. Investigate with the read-only tools, then reply with a concrete step-by-step plan: the files to change and how, the approach, how you will test it, and any risks or questions for me. Put the steps in the todo list. Stop after the plan; I'll approve it or ask for changes.`;\n\n/** Where a chat's conversation is saved, inside its store. */\nconst CHAT_FILE = 'chat.json';\n\n/** Tools chat leaves out: helpers need a run. */\nconst CHAT_DENIED = new Set(['task']);\n\n/** What the chat shows the person. */\nexport type ChatLine =\n | { kind: 'act'; text: string }\n | { kind: 'think'; text: string }\n | { kind: 'out'; text: string }\n | { kind: 'feed'; text: string }\n | { kind: 'err'; text: string };\n\n/** The agent's browser after an action, for a live view (the image is a file under the logs). */\nexport interface BrowserFrame {\n path: string;\n mime: string;\n url: string;\n action: string;\n}\n\n/** A helper (subagent) starting or finishing, for a live view of who is doing what. */\nexport type HelperEvent =\n | { phase: 'start'; id: number; kind: string; agent?: string; description: string }\n | { phase: 'finish'; id: number; end: string; turns: number; usd: number };\n\nexport interface ChatView {\n line: (l: ChatLine) => void;\n browser?: (f: BrowserFrame) => void;\n helper?: (h: HelperEvent) => void;\n /** Reply text as it streams in (cleared when the reply lands as an `out` line). */\n stream: (delta: string) => void;\n todo: (items: TodoItem[]) => void;\n}\n\n/** The line of a failed action's output worth showing: the error, not the `[exit 1, …]` header. */\nexport function errorLine(t: string): string {\n const lines = t\n .split('\\n')\n .map((l) => l.trim())\n .filter(Boolean);\n const telling = lines.find(\n (l) =>\n !l.startsWith('[exit') &&\n /error|fail|not ok|assert|expected|cannot|denied|refused|✗/i.test(l),\n );\n return telling ?? lines.find((l) => !l.startsWith('[exit')) ?? lines[0] ?? '';\n}\n\n/** Price an unpriced model at $0 so chat works on any endpoint; spend then reads $0.00. */\nfunction chatModel(ref: string, config: OmnexxConfig): ResolvedModel {\n const m = resolveModelLenient(ref, config);\n return {\n ...m,\n price: m.price ?? {\n id: m.id,\n input: 0,\n output: 0,\n cache_write_5m: 0,\n cache_write_1h: 0,\n cache_read: 0,\n },\n };\n}\n\n/**\n * Turn-by-turn coding in the user's own checkout (no worktree, no rollback). The conversation\n * carries across messages; messages typed mid-turn reach the model at its next step; Esc ends a\n * turn cleanly; long conversations are trimmed, then summarized, between messages.\n */\nexport class CodeChat {\n /** The last thinking line shown, so an identical one isn't printed again. */\n private lastThought = '';\n private history: Message[] = [];\n /** Set by open(); where repo memory lives. */\n paths: OmnexxPaths | undefined;\n /** Before each message: the files as they were, and how long the conversation was. */\n private snapshots: { tree: string | undefined; historyLength: number }[] = [];\n private queue: string[] = [];\n private abort: AbortController | undefined;\n private view: ChatView | undefined;\n private models: ResolvedModel[];\n usd = 0;\n /** Token, tool and turn totals for this conversation, folded from its events. */\n telemetry: Telemetry = emptyTelemetry();\n /** Estimated tokens the next request starts with (system + tools + conversation). */\n contextUsed = 0;\n\n private constructor(\n private readonly config: OmnexxConfig,\n private readonly provider: Provider,\n private readonly tools: readonly Tool[],\n private readonly system: { text: string; cacheBreakpoint?: boolean }[],\n private readonly ctx: ToolContext,\n private readonly events: EventLog,\n private readonly store: RunStore,\n model: string | undefined,\n ) {\n const refs = model ? [model] : config.models.worker;\n this.models = orderForStrategy(\n (typeof refs === 'string' ? [refs] : refs).map((r) => chatModel(r, config)),\n strategyFor(config.models, 'chat'),\n );\n this.contextUsed = this.baseTokens();\n // One listener for the whole chat: every action shows as a line while it happens.\n events.onEvent((e) => {\n fold(this.telemetry, e);\n this.onEvent(e);\n });\n }\n\n static async open(\n io: CliIO,\n ask: (q: string) => Promise<boolean>,\n model?: string,\n ): Promise<CodeChat> {\n // Build the provider for the chat model only (not every role's), so chatting on one\n // endpoint never needs keys for the others.\n const { config: base } = await loadConfig({ cwd: io.cwd, env: io.env });\n const ref = model ?? base.models.chat;\n const { config, paths, deps } = await resolveRunDeps(\n io,\n io.cwd,\n ref ? { models: { planner: ref, worker: ref, cheap: ref } } : {},\n );\n const store = new RunStore(\n paths,\n `chat_${new Date().toISOString().replace(/\\D/g, '').slice(0, 14)}_${randomBytes(3).toString('hex')}`,\n );\n await mkdir(store.logsDir, { recursive: true, mode: 0o700 });\n const redactor = Redactor.fromEnv(io.env, deps.secrets);\n const clock = io.clock ?? realClock;\n const events = new EventLog(store.eventsPath, store.runId, redactor, clock);\n const tmp = join('/tmp', `omnexx-${store.runId}`);\n await mkdir(tmp, { recursive: true, mode: 0o700 });\n const jail = new PathJail(io.cwd);\n let n = 0;\n const ctx: ToolContext = {\n jail,\n env: scrubEnv(io.env, { passthrough: config.policy.env_passthrough, set: { TMPDIR: tmp } }),\n store,\n events,\n redactor,\n policy: {\n root: jail.root,\n home: io.env.HOME ?? '/nonexistent',\n allowNetwork: config.policy.allow_network,\n extraDeny: config.policy.deny,\n scratch: [tmp, `/private${tmp}`],\n },\n cycle: 0,\n maxCmdTimeoutMs: parseDuration(config.budget.max_cmd_timeout),\n notesMaxTokens: config.context.notes_max_tokens,\n today: new Date(clock.now()).toISOString().slice(0, 10),\n nextCommandId: () => `cmd-chat-${++n}`,\n edited: new Set(),\n reads: new Map(),\n ask,\n runningContext: new RunningContext(store, 'chat'),\n };\n const tools = (await workerTools(config, { repoRoot: jail.root, env: io.env })).filter(\n (t) => !CHAT_DENIED.has(t.name),\n );\n const instructions = renderInstructions(await loadInstructions(jail.root, jail.root));\n const codemap = renderCodemap(\n await buildCodemap(jail.root),\n config.context.repo_map_max_tokens,\n );\n // What earlier runs and chats learned about this repo; the remember tool adds to it.\n const known = await readRepoNotes(paths, jail.root);\n if (known.length) await store.writeNotes(known);\n const system = [\n { text: CHAT_SYSTEM },\n ...(instructions ? [{ text: instructions }] : []),\n ...(known.length ? [{ text: `# Lessons about this repo\\n\\n${renderNotes(known)}` }] : []),\n { text: codemap, cacheBreakpoint: true },\n ];\n const chat = new CodeChat(config, deps.provider, tools, system, ctx, events, store, ref);\n chat.paths = paths;\n // Chat gets helpers too (the task and agent tools), on the chat model and the chat's spend.\n ctx.subagent = chat.helpers({ repoRoot: jail.root, env: io.env }, clock);\n return chat;\n }\n\n /** Read-only helpers that run on this chat's model, budget and spend. */\n helpers(where: ToolWhere, clock: Clock): NonNullable<ToolContext['subagent']> {\n const cfg = this.config.context;\n return helperRunner(\n {\n tools: () => readOnlyTools(this.config, where),\n // models.helpers when set (so helpers can draw on other providers' quotas), else chat's.\n models: () => Promise.resolve(this.helperModels()),\n strategy: strategyFor(this.config.models, 'helpers'),\n maxTokens: Math.min(this.config.providers.anthropic.max_tokens, 4_096),\n limits: { tokens: cfg.subagent_max_tokens, turns: cfg.subagent_max_turns },\n loop: {\n provider: this.provider,\n budget: this.config.budget,\n clock,\n events: this.events,\n spentUsd: () => this.usd,\n onUsage: (_u, usd) => {\n this.usd += usd;\n return Promise.resolve();\n },\n control: () => Promise.resolve(this.paused ? 'pause' : 'continue'),\n pausePollMs: 300,\n },\n },\n this.ctx,\n );\n }\n\n private helperModels(): ResolvedModel[] {\n const h = this.config.models.helpers;\n if (!h) return this.models;\n return (typeof h === 'string' ? [h] : h).map((ref) => chatModel(ref, this.config));\n }\n\n /** \"provider:model\" of the model chat talks to. */\n get model(): string {\n const m = this.models[0];\n return m ? `${m.provider}:${m.alias}` : '?';\n }\n\n /** The conversation so far, to carry into a chat on another model (/model). */\n get conversation(): Message[] {\n return this.history;\n }\n\n set conversation(m: Message[]) {\n this.history = m;\n this.contextUsed = this.baseTokens() + contextTokens(m);\n }\n\n get busy(): boolean {\n return this.abort !== undefined;\n }\n\n /** A message typed while the agent works: it joins the conversation at the next step. */\n steer(text: string): void {\n this.queue.push(text);\n }\n\n /** Messages that arrived after the agent's last step: the session sends them next. */\n takeLeftovers(): string[] {\n return this.queue.splice(0);\n }\n\n /** /pause: hold at the next step boundary until resume() (the loop polls control()). */\n paused = false;\n\n /** Esc: end the current turn, keeping every completed step. */\n interrupt(): void {\n this.abort?.abort();\n }\n\n /** The context limit the chat compacts at. */\n get contextLimit(): number {\n return this.config.context.compact_at;\n }\n\n private baseTokens(): number {\n return estimateTokens(JSON.stringify([this.system, this.tools.map(toolSpec)]));\n }\n\n /**\n * What the context is made of, for the editor: the system prompt (instructions, project notes),\n * every tool's definition, and the conversation. The first two are sent with every request,\n * even before anything is said.\n */\n get contextParts(): { system: number; tools: number; conversation: number } {\n const system = estimateTokens(JSON.stringify(this.system));\n const tools = estimateTokens(JSON.stringify(this.tools.map(toolSpec)));\n return { system, tools, conversation: Math.max(0, this.contextUsed - system - tools) };\n }\n\n private onEvent(e: OmnexxEvent): void {\n const v = this.view;\n if (!v) return;\n const str = (x: unknown): string => (typeof x === 'string' ? x : '');\n const num = (x: unknown): number => (typeof x === 'number' ? x : 0);\n switch (e.type) {\n case 'browser.frame':\n v.browser?.({\n path: str(e.path),\n mime: str(e.mime),\n url: str(e.url),\n action: str(e.action),\n });\n return;\n case 'subagent.start':\n v.helper?.({\n phase: 'start',\n id: num(e.id),\n kind: str(e.kind),\n ...(typeof e.agent === 'string' ? { agent: e.agent } : {}),\n description: str(e.description),\n });\n return;\n case 'subagent.finish':\n v.helper?.({\n phase: 'finish',\n id: num(e.id),\n end: str(e.end),\n turns: num(e.turns),\n usd: num(e.usd),\n });\n return;\n case 'tool.start':\n v.line({ kind: 'act', text: str(e.doing) });\n return;\n case 'tool.call':\n if (e.isError)\n v.line({ kind: 'err', text: ` ✗ ${errorLine(str(e.error)).slice(0, 160)}` });\n return;\n case 'agent.thinking': {\n const gist = thinkingGist(str(e.text), this.lastThought);\n if (!gist) return;\n this.lastThought = gist;\n v.line({ kind: 'think', text: gist });\n return;\n }\n case 'todo.update':\n v.todo(Array.isArray(e.items) ? (e.items as TodoItem[]) : []);\n return;\n case 'steer.applied':\n v.line({ kind: 'feed', text: 'read your new message' });\n return;\n case 'context.cleared':\n v.line({ kind: 'feed', text: 'trimmed old tool output to keep the context small' });\n return;\n case 'provider.retry':\n v.line({\n kind: 'feed',\n text: `waiting for the model provider (${str(e.error).slice(0, 80)})`,\n });\n return;\n case 'provider.failover':\n v.line({\n kind: 'feed',\n text: `switching from ${str(e.provider)} (${str(e.error).slice(0, 60)})`,\n });\n return;\n default:\n }\n }\n\n /** One message: the agent works until it replies. */\n async send(\n text: string,\n view: ChatView,\n opts: { plan?: boolean; images?: ContentBlock[] } = {},\n ): Promise<void> {\n // Plan mode: read-only tools, and the ask goes in the message (the cached prefix is unchanged).\n const tools = opts.plan ? this.tools.filter((t) => t.readOnly) : this.tools;\n const ask = opts.plan ? `${PLAN_MODE}\\n\\n${text}` : text;\n this.view = view;\n this.snapshots.push({\n tree: await snapshotTree(this.ctx.jail.root),\n historyLength: this.history.length,\n });\n this.ctx.edited.clear();\n this.ctx.reads?.clear();\n await this.compactIfNeeded();\n const abort = new AbortController();\n this.abort = abort;\n try {\n const result = await runAgentLoop(\n {\n system: this.system,\n history: this.history,\n first: { role: 'user', content: [{ type: 'text', text: ask }, ...(opts.images ?? [])] },\n tools: tools.map(toolSpec),\n },\n {\n provider: this.provider,\n models: this.models,\n ...(this.paths\n ? (() => {\n const quota = new QuotaLedger(join(this.paths.configHome, 'quota.json'));\n return {\n modelExhausted: (r: string) => quota.exhaustedAt(r) !== undefined,\n markExhausted: (r: string) => {\n quota.mark(r);\n },\n };\n })()\n : {}),\n tools,\n toolCtx: this.ctx,\n budget: { ...this.config.budget, max_turns_per_cycle: 200 },\n // A chat turn that circles gets told so once, then stops (it once burned 400k tokens).\n watch: new InCycleWatch(\n {\n repeatedToolCall: this.config.stuck.repeated_tool_call,\n noEditTurns: CHAT_NO_EDIT_TURNS,\n burnTokens: undefined,\n },\n () => this.ctx.edited.size,\n ),\n watchMode: 'nudge',\n maxTokens: this.config.providers.anthropic.max_tokens,\n clock: realClock,\n events: this.events,\n spentUsd: () => this.usd,\n onUsage: (_u, usd) => {\n this.usd += usd;\n return Promise.resolve();\n },\n control: () => Promise.resolve(this.paused ? 'pause' : 'continue'),\n pausePollMs: 300,\n signal: abort.signal,\n onDelta: (d) => {\n if (d.text) view.stream(d.text);\n },\n pendingInput: () => this.queue.splice(0),\n // Within one long turn only old tool output is trimmed; summaries happen between turns.\n compaction: {\n settings: {\n clearAt: this.config.context.clear_tool_results_at,\n keepToolResults: this.config.context.keep_tool_results,\n compactAt: Number.MAX_SAFE_INTEGER,\n keepTurns: this.config.context.compact_keep_turns,\n },\n },\n },\n );\n this.history = result.messages;\n view.stream('');\n if (result.finalText) view.line({ kind: 'out', text: result.finalText });\n if (result.end === 'stop-now')\n view.line({ kind: 'feed', text: 'stopped. tell me what to do instead' });\n else if (result.end !== 'done') view.line({ kind: 'err', text: `(stopped: ${result.end})` });\n } finally {\n this.abort = undefined;\n this.paused = false;\n this.contextUsed = this.baseTokens() + contextTokens(this.history);\n }\n if (this.ctx.edited.size && this.config.gates.length) await this.check(view);\n await this.rememberForRepo();\n await this.persist();\n }\n\n /**\n * /undo: put the files back as they were before the last message and drop that exchange.\n * Returns the paths restored, or undefined when there is nothing to undo.\n */\n async undo(): Promise<string[] | undefined> {\n const snap = this.snapshots.pop();\n if (!snap) return undefined;\n const touched = snap.tree ? await restoreTree(this.ctx.jail.root, snap.tree) : [];\n this.history = this.history.slice(0, snap.historyLength);\n this.contextUsed = this.baseTokens() + contextTokens(this.history);\n await this.persist();\n return touched;\n }\n\n /** Save the conversation so `omnexx --continue` can pick it up. */\n private async persist(): Promise<void> {\n await writeJsonAtomic(this.store.file(CHAT_FILE), {\n cwd: this.ctx.jail.root,\n model: this.model,\n savedAt: Date.now(),\n history: this.history,\n }).catch(() => undefined);\n }\n\n /** Load the newest saved chat for this folder into this one (--continue). Returns its length. */\n async resumeLatest(): Promise<number> {\n if (!this.paths) return 0;\n const dirs = (await readdir(runsDir(this.paths)).catch(() => [] as string[]))\n .filter((d) => d.startsWith('chat_') && d !== this.store.runId)\n .sort()\n .reverse();\n for (const d of dirs) {\n const raw = await readTextOr(join(runsDir(this.paths), d, CHAT_FILE), '');\n if (!raw) continue;\n const saved = JSON.parse(raw) as { cwd?: string; history?: Message[] };\n if (saved.cwd !== this.ctx.jail.root || !saved.history?.length) continue;\n this.conversation = saved.history;\n return saved.history.filter(\n (m) => m.role === 'user' && m.content.some((b) => b.type === 'text'),\n ).length;\n }\n return 0;\n }\n\n /** Lessons the agent recorded this message become part of the repo's memory. */\n private async rememberForRepo(): Promise<void> {\n if (!this.paths) return;\n await saveToRepoMemory(\n this.paths,\n this.ctx.jail.root,\n await this.store.readNotes(),\n this.config.context.notes_max_tokens,\n ).catch(() => undefined);\n }\n\n /** Between messages: trim old tool output, then summarize the oldest exchanges if still big. */\n private async compactIfNeeded(force = false): Promise<void> {\n const limit = this.config.context.compact_at;\n if (!force && contextTokens(this.history) < this.config.context.clear_tool_results_at) return;\n this.history = clearOldToolResults(\n pruneSuperseded(this.history).messages,\n this.config.context.keep_tool_results,\n ).messages;\n if (!force && contextTokens(this.history) < limit) return;\n // Keep the last few exchanges verbatim; an exchange starts at a user message with text.\n const starts = this.history.flatMap((m, i) =>\n m.role === 'user' && m.content.some((b) => b.type === 'text') ? [i] : [],\n );\n const cut = starts.at(-Math.max(1, this.config.context.compact_keep_turns));\n if (cut === undefined || cut === 0) return;\n const head = this.history.slice(0, cut);\n const summary = await this.summarize(head).catch(() => undefined);\n // Your own running context survives the summary verbatim: it's where the work stands.\n const running = (await this.ctx.runningContext?.forPrompt()) ?? '';\n if (!summary) return;\n this.history = [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `Summary of our conversation so far:\\n${summary}${running ? `\\n\\n${running}` : ''}`,\n },\n ],\n },\n {\n role: 'assistant',\n content: [{ type: 'text', text: 'Understood; continuing from there.' }],\n },\n ...this.history.slice(cut),\n ];\n this.view?.line({\n kind: 'feed',\n text: 'summarized the earlier conversation to free up context',\n });\n this.contextUsed = this.baseTokens() + contextTokens(this.history);\n }\n\n /** /compact. */\n async compact(view: ChatView): Promise<void> {\n this.view = view;\n await this.compactIfNeeded(true);\n }\n\n private async summarize(head: readonly Message[]): Promise<string | undefined> {\n const m = this.models[0];\n if (!m) return undefined;\n const res = await this.provider.complete({\n model: m.id,\n route: m.provider,\n system: [\n { text: 'You compress a coding conversation so it can continue without the transcript.' },\n ],\n tools: [],\n messages: [\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: `Summarize: what the user asked for and decided, what was changed (file paths), what was verified, and what is still open. Concrete and brief.\\n\\n${this.ctx.redactor.text(transcriptFor(head))}`,\n },\n ],\n },\n ],\n maxTokens: 1_500,\n messageBreakpoints: [],\n });\n const t = res.content\n .flatMap((b) => (b.type === 'text' ? [b.text] : []))\n .join('\\n')\n .trim();\n return t || undefined;\n }\n\n private async check(view: ChatView): Promise<void> {\n view.line({ kind: 'act', text: 'running your checks' });\n const results = await runGates(this.config.gates, {\n cwd: this.ctx.jail.root,\n env: this.ctx.env,\n logsDir: this.store.logsDir,\n label: `chat${Date.now()}`,\n maxCmdTimeoutMs: this.ctx.maxCmdTimeoutMs,\n redact: (s) => this.ctx.redactor.text(s),\n });\n const line = results\n .map(\n (r) =>\n `${r.name} ${r.exitCode === 0 ? '✓' : `✗${r.failures.length ? ` (${r.failures.length})` : ''}`}`,\n )\n .join(' ');\n view.line({\n kind: results.every((r) => r.exitCode === 0) ? 'feed' : 'err',\n text: `checks ${line}`,\n });\n }\n\n /** Forget the conversation (the codebase map and instructions stay). */\n async clear(): Promise<void> {\n this.history = [];\n this.telemetry = emptyTelemetry();\n this.contextUsed = this.baseTokens();\n // A fresh conversation starts a fresh running context; the old one is kept with the chat.\n await this.ctx.runningContext?.archive();\n }\n}\n","import { randomUUID } from 'node:crypto';\nimport { rm } from 'node:fs/promises';\nimport { tmpdir } from 'node:os';\nimport { join } from 'node:path';\nimport { git } from '../git/git.js';\n\n/**\n * The working tree (tracked and untracked, not ignored files) as a git tree object, written\n * through a throwaway index so the person's index, stash and branches are never touched.\n * Undefined outside a git repository.\n */\nexport async function snapshotTree(root: string): Promise<string | undefined> {\n const index = join(tmpdir(), `omnexx-snap-${randomUUID()}`);\n const env = { GIT_INDEX_FILE: index };\n try {\n const add = await git(root, ['add', '-A'], { allowFailure: true, env });\n if (add.exitCode !== 0) return undefined;\n const tree = await git(root, ['write-tree'], { allowFailure: true, env });\n return tree.exitCode === 0 ? tree.stdout.trim() : undefined;\n } finally {\n await rm(index, { force: true });\n }\n}\n\n/**\n * Put the working tree back to `tree`: changed and deleted files are restored, files created\n * since are removed. Returns the paths it touched.\n */\nexport async function restoreTree(root: string, tree: string): Promise<string[]> {\n const now = await snapshotTree(root);\n if (!now || now === tree) return [];\n const diff = await git(root, ['diff', '--name-status', '--no-renames', '-z', tree, now]);\n const parts = diff.stdout.split('\\0').filter(Boolean);\n const touched: string[] = [];\n const restore: string[] = [];\n for (let i = 0; i + 1 < parts.length; i += 2) {\n const status = parts[i];\n const path = parts[i + 1] ?? '';\n touched.push(path);\n if (status === 'A') await rm(join(root, path), { force: true });\n else restore.push(path);\n }\n if (restore.length)\n await git(root, ['restore', `--source=${tree}`, '--worktree', '--', ...restore]);\n return touched;\n}\n","import { readdir } from 'node:fs/promises';\nimport { isAbsolute, join, relative, sep } from 'node:path';\nimport { CHARS_PER_TOKEN } from '../core/tokens.js';\nimport { parseFrontmatter } from './frontmatter.js';\nimport { readTextIfExists } from './read.js';\n\n/** Total instruction budget across every file, in estimated tokens. */\nexport const INSTRUCTIONS_MAX_TOKENS = 8_000;\n\nconst TRUNCATE_MARKER = '… (truncated)';\n\nexport interface InstructionFile {\n /** Repo-relative POSIX path, used as the section header (and in the truncation note). */\n path: string;\n /** Raw file text; rendering trims it. Cut by the budget before it gets here. */\n text: string;\n}\n\nexport interface LoadedInstructions {\n /**\n * Render order, which is also the precedence order: the first file survives longest when the\n * 8k-token budget cuts. Root files in the documented order, then nested files shallow → deep.\n */\n files: InstructionFile[];\n /** Files dropped or cut to fit the budget, lowest precedence first. */\n truncated: string[];\n}\n\n/** Root files in precedence order; nested files are appended after these. */\nconst ROOT_FILES = ['OMNEXX.md', 'AGENTS.md', 'CLAUDE.md'] as const;\nconst NESTED_FILES = ['AGENTS.md', 'CLAUDE.md'] as const;\n\nlet last: LoadedInstructions | undefined;\n\n/** Directories between the repo root and cwdInRepo, shallow first; [] when cwd is outside. */\nfunction nestedDirs(repoRoot: string, cwdInRepo: string): string[] {\n const rel = relative(repoRoot, cwdInRepo);\n if (rel === '' || rel.startsWith('..') || isAbsolute(rel)) return [];\n const dirs: string[] = [];\n let acc = '';\n for (const part of rel.split(sep).filter(Boolean)) {\n acc = acc === '' ? part : join(acc, part);\n dirs.push(acc);\n }\n return dirs;\n}\n\nfunction compose(files: readonly InstructionFile[], truncated: readonly string[]): string {\n if (files.length === 0) return '';\n const parts = ['# Project instructions', ''];\n for (const f of files) parts.push(`### ${f.path}`, '', f.text.trim(), '');\n let out = parts.join('\\n').trimEnd();\n if (truncated.length > 0) {\n out += `\\n\\n> Note: project instructions were truncated to fit the ${INSTRUCTIONS_MAX_TOKENS}-token budget; cut: ${truncated.join(', ')}`;\n }\n return out;\n}\n\n/**\n * Read every project instruction file relevant to cwdInRepo: the root files in the documented\n * precedence order, `.cursor/rules/*.mdc` (frontmatter stripped, sorted by name), and nested\n * AGENTS.md/CLAUDE.md in the parent directories of cwdInRepo. The combined render is capped at\n * 8k tokens, cutting the lowest precedence (last rendered) files first and noting every cut.\n * The same repo and cwd always produce byte-identical output, so the prompt prefix stays stable.\n */\nexport async function loadInstructions(\n repoRoot: string,\n cwdInRepo: string,\n): Promise<LoadedInstructions> {\n const files: InstructionFile[] = [];\n const push = async (path: string, file: string, frontmatter = false): Promise<void> => {\n const raw = await readTextIfExists(file);\n if (raw === undefined) return;\n const text = frontmatter ? parseFrontmatter(raw).body : raw;\n if (text.trim()) files.push({ path, text });\n };\n for (const name of ROOT_FILES) await push(name, join(repoRoot, name));\n const rulesDir = join(repoRoot, '.cursor', 'rules');\n const rules = await readdir(rulesDir).catch(() => []);\n for (const name of [...rules]\n .filter((n) => n.endsWith('.mdc'))\n .sort((a, b) => a.localeCompare(b))) {\n await push(`.cursor/rules/${name}`, join(rulesDir, name), true);\n }\n await push(\n '.github/copilot-instructions.md',\n join(repoRoot, '.github', 'copilot-instructions.md'),\n );\n for (const dir of nestedDirs(repoRoot, cwdInRepo)) {\n const posixDir = dir.split(sep).join('/');\n for (const name of NESTED_FILES) await push(`${posixDir}/${name}`, join(repoRoot, dir, name));\n }\n\n const budget = INSTRUCTIONS_MAX_TOKENS * CHARS_PER_TOKEN;\n const truncated: string[] = [];\n let out = compose(files, truncated);\n while (out.length > budget && files.length > 1) {\n const dropped = files.pop();\n if (dropped) truncated.push(dropped.path);\n out = compose(files, truncated);\n }\n if (out.length > budget) {\n // One file bigger than the whole budget: keep its head instead of dropping it.\n const only = files[0];\n if (only) {\n truncated.push(only.path);\n let text = only.text;\n for (;;) {\n only.text = text;\n out = compose(files, truncated);\n if (out.length <= budget) break;\n const keep = text.length - (out.length - budget) - TRUNCATE_MARKER.length - 1;\n const next = `${text.slice(0, Math.max(0, keep))}\\n${TRUNCATE_MARKER}`;\n if (next === text) break;\n text = next;\n }\n }\n }\n const loaded: LoadedInstructions = { files, truncated };\n last = loaded;\n return loaded;\n}\n\n/**\n * Render the instructions for the prompt prefix. Zero-arg form uses the result of the last\n * `loadInstructions` call (prompts.ts wires this); pass an explicit value in tests. Returns ''\n * when nothing was loaded, so callers can skip the block entirely.\n */\nexport function renderInstructions(\n loaded: LoadedInstructions = last ?? { files: [], truncated: [] },\n): string {\n return compose(loaded.files, loaded.truncated);\n}\n","import { ConfigError } from '../errors.js';\nimport type { OmnexxConfig, PriceConfig } from '../config/schema.js';\nimport { BUILTIN_PRICING, resolveModel, type ResolvedModel } from './pricing.js';\n\n/** A resolved model whose price may be unknown (unpriced). */\nexport interface PricedModel extends Omit<ResolvedModel, 'price'> {\n price: PriceConfig | undefined;\n}\n\nexport interface CostBreakdown {\n usd: number | undefined;\n unpriced: boolean;\n}\n\n/**\n * Lenient sibling of `resolveModel` (src/providers/pricing.ts): never throws for a\n * missing price. Unknown models resolve to their literal id with `price: undefined`\n * so tokens are still counted while cost stays unknown. Unknown providers still\n * throw: that is a config error, not a pricing gap. Run-deps and the loop can\n * switch to this resolver (see INTEGRATION note in docs/integration-notes.md).\n */\nexport function resolveModelLenient(ref: string, config: OmnexxConfig): PricedModel {\n if (!/^[a-z][a-z0-9_-]*:\\S+$/.test(ref))\n throw new ConfigError(`bad model ref \"${ref}\"`, 'expected \"<provider>:<alias-or-model-id>\"');\n try {\n const m = resolveModel(ref, config);\n return m;\n } catch (err) {\n if (!(err instanceof ConfigError)) throw err;\n // Unknown provider (\"unknown provider ...\") or unknown price (\"no price for\n // model ...\"): both resolve leniently. Only malformed refs still throw, and\n // resolveModel throws those as ConfigError too, so check the prefix.\n if (\n !err.message.startsWith('no price for model') &&\n !err.message.startsWith('unknown provider')\n )\n throw err;\n const cut = ref.indexOf(':');\n const provider = ref.slice(0, cut);\n const name = ref.slice(cut + 1);\n const known = Object.entries({ ...BUILTIN_PRICING, ...config.pricing }).find(\n ([, p]) => p.id === name,\n );\n if (known) return { provider, alias: known[0], id: name, price: known[1] };\n return { provider, alias: name, id: name, price: undefined };\n }\n}\n\n/** A role's failover chain, in order, never throwing for missing prices. */\nexport function resolveChainLenient(\n refs: string | readonly string[],\n config: OmnexxConfig,\n): PricedModel[] {\n return (typeof refs === 'string' ? [refs] : refs).map((r) => resolveModelLenient(r, config));\n}\n\n/** Cost in USD, or undefined when the model has no price. Tokens are always counted. */\nexport function costOf(\n usage: {\n uncached: number;\n cacheWrite5m: number;\n cacheWrite1h: number;\n cacheRead: number;\n output: number;\n },\n price: PriceConfig | undefined,\n): CostBreakdown {\n if (!price) return { usd: undefined, unpriced: true };\n const M = 1_000_000;\n return {\n usd:\n (usage.uncached * price.input +\n usage.cacheWrite5m * price.cache_write_5m +\n usage.cacheWrite1h * price.cache_write_1h +\n usage.cacheRead * price.cache_read +\n usage.output * price.output) /\n M,\n unpriced: false,\n };\n}\n\n/** Display for a cost that may be unknown: \"$1.2345\" or \"\\u2013\". */\nexport function formatCost(usd: number | undefined): string {\n return usd === undefined ? '\\u2013' : `$${usd.toFixed(4)}`;\n}\n\n/** Worst-case cost for pre-flight, or undefined when unpriced (token caps apply). */\nexport function worstCaseOf(\n inputTokens: number,\n maxOutput: number,\n price: PriceConfig | undefined,\n): number | undefined {\n if (!price) return undefined;\n const M = 1_000_000;\n const inRate = Math.max(price.input, price.cache_write_5m, price.cache_write_1h);\n return (inputTokens * inRate + maxOutput * price.output) / M;\n}\n"],"mappings":"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","names":["readdir","join","isAbsolute","join","isAbsolute","join","join","readdir"]}
@@ -5077,6 +5077,138 @@ function sortKeys(v) {
5077
5077
  return v;
5078
5078
  }
5079
5079
 
5080
+ // src/telemetry/narrate.ts
5081
+ var s = (v) => typeof v === "string" ? v : "";
5082
+ function host(url) {
5083
+ try {
5084
+ return new URL(url).host.replace(/^www\./, "") || url;
5085
+ } catch {
5086
+ return url;
5087
+ }
5088
+ }
5089
+ var clip3 = (t, n2 = 70) => t.length > n2 ? `${t.slice(0, n2 - 1)}\u2026` : t;
5090
+ var basename4 = (p) => p.split("/").pop() ?? p;
5091
+ var IMAGE = /\.(png|jpe?g|gif|webp|bmp|svg)$/i;
5092
+ function describeCommand(cmd) {
5093
+ const c = cmd.trim();
5094
+ const rules = [
5095
+ [
5096
+ /\b(vitest|jest|pytest|mocha|go test|cargo test|node --test|npm (run )?test|pnpm (run )?test|yarn test|bun test)\b/,
5097
+ "running test suite"
5098
+ ],
5099
+ [/\b(tsc|mypy|pyright)\b|\btypecheck\b/, "checking types"],
5100
+ [
5101
+ /\b(eslint|ruff|flake8|clippy|golangci-lint)\b|\b(npm|pnpm|yarn) (run )?lint\b/,
5102
+ "checking codebase for errors"
5103
+ ],
5104
+ [/\b(prettier|black|gofmt|rustfmt)\b|\bformat\b/, "formatting code"],
5105
+ [
5106
+ /\b(npm (ci|install|i)|pnpm (i|install)|yarn( install)?$|pip install|uv (sync|add)|cargo fetch|go mod download)\b/,
5107
+ "installing dependencies"
5108
+ ],
5109
+ [
5110
+ /\b(npm|pnpm|yarn) (run )?build\b|\b(tsup|vite build|webpack|cargo build|go build|make)\b/,
5111
+ "building the project"
5112
+ ],
5113
+ [/\bgit (diff|status|log|show)\b/, "reviewing changes"],
5114
+ [/^(ls|tree|find)\b/, "looking around the codebase"],
5115
+ [/^(cat|head|tail|less|sed -n)\b/, "reading files"],
5116
+ [/^(rg|grep)\b/, "searching the codebase"],
5117
+ [/\b(npm|pnpm|yarn) (run )?(dev|start)\b|\bnode .*server/, "starting the app"],
5118
+ [/^curl\b|^wget\b/, "making a web request"]
5119
+ ];
5120
+ for (const [re, words] of rules) if (re.test(c)) return words;
5121
+ return `running \`${clip3(c, 60)}\``;
5122
+ }
5123
+ function narrateTool(name, input) {
5124
+ const path = s(input.path);
5125
+ switch (name) {
5126
+ case "read":
5127
+ return IMAGE.test(path) ? `analyzing image ${path}` : `reading ${path}`;
5128
+ case "outline":
5129
+ return `skimming the structure of ${path}`;
5130
+ case "search": {
5131
+ const pattern = s(input.pattern).trim();
5132
+ return pattern ? `searching the codebase for ${clip3(pattern, 50)}` : "listing the files";
5133
+ }
5134
+ case "list":
5135
+ return `listing ${path || "the project"}`;
5136
+ case "write_file":
5137
+ return `writing code to ${path}`;
5138
+ case "str_replace":
5139
+ case "multi_edit":
5140
+ return `editing ${path}`;
5141
+ case "bash":
5142
+ return describeCommand(s(input.command));
5143
+ case "read_log":
5144
+ return "reading the full command output";
5145
+ case "remember":
5146
+ return `remembering: ${clip3(s(input.text), 60)}`;
5147
+ case "recall":
5148
+ return `recalling earlier work about ${clip3(s(input.query), 50)}`;
5149
+ case "task":
5150
+ return `asking a helper: ${clip3(s(input.description), 60)}`;
5151
+ case "todo":
5152
+ return "updating the todo list";
5153
+ case "semantic_search":
5154
+ return `looking through the codebase for ${clip3(s(input.query), 50)}`;
5155
+ case "web_search":
5156
+ return `searching the web for ${clip3(s(input.query), 50)}`;
5157
+ case "web_fetch":
5158
+ return `reading ${host(s(input.url))}`;
5159
+ case "skill":
5160
+ return `using the ${s(input.name) || "requested"} skill`;
5161
+ case "mcp_search":
5162
+ return `looking for a tool: ${clip3(s(input.query), 50)}`;
5163
+ case "mcp_call":
5164
+ return `using ${s(input.tool) || s(input.name) || "an external tool"}`;
5165
+ case "write_plan":
5166
+ return "writing the plan";
5167
+ case "write_intent":
5168
+ return "working out what you want built";
5169
+ case "browser": {
5170
+ const a = s(input.action);
5171
+ if (a === "open") return `browsing ${host(s(input.url))}`;
5172
+ if (a === "click") return `clicking on ${s(input.ref)}`;
5173
+ if (a === "type") return `typing into ${s(input.ref)}`;
5174
+ if (a === "press") return `pressing ${s(input.key)}`;
5175
+ if (a === "scroll") return `scrolling ${s(input.direction) || "down"}`;
5176
+ if (a === "screenshot") return "taking a screenshot to look at the page";
5177
+ if (a === "snapshot") return "reading the page";
5178
+ if (a === "console") return "checking the browser console for errors";
5179
+ if (a === "close") return "closing the browser";
5180
+ if (a === "fill") return `filling in ${s(input.ref)}`;
5181
+ if (a === "hover") return `hovering over ${s(input.ref)}`;
5182
+ if (a === "select") return `choosing '${clip3(s(input.value), 40)}' in ${s(input.ref)}`;
5183
+ if (a === "check") return `ticking ${s(input.ref)}`;
5184
+ if (a === "uncheck") return `unticking ${s(input.ref)}`;
5185
+ if (a === "upload") return `uploading ${basename4(s(input.path))}`;
5186
+ if (a === "wait_for")
5187
+ return `waiting for ${s(input.ref) || `"${clip3(s(input.text), 50)}"`} to appear`;
5188
+ if (a === "get_text") return `reading the text of ${s(input.ref)}`;
5189
+ if (a === "get_url") return "checking which page the browser is on";
5190
+ if (a === "eval") return "running a script in the page";
5191
+ if (a === "network") return "checking the network for failed requests";
5192
+ if (a === "tabs") return "listing the open tabs";
5193
+ if (a === "switch_tab") return `switching to tab ${s(input.tab)}`;
5194
+ if (a === "new_tab") return `opening ${host(s(input.url))} in a new tab`;
5195
+ if (a === "close_tab")
5196
+ return s(input.tab) ? `closing tab ${s(input.tab)}` : "closing the tab";
5197
+ return `using the browser (${a})`;
5198
+ }
5199
+ default:
5200
+ return `using ${name}`;
5201
+ }
5202
+ }
5203
+ var RESTATES = /^(?:the\s+)?user(?:'s)?\b|^(?:we|i)\s+(?:need|have|want|should|must)\s+to\b|^(?:this|that)\s+is\s+vague\b|^(?:likely|probably|perhaps|maybe)\b|^per\b|^request\b/i;
5204
+ function thinkingGist(text, previous = "", max = 200) {
5205
+ const sentences = text.replace(/\s+/g, " ").split(/(?<=[.!?])\s+/).map((x) => x.trim()).filter(Boolean);
5206
+ const fresh = sentences.filter((x) => !RESTATES.test(x) && !/"[^"]{20,}"/.test(x));
5207
+ const picked = (fresh.length ? fresh : sentences).slice(-2).join(" ");
5208
+ const gist2 = picked.length > max ? `\u2026${picked.slice(-(max - 1))}` : picked;
5209
+ return gist2 && gist2 !== previous ? gist2 : "";
5210
+ }
5211
+
5080
5212
  // src/agent/compaction.ts
5081
5213
  import { z as z23 } from "zod";
5082
5214
  var MIN_CLEAR_TOKENS = 4e3;
@@ -5107,11 +5239,11 @@ function contextTokens(messages) {
5107
5239
  var LOG_ID = /^\[[^\]\n]*\blog ([\w.-]+)\]/;
5108
5240
  function clearedStub(content, call) {
5109
5241
  const log = LOG_ID.exec(content)?.[1];
5110
- const what = call ? `${call.name} ${clip3(JSON.stringify(call.input), 160)}` : "a tool call";
5242
+ const what = call ? `${call.name} ${clip4(JSON.stringify(call.input), 160)}` : "a tool call";
5111
5243
  const back = log ? `read_log id="${log}" has it all` : "repeat the call if you still need it";
5112
5244
  return `${CLEARED_PREFIX} ${content.length} chars of old output from ${what}; ${back}]`;
5113
5245
  }
5114
- var clip3 = (s2, n2) => s2.length > n2 ? `${s2.slice(0, n2)}\u2026` : s2;
5246
+ var clip4 = (s2, n2) => s2.length > n2 ? `${s2.slice(0, n2)}\u2026` : s2;
5115
5247
  function clearOldToolResults(messages, keep, minChars = 0) {
5116
5248
  const positions = [];
5117
5249
  const calls = /* @__PURE__ */ new Map();
@@ -5165,7 +5297,7 @@ function supersededBy(call, isError, later) {
5165
5297
  if (call.name === "bash" && isError && typeof command === "string" && later.some(
5166
5298
  (l) => l.call.name === "bash" && !l.isError && field(l.call.input, "command") === command
5167
5299
  ))
5168
- return `\`${clip3(command, 120)}\` failed here and passed later; the failure is resolved`;
5300
+ return `\`${clip4(command, 120)}\` failed here and passed later; the failure is resolved`;
5169
5301
  return void 0;
5170
5302
  }
5171
5303
  function pruneSuperseded(messages) {
@@ -5258,7 +5390,7 @@ function factSummary(head) {
5258
5390
  let lastText = "";
5259
5391
  for (const m of head)
5260
5392
  for (const b of m.content) {
5261
- if (b.type === "tool_use") calls.push(`${b.name} ${clip3(JSON.stringify(b.input), 200)}`);
5393
+ if (b.type === "tool_use") calls.push(`${b.name} ${clip4(JSON.stringify(b.input), 200)}`);
5262
5394
  else if (b.type === "text" && m.role === "assistant" && b.text.trim()) lastText = b.text;
5263
5395
  }
5264
5396
  return {
@@ -5590,126 +5722,6 @@ async function withRetry(fn, hooks) {
5590
5722
  }
5591
5723
  }
5592
5724
 
5593
- // src/telemetry/narrate.ts
5594
- var s = (v) => typeof v === "string" ? v : "";
5595
- function host(url) {
5596
- try {
5597
- return new URL(url).host.replace(/^www\./, "") || url;
5598
- } catch {
5599
- return url;
5600
- }
5601
- }
5602
- var clip4 = (t, n2 = 70) => t.length > n2 ? `${t.slice(0, n2 - 1)}\u2026` : t;
5603
- var basename4 = (p) => p.split("/").pop() ?? p;
5604
- var IMAGE = /\.(png|jpe?g|gif|webp|bmp|svg)$/i;
5605
- function describeCommand(cmd) {
5606
- const c = cmd.trim();
5607
- const rules = [
5608
- [
5609
- /\b(vitest|jest|pytest|mocha|go test|cargo test|node --test|npm (run )?test|pnpm (run )?test|yarn test|bun test)\b/,
5610
- "running test suite"
5611
- ],
5612
- [/\b(tsc|mypy|pyright)\b|\btypecheck\b/, "checking types"],
5613
- [
5614
- /\b(eslint|ruff|flake8|clippy|golangci-lint)\b|\b(npm|pnpm|yarn) (run )?lint\b/,
5615
- "checking codebase for errors"
5616
- ],
5617
- [/\b(prettier|black|gofmt|rustfmt)\b|\bformat\b/, "formatting code"],
5618
- [
5619
- /\b(npm (ci|install|i)|pnpm (i|install)|yarn( install)?$|pip install|uv (sync|add)|cargo fetch|go mod download)\b/,
5620
- "installing dependencies"
5621
- ],
5622
- [
5623
- /\b(npm|pnpm|yarn) (run )?build\b|\b(tsup|vite build|webpack|cargo build|go build|make)\b/,
5624
- "building the project"
5625
- ],
5626
- [/\bgit (diff|status|log|show)\b/, "reviewing changes"],
5627
- [/^(ls|tree|find)\b/, "looking around the codebase"],
5628
- [/^(cat|head|tail|less|sed -n)\b/, "reading files"],
5629
- [/^(rg|grep)\b/, "searching the codebase"],
5630
- [/\b(npm|pnpm|yarn) (run )?(dev|start)\b|\bnode .*server/, "starting the app"],
5631
- [/^curl\b|^wget\b/, "making a web request"]
5632
- ];
5633
- for (const [re, words] of rules) if (re.test(c)) return words;
5634
- return `running \`${clip4(c, 60)}\``;
5635
- }
5636
- function narrateTool(name, input) {
5637
- const path = s(input.path);
5638
- switch (name) {
5639
- case "read":
5640
- return IMAGE.test(path) ? `analyzing image ${path}` : `reading ${path}`;
5641
- case "outline":
5642
- return `skimming the structure of ${path}`;
5643
- case "search":
5644
- return `searching the codebase for ${clip4(s(input.pattern), 50)}`;
5645
- case "write_file":
5646
- return `writing code to ${path}`;
5647
- case "str_replace":
5648
- case "multi_edit":
5649
- return `editing ${path}`;
5650
- case "bash":
5651
- return describeCommand(s(input.command));
5652
- case "read_log":
5653
- return "reading the full command output";
5654
- case "remember":
5655
- return `remembering: ${clip4(s(input.text), 60)}`;
5656
- case "recall":
5657
- return `recalling earlier work about ${clip4(s(input.query), 50)}`;
5658
- case "task":
5659
- return `asking a helper: ${clip4(s(input.description), 60)}`;
5660
- case "todo":
5661
- return "updating the todo list";
5662
- case "semantic_search":
5663
- return `looking through the codebase for ${clip4(s(input.query), 50)}`;
5664
- case "web_search":
5665
- return `searching the web for ${clip4(s(input.query), 50)}`;
5666
- case "web_fetch":
5667
- return `reading ${host(s(input.url))}`;
5668
- case "skill":
5669
- return `using the ${s(input.name) || "requested"} skill`;
5670
- case "mcp_search":
5671
- return `looking for a tool: ${clip4(s(input.query), 50)}`;
5672
- case "mcp_call":
5673
- return `using ${s(input.tool) || s(input.name) || "an external tool"}`;
5674
- case "write_plan":
5675
- return "writing the plan";
5676
- case "write_intent":
5677
- return "working out what you want built";
5678
- case "browser": {
5679
- const a = s(input.action);
5680
- if (a === "open") return `browsing ${host(s(input.url))}`;
5681
- if (a === "click") return `clicking on ${s(input.ref)}`;
5682
- if (a === "type") return `typing into ${s(input.ref)}`;
5683
- if (a === "press") return `pressing ${s(input.key)}`;
5684
- if (a === "scroll") return `scrolling ${s(input.direction) || "down"}`;
5685
- if (a === "screenshot") return "taking a screenshot to look at the page";
5686
- if (a === "snapshot") return "reading the page";
5687
- if (a === "console") return "checking the browser console for errors";
5688
- if (a === "close") return "closing the browser";
5689
- if (a === "fill") return `filling in ${s(input.ref)}`;
5690
- if (a === "hover") return `hovering over ${s(input.ref)}`;
5691
- if (a === "select") return `choosing '${clip4(s(input.value), 40)}' in ${s(input.ref)}`;
5692
- if (a === "check") return `ticking ${s(input.ref)}`;
5693
- if (a === "uncheck") return `unticking ${s(input.ref)}`;
5694
- if (a === "upload") return `uploading ${basename4(s(input.path))}`;
5695
- if (a === "wait_for")
5696
- return `waiting for ${s(input.ref) || `"${clip4(s(input.text), 50)}"`} to appear`;
5697
- if (a === "get_text") return `reading the text of ${s(input.ref)}`;
5698
- if (a === "get_url") return "checking which page the browser is on";
5699
- if (a === "eval") return "running a script in the page";
5700
- if (a === "network") return "checking the network for failed requests";
5701
- if (a === "tabs") return "listing the open tabs";
5702
- if (a === "switch_tab") return `switching to tab ${s(input.tab)}`;
5703
- if (a === "new_tab") return `opening ${host(s(input.url))} in a new tab`;
5704
- if (a === "close_tab")
5705
- return s(input.tab) ? `closing tab ${s(input.tab)}` : "closing the tab";
5706
- return `using the browser (${a})`;
5707
- }
5708
- default:
5709
- return `using ${name}`;
5710
- }
5711
- }
5712
-
5713
5725
  // src/providers/sticky.ts
5714
5726
  import { randomInt } from "crypto";
5715
5727
  import { mkdir as mkdir8, readFile as readFile14, writeFile as writeFile8 } from "fs/promises";
@@ -6259,8 +6271,9 @@ async function runAgentLoop(ctx, deps) {
6259
6271
  const toolStart = deps.clock.now();
6260
6272
  deps.events.emit("tool.start", {
6261
6273
  tool: call.name,
6274
+ // An invented name (print_tree) is narrated as the tool that actually runs (list).
6262
6275
  doing: narrateTool(
6263
- call.name,
6276
+ tool?.name ?? call.name,
6264
6277
  typeof call.input === "object" && call.input !== null ? call.input : {}
6265
6278
  )
6266
6279
  });
@@ -7898,6 +7911,7 @@ export {
7898
7911
  runLimitHit,
7899
7912
  inWrapup,
7900
7913
  shouldWarn,
7914
+ thinkingGist,
7901
7915
  isQuotaError,
7902
7916
  shouldFailover,
7903
7917
  totalInput,
@@ -7932,4 +7946,4 @@ export {
7932
7946
  cacheHitRate,
7933
7947
  tokensPerCommit
7934
7948
  };
7935
- //# sourceMappingURL=chunk-AGALUEDT.js.map
7949
+ //# sourceMappingURL=chunk-CP53ICXW.js.map