paseo-plugin-zcode-provider 0.0.0-stage → 0.1.0
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/LICENSE +21 -0
- package/NOTICE.md +29 -0
- package/README.md +98 -2
- package/client/diagnostics.tsx +397 -0
- package/client/setup.tsx +347 -0
- package/icon.svg +1 -0
- package/index.client.tsx +22 -0
- package/index.server.ts +21 -0
- package/package.json +75 -4
- package/paseo-plugin.json +6 -0
- package/server/attachments.ts +150 -0
- package/server/build-info.ts +2 -0
- package/server/conversation.ts +319 -0
- package/server/diagnostics.ts +235 -0
- package/server/discovery/discover.ts +225 -0
- package/server/discovery/manifest.ts +36 -0
- package/server/discovery/types.ts +30 -0
- package/server/errors.ts +36 -0
- package/server/host/bridge.ts +463 -0
- package/server/host/schemas.ts +119 -0
- package/server/logger.ts +19 -0
- package/server/mapping.ts +270 -0
- package/server/models.ts +68 -0
- package/server/persistence.ts +146 -0
- package/server/presentation.ts +428 -0
- package/server/provider.ts +831 -0
- package/server/runtime/managed.ts +501 -0
- package/server/runtime/pins.ts +72 -0
- package/server/runtime-setup.ts +102 -0
- package/server/session-types.ts +49 -0
- package/server/session.ts +866 -0
- package/server/status.ts +89 -0
- package/server/timeline.ts +18 -0
- package/server/vendor/zcode/LICENSE +202 -0
- package/server/vendor/zcode/NOTICE.md +70 -0
- package/server/vendor/zcode/THIRD-PARTY-NOTICES.md +37 -0
- package/server/vendor/zcode/packages/model-option-map/src/compiler.ts +92 -0
- package/server/vendor/zcode/packages/model-option-map/src/evaluator.ts +214 -0
- package/server/vendor/zcode/packages/model-option-map/src/index.ts +5 -0
- package/server/vendor/zcode/packages/model-option-map/src/merge-patch.ts +97 -0
- package/server/vendor/zcode/packages/model-option-map/src/option-maps.ts +42 -0
- package/server/vendor/zcode/packages/model-option-map/src/parser.ts +240 -0
- package/server/vendor/zcode/packages/model-option-map/src/tokenizer.ts +141 -0
- package/server/vendor/zcode/packages/model-option-map/src/types.ts +36 -0
- package/server/vendor/zcode/packages/rpc/src/buffer.ts +88 -0
- package/server/vendor/zcode/packages/rpc/src/channelClient.ts +275 -0
- package/server/vendor/zcode/packages/rpc/src/channelServer.ts +283 -0
- package/server/vendor/zcode/packages/rpc/src/channels.shared.ts +66 -0
- package/server/vendor/zcode/packages/rpc/src/channels.ts +4 -0
- package/server/vendor/zcode/packages/rpc/src/delayedChannel.ts +17 -0
- package/server/vendor/zcode/packages/rpc/src/foundation.ts +260 -0
- package/server/vendor/zcode/packages/rpc/src/protocol.ts +397 -0
- package/server/vendor/zcode/packages/rpc/src/proxy-channel.ts +213 -0
- package/server/vendor/zcode/packages/rpc/src/serialization.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/account-provider-state.ts +13 -0
- package/server/vendor/zcode/packages/shared/src/background-bash-output.ts +24 -0
- package/server/vendor/zcode/packages/shared/src/background-task-controls.ts +404 -0
- package/server/vendor/zcode/packages/shared/src/bash-output-display.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/bots.ts +558 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/backend.ts +80 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/command-metadata.ts +170 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/commands.ts +588 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/result.ts +130 -0
- package/server/vendor/zcode/packages/shared/src/browser-use/snapshot.ts +77 -0
- package/server/vendor/zcode/packages/shared/src/command-types.ts +96 -0
- package/server/vendor/zcode/packages/shared/src/config-schema.ts +8 -0
- package/server/vendor/zcode/packages/shared/src/cuaAccessibilitySettings.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/database-startup.ts +178 -0
- package/server/vendor/zcode/packages/shared/src/env.ts +63 -0
- package/server/vendor/zcode/packages/shared/src/execution-output-preview.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/localTtft.ts +192 -0
- package/server/vendor/zcode/packages/shared/src/model-config.ts +123 -0
- package/server/vendor/zcode/packages/shared/src/model-execution.ts +23 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-family.ts +171 -0
- package/server/vendor/zcode/packages/shared/src/model-provider-types.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/model-selection.ts +60 -0
- package/server/vendor/zcode/packages/shared/src/oauth.ts +203 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-auth.ts +272 -0
- package/server/vendor/zcode/packages/shared/src/official-mcp-tool-error.ts +68 -0
- package/server/vendor/zcode/packages/shared/src/process-diagnostic.ts +42 -0
- package/server/vendor/zcode/packages/shared/src/protocol.ts +369 -0
- package/server/vendor/zcode/packages/shared/src/provider-family-connection-selection.ts +32 -0
- package/server/vendor/zcode/packages/shared/src/remoteAssetInstallMode.ts +11 -0
- package/server/vendor/zcode/packages/shared/src/remoteResourcePackages.ts +91 -0
- package/server/vendor/zcode/packages/shared/src/runtimeEnv.ts +315 -0
- package/server/vendor/zcode/packages/shared/src/settings-source.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/usage-quota.ts +74 -0
- package/server/vendor/zcode/packages/shared/src/usage-stats.ts +462 -0
- package/server/vendor/zcode/packages/shared/src/validationAppSettings.ts +562 -0
- package/server/vendor/zcode/packages/shared/src/workspacePurpose.ts +2 -0
- package/server/vendor/zcode/packages/shared/src/wslUserValidation.ts +33 -0
- package/server/vendor/zcode/packages/shared/src/zcode-agent-policy.ts +25 -0
- package/server/vendor/zcode/packages/shared/src/zcode-media-policy.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol/index.ts +3720 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-legacy-types.ts +529 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/apply.ts +218 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/attachment-ref.ts +14 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/command.ts +482 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/core.ts +100 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/create-workflow-display.ts +156 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/cuaPermission.ts +41 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/delta.ts +147 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/input-intent.ts +69 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/rows.ts +432 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/session-config.ts +43 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index-workflow-activity.ts +195 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/sessions-index.ts +85 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-import.ts +38 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/shared-context-ref.ts +10 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/snapshot.ts +509 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/submission.ts +5 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/toolDisplay.ts +264 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/transport.ts +1152 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-assembler.ts +574 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-binary.ts +65 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-codec.ts +240 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire-fault.ts +39 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/wire.ts +159 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-artifacts.ts +291 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-observation-display.ts +281 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-row-meta.ts +163 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-run-settings-command.ts +62 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs-delta.ts +517 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workflow-runs.ts +512 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-config.ts +81 -0
- package/server/vendor/zcode/packages/shared/src/zcode-protocol-v4/workspace-hook-review.ts +210 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-mode-schema.ts +6 -0
- package/server/vendor/zcode/packages/shared/src/zcode-task-types-core.ts +1187 -0
- package/server/vendor/zcode/packages/shared/src/zcodeEndpoint.ts +296 -0
- package/server/vendor/zcode/provenance.json +405 -0
- package/shared/diagnostics.ts +52 -0
- package/shared/runtime-setup.ts +75 -0
- package/vendor/acp-registry/LICENSE +176 -0
- package/vendor/paseo/LICENSE +211 -0
|
@@ -0,0 +1,831 @@
|
|
|
1
|
+
import { resolveModelSelection } from "./models.js";
|
|
2
|
+
import {
|
|
3
|
+
PROVIDER_PROTOCOL_VERSION,
|
|
4
|
+
ProviderInputSchema,
|
|
5
|
+
ProviderEventSchema,
|
|
6
|
+
negotiateProviderCapabilities,
|
|
7
|
+
requireProviderCapabilities,
|
|
8
|
+
type ProviderRegistration,
|
|
9
|
+
type ProviderConnection,
|
|
10
|
+
type ProviderEvent,
|
|
11
|
+
type ProviderInput,
|
|
12
|
+
type ProviderCapability,
|
|
13
|
+
type ProviderError,
|
|
14
|
+
} from "@getpaseo/plugin/server/provider";
|
|
15
|
+
import { randomUUID } from "node:crypto";
|
|
16
|
+
import {
|
|
17
|
+
SessionPersistenceStore,
|
|
18
|
+
parsePersistence,
|
|
19
|
+
persistenceHandle,
|
|
20
|
+
type PersistenceData,
|
|
21
|
+
} from "./persistence.js";
|
|
22
|
+
import { homedir } from "node:os";
|
|
23
|
+
import { z } from "zod";
|
|
24
|
+
import {
|
|
25
|
+
assertRuntimeSupported,
|
|
26
|
+
discoverRuntime,
|
|
27
|
+
runRuntimeSmoke,
|
|
28
|
+
} from "./discovery/discover.js";
|
|
29
|
+
import { ZCodeHostBridge, type HostBridge } from "./host/bridge.js";
|
|
30
|
+
import { logger } from "./logger.js";
|
|
31
|
+
import { MINIMUM_ZCODE_VERSION } from "./discovery/manifest.js";
|
|
32
|
+
import {
|
|
33
|
+
diagnosticError,
|
|
34
|
+
formatDiagnostic,
|
|
35
|
+
runtimeDiagnostic,
|
|
36
|
+
} from "./diagnostics.js";
|
|
37
|
+
import { AdapterError } from "./errors.js";
|
|
38
|
+
import {
|
|
39
|
+
catalogModels,
|
|
40
|
+
decodeModel,
|
|
41
|
+
encodeModel,
|
|
42
|
+
mapMcpServers,
|
|
43
|
+
ZCODE_MODES,
|
|
44
|
+
} from "./mapping.js";
|
|
45
|
+
import {
|
|
46
|
+
ZCodeSession,
|
|
47
|
+
initializeWorkspace,
|
|
48
|
+
resolveWorkspace,
|
|
49
|
+
} from "./session.js";
|
|
50
|
+
import { SessionSnapshotSchema, SessionListSchema } from "./host/schemas.js";
|
|
51
|
+
import { TimelineSnapshots } from "./timeline.js";
|
|
52
|
+
import type { NativeSessionEvent } from "./session-types.js";
|
|
53
|
+
|
|
54
|
+
export const CAPABILITIES = [
|
|
55
|
+
"prompt.message",
|
|
56
|
+
"prompt.command",
|
|
57
|
+
"prompt.image",
|
|
58
|
+
"prompt.steer",
|
|
59
|
+
"session.configure",
|
|
60
|
+
"session.list",
|
|
61
|
+
"session.persistence",
|
|
62
|
+
"permission",
|
|
63
|
+
] as const satisfies readonly ProviderCapability[];
|
|
64
|
+
|
|
65
|
+
export type BridgeFactory = (
|
|
66
|
+
environment: Readonly<Record<string, string>>,
|
|
67
|
+
signal: AbortSignal,
|
|
68
|
+
workspace?: string,
|
|
69
|
+
) => Promise<HostBridge>;
|
|
70
|
+
|
|
71
|
+
async function createHost(
|
|
72
|
+
environment: Readonly<Record<string, string>>,
|
|
73
|
+
signal: AbortSignal,
|
|
74
|
+
workspace?: string,
|
|
75
|
+
): Promise<HostBridge> {
|
|
76
|
+
const env = { ...process.env, ...environment };
|
|
77
|
+
const runtime = await discoverRuntime({ environment: env, signal });
|
|
78
|
+
assertRuntimeSupported(runtime);
|
|
79
|
+
try {
|
|
80
|
+
const smoke = await runRuntimeSmoke(runtime, env, signal);
|
|
81
|
+
if (!smoke.passed)
|
|
82
|
+
throw diagnosticError(
|
|
83
|
+
new AdapterError("RUNTIME_SMOKE_FAILED", "ZCode runtime smoke failed"),
|
|
84
|
+
{ stage: "smoke", check: "cli-smoke" },
|
|
85
|
+
);
|
|
86
|
+
signal.throwIfAborted();
|
|
87
|
+
return ZCodeHostBridge.start(runtime, logger, env, workspace);
|
|
88
|
+
} catch (error) {
|
|
89
|
+
if (signal.aborted) throw error;
|
|
90
|
+
throw diagnosticError(
|
|
91
|
+
error,
|
|
92
|
+
{ ...runtimeDiagnostic(runtime), stage: "host-start" },
|
|
93
|
+
"RUNTIME_DISCOVERY_FAILED",
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const optionsSchema = z.object({}).strict();
|
|
99
|
+
const settingsSchema = z.object({ plan_mode: z.boolean().optional() }).strict();
|
|
100
|
+
|
|
101
|
+
interface SessionEntry {
|
|
102
|
+
native: ZCodeSession;
|
|
103
|
+
persistence: PersistenceData;
|
|
104
|
+
submitted: boolean;
|
|
105
|
+
bridge: HostBridge;
|
|
106
|
+
unsubscribe: () => void;
|
|
107
|
+
unsubscribeFailure: () => void;
|
|
108
|
+
timeline: TimelineSnapshots;
|
|
109
|
+
messages: Map<string, boolean>;
|
|
110
|
+
inputGeneration: number;
|
|
111
|
+
queue: Promise<void>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function createZCodeProvider(
|
|
115
|
+
bridgeFactory: BridgeFactory = createHost,
|
|
116
|
+
persistenceStore = new SessionPersistenceStore(),
|
|
117
|
+
): ProviderRegistration {
|
|
118
|
+
return {
|
|
119
|
+
id: "zcode",
|
|
120
|
+
label: "ZCode",
|
|
121
|
+
description:
|
|
122
|
+
"ZCode workspace agent with native planning, tools, and model selection",
|
|
123
|
+
icon: "icon.svg",
|
|
124
|
+
async connect(request) {
|
|
125
|
+
if (!request.versions.includes(PROVIDER_PROTOCOL_VERSION))
|
|
126
|
+
throw new Error("ZCode requires provider protocol version 1");
|
|
127
|
+
return new ZCodeConnection(
|
|
128
|
+
negotiateProviderCapabilities(request.capabilities, CAPABILITIES),
|
|
129
|
+
bridgeFactory,
|
|
130
|
+
persistenceStore,
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export class ZCodeConnection implements ProviderConnection {
|
|
137
|
+
readonly version = PROVIDER_PROTOCOL_VERSION;
|
|
138
|
+
private readonly listeners = new Set<(event: ProviderEvent) => void>();
|
|
139
|
+
private readonly sessions = new Map<string, SessionEntry>();
|
|
140
|
+
private readonly opening = new Set<string>();
|
|
141
|
+
private readonly jobs = new Set<Promise<void>>();
|
|
142
|
+
private readonly hosts = new Set<HostBridge>();
|
|
143
|
+
private readonly abort = new AbortController();
|
|
144
|
+
private closed = false;
|
|
145
|
+
private closing?: Promise<void>;
|
|
146
|
+
|
|
147
|
+
constructor(
|
|
148
|
+
readonly capabilities: readonly string[],
|
|
149
|
+
private readonly bridgeFactory: BridgeFactory,
|
|
150
|
+
private readonly persistenceStore: SessionPersistenceStore,
|
|
151
|
+
) {}
|
|
152
|
+
|
|
153
|
+
onEvent(listener: (event: ProviderEvent) => void): () => void {
|
|
154
|
+
if (this.closed) throw new Error("ZCode connection is closed");
|
|
155
|
+
this.listeners.add(listener);
|
|
156
|
+
return () => this.listeners.delete(listener);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async send(raw: ProviderInput): Promise<void> {
|
|
160
|
+
if (this.closed) throw new Error("ZCode connection is closed");
|
|
161
|
+
const input = ProviderInputSchema.parse(raw);
|
|
162
|
+
requireProviderCapabilities(this.capabilities, input);
|
|
163
|
+
if (input.type === "session.open") {
|
|
164
|
+
if (
|
|
165
|
+
this.sessions.has(input.sessionId) ||
|
|
166
|
+
this.opening.has(input.sessionId)
|
|
167
|
+
)
|
|
168
|
+
throw new Error("Duplicate ZCode session ID");
|
|
169
|
+
this.opening.add(input.sessionId);
|
|
170
|
+
}
|
|
171
|
+
if (input.type === "session.prompt") {
|
|
172
|
+
const entry = this.requireSession(input.sessionId);
|
|
173
|
+
if (entry.messages.has(input.prompt.clientMessageId))
|
|
174
|
+
throw new Error("Duplicate ZCode clientMessageId");
|
|
175
|
+
entry.messages.set(input.prompt.clientMessageId, false);
|
|
176
|
+
}
|
|
177
|
+
if (input.type === "session.interrupt")
|
|
178
|
+
this.requireSession(input.sessionId).inputGeneration++;
|
|
179
|
+
const entry =
|
|
180
|
+
input.type === "session.prompt" || input.type === "session.configure"
|
|
181
|
+
? this.requireSession(input.sessionId)
|
|
182
|
+
: undefined;
|
|
183
|
+
const releaseInput =
|
|
184
|
+
input.type === "session.prompt"
|
|
185
|
+
? entry!.native.reserveInput()
|
|
186
|
+
: undefined;
|
|
187
|
+
const inputGeneration = entry?.inputGeneration;
|
|
188
|
+
const operation = entry
|
|
189
|
+
? entry.queue.then(() => {
|
|
190
|
+
if (
|
|
191
|
+
input.type === "session.prompt" &&
|
|
192
|
+
inputGeneration !== entry.inputGeneration
|
|
193
|
+
)
|
|
194
|
+
throw new AdapterError(
|
|
195
|
+
"SESSION_BUSY",
|
|
196
|
+
"ZCode input was cancelled before sending",
|
|
197
|
+
);
|
|
198
|
+
return this.dispatch(input);
|
|
199
|
+
})
|
|
200
|
+
: this.dispatch(input);
|
|
201
|
+
if (entry) entry.queue = operation.catch(() => undefined);
|
|
202
|
+
const job = operation
|
|
203
|
+
.catch((cause: unknown) => {
|
|
204
|
+
const error = diagnosticError(
|
|
205
|
+
cause,
|
|
206
|
+
{
|
|
207
|
+
...entry?.bridge.diagnostic,
|
|
208
|
+
stage: "request",
|
|
209
|
+
operation: input.type,
|
|
210
|
+
},
|
|
211
|
+
"INVALID_CONFIGURATION",
|
|
212
|
+
);
|
|
213
|
+
logger.error("zcode.provider.request_failed", error);
|
|
214
|
+
if (input.type === "session.prompt") {
|
|
215
|
+
const promptEntry = entry ?? this.sessions.get(input.sessionId);
|
|
216
|
+
if (promptEntry?.messages.get(input.prompt.clientMessageId)) return;
|
|
217
|
+
promptEntry?.messages.set(input.prompt.clientMessageId, true);
|
|
218
|
+
this.emit({
|
|
219
|
+
type: "session.prompt_result",
|
|
220
|
+
sessionId: input.sessionId,
|
|
221
|
+
clientMessageId: input.prompt.clientMessageId,
|
|
222
|
+
result: { type: "failed", error: publicError(error) },
|
|
223
|
+
});
|
|
224
|
+
} else if ("requestId" in input) {
|
|
225
|
+
this.emit({
|
|
226
|
+
type: "request.failed",
|
|
227
|
+
requestId: input.requestId,
|
|
228
|
+
error: publicError(error),
|
|
229
|
+
});
|
|
230
|
+
} else {
|
|
231
|
+
// Permission send has no request ID; fail and close the native runtime too.
|
|
232
|
+
const entry = this.sessions.get(input.sessionId);
|
|
233
|
+
if (entry)
|
|
234
|
+
entry.native.runtimeFailed(
|
|
235
|
+
error instanceof AdapterError
|
|
236
|
+
? error
|
|
237
|
+
: new AdapterError(
|
|
238
|
+
"NATIVE_PROTOCOL_ERROR",
|
|
239
|
+
"ZCode permission response failed",
|
|
240
|
+
),
|
|
241
|
+
);
|
|
242
|
+
else
|
|
243
|
+
this.emit({
|
|
244
|
+
type: "session.runtime_failed",
|
|
245
|
+
sessionId: input.sessionId,
|
|
246
|
+
error: publicError(error),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
.finally(() => {
|
|
251
|
+
releaseInput?.();
|
|
252
|
+
this.jobs.delete(job);
|
|
253
|
+
if (input.type === "session.open") this.opening.delete(input.sessionId);
|
|
254
|
+
});
|
|
255
|
+
this.jobs.add(job);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
close(): Promise<void> {
|
|
259
|
+
if (this.closing) return this.closing;
|
|
260
|
+
this.closed = true;
|
|
261
|
+
this.abort.abort();
|
|
262
|
+
this.closing = this.closeAll();
|
|
263
|
+
return this.closing;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private async closeAll(): Promise<void> {
|
|
267
|
+
// Closing the transport rejects pending RPCs and releases turns awaiting a terminal event.
|
|
268
|
+
for (const entry of this.sessions.values()) entry.native.runtimeFailed();
|
|
269
|
+
await Promise.allSettled([
|
|
270
|
+
...[...this.sessions.keys()].map((id) => this.closeSession(id)),
|
|
271
|
+
...[...this.hosts].map((host) => host.close()),
|
|
272
|
+
]);
|
|
273
|
+
await Promise.allSettled([...this.jobs]);
|
|
274
|
+
this.listeners.clear();
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private async host(
|
|
278
|
+
env: Readonly<Record<string, string>>,
|
|
279
|
+
workspace: string,
|
|
280
|
+
): Promise<HostBridge> {
|
|
281
|
+
const host = await this.bridgeFactory(env, this.abort.signal, workspace);
|
|
282
|
+
if (this.closed) {
|
|
283
|
+
await host.close();
|
|
284
|
+
throw new Error("ZCode connection is closed");
|
|
285
|
+
}
|
|
286
|
+
this.hosts.add(host);
|
|
287
|
+
return host;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async dispatch(input: ProviderInput): Promise<void> {
|
|
291
|
+
if (input.type === "catalog" || input.type === "sessions") {
|
|
292
|
+
if (input.type === "sessions" && !input.cwd)
|
|
293
|
+
throw new AdapterError(
|
|
294
|
+
"INVALID_WORKSPACE",
|
|
295
|
+
"ZCode session listing requires a workspace",
|
|
296
|
+
);
|
|
297
|
+
const cwd = await resolveWorkspace(input.cwd ?? homedir());
|
|
298
|
+
const host = await this.host({}, cwd);
|
|
299
|
+
try {
|
|
300
|
+
const { presentation, selection } = await initializeWorkspace(
|
|
301
|
+
host,
|
|
302
|
+
cwd,
|
|
303
|
+
);
|
|
304
|
+
if (input.type === "catalog") {
|
|
305
|
+
const models = catalogModels(
|
|
306
|
+
selection.models,
|
|
307
|
+
selection.preferredSelection,
|
|
308
|
+
);
|
|
309
|
+
this.emit({
|
|
310
|
+
type: "catalog",
|
|
311
|
+
requestId: input.requestId,
|
|
312
|
+
catalog: {
|
|
313
|
+
models,
|
|
314
|
+
modes: ZCODE_MODES,
|
|
315
|
+
defaultModel: models.find((model) => model.isDefault)?.id,
|
|
316
|
+
defaultMode: presentation.mode,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
} else {
|
|
320
|
+
const rows = await host.request(
|
|
321
|
+
"listSessions",
|
|
322
|
+
{ workspacePath: cwd, includeArchived: false },
|
|
323
|
+
SessionListSchema,
|
|
324
|
+
);
|
|
325
|
+
const sessions = rows
|
|
326
|
+
.map((row) => {
|
|
327
|
+
if (row.workspace.workspacePath !== cwd)
|
|
328
|
+
throw new AdapterError(
|
|
329
|
+
"INVALID_WORKSPACE",
|
|
330
|
+
"ZCode returned a session from another workspace",
|
|
331
|
+
);
|
|
332
|
+
return {
|
|
333
|
+
persistence: {
|
|
334
|
+
version: 3,
|
|
335
|
+
data: { kind: "native", sessionId: row.sessionId, cwd },
|
|
336
|
+
},
|
|
337
|
+
cwd,
|
|
338
|
+
title: row.title,
|
|
339
|
+
...(row.updatedAt === undefined
|
|
340
|
+
? {}
|
|
341
|
+
: { updatedAt: new Date(row.updatedAt).toISOString() }),
|
|
342
|
+
};
|
|
343
|
+
})
|
|
344
|
+
.filter(
|
|
345
|
+
(row) =>
|
|
346
|
+
!input.query ||
|
|
347
|
+
(row.title ?? "")
|
|
348
|
+
.toLowerCase()
|
|
349
|
+
.includes(input.query.toLowerCase()),
|
|
350
|
+
)
|
|
351
|
+
.slice(0, input.limit);
|
|
352
|
+
this.emit({ type: "sessions", requestId: input.requestId, sessions });
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
this.hosts.delete(host);
|
|
356
|
+
await host.close();
|
|
357
|
+
}
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
if (input.type === "session.open") return this.openSession(input);
|
|
361
|
+
if (
|
|
362
|
+
input.type === "session.archive" ||
|
|
363
|
+
input.type === "session.unarchive" ||
|
|
364
|
+
input.type === "session.revert"
|
|
365
|
+
)
|
|
366
|
+
throw new Error("Unsupported ZCode operation");
|
|
367
|
+
const entry = this.requireSession(input.sessionId);
|
|
368
|
+
switch (input.type) {
|
|
369
|
+
case "session.prompt": {
|
|
370
|
+
const prompt = input.prompt.input;
|
|
371
|
+
if (input.prompt.clearPendingPermissions)
|
|
372
|
+
throw new AdapterError(
|
|
373
|
+
"INTERACTION_UNSUPPORTED",
|
|
374
|
+
"Steering does not resolve pending ZCode permissions",
|
|
375
|
+
);
|
|
376
|
+
if (input.prompt.delivery === "steer" && !entry.native.hasActiveTurn())
|
|
377
|
+
throw new AdapterError(
|
|
378
|
+
"SESSION_BUSY",
|
|
379
|
+
"ZCode has no active turn to steer",
|
|
380
|
+
);
|
|
381
|
+
if (
|
|
382
|
+
entry.native.hasActiveTurn() &&
|
|
383
|
+
(prompt.type === "command" ||
|
|
384
|
+
prompt.content.some(
|
|
385
|
+
(part) => part.type === "text" && /^\s*\//.test(part.text),
|
|
386
|
+
))
|
|
387
|
+
)
|
|
388
|
+
throw new AdapterError(
|
|
389
|
+
"SESSION_BUSY",
|
|
390
|
+
"Commands cannot run while ZCode is processing input",
|
|
391
|
+
);
|
|
392
|
+
if (
|
|
393
|
+
prompt.type === "command" &&
|
|
394
|
+
!(await entry.native.listCommands()).some(
|
|
395
|
+
(command) => command.name === prompt.name,
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
throw new Error("Unknown ZCode command");
|
|
399
|
+
// ZCode desktop implements /plan as setMode plus an optional task.
|
|
400
|
+
// Native text admission does not interpret this shortcut.
|
|
401
|
+
const text =
|
|
402
|
+
prompt.type === "message" && prompt.content[0]?.type === "text"
|
|
403
|
+
? prompt.content[0].text.trim()
|
|
404
|
+
: undefined;
|
|
405
|
+
const planTask =
|
|
406
|
+
prompt.type === "command" && prompt.name === "plan"
|
|
407
|
+
? prompt.arguments.trim()
|
|
408
|
+
: (text?.match(/^\/plan(?:\s+([\s\S]*))?$/)?.[1]?.trim() ??
|
|
409
|
+
(text === "/plan" ? "" : undefined));
|
|
410
|
+
if (planTask !== undefined) {
|
|
411
|
+
if (prompt.type === "message" && prompt.content.length !== 1)
|
|
412
|
+
throw new AdapterError(
|
|
413
|
+
"INVALID_CONFIGURATION",
|
|
414
|
+
"The /plan shortcut requires text without attachments",
|
|
415
|
+
);
|
|
416
|
+
await entry.native.configureMode(undefined, true);
|
|
417
|
+
if (!planTask) {
|
|
418
|
+
entry.messages.set(input.prompt.clientMessageId, true);
|
|
419
|
+
this.emit({
|
|
420
|
+
type: "session.prompt_result",
|
|
421
|
+
sessionId: input.sessionId,
|
|
422
|
+
clientMessageId: input.prompt.clientMessageId,
|
|
423
|
+
result: { type: "completed" },
|
|
424
|
+
});
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
await entry.native.startTurn(
|
|
429
|
+
planTask ??
|
|
430
|
+
(prompt.type === "command"
|
|
431
|
+
? `/${prompt.name}${prompt.arguments ? ` ${prompt.arguments}` : ""}`
|
|
432
|
+
: prompt.content),
|
|
433
|
+
{
|
|
434
|
+
clientMessageId: input.prompt.clientMessageId,
|
|
435
|
+
delivery: input.prompt.delivery,
|
|
436
|
+
beforeSend: async () => {
|
|
437
|
+
if (!entry.submitted) {
|
|
438
|
+
await this.persistenceStore.save(
|
|
439
|
+
entry.persistence,
|
|
440
|
+
entry.native.id,
|
|
441
|
+
);
|
|
442
|
+
entry.submitted = true;
|
|
443
|
+
}
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
);
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
case "session.configure": {
|
|
450
|
+
const settings = settingsSchema.parse(input.changes.settings ?? {});
|
|
451
|
+
if (
|
|
452
|
+
input.changes.mode !== undefined &&
|
|
453
|
+
!ZCODE_MODES.some((mode) => mode.id === input.changes.mode)
|
|
454
|
+
)
|
|
455
|
+
throw new AdapterError(
|
|
456
|
+
"INVALID_CONFIGURATION",
|
|
457
|
+
"ZCode editing mode must be build, edit, or yolo",
|
|
458
|
+
);
|
|
459
|
+
if (input.changes.model !== undefined)
|
|
460
|
+
await entry.native.setModel(input.changes.model);
|
|
461
|
+
if (input.changes.thinkingOption !== undefined)
|
|
462
|
+
await entry.native.setThinkingOption(input.changes.thinkingOption);
|
|
463
|
+
if (
|
|
464
|
+
input.changes.mode !== undefined ||
|
|
465
|
+
settings.plan_mode !== undefined
|
|
466
|
+
)
|
|
467
|
+
await entry.native.configureMode(
|
|
468
|
+
input.changes.mode,
|
|
469
|
+
settings.plan_mode,
|
|
470
|
+
);
|
|
471
|
+
this.emitConfig(input.sessionId, entry);
|
|
472
|
+
break;
|
|
473
|
+
}
|
|
474
|
+
case "session.interrupt":
|
|
475
|
+
await entry.native.interrupt();
|
|
476
|
+
break;
|
|
477
|
+
case "session.permission":
|
|
478
|
+
await entry.native.respondToPermission(
|
|
479
|
+
input.permissionId,
|
|
480
|
+
input.response,
|
|
481
|
+
);
|
|
482
|
+
return;
|
|
483
|
+
case "session.close":
|
|
484
|
+
await this.closeSession(input.sessionId);
|
|
485
|
+
this.emit({ type: "session.closed", sessionId: input.sessionId });
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
this.emit({ type: "request.completed", requestId: input.requestId });
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private async openSession(
|
|
492
|
+
input: Extract<ProviderInput, { type: "session.open" }>,
|
|
493
|
+
): Promise<void> {
|
|
494
|
+
const config = input.config;
|
|
495
|
+
optionsSchema.parse(config.providerOptions ?? {});
|
|
496
|
+
const settings = settingsSchema.parse(config.settings);
|
|
497
|
+
if (
|
|
498
|
+
config.mode !== undefined &&
|
|
499
|
+
!ZCODE_MODES.some((mode) => mode.id === config.mode)
|
|
500
|
+
)
|
|
501
|
+
throw new AdapterError(
|
|
502
|
+
"INVALID_CONFIGURATION",
|
|
503
|
+
"ZCode editing mode must be build, edit, or yolo",
|
|
504
|
+
);
|
|
505
|
+
if (config.systemPrompt?.trim())
|
|
506
|
+
throw new AdapterError(
|
|
507
|
+
"INVALID_CONFIGURATION",
|
|
508
|
+
"Custom system prompts are unsupported by the verified ZCode host",
|
|
509
|
+
);
|
|
510
|
+
if (!config.persist)
|
|
511
|
+
throw new AdapterError(
|
|
512
|
+
"INVALID_CONFIGURATION",
|
|
513
|
+
"The verified ZCode host requires persistent sessions",
|
|
514
|
+
);
|
|
515
|
+
const cwd = await resolveWorkspace(config.cwd);
|
|
516
|
+
const persistence = input.persistence
|
|
517
|
+
? parsePersistence(input.persistence)
|
|
518
|
+
: { kind: "logical" as const, id: randomUUID(), cwd };
|
|
519
|
+
if (persistence.cwd !== cwd)
|
|
520
|
+
throw new AdapterError(
|
|
521
|
+
"INVALID_WORKSPACE",
|
|
522
|
+
"ZCode persistence belongs to another workspace",
|
|
523
|
+
);
|
|
524
|
+
const nativeId = await this.persistenceStore.resolve(persistence);
|
|
525
|
+
const host = await this.host(config.env, cwd);
|
|
526
|
+
let native: ZCodeSession | undefined;
|
|
527
|
+
try {
|
|
528
|
+
const { selection } = await initializeWorkspace(host, cwd);
|
|
529
|
+
const requested = nativeId
|
|
530
|
+
? undefined
|
|
531
|
+
: config.model === undefined
|
|
532
|
+
? selection.preferredSelection
|
|
533
|
+
: decodeModel(config.model);
|
|
534
|
+
const initialModel =
|
|
535
|
+
requested &&
|
|
536
|
+
(await resolveModelSelection(host, selection, {
|
|
537
|
+
...requested,
|
|
538
|
+
...(config.thinkingOption !== undefined
|
|
539
|
+
? { options: { reasoningLevel: config.thinkingOption } }
|
|
540
|
+
: {}),
|
|
541
|
+
}));
|
|
542
|
+
const snapshot = await host.request(
|
|
543
|
+
nativeId ? "resumeSession" : "createSession",
|
|
544
|
+
{
|
|
545
|
+
workspacePath: cwd,
|
|
546
|
+
...(nativeId
|
|
547
|
+
? { sessionId: nativeId }
|
|
548
|
+
: {
|
|
549
|
+
persistence: "deferred",
|
|
550
|
+
// session/create converts model to provider/model internally;
|
|
551
|
+
// the native create contract takes the reasoning level separately.
|
|
552
|
+
...(initialModel
|
|
553
|
+
? {
|
|
554
|
+
model: initialModel,
|
|
555
|
+
thoughtLevel: initialModel.options?.reasoningLevel,
|
|
556
|
+
}
|
|
557
|
+
: {}),
|
|
558
|
+
}),
|
|
559
|
+
mcpServers: mapMcpServers(config.mcpServers),
|
|
560
|
+
},
|
|
561
|
+
SessionSnapshotSchema,
|
|
562
|
+
60000,
|
|
563
|
+
);
|
|
564
|
+
if (nativeId && snapshot.session.sessionId !== nativeId)
|
|
565
|
+
throw new Error("ZCode resumed a different session");
|
|
566
|
+
native = await ZCodeSession.create({
|
|
567
|
+
bridge: host,
|
|
568
|
+
logger,
|
|
569
|
+
workspace: cwd,
|
|
570
|
+
snapshot,
|
|
571
|
+
modelCatalog: selection.models,
|
|
572
|
+
onClose() {},
|
|
573
|
+
});
|
|
574
|
+
if (
|
|
575
|
+
initialModel &&
|
|
576
|
+
(native.getConfig().model !== encodeModel(initialModel) ||
|
|
577
|
+
native.getConfig().thinkingOption !==
|
|
578
|
+
initialModel.options?.reasoningLevel)
|
|
579
|
+
)
|
|
580
|
+
throw new AdapterError(
|
|
581
|
+
"NATIVE_PROTOCOL_ERROR",
|
|
582
|
+
"ZCode did not apply the requested initial model selection",
|
|
583
|
+
);
|
|
584
|
+
await native.applyInitialConfig({
|
|
585
|
+
...config,
|
|
586
|
+
...(nativeId
|
|
587
|
+
? {}
|
|
588
|
+
: {
|
|
589
|
+
model: undefined,
|
|
590
|
+
...(initialModel ? { thinkingOption: undefined } : {}),
|
|
591
|
+
}),
|
|
592
|
+
settings: {
|
|
593
|
+
...settings,
|
|
594
|
+
...(!nativeId && settings.plan_mode === undefined
|
|
595
|
+
? { plan_mode: false }
|
|
596
|
+
: {}),
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
if (this.closed) throw new Error("ZCode connection is closed");
|
|
600
|
+
const entry: SessionEntry = {
|
|
601
|
+
native,
|
|
602
|
+
persistence,
|
|
603
|
+
submitted: nativeId !== undefined,
|
|
604
|
+
bridge: host,
|
|
605
|
+
unsubscribe() {},
|
|
606
|
+
unsubscribeFailure() {},
|
|
607
|
+
timeline: new TimelineSnapshots(),
|
|
608
|
+
messages: new Map(),
|
|
609
|
+
inputGeneration: 0,
|
|
610
|
+
queue: Promise.resolve(),
|
|
611
|
+
};
|
|
612
|
+
this.sessions.set(input.sessionId, entry);
|
|
613
|
+
this.emit({
|
|
614
|
+
type: "session.opened",
|
|
615
|
+
requestId: input.requestId,
|
|
616
|
+
sessionId: input.sessionId,
|
|
617
|
+
capabilities: this.capabilities,
|
|
618
|
+
restoration: "core",
|
|
619
|
+
persistence: persistenceHandle(persistence),
|
|
620
|
+
cwd,
|
|
621
|
+
title: config.title ?? snapshot.session.title,
|
|
622
|
+
});
|
|
623
|
+
this.emitConfig(input.sessionId, entry);
|
|
624
|
+
if (input.history === "replay")
|
|
625
|
+
for (const { item, timestamp } of native.historyItems())
|
|
626
|
+
this.emit({
|
|
627
|
+
type: "timeline.item",
|
|
628
|
+
sessionId: input.sessionId,
|
|
629
|
+
item: entry.timeline.replay(item),
|
|
630
|
+
timestamp,
|
|
631
|
+
});
|
|
632
|
+
entry.unsubscribe = native.subscribe((event) =>
|
|
633
|
+
this.accept(input.sessionId, entry, event),
|
|
634
|
+
);
|
|
635
|
+
entry.unsubscribeFailure = host.onFailure((error) =>
|
|
636
|
+
native!.runtimeFailed(error),
|
|
637
|
+
);
|
|
638
|
+
if (!this.sessions.has(input.sessionId))
|
|
639
|
+
throw new AdapterError(
|
|
640
|
+
"NATIVE_EXITED",
|
|
641
|
+
"ZCode disconnected during session open",
|
|
642
|
+
);
|
|
643
|
+
for (const request of native.getPendingPermissions())
|
|
644
|
+
this.emit({
|
|
645
|
+
type: "session.permission",
|
|
646
|
+
sessionId: input.sessionId,
|
|
647
|
+
request,
|
|
648
|
+
});
|
|
649
|
+
this.emit({
|
|
650
|
+
type: "session.ready",
|
|
651
|
+
requestId: input.requestId,
|
|
652
|
+
sessionId: input.sessionId,
|
|
653
|
+
});
|
|
654
|
+
} catch (error) {
|
|
655
|
+
this.sessions.delete(input.sessionId);
|
|
656
|
+
try {
|
|
657
|
+
await native?.close();
|
|
658
|
+
} finally {
|
|
659
|
+
this.hosts.delete(host);
|
|
660
|
+
await host.close();
|
|
661
|
+
}
|
|
662
|
+
throw error;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
private emitConfig(id: string, entry: SessionEntry): void {
|
|
667
|
+
this.emit({
|
|
668
|
+
type: "session.config",
|
|
669
|
+
sessionId: id,
|
|
670
|
+
config: entry.native.getConfig(),
|
|
671
|
+
});
|
|
672
|
+
this.emit({
|
|
673
|
+
type: "session.commands",
|
|
674
|
+
sessionId: id,
|
|
675
|
+
commands: entry.native.listCommands(),
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
private accept(
|
|
680
|
+
id: string,
|
|
681
|
+
entry: SessionEntry,
|
|
682
|
+
event: NativeSessionEvent,
|
|
683
|
+
): void {
|
|
684
|
+
switch (event.type) {
|
|
685
|
+
case "timeline":
|
|
686
|
+
this.emit({
|
|
687
|
+
type: "timeline.item",
|
|
688
|
+
sessionId: id,
|
|
689
|
+
item: entry.timeline.live(event.item, event.turnId),
|
|
690
|
+
timestamp: event.timestamp,
|
|
691
|
+
});
|
|
692
|
+
break;
|
|
693
|
+
case "usage_updated":
|
|
694
|
+
this.emit({
|
|
695
|
+
type: "session.usage",
|
|
696
|
+
sessionId: id,
|
|
697
|
+
turnId: event.turnId,
|
|
698
|
+
usage: event.usage,
|
|
699
|
+
});
|
|
700
|
+
break;
|
|
701
|
+
case "prompt_accepted":
|
|
702
|
+
if (!entry.messages.has(event.clientMessageId))
|
|
703
|
+
throw new Error("ZCode accepted an unrequested input");
|
|
704
|
+
if (entry.messages.get(event.clientMessageId))
|
|
705
|
+
throw new Error("ZCode accepted an input twice");
|
|
706
|
+
entry.messages.set(event.clientMessageId, true);
|
|
707
|
+
this.emit({
|
|
708
|
+
type: "session.prompt_result",
|
|
709
|
+
sessionId: id,
|
|
710
|
+
clientMessageId: event.clientMessageId,
|
|
711
|
+
result: { type: event.delivery, turnId: event.turnId },
|
|
712
|
+
});
|
|
713
|
+
break;
|
|
714
|
+
case "turn_started":
|
|
715
|
+
this.emit({
|
|
716
|
+
type: "session.turn",
|
|
717
|
+
sessionId: id,
|
|
718
|
+
turnId: event.turnId,
|
|
719
|
+
state: "started",
|
|
720
|
+
});
|
|
721
|
+
break;
|
|
722
|
+
case "turn_completed":
|
|
723
|
+
case "turn_canceled":
|
|
724
|
+
this.emit({
|
|
725
|
+
type: "session.turn",
|
|
726
|
+
sessionId: id,
|
|
727
|
+
turnId: event.turnId,
|
|
728
|
+
state: event.type === "turn_completed" ? "completed" : "canceled",
|
|
729
|
+
});
|
|
730
|
+
break;
|
|
731
|
+
case "turn_failed":
|
|
732
|
+
if (event.turnId)
|
|
733
|
+
this.emit({
|
|
734
|
+
type: "session.turn",
|
|
735
|
+
sessionId: id,
|
|
736
|
+
turnId: event.turnId,
|
|
737
|
+
state: "failed",
|
|
738
|
+
error: {
|
|
739
|
+
code: event.code,
|
|
740
|
+
message: "ZCode turn failed",
|
|
741
|
+
diagnostic: event.diagnostic,
|
|
742
|
+
},
|
|
743
|
+
});
|
|
744
|
+
break;
|
|
745
|
+
case "runtime_failed": {
|
|
746
|
+
this.emit({
|
|
747
|
+
type: "session.runtime_failed",
|
|
748
|
+
sessionId: id,
|
|
749
|
+
error: {
|
|
750
|
+
code: event.code,
|
|
751
|
+
message: "ZCode session failed",
|
|
752
|
+
diagnostic: event.diagnostic,
|
|
753
|
+
},
|
|
754
|
+
});
|
|
755
|
+
if (this.sessions.get(id) !== entry) break;
|
|
756
|
+
const cleanup = this.closeSession(id)
|
|
757
|
+
.catch(() => undefined)
|
|
758
|
+
.finally(() => this.jobs.delete(cleanup));
|
|
759
|
+
this.jobs.add(cleanup);
|
|
760
|
+
break;
|
|
761
|
+
}
|
|
762
|
+
case "permission_requested":
|
|
763
|
+
this.emit({
|
|
764
|
+
type: "session.permission",
|
|
765
|
+
sessionId: id,
|
|
766
|
+
request: event.request,
|
|
767
|
+
});
|
|
768
|
+
break;
|
|
769
|
+
case "permission_resolved":
|
|
770
|
+
this.emit({
|
|
771
|
+
type: "session.permission_resolved",
|
|
772
|
+
sessionId: id,
|
|
773
|
+
permissionId: event.requestId,
|
|
774
|
+
});
|
|
775
|
+
break;
|
|
776
|
+
case "config_changed":
|
|
777
|
+
this.emitConfig(id, entry);
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
private requireSession(id: string): SessionEntry {
|
|
783
|
+
const entry = this.sessions.get(id);
|
|
784
|
+
if (!entry)
|
|
785
|
+
throw new AdapterError("SESSION_NOT_FOUND", "Unknown ZCode session");
|
|
786
|
+
return entry;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
private async closeSession(id: string): Promise<void> {
|
|
790
|
+
const entry = this.requireSession(id);
|
|
791
|
+
this.sessions.delete(id);
|
|
792
|
+
try {
|
|
793
|
+
await entry.native.close();
|
|
794
|
+
} finally {
|
|
795
|
+
entry.unsubscribe();
|
|
796
|
+
entry.unsubscribeFailure();
|
|
797
|
+
this.hosts.delete(entry.bridge);
|
|
798
|
+
await entry.bridge.close();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
private emit(event: ProviderEvent): void {
|
|
803
|
+
if (this.closed) return;
|
|
804
|
+
const valid = ProviderEventSchema.parse(event);
|
|
805
|
+
for (const listener of this.listeners) listener(valid);
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
function publicError(error: unknown): ProviderError {
|
|
810
|
+
const messages: Record<string, string> = {
|
|
811
|
+
UNSUPPORTED_ZCODE: `ZCode requires stable app >=${MINIMUM_ZCODE_VERSION.app} and bundled CLI >=${MINIMUM_ZCODE_VERSION.cli}.`,
|
|
812
|
+
RUNTIME_DISCOVERY_FAILED:
|
|
813
|
+
"ZCode installation or required host structure could not be inspected. See the diagnostic for the failure stage.",
|
|
814
|
+
RUNTIME_SETUP_REQUIRED:
|
|
815
|
+
"ZCode runtime is not set up. Open Settings → Plugins → zcode-provider to set it up.",
|
|
816
|
+
PERSISTENCE_VERSION_UNSUPPORTED:
|
|
817
|
+
"ZCode persistence version is unsupported. Create a new Paseo ZCode session.",
|
|
818
|
+
PERSISTENCE_INVALID:
|
|
819
|
+
"ZCode resume information is unreadable or invalid. No replacement conversation was created.",
|
|
820
|
+
PERSISTENCE_WRITE_FAILED:
|
|
821
|
+
"ZCode resume information could not be saved. The prompt was not sent.",
|
|
822
|
+
};
|
|
823
|
+
return {
|
|
824
|
+
diagnostic: formatDiagnostic(error),
|
|
825
|
+
code: error instanceof AdapterError ? error.code : "INVALID_CONFIGURATION",
|
|
826
|
+
message:
|
|
827
|
+
error instanceof AdapterError
|
|
828
|
+
? (messages[error.code] ?? `ZCode operation failed (${error.code})`)
|
|
829
|
+
: "Invalid ZCode provider request",
|
|
830
|
+
};
|
|
831
|
+
}
|