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.
- package/README.md +808 -0
- package/agents/api-documenter.md +32 -0
- package/agents/bash-expert.md +53 -0
- package/agents/code-reviewer-docs.md +145 -0
- package/agents/code-reviewer-guidelines.md +97 -0
- package/agents/code-reviewer-quality.md +142 -0
- package/agents/code-reviewer-security.md +107 -0
- package/agents/code-reviewer-spec.md +164 -0
- package/agents/debugger.md +63 -0
- package/agents/docker-expert.md +40 -0
- package/agents/docs-fetcher.md +48 -0
- package/agents/git-expert.md +63 -0
- package/agents/github-expert.md +83 -0
- package/agents/go-expert.md +62 -0
- package/agents/issue-reviewer-feasibility.md +60 -0
- package/agents/issue-reviewer-scope.md +63 -0
- package/agents/issue-reviewer-spec.md +77 -0
- package/agents/java-expert.md +31 -0
- package/agents/jenkins-expert.md +38 -0
- package/agents/kubernetes-expert.md +40 -0
- package/agents/planner.md +42 -0
- package/agents/python-expert.md +61 -0
- package/agents/reviewer.md +26 -0
- package/agents/scout.md +40 -0
- package/agents/security-auditor.md +254 -0
- package/agents/technical-documentation-writer.md +37 -0
- package/agents/test-automator.md +55 -0
- package/agents/test-runner.md +47 -0
- package/agents/ts-expert.md +23 -0
- package/agents/worker.md +30 -0
- package/extensions/acpx-provider/index.ts +104 -0
- package/extensions/acpx-provider/load-runtime.ts +99 -0
- package/extensions/acpx-provider/runtime-models.ts +40 -0
- package/extensions/cli-provider/agents/claude.ts +128 -0
- package/extensions/cli-provider/agents/cursor.ts +84 -0
- package/extensions/cli-provider/agents/gemini.ts +100 -0
- package/extensions/cli-provider/discover.ts +77 -0
- package/extensions/cli-provider/index.ts +57 -0
- package/extensions/cli-provider/parsers.ts +304 -0
- package/extensions/cli-provider/providers.ts +58 -0
- package/extensions/cli-provider/runner.ts +225 -0
- package/extensions/cli-provider/runtime-models.ts +32 -0
- package/extensions/cli-provider/session-reaper.ts +122 -0
- package/extensions/cli-provider/sessions.ts +533 -0
- package/extensions/cli-provider/shared/discover-cache.ts +235 -0
- package/extensions/cli-provider/types.ts +48 -0
- package/extensions/coms/coms-net-server.ts +1642 -0
- package/extensions/coms/coms-net-wrapper.ts +521 -0
- package/extensions/coms/coms-net.ts +1589 -0
- package/extensions/coms/coms-p2p.ts +1879 -0
- package/extensions/coms/coms-shared.ts +510 -0
- package/extensions/coms/coms-wrapper.ts +138 -0
- package/extensions/coms/index.ts +20 -0
- package/extensions/coms/themeMap.ts +147 -0
- package/extensions/image-gen/image-gen.ts +332 -0
- package/extensions/image-gen/index.ts +14 -0
- package/extensions/orchestrator/agents.ts +134 -0
- package/extensions/orchestrator/ask-user.ts +293 -0
- package/extensions/orchestrator/async-agents.ts +1037 -0
- package/extensions/orchestrator/async-capability.ts +129 -0
- package/extensions/orchestrator/async-runner.ts +324 -0
- package/extensions/orchestrator/async-status-parse.ts +31 -0
- package/extensions/orchestrator/async-status-ui.ts +201 -0
- package/extensions/orchestrator/async-wait.ts +36 -0
- package/extensions/orchestrator/btw.ts +210 -0
- package/extensions/orchestrator/cron-status-format.ts +126 -0
- package/extensions/orchestrator/cron-status-ui.ts +132 -0
- package/extensions/orchestrator/cron.ts +730 -0
- package/extensions/orchestrator/dreaming.ts +355 -0
- package/extensions/orchestrator/enforcement-helpers.ts +530 -0
- package/extensions/orchestrator/enforcement-rules.ts +329 -0
- package/extensions/orchestrator/enforcement.ts +828 -0
- package/extensions/orchestrator/extended-autocomplete.ts +593 -0
- package/extensions/orchestrator/git-helpers.ts +420 -0
- package/extensions/orchestrator/github-autocomplete.ts +250 -0
- package/extensions/orchestrator/icons.ts +17 -0
- package/extensions/orchestrator/index.ts +100 -0
- package/extensions/orchestrator/memory-embeddings.ts +281 -0
- package/extensions/orchestrator/memory-promotion.ts +449 -0
- package/extensions/orchestrator/memory-provenance.ts +80 -0
- package/extensions/orchestrator/memory-query-class.ts +90 -0
- package/extensions/orchestrator/memory-scoring.ts +434 -0
- package/extensions/orchestrator/memory-tools.ts +883 -0
- package/extensions/orchestrator/memory-tree.ts +212 -0
- package/extensions/orchestrator/nvim.ts +169 -0
- package/extensions/orchestrator/overlay-dashboard-utils.ts +25 -0
- package/extensions/orchestrator/overlay-dashboard.ts +512 -0
- package/extensions/orchestrator/pi-config-review-state.ts +303 -0
- package/extensions/orchestrator/preference-extractor.ts +106 -0
- package/extensions/orchestrator/project-settings.ts +468 -0
- package/extensions/orchestrator/promotion-queue.ts +311 -0
- package/extensions/orchestrator/resolve-agent-model.ts +32 -0
- package/extensions/orchestrator/review-ui.ts +204 -0
- package/extensions/orchestrator/rule-placeholders.ts +12 -0
- package/extensions/orchestrator/rules.ts +481 -0
- package/extensions/orchestrator/session-search.ts +243 -0
- package/extensions/orchestrator/session-validation.ts +374 -0
- package/extensions/orchestrator/situation-report.ts +370 -0
- package/extensions/orchestrator/status-bar.ts +31 -0
- package/extensions/orchestrator/status-line.ts +192 -0
- package/extensions/orchestrator/status.ts +110 -0
- package/extensions/orchestrator/subagent-tool.ts +1748 -0
- package/extensions/orchestrator/task-lifecycle.ts +88 -0
- package/extensions/orchestrator/utils.ts +227 -0
- package/extensions/pidash/index.ts +15 -0
- package/extensions/pidash/pidash.ts +941 -0
- package/extensions/pidiff/index.ts +13 -0
- package/extensions/pidiff/pidiff.ts +369 -0
- package/extensions/providers/acpx-driver.ts +479 -0
- package/extensions/providers/built-in-drivers.ts +52 -0
- package/extensions/providers/claude-driver.ts +319 -0
- package/extensions/providers/cursor-acpx-driver.ts +398 -0
- package/extensions/providers/cursor-cli-driver.ts +484 -0
- package/extensions/providers/gemini-driver.ts +302 -0
- package/extensions/providers/index.ts +513 -0
- package/extensions/shared/build-system-prompt.ts +57 -0
- package/extensions/shared/create-runtime-provider.ts +190 -0
- package/extensions/shared/daemon-manager.ts +248 -0
- package/extensions/shared/file-logger.ts +115 -0
- package/extensions/shared/managed-refresh.ts +211 -0
- package/extensions/shared/provider-driver.ts +295 -0
- package/extensions/shared/provider-errors.ts +305 -0
- package/extensions/shared/provider-registry.ts +388 -0
- package/extensions/shared/stream-builder.ts +275 -0
- package/extensions/shared/types.ts +20 -0
- package/extensions/shared/ui/README.md +10 -0
- package/extensions/shared/ui/badge.tsx +52 -0
- package/extensions/shared/ui/button.tsx +58 -0
- package/extensions/shared/ui/collapsible.tsx +21 -0
- package/extensions/shared/ui/dropdown-menu.tsx +266 -0
- package/extensions/shared/ui/input.tsx +20 -0
- package/extensions/shared/ui/popover.tsx +114 -0
- package/extensions/shared/ui/resizable.tsx +48 -0
- package/extensions/shared/ui/scroll-area.tsx +53 -0
- package/extensions/shared/ui/select.tsx +201 -0
- package/extensions/shared/ui/separator.tsx +25 -0
- package/extensions/shared/ui/tooltip.tsx +64 -0
- package/extensions/shared/ws-client.ts +74 -0
- package/package.json +59 -0
- package/prompts/coderabbit-rate-limit.md +95 -0
- package/prompts/create-coms-feature-manager.md +186 -0
- package/prompts/create-skill.md +128 -0
- package/prompts/domain-model.md +109 -0
- package/prompts/external-ai.md +665 -0
- package/prompts/implement-and-review.md +25 -0
- package/prompts/implement.md +26 -0
- package/prompts/issue-review.md +256 -0
- package/prompts/pr-review.md +753 -0
- package/prompts/query-db.md +154 -0
- package/prompts/refine-review.md +208 -0
- package/prompts/release.md +235 -0
- package/prompts/remember.md +33 -0
- package/prompts/review-handler-status.md +27 -0
- package/prompts/review-handler.md +677 -0
- package/prompts/review-local.md +159 -0
- package/prompts/scout-and-plan.md +21 -0
- package/rules/00-orchestrator-core.md +42 -0
- package/rules/05-issue-first-workflow.md +119 -0
- package/rules/10-agent-routing.md +58 -0
- package/rules/15-mcp-launchpad.md +46 -0
- package/rules/20-code-review-loop.md +234 -0
- package/rules/25-documentation-updates.md +15 -0
- package/rules/30-prompt-templates.md +19 -0
- package/rules/35-memory.md +240 -0
- package/rules/40-critical-rules.md +124 -0
- package/rules/45-file-preview.md +29 -0
- package/rules/50-agent-bug-reporting.md +90 -0
- package/rules/55-coms-protocol.md +63 -0
- package/rules/60-task-tracking.md +66 -0
- package/skills/conflict-resolver/SKILL.md +213 -0
- package/skills/skill-creator/SKILL.md +85 -0
- package/skills/systematic-debugging/SKILL.md +353 -0
- package/skills/test-driven-development/SKILL.md +335 -0
- package/skills/update-agents-md/SKILL.md +282 -0
- package/templates/coms-feature-manager-prompt.md +313 -0
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AcpxDriver — ProviderDriver for agents 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/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 = "acpx-driver";
|
|
40
|
+
const DRIVER_KIND = "acpx";
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Config
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
export interface AcpxConfig {
|
|
47
|
+
readonly agent: string;
|
|
48
|
+
readonly enabled: boolean;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const acpxConfigSchema: ConfigSchema<AcpxConfig> = {
|
|
52
|
+
parse: (raw: unknown): AcpxConfig => {
|
|
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 createAcpxAdapter(
|
|
76
|
+
config: AcpxConfig,
|
|
77
|
+
cwd: string,
|
|
78
|
+
runtime: AcpxRuntime,
|
|
79
|
+
cwdSlug: string,
|
|
80
|
+
initialHandle?: AcpRuntimeHandle,
|
|
81
|
+
): ProviderAdapterShape {
|
|
82
|
+
const handles = new Map<string, AcpRuntimeHandle>();
|
|
83
|
+
const prevCumulative = new Map<string, { inputTokens?: number; outputTokens?: number; totalTokens?: number }>();
|
|
84
|
+
// Seed with the discovery handle so the first turn reuses it
|
|
85
|
+
if (initialHandle) {
|
|
86
|
+
handles.set("default", initialHandle);
|
|
87
|
+
}
|
|
88
|
+
const pendingHandles = new Map<string, Promise<AcpRuntimeHandle>>();
|
|
89
|
+
const systemPromptSent = new Set<string>();
|
|
90
|
+
|
|
91
|
+
function sessionKey(modelId?: string): string {
|
|
92
|
+
const model = modelId && modelId !== "default"
|
|
93
|
+
? `-${modelId.replace(/[^a-zA-Z0-9.-]/g, "_")}`
|
|
94
|
+
: "";
|
|
95
|
+
return `pi-${config.agent}${model}-${cwdSlug}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function ensureHandle(
|
|
99
|
+
acpxModelId: string | undefined,
|
|
100
|
+
systemPrompt?: string,
|
|
101
|
+
): Promise<AcpRuntimeHandle> {
|
|
102
|
+
const key = acpxModelId || "default";
|
|
103
|
+
|
|
104
|
+
const existing = handles.get(key);
|
|
105
|
+
if (existing) return existing;
|
|
106
|
+
|
|
107
|
+
const pending = pendingHandles.get(key);
|
|
108
|
+
if (pending) return pending;
|
|
109
|
+
|
|
110
|
+
const promise = (async () => {
|
|
111
|
+
try {
|
|
112
|
+
const sessionOpts: { model?: string; systemPrompt?: string } = {};
|
|
113
|
+
if (acpxModelId && acpxModelId !== "default") {
|
|
114
|
+
sessionOpts.model = acpxModelId;
|
|
115
|
+
}
|
|
116
|
+
if (systemPrompt) {
|
|
117
|
+
sessionOpts.systemPrompt = systemPrompt;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const handle = await runtime.ensureSession({
|
|
121
|
+
sessionKey: sessionKey(acpxModelId),
|
|
122
|
+
agent: config.agent,
|
|
123
|
+
mode: "persistent",
|
|
124
|
+
cwd,
|
|
125
|
+
...(Object.keys(sessionOpts).length > 0
|
|
126
|
+
? { sessionOptions: sessionOpts }
|
|
127
|
+
: {}),
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
handles.set(key, handle);
|
|
131
|
+
return handle;
|
|
132
|
+
} finally {
|
|
133
|
+
pendingHandles.delete(key);
|
|
134
|
+
}
|
|
135
|
+
})();
|
|
136
|
+
|
|
137
|
+
pendingHandles.set(key, promise);
|
|
138
|
+
return promise;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
startSession: async (opts: SessionStartOptions): Promise<SessionHandle> => {
|
|
143
|
+
const model = opts.model || "default";
|
|
144
|
+
const systemPrompt = opts.systemPrompt
|
|
145
|
+
? opts.systemPrompt
|
|
146
|
+
: buildExternalSystemPrompt({ systemPrompt: undefined }, cwd);
|
|
147
|
+
const handle = await ensureHandle(model, systemPrompt);
|
|
148
|
+
return {
|
|
149
|
+
sessionId: sessionKey(model),
|
|
150
|
+
model,
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
sendTurn: async (
|
|
155
|
+
handle: SessionHandle,
|
|
156
|
+
prompt: string,
|
|
157
|
+
opts?: TurnOptions,
|
|
158
|
+
): Promise<TurnResult> => {
|
|
159
|
+
const handleKey = handle.model || "default";
|
|
160
|
+
const needsSystemPrompt = !systemPromptSent.has(handleKey);
|
|
161
|
+
const systemPrompt = needsSystemPrompt
|
|
162
|
+
? buildExternalSystemPrompt({ systemPrompt: undefined }, cwd)
|
|
163
|
+
: undefined;
|
|
164
|
+
|
|
165
|
+
const acpxHandle = await ensureHandle(handle.model, systemPrompt);
|
|
166
|
+
if (needsSystemPrompt) {
|
|
167
|
+
systemPromptSent.add(handleKey);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const abortController = new AbortController();
|
|
171
|
+
if (opts?.signal) {
|
|
172
|
+
if (opts.signal.aborted) {
|
|
173
|
+
abortController.abort();
|
|
174
|
+
} else {
|
|
175
|
+
opts.signal.addEventListener("abort", () => abortController.abort(), { once: true });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const turn = runtime.startTurn({
|
|
180
|
+
handle: acpxHandle,
|
|
181
|
+
text: prompt,
|
|
182
|
+
mode: "prompt",
|
|
183
|
+
requestId: `pi-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
|
|
184
|
+
signal: abortController.signal,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
let text = "";
|
|
188
|
+
let thinking = "";
|
|
189
|
+
|
|
190
|
+
let lastBreakdown: any = undefined;
|
|
191
|
+
let lastCost: any = undefined;
|
|
192
|
+
let lastUsed: number | undefined;
|
|
193
|
+
|
|
194
|
+
for await (const event of turn.events) {
|
|
195
|
+
if (abortController.signal.aborted) break;
|
|
196
|
+
|
|
197
|
+
if (event.type === "text_delta" && event.text) {
|
|
198
|
+
if (event.stream === "thought") {
|
|
199
|
+
thinking += event.text;
|
|
200
|
+
opts?.onEvent?.({ kind: "thinking_delta", text: event.text });
|
|
201
|
+
} else {
|
|
202
|
+
text += event.text;
|
|
203
|
+
opts?.onEvent?.({ kind: "text_delta", text: event.text });
|
|
204
|
+
}
|
|
205
|
+
} else if (event.type === "status") {
|
|
206
|
+
if ((event as any).breakdown) {
|
|
207
|
+
lastBreakdown = (event as any).breakdown;
|
|
208
|
+
}
|
|
209
|
+
if ((event as any).cost) {
|
|
210
|
+
lastCost = (event as any).cost;
|
|
211
|
+
}
|
|
212
|
+
if (typeof (event as any).used === "number") {
|
|
213
|
+
lastUsed = (event as any).used;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const result = await turn.result;
|
|
219
|
+
let stopReason = "stop";
|
|
220
|
+
if (result.status === "completed") {
|
|
221
|
+
stopReason = result.stopReason === "end_turn" ? "stop" : (result.stopReason || "stop");
|
|
222
|
+
} else if (result.status === "failed") {
|
|
223
|
+
throw new Error(`acpx turn failed: ${result.error.message}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Build usage: prefer stream breakdown, fall back to getStatus() cumulative
|
|
227
|
+
let usage: {
|
|
228
|
+
inputTokens?: number;
|
|
229
|
+
outputTokens?: number;
|
|
230
|
+
cachedReadTokens?: number;
|
|
231
|
+
cachedWriteTokens?: number;
|
|
232
|
+
thoughtTokens?: number;
|
|
233
|
+
totalTokens?: number;
|
|
234
|
+
costUsd?: number;
|
|
235
|
+
} | undefined;
|
|
236
|
+
|
|
237
|
+
if (lastBreakdown) {
|
|
238
|
+
// Stream carried per-turn breakdown (Claude Code adapter does this)
|
|
239
|
+
usage = {
|
|
240
|
+
inputTokens: lastBreakdown.inputTokens ?? undefined,
|
|
241
|
+
outputTokens: lastBreakdown.outputTokens ?? undefined,
|
|
242
|
+
cachedReadTokens: lastBreakdown.cachedReadTokens ?? undefined,
|
|
243
|
+
cachedWriteTokens: lastBreakdown.cachedWriteTokens ?? undefined,
|
|
244
|
+
thoughtTokens: lastBreakdown.thoughtTokens ?? undefined,
|
|
245
|
+
totalTokens: lastBreakdown.totalTokens ?? undefined,
|
|
246
|
+
costUsd: lastCost?.total ?? lastCost?.usd ?? undefined,
|
|
247
|
+
};
|
|
248
|
+
} else if (lastUsed !== undefined) {
|
|
249
|
+
// Stream carried total token count via status.used (Cursor adapter)
|
|
250
|
+
usage = {
|
|
251
|
+
totalTokens: lastUsed,
|
|
252
|
+
costUsd: lastCost?.total ?? lastCost?.usd ?? undefined,
|
|
253
|
+
};
|
|
254
|
+
} else {
|
|
255
|
+
// Fall back to session-level usage from getStatus()
|
|
256
|
+
try {
|
|
257
|
+
const status = await runtime.getStatus({ handle: acpxHandle });
|
|
258
|
+
const su = status.usage;
|
|
259
|
+
if (su?.cumulative) {
|
|
260
|
+
const c = su.cumulative;
|
|
261
|
+
const key = handle.model || "default";
|
|
262
|
+
const prev = prevCumulative.get(key) || { inputTokens: 0, outputTokens: 0, totalTokens: 0 };
|
|
263
|
+
const deltaInput = (c.inputTokens ?? 0) - (prev.inputTokens ?? 0);
|
|
264
|
+
const deltaOutput = (c.outputTokens ?? 0) - (prev.outputTokens ?? 0);
|
|
265
|
+
const deltaTotal = (c.totalTokens ?? 0) - (prev.totalTokens ?? 0);
|
|
266
|
+
prevCumulative.set(key, {
|
|
267
|
+
inputTokens: c.inputTokens ?? 0,
|
|
268
|
+
outputTokens: c.outputTokens ?? 0,
|
|
269
|
+
totalTokens: c.totalTokens ?? 0,
|
|
270
|
+
});
|
|
271
|
+
if (deltaInput > 0 || deltaOutput > 0 || deltaTotal > 0) {
|
|
272
|
+
usage = {
|
|
273
|
+
inputTokens: deltaInput > 0 ? deltaInput : undefined,
|
|
274
|
+
outputTokens: deltaOutput > 0 ? deltaOutput : undefined,
|
|
275
|
+
totalTokens: deltaTotal > 0 ? deltaTotal : undefined,
|
|
276
|
+
costUsd: su.cost?.total ?? su.cost?.usd ?? undefined,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
} catch {
|
|
281
|
+
// Usage is best-effort — don't fail the turn
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return { text, thinking: thinking || undefined, stopReason, usage };
|
|
286
|
+
},
|
|
287
|
+
|
|
288
|
+
stopSession: async (handle: SessionHandle): Promise<void> => {
|
|
289
|
+
const key = handle.model || "default";
|
|
290
|
+
const acpxHandle = handles.get(key);
|
|
291
|
+
if (acpxHandle) {
|
|
292
|
+
await runtime.close({ handle: acpxHandle, reason: "session stop" }).catch((err: unknown) => {
|
|
293
|
+
fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
|
|
294
|
+
`session close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
295
|
+
});
|
|
296
|
+
handles.delete(key);
|
|
297
|
+
}
|
|
298
|
+
systemPromptSent.delete(key);
|
|
299
|
+
prevCumulative.delete(key);
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
stopAll: async (): Promise<void> => {
|
|
303
|
+
const closePromises: Promise<void>[] = [];
|
|
304
|
+
for (const [key, acpxHandle] of handles) {
|
|
305
|
+
closePromises.push(
|
|
306
|
+
runtime.close({ handle: acpxHandle, reason: "stop all" }).catch((err: unknown) => {
|
|
307
|
+
fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
|
|
308
|
+
`session close failed for ${key}: ${err instanceof Error ? err.message : String(err)}`);
|
|
309
|
+
}),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
await Promise.allSettled(closePromises);
|
|
313
|
+
handles.clear();
|
|
314
|
+
pendingHandles.clear();
|
|
315
|
+
systemPromptSent.clear();
|
|
316
|
+
prevCumulative.clear();
|
|
317
|
+
},
|
|
318
|
+
|
|
319
|
+
hasSession: (sessionId: string): boolean => {
|
|
320
|
+
for (const key of handles.keys()) {
|
|
321
|
+
if (sessionKey(key === "default" ? undefined : key) === sessionId) {
|
|
322
|
+
return true;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return false;
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ---------------------------------------------------------------------------
|
|
331
|
+
// Discovery helper
|
|
332
|
+
// ---------------------------------------------------------------------------
|
|
333
|
+
|
|
334
|
+
async function discoverAcpxModelsInternal(
|
|
335
|
+
agent: string,
|
|
336
|
+
cwd: string,
|
|
337
|
+
): Promise<string[]> {
|
|
338
|
+
const { createAcpRuntime, createFileSessionStore, createAgentRegistry } =
|
|
339
|
+
await loadAcpxRuntime();
|
|
340
|
+
|
|
341
|
+
const uid = randomUUID().slice(0, 8);
|
|
342
|
+
const stateDir = path.join(os.homedir(), ".acpx", `discover-${process.pid}-${uid}`);
|
|
343
|
+
const runtime = createAcpRuntime({
|
|
344
|
+
cwd,
|
|
345
|
+
sessionStore: createFileSessionStore({ stateDir }),
|
|
346
|
+
agentRegistry: createAgentRegistry(),
|
|
347
|
+
permissionMode: "deny-all",
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
let handle: AcpRuntimeHandle | undefined;
|
|
351
|
+
try {
|
|
352
|
+
handle = await runtime.ensureSession({
|
|
353
|
+
sessionKey: `discover-${agent}-${uid}`,
|
|
354
|
+
agent,
|
|
355
|
+
mode: "oneshot",
|
|
356
|
+
cwd,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const status = await runtime.getStatus({ handle });
|
|
360
|
+
return status.models?.availableModelIds || [];
|
|
361
|
+
} catch (err) {
|
|
362
|
+
fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
|
|
363
|
+
`model discovery failed for ${agent}: ${err instanceof Error ? err.message : String(err)}`);
|
|
364
|
+
return [];
|
|
365
|
+
} finally {
|
|
366
|
+
if (handle) {
|
|
367
|
+
await runtime.close({ handle, reason: "discovery complete" }).catch(() => {});
|
|
368
|
+
}
|
|
369
|
+
await rm(stateDir, { recursive: true, force: true }).catch(() => {});
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ---------------------------------------------------------------------------
|
|
374
|
+
// Driver
|
|
375
|
+
// ---------------------------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
export const AcpxDriver: ProviderDriver<AcpxConfig> = {
|
|
378
|
+
driverKind: DRIVER_KIND,
|
|
379
|
+
metadata: {
|
|
380
|
+
displayName: "ACPX",
|
|
381
|
+
supportsMultipleInstances: false,
|
|
382
|
+
},
|
|
383
|
+
configSchema: acpxConfigSchema,
|
|
384
|
+
defaultConfig: () => ({ agent: "cursor", enabled: true }),
|
|
385
|
+
|
|
386
|
+
probe: async (_config: AcpxConfig): Promise<ProviderProbeResult> => {
|
|
387
|
+
try {
|
|
388
|
+
await loadAcpxRuntime();
|
|
389
|
+
return { available: true };
|
|
390
|
+
} catch (err) {
|
|
391
|
+
return {
|
|
392
|
+
available: false,
|
|
393
|
+
reason: `acpx runtime not available: ${err instanceof Error ? err.message : String(err)}`,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
},
|
|
397
|
+
|
|
398
|
+
create: async (
|
|
399
|
+
input: ProviderDriverCreateInput<AcpxConfig>,
|
|
400
|
+
): Promise<ProviderInstance> => {
|
|
401
|
+
const { instanceId, config, cwd } = input;
|
|
402
|
+
const cwdSlug = createHash("sha256").update(cwd).digest("hex").slice(0, 12);
|
|
403
|
+
|
|
404
|
+
// Load runtime
|
|
405
|
+
const { createAcpRuntime, createFileSessionStore, createAgentRegistry } =
|
|
406
|
+
await loadAcpxRuntime();
|
|
407
|
+
|
|
408
|
+
const stateDir = path.join(os.homedir(), ".acpx", `pi-${cwdSlug}`);
|
|
409
|
+
const runtime = createAcpRuntime({
|
|
410
|
+
cwd,
|
|
411
|
+
sessionStore: createFileSessionStore({ stateDir }),
|
|
412
|
+
agentRegistry: createAgentRegistry(),
|
|
413
|
+
permissionMode: "approve-all",
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// Discover models — reuse the session for the adapter (no throwaway)
|
|
417
|
+
let discoveredModelIds: string[] = [];
|
|
418
|
+
let initialHandle: AcpRuntimeHandle | undefined;
|
|
419
|
+
try {
|
|
420
|
+
initialHandle = await runtime.ensureSession({
|
|
421
|
+
sessionKey: `pi-${config.agent}-default`,
|
|
422
|
+
agent: config.agent,
|
|
423
|
+
mode: "persistent",
|
|
424
|
+
cwd,
|
|
425
|
+
});
|
|
426
|
+
const status = await runtime.getStatus({ handle: initialHandle });
|
|
427
|
+
discoveredModelIds = status.models?.availableModelIds || [];
|
|
428
|
+
} catch (err) {
|
|
429
|
+
fileLog(LOG_DOMAIN, "warn", LOG_DOMAIN,
|
|
430
|
+
`initial model discovery failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const discoveredModels: DiscoveredModel[] = discoveredModelIds.map((id) => ({
|
|
434
|
+
id,
|
|
435
|
+
name: modelIdToDisplayName(id),
|
|
436
|
+
}));
|
|
437
|
+
|
|
438
|
+
// Managed snapshot with periodic refresh
|
|
439
|
+
const managed = makeManagedSnapshot({
|
|
440
|
+
skipInitialRefresh: true,
|
|
441
|
+
initialSnapshot: () => buildInitialSnapshot(true, discoveredModels),
|
|
442
|
+
checkProvider: async () => {
|
|
443
|
+
try {
|
|
444
|
+
const modelIds = await discoverAcpxModelsInternal(config.agent, cwd);
|
|
445
|
+
const models = modelIds.map((id) => ({
|
|
446
|
+
id,
|
|
447
|
+
name: modelIdToDisplayName(id),
|
|
448
|
+
}));
|
|
449
|
+
return buildInitialSnapshot(true, models);
|
|
450
|
+
} catch {
|
|
451
|
+
return buildInitialSnapshot(true, discoveredModels);
|
|
452
|
+
}
|
|
453
|
+
},
|
|
454
|
+
getSettings: () => config,
|
|
455
|
+
haveSettingsChanged: (prev, next) =>
|
|
456
|
+
prev.agent !== next.agent || prev.enabled !== next.enabled,
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
// Create adapter
|
|
460
|
+
const adapter = createAcpxAdapter(config, cwd, runtime, cwdSlug, initialHandle);
|
|
461
|
+
|
|
462
|
+
return {
|
|
463
|
+
instanceId,
|
|
464
|
+
driverKind: DRIVER_KIND,
|
|
465
|
+
displayName: input.displayName || `ACPX ${config.agent}`,
|
|
466
|
+
enabled: input.enabled,
|
|
467
|
+
snapshot: {
|
|
468
|
+
getSnapshot: managed.getSnapshot,
|
|
469
|
+
refresh: managed.refresh,
|
|
470
|
+
dispose: managed.dispose,
|
|
471
|
+
},
|
|
472
|
+
adapter,
|
|
473
|
+
dispose: async () => {
|
|
474
|
+
managed.dispose();
|
|
475
|
+
await adapter.stopAll();
|
|
476
|
+
},
|
|
477
|
+
};
|
|
478
|
+
},
|
|
479
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BUILT_IN_DRIVERS — the static set of ProviderDrivers this build ships with.
|
|
3
|
+
*
|
|
4
|
+
* Adapted from t3code's `builtInDrivers.ts`. Every driver that the server
|
|
5
|
+
* knows how to instantiate from settings is listed here. The registry
|
|
6
|
+
* iterates this array when resolving provider instances; anything not in
|
|
7
|
+
* the array surfaces as an "unavailable" shadow snapshot.
|
|
8
|
+
*
|
|
9
|
+
* Adding a new first-party driver means:
|
|
10
|
+
* 1. Implement `ProviderDriver` in a sibling `<Name>Driver.ts`
|
|
11
|
+
* 2. Add it to this array
|
|
12
|
+
* 3. Update the unified extension entry to register it
|
|
13
|
+
*
|
|
14
|
+
* @module providers/built-in-drivers
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { AnyProviderDriver } from "../shared/provider-driver.js";
|
|
18
|
+
import { ClaudeDriver } from "./claude-driver.js";
|
|
19
|
+
import { GeminiDriver } from "./gemini-driver.js";
|
|
20
|
+
import { CursorCliDriver } from "./cursor-cli-driver.js";
|
|
21
|
+
import { AcpxDriver } from "./acpx-driver.js";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Ordered list of built-in drivers. Order matters only for tie-breaking
|
|
25
|
+
* in UI presentation — the registry itself is keyed by `driverKind`, so
|
|
26
|
+
* iteration order has no functional effect on instance lookup.
|
|
27
|
+
*/
|
|
28
|
+
export const BUILT_IN_DRIVERS: readonly AnyProviderDriver[] = [
|
|
29
|
+
ClaudeDriver,
|
|
30
|
+
GeminiDriver,
|
|
31
|
+
CursorCliDriver,
|
|
32
|
+
AcpxDriver,
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Map from setting agent name → driver kind(s).
|
|
37
|
+
*
|
|
38
|
+
* Settings use agent names like "cursor", "claude", "gemini".
|
|
39
|
+
* Drivers use kinds like "cursor-cli", "acpx", "claude-cli".
|
|
40
|
+
* This map resolves the mapping.
|
|
41
|
+
*/
|
|
42
|
+
export const CLI_AGENT_TO_DRIVER: Record<string, string> = {
|
|
43
|
+
claude: "claude-cli",
|
|
44
|
+
gemini: "gemini-cli",
|
|
45
|
+
cursor: "cursor-cli",
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const ACPX_AGENT_TO_DRIVER: Record<string, string> = {
|
|
49
|
+
cursor: "acpx",
|
|
50
|
+
// Add more as they're implemented:
|
|
51
|
+
// copilot: "acpx",
|
|
52
|
+
};
|