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