pi-orchestrator-config 4.1.5

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 (175) hide show
  1. package/README.md +808 -0
  2. package/agents/api-documenter.md +32 -0
  3. package/agents/bash-expert.md +53 -0
  4. package/agents/code-reviewer-docs.md +145 -0
  5. package/agents/code-reviewer-guidelines.md +97 -0
  6. package/agents/code-reviewer-quality.md +142 -0
  7. package/agents/code-reviewer-security.md +107 -0
  8. package/agents/code-reviewer-spec.md +164 -0
  9. package/agents/debugger.md +63 -0
  10. package/agents/docker-expert.md +40 -0
  11. package/agents/docs-fetcher.md +48 -0
  12. package/agents/git-expert.md +63 -0
  13. package/agents/github-expert.md +83 -0
  14. package/agents/go-expert.md +62 -0
  15. package/agents/issue-reviewer-feasibility.md +60 -0
  16. package/agents/issue-reviewer-scope.md +63 -0
  17. package/agents/issue-reviewer-spec.md +77 -0
  18. package/agents/java-expert.md +31 -0
  19. package/agents/jenkins-expert.md +38 -0
  20. package/agents/kubernetes-expert.md +40 -0
  21. package/agents/planner.md +42 -0
  22. package/agents/python-expert.md +61 -0
  23. package/agents/reviewer.md +26 -0
  24. package/agents/scout.md +40 -0
  25. package/agents/security-auditor.md +254 -0
  26. package/agents/technical-documentation-writer.md +37 -0
  27. package/agents/test-automator.md +55 -0
  28. package/agents/test-runner.md +47 -0
  29. package/agents/ts-expert.md +23 -0
  30. package/agents/worker.md +30 -0
  31. package/extensions/acpx-provider/index.ts +104 -0
  32. package/extensions/acpx-provider/load-runtime.ts +99 -0
  33. package/extensions/acpx-provider/runtime-models.ts +40 -0
  34. package/extensions/cli-provider/agents/claude.ts +128 -0
  35. package/extensions/cli-provider/agents/cursor.ts +84 -0
  36. package/extensions/cli-provider/agents/gemini.ts +100 -0
  37. package/extensions/cli-provider/discover.ts +77 -0
  38. package/extensions/cli-provider/index.ts +57 -0
  39. package/extensions/cli-provider/parsers.ts +304 -0
  40. package/extensions/cli-provider/providers.ts +58 -0
  41. package/extensions/cli-provider/runner.ts +225 -0
  42. package/extensions/cli-provider/runtime-models.ts +32 -0
  43. package/extensions/cli-provider/session-reaper.ts +122 -0
  44. package/extensions/cli-provider/sessions.ts +533 -0
  45. package/extensions/cli-provider/shared/discover-cache.ts +235 -0
  46. package/extensions/cli-provider/types.ts +48 -0
  47. package/extensions/coms/coms-net-server.ts +1642 -0
  48. package/extensions/coms/coms-net-wrapper.ts +521 -0
  49. package/extensions/coms/coms-net.ts +1589 -0
  50. package/extensions/coms/coms-p2p.ts +1879 -0
  51. package/extensions/coms/coms-shared.ts +510 -0
  52. package/extensions/coms/coms-wrapper.ts +138 -0
  53. package/extensions/coms/index.ts +20 -0
  54. package/extensions/coms/themeMap.ts +147 -0
  55. package/extensions/image-gen/image-gen.ts +332 -0
  56. package/extensions/image-gen/index.ts +14 -0
  57. package/extensions/orchestrator/agents.ts +134 -0
  58. package/extensions/orchestrator/ask-user.ts +293 -0
  59. package/extensions/orchestrator/async-agents.ts +1037 -0
  60. package/extensions/orchestrator/async-capability.ts +129 -0
  61. package/extensions/orchestrator/async-runner.ts +324 -0
  62. package/extensions/orchestrator/async-status-parse.ts +31 -0
  63. package/extensions/orchestrator/async-status-ui.ts +201 -0
  64. package/extensions/orchestrator/async-wait.ts +36 -0
  65. package/extensions/orchestrator/btw.ts +210 -0
  66. package/extensions/orchestrator/cron-status-format.ts +126 -0
  67. package/extensions/orchestrator/cron-status-ui.ts +132 -0
  68. package/extensions/orchestrator/cron.ts +730 -0
  69. package/extensions/orchestrator/dreaming.ts +355 -0
  70. package/extensions/orchestrator/enforcement-helpers.ts +530 -0
  71. package/extensions/orchestrator/enforcement-rules.ts +329 -0
  72. package/extensions/orchestrator/enforcement.ts +828 -0
  73. package/extensions/orchestrator/extended-autocomplete.ts +593 -0
  74. package/extensions/orchestrator/git-helpers.ts +420 -0
  75. package/extensions/orchestrator/github-autocomplete.ts +250 -0
  76. package/extensions/orchestrator/icons.ts +17 -0
  77. package/extensions/orchestrator/index.ts +100 -0
  78. package/extensions/orchestrator/memory-embeddings.ts +281 -0
  79. package/extensions/orchestrator/memory-promotion.ts +449 -0
  80. package/extensions/orchestrator/memory-provenance.ts +80 -0
  81. package/extensions/orchestrator/memory-query-class.ts +90 -0
  82. package/extensions/orchestrator/memory-scoring.ts +434 -0
  83. package/extensions/orchestrator/memory-tools.ts +883 -0
  84. package/extensions/orchestrator/memory-tree.ts +212 -0
  85. package/extensions/orchestrator/nvim.ts +169 -0
  86. package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
  87. package/extensions/orchestrator/overlay-dashboard.ts +512 -0
  88. package/extensions/orchestrator/pi-config-review-state.ts +303 -0
  89. package/extensions/orchestrator/preference-extractor.ts +106 -0
  90. package/extensions/orchestrator/project-settings.ts +468 -0
  91. package/extensions/orchestrator/promotion-queue.ts +311 -0
  92. package/extensions/orchestrator/resolve-agent-model.ts +32 -0
  93. package/extensions/orchestrator/review-ui.ts +204 -0
  94. package/extensions/orchestrator/rule-placeholders.ts +12 -0
  95. package/extensions/orchestrator/rules.ts +481 -0
  96. package/extensions/orchestrator/session-search.ts +243 -0
  97. package/extensions/orchestrator/session-validation.ts +374 -0
  98. package/extensions/orchestrator/situation-report.ts +370 -0
  99. package/extensions/orchestrator/status-bar.ts +31 -0
  100. package/extensions/orchestrator/status-line.ts +192 -0
  101. package/extensions/orchestrator/status.ts +110 -0
  102. package/extensions/orchestrator/subagent-tool.ts +1748 -0
  103. package/extensions/orchestrator/task-lifecycle.ts +88 -0
  104. package/extensions/orchestrator/utils.ts +227 -0
  105. package/extensions/pidash/index.ts +15 -0
  106. package/extensions/pidash/pidash.ts +941 -0
  107. package/extensions/pidiff/index.ts +13 -0
  108. package/extensions/pidiff/pidiff.ts +369 -0
  109. package/extensions/providers/acpx-driver.ts +479 -0
  110. package/extensions/providers/built-in-drivers.ts +52 -0
  111. package/extensions/providers/claude-driver.ts +319 -0
  112. package/extensions/providers/cursor-acpx-driver.ts +398 -0
  113. package/extensions/providers/cursor-cli-driver.ts +484 -0
  114. package/extensions/providers/gemini-driver.ts +302 -0
  115. package/extensions/providers/index.ts +513 -0
  116. package/extensions/shared/build-system-prompt.ts +57 -0
  117. package/extensions/shared/create-runtime-provider.ts +190 -0
  118. package/extensions/shared/daemon-manager.ts +248 -0
  119. package/extensions/shared/file-logger.ts +115 -0
  120. package/extensions/shared/managed-refresh.ts +211 -0
  121. package/extensions/shared/provider-driver.ts +295 -0
  122. package/extensions/shared/provider-errors.ts +305 -0
  123. package/extensions/shared/provider-registry.ts +388 -0
  124. package/extensions/shared/stream-builder.ts +275 -0
  125. package/extensions/shared/types.ts +20 -0
  126. package/extensions/shared/ui/README.md +10 -0
  127. package/extensions/shared/ui/badge.tsx +52 -0
  128. package/extensions/shared/ui/button.tsx +58 -0
  129. package/extensions/shared/ui/collapsible.tsx +21 -0
  130. package/extensions/shared/ui/dropdown-menu.tsx +266 -0
  131. package/extensions/shared/ui/input.tsx +20 -0
  132. package/extensions/shared/ui/popover.tsx +114 -0
  133. package/extensions/shared/ui/resizable.tsx +48 -0
  134. package/extensions/shared/ui/scroll-area.tsx +53 -0
  135. package/extensions/shared/ui/select.tsx +201 -0
  136. package/extensions/shared/ui/separator.tsx +25 -0
  137. package/extensions/shared/ui/tooltip.tsx +64 -0
  138. package/extensions/shared/ws-client.ts +74 -0
  139. package/package.json +59 -0
  140. package/prompts/coderabbit-rate-limit.md +95 -0
  141. package/prompts/create-coms-feature-manager.md +186 -0
  142. package/prompts/create-skill.md +128 -0
  143. package/prompts/domain-model.md +109 -0
  144. package/prompts/external-ai.md +665 -0
  145. package/prompts/implement-and-review.md +25 -0
  146. package/prompts/implement.md +26 -0
  147. package/prompts/issue-review.md +256 -0
  148. package/prompts/pr-review.md +753 -0
  149. package/prompts/query-db.md +154 -0
  150. package/prompts/refine-review.md +208 -0
  151. package/prompts/release.md +235 -0
  152. package/prompts/remember.md +33 -0
  153. package/prompts/review-handler-status.md +27 -0
  154. package/prompts/review-handler.md +677 -0
  155. package/prompts/review-local.md +159 -0
  156. package/prompts/scout-and-plan.md +21 -0
  157. package/rules/00-orchestrator-core.md +42 -0
  158. package/rules/05-issue-first-workflow.md +119 -0
  159. package/rules/10-agent-routing.md +58 -0
  160. package/rules/15-mcp-launchpad.md +46 -0
  161. package/rules/20-code-review-loop.md +234 -0
  162. package/rules/25-documentation-updates.md +15 -0
  163. package/rules/30-prompt-templates.md +19 -0
  164. package/rules/35-memory.md +240 -0
  165. package/rules/40-critical-rules.md +124 -0
  166. package/rules/45-file-preview.md +29 -0
  167. package/rules/50-agent-bug-reporting.md +90 -0
  168. package/rules/55-coms-protocol.md +63 -0
  169. package/rules/60-task-tracking.md +66 -0
  170. package/skills/conflict-resolver/SKILL.md +213 -0
  171. package/skills/skill-creator/SKILL.md +85 -0
  172. package/skills/systematic-debugging/SKILL.md +353 -0
  173. package/skills/test-driven-development/SKILL.md +335 -0
  174. package/skills/update-agents-md/SKILL.md +282 -0
  175. package/templates/coms-feature-manager-prompt.md +313 -0
