@goah/cli 0.13.0 → 0.13.2

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 (236) hide show
  1. package/dist/LICENSE +201 -0
  2. package/dist/THIRD-PARTY-NOTICES.md +2319 -0
  3. package/dist/anthropic-messages-WBFOHES5.js +7214 -0
  4. package/dist/azure-openai-responses-NZFH3PO6.js +253 -0
  5. package/dist/chunk-25XSBFX6.js +639 -0
  6. package/dist/chunk-2QBQZGXF.js +110 -0
  7. package/dist/chunk-2ZP2NBW3.js +345 -0
  8. package/dist/chunk-4KA63VNZ.js +2 -0
  9. package/dist/chunk-4M6TXTT5.js +86 -0
  10. package/dist/chunk-4US7ZHT3.js +20 -0
  11. package/dist/chunk-6M3OOCKO.js +11809 -0
  12. package/dist/chunk-7AXAFDPG.js +17 -0
  13. package/dist/chunk-7E7V6OIU.js +920 -0
  14. package/dist/chunk-BXA47DTF.js +548 -0
  15. package/dist/chunk-D2DVAH37.js +25 -0
  16. package/dist/chunk-FSBUS2BI.js +26 -0
  17. package/dist/chunk-GKDRAHLI.js +4787 -0
  18. package/dist/chunk-H462EFJP.js +89 -0
  19. package/dist/chunk-IIJQ3DW4.js +35 -0
  20. package/dist/chunk-JQD5EWJY.js +34 -0
  21. package/dist/chunk-K26BVR6O.js +61 -0
  22. package/dist/chunk-KAO7WIXG.js +31 -0
  23. package/dist/chunk-KJ4HJCTK.js +1178 -0
  24. package/dist/chunk-LBKI23CC.js +9810 -0
  25. package/dist/chunk-LKBKZEHT.js +41 -0
  26. package/dist/chunk-MD7TLTXZ.js +18 -0
  27. package/dist/chunk-MPXK7QBL.js +868 -0
  28. package/dist/chunk-NRLGWZJC.js +11 -0
  29. package/dist/chunk-QBD6EJ2Z.js +1746 -0
  30. package/dist/chunk-R6I3VKME.js +132 -0
  31. package/dist/chunk-TJON22J4.js +87 -0
  32. package/dist/chunk-ULY36ME4.js +33097 -0
  33. package/dist/chunk-XALZLRYK.js +9221 -0
  34. package/dist/chunk-XPQKM3L7.js +57 -0
  35. package/dist/chunk-YH6YJ2IB.js +1773 -0
  36. package/dist/cli.d.ts +0 -2
  37. package/dist/cli.js +13716 -580
  38. package/dist/console/assets/{index-Rtc_B7cs.js → index-B1vGMXKr.js} +11 -11
  39. package/dist/console/assets/{index-Rtc_B7cs.js.map → index-B1vGMXKr.js.map} +1 -1
  40. package/dist/console/index.html +1 -1
  41. package/dist/dist-65MDQA4Q.js +1126 -0
  42. package/dist/dist-KNR2XL6J.js +39 -0
  43. package/dist/execution.d.ts +557 -2
  44. package/dist/execution.js +37 -2
  45. package/dist/faux-runner-worker.js +128 -0
  46. package/dist/google-generative-ai-YMHXZSFE.js +422 -0
  47. package/dist/google-vertex-X6DB3ZQ6.js +471 -0
  48. package/dist/index.d.ts +853 -25
  49. package/dist/index.js +84 -385
  50. package/dist/kernel.d.ts +39 -2
  51. package/dist/kernel.js +21 -2
  52. package/dist/mistral-conversations-TY3MMD6G.js +718 -0
  53. package/dist/multipart-parser-F6M4NJZU.js +373 -0
  54. package/dist/openai-codex-responses-BRDUMLN5.js +1374 -0
  55. package/dist/openai-completions-CX634XN3.js +1139 -0
  56. package/dist/openai-responses-HMCXJIHT.js +300 -0
  57. package/dist/openrouter-images-KTWET2H2.js +148 -0
  58. package/dist/pi-messages-XL5OEC4K.js +307 -0
  59. package/dist/pi-worker.js +671 -0
  60. package/dist/runner-pi.d.ts +342 -2
  61. package/dist/runner-pi.js +46 -2
  62. package/dist/runner-registry-GD4BYCQC.js +22 -0
  63. package/dist/sqlite.d.ts +446 -2
  64. package/dist/sqlite.js +15 -2
  65. package/dist/src-F2KBSV4T.js +1398 -0
  66. package/dist/supervisor.d.ts +625 -2
  67. package/dist/supervisor.js +39 -2
  68. package/dist/testkit.d.ts +564 -2
  69. package/dist/testkit.js +127 -2
  70. package/dist/transcript.d.ts +84 -2
  71. package/dist/transcript.js +25 -2
  72. package/dist/verification-worker.js +72 -0
  73. package/package.json +2 -26
  74. package/dist/cli.d.ts.map +0 -1
  75. package/dist/cli.js.map +0 -1
  76. package/dist/control.d.ts +0 -106
  77. package/dist/control.d.ts.map +0 -1
  78. package/dist/control.js +0 -242
  79. package/dist/control.js.map +0 -1
  80. package/dist/execution.d.ts.map +0 -1
  81. package/dist/execution.js.map +0 -1
  82. package/dist/index.d.ts.map +0 -1
  83. package/dist/index.js.map +0 -1
  84. package/dist/kernel.d.ts.map +0 -1
  85. package/dist/kernel.js.map +0 -1
  86. package/dist/live-config.d.ts +0 -10
  87. package/dist/live-config.d.ts.map +0 -1
  88. package/dist/live-config.js +0 -44
  89. package/dist/live-config.js.map +0 -1
  90. package/dist/prompt-queue.d.ts +0 -13
  91. package/dist/prompt-queue.d.ts.map +0 -1
  92. package/dist/prompt-queue.js +0 -35
  93. package/dist/prompt-queue.js.map +0 -1
  94. package/dist/runner-pi.d.ts.map +0 -1
  95. package/dist/runner-pi.js.map +0 -1
  96. package/dist/runner-registry.d.ts +0 -8
  97. package/dist/runner-registry.d.ts.map +0 -1
  98. package/dist/runner-registry.js +0 -12
  99. package/dist/runner-registry.js.map +0 -1
  100. package/dist/searchable-select.d.ts +0 -24
  101. package/dist/searchable-select.d.ts.map +0 -1
  102. package/dist/searchable-select.js +0 -61
  103. package/dist/searchable-select.js.map +0 -1
  104. package/dist/secret-input.d.ts +0 -8
  105. package/dist/secret-input.d.ts.map +0 -1
  106. package/dist/secret-input.js +0 -14
  107. package/dist/secret-input.js.map +0 -1
  108. package/dist/setup-wizard.d.ts +0 -22
  109. package/dist/setup-wizard.d.ts.map +0 -1
  110. package/dist/setup-wizard.js +0 -154
  111. package/dist/setup-wizard.js.map +0 -1
  112. package/dist/sqlite.d.ts.map +0 -1
  113. package/dist/sqlite.js.map +0 -1
  114. package/dist/supervisor.d.ts.map +0 -1
  115. package/dist/supervisor.js.map +0 -1
  116. package/dist/testkit.d.ts.map +0 -1
  117. package/dist/testkit.js.map +0 -1
  118. package/dist/thread-inspect.d.ts +0 -47
  119. package/dist/thread-inspect.d.ts.map +0 -1
  120. package/dist/thread-inspect.js +0 -78
  121. package/dist/thread-inspect.js.map +0 -1
  122. package/dist/transcript.d.ts.map +0 -1
  123. package/dist/transcript.js.map +0 -1
  124. package/dist/tui-theme.d.ts +0 -17
  125. package/dist/tui-theme.d.ts.map +0 -1
  126. package/dist/tui-theme.js +0 -19
  127. package/dist/tui-theme.js.map +0 -1
  128. package/dist/tui.d.ts +0 -24
  129. package/dist/tui.d.ts.map +0 -1
  130. package/dist/tui.js +0 -784
  131. package/dist/tui.js.map +0 -1
  132. package/dist/update.d.ts +0 -16
  133. package/dist/update.d.ts.map +0 -1
  134. package/dist/update.js +0 -67
  135. package/dist/update.js.map +0 -1
  136. package/dist/web-console.d.ts +0 -33
  137. package/dist/web-console.d.ts.map +0 -1
  138. package/dist/web-console.js +0 -242
  139. package/dist/web-console.js.map +0 -1
  140. package/dist/welcome.d.ts +0 -30
  141. package/dist/welcome.d.ts.map +0 -1
  142. package/dist/welcome.js +0 -69
  143. package/dist/welcome.js.map +0 -1
  144. package/node_modules/goah-ledger-contract/dist/execution.d.ts +0 -544
  145. package/node_modules/goah-ledger-contract/dist/execution.d.ts.map +0 -1
  146. package/node_modules/goah-ledger-contract/dist/execution.js +0 -52
  147. package/node_modules/goah-ledger-contract/dist/execution.js.map +0 -1
  148. package/node_modules/goah-ledger-contract/dist/index.d.ts +0 -4
  149. package/node_modules/goah-ledger-contract/dist/index.d.ts.map +0 -1
  150. package/node_modules/goah-ledger-contract/dist/index.js +0 -4
  151. package/node_modules/goah-ledger-contract/dist/index.js.map +0 -1
  152. package/node_modules/goah-ledger-contract/dist/kernel.d.ts +0 -37
  153. package/node_modules/goah-ledger-contract/dist/kernel.d.ts.map +0 -1
  154. package/node_modules/goah-ledger-contract/dist/kernel.js +0 -8
  155. package/node_modules/goah-ledger-contract/dist/kernel.js.map +0 -1
  156. package/node_modules/goah-ledger-contract/dist/transcript.d.ts +0 -63
  157. package/node_modules/goah-ledger-contract/dist/transcript.d.ts.map +0 -1
  158. package/node_modules/goah-ledger-contract/dist/transcript.js +0 -115
  159. package/node_modules/goah-ledger-contract/dist/transcript.js.map +0 -1
  160. package/node_modules/goah-ledger-contract/package.json +0 -28
  161. package/node_modules/goah-ledger-sqlite/dist/index.d.ts +0 -86
  162. package/node_modules/goah-ledger-sqlite/dist/index.d.ts.map +0 -1
  163. package/node_modules/goah-ledger-sqlite/dist/index.js +0 -1254
  164. package/node_modules/goah-ledger-sqlite/dist/index.js.map +0 -1
  165. package/node_modules/goah-ledger-sqlite/package.json +0 -26
  166. package/node_modules/goah-runner-pi/dist/configurator.d.ts +0 -20
  167. package/node_modules/goah-runner-pi/dist/configurator.d.ts.map +0 -1
  168. package/node_modules/goah-runner-pi/dist/configurator.js +0 -423
  169. package/node_modules/goah-runner-pi/dist/configurator.js.map +0 -1
  170. package/node_modules/goah-runner-pi/dist/credential-store.d.ts +0 -14
  171. package/node_modules/goah-runner-pi/dist/credential-store.d.ts.map +0 -1
  172. package/node_modules/goah-runner-pi/dist/credential-store.js +0 -163
  173. package/node_modules/goah-runner-pi/dist/credential-store.js.map +0 -1
  174. package/node_modules/goah-runner-pi/dist/env-spec.d.ts +0 -17
  175. package/node_modules/goah-runner-pi/dist/env-spec.d.ts.map +0 -1
  176. package/node_modules/goah-runner-pi/dist/env-spec.js +0 -95
  177. package/node_modules/goah-runner-pi/dist/env-spec.js.map +0 -1
  178. package/node_modules/goah-runner-pi/dist/index.d.ts +0 -76
  179. package/node_modules/goah-runner-pi/dist/index.d.ts.map +0 -1
  180. package/node_modules/goah-runner-pi/dist/index.js +0 -326
  181. package/node_modules/goah-runner-pi/dist/index.js.map +0 -1
  182. package/node_modules/goah-runner-pi/dist/model-provider.d.ts +0 -30
  183. package/node_modules/goah-runner-pi/dist/model-provider.d.ts.map +0 -1
  184. package/node_modules/goah-runner-pi/dist/model-provider.js +0 -96
  185. package/node_modules/goah-runner-pi/dist/model-provider.js.map +0 -1
  186. package/node_modules/goah-runner-pi/dist/pi-worker.d.ts +0 -31
  187. package/node_modules/goah-runner-pi/dist/pi-worker.d.ts.map +0 -1
  188. package/node_modules/goah-runner-pi/dist/pi-worker.js +0 -579
  189. package/node_modules/goah-runner-pi/dist/pi-worker.js.map +0 -1
  190. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts +0 -2
  191. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts.map +0 -1
  192. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js +0 -3
  193. package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js.map +0 -1
  194. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts +0 -2
  195. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts.map +0 -1
  196. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js +0 -5
  197. package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js.map +0 -1
  198. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts +0 -2
  199. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts.map +0 -1
  200. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js +0 -5
  201. package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js.map +0 -1
  202. package/node_modules/goah-runner-pi/dist/test-helpers.d.ts +0 -4
  203. package/node_modules/goah-runner-pi/dist/test-helpers.d.ts.map +0 -1
  204. package/node_modules/goah-runner-pi/dist/test-helpers.js +0 -13
  205. package/node_modules/goah-runner-pi/dist/test-helpers.js.map +0 -1
  206. package/node_modules/goah-runner-pi/dist/verification-worker.d.ts +0 -2
  207. package/node_modules/goah-runner-pi/dist/verification-worker.d.ts.map +0 -1
  208. package/node_modules/goah-runner-pi/dist/verification-worker.js +0 -46
  209. package/node_modules/goah-runner-pi/dist/verification-worker.js.map +0 -1
  210. package/node_modules/goah-runner-pi/package.json +0 -28
  211. package/node_modules/goah-supervisor/dist/context-view.d.ts +0 -30
  212. package/node_modules/goah-supervisor/dist/context-view.d.ts.map +0 -1
  213. package/node_modules/goah-supervisor/dist/context-view.js +0 -58
  214. package/node_modules/goah-supervisor/dist/context-view.js.map +0 -1
  215. package/node_modules/goah-supervisor/dist/index.d.ts +0 -96
  216. package/node_modules/goah-supervisor/dist/index.d.ts.map +0 -1
  217. package/node_modules/goah-supervisor/dist/index.js +0 -1075
  218. package/node_modules/goah-supervisor/dist/index.js.map +0 -1
  219. package/node_modules/goah-supervisor/dist/roles.d.ts +0 -4
  220. package/node_modules/goah-supervisor/dist/roles.d.ts.map +0 -1
  221. package/node_modules/goah-supervisor/dist/roles.js +0 -29
  222. package/node_modules/goah-supervisor/dist/roles.js.map +0 -1
  223. package/node_modules/goah-supervisor/dist/verification.d.ts +0 -61
  224. package/node_modules/goah-supervisor/dist/verification.d.ts.map +0 -1
  225. package/node_modules/goah-supervisor/dist/verification.js +0 -135
  226. package/node_modules/goah-supervisor/dist/verification.js.map +0 -1
  227. package/node_modules/goah-supervisor/package.json +0 -26
  228. package/node_modules/goah-testkit/dist/faux-runner-worker.d.ts +0 -2
  229. package/node_modules/goah-testkit/dist/faux-runner-worker.d.ts.map +0 -1
  230. package/node_modules/goah-testkit/dist/faux-runner-worker.js +0 -82
  231. package/node_modules/goah-testkit/dist/faux-runner-worker.js.map +0 -1
  232. package/node_modules/goah-testkit/dist/index.d.ts +0 -49
  233. package/node_modules/goah-testkit/dist/index.d.ts.map +0 -1
  234. package/node_modules/goah-testkit/dist/index.js +0 -96
  235. package/node_modules/goah-testkit/dist/index.js.map +0 -1
  236. package/node_modules/goah-testkit/package.json +0 -29
