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.
- package/dist/index.js +389 -94
- 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
|
|
255
|
+
for (const path4 of globalPaths) {
|
|
238
256
|
try {
|
|
239
|
-
await access2(
|
|
240
|
-
const patterns = [join(
|
|
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 (
|
|
1117
|
-
return await readFile4(
|
|
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 [
|
|
1125
|
-
await writeFile2(
|
|
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(
|
|
1989
|
-
this.files.add(
|
|
2107
|
+
addFile(path4) {
|
|
2108
|
+
this.files.add(path4);
|
|
1990
2109
|
}
|
|
1991
|
-
dropFile(
|
|
1992
|
-
this.files.delete(
|
|
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
|
|
2009
|
-
const
|
|
2010
|
-
const
|
|
2011
|
-
const
|
|
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:
|
|
2158
|
+
signal: controller.signal
|
|
2025
2159
|
});
|
|
2026
|
-
clearTimeout(
|
|
2027
|
-
if (!
|
|
2028
|
-
const error = await
|
|
2029
|
-
throw new Error(`Anthropic-compatible endpoint 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
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
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
|
-
|
|
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((
|
|
3209
|
-
path:
|
|
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(
|
|
3485
|
+
async acquire(path4, taskId) {
|
|
3332
3486
|
if (!this.useSqlite || !this.db) {
|
|
3333
|
-
const existing = this.inMemoryLocks.get(
|
|
3487
|
+
const existing = this.inMemoryLocks.get(path4);
|
|
3334
3488
|
if (existing && existing.taskId !== taskId) return false;
|
|
3335
|
-
this.inMemoryLocks.set(
|
|
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(
|
|
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(
|
|
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 [
|
|
3515
|
+
for (const [path4, lock] of this.inMemoryLocks) {
|
|
3362
3516
|
if (lock.taskId === taskId) {
|
|
3363
|
-
this.inMemoryLocks.delete(
|
|
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 [
|
|
3525
|
+
for (const [path4, lock] of this.getLocks()) {
|
|
3372
3526
|
if (lock.taskId === taskId) {
|
|
3373
|
-
this.inMemoryLocks.delete(
|
|
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 [
|
|
3586
|
+
for (const [path4, lock] of this.inMemoryLocks) {
|
|
3433
3587
|
if (now - lock.acquiredAt > maxAgeMs) {
|
|
3434
|
-
this.inMemoryLocks.delete(
|
|
3435
|
-
stale.push(
|
|
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 [
|
|
3602
|
+
for (const [path4, lock] of locks) {
|
|
3449
3603
|
if (now - lock.acquiredAt > maxAgeMs) {
|
|
3450
|
-
this.inMemoryLocks.delete(
|
|
3451
|
-
stale.push(
|
|
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 [
|
|
3547
|
-
if (conflicts.has(
|
|
3548
|
-
resolution.set(
|
|
3700
|
+
for (const [path4] of fileStates) {
|
|
3701
|
+
if (conflicts.has(path4)) {
|
|
3702
|
+
resolution.set(path4, "conflict");
|
|
3549
3703
|
} else {
|
|
3550
|
-
resolution.set(
|
|
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 [
|
|
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(
|
|
4785
|
-
return this.send("loadExtension", [
|
|
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(
|
|
4975
|
-
this.db.loadExtension(
|
|
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
|
|
5040
|
-
import * as
|
|
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 =
|
|
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 =
|
|
5306
|
-
if (!
|
|
5307
|
-
|
|
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
|
|
5674
|
-
import
|
|
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 =
|
|
5702
|
-
if (!
|
|
5703
|
-
|
|
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 =
|
|
5707
|
-
this.logStream =
|
|
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
|
-
|
|
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
|
-
|
|
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} [
|
|
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.
|
|
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",
|