@rynx-ai/server 0.1.11-beta.21 → 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.
- package/dist/control-api.d.ts +8 -3
- package/dist/control-api.js +44 -11
- package/dist/control-web/assets/{highlighted-body-OFNGDK62-Ctle6Bpj.js → highlighted-body-OFNGDK62-DV5jYwcf.js} +1 -1
- package/dist/control-web/assets/{index-dOafa0IY.js → index-D-5t2eCT.js} +105 -105
- package/dist/control-web/assets/{mermaid-GHXKKRXX-DvISAPzD.js → mermaid-GHXKKRXX-DPZO2Q9W.js} +3 -3
- package/dist/control-web/index.html +1 -1
- package/dist/machine-session-service.d.ts +6 -5
- package/dist/machine-session-service.js +45 -69
- package/dist/remote-runtime-dispatcher.js +4 -1
- package/dist/remote-runtime-session-projection.js +1 -0
- package/dist/server.d.ts +3 -3
- package/dist/server.js +9 -1
- package/dist/session-runtime-index.d.ts +0 -6
- package/dist/session-runtime-index.js +8 -26
- package/package.json +7 -7
|
@@ -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-
|
|
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>
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import { type AdmissionReservation, type AgentRuntimeId, type ConversationRuntime, type ResolvedExecutionSnapshot, type SessionWorkspaceSnapshot, type SessionProviderId, type MachineSessionRecord, type ReasoningEffort, type RuntimeUserInput, type SessionBus, type SessionLogStore, type SessionRegistry } from "@rynx-ai/core";
|
|
1
|
+
import { type AdmissionReservation, type AgentRuntimeId, type ConversationRuntime, type ResolvedExecutionSnapshot, type SessionWorkspaceSnapshot, type SessionProviderId, type MachineSessionRecord, type LiveSessionFailure, type ReasoningEffort, type RuntimeUserInput, type SessionBus, type SessionLogStore, type SessionRegistry } from "@rynx-ai/core";
|
|
2
2
|
import type { SequencedSessionEvent, SessionEvent, SessionInteractionResolution, SessionItem, UserContentPart } from "@rynx-ai/protocol";
|
|
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,
|
|
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 {
|
|
@@ -68,7 +67,8 @@ export interface MachineSessionRunnerPort {
|
|
|
68
67
|
execution?: ResolvedExecutionSnapshot;
|
|
69
68
|
}): Promise<boolean>;
|
|
70
69
|
lastLiveSessionError?(sessionId: string): string | undefined;
|
|
71
|
-
|
|
70
|
+
lastLiveSessionFailure?(sessionId: string): LiveSessionFailure | undefined;
|
|
71
|
+
injectMessage(sessionId: string, input: RuntimeUserInput | string): Promise<InjectResult>;
|
|
72
72
|
stopRunner(sessionId: string): void;
|
|
73
73
|
resolveInteraction(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
|
|
74
74
|
disposition: ControlSessionInteractionResolveDisposition;
|
|
@@ -275,7 +275,8 @@ export type MachineSessionServiceFailureCode = "not_found" | "failed_preconditio
|
|
|
275
275
|
export declare class MachineSessionServiceFailure extends Error {
|
|
276
276
|
readonly code: MachineSessionServiceFailureCode;
|
|
277
277
|
readonly detail?: string | undefined;
|
|
278
|
-
|
|
278
|
+
readonly runtimeFailure?: LiveSessionFailure | undefined;
|
|
279
|
+
constructor(code: MachineSessionServiceFailureCode, message: string, detail?: string | undefined, runtimeFailure?: LiveSessionFailure | undefined);
|
|
279
280
|
}
|
|
280
281
|
/** Application boundary shared by an in-process Local adapter and Direct RPC. */
|
|
281
282
|
export declare class MachineSessionService {
|
|
@@ -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
|
|
11
|
-
import { SESSION_PROVIDER_IDS, getRuntimeProfile, isSessionProviderId,
|
|
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;
|
|
@@ -31,10 +31,12 @@ export class MachineSessionServiceInputError extends Error {
|
|
|
31
31
|
export class MachineSessionServiceFailure extends Error {
|
|
32
32
|
code;
|
|
33
33
|
detail;
|
|
34
|
-
|
|
34
|
+
runtimeFailure;
|
|
35
|
+
constructor(code, message, detail, runtimeFailure) {
|
|
35
36
|
super(message);
|
|
36
37
|
this.code = code;
|
|
37
38
|
this.detail = detail;
|
|
39
|
+
this.runtimeFailure = runtimeFailure;
|
|
38
40
|
this.name = "MachineSessionServiceFailure";
|
|
39
41
|
}
|
|
40
42
|
}
|
|
@@ -506,93 +508,51 @@ export class MachineSessionService {
|
|
|
506
508
|
if (request.clientMessageId) {
|
|
507
509
|
await resources?.markMessageInjecting(sessionId, request.clientMessageId);
|
|
508
510
|
}
|
|
509
|
-
const responseId = this.ports.publishEvent ? `resp_${randomUUID()}` : undefined;
|
|
510
|
-
if (responseId) {
|
|
511
|
-
await this.ports.publishEvent(sessionId, {
|
|
512
|
-
type: "session.input.consumed",
|
|
513
|
-
item: {
|
|
514
|
-
id: newSessionItemId("message"),
|
|
515
|
-
sessionId,
|
|
516
|
-
position: 0,
|
|
517
|
-
responseId,
|
|
518
|
-
status: "completed",
|
|
519
|
-
createdAt: Date.now(),
|
|
520
|
-
type: "message",
|
|
521
|
-
data: {
|
|
522
|
-
role: "user",
|
|
523
|
-
content: prepared.input.content.map((part) => part.type === "text"
|
|
524
|
-
? { type: "input_text", text: part.text }
|
|
525
|
-
: { ...part.resource }),
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
});
|
|
529
|
-
await this.ports.publishEvent(sessionId, {
|
|
530
|
-
type: "session.status",
|
|
531
|
-
sessionId,
|
|
532
|
-
responseId,
|
|
533
|
-
status: "running",
|
|
534
|
-
statusKind: "startup",
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
const abandonResponseHandoff = responseId
|
|
538
|
-
? this.ports.runtimeState.beginResponseHandoff?.(sessionId, responseId)
|
|
539
|
-
: undefined;
|
|
540
|
-
const clearStartup = async (status) => {
|
|
541
|
-
if (!responseId)
|
|
542
|
-
return;
|
|
543
|
-
await this.ports.publishEvent(sessionId, {
|
|
544
|
-
type: "session.status",
|
|
545
|
-
sessionId,
|
|
546
|
-
responseId,
|
|
547
|
-
status,
|
|
548
|
-
});
|
|
549
|
-
};
|
|
550
511
|
let execution;
|
|
551
512
|
try {
|
|
552
513
|
execution = await this.ensureLiveSession(sessionId, meta);
|
|
553
514
|
}
|
|
554
515
|
catch (error) {
|
|
555
|
-
abandonResponseHandoff?.();
|
|
556
|
-
await clearStartup("idle");
|
|
557
516
|
if (request.clientMessageId) {
|
|
558
517
|
await resources?.markMessageFailedNotStarted(sessionId, request.clientMessageId, error instanceof Error ? error.message : String(error));
|
|
559
518
|
}
|
|
560
519
|
throw error;
|
|
561
520
|
}
|
|
562
|
-
let
|
|
521
|
+
let result;
|
|
563
522
|
try {
|
|
564
|
-
|
|
565
|
-
? { ...prepared.input, responseId }
|
|
566
|
-
: prepared.input;
|
|
567
|
-
outcome = await execution.runner.injectMessage(sessionId, responseId || needsPreparedOperation ? runtimeInput : request.message);
|
|
523
|
+
result = await execution.runner.injectMessage(sessionId, needsPreparedOperation ? prepared.input : request.message);
|
|
568
524
|
}
|
|
569
525
|
catch (error) {
|
|
570
|
-
|
|
571
|
-
|
|
526
|
+
const failure = execution.runner.lastLiveSessionFailure?.(sessionId)
|
|
527
|
+
?? liveSessionFailureFrom(error);
|
|
528
|
+
const reason = failure?.message
|
|
529
|
+
?? (error instanceof Error ? error.message : String(error));
|
|
572
530
|
if (request.clientMessageId) {
|
|
573
|
-
await Promise.resolve(resources?.markMessageOutcomeUnknown(sessionId, request.clientMessageId,
|
|
531
|
+
await Promise.resolve(resources?.markMessageOutcomeUnknown(sessionId, request.clientMessageId, reason)).catch(() => undefined);
|
|
574
532
|
}
|
|
575
|
-
throw new MachineSessionServiceFailure("outcome_unknown", "live injection outcome is unknown");
|
|
533
|
+
throw new MachineSessionServiceFailure("outcome_unknown", "live injection outcome is unknown", reason, failure);
|
|
576
534
|
}
|
|
535
|
+
const outcome = result.outcome;
|
|
577
536
|
if (outcome === "failed") {
|
|
578
|
-
|
|
579
|
-
|
|
537
|
+
const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
|
|
538
|
+
const reason = failure?.message
|
|
539
|
+
?? execution.runner.lastLiveSessionError?.(sessionId)
|
|
540
|
+
?? `live injection ${outcome}`;
|
|
580
541
|
if (request.clientMessageId) {
|
|
581
|
-
await resources?.markMessageOutcomeUnknown(sessionId, request.clientMessageId,
|
|
542
|
+
await resources?.markMessageOutcomeUnknown(sessionId, request.clientMessageId, reason);
|
|
582
543
|
}
|
|
583
|
-
throw new MachineSessionServiceFailure("outcome_unknown", `live injection ${outcome}
|
|
544
|
+
throw new MachineSessionServiceFailure("outcome_unknown", `live injection ${outcome}`, reason, failure);
|
|
584
545
|
}
|
|
585
546
|
if (outcome !== "injected" && outcome !== "steered") {
|
|
586
|
-
|
|
587
|
-
|
|
547
|
+
const failure = execution.runner.lastLiveSessionFailure?.(sessionId);
|
|
548
|
+
const reason = failure?.message
|
|
549
|
+
?? execution.runner.lastLiveSessionError?.(sessionId)
|
|
550
|
+
?? `live injection ${outcome}`;
|
|
588
551
|
if (request.clientMessageId) {
|
|
589
|
-
await resources?.markMessageFailedNotStarted(sessionId, request.clientMessageId,
|
|
552
|
+
await resources?.markMessageFailedNotStarted(sessionId, request.clientMessageId, reason);
|
|
590
553
|
}
|
|
591
|
-
throw new MachineSessionServiceFailure("failed_precondition", `live injection ${outcome}
|
|
554
|
+
throw new MachineSessionServiceFailure("failed_precondition", `live injection ${outcome}`, reason, failure);
|
|
592
555
|
}
|
|
593
|
-
if (outcome === "steered")
|
|
594
|
-
abandonResponseHandoff?.();
|
|
595
|
-
await clearStartup("running");
|
|
596
556
|
if (request.clientMessageId) {
|
|
597
557
|
try {
|
|
598
558
|
await resources?.markMessageInjected(sessionId, request.clientMessageId);
|
|
@@ -679,7 +639,8 @@ export class MachineSessionService {
|
|
|
679
639
|
request.execution.runner.ensureLiveSession.bind(request.execution.runner);
|
|
680
640
|
const started = await start(sessionId, request.options);
|
|
681
641
|
if (!started) {
|
|
682
|
-
|
|
642
|
+
const failure = request.execution.runner.lastLiveSessionFailure?.(sessionId);
|
|
643
|
+
throw new MachineSessionServiceFailure("failed_precondition", `Provider terminal unavailable (${request.liveRuntime})`, failure?.message ?? request.execution.runner.lastLiveSessionError?.(sessionId), failure);
|
|
683
644
|
}
|
|
684
645
|
return { ok: true };
|
|
685
646
|
}
|
|
@@ -687,7 +648,8 @@ export class MachineSessionService {
|
|
|
687
648
|
const request = await this.liveSessionRequest(sessionId, meta);
|
|
688
649
|
const live = await request.execution.runner.ensureLiveSession(sessionId, request.options);
|
|
689
650
|
if (!live) {
|
|
690
|
-
|
|
651
|
+
const failure = request.execution.runner.lastLiveSessionFailure?.(sessionId);
|
|
652
|
+
throw new MachineSessionServiceFailure("failed_precondition", `live session unavailable (${request.liveRuntime})`, failure?.message ?? request.execution.runner.lastLiveSessionError?.(sessionId), failure);
|
|
691
653
|
}
|
|
692
654
|
return request.execution;
|
|
693
655
|
}
|
|
@@ -822,7 +784,7 @@ export class MachineSessionService {
|
|
|
822
784
|
current.message !== pending.message)
|
|
823
785
|
return;
|
|
824
786
|
injectionAttempted = true;
|
|
825
|
-
const outcome = await execution.runner.injectMessage(sessionId, pending.message);
|
|
787
|
+
const { outcome } = await execution.runner.injectMessage(sessionId, pending.message);
|
|
826
788
|
if (outcome === "injected" || outcome === "steered") {
|
|
827
789
|
await Promise.resolve(pendingStore.delete(sessionId)).catch(async () => {
|
|
828
790
|
await Promise.resolve(pendingStore.markOutcomeUnknown(sessionId)).catch(() => undefined);
|
|
@@ -1132,6 +1094,20 @@ function cloneRuntimeSnapshot(snapshot) {
|
|
|
1132
1094
|
})),
|
|
1133
1095
|
};
|
|
1134
1096
|
}
|
|
1097
|
+
function liveSessionFailureFrom(error) {
|
|
1098
|
+
if (!error || typeof error !== "object")
|
|
1099
|
+
return undefined;
|
|
1100
|
+
const candidate = error;
|
|
1101
|
+
if (typeof candidate.message !== "string" ||
|
|
1102
|
+
typeof candidate.code !== "string" ||
|
|
1103
|
+
typeof candidate.statusCode !== "number")
|
|
1104
|
+
return undefined;
|
|
1105
|
+
return {
|
|
1106
|
+
message: candidate.message,
|
|
1107
|
+
code: candidate.code,
|
|
1108
|
+
statusCode: candidate.statusCode,
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1135
1111
|
function requestLimit(value, fallback, maximum, field) {
|
|
1136
1112
|
return value === undefined ? Math.min(fallback, maximum) : boundedInteger(value, 1, maximum, field);
|
|
1137
1113
|
}
|
|
@@ -347,7 +347,10 @@ function mapHostError(request, error) {
|
|
|
347
347
|
: error.code === "conflict"
|
|
348
348
|
? "Remote Session resource conflicts with existing state"
|
|
349
349
|
: "Remote Session operation may have been applied";
|
|
350
|
-
|
|
350
|
+
// Native lifecycle failures are already reduced to a user-safe phase and
|
|
351
|
+
// concrete Provider cause. Preserve that text over Direct Runtime so the
|
|
352
|
+
// remote UI does not collapse every startup failure into one wrapper.
|
|
353
|
+
return { code: error.code, message: error.runtimeFailure?.message ?? message };
|
|
351
354
|
}
|
|
352
355
|
if (request.method === "session.interrupt") {
|
|
353
356
|
return {
|
|
@@ -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<{
|
|
@@ -350,7 +350,7 @@ export interface RuntimeConnectionLease {
|
|
|
350
350
|
};
|
|
351
351
|
call<M extends RemoteRuntimeRpcMethod>(method: M, params: RemoteRuntimeRpcParams<M>, options?: {
|
|
352
352
|
signal?: AbortSignal;
|
|
353
|
-
timeoutMs?: number;
|
|
353
|
+
timeoutMs?: number | null;
|
|
354
354
|
}): Promise<RemoteRuntimeRpcResultFor<M>>;
|
|
355
355
|
subscribeSessionEvents(sessionId: string, options?: {
|
|
356
356
|
signal?: AbortSignal;
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
31
|
-
"@rynx-ai/plugin-sdk": "0.1.11-beta.
|
|
32
|
-
"@rynx-ai/protocol": "0.1.11-beta.
|
|
33
|
-
"@rynx-ai/remote-runtime-e2ee": "0.1.11-beta.
|
|
34
|
-
"@rynx-ai/runtime": "0.1.11-beta.
|
|
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.
|
|
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/",
|