@livx.cc/agentx 0.99.123 → 0.99.125
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 +185 -104
- 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/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as AgentOptions, H as Hooks,
|
|
2
|
-
export { A as ADVISE_LIMIT, a as AdvisorSetup, C as CC_WRITE_TOOLS, e as ChatFragment, f as ChatUsage, g as Classification, h as Classifier, i as CompactResult, D as DEFAULT_MUTATING,
|
|
1
|
+
import { c as AgentOptions, H as Hooks, x as RunResult, k as Decider, b as Agent, q as PermissionPolicy, G as Gender, L as LineKind, d as ChannelMode, y as SecondChannelOptions, S as SecondChannel } from './secondChannel-GFT69ax4.js';
|
|
2
|
+
export { A as ADVISE_LIMIT, a as AdvisorSetup, C as CC_WRITE_TOOLS, e as ChatFragment, f as ChatUsage, g as Classification, h as Classifier, i as CompactResult, j as ContextFileSpec, D as DEFAULT_MUTATING, l as Decision, M as ModelRouter, m as ModelRouterOptions, P as PLANNER_BLOCKED, n as PLANNER_REFUSAL, o as PLAN_FIRST, p as PermissionOptions, r as PermissionRule, s as PreToolUseDecision, R as ReasoningEffort, t as RecordingHooks, u as RecordingLifecycle, v as RouteDecision, w as RouterState, T as Tier, z as ToolUse, B as ToolUseMeta, W as WRITE_TOOLS, E as advisorFor, F as advisorPrompt, I as ccModeOptions, J as claudeCodeDisallowedTools, K as composeHooks, N as delegatePrompt, O as delegateTools, Q as delegateWorkerFor, U as estimateTokens, V as jevAvailable, X as jevClassifier, Y as loadContextFiles, Z as loadInstructions, _ as makeAdvisor, $ as meterWorkers, a0 as pickTier, a1 as planMode, a2 as plannerBlocks, a3 as reasoningToChatFragment } from './secondChannel-GFT69ax4.js';
|
|
3
3
|
export { MODEL_ALIASES, ModelSwitch, ResolveModelSwitchOpts, latestClaude, modelShortLabel, resolveModelAlias, resolveModelSwitch } from './models.js';
|
|
4
4
|
import { IFilesystem, FileMetadata } from '@livx.cc/wcli/core';
|
|
5
5
|
export { CommandExecutor, FileMetadata, IFilesystem, IndexedDbFilesystem, MemFilesystem, registerHeadlessCommands } from '@livx.cc/wcli/core';
|
|
@@ -175,6 +175,8 @@ declare class JailOptions {
|
|
|
175
175
|
deny: string[];
|
|
176
176
|
/** Globs readable but not writable/deletable (the pinned constitution). */
|
|
177
177
|
readonly: string[];
|
|
178
|
+
/** Appended to readonly-violation errors (e.g. "edit it via the kb tool instead"). */
|
|
179
|
+
readonlyHint?: string;
|
|
178
180
|
/** If set, only paths matching these globs are accessible at all. */
|
|
179
181
|
confineTo?: string[];
|
|
180
182
|
/** Audit hook fired on every blocked operation (drives the circuit breaker). */
|
|
@@ -753,8 +755,12 @@ declare function expandCommand(fs: IFilesystem, cmd: CommandInfo, args: string):
|
|
|
753
755
|
/** Scan `dir`(s) for `<dir>/<name>.md` command templates (first dir wins on duplicate names). */
|
|
754
756
|
declare function scanCommands(fs: IFilesystem, dir: string | string[]): Promise<CommandInfo[]>;
|
|
755
757
|
declare function loadCommands(fs: IFilesystem, dir: string | string[], opts?: {
|
|
758
|
+
/** @deprecated no-op — the catalog is full + task-independent (stable prompt prefix). */
|
|
756
759
|
relevanceHint?: string;
|
|
760
|
+
/** @deprecated no-op — use `budgetBytes`. */
|
|
757
761
|
max?: number;
|
|
762
|
+
/** Catalog byte budget (default 6000); over it descriptions trim, names never drop. */
|
|
763
|
+
budgetBytes?: number;
|
|
758
764
|
sibling?: SiblingResolver;
|
|
759
765
|
}): Promise<{
|
|
760
766
|
commands: CommandInfo[];
|
|
@@ -779,8 +785,12 @@ interface SkillInfo {
|
|
|
779
785
|
/** Scan `dir`(s) for `<dir>/<id>/SKILL.md` entries (first dir wins on duplicate ids). */
|
|
780
786
|
declare function scanSkills(fs: IFilesystem, dir: string | string[]): Promise<SkillInfo[]>;
|
|
781
787
|
declare function loadSkills(fs: IFilesystem, dir: string | string[], opts?: {
|
|
788
|
+
/** @deprecated no-op — the catalog is full + task-independent (stable prompt prefix). */
|
|
782
789
|
relevanceHint?: string;
|
|
790
|
+
/** @deprecated no-op — use `budgetBytes`. */
|
|
783
791
|
max?: number;
|
|
792
|
+
/** Catalog byte budget (default 6000); over it descriptions trim, names never drop. */
|
|
793
|
+
budgetBytes?: number;
|
|
784
794
|
sibling?: SiblingResolver;
|
|
785
795
|
}): Promise<{
|
|
786
796
|
skills: SkillInfo[];
|
|
@@ -827,8 +837,12 @@ declare const VOICE_MEMORY_PROMPT: string;
|
|
|
827
837
|
* memory mechanism therefore works in node, a browser tab, or on edge.
|
|
828
838
|
*/
|
|
829
839
|
interface LoadMemoryOpts {
|
|
840
|
+
/** @deprecated no-op — the index is full + task-independent (stable prompt prefix). */
|
|
830
841
|
relevanceHint?: string;
|
|
842
|
+
/** @deprecated no-op — use `budgetBytes`. */
|
|
831
843
|
max?: number;
|
|
844
|
+
/** Index byte budget (default 6000); over it hooks trim, slugs never drop. */
|
|
845
|
+
budgetBytes?: number;
|
|
832
846
|
/** Max Remember calls per session (default 25). Prevents runaway memory writes. */
|
|
833
847
|
maxWritesPerSession?: number;
|
|
834
848
|
/** Called after each successful memory write (auditing, UI indicators). */
|
|
@@ -901,14 +915,30 @@ interface ReflectOptions {
|
|
|
901
915
|
}
|
|
902
916
|
declare function reflectOnRun(o: ReflectOptions): Promise<string | null>;
|
|
903
917
|
|
|
918
|
+
/** One catalog row: a loadable thing (skill, command, memory) the model can pull by name. */
|
|
919
|
+
interface CatalogEntry {
|
|
920
|
+
name: string;
|
|
921
|
+
description?: string;
|
|
922
|
+
path?: string;
|
|
923
|
+
}
|
|
924
|
+
interface CatalogOptions {
|
|
925
|
+
/** Heading line (e.g. `## Skills …`). */
|
|
926
|
+
title: string;
|
|
927
|
+
/** Closing line (how to load an entry). */
|
|
928
|
+
footer?: string;
|
|
929
|
+
/** Max UTF-8 bytes for the whole block (default 6000). Names are never dropped, so a huge set may exceed it. */
|
|
930
|
+
budgetBytes?: number;
|
|
931
|
+
/** Prefix shown before each name (e.g. `/` for commands). */
|
|
932
|
+
namePrefix?: string;
|
|
933
|
+
}
|
|
934
|
+
declare const DEFAULT_CATALOG_BUDGET = 6000;
|
|
904
935
|
/**
|
|
905
|
-
*
|
|
906
|
-
* (
|
|
907
|
-
*
|
|
908
|
-
*
|
|
909
|
-
* (no duplicates from the same level). Edge-portable: works over any IFilesystem.
|
|
936
|
+
* Render a full, stable catalog: every entry as `- **name** — desc (\`path\`)`.
|
|
937
|
+
* Over `budgetBytes`, descriptions are trimmed evenly (one shared max length), then — if even
|
|
938
|
+
* empty descriptions don't fit — names only. Never drops a name; output depends only on the
|
|
939
|
+
* entries (no task hint), so the prompt prefix stays byte-identical across turns/tasks.
|
|
910
940
|
*/
|
|
911
|
-
declare function
|
|
941
|
+
declare function formatCatalog(entries: CatalogEntry[], opts: CatalogOptions): string;
|
|
912
942
|
|
|
913
943
|
type OverlapVerdict = 'keep' | 'cut';
|
|
914
944
|
|
|
@@ -1852,4 +1882,4 @@ declare function exitWorktree(): {
|
|
|
1852
1882
|
reason?: string;
|
|
1853
1883
|
} | null;
|
|
1854
1884
|
|
|
1855
|
-
export { Agent, type AgentDef, AgentOptions, AgentTool, type AskOptions, type Attempt, BodDbFilesystem, type CallMetric, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, DEFAULT_MAX_DEPTH, DEFAULT_SKIP_DIRS, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, type FileScanOptions, type FillKind, type GateDrop, type GateTurn, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type LoadMemoryOpts, MEMORY_PROMPT, type MainEvent, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, PROGRESS_TEMPLATES, PermissionPolicy, Progress, ProgressOptions, type ReflectOptions, RunResult, SCRATCH_DIR, type ScheduledJob, type ScheduledJobSnapshot, Scheduler, type SchedulerOptions, Scratch, type ScratchOptions, ScriptedHostBridge, type SiblingResolver, type SkillInfo, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, type Trigger, type TriggerCron, type TriggerInterval, type TriggerOneOff, TurnGate, TurnGateOptions, UserQuestion, VOICE_MEMORY_PROMPT, VOICE_SYSTEM_PROMPT, VoiceCall, VoiceCallOptions, type WebFetchOptions, type WebSearchOptions, type WorkerTier, type WorktreeInfo, type WorktreeSession, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, cleanupWorktree, compileSynthesizedTool, cronMatches, decodeDdgUrl, diskAgentOptions, endsMidPhrase, enterWorktree, exitWorktree, expandCommand, expandEntry, expandTemplate, findGitRoot, forComponent, forgetFact, fullAgentOptions, getOrCreateWorktree, getWorktreeSession, globTool, grepTool, htmlToText, idfWeights, isClosing, lessonCapture, loadAgents, loadCommands,
|
|
1885
|
+
export { Agent, type AgentDef, AgentOptions, AgentTool, type AskOptions, type Attempt, BodDbFilesystem, type CallMetric, type CatalogEntry, type CatalogOptions, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_CATALOG_BUDGET, DEFAULT_DENY, DEFAULT_MAX_DEPTH, DEFAULT_SKIP_DIRS, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, type FileScanOptions, type FillKind, type GateDrop, type GateTurn, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type LoadMemoryOpts, MEMORY_PROMPT, type MainEvent, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, PROGRESS_TEMPLATES, PermissionPolicy, Progress, ProgressOptions, type ReflectOptions, RunResult, SCRATCH_DIR, type ScheduledJob, type ScheduledJobSnapshot, Scheduler, type SchedulerOptions, Scratch, type ScratchOptions, ScriptedHostBridge, type SiblingResolver, type SkillInfo, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, type Trigger, type TriggerCron, type TriggerInterval, type TriggerOneOff, TurnGate, TurnGateOptions, UserQuestion, VOICE_MEMORY_PROMPT, VOICE_SYSTEM_PROMPT, VoiceCall, VoiceCallOptions, type WebFetchOptions, type WebSearchOptions, type WorkerTier, type WorktreeInfo, type WorktreeSession, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, cleanupWorktree, compileSynthesizedTool, cronMatches, decodeDdgUrl, diskAgentOptions, endsMidPhrase, enterWorktree, exitWorktree, expandCommand, expandEntry, expandTemplate, findGitRoot, forComponent, forgetFact, formatCatalog, fullAgentOptions, getOrCreateWorktree, getWorktreeSession, globTool, grepTool, htmlToText, idfWeights, isClosing, lessonCapture, loadAgents, loadCommands, loadMemory, loadSkills, makeAskTool, makeGlobTool, makeGrepTool, makeScheduleTools, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchAnthropicTool, makeWebSearchTool, mkdirp, multiEditTool, nextCronAfter, parseCron, parseDdgHtml, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, rollbackTool, sandboxAgentOptions, scanCommands, scanSkills, slugify, tokenize, toolCall, topByRelevance, validateToolCode, validateWorktreeSlug, webFetchTool, webSearchAnthropicTool, webSearchTool, worktreeBranchName, writeFact, writeTool };
|
package/dist/index.js
CHANGED
|
@@ -120,13 +120,13 @@ function elideImageParts(content, hit, stub) {
|
|
|
120
120
|
}
|
|
121
121
|
function mintImageRef(path, mime) {
|
|
122
122
|
const token = randomBytes(16).toString("hex");
|
|
123
|
-
let
|
|
123
|
+
let bytes2 = 0;
|
|
124
124
|
try {
|
|
125
|
-
|
|
125
|
+
bytes2 = Math.ceil(statSync2(path).size * 4 / 3);
|
|
126
126
|
} catch (e) {
|
|
127
127
|
log3.warn(`image ref stat failed, weighing as 0: ${path}`, e);
|
|
128
128
|
}
|
|
129
|
-
refRegistry.set(token, { mime, path, bytes });
|
|
129
|
+
refRegistry.set(token, { mime, path, bytes: bytes2 });
|
|
130
130
|
while (refRegistry.size > REF_REGISTRY_MAX) refRegistry.delete(refRegistry.keys().next().value);
|
|
131
131
|
return `${IMAGE_REF_SCHEME}${mime};${token};${path}`;
|
|
132
132
|
}
|
|
@@ -1634,14 +1634,14 @@ var init_tools = __esm({
|
|
|
1634
1634
|
if (!await ctx.fs.exists(path)) return `Error: File not found: ${path}`;
|
|
1635
1635
|
return imageRefResult(real, IMG_MIME[ext]);
|
|
1636
1636
|
}
|
|
1637
|
-
const
|
|
1638
|
-
const b64 = Buffer.from(
|
|
1637
|
+
const bytes2 = await fs.readFileBytes(path);
|
|
1638
|
+
const b64 = Buffer.from(bytes2).toString("base64");
|
|
1639
1639
|
return JSON.stringify({ dataUrl: `data:${IMG_MIME[ext]};base64,${b64}`, image: path });
|
|
1640
1640
|
}
|
|
1641
1641
|
const raw = await ctx.fs.readFile(path);
|
|
1642
1642
|
if (isBinary(raw)) {
|
|
1643
|
-
const
|
|
1644
|
-
return `[binary file: ${path} \u2014 ${
|
|
1643
|
+
const bytes2 = await byteSize(ctx.fs, path, raw);
|
|
1644
|
+
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}.]`;
|
|
1645
1645
|
}
|
|
1646
1646
|
ctx.readState.set(ctx.fs.resolvePath(path), raw);
|
|
1647
1647
|
if (raw === "") return `[empty file: ${path} \u2014 0 bytes]`;
|
|
@@ -1972,8 +1972,8 @@ var init_tools_jobs = __esm({
|
|
|
1972
1972
|
return [...this.jobs.values()].map((j) => this.info(j));
|
|
1973
1973
|
}
|
|
1974
1974
|
info(j) {
|
|
1975
|
-
const { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes, holds, result } = j;
|
|
1976
|
-
return { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes, holds, result };
|
|
1975
|
+
const { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes: bytes2, holds, result } = j;
|
|
1976
|
+
return { id, kind, label, status, startedAt, lastActivityAt, endedAt, reason, bytes: bytes2, holds, result };
|
|
1977
1977
|
}
|
|
1978
1978
|
/** Wait (bounded) for a job to settle. Resolves the job's info either way; null for an unknown id. */
|
|
1979
1979
|
async wait(id, timeoutMs) {
|
|
@@ -2407,7 +2407,8 @@ var init_JailedFilesystem = __esm({
|
|
|
2407
2407
|
violate(op, abs, rule) {
|
|
2408
2408
|
this.note(op, abs, rule);
|
|
2409
2409
|
if (rule === "deny" || rule === "confine") throw new Error(`File not found: ${abs}`);
|
|
2410
|
-
|
|
2410
|
+
const hint = rule === "readonly" && this.options.readonlyHint ? ` \u2014 ${this.options.readonlyHint}` : "";
|
|
2411
|
+
throw new Error(`Blocked by filesystem jail (${rule}): ${abs}${hint}`);
|
|
2411
2412
|
}
|
|
2412
2413
|
/** Fire the audit hook without throwing (for boolean probes that must still return false). */
|
|
2413
2414
|
note(op, abs, rule) {
|
|
@@ -2532,6 +2533,8 @@ var init_JailedFilesystem = __esm({
|
|
|
2532
2533
|
deny = [...DEFAULT_DENY];
|
|
2533
2534
|
/** Globs readable but not writable/deletable (the pinned constitution). */
|
|
2534
2535
|
readonly = [];
|
|
2536
|
+
/** Appended to readonly-violation errors (e.g. "edit it via the kb tool instead"). */
|
|
2537
|
+
readonlyHint;
|
|
2535
2538
|
/** If set, only paths matching these globs are accessible at all. */
|
|
2536
2539
|
confineTo;
|
|
2537
2540
|
/** Audit hook fired on every blocked operation (drives the circuit breaker). */
|
|
@@ -3287,69 +3290,39 @@ ${prompt} [y/N] `);
|
|
|
3287
3290
|
}
|
|
3288
3291
|
};
|
|
3289
3292
|
|
|
3290
|
-
// src/
|
|
3291
|
-
var
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
"
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
"
|
|
3302
|
-
"
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
"
|
|
3308
|
-
"
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
if (w.length >= 3 && !STOP.has(w)) out.add(w);
|
|
3324
|
-
}
|
|
3325
|
-
return out;
|
|
3326
|
-
}
|
|
3327
|
-
function idfWeights(corpus) {
|
|
3328
|
-
const N = corpus.length;
|
|
3329
|
-
if (N === 0) return /* @__PURE__ */ new Map();
|
|
3330
|
-
const df = /* @__PURE__ */ new Map();
|
|
3331
|
-
for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
3332
|
-
const idf = /* @__PURE__ */ new Map();
|
|
3333
|
-
for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1);
|
|
3334
|
-
return idf;
|
|
3335
|
-
}
|
|
3336
|
-
function relevanceScore(text, queryTokens, idf) {
|
|
3337
|
-
if (queryTokens.size === 0) return 0;
|
|
3338
|
-
const t = tokenize(text);
|
|
3339
|
-
let score = 0;
|
|
3340
|
-
for (const q of queryTokens) if (t.has(q)) score += idf?.get(q) ?? 1;
|
|
3341
|
-
return score;
|
|
3342
|
-
}
|
|
3343
|
-
function topByRelevance(items, query, text, k, corpus) {
|
|
3344
|
-
if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] };
|
|
3345
|
-
if (items.length <= k || !query.trim()) return { kept: items, rest: [] };
|
|
3346
|
-
const q = tokenize(query);
|
|
3347
|
-
if (q.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };
|
|
3348
|
-
const idf = corpus?.length ? idfWeights(corpus) : void 0;
|
|
3349
|
-
const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q, idf) }));
|
|
3350
|
-
scored.sort((a, b) => b.s - a.s || a.i - b.i);
|
|
3351
|
-
const keep = new Set(scored.slice(0, k).map((e) => e.i));
|
|
3352
|
-
return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };
|
|
3293
|
+
// src/catalog.ts
|
|
3294
|
+
var DEFAULT_CATALOG_BUDGET = 6e3;
|
|
3295
|
+
var enc = new TextEncoder();
|
|
3296
|
+
var bytes = (s) => enc.encode(s).length;
|
|
3297
|
+
function clip(s, n) {
|
|
3298
|
+
const cps = [...s];
|
|
3299
|
+
if (cps.length <= n) return s;
|
|
3300
|
+
return n <= 1 ? "\u2026" : cps.slice(0, n - 1).join("").trimEnd() + "\u2026";
|
|
3301
|
+
}
|
|
3302
|
+
function formatCatalog(entries, opts) {
|
|
3303
|
+
const budget = opts.budgetBytes ?? DEFAULT_CATALOG_BUDGET;
|
|
3304
|
+
const pre = opts.namePrefix ?? "";
|
|
3305
|
+
const wrap = (body) => [opts.title, body, opts.footer].filter((x) => x !== void 0 && x !== "").join("\n");
|
|
3306
|
+
const render = (max) => wrap(entries.map((e) => {
|
|
3307
|
+
const d = (e.description ?? "").replace(/\s+/g, " ").trim();
|
|
3308
|
+
const desc = d && max > 0 ? ` \u2014 ${clip(d, max)}` : "";
|
|
3309
|
+
return `- **${pre}${e.name}**${desc}${e.path ? ` (\`${e.path}\`)` : ""}`;
|
|
3310
|
+
}).join("\n"));
|
|
3311
|
+
const longest = Math.max(0, ...entries.map((e) => [...e.description ?? ""].length));
|
|
3312
|
+
const full = render(longest);
|
|
3313
|
+
if (bytes(full) <= budget) return full;
|
|
3314
|
+
if (bytes(render(0)) <= budget) {
|
|
3315
|
+
let lo = 0, hi = longest;
|
|
3316
|
+
while (lo < hi) {
|
|
3317
|
+
const mid = Math.ceil((lo + hi) / 2);
|
|
3318
|
+
if (bytes(render(mid)) <= budget) lo = mid;
|
|
3319
|
+
else hi = mid - 1;
|
|
3320
|
+
}
|
|
3321
|
+
return render(lo);
|
|
3322
|
+
}
|
|
3323
|
+
const names = wrap(entries.map((e) => `- **${pre}${e.name}**`).join("\n"));
|
|
3324
|
+
if (bytes(names) <= budget) return names;
|
|
3325
|
+
return wrap(`- ${entries.map((e) => pre + e.name).join(", ")}`);
|
|
3353
3326
|
}
|
|
3354
3327
|
|
|
3355
3328
|
// src/frontmatter.ts
|
|
@@ -3371,7 +3344,6 @@ function grabField(block, key) {
|
|
|
3371
3344
|
}
|
|
3372
3345
|
|
|
3373
3346
|
// src/commands.ts
|
|
3374
|
-
var MAX_CATALOG = 25;
|
|
3375
3347
|
function parseFrontmatter(md) {
|
|
3376
3348
|
const { block, body } = splitFrontmatter(md);
|
|
3377
3349
|
return { description: grabField(block, "description"), body };
|
|
@@ -3428,9 +3400,12 @@ async function scanCommands(fs, dir) {
|
|
|
3428
3400
|
async function loadCommands(fs, dir, opts = {}) {
|
|
3429
3401
|
const commands = await scanCommands(fs, dir);
|
|
3430
3402
|
if (commands.length === 0) return { commands, catalog: "" };
|
|
3431
|
-
const
|
|
3432
|
-
|
|
3433
|
-
|
|
3403
|
+
const catalog = formatCatalog(commands, {
|
|
3404
|
+
title: "## Slash commands (reusable prompt templates)",
|
|
3405
|
+
footer: "To run one, call the `SlashCommand` tool with its name (and optional `args`); it returns the expanded template to act on.",
|
|
3406
|
+
namePrefix: "/",
|
|
3407
|
+
budgetBytes: opts.budgetBytes
|
|
3408
|
+
});
|
|
3434
3409
|
const tool = {
|
|
3435
3410
|
name: "SlashCommand",
|
|
3436
3411
|
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.",
|
|
@@ -3458,7 +3433,6 @@ async function loadCommands(fs, dir, opts = {}) {
|
|
|
3458
3433
|
}
|
|
3459
3434
|
|
|
3460
3435
|
// src/skills.ts
|
|
3461
|
-
var MAX_CATALOG2 = 25;
|
|
3462
3436
|
function parseFrontmatter2(md) {
|
|
3463
3437
|
const { block } = splitFrontmatter(md);
|
|
3464
3438
|
const b = block || md.slice(0, 600);
|
|
@@ -3484,9 +3458,11 @@ async function scanSkills(fs, dir) {
|
|
|
3484
3458
|
async function loadSkills(fs, dir, opts = {}) {
|
|
3485
3459
|
const skills = await scanSkills(fs, dir);
|
|
3486
3460
|
if (skills.length === 0) return { skills, catalog: "" };
|
|
3487
|
-
const
|
|
3488
|
-
|
|
3489
|
-
|
|
3461
|
+
const catalog = formatCatalog(skills, {
|
|
3462
|
+
title: "## Skills (load one before acting on a matching task)",
|
|
3463
|
+
footer: "When a task matches a skill, call the `Skill` tool with its name to load its full instructions.",
|
|
3464
|
+
budgetBytes: opts.budgetBytes
|
|
3465
|
+
});
|
|
3490
3466
|
const tool = {
|
|
3491
3467
|
name: "Skill",
|
|
3492
3468
|
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.",
|
|
@@ -3511,7 +3487,73 @@ async function loadSkills(fs, dir, opts = {}) {
|
|
|
3511
3487
|
|
|
3512
3488
|
// src/memory.ts
|
|
3513
3489
|
init_tools_structured();
|
|
3514
|
-
|
|
3490
|
+
|
|
3491
|
+
// src/relevance.ts
|
|
3492
|
+
var STOP = /* @__PURE__ */ new Set([
|
|
3493
|
+
"the",
|
|
3494
|
+
"and",
|
|
3495
|
+
"for",
|
|
3496
|
+
"with",
|
|
3497
|
+
"that",
|
|
3498
|
+
"this",
|
|
3499
|
+
"from",
|
|
3500
|
+
"into",
|
|
3501
|
+
"your",
|
|
3502
|
+
"you",
|
|
3503
|
+
"are",
|
|
3504
|
+
"was",
|
|
3505
|
+
"will",
|
|
3506
|
+
"use",
|
|
3507
|
+
"using",
|
|
3508
|
+
"run",
|
|
3509
|
+
"add",
|
|
3510
|
+
"fix",
|
|
3511
|
+
"make",
|
|
3512
|
+
"file",
|
|
3513
|
+
"files",
|
|
3514
|
+
"code",
|
|
3515
|
+
"please",
|
|
3516
|
+
"need",
|
|
3517
|
+
"want",
|
|
3518
|
+
"should",
|
|
3519
|
+
"all"
|
|
3520
|
+
]);
|
|
3521
|
+
function tokenize(s) {
|
|
3522
|
+
const out = /* @__PURE__ */ new Set();
|
|
3523
|
+
for (const w of String(s ?? "").toLowerCase().match(/[a-z0-9]+/g) ?? []) {
|
|
3524
|
+
if (w.length >= 3 && !STOP.has(w)) out.add(w);
|
|
3525
|
+
}
|
|
3526
|
+
return out;
|
|
3527
|
+
}
|
|
3528
|
+
function idfWeights(corpus) {
|
|
3529
|
+
const N = corpus.length;
|
|
3530
|
+
if (N === 0) return /* @__PURE__ */ new Map();
|
|
3531
|
+
const df = /* @__PURE__ */ new Map();
|
|
3532
|
+
for (const doc of corpus) for (const t of tokenize(doc)) df.set(t, (df.get(t) ?? 0) + 1);
|
|
3533
|
+
const idf = /* @__PURE__ */ new Map();
|
|
3534
|
+
for (const [t, n] of df) idf.set(t, Math.log((N + 1) / (n + 1)) + 1);
|
|
3535
|
+
return idf;
|
|
3536
|
+
}
|
|
3537
|
+
function relevanceScore(text, queryTokens, idf) {
|
|
3538
|
+
if (queryTokens.size === 0) return 0;
|
|
3539
|
+
const t = tokenize(text);
|
|
3540
|
+
let score = 0;
|
|
3541
|
+
for (const q of queryTokens) if (t.has(q)) score += idf?.get(q) ?? 1;
|
|
3542
|
+
return score;
|
|
3543
|
+
}
|
|
3544
|
+
function topByRelevance(items, query, text, k, corpus) {
|
|
3545
|
+
if (!Number.isInteger(k) || k < 1) return { kept: items, rest: [] };
|
|
3546
|
+
if (items.length <= k || !query.trim()) return { kept: items, rest: [] };
|
|
3547
|
+
const q = tokenize(query);
|
|
3548
|
+
if (q.size === 0) return { kept: items.slice(0, k), rest: items.slice(k) };
|
|
3549
|
+
const idf = corpus?.length ? idfWeights(corpus) : void 0;
|
|
3550
|
+
const scored = items.map((x, i) => ({ i, s: relevanceScore(text(x), q, idf) }));
|
|
3551
|
+
scored.sort((a, b) => b.s - a.s || a.i - b.i);
|
|
3552
|
+
const keep = new Set(scored.slice(0, k).map((e) => e.i));
|
|
3553
|
+
return { kept: items.filter((_, i) => keep.has(i)), rest: items.filter((_, i) => !keep.has(i)) };
|
|
3554
|
+
}
|
|
3555
|
+
|
|
3556
|
+
// src/memory.ts
|
|
3515
3557
|
var MAX_INDEX_LINES = 200;
|
|
3516
3558
|
var MAX_INDEX_BYTES = 25e3;
|
|
3517
3559
|
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.';
|
|
@@ -3546,10 +3588,18 @@ async function loadMemory(fs, dir, opts = {}) {
|
|
|
3546
3588
|
}
|
|
3547
3589
|
}
|
|
3548
3590
|
if (!hasContent) return { index: MEMORY_PROMPT, tools };
|
|
3549
|
-
const
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3591
|
+
const entries = allPointers.map((l) => {
|
|
3592
|
+
const m = l.match(/\]\(([^)]+)\.md\)/);
|
|
3593
|
+
const slug2 = m?.[1] ?? l;
|
|
3594
|
+
const title = m ? l.slice(l.indexOf("[") + 1, m.index).trim() : "";
|
|
3595
|
+
const hook = m ? l.slice(m.index + m[0].length).replace(/^\s*(?:[—:-]\s*)?/, "").trim() : "";
|
|
3596
|
+
return { name: slug2, description: title && title !== slug2 ? hook ? `${title} \u2014 ${hook}` : title : hook };
|
|
3597
|
+
});
|
|
3598
|
+
const index = MEMORY_PROMPT + "\n\n" + formatCatalog(entries, {
|
|
3599
|
+
title: "## Memory index (persistent context \u2014 recalled across sessions)" + (header ? "\n" + header : ""),
|
|
3600
|
+
footer: "Call `Recall` with a slug to load its full fact.",
|
|
3601
|
+
budgetBytes: opts.budgetBytes
|
|
3602
|
+
});
|
|
3553
3603
|
return { index, tools };
|
|
3554
3604
|
}
|
|
3555
3605
|
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
|
|
@@ -3823,6 +3873,25 @@ ${sections.join("\n\n---\n\n")}`;
|
|
|
3823
3873
|
}
|
|
3824
3874
|
return "";
|
|
3825
3875
|
}
|
|
3876
|
+
async function loadContextFiles(fs, specs = []) {
|
|
3877
|
+
const sections = [];
|
|
3878
|
+
for (const { path, maxChars, label } of specs) {
|
|
3879
|
+
if (!await fs.exists(path)) continue;
|
|
3880
|
+
let md = (await fs.readFile(path)).trim();
|
|
3881
|
+
if (!md) continue;
|
|
3882
|
+
if (maxChars !== void 0) {
|
|
3883
|
+
const cps = [...md], n = Math.max(0, maxChars);
|
|
3884
|
+
if (cps.length > n) md = `${cps.slice(0, n).join("").trimEnd()}
|
|
3885
|
+
\u2026 (truncated \u2014 full file: ${path})`;
|
|
3886
|
+
}
|
|
3887
|
+
sections.push(`<!-- ${path} -->
|
|
3888
|
+
${label ? `### ${label}
|
|
3889
|
+
` : ""}${md}`);
|
|
3890
|
+
}
|
|
3891
|
+
return sections.length ? `## Context files
|
|
3892
|
+
|
|
3893
|
+
${sections.join("\n\n---\n\n")}` : "";
|
|
3894
|
+
}
|
|
3826
3895
|
|
|
3827
3896
|
// src/subagent.ts
|
|
3828
3897
|
init_tools();
|
|
@@ -4862,6 +4931,8 @@ var AgentOptions = class {
|
|
|
4862
4931
|
* Walks the VFS tree and merges all found files (general → specific, like Claude Code).
|
|
4863
4932
|
* `true` (default) = auto-discover standard names. `string[]` = custom names. `false` = skip. */
|
|
4864
4933
|
instructionFiles = true;
|
|
4934
|
+
/** Extra files injected right after the instructions (missing → skipped; over `maxChars` → head + pointer to the full file). */
|
|
4935
|
+
contextFiles;
|
|
4865
4936
|
/** Host interaction channel (human-in-the-loop). If set: adds the `AskUserQuestion` tool. */
|
|
4866
4937
|
host;
|
|
4867
4938
|
/** Add the `AskUserQuestion` tool when a host is present (default true). Set false for an agent that
|
|
@@ -5190,7 +5261,7 @@ var Agent = class _Agent {
|
|
|
5190
5261
|
* across turns (and the frozen prompt pairs well with prompt caching). Does NOT touch
|
|
5191
5262
|
* the transcript; `run`/`send` own that.
|
|
5192
5263
|
*/
|
|
5193
|
-
async prepare(
|
|
5264
|
+
async prepare() {
|
|
5194
5265
|
if (this.prepared) return this.systemPromptCache;
|
|
5195
5266
|
const o = this.options;
|
|
5196
5267
|
const fs = o.fs;
|
|
@@ -5208,8 +5279,12 @@ var Agent = class _Agent {
|
|
|
5208
5279
|
const ins = await loadInstructions(fs, names);
|
|
5209
5280
|
if (ins) systemPrompt += "\n\n" + ins;
|
|
5210
5281
|
}
|
|
5282
|
+
if (o.contextFiles?.length) {
|
|
5283
|
+
const ctxFiles = await loadContextFiles(fs, o.contextFiles);
|
|
5284
|
+
if (ctxFiles) systemPrompt += "\n\n" + ctxFiles;
|
|
5285
|
+
}
|
|
5211
5286
|
if (o.memoryDir) {
|
|
5212
|
-
const { index, tools: memTools } = await loadMemory(fs, o.memoryDir, {
|
|
5287
|
+
const { index, tools: memTools } = await loadMemory(fs, o.memoryDir, { userDir: o.memoryUserDir });
|
|
5213
5288
|
if (index) systemPrompt += "\n\n" + index;
|
|
5214
5289
|
tools = [...tools, ...memTools];
|
|
5215
5290
|
}
|
|
@@ -5219,7 +5294,7 @@ var Agent = class _Agent {
|
|
|
5219
5294
|
const c = (await scanCommands(fs, commandsDir)).find((x) => x.name === name);
|
|
5220
5295
|
return c ? { path: c.path, stripFrontmatter: true } : void 0;
|
|
5221
5296
|
} : void 0;
|
|
5222
|
-
const { catalog, tool } = await loadSkills(fs, skillsDir, {
|
|
5297
|
+
const { catalog, tool } = await loadSkills(fs, skillsDir, { sibling });
|
|
5223
5298
|
if (catalog) systemPrompt += "\n\n" + catalog;
|
|
5224
5299
|
if (tool) tools = [...tools, tool];
|
|
5225
5300
|
}
|
|
@@ -5228,7 +5303,7 @@ var Agent = class _Agent {
|
|
|
5228
5303
|
const s = (await scanSkills(fs, skillsDir)).find((x) => x.name === name);
|
|
5229
5304
|
return s ? { path: s.path, stripFrontmatter: false } : void 0;
|
|
5230
5305
|
} : void 0;
|
|
5231
|
-
const { catalog, tool } = await loadCommands(fs, commandsDir, {
|
|
5306
|
+
const { catalog, tool } = await loadCommands(fs, commandsDir, { sibling });
|
|
5232
5307
|
if (catalog) systemPrompt += "\n\n" + catalog;
|
|
5233
5308
|
if (tool) tools = [...tools, tool];
|
|
5234
5309
|
}
|
|
@@ -5287,7 +5362,7 @@ var Agent = class _Agent {
|
|
|
5287
5362
|
this.invokedTools.clear();
|
|
5288
5363
|
this.fabricatedClaims.clear();
|
|
5289
5364
|
this.lastTask = contentText(task);
|
|
5290
|
-
const systemPrompt = await this.prepare(
|
|
5365
|
+
const systemPrompt = await this.prepare();
|
|
5291
5366
|
const startCtx = await this.fireSessionStart();
|
|
5292
5367
|
const userContent = await this.applyPromptSubmit(task);
|
|
5293
5368
|
this.transcript = [
|
|
@@ -5323,7 +5398,7 @@ var Agent = class _Agent {
|
|
|
5323
5398
|
async send(task) {
|
|
5324
5399
|
await this.ensureFs();
|
|
5325
5400
|
this.lastTask = contentText(task);
|
|
5326
|
-
const systemPrompt = await this.prepare(
|
|
5401
|
+
const systemPrompt = await this.prepare();
|
|
5327
5402
|
const startCtx = await this.fireSessionStart();
|
|
5328
5403
|
const userContent = await this.applyPromptSubmit(task);
|
|
5329
5404
|
const sys = systemPrompt + (startCtx ? "\n\n" + startCtx : "");
|
|
@@ -6147,9 +6222,9 @@ function capOversizedBodies(messages, target, keepRecentImages = 1) {
|
|
|
6147
6222
|
return out;
|
|
6148
6223
|
}
|
|
6149
6224
|
function elideBody(x, cap) {
|
|
6150
|
-
const note = (
|
|
6225
|
+
const note = (bytes2, imgs2) => `
|
|
6151
6226
|
|
|
6152
|
-
[${
|
|
6227
|
+
[${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` : ""}]`;
|
|
6153
6228
|
if (!Array.isArray(x.content)) {
|
|
6154
6229
|
const text = contentText(x.content);
|
|
6155
6230
|
if (text.length <= cap) return x;
|
|
@@ -12085,11 +12160,11 @@ Another agent just implemented the above. Independently check the CURRENT state
|
|
|
12085
12160
|
const list = id ? [this.tasks.get(String(id))].filter(Boolean) : [...this.tasks.values()];
|
|
12086
12161
|
if (!list.length) return id ? `No task '${id}'.` : "No background tasks.";
|
|
12087
12162
|
const cap = id ? 1500 : 300;
|
|
12088
|
-
const
|
|
12163
|
+
const clip2 = (x) => x.length > cap ? `${x.slice(0, cap)}\u2026 (truncated)` : x;
|
|
12089
12164
|
return list.map((t) => {
|
|
12090
12165
|
const head = `${t.id} (${t.label}): ${t.status}`;
|
|
12091
12166
|
const body = (t.status === "done" ? t.spokenText?.trim() || t.result?.trim() : t.status === "error" ? t.result?.trim() : "") ?? "";
|
|
12092
|
-
return body ? `${head}. ${t.status === "error" ? "Error" : "Result"}: ${
|
|
12167
|
+
return body ? `${head}. ${t.status === "error" ? "Error" : "Result"}: ${clip2(body)}` : head;
|
|
12093
12168
|
}).join("\n");
|
|
12094
12169
|
}
|
|
12095
12170
|
};
|
|
@@ -13990,6 +14065,7 @@ export {
|
|
|
13990
14065
|
CartesiaTTSOptions,
|
|
13991
14066
|
CommandExecutor2 as CommandExecutor,
|
|
13992
14067
|
ConsoleHostBridge,
|
|
14068
|
+
DEFAULT_CATALOG_BUDGET,
|
|
13993
14069
|
DEFAULT_DENY,
|
|
13994
14070
|
DEFAULT_MAX_DEPTH,
|
|
13995
14071
|
DEFAULT_MUTATING,
|
|
@@ -14080,6 +14156,7 @@ export {
|
|
|
14080
14156
|
findGitRoot,
|
|
14081
14157
|
forComponent,
|
|
14082
14158
|
forgetFact,
|
|
14159
|
+
formatCatalog,
|
|
14083
14160
|
formatJobNotice,
|
|
14084
14161
|
fullAgentOptions,
|
|
14085
14162
|
getOrCreateWorktree,
|
|
@@ -14098,6 +14175,7 @@ export {
|
|
|
14098
14175
|
lessonCapture,
|
|
14099
14176
|
loadAgents,
|
|
14100
14177
|
loadCommands,
|
|
14178
|
+
loadContextFiles,
|
|
14101
14179
|
loadInstructions,
|
|
14102
14180
|
loadMemory,
|
|
14103
14181
|
loadSkills,
|