meow-swarm 0.1.2 → 0.2.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.
Files changed (2) hide show
  1. package/dist/index.js +389 -94
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -16,6 +16,15 @@ var __export = (target, all) => {
16
16
  };
17
17
 
18
18
  // src/config/env.ts
19
+ function parseIntOrUndefined(env) {
20
+ if (!env) return void 0;
21
+ const v = parseInt(env, 10);
22
+ return isNaN(v) ? void 0 : v;
23
+ }
24
+ function parseBool(env, fallback) {
25
+ if (!env) return fallback;
26
+ return env === "1" || env === "true" || env === "yes";
27
+ }
19
28
  var config;
20
29
  var init_env = __esm({
21
30
  "src/config/env.ts"() {
@@ -24,7 +33,16 @@ var init_env = __esm({
24
33
  apiKey: process.env.LLM_API_KEY || process.env.ANTHROPIC_API_KEY,
25
34
  baseUrl: process.env.LLM_BASE_URL || process.env.ANTHROPIC_BASE_URL || "http://localhost:11434",
26
35
  model: process.env.ANTHROPIC_MODEL || process.env.MEOW_MODEL || "claude-3-5-sonnet-latest",
27
- embeddingDimension: parseInt(process.env.EMBEDDING_DIMENSION || "1536")
36
+ embeddingDimension: parseInt(process.env.EMBEDDING_DIMENSION || "1536"),
37
+ // Reproducibility
38
+ seed: parseIntOrUndefined(process.env.MEOW_SEED),
39
+ deterministic: parseBool(process.env.MEOW_DETERMINISTIC, false),
40
+ // Budget (per mission, in US cents)
41
+ budgetCents: parseIntOrUndefined(process.env.MEOW_BUDGET_CENTS),
42
+ // Audit log path
43
+ auditDir: process.env.MEOW_AUDIT_DIR || `${process.env.HOME || "."}/.meow/audit`,
44
+ // Ambiguity: agent asks for clarification if uncertainty > this threshold (0.0-1.0)
45
+ ambiguityThreshold: parseFloat(process.env.MEOW_AMBIGUITY_THRESHOLD || "0.7")
28
46
  };
29
47
  }
30
48
  });
@@ -234,10 +252,10 @@ var init_discovery = __esm({
234
252
  join(this.home, ".agents", "skills")
235
253
  ];
236
254
  const skills = [];
237
- for (const path3 of globalPaths) {
255
+ for (const path4 of globalPaths) {
238
256
  try {
239
- await access2(path3);
240
- const patterns = [join(path3, "**/SKILL.md")];
257
+ await access2(path4);
258
+ const patterns = [join(path4, "**/SKILL.md")];
241
259
  const files = await globby2(patterns, { absolute: true });
242
260
  for (const file of files) {
243
261
  try {
@@ -1113,16 +1131,16 @@ var init_tool = __esm({
1113
1131
  {
1114
1132
  name: "read",
1115
1133
  description: "Read file contents",
1116
- execute: async (path3) => {
1117
- return await readFile4(path3.trim(), "utf-8");
1134
+ execute: async (path4) => {
1135
+ return await readFile4(path4.trim(), "utf-8");
1118
1136
  }
1119
1137
  },
1120
1138
  {
1121
1139
  name: "write",
1122
1140
  description: "Write file contents",
1123
1141
  execute: async (args) => {
1124
- const [path3, content] = args.split("|");
1125
- await writeFile2(path3.trim(), content);
1142
+ const [path4, content] = args.split("|");
1143
+ await writeFile2(path4.trim(), content);
1126
1144
  return "Written successfully";
1127
1145
  }
1128
1146
  },
@@ -1686,12 +1704,92 @@ var init_reasoning = __esm({
1686
1704
  }
1687
1705
  });
1688
1706
 
1707
+ // src/kernel/audit.ts
1708
+ import fs from "fs";
1709
+ import path from "path";
1710
+ var AuditLogger;
1711
+ var init_audit = __esm({
1712
+ "src/kernel/audit.ts"() {
1713
+ "use strict";
1714
+ init_env();
1715
+ AuditLogger = class {
1716
+ logStream = null;
1717
+ logDir;
1718
+ runId;
1719
+ constructor(runId) {
1720
+ this.runId = runId;
1721
+ this.logDir = config.auditDir;
1722
+ this.ensureLogDir();
1723
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 7);
1724
+ const logPath = path.join(this.logDir, `${today}.jsonl`);
1725
+ this.logStream = fs.createWriteStream(logPath, { flags: "a" });
1726
+ }
1727
+ ensureLogDir() {
1728
+ if (!fs.existsSync(this.logDir)) {
1729
+ fs.mkdirSync(this.logDir, { recursive: true });
1730
+ }
1731
+ }
1732
+ log(entry) {
1733
+ const full = {
1734
+ ...entry,
1735
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1736
+ runId: this.runId
1737
+ };
1738
+ this.logStream?.write(JSON.stringify(full) + "\n");
1739
+ }
1740
+ // Convenience methods
1741
+ llmCall(model, inputTokens, outputTokens, durationMs, costCents, success) {
1742
+ this.log({
1743
+ level: "debug",
1744
+ actionType: "llm_call",
1745
+ detail: `${model}: ${inputTokens} in / ${outputTokens} out / ${costCents.toFixed(4)}\xA2 / ${durationMs}ms`,
1746
+ tokensUsed: { input: inputTokens, output: outputTokens },
1747
+ costCents,
1748
+ durationMs,
1749
+ success
1750
+ });
1751
+ }
1752
+ toolExec(toolName, args, exitCode, durationMs, success) {
1753
+ this.log({
1754
+ level: "info",
1755
+ actionType: "tool_exec",
1756
+ detail: `${toolName}(${args.slice(0, 100)}) \u2192 ${exitCode} (${durationMs}ms)`,
1757
+ durationMs,
1758
+ success: exitCode === 0
1759
+ });
1760
+ }
1761
+ fileWrite(path4, lines) {
1762
+ this.log({ level: "info", actionType: "file_write", detail: `${path4} (+${lines} lines)` });
1763
+ }
1764
+ shellCmd(cmd, exitCode, durationMs) {
1765
+ this.log({
1766
+ level: "info",
1767
+ actionType: "shell_cmd",
1768
+ detail: `${cmd.slice(0, 120)} \u2192 exit ${exitCode} (${durationMs}ms)`,
1769
+ durationMs,
1770
+ success: exitCode === 0
1771
+ });
1772
+ }
1773
+ checkpoint(step, status) {
1774
+ this.log({ level: "info", actionType: "checkpoint", detail: `step ${step}: ${status}` });
1775
+ }
1776
+ error(actionType, detail) {
1777
+ this.log({ level: "error", actionType, detail });
1778
+ }
1779
+ close() {
1780
+ this.logStream?.end();
1781
+ }
1782
+ };
1783
+ }
1784
+ });
1785
+
1689
1786
  // src/agent/agent.ts
1690
1787
  import { readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
1691
1788
  import { execSync as execSync3 } from "child_process";
1692
1789
  import { basename } from "path";
1693
1790
  import DiffMatchPatch from "diff-match-patch";
1694
1791
  import { resolve as resolve3 } from "path";
1792
+ import { v4 as uuidv4 } from "uuid";
1695
1793
  import pc4 from "picocolors";
1696
1794
  var HEAD_PATTERN, DIVIDER_PATTERN, UPDATED_PATTERN, UDIFF_PATTERN, UDIFF_HUNK_PATTERN, REASONING_TAGS, Agent;
1697
1795
  var init_agent = __esm({
@@ -1705,6 +1803,7 @@ var init_agent = __esm({
1705
1803
  init_memory();
1706
1804
  init_reasoning();
1707
1805
  init_env();
1806
+ init_audit();
1708
1807
  HEAD_PATTERN = /^<{5,9} SEARCH>?\s*$/;
1709
1808
  DIVIDER_PATTERN = /^={5,9}\s*$/;
1710
1809
  UPDATED_PATTERN = /^>{5,9} REPLACE\s*$/;
@@ -1743,6 +1842,12 @@ var init_agent = __esm({
1743
1842
  3. SERIALIZED EXECUTION: Favor simple synchronous/serial patterns. Avoid complex parallel async logic.
1744
1843
  4. ROT RESISTANCE: Prefer Vanilla JS/TS over external dependencies. Match existing surgical style.
1745
1844
  `.trim();
1845
+ // Priority 1: Reproducibility + Observability
1846
+ runId;
1847
+ auditLogger;
1848
+ totalCostCents = 0;
1849
+ recentActions = [];
1850
+ // For loop detection
1746
1851
  constructor(config2) {
1747
1852
  this._model = config2.model;
1748
1853
  this._baseUrl = config2.baseUrl;
@@ -1751,6 +1856,10 @@ var init_agent = __esm({
1751
1856
  if (config2.files) {
1752
1857
  config2.files.forEach((f) => this.files.add(f));
1753
1858
  }
1859
+ this.runId = uuidv4();
1860
+ const meowDb = config2.db;
1861
+ meowDb.startRun(this.runId, "mission", config2.seed, config2.deterministic);
1862
+ this.auditLogger = new AuditLogger(this.runId);
1754
1863
  this.skillManager = new SkillManager();
1755
1864
  this.mcpManager = new McpManager();
1756
1865
  this.discoveryModule = new DiscoveryModule();
@@ -1760,6 +1869,16 @@ var init_agent = __esm({
1760
1869
  this.reasoningEngine = new ReasoningEngine();
1761
1870
  this.agenticMemory = new AgenticMemory(config2.db, config2.kernel, this.reasoningEngine);
1762
1871
  }
1872
+ // ── Priority 1: Budget enforcement ──────────────────────────────────────
1873
+ checkBudget() {
1874
+ const budget = config.budgetCents;
1875
+ if (budget && this.totalCostCents >= budget) {
1876
+ this.auditLogger.error("budget", `Budget exceeded: ${this.totalCostCents}\xA2 >= ${budget}\xA2`);
1877
+ const meowDb = this.db;
1878
+ meowDb.endRun(this.runId, "budget_exceeded");
1879
+ throw new Error(`Budget exceeded: ${this.totalCostCents.toFixed(4)}\xA2 >= ${budget}\xA2. Checkpoint saved.`);
1880
+ }
1881
+ }
1763
1882
  async chat(userInput, runTests = false, testCmd, onStatus) {
1764
1883
  this.messages.push({ role: "user", content: userInput });
1765
1884
  this.updateTokenEstimate();
@@ -1985,11 +2104,11 @@ Check MEOW manually at ${meowDir}`;
1985
2104
  }
1986
2105
  return result.trim();
1987
2106
  }
1988
- addFile(path3) {
1989
- this.files.add(path3);
2107
+ addFile(path4) {
2108
+ this.files.add(path4);
1990
2109
  }
1991
- dropFile(path3) {
1992
- this.files.delete(path3);
2110
+ dropFile(path4) {
2111
+ this.files.delete(path4);
1993
2112
  }
1994
2113
  getFiles() {
1995
2114
  return Array.from(this.files);
@@ -2004,11 +2123,26 @@ Check MEOW manually at ${meowDir}`;
2004
2123
  return this._apiKey;
2005
2124
  }
2006
2125
  async callLLM(systemPrompt, messages) {
2126
+ this.checkBudget();
2127
+ const actionSig = JSON.stringify(messages.slice(-2));
2128
+ this.recentActions.push(actionSig);
2129
+ if (this.recentActions.length > 3) this.recentActions.shift();
2130
+ const last3 = this.recentActions.slice(-3);
2131
+ if (last3.length === 3 && last3.every((a) => a === last3[0])) {
2132
+ this.auditLogger.log({ level: "warn", actionType: "loop_detected", detail: "Agent repeated same action 3\xD7 \u2014 flagging for review" });
2133
+ console.warn(pc4.yellow("\u26A0\uFE0F [LOOP DETECTED] Repeating the same action. Consider pivoting strategy."));
2134
+ }
2135
+ const startTime = Date.now();
2136
+ let inputTokens = 0;
2137
+ let outputTokens = 0;
2138
+ let costCents = 0;
2139
+ let success = false;
2140
+ let text2 = "";
2007
2141
  if (this._apiKey && this._baseUrl.includes("anthropic")) {
2008
- const url2 = this._baseUrl.endsWith("/v1/messages") ? this._baseUrl : `${this._baseUrl}/v1/messages`;
2009
- const controller2 = new AbortController();
2010
- const timeout2 = setTimeout(() => controller2.abort(), 12e4);
2011
- const response2 = await fetch(url2, {
2142
+ const url = this._baseUrl.endsWith("/v1/messages") ? this._baseUrl : `${this._baseUrl}/v1/messages`;
2143
+ const controller = new AbortController();
2144
+ const timeout = setTimeout(() => controller.abort(), 12e4);
2145
+ const response = await fetch(url, {
2012
2146
  method: "POST",
2013
2147
  headers: {
2014
2148
  "Content-Type": "application/json",
@@ -2021,45 +2155,65 @@ Check MEOW manually at ${meowDir}`;
2021
2155
  messages: messages.map((m) => ({ role: m.role, content: m.content })),
2022
2156
  max_tokens: 4096
2023
2157
  }),
2024
- signal: controller2.signal
2158
+ signal: controller.signal
2025
2159
  });
2026
- clearTimeout(timeout2);
2027
- if (!response2.ok) {
2028
- const error = await response2.text();
2029
- throw new Error(`Anthropic-compatible endpoint error: ${response2.status} - ${error}`);
2160
+ clearTimeout(timeout);
2161
+ if (!response.ok) {
2162
+ const error = await response.text();
2163
+ throw new Error(`Anthropic-compatible endpoint error: ${response.status} - ${error}`);
2030
2164
  }
2031
- const data2 = await response2.json();
2032
- const textBlock = data2.content?.find((c) => c.type === "text" && c.text);
2033
- const text2 = textBlock?.text || "";
2034
- return text2;
2035
- }
2036
- const fullMessages = [
2037
- { role: "system", content: systemPrompt },
2038
- ...messages
2039
- ];
2040
- const url = this._baseUrl.includes("/api/chat") ? this._baseUrl : `${this._baseUrl}/api/chat`;
2041
- const controller = new AbortController();
2042
- const timeout = setTimeout(() => controller.abort(), 3e4);
2043
- const response = await fetch(url, {
2044
- method: "POST",
2045
- headers: {
2046
- "Content-Type": "application/json",
2047
- ...this._apiKey ? { "Authorization": `Bearer ${this._apiKey}` } : {}
2048
- },
2049
- body: JSON.stringify({
2050
- model: this._model,
2051
- messages: fullMessages,
2052
- stream: false
2053
- }),
2054
- signal: controller.signal
2055
- });
2056
- clearTimeout(timeout);
2057
- if (!response.ok) {
2058
- const error = await response.text();
2059
- throw new Error(`LLM error: ${response.status} - ${error}`);
2165
+ const data = await response.json();
2166
+ inputTokens = data.usage?.input_tokens ?? 0;
2167
+ outputTokens = data.usage?.output_tokens ?? 0;
2168
+ const textBlock = data.content?.find((c) => c.type === "text" && c.text);
2169
+ text2 = textBlock?.text || "";
2170
+ success = true;
2171
+ } else {
2172
+ const fullMessages = [
2173
+ { role: "system", content: systemPrompt },
2174
+ ...messages
2175
+ ];
2176
+ const url = this._baseUrl.includes("/api/chat") ? this._baseUrl : `${this._baseUrl}/api/chat`;
2177
+ const controller = new AbortController();
2178
+ const timeout = setTimeout(() => controller.abort(), 3e4);
2179
+ const response = await fetch(url, {
2180
+ method: "POST",
2181
+ headers: {
2182
+ "Content-Type": "application/json",
2183
+ ...this._apiKey ? { "Authorization": `Bearer ${this._apiKey}` } : {}
2184
+ },
2185
+ body: JSON.stringify({
2186
+ model: this._model,
2187
+ messages: fullMessages,
2188
+ stream: false
2189
+ }),
2190
+ signal: controller.signal
2191
+ });
2192
+ clearTimeout(timeout);
2193
+ if (!response.ok) {
2194
+ const error = await response.text();
2195
+ throw new Error(`LLM error: ${response.status} - ${error}`);
2196
+ }
2197
+ const data = await response.json();
2198
+ text2 = data.message?.content || data.choices?.[0]?.message?.content || "";
2199
+ success = true;
2200
+ }
2201
+ const durationMs = Date.now() - startTime;
2202
+ if (inputTokens > 0 || outputTokens > 0) {
2203
+ costCents = inputTokens * 3e-3 / 1e6 + outputTokens * 0.015 / 1e6;
2204
+ this.totalCostCents += costCents;
2205
+ const meowDb = this.db;
2206
+ meowDb.logCost(this.runId, this._model, inputTokens, outputTokens, costCents);
2207
+ this.auditLogger.llmCall(this._model, inputTokens, outputTokens, durationMs, costCents, success);
2208
+ }
2209
+ if (costCents > 0) {
2210
+ const totalSoFar = this.totalCostCents;
2211
+ const budgetInfo = config.budgetCents ? ` / ${config.budgetCents}\xA2 budget` : "";
2212
+ process.stdout.write(pc4.dim(`
2213
+ \u{1F4B0} ${costCents.toFixed(4)}\xA2 (session total: ${totalSoFar.toFixed(4)}\xA2${budgetInfo})
2214
+ `));
2060
2215
  }
2061
- const data = await response.json();
2062
- return data.message?.content || data.choices?.[0]?.message?.content || "";
2216
+ return text2;
2063
2217
  }
2064
2218
  getBasePrompt() {
2065
2219
  return `You are an expert software developer.
@@ -3205,8 +3359,8 @@ var init_ParallelExecutor = __esm({
3205
3359
  void 0,
3206
3360
  (status) => this.taskEvents?.onProgress?.(task.id, status)
3207
3361
  );
3208
- const artifacts = agent.getEditedFiles().map((path3) => ({
3209
- path: path3,
3362
+ const artifacts = agent.getEditedFiles().map((path4) => ({
3363
+ path: path4,
3210
3364
  operation: "update"
3211
3365
  }));
3212
3366
  return {
@@ -3328,24 +3482,24 @@ var init_FileCoordinator = __esm({
3328
3482
  }
3329
3483
  return locks;
3330
3484
  }
3331
- async acquire(path3, taskId) {
3485
+ async acquire(path4, taskId) {
3332
3486
  if (!this.useSqlite || !this.db) {
3333
- const existing = this.inMemoryLocks.get(path3);
3487
+ const existing = this.inMemoryLocks.get(path4);
3334
3488
  if (existing && existing.taskId !== taskId) return false;
3335
- this.inMemoryLocks.set(path3, { path: path3, taskId, acquiredAt: Date.now() });
3489
+ this.inMemoryLocks.set(path4, { path: path4, taskId, acquiredAt: Date.now() });
3336
3490
  return true;
3337
3491
  }
3338
3492
  try {
3339
3493
  const stmt = this.db.prepare("BEGIN IMMEDIATE");
3340
3494
  stmt.run();
3341
- const row = this.db.prepare("SELECT task_id FROM file_locks WHERE path = ?").get(path3);
3495
+ const row = this.db.prepare("SELECT task_id FROM file_locks WHERE path = ?").get(path4);
3342
3496
  if (row && row.task_id !== taskId) {
3343
3497
  this.db.prepare("COMMIT").run();
3344
3498
  return false;
3345
3499
  }
3346
3500
  this.db.prepare(
3347
3501
  "INSERT OR REPLACE INTO file_locks (path, task_id, acquired_at) VALUES (?, ?, ?)"
3348
- ).run(path3, taskId, Date.now());
3502
+ ).run(path4, taskId, Date.now());
3349
3503
  this.db.prepare("COMMIT").run();
3350
3504
  return true;
3351
3505
  } catch {
@@ -3358,9 +3512,9 @@ var init_FileCoordinator = __esm({
3358
3512
  }
3359
3513
  release(taskId) {
3360
3514
  if (!this.useSqlite || !this.db) {
3361
- for (const [path3, lock] of this.inMemoryLocks) {
3515
+ for (const [path4, lock] of this.inMemoryLocks) {
3362
3516
  if (lock.taskId === taskId) {
3363
- this.inMemoryLocks.delete(path3);
3517
+ this.inMemoryLocks.delete(path4);
3364
3518
  }
3365
3519
  }
3366
3520
  return;
@@ -3368,9 +3522,9 @@ var init_FileCoordinator = __esm({
3368
3522
  try {
3369
3523
  this.db.prepare("DELETE FROM file_locks WHERE task_id = ?").run(taskId);
3370
3524
  } catch {
3371
- for (const [path3, lock] of this.getLocks()) {
3525
+ for (const [path4, lock] of this.getLocks()) {
3372
3526
  if (lock.taskId === taskId) {
3373
- this.inMemoryLocks.delete(path3);
3527
+ this.inMemoryLocks.delete(path4);
3374
3528
  }
3375
3529
  }
3376
3530
  }
@@ -3429,10 +3583,10 @@ var init_FileCoordinator = __esm({
3429
3583
  const now = Date.now();
3430
3584
  const stale = [];
3431
3585
  if (!this.useSqlite || !this.db) {
3432
- for (const [path3, lock] of this.inMemoryLocks) {
3586
+ for (const [path4, lock] of this.inMemoryLocks) {
3433
3587
  if (now - lock.acquiredAt > maxAgeMs) {
3434
- this.inMemoryLocks.delete(path3);
3435
- stale.push(path3);
3588
+ this.inMemoryLocks.delete(path4);
3589
+ stale.push(path4);
3436
3590
  }
3437
3591
  }
3438
3592
  return stale;
@@ -3445,10 +3599,10 @@ var init_FileCoordinator = __esm({
3445
3599
  this.db.prepare("DELETE FROM file_locks WHERE ? - acquired_at > ?").run(now, maxAgeMs);
3446
3600
  } catch {
3447
3601
  const locks = this.getLocks();
3448
- for (const [path3, lock] of locks) {
3602
+ for (const [path4, lock] of locks) {
3449
3603
  if (now - lock.acquiredAt > maxAgeMs) {
3450
- this.inMemoryLocks.delete(path3);
3451
- stale.push(path3);
3604
+ this.inMemoryLocks.delete(path4);
3605
+ stale.push(path4);
3452
3606
  }
3453
3607
  }
3454
3608
  }
@@ -3543,11 +3697,11 @@ var init_ResultAggregator = __esm({
3543
3697
  }
3544
3698
  }
3545
3699
  const resolution = /* @__PURE__ */ new Map();
3546
- for (const [path3] of fileStates) {
3547
- if (conflicts.has(path3)) {
3548
- resolution.set(path3, "conflict");
3700
+ for (const [path4] of fileStates) {
3701
+ if (conflicts.has(path4)) {
3702
+ resolution.set(path4, "conflict");
3549
3703
  } else {
3550
- resolution.set(path3, "use_latest");
3704
+ resolution.set(path4, "use_latest");
3551
3705
  }
3552
3706
  }
3553
3707
  return resolution;
@@ -4684,7 +4838,7 @@ var init_Orchestrator = __esm({
4684
4838
  if (stale.length > 0) {
4685
4839
  console.log(`[Orchestrator] Released ${stale.length} stale file locks: ${stale.join(", ")}`);
4686
4840
  }
4687
- for (const [path3, lock] of this.coordinator.getLockedFiles()) {
4841
+ for (const [path4, lock] of this.coordinator.getLockedFiles()) {
4688
4842
  this.coordinator.release(lock.taskId);
4689
4843
  }
4690
4844
  }
@@ -4781,8 +4935,8 @@ var init_extension = __esm({
4781
4935
  exec(sql) {
4782
4936
  return this.send("exec", [sql]);
4783
4937
  }
4784
- loadExtension(path3) {
4785
- return this.send("loadExtension", [path3]);
4938
+ loadExtension(path4) {
4939
+ return this.send("loadExtension", [path4]);
4786
4940
  }
4787
4941
  batch(actions) {
4788
4942
  return this.send("batch", [actions]);
@@ -4920,6 +5074,65 @@ var init_database = __esm({
4920
5074
  last_pulse DATETIME DEFAULT CURRENT_TIMESTAMP,
4921
5075
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
4922
5076
  );
5077
+ `);
5078
+ this.db.exec(`
5079
+ CREATE TABLE IF NOT EXISTS mission_runs (
5080
+ run_id TEXT PRIMARY KEY,
5081
+ mission_id TEXT NOT NULL,
5082
+ seed INTEGER,
5083
+ deterministic INTEGER DEFAULT 0,
5084
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
5085
+ completed_at DATETIME,
5086
+ status TEXT DEFAULT 'running',
5087
+ checkpoint_path TEXT
5088
+ );
5089
+ `);
5090
+ this.db.exec(`
5091
+ CREATE TABLE IF NOT EXISTS mission_cost (
5092
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
5093
+ run_id TEXT NOT NULL,
5094
+ model TEXT NOT NULL,
5095
+ input_tokens INTEGER DEFAULT 0,
5096
+ output_tokens INTEGER DEFAULT 0,
5097
+ cost_cents REAL DEFAULT 0,
5098
+ logged_at DATETIME DEFAULT CURRENT_TIMESTAMP,
5099
+ FOREIGN KEY (run_id) REFERENCES mission_runs(run_id)
5100
+ );
5101
+ CREATE INDEX IF NOT EXISTS idx_cost_run_id ON mission_cost(run_id);
5102
+ `);
5103
+ this.db.exec(`
5104
+ CREATE TABLE IF NOT EXISTS audit_log (
5105
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
5106
+ run_id TEXT,
5107
+ timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
5108
+ level TEXT DEFAULT 'info',
5109
+ action_type TEXT NOT NULL,
5110
+ detail TEXT,
5111
+ agent TEXT,
5112
+ mission_goal TEXT
5113
+ );
5114
+ CREATE INDEX IF NOT EXISTS idx_audit_run_id ON audit_log(run_id);
5115
+ CREATE INDEX IF NOT EXISTS idx_audit_timestamp ON audit_log(timestamp);
5116
+ `);
5117
+ this.db.exec(`
5118
+ CREATE TABLE IF NOT EXISTS episodic_memory (
5119
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
5120
+ session_id TEXT NOT NULL,
5121
+ summary TEXT NOT NULL,
5122
+ raw_content TEXT,
5123
+ relevance REAL DEFAULT 0.5,
5124
+ reinforced INTEGER DEFAULT 0,
5125
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
5126
+ );
5127
+ CREATE INDEX IF NOT EXISTS idx_episodic_session ON episodic_memory(session_id);
5128
+ `);
5129
+ this.db.exec(`
5130
+ CREATE TABLE IF NOT EXISTS agent_registry (
5131
+ agent_name TEXT PRIMARY KEY,
5132
+ capabilities TEXT,
5133
+ last_seen DATETIME DEFAULT CURRENT_TIMESTAMP,
5134
+ status TEXT DEFAULT 'available'
5135
+ );
4923
5136
  `);
4924
5137
  }
4925
5138
  // Implements DatabasePort
@@ -4971,8 +5184,8 @@ var init_database = __esm({
4971
5184
  transaction(actions);
4972
5185
  return { processed: actions.length, errors };
4973
5186
  }
4974
- async loadExtension(path3) {
4975
- this.db.loadExtension(path3);
5187
+ async loadExtension(path4) {
5188
+ this.db.loadExtension(path4);
4976
5189
  }
4977
5190
  async close() {
4978
5191
  this.db.close();
@@ -4981,6 +5194,59 @@ var init_database = __esm({
4981
5194
  getRawDb() {
4982
5195
  return this.db;
4983
5196
  }
5197
+ // ── Priority 1: Run / Cost helpers ──────────────────────────────────────
5198
+ startRun(runId, missionId, seed, deterministic) {
5199
+ this.db.prepare(`
5200
+ INSERT OR REPLACE INTO mission_runs (run_id, mission_id, seed, deterministic, status)
5201
+ VALUES (?, ?, ?, ?, 'running')
5202
+ `).run(runId, missionId, seed ?? null, deterministic ? 1 : 0);
5203
+ }
5204
+ endRun(runId, status = "completed") {
5205
+ this.db.prepare(`
5206
+ UPDATE mission_runs SET completed_at = CURRENT_TIMESTAMP, status = ? WHERE run_id = ?
5207
+ `).run(status, runId);
5208
+ }
5209
+ logCost(runId, model, inputTokens, outputTokens, costCents) {
5210
+ this.db.prepare(`
5211
+ INSERT INTO mission_cost (run_id, model, input_tokens, output_tokens, cost_cents)
5212
+ VALUES (?, ?, ?, ?, ?)
5213
+ `).run(runId, model, inputTokens, outputTokens, costCents);
5214
+ }
5215
+ getTotalCost(runId) {
5216
+ const row = this.db.prepare(
5217
+ "SELECT COALESCE(SUM(cost_cents), 0) as total FROM mission_cost WHERE run_id = ?"
5218
+ ).get(runId);
5219
+ return row?.total ?? 0;
5220
+ }
5221
+ // ── Priority 2: Audit log ────────────────────────────────────────────────
5222
+ audit(actionType, detail, level = "info", runId, agent, missionGoal) {
5223
+ this.db.prepare(`
5224
+ INSERT INTO audit_log (run_id, action_type, detail, level, agent, mission_goal)
5225
+ VALUES (?, ?, ?, ?, ?, ?)
5226
+ `).run(runId ?? null, actionType, detail, level, agent ?? null, missionGoal ?? null);
5227
+ }
5228
+ // ── Priority 2: Cross-session memory ─────────────────────────────────────
5229
+ storeEpisodic(sessionId, summary, rawContent, relevance = 0.5) {
5230
+ this.db.prepare(`
5231
+ INSERT INTO episodic_memory (session_id, summary, raw_content, relevance)
5232
+ VALUES (?, ?, ?, ?)
5233
+ `).run(sessionId, summary, rawContent ?? null, relevance);
5234
+ }
5235
+ getRecentEpisodic(limit = 10) {
5236
+ return this.db.prepare(`
5237
+ SELECT session_id, summary, relevance, created_at
5238
+ FROM episodic_memory
5239
+ ORDER BY created_at DESC, relevance DESC
5240
+ LIMIT ?
5241
+ `).all(limit);
5242
+ }
5243
+ // ── Priority 2: Agent registry ────────────────────────────────────────────
5244
+ registerAgent(name, capabilities) {
5245
+ this.db.prepare(`
5246
+ INSERT OR REPLACE INTO agent_registry (agent_name, capabilities, last_seen, status)
5247
+ VALUES (?, ?, CURRENT_TIMESTAMP, 'available')
5248
+ `).run(name, JSON.stringify(capabilities));
5249
+ }
4984
5250
  };
4985
5251
  }
4986
5252
  });
@@ -5036,8 +5302,8 @@ __export(tui_exports, {
5036
5302
  MeowTUI: () => MeowTUI
5037
5303
  });
5038
5304
  import * as blessed from "blessed";
5039
- import * as path2 from "path";
5040
- import * as fs2 from "fs";
5305
+ import * as path3 from "path";
5306
+ import * as fs3 from "fs";
5041
5307
  function stylize(open, text2) {
5042
5308
  return text2 ? `${open}${text2}{/}` : text2;
5043
5309
  }
@@ -5183,7 +5449,7 @@ var init_tui = __esm({
5183
5449
  // ── constructor ─────────────────────────────────────────────────────────────
5184
5450
  constructor(agent, screen2) {
5185
5451
  this.agent = agent;
5186
- this.historyPath = path2.join(process.env.HOME ?? "/tmp", ".meow", "history.txt");
5452
+ this.historyPath = path3.join(process.env.HOME ?? "/tmp", ".meow", "history.txt");
5187
5453
  this.screen = screen2 ?? blessed.screen({
5188
5454
  smartCSR: true,
5189
5455
  title: "MEOW",
@@ -5302,9 +5568,9 @@ var init_tui = __esm({
5302
5568
  this.history.push(cmd);
5303
5569
  this.historyIdx = -1;
5304
5570
  try {
5305
- const dir = path2.dirname(this.historyPath);
5306
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
5307
- fs2.appendFileSync(this.historyPath, cmd + "\n");
5571
+ const dir = path3.dirname(this.historyPath);
5572
+ if (!fs3.existsSync(dir)) fs3.mkdirSync(dir, { recursive: true });
5573
+ fs3.appendFileSync(this.historyPath, cmd + "\n");
5308
5574
  } catch {
5309
5575
  }
5310
5576
  }
@@ -5670,8 +5936,8 @@ function createRepl(agent) {
5670
5936
 
5671
5937
  // src/kernel/kernel.ts
5672
5938
  import pc9 from "picocolors";
5673
- import fs from "fs";
5674
- import path from "path";
5939
+ import fs2 from "fs";
5940
+ import path2 from "path";
5675
5941
  var MeowKernel = class {
5676
5942
  db;
5677
5943
  queue = [];
@@ -5698,13 +5964,13 @@ var MeowKernel = class {
5698
5964
  this.setupExitHandlers();
5699
5965
  }
5700
5966
  setupLogDirectory() {
5701
- const logDir = path.join(process.cwd(), ".meow", "logs");
5702
- if (!fs.existsSync(logDir)) {
5703
- fs.mkdirSync(logDir, { recursive: true });
5967
+ const logDir = path2.join(process.cwd(), ".meow", "logs");
5968
+ if (!fs2.existsSync(logDir)) {
5969
+ fs2.mkdirSync(logDir, { recursive: true });
5704
5970
  }
5705
5971
  this.logDir = logDir;
5706
- this.logFile = path.join(logDir, `meow-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
5707
- this.logStream = fs.createWriteStream(this.logFile, { flags: "a" });
5972
+ this.logFile = path2.join(logDir, `meow-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}.log`);
5973
+ this.logStream = fs2.createWriteStream(this.logFile, { flags: "a" });
5708
5974
  this.log(pc9.cyan("\u{1F680} Meow Kernel initialized"), "KERNEL");
5709
5975
  }
5710
5976
  logDir = "";
@@ -5975,6 +6241,22 @@ async function main() {
5975
6241
  }
5976
6242
  const kernel = new MeowKernel(db);
5977
6243
  kernel.start();
6244
+ const continueMode = process.argv.includes("--continue") || process.argv.includes("-c");
6245
+ if (continueMode) {
6246
+ const { MeowDatabase: MeowDatabase2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6247
+ const meowDb = db;
6248
+ const recent = meowDb.getRawDb().prepare(`
6249
+ SELECT run_id FROM mission_runs
6250
+ WHERE status = 'running'
6251
+ ORDER BY created_at DESC LIMIT 1
6252
+ `).get();
6253
+ if (recent) {
6254
+ console.log(`\u21BB Continuing run: ${recent.run_id}`);
6255
+ process.env.MEOW_RUN_ID = recent.run_id;
6256
+ } else {
6257
+ console.log("\u21BB No previous run found. Starting fresh.");
6258
+ }
6259
+ }
5978
6260
  const agent = new Agent({
5979
6261
  model: config.model,
5980
6262
  baseUrl: config.baseUrl,
@@ -5989,12 +6271,25 @@ async function main() {
5989
6271
  console.error('Usage: meow -p "<task description>"');
5990
6272
  process.exit(1);
5991
6273
  }
5992
- console.log(`\u{1F916} [MEOW] Plan mode: ${command2}`);
6274
+ if (config.deterministic || config.seed !== void 0) {
6275
+ const seedInfo = config.seed !== void 0 ? ` seed=${config.seed}` : " deterministic";
6276
+ console.log(`\u{1F916} [meow] Plan mode${seedInfo}: ${command2}`);
6277
+ } else {
6278
+ console.log(`\u{1F916} [meow] Plan mode: ${command2}`);
6279
+ }
5993
6280
  const response = await agent.chat(command2, false, void 0, (status) => {
5994
6281
  process.stdout.write(`\r${status}`);
5995
6282
  });
5996
6283
  console.log("\n" + response);
5997
- console.log("\n\u2705 Command completed.");
6284
+ const { MeowDatabase: MeowDatabase2 } = await Promise.resolve().then(() => (init_database(), database_exports));
6285
+ const meowDb = db;
6286
+ const totalCost = meowDb.getTotalCost(agent.runId);
6287
+ if (totalCost > 0) {
6288
+ const budgetInfo = config.budgetCents ? ` (budget: ${config.budgetCents}\xA2)` : "";
6289
+ console.log(`
6290
+ \u{1F4B0} Total cost: ${totalCost.toFixed(4)}\xA2${budgetInfo}`);
6291
+ }
6292
+ meowDb.endRun(agent.runId, "completed");
5998
6293
  await kernel.shutdown();
5999
6294
  process.exit(0);
6000
6295
  return;
@@ -6007,7 +6302,7 @@ async function main() {
6007
6302
  }
6008
6303
  const command = process.argv.filter((arg) => !arg.startsWith("--")).slice(2).join(" ");
6009
6304
  if (command) {
6010
- console.log(`\u{1F916} [MEOW] Executing command: ${command}`);
6305
+ console.log(`\u{1F916} [meow] Executing command: ${command}`);
6011
6306
  const response = await agent.chat(command, false, void 0, (status) => {
6012
6307
  process.stdout.write(`\r${status}`);
6013
6308
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "meow-swarm",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "meow -p: Autonomous multi-agent coding harness. Run coding agents in background, checkpoint state, TUI dashboard.",
6
6
  "main": "dist/index.js",
@@ -33,7 +33,8 @@
33
33
  "js-yaml": "4.1.1",
34
34
  "mathjs": "^15.2.0",
35
35
  "picocolors": "1.1.1",
36
- "sqlite-vec": "^0.1.9"
36
+ "sqlite-vec": "^0.1.9",
37
+ "uuid": "^11.0.0"
37
38
  },
38
39
  "devDependencies": {
39
40
  "@types/better-sqlite3": "7.6.13",