@livx.cc/agentx 0.99.27 → 0.99.29

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,4 +1,4 @@
1
- import { A as AgentTool } from './tools-CJAPSBhS.js';
1
+ import { A as AgentTool } from './tools-BZVppx-6.js';
2
2
 
3
3
  /**
4
4
  * MCP bridge — adapt a Model Context Protocol tool list into the agent's AgentTool[],
@@ -1,5 +1,5 @@
1
- import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-DiAiMiG5.js';
2
- import { A as AgentTool } from './tools-CJAPSBhS.js';
1
+ import { e as McpToolSearchOptions, f as McpToolSpec } from './mcp-Cnq0pS95.js';
2
+ import { A as AgentTool } from './tools-BZVppx-6.js';
3
3
  import '@livx.cc/wcli/core';
4
4
 
5
5
  /** A JSON-RPC 2.0 transport: request (awaits a result), notify (fire-and-forget), close. */
@@ -36,6 +36,103 @@ interface Message {
36
36
  declare function contentText(content: MessageContent | undefined): string;
37
37
  /** Build an image content part from a data-URI or http(s) URL. */
38
38
  declare function imagePart(url: string): ContentPart;
39
+ /** Stub left when an inline data-URI image is elided from the stored transcript (RAM + disk). */
40
+ declare const IMAGE_ELIDE_STUB = "[image elided \u2014 re-attach @path or /paste to view again]";
41
+ /** True when content carries an inline base64 image (the heavy case — http(s) URLs are tiny). */
42
+ declare function messageHasInlineImage(content: MessageContent | undefined): boolean;
43
+ /** Byte-weight for context budgeting — counts base64 payloads, not the `[image]` display placeholder. */
44
+ declare function contentBytes(content: MessageContent | undefined): number;
45
+ /** Replace inline data-URI image parts with a text stub; http(s) URLs, image REFS and plain text
46
+ * unchanged — a ref is ~60 bytes and stays re-expandable, so there is nothing to reclaim. */
47
+ declare function elideInlineImages(content: MessageContent, stub?: string): MessageContent;
48
+ /** Replace EVERY heavy image part — inline base64 AND refs — with the stub. This is the destructive
49
+ * form used by the `/compact` elision ladder: a ref is cheap on disk but weighs its whole file on the
50
+ * wire while it's inside the send window, so it must be shave-able or the ladder falls through to
51
+ * shredding (then dropping) the user's own text. `elideInlineImages` stays the non-destructive form. */
52
+ declare function elideImages(content: MessageContent, stub?: string): MessageContent;
53
+ /**
54
+ * A persist-safe REFERENCE to an image FILE. Stored in the transcript (and the session JSON) instead
55
+ * of the megabytes of base64 — `expandImagesForSend` turns it back into a data-URI in the OUTGOING
56
+ * request only, so neither the heap nor the disk ever holds historical payloads.
57
+ *
58
+ * SECURITY: a ref is a CAPABILITY, not a path. The URL carries an unguessable token; the real path
59
+ * lives only in this in-process registry, and only HOST code that already did the jail/permission
60
+ * check can mint one (`mintImageRef`, used by the CLI's @image attach and the image `Read` tool).
61
+ * Tool results are MODEL-CONTROLLED text (bash prints whatever it likes) — parsing a path out of them
62
+ * could never be the authorization. An unknown/evicted token is simply not a ref: it degrades to the
63
+ * elision stub, never a file read. That also covers a resumed session in a fresh process.
64
+ * Shape stays inside `image_url.url` so old readers/serializers need no schema change:
65
+ * `agentx-image:<mime>;<token>;<path>`
66
+ * The trailing path is INERT BOOKKEEPING — `parseImageRef` never reads it, so it can't authorize
67
+ * anything. It exists only so `remintImageRefs` can re-grant the capability on session load, after
68
+ * re-checking the path against an allowed root (see there).
69
+ */
70
+ declare const IMAGE_REF_SCHEME = "agentx-image:";
71
+ interface ImageRef {
72
+ mime: string;
73
+ /** Absolute on-disk path. Never derived from model output — only from a mint-time capability. */
74
+ path: string;
75
+ /** base64 wire weight, stat'ed AT MINT TIME (so an overwritten-in-place path can never go stale). */
76
+ bytes: number;
77
+ }
78
+ /**
79
+ * Mint an image-ref URL for an ALREADY-AUTHORIZED absolute path. Host-only: call this after the
80
+ * jail/permission check, never on a path that came out of model output.
81
+ */
82
+ declare function mintImageRef(path: string, mime: string): string;
83
+ /** Test/host hook: forget every minted ref (simulates a fresh process / resumed session). */
84
+ declare function clearImageRefs(): void;
85
+ /** Build an image REFERENCE part for an authorized path. */
86
+ declare function imageRefPart(path: string, mime: string): ContentPart;
87
+ /** Resolve an image-ref URL through the registry; null when it isn't one, or the token is unknown
88
+ * (forged by the model, evicted, or from a previous process). NEVER trusts the URL's own text. */
89
+ declare function parseImageRef(url: string | undefined): ImageRef | null;
90
+ /**
91
+ * Session LOAD: re-grant the capability for stored image parts whose token died with the previous
92
+ * process, so a resumed session's recent screenshot is visible again (parity with the pre-ref
93
+ * behaviour, where `elideStaleImages` kept the last inline image).
94
+ *
95
+ * A session file is WRITABLE BY THE MODEL (it can Write/Edit it, and the user may hand-edit it), so a
96
+ * stored path is untrusted input — its provenance proves nothing. The ROOT CHECK is what makes this
97
+ * safe: only a path whose REALPATH (symlinks resolved) sits inside one of `roots` is re-minted.
98
+ * Anything else is left unresolvable and degrades to the elision stub, with a warn.
99
+ *
100
+ * Only image PARTS in user/assistant content are considered. Tool-result strings stay inert forever —
101
+ * those are the model's own stdout and must never mint anything.
102
+ * Pure view: returns a new array only when something changed.
103
+ */
104
+ declare function remintImageRefs(messages: Message[], roots: string[]): Message[];
105
+ /** True when content carries an image REF part (weightless on disk, expanded at send time). The
106
+ * CLAIM is enough: an unresolvable ref (resumed session / evicted token) still counts as image-bearing
107
+ * so the send path turns it into the stub instead of shipping the unresolvable scheme to the provider. */
108
+ declare function messageHasImageRef(content: MessageContent | undefined): boolean;
109
+ /** Any image at all — inline base64 (legacy sessions) or a ref. Drives `keepRecent` windows. */
110
+ declare function messageHasImage(content: MessageContent | undefined): boolean;
111
+ /** `Read` on an image persists this instead of the base64; expanded to `{dataUrl}` at send time.
112
+ * `path` must already have passed the filesystem's jail check (see `mintImageRef`). */
113
+ declare function imageRefResult(path: string, mime: string): string;
114
+ /** The registered ref behind a tool message's `{"imageRef":…}` JSON body, if that's what it is.
115
+ * A model-forged body parses fine but resolves to null — the token isn't in the registry. */
116
+ declare function toolImageRef(content: MessageContent | undefined): ImageRef | null;
117
+ /** Elide inline images from all but the most-recent `keepRecent` image-bearing messages (mutates copies). */
118
+ declare function elideStaleImages(messages: Message[], keepRecent?: number): Message[];
119
+ /**
120
+ * SEND-PATH expansion: image REFS become real data-URIs only in the outgoing request, and only for the
121
+ * most-recent `keepRecent` messages of EACH kind (user attachment / tool image Read) — older ones (refs
122
+ * AND legacy inline base64) collapse to the elision stub. Pure view: the caller's messages are never mutated.
123
+ * A missing/unreadable file (or an unregistered ref) degrades to the same stub and is LOGGED — never throws.
124
+ */
125
+ declare function expandImagesForSend(messages: Message[], keepRecent?: number, stub?: string): Message[];
126
+ /**
127
+ * Byte-weight of a transcript AS IT WILL GO ON THE WIRE — refs inside the keep window weigh their
128
+ * file's base64 size, everything stale weighs the stub — WITHOUT materializing a single byte of
129
+ * base64. This is the honest number for `/context` and the CLI footer, where the old behaviour made
130
+ * every historical screenshot immortal and produced false "90% full" warnings.
131
+ */
132
+ declare function sendBytes(messages: Message[], keepRecent?: number, stub?: string): number;
133
+ /** `sendBytes` split per message — same window, same weights. Lets a budgeter drop/shave message-by-
134
+ * message without re-deriving the window (and without charging a stale ref its file weight). */
135
+ declare function sendBytesPer(messages: Message[], keepRecent?: number, stub?: string): number[];
39
136
  interface Tool {
40
137
  type: 'function';
41
138
  function: {
@@ -317,4 +414,4 @@ declare function toolRegistry(): Record<string, AgentTool>;
317
414
  /** Resolve a list of tool names against the registry (unknown names throw). */
318
415
  declare function toolsByName(names: string[]): AgentTool[];
319
416
 
320
- export { type AgentTool as A, type ChatLike as C, type HostBridge as H, type Message as M, type Role as R, SandboxJobRegistry as S, type TodoItem as T, type UserQuestion as U, type ChatOptions as a, type ChatResponse as b, type ContentPart as c, type HostEvent as d, type MessageContent as e, type StreamChunk as f, type Tool as g, type ToolCall as h, type ToolContext as i, bashTool as j, contentText as k, defaultTools as l, editTool as m, exitSessionTool as n, imagePart as o, makeContext as p, makeJobTools as q, readTool as r, todoWriteTool as s, toWireTools as t, toolRegistry as u, toolsByName as v };
417
+ export { type AgentTool as A, makeJobTools as B, type ChatLike as C, messageHasImage as D, messageHasImageRef as E, messageHasInlineImage as F, mintImageRef as G, type HostBridge as H, IMAGE_ELIDE_STUB as I, parseImageRef as J, readTool as K, remintImageRefs as L, type Message as M, sendBytes as N, sendBytesPer as O, toWireTools as P, todoWriteTool as Q, type Role as R, SandboxJobRegistry as S, type TodoItem as T, type UserQuestion as U, toolImageRef as V, toolRegistry as W, toolsByName as X, type ChatOptions as a, type ChatResponse as b, type ContentPart as c, type HostEvent as d, IMAGE_REF_SCHEME as e, type ImageRef as f, type MessageContent as g, type StreamChunk as h, type Tool as i, type ToolCall as j, type ToolContext as k, bashTool as l, clearImageRefs as m, contentBytes as n, contentText as o, defaultTools as p, editTool as q, elideImages as r, elideInlineImages as s, elideStaleImages as t, exitSessionTool as u, expandImagesForSend as v, imagePart as w, imageRefPart as x, imageRefResult as y, makeContext as z };
@@ -1,4 +1,4 @@
1
- import { A as AgentTool } from './tools-CJAPSBhS.js';
1
+ import { A as AgentTool } from './tools-BZVppx-6.js';
2
2
  import '@livx.cc/wcli/core';
3
3
 
4
4
  /**
@@ -1,6 +1,18 @@
1
1
  // src/tools.ts
2
2
  import { CommandExecutor, registerHeadlessCommands } from "@livx.cc/wcli/core";
3
3
 
4
+ // src/llm.ts
5
+ import { readFileSync, statSync, realpathSync } from "fs";
6
+ import { resolve, sep } from "path";
7
+ import { randomBytes } from "crypto";
8
+
9
+ // src/logging.ts
10
+ import { log } from "libx.js/src/modules/log";
11
+ var forComponent = (name) => log.forComponent(name);
12
+
13
+ // src/llm.ts
14
+ var log2 = forComponent("llm");
15
+
4
16
  // src/redact.ts
5
17
  var REDACTED = "\u2039redacted\u203A";
6
18
  var SECRET_PAIR = /((?:^|[\s,{[])(?:export\s+)?["']?[\w.\-]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|PRIVATE_KEY|ACCESS_?KEY|AUTH(?:_?TOKEN)?|BEARER)[\w.\-]*["']?\s*[:=]\s*)(["']?)([^\s"',{}\]]+)/gi;
@@ -10,12 +22,8 @@ function redactSecrets(text) {
10
22
  return text.replace(SECRET_PAIR, (_m, head, quote, _val) => `${head}${quote}${REDACTED}`).replace(SECRET_TOKEN, REDACTED);
11
23
  }
12
24
 
13
- // src/logging.ts
14
- import { log } from "libx.js/src/modules/log";
15
- var forComponent = (name) => log.forComponent(name);
16
-
17
25
  // src/tools.web.ts
18
- var log2 = forComponent("web");
26
+ var log3 = forComponent("web");
19
27
  function htmlToText(html) {
20
28
  let s = html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<title[\s\S]*?<\/title>/gi, " ").replace(/<noscript[\s\S]*?<\/noscript>/gi, " ").replace(/<textarea[\s\S]*?<\/textarea>/gi, " ").replace(/<!--[\s\S]*?-->/g, " ").replace(/<\/(p|div|li|h[1-6]|tr|section|article|header|footer|nav)>/gi, "\n").replace(/<br\s*\/?>/gi, "\n").replace(/<[^>]+>/g, " ");
21
29
  s = s.replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&#x27;/gi, "'");
@@ -137,7 +145,7 @@ function makeWebFetchTool(options = {}) {
137
145
 
138
146
  ${capped}`;
139
147
  } catch (e) {
140
- log2.debug(`WebFetch ${u} failed`, e);
148
+ log3.debug(`WebFetch ${u} failed`, e);
141
149
  return `Error fetching ${u}: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
142
150
  } finally {
143
151
  clearTimeout(timer);
@@ -261,7 +269,7 @@ function makeWebSearchTool(options = {}) {
261
269
  if (!res.ok) return `Error: search returned ${res.status} ${res.statusText}`;
262
270
  return formatHits(parseDdgHtml(await res.text(), maxResults));
263
271
  } catch (e) {
264
- log2.debug("WebSearch failed", e);
272
+ log3.debug("WebSearch failed", e);
265
273
  return `Error searching: ${e?.name === "AbortError" ? `timed out after ${timeoutMs}ms` : e?.message ?? e}`;
266
274
  } finally {
267
275
  clearTimeout(timer);
@@ -278,6 +286,7 @@ function makeWebSearchAnthropicTool(opts = {}) {
278
286
  var webSearchAnthropicTool = makeWebSearchAnthropicTool();
279
287
 
280
288
  // src/tools.ts
289
+ var log4 = forComponent("tools");
281
290
  function truncateOutput(s, headLines = 80, tailLines = 20) {
282
291
  const lines = s.split("\n");
283
292
  if (lines.length <= headLines + tailLines + 1) return s;
@@ -332,7 +341,7 @@ async function findSandboxWrapper(platform = process.platform) {
332
341
  }
333
342
 
334
343
  // src/tools.shell.ts
335
- var log3 = forComponent("shell");
344
+ var log5 = forComponent("shell");
336
345
  var clean = (s) => truncateOutput(redactSecrets(s.replace(/\n+$/, "")));
337
346
  var DETACHED = { stdio: ["ignore", "pipe", "pipe"], detached: true };
338
347
  function killGroup(proc, signal) {
@@ -490,13 +499,13 @@ function makeRealShellTool(options) {
490
499
  }
491
500
  };
492
501
  try {
493
- return await new Promise((resolve) => {
502
+ return await new Promise((resolve2) => {
494
503
  let out = "";
495
504
  let settled = false;
496
505
  const finish = (s) => {
497
506
  if (settled) return;
498
507
  settled = true;
499
- resolve(s);
508
+ resolve2(s);
500
509
  };
501
510
  let proc;
502
511
  try {
@@ -519,7 +528,7 @@ function makeRealShellTool(options) {
519
528
  proc.stderr?.on("data", collect);
520
529
  proc.on("error", (err) => {
521
530
  if (err?.name === "AbortError" || ctl.signal.aborted) return finish(reasonFor(timedOut, timeoutMs, clean(out)));
522
- log3.debug("shell spawn error", err);
531
+ log5.debug("shell spawn error", err);
523
532
  finish(`[exit 1] ${err?.message ?? err}${out ? "\n" + clean(out) : ""}`);
524
533
  });
525
534
  proc.on("close", (code) => {