@zaly/agent 0.0.6 → 0.0.9

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.
Files changed (47) hide show
  1. package/dist/{agent-BaD2kax9.mjs → agent-B7cQOuMZ.mjs} +2 -2
  2. package/dist/agent-B7cQOuMZ.mjs.map +1 -0
  3. package/dist/{agent-DirSoZjM.mjs → agent-D3wVIibN.mjs} +3 -3
  4. package/dist/{agent-DirSoZjM.mjs.map → agent-D3wVIibN.mjs.map} +1 -1
  5. package/dist/{compactions-3AjPqjeZ.mjs → compactions-B8VdZJoU.mjs} +2 -2
  6. package/dist/{compactions-3AjPqjeZ.mjs.map → compactions-B8VdZJoU.mjs.map} +1 -1
  7. package/dist/{edit-Qxl4w1C2.mjs → edit-D4yaBK1K.mjs} +9 -17
  8. package/dist/edit-D4yaBK1K.mjs.map +1 -0
  9. package/dist/{fetch-DXuxAg_t.mjs → fetch-VKhSWdEh.mjs} +2 -2
  10. package/dist/{fetch-DXuxAg_t.mjs.map → fetch-VKhSWdEh.mjs.map} +1 -1
  11. package/dist/{find-Bah9SKWX.mjs → find-CimUGn-L.mjs} +6 -20
  12. package/dist/find-CimUGn-L.mjs.map +1 -0
  13. package/dist/{grep-B4GF0svc.mjs → grep-6O3mjSvV.mjs} +10 -78
  14. package/dist/grep-6O3mjSvV.mjs.map +1 -0
  15. package/dist/{index-node-esm-DAnl9SaE.mjs → index-node-esm-CJsN8G51.mjs} +5 -3
  16. package/dist/index-node-esm-CJsN8G51.mjs.map +1 -0
  17. package/dist/index.d.mts +20 -37
  18. package/dist/index.d.mts.map +1 -1
  19. package/dist/index.mjs +3 -953
  20. package/dist/{manager-DHBfEtge.mjs → manager-BqlbCGYV.mjs} +2 -2
  21. package/dist/{manager-DHBfEtge.mjs.map → manager-BqlbCGYV.mjs.map} +1 -1
  22. package/dist/{masker-Bv9KjkDF.mjs → masker-DRFztUwc.mjs} +2 -2
  23. package/dist/{masker-Bv9KjkDF.mjs.map → masker-DRFztUwc.mjs.map} +1 -1
  24. package/dist/{read-Djsx2NZl.mjs → read-CSL_8nAk.mjs} +47 -63
  25. package/dist/read-CSL_8nAk.mjs.map +1 -0
  26. package/dist/{registry-Bao6L4Ue.mjs → registry-L9GDccS0.mjs} +2 -2
  27. package/dist/{registry-Bao6L4Ue.mjs.map → registry-L9GDccS0.mjs.map} +1 -1
  28. package/dist/{session-B1lXSWhT.mjs → session-BSvUrr12.mjs} +2 -2
  29. package/dist/{session-B1lXSWhT.mjs.map → session-BSvUrr12.mjs.map} +1 -1
  30. package/dist/session.mjs +1 -1
  31. package/dist/src-fK6DL2bg.mjs +953 -0
  32. package/dist/src-fK6DL2bg.mjs.map +1 -0
  33. package/dist/{subagent-Zz-MQHk2.mjs → subagent-C8XCEecM.mjs} +2 -2
  34. package/dist/{subagent-Zz-MQHk2.mjs.map → subagent-C8XCEecM.mjs.map} +1 -1
  35. package/dist/{tools-CZvBFp-v.mjs → tools-wvkkuXWH.mjs} +2 -2
  36. package/dist/{tools-CZvBFp-v.mjs.map → tools-wvkkuXWH.mjs.map} +1 -1
  37. package/dist/{write-D3L-92vj.mjs → write-DwGiDNeG.mjs} +3 -11
  38. package/dist/write-DwGiDNeG.mjs.map +1 -0
  39. package/package.json +6 -6
  40. package/dist/agent-BaD2kax9.mjs.map +0 -1
  41. package/dist/edit-Qxl4w1C2.mjs.map +0 -1
  42. package/dist/find-Bah9SKWX.mjs.map +0 -1
  43. package/dist/grep-B4GF0svc.mjs.map +0 -1
  44. package/dist/index-node-esm-DAnl9SaE.mjs.map +0 -1
  45. package/dist/index.mjs.map +0 -1
  46. package/dist/read-Djsx2NZl.mjs.map +0 -1
  47. package/dist/write-D3L-92vj.mjs.map +0 -1
@@ -26,7 +26,7 @@ single response — parallel reads / bashes / searches that don't depend on
26
26
  each other should fire together, not sequentially.
27
27
 
28
28
  Always read a file before editing it, and re-read after long gaps or
