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

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.
@@ -7,7 +7,7 @@
7
7
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
8
  <link rel="icon" type="image/png" href="/favicon.png" />
9
9
  <title>rynx · control</title>
10
- <script type="module" crossorigin src="/assets/index-BQFR9W8k.js"></script>
10
+ <script type="module" crossorigin src="/assets/index-D-5t2eCT.js"></script>
11
11
  <link rel="stylesheet" crossorigin href="/assets/index-0noHpRXD.css">
12
12
  </head>
13
13
  <body>
@@ -3,10 +3,9 @@ import type { SequencedSessionEvent, SessionEvent, SessionInteractionResolution,
3
3
  import type { RemoteRuntimeSessionResourceDeleteParams, RemoteRuntimeSessionResourceDeleteResult, RemoteRuntimeSessionResourceGetParams, RemoteRuntimeSessionResourceGetResult, RemoteRuntimeSessionResourcePolicyResult, RemoteRuntimeSessionResourceReadParams, RemoteRuntimeSessionResourceReadResult, RemoteRuntimeSessionResourceUploadBeginParams, RemoteRuntimeSessionResourceUploadBeginResult, RemoteRuntimeSessionResourceUploadChunkParams, RemoteRuntimeSessionResourceUploadChunkResult, RemoteRuntimeSessionResourceUploadCommitParams, RemoteRuntimeSessionResourceUploadCommitResult } from "@rynx-ai/protocol/remote-runtime-rpc";
4
4
  import type { ControlAgentModelOption, ControlAgentSummary, ControlSessionInteractionResolveDisposition, ControlSessionRuntimeSnapshot, SessionPermissionPreset } from "@rynx-ai/protocol/control";
5
5
  import { type RemoteRuntimeSessionInterruptResult, type RemoteRuntimeSessionExecutionGetResult, type RemoteRuntimeSessionExecutionUpdateParams, type RemoteRuntimeSessionExecutionUpdateResult, type RemoteRuntimeSessionLaunchOptionsListResult, type RemoteRuntimeSessionListParams, type RemoteRuntimeSessionListResult, type RemoteRuntimeSessionSnapshotParams } from "@rynx-ai/protocol/remote-runtime-rpc";
6
- import type { CodexRuntimeStatus, InjectOutcome } from "@rynx-ai/runtime";
6
+ import type { CodexRuntimeStatus, InjectResult } from "@rynx-ai/runtime";
7
7
  export interface MachineSessionRuntimeStatePort {
8
8
  snapshot(sessionId: string): ControlSessionRuntimeSnapshot;
9
- beginResponseHandoff?(sessionId: string, responseId: string): () => void;
10
9
  remove?(sessionId: string): void;
11
10
  }
12
11
  export interface MachineSessionInterruptPort {
@@ -69,7 +68,7 @@ export interface MachineSessionRunnerPort {
69
68
  }): Promise<boolean>;
70
69
  lastLiveSessionError?(sessionId: string): string | undefined;
71
70
  lastLiveSessionFailure?(sessionId: string): LiveSessionFailure | undefined;
72
- injectMessage(sessionId: string, input: RuntimeUserInput | string): Promise<InjectOutcome>;
71
+ injectMessage(sessionId: string, input: RuntimeUserInput | string): Promise<InjectResult>;
73
72
  stopRunner(sessionId: string): void;
74
73
  resolveInteraction(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
75
74
  disposition: ControlSessionInteractionResolveDisposition;
@@ -7,8 +7,8 @@
7
7
  * Direct adapters can therefore expose the same behavior without copying state
8
8
  * into a Control Plane database.
9
9
  */
10
- import { createHash, randomUUID } from "node:crypto";
11
- import { SESSION_PROVIDER_IDS, getRuntimeProfile, isSessionProviderId, newSessionItemId, newSessionId, normalizeSessionTitle, } from "@rynx-ai/core";
10
+ import { createHash } from "node:crypto";
11
+ import { SESSION_PROVIDER_IDS, getRuntimeProfile, isSessionProviderId, newSessionId, normalizeSessionTitle, } from "@rynx-ai/core";
12
12
  import { REMOTE_RUNTIME_SESSION_DEFAULT_PAGE_SIZE, REMOTE_RUNTIME_SESSION_MAX_CURSOR_CHARS, REMOTE_RUNTIME_SESSION_MAX_ID_CHARS, REMOTE_RUNTIME_SESSION_MAX_PAGE_SIZE, } from "@rynx-ai/protocol/remote-runtime-rpc";
13
13
  const DEFAULT_LIST_LIMIT = REMOTE_RUNTIME_SESSION_DEFAULT_PAGE_SIZE;
14
14
  const MAX_LIST_LIMIT = REMOTE_RUNTIME_SESSION_MAX_PAGE_SIZE;
@@ -508,69 +508,21 @@ export class MachineSessionService {
508
508
  if (request.clientMessageId) {
509
509
  await resources?.markMessageInjecting(sessionId, request.clientMessageId);
510
510
  }
511
- const responseId = this.ports.publishEvent ? `resp_${randomUUID()}` : undefined;
512
- if (responseId) {
513
- await this.ports.publishEvent(sessionId, {
514
- type: "session.input.consumed",
515
- item: {
516
- id: newSessionItemId("message"),
517
- sessionId,
518
- position: 0,
519
- responseId,
520
- status: "completed",
521
- createdAt: Date.now(),
522
- type: "message",
523
- data: {
524
- role: "user",
525
- content: prepared.input.content.map((part) => part.type === "text"
526
- ? { type: "input_text", text: part.text }
527
- : { ...part.resource }),
528
- },
529
- },
530
- });
531
- await this.ports.publishEvent(sessionId, {
532
- type: "session.status",
533
- sessionId,
534
- responseId,
535
- status: "running",
536
- statusKind: "startup",
537
- });
538
- }
539
- const abandonResponseHandoff = responseId
540
- ? this.ports.runtimeState.beginResponseHandoff?.(sessionId, responseId)
541
- : undefined;
542
- const clearStartup = async (status) => {
543
- if (!responseId)
544
- return;
545
- await this.ports.publishEvent(sessionId, {
546
- type: "session.status",
547
- sessionId,
548
- responseId,
549
- status,
550
- });
551
- };
552
511
  let execution;
553
512
  try {
554
513
  execution = await this.ensureLiveSession(sessionId, meta);
555
514
  }
556
515
  catch (error) {
557
- abandonResponseHandoff?.();
558
- await clearStartup("idle");
559
516
  if (request.clientMessageId) {
560
517
  await resources?.markMessageFailedNotStarted(sessionId, request.clientMessageId, error instanceof Error ? error.message : String(error));
561
518
  }
562
519
  throw error;
563
520
  }
564
- let outcome;
521
+ let result;
565
522
  try {
566
- const runtimeInput = responseId
567
- ? { ...prepared.input, responseId }
568
- : prepared.input;
569
- outcome = await execution.runner.injectMessage(sessionId, responseId || needsPreparedOperation ? runtimeInput : request.message);
523
+ result = await execution.runner.injectMessage(sessionId, needsPreparedOperation ? prepared.input : request.message);
570
524
  }
571
525
  catch (error) {
572
- abandonResponseHandoff?.();
573
- await clearStartup("idle");
574
526
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId)
575
527
  ?? liveSessionFailureFrom(error);
576
528
  const reason = failure?.message
@@ -580,9 +532,8 @@ export class MachineSessionService {
580
532
  }
581
533
  throw new MachineSessionServiceFailure("outcome_unknown", "live injection outcome is unknown", reason, failure);
582
534
  }
535
+ const outcome = result.outcome;
583
536
  if (outcome === "failed") {
584
- abandonResponseHandoff?.();
585
- await clearStartup("idle");
586
537
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
587
538
  const reason = failure?.message
588
539
  ?? execution.runner.lastLiveSessionError?.(sessionId)
@@ -593,8 +544,6 @@ export class MachineSessionService {
593
544
  throw new MachineSessionServiceFailure("outcome_unknown", `live injection ${outcome}`, reason, failure);
594
545
  }
595
546
  if (outcome !== "injected" && outcome !== "steered") {
596
- abandonResponseHandoff?.();
597
- await clearStartup("idle");
598
547
  const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
599
548
  const reason = failure?.message
600
549
  ?? execution.runner.lastLiveSessionError?.(sessionId)
@@ -604,9 +553,6 @@ export class MachineSessionService {
604
553
  }
605
554
  throw new MachineSessionServiceFailure("failed_precondition", `live injection ${outcome}`, reason, failure);
606
555
  }
607
- if (outcome === "steered")
608
- abandonResponseHandoff?.();
609
- await clearStartup("running");
610
556
  if (request.clientMessageId) {
611
557
  try {
612
558
  await resources?.markMessageInjected(sessionId, request.clientMessageId);
@@ -838,7 +784,7 @@ export class MachineSessionService {
838
784
  current.message !== pending.message)
839
785
  return;
840
786
  injectionAttempted = true;
841
- const outcome = await execution.runner.injectMessage(sessionId, pending.message);
787
+ const { outcome } = await execution.runner.injectMessage(sessionId, pending.message);
842
788
  if (outcome === "injected" || outcome === "steered") {
843
789
  await Promise.resolve(pendingStore.delete(sessionId)).catch(async () => {
844
790
  await Promise.resolve(pendingStore.markOutcomeUnknown(sessionId)).catch(() => undefined);
@@ -534,6 +534,7 @@ function assertEventScope(event, expectedSessionId) {
534
534
  case "session.interaction.requested":
535
535
  case "session.interaction.resolved":
536
536
  case "session.interaction.cancelled":
537
+ case "session.interrupted":
537
538
  case "session.todos":
538
539
  case "session.rotated":
539
540
  actual = event.sessionId;
package/dist/server.d.ts CHANGED
@@ -4,7 +4,7 @@ import { ConversationRuntime, type AdmissionReservation, type AgentCapabilities,
4
4
  import type { SessionInteractionResolution } from "@rynx-ai/protocol";
5
5
  import type { ControlRuntimeShareAddress, DaemonBrowserArtifactCleanResult, DaemonBrowserArtifactInstallInput, DaemonBrowserArtifactInstallResult, DaemonBrowserArtifactUpdateInput, DaemonBrowserArtifactVersionResult, DaemonCleanupSessionsInput, DaemonCleanupSessionsResult, DaemonChromeInspectionConfigureInput, DaemonChromeInspectionStatus, DaemonShutdownIfIdleResult } from "@rynx-ai/protocol/control";
6
6
  import type { PluginInstallCommitInput, PluginInstallCommitResult, PluginInstallPreparation, PluginInstallPrepareInput, PluginManagementItem, PluginMarketplaceAddInput, PluginMarketplaceItem } from "@rynx-ai/protocol/plugin-management";
7
- import type { PluginJsonValue } from "@rynx-ai/plugin-sdk";
7
+ import type { PluginAgentSummary, PluginJsonValue, PluginResolvedSessionExecution, PluginSessionActivitySnapshot, PluginSessionExecutionSnapshot } from "@rynx-ai/plugin-sdk";
8
8
  import type { PairingOffer } from "@rynx-ai/protocol/direct-runtime";
9
9
  import type { DaemonStatus } from "@rynx-ai/protocol/remote-runtime";
10
10
  import type { RuntimeBrowserBootstrapCredential, RuntimeBrowserEndpointDescriptor } from "@rynx-ai/protocol/runtime-browser-bootstrap";
@@ -12,7 +12,6 @@ import type { RuntimeBrowserInspectMessage, RuntimeBrowserInspectTarget } from "
12
12
  import type { RuntimeBrowserSurfaceClientFrame, RuntimeBrowserSurfaceImageFrame, RuntimeBrowserSurfaceOpenFrame, RuntimeBrowserSurfaceReadyFrame, RuntimeBrowserSurfaceServerFrame } from "@rynx-ai/protocol/runtime-browser-surface";
13
13
  import type { RuntimeEmulatorSurfaceImageFrame, RuntimeEmulatorSurfaceReadyFrame } from "@rynx-ai/protocol/runtime-emulator-surface";
14
14
  import type { RemoteRuntimeRpcMethod, RemoteRuntimeRpcParams, RemoteRuntimeRpcResultFor, RemoteRuntimeSequencedSessionEvent } from "@rynx-ai/protocol/remote-runtime-rpc";
15
- import type { PluginAgentSummary, PluginResolvedSessionExecution, PluginSessionExecutionSnapshot } from "@rynx-ai/plugin-sdk";
16
15
  import { RunnerManager, type CodexSessionStore, type RunnerSessionContextProvider } from "@rynx-ai/runtime";
17
16
  import { type ControlPlaneDeps, type LocalSessionResourceHost, type SessionResourceLifecycle } from "./control-api.js";
18
17
  import type { MachineSessionService } from "./machine-session-service.js";
@@ -160,6 +159,7 @@ export interface PluginRuntimeHostServices {
160
159
  session: PluginSessionExecutionSnapshot;
161
160
  }): Promise<PluginResolvedSessionExecution>;
162
161
  };
162
+ sessionActivity(sessionId: string): PluginSessionActivitySnapshot;
163
163
  interruptSession(sessionId: string): Promise<boolean>;
164
164
  terminateSession?(sessionId: string): Promise<boolean>;
165
165
  resolveSessionInteraction(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
package/dist/server.js CHANGED
@@ -75,7 +75,6 @@ export function createSessionRuntimeServices(input) {
75
75
  config: input.config,
76
76
  executor: runnerManager,
77
77
  sessionBus,
78
- sessionEventSink: mirrorSessionEvent,
79
78
  });
80
79
  runnerManager.onMirror((sessionId, event) => {
81
80
  void mirrorSessionEvent(sessionId, event).catch((error) => {
@@ -222,6 +221,15 @@ export async function startServer({ config = loadConfig(), control, sessionLog,
222
221
  session: input.session,
223
222
  }),
224
223
  },
224
+ sessionActivity: (sessionId) => {
225
+ const snapshot = sessionRuntimeIndex.snapshot(sessionId);
226
+ return {
227
+ ...snapshot,
228
+ status: snapshot.pendingInteractions.length > 0
229
+ ? "waiting"
230
+ : snapshot.status,
231
+ };
232
+ },
225
233
  interruptSession: (sessionId) => runnerManager.interruptLiveSession(sessionId),
226
234
  terminateSession: (sessionId) => runnerManager.terminateLiveSession(sessionId),
227
235
  resolveSessionInteraction: (sessionId, interactionId, resolution) => runnerManager.resolveInteraction(sessionId, interactionId, resolution),
@@ -13,12 +13,6 @@ export interface SessionRuntimeActivitySnapshot {
13
13
  */
14
14
  export declare class SessionRuntimeIndex {
15
15
  private readonly sessions;
16
- /**
17
- * Make an accepted Turn visible before the request admission reservation is
18
- * released. The returned callback only abandons the pre-created handoff; once
19
- * response.created arrives, the native response remains authoritative.
20
- */
21
- beginResponseHandoff(sessionId: string, responseId: string): () => void;
22
16
  observe(sessionId: string, event: SessionEvent): void;
23
17
  snapshot(sessionId: string): SessionRuntimeSnapshot;
24
18
  /** Aggregate only process-live work; durable Session history is not activity. */
@@ -6,48 +6,33 @@
6
6
  */
7
7
  export class SessionRuntimeIndex {
8
8
  sessions = new Map();
9
- /**
10
- * Make an accepted Turn visible before the request admission reservation is
11
- * released. The returned callback only abandons the pre-created handoff; once
12
- * response.created arrives, the native response remains authoritative.
13
- */
14
- beginResponseHandoff(sessionId, responseId) {
15
- const state = this.state(sessionId);
16
- state.pendingResponseIds.add(responseId);
17
- state.status = "running";
18
- state.statusKind = "startup";
19
- let pending = true;
20
- return () => {
21
- if (!pending)
22
- return;
23
- pending = false;
24
- state.pendingResponseIds.delete(responseId);
25
- };
26
- }
27
9
  observe(sessionId, event) {
28
10
  const state = this.state(sessionId);
29
11
  switch (event.type) {
30
12
  case "response.created":
31
- state.pendingResponseIds.delete(event.responseId);
32
13
  state.activeResponseIds.add(event.responseId);
33
14
  state.status = "running";
34
15
  state.statusKind = undefined;
35
16
  return;
36
17
  case "response.completed":
37
- state.pendingResponseIds.delete(event.responseId);
38
18
  state.activeResponseIds.delete(event.responseId);
39
19
  this.removeResponseInteractions(state, event.responseId);
40
20
  state.status = this.hasRunningTurn(state) ? "running" : "idle";
41
21
  state.statusKind = undefined;
42
22
  return;
43
23
  case "response.failed":
44
- state.pendingResponseIds.delete(event.responseId);
45
24
  state.activeResponseIds.delete(event.responseId);
46
25
  this.removeResponseInteractions(state, event.responseId);
47
26
  // Response failure is Turn-local. It must not poison a reusable Session.
48
27
  state.status = this.hasRunningTurn(state) ? "running" : "idle";
49
28
  state.statusKind = undefined;
50
29
  return;
30
+ case "session.interrupted":
31
+ state.activeResponseIds.delete(event.responseId);
32
+ this.removeResponseInteractions(state, event.responseId);
33
+ state.status = this.hasRunningTurn(state) ? "running" : "idle";
34
+ state.statusKind = undefined;
35
+ return;
51
36
  case "session.status":
52
37
  state.status =
53
38
  state.pendingInteractions.size > 0 || this.hasRunningTurn(state)
@@ -56,7 +41,6 @@ export class SessionRuntimeIndex {
56
41
  state.statusKind = event.statusKind;
57
42
  return;
58
43
  case "session.interaction.requested":
59
- state.pendingResponseIds.delete(event.responseId);
60
44
  state.pendingInteractions.set(event.interaction.interactionId, {
61
45
  responseId: event.responseId,
62
46
  interaction: event.interaction,
@@ -71,7 +55,6 @@ export class SessionRuntimeIndex {
71
55
  state.status = this.hasRunningTurn(state) ? "running" : state.status;
72
56
  return;
73
57
  case "session.rotated":
74
- state.pendingResponseIds.clear();
75
58
  state.activeResponseIds.clear();
76
59
  state.pendingInteractions.clear();
77
60
  state.status = "idle";
@@ -99,7 +82,7 @@ export class SessionRuntimeIndex {
99
82
  let runningTurns = 0;
100
83
  let pendingInteractions = 0;
101
84
  for (const state of this.sessions.values()) {
102
- runningTurns += state.pendingResponseIds.size + state.activeResponseIds.size;
85
+ runningTurns += state.activeResponseIds.size;
103
86
  pendingInteractions += state.pendingInteractions.size;
104
87
  }
105
88
  return { runningTurns, pendingInteractions };
@@ -112,7 +95,6 @@ export class SessionRuntimeIndex {
112
95
  if (!state) {
113
96
  state = {
114
97
  status: "idle",
115
- pendingResponseIds: new Set(),
116
98
  activeResponseIds: new Set(),
117
99
  pendingInteractions: new Map(),
118
100
  };
@@ -127,6 +109,6 @@ export class SessionRuntimeIndex {
127
109
  }
128
110
  }
129
111
  hasRunningTurn(state) {
130
- return state.pendingResponseIds.size > 0 || state.activeResponseIds.size > 0;
112
+ return state.activeResponseIds.size > 0;
131
113
  }
132
114
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rynx-ai/server",
3
- "version": "0.1.11-beta.22",
3
+ "version": "0.1.11-beta.23",
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.22",
31
- "@rynx-ai/plugin-sdk": "0.1.11-beta.22",
32
- "@rynx-ai/protocol": "0.1.11-beta.22",
33
- "@rynx-ai/runtime": "0.1.11-beta.22",
34
- "@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.22"
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"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/ws": "^8.18.1",
38
- "@rynx-ai/control-web": "0.1.11-beta.22"
38
+ "@rynx-ai/control-web": "0.1.11-beta.23"
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/",