@oratis/lisa 0.7.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/autostart/install.d.ts +28 -0
  2. package/dist/autostart/install.d.ts.map +1 -0
  3. package/dist/autostart/install.js +180 -0
  4. package/dist/autostart/install.js.map +1 -0
  5. package/dist/cli.js +49 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/heartbeat/install.d.ts.map +1 -1
  8. package/dist/heartbeat/install.js +1 -43
  9. package/dist/heartbeat/install.js.map +1 -1
  10. package/dist/heartbeat/runner.d.ts.map +1 -1
  11. package/dist/heartbeat/runner.js +11 -0
  12. package/dist/heartbeat/runner.js.map +1 -1
  13. package/dist/integrations/aider/observer.d.ts +48 -0
  14. package/dist/integrations/aider/observer.d.ts.map +1 -0
  15. package/dist/integrations/aider/observer.js +196 -0
  16. package/dist/integrations/aider/observer.js.map +1 -0
  17. package/dist/integrations/comparisons.d.ts +23 -0
  18. package/dist/integrations/comparisons.d.ts.map +1 -0
  19. package/dist/integrations/comparisons.js +47 -0
  20. package/dist/integrations/comparisons.js.map +1 -0
  21. package/dist/integrations/github-pr/observer.d.ts +85 -0
  22. package/dist/integrations/github-pr/observer.d.ts.map +1 -0
  23. package/dist/integrations/github-pr/observer.js +261 -0
  24. package/dist/integrations/github-pr/observer.js.map +1 -0
  25. package/dist/integrations/hub.d.ts.map +1 -1
  26. package/dist/integrations/hub.js +10 -0
  27. package/dist/integrations/hub.js.map +1 -1
  28. package/dist/integrations/opencode/observer.d.ts +74 -0
  29. package/dist/integrations/opencode/observer.d.ts.map +1 -0
  30. package/dist/integrations/opencode/observer.js +207 -0
  31. package/dist/integrations/opencode/observer.js.map +1 -0
  32. package/dist/integrations/registry.d.ts.map +1 -1
  33. package/dist/integrations/registry.js +3 -1
  34. package/dist/integrations/registry.js.map +1 -1
  35. package/dist/integrations/scheduled-dispatch.d.ts +42 -0
  36. package/dist/integrations/scheduled-dispatch.d.ts.map +1 -0
  37. package/dist/integrations/scheduled-dispatch.js +140 -0
  38. package/dist/integrations/scheduled-dispatch.js.map +1 -0
  39. package/dist/launchd.d.ts +18 -0
  40. package/dist/launchd.d.ts.map +1 -0
  41. package/dist/launchd.js +60 -0
  42. package/dist/launchd.js.map +1 -0
  43. package/dist/mcp/config.d.ts +4 -0
  44. package/dist/mcp/config.d.ts.map +1 -1
  45. package/dist/mcp/config.js +34 -0
  46. package/dist/mcp/config.js.map +1 -1
  47. package/dist/orchestrator/journal.d.ts +46 -0
  48. package/dist/orchestrator/journal.d.ts.map +1 -0
  49. package/dist/orchestrator/journal.js +82 -0
  50. package/dist/orchestrator/journal.js.map +1 -0
  51. package/dist/orchestrator/recap.d.ts +38 -0
  52. package/dist/orchestrator/recap.d.ts.map +1 -0
  53. package/dist/orchestrator/recap.js +134 -0
  54. package/dist/orchestrator/recap.js.map +1 -0
  55. package/dist/screen_advisor/engine.d.ts +79 -0
  56. package/dist/screen_advisor/engine.d.ts.map +1 -0
  57. package/dist/screen_advisor/engine.js +128 -0
  58. package/dist/screen_advisor/engine.js.map +1 -0
  59. package/dist/soul/lock.d.ts +7 -4
  60. package/dist/soul/lock.d.ts.map +1 -1
  61. package/dist/soul/lock.js +33 -9
  62. package/dist/soul/lock.js.map +1 -1
  63. package/dist/tools/agent_recap.d.ts +21 -0
  64. package/dist/tools/agent_recap.d.ts.map +1 -0
  65. package/dist/tools/agent_recap.js +43 -0
  66. package/dist/tools/agent_recap.js.map +1 -0
  67. package/dist/tools/compare_agents.d.ts +16 -0
  68. package/dist/tools/compare_agents.d.ts.map +1 -0
  69. package/dist/tools/compare_agents.js +145 -0
  70. package/dist/tools/compare_agents.js.map +1 -0
  71. package/dist/tools/dispatch_agent.d.ts +13 -0
  72. package/dist/tools/dispatch_agent.d.ts.map +1 -1
  73. package/dist/tools/dispatch_agent.js +46 -46
  74. package/dist/tools/dispatch_agent.js.map +1 -1
  75. package/dist/tools/exec-util.d.ts +18 -0
  76. package/dist/tools/exec-util.d.ts.map +1 -0
  77. package/dist/tools/exec-util.js +72 -0
  78. package/dist/tools/exec-util.js.map +1 -0
  79. package/dist/tools/github.d.ts +34 -0
  80. package/dist/tools/github.d.ts.map +1 -0
  81. package/dist/tools/github.js +140 -0
  82. package/dist/tools/github.js.map +1 -0
  83. package/dist/tools/github_link.d.ts +36 -0
  84. package/dist/tools/github_link.d.ts.map +1 -0
  85. package/dist/tools/github_link.js +127 -0
  86. package/dist/tools/github_link.js.map +1 -0
  87. package/dist/tools/inspect_agent.d.ts +21 -0
  88. package/dist/tools/inspect_agent.d.ts.map +1 -0
  89. package/dist/tools/inspect_agent.js +87 -0
  90. package/dist/tools/inspect_agent.js.map +1 -0
  91. package/dist/tools/list_agents.d.ts +21 -0
  92. package/dist/tools/list_agents.d.ts.map +1 -0
  93. package/dist/tools/list_agents.js +88 -0
  94. package/dist/tools/list_agents.js.map +1 -0
  95. package/dist/tools/mcp.d.ts +28 -0
  96. package/dist/tools/mcp.d.ts.map +1 -0
  97. package/dist/tools/mcp.js +55 -0
  98. package/dist/tools/mcp.js.map +1 -0
  99. package/dist/tools/npm_info.d.ts +21 -0
  100. package/dist/tools/npm_info.d.ts.map +1 -0
  101. package/dist/tools/npm_info.js +102 -0
  102. package/dist/tools/npm_info.js.map +1 -0
  103. package/dist/tools/pr_status.d.ts +31 -0
  104. package/dist/tools/pr_status.d.ts.map +1 -0
  105. package/dist/tools/pr_status.js +90 -0
  106. package/dist/tools/pr_status.js.map +1 -0
  107. package/dist/tools/registry.d.ts.map +1 -1
  108. package/dist/tools/registry.js +27 -0
  109. package/dist/tools/registry.js.map +1 -1
  110. package/dist/tools/repo_digest.d.ts +33 -0
  111. package/dist/tools/repo_digest.d.ts.map +1 -0
  112. package/dist/tools/repo_digest.js +113 -0
  113. package/dist/tools/repo_digest.js.map +1 -0
  114. package/dist/tools/review_diff.d.ts +28 -0
  115. package/dist/tools/review_diff.d.ts.map +1 -0
  116. package/dist/tools/review_diff.js +68 -0
  117. package/dist/tools/review_diff.js.map +1 -0
  118. package/dist/tools/run_checks.d.ts +15 -0
  119. package/dist/tools/run_checks.d.ts.map +1 -0
  120. package/dist/tools/run_checks.js +117 -0
  121. package/dist/tools/run_checks.js.map +1 -0
  122. package/dist/tools/scheduled_dispatch.d.ts +27 -0
  123. package/dist/tools/scheduled_dispatch.d.ts.map +1 -0
  124. package/dist/tools/scheduled_dispatch.js +57 -0
  125. package/dist/tools/scheduled_dispatch.js.map +1 -0
  126. package/dist/voice/dictation.d.ts +51 -0
  127. package/dist/voice/dictation.d.ts.map +1 -0
  128. package/dist/voice/dictation.js +70 -0
  129. package/dist/voice/dictation.js.map +1 -0
  130. package/dist/web/island.d.ts +1 -1
  131. package/dist/web/island.d.ts.map +1 -1
  132. package/dist/web/island.js +84 -8
  133. package/dist/web/island.js.map +1 -1
  134. package/dist/web/lisa-html.d.ts +1 -1
  135. package/dist/web/lisa-html.d.ts.map +1 -1
  136. package/dist/web/lisa-html.js +80 -13
  137. package/dist/web/lisa-html.js.map +1 -1
  138. package/dist/web/server.d.ts.map +1 -1
  139. package/dist/web/server.js +156 -1
  140. package/dist/web/server.js.map +1 -1
  141. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Recap (L6) — synthesize the orchestrator journal into a cross-agent
