@rynx-ai/runtime 0.1.11-beta.28 → 0.1.11-beta.29
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/codex-app-server/client.d.ts +6 -3
- package/dist/codex-app-server/client.js +21 -2
- package/dist/codex-app-server/forwarder.d.ts +7 -1
- package/dist/codex-app-server/forwarder.js +12 -0
- package/dist/codex-app-server/protocol.d.ts +22 -0
- package/dist/host.js +81 -27
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { RuntimeUserInput, SessionInteractionResolution } from "@rynx-ai/core";
|
|
2
|
-
import type { AskForApproval, ClientInfo, CollaborationModeListResponse, GetAuthStatusParams, GetAuthStatusResponse, InitializeResponse, ModelListParams, ModelListResponse, ReviewStartParams, ReviewStartResponse, SandboxMode, ThreadForkParams, ThreadGoalClearParams, ThreadGoalGetParams, ThreadGoalGetResponse, ThreadGoalSetParams, ThreadListParams, ThreadListResponse, ResumedThread, ThreadResumeParams, ThreadSettingsUpdateParams, ThreadStartParams, TurnInterruptParams, TurnStartParams, TurnSteerParams, UserInput } from "./protocol.js";
|
|
2
|
+
import type { AskForApproval, ClientInfo, CollaborationModeListResponse, ConfigReadParams, ConfigReadResponse, GetAuthStatusParams, GetAuthStatusResponse, InitializeResponse, ModelListParams, ModelListResponse, ReviewStartParams, ReviewStartResponse, SandboxMode, ThreadForkParams, ThreadGoalClearParams, ThreadGoalGetParams, ThreadGoalGetResponse, ThreadGoalSetParams, ThreadListParams, ThreadListResponse, ResumedThread, ThreadResumeParams, ThreadRuntimeSettings, ThreadSettingsUpdateParams, ThreadStartParams, TurnInterruptParams, TurnStartParams, TurnSteerParams, UserInput } from "./protocol.js";
|
|
3
3
|
import { CodexAppServerTransport, type CodexAppServerProcessSpawner, type RpcChannel, type TransportLogger } from "./transport.js";
|
|
4
4
|
import type { ResolveInteractionResult, RuntimeInteractionListener } from "../interactions.js";
|
|
5
5
|
export type ApprovalDecisionPolicy = "auto-approve-session" | "auto-decline" | "auto-cancel";
|
|
@@ -44,13 +44,16 @@ export declare class CodexAppServerClient {
|
|
|
44
44
|
terminalRemoteUrl(): string | undefined;
|
|
45
45
|
ensureInitialized(): Promise<InitializeResponse>;
|
|
46
46
|
getAuthStatus(params?: GetAuthStatusParams): Promise<GetAuthStatusResponse>;
|
|
47
|
+
/** Read the runtime's merged effective config. This is the stable seam for
|
|
48
|
+
* discovering a fresh thread's model across Traex YAML/TOML generations. */
|
|
49
|
+
configRead(params: ConfigReadParams): Promise<ConfigReadResponse>;
|
|
47
50
|
threadStart(params: ThreadStartParams): Promise<{
|
|
48
51
|
threadId: string;
|
|
49
|
-
}>;
|
|
52
|
+
} & ThreadRuntimeSettings>;
|
|
50
53
|
threadResume(params: ThreadResumeParams): Promise<{
|
|
51
54
|
threadId: string;
|
|
52
55
|
thread: ResumedThread;
|
|
53
|
-
}>;
|
|
56
|
+
} & ThreadRuntimeSettings>;
|
|
54
57
|
turnStart(params: TurnStartParams): Promise<{
|
|
55
58
|
turnId: string;
|
|
56
59
|
}>;
|
|
@@ -1020,17 +1020,36 @@ export class CodexAppServerClient {
|
|
|
1020
1020
|
await this.ensureInitialized();
|
|
1021
1021
|
return this.transport.sendRequest("getAuthStatus", params);
|
|
1022
1022
|
}
|
|
1023
|
+
/** Read the runtime's merged effective config. This is the stable seam for
|
|
1024
|
+
* discovering a fresh thread's model across Traex YAML/TOML generations. */
|
|
1025
|
+
async configRead(params) {
|
|
1026
|
+
await this.ensureInitialized();
|
|
1027
|
+
return this.transport.sendRequest("config/read", params);
|
|
1028
|
+
}
|
|
1023
1029
|
async threadStart(params) {
|
|
1024
1030
|
await this.ensureInitialized();
|
|
1025
1031
|
const response = await this.transport.sendRequest("thread/start", params);
|
|
1026
|
-
return {
|
|
1032
|
+
return {
|
|
1033
|
+
threadId: response.thread.id,
|
|
1034
|
+
...(response.model ? { model: response.model } : {}),
|
|
1035
|
+
...(response.reasoningEffort === undefined
|
|
1036
|
+
? {}
|
|
1037
|
+
: { reasoningEffort: response.reasoningEffort }),
|
|
1038
|
+
};
|
|
1027
1039
|
}
|
|
1028
1040
|
async threadResume(params) {
|
|
1029
1041
|
await this.ensureInitialized();
|
|
1030
1042
|
const response = await this.transport.sendRequest("thread/resume", params);
|
|
1031
1043
|
// Expose the whole `thread` (not just its id): its `turns[].items[]` are the
|
|
1032
1044
|
// backfill the forwarder replays for a fresh thread's first turn.
|
|
1033
|
-
return {
|
|
1045
|
+
return {
|
|
1046
|
+
threadId: response.thread.id,
|
|
1047
|
+
thread: response.thread,
|
|
1048
|
+
...(response.model ? { model: response.model } : {}),
|
|
1049
|
+
...(response.reasoningEffort === undefined
|
|
1050
|
+
? {}
|
|
1051
|
+
: { reasoningEffort: response.reasoningEffort }),
|
|
1052
|
+
};
|
|
1034
1053
|
}
|
|
1035
1054
|
async turnStart(params) {
|
|
1036
1055
|
await this.ensureInitialized();
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import type { AgentEvent, UserContentPart } from "@rynx-ai/core";
|
|
25
25
|
import type { CodexAppServerClient } from "./client.js";
|
|
26
26
|
import type { McpStartupPlan } from "./mcp-startup.js";
|
|
27
|
-
import type { CollaborationModeKind, ResumedTurn } from "./protocol.js";
|
|
27
|
+
import type { CollaborationModeKind, ReasoningEffort, ResumedTurn } from "./protocol.js";
|
|
28
28
|
export interface CodexForwarderSink {
|
|
29
29
|
/** A turn began. `turnId` is codex's turn id, used to derive a stable
|
|
30
30
|
* `responseId`. Start a fresh normalizer/response. */
|
|
@@ -65,6 +65,12 @@ export interface CodexForwarderSink {
|
|
|
65
65
|
onThreadActive?(): void;
|
|
66
66
|
/** The native TUI or another app-server client changed collaboration mode. */
|
|
67
67
|
onCollaborationModeChanged?(mode: CollaborationModeKind): void;
|
|
68
|
+
/** Full mutable settings reported by the native thread. In particular, a
|
|
69
|
+
* TUI `/model` switch must become the model used by the next mode snapshot. */
|
|
70
|
+
onThreadSettingsChanged?(settings: {
|
|
71
|
+
model?: string;
|
|
72
|
+
reasoningEffort?: ReasoningEffort | null;
|
|
73
|
+
}): void;
|
|
68
74
|
/** Codex's terminal-local Plan picker is not emitted by app-server today.
|
|
69
75
|
* Synthesize it only after a live Plan item and its Turn both complete. */
|
|
70
76
|
onPlanImplementationPrompt?(prompt: CodexPlanImplementationPrompt): void;
|
|
@@ -279,6 +279,18 @@ export class CodexSessionForwarder {
|
|
|
279
279
|
}
|
|
280
280
|
if (method === "thread/settings/updated") {
|
|
281
281
|
const threadSettings = params?.threadSettings;
|
|
282
|
+
const model = typeof threadSettings?.model === "string" && threadSettings.model.trim()
|
|
283
|
+
? threadSettings.model.trim()
|
|
284
|
+
: undefined;
|
|
285
|
+
const effort = threadSettings?.effort;
|
|
286
|
+
if (model !== undefined || effort === null || typeof effort === "string") {
|
|
287
|
+
this.sink.onThreadSettingsChanged?.({
|
|
288
|
+
...(model === undefined ? {} : { model }),
|
|
289
|
+
...(effort === null || typeof effort === "string"
|
|
290
|
+
? { reasoningEffort: effort }
|
|
291
|
+
: {}),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
282
294
|
const mode = (threadSettings?.collaborationMode ?? threadSettings?.collaboration_mode)?.mode;
|
|
283
295
|
if (mode === "plan" || mode === "default") {
|
|
284
296
|
this.sink.onCollaborationModeChanged?.(mode);
|
|
@@ -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 = {
|
|
@@ -139,6 +154,13 @@ export interface ResumedThread {
|
|
|
139
154
|
turns?: ResumedTurn[];
|
|
140
155
|
[key: string]: unknown;
|
|
141
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
|
+
}
|
|
142
164
|
export interface ThreadDescriptor {
|
|
143
165
|
id: string;
|
|
144
166
|
cwd: string;
|
package/dist/host.js
CHANGED
|
@@ -531,10 +531,13 @@ export class LocalAgentHost {
|
|
|
531
531
|
try {
|
|
532
532
|
if (!suppliedClient)
|
|
533
533
|
await client.ensureInitialized();
|
|
534
|
+
const collaborationMode = buildCollaborationMode(mode, live.model, live.reasoningEffort);
|
|
534
535
|
await client.threadSettingsUpdate({
|
|
535
536
|
threadId,
|
|
536
|
-
collaborationMode
|
|
537
|
+
collaborationMode,
|
|
537
538
|
});
|
|
539
|
+
live.appliedModel = collaborationMode.settings.model;
|
|
540
|
+
live.appliedReasoningEffort = collaborationMode.settings.reasoning_effort ?? undefined;
|
|
538
541
|
live.appliedCollaborationMode = mode;
|
|
539
542
|
live.execution = { ...live.execution, collaborationMode: mode };
|
|
540
543
|
}
|
|
@@ -746,6 +749,34 @@ export class LocalAgentHost {
|
|
|
746
749
|
console.error(`[codex-live] session=${localThreadId} runtime=${runtime} app-server init failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
747
750
|
return abandon();
|
|
748
751
|
}
|
|
752
|
+
// A fresh remote TUI creates the thread, so Rynx does not receive its
|
|
753
|
+
// `thread/start` response. Ask the already-initialized app-server for the
|
|
754
|
+
// effective model instead of parsing Traex's version-dependent YAML/TOML
|
|
755
|
+
// files or confusing `model/list.isDefault` with this Session's model.
|
|
756
|
+
let configModel;
|
|
757
|
+
let configReasoningEffort;
|
|
758
|
+
if (!execution.model || !execution.reasoningEffort) {
|
|
759
|
+
try {
|
|
760
|
+
const effective = await appServerOwner.configRead({
|
|
761
|
+
includeLayers: false,
|
|
762
|
+
cwd: workspace.cwd,
|
|
763
|
+
});
|
|
764
|
+
const reportedModel = effective.config.model;
|
|
765
|
+
if (typeof reportedModel === "string" && reportedModel.trim()) {
|
|
766
|
+
configModel = reportedModel.trim();
|
|
767
|
+
}
|
|
768
|
+
const reportedEffort = effective.config.model_reasoning_effort;
|
|
769
|
+
if (typeof reportedEffort === "string" && reportedEffort) {
|
|
770
|
+
configReasoningEffort = reportedEffort;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
// Explicit Session settings remain usable on an older Provider. If a
|
|
775
|
+
// requested collaboration mode later needs an unknown model, that
|
|
776
|
+
// update fails clearly without mutating the native thread.
|
|
777
|
+
console.warn(`[codex-live] session=${localThreadId} runtime=${runtime} effective config unavailable: ${error instanceof Error ? error.message : String(error)}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
749
780
|
const appServerUrl = appServerOwner.terminalRemoteUrl();
|
|
750
781
|
if (!appServerUrl) {
|
|
751
782
|
this.liveStartupErrors.set(localThreadId, nativeLiveFailure(runtime, "native_app_server_endpoint_missing", "app-server started without a Terminal remote endpoint"));
|
|
@@ -772,8 +803,8 @@ export class LocalAgentHost {
|
|
|
772
803
|
return abandon();
|
|
773
804
|
}
|
|
774
805
|
}
|
|
775
|
-
const model = execution.model
|
|
776
|
-
const reasoningEffort = execution.reasoningEffort ??
|
|
806
|
+
const model = execution.model?.trim() || configModel || "";
|
|
807
|
+
const reasoningEffort = execution.reasoningEffort ?? configReasoningEffort;
|
|
777
808
|
let markReady;
|
|
778
809
|
const ready = new Promise((resolve) => {
|
|
779
810
|
markReady = resolve;
|
|
@@ -1024,7 +1055,7 @@ export class LocalAgentHost {
|
|
|
1024
1055
|
const client = this.injectionClientFactory(live.appServerUrl);
|
|
1025
1056
|
try {
|
|
1026
1057
|
await client.ensureInitialized();
|
|
1027
|
-
const collaborationMode =
|
|
1058
|
+
const collaborationMode = buildCollaborationMode("default", live.model, live.reasoningEffort);
|
|
1028
1059
|
let threadId = prompt.threadId;
|
|
1029
1060
|
let text = CODEX_PLAN_IMPLEMENTATION_CODING_MESSAGE;
|
|
1030
1061
|
if (choice === CODEX_PLAN_IMPLEMENTATION_CLEAR_CONTEXT) {
|
|
@@ -1292,6 +1323,16 @@ export class LocalAgentHost {
|
|
|
1292
1323
|
mode,
|
|
1293
1324
|
});
|
|
1294
1325
|
},
|
|
1326
|
+
onThreadSettingsChanged: ({ model: reportedModel, reasoningEffort: reportedEffort }) => {
|
|
1327
|
+
if (reportedModel) {
|
|
1328
|
+
live.model = reportedModel;
|
|
1329
|
+
live.appliedModel = reportedModel;
|
|
1330
|
+
}
|
|
1331
|
+
if (reportedEffort !== undefined) {
|
|
1332
|
+
live.reasoningEffort = reportedEffort ?? undefined;
|
|
1333
|
+
live.appliedReasoningEffort = reportedEffort ?? undefined;
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1295
1336
|
onPlanImplementationPrompt: publishPlanImplementationPrompt,
|
|
1296
1337
|
};
|
|
1297
1338
|
const forwarder = new CodexSessionForwarder(forwarderClient, sink, {
|
|
@@ -1400,6 +1441,7 @@ export class LocalAgentHost {
|
|
|
1400
1441
|
approvalPolicy,
|
|
1401
1442
|
excludeTurns: true,
|
|
1402
1443
|
});
|
|
1444
|
+
adoptResumedThreadSettings(live, resumed);
|
|
1403
1445
|
if (execution.collaborationMode) {
|
|
1404
1446
|
await this.applyLiveCollaborationMode(live, resumed.threadId, execution.collaborationMode, preloadClient);
|
|
1405
1447
|
}
|
|
@@ -1597,6 +1639,7 @@ export class LocalAgentHost {
|
|
|
1597
1639
|
approvalPolicy: live.approvalPolicy,
|
|
1598
1640
|
...(!sawNotReady ? { excludeTurns: true } : {}),
|
|
1599
1641
|
});
|
|
1642
|
+
adoptResumedThreadSettings(live, resp);
|
|
1600
1643
|
if (sawNotReady) {
|
|
1601
1644
|
const turns = Array.isArray(resp.thread.turns)
|
|
1602
1645
|
? resp.thread.turns
|
|
@@ -1848,7 +1891,7 @@ export class LocalAgentHost {
|
|
|
1848
1891
|
const desiredCollaborationMode = options?.collaborationMode ?? live.execution.collaborationMode;
|
|
1849
1892
|
const collaborationMode = desiredCollaborationMode &&
|
|
1850
1893
|
desiredCollaborationMode !== live.appliedCollaborationMode
|
|
1851
|
-
?
|
|
1894
|
+
? buildCollaborationMode(desiredCollaborationMode, desiredModel, desiredReasoningEffort)
|
|
1852
1895
|
: undefined;
|
|
1853
1896
|
const settings = {
|
|
1854
1897
|
threadId,
|
|
@@ -2895,28 +2938,30 @@ function raceReady(ready, timeoutMs, failed) {
|
|
|
2895
2938
|
clearTimeout(timer);
|
|
2896
2939
|
});
|
|
2897
2940
|
}
|
|
2941
|
+
/** Record the authoritative settings returned while binding an existing
|
|
2942
|
+
* native thread. A Session-level explicit selection remains a pending desired
|
|
2943
|
+
* override; otherwise the resumed thread itself is the source of truth. */
|
|
2944
|
+
function adoptResumedThreadSettings(live, settings) {
|
|
2945
|
+
const model = settings.model?.trim();
|
|
2946
|
+
if (model) {
|
|
2947
|
+
live.appliedModel = model;
|
|
2948
|
+
if (!live.execution.model)
|
|
2949
|
+
live.model = model;
|
|
2950
|
+
}
|
|
2951
|
+
if (settings.reasoningEffort !== undefined) {
|
|
2952
|
+
live.appliedReasoningEffort = settings.reasoningEffort ?? undefined;
|
|
2953
|
+
if (!live.execution.reasoningEffort) {
|
|
2954
|
+
live.reasoningEffort = settings.reasoningEffort ?? undefined;
|
|
2955
|
+
}
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2898
2958
|
/** Expand the public mode enum into the App Server's required settings
|
|
2899
|
-
* snapshot
|
|
2900
|
-
*
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
let model = desiredModel.trim();
|
|
2959
|
+
* snapshot using the Session's tracked effective model. `model/list` is a
|
|
2960
|
+
* catalog and its `isDefault` marker is not the current thread setting. */
|
|
2961
|
+
function buildCollaborationMode(mode, desiredModel, desiredReasoningEffort) {
|
|
2962
|
+
const model = desiredModel.trim();
|
|
2904
2963
|
if (!model) {
|
|
2905
|
-
|
|
2906
|
-
try {
|
|
2907
|
-
catalog = await client.modelList();
|
|
2908
|
-
}
|
|
2909
|
-
catch (error) {
|
|
2910
|
-
throw new CodexRuntimeError(`collaboration mode requires the current model: ${error instanceof Error ? error.message : String(error)}`, 503, "collaboration_mode_model_unknown");
|
|
2911
|
-
}
|
|
2912
|
-
const defaults = [...new Set(catalog.data
|
|
2913
|
-
.filter((candidate) => candidate.isDefault === true)
|
|
2914
|
-
.map((candidate) => (candidate.model || candidate.id).trim())
|
|
2915
|
-
.filter(Boolean))];
|
|
2916
|
-
if (defaults.length !== 1) {
|
|
2917
|
-
throw new CodexRuntimeError(`collaboration mode requires exactly one current default model; Provider advertised ${defaults.length}`, 503, "collaboration_mode_model_unknown");
|
|
2918
|
-
}
|
|
2919
|
-
model = defaults[0];
|
|
2964
|
+
throw new CodexRuntimeError("collaboration mode requires the current model, but the Provider did not report one", 503, "collaboration_mode_model_unknown");
|
|
2920
2965
|
}
|
|
2921
2966
|
return {
|
|
2922
2967
|
mode,
|
|
@@ -2958,9 +3003,18 @@ function updateLiveCodexTurnSettings(live, opts) {
|
|
|
2958
3003
|
if (!sameImmutableSessionSnapshots(live.workspace, live.execution, opts)) {
|
|
2959
3004
|
return false;
|
|
2960
3005
|
}
|
|
3006
|
+
const previousModel = live.execution.model;
|
|
3007
|
+
const previousReasoningEffort = live.execution.reasoningEffort;
|
|
2961
3008
|
live.execution = structuredClone(opts.execution);
|
|
2962
|
-
|
|
2963
|
-
|
|
3009
|
+
// Repeated calls normally carry the same immutable Session snapshot. Preserve
|
|
3010
|
+
// a newer native `/model` or effort change unless Core explicitly changed the
|
|
3011
|
+
// corresponding Session setting between calls.
|
|
3012
|
+
if (opts.execution.model !== previousModel) {
|
|
3013
|
+
live.model = opts.execution.model?.trim() ?? "";
|
|
3014
|
+
}
|
|
3015
|
+
if (opts.execution.reasoningEffort !== previousReasoningEffort) {
|
|
3016
|
+
live.reasoningEffort = opts.execution.reasoningEffort ?? undefined;
|
|
3017
|
+
}
|
|
2964
3018
|
return true;
|
|
2965
3019
|
}
|
|
2966
3020
|
/** Freeze the Session's permission choice into the explicit managed settings.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rynx-ai/runtime",
|
|
3
|
-
"version": "0.1.11-beta.
|
|
3
|
+
"version": "0.1.11-beta.29",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/rynx-ai/rynx.git",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"node-pty": "1.2.0-beta.15",
|
|
28
28
|
"smol-toml": "1.7.1",
|
|
29
29
|
"ws": "^8.21.0",
|
|
30
|
-
"@rynx-ai/core": "0.1.11-beta.
|
|
30
|
+
"@rynx-ai/core": "0.1.11-beta.29"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/ws": "^8.18.1"
|