agent.libx.js 0.93.27 → 0.93.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.
- package/cli/cli.ts +14 -4
- package/dist/cli.js +26 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +42 -38
- package/dist/index.js +100 -100
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -375,61 +375,65 @@ declare const webFetchTool: AgentTool;
|
|
|
375
375
|
declare const webSearchTool: AgentTool;
|
|
376
376
|
|
|
377
377
|
/**
|
|
378
|
-
*
|
|
378
|
+
* Scratch — keep large tool/subagent outputs OUT of the main context, but queryable AS FILES.
|
|
379
379
|
*
|
|
380
380
|
* Pattern: many-tool / many-subagent engines bloat context with raw outputs (search results, big
|
|
381
381
|
* file reads, subagent reports) that are mostly noise once a gist is taken. Here, a tool wrapped with
|
|
382
|
-
* `
|
|
383
|
-
* (
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
382
|
+
* `Scratch.capture` writes any oversized result to an ephemeral scratch filesystem (a real VFS) and
|
|
383
|
+
* returns a compact stub (path + preview) to context. To recover a buried detail later you do NOT
|
|
384
|
+
* re-bloat context — you peek with the FILE tools already on hand:
|
|
385
|
+
* • Grep/Glob/Read over the scratch dir — returns matching lines, not the blob (a free, light peek).
|
|
386
|
+
* • Ask({question, over?}) — a CHEAP child agent over the scratch FS that greps/reads and returns
|
|
387
|
+
* just the answer; its reasoning tokens never touch the caller's context.
|
|
388
|
+
* Two-tier division of labour: cheap RETRIEVE/EXTRACT, strong caller SYNTHESIZE.
|
|
387
389
|
*
|
|
388
|
-
*
|
|
390
|
+
* The scratch FS is injected (IoC): pass a MemFilesystem for ephemeral, or a disk-backed one under the
|
|
391
|
+
* session dir to persist across resume (CC's lesson: the filesystem is the durable store). Segregated
|
|
392
|
+
* module — zero Agent-core changes.
|
|
389
393
|
*/
|
|
390
394
|
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
brief: string;
|
|
395
|
-
content: string;
|
|
396
|
-
bytes: number;
|
|
397
|
-
}
|
|
398
|
-
/** In-memory store of captured tool outputs. Ids are a monotonic `a1, a2, …` (test-friendly, no RNG). */
|
|
399
|
-
declare class ArtifactStore {
|
|
400
|
-
private items;
|
|
401
|
-
private seq;
|
|
402
|
-
put(tool: string, content: string, brief?: string): Artifact;
|
|
403
|
-
get(id: string): Artifact | undefined;
|
|
404
|
-
list(): Artifact[];
|
|
405
|
-
get size(): number;
|
|
406
|
-
/** Cheap keyword retrieval over brief+content — rank by how many query terms each artifact contains. */
|
|
407
|
-
search(query: string, k?: number): Artifact[];
|
|
408
|
-
}
|
|
409
|
-
interface CaptureOptions {
|
|
395
|
+
/** Default scratch dir (a path in the injected scratch FS). */
|
|
396
|
+
declare const SCRATCH_DIR = "/scratch";
|
|
397
|
+
interface ScratchOptions {
|
|
410
398
|
/** Min result length (chars) to capture; smaller results pass through untouched. Default 1500. */
|
|
411
399
|
threshold?: number;
|
|
400
|
+
/** Dir within the scratch FS to write captures. Default SCRATCH_DIR. */
|
|
401
|
+
dir?: string;
|
|
412
402
|
/** Chars of the captured output to preview in the stub. Default 320. */
|
|
413
403
|
previewChars?: number;
|
|
414
404
|
}
|
|
415
405
|
/**
|
|
416
|
-
*
|
|
417
|
-
*
|
|
406
|
+
* Owns an (injected) scratch filesystem + a capture counter. `capture(tool)` wraps a tool so its
|
|
407
|
+
* oversized string results spill to a scratch file and are replaced in context with a stub.
|
|
418
408
|
*/
|
|
419
|
-
declare
|
|
409
|
+
declare class Scratch {
|
|
410
|
+
fs: IFilesystem;
|
|
411
|
+
options: Required<ScratchOptions>;
|
|
412
|
+
private seq;
|
|
413
|
+
private dirReady?;
|
|
414
|
+
constructor(fs: IFilesystem, options?: ScratchOptions);
|
|
415
|
+
/** Number of captures so far. */
|
|
416
|
+
get count(): number;
|
|
417
|
+
/** Wrap a tool: oversized STRING results spill to a scratch file; everything else passes through. */
|
|
418
|
+
capture(tool: AgentTool): AgentTool;
|
|
419
|
+
/** Wrap many tools at once. */
|
|
420
|
+
captureAll(tools: AgentTool[]): AgentTool[];
|
|
421
|
+
}
|
|
420
422
|
interface AskOptions {
|
|
421
|
-
|
|
423
|
+
/** The scratch filesystem to peek into (dedicated VFS holding only scratch files). */
|
|
424
|
+
fs: IFilesystem;
|
|
422
425
|
ai: ChatLike;
|
|
423
|
-
/** Model for the
|
|
426
|
+
/** Model for the peek — intentionally a CHEAP model; the caller synthesizes. */
|
|
424
427
|
model: string;
|
|
425
|
-
/**
|
|
426
|
-
|
|
428
|
+
/** Step budget for the peek child (default 6). */
|
|
429
|
+
maxSteps?: number;
|
|
430
|
+
/** Scratch dir to search when `over` is omitted. Default SCRATCH_DIR. */
|
|
431
|
+
dir?: string;
|
|
427
432
|
}
|
|
428
433
|
/**
|
|
429
|
-
* `Ask` — peek into
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* retrieve+extract here; the (stronger) caller synthesizes the final answer from what comes back.
|
|
434
|
+
* `Ask` — peek into the scratch FS to answer a question WITHOUT loading raw data into the caller's
|
|
435
|
+
* context. Spawns a cheap child agent (Read/Grep/Glob over scratch) that greps/reads and returns just
|
|
436
|
+
* the answer. Pass `over` (a path) for a targeted peek, or omit to grep-discover.
|
|
433
437
|
*/
|
|
434
438
|
declare function makeAskTool(o: AskOptions): AgentTool;
|
|
435
439
|
|
|
@@ -1134,4 +1138,4 @@ declare class CartesiaTTS {
|
|
|
1134
1138
|
close(): void;
|
|
1135
1139
|
}
|
|
1136
1140
|
|
|
1137
|
-
export { Agent, type AgentDef, AgentOptions, AgentTool, type
|
|
1141
|
+
export { Agent, type AgentDef, AgentOptions, AgentTool, type AskOptions, type Attempt, type AudioSink, type AudioSource, type AuthProvider, BodDbFilesystem, CartesiaTTS, CartesiaTTSOptions, ChatLike, ChatOptions, ChatResponse, type CommandInfo, ConsoleHostBridge, DEFAULT_DENY, DuplexAgent, DuplexAgentOptions, type DuplexTaskStatus, FakeAIClient, Hooks, HostBridge, JailOptions, JailedFilesystem, type LessonOptions, LessonOptionsDefaults, type LoadMemoryOpts, MEMORY_PROMPT, MessageContent, type Mount, MountFilesystem, NodeDiskFilesystem, OverlayFilesystem, type ReflectOptions, RunResult, SCRATCH_DIR, STT_SAMPLE_RATE, Scratch, type ScratchOptions, ScriptedHostBridge, type SkillInfo, SonioxSTT, SonioxSTTOptions, type SttLike, TTS_SAMPLE_RATE, type TaskRecord, type TaskToolOptions, ToolCall, type ToolSpec, type TtsLike, UserQuestion, VOICE_MEMORY_PROMPT, VOICE_SYSTEM_PROMPT, VoiceEngine, VoiceEngineOptions, type VoiceState, type WebFetchOptions, type WebSearchOptions, type WorkerTier, applyEditsTool, askUserQuestionTool, checkpointTool, checkpointTools, compileSynthesizedTool, decodeDdgUrl, diskAgentOptions, expandCommand, expandTemplate, forComponent, fullAgentOptions, globTool, grepTool, htmlToText, idfWeights, lessonCapture, loadAgents, loadCommands, loadInstructions, loadMemory, loadSkills, makeAskTool, makeTaskBatchTool, makeTaskTool, makeWebFetchTool, makeWebSearchTool, mkdirp, multiEditTool, parseDdgHtml, raceAttempts, reflectOnRun, relevanceScore, repoIndex, repoMapTool, resolveAuth, rollbackTool, sandboxAgentOptions, slugify, tokenize, toolCall, topByRelevance, validateToolCode, webFetchTool, webSearchTool, writeFact, writeTool };
|
package/dist/index.js
CHANGED
|
@@ -1998,9 +1998,9 @@ async function loadCommands(fs, dir, opts = {}) {
|
|
|
1998
1998
|
properties: { name: { type: "string" }, args: { type: "string", description: "arguments to fill the template" } }
|
|
1999
1999
|
},
|
|
2000
2000
|
async run({ name, args }, ctx) {
|
|
2001
|
-
const
|
|
2002
|
-
const c = commands.find((x) => x.name ===
|
|
2003
|
-
if (!c) return `Error: no command named '${
|
|
2001
|
+
const slug2 = String(name ?? "").replace(/^\//, "");
|
|
2002
|
+
const c = commands.find((x) => x.name === slug2);
|
|
2003
|
+
if (!c) return `Error: no command named '${slug2}'. Available: ${commands.map((x) => x.name).join(", ")}`;
|
|
2004
2004
|
return expandCommand(ctx.fs, c, String(args ?? ""));
|
|
2005
2005
|
}
|
|
2006
2006
|
};
|
|
@@ -2035,9 +2035,9 @@ async function loadMemory(fs, dir, opts = {}) {
|
|
|
2035
2035
|
const lines = md.split("\n");
|
|
2036
2036
|
if (!header) header = lines.filter((l) => !/^\s*-\s*\[.+\]\(.+\.md\)/.test(l)).join("\n").trim();
|
|
2037
2037
|
for (const l of lines.filter((l2) => /^\s*-\s*\[.+\]\(.+\.md\)/.test(l2))) {
|
|
2038
|
-
const
|
|
2039
|
-
if (
|
|
2040
|
-
seenSlugs.add(
|
|
2038
|
+
const slug2 = l.match(/\]\(([^)]+)\.md\)/)?.[1];
|
|
2039
|
+
if (slug2 && !seenSlugs.has(slug2)) {
|
|
2040
|
+
seenSlugs.add(slug2);
|
|
2041
2041
|
allPointers.push(l);
|
|
2042
2042
|
}
|
|
2043
2043
|
}
|
|
@@ -2053,20 +2053,20 @@ function slugify(s, fallback = "note") {
|
|
|
2053
2053
|
const base = String(s ?? "").trim().toLowerCase().replace(/\.md$/i, "").replace(/[^\w\s-]/g, "").replace(/[\s_]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 48);
|
|
2054
2054
|
return base || fallback;
|
|
2055
2055
|
}
|
|
2056
|
-
async function writeFact(fs, dir,
|
|
2056
|
+
async function writeFact(fs, dir, slug2, body, opts) {
|
|
2057
2057
|
await mkdirp(fs, dir);
|
|
2058
2058
|
const content = opts?.type ? `---
|
|
2059
2059
|
type: ${opts.type}
|
|
2060
2060
|
---
|
|
2061
2061
|
|
|
2062
2062
|
${body}` : body;
|
|
2063
|
-
await fs.writeFile(`${dir}/${
|
|
2063
|
+
await fs.writeFile(`${dir}/${slug2}.md`, content.endsWith("\n") ? content : content + "\n");
|
|
2064
2064
|
const indexPath = `${dir}/MEMORY.md`;
|
|
2065
2065
|
const idx = await fs.exists(indexPath) ? await fs.readFile(indexPath) : "# Memory Index\n";
|
|
2066
2066
|
const summary = opts?.description || body.split("\n")[0].slice(0, 80);
|
|
2067
|
-
const line = `- [${
|
|
2067
|
+
const line = `- [${slug2}](${slug2}.md) \u2014 ${summary}`;
|
|
2068
2068
|
const lines = idx.split("\n");
|
|
2069
|
-
const at = lines.findIndex((l) => l.includes(`(${
|
|
2069
|
+
const at = lines.findIndex((l) => l.includes(`(${slug2}.md)`));
|
|
2070
2070
|
if (at >= 0) {
|
|
2071
2071
|
if (lines[at] !== line) {
|
|
2072
2072
|
lines[at] = line;
|
|
@@ -2117,8 +2117,8 @@ async function listSlugs(fs, dir, prefix = "") {
|
|
|
2117
2117
|
}
|
|
2118
2118
|
return out;
|
|
2119
2119
|
}
|
|
2120
|
-
async function loadFact(fs, dir,
|
|
2121
|
-
const path = `${dir}/${
|
|
2120
|
+
async function loadFact(fs, dir, slug2) {
|
|
2121
|
+
const path = `${dir}/${slug2}.md`;
|
|
2122
2122
|
try {
|
|
2123
2123
|
const raw = await fs.readFile(path);
|
|
2124
2124
|
const { body } = splitFrontmatter(raw);
|
|
@@ -2127,9 +2127,9 @@ async function loadFact(fs, dir, slug) {
|
|
|
2127
2127
|
return null;
|
|
2128
2128
|
}
|
|
2129
2129
|
}
|
|
2130
|
-
async function loadFactMulti(fs, dirs,
|
|
2130
|
+
async function loadFactMulti(fs, dirs, slug2) {
|
|
2131
2131
|
for (const d of dirs) {
|
|
2132
|
-
const r = await loadFact(fs, d,
|
|
2132
|
+
const r = await loadFact(fs, d, slug2);
|
|
2133
2133
|
if (r != null) return r;
|
|
2134
2134
|
}
|
|
2135
2135
|
return null;
|
|
@@ -2155,9 +2155,9 @@ function recallTool(fs, dirs) {
|
|
|
2155
2155
|
pattern: { type: "string", description: 'glob pattern to match against slugs (e.g. "auth*", "*database*")' }
|
|
2156
2156
|
}
|
|
2157
2157
|
},
|
|
2158
|
-
async run({ slug, slugs, pattern }, ctx) {
|
|
2158
|
+
async run({ slug: slug2, slugs, pattern }, ctx) {
|
|
2159
2159
|
let targets = [];
|
|
2160
|
-
if (
|
|
2160
|
+
if (slug2) targets = [cleanSlug(slug2)];
|
|
2161
2161
|
else if (Array.isArray(slugs)) targets = slugs.map(cleanSlug).filter(Boolean);
|
|
2162
2162
|
else if (pattern) {
|
|
2163
2163
|
const escaped = String(pattern).replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
@@ -2208,19 +2208,19 @@ function memorySearchTool(fs, dirs) {
|
|
|
2208
2208
|
matcher = (l) => l.toLowerCase().includes(lower);
|
|
2209
2209
|
}
|
|
2210
2210
|
const loaded = [];
|
|
2211
|
-
for (const
|
|
2212
|
-
const body = await loadFactMulti(fs, dirs,
|
|
2213
|
-
if (body) loaded.push({ slug, body });
|
|
2211
|
+
for (const slug2 of slugs) {
|
|
2212
|
+
const body = await loadFactMulti(fs, dirs, slug2);
|
|
2213
|
+
if (body) loaded.push({ slug: slug2, body });
|
|
2214
2214
|
}
|
|
2215
2215
|
const idf = idfWeights(loaded.map((l) => l.body));
|
|
2216
2216
|
const qTokens = tokenize(q);
|
|
2217
2217
|
const hits = [];
|
|
2218
|
-
for (const { slug, body } of loaded) {
|
|
2218
|
+
for (const { slug: slug2, body } of loaded) {
|
|
2219
2219
|
const lines = body.split("\n");
|
|
2220
2220
|
const matchLine = lines.find(matcher);
|
|
2221
2221
|
if (!matchLine && !relevanceScore(body, qTokens, idf)) continue;
|
|
2222
2222
|
const snippet = matchLine?.trim().slice(0, 120) || lines.find((l) => l.trim())?.trim().slice(0, 120) || "";
|
|
2223
|
-
hits.push({ slug, snippet, score: relevanceScore(body, qTokens, idf) });
|
|
2223
|
+
hits.push({ slug: slug2, snippet, score: relevanceScore(body, qTokens, idf) });
|
|
2224
2224
|
}
|
|
2225
2225
|
if (!hits.length) return "(no matches)";
|
|
2226
2226
|
hits.sort((a, b) => b.score - a.score);
|
|
@@ -2244,11 +2244,11 @@ function rememberTool(fs, dir, memOpts = {}) {
|
|
|
2244
2244
|
description: { type: "string", description: "one-line summary for the memory index (\u226480 chars)" }
|
|
2245
2245
|
}
|
|
2246
2246
|
},
|
|
2247
|
-
async run({ fact, slug, type, description }, ctx) {
|
|
2247
|
+
async run({ fact, slug: slug2, type, description }, ctx) {
|
|
2248
2248
|
const body = String(fact ?? "").trim();
|
|
2249
2249
|
if (!body) return `Error: nothing to remember (empty fact).`;
|
|
2250
2250
|
if (++writes > maxWrites) return `Rate limit: too many memories this session (${maxWrites}). Only persist genuinely durable facts.`;
|
|
2251
|
-
const name = slugify(
|
|
2251
|
+
const name = slugify(slug2 || body.split("\n")[0]);
|
|
2252
2252
|
const isGlobal = (type === "user" || type === "feedback") && memOpts.userDir;
|
|
2253
2253
|
const targetDir = isGlobal ? memOpts.userDir : dir;
|
|
2254
2254
|
await writeFact(fs, targetDir, name, body, { type, description });
|
|
@@ -3568,100 +3568,100 @@ init_tools_structured();
|
|
|
3568
3568
|
init_todo();
|
|
3569
3569
|
init_tools_web();
|
|
3570
3570
|
|
|
3571
|
-
// src/
|
|
3571
|
+
// src/scratch.ts
|
|
3572
|
+
init_tools();
|
|
3573
|
+
init_tools_structured();
|
|
3572
3574
|
init_logging();
|
|
3573
|
-
var log5 = forComponent("
|
|
3574
|
-
var
|
|
3575
|
-
items = /* @__PURE__ */ new Map();
|
|
3576
|
-
seq = 0;
|
|
3577
|
-
put(tool, content, brief) {
|
|
3578
|
-
const id = "a" + ++this.seq;
|
|
3579
|
-
const art = { id, tool, brief: brief || content.replace(/\s+/g, " ").trim().slice(0, 80), content, bytes: content.length };
|
|
3580
|
-
this.items.set(id, art);
|
|
3581
|
-
return art;
|
|
3582
|
-
}
|
|
3583
|
-
get(id) {
|
|
3584
|
-
return this.items.get(id);
|
|
3585
|
-
}
|
|
3586
|
-
list() {
|
|
3587
|
-
return [...this.items.values()];
|
|
3588
|
-
}
|
|
3589
|
-
get size() {
|
|
3590
|
-
return this.items.size;
|
|
3591
|
-
}
|
|
3592
|
-
/** Cheap keyword retrieval over brief+content — rank by how many query terms each artifact contains. */
|
|
3593
|
-
search(query, k = 3) {
|
|
3594
|
-
const terms = [...new Set(query.toLowerCase().split(/\W+/).filter((t) => t.length > 2))];
|
|
3595
|
-
if (!terms.length) return this.list().slice(-k);
|
|
3596
|
-
return this.list().map((a) => {
|
|
3597
|
-
const hay = (a.brief + " " + a.content).toLowerCase();
|
|
3598
|
-
return { a, score: terms.reduce((s, t) => s + (hay.includes(t) ? 1 : 0), 0) };
|
|
3599
|
-
}).filter((x) => x.score > 0).sort((x, y) => y.score - x.score).slice(0, k).map((x) => x.a);
|
|
3600
|
-
}
|
|
3601
|
-
};
|
|
3575
|
+
var log5 = forComponent("scratch");
|
|
3576
|
+
var SCRATCH_DIR = "/scratch";
|
|
3602
3577
|
function shortArgs(args) {
|
|
3603
3578
|
try {
|
|
3604
|
-
const s = JSON.stringify(args);
|
|
3605
|
-
return s.length >
|
|
3579
|
+
const s = JSON.stringify(args ?? {});
|
|
3580
|
+
return s.length > 50 ? s.slice(0, 47) + "\u2026" : s;
|
|
3606
3581
|
} catch {
|
|
3607
3582
|
return "";
|
|
3608
3583
|
}
|
|
3609
3584
|
}
|
|
3610
|
-
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
...
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3585
|
+
var slug = (s) => s.replace(/[^a-z0-9]+/gi, "-").replace(/^-+|-+$/g, "").toLowerCase().slice(0, 32) || "out";
|
|
3586
|
+
var Scratch = class {
|
|
3587
|
+
constructor(fs, options = {}) {
|
|
3588
|
+
this.fs = fs;
|
|
3589
|
+
this.options = { threshold: 1500, dir: SCRATCH_DIR, previewChars: 320, ...options };
|
|
3590
|
+
}
|
|
3591
|
+
fs;
|
|
3592
|
+
options;
|
|
3593
|
+
seq = 0;
|
|
3594
|
+
dirReady;
|
|
3595
|
+
/** Number of captures so far. */
|
|
3596
|
+
get count() {
|
|
3597
|
+
return this.seq;
|
|
3598
|
+
}
|
|
3599
|
+
/** Wrap a tool: oversized STRING results spill to a scratch file; everything else passes through. */
|
|
3600
|
+
capture(tool) {
|
|
3601
|
+
const { threshold, dir, previewChars } = this.options;
|
|
3602
|
+
return {
|
|
3603
|
+
...tool,
|
|
3604
|
+
run: async (args, ctx) => {
|
|
3605
|
+
const raw = await tool.run(args, ctx);
|
|
3606
|
+
if (typeof raw !== "string" || raw.length <= threshold) return raw;
|
|
3607
|
+
const id = "a" + ++this.seq;
|
|
3608
|
+
const path = `${dir}/${id}-${slug(tool.name)}.txt`;
|
|
3609
|
+
const header = `# ${tool.name}(${shortArgs(args)}) \u2014 ${raw.length} bytes
|
|
3610
|
+
`;
|
|
3611
|
+
try {
|
|
3612
|
+
await (this.dirReady ??= mkdirp(this.fs, dir));
|
|
3613
|
+
await this.fs.writeFile(path, header + raw);
|
|
3614
|
+
} catch (e) {
|
|
3615
|
+
log5.debug("scratch write failed; returning raw", e);
|
|
3616
|
+
return raw;
|
|
3617
|
+
}
|
|
3618
|
+
const preview = raw.slice(0, previewChars).replace(/\s+/g, " ").trim();
|
|
3619
|
+
return `[scratch ${path} \xB7 ${tool.name} \xB7 ${raw.length} bytes \u2014 full output saved out of context to keep it clean]
|
|
3621
3620
|
preview: ${preview}\u2026
|
|
3622
|
-
To pull a specific detail
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
}
|
|
3626
|
-
|
|
3621
|
+
To pull a specific detail, Grep/Read ${path}, or call Ask({ question: "\u2026", over: "${path}" }). Do NOT guess at what the preview cuts off.`;
|
|
3622
|
+
}
|
|
3623
|
+
};
|
|
3624
|
+
}
|
|
3625
|
+
/** Wrap many tools at once. */
|
|
3626
|
+
captureAll(tools) {
|
|
3627
|
+
return tools.map((t) => this.capture(t));
|
|
3628
|
+
}
|
|
3629
|
+
};
|
|
3630
|
+
var ASK_PROMPT = "You are a retrieval-extraction step with Read, Grep and Glob over a scratch filesystem holding raw outputs from earlier tools. Find the information that answers the question and return it concisely, quoting values/facts verbatim. Do NOT add analysis or anything not grounded in the files. If the answer is not present, say so plainly.";
|
|
3627
3631
|
function makeAskTool(o) {
|
|
3628
|
-
const
|
|
3632
|
+
const dir = o.dir ?? SCRATCH_DIR;
|
|
3629
3633
|
return {
|
|
3630
3634
|
name: "Ask",
|
|
3631
|
-
description:
|
|
3635
|
+
description: "Answer a question by peeking into the scratch files \u2014 large earlier outputs (web search, big reads, subagent reports) kept out of your context. Pass `over` with a scratch path for a targeted lookup, or omit it to search the scratch dir. Returns only the extracted answer; the full data never enters your context.",
|
|
3632
3636
|
parameters: {
|
|
3633
3637
|
type: "object",
|
|
3634
3638
|
required: ["question"],
|
|
3635
3639
|
properties: {
|
|
3636
|
-
question: { type: "string", description: "what you need from the
|
|
3637
|
-
over: { type: "string", description: '
|
|
3640
|
+
question: { type: "string", description: "what you need from the scratch files" },
|
|
3641
|
+
over: { type: "string", description: 'scratch path to read, e.g. "/scratch/a3-websearch.txt"; omit to search' }
|
|
3638
3642
|
}
|
|
3639
3643
|
},
|
|
3640
3644
|
async run({ question, over }) {
|
|
3641
3645
|
const q = String(question ?? "").trim();
|
|
3642
3646
|
if (!q) return "Error: empty question";
|
|
3643
|
-
const
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
+
const child = new Agent({
|
|
3648
|
+
ai: o.ai,
|
|
3649
|
+
model: o.model,
|
|
3650
|
+
fs: o.fs,
|
|
3651
|
+
tools: toolsByName(["Read", "Grep", "Glob"]),
|
|
3652
|
+
maxSteps: o.maxSteps ?? 6,
|
|
3653
|
+
systemPrompt: ASK_PROMPT
|
|
3654
|
+
});
|
|
3655
|
+
const hint = over ? `Start by reading: ${over}.` : `Grep/Glob ${dir} to find the relevant file(s) first.`;
|
|
3647
3656
|
try {
|
|
3648
|
-
const res = await
|
|
3649
|
-
model: o.model,
|
|
3650
|
-
messages: [
|
|
3651
|
-
{ role: "system", content: ASK_SYSTEM },
|
|
3652
|
-
{ role: "user", content: `ARTIFACTS:
|
|
3653
|
-
${corpus}
|
|
3654
|
-
|
|
3655
|
-
QUESTION: ${q}` }
|
|
3656
|
-
]
|
|
3657
|
-
});
|
|
3658
|
-
const answer = (res?.content ?? "").trim();
|
|
3659
|
-
return answer ? `${answer}
|
|
3657
|
+
const res = await child.run(`${hint}
|
|
3660
3658
|
|
|
3661
|
-
|
|
3659
|
+
Question: ${q}`);
|
|
3660
|
+
const answer = (res.text ?? "").trim();
|
|
3661
|
+
return answer || "(no answer found in scratch)";
|
|
3662
3662
|
} catch (e) {
|
|
3663
|
-
log5.debug("Ask
|
|
3664
|
-
return `Error querying
|
|
3663
|
+
log5.debug("Ask peek failed", e);
|
|
3664
|
+
return `Error querying scratch: ${e?.message ?? e}`;
|
|
3665
3665
|
}
|
|
3666
3666
|
}
|
|
3667
3667
|
};
|
|
@@ -3724,15 +3724,15 @@ If the run was fine or the issue was purely task-specific (not generalizable), r
|
|
|
3724
3724
|
const m = text.match(/LESSON:\s*(.+)/i);
|
|
3725
3725
|
const lesson = m?.[1]?.trim().slice(0, 200) ?? "";
|
|
3726
3726
|
if (!lesson || /^none\b/i.test(lesson)) return null;
|
|
3727
|
-
const
|
|
3727
|
+
const slug2 = ("lesson-" + slugify(lesson)).slice(0, 56);
|
|
3728
3728
|
try {
|
|
3729
|
-
await writeFact(o.fs, o.dir,
|
|
3729
|
+
await writeFact(o.fs, o.dir, slug2, lesson);
|
|
3730
3730
|
} catch (e) {
|
|
3731
3731
|
log7.warn(`could not persist lesson: ${e?.message ?? e}`);
|
|
3732
3732
|
return null;
|
|
3733
3733
|
}
|
|
3734
|
-
log7.debug(`reflection persisted ${
|
|
3735
|
-
return
|
|
3734
|
+
log7.debug(`reflection persisted ${slug2}`);
|
|
3735
|
+
return slug2;
|
|
3736
3736
|
}
|
|
3737
3737
|
function digestRun(messages, maxChars) {
|
|
3738
3738
|
const lines = [];
|
|
@@ -5233,7 +5233,6 @@ import { MemFilesystem as MemFilesystem3, IndexedDbFilesystem, CommandExecutor a
|
|
|
5233
5233
|
export {
|
|
5234
5234
|
Agent,
|
|
5235
5235
|
AgentOptions,
|
|
5236
|
-
ArtifactStore,
|
|
5237
5236
|
BodDbFilesystem,
|
|
5238
5237
|
CartesiaTTS,
|
|
5239
5238
|
CartesiaTTSOptions,
|
|
@@ -5257,8 +5256,10 @@ export {
|
|
|
5257
5256
|
PermissionPolicy,
|
|
5258
5257
|
RecordingHooks,
|
|
5259
5258
|
RecordingLifecycle,
|
|
5259
|
+
SCRATCH_DIR,
|
|
5260
5260
|
STT_SAMPLE_RATE,
|
|
5261
5261
|
SandboxJobRegistry,
|
|
5262
|
+
Scratch,
|
|
5262
5263
|
ScriptedHostBridge,
|
|
5263
5264
|
SonioxSTT,
|
|
5264
5265
|
SonioxSTTOptions,
|
|
@@ -5335,7 +5336,6 @@ export {
|
|
|
5335
5336
|
validateToolCode,
|
|
5336
5337
|
webFetchTool,
|
|
5337
5338
|
webSearchTool,
|
|
5338
|
-
withArtifactCapture,
|
|
5339
5339
|
writeFact,
|
|
5340
5340
|
writeTool
|
|
5341
5341
|
};
|