@rynx-ai/server 0.1.11-beta.23 → 0.1.11-beta.24

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.
@@ -312,10 +312,11 @@ export declare class MachineSessionService {
312
312
  launchOptions(): Promise<RemoteRuntimeSessionLaunchOptionsListResult>;
313
313
  /** Read the effective model defaults used for the next turn. */
314
314
  executionSettings(sessionIdInput: string): Promise<RemoteRuntimeSessionExecutionGetResult>;
315
- /** Replace the Turn defaults of an idle Session. Execution settings are
316
- * passed through to the Provider CLI, which owns availability validation.
317
- * The runner is closed so every Provider resumes its native context with
318
- * the new settings on the next turn. */
315
+ /** Replace the defaults for the next new Turn. Codex-lineage Providers keep
316
+ * their live runner: the host applies changed native thread settings under
317
+ * its injection lock immediately before the next `turn/start`. Claude still
318
+ * needs an idle cold resume because its live CLI has no confirmed settings
319
+ * update boundary. Provider CLIs own model availability validation. */
319
320
  updateExecutionSettings(input: RemoteRuntimeSessionExecutionUpdateParams): Promise<RemoteRuntimeSessionExecutionUpdateResult>;
320
321
  /** Create target-owned Session identity from one Agent preset or direct Provider. */
321
322
  create(input: MachineSessionCreateInput): Promise<MachineSessionCreateResult>;
@@ -210,10 +210,11 @@ export class MachineSessionService {
210
210
  }
211
211
  return executionSettingsResult(meta.execution);
212
212
  }
