@oratis/lisa 0.5.0 → 0.8.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/README.md +5 -0
- package/dist/autostart/install.d.ts +28 -0
- package/dist/autostart/install.d.ts.map +1 -0
- package/dist/autostart/install.js +180 -0
- package/dist/autostart/install.js.map +1 -0
- package/dist/cli.js +49 -1
- package/dist/cli.js.map +1 -1
- package/dist/heartbeat/install.d.ts.map +1 -1
- package/dist/heartbeat/install.js +1 -43
- package/dist/heartbeat/install.js.map +1 -1
- package/dist/heartbeat/runner.d.ts.map +1 -1
- package/dist/heartbeat/runner.js +11 -0
- package/dist/heartbeat/runner.js.map +1 -1
- package/dist/idle/runner.d.ts +12 -0
- package/dist/idle/runner.d.ts.map +1 -1
- package/dist/idle/runner.js +33 -2
- package/dist/idle/runner.js.map +1 -1
- package/dist/integrations/aider/observer.d.ts +48 -0
- package/dist/integrations/aider/observer.d.ts.map +1 -0
- package/dist/integrations/aider/observer.js +196 -0
- package/dist/integrations/aider/observer.js.map +1 -0
- package/dist/integrations/comparisons.d.ts +23 -0
- package/dist/integrations/comparisons.d.ts.map +1 -0
- package/dist/integrations/comparisons.js +47 -0
- package/dist/integrations/comparisons.js.map +1 -0
- package/dist/integrations/dispatch-ledger.d.ts +51 -0
- package/dist/integrations/dispatch-ledger.d.ts.map +1 -0
- package/dist/integrations/dispatch-ledger.js +106 -0
- package/dist/integrations/dispatch-ledger.js.map +1 -0
- package/dist/integrations/github-pr/observer.d.ts +85 -0
- package/dist/integrations/github-pr/observer.d.ts.map +1 -0
- package/dist/integrations/github-pr/observer.js +261 -0
- package/dist/integrations/github-pr/observer.js.map +1 -0
- package/dist/integrations/hub.d.ts.map +1 -1
- package/dist/integrations/hub.js +10 -0
- package/dist/integrations/hub.js.map +1 -1
- package/dist/integrations/opencode/observer.d.ts +74 -0
- package/dist/integrations/opencode/observer.d.ts.map +1 -0
- package/dist/integrations/opencode/observer.js +207 -0
- package/dist/integrations/opencode/observer.js.map +1 -0
- package/dist/integrations/registry.d.ts.map +1 -1
- package/dist/integrations/registry.js +3 -1
- package/dist/integrations/registry.js.map +1 -1
- package/dist/integrations/scheduled-dispatch.d.ts +42 -0
- package/dist/integrations/scheduled-dispatch.d.ts.map +1 -0
- package/dist/integrations/scheduled-dispatch.js +140 -0
- package/dist/integrations/scheduled-dispatch.js.map +1 -0
- package/dist/launchd.d.ts +18 -0
- package/dist/launchd.d.ts.map +1 -0
- package/dist/launchd.js +60 -0
- package/dist/launchd.js.map +1 -0
- package/dist/orchestrator/journal.d.ts +46 -0
- package/dist/orchestrator/journal.d.ts.map +1 -0
- package/dist/orchestrator/journal.js +82 -0
- package/dist/orchestrator/journal.js.map +1 -0
- package/dist/orchestrator/recap.d.ts +38 -0
- package/dist/orchestrator/recap.d.ts.map +1 -0
- package/dist/orchestrator/recap.js +134 -0
- package/dist/orchestrator/recap.js.map +1 -0
- package/dist/screen_advisor/engine.d.ts +79 -0
- package/dist/screen_advisor/engine.d.ts.map +1 -0
- package/dist/screen_advisor/engine.js +128 -0
- package/dist/screen_advisor/engine.js.map +1 -0
- package/dist/soul/birth.d.ts.map +1 -1
- package/dist/soul/birth.js +8 -0
- package/dist/soul/birth.js.map +1 -1
- package/dist/soul/lock.d.ts +7 -4
- package/dist/soul/lock.d.ts.map +1 -1
- package/dist/soul/lock.js +33 -9
- package/dist/soul/lock.js.map +1 -1
- package/dist/tools/agent_recap.d.ts +21 -0
- package/dist/tools/agent_recap.d.ts.map +1 -0
- package/dist/tools/agent_recap.js +43 -0
- package/dist/tools/agent_recap.js.map +1 -0
- package/dist/tools/compare_agents.d.ts +16 -0
- package/dist/tools/compare_agents.d.ts.map +1 -0
- package/dist/tools/compare_agents.js +145 -0
- package/dist/tools/compare_agents.js.map +1 -0
- package/dist/tools/dispatch_agent.d.ts +13 -0
- package/dist/tools/dispatch_agent.d.ts.map +1 -1
- package/dist/tools/dispatch_agent.js +49 -37
- package/dist/tools/dispatch_agent.js.map +1 -1
- package/dist/tools/exec-util.d.ts +18 -0
- package/dist/tools/exec-util.d.ts.map +1 -0
- package/dist/tools/exec-util.js +72 -0
- package/dist/tools/exec-util.js.map +1 -0
- package/dist/tools/inspect_agent.d.ts +21 -0
- package/dist/tools/inspect_agent.d.ts.map +1 -0
- package/dist/tools/inspect_agent.js +87 -0
- package/dist/tools/inspect_agent.js.map +1 -0
- package/dist/tools/list_agents.d.ts +21 -0
- package/dist/tools/list_agents.d.ts.map +1 -0
- package/dist/tools/list_agents.js +88 -0
- package/dist/tools/list_agents.js.map +1 -0
- package/dist/tools/pr_status.d.ts +31 -0
- package/dist/tools/pr_status.d.ts.map +1 -0
- package/dist/tools/pr_status.js +90 -0
- package/dist/tools/pr_status.js.map +1 -0
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +26 -0
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/repo_digest.d.ts +33 -0
- package/dist/tools/repo_digest.d.ts.map +1 -0
- package/dist/tools/repo_digest.js +113 -0
- package/dist/tools/repo_digest.js.map +1 -0
- package/dist/tools/review_diff.d.ts +28 -0
- package/dist/tools/review_diff.d.ts.map +1 -0
- package/dist/tools/review_diff.js +68 -0
- package/dist/tools/review_diff.js.map +1 -0
- package/dist/tools/run_checks.d.ts +15 -0
- package/dist/tools/run_checks.d.ts.map +1 -0
- package/dist/tools/run_checks.js +117 -0
- package/dist/tools/run_checks.js.map +1 -0
- package/dist/tools/scheduled_dispatch.d.ts +27 -0
- package/dist/tools/scheduled_dispatch.d.ts.map +1 -0
- package/dist/tools/scheduled_dispatch.js +57 -0
- package/dist/tools/scheduled_dispatch.js.map +1 -0
- package/dist/tools/signal_agent.d.ts +36 -0
- package/dist/tools/signal_agent.d.ts.map +1 -0
- package/dist/tools/signal_agent.js +140 -0
- package/dist/tools/signal_agent.js.map +1 -0
- package/dist/web/island.d.ts +1 -1
- package/dist/web/island.d.ts.map +1 -1
- package/dist/web/island.js +139 -27
- package/dist/web/island.js.map +1 -1
- package/dist/web/lisa-html.d.ts +1 -1
- package/dist/web/lisa-html.d.ts.map +1 -1
- package/dist/web/lisa-html.js +132 -4
- package/dist/web/lisa-html.js.map +1 -1
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +190 -0
- package/dist/web/server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode observer (O3 — local CLI agent, SQLite-backed).
|
|
3
|
+
*
|
|
4
|
+
* Recent OpenCode (≥ ~1.15) stores sessions in a SQLite database at
|
|
5
|
+
* $XDG_DATA_HOME/opencode/opencode.db (≈ ~/.local/share/opencode/opencode.db)
|
|
6
|
+
* — not flat JSONL like Claude Code / Codex. So instead of tailing files this
|
|
7
|
+
* adapter POLLS the DB (read-only) via the system `sqlite3` CLI, which keeps us
|
|
8
|
+
* dependency-free (no sqlite npm package, and `node:sqlite` isn't available on
|
|
9
|
+
* the Node 20/22 we support).
|
|
10
|
+
*
|
|
11
|
+
* State is derived from the `session` row plus its newest `message`:
|
|
12
|
+
* - session.time_archived set → done
|
|
13
|
+
* - session.time_compacting set → working (compacting context)
|
|
14
|
+
* - latest message has `error` → error
|
|
15
|
+
* - latest message role=assistant + time.completed set → waiting (it replied)
|
|
16
|
+
* - latest message role=assistant, not completed → working (mid-turn)
|
|
17
|
+
* - latest message role=user → working (its turn)
|
|
18
|
+
*
|
|
19
|
+
* PRIVACY: only structural fields — session title, directory, token counts,
|
|
20
|
+
* message role/role-timing/error flag. Never message text or tool arguments.
|
|
21
|
+
* DISABLED by default; opt in via ~/.lisa/agents.json. Graceful no-op if
|
|
22
|
+
* `sqlite3` or the DB is absent (every read failure → empty, never throws).
|
|
23
|
+
*/
|
|
24
|
+
import os from "node:os";
|
|
25
|
+
import path from "node:path";
|
|
26
|
+
import { EventEmitter } from "node:events";
|
|
27
|
+
import { execFile } from "node:child_process";
|
|
28
|
+
import { promisify } from "node:util";
|
|
29
|
+
import { registerIntegration } from "../registry.js";
|
|
30
|
+
const pexec = promisify(execFile);
|
|
31
|
+
const POLL_MS_DEFAULT = 60_000;
|
|
32
|
+
const ACTIVE_WINDOW_MS_DEFAULT = 6 * 60 * 60_000; // 6h
|
|
33
|
+
const MAX_LISTED = 12;
|
|
34
|
+
function defaultDbPath(cfg) {
|
|
35
|
+
const home = cfg.home
|
|
36
|
+
? cfg.home.replace(/^~/, os.homedir())
|
|
37
|
+
: process.env.XDG_DATA_HOME
|
|
38
|
+
? path.join(process.env.XDG_DATA_HOME, "opencode")
|
|
39
|
+
: path.join(os.homedir(), ".local", "share", "opencode");
|
|
40
|
+
return path.join(home, "opencode.db");
|
|
41
|
+
}
|
|
42
|
+
/** Map one query row to the normalized session. Pure — the unit under test. */
|
|
43
|
+
export function mapOpencodeSession(row) {
|
|
44
|
+
const directory = row.directory ?? undefined;
|
|
45
|
+
const lastMtime = typeof row.time_updated === "number" ? row.time_updated : 0;
|
|
46
|
+
let state;
|
|
47
|
+
let reason;
|
|
48
|
+
if (row.time_archived) {
|
|
49
|
+
state = "done";
|
|
50
|
+
reason = "archived";
|
|
51
|
+
}
|
|
52
|
+
else if (row.time_compacting) {
|
|
53
|
+
state = "working";
|
|
54
|
+
reason = "compacting";
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const msg = parseLastMessage(row.last_msg);
|
|
58
|
+
if (msg.error) {
|
|
59
|
+
state = "error";
|
|
60
|
+
reason = msg.errorReason ?? "error";
|
|
61
|
+
}
|
|
62
|
+
else if (msg.role === "assistant") {
|
|
63
|
+
state = msg.completed ? "waiting" : "working";
|
|
64
|
+
reason = msg.completed ? "assistant" : "assistant-streaming";
|
|
65
|
+
}
|
|
66
|
+
else if (msg.role === "user") {
|
|
67
|
+
state = "working";
|
|
68
|
+
reason = "user";
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
state = "idle";
|
|
72
|
+
reason = "no-messages";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const title = (row.title ?? "").trim();
|
|
76
|
+
const project = directory ? path.basename(directory) : title || row.id;
|
|
77
|
+
const session = {
|
|
78
|
+
agent: "opencode",
|
|
79
|
+
sessionId: row.id,
|
|
80
|
+
project,
|
|
81
|
+
cwd: directory,
|
|
82
|
+
state,
|
|
83
|
+
stateReason: reason,
|
|
84
|
+
lastMtime,
|
|
85
|
+
};
|
|
86
|
+
const tin = row.tokens_input ?? 0;
|
|
87
|
+
const tout = row.tokens_output ?? 0;
|
|
88
|
+
if (tin || tout) {
|
|
89
|
+
session.activity = {
|
|
90
|
+
turnCount: 0,
|
|
91
|
+
lastTools: [],
|
|
92
|
+
filesTouched: [],
|
|
93
|
+
tokens: { input: tin, output: tout },
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return session;
|
|
97
|
+
}
|
|
98
|
+
/** Tolerant parse of an OpenCode message `data` JSON blob. */
|
|
99
|
+
export function parseLastMessage(raw) {
|
|
100
|
+
if (!raw)
|
|
101
|
+
return {};
|
|
102
|
+
let d;
|
|
103
|
+
try {
|
|
104
|
+
d = JSON.parse(raw);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
const role = typeof d.role === "string" ? d.role : undefined;
|
|
110
|
+
const time = (d.time ?? {});
|
|
111
|
+
const completed = time.completed != null && time.completed !== 0;
|
|
112
|
+
const err = d.error;
|
|
113
|
+
let error = false;
|
|
114
|
+
let errorReason;
|
|
115
|
+
if (err && typeof err === "object") {
|
|
116
|
+
error = true;
|
|
117
|
+
const m = err.data?.message;
|
|
118
|
+
errorReason =
|
|
119
|
+
typeof m === "string"
|
|
120
|
+
? m.slice(0, 80)
|
|
121
|
+
: typeof err.name === "string"
|
|
122
|
+
? err.name
|
|
123
|
+
: "error";
|
|
124
|
+
}
|
|
125
|
+
return { role, completed, error, errorReason };
|
|
126
|
+
}
|
|
127
|
+
const QUERY = "SELECT s.id AS id, s.directory AS directory, s.title AS title, s.agent AS agent, " +
|
|
128
|
+
"s.time_updated AS time_updated, s.time_archived AS time_archived, " +
|
|
129
|
+
"s.time_compacting AS time_compacting, s.tokens_input AS tokens_input, " +
|
|
130
|
+
"s.tokens_output AS tokens_output, " +
|
|
131
|
+
"(SELECT m.data FROM message m WHERE m.session_id = s.id ORDER BY m.time_created DESC LIMIT 1) AS last_msg " +
|
|
132
|
+
"FROM session s ORDER BY s.time_updated DESC LIMIT 40;";
|
|
133
|
+
/** Read sessions from the OpenCode DB via the system sqlite3 CLI. [] on failure. */
|
|
134
|
+
async function sqliteFetch(dbPath) {
|
|
135
|
+
try {
|
|
136
|
+
const { stdout } = await pexec("sqlite3", ["-json", "-readonly", dbPath, QUERY], {
|
|
137
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
138
|
+
timeout: 15_000,
|
|
139
|
+
});
|
|
140
|
+
const s = stdout.trim();
|
|
141
|
+
if (!s)
|
|
142
|
+
return [];
|
|
143
|
+
return JSON.parse(s);
|
|
144
|
+
}
|
|
145
|
+
catch {
|
|
146
|
+
return []; // sqlite3 missing, db absent/locked, parse error — graceful no-op
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export class OpencodeObserver extends EventEmitter {
|
|
150
|
+
agent = "opencode";
|
|
151
|
+
sessions = new Map();
|
|
152
|
+
timer = null;
|
|
153
|
+
emitFn = null;
|
|
154
|
+
fetcher;
|
|
155
|
+
pollMs;
|
|
156
|
+
windowMs;
|
|
157
|
+
now;
|
|
158
|
+
constructor(cfg) {
|
|
159
|
+
super();
|
|
160
|
+
const db = defaultDbPath(cfg);
|
|
161
|
+
this.fetcher = cfg.fetchRows ?? (() => sqliteFetch(db));
|
|
162
|
+
this.pollMs = typeof cfg.pollMs === "number" && cfg.pollMs > 0 ? cfg.pollMs : POLL_MS_DEFAULT;
|
|
163
|
+
this.windowMs =
|
|
164
|
+
typeof cfg.activeWindowMs === "number" && cfg.activeWindowMs > 0
|
|
165
|
+
? cfg.activeWindowMs
|
|
166
|
+
: ACTIVE_WINDOW_MS_DEFAULT;
|
|
167
|
+
this.now = cfg.now ?? Date.now;
|
|
168
|
+
}
|
|
169
|
+
async start(emit) {
|
|
170
|
+
this.emitFn = emit;
|
|
171
|
+
await this.poll();
|
|
172
|
+
this.timer = setInterval(() => void this.poll(), this.pollMs);
|
|
173
|
+
this.timer.unref?.();
|
|
174
|
+
}
|
|
175
|
+
list() {
|
|
176
|
+
const cutoff = this.now() - this.windowMs;
|
|
177
|
+
return [...this.sessions.values()]
|
|
178
|
+
.filter((s) => s.lastMtime >= cutoff)
|
|
179
|
+
.sort((a, b) => b.lastMtime - a.lastMtime)
|
|
180
|
+
.slice(0, MAX_LISTED);
|
|
181
|
+
}
|
|
182
|
+
async stop() {
|
|
183
|
+
if (this.timer)
|
|
184
|
+
clearInterval(this.timer);
|
|
185
|
+
this.timer = null;
|
|
186
|
+
}
|
|
187
|
+
/** One refresh cycle — exposed for deterministic tests. */
|
|
188
|
+
async poll() {
|
|
189
|
+
let rows;
|
|
190
|
+
try {
|
|
191
|
+
rows = await this.fetcher();
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
for (const row of rows) {
|
|
197
|
+
const s = mapOpencodeSession(row);
|
|
198
|
+
const prev = this.sessions.get(s.sessionId);
|
|
199
|
+
this.sessions.set(s.sessionId, s);
|
|
200
|
+
if (this.emitFn && (!prev || prev.state !== s.state || prev.lastMtime !== s.lastMtime)) {
|
|
201
|
+
this.emitFn(s);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
registerIntegration("opencode", (cfg) => new OpencodeObserver(cfg));
|
|
207
|
+
//# sourceMappingURL=observer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"observer.js","sourceRoot":"","sources":["../../../src/integrations/opencode/observer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAEN;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,sBAAsB,GAC1B,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAEpD;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAKD,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAIpB,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,oBAAoB,GAC5B,IAAI,CAEN;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,sBAAsB,GAC1B,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAEpD;AAED,yDAAyD;AACzD,wBAAgB,yBAAyB,IAAI,IAAI,CAGhD;AAKD,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,IAAI,CAAC,CAQjE"}
|
|
@@ -33,6 +33,8 @@ export async function registerBuiltinIntegrations() {
|
|
|
33
33
|
builtinsRegistered = true;
|
|
34
34
|
await import("./claude-code/observer.js");
|
|
35
35
|
await import("./codex/observer.js");
|
|
36
|
-
|
|
36
|
+
await import("./github-pr/observer.js");
|
|
37
|
+
await import("./opencode/observer.js");
|
|
38
|
+
await import("./aider/observer.js");
|
|
37
39
|
}
|
|
38
40
|
//# sourceMappingURL=registry.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE1D,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,OAA6B;IAE7B,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,GAA2B;IAE3B,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,aAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAC7C,EAAE,CACH,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,yBAAyB;IACvC,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,kBAAkB,GAAG,KAAK,CAAC;AAC7B,CAAC;AAED,4DAA4D;AAC5D,wCAAwC;AACxC,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAC/B,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,IAAI,kBAAkB;QAAE,OAAO;IAC/B,kBAAkB,GAAG,IAAI,CAAC;IAC1B,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACpC,
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAQH,MAAM,SAAS,GAAG,IAAI,GAAG,EAAgC,CAAC;AAE1D,MAAM,UAAU,mBAAmB,CACjC,IAAY,EACZ,OAA6B;IAE7B,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,GAA2B;IAE3B,MAAM,OAAO,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,wBAAwB,IAAI,aAC1B,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,mBAC7C,EAAE,CACH,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,yBAAyB;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7C,CAAC;AAED,yDAAyD;AACzD,MAAM,UAAU,yBAAyB;IACvC,SAAS,CAAC,KAAK,EAAE,CAAC;IAClB,kBAAkB,GAAG,KAAK,CAAC;AAC7B,CAAC;AAED,4DAA4D;AAC5D,wCAAwC;AACxC,IAAI,kBAAkB,GAAG,KAAK,CAAC;AAC/B,MAAM,CAAC,KAAK,UAAU,2BAA2B;IAC/C,IAAI,kBAAkB;QAAE,OAAO;IAC/B,kBAAkB,GAAG,IAAI,CAAC;IAC1B,MAAM,MAAM,CAAC,2BAA2B,CAAC,CAAC;IAC1C,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;IACpC,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAC;IACxC,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;IACvC,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type ScheduledAgent = "claude" | "codex" | "opencode" | "aider";
|
|
2
|
+
export interface ScheduledDispatch {
|
|
3
|
+
id: string;
|
|
4
|
+
agent: ScheduledAgent;
|
|
5
|
+
task: string;
|
|
6
|
+
cwd: string;
|
|
7
|
+
/** "every:30m" | "every:2h" | "every:1d" | "daily:09:00" */
|
|
8
|
+
schedule: string;
|
|
9
|
+
maxRuns: number;
|
|
10
|
+
runs: number;
|
|
11
|
+
lastRunAt?: number;
|
|
12
|
+
createdAt: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const DEFAULT_MAX_RUNS = 30;
|
|
15
|
+
export declare function loadScheduled(): Promise<ScheduledDispatch[]>;
|
|
16
|
+
export declare function addScheduled(e: Omit<ScheduledDispatch, "id" | "runs" | "createdAt"> & {
|
|
17
|
+
createdAt?: number;
|
|
18
|
+
}): Promise<ScheduledDispatch>;
|
|
19
|
+
export declare function removeScheduled(id: string): Promise<boolean>;
|
|
20
|
+
/** Persist run bookkeeping after a fire. */
|
|
21
|
+
export declare function markRan(id: string, when: number): Promise<void>;
|
|
22
|
+
export declare function isValidSchedule(schedule: string): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Is this entry due to fire at `now`? Pure + testable.
|
|
25
|
+
* - capped: false once runs >= maxRuns
|
|
26
|
+
* - every:N — due when now - lastRunAt >= interval (or never run)
|
|
27
|
+
* - daily:HH:MM — due when now is past today's HH:MM and it hasn't run since
|
|
28
|
+
*/
|
|
29
|
+
export declare function isDue(e: ScheduledDispatch, now: number): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Fire every due entry via the injected `launch` (dependency-injected so this
|
|
32
|
+
* module stays free of the tools layer and is testable). Marks each fired entry
|
|
33
|
+
* regardless of launch success, so a broken entry burns its run budget rather
|
|
34
|
+
* than retrying every tick. Returns one summary line per fire.
|
|
35
|
+
*/
|
|
36
|
+
export declare function fireDue(now: number, launch: (e: ScheduledDispatch) => Promise<{
|
|
37
|
+
pid?: number;
|
|
38
|
+
error?: string;
|
|
39
|
+
}>): Promise<string[]>;
|
|
40
|
+
/** Human description of an entry for listing. */
|
|
41
|
+
export declare function describeScheduled(e: ScheduledDispatch): string;
|
|
42
|
+
//# sourceMappingURL=scheduled-dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-dispatch.d.ts","sourceRoot":"","sources":["../../src/integrations/scheduled-dispatch.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,cAAc,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,4DAA4D;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAGD,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAMnC,wBAAsB,aAAa,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAQlE;AAMD,wBAAsB,YAAY,CAChC,CAAC,EAAE,IAAI,CAAC,iBAAiB,EAAE,IAAI,GAAG,MAAM,GAAG,WAAW,CAAC,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,iBAAiB,CAAC,CAW5B;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMlE;AAED,4CAA4C;AAC5C,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAOrE;AAqBD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,CAAC,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAoBhE;AAED;;;;;GAKG;AACH,wBAAsB,OAAO,CAC3B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,OAAO,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,GAC1E,OAAO,CAAC,MAAM,EAAE,CAAC,CAenB;AAED,iDAAiD;AACjD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAI9D"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scheduled dispatches — a small persisted store of "launch agent X in repo Y
|
|
3
|
+
* with task Z on schedule S". The heartbeat runner checks it each tick and
|
|
4
|
+
* fires the due ones (see fireDueScheduledDispatches, called from the runner).
|
|
5
|
+
*
|
|
6
|
+
* Safety: this auto-launches autonomous agents unattended at heartbeat cadence,
|
|
7
|
+
* so each entry carries a maxRuns cap (default 30) and records its run count;
|
|
8
|
+
* once spent it's inert until the user removes/re-adds it.
|
|
9
|
+
*/
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { readFile } from "node:fs/promises";
|
|
13
|
+
import { atomicWrite } from "../fs-utils.js";
|
|
14
|
+
import { LISA_HOME } from "../paths.js";
|
|
15
|
+
const FILE = path.join(LISA_HOME, "scheduled-dispatches.json");
|
|
16
|
+
export const DEFAULT_MAX_RUNS = 30;
|
|
17
|
+
export async function loadScheduled() {
|
|
18
|
+
try {
|
|
19
|
+
const raw = await readFile(FILE, "utf8");
|
|
20
|
+
const store = JSON.parse(raw);
|
|
21
|
+
return Array.isArray(store.entries) ? store.entries : [];
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async function saveScheduled(entries) {
|
|
28
|
+
await atomicWrite(FILE, JSON.stringify({ entries }, null, 2));
|
|
29
|
+
}
|
|
30
|
+
export async function addScheduled(e) {
|
|
31
|
+
const entries = await loadScheduled();
|
|
32
|
+
const entry = {
|
|
33
|
+
...e,
|
|
34
|
+
id: randomUUID().slice(0, 8),
|
|
35
|
+
runs: 0,
|
|
36
|
+
createdAt: e.createdAt ?? Date.now(),
|
|
37
|
+
};
|
|
38
|
+
entries.push(entry);
|
|
39
|
+
await saveScheduled(entries);
|
|
40
|
+
return entry;
|
|
41
|
+
}
|
|
42
|
+
export async function removeScheduled(id) {
|
|
43
|
+
const entries = await loadScheduled();
|
|
44
|
+
const next = entries.filter((e) => e.id !== id && !e.id.startsWith(id));
|
|
45
|
+
if (next.length === entries.length)
|
|
46
|
+
return false;
|
|
47
|
+
await saveScheduled(next);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
/** Persist run bookkeeping after a fire. */
|
|
51
|
+
export async function markRan(id, when) {
|
|
52
|
+
const entries = await loadScheduled();
|
|
53
|
+
const e = entries.find((x) => x.id === id);
|
|
54
|
+
if (!e)
|
|
55
|
+
return;
|
|
56
|
+
e.runs += 1;
|
|
57
|
+
e.lastRunAt = when;
|
|
58
|
+
await saveScheduled(entries);
|
|
59
|
+
}
|
|
60
|
+
/** Parse "every:<N><m|h|d>" → interval ms, or null if not that form. */
|
|
61
|
+
function parseEvery(schedule) {
|
|
62
|
+
const m = schedule.match(/^every:(\d+)([mhd])$/i);
|
|
63
|
+
if (!m)
|
|
64
|
+
return null;
|
|
65
|
+
const n = parseInt(m[1], 10);
|
|
66
|
+
const unit = m[2].toLowerCase();
|
|
67
|
+
return n * (unit === "m" ? 60_000 : unit === "h" ? 3_600_000 : 86_400_000);
|
|
68
|
+
}
|
|
69
|
+
/** Parse "daily:HH:MM" → {h, m}, or null. */
|
|
70
|
+
function parseDaily(schedule) {
|
|
71
|
+
const m = schedule.match(/^daily:(\d{1,2}):(\d{2})$/i);
|
|
72
|
+
if (!m)
|
|
73
|
+
return null;
|
|
74
|
+
const h = parseInt(m[1], 10);
|
|
75
|
+
const min = parseInt(m[2], 10);
|
|
76
|
+
if (h > 23 || min > 59)
|
|
77
|
+
return null;
|
|
78
|
+
return { h, m: min };
|
|
79
|
+
}
|
|
80
|
+
export function isValidSchedule(schedule) {
|
|
81
|
+
return parseEvery(schedule) !== null || parseDaily(schedule) !== null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Is this entry due to fire at `now`? Pure + testable.
|
|
85
|
+
* - capped: false once runs >= maxRuns
|
|
86
|
+
* - every:N — due when now - lastRunAt >= interval (or never run)
|
|
87
|
+
* - daily:HH:MM — due when now is past today's HH:MM and it hasn't run since
|
|
88
|
+
*/
|
|
89
|
+
export function isDue(e, now) {
|
|
90
|
+
if (e.runs >= e.maxRuns)
|
|
91
|
+
return false;
|
|
92
|
+
const everyMs = parseEvery(e.schedule);
|
|
93
|
+
if (everyMs !== null) {
|
|
94
|
+
if (e.lastRunAt == null)
|
|
95
|
+
return true;
|
|
96
|
+
return now - e.lastRunAt >= everyMs;
|
|
97
|
+
}
|
|
98
|
+
const daily = parseDaily(e.schedule);
|
|
99
|
+
if (daily) {
|
|
100
|
+
const target = new Date(now);
|
|
101
|
+
target.setHours(daily.h, daily.m, 0, 0);
|
|
102
|
+
const targetMs = target.getTime();
|
|
103
|
+
if (now < targetMs)
|
|
104
|
+
return false; // today's time hasn't arrived
|
|
105
|
+
// Past today's target: due unless we already ran at/after it.
|
|
106
|
+
return e.lastRunAt == null || e.lastRunAt < targetMs;
|
|
107
|
+
}
|
|
108
|
+
return false; // unparseable schedule never fires
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Fire every due entry via the injected `launch` (dependency-injected so this
|
|
112
|
+
* module stays free of the tools layer and is testable). Marks each fired entry
|
|
113
|
+
* regardless of launch success, so a broken entry burns its run budget rather
|
|
114
|
+
* than retrying every tick. Returns one summary line per fire.
|
|
115
|
+
*/
|
|
116
|
+
export async function fireDue(now, launch) {
|
|
117
|
+
const entries = await loadScheduled();
|
|
118
|
+
const fired = [];
|
|
119
|
+
for (const e of entries) {
|
|
120
|
+
if (!isDue(e, now))
|
|
121
|
+
continue;
|
|
122
|
+
let result;
|
|
123
|
+
try {
|
|
124
|
+
result = await launch(e);
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
result = { error: err.message };
|
|
128
|
+
}
|
|
129
|
+
await markRan(e.id, now);
|
|
130
|
+
fired.push(`${e.agent} in ${e.cwd} — ${result.error ? "FAILED: " + result.error : "pid " + result.pid}`);
|
|
131
|
+
}
|
|
132
|
+
return fired;
|
|
133
|
+
}
|
|
134
|
+
/** Human description of an entry for listing. */
|
|
135
|
+
export function describeScheduled(e) {
|
|
136
|
+
const last = e.lastRunAt ? new Date(e.lastRunAt).toISOString().slice(0, 16).replace("T", " ") : "never";
|
|
137
|
+
const task = e.task.length > 60 ? e.task.slice(0, 57) + "…" : e.task;
|
|
138
|
+
return `• ${e.id} ${e.agent} @ ${e.schedule} (${e.runs}/${e.maxRuns} runs, last ${last})\n ${e.cwd}\n "${task}"`;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=scheduled-dispatch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-dispatch.js","sourceRoot":"","sources":["../../src/integrations/scheduled-dispatch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** Escape a string for inclusion as XML text/attribute content in a plist. */
|
|
2
|
+
export declare function escapeXml(s: string): string;
|
|
3
|
+
/** Run a command, resolving stdout on exit 0 and rejecting otherwise. */
|
|
4
|
+
export declare function runCmd(cmd: string, args: string[]): Promise<string>;
|
|
5
|
+
/**
|
|
6
|
+
* Best-effort path to the `lisa` binary, used for the human-readable
|
|
7
|
+
* instructions line and the Linux service snippet. May return a "node <path>"
|
|
8
|
+
* form when only the local dist build is available.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveLisaBin(): Promise<string>;
|
|
11
|
+
/**
|
|
12
|
+
* Resolve the displayed binary into launchd ProgramArguments slots (one argv
|
|
13
|
+
* element per array entry). A "node <path>" form is split into two slots with
|
|
14
|
+
* an absolute node path so launchd — which has no shell and a minimal PATH —
|
|
15
|
+
* can exec it.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveLisaArgv(displayedBin: string): Promise<string[]>;
|
|
18
|
+
//# sourceMappingURL=launchd.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.d.ts","sourceRoot":"","sources":["../src/launchd.ts"],"names":[],"mappings":"AAQA,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,yEAAyE;AACzE,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAcnE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAYtD;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAO7E"}
|
package/dist/launchd.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared launchd / process helpers used by both the heartbeat scheduler and
|
|
3
|
+
* the login autostart installer. Kept dependency-free (node built-ins only).
|
|
4
|
+
*/
|
|
5
|
+
import fs from "node:fs/promises";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { spawn } from "node:child_process";
|
|
8
|
+
/** Escape a string for inclusion as XML text/attribute content in a plist. */
|
|
9
|
+
export function escapeXml(s) {
|
|
10
|
+
return s.replace(/[<>&"']/g, (c) => ({ "<": "<", ">": ">", "&": "&", '"': """, "'": "'" }[c]));
|
|
11
|
+
}
|
|
12
|
+
/** Run a command, resolving stdout on exit 0 and rejecting otherwise. */
|
|
13
|
+
export function runCmd(cmd, args) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const child = spawn(cmd, args);
|
|
16
|
+
let stdout = "";
|
|
17
|
+
let stderr = "";
|
|
18
|
+
child.stdout.on("data", (b) => (stdout += b.toString("utf8")));
|
|
19
|
+
child.stderr.on("data", (b) => (stderr += b.toString("utf8")));
|
|
20
|
+
child.on("error", reject);
|
|
21
|
+
child.on("close", (code) => code === 0
|
|
22
|
+
? resolve(stdout)
|
|
23
|
+
: reject(new Error(`${cmd} exited ${code}: ${stderr.trim()}`)));
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Best-effort path to the `lisa` binary, used for the human-readable
|
|
28
|
+
* instructions line and the Linux service snippet. May return a "node <path>"
|
|
29
|
+
* form when only the local dist build is available.
|
|
30
|
+
*/
|
|
31
|
+
export async function resolveLisaBin() {
|
|
32
|
+
try {
|
|
33
|
+
const out = await runCmd("which", ["lisa"]);
|
|
34
|
+
const trimmed = out.trim();
|
|
35
|
+
if (trimmed)
|
|
36
|
+
return trimmed;
|
|
37
|
+
}
|
|
38
|
+
catch { }
|
|
39
|
+
const here = path.resolve(process.cwd(), "dist", "cli.js");
|
|
40
|
+
try {
|
|
41
|
+
await fs.access(here);
|
|
42
|
+
return `node ${here}`;
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
return "lisa";
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the displayed binary into launchd ProgramArguments slots (one argv
|
|
49
|
+
* element per array entry). A "node <path>" form is split into two slots with
|
|
50
|
+
* an absolute node path so launchd — which has no shell and a minimal PATH —
|
|
51
|
+
* can exec it.
|
|
52
|
+
*/
|
|
53
|
+
export async function resolveLisaArgv(displayedBin) {
|
|
54
|
+
if (displayedBin.startsWith("node ")) {
|
|
55
|
+
const nodePath = (await runCmd("which", ["node"]).catch(() => "node")).trim() || "node";
|
|
56
|
+
return [nodePath, displayedBin.slice("node ".length)];
|
|
57
|
+
}
|
|
58
|
+
return [displayedBin];
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=launchd.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"launchd.js","sourceRoot":"","sources":["../src/launchd.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Orchestrator journal (L6) — a small temporal record of agent activity.
|
|
3
|
+
*
|
|
4
|
+
* The hub (L1) only holds a *live snapshot*; once a session goes idle/done it
|
|
5
|
+
* falls out of `hub.list()`. The advisor (L5) surfaces relevance-gated alerts.
|
|
6
|
+
* Neither answers "what actually happened across all my agents while I was
|
|
7
|
+
* away?" — which needs a record over *time*, including sessions that have since
|
|
8
|
+
* ended. This journal is that record: every meaningful state transition the hub
|
|
9
|
+
* emits is appended to a capped ring buffer, from which recap.ts synthesizes a
|
|
10
|
+
* cross-agent "while you were away" digest.
|
|
11
|
+
*
|
|
12
|
+
* In-memory + capped (no growth): a recap covers the current server's uptime,
|
|
13
|
+
* which is exactly the window the user was away *for*. PRIVACY: stores only the
|
|
14
|
+
* same structural metadata the hub emits (agent, project, state, tool/file
|
|
15
|
+
* names, error strings) — never prompts, replies, or file contents.
|
|
16
|
+
*/
|
|
17
|
+
import type { AgentSession, AgentSessionState } from "../integrations/types.js";
|
|
18
|
+
export interface AgentEvent {
|
|
19
|
+
agent: string;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
project: string;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
state: AgentSessionState;
|
|
24
|
+
stateReason: string;
|
|
25
|
+
/** Epoch ms of the transition. */
|
|
26
|
+
at: number;
|
|
27
|
+
/** Compact structural activity summary (tool · file · $cmd), if any. */
|
|
28
|
+
activity?: string;
|
|
29
|
+
/** Short error string when the session errored. */
|
|
30
|
+
error?: string;
|
|
31
|
+
}
|
|
32
|
+
/** Compact, structural one-liner from a session's Tier-2 activity. */
|
|
33
|
+
export declare function summarizeActivity(s: AgentSession): string | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Append a transition. Consecutive events for the same session with the same
|
|
36
|
+
* state + reason are collapsed (we only record *changes*), so a session that
|
|
37
|
+
* sits in "working" for an hour produces one entry, not hundreds.
|
|
38
|
+
*/
|
|
39
|
+
export declare function recordEvent(s: AgentSession, now?: number): AgentEvent | null;
|
|
40
|
+
/** All recorded events (oldest → newest). */
|
|
41
|
+
export declare function allEvents(): AgentEvent[];
|
|
42
|
+
/** Events at or after `sinceMs` (epoch ms), oldest → newest. */
|
|
43
|
+
export declare function eventsSince(sinceMs: number): AgentEvent[];
|
|
44
|
+
/** Test hook — wipe the buffer. */
|
|
45
|
+
export declare function _resetJournalForTest(): void;
|
|
46
|
+
//# sourceMappingURL=journal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal.d.ts","sourceRoot":"","sources":["../../src/orchestrator/journal.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,iBAAiB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,wEAAwE;IACxE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAKD,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAYrE;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,YAAY,EAAE,GAAG,GAAE,MAAmB,GAAG,UAAU,GAAG,IAAI,CA0BxF;AAED,6CAA6C;AAC7C,wBAAgB,SAAS,IAAI,UAAU,EAAE,CAExC;AAED,gEAAgE;AAChE,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,CAEzD;AAED,mCAAmC;AACnC,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
|