@doist/doistbot-cli 1.0.4 → 1.0.6
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/dist/actions/auth.js +62 -28
- package/dist/actions/doctor.js +16 -11
- package/dist/actions/review.js +77 -11
- package/dist/auth.js +53 -8
- package/dist/config.js +40 -6
- package/dist/env.js +1 -1
- package/dist/file-store.js +1 -1
- package/dist/runtime.js +1 -1
- package/dist/terminal.js +2 -7
- package/package.json +7 -7
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.d.ts +5 -23
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.js +9 -85
- package/sandbox/dist/core/bot-logins.js +0 -13
- package/sandbox/dist/core/check-run.js +16 -0
- package/sandbox/dist/core/datadog-metrics.js +6 -54
- package/sandbox/dist/core/logger-span-processor.js +78 -0
- package/sandbox/dist/core/pi.js +270 -186
- package/sandbox/dist/core/tracing-setup.js +15 -0
- package/sandbox/dist/core/tracing.js +71 -0
- package/sandbox/dist/main.js +4 -1
- package/sandbox/dist/providers/config.js +1 -1
- package/sandbox/dist/providers/credentials.js +40 -0
- package/sandbox/dist/providers/gemini.js +8 -10
- package/sandbox/dist/providers/helpers.js +18 -38
- package/sandbox/dist/providers/index.js +4 -3
- package/sandbox/dist/providers/openai.js +1 -8
- package/sandbox/dist/providers/openrouter-pi.js +25 -0
- package/sandbox/dist/providers/pi-review.js +28 -55
- package/sandbox/dist/providers/schemas.js +0 -39
- package/sandbox/dist/tasks/chat/chat.js +162 -101
- package/sandbox/dist/tasks/issue-fix-retry/fix-retry.js +210 -185
- package/sandbox/dist/tasks/issue-summarize/context.js +1 -1
- package/sandbox/dist/tasks/issue-summarize/model.js +4 -2
- package/sandbox/dist/tasks/issue-summarize/summarize.js +1 -1
- package/sandbox/dist/tasks/issue-triage/clone.js +40 -0
- package/sandbox/dist/tasks/issue-triage/context.js +25 -2
- package/sandbox/dist/tasks/issue-triage/fix-attempt.js +31 -8
- package/sandbox/dist/tasks/issue-triage/fix-dispatch.js +154 -101
- package/sandbox/dist/tasks/issue-triage/fix-loop/runners.js +15 -6
- package/sandbox/dist/tasks/issue-triage/model.js +120 -36
- package/sandbox/dist/tasks/issue-triage/output.js +27 -0
- package/sandbox/dist/tasks/issue-triage/pr-creator.js +3 -2
- package/sandbox/dist/tasks/issue-triage/resolution.js +26 -0
- package/sandbox/dist/tasks/issue-triage/routing.js +9 -9
- package/sandbox/dist/tasks/issue-triage/side-effects.js +9 -0
- package/sandbox/dist/tasks/issue-triage/triage.js +288 -418
- package/sandbox/dist/tasks/persist-logs/dd-proxy.js +2 -2
- package/sandbox/dist/tasks/persist-logs/llm-query-planner.js +1 -1
- package/sandbox/dist/tasks/persist-logs/persist-logs.js +1 -1
- package/sandbox/dist/tasks/review/check-run.js +2 -1
- package/sandbox/dist/tasks/review/conversation-context.js +15 -8
- package/sandbox/dist/tasks/review/engines/dedupe.js +19 -9
- package/sandbox/dist/tasks/review/engines/multi-focus.js +62 -36
- package/sandbox/dist/tasks/review/engines/shared.js +15 -61
- package/sandbox/dist/tasks/review/github-comment-format.js +64 -10
- package/sandbox/dist/tasks/review/local.js +2 -29
- package/sandbox/dist/tasks/review/prompt.js +2 -49
- package/sandbox/dist/tasks/review/review-summary.js +120 -0
- package/sandbox/dist/tasks/review/review.js +49 -69
- package/sandbox/dist/tasks/review/runner.js +57 -52
- package/sandbox/dist/tasks/review/summary-model.js +172 -16
- package/sandbox/dist/tasks/review/thinking-level.js +4 -2
- package/sandbox/node_modules/doistbot-repo-config/dist/index.d.ts +5 -23
- package/sandbox/node_modules/doistbot-repo-config/dist/index.js +9 -85
- package/sandbox/_node_modules/doistbot-repo-config/dist/review-engine.d.ts +0 -11
- package/sandbox/_node_modules/doistbot-repo-config/dist/review-engine.js +0 -41
- package/sandbox/dist/providers/anthropic.js +0 -30
- package/sandbox/dist/tasks/review/author-format.js +0 -14
- package/sandbox/dist/tasks/review/author-profile.js +0 -67
- package/sandbox/dist/tasks/review/council/normalizer.js +0 -104
- package/sandbox/dist/tasks/review/council/orchestrator.js +0 -194
- package/sandbox/dist/tasks/review/council/provider-health.js +0 -31
- package/sandbox/dist/tasks/review/council/summary.js +0 -192
- package/sandbox/dist/tasks/review/council/types.js +0 -4
- package/sandbox/dist/tasks/review/council/voter.js +0 -384
- package/sandbox/dist/tasks/review/engines/council.js +0 -126
- package/sandbox/dist/tasks/review/engines/index.js +0 -12
- package/sandbox/dist/tasks/review/review-engine.js +0 -5
- package/sandbox/node_modules/doistbot-repo-config/dist/review-engine.d.ts +0 -11
- package/sandbox/node_modules/doistbot-repo-config/dist/review-engine.js +0 -41
- package/sandbox/review-prompt.md +0 -209
- package/sandbox/vote-prompt.md +0 -69
package/sandbox/dist/core/pi.js
CHANGED
|
@@ -3,41 +3,53 @@ import { mkdtempSync, rmSync, writeFileSync } from 'fs';
|
|
|
3
3
|
import { tmpdir } from 'os';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
const PI_PROVIDER_BY_DOISTBOT_PROVIDER = {
|
|
6
|
+
export const PI_PROVIDER_BY_DOISTBOT_PROVIDER = {
|
|
7
|
+
deepseek: 'openrouter',
|
|
7
8
|
gemini: 'google',
|
|
9
|
+
zai: 'openrouter',
|
|
8
10
|
openai: 'openai',
|
|
9
|
-
anthropic: 'anthropic',
|
|
10
|
-
};
|
|
11
|
-
const PI_API_KEY_ENV_VAR_BY_PROVIDER = {
|
|
12
|
-
gemini: 'GEMINI_API_KEY',
|
|
13
|
-
openai: 'OPENAI_API_KEY',
|
|
14
|
-
anthropic: 'ANTHROPIC_API_KEY',
|
|
15
11
|
};
|
|
16
12
|
export const DEFAULT_PI_MODELS = {
|
|
13
|
+
deepseek: 'deepseek-v4-pro',
|
|
17
14
|
gemini: 'gemini-3.1-pro-preview',
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
zai: 'glm5.2',
|
|
16
|
+
openai: 'gpt-5.5',
|
|
17
|
+
};
|
|
18
|
+
export const DEFAULT_OPENROUTER_MODELS = {
|
|
19
|
+
deepseek: 'deepseek/deepseek-v4-pro',
|
|
20
|
+
gemini: 'google/gemini-3.1-pro-preview',
|
|
21
|
+
zai: 'z-ai/glm-5.2',
|
|
20
22
|
};
|
|
21
23
|
export const MAX_PI_THINKING_BY_PROVIDER = {
|
|
24
|
+
deepseek: 'high',
|
|
22
25
|
gemini: 'high',
|
|
26
|
+
zai: 'high',
|
|
23
27
|
openai: 'xhigh',
|
|
24
|
-
anthropic: 'xhigh',
|
|
25
28
|
};
|
|
26
|
-
const
|
|
29
|
+
const PI_API_KEY_ENV_VAR_BY_PI_PROVIDER = {
|
|
30
|
+
google: 'GEMINI_API_KEY',
|
|
31
|
+
openai: 'OPENAI_API_KEY',
|
|
32
|
+
'openai-codex': 'OPENAI_API_KEY',
|
|
33
|
+
openrouter: 'OPENROUTER_API_KEY',
|
|
34
|
+
};
|
|
35
|
+
const PI_PROVIDER_IDS = new Set(['google', 'openai', 'openai-codex', 'openrouter']);
|
|
27
36
|
export const PI_AUTH_API_KEY_PREFIX = 'pi-auth://';
|
|
28
37
|
const DOISTBOT_PI_SYSTEM_PROMPT = 'You are running inside doistbot in a non-interactive environment. Inspect the repository, configuration, documentation, and relevant history when they can improve accuracy. Use available tools proactively to gather evidence, avoid guessing when the workspace can answer the question, and follow the task prompt exactly.';
|
|
29
38
|
const PI_PROGRESS_HEARTBEAT_MS = 30_000;
|
|
39
|
+
const OPENROUTER_SESSION_ID_ENV_VAR = 'DOISTBOT_OPENROUTER_SESSION_ID';
|
|
40
|
+
const OPENROUTER_SESSION_ID_MAX_LENGTH = 256;
|
|
30
41
|
const PI_TOOLS_BY_PRESET = {
|
|
31
42
|
'read-only': ['read', 'grep', 'find', 'ls'],
|
|
32
43
|
'workspace-inspection': ['read', 'bash', 'grep', 'find', 'ls'],
|
|
33
44
|
'shell-focused': ['read', 'bash', 'ls'],
|
|
34
45
|
};
|
|
35
46
|
function resolvePiCliPath() {
|
|
36
|
-
const piPackageEntryPath = fileURLToPath(import.meta.resolve('@
|
|
47
|
+
const piPackageEntryPath = fileURLToPath(import.meta.resolve('@earendil-works/pi-coding-agent'));
|
|
37
48
|
return path.join(path.dirname(piPackageEntryPath), 'cli.js');
|
|
38
49
|
}
|
|
39
50
|
const PI_CLI_PATH = resolvePiCliPath();
|
|
40
51
|
let cachedNodeExecutable;
|
|
52
|
+
let cachedOpenRouterSessionExtensionPath;
|
|
41
53
|
export function resolveNodeExecutable({ execPath = process.execPath, argv0 = process.argv0, canExecute = canExecuteNode, } = {}) {
|
|
42
54
|
const canUseCachedExecutable = execPath === process.execPath && argv0 === process.argv0 && canExecute === canExecuteNode;
|
|
43
55
|
if (canUseCachedExecutable && cachedNodeExecutable) {
|
|
@@ -62,10 +74,12 @@ function canExecuteNode(candidate) {
|
|
|
62
74
|
}
|
|
63
75
|
function getPiModelSpec({ provider, piProviderId, modelName, thinkingLevel, }) {
|
|
64
76
|
const providerId = piProviderId ?? PI_PROVIDER_BY_DOISTBOT_PROVIDER[provider];
|
|
65
|
-
const resolvedModelName = modelName ??
|
|
77
|
+
const resolvedModelName = modelName ??
|
|
78
|
+
(providerId === 'openrouter' ? DEFAULT_OPENROUTER_MODELS[provider] : undefined) ??
|
|
79
|
+
DEFAULT_PI_MODELS[provider];
|
|
66
80
|
return `${providerId}/${resolvedModelName}:${thinkingLevel}`;
|
|
67
81
|
}
|
|
68
|
-
function parsePiAuthProviderId(apiKey) {
|
|
82
|
+
export function parsePiAuthProviderId(apiKey) {
|
|
69
83
|
if (!apiKey.startsWith(PI_AUTH_API_KEY_PREFIX)) {
|
|
70
84
|
return undefined;
|
|
71
85
|
}
|
|
@@ -110,6 +124,54 @@ function createImageArgs(images) {
|
|
|
110
124
|
},
|
|
111
125
|
};
|
|
112
126
|
}
|
|
127
|
+
function normalizeOpenRouterSessionId(sessionId) {
|
|
128
|
+
const trimmed = sessionId?.trim();
|
|
129
|
+
if (!trimmed) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
return trimmed.slice(0, OPENROUTER_SESSION_ID_MAX_LENGTH);
|
|
133
|
+
}
|
|
134
|
+
function getOpenRouterSessionExtensionPath() {
|
|
135
|
+
if (cachedOpenRouterSessionExtensionPath) {
|
|
136
|
+
return cachedOpenRouterSessionExtensionPath;
|
|
137
|
+
}
|
|
138
|
+
const tempDir = mkdtempSync(path.join(tmpdir(), 'doistbot-pi-openrouter-session-'));
|
|
139
|
+
const extensionPath = path.join(tempDir, 'openrouter-session-id.mjs');
|
|
140
|
+
writeFileSync(extensionPath, `export default function(pi) {
|
|
141
|
+
const sessionId = process.env[${JSON.stringify(OPENROUTER_SESSION_ID_ENV_VAR)}]
|
|
142
|
+
if (!sessionId) return
|
|
143
|
+
|
|
144
|
+
pi.on("before_provider_request", (event) => {
|
|
145
|
+
const payload = event.payload
|
|
146
|
+
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return undefined
|
|
147
|
+
|
|
148
|
+
return { ...payload, session_id: sessionId }
|
|
149
|
+
})
|
|
150
|
+
}
|
|
151
|
+
`);
|
|
152
|
+
process.once('exit', () => {
|
|
153
|
+
rmSync(tempDir, { force: true, recursive: true });
|
|
154
|
+
});
|
|
155
|
+
cachedOpenRouterSessionExtensionPath = extensionPath;
|
|
156
|
+
return extensionPath;
|
|
157
|
+
}
|
|
158
|
+
function createOpenRouterSessionExtension({ piProviderId, sessionId, }) {
|
|
159
|
+
const normalizedSessionId = normalizeOpenRouterSessionId(sessionId);
|
|
160
|
+
if (piProviderId !== 'openrouter' || !normalizedSessionId) {
|
|
161
|
+
return {
|
|
162
|
+
args: [],
|
|
163
|
+
extraEnv: {},
|
|
164
|
+
cleanup() { },
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
args: ['--extension', getOpenRouterSessionExtensionPath()],
|
|
169
|
+
extraEnv: {
|
|
170
|
+
[OPENROUTER_SESSION_ID_ENV_VAR]: normalizedSessionId,
|
|
171
|
+
},
|
|
172
|
+
cleanup() { },
|
|
173
|
+
};
|
|
174
|
+
}
|
|
113
175
|
function extractTextSegments(content) {
|
|
114
176
|
if (!Array.isArray(content)) {
|
|
115
177
|
return [];
|
|
@@ -176,7 +238,8 @@ function maybeEmitProgress({ event, onProgress, lastThinkingAt, lastRespondingAt
|
|
|
176
238
|
case 'tool_execution_start': {
|
|
177
239
|
const toolName = event.toolName;
|
|
178
240
|
if (typeof toolName === 'string' && toolName.length > 0) {
|
|
179
|
-
|
|
241
|
+
const toolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
242
|
+
onProgress({ type: 'tool_start', toolName, ...(toolCallId ? { toolCallId } : {}) });
|
|
180
243
|
}
|
|
181
244
|
return;
|
|
182
245
|
}
|
|
@@ -185,23 +248,30 @@ function maybeEmitProgress({ event, onProgress, lastThinkingAt, lastRespondingAt
|
|
|
185
248
|
if (typeof toolName !== 'string' || toolName.length === 0) {
|
|
186
249
|
return;
|
|
187
250
|
}
|
|
188
|
-
const
|
|
251
|
+
const rawToolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
252
|
+
const toolCallId = rawToolCallId ?? toolName;
|
|
189
253
|
const now = Date.now();
|
|
190
254
|
const lastUpdateAt = lastToolUpdateAt.get(toolCallId) ?? 0;
|
|
191
255
|
if (now - lastUpdateAt >= PI_PROGRESS_HEARTBEAT_MS) {
|
|
192
256
|
lastToolUpdateAt.set(toolCallId, now);
|
|
193
|
-
onProgress({
|
|
257
|
+
onProgress({
|
|
258
|
+
type: 'tool_update',
|
|
259
|
+
toolName,
|
|
260
|
+
...(rawToolCallId ? { toolCallId: rawToolCallId } : {}),
|
|
261
|
+
});
|
|
194
262
|
}
|
|
195
263
|
return;
|
|
196
264
|
}
|
|
197
265
|
case 'tool_execution_end': {
|
|
198
266
|
const toolName = event.toolName;
|
|
199
267
|
if (typeof toolName === 'string' && toolName.length > 0) {
|
|
200
|
-
const
|
|
268
|
+
const rawToolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
269
|
+
const toolCallId = rawToolCallId ?? toolName;
|
|
201
270
|
const startedAt = toolStartedAt.get(toolCallId);
|
|
202
271
|
onProgress({
|
|
203
272
|
type: 'tool_end',
|
|
204
273
|
toolName,
|
|
274
|
+
...(rawToolCallId ? { toolCallId: rawToolCallId } : {}),
|
|
205
275
|
isError: event.isError === true,
|
|
206
276
|
durationMs: startedAt == null ? undefined : Math.max(0, Date.now() - startedAt),
|
|
207
277
|
});
|
|
@@ -217,18 +287,23 @@ function toSortedToolUsage(toolUsageByName) {
|
|
|
217
287
|
return left.toolName.localeCompare(right.toolName);
|
|
218
288
|
});
|
|
219
289
|
}
|
|
220
|
-
export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv, capabilityPreset = 'workspace-inspection', thinkingLevel, systemPrompt = DOISTBOT_PI_SYSTEM_PROMPT, onProgress, }) {
|
|
290
|
+
export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv, capabilityPreset = 'workspace-inspection', thinkingLevel, systemPrompt = DOISTBOT_PI_SYSTEM_PROMPT, noContextFiles = false, openRouterSessionId, onProgress, }) {
|
|
221
291
|
return {
|
|
222
292
|
async invoke({ prompt, modelName, timeoutMs = 5 * 60 * 1000, images, }) {
|
|
223
293
|
const startedAt = Date.now();
|
|
224
294
|
const piAuthProviderId = parsePiAuthProviderId(apiKey);
|
|
295
|
+
const resolvedPiProviderId = piAuthProviderId ?? piProviderId ?? PI_PROVIDER_BY_DOISTBOT_PROVIDER[provider];
|
|
225
296
|
const modelSpec = getPiModelSpec({
|
|
226
297
|
provider,
|
|
227
|
-
piProviderId:
|
|
298
|
+
piProviderId: resolvedPiProviderId,
|
|
228
299
|
modelName,
|
|
229
300
|
thinkingLevel: thinkingLevel ?? MAX_PI_THINKING_BY_PROVIDER[provider],
|
|
230
301
|
});
|
|
231
302
|
const { imageArgs, cleanup } = createImageArgs(images);
|
|
303
|
+
const openRouterSessionExtension = createOpenRouterSessionExtension({
|
|
304
|
+
piProviderId: resolvedPiProviderId,
|
|
305
|
+
sessionId: openRouterSessionId,
|
|
306
|
+
});
|
|
232
307
|
const tools = PI_TOOLS_BY_PRESET[capabilityPreset].join(',');
|
|
233
308
|
const args = [
|
|
234
309
|
PI_CLI_PATH,
|
|
@@ -238,12 +313,14 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
238
313
|
'--no-extensions',
|
|
239
314
|
'--no-skills',
|
|
240
315
|
'--no-prompt-templates',
|
|
316
|
+
...(noContextFiles ? ['--no-context-files'] : []),
|
|
241
317
|
'--model',
|
|
242
318
|
modelSpec,
|
|
243
319
|
'--tools',
|
|
244
320
|
tools,
|
|
245
321
|
'--system-prompt',
|
|
246
322
|
systemPrompt,
|
|
323
|
+
...openRouterSessionExtension.args,
|
|
247
324
|
...imageArgs,
|
|
248
325
|
];
|
|
249
326
|
const childEnv = {
|
|
@@ -252,8 +329,9 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
252
329
|
PATH: process.env.PATH,
|
|
253
330
|
GIT_TERMINAL_PROMPT: '0',
|
|
254
331
|
...extraEnv,
|
|
332
|
+
...openRouterSessionExtension.extraEnv,
|
|
255
333
|
};
|
|
256
|
-
const apiKeyEnvVar =
|
|
334
|
+
const apiKeyEnvVar = PI_API_KEY_ENV_VAR_BY_PI_PROVIDER[resolvedPiProviderId];
|
|
257
335
|
if (piAuthProviderId) {
|
|
258
336
|
delete childEnv[apiKeyEnvVar];
|
|
259
337
|
}
|
|
@@ -261,205 +339,209 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
261
339
|
childEnv[apiKeyEnvVar] = apiKey;
|
|
262
340
|
}
|
|
263
341
|
delete childEnv.GITHUB_TOKEN;
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
}
|
|
291
|
-
try {
|
|
292
|
-
const parsed = JSON.parse(trimmed);
|
|
293
|
-
if (parsed.type === 'session') {
|
|
294
|
-
sessionHeaderSeen = true;
|
|
342
|
+
try {
|
|
343
|
+
const child = spawn(resolveNodeExecutable(), args, {
|
|
344
|
+
cwd,
|
|
345
|
+
env: childEnv,
|
|
346
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
347
|
+
});
|
|
348
|
+
child.stdin?.on('error', () => { });
|
|
349
|
+
child.stdin?.end(prompt);
|
|
350
|
+
let timedOut = false;
|
|
351
|
+
let stdoutBuffer = '';
|
|
352
|
+
let stderr = '';
|
|
353
|
+
let lastAssistantText = '';
|
|
354
|
+
let lastAssistantError = '';
|
|
355
|
+
let sessionHeaderSeen = false;
|
|
356
|
+
const lastThinkingAt = { value: 0 };
|
|
357
|
+
const lastRespondingAt = { value: 0 };
|
|
358
|
+
const lastToolUpdateAt = new Map();
|
|
359
|
+
const toolStartedAt = new Map();
|
|
360
|
+
const toolUsageByName = new Map();
|
|
361
|
+
function parseStdout(chunk) {
|
|
362
|
+
stdoutBuffer += chunk;
|
|
363
|
+
const lines = stdoutBuffer.split(/\r?\n/);
|
|
364
|
+
stdoutBuffer = lines.pop() ?? '';
|
|
365
|
+
for (const line of lines) {
|
|
366
|
+
const trimmed = line.trim();
|
|
367
|
+
if (!trimmed) {
|
|
295
368
|
continue;
|
|
296
369
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
if (
|
|
314
|
-
|
|
370
|
+
try {
|
|
371
|
+
const parsed = JSON.parse(trimmed);
|
|
372
|
+
if (parsed.type === 'session') {
|
|
373
|
+
sessionHeaderSeen = true;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
maybeEmitProgress({
|
|
377
|
+
event: parsed,
|
|
378
|
+
onProgress,
|
|
379
|
+
lastThinkingAt,
|
|
380
|
+
lastRespondingAt,
|
|
381
|
+
lastToolUpdateAt,
|
|
382
|
+
toolStartedAt,
|
|
383
|
+
});
|
|
384
|
+
if (parsed.type === 'tool_execution_start') {
|
|
385
|
+
const toolName = typeof parsed.toolName === 'string' ? parsed.toolName : null;
|
|
386
|
+
if (toolName) {
|
|
387
|
+
const toolCallId = typeof parsed.toolCallId === 'string'
|
|
388
|
+
? parsed.toolCallId
|
|
389
|
+
: toolName;
|
|
390
|
+
toolStartedAt.set(toolCallId, Date.now());
|
|
391
|
+
const current = toolUsageByName.get(toolName);
|
|
392
|
+
if (current) {
|
|
393
|
+
current.calls += 1;
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
toolUsageByName.set(toolName, {
|
|
397
|
+
toolName,
|
|
398
|
+
calls: 1,
|
|
399
|
+
totalDurationMs: 0,
|
|
400
|
+
errorCount: 0,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
315
403
|
}
|
|
316
|
-
|
|
317
|
-
|
|
404
|
+
}
|
|
405
|
+
if (parsed.type === 'tool_execution_end') {
|
|
406
|
+
const toolName = typeof parsed.toolName === 'string' ? parsed.toolName : null;
|
|
407
|
+
if (toolName) {
|
|
408
|
+
const current = toolUsageByName.get(toolName) ?? {
|
|
318
409
|
toolName,
|
|
319
|
-
calls:
|
|
410
|
+
calls: 0,
|
|
320
411
|
totalDurationMs: 0,
|
|
321
412
|
errorCount: 0,
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
const toolCallId = typeof parsed.toolCallId === 'string'
|
|
336
|
-
? parsed.toolCallId
|
|
337
|
-
: toolName;
|
|
338
|
-
const startedAt = toolStartedAt.get(toolCallId);
|
|
339
|
-
toolStartedAt.delete(toolCallId);
|
|
340
|
-
if (startedAt != null) {
|
|
341
|
-
current.totalDurationMs += Math.max(0, Date.now() - startedAt);
|
|
413
|
+
};
|
|
414
|
+
const toolCallId = typeof parsed.toolCallId === 'string'
|
|
415
|
+
? parsed.toolCallId
|
|
416
|
+
: toolName;
|
|
417
|
+
const startedAt = toolStartedAt.get(toolCallId);
|
|
418
|
+
toolStartedAt.delete(toolCallId);
|
|
419
|
+
if (startedAt != null) {
|
|
420
|
+
current.totalDurationMs += Math.max(0, Date.now() - startedAt);
|
|
421
|
+
}
|
|
422
|
+
if (parsed.isError === true) {
|
|
423
|
+
current.errorCount += 1;
|
|
424
|
+
}
|
|
425
|
+
toolUsageByName.set(toolName, current);
|
|
342
426
|
}
|
|
343
|
-
if (parsed.isError === true) {
|
|
344
|
-
current.errorCount += 1;
|
|
345
|
-
}
|
|
346
|
-
toolUsageByName.set(toolName, current);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
if (parsed.type === 'message_end') {
|
|
350
|
-
const result = extractAssistantMessageResult(parsed.message);
|
|
351
|
-
if (result?.text) {
|
|
352
|
-
lastAssistantText = result.text;
|
|
353
|
-
}
|
|
354
|
-
else if (result?.error) {
|
|
355
|
-
lastAssistantError = result.error;
|
|
356
427
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (parsed.type === 'agent_end' && Array.isArray(parsed.messages)) {
|
|
360
|
-
for (const message of parsed.messages) {
|
|
361
|
-
const result = extractAssistantMessageResult(message);
|
|
428
|
+
if (parsed.type === 'message_end') {
|
|
429
|
+
const result = extractAssistantMessageResult(parsed.message);
|
|
362
430
|
if (result?.text) {
|
|
363
431
|
lastAssistantText = result.text;
|
|
364
432
|
}
|
|
365
433
|
else if (result?.error) {
|
|
366
434
|
lastAssistantError = result.error;
|
|
367
435
|
}
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (parsed.type === 'agent_end' && Array.isArray(parsed.messages)) {
|
|
439
|
+
for (const message of parsed.messages) {
|
|
440
|
+
const result = extractAssistantMessageResult(message);
|
|
441
|
+
if (result?.text) {
|
|
442
|
+
lastAssistantText = result.text;
|
|
443
|
+
}
|
|
444
|
+
else if (result?.error) {
|
|
445
|
+
lastAssistantError = result.error;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
368
448
|
}
|
|
369
449
|
}
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
450
|
+
catch {
|
|
451
|
+
if (!sessionHeaderSeen && !lastAssistantText) {
|
|
452
|
+
lastAssistantText = trimmed;
|
|
453
|
+
}
|
|
374
454
|
}
|
|
375
455
|
}
|
|
376
456
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
parseStdout(chunk);
|
|
386
|
-
});
|
|
387
|
-
child.stderr.setEncoding('utf8');
|
|
388
|
-
child.stderr.on('data', (chunk) => {
|
|
389
|
-
stderr += chunk;
|
|
390
|
-
});
|
|
391
|
-
child.on('error', (error) => {
|
|
392
|
-
clearTimeout(timeoutHandle);
|
|
393
|
-
resolve({
|
|
394
|
-
ok: false,
|
|
395
|
-
error: error.message,
|
|
396
|
-
durationMs: Date.now() - startedAt,
|
|
397
|
-
timedOut,
|
|
398
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
457
|
+
return await new Promise((resolve) => {
|
|
458
|
+
const timeoutHandle = setTimeout(() => {
|
|
459
|
+
timedOut = true;
|
|
460
|
+
child.kill('SIGTERM');
|
|
461
|
+
}, timeoutMs);
|
|
462
|
+
child.stdout.setEncoding('utf8');
|
|
463
|
+
child.stdout.on('data', (chunk) => {
|
|
464
|
+
parseStdout(chunk);
|
|
399
465
|
});
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
if (timedOut) {
|
|
407
|
-
resolve({
|
|
408
|
-
ok: false,
|
|
409
|
-
error: `Pi request timed out after ${timeoutMs}ms`,
|
|
410
|
-
durationMs: Date.now() - startedAt,
|
|
411
|
-
timedOut: true,
|
|
412
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
413
|
-
});
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
if (signal) {
|
|
417
|
-
resolve({
|
|
418
|
-
ok: false,
|
|
419
|
-
error: `Pi process terminated with signal ${signal}`,
|
|
420
|
-
durationMs: Date.now() - startedAt,
|
|
421
|
-
timedOut: false,
|
|
422
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
423
|
-
});
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
if (code !== 0) {
|
|
466
|
+
child.stderr.setEncoding('utf8');
|
|
467
|
+
child.stderr.on('data', (chunk) => {
|
|
468
|
+
stderr += chunk;
|
|
469
|
+
});
|
|
470
|
+
child.on('error', (error) => {
|
|
471
|
+
clearTimeout(timeoutHandle);
|
|
427
472
|
resolve({
|
|
428
473
|
ok: false,
|
|
429
|
-
error:
|
|
474
|
+
error: error.message,
|
|
430
475
|
durationMs: Date.now() - startedAt,
|
|
431
|
-
timedOut
|
|
476
|
+
timedOut,
|
|
432
477
|
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
433
478
|
});
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
479
|
+
});
|
|
480
|
+
child.on('close', (code, signal) => {
|
|
481
|
+
clearTimeout(timeoutHandle);
|
|
482
|
+
if (stdoutBuffer.trim()) {
|
|
483
|
+
parseStdout('\n');
|
|
484
|
+
}
|
|
485
|
+
if (timedOut) {
|
|
486
|
+
resolve({
|
|
487
|
+
ok: false,
|
|
488
|
+
error: `Pi request timed out after ${timeoutMs}ms`,
|
|
489
|
+
durationMs: Date.now() - startedAt,
|
|
490
|
+
timedOut: true,
|
|
491
|
+
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
492
|
+
});
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
if (signal) {
|
|
496
|
+
resolve({
|
|
497
|
+
ok: false,
|
|
498
|
+
error: `Pi process terminated with signal ${signal}`,
|
|
499
|
+
durationMs: Date.now() - startedAt,
|
|
500
|
+
timedOut: false,
|
|
501
|
+
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
502
|
+
});
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
if (code !== 0) {
|
|
506
|
+
resolve({
|
|
507
|
+
ok: false,
|
|
508
|
+
error: stderr.trim() || `Pi exited with status ${String(code)}`,
|
|
509
|
+
durationMs: Date.now() - startedAt,
|
|
510
|
+
timedOut: false,
|
|
511
|
+
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
512
|
+
});
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
const output = lastAssistantText.trim();
|
|
516
|
+
if (!output) {
|
|
517
|
+
resolve({
|
|
518
|
+
ok: false,
|
|
519
|
+
error: lastAssistantError ||
|
|
520
|
+
stderr.trim() ||
|
|
521
|
+
'Pi session did not produce an assistant response',
|
|
522
|
+
durationMs: Date.now() - startedAt,
|
|
523
|
+
timedOut: false,
|
|
524
|
+
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
525
|
+
});
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
438
528
|
resolve({
|
|
439
|
-
ok:
|
|
440
|
-
|
|
441
|
-
stderr.trim() ||
|
|
442
|
-
'Pi session did not produce an assistant response',
|
|
529
|
+
ok: true,
|
|
530
|
+
output,
|
|
443
531
|
durationMs: Date.now() - startedAt,
|
|
444
|
-
timedOut: false,
|
|
445
532
|
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
446
533
|
});
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
resolve({
|
|
450
|
-
ok: true,
|
|
451
|
-
output,
|
|
452
|
-
durationMs: Date.now() - startedAt,
|
|
453
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
454
534
|
});
|
|
455
535
|
});
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
cleanup();
|
|
539
|
+
openRouterSessionExtension.cleanup();
|
|
540
|
+
}
|
|
459
541
|
},
|
|
460
542
|
};
|
|
461
543
|
}
|
|
462
|
-
export async function invokePiPrompt({ provider, apiKey, piProviderId, prompt, cwd, modelName, timeoutMs = 5 * 60 * 1000, extraEnv, images, capabilityPreset, thinkingLevel, systemPrompt, onProgress, }) {
|
|
544
|
+
export async function invokePiPrompt({ provider, apiKey, piProviderId, prompt, cwd, modelName, timeoutMs = 5 * 60 * 1000, extraEnv, images, capabilityPreset, thinkingLevel, systemPrompt, noContextFiles, openRouterSessionId, onProgress, }) {
|
|
463
545
|
return await createPiInvoker({
|
|
464
546
|
provider,
|
|
465
547
|
apiKey,
|
|
@@ -469,6 +551,8 @@ export async function invokePiPrompt({ provider, apiKey, piProviderId, prompt, c
|
|
|
469
551
|
capabilityPreset,
|
|
470
552
|
thinkingLevel,
|
|
471
553
|
systemPrompt,
|
|
554
|
+
noContextFiles,
|
|
555
|
+
openRouterSessionId,
|
|
472
556
|
onProgress,
|
|
473
557
|
}).invoke({
|
|
474
558
|
prompt,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { context, trace } from '@opentelemetry/api';
|
|
2
|
+
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
|
|
3
|
+
import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base';
|
|
4
|
+
import { LoggerSpanProcessor } from './logger-span-processor.js';
|
|
5
|
+
let registered = false;
|
|
6
|
+
export function setupTracing() {
|
|
7
|
+
if (registered)
|
|
8
|
+
return;
|
|
9
|
+
registered = true;
|
|
10
|
+
context.setGlobalContextManager(new AsyncLocalStorageContextManager());
|
|
11
|
+
const provider = new BasicTracerProvider({
|
|
12
|
+
spanProcessors: [new LoggerSpanProcessor()],
|
|
13
|
+
});
|
|
14
|
+
trace.setGlobalTracerProvider(provider);
|
|
15
|
+
}
|