@rynx-ai/daemon 0.1.11-beta.1 → 0.1.11-beta.3

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 (148) hide show
  1. package/bundled-plugins/.rynx-plugin/marketplace.json +10 -0
  2. package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +37 -0
  3. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.d.ts +48 -0
  4. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +54 -0
  5. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.d.ts +85 -0
  6. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.js +333 -0
  7. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.d.ts +109 -0
  8. package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.js +347 -0
  9. package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +95 -0
  10. package/bundled-plugins/plugins/lark/dist/host-adapters.js +504 -0
  11. package/bundled-plugins/plugins/lark/dist/index.d.ts +7 -0
  12. package/bundled-plugins/plugins/lark/dist/index.js +7 -0
  13. package/bundled-plugins/plugins/lark/dist/lark-card-stream.d.ts +363 -0
  14. package/bundled-plugins/plugins/lark/dist/lark-card-stream.js +1335 -0
  15. package/bundled-plugins/plugins/lark/dist/lark-config-store.d.ts +30 -0
  16. package/bundled-plugins/plugins/lark/dist/lark-config-store.js +135 -0
  17. package/bundled-plugins/plugins/lark/dist/lark-content.d.ts +30 -0
  18. package/bundled-plugins/plugins/lark/dist/lark-content.js +138 -0
  19. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.d.ts +65 -0
  20. package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.js +106 -0
  21. package/bundled-plugins/plugins/lark/dist/lark-diff-card.d.ts +25 -0
  22. package/bundled-plugins/plugins/lark/dist/lark-diff-card.js +58 -0
  23. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.d.ts +16 -0
  24. package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.js +141 -0
  25. package/bundled-plugins/plugins/lark/dist/lark-resume-card.d.ts +31 -0
  26. package/bundled-plugins/plugins/lark/dist/lark-resume-card.js +105 -0
  27. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.d.ts +18 -0
  28. package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.js +95 -0
  29. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.d.ts +8 -0
  30. package/bundled-plugins/plugins/lark/dist/lark-sdk/client.js +32 -0
  31. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.d.ts +1 -0
  32. package/bundled-plugins/plugins/lark/dist/lark-sdk/index.js +1 -0
  33. package/bundled-plugins/plugins/lark/dist/lark-sender.d.ts +114 -0
  34. package/bundled-plugins/plugins/lark/dist/lark-sender.js +323 -0
  35. package/bundled-plugins/plugins/lark/dist/lark-session-store.d.ts +13 -0
  36. package/bundled-plugins/plugins/lark/dist/lark-session-store.js +14 -0
  37. package/bundled-plugins/plugins/lark/dist/lark-settings-card.d.ts +55 -0
  38. package/bundled-plugins/plugins/lark/dist/lark-settings-card.js +158 -0
  39. package/bundled-plugins/plugins/lark/dist/lark-setup.d.ts +39 -0
  40. package/bundled-plugins/plugins/lark/dist/lark-setup.js +103 -0
  41. package/bundled-plugins/plugins/lark/dist/lark.d.ts +507 -0
  42. package/bundled-plugins/plugins/lark/dist/lark.js +1977 -0
  43. package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
  44. package/bundled-plugins/plugins/lark/dist/runtime.js +131313 -0
  45. package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
  46. package/bundled-plugins/plugins/lark/dist/settings.js +126958 -0
  47. package/bundled-plugins/plugins/lark/package.json +57 -0
  48. package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +1 -0
  49. package/bundled-plugins/plugins/lark/ui/assets/index-D9N-AkLX.js +201 -0
  50. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  51. package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  52. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  53. package/bundled-plugins/plugins/lark/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  54. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  55. package/bundled-plugins/plugins/lark/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  56. package/bundled-plugins/plugins/lark/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  57. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  58. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  59. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  60. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  61. package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  62. package/bundled-plugins/plugins/lark/ui/index.html +13 -0
  63. package/dist/browser-artifact-management.d.ts +4 -1
  64. package/dist/browser-artifact-management.js +18 -10
  65. package/dist/browser-artifacts.d.ts +1 -0
  66. package/dist/browser-artifacts.js +1 -0
  67. package/dist/bundled-plugins.d.ts +15 -0
  68. package/dist/bundled-plugins.js +134 -0
  69. package/dist/chrome-for-testing-runtime.d.ts +27 -0
  70. package/dist/chrome-for-testing-runtime.js +193 -0
  71. package/dist/chrome-for-testing-store.d.ts +2 -0
  72. package/dist/chrome-for-testing-store.js +9 -0
  73. package/dist/control-deps.js +2 -79
  74. package/dist/daemon-server.d.ts +7 -1
  75. package/dist/daemon-server.js +299 -84
  76. package/dist/db.js +147 -124
  77. package/dist/entry-path.d.ts +3 -0
  78. package/dist/entry-path.js +12 -0
  79. package/dist/index-daemon.js +3 -66
  80. package/dist/lifecycle.d.ts +12 -0
  81. package/dist/lifecycle.js +16 -0
  82. package/dist/maintenance-lease.d.ts +39 -0
  83. package/dist/maintenance-lease.js +250 -0
  84. package/dist/migrations/cleanup-legacy-sessions.d.ts +1 -1
  85. package/dist/migrations/cleanup-legacy-sessions.js +33 -15
  86. package/dist/official-plugins.d.ts +2 -0
  87. package/dist/official-plugins.js +2 -0
  88. package/dist/plugin-cli.d.ts +1 -3
  89. package/dist/plugin-cli.js +56 -45
  90. package/dist/plugin-host-leases.d.ts +4 -0
  91. package/dist/plugin-host-leases.js +7 -0
  92. package/dist/plugin-host-rpc.d.ts +60 -4
  93. package/dist/plugin-host-rpc.js +300 -50
  94. package/dist/plugin-installer.d.ts +24 -55
  95. package/dist/plugin-installer.js +258 -412
  96. package/dist/plugin-management-service.d.ts +30 -23
  97. package/dist/plugin-management-service.js +290 -105
  98. package/dist/plugin-marketplace.d.ts +35 -0
  99. package/dist/plugin-marketplace.js +299 -0
  100. package/dist/plugin-package.d.ts +22 -24
  101. package/dist/plugin-package.js +520 -571
  102. package/dist/plugin-skill-context.d.ts +4 -0
  103. package/dist/plugin-skill-context.js +35 -0
  104. package/dist/plugin-store.d.ts +36 -37
  105. package/dist/plugin-store.js +140 -92
  106. package/dist/plugin-supervisor.d.ts +50 -125
  107. package/dist/plugin-supervisor.js +682 -1030
  108. package/dist/pm2.d.ts +11 -2
  109. package/dist/pm2.js +130 -28
  110. package/dist/process.d.ts +12 -0
  111. package/dist/process.js +91 -0
  112. package/dist/runtime-project-store.d.ts +9 -0
  113. package/dist/runtime-project-store.js +83 -0
  114. package/dist/session-fallback-workspace.d.ts +22 -0
  115. package/dist/session-fallback-workspace.js +120 -0
  116. package/dist/session-fork-store.d.ts +41 -0
  117. package/dist/session-fork-store.js +288 -0
  118. package/dist/session-launch-operations.d.ts +23 -2
  119. package/dist/session-launch-operations.js +48 -7
  120. package/dist/session-meta-store.d.ts +7 -9
  121. package/dist/session-meta-store.js +28 -49
  122. package/dist/setup-service.d.ts +20 -0
  123. package/dist/setup-service.js +38 -0
  124. package/package.json +36 -20
  125. package/dist/browser-cli-args.d.ts +0 -20
  126. package/dist/browser-cli-args.js +0 -100
  127. package/dist/channel-store.d.ts +0 -22
  128. package/dist/channel-store.js +0 -65
  129. package/dist/cli.d.ts +0 -2
  130. package/dist/cli.js +0 -732
  131. package/dist/control-client.d.ts +0 -122
  132. package/dist/control-client.js +0 -855
  133. package/dist/control-endpoint.d.ts +0 -14
  134. package/dist/control-endpoint.js +0 -124
  135. package/dist/desktop-browser-host-client.d.ts +0 -35
  136. package/dist/desktop-browser-host-client.js +0 -415
  137. package/dist/instance-store.d.ts +0 -38
  138. package/dist/instance-store.js +0 -85
  139. package/dist/plugin-install-restart.d.ts +0 -13
  140. package/dist/plugin-install-restart.js +0 -24
  141. package/dist/plugin-runtime-control.d.ts +0 -2
  142. package/dist/plugin-runtime-control.js +0 -37
  143. package/dist/registry.d.ts +0 -10
  144. package/dist/registry.js +0 -52
  145. package/dist/setup.d.ts +0 -20
  146. package/dist/setup.js +0 -374
  147. package/dist/update.d.ts +0 -25
  148. package/dist/update.js +0 -133
