@rind-ai/cli 0.4.1 → 0.6.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/bin/rind.js +5 -5
- package/lib/assistant-renderer.js +179 -265
- package/lib/choice-menu-state.js +46 -46
- package/lib/cli-input-actions.js +548 -0
- package/lib/cli-output-controller.js +460 -0
- package/lib/cli-runtime-controller.js +350 -0
- package/lib/cli-state-store.js +32 -0
- package/lib/cli-state.js +41 -0
- package/lib/command-controller.js +159 -126
- package/lib/compact-context-state.js +22 -22
- package/lib/components/assistant-message.js +169 -0
- package/lib/components/composer-area.js +25 -0
- package/lib/components/dynamic-block.js +20 -0
- package/lib/components/monitor-stack.js +35 -0
- package/lib/components/text-block.js +47 -0
- package/lib/components/tool-block.js +122 -0
- package/lib/composer-terminal.js +224 -203
- package/lib/event-controller.js +243 -242
- package/lib/frontend-cli-implementation.js +656 -1111
- package/lib/input-controller.js +75 -94
- package/lib/input-errors.js +3 -3
- package/lib/interrupt-state.js +9 -9
- package/lib/line-editor.js +541 -541
- package/lib/local-slash-commands.js +217 -0
- package/lib/markdown-lines.js +103 -0
- package/lib/model-menu-state.js +50 -50
- package/lib/one-shot-progress.js +145 -0
- package/lib/one-shot.js +228 -0
- package/lib/question-menu-state.js +61 -0
- package/lib/rendering.js +1309 -1060
- package/lib/runtime-client.js +241 -193
- package/lib/runtime-env.js +21 -21
- package/lib/runtime-protocol.js +122 -15
- package/lib/slash-command-mode.js +0 -11
- package/lib/slash-menu-state.js +59 -59
- package/lib/{background-controller.js → task-monitor-controller.js} +411 -289
- package/lib/terminal-key.js +97 -97
- package/lib/text-width.js +335 -151
- package/lib/theme-menu-state.js +31 -0
- package/lib/theme.js +134 -0
- package/lib/tool-display.js +675 -0
- package/lib/tui/component.js +55 -0
- package/lib/tui/cursor.js +29 -0
- package/lib/tui/input-buffer.js +172 -0
- package/lib/tui/tui.js +591 -0
- package/lib/turn-controller.js +68 -78
- package/package.json +28 -28
- package/lib/assistant-stream-buffer.js +0 -25
- package/lib/terminal-ui.js +0 -581
package/lib/runtime-client.js
CHANGED
|
@@ -1,201 +1,249 @@
|
|
|
1
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
import { buildRuntimeEnv } from "./runtime-env.js";
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import { buildRuntimeEnv } from "./runtime-env.js";
|
|
5
|
+
import {
|
|
6
|
+
createRuntimeRequest,
|
|
7
|
+
isRuntimeEvent,
|
|
8
|
+
isRuntimeResponse,
|
|
9
|
+
runtimeMethods,
|
|
10
|
+
runtimeRequestId,
|
|
11
|
+
} from "./runtime-protocol.js";
|
|
12
|
+
|
|
13
|
+
export function resolveRuntimeLaunch({ python, repoRoot, runtimePath = "", cliArgs = [] }) {
|
|
14
|
+
const executable = runtimePath
|
|
15
|
+
? { command: runtimePath, args: [] }
|
|
16
|
+
: { command: python, args: [path.join(repoRoot, "main.py")] };
|
|
17
|
+
const traceFlag = isTraceLlmEnvSet() ? ["--trace-llm"] : [];
|
|
18
|
+
return {
|
|
19
|
+
command: executable.command,
|
|
20
|
+
args: [...executable.args, "app-server", "--stdio", ...traceFlag, ...cliArgs],
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const TRACE_TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
25
|
+
export function isTraceLlmEnvSet() {
|
|
26
|
+
return TRACE_TRUTHY.has(String(process.env.RIND_TRACE_LLM || "").trim().toLowerCase());
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Turns stream for as long as the model runs; every other method must answer
|
|
30
|
+
// within a bounded window so a stalled runtime surfaces as an error, not silence.
|
|
31
|
+
const LONG_RUNNING_METHODS = new Set([runtimeMethods.sessionPrompt, runtimeMethods.sessionFollowUp]);
|
|
32
|
+
const REQUEST_TIMEOUT_MS = 120_000;
|
|
33
|
+
|
|
34
|
+
export function runHelpVersion({ python, repoRoot, runtimePath = "", cliArgs, cwd = process.cwd() }) {
|
|
35
|
+
const executable = runtimePath
|
|
36
|
+
? { command: runtimePath, args: [] }
|
|
37
|
+
: { command: python, args: [path.join(repoRoot, "main.py")] };
|
|
38
|
+
const result = spawnSync(executable.command, [...executable.args, ...cliArgs], {
|
|
39
|
+
cwd,
|
|
40
|
+
env: buildRuntimeEnv(repoRoot, process.env, { sourceRuntime: !runtimePath }),
|
|
41
|
+
stdio: "inherit",
|
|
42
|
+
});
|
|
43
|
+
return result.status ?? 1;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function createRuntimeClient({
|
|
47
|
+
python,
|
|
48
|
+
repoRoot,
|
|
49
|
+
cliArgs = [],
|
|
50
|
+
cwd = process.cwd(),
|
|
51
|
+
rindHome = process.env.RIND_HOME,
|
|
52
|
+
runtimePath = process.env.RIND_RUNTIME_PATH || "",
|
|
53
|
+
onEvent = () => {},
|
|
54
|
+
onMessage = null,
|
|
55
|
+
onStderr = () => {},
|
|
56
|
+
onExit = () => {},
|
|
57
|
+
}) {
|
|
58
|
+
const handleEvent = onMessage || onEvent;
|
|
59
|
+
const launch = resolveRuntimeLaunch({ python, repoRoot, runtimePath, cliArgs });
|
|
60
|
+
|
|
61
|
+
let nextId = 1;
|
|
62
|
+
let stdoutBuffer = "";
|
|
63
|
+
let closing = false;
|
|
64
|
+
let killTimer = null;
|
|
65
|
+
let exitHandled = false;
|
|
66
|
+
const pending = new Map();
|
|
67
|
+
let child = null;
|
|
68
|
+
|
|
69
|
+
function start() {
|
|
70
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
71
|
+
return child;
|
|
72
|
+
}
|
|
73
|
+
if (closing) {
|
|
74
|
+
throw new Error("Runtime is shutting down.");
|
|
75
|
+
}
|
|
76
|
+
stdoutBuffer = "";
|
|
77
|
+
exitHandled = false;
|
|
78
|
+
child = spawn(launch.command, launch.args, {
|
|
79
|
+
cwd,
|
|
80
|
+
env: buildRuntimeEnv(repoRoot, process.env, {
|
|
81
|
+
sourceRuntime: !runtimePath,
|
|
82
|
+
rindHome,
|
|
83
|
+
}),
|
|
84
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
85
|
+
});
|
|
86
|
+
child.stdout.setEncoding("utf8");
|
|
87
|
+
child.stdout.on("data", (chunk) => {
|
|
88
|
+
stdoutBuffer += chunk;
|
|
89
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
90
|
+
stdoutBuffer = lines.pop() || "";
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
if (line) {
|
|
93
|
+
receive(line);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
child.stderr.on("data", (chunk) => onStderr(chunk));
|
|
98
|
+
child.once("error", (error) => {
|
|
99
|
+
handleExit(null, null, error);
|
|
100
|
+
});
|
|
101
|
+
child.once("exit", (code, signal) => {
|
|
102
|
+
handleExit(code, signal);
|
|
103
|
+
});
|
|
104
|
+
return child;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function handleExit(code, signal, cause = null) {
|
|
108
|
+
if (exitHandled) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
exitHandled = true;
|
|
112
|
+
clearKillTimer();
|
|
113
|
+
const error = cause || new Error(`Runtime exited with ${signal || code}`);
|
|
114
|
+
for (const entry of pending.values()) {
|
|
115
|
+
clearRequestTimer(entry);
|
|
116
|
+
entry.reject(error);
|
|
117
|
+
}
|
|
118
|
+
pending.clear();
|
|
119
|
+
child = null;
|
|
120
|
+
onExit(code, signal, { closing, error });
|
|
121
|
+
}
|
|
122
|
+
|
|
98
123
|
function request(method, params = {}) {
|
|
99
124
|
const id = nextId++;
|
|
100
125
|
return new Promise((resolve, reject) => {
|
|
101
|
-
if (!child.stdin.writable || child.destroyed) {
|
|
102
|
-
reject(new Error("Runtime
|
|
126
|
+
if (!child || !child.stdin.writable || child.destroyed) {
|
|
127
|
+
reject(new Error("Runtime is not running. Start it before sending a request."));
|
|
103
128
|
return;
|
|
104
129
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
if (message.kind === "response") {
|
|
124
|
-
finishRequest(message);
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (message.kind === "event") {
|
|
128
|
-
handleEvent(message);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function finishRequest(message) {
|
|
133
|
-
const id = runtimeRequestId(message);
|
|
134
|
-
const callbacks = pending.get(id);
|
|
135
|
-
if (!callbacks) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
pending.delete(id);
|
|
139
|
-
if (message.error) {
|
|
140
|
-
callbacks.reject(new Error(message.error.message || "Runtime request failed"));
|
|
141
|
-
} else {
|
|
142
|
-
callbacks.resolve(message.result);
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function shutdown() {
|
|
147
|
-
if (closing) {
|
|
148
|
-
return Promise.resolve();
|
|
149
|
-
}
|
|
150
|
-
closing = true;
|
|
151
|
-
scheduleKill();
|
|
152
|
-
return request("shutdown").catch(() => {
|
|
153
|
-
forceShutdown();
|
|
154
|
-
}).finally(() => {
|
|
155
|
-
if (child.stdin.writable) {
|
|
156
|
-
child.stdin.end();
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
function forceShutdown() {
|
|
162
|
-
closing = true;
|
|
163
|
-
clearKillTimer();
|
|
164
|
-
if (!child.killed && child.exitCode === null) {
|
|
165
|
-
try {
|
|
166
|
-
child.kill("SIGKILL");
|
|
167
|
-
} catch {
|
|
168
|
-
// Ignore kill races during shutdown.
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function closeInput() {
|
|
174
|
-
if (child.stdin.writable) {
|
|
175
|
-
child.stdin.end();
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function scheduleKill() {
|
|
180
|
-
clearKillTimer();
|
|
181
|
-
killTimer = setTimeout(forceShutdown, 1500);
|
|
182
|
-
killTimer.unref?.();
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function clearKillTimer() {
|
|
186
|
-
if (!killTimer) {
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
clearTimeout(killTimer);
|
|
190
|
-
killTimer = null;
|
|
130
|
+
const entry = { resolve, reject };
|
|
131
|
+
if (!LONG_RUNNING_METHODS.has(method)) {
|
|
132
|
+
entry.timer = setTimeout(() => {
|
|
133
|
+
pending.delete(id);
|
|
134
|
+
reject(new Error(`Runtime request timed out after ${REQUEST_TIMEOUT_MS / 1000}s: ${method}`));
|
|
135
|
+
}, REQUEST_TIMEOUT_MS);
|
|
136
|
+
entry.timer.unref?.();
|
|
137
|
+
}
|
|
138
|
+
pending.set(id, entry);
|
|
139
|
+
child.stdin.write(JSON.stringify(createRuntimeRequest(id, method, params)) + "\n", (error) => {
|
|
140
|
+
if (!error) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
pending.delete(id);
|
|
144
|
+
clearRequestTimer(entry);
|
|
145
|
+
reject(error);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
191
148
|
}
|
|
192
149
|
|
|
193
|
-
|
|
194
|
-
|
|
150
|
+
function receive(line) {
|
|
151
|
+
let message;
|
|
152
|
+
try {
|
|
153
|
+
message = JSON.parse(line);
|
|
154
|
+
} catch {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (isRuntimeResponse(message)) {
|
|
158
|
+
finishRequest(message);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (isRuntimeEvent(message)) {
|
|
162
|
+
handleEvent(message);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function finishRequest(message) {
|
|
167
|
+
const id = runtimeRequestId(message);
|
|
168
|
+
const callbacks = pending.get(id);
|
|
169
|
+
if (!callbacks) {
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
pending.delete(id);
|
|
173
|
+
clearRequestTimer(callbacks);
|
|
174
|
+
if (message.error) {
|
|
175
|
+
callbacks.reject(new Error(message.error.message || "Runtime request failed"));
|
|
176
|
+
} else {
|
|
177
|
+
callbacks.resolve(message.result);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function clearRequestTimer(entry) {
|
|
182
|
+
if (entry.timer) {
|
|
183
|
+
clearTimeout(entry.timer);
|
|
184
|
+
entry.timer = null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function shutdown() {
|
|
189
|
+
if (closing) {
|
|
190
|
+
return Promise.resolve();
|
|
191
|
+
}
|
|
192
|
+
closing = true;
|
|
193
|
+
if (!child) {
|
|
194
|
+
return Promise.resolve();
|
|
195
|
+
}
|
|
196
|
+
scheduleKill();
|
|
197
|
+
return request(runtimeMethods.shutdown).catch(() => {
|
|
198
|
+
forceShutdown();
|
|
199
|
+
}).finally(() => {
|
|
200
|
+
if (child?.stdin.writable) {
|
|
201
|
+
child.stdin.end();
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function forceShutdown() {
|
|
207
|
+
closing = true;
|
|
208
|
+
clearKillTimer();
|
|
209
|
+
if (child && !child.killed && child.exitCode === null) {
|
|
210
|
+
try {
|
|
211
|
+
child.kill("SIGKILL");
|
|
212
|
+
} catch {
|
|
213
|
+
// Ignore kill races during shutdown.
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function closeInput() {
|
|
219
|
+
if (child?.stdin.writable) {
|
|
220
|
+
child.stdin.end();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function scheduleKill() {
|
|
225
|
+
clearKillTimer();
|
|
226
|
+
killTimer = setTimeout(forceShutdown, 1500);
|
|
227
|
+
killTimer.unref?.();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function clearKillTimer() {
|
|
231
|
+
if (!killTimer) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
clearTimeout(killTimer);
|
|
235
|
+
killTimer = null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
get child() {
|
|
240
|
+
return child;
|
|
241
|
+
},
|
|
242
|
+
start,
|
|
195
243
|
request,
|
|
196
|
-
shutdown,
|
|
197
|
-
forceShutdown,
|
|
198
|
-
closeInput,
|
|
199
|
-
isClosing: () => closing,
|
|
200
|
-
};
|
|
201
|
-
}
|
|
244
|
+
shutdown,
|
|
245
|
+
forceShutdown,
|
|
246
|
+
closeInput,
|
|
247
|
+
isClosing: () => closing,
|
|
248
|
+
};
|
|
249
|
+
}
|
package/lib/runtime-env.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
|
|
3
|
-
export function buildRuntimeEnv(repoRoot, baseEnv = process.env, { sourceRuntime = true, rindHome } = {}) {
|
|
4
|
-
const env = { ...baseEnv };
|
|
5
|
-
if (rindHome) {
|
|
6
|
-
env.RIND_HOME = rindHome;
|
|
7
|
-
}
|
|
8
|
-
if (!sourceRuntime) {
|
|
9
|
-
return env;
|
|
10
|
-
}
|
|
11
|
-
return {
|
|
12
|
-
...env,
|
|
13
|
-
PYTHONIOENCODING: "utf-8",
|
|
14
|
-
PYTHONPATH: prependPath(repoRoot, baseEnv.PYTHONPATH),
|
|
15
|
-
PYTHONUTF8: "1",
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function prependPath(entry, value) {
|
|
20
|
-
return value ? `${entry}${path.delimiter}${value}` : entry;
|
|
21
|
-
}
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
export function buildRuntimeEnv(repoRoot, baseEnv = process.env, { sourceRuntime = true, rindHome } = {}) {
|
|
4
|
+
const env = { ...baseEnv };
|
|
5
|
+
if (rindHome) {
|
|
6
|
+
env.RIND_HOME = rindHome;
|
|
7
|
+
}
|
|
8
|
+
if (!sourceRuntime) {
|
|
9
|
+
return env;
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
...env,
|
|
13
|
+
PYTHONIOENCODING: "utf-8",
|
|
14
|
+
PYTHONPATH: prependPath(repoRoot, baseEnv.PYTHONPATH),
|
|
15
|
+
PYTHONUTF8: "1",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function prependPath(entry, value) {
|
|
20
|
+
return value ? `${entry}${path.delimiter}${value}` : entry;
|
|
21
|
+
}
|
package/lib/runtime-protocol.js
CHANGED
|
@@ -1,15 +1,122 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
export const runtimeProtocolVersion = "2";
|
|
2
|
+
|
|
3
|
+
export const REASONING_EFFORTS = Object.freeze(["low", "medium", "high", "xhigh", "max"]);
|
|
4
|
+
|
|
5
|
+
export const runtimeMethods = Object.freeze({
|
|
6
|
+
initialize: "initialize",
|
|
7
|
+
shutdown: "shutdown",
|
|
8
|
+
sessionNew: "session/new",
|
|
9
|
+
sessionList: "session/list",
|
|
10
|
+
sessionSwitch: "session/switch",
|
|
11
|
+
sessionReplay: "session/replay",
|
|
12
|
+
sessionPrompt: "session/prompt",
|
|
13
|
+
sessionCancel: "session/cancel",
|
|
14
|
+
modelList: "model/list",
|
|
15
|
+
modelSet: "model/set",
|
|
16
|
+
modelEffortSet: "model/effort",
|
|
17
|
+
sessionSteer: "rind/session/steer",
|
|
18
|
+
sessionFollowUp: "rind/session/follow_up",
|
|
19
|
+
sessionPromoteFollowUp: "rind/session/promote_follow_up",
|
|
20
|
+
sessionUnsteer: "rind/session/unsteer",
|
|
21
|
+
sessionDequeueFollowUp: "rind/session/dequeue_follow_up",
|
|
22
|
+
sessionCompact: "rind/session/compact",
|
|
23
|
+
commandExecute: "rind/command/execute",
|
|
24
|
+
userQuestionRespond: "rind/user-question/respond",
|
|
25
|
+
backgroundList: "rind/background/list",
|
|
26
|
+
backgroundOutput: "rind/background/output",
|
|
27
|
+
goalGet: "rind/goal/get",
|
|
28
|
+
goalSet: "rind/goal/set",
|
|
29
|
+
goalStatus: "rind/goal/status",
|
|
30
|
+
goalClear: "rind/goal/clear",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
export const sessionScopedMethods = new Set([
|
|
34
|
+
runtimeMethods.sessionPrompt,
|
|
35
|
+
runtimeMethods.sessionReplay,
|
|
36
|
+
runtimeMethods.sessionSwitch,
|
|
37
|
+
runtimeMethods.sessionCancel,
|
|
38
|
+
runtimeMethods.modelSet,
|
|
39
|
+
runtimeMethods.modelEffortSet,
|
|
40
|
+
runtimeMethods.sessionSteer,
|
|
41
|
+
runtimeMethods.sessionFollowUp,
|
|
42
|
+
runtimeMethods.sessionPromoteFollowUp,
|
|
43
|
+
runtimeMethods.sessionUnsteer,
|
|
44
|
+
runtimeMethods.sessionDequeueFollowUp,
|
|
45
|
+
runtimeMethods.sessionCompact,
|
|
46
|
+
runtimeMethods.commandExecute,
|
|
47
|
+
runtimeMethods.userQuestionRespond,
|
|
48
|
+
runtimeMethods.backgroundList,
|
|
49
|
+
runtimeMethods.backgroundOutput,
|
|
50
|
+
runtimeMethods.goalGet,
|
|
51
|
+
runtimeMethods.goalSet,
|
|
52
|
+
runtimeMethods.goalStatus,
|
|
53
|
+
runtimeMethods.goalClear,
|
|
54
|
+
]);
|
|
55
|
+
|
|
56
|
+
export const turnScopedMethods = new Set([
|
|
57
|
+
runtimeMethods.sessionCancel,
|
|
58
|
+
runtimeMethods.sessionSteer,
|
|
59
|
+
]);
|
|
60
|
+
|
|
61
|
+
export function createRuntimeRequest(requestId, method, params = {}) {
|
|
62
|
+
return { kind: "request", request_id: requestId, method, params };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function isRuntimeResponse(message) {
|
|
66
|
+
return message?.kind === "response"
|
|
67
|
+
&& isRequestId(message.request_id)
|
|
68
|
+
&& (Object.hasOwn(message, "result") || isRuntimeError(message.error));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function isRuntimeEvent(message) {
|
|
72
|
+
return message?.kind === "event"
|
|
73
|
+
&& message.method === "session/update"
|
|
74
|
+
&& Number.isInteger(message.sequence)
|
|
75
|
+
&& (message.durability === "durable" || message.durability === "incremental")
|
|
76
|
+
&& typeof message.session_id === "string"
|
|
77
|
+
&& typeof message.turn_id === "string"
|
|
78
|
+
&& isRecord(message.event);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function requireRuntimeInitialization(result) {
|
|
82
|
+
if (!isRecord(result) || result.protocol_version !== runtimeProtocolVersion) {
|
|
83
|
+
const received = isRecord(result) ? String(result.protocol_version || "missing") : "invalid";
|
|
84
|
+
throw new Error(`Unsupported Runtime protocol version: ${received}.`);
|
|
85
|
+
}
|
|
86
|
+
if (!Array.isArray(result.capabilities) || !Array.isArray(result.methods)) {
|
|
87
|
+
throw new Error("Runtime initialization response is missing capabilities or methods.");
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function runtimeRequestId(message) {
|
|
93
|
+
return message?.request_id;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export function runtimeEventType(message) {
|
|
97
|
+
return message?.event?.type || "";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function isRuntimeEventForTurn(message, sessionId = "", turnId = "") {
|
|
101
|
+
const eventSessionId = String(message?.session_id || "");
|
|
102
|
+
if (sessionId && eventSessionId && eventSessionId !== sessionId) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (runtimeEventType(message) === "turn_started") {
|
|
106
|
+
return Boolean(String(message?.turn_id || ""));
|
|
107
|
+
}
|
|
108
|
+
const eventTurnId = String(message?.turn_id || "");
|
|
109
|
+
return !eventTurnId || Boolean(turnId && eventTurnId === turnId);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function isRequestId(value) {
|
|
113
|
+
return (typeof value === "string" && value.trim()) || (typeof value === "number" && Number.isFinite(value));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function isRuntimeError(value) {
|
|
117
|
+
return isRecord(value) && typeof value.type === "string" && typeof value.message === "string";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function isRecord(value) {
|
|
121
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
122
|
+
}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
export function isReadonlySlashCommand(value) {
|
|
2
|
-
const text = String(value || "").trim().toLowerCase();
|
|
3
|
-
return text === "/status" || text.startsWith("/status ") || text === "/doctor" || text.startsWith("/doctor ");
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
export function steeringCommandText(value) {
|
|
7
|
-
const text = String(value || "").trim();
|
|
8
|
-
const match = text.match(/^\/steer(?:\s+([\s\S]*))?$/i);
|
|
9
|
-
return match ? String(match[1] || "").trim() : null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
1
|
export function parseGoalCommand(value) {
|
|
13
2
|
const text = String(value || "").trim();
|
|
14
3
|
const match = text.match(/^\/goal(?:\s+([\s\S]*))?$/i);
|