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

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 (61) hide show
  1. package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +0 -12
  2. package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +3 -1
  3. package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +3 -3
  4. package/bundled-plugins/plugins/lark/dist/host-adapters.js +24 -18
  5. package/bundled-plugins/plugins/lark/dist/lark.js +18 -12
  6. package/bundled-plugins/plugins/lark/dist/runtime.js +6046 -6018
  7. package/bundled-plugins/plugins/lark/dist/settings.js +6006 -5987
  8. package/bundled-plugins/plugins/lark/package.json +1 -1
  9. package/bundled-plugins/plugins/lark/ui/assets/index-CGUVAMsv.css +1 -0
  10. package/bundled-plugins/plugins/lark/ui/index.html +2 -2
  11. package/dist/agent-file.js +0 -1
  12. package/dist/browser-artifact-management.js +4 -1
  13. package/dist/chrome-for-testing-runtime.js +4 -3
  14. package/dist/chrome-for-testing-store.js +29 -7
  15. package/dist/control-deps.d.ts +6 -2
  16. package/dist/control-deps.js +87 -4
  17. package/dist/control-link-store.js +4 -3
  18. package/dist/daemon-owner.js +4 -7
  19. package/dist/daemon-server.js +25 -14
  20. package/dist/db.d.ts +2 -2
  21. package/dist/db.js +133 -21
  22. package/dist/lifecycle.d.ts +1 -0
  23. package/dist/lifecycle.js +3 -0
  24. package/dist/migrations/cleanup-legacy-sessions.js +4 -3
  25. package/dist/plugin-cli.d.ts +10 -0
  26. package/dist/plugin-cli.js +19 -2
  27. package/dist/plugin-host-rpc.d.ts +31 -9
  28. package/dist/plugin-host-rpc.js +428 -80
  29. package/dist/plugin-installer.js +3 -2
  30. package/dist/plugin-management-service.js +0 -2
  31. package/dist/plugin-marketplace.js +2 -1
  32. package/dist/plugin-store.js +2 -4
  33. package/dist/plugin-supervisor.js +0 -4
  34. package/dist/remote-runtime-access-store.d.ts +2 -2
  35. package/dist/remote-runtime-access-store.js +6 -9
  36. package/dist/remote-runtime-connection-manager.js +1 -1
  37. package/dist/remote-runtime-target-store.d.ts +2 -2
  38. package/dist/remote-runtime-target-store.js +6 -6
  39. package/dist/session-emulator-binding-store.js +4 -6
  40. package/dist/session-fork-store.d.ts +1 -0
  41. package/dist/session-fork-store.js +82 -28
  42. package/dist/session-launch-operations.js +6 -5
  43. package/dist/session-log-store.d.ts +1 -0
  44. package/dist/session-log-store.js +19 -16
  45. package/dist/session-meta-store.d.ts +5 -0
  46. package/dist/session-meta-store.js +25 -2
  47. package/dist/session-portal-grant-store.d.ts +29 -0
  48. package/dist/session-portal-grant-store.js +85 -0
  49. package/dist/session-resource-store.d.ts +20 -6
  50. package/dist/session-resource-store.js +364 -68
  51. package/dist/setup-service.d.ts +1 -0
  52. package/dist/setup-service.js +1 -0
  53. package/dist/skills-catalog.d.ts +11 -2
  54. package/dist/skills-catalog.js +317 -10
  55. package/dist/sqlite.d.ts +22 -0
  56. package/dist/sqlite.js +57 -0
  57. package/dist/system-dependencies.d.ts +22 -0
  58. package/dist/system-dependencies.js +79 -0
  59. package/package.json +15 -12
  60. package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +0 -1
  61. /package/bundled-plugins/plugins/lark/ui/assets/{index-D9N-AkLX.js → index-D8Amoe6a.js} +0 -0
@@ -17,9 +17,10 @@ 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, MachineSessionServiceFailure, listRuntimeModels, RemoteRuntimeDispatcher, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceCoordinator, SessionEmulatorService, startServer, startDirectRuntimeServer, } from "@rynx-ai/server";
20
+ import { createDaemonRuntimeHost, createDesktopFirstSessionBrowserHost, createRunnerSessionTerminalHost, createSessionRuntimeServices, DesktopBrowserHostAbsentError, DesktopBrowserHostRegistry, DirectRuntimeConnectionHub, DirectRuntimeBrowserInspectHostError, MachineSessionService, MachineSessionServiceFailure, RemoteRuntimeDispatcher, SessionBrowserService, SessionBrowserServiceError, SessionBrowserSurfaceCoordinator, SessionEmulatorService, startServer, startDirectRuntimeServer, } from "@rynx-ai/server";
21
+ import { authorizeSessionPortalGrant, redeemSessionPortalTicket, } from "./session-portal-grant-store.js";
21
22
  import { APP_BROWSER_HOST_BOOTSTRAP_TYPE, AppBrowserHostSupervisor, parseAppBrowserHostLaunchConfig, } from "./app-browser-host-supervisor.js";
