@rynx-ai/runtime 0.1.11-beta.37 → 0.1.11-beta.39
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/native-bridge.d.ts +85 -0
- package/dist/claude/native-bridge.js +335 -17
- package/dist/claude/native-hook-main.js +18 -1
- package/dist/claude/native-hooks.js +7 -0
- package/dist/claude/native-integration.d.ts +120 -18
- package/dist/claude/native-integration.js +1200 -161
- 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 +131 -30
- package/dist/codex-session-store.d.ts +23 -0
- package/dist/codex-session-store.js +21 -0
- package/dist/host.d.ts +31 -2
- package/dist/host.js +551 -72
- package/dist/runner/child.d.ts +29 -5
- package/dist/runner/child.js +635 -54
- package/dist/runner/manager.d.ts +25 -0
- package/dist/runner/manager.js +805 -115
- package/dist/runner/protocol.d.ts +76 -3
- package/dist/runner/transport.d.ts +9 -0
- package/dist/runner/transport.js +39 -12
- package/package.json +2 -2
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { readFileSync } from "node:fs";
|
|
16
16
|
import { open, stat } from "node:fs/promises";
|
|
17
17
|
import { join } from "node:path";
|
|
18
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
18
19
|
/** A sub-agent (Task) writes its own transcript to
|
|
19
20
|
* `<project>/<sessionId>/subagents/agent-<agentId>.jsonl`, alongside the parent
|
|
20
21
|
* `<sessionId>.jsonl`. Derive that path from the parent transcript path. */
|
|
@@ -60,6 +61,21 @@ function stringifyToolContent(content) {
|
|
|
60
61
|
const BASH_INPUT_RE = /<bash-input>([\s\S]*?)<\/bash-input>/;
|
|
61
62
|
const BASH_STDOUT_RE = /<bash-stdout>([\s\S]*?)<\/bash-stdout>/;
|
|
62
63
|
const BASH_STDERR_RE = /<bash-stderr>([\s\S]*?)<\/bash-stderr>/;
|
|
64
|
+
/** Parse either half of Claude's shell-mode record. Newer and older Claude
|
|
65
|
+
* builds may split `<bash-input>` from the later stdout/stderr record. */
|
|
66
|
+
export function parseTerminalCommandFragments(content) {
|
|
67
|
+
const input = BASH_INPUT_RE.exec(content);
|
|
68
|
+
const stdout = BASH_STDOUT_RE.exec(content);
|
|
69
|
+
const stderr = BASH_STDERR_RE.exec(content);
|
|
70
|
+
if (!input && !stdout && !stderr)
|
|
71
|
+
return undefined;
|
|
72
|
+
return {
|
|
73
|
+
...(input ? { command: input[1].trim() } : {}),
|
|
74
|
+
...(stdout ? { stdout: stdout[1] } : {}),
|
|
75
|
+
...(stderr ? { stderr: stderr[1] } : {}),
|
|
76
|
+
hasOutput: Boolean(stdout || stderr),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
63
79
|
/**
|
|
64
80
|
* Parse a claude local-command (`!` bash mode) user record's string content into
|
|
65
81
|
* a {@link TerminalCommandData}. Claude records the command and its captured
|
|
@@ -69,16 +85,14 @@ const BASH_STDERR_RE = /<bash-stderr>([\s\S]*?)<\/bash-stderr>/;
|
|
|
69
85
|
* `<system-reminder>`) are bookkeeping, not terminal commands.
|
|
70
86
|
*/
|
|
71
87
|
export function parseTerminalCommand(content) {
|
|
72
|
-
const
|
|
73
|
-
if (!
|
|
88
|
+
const fragments = parseTerminalCommandFragments(content);
|
|
89
|
+
if (!fragments || fragments.command === undefined)
|
|
74
90
|
return undefined;
|
|
75
|
-
const data = { command:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (
|
|
79
|
-
data.
|
|
80
|
-
if (stderr)
|
|
81
|
-
data.stderr = stderr;
|
|
91
|
+
const data = { command: fragments.command };
|
|
92
|
+
if (fragments.stdout)
|
|
93
|
+
data.stdout = fragments.stdout;
|
|
94
|
+
if (fragments.stderr)
|
|
95
|
+
data.stderr = fragments.stderr;
|
|
82
96
|
return data;
|
|
83
97
|
}
|
|
84
98
|
/**
|
|
@@ -113,26 +127,42 @@ export function parseTranscriptRecord(record, opts) {
|
|
|
113
127
|
if (rec.isSidechain && !parent)
|
|
114
128
|
return []; // sub-agent turns are replayed separately
|
|
115
129
|
const message = rec.message;
|
|
116
|
-
if (!message
|
|
130
|
+
if (!message)
|
|
117
131
|
return [];
|
|
118
132
|
const out = [];
|
|
119
133
|
if (rec.type === "assistant" && message.role === "assistant") {
|
|
134
|
+
const content = typeof message.content === "string"
|
|
135
|
+
? [{ type: "text", text: message.content }]
|
|
136
|
+
: message.content;
|
|
137
|
+
if (!Array.isArray(content))
|
|
138
|
+
return [];
|
|
120
139
|
// Claude writes this zero-token synthetic filler after an accidental empty
|
|
121
140
|
// submit. It is transcript bookkeeping, not an assistant response. Filter
|
|
122
141
|
// by provider provenance and exact shape here, before normalization loses
|
|
123
142
|
// `model: "<synthetic>"`; a UI string filter could hide legitimate output.
|
|
124
143
|
if (!rec.isApiErrorMessage &&
|
|
125
144
|
message.model === "<synthetic>" &&
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
145
|
+
content.length === 1 &&
|
|
146
|
+
content[0]?.type === "text" &&
|
|
147
|
+
content[0].text === "No response requested.") {
|
|
129
148
|
return [];
|
|
130
149
|
}
|
|
131
150
|
const itemId = message.id;
|
|
132
|
-
const
|
|
133
|
-
for (const block of
|
|
151
|
+
const textBlockCount = content.filter((block) => block.type === "text" && block.text).length;
|
|
152
|
+
for (const [blockIndex, block] of content.entries()) {
|
|
134
153
|
if (block.type === "text" && block.text) {
|
|
135
|
-
|
|
154
|
+
// Preserve Claude's block order. Keep a single text block's
|
|
155
|
+
// native message id for MessageDisplay correlation; only derive an id
|
|
156
|
+
// when one record contains multiple independently ordered text blocks.
|
|
157
|
+
const textItemId = itemId && textBlockCount > 1
|
|
158
|
+
? `${itemId}:text:${blockIndex}`
|
|
159
|
+
: itemId;
|
|
160
|
+
out.push({
|
|
161
|
+
type: "message_completed",
|
|
162
|
+
text: block.text,
|
|
163
|
+
...(textItemId ? { itemId: textItemId } : {}),
|
|
164
|
+
...parentTag,
|
|
165
|
+
});
|
|
136
166
|
}
|
|
137
167
|
else if (block.type === "thinking" && block.thinking) {
|
|
138
168
|
out.push({ type: "reasoning_completed", summary: [block.thinking], ...(itemId ? { itemId } : {}), ...parentTag });
|
|
@@ -143,17 +173,19 @@ export function parseTranscriptRecord(record, opts) {
|
|
|
143
173
|
event: "on_tool_start",
|
|
144
174
|
name: block.name,
|
|
145
175
|
input: { ...(isObject(block.input) ? block.input : {}), id: block.id },
|
|
146
|
-
|
|
176
|
+
// A transcript tool_use is already a completed Provider record. Live
|
|
177
|
+
// runtimes still omit this marker and remain in_progress until their
|
|
178
|
+
// completion event arrives.
|
|
179
|
+
data: { id: block.id, itemStatus: "completed" },
|
|
147
180
|
...parentTag,
|
|
148
181
|
});
|
|
149
182
|
}
|
|
150
183
|
}
|
|
151
|
-
if (texts.length) {
|
|
152
|
-
out.push({ type: "message_completed", text: texts.join(""), ...(itemId ? { itemId } : {}), ...parentTag });
|
|
153
|
-
}
|
|
154
184
|
return out;
|
|
155
185
|
}
|
|
156
186
|
if (rec.type === "user" && message.role === "user") {
|
|
187
|
+
if (!Array.isArray(message.content))
|
|
188
|
+
return [];
|
|
157
189
|
for (const block of message.content) {
|
|
158
190
|
if (block.type === "tool_result") {
|
|
159
191
|
out.push({
|
|
@@ -166,7 +198,7 @@ export function parseTranscriptRecord(record, opts) {
|
|
|
166
198
|
exitCode: null,
|
|
167
199
|
},
|
|
168
200
|
...(block.is_error ? { error: "tool_error" } : {}),
|
|
169
|
-
data: { tool_use_id: block.tool_use_id },
|
|
201
|
+
data: { tool_use_id: block.tool_use_id, itemStatus: "completed" },
|
|
170
202
|
...parentTag,
|
|
171
203
|
});
|
|
172
204
|
}
|
|
@@ -179,13 +211,14 @@ function isObject(value) {
|
|
|
179
211
|
}
|
|
180
212
|
/**
|
|
181
213
|
* Whether a transcript is a `/fork` (branch) of another session: claude stamps a
|
|
182
|
-
* `forkedFrom: { sessionId }` marker
|
|
214
|
+
* `forkedFrom: { sessionId }` marker on copied records pointing at the source
|
|
183
215
|
* session (reference implementation's `transcript_has_forked_from_marker`). Used to distinguish a
|
|
184
216
|
* fork from an ordinary `resume` (both arrive as `SessionStart source="resume"`).
|
|
185
|
-
*
|
|
186
|
-
*
|
|
217
|
+
* The marker must belong to the announced target and expected source; sample
|
|
218
|
+
* the first and last 200 records because long copied histories can place it at
|
|
219
|
+
* either edge.
|
|
187
220
|
*/
|
|
188
|
-
export function transcriptHasForkedFrom(path,
|
|
221
|
+
export function transcriptHasForkedFrom(path, claudeSessionId, sourceClaudeSessionId) {
|
|
189
222
|
let text;
|
|
190
223
|
try {
|
|
191
224
|
text = readFileSync(path, "utf8");
|
|
@@ -193,17 +226,60 @@ export function transcriptHasForkedFrom(path, currentSessionId) {
|
|
|
193
226
|
catch {
|
|
194
227
|
return false;
|
|
195
228
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
229
|
+
const lines = text.split("\n");
|
|
230
|
+
const sampled = lines.length <= 400
|
|
231
|
+
? lines
|
|
232
|
+
: [...lines.slice(0, 200), ...lines.slice(-200)];
|
|
233
|
+
for (const line of sampled) {
|
|
200
234
|
const trimmed = line.trim();
|
|
201
235
|
if (!trimmed || !trimmed.includes("forkedFrom"))
|
|
202
236
|
continue;
|
|
203
237
|
try {
|
|
204
238
|
const rec = JSON.parse(trimmed);
|
|
239
|
+
if (rec.sessionId !== claudeSessionId)
|
|
240
|
+
continue;
|
|
205
241
|
const from = rec.forkedFrom?.sessionId;
|
|
206
|
-
if (typeof from === "string" &&
|
|
242
|
+
if (typeof from === "string" &&
|
|
243
|
+
from &&
|
|
244
|
+
from !== claudeSessionId &&
|
|
245
|
+
(sourceClaudeSessionId === undefined || from === sourceClaudeSessionId))
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// skip malformed
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
const RECENT_LOCAL_COMMAND_LINE_LIMIT = 200;
|
|
255
|
+
const RECENT_LOCAL_COMMAND_WINDOW_MS = 10_000;
|
|
256
|
+
/** Claude versions without a copied-record marker still persist `/fork` and
|
|
257
|
+
* `/branch` as a recent top-level local command. Match only the new native
|
|
258
|
+
* Session and the hook's narrow time window. */
|
|
259
|
+
export function transcriptHasRecentLocalCommand(path, claudeSessionId, recordedAtMs, commandNames = new Set(["/fork", "/branch"])) {
|
|
260
|
+
let text;
|
|
261
|
+
try {
|
|
262
|
+
text = readFileSync(path, "utf8");
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
for (const line of text.split("\n").slice(-RECENT_LOCAL_COMMAND_LINE_LIMIT)) {
|
|
268
|
+
const trimmed = line.trim();
|
|
269
|
+
if (!trimmed)
|
|
270
|
+
continue;
|
|
271
|
+
try {
|
|
272
|
+
const rec = JSON.parse(trimmed);
|
|
273
|
+
if (rec.sessionId !== claudeSessionId || rec.subtype !== "local_command")
|
|
274
|
+
continue;
|
|
275
|
+
const timestamp = transcriptTimestampMs(rec.timestamp);
|
|
276
|
+
if (timestamp === undefined ||
|
|
277
|
+
Math.abs(timestamp - recordedAtMs) > RECENT_LOCAL_COMMAND_WINDOW_MS ||
|
|
278
|
+
typeof rec.content !== "string")
|
|
279
|
+
continue;
|
|
280
|
+
const command = /<command-name>([\s\S]*?)<\/command-name>/
|
|
281
|
+
.exec(rec.content)?.[1]?.trim();
|
|
282
|
+
if (command && commandNames.has(command))
|
|
207
283
|
return true;
|
|
208
284
|
}
|
|
209
285
|
catch {
|
|
@@ -212,6 +288,31 @@ export function transcriptHasForkedFrom(path, currentSessionId) {
|
|
|
212
288
|
}
|
|
213
289
|
return false;
|
|
214
290
|
}
|
|
291
|
+
/** Wait briefly for Claude to flush either fork signal after SessionStart.
|
|
292
|
+
* The observer hook calls this before recording the edge, allowing a
|
|
293
|
+
* one-second late-marker window without delaying ordinary transcript polling. */
|
|
294
|
+
export async function waitForTranscriptForkSignal(path, claudeSessionId, sourceClaudeSessionId, recordedAtMs, options = {}) {
|
|
295
|
+
const timeoutMs = Math.max(0, options.timeoutMs ?? 1_000);
|
|
296
|
+
const pollMs = Math.max(1, options.pollMs ?? 50);
|
|
297
|
+
const deadline = Date.now() + timeoutMs;
|
|
298
|
+
do {
|
|
299
|
+
if (transcriptHasForkedFrom(path, claudeSessionId, sourceClaudeSessionId) ||
|
|
300
|
+
transcriptHasRecentLocalCommand(path, claudeSessionId, recordedAtMs))
|
|
301
|
+
return true;
|
|
302
|
+
if (Date.now() >= deadline)
|
|
303
|
+
return false;
|
|
304
|
+
await sleep(Math.min(pollMs, Math.max(1, deadline - Date.now())));
|
|
305
|
+
} while (true);
|
|
306
|
+
}
|
|
307
|
+
function transcriptTimestampMs(value) {
|
|
308
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
309
|
+
return value < 10_000_000_000 ? value * 1_000 : value;
|
|
310
|
+
}
|
|
311
|
+
if (typeof value !== "string" || !value)
|
|
312
|
+
return undefined;
|
|
313
|
+
const parsed = Date.parse(value);
|
|
314
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
315
|
+
}
|
|
215
316
|
/**
|
|
216
317
|
* Read a sub-agent (Task) transcript in full and map it to {@link AgentEvent}s
|
|
217
318
|
* tagged with `parentToolUseId`. Called once the parent Task tool_result arrives
|
|
@@ -8,6 +8,23 @@ export interface CodexSessionRecord {
|
|
|
8
8
|
* metadata remains self-contained; this only tells a runner where the
|
|
9
9
|
* Provider persisted its rollout/state. */
|
|
10
10
|
runtimeHomeOwnerSessionId?: string;
|
|
11
|
+
/** Claude hook/forwarder rendezvous ownership. A rotated target keeps the
|
|
12
|
+
* physical pane's bridge while the retired logical source is re-keyed to an
|
|
13
|
+
* isolated bridge; both may still share one Provider runtime home. */
|
|
14
|
+
bridgeOwnerSessionId?: string;
|
|
15
|
+
/** Durable recovery edge for a Claude-native `/clear`·`/fork`. The target
|
|
16
|
+
* binding is written before IPC publication; `published` is flipped only
|
|
17
|
+
* after the parent durably creates the logical target. */
|
|
18
|
+
nativeRotationSourceSessionId?: string;
|
|
19
|
+
/** Authoritative latest published target when this record is the source.
|
|
20
|
+
* Avoids choosing an arbitrary historical edge after repeated rotations. */
|
|
21
|
+
nativeRotationTargetSessionId?: string;
|
|
22
|
+
nativeRotationKind?: "clear" | "fork";
|
|
23
|
+
nativeRotationPublished?: boolean;
|
|
24
|
+
/** Stable transcript boundary captured by the rotating forwarder at the
|
|
25
|
+
* target SessionStart. Clear starts at zero; fork starts after the copied
|
|
26
|
+
* source prefix. Recovery must not replace this with a later file EOF. */
|
|
27
|
+
nativeRotationInitialTranscriptOffset?: number;
|
|
11
28
|
updatedAt: string;
|
|
12
29
|
}
|
|
13
30
|
export interface ClaudeForkIntent {
|
|
@@ -15,6 +32,12 @@ export interface ClaudeForkIntent {
|
|
|
15
32
|
sourceSessionId: string;
|
|
16
33
|
sourceClaudeSessionId: string;
|
|
17
34
|
targetClaudeSessionId: string;
|
|
35
|
+
/** Target transcript atomically cloned before the target runner starts. Its
|
|
36
|
+
* presence means recovery must resume this exact file, never re-clone a
|
|
37
|
+
* source that may have advanced since the fork boundary. */
|
|
38
|
+
forkTranscriptPath?: string;
|
|
39
|
+
/** Exact byte length of the cloned prefix, measured before Claude starts. */
|
|
40
|
+
forkTranscriptPrefixBytes?: number;
|
|
18
41
|
updatedAt: string;
|
|
19
42
|
}
|
|
20
43
|
export interface CodexSessionStore {
|
|
@@ -107,6 +107,27 @@ export class FileCodexSessionStore {
|
|
|
107
107
|
...(record.runtimeHomeOwnerSessionId
|
|
108
108
|
? { runtimeHomeOwnerSessionId: record.runtimeHomeOwnerSessionId }
|
|
109
109
|
: {}),
|
|
110
|
+
...(record.bridgeOwnerSessionId
|
|
111
|
+
? { bridgeOwnerSessionId: record.bridgeOwnerSessionId }
|
|
112
|
+
: {}),
|
|
113
|
+
...(record.nativeRotationSourceSessionId
|
|
114
|
+
? { nativeRotationSourceSessionId: record.nativeRotationSourceSessionId }
|
|
115
|
+
: {}),
|
|
116
|
+
...(record.nativeRotationTargetSessionId
|
|
117
|
+
? { nativeRotationTargetSessionId: record.nativeRotationTargetSessionId }
|
|
118
|
+
: {}),
|
|
119
|
+
...(record.nativeRotationKind
|
|
120
|
+
? { nativeRotationKind: record.nativeRotationKind }
|
|
121
|
+
: {}),
|
|
122
|
+
...(record.nativeRotationPublished
|
|
123
|
+
? { nativeRotationPublished: true }
|
|
124
|
+
: {}),
|
|
125
|
+
...(Number.isSafeInteger(record.nativeRotationInitialTranscriptOffset) &&
|
|
126
|
+
record.nativeRotationInitialTranscriptOffset >= 0
|
|
127
|
+
? {
|
|
128
|
+
nativeRotationInitialTranscriptOffset: record.nativeRotationInitialTranscriptOffset,
|
|
129
|
+
}
|
|
130
|
+
: {}),
|
|
110
131
|
updatedAt: record.updatedAt,
|
|
111
132
|
};
|
|
112
133
|
}
|
package/dist/host.d.ts
CHANGED
|
@@ -9,6 +9,27 @@ import type { ModelListResponse, ThreadGoal } from "./codex-app-server/protocol.
|
|
|
9
9
|
import { type TerminalInjector } from "./claude/native-integration.js";
|
|
10
10
|
import { FileCodexSessionStore, resolveCodexSessionStorePath, type ClaudeForkIntent, type CodexSessionRecord, type CodexSessionStore } from "./codex-session-store.js";
|
|
11
11
|
import { AgentRuntimeError as CodexRuntimeError } from "@rynx-ai/core";
|
|
12
|
+
/** Delivery policy carried only inside the runner process. It is not part of
|
|
13
|
+
* the public SessionEvent protocol: the same canonical event has different
|
|
14
|
+
* retry semantics depending on whether it came from a transcript item, a
|
|
15
|
+
* terminal hook, or a best-effort live snapshot. */
|
|
16
|
+
export type MirrorDeliveryPolicy = "ordinary" | "terminal-status" | "best-effort" | "compaction" | "compaction-hook";
|
|
17
|
+
export type MirrorDeliveryOutcome = "confirmed" | "ambiguous" | "dropped" | "superseded";
|
|
18
|
+
export interface MirrorDeliveryOptions {
|
|
19
|
+
policy: MirrorDeliveryPolicy;
|
|
20
|
+
/** Stable local source key used by the Claude forwarder to checkpoint one
|
|
21
|
+
* item only after this delivery is handled. It is not sent to SQLite. */
|
|
22
|
+
sourceId?: string;
|
|
23
|
+
/** Independent retry lane. Claude native sub-agents use one lane each so a
|
|
24
|
+
* blocked child cannot stop its siblings or the parent transcript. */
|
|
25
|
+
lane?: string;
|
|
26
|
+
/** Claude-local write-only dead letter for an exhausted permanent ordinary
|
|
27
|
+
* item. No startup path replays this file automatically. */
|
|
28
|
+
deadLetterPath?: string;
|
|
29
|
+
/** Internal completion detail for state machines (not part of the wire). */
|
|
30
|
+
onOutcome?: (outcome: MirrorDeliveryOutcome) => void;
|
|
31
|
+
}
|
|
32
|
+
export type MirrorEmitter = (event: SessionEvent, delivery?: MirrorDeliveryOptions) => void | Promise<void>;
|
|
12
33
|
export { FileCodexSessionStore, resolveCodexSessionStorePath, type CodexSessionRecord, type CodexSessionStore, type ClaudeForkIntent, };
|
|
13
34
|
export interface CodexRuntimeStatus {
|
|
14
35
|
codex_available: boolean;
|
|
@@ -99,7 +120,10 @@ export type RetargetMirror = (newSessionId: string, meta: {
|
|
|
99
120
|
workspace: SessionWorkspaceSnapshot;
|
|
100
121
|
execution: ResolvedExecutionSnapshot;
|
|
101
122
|
parentSessionId?: string;
|
|
102
|
-
|
|
123
|
+
/** Runs after parent target persistence + child resource transfer, but
|
|
124
|
+
* before the child proves `rotate.applied` to the parent. */
|
|
125
|
+
beforeApplied?: () => void | Promise<void>;
|
|
126
|
+
}) => void | Promise<void>;
|
|
103
127
|
export interface LiveSessionOpts {
|
|
104
128
|
/** Immutable Session-owned snapshots. Provider launch/resume never re-opens a
|
|
105
129
|
* Project or Agent template. */
|
|
@@ -212,7 +236,7 @@ export declare class LocalAgentHost implements CodexCapabilities {
|
|
|
212
236
|
* existing session, the app-server resumes the persisted id. Rynx never writes
|
|
213
237
|
* or repairs Codex's private rollout files.
|
|
214
238
|
*/
|
|
215
|
-
ensureLiveCodexSession(localThreadId: string, emit:
|
|
239
|
+
ensureLiveCodexSession(localThreadId: string, emit: MirrorEmitter, opts: LiveSessionOpts): Promise<boolean>;
|
|
216
240
|
private startLiveCodexSession;
|
|
217
241
|
/** Bind a session's codex thread id once known (TUI broadcast or store): persist
|
|
218
242
|
* it, unblock injection, and kick off the resume-subscribe loop (once). */
|
|
@@ -301,6 +325,11 @@ export declare class LocalAgentHost implements CodexCapabilities {
|
|
|
301
325
|
* Provider startup never re-opens an Agent template or process-level plugin
|
|
302
326
|
* snapshot. */
|
|
303
327
|
private prepareExecutionSkills;
|
|
328
|
+
/** Repair the only cross-process crash window in native rotation. A target
|
|
329
|
+
* started through the Session registry proves that logical publication is
|
|
330
|
+
* durable; a published target in turn proves its source must no longer own
|
|
331
|
+
* the shared live bridge. */
|
|
332
|
+
private reconcileClaudeNativeRotation;
|
|
304
333
|
private startLiveClaudeSession;
|
|
305
334
|
/** Persist claude's discovered session id (reusing the `codexSessionId` store
|
|
306
335
|
* field, as the claude executor already does) and release the readiness gate. */
|