@jx-grxf/patchpilot 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.
- package/.env.example +13 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +71 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/agent.d.ts +21 -0
- package/dist/core/agent.js +346 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/core/codex.d.ts +22 -0
- package/dist/core/codex.js +242 -0
- package/dist/core/codex.js.map +1 -0
- package/dist/core/compute.d.ts +9 -0
- package/dist/core/compute.js +18 -0
- package/dist/core/compute.js.map +1 -0
- package/dist/core/doctor.d.ts +7 -0
- package/dist/core/doctor.js +226 -0
- package/dist/core/doctor.js.map +1 -0
- package/dist/core/env.d.ts +6 -0
- package/dist/core/env.js +103 -0
- package/dist/core/env.js.map +1 -0
- package/dist/core/gemini.d.ts +20 -0
- package/dist/core/gemini.js +177 -0
- package/dist/core/gemini.js.map +1 -0
- package/dist/core/json.d.ts +3 -0
- package/dist/core/json.js +95 -0
- package/dist/core/json.js.map +1 -0
- package/dist/core/modelClient.d.ts +8 -0
- package/dist/core/modelClient.js +42 -0
- package/dist/core/modelClient.js.map +1 -0
- package/dist/core/nvidia.d.ts +19 -0
- package/dist/core/nvidia.js +160 -0
- package/dist/core/nvidia.js.map +1 -0
- package/dist/core/ollama.d.ts +31 -0
- package/dist/core/ollama.js +176 -0
- package/dist/core/ollama.js.map +1 -0
- package/dist/core/openrouter.d.ts +27 -0
- package/dist/core/openrouter.js +168 -0
- package/dist/core/openrouter.js.map +1 -0
- package/dist/core/subagents.d.ts +14 -0
- package/dist/core/subagents.js +89 -0
- package/dist/core/subagents.js.map +1 -0
- package/dist/core/tokenAccounting.d.ts +6 -0
- package/dist/core/tokenAccounting.js +134 -0
- package/dist/core/tokenAccounting.js.map +1 -0
- package/dist/core/types.d.ts +90 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/workspace.d.ts +28 -0
- package/dist/core/workspace.js +616 -0
- package/dist/core/workspace.js.map +1 -0
- package/dist/tui/App.d.ts +6 -0
- package/dist/tui/App.js +1717 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/commands.d.ts +14 -0
- package/dist/tui/commands.js +210 -0
- package/dist/tui/commands.js.map +1 -0
- package/dist/tui/components/CommandSuggestions.d.ts +12 -0
- package/dist/tui/components/CommandSuggestions.js +12 -0
- package/dist/tui/components/CommandSuggestions.js.map +1 -0
- package/dist/tui/components/Composer.d.ts +13 -0
- package/dist/tui/components/Composer.js +29 -0
- package/dist/tui/components/Composer.js.map +1 -0
- package/dist/tui/components/Header.d.ts +25 -0
- package/dist/tui/components/Header.js +62 -0
- package/dist/tui/components/Header.js.map +1 -0
- package/dist/tui/components/OnboardingPanel.d.ts +38 -0
- package/dist/tui/components/OnboardingPanel.js +85 -0
- package/dist/tui/components/OnboardingPanel.js.map +1 -0
- package/dist/tui/components/Sidebar.d.ts +22 -0
- package/dist/tui/components/Sidebar.js +133 -0
- package/dist/tui/components/Sidebar.js.map +1 -0
- package/dist/tui/components/Transcript.d.ts +10 -0
- package/dist/tui/components/Transcript.js +111 -0
- package/dist/tui/components/Transcript.js.map +1 -0
- package/dist/tui/format.d.ts +29 -0
- package/dist/tui/format.js +202 -0
- package/dist/tui/format.js.map +1 -0
- package/dist/tui/hosts.d.ts +34 -0
- package/dist/tui/hosts.js +338 -0
- package/dist/tui/hosts.js.map +1 -0
- package/dist/tui/inputRouting.d.ts +8 -0
- package/dist/tui/inputRouting.js +94 -0
- package/dist/tui/inputRouting.js.map +1 -0
- package/dist/tui/platform.d.ts +2 -0
- package/dist/tui/platform.js +13 -0
- package/dist/tui/platform.js.map +1 -0
- package/dist/tui/systemStats.d.ts +25 -0
- package/dist/tui/systemStats.js +88 -0
- package/dist/tui/systemStats.js.map +1 -0
- package/dist/tui/types.d.ts +16 -0
- package/dist/tui/types.js +2 -0
- package/dist/tui/types.js.map +1 -0
- package/docs/showcase/patchpilot-showcase.svg +39 -0
- package/package.json +63 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { attachTokenCost } from "./tokenAccounting.js";
|
|
2
|
+
export const defaultOpenRouterModel = "openrouter/auto";
|
|
3
|
+
export const defaultOpenRouterBaseUrl = "https://openrouter.ai/api/v1";
|
|
4
|
+
const modelPricingCacheTtlMs = 15 * 60_000;
|
|
5
|
+
let modelPricingCache = null;
|
|
6
|
+
export class OpenRouterClient {
|
|
7
|
+
apiKey;
|
|
8
|
+
baseUrl;
|
|
9
|
+
runtimeOptions;
|
|
10
|
+
constructor(apiKey = readOpenRouterApiKey(), baseUrl = process.env.PATCHPILOT_OPENROUTER_BASE_URL ?? defaultOpenRouterBaseUrl, runtimeOptions = readOpenRouterRuntimeOptions()) {
|
|
11
|
+
this.apiKey = apiKey;
|
|
12
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
13
|
+
this.runtimeOptions = runtimeOptions;
|
|
14
|
+
}
|
|
15
|
+
async chat(options) {
|
|
16
|
+
this.assertConfigured();
|
|
17
|
+
const startedAt = Date.now();
|
|
18
|
+
const response = await this.fetchOpenRouter("/chat/completions", {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: {
|
|
21
|
+
"Content-Type": "application/json",
|
|
22
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
23
|
+
"HTTP-Referer": "https://github.com/jx-grxf/PatchPilot",
|
|
24
|
+
"X-Title": "PatchPilot"
|
|
25
|
+
},
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
model: normalizeOpenRouterModel(options.model),
|
|
28
|
+
messages: options.messages,
|
|
29
|
+
max_tokens: this.runtimeOptions.maxTokens,
|
|
30
|
+
temperature: this.runtimeOptions.temperature,
|
|
31
|
+
reasoning: options.reasoningEffort
|
|
32
|
+
? {
|
|
33
|
+
effort: options.reasoningEffort,
|
|
34
|
+
exclude: true
|
|
35
|
+
}
|
|
36
|
+
: undefined,
|
|
37
|
+
response_format: options.formatJson ? { type: "json_object" } : undefined
|
|
38
|
+
}),
|
|
39
|
+
signal: options.signal
|
|
40
|
+
});
|
|
41
|
+
const durationMs = Date.now() - startedAt;
|
|
42
|
+
const payload = (await readJsonSafely(response));
|
|
43
|
+
if (!response.ok || payload.error) {
|
|
44
|
+
const reason = payload.error?.message ? ` ${payload.error.message}` : "";
|
|
45
|
+
throw new Error(`OpenRouter chat failed for model "${options.model}": HTTP ${response.status}.${reason}`);
|
|
46
|
+
}
|
|
47
|
+
const content = payload.choices?.[0]?.message?.content?.trim() ?? "";
|
|
48
|
+
if (!content) {
|
|
49
|
+
throw new Error("OpenRouter returned an empty response.");
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
content,
|
|
53
|
+
telemetry: await toTelemetry(payload, durationMs, options.model)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async listModels() {
|
|
57
|
+
const models = await fetchOpenRouterModels(this.baseUrl);
|
|
58
|
+
return [defaultOpenRouterModel, ...models.map((model) => model.id).sort()];
|
|
59
|
+
}
|
|
60
|
+
async fetchOpenRouter(path, init) {
|
|
61
|
+
try {
|
|
62
|
+
return await fetch(`${this.baseUrl}${path}`, init);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const suffix = error instanceof Error ? ` ${error.message}` : "";
|
|
66
|
+
throw new Error(`Cannot reach OpenRouter API at ${this.baseUrl}.${suffix}`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
assertConfigured() {
|
|
70
|
+
if (!this.apiKey) {
|
|
71
|
+
throw new Error("OpenRouter API key missing. Set OPENROUTER_API_KEY in PatchPilot config, .env, or your shell.");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
export function readOpenRouterApiKey(env = process.env) {
|
|
76
|
+
return env.OPENROUTER_API_KEY?.trim() || env.PATCHPILOT_OPENROUTER_API_KEY?.trim() || "";
|
|
77
|
+
}
|
|
78
|
+
export function isOpenRouterFreeModel(model) {
|
|
79
|
+
return normalizeOpenRouterModel(model).endsWith(":free");
|
|
80
|
+
}
|
|
81
|
+
export async function getOpenRouterModelRates(model) {
|
|
82
|
+
const normalizedModel = normalizeOpenRouterModel(model);
|
|
83
|
+
const rates = await readOpenRouterPricing();
|
|
84
|
+
return rates.get(normalizedModel) ?? null;
|
|
85
|
+
}
|
|
86
|
+
function normalizeOpenRouterModel(model) {
|
|
87
|
+
const trimmedModel = model.trim();
|
|
88
|
+
if (!trimmedModel || trimmedModel === "auto" || trimmedModel === "openrouter/auto") {
|
|
89
|
+
return defaultOpenRouterModel;
|
|
90
|
+
}
|
|
91
|
+
return trimmedModel;
|
|
92
|
+
}
|
|
93
|
+
async function readOpenRouterPricing() {
|
|
94
|
+
if (modelPricingCache && modelPricingCache.expiresAt > Date.now()) {
|
|
95
|
+
return modelPricingCache.rates;
|
|
96
|
+
}
|
|
97
|
+
const models = await fetchOpenRouterModels(process.env.PATCHPILOT_OPENROUTER_BASE_URL ?? defaultOpenRouterBaseUrl).catch(() => []);
|
|
98
|
+
const rates = new Map();
|
|
99
|
+
for (const model of models) {
|
|
100
|
+
rates.set(model.id, {
|
|
101
|
+
inputPerToken: readPrice(model.pricing?.prompt),
|
|
102
|
+
cachedInputPerToken: readPrice(model.pricing?.input_cache_read),
|
|
103
|
+
cacheWritePerToken: readPrice(model.pricing?.input_cache_write),
|
|
104
|
+
outputPerToken: readPrice(model.pricing?.completion)
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
modelPricingCache = {
|
|
108
|
+
expiresAt: Date.now() + modelPricingCacheTtlMs,
|
|
109
|
+
rates
|
|
110
|
+
};
|
|
111
|
+
return rates;
|
|
112
|
+
}
|
|
113
|
+
async function fetchOpenRouterModels(baseUrl) {
|
|
114
|
+
const response = await fetch(`${baseUrl.replace(/\/$/, "")}/models`);
|
|
115
|
+
const payload = (await readJsonSafely(response));
|
|
116
|
+
if (!response.ok || payload.error) {
|
|
117
|
+
const reason = payload.error?.message ? ` ${payload.error.message}` : "";
|
|
118
|
+
throw new Error(`OpenRouter models failed with HTTP ${response.status}.${reason}`);
|
|
119
|
+
}
|
|
120
|
+
return payload.data ?? [];
|
|
121
|
+
}
|
|
122
|
+
async function toTelemetry(payload, durationMs, model) {
|
|
123
|
+
const promptTokens = payload.usage?.prompt_tokens ?? 0;
|
|
124
|
+
const responseTokens = payload.usage?.completion_tokens ?? 0;
|
|
125
|
+
const totalTokens = payload.usage?.total_tokens ?? promptTokens + responseTokens;
|
|
126
|
+
const cachedPromptTokens = payload.usage?.prompt_tokens_details?.cached_tokens ?? 0;
|
|
127
|
+
const cacheWriteTokens = payload.usage?.prompt_tokens_details?.cache_write_tokens ?? 0;
|
|
128
|
+
const rates = await getOpenRouterModelRates(model);
|
|
129
|
+
return attachTokenCost({
|
|
130
|
+
promptTokens,
|
|
131
|
+
cachedPromptTokens,
|
|
132
|
+
cacheWriteTokens,
|
|
133
|
+
responseTokens,
|
|
134
|
+
totalTokens,
|
|
135
|
+
evalTokensPerSecond: responseTokens > 0 && durationMs > 0 ? responseTokens / (durationMs / 1000) : null,
|
|
136
|
+
promptDurationMs: 0,
|
|
137
|
+
responseDurationMs: durationMs,
|
|
138
|
+
totalDurationMs: durationMs,
|
|
139
|
+
tokenSource: "provider"
|
|
140
|
+
}, "openrouter", model, rates, payload.usage?.cost);
|
|
141
|
+
}
|
|
142
|
+
function readOpenRouterRuntimeOptions(env = process.env) {
|
|
143
|
+
return {
|
|
144
|
+
maxTokens: readPositiveInteger(env.PATCHPILOT_NUM_PREDICT, 1024),
|
|
145
|
+
temperature: readTemperature(env.PATCHPILOT_TEMPERATURE, 0.1)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async function readJsonSafely(response) {
|
|
149
|
+
try {
|
|
150
|
+
return await response.json();
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return {};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function readPrice(value) {
|
|
157
|
+
const parsedValue = Number.parseFloat(value ?? "0");
|
|
158
|
+
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : 0;
|
|
159
|
+
}
|
|
160
|
+
function readPositiveInteger(value, fallback) {
|
|
161
|
+
const parsedValue = Number.parseInt(value ?? "", 10);
|
|
162
|
+
return Number.isFinite(parsedValue) && parsedValue > 0 ? parsedValue : fallback;
|
|
163
|
+
}
|
|
164
|
+
function readTemperature(value, fallback) {
|
|
165
|
+
const parsedValue = Number.parseFloat(value ?? "");
|
|
166
|
+
return Number.isFinite(parsedValue) && parsedValue >= 0 ? parsedValue : fallback;
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/core/openrouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ModelClient, ModelTelemetry, SubagentRole } from "./types.js";
|
|
2
|
+
export type SubagentAdvice = {
|
|
3
|
+
role: SubagentRole;
|
|
4
|
+
message: string;
|
|
5
|
+
telemetry: ModelTelemetry;
|
|
6
|
+
};
|
|
7
|
+
export declare function runSubagentAdvisors(options: {
|
|
8
|
+
client: ModelClient;
|
|
9
|
+
model: string;
|
|
10
|
+
task: string;
|
|
11
|
+
workspaceRoot: string;
|
|
12
|
+
workspaceSummary?: string;
|
|
13
|
+
}): Promise<SubagentAdvice[]>;
|
|
14
|
+
export declare function formatSubagentContext(advice: SubagentAdvice[]): string;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const advisorSpecs = [
|
|
4
|
+
{
|
|
5
|
+
role: "explorer",
|
|
6
|
+
system: [
|
|
7
|
+
"You are PatchPilot's explorer subagent.",
|
|
8
|
+
"Map likely files, commands, and repo shape for the primary agent.",
|
|
9
|
+
"Stay factual and brief. Do not warn unless a risk blocks progress.",
|
|
10
|
+
"Use at most 4 short bullet lines."
|
|
11
|
+
].join("\n")
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
role: "planner",
|
|
15
|
+
system: [
|
|
16
|
+
"You are PatchPilot's planner subagent.",
|
|
17
|
+
"Give the primary agent a direct tactical path before it edits code.",
|
|
18
|
+
"Focus on likely files, next actions, and fast verification.",
|
|
19
|
+
"Do not claim you inspected files beyond the provided workspace hint.",
|
|
20
|
+
"Use at most 4 short bullet lines."
|
|
21
|
+
].join("\n")
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
role: "reviewer",
|
|
25
|
+
system: [
|
|
26
|
+
"You are PatchPilot's reviewer subagent.",
|
|
27
|
+
"Call out only concrete blockers or easy checks the primary agent should not miss.",
|
|
28
|
+
"Do not produce generic warnings about permissions, overwrites, or tests for simple explicit file edits.",
|
|
29
|
+
"Use at most 3 short bullet lines."
|
|
30
|
+
].join("\n")
|
|
31
|
+
}
|
|
32
|
+
];
|
|
33
|
+
export async function runSubagentAdvisors(options) {
|
|
34
|
+
const workspaceHint = await buildWorkspaceHint(options.workspaceRoot);
|
|
35
|
+
const userMessage = [
|
|
36
|
+
`Task: ${options.task}`,
|
|
37
|
+
`Workspace: ${path.basename(options.workspaceRoot) || "workspace"}`,
|
|
38
|
+
"",
|
|
39
|
+
"Workspace hint:",
|
|
40
|
+
workspaceHint,
|
|
41
|
+
options.workspaceSummary ? `\nWorkspace context:\n${options.workspaceSummary}` : ""
|
|
42
|
+
].join("\n");
|
|
43
|
+
const results = await Promise.allSettled(advisorSpecs.map(async (advisor) => {
|
|
44
|
+
const messages = [
|
|
45
|
+
{
|
|
46
|
+
role: "system",
|
|
47
|
+
content: advisor.system
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
role: "user",
|
|
51
|
+
content: userMessage
|
|
52
|
+
}
|
|
53
|
+
];
|
|
54
|
+
const response = await options.client.chat({
|
|
55
|
+
model: options.model,
|
|
56
|
+
messages,
|
|
57
|
+
formatJson: false
|
|
58
|
+
});
|
|
59
|
+
return {
|
|
60
|
+
role: advisor.role,
|
|
61
|
+
message: clipAdvice(response.content),
|
|
62
|
+
telemetry: response.telemetry
|
|
63
|
+
};
|
|
64
|
+
}));
|
|
65
|
+
return results.flatMap((result) => (result.status === "fulfilled" ? [result.value] : []));
|
|
66
|
+
}
|
|
67
|
+
export function formatSubagentContext(advice) {
|
|
68
|
+
if (advice.length === 0) {
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
return advice.map((item) => `${item.role} subagent:\n${item.message}`).join("\n\n");
|
|
72
|
+
}
|
|
73
|
+
async function buildWorkspaceHint(workspaceRoot) {
|
|
74
|
+
const entries = await readdir(workspaceRoot, { withFileTypes: true }).catch(() => []);
|
|
75
|
+
const visibleEntries = entries
|
|
76
|
+
.filter((entry) => !entry.name.startsWith(".") || entry.name === ".github")
|
|
77
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
78
|
+
.slice(0, 60)
|
|
79
|
+
.map((entry) => `${entry.isDirectory() ? "dir " : "file"} ${entry.name}`);
|
|
80
|
+
return visibleEntries.length > 0 ? visibleEntries.join("\n") : "No top-level workspace entries could be read.";
|
|
81
|
+
}
|
|
82
|
+
function clipAdvice(value) {
|
|
83
|
+
const trimmedValue = value.trim();
|
|
84
|
+
if (trimmedValue.length <= 1200) {
|
|
85
|
+
return trimmedValue;
|
|
86
|
+
}
|
|
87
|
+
return `${trimmedValue.slice(0, 1200)}\n...[clipped ${trimmedValue.length - 1200} chars]`;
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=subagents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagents.js","sourceRoot":"","sources":["../../src/core/subagents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ModelProvider, ModelTelemetry, SessionTelemetry } from "./types.js";
|
|
2
|
+
import type { OpenRouterTokenRates } from "./openrouter.js";
|
|
3
|
+
export declare function estimateTokens(value: string): number;
|
|
4
|
+
export declare function attachTokenCost(telemetry: Omit<ModelTelemetry, "estimatedCostUsd" | "costSource">, provider: ModelProvider, model: string, openRouterRates?: OpenRouterTokenRates | null, providerCostUsd?: number | null): ModelTelemetry;
|
|
5
|
+
export declare function emptySessionTelemetry(): SessionTelemetry;
|
|
6
|
+
export declare function addTelemetryToSession(session: SessionTelemetry, telemetry: ModelTelemetry): SessionTelemetry;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
const codexApiTokenRates = {
|
|
2
|
+
"gpt-5.4": {
|
|
3
|
+
inputPerMillion: 2.5,
|
|
4
|
+
cachedInputPerMillion: 0.25,
|
|
5
|
+
outputPerMillion: 15
|
|
6
|
+
},
|
|
7
|
+
"gpt-5.4-mini": {
|
|
8
|
+
inputPerMillion: 0.75,
|
|
9
|
+
cachedInputPerMillion: 0.075,
|
|
10
|
+
outputPerMillion: 4.5
|
|
11
|
+
},
|
|
12
|
+
"gpt-5.2": {
|
|
13
|
+
inputPerMillion: 1.75,
|
|
14
|
+
cachedInputPerMillion: 0.175,
|
|
15
|
+
outputPerMillion: 14
|
|
16
|
+
},
|
|
17
|
+
"gpt-5.2-codex": {
|
|
18
|
+
inputPerMillion: 1.75,
|
|
19
|
+
cachedInputPerMillion: 0.175,
|
|
20
|
+
outputPerMillion: 14
|
|
21
|
+
},
|
|
22
|
+
"gpt-5.3-codex": {
|
|
23
|
+
inputPerMillion: 1.75,
|
|
24
|
+
cachedInputPerMillion: 0.175,
|
|
25
|
+
outputPerMillion: 14
|
|
26
|
+
},
|
|
27
|
+
"gpt-5.1-codex-max": {
|
|
28
|
+
inputPerMillion: 1.25,
|
|
29
|
+
cachedInputPerMillion: 0.125,
|
|
30
|
+
outputPerMillion: 10
|
|
31
|
+
},
|
|
32
|
+
"gpt-5.1-codex-mini": {
|
|
33
|
+
inputPerMillion: 0.25,
|
|
34
|
+
cachedInputPerMillion: 0.025,
|
|
35
|
+
outputPerMillion: 2
|
|
36
|
+
},
|
|
37
|
+
"codex-mini-latest": {
|
|
38
|
+
inputPerMillion: 1.5,
|
|
39
|
+
cachedInputPerMillion: 0.375,
|
|
40
|
+
outputPerMillion: 6
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
export function estimateTokens(value) {
|
|
44
|
+
const normalizedValue = value.trim();
|
|
45
|
+
return normalizedValue ? Math.ceil(normalizedValue.length / 4) : 0;
|
|
46
|
+
}
|
|
47
|
+
export function attachTokenCost(telemetry, provider, model, openRouterRates, providerCostUsd) {
|
|
48
|
+
if (provider === "ollama") {
|
|
49
|
+
return {
|
|
50
|
+
...telemetry,
|
|
51
|
+
estimatedCostUsd: 0,
|
|
52
|
+
costSource: "local"
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
if (provider === "openrouter") {
|
|
56
|
+
if (providerCostUsd !== undefined && providerCostUsd !== null) {
|
|
57
|
+
return {
|
|
58
|
+
...telemetry,
|
|
59
|
+
cachedPromptTokens: Math.min(telemetry.cachedPromptTokens, telemetry.promptTokens),
|
|
60
|
+
estimatedCostUsd: providerCostUsd,
|
|
61
|
+
costSource: "api-pricing"
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
if (!openRouterRates) {
|
|
65
|
+
return {
|
|
66
|
+
...telemetry,
|
|
67
|
+
cachedPromptTokens: Math.min(telemetry.cachedPromptTokens, telemetry.promptTokens),
|
|
68
|
+
estimatedCostUsd: null,
|
|
69
|
+
costSource: "unknown"
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const cachedPromptTokens = Math.min(telemetry.cachedPromptTokens, telemetry.promptTokens);
|
|
73
|
+
const cacheWriteTokens = Math.min(telemetry.cacheWriteTokens, Math.max(0, telemetry.promptTokens - cachedPromptTokens));
|
|
74
|
+
const uncachedPromptTokens = Math.max(0, telemetry.promptTokens - cachedPromptTokens - cacheWriteTokens);
|
|
75
|
+
const estimatedCostUsd = uncachedPromptTokens * openRouterRates.inputPerToken +
|
|
76
|
+
cachedPromptTokens * openRouterRates.cachedInputPerToken +
|
|
77
|
+
cacheWriteTokens * openRouterRates.cacheWritePerToken +
|
|
78
|
+
telemetry.responseTokens * openRouterRates.outputPerToken;
|
|
79
|
+
return {
|
|
80
|
+
...telemetry,
|
|
81
|
+
cachedPromptTokens,
|
|
82
|
+
estimatedCostUsd,
|
|
83
|
+
costSource: "api-pricing"
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
const rates = provider === "codex" ? codexApiTokenRates[model] : undefined;
|
|
87
|
+
if (!rates) {
|
|
88
|
+
return {
|
|
89
|
+
...telemetry,
|
|
90
|
+
estimatedCostUsd: null,
|
|
91
|
+
costSource: "unknown"
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const cachedPromptTokens = Math.min(telemetry.cachedPromptTokens, telemetry.promptTokens);
|
|
95
|
+
const uncachedPromptTokens = Math.max(0, telemetry.promptTokens - cachedPromptTokens);
|
|
96
|
+
const estimatedCostUsd = (uncachedPromptTokens * rates.inputPerMillion +
|
|
97
|
+
cachedPromptTokens * rates.cachedInputPerMillion +
|
|
98
|
+
telemetry.responseTokens * rates.outputPerMillion) /
|
|
99
|
+
1_000_000;
|
|
100
|
+
return {
|
|
101
|
+
...telemetry,
|
|
102
|
+
cachedPromptTokens,
|
|
103
|
+
estimatedCostUsd,
|
|
104
|
+
costSource: "api-pricing"
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
export function emptySessionTelemetry() {
|
|
108
|
+
return {
|
|
109
|
+
requests: 0,
|
|
110
|
+
promptTokens: 0,
|
|
111
|
+
cachedPromptTokens: 0,
|
|
112
|
+
cacheWriteTokens: 0,
|
|
113
|
+
responseTokens: 0,
|
|
114
|
+
totalTokens: 0,
|
|
115
|
+
estimatedCostUsd: null
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export function addTelemetryToSession(session, telemetry) {
|
|
119
|
+
const estimatedCostUsd = session.requests === 0
|
|
120
|
+
? telemetry.estimatedCostUsd
|
|
121
|
+
: session.estimatedCostUsd === null || telemetry.estimatedCostUsd === null
|
|
122
|
+
? null
|
|
123
|
+
: session.estimatedCostUsd + telemetry.estimatedCostUsd;
|
|
124
|
+
return {
|
|
125
|
+
requests: session.requests + 1,
|
|
126
|
+
promptTokens: session.promptTokens + telemetry.promptTokens,
|
|
127
|
+
cachedPromptTokens: session.cachedPromptTokens + telemetry.cachedPromptTokens,
|
|
128
|
+
cacheWriteTokens: session.cacheWriteTokens + telemetry.cacheWriteTokens,
|
|
129
|
+
responseTokens: session.responseTokens + telemetry.responseTokens,
|
|
130
|
+
totalTokens: session.totalTokens + telemetry.totalTokens,
|
|
131
|
+
estimatedCostUsd
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=tokenAccounting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenAccounting.js","sourceRoot":"","sources":["../../src/core/tokenAccounting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type ChatRole = "system" | "user" | "assistant";
|
|
2
|
+
export type ChatMessage = {
|
|
3
|
+
role: ChatRole;
|
|
4
|
+
content: string;
|
|
5
|
+
};
|
|
6
|
+
export type ModelProvider = "ollama" | "gemini" | "codex" | "openrouter" | "nvidia";
|
|
7
|
+
export type ReasoningEffort = "low" | "medium" | "high" | "xhigh";
|
|
8
|
+
export type ModelChatOptions = {
|
|
9
|
+
model: string;
|
|
10
|
+
messages: ChatMessage[];
|
|
11
|
+
formatJson?: boolean;
|
|
12
|
+
reasoningEffort?: ReasoningEffort;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
};
|
|
15
|
+
export type ModelChatResult = {
|
|
16
|
+
content: string;
|
|
17
|
+
telemetry: ModelTelemetry;
|
|
18
|
+
};
|
|
19
|
+
export type ModelClient = {
|
|
20
|
+
chat(options: ModelChatOptions): Promise<ModelChatResult>;
|
|
21
|
+
listModels(): Promise<string[]>;
|
|
22
|
+
};
|
|
23
|
+
export type AgentToolName = "list_files" | "read_file" | "search_text" | "inspect_document" | "write_file" | "run_shell";
|
|
24
|
+
export type AgentToolCall = {
|
|
25
|
+
name: AgentToolName;
|
|
26
|
+
arguments: Record<string, unknown>;
|
|
27
|
+
};
|
|
28
|
+
export type AgentResponse = {
|
|
29
|
+
action: "tools";
|
|
30
|
+
message: string;
|
|
31
|
+
tool_calls: AgentToolCall[];
|
|
32
|
+
} | {
|
|
33
|
+
action: "final";
|
|
34
|
+
message: string;
|
|
35
|
+
};
|
|
36
|
+
export type SubagentRole = "planner" | "reviewer" | "explorer";
|
|
37
|
+
export type AgentEvent = {
|
|
38
|
+
type: "status";
|
|
39
|
+
message: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: "metrics";
|
|
42
|
+
metrics: ModelTelemetry;
|
|
43
|
+
} | {
|
|
44
|
+
type: "assistant";
|
|
45
|
+
message: string;
|
|
46
|
+
} | {
|
|
47
|
+
type: "subagent";
|
|
48
|
+
role: SubagentRole;
|
|
49
|
+
message: string;
|
|
50
|
+
metrics: ModelTelemetry;
|
|
51
|
+
} | {
|
|
52
|
+
type: "tool";
|
|
53
|
+
name: AgentToolName;
|
|
54
|
+
summary: string;
|
|
55
|
+
ok: boolean;
|
|
56
|
+
} | {
|
|
57
|
+
type: "final";
|
|
58
|
+
message: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: "error";
|
|
61
|
+
message: string;
|
|
62
|
+
};
|
|
63
|
+
export type ToolResult = {
|
|
64
|
+
ok: boolean;
|
|
65
|
+
summary: string;
|
|
66
|
+
content: string;
|
|
67
|
+
};
|
|
68
|
+
export type ModelTelemetry = {
|
|
69
|
+
promptTokens: number;
|
|
70
|
+
cachedPromptTokens: number;
|
|
71
|
+
cacheWriteTokens: number;
|
|
72
|
+
responseTokens: number;
|
|
73
|
+
totalTokens: number;
|
|
74
|
+
evalTokensPerSecond: number | null;
|
|
75
|
+
promptDurationMs: number;
|
|
76
|
+
responseDurationMs: number;
|
|
77
|
+
totalDurationMs: number;
|
|
78
|
+
estimatedCostUsd: number | null;
|
|
79
|
+
tokenSource: "provider" | "estimated";
|
|
80
|
+
costSource: "api-pricing" | "local" | "unknown";
|
|
81
|
+
};
|
|
82
|
+
export type SessionTelemetry = {
|
|
83
|
+
requests: number;
|
|
84
|
+
promptTokens: number;
|
|
85
|
+
cachedPromptTokens: number;
|
|
86
|
+
cacheWriteTokens: number;
|
|
87
|
+
responseTokens: number;
|
|
88
|
+
totalTokens: number;
|
|
89
|
+
estimatedCostUsd: number | null;
|
|
90
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { AgentToolCall, ToolResult } from "./types.js";
|
|
2
|
+
export type WorkspaceToolsOptions = {
|
|
3
|
+
root: string;
|
|
4
|
+
allowWrite: boolean;
|
|
5
|
+
allowShell: boolean;
|
|
6
|
+
timeoutMs?: number;
|
|
7
|
+
signal?: AbortSignal;
|
|
8
|
+
};
|
|
9
|
+
export declare class WorkspaceTools {
|
|
10
|
+
readonly root: string;
|
|
11
|
+
private readonly rootRealPath;
|
|
12
|
+
private readonly allowWrite;
|
|
13
|
+
private readonly allowShell;
|
|
14
|
+
private readonly timeoutMs;
|
|
15
|
+
private readonly signal?;
|
|
16
|
+
constructor(options: WorkspaceToolsOptions);
|
|
17
|
+
execute(call: AgentToolCall): Promise<ToolResult>;
|
|
18
|
+
resolveInsideWorkspace(requestedPath: string): string;
|
|
19
|
+
normalizeWorkspaceRelativePath(requestedPath: string): string;
|
|
20
|
+
private listFiles;
|
|
21
|
+
private readFile;
|
|
22
|
+
private inspectDocument;
|
|
23
|
+
private searchText;
|
|
24
|
+
private writeFile;
|
|
25
|
+
private runShell;
|
|
26
|
+
private resolveReadPath;
|
|
27
|
+
private resolveWritePath;
|
|
28
|
+
}
|