@livx.cc/agentx 0.99.27 → 0.99.30

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.
@@ -1,5 +1,5 @@
1
1
  import { IFilesystem } from '@livx.cc/wcli/core';
2
- import { M as Message, H as HostBridge, A as AgentTool, C as ChatLike, e as MessageContent, U as UserQuestion } from './tools-CJAPSBhS.js';
2
+ import { M as Message, H as HostBridge, A as AgentTool, C as ChatLike, g as MessageContent, U as UserQuestion } from './tools-BZVppx-6.js';
3
3
 
4
4
  /**
5
5
  * Hooks — deterministic interception points around tool execution, run by the
@@ -184,7 +184,7 @@ interface RunResult {
184
184
  * `needs_input` is NOT a failure: the agent hit a decision that was the user's to make with no
185
185
  * human reachable, so it stopped and raised the question (see `question`) instead of guessing.
186
186
  * Answer it and resume the session to continue. */
187
- finishReason: 'stop' | 'max_steps' | 'budget' | 'timeout' | 'loop' | 'max_tool_calls' | 'aborted' | 'error' | 'needs_input';
187
+ finishReason: 'stop' | 'max_steps' | 'budget' | 'timeout' | 'loop' | 'max_tool_calls' | 'aborted' | 'error' | 'needs_input' | 'empty';
188
188
  messages: Message[];
189
189
  /** The parked question — present iff `finishReason === 'needs_input'`. */
190
190
  question?: UserQuestion;
@@ -201,6 +201,14 @@ interface RunResult {
201
201
  usageEstimated?: boolean;
202
202
  error?: unknown;
203
203
  }
