@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.
Files changed (133) hide show
  1. package/README.md +5 -0
  2. package/dist/autostart/install.d.ts +28 -0
  3. package/dist/autostart/install.d.ts.map +1 -0
  4. package/dist/autostart/install.js +180 -0
  5. package/dist/autostart/install.js.map +1 -0
  6. package/dist/cli.js +49 -1
  7. package/dist/cli.js.map +1 -1
  8. package/dist/heartbeat/install.d.ts.map +1 -1
  9. package/dist/heartbeat/install.js +1 -43
  10. package/dist/heartbeat/install.js.map +1 -1
  11. package/dist/heartbeat/runner.d.ts.map +1 -1
  12. package/dist/heartbeat/runner.js +11 -0
  13. package/dist/heartbeat/runner.js.map +1 -1
  14. package/dist/idle/runner.d.ts +12 -0
  15. package/dist/idle/runner.d.ts.map +1 -1
  16. package/dist/idle/runner.js +33 -2
  17. package/dist/idle/runner.js.map +1 -1
  18. package/dist/integrations/aider/observer.d.ts +48 -0
  19. package/dist/integrations/aider/observer.d.ts.map +1 -0
  20. package/dist/integrations/aider/observer.js +196 -0
  21. package/dist/integrations/aider/observer.js.map +1 -0
  22. package/dist/integrations/comparisons.d.ts +23 -0
  23. package/dist/integrations/comparisons.d.ts.map +1 -0
  24. package/dist/integrations/comparisons.js +47 -0
  25. package/dist/integrations/comparisons.js.map +1 -0
  26. package/dist/integrations/dispatch-ledger.d.ts +51 -0
  27. package/dist/integrations/dispatch-ledger.d.ts.map +1 -0
  28. package/dist/integrations/dispatch-ledger.js +106 -0
  29. package/dist/integrations/dispatch-ledger.js.map +1 -0
  30. package/dist/integrations/github-pr/observer.d.ts +85 -0
  31. package/dist/integrations/github-pr/observer.d.ts.map +1 -0
  32. package/dist/integrations/github-pr/observer.js +261 -0
  33. package/dist/integrations/github-pr/observer.js.map +1 -0
  34. package/dist/integrations/hub.d.ts.map +1 -1
  35. package/dist/integrations/hub.js +10 -0
  36. package/dist/integrations/hub.js.map +1 -1
  37. package/dist/integrations/opencode/observer.d.ts +74 -0
  38. package/dist/integrations/opencode/observer.d.ts.map +1 -0
  39. package/dist/integrations/opencode/observer.js +207 -0
  40. package/dist/integrations/opencode/observer.js.map +1 -0
  41. package/dist/integrations/registry.d.ts.map +1 -1
  42. package/dist/integrations/registry.js +3 -1
  43. package/dist/integrations/registry.js.map +1 -1
  44. package/dist/integrations/scheduled-dispatch.d.ts +42 -0
  45. package/dist/integrations/scheduled-dispatch.d.ts.map +1 -0
  46. package/dist/integrations/scheduled-dispatch.js +140 -0
  47. package/dist/integrations/scheduled-dispatch.js.map +1 -0
  48. package/dist/launchd.d.ts +18 -0
  49. package/dist/launchd.d.ts.map +1 -0
  50. package/dist/launchd.js +60 -0
  51. package/dist/launchd.js.map +1 -0
  52. package/dist/orchestrator/journal.d.ts +46 -0
  53. package/dist/orchestrator/journal.d.ts.map +1 -0
  54. package/dist/orchestrator/journal.js +82 -0
  55. package/dist/orchestrator/journal.js.map +1 -0
  56. package/dist/orchestrator/recap.d.ts +38 -0
  57. package/dist/orchestrator/recap.d.ts.map +1 -0
  58. package/dist/orchestrator/recap.js +134 -0
  59. package/dist/orchestrator/recap.js.map +1 -0
  60. package/dist/screen_advisor/engine.d.ts +79 -0
  61. package/dist/screen_advisor/engine.d.ts.map +1 -0
  62. package/dist/screen_advisor/engine.js +128 -0
  63. package/dist/screen_advisor/engine.js.map +1 -0
  64. package/dist/soul/birth.d.ts.map +1 -1
  65. package/dist/soul/birth.js +8 -0
  66. package/dist/soul/birth.js.map +1 -1
  67. package/dist/soul/lock.d.ts +7 -4
  68. package/dist/soul/lock.d.ts.map +1 -1
  69. package/dist/soul/lock.js +33 -9
  70. package/dist/soul/lock.js.map +1 -1
  71. package/dist/tools/agent_recap.d.ts +21 -0
  72. package/dist/tools/agent_recap.d.ts.map +1 -0
  73. package/dist/tools/agent_recap.js +43 -0
  74. package/dist/tools/agent_recap.js.map +1 -0
  75. package/dist/tools/compare_agents.d.ts +16 -0
  76. package/dist/tools/compare_agents.d.ts.map +1 -0
  77. package/dist/tools/compare_agents.js +145 -0
  78. package/dist/tools/compare_agents.js.map +1 -0
  79. package/dist/tools/dispatch_agent.d.ts +13 -0
  80. package/dist/tools/dispatch_agent.d.ts.map +1 -1
  81. package/dist/tools/dispatch_agent.js +49 -37
  82. package/dist/tools/dispatch_agent.js.map +1 -1
  83. package/dist/tools/exec-util.d.ts +18 -0
  84. package/dist/tools/exec-util.d.ts.map +1 -0
  85. package/dist/tools/exec-util.js +72 -0
  86. package/dist/tools/exec-util.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/pr_status.d.ts +31 -0
  96. package/dist/tools/pr_status.d.ts.map +1 -0
  97. package/dist/tools/pr_status.js +90 -0
  98. package/dist/tools/pr_status.js.map +1 -0
  99. package/dist/tools/registry.d.ts.map +1 -1
  100. package/dist/tools/registry.js +26 -0
  101. package/dist/tools/registry.js.map +1 -1
  102. package/dist/tools/repo_digest.d.ts +33 -0
  103. package/dist/tools/repo_digest.d.ts.map +1 -0
  104. package/dist/tools/repo_digest.js +113 -0
  105. package/dist/tools/repo_digest.js.map +1 -0
  106. package/dist/tools/review_diff.d.ts +28 -0
  107. package/dist/tools/review_diff.d.ts.map +1 -0
  108. package/dist/tools/review_diff.js +68 -0
  109. package/dist/tools/review_diff.js.map +1 -0
  110. package/dist/tools/run_checks.d.ts +15 -0
  111. package/dist/tools/run_checks.d.ts.map +1 -0
  112. package/dist/tools/run_checks.js +117 -0
  113. package/dist/tools/run_checks.js.map +1 -0
  114. package/dist/tools/scheduled_dispatch.d.ts +27 -0
  115. package/dist/tools/scheduled_dispatch.d.ts.map +1 -0
  116. package/dist/tools/scheduled_dispatch.js +57 -0
  117. package/dist/tools/scheduled_dispatch.js.map +1 -0
  118. package/dist/tools/signal_agent.d.ts +36 -0
  119. package/dist/tools/signal_agent.d.ts.map +1 -0
  120. package/dist/tools/signal_agent.js +140 -0
  121. package/dist/tools/signal_agent.js.map +1 -0
  122. package/dist/web/island.d.ts +1 -1
  123. package/dist/web/island.d.ts.map +1 -1
  124. package/dist/web/island.js +139 -27
  125. package/dist/web/island.js.map +1 -1
  126. package/dist/web/lisa-html.d.ts +1 -1
  127. package/dist/web/lisa-html.d.ts.map +1 -1
  128. package/dist/web/lisa-html.js +132 -4
  129. package/dist/web/lisa-html.js.map +1 -1
  130. package/dist/web/server.d.ts.map +1 -1
  131. package/dist/web/server.js +190 -0
  132. package/dist/web/server.js.map +1 -1
  133. package/package.json +1 -1
