exempclaw 0.4.0
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/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/agent/agent.d.ts +91 -0
- package/dist/agent/agent.js +258 -0
- package/dist/agent/agent.js.map +1 -0
- package/dist/agent/config.d.ts +49 -0
- package/dist/agent/config.js +58 -0
- package/dist/agent/config.js.map +1 -0
- package/dist/agent/persona.d.ts +39 -0
- package/dist/agent/persona.js +81 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agents/registry.d.ts +21 -0
- package/dist/agents/registry.js +51 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/cli/approve.d.ts +17 -0
- package/dist/cli/approve.js +50 -0
- package/dist/cli/approve.js.map +1 -0
- package/dist/cli/chat.d.ts +16 -0
- package/dist/cli/chat.js +148 -0
- package/dist/cli/chat.js.map +1 -0
- package/dist/cli/demo.d.ts +7 -0
- package/dist/cli/demo.js +82 -0
- package/dist/cli/demo.js.map +1 -0
- package/dist/cli/init.d.ts +17 -0
- package/dist/cli/init.js +89 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/live.d.ts +10 -0
- package/dist/cli/live.js +109 -0
- package/dist/cli/live.js.map +1 -0
- package/dist/cli/offline.d.ts +23 -0
- package/dist/cli/offline.js +236 -0
- package/dist/cli/offline.js.map +1 -0
- package/dist/cli/probe.d.ts +23 -0
- package/dist/cli/probe.js +140 -0
- package/dist/cli/probe.js.map +1 -0
- package/dist/cli/render.d.ts +15 -0
- package/dist/cli/render.js +50 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/tui.d.ts +101 -0
- package/dist/cli/tui.js +334 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/config/index.d.ts +33 -0
- package/dist/config/index.js +48 -0
- package/dist/config/index.js.map +1 -0
- package/dist/connectors/connector.d.ts +58 -0
- package/dist/connectors/connector.js +30 -0
- package/dist/connectors/connector.js.map +1 -0
- package/dist/connectors/email/email-connector.d.ts +43 -0
- package/dist/connectors/email/email-connector.js +364 -0
- package/dist/connectors/email/email-connector.js.map +1 -0
- package/dist/connectors/github/github-connector.d.ts +52 -0
- package/dist/connectors/github/github-connector.js +271 -0
- package/dist/connectors/github/github-connector.js.map +1 -0
- package/dist/connectors/http.d.ts +34 -0
- package/dist/connectors/http.js +78 -0
- package/dist/connectors/http.js.map +1 -0
- package/dist/connectors/index.d.ts +34 -0
- package/dist/connectors/index.js +86 -0
- package/dist/connectors/index.js.map +1 -0
- package/dist/connectors/notion/notion-connector.d.ts +45 -0
- package/dist/connectors/notion/notion-connector.js +222 -0
- package/dist/connectors/notion/notion-connector.js.map +1 -0
- package/dist/connectors/slack/slack-connector.d.ts +43 -0
- package/dist/connectors/slack/slack-connector.js +291 -0
- package/dist/connectors/slack/slack-connector.js.map +1 -0
- package/dist/core/errors.d.ts +36 -0
- package/dist/core/errors.js +40 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/logger.d.ts +14 -0
- package/dist/core/logger.js +44 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/run-log.d.ts +37 -0
- package/dist/core/run-log.js +37 -0
- package/dist/core/run-log.js.map +1 -0
- package/dist/core/usage.d.ts +22 -0
- package/dist/core/usage.js +58 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/dashboard/data.d.ts +62 -0
- package/dist/dashboard/data.js +84 -0
- package/dist/dashboard/data.js.map +1 -0
- package/dist/dashboard/page.d.ts +9 -0
- package/dist/dashboard/page.js +421 -0
- package/dist/dashboard/page.js.map +1 -0
- package/dist/dashboard/server.d.ts +19 -0
- package/dist/dashboard/server.js +44 -0
- package/dist/dashboard/server.js.map +1 -0
- package/dist/demo/bootstrap.d.ts +25 -0
- package/dist/demo/bootstrap.js +60 -0
- package/dist/demo/bootstrap.js.map +1 -0
- package/dist/demo/claude.d.ts +31 -0
- package/dist/demo/claude.js +230 -0
- package/dist/demo/claude.js.map +1 -0
- package/dist/demo/demo-connector.d.ts +19 -0
- package/dist/demo/demo-connector.js +168 -0
- package/dist/demo/demo-connector.js.map +1 -0
- package/dist/demo/world.d.ts +60 -0
- package/dist/demo/world.js +117 -0
- package/dist/demo/world.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +396 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/ingest.d.ts +63 -0
- package/dist/ingest/ingest.js +258 -0
- package/dist/ingest/ingest.js.map +1 -0
- package/dist/llm/claude.d.ts +97 -0
- package/dist/llm/claude.js +163 -0
- package/dist/llm/claude.js.map +1 -0
- package/dist/memory/compaction.d.ts +22 -0
- package/dist/memory/compaction.js +79 -0
- package/dist/memory/compaction.js.map +1 -0
- package/dist/memory/file-store.d.ts +28 -0
- package/dist/memory/file-store.js +110 -0
- package/dist/memory/file-store.js.map +1 -0
- package/dist/memory/store.d.ts +32 -0
- package/dist/memory/store.js +2 -0
- package/dist/memory/store.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +63 -0
- package/dist/orchestrator/orchestrator.js +181 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/scheduler.d.ts +33 -0
- package/dist/orchestrator/scheduler.js +67 -0
- package/dist/orchestrator/scheduler.js.map +1 -0
- package/dist/orchestrator/seen-events.d.ts +21 -0
- package/dist/orchestrator/seen-events.js +71 -0
- package/dist/orchestrator/seen-events.js.map +1 -0
- package/dist/plugins/apply.d.ts +9 -0
- package/dist/plugins/apply.js +17 -0
- package/dist/plugins/apply.js.map +1 -0
- package/dist/plugins/define.d.ts +29 -0
- package/dist/plugins/define.js +30 -0
- package/dist/plugins/define.js.map +1 -0
- package/dist/plugins/loader.d.ts +31 -0
- package/dist/plugins/loader.js +61 -0
- package/dist/plugins/loader.js.map +1 -0
- package/dist/plugins/scaffold.d.ts +5 -0
- package/dist/plugins/scaffold.js +72 -0
- package/dist/plugins/scaffold.js.map +1 -0
- package/dist/tools/builtin.d.ts +8 -0
- package/dist/tools/builtin.js +63 -0
- package/dist/tools/builtin.js.map +1 -0
- package/dist/tools/tool.d.ts +84 -0
- package/dist/tools/tool.js +70 -0
- package/dist/tools/tool.js.map +1 -0
- package/dist/ui/agent-view.test.d.ts +1 -0
- package/dist/ui/agent-view.test.js +54 -0
- package/dist/ui/agent-view.test.js.map +1 -0
- package/dist/ui/agents-data.d.ts +7 -0
- package/dist/ui/agents-data.js +25 -0
- package/dist/ui/agents-data.js.map +1 -0
- package/dist/ui/app.d.ts +24 -0
- package/dist/ui/app.js +59 -0
- package/dist/ui/app.js.map +1 -0
- package/dist/ui/app.test.d.ts +1 -0
- package/dist/ui/app.test.js +47 -0
- package/dist/ui/app.test.js.map +1 -0
- package/dist/ui/components/key-hints.d.ts +4 -0
- package/dist/ui/components/key-hints.js +6 -0
- package/dist/ui/components/key-hints.js.map +1 -0
- package/dist/ui/components/menu.d.ts +11 -0
- package/dist/ui/components/menu.js +20 -0
- package/dist/ui/components/menu.js.map +1 -0
- package/dist/ui/create-wizard.test.d.ts +1 -0
- package/dist/ui/create-wizard.test.js +58 -0
- package/dist/ui/create-wizard.test.js.map +1 -0
- package/dist/ui/doctor-data.d.ts +6 -0
- package/dist/ui/doctor-data.js +29 -0
- package/dist/ui/doctor-data.js.map +1 -0
- package/dist/ui/history-data.d.ts +2 -0
- package/dist/ui/history-data.js +18 -0
- package/dist/ui/history-data.js.map +1 -0
- package/dist/ui/screens/agent.d.ts +8 -0
- package/dist/ui/screens/agent.js +95 -0
- package/dist/ui/screens/agent.js.map +1 -0
- package/dist/ui/screens/agents.d.ts +7 -0
- package/dist/ui/screens/agents.js +47 -0
- package/dist/ui/screens/agents.js.map +1 -0
- package/dist/ui/screens/create.d.ts +7 -0
- package/dist/ui/screens/create.js +141 -0
- package/dist/ui/screens/create.js.map +1 -0
- package/dist/ui/screens/doctor.d.ts +5 -0
- package/dist/ui/screens/doctor.js +13 -0
- package/dist/ui/screens/doctor.js.map +1 -0
- package/dist/ui/screens/history.d.ts +7 -0
- package/dist/ui/screens/history.js +50 -0
- package/dist/ui/screens/history.js.map +1 -0
- package/dist/ui/screens/home.d.ts +8 -0
- package/dist/ui/screens/home.js +35 -0
- package/dist/ui/screens/home.js.map +1 -0
- package/dist/ui/screens/plugins.d.ts +7 -0
- package/dist/ui/screens/plugins.js +40 -0
- package/dist/ui/screens/plugins.js.map +1 -0
- package/dist/ui/services.d.ts +33 -0
- package/dist/ui/services.js +67 -0
- package/dist/ui/services.js.map +1 -0
- package/dist/ui/start.d.ts +1 -0
- package/dist/ui/start.js +16 -0
- package/dist/ui/start.js.map +1 -0
- package/dist/ui/theme.d.ts +6 -0
- package/dist/ui/theme.js +26 -0
- package/dist/ui/theme.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join, relative, extname } from "node:path";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { simpleParser } from "mailparser";
|
|
5
|
+
import { addUsage, emptyUsage } from "../core/usage.js";
|
|
6
|
+
/**
|
|
7
|
+
* The onboarding pass: read a departed employee's exported artifacts (mail
|
|
8
|
+
* exports, docs, notes, tickets — anything text), distill them into durable
|
|
9
|
+
* role memories, and seed the agent's MemoryStore. Run once per agent with
|
|
10
|
+
* `exempclaw ingest <agent.json> <dir>`.
|
|
11
|
+
*/
|
|
12
|
+
const TEXT_EXTENSIONS = new Set([
|
|
13
|
+
".txt",
|
|
14
|
+
".md",
|
|
15
|
+
".markdown",
|
|
16
|
+
".eml",
|
|
17
|
+
".mbox",
|
|
18
|
+
".json",
|
|
19
|
+
".csv",
|
|
20
|
+
".tsv",
|
|
21
|
+
".log",
|
|
22
|
+
".html",
|
|
23
|
+
".htm",
|
|
24
|
+
".xml",
|
|
25
|
+
".yaml",
|
|
26
|
+
".yml",
|
|
27
|
+
]);
|
|
28
|
+
const DistilledSchema = z.object({
|
|
29
|
+
memories: z.array(z.object({
|
|
30
|
+
text: z.string().describe("One durable, self-contained fact."),
|
|
31
|
+
tags: z.array(z.string()).default([]),
|
|
32
|
+
})),
|
|
33
|
+
});
|
|
34
|
+
export async function ingestArchive(opts) {
|
|
35
|
+
const maxFileBytes = opts.maxFileBytes ?? 512 * 1024;
|
|
36
|
+
const chunkChars = opts.chunkChars ?? 24_000;
|
|
37
|
+
const maxChunks = opts.maxChunks ?? 60;
|
|
38
|
+
const progress = opts.onProgress ?? (() => undefined);
|
|
39
|
+
const { files, skipped } = await collectFiles(opts.dir, maxFileBytes);
|
|
40
|
+
if (files.length === 0) {
|
|
41
|
+
progress({ phase: "scanned", files: 0, skipped: skipped.length });
|
|
42
|
+
return { files: 0, chunks: 0, memoriesAdded: 0, skippedFiles: skipped, usage: emptyUsage() };
|
|
43
|
+
}
|
|
44
|
+
progress({ phase: "scanned", files: files.length, skipped: skipped.length });
|
|
45
|
+
let chunks = chunkFiles(files, chunkChars);
|
|
46
|
+
if (chunks.length > maxChunks) {
|
|
47
|
+
opts.log.warn("archive larger than chunk budget; ingesting the first chunks only", {
|
|
48
|
+
chunks: chunks.length,
|
|
49
|
+
maxChunks,
|
|
50
|
+
});
|
|
51
|
+
chunks = chunks.slice(0, maxChunks);
|
|
52
|
+
}
|
|
53
|
+
let usage = emptyUsage();
|
|
54
|
+
let memoriesAdded = 0;
|
|
55
|
+
const distilledTexts = [];
|
|
56
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
57
|
+
if (opts.signal?.aborted)
|
|
58
|
+
break;
|
|
59
|
+
progress({ phase: "chunk", index: index + 1, total: chunks.length, files: chunk.files });
|
|
60
|
+
const { value, usage: turnUsage } = await opts.claude.extract({
|
|
61
|
+
model: opts.model,
|
|
62
|
+
schema: DistilledSchema,
|
|
63
|
+
system: distillationSystemPrompt(opts.persona),
|
|
64
|
+
prompt: chunk.text,
|
|
65
|
+
effort: "medium",
|
|
66
|
+
signal: opts.signal,
|
|
67
|
+
});
|
|
68
|
+
usage = addUsage(usage, turnUsage);
|
|
69
|
+
for (const memory of value.memories) {
|
|
70
|
+
await opts.memory.addMemory({
|
|
71
|
+
text: memory.text,
|
|
72
|
+
source: "onboarding",
|
|
73
|
+
tags: [...new Set(memory.tags.map((t) => t.toLowerCase()))],
|
|
74
|
+
});
|
|
75
|
+
distilledTexts.push(memory.text);
|
|
76
|
+
memoriesAdded++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
let briefing;
|
|
80
|
+
if ((opts.briefing ?? true) && distilledTexts.length > 0 && !opts.signal?.aborted) {
|
|
81
|
+
progress({ phase: "briefing" });
|
|
82
|
+
const summary = await opts.claude.summarize(distilledTexts.join("\n- "), {
|
|
83
|
+
model: opts.model,
|
|
84
|
+
instruction: briefingInstruction(opts.persona),
|
|
85
|
+
signal: opts.signal,
|
|
86
|
+
});
|
|
87
|
+
await opts.memory.addMemory({ text: summary, source: "onboarding", tags: ["briefing"] });
|
|
88
|
+
memoriesAdded++;
|
|
89
|
+
briefing = summary;
|
|
90
|
+
}
|
|
91
|
+
return { files: files.length, chunks: chunks.length, memoriesAdded, skippedFiles: skipped, briefing, usage };
|
|
92
|
+
}
|
|
93
|
+
function distillationSystemPrompt(persona) {
|
|
94
|
+
return [
|
|
95
|
+
`You are preparing role context for ${persona.name}, who is taking over as ${persona.role}` +
|
|
96
|
+
(persona.succeeds ? ` from ${persona.succeeds}.` : "."),
|
|
97
|
+
"You will be given raw work artifacts (emails, docs, notes, tickets). Extract durable, forward-useful facts:",
|
|
98
|
+
"- people: who they are, their role, and how they relate to this role",
|
|
99
|
+
"- in-flight work, open commitments, and deadlines (with dates when stated)",
|
|
100
|
+
"- recurring processes, conventions, and where things live",
|
|
101
|
+
"- key decisions and their rationale",
|
|
102
|
+
"Each memory must be one self-contained sentence or two, understandable without the source document.",
|
|
103
|
+
"Skip pleasantries, duplicates, trivia, and anything stale. NEVER extract passwords, API keys, tokens, or other secrets — omit them entirely.",
|
|
104
|
+
"Tag each memory with short lowercase topics (e.g. a person's name, a project, \"process\", \"deadline\").",
|
|
105
|
+
"If a document contains nothing durable, return an empty list.",
|
|
106
|
+
].join("\n");
|
|
107
|
+
}
|
|
108
|
+
function briefingInstruction(persona) {
|
|
109
|
+
return (`Write a role briefing for ${persona.name}, the incoming ${persona.role}` +
|
|
110
|
+
(persona.succeeds ? ` succeeding ${persona.succeeds}` : "") +
|
|
111
|
+
". From the distilled notes below, produce a compact briefing covering: top priorities right now, " +
|
|
112
|
+
"key people and what they need, in-flight commitments with dates, and how this role usually operates. " +
|
|
113
|
+
"Dense prose, no fluff.");
|
|
114
|
+
}
|
|
115
|
+
/** Recursively collects readable text files, skipping junk and oversized files. */
|
|
116
|
+
export async function collectFiles(dir, maxFileBytes) {
|
|
117
|
+
const files = [];
|
|
118
|
+
const skipped = [];
|
|
119
|
+
async function walk(current) {
|
|
120
|
+
const entries = await readdir(current, { withFileTypes: true });
|
|
121
|
+
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
122
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
123
|
+
continue;
|
|
124
|
+
const full = join(current, entry.name);
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
await walk(full);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
if (!entry.isFile())
|
|
130
|
+
continue;
|
|
131
|
+
const rel = relative(dir, full);
|
|
132
|
+
if (!TEXT_EXTENSIONS.has(extname(entry.name).toLowerCase())) {
|
|
133
|
+
skipped.push(rel);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const info = await stat(full);
|
|
137
|
+
if (info.size > maxFileBytes) {
|
|
138
|
+
skipped.push(`${rel} (too large)`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const raw = await readFile(full, "utf8");
|
|
142
|
+
const text = await normalizeText(raw, extname(entry.name).toLowerCase());
|
|
143
|
+
if (text.trim().length === 0) {
|
|
144
|
+
skipped.push(`${rel} (empty)`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
files.push({ path: rel, text });
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
await walk(dir);
|
|
151
|
+
return { files, skipped };
|
|
152
|
+
}
|
|
153
|
+
async function normalizeText(raw, ext) {
|
|
154
|
+
let text = raw;
|
|
155
|
+
if (ext === ".html" || ext === ".htm") {
|
|
156
|
+
text = text
|
|
157
|
+
.replace(/<style[\s\S]*?<\/style>/gi, "")
|
|
158
|
+
.replace(/<script[\s\S]*?<\/script>/gi, "")
|
|
159
|
+
.replace(/<[^>]+>/g, " ");
|
|
160
|
+
}
|
|
161
|
+
else if (ext === ".eml") {
|
|
162
|
+
text = await parseEmlForIngest(raw);
|
|
163
|
+
}
|
|
164
|
+
else if (ext === ".mbox") {
|
|
165
|
+
text = await parseMboxForIngest(raw);
|
|
166
|
+
}
|
|
167
|
+
return text.replace(/\r\n/g, "\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n");
|
|
168
|
+
}
|
|
169
|
+
/** Parses one RFC-822 message into clean header + body text for distillation. */
|
|
170
|
+
async function parseEmlForIngest(raw) {
|
|
171
|
+
try {
|
|
172
|
+
const mail = await simpleParser(raw);
|
|
173
|
+
return renderMailForIngest(mail);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return raw; // fall back to the raw source rather than dropping the file
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Splits a classic mbox (messages separated by "From " lines) and parses each
|
|
181
|
+
* message. Caps the number of messages so a giant archive can't explode a
|
|
182
|
+
* single file into unbounded text.
|
|
183
|
+
*/
|
|
184
|
+
async function parseMboxForIngest(raw, maxMessages = 200) {
|
|
185
|
+
const parts = raw.split(/^From .*$/m).filter((p) => p.trim().length > 0);
|
|
186
|
+
if (parts.length === 0)
|
|
187
|
+
return raw;
|
|
188
|
+
const rendered = [];
|
|
189
|
+
for (const part of parts.slice(0, maxMessages)) {
|
|
190
|
+
try {
|
|
191
|
+
const mail = await simpleParser(part.trim());
|
|
192
|
+
rendered.push(renderMailForIngest(mail));
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// skip unparseable fragments
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (parts.length > maxMessages) {
|
|
199
|
+
rendered.push(`[…${parts.length - maxMessages} more messages truncated…]`);
|
|
200
|
+
}
|
|
201
|
+
return rendered.join("\n\n----\n\n");
|
|
202
|
+
}
|
|
203
|
+
function renderMailForIngest(mail) {
|
|
204
|
+
const to = Array.isArray(mail.to) ? mail.to.map((t) => t.text).join(", ") : mail.to?.text;
|
|
205
|
+
const body = (mail.text ?? "").trim();
|
|
206
|
+
return [
|
|
207
|
+
`From: ${mail.from?.text ?? "?"}`,
|
|
208
|
+
to ? `To: ${to}` : "",
|
|
209
|
+
mail.date ? `Date: ${mail.date.toISOString()}` : "",
|
|
210
|
+
`Subject: ${mail.subject ?? "(no subject)"}`,
|
|
211
|
+
"",
|
|
212
|
+
body,
|
|
213
|
+
]
|
|
214
|
+
.filter((line) => line !== "")
|
|
215
|
+
.join("\n");
|
|
216
|
+
}
|
|
217
|
+
/** Greedily packs files into chunks; oversized files split on paragraphs. */
|
|
218
|
+
export function chunkFiles(files, chunkChars) {
|
|
219
|
+
const chunks = [];
|
|
220
|
+
let buffer = "";
|
|
221
|
+
let bufferFiles = [];
|
|
222
|
+
const flush = () => {
|
|
223
|
+
if (buffer.trim())
|
|
224
|
+
chunks.push({ text: buffer, files: bufferFiles });
|
|
225
|
+
buffer = "";
|
|
226
|
+
bufferFiles = [];
|
|
227
|
+
};
|
|
228
|
+
for (const file of files) {
|
|
229
|
+
const pieces = file.text.length > chunkChars ? splitText(file.text, chunkChars) : [file.text];
|
|
230
|
+
for (const [i, piece] of pieces.entries()) {
|
|
231
|
+
const label = pieces.length > 1 ? `${file.path} (part ${i + 1}/${pieces.length})` : file.path;
|
|
232
|
+
const section = `===== FILE: ${label} =====\n${piece}\n`;
|
|
233
|
+
if (buffer.length > 0 && buffer.length + section.length > chunkChars)
|
|
234
|
+
flush();
|
|
235
|
+
buffer += section;
|
|
236
|
+
if (!bufferFiles.includes(file.path))
|
|
237
|
+
bufferFiles.push(file.path);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
flush();
|
|
241
|
+
return chunks;
|
|
242
|
+
}
|
|
243
|
+
function splitText(text, max) {
|
|
244
|
+
const parts = [];
|
|
245
|
+
let remaining = text;
|
|
246
|
+
while (remaining.length > max) {
|
|
247
|
+
// Prefer a paragraph boundary in the back half of the window.
|
|
248
|
+
let cut = remaining.lastIndexOf("\n\n", max);
|
|
249
|
+
if (cut < max / 2)
|
|
250
|
+
cut = max;
|
|
251
|
+
parts.push(remaining.slice(0, cut));
|
|
252
|
+
remaining = remaining.slice(cut);
|
|
253
|
+
}
|
|
254
|
+
if (remaining.trim())
|
|
255
|
+
parts.push(remaining);
|
|
256
|
+
return parts;
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/ingest/ingest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { Logger } from "../core/logger.js";
|
|
4
|
+
import { ExempclawError } from "../core/errors.js";
|
|
5
|
+
/**
|
|
6
|
+
* Thin wrapper over the Anthropic SDK. Centralizes model defaults, adaptive
|
|
7
|
+
* thinking, prompt caching, streaming, and refusal handling so the agent
|
|
8
|
+
* runtime never touches the SDK directly. Claude is the ONLY provider
|
|
9
|
+
* Exempclaw uses — there is no abstraction layer for other LLMs by design.
|
|
10
|
+
*/
|
|
11
|
+
export type EffortLevel = "low" | "medium" | "high" | "xhigh" | "max";
|
|
12
|
+
export interface ClaudeTurnParams {
|
|
13
|
+
model?: string;
|
|
14
|
+
/**
|
|
15
|
+
* System prompt blocks. Place `cache_control` on the last *stable* block
|
|
16
|
+
* (see persona.buildSystemBlocks) so the tools+persona prefix is cached.
|
|
17
|
+
*/
|
|
18
|
+
system: Anthropic.TextBlockParam[];
|
|
19
|
+
messages: Anthropic.MessageParam[];
|
|
20
|
+
tools?: Anthropic.ToolUnion[];
|
|
21
|
+
/** Caps a single response. Default 64000 (streamed). */
|
|
22
|
+
maxTokens?: number;
|
|
23
|
+
/** Controls reasoning depth + token spend. Defaults to "high". */
|
|
24
|
+
effort?: EffortLevel;
|
|
25
|
+
/** Cooperative cancellation — aborts the in-flight HTTP request. */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
/** Receives text deltas as they stream, for live terminal output. */
|
|
28
|
+
onText?: (delta: string) => void;
|
|
29
|
+
/**
|
|
30
|
+
* Adds a cache breakpoint on the last message so the whole conversation
|
|
31
|
+
* prefix is reusable next turn. On by default for the agent loop.
|
|
32
|
+
*/
|
|
33
|
+
cacheConversation?: boolean;
|
|
34
|
+
}
|
|
35
|
+
export declare class RefusalError extends ExempclawError {
|
|
36
|
+
readonly category: string | null;
|
|
37
|
+
constructor(category: string | null, explanation?: string | null);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The surface the agent runtime depends on. ClaudeClient implements it; tests
|
|
41
|
+
* substitute a scripted double.
|
|
42
|
+
*/
|
|
43
|
+
export interface ClaudeLike {
|
|
44
|
+
turn(params: ClaudeTurnParams): Promise<Anthropic.Message>;
|
|
45
|
+
summarize(text: string, opts?: {
|
|
46
|
+
model?: string;
|
|
47
|
+
instruction?: string;
|
|
48
|
+
signal?: AbortSignal;
|
|
49
|
+
}): Promise<string>;
|
|
50
|
+
}
|
|
51
|
+
export declare class ClaudeClient implements ClaudeLike {
|
|
52
|
+
private readonly defaultModel;
|
|
53
|
+
private readonly log;
|
|
54
|
+
private readonly client;
|
|
55
|
+
constructor(apiKey: string, defaultModel: string, log: Logger);
|
|
56
|
+
/**
|
|
57
|
+
* Runs a single assistant turn (streamed). Returns the full Message so the
|
|
58
|
+
* caller can inspect stop_reason, tool_use blocks, and usage. Throws
|
|
59
|
+
* RefusalError if the safety classifiers decline (possible on Fable 5).
|
|
60
|
+
*/
|
|
61
|
+
turn(params: ClaudeTurnParams): Promise<Anthropic.Message>;
|
|
62
|
+
/**
|
|
63
|
+
* One-shot structured extraction: constrains the response to `schema` via
|
|
64
|
+
* structured outputs, parses, and validates. Used by the ingestion pipeline.
|
|
65
|
+
*/
|
|
66
|
+
extract<T>(opts: {
|
|
67
|
+
prompt: string;
|
|
68
|
+
schema: z.ZodType<T>;
|
|
69
|
+
system?: string;
|
|
70
|
+
model?: string;
|
|
71
|
+
effort?: EffortLevel;
|
|
72
|
+
maxTokens?: number;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}): Promise<{
|
|
75
|
+
value: T;
|
|
76
|
+
usage: Anthropic.Usage;
|
|
77
|
+
}>;
|
|
78
|
+
/** Cheap one-shot summarization, used for history compaction. */
|
|
79
|
+
summarize(text: string, opts?: {
|
|
80
|
+
model?: string;
|
|
81
|
+
instruction?: string;
|
|
82
|
+
signal?: AbortSignal;
|
|
83
|
+
}): Promise<string>;
|
|
84
|
+
/** Verifies the API key + model are usable. Used by `exempclaw doctor`. */
|
|
85
|
+
ping(model?: string): Promise<{
|
|
86
|
+
id: string;
|
|
87
|
+
displayName: string;
|
|
88
|
+
}>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Returns a copy of `messages` with a cache breakpoint on the final content
|
|
92
|
+
* block, so the entire conversation prefix is served from cache next turn.
|
|
93
|
+
* The caller's array is not mutated.
|
|
94
|
+
*/
|
|
95
|
+
export declare function withConversationBreakpoint(messages: Anthropic.MessageParam[]): Anthropic.MessageParam[];
|
|
96
|
+
/** Parses model-produced JSON, tolerating markdown code fences. */
|
|
97
|
+
export declare function parseJsonLenient(text: string): unknown;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { ExempclawError } from "../core/errors.js";
|
|
4
|
+
export class RefusalError extends ExempclawError {
|
|
5
|
+
category;
|
|
6
|
+
constructor(category, explanation) {
|
|
7
|
+
super(`Claude declined the request${category ? ` (${category})` : ""}${explanation ? `: ${explanation}` : ""}`);
|
|
8
|
+
this.category = category;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export class ClaudeClient {
|
|
12
|
+
defaultModel;
|
|
13
|
+
log;
|
|
14
|
+
client;
|
|
15
|
+
constructor(apiKey, defaultModel, log) {
|
|
16
|
+
this.defaultModel = defaultModel;
|
|
17
|
+
this.log = log;
|
|
18
|
+
this.client = new Anthropic({ apiKey, maxRetries: 4 });
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Runs a single assistant turn (streamed). Returns the full Message so the
|
|
22
|
+
* caller can inspect stop_reason, tool_use blocks, and usage. Throws
|
|
23
|
+
* RefusalError if the safety classifiers decline (possible on Fable 5).
|
|
24
|
+
*/
|
|
25
|
+
async turn(params) {
|
|
26
|
+
const model = params.model || this.defaultModel;
|
|
27
|
+
const request = {
|
|
28
|
+
model,
|
|
29
|
+
max_tokens: params.maxTokens ?? 64000,
|
|
30
|
+
system: params.system,
|
|
31
|
+
messages: params.cacheConversation ? withConversationBreakpoint(params.messages) : params.messages,
|
|
32
|
+
thinking: { type: "adaptive" },
|
|
33
|
+
output_config: { effort: params.effort ?? "high" },
|
|
34
|
+
...(params.tools && params.tools.length > 0 ? { tools: params.tools } : {}),
|
|
35
|
+
};
|
|
36
|
+
const stream = this.client.messages.stream(request, { signal: params.signal });
|
|
37
|
+
if (params.onText)
|
|
38
|
+
stream.on("text", params.onText);
|
|
39
|
+
const message = await stream.finalMessage();
|
|
40
|
+
if (message.stop_reason === "refusal") {
|
|
41
|
+
throw new RefusalError(message.stop_details?.category ?? null, message.stop_details?.explanation);
|
|
42
|
+
}
|
|
43
|
+
this.log.debug("claude turn complete", {
|
|
44
|
+
model,
|
|
45
|
+
stop_reason: message.stop_reason ?? "unknown",
|
|
46
|
+
input_tokens: message.usage.input_tokens,
|
|
47
|
+
output_tokens: message.usage.output_tokens,
|
|
48
|
+
cache_read: message.usage.cache_read_input_tokens ?? 0,
|
|
49
|
+
});
|
|
50
|
+
return message;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One-shot structured extraction: constrains the response to `schema` via
|
|
54
|
+
* structured outputs, parses, and validates. Used by the ingestion pipeline.
|
|
55
|
+
*/
|
|
56
|
+
async extract(opts) {
|
|
57
|
+
const model = opts.model || this.defaultModel;
|
|
58
|
+
const jsonSchema = z.toJSONSchema(opts.schema, { target: "draft-7" });
|
|
59
|
+
const stream = this.client.messages.stream({
|
|
60
|
+
model,
|
|
61
|
+
max_tokens: opts.maxTokens ?? 16000,
|
|
62
|
+
...(opts.system ? { system: opts.system } : {}),
|
|
63
|
+
messages: [{ role: "user", content: opts.prompt }],
|
|
64
|
+
thinking: { type: "adaptive" },
|
|
65
|
+
output_config: {
|
|
66
|
+
effort: opts.effort ?? "medium",
|
|
67
|
+
format: { type: "json_schema", schema: jsonSchema },
|
|
68
|
+
},
|
|
69
|
+
}, { signal: opts.signal });
|
|
70
|
+
const message = await stream.finalMessage();
|
|
71
|
+
if (message.stop_reason === "refusal") {
|
|
72
|
+
throw new RefusalError(message.stop_details?.category ?? null, message.stop_details?.explanation);
|
|
73
|
+
}
|
|
74
|
+
const text = message.content
|
|
75
|
+
.filter((b) => b.type === "text")
|
|
76
|
+
.map((b) => b.text)
|
|
77
|
+
.join("");
|
|
78
|
+
const parsed = opts.schema.safeParse(parseJsonLenient(text));
|
|
79
|
+
if (!parsed.success) {
|
|
80
|
+
throw new ExempclawError(`structured extraction did not match schema: ${parsed.error.message}`, {
|
|
81
|
+
retryable: true,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return { value: parsed.data, usage: message.usage };
|
|
85
|
+
}
|
|
86
|
+
/** Cheap one-shot summarization, used for history compaction. */
|
|
87
|
+
async summarize(text, opts = {}) {
|
|
88
|
+
const instruction = opts.instruction ??
|
|
89
|
+
"Summarize this agent conversation transcript for the agent's own future reference. Preserve: open commitments and deadlines, key facts and decisions, people and their roles, unresolved threads, and anything the agent promised to do. Omit pleasantries and tool mechanics. Write it as dense factual notes.";
|
|
90
|
+
const message = await this.turn({
|
|
91
|
+
model: opts.model,
|
|
92
|
+
system: [{ type: "text", text: "You produce dense, factual summaries. No preamble." }],
|
|
93
|
+
messages: [{ role: "user", content: `${instruction}\n\n<transcript>\n${text}\n</transcript>` }],
|
|
94
|
+
effort: "low",
|
|
95
|
+
maxTokens: 4000,
|
|
96
|
+
signal: opts.signal,
|
|
97
|
+
});
|
|
98
|
+
return message.content
|
|
99
|
+
.filter((b) => b.type === "text")
|
|
100
|
+
.map((b) => b.text)
|
|
101
|
+
.join("\n")
|
|
102
|
+
.trim();
|
|
103
|
+
}
|
|
104
|
+
/** Verifies the API key + model are usable. Used by `exempclaw doctor`. */
|
|
105
|
+
async ping(model) {
|
|
106
|
+
const info = await this.client.models.retrieve(model || this.defaultModel);
|
|
107
|
+
return { id: info.id, displayName: info.display_name };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Returns a copy of `messages` with a cache breakpoint on the final content
|
|
112
|
+
* block, so the entire conversation prefix is served from cache next turn.
|
|
113
|
+
* The caller's array is not mutated.
|
|
114
|
+
*/
|
|
115
|
+
export function withConversationBreakpoint(messages) {
|
|
116
|
+
if (messages.length === 0)
|
|
117
|
+
return messages;
|
|
118
|
+
const out = messages.slice();
|
|
119
|
+
const last = out[out.length - 1];
|
|
120
|
+
const cacheControl = { type: "ephemeral" };
|
|
121
|
+
if (typeof last.content === "string") {
|
|
122
|
+
out[out.length - 1] = {
|
|
123
|
+
...last,
|
|
124
|
+
content: [{ type: "text", text: last.content, cache_control: cacheControl }],
|
|
125
|
+
};
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
const blocks = last.content.slice();
|
|
129
|
+
const lastBlock = blocks[blocks.length - 1];
|
|
130
|
+
if (!lastBlock)
|
|
131
|
+
return messages;
|
|
132
|
+
// Only block types that accept cache_control; skip exotic blocks rather than error.
|
|
133
|
+
if (lastBlock.type === "text" ||
|
|
134
|
+
lastBlock.type === "tool_result" ||
|
|
135
|
+
lastBlock.type === "tool_use" ||
|
|
136
|
+
lastBlock.type === "image" ||
|
|
137
|
+
lastBlock.type === "document") {
|
|
138
|
+
blocks[blocks.length - 1] = { ...lastBlock, cache_control: cacheControl };
|
|
139
|
+
out[out.length - 1] = { ...last, content: blocks };
|
|
140
|
+
return out;
|
|
141
|
+
}
|
|
142
|
+
return messages;
|
|
143
|
+
}
|
|
144
|
+
/** Parses model-produced JSON, tolerating markdown code fences. */
|
|
145
|
+
export function parseJsonLenient(text) {
|
|
146
|
+
const trimmed = text.trim();
|
|
147
|
+
try {
|
|
148
|
+
return JSON.parse(trimmed);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/.exec(trimmed);
|
|
152
|
+
if (fenced?.[1]) {
|
|
153
|
+
try {
|
|
154
|
+
return JSON.parse(fenced[1].trim());
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return undefined;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=claude.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude.js","sourceRoot":"","sources":["../../src/llm/claude.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
/**
|
|
3
|
+
* Client-side history compaction. When a conversation outgrows the context
|
|
4
|
+
* budget, the older portion is summarized into a single exchange and the
|
|
5
|
+
* recent turns are kept verbatim. The cut point is chosen so tool_use /
|
|
6
|
+
* tool_result pairs are never split.
|
|
7
|
+
*/
|
|
8
|
+
export interface CompactionOptions {
|
|
9
|
+
/** Roughly how many trailing messages to keep verbatim. Default 8. */
|
|
10
|
+
keepRecent?: number;
|
|
11
|
+
/** Produces the summary text for the compacted transcript. */
|
|
12
|
+
summarize: (transcript: string) => Promise<string>;
|
|
13
|
+
}
|
|
14
|
+
export declare function compactHistory(messages: Anthropic.MessageParam[], opts: CompactionOptions): Promise<Anthropic.MessageParam[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Finds the earliest valid boundary at or after (length - keepRecent): the
|
|
17
|
+
* boundary message must be a user turn that is not a tool_result follow-up,
|
|
18
|
+
* so the kept tail never references a tool_use that was summarized away.
|
|
19
|
+
*/
|
|
20
|
+
export declare function findCutIndex(messages: Anthropic.MessageParam[], keepRecent: number): number | null;
|
|
21
|
+
/** Renders messages into a plain-text transcript for the summarizer. */
|
|
22
|
+
export declare function renderTranscript(messages: Anthropic.MessageParam[]): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export async function compactHistory(messages, opts) {
|
|
2
|
+
const keepRecent = opts.keepRecent ?? 8;
|
|
3
|
+
const cut = findCutIndex(messages, keepRecent);
|
|
4
|
+
if (cut === null || cut <= 1)
|
|
5
|
+
return messages;
|
|
6
|
+
const transcript = renderTranscript(messages.slice(0, cut));
|
|
7
|
+
const summary = await opts.summarize(transcript);
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
role: "user",
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: "text",
|
|
14
|
+
text: `<conversation_summary>\n${summary}\n</conversation_summary>\nEarlier turns of this conversation were compacted into the summary above. Continue from it as if you lived through those turns.`,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
},
|
|
18
|
+
{ role: "assistant", content: "Understood — I have the summary of the earlier conversation and will continue from it." },
|
|
19
|
+
...messages.slice(cut),
|
|
20
|
+
];
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Finds the earliest valid boundary at or after (length - keepRecent): the
|
|
24
|
+
* boundary message must be a user turn that is not a tool_result follow-up,
|
|
25
|
+
* so the kept tail never references a tool_use that was summarized away.
|
|
26
|
+
*/
|
|
27
|
+
export function findCutIndex(messages, keepRecent) {
|
|
28
|
+
const target = messages.length - keepRecent;
|
|
29
|
+
if (target <= 1)
|
|
30
|
+
return null;
|
|
31
|
+
for (let i = target; i < messages.length; i++) {
|
|
32
|
+
const msg = messages[i];
|
|
33
|
+
if (msg.role !== "user")
|
|
34
|
+
continue;
|
|
35
|
+
if (typeof msg.content === "string")
|
|
36
|
+
return i;
|
|
37
|
+
const hasToolResult = msg.content.some((b) => b.type === "tool_result");
|
|
38
|
+
if (!hasToolResult)
|
|
39
|
+
return i;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
/** Renders messages into a plain-text transcript for the summarizer. */
|
|
44
|
+
export function renderTranscript(messages) {
|
|
45
|
+
const lines = [];
|
|
46
|
+
for (const msg of messages) {
|
|
47
|
+
if (typeof msg.content === "string") {
|
|
48
|
+
lines.push(`${msg.role.toUpperCase()}: ${msg.content}`);
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
for (const block of msg.content) {
|
|
52
|
+
switch (block.type) {
|
|
53
|
+
case "text":
|
|
54
|
+
lines.push(`${msg.role.toUpperCase()}: ${block.text}`);
|
|
55
|
+
break;
|
|
56
|
+
case "tool_use":
|
|
57
|
+
lines.push(`${msg.role.toUpperCase()} called ${block.name}(${truncate(JSON.stringify(block.input), 400)})`);
|
|
58
|
+
break;
|
|
59
|
+
case "tool_result": {
|
|
60
|
+
const content = typeof block.content === "string"
|
|
61
|
+
? block.content
|
|
62
|
+
: (block.content ?? [])
|
|
63
|
+
.map((c) => (c.type === "text" ? c.text : `[${c.type}]`))
|
|
64
|
+
.join(" ");
|
|
65
|
+
lines.push(`TOOL RESULT${block.is_error ? " (error)" : ""}: ${truncate(content, 600)}`);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
default:
|
|
69
|
+
// thinking blocks and other internals are deliberately omitted
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return lines.join("\n");
|
|
75
|
+
}
|
|
76
|
+
function truncate(text, max) {
|
|
77
|
+
return text.length > max ? `${text.slice(0, max)}…` : text;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=compaction.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../src/memory/compaction.ts"],"names":[],"mappings":"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"}
|