@doist/doistbot-cli 1.0.5 → 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 +55 -21
- package/dist/actions/doctor.js +13 -6
- package/dist/actions/review.js +75 -10
- package/dist/auth.js +51 -6
- package/dist/config.js +34 -5
- package/dist/env.js +1 -0
- package/dist/runtime.js +1 -1
- package/dist/terminal.js +2 -0
- package/package.json +4 -4
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.d.ts +4 -8
- package/sandbox/_node_modules/doistbot-repo-config/dist/index.js +8 -20
- package/sandbox/dist/core/check-run.js +16 -0
- package/sandbox/dist/core/datadog-metrics.js +6 -52
- package/sandbox/dist/core/logger-span-processor.js +78 -0
- package/sandbox/dist/core/pi.js +257 -176
- 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/credentials.js +12 -2
- package/sandbox/dist/providers/gemini.js +5 -1
- package/sandbox/dist/providers/helpers.js +12 -17
- package/sandbox/dist/providers/index.js +4 -0
- package/sandbox/dist/providers/openrouter-pi.js +25 -0
- package/sandbox/dist/providers/pi-review.js +12 -1
- package/sandbox/dist/tasks/chat/chat.js +161 -101
- package/sandbox/dist/tasks/issue-fix-retry/fix-retry.js +210 -185
- package/sandbox/dist/tasks/issue-summarize/model.js +1 -1
- package/sandbox/dist/tasks/issue-triage/clone.js +40 -0
- package/sandbox/dist/tasks/issue-triage/context.js +25 -1
- 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/review/check-run.js +2 -1
- package/sandbox/dist/tasks/review/conversation-context.js +2 -1
- package/sandbox/dist/tasks/review/engines/dedupe.js +5 -4
- package/sandbox/dist/tasks/review/engines/multi-focus.js +40 -9
- package/sandbox/dist/tasks/review/engines/shared.js +8 -6
- package/sandbox/dist/tasks/review/github-comment-format.js +64 -10
- package/sandbox/dist/tasks/review/local.js +2 -4
- package/sandbox/dist/tasks/review/review-summary.js +45 -7
- package/sandbox/dist/tasks/review/review.js +38 -24
- package/sandbox/dist/tasks/review/runner.js +23 -2
- package/sandbox/dist/tasks/review/summary-model.js +164 -17
- package/sandbox/dist/tasks/review/thinking-level.js +4 -0
- package/sandbox/node_modules/doistbot-repo-config/dist/index.d.ts +4 -8
- package/sandbox/node_modules/doistbot-repo-config/dist/index.js +8 -20
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 = {
|
|
7
|
-
|
|
6
|
+
export const PI_PROVIDER_BY_DOISTBOT_PROVIDER = {
|
|
7
|
+
deepseek: 'openrouter',
|
|
8
|
+
gemini: 'google',
|
|
9
|
+
zai: 'openrouter',
|
|
8
10
|
openai: 'openai',
|
|
9
11
|
};
|
|
10
12
|
export const DEFAULT_PI_MODELS = {
|
|
13
|
+
deepseek: 'deepseek-v4-pro',
|
|
11
14
|
gemini: 'gemini-3.1-pro-preview',
|
|
12
|
-
|
|
15
|
+
zai: 'glm5.2',
|
|
16
|
+
openai: 'gpt-5.5',
|
|
13
17
|
};
|
|
14
18
|
export const DEFAULT_OPENROUTER_MODELS = {
|
|
19
|
+
deepseek: 'deepseek/deepseek-v4-pro',
|
|
15
20
|
gemini: 'google/gemini-3.1-pro-preview',
|
|
21
|
+
zai: 'z-ai/glm-5.2',
|
|
16
22
|
};
|
|
17
23
|
export const MAX_PI_THINKING_BY_PROVIDER = {
|
|
24
|
+
deepseek: 'high',
|
|
18
25
|
gemini: 'high',
|
|
26
|
+
zai: 'high',
|
|
19
27
|
openai: 'xhigh',
|
|
20
28
|
};
|
|
21
29
|
const PI_API_KEY_ENV_VAR_BY_PI_PROVIDER = {
|
|
30
|
+
google: 'GEMINI_API_KEY',
|
|
22
31
|
openai: 'OPENAI_API_KEY',
|
|
23
32
|
'openai-codex': 'OPENAI_API_KEY',
|
|
24
33
|
openrouter: 'OPENROUTER_API_KEY',
|
|
25
34
|
};
|
|
26
|
-
const PI_PROVIDER_IDS = new Set(['openai', 'openai-codex', 'openrouter']);
|
|
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) {
|
|
@@ -67,7 +79,7 @@ function getPiModelSpec({ provider, piProviderId, modelName, thinkingLevel, }) {
|
|
|
67
79
|
DEFAULT_PI_MODELS[provider];
|
|
68
80
|
return `${providerId}/${resolvedModelName}:${thinkingLevel}`;
|
|
69
81
|
}
|
|
70
|
-
function parsePiAuthProviderId(apiKey) {
|
|
82
|
+
export function parsePiAuthProviderId(apiKey) {
|
|
71
83
|
if (!apiKey.startsWith(PI_AUTH_API_KEY_PREFIX)) {
|
|
72
84
|
return undefined;
|
|
73
85
|
}
|
|
@@ -112,6 +124,54 @@ function createImageArgs(images) {
|
|
|
112
124
|
},
|
|
113
125
|
};
|
|
114
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
|
+
}
|
|
115
175
|
function extractTextSegments(content) {
|
|
116
176
|
if (!Array.isArray(content)) {
|
|
117
177
|
return [];
|
|
@@ -178,7 +238,8 @@ function maybeEmitProgress({ event, onProgress, lastThinkingAt, lastRespondingAt
|
|
|
178
238
|
case 'tool_execution_start': {
|
|
179
239
|
const toolName = event.toolName;
|
|
180
240
|
if (typeof toolName === 'string' && toolName.length > 0) {
|
|
181
|
-
|
|
241
|
+
const toolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
242
|
+
onProgress({ type: 'tool_start', toolName, ...(toolCallId ? { toolCallId } : {}) });
|
|
182
243
|
}
|
|
183
244
|
return;
|
|
184
245
|
}
|
|
@@ -187,23 +248,30 @@ function maybeEmitProgress({ event, onProgress, lastThinkingAt, lastRespondingAt
|
|
|
187
248
|
if (typeof toolName !== 'string' || toolName.length === 0) {
|
|
188
249
|
return;
|
|
189
250
|
}
|
|
190
|
-
const
|
|
251
|
+
const rawToolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
252
|
+
const toolCallId = rawToolCallId ?? toolName;
|
|
191
253
|
const now = Date.now();
|
|
192
254
|
const lastUpdateAt = lastToolUpdateAt.get(toolCallId) ?? 0;
|
|
193
255
|
if (now - lastUpdateAt >= PI_PROGRESS_HEARTBEAT_MS) {
|
|
194
256
|
lastToolUpdateAt.set(toolCallId, now);
|
|
195
|
-
onProgress({
|
|
257
|
+
onProgress({
|
|
258
|
+
type: 'tool_update',
|
|
259
|
+
toolName,
|
|
260
|
+
...(rawToolCallId ? { toolCallId: rawToolCallId } : {}),
|
|
261
|
+
});
|
|
196
262
|
}
|
|
197
263
|
return;
|
|
198
264
|
}
|
|
199
265
|
case 'tool_execution_end': {
|
|
200
266
|
const toolName = event.toolName;
|
|
201
267
|
if (typeof toolName === 'string' && toolName.length > 0) {
|
|
202
|
-
const
|
|
268
|
+
const rawToolCallId = typeof event.toolCallId === 'string' ? event.toolCallId : undefined;
|
|
269
|
+
const toolCallId = rawToolCallId ?? toolName;
|
|
203
270
|
const startedAt = toolStartedAt.get(toolCallId);
|
|
204
271
|
onProgress({
|
|
205
272
|
type: 'tool_end',
|
|
206
273
|
toolName,
|
|
274
|
+
...(rawToolCallId ? { toolCallId: rawToolCallId } : {}),
|
|
207
275
|
isError: event.isError === true,
|
|
208
276
|
durationMs: startedAt == null ? undefined : Math.max(0, Date.now() - startedAt),
|
|
209
277
|
});
|
|
@@ -219,7 +287,7 @@ function toSortedToolUsage(toolUsageByName) {
|
|
|
219
287
|
return left.toolName.localeCompare(right.toolName);
|
|
220
288
|
});
|
|
221
289
|
}
|
|
222
|
-
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, }) {
|
|
223
291
|
return {
|
|
224
292
|
async invoke({ prompt, modelName, timeoutMs = 5 * 60 * 1000, images, }) {
|
|
225
293
|
const startedAt = Date.now();
|
|
@@ -232,6 +300,10 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
232
300
|
thinkingLevel: thinkingLevel ?? MAX_PI_THINKING_BY_PROVIDER[provider],
|
|
233
301
|
});
|
|
234
302
|
const { imageArgs, cleanup } = createImageArgs(images);
|
|
303
|
+
const openRouterSessionExtension = createOpenRouterSessionExtension({
|
|
304
|
+
piProviderId: resolvedPiProviderId,
|
|
305
|
+
sessionId: openRouterSessionId,
|
|
306
|
+
});
|
|
235
307
|
const tools = PI_TOOLS_BY_PRESET[capabilityPreset].join(',');
|
|
236
308
|
const args = [
|
|
237
309
|
PI_CLI_PATH,
|
|
@@ -241,12 +313,14 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
241
313
|
'--no-extensions',
|
|
242
314
|
'--no-skills',
|
|
243
315
|
'--no-prompt-templates',
|
|
316
|
+
...(noContextFiles ? ['--no-context-files'] : []),
|
|
244
317
|
'--model',
|
|
245
318
|
modelSpec,
|
|
246
319
|
'--tools',
|
|
247
320
|
tools,
|
|
248
321
|
'--system-prompt',
|
|
249
322
|
systemPrompt,
|
|
323
|
+
...openRouterSessionExtension.args,
|
|
250
324
|
...imageArgs,
|
|
251
325
|
];
|
|
252
326
|
const childEnv = {
|
|
@@ -255,6 +329,7 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
255
329
|
PATH: process.env.PATH,
|
|
256
330
|
GIT_TERMINAL_PROMPT: '0',
|
|
257
331
|
...extraEnv,
|
|
332
|
+
...openRouterSessionExtension.extraEnv,
|
|
258
333
|
};
|
|
259
334
|
const apiKeyEnvVar = PI_API_KEY_ENV_VAR_BY_PI_PROVIDER[resolvedPiProviderId];
|
|
260
335
|
if (piAuthProviderId) {
|
|
@@ -264,205 +339,209 @@ export function createPiInvoker({ provider, apiKey, piProviderId, cwd, extraEnv,
|
|
|
264
339
|
childEnv[apiKeyEnvVar] = apiKey;
|
|
265
340
|
}
|
|
266
341
|
delete childEnv.GITHUB_TOKEN;
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
try {
|
|
295
|
-
const parsed = JSON.parse(trimmed);
|
|
296
|
-
if (parsed.type === 'session') {
|
|
297
|
-
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) {
|
|
298
368
|
continue;
|
|
299
369
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const
|
|
316
|
-
if (
|
|
317
|
-
|
|
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
|
+
}
|
|
318
403
|
}
|
|
319
|
-
|
|
320
|
-
|
|
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) ?? {
|
|
321
409
|
toolName,
|
|
322
|
-
calls:
|
|
410
|
+
calls: 0,
|
|
323
411
|
totalDurationMs: 0,
|
|
324
412
|
errorCount: 0,
|
|
325
|
-
}
|
|
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);
|
|
326
426
|
}
|
|
327
427
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
const toolName = typeof parsed.toolName === 'string' ? parsed.toolName : null;
|
|
331
|
-
if (toolName) {
|
|
332
|
-
const current = toolUsageByName.get(toolName) ?? {
|
|
333
|
-
toolName,
|
|
334
|
-
calls: 0,
|
|
335
|
-
totalDurationMs: 0,
|
|
336
|
-
errorCount: 0,
|
|
337
|
-
};
|
|
338
|
-
const toolCallId = typeof parsed.toolCallId === 'string'
|
|
339
|
-
? parsed.toolCallId
|
|
340
|
-
: toolName;
|
|
341
|
-
const startedAt = toolStartedAt.get(toolCallId);
|
|
342
|
-
toolStartedAt.delete(toolCallId);
|
|
343
|
-
if (startedAt != null) {
|
|
344
|
-
current.totalDurationMs += Math.max(0, Date.now() - startedAt);
|
|
345
|
-
}
|
|
346
|
-
if (parsed.isError === true) {
|
|
347
|
-
current.errorCount += 1;
|
|
348
|
-
}
|
|
349
|
-
toolUsageByName.set(toolName, current);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
if (parsed.type === 'message_end') {
|
|
353
|
-
const result = extractAssistantMessageResult(parsed.message);
|
|
354
|
-
if (result?.text) {
|
|
355
|
-
lastAssistantText = result.text;
|
|
356
|
-
}
|
|
357
|
-
else if (result?.error) {
|
|
358
|
-
lastAssistantError = result.error;
|
|
359
|
-
}
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
if (parsed.type === 'agent_end' && Array.isArray(parsed.messages)) {
|
|
363
|
-
for (const message of parsed.messages) {
|
|
364
|
-
const result = extractAssistantMessageResult(message);
|
|
428
|
+
if (parsed.type === 'message_end') {
|
|
429
|
+
const result = extractAssistantMessageResult(parsed.message);
|
|
365
430
|
if (result?.text) {
|
|
366
431
|
lastAssistantText = result.text;
|
|
367
432
|
}
|
|
368
433
|
else if (result?.error) {
|
|
369
434
|
lastAssistantError = result.error;
|
|
370
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
|
+
}
|
|
371
448
|
}
|
|
372
449
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
450
|
+
catch {
|
|
451
|
+
if (!sessionHeaderSeen && !lastAssistantText) {
|
|
452
|
+
lastAssistantText = trimmed;
|
|
453
|
+
}
|
|
377
454
|
}
|
|
378
455
|
}
|
|
379
456
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
parseStdout(chunk);
|
|
389
|
-
});
|
|
390
|
-
child.stderr.setEncoding('utf8');
|
|
391
|
-
child.stderr.on('data', (chunk) => {
|
|
392
|
-
stderr += chunk;
|
|
393
|
-
});
|
|
394
|
-
child.on('error', (error) => {
|
|
395
|
-
clearTimeout(timeoutHandle);
|
|
396
|
-
resolve({
|
|
397
|
-
ok: false,
|
|
398
|
-
error: error.message,
|
|
399
|
-
durationMs: Date.now() - startedAt,
|
|
400
|
-
timedOut,
|
|
401
|
-
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);
|
|
402
465
|
});
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
if (timedOut) {
|
|
410
|
-
resolve({
|
|
411
|
-
ok: false,
|
|
412
|
-
error: `Pi request timed out after ${timeoutMs}ms`,
|
|
413
|
-
durationMs: Date.now() - startedAt,
|
|
414
|
-
timedOut: true,
|
|
415
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
416
|
-
});
|
|
417
|
-
return;
|
|
418
|
-
}
|
|
419
|
-
if (signal) {
|
|
420
|
-
resolve({
|
|
421
|
-
ok: false,
|
|
422
|
-
error: `Pi process terminated with signal ${signal}`,
|
|
423
|
-
durationMs: Date.now() - startedAt,
|
|
424
|
-
timedOut: false,
|
|
425
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
426
|
-
});
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
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);
|
|
430
472
|
resolve({
|
|
431
473
|
ok: false,
|
|
432
|
-
error:
|
|
474
|
+
error: error.message,
|
|
433
475
|
durationMs: Date.now() - startedAt,
|
|
434
|
-
timedOut
|
|
476
|
+
timedOut,
|
|
435
477
|
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
436
478
|
});
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
+
}
|
|
441
528
|
resolve({
|
|
442
|
-
ok:
|
|
443
|
-
|
|
444
|
-
stderr.trim() ||
|
|
445
|
-
'Pi session did not produce an assistant response',
|
|
529
|
+
ok: true,
|
|
530
|
+
output,
|
|
446
531
|
durationMs: Date.now() - startedAt,
|
|
447
|
-
timedOut: false,
|
|
448
532
|
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
449
533
|
});
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
resolve({
|
|
453
|
-
ok: true,
|
|
454
|
-
output,
|
|
455
|
-
durationMs: Date.now() - startedAt,
|
|
456
|
-
toolUsage: toSortedToolUsage(toolUsageByName),
|
|
457
534
|
});
|
|
458
535
|
});
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
536
|
+
}
|
|
537
|
+
finally {
|
|
538
|
+
cleanup();
|
|
539
|
+
openRouterSessionExtension.cleanup();
|
|
540
|
+
}
|
|
462
541
|
},
|
|
463
542
|
};
|
|
464
543
|
}
|
|
465
|
-
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, }) {
|
|
466
545
|
return await createPiInvoker({
|
|
467
546
|
provider,
|
|
468
547
|
apiKey,
|
|
@@ -472,6 +551,8 @@ export async function invokePiPrompt({ provider, apiKey, piProviderId, prompt, c
|
|
|
472
551
|
capabilityPreset,
|
|
473
552
|
thinkingLevel,
|
|
474
553
|
systemPrompt,
|
|
554
|
+
noContextFiles,
|
|
555
|
+
openRouterSessionId,
|
|
475
556
|
onProgress,
|
|
476
557
|
}).invoke({
|
|
477
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
|
+
}
|