22
- import { buildControlDeps } from "./control-deps.js";
23
+ import { buildControlDeps, listControlAgentModels } from "./control-deps.js";
23
24
  import { createBrowserArtifactManagementService } from "./browser-artifact-management.js";
24
25
  import { createDaemonBuildStatus } from "./daemon-build-status.js";
25
26
  import { MaintenanceLeaseStore } from "./maintenance-lease.js";
@@ -49,7 +50,7 @@ import { SqliteSessionResourceStore } from "./session-resource-store.js";
49
50
  import { createSessionFallbackWorkspace, discardSessionFallbackWorkspace, } from "./session-fallback-workspace.js";
50
51
  import { SessionBrowserCapabilityStore } from "./session-browser-capability-store.js";
51
52
  import { SqliteSessionEmulatorBindingStore } from "./session-emulator-binding-store.js";
52
- import { sessionRegistry } from "./session-meta-store.js";
53
+ import { sessionBelongsToPlugin, sessionRegistry } from "./session-meta-store.js";
53
54
  import { sessionPendingMessageStore } from "./session-pending-message-store.js";
54
55
  import { RemoteRuntimeAccessStore } from "./remote-runtime-access-store.js";
55
56
  import { DaemonRemoteRuntimeAdmin } from "./remote-runtime-admin.js";
@@ -68,13 +69,7 @@ const require = createRequire(import.meta.url);
68
69
  const DAEMON_PACKAGE_VERSION = readDaemonPackageVersion();
69
70
  async function resolveSessionExecutionSnapshot(input) {
70
71
  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
- }),
72
+ ...await resolveExecutionSnapshot(input),
78
73
  pluginSkills: snapshotEnabledPluginSkillSources(),
79
74
  };
80
75
  }
@@ -280,7 +275,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
280
275
  const directConfig = directHostingSupported
281
276
  ? await loadDirectConfig()
282
277
  : undefined;
283
- const control = buildControlDeps();
278
+ const control = buildControlDeps(appConfig);
284
279
  const installationIdentity = await loadOrCreateDaemonInstallationIdentity();
285
280
  const directEncryptionIdentity = directHostingSupported
286
281
  ? await loadOrCreateDaemonDirectRuntimeEncryptionIdentity()
@@ -304,10 +299,13 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
304
299
  "semantic.session.interrupt.v1",
305
300
  "semantic.session.agent-options.v1",
306
301
  "semantic.session.launch-options.v2",
302
+ "semantic.session.execution-settings.v1",
307
303
  "semantic.session.create.v1",
308
304
  "semantic.session.fork.v1",
309
305
  "semantic.session.message.v1",
310
- "semantic.session.resources.v1",
306
+ "semantic.session.resources.v2",
307
+ "semantic.session.files.v1",
308
+ "semantic.session.workspace-files.v1",
311
309
  "semantic.session.message-queue.v1",
312
310
  "semantic.session.delete.v1",
313
311
  "semantic.session.interaction.v1",
@@ -410,7 +408,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
410
408
  ...((selection.provider || (!selection.agent && !selection.config))
411
409
  ? { provider: selection.provider ?? appConfig.DEFAULT_RUNTIME }
412
410
  : {}),
413
- ...(selection.model ? { model: selection.model } : {}),
411
+ ...(selection.model !== undefined ? { model: selection.model } : {}),
414
412
  ...(selection.reasoningEffort
415
413
  ? { reasoningEffort: selection.reasoningEffort }
416
414
  : {}),
@@ -419,6 +417,11 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
419
417
  };
420
418
  },
421
419
  },
420
+ sessionExecution: {
421
+ update: async (input) => {
422
+ await machineSessions.updateExecutionSettings(input);
423
+ },
424
+ },
422
425
  sessionFork: (input) => machineSessions.fork(input),
423
426
  admissionOpen,
424
427
  admissionReserve: reserveAdmission,
@@ -446,6 +449,7 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
446
449
  sessionContextProvider: browserRunnerSessionContexts,
447
450
  admissionOpen,
448
451
  admissionReserve: reserveAdmission,
