@oratis/lisa 0.3.1 → 0.5.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 (70) hide show
  1. package/dist/advisor/detectors.d.ts +25 -0
  2. package/dist/advisor/detectors.d.ts.map +1 -0
  3. package/dist/advisor/detectors.js +161 -0
  4. package/dist/advisor/detectors.js.map +1 -0
  5. package/dist/advisor/engine.d.ts +41 -0
  6. package/dist/advisor/engine.d.ts.map +1 -0
  7. package/dist/advisor/engine.js +156 -0
  8. package/dist/advisor/engine.js.map +1 -0
  9. package/dist/advisor/types.d.ts +68 -0
  10. package/dist/advisor/types.d.ts.map +1 -0
  11. package/dist/advisor/types.js +19 -0
  12. package/dist/advisor/types.js.map +1 -0
  13. package/dist/integrations/claude-code/observer.d.ts +19 -0
  14. package/dist/integrations/claude-code/observer.d.ts.map +1 -0
  15. package/dist/integrations/claude-code/observer.js +59 -0
  16. package/dist/integrations/claude-code/observer.js.map +1 -0
  17. package/dist/integrations/claude-code/parser.d.ts +2 -0
  18. package/dist/integrations/claude-code/parser.d.ts.map +1 -1
  19. package/dist/integrations/claude-code/parser.js +153 -0
  20. package/dist/integrations/claude-code/parser.js.map +1 -1
  21. package/dist/integrations/claude-code/watcher.d.ts +9 -0
  22. package/dist/integrations/claude-code/watcher.d.ts.map +1 -1
  23. package/dist/integrations/claude-code/watcher.js +25 -4
  24. package/dist/integrations/claude-code/watcher.js.map +1 -1
  25. package/dist/integrations/codex/observer.d.ts +48 -0
  26. package/dist/integrations/codex/observer.d.ts.map +1 -0
  27. package/dist/integrations/codex/observer.js +213 -0
  28. package/dist/integrations/codex/observer.js.map +1 -0
  29. package/dist/integrations/current-hub.d.ts +13 -0
  30. package/dist/integrations/current-hub.d.ts.map +1 -0
  31. package/dist/integrations/current-hub.js +17 -0
  32. package/dist/integrations/current-hub.js.map +1 -0
  33. package/dist/integrations/hub.d.ts +45 -0
  34. package/dist/integrations/hub.d.ts.map +1 -0
  35. package/dist/integrations/hub.js +110 -0
  36. package/dist/integrations/hub.js.map +1 -0
  37. package/dist/integrations/registry.d.ts +15 -0
  38. package/dist/integrations/registry.d.ts.map +1 -0
  39. package/dist/integrations/registry.js +38 -0
  40. package/dist/integrations/registry.js.map +1 -0
  41. package/dist/integrations/types.d.ts +90 -0
  42. package/dist/integrations/types.d.ts.map +1 -0
  43. package/dist/integrations/types.js +12 -0
  44. package/dist/integrations/types.js.map +1 -0
  45. package/dist/tools/advise_now.d.ts +11 -0
  46. package/dist/tools/advise_now.d.ts.map +1 -0
  47. package/dist/tools/advise_now.js +34 -0
  48. package/dist/tools/advise_now.js.map +1 -0
  49. package/dist/tools/dispatch_agent.d.ts +38 -0
  50. package/dist/tools/dispatch_agent.d.ts.map +1 -0
  51. package/dist/tools/dispatch_agent.js +145 -0
  52. package/dist/tools/dispatch_agent.js.map +1 -0
  53. package/dist/tools/registry.d.ts.map +1 -1
  54. package/dist/tools/registry.js.map +1 -1
  55. package/dist/vision/capture.d.ts +44 -0
  56. package/dist/vision/capture.d.ts.map +1 -0
  57. package/dist/vision/capture.js +86 -0
  58. package/dist/vision/capture.js.map +1 -0
  59. package/dist/web/island.d.ts +1 -1
  60. package/dist/web/island.d.ts.map +1 -1
  61. package/dist/web/island.js +43 -0
  62. package/dist/web/island.js.map +1 -1
  63. package/dist/web/lisa-html.d.ts +1 -1
  64. package/dist/web/lisa-html.d.ts.map +1 -1
  65. package/dist/web/lisa-html.js +61 -4
  66. package/dist/web/lisa-html.js.map +1 -1
  67. package/dist/web/server.d.ts.map +1 -1
  68. package/dist/web/server.js +112 -17
  69. package/dist/web/server.js.map +1 -1
  70. package/package.json +1 -1
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Process-wide handle to the live OrchestratorHub.
3
+ *
4
+ * The hub's session state lives in memory in the web-server process. The
5
+ * advise_now tool (which runs in that same process during a chat turn) and
6
+ * the server's periodic advisor tick both need it, so we expose it as a
7
+ * tiny singleton the server sets at startup. Null until the server starts
8
+ * (e.g. CLI-only runs) — callers must handle that.
9
+ */
10
+ import type { OrchestratorHub } from "./hub.js";
11
+ export declare function setCurrentHub(hub: OrchestratorHub | null): void;
12
+ export declare function getCurrentHub(): OrchestratorHub | null;
13
+ //# sourceMappingURL=current-hub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-hub.d.ts","sourceRoot":"","sources":["../../src/integrations/current-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAIhD,wBAAgB,aAAa,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI,GAAG,IAAI,CAE/D;AAED,wBAAgB,aAAa,IAAI,eAAe,GAAG,IAAI,CAEtD"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Process-wide handle to the live OrchestratorHub.
3
+ *
4
+ * The hub's session state lives in memory in the web-server process. The
5
+ * advise_now tool (which runs in that same process during a chat turn) and
6
+ * the server's periodic advisor tick both need it, so we expose it as a
7
+ * tiny singleton the server sets at startup. Null until the server starts
8
+ * (e.g. CLI-only runs) — callers must handle that.
9
+ */
10
+ let current = null;
11
+ export function setCurrentHub(hub) {
12
+ current = hub;
13
+ }
14
+ export function getCurrentHub() {
15
+ return current;
16
+ }
17
+ //# sourceMappingURL=current-hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-hub.js","sourceRoot":"","sources":["../../src/integrations/current-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,IAAI,OAAO,GAA2B,IAAI,CAAC;AAE3C,MAAM,UAAU,aAAa,CAAC,GAA2B;IACvD,OAAO,GAAG,GAAG,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * OrchestratorHub — the one thing the web server talks to for cross-agent
3
+ * session state. Owns every registered AgentObserver, merges their sessions
4
+ * into one normalized list, and re-emits a single "update" event.
5
+ *
6
+ * Replaces the bare single-purpose ClaudeCodeWatcher wiring in server.ts:
7
+ * instead of one watcher, the hub fans out over all enabled integrations.
8
+ */
9
+ import { EventEmitter } from "node:events";
10
+ import type { AgentIntegrationConfig, AgentSession, VisibilityTier } from "./types.js";
11
+ export interface OrchestratorConfig {
12
+ /** Per-integration config, keyed by integration name. */
13
+ integrations: Record<string, AgentIntegrationConfig>;
14
+ /** Global visibility tier; integrations may override per-entry. */
15
+ visibility: VisibilityTier;
16
+ }
17
+ /** Default config when ~/.lisa/agents.json is absent: just Claude Code, at
18
+ * the "activity" tier (Tier 2 — structural, no conversation content). */
19
+ export declare const DEFAULT_ORCHESTRATOR_CONFIG: OrchestratorConfig;
20
+ type Log = (msg: string) => void;
21
+ export declare class OrchestratorHub extends EventEmitter {
22
+ private observers;
23
+ private readonly cfg;
24
+ private readonly log;
25
+ private started;
26
+ private readonly registerBuiltins;
27
+ constructor(cfg: OrchestratorConfig, opts?: {
28
+ log?: Log;
29
+ registerBuiltins?: boolean;
30
+ });
31
+ /** Instantiate + start every enabled integration. Idempotent. */
32
+ start(): Promise<void>;
33
+ stop(): Promise<void>;
34
+ /**
35
+ * Merged snapshot across all observers. Sorted by recency (newest first)
36
+ * so the UI + advisor see a single ranked stream.
37
+ */
38
+ list(): AgentSession[];
39
+ /** Sessions for one agent kind. */
40
+ listByAgent(agent: string): AgentSession[];
41
+ }
42
+ /** Load ~/.lisa/agents.json, falling back to the default config. */
43
+ export declare function loadOrchestratorConfig(path: string): Promise<OrchestratorConfig>;
44
+ export {};
45
+ //# sourceMappingURL=hub.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.d.ts","sourceRoot":"","sources":["../../src/integrations/hub.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EACV,sBAAsB,EAEtB,YAAY,EACZ,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;IACrD,mEAAmE;IACnE,UAAU,EAAE,cAAc,CAAC;CAC5B;AAED;0EAC0E;AAC1E,eAAO,MAAM,2BAA2B,EAAE,kBAQzC,CAAC;AAEF,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;AAEjC,qBAAa,eAAgB,SAAQ,YAAY;IAC/C,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAqB;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAM;IAC1B,OAAO,CAAC,OAAO,CAAS;IAExB,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAU;gBAE/B,GAAG,EAAE,kBAAkB,EAAE,IAAI,GAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAC;QAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;KAAO;IAUzF,iEAAiE;IAC3D,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAuBtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B;;;OAGG;IACH,IAAI,IAAI,YAAY,EAAE;IAYtB,mCAAmC;IACnC,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,EAAE;CAG3C;AAED,oEAAoE;AACpE,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,kBAAkB,CAAC,CAiB7B"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * OrchestratorHub — the one thing the web server talks to for cross-agent
3
+ * session state. Owns every registered AgentObserver, merges their sessions
4
+ * into one normalized list, and re-emits a single "update" event.
5
+ *
6
+ * Replaces the bare single-purpose ClaudeCodeWatcher wiring in server.ts:
7
+ * instead of one watcher, the hub fans out over all enabled integrations.
8
+ */
9
+ import { EventEmitter } from "node:events";
10
+ import { makeIntegration, registerBuiltinIntegrations } from "./registry.js";
11
+ /** Default config when ~/.lisa/agents.json is absent: just Claude Code, at
12
+ * the "activity" tier (Tier 2 — structural, no conversation content). */
13
+ export const DEFAULT_ORCHESTRATOR_CONFIG = {
14
+ integrations: {
15
+ "claude-code": { enabled: true },
16
+ // Available but off by default — enable in ~/.lisa/agents.json once you
17
+ // use Codex. Graceful no-op if ~/.codex/sessions is absent anyway.
18
+ codex: { enabled: false },
19
+ },
20
+ visibility: "activity",
21
+ };
22
+ export class OrchestratorHub extends EventEmitter {
23
+ observers = [];
24
+ cfg;
25
+ log;
26
+ started = false;
27
+ registerBuiltins;
28
+ constructor(cfg, opts = {}) {
29
+ super();
30
+ this.cfg = cfg;
31
+ this.log = opts.log ?? (() => { });
32
+ // Tests pre-register fake observers and set this false so start() doesn't
33
+ // pull in (and clobber with) the real builtin adapters.
34
+ this.registerBuiltins = opts.registerBuiltins ?? true;
35
+ this.setMaxListeners(64);
36
+ }
37
+ /** Instantiate + start every enabled integration. Idempotent. */
38
+ async start() {
39
+ if (this.started)
40
+ return;
41
+ this.started = true;
42
+ if (this.registerBuiltins)
43
+ await registerBuiltinIntegrations();
44
+ for (const [name, entry] of Object.entries(this.cfg.integrations)) {
45
+ if (entry.enabled === false)
46
+ continue;
47
+ // Resolve the effective visibility for this integration.
48
+ const visibility = entry.visibility ?? this.cfg.visibility;
49
+ try {
50
+ const obs = await makeIntegration(name, { ...entry, visibility });
51
+ await obs.start((session) => {
52
+ this.emit("update", session);
53
+ });
54
+ this.observers.push(obs);
55
+ this.log(`[orchestrator] integration "${name}" started (visibility=${visibility})`);
56
+ }
57
+ catch (err) {
58
+ // A bad/unknown integration must not take down the others.
59
+ this.log(`[orchestrator] integration "${name}" failed: ${err.message}`);
60
+ }
61
+ }
62
+ }
63
+ async stop() {
64
+ this.started = false;
65
+ await Promise.all(this.observers.map((o) => o.stop().catch(() => { })));
66
+ this.observers = [];
67
+ }
68
+ /**
69
+ * Merged snapshot across all observers. Sorted by recency (newest first)
70
+ * so the UI + advisor see a single ranked stream.
71
+ */
72
+ list() {
73
+ const all = [];
74
+ for (const o of this.observers) {
75
+ try {
76
+ all.push(...o.list());
77
+ }
78
+ catch {
79
+ // one flaky observer shouldn't break the merge
80
+ }
81
+ }
82
+ return all.sort((a, b) => b.lastMtime - a.lastMtime);
83
+ }
84
+ /** Sessions for one agent kind. */
85
+ listByAgent(agent) {
86
+ return this.list().filter((s) => s.agent === agent);
87
+ }
88
+ }
89
+ /** Load ~/.lisa/agents.json, falling back to the default config. */
90
+ export async function loadOrchestratorConfig(path) {
91
+ const fs = await import("node:fs/promises");
92
+ let raw;
93
+ try {
94
+ raw = await fs.readFile(path, "utf8");
95
+ }
96
+ catch {
97
+ return DEFAULT_ORCHESTRATOR_CONFIG;
98
+ }
99
+ try {
100
+ const parsed = JSON.parse(raw);
101
+ return {
102
+ integrations: parsed.integrations ?? DEFAULT_ORCHESTRATOR_CONFIG.integrations,
103
+ visibility: parsed.visibility ?? DEFAULT_ORCHESTRATOR_CONFIG.visibility,
104
+ };
105
+ }
106
+ catch {
107
+ return DEFAULT_ORCHESTRATOR_CONFIG;
108
+ }
109
+ }
110
+ //# sourceMappingURL=hub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hub.js","sourceRoot":"","sources":["../../src/integrations/hub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Integration registry — mirrors src/channels/registry.ts.
3
+ *
4
+ * Each agent adapter calls registerIntegration() at module-load time; the
5
+ * hub looks them up by name. Keeps adapters decoupled and lets community
6
+ * adapters slot in by importing one more module.
7
+ */
8
+ import type { AgentIntegrationConfig, AgentObserver, AgentObserverFactory } from "./types.js";
9
+ export declare function registerIntegration(name: string, factory: AgentObserverFactory): void;
10
+ export declare function makeIntegration(name: string, cfg: AgentIntegrationConfig): Promise<AgentObserver>;
11
+ export declare function listAvailableIntegrations(): string[];
12
+ /** Test hook — clear the registry between unit tests. */
13
+ export declare function _resetIntegrationsForTest(): void;
14
+ export declare function registerBuiltinIntegrations(): Promise<void>;
15
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +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,CAMjE"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Integration registry — mirrors src/channels/registry.ts.
3
+ *
4
+ * Each agent adapter calls registerIntegration() at module-load time; the
5
+ * hub looks them up by name. Keeps adapters decoupled and lets community
6
+ * adapters slot in by importing one more module.
7
+ */
8
+ const FACTORIES = new Map();
9
+ export function registerIntegration(name, factory) {
10
+ FACTORIES.set(name, factory);
11
+ }
12
+ export async function makeIntegration(name, cfg) {
13
+ const factory = FACTORIES.get(name);
14
+ if (!factory) {
15
+ throw new Error(`unknown integration "${name}". Known: ${Array.from(FACTORIES.keys()).join(", ") || "(none registered)"}`);
16
+ }
17
+ return await factory(cfg);
18
+ }
19
+ export function listAvailableIntegrations() {
20
+ return Array.from(FACTORIES.keys()).sort();
21
+ }
22
+ /** Test hook — clear the registry between unit tests. */
23
+ export function _resetIntegrationsForTest() {
24
+ FACTORIES.clear();
25
+ builtinsRegistered = false;
26
+ }
27
+ // Lazy registration of built-in adapters. Each module calls
28
+ // registerIntegration() at import time.
29
+ let builtinsRegistered = false;
30
+ export async function registerBuiltinIntegrations() {
31
+ if (builtinsRegistered)
32
+ return;
33
+ builtinsRegistered = true;
34
+ await import("./claude-code/observer.js");
35
+ await import("./codex/observer.js");
36
+ // Additional adapters register here as they land (opencode, aider, …).
37
+ }
38
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +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,uEAAuE;AACzE,CAAC"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Cross-agent orchestration — shared types.
3
+ *
4
+ * LISA observes many CLI agents (Claude Code, Codex, OpenCode, Aider, …),
5
+ * each with its own on-disk session format. These types are the normalized,
6
+ * agent-agnostic shape every adapter produces, so the UI, the hub, and the
7
+ * advisor never care which agent a session came from.
8
+ *
9
+ * See docs/ORCHESTRATOR_PLAN.md for the full design (L1 OBSERVE, L2 UNDERSTAND).
10
+ */
11
+ /** Which agent produced a session. Open-ended string so community adapters
12
+ * can add kinds without touching this file. */
13
+ export type AgentKind = "claude-code" | "codex" | "opencode" | "aider" | "gemini" | "cursor" | "github-pr" | "mcp" | "lisa" | (string & {});
14
+ /** Normalized session state across all agents. Superset of any single
15
+ * agent's states; adapters map their native states onto these. */
16
+ export type AgentSessionState = "working" | "waiting" | "error" | "idle" | "done" | "unknown";
17
+ /**
18
+ * L2 — structural activity for a session. Populated by tier ≥ "activity"
19
+ * adapters. PRIVACY: every field here is structural metadata (tool NAMES,
20
+ * file PATHS, command argv[0], error strings, counts) — never a prompt, a
21
+ * model reply, or full command arguments. See parser privacy tests.
22
+ */
23
+ export interface SessionActivity {
24
+ /** Number of assistant/user turns observed (rough progress indicator). */
25
+ turnCount: number;
26
+ /** Most recent tool names, newest last, e.g. ["Read","Edit","Bash"]. */
27
+ lastTools: string[];
28
+ /** File paths touched by Edit/Write-style tools (deduped, capped). */
29
+ filesTouched: string[];
30
+ /** argv[0] of the most recent shell command, e.g. "npm" — not the full command. */
31
+ lastCommandName?: string;
32
+ /** Short error summary if the last meaningful activity errored. */
33
+ lastError?: string;
34
+ /** git branch recorded by the agent, if any. */
35
+ gitBranch?: string;
36
+ /** Token usage so far (for cost-spike detection). */
37
+ tokens?: {
38
+ input: number;
39
+ output: number;
40
+ };
41
+ /** Tool name awaiting a permission decision, if the session is blocked on one. */
42
+ pendingPermission?: string;
43
+ }
44
+ /**
45
+ * One normalized session. The merge target for every adapter's output.
46
+ */
47
+ export interface AgentSession {
48
+ agent: AgentKind;
49
+ /** Stable id within this agent (uuid, rollout filename, PR number, …). */
50
+ sessionId: string;
51
+ /** Human label — usually the basename of cwd. */
52
+ project: string;
53
+ cwd?: string;
54
+ state: AgentSessionState;
55
+ /** Short machine-ish reason for the state ("end_turn", "permission", …). */
56
+ stateReason: string;
57
+ /** Last activity time, epoch ms. */
58
+ lastMtime: number;
59
+ /** L2 activity, present when the adapter runs at tier ≥ "activity". */
60
+ activity?: SessionActivity;
61
+ }
62
+ /** Visibility tier — how deeply LISA may inspect a session. See plan §3. */
63
+ export type VisibilityTier = "off" | "metadata" | "activity" | "intent";
64
+ /** Per-integration config from ~/.lisa/agents.json. */
65
+ export interface AgentIntegrationConfig {
66
+ enabled?: boolean;
67
+ /** Home dir / data dir override (e.g. CODEX_HOME, OPENCODE_DATA_DIR). */
68
+ home?: string;
69
+ /** Extra roots to watch (e.g. Aider's per-project logs). */
70
+ watchRoots?: string[];
71
+ /** Per-integration visibility override; falls back to the global tier. */
72
+ visibility?: VisibilityTier;
73
+ /** Arbitrary adapter-specific options. */
74
+ [k: string]: unknown;
75
+ }
76
+ /**
77
+ * An adapter that watches one agent kind and emits normalized sessions.
78
+ * Stateful: holds file watchers / timers between start() and stop().
79
+ */
80
+ export interface AgentObserver {
81
+ readonly agent: AgentKind;
82
+ /** Begin watching. `emit` is called on every new/changed session. */
83
+ start(emit: (session: AgentSession) => void): Promise<void>;
84
+ /** Current snapshot of active sessions. */
85
+ list(): AgentSession[];
86
+ /** Tear down watchers/timers. Idempotent. */
87
+ stop(): Promise<void>;
88
+ }
89
+ export type AgentObserverFactory = (cfg: AgentIntegrationConfig) => AgentObserver | Promise<AgentObserver>;
90
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/integrations/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH;gDACgD;AAChD,MAAM,MAAM,SAAS,GACjB,aAAa,GACb,OAAO,GACP,UAAU,GACV,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,WAAW,GACX,KAAK,GACL,MAAM,GACN,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB;mEACmE;AACnE,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,SAAS,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,CAAC;AAEd;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,sEAAsE;IACtE,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,kFAAkF;IAClF,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,CAAC;IACjB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,iBAAiB,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,eAAe,CAAC;CAC5B;AAED,4EAA4E;AAC5E,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;AAExE,uDAAuD;AACvD,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,0CAA0C;IAC1C,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,qEAAqE;IACrE,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,2CAA2C;IAC3C,IAAI,IAAI,YAAY,EAAE,CAAC;IACvB,6CAA6C;IAC7C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,MAAM,oBAAoB,GAAG,CACjC,GAAG,EAAE,sBAAsB,KACxB,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Cross-agent orchestration — shared types.
3
+ *
4
+ * LISA observes many CLI agents (Claude Code, Codex, OpenCode, Aider, …),
5
+ * each with its own on-disk session format. These types are the normalized,
6
+ * agent-agnostic shape every adapter produces, so the UI, the hub, and the
7
+ * advisor never care which agent a session came from.
8
+ *
9
+ * See docs/ORCHESTRATOR_PLAN.md for the full design (L1 OBSERVE, L2 UNDERSTAND).
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/integrations/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * advise_now — on-demand pull of the advisor's current suggestions.
3
+ *
4
+ * "Lisa, what should I know about what's running?" → she runs the cross-agent
5
+ * detectors against the live session snapshot and reports what clears the
6
+ * relevance bar (throttle/dedup bypassed for an explicit ask). Structural
7
+ * only — never conversation content.
8
+ */
9
+ import type { ToolDefinition } from "../types.js";
10
+ export declare const adviseNowTool: ToolDefinition<Record<string, never>, string>;
11
+ //# sourceMappingURL=advise_now.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advise_now.d.ts","sourceRoot":"","sources":["../../src/tools/advise_now.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAIlD,eAAO,MAAM,aAAa,EAAE,cAAc,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAsBvE,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * advise_now — on-demand pull of the advisor's current suggestions.
3
+ *
4
+ * "Lisa, what should I know about what's running?" → she runs the cross-agent
5
+ * detectors against the live session snapshot and reports what clears the
6
+ * relevance bar (throttle/dedup bypassed for an explicit ask). Structural
7
+ * only — never conversation content.
8
+ */
9
+ import { getCurrentHub } from "../integrations/current-hub.js";
10
+ import { adviseNow, loadAdvisorState, formatDigest } from "../advisor/engine.js";
11
+ export const adviseNowTool = {
12
+ name: "advise_now",
13
+ description: "Check what's worth knowing across all running agents (Claude Code, Codex, …) right now: " +
14
+ "stuck sessions, same-repo conflicts, permission prompts, cost spikes, finished work. " +
15
+ "Returns a short list of suggestions, or '(nothing notable)'. Use when the user asks " +
16
+ "what's going on with their agents, or whether anything needs attention.",
17
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
18
+ async execute() {
19
+ const hub = getCurrentHub();
20
+ if (!hub) {
21
+ return "(agent monitoring isn't active — start the web server with `lisa serve --web`)";
22
+ }
23
+ const sessions = hub.list();
24
+ if (sessions.length === 0)
25
+ return "(no agents are active right now)";
26
+ const state = await loadAdvisorState().catch(() => undefined);
27
+ const suggestions = adviseNow({ sessions, now: Date.now() }, state);
28
+ if (suggestions.length === 0) {
29
+ return `(${sessions.length} agent session(s) active, nothing notable — all progressing normally)`;
30
+ }
31
+ return formatDigest(suggestions);
32
+ },
33
+ };
34
+ //# sourceMappingURL=advise_now.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"advise_now.js","sourceRoot":"","sources":["../../src/tools/advise_now.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEjF,MAAM,CAAC,MAAM,aAAa,GAAkD;IAC1E,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,0FAA0F;QAC1F,uFAAuF;QACvF,sFAAsF;QACtF,yEAAyE;IAC3E,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAE;IAC5E,KAAK,CAAC,OAAO;QACX,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,gFAAgF,CAAC;QAC1F,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,kCAAkC,CAAC;QACrE,MAAM,KAAK,GAAG,MAAM,gBAAgB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC9D,MAAM,WAAW,GAAG,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QACpE,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,IAAI,QAAQ,CAAC,MAAM,uEAAuE,CAAC;QACpG,CAAC;QACD,OAAO,YAAY,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC;CACF,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * dispatch_agent (L3 DISPATCH) — LISA launches another CLI agent with a task
3
+ * and tracks it, turning observation into orchestration.
4
+ *
5
+ * Each supported agent has a non-interactive ("headless") invocation:
6
+ * claude → claude -p "<task>"
7
+ * codex → codex exec "<task>"
8
+ * opencode → opencode run "<task>"
9
+ * aider → aider --message "<task>" --yes
10
+ *
11
+ * The agent is spawned detached so it keeps running independently; it then
12
+ * shows up in the orchestrator hub via its normal session file (the same
13
+ * path the observers already watch). This tool returns immediately with a
14
+ * handle — it does NOT block on completion.
15
+ *
16
+ * SAFETY: spawning an autonomous agent is an explicit-permission action
17
+ * (it can read/write files + run commands). The tool surfaces the exact
18
+ * argv it will run; the host's approval layer (ToolContext.onObjection /
19
+ * the approval mode) gates it. The task string is passed as a single argv
20
+ * element — never interpolated into a shell — so there's no shell-injection
21
+ * surface (same lesson as the iMessage argv fix).
22
+ */
23
+ import type { ToolDefinition } from "../types.js";
24
+ interface DispatchInput {
25
+ agent: "claude" | "codex" | "opencode" | "aider";
26
+ task: string;
27
+ cwd?: string;
28
+ /** Override the L4 same-cwd conflict guard (default false). */
29
+ force?: boolean;
30
+ }
31
+ /** Build the argv for an agent's headless invocation. Pure + tested. */
32
+ export declare function buildDispatchArgv(agent: DispatchInput["agent"], task: string): {
33
+ cmd: string;
34
+ args: string[];
35
+ };
36
+ export declare const dispatchAgentTool: ToolDefinition<DispatchInput, string>;
37
+ export {};
38
+ //# sourceMappingURL=dispatch_agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch_agent.d.ts","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,aAAa;IACrB,KAAK,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,CAAC;IACjD,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAoBD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAgBjC;AAED,eAAO,MAAM,iBAAiB,EAAE,cAAc,CAAC,aAAa,EAAE,MAAM,CA+FnE,CAAC"}
@@ -0,0 +1,145 @@
1
+ /**
2
+ * dispatch_agent (L3 DISPATCH) — LISA launches another CLI agent with a task
3
+ * and tracks it, turning observation into orchestration.
4
+ *
5
+ * Each supported agent has a non-interactive ("headless") invocation:
6
+ * claude → claude -p "<task>"
7
+ * codex → codex exec "<task>"
8
+ * opencode → opencode run "<task>"
9
+ * aider → aider --message "<task>" --yes
10
+ *
11
+ * The agent is spawned detached so it keeps running independently; it then
12
+ * shows up in the orchestrator hub via its normal session file (the same
13
+ * path the observers already watch). This tool returns immediately with a
14
+ * handle — it does NOT block on completion.
15
+ *
16
+ * SAFETY: spawning an autonomous agent is an explicit-permission action
17
+ * (it can read/write files + run commands). The tool surfaces the exact
18
+ * argv it will run; the host's approval layer (ToolContext.onObjection /
19
+ * the approval mode) gates it. The task string is passed as a single argv
20
+ * element — never interpolated into a shell — so there's no shell-injection
21
+ * surface (same lesson as the iMessage argv fix).
22
+ */
23
+ import { spawn } from "node:child_process";
24
+ import { getCurrentHub } from "../integrations/current-hub.js";
25
+ /**
26
+ * L4 COORDINATE: is another agent already actively working in this cwd?
27
+ * Launching a second agent into the same directory is the #1 way multi-agent
28
+ * setups clobber a repo, so dispatch refuses by default (overridable with
29
+ * force). Returns the conflicting session's label, or null if clear.
30
+ */
31
+ function activeAgentInCwd(cwd) {
32
+ const hub = getCurrentHub();
33
+ if (!hub)
34
+ return null; // no monitor → can't check; allow
35
+ const clash = hub
36
+ .list()
37
+ .find((s) => s.cwd === cwd && (s.state === "working" || s.state === "waiting"));
38
+ return clash ? `${clash.agent} (${clash.project})` : null;
39
+ }
40
+ /** Build the argv for an agent's headless invocation. Pure + tested. */
41
+ export function buildDispatchArgv(agent, task) {
42
+ switch (agent) {
43
+ case "claude":
44
+ return { cmd: "claude", args: ["-p", task] };
45
+ case "codex":
46
+ return { cmd: "codex", args: ["exec", task] };
47
+ case "opencode":
48
+ return { cmd: "opencode", args: ["run", task] };
49
+ case "aider":
50
+ return { cmd: "aider", args: ["--message", task, "--yes"] };
51
+ default: {
52
+ // exhaustiveness guard
53
+ const _never = agent;
54
+ throw new Error(`unknown agent: ${String(_never)}`);
55
+ }
56
+ }
57
+ }
58
+ export const dispatchAgentTool = {
59
+ name: "dispatch_agent",
60
+ description: "Launch another CLI coding agent (claude, codex, opencode, aider) to work on a task " +
61
+ "autonomously in a given directory. The agent runs in the background and appears in " +
62
+ "the agent session monitor. Use when the user asks you to hand work to another agent, " +
63
+ "or to pursue a desire by dispatching one. Returns a handle; it does NOT wait for the " +
64
+ "agent to finish — check the session monitor or use advise_now for progress. " +
65
+ "This spawns an autonomous process, so it requires user approval.",
66
+ inputSchema: {
67
+ type: "object",
68
+ properties: {
69
+ agent: {
70
+ type: "string",
71
+ enum: ["claude", "codex", "opencode", "aider"],
72
+ description: "Which CLI agent to launch.",
73
+ },
74
+ task: {
75
+ type: "string",
76
+ description: "The task/prompt to give the agent (passed as a single argument, not a shell string).",
77
+ minLength: 1,
78
+ },
79
+ cwd: {
80
+ type: "string",
81
+ description: "Absolute working directory the agent should run in. Defaults to the current directory.",
82
+ },
83
+ force: {
84
+ type: "boolean",
85
+ description: "Launch even if another agent is already active in this directory (default false — dispatch refuses to avoid clobbering).",
86
+ },
87
+ },
88
+ required: ["agent", "task"],
89
+ additionalProperties: false,
90
+ },
91
+ async execute(input, ctx) {
92
+ const cwd = input.cwd && input.cwd.startsWith("/") ? input.cwd : ctx.cwd;
93
+ // L4 coordination: don't launch into a directory another agent is already
94
+ // working in unless explicitly forced.
95
+ if (!input.force) {
96
+ const clash = activeAgentInCwd(cwd);
97
+ if (clash) {
98
+ return (`Refusing to launch ${input.agent} in ${cwd}: ${clash} is already active there. ` +
99
+ `Running two agents in one directory risks clobbering changes. ` +
100
+ `Wait for it to finish, pick a different directory, or pass force:true to override.`);
101
+ }
102
+ }
103
+ const { cmd, args } = buildDispatchArgv(input.agent, input.task);
104
+ let child;
105
+ try {
106
+ child = spawn(cmd, args, {
107
+ cwd,
108
+ detached: true, // survive LISA; the agent runs on its own
109
+ stdio: "ignore",
110
+ });
111
+ }
112
+ catch (err) {
113
+ return `Failed to launch ${input.agent}: ${err.message}. Is "${cmd}" installed and on PATH?`;
114
+ }
115
+ // If the binary is missing, spawn emits 'error' asynchronously. Surface a
116
+ // useful message in that case rather than silently "succeeding".
117
+ const launchError = await new Promise((resolve) => {
118
+ let settled = false;
119
+ child.once("error", (e) => {
120
+ if (settled)
121
+ return;
122
+ settled = true;
123
+ resolve(e.code === "ENOENT"
124
+ ? `"${cmd}" not found on PATH — is ${input.agent} installed?`
125
+ : `launch error: ${e.message}`);
126
+ });
127
+ // Give the spawn a tick to fail fast; if it doesn't, assume it started.
128
+ setTimeout(() => {
129
+ if (settled)
130
+ return;
131
+ settled = true;
132
+ resolve(null);
133
+ }, 150);
134
+ });
135
+ if (launchError)
136
+ return launchError;
137
+ const pid = child.pid;
138
+ child.unref(); // don't keep LISA's event loop alive for it
139
+ ctx.log(`[dispatch] launched ${input.agent} (pid ${pid}) in ${cwd}: ${input.task.slice(0, 80)}`);
140
+ return (`Launched ${input.agent} (pid ${pid}) in ${cwd}.\n` +
141
+ `It's running autonomously and will appear in the agent session monitor. ` +
142
+ `I won't block on it — ask me "what's running?" (advise_now) to check progress.`);
143
+ },
144
+ };
145
+ //# sourceMappingURL=dispatch_agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch_agent.js","sourceRoot":"","sources":["../../src/tools/dispatch_agent.ts"],"names":[],"mappings":"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"}