package/dist/tui.js DELETED
@@ -1,784 +0,0 @@
1
- /** Full-screen goah TUI: streaming CEO transcript over the resident Supervisor control socket. */
2
- import { TuiAltScreen, Text, Markdown, Editor, ProcessTerminal, ScrollView, VStack } from "@earendil-works/pi-tui";
3
- import { controlAvailable, requestControl, streamControl } from "./control.js";
4
- import { loadConfig, readConsoleMetadata, readDefaultRunnerProfile } from "./index.js";
5
- import { switchModel, reloadDaemon, readRunnerDisplay } from "./live-config.js";
6
- import { welcomeSnapshot, renderWelcome } from "./welcome.js";
7
- import { chooseSetupSection, runRunnerCommandWizard, runSetupWizard, applyWizardResult } from "./setup-wizard.js";
8
- import { installedVersion } from "./update.js";
9
- import { tuiTheme } from "./tui-theme.js";
10
- import { spawn } from "node:child_process";
11
- import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
12
- import { join, resolve } from "node:path";
13
- class HeaderBar {
14
- runner;
15
- target;
16
- version;
17
- constructor(runner, target, version) {
18
- this.runner = runner;
19
- this.target = target;
20
- this.version = version;
21
- }
22
- render(width) { return [renderTuiHeader(width, this.runner, this.target, this.version)]; }
23
- invalidate() { }
24
- }
25
- class GoalBar {
26
- root;
27
- constructor(root) {
28
- this.root = root;
29
- }
30
- setRoot(root) { this.root = root; }
31
- render(width) {
32
- const line = this.root ? `${tuiTheme.active(" GOAL ")} ${this.root.objective} ${tuiTheme.muted(this.root.phase)}` : `${tuiTheme.muted("no active Goal")} ${tuiTheme.accent("/goal")}`;
33
- return new Text(line, 1, 0).render(width);
34
- }
35
- invalidate() { }
36
- }
37
- const markdownTheme = {
38
- heading: tuiTheme.accent, link: tuiTheme.accent, linkUrl: tuiTheme.muted, code: tuiTheme.warning,
39
- codeBlock: tuiTheme.subtle, codeBlockBorder: tuiTheme.muted, quote: tuiTheme.subtle, quoteBorder: tuiTheme.accent,
40
- hr: tuiTheme.muted, listBullet: tuiTheme.accent, bold: tuiTheme.strong, italic: tuiTheme.muted,
41
- strikethrough: tuiTheme.muted, underline: tuiTheme.underline, codeBlockIndent: " ",
42
- };
43
- class ConversationView {
44
- entries;
45
- liveMarkdown = "";
46
- liveThinking = "";
47
- thinkingActive = false;
48
- constructor(initial) { this.entries = [{ kind: "text", content: initial.join("\n") }]; }
49
- addText(content) { this.entries.push({ kind: "text", content }); this.trim(); }
50
- addUser(content) { this.entries.push({ kind: "user", content }); this.trim(); }
51
- addMarkdown(content) {
52
- this.liveMarkdown = "";
53
- content = content.trim();
54
- if (!content)
55
- return;
56
- const previous = this.entries.at(-1);
57
- if (previous?.kind === "markdown" && previous.content === content)
58
- return;
59
- this.entries.push({ kind: "markdown", content });
60
- this.trim();
61
- }
62
- setLiveMarkdown(content) { this.liveMarkdown = content; }
63
- clearLiveMarkdown() { this.liveMarkdown = ""; }
64
- updateThinking(activity) {
65
- if (activity.phase === "start") {
66
- this.liveThinking = "";
67
- this.thinkingActive = true;
68
- return;
69
- }
70
- if (activity.phase === "delta") {
71
- this.liveThinking += activity.text;
72
- this.thinkingActive = true;
73
- return;
74
- }
75
- if (activity.phase === "clear") {
76
- this.liveThinking = "";
77
- this.thinkingActive = false;
78
- return;
79
- }
80
- const content = (activity.text || this.liveThinking).trim();
81
- this.liveThinking = "";
82
- this.thinkingActive = false;
83
- const previous = this.entries.at(-1);
84
- if (content && !(previous?.kind === "thinking" && previous.content === content))
85
- this.entries.push({ kind: "thinking", content });
86
- this.trim();
87
- }
88
- updateTool(activity) {
89
- const previous = this.entries.findLast((entry) => entry.kind === "tool" && entry.callId === activity.callId);
90
- if (previous?.kind === "tool")
91
- Object.assign(previous, { ...activity, detail: activity.detail || previous.detail });
92
- else
93
- this.entries.push(activity);
94
- this.trim();
95
- }
96
- trim() { if (this.entries.length > 240)
97
- this.entries.splice(1, this.entries.length - 240); }
98
- render(width) {
99
- const rendered = this.entries.flatMap((entry) => entry.kind === "markdown"
100
- ? [...new Markdown(entry.content, 2, 0, markdownTheme).render(width), ""]
101
- : entry.kind === "user"
102
- ? renderUserMessage(entry.content, width)
103
- : entry.kind === "thinking"
104
- ? [tuiTheme.muted(" thinking"), ...new Markdown(entry.content, 2, 0, markdownTheme, { color: tuiTheme.muted, italic: true }).render(width), ""]
105
- : entry.kind === "tool"
106
- ? new Text(toolActivityLine(entry), 2, 0).render(width)
107
- : new Text(entry.content, 2, 0).render(width));
108
- if (this.liveThinking)
109
- rendered.push(tuiTheme.muted(" thinking"), ...new Markdown(this.liveThinking, 2, 0, markdownTheme, { color: tuiTheme.muted, italic: true }).render(width));
110
- else if (this.thinkingActive)
111
- rendered.push(tuiTheme.muted(" thinking…"));
112
- if (this.liveMarkdown)
113
- rendered.push(...new Markdown(this.liveMarkdown, 2, 0, markdownTheme).render(width));
114
- return rendered;
115
- }
116
- invalidate() { }
117
- }
118
- export function renderUserMessage(content, width) { return new Text(content, 2, 1, tuiTheme.userMessage).render(width); }
119
- export function renderTuiHeader(width, runner, target, version) {
120
- const brand = " GOAH ";
121
- const release = width >= 34 ? ` v${version} ` : "";
122
- const rawContext = ` ${runner || "runner"} · ${target || "unconfigured"}`;
123
- const contextWidth = Math.max(1, width - brand.length - release.length - 1);
124
- const context = rawContext.length > contextWidth ? `${rawContext.slice(0, Math.max(1, contextWidth - 1))}…` : rawContext;
125
- const fill = " ".repeat(Math.max(0, width - brand.length - context.length - release.length));
126
- return `${tuiTheme.brand(brand)}${tuiTheme.rail(`${context}${fill}${release}`)}`;
127
- }
128
- function statusText(mode, queued = 0) {
129
- if (mode === "working")
130
- return `${tuiTheme.accent("working")} ${tuiTheme.muted(queued ? `${queued} queued · /stop cancels` : "/stop cancels")}`;
131
- if (mode === "queued")
132
- return `${tuiTheme.warning(`${queued} queued`)} ${tuiTheme.muted("continuing in order")}`;
133
- if (mode === "setup")
134
- return tuiTheme.accent("opening setup…");
135
- return `${tuiTheme.muted("ready")} ${tuiTheme.accent("/help")}`;
136
- }
137
- export function classifyTuiInput(value, busy) {
138
- const text = value.trim();
139
- if (!text)
140
- return { action: "empty", text };
141
- if (text === "/quit" || text === "/exit")
142
- return { action: "quit", text };
143
- if (text === "/help")
144
- return { action: "help", text };
145
- if (text === "/status")
146
- return { action: "status", text };
147
- if (text === "/records" || text.startsWith("/records ") || text.startsWith("/history "))
148
- return { action: "records", text };
149
- if (text === "/stop")
150
- return { action: "stop", text };
151
- if (text === "/model" || text.startsWith("/model "))
152
- return { action: "model", text };
153
- if (text === "/login" || text.startsWith("/login "))
154
- return { action: "login", text };
155
- if (text === "/logout" || text.startsWith("/logout "))
156
- return { action: "logout", text };
157
- if (text === "/setup" || text.startsWith("/setup "))
158
- return { action: "setup", text };
159
- if (text.startsWith("/goal ") || text.startsWith("/observe "))
160
- return { action: "goal", text };
161
- if (text.startsWith("/"))
162
- return { action: "unknown", text };
163
- return { action: busy ? "steer" : "send", text };
164
- }
165
- export async function runGoahTui(configPath, stateDir, initialMessage) {
166
- if (!process.stdout.isTTY || !process.stdin.isTTY)
167
- return runNonInteractive(configPath, stateDir, initialMessage);
168
- const runner = readRunnerDisplay(configPath);
169
- const snapshot = welcomeSnapshot(stateDir, runner);
170
- const welcome = renderWelcome(snapshot, Boolean(snapshot.root || snapshot.handoffs.length || snapshot.conversation.length));
171
- await ensureDaemon(configPath, stateDir);
172
- const liveSnapshot = await requestControl(stateDir, { op: "status" }).catch(() => null);
173
- const liveTurns = liveSnapshot && typeof liveSnapshot === "object" && !Array.isArray(liveSnapshot) && Array.isArray(liveSnapshot.turns) ? liveSnapshot.turns : [];
174
- const liveInteractionTurnId = findLiveTurnId(liveTurns);
175
- const terminal = new ProcessTerminal();
176
- const tui = new TuiAltScreen(terminal, true, undefined, { mouse: true });
177
- terminal.setTitle(`Goah · ${runner.target}`);
178
- const headerView = new HeaderBar(runner.runner, runner.target, installedVersion());
179
- const transcriptView = new ConversationView(welcome);
180
- for (const row of snapshot.conversation)
181
- if (row.speaker === "You")
182
- transcriptView.addUser(row.text);
183
- else
184
- transcriptView.addMarkdown(row.text);
185
- const conversationScroll = new ScrollView(transcriptView, { follow: "end", primary: true, scrollbar: "auto", scrollbarStyle: tuiTheme.muted });
186
- const goalView = new GoalBar(snapshot.root);
187
- const input = new Editor(tui, {
188
- borderColor: tuiTheme.accent,
189
- selectList: { selectedPrefix: tuiTheme.accent, selectedText: tuiTheme.strong, description: tuiTheme.muted, scrollInfo: tuiTheme.muted, noMatch: tuiTheme.error },
190
- }, { paddingX: 1, autocompleteMaxVisible: 6 });
191
- const statusView = new Text(statusText("ready"), 1, 0);
192
- const shell = new VStack([
193
- { component: headerView, basis: 1, shrink: 0 },
194
- { component: conversationScroll, grow: 1, minSize: 1 },
195
- { component: goalView, basis: 1, shrink: 0 },
196
- { component: statusView, basis: 1, shrink: 0 },
197
- { component: input, basis: "auto", minSize: 3, maxSize: 8, shrink: 0 },
198
- ]);
199
- const busy = { active: false };
200
- const queued = [];
201
- const queuedTurnIds = [];
202
- let activeStream = null;
203
- let activeInteractionTurnId = null;
204
- let steeringTail = Promise.resolve();
205
- let configuring = false;
206
- let exiting = false;
207
- const push = (line) => {
208
- transcriptView.addText(line);
209
- tui.requestRender();
210
- };
211
- let liveText = "";
212
- const appendLive = (text) => { liveText += text; transcriptView.setLiveMarkdown(liveText); tui.requestRender(); };
213
- const commitLive = (text) => { liveText = ""; transcriptView.clearLiveMarkdown(); if (text)
214
- transcriptView.addMarkdown(text); tui.requestRender(); };
215
- const pushResponse = (text) => { transcriptView.addMarkdown(text); tui.requestRender(); };
216
- const updateTool = (activity) => { transcriptView.updateTool(activity); tui.requestRender(); };
217
- const updateThinking = (activity) => { transcriptView.updateThinking(activity); tui.requestRender(); };
218
- const setWakeState = (mode) => { statusView.setText(statusText(mode, mode === "queued" ? 1 : queued.length)); tui.requestRender(); };
219
- const send = async (message, showUser = true) => {
220
- busy.active = true;
221
- const controller = new AbortController();
222
- activeStream = controller;
223
- statusView.setText(statusText("working", queued.length));
224
- if (showUser) {
225
- transcriptView.addUser(message);
226
- tui.requestRender();
227
- }
228
- try {
229
- await streamControl(stateDir, { op: "interact", message }, (frame) => { if (frame.type === "accepted")
230
- activeInteractionTurnId = frame.turnId; if (frame.type === "result" || frame.type === "error")
231
- activeInteractionTurnId = null; renderFrame(frame, push, appendLive, commitLive, pushResponse, updateTool, updateThinking, setWakeState, () => commitLive("")); }, controller.signal);
232
- }
233
- catch (error) {
234
- transcriptView.clearLiveMarkdown();
235
- transcriptView.updateThinking({ phase: "clear", text: "" });
236
- if (!controller.signal.aborted)
237
- push(errorLine(error));
238
- }
239
- finally {
240
- if (activeStream === controller)
241
- activeStream = null;
242
- busy.active = false;
243
- await refreshGoalBar(stateDir, goalView, tui);
244
- statusView.setText(statusText(queuedTurnIds.length || queued.length ? "queued" : "ready", queuedTurnIds.length + queued.length));
245
- continuePending();
246
- }
247
- };
248
- const attachTurn = async (turnId) => {
249
- busy.active = true;
250
- activeInteractionTurnId = turnId;
251
- const controller = new AbortController();
252
- activeStream = controller;
253
- statusView.setText(statusText("queued", 1));
254
- try {
255
- await streamControl(stateDir, { op: "turn.attach", turnId }, (frame) => { if (frame.type === "result" || frame.type === "error")
256
- activeInteractionTurnId = null; renderFrame(frame, push, appendLive, commitLive, pushResponse, updateTool, updateThinking, setWakeState, () => commitLive("")); }, controller.signal);
257
- }
258
- catch (error) {
259
- if (!controller.signal.aborted)
260
- push(errorLine(error));
261
- }
262
- finally {
263
- if (activeStream === controller)
264
- activeStream = null;
265
- busy.active = false;
266
- await refreshGoalBar(stateDir, goalView, tui);
267
- statusView.setText(statusText("ready"));
268
- continuePending();
269
- }
270
- };
271
- const continuePending = () => {
272
- if (exiting || configuring || busy.active)
273
- return;
274
- const turnId = queuedTurnIds.shift();
275
- if (turnId) {
276
- void attachTurn(turnId);
277
- return;
278
- }
279
- const next = queued.shift();
280
- if (next)
281
- void send(next, false);
282
- };
283
- const steer = async (message) => {
284
- try {
285
- const outcome = await requestControl(stateDir, { op: "turn.steer", message });
286
- const value = outcome && typeof outcome === "object" && !Array.isArray(outcome) ? outcome : {};
287
- const queuedBySupervisor = value.steered === false && typeof value.turnId === "string";
288
- if (queuedBySupervisor) {
289
- queuedTurnIds.push(String(value.turnId));
290
- activeStream?.abort();
291
- }
292
- statusView.setText(queuedBySupervisor ? `${tuiTheme.warning("new Turn")} ${tuiTheme.muted("Runner stopped accepting steering; continuing in a fresh Turn")}` : `${tuiTheme.accent("working")} ${tuiTheme.muted("your update is steering this turn · /stop cancels")}`);
293
- tui.requestRender();
294
- if (queuedBySupervisor && !busy.active)
295
- continuePending();
296
- }
297
- catch (error) {
298
- const reason = error instanceof Error ? error.message : String(error);
299
- if (reason.includes("no longer accepting steering messages")) {
300
- queued.push(message);
301
- statusView.setText(statusText("working", queued.length));
302
- if (!busy.active)
303
- continuePending();
304
- }
305
- else
306
- push(errorLine(error));
307
- }
308
- };
309
- const submitSteer = (message) => {
310
- transcriptView.addUser(message);
311
- tui.requestRender();
312
- steeringTail = steeringTail.then(() => exiting ? undefined : steer(message));
313
- };
314
- const withConfigurationScreen = async (work) => {
315
- if (busy.active) {
316
- const snapshot = await requestControl(stateDir, { op: "status" }).catch(() => null);
317
- if (!snapshot) {
318
- push("Cannot inspect the current turn; use /stop before changing Runner configuration.");
319
- return;
320
- }
321
- const turns = snapshot && typeof snapshot === "object" && !Array.isArray(snapshot) && Array.isArray(snapshot.turns) ? snapshot.turns : [];
322
- if (turns.some((turn) => turn.status === "in_progress")) {
323
- push("Wait for the current Turn or use /stop before changing Runner configuration.");
324
- return;
325
- }
326
- }
327
- else
328
- configuring = true;
329
- statusView.setText(statusText("setup"));
330
- tui.stop();
331
- try {
332
- await work();
333
- }
334
- catch (error) {
335
- push(errorLine(error));
336
- }
337
- finally {
338
- configuring = false;
339
- if (!exiting) {
340
- tui.start();
341
- tui.requestRender(true);
342
- statusView.setText(statusText("ready"));
343
- continuePending();
344
- }
345
- }
346
- };
347
- const launchRunnerCommand = async (command, commandArgs = []) => withConfigurationScreen(async () => {
348
- const current = configuredRunnerProfile(configPath);
349
- if (!current)
350
- throw new Error("No Runner Profile is configured; use /setup first.");
351
- const result = await runRunnerCommandWizard(current, command, commandArgs);
352
- try {
353
- await applyWizardResult({ profile: result.profile }, existsSync(configPath) ? configPath : null);
354
- }
355
- catch (error) {
356
- await result.rollback?.();
357
- throw error;
358
- }
359
- for (const line of result.output)
360
- push(line);
361
- push(await reloadDaemon(stateDir, configPath) ? "Configuration updated — applies to the next Turn." : "Configuration saved — restart Goah to apply it.");
362
- });
363
- const launchSetup = async (text) => withConfigurationScreen(async () => {
364
- const current = configuredRunnerProfile(configPath);
365
- const requested = text.slice("/setup".length).trim();
366
- if (requested && !["runner", "model", "auth"].includes(requested))
367
- throw new Error("usage: /setup [runner|model|auth]");
368
- const section = requested || (current ? await chooseSetupSection(current) : "runner");
369
- if (!section)
370
- return;
371
- if (section === "runner" || !current) {
372
- const result = await runSetupWizard(current);
373
- if (!result.profile)
374
- return;
375
- await applyWizardResult(result, existsSync(configPath) ? configPath : null);
376
- push(await reloadDaemon(stateDir, configPath) ? "Runner profile updated — applies to the next Turn." : "Runner profile saved — restart Goah to apply it.");
377
- return;
378
- }
379
- const result = await runRunnerCommandWizard(current, section === "model" ? "model" : "auth");
380
- try {
381
- await applyWizardResult({ profile: result.profile }, existsSync(configPath) ? configPath : null);
382
- }
383
- catch (error) {
384
- await result.rollback?.();
385
- throw error;
386
- }
387
- for (const line of result.output)
388
- push(line);
389
- push(await reloadDaemon(stateDir, configPath) ? "Configuration updated — applies to the next Turn." : "Configuration saved — restart Goah to apply it.");
390
- });
391
- input.onSubmit = (line) => {
392
- const { action, text } = classifyTuiInput(line, busy.active);
393
- input.setText("");
394
- if (action === "quit") {
395
- exiting = true;
396
- activeStream?.abort();
397
- tui.stop();
398
- resolveExit();
399
- return;
400
- }
401
- if (action === "help") {
402
- push(`${tuiTheme.strong("Commands")}\n /model /login /logout /setup /status\n /records /history /goal /observe /stop /quit\n`);
403
- return;
404
- }
405
- if (action === "status") {
406
- void printStatus(stateDir, push).finally(() => refreshGoalBar(stateDir, goalView, tui));
407
- return;
408
- }
409
- if (action === "records") {
410
- void printRecords(text, stateDir, push);
411
- return;
412
- }
413
- if (action === "stop") {
414
- void stopCeoWake(stateDir, push);
415
- return;
416
- }
417
- if (action === "model") {
418
- if (text === "/model")
419
- void launchRunnerCommand("model");
420
- else
421
- void switchModelCommand(text, configPath, stateDir, push);
422
- return;
423
- }
424
- if (action === "login") {
425
- void launchRunnerCommand("auth", ["login", text.slice("/login".length).trim()].filter(Boolean));
426
- return;
427
- }
428
- if (action === "logout") {
429
- void launchRunnerCommand("auth", ["logout", text.slice("/logout".length).trim()].filter(Boolean));
430
- return;
431
- }
432
- if (action === "setup") {
433
- void launchSetup(text);
434
- return;
435
- }
436
- if (action === "goal") {
437
- void slashGoal(text, stateDir, push).finally(() => refreshGoalBar(stateDir, goalView, tui));
438
- return;
439
- }
440
- if (action === "unknown") {
441
- push(errorLine(`Unknown command: ${text.split(/\s+/, 1)[0]}. Use /help to list commands.`));
442
- return;
443
- }
444
- if (action === "steer") {
445
- submitSteer(text);
446
- return;
447
- }
448
- if (action === "send")
449
- void send(text);
450
- };
451
- tui.setLayoutRoot(shell);
452
- tui.setFocus(input);
453
- const { promise: exited, resolve: resolveExit } = Promise.withResolvers();
454
- tui.addInputListener((data) => {
455
- if (data !== "\x03")
456
- return undefined;
457
- exiting = true;
458
- push(busy.active ? "Detached — the current Turn continues in the daemon. Use /stop before detaching when you intend to cancel it." : "Detached.");
459
- activeStream?.abort();
460
- tui.stop();
461
- resolveExit();
462
- return { consume: true };
463
- });
464
- tui.start();
465
- if (initialMessage)
466
- void send(initialMessage);
467
- else if (typeof liveInteractionTurnId === "string")
468
- void attachTurn(liveInteractionTurnId);
469
- await exited;
470
- }
471
- export function findLiveTurnId(turns) {
472
- const id = [...turns].reverse().find((turn) => turn.status === "in_progress" && turn.source === "human")?.id;
473
- return typeof id === "string" ? id : null;
474
- }
475
- async function runNonInteractive(configPath, stateDir, initialMessage) {
476
- await ensureDaemon(configPath, stateDir);
477
- console.log(`Console: ${(await waitForConsoleMetadata(stateDir)).url}`);
478
- if (!initialMessage)
479
- return;
480
- await streamControl(stateDir, { op: "interact", message: initialMessage }, (frame) => {
481
- const line = frameToLine(frame);
482
- if (line)
483
- console.log(line);
484
- });
485
- }
486
- function waitForConsoleMetadata(stateDir) {
487
- const deadline = Date.now() + 10_000;
488
- const { promise, resolve, reject } = Promise.withResolvers();
489
- const poll = () => {
490
- try {
491
- const metadata = readConsoleMetadata(stateDir);
492
- if (metadata)
493
- resolve({ url: metadata.url });
494
- else if (Date.now() > deadline)
495
- reject(new Error("Goah Console did not start; restart the resident Supervisor"));
496
- else
497
- setTimeout(poll, 100);
498
- }
499
- catch (error) {
500
- reject(error instanceof Error ? error : new Error(String(error)));
501
- }
502
- };
503
- poll();
504
- return promise;
505
- }
506
- /** One frame to one printable line (shared with the TUI transcript renderer). */
507
- function frameToLine(frame) {
508
- const lines = [];
509
- renderFrame(frame, (line) => lines.push(line));
510
- return lines.length ? lines.join("\n") : null;
511
- }
512
- /** Render one structured control-protocol frame into its own transcript channel. */
513
- export function renderFrame(frame, push, appendLive = push, commitLive = push, pushResponse = push, updateTool = (activity) => push(toolActivityLine(activity)), updateThinking = () => { }, setWakeState = () => { }, clearLive = () => { }) {
514
- if (frame.type === "error") {
515
- updateThinking({ phase: "clear", text: "" });
516
- clearLive();
517
- push(`${tuiTheme.error("error")} ${safeError(frame.error)}\n`);
518
- return;
519
- }
520
- if (frame.type === "accepted") {
521
- const value = frame.value && typeof frame.value === "object" && !Array.isArray(frame.value) ? frame.value : {};
522
- setWakeState(value.steered === true ? "working" : "queued");
523
- return;
524
- }
525
- if (frame.type === "result") {
526
- const value = frame.value && typeof frame.value === "object" && !Array.isArray(frame.value) ? frame.value : {};
527
- const response = value.response && typeof value.response === "object" && !Array.isArray(value.response) ? value.response : {};
528
- const turn = value.turn && typeof value.turn === "object" && !Array.isArray(value.turn) ? value.turn : {};
529
- const error = turn.error && typeof turn.error === "object" && !Array.isArray(turn.error) ? turn.error : {};
530
- updateThinking({ phase: "done", text: "" });
531
- if (typeof response.content === "string" && response.content.trim())
532
- pushResponse(response.content.trim());
533
- else if (turn.status === "failed" && typeof error.message === "string")
534
- push(`${tuiTheme.error("error")} ${safeError(error.message)}\n`);
535
- return;
536
- }
537
- if (frame.type !== "event")
538
- return;
539
- const event = frame.event;
540
- if (!event || typeof event !== "object" || Array.isArray(event))
541
- return;
542
- const record = event;
543
- const data = record.data && typeof record.data === "object" && !Array.isArray(record.data) ? record.data : {};
544
- if (record.type === "wake.enqueued") {
545
- setWakeState("queued");
546
- return;
547
- }
548
- if (record.type === "turn.started" || record.type === "transcript.started") {
549
- setWakeState("working");
550
- return;
551
- }
552
- if (record.type === "tool.called") {
553
- updateThinking({ phase: "done", text: "" });
554
- updateTool({ kind: "tool", callId: String(data.callId ?? "tool"), name: String(data.name ?? "tool"), detail: toolDetail(data.arguments), status: "running" });
555
- }
556
- else if (record.type === "tool.completed") {
557
- updateTool({ kind: "tool", callId: String(data.callId ?? "tool"), name: String(data.name ?? "tool"), detail: "", status: data.isError ? "failed" : "done" });
558
- }
559
- else if (record.type === "message.assistant.delta") {
560
- const delta = data.delta && typeof data.delta === "object" && !Array.isArray(data.delta) ? data.delta : {};
561
- if (delta.type === "thinking_start")
562
- updateThinking({ phase: "start", text: "" });
563
- else if (delta.type === "thinking_delta" && typeof delta.delta === "string")
564
- updateThinking({ phase: "delta", text: delta.delta });
565
- else if (delta.type === "thinking_end")
566
- updateThinking({ phase: "done", text: typeof delta.content === "string" ? delta.content : "" });
567
- else if (delta.type === "text_start" || delta.type === "text_delta") {
568
- updateThinking({ phase: "done", text: "" });
569
- if (delta.type === "text_delta" && typeof delta.delta === "string")
570
- appendLive(delta.delta);
571
- }
572
- else if (delta.type === "toolcall_start" || delta.type === "toolcall_delta")
573
- updateThinking({ phase: "done", text: "" });
574
- }
575
- else if (record.type === "message.assistant.completed") {
576
- updateThinking({ phase: "done", text: "" });
577
- const message = data.message && typeof data.message === "object" ? data.message : {};
578
- if (message.stopReason === "error" || message.stopReason === "aborted") {
579
- clearLive();
580
- return;
581
- }
582
- const text = messageText(message.content);
583
- if (data.completionIntent === "handoff") {
584
- clearLive();
585
- return;
586
- }
587
- if (text)
588
- commitLive(text);
589
- }
590
- else if (record.type === "response.committed") {
591
- if (typeof data.text === "string" && data.text.trim())
592
- commitLive(data.text.trim());
593
- }
594
- else if (record.type === "handoff.recorded") {
595
- if (typeof data.goalId === "string")
596
- push(`${data.outcome === "blocked" ? tuiTheme.error("goal blocked") : tuiTheme.success("goal saved")} ${tuiTheme.muted(`${String(data.outcome).replaceAll("_", " ")} · record r${String(data.recordRevision)}`)}`);
597
- }
598
- else if (record.type === "ceo.human_requested") {
599
- push(`${tuiTheme.warning("needs you")} ${safeError(compact(record.data ?? {}))}`);
600
- }
601
- else if (record.type === "transcript.interrupted") {
602
- updateThinking({ phase: "clear", text: "" });
603
- clearLive();
604
- push(`${tuiTheme.error("error")} ${safeError(typeof data.reason === "string" ? data.reason : "Turn failed")}\n`);
605
- }
606
- }
607
- function toolDetail(value) {
608
- if (!value || typeof value !== "object" || Array.isArray(value))
609
- return "";
610
- const args = value;
611
- const candidate = [args.path, args.file_path, args.query, args.command, args.goalId].find((item) => typeof item === "string");
612
- if (!candidate)
613
- return "";
614
- const oneLine = candidate.replace(/\s+/g, " ").trim();
615
- return oneLine.length > 72 ? `${oneLine.slice(0, 69)}…` : oneLine;
616
- }
617
- function toolActivityLine(activity) {
618
- const state = activity.status === "running" ? tuiTheme.accent("running") : activity.status === "failed" ? tuiTheme.error("failed") : tuiTheme.success("done");
619
- const detail = activity.detail ? ` ${tuiTheme.muted(activity.detail)}` : "";
620
- return ` ${state} ${tuiTheme.strong(activity.name)}${detail}`;
621
- }
622
- async function printStatus(stateDir, push) {
623
- try {
624
- const status = await requestControl(stateDir, { op: "ceo.status" });
625
- const value = status && typeof status === "object" && !Array.isArray(status) ? status : {};
626
- const roots = Array.isArray(value.roots) ? value.roots : [];
627
- const team = Array.isArray(value.team) ? value.team : [];
628
- const pending = Array.isArray(value.pendingHuman) ? value.pendingHuman.length : 0;
629
- push([tuiTheme.strong("Status"), roots[0] ? ` ${tuiTheme.active(" GOAL ")} ${String(roots[0].objective)} ${tuiTheme.muted(String(roots[0].phase))}` : ` ${tuiTheme.muted("No active Goal")}`, ` ${tuiTheme.muted(team.length ? team.map((member) => `${String(member.agent)} ${String(member.motion)}${member.lastOutcome ? `/${String(member.lastOutcome)}` : ""}`).join(" · ") : "No Goal Agents")}`, ...(pending ? [` ${tuiTheme.warning(`${pending} decision${pending === 1 ? "" : "s"} need you`)}`] : []), ""].join("\n"));
630
- }
631
- catch (error) {
632
- push(errorLine(error));
633
- }
634
- }
635
- async function refreshGoalBar(stateDir, view, tui) {
636
- try {
637
- const status = await requestControl(stateDir, { op: "ceo.status" });
638
- const roots = status && typeof status === "object" && !Array.isArray(status) && Array.isArray(status.roots) ? status.roots : [];
639
- const root = roots[0];
640
- view.setRoot(root ? { objective: String(root.objective), phase: String(root.phase) } : null);
641
- tui.requestRender();
642
- }
643
- catch { }
644
- }
645
- async function printRecords(text, stateDir, push) {
646
- try {
647
- if (text === "/records") {
648
- const records = await requestControl(stateDir, { op: "work.records" });
649
- const values = Array.isArray(records) ? records : [];
650
- push(values.length ? `${tuiTheme.strong("Work Records")}\n${values.map((record) => ` ${tuiTheme.accent(String(record.goalId))} ${tuiTheme.muted(`r${String(record.recordRevision)} · ${String(record.updatedBy)}`)}`).join("\n")}\n` : tuiTheme.muted("No Goal Work Records."));
651
- return;
652
- }
653
- const history = text.startsWith("/history ");
654
- const goalId = text.slice(history ? 9 : 9).trim();
655
- const value = await requestControl(stateDir, history ? { op: "work.history", goalId } : { op: "work.record", goalId });
656
- if (history && Array.isArray(value))
657
- push(`${tuiTheme.strong(`History · ${goalId}`)}\n${value.map((record) => ` ${tuiTheme.accent(`r${String(record.recordRevision)}`)} ${String(record.updatedBy)} · ${tuiTheme.muted(String(record.reason))}`).join("\n")}\n`);
658
- else if (value && typeof value === "object" && !Array.isArray(value))
659
- push(String(value.content ?? "Record is empty."));
660
- else
661
- push("Work Record not found.");
662
- }
663
- catch (error) {
664
- push(errorLine(error));
665
- }
666
- }
667
- async function stopCeoWake(stateDir, push) {
668
- try {
669
- const snapshot = await requestControl(stateDir, { op: "status" });
670
- const turns = snapshot && typeof snapshot === "object" && !Array.isArray(snapshot) && Array.isArray(snapshot.turns) ? snapshot.turns : [];
671
- const turnId = findLiveTurnId(turns);
672
- if (!turnId) {
673
- push(tuiTheme.muted("No active Turn."));
674
- return;
675
- }
676
- await requestControl(stateDir, { op: "turn.interrupt", turnId });
677
- push(`${tuiTheme.warning("stopping")} Current Turn was interrupted.`);
678
- }
679
- catch (error) {
680
- push(errorLine(error));
681
- }
682
- }
683
- async function slashGoal(text, stateDir, push) {
684
- const isGoal = text.startsWith("/goal ");
685
- const value = text.slice(isGoal ? 6 : 9).trim();
686
- try {
687
- const status = await requestControl(stateDir, { op: "ceo.status" });
688
- const roots = status && typeof status === "object" && !Array.isArray(status) && Array.isArray(status.roots) ? status.roots : [];
689
- const root = roots.find((item) => item && typeof item === "object" && !Array.isArray(item));
690
- if (!root) {
691
- if (!isGoal)
692
- throw new Error("no active root goal");
693
- const created = await requestControl(stateDir, { op: "goal.start", objective: value });
694
- const goal = created && typeof created === "object" && !Array.isArray(created) ? created.goal : undefined;
695
- push(`${tuiTheme.success("goal created")} ${String(goal?.objective ?? value)}\n`);
696
- return;
697
- }
698
- const op = isGoal
699
- ? { op: "goal.update", id: String(root.id), objective: value }
700
- : { op: "goal.observe", id: String(root.id), observationMethod: value };
701
- const updated = await requestControl(stateDir, op);
702
- const goal = updated && typeof updated === "object" && !Array.isArray(updated) && updated.goal && typeof updated.goal === "object" ? updated.goal : updated;
703
- push(`${tuiTheme.success(isGoal ? "goal updated" : "observation set")} ${String(goal?.objective ?? value)}\n`);
704
- }
705
- catch (error) {
706
- push(errorLine(error));
707
- }
708
- }
709
- async function ensureDaemon(configPath, stateDir) {
710
- if (await controlAvailable(stateDir)) {
711
- const version = await requestControl(stateDir, { op: "daemon.version" }).catch(() => null);
712
- if (version === installedVersion())
713
- return;
714
- await requestControl(stateDir, { op: "daemon.stop" }).catch(() => undefined);
715
- const stopDeadline = Date.now() + 5_000;
716
- while (await controlAvailable(stateDir) && Date.now() < stopDeadline)
717
- await new Promise((resolveWait) => setTimeout(resolveWait, 50));
718
- }
719
- mkdirSync(stateDir, { recursive: true });
720
- const log = openSync(join(stateDir, "daemon.log"), "a");
721
- spawn(process.execPath, [process.argv[1], "start", "--config", resolve(configPath)], { cwd: process.cwd(), detached: true, stdio: ["ignore", log, log], env: process.env }).unref();
722
- closeSync(log);
723
- const deadline = Date.now() + 10_000;
724
- while (Date.now() < deadline) {
725
- if (await controlAvailable(stateDir))
726
- return;
727
- const { promise: tick, resolve: resolveWait } = Promise.withResolvers();
728
- setTimeout(resolveWait, 100);
729
- await tick;
730
- }
731
- throw new Error("Goah Supervisor did not start; run `goah doctor` and inspect the configured provider credentials");
732
- }
733
- function compact(value) {
734
- const json = JSON.stringify(value) ?? "";
735
- return json.length > 160 ? `${json.slice(0, 157)}…` : json;
736
- }
737
- function errorLine(error) { return `${tuiTheme.error("error")} ${safeError(error instanceof Error ? error.message : String(error))}\n`; }
738
- function safeError(value) {
739
- const sanitized = value
740
- .replace(/environment variable is missing:\s*[^\s(]+/gi, "environment variable is missing: [REDACTED]")
741
- .replace(/(?:sk|key|token)[-_][A-Za-z0-9._-]{12,}/gi, "[REDACTED]");
742
- const provider = sanitized.match(/^(\d{3}):\s*(\{.*\})$/s);
743
- if (provider) {
744
- try {
745
- const body = JSON.parse(provider[2]);
746
- if (typeof body.message === "string")
747
- return `Provider ${provider[1]}: ${body.message}`;
748
- }
749
- catch { }
750
- }
751
- const named = sanitized.match(/^(?:TypeError|RangeError|ReferenceError|SyntaxError|Error):\s*[^\n]+/m)?.[0];
752
- if (named)
753
- return named;
754
- return sanitized.split("\n").map((line) => line.trim()).find((line) => line && !line.startsWith("file://") && !line.startsWith("at ") && line !== "^") ?? "Turn failed";
755
- }
756
- function messageText(value) {
757
- if (typeof value === "string")
758
- return value;
759
- if (!Array.isArray(value))
760
- return "";
761
- return value
762
- .map((item) => item && typeof item === "object" && !Array.isArray(item) && item.type === "text" && typeof item.text === "string" ? item.text : "")
763
- .filter(Boolean)
764
- .join("\n");
765
- }
766
- /** /model ID — write config and hot-reload the daemon runner. */
767
- async function switchModelCommand(text, configPath, stateDir, push) {
768
- const model = text.slice(7).trim();
769
- if (!model) {
770
- push(errorLine("usage: /model MODEL_ID"));
771
- return;
772
- }
773
- try {
774
- push(await switchModel(configPath, stateDir, model));
775
- }
776
- catch (error) {
777
- push(errorLine(error));
778
- }
779
- }
780
- function configuredRunnerProfile(configPath) {
781
- const workspace = existsSync(configPath) ? loadConfig(configPath) : null;
782
- return workspace?.runnerProfiles?.find((profile) => profile.id === "default") ?? workspace?.runnerProfiles?.[0] ?? readDefaultRunnerProfile();
783
- }
784
- //# sourceMappingURL=tui.js.map