@@ -0,0 +1,398 @@
1
+ /**
2
+ * CursorAcpxDriver — ProviderDriver for the Cursor Agent via ACP runtime.
3
+ *
4
+ * Wraps the acpx runtime for ACP-based session management. Owns
5
+ * ensureHandle, model discovery via runtime.getStatus, and event
6
+ * stream from turn.events.
7
+ *
8
+ * Reuses existing modules:
9
+ * - acpx-provider/load-runtime.ts — dynamic acpx import
10
+ * - shared/build-system-prompt.ts — system prompt injection
11
+ *
12
+ * @module providers/cursor-acpx-driver
13
+ */
14
+
15
+ import { createHash, randomUUID } from "node:crypto";
16
+ import path from "node:path";
17
+ import os from "node:os";
18
+ import { rm } from "node:fs/promises";
19
+ import type {
20
+ ConfigSchema,
21
+ DiscoveredModel,
22
+ ProviderAdapterShape,
23
+ ProviderDriver,
24
+ ProviderDriverCreateInput,
25
+ ProviderInstance,
26
+ ProviderProbeResult,
27
+ SessionHandle,
28
+ SessionStartOptions,
29
+ TurnOptions,
30
+ TurnResult,
31
+ } from "../shared/provider-driver.js";
32
+ import { ProviderDriverError } from "../shared/provider-errors.js";
33
+ import { makeManagedSnapshot, buildInitialSnapshot } from "../shared/managed-refresh.js";
34
+ import { loadAcpxRuntime, type AcpxRuntimeModule } from "../acpx-provider/load-runtime.js";
35
+ import { modelIdToDisplayName } from "../acpx-provider/runtime-models.js";
36
+ import { buildExternalSystemPrompt } from "../shared/build-system-prompt.js";
37
+ import { fileLog } from "../shared/file-logger.js";
38
+
39
+ const LOG_DOMAIN = "cursor-acpx-driver";
40
+ const DRIVER_KIND = "cursor-acpx";
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Config
44
+ // ---------------------------------------------------------------------------
45
+
46
+ export interface CursorAcpxConfig {
47
+ readonly agent: string;
48
+ readonly enabled: boolean;
49
+ }
50
+
51
+ const cursorAcpxConfigSchema: ConfigSchema<CursorAcpxConfig> = {
52
+ parse: (raw: unknown): CursorAcpxConfig => {
53
+ if (!raw || typeof raw !== "object") {
54
+ return { agent: "cursor", enabled: true };
55
+ }
56
+ const obj = raw as Record<string, unknown>;
57
+ return {
58
+ agent: typeof obj.agent === "string" ? obj.agent : "cursor",
59
+ enabled: typeof obj.enabled === "boolean" ? obj.enabled : true,
60
+ };
61
+ },
62
+ };
63
+
64
+ // ---------------------------------------------------------------------------
65
+ // Runtime types
66
+ // ---------------------------------------------------------------------------
67
+
68
+ type AcpxRuntime = ReturnType<AcpxRuntimeModule["createAcpRuntime"]>;
69
+ type AcpRuntimeHandle = Awaited<ReturnType<AcpxRuntime["ensureSession"]>>;
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Adapter
73
+ // ---------------------------------------------------------------------------
74
+
75
+ function createCursorAcpxAdapter(
76
+ config: CursorAcpxConfig,
77
+ cwd: string,
78
+ runtime: AcpxRuntime,
79
+ cwdSlug: string,
80
+ ): ProviderAdapterShape {
81
+ const handles = new Map<string, AcpRuntimeHandle>();
82
+ const pendingHandles = new Map<string, Promise<AcpRuntimeHandle>>();
83
+ const systemPromptSent = new Set<string>();
84
+
85
+ function sessionKey(modelId?: string): string {
86
+ const model = modelId && modelId !== "default"
87
+ ? `-${modelId.replace(/[^a-zA-Z0-9.-]/g, "_")}`
88
+ : "";
89
+ return `pi-${config.agent}${model}-${cwdSlug}`;
90
+ }
91
+
92
+ async function ensureHandle(
93
+ acpxModelId: string | undefined,
94
+ systemPrompt?: string,
95
+ ): Promise<AcpRuntimeHandle> {
96
+ const key = acpxModelId || "default";
97
+
98
+ const existing = handles.get(key);
99
+ if (existing) return existing;
100
+
101
+ const pending = pendingHandles.get(key);
102
+ if (pending) return pending;
103
+
104
+ const promise = (async () => {
105
+ try {
106
+ const sessionOpts: { model?: string; systemPrompt?: string } = {};
107
+ if (acpxModelId && acpxModelId !== "default") {
108
+ sessionOpts.model = acpxModelId;
109
+ }
110
+ if (systemPrompt) {
111
+ sessionOpts.systemPrompt = systemPrompt;
112
+ }
113
+
114
+ const handle = await runtime.ensureSession({
115
+ sessionKey: sessionKey(acpxModelId),
116
+ agent: config.agent,
117
+ mode: "persistent",
118
+ cwd,
119
+ ...(Object.keys(sessionOpts).length > 0
120
+ ? { sessionOptions: sessionOpts }
121
+ : {}),
122
+ });
123
+
124
+ handles.set(key, handle);
125
+ return handle;
126
+ } finally {
127
+ pendingHandles.delete(key);
128
+ }
129
+ })();
130
+
131
+ pendingHandles.set(key, promise);
132
+ return promise;
133
+ }
134
+
135
+ return {
136
+ startSession: async (opts: SessionStartOptions): Promise<SessionHandle> => {
137
+ const model = opts.model || "default";
138
+ const systemPrompt = opts.systemPrompt
139
+ ? opts.systemPrompt
140
+ : buildExternalSystemPrompt({ systemPrompt: undefined }, cwd);
141
+ const handle = await ensureHandle(model, systemPrompt);
142
+ return {
143
+ sessionId: sessionKey(model),
144
+ model,
145
+ };
146
+ },
147
+
148
+ sendTurn: async (
149
+ handle: SessionHandle,
150
+ prompt: string,
151
+ opts?: TurnOptions,
152
+ ): Promise<TurnResult> => {
153
+ const handleKey = handle.model || "default";
154
+ const needsSystemPrompt = !systemPromptSent.has(handleKey);
155
+ const systemPrompt = needsSystemPrompt
156
+ ? buildExternalSystemPrompt({ systemPrompt: undefined }, cwd)
157
+ : undefined;
158
+
159
+ const acpxHandle = await ensureHandle(handle.model, systemPrompt);
160
+ if (needsSystemPrompt) {
161
+ systemPromptSent.add(handleKey);
162
+ }
163
+
164
+ const abortController = new AbortController();
165
+ if (opts?.signal) {
166
+ if (opts.signal.aborted) {
167
+ abortController.abort();
168
+ } else {
169
+ opts.signal.addEventListener("abort", () => abortController.abort(), { once: true });
170
+ }
171
+ }
172
+
173
+ const turn = runtime.startTurn({
174
+ handle: acpxHandle,
175
+ text: prompt,
176
+ mode: "prompt",
177
+ requestId: `pi-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
178
+ signal: abortController.signal,
179
+ });
180
+
181
+ let text = "";
182
+ let thinking = "";
183
+
184
+ for await (const event of turn.events) {
185
+ if (abortController.signal.aborted) break;
186
+
187
+ if (event.type === "text_delta" && event.text) {
188
+ if (event.stream === "thought") {
189
+ thinking += event.text;
190
+ opts?.onEvent?.({ kind: "thinking_delta", text: event.text });
191
+ } else {
192
+ text += event.text;
193
+ opts?.onEvent?.({ kind: "text_delta", text: event.text });
194
+ }
195
+ }
196
+ }
197
+
198
+ const result = await turn.result;
199
+ let stopReason = "stop";
200
+ if (result.status === "completed") {
201
+ stopReason = result.stopReason === "end_turn" ? "stop" : (result.stopReason || "stop");
202
+ } else if (result.status === "failed") {
203
+ throw new Error(`acpx turn failed: ${result.error.message}`);
204
+ }
205
+
206
+ return { text, thinking: thinking || undefined, stopReason };
207
+ },
208
+
209
+ stopSession: async (handle: SessionHandle): Promise<void> => {
210
+ const key = handle.model || "default";
211
+ const acpxHandle = handles.get(key);
212
+ if (acpxHandle) {
213
+ await runtime.close({ handle: acpxHandle, reason: "session stop" }).catch((err: unknown) => {
214
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
215
+ `session close failed: ${err instanceof Error ? err.message : String(err)}`);
216
+ });
217
+ handles.delete(key);
218
+ }
219
+ systemPromptSent.delete(key);
220
+ },
221
+
222
+ stopAll: async (): Promise<void> => {
223
+ const closePromises: Promise<void>[] = [];
224
+ for (const [key, acpxHandle] of handles) {
225
+ closePromises.push(
226
+ runtime.close({ handle: acpxHandle, reason: "stop all" }).catch((err: unknown) => {
227
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
228
+ `session close failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
229
+ }),
230
+ );
231
+ }
232
+ await Promise.allSettled(closePromises);
233
+ handles.clear();
234
+ pendingHandles.clear();
235
+ systemPromptSent.clear();
236
+ },
237
+
238
+ hasSession: (sessionId: string): boolean => {
239
+ for (const key of handles.keys()) {
240
+ if (sessionKey(key === "default" ? undefined : key) === sessionId) {
241
+ return true;
242
+ }
243
+ }
244
+ return false;
245
+ },
246
+ };
247
+ }
248
+
249
+ // ---------------------------------------------------------------------------
250
+ // Discovery helper
251
+ // ---------------------------------------------------------------------------
252
+
253
+ async function discoverAcpxModelsInternal(
254
+ agent: string,
255
+ cwd: string,
256
+ ): Promise<string[]> {
257
+ const { createAcpRuntime, createFileSessionStore, createAgentRegistry } =
258
+ await loadAcpxRuntime();
259
+
260
+ const uid = randomUUID().slice(0, 8);
261
+ const stateDir = path.join(os.homedir(), ".acpx", `discover-${process.pid}-${uid}`);
262
+ const runtime = createAcpRuntime({
263
+ cwd,
264
+ sessionStore: createFileSessionStore({ stateDir }),
265
+ agentRegistry: createAgentRegistry(),
266
+ permissionMode: "deny-all",
267
+ });
268
+
269
+ let handle: AcpRuntimeHandle | undefined;
270
+ try {
271
+ handle = await runtime.ensureSession({
272
+ sessionKey: `discover-${agent}-${uid}`,
273
+ agent,
274
+ mode: "oneshot",
275
+ cwd,
276
+ });
277
+
278
+ const status = await runtime.getStatus({ handle });
279
+ return status.models?.availableModelIds || [];
280
+ } catch (err) {
281
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
282
+ `model discovery failed for ${agent}: ${err instanceof Error ? err.message : String(err)}`);
283
+ return [];
284
+ } finally {
285
+ if (handle) {
286
+ await runtime.close({ handle, reason: "discovery complete" }).catch(() => {});
287
+ }
288
+ await rm(stateDir, { recursive: true, force: true }).catch(() => {});
289
+ }
290
+ }
291
+
292
+ // ---------------------------------------------------------------------------
293
+ // Driver
294
+ // ---------------------------------------------------------------------------
295
+
296
+ export const CursorAcpxDriver: ProviderDriver<CursorAcpxConfig> = {
297
+ driverKind: DRIVER_KIND,
298
+ metadata: {
299
+ displayName: "Cursor ACPX",
300
+ supportsMultipleInstances: false,
301
+ },
302
+ configSchema: cursorAcpxConfigSchema,
303
+ defaultConfig: () => ({ agent: "cursor", enabled: true }),
304
+
305
+ probe: async (_config: CursorAcpxConfig): Promise<ProviderProbeResult> => {
306
+ try {
307
+ await loadAcpxRuntime();
308
+ return { available: true };
309
+ } catch (err) {
310
+ return {
311
+ available: false,
312
+ reason: `acpx runtime not available: ${err instanceof Error ? err.message : String(err)}`,
313
+ };
314
+ }
315
+ },
316
+
317
+ create: async (
318
+ input: ProviderDriverCreateInput<CursorAcpxConfig>,
319
+ ): Promise<ProviderInstance> => {
320
+ const { instanceId, config, cwd } = input;
321
+ const cwdSlug = createHash("sha256").update(cwd).digest("hex").slice(0, 12);
322
+
323
+ // Load runtime
324
+ const { createAcpRuntime, createFileSessionStore, createAgentRegistry } =
325
+ await loadAcpxRuntime();
326
+
327
+ const stateDir = path.join(os.homedir(), ".acpx", `pi-${cwdSlug}`);
328
+ const runtime = createAcpRuntime({
329
+ cwd,
330
+ sessionStore: createFileSessionStore({ stateDir }),
331
+ agentRegistry: createAgentRegistry(),
332
+ permissionMode: "approve-all",
333
+ });
334
+
335
+ // Discover models
336
+ let discoveredModelIds: string[] = [];
337
+ try {
338
+ // Use the runtime's own status to discover models
339
+ const tmpHandle = await runtime.ensureSession({
340
+ sessionKey: `discover-${config.agent}-init`,
341
+ agent: config.agent,
342
+ mode: "oneshot",
343
+ cwd,
344
+ });
345
+ const status = await runtime.getStatus({ handle: tmpHandle });
346
+ discoveredModelIds = status.models?.availableModelIds || [];
347
+ await runtime.close({ handle: tmpHandle, reason: "initial discovery" }).catch(() => {});
348
+ } catch (err) {
349
+ fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
350
+ `initial model discovery failed: ${err instanceof Error ? err.message : String(err)}`);
351
+ }
352
+
353
+ const discoveredModels: DiscoveredModel[] = discoveredModelIds.map((id) => ({
354
+ id,
355
+ name: modelIdToDisplayName(id),
356
+ }));
357
+
358
+ // Managed snapshot with periodic refresh
359
+ const managed = makeManagedSnapshot({
360
+ initialSnapshot: () => buildInitialSnapshot(true, discoveredModels),
361
+ checkProvider: async () => {
362
+ try {
363
+ const modelIds = await discoverAcpxModelsInternal(config.agent, cwd);
364
+ const models = modelIds.map((id) => ({
365
+ id,
366
+ name: modelIdToDisplayName(id),
367
+ }));
368
+ return buildInitialSnapshot(true, models);
369
+ } catch {
370
+ return buildInitialSnapshot(true, discoveredModels);
371
+ }
372
+ },
373
+ getSettings: () => config,
374
+ haveSettingsChanged: (prev, next) =>
375
+ prev.agent !== next.agent || prev.enabled !== next.enabled,
376
+ });
377
+
378
+ // Create adapter
379
+ const adapter = createCursorAcpxAdapter(config, cwd, runtime, cwdSlug);
380
+
381
+ return {
382
+ instanceId,
383
+ driverKind: DRIVER_KIND,
384
+ displayName: input.displayName || `ACPX ${config.agent}`,
385
+ enabled: input.enabled,
386
+ snapshot: {
387
+ getSnapshot: managed.getSnapshot,
388
+ refresh: managed.refresh,
389
+ dispose: managed.dispose,
390
+ },
391
+ adapter,
392
+ dispose: async () => {
393
+ managed.dispose();
394
+ await adapter.stopAll();
395
+ },
396
+ };
397
+ },
398
+ };