452
+ materializeGeneratedImage: (input) => sessionResources.materializeGeneratedImage(input),
449
453
  onMirrorError: (error, sessionId) => warn(`Session mirror failed for ${sessionId}: ${error instanceof Error ? error.message : String(error)}`),
450
454
  });
451
455
  const sessionBrowserHost = appBrowserHostSupervisor
@@ -556,14 +560,16 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
556
560
  forks: sessionForks,
557
561
  execution: {
558
562
  runtime: sessionRuntimeServices.conversationRuntime,
559
- resolve: ({ agent, provider, model, reasoningEffort, cwd }) => resolveSessionExecutionSnapshot({
563
+ resolve: ({ agent, provider, model, reasoningEffort, permissionPreset, cwd }) => resolveSessionExecutionSnapshot({
560
564
  config: appConfig,
561
565
  ...(agent ? { agentName: agent } : {}),
562
566
  ...(provider ? { provider } : {}),
563
567
  ...(model ? { model } : {}),
564
568
  ...(reasoningEffort ? { reasoningEffort } : {}),
569
+ ...(permissionPreset ? { permissionPreset } : {}),
565
570
  cwd,
566
571
  }),
572
+ listModels: (provider) => listControlAgentModels(appConfig, provider),
567
573
  runner: sessionRuntimeServices.runnerManager,
568
574
  },
569
575
  workspace: {
@@ -794,6 +800,11 @@ export async function startRynxDaemonServer({ config, warn = defaultWarn, onShut
794
800
  registry: desktopBrowserHosts,
795
801
  daemonInstanceId,
796
802
  },
803
+ sessionPortalAuthorization: {
804
+ redeem: redeemSessionPortalTicket,
805
+ authorize: authorizeSessionPortalGrant,
806
+ ownsSession: sessionBelongsToPlugin,
807
+ },
797
808
  });
798
809
  baseShutdown = server.shutdown.bind(server);
799
810
  if (directConfig && directEncryptionIdentity) {
package/dist/db.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import Database from "better-sqlite3";
1
+ import { DatabaseSync } from "node:sqlite";
2
2
  /** Absolute path to the SQLite file (override via `RYNX_DB`, else `~/.rynx/rynx.db`). */
3
3
  export declare function dbPath(): string;
4
4
  /** Open (or reuse) the database at the current path, migrating + importing on first open. */
5
- export declare function db(): Database.Database;
5
+ export declare function db(): DatabaseSync;
package/dist/db.js CHANGED
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * `~/.rynx/rynx.db` — the daemon's SQLite store. Concerns kept apart:
3
- * - `plugin_installations` — source-qualified manifests, capabilities, and immutable packages.
3
+ * - `plugin_installations` — source-qualified manifests and immutable packages.
4
4
  * - `plugin_marketplaces` — catalog registrations; catalog data is never executable.
5
5
  * - `plugin_session_launch_operations` — durable idempotency/tombstones.
6
6
  * - `control_links` — generic, one-use links to the local session console.
7
+ * - `session_portal_tickets` / `session_portal_grants` — hashed, scoped,
8
+ * browser Session Portal authorization.
7
9
  * - `runtime_pairing_claims` / `runtime_client_grants` — one-use enrollment
8
10
  * claims and the durable client identities they issue for direct runtimes.
9
11
  * - `runtime_targets` / `runtime_direct_routes` / `runtime_pending_enrollments`
@@ -16,8 +18,9 @@
16
18
  */
17
19
  import { chmodSync, mkdirSync } from "node:fs";
18
20
  import { dirname, isAbsolute, join, resolve } from "node:path";
21
+ import { DatabaseSync } from "node:sqlite";
19
22
  import { rynxHome } from "@rynx-ai/core";
20
- import Database from "better-sqlite3";
23
+ import { transaction } from "./sqlite.js";
21
24
  const connections = new Map();
22
25
  /** Absolute path to the SQLite file (override via `RYNX_DB`, else `~/.rynx/rynx.db`). */
23
26
  export function dbPath() {
@@ -34,9 +37,7 @@ export function db() {
34
37
  if (cached)
35
38
  return cached;
36
39
  mkdirSync(dirname(path), { recursive: true });
37
- const conn = new Database(path);
38
- conn.pragma("busy_timeout = 5000");
39
- conn.pragma("foreign_keys = ON");
40
+ const conn = new DatabaseSync(path, { timeout: 5000, enableForeignKeyConstraints: true });
40
41
  migrate(conn);
41
42
  // Plugin state and daemon credentials are private.
42
43
  try {
@@ -86,7 +87,6 @@ function migrate(conn) {
86
87
  install_path TEXT NOT NULL,
87
88
  manifest TEXT NOT NULL,
88
89
  integrity TEXT NOT NULL,
89
- capabilities TEXT NOT NULL,
90
90
  runtime_state TEXT,
91
91
  runtime_error TEXT,
92
92
  UNIQUE(marketplace_id, plugin_id)
@@ -109,8 +109,9 @@ function migrate(conn) {
109
109
  session_id TEXT NOT NULL,
110
110
  client_upload_id TEXT NOT NULL,
111
111
  upload_id TEXT NOT NULL UNIQUE,
112
+ kind TEXT NOT NULL DEFAULT 'image' CHECK(kind IN ('image', 'file')),
112
113
  filename TEXT NOT NULL,
113
- media_type TEXT NOT NULL CHECK(media_type IN ('image/png', 'image/jpeg', 'image/webp')),
114
+ media_type TEXT NOT NULL,
114
115
  byte_length INTEGER NOT NULL,
115
116
  uploaded_bytes INTEGER NOT NULL DEFAULT 0,
116
117
  sha256 TEXT,
@@ -141,6 +142,9 @@ function migrate(conn) {
141
142
  'failed_not_started'
142
143
  )),
143
144
  error TEXT,
145
+ response_id TEXT,
146
+ message_item_id TEXT,
147
+ execution_snapshot TEXT,
144
148
  created_at INTEGER NOT NULL,
145
149
  updated_at INTEGER NOT NULL,
146
150
  PRIMARY KEY(session_id, client_message_id)
@@ -245,6 +249,39 @@ function migrate(conn) {
245
249
  );
246
250
  CREATE INDEX IF NOT EXISTS idx_control_links_expires
247
251
  ON control_links(expires_at);
252
+ CREATE TABLE IF NOT EXISTS session_portal_tickets (
253
+ ticket_hash TEXT PRIMARY KEY,
254
+ plugin_id TEXT NOT NULL,
255
+ scope TEXT NOT NULL CHECK(scope IN ('plugin_sessions', 'single_session')),
256
+ access TEXT NOT NULL CHECK(access IN ('read_only', 'read_write')),
257
+ session_id TEXT,
258
+ created_at TEXT NOT NULL,
259
+ expires_at TEXT NOT NULL,
260
+ used_at TEXT,
261
+ CHECK(
262
+ (scope = 'plugin_sessions' AND session_id IS NULL)
263
+ OR (scope = 'single_session' AND session_id IS NOT NULL)
264
+ )
265
+ );
266
+ CREATE INDEX IF NOT EXISTS idx_session_portal_tickets_expires
267
+ ON session_portal_tickets(expires_at);
268
+ CREATE TABLE IF NOT EXISTS session_portal_grants (
269
+ portal_id TEXT PRIMARY KEY,
270
+ secret_hash TEXT NOT NULL,
271
+ plugin_id TEXT NOT NULL,
272
+ scope TEXT NOT NULL CHECK(scope IN ('plugin_sessions', 'single_session')),
273
+ access TEXT NOT NULL CHECK(access IN ('read_only', 'read_write')),
274
+ session_id TEXT,
275
+ created_at TEXT NOT NULL,
276
+ expires_at TEXT NOT NULL,
277
+ revoked_at TEXT,
278
+ CHECK(
279
+ (scope = 'plugin_sessions' AND session_id IS NULL)
280
+ OR (scope = 'single_session' AND session_id IS NOT NULL)
281
+ )
282
+ );
283
+ CREATE INDEX IF NOT EXISTS idx_session_portal_grants_expires
284
+ ON session_portal_grants(expires_at);
248
285
  CREATE TABLE IF NOT EXISTS runtime_pairing_claims (
249
286
  claim_id TEXT PRIMARY KEY,
250
287
  secret_hash TEXT NOT NULL,
@@ -308,7 +345,8 @@ function migrate(conn) {
308
345
  CREATE INDEX IF NOT EXISTS idx_runtime_pending_enrollments_expires
309
346
  ON runtime_pending_enrollments(expires_at);
310
347
  `);
311
- migratePluginCapabilities(conn);
348
+ migrateSessionResourceFiles(conn);
349
+ migratePluginCapabilityDeclarations(conn);
312
350
  migrateSessionSnapshots(conn);
313
351
  // Breaking plugin-runtime cutover: remove obsolete core-owned state.
314
352
  conn.exec(`
@@ -318,6 +356,92 @@ function migrate(conn) {
318
356
  addColumnIfMissing(conn, "plugin_marketplaces", "built_in", "INTEGER NOT NULL DEFAULT 0");
319
357
  addColumnIfMissing(conn, "sessions", "forked_from_session_id", "TEXT");
320
358
  addColumnIfMissing(conn, "plugin_session_launch_operations", "raw_request_hash", "TEXT");
359
+ addColumnIfMissing(conn, "session_message_operations", "response_id", "TEXT");
360
+ addColumnIfMissing(conn, "session_message_operations", "message_item_id", "TEXT");
361
+ addColumnIfMissing(conn, "session_message_operations", "execution_snapshot", "TEXT");
362
+ }
363
+ /** Rebuild the original image-only table without losing committed resources. */
364
+ function migrateSessionResourceFiles(conn) {
365
+ const columns = conn
366
+ .prepare("PRAGMA table_info(session_resources)")
367
+ .all();
368
+ if (columns.some((column) => column.name === "kind"))
369
+ return;
370
+ conn.exec("PRAGMA foreign_keys = OFF");
371
+ try {
372
+ transaction(conn, () => {
373
+ conn.exec(`
374
+ ALTER TABLE session_message_operation_resources
375
+ RENAME TO session_message_operation_resources_image_v1;
376
+ ALTER TABLE session_resources RENAME TO session_resources_image_v1;
377
+
378
+ CREATE TABLE session_resources (
379
+ id TEXT PRIMARY KEY,
380
+ session_id TEXT NOT NULL,
381
+ client_upload_id TEXT NOT NULL,
382
+ upload_id TEXT NOT NULL UNIQUE,
383
+ kind TEXT NOT NULL DEFAULT 'image' CHECK(kind IN ('image', 'file')),
384
+ filename TEXT NOT NULL,
385
+ media_type TEXT NOT NULL,
386
+ byte_length INTEGER NOT NULL,
387
+ uploaded_bytes INTEGER NOT NULL DEFAULT 0,
388
+ sha256 TEXT,
389
+ width INTEGER,
390
+ height INTEGER,
391
+ state TEXT NOT NULL DEFAULT 'uploading'
392
+ CHECK(state IN ('uploading', 'staged', 'committed')),
393
+ message_item_id TEXT,
394
+ created_at INTEGER NOT NULL,
395
+ committed_at INTEGER,
396
+ UNIQUE(session_id, client_upload_id)
397
+ );
398
+ INSERT INTO session_resources (
399
+ id, session_id, client_upload_id, upload_id, kind, filename, media_type,
400
+ byte_length, uploaded_bytes, sha256, width, height, state,
401
+ message_item_id, created_at, committed_at
402
+ )
403
+ SELECT
404
+ id, session_id, client_upload_id, upload_id, 'image', filename, media_type,
405
+ byte_length, uploaded_bytes, sha256, width, height, state,
406
+ message_item_id, created_at, committed_at
407
+ FROM session_resources_image_v1;
408
+
409
+ CREATE TABLE session_message_operation_resources (
410
+ session_id TEXT NOT NULL,
411
+ client_message_id TEXT NOT NULL,
412
+ resource_id TEXT NOT NULL UNIQUE REFERENCES session_resources(id) ON DELETE CASCADE,
413
+ PRIMARY KEY(session_id, client_message_id, resource_id),
414
+ FOREIGN KEY(session_id, client_message_id)
415
+ REFERENCES session_message_operations(session_id, client_message_id)
416
+ ON DELETE CASCADE
417
+ );
418
+ INSERT INTO session_message_operation_resources
419
+ SELECT * FROM session_message_operation_resources_image_v1;
420
+
421
+ DROP TABLE session_message_operation_resources_image_v1;
422
+ DROP TABLE session_resources_image_v1;
423
+ CREATE INDEX idx_session_resources_session
424
+ ON session_resources(session_id, created_at);
425
+ CREATE INDEX idx_session_resources_message
426
+ ON session_resources(message_item_id);
427
+ `);
428
+ })();
429
+ }
430
+ finally {
431
+ conn.exec("PRAGMA foreign_keys = ON");
432
+ }
433
+ }
434
+ /** Remove the inert permission-declaration columns left by older installations. */
435
+ function migratePluginCapabilityDeclarations(conn) {
436
+ const columns = conn
437
+ .prepare("PRAGMA table_info(plugin_installations)")
438
+ .all();
439
+ const names = new Set(columns.map((column) => column.name));
440
+ for (const legacyColumn of ["capabilities", "grants"]) {
441
+ if (names.has(legacyColumn)) {
442
+ conn.exec(`ALTER TABLE plugin_installations DROP COLUMN ${legacyColumn}`);
443
+ }
444
+ }
321
445
  }
322
446
  /**
323
447
  * Breaking Session cutover: the previous rows contain mutable Agent/Provider
@@ -330,7 +454,7 @@ function migrateSessionSnapshots(conn) {
330
454
  const names = new Set(columns.map((column) => column.name));
331
455
  if (names.has("workspace") && names.has("execution"))
332
456
  return;
333
- conn.transaction(() => {
457
+ transaction(conn, () => {
334
458
  conn.exec(`
335
459
  INSERT OR IGNORE INTO session_snapshot_cleanup (session_id)
336
460
  SELECT id FROM sessions;
@@ -358,18 +482,6 @@ function migrateSessionSnapshots(conn) {
358
482
  `);
359
483
  }).immediate();
360
484
  }
361
- function migratePluginCapabilities(conn) {
362
- const columns = conn
363
- .prepare("PRAGMA table_info(plugin_installations)")
364
- .all();
365
- if (columns.some((column) => column.name === "capabilities"))
366
- return;
367
- if (columns.some((column) => column.name === "grants")) {
368
- conn.exec("ALTER TABLE plugin_installations RENAME COLUMN grants TO capabilities");
369
- return;
370
- }
371
- conn.exec("ALTER TABLE plugin_installations ADD COLUMN capabilities TEXT NOT NULL DEFAULT '[]'");
372
- }
373
485
  /** Idempotently add a column to an existing table (no-op if already present). */
374
486
  function addColumnIfMissing(conn, table, column, ddl) {
375
487
  const cols = conn.prepare(`PRAGMA table_info(${table})`).all();
@@ -4,6 +4,7 @@ export interface StandaloneDaemonLaunchContext {
4
4
  builtinSkillsDir: string;
5
5
  cliVersion: string;
6
6
  productVersion: string;
7
+ tmuxBin?: string;
7
8
  }
8
9
  export declare function startDaemon(options: {
9
10
  force: boolean;
package/dist/lifecycle.js CHANGED
@@ -11,6 +11,9 @@ export function startDaemon(options) {
11
11
  RYNX_DISTRIBUTION: "standalone",
12
12
  RYNX_PRODUCT_VERSION: options.context.productVersion,
13
13
  RYNX_DAEMON_LIFECYCLE: "standalone",
14
+ // An empty value explicitly disables PATH fallback when the optional
15
+ // platform package is missing, while keeping the control plane runnable.
16
+ RYNX_TMUX_BIN: options.context.tmuxBin ?? "",
14
17
  },
15
18
  });
16
19
  }
@@ -20,6 +20,7 @@ import { mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
20
20
  import path from "node:path";
21
21
  import { rynxHome } from "@rynx-ai/core";
22
22
  import { db } from "../db.js";
23
+ import { transaction } from "../sqlite.js";
23
24
  /** Mirrors the daemon runtime store's default resolution. */
24
25
  function defaultProxyDir() {
25
26
  return path.join(rynxHome(), ".codex-proxy");
@@ -46,9 +47,9 @@ export function cleanupLegacySessions(opts = {}) {
46
47
  let deletedLogRows = 0;
47
48
  let deletedSessions = 0;
48
49
  let prunedStoreEntries = 0;
49
- conn.transaction(() => {
50
- deletedLogRows = conn.prepare(`DELETE FROM session_items WHERE ${LEGACY_LOG}`).run().changes;
51
- deletedSessions = conn.prepare(`DELETE FROM sessions WHERE ${LEGACY_META}`).run().changes;
50
+ transaction(conn, () => {
51
+ deletedLogRows = Number(conn.prepare(`DELETE FROM session_items WHERE ${LEGACY_LOG}`).run().changes);
52
+ deletedSessions = Number(conn.prepare(`DELETE FROM sessions WHERE ${LEGACY_META}`).run().changes);
52
53
  })();
53
54
  // The runtime store is daemon-owned. Plugin routing stores remain private to
54
55
  // their plugins and are never inspected by this migration.
@@ -20,6 +20,16 @@ export interface PluginCliCommandResult {
20
20
  stderrTruncated: boolean;
21
21
  };
22
22
  }
23
+ export interface InstalledPluginListItem {
24
+ id: string;
25
+ version: string;
26
+ source: "local" | "git";
27
+ state: "enabled" | "disabled";
28
+ }
29
+ /** Read the durable installed-plugin inventory without contacting the daemon. */
30
+ export declare function listInstalledPlugins(): InstalledPluginListItem[];
31
+ /** Render manifest-declared command help without executing plugin code. */
32
+ export declare function installedPluginCommandUsage(pluginId: string): string;
23
33
  /** Execute one statically declared, language-neutral plugin command. */
24
34
  export declare function runPluginCliCommand(pluginId: string, args: readonly string[], options?: RunPluginCliOptions): Promise<PluginCliCommandResult>;
25
35
  export declare function pluginCommandUsage(record: RunnablePluginRecord): string;
@@ -3,12 +3,29 @@ import { mkdirSync, realpathSync, statSync } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { createPluginCliInvocationLeaseId, removePluginRuntimeLease, writePluginRuntimeLease, } from "@rynx-ai/plugin-sdk";
5
5
  import { db } from "./db.js";
6
+ import { transaction } from "./sqlite.js";
6
7
  import { pluginDataDir, pluginRuntimeCacheDir } from "./plugin-installer.js";
7
8
  import { validatePluginPackageSnapshot } from "./plugin-package.js";
8
- import { getPlugin } from "./plugin-store.js";
9
+ import { getPlugin, listPlugins, } from "./plugin-store.js";
9
10
  import { loadOrCreateDaemonInstallationIdentitySync } from "./installation-id.js";
10
11
  const DEFAULT_TERMINATION_GRACE_MS = 2_000;
11
12
  const MAX_TERMINATION_GRACE_MS = 30_000;
13
+ /** Read the durable installed-plugin inventory without contacting the daemon. */
14
+ export function listInstalledPlugins() {
15
+ return listPlugins().map((record) => ({
16
+ id: record.canonicalId,
17
+ version: record.version,
18
+ source: record.source,
19
+ state: record.enabled ? "enabled" : "disabled",
20
+ }));
21
+ }
22
+ /** Render manifest-declared command help without executing plugin code. */
23
+ export function installedPluginCommandUsage(pluginId) {
24
+ const record = getPlugin(pluginId);
25
+ if (!record)
26
+ throw new Error(`plugin "${pluginId}" is not installed`);
27
+ return pluginCommandUsage(record);
28
+ }
12
29
  /** Execute one statically declared, language-neutral plugin command. */
13
30
  export async function runPluginCliCommand(pluginId, args, options = {}) {
14
31
  const hostEnv = options.env ?? process.env;
@@ -74,7 +91,7 @@ export async function runPluginCliCommand(pluginId, args, options = {}) {
74
91
  removePluginRuntimeLease(dataDir, leaseId);
75
92
  };
76
93
  try {
77
- db().transaction(() => {
94
+ transaction(db(), () => {
78
95
  const record = getPlugin(pluginId);
79
96
  if (!record)
80
97
  throw new Error(`plugin "${pluginId}" is not installed`);
@@ -1,6 +1,7 @@
1
- import { type AdmissionReservation, type AgentSpec, type AgentCapabilities, type AgentRuntimeId, type AgentSessionStore, type ConversationRuntime, type ResolvedExecutionSnapshot, type SessionRegistry, type SessionWorkspaceSnapshot } from "@rynx-ai/core";
2
- import { type PluginAgentSummary, type PluginCapability, type PluginHostEvent, type PluginResolvedSessionExecution, type PluginSessionExecutionSnapshot, type PluginSessionInteractionResolveDisposition } from "@rynx-ai/plugin-sdk";
1
+ import { type AdmissionReservation, type AgentSpec, type AgentCapabilities, type AgentRuntimeId, type AgentSessionStore, type ResolvedExecutionSnapshot, type RuntimeCollaborationMode, type SessionRegistry, type SessionWorkspaceSnapshot, type SteerSignal } from "@rynx-ai/core";
2
+ import { type PluginAgentSummary, type PluginHostEvent, type PluginSessionActivitySnapshot, type PluginResolvedSessionExecution, type PluginSessionExecutionSnapshot, type PluginSessionInteractionResolveDisposition } from "@rynx-ai/plugin-sdk";
3
3
  import type { SessionInteractionResolution } from "@rynx-ai/protocol";
4
+ import type { MachineSessionMessageInput, StartedMachineSessionRun } from "@rynx-ai/server";
4
5
  import { PluginRunnerError } from "@rynx-ai/plugin-runner";
5
6
  /** Leaves ample room for the RPC envelope below the runner's 1 MiB line cap. */
6
7
  export declare const MAX_PLUGIN_HOST_EVENT_BYTES: number;
@@ -22,7 +23,7 @@ export interface PluginSessionLaunchSelection {
22
23
  agent?: string;
23
24
  config?: AgentSpec;
24
25
  provider?: AgentRuntimeId;
25
- model?: string;
26
+ model?: string | null;
26
27
  reasoningEffort?: string;
27
28
  title?: string;
28
29
  }
@@ -37,7 +38,16 @@ export interface PluginHostServices {
37
38
  publicKey: string;
38
39
  sign(pluginId: string, challenge: string): string;
39
40
  };
40
- conversationRuntime: ConversationRuntime;
41
+ sessionRuns: {
42
+ startRun: (sessionId: string, input: string | MachineSessionMessageInput, options?: {
43
+ signal?: AbortSignal;
44
+ steer?: SteerSignal;
45
+ collaborationMode?: RuntimeCollaborationMode;
46
+ }) => Promise<StartedMachineSessionRun>;
47
+ };
48
+ sessionFiles: {
49
+ resolveResourcePath(sessionId: string, resourceId: string): Promise<string>;
50
+ };
41
51
  capabilities: AgentCapabilities;
42
52
  sessionStore: AgentSessionStore;
43
53
  sessionRegistry: SessionRegistry;
@@ -45,6 +55,14 @@ export interface PluginHostServices {
45
55
  sessionLaunch: {
46
56
  resolve(input: PluginSessionLaunchSelection): Promise<PluginResolvedSessionLaunch>;
47
57
  };
58
+ sessionExecution: {
59
+ update(input: {
60
+ sessionId: string;
61
+ model: string | null;
62
+ reasoningEffort: string | null;
63
+ }): Promise<void>;
64
+ };
65
+ sessionActivity(sessionId: string): PluginSessionActivitySnapshot | Promise<PluginSessionActivitySnapshot>;
48
66
  interruptSession?(sessionId: string): Promise<boolean>;
49
67
  terminateSession?(sessionId: string): Promise<boolean>;
50
68
  resolveSessionInteraction?(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
@@ -60,6 +78,11 @@ export interface PluginHostServices {
60
78
  }>;
61
79
  admissionOpen?: () => boolean;
62
80
  admissionReserve?: () => AdmissionReservation | undefined;
81
+ sessionPortal?: {
82
+ scheme: "http" | "https";
83
+ bindHost: string;
84
+ port: number;
85
+ };
63
86
  }
64
87
  export interface PluginHostRpcOptions {
65
88
  /** Manifest id returned by host.info. */
@@ -69,7 +92,6 @@ export interface PluginHostRpcOptions {
69
92
  daemonInstanceId: string;
70
93
  productVersion: string;
71
94
  cliVersion: string;
72
- capabilities: readonly PluginCapability[];
73
95
  services: PluginHostServices;
74
96
  /** Canonical plugin-owned root permitted for the Workspace plugin's isolated
75
97
  * Session directory. Omitted for every other plugin. */
@@ -80,7 +102,7 @@ export interface PluginHostRpcOptions {
80
102
  disposeForceAfterMs?: number;
81
103
  }
82
104
  /**
83
- * One authenticated plugin's capability-gated Host RPC surface.
105
+ * One authenticated plugin's Host RPC surface.
84
106
  *
85
107
  * This adapter is deliberately the only place that maps public string methods
86
108
  * to trusted runtime objects. The runner receives neither this class nor its
@@ -92,7 +114,6 @@ export declare class PluginHostRpc {
92
114
  private readonly daemonInstanceId;
93
115
  private readonly productVersion;
94
116
  private readonly cliVersion;
95
- private readonly capabilities;
96
117
  private readonly services;
97
118
  private readonly managedWorkspaceRoot;
98
119
  private readonly emit;
@@ -113,11 +134,11 @@ export declare class PluginHostRpc {
113
134
  private runSession;
114
135
  private runSessionAdmitted;
115
136
  private pumpRun;
137
+ private projectSessionFileEvent;
116
138
  private releaseRun;
117
- private steerSession;
118
139
  private interruptRun;
140
+ private sessionActivity;
119
141
  private interruptWon;
120
- private activeRun;
121
142
  private activeRunByIdentity;
122
143
  private getBinding;
123
144
  private listBindings;
@@ -131,6 +152,7 @@ export declare class PluginHostRpc {
131
152
  private setSessionTitle;
132
153
  private resolveAgentExecution;
133
154
  private issueControlLink;
155
+ private issueSessionPortalLink;
134
156
  private ownedSession;
135
157
  private isOwned;
136
158
  }