@rynx-ai/runtime 0.1.11-beta.4 → 0.1.11-beta.40
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/claude/executor.d.ts +19 -5
- package/dist/claude/executor.js +56 -12
- package/dist/claude/models.d.ts +0 -5
- package/dist/claude/models.js +1 -7
- package/dist/claude/native-bridge.d.ts +103 -1
- package/dist/claude/native-bridge.js +445 -30
- package/dist/claude/native-hook-main.js +81 -1
- package/dist/claude/native-hooks.js +7 -0
- package/dist/claude/native-integration.d.ts +178 -26
- package/dist/claude/native-integration.js +1528 -170
- package/dist/claude/session-status.d.ts +39 -0
- package/dist/claude/session-status.js +163 -0
- package/dist/claude/transcript-clone.d.ts +18 -0
- package/dist/claude/transcript-clone.js +497 -0
- package/dist/claude/transcript.d.ts +27 -4
- package/dist/claude/transcript.js +158 -47
- package/dist/codex-app-server/client.d.ts +10 -6
- package/dist/codex-app-server/client.js +67 -15
- package/dist/codex-app-server/forwarder.d.ts +92 -3
- package/dist/codex-app-server/forwarder.js +532 -57
- package/dist/codex-app-server/mapping.d.ts +3 -6
- package/dist/codex-app-server/mapping.js +206 -36
- package/dist/codex-app-server/mcp-startup.d.ts +13 -0
- package/dist/codex-app-server/mcp-startup.js +63 -0
- package/dist/codex-app-server/process-registry.d.ts +36 -0
- package/dist/codex-app-server/process-registry.js +320 -0
- package/dist/codex-app-server/protocol.d.ts +64 -7
- package/dist/codex-app-server/ws-channel.d.ts +7 -0
- package/dist/codex-app-server/ws-channel.js +104 -28
- package/dist/codex-home.d.ts +35 -3
- package/dist/codex-home.js +323 -18
- package/dist/codex-session-store.d.ts +23 -0
- package/dist/codex-session-store.js +21 -0
- package/dist/host.d.ts +103 -46
- package/dist/host.js +1988 -634
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -1
- package/dist/input-resources.d.ts +4 -0
- package/dist/input-resources.js +21 -5
- package/dist/models-catalog.d.ts +2 -1
- package/dist/models-catalog.js +94 -6
- package/dist/runner/child.d.ts +97 -28
- package/dist/runner/child.js +1486 -100
- package/dist/runner/manager.d.ts +110 -29
- package/dist/runner/manager.js +1481 -246
- package/dist/runner/protocol.d.ts +212 -24
- package/dist/runner/protocol.js +5 -0
- package/dist/runner/startup-policy.d.ts +7 -0
- package/dist/runner/startup-policy.js +10 -0
- package/dist/runner/transport.d.ts +18 -2
- package/dist/runner/transport.js +82 -3
- package/dist/runner-main.js +8 -3
- package/dist/terminal/claude-tui.d.ts +3 -1
- package/dist/terminal/claude-tui.js +3 -1
- package/dist/terminal/codex-tui.d.ts +4 -0
- package/dist/terminal/codex-tui.js +5 -0
- package/dist/terminal/control-parser.d.ts +39 -0
- package/dist/terminal/control-parser.js +172 -0
- package/dist/terminal/registry.d.ts +18 -15
- package/dist/terminal/registry.js +44 -23
- package/dist/terminal/spool.d.ts +47 -0
- package/dist/terminal/spool.js +231 -0
- package/dist/terminal/tmux.d.ts +126 -74
- package/dist/terminal/tmux.js +807 -211
- package/package.json +4 -4
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync, } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
import { randomUUID } from "node:crypto";
|
|
5
|
+
import { rynxRuntimeDir } from "@rynx-ai/core";
|
|
6
|
+
const REGISTRY_FILE = "process-registry.json";
|
|
7
|
+
const TAG_PREFIX = "rynx_crash_teardown_tag=";
|
|
8
|
+
const STATE_PREFIX = "rynx_crash_teardown_state_dir=";
|
|
9
|
+
export function runtimeProcessRegistryPath() {
|
|
10
|
+
return join(rynxRuntimeDir(), REGISTRY_FILE);
|
|
11
|
+
}
|
|
12
|
+
export function runtimeProcessTagArg(sessionTag) {
|
|
13
|
+
if (!sessionTag)
|
|
14
|
+
throw new Error("sessionTag must be non-empty");
|
|
15
|
+
return `${TAG_PREFIX}${sessionTag}`;
|
|
16
|
+
}
|
|
17
|
+
export function runtimeProcessArgv0(cliPath, sessionTag) {
|
|
18
|
+
return `${basename(cliPath)} ${runtimeProcessTagArg(sessionTag)}`;
|
|
19
|
+
}
|
|
20
|
+
export function withRuntimeProcessStateArg(baseArgs, stateDir) {
|
|
21
|
+
const args = [...baseArgs];
|
|
22
|
+
const marker = ["-c", `${STATE_PREFIX}${JSON.stringify(stateDir)}`];
|
|
23
|
+
const subcommand = args.indexOf("app-server");
|
|
24
|
+
args.splice(subcommand >= 0 ? subcommand : 0, 0, ...marker);
|
|
25
|
+
return args;
|
|
26
|
+
}
|
|
27
|
+
export function registerRuntimeProcess(entry, registryPath = runtimeProcessRegistryPath()) {
|
|
28
|
+
if (!validEntry(entry))
|
|
29
|
+
return;
|
|
30
|
+
mutateRegistry(registryPath, (entries) => [
|
|
31
|
+
...entries.filter((candidate) => candidate.sessionTag !== entry.sessionTag),
|
|
32
|
+
entry,
|
|
33
|
+
]);
|
|
34
|
+
}
|
|
35
|
+
export function unregisterRuntimeProcess(sessionTag, registryPath = runtimeProcessRegistryPath()) {
|
|
36
|
+
if (!sessionTag)
|
|
37
|
+
return;
|
|
38
|
+
mutateRegistry(registryPath, (entries) => entries.filter((entry) => entry.sessionTag !== sessionTag));
|
|
39
|
+
}
|
|
40
|
+
export function reconcileRuntimeProcesses(opts = {}) {
|
|
41
|
+
return reconcileEntries(undefined, opts);
|
|
42
|
+
}
|
|
43
|
+
export function reapRuntimeProcessesForStateDir(stateDir, opts = {}) {
|
|
44
|
+
if (process.platform === "win32" && !opts.processListing)
|
|
45
|
+
return 0;
|
|
46
|
+
const listing = opts.processListing?.() ?? listProcesses();
|
|
47
|
+
if (!listing)
|
|
48
|
+
return 0;
|
|
49
|
+
const currentPgid = opts.currentPgid?.() ?? processGroupId(process.pid);
|
|
50
|
+
const signalGroup = opts.signalGroup ?? signalProcessGroup;
|
|
51
|
+
const childAlive = opts.childAlive ?? isProcessAlive;
|
|
52
|
+
const graceMs = opts.graceMs ?? 1_500;
|
|
53
|
+
const victims = new Map();
|
|
54
|
+
for (const line of listing.split("\n")) {
|
|
55
|
+
const match = /^\s*(\d+)\s+(\d+)\s+([\s\S]+)$/.exec(line);
|
|
56
|
+
if (!match)
|
|
57
|
+
continue;
|
|
58
|
+
const pid = Number.parseInt(match[1], 10);
|
|
59
|
+
const pgid = Number.parseInt(match[2], 10);
|
|
60
|
+
const command = match[3];
|
|
61
|
+
if (!command.includes(stateDir) || !command.includes("app-server"))
|
|
62
|
+
continue;
|
|
63
|
+
if (pid === process.pid || pgid <= 0 || pgid === currentPgid)
|
|
64
|
+
continue;
|
|
65
|
+
victims.set(pid, pgid);
|
|
66
|
+
}
|
|
67
|
+
if (victims.size === 0)
|
|
68
|
+
return 0;
|
|
69
|
+
for (const pgid of new Set(victims.values()))
|
|
70
|
+
signalGroup(pgid, "SIGTERM");
|
|
71
|
+
const deadline = Date.now() + graceMs;
|
|
72
|
+
while (Date.now() < deadline && [...victims.keys()].some(childAlive)) {
|
|
73
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
|
|
74
|
+
}
|
|
75
|
+
for (const [pid, pgid] of victims) {
|
|
76
|
+
if (childAlive(pid))
|
|
77
|
+
signalGroup(pgid, "SIGKILL");
|
|
78
|
+
}
|
|
79
|
+
return victims.size;
|
|
80
|
+
}
|
|
81
|
+
function reconcileEntries(stateDir, opts) {
|
|
82
|
+
const registryPath = opts.registryPath ?? runtimeProcessRegistryPath();
|
|
83
|
+
const ownerAlive = opts.ownerAlive ?? isProcessAlive;
|
|
84
|
+
const ownerIdentity = opts.ownerIdentity ?? runtimeProcessOwnerIdentity;
|
|
85
|
+
const childAlive = opts.childAlive ?? isProcessAlive;
|
|
86
|
+
const commandLine = opts.commandLine ?? processCommandLine;
|
|
87
|
+
const terminateGroup = opts.terminateGroup ?? terminateProcessGroup;
|
|
88
|
+
let reaped = 0;
|
|
89
|
+
mutateRegistry(registryPath, (entries) => {
|
|
90
|
+
const survivors = [];
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
if (stateDir !== undefined && entry.stateDir !== stateDir) {
|
|
93
|
+
survivors.push(entry);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if (ownerAlive(entry.ownerPid)) {
|
|
97
|
+
const currentOwnerIdentity = ownerIdentity(entry.ownerPid);
|
|
98
|
+
// Old registry entries have no start identity. Keep the conservative
|
|
99
|
+
// PID-only behavior for those entries; every newly registered process
|
|
100
|
+
// is protected against PID reuse.
|
|
101
|
+
if (!entry.ownerIdentity ||
|
|
102
|
+
!currentOwnerIdentity ||
|
|
103
|
+
currentOwnerIdentity === entry.ownerIdentity) {
|
|
104
|
+
survivors.push(entry);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (!childAlive(entry.pid))
|
|
109
|
+
continue;
|
|
110
|
+
if (!commandLine(entry.pid).includes(runtimeProcessTagArg(entry.sessionTag)))
|
|
111
|
+
continue;
|
|
112
|
+
if (!terminateGroup(entry.pgid)) {
|
|
113
|
+
survivors.push(entry);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
reaped += 1;
|
|
117
|
+
}
|
|
118
|
+
return survivors;
|
|
119
|
+
});
|
|
120
|
+
return reaped;
|
|
121
|
+
}
|
|
122
|
+
function validEntry(entry) {
|
|
123
|
+
return Number.isSafeInteger(entry.pid) && entry.pid > 0 &&
|
|
124
|
+
Number.isSafeInteger(entry.pgid) && entry.pgid > 0 &&
|
|
125
|
+
Number.isSafeInteger(entry.ownerPid) && entry.ownerPid > 0 &&
|
|
126
|
+
(entry.ownerIdentity === undefined ||
|
|
127
|
+
(typeof entry.ownerIdentity === "string" && entry.ownerIdentity.length > 0)) &&
|
|
128
|
+
Boolean(entry.sessionTag) && Boolean(entry.stateDir);
|
|
129
|
+
}
|
|
130
|
+
function readRegistry(path) {
|
|
131
|
+
let value;
|
|
132
|
+
try {
|
|
133
|
+
value = JSON.parse(readFileSync(path, "utf8"));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
if (!Array.isArray(value))
|
|
139
|
+
return [];
|
|
140
|
+
return value.filter((entry) => typeof entry === "object" && entry !== null && validEntry(entry));
|
|
141
|
+
}
|
|
142
|
+
function mutateRegistry(path, mutation) {
|
|
143
|
+
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
144
|
+
const lockDir = `${path}.lock`;
|
|
145
|
+
const deadline = Date.now() + 2_000;
|
|
146
|
+
while (true) {
|
|
147
|
+
try {
|
|
148
|
+
mkdirSync(lockDir, { mode: 0o700 });
|
|
149
|
+
writeFileSync(join(lockDir, "owner.pid"), JSON.stringify({
|
|
150
|
+
pid: process.pid,
|
|
151
|
+
identity: runtimeProcessOwnerIdentity(process.pid),
|
|
152
|
+
}), { mode: 0o600 });
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
if (error.code !== "EEXIST")
|
|
157
|
+
return;
|
|
158
|
+
let ownerPid = 0;
|
|
159
|
+
let ownerIdentity = "";
|
|
160
|
+
try {
|
|
161
|
+
const rawOwner = readFileSync(join(lockDir, "owner.pid"), "utf8");
|
|
162
|
+
try {
|
|
163
|
+
const parsed = JSON.parse(rawOwner);
|
|
164
|
+
ownerPid = typeof parsed.pid === "number" ? parsed.pid : 0;
|
|
165
|
+
ownerIdentity = typeof parsed.identity === "string" ? parsed.identity : "";
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// Backward-compatible recovery for a lock written by the earlier
|
|
169
|
+
// PID-only implementation.
|
|
170
|
+
ownerPid = Number.parseInt(rawOwner, 10);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// A creator may be between mkdir and marker write; the bounded retry handles it.
|
|
175
|
+
}
|
|
176
|
+
const currentOwnerIdentity = ownerPid > 0
|
|
177
|
+
? runtimeProcessOwnerIdentity(ownerPid)
|
|
178
|
+
: "";
|
|
179
|
+
const staleKnownOwner = ownerPid > 0 && (!isProcessAlive(ownerPid) ||
|
|
180
|
+
Boolean(ownerIdentity && currentOwnerIdentity && ownerIdentity !== currentOwnerIdentity));
|
|
181
|
+
let staleUnownedLock = false;
|
|
182
|
+
if (ownerPid <= 0) {
|
|
183
|
+
try {
|
|
184
|
+
staleUnownedLock = Date.now() - statSync(lockDir).mtimeMs >= 1_000;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
staleUnownedLock = true;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (staleKnownOwner || staleUnownedLock) {
|
|
191
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
if (Date.now() >= deadline)
|
|
195
|
+
return;
|
|
196
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 10);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
try {
|
|
200
|
+
const entries = mutation(readRegistry(path));
|
|
201
|
+
const temp = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
202
|
+
writeFileSync(temp, `${JSON.stringify(entries)}\n`, { mode: 0o600 });
|
|
203
|
+
renameSync(temp, path);
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
rmSync(lockDir, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
function isProcessAlive(pid) {
|
|
210
|
+
try {
|
|
211
|
+
process.kill(pid, 0);
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
return error.code === "EPERM";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/** Return an OS process-start identity used to distinguish PID reuse. */
|
|
219
|
+
export function runtimeProcessOwnerIdentity(pid) {
|
|
220
|
+
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
221
|
+
return "";
|
|
222
|
+
if (process.platform === "linux") {
|
|
223
|
+
try {
|
|
224
|
+
const stat = readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
225
|
+
const commandEnd = stat.lastIndexOf(")");
|
|
226
|
+
if (commandEnd >= 0) {
|
|
227
|
+
// Fields after ')' begin at proc field 3; starttime is field 22.
|
|
228
|
+
const startTicks = stat.slice(commandEnd + 2).trim().split(/\s+/)[19];
|
|
229
|
+
if (startTicks)
|
|
230
|
+
return `linux:${startTicks}`;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return "";
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const startedAt = execFileSync("ps", ["-p", String(pid), "-ww", "-o", "lstart="], {
|
|
239
|
+
encoding: "utf8",
|
|
240
|
+
timeout: 2_000,
|
|
241
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
242
|
+
}).trim();
|
|
243
|
+
return startedAt ? `${process.platform}:${startedAt}` : "";
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
return "";
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
function processCommandLine(pid) {
|
|
250
|
+
if (process.platform === "linux") {
|
|
251
|
+
try {
|
|
252
|
+
return readFileSync(`/proc/${pid}/cmdline`)
|
|
253
|
+
.toString("utf8")
|
|
254
|
+
.replaceAll("\0", " ");
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
// Fall through to ps for macOS and restricted procfs environments.
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
return execFileSync("ps", ["-p", String(pid), "-ww", "-o", "command="], {
|
|
262
|
+
encoding: "utf8",
|
|
263
|
+
timeout: 2_000,
|
|
264
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
265
|
+
}).trim();
|
|
266
|
+
}
|
|
267
|
+
catch {
|
|
268
|
+
return "";
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
function listProcesses() {
|
|
272
|
+
try {
|
|
273
|
+
return execFileSync("ps", ["-axww", "-o", "pid=,pgid=,command="], {
|
|
274
|
+
encoding: "utf8",
|
|
275
|
+
timeout: 5_000,
|
|
276
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function processGroupId(pid) {
|
|
284
|
+
try {
|
|
285
|
+
const value = execFileSync("ps", ["-p", String(pid), "-o", "pgid="], {
|
|
286
|
+
encoding: "utf8",
|
|
287
|
+
timeout: 2_000,
|
|
288
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
289
|
+
});
|
|
290
|
+
return Number.parseInt(value.trim(), 10) || -1;
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
return -1;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
function signalProcessGroup(pgid, signal) {
|
|
297
|
+
if (process.platform === "win32")
|
|
298
|
+
return false;
|
|
299
|
+
try {
|
|
300
|
+
process.kill(-pgid, signal);
|
|
301
|
+
return true;
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
return error.code === "ESRCH";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
function terminateProcessGroup(pgid) {
|
|
308
|
+
if (process.platform === "win32")
|
|
309
|
+
return false;
|
|
310
|
+
try {
|
|
311
|
+
process.kill(-pgid, "SIGTERM");
|
|
312
|
+
return true;
|
|
313
|
+
}
|
|
314
|
+
catch (error) {
|
|
315
|
+
return error.code === "ESRCH";
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
export function newRuntimeProcessTag() {
|
|
319
|
+
return `native-${randomUUID().replaceAll("-", "")}`;
|
|
320
|
+
}
|
|
@@ -56,6 +56,21 @@ export interface GetAuthStatusResponse {
|
|
|
56
56
|
authToken: string | null;
|
|
57
57
|
requiresOpenaiAuth: boolean;
|
|
58
58
|
}
|
|
59
|
+
/** Read the Provider's effective configuration through the app-server rather
|
|
60
|
+
* than parsing a runtime-specific config file (Traex has used both YAML and
|
|
61
|
+
* TOML across releases). `cwd` includes project-scoped configuration layers. */
|
|
62
|
+
export interface ConfigReadParams {
|
|
63
|
+
includeLayers: boolean;
|
|
64
|
+
cwd?: string | null;
|
|
65
|
+
}
|
|
66
|
+
export interface ConfigReadResponse {
|
|
67
|
+
config: {
|
|
68
|
+
model: string | null;
|
|
69
|
+
model_reasoning_effort: ReasoningEffort | null;
|
|
70
|
+
[key: string]: unknown;
|
|
71
|
+
};
|
|
72
|
+
[key: string]: unknown;
|
|
73
|
+
}
|
|
59
74
|
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | "never";
|
|
60
75
|
export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
|
61
76
|
export type SandboxPolicy = {
|
|
@@ -87,6 +102,16 @@ export interface PermissionProfileModificationParams {
|
|
|
87
102
|
export type PermissionSelection = string | PermissionProfileSelectionParams;
|
|
88
103
|
/** Open since Codex App Server 0.144; values are advertised by `model/list`. */
|
|
89
104
|
export type ReasoningEffort = string;
|
|
105
|
+
export type CollaborationModeKind = "plan" | "default";
|
|
106
|
+
/** Full Codex-lineage mode snapshot required by the App Server wire protocol. */
|
|
107
|
+
export interface CollaborationMode {
|
|
108
|
+
mode: CollaborationModeKind;
|
|
109
|
+
settings: {
|
|
110
|
+
model: string;
|
|
111
|
+
reasoning_effort: ReasoningEffort | null;
|
|
112
|
+
developer_instructions: string | null;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
90
115
|
export interface ThreadStartParams {
|
|
91
116
|
model?: string | null;
|
|
92
117
|
modelProvider?: string | null;
|
|
@@ -102,14 +127,18 @@ export interface ThreadStartParams {
|
|
|
102
127
|
baseInstructions?: string | null;
|
|
103
128
|
developerInstructions?: string | null;
|
|
104
129
|
ephemeral?: boolean | null;
|
|
130
|
+
/** Native UI intent for a context-clearing fresh thread. */
|
|
131
|
+
sessionStartSource?: "clear" | string;
|
|
105
132
|
}
|
|
106
133
|
export interface ThreadResumeParams extends ThreadStartParams {
|
|
107
134
|
threadId: string;
|
|
108
|
-
/**
|
|
109
|
-
* SUBSCRIBE without re-replaying history). When false/absent, the response
|
|
110
|
-
* carries `thread.turns[].items[]` — the backfill the forwarder replays for a
|
|
111
|
-
* fresh thread's first turn (reference implementation's `_replay_resume_response`). */
|
|
135
|
+
/** Suppress rollout history when the caller only needs to load/subscribe. */
|
|
112
136
|
excludeTurns?: boolean;
|
|
137
|
+
initialTurnsPage?: {
|
|
138
|
+
limit?: number | null;
|
|
139
|
+
sortDirection?: "asc" | "desc" | null;
|
|
140
|
+
itemsView?: "notLoaded" | "summary" | "full" | null;
|
|
141
|
+
} | null;
|
|
113
142
|
}
|
|
114
143
|
/** One turn in a resumed thread's backlog (`thread/resume` response). */
|
|
115
144
|
export interface ResumedTurn {
|
|
@@ -125,6 +154,13 @@ export interface ResumedThread {
|
|
|
125
154
|
turns?: ResumedTurn[];
|
|
126
155
|
[key: string]: unknown;
|
|
127
156
|
}
|
|
157
|
+
/** Settings selected by the Provider while starting or resuming a thread.
|
|
158
|
+
* Older app-server builds may omit them, so the bridge treats them as optional
|
|
159
|
+
* and falls back to `config/read` only for a fresh, not-yet-created thread. */
|
|
160
|
+
export interface ThreadRuntimeSettings {
|
|
161
|
+
model?: string;
|
|
162
|
+
reasoningEffort?: ReasoningEffort | null;
|
|
163
|
+
}
|
|
128
164
|
export interface ThreadDescriptor {
|
|
129
165
|
id: string;
|
|
130
166
|
cwd: string;
|
|
@@ -155,6 +191,7 @@ export interface TurnStartParams {
|
|
|
155
191
|
permissions?: PermissionSelection | null;
|
|
156
192
|
model?: string | null;
|
|
157
193
|
effort?: ReasoningEffort | null;
|
|
194
|
+
collaborationMode?: CollaborationMode | null;
|
|
158
195
|
}
|
|
159
196
|
export interface TurnInterruptParams {
|
|
160
197
|
threadId: string;
|
|
@@ -169,7 +206,7 @@ export interface TurnSteerParams {
|
|
|
169
206
|
expectedTurnId: string;
|
|
170
207
|
input: UserInput[];
|
|
171
208
|
}
|
|
172
|
-
export type TurnStatus = "completed" | "interrupted" | "failed" | "inProgress";
|
|
209
|
+
export type TurnStatus = "completed" | "interrupted" | "cancelled" | "canceled" | "failed" | "errored" | "inProgress";
|
|
173
210
|
export interface TurnPlanStep {
|
|
174
211
|
step: string;
|
|
175
212
|
status: "pending" | "inProgress" | "completed";
|
|
@@ -252,7 +289,16 @@ export interface WebSearchItem extends ThreadItemBase {
|
|
|
252
289
|
type: "other";
|
|
253
290
|
} | null;
|
|
254
291
|
}
|
|
255
|
-
export
|
|
292
|
+
export interface ImageGenerationItem extends ThreadItemBase {
|
|
293
|
+
type: "imageGeneration";
|
|
294
|
+
status: string;
|
|
295
|
+
revisedPrompt: string | null;
|
|
296
|
+
/** Base64 image bytes supplied when no durable saved path is available. */
|
|
297
|
+
result: string;
|
|
298
|
+
/** Codex App Server guarantees this is absolute when present. */
|
|
299
|
+
savedPath?: string;
|
|
300
|
+
}
|
|
301
|
+
export type ThreadItem = UserMessageItem | AgentMessageItem | ReasoningItem | PlanItem | CommandExecutionItem | FileChangeItem | McpToolCallItem | DynamicToolCallItem | WebSearchItem | ImageGenerationItem | (ThreadItemBase & Record<string, unknown>);
|
|
256
302
|
export interface ThreadSummary {
|
|
257
303
|
id?: string;
|
|
258
304
|
threadId?: string;
|
|
@@ -302,7 +348,7 @@ export interface ThreadSettingsUpdateParams {
|
|
|
302
348
|
serviceTier?: string | null;
|
|
303
349
|
effort?: ReasoningEffort | null;
|
|
304
350
|
summary?: string | null;
|
|
305
|
-
collaborationMode?:
|
|
351
|
+
collaborationMode?: CollaborationMode | null;
|
|
306
352
|
personality?: string | null;
|
|
307
353
|
}
|
|
308
354
|
export interface ModelListParams {
|
|
@@ -476,6 +522,14 @@ export interface QueueStatusNotificationParams {
|
|
|
476
522
|
export type ServerNotification = {
|
|
477
523
|
method: "thread/started";
|
|
478
524
|
params: ThreadStartedNotificationParams;
|
|
525
|
+
} | {
|
|
526
|
+
method: "thread/settings/updated";
|
|
527
|
+
params: {
|
|
528
|
+
threadId: string;
|
|
529
|
+
threadSettings: {
|
|
530
|
+
collaborationMode?: CollaborationMode | null;
|
|
531
|
+
} & Record<string, unknown>;
|
|
532
|
+
};
|
|
479
533
|
} | {
|
|
480
534
|
method: "turn/started";
|
|
481
535
|
params: TurnStartedNotificationParams;
|
|
@@ -497,6 +551,9 @@ export type ServerNotification = {
|
|
|
497
551
|
} | {
|
|
498
552
|
method: "item/agentMessage/delta";
|
|
499
553
|
params: AgentMessageDeltaNotificationParams;
|
|
554
|
+
} | {
|
|
555
|
+
method: "item/plan/delta";
|
|
556
|
+
params: AgentMessageDeltaNotificationParams;
|
|
500
557
|
} | {
|
|
501
558
|
method: "item/reasoning/summaryTextDelta";
|
|
502
559
|
params: ReasoningSummaryTextDeltaNotificationParams;
|
|
@@ -8,6 +8,8 @@ export interface WsRpcChannelOptions {
|
|
|
8
8
|
logger?: TransportLogger;
|
|
9
9
|
/** Ms to wait for the app-server to accept a connection. */
|
|
10
10
|
readyTimeoutMs?: number;
|
|
11
|
+
/** Durable per-session runtime state used for targeted crash reconciliation. */
|
|
12
|
+
stateDir?: string;
|
|
11
13
|
}
|
|
12
14
|
export declare class WsRpcChannel implements RpcChannel {
|
|
13
15
|
private readonly opts;
|
|
@@ -16,6 +18,8 @@ export declare class WsRpcChannel implements RpcChannel {
|
|
|
16
18
|
private lineCb;
|
|
17
19
|
private closeCb;
|
|
18
20
|
private closedEmitted;
|
|
21
|
+
private processTag;
|
|
22
|
+
private stopTask;
|
|
19
23
|
private readonly readyTimeoutMs;
|
|
20
24
|
/** The `ws://IP:PORT` the app-server listens on — pass to the TUI's `--remote`. */
|
|
21
25
|
url: string;
|
|
@@ -30,6 +34,9 @@ export declare class WsRpcChannel implements RpcChannel {
|
|
|
30
34
|
start(): Promise<void>;
|
|
31
35
|
send(line: string): Promise<void>;
|
|
32
36
|
stop(signal?: NodeJS.Signals): Promise<void>;
|
|
37
|
+
private stopOwnedProcess;
|
|
38
|
+
private signalChild;
|
|
39
|
+
private unregisterProcess;
|
|
33
40
|
private emitClose;
|
|
34
41
|
private connectWithRetry;
|
|
35
42
|
private tryConnect;
|