@rynx-ai/runtime 0.1.0 → 0.1.10-beta.2
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/executor.d.ts +3 -5
- package/dist/claude/executor.js +3 -5
- package/dist/claude/native-bridge.d.ts +74 -17
- package/dist/claude/native-bridge.js +225 -30
- package/dist/claude/native-hook-main.js +327 -38
- package/dist/claude/native-hooks.d.ts +3 -2
- package/dist/claude/native-hooks.js +15 -6
- package/dist/claude/native-integration.d.ts +123 -16
- package/dist/claude/native-integration.js +624 -81
- package/dist/claude/settings.d.ts +8 -0
- package/dist/claude/settings.js +50 -0
- package/dist/claude/transcript.d.ts +2 -2
- package/dist/claude/transcript.js +14 -3
- package/dist/codex/rollout-synth.d.ts +8 -3
- package/dist/codex/rollout-synth.js +65 -32
- package/dist/codex-app-server/client.d.ts +27 -40
- package/dist/codex-app-server/client.js +1134 -99
- package/dist/codex-app-server/forwarder.d.ts +36 -10
- package/dist/codex-app-server/forwarder.js +146 -28
- package/dist/codex-app-server/mapping.d.ts +1 -1
- package/dist/codex-app-server/mapping.js +64 -5
- package/dist/codex-app-server/protocol.d.ts +269 -4
- package/dist/codex-app-server/transport.d.ts +20 -5
- package/dist/codex-app-server/transport.js +93 -40
- package/dist/codex-app-server/ws-channel.d.ts +3 -3
- package/dist/codex-app-server/ws-channel.js +23 -7
- package/dist/codex-child-env.js +33 -0
- package/dist/codex-home.d.ts +16 -6
- package/dist/codex-home.js +46 -15
- package/dist/codex-session-store.d.ts +2 -1
- package/dist/host.d.ts +38 -38
- package/dist/host.js +626 -121
- package/dist/index.d.ts +4 -3
- package/dist/index.js +1 -1
- package/dist/input-resources.d.ts +13 -0
- package/dist/input-resources.js +67 -0
- package/dist/interactions.d.ts +61 -0
- package/dist/interactions.js +236 -0
- package/dist/models-catalog.d.ts +5 -13
- package/dist/models-catalog.js +60 -9
- package/dist/runner/child.d.ts +9 -1
- package/dist/runner/child.js +100 -19
- package/dist/runner/manager.d.ts +79 -11
- package/dist/runner/manager.js +423 -43
- package/dist/runner/protocol.d.ts +30 -11
- package/dist/runner-main.js +9 -6
- package/dist/runtime-status.js +1 -1
- package/dist/terminal/claude-tui.d.ts +8 -3
- package/dist/terminal/claude-tui.js +6 -2
- package/dist/terminal/codex-tui.d.ts +3 -3
- package/dist/terminal/codex-tui.js +1 -1
- package/dist/terminal/registry.d.ts +1 -1
- package/dist/terminal/registry.js +1 -1
- package/dist/terminal/tmux.d.ts +6 -6
- package/dist/terminal/tmux.js +10 -10
- package/package.json +8 -3
|
@@ -58,7 +58,8 @@ export interface GetAuthStatusResponse {
|
|
|
58
58
|
}
|
|
59
59
|
export type AskForApproval = "untrusted" | "on-failure" | "on-request" | "never";
|
|
60
60
|
export type SandboxMode = "read-only" | "workspace-write" | "danger-full-access";
|
|
61
|
-
|
|
61
|
+
/** Open since Codex App Server 0.144; values are advertised by `model/list`. */
|
|
62
|
+
export type ReasoningEffort = string;
|
|
62
63
|
export interface ThreadStartParams {
|
|
63
64
|
model?: string | null;
|
|
64
65
|
modelProvider?: string | null;
|
|
@@ -75,13 +76,15 @@ export interface ThreadResumeParams extends ThreadStartParams {
|
|
|
75
76
|
/** When true, the resume response omits the thread's `turns` backlog (used to
|
|
76
77
|
* SUBSCRIBE without re-replaying history). When false/absent, the response
|
|
77
78
|
* carries `thread.turns[].items[]` — the backfill the forwarder replays for a
|
|
78
|
-
* fresh thread's first turn (
|
|
79
|
+
* fresh thread's first turn (reference implementation's `_replay_resume_response`). */
|
|
79
80
|
excludeTurns?: boolean;
|
|
80
81
|
}
|
|
81
82
|
/** One turn in a resumed thread's backlog (`thread/resume` response). */
|
|
82
83
|
export interface ResumedTurn {
|
|
83
84
|
id?: string;
|
|
84
85
|
turnId?: string;
|
|
86
|
+
status: TurnStatus;
|
|
87
|
+
error?: unknown;
|
|
85
88
|
items?: ThreadItem[];
|
|
86
89
|
}
|
|
87
90
|
/** The `thread` object a `thread/resume` response carries (id + optional backlog). */
|
|
@@ -197,6 +200,20 @@ export interface DynamicToolCallItem extends ThreadItemBase {
|
|
|
197
200
|
export interface WebSearchItem extends ThreadItemBase {
|
|
198
201
|
type: "webSearch";
|
|
199
202
|
query: string;
|
|
203
|
+
action?: {
|
|
204
|
+
type: "search";
|
|
205
|
+
query: string | null;
|
|
206
|
+
queries: string[] | null;
|
|
207
|
+
} | {
|
|
208
|
+
type: "openPage";
|
|
209
|
+
url: string | null;
|
|
210
|
+
} | {
|
|
211
|
+
type: "findInPage";
|
|
212
|
+
url: string | null;
|
|
213
|
+
pattern: string | null;
|
|
214
|
+
} | {
|
|
215
|
+
type: "other";
|
|
216
|
+
} | null;
|
|
200
217
|
}
|
|
201
218
|
export type ThreadItem = UserMessageItem | AgentMessageItem | ReasoningItem | PlanItem | CommandExecutionItem | FileChangeItem | McpToolCallItem | DynamicToolCallItem | WebSearchItem | (ThreadItemBase & Record<string, unknown>);
|
|
202
219
|
export interface ThreadSummary {
|
|
@@ -256,6 +273,11 @@ export interface ModelInfo {
|
|
|
256
273
|
hidden?: boolean;
|
|
257
274
|
isDefault?: boolean;
|
|
258
275
|
supportsPersonality?: boolean;
|
|
276
|
+
supportedReasoningEfforts?: Array<{
|
|
277
|
+
reasoningEffort: ReasoningEffort;
|
|
278
|
+
description?: string;
|
|
279
|
+
}>;
|
|
280
|
+
defaultReasoningEffort?: ReasoningEffort;
|
|
259
281
|
[key: string]: unknown;
|
|
260
282
|
}
|
|
261
283
|
export interface ModelListResponse {
|
|
@@ -398,6 +420,15 @@ export interface ErrorNotificationParams {
|
|
|
398
420
|
threadId: string;
|
|
399
421
|
turnId: string;
|
|
400
422
|
}
|
|
423
|
+
/** Traex backend-capacity queue update. */
|
|
424
|
+
export interface QueueStatusNotificationParams {
|
|
425
|
+
threadId: string;
|
|
426
|
+
turnId: string;
|
|
427
|
+
/** `queued` | `waiting` | `ready`. */
|
|
428
|
+
state: string;
|
|
429
|
+
position: number | null;
|
|
430
|
+
message: string | null;
|
|
431
|
+
}
|
|
401
432
|
export type ServerNotification = {
|
|
402
433
|
method: "thread/started";
|
|
403
434
|
params: ThreadStartedNotificationParams;
|
|
@@ -437,6 +468,9 @@ export type ServerNotification = {
|
|
|
437
468
|
} | {
|
|
438
469
|
method: "thread/tokenUsage/updated";
|
|
439
470
|
params: ThreadTokenUsageUpdatedNotificationParams;
|
|
471
|
+
} | {
|
|
472
|
+
method: "queue/status";
|
|
473
|
+
params: QueueStatusNotificationParams;
|
|
440
474
|
} | {
|
|
441
475
|
method: "error";
|
|
442
476
|
params: ErrorNotificationParams;
|
|
@@ -450,11 +484,26 @@ export interface CommandExecutionRequestApprovalParams {
|
|
|
450
484
|
itemId: string;
|
|
451
485
|
startedAtMs: number;
|
|
452
486
|
approvalId?: string | null;
|
|
487
|
+
environmentId: string | null;
|
|
453
488
|
reason?: string | null;
|
|
489
|
+
networkApprovalContext?: NetworkApprovalContext | null;
|
|
454
490
|
command?: string | null;
|
|
455
491
|
cwd?: string | null;
|
|
456
|
-
|
|
457
|
-
|
|
492
|
+
commandActions?: Array<Record<string, unknown>> | null;
|
|
493
|
+
additionalPermissions?: AdditionalPermissionProfile | null;
|
|
494
|
+
proposedExecpolicyAmendment?: string[] | null;
|
|
495
|
+
proposedNetworkPolicyAmendments?: NetworkPolicyAmendment[] | null;
|
|
496
|
+
availableDecisions?: CommandExecutionApprovalDecision[] | null;
|
|
497
|
+
}
|
|
498
|
+
export type CommandExecutionApprovalDecision = "accept" | "acceptForSession" | {
|
|
499
|
+
acceptWithExecpolicyAmendment: {
|
|
500
|
+
execpolicy_amendment: string[];
|
|
501
|
+
};
|
|
502
|
+
} | {
|
|
503
|
+
applyNetworkPolicyAmendment: {
|
|
504
|
+
network_policy_amendment: NetworkPolicyAmendment;
|
|
505
|
+
};
|
|
506
|
+
} | "decline" | "cancel";
|
|
458
507
|
export interface CommandExecutionRequestApprovalResponse {
|
|
459
508
|
decision: CommandExecutionApprovalDecision;
|
|
460
509
|
}
|
|
@@ -470,3 +519,219 @@ export type FileChangeApprovalDecision = "accept" | "acceptForSession" | "declin
|
|
|
470
519
|
export interface FileChangeRequestApprovalResponse {
|
|
471
520
|
decision: FileChangeApprovalDecision;
|
|
472
521
|
}
|
|
522
|
+
/** Legacy/experimental app-server approval methods still emitted by current
|
|
523
|
+
* Codex builds alongside the v2 item approval methods. */
|
|
524
|
+
export interface ExecCommandApprovalParams {
|
|
525
|
+
conversationId: string;
|
|
526
|
+
callId: string;
|
|
527
|
+
approvalId: string | null;
|
|
528
|
+
command: string[];
|
|
529
|
+
cwd: string;
|
|
530
|
+
reason: string | null;
|
|
531
|
+
}
|
|
532
|
+
export interface ApplyPatchApprovalParams {
|
|
533
|
+
conversationId: string;
|
|
534
|
+
callId: string;
|
|
535
|
+
fileChanges: Record<string, {
|
|
536
|
+
type: "add" | "delete";
|
|
537
|
+
content: string;
|
|
538
|
+
} | {
|
|
539
|
+
type: "update";
|
|
540
|
+
unified_diff: string;
|
|
541
|
+
move_path: string | null;
|
|
542
|
+
}>;
|
|
543
|
+
reason: string | null;
|
|
544
|
+
grantRoot: string | null;
|
|
545
|
+
}
|
|
546
|
+
export type LegacyReviewDecision = "approved" | "approved_for_session" | "denied" | "timed_out" | "abort";
|
|
547
|
+
export interface LegacyApprovalResponse {
|
|
548
|
+
decision: LegacyReviewDecision;
|
|
549
|
+
}
|
|
550
|
+
/** EXPERIMENTAL request_user_input question advertised by Codex app-server. */
|
|
551
|
+
export interface ToolRequestUserInputQuestion {
|
|
552
|
+
id: string;
|
|
553
|
+
header: string;
|
|
554
|
+
question: string;
|
|
555
|
+
isOther: boolean;
|
|
556
|
+
isSecret: boolean;
|
|
557
|
+
/** Traex extension; absent in Codex 0.144. */
|
|
558
|
+
multiSelect?: boolean;
|
|
559
|
+
options: Array<{
|
|
560
|
+
label: string;
|
|
561
|
+
description: string;
|
|
562
|
+
}> | null;
|
|
563
|
+
}
|
|
564
|
+
export interface ToolRequestUserInputParams {
|
|
565
|
+
threadId: string;
|
|
566
|
+
turnId: string;
|
|
567
|
+
itemId: string;
|
|
568
|
+
questions: ToolRequestUserInputQuestion[];
|
|
569
|
+
/** Codex supplies an optional auto-resolution deadline. */
|
|
570
|
+
autoResolutionMs?: number | null;
|
|
571
|
+
/** Traex 0.200 supplies blocking semantics instead of a deadline. */
|
|
572
|
+
isBlocking?: boolean;
|
|
573
|
+
}
|
|
574
|
+
export interface ToolRequestUserInputResponse {
|
|
575
|
+
answers: Record<string, {
|
|
576
|
+
answers: string[];
|
|
577
|
+
}>;
|
|
578
|
+
}
|
|
579
|
+
export interface McpElicitationConstOption {
|
|
580
|
+
const: string;
|
|
581
|
+
title: string;
|
|
582
|
+
}
|
|
583
|
+
export type McpElicitationPrimitiveSchema = {
|
|
584
|
+
type: "string";
|
|
585
|
+
title?: string;
|
|
586
|
+
description?: string;
|
|
587
|
+
minLength?: number;
|
|
588
|
+
maxLength?: number;
|
|
589
|
+
format?: "email" | "uri" | "date" | "date-time";
|
|
590
|
+
default?: string;
|
|
591
|
+
} | {
|
|
592
|
+
type: "string";
|
|
593
|
+
title?: string;
|
|
594
|
+
description?: string;
|
|
595
|
+
enum: string[];
|
|
596
|
+
enumNames?: string[];
|
|
597
|
+
default?: string;
|
|
598
|
+
} | {
|
|
599
|
+
type: "string";
|
|
600
|
+
title?: string;
|
|
601
|
+
description?: string;
|
|
602
|
+
oneOf: McpElicitationConstOption[];
|
|
603
|
+
default?: string;
|
|
604
|
+
} | {
|
|
605
|
+
type: "array";
|
|
606
|
+
title?: string;
|
|
607
|
+
description?: string;
|
|
608
|
+
minItems?: number;
|
|
609
|
+
maxItems?: number;
|
|
610
|
+
items: {
|
|
611
|
+
type: "string";
|
|
612
|
+
enum: string[];
|
|
613
|
+
} | {
|
|
614
|
+
anyOf: McpElicitationConstOption[];
|
|
615
|
+
};
|
|
616
|
+
default?: string[];
|
|
617
|
+
} | {
|
|
618
|
+
type: "number" | "integer";
|
|
619
|
+
title?: string;
|
|
620
|
+
description?: string;
|
|
621
|
+
minimum?: number;
|
|
622
|
+
maximum?: number;
|
|
623
|
+
default?: number;
|
|
624
|
+
} | {
|
|
625
|
+
type: "boolean";
|
|
626
|
+
title?: string;
|
|
627
|
+
description?: string;
|
|
628
|
+
default?: boolean;
|
|
629
|
+
};
|
|
630
|
+
export interface McpElicitationSchema {
|
|
631
|
+
$schema?: string;
|
|
632
|
+
type: "object";
|
|
633
|
+
properties: Record<string, McpElicitationPrimitiveSchema>;
|
|
634
|
+
required?: string[];
|
|
635
|
+
}
|
|
636
|
+
/** MCP elicitation request. `openai/form` deliberately keeps its schema open. */
|
|
637
|
+
export type McpServerElicitationRequestParams = {
|
|
638
|
+
threadId: string;
|
|
639
|
+
turnId: string | null;
|
|
640
|
+
serverName: string;
|
|
641
|
+
} & ({
|
|
642
|
+
mode: "form";
|
|
643
|
+
message: string;
|
|
644
|
+
requestedSchema: McpElicitationSchema;
|
|
645
|
+
_meta: unknown;
|
|
646
|
+
} | {
|
|
647
|
+
mode: "openai/form";
|
|
648
|
+
message: string;
|
|
649
|
+
requestedSchema: Record<string, unknown>;
|
|
650
|
+
_meta: unknown;
|
|
651
|
+
} | {
|
|
652
|
+
mode: "url";
|
|
653
|
+
message: string;
|
|
654
|
+
url: string;
|
|
655
|
+
elicitationId: string;
|
|
656
|
+
_meta: unknown;
|
|
657
|
+
});
|
|
658
|
+
export interface McpServerElicitationRequestResponse {
|
|
659
|
+
action: "accept" | "decline" | "cancel";
|
|
660
|
+
content: unknown | null;
|
|
661
|
+
_meta: unknown | null;
|
|
662
|
+
}
|
|
663
|
+
export interface AdditionalNetworkPermissions {
|
|
664
|
+
enabled: boolean | null;
|
|
665
|
+
}
|
|
666
|
+
export type FileSystemSpecialPath = {
|
|
667
|
+
kind: "root";
|
|
668
|
+
} | {
|
|
669
|
+
kind: "minimal";
|
|
670
|
+
} | {
|
|
671
|
+
kind: "project_roots";
|
|
672
|
+
subpath: string | null;
|
|
673
|
+
} | {
|
|
674
|
+
kind: "tmpdir";
|
|
675
|
+
} | {
|
|
676
|
+
kind: "slash_tmp";
|
|
677
|
+
} | {
|
|
678
|
+
kind: "unknown";
|
|
679
|
+
path: string;
|
|
680
|
+
subpath: string | null;
|
|
681
|
+
};
|
|
682
|
+
export type FileSystemPath = {
|
|
683
|
+
type: "path";
|
|
684
|
+
path: string;
|
|
685
|
+
} | {
|
|
686
|
+
type: "glob_pattern";
|
|
687
|
+
pattern: string;
|
|
688
|
+
} | {
|
|
689
|
+
type: "special";
|
|
690
|
+
value: FileSystemSpecialPath;
|
|
691
|
+
};
|
|
692
|
+
export interface FileSystemSandboxEntry {
|
|
693
|
+
path: FileSystemPath;
|
|
694
|
+
access: "read" | "write" | "deny";
|
|
695
|
+
}
|
|
696
|
+
export interface AdditionalFileSystemPermissions {
|
|
697
|
+
read: string[] | null;
|
|
698
|
+
write: string[] | null;
|
|
699
|
+
globScanMaxDepth?: number;
|
|
700
|
+
entries?: FileSystemSandboxEntry[];
|
|
701
|
+
}
|
|
702
|
+
export interface AdditionalPermissionProfile {
|
|
703
|
+
network: AdditionalNetworkPermissions | null;
|
|
704
|
+
fileSystem: AdditionalFileSystemPermissions | null;
|
|
705
|
+
}
|
|
706
|
+
export interface NetworkApprovalContext {
|
|
707
|
+
host: string;
|
|
708
|
+
protocol: "http" | "https" | "socks5Tcp" | "socks5Udp";
|
|
709
|
+
}
|
|
710
|
+
export interface NetworkPolicyAmendment {
|
|
711
|
+
host: string;
|
|
712
|
+
action: "allow" | "deny";
|
|
713
|
+
}
|
|
714
|
+
export type RequestPermissionProfile = AdditionalPermissionProfile;
|
|
715
|
+
export interface PermissionsRequestApprovalParams {
|
|
716
|
+
threadId: string;
|
|
717
|
+
turnId: string;
|
|
718
|
+
itemId: string;
|
|
719
|
+
environmentId: string | null;
|
|
720
|
+
startedAtMs: number;
|
|
721
|
+
cwd: string;
|
|
722
|
+
reason: string | null;
|
|
723
|
+
permissions: RequestPermissionProfile;
|
|
724
|
+
}
|
|
725
|
+
export interface PermissionsRequestApprovalResponse {
|
|
726
|
+
permissions: {
|
|
727
|
+
network?: AdditionalNetworkPermissions;
|
|
728
|
+
fileSystem?: AdditionalFileSystemPermissions;
|
|
729
|
+
};
|
|
730
|
+
scope: "turn" | "session";
|
|
731
|
+
strictAutoReview?: boolean;
|
|
732
|
+
}
|
|
733
|
+
/** A different app-server client (usually the native TUI) answered first. */
|
|
734
|
+
export interface ServerRequestResolvedNotificationParams {
|
|
735
|
+
threadId?: string;
|
|
736
|
+
requestId: RequestId;
|
|
737
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
|
-
import type { SandboxMode } from "./protocol.js";
|
|
2
|
+
import type { RequestId, SandboxMode } from "./protocol.js";
|
|
3
3
|
export declare function buildAppServerSpawnArgs(sandboxMode: SandboxMode, listen?: string): string[];
|
|
4
4
|
/** app-server args WITHOUT `--listen` — for a channel that appends its own
|
|
5
5
|
* transport endpoint (e.g. {@link import("./ws-channel.js").WsRpcChannel}). */
|
|
@@ -29,7 +29,7 @@ export interface RpcChannel {
|
|
|
29
29
|
/** Establish the channel (connect / await readiness). */
|
|
30
30
|
start(): Promise<void>;
|
|
31
31
|
/** Write one framed message (already newline-terminated). */
|
|
32
|
-
send(line: string): void
|
|
32
|
+
send(line: string): void | Promise<void>;
|
|
33
33
|
/** Register the inbound-line handler (one framed message per call). */
|
|
34
34
|
onLine(cb: (line: string) => void): void;
|
|
35
35
|
/** Register the close/error handler. */
|
|
@@ -64,8 +64,17 @@ export declare class CodexTransportError extends Error {
|
|
|
64
64
|
readonly data?: unknown;
|
|
65
65
|
constructor(message: string, code?: number, data?: unknown);
|
|
66
66
|
}
|
|
67
|
-
export type ServerRequestHandler = (method: string, params: unknown) => Promise<unknown> | unknown;
|
|
67
|
+
export type ServerRequestHandler = (method: string, params: unknown, requestId: RequestId) => Promise<unknown | typeof NO_SERVER_RESPONSE> | unknown | typeof NO_SERVER_RESPONSE;
|
|
68
|
+
/** A server request was resolved by another app-server client. In that case the
|
|
69
|
+
* local handler must settle without sending a second JSON-RPC response. */
|
|
70
|
+
export declare const NO_SERVER_RESPONSE: unique symbol;
|
|
68
71
|
export type ServerNotificationHandler = (method: string, params: unknown) => void;
|
|
72
|
+
export type ServerRequestResponseDeliveryHandler = (requestId: RequestId, result: {
|
|
73
|
+
delivered: true;
|
|
74
|
+
} | {
|
|
75
|
+
delivered: false;
|
|
76
|
+
error: Error;
|
|
77
|
+
}) => void;
|
|
69
78
|
export interface CodexAppServerTransportOptions {
|
|
70
79
|
/** Default stdio transport: spawns `app-server --listen stdio://`. Mutually
|
|
71
80
|
* exclusive with {@link channel}; provide exactly one. */
|
|
@@ -83,6 +92,10 @@ export interface CodexAppServerTransportOptions {
|
|
|
83
92
|
* propagated back to the server.
|
|
84
93
|
*/
|
|
85
94
|
onNotification?: ServerNotificationHandler;
|
|
95
|
+
/** Reports whether a server-request response was actually handed to the
|
|
96
|
+
* transport. Provider acknowledgement still arrives separately as
|
|
97
|
+
* `serverRequest/resolved`. */
|
|
98
|
+
onServerRequestResponseDelivery?: ServerRequestResponseDeliveryHandler;
|
|
86
99
|
}
|
|
87
100
|
interface TransportEvents {
|
|
88
101
|
exit: [code: number | null, signal: NodeJS.Signals | null];
|
|
@@ -109,6 +122,7 @@ export declare class CodexAppServerTransport extends EventEmitter {
|
|
|
109
122
|
private readonly logger;
|
|
110
123
|
private readonly onServerRequest?;
|
|
111
124
|
private readonly onNotification?;
|
|
125
|
+
private readonly onServerRequestResponseDelivery?;
|
|
112
126
|
private child;
|
|
113
127
|
private rl;
|
|
114
128
|
private stderrRl;
|
|
@@ -116,14 +130,14 @@ export declare class CodexAppServerTransport extends EventEmitter {
|
|
|
116
130
|
private readonly pending;
|
|
117
131
|
private startPromise;
|
|
118
132
|
private exited;
|
|
119
|
-
constructor({ spawner, channel, logger, onServerRequest, onNotification, }: CodexAppServerTransportOptions);
|
|
133
|
+
constructor({ spawner, channel, logger, onServerRequest, onNotification, onServerRequestResponseDelivery, }: CodexAppServerTransportOptions);
|
|
120
134
|
emit<K extends keyof TransportEvents>(event: K, ...args: TransportEvents[K]): boolean;
|
|
121
135
|
on<K extends keyof TransportEvents>(event: K, listener: (...args: TransportEvents[K]) => void): this;
|
|
122
136
|
off<K extends keyof TransportEvents>(event: K, listener: (...args: TransportEvents[K]) => void): this;
|
|
123
137
|
isRunning(): boolean;
|
|
124
138
|
ensureStarted(): Promise<void>;
|
|
125
139
|
sendRequest<TResult = unknown>(method: string, params?: unknown): Promise<TResult>;
|
|
126
|
-
sendNotification(method: string, params?: unknown): void
|
|
140
|
+
sendNotification(method: string, params?: unknown): Promise<void>;
|
|
127
141
|
stop(signal?: NodeJS.Signals): Promise<void>;
|
|
128
142
|
private startTransport;
|
|
129
143
|
private startChild;
|
|
@@ -134,6 +148,7 @@ export declare class CodexAppServerTransport extends EventEmitter {
|
|
|
134
148
|
private handleErrorEnvelope;
|
|
135
149
|
private handleServerRequest;
|
|
136
150
|
private respondToServerRequest;
|
|
151
|
+
private notifyServerRequestResponseDelivery;
|
|
137
152
|
private handleNotification;
|
|
138
153
|
}
|
|
139
154
|
export {};
|
|
@@ -67,6 +67,9 @@ export class CodexTransportError extends Error {
|
|
|
67
67
|
this.data = data;
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
/** A server request was resolved by another app-server client. In that case the
|
|
71
|
+
* local handler must settle without sending a second JSON-RPC response. */
|
|
72
|
+
export const NO_SERVER_RESPONSE = Symbol("NO_SERVER_RESPONSE");
|
|
70
73
|
/**
|
|
71
74
|
* Wraps the Codex `app-server` subprocess and exposes a typed JSON-RPC API
|
|
72
75
|
* over its stdio. Frames are line-delimited NDJSON (no Content-Length
|
|
@@ -88,6 +91,7 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
88
91
|
logger;
|
|
89
92
|
onServerRequest;
|
|
90
93
|
onNotification;
|
|
94
|
+
onServerRequestResponseDelivery;
|
|
91
95
|
child = null;
|
|
92
96
|
rl = null;
|
|
93
97
|
stderrRl = null;
|
|
@@ -95,7 +99,7 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
95
99
|
pending = new Map();
|
|
96
100
|
startPromise = null;
|
|
97
101
|
exited = false;
|
|
98
|
-
constructor({ spawner, channel, logger = defaultLogger, onServerRequest, onNotification, }) {
|
|
102
|
+
constructor({ spawner, channel, logger = defaultLogger, onServerRequest, onNotification, onServerRequestResponseDelivery, }) {
|
|
99
103
|
super();
|
|
100
104
|
if (!spawner && !channel) {
|
|
101
105
|
throw new Error("CodexAppServerTransport requires a spawner or a channel");
|
|
@@ -105,6 +109,7 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
105
109
|
this.logger = logger;
|
|
106
110
|
this.onServerRequest = onServerRequest;
|
|
107
111
|
this.onNotification = onNotification;
|
|
112
|
+
this.onServerRequestResponseDelivery = onServerRequestResponseDelivery;
|
|
108
113
|
}
|
|
109
114
|
emit(event, ...args) {
|
|
110
115
|
return super.emit(event, ...args);
|
|
@@ -145,39 +150,46 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
145
150
|
async sendRequest(method, params) {
|
|
146
151
|
await this.ensureStarted();
|
|
147
152
|
const id = this.nextId++;
|
|
148
|
-
|
|
153
|
+
const envelope = {
|
|
154
|
+
jsonrpc: "2.0",
|
|
155
|
+
id,
|
|
156
|
+
method,
|
|
157
|
+
...(params === undefined ? {} : { params }),
|
|
158
|
+
};
|
|
159
|
+
let rejectResponse;
|
|
160
|
+
const response = new Promise((resolve, reject) => {
|
|
161
|
+
rejectResponse = reject;
|
|
149
162
|
this.pending.set(id, {
|
|
150
163
|
resolve: resolve,
|
|
151
164
|
reject,
|
|
152
165
|
method,
|
|
153
166
|
});
|
|
154
|
-
const envelope = {
|
|
155
|
-
jsonrpc: "2.0",
|
|
156
|
-
id,
|
|
157
|
-
method,
|
|
158
|
-
...(params === undefined ? {} : { params }),
|
|
159
|
-
};
|
|
160
|
-
try {
|
|
161
|
-
this.writeLine(envelope);
|
|
162
|
-
}
|
|
163
|
-
catch (error) {
|
|
164
|
-
this.pending.delete(id);
|
|
165
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
166
|
-
}
|
|
167
167
|
});
|
|
168
|
+
try {
|
|
169
|
+
await this.writeLine(envelope);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
this.pending.delete(id);
|
|
173
|
+
rejectResponse(error instanceof Error ? error : new Error(String(error)));
|
|
174
|
+
}
|
|
175
|
+
return await response;
|
|
168
176
|
}
|
|
169
|
-
sendNotification(method, params) {
|
|
177
|
+
async sendNotification(method, params) {
|
|
170
178
|
const envelope = {
|
|
171
179
|
jsonrpc: "2.0",
|
|
172
180
|
method,
|
|
173
181
|
...(params === undefined ? {} : { params }),
|
|
174
182
|
};
|
|
175
|
-
this.writeLine(envelope);
|
|
183
|
+
await this.writeLine(envelope);
|
|
176
184
|
}
|
|
177
185
|
async stop(signal = "SIGTERM") {
|
|
178
186
|
if (this.channel) {
|
|
179
|
-
this.exited = true;
|
|
180
187
|
await this.channel.stop(signal);
|
|
188
|
+
// Channel implementations may report close asynchronously (or not at all).
|
|
189
|
+
// Run the same exit path synchronously so pending RPCs reject and clients
|
|
190
|
+
// clear pending native interactions before stop() resolves.
|
|
191
|
+
if (!this.exited)
|
|
192
|
+
this.handleExit(null, signal, null);
|
|
181
193
|
return;
|
|
182
194
|
}
|
|
183
195
|
const child = this.child;
|
|
@@ -268,25 +280,29 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
268
280
|
});
|
|
269
281
|
this.emit("exit", code, signal);
|
|
270
282
|
}
|
|
271
|
-
writeLine(envelope) {
|
|
283
|
+
async writeLine(envelope) {
|
|
272
284
|
const line = `${JSON.stringify(envelope)}\n`;
|
|
273
285
|
if (this.channel) {
|
|
274
286
|
if (this.exited) {
|
|
275
287
|
throw new CodexTransportError("Codex app-server is not running");
|
|
276
288
|
}
|
|
277
|
-
this.channel.send(line);
|
|
289
|
+
await this.channel.send(line);
|
|
278
290
|
return;
|
|
279
291
|
}
|
|
280
292
|
const child = this.child;
|
|
281
293
|
if (!child || this.exited) {
|
|
282
294
|
throw new CodexTransportError("Codex app-server is not running");
|
|
283
295
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
296
|
+
await new Promise((resolve, reject) => {
|
|
297
|
+
const ok = child.stdin.write(line, (error) => {
|
|
298
|
+
if (error)
|
|
299
|
+
reject(error);
|
|
300
|
+
else
|
|
301
|
+
resolve();
|
|
302
|
+
});
|
|
303
|
+
if (!ok)
|
|
304
|
+
this.logger.log({ event: "transport.stdin_backpressure" });
|
|
305
|
+
});
|
|
290
306
|
}
|
|
291
307
|
handleLine(line) {
|
|
292
308
|
if (!line.trim()) {
|
|
@@ -352,34 +368,71 @@ export class CodexAppServerTransport extends EventEmitter {
|
|
|
352
368
|
}
|
|
353
369
|
async handleServerRequest(envelope) {
|
|
354
370
|
if (!this.onServerRequest) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
371
|
+
try {
|
|
372
|
+
await this.respondToServerRequest(envelope.id, undefined, {
|
|
373
|
+
code: -32601,
|
|
374
|
+
message: `No handler registered for ${envelope.method}`,
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
catch (error) {
|
|
378
|
+
this.logger.log({
|
|
379
|
+
event: "transport.server_request_error_write_failed",
|
|
380
|
+
error: error instanceof Error ? error.message : String(error),
|
|
381
|
+
});
|
|
382
|
+
}
|
|
359
383
|
return;
|
|
360
384
|
}
|
|
385
|
+
let result;
|
|
361
386
|
try {
|
|
362
|
-
|
|
363
|
-
this.respondToServerRequest(envelope.id, result, null);
|
|
387
|
+
result = await this.onServerRequest(envelope.method, envelope.params, envelope.id);
|
|
364
388
|
}
|
|
365
389
|
catch (error) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
390
|
+
try {
|
|
391
|
+
await this.respondToServerRequest(envelope.id, undefined, {
|
|
392
|
+
code: -32000,
|
|
393
|
+
message: error instanceof Error ? error.message : String(error),
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
catch (writeError) {
|
|
397
|
+
this.logger.log({
|
|
398
|
+
event: "transport.server_request_error_write_failed",
|
|
399
|
+
error: writeError instanceof Error ? writeError.message : String(writeError),
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
if (result === NO_SERVER_RESPONSE)
|
|
405
|
+
return;
|
|
406
|
+
try {
|
|
407
|
+
await this.respondToServerRequest(envelope.id, result, null);
|
|
408
|
+
this.notifyServerRequestResponseDelivery(envelope.id, { delivered: true });
|
|
409
|
+
}
|
|
410
|
+
catch (writeError) {
|
|
411
|
+
const error = writeError instanceof Error ? writeError : new Error(String(writeError));
|
|
412
|
+
this.logger.log({
|
|
413
|
+
event: "transport.server_request_write_failed",
|
|
414
|
+
error: error.message,
|
|
415
|
+
});
|
|
416
|
+
this.notifyServerRequestResponseDelivery(envelope.id, {
|
|
417
|
+
delivered: false,
|
|
418
|
+
error,
|
|
369
419
|
});
|
|
370
420
|
}
|
|
371
421
|
}
|
|
372
|
-
respondToServerRequest(id, result, error) {
|
|
422
|
+
async respondToServerRequest(id, result, error) {
|
|
373
423
|
const envelope = error
|
|
374
424
|
? { jsonrpc: "2.0", id, error }
|
|
375
425
|
: { jsonrpc: "2.0", id, result };
|
|
426
|
+
await this.writeLine(envelope);
|
|
427
|
+
}
|
|
428
|
+
notifyServerRequestResponseDelivery(requestId, result) {
|
|
376
429
|
try {
|
|
377
|
-
this.
|
|
430
|
+
this.onServerRequestResponseDelivery?.(requestId, result);
|
|
378
431
|
}
|
|
379
|
-
catch (
|
|
432
|
+
catch (error) {
|
|
380
433
|
this.logger.log({
|
|
381
|
-
event: "transport.
|
|
382
|
-
error:
|
|
434
|
+
event: "transport.server_request_delivery_handler_failed",
|
|
435
|
+
error: error instanceof Error ? error.message : String(error),
|
|
383
436
|
});
|
|
384
437
|
}
|
|
385
438
|
}
|
|
@@ -28,7 +28,7 @@ export declare class WsRpcChannel implements RpcChannel {
|
|
|
28
28
|
}) => void): void;
|
|
29
29
|
isOpen(): boolean;
|
|
30
30
|
start(): Promise<void>;
|
|
31
|
-
send(line: string): void
|
|
31
|
+
send(line: string): Promise<void>;
|
|
32
32
|
stop(signal?: NodeJS.Signals): Promise<void>;
|
|
33
33
|
private emitClose;
|
|
34
34
|
private connectWithRetry;
|
|
@@ -37,7 +37,7 @@ export declare class WsRpcChannel implements RpcChannel {
|
|
|
37
37
|
/**
|
|
38
38
|
* Connect-only {@link RpcChannel}: attaches an ADDITIONAL client to an app-server
|
|
39
39
|
* someone else already started (a {@link WsRpcChannel}'s `url`) — no spawn. This
|
|
40
|
-
* is how rynx runs
|
|
40
|
+
* is how rynx runs reference implementation's multi-connection codex-native model: the backend
|
|
41
41
|
* client owns the app-server + drives injection, while a SEPARATE forwarder
|
|
42
42
|
* connection `thread/resume`s the same thread to subscribe to its item/turn
|
|
43
43
|
* notifications (verified: codex delivers a thread's items to every connection
|
|
@@ -65,7 +65,7 @@ export declare class ExternalWsChannel implements RpcChannel {
|
|
|
65
65
|
}) => void): void;
|
|
66
66
|
isOpen(): boolean;
|
|
67
67
|
start(): Promise<void>;
|
|
68
|
-
send(line: string): void
|
|
68
|
+
send(line: string): Promise<void>;
|
|
69
69
|
stop(): Promise<void>;
|
|
70
70
|
private emitClose;
|
|
71
71
|
private connect;
|