@livx.cc/agentx 0.99.123 → 0.99.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +176 -101
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +39 -9
- package/dist/index.js +174 -96
- package/dist/index.js.map +1 -1
- package/dist/{secondChannel-DES_VIeN.d.ts → secondChannel-GFT69ax4.d.ts} +23 -1
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
import {
|
|
2
|
+
import { w as RouterState, H as Hooks, c as AgentOptions, x as RunResult, f as ChatUsage, R as ReasoningEffort, d as ChannelMode, b as Agent } from './secondChannel-GFT69ax4.js';
|
|
3
3
|
import { IFilesystem } from '@livx.cc/wcli/core';
|
|
4
4
|
import { M as Message, U as UserQuestion, H as HostBridge, c as ContentPart, o as MessageContent } from './tools-DwfeF8VQ.js';
|
|
5
5
|
|
package/dist/cli.js
CHANGED
|
@@ -121,13 +121,13 @@ function elideImageParts(content, hit, stub) {
|
|
|
121
121
|
}
|
|
122
122
|
function mintImageRef(path, mime) {
|
|
123
123
|
const token = randomBytes(16).toString("hex");
|
|
124
|
-
let
|
|
124
|
+
let bytes2 = 0;
|
|
125
125
|
try {
|
|
126
|
-
|
|
126
|
+
bytes2 = Math.ceil(statSync3(path).size * 4 / 3);
|
|
127
127
|
} catch (e) {
|
|
128
128
|
log3.warn(`image ref stat failed, weighing as 0: ${path}`, e);
|
|
129
129
|
}
|
|
130
|
-
refRegistry.set(token, { mime, path, bytes });
|
|
130
|
+
refRegistry.set(token, { mime, path, bytes: bytes2 });
|
|
131
131
|
while (refRegistry.size > REF_REGISTRY_MAX) refRegistry.delete(refRegistry.keys().next().value);
|
|
132
132
|
return `${IMAGE_REF_SCHEME}${mime};${token};${path}`;
|
|
133
133
|
}
|
|
@@ -1639,14 +1639,14 @@ var init_tools = __esm({
|
|
|
1639
1639
|
if (!await ctx.fs.exists(path)) return `Error: File not found: ${path}`;
|
|
1640
1640
|
return imageRefResult(real, IMG_MIME[ext]);
|
|
1641
1641
|
}
|
|
1642
|
-
const
|
|
1643
|
-
const b64 = Buffer.from(
|
|
1642
|
+
const bytes2 = await fs.readFileBytes(path);
|
|
1643
|
+
const b64 = Buffer.from(bytes2).toString("base64");
|
|
1644
1644
|
return JSON.stringify({ dataUrl: `data:${IMG_MIME[ext]};base64,${b64}`, image: path });
|
|
1645
1645
|
}
|
|
1646
1646
|
const raw = await ctx.fs.readFile(path);
|
|
1647
1647
|
if (isBinary(raw)) {
|
|
1648
|
-
const
|
|
1649
|
-
return `[binary file: ${path} \u2014 ${
|
|
1648
|
+
const bytes2 = await byteSize(ctx.fs, path, raw);
|
|
1649
|
+
return `[binary file: ${path} \u2014 ${bytes2} bytes, not shown as text. Read renders png/jpg/jpeg/gif/webp images directly; otherwise attach it as @${path}.]`;
|
|
1650
1650
|
}
|
|
1651
1651
|
ctx.readState.set(ctx.fs.resolvePath(path), raw);
|
|
1652
1652
|
if (raw === "") return `[empty file: ${path} \u2014 0 bytes]`;
|
|
@@ -1977,8 +1977,8 @@ var init_tools_jobs = __esm({
|
|
|
1977
1977
|
return [...this.jobs.values()].map((j) => this.info(j));
|
|
1978
1978
|
}
|
|
1979
1979
|
info(j) {
|
|
1980
|
-
const { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes, holds, result } = j;
|
|
1981
|
-
return { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes, holds, result };
|
|
1980
|
+
const { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes: bytes2, holds, result } = j;
|
|
1981
|
+
return { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes: bytes2, holds, result };
|
|
1982
1982
|
}
|
|
1983
1983
|
/** Wait (bounded) for a job to settle. Resolves the job's info either way; null for an unknown id. */
|
|
1984
1984
|
async wait(id, timeoutMs) {
|
|
@@ -2573,7 +2573,8 @@ var init_JailedFilesystem = __esm({
|
|
|
2573
2573
|
violate(op, abs, rule) {
|
|
2574
2574
|
this.note(op, abs, rule);
|
|
2575
2575
|
if (rule === "deny" || rule === "confine") throw new Error(`File not found: ${abs}`);
|
|
2576
|
-
|
|
2576
|
+
const hint = rule === "readonly" && this.options.readonlyHint ? ` \u2014 ${this.options.readonlyHint}` : "";
|
|
2577
|
+
throw new Error(`Blocked by filesystem jail (${rule}): ${abs}${hint}`);
|
|
2577
2578
|
}
|
|
2578
2579
|
/** Fire the audit hook without throwing (for boolean probes that must still return false). */
|
|
2579
2580
|
note(op, abs, rule) {
|
|
@@ -2698,6 +2699,8 @@ var init_JailedFilesystem = __esm({
|
|
|
2698
2699
|
deny = [...DEFAULT_DENY];
|
|
2699
2700
|
/** Globs readable but not writable/deletable (the pinned constitution). */
|
|
2700
2701
|
readonly = [];
|
|
2702
|
+
/** Appended to readonly-violation errors (e.g. "edit it via the kb tool instead"). */
|
|
2703
|
+
readonlyHint;
|
|
2701
2704
|
/** If set, only paths matching these globs are accessible at all. */
|
|
2702
2705
|
confineTo;
|
|
2703
2706
|
/** Audit hook fired on every blocked operation (drives the circuit breaker). */
|
|
@@ -3520,69 +3523,39 @@ function normalizeQuestion(input) {
|
|
|
3520
3523
|
return { question: q2.question, header: typeof q2.header === "string" ? q2.header : void 0, options, multiSelect: q2.multiSelect === true };
|
|
3521
3524
|
}
|
|
3522
3525
|
|
|
3523
|
-
// src/
|
|
3524
|
-
var
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
"
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
"
|
|
3535
|
-
"
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
"
|
|
3541
|
-
"
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
if (w.length >= 3 && !STOP.has(w)) out.add(w);
|
|
3557
|
-
}
|
|
3558
|
-
return out;
|
|
3559
|
-
}
|
|
3560
|
-
function idfWeights(corpus) {
|
|
3561
|
-
const N = corpus.length;
|
|
3562
|
-
if (N === 0) return /* @__PURE__ */ new Map();
|
|
3563
|
-
const df = /* @__PURE__ */ new Map();
|
|
3564
|
-
for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
3565
|
-
const idf = /* @__PURE__ */ new Map();
|
|
3566
|
-
for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1);
|
|
3567
|
-
return idf;
|
|
3568
|
-
}
|
|
3569
|
-
function relevanceScore(text, queryTokens, idf) {
|
|
3570
|
-
if (queryTokens.size === 0) return 0;
|
|
3571
|
-
const t = tokenize(text);
|
|
3572
|
-
let score = 0;
|
|
3573
|
-
for (const q2 of queryTokens) if (t.has(q2)) score += idf?.get(q2) ?? 1;
|
|
3574
|
-
return score;
|
|
3575
|
-
}
|
|
3576
|
-
function topByRelevance(items, query, text, k, corpus) {
|
|
3577
|
-
if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] };
|
|
3578
|
-
if (items.length <= k || !query.trim()) return { kept: items, rest: [] };
|
|
3579
|
-
const q2 = tokenize(query);
|
|
3580
|
-
if (q2.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };
|
|
3581
|
-
const idf = corpus?.length ? idfWeights(corpus) : void 0;
|
|
3582
|
-
const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q2, idf) }));
|
|
3583
|
-
scored.sort((a, b) => b.s - a.s || a.i - b.i);
|
|
3584
|
-
const keep = new Set(scored.slice(0, k).map((e) => e.i));
|
|
3585
|
-
return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };
|
|
3526
|
+
// src/catalog.ts
|
|
3527
|
+
var DEFAULT_CATALOG_BUDGET = 6e3;
|
|
3528
|
+
var enc = new TextEncoder();
|
|
3529
|
+
var bytes = (s) => enc.encode(s).length;
|
|
3530
|
+
function clip(s, n) {
|
|
3531
|
+
const cps = [...s];
|
|
3532
|
+
if (cps.length <= n) return s;
|
|
3533
|
+
return n <= 1 ? "\u2026" : cps.slice(0, n - 1).join("").trimEnd() + "\u2026";
|
|
3534
|
+
}
|
|
3535
|
+
function formatCatalog(entries, opts) {
|
|
3536
|
+
const budget = opts.budgetBytes ?? DEFAULT_CATALOG_BUDGET;
|
|
3537
|
+
const pre = opts.namePrefix ?? "";
|
|
3538
|
+
const wrap = (body) => [opts.title, body, opts.footer].filter((x) => x !== void 0 && x !== "").join("\n");
|
|
3539
|
+
const render = (max) => wrap(entries.map((e) => {
|
|
3540
|
+
const d = (e.description ?? "").replace(/\s+/g, " ").trim();
|
|
3541
|
+
const desc = d && max > 0 ? ` \u2014 ${clip(d, max)}` : "";
|
|
3542
|
+
return `- **${pre}${e.name}**${desc}${e.path ? ` (\`${e.path}\`)` : ""}`;
|
|
3543
|
+
}).join("\n"));
|
|
3544
|
+
const longest = Math.max(0, ...entries.map((e) => [...e.description ?? ""].length));
|
|
3545
|
+
const full = render(longest);
|
|
3546
|
+
if (bytes(full) <= budget) return full;
|
|
3547
|
+
if (bytes(render(0)) <= budget) {
|
|
3548
|
+
let lo = 0, hi = longest;
|
|
3549
|
+
while (lo < hi) {
|
|
3550
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
3551
|
+
if (bytes(render(mid)) <= budget) lo = mid;
|
|
3552
|
+
else hi = mid - 1;
|
|
3553
|
+
}
|
|
3554
|
+
return render(lo);
|
|
3555
|
+
}
|
|
3556
|
+
const names = wrap(entries.map((e) => `- **${pre}${e.name}**`).join("\n"));
|
|
3557
|
+
if (bytes(names) <= budget) return names;
|
|
3558
|
+
return wrap(`- ${entries.map((e) => pre + e.name).join(", ")}`);
|
|
3586
3559
|
}
|
|
3587
3560
|
|
|
3588
3561
|
// src/frontmatter.ts
|
|
@@ -3604,7 +3577,6 @@ function grabField(block, key) {
|
|
|
3604
3577
|
}
|
|
3605
3578
|
|
|
3606
3579
|
// src/commands.ts
|
|
3607
|
-
var MAX_CATALOG = 25;
|
|
3608
3580
|
function parseFrontmatter(md) {
|
|
3609
3581
|
const { block, body } = splitFrontmatter(md);
|
|
3610
3582
|
return { description: grabField(block, "description"), body };
|
|
@@ -3661,9 +3633,12 @@ async function scanCommands(fs, dir) {
|
|
|
3661
3633
|
async function loadCommands(fs, dir, opts = {}) {
|
|
3662
3634
|
const commands = await scanCommands(fs, dir);
|
|
3663
3635
|
if (commands.length === 0) return { commands, catalog: "" };
|
|
3664
|
-
const
|
|
3665
|
-
|
|
3666
|
-
|
|
3636
|
+
const catalog = formatCatalog(commands, {
|
|
3637
|
+
title: "## Slash commands (reusable prompt templates)",
|
|
3638
|
+
footer: "To run one, call the `SlashCommand` tool with its name (and optional `args`); it returns the expanded template to act on.",
|
|
3639
|
+
namePrefix: "/",
|
|
3640
|
+
budgetBytes: opts.budgetBytes
|
|
3641
|
+
});
|
|
3667
3642
|
const tool = {
|
|
3668
3643
|
name: "SlashCommand",
|
|
3669
3644
|
description: "Run a slash command (or skill) by name \u2014 returns its expanded prompt template/instructions (with your `args` substituted/appended). Resolves both the command and skill namespaces. Use when a task matches a listed command or skill.",
|
|
@@ -3691,7 +3666,6 @@ async function loadCommands(fs, dir, opts = {}) {
|
|
|
3691
3666
|
}
|
|
3692
3667
|
|
|
3693
3668
|
// src/skills.ts
|
|
3694
|
-
var MAX_CATALOG2 = 25;
|
|
3695
3669
|
function parseFrontmatter2(md) {
|
|
3696
3670
|
const { block } = splitFrontmatter(md);
|
|
3697
3671
|
const b = block || md.slice(0, 600);
|
|
@@ -3717,9 +3691,11 @@ async function scanSkills(fs, dir) {
|
|
|
3717
3691
|
async function loadSkills(fs, dir, opts = {}) {
|
|
3718
3692
|
const skills = await scanSkills(fs, dir);
|
|
3719
3693
|
if (skills.length === 0) return { skills, catalog: "" };
|
|
3720
|
-
const
|
|
3721
|
-
|
|
3722
|
-
|
|
3694
|
+
const catalog = formatCatalog(skills, {
|
|
3695
|
+
title: "## Skills (load one before acting on a matching task)",
|
|
3696
|
+
footer: "When a task matches a skill, call the `Skill` tool with its name to load its full instructions.",
|
|
3697
|
+
budgetBytes: opts.budgetBytes
|
|
3698
|
+
});
|
|
3723
3699
|
const tool = {
|
|
3724
3700
|
name: "Skill",
|
|
3725
3701
|
description: "Load a skill (or slash command) by name \u2014 returns its full instructions (SKILL.md / expanded template), with any `args` substituted/appended. Resolves both the skill and command namespaces. Use when a task matches a listed skill or command.",
|
|
@@ -3744,7 +3720,73 @@ async function loadSkills(fs, dir, opts = {}) {
|
|
|
3744
3720
|
|
|
3745
3721
|
// src/memory.ts
|
|
3746
3722
|
init_tools_structured();
|
|
3747
|
-
|
|
3723
|
+
|
|
3724
|
+
// src/relevance.ts
|
|
3725
|
+
var STOP = /* @__PURE__ */ new Set([
|
|
3726
|
+
"the",
|
|
3727
|
+
"and",
|
|
3728
|
+
"for",
|
|
3729
|
+
"with",
|
|
3730
|
+
"that",
|
|
3731
|
+
"this",
|
|
3732
|
+
"from",
|
|
3733
|
+
"into",
|
|
3734
|
+
"your",
|
|
3735
|
+
"you",
|
|
3736
|
+
"are",
|
|
3737
|
+
"was",
|
|
3738
|
+
"will",
|
|
3739
|
+
"use",
|
|
3740
|
+
"using",
|
|
3741
|
+
"run",
|
|
3742
|
+
"add",
|
|
3743
|
+
"fix",
|
|
3744
|
+
"make",
|
|
3745
|
+
"file",
|
|
3746
|
+
"files",
|
|
3747
|
+
"code",
|
|
3748
|
+
"please",
|
|
3749
|
+
"need",
|
|
3750
|
+
"want",
|
|
3751
|
+
"should",
|
|
3752
|
+
"all"
|
|
3753
|
+
]);
|
|
3754
|
+
function tokenize(s) {
|
|
3755
|
+
const out = /* @__PURE__ */ new Set();
|
|
3756
|
+
for (const w of String(s ?? "").toLowerCase().match(/[a-z0-9]+/g) ?? []) {
|
|
3757
|
+
if (w.length >= 3 && !STOP.has(w)) out.add(w);
|
|
3758
|
+
}
|
|
3759
|
+
return out;
|
|
3760
|
+
}
|
|
3761
|
+
function idfWeights(corpus) {
|
|
3762
|
+
const N = corpus.length;
|
|
3763
|
+
if (N === 0) return /* @__PURE__ */ new Map();
|
|
3764
|
+
const df = /* @__PURE__ */ new Map();
|
|
3765
|
+
for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
3766
|
+
const idf = /* @__PURE__ */ new Map();
|
|
3767
|
+
for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1);
|
|
3768
|
+
return idf;
|
|
3769
|
+
}
|
|
3770
|
+
function relevanceScore(text, queryTokens, idf) {
|
|
3771
|
+
if (queryTokens.size === 0) return 0;
|
|
3772
|
+
const t = tokenize(text);
|
|
3773
|
+
let score = 0;
|
|
3774
|
+
for (const q2 of queryTokens) if (t.has(q2)) score += idf?.get(q2) ?? 1;
|
|
3775
|
+
return score;
|
|
3776
|
+
}
|
|
3777
|
+
function topByRelevance(items, query, text, k, corpus) {
|
|
3778
|
+
if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] };
|
|
3779
|
+
if (items.length <= k || !query.trim()) return { kept: items, rest: [] };
|
|
3780
|
+
const q2 = tokenize(query);
|
|
3781
|
+
if (q2.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };
|
|
3782
|
+
const idf = corpus?.length ? idfWeights(corpus) : void 0;
|
|
3783
|
+
const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q2, idf) }));
|
|
3784
|
+
scored.sort((a, b) => b.s - a.s || a.i - b.i);
|
|
3785
|
+
const keep = new Set(scored.slice(0, k).map((e) => e.i));
|
|
3786
|
+
return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };
|
|
3787
|
+
}
|
|
3788
|
+
|
|
3789
|
+
// src/memory.ts
|
|
3748
3790
|
var MAX_INDEX_LINES = 200;
|
|
3749
3791
|
var MAX_INDEX_BYTES = 25e3;
|
|
3750
3792
|
var MEMORY_PROMPT = '## Memory guidelines\n**When to Remember:** user corrections ("no, do X instead"), preferences ("I prefer Y"), project constraints, recurring gotchas, role/context ("I\'m a data scientist"), workflow patterns confirmed by the user. Capture the WHY, not just the what.\n**When NOT to Remember:** task-specific scratch, transient state, things derivable from code/git, conversation filler, anything already in instruction files. If removing the memory wouldn\'t change future behavior, don\'t save it.\n**Types** (pass to Remember \u2014 this determines where the memory is stored):\n- `user` \u2014 role, preferences, knowledge level, collaboration style (GLOBAL \u2014 follows you across projects)\n- `feedback` \u2014 corrections, what to avoid/repeat, with WHY (GLOBAL)\n- `project` \u2014 ongoing work, goals, constraints, deadlines (LOCAL to this project; convert relative dates to absolute)\n- `reference` \u2014 pointers to external resources, URLs, vendor docs (LOCAL to this project)\n**Before acting on a recalled memory:** verify it\'s still true \u2014 a memory naming a file/function/flag is a claim from when it was written. Grep/read to confirm before recommending. Stale memory \u2192 update via Remember (same slug overwrites).\n**Forget:** when the user asks to remove/delete/forget a memory, call `Forget` with its slug \u2014 it deletes the fact and its index line. Never "remove" by re-Remembering a note that says it was removed: that keeps the memory.\n**Dedup:** before writing, check if a similar memory exists (Recall or MemorySearch). Update the existing one rather than creating duplicates.\nCall `Recall` with a slug (or multiple slugs/a pattern) to load full bodies when relevant.\nCall `MemorySearch` with a query to find memories by content when you don\'t know the slug.\nCall `Remember` to persist a durable fact for future sessions.';
|
|
@@ -3779,10 +3821,18 @@ async function loadMemory(fs, dir, opts = {}) {
|
|
|
3779
3821
|
}
|
|
3780
3822
|
}
|
|
3781
3823
|
if (!hasContent) return { index: MEMORY_PROMPT, tools };
|
|
3782
|
-
const
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3824
|
+
const entries = allPointers.map((l) => {
|
|
3825
|
+
const m = l.match(/\]\(([^)]+)\.md\)/);
|
|
3826
|
+
const slug2 = m?.[1] ?? l;
|
|
3827
|
+
const title = m ? l.slice(l.indexOf("[") + 1, m.index).trim() : "";
|
|
3828
|
+
const hook = m ? l.slice(m.index + m[0].length).replace(/^\s*(?:[—:-]\s*)?/, "").trim() : "";
|
|
3829
|
+
return { name: slug2, description: title && title !== slug2 ? hook ? `${title} \u2014 ${hook}` : title : hook };
|
|
3830
|
+
});
|
|
3831
|
+
const index = MEMORY_PROMPT + "\n\n" + formatCatalog(entries, {
|
|
3832
|
+
title: "## Memory index (persistent context \u2014 recalled across sessions)" + (header ? "\n" + header : ""),
|
|
3833
|
+
footer: "Call `Recall` with a slug to load its full fact.",
|
|
3834
|
+
budgetBytes: opts.budgetBytes
|
|
3835
|
+
});
|
|
3786
3836
|
return { index, tools };
|
|
3787
3837
|
}
|
|
3788
3838
|
var TERM_STOPWORDS = new Set(`the and for but not you your yours our ours his her hers its their they them this that these those
|
|
@@ -4082,6 +4132,25 @@ ${sections.join("\n\n---\n\n")}`;
|
|
|
4082
4132
|
}
|
|
4083
4133
|
return "";
|
|
4084
4134
|
}
|
|
4135
|
+
async function loadContextFiles(fs, specs = []) {
|
|
4136
|
+
const sections = [];
|
|
4137
|
+
for (const { path, maxChars, label } of specs) {
|
|
4138
|
+
if (!await fs.exists(path)) continue;
|
|
4139
|
+
let md = (await fs.readFile(path)).trim();
|
|
4140
|
+
if (!md) continue;
|
|
4141
|
+
if (maxChars !== void 0) {
|
|
4142
|
+
const cps = [...md], n = Math.max(0, maxChars);
|
|
4143
|
+
if (cps.length > n) md = `${cps.slice(0, n).join("").trimEnd()}
|
|
4144
|
+
\u2026 (truncated \u2014 full file: ${path})`;
|
|
4145
|
+
}
|
|
4146
|
+
sections.push(`<!-- ${path} -->
|
|
4147
|
+
${label ? `### ${label}
|
|
4148
|
+
` : ""}${md}`);
|
|
4149
|
+
}
|
|
4150
|
+
return sections.length ? `## Context files
|
|
4151
|
+
|
|
4152
|
+
${sections.join("\n\n---\n\n")}` : "";
|
|
4153
|
+
}
|
|
4085
4154
|
|
|
4086
4155
|
// src/subagent.ts
|
|
4087
4156
|
init_tools();
|
|
@@ -4971,6 +5040,8 @@ var AgentOptions = class {
|
|
|
4971
5040
|
* Walks the VFS tree and merges all found files (general → specific, like Claude Code).
|
|
4972
5041
|
* `true` (default) = auto-discover standard names. `string[]` = custom names. `false` = skip. */
|
|
4973
5042
|
instructionFiles = true;
|
|
5043
|
+
/** Extra files injected right after the instructions (missing → skipped; over `maxChars` → head + pointer to the full file). */
|
|
5044
|
+
contextFiles;
|
|
4974
5045
|
/** Host interaction channel (human-in-the-loop). If set: adds the `AskUserQuestion` tool. */
|
|
4975
5046
|
host;
|
|
4976
5047
|
/** Add the `AskUserQuestion` tool when a host is present (default true). Set false for an agent that
|
|
@@ -5299,7 +5370,7 @@ var Agent = class _Agent {
|
|
|
5299
5370
|
* across turns (and the frozen prompt pairs well with prompt caching). Does NOT touch
|
|
5300
5371
|
* the transcript; `run`/`send` own that.
|
|
5301
5372
|
*/
|
|
5302
|
-
async prepare(
|
|
5373
|
+
async prepare() {
|
|
5303
5374
|
if (this.prepared) return this.systemPromptCache;
|
|
5304
5375
|
const o = this.options;
|
|
5305
5376
|
const fs = o.fs;
|
|
@@ -5317,8 +5388,12 @@ var Agent = class _Agent {
|
|
|
5317
5388
|
const ins = await loadInstructions(fs, names);
|
|
5318
5389
|
if (ins) systemPrompt += "\n\n" + ins;
|
|
5319
5390
|
}
|
|
5391
|
+
if (o.contextFiles?.length) {
|
|
5392
|
+
const ctxFiles = await loadContextFiles(fs, o.contextFiles);
|
|
5393
|
+
if (ctxFiles) systemPrompt += "\n\n" + ctxFiles;
|
|
5394
|
+
}
|
|
5320
5395
|
if (o.memoryDir) {
|
|
5321
|
-
const { index, tools: memTools } = await loadMemory(fs, o.memoryDir, {
|
|
5396
|
+
const { index, tools: memTools } = await loadMemory(fs, o.memoryDir, { userDir: o.memoryUserDir });
|
|
5322
5397
|
if (index) systemPrompt += "\n\n" + index;
|
|
5323
5398
|
tools = [...tools, ...memTools];
|
|
5324
5399
|
}
|
|
@@ -5328,7 +5403,7 @@ var Agent = class _Agent {
|
|
|
5328
5403
|
const c = (await scanCommands(fs, commandsDir)).find((x) => x.name === name);
|
|
5329
5404
|
return c ? { path: c.path, stripFrontmatter: true } : void 0;
|
|
5330
5405
|
} : void 0;
|
|
5331
|
-
const { catalog, tool } = await loadSkills(fs, skillsDir, {
|
|
5406
|
+
const { catalog, tool } = await loadSkills(fs, skillsDir, { sibling });
|
|
5332
5407
|
if (catalog) systemPrompt += "\n\n" + catalog;
|
|
5333
5408
|
if (tool) tools = [...tools, tool];
|
|
5334
5409
|
}
|
|
@@ -5337,7 +5412,7 @@ var Agent = class _Agent {
|
|
|
5337
5412
|
const s = (await scanSkills(fs, skillsDir)).find((x) => x.name === name);
|
|
5338
5413
|
return s ? { path: s.path, stripFrontmatter: false } : void 0;
|
|
5339
5414
|
} : void 0;
|
|
5340
|
-
const { catalog, tool } = await loadCommands(fs, commandsDir, {
|
|
5415
|
+
const { catalog, tool } = await loadCommands(fs, commandsDir, { sibling });
|
|
5341
5416
|
if (catalog) systemPrompt += "\n\n" + catalog;
|
|
5342
5417
|
if (tool) tools = [...tools, tool];
|
|
5343
5418
|
}
|
|
@@ -5396,7 +5471,7 @@ var Agent = class _Agent {
|
|
|
5396
5471
|
this.invokedTools.clear();
|
|
5397
5472
|
this.fabricatedClaims.clear();
|
|
5398
5473
|
this.lastTask = contentText(task);
|
|
5399
|
-
const systemPrompt = await this.prepare(
|
|
5474
|
+
const systemPrompt = await this.prepare();
|
|
5400
5475
|
const startCtx = await this.fireSessionStart();
|
|
5401
5476
|
const userContent = await this.applyPromptSubmit(task);
|
|
5402
5477
|
this.transcript = [
|
|
@@ -5432,7 +5507,7 @@ var Agent = class _Agent {
|
|
|
5432
5507
|
async send(task) {
|
|
5433
5508
|
await this.ensureFs();
|
|
5434
5509
|
this.lastTask = contentText(task);
|
|
5435
|
-
const systemPrompt = await this.prepare(
|
|
5510
|
+
const systemPrompt = await this.prepare();
|
|
5436
5511
|
const startCtx = await this.fireSessionStart();
|
|
5437
5512
|
const userContent = await this.applyPromptSubmit(task);
|
|
5438
5513
|
const sys = systemPrompt + (startCtx ? "\n\n" + startCtx : "");
|
|
@@ -6256,9 +6331,9 @@ function capOversizedBodies(messages, target, keepRecentImages = 1) {
|
|
|
6256
6331
|
return out;
|
|
6257
6332
|
}
|
|
6258
6333
|
function elideBody(x, cap) {
|
|
6259
|
-
const note = (
|
|
6334
|
+
const note = (bytes2, imgs2) => `
|
|
6260
6335
|
|
|
6261
|
-
[${
|
|
6336
|
+
[${bytes2} bytes elided by /compact${x.role === "tool" ? " \u2014 re-run the tool to view" : ""}${imgs2 ? `; ${imgs2} image part(s) KEPT in full \u2014 they still cost context` : ""}]`;
|
|
6262
6337
|
if (!Array.isArray(x.content)) {
|
|
6263
6338
|
const text = contentText(x.content);
|
|
6264
6339
|
if (text.length <= cap) return x;
|
|
@@ -12417,11 +12492,11 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12417
12492
|
const list = id ? [this.tasks.get(String(id))].filter(Boolean) : [...this.tasks.values()];
|
|
12418
12493
|
if (!list.length) return id ? `No task '${id}'.` : "No background tasks.";
|
|
12419
12494
|
const cap = id ? 1500 : 300;
|
|
12420
|
-
const
|
|
12495
|
+
const clip2 = (x) => x.length > cap ? `${x.slice(0, cap)}\u2026 (truncated)` : x;
|
|
12421
12496
|
return list.map((t) => {
|
|
12422
12497
|
const head = `${t.id} (${t.label}): ${t.status}`;
|
|
12423
12498
|
const body = (t.status === "done" ? t.spokenText?.trim() || t.result?.trim() : t.status === "error" ? t.result?.trim() : "") ?? "";
|
|
12424
|
-
return body ? `${head}. ${t.status === "error" ? "Error" : "Result"}: ${
|
|
12499
|
+
return body ? `${head}. ${t.status === "error" ? "Error" : "Result"}: ${clip2(body)}` : head;
|
|
12425
12500
|
}).join("\n");
|
|
12426
12501
|
}
|
|
12427
12502
|
};
|
|
@@ -19765,7 +19840,7 @@ function makeHost(format = "text", opts) {
|
|
|
19765
19840
|
spinner.start();
|
|
19766
19841
|
return;
|
|
19767
19842
|
}
|
|
19768
|
-
const
|
|
19843
|
+
const clip2 = (s) => s.length > 200 ? s.slice(0, 200) + "\u2026" : s;
|
|
19769
19844
|
const terminal = a.status === "completed" || a.status === "error";
|
|
19770
19845
|
if (!terminal || !a.id || !activitySeen.has(a.id)) {
|
|
19771
19846
|
if (a.id) activitySeen.add(a.id);
|
|
@@ -19784,8 +19859,8 @@ function makeHost(format = "text", opts) {
|
|
|
19784
19859
|
}
|
|
19785
19860
|
const lines = text.split("\n");
|
|
19786
19861
|
const shown = lines.slice(0, previewLines());
|
|
19787
|
-
err(paint(" \u23BF " +
|
|
19788
|
-
for (const ln of shown.slice(1)) err(dim(" " +
|
|
19862
|
+
err(paint(" \u23BF " + clip2(shown[0])) + "\n");
|
|
19863
|
+
for (const ln of shown.slice(1)) err(dim(" " + clip2(ln)) + "\n");
|
|
19789
19864
|
const more = lines.length - shown.length;
|
|
19790
19865
|
if (more > 0 || a.truncated?.result) err(dim(` \u2026 (${more > 0 ? `+${more} more line${more > 1 ? "s" : ""}` : "truncated by backend"}${a.truncated?.result && more > 0 ? ", truncated" : ""} \xB7 ctrl+o expands)
|
|
19791
19866
|
`));
|
|
@@ -20021,7 +20096,7 @@ function formatTranscriptFull(messages, opts) {
|
|
|
20021
20096
|
if (!shown.length) return dim(" (empty transcript)\n");
|
|
20022
20097
|
const results = /* @__PURE__ */ new Map();
|
|
20023
20098
|
for (const m of shown) if (m.role === "tool" && m.tool_call_id) results.set(m.tool_call_id, m);
|
|
20024
|
-
const
|
|
20099
|
+
const clip2 = (s) => {
|
|
20025
20100
|
const lines = s.split("\n");
|
|
20026
20101
|
return lines.length > cap ? lines.slice(0, cap).join("\n") + `
|
|
20027
20102
|
\u2026 (+${lines.length - cap} more lines)` : s;
|
|
@@ -20042,7 +20117,7 @@ function formatTranscriptFull(messages, opts) {
|
|
|
20042
20117
|
}
|
|
20043
20118
|
out.push(cyan(" \u2699 ") + tc.function.name + dim(" " + summarizeCall(tc.function.name, args)) + "\n");
|
|
20044
20119
|
const r = results.get(tc.id);
|
|
20045
|
-
if (r) out.push(dim(" " +
|
|
20120
|
+
if (r) out.push(dim(" " + clip2(contentText(r.content).trimEnd()).replace(/\n/g, "\n ")) + "\n");
|
|
20046
20121
|
}
|
|
20047
20122
|
}
|
|
20048
20123
|
}
|