@@ -0,0 +1,28 @@
1
+ /**
2
+ * review_diff (vibe-coding) — show the actual code changes in a repo (or a PR)
3
+ * so LISA can review an agent's work: spot bugs, risky edits, leftover debug
4
+ * code, missing tests. The natural follow-up to repo_digest ("something
5
+ * changed") and the natural lead-in to run_checks ("does it pass?").
6
+ *
7
+ * Read-only. Diff output is capped so a huge change doesn't blow the context.
8
+ */
9
+ import type { ToolDefinition } from "../types.js";
10
+ interface ReviewDiffInput {
11
+ /** Repo path. Defaults to the current working directory. */
12
+ cwd?: string;
13
+ /**
14
+ * What to diff: "head" (all uncommitted vs HEAD, default), "working"
15
+ * (unstaged only), "staged" (staged only), or any git ref/range
16
+ * (e.g. "main...HEAD", "HEAD~3").
17
+ */
18
+ target?: string;
19
+ /** Review a GitHub PR's diff instead (needs the `gh` CLI). */
20
+ pr?: number;
21
+ /** Max diff lines to return. Default 600. */
22
+ max_lines?: number;
23
+ }
24
+ /** Pure: assemble the stat header + (capped) diff body. Exported for tests. */
25
+ export declare function assembleReview(stat: string, diff: string, maxLines: number): string;
26
+ export declare const reviewDiffTool: ToolDefinition<ReviewDiffInput, string>;
27
+ export {};
28
+ //# sourceMappingURL=review_diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review_diff.d.ts","sourceRoot":"","sources":["../../src/tools/review_diff.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,eAAe;IACvB,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAgBD,+EAA+E;AAC/E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOnF;AAED,eAAO,MAAM,cAAc,EAAE,cAAc,CAAC,eAAe,EAAE,MAAM,CAuClE,CAAC"}
@@ -0,0 +1,68 @@
1
+ import { runIn, isDir, gitRoot } from "./exec-util.js";
2
+ function gitDiffArgs(target) {
3
+ switch (target) {
4
+ case "working":
5
+ return ["diff"];
6
+ case "staged":
7
+ return ["diff", "--cached"];
8
+ case "head":
9
+ case "":
10
+ return ["diff", "HEAD"];
11
+ default:
12
+ return ["diff", target];
13
+ }
14
+ }
15
+ /** Pure: assemble the stat header + (capped) diff body. Exported for tests. */
16
+ export function assembleReview(stat, diff, maxLines) {
17
+ const header = stat.trim() ? stat.trim() : "(no changes)";
18
+ if (!diff.trim())
19
+ return header;
20
+ const lines = diff.split("\n");
21
+ if (lines.length <= maxLines)
22
+ return `${header}\n\n${diff.trimEnd()}`;
23
+ const shown = lines.slice(0, maxLines).join("\n");
24
+ return `${header}\n\n${shown}\n… [diff truncated — ${maxLines} of ${lines.length} lines shown; narrow with target or review per-file]`;
25
+ }
26
+ export const reviewDiffTool = {
27
+ name: "review_diff",
28
+ description: "Show the actual code diff in a repo so you can review it — uncommitted changes vs HEAD by " +
29
+ "default, or working/staged only, or any git ref/range, or a GitHub PR (pr:<n>, needs `gh`). " +
30
+ "Use to review what an agent wrote before committing/merging: bugs, risky edits, debug leftovers, " +
31
+ "missing tests. Read-only; output is capped. Pair with repo_digest (what changed) and run_checks " +
32
+ "(does it pass).",
33
+ inputSchema: {
34
+ type: "object",
35
+ properties: {
36
+ cwd: { type: "string", description: "Absolute path inside the repo. Defaults to the current directory." },
37
+ target: { type: "string", description: '"head" (default, all uncommitted), "working", "staged", or a git ref/range like "main...HEAD".' },
38
+ pr: { type: "integer", description: "Review this GitHub PR's diff instead (needs `gh`)." },
39
+ max_lines: { type: "integer", minimum: 50, maximum: 4000, description: "Max diff lines (default 600)." },
40
+ },
41
+ additionalProperties: false,
42
+ },
43
+ async execute(input, ctx) {
44
+ const cwd = input.cwd && input.cwd.startsWith("/") ? input.cwd : ctx.cwd;
45
+ if (!(await isDir(cwd)))
46
+ return `(not a directory: ${cwd})`;
47
+ const root = await gitRoot(cwd, ctx.signal);
48
+ if (!root)
49
+ return `(not a git repo: ${cwd})`;
50
+ const maxLines = input.max_lines ?? 600;
51
+ if (typeof input.pr === "number") {
52
+ const r = await runIn(root, "gh", ["pr", "diff", String(input.pr)], { timeoutMs: 20000, signal: ctx.signal, maxBytes: 200_000 });
53
+ if (r.spawnError)
54
+ return "(the `gh` CLI isn't installed — needed to fetch PR diffs)";
55
+ if (r.code !== 0)
56
+ return `(gh pr diff ${input.pr} failed: ${r.stderr.trim().slice(0, 200) || "unknown error"})`;
57
+ return assembleReview(`PR #${input.pr} diff`, r.stdout, maxLines);
58
+ }
59
+ const target = (input.target ?? "head").trim();
60
+ const args = gitDiffArgs(target);
61
+ const stat = await runIn(root, "git", ["-C", root, ...args, "--stat"], { timeoutMs: 10000, signal: ctx.signal });
62
+ const diff = await runIn(root, "git", ["-C", root, ...args], { timeoutMs: 15000, signal: ctx.signal, maxBytes: 200_000 });
63
+ if (diff.code !== 0)
64
+ return `(git diff failed: ${diff.stderr.trim().slice(0, 200) || "bad target?"})`;
65
+ return assembleReview(stat.stdout, diff.stdout, maxLines);
66
+ },
67
+ };
68
+ //# sourceMappingURL=review_diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review_diff.js","sourceRoot":"","sources":["../../src/tools/review_diff.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAiBvD,SAAS,WAAW,CAAC,MAAc;IACjC,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,SAAS;YACZ,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,KAAK,QAAQ;YACX,OAAO,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QAC9B,KAAK,MAAM,CAAC;QACZ,KAAK,EAAE;YACL,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC1B;YACE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,IAAY,EAAE,QAAgB;IACzE,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,cAAc,CAAC;IAC1D,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;QAAE,OAAO,MAAM,CAAC;IAChC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,GAAG,MAAM,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;IACtE,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,OAAO,GAAG,MAAM,OAAO,KAAK,yBAAyB,QAAQ,OAAO,KAAK,CAAC,MAAM,sDAAsD,CAAC;AACzI,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAA4C;IACrE,IAAI,EAAE,aAAa;IACnB,WAAW,EACT,4FAA4F;QAC5F,8FAA8F;QAC9F,mGAAmG;QACnG,kGAAkG;QAClG,iBAAiB;IACnB,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mEAAmE,EAAE;YACzG,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gGAAgG,EAAE;YACzI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,oDAAoD,EAAE;YAC1F,SAAS,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,+BAA+B,EAAE;SACzG;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;QACzE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,qBAAqB,GAAG,GAAG,CAAC;QAC5D,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,oBAAoB,GAAG,GAAG,CAAC;QAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,SAAS,IAAI,GAAG,CAAC;QAExC,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;YACjC,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YACjI,IAAI,CAAC,CAAC,UAAU;gBAAE,OAAO,2DAA2D,CAAC;YACrF,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,eAAe,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,eAAe,GAAG,CAAC;YAChH,OAAO,cAAc,CAAC,OAAO,KAAK,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACpE,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,EAAE,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QACjH,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QAC1H,IAAI,IAAI,CAAC,IAAI,KAAK,CAAC;YAAE,OAAO,qBAAqB,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,aAAa,GAAG,CAAC;QACtG,OAAO,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC5D,CAAC;CACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { ToolDefinition } from "../types.js";
2
+ interface RunChecksInput {
3
+ cwd?: string;
4
+ /** Subset to run, e.g. ["test","typecheck"]. Omit = all detected. */
5
+ only?: string[];
6
+ }
7
+ export interface DetectedCheck {
8
+ name: string;
9
+ script: string;
10
+ }
11
+ /** Pure: pick which checks to run from a scripts map. Exported for tests. */
12
+ export declare function detectChecks(scripts: Record<string, string>, only?: string[]): DetectedCheck[];
13
+ export declare const runChecksTool: ToolDefinition<RunChecksInput, string>;
14
+ export {};
15
+ //# sourceMappingURL=run_checks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_checks.d.ts","sourceRoot":"","sources":["../../src/tools/run_checks.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,UAAU,cAAc;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAiBD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAe9F;AAOD,eAAO,MAAM,aAAa,EAAE,cAAc,CAAC,cAAc,EAAE,MAAM,CA8DhE,CAAC"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * run_checks (vibe-coding) — the quality gate. Auto-detects the repo's own
3
+ * checks (typecheck / lint / test / build from package.json scripts) and runs
4
+ * them, reporting pass/fail + the tail of any failure. "Lisa, does the agent's
5
+ * work pass?" before you commit or merge.
6
+ *
7
+ * Runs the project's OWN scripts (npm/pnpm/yarn run <script>) — same trust as
8
+ * running them yourself; capped per-check by a timeout.
9
+ */
10
+ import { readFile } from "node:fs/promises";
11
+ import path from "node:path";
12
+ import { runIn, isDir, gitRoot } from "./exec-util.js";
13
+ /** Preferred order — fast/cheap signals first, slow build last. */
14
+ const CHECK_PRIORITY = ["typecheck", "lint", "test", "build"];
15
+ /** Script-name aliases mapped onto a canonical check. */
16
+ const ALIASES = {
17
+ typecheck: "typecheck",
18
+ tsc: "typecheck",
19
+ "type-check": "typecheck",
20
+ lint: "lint",
21
+ eslint: "lint",
22
+ test: "test",
23
+ tests: "test",
24
+ build: "build",
25
+ check: "lint",
26
+ };
27
+ /** Pure: pick which checks to run from a scripts map. Exported for tests. */
28
+ export function detectChecks(scripts, only) {
29
+ const onlySet = only && only.length ? new Set(only.map((s) => s.toLowerCase())) : null;
30
+ const byCanon = new Map(); // canonical → script key (first wins)
31
+ for (const key of Object.keys(scripts)) {
32
+ const canon = ALIASES[key.toLowerCase()];
33
+ if (!canon)
34
+ continue;
35
+ if (!byCanon.has(canon))
36
+ byCanon.set(canon, key);
37
+ }
38
+ const out = [];
39
+ for (const canon of CHECK_PRIORITY) {
40
+ if (!byCanon.has(canon))
41
+ continue;
42
+ if (onlySet && !onlySet.has(canon))
43
+ continue;
44
+ out.push({ name: canon, script: byCanon.get(canon) });
45
+ }
46
+ return out;
47
+ }
48
+ function tail(s, n) {
49
+ const lines = s.split("\n").filter((l) => l.length);
50
+ return lines.slice(-n).join("\n");
51
+ }
52
+ export const runChecksTool = {
53
+ name: "run_checks",
54
+ description: "Run a repo's own quality checks (typecheck, lint, test, build — auto-detected from package.json " +
55
+ "scripts) and report pass/fail with the tail of any failure. Use to verify an agent's work before " +
56
+ "committing/merging, or when the user asks 'does it pass / are tests green'. Runs the project's own " +
57
+ "npm/pnpm/yarn scripts (capped by a per-check timeout). Narrow with only:['test'] etc.",
58
+ inputSchema: {
59
+ type: "object",
60
+ properties: {
61
+ cwd: { type: "string", description: "Absolute path inside the repo. Defaults to the current directory." },
62
+ only: { type: "array", items: { type: "string" }, description: "Subset: any of typecheck/lint/test/build. Omit to run all detected." },
63
+ },
64
+ additionalProperties: false,
65
+ },
66
+ async execute(input, ctx) {
67
+ const cwd = input.cwd && input.cwd.startsWith("/") ? input.cwd : ctx.cwd;
68
+ if (!(await isDir(cwd)))
69
+ return `(not a directory: ${cwd})`;
70
+ const root = (await gitRoot(cwd, ctx.signal)) ?? cwd;
71
+ let scripts = {};
72
+ try {
73
+ const pkg = JSON.parse(await readFile(path.join(root, "package.json"), "utf8"));
74
+ scripts = pkg.scripts ?? {};
75
+ }
76
+ catch {
77
+ return "(no package.json with scripts found — run_checks currently supports Node/npm projects)";
78
+ }
79
+ const checks = detectChecks(scripts, input.only);
80
+ if (checks.length === 0) {
81
+ return `(no recognised check scripts in package.json; available: ${Object.keys(scripts).join(", ") || "none"})`;
82
+ }
83
+ // Pick the package manager from the lockfile.
84
+ const has = async (f) => isDir(root).then(() => readFile(path.join(root, f)).then(() => true).catch(() => false));
85
+ let pm = "npm";
86
+ if (await has("pnpm-lock.yaml"))
87
+ pm = "pnpm";
88
+ else if (await has("yarn.lock"))
89
+ pm = "yarn";
90
+ else if (await has("bun.lockb"))
91
+ pm = "bun";
92
+ const results = [];
93
+ const failures = [];
94
+ for (const c of checks) {
95
+ const r = await runIn(root, pm, ["run", c.script], { timeoutMs: 240_000, signal: ctx.signal, maxBytes: 200_000 });
96
+ if (r.spawnError) {
97
+ results.push(`✗ ${c.name} (couldn't run ${pm})`);
98
+ continue;
99
+ }
100
+ if (r.timedOut) {
101
+ results.push(`⏱ ${c.name} (timed out at 240s)`);
102
+ continue;
103
+ }
104
+ if (r.code === 0) {
105
+ results.push(`✓ ${c.name}`);
106
+ }
107
+ else {
108
+ results.push(`✗ ${c.name} (exit ${r.code})`);
109
+ const out = tail(r.stdout + "\n" + r.stderr, 20);
110
+ failures.push(`── ${c.name} (${pm} run ${c.script}) ──\n${out}`);
111
+ }
112
+ }
113
+ const head = `${root.split("/").pop()}: ${results.join(" ")}`;
114
+ return failures.length ? `${head}\n\n${failures.join("\n\n")}` : head;
115
+ },
116
+ };
117
+ //# sourceMappingURL=run_checks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_checks.js","sourceRoot":"","sources":["../../src/tools/run_checks.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAQvD,mEAAmE;AACnE,MAAM,cAAc,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AACvE,yDAAyD;AACzD,MAAM,OAAO,GAA2B;IACtC,SAAS,EAAE,WAAW;IACtB,GAAG,EAAE,WAAW;IAChB,YAAY,EAAE,WAAW;IACzB,IAAI,EAAE,MAAM;IACZ,MAAM,EAAE,MAAM;IACd,IAAI,EAAE,MAAM;IACZ,KAAK,EAAE,MAAM;IACb,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,MAAM;CACd,CAAC;AAOF,6EAA6E;AAC7E,MAAM,UAAU,YAAY,CAAC,OAA+B,EAAE,IAAe;IAC3E,MAAM,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvF,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC,CAAC,sCAAsC;IACjF,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,GAAG,GAAoB,EAAE,CAAC;IAChC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QAClC,IAAI,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,SAAS;QAC7C,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAE,EAAE,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS;IAChC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,MAAM,aAAa,GAA2C;IACnE,IAAI,EAAE,YAAY;IAClB,WAAW,EACT,kGAAkG;QAClG,mGAAmG;QACnG,qGAAqG;QACrG,uFAAuF;IACzF,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,GAAG,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,mEAAmE,EAAE;YACzG,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,qEAAqE,EAAE;SACvI;QACD,oBAAoB,EAAE,KAAK;KAC5B;IACD,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG;QACtB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;QACzE,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,qBAAqB,GAAG,GAAG,CAAC;QAC5D,MAAM,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,GAAG,CAAC;QAErD,IAAI,OAAO,GAA2B,EAAE,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;YAChF,OAAO,GAAI,GAAG,CAAC,OAAkC,IAAI,EAAE,CAAC;QAC1D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,wFAAwF,CAAC;QAClG,CAAC;QACD,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,4DAA4D,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC;QAClH,CAAC;QAED,8CAA8C;QAC9C,MAAM,GAAG,GAAG,KAAK,EAAE,CAAS,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1H,IAAI,EAAE,GAAG,KAAK,CAAC;QACf,IAAI,MAAM,GAAG,CAAC,gBAAgB,CAAC;YAAE,EAAE,GAAG,MAAM,CAAC;aACxC,IAAI,MAAM,GAAG,CAAC,WAAW,CAAC;YAAE,EAAE,GAAG,MAAM,CAAC;aACxC,IAAI,MAAM,GAAG,CAAC,WAAW,CAAC;YAAE,EAAE,GAAG,KAAK,CAAC;QAE5C,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;YAClH,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;gBACjB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,kBAAkB,EAAE,GAAG,CAAC,CAAC;gBACjD,SAAS;YACX,CAAC;YACD,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;gBACf,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,sBAAsB,CAAC,CAAC;gBAChD,SAAS;YACX,CAAC;YACD,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACjB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;gBAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACjD,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,EAAE,QAAQ,CAAC,CAAC,MAAM,SAAS,GAAG,EAAE,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QACD,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/D,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACxE,CAAC;CACF,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * scheduled_dispatch (vibe-coding) — schedule an agent to run a task on a
3
+ * recurring basis: "every night triage the issue backlog", "every 2h sync the
4
+ * docs". Entries are stored and fired by the heartbeat at its cadence.
5
+ *
6
+ * SAFETY: this auto-launches an autonomous agent unattended (no approval at
7
+ * fire time, like any heartbeat action). Each entry has a maxRuns cap (default
8
+ * 30) so a forgotten schedule can't run forever. Needs the heartbeat installed
9
+ * (`lisa heartbeat install`) to actually fire.
10
+ */
11
+ import type { ToolDefinition } from "../types.js";
12
+ import { type ScheduledAgent } from "../integrations/scheduled-dispatch.js";
13
+ interface ScheduledDispatchInput {
14
+ action: "add" | "list" | "remove";
15
+ agent?: ScheduledAgent;
16
+ task?: string;
17
+ cwd?: string;
18
+ /** "every:30m" | "every:2h" | "every:1d" | "daily:09:00" */
19
+ schedule?: string;
20
+ /** Max times to fire before going inert. Default 30. */
21
+ max_runs?: number;
22
+ /** For remove: the entry id (or prefix). */
23
+ id?: string;
24
+ }
25
+ export declare const scheduledDispatchTool: ToolDefinition<ScheduledDispatchInput, string>;
26
+ export {};
27
+ //# sourceMappingURL=scheduled_dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled_dispatch.d.ts","sourceRoot":"","sources":["../../src/tools/scheduled_dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,uCAAuC,CAAC;AAE/C,UAAU,sBAAsB;IAC9B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAClC,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4CAA4C;IAC5C,EAAE,CAAC,EAAE,MAAM,CAAC;CACb;AAED,eAAO,MAAM,qBAAqB,EAAE,cAAc,CAAC,sBAAsB,EAAE,MAAM,CA0DhF,CAAC"}
@@ -0,0 +1,57 @@
1
+ import { loadScheduled, addScheduled, removeScheduled, isValidSchedule, describeScheduled, DEFAULT_MAX_RUNS, } from "../integrations/scheduled-dispatch.js";
2
+ export const scheduledDispatchTool = {
3
+ name: "scheduled_dispatch",
4
+ description: "Schedule a CLI agent to run a task recurringly (fired by the heartbeat). action:'add' needs " +
5
+ "agent + task + cwd + schedule ('every:30m'/'every:2h'/'every:1d'/'daily:09:00'); 'list' shows " +
6
+ "scheduled dispatches; 'remove' (needs id) deletes one. Use for standing autonomous work like " +
7
+ "nightly issue triage or periodic doc sync. It auto-launches an agent unattended, so it has a " +
8
+ "maxRuns cap (default 30) and needs `lisa heartbeat install` to fire.",
9
+ inputSchema: {
10
+ type: "object",
11
+ properties: {
12
+ action: { type: "string", enum: ["add", "list", "remove"] },
13
+ agent: { type: "string", enum: ["claude", "codex", "opencode", "aider"], description: "Which agent to launch (for add)." },
14
+ task: { type: "string", description: "Task/prompt for the agent (for add).", minLength: 1 },
15
+ cwd: { type: "string", description: "Absolute working directory (for add). Defaults to the current directory." },
16
+ schedule: { type: "string", description: "every:30m | every:2h | every:1d | daily:HH:MM (for add)." },
17
+ max_runs: { type: "integer", minimum: 1, maximum: 1000, description: `Max fires before inert (default ${DEFAULT_MAX_RUNS}).` },
18
+ id: { type: "string", description: "Entry id or prefix (for remove)." },
19
+ },
20
+ required: ["action"],
21
+ additionalProperties: false,
22
+ },
23
+ async execute(input, ctx) {
24
+ if (input.action === "list") {
25
+ const entries = await loadScheduled();
26
+ if (entries.length === 0)
27
+ return "(no scheduled dispatches)";
28
+ return `${entries.length} scheduled dispatch(es):\n` + entries.map(describeScheduled).join("\n");
29
+ }
30
+ if (input.action === "remove") {
31
+ if (!input.id)
32
+ return "(remove needs an id — from scheduled_dispatch action:'list')";
33
+ const ok = await removeScheduled(input.id);
34
+ return ok ? `Removed scheduled dispatch ${input.id}.` : `(no scheduled dispatch matches "${input.id}")`;
35
+ }
36
+ // add
37
+ if (!input.agent || !input.task || !input.schedule) {
38
+ return "(add needs agent, task and schedule)";
39
+ }
40
+ if (!isValidSchedule(input.schedule)) {
41
+ return `(bad schedule "${input.schedule}" — use every:30m / every:2h / every:1d / daily:09:00)`;
42
+ }
43
+ const cwd = input.cwd && input.cwd.startsWith("/") ? input.cwd : ctx.cwd;
44
+ const entry = await addScheduled({
45
+ agent: input.agent,
46
+ task: input.task,
47
+ cwd,
48
+ schedule: input.schedule,
49
+ maxRuns: input.max_runs ?? DEFAULT_MAX_RUNS,
50
+ });
51
+ const heartbeatNote = "\n(Fires via the heartbeat — if you haven't, run `lisa heartbeat install` so it actually ticks.)";
52
+ return (`Scheduled ${entry.agent} ${entry.schedule} in ${cwd} (id ${entry.id}, max ${entry.maxRuns} runs):\n` +
53
+ ` "${input.task.slice(0, 100)}"` +
54
+ heartbeatNote);
55
+ },
56
+ };
57
+ //# sourceMappingURL=scheduled_dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scheduled_dispatch.js","sourceRoot":"","sources":["../../src/tools/scheduled_dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * signal_agent (L3/O5 DISPATCH — active control) — the other half of
3
+ * dispatch_agent. Once LISA has launched CLI agents she should be able to act
4
+ * on what she observes: see which of her dispatched agents are still running
5
+ * and stop one that's stuck, conflicting, or burning tokens.
6
+ *
7
+ * Scope & safety:
8
+ * - It only ever targets agents recorded in the dispatch ledger — i.e. ones
9
+ * LISA *herself* launched via dispatch_agent. The user's own manually
10
+ * started sessions are observed via session files, carry no pid, and are
11
+ * therefore unreachable here. LISA cannot kill an arbitrary process.
12
+ * - `cancel` terminates an autonomous process the host already approved
13
+ * LISA to spawn; it's the inverse of that approval. It sends SIGTERM to the
14
+ * whole process *group* (dispatch spawns detached group leaders, so the
15
+ * agent's child processes die with it), then escalates to SIGKILL after a
16
+ * short grace period if the group is still alive.
17
+ *
18
+ * Composes with the advisor: "codex looks stuck in ~/p — cancel it?" →
19
+ * signal_agent({action:"cancel", target:"<id>"}).
20
+ */
21
+ import type { ToolDefinition } from "../types.js";
22
+ import { type DispatchEntry } from "../integrations/dispatch-ledger.js";
23
+ interface SignalInput {
24
+ action: "list" | "cancel";
25
+ /** For cancel: the dispatch id (preferred) or pid of the agent to stop. */
26
+ target?: string;
27
+ /** Skip the graceful SIGTERM and SIGKILL the group immediately. */
28
+ force?: boolean;
29
+ }
30
+ /** Human-friendly elapsed time, e.g. "3m", "2h 5m", "8s". */
31
+ export declare function formatUptime(ms: number): string;
32
+ /** One ledger entry → a readable line. Pure, for display + tests. */
33
+ export declare function formatDispatchLine(e: DispatchEntry, now: number): string;
34
+ export declare const signalAgentTool: ToolDefinition<SignalInput, string>;
35
+ export {};
36
+ //# sourceMappingURL=signal_agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal_agent.d.ts","sourceRoot":"","sources":["../../src/tools/signal_agent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAKL,KAAK,aAAa,EACnB,MAAM,oCAAoC,CAAC;AAE5C,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC1B,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,6DAA6D;AAC7D,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAQ/C;AAED,qEAAqE;AACrE,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIxE;AAsBD,eAAO,MAAM,eAAe,EAAE,cAAc,CAAC,WAAW,EAAE,MAAM,CA2F/D,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * signal_agent (L3/O5 DISPATCH — active control) — the other half of
3
+ * dispatch_agent. Once LISA has launched CLI agents she should be able to act
4
+ * on what she observes: see which of her dispatched agents are still running
5
+ * and stop one that's stuck, conflicting, or burning tokens.
6
+ *
7
+ * Scope & safety:
8
+ * - It only ever targets agents recorded in the dispatch ledger — i.e. ones
9
+ * LISA *herself* launched via dispatch_agent. The user's own manually
10
+ * started sessions are observed via session files, carry no pid, and are
11
+ * therefore unreachable here. LISA cannot kill an arbitrary process.
12
+ * - `cancel` terminates an autonomous process the host already approved
13
+ * LISA to spawn; it's the inverse of that approval. It sends SIGTERM to the
14
+ * whole process *group* (dispatch spawns detached group leaders, so the
15
+ * agent's child processes die with it), then escalates to SIGKILL after a
16
+ * short grace period if the group is still alive.
17
+ *
18
+ * Composes with the advisor: "codex looks stuck in ~/p — cancel it?" →
19
+ * signal_agent({action:"cancel", target:"<id>"}).
20
+ */
21
+ import { findDispatch, listLiveDispatches, removeDispatch, isAlive, } from "../integrations/dispatch-ledger.js";
22
+ /** Human-friendly elapsed time, e.g. "3m", "2h 5m", "8s". */
23
+ export function formatUptime(ms) {
24
+ if (ms < 0)
25
+ ms = 0;
26
+ const s = Math.floor(ms / 1000);
27
+ if (s < 60)
28
+ return `${s}s`;
29
+ const m = Math.floor(s / 60);
30
+ if (m < 60)
31
+ return `${m}m`;
32
+ const h = Math.floor(m / 60);
33
+ return `${h}h ${m % 60}m`;
34
+ }
35
+ /** One ledger entry → a readable line. Pure, for display + tests. */
36
+ export function formatDispatchLine(e, now) {
37
+ const up = formatUptime(now - e.startedAt);
38
+ const task = e.task.length > 70 ? e.task.slice(0, 67) + "…" : e.task;
39
+ return `• ${e.id} ${e.agent} (pid ${e.pid}, up ${up}) ${e.cwd}\n "${task}"`;
40
+ }
41
+ /**
42
+ * Send `signal` to the process group led by `pid`, falling back to the lone
43
+ * process if the group send fails. Returns true if either delivery succeeded.
44
+ */
45
+ function signalGroup(pid, signal) {
46
+ try {
47
+ process.kill(-pid, signal); // negative pid → whole process group
48
+ return true;
49
+ }
50
+ catch {
51
+ try {
52
+ process.kill(pid, signal);
53
+ return true;
54
+ }
55
+ catch {
56
+ return false; // already gone, or not permitted
57
+ }
58
+ }
59
+ }
60
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
61
+ export const signalAgentTool = {
62
+ name: "signal_agent",
63
+ description: "List or stop the CLI agents LISA dispatched. action:'list' shows the still-running " +
64
+ "agents LISA launched via dispatch_agent (id, agent, pid, uptime, cwd, task). " +
65
+ "action:'cancel' (needs target = the id or pid from the list) stops one: SIGTERM to " +
66
+ "its process group, escalating to SIGKILL after a grace period (or pass force:true to " +
67
+ "kill immediately). Use when a dispatched agent is stuck, conflicting, or running away. " +
68
+ "Only agents LISA herself dispatched can be targeted — never the user's own sessions.",
69
+ inputSchema: {
70
+ type: "object",
71
+ properties: {
72
+ action: {
73
+ type: "string",
74
+ enum: ["list", "cancel"],
75
+ description: "'list' the running dispatched agents, or 'cancel' one.",
76
+ },
77
+ target: {
78
+ type: "string",
79
+ description: "For cancel: the dispatch id (preferred) or the pid of the agent to stop. From action:'list'.",
80
+ },
81
+ force: {
82
+ type: "boolean",
83
+ description: "Cancel only: skip graceful SIGTERM and SIGKILL the process group immediately (default false).",
84
+ },
85
+ },
86
+ required: ["action"],
87
+ additionalProperties: false,
88
+ },
89
+ async execute(input, ctx) {
90
+ const now = Date.now();
91
+ if (input.action === "list") {
92
+ const live = listLiveDispatches();
93
+ if (live.length === 0) {
94
+ return "No agents dispatched by LISA are currently running. (Only agents launched via dispatch_agent are tracked here.)";
95
+ }
96
+ const lines = live
97
+ .sort((a, b) => a.startedAt - b.startedAt)
98
+ .map((e) => formatDispatchLine(e, now))
99
+ .join("\n");
100
+ return `${live.length} dispatched agent${live.length === 1 ? "" : "s"} running:\n${lines}\n\nCancel one with signal_agent({action:"cancel", target:"<id>"}).`;
101
+ }
102
+ // action === "cancel"
103
+ const target = (input.target ?? "").trim();
104
+ if (!target) {
105
+ return 'cancel needs a target — the dispatch id or pid from signal_agent({action:"list"}).';
106
+ }
107
+ const entry = findDispatch(target);
108
+ if (!entry) {
109
+ const live = listLiveDispatches();
110
+ const hint = live.length > 0
111
+ ? ` Currently running: ${live.map((e) => `${e.id} (${e.agent}, pid ${e.pid})`).join(", ")}.`
112
+ : " No dispatched agents are running.";
113
+ return `No running dispatched agent matches "${target}".${hint}`;
114
+ }
115
+ const sig = input.force ? "SIGKILL" : "SIGTERM";
116
+ const delivered = signalGroup(entry.pid, sig);
117
+ if (!delivered && !isAlive(entry.pid)) {
118
+ // It exited on its own between the lookup and the signal — clean up.
119
+ removeDispatch(entry.id);
120
+ return `${entry.agent} (pid ${entry.pid}) had already exited; removed it from the ledger.`;
121
+ }
122
+ let escalated = false;
123
+ if (!input.force) {
124
+ // Give it a moment to shut down gracefully, then SIGKILL if it clings on.
125
+ await sleep(1500);
126
+ if (isAlive(entry.pid)) {
127
+ escalated = signalGroup(entry.pid, "SIGKILL");
128
+ }
129
+ }
130
+ removeDispatch(entry.id);
131
+ ctx.log(`[signal] cancelled ${entry.agent} (pid ${entry.pid}) in ${entry.cwd}${escalated ? " [SIGKILL]" : ""}`);
132
+ const how = input.force
133
+ ? "SIGKILL"
134
+ : escalated
135
+ ? "SIGTERM then SIGKILL"
136
+ : "SIGTERM";
137
+ return `Cancelled ${entry.agent} (pid ${entry.pid}) in ${entry.cwd} via ${how}. Removed from the dispatch ledger.`;
138
+ },
139
+ };
140
+ //# sourceMappingURL=signal_agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signal_agent.js","sourceRoot":"","sources":["../../src/tools/signal_agent.ts"],"names":[],"mappings":"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"}