@oh-my-pi/pi-coding-agent 3.25.0 → 3.30.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/CHANGELOG.md +19 -0
- package/package.json +4 -4
- package/src/core/tools/complete.ts +2 -4
- package/src/core/tools/jtd-to-json-schema.ts +174 -196
- package/src/core/tools/read.ts +4 -4
- package/src/core/tools/task/executor.ts +146 -20
- package/src/core/tools/task/name-generator.ts +1544 -214
- package/src/core/tools/task/types.ts +19 -5
- package/src/core/tools/task/worker.ts +103 -13
- package/src/core/tools/web-fetch-handlers/academic.test.ts +239 -0
- package/src/core/tools/web-fetch-handlers/artifacthub.ts +210 -0
- package/src/core/tools/web-fetch-handlers/arxiv.ts +84 -0
- package/src/core/tools/web-fetch-handlers/aur.ts +171 -0
- package/src/core/tools/web-fetch-handlers/biorxiv.ts +136 -0
- package/src/core/tools/web-fetch-handlers/bluesky.ts +277 -0
- package/src/core/tools/web-fetch-handlers/brew.ts +173 -0
- package/src/core/tools/web-fetch-handlers/business.test.ts +82 -0
- package/src/core/tools/web-fetch-handlers/cheatsh.ts +73 -0
- package/src/core/tools/web-fetch-handlers/chocolatey.ts +153 -0
- package/src/core/tools/web-fetch-handlers/coingecko.ts +179 -0
- package/src/core/tools/web-fetch-handlers/crates-io.ts +123 -0
- package/src/core/tools/web-fetch-handlers/dev-platforms.test.ts +254 -0
- package/src/core/tools/web-fetch-handlers/devto.ts +173 -0
- package/src/core/tools/web-fetch-handlers/discogs.ts +303 -0
- package/src/core/tools/web-fetch-handlers/dockerhub.ts +156 -0
- package/src/core/tools/web-fetch-handlers/documentation.test.ts +85 -0
- package/src/core/tools/web-fetch-handlers/finance-media.test.ts +144 -0
- package/src/core/tools/web-fetch-handlers/git-hosting.test.ts +272 -0
- package/src/core/tools/web-fetch-handlers/github-gist.ts +64 -0
- package/src/core/tools/web-fetch-handlers/github.ts +424 -0
- package/src/core/tools/web-fetch-handlers/gitlab.ts +444 -0
- package/src/core/tools/web-fetch-handlers/go-pkg.ts +271 -0
- package/src/core/tools/web-fetch-handlers/hackage.ts +89 -0
- package/src/core/tools/web-fetch-handlers/hackernews.ts +208 -0
- package/src/core/tools/web-fetch-handlers/hex.ts +121 -0
- package/src/core/tools/web-fetch-handlers/huggingface.ts +385 -0
- package/src/core/tools/web-fetch-handlers/iacr.ts +82 -0
- package/src/core/tools/web-fetch-handlers/index.ts +69 -0
- package/src/core/tools/web-fetch-handlers/lobsters.ts +186 -0
- package/src/core/tools/web-fetch-handlers/mastodon.ts +302 -0
- package/src/core/tools/web-fetch-handlers/maven.ts +147 -0
- package/src/core/tools/web-fetch-handlers/mdn.ts +174 -0
- package/src/core/tools/web-fetch-handlers/media.test.ts +138 -0
- package/src/core/tools/web-fetch-handlers/metacpan.ts +247 -0
- package/src/core/tools/web-fetch-handlers/npm.ts +107 -0
- package/src/core/tools/web-fetch-handlers/nuget.ts +201 -0
- package/src/core/tools/web-fetch-handlers/nvd.ts +238 -0
- package/src/core/tools/web-fetch-handlers/opencorporates.ts +273 -0
- package/src/core/tools/web-fetch-handlers/openlibrary.ts +313 -0
- package/src/core/tools/web-fetch-handlers/osv.ts +184 -0
- package/src/core/tools/web-fetch-handlers/package-managers-2.test.ts +199 -0
- package/src/core/tools/web-fetch-handlers/package-managers.test.ts +171 -0
- package/src/core/tools/web-fetch-handlers/package-registries.test.ts +259 -0
- package/src/core/tools/web-fetch-handlers/packagist.ts +170 -0
- package/src/core/tools/web-fetch-handlers/pub-dev.ts +185 -0
- package/src/core/tools/web-fetch-handlers/pubmed.ts +174 -0
- package/src/core/tools/web-fetch-handlers/pypi.ts +125 -0
- package/src/core/tools/web-fetch-handlers/readthedocs.ts +122 -0
- package/src/core/tools/web-fetch-handlers/reddit.ts +100 -0
- package/src/core/tools/web-fetch-handlers/repology.ts +257 -0
- package/src/core/tools/web-fetch-handlers/research.test.ts +107 -0
- package/src/core/tools/web-fetch-handlers/rfc.ts +205 -0
- package/src/core/tools/web-fetch-handlers/rubygems.ts +112 -0
- package/src/core/tools/web-fetch-handlers/sec-edgar.ts +269 -0
- package/src/core/tools/web-fetch-handlers/security.test.ts +103 -0
- package/src/core/tools/web-fetch-handlers/semantic-scholar.ts +190 -0
- package/src/core/tools/web-fetch-handlers/social-extended.test.ts +192 -0
- package/src/core/tools/web-fetch-handlers/social.test.ts +259 -0
- package/src/core/tools/web-fetch-handlers/spotify.ts +218 -0
- package/src/core/tools/web-fetch-handlers/stackexchange.test.ts +120 -0
- package/src/core/tools/web-fetch-handlers/stackoverflow.ts +123 -0
- package/src/core/tools/web-fetch-handlers/standards.test.ts +122 -0
- package/src/core/tools/web-fetch-handlers/terraform.ts +296 -0
- package/src/core/tools/web-fetch-handlers/tldr.ts +47 -0
- package/src/core/tools/web-fetch-handlers/twitter.ts +84 -0
- package/src/core/tools/web-fetch-handlers/types.ts +163 -0
- package/src/core/tools/web-fetch-handlers/utils.ts +91 -0
- package/src/core/tools/web-fetch-handlers/vimeo.ts +152 -0
- package/src/core/tools/web-fetch-handlers/wikidata.ts +349 -0
- package/src/core/tools/web-fetch-handlers/wikipedia.test.ts +73 -0
- package/src/core/tools/web-fetch-handlers/wikipedia.ts +91 -0
- package/src/core/tools/web-fetch-handlers/youtube.test.ts +198 -0
- package/src/core/tools/web-fetch-handlers/youtube.ts +319 -0
- package/src/core/tools/web-fetch.ts +152 -1324
- package/src/utils/tools-manager.ts +110 -8
|
@@ -207,13 +207,39 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
207
207
|
const sessionFile = subtaskSessionFile ?? options.sessionFile ?? null;
|
|
208
208
|
const spawnsEnv = agent.spawns === undefined ? "" : agent.spawns === "*" ? "*" : agent.spawns.join(",");
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
let worker: Worker;
|
|
211
|
+
try {
|
|
212
|
+
worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
|
|
213
|
+
} catch (err) {
|
|
214
|
+
return {
|
|
215
|
+
index,
|
|
216
|
+
taskId,
|
|
217
|
+
agent: agent.name,
|
|
218
|
+
agentSource: agent.source,
|
|
219
|
+
task,
|
|
220
|
+
description: options.description,
|
|
221
|
+
exitCode: 1,
|
|
222
|
+
output: "",
|
|
223
|
+
stderr: `Failed to create worker: ${err instanceof Error ? err.message : String(err)}`,
|
|
224
|
+
truncated: false,
|
|
225
|
+
durationMs: Date.now() - startTime,
|
|
226
|
+
tokens: 0,
|
|
227
|
+
modelOverride,
|
|
228
|
+
error: `Failed to create worker: ${err instanceof Error ? err.message : String(err)}`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
211
231
|
|
|
212
232
|
let output = "";
|
|
213
233
|
let stderr = "";
|
|
214
234
|
let finalOutput = "";
|
|
215
235
|
let resolved = false;
|
|
216
236
|
let pendingTermination = false; // Set when shouldTerminate fires, wait for message_end
|
|
237
|
+
type AbortReason = "signal" | "terminate";
|
|
238
|
+
let abortSent = false;
|
|
239
|
+
let abortReason: AbortReason | undefined;
|
|
240
|
+
let abortTerminateTimer: ReturnType<typeof setTimeout> | undefined;
|
|
241
|
+
let pendingTerminationTimer: ReturnType<typeof setTimeout> | undefined;
|
|
242
|
+
let finalize: ((message: Extract<SubagentWorkerResponse, { type: "done" }>) => void) | null = null;
|
|
217
243
|
|
|
218
244
|
// Accumulate usage incrementally from message_end events (no memory for streaming events)
|
|
219
245
|
const accumulatedUsage = {
|
|
@@ -226,22 +252,55 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
226
252
|
};
|
|
227
253
|
let hasUsage = false;
|
|
228
254
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
255
|
+
const clearTimers = (): void => {
|
|
256
|
+
if (abortTerminateTimer) clearTimeout(abortTerminateTimer);
|
|
257
|
+
abortTerminateTimer = undefined;
|
|
258
|
+
if (pendingTerminationTimer) clearTimeout(pendingTerminationTimer);
|
|
259
|
+
pendingTerminationTimer = undefined;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const requestAbort = (reason: AbortReason) => {
|
|
263
|
+
if (abortSent) {
|
|
264
|
+
if (reason === "signal" && abortReason !== "signal") {
|
|
265
|
+
abortReason = "signal";
|
|
266
|
+
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
if (resolved) return;
|
|
232
270
|
abortSent = true;
|
|
271
|
+
abortReason = reason;
|
|
272
|
+
if (pendingTerminationTimer) clearTimeout(pendingTerminationTimer);
|
|
273
|
+
pendingTerminationTimer = undefined;
|
|
233
274
|
const abortMessage: SubagentWorkerRequest = { type: "abort" };
|
|
234
|
-
|
|
235
|
-
|
|
275
|
+
try {
|
|
276
|
+
worker.postMessage(abortMessage);
|
|
277
|
+
} catch {
|
|
278
|
+
// Worker already terminated, nothing to do
|
|
279
|
+
}
|
|
280
|
+
if (abortTerminateTimer) clearTimeout(abortTerminateTimer);
|
|
281
|
+
abortTerminateTimer = setTimeout(() => {
|
|
236
282
|
if (!resolved) {
|
|
237
|
-
|
|
283
|
+
try {
|
|
284
|
+
worker.terminate();
|
|
285
|
+
} catch {
|
|
286
|
+
// Ignore termination errors
|
|
287
|
+
}
|
|
288
|
+
if (finalize && !resolved) {
|
|
289
|
+
finalize({
|
|
290
|
+
type: "done",
|
|
291
|
+
exitCode: 1,
|
|
292
|
+
durationMs: Date.now() - startTime,
|
|
293
|
+
error: reason === "signal" ? "Aborted" : "Worker terminated after tool completion",
|
|
294
|
+
aborted: reason === "signal",
|
|
295
|
+
});
|
|
296
|
+
}
|
|
238
297
|
}
|
|
239
298
|
}, 2000);
|
|
240
299
|
};
|
|
241
300
|
|
|
242
301
|
// Handle abort signal
|
|
243
302
|
const onAbort = () => {
|
|
244
|
-
if (!resolved) requestAbort();
|
|
303
|
+
if (!resolved) requestAbort("signal");
|
|
245
304
|
};
|
|
246
305
|
if (signal) {
|
|
247
306
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
@@ -349,9 +408,10 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
349
408
|
// Don't terminate immediately - wait for message_end to get token counts
|
|
350
409
|
pendingTermination = true;
|
|
351
410
|
// Safety timeout in case message_end never arrives
|
|
352
|
-
|
|
411
|
+
if (pendingTerminationTimer) clearTimeout(pendingTerminationTimer);
|
|
412
|
+
pendingTerminationTimer = setTimeout(() => {
|
|
353
413
|
if (!resolved) {
|
|
354
|
-
requestAbort();
|
|
414
|
+
requestAbort("terminate");
|
|
355
415
|
}
|
|
356
416
|
}, 2000);
|
|
357
417
|
}
|
|
@@ -421,7 +481,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
421
481
|
}
|
|
422
482
|
// If pending termination, now we have tokens - terminate
|
|
423
483
|
if (pendingTermination && !resolved) {
|
|
424
|
-
requestAbort();
|
|
484
|
+
requestAbort("terminate");
|
|
425
485
|
}
|
|
426
486
|
break;
|
|
427
487
|
}
|
|
@@ -469,38 +529,96 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
469
529
|
}
|
|
470
530
|
|
|
471
531
|
const done = await new Promise<Extract<SubagentWorkerResponse, { type: "done" }>>((resolve) => {
|
|
532
|
+
const cleanup = () => {
|
|
533
|
+
worker.removeEventListener("message", onMessage);
|
|
534
|
+
worker.removeEventListener("error", onError);
|
|
535
|
+
worker.removeEventListener("close", onClose);
|
|
536
|
+
worker.removeEventListener("messageerror", onMessageError);
|
|
537
|
+
clearTimers();
|
|
538
|
+
};
|
|
539
|
+
finalize = (message) => {
|
|
540
|
+
if (resolved) return;
|
|
541
|
+
resolved = true;
|
|
542
|
+
cleanup();
|
|
543
|
+
resolve(message);
|
|
544
|
+
};
|
|
472
545
|
const onMessage = (event: WorkerMessageEvent<SubagentWorkerResponse>) => {
|
|
473
546
|
const message = event.data;
|
|
474
547
|
if (!message || resolved) return;
|
|
475
548
|
if (message.type === "event") {
|
|
476
|
-
|
|
549
|
+
try {
|
|
550
|
+
processEvent(message.event);
|
|
551
|
+
} catch (err) {
|
|
552
|
+
finalize?.({
|
|
553
|
+
type: "done",
|
|
554
|
+
exitCode: 1,
|
|
555
|
+
durationMs: Date.now() - startTime,
|
|
556
|
+
error: `Failed to process worker event: ${err instanceof Error ? err.message : String(err)}`,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
477
559
|
return;
|
|
478
560
|
}
|
|
479
561
|
if (message.type === "done") {
|
|
480
|
-
|
|
481
|
-
resolve(message);
|
|
562
|
+
finalize?.(message);
|
|
482
563
|
}
|
|
483
564
|
};
|
|
484
565
|
const onError = (event: WorkerErrorEvent) => {
|
|
485
|
-
|
|
486
|
-
resolved = true;
|
|
487
|
-
resolve({
|
|
566
|
+
finalize?.({
|
|
488
567
|
type: "done",
|
|
489
568
|
exitCode: 1,
|
|
490
569
|
durationMs: Date.now() - startTime,
|
|
491
570
|
error: event.message,
|
|
492
571
|
});
|
|
493
572
|
};
|
|
573
|
+
const onMessageError = () => {
|
|
574
|
+
finalize?.({
|
|
575
|
+
type: "done",
|
|
576
|
+
exitCode: 1,
|
|
577
|
+
durationMs: Date.now() - startTime,
|
|
578
|
+
error: "Worker message deserialization failed",
|
|
579
|
+
});
|
|
580
|
+
};
|
|
581
|
+
const onClose = () => {
|
|
582
|
+
// Worker terminated unexpectedly (crashed or was killed without sending done)
|
|
583
|
+
const abortMessage =
|
|
584
|
+
abortSent && abortReason === "signal"
|
|
585
|
+
? "Worker terminated after abort"
|
|
586
|
+
: abortSent
|
|
587
|
+
? "Worker terminated after tool completion"
|
|
588
|
+
: "Worker terminated unexpectedly";
|
|
589
|
+
finalize?.({
|
|
590
|
+
type: "done",
|
|
591
|
+
exitCode: 1,
|
|
592
|
+
durationMs: Date.now() - startTime,
|
|
593
|
+
error: abortMessage,
|
|
594
|
+
aborted: abortReason === "signal",
|
|
595
|
+
});
|
|
596
|
+
};
|
|
494
597
|
worker.addEventListener("message", onMessage);
|
|
495
598
|
worker.addEventListener("error", onError);
|
|
496
|
-
worker.
|
|
599
|
+
worker.addEventListener("close", onClose);
|
|
600
|
+
worker.addEventListener("messageerror", onMessageError);
|
|
601
|
+
try {
|
|
602
|
+
worker.postMessage(startMessage);
|
|
603
|
+
} catch (err) {
|
|
604
|
+
finalize({
|
|
605
|
+
type: "done",
|
|
606
|
+
exitCode: 1,
|
|
607
|
+
durationMs: Date.now() - startTime,
|
|
608
|
+
error: `Failed to start worker: ${err instanceof Error ? err.message : String(err)}`,
|
|
609
|
+
});
|
|
610
|
+
}
|
|
497
611
|
});
|
|
498
612
|
|
|
499
613
|
// Cleanup
|
|
500
614
|
if (signal) {
|
|
501
615
|
signal.removeEventListener("abort", onAbort);
|
|
502
616
|
}
|
|
503
|
-
|
|
617
|
+
try {
|
|
618
|
+
worker.terminate();
|
|
619
|
+
} catch {
|
|
620
|
+
// Ignore termination errors
|
|
621
|
+
}
|
|
504
622
|
|
|
505
623
|
let exitCode = done.exitCode;
|
|
506
624
|
if (done.error) {
|
|
@@ -528,7 +646,15 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
|
|
|
528
646
|
}
|
|
529
647
|
} else {
|
|
530
648
|
// Normal successful completion
|
|
531
|
-
|
|
649
|
+
let completeData = lastComplete?.data ?? null;
|
|
650
|
+
// Handle double-stringified JSON (subagent returned JSON string instead of object)
|
|
651
|
+
if (typeof completeData === "string" && (completeData.startsWith("{") || completeData.startsWith("["))) {
|
|
652
|
+
try {
|
|
653
|
+
completeData = JSON.parse(completeData);
|
|
654
|
+
} catch {
|
|
655
|
+
// Not valid JSON, keep as string
|
|
656
|
+
}
|
|
657
|
+
}
|
|
532
658
|
try {
|
|
533
659
|
rawOutput = JSON.stringify(completeData, null, 2) ?? "null";
|
|
534
660
|
} catch (err) {
|