@rynx-ai/runtime 0.1.0 → 0.1.9

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.
Files changed (53) hide show
  1. package/dist/claude/executor.d.ts +3 -5
  2. package/dist/claude/executor.js +3 -5
  3. package/dist/claude/native-bridge.d.ts +74 -17
  4. package/dist/claude/native-bridge.js +225 -30
  5. package/dist/claude/native-hook-main.js +291 -39
  6. package/dist/claude/native-hooks.d.ts +3 -2
  7. package/dist/claude/native-hooks.js +15 -6
  8. package/dist/claude/native-integration.d.ts +78 -5
  9. package/dist/claude/native-integration.js +417 -26
  10. package/dist/claude/settings.d.ts +8 -0
  11. package/dist/claude/settings.js +50 -0
  12. package/dist/claude/transcript.d.ts +2 -2
  13. package/dist/claude/transcript.js +3 -3
  14. package/dist/codex/rollout-synth.js +1 -1
  15. package/dist/codex-app-server/client.d.ts +26 -40
  16. package/dist/codex-app-server/client.js +1128 -99
  17. package/dist/codex-app-server/forwarder.d.ts +7 -7
  18. package/dist/codex-app-server/forwarder.js +11 -5
  19. package/dist/codex-app-server/mapping.d.ts +1 -1
  20. package/dist/codex-app-server/mapping.js +27 -2
  21. package/dist/codex-app-server/protocol.d.ts +238 -4
  22. package/dist/codex-app-server/transport.d.ts +20 -5
  23. package/dist/codex-app-server/transport.js +93 -40
  24. package/dist/codex-app-server/ws-channel.d.ts +3 -3
  25. package/dist/codex-app-server/ws-channel.js +23 -7
  26. package/dist/codex-child-env.js +33 -0
  27. package/dist/codex-home.d.ts +6 -6
  28. package/dist/codex-home.js +8 -9
  29. package/dist/codex-session-store.d.ts +2 -1
  30. package/dist/host.d.ts +34 -33
  31. package/dist/host.js +531 -91
  32. package/dist/index.d.ts +4 -3
  33. package/dist/index.js +1 -1
  34. package/dist/interactions.d.ts +61 -0
  35. package/dist/interactions.js +236 -0
  36. package/dist/models-catalog.d.ts +1 -1
  37. package/dist/models-catalog.js +1 -1
  38. package/dist/runner/child.d.ts +9 -1
  39. package/dist/runner/child.js +93 -15
  40. package/dist/runner/manager.d.ts +59 -10
  41. package/dist/runner/manager.js +385 -41
  42. package/dist/runner/protocol.d.ts +18 -7
  43. package/dist/runner-main.js +9 -6
  44. package/dist/runtime-status.js +1 -1
  45. package/dist/terminal/claude-tui.d.ts +8 -3
  46. package/dist/terminal/claude-tui.js +6 -2
  47. package/dist/terminal/codex-tui.d.ts +3 -3
  48. package/dist/terminal/codex-tui.js +1 -1
  49. package/dist/terminal/registry.d.ts +1 -1
  50. package/dist/terminal/registry.js +1 -1
  51. package/dist/terminal/tmux.d.ts +6 -6
  52. package/dist/terminal/tmux.js +10 -10
  53. package/package.json +3 -3
