@goah/cli 0.11.4 → 0.13.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.
- package/dist/cli.js +165 -66
- package/dist/cli.js.map +1 -1
- package/dist/console/assets/index-3sYrDMmt.css +1 -0
- package/dist/console/assets/index-Rtc_B7cs.js +52 -0
- package/dist/console/assets/index-Rtc_B7cs.js.map +1 -0
- package/dist/console/index.html +2 -2
- package/dist/control.d.ts +13 -13
- package/dist/control.d.ts.map +1 -1
- package/dist/control.js +77 -50
- package/dist/control.js.map +1 -1
- package/dist/index.d.ts +6 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +156 -28
- package/dist/index.js.map +1 -1
- package/dist/live-config.js +2 -2
- package/dist/live-config.js.map +1 -1
- package/dist/prompt-queue.d.ts.map +1 -1
- package/dist/prompt-queue.js +5 -0
- package/dist/prompt-queue.js.map +1 -1
- package/dist/setup-wizard.d.ts +13 -2
- package/dist/setup-wizard.d.ts.map +1 -1
- package/dist/setup-wizard.js +87 -63
- package/dist/setup-wizard.js.map +1 -1
- package/dist/thread-inspect.d.ts +47 -0
- package/dist/thread-inspect.d.ts.map +1 -0
- package/dist/thread-inspect.js +78 -0
- package/dist/thread-inspect.js.map +1 -0
- package/dist/transcript.d.ts +2 -0
- package/dist/transcript.d.ts.map +1 -0
- package/dist/transcript.js +2 -0
- package/dist/transcript.js.map +1 -0
- package/dist/tui-theme.d.ts +1 -0
- package/dist/tui-theme.d.ts.map +1 -1
- package/dist/tui-theme.js +1 -0
- package/dist/tui-theme.js.map +1 -1
- package/dist/tui.d.ts +16 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +332 -80
- package/dist/tui.js.map +1 -1
- package/dist/update.d.ts +1 -0
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +15 -7
- package/dist/update.js.map +1 -1
- package/dist/web-console.d.ts +1 -1
- package/dist/web-console.d.ts.map +1 -1
- package/dist/web-console.js +27 -37
- package/dist/web-console.js.map +1 -1
- package/dist/welcome.d.ts +2 -2
- package/dist/welcome.d.ts.map +1 -1
- package/dist/welcome.js +24 -36
- package/dist/welcome.js.map +1 -1
- package/node_modules/goah-ledger-contract/dist/execution.d.ts +272 -130
- package/node_modules/goah-ledger-contract/dist/execution.d.ts.map +1 -1
- package/node_modules/goah-ledger-contract/dist/execution.js +31 -19
- package/node_modules/goah-ledger-contract/dist/execution.js.map +1 -1
- package/node_modules/goah-ledger-contract/dist/index.d.ts +1 -2
- package/node_modules/goah-ledger-contract/dist/index.d.ts.map +1 -1
- package/node_modules/goah-ledger-contract/dist/index.js +1 -2
- package/node_modules/goah-ledger-contract/dist/index.js.map +1 -1
- package/node_modules/goah-ledger-contract/dist/kernel.d.ts +1 -1
- package/node_modules/goah-ledger-contract/dist/kernel.d.ts.map +1 -1
- package/node_modules/goah-ledger-contract/dist/kernel.js +1 -1
- package/node_modules/goah-ledger-contract/dist/kernel.js.map +1 -1
- package/node_modules/goah-ledger-contract/dist/transcript.d.ts +63 -0
- package/node_modules/goah-ledger-contract/dist/transcript.d.ts.map +1 -0
- package/node_modules/goah-ledger-contract/dist/transcript.js +115 -0
- package/node_modules/goah-ledger-contract/dist/transcript.js.map +1 -0
- package/node_modules/goah-ledger-contract/package.json +3 -4
- package/node_modules/goah-ledger-sqlite/dist/index.d.ts +32 -23
- package/node_modules/goah-ledger-sqlite/dist/index.d.ts.map +1 -1
- package/node_modules/goah-ledger-sqlite/dist/index.js +764 -394
- package/node_modules/goah-ledger-sqlite/dist/index.js.map +1 -1
- package/node_modules/goah-ledger-sqlite/package.json +2 -2
- package/node_modules/goah-runner-pi/dist/configurator.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/configurator.js +186 -30
- package/node_modules/goah-runner-pi/dist/configurator.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/credential-store.d.ts +2 -0
- package/node_modules/goah-runner-pi/dist/credential-store.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/credential-store.js +117 -23
- package/node_modules/goah-runner-pi/dist/credential-store.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/index.d.ts +18 -6
- package/node_modules/goah-runner-pi/dist/index.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/index.js +97 -12
- package/node_modules/goah-runner-pi/dist/index.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/pi-worker.d.ts +6 -2
- package/node_modules/goah-runner-pi/dist/pi-worker.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/pi-worker.js +232 -70
- package/node_modules/goah-runner-pi/dist/pi-worker.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts +2 -0
- package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.d.ts.map +1 -0
- package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js +3 -0
- package/node_modules/goah-runner-pi/dist/steering-no-ack.test-fixture.js.map +1 -0
- package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts +2 -0
- package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.d.ts.map +1 -0
- package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js +5 -0
- package/node_modules/goah-runner-pi/dist/steering-reject.test-fixture.js.map +1 -0
- package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts +2 -0
- package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.d.ts.map +1 -0
- package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js +5 -0
- package/node_modules/goah-runner-pi/dist/steering-worker.test-fixture.js.map +1 -0
- package/node_modules/goah-runner-pi/dist/test-helpers.d.ts +1 -1
- package/node_modules/goah-runner-pi/dist/test-helpers.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/test-helpers.js +3 -1
- package/node_modules/goah-runner-pi/dist/test-helpers.js.map +1 -1
- package/node_modules/goah-runner-pi/dist/verification-worker.d.ts.map +1 -1
- package/node_modules/goah-runner-pi/dist/verification-worker.js +3 -5
- package/node_modules/goah-runner-pi/dist/verification-worker.js.map +1 -1
- package/node_modules/goah-runner-pi/package.json +2 -2
- package/node_modules/goah-supervisor/dist/context-view.d.ts +4 -5
- package/node_modules/goah-supervisor/dist/context-view.d.ts.map +1 -1
- package/node_modules/goah-supervisor/dist/context-view.js +7 -20
- package/node_modules/goah-supervisor/dist/context-view.js.map +1 -1
- package/node_modules/goah-supervisor/dist/index.d.ts +38 -36
- package/node_modules/goah-supervisor/dist/index.d.ts.map +1 -1
- package/node_modules/goah-supervisor/dist/index.js +762 -575
- package/node_modules/goah-supervisor/dist/index.js.map +1 -1
- package/node_modules/goah-supervisor/dist/roles.d.ts +2 -1
- package/node_modules/goah-supervisor/dist/roles.d.ts.map +1 -1
- package/node_modules/goah-supervisor/dist/roles.js +20 -5
- package/node_modules/goah-supervisor/dist/roles.js.map +1 -1
- package/node_modules/goah-supervisor/dist/verification.d.ts +8 -29
- package/node_modules/goah-supervisor/dist/verification.d.ts.map +1 -1
- package/node_modules/goah-supervisor/dist/verification.js +71 -46
- package/node_modules/goah-supervisor/dist/verification.js.map +1 -1
- package/node_modules/goah-supervisor/package.json +2 -2
- package/node_modules/goah-testkit/dist/faux-runner-worker.js +16 -9
- package/node_modules/goah-testkit/dist/faux-runner-worker.js.map +1 -1
- package/node_modules/goah-testkit/dist/index.d.ts +13 -16
- package/node_modules/goah-testkit/dist/index.d.ts.map +1 -1
- package/node_modules/goah-testkit/dist/index.js +27 -44
- package/node_modules/goah-testkit/dist/index.js.map +1 -1
- package/node_modules/goah-testkit/package.json +5 -5
- package/package.json +7 -8
- package/dist/console/assets/index-BJTGvlCH.js +0 -52
- package/dist/console/assets/index-BJTGvlCH.js.map +0 -1
- package/dist/console/assets/index-msBn41Cl.css +0 -1
- package/dist/inspect.d.ts +0 -61
- package/dist/inspect.d.ts.map +0 -1
- package/dist/inspect.js +0 -110
- package/dist/inspect.js.map +0 -1
- package/dist/metrics.d.ts +0 -2
- package/dist/metrics.d.ts.map +0 -1
- package/dist/metrics.js +0 -2
- package/dist/metrics.js.map +0 -1
- package/dist/session.d.ts +0 -2
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js +0 -2
- package/dist/session.js.map +0 -1
- package/node_modules/goah-ledger-contract/dist/metrics.d.ts +0 -40
- package/node_modules/goah-ledger-contract/dist/metrics.d.ts.map +0 -1
- package/node_modules/goah-ledger-contract/dist/metrics.js +0 -25
- package/node_modules/goah-ledger-contract/dist/metrics.js.map +0 -1
- package/node_modules/goah-ledger-contract/dist/session.d.ts +0 -63
- package/node_modules/goah-ledger-contract/dist/session.d.ts.map +0 -1
- package/node_modules/goah-ledger-contract/dist/session.js +0 -115
- package/node_modules/goah-ledger-contract/dist/session.js.map +0 -1
- package/node_modules/goah-testkit/dist/mock-connector-worker.d.ts +0 -2
- package/node_modules/goah-testkit/dist/mock-connector-worker.d.ts.map +0 -1
- package/node_modules/goah-testkit/dist/mock-connector-worker.js +0 -29
- package/node_modules/goah-testkit/dist/mock-connector-worker.js.map +0 -1
package/dist/tui.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/** Full-screen goah TUI: streaming CEO transcript over the resident Supervisor control socket. */
|
|
2
2
|
import { TuiAltScreen, Text, Markdown, Editor, ProcessTerminal, ScrollView, VStack } from "@earendil-works/pi-tui";
|
|
3
3
|
import { controlAvailable, requestControl, streamControl } from "./control.js";
|
|
4
|
-
import { readConsoleMetadata } from "./index.js";
|
|
4
|
+
import { loadConfig, readConsoleMetadata, readDefaultRunnerProfile } from "./index.js";
|
|
5
5
|
import { switchModel, reloadDaemon, readRunnerDisplay } from "./live-config.js";
|
|
6
6
|
import { welcomeSnapshot, renderWelcome } from "./welcome.js";
|
|
7
|
-
import { runSetupWizard, applyWizardResult } from "./setup-wizard.js";
|
|
7
|
+
import { chooseSetupSection, runRunnerCommandWizard, runSetupWizard, applyWizardResult } from "./setup-wizard.js";
|
|
8
8
|
import { installedVersion } from "./update.js";
|
|
9
9
|
import { tuiTheme } from "./tui-theme.js";
|
|
10
10
|
import { spawn } from "node:child_process";
|
|
11
|
-
import { closeSync, mkdirSync, openSync } from "node:fs";
|
|
11
|
+
import { closeSync, existsSync, mkdirSync, openSync } from "node:fs";
|
|
12
12
|
import { join, resolve } from "node:path";
|
|
13
13
|
class HeaderBar {
|
|
14
14
|
runner;
|
|
@@ -43,23 +43,79 @@ const markdownTheme = {
|
|
|
43
43
|
class ConversationView {
|
|
44
44
|
entries;
|
|
45
45
|
liveMarkdown = "";
|
|
46
|
+
liveThinking = "";
|
|
47
|
+
thinkingActive = false;
|
|
46
48
|
constructor(initial) { this.entries = [{ kind: "text", content: initial.join("\n") }]; }
|
|
47
49
|
addText(content) { this.entries.push({ kind: "text", content }); this.trim(); }
|
|
48
|
-
|
|
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
|
+
}
|
|
49
62
|
setLiveMarkdown(content) { this.liveMarkdown = content; }
|
|
50
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
|
+
}
|
|
51
96
|
trim() { if (this.entries.length > 240)
|
|
52
97
|
this.entries.splice(1, this.entries.length - 240); }
|
|
53
98
|
render(width) {
|
|
54
99
|
const rendered = this.entries.flatMap((entry) => entry.kind === "markdown"
|
|
55
|
-
? [
|
|
56
|
-
:
|
|
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…"));
|
|
57
112
|
if (this.liveMarkdown)
|
|
58
|
-
rendered.push(
|
|
113
|
+
rendered.push(...new Markdown(this.liveMarkdown, 2, 0, markdownTheme).render(width));
|
|
59
114
|
return rendered;
|
|
60
115
|
}
|
|
61
116
|
invalidate() { }
|
|
62
117
|
}
|
|
118
|
+
export function renderUserMessage(content, width) { return new Text(content, 2, 1, tuiTheme.userMessage).render(width); }
|
|
63
119
|
export function renderTuiHeader(width, runner, target, version) {
|
|
64
120
|
const brand = " GOAH ";
|
|
65
121
|
const release = width >= 34 ? ` v${version} ` : "";
|
|
@@ -94,13 +150,17 @@ export function classifyTuiInput(value, busy) {
|
|
|
94
150
|
return { action: "stop", text };
|
|
95
151
|
if (text === "/model" || text.startsWith("/model "))
|
|
96
152
|
return { action: "model", text };
|
|
97
|
-
if (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 "))
|
|
98
158
|
return { action: "setup", text };
|
|
99
159
|
if (text.startsWith("/goal ") || text.startsWith("/observe "))
|
|
100
160
|
return { action: "goal", text };
|
|
101
|
-
if (text.startsWith("/
|
|
102
|
-
return { action: "
|
|
103
|
-
return { action: busy ? "
|
|
161
|
+
if (text.startsWith("/"))
|
|
162
|
+
return { action: "unknown", text };
|
|
163
|
+
return { action: busy ? "steer" : "send", text };
|
|
104
164
|
}
|
|
105
165
|
export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
106
166
|
if (!process.stdout.isTTY || !process.stdin.isTTY)
|
|
@@ -109,11 +169,19 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
109
169
|
const snapshot = welcomeSnapshot(stateDir, runner);
|
|
110
170
|
const welcome = renderWelcome(snapshot, Boolean(snapshot.root || snapshot.handoffs.length || snapshot.conversation.length));
|
|
111
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);
|
|
112
175
|
const terminal = new ProcessTerminal();
|
|
113
176
|
const tui = new TuiAltScreen(terminal, true, undefined, { mouse: true });
|
|
114
177
|
terminal.setTitle(`Goah · ${runner.target}`);
|
|
115
178
|
const headerView = new HeaderBar(runner.runner, runner.target, installedVersion());
|
|
116
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);
|
|
117
185
|
const conversationScroll = new ScrollView(transcriptView, { follow: "end", primary: true, scrollbar: "auto", scrollbarStyle: tuiTheme.muted });
|
|
118
186
|
const goalView = new GoalBar(snapshot.root);
|
|
119
187
|
const input = new Editor(tui, {
|
|
@@ -130,7 +198,11 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
130
198
|
]);
|
|
131
199
|
const busy = { active: false };
|
|
132
200
|
const queued = [];
|
|
201
|
+
const queuedTurnIds = [];
|
|
133
202
|
let activeStream = null;
|
|
203
|
+
let activeInteractionTurnId = null;
|
|
204
|
+
let steeringTail = Promise.resolve();
|
|
205
|
+
let configuring = false;
|
|
134
206
|
let exiting = false;
|
|
135
207
|
const push = (line) => {
|
|
136
208
|
transcriptView.addText(line);
|
|
@@ -138,20 +210,29 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
138
210
|
};
|
|
139
211
|
let liveText = "";
|
|
140
212
|
const appendLive = (text) => { liveText += text; transcriptView.setLiveMarkdown(liveText); tui.requestRender(); };
|
|
141
|
-
const commitLive = (text) => { liveText = ""; if (text)
|
|
213
|
+
const commitLive = (text) => { liveText = ""; transcriptView.clearLiveMarkdown(); if (text)
|
|
142
214
|
transcriptView.addMarkdown(text); tui.requestRender(); };
|
|
143
215
|
const pushResponse = (text) => { transcriptView.addMarkdown(text); tui.requestRender(); };
|
|
144
|
-
const
|
|
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) => {
|
|
145
220
|
busy.active = true;
|
|
146
221
|
const controller = new AbortController();
|
|
147
222
|
activeStream = controller;
|
|
148
223
|
statusView.setText(statusText("working", queued.length));
|
|
149
|
-
|
|
224
|
+
if (showUser) {
|
|
225
|
+
transcriptView.addUser(message);
|
|
226
|
+
tui.requestRender();
|
|
227
|
+
}
|
|
150
228
|
try {
|
|
151
|
-
await streamControl(stateDir, { op: "interact", message }, (frame) =>
|
|
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);
|
|
152
232
|
}
|
|
153
233
|
catch (error) {
|
|
154
234
|
transcriptView.clearLiveMarkdown();
|
|
235
|
+
transcriptView.updateThinking({ phase: "clear", text: "" });
|
|
155
236
|
if (!controller.signal.aborted)
|
|
156
237
|
push(errorLine(error));
|
|
157
238
|
}
|
|
@@ -160,30 +241,153 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
160
241
|
activeStream = null;
|
|
161
242
|
busy.active = false;
|
|
162
243
|
await refreshGoalBar(stateDir, goalView, tui);
|
|
163
|
-
statusView.setText(statusText(queued.length ? "queued" : "ready", queued.length));
|
|
164
|
-
|
|
165
|
-
if (next)
|
|
166
|
-
void send(next);
|
|
244
|
+
statusView.setText(statusText(queuedTurnIds.length || queued.length ? "queued" : "ready", queuedTurnIds.length + queued.length));
|
|
245
|
+
continuePending();
|
|
167
246
|
}
|
|
168
247
|
};
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
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);
|
|
172
277
|
return;
|
|
173
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;
|
|
174
329
|
statusView.setText(statusText("setup"));
|
|
175
330
|
tui.stop();
|
|
176
331
|
try {
|
|
177
|
-
await
|
|
332
|
+
await work();
|
|
333
|
+
}
|
|
334
|
+
catch (error) {
|
|
335
|
+
push(errorLine(error));
|
|
178
336
|
}
|
|
179
337
|
finally {
|
|
338
|
+
configuring = false;
|
|
180
339
|
if (!exiting) {
|
|
181
340
|
tui.start();
|
|
182
341
|
tui.requestRender(true);
|
|
183
342
|
statusView.setText(statusText("ready"));
|
|
343
|
+
continuePending();
|
|
184
344
|
}
|
|
185
345
|
}
|
|
186
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
|
+
});
|
|
187
391
|
input.onSubmit = (line) => {
|
|
188
392
|
const { action, text } = classifyTuiInput(line, busy.active);
|
|
189
393
|
input.setText("");
|
|
@@ -195,7 +399,7 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
195
399
|
return;
|
|
196
400
|
}
|
|
197
401
|
if (action === "help") {
|
|
198
|
-
push(`${tuiTheme.strong("Commands")}\n /model /setup /status /records /history
|
|
402
|
+
push(`${tuiTheme.strong("Commands")}\n /model /login /logout /setup /status\n /records /history /goal /observe /stop /quit\n`);
|
|
199
403
|
return;
|
|
200
404
|
}
|
|
201
405
|
if (action === "status") {
|
|
@@ -212,27 +416,33 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
212
416
|
}
|
|
213
417
|
if (action === "model") {
|
|
214
418
|
if (text === "/model")
|
|
215
|
-
void
|
|
419
|
+
void launchRunnerCommand("model");
|
|
216
420
|
else
|
|
217
421
|
void switchModelCommand(text, configPath, stateDir, push);
|
|
218
422
|
return;
|
|
219
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
|
+
}
|
|
220
432
|
if (action === "setup") {
|
|
221
|
-
void launchSetup();
|
|
433
|
+
void launchSetup(text);
|
|
222
434
|
return;
|
|
223
435
|
}
|
|
224
436
|
if (action === "goal") {
|
|
225
437
|
void slashGoal(text, stateDir, push).finally(() => refreshGoalBar(stateDir, goalView, tui));
|
|
226
438
|
return;
|
|
227
439
|
}
|
|
228
|
-
if (action === "
|
|
229
|
-
|
|
440
|
+
if (action === "unknown") {
|
|
441
|
+
push(errorLine(`Unknown command: ${text.split(/\s+/, 1)[0]}. Use /help to list commands.`));
|
|
230
442
|
return;
|
|
231
443
|
}
|
|
232
|
-
if (action === "
|
|
233
|
-
|
|
234
|
-
push(tuiTheme.muted(`queued ${text}`));
|
|
235
|
-
statusView.setText(statusText("working", queued.length));
|
|
444
|
+
if (action === "steer") {
|
|
445
|
+
submitSteer(text);
|
|
236
446
|
return;
|
|
237
447
|
}
|
|
238
448
|
if (action === "send")
|
|
@@ -245,7 +455,7 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
245
455
|
if (data !== "\x03")
|
|
246
456
|
return undefined;
|
|
247
457
|
exiting = true;
|
|
248
|
-
push(busy.active ? "Detached — the current
|
|
458
|
+
push(busy.active ? "Detached — the current Turn continues in the daemon. Use /stop before detaching when you intend to cancel it." : "Detached.");
|
|
249
459
|
activeStream?.abort();
|
|
250
460
|
tui.stop();
|
|
251
461
|
resolveExit();
|
|
@@ -254,8 +464,14 @@ export async function runGoahTui(configPath, stateDir, initialMessage) {
|
|
|
254
464
|
tui.start();
|
|
255
465
|
if (initialMessage)
|
|
256
466
|
void send(initialMessage);
|
|
467
|
+
else if (typeof liveInteractionTurnId === "string")
|
|
468
|
+
void attachTurn(liveInteractionTurnId);
|
|
257
469
|
await exited;
|
|
258
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
|
+
}
|
|
259
475
|
async function runNonInteractive(configPath, stateDir, initialMessage) {
|
|
260
476
|
await ensureDaemon(configPath, stateDir);
|
|
261
477
|
console.log(`Console: ${(await waitForConsoleMetadata(stateDir)).url}`);
|
|
@@ -293,19 +509,29 @@ function frameToLine(frame) {
|
|
|
293
509
|
renderFrame(frame, (line) => lines.push(line));
|
|
294
510
|
return lines.length ? lines.join("\n") : null;
|
|
295
511
|
}
|
|
296
|
-
/** Render one control-protocol frame into
|
|
297
|
-
export function renderFrame(frame, push, appendLive = push, commitLive = push, pushResponse = push) {
|
|
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 = () => { }) {
|
|
298
514
|
if (frame.type === "error") {
|
|
515
|
+
updateThinking({ phase: "clear", text: "" });
|
|
516
|
+
clearLive();
|
|
299
517
|
push(`${tuiTheme.error("error")} ${safeError(frame.error)}\n`);
|
|
300
518
|
return;
|
|
301
519
|
}
|
|
302
|
-
if (frame.type === "accepted")
|
|
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");
|
|
303
523
|
return;
|
|
524
|
+
}
|
|
304
525
|
if (frame.type === "result") {
|
|
305
526
|
const value = frame.value && typeof frame.value === "object" && !Array.isArray(frame.value) ? frame.value : {};
|
|
306
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: "" });
|
|
307
531
|
if (typeof response.content === "string" && response.content.trim())
|
|
308
532
|
pushResponse(response.content.trim());
|
|
533
|
+
else if (turn.status === "failed" && typeof error.message === "string")
|
|
534
|
+
push(`${tuiTheme.error("error")} ${safeError(error.message)}\n`);
|
|
309
535
|
return;
|
|
310
536
|
}
|
|
311
537
|
if (frame.type !== "event")
|
|
@@ -315,40 +541,84 @@ export function renderFrame(frame, push, appendLive = push, commitLive = push, p
|
|
|
315
541
|
return;
|
|
316
542
|
const record = event;
|
|
317
543
|
const data = record.data && typeof record.data === "object" && !Array.isArray(record.data) ? record.data : {};
|
|
318
|
-
if (record.type === "
|
|
544
|
+
if (record.type === "wake.enqueued") {
|
|
545
|
+
setWakeState("queued");
|
|
319
546
|
return;
|
|
320
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
|
+
}
|
|
321
556
|
else if (record.type === "tool.completed") {
|
|
322
|
-
|
|
557
|
+
updateTool({ kind: "tool", callId: String(data.callId ?? "tool"), name: String(data.name ?? "tool"), detail: "", status: data.isError ? "failed" : "done" });
|
|
323
558
|
}
|
|
324
559
|
else if (record.type === "message.assistant.delta") {
|
|
325
560
|
const delta = data.delta && typeof data.delta === "object" && !Array.isArray(data.delta) ? data.delta : {};
|
|
326
|
-
if (
|
|
327
|
-
|
|
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: "" });
|
|
328
574
|
}
|
|
329
575
|
else if (record.type === "message.assistant.completed") {
|
|
576
|
+
updateThinking({ phase: "done", text: "" });
|
|
330
577
|
const message = data.message && typeof data.message === "object" ? data.message : {};
|
|
578
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
579
|
+
clearLive();
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
331
582
|
const text = messageText(message.content);
|
|
583
|
+
if (data.completionIntent === "handoff") {
|
|
584
|
+
clearLive();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
332
587
|
if (text)
|
|
333
588
|
commitLive(text);
|
|
334
589
|
}
|
|
590
|
+
else if (record.type === "response.committed") {
|
|
591
|
+
if (typeof data.text === "string" && data.text.trim())
|
|
592
|
+
commitLive(data.text.trim());
|
|
593
|
+
}
|
|
335
594
|
else if (record.type === "handoff.recorded") {
|
|
336
595
|
if (typeof data.goalId === "string")
|
|
337
596
|
push(`${data.outcome === "blocked" ? tuiTheme.error("goal blocked") : tuiTheme.success("goal saved")} ${tuiTheme.muted(`${String(data.outcome).replaceAll("_", " ")} · record r${String(data.recordRevision)}`)}`);
|
|
338
|
-
else {
|
|
339
|
-
const results = Array.isArray(data.results) ? data.results : [];
|
|
340
|
-
for (const result of results)
|
|
341
|
-
if (typeof result === "string")
|
|
342
|
-
push(`${tuiTheme.success("done")} ${result}`);
|
|
343
|
-
}
|
|
344
597
|
}
|
|
345
598
|
else if (record.type === "ceo.human_requested") {
|
|
346
599
|
push(`${tuiTheme.warning("needs you")} ${safeError(compact(record.data ?? {}))}`);
|
|
347
600
|
}
|
|
348
|
-
else if (record.type === "
|
|
349
|
-
|
|
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`);
|
|
350
605
|
}
|
|
351
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
|
+
}
|
|
352
622
|
async function printStatus(stateDir, push) {
|
|
353
623
|
try {
|
|
354
624
|
const status = await requestControl(stateDir, { op: "ceo.status" });
|
|
@@ -356,7 +626,7 @@ async function printStatus(stateDir, push) {
|
|
|
356
626
|
const roots = Array.isArray(value.roots) ? value.roots : [];
|
|
357
627
|
const team = Array.isArray(value.team) ? value.team : [];
|
|
358
628
|
const pending = Array.isArray(value.pendingHuman) ? value.pendingHuman.length : 0;
|
|
359
|
-
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.
|
|
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"));
|
|
360
630
|
}
|
|
361
631
|
catch (error) {
|
|
362
632
|
push(errorLine(error));
|
|
@@ -396,8 +666,15 @@ async function printRecords(text, stateDir, push) {
|
|
|
396
666
|
}
|
|
397
667
|
async function stopCeoWake(stateDir, push) {
|
|
398
668
|
try {
|
|
399
|
-
const
|
|
400
|
-
|
|
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.`);
|
|
401
678
|
}
|
|
402
679
|
catch (error) {
|
|
403
680
|
push(errorLine(error));
|
|
@@ -429,25 +706,6 @@ async function slashGoal(text, stateDir, push) {
|
|
|
429
706
|
push(errorLine(error));
|
|
430
707
|
}
|
|
431
708
|
}
|
|
432
|
-
/** /approve ID --reason TEXT --evidence SEQ[,SEQ] (and /reject with the same shape). */
|
|
433
|
-
async function slashApprove(text, stateDir, push) {
|
|
434
|
-
const approve = text.startsWith("/approve ");
|
|
435
|
-
const rest = text.slice(approve ? 9 : 8).match(/^(\S+)(?:\s+--reason\s+(.*?))?(?:\s+--evidence\s+([\d,]+))?$/);
|
|
436
|
-
if (!rest) {
|
|
437
|
-
push(errorLine("usage: /approve ACTION_ID --reason TEXT --evidence SEQ[,SEQ]"));
|
|
438
|
-
return;
|
|
439
|
-
}
|
|
440
|
-
const [, id = "", reason = "terminal approval", evidence = ""] = rest;
|
|
441
|
-
try {
|
|
442
|
-
const op = approve ? { op: "action.approve", id, reason, evidence: evidence ? evidence.split(",").map(Number) : [] }
|
|
443
|
-
: { op: "action.reject", id, reason, evidence: evidence ? evidence.split(",").map(Number) : [] };
|
|
444
|
-
await requestControl(stateDir, op);
|
|
445
|
-
push(`${tuiTheme.success(approve ? "approved" : "rejected")} ${id}`);
|
|
446
|
-
}
|
|
447
|
-
catch (error) {
|
|
448
|
-
push(errorLine(error));
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
709
|
async function ensureDaemon(configPath, stateDir) {
|
|
452
710
|
if (await controlAvailable(stateDir)) {
|
|
453
711
|
const version = await requestControl(stateDir, { op: "daemon.version" }).catch(() => null);
|
|
@@ -493,7 +751,7 @@ function safeError(value) {
|
|
|
493
751
|
const named = sanitized.match(/^(?:TypeError|RangeError|ReferenceError|SyntaxError|Error):\s*[^\n]+/m)?.[0];
|
|
494
752
|
if (named)
|
|
495
753
|
return named;
|
|
496
|
-
return sanitized.split("\n").map((line) => line.trim()).find((line) => line && !line.startsWith("file://") && !line.startsWith("at ") && line !== "^") ?? "
|
|
754
|
+
return sanitized.split("\n").map((line) => line.trim()).find((line) => line && !line.startsWith("file://") && !line.startsWith("at ") && line !== "^") ?? "Turn failed";
|
|
497
755
|
}
|
|
498
756
|
function messageText(value) {
|
|
499
757
|
if (typeof value === "string")
|
|
@@ -519,14 +777,8 @@ async function switchModelCommand(text, configPath, stateDir, push) {
|
|
|
519
777
|
push(errorLine(error));
|
|
520
778
|
}
|
|
521
779
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
if (!result.profile) {
|
|
526
|
-
push("setup cancelled; nothing changed");
|
|
527
|
-
return;
|
|
528
|
-
}
|
|
529
|
-
applyWizardResult(result, configPath);
|
|
530
|
-
push(await reloadDaemon(stateDir, configPath) ? "config reloaded — applies to the next wake" : `saved to ${resolve(configPath)}; daemon will pick it up on next start`);
|
|
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();
|
|
531
783
|
}
|
|
532
784
|
//# sourceMappingURL=tui.js.map
|