204
+ /** What a manual `/compact` actually freed (token estimates, ~4 chars/token). */
205
+ interface CompactResult {
206
+ tokensBefore: number;
207
+ tokensAfter: number;
208
+ target: number;
209
+ cap: number;
210
+ removed: number;
211
+ }
204
212
  declare class AgentOptions {
205
213
  /** Any ai.libx.js AIClient (or a FakeAIClient). */
206
214
  ai: ChatLike;
@@ -217,7 +225,8 @@ declare class AgentOptions {
217
225
  /** Hard ceiling on accumulated tokens (prompt+completion) across the run; cache READS count at 0.1×
218
226
  * (their real price) so a fat cached context doesn't trip the guard on healthy turns. 0 = unbounded. */
219
227
  maxTokens: number;
220
- /** Wall-clock ceiling in ms for the whole run. 0 = unbounded. */
228
+ /** Idle ceiling in ms: max time since the last productive step before the run is killed (a long/active
229
+ * step resets it — see runLoop's `lastProgressAt`). Not a whole-run wall clock. 0 = unbounded. */
221
230
  timeoutMs: number;
222
231
  /** Idle-stall watchdog for a SINGLE streamed model request: abort it if no chunk (text OR reasoning)
223
232
  * arrives for this long. The between-steps `timeoutMs` can't preempt an in-flight `chat()`, so a
@@ -229,15 +238,33 @@ declare class AgentOptions {
229
238
  maxToolCalls: number;
230
239
  /** External cancellation — abort the loop between steps (e.g. a UI "Cancel" button). */
231
240
  signal?: AbortSignal;
241
+ /** Retry budget for an EMPTY/silent-turn (a delegated-runtime wedge: cursor's warm helper spins then
242
+ * returns a content-less end, surfaced as `code:'empty'`/`code:'cursor_silent'`). Tuned WIDER than the
243
+ * network-drop budget (fixed 2) because clearing a wedge needs several reap+respawn cycles — each retry
244
+ * hits a FRESH cursor session (the adapter evicts the wedged warm helper on the silent-end). Still bounded:
245
+ * after exhaustion the turn surfaces `finishReason:'empty'` (non-zero exit), never infinite, never silent.
246
+ * Env: AGENTX_EMPTY_RETRIES. */
247
+ emptyRetries: number;
248
+ /** Base backoff (ms) between empty-turn retries; grows linearly per attempt (longer than the network
249
+ * class's fixed 1s so a wedged helper has time to be reaped/respawned). Env: AGENTX_EMPTY_RETRY_BASE_MS. */
250
+ emptyRetryBaseMs: number;
232
251
  /** 0 = never trim. Otherwise cap the messages sent per turn (system + most recent). */
233
252
  maxContextMessages: number;
234
253
  /** Note-taking: keep the most-recent N tool-result outputs verbatim; collapse OLDER ones to a one-line
235
254
  * stub in the sent context (the model already consumed them — it can re-Read/re-run). The stored
236
255
  * transcript is never mutated. 0 = keep all verbatim. */
237
256
  keepToolOutputs: number;
257
+ /** How many of the most-recent image-bearing messages get their image REF expanded to real base64 in
258
+ * the outgoing request. Older ones (and legacy inline base64) go on the wire as the elision stub. */
259
+ keepRecentImages: number;
238
260
  /** Token-aware backstop (~4 chars/token estimate). After note-taking, drop oldest messages from the
239
261
  * sent context until the estimate is under this ceiling (pairing-safe). 0 = off. */
240
262
  maxContextTokens: number;
263
+ /** The MODEL's context window (tokens) — what the context % is measured against and what `/compact`
264
+ * targets 50% of. Filled by the CLI from model metadata; 0 = fall back to 200k. Deliberately NOT
265
+ * `maxTokens`: that is the cumulative SPEND kill-switch, and a spend budget must never be able to
266
+ * drive destructive compaction (`--max-tokens 50000` would otherwise shred the transcript). */
267
+ contextWindow: number;
241
268
  /** Pagination ceiling for a SINGLE tool result (bytes). A result over this is cropped to page 1 with
242
269
  * a marker telling the model it was cropped (refine the query, or page further). Guards against one
243
270
  * Grep/Read/MCP call blowing the whole context window. 0 = off. Default 60k (~15k tokens). */
@@ -299,8 +326,10 @@ declare class AgentOptions {
299
326
  /** Stream tokens from the model. Takes effect only with a `host.notify`; off => current (non-stream) behavior. */
300
327
  stream: boolean;
301
328
  /** Closure reflex for delegated runtimes (cursor/claude-code): when such a turn ends on a tool call with
302
- * no assistant text after it (the runtime went silent post-work), nudge ONE more step for a brief summary
303
- * of what changed + what's pending — so the turn delivers a wrap-up instead of trailing off mid-narration.
329
+ * no assistant text after it (the runtime went silent post-work), nudge ONE more step to ANSWER the user
330
+ * from the tool results already in the transcript — so the turn delivers a reply instead of trailing off
331
+ * mid-narration. Deliberately not a "summarize what you did" nudge: that produced process reports and
332
+ * permission-asking instead of answers (see the nudge text in runLoop).
304
333
  * Fires at most once per run and only for delegated runtimes (native turns never emit the trigger). */
305
334
  closeDelegatedTurns: boolean;
306
335
  /** Fold the dropped middle of an over-long transcript into a synthetic summary (edge-safe, no LLM). Off => drop-oldest. */
@@ -437,13 +466,16 @@ declare class Agent {
437
466
  */
438
467
  send(task: MessageContent): Promise<RunResult>;
439
468
  /**
440
- * Fold the conversation in place (manual `/compact`): keep the system message + the
441
- * most-recent window, summarizing the dropped middle (deterministic, no LLM call).
442
- * No-op when the transcript already fits. Returns the number of messages removed.
443
- * `focus` (e.g. from `/compact keep the API details`) preserves matching lines from the
444
- * dropped span verbatim in the summary, instead of losing them to the generic recap.
469
+ * Fold the conversation in place (manual `/compact`) until it fits a TOKEN budget — not a message
470
+ * count. Message-count folding alone frees almost nothing when a few recent tool results are huge,
471
+ * so we escalate: fold the middle into a synthetic recap → elide oversized tool-result bodies in the
472
+ * retained window → halve the window → drop-oldest backstop. Deterministic, no LLM call.
473
+ * `focus` (e.g. `/compact keep the API details`) preserves matching lines from the dropped span verbatim.
474
+ * Returns before/after token estimates so the caller can report honestly (0 freed => say so).
445
475
  */
446
- compactNow(maxMessages?: number, focus?: string): number;
476
+ compactNow(maxMessages?: number, focus?: string): CompactResult;
477
+ /** Drop inline base64 images from older messages so Bun/JSC can reclaim the heap (see contentBytes). */
478
+ releaseStaleImages(keepRecent?: number): void;
447
479
  private runLoop;
448
480
  /**
449
481
  * Drain a streamed chat() response: emit each text delta to the host
@@ -458,8 +490,16 @@ declare class Agent {
458
490
  * `stallMs = 0` disables the timer but still links parent-abort → child so cancellation propagates.
459
491
  */
460
492
  private armStallWatchdog;
493
+ /** A clean-stop turn that carries no assistant text AND no tool calls (native or delegated) — a
494
+ * non-result the model produced by going silent. Callers retry it rather than report "done". */
495
+ private isEmptyStop;
461
496
  private consumeStream;
462
497
  private dispatch;
498
+ /** `/compact` aims to land the transcript at this fraction of the context window. */
499
+ private static readonly COMPACT_TARGET;
500
+ /** THE context-window cap resolution — `/compact`'s budget and the CLI's context footer/warnings both
501
+ * route here, so the percentage the user reads and the one compaction targets can never drift. */
502
+ static contextCap(o: Pick<AgentOptions, 'maxContextTokens' | 'contextWindow' | 'model'> | Partial<AgentOptions>): number;
463
503
  private static readonly WRITE_CLASS;
464
504
  /** Append an autoTest failure section to a write-class tool result, if configured. */
465
505
  private maybeAutoTest;
@@ -474,5 +514,11 @@ declare class Agent {
474
514
  */
475
515
  trimContext(): Message[];
476
516
  }
517
+ /** ~4 chars/token estimate over content + serialized tool_calls. THE estimator — the CLI footer,
518
+ * `/context` and `/compact` all route here so what the user sees and what compaction targets can't
519
+ * drift apart. Images are weighed AS SENT (`sendBytes`): only the recent window's refs cost their
520
+ * file, stale ones cost the stub. Budgeting a ref at full file size while the wire carries a 50-byte
521
+ * stub made `/compact` chase a target it could never reach and drop-oldest the conversation away. */
522
+ declare function estimateTokens(m: Message[], keepRecentImages?: number): number;
477
523
 
478
- export { Agent as A, type ChatFragment as C, DEFAULT_MUTATING as D, type Hooks as H, PermissionOptions as P, type ReasoningEffort as R, type ToolUse as T, AgentOptions as a, type Decision as b, PermissionPolicy as c, type PermissionRule as d, type PreToolUseDecision as e, RecordingHooks as f, RecordingLifecycle as g, type RunResult as h, type ToolUseMeta as i, composeHooks as j, planMode as p, reasoningToChatFragment as r };
524
+ export { Agent as A, type ChatFragment as C, DEFAULT_MUTATING as D, type Hooks as H, PermissionOptions as P, type ReasoningEffort as R, type ToolUse as T, AgentOptions as a, type CompactResult as b, type Decision as c, PermissionPolicy as d, type PermissionRule as e, type PreToolUseDecision as f, RecordingHooks as g, RecordingLifecycle as h, type RunResult as i, type ToolUseMeta as j, composeHooks as k, estimateTokens as l, planMode as p, reasoningToChatFragment as r };
package/dist/cli.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
- import { H as Hooks, h as RunResult, R as ReasoningEffort, A as Agent } from './Agent-ChSs1QSx.js';
2
+ import { H as Hooks, i as RunResult, R as ReasoningEffort, A as Agent } from './Agent-DC7FEEjF.js';
3
3
  import { IFilesystem } from '@livx.cc/wcli/core';
4
- import { M as Message, U as UserQuestion, H as HostBridge, c as ContentPart, e as MessageContent } from './tools-CJAPSBhS.js';
4
+ import { M as Message, U as UserQuestion, H as HostBridge, c as ContentPart, g as MessageContent } from './tools-BZVppx-6.js';
5
5
 
6
6
  /**
7
7
  * On-disk session store for the CLI: each conversation is one JSON file at
@@ -205,8 +205,10 @@ declare function costOf(pricing: {
205
205
  } | undefined, promptTokens?: number, completionTokens?: number, cacheCreationTokens?: number, cacheReadTokens?: number, model?: string): number;
206
206
  /** Format a USD amount: 2 decimals at $1+, 4 below (agent turns are sub-cent). */
207
207
  declare function fmtUsd(n: number): string;
208
- /** ~4 chars/token estimate over a transcript (matches the Agent's context-budget heuristic). */
209
- declare function estimateTranscriptTokens(messages: Message[]): number;
208
+ /** ~4 chars/token estimate over a transcript (matches the Agent's context-budget heuristic).
209
+ * Weighs images AS SENT (`sendBytes`): only the recent window's refs cost their file; stale ones cost
210
+ * the stub — otherwise every old screenshot would stay immortal and fake a "90% full" warning. */
211
+ declare function estimateTranscriptTokens(messages: Message[], keepRecentImages?: number): number;
210
212
  /** One-screen session status (model, dir, fs-mode, tools, permission posture, turns/tokens). Pure. */
211
213
  declare function formatStatus(s: {
212
214
  model: string;
@@ -251,8 +253,9 @@ declare function resolvePermMode(args: {
251
253
  * form is what lets a drag-dropped macOS screenshot ("Screenshot 2026-06-11 at ….png") attach at all.
252
254
  * Bare refs get trailing sentence punctuation stripped (e.g. "@a.ts." / "@a.ts?"). */
253
255
  declare function mentionRefs(line: string): string[];
254
- /** Read `@image.png` mentions from `line` as base64 image content parts (real files under cwd; binary,
255
- * so read via node fs — the VFS readFile is utf8). Unreadable/missing images are skipped silently
256
+ /** Read `@image.png` mentions from `line` as image REFERENCE parts — the path, not the payload. The
257
+ * base64 is materialized only in the outgoing request (Agent.trimContext → expandImagesForSend), so the
258
+ * transcript and session JSON never carry megabytes. Missing/unreadable images are skipped
256
259
  * (expandMentions reports the missing @ref separately). */
257
260
  declare function readImageParts(cwd: string, line: string): ContentPart[];
258
261
  /** Classify a pasted blob as a file attachment when it's a drag-dropped/typed path to an existing file.
@@ -275,7 +278,7 @@ declare function jsonResult(res: RunResult, session: SessionData): {
275
278
  error?: any;
276
279
  question?: UserQuestion | undefined;
277
280
  ok: boolean;
278
- finishReason: "error" | "budget" | "stop" | "max_steps" | "timeout" | "loop" | "max_tool_calls" | "aborted" | "needs_input";
281
+ finishReason: "error" | "budget" | "stop" | "max_steps" | "timeout" | "loop" | "max_tool_calls" | "aborted" | "needs_input" | "empty";
279
282
  text: string;
280
283
  steps: number;
281
284
  tools: number;