@@ -0,0 +1,50 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ function isRecord(value) {
5
+ return value !== null && typeof value === "object" && !Array.isArray(value);
6
+ }
7
+ function readSettings(path) {
8
+ try {
9
+ const value = JSON.parse(readFileSync(path, "utf8"));
10
+ return isRecord(value) ? value : {};
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ }
16
+ function mergeSettings(base, override) {
17
+ const merged = { ...base };
18
+ for (const [key, value] of Object.entries(override)) {
19
+ const current = merged[key];
20
+ merged[key] = isRecord(current) && isRecord(value)
21
+ ? mergeSettings(current, value)
22
+ : value;
23
+ }
24
+ return merged;
25
+ }
26
+ /**
27
+ * Build the single explicit settings document for a rynx-managed Claude
28
+ * Session. Host/project settings still provide auth, env and permission policy,
29
+ * but their plugins and hooks are excluded: rynx must be the only owner of
30
+ * PermissionRequest and AskUserQuestion, and Agent Skills arrive only through
31
+ * the selected `--plugin-dir`.
32
+ */
33
+ export function buildManagedClaudeSettings(cwd, rynxSettings, configDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join(homedir(), ".claude")) {
34
+ const sources = [
35
+ join(configDir, "settings.json"),
36
+ join(cwd, ".claude", "settings.json"),
37
+ join(cwd, ".claude", "settings.local.json"),
38
+ ];
39
+ let inherited = {};
40
+ for (const source of sources)
41
+ inherited = mergeSettings(inherited, readSettings(source));
42
+ // These sources can register competing interaction owners or host Skills.
43
+ // Rynx injects its own hooks/status line below and selected Skills separately.
44
+ delete inherited.hooks;
45
+ delete inherited.statusLine;
46
+ delete inherited.disableAllHooks;
47
+ delete inherited.enabledPlugins;
48
+ delete inherited.extraKnownMarketplaces;
49
+ return mergeSettings(inherited, rynxSettings);
50
+ }
@@ -45,10 +45,10 @@ export declare function parseTranscriptRecord(record: unknown, opts?: ParseTrans
45
45
  /**
46
46
  * Whether a transcript is a `/fork` (branch) of another session: claude stamps a
47
47
  * `forkedFrom: { sessionId }` marker in an early record pointing at the source
48
- * session (omnigent's `transcript_has_forked_from_marker`). Used to distinguish a
48
+ * session (reference implementation's `transcript_has_forked_from_marker`). Used to distinguish a
49
49
  * fork from an ordinary `resume` (both arrive as `SessionStart source="resume"`).
50
50
  * Scans only the head of the file (the marker lands up front). NOTE: unverified on
51
- * this host — no local transcript carries the marker — so it follows omnigent's shape.
51
+ * this host — no local transcript carries the marker — so it follows reference implementation's shape.
52
52
  */
53
53
  export declare function transcriptHasForkedFrom(path: string, currentSessionId?: string): boolean;
54
54
  /**
@@ -2,7 +2,7 @@
2
2
  * Claude Code transcript reader (Phase E, claude-native). Claude Code appends a
3
3
  * JSONL transcript per session at
4
4
  * `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`; the interactive TUI's
5
- * stdout is a full-screen UI, so — like omnigent's claude-native — we take the
5
+ * stdout is a full-screen UI, so — like reference implementation's claude-native — we take the
6
6
  * structured story from this file, not the PTY. Each `user`/`assistant` record
7
7
  * wraps an Anthropic message (`{ role, content: [blocks] }`), so we map its
8
8
  * blocks to the same typed {@link AgentEvent}s the headless executor emits.
@@ -173,10 +173,10 @@ function isObject(value) {
173
173
  /**
174
174
  * Whether a transcript is a `/fork` (branch) of another session: claude stamps a
175
175
  * `forkedFrom: { sessionId }` marker in an early record pointing at the source
176
- * session (omnigent's `transcript_has_forked_from_marker`). Used to distinguish a
176
+ * session (reference implementation's `transcript_has_forked_from_marker`). Used to distinguish a
177
177
  * fork from an ordinary `resume` (both arrive as `SessionStart source="resume"`).
178
178
  * Scans only the head of the file (the marker lands up front). NOTE: unverified on
179
- * this host — no local transcript carries the marker — so it follows omnigent's shape.
179
+ * this host — no local transcript carries the marker — so it follows reference implementation's shape.
180
180
  */
181
181
  export function transcriptHasForkedFrom(path, currentSessionId) {
182
182
  let text;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Synthesize a codex rollout file from rynx's canonical session log, so
3
3
  * `codex --remote resume <threadId>` works when the local rollout is missing
4
- * (fork / worktree / cross-machine). Ports omnigent's
4
+ * (fork / worktree / cross-machine). Ports reference implementation's
5
5
  * `_ensure_local_codex_resume_rollout`, adapted to rynx: the daemon (control-api)
6
6
  * has BOTH the session items (`SessionLogStore`) and — since the private CODEX_HOME
7
7
  * is a deterministic uid-scoped path — the app-server's rollout dir, so this runs
@@ -1,5 +1,7 @@
1
+ import type { SessionInteractionResolution } from "@rynx-ai/core";
1
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
3
  import { CodexAppServerTransport, type CodexAppServerProcessSpawner, type RpcChannel, type TransportLogger } from "./transport.js";
4
+ import type { ResolveInteractionResult, RuntimeInteractionListener } from "../interactions.js";
3
5
  export type ApprovalDecisionPolicy = "auto-approve-session" | "auto-decline" | "auto-cancel";
4
6
  export type CodexNotificationListener = (method: string, params: unknown) => void;
5
7
  export interface CodexAppServerClientOptions {
@@ -16,36 +18,21 @@ export interface CodexAppServerClientOptions {
16
18
  * prompting a human.
17
19
  */
18
20
  approvalDecisionPolicy?: ApprovalDecisionPolicy;
19
- /**
20
- * Surface approvals to a user instead of auto-deciding. When true and an
21
- * approval listener is set, each request blocks until {@link
22
- * CodexAppServerClient.resolveApproval} is called (or a timeout falls back to
23
- * {@link approvalDecisionPolicy}).
24
- */
25
- interactiveApprovals?: boolean;
26
- }
27
- /** A codex approval decision (superset of exec + patch decision enums). */
28
- export type ApprovalDecision = "acceptForSession" | "accept" | "decline" | "cancel";
29
- /** A pending approval surfaced to the user, awaiting {@link CodexAppServerClient.resolveApproval}. */
30
- export interface ApprovalRequest {
31
- approvalId: string;
32
- kind: "exec" | "patch";
33
- command?: string;
34
- cwd?: string;
35
- diff?: string;
36
21
  }
37
22
  export declare class CodexAppServerClient {
38
23
  readonly transport: CodexAppServerTransport;
39
24
  private readonly logger;
40
25
  private readonly clientInfo;
41
26
  private readonly approvalDecisionPolicy;
42
- private readonly interactiveApprovals;
43
27
  private readonly channel;
44
28
  private readonly notificationSubscribers;
45
- private approvalListener;
46
- private readonly pendingApprovals;
29
+ private interactionListener;
30
+ private connectionListener;
31
+ private connectionState;
32
+ private readonly pendingInteractions;
33
+ private readonly settledInteractions;
47
34
  private initializeResponse;
48
- constructor({ spawner, channel, logger, clientInfo, approvalDecisionPolicy, interactiveApprovals, }: CodexAppServerClientOptions);
35
+ constructor({ spawner, channel, logger, clientInfo, approvalDecisionPolicy, }: CodexAppServerClientOptions);
49
36
  /**
50
37
  * The multi-client endpoint a `codex --remote` TUI can attach to, when this
51
38
  * client runs over a {@link WsRpcChannel}. `undefined` for the default stdio
@@ -101,27 +88,26 @@ export declare class CodexAppServerClient {
101
88
  private dispatchNotification;
102
89
  private handleServerRequest;
103
90
  /**
104
- * Register the listener that surfaces interactive approval requests. Set by
105
- * the executor for the duration of a run so requests reach its event stream
106
- * (and thus the web / Lark approval card).
107
- */
108
- setApprovalRequestListener(listener: ((request: ApprovalRequest) => void) | null): void;
109
- /**
110
- * Deliver a user's decision for a pending interactive approval. Returns false
111
- * if the approval id is unknown (already resolved, timed out, or auto-decided).
112
- */
113
- resolveApproval(approvalId: string, decision: ApprovalDecision): boolean;
114
- /**
115
- * Interactive approval path: surface the request and block the codex
116
- * server-request until the user decides (or the timeout falls back to the
117
- * auto policy). When interactive approvals are off, decide immediately.
91
+ * Register the provider-neutral interaction listener. A request is inserted
92
+ * into the pending map before the listener is invoked, so a synchronous
93
+ * resolver still wins correctly.
118
94
  */
119
- private handleApproval;
95
+ setInteractionListener(listener: RuntimeInteractionListener | null): void;
96
+ /** Observe the underlying app-server connection independently from any one
97
+ * interaction. A disconnected client that never received the duplicate
98
+ * native request still has to count as unavailable during host failover. */
99
+ setConnectionListener(listener: ((state: "connected" | "disconnected") => void) | null): void;
100
+ private setConnectionState;
101
+ resolveInteraction(interactionId: string, resolution: SessionInteractionResolution): ResolveInteractionResult;
102
+ /** Cancel every pending request owned by this connection without replying. */
103
+ cancelInteractions(reason?: string): void;
104
+ private handleInteraction;
120
105
  private autoApprovalDecision;
121
- private respondCommandApproval;
122
- private respondFileChangeApproval;
123
- private declineDecisionForApprovals;
124
- private respondNotSupported;
106
+ private resolveByProvider;
107
+ private handleServerRequestResponseDelivery;
108
+ private cancelPendingInteractions;
109
+ private emitInteraction;
110
+ private rememberSettled;
125
111
  }
126
112
  export interface BuildSandboxPolicyOptions {
127
113
  mode: SandboxMode;