@@ -9,10 +9,16 @@ export declare function daemonActivityBlocksShutdown(activity: DaemonActivitySna
9
9
  export interface StartRynxDaemonServerOptions {
10
10
  config?: AppConfig;
11
11
  warn?: (message: string) => void;
12
+ /** Process-owner callback after every daemon resource and owner lease has
13
+ * shut down successfully. Library callers normally leave this unset. */
14
+ onShutdownComplete?: () => void;
12
15
  /** Test/deployment seam; failures roll back the already-listening server. */
13
16
  writeEndpoint?: (config: AppConfig, managementToken: string) => Promise<void>;
14
17
  /** Test seam for owner-lock failure injection. Production always uses the RYNX_HOME lease. */
15
18
  acquireOwner?: () => Promise<DaemonOwnerLease>;
19
+ /** Runs after the exclusive daemon-owner lock is acquired and before plugin
20
+ * records are loaded or runners are started. */
21
+ preparePlugins?: () => Promise<void>;
16
22
  /** Test/deployment seam for the always-available Direct Runtime socket. */
17
23
  loadDirectConfig?: () => Promise<DirectRuntimeConfig>;
18
24
  /** Test seam for listener lifecycle and bind failures. */
@@ -22,7 +28,7 @@ export interface StartRynxDaemonServerOptions {
22
28
  /** Test seam for the App-only embedded Chromium process supervisor. */
23
29
  createBrowserHostSupervisor?: (options: AppBrowserHostSupervisorOptions) => Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart" | "stop">;
24
30
  }
25
- export declare function startRynxDaemonServer({ config, warn, writeEndpoint, acquireOwner, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
31
+ export declare function startRynxDaemonServer({ config, warn, onShutdownComplete, writeEndpoint, acquireOwner, preparePlugins, loadDirectConfig, startDirectServer, createChromeInspectionManager, createBrowserHostSupervisor, }?: StartRynxDaemonServerOptions): Promise<Awaited<ReturnType<typeof startServer>>>;
26
32
  export declare function createSupervisedAppSessionBrowserHost(desktop: SessionBrowserHost, supervisor: Pick<AppBrowserHostSupervisor, "ensureConnected" | "restart">): SessionBrowserHost;
27
33
  /**
28
34
  * Resolve the authenticated semantic target inside its owning Runtime and
@@ -6,26 +6,30 @@
6
6
  * session registry. Keeping that wiring here avoids a second app-specific fork.
7
7
  */
8
8
  import { randomBytes, randomUUID } from "node:crypto";
9
- import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
9
+ import { mkdir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
10
10
  import { createRequire } from "node:module";
11
11
  import { fileURLToPath } from "node:url";
12
12
  import { dirname, join } from "node:path";
13
13
  import WebSocket from "ws";
14
- import { loadConfig, rynxHome } from "@rynx-ai/core";
14
+ import { CloseableAdmissionGate, loadConfig, resolveExecutionSnapshot, rynxHome, sessionWorkspaceSnapshotSchema, } from "@rynx-ai/core";
15
15
  import { LOCAL_MANAGEMENT_PROTOCOL, } from "@rynx-ai/protocol/control";
16
16
  import { REMOTE_RUNTIME_CORE_PROTOCOL } from "@rynx-ai/protocol/remote-runtime";
17
17
  import { RUNTIME_BROWSER_INSPECT_MAX_MESSAGE_BYTES, RUNTIME_BROWSER_INSPECT_SEMANTIC_CAPABILITY, } from "@rynx-ai/protocol/runtime-browser-inspect";
18
18
  import { RUNTIME_BROWSER_SURFACE_SEMANTIC_CAPABILITY } from "@rynx-ai/protocol/runtime-browser-surface";
19
19
  import { RUNTIME_EMULATOR_SURFACE_SEMANTIC_CAPABILITY } from "@rynx-ai/protocol/runtime-emulator-surface";
20
- import { createDaemonRuntimeHost, createDesktopFirstSessionBrowserHost, createRunnerSessionTerminalHost, createSessionRuntimeServices, DesktopBrowserHostAbsentError, DesktopBrowserHostRegistry, DirectRuntimeConnectionHub, DirectRuntimeBrowserInspectHostError, MachineSessionService, RemoteRuntimeDispatcher, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceCoordinator, SessionEmulatorService, startServer, startDirectRuntimeServer, } from "@rynx-ai/server";
20
+ import { createDaemonRuntimeHost, createDesktopFirstSessionBrowserHost, createRunnerSessionTerminalHost, createSessionRuntimeServices, DesktopBrowserHostAbsentError, DesktopBrowserHostRegistry, DirectRuntimeConnectionHub, DirectRuntimeBrowserInspectHostError, MachineSessionService, MachineSessionServiceFailure, listRuntimeModels, RemoteRuntimeDispatcher, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceCoordinator, SessionEmulatorService, startServer, startDirectRuntimeServer, } from "@rynx-ai/server";
21
21
  import { APP_BROWSER_HOST_BOOTSTRAP_TYPE, AppBrowserHostSupervisor, parseAppBrowserHostLaunchConfig, } from "./app-browser-host-supervisor.js";
22
22
  import { buildControlDeps } from "./control-deps.js";
23
23
  import { createBrowserArtifactManagementService } from "./browser-artifact-management.js";
24
24
  import { createDaemonBuildStatus } from "./daemon-build-status.js";
25
+ import { MaintenanceLeaseStore } from "./maintenance-lease.js";
25
26
  import { BrowserRunnerSessionContextProvider } from "./browser-runner-session-context.js";
27
+ import { removeLegacyPluginSkillSnapshot, snapshotEnabledPluginSkillSources, } from "./plugin-skill-context.js";
28
+ import { installedDaemonVersion } from "./entry-path.js";
26
29
  import { isChromeForTestingHostSupported } from "./chrome-for-testing-store.js";
27
30
  import { ChromeInspectionManager, } from "./chrome-inspection-manager.js";
28
31
  import { createHeadlessBrowserHost } from "./headless-browser-host.js";
32
+ import { db } from "./db.js";
29
33
  import { acquireDaemonOwner } from "./daemon-owner.js";
30
34
  import { DaemonDirectRuntimeAuthenticator } from "./direct-runtime-authenticator.js";
31
35
  import { DurableEmbeddedBrowserProfileHost } from "./embedded-browser-profile-cleanup.js";
@@ -34,13 +38,15 @@ import { isDaemonDirectRuntimeHostingSupported, loadOrCreateDaemonDirectRuntimeE
34
38
  import { cleanupLegacySessions } from "./migrations/cleanup-legacy-sessions.js";
35
39
  import { createMaintenanceManagementService } from "./maintenance-management.js";
36
40
  import { PluginManagementService } from "./plugin-management-service.js";
41
+ import { addPluginMarketplace, listPluginMarketplaces, refreshPluginMarketplace, removePluginMarketplace, } from "./plugin-marketplace.js";
37
42
  import { PluginSupervisor } from "./plugin-supervisor.js";
38
43
  import { pruneInactivePluginInstallations } from "./plugin-installer.js";
39
44
  import { runPluginCliCommand } from "./plugin-cli.js";
40
- import { getPlugin, isRunnablePluginRecord, listEnabledPlugins } from "./plugin-store.js";
41
- import { loadPlugins, resolveChannelInstances } from "./registry.js";
45
+ import { listPlugins } from "./plugin-store.js";
42
46
  import { SqliteSessionLogStore } from "./session-log-store.js";
47
+ import { SqliteSessionForkStore } from "./session-fork-store.js";
43
48
  import { SqliteSessionResourceStore } from "./session-resource-store.js";
49
+ import { createSessionFallbackWorkspace, discardSessionFallbackWorkspace, } from "./session-fallback-workspace.js";
44
50
  import { SessionBrowserCapabilityStore } from "./session-browser-capability-store.js";
45
51
  import { SqliteSessionEmulatorBindingStore } from "./session-emulator-binding-store.js";
46
52
  import { sessionRegistry } from "./session-meta-store.js";
@@ -52,6 +58,7 @@ import { DaemonRuntimeConnectionManager } from "./remote-runtime-connection-mana
52
58
  import { DaemonRuntimeTargetControl } from "./remote-runtime-target-control.js";
53
59
  import { RemoteRuntimeTargetStore } from "./remote-runtime-target-store.js";
54
60
  import { ProviderCliService } from "./provider-cli-service.js";
61
+ import { SqliteRuntimeProjectStore } from "./runtime-project-store.js";
55
62
  const PLUGIN_CLI_OUTPUT_MAX_BYTES = 256 * 1024;
56
63
  const BROWSER_INSPECT_OPEN_TIMEOUT_MS = 10_000;
57
64
  const BROWSER_INSPECT_CLOSE_TIMEOUT_MS = 1_000;
@@ -59,8 +66,78 @@ const BROWSER_INSPECT_MAX_QUEUED_BYTES = 32 * 1024 * 1024;
59
66
  const DIRECT_RUNTIME_HOSTING_UNSUPPORTED = "Direct Runtime hosting is supported only on macOS and Linux until protected server-key storage is available on other operating systems";
60
67
  const require = createRequire(import.meta.url);
61
68
  const DAEMON_PACKAGE_VERSION = readDaemonPackageVersion();
69
+ async function resolveSessionExecutionSnapshot(input) {
70
+ return {
71
+ ...await resolveExecutionSnapshot({
72
+ ...input,
73
+ resolveDefaultModel: async (provider) => {
74
+ const models = await listRuntimeModels(input.config, provider);
75
+ return models?.data.find((candidate) => candidate.isDefault)?.model;
76
+ },
77
+ }),
78
+ pluginSkills: snapshotEnabledPluginSkillSources(),
79
+ };
80
+ }
81
+ async function resolveSessionWorkspace(projects, projectId, managedCwd, workspace) {
82
+ if (workspace) {
83
+ return sessionWorkspaceSnapshotSchema.parse(workspace);
84
+ }
85
+ if (projectId) {
86
+ const project = await projects.get(projectId);
87
+ if (!project) {
88
+ throw new MachineSessionServiceFailure("not_found", "project not found");
89
+ }
90
+ await assertWorkspaceDirectories(project.directories);
91
+ return sessionWorkspaceSnapshotSchema.parse({
92
+ cwd: project.primary,
93
+ allowedDirs: [
94
+ project.primary,
95
+ ...project.directories.filter((directory) => directory !== project.primary),
96
+ ],
97
+ });
98
+ }
99
+ if (managedCwd) {
100
+ let canonical;
101
+ try {
102
+ canonical = await realpath(managedCwd);
103
+ await assertWorkspaceDirectories([canonical]);
104
+ }
105
+ catch (error) {
106
+ if (error instanceof MachineSessionServiceFailure)
107
+ throw error;
108
+ throw new MachineSessionServiceFailure("failed_precondition", "Session working directory is unavailable");
109
+ }
110
+ return sessionWorkspaceSnapshotSchema.parse({
111
+ cwd: canonical,
112
+ allowedDirs: [canonical],
113
+ });
114
+ }
115
+ return await createSessionFallbackWorkspace();
116
+ }
117
+ async function assertWorkspaceDirectories(directories) {
118
+ try {
119
+ await Promise.all(directories.map(async (directory) => {
120
+ if (!(await stat(directory)).isDirectory())
121
+ throw new Error(directory);
122
+ }));
123
+ }
124
+ catch {
125
+ throw new MachineSessionServiceFailure("failed_precondition", "one or more Project directories are unavailable");
126
+ }
127
+ }
128
+ function toMarketplaceItem(record) {
129
+ return {
130
+ id: record.marketplaceId,
131
+ source: record.sourceType,
132
+ builtIn: record.builtIn,
133
+ plugins: record.catalog.plugins.map((plugin) => ({ id: plugin.id })),
134
+ resolvedRevision: record.resolvedRevision,
135
+ refreshedAt: record.refreshedAt,
136
+ };
137
+ }
62
138
  export function daemonActivityBlocksShutdown(activity) {
63
- return activity.runningTurns > 0 || activity.pendingInteractions > 0;
139
+ return activity.runningTurns > 0
140
+ || activity.pendingInteractions > 0;
64
141
  }
65
142
  function defaultWarn(message) {
66
143
  console.warn(JSON.stringify({ level: "warn", type: "channel-registry", msg: message }));
@@ -179,11 +256,24 @@ async function removeControlEndpointForCurrentProcess() {
179
256
  // Best effort only; stale endpoint files are ignored by the client fallback.
180
257
  }
181
258
  }
182
- export async function startRynxDaemonServer({ config, warn = defaultWarn, writeEndpoint = writeControlEndpoint, acquireOwner = acquireDaemonOwner, loadDirectConfig = loadDirectRuntimeConfig, startDirectServer = startDirectRuntimeServer, createChromeInspectionManager = (options) => ChromeInspectionManager.create(options), createBrowserHostSupervisor = (options) => new AppBrowserHostSupervisor(options), } = {}) {
259
+ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShutdownComplete, writeEndpoint = writeControlEndpoint, acquireOwner = acquireDaemonOwner, preparePlugins, loadDirectConfig = loadDirectRuntimeConfig, startDirectServer = startDirectRuntimeServer, createChromeInspectionManager = (options) => ChromeInspectionManager.create(options), createBrowserHostSupervisor = (options) => new AppBrowserHostSupervisor(options), } = {}) {
183
260
  const owner = await acquireOwner();
184
261
  let ownerManagedByServer = false;
185
262
  try {
263
+ await preparePlugins?.();
186
264
  const appConfig = config ?? loadConfig();
265
+ const admissionGate = new CloseableAdmissionGate();
266
+ let shutdownAccepted = false;
267
+ const maintenanceLease = new MaintenanceLeaseStore({
268
+ // Expiry restores the gate primitive for a process that outlives its
269
+ // upgrader. The independent accepted-shutdown fence below still prevents
270
+ // new work after an irreversible server shutdown has begun.
271
+ onExpired: () => admissionGate.reopen(),
272
+ });
273
+ const admissionOpen = () => !shutdownAccepted && !maintenanceLease.isActive();
274
+ const reserveAdmission = () => !admissionOpen()
275
+ ? undefined
276
+ : admissionGate.tryReserve();
187
277
  const appBrowserHostLaunch = parseAppBrowserHostLaunchConfig();
188
278
  const directHostingSupported = isDaemonDirectRuntimeHostingSupported();
189
279
  const runtimeBrowserSupported = appBrowserHostLaunch !== undefined || isChromeForTestingHostSupported();
@@ -215,11 +305,13 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
215
305
  "semantic.session.agent-options.v1",
216
306
  "semantic.session.launch-options.v2",
217
307
  "semantic.session.create.v1",
308
+ "semantic.session.fork.v1",
218
309
  "semantic.session.message.v1",
219
310
  "semantic.session.resources.v1",
220
311
  "semantic.session.message-queue.v1",
221
312
  "semantic.session.delete.v1",
222
313
  "semantic.session.interaction.v1",
314
+ "semantic.project.manage.v1",
223
315
  "semantic.provider-cli.status.v1",
224
316
  ...(runtimeBrowserSupported ? ["semantic.browser.v1"] : []),
225
317
  ...(runtimeBrowserSupported && directHostingSupported
@@ -259,8 +351,11 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
259
351
  catch (error) {
260
352
  warn(`plugin package cleanup failed: ${error instanceof Error ? error.message : String(error)}`);
261
353
  }
262
- const pluginSupervisor = new PluginSupervisor(listEnabledPlugins(), {
354
+ const pluginSupervisor = new PluginSupervisor(listPlugins(), {
263
355
  daemonInstanceId,
356
+ productVersion: controlEndpointDistributionMetadata().productVersion,
357
+ cliVersion: boundedEnvironmentMetadata(process.env.RYNX_CLI_VERSION, 128)
358
+ ?? installedDaemonVersion(),
264
359
  log: (entry) => {
265
360
  const output = entry.level === "error"
266
361
  ? console.error
@@ -270,39 +365,14 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
270
365
  output(JSON.stringify(entry));
271
366
  },
272
367
  });
273
- const pluginManagement = new PluginManagementService();
368
+ const pluginManagement = new PluginManagementService(undefined, {
369
+ supervisor: pluginSupervisor,
370
+ });
274
371
  const browserArtifactManagement = createBrowserArtifactManagementService();
275
372
  const maintenanceManagement = createMaintenanceManagementService({
276
373
  codexStorePath: appConfig.AGENT_SESSION_STORE_PATH,
277
374
  });
278
- await loadPlugins(pluginSupervisor, warn);
279
375
  logLegacyCleanup(appConfig);
280
- const reloadPluginRuntime = async (pluginId) => {
281
- const record = getPlugin(pluginId);
282
- if (!record)
283
- throw new Error(`plugin "${pluginId}" is not installed`);
284
- if (!record.enabled)
285
- throw new Error(`plugin "${pluginId}" is disabled`);
286
- if (!isRunnablePluginRecord(record)) {
287
- throw new Error(`plugin "${pluginId}" must be reinstalled before its runtime can start`);
288
- }
289
- const status = await pluginSupervisor.reloadPluginRuntime(record);
290
- await loadPlugins(pluginSupervisor, warn);
291
- return { pluginId, status };
292
- };
293
- const ensurePluginRuntime = async (pluginId) => {
294
- const record = getPlugin(pluginId);
295
- if (!record)
296
- throw new Error(`plugin "${pluginId}" is not installed`);
297
- if (!record.enabled)
298
- throw new Error(`plugin "${pluginId}" is disabled`);
299
- if (!isRunnablePluginRecord(record)) {
300
- throw new Error(`plugin "${pluginId}" must be reinstalled before its runtime can start`);
301
- }
302
- const status = await pluginSupervisor.ensurePluginRuntime(record);
303
- await loadPlugins(pluginSupervisor, warn);
304
- return { pluginId, status };
305
- };
306
376
  const invokePluginCli = async ({ pluginId, args, stdin }, { signal }) => {
307
377
  const result = await runPluginCliCommand(pluginId, args, {
308
378
  stdio: "pipe",
@@ -310,14 +380,6 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
310
380
  captureOutputBytes: PLUGIN_CLI_OUTPUT_MAX_BYTES,
311
381
  signal,
312
382
  });
313
- if (result.code === 0) {
314
- const reload = result.effects.some((effect) => effect.effect === "runtime.reload");
315
- const ensure = result.effects.some((effect) => effect.effect === "runtime.ensure");
316
- if (reload)
317
- await reloadPluginRuntime(pluginId);
318
- else if (ensure)
319
- await ensurePluginRuntime(pluginId);
320
- }
321
383
  const output = result.output;
322
384
  return {
323
385
  code: result.code,
@@ -326,6 +388,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
326
388
  truncated: Boolean(output?.stdoutTruncated || output?.stderrTruncated),
327
389
  };
328
390
  };
391
+ let machineSessions;
329
392
  const pluginRuntime = {
330
393
  bindHostServices: (services) => pluginSupervisor.bindHostServices({
331
394
  ...services,
@@ -335,10 +398,32 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
335
398
  publicKey: installationIdentity.publicKey,
336
399
  sign: installationIdentity.sign,
337
400
  },
401
+ sessionLaunch: {
402
+ resolve: async (selection) => {
403
+ const workspace = await resolveSessionWorkspace(runtimeProjects, selection.projectId, selection.managedCwd, selection.workspace);
404
+ return {
405
+ workspace,
406
+ execution: await resolveSessionExecutionSnapshot({
407
+ config: appConfig,
408
+ ...(selection.agent ? { agentName: selection.agent } : {}),
409
+ ...(selection.config ? { spec: selection.config } : {}),
410
+ ...((selection.provider || (!selection.agent && !selection.config))
411
+ ? { provider: selection.provider ?? appConfig.DEFAULT_RUNTIME }
412
+ : {}),
413
+ ...(selection.model ? { model: selection.model } : {}),
414
+ ...(selection.reasoningEffort
415
+ ? { reasoningEffort: selection.reasoningEffort }
416
+ : {}),
417
+ cwd: workspace.cwd,
418
+ }),
419
+ };
420
+ },
421
+ },
422
+ sessionFork: (input) => machineSessions.fork(input),
423
+ admissionOpen,
424
+ admissionReserve: reserveAdmission,
338
425
  }),
339
426
  startPluginRuntimes: pluginSupervisor.startPluginRuntimes.bind(pluginSupervisor),
340
- reloadPluginRuntime,
341
- ensurePluginRuntime,
342
427
  statusSnapshot: pluginSupervisor.statusSnapshot.bind(pluginSupervisor),
343
428
  dispose: pluginSupervisor.dispose.bind(pluginSupervisor),
344
429
  };
@@ -346,6 +431,8 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
346
431
  const directConnections = new DirectRuntimeConnectionHub();
347
432
  const sessionLog = new SqliteSessionLogStore();
348
433
  const sessionResources = new SqliteSessionResourceStore();
434
+ const sessionForks = new SqliteSessionForkStore();
435
+ const runtimeProjects = new SqliteRuntimeProjectStore();
349
436
  const providerClis = new ProviderCliService();
350
437
  const sessionBrowserCapabilities = new SessionBrowserCapabilityStore();
351
438
  const durableDesktopBrowserHost = new DurableEmbeddedBrowserProfileHost(desktopBrowserHosts, { warn });
@@ -357,16 +444,21 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
357
444
  config: appConfig,
358
445
  sessionLog,
359
446
  sessionContextProvider: browserRunnerSessionContexts,
447
+ admissionOpen,
448
+ admissionReserve: reserveAdmission,
360
449
  onMirrorError: (error, sessionId) => warn(`Session mirror failed for ${sessionId}: ${error instanceof Error ? error.message : String(error)}`),
361
450
  });
451
+ const sessionBrowserHost = appBrowserHostSupervisor
452
+ ? createSupervisedAppSessionBrowserHost(durableDesktopBrowserHost, appBrowserHostSupervisor)
453
+ : createDesktopFirstSessionBrowserHost(durableDesktopBrowserHost, createHeadlessBrowserHost());
362
454
  const sessionBrowsers = new SessionBrowserService({
363
- host: appBrowserHostSupervisor
364
- ? createSupervisedAppSessionBrowserHost(durableDesktopBrowserHost, appBrowserHostSupervisor)
365
- : createDesktopFirstSessionBrowserHost(durableDesktopBrowserHost, createHeadlessBrowserHost()),
455
+ host: sessionBrowserHost,
366
456
  sessions: {
367
457
  exists: async (sessionId) => Boolean(sessionRegistry.get(sessionId) ??
368
458
  await sessionRuntimeServices.sessionStore.get(sessionId)),
369
459
  },
460
+ }, {
461
+ admissionReserve: reserveAdmission,
370
462
  });
371
463
  const sessionEmulators = control.emulator
372
464
  ? new SessionEmulatorService({
@@ -422,14 +514,35 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
422
514
  sessionEmulators?.cleanupSession(sessionId),
423
515
  ]);
424
516
  },
425
- afterDelete: (sessionId) => {
517
+ afterDelete: async (sessionId) => {
426
518
  sessionBrowserCapabilities.revoke(sessionId);
519
+ removeLegacyPluginSkillSnapshot(sessionId);
520
+ await sessionRuntimeServices.sessionStore.delete(sessionId);
427
521
  },
428
522
  };
429
- const machineSessions = new MachineSessionService({
523
+ const deletedSessions = db()
524
+ .prepare("SELECT session_id AS sessionId FROM session_snapshot_cleanup ORDER BY session_id")
525
+ .all();
526
+ for (const { sessionId } of deletedSessions) {
527
+ try {
528
+ await Promise.all([
529
+ sessionBrowsers.cleanupSession(sessionId),
530
+ sessionEmulators?.cleanupSession(sessionId),
531
+ ]);
532
+ sessionResources.deleteSession(sessionId);
533
+ removeLegacyPluginSkillSnapshot(sessionId);
534
+ await sessionRuntimeServices.sessionStore.delete(sessionId);
535
+ db().prepare("DELETE FROM session_snapshot_cleanup WHERE session_id = ?").run(sessionId);
536
+ }
537
+ catch (error) {
538
+ warn(`Legacy Session artifact cleanup will retry for ${sessionId}: ${error instanceof Error ? error.message : String(error)}`);
539
+ }
540
+ }
541
+ machineSessions = new MachineSessionService({
430
542
  registry: sessionRegistry,
431
543
  log: sessionLog,
432
544
  events: sessionRuntimeServices.sessionBus,
545
+ publishEvent: sessionRuntimeServices.publishSessionEvent,
433
546
  runtimeState: sessionRuntimeServices.sessionRuntimeIndex,
434
547
  interruption: {
435
548
  interrupt: (sessionId) => sessionRuntimeServices.runnerManager.interruptLiveSession(sessionId),
@@ -440,29 +553,129 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
440
553
  },
441
554
  pendingMessages: sessionPendingMessageStore,
442
555
  resources: sessionResources,
556
+ forks: sessionForks,
443
557
  execution: {
444
558
  runtime: sessionRuntimeServices.conversationRuntime,
559
+ resolve: ({ agent, provider, model, reasoningEffort, cwd }) => resolveSessionExecutionSnapshot({
560
+ config: appConfig,
561
+ ...(agent ? { agentName: agent } : {}),
562
+ ...(provider ? { provider } : {}),
563
+ ...(model ? { model } : {}),
564
+ ...(reasoningEffort ? { reasoningEffort } : {}),
565
+ cwd,
566
+ }),
445
567
  runner: sessionRuntimeServices.runnerManager,
446
- sessionStore: sessionRuntimeServices.sessionStore,
447
568
  },
569
+ workspace: {
570
+ resolve: (projectId) => resolveSessionWorkspace(runtimeProjects, projectId),
571
+ discardUnbound: discardSessionFallbackWorkspace,
572
+ },
573
+ }, {
574
+ admissionOpen,
575
+ admissionReserve: reserveAdmission,
448
576
  });
449
577
  const directAuthenticator = new DaemonDirectRuntimeAuthenticator(installationIdentity, remoteRuntime, remoteRuntimeAccess);
450
578
  const sessionTerminals = createRunnerSessionTerminalHost({
451
579
  runnerManager: sessionRuntimeServices.runnerManager,
580
+ admissionReserve: reserveAdmission,
452
581
  resolveLiveSession: async (sessionId) => {
453
- const bound = await sessionRuntimeServices.sessionStore.get(sessionId);
454
582
  const registered = sessionRegistry.get(sessionId);
455
- const cwd = bound?.cwd ?? registered?.config?.osEnv?.cwd;
456
- if ((!bound && !registered) ||
457
- !cwd ||
583
+ if (!registered ||
458
584
  !sessionRuntimeServices.runnerManager.hasLiveSession(sessionId)) {
459
585
  return undefined;
460
586
  }
461
- return { cwd };
587
+ return { cwd: registered.workspace.cwd };
462
588
  },
463
589
  });
464
- let shutdownAccepted = false;
465
590
  let requestServerShutdown;
591
+ let shutdownAttempt;
592
+ let shutdownAttemptLeaseToken;
593
+ const shutdownIfIdle = (maintenanceLeaseToken) => {
594
+ if (maintenanceLease.isActive()) {
595
+ if (!maintenanceLeaseToken || !maintenanceLease.isHeldBy(maintenanceLeaseToken)) {
596
+ return Promise.reject(new Error("a different maintenance lease is already active"));
597
+ }
598
+ // The previous daemon may have persisted the lease and exited after
599
+ // its 202 response was lost. A replacement process holding the same
600
+ // token must perform its own close/drain/shutdown; returning accepted
601
+ // without scheduling this PID would strand the updater waiting for a
602
+ // process that can never exit.
603
+ }
604
+ if (shutdownAccepted) {
605
+ if (maintenanceLeaseToken) {
606
+ maintenanceLease.acquire(maintenanceLeaseToken);
607
+ return Promise.resolve({
608
+ outcome: "accepted",
609
+ maintenanceLease: "active",
610
+ });
611
+ }
612
+ return Promise.resolve({ outcome: "accepted" });
613
+ }
614
+ if (shutdownAttempt) {
615
+ if (maintenanceLeaseToken === shutdownAttemptLeaseToken) {
616
+ return shutdownAttempt;
617
+ }
618
+ return Promise.reject(new Error("another idle shutdown check is already in progress"));
619
+ }
620
+ shutdownAttemptLeaseToken = maintenanceLeaseToken;
621
+ const attempt = (async () => {
622
+ await admissionGate.closeAndDrain();
623
+ const runtimeActivity = sessionRuntimeServices.sessionRuntimeIndex.activitySnapshot();
624
+ const activity = {
625
+ ...runtimeActivity,
626
+ // A terminal submission keeps its own admission reservation until the
627
+ // first response or native rotation is visible. Failed process-tree
628
+ // cleanup releases the reservation but remains pinned here, so an
629
+ // idle check returns busy instead of hanging or killing hidden work.
630
+ runningTurns: runtimeActivity.runningTurns
631
+ + sessionRuntimeServices.runnerManager.pendingTerminalInputCount(),
632
+ activeTerminals: sessionTerminals.activeTerminalCount(),
633
+ activeBrowsers: sessionBrowsers.activeBrowserCount(),
634
+ };
635
+ if (daemonActivityBlocksShutdown(activity)) {
636
+ admissionGate.reopen();
637
+ return { outcome: "busy", activity };
638
+ }
639
+ const shutdown = requestServerShutdown;
640
+ if (!shutdown) {
641
+ admissionGate.reopen();
642
+ throw new Error("idle daemon shutdown was requested before server lifecycle was ready");
643
+ }
644
+ const acquiredLease = maintenanceLeaseToken
645
+ ? maintenanceLease.acquire(maintenanceLeaseToken)
646
+ : false;
647
+ shutdownAccepted = true;
648
+ try {
649
+ void shutdown().catch((error) => {
650
+ shutdownAccepted = false;
651
+ if (acquiredLease && maintenanceLeaseToken) {
652
+ maintenanceLease.release(maintenanceLeaseToken);
653
+ }
654
+ admissionGate.reopen();
655
+ warn(`idle daemon shutdown failed: ${error instanceof Error ? error.message : String(error)}`);
656
+ });
657
+ }
658
+ catch (error) {
659
+ shutdownAccepted = false;
660
+ if (acquiredLease && maintenanceLeaseToken) {
661
+ maintenanceLease.release(maintenanceLeaseToken);
662
+ }
663
+ admissionGate.reopen();
664
+ throw error;
665
+ }
666
+ return maintenanceLeaseToken
667
+ ? { outcome: "accepted", maintenanceLease: "active" }
668
+ : { outcome: "accepted" };
669
+ })();
670
+ shutdownAttempt = attempt;
671
+ void attempt.finally(() => {
672
+ if (shutdownAttempt === attempt) {
673
+ shutdownAttempt = undefined;
674
+ shutdownAttemptLeaseToken = undefined;
675
+ }
676
+ }).catch(() => undefined);
677
+ return attempt;
678
+ };
466
679
  const daemonManagement = {
467
680
  managementToken,
468
681
  identity: () => ({
@@ -471,6 +684,14 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
471
684
  publicKey: installationIdentity.publicKey,
472
685
  }),
473
686
  buildStatus: daemonBuildStatus,
687
+ releaseMaintenanceLease: (token) => ({
688
+ outcome: (() => {
689
+ const outcome = maintenanceLease.release(token);
690
+ if (outcome === "released")
691
+ admissionGate.reopen();
692
+ return outcome;
693
+ })(),
694
+ }),
474
695
  pluginManagement: {
475
696
  listPlugins: () => pluginManagement.list(),
476
697
  setPluginEnabled: (pluginId, enabled) => pluginManagement.setEnabled(pluginId, enabled),
@@ -478,34 +699,25 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
478
699
  prepareInstallation: (input, context) => pluginManagement.prepareInstallation(input, context),
479
700
  commitInstallation: (token, input, context) => pluginManagement.commitInstallation(token, input, context),
480
701
  cancelPreparation: (token) => pluginManagement.cancelPreparation(token),
702
+ settingsUi: (pluginId) => pluginManagement.settingsUi(pluginId),
703
+ settingsUiAsset: (pluginId, assetGeneration, assetPath) => pluginManagement.settingsUiAsset(pluginId, assetGeneration, assetPath),
704
+ callSettings: (pluginId, generation, method, params, context) => pluginManagement.callSettings(pluginId, generation, method, params, context),
481
705
  dispose: () => pluginManagement.dispose(),
482
706
  },
707
+ pluginMarketplaces: {
708
+ list: () => listPluginMarketplaces().map(toMarketplaceItem),
709
+ add: async (input, context) => toMarketplaceItem(await addPluginMarketplace(input.source, {
710
+ ...(input.alias ? { alias: input.alias } : {}),
711
+ signal: context.signal,
712
+ })),
713
+ refresh: async (id, context) => toMarketplaceItem(await refreshPluginMarketplace(id, {
714
+ signal: context.signal,
715
+ })),
716
+ remove: (id) => removePluginMarketplace(id),
717
+ },
483
718
  browserArtifacts: browserArtifactManagement,
484
719
  maintenance: maintenanceManagement,
485
- shutdownIfIdle: () => {
486
- if (shutdownAccepted)
487
- return { outcome: "accepted" };
488
- const activity = {
489
- ...sessionRuntimeServices.sessionRuntimeIndex.activitySnapshot(),
490
- activeTerminals: sessionTerminals.activeTerminalCount(),
491
- activeBrowsers: sessionBrowsers.activeBrowserCount(),
492
- };
493
- if (daemonActivityBlocksShutdown(activity)) {
494
- return { outcome: "busy", activity };
495
- }
496
- shutdownAccepted = true;
497
- const shutdown = requestServerShutdown;
498
- if (!shutdown) {
499
- shutdownAccepted = false;
500
- throw new Error("idle daemon shutdown was requested before server lifecycle was ready");
501
- }
502
- // Start admission fences in this event-loop turn, before another
503
- // request or Direct dispatch can be accepted.
504
- void shutdown().catch((error) => {
505
- warn(`idle daemon shutdown failed: ${error instanceof Error ? error.message : String(error)}`);
506
- });
507
- return { outcome: "accepted" };
508
- },
720
+ shutdownIfIdle,
509
721
  invokePluginCli,
510
722
  };
511
723
  const remoteRuntimeDispatcher = new RemoteRuntimeDispatcher({
@@ -516,6 +728,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
516
728
  terminals: sessionTerminals,
517
729
  browsers: sessionBrowsers,
518
730
  providerClis,
731
+ projects: runtimeProjects,
519
732
  onError: (error) => warn(`Remote Runtime dispatch failed: ${error instanceof Error ? error.message : String(error)}`),
520
733
  });
521
734
  const remoteRuntimeAdmin = directConfig && directEncryptionIdentity
@@ -552,7 +765,6 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
552
765
  };
553
766
  server = await startServer({
554
767
  config: appConfig,
555
- loadInstances: () => resolveChannelInstances(warn),
556
768
  control,
557
769
  sessionLog,
558
770
  sessionRegistry,
@@ -564,6 +776,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
564
776
  runtimeTargetControl,
565
777
  runtimeConnectionResolver: runtimeConnectionManager,
566
778
  localSessionResources: machineSessions,
779
+ localMachineSessions: machineSessions,
567
780
  providerClis,
568
781
  sessionEmulators,
569
782
  sessionLifecycle: sessionResourceLifecycle,
@@ -743,8 +956,10 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, writeE
743
956
  // A failed resource shutdown may leave plugin transports alive. Keep
744
957
  // the process-scoped owner lease in that fail-closed state; it will be
745
958
  // released by the OS when the process exits.
746
- if (shutdownComplete)
959
+ if (shutdownComplete) {
747
960
  await owner.release();
961
+ onShutdownComplete?.();
962
+ }
748
963
  }
749
964
  })();
750
965
  return shutdownPromise;