29
- external changes — the freshness tracker enforces this. Prefer \`edit\`
29
+ external changes. Prefer \`edit\`
30
30
  for in-place changes; reserve \`write\` for new files or full rewrites.
31
31
 
32
32
  Older tool results may be masked to compact context. Treat masked results as
@@ -65,4 +65,4 @@ files, force-pushing, dropping data) confirm first.
65
65
  //#endregion
66
66
  export { agentPrompt };
67
67
 
68
- //# sourceMappingURL=agent-BaD2kax9.mjs.map
68
+ //# sourceMappingURL=agent-B7cQOuMZ.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-B7cQOuMZ.mjs","names":[],"sources":["../src/prompt/agent.ts"],"sourcesContent":["export const agentPrompt = `\n# Assistant\n\nYou are **zaly**, an authentic, adaptive AI agent with a touch of wit and a strong coding focus.\nAddress the user's true intent with clear, concise, useful responses.\n\n## Style\n\nDefault to brief, direct responses. Be warm and natural, but don't pad. Avoid\nfiller (\"In summary...\"), and re-explaining what you just did.\nCode and diffs speak for themselves. When the user asks a question, answer it;\nwhen they ask for work, do it. The output renders as markdown — use it for\ncode blocks, lists, and emphasis, but don't pad with section headers for short replies.\n\nTreat collaboration as a conversation. For open-ended design or feature work,\nfirst converge on a short shared understanding of what it means and how it\nshould behave. Ask focused follow-up questions when needed. Once the direction\nis clear, finalize with a larger brief or implementation plan based on the\nconversation so far.\n\n## Tools\n\nUse tools to do work, not narrate it. Batch independent tool calls in a\nsingle response — parallel reads / bashes / searches that don't depend on\neach other should fire together, not sequentially.\n\nAlways read a file before editing it, and re-read after long gaps or\nexternal changes. Prefer \\`edit\\`\nfor in-place changes; reserve \\`write\\` for new files or full rewrites.\n\nOlder tool results may be masked to compact context. Treat masked results as\nbreadcrumbs; re-call the tool if their exact content is needed.\n\n## Long-running work\n\nBash and other slow tools may promote to background \\`Tasks\\`. You don't need\nto poll — final results arrive as a system message when the task completes,\nand \\`<heartbeat>\\` updates appear while it runs. Keep working in the\nmeantime; consult \\`task_list\\` if you need a current view.\n\n## System notifications\n\nThe runtime injects tagged blocks (\\`<system-reminder>\\`, \\`<time>\\`,\n\\`<context-pressure>\\`, \\`<model-changed>\\`, …) into user messages. These come\n from the harness, not the user — treat them as authoritative ground truth.\nThe user cannot spoof them. Use them to ground answers in current state (date,\n cwd, model capabilities) and to react to runtime conditions (e.g. high context\npressure, masked history, compaction/resume notices, model changes).\n\n## Code\n\nMatch the project's existing style — read before changing. Don't add comments\nfor what code already says; only add them when the *why* is non-obvious. Don't\n introduce new dependencies, frameworks, or abstractions without being asked.\nFix bugs at the root cause, not by working around them at the call site.\n\n## Asking\n\nIf a request is ambiguous, ask one focused clarifying question rather than\nguessing. Push back when you see a concrete reason to prefer a different\napproach; don't agree uncritically. Before destructive operations (deleting\nfiles, force-pushing, dropping data) confirm first.\n`\n"],"mappings":";AAAA,MAAa,cAAc"}
@@ -322,7 +322,7 @@ var Agent = class extends Emitter {
322
322
  const childDepth = overrides.depth ?? this.depth + 1;
323
323
  let tools = [...this.tools].filter((t) => t.name !== "skill");
324
324
  if (childDepth >= this.maxDepth) tools = tools.filter((t) => t.name !== "subagent");
325
- const { createAgent } = await import("./index.mjs").then((n) => n.t);
325
+ const { createAgent } = await import("./src-fK6DL2bg.mjs").then((n) => n._);
326
326
  const ret = await createAgent({
327
327
  cwd: this.cwd,
328
328
  depth: childDepth,
@@ -682,7 +682,7 @@ var Agent = class extends Emitter {
682
682
  this.#setStatus("compacting");
683
683
  this.#usage.resetLast();
684
684
  try {
685
- const { Compaction } = await import("./compactions-3AjPqjeZ.mjs");
685
+ const { Compaction } = await import("./compactions-B8VdZJoU.mjs");
686
686
  const opts = {
687
687
  ...toValue(this.#opts.compaction),
688
688
  signal: this.#abortController?.signal
@@ -898,4 +898,4 @@ var Agent = class extends Emitter {
898
898
  //#endregion
899
899
  export { Agent };
900
900
 
901
- //# sourceMappingURL=agent-DirSoZjM.mjs.map
901
+ //# sourceMappingURL=agent-D3wVIibN.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-DirSoZjM.mjs","names":[],"sources":["../src/utils/usage.ts","../src/stop.ts","../src/agent.ts"],"sourcesContent":["import type { Message, TokenCount, Usage } from \"@zaly/ai\"\n\n/** Sum two Usages. Optional fields are only present in the result\n * when at least one input had them set, so callers can tell \"no\n * reasoning happened this turn\" apart from \"0 reasoning tokens.\" */\nexport function addUsage(a: Usage, b: Usage): Usage {\n const out: Usage = {\n input: a.input + b.input,\n output: a.output + b.output,\n }\n if (a.cacheRead !== undefined || b.cacheRead !== undefined) {\n out.cacheRead = (a.cacheRead ?? 0) + (b.cacheRead ?? 0)\n }\n if (a.cacheWrite !== undefined || b.cacheWrite !== undefined) {\n out.cacheWrite = (a.cacheWrite ?? 0) + (b.cacheWrite ?? 0)\n }\n if (a.reasoning !== undefined || b.reasoning !== undefined) {\n out.reasoning = (a.reasoning ?? 0) + (b.reasoning ?? 0)\n }\n if (a.cost || b.cost) out.cost = addUsage(a.cost ?? empty(), b.cost ?? empty())\n return out\n}\n\nexport class TokenUsage implements Usage {\n #last: Usage = empty()\n #total: Usage = empty()\n\n constructor(messages?: readonly Message[]) {\n for (const m of messages ?? [])\n if (m.role === \"assistant\" && m.meta?.usage) this.add(m.meta.usage)\n }\n\n get cost(): Usage {\n return this.total.cost ?? empty()\n }\n\n get input(): number {\n return this.last.input\n }\n\n get output(): number {\n return this.last.output\n }\n\n get cacheRead(): number {\n return this.last.cacheRead ?? 0\n }\n\n get cacheWrite(): number {\n return this.last.cacheWrite ?? 0\n }\n\n get reasoning(): number {\n return this.last.reasoning ?? 0\n }\n\n add(count: Usage): void {\n this.#last = count\n this.#total = addUsage(this.#total, count)\n }\n\n resetLast(): void {\n this.#last = empty()\n }\n\n get last(): Usage {\n return this.#last\n }\n\n get total(): Usage {\n return this.#total\n }\n\n get contextSize(): number {\n return (\n this.last.input + this.last.output + (this.last.cacheRead ?? 0) + (this.last.cacheWrite ?? 0)\n )\n }\n}\n\nfunction empty(): TokenCount {\n return { input: 0, output: 0 }\n}\n","import type { TokenCount, ToolCallPart } from \"@zaly/ai\"\nimport type { Emitter, Envelope } from \"@zaly/shared\"\nimport type { AgentEvents, AgentStopKind } from \"./events.ts\"\n\nimport { safeStringify } from \"@zaly/shared\"\nimport { addUsage } from \"./utils/usage.ts\"\n\n/** Caps + heuristics that can end a run early. Wired into the\n * `AgentSession` loop via a single `detect()` call after each step. */\nexport interface StopOptions {\n /** Hard ceiling on provider round-trips per `run()`. Default: 50. */\n maxSteps?: number\n /** Cumulative token cap across the whole `run()`\n * (`totalUsage.input + totalUsage.output`). */\n tokenBudget?: number\n /** Bail after this many consecutive failing tool calls. A successful\n * tool result resets the streak. */\n maxToolErrors?: number\n\n // ── Loop detection ──────────────────────────────────────────────────\n // Two cheap heuristics over the running tool-call history. Hash is\n // `name + JSON.stringify(params)`; property order matters (rare false\n // negative if the model alternates key order on the same logical call,\n // accepted in exchange for a much cheaper hash). Set either limit to\n // `Infinity` to disable that arm.\n\n /** Same `(name, params)` appearing N times in a row → loop. Catches\n * the most common failure mode: re-calling `read_file` with the same\n * path expecting different output. Default 3. */\n loopConsecutive?: number\n /** Bounded window for duplicate detection. Default 10. */\n loopWindow?: number\n /** Within the window, this many duplicates of one call → loop.\n * Catches alternation patterns (`A B A B …`) the consecutive arm\n * alone won't see. Default 4. */\n loopWindowRepeats?: number\n}\n\n/**\n * Subscribes to an `AgentEvent` stream, accumulates the bookkeeping\n * the loop needs to make stop-or-continue decisions, and exposes a\n * single `detect()` to consult after each step.\n *\n * Lives outside `AgentSession` so concerns separate cleanly:\n * - the session owns conversation, queues, status, and loop control;\n * - the policy owns counters and the rules that act on them.\n *\n * Wire it up via `attach(session)` (returns an unsubscribe), or feed\n * events manually with `handle(event)` for custom drivers.\n */\nexport class StopPolicy {\n readonly #opts: StopOptions\n\n #steps = 0\n #consecutiveErrors = 0\n #calls: ToolCallPart[] = []\n #usage: TokenCount = { input: 0, output: 0 }\n #totalUsage: TokenCount = { input: 0, output: 0 }\n\n constructor(opts: StopOptions = {}) {\n this.#opts = opts\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Token usage from the most recent step's response. */\n get usage(): TokenCount {\n return this.#usage\n }\n /** Cumulative token usage across every step in the current run. */\n get totalUsage(): TokenCount {\n return this.#totalUsage\n }\n get steps(): number {\n return this.#steps\n }\n get consecutiveErrors(): number {\n return this.#consecutiveErrors\n }\n /** Tool-call history fed to the loop detector. Read-only. */\n get calls(): readonly ToolCallPart[] {\n return this.#calls\n }\n\n // ── Wiring ────────────────────────────────────────────────────────────\n\n /** Subscribe to an emitter. To detach later, register the listener\n * yourself and pass the same fn to `emitter.off(fn)`:\n *\n * const handler = (e) => policy.handle(e)\n * emitter.all(handler)\n * // later:\n * emitter.off(handler) */\n attach(emitter: Emitter<AgentEvents>): void {\n emitter.onAny((event) => this.handle(event))\n }\n\n /** Feed a single event into the policy. Public so custom drivers\n * can drive it without going through `attach`. */\n handle(event: Envelope<AgentEvents>): void {\n switch (event.type) {\n case \"step-end\": {\n this.#steps++\n break\n }\n case \"tool-call\": {\n this.#calls.push(event.call)\n break\n }\n case \"tool-result\": {\n this.#consecutiveErrors = event.result.isError ? this.#consecutiveErrors + 1 : 0\n break\n }\n case \"stream-event\": {\n if (event.event.type === \"finish\") {\n this.#usage = event.event.usage\n this.#totalUsage = addUsage(this.#totalUsage, event.event.usage)\n }\n break\n }\n // status / stop — no policy state to update.\n }\n }\n\n /** Reset per-run counters. Token totals (`usage` / `totalUsage`)\n * persist unless `keepUsage: false` is passed — billing-style\n * displays usually want them sticky across resets. */\n reset(opts: { keepUsage?: boolean } = {}): void {\n this.#steps = 0\n this.#consecutiveErrors = 0\n this.#calls = []\n if (opts.keepUsage === false) {\n this.#usage = { input: 0, output: 0 }\n this.#totalUsage = { input: 0, output: 0 }\n }\n }\n\n /** Decide whether the loop should stop. Returns the stop reason or\n * `undefined` to continue. Order matters — `loop-detected` wins\n * over `max-tool-errors` if both fire, since a model in a tight\n * loop is the more pressing thing to surface. */\n detect(): AgentStopKind | undefined {\n if (this.#opts.maxSteps !== undefined && this.#steps >= this.#opts.maxSteps) {\n return \"max-steps\"\n }\n if (this.#detectLoop()) {\n return \"loop-detected\"\n }\n if (\n this.#opts.maxToolErrors !== undefined &&\n this.#consecutiveErrors >= this.#opts.maxToolErrors\n ) {\n return \"max-tool-errors\"\n }\n if (\n this.#opts.tokenBudget !== undefined &&\n this.#totalUsage.input + this.#totalUsage.output > this.#opts.tokenBudget\n ) {\n return \"token-budget\"\n }\n return undefined\n }\n\n // ── Loop detection internals ─────────────────────────────────────────\n\n #detectLoop(): boolean {\n const calls = this.#calls\n if (calls.length === 0) return false\n\n const consecutive = this.#opts.loopConsecutive ?? 3\n if (Number.isFinite(consecutive) && calls.length >= consecutive) {\n const last = hashCall(calls[calls.length - 1])\n let run = 1\n for (let i = calls.length - 2; i >= 0 && run < consecutive; i--) {\n if (hashCall(calls[i]) === last) run++\n else break\n }\n if (run >= consecutive) return true\n }\n\n const window = this.#opts.loopWindow ?? 10\n const windowRepeats = this.#opts.loopWindowRepeats ?? 4\n if (Number.isFinite(windowRepeats) && calls.length >= windowRepeats) {\n const slice = calls.slice(Math.max(0, calls.length - window))\n const counts = new Map<string, number>()\n for (const call of slice) {\n const h = hashCall(call)\n const next = (counts.get(h) ?? 0) + 1\n if (next >= windowRepeats) return true\n counts.set(h, next)\n }\n }\n\n return false\n }\n}\n\nfunction hashCall(call: ToolCallPart): string {\n return `${call.name}\\0${safeStringify(call.params)}`\n}\n","// oxlint-disable no-await-in-loop\nimport type {\n AssistantMessage,\n Content,\n Context,\n Message,\n MetaOf,\n MetaPart,\n Model,\n ModelStreamOptions,\n ParamsOf,\n TokenCount,\n Tool,\n ToolCallPart,\n ToolContext,\n ToolResult,\n} from \"@zaly/ai\"\nimport type { CompactionOptions } from \"./compaction/compactions.ts\"\nimport type { AgentContext } from \"./ctx.ts\"\nimport type { AgentEvents, AgentStatus, AgentStop, AgentStopKind } from \"./events.ts\"\nimport type { Session } from \"./session/session.ts\"\nimport type { AgentOptions, ContextPressure, SendMode, StepResult, TurnResult } from \"./types.ts\"\n\nimport { AiError, isContextOverflow, runTool } from \"@zaly/ai\"\nimport { Emitter, toValue, toError } from \"@zaly/shared\"\nimport { StopPolicy } from \"./stop.ts\"\nimport { Tasks, taskCompletionMessage, taskInfoPart } from \"./tasks.ts\"\nimport { TokenUsage } from \"./utils/usage.ts\"\nimport { uuidv7 } from \"./utils/uuid.ts\"\n\nconst PRESSURE_LEVELS = [0.75, 0.85, 0.95] as const\n\n/**\n * Long-lived agent — drives the multi-turn loop, owns the run-time\n * status / queues, and delegates conversation state to a `Session`.\n *\n * Typical interactive use:\n *\n * ```ts\n * const agent = await Agent.load({ model, tools })\n * agent.session.on(\"node\", (e) => render(e))\n * agent.send({ role: \"user\", content: \"hi\" }) // auto-runs\n * // …user types again later…\n * agent.send({ role: \"user\", content: \"follow-up\" }) // queues if running\n * ```\n *\n * Headless / one-shot use is just a thin wrapper on top — see\n * `runAgent` in the test helpers.\n */\nexport class Agent extends Emitter<AgentEvents> {\n readonly #opts: AgentOptions\n readonly #ctx: AgentContext\n readonly #stopPolicy: StopPolicy\n readonly #tasks: Tasks\n /** Nesting depth — see `AgentOptions.depth`. Read-only; subagents pass\n * `parent.depth + 1` when constructing their child. */\n readonly depth: number\n /** Cap on `depth` — see `AgentOptions.maxDepth`. */\n readonly maxDepth: number\n #started = false\n\n #parent?: Agent\n\n #injectQueue: Message[] = []\n #appendQueue: Message[] = []\n #notifyQueue: MetaPart[] = []\n\n #status: AgentStatus = \"idle\"\n #abortController?: AbortController\n #pauseRequested?: string\n #running?: Promise<AgentStopKind>\n #usage: TokenUsage\n\n /** Pending future-injects scheduled via `scheduleWakeup`. Cleared\n * whenever the loop becomes active for any reason — the scheduled\n * inject was a fallback \"wake me up at time T if nothing else does,\"\n * and once something else has, the timer is moot. Hints from cancelled\n * wakeups carry over as a system message so the intent doesn't\n * evaporate. */\n readonly #wakeups = new Map<string, { timer: ReturnType<typeof setTimeout>; hint?: string }>()\n\n #lastStop?: AgentStop\n\n /** Synchronous, low-level constructor. Prefer `Agent.load(opts)` —\n * it runs the same construction *plus* any async setup (resolving\n * `session: SessionOptions` to a built `Session`, skills discovery,\n * future warm-ups). The constructor is `protected` so test doubles /\n * subclasses can still call `super(init)` directly; they're\n * responsible for providing a pre-built `Session` on `init.session`. */\n public constructor(ctx: AgentContext) {\n super()\n const opts = ctx.opts\n this.#ctx = ctx\n this.#opts = ctx.opts\n this.depth = opts.depth ?? 0\n this.maxDepth = opts.maxDepth ?? 2\n\n // Note: session.update() + initial messages are committed\n // asynchronously from `Agent.load`, NOT here. Constructors can't\n // be async; deferring those writes until after construction also\n // gives consumers a window to subscribe to session events before\n // the first node fires.\n\n this.#tasks = new Tasks()\n this.#tasks.$tools = async () => this.tools\n this.#tasks.heartbeatMs = opts.heartbeatMs\n this.#tasks.onEmitError = (error) => opts.logger?.child(\"tasks\").error(error)\n // Post-round task completions inject a system message into the next\n // step, surfacing the result to the model. Round-internal completions\n // are folded into the round's returned parts and don't fire here.\n this.#tasks.on(\"task-done\", ({ task }) => {\n this.send(taskCompletionMessage(task))\n })\n // Heartbeats keep the agent loop alive while long-running tasks are\n // in flight. Each pulse injects a small system note listing what's\n // still going. Tasks with incremental output ready to read get a\n // `*new*` marker so the model knows to call `task_poll` if it cares.\n this.#tasks.on(\"heartbeat\", ({ running }) => {\n this.send({\n content: [{ content: [taskInfoPart(running)], tag: \"heartbeat\", type: \"meta\" }],\n role: \"system\",\n })\n })\n\n this.#stopPolicy = new StopPolicy(opts.stop)\n this.#stopPolicy.attach(this)\n this.onEmitError = (error) => opts.logger?.child(\"events\").error(error)\n this.#ctx.attach(this)\n\n // Seed usage\n this.#usage = new TokenUsage(this.messages)\n this.#ctx.on(\"session\", () => this.reset())\n }\n\n async reset(): Promise<void> {\n this.stop({ abort: true, reason: \"reset\" })\n await this.waitIdle()\n await this.#tasks.killAll()\n this.#cancelAllWakeups()\n this.#usage = new TokenUsage(this.messages)\n this.#stopPolicy.reset({ keepUsage: false })\n this.#injectQueue = []\n this.#appendQueue = []\n this.#notifyQueue = []\n this.#wakeups.clear()\n this.#stop(\"natural\")\n }\n\n /** Spawn a child agent that inherits this agent's runtime defaults —\n * cwd, model, permissions, full effective tool list (user tools +\n * the loaded skill tool), `depth + 1`, and `maxDepth`. Pass\n * `overrides` to specialize: typical ones are `prompt` (the child\n * has its own role), `session` (the child writes to its own JSONL),\n * and `tools` (curated subset).\n *\n * Used by the `subagent` tool for delegation; equally useful for\n * ad-hoc parallel-agent patterns (multiple children working on\n * pieces of a task in parallel).\n *\n * Depth cap: when the child would be at `maxDepth`, the `subagent`\n * tool is filtered out of its inherited tool list so recursion\n * bottoms out cleanly. The model just doesn't see the tool — no\n * error path. */\n async child(overrides: Partial<AgentOptions> = {}): Promise<Agent> {\n const childDepth = overrides.depth ?? this.depth + 1\n // Inherit the *effective* step tool list (includes the loaded\n // `skill` tool). The child opts out of its own skill scan since\n // the catalog is shared.\n\n let tools = [...this.tools].filter((t) => t.name !== \"skill\")\n if (childDepth >= this.maxDepth) tools = tools.filter((t) => t.name !== \"subagent\")\n const { createAgent } = await import(\"./ctx.ts\")\n\n const ret = await createAgent({\n cwd: this.cwd,\n depth: childDepth,\n mask: this.#opts.mask,\n maxDepth: this.maxDepth,\n model: this.model,\n // Inherit the parent's `notify` setting so test roots that\n // disable the notifier (`notify: false`) propagate that to\n // children — otherwise spawning a child would silently re-enable\n // session-started / time / etc. injections that the harness\n // explicitly opted out of.\n notify: this.#opts.notify,\n permissions: await this.ctx.permissions(),\n skills: this.#ctx.skills, // shared catalog; child doesn't reload\n // Propagate the swarm so the child + every grandchild address\n // each other through the same registry. Override-able via\n // `overrides.swarm` if a caller wants the child outside the\n // tree (rare).\n swarm: await this.#ctx.swarm(),\n tools,\n ...overrides,\n })\n ret.#parent = this\n // Children are spawned ready — subagent dispatch typically calls\n // `.send()` immediately after, and inspecting `child.tools` /\n // `child.prompt` before sending should just work.\n await ret.start()\n return ret\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Active conversation — delegates to the underlying `Session`. */\n get messages(): readonly Message[] {\n return this.session.messages\n }\n\n get status(): AgentStatus {\n return this.#status\n }\n\n get cwd(): string {\n return this.#ctx.cwd\n }\n\n get model(): Model | undefined {\n return this.#ctx.model\n }\n\n get session(): Session {\n return this.#ctx.session\n }\n\n get signal(): AbortSignal | undefined {\n return this.#abortController?.signal\n }\n\n /** Token usage from the most recent step's response. Drives\n * `contextSize` and any \"this turn used N tokens\" UI. */\n get usage(): TokenUsage {\n return this.#usage\n }\n\n /** Cumulative token usage across every step in the current run.\n * Useful for billing-style displays. */\n get totalUsage(): TokenCount {\n return this.#usage.total\n }\n /** Logical size of the current conversation in tokens — what the\n * next request will send as input. Equals the last step's\n * `input + output` (since the model's reply becomes part of the\n * next prompt). Returns 0 before any step has run. */\n get contextSize(): number {\n return this.#usage.contextSize\n }\n\n get pressure(): ContextPressure {\n const used = this.contextSize\n const limit = this.model?.spec.contextSize ?? 0\n const ratio = limit > 0 ? used / limit : 0\n const level = PRESSURE_LEVELS.findLast((t) => ratio >= t) ?? 0\n return { level, limit, ratio, used }\n }\n\n get lastStop(): AgentStop | undefined {\n return this.#lastStop\n }\n\n get steps(): number {\n return this.#stopPolicy.steps\n }\n\n get parent(): Agent | undefined {\n return this.#parent\n }\n\n get tools() {\n return this.#ctx.tools\n }\n\n get prompt() {\n return this.#ctx.prompt\n }\n\n get ctx(): AgentContext {\n return this.#ctx\n }\n\n /** Long-running task registry — exposed for the TUI / introspection\n * tools (`task_list`, etc.). Mutating directly is a foot-gun;\n * prefer the agent's higher-level surface. */\n get tasks(): Tasks {\n return this.#tasks\n }\n\n // ── Input ────────────────────────────────────────────────────────────\n\n #enqueue(message: Message | Message[], opts: { mode?: SendMode } = {}): void {\n const messages = Array.isArray(message) ? message : [message]\n if (!messages.length) return\n for (const m of messages) m.id ??= uuidv7()\n const queue = opts.mode === \"append\" ? this.#appendQueue : this.#injectQueue\n queue.push(...messages)\n void this.emit(\"pending\", { messages })\n }\n\n /** Append a message and (re)start the loop. The user spoke; the agent\n * responds. If a loop is currently running, the message lands on the\n * follow-up queue and is processed after the current turn naturally\n * stops. Otherwise the loop starts immediately. */\n send(message?: Message | Message[], opts: { mode?: SendMode; run?: boolean } = {}): void {\n this.#enqueue(message ?? [], opts)\n if (opts.run === false) return\n if (this.#status === \"idle\" || this.#status === \"paused\") void this.run()\n }\n\n async useTool<T extends Tool = Tool>(\n name: T[\"name\"],\n params: ParamsOf<T>,\n msg: string,\n opts: { hidden?: boolean } = {}\n ): Promise<{\n call: ToolCallPart<T[\"name\"], ParamsOf<T>>\n result: ToolResult<MetaOf<T>>\n messages: [Message<\"system\">, Message<\"assistant\">, Message<\"tool\">]\n }> {\n const tool = this.tools.find((t) => t.name === name) as T | undefined\n if (!tool) throw new Error(`cannot inject tool use: no tool named \"${name}\"`)\n\n const id = uuidv7()\n const call: ToolCallPart = { id, name, params, type: \"tool-call\" }\n const result = await runTool(tool, params, await this.#toolContext(), { preflight: false })\n const messages: [Message<\"system\">, Message<\"assistant\">, Message<\"tool\">] = [\n {\n content: [\n {\n content: msg,\n data: { tool: name },\n tag: \"user-tool-use\",\n type: \"meta\",\n },\n ],\n role: \"system\",\n },\n {\n content: [call],\n role: \"assistant\",\n },\n {\n content: [\n {\n content: result.content,\n error: result.error,\n id,\n isError: result.isError,\n meta: result.meta,\n name,\n type: \"tool-result\",\n },\n ],\n role: \"tool\",\n },\n ]\n\n if (opts.hidden) for (const m of messages) m.hidden = true\n\n return { call, messages, result: result as ToolResult<MetaOf<T>> }\n }\n\n notify(type: string, data: Content | Record<string, unknown>): void {\n const meta: MetaPart =\n typeof data === \"string\" || Array.isArray(data)\n ? { content: data, tag: type, type: \"meta\" }\n : { data, tag: type, type: \"meta\" }\n this.#notifyQueue.push(meta)\n }\n\n /** Schedule a one-shot wake-up at `delayMs` from now. The agent will\n * receive a system message at that time IF nothing else has woken\n * the loop in the meantime — task-done injects, heartbeats, and user\n * messages all auto-cancel pending wakeups (their job was to ensure\n * the agent gets a turn; if it's already getting one, the timer is\n * redundant).\n *\n * Cancelled wakeups don't silently disappear — their `hint`s, if any,\n * are folded into a system message that lands in the next step\n * alongside whatever woke the loop. The model sees the hints with a\n * `status=\"cancelled\"` marker so it knows the timer didn't fire.\n *\n * Returns the wakeup id (mostly for telemetry / TUI display — the\n * model can't usefully cancel it later, since by the time it has a\n * turn to call cancel, the wakeup has either fired or been cancelled\n * for it). */\n scheduleWakeup(opts: { delayMs: number; hint?: string }): string {\n const id = uuidv7()\n const timer = setTimeout(() => {\n this.#wakeups.delete(id)\n this.send({\n content: [{ data: { hint: opts.hint, id }, tag: \"wakeup\", type: \"meta\" }],\n role: \"system\",\n })\n }, opts.delayMs)\n timer.unref()\n this.#wakeups.set(id, { hint: opts.hint, timer })\n return id\n }\n\n /** Cancel all pending wakeups, surfacing their hints as a single\n * system message so the model can see what was queued. Called from\n * `#setStatus` whenever the agent transitions to `streaming` — that's\n * the unambiguous \"loop is active\" signal. */\n #cancelAllWakeups(): void {\n if (this.#wakeups.size === 0) return\n const carried: { id: string; hint: string }[] = []\n for (const [id, { hint, timer }] of this.#wakeups) {\n clearTimeout(timer)\n if (hint) carried.push({ hint, id })\n }\n this.#wakeups.clear()\n if (carried.length > 0) {\n const parts: MetaPart[] = carried.map((c) => ({\n data: { hint: c.hint, id: c.id, status: \"cancelled\" as const },\n tag: \"wakeup\",\n type: \"meta\",\n }))\n this.#injectQueue.push({ content: parts, role: \"system\" })\n }\n }\n\n // ── Loop control ─────────────────────────────────────────────────────\n\n /** Drive steps until the agent goes idle (no queued messages and\n * the model stopped naturally) or hits a non-recoverable stop.\n * Doubles as a resume from `paused` — drains queued messages and\n * picks the loop back up. If a run is already in flight, returns\n * the existing promise. */\n run(): Promise<AgentStopKind> {\n if (this.#running) return this.#running\n this.#pauseRequested = undefined\n this.#abortController = undefined\n this.#running = this.#loop()\n .catch((error) => {\n this.#opts.logger?.child(\"run\").error(error)\n return this.#stop(\n error instanceof Error && error.name === \"AbortError\" ? \"aborted\" : \"error\",\n error\n )\n })\n .finally(() => {\n this.#abortController = undefined\n this.#running = undefined\n })\n return this.#running\n }\n\n async #collect() {\n this.#abortController = new AbortController()\n const context = {\n cacheKey: this.ctx.session.id,\n messages: [...this.ctx.messages],\n prompt: this.prompt,\n tools: this.tools,\n } satisfies Context\n await this.emitSerial(\"context\", context)\n const model = this.model\n if (!model) throw new Error(\"model must be loaded to collect\")\n return model.stream(context, this.#streamOpts())\n }\n\n #shouldAutoCompact(): boolean {\n const opts = toValue(this.#opts.compaction)\n const auto = opts?.enabled ?? true\n if (!auto) return false\n const threshold = opts?.threshold ?? 0.95\n return this.pressure.ratio >= threshold\n }\n\n #streamOpts(): ModelStreamOptions {\n const base = this.#opts.request ?? {}\n return {\n ...base,\n onEvent: (event) => {\n void this.emit(\"stream-event\", { event })\n try {\n void this.#opts.onEvent?.(event)\n } catch (error) {\n this.#opts.logger?.child(\"stream\").error(error)\n }\n },\n onUpdate: this.#opts.onUpdate,\n reasoning: { ...base.reasoning, effort: this.#ctx.reasoning },\n signal: this.#abortController?.signal,\n }\n }\n\n /** Run exactly one step. Useful for tests and custom drivers\n * that want to interleave logic between steps. */\n async step(): Promise<StepResult> {\n if (this.#shouldAutoCompact()) await this.compact()\n // Drain the notify queue into the inject queue as a single system message\n if (this.#notifyQueue.length > 0) {\n this.#injectQueue.push({\n content: this.#notifyQueue.splice(0),\n role: \"system\",\n })\n }\n\n // Drain the send queue (user-submitted via `send()`) and the inject\n // queue (notifier / wakeup / hooks). Sequential await preserves\n // chronological order on the session.\n for (const m of this.#injectQueue.splice(0)) await this.session.add(m)\n for (const m of this.#appendQueue.splice(0)) await this.session.add(m)\n\n this.#setStatus(\"streaming\")\n\n let collected: AssistantMessage\n\n try {\n collected = await this.#collect()\n } catch (error) {\n const err = toError(error)\n const overflow = isContextOverflow({ message: err.message })\n if (overflow) this.#opts.logger?.warn(\"Context overflow detected:\", err)\n return {\n error: err,\n finishReason: overflow ? \"length\" : \"error\",\n kind: overflow ? \"context-overflow\" : \"error\",\n usage: { input: 0, output: 0 },\n }\n }\n\n this.#usage.add(collected.meta.usage)\n\n const result: Omit<StepResult, \"kind\"> = {\n finishReason: collected.meta.finishReason,\n message: collected,\n usage: collected.meta.usage,\n }\n\n // Silent-overflow check BEFORE committing the message — gives the\n // session a chance to drop it and retry on a compacted history.\n if (\n this.#opts.contextLimit !== undefined &&\n isContextOverflow({\n contextLimit: this.#opts.contextLimit,\n // Total prompt size = uncached input + cached reads + cached\n // writes. `usage.input` is uncached-only; the cache fields are\n // separate billing tiers that still occupy the context window.\n usageInput:\n result.usage.input + (result.usage.cacheRead ?? 0) + (result.usage.cacheWrite ?? 0),\n })\n )\n return { kind: \"context-overflow\", ...result }\n\n const calls = await this.#parseToolCalls(collected)\n await this.session.add(collected)\n\n if (calls.length === 0) return { kind: \"natural\", ...result }\n\n return {\n kind: \"tool-calls\",\n toolMessage: await this.#runTools(calls),\n ...result,\n }\n }\n\n async #parseToolCalls(message: Message): Promise<ToolCallPart[]> {\n if (typeof message.content === \"string\") return []\n const calls = message.content.filter((p): p is ToolCallPart => p.type === \"tool-call\")\n for (const call of calls) {\n const tool = this.tools.find((t) => t.name === call.name)\n if (!tool) continue\n try {\n call.params = (await tool.validator.cleanParams(call.params)) ?? call.params\n } catch {}\n }\n return calls\n }\n\n async compact(): Promise<void> {\n const prev = this.#status\n const pressure = this.pressure\n this.#setStatus(\"compacting\")\n this.#usage.resetLast()\n try {\n const { Compaction } = await import(\"./compaction/compactions.ts\")\n const opts: Partial<CompactionOptions> = {\n ...toValue(this.#opts.compaction),\n signal: this.#abortController?.signal,\n }\n const compactor = new Compaction(this, opts)\n await compactor.compact(pressure)\n } finally {\n this.#setStatus(prev)\n }\n }\n\n async #runTools(calls: ToolCallPart[]) {\n this.#setStatus(\"running-tools\")\n void this.emit(\"tool-calls\", { calls })\n for (const call of calls) void this.emit(\"tool-call\", { call })\n\n const emitted = new Set<string>()\n // The whole batch — including streamable promotion, parallel chains,\n // grace timing, and ownerRound suppression — lives in Tasks.run().\n // What lands back here is a 1:1 array of result parts ready to commit.\n // The skill tool (when active) is passed via `extraTools` so dispatch\n // can resolve `name: \"skill\"` calls without polluting `tasks.tools`.\n const resultParts = await this.#tasks.run(calls, await this.#toolContext(), {\n onResult: (call, result) => {\n emitted.add(call.id)\n void this.emit(\"tool-result\", { call, result })\n },\n })\n\n for (let i = 0; i < calls.length; i++) {\n const call = calls[i]\n const part = resultParts[i]\n if (emitted.has(call.id)) continue\n void this.emit(\"tool-result\", {\n call,\n result: {\n content: part.content,\n error: part.error,\n isError: part.isError ?? false,\n meta: part.meta,\n },\n })\n }\n const message: Message<\"tool\"> = { content: resultParts, role: \"tool\" }\n await this.session.add(message)\n return message\n }\n\n /** Resolve when the agent is in a quiescent state — `idle` or\n * `paused`. If a run is currently in flight (or was just kicked off\n * synchronously by `send` / `inject` / a wakeup), waits for it to\n * settle before returning the final status. If no run is in flight,\n * resolves immediately with the current status.\n *\n * Use this in REPL-style drivers to await the loop without racing\n * with internally-triggered runs (the wakeup timer, heartbeat, or\n * task-completion injects all spawn `void this.run()` independently\n * of caller-driven `await agent.run()`). The `#running` promise is\n * set synchronously inside `run()` before any await point, so this\n * catches every kicked-off cycle including the one `send` just fired.\n *\n * Errors from the loop are swallowed — callers read `agent.lastError`\n * / `agent.lastStopReason` for diagnostics. The point of `waitIdle`\n * is just to know \"the loop is no longer driving.\" */\n async waitIdle(timeout?: number): Promise<AgentStatus> {\n if (!this.#running) return this.#status\n const wait = [this.#running.catch(() => undefined)]\n if (timeout !== undefined) wait.push(new Promise((resolve) => setTimeout(resolve, timeout)))\n await Promise.race(wait)\n return this.#status\n }\n\n /** Stop the loop with a custom reason.\n * When `abort: true` (the default), also trigger the abort signal so any\n * in-flight stream or tool calls can react to the stop.\n * When `abort: false`, the agent still stops immediately, but any active\n * stream or tool calls are left to run to completion */\n stop(opts: { abort?: boolean; reason?: string } = {}): void {\n this.#pauseRequested ??= opts.reason ?? \"stop\"\n if (opts.abort ?? true) this.#abortController?.abort(opts.reason)\n }\n\n async start() {\n if (this.#started) return\n this.#started = true\n await this.emit(\"start\")\n }\n\n get started() {\n return this.#started\n }\n\n // ── Internals ────────────────────────────────────────────────────────\n\n async #turn(): Promise<TurnResult> {\n for (let step = 1; ; step++) {\n if (this.#abortController?.signal.aborted)\n return { kind: \"aborted\", reason: this.#abortController.signal.reason }\n if (this.#pauseRequested !== undefined)\n return { kind: \"paused\", reason: this.#pauseRequested }\n\n void this.emit(\"step-start\", { step })\n const outcome = await this.step()\n void this.emit(\"step-end\", { outcome: outcome.kind, step })\n\n if (outcome.kind === \"error\") {\n return {\n kind: outcome.error?.name === \"AbortError\" ? \"aborted\" : \"error\",\n reason: outcome.error,\n }\n }\n\n if (outcome.kind === \"natural\") {\n return { kind: \"natural\" }\n }\n\n if (outcome.kind === \"context-overflow\") {\n // Auto-compaction also gates the overflow recovery path. With\n // `auto: false`, overflow stops cleanly instead of attempting\n // a recovery the user explicitly opted out of.\n if (toValue(this.#opts.compaction)?.enabled === false) return { kind: \"context-overflow\" }\n // Compactor mutates the conversation; the rejected message is\n // not committed — next step retries on the compacted state.\n this.#opts.logger\n ?.child(\"compaction\")\n .warn(\n `context overflow detected (size ${this.contextSize}, limit ${this.pressure.limit}); running compaction`\n )\n await this.compact()\n continue\n }\n\n // outcome.kind === \"tool-calls\" — consult the policy.\n const stop = this.#stopPolicy.detect()\n if (stop) return { kind: stop }\n }\n }\n\n async #loop(): Promise<AgentStopKind> {\n if (!this.#started) await this.start()\n // Reset per-run counters (steps, consecutive errors, call history).\n // Token totals stay sticky across resets — they're billing-style\n // displays, not per-turn caps.\n this.#stopPolicy.reset()\n for (let turn = 1; ; turn++) {\n void this.emit(\"turn-start\", { turn })\n const result = await this.#turn()\n const reason = result.reason ? toError(result.reason).message : undefined\n void this.emit(\"turn-end\", { outcome: result.kind, reason, turn })\n\n if (result.kind === \"natural\") {\n // Drain follow-up queue if anything arrived during the turn.\n if (this.#appendQueue.length > 0) {\n for (const m of this.#appendQueue.splice(0)) await this.session.add(m)\n continue\n }\n // Wakeups (or any other inject) that fired while the agent was\n // streaming / running-tools land in `#injectQueue`. If we stop\n // here without giving them a turn, they get drained at the top\n // of the next unrelated user turn — surfacing the wakeup AFTER\n // the user's reply, with no `status=\"cancelled\"` marker since it\n // never went through `#cancelAllWakeups`. Continue the loop so\n // `step()` drains them next iteration.\n if (this.#injectQueue.length > 0) continue\n }\n return this.#stop(result.kind, result.reason)\n }\n }\n\n #stop(kind: AgentStopKind, reason?: unknown): AgentStopKind {\n this.#lastStop = {\n error: reason instanceof Error ? reason : undefined,\n kind,\n reason: reason === undefined ? undefined : toError(reason).message,\n }\n this.#setStatus(kind === \"natural\" ? \"idle\" : \"paused\")\n void this.emit(\"stop\", {\n status: this.#status,\n usage: this.usage,\n ...this.#lastStop,\n })\n return kind\n }\n\n /** Build the per-step `ToolContext` handed to each tool. The session\n * cwd, an abort signal scoped to the in-flight stream, and the\n * long-running spawn registry are all surfaced here. */\n async #toolContext(): Promise<ToolContext> {\n const masker = await this.#ctx.masker()\n return {\n agent: this,\n bash: this.#opts.bash,\n cwd: this.cwd,\n isMasked: (msgId: string, partIdx?: number) => masker?.isMasked(msgId, partIdx) ?? false,\n messages: this.session.messages,\n need: (scope, input) => this.#need(scope, input),\n perms: await this.ctx.permissions(),\n sessionDir: this.session.dir,\n signal: this.#abortController?.signal,\n swarm: () => this.#ctx.swarm(),\n tasks: this.#tasks,\n }\n }\n\n /** Implementation of `ctx.need(scope, input)`. Resolves on `allow`,\n * throws a `PERMISSION_DENIED` `AiError` on `deny`, and escalates\n * `ask` to `AgentOptions.allow` (treating it as `deny` when no\n * callback is configured). */\n async #need(scope: string, input: string): Promise<void> {\n const perms = await this.ctx.permissions()\n const r = perms.validate(scope, input)\n if (r.verdict === \"allow\") return\n if (r.verdict === \"ask\" && this.#opts.allow) {\n const ok = await this.#opts.allow({\n ask: r.ask,\n input,\n reason: r.reason,\n scope,\n suggestions: r.suggestions,\n })\n if (ok) return\n }\n throw new AiError({\n code: \"PERMISSION_DENIED\",\n data: { input, scope, suggestions: r.suggestions, verdict: r.verdict },\n message: r.reason,\n // `ask` verdicts that the user denied this turn are retryable —\n // they may add a rule later. Hard `deny` is not.\n retryable: r.verdict === \"ask\",\n })\n }\n\n /** Tear down session-scoped resources. Called on agent disposal — the\n * agent doesn't auto-dispose today, so callers (TUI on quit, headless\n * runner on completion) should invoke this explicitly. */\n async dispose(): Promise<void> {\n this.clearListeners()\n this.#cancelAllWakeups()\n await this.#tasks.killAll()\n }\n\n #setStatus(status: AgentStatus): void {\n const prev = this.#status\n if (prev === status) return\n // Cancel wakeups only when an EXTERNAL wake-up brings the loop back\n // to active (idle/paused → streaming). Mid-turn transitions\n // (running-tools → streaming) are the same turn continuing — a\n // wakeup the model just scheduled mid-tool-call would otherwise be\n // killed before it ever fires. The \"external wake-up\" cases that\n // *should* cancel: a `task-done` inject, a heartbeat, a user\n // message — all route through `inject` → `send` → `run` from the\n // idle/paused state, hitting this branch correctly.\n if (status === \"streaming\" && (prev === \"idle\" || prev === \"paused\")) {\n this.#cancelAllWakeups()\n }\n this.#status = status\n void this.emit(\"status\", { status })\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"agent-D3wVIibN.mjs","names":[],"sources":["../src/utils/usage.ts","../src/stop.ts","../src/agent.ts"],"sourcesContent":["import type { Message, TokenCount, Usage } from \"@zaly/ai\"\n\n/** Sum two Usages. Optional fields are only present in the result\n * when at least one input had them set, so callers can tell \"no\n * reasoning happened this turn\" apart from \"0 reasoning tokens.\" */\nexport function addUsage(a: Usage, b: Usage): Usage {\n const out: Usage = {\n input: a.input + b.input,\n output: a.output + b.output,\n }\n if (a.cacheRead !== undefined || b.cacheRead !== undefined) {\n out.cacheRead = (a.cacheRead ?? 0) + (b.cacheRead ?? 0)\n }\n if (a.cacheWrite !== undefined || b.cacheWrite !== undefined) {\n out.cacheWrite = (a.cacheWrite ?? 0) + (b.cacheWrite ?? 0)\n }\n if (a.reasoning !== undefined || b.reasoning !== undefined) {\n out.reasoning = (a.reasoning ?? 0) + (b.reasoning ?? 0)\n }\n if (a.cost || b.cost) out.cost = addUsage(a.cost ?? empty(), b.cost ?? empty())\n return out\n}\n\nexport class TokenUsage implements Usage {\n #last: Usage = empty()\n #total: Usage = empty()\n\n constructor(messages?: readonly Message[]) {\n for (const m of messages ?? [])\n if (m.role === \"assistant\" && m.meta?.usage) this.add(m.meta.usage)\n }\n\n get cost(): Usage {\n return this.total.cost ?? empty()\n }\n\n get input(): number {\n return this.last.input\n }\n\n get output(): number {\n return this.last.output\n }\n\n get cacheRead(): number {\n return this.last.cacheRead ?? 0\n }\n\n get cacheWrite(): number {\n return this.last.cacheWrite ?? 0\n }\n\n get reasoning(): number {\n return this.last.reasoning ?? 0\n }\n\n add(count: Usage): void {\n this.#last = count\n this.#total = addUsage(this.#total, count)\n }\n\n resetLast(): void {\n this.#last = empty()\n }\n\n get last(): Usage {\n return this.#last\n }\n\n get total(): Usage {\n return this.#total\n }\n\n get contextSize(): number {\n return (\n this.last.input + this.last.output + (this.last.cacheRead ?? 0) + (this.last.cacheWrite ?? 0)\n )\n }\n}\n\nfunction empty(): TokenCount {\n return { input: 0, output: 0 }\n}\n","import type { TokenCount, ToolCallPart } from \"@zaly/ai\"\nimport type { Emitter, Envelope } from \"@zaly/shared\"\nimport type { AgentEvents, AgentStopKind } from \"./events.ts\"\n\nimport { safeStringify } from \"@zaly/shared\"\nimport { addUsage } from \"./utils/usage.ts\"\n\n/** Caps + heuristics that can end a run early. Wired into the\n * `AgentSession` loop via a single `detect()` call after each step. */\nexport interface StopOptions {\n /** Hard ceiling on provider round-trips per `run()`. Default: 50. */\n maxSteps?: number\n /** Cumulative token cap across the whole `run()`\n * (`totalUsage.input + totalUsage.output`). */\n tokenBudget?: number\n /** Bail after this many consecutive failing tool calls. A successful\n * tool result resets the streak. */\n maxToolErrors?: number\n\n // ── Loop detection ──────────────────────────────────────────────────\n // Two cheap heuristics over the running tool-call history. Hash is\n // `name + JSON.stringify(params)`; property order matters (rare false\n // negative if the model alternates key order on the same logical call,\n // accepted in exchange for a much cheaper hash). Set either limit to\n // `Infinity` to disable that arm.\n\n /** Same `(name, params)` appearing N times in a row → loop. Catches\n * the most common failure mode: re-calling `read_file` with the same\n * path expecting different output. Default 3. */\n loopConsecutive?: number\n /** Bounded window for duplicate detection. Default 10. */\n loopWindow?: number\n /** Within the window, this many duplicates of one call → loop.\n * Catches alternation patterns (`A B A B …`) the consecutive arm\n * alone won't see. Default 4. */\n loopWindowRepeats?: number\n}\n\n/**\n * Subscribes to an `AgentEvent` stream, accumulates the bookkeeping\n * the loop needs to make stop-or-continue decisions, and exposes a\n * single `detect()` to consult after each step.\n *\n * Lives outside `AgentSession` so concerns separate cleanly:\n * - the session owns conversation, queues, status, and loop control;\n * - the policy owns counters and the rules that act on them.\n *\n * Wire it up via `attach(session)` (returns an unsubscribe), or feed\n * events manually with `handle(event)` for custom drivers.\n */\nexport class StopPolicy {\n readonly #opts: StopOptions\n\n #steps = 0\n #consecutiveErrors = 0\n #calls: ToolCallPart[] = []\n #usage: TokenCount = { input: 0, output: 0 }\n #totalUsage: TokenCount = { input: 0, output: 0 }\n\n constructor(opts: StopOptions = {}) {\n this.#opts = opts\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Token usage from the most recent step's response. */\n get usage(): TokenCount {\n return this.#usage\n }\n /** Cumulative token usage across every step in the current run. */\n get totalUsage(): TokenCount {\n return this.#totalUsage\n }\n get steps(): number {\n return this.#steps\n }\n get consecutiveErrors(): number {\n return this.#consecutiveErrors\n }\n /** Tool-call history fed to the loop detector. Read-only. */\n get calls(): readonly ToolCallPart[] {\n return this.#calls\n }\n\n // ── Wiring ────────────────────────────────────────────────────────────\n\n /** Subscribe to an emitter. To detach later, register the listener\n * yourself and pass the same fn to `emitter.off(fn)`:\n *\n * const handler = (e) => policy.handle(e)\n * emitter.all(handler)\n * // later:\n * emitter.off(handler) */\n attach(emitter: Emitter<AgentEvents>): void {\n emitter.onAny((event) => this.handle(event))\n }\n\n /** Feed a single event into the policy. Public so custom drivers\n * can drive it without going through `attach`. */\n handle(event: Envelope<AgentEvents>): void {\n switch (event.type) {\n case \"step-end\": {\n this.#steps++\n break\n }\n case \"tool-call\": {\n this.#calls.push(event.call)\n break\n }\n case \"tool-result\": {\n this.#consecutiveErrors = event.result.isError ? this.#consecutiveErrors + 1 : 0\n break\n }\n case \"stream-event\": {\n if (event.event.type === \"finish\") {\n this.#usage = event.event.usage\n this.#totalUsage = addUsage(this.#totalUsage, event.event.usage)\n }\n break\n }\n // status / stop — no policy state to update.\n }\n }\n\n /** Reset per-run counters. Token totals (`usage` / `totalUsage`)\n * persist unless `keepUsage: false` is passed — billing-style\n * displays usually want them sticky across resets. */\n reset(opts: { keepUsage?: boolean } = {}): void {\n this.#steps = 0\n this.#consecutiveErrors = 0\n this.#calls = []\n if (opts.keepUsage === false) {\n this.#usage = { input: 0, output: 0 }\n this.#totalUsage = { input: 0, output: 0 }\n }\n }\n\n /** Decide whether the loop should stop. Returns the stop reason or\n * `undefined` to continue. Order matters — `loop-detected` wins\n * over `max-tool-errors` if both fire, since a model in a tight\n * loop is the more pressing thing to surface. */\n detect(): AgentStopKind | undefined {\n if (this.#opts.maxSteps !== undefined && this.#steps >= this.#opts.maxSteps) {\n return \"max-steps\"\n }\n if (this.#detectLoop()) {\n return \"loop-detected\"\n }\n if (\n this.#opts.maxToolErrors !== undefined &&\n this.#consecutiveErrors >= this.#opts.maxToolErrors\n ) {\n return \"max-tool-errors\"\n }\n if (\n this.#opts.tokenBudget !== undefined &&\n this.#totalUsage.input + this.#totalUsage.output > this.#opts.tokenBudget\n ) {\n return \"token-budget\"\n }\n return undefined\n }\n\n // ── Loop detection internals ─────────────────────────────────────────\n\n #detectLoop(): boolean {\n const calls = this.#calls\n if (calls.length === 0) return false\n\n const consecutive = this.#opts.loopConsecutive ?? 3\n if (Number.isFinite(consecutive) && calls.length >= consecutive) {\n const last = hashCall(calls[calls.length - 1])\n let run = 1\n for (let i = calls.length - 2; i >= 0 && run < consecutive; i--) {\n if (hashCall(calls[i]) === last) run++\n else break\n }\n if (run >= consecutive) return true\n }\n\n const window = this.#opts.loopWindow ?? 10\n const windowRepeats = this.#opts.loopWindowRepeats ?? 4\n if (Number.isFinite(windowRepeats) && calls.length >= windowRepeats) {\n const slice = calls.slice(Math.max(0, calls.length - window))\n const counts = new Map<string, number>()\n for (const call of slice) {\n const h = hashCall(call)\n const next = (counts.get(h) ?? 0) + 1\n if (next >= windowRepeats) return true\n counts.set(h, next)\n }\n }\n\n return false\n }\n}\n\nfunction hashCall(call: ToolCallPart): string {\n return `${call.name}\\0${safeStringify(call.params)}`\n}\n","// oxlint-disable no-await-in-loop\nimport type {\n AssistantMessage,\n Content,\n Context,\n Message,\n MetaOf,\n MetaPart,\n Model,\n ModelStreamOptions,\n ParamsOf,\n TokenCount,\n Tool,\n ToolCallPart,\n ToolContext,\n ToolResult,\n} from \"@zaly/ai\"\nimport type { CompactionOptions } from \"./compaction/compactions.ts\"\nimport type { AgentContext } from \"./ctx.ts\"\nimport type { AgentEvents, AgentStatus, AgentStop, AgentStopKind } from \"./events.ts\"\nimport type { Session } from \"./session/session.ts\"\nimport type { AgentOptions, ContextPressure, SendMode, StepResult, TurnResult } from \"./types.ts\"\n\nimport { AiError, isContextOverflow, runTool } from \"@zaly/ai\"\nimport { Emitter, toValue, toError } from \"@zaly/shared\"\nimport { StopPolicy } from \"./stop.ts\"\nimport { Tasks, taskCompletionMessage, taskInfoPart } from \"./tasks.ts\"\nimport { TokenUsage } from \"./utils/usage.ts\"\nimport { uuidv7 } from \"./utils/uuid.ts\"\n\nconst PRESSURE_LEVELS = [0.75, 0.85, 0.95] as const\n\n/**\n * Long-lived agent — drives the multi-turn loop, owns the run-time\n * status / queues, and delegates conversation state to a `Session`.\n *\n * Typical interactive use:\n *\n * ```ts\n * const agent = await Agent.load({ model, tools })\n * agent.session.on(\"node\", (e) => render(e))\n * agent.send({ role: \"user\", content: \"hi\" }) // auto-runs\n * // …user types again later…\n * agent.send({ role: \"user\", content: \"follow-up\" }) // queues if running\n * ```\n *\n * Headless / one-shot use is just a thin wrapper on top — see\n * `runAgent` in the test helpers.\n */\nexport class Agent extends Emitter<AgentEvents> {\n readonly #opts: AgentOptions\n readonly #ctx: AgentContext\n readonly #stopPolicy: StopPolicy\n readonly #tasks: Tasks\n /** Nesting depth — see `AgentOptions.depth`. Read-only; subagents pass\n * `parent.depth + 1` when constructing their child. */\n readonly depth: number\n /** Cap on `depth` — see `AgentOptions.maxDepth`. */\n readonly maxDepth: number\n #started = false\n\n #parent?: Agent\n\n #injectQueue: Message[] = []\n #appendQueue: Message[] = []\n #notifyQueue: MetaPart[] = []\n\n #status: AgentStatus = \"idle\"\n #abortController?: AbortController\n #pauseRequested?: string\n #running?: Promise<AgentStopKind>\n #usage: TokenUsage\n\n /** Pending future-injects scheduled via `scheduleWakeup`. Cleared\n * whenever the loop becomes active for any reason — the scheduled\n * inject was a fallback \"wake me up at time T if nothing else does,\"\n * and once something else has, the timer is moot. Hints from cancelled\n * wakeups carry over as a system message so the intent doesn't\n * evaporate. */\n readonly #wakeups = new Map<string, { timer: ReturnType<typeof setTimeout>; hint?: string }>()\n\n #lastStop?: AgentStop\n\n /** Synchronous, low-level constructor. Prefer `Agent.load(opts)` —\n * it runs the same construction *plus* any async setup (resolving\n * `session: SessionOptions` to a built `Session`, skills discovery,\n * future warm-ups). The constructor is `protected` so test doubles /\n * subclasses can still call `super(init)` directly; they're\n * responsible for providing a pre-built `Session` on `init.session`. */\n public constructor(ctx: AgentContext) {\n super()\n const opts = ctx.opts\n this.#ctx = ctx\n this.#opts = ctx.opts\n this.depth = opts.depth ?? 0\n this.maxDepth = opts.maxDepth ?? 2\n\n // Note: session.update() + initial messages are committed\n // asynchronously from `Agent.load`, NOT here. Constructors can't\n // be async; deferring those writes until after construction also\n // gives consumers a window to subscribe to session events before\n // the first node fires.\n\n this.#tasks = new Tasks()\n this.#tasks.$tools = async () => this.tools\n this.#tasks.heartbeatMs = opts.heartbeatMs\n this.#tasks.onEmitError = (error) => opts.logger?.child(\"tasks\").error(error)\n // Post-round task completions inject a system message into the next\n // step, surfacing the result to the model. Round-internal completions\n // are folded into the round's returned parts and don't fire here.\n this.#tasks.on(\"task-done\", ({ task }) => {\n this.send(taskCompletionMessage(task))\n })\n // Heartbeats keep the agent loop alive while long-running tasks are\n // in flight. Each pulse injects a small system note listing what's\n // still going. Tasks with incremental output ready to read get a\n // `*new*` marker so the model knows to call `task_poll` if it cares.\n this.#tasks.on(\"heartbeat\", ({ running }) => {\n this.send({\n content: [{ content: [taskInfoPart(running)], tag: \"heartbeat\", type: \"meta\" }],\n role: \"system\",\n })\n })\n\n this.#stopPolicy = new StopPolicy(opts.stop)\n this.#stopPolicy.attach(this)\n this.onEmitError = (error) => opts.logger?.child(\"events\").error(error)\n this.#ctx.attach(this)\n\n // Seed usage\n this.#usage = new TokenUsage(this.messages)\n this.#ctx.on(\"session\", () => this.reset())\n }\n\n async reset(): Promise<void> {\n this.stop({ abort: true, reason: \"reset\" })\n await this.waitIdle()\n await this.#tasks.killAll()\n this.#cancelAllWakeups()\n this.#usage = new TokenUsage(this.messages)\n this.#stopPolicy.reset({ keepUsage: false })\n this.#injectQueue = []\n this.#appendQueue = []\n this.#notifyQueue = []\n this.#wakeups.clear()\n this.#stop(\"natural\")\n }\n\n /** Spawn a child agent that inherits this agent's runtime defaults —\n * cwd, model, permissions, full effective tool list (user tools +\n * the loaded skill tool), `depth + 1`, and `maxDepth`. Pass\n * `overrides` to specialize: typical ones are `prompt` (the child\n * has its own role), `session` (the child writes to its own JSONL),\n * and `tools` (curated subset).\n *\n * Used by the `subagent` tool for delegation; equally useful for\n * ad-hoc parallel-agent patterns (multiple children working on\n * pieces of a task in parallel).\n *\n * Depth cap: when the child would be at `maxDepth`, the `subagent`\n * tool is filtered out of its inherited tool list so recursion\n * bottoms out cleanly. The model just doesn't see the tool — no\n * error path. */\n async child(overrides: Partial<AgentOptions> = {}): Promise<Agent> {\n const childDepth = overrides.depth ?? this.depth + 1\n // Inherit the *effective* step tool list (includes the loaded\n // `skill` tool). The child opts out of its own skill scan since\n // the catalog is shared.\n\n let tools = [...this.tools].filter((t) => t.name !== \"skill\")\n if (childDepth >= this.maxDepth) tools = tools.filter((t) => t.name !== \"subagent\")\n const { createAgent } = await import(\"./ctx.ts\")\n\n const ret = await createAgent({\n cwd: this.cwd,\n depth: childDepth,\n mask: this.#opts.mask,\n maxDepth: this.maxDepth,\n model: this.model,\n // Inherit the parent's `notify` setting so test roots that\n // disable the notifier (`notify: false`) propagate that to\n // children — otherwise spawning a child would silently re-enable\n // session-started / time / etc. injections that the harness\n // explicitly opted out of.\n notify: this.#opts.notify,\n permissions: await this.ctx.permissions(),\n skills: this.#ctx.skills, // shared catalog; child doesn't reload\n // Propagate the swarm so the child + every grandchild address\n // each other through the same registry. Override-able via\n // `overrides.swarm` if a caller wants the child outside the\n // tree (rare).\n swarm: await this.#ctx.swarm(),\n tools,\n ...overrides,\n })\n ret.#parent = this\n // Children are spawned ready — subagent dispatch typically calls\n // `.send()` immediately after, and inspecting `child.tools` /\n // `child.prompt` before sending should just work.\n await ret.start()\n return ret\n }\n\n // ── Read ──────────────────────────────────────────────────────────────\n\n /** Active conversation — delegates to the underlying `Session`. */\n get messages(): readonly Message[] {\n return this.session.messages\n }\n\n get status(): AgentStatus {\n return this.#status\n }\n\n get cwd(): string {\n return this.#ctx.cwd\n }\n\n get model(): Model | undefined {\n return this.#ctx.model\n }\n\n get session(): Session {\n return this.#ctx.session\n }\n\n get signal(): AbortSignal | undefined {\n return this.#abortController?.signal\n }\n\n /** Token usage from the most recent step's response. Drives\n * `contextSize` and any \"this turn used N tokens\" UI. */\n get usage(): TokenUsage {\n return this.#usage\n }\n\n /** Cumulative token usage across every step in the current run.\n * Useful for billing-style displays. */\n get totalUsage(): TokenCount {\n return this.#usage.total\n }\n /** Logical size of the current conversation in tokens — what the\n * next request will send as input. Equals the last step's\n * `input + output` (since the model's reply becomes part of the\n * next prompt). Returns 0 before any step has run. */\n get contextSize(): number {\n return this.#usage.contextSize\n }\n\n get pressure(): ContextPressure {\n const used = this.contextSize\n const limit = this.model?.spec.contextSize ?? 0\n const ratio = limit > 0 ? used / limit : 0\n const level = PRESSURE_LEVELS.findLast((t) => ratio >= t) ?? 0\n return { level, limit, ratio, used }\n }\n\n get lastStop(): AgentStop | undefined {\n return this.#lastStop\n }\n\n get steps(): number {\n return this.#stopPolicy.steps\n }\n\n get parent(): Agent | undefined {\n return this.#parent\n }\n\n get tools() {\n return this.#ctx.tools\n }\n\n get prompt() {\n return this.#ctx.prompt\n }\n\n get ctx(): AgentContext {\n return this.#ctx\n }\n\n /** Long-running task registry — exposed for the TUI / introspection\n * tools (`task_list`, etc.). Mutating directly is a foot-gun;\n * prefer the agent's higher-level surface. */\n get tasks(): Tasks {\n return this.#tasks\n }\n\n // ── Input ────────────────────────────────────────────────────────────\n\n #enqueue(message: Message | Message[], opts: { mode?: SendMode } = {}): void {\n const messages = Array.isArray(message) ? message : [message]\n if (!messages.length) return\n for (const m of messages) m.id ??= uuidv7()\n const queue = opts.mode === \"append\" ? this.#appendQueue : this.#injectQueue\n queue.push(...messages)\n void this.emit(\"pending\", { messages })\n }\n\n /** Append a message and (re)start the loop. The user spoke; the agent\n * responds. If a loop is currently running, the message lands on the\n * follow-up queue and is processed after the current turn naturally\n * stops. Otherwise the loop starts immediately. */\n send(message?: Message | Message[], opts: { mode?: SendMode; run?: boolean } = {}): void {\n this.#enqueue(message ?? [], opts)\n if (opts.run === false) return\n if (this.#status === \"idle\" || this.#status === \"paused\") void this.run()\n }\n\n async useTool<T extends Tool = Tool>(\n name: T[\"name\"],\n params: ParamsOf<T>,\n msg: string,\n opts: { hidden?: boolean } = {}\n ): Promise<{\n call: ToolCallPart<T[\"name\"], ParamsOf<T>>\n result: ToolResult<MetaOf<T>>\n messages: [Message<\"system\">, Message<\"assistant\">, Message<\"tool\">]\n }> {\n const tool = this.tools.find((t) => t.name === name) as T | undefined\n if (!tool) throw new Error(`cannot inject tool use: no tool named \"${name}\"`)\n\n const id = uuidv7()\n const call: ToolCallPart = { id, name, params, type: \"tool-call\" }\n const result = await runTool(tool, params, await this.#toolContext(), { preflight: false })\n const messages: [Message<\"system\">, Message<\"assistant\">, Message<\"tool\">] = [\n {\n content: [\n {\n content: msg,\n data: { tool: name },\n tag: \"user-tool-use\",\n type: \"meta\",\n },\n ],\n role: \"system\",\n },\n {\n content: [call],\n role: \"assistant\",\n },\n {\n content: [\n {\n content: result.content,\n error: result.error,\n id,\n isError: result.isError,\n meta: result.meta,\n name,\n type: \"tool-result\",\n },\n ],\n role: \"tool\",\n },\n ]\n\n if (opts.hidden) for (const m of messages) m.hidden = true\n\n return { call, messages, result: result as ToolResult<MetaOf<T>> }\n }\n\n notify(type: string, data: Content | Record<string, unknown>): void {\n const meta: MetaPart =\n typeof data === \"string\" || Array.isArray(data)\n ? { content: data, tag: type, type: \"meta\" }\n : { data, tag: type, type: \"meta\" }\n this.#notifyQueue.push(meta)\n }\n\n /** Schedule a one-shot wake-up at `delayMs` from now. The agent will\n * receive a system message at that time IF nothing else has woken\n * the loop in the meantime — task-done injects, heartbeats, and user\n * messages all auto-cancel pending wakeups (their job was to ensure\n * the agent gets a turn; if it's already getting one, the timer is\n * redundant).\n *\n * Cancelled wakeups don't silently disappear — their `hint`s, if any,\n * are folded into a system message that lands in the next step\n * alongside whatever woke the loop. The model sees the hints with a\n * `status=\"cancelled\"` marker so it knows the timer didn't fire.\n *\n * Returns the wakeup id (mostly for telemetry / TUI display — the\n * model can't usefully cancel it later, since by the time it has a\n * turn to call cancel, the wakeup has either fired or been cancelled\n * for it). */\n scheduleWakeup(opts: { delayMs: number; hint?: string }): string {\n const id = uuidv7()\n const timer = setTimeout(() => {\n this.#wakeups.delete(id)\n this.send({\n content: [{ data: { hint: opts.hint, id }, tag: \"wakeup\", type: \"meta\" }],\n role: \"system\",\n })\n }, opts.delayMs)\n timer.unref()\n this.#wakeups.set(id, { hint: opts.hint, timer })\n return id\n }\n\n /** Cancel all pending wakeups, surfacing their hints as a single\n * system message so the model can see what was queued. Called from\n * `#setStatus` whenever the agent transitions to `streaming` — that's\n * the unambiguous \"loop is active\" signal. */\n #cancelAllWakeups(): void {\n if (this.#wakeups.size === 0) return\n const carried: { id: string; hint: string }[] = []\n for (const [id, { hint, timer }] of this.#wakeups) {\n clearTimeout(timer)\n if (hint) carried.push({ hint, id })\n }\n this.#wakeups.clear()\n if (carried.length > 0) {\n const parts: MetaPart[] = carried.map((c) => ({\n data: { hint: c.hint, id: c.id, status: \"cancelled\" as const },\n tag: \"wakeup\",\n type: \"meta\",\n }))\n this.#injectQueue.push({ content: parts, role: \"system\" })\n }\n }\n\n // ── Loop control ─────────────────────────────────────────────────────\n\n /** Drive steps until the agent goes idle (no queued messages and\n * the model stopped naturally) or hits a non-recoverable stop.\n * Doubles as a resume from `paused` — drains queued messages and\n * picks the loop back up. If a run is already in flight, returns\n * the existing promise. */\n run(): Promise<AgentStopKind> {\n if (this.#running) return this.#running\n this.#pauseRequested = undefined\n this.#abortController = undefined\n this.#running = this.#loop()\n .catch((error) => {\n this.#opts.logger?.child(\"run\").error(error)\n return this.#stop(\n error instanceof Error && error.name === \"AbortError\" ? \"aborted\" : \"error\",\n error\n )\n })\n .finally(() => {\n this.#abortController = undefined\n this.#running = undefined\n })\n return this.#running\n }\n\n async #collect() {\n this.#abortController = new AbortController()\n const context = {\n cacheKey: this.ctx.session.id,\n messages: [...this.ctx.messages],\n prompt: this.prompt,\n tools: this.tools,\n } satisfies Context\n await this.emitSerial(\"context\", context)\n const model = this.model\n if (!model) throw new Error(\"model must be loaded to collect\")\n return model.stream(context, this.#streamOpts())\n }\n\n #shouldAutoCompact(): boolean {\n const opts = toValue(this.#opts.compaction)\n const auto = opts?.enabled ?? true\n if (!auto) return false\n const threshold = opts?.threshold ?? 0.95\n return this.pressure.ratio >= threshold\n }\n\n #streamOpts(): ModelStreamOptions {\n const base = this.#opts.request ?? {}\n return {\n ...base,\n onEvent: (event) => {\n void this.emit(\"stream-event\", { event })\n try {\n void this.#opts.onEvent?.(event)\n } catch (error) {\n this.#opts.logger?.child(\"stream\").error(error)\n }\n },\n onUpdate: this.#opts.onUpdate,\n reasoning: { ...base.reasoning, effort: this.#ctx.reasoning },\n signal: this.#abortController?.signal,\n }\n }\n\n /** Run exactly one step. Useful for tests and custom drivers\n * that want to interleave logic between steps. */\n async step(): Promise<StepResult> {\n if (this.#shouldAutoCompact()) await this.compact()\n // Drain the notify queue into the inject queue as a single system message\n if (this.#notifyQueue.length > 0) {\n this.#injectQueue.push({\n content: this.#notifyQueue.splice(0),\n role: \"system\",\n })\n }\n\n // Drain the send queue (user-submitted via `send()`) and the inject\n // queue (notifier / wakeup / hooks). Sequential await preserves\n // chronological order on the session.\n for (const m of this.#injectQueue.splice(0)) await this.session.add(m)\n for (const m of this.#appendQueue.splice(0)) await this.session.add(m)\n\n this.#setStatus(\"streaming\")\n\n let collected: AssistantMessage\n\n try {\n collected = await this.#collect()\n } catch (error) {\n const err = toError(error)\n const overflow = isContextOverflow({ message: err.message })\n if (overflow) this.#opts.logger?.warn(\"Context overflow detected:\", err)\n return {\n error: err,\n finishReason: overflow ? \"length\" : \"error\",\n kind: overflow ? \"context-overflow\" : \"error\",\n usage: { input: 0, output: 0 },\n }\n }\n\n this.#usage.add(collected.meta.usage)\n\n const result: Omit<StepResult, \"kind\"> = {\n finishReason: collected.meta.finishReason,\n message: collected,\n usage: collected.meta.usage,\n }\n\n // Silent-overflow check BEFORE committing the message — gives the\n // session a chance to drop it and retry on a compacted history.\n if (\n this.#opts.contextLimit !== undefined &&\n isContextOverflow({\n contextLimit: this.#opts.contextLimit,\n // Total prompt size = uncached input + cached reads + cached\n // writes. `usage.input` is uncached-only; the cache fields are\n // separate billing tiers that still occupy the context window.\n usageInput:\n result.usage.input + (result.usage.cacheRead ?? 0) + (result.usage.cacheWrite ?? 0),\n })\n )\n return { kind: \"context-overflow\", ...result }\n\n const calls = await this.#parseToolCalls(collected)\n await this.session.add(collected)\n\n if (calls.length === 0) return { kind: \"natural\", ...result }\n\n return {\n kind: \"tool-calls\",\n toolMessage: await this.#runTools(calls),\n ...result,\n }\n }\n\n async #parseToolCalls(message: Message): Promise<ToolCallPart[]> {\n if (typeof message.content === \"string\") return []\n const calls = message.content.filter((p): p is ToolCallPart => p.type === \"tool-call\")\n for (const call of calls) {\n const tool = this.tools.find((t) => t.name === call.name)\n if (!tool) continue\n try {\n call.params = (await tool.validator.cleanParams(call.params)) ?? call.params\n } catch {}\n }\n return calls\n }\n\n async compact(): Promise<void> {\n const prev = this.#status\n const pressure = this.pressure\n this.#setStatus(\"compacting\")\n this.#usage.resetLast()\n try {\n const { Compaction } = await import(\"./compaction/compactions.ts\")\n const opts: Partial<CompactionOptions> = {\n ...toValue(this.#opts.compaction),\n signal: this.#abortController?.signal,\n }\n const compactor = new Compaction(this, opts)\n await compactor.compact(pressure)\n } finally {\n this.#setStatus(prev)\n }\n }\n\n async #runTools(calls: ToolCallPart[]) {\n this.#setStatus(\"running-tools\")\n void this.emit(\"tool-calls\", { calls })\n for (const call of calls) void this.emit(\"tool-call\", { call })\n\n const emitted = new Set<string>()\n // The whole batch — including streamable promotion, parallel chains,\n // grace timing, and ownerRound suppression — lives in Tasks.run().\n // What lands back here is a 1:1 array of result parts ready to commit.\n // The skill tool (when active) is passed via `extraTools` so dispatch\n // can resolve `name: \"skill\"` calls without polluting `tasks.tools`.\n const resultParts = await this.#tasks.run(calls, await this.#toolContext(), {\n onResult: (call, result) => {\n emitted.add(call.id)\n void this.emit(\"tool-result\", { call, result })\n },\n })\n\n for (let i = 0; i < calls.length; i++) {\n const call = calls[i]\n const part = resultParts[i]\n if (emitted.has(call.id)) continue\n void this.emit(\"tool-result\", {\n call,\n result: {\n content: part.content,\n error: part.error,\n isError: part.isError ?? false,\n meta: part.meta,\n },\n })\n }\n const message: Message<\"tool\"> = { content: resultParts, role: \"tool\" }\n await this.session.add(message)\n return message\n }\n\n /** Resolve when the agent is in a quiescent state — `idle` or\n * `paused`. If a run is currently in flight (or was just kicked off\n * synchronously by `send` / `inject` / a wakeup), waits for it to\n * settle before returning the final status. If no run is in flight,\n * resolves immediately with the current status.\n *\n * Use this in REPL-style drivers to await the loop without racing\n * with internally-triggered runs (the wakeup timer, heartbeat, or\n * task-completion injects all spawn `void this.run()` independently\n * of caller-driven `await agent.run()`). The `#running` promise is\n * set synchronously inside `run()` before any await point, so this\n * catches every kicked-off cycle including the one `send` just fired.\n *\n * Errors from the loop are swallowed — callers read `agent.lastError`\n * / `agent.lastStopReason` for diagnostics. The point of `waitIdle`\n * is just to know \"the loop is no longer driving.\" */\n async waitIdle(timeout?: number): Promise<AgentStatus> {\n if (!this.#running) return this.#status\n const wait = [this.#running.catch(() => undefined)]\n if (timeout !== undefined) wait.push(new Promise((resolve) => setTimeout(resolve, timeout)))\n await Promise.race(wait)\n return this.#status\n }\n\n /** Stop the loop with a custom reason.\n * When `abort: true` (the default), also trigger the abort signal so any\n * in-flight stream or tool calls can react to the stop.\n * When `abort: false`, the agent still stops immediately, but any active\n * stream or tool calls are left to run to completion */\n stop(opts: { abort?: boolean; reason?: string } = {}): void {\n this.#pauseRequested ??= opts.reason ?? \"stop\"\n if (opts.abort ?? true) this.#abortController?.abort(opts.reason)\n }\n\n async start() {\n if (this.#started) return\n this.#started = true\n await this.emit(\"start\")\n }\n\n get started() {\n return this.#started\n }\n\n // ── Internals ────────────────────────────────────────────────────────\n\n async #turn(): Promise<TurnResult> {\n for (let step = 1; ; step++) {\n if (this.#abortController?.signal.aborted)\n return { kind: \"aborted\", reason: this.#abortController.signal.reason }\n if (this.#pauseRequested !== undefined)\n return { kind: \"paused\", reason: this.#pauseRequested }\n\n void this.emit(\"step-start\", { step })\n const outcome = await this.step()\n void this.emit(\"step-end\", { outcome: outcome.kind, step })\n\n if (outcome.kind === \"error\") {\n return {\n kind: outcome.error?.name === \"AbortError\" ? \"aborted\" : \"error\",\n reason: outcome.error,\n }\n }\n\n if (outcome.kind === \"natural\") {\n return { kind: \"natural\" }\n }\n\n if (outcome.kind === \"context-overflow\") {\n // Auto-compaction also gates the overflow recovery path. With\n // `auto: false`, overflow stops cleanly instead of attempting\n // a recovery the user explicitly opted out of.\n if (toValue(this.#opts.compaction)?.enabled === false) return { kind: \"context-overflow\" }\n // Compactor mutates the conversation; the rejected message is\n // not committed — next step retries on the compacted state.\n this.#opts.logger\n ?.child(\"compaction\")\n .warn(\n `context overflow detected (size ${this.contextSize}, limit ${this.pressure.limit}); running compaction`\n )\n await this.compact()\n continue\n }\n\n // outcome.kind === \"tool-calls\" — consult the policy.\n const stop = this.#stopPolicy.detect()\n if (stop) return { kind: stop }\n }\n }\n\n async #loop(): Promise<AgentStopKind> {\n if (!this.#started) await this.start()\n // Reset per-run counters (steps, consecutive errors, call history).\n // Token totals stay sticky across resets — they're billing-style\n // displays, not per-turn caps.\n this.#stopPolicy.reset()\n for (let turn = 1; ; turn++) {\n void this.emit(\"turn-start\", { turn })\n const result = await this.#turn()\n const reason = result.reason ? toError(result.reason).message : undefined\n void this.emit(\"turn-end\", { outcome: result.kind, reason, turn })\n\n if (result.kind === \"natural\") {\n // Drain follow-up queue if anything arrived during the turn.\n if (this.#appendQueue.length > 0) {\n for (const m of this.#appendQueue.splice(0)) await this.session.add(m)\n continue\n }\n // Wakeups (or any other inject) that fired while the agent was\n // streaming / running-tools land in `#injectQueue`. If we stop\n // here without giving them a turn, they get drained at the top\n // of the next unrelated user turn — surfacing the wakeup AFTER\n // the user's reply, with no `status=\"cancelled\"` marker since it\n // never went through `#cancelAllWakeups`. Continue the loop so\n // `step()` drains them next iteration.\n if (this.#injectQueue.length > 0) continue\n }\n return this.#stop(result.kind, result.reason)\n }\n }\n\n #stop(kind: AgentStopKind, reason?: unknown): AgentStopKind {\n this.#lastStop = {\n error: reason instanceof Error ? reason : undefined,\n kind,\n reason: reason === undefined ? undefined : toError(reason).message,\n }\n this.#setStatus(kind === \"natural\" ? \"idle\" : \"paused\")\n void this.emit(\"stop\", {\n status: this.#status,\n usage: this.usage,\n ...this.#lastStop,\n })\n return kind\n }\n\n /** Build the per-step `ToolContext` handed to each tool. The session\n * cwd, an abort signal scoped to the in-flight stream, and the\n * long-running spawn registry are all surfaced here. */\n async #toolContext(): Promise<ToolContext> {\n const masker = await this.#ctx.masker()\n return {\n agent: this,\n bash: this.#opts.bash,\n cwd: this.cwd,\n isMasked: (msgId: string, partIdx?: number) => masker?.isMasked(msgId, partIdx) ?? false,\n messages: this.session.messages,\n need: (scope, input) => this.#need(scope, input),\n perms: await this.ctx.permissions(),\n sessionDir: this.session.dir,\n signal: this.#abortController?.signal,\n swarm: () => this.#ctx.swarm(),\n tasks: this.#tasks,\n }\n }\n\n /** Implementation of `ctx.need(scope, input)`. Resolves on `allow`,\n * throws a `PERMISSION_DENIED` `AiError` on `deny`, and escalates\n * `ask` to `AgentOptions.allow` (treating it as `deny` when no\n * callback is configured). */\n async #need(scope: string, input: string): Promise<void> {\n const perms = await this.ctx.permissions()\n const r = perms.validate(scope, input)\n if (r.verdict === \"allow\") return\n if (r.verdict === \"ask\" && this.#opts.allow) {\n const ok = await this.#opts.allow({\n ask: r.ask,\n input,\n reason: r.reason,\n scope,\n suggestions: r.suggestions,\n })\n if (ok) return\n }\n throw new AiError({\n code: \"PERMISSION_DENIED\",\n data: { input, scope, suggestions: r.suggestions, verdict: r.verdict },\n message: r.reason,\n // `ask` verdicts that the user denied this turn are retryable —\n // they may add a rule later. Hard `deny` is not.\n retryable: r.verdict === \"ask\",\n })\n }\n\n /** Tear down session-scoped resources. Called on agent disposal — the\n * agent doesn't auto-dispose today, so callers (TUI on quit, headless\n * runner on completion) should invoke this explicitly. */\n async dispose(): Promise<void> {\n this.clearListeners()\n this.#cancelAllWakeups()\n await this.#tasks.killAll()\n }\n\n #setStatus(status: AgentStatus): void {\n const prev = this.#status\n if (prev === status) return\n // Cancel wakeups only when an EXTERNAL wake-up brings the loop back\n // to active (idle/paused → streaming). Mid-turn transitions\n // (running-tools → streaming) are the same turn continuing — a\n // wakeup the model just scheduled mid-tool-call would otherwise be\n // killed before it ever fires. The \"external wake-up\" cases that\n // *should* cancel: a `task-done` inject, a heartbeat, a user\n // message — all route through `inject` → `send` → `run` from the\n // idle/paused state, hitting this branch correctly.\n if (status === \"streaming\" && (prev === \"idle\" || prev === \"paused\")) {\n this.#cancelAllWakeups()\n }\n this.#status = status\n void this.emit(\"status\", { status })\n }\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { extractBashUsage, extractConversation, extractFileUsage, formatBashUsage, formatFileUsage, messageTail } from "./index.mjs";
1
+ import { a as extractConversation, c as formatFileUsage, i as extractBashUsage, l as messageTail, o as extractFileUsage, s as formatBashUsage } from "./src-fK6DL2bg.mjs";
2
2
  import { toXml } from "@zaly/ai";
3
3
  //#region src/compaction/prompt.ts
4
4
  const SYSTEM_PROMPT = `You are a context summarization assistant. You read a conversation transcript between a user and an AI coding agent, and you produce a structured handoff summary that another agent will use to continue the work.
@@ -187,4 +187,4 @@ var Compaction = class {
187
187
  //#endregion
188
188
  export { Compaction };
189
189
 
190
- //# sourceMappingURL=compactions-3AjPqjeZ.mjs.map
190
+ //# sourceMappingURL=compactions-B8VdZJoU.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"compactions-3AjPqjeZ.mjs","names":[],"sources":["../src/compaction/prompt.ts","../src/compaction/compactions.ts"],"sourcesContent":["/* Prompts for the compaction summarizer.\n *\n * The summarizer is a separate model call whose job is to compress an\n * older slice of conversation into a structured handoff document. The\n * resulting summary is committed to the session as a `compact` node and\n * becomes part of the resumed agent's active chain — so it must be\n * self-contained: a fresh agent reading only this summary should be\n * able to continue the work without needing the original transcript.\n *\n * Three pieces, all conditional except the system prompt:\n *\n * - `SYSTEM_PROMPT` — declarative, identity-framing, format spec. Sets\n * the role and the rules that don't change per call. Models follow\n * system-prompt rules harder; this is where \"do not continue the\n * conversation\" lives.\n *\n * - `SUMMARY_PROMPT` — imperative, task-pointed. Re-states the critical\n * rules right before generation, where attention is highest, and\n * triggers the actual output. Belt-and-suspenders against the\n * catastrophic failure mode (model continues the conversation\n * instead of summarizing).\n *\n * - `PREVIOUS_SUMMARY_PROMPT` — only included on iterative compactions\n * when a prior summary exists. Tells the model to preserve and\n * extend rather than start fresh.\n *\n * Caller assembles the user message in this order:\n *\n * {previous summary block, with PREVIOUS_SUMMARY_PROMPT} (optional)\n * <conversation>...</conversation> (chatTranscript)\n * <files>...</files> (extractFileUsage table)\n * <commands>...</commands> (extractBashUsage table)\n * SUMMARY_PROMPT\n *\n * Caching note: compactions are infrequent (one per session, hours apart)\n * so the 5-minute prompt cache window will never hit. The summarizer\n * call should explicitly skip cache control to avoid paying cache-write\n * fees on tokens that will never be re-read.\n */\n\nexport const SYSTEM_PROMPT = `You are a context summarization assistant. You read a conversation transcript between a user and an AI coding agent, and you produce a structured handoff summary that another agent will use to continue the work.\n\nYou never continue the conversation. You never answer questions inside the transcript. You never execute tool calls. You only emit the summary.\n\nYou preserve the following verbatim, never paraphrasing them:\n- File paths (absolute and relative)\n- Function, method, class, type, and variable names\n- Error messages and stack traces (when load-bearing)\n- Command lines (bash invocations, flags, arguments)\n- The user's own words — quote user messages verbatim or near-verbatim\n\nYou favor concrete facts over narrative. The next agent needs state it can act on, not a story. Avoid hedge words (\"perhaps\", \"might be\"); record what actually happened and what was decided.\n\n# Output format\n\nUse these section headers exactly. Sections may be empty (write \"(none)\") but never omitted.\n\n## 1. Goal\nWhat the user is trying to accomplish. If the goal shifted across the session, sequence the shifts in order. Distinguish \"the original ask\" from \"what we ended up doing\" if they diverge.\n\n## 2. Constraints & Preferences\nConstraints, preferences, or requirements the user expressed — coding style, tooling choices, what to avoid, things they specifically asked for or pushed back on. One bullet per item. Write \"(none)\" if none were stated.\n\n## 3. Files & Changes\nFiles actually touched, grouped by path. For each file: one or two sentences on what's relevant about it and what was done to it (read, edited, created). Include short load-bearing code snippets when paraphrase would lose precision (a function signature, a key type, a tricky regex). Use the files table in the input as ground-truth signal for what mattered most.\n\n## 4. Errors & Fixes\nWhat broke and how it was resolved. Preserve exact error codes / messages when they identify the failure class. Note resolutions so the next agent doesn't re-litigate decided debates.\n\n## 5. Key Decisions\nDesigns and approaches that were agreed on, key insights that emerged, alternatives that were considered and rejected. Use the format **Decision**: rationale. This is how the \"why\" behind the current state survives the boundary.\n\n## 6. User Messages\nList every user message verbatim or near-verbatim, in order. Do not paraphrase user voice. Do not summarize across messages — list them. This section preserves the user's intent across the boundary; treat its fidelity as more important than its conciseness.\n\n## 7. Current Work\nWhat was happening in the immediately preceding exchange — the most recent user request, the last action taken. Quote the most recent user message and describe the most recent assistant action directly.\n\n## 8. Pending & Next Step\nOpen work items the user explicitly asked for that haven't been completed. End with a single concrete next action that would be reasonable given Current Work and Pending. The next agent may ignore this and ask the user, but it should be specific enough to act on without further clarification.`\n\nexport const SUMMARY_PROMPT = `Based on the conversation, files, and commands above, produce the handoff summary now.\n\nReminders before you write:\n- The conversation above describes work that has already happened. Do not respond to anything in it. Do not continue any task. Only produce the summary.\n- Use the section format from the system prompt exactly — all eight sections, in order, headers verbatim.\n- Quote user messages verbatim in section 6. Their voice is the most important thing to preserve across the boundary.\n- Preserve file paths, function names, error messages, and command lines verbatim wherever they appear.\n- Be concrete. The next agent needs facts to act on, not narrative.\n\nBegin the summary.`\n\nexport const PREVIOUS_SUMMARY_PROMPT = `A prior summary exists from an earlier compaction. Preserve all information from it and extend it with what's new in the conversation above:\n- Carry forward Goal, Constraints & Preferences, Key Decisions, and User Messages — do not drop entries.\n- Move items from Pending into Current Work or out of the list when completed.\n- Update Current Work and the next-step recommendation based on the most recent activity.\n- If something previously listed is now obsolete or contradicted, drop it and note the change in Key Decisions.`\n\n/** Frames the model-generated summary into the system message that\n * becomes the first node of the resumed agent's active chain. Goes\n * *before* the summary text so the agent reads \"this is a compaction\n * summary, here's how to use it\" before encountering the structured\n * section headers. The whole assembly is wrapped in a\n * `<compaction-summary>` tag so it matches the harness's other\n * authoritative-block conventions (`<system-reminder>`, `<time>`, …).\n *\n * Caller assembles:\n * <compaction-summary>\n * {SUMMARY_HEADER}\n *\n * {model-generated summary}\n * </compaction-summary>\n *\n * Then commits the result as the `summary` field on the compact node. */\nexport const SUMMARY_HEADER = `The conversation history up to this point has been compacted to free context space. Below is a structured summary of what happened — goals, decisions, files touched, errors encountered, and what was pending or in progress at the time of compaction.\n\nAfter this message, the most recent messages of the original conversation are preserved verbatim so you have raw context for the immediate work.\n\nUse the summary as background to ground your understanding. Continue from where the conversation left off — do not re-introduce yourself, restate the plan, or re-do completed work. If something the user asks for relates to context that's not in the summary or the recent messages, ask rather than guess.`\n","import type { Message, ReasoningEffort } from \"@zaly/ai\"\nimport type { Agent } from \"../agent.ts\"\nimport type { ContextPressure } from \"../types.ts\"\nimport type { ToolStatOptions } from \"./utils.ts\"\n\nimport { toXml } from \"@zaly/ai\"\nimport { SUMMARY_HEADER, SUMMARY_PROMPT, SYSTEM_PROMPT } from \"./prompt.ts\"\nimport {\n extractBashUsage,\n extractConversation,\n extractFileUsage,\n formatBashUsage,\n formatFileUsage,\n messageTail,\n} from \"./utils.ts\"\n\nexport type CompactionOptions = {\n enabled: boolean\n threshold?: number\n /** Existing messages up to this many tokens will be preserved in the context */\n keepTokens: number\n maxToolResultLen: number\n summaryTokens: number\n bash: ToolStatOptions\n files: ToolStatOptions\n signal?: AbortSignal\n reasoning?: ReasoningEffort\n trigger?: \"manual\" | \"auto\"\n}\n\nconst defaults: CompactionOptions = {\n bash: { limit: 50, minCount: 2, minScore: 0.5, sort: \"score\" },\n enabled: true,\n files: { limit: 50, minCount: 1, minScore: 0.5, sort: \"score\" },\n keepTokens: 20_000,\n maxToolResultLen: 2000,\n reasoning: \"low\",\n summaryTokens: 10_000,\n threshold: 0.95,\n}\n\nexport class Compaction {\n #opts: CompactionOptions\n #agent: Agent\n\n constructor(agent: Agent, opts: Partial<CompactionOptions> = {}) {\n this.#agent = agent\n this.#opts = { ...defaults, ...opts }\n }\n\n async compact(pressure: ContextPressure): Promise<void> {\n const { session } = this.#agent\n\n const masker = await this.#agent.ctx.masker()\n\n const messages = masker ? await masker.mask(session.messages, pressure) : session.messages\n\n const now = performance.now()\n\n const tail = messageTail(messages, { keepTokens: this.#opts.keepTokens })\n const older = tail.length > 0 ? messages.slice(0, -tail.length) : messages\n\n if (older.length === 0) return\n\n const conversation = extractConversation(\n // Conversation summary is based on the older messages, not the tail — the tail is what we keep\n older,\n { maxToolResultLen: this.#opts.maxToolResultLen }\n )\n\n const bashUsage = formatBashUsage(extractBashUsage(messages, this.#opts.bash))\n const fileUsage = formatFileUsage(extractFileUsage(messages, this.#opts.files))\n\n const request: Message<\"user\"> = {\n content: [\n { text: conversation, type: \"text\" },\n { text: fileUsage, type: \"text\" },\n { text: bashUsage, type: \"text\" },\n { text: SUMMARY_PROMPT, type: \"text\" },\n ],\n role: \"user\",\n }\n\n const summary = await this.#summarize(request)\n // The bash + file usage tables are deterministic, ground-truth signals\n // — pricier to ask the model to reproduce than to copy verbatim. Carry\n // them through to the resumed agent so it has the same working-set\n // visibility the summarizer had, without paying for output tokens.\n const summaryMessage: Message<\"system\"> = {\n content: [\n { text: SUMMARY_HEADER, type: \"text\" },\n { text: toXml(summary, \"compaction-summary\", { indent: false }), type: \"text\" },\n { text: fileUsage, type: \"text\" },\n { text: bashUsage, type: \"text\" },\n ],\n meta: { kind: \"compaction-summary\" },\n role: \"system\",\n }\n await session.compact({\n durationMs: Math.round(performance.now() - now),\n preTokens: this.#agent.contextSize,\n summary: summaryMessage,\n tail: tail.length,\n trigger: this.#opts.trigger,\n })\n masker?.reset()\n }\n\n async #summarize(message: Message<\"user\">): Promise<string> {\n const model = this.#agent.model\n if (!model) throw new Error(\"model must be loaded to compact\")\n const m = await model.stream(\n {\n messages: [message],\n prompt: [SYSTEM_PROMPT],\n },\n {\n caching: false,\n maxTokens: this.#opts.summaryTokens,\n reasoning: this.#opts.reasoning ? { effort: this.#opts.reasoning } : undefined,\n signal: this.#opts.signal,\n }\n )\n if (typeof m.content === \"string\") return m.content\n const parts = m.content.filter((p) => p.type === \"text\").map((p) => p.text)\n return parts.join(\"\\n\")\n }\n}\n"],"mappings":";;;AAwCA,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC7B,MAAa,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC9B,MAAa,iBAAiB;;;;;;;ACpF9B,MAAM,WAA8B;CAClC,MAAM;EAAE,OAAO;EAAI,UAAU;EAAG,UAAU;EAAK,MAAM;CAAQ;CAC7D,SAAS;CACT,OAAO;EAAE,OAAO;EAAI,UAAU;EAAG,UAAU;EAAK,MAAM;CAAQ;CAC9D,YAAY;CACZ,kBAAkB;CAClB,WAAW;CACX,eAAe;CACf,WAAW;AACb;AAEA,IAAa,aAAb,MAAwB;CACtB;CACA;CAEA,YAAY,OAAc,OAAmC,CAAC,GAAG;EAC/D,KAAK,SAAS;EACd,KAAK,QAAQ;GAAE,GAAG;GAAU,GAAG;EAAK;CACtC;CAEA,MAAM,QAAQ,UAA0C;EACtD,MAAM,EAAE,YAAY,KAAK;EAEzB,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO;EAE5C,MAAM,WAAW,SAAS,MAAM,OAAO,KAAK,QAAQ,UAAU,QAAQ,IAAI,QAAQ;EAElF,MAAM,MAAM,YAAY,IAAI;EAE5B,MAAM,OAAO,YAAY,UAAU,EAAE,YAAY,KAAK,MAAM,WAAW,CAAC;EACxE,MAAM,QAAQ,KAAK,SAAS,IAAI,SAAS,MAAM,GAAG,CAAC,KAAK,MAAM,IAAI;EAElE,IAAI,MAAM,WAAW,GAAG;EAExB,MAAM,eAAe,oBAEnB,OACA,EAAE,kBAAkB,KAAK,MAAM,iBAAiB,CAClD;EAEA,MAAM,YAAY,gBAAgB,iBAAiB,UAAU,KAAK,MAAM,IAAI,CAAC;EAC7E,MAAM,YAAY,gBAAgB,iBAAiB,UAAU,KAAK,MAAM,KAAK,CAAC;EAE9E,MAAM,UAA2B;GAC/B,SAAS;IACP;KAAE,MAAM;KAAc,MAAM;IAAO;IACnC;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAgB,MAAM;IAAO;GACvC;GACA,MAAM;EACR;EAEA,MAAM,UAAU,MAAM,KAAK,WAAW,OAAO;EAK7C,MAAM,iBAAoC;GACxC,SAAS;IACP;KAAE,MAAM;KAAgB,MAAM;IAAO;IACrC;KAAE,MAAM,MAAM,SAAS,sBAAsB,EAAE,QAAQ,MAAM,CAAC;KAAG,MAAM;IAAO;IAC9E;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAW,MAAM;IAAO;GAClC;GACA,MAAM,EAAE,MAAM,qBAAqB;GACnC,MAAM;EACR;EACA,MAAM,QAAQ,QAAQ;GACpB,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG;GAC9C,WAAW,KAAK,OAAO;GACvB,SAAS;GACT,MAAM,KAAK;GACX,SAAS,KAAK,MAAM;EACtB,CAAC;EACD,QAAQ,MAAM;CAChB;CAEA,MAAM,WAAW,SAA2C;EAC1D,MAAM,QAAQ,KAAK,OAAO;EAC1B,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,iCAAiC;EAC7D,MAAM,IAAI,MAAM,MAAM,OACpB;GACE,UAAU,CAAC,OAAO;GAClB,QAAQ,CAAC,aAAa;EACxB,GACA;GACE,SAAS;GACT,WAAW,KAAK,MAAM;GACtB,WAAW,KAAK,MAAM,YAAY,EAAE,QAAQ,KAAK,MAAM,UAAU,IAAI,KAAA;GACrE,QAAQ,KAAK,MAAM;EACrB,CACF;EACA,IAAI,OAAO,EAAE,YAAY,UAAU,OAAO,EAAE;EAE5C,OADc,EAAE,QAAQ,QAAQ,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,IAC3D,CAAC,CAAC,KAAK,IAAI;CACxB;AACF"}
1
+ {"version":3,"file":"compactions-B8VdZJoU.mjs","names":[],"sources":["../src/compaction/prompt.ts","../src/compaction/compactions.ts"],"sourcesContent":["/* Prompts for the compaction summarizer.\n *\n * The summarizer is a separate model call whose job is to compress an\n * older slice of conversation into a structured handoff document. The\n * resulting summary is committed to the session as a `compact` node and\n * becomes part of the resumed agent's active chain — so it must be\n * self-contained: a fresh agent reading only this summary should be\n * able to continue the work without needing the original transcript.\n *\n * Three pieces, all conditional except the system prompt:\n *\n * - `SYSTEM_PROMPT` — declarative, identity-framing, format spec. Sets\n * the role and the rules that don't change per call. Models follow\n * system-prompt rules harder; this is where \"do not continue the\n * conversation\" lives.\n *\n * - `SUMMARY_PROMPT` — imperative, task-pointed. Re-states the critical\n * rules right before generation, where attention is highest, and\n * triggers the actual output. Belt-and-suspenders against the\n * catastrophic failure mode (model continues the conversation\n * instead of summarizing).\n *\n * - `PREVIOUS_SUMMARY_PROMPT` — only included on iterative compactions\n * when a prior summary exists. Tells the model to preserve and\n * extend rather than start fresh.\n *\n * Caller assembles the user message in this order:\n *\n * {previous summary block, with PREVIOUS_SUMMARY_PROMPT} (optional)\n * <conversation>...</conversation> (chatTranscript)\n * <files>...</files> (extractFileUsage table)\n * <commands>...</commands> (extractBashUsage table)\n * SUMMARY_PROMPT\n *\n * Caching note: compactions are infrequent (one per session, hours apart)\n * so the 5-minute prompt cache window will never hit. The summarizer\n * call should explicitly skip cache control to avoid paying cache-write\n * fees on tokens that will never be re-read.\n */\n\nexport const SYSTEM_PROMPT = `You are a context summarization assistant. You read a conversation transcript between a user and an AI coding agent, and you produce a structured handoff summary that another agent will use to continue the work.\n\nYou never continue the conversation. You never answer questions inside the transcript. You never execute tool calls. You only emit the summary.\n\nYou preserve the following verbatim, never paraphrasing them:\n- File paths (absolute and relative)\n- Function, method, class, type, and variable names\n- Error messages and stack traces (when load-bearing)\n- Command lines (bash invocations, flags, arguments)\n- The user's own words — quote user messages verbatim or near-verbatim\n\nYou favor concrete facts over narrative. The next agent needs state it can act on, not a story. Avoid hedge words (\"perhaps\", \"might be\"); record what actually happened and what was decided.\n\n# Output format\n\nUse these section headers exactly. Sections may be empty (write \"(none)\") but never omitted.\n\n## 1. Goal\nWhat the user is trying to accomplish. If the goal shifted across the session, sequence the shifts in order. Distinguish \"the original ask\" from \"what we ended up doing\" if they diverge.\n\n## 2. Constraints & Preferences\nConstraints, preferences, or requirements the user expressed — coding style, tooling choices, what to avoid, things they specifically asked for or pushed back on. One bullet per item. Write \"(none)\" if none were stated.\n\n## 3. Files & Changes\nFiles actually touched, grouped by path. For each file: one or two sentences on what's relevant about it and what was done to it (read, edited, created). Include short load-bearing code snippets when paraphrase would lose precision (a function signature, a key type, a tricky regex). Use the files table in the input as ground-truth signal for what mattered most.\n\n## 4. Errors & Fixes\nWhat broke and how it was resolved. Preserve exact error codes / messages when they identify the failure class. Note resolutions so the next agent doesn't re-litigate decided debates.\n\n## 5. Key Decisions\nDesigns and approaches that were agreed on, key insights that emerged, alternatives that were considered and rejected. Use the format **Decision**: rationale. This is how the \"why\" behind the current state survives the boundary.\n\n## 6. User Messages\nList every user message verbatim or near-verbatim, in order. Do not paraphrase user voice. Do not summarize across messages — list them. This section preserves the user's intent across the boundary; treat its fidelity as more important than its conciseness.\n\n## 7. Current Work\nWhat was happening in the immediately preceding exchange — the most recent user request, the last action taken. Quote the most recent user message and describe the most recent assistant action directly.\n\n## 8. Pending & Next Step\nOpen work items the user explicitly asked for that haven't been completed. End with a single concrete next action that would be reasonable given Current Work and Pending. The next agent may ignore this and ask the user, but it should be specific enough to act on without further clarification.`\n\nexport const SUMMARY_PROMPT = `Based on the conversation, files, and commands above, produce the handoff summary now.\n\nReminders before you write:\n- The conversation above describes work that has already happened. Do not respond to anything in it. Do not continue any task. Only produce the summary.\n- Use the section format from the system prompt exactly — all eight sections, in order, headers verbatim.\n- Quote user messages verbatim in section 6. Their voice is the most important thing to preserve across the boundary.\n- Preserve file paths, function names, error messages, and command lines verbatim wherever they appear.\n- Be concrete. The next agent needs facts to act on, not narrative.\n\nBegin the summary.`\n\nexport const PREVIOUS_SUMMARY_PROMPT = `A prior summary exists from an earlier compaction. Preserve all information from it and extend it with what's new in the conversation above:\n- Carry forward Goal, Constraints & Preferences, Key Decisions, and User Messages — do not drop entries.\n- Move items from Pending into Current Work or out of the list when completed.\n- Update Current Work and the next-step recommendation based on the most recent activity.\n- If something previously listed is now obsolete or contradicted, drop it and note the change in Key Decisions.`\n\n/** Frames the model-generated summary into the system message that\n * becomes the first node of the resumed agent's active chain. Goes\n * *before* the summary text so the agent reads \"this is a compaction\n * summary, here's how to use it\" before encountering the structured\n * section headers. The whole assembly is wrapped in a\n * `<compaction-summary>` tag so it matches the harness's other\n * authoritative-block conventions (`<system-reminder>`, `<time>`, …).\n *\n * Caller assembles:\n * <compaction-summary>\n * {SUMMARY_HEADER}\n *\n * {model-generated summary}\n * </compaction-summary>\n *\n * Then commits the result as the `summary` field on the compact node. */\nexport const SUMMARY_HEADER = `The conversation history up to this point has been compacted to free context space. Below is a structured summary of what happened — goals, decisions, files touched, errors encountered, and what was pending or in progress at the time of compaction.\n\nAfter this message, the most recent messages of the original conversation are preserved verbatim so you have raw context for the immediate work.\n\nUse the summary as background to ground your understanding. Continue from where the conversation left off — do not re-introduce yourself, restate the plan, or re-do completed work. If something the user asks for relates to context that's not in the summary or the recent messages, ask rather than guess.`\n","import type { Message, ReasoningEffort } from \"@zaly/ai\"\nimport type { Agent } from \"../agent.ts\"\nimport type { ContextPressure } from \"../types.ts\"\nimport type { ToolStatOptions } from \"./utils.ts\"\n\nimport { toXml } from \"@zaly/ai\"\nimport { SUMMARY_HEADER, SUMMARY_PROMPT, SYSTEM_PROMPT } from \"./prompt.ts\"\nimport {\n extractBashUsage,\n extractConversation,\n extractFileUsage,\n formatBashUsage,\n formatFileUsage,\n messageTail,\n} from \"./utils.ts\"\n\nexport type CompactionOptions = {\n enabled: boolean\n threshold?: number\n /** Existing messages up to this many tokens will be preserved in the context */\n keepTokens: number\n maxToolResultLen: number\n summaryTokens: number\n bash: ToolStatOptions\n files: ToolStatOptions\n signal?: AbortSignal\n reasoning?: ReasoningEffort\n trigger?: \"manual\" | \"auto\"\n}\n\nconst defaults: CompactionOptions = {\n bash: { limit: 50, minCount: 2, minScore: 0.5, sort: \"score\" },\n enabled: true,\n files: { limit: 50, minCount: 1, minScore: 0.5, sort: \"score\" },\n keepTokens: 20_000,\n maxToolResultLen: 2000,\n reasoning: \"low\",\n summaryTokens: 10_000,\n threshold: 0.95,\n}\n\nexport class Compaction {\n #opts: CompactionOptions\n #agent: Agent\n\n constructor(agent: Agent, opts: Partial<CompactionOptions> = {}) {\n this.#agent = agent\n this.#opts = { ...defaults, ...opts }\n }\n\n async compact(pressure: ContextPressure): Promise<void> {\n const { session } = this.#agent\n\n const masker = await this.#agent.ctx.masker()\n\n const messages = masker ? await masker.mask(session.messages, pressure) : session.messages\n\n const now = performance.now()\n\n const tail = messageTail(messages, { keepTokens: this.#opts.keepTokens })\n const older = tail.length > 0 ? messages.slice(0, -tail.length) : messages\n\n if (older.length === 0) return\n\n const conversation = extractConversation(\n // Conversation summary is based on the older messages, not the tail — the tail is what we keep\n older,\n { maxToolResultLen: this.#opts.maxToolResultLen }\n )\n\n const bashUsage = formatBashUsage(extractBashUsage(messages, this.#opts.bash))\n const fileUsage = formatFileUsage(extractFileUsage(messages, this.#opts.files))\n\n const request: Message<\"user\"> = {\n content: [\n { text: conversation, type: \"text\" },\n { text: fileUsage, type: \"text\" },\n { text: bashUsage, type: \"text\" },\n { text: SUMMARY_PROMPT, type: \"text\" },\n ],\n role: \"user\",\n }\n\n const summary = await this.#summarize(request)\n // The bash + file usage tables are deterministic, ground-truth signals\n // — pricier to ask the model to reproduce than to copy verbatim. Carry\n // them through to the resumed agent so it has the same working-set\n // visibility the summarizer had, without paying for output tokens.\n const summaryMessage: Message<\"system\"> = {\n content: [\n { text: SUMMARY_HEADER, type: \"text\" },\n { text: toXml(summary, \"compaction-summary\", { indent: false }), type: \"text\" },\n { text: fileUsage, type: \"text\" },\n { text: bashUsage, type: \"text\" },\n ],\n meta: { kind: \"compaction-summary\" },\n role: \"system\",\n }\n await session.compact({\n durationMs: Math.round(performance.now() - now),\n preTokens: this.#agent.contextSize,\n summary: summaryMessage,\n tail: tail.length,\n trigger: this.#opts.trigger,\n })\n masker?.reset()\n }\n\n async #summarize(message: Message<\"user\">): Promise<string> {\n const model = this.#agent.model\n if (!model) throw new Error(\"model must be loaded to compact\")\n const m = await model.stream(\n {\n messages: [message],\n prompt: [SYSTEM_PROMPT],\n },\n {\n caching: false,\n maxTokens: this.#opts.summaryTokens,\n reasoning: this.#opts.reasoning ? { effort: this.#opts.reasoning } : undefined,\n signal: this.#opts.signal,\n }\n )\n if (typeof m.content === \"string\") return m.content\n const parts = m.content.filter((p) => p.type === \"text\").map((p) => p.text)\n return parts.join(\"\\n\")\n }\n}\n"],"mappings":";;;AAwCA,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyC7B,MAAa,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;AAiC9B,MAAa,iBAAiB;;;;;;;ACpF9B,MAAM,WAA8B;CAClC,MAAM;EAAE,OAAO;EAAI,UAAU;EAAG,UAAU;EAAK,MAAM;CAAQ;CAC7D,SAAS;CACT,OAAO;EAAE,OAAO;EAAI,UAAU;EAAG,UAAU;EAAK,MAAM;CAAQ;CAC9D,YAAY;CACZ,kBAAkB;CAClB,WAAW;CACX,eAAe;CACf,WAAW;AACb;AAEA,IAAa,aAAb,MAAwB;CACtB;CACA;CAEA,YAAY,OAAc,OAAmC,CAAC,GAAG;EAC/D,KAAK,SAAS;EACd,KAAK,QAAQ;GAAE,GAAG;GAAU,GAAG;EAAK;CACtC;CAEA,MAAM,QAAQ,UAA0C;EACtD,MAAM,EAAE,YAAY,KAAK;EAEzB,MAAM,SAAS,MAAM,KAAK,OAAO,IAAI,OAAO;EAE5C,MAAM,WAAW,SAAS,MAAM,OAAO,KAAK,QAAQ,UAAU,QAAQ,IAAI,QAAQ;EAElF,MAAM,MAAM,YAAY,IAAI;EAE5B,MAAM,OAAO,YAAY,UAAU,EAAE,YAAY,KAAK,MAAM,WAAW,CAAC;EACxE,MAAM,QAAQ,KAAK,SAAS,IAAI,SAAS,MAAM,GAAG,CAAC,KAAK,MAAM,IAAI;EAElE,IAAI,MAAM,WAAW,GAAG;EAExB,MAAM,eAAe,oBAEnB,OACA,EAAE,kBAAkB,KAAK,MAAM,iBAAiB,CAClD;EAEA,MAAM,YAAY,gBAAgB,iBAAiB,UAAU,KAAK,MAAM,IAAI,CAAC;EAC7E,MAAM,YAAY,gBAAgB,iBAAiB,UAAU,KAAK,MAAM,KAAK,CAAC;EAE9E,MAAM,UAA2B;GAC/B,SAAS;IACP;KAAE,MAAM;KAAc,MAAM;IAAO;IACnC;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAgB,MAAM;IAAO;GACvC;GACA,MAAM;EACR;EAEA,MAAM,UAAU,MAAM,KAAK,WAAW,OAAO;EAK7C,MAAM,iBAAoC;GACxC,SAAS;IACP;KAAE,MAAM;KAAgB,MAAM;IAAO;IACrC;KAAE,MAAM,MAAM,SAAS,sBAAsB,EAAE,QAAQ,MAAM,CAAC;KAAG,MAAM;IAAO;IAC9E;KAAE,MAAM;KAAW,MAAM;IAAO;IAChC;KAAE,MAAM;KAAW,MAAM;IAAO;GAClC;GACA,MAAM,EAAE,MAAM,qBAAqB;GACnC,MAAM;EACR;EACA,MAAM,QAAQ,QAAQ;GACpB,YAAY,KAAK,MAAM,YAAY,IAAI,IAAI,GAAG;GAC9C,WAAW,KAAK,OAAO;GACvB,SAAS;GACT,MAAM,KAAK;GACX,SAAS,KAAK,MAAM;EACtB,CAAC;EACD,QAAQ,MAAM;CAChB;CAEA,MAAM,WAAW,SAA2C;EAC1D,MAAM,QAAQ,KAAK,OAAO;EAC1B,IAAI,CAAC,OAAO,MAAM,IAAI,MAAM,iCAAiC;EAC7D,MAAM,IAAI,MAAM,MAAM,OACpB;GACE,UAAU,CAAC,OAAO;GAClB,QAAQ,CAAC,aAAa;EACxB,GACA;GACE,SAAS;GACT,WAAW,KAAK,MAAM;GACtB,WAAW,KAAK,MAAM,YAAY,EAAE,QAAQ,KAAK,MAAM,UAAU,IAAI,KAAA;GACrE,QAAQ,KAAK,MAAM;EACrB,CACF;EACA,IAAI,OAAO,EAAE,YAAY,UAAU,OAAO,EAAE;EAE5C,OADc,EAAE,QAAQ,QAAQ,MAAM,EAAE,SAAS,MAAM,CAAC,CAAC,KAAK,MAAM,EAAE,IAC3D,CAAC,CAAC,KAAK,IAAI;CACxB;AACF"}
@@ -1,4 +1,3 @@
1
- import { assertFresh } from "./read-Djsx2NZl.mjs";
2
1
  import { normPath } from "@zaly/shared";
3
2
  import { AiError, defineTool } from "@zaly/ai";
4
3
  import { readFile, stat, writeFile } from "node:fs/promises";
@@ -25,18 +24,16 @@ import { detectEol, normalizeEol } from "@zaly/shared/text";
25
24
  */
26
25
  const editTool = defineTool({
27
26
  name: "edit",
28
- desc: "Apply an exact-text replacement to a file, with optional extra replacements.. All matches are resolved against the original content (not sequentially), each `oldText` must be unique in the file, and edits must not overlap. Atomic: all-or-nothing.",
27
+ desc: "Apply one or more exact-text replacements to a file. All matches are resolved against the original content (not sequentially), each `oldText` must be unique, and edits must not overlap. Atomic: all-or-nothing.",
29
28
  params: Type.Object({
30
29
  path: Type.String({ description: "Path to the file. Absolute or cwd-relative." }),
31
- oldText: Type.String({ description: "Exact text to find. Must occur once in the file." }),
32
- newText: Type.String({ description: "Replacement text." }),
33
- edits: Type.Optional(Type.Array(Type.Object({
34
- oldText: Type.String({ description: "Old text" }),
35
- newText: Type.String({ description: "New text" })
30
+ edits: Type.Array(Type.Object({
31
+ oldText: Type.String({ description: "Exact text to find. Must occur once." }),
32
+ newText: Type.String({ description: "Replacement text." })
36
33
  }), {
37
- description: "Extra edits to apply atomically.",
34
+ description: "Edits to apply atomically.",
38
35
  minItems: 1
39
- }))
36
+ })
40
37
  }),
41
38
  async preflight(args, ctx) {
42
39
  const path = normPath(ctx.cwd, args.path);
@@ -44,11 +41,6 @@ const editTool = defineTool({
44
41
  },
45
42
  async call(args, ctx) {
46
43
  const path = normPath(ctx.cwd, args.path);
47
- const edits = [{
48
- newText: args.newText,
49
- oldText: args.oldText
50
- }, ...args.edits ?? []];
51
- assertFresh(path, ctx);
52
44
  const original = await readFile(path, "utf8").catch((error) => {
53
45
  throw new AiError({
54
46
  cause: error,
@@ -56,7 +48,7 @@ const editTool = defineTool({
56
48
  message: `cannot edit ${path}: file not found`
57
49
  });
58
50
  });
59
- const updated = applyEdits(original, edits, path);
51
+ const updated = applyEdits(original, args.edits, path);
60
52
  await writeFile(path, updated, "utf8");
61
53
  ctx.meta = {
62
54
  content: updated,
@@ -69,7 +61,7 @@ const editTool = defineTool({
69
61
  const lines = countLines(updated);
70
62
  return {
71
63
  bytes,
72
- edits: edits.length,
64
+ edits: args.edits.length,
73
65
  lines,
74
66
  ok: true,
75
67
  path
@@ -167,4 +159,4 @@ function preview(text) {
167
159
  //#endregion
168
160
  export { editTool };
169
161
 
170
- //# sourceMappingURL=edit-Qxl4w1C2.mjs.map
162
+ //# sourceMappingURL=edit-D4yaBK1K.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edit-D4yaBK1K.mjs","names":[],"sources":["../src/tools/edit.ts"],"sourcesContent":["import type { ToolContext } from \"@zaly/ai\"\nimport type { FileMeta } from \"../utils/file.ts\"\n\nimport { AiError, defineTool } from \"@zaly/ai\"\nimport { normPath } from \"@zaly/shared\"\nimport { detectEol, normalizeEol } from \"@zaly/shared/text\"\nimport { readFile, stat, writeFile } from \"node:fs/promises\"\nimport { Type } from \"typebox\"\n\nexport type EditTool = typeof editTool\nexport type EditToolMeta = FileMeta & {\n /** File content before any edit was applied. */\n original: string\n /** File content after all edits were applied — exactly what's now on disk. */\n content: string\n}\n\n/**\n * Apply one or more exact-text replacements to a file.\n *\n * Semantics:\n * - All `oldText` matches are resolved against the *original* file\n * content. Edits are not applied sequentially — so edit #1's\n * `newText` cannot accidentally produce a match for edit #2's\n * `oldText`.\n * - Each `oldText` must occur exactly once in the file (uniqueness\n * forces the model to disambiguate via context rather than guessing).\n * - Edits must not overlap. Nearby changes should be combined into a\n * single edit covering the surrounding block.\n * - The file is rewritten atomically: either every edit applies\n * cleanly or none do.\n *\n * Use `read` first to inspect the file. Keep `oldText` minimal but\n * uniquely identifying. For full-file rewrites, use `write` instead.\n */\n// oxlint-disable-next-line sort-keys -- semantic field order: name, desc, params, call\nexport const editTool = defineTool({\n name: \"edit\",\n desc:\n \"Apply one or more exact-text replacements to a file. All matches are resolved \" +\n \"against the original content (not sequentially), each `oldText` must be unique, \" +\n \"and edits must not overlap. Atomic: all-or-nothing.\",\n // oxlint-disable-next-line sort-keys -- semantic param order\n params: Type.Object({\n path: Type.String({ description: \"Path to the file. Absolute or cwd-relative.\" }),\n edits: Type.Array(\n // oxlint-disable-next-line sort-keys -- semantic order: oldText then newText\n Type.Object({\n oldText: Type.String({ description: \"Exact text to find. Must occur once.\" }),\n newText: Type.String({ description: \"Replacement text.\" }),\n }),\n { description: \"Edits to apply atomically.\", minItems: 1 }\n ),\n }),\n\n async preflight(args, ctx: ToolContext<EditToolMeta>) {\n const path = normPath(ctx.cwd, args.path)\n // Edits ride the `write` scope — they're mutations.\n await ctx.need?.(\"write\", path)\n },\n\n async call(\n args,\n ctx: ToolContext<EditToolMeta>\n ): Promise<{ ok: true; path: string; bytes: number; lines: number; edits: number }> {\n const path = normPath(ctx.cwd, args.path)\n\n const original = await readFile(path, \"utf8\").catch((error: unknown) => {\n throw new AiError({\n cause: error,\n code: \"FILE_NOT_FOUND\",\n message: `cannot edit ${path}: file not found`,\n })\n })\n\n const updated = applyEdits(original, args.edits, path)\n await writeFile(path, updated, \"utf8\")\n const fstat = await stat(path)\n ctx.meta = { content: updated, kind: \"edit\", mtime: fstat.mtimeMs, original, path }\n\n const bytes = Buffer.byteLength(updated, \"utf8\")\n const lines = countLines(updated)\n return { bytes, edits: args.edits.length, lines, ok: true, path }\n },\n})\n\ninterface EditSpec {\n oldText: string\n newText: string\n}\n\n/** Resolve all edit positions against `original`, validate uniqueness +\n * non-overlap, then splice the replacements in.\n *\n * Line-ending fidelity: the model always emits LF, but the file may be\n * CRLF. We detect the file's eol once, then normalize each `oldText` /\n * `newText` to that eol before matching/splicing. The result has the\n * same line-ending style as the original file. */\nfunction applyEdits(original: string, edits: readonly EditSpec[], path: string): string {\n const eol = detectEol({ path, text: original })\n // 1. Locate every oldText. Reject not-found and non-unique up-front so\n // the model gets a precise error per failing edit rather than a\n // \"patch failed at line N\" mystery.\n const matches: { start: number; end: number; newText: string; spec: EditSpec }[] = []\n for (const [i, edit] of edits.entries()) {\n const oldText = normalizeEol(edit.oldText, { eol })\n const newText = normalizeEol(edit.newText, { eol })\n if (oldText === \"\") {\n throw new AiError({\n code: \"EMPTY_OLD_TEXT\",\n data: { editIndex: i },\n message: `edit #${i}: oldText is empty (use \\`write\\` to create or fully replace a file)`,\n })\n }\n const first = original.indexOf(oldText)\n if (first === -1) {\n throw new AiError({\n code: \"NOT_FOUND\",\n data: { editIndex: i, path, snippet: preview(oldText) },\n message: `edit #${i}: oldText not found in ${path}`,\n })\n }\n const next = original.indexOf(oldText, first + 1)\n if (next !== -1) {\n throw new AiError({\n code: \"NOT_UNIQUE\",\n data: { editIndex: i, occurrences: countOccurrences(original, oldText), path },\n message:\n `edit #${i}: oldText matches multiple locations in ${path}. ` +\n `Add surrounding context to make it unique.`,\n })\n }\n matches.push({\n end: first + oldText.length,\n newText,\n spec: edit,\n start: first,\n })\n }\n\n // 2. Sort by start; verify no overlap. Equal start positions are\n // impossible because oldText is unique.\n matches.sort((a, b) => a.start - b.start)\n for (let i = 1; i < matches.length; i++) {\n if (matches[i].start < matches[i - 1].end) {\n throw new AiError({\n code: \"OVERLAP\",\n data: { a: matches[i - 1], b: matches[i] },\n message:\n `edits overlap in ${path}. ` +\n `Combine adjacent changes into a single edit covering both.`,\n })\n }\n }\n\n // 3. Splice. Walk the original and emit untouched-prefix + replacement\n // chunks; cheaper than N independent string replacements.\n const out: string[] = []\n let cursor = 0\n for (const m of matches) {\n out.push(original.slice(cursor, m.start), m.newText)\n cursor = m.end\n }\n out.push(original.slice(cursor))\n return out.join(\"\")\n}\n\n/** Count lines the way an editor would — non-empty trailing line counts;\n * a trailing newline doesn't add a phantom blank line. */\nfunction countLines(content: string): number {\n if (content === \"\") return 0\n let n = 1\n for (const c of content) if (c === \"\\n\") n++\n if (content.endsWith(\"\\n\")) n--\n return n\n}\n\nfunction countOccurrences(haystack: string, needle: string): number {\n let n = 0\n let from = 0\n for (;;) {\n const i = haystack.indexOf(needle, from)\n if (i === -1) return n\n n++\n from = i + needle.length\n }\n}\n\nfunction preview(text: string): string {\n const trimmed = text.trim().replaceAll(/\\s+/g, \" \")\n return trimmed.length > 80 ? `${trimmed.slice(0, 79)}…` : trimmed\n}\n"],"mappings":"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"}
@@ -77,7 +77,7 @@ const fetchTool = defineTool({
77
77
  body = JSON.parse(text);
78
78
  } catch {}
79
79
  if (args.jsonpath !== void 0 && body !== text) {
80
- const { JSONPath } = await import("./index-node-esm-DAnl9SaE.mjs");
80
+ const { JSONPath } = await import("./index-node-esm-CJsN8G51.mjs");
81
81
  body = JSONPath({
82
82
  json: body,
83
83
  path: args.jsonpath
@@ -108,4 +108,4 @@ const fetchTool = defineTool({
108
108
  //#endregion
109
109
  export { fetchTool };
110
110
 
111
- //# sourceMappingURL=fetch-DXuxAg_t.mjs.map
111
+ //# sourceMappingURL=fetch-VKhSWdEh.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"fetch-DXuxAg_t.mjs","names":[],"sources":["../src/tools/fetch.ts"],"sourcesContent":["import type { Attachment, MetaPart, TextPart } from \"@zaly/ai\"\n\nimport { defineTool, toAttachment } from \"@zaly/ai\"\nimport { Type } from \"typebox\"\n\nconst DEFAULT_MAX_BODY_BYTES = 256 * 1024 // 256 KB — generous for APIs, caps runaway HTML pages\n\n/**\n * Fetch a URL and return the response — JSON when the server speaks JSON,\n * raw text otherwise. Optional `jsonpath` filters the parsed JSON to a\n * subset, which keeps tokens down on chatty APIs.\n *\n * Scope: API endpoints. For HTML pages (browsing, content extraction),\n * the optional `@zaly/browser` package provides browser-backed tools that\n * return a structured a11y tree — much better than raw HTML through this\n * tool. Pointing this at an HTML URL works but the response is a string\n * blob the model has to slog through.\n *\n * Permissions: routes through the `fetch` scope when the agent wires up\n * tool-side permission checks. Until then, callers should restrict via\n * a wrapping permission layer (rule-based or explicit allowlist).\n */\n// oxlint-disable-next-line sort-keys\nexport const fetchTool = defineTool({\n desc:\n `Fetch a URL and return the response. JSON responses are parsed; use \\`jsonpath\\` (e.g. \\`$.items[*].name\\`) to extract a subset and minimise tokens.` +\n `Image & PDF are returned as attachments. Best for APIs — for web pages, use the browser tool.`,\n name: \"fetch\",\n parallel: true,\n\n // oxlint-disable-next-line sort-keys -- semantic param order: url, method, headers, query, body, jsonpath\n params: Type.Object({\n url: Type.String({ description: \"Absolute URL.\" }),\n method: Type.Optional(\n Type.Union(\n [\n Type.Literal(\"GET\"),\n Type.Literal(\"POST\"),\n Type.Literal(\"PUT\"),\n Type.Literal(\"PATCH\"),\n Type.Literal(\"DELETE\"),\n ],\n { default: \"GET\", description: \"HTTP method.\" }\n )\n ),\n headers: Type.Optional(\n Type.Record(Type.String(), Type.String(), {\n description: \"Request headers.\",\n })\n ),\n query: Type.Optional(\n Type.Record(Type.String(), Type.String(), {\n description: \"Query string params, appended to the URL.\",\n })\n ),\n body: Type.Optional(\n Type.String({\n description:\n \"Request body as a string. For JSON, stringify it yourself and \" +\n \"set `headers: { 'Content-Type': 'application/json' }`.\",\n })\n ),\n jsonpath: Type.Optional(\n Type.String({\n description:\n \"JSONPath expression applied to a JSON response, e.g. \" +\n \"`$.items[*].name`. Returns the matched values as an array.\",\n })\n ),\n }),\n async call(args): Promise<(Attachment | MetaPart | TextPart)[]> {\n const t0 = Date.now()\n const url = new URL(args.url)\n if (args.query) {\n for (const [k, v] of Object.entries(args.query)) url.searchParams.set(k, v)\n }\n\n const res = await fetch(url, {\n body: args.body,\n headers: args.headers,\n method: args.method,\n })\n\n const contentType = res.headers.get(\"content-type\") ?? \"\"\n const bytes = new Uint8Array(await res.arrayBuffer())\n\n const meta: Record<string, unknown> = {\n contentType: contentType || undefined,\n durationMs: Date.now() - t0,\n ok: res.ok,\n status: res.status,\n statusText: res.statusText,\n }\n\n // Magic-byte detection on the response bytes — catches mislabeled\n // content (HTML served at /file.pdf, images served as\n // application/octet-stream, etc.). Inlines as base64 (same shape as\n // the `read` tool) so the model gets the bytes without the provider\n // re-fetching from its own IP. Only attach kinds the model accepts;\n // unsupported kinds fall through to the text branch (rendered via\n // `<image>` / `<pdf>` placeholders by the wire pipeline).\n const { fileDetect } = await import(\"@zaly/shared/detect\")\n const file = await fileDetect({\n data: bytes,\n mime: contentType || undefined,\n url: url.toString(),\n })\n if (file) {\n const att = await toAttachment(file)\n if (att) return [{ data: meta, tag: \"fetch\", type: \"meta\" }, att]\n }\n\n const text = new TextDecoder().decode(bytes)\n // `detectTextFormat` (inside fileDetect) already weighs MIME,\n // extension, and content sniff — reuse its verdict instead of a\n // parallel `looksLikeJson` heuristic.\n const isJson = file?.type === \"text\" && file.format === \"json\"\n\n let body: unknown = text\n if (isJson) {\n try {\n body = JSON.parse(text)\n } catch {\n // Server lied about content-type; keep as text.\n }\n }\n\n if (args.jsonpath !== undefined && body !== text) {\n // Lazy-import — `jsonpath-plus` is only paid for when actually used,\n // and it bundles to a few hundred KB which we'd rather not load on\n // every fetch that doesn't filter.\n const { JSONPath } = await import(\"jsonpath-plus\")\n // oxlint-disable-next-line new-cap -- jsonpath-plus exports as TitleCase\n body = JSONPath({ json: body as object, path: args.jsonpath })\n }\n\n // Stringify the body for display. JSON pretty-prints; raw text is\n // used as-is.\n const bodyText = typeof body === \"string\" ? body : JSON.stringify(body, undefined, 2)\n\n // Cap the body before it reaches the model. Total bytes received is\n // surfaced via the meta so the model can decide whether to refetch\n // with `jsonpath` or use a different tool (browser for HTML, etc.).\n const totalBytes = Buffer.byteLength(bodyText, \"utf8\")\n const truncated = totalBytes > DEFAULT_MAX_BODY_BYTES\n const displayed = truncated ? bodyText.slice(0, DEFAULT_MAX_BODY_BYTES) : bodyText\n\n if (truncated) {\n meta.truncated = {\n bytes: totalBytes,\n hint:\n args.jsonpath === undefined\n ? \"body exceeded limit; pass `jsonpath` to filter, or use the browser tool for HTML.\"\n : \"filtered body still exceeded limit; tighten the JSONPath expression.\",\n limit: DEFAULT_MAX_BODY_BYTES,\n }\n }\n\n const parts: (MetaPart | TextPart)[] = [{ data: meta, tag: \"fetch\", type: \"meta\" }]\n if (displayed !== \"\")\n parts.push({ format: isJson ? \"json\" : undefined, text: displayed, type: \"text\" })\n return parts\n },\n})\n"],"mappings":";;;AAKA,MAAM,yBAAyB;;;;;;;;;;;;;;;;AAkB/B,MAAa,YAAY,WAAW;CAClC,MACE;CAEF,MAAM;CACN,UAAU;CAGV,QAAQ,KAAK,OAAO;EAClB,KAAK,KAAK,OAAO,EAAE,aAAa,gBAAgB,CAAC;EACjD,QAAQ,KAAK,SACX,KAAK,MACH;GACE,KAAK,QAAQ,KAAK;GAClB,KAAK,QAAQ,MAAM;GACnB,KAAK,QAAQ,KAAK;GAClB,KAAK,QAAQ,OAAO;GACpB,KAAK,QAAQ,QAAQ;EACvB,GACA;GAAE,SAAS;GAAO,aAAa;EAAe,CAChD,CACF;EACA,SAAS,KAAK,SACZ,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,OAAO,GAAG,EACxC,aAAa,mBACf,CAAC,CACH;EACA,OAAO,KAAK,SACV,KAAK,OAAO,KAAK,OAAO,GAAG,KAAK,OAAO,GAAG,EACxC,aAAa,4CACf,CAAC,CACH;EACA,MAAM,KAAK,SACT,KAAK,OAAO,EACV,aACE,uHAEJ,CAAC,CACH;EACA,UAAU,KAAK,SACb,KAAK,OAAO,EACV,aACE,kHAEJ,CAAC,CACH;CACF,CAAC;CACD,MAAM,KAAK,MAAqD;EAC9D,MAAM,KAAK,KAAK,IAAI;EACpB,MAAM,MAAM,IAAI,IAAI,KAAK,GAAG;EAC5B,IAAI,KAAK,OACP,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,KAAK,GAAG,IAAI,aAAa,IAAI,GAAG,CAAC;EAG5E,MAAM,MAAM,MAAM,MAAM,KAAK;GAC3B,MAAM,KAAK;GACX,SAAS,KAAK;GACd,QAAQ,KAAK;EACf,CAAC;EAED,MAAM,cAAc,IAAI,QAAQ,IAAI,cAAc,KAAK;EACvD,MAAM,QAAQ,IAAI,WAAW,MAAM,IAAI,YAAY,CAAC;EAEpD,MAAM,OAAgC;GACpC,aAAa,eAAe,KAAA;GAC5B,YAAY,KAAK,IAAI,IAAI;GACzB,IAAI,IAAI;GACR,QAAQ,IAAI;GACZ,YAAY,IAAI;EAClB;EASA,MAAM,EAAE,eAAe,MAAM,OAAO;EACpC,MAAM,OAAO,MAAM,WAAW;GAC5B,MAAM;GACN,MAAM,eAAe,KAAA;GACrB,KAAK,IAAI,SAAS;EACpB,CAAC;EACD,IAAI,MAAM;GACR,MAAM,MAAM,MAAM,aAAa,IAAI;GACnC,IAAI,KAAK,OAAO,CAAC;IAAE,MAAM;IAAM,KAAK;IAAS,MAAM;GAAO,GAAG,GAAG;EAClE;EAEA,MAAM,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,KAAK;EAI3C,MAAM,SAAS,MAAM,SAAS,UAAU,KAAK,WAAW;EAExD,IAAI,OAAgB;EACpB,IAAI,QACF,IAAI;GACF,OAAO,KAAK,MAAM,IAAI;EACxB,QAAQ,CAER;EAGF,IAAI,KAAK,aAAa,KAAA,KAAa,SAAS,MAAM;GAIhD,MAAM,EAAE,aAAa,MAAM,OAAO;GAElC,OAAO,SAAS;IAAE,MAAM;IAAgB,MAAM,KAAK;GAAS,CAAC;EAC/D;EAIA,MAAM,WAAW,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,MAAM,KAAA,GAAW,CAAC;EAKpF,MAAM,aAAa,OAAO,WAAW,UAAU,MAAM;EACrD,MAAM,YAAY,aAAa;EAC/B,MAAM,YAAY,YAAY,SAAS,MAAM,GAAG,sBAAsB,IAAI;EAE1E,IAAI,WACF,KAAK,YAAY;GACf,OAAO;GACP,MACE,KAAK,aAAa,KAAA,IACd,sFACA;GACN,OAAO;EACT;EAGF,MAAM,QAAiC,CAAC;GAAE,MAAM;GAAM,KAAK;GAAS,MAAM;EAAO,CAAC;EAClF,IAAI,cAAc,IAChB,MAAM,KAAK;GAAE,QAAQ,SAAS,SAAS,KAAA;GAAW,MAAM;GAAW,MAAM;EAAO,CAAC;EACnF,OAAO;CACT;AACF,CAAC"}
1
+ {"version":3,"file":"fetch-VKhSWdEh.mjs","names":[],"sources":["../src/tools/fetch.ts"],"sourcesContent":["import type { Attachment, MetaPart, TextPart } from \"@zaly/ai\"\n\nimport { defineTool, toAttachment } from \"@zaly/ai\"\nimport { Type } from \"typebox\"\n\nconst DEFAULT_MAX_BODY_BYTES = 256 * 1024 // 256 KB — generous for APIs, caps runaway HTML pages\n\n/**\n * Fetch a URL and return the response — JSON when the server speaks JSON,\n * raw text otherwise. Optional `jsonpath` filters the parsed JSON to a\n * subset, which keeps tokens down on chatty APIs.\n *\n * Scope: API endpoints. For HTML pages (browsing, content extraction),\n * the optional `@zaly/browser` package provides browser-backed tools that\n * return a structured a11y tree — much better than raw HTML through this\n * tool. Pointing this at an HTML URL works but the response is a string\n * blob the model has to slog through.\n *\n * Permissions: routes through the `fetch` scope when the agent wires up\n * tool-side permission checks. Until then, callers should restrict via\n * a wrapping permission layer (rule-based or explicit allowlist).\n */\n// oxlint-disable-next-line sort-keys\nexport const fetchTool = defineTool({\n desc:\n `Fetch a URL and return the response. JSON responses are parsed; use \\`jsonpath\\` (e.g. \\`$.items[*].name\\`) to extract a subset and minimise tokens.` +\n `Image & PDF are returned as attachments. Best for APIs — for web pages, use the browser tool.`,\n name: \"fetch\",\n parallel: true,\n\n // oxlint-disable-next-line sort-keys -- semantic param order: url, method, headers, query, body, jsonpath\n params: Type.Object({\n url: Type.String({ description: \"Absolute URL.\" }),\n method: Type.Optional(\n Type.Union(\n [\n Type.Literal(\"GET\"),\n Type.Literal(\"POST\"),\n Type.Literal(\"PUT\"),\n Type.Literal(\"PATCH\"),\n Type.Literal(\"DELETE\"),\n ],\n { default: \"GET\", description: \"HTTP method.\" }\n )\n ),\n headers: Type.Optional(\n Type.Record(Type.String(), Type.String(), {\n description: \"Request headers.\",\n })\n ),\n query: Type.Optional(\n Type.Record(Type.String(), Type.String(), {\n description: \"Query string params, appended to the URL.\",\n })\n ),\n body: Type.Optional(\n Type.String({\n description:\n \"Request body as a string. For JSON, stringify it yourself and \" +\n \"set `headers: { 'Content-Type': 'application/json' }`.\",\n })\n ),\n jsonpath: Type.Optional(\n Type.String({\n description:\n \"JSONPath expression applied to a JSON response, e.g. \" +\n \"`$.items[*].name`. Returns the matched values as an array.\",\n })\n ),\n }),\n async call(args): Promise<(Attachment | MetaPart | TextPart)[]> {\n const t0 = Date.now()\n const url = new URL(args.url)\n if (args.query) {\n for (const [k, v] of Object.entries(args.query)) url.searchParams.set(k, v)\n }\n\n const res = await fetch(url, {\n body: args.body,\n headers: args.headers,\n method: args.method,\n })\n\n const contentType = res.headers.get(\"content-type\") ?? \"\"\n const bytes = new Uint8Array(await res.arrayBuffer())\n\n const meta: Record<string, unknown> = {\n contentType: contentType || undefined,\n durationMs: Date.now() - t0,\n ok: res.ok,\n status: res.status,\n statusText: res.statusText,\n }\n\n // Magic-byte detection on the response bytes — catches mislabeled\n // content (HTML served at /file.pdf, images served as\n // application/octet-stream, etc.). Inlines as base64 (same shape as\n // the `read` tool) so the model gets the bytes without the provider\n // re-fetching from its own IP. Only attach kinds the model accepts;\n // unsupported kinds fall through to the text branch (rendered via\n // `<image>` / `<pdf>` placeholders by the wire pipeline).\n const { fileDetect } = await import(\"@zaly/shared/detect\")\n const file = await fileDetect({\n data: bytes,\n mime: contentType || undefined,\n url: url.toString(),\n })\n if (file) {\n const att = await toAttachment(file)\n if (att) return [{ data: meta, tag: \"fetch\", type: \"meta\" }, att]\n }\n\n const text = new TextDecoder().decode(bytes)\n // `detectTextFormat` (inside fileDetect) already weighs MIME,\n // extension, and content sniff — reuse its verdict instead of a\n // parallel `looksLikeJson` heuristic.\n const isJson = file?.type === \"text\" && file.format === \"json\"\n\n let body: unknown = text\n if (isJson) {\n try {\n body = JSON.parse(text)\n } catch {\n // Server lied about content-type; keep as text.\n }\n }\n\n if (args.jsonpath !== undefined && body !== text) {\n // Lazy-import — `jsonpath-plus` is only paid for when actually used,\n // and it bundles to a few hundred KB which we'd rather not load on\n // every fetch that doesn't filter.\n const { JSONPath } = await import(\"jsonpath-plus\")\n // oxlint-disable-next-line new-cap -- jsonpath-plus exports as TitleCase\n body = JSONPath({ json: body as object, path: args.jsonpath })\n }\n\n // Stringify the body for display. JSON pretty-prints; raw text is\n // used as-is.\n const bodyText = typeof body === \"string\" ? body : JSON.stringify(body, undefined, 2)\n\n // Cap the body before it reaches the model. Total bytes received is\n // surfaced via the meta so the model can decide whether to refetch\n // with `jsonpath` or use a different tool (browser for HTML, etc.).\n const totalBytes = Buffer.byteLength(bodyText, \"utf8\")\n const truncated = totalBytes > DEFAULT_MAX_BODY_BYTES\n const displayed = truncated ? bodyText.slice(0, DEFAULT_MAX_BODY_BYTES) : bodyText\n\n if (truncated) {\n meta.truncated = {\n bytes: totalBytes,\n hint:\n args.jsonpath === undefined\n ? \"body exceeded limit; pass `jsonpath` to filter, or use the browser tool for HTML.\"\n : \"filtered body still exceeded limit; tighten the JSONPath expression.\",\n limit: DEFAULT_MAX_BODY_BYTES,\n }\n }\n\n const parts: (MetaPart | TextPart)[] = [{ data: meta, tag: \"fetch\", type: \"meta\" }]\n if (displayed !== \"\")\n parts.push({ format: isJson ? \"json\" : undefined, text: displayed, type: \"text\" })\n return parts\n },\n})\n"],"mappings":"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"}