@easynet/agent-runtime 1.0.3 → 1.0.4

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 (205) hide show
  1. package/.github/workflows/ci.yml +9 -24
  2. package/.github/workflows/release.yml +14 -35
  3. package/agent-runtime/.github/workflows/ci.yml +69 -0
  4. package/agent-runtime/.github/workflows/release.yml +118 -0
  5. package/agent-runtime/.releaserc.cjs +26 -0
  6. package/agent-runtime/config/agent.deep.yaml +25 -0
  7. package/agent-runtime/config/agent.react.yaml +24 -0
  8. package/agent-runtime/example/basic-usage.ts +49 -0
  9. package/agent-runtime/package-lock.json +7740 -0
  10. package/agent-runtime/package.json +49 -0
  11. package/agent-runtime/pnpm-lock.yaml +3712 -0
  12. package/agent-runtime/scripts/resolve-deps.js +54 -0
  13. package/agent-runtime/src/agents/deep-agent.ts +165 -0
  14. package/agent-runtime/src/agents/react-agent.helpers.ts +227 -0
  15. package/agent-runtime/src/agents/react-agent.ts +584 -0
  16. package/{src → agent-runtime/src/agents}/sub-agent.ts +2 -2
  17. package/agent-runtime/src/cli/args.ts +15 -0
  18. package/agent-runtime/src/cli/event-listener.ts +162 -0
  19. package/agent-runtime/src/cli/interactive.ts +144 -0
  20. package/agent-runtime/src/cli/runtime.ts +31 -0
  21. package/agent-runtime/src/cli/spinner.ts +23 -0
  22. package/agent-runtime/src/cli/terminal-render.ts +322 -0
  23. package/agent-runtime/src/cli/types.ts +33 -0
  24. package/agent-runtime/src/cli.ts +134 -0
  25. package/agent-runtime/src/config/helpers.ts +179 -0
  26. package/agent-runtime/src/config/index.ts +245 -0
  27. package/agent-runtime/src/config/types.ts +62 -0
  28. package/agent-runtime/src/core/context.ts +266 -0
  29. package/agent-runtime/src/index.ts +55 -0
  30. package/agent-runtime/tsconfig.json +18 -0
  31. package/apps/imessagebot/README.md +38 -0
  32. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/README.md +33 -0
  33. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package-lock.json +15257 -0
  34. package/apps/imessagebot/config/.agent/cache/easynet/agent-tool-buildin/0.0.45/package.json +55 -0
  35. package/apps/imessagebot/config/agents/deep/agent.yaml +31 -0
  36. package/apps/imessagebot/config/agents/react/agent.yaml +58 -0
  37. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/README.md +33 -0
  38. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package-lock.json +15457 -0
  39. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.43/package.json +55 -0
  40. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/README.md +33 -0
  41. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package-lock.json +15257 -0
  42. package/apps/imessagebot/config/agents/shared/.agent/cache/easynet/agent-tool-buildin/0.0.46/package.json +62 -0
  43. package/apps/imessagebot/config/agents/shared/memory.yaml +31 -0
  44. package/apps/imessagebot/config/agents/shared/model.yaml +23 -0
  45. package/apps/imessagebot/config/agents/shared/tool.yaml +13 -0
  46. package/apps/imessagebot/config/app.yaml +14 -0
  47. package/apps/imessagebot/package-lock.json +53695 -0
  48. package/apps/imessagebot/package.json +41 -0
  49. package/apps/imessagebot/pnpm-lock.yaml +1589 -0
  50. package/apps/imessagebot/scripts/resolve-deps.js +41 -0
  51. package/apps/imessagebot/scripts/test-llm.mjs +27 -0
  52. package/apps/imessagebot/scripts/validate-tools-config.mjs +174 -0
  53. package/apps/imessagebot/src/config.ts +76 -0
  54. package/apps/imessagebot/src/context.ts +35 -0
  55. package/apps/imessagebot/src/index.ts +17 -0
  56. package/apps/imessagebot/tsconfig.json +18 -0
  57. package/apps/itermbot/.github/workflows/ci.yml +61 -0
  58. package/apps/itermbot/.github/workflows/release.yml +80 -0
  59. package/apps/itermbot/.releaserc.cjs +26 -0
  60. package/apps/itermbot/README.md +82 -0
  61. package/apps/itermbot/config/app.yaml +29 -0
  62. package/apps/itermbot/config/tsconfig.json +18 -0
  63. package/apps/itermbot/macos_disk_usage_agent_plan.md +244 -0
  64. package/apps/itermbot/package-lock.json +53697 -0
  65. package/apps/itermbot/package.json +57 -0
  66. package/apps/itermbot/pnpm-lock.yaml +3966 -0
  67. package/apps/itermbot/scripts/patch-buildin-cache.sh +25 -0
  68. package/apps/itermbot/scripts/resolve-deps.js +41 -0
  69. package/apps/itermbot/scripts/test-llm.mjs +32 -0
  70. package/apps/itermbot/skills/command-explain-and-guard/SKILL.md +39 -0
  71. package/apps/itermbot/skills/command-explain-and-guard/handler.js +86 -0
  72. package/apps/itermbot/skills/disk-usage-investigate/SKILL.md +44 -0
  73. package/apps/itermbot/skills/disk-usage-investigate/handler.js +12 -0
  74. package/apps/itermbot/skills/gpu-ssh-monitor/SKILL.md +64 -0
  75. package/apps/itermbot/skills/repo-triage/SKILL.md +40 -0
  76. package/apps/itermbot/skills/repo-triage/handler.js +56 -0
  77. package/apps/itermbot/skills/test-failure-diagnose/SKILL.md +43 -0
  78. package/apps/itermbot/skills/test-failure-diagnose/handler.js +107 -0
  79. package/apps/itermbot/src/config.ts +95 -0
  80. package/apps/itermbot/src/context.ts +35 -0
  81. package/apps/itermbot/src/index.ts +223 -0
  82. package/apps/itermbot/src/iterm/session-hint.ts +40 -0
  83. package/apps/itermbot/src/iterm/target-routing.ts +419 -0
  84. package/apps/itermbot/src/startup/colors.ts +317 -0
  85. package/apps/itermbot/src/startup/diagnostics.ts +97 -0
  86. package/apps/itermbot/src/startup/ui.ts +141 -0
  87. package/config/agent.deep.yaml +25 -0
  88. package/config/agent.react.yaml +24 -0
  89. package/dist/agents/deep-agent.d.ts +37 -0
  90. package/dist/agents/deep-agent.d.ts.map +1 -0
  91. package/dist/agents/deep-agent.js +115 -0
  92. package/dist/agents/deep-agent.js.map +1 -0
  93. package/dist/agents/react-agent.d.ts +40 -0
  94. package/dist/agents/react-agent.d.ts.map +1 -0
  95. package/dist/agents/react-agent.helpers.d.ts +40 -0
  96. package/dist/agents/react-agent.helpers.d.ts.map +1 -0
  97. package/dist/agents/react-agent.helpers.js +196 -0
  98. package/dist/agents/react-agent.helpers.js.map +1 -0
  99. package/dist/agents/react-agent.js +400 -0
  100. package/dist/agents/react-agent.js.map +1 -0
  101. package/dist/agents/sub-agent.d.ts +34 -0
  102. package/dist/agents/sub-agent.d.ts.map +1 -0
  103. package/dist/agents/sub-agent.js +53 -0
  104. package/dist/agents/sub-agent.js.map +1 -0
  105. package/dist/cli/args.d.ts +8 -0
  106. package/dist/cli/args.d.ts.map +1 -0
  107. package/dist/cli/args.js +9 -0
  108. package/dist/cli/args.js.map +1 -0
  109. package/dist/cli/event-listener.d.ts +3 -0
  110. package/dist/cli/event-listener.d.ts.map +1 -0
  111. package/dist/cli/event-listener.js +131 -0
  112. package/dist/cli/event-listener.js.map +1 -0
  113. package/dist/cli/interactive.d.ts +4 -0
  114. package/dist/cli/interactive.d.ts.map +1 -0
  115. package/dist/cli/interactive.js +118 -0
  116. package/dist/cli/interactive.js.map +1 -0
  117. package/dist/cli/runtime.d.ts +8 -0
  118. package/dist/cli/runtime.d.ts.map +1 -0
  119. package/dist/cli/runtime.js +27 -0
  120. package/dist/cli/runtime.js.map +1 -0
  121. package/dist/cli/spinner.d.ts +2 -0
  122. package/dist/cli/spinner.d.ts.map +1 -0
  123. package/dist/cli/spinner.js +22 -0
  124. package/dist/cli/spinner.js.map +1 -0
  125. package/dist/cli/terminal-render.d.ts +7 -0
  126. package/dist/cli/terminal-render.d.ts.map +1 -0
  127. package/dist/cli/terminal-render.js +282 -0
  128. package/dist/cli/terminal-render.js.map +1 -0
  129. package/dist/cli/types.d.ts +29 -0
  130. package/dist/cli/types.d.ts.map +1 -0
  131. package/dist/cli/types.js +3 -0
  132. package/dist/cli/types.js.map +1 -0
  133. package/dist/cli.d.ts +4 -41
  134. package/dist/cli.d.ts.map +1 -1
  135. package/dist/cli.js +84 -588
  136. package/dist/cli.js.map +1 -1
  137. package/dist/config/helpers.d.ts +6 -0
  138. package/dist/config/helpers.d.ts.map +1 -0
  139. package/dist/config/helpers.js +164 -0
  140. package/dist/config/helpers.js.map +1 -0
  141. package/dist/config/index.d.ts +15 -0
  142. package/dist/config/index.d.ts.map +1 -0
  143. package/dist/config/index.js +160 -0
  144. package/dist/config/index.js.map +1 -0
  145. package/dist/config/types.d.ts +57 -0
  146. package/dist/config/types.d.ts.map +1 -0
  147. package/dist/config/types.js +2 -0
  148. package/dist/config/types.js.map +1 -0
  149. package/dist/context.d.ts +8 -69
  150. package/dist/context.d.ts.map +1 -1
  151. package/dist/context.js +44 -24
  152. package/dist/context.js.map +1 -1
  153. package/dist/core/context.d.ts +66 -0
  154. package/dist/core/context.d.ts.map +1 -0
  155. package/dist/core/context.js +149 -0
  156. package/dist/core/context.js.map +1 -0
  157. package/dist/deep-agent.d.ts +5 -2
  158. package/dist/deep-agent.d.ts.map +1 -1
  159. package/dist/deep-agent.js +44 -11
  160. package/dist/deep-agent.js.map +1 -1
  161. package/dist/index.d.ts +6 -6
  162. package/dist/index.d.ts.map +1 -1
  163. package/dist/index.js +6 -6
  164. package/dist/index.js.map +1 -1
  165. package/dist/middleware/malformed-tool-call-middleware.d.ts +8 -0
  166. package/dist/middleware/malformed-tool-call-middleware.d.ts.map +1 -0
  167. package/dist/middleware/malformed-tool-call-middleware.js +191 -0
  168. package/dist/middleware/malformed-tool-call-middleware.js.map +1 -0
  169. package/dist/react-agent.d.ts +2 -2
  170. package/dist/react-agent.d.ts.map +1 -1
  171. package/dist/react-agent.js +28 -9
  172. package/dist/react-agent.js.map +1 -1
  173. package/package.json +1 -1
  174. package/scripts/resolve-deps.js +54 -0
  175. package/src/agents/deep-agent.ts +165 -0
  176. package/src/agents/react-agent.helpers.ts +227 -0
  177. package/src/agents/react-agent.ts +584 -0
  178. package/src/agents/sub-agent.ts +82 -0
  179. package/src/cli/args.ts +15 -0
  180. package/src/cli/event-listener.ts +162 -0
  181. package/src/cli/interactive.ts +144 -0
  182. package/src/cli/runtime.ts +31 -0
  183. package/src/cli/spinner.ts +23 -0
  184. package/src/cli/terminal-render.ts +322 -0
  185. package/src/cli/types.ts +33 -0
  186. package/src/cli.ts +91 -702
  187. package/src/config/helpers.ts +179 -0
  188. package/src/config/index.ts +245 -0
  189. package/src/config/types.ts +62 -0
  190. package/src/core/context.ts +266 -0
  191. package/src/index.ts +13 -11
  192. package/src/middleware/malformed-tool-call-middleware.ts +239 -0
  193. package/src/types/markdown-it-terminal.d.ts +4 -0
  194. package/src/types/marked-terminal.d.ts +16 -0
  195. package/dist/config.d.ts +0 -86
  196. package/dist/config.d.ts.map +0 -1
  197. package/dist/config.js +0 -84
  198. package/dist/config.js.map +0 -1
  199. package/src/config.ts +0 -177
  200. package/src/context.ts +0 -247
  201. package/src/deep-agent.ts +0 -104
  202. package/src/react-agent.ts +0 -576
  203. /package/{src → agent-runtime/src/middleware}/malformed-tool-call-middleware.ts +0 -0
  204. /package/{src → agent-runtime/src/types}/markdown-it-terminal.d.ts +0 -0
  205. /package/{src → agent-runtime/src/types}/marked-terminal.d.ts +0 -0