3
+ * "while you were away" digest. Deterministic (no LLM): given the journal
4
+ * events in a time window, group by project, tally how each session ended,
5
+ * and surface the notable moments (errors, finishes, merged PRs, blocks).
6
+ *
7
+ * This is the narrative counterpart to the advisor's alerts and list_agents'
8
+ * roster: it answers "what happened, across everything, since I left?"
9
+ */
10
+ const FILES_PER_PROJECT = 6;
11
+ function basename(p) {
12
+ return p.split("/").pop() || p;
13
+ }
14
+ /** Build the structured recap from journal events in [sinceMs, now]. Pure. */
15
+ export function buildRecap(events, sinceMs, now) {
16
+ const inWindow = events.filter((e) => e.at >= sinceMs);
17
+ // Roll up per session (the latest meaningful state wins).
18
+ const sessions = new Map();
19
+ for (const e of inWindow) {
20
+ let r = sessions.get(e.sessionId);
21
+ if (!r) {
22
+ r = {
23
+ agent: e.agent,
24
+ project: e.project || "(unknown)",
25
+ lastState: e.state,
26
+ lastReason: e.stateReason,
27
+ lastAt: e.at,
28
+ files: new Set(),
29
+ errored: false,
30
+ };
31
+ sessions.set(e.sessionId, r);
32
+ }
33
+ if (e.at >= r.lastAt) {
34
+ r.lastState = e.state;
35
+ r.lastReason = e.stateReason;
36
+ r.lastAt = e.at;
37
+ if (e.activity)
38
+ r.lastActivity = e.activity;
39
+ }
40
+ if (e.state === "error") {
41
+ r.errored = true;
42
+ if (e.error || e.stateReason)
43
+ r.lastError = e.error || e.stateReason;
44
+ }
45
+ if (e.activity) {
46
+ // The activity summary's last token is often a file basename; cheap to keep.
47
+ const f = e.activity.split(" · ").find((t) => /\.[a-z0-9]+$/i.test(t));
48
+ if (f)
49
+ r.files.add(f);
50
+ }
51
+ }
52
+ // Group sessions by project.
53
+ const byProject = new Map();
54
+ for (const r of sessions.values()) {
55
+ const list = byProject.get(r.project) ?? [];
56
+ list.push(r);
57
+ byProject.set(r.project, list);
58
+ }
59
+ const projects = [];
60
+ let totalFinished = 0, totalErrored = 0, totalActive = 0;
61
+ for (const [project, rolls] of byProject) {
62
+ const agents = [...new Set(rolls.map((r) => r.agent))].sort();
63
+ let finished = 0, errored = 0, active = 0;
64
+ const files = new Set();
65
+ const notable = [];
66
+ for (const r of rolls) {
67
+ if (r.errored) {
68
+ errored++;
69
+ notable.push(`✗ ${r.agent} errored${r.lastError ? `: ${r.lastError}` : ""}`);
70
+ }
71
+ else if (r.lastState === "done") {
72
+ finished++;
73
+ notable.push(`✓ ${r.agent} finished${r.lastReason ? ` (${r.lastReason})` : ""}`);
74
+ }
75
+ else if (r.lastState === "working" || r.lastState === "waiting") {
76
+ active++;
77
+ if (r.lastState === "waiting" && r.lastReason)
78
+ notable.push(`• ${r.agent} waiting — ${r.lastReason}`);
79
+ }
80
+ for (const f of r.files)
81
+ files.add(basename(f));
82
+ }
83
+ totalFinished += finished;
84
+ totalErrored += errored;
85
+ totalActive += active;
86
+ projects.push({
87
+ project,
88
+ agents,
89
+ sessions: rolls.length,
90
+ finished,
91
+ errored,
92
+ active,
93
+ files: [...files].slice(0, FILES_PER_PROJECT),
94
+ notable: notable.slice(0, 6),
95
+ });
96
+ }
97
+ // Errors first, then most sessions, then name.
98
+ projects.sort((a, b) => b.errored - a.errored || b.sessions - a.sessions || a.project.localeCompare(b.project));
99
+ const parts = [];
100
+ if (totalFinished)
101
+ parts.push(`${totalFinished} finished`);
102
+ if (totalErrored)
103
+ parts.push(`${totalErrored} errored`);
104
+ if (totalActive)
105
+ parts.push(`${totalActive} still going`);
106
+ const headline = sessions.size === 0
107
+ ? "No agent activity in this window."
108
+ : `${sessions.size} agent session${sessions.size === 1 ? "" : "s"} across ` +
109
+ `${projects.length} project${projects.length === 1 ? "" : "s"}` +
110
+ (parts.length ? ` — ${parts.join(", ")}.` : ".");
111
+ return {
112
+ sinceMs,
113
+ now,
114
+ totalSessions: sessions.size,
115
+ totalEvents: inWindow.length,
116
+ projects,
117
+ headline,
118
+ };
119
+ }
120
+ /** Render the recap as a readable multi-line digest. Pure. */
121
+ export function formatRecap(recap) {
122
+ if (recap.totalSessions === 0)
123
+ return recap.headline;
124
+ const lines = [recap.headline, ""];
125
+ for (const p of recap.projects) {
126
+ lines.push(`${p.project} · ${p.agents.join(", ")}`);
127
+ for (const n of p.notable)
128
+ lines.push(` ${n}`);
129
+ if (p.files.length)
130
+ lines.push(` touched: ${p.files.join(", ")}`);
131
+ }
132
+ return lines.join("\n");
133
+ }
134
+ //# sourceMappingURL=recap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recap.js","sourceRoot":"","sources":["../../src/orchestrator/recap.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Screen advisor — periodic, opt-in screen-aware coaching.
3
+ *
4
+ * When enabled, the server captures a full-screen screenshot every N minutes,
5
+ * asks the model for the single best next coding step grounded in what's on
6
+ * screen, and pushes it to the Lisa Island as a suggestion card. Clicking it
7
+ * prefills the suggestion into the chat composer so the user can confirm before
8
+ * any coding agent is dispatched.
9
+ *
10
+ * PRIVACY (this is a sensitive capability — treat it as such):
11
+ * - DISABLED by default. Nothing is captured until the user turns it on in
12
+ * Settings.
13
+ * - The screenshot only leaves the machine for the single analysis call to
14
+ * the model the user already uses; the image is never persisted by LISA
15
+ * (capture writes a temp PNG that the capture layer deletes), and only the
16
+ * short text suggestion is kept in memory.
17
+ * - The suggestion never auto-runs anything: clicking it only prefills chat.
18
+ *
19
+ * This module is provider-agnostic and side-effect-light so the parsing and
20
+ * config logic can be unit-tested without a real LLM or a real screen.
21
+ */
22
+ export interface ScreenAdvisorConfig {
23
+ /** Master switch. Off by default — capture only happens when true. */
24
+ enabled: boolean;
25
+ /** Minutes between captures. Clamped to [MIN, MAX]. */
26
+ intervalMinutes: number;
27
+ }
28
+ export declare const DEFAULT_SCREEN_ADVISOR_CONFIG: ScreenAdvisorConfig;
29
+ export declare const MIN_INTERVAL_MINUTES = 2;
30
+ export declare const MAX_INTERVAL_MINUTES = 240;
31
+ export declare const SCREEN_ADVISOR_CONFIG_PATH: string;
32
+ /** Coerce arbitrary input into a valid config (clamped interval, boolean enabled). */
33
+ export declare function normalizeConfig(raw: Partial<ScreenAdvisorConfig> | null | undefined): ScreenAdvisorConfig;
34
+ export declare function loadScreenAdvisorConfig(p?: string): Promise<ScreenAdvisorConfig>;
35
+ export declare function saveScreenAdvisorConfig(cfg: ScreenAdvisorConfig, p?: string): Promise<ScreenAdvisorConfig>;
36
+ /** A single screen-grounded next-step suggestion. */
37
+ export interface ScreenSuggestion {
38
+ /** ≤8-word imperative headline. */
39
+ title: string;
40
+ /** 1–2 sentences citing what was on screen. */
41
+ rationale: string;
42
+ /** Self-contained prompt a coding agent could act on. */
43
+ task: string;
44
+ /** ISO timestamp, stamped by the caller. */
45
+ at: string;
46
+ }
47
+ export declare const SCREEN_ADVISOR_SYSTEM = "You are Lisa's screen advisor. You are shown ONE screenshot of the user's screen. Suggest the single highest-value next CODING step, grounded in what is actually visible \u2014 an error, a failing test, a TODO/FIXME, an open file, a diff, a PR, a design. Be specific and actionable: prefer something a coding agent could pick up and finish.\n\nRespond with ONLY a compact JSON object \u2014 no prose, no markdown, no code fences:\n{\"title\":\"<=8-word imperative headline\",\"rationale\":\"1-2 sentences citing what you saw\",\"task\":\"a self-contained prompt for a coding agent: exactly what to do, and in which file/dir if visible\"}\n\nIf the screen shows nothing actionable for coding (a video, a chat, the desktop, unrelated browsing), respond with exactly: {\"skip\":true}";
48
+ /**
49
+ * Tolerantly parse the model's reply into a suggestion. Strips code fences,
50
+ * extracts the first JSON object, and returns null for {"skip":true} or any
51
+ * malformed / incomplete reply (so the loop simply surfaces nothing).
52
+ */
53
+ export declare function parseSuggestion(text: string | null | undefined): Omit<ScreenSuggestion, "at"> | null;
54
+ /** Minimal provider surface the analyzer needs (keeps the engine testable). */
55
+ export interface SuggestionProvider {
56
+ runTurn(opts: {
57
+ systemPrompt: string;
58
+ messages: Array<{
59
+ role: "user" | "assistant";
60
+ content: unknown;
61
+ }>;
62
+ tools: unknown[];
63
+ model: string;
64
+ maxTokens?: number;
65
+ }): Promise<{
66
+ content: Array<{
67
+ type: string;
68
+ text?: string;
69
+ }>;
70
+ }>;
71
+ }
72
+ /** Run one screenshot through the model and parse the suggestion (or null). */
73
+ export declare function analyzeScreenshot(opts: {
74
+ provider: SuggestionProvider;
75
+ model: string;
76
+ imageBase64: string;
77
+ mediaType?: string;
78
+ }): Promise<Omit<ScreenSuggestion, "at"> | null>;
79
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/screen_advisor/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAMH,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,6BAA6B,EAAE,mBAG3C,CAAC;AAEF,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,eAAO,MAAM,0BAA0B,QAA8C,CAAC;AAEtF,sFAAsF;AACtF,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,CAAC,mBAAmB,CAAC,GAAG,IAAI,GAAG,SAAS,GACnD,mBAAmB,CAQrB;AAED,wBAAsB,uBAAuB,CAC3C,CAAC,GAAE,MAAmC,GACrC,OAAO,CAAC,mBAAmB,CAAC,CAO9B;AAED,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,mBAAmB,EACxB,CAAC,GAAE,MAAmC,GACrC,OAAO,CAAC,mBAAmB,CAAC,CAK9B;AAED,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;IAClB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAC;IACb,4CAA4C;IAC5C,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,eAAO,MAAM,qBAAqB,ixBAKwG,CAAC;AAE3I;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,IAAI,CAwBpG;AAED,+EAA+E;AAC/E,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,IAAI,EAAE;QACZ,YAAY,EAAE,MAAM,CAAC;QACrB,QAAQ,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;QAClE,KAAK,EAAE,OAAO,EAAE,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC,CAAC;CAClE;AAED,+EAA+E;AAC/E,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GAAG,OAAO,CAAC,IAAI,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,IAAI,CAAC,CAyB/C"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Screen advisor — periodic, opt-in screen-aware coaching.
3
+ *
4
+ * When enabled, the server captures a full-screen screenshot every N minutes,
5
+ * asks the model for the single best next coding step grounded in what's on
6
+ * screen, and pushes it to the Lisa Island as a suggestion card. Clicking it
7
+ * prefills the suggestion into the chat composer so the user can confirm before
8
+ * any coding agent is dispatched.
9
+ *
10
+ * PRIVACY (this is a sensitive capability — treat it as such):
11
+ * - DISABLED by default. Nothing is captured until the user turns it on in
12
+ * Settings.
13
+ * - The screenshot only leaves the machine for the single analysis call to
14
+ * the model the user already uses; the image is never persisted by LISA
15
+ * (capture writes a temp PNG that the capture layer deletes), and only the
16
+ * short text suggestion is kept in memory.
17
+ * - The suggestion never auto-runs anything: clicking it only prefills chat.
18
+ *
19
+ * This module is provider-agnostic and side-effect-light so the parsing and
20
+ * config logic can be unit-tested without a real LLM or a real screen.
21
+ */
22
+ import fsp from "node:fs/promises";
23
+ import path from "node:path";
24
+ import { LISA_HOME } from "../paths.js";
25
+ export const DEFAULT_SCREEN_ADVISOR_CONFIG = {
26
+ enabled: false,
27
+ intervalMinutes: 10,
28
+ };
29
+ export const MIN_INTERVAL_MINUTES = 2;
30
+ export const MAX_INTERVAL_MINUTES = 240;
31
+ export const SCREEN_ADVISOR_CONFIG_PATH = path.join(LISA_HOME, "screen-advisor.json");
32
+ /** Coerce arbitrary input into a valid config (clamped interval, boolean enabled). */
33
+ export function normalizeConfig(raw) {
34
+ const enabled = raw?.enabled === true;
35
+ let n = Number(raw?.intervalMinutes);
36
+ if (!Number.isFinite(n))
37
+ n = DEFAULT_SCREEN_ADVISOR_CONFIG.intervalMinutes;
38
+ n = Math.round(n);
39
+ if (n < MIN_INTERVAL_MINUTES)
40
+ n = MIN_INTERVAL_MINUTES;
41
+ if (n > MAX_INTERVAL_MINUTES)
42
+ n = MAX_INTERVAL_MINUTES;
43
+ return { enabled, intervalMinutes: n };
44
+ }
45
+ export async function loadScreenAdvisorConfig(p = SCREEN_ADVISOR_CONFIG_PATH) {
46
+ try {
47
+ const raw = JSON.parse(await fsp.readFile(p, "utf8"));
48
+ return normalizeConfig(raw);
49
+ }
50
+ catch {
51
+ return { ...DEFAULT_SCREEN_ADVISOR_CONFIG };
52
+ }
53
+ }
54
+ export async function saveScreenAdvisorConfig(cfg, p = SCREEN_ADVISOR_CONFIG_PATH) {
55
+ const norm = normalizeConfig(cfg);
56
+ await fsp.mkdir(path.dirname(p), { recursive: true });
57
+ await fsp.writeFile(p, JSON.stringify(norm, null, 2));
58
+ return norm;
59
+ }
60
+ export const SCREEN_ADVISOR_SYSTEM = `You are Lisa's screen advisor. You are shown ONE screenshot of the user's screen. Suggest the single highest-value next CODING step, grounded in what is actually visible — an error, a failing test, a TODO/FIXME, an open file, a diff, a PR, a design. Be specific and actionable: prefer something a coding agent could pick up and finish.
61
+
62
+ Respond with ONLY a compact JSON object — no prose, no markdown, no code fences:
63
+ {"title":"<=8-word imperative headline","rationale":"1-2 sentences citing what you saw","task":"a self-contained prompt for a coding agent: exactly what to do, and in which file/dir if visible"}
64
+
65
+ If the screen shows nothing actionable for coding (a video, a chat, the desktop, unrelated browsing), respond with exactly: {"skip":true}`;
66
+ /**
67
+ * Tolerantly parse the model's reply into a suggestion. Strips code fences,
68
+ * extracts the first JSON object, and returns null for {"skip":true} or any
69
+ * malformed / incomplete reply (so the loop simply surfaces nothing).
70
+ */
71
+ export function parseSuggestion(text) {
72
+ if (!text)
73
+ return null;
74
+ let s = text.trim();
75
+ // Drop a leading ```json / ``` fence and a trailing fence if present.
76
+ s = s.replace(/^```[a-zA-Z]*\s*/, "").replace(/\s*```$/, "").trim();
77
+ const a = s.indexOf("{");
78
+ const b = s.lastIndexOf("}");
79
+ if (a < 0 || b <= a)
80
+ return null;
81
+ let obj;
82
+ try {
83
+ obj = JSON.parse(s.slice(a, b + 1));
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ if (obj.skip === true)
89
+ return null;
90
+ const title = typeof obj.title === "string" ? obj.title.trim() : "";
91
+ const task = typeof obj.task === "string" ? obj.task.trim() : "";
92
+ if (!title || !task)
93
+ return null;
94
+ const rationale = typeof obj.rationale === "string" ? obj.rationale.trim() : "";
95
+ return {
96
+ title: title.slice(0, 120),
97
+ rationale: rationale.slice(0, 400),
98
+ task: task.slice(0, 2000),
99
+ };
100
+ }
101
+ /** Run one screenshot through the model and parse the suggestion (or null). */
102
+ export async function analyzeScreenshot(opts) {
103
+ const result = await opts.provider.runTurn({
104
+ systemPrompt: SCREEN_ADVISOR_SYSTEM,
105
+ messages: [
106
+ {
107
+ role: "user",
108
+ content: [
109
+ { type: "text", text: "Here is my screen right now. What is the single best next coding step?" },
110
+ {
111
+ type: "image",
112
+ source: {
113
+ type: "base64",
114
+ media_type: opts.mediaType ?? "image/png",
115
+ data: opts.imageBase64,
116
+ },
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ tools: [],
122
+ model: opts.model,
123
+ maxTokens: 600,
124
+ });
125
+ const textBlock = result.content.find((b) => b.type === "text");
126
+ return parseSuggestion(textBlock?.text ?? "");
127
+ }
128
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/screen_advisor/engine.ts"],"names":[],"mappings":"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"}
@@ -9,10 +9,13 @@
9
9
  * - lose a desire-progress append (read-modify-write race), or
10
10
  * - collide on .git/index.lock (one commit fails).
11
11
  *
12
- * withFileLock implements a portable advisory mutex via exclusive file
13
- * creation (`open(..., "wx")` succeeds only if the file doesn't exist). It
14
- * self-heals from a crashed holder via a staleness timeout + a best-effort
15
- * liveness check on the recorded pid.
12
+ * withFileLock implements a portable advisory mutex by writing the lock body to
13
+ * a private temp file and then `link()`-ing it into place link() fails if the
14
+ * target exists, so the lock is created exclusively AND already holds its full
15
+ * content (no window where a contender can read a half-written file and mistake
16
+ * it for stale). It self-heals from a crashed holder via a staleness timeout +
17
+ * a best-effort liveness check on the recorded pid, stealing atomically by
18
+ * renaming the stale file away so racing contenders can't clobber each other.
16
19
  */
17
20
  export interface FileLockOpts {
18
21
  /** Treat a held lock as abandoned after this many ms (crashed holder). */
@@ -1 +1 @@
1
- {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAoCD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,CAAC,CAAC,CAmCZ;AAED,0CAA0C;AAC1C,eAAO,MAAM,cAAc,QAAqC,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAErF"}
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAOH,MAAM,WAAW,YAAY;IAC3B,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4CAA4C;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAyCD;;;GAGG;AACH,wBAAsB,YAAY,CAAC,CAAC,EAClC,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,CAAC,CAAC,CAkDZ;AAED,0CAA0C;AAC1C,eAAO,MAAM,cAAc,QAAqC,CAAC;AAEjE;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC,CAAC,CAErF"}
package/dist/soul/lock.js CHANGED
@@ -9,10 +9,13 @@
9
9
  * - lose a desire-progress append (read-modify-write race), or
10
10
  * - collide on .git/index.lock (one commit fails).
11
11
  *
12
- * withFileLock implements a portable advisory mutex via exclusive file
13
- * creation (`open(..., "wx")` succeeds only if the file doesn't exist). It
14
- * self-heals from a crashed holder via a staleness timeout + a best-effort
15
- * liveness check on the recorded pid.
12
+ * withFileLock implements a portable advisory mutex by writing the lock body to
13
+ * a private temp file and then `link()`-ing it into place link() fails if the
14
+ * target exists, so the lock is created exclusively AND already holds its full
15
+ * content (no window where a contender can read a half-written file and mistake
16
+ * it for stale). It self-heals from a crashed holder via a staleness timeout +
17
+ * a best-effort liveness check on the recorded pid, stealing atomically by
18
+ * renaming the stale file away so racing contenders can't clobber each other.
16
19
  */
17
20
  import fsp from "node:fs/promises";
18
21
  import path from "node:path";
@@ -28,7 +31,13 @@ async function isStale(lockPath, staleMs) {
28
31
  try {
29
32
  body = JSON.parse(await fsp.readFile(lockPath, "utf8"));
30
33
  }
31
- catch {
34
+ catch (e) {
35
+ // The file vanished between the failed create and our read — the holder
36
+ // released it. There's nothing to steal; let the caller retry the create
37
+ // race. (Treating this as "stale" would race a concurrent acquirer into an
38
+ // unconditional rm that could delete a *different* holder's fresh lock.)
39
+ if (e.code === "ENOENT")
40
+ return false;
32
41
  // Unreadable / malformed lock file → treat as stale so we can recover.
33
42
  return true;
34
43
  }
@@ -58,22 +67,37 @@ export async function withFileLock(lockPath, fn, opts = {}) {
58
67
  const deadline = Date.now() + timeoutMs;
59
68
  // ── acquire ──
60
69
  for (;;) {
70
+ // Write the lock body to a private temp file first, then atomically link it
71
+ // into place. link() fails with EEXIST if the lock already exists, so
72
+ // exclusive creation and "the lock file always has complete content" hold
73
+ // simultaneously — a contender can never observe a half-written lock and
74
+ // mistake it for malformed/stale. The temp name is unique per attempt so
75
+ // concurrent acquirers in the *same* process don't clobber each other.
76
+ const tmp = `${lockPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
61
77
  try {
62
- const fh = await fsp.open(lockPath, "wx");
78
+ await fsp.writeFile(tmp, JSON.stringify({ pid: process.pid, ts: Date.now() }));
63
79
  try {
64
- await fh.writeFile(JSON.stringify({ pid: process.pid, ts: Date.now() }));
80
+ await fsp.link(tmp, lockPath);
65
81
  }
66
82
  finally {
67
- await fh.close();
83
+ await fsp.rm(tmp, { force: true }).catch(() => { });
68
84
  }
69
85
  break; // acquired
70
86
  }
71
87
  catch (e) {
88
+ await fsp.rm(tmp, { force: true }).catch(() => { });
72
89
  if (e.code !== "EEXIST")
73
90
  throw e;
74
91
  // Held by someone. Steal if stale, else wait.
75
92
  if (await isStale(lockPath, staleMs)) {
76
- await fsp.rm(lockPath, { force: true }).catch(() => { });
93
+ // Steal atomically: rename the stale file to a unique name so only one
94
+ // contender can claim it. Losers get ENOENT and just retry the link
95
+ // race — nobody unconditionally rm's a path that may already hold a
96
+ // fresh lock from a concurrent acquirer.
97
+ await fsp
98
+ .rename(lockPath, tmp)
99
+ .then(() => fsp.rm(tmp, { force: true }))
100
+ .catch(() => { });
77
101
  continue; // retry immediately
78
102
  }
79
103
  if (Date.now() >= deadline) {
@@ -1 +1 @@
1
- {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/soul/lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * agent_recap (L6) — the cross-agent "while you were away" digest.
3
+ *
4
+ * Answers "what happened across all my agents since I left?" by synthesizing
5
+ * the orchestrator journal (every observed session's state transitions over
6
+ * time) into a readable, project-grouped recap: who ran where, what finished,
7
+ * what errored, what's still going. Complements advise_now (relevance-gated
8
+ * alerts, now) and list_agents (the current roster) with the temporal story —
9
+ * including sessions that have since ended and dropped out of the live list.
10
+ *
11
+ * Structural metadata only (agent, project, state, tool/file names, errors) —
12
+ * never prompts, replies, or file contents. Read-only.
13
+ */
14
+ import type { ToolDefinition } from "../types.js";
15
+ interface RecapInput {
16
+ /** Look back this many minutes (default 120, clamped 1–1440). */
17
+ sinceMinutes?: number;
18
+ }
19
+ export declare const agentRecapTool: ToolDefinition<RecapInput, string>;
20
+ export {};
21
+ //# sourceMappingURL=agent_recap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent_recap.d.ts","sourceRoot":"","sources":["../../src/tools/agent_recap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,UAAU,UAAU;IAClB,iEAAiE;IACjE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,UAAU,EAAE,MAAM,CA2B7D,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * agent_recap (L6) — the cross-agent "while you were away" digest.
3
+ *
4
+ * Answers "what happened across all my agents since I left?" by synthesizing
5
+ * the orchestrator journal (every observed session's state transitions over
6
+ * time) into a readable, project-grouped recap: who ran where, what finished,
7
+ * what errored, what's still going. Complements advise_now (relevance-gated
8
+ * alerts, now) and list_agents (the current roster) with the temporal story —
9
+ * including sessions that have since ended and dropped out of the live list.
10
+ *
11
+ * Structural metadata only (agent, project, state, tool/file names, errors) —
12
+ * never prompts, replies, or file contents. Read-only.
13
+ */
14
+ import { eventsSince } from "../orchestrator/journal.js";
15
+ import { buildRecap, formatRecap } from "../orchestrator/recap.js";
16
+ export const agentRecapTool = {
17
+ name: "agent_recap",
18
+ description: "Summarize what happened across ALL the coding agents LISA observes (Claude Code, " +
19
+ "Codex, OpenCode, Aider, GitHub PRs) over a recent time window — the cross-agent " +
20
+ '"while you were away" recap. Use when the user asks what their agents did, what ' +
21
+ "happened since they were gone, or for an end-of-session wrap-up. Groups by project " +
22
+ "and reports what finished, errored, or is still running. Structural metadata only — " +
23
+ "never conversation content. Read-only.",
24
+ inputSchema: {
25
+ type: "object",
26
+ properties: {
27
+ sinceMinutes: {
28
+ type: "number",
29
+ description: "Window to look back over, in minutes (default 120; max 1440).",
30
+ },
31
+ },
32
+ additionalProperties: false,
33
+ },
34
+ async execute(input) {
35
+ const mins = Math.max(1, Math.min(1440, Math.round(input.sinceMinutes ?? 120)));
36
+ const now = Date.now();
37
+ const sinceMs = now - mins * 60_000;
38
+ const recap = buildRecap(eventsSince(sinceMs), sinceMs, now);
39
+ const window = mins >= 60 ? `${Math.round(mins / 60)}h` : `${mins}m`;
40
+ return `Cross-agent recap (last ${window}):\n${formatRecap(recap)}`;
41
+ },
42
+ };
43
+ //# sourceMappingURL=agent_recap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent_recap.js","sourceRoot":"","sources":["../../src/tools/agent_recap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAOnE,MAAM,CAAC,MAAM,cAAc,GAAuC;IAChE,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,mFAAmF;QACnF,kFAAkF;QAClF,kFAAkF;QAClF,qFAAqF;QACrF,sFAAsF;QACtF,wCAAwC;IAC1C,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,YAAY,EAAE;gBACZ,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,+DAA+D;aAC7E;SACF;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,KAAK;QACjB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,YAAY,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;QAChF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,IAAI,GAAG,MAAM,CAAC;QACpC,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC;QACrE,OAAO,2BAA2B,MAAM,OAAO,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;IACtE,CAAC;CACF,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { ToolDefinition } from "../types.js";
2
+ import { type ComparisonJob, type ComparisonEntry, type CompareAgentKind } from "../integrations/comparisons.js";
3
+ interface CompareInput {
4
+ action?: "start" | "status" | "collect" | "cleanup" | "list";
5
+ task?: string;
6
+ agents?: CompareAgentKind[];
7
+ cwd?: string;
8
+ id?: string;
9
+ }
10
+ /** Pure: one line summarising an entry's live status. Exported for tests. */
11
+ export declare function formatStatusLine(e: ComparisonEntry, state: string | null, changed: number): string;
12
+ /** Pure: header line for a job. Exported for tests. */
13
+ export declare function formatJobHeader(j: ComparisonJob): string;
14
+ export declare const compareAgentsTool: ToolDefinition<CompareInput, string>;
15
+ export {};
16
+ //# sourceMappingURL=compare_agents.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare_agents.d.ts","sourceRoot":"","sources":["../../src/tools/compare_agents.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,OAAO,EAOL,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACtB,MAAM,gCAAgC,CAAC;AAExC,UAAU,YAAY;IACpB,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAC;IAC7D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAIlG;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,CAAC,EAAE,aAAa,GAAG,MAAM,CAGxD;AAOD,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,YAAY,EAAE,MAAM,CA8GlE,CAAC"}