@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.
- package/bundled-plugins/.rynx-plugin/marketplace.json +10 -0
- package/bundled-plugins/plugins/lark/.rynx-plugin/plugin.json +37 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.d.ts +48 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/mirror-index.js +54 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.d.ts +85 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-mirror.js +333 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.d.ts +109 -0
- package/bundled-plugins/plugins/lark/dist/cli-mirror/rollout-watcher.js +347 -0
- package/bundled-plugins/plugins/lark/dist/host-adapters.d.ts +95 -0
- package/bundled-plugins/plugins/lark/dist/host-adapters.js +504 -0
- package/bundled-plugins/plugins/lark/dist/index.d.ts +7 -0
- package/bundled-plugins/plugins/lark/dist/index.js +7 -0
- package/bundled-plugins/plugins/lark/dist/lark-card-stream.d.ts +363 -0
- package/bundled-plugins/plugins/lark/dist/lark-card-stream.js +1335 -0
- package/bundled-plugins/plugins/lark/dist/lark-config-store.d.ts +30 -0
- package/bundled-plugins/plugins/lark/dist/lark-config-store.js +135 -0
- package/bundled-plugins/plugins/lark/dist/lark-content.d.ts +30 -0
- package/bundled-plugins/plugins/lark/dist/lark-content.js +138 -0
- package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.d.ts +65 -0
- package/bundled-plugins/plugins/lark/dist/lark-conversation-directory.js +106 -0
- package/bundled-plugins/plugins/lark/dist/lark-diff-card.d.ts +25 -0
- package/bundled-plugins/plugins/lark/dist/lark-diff-card.js +58 -0
- package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.d.ts +16 -0
- package/bundled-plugins/plugins/lark/dist/lark-fork-transcript.js +141 -0
- package/bundled-plugins/plugins/lark/dist/lark-resume-card.d.ts +31 -0
- package/bundled-plugins/plugins/lark/dist/lark-resume-card.js +105 -0
- package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.d.ts +18 -0
- package/bundled-plugins/plugins/lark/dist/lark-runtime-state-store.js +95 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/client.d.ts +8 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/client.js +32 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/index.d.ts +1 -0
- package/bundled-plugins/plugins/lark/dist/lark-sdk/index.js +1 -0
- package/bundled-plugins/plugins/lark/dist/lark-sender.d.ts +114 -0
- package/bundled-plugins/plugins/lark/dist/lark-sender.js +323 -0
- package/bundled-plugins/plugins/lark/dist/lark-session-store.d.ts +13 -0
- package/bundled-plugins/plugins/lark/dist/lark-session-store.js +14 -0
- package/bundled-plugins/plugins/lark/dist/lark-settings-card.d.ts +55 -0
- package/bundled-plugins/plugins/lark/dist/lark-settings-card.js +158 -0
- package/bundled-plugins/plugins/lark/dist/lark-setup.d.ts +39 -0
- package/bundled-plugins/plugins/lark/dist/lark-setup.js +103 -0
- package/bundled-plugins/plugins/lark/dist/lark.d.ts +507 -0
- package/bundled-plugins/plugins/lark/dist/lark.js +1977 -0
- package/bundled-plugins/plugins/lark/dist/runtime.d.ts +13 -0
- package/bundled-plugins/plugins/lark/dist/runtime.js +131313 -0
- package/bundled-plugins/plugins/lark/dist/settings.d.ts +7 -0
- package/bundled-plugins/plugins/lark/dist/settings.js +126958 -0
- package/bundled-plugins/plugins/lark/package.json +57 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-B2dhMlMU.css +1 -0
- package/bundled-plugins/plugins/lark/ui/assets/index-D9N-AkLX.js +201 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/bundled-plugins/plugins/lark/ui/index.html +13 -0
- package/dist/browser-artifact-management.d.ts +4 -1
- package/dist/browser-artifact-management.js +18 -10
- package/dist/browser-artifacts.d.ts +1 -0
- package/dist/browser-artifacts.js +1 -0
- package/dist/bundled-plugins.d.ts +15 -0
- package/dist/bundled-plugins.js +134 -0
- package/dist/chrome-for-testing-runtime.d.ts +27 -0
- package/dist/chrome-for-testing-runtime.js +193 -0
- package/dist/chrome-for-testing-store.d.ts +2 -0
- package/dist/chrome-for-testing-store.js +9 -0
- package/dist/control-deps.js +2 -79
- package/dist/daemon-server.d.ts +7 -1
- package/dist/daemon-server.js +299 -84
- package/dist/db.js +147 -124
- package/dist/entry-path.d.ts +3 -0
- package/dist/entry-path.js +12 -0
- package/dist/index-daemon.js +3 -66
- package/dist/lifecycle.d.ts +12 -0
- package/dist/lifecycle.js +16 -0
- package/dist/maintenance-lease.d.ts +39 -0
- package/dist/maintenance-lease.js +250 -0
- package/dist/migrations/cleanup-legacy-sessions.d.ts +1 -1
- package/dist/migrations/cleanup-legacy-sessions.js +33 -15
- package/dist/official-plugins.d.ts +2 -0
- package/dist/official-plugins.js +2 -0
- package/dist/plugin-cli.d.ts +1 -3
- package/dist/plugin-cli.js +56 -45
- package/dist/plugin-host-leases.d.ts +4 -0
- package/dist/plugin-host-leases.js +7 -0
- package/dist/plugin-host-rpc.d.ts +60 -4
- package/dist/plugin-host-rpc.js +300 -50
- package/dist/plugin-installer.d.ts +24 -55
- package/dist/plugin-installer.js +258 -412
- package/dist/plugin-management-service.d.ts +30 -23
- package/dist/plugin-management-service.js +290 -105
- package/dist/plugin-marketplace.d.ts +35 -0
- package/dist/plugin-marketplace.js +299 -0
- package/dist/plugin-package.d.ts +22 -24
- package/dist/plugin-package.js +520 -571
- package/dist/plugin-skill-context.d.ts +4 -0
- package/dist/plugin-skill-context.js +35 -0
- package/dist/plugin-store.d.ts +36 -37
- package/dist/plugin-store.js +140 -92
- package/dist/plugin-supervisor.d.ts +50 -125
- package/dist/plugin-supervisor.js +682 -1030
- package/dist/pm2.d.ts +11 -2
- package/dist/pm2.js +130 -28
- package/dist/process.d.ts +12 -0
- package/dist/process.js +91 -0
- package/dist/runtime-project-store.d.ts +9 -0
- package/dist/runtime-project-store.js +83 -0
- package/dist/session-fallback-workspace.d.ts +22 -0
- package/dist/session-fallback-workspace.js +120 -0
- package/dist/session-fork-store.d.ts +41 -0
- package/dist/session-fork-store.js +288 -0
- package/dist/session-launch-operations.d.ts +23 -2
- package/dist/session-launch-operations.js +48 -7
- package/dist/session-meta-store.d.ts +7 -9
- package/dist/session-meta-store.js +28 -49
- package/dist/setup-service.d.ts +20 -0
- package/dist/setup-service.js +38 -0
- package/package.json +36 -20
- package/dist/browser-cli-args.d.ts +0 -20
- package/dist/browser-cli-args.js +0 -100
- package/dist/channel-store.d.ts +0 -22
- package/dist/channel-store.js +0 -65
- package/dist/cli.d.ts +0 -2
- package/dist/cli.js +0 -732
- package/dist/control-client.d.ts +0 -122
- package/dist/control-client.js +0 -855
- package/dist/control-endpoint.d.ts +0 -14
- package/dist/control-endpoint.js +0 -124
- package/dist/desktop-browser-host-client.d.ts +0 -35
- package/dist/desktop-browser-host-client.js +0 -415
- package/dist/instance-store.d.ts +0 -38
- package/dist/instance-store.js +0 -85
- package/dist/plugin-install-restart.d.ts +0 -13
- package/dist/plugin-install-restart.js +0 -24
- package/dist/plugin-runtime-control.d.ts +0 -2
- package/dist/plugin-runtime-control.js +0 -37
- package/dist/registry.d.ts +0 -10
- package/dist/registry.js +0 -52
- package/dist/setup.d.ts +0 -20
- package/dist/setup.js +0 -374
- package/dist/update.d.ts +0 -25
- package/dist/update.js +0 -133
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AgentCapabilities, type AgentSessionStore, type ConversationRuntime, type SessionRegistry } from "@rynx-ai/core";
|
|
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
2
|
import { type PluginAgentSummary, type PluginCapability, type PluginHostEvent, type PluginResolvedSessionExecution, type PluginSessionExecutionSnapshot, type PluginSessionInteractionResolveDisposition } from "@rynx-ai/plugin-sdk";
|
|
3
3
|
import type { SessionInteractionResolution } from "@rynx-ai/protocol";
|
|
4
4
|
import { PluginRunnerError } from "@rynx-ai/plugin-runner";
|
|
@@ -12,6 +12,24 @@ export interface PluginAgentCatalogHost {
|
|
|
12
12
|
session: PluginSessionExecutionSnapshot;
|
|
13
13
|
}): Promise<PluginResolvedSessionExecution>;
|
|
14
14
|
}
|
|
15
|
+
/** Mutable launch selectors are accepted only at the Host boundary. The
|
|
16
|
+
* resolver turns them into immutable Session-owned snapshots before a registry
|
|
17
|
+
* row is created; none of these selectors are persisted on the Session. */
|
|
18
|
+
export interface PluginSessionLaunchSelection {
|
|
19
|
+
projectId?: string;
|
|
20
|
+
managedCwd?: string;
|
|
21
|
+
workspace?: SessionWorkspaceSnapshot;
|
|
22
|
+
agent?: string;
|
|
23
|
+
config?: AgentSpec;
|
|
24
|
+
provider?: AgentRuntimeId;
|
|
25
|
+
model?: string;
|
|
26
|
+
reasoningEffort?: string;
|
|
27
|
+
title?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface PluginResolvedSessionLaunch {
|
|
30
|
+
workspace: SessionWorkspaceSnapshot;
|
|
31
|
+
execution: ResolvedExecutionSnapshot;
|
|
32
|
+
}
|
|
15
33
|
export interface PluginHostServices {
|
|
16
34
|
installationId: string;
|
|
17
35
|
installationProof: {
|
|
@@ -24,18 +42,42 @@ export interface PluginHostServices {
|
|
|
24
42
|
sessionStore: AgentSessionStore;
|
|
25
43
|
sessionRegistry: SessionRegistry;
|
|
26
44
|
agentCatalog: PluginAgentCatalogHost;
|
|
45
|
+
sessionLaunch: {
|
|
46
|
+
resolve(input: PluginSessionLaunchSelection): Promise<PluginResolvedSessionLaunch>;
|
|
47
|
+
};
|
|
27
48
|
interruptSession?(sessionId: string): Promise<boolean>;
|
|
49
|
+
terminateSession?(sessionId: string): Promise<boolean>;
|
|
28
50
|
resolveSessionInteraction?(sessionId: string, interactionId: string, resolution: SessionInteractionResolution): Promise<{
|
|
29
51
|
disposition: PluginSessionInteractionResolveDisposition;
|
|
30
52
|
}>;
|
|
53
|
+
sessionFork?(input: {
|
|
54
|
+
sourceSessionId: string;
|
|
55
|
+
operationId: string;
|
|
56
|
+
title?: string;
|
|
57
|
+
}): Promise<{
|
|
58
|
+
sessionId: string;
|
|
59
|
+
disposition: "created" | "replayed";
|
|
60
|
+
}>;
|
|
61
|
+
admissionOpen?: () => boolean;
|
|
62
|
+
admissionReserve?: () => AdmissionReservation | undefined;
|
|
31
63
|
}
|
|
32
64
|
export interface PluginHostRpcOptions {
|
|
65
|
+
/** Manifest id returned by host.info. */
|
|
33
66
|
pluginId: string;
|
|
67
|
+
/** Marketplace-qualified principal used for ownership and proof scopes. */
|
|
68
|
+
principalId?: string;
|
|
34
69
|
daemonInstanceId: string;
|
|
35
|
-
|
|
70
|
+
productVersion: string;
|
|
71
|
+
cliVersion: string;
|
|
72
|
+
capabilities: readonly PluginCapability[];
|
|
36
73
|
services: PluginHostServices;
|
|
74
|
+
/** Canonical plugin-owned root permitted for the Workspace plugin's isolated
|
|
75
|
+
* Session directory. Omitted for every other plugin. */
|
|
76
|
+
managedWorkspaceRoot?: string;
|
|
37
77
|
/** Resolving this promise is the stream's backpressure acknowledgement. */
|
|
38
78
|
emit(event: PluginHostEvent): void | Promise<void>;
|
|
79
|
+
/** Bounded graceful-stop window used while disposing this Host. */
|
|
80
|
+
disposeForceAfterMs?: number;
|
|
39
81
|
}
|
|
40
82
|
/**
|
|
41
83
|
* One authenticated plugin's capability-gated Host RPC surface.
|
|
@@ -46,19 +88,30 @@ export interface PluginHostRpcOptions {
|
|
|
46
88
|
*/
|
|
47
89
|
export declare class PluginHostRpc {
|
|
48
90
|
private readonly pluginId;
|
|
91
|
+
private readonly principalId;
|
|
49
92
|
private readonly daemonInstanceId;
|
|
50
|
-
private readonly
|
|
93
|
+
private readonly productVersion;
|
|
94
|
+
private readonly cliVersion;
|
|
95
|
+
private readonly capabilities;
|
|
51
96
|
private readonly services;
|
|
97
|
+
private readonly managedWorkspaceRoot;
|
|
52
98
|
private readonly emit;
|
|
99
|
+
private readonly disposeForceAfterMs;
|
|
53
100
|
private readonly activeRuns;
|
|
54
101
|
private readonly activeSessionRuns;
|
|
55
102
|
private readonly activeRunsByOperation;
|
|
56
103
|
private disposed;
|
|
104
|
+
private disposePromise?;
|
|
57
105
|
constructor(options: PluginHostRpcOptions);
|
|
58
|
-
call(method: string, params: unknown): Promise<unknown>;
|
|
106
|
+
call(method: string, params: unknown, signal?: AbortSignal): Promise<unknown>;
|
|
59
107
|
dispose(): Promise<void>;
|
|
60
108
|
private ensureSession;
|
|
109
|
+
private ensureSessionAdmitted;
|
|
110
|
+
private resolveManagedWorkspace;
|
|
111
|
+
private resolveWorkspaceSnapshot;
|
|
112
|
+
private sessionEnsureResult;
|
|
61
113
|
private runSession;
|
|
114
|
+
private runSessionAdmitted;
|
|
62
115
|
private pumpRun;
|
|
63
116
|
private releaseRun;
|
|
64
117
|
private steerSession;
|
|
@@ -72,6 +125,9 @@ export declare class PluginHostRpc {
|
|
|
72
125
|
private withOwnedSession;
|
|
73
126
|
private resolveSessionInteraction;
|
|
74
127
|
private forkSession;
|
|
128
|
+
private forkSessionAdmitted;
|
|
129
|
+
private reserveAdmission;
|
|
130
|
+
private withAdmission;
|
|
75
131
|
private setSessionTitle;
|
|
76
132
|
private resolveAgentExecution;
|
|
77
133
|
private issueControlLink;
|
package/dist/plugin-host-rpc.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { realpath, stat } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { AgentRuntimeError, agentSpecSchema, resolvedExecutionSnapshotSchema, sessionWorkspaceSnapshotSchema, SteerController, } from "@rynx-ai/core";
|
|
3
5
|
import { PLUGIN_HOST_MAX_ACTIVE_RUNS, } from "@rynx-ai/plugin-sdk";
|
|
4
6
|
import { PluginRunnerError } from "@rynx-ai/plugin-runner";
|
|
5
|
-
import { ensurePluginSessionLaunch, SessionLaunchOperationConflictError, SessionLaunchOperationTargetDeletedError, } from "./session-launch-operations.js";
|
|
7
|
+
import { ensurePluginSessionLaunch, replayPluginSessionLaunch, SessionLaunchOperationConflictError, SessionLaunchOperationTargetDeletedError, } from "./session-launch-operations.js";
|
|
6
8
|
import { createControlLink } from "./control-link-store.js";
|
|
7
9
|
const MAX_MESSAGE_LENGTH = 1_000_000;
|
|
8
10
|
/** Leaves ample room for the RPC envelope below the runner's 1 MiB line cap. */
|
|
@@ -43,42 +45,58 @@ const METHOD_CAPABILITY = {
|
|
|
43
45
|
*/
|
|
44
46
|
export class PluginHostRpc {
|
|
45
47
|
pluginId;
|
|
48
|
+
principalId;
|
|
46
49
|
daemonInstanceId;
|
|
47
|
-
|
|
50
|
+
productVersion;
|
|
51
|
+
cliVersion;
|
|
52
|
+
capabilities;
|
|
48
53
|
services;
|
|
54
|
+
managedWorkspaceRoot;
|
|
49
55
|
emit;
|
|
56
|
+
disposeForceAfterMs;
|
|
50
57
|
activeRuns = new Map();
|
|
51
58
|
activeSessionRuns = new Map();
|
|
52
59
|
activeRunsByOperation = new Map();
|
|
53
60
|
disposed = false;
|
|
61
|
+
disposePromise;
|
|
54
62
|
constructor(options) {
|
|
55
63
|
this.pluginId = requiredString(options.pluginId, "pluginId", 64);
|
|
64
|
+
this.principalId = requiredString(options.principalId ?? options.pluginId, "principalId", 160);
|
|
56
65
|
this.daemonInstanceId = requiredString(options.daemonInstanceId, "daemonInstanceId", 128);
|
|
57
|
-
this.
|
|
66
|
+
this.productVersion = requiredString(options.productVersion, "productVersion", 128);
|
|
67
|
+
this.cliVersion = requiredString(options.cliVersion, "cliVersion", 128);
|
|
68
|
+
this.capabilities = new Set(options.capabilities);
|
|
58
69
|
this.services = options.services;
|
|
70
|
+
this.managedWorkspaceRoot = options.managedWorkspaceRoot
|
|
71
|
+
? path.resolve(options.managedWorkspaceRoot)
|
|
72
|
+
: undefined;
|
|
59
73
|
this.emit = options.emit;
|
|
74
|
+
this.disposeForceAfterMs = options.disposeForceAfterMs ?? 10_000;
|
|
60
75
|
}
|
|
61
|
-
async call(method, params) {
|
|
76
|
+
async call(method, params, signal) {
|
|
62
77
|
if (this.disposed)
|
|
63
78
|
throw new PluginHostCallError("HOST_CLOSED", "plugin host is closed");
|
|
79
|
+
throwIfHostCallAborted(signal);
|
|
64
80
|
if (method === "host.info") {
|
|
65
81
|
return {
|
|
66
82
|
pluginId: this.pluginId,
|
|
67
83
|
installationId: this.services.installationId,
|
|
68
84
|
daemonInstanceId: this.daemonInstanceId,
|
|
85
|
+
productVersion: this.productVersion,
|
|
86
|
+
cliVersion: this.cliVersion,
|
|
69
87
|
installationProof: {
|
|
70
88
|
algorithm: this.services.installationProof.algorithm,
|
|
71
89
|
publicKey: this.services.installationProof.publicKey,
|
|
72
90
|
},
|
|
73
91
|
protocolVersion: 1,
|
|
74
|
-
|
|
92
|
+
capabilities: [...this.capabilities],
|
|
75
93
|
};
|
|
76
94
|
}
|
|
77
95
|
const capability = METHOD_CAPABILITY[method];
|
|
78
96
|
if (!capability)
|
|
79
97
|
throw new PluginHostCallError("METHOD_NOT_FOUND", `unknown host method: ${method}`);
|
|
80
|
-
if (!this.
|
|
81
|
-
throw new PluginHostCallError("
|
|
98
|
+
if (!this.capabilities.has(capability)) {
|
|
99
|
+
throw new PluginHostCallError("CAPABILITY_NOT_DECLARED", `host method ${method} is missing manifest capability ${capability}`);
|
|
82
100
|
}
|
|
83
101
|
try {
|
|
84
102
|
switch (method) {
|
|
@@ -87,7 +105,7 @@ export class PluginHostRpc {
|
|
|
87
105
|
return {
|
|
88
106
|
algorithm: this.services.installationProof.algorithm,
|
|
89
107
|
publicKey: this.services.installationProof.publicKey,
|
|
90
|
-
signature: this.services.installationProof.sign(this.
|
|
108
|
+
signature: this.services.installationProof.sign(this.principalId, challenge),
|
|
91
109
|
};
|
|
92
110
|
}
|
|
93
111
|
case "agent.catalog.list":
|
|
@@ -97,9 +115,9 @@ export class PluginHostRpc {
|
|
|
97
115
|
case "agent.execution.resolve":
|
|
98
116
|
return await this.resolveAgentExecution(params);
|
|
99
117
|
case "session.ensure":
|
|
100
|
-
return this.ensureSession(params);
|
|
118
|
+
return await this.ensureSession(params);
|
|
101
119
|
case "session.run":
|
|
102
|
-
return await this.runSession(params);
|
|
120
|
+
return await this.runSession(params, signal);
|
|
103
121
|
case "session.steer":
|
|
104
122
|
return this.steerSession(params);
|
|
105
123
|
case "session.interrupt":
|
|
@@ -142,40 +160,162 @@ export class PluginHostRpc {
|
|
|
142
160
|
}
|
|
143
161
|
}
|
|
144
162
|
async dispose() {
|
|
145
|
-
if (this.
|
|
146
|
-
return;
|
|
163
|
+
if (this.disposePromise)
|
|
164
|
+
return this.disposePromise;
|
|
147
165
|
this.disposed = true;
|
|
148
166
|
const runs = [...this.activeRuns.values()];
|
|
149
|
-
this.
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
run.
|
|
167
|
+
this.disposePromise = (async () => {
|
|
168
|
+
const stopped = await Promise.allSettled(runs.map(async (run) => {
|
|
169
|
+
if (this.activeRuns.get(run.runId) !== run)
|
|
170
|
+
return;
|
|
171
|
+
run.interruptRequested = true;
|
|
172
|
+
const graceful = run.interruptResult ?? (this.services.interruptSession
|
|
173
|
+
? Promise.resolve().then(() => this.services.interruptSession(run.sessionId))
|
|
174
|
+
: Promise.resolve().then(() => {
|
|
175
|
+
run.abort.abort();
|
|
176
|
+
return false;
|
|
177
|
+
}));
|
|
178
|
+
const authoritative = forceStoppedInterrupt(graceful, this.disposeForceAfterMs, () => this.services.terminateSession?.(run.sessionId) ?? Promise.resolve(false));
|
|
179
|
+
run.interruptResult = authoritative;
|
|
180
|
+
if (!(await authoritative)) {
|
|
181
|
+
throw new Error(`could not prove Session ${run.sessionId} stopped`);
|
|
182
|
+
}
|
|
183
|
+
this.releaseRun(run);
|
|
184
|
+
}));
|
|
185
|
+
const failures = stopped
|
|
186
|
+
.filter((result) => result.status === "rejected")
|
|
187
|
+
.map((result) => result.reason);
|
|
188
|
+
if (failures.length > 0) {
|
|
189
|
+
throw new AggregateError(failures, "plugin Host could not stop every active run");
|
|
161
190
|
}
|
|
162
|
-
}
|
|
191
|
+
})();
|
|
192
|
+
return this.disposePromise;
|
|
193
|
+
}
|
|
194
|
+
async ensureSession(params) {
|
|
195
|
+
return this.withAdmission(() => this.ensureSessionAdmitted(params));
|
|
163
196
|
}
|
|
164
|
-
|
|
197
|
+
async ensureSessionAdmitted(params) {
|
|
165
198
|
const input = record(params);
|
|
166
199
|
const scopeId = requiredString(input.scopeId, "scopeId", 512);
|
|
167
200
|
const operationId = requiredString(input.operationId, "operationId", 512);
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
201
|
+
const parsedSelection = sessionLaunchSelection(input.session);
|
|
202
|
+
if (parsedSelection.managedCwd && !this.managedWorkspaceRoot) {
|
|
203
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.managedCwd is reserved for the built-in Workspace plugin");
|
|
204
|
+
}
|
|
205
|
+
const rawRequestHash = hashCanonicalJson(parsedSelection);
|
|
206
|
+
const replay = replayPluginSessionLaunch({
|
|
207
|
+
pluginId: this.principalId,
|
|
208
|
+
scopeId,
|
|
209
|
+
operationId,
|
|
210
|
+
requestHash: rawRequestHash,
|
|
211
|
+
allowRequestHashMismatch: true,
|
|
212
|
+
});
|
|
213
|
+
if (replay)
|
|
214
|
+
return this.sessionEnsureResult(replay);
|
|
215
|
+
const selection = parsedSelection.managedCwd
|
|
216
|
+
? {
|
|
217
|
+
...parsedSelection,
|
|
218
|
+
managedCwd: await this.resolveManagedWorkspace(parsedSelection.managedCwd),
|
|
219
|
+
}
|
|
220
|
+
: parsedSelection.workspace
|
|
221
|
+
? {
|
|
222
|
+
...parsedSelection,
|
|
223
|
+
workspace: await this.resolveWorkspaceSnapshot(parsedSelection.workspace),
|
|
224
|
+
}
|
|
225
|
+
: parsedSelection;
|
|
226
|
+
const requestHash = hashCanonicalJson(selection);
|
|
227
|
+
const canonicalReplay = replayPluginSessionLaunch({
|
|
228
|
+
pluginId: this.principalId,
|
|
172
229
|
scopeId,
|
|
173
230
|
operationId,
|
|
174
231
|
requestHash,
|
|
175
|
-
session,
|
|
176
232
|
});
|
|
233
|
+
if (canonicalReplay)
|
|
234
|
+
return this.sessionEnsureResult(canonicalReplay);
|
|
235
|
+
const resolved = await this.services.sessionLaunch.resolve(selection);
|
|
236
|
+
const ensured = ensurePluginSessionLaunch({
|
|
237
|
+
pluginId: this.principalId,
|
|
238
|
+
scopeId,
|
|
239
|
+
operationId,
|
|
240
|
+
requestHash,
|
|
241
|
+
rawRequestHash,
|
|
242
|
+
session: {
|
|
243
|
+
workspace: sessionWorkspaceSnapshotSchema.parse(resolved.workspace),
|
|
244
|
+
execution: resolvedExecutionSnapshotSchema.parse(resolved.execution),
|
|
245
|
+
...(selection.title ? { title: selection.title } : {}),
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
return this.sessionEnsureResult(ensured);
|
|
249
|
+
}
|
|
250
|
+
async resolveManagedWorkspace(directory) {
|
|
251
|
+
const root = this.managedWorkspaceRoot;
|
|
252
|
+
if (!root) {
|
|
253
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.managedCwd is reserved for the built-in Workspace plugin");
|
|
254
|
+
}
|
|
255
|
+
if (!path.isAbsolute(directory)) {
|
|
256
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.managedCwd must be absolute");
|
|
257
|
+
}
|
|
258
|
+
let canonicalRoot;
|
|
259
|
+
let canonicalDirectory;
|
|
260
|
+
try {
|
|
261
|
+
[canonicalRoot, canonicalDirectory] = await Promise.all([
|
|
262
|
+
realpath(root),
|
|
263
|
+
realpath(directory),
|
|
264
|
+
]);
|
|
265
|
+
}
|
|
266
|
+
catch {
|
|
267
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.managedCwd must be an existing plugin-managed directory");
|
|
268
|
+
}
|
|
269
|
+
const relative = path.relative(canonicalRoot, canonicalDirectory);
|
|
270
|
+
if (relative === ".." ||
|
|
271
|
+
relative.startsWith(`..${path.sep}`) ||
|
|
272
|
+
path.isAbsolute(relative)) {
|
|
273
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.managedCwd escapes the plugin-managed workspace root");
|
|
274
|
+
}
|
|
275
|
+
return canonicalDirectory;
|
|
276
|
+
}
|
|
277
|
+
async resolveWorkspaceSnapshot(workspace) {
|
|
278
|
+
const directories = [workspace.cwd, ...workspace.allowedDirs];
|
|
279
|
+
if (directories.some((directory) => !path.isAbsolute(directory))) {
|
|
280
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.workspace paths must be absolute");
|
|
281
|
+
}
|
|
282
|
+
let canonical;
|
|
283
|
+
try {
|
|
284
|
+
canonical = await Promise.all(directories.map(async (directory) => {
|
|
285
|
+
const resolved = await realpath(directory);
|
|
286
|
+
if (!(await stat(resolved)).isDirectory()) {
|
|
287
|
+
throw new Error("not a directory");
|
|
288
|
+
}
|
|
289
|
+
return resolved;
|
|
290
|
+
}));
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.workspace paths must be existing directories");
|
|
294
|
+
}
|
|
295
|
+
const parsed = sessionWorkspaceSnapshotSchema.safeParse({
|
|
296
|
+
cwd: canonical[0],
|
|
297
|
+
allowedDirs: canonical.slice(1),
|
|
298
|
+
});
|
|
299
|
+
if (!parsed.success) {
|
|
300
|
+
throw new PluginHostCallError("INVALID_PARAMS", `invalid session.workspace: ${parsed.error.issues[0]?.message ?? "invalid snapshot"}`);
|
|
301
|
+
}
|
|
302
|
+
return parsed.data;
|
|
303
|
+
}
|
|
304
|
+
sessionEnsureResult(result) {
|
|
305
|
+
const session = this.ownedSession(result.sessionId);
|
|
306
|
+
return {
|
|
307
|
+
...result,
|
|
308
|
+
execution: {
|
|
309
|
+
provider: session.execution.provider,
|
|
310
|
+
model: session.execution.model,
|
|
311
|
+
reasoningEffort: session.execution.reasoningEffort,
|
|
312
|
+
},
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
async runSession(params, signal) {
|
|
316
|
+
return this.withAdmission(() => this.runSessionAdmitted(params, signal));
|
|
177
317
|
}
|
|
178
|
-
async
|
|
318
|
+
async runSessionAdmitted(params, signal) {
|
|
179
319
|
const input = record(params);
|
|
180
320
|
const sessionId = requiredSessionId(input);
|
|
181
321
|
const operationId = optionalString(input.operationId, "operationId", 512);
|
|
@@ -191,6 +331,12 @@ export class PluginHostRpc {
|
|
|
191
331
|
}
|
|
192
332
|
const message = requiredText(input.message, "message", MAX_MESSAGE_LENGTH);
|
|
193
333
|
const abort = new AbortController();
|
|
334
|
+
const cancel = () => abort.abort(signal?.reason);
|
|
335
|
+
if (signal?.aborted) {
|
|
336
|
+
cancel();
|
|
337
|
+
throwIfHostCallAborted(signal);
|
|
338
|
+
}
|
|
339
|
+
signal?.addEventListener("abort", cancel, { once: true });
|
|
194
340
|
const steer = new SteerController();
|
|
195
341
|
const runId = randomUUID();
|
|
196
342
|
const run = {
|
|
@@ -210,23 +356,23 @@ export class PluginHostRpc {
|
|
|
210
356
|
started = await this.services.conversationRuntime.runLive({
|
|
211
357
|
message,
|
|
212
358
|
threadId: sessionId,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
reasoningEffort: optionalString(input.reasoningEffort, "reasoningEffort", 128) ?? session.reasoningEffort,
|
|
216
|
-
cwd: optionalString(input.cwd, "cwd", 16_384),
|
|
217
|
-
agentName: optionalAgentId(input.agent, "agent") ?? session.agent,
|
|
218
|
-
agentSpec: session.config,
|
|
359
|
+
workspace: session.workspace,
|
|
360
|
+
execution: session.execution,
|
|
219
361
|
signal: abort.signal,
|
|
220
362
|
steer,
|
|
221
|
-
developerInstructions: optionalString(input.developerInstructions, "developerInstructions", 200_000),
|
|
222
363
|
});
|
|
223
364
|
}
|
|
224
365
|
catch (error) {
|
|
225
366
|
this.releaseRun(run);
|
|
226
367
|
throw error;
|
|
227
368
|
}
|
|
228
|
-
|
|
369
|
+
finally {
|
|
370
|
+
signal?.removeEventListener("abort", cancel);
|
|
371
|
+
}
|
|
372
|
+
if (signal?.aborted || this.disposed || this.activeRuns.get(runId) !== run) {
|
|
373
|
+
abort.abort(signal?.reason);
|
|
229
374
|
this.releaseRun(run);
|
|
375
|
+
throwIfHostCallAborted(signal);
|
|
230
376
|
throw new PluginHostCallError("HOST_CLOSED", "plugin host is closed");
|
|
231
377
|
}
|
|
232
378
|
// Let the RPC transport write the request response before stream events are
|
|
@@ -338,6 +484,10 @@ export class PluginHostRpc {
|
|
|
338
484
|
async interruptRun(params) {
|
|
339
485
|
const input = record(params);
|
|
340
486
|
const run = this.activeRunByIdentity(input);
|
|
487
|
+
const forceAfterMs = optionalPositiveNumber(input.forceAfterMs, "forceAfterMs");
|
|
488
|
+
if (forceAfterMs !== undefined && forceAfterMs > 30_000) {
|
|
489
|
+
throw new PluginHostCallError("INVALID_PARAMS", "forceAfterMs must not exceed 30000");
|
|
490
|
+
}
|
|
341
491
|
if (run.interruptResult) {
|
|
342
492
|
return { interrupted: await run.interruptResult };
|
|
343
493
|
}
|
|
@@ -345,7 +495,7 @@ export class PluginHostRpc {
|
|
|
345
495
|
// the authoritative stop is pending wait for its result: true suppresses
|
|
346
496
|
// them, while false/error lets genuine native progress/terminal continue.
|
|
347
497
|
run.interruptRequested = true;
|
|
348
|
-
const
|
|
498
|
+
const graceful = this.services.interruptSession
|
|
349
499
|
// ConversationRuntime wires AbortSignal to this same native operation.
|
|
350
500
|
// Invoke it directly once so this boolean is the authoritative result;
|
|
351
501
|
// aborting first would trigger a duplicate, unobserved interrupt.
|
|
@@ -356,6 +506,9 @@ export class PluginHostRpc {
|
|
|
356
506
|
// effort and cannot prove that native execution stopped.
|
|
357
507
|
return false;
|
|
358
508
|
});
|
|
509
|
+
const interruptResult = forceAfterMs === undefined
|
|
510
|
+
? graceful
|
|
511
|
+
: forceStoppedInterrupt(graceful, forceAfterMs, () => this.services.terminateSession?.(run.sessionId) ?? Promise.resolve(false));
|
|
359
512
|
run.interruptResult = interruptResult;
|
|
360
513
|
try {
|
|
361
514
|
const interrupted = await interruptResult;
|
|
@@ -437,12 +590,40 @@ export class PluginHostRpc {
|
|
|
437
590
|
return await resolve(sessionId, interactionId, resolution);
|
|
438
591
|
}
|
|
439
592
|
async forkSession(params) {
|
|
593
|
+
return this.withAdmission(() => this.forkSessionAdmitted(params));
|
|
594
|
+
}
|
|
595
|
+
async forkSessionAdmitted(params) {
|
|
440
596
|
const input = record(params);
|
|
441
|
-
const
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
this.ownedSession(
|
|
445
|
-
|
|
597
|
+
const sourceSessionId = requiredString(input.sourceSessionId, "sourceSessionId", 512);
|
|
598
|
+
const operationId = requiredString(input.operationId, "operationId", 512);
|
|
599
|
+
const title = optionalString(input.title, "title", 1_000);
|
|
600
|
+
this.ownedSession(sourceSessionId);
|
|
601
|
+
if (!this.services.sessionFork) {
|
|
602
|
+
throw new PluginHostCallError("HOST_CALL_FAILED", "Session fork is unavailable");
|
|
603
|
+
}
|
|
604
|
+
return await this.services.sessionFork({
|
|
605
|
+
sourceSessionId,
|
|
606
|
+
operationId: `${this.principalId}:${operationId}`,
|
|
607
|
+
...(title ? { title } : {}),
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
reserveAdmission() {
|
|
611
|
+
const reservation = this.services.admissionReserve?.();
|
|
612
|
+
if (reservation)
|
|
613
|
+
return reservation;
|
|
614
|
+
if (!this.services.admissionReserve && this.services.admissionOpen?.() !== false) {
|
|
615
|
+
return { release() { } };
|
|
616
|
+
}
|
|
617
|
+
throw new PluginHostCallError("HOST_CALL_FAILED", "daemon maintenance is in progress", true);
|
|
618
|
+
}
|
|
619
|
+
async withAdmission(operation) {
|
|
620
|
+
const reservation = this.reserveAdmission();
|
|
621
|
+
try {
|
|
622
|
+
return await operation();
|
|
623
|
+
}
|
|
624
|
+
finally {
|
|
625
|
+
reservation.release();
|
|
626
|
+
}
|
|
446
627
|
}
|
|
447
628
|
setSessionTitle(params) {
|
|
448
629
|
const input = record(params);
|
|
@@ -466,7 +647,7 @@ export class PluginHostRpc {
|
|
|
466
647
|
this.ownedSession(sessionId);
|
|
467
648
|
const ttlSeconds = optionalPositiveNumber(input.ttlSeconds, "ttlSeconds");
|
|
468
649
|
return createControlLink({
|
|
469
|
-
pluginId: this.
|
|
650
|
+
pluginId: this.principalId,
|
|
470
651
|
sessionId,
|
|
471
652
|
...(ttlSeconds === undefined ? {} : { ttlMs: ttlSeconds * 1_000 }),
|
|
472
653
|
});
|
|
@@ -479,9 +660,24 @@ export class PluginHostRpc {
|
|
|
479
660
|
return session;
|
|
480
661
|
}
|
|
481
662
|
isOwned(session) {
|
|
482
|
-
|
|
663
|
+
const seen = new Set();
|
|
664
|
+
let current = session;
|
|
665
|
+
while (current && !seen.has(current.id)) {
|
|
666
|
+
if (current.source === `plugin:${this.principalId}`)
|
|
667
|
+
return true;
|
|
668
|
+
seen.add(current.id);
|
|
669
|
+
current = current.forkedFromSessionId
|
|
670
|
+
? this.services.sessionRegistry.get(current.forkedFromSessionId)
|
|
671
|
+
: undefined;
|
|
672
|
+
}
|
|
673
|
+
return false;
|
|
483
674
|
}
|
|
484
675
|
}
|
|
676
|
+
function throwIfHostCallAborted(signal) {
|
|
677
|
+
if (!signal?.aborted)
|
|
678
|
+
return;
|
|
679
|
+
throw new PluginHostCallError("REQUEST_CANCELLED", "plugin host request was cancelled");
|
|
680
|
+
}
|
|
485
681
|
export class PluginHostCallError extends PluginRunnerError {
|
|
486
682
|
retryable;
|
|
487
683
|
data;
|
|
@@ -512,22 +708,50 @@ function toHostCallError(error) {
|
|
|
512
708
|
}
|
|
513
709
|
return new PluginHostCallError("HOST_CALL_FAILED", error instanceof Error ? error.message : String(error));
|
|
514
710
|
}
|
|
515
|
-
function
|
|
711
|
+
function sessionLaunchSelection(value) {
|
|
516
712
|
const input = record(value);
|
|
517
713
|
assertJsonValue(input, "session");
|
|
714
|
+
const projectId = optionalString(input.projectId, "session.projectId", 512);
|
|
715
|
+
const managedCwd = optionalString(input.managedCwd, "session.managedCwd", 16_384);
|
|
716
|
+
const workspace = optionalSessionWorkspace(input.workspace);
|
|
518
717
|
const agent = optionalAgentId(input.agent, "session.agent");
|
|
519
718
|
const config = input.config === undefined ? undefined : agentSpecSchema.parse(input.config);
|
|
719
|
+
const provider = optionalRuntimeValue(input.provider);
|
|
520
720
|
const model = optionalString(input.model, "session.model", 256);
|
|
521
721
|
const reasoningEffort = optionalString(input.reasoningEffort, "session.reasoningEffort", 128);
|
|
522
722
|
const title = optionalString(input.title, "session.title", 1_000);
|
|
723
|
+
if (Number(Boolean(projectId)) + Number(Boolean(managedCwd)) + Number(Boolean(workspace)) > 1) {
|
|
724
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.projectId, session.managedCwd, and session.workspace are mutually exclusive");
|
|
725
|
+
}
|
|
726
|
+
if (Number(Boolean(agent)) +
|
|
727
|
+
Number(Boolean(config)) +
|
|
728
|
+
Number(Boolean(provider)) >
|
|
729
|
+
1) {
|
|
730
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.agent, session.config, and session.provider are mutually exclusive");
|
|
731
|
+
}
|
|
523
732
|
return {
|
|
733
|
+
...(projectId ? { projectId } : {}),
|
|
734
|
+
...(managedCwd ? { managedCwd } : {}),
|
|
735
|
+
...(workspace ? { workspace } : {}),
|
|
524
736
|
...(agent ? { agent } : {}),
|
|
525
737
|
...(config ? { config } : {}),
|
|
738
|
+
...(provider ? { provider } : {}),
|
|
526
739
|
...(model ? { model } : {}),
|
|
527
740
|
...(reasoningEffort ? { reasoningEffort } : {}),
|
|
528
741
|
...(title ? { title } : {}),
|
|
529
742
|
};
|
|
530
743
|
}
|
|
744
|
+
function optionalSessionWorkspace(value) {
|
|
745
|
+
if (value === undefined)
|
|
746
|
+
return undefined;
|
|
747
|
+
const input = record(value);
|
|
748
|
+
const cwd = requiredPathString(input.cwd, "session.workspace.cwd", 16_384);
|
|
749
|
+
if (!Array.isArray(input.allowedDirs) || input.allowedDirs.length === 0) {
|
|
750
|
+
throw new PluginHostCallError("INVALID_PARAMS", "session.workspace.allowedDirs must be a non-empty string array");
|
|
751
|
+
}
|
|
752
|
+
const allowedDirs = input.allowedDirs.map((directory, index) => requiredPathString(directory, `session.workspace.allowedDirs[${index}]`, 16_384));
|
|
753
|
+
return { cwd, allowedDirs };
|
|
754
|
+
}
|
|
531
755
|
function sessionExecutionSnapshot(value) {
|
|
532
756
|
const input = record(value);
|
|
533
757
|
const runtime = optionalRuntimeValue(input.runtime);
|
|
@@ -608,6 +832,15 @@ function requiredString(value, label, maxLength) {
|
|
|
608
832
|
}
|
|
609
833
|
return value.trim();
|
|
610
834
|
}
|
|
835
|
+
function requiredPathString(value, label, maxLength) {
|
|
836
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
837
|
+
throw new PluginHostCallError("INVALID_PARAMS", `${label} is required`);
|
|
838
|
+
}
|
|
839
|
+
if (value.length > maxLength) {
|
|
840
|
+
throw new PluginHostCallError("INVALID_PARAMS", `${label} is too long`);
|
|
841
|
+
}
|
|
842
|
+
return value;
|
|
843
|
+
}
|
|
611
844
|
function requiredText(value, label, maxLength) {
|
|
612
845
|
if (typeof value !== "string" || !value.trim()) {
|
|
613
846
|
throw new PluginHostCallError("INVALID_PARAMS", `${label} is required`);
|
|
@@ -648,6 +881,23 @@ function optionalPositiveNumber(value, label) {
|
|
|
648
881
|
}
|
|
649
882
|
return value;
|
|
650
883
|
}
|
|
884
|
+
async function forceStoppedInterrupt(graceful, forceAfterMs, terminate) {
|
|
885
|
+
let timer;
|
|
886
|
+
const graceExpired = new Promise((resolve) => {
|
|
887
|
+
timer = setTimeout(() => resolve(false), forceAfterMs);
|
|
888
|
+
timer.unref?.();
|
|
889
|
+
});
|
|
890
|
+
try {
|
|
891
|
+
if (await Promise.race([graceful.catch(() => false), graceExpired])) {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
finally {
|
|
896
|
+
if (timer)
|
|
897
|
+
clearTimeout(timer);
|
|
898
|
+
}
|
|
899
|
+
return terminate();
|
|
900
|
+
}
|
|
651
901
|
function assertJsonValue(value, label, seen = new Set()) {
|
|
652
902
|
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
653
903
|
return;
|