augure 0.7.1 → 0.9.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.
@@ -0,0 +1,164 @@
1
+ #!/usr/bin/env node
2
+
3
+ // ../memory/dist/store.js
4
+ import { readFile, writeFile, mkdir, readdir, access } from "fs/promises";
5
+ import { join, dirname, relative } from "path";
6
+ var FileMemoryStore = class {
7
+ basePath;
8
+ constructor(basePath) {
9
+ this.basePath = basePath;
10
+ }
11
+ async read(path) {
12
+ return readFile(this.resolve(path), "utf-8");
13
+ }
14
+ async write(path, content) {
15
+ const full = this.resolve(path);
16
+ await mkdir(dirname(full), { recursive: true });
17
+ await writeFile(full, content, "utf-8");
18
+ }
19
+ async append(path, content) {
20
+ const full = this.resolve(path);
21
+ try {
22
+ const existing = await readFile(full, "utf-8");
23
+ await writeFile(full, existing + content, "utf-8");
24
+ } catch {
25
+ await this.write(path, content);
26
+ }
27
+ }
28
+ async list(directory) {
29
+ const dir = directory ? this.resolve(directory) : this.basePath;
30
+ return this.listRecursive(dir);
31
+ }
32
+ async exists(path) {
33
+ try {
34
+ await access(this.resolve(path));
35
+ return true;
36
+ } catch {
37
+ return false;
38
+ }
39
+ }
40
+ resolve(path) {
41
+ return join(this.basePath, path);
42
+ }
43
+ async listRecursive(dir) {
44
+ const entries = await readdir(dir, { withFileTypes: true });
45
+ const files = [];
46
+ for (const entry of entries) {
47
+ const full = join(dir, entry.name);
48
+ if (entry.isDirectory()) {
49
+ files.push(...await this.listRecursive(full));
50
+ } else {
51
+ files.push(relative(this.basePath, full));
52
+ }
53
+ }
54
+ return files;
55
+ }
56
+ };
57
+
58
+ // ../memory/dist/ingest.js
59
+ var EXTRACTION_PROMPT = `You are a memory extraction agent. Given a conversation, extract key factual observations about the user.
60
+
61
+ Rules:
62
+ - Return a markdown bullet list of observations (one per line, starting with "- ")
63
+ - Only extract facts, preferences, decisions, plans, and personal details
64
+ - Be concise: one fact per bullet
65
+ - If there are no notable observations, return exactly "No notable observations."
66
+ - Do not include greetings, small talk, or meta-conversation
67
+ - Use present tense ("User prefers X", not "User said they prefer X")
68
+
69
+ Example output:
70
+ - User prefers TypeScript over JavaScript
71
+ - User is building a project called Augure
72
+ - User lives in Bordeaux, France`;
73
+ var MemoryIngester = class {
74
+ llm;
75
+ store;
76
+ constructor(llm, store) {
77
+ this.llm = llm;
78
+ this.store = store;
79
+ }
80
+ async ingest(conversation) {
81
+ if (conversation.length === 0)
82
+ return;
83
+ const conversationText = conversation.filter((m) => m.role === "user" || m.role === "assistant").map((m) => `${m.role}: ${m.content}`).join("\n");
84
+ const messages = [
85
+ { role: "system", content: EXTRACTION_PROMPT },
86
+ { role: "user", content: conversationText }
87
+ ];
88
+ const response = await this.llm.chat(messages);
89
+ const observations = this.parseObservations(response.content);
90
+ if (observations.length === 0)
91
+ return;
92
+ const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
93
+ const block = `## ${date}
94
+ ${observations.map((o) => `- ${o}`).join("\n")}
95
+
96
+ `;
97
+ await this.store.append("observations.md", block);
98
+ }
99
+ parseObservations(content) {
100
+ const lines = content.split("\n");
101
+ return lines.filter((line) => line.trim().startsWith("- ")).map((line) => line.trim().slice(2).trim()).filter((line) => line.length > 0);
102
+ }
103
+ };
104
+
105
+ // ../memory/dist/retrieve.js
106
+ var PRIORITY_FILES = ["identity.md", "observations.md"];
107
+ var CHARS_PER_TOKEN = 4;
108
+ var MemoryRetriever = class {
109
+ store;
110
+ maxChars;
111
+ constructor(store, options = {}) {
112
+ this.store = store;
113
+ const maxTokens = options.maxTokens ?? 1e4;
114
+ this.maxChars = maxTokens * CHARS_PER_TOKEN;
115
+ }
116
+ async retrieve() {
117
+ const allFiles = await this.safeList();
118
+ if (allFiles.length === 0)
119
+ return "";
120
+ const prioritySet = new Set(PRIORITY_FILES);
121
+ const orderedFiles = [
122
+ ...PRIORITY_FILES.filter((f) => allFiles.includes(f)),
123
+ ...allFiles.filter((f) => !prioritySet.has(f)).sort()
124
+ ];
125
+ const sections = [];
126
+ let totalChars = 0;
127
+ for (const file of orderedFiles) {
128
+ if (totalChars >= this.maxChars)
129
+ break;
130
+ try {
131
+ const content = await this.store.read(file);
132
+ const header = `### ${file}`;
133
+ const section = `${header}
134
+ ${content}`;
135
+ const sectionChars = section.length;
136
+ if (totalChars + sectionChars > this.maxChars) {
137
+ const remaining = this.maxChars - totalChars;
138
+ if (remaining > header.length + 50) {
139
+ sections.push(section.slice(0, remaining) + "\n[...truncated]");
140
+ totalChars = this.maxChars;
141
+ }
142
+ break;
143
+ }
144
+ sections.push(section);
145
+ totalChars += sectionChars;
146
+ } catch {
147
+ }
148
+ }
149
+ return sections.join("\n\n");
150
+ }
151
+ async safeList() {
152
+ try {
153
+ return await this.store.list();
154
+ } catch {
155
+ return [];
156
+ }
157
+ }
158
+ };
159
+
160
+ export {
161
+ FileMemoryStore,
162
+ MemoryIngester,
163
+ MemoryRetriever
164
+ };
@@ -0,0 +1,348 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ noopLogger
4
+ } from "./chunk-62OPINAX.js";
5
+
6
+ // ../sandbox/dist/container.js
7
+ import { PassThrough } from "stream";
8
+ var MAX_OUTPUT_BYTES = 1024 * 1024;
9
+ var DockerContainer = class {
10
+ id;
11
+ _status = "idle";
12
+ raw;
13
+ demux;
14
+ constructor(raw, demux) {
15
+ this.raw = raw;
16
+ this.id = raw.id;
17
+ this.demux = demux;
18
+ }
19
+ get status() {
20
+ return this._status;
21
+ }
22
+ async exec(command, opts) {
23
+ this._status = "busy";
24
+ try {
25
+ return await this._exec(command, opts);
26
+ } finally {
27
+ if (this._status !== "stopped") {
28
+ this._status = "idle";
29
+ }
30
+ }
31
+ }
32
+ async stop() {
33
+ if (this._status === "stopped")
34
+ return;
35
+ try {
36
+ await this.raw.stop({ t: 5 });
37
+ } catch {
38
+ }
39
+ try {
40
+ await this.raw.remove({ force: true });
41
+ } catch {
42
+ }
43
+ this._status = "stopped";
44
+ }
45
+ /* ------------------------------------------------------------------ */
46
+ async _exec(command, opts) {
47
+ const createOpts = {
48
+ Cmd: ["sh", "-c", command],
49
+ AttachStdout: true,
50
+ AttachStderr: true
51
+ };
52
+ if (opts?.cwd) {
53
+ createOpts.WorkingDir = opts.cwd;
54
+ }
55
+ if (opts?.env) {
56
+ createOpts.Env = Object.entries(opts.env).map(([k, v]) => `${k}=${v}`);
57
+ }
58
+ const exec = await this.raw.exec(createOpts);
59
+ const stream = await exec.start({ hijack: true, stdin: false });
60
+ return new Promise((resolve, reject) => {
61
+ const stdoutPT = new PassThrough();
62
+ const stderrPT = new PassThrough();
63
+ const stdoutBufs = [];
64
+ const stderrBufs = [];
65
+ let stdoutLen = 0;
66
+ let stderrLen = 0;
67
+ stdoutPT.on("data", (chunk) => {
68
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
69
+ const remaining = MAX_OUTPUT_BYTES - stdoutLen;
70
+ if (remaining > 0) {
71
+ stdoutBufs.push(buf.subarray(0, remaining));
72
+ stdoutLen += Math.min(buf.length, remaining);
73
+ }
74
+ });
75
+ stderrPT.on("data", (chunk) => {
76
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
77
+ const remaining = MAX_OUTPUT_BYTES - stderrLen;
78
+ if (remaining > 0) {
79
+ stderrBufs.push(buf.subarray(0, remaining));
80
+ stderrLen += Math.min(buf.length, remaining);
81
+ }
82
+ });
83
+ let timer;
84
+ let settled = false;
85
+ let stdoutEnded = false;
86
+ let stderrEnded = false;
87
+ const cleanup = () => {
88
+ if (timer)
89
+ clearTimeout(timer);
90
+ };
91
+ if (opts?.timeout && opts.timeout > 0) {
92
+ const timeoutMs = opts.timeout * 1e3;
93
+ timer = setTimeout(() => {
94
+ if (!settled) {
95
+ settled = true;
96
+ stream.destroy();
97
+ reject(new Error(`Exec timed out after ${opts.timeout}s`));
98
+ }
99
+ }, timeoutMs);
100
+ }
101
+ const tryResolve = async () => {
102
+ if (!stdoutEnded || !stderrEnded)
103
+ return;
104
+ if (settled)
105
+ return;
106
+ settled = true;
107
+ cleanup();
108
+ try {
109
+ const info = await exec.inspect();
110
+ resolve({
111
+ exitCode: info.ExitCode ?? 1,
112
+ stdout: Buffer.concat(stdoutBufs).toString("utf-8"),
113
+ stderr: Buffer.concat(stderrBufs).toString("utf-8")
114
+ });
115
+ } catch (err) {
116
+ reject(err);
117
+ }
118
+ };
119
+ stdoutPT.on("end", () => {
120
+ stdoutEnded = true;
121
+ tryResolve();
122
+ });
123
+ stderrPT.on("end", () => {
124
+ stderrEnded = true;
125
+ tryResolve();
126
+ });
127
+ stream.on("error", (err) => {
128
+ if (!settled) {
129
+ settled = true;
130
+ cleanup();
131
+ reject(err);
132
+ }
133
+ });
134
+ this.demux(stream, stdoutPT, stderrPT);
135
+ stream.on("end", () => {
136
+ stdoutPT.end();
137
+ stderrPT.end();
138
+ });
139
+ });
140
+ }
141
+ };
142
+
143
+ // ../sandbox/dist/pool.js
144
+ function parseMemory(mem) {
145
+ const match = mem.match(/^(\d+(?:\.\d+)?)\s*([mg])$/i);
146
+ if (!match)
147
+ throw new Error(`Invalid memory value: ${mem}`);
148
+ const value = parseFloat(match[1]);
149
+ const unit = match[2].toLowerCase();
150
+ if (unit === "m")
151
+ return Math.round(value * 1024 * 1024);
152
+ return Math.round(value * 1024 * 1024 * 1024);
153
+ }
154
+ function parseCpu(cpu) {
155
+ const value = parseFloat(cpu);
156
+ if (Number.isNaN(value))
157
+ throw new Error(`Invalid cpu value: ${cpu}`);
158
+ return Math.round(value * 1e9);
159
+ }
160
+ var DockerContainerPool = class {
161
+ docker;
162
+ image;
163
+ maxTotal;
164
+ log;
165
+ // C3: idle cache keyed by trust level to prevent cross-trust reuse
166
+ idle = /* @__PURE__ */ new Map([
167
+ ["sandboxed", /* @__PURE__ */ new Set()],
168
+ ["trusted", /* @__PURE__ */ new Set()]
169
+ ]);
170
+ busy = /* @__PURE__ */ new Set();
171
+ containerTrust = /* @__PURE__ */ new Map();
172
+ constructor(docker, config) {
173
+ this.docker = docker;
174
+ this.image = config.image;
175
+ this.maxTotal = config.maxTotal;
176
+ this.log = config.logger ?? noopLogger;
177
+ }
178
+ get idleCount() {
179
+ let count = 0;
180
+ for (const set of this.idle.values())
181
+ count += set.size;
182
+ return count;
183
+ }
184
+ /* ---- acquire ---- */
185
+ async acquire(opts) {
186
+ this.log.debug(`Acquiring container: trust=${opts.trust} memory=${opts.memory} cpu=${opts.cpu}`);
187
+ const trustIdle = this.idle.get(opts.trust);
188
+ const cached = trustIdle.values().next();
189
+ if (!cached.done) {
190
+ const container2 = cached.value;
191
+ trustIdle.delete(container2);
192
+ this.busy.add(container2);
193
+ this.log.debug(`Reusing cached container: ${container2.id.slice(0, 12)}`);
194
+ return container2;
195
+ }
196
+ const total = this.idleCount + this.busy.size;
197
+ if (total >= this.maxTotal) {
198
+ this.log.error(`Pool limit reached: ${total}/${this.maxTotal}`);
199
+ throw new Error("Pool limit reached");
200
+ }
201
+ this.log.debug("Creating new container...");
202
+ const raw = await this.docker.createContainer(this.buildCreateOpts(opts));
203
+ await raw.start();
204
+ const modem = this.docker.modem;
205
+ const demux = modem.demuxStream.bind(modem);
206
+ const container = new DockerContainer(raw, demux);
207
+ this.containerTrust.set(container.id, opts.trust);
208
+ this.busy.add(container);
209
+ this.log.debug(`Container created: ${container.id.slice(0, 12)}`);
210
+ return container;
211
+ }
212
+ /* ---- release ---- */
213
+ async release(container) {
214
+ this.busy.delete(container);
215
+ if (container.status === "stopped") {
216
+ this.containerTrust.delete(container.id);
217
+ return;
218
+ }
219
+ const trust = this.containerTrust.get(container.id) ?? "sandboxed";
220
+ this.idle.get(trust).add(container);
221
+ this.log.debug(`Container released: ${container.id.slice(0, 12)} \u2192 idle (${trust})`);
222
+ }
223
+ /* ---- destroy ---- */
224
+ async destroy(container) {
225
+ this.busy.delete(container);
226
+ for (const set of this.idle.values())
227
+ set.delete(container);
228
+ this.containerTrust.delete(container.id);
229
+ await container.stop();
230
+ }
231
+ /* ---- destroyAll ---- */
232
+ async destroyAll() {
233
+ const all = [...this.busy];
234
+ for (const set of this.idle.values()) {
235
+ all.push(...set);
236
+ set.clear();
237
+ }
238
+ this.busy.clear();
239
+ this.containerTrust.clear();
240
+ await Promise.allSettled(all.map((c) => c.stop()));
241
+ }
242
+ /* ---- stats ---- */
243
+ stats() {
244
+ const idle = this.idleCount;
245
+ return {
246
+ idle,
247
+ busy: this.busy.size,
248
+ total: idle + this.busy.size,
249
+ maxTotal: this.maxTotal
250
+ };
251
+ }
252
+ /* ---- internal ---- */
253
+ buildCreateOpts(opts) {
254
+ const isSandboxed = opts.trust === "sandboxed";
255
+ const hostConfig = {
256
+ Memory: parseMemory(opts.memory),
257
+ NanoCpus: parseCpu(opts.cpu),
258
+ PidsLimit: 512
259
+ // I6: prevent fork bombs
260
+ };
261
+ if (!isSandboxed) {
262
+ hostConfig.NetworkMode = "host";
263
+ }
264
+ if (opts.mounts?.length) {
265
+ hostConfig.Binds = opts.mounts.map((m) => `${m.host}:${m.container}${m.readonly ? ":ro" : ""}`);
266
+ }
267
+ const createOpts = {
268
+ Image: this.image,
269
+ Cmd: ["sleep", "infinity"],
270
+ WorkingDir: "/workspace",
271
+ NetworkDisabled: isSandboxed,
272
+ HostConfig: hostConfig
273
+ };
274
+ if (opts.env) {
275
+ createOpts.Env = Object.entries(opts.env).map(([k, v]) => `${k}=${v}`);
276
+ }
277
+ return createOpts;
278
+ }
279
+ };
280
+
281
+ // ../sandbox/dist/ensure-image.js
282
+ import { Readable } from "stream";
283
+ var DOCKERFILE = `FROM node:22-slim
284
+ RUN apt-get update && apt-get install -y --no-install-recommends \\
285
+ python3 curl jq git \\
286
+ && rm -rf /var/lib/apt/lists/*
287
+ WORKDIR /workspace
288
+ `;
289
+ function buildTar(content) {
290
+ const data = Buffer.from(content, "utf-8");
291
+ const name = "Dockerfile";
292
+ const header = Buffer.alloc(512, 0);
293
+ header.write(name, 0, 100, "utf-8");
294
+ header.write("0000644\0", 100, 8, "utf-8");
295
+ header.write("0000000\0", 108, 8, "utf-8");
296
+ header.write("0000000\0", 116, 8, "utf-8");
297
+ header.write(data.length.toString(8).padStart(11, "0") + "\0", 124, 12, "utf-8");
298
+ header.write(Math.floor(Date.now() / 1e3).toString(8).padStart(11, "0") + "\0", 136, 12, "utf-8");
299
+ header.write("0", 156, 1, "utf-8");
300
+ header.fill(32, 148, 156);
301
+ let checksum = 0;
302
+ for (let i = 0; i < 512; i++)
303
+ checksum += header[i];
304
+ header.write(checksum.toString(8).padStart(6, "0") + "\0 ", 148, 8, "utf-8");
305
+ const padding = 512 - (data.length % 512 || 512);
306
+ const dataPadded = padding > 0 && padding < 512 ? Buffer.concat([data, Buffer.alloc(padding, 0)]) : data;
307
+ const end = Buffer.alloc(1024, 0);
308
+ return Buffer.concat([header, dataPadded, end]);
309
+ }
310
+ async function ensureImage(docker, imageName, logger) {
311
+ const log = logger ?? noopLogger;
312
+ log.debug(`Checking image: ${imageName}`);
313
+ try {
314
+ await docker.getImage(imageName).inspect();
315
+ log.debug("Image exists");
316
+ return;
317
+ } catch (err) {
318
+ const statusCode = err.statusCode;
319
+ if (statusCode !== void 0 && statusCode !== 404)
320
+ throw err;
321
+ }
322
+ log.info(`Image "${imageName}" not found, building...`);
323
+ const tar = buildTar(DOCKERFILE);
324
+ const stream = await docker.buildImage(Readable.from(tar), {
325
+ t: imageName
326
+ });
327
+ await new Promise((resolve, reject) => {
328
+ docker.modem.followProgress(stream, (err) => {
329
+ if (err)
330
+ reject(err);
331
+ else
332
+ resolve();
333
+ }, (event) => {
334
+ if (event.stream) {
335
+ const line = event.stream.trim();
336
+ if (line)
337
+ log.debug(line);
338
+ }
339
+ });
340
+ });
341
+ log.info(`Image "${imageName}" built`);
342
+ }
343
+
344
+ export {
345
+ DockerContainer,
346
+ DockerContainerPool,
347
+ ensureImage
348
+ };