package/dist/cli.js CHANGED
@@ -2,613 +2,109 @@
2
2
  * Shared CLI runner for agent apps.
3
3
  * Provides: arg parsing, single-shot mode, interactive REPL, event logging.
4
4
  */
5
- import os from "node:os";
6
- import { createInterface } from "node:readline";
7
- import MarkdownIt from "markdown-it";
8
- import markdownItTerminal from "markdown-it-terminal";
9
- import { createConsoleAgentEventListener, } from "@easynet/agent-common";
10
- import { createReactAgent } from "./react-agent.js";
11
- import { createDeepAgent } from "./deep-agent.js";
12
- import { malformedToolCallMiddleware } from "./malformed-tool-call-middleware.js";
13
- const REACT = "react";
14
- const DEEP = "deep";
15
- function parseArgs() {
16
- const args = process.argv.slice(2);
17
- const first = args[0]?.toLowerCase();
18
- const kind = first === DEEP ? DEEP : REACT;
19
- const query = first === REACT || first === DEEP ? (args[1] ?? undefined) : (first ?? undefined);
20
- return { kind, query };
21
- }
22
- function escapeRegExp(value) {
23
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
24
- }
25
- async function createRuntime(ctx, kind) {
26
- if (kind === REACT) {
27
- return createReactAgent(ctx, {
28
- middleware: [malformedToolCallMiddleware()],
29
- });
30
- }
31
- return createDeepAgent(ctx);
32
- }
33
- async function runOne(ctx, kind, query) {
34
- const runtime = await createRuntime(ctx, kind);
35
- const { text } = await runtime.run(query);
36
- return text;
37
- }
38
- function createAnsi(useColor) {
39
- return {
40
- reset: useColor ? "\x1b[0m" : "",
41
- dim: useColor ? "\x1b[2m" : "",
42
- heading: useColor ? "\x1b[1;38;5;45m" : "",
43
- bullet: useColor ? "\x1b[38;5;81m" : "",
44
- code: useColor ? "\x1b[38;5;221m" : "",
45
- quote: useColor ? "\x1b[38;5;245m" : "",
46
- hr: useColor ? "\x1b[38;5;240m" : "",
47
- };
48
- }
49
- function styleInlineMarkdown(line, ansi) {
50
- return line
51
- .replace(/`([^`]+)`/g, `${ansi.code}\`$1\`${ansi.reset}`)
52
- .replace(/\*\*([^*]+)\*\*/g, `${ansi.heading}$1${ansi.reset}`);
53
- }
54
- function parseTableRow(line) {
55
- const trimmed = line.trim().replace(/^\|/, "").replace(/\|$/, "");
56
- return trimmed.split("|").map((cell) => cell.trim());
57
- }
58
- function isTableSeparatorRow(cells) {
59
- return cells.length > 0 && cells.every((c) => /^:?-{3,}:?$/.test(c));
60
- }
61
- function renderTable(rows, ansi) {
62
- const normalized = rows.map((r) => [...r]);
63
- const colCount = Math.max(...normalized.map((r) => r.length));
64
- for (const row of normalized) {
65
- while (row.length < colCount)
66
- row.push("");
67
- }
68
- const widths = Array.from({ length: colCount }, (_, i) => Math.max(...normalized.map((r) => (r[i] ?? "").length)));
69
- const line = `+${widths.map((w) => "-".repeat(w + 2)).join("+")}+`;
70
- const out = [`${ansi.hr}${line}${ansi.reset}`];
71
- normalized.forEach((row, idx) => {
72
- const body = row
73
- .map((cell, i) => ` ${(cell ?? "").padEnd(widths[i] ?? 0)} `)
74
- .join("|");
75
- const styledBody = idx === 0
76
- ? `${ansi.heading}|${styleInlineMarkdown(body, ansi)}|${ansi.reset}`
77
- : `|${styleInlineMarkdown(body, ansi)}|`;
78
- out.push(styledBody);
79
- if (idx === 0)
80
- out.push(`${ansi.hr}${line}${ansi.reset}`);
81
- });
82
- out.push(`${ansi.hr}${line}${ansi.reset}`);
83
- return out;
84
- }
85
- function formatMarkdownForTerminal(markdown, options = { useColor: true }) {
86
- const ansi = createAnsi(options.useColor);
87
- const lines = markdown.replace(/\r\n/g, "\n").split("\n");
88
- const out = [];
89
- let inCodeBlock = false;
90
- let codeFenceLang = "";
91
- let codeBoxWidth = 0;
92
- for (let i = 0; i < lines.length; i += 1) {
93
- const rawLine = lines[i];
94
- const line = rawLine ?? "";
95
- if (line.trim().startsWith("```")) {
96
- if (!inCodeBlock) {
97
- inCodeBlock = true;
98
- codeFenceLang = line.trim().slice(3).trim();
99
- const title = codeFenceLang ? ` code:${codeFenceLang} ` : " code ";
100
- codeBoxWidth = Math.max(16, title.length + 4);
101
- out.push(`${ansi.hr}┌${"─".repeat(codeBoxWidth)}┐${ansi.reset}`);
102
- out.push(`${ansi.hr}│${ansi.reset}${ansi.dim}${title.padEnd(codeBoxWidth)}${ansi.reset}${ansi.hr}│${ansi.reset}`);
103
- }
104
- else {
105
- inCodeBlock = false;
106
- codeFenceLang = "";
107
- out.push(`${ansi.hr}└${"─".repeat(codeBoxWidth)}┘${ansi.reset}`);
108
- }
109
- continue;
110
- }
111
- if (inCodeBlock) {
112
- const content = line.length > codeBoxWidth ? line.slice(0, codeBoxWidth) : line.padEnd(codeBoxWidth);
113
- out.push(`${ansi.hr}│${ansi.reset}${ansi.code}${content}${ansi.reset}${ansi.hr}│${ansi.reset}`);
114
- continue;
115
- }
116
- // Markdown table: header row + separator row + data rows
117
- if (line.includes("|") &&
118
- i + 1 < lines.length &&
119
- (lines[i + 1] ?? "").includes("|")) {
120
- const header = parseTableRow(line);
121
- const sep = parseTableRow(lines[i + 1] ?? "");
122
- if (header.length > 1 && header.length === sep.length && isTableSeparatorRow(sep)) {
123
- const rows = [header];
124
- let j = i + 2;
125
- while (j < lines.length && (lines[j] ?? "").includes("|")) {
126
- rows.push(parseTableRow(lines[j] ?? ""));
127
- j += 1;
128
- }
129
- out.push(...renderTable(rows, ansi));
130
- i = j - 1;
131
- continue;
132
- }
133
- }
134
- if (/^\s*#{1,6}\s+/.test(line)) {
135
- const headingText = line.replace(/^\s*#{1,6}\s+/, "").trim();
136
- out.push(`${ansi.heading}${headingText}${ansi.reset}`);
137
- continue;
138
- }
139
- if (/^\s*>\s?/.test(line)) {
140
- out.push(`${ansi.quote}${line}${ansi.reset}`);
141
- continue;
142
- }
143
- if (/^\s*([-*]|\d+\.)\s+/.test(line)) {
144
- out.push(`${ansi.bullet}${styleInlineMarkdown(line, ansi)}${ansi.reset}`);
145
- continue;
146
- }
147
- if (/^\s*---+\s*$/.test(line)) {
148
- out.push(`${ansi.hr}${"─".repeat(56)}${ansi.reset}`);
149
- continue;
150
- }
151
- out.push(styleInlineMarkdown(line, ansi));
152
- }
153
- return out.join("\n");
154
- }
155
- const markdownRenderers = new Map();
156
- function getMarkdownRenderer(useColor) {
157
- const key = useColor ? "color" : "plain";
158
- const cached = markdownRenderers.get(key);
159
- if (cached)
160
- return cached;
161
- const md = new MarkdownIt({
162
- html: false,
163
- linkify: true,
164
- typographer: true,
165
- breaks: false,
166
- });
167
- md.use(markdownItTerminal, {
168
- indent: "",
169
- ...(useColor
170
- ? {}
171
- : {
172
- styleOptions: {
173
- code: (s) => s,
174
- blockquote: (s) => s,
175
- html: (s) => s,
176
- heading: (s) => s,
177
- firstHeading: (s) => s,
178
- hr: (s) => s,
179
- listitem: (s) => s,
180
- table: (s) => s,
181
- paragraph: (s) => s,
182
- strong: (s) => s,
183
- em: (s) => s,
184
- codespan: (s) => s,
185
- del: (s) => s,
186
- link: (s) => s,
187
- href: (s) => s,
188
- },
189
- }),
190
- });
191
- markdownRenderers.set(key, md);
192
- return md;
193
- }
194
- function normalizeAssistantMarkdown(text) {
195
- const source = text.replace(/\r\n/g, "\n");
196
- const hasMarkdownHeadings = /^\s*#{1,6}\s+/m.test(source);
197
- const sanitizeLine = (line) => {
198
- let next = line;
199
- const trimmed = next.trim();
200
- // Remove stray inline fence markers that frequently break markdown rendering.
201
- if (next.includes("```") && !trimmed.startsWith("```")) {
202
- next = next.replace(/```+/g, "").replace(/\s+$/, "");
5
+ import { createConsoleAgentEventListener } from "@easynet/agent-common";
6
+ import { parseArgs } from "./cli/args.js";
7
+ import { createStructuredRunEventListener } from "./cli/event-listener.js";
8
+ import { interactive } from "./cli/interactive.js";
9
+ import { readDefaultAgentKindFromConfig, runOne } from "./cli/runtime.js";
10
+ import { startLoadingSpinner } from "./cli/spinner.js";
11
+ import { renderForTerminal } from "./cli/terminal-render.js";
12
+ import { REACT } from "./cli/types.js";
13
+ export { createStructuredRunEventListener };
14
+ function createExitHandler(appName, onShutdown, ctx) {
15
+ let didCleanup = false;
16
+ const cleanup = () => {
17
+ if (didCleanup)
18
+ return;
19
+ didCleanup = true;
20
+ try {
21
+ onShutdown?.(ctx);
203
22
  }
204
- // Keep headings stable and readable in terminal output.
205
- if (/^\s*#{3,}\s+/.test(next)) {
206
- next = next.replace(/^\s*#{3,}\s+/, "## ");
23
+ catch (err) {
24
+ console.error(`${appName}: onShutdown hook failed:`, err instanceof Error ? err.message : err);
207
25
  }
208
- return next;
209
26
  };
210
- const normalizeListLine = (line) => {
211
- if (/^(\s{2,}|\t+)([*-])\s+/.test(line)) {
212
- return line.replace(/^(\s{2,}|\t+)([*-])\s+/, "- ");
213
- }
214
- if (/^(\s{2,}|\t+)(\d+\.)\s+/.test(line)) {
215
- return line.replace(/^(\s{2,}|\t+)(\d+\.)\s+/, "$2 ");
216
- }
217
- return line;
27
+ process.once("exit", cleanup);
28
+ const exitApp = (code) => {
29
+ cleanup();
30
+ process.exit(code);
218
31
  };
219
- const lines = source
220
- .split("\n")
221
- .map(sanitizeLine)
222
- .map(normalizeListLine);
223
- const titled = lines.map((line) => {
224
- const trimmed = line.trim();
225
- const bulletHeading = trimmed.match(/^[-*]\s+\*\*([^*]+)\*\*:?\s*$/);
226
- if (bulletHeading?.[1]) {
227
- return `### ${bulletHeading[1].trim()}`;
228
- }
229
- const plainBulletHeading = trimmed.match(/^[-*]\s+([^`].+):\s*$/);
230
- if (plainBulletHeading?.[1] && plainBulletHeading[1].length <= 48) {
231
- return `### ${plainBulletHeading[1].trim()}`;
232
- }
233
- if (hasMarkdownHeadings)
234
- return trimmed === "Summary (3‑8 bullets)" ? "## Summary" : line;
235
- const unwrapped = trimmed.replace(/^\*\*(.+)\*\*$/, "$1").trim();
236
- if (/^(Key terminal output.*|Current terminal buffer.*|Summary.*|Next steps.*)$/i.test(unwrapped)) {
237
- return `## ${unwrapped}`;
238
- }
239
- if (/^(Key observations|Findings|Analysis|Conclusion)$/i.test(unwrapped)) {
240
- return `## ${unwrapped}`;
241
- }
242
- return line;
243
- });
244
- // Merge accidental line breaks inside list items so markdown renders as one bullet.
245
- const merged = [];
246
- for (let i = 0; i < titled.length; i += 1) {
247
- const current = titled[i] ?? "";
248
- const next = titled[i + 1] ?? "";
249
- const currentTrim = current.trim();
250
- const nextTrim = next.trim();
251
- const isListLine = /^([-*]|\d+\.)\s+/.test(currentTrim);
252
- const nextStartsNewBlock = nextTrim.length === 0 ||
253
- /^([-*]|\d+\.)\s+/.test(nextTrim) ||
254
- /^#{1,6}\s+/.test(nextTrim) ||
255
- /^```/.test(nextTrim) ||
256
- /^---+$/.test(nextTrim);
257
- const looksLikeWrappedContinuation = isListLine &&
258
- !nextStartsNewBlock &&
259
- /^[0-9./~]/.test(nextTrim);
260
- if (looksLikeWrappedContinuation) {
261
- merged.push(`${current.replace(/\s+$/, "")} ${nextTrim}`);
262
- i += 1;
263
- continue;
264
- }
265
- merged.push(current);
266
- }
267
- const out = [];
268
- for (let i = 0; i < merged.length; i += 1) {
269
- const line = merged[i] ?? "";
270
- out.push(line);
271
- const heading = line.trim();
272
- const isOutputHeading = /^##\s*(Key terminal output|Current terminal buffer|Terminal output)/i.test(heading);
273
- if (!isOutputHeading)
274
- continue;
275
- if ((merged[i + 1] ?? "").trim().startsWith("```"))
276
- continue;
277
- let j = i + 1;
278
- const block = [];
279
- while (j < merged.length) {
280
- const current = merged[j] ?? "";
281
- if (!current.trim()) {
282
- if (block.length === 0) {
283
- j += 1;
284
- continue;
285
- }
286
- break;
287
- }
288
- if (!/^\s{2,}\S/.test(current))
289
- break;
290
- block.push(current.replace(/^\s+/, ""));
291
- j += 1;
292
- }
293
- if (block.length >= 2) {
294
- out.push("```text");
295
- out.push(...block);
296
- out.push("```");
297
- i = j - 1;
298
- }
32
+ process.once("SIGINT", () => exitApp(130));
33
+ process.once("SIGTERM", () => exitApp(143));
34
+ return exitApp;
35
+ }
36
+ function subscribeEventListeners(options, ctx) {
37
+ if (process.env.AGENT_EVENT_STDERR === "1") {
38
+ ctx.events.subscribe(createConsoleAgentEventListener());
299
39
  }
300
- const fencedCount = out.reduce((count, line) => count + (/^\s*```/.test(line) ? 1 : 0), 0);
301
- if (fencedCount % 2 === 1) {
302
- out.push("```");
40
+ const listeners = Array.isArray(options.eventListener)
41
+ ? options.eventListener
42
+ : options.eventListener
43
+ ? [options.eventListener]
44
+ : [];
45
+ for (const listener of listeners) {
46
+ ctx.events.subscribe(listener);
303
47
  }
304
- return out.join("\n");
305
48
  }
306
- function renderForTerminal(text, options) {
307
- if (!options.renderMarkdown)
308
- return text;
309
- const normalizedText = normalizeAssistantMarkdown(text);
49
+ async function runOnReady(options, appName, ctx) {
50
+ if (!options.onReady)
51
+ return;
310
52
  try {
311
- return getMarkdownRenderer(options.useColor).render(normalizedText, {});
53
+ await options.onReady(ctx);
312
54
  }
313
- catch {
314
- return formatMarkdownForTerminal(normalizedText, { useColor: options.useColor });
55
+ catch (err) {
56
+ console.error(`${appName}: onReady hook failed:`, err instanceof Error ? err.message : err);
315
57
  }
316
58
  }
317
- function startLoadingSpinner(message) {
318
- const frames = ["", ".", "..", "..."];
319
- let frameIndex = 0;
320
- let lastLength = 0;
321
- const render = () => {
322
- const frame = frames[frameIndex % frames.length] ?? "";
323
- const trimmed = message.trim();
324
- const base = trimmed.startsWith("⏳") ? trimmed : `⏳ ${trimmed}`;
325
- const line = `${base}${frame}`;
326
- const padded = lastLength > line.length ? line.padEnd(lastLength, " ") : line;
327
- process.stderr.write(`\r${padded}\r`);
328
- lastLength = padded.length;
329
- frameIndex += 1;
330
- };
331
- render();
332
- const timer = setInterval(render, 90);
333
- return () => {
334
- clearInterval(timer);
335
- process.stderr.write(`\r${" ".repeat(lastLength)}\r`);
336
- };
337
- }
338
- async function interactive(ctx, kind, options, exitApp) {
339
- const name = kind === REACT ? "ReAct" : "Deep";
340
- const runtime = await createRuntime(ctx, kind);
341
- const userInfo = os.userInfo();
342
- const userLabel = options.ui?.userLabel ?? `${userInfo.username}`;
343
- const assistantLabel = options.ui?.assistantLabel ?? name;
344
- const useColor = options.ui?.useColor ?? (Boolean(process.stdout.isTTY) && !process.env.NO_COLOR);
345
- const renderMarkdown = options.ui?.renderMarkdown ?? true;
346
- const echoUserQuestion = options.ui?.echoUserQuestion ?? true;
347
- const showProcessingSpinner = options.ui?.processingSpinner ?? Boolean(process.stderr.isTTY);
348
- const processingText = options.ui?.processingText ?? "Processing";
349
- const color = {
350
- reset: useColor ? "\x1b[0m" : "",
351
- dim: useColor ? "\x1b[2m" : "",
352
- user: useColor ? "\x1b[38;5;39m" : "",
353
- bot: useColor ? "\x1b[38;5;48m" : "",
354
- prompt: useColor ? "\x1b[38;5;245m" : "",
355
- promptUser: useColor ? "\x1b[1;38;5;45m" : "",
356
- };
357
- const hr = `${color.dim}${"-".repeat(56)}${color.reset}`;
358
- const promptText = `${color.prompt}[${color.promptUser}${userLabel}${color.reset}${color.prompt}]${color.reset} `;
359
- const rl = createInterface({ input: process.stdin, output: process.stdout });
360
- const userLabelPrefixPattern = new RegExp(`^(?:\\[${escapeRegExp(userLabel)}\\]\\s*)+`, "i");
361
- const prompt = () => rl.question(promptText, async (line) => {
362
- const qRaw = line?.trim();
363
- const q = qRaw?.replace(userLabelPrefixPattern, "").trim();
364
- if (!q) {
365
- prompt();
366
- return;
367
- }
368
- if (q === "exit" || q === "quit") {
369
- rl.close();
370
- exitApp(0);
371
- }
372
- const handler = options.interactiveCommands?.[q.toLowerCase()];
373
- if (handler) {
374
- await handler(ctx);
375
- prompt();
376
- return;
377
- }
378
- let stopProcessingSpinner = null;
379
- try {
380
- console.log(`\n${hr}`);
381
- if (echoUserQuestion) {
382
- console.log(`${color.user}[${userLabel}]${color.reset}`);
383
- console.log(`> ${q}`);
384
- console.log("");
385
- }
386
- console.log(`${color.bot}[${assistantLabel}]${color.reset}`);
387
- stopProcessingSpinner = showProcessingSpinner
388
- ? startLoadingSpinner(processingText === false ? "⏳" : `⏳ ${processingText}`)
389
- : null;
390
- const { text } = await runtime.run(q);
391
- if (stopProcessingSpinner) {
392
- stopProcessingSpinner();
393
- stopProcessingSpinner = null;
394
- }
395
- console.log(renderForTerminal(text, { renderMarkdown, useColor }));
396
- console.log(`${hr}\n`);
397
- }
398
- catch (err) {
399
- if (stopProcessingSpinner) {
400
- stopProcessingSpinner();
401
- stopProcessingSpinner = null;
402
- }
403
- console.error("Error:", err instanceof Error ? err.message : err);
59
+ async function createContextWithLoading(options) {
60
+ const loadingText = options.ui?.loadingText ?? `${options.appName}: loading config, LLM, memory, tools...`;
61
+ const useLoadingSpinner = options.ui?.loadingSpinner ?? false;
62
+ let stopLoadingSpinner = null;
63
+ if (loadingText !== false) {
64
+ if (useLoadingSpinner && Boolean(process.stderr.isTTY)) {
65
+ stopLoadingSpinner = startLoadingSpinner(loadingText);
404
66
  }
405
- finally {
406
- if (stopProcessingSpinner) {
407
- stopProcessingSpinner();
408
- stopProcessingSpinner = null;
409
- }
67
+ else {
68
+ console.error(loadingText);
410
69
  }
411
- prompt();
412
- });
413
- const introText = options.ui?.interactiveIntro ??
414
- `${options.appName} (${name} agent). Type your message or "exit" to quit.`;
415
- if (introText !== false) {
416
- console.log(`${introText}\n`);
417
70
  }
418
- prompt();
71
+ const ctx = await options.createBotContext();
72
+ stopLoadingSpinner?.();
73
+ return ctx;
74
+ }
75
+ function printReady(options, kind) {
76
+ const agentLabel = kind === REACT ? "ReAct (LangChain)" : "Deep (DeepAgents)";
77
+ const readyText = options.ui?.readyText ?? `Ready. Agent: ${agentLabel}`;
78
+ if (readyText !== false) {
79
+ console.error(readyText);
80
+ }
419
81
  }
420
- export function createStructuredRunEventListener(writer = console.error) {
421
- let step = 0;
422
- let lastCommandSignature = "";
423
- const stepActionByNumber = new Map();
424
- const okMark = process.stderr.isTTY && !process.env.NO_COLOR ? "\x1b[32m✔\x1b[0m" : "✔";
425
- const asRecord = (value) => typeof value === "object" && value !== null ? value : null;
426
- const asNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
427
- const asString = (value) => typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
428
- const asAnyString = (value) => typeof value === "string" ? value : null;
429
- const parseJsonObject = (value) => {
430
- if (typeof value !== "string")
431
- return null;
432
- try {
433
- const parsed = JSON.parse(value);
434
- return asRecord(parsed);
435
- }
436
- catch {
437
- return null;
438
- }
439
- };
440
- const extractCommandMeta = (args) => {
441
- const rec = asRecord(args) ?? parseJsonObject(args);
442
- if (!rec)
443
- return null;
444
- const commandRaw = asAnyString(rec.command);
445
- if (commandRaw === null)
446
- return null;
447
- const command = commandRaw.trim();
448
- return {
449
- command,
450
- windowId: asNumber(rec.windowId),
451
- tabIndex: asNumber(rec.tabIndex),
452
- sessionId: asString(rec.sessionId),
453
- };
454
- };
455
- const isNoopCaptureCommand = (command) => {
456
- const normalized = command.trim().replace(/\s+/g, " ");
457
- return (normalized === "" ||
458
- normalized === ":" ||
459
- normalized === "true" ||
460
- normalized === "printf ''" ||
461
- normalized === "printf \"\"");
462
- };
463
- const extractToolResultMeta = (payload) => {
464
- const payloadRec = asRecord(payload);
465
- const rawResult = payloadRec ? (payloadRec.result ?? null) : null;
466
- const resultRec = asRecord(rawResult) ?? parseJsonObject(rawResult);
467
- const nestedResult = resultRec ? asRecord(resultRec.result) : null;
468
- const output = asString((nestedResult ?? resultRec ?? {}).output) ??
469
- asString((nestedResult ?? resultRec ?? {}).result);
470
- const outputLines = output ? output.split(/\r?\n/).filter((line) => line.length > 0).length : null;
471
- const error = asString((nestedResult ?? resultRec ?? {}).error);
472
- return { outputLines, error };
473
- };
474
- return (event) => {
475
- switch (event.name) {
476
- case "agent.react.run.start":
477
- case "agent.deep.run.start":
478
- step = 0;
479
- lastCommandSignature = "";
480
- stepActionByNumber.clear();
481
- writer("Analyzing started");
482
- return;
483
- case "agent.react.skill.matched": {
484
- const payload = (event.payload ?? {});
485
- const score = typeof payload.score === "number" ? payload.score.toFixed(3) : "?";
486
- writer(`[skill] matched ${payload.skill ?? "unknown"} (score ${score})`);
487
- return;
488
- }
489
- case "agent.react.tool.invoke.start": {
490
- step += 1;
491
- const payload = (event.payload ?? {});
492
- const commandMeta = extractCommandMeta(payload.args);
493
- if (commandMeta) {
494
- const signature = `${commandMeta.command}|${commandMeta.windowId ?? ""}|${commandMeta.tabIndex ?? ""}|${commandMeta.sessionId ?? ""}`;
495
- const isRepeat = signature === lastCommandSignature;
496
- lastCommandSignature = signature;
497
- const action = isNoopCaptureCommand(commandMeta.command)
498
- ? "capture current screen"
499
- : commandMeta.command;
500
- stepActionByNumber.set(step, `${action}${isRepeat ? " (repeat)" : ""}`);
501
- }
502
- else {
503
- stepActionByNumber.set(step, `tool: ${event.to}`);
504
- }
505
- return;
506
- }
507
- case "agent.react.tool.invoke.done": {
508
- const resultMeta = extractToolResultMeta(event.payload);
509
- const action = stepActionByNumber.get(step) ?? `tool: ${event.to}`;
510
- if (resultMeta.error) {
511
- writer(`[step ${step}] ${action} ✖ (${resultMeta.error})`);
512
- stepActionByNumber.delete(step);
513
- return;
514
- }
515
- writer(`[step ${step}] ${action} ${okMark}`);
516
- stepActionByNumber.delete(step);
517
- return;
518
- }
519
- case "agent.react.tool.invoke.error": {
520
- const payload = (event.payload ?? {});
521
- const action = stepActionByNumber.get(step) ?? `tool: ${event.to}`;
522
- const message = typeof payload.error === "string"
523
- ? payload.error
524
- : payload.error instanceof Error
525
- ? payload.error.message
526
- : "unknown error";
527
- writer(`[step ${step}] ${action} ✖ (${message})`);
528
- stepActionByNumber.delete(step);
529
- return;
530
- }
531
- case "agent.react.run.done":
532
- case "agent.deep.run.done":
533
- writer("completed");
534
- return;
535
- default:
536
- return;
537
- }
538
- };
82
+ function resolveAgentKind(parsed, ctx) {
83
+ if (parsed.explicitKind)
84
+ return parsed.kind;
85
+ return readDefaultAgentKindFromConfig(ctx) ?? parsed.kind;
86
+ }
87
+ async function runSingleQuery(options, ctx, kind, query) {
88
+ const text = await runOne(ctx, kind, query);
89
+ const useColor = options.ui?.useColor ?? (Boolean(process.stdout.isTTY) && !process.env.NO_COLOR);
90
+ const renderMarkdown = options.ui?.renderMarkdown ?? true;
91
+ console.log(renderForTerminal(text, { renderMarkdown, useColor }));
539
92
  }
540
93
  export function runAppCli(options) {
541
- const { appName, createBotContext } = options;
542
- async function main() {
543
- const { kind, query } = parseArgs();
544
- const agentLabel = kind === REACT ? "ReAct (LangChain)" : "Deep (DeepAgents)";
545
- const loadingText = options.ui?.loadingText ?? `${appName}: loading config, LLM, memory, tools...`;
546
- const useLoadingSpinner = options.ui?.loadingSpinner ?? false;
547
- let stopLoadingSpinner = null;
548
- if (loadingText !== false) {
549
- if (useLoadingSpinner && Boolean(process.stderr.isTTY)) {
550
- stopLoadingSpinner = startLoadingSpinner(loadingText);
551
- }
552
- else {
553
- console.error(loadingText);
554
- }
555
- }
556
- const ctx = await createBotContext();
557
- if (stopLoadingSpinner) {
558
- stopLoadingSpinner();
559
- }
560
- let didCleanup = false;
561
- const cleanup = () => {
562
- if (didCleanup)
563
- return;
564
- didCleanup = true;
565
- try {
566
- options.onShutdown?.(ctx);
567
- }
568
- catch (err) {
569
- console.error(`${appName}: onShutdown hook failed:`, err instanceof Error ? err.message : err);
570
- }
571
- };
572
- const exitApp = (code) => {
573
- cleanup();
574
- process.exit(code);
575
- };
576
- process.once("exit", cleanup);
577
- process.once("SIGINT", () => exitApp(130));
578
- process.once("SIGTERM", () => exitApp(143));
579
- if (process.env.AGENT_EVENT_STDERR === "1") {
580
- ctx.events.subscribe(createConsoleAgentEventListener());
581
- }
582
- const eventListeners = Array.isArray(options.eventListener)
583
- ? options.eventListener
584
- : options.eventListener
585
- ? [options.eventListener]
586
- : [];
587
- for (const listener of eventListeners) {
588
- ctx.events.subscribe(listener);
589
- }
590
- if (options.onReady) {
591
- try {
592
- await options.onReady(ctx);
593
- }
594
- catch (err) {
595
- console.error(`${appName}: onReady hook failed:`, err instanceof Error ? err.message : err);
596
- }
597
- }
598
- const readyText = options.ui?.readyText ?? `Ready. Agent: ${agentLabel}`;
599
- if (readyText !== false) {
600
- console.error(readyText);
601
- }
602
- if (query) {
603
- const text = await runOne(ctx, kind, query);
604
- const useColor = options.ui?.useColor ?? (Boolean(process.stdout.isTTY) && !process.env.NO_COLOR);
605
- const renderMarkdown = options.ui?.renderMarkdown ?? true;
606
- console.log(renderForTerminal(text, { renderMarkdown, useColor }));
607
- cleanup();
608
- return;
94
+ const main = async () => {
95
+ const parsed = parseArgs();
96
+ const ctx = await createContextWithLoading(options);
97
+ const kind = resolveAgentKind(parsed, ctx);
98
+ const exitApp = createExitHandler(options.appName, options.onShutdown, ctx);
99
+ subscribeEventListeners(options, ctx);
100
+ await runOnReady(options, options.appName, ctx);
101
+ printReady(options, kind);
102
+ if (parsed.query) {
103
+ await runSingleQuery(options, ctx, kind, parsed.query);
104
+ exitApp(0);
609
105
  }
610
106
  await interactive(ctx, kind, options, exitApp);
611
- }
107
+ };
612
108
  main().catch((err) => {
613
109
  console.error(err);
614
110
  process.exit(1);