213
- /** Replace the Turn defaults of an idle Session. Execution settings are
214
- * passed through to the Provider CLI, which owns availability validation.
215
- * The runner is closed so every Provider resumes its native context with
216
- * the new settings on the next turn. */
213
+ /** Replace the defaults for the next new Turn. Codex-lineage Providers keep
214
+ * their live runner: the host applies changed native thread settings under
215
+ * its injection lock immediately before the next `turn/start`. Claude still
216
+ * needs an idle cold resume because its live CLI has no confirmed settings
217
+ * update boundary. Provider CLIs own model availability validation. */
217
218
  async updateExecutionSettings(input) {
218
219
  const sessionId = validOpaqueToken(input.sessionId, "sessionId", MAX_SESSION_ID_CHARS);
219
220
  const modelId = input.model === null ? null : validRequiredText(input.model, "model");
@@ -222,13 +223,15 @@ export class MachineSessionService {
222
223
  throw new MachineSessionServiceFailure("not_found", "session not found");
223
224
  }
224
225
  await this.assertNotForkReserved(sessionId);
225
- const runtime = this.ports.runtimeState.snapshot(sessionId);
226
- const pendingMessage = await this.ports.pendingMessages?.get(sessionId);
227
- if (runtime.status !== "idle" ||
228
- runtime.activeResponseIds.length > 0 ||
229
- runtime.pendingInteractions.length > 0 ||
230
- pendingMessage) {
231
- throw new MachineSessionServiceFailure("failed_precondition", "Session must be idle before changing model settings");
226
+ if (meta.execution.provider === "claude") {
227
+ const runtime = this.ports.runtimeState.snapshot(sessionId);
228
+ const pendingMessage = await this.ports.pendingMessages?.get(sessionId);
229
+ if (runtime.status !== "idle" ||
230
+ runtime.activeResponseIds.length > 0 ||
231
+ runtime.pendingInteractions.length > 0 ||
232
+ pendingMessage) {
233
+ throw new MachineSessionServiceFailure("failed_precondition", "Claude Session must be idle before changing model settings");
234
+ }
232
235
  }
233
236
  const executionPort = this.requireExecution();
234
237
  const reasoningEffort = input.reasoningEffort === null
@@ -240,7 +243,9 @@ export class MachineSessionService {
240
243
  reasoningEffort,
241
244
  };
242
245
  this.ports.registry.setExecution(sessionId, nextExecution);
243
- executionPort.runner.stopRunner(sessionId);
246
+ if (nextExecution.provider === "claude") {
247
+ executionPort.runner.stopRunner(sessionId);
248
+ }
244
249
  return executionSettingsResult(nextExecution);
245
250
  }
246
251
  /** Create target-owned Session identity from one Agent preset or direct Provider. */
@@ -21,4 +21,6 @@ export declare class SessionRuntimeIndex {
21
21
  private state;
22
22
  private removeResponseInteractions;
23
23
  private hasRunningTurn;
24
+ private statusAfterResponseTerminal;
25
+ private retireStatusResponses;
24
26
  }
@@ -17,29 +17,51 @@ export class SessionRuntimeIndex {
17
17
  case "response.completed":
18
18
  state.activeResponseIds.delete(event.responseId);
19
19
  this.removeResponseInteractions(state, event.responseId);
20
- state.status = this.hasRunningTurn(state) ? "running" : "idle";
20
+ state.status = this.statusAfterResponseTerminal(state);
21
21
  state.statusKind = undefined;
22
22
  return;
23
23
  case "response.failed":
24
24
  state.activeResponseIds.delete(event.responseId);
25
25
  this.removeResponseInteractions(state, event.responseId);
26
26
  // Response failure is Turn-local. It must not poison a reusable Session.
27
- state.status = this.hasRunningTurn(state) ? "running" : "idle";
27
+ state.status = this.statusAfterResponseTerminal(state);
28
28
  state.statusKind = undefined;
29
29
  return;
30
30
  case "session.interrupted":
31
31
  state.activeResponseIds.delete(event.responseId);
32
32
  this.removeResponseInteractions(state, event.responseId);
33
- state.status = this.hasRunningTurn(state) ? "running" : "idle";
33
+ state.status = this.statusAfterResponseTerminal(state);
34
34
  state.statusKind = undefined;
35
35
  return;
36
- case "session.status":
37
- state.status =
38
- state.pendingInteractions.size > 0 || this.hasRunningTurn(state)
39
- ? "running"
40
- : event.status;
41
- state.statusKind = event.statusKind;
36
+ case "session.status": {
37
+ if (event.status === "running") {
38
+ if (event.responseId)
39
+ state.activeResponseIds.add(event.responseId);
40
+ state.status = "running";
41
+ state.statusKind = event.statusKind;
42
+ return;
43
+ }
44
+ // A provider interaction is a stronger in-flight fact than a quiet-pane
45
+ // idle observation. Claude's status-file path reports waiting as running;
46
+ // this guard preserves the same truth on its legacy PTY fallback.
47
+ if (event.status === "idle" && state.pendingInteractions.size > 0) {
48
+ state.status = "running";
49
+ state.statusKind = undefined;
50
+ return;
51
+ }
52
+ this.retireStatusResponses(state);
53
+ if (event.status === "failed") {
54
+ state.status = "failed";
55
+ }
56
+ else if (state.status !== "failed") {
57
+ // Provider idle is authoritative even when final transcript records
58
+ // arrive later. Response identity remains on those events themselves;
59
+ // it must not keep Session activity/loading alive.
60
+ state.status = this.hasRunningTurn(state) ? "running" : "idle";
61
+ }
62
+ state.statusKind = undefined;
42
63
  return;
64
+ }
43
65
  case "session.interaction.requested":
44
66
  state.pendingInteractions.set(event.interaction.interactionId, {
45
67
  responseId: event.responseId,
@@ -52,7 +74,8 @@ export class SessionRuntimeIndex {
52
74
  case "session.interaction.resolved":
53
75
  case "session.interaction.cancelled":
54
76
  state.pendingInteractions.delete(event.interactionId);
55
- state.status = this.hasRunningTurn(state) ? "running" : state.status;
77
+ if (this.hasRunningTurn(state))
78
+ state.status = "running";
56
79
  return;
57
80
  case "session.rotated":
58
81
  state.activeResponseIds.clear();
@@ -69,9 +92,7 @@ export class SessionRuntimeIndex {
69
92
  if (!state)
70
93
  return { status: "idle", activeResponseIds: [], pendingInteractions: [] };
71
94
  return {
72
- status: state.pendingInteractions.size > 0 || this.hasRunningTurn(state)
73
- ? "running"
74
- : state.status,
95
+ status: state.pendingInteractions.size > 0 ? "running" : state.status,
75
96
  ...(state.statusKind === undefined ? {} : { statusKind: state.statusKind }),
76
97
  activeResponseIds: [...state.activeResponseIds],
77
98
  pendingInteractions: [...state.pendingInteractions.values()],
@@ -82,7 +103,9 @@ export class SessionRuntimeIndex {
82
103
  let runningTurns = 0;
83
104
  let pendingInteractions = 0;
84
105
  for (const state of this.sessions.values()) {
85
- runningTurns += state.activeResponseIds.size;
106
+ if (state.status === "running" || state.pendingInteractions.size > 0) {
107
+ runningTurns += Math.max(1, state.activeResponseIds.size);
108
+ }
86
109
  pendingInteractions += state.pendingInteractions.size;
87
110
  }
88
111
  return { runningTurns, pendingInteractions };
@@ -111,4 +134,18 @@ export class SessionRuntimeIndex {
111
134
  hasRunningTurn(state) {
112
135
  return state.activeResponseIds.size > 0;
113
136
  }
137
+ statusAfterResponseTerminal(state) {
138
+ if (state.pendingInteractions.size > 0 || this.hasRunningTurn(state))
139
+ return "running";
140
+ return state.status === "failed" ? "failed" : "idle";
141
+ }
142
+ retireStatusResponses(state) {
143
+ // session.status is Session-wide. Match Omnigent's
144
+ // _session_active_response_cache: every idle/failed edge closes the one
145
+ // in-flight native response projection, even when the edge happens to be
146
+ // stamped with that response id. A late transcript event may still carry
147
+ // the old id for grouping, but it cannot keep activity alive.
148
+ state.activeResponseIds.clear();
149
+ state.pendingInteractions.clear();
150
+ }
114
151
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/server",
3
- "version": "0.1.11-beta.23",
3
+ "version": "0.1.11-beta.24",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/rynx-ai/rynx.git",
@@ -27,15 +27,15 @@
27
27
  "@koa/router": "^15.4.0",
28
28
  "koa": "^3.2.0",
29
29
  "ws": "^8.21.0",
30
- "@rynx-ai/core": "0.1.11-beta.23",
31
- "@rynx-ai/plugin-sdk": "0.1.11-beta.23",
32
- "@rynx-ai/protocol": "0.1.11-beta.23",
33
- "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.23",
34
- "@rynx-ai/runtime": "0.1.11-beta.23"
30
+ "@rynx-ai/plugin-sdk": "0.1.11-beta.24",
31
+ "@rynx-ai/protocol": "0.1.11-beta.24",
32
+ "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.24",
33
+ "@rynx-ai/core": "0.1.11-beta.24",
34
+ "@rynx-ai/runtime": "0.1.11-beta.24"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/ws": "^8.18.1",
38
- "@rynx-ai/control-web": "0.1.11-beta.23"
38
+ "@rynx-ai/control-web": "0.1.11-beta.24"
39
39
  },
40
40
  "scripts": {
41
41
  "build": "rm -rf dist && tsc -p tsconfig.json && mkdir -p dist/control-web && cp -R ../control-web/dist/. dist/control-web/",