aoaoe 0.1.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 +250 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +149 -0
- package/dist/config.js.map +1 -0
- package/dist/dashboard.d.ts +4 -0
- package/dist/dashboard.d.ts.map +1 -0
- package/dist/dashboard.js +49 -0
- package/dist/dashboard.js.map +1 -0
- package/dist/executor.d.ts +26 -0
- package/dist/executor.d.ts.map +1 -0
- package/dist/executor.js +138 -0
- package/dist/executor.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +153 -0
- package/dist/index.js.map +1 -0
- package/dist/poller.d.ts +14 -0
- package/dist/poller.d.ts.map +1 -0
- package/dist/poller.js +167 -0
- package/dist/poller.js.map +1 -0
- package/dist/reasoner/claude-code.d.ts +15 -0
- package/dist/reasoner/claude-code.d.ts.map +1 -0
- package/dist/reasoner/claude-code.js +119 -0
- package/dist/reasoner/claude-code.js.map +1 -0
- package/dist/reasoner/index.d.ts +5 -0
- package/dist/reasoner/index.d.ts.map +1 -0
- package/dist/reasoner/index.js +15 -0
- package/dist/reasoner/index.js.map +1 -0
- package/dist/reasoner/opencode.d.ts +17 -0
- package/dist/reasoner/opencode.d.ts.map +1 -0
- package/dist/reasoner/opencode.js +193 -0
- package/dist/reasoner/opencode.js.map +1 -0
- package/dist/reasoner/prompt.d.ts +4 -0
- package/dist/reasoner/prompt.d.ts.map +1 -0
- package/dist/reasoner/prompt.js +68 -0
- package/dist/reasoner/prompt.js.map +1 -0
- package/dist/shell.d.ts +8 -0
- package/dist/shell.d.ts.map +1 -0
- package/dist/shell.js +27 -0
- package/dist/shell.js.map +1 -0
- package/dist/types.d.ts +85 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +54 -0
package/dist/executor.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { exec, execQuiet } from "./shell.js";
|
|
2
|
+
import { appendFileSync, mkdirSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
const LOG_DIR = join(homedir(), ".aoaoe");
|
|
6
|
+
const LOG_FILE = join(LOG_DIR, "actions.log");
|
|
7
|
+
export class Executor {
|
|
8
|
+
config;
|
|
9
|
+
actionLog = [];
|
|
10
|
+
recentActions = new Map(); // session -> last action timestamp
|
|
11
|
+
constructor(config) {
|
|
12
|
+
this.config = config;
|
|
13
|
+
// ensure log dir exists
|
|
14
|
+
try {
|
|
15
|
+
mkdirSync(LOG_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
}
|
|
19
|
+
async execute(actions, snapshots) {
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const action of actions) {
|
|
22
|
+
// rate limit: don't hammer the same session
|
|
23
|
+
if ("session" in action && action.session) {
|
|
24
|
+
if (this.isRateLimited(action.session)) {
|
|
25
|
+
const entry = this.logAction(action, false, "rate limited (too soon)");
|
|
26
|
+
results.push(entry);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const entry = await this.executeOne(action, snapshots);
|
|
31
|
+
results.push(entry);
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
async executeOne(action, snapshots) {
|
|
36
|
+
switch (action.action) {
|
|
37
|
+
case "send_input":
|
|
38
|
+
return this.sendInput(action.session, action.text, snapshots);
|
|
39
|
+
case "start_session":
|
|
40
|
+
return this.startSession(action.session);
|
|
41
|
+
case "stop_session":
|
|
42
|
+
return this.stopSession(action.session);
|
|
43
|
+
case "create_agent":
|
|
44
|
+
return this.createAgent(action.path, action.title, action.tool);
|
|
45
|
+
case "remove_agent":
|
|
46
|
+
return this.removeAgent(action.session);
|
|
47
|
+
case "wait":
|
|
48
|
+
return this.logAction(action, true, action.reason ?? "no action needed");
|
|
49
|
+
default:
|
|
50
|
+
return this.logAction(action, false, "unknown action type");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async sendInput(sessionId, text, snapshots) {
|
|
54
|
+
// resolve tmux session name from session ID
|
|
55
|
+
const tmuxName = this.resolveTmuxName(sessionId, snapshots);
|
|
56
|
+
if (!tmuxName) {
|
|
57
|
+
return this.logAction({ action: "send_input", session: sessionId, text }, false, `could not resolve tmux name for session ${sessionId}`);
|
|
58
|
+
}
|
|
59
|
+
// safety: refuse empty or whitespace-only input
|
|
60
|
+
if (!text.trim()) {
|
|
61
|
+
return this.logAction({ action: "send_input", session: sessionId, text }, false, "refusing to send empty input");
|
|
62
|
+
}
|
|
63
|
+
// tmux send-keys sends literal text then Enter
|
|
64
|
+
const ok = await execQuiet("tmux", ["send-keys", "-t", tmuxName, text, "Enter"]);
|
|
65
|
+
this.markAction(sessionId);
|
|
66
|
+
return this.logAction({ action: "send_input", session: sessionId, text }, ok, ok ? `sent to ${tmuxName}` : `send-keys failed for ${tmuxName}`);
|
|
67
|
+
}
|
|
68
|
+
async startSession(sessionId) {
|
|
69
|
+
const result = await exec("aoe", ["session", "start", sessionId]);
|
|
70
|
+
this.markAction(sessionId);
|
|
71
|
+
return this.logAction({ action: "start_session", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "started" : result.stderr.trim());
|
|
72
|
+
}
|
|
73
|
+
async stopSession(sessionId) {
|
|
74
|
+
const result = await exec("aoe", ["session", "stop", sessionId]);
|
|
75
|
+
this.markAction(sessionId);
|
|
76
|
+
return this.logAction({ action: "stop_session", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "stopped" : result.stderr.trim());
|
|
77
|
+
}
|
|
78
|
+
async createAgent(path, title, tool) {
|
|
79
|
+
const args = ["add", path, "-t", title, "-c", tool, "-y"]; // -y for yolo mode
|
|
80
|
+
const result = await exec("aoe", args);
|
|
81
|
+
return this.logAction({ action: "create_agent", path, title, tool }, result.exitCode === 0, result.exitCode === 0 ? "created" : result.stderr.trim());
|
|
82
|
+
}
|
|
83
|
+
async removeAgent(sessionId) {
|
|
84
|
+
const result = await exec("aoe", ["remove", sessionId, "-y"]);
|
|
85
|
+
this.markAction(sessionId);
|
|
86
|
+
return this.logAction({ action: "remove_agent", session: sessionId }, result.exitCode === 0, result.exitCode === 0 ? "removed" : result.stderr.trim());
|
|
87
|
+
}
|
|
88
|
+
resolveTmuxName(sessionId, snapshots) {
|
|
89
|
+
// try exact match first
|
|
90
|
+
const exact = snapshots.find((s) => s.session.id === sessionId);
|
|
91
|
+
if (exact?.session.tmux_name)
|
|
92
|
+
return exact.session.tmux_name;
|
|
93
|
+
// try prefix match (reasoner might return truncated IDs)
|
|
94
|
+
const prefix = snapshots.find((s) => s.session.id.startsWith(sessionId));
|
|
95
|
+
if (prefix?.session.tmux_name)
|
|
96
|
+
return prefix.session.tmux_name;
|
|
97
|
+
// try title match
|
|
98
|
+
const byTitle = snapshots.find((s) => s.session.title.toLowerCase() === sessionId.toLowerCase());
|
|
99
|
+
if (byTitle?.session.tmux_name)
|
|
100
|
+
return byTitle.session.tmux_name;
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
// rate limiting: don't act on the same session more than once per 30s
|
|
104
|
+
isRateLimited(sessionId) {
|
|
105
|
+
const last = this.recentActions.get(sessionId);
|
|
106
|
+
if (!last)
|
|
107
|
+
return false;
|
|
108
|
+
return Date.now() - last < 30_000;
|
|
109
|
+
}
|
|
110
|
+
markAction(sessionId) {
|
|
111
|
+
this.recentActions.set(sessionId, Date.now());
|
|
112
|
+
}
|
|
113
|
+
logAction(action, success, detail) {
|
|
114
|
+
const entry = {
|
|
115
|
+
timestamp: Date.now(),
|
|
116
|
+
action,
|
|
117
|
+
success,
|
|
118
|
+
detail,
|
|
119
|
+
};
|
|
120
|
+
this.actionLog.push(entry);
|
|
121
|
+
// keep in-memory log bounded
|
|
122
|
+
if (this.actionLog.length > 1000) {
|
|
123
|
+
this.actionLog = this.actionLog.slice(-500);
|
|
124
|
+
}
|
|
125
|
+
// persist to ~/.aoaoe/actions.log (JSONL, one entry per line)
|
|
126
|
+
if (action.action !== "wait") {
|
|
127
|
+
try {
|
|
128
|
+
appendFileSync(LOG_FILE, JSON.stringify(entry) + "\n");
|
|
129
|
+
}
|
|
130
|
+
catch { } // best-effort, don't crash the daemon
|
|
131
|
+
}
|
|
132
|
+
return entry;
|
|
133
|
+
}
|
|
134
|
+
getRecentLog(n = 20) {
|
|
135
|
+
return this.actionLog.slice(-n);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { loadConfig, validateEnvironment, parseCliArgs, printHelp } from "./config.js";
|
|
3
|
+
import { Poller } from "./poller.js";
|
|
4
|
+
import { createReasoner } from "./reasoner/index.js";
|
|
5
|
+
import { Executor } from "./executor.js";
|
|
6
|
+
import { printDashboard } from "./dashboard.js";
|
|
7
|
+
import { readFileSync } from "node:fs";
|
|
8
|
+
import { resolve, dirname } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
11
|
+
async function main() {
|
|
12
|
+
const { overrides, help, version } = parseCliArgs(process.argv);
|
|
13
|
+
if (help) {
|
|
14
|
+
printHelp();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
}
|
|
17
|
+
if (version) {
|
|
18
|
+
try {
|
|
19
|
+
const pkg = JSON.parse(readFileSync(resolve(__dirname, "..", "package.json"), "utf-8"));
|
|
20
|
+
console.log(`aoaoe ${pkg.version}`);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
console.log("aoaoe (version unknown)");
|
|
24
|
+
}
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
const config = loadConfig(overrides);
|
|
28
|
+
log("starting aoaoe supervisor");
|
|
29
|
+
log(`reasoner: ${config.reasoner}`);
|
|
30
|
+
log(`poll interval: ${config.pollIntervalMs}ms`);
|
|
31
|
+
if (config.dryRun)
|
|
32
|
+
log("DRY RUN -- will observe and reason but not execute");
|
|
33
|
+
// validate tools are installed
|
|
34
|
+
await validateEnvironment(config);
|
|
35
|
+
const poller = new Poller(config);
|
|
36
|
+
const reasoner = createReasoner(config);
|
|
37
|
+
const executor = new Executor(config);
|
|
38
|
+
// init reasoner (starts opencode serve, verifies claude, etc)
|
|
39
|
+
log("initializing reasoner...");
|
|
40
|
+
await reasoner.init();
|
|
41
|
+
log("reasoner ready");
|
|
42
|
+
// graceful shutdown
|
|
43
|
+
let running = true;
|
|
44
|
+
const shutdown = async () => {
|
|
45
|
+
if (!running)
|
|
46
|
+
return;
|
|
47
|
+
running = false;
|
|
48
|
+
log("shutting down...");
|
|
49
|
+
await reasoner.shutdown();
|
|
50
|
+
process.exit(0);
|
|
51
|
+
};
|
|
52
|
+
process.on("SIGINT", shutdown);
|
|
53
|
+
process.on("SIGTERM", shutdown);
|
|
54
|
+
// main loop
|
|
55
|
+
let pollCount = 0;
|
|
56
|
+
log("entering main loop (Ctrl+C to stop)\n");
|
|
57
|
+
while (running) {
|
|
58
|
+
pollCount++;
|
|
59
|
+
try {
|
|
60
|
+
await tick(config, poller, reasoner, executor, pollCount);
|
|
61
|
+
}
|
|
62
|
+
catch (err) {
|
|
63
|
+
console.error(`[error] tick ${pollCount} failed: ${err}`);
|
|
64
|
+
}
|
|
65
|
+
if (running) {
|
|
66
|
+
await sleep(config.pollIntervalMs);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function tick(config, poller, reasoner, executor, pollCount) {
|
|
71
|
+
// 1. poll
|
|
72
|
+
const observation = await poller.poll();
|
|
73
|
+
const sessionCount = observation.sessions.length;
|
|
74
|
+
const changeCount = observation.changes.length;
|
|
75
|
+
if (sessionCount === 0) {
|
|
76
|
+
if (pollCount % 6 === 1) {
|
|
77
|
+
// log every ~60s when no sessions
|
|
78
|
+
log("no active aoe sessions found");
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
// dashboard every 6 polls (~60s at default interval)
|
|
83
|
+
if (pollCount % 6 === 1) {
|
|
84
|
+
printDashboard(observation, executor.getRecentLog(), pollCount, config);
|
|
85
|
+
}
|
|
86
|
+
// status line
|
|
87
|
+
const statuses = summarizeStatuses(observation);
|
|
88
|
+
process.stdout.write(`\r[poll #${pollCount}] ${sessionCount} sessions (${statuses}) | ${changeCount} changed`);
|
|
89
|
+
// 2. if nothing changed, skip reasoning (save tokens)
|
|
90
|
+
if (changeCount === 0) {
|
|
91
|
+
if (config.verbose) {
|
|
92
|
+
process.stdout.write(" | no changes, skipping reasoner\n");
|
|
93
|
+
}
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
process.stdout.write(" | reasoning...");
|
|
97
|
+
// 3. reason (with timeout to avoid blocking the loop)
|
|
98
|
+
const result = await withTimeout(reasoner.decide(observation), 90_000, // 90s max for a single reasoning call
|
|
99
|
+
{ actions: [{ action: "wait", reason: "reasoner timeout" }] });
|
|
100
|
+
const actionSummary = result.actions.map((a) => a.action).join(", ");
|
|
101
|
+
process.stdout.write(` -> ${actionSummary}\n`);
|
|
102
|
+
if (result.reasoning && config.verbose) {
|
|
103
|
+
log(`reasoning: ${result.reasoning}`);
|
|
104
|
+
}
|
|
105
|
+
// 4. execute (skip if all actions are "wait")
|
|
106
|
+
const nonWaitActions = result.actions.filter((a) => a.action !== "wait");
|
|
107
|
+
if (nonWaitActions.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// dry-run: log what would happen, don't actually execute
|
|
111
|
+
if (config.dryRun) {
|
|
112
|
+
for (const action of nonWaitActions) {
|
|
113
|
+
log(`[dry-run] would ${action.action}: ${JSON.stringify(action)}`);
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const entries = await executor.execute(result.actions, observation.sessions);
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (entry.action.action === "wait")
|
|
120
|
+
continue;
|
|
121
|
+
const icon = entry.success ? "+" : "!";
|
|
122
|
+
log(`[${icon}] ${entry.action.action}: ${entry.detail}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function summarizeStatuses(obs) {
|
|
126
|
+
const counts = new Map();
|
|
127
|
+
for (const snap of obs.sessions) {
|
|
128
|
+
const s = snap.session.status;
|
|
129
|
+
counts.set(s, (counts.get(s) ?? 0) + 1);
|
|
130
|
+
}
|
|
131
|
+
return [...counts.entries()].map(([k, v]) => `${v} ${k}`).join(", ");
|
|
132
|
+
}
|
|
133
|
+
function log(msg) {
|
|
134
|
+
const ts = new Date().toLocaleTimeString();
|
|
135
|
+
console.error(`[${ts}] ${msg}`);
|
|
136
|
+
}
|
|
137
|
+
function sleep(ms) {
|
|
138
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
139
|
+
}
|
|
140
|
+
function withTimeout(promise, ms, fallback) {
|
|
141
|
+
return Promise.race([
|
|
142
|
+
promise,
|
|
143
|
+
new Promise((resolve) => setTimeout(() => {
|
|
144
|
+
log(`reasoner timed out after ${ms}ms, using fallback`);
|
|
145
|
+
resolve(fallback);
|
|
146
|
+
}, ms)),
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
main().catch((err) => {
|
|
150
|
+
console.error(`fatal: ${err}`);
|
|
151
|
+
process.exit(1);
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/poller.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { AoaoeConfig, Observation } from "./types.js";
|
|
2
|
+
export declare class Poller {
|
|
3
|
+
private config;
|
|
4
|
+
private previousSnapshots;
|
|
5
|
+
constructor(config: AoaoeConfig);
|
|
6
|
+
poll(): Promise<Observation>;
|
|
7
|
+
private listSessions;
|
|
8
|
+
private getSessionStatus;
|
|
9
|
+
private captureSession;
|
|
10
|
+
private captureTmuxPane;
|
|
11
|
+
private diffSnapshots;
|
|
12
|
+
private log;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=poller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poller.d.ts","sourceRoot":"","sources":["../src/poller.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,WAAW,EAIX,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,MAAM;IACjB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,iBAAiB,CAA2C;gBAExD,MAAM,EAAE,WAAW;IAIzB,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;YAsBpB,YAAY;YAwCZ,gBAAgB;YAWhB,cAAc;YAYd,eAAe;IAqB7B,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,GAAG;CAKZ"}
|
package/dist/poller.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { exec } from "./shell.js";
|
|
3
|
+
export class Poller {
|
|
4
|
+
config;
|
|
5
|
+
previousSnapshots = new Map();
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
async poll() {
|
|
10
|
+
const sessions = await this.listSessions();
|
|
11
|
+
const snapshots = await Promise.all(sessions.map((s) => this.captureSession(s)));
|
|
12
|
+
const changes = this.diffSnapshots(snapshots);
|
|
13
|
+
const observation = {
|
|
14
|
+
timestamp: Date.now(),
|
|
15
|
+
sessions: snapshots,
|
|
16
|
+
changes,
|
|
17
|
+
};
|
|
18
|
+
// store current snapshots for next diff
|
|
19
|
+
this.previousSnapshots.clear();
|
|
20
|
+
for (const snap of snapshots) {
|
|
21
|
+
this.previousSnapshots.set(snap.session.id, snap);
|
|
22
|
+
}
|
|
23
|
+
return observation;
|
|
24
|
+
}
|
|
25
|
+
async listSessions() {
|
|
26
|
+
// aoe list --json returns array with id, title, path, tool, command, profile, created_at
|
|
27
|
+
// does NOT include status or tmux_name -- we derive both
|
|
28
|
+
const result = await exec("aoe", ["list", "--json"]);
|
|
29
|
+
if (result.exitCode !== 0) {
|
|
30
|
+
this.log(`aoe list failed: ${result.stderr}`);
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
let raw;
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(result.stdout);
|
|
36
|
+
raw = Array.isArray(parsed) ? parsed : [];
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
this.log(`failed to parse aoe list output: ${e}`);
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
// fetch per-session status in parallel
|
|
43
|
+
const sessions = await Promise.all(raw.map(async (s) => {
|
|
44
|
+
const id = String(s.id ?? "");
|
|
45
|
+
const title = String(s.title ?? "");
|
|
46
|
+
const status = await this.getSessionStatus(id);
|
|
47
|
+
return {
|
|
48
|
+
id,
|
|
49
|
+
title,
|
|
50
|
+
path: String(s.path ?? ""),
|
|
51
|
+
tool: String(s.tool ?? ""),
|
|
52
|
+
status,
|
|
53
|
+
tmux_name: computeTmuxName(id, title),
|
|
54
|
+
group: s.group ? String(s.group) : undefined,
|
|
55
|
+
created_at: s.created_at ? String(s.created_at) : undefined,
|
|
56
|
+
};
|
|
57
|
+
}));
|
|
58
|
+
return sessions;
|
|
59
|
+
}
|
|
60
|
+
async getSessionStatus(id) {
|
|
61
|
+
const result = await exec("aoe", ["session", "show", id, "--json"]);
|
|
62
|
+
if (result.exitCode !== 0)
|
|
63
|
+
return "unknown";
|
|
64
|
+
try {
|
|
65
|
+
const data = JSON.parse(result.stdout);
|
|
66
|
+
return String(data.status ?? "unknown");
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return "unknown";
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async captureSession(session) {
|
|
73
|
+
const output = await this.captureTmuxPane(session.tmux_name);
|
|
74
|
+
const outputHash = quickHash(output);
|
|
75
|
+
return {
|
|
76
|
+
session,
|
|
77
|
+
output,
|
|
78
|
+
outputHash,
|
|
79
|
+
capturedAt: Date.now(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
async captureTmuxPane(tmuxName) {
|
|
83
|
+
if (!tmuxName)
|
|
84
|
+
return "";
|
|
85
|
+
const result = await exec("tmux", [
|
|
86
|
+
"capture-pane",
|
|
87
|
+
"-t",
|
|
88
|
+
tmuxName,
|
|
89
|
+
"-p", // print to stdout
|
|
90
|
+
"-S",
|
|
91
|
+
`-${this.config.captureLinesCount}`, // last N lines
|
|
92
|
+
]);
|
|
93
|
+
if (result.exitCode !== 0) {
|
|
94
|
+
// session might not exist in tmux (stopped, etc)
|
|
95
|
+
return "";
|
|
96
|
+
}
|
|
97
|
+
// trim trailing blank lines that tmux pads
|
|
98
|
+
return result.stdout.replace(/\n+$/, "");
|
|
99
|
+
}
|
|
100
|
+
diffSnapshots(current) {
|
|
101
|
+
const changes = [];
|
|
102
|
+
for (const snap of current) {
|
|
103
|
+
const prev = this.previousSnapshots.get(snap.session.id);
|
|
104
|
+
// no previous snapshot = first poll, skip to avoid spamming the reasoner
|
|
105
|
+
// with all existing output on startup
|
|
106
|
+
if (!prev)
|
|
107
|
+
continue;
|
|
108
|
+
// same hash = no change
|
|
109
|
+
if (snap.outputHash === prev.outputHash)
|
|
110
|
+
continue;
|
|
111
|
+
// find new lines by diffing output
|
|
112
|
+
const newLines = extractNewLines(prev.output, snap.output);
|
|
113
|
+
if (newLines.trim()) {
|
|
114
|
+
changes.push({
|
|
115
|
+
sessionId: snap.session.id,
|
|
116
|
+
title: snap.session.title,
|
|
117
|
+
tool: snap.session.tool,
|
|
118
|
+
status: snap.session.status,
|
|
119
|
+
newLines,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return changes;
|
|
124
|
+
}
|
|
125
|
+
log(msg) {
|
|
126
|
+
if (this.config.verbose) {
|
|
127
|
+
console.error(`[poller] ${msg}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// replicate AoE tmux naming: aoe_<sanitized_title_max20>_<first8_of_id>
|
|
132
|
+
// from agent-of-empires/src/tmux/session.rs:22-25
|
|
133
|
+
function computeTmuxName(id, title) {
|
|
134
|
+
const safeTitle = sanitizeTmuxName(title);
|
|
135
|
+
const shortId = id.slice(0, 8);
|
|
136
|
+
return `aoe_${safeTitle}_${shortId}`;
|
|
137
|
+
}
|
|
138
|
+
// AoE sanitization: only [a-zA-Z0-9_-], max 20 chars, everything else -> _
|
|
139
|
+
// from agent-of-empires/src/tmux/session.rs:220-231
|
|
140
|
+
function sanitizeTmuxName(name) {
|
|
141
|
+
const sanitized = name.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
142
|
+
return sanitized.slice(0, 20);
|
|
143
|
+
}
|
|
144
|
+
function quickHash(s) {
|
|
145
|
+
return createHash("sha256").update(s).digest("hex").slice(0, 16);
|
|
146
|
+
}
|
|
147
|
+
// extract lines in `current` that weren't in `previous`
|
|
148
|
+
function extractNewLines(previous, current) {
|
|
149
|
+
const prevLines = previous.split("\n");
|
|
150
|
+
const currLines = current.split("\n");
|
|
151
|
+
if (prevLines.length === 0)
|
|
152
|
+
return current;
|
|
153
|
+
// look for the last few non-empty lines of previous in current to find the overlap point
|
|
154
|
+
const anchorLines = prevLines.filter((l) => l.trim()).slice(-5);
|
|
155
|
+
if (anchorLines.length === 0)
|
|
156
|
+
return current;
|
|
157
|
+
const anchor = anchorLines.join("\n");
|
|
158
|
+
const currJoined = currLines.join("\n");
|
|
159
|
+
const anchorIdx = currJoined.lastIndexOf(anchor);
|
|
160
|
+
if (anchorIdx >= 0) {
|
|
161
|
+
const after = currJoined.slice(anchorIdx + anchor.length);
|
|
162
|
+
return after.replace(/^\n/, "");
|
|
163
|
+
}
|
|
164
|
+
// no overlap found (screen clear, etc) -- return last 20 lines
|
|
165
|
+
return currLines.slice(-20).join("\n");
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=poller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poller.js","sourceRoot":"","sources":["../src/poller.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AoaoeConfig, Reasoner, Observation, ReasonerResult } from "../types.js";
|
|
2
|
+
export declare class ClaudeCodeReasoner implements Reasoner {
|
|
3
|
+
private config;
|
|
4
|
+
private sessionId;
|
|
5
|
+
constructor(config: AoaoeConfig);
|
|
6
|
+
init(): Promise<void>;
|
|
7
|
+
decide(observation: Observation): Promise<ReasonerResult>;
|
|
8
|
+
shutdown(): Promise<void>;
|
|
9
|
+
private buildArgs;
|
|
10
|
+
private tryExtractSessionId;
|
|
11
|
+
private parseResponse;
|
|
12
|
+
private validateResult;
|
|
13
|
+
private log;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=claude-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../src/reasoner/claude-code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAU,MAAM,aAAa,CAAC;AAM9F,qBAAa,kBAAmB,YAAW,QAAQ;IACjD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAuB;gBAE5B,MAAM,EAAE,WAAW;IAIzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IASrB,MAAM,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAkBzD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,OAAO,CAAC,SAAS;IA8BjB,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,aAAa;IAkCrB,OAAO,CAAC,cAAc;IAsBtB,OAAO,CAAC,GAAG;CAGZ"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { exec } from "../shell.js";
|
|
2
|
+
import { SYSTEM_PROMPT, formatObservation } from "./prompt.js";
|
|
3
|
+
// Claude Code backend: uses `claude --print` subprocess for each decision.
|
|
4
|
+
// Optionally stateful via `--resume`.
|
|
5
|
+
export class ClaudeCodeReasoner {
|
|
6
|
+
config;
|
|
7
|
+
sessionId = null;
|
|
8
|
+
constructor(config) {
|
|
9
|
+
this.config = config;
|
|
10
|
+
}
|
|
11
|
+
async init() {
|
|
12
|
+
// verify claude is available
|
|
13
|
+
const result = await exec("claude", ["--version"]);
|
|
14
|
+
if (result.exitCode !== 0) {
|
|
15
|
+
throw new Error("claude CLI not found or not working");
|
|
16
|
+
}
|
|
17
|
+
this.log(`claude available: ${result.stdout.trim()}`);
|
|
18
|
+
}
|
|
19
|
+
async decide(observation) {
|
|
20
|
+
const prompt = formatObservation(observation);
|
|
21
|
+
const args = this.buildArgs(prompt);
|
|
22
|
+
const result = await exec("claude", args, 120_000);
|
|
23
|
+
if (result.exitCode !== 0) {
|
|
24
|
+
this.log(`claude failed: ${result.stderr}`);
|
|
25
|
+
return { actions: [{ action: "wait", reason: "reasoner error" }] };
|
|
26
|
+
}
|
|
27
|
+
// capture session ID from output for --resume on next call
|
|
28
|
+
if (this.config.claudeCode.resume) {
|
|
29
|
+
this.tryExtractSessionId(result.stderr + result.stdout);
|
|
30
|
+
}
|
|
31
|
+
return this.parseResponse(result.stdout);
|
|
32
|
+
}
|
|
33
|
+
async shutdown() {
|
|
34
|
+
// stateless subprocess, nothing to clean up
|
|
35
|
+
}
|
|
36
|
+
buildArgs(prompt) {
|
|
37
|
+
const args = ["--print"];
|
|
38
|
+
// output format
|
|
39
|
+
args.push("--output-format", "text");
|
|
40
|
+
// system prompt injection
|
|
41
|
+
args.push("--append-system-prompt", SYSTEM_PROMPT);
|
|
42
|
+
// model selection
|
|
43
|
+
if (this.config.claudeCode.model) {
|
|
44
|
+
args.push("--model", this.config.claudeCode.model);
|
|
45
|
+
}
|
|
46
|
+
// YOLO mode for unattended operation
|
|
47
|
+
if (this.config.claudeCode.yolo) {
|
|
48
|
+
args.push("--dangerously-skip-permissions");
|
|
49
|
+
}
|
|
50
|
+
// resume previous session for context continuity
|
|
51
|
+
if (this.config.claudeCode.resume && this.sessionId) {
|
|
52
|
+
args.push("--resume", this.sessionId);
|
|
53
|
+
}
|
|
54
|
+
// the actual prompt
|
|
55
|
+
args.push(prompt);
|
|
56
|
+
return args;
|
|
57
|
+
}
|
|
58
|
+
tryExtractSessionId(output) {
|
|
59
|
+
// claude prints session info to stderr, try to capture it
|
|
60
|
+
// format varies by version, look for common patterns
|
|
61
|
+
const match = output.match(/session[_\s]?(?:id)?[:\s]+([a-f0-9-]+)/i);
|
|
62
|
+
if (match) {
|
|
63
|
+
this.sessionId = match[1];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
parseResponse(raw) {
|
|
67
|
+
const trimmed = raw.trim();
|
|
68
|
+
// try direct JSON parse
|
|
69
|
+
try {
|
|
70
|
+
return this.validateResult(JSON.parse(trimmed));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// not direct JSON
|
|
74
|
+
}
|
|
75
|
+
// extract from markdown code blocks
|
|
76
|
+
const jsonMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
77
|
+
if (jsonMatch) {
|
|
78
|
+
try {
|
|
79
|
+
return this.validateResult(JSON.parse(jsonMatch[1]));
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// fall through
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// find first { ... } block
|
|
86
|
+
const braceMatch = trimmed.match(/\{[\s\S]*\}/);
|
|
87
|
+
if (braceMatch) {
|
|
88
|
+
try {
|
|
89
|
+
return this.validateResult(JSON.parse(braceMatch[0]));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// give up
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.log(`failed to parse: ${trimmed.slice(0, 200)}`);
|
|
96
|
+
return { actions: [{ action: "wait", reason: "failed to parse reasoner response" }] };
|
|
97
|
+
}
|
|
98
|
+
validateResult(parsed) {
|
|
99
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
100
|
+
return { actions: [{ action: "wait", reason: "invalid response" }] };
|
|
101
|
+
}
|
|
102
|
+
const obj = parsed;
|
|
103
|
+
const actions = Array.isArray(obj.actions) ? obj.actions : [];
|
|
104
|
+
const validActions = actions
|
|
105
|
+
.filter((a) => typeof a === "object" && a !== null && "action" in a)
|
|
106
|
+
.map((a) => a);
|
|
107
|
+
if (validActions.length === 0) {
|
|
108
|
+
validActions.push({ action: "wait", reason: "no valid actions" });
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
actions: validActions,
|
|
112
|
+
reasoning: typeof obj.reasoning === "string" ? obj.reasoning : undefined,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
log(msg) {
|
|
116
|
+
console.error(`[reasoner:claude-code] ${msg}`);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=claude-code.js.map
|