@oh-my-pi/subagents 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +1 -1
- package/tools/task/index.ts +79 -6
package/README.md
CHANGED
package/package.json
CHANGED
package/tools/task/index.ts
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
import * as fs from "node:fs";
|
|
30
30
|
import * as os from "node:os";
|
|
31
31
|
import * as path from "node:path";
|
|
32
|
-
import { spawn } from "node:child_process";
|
|
32
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
33
33
|
import * as readline from "node:readline";
|
|
34
34
|
import { Type } from "@sinclair/typebox";
|
|
35
35
|
import { StringEnum, type AgentToolUpdateCallback } from "@mariozechner/pi-ai";
|
|
@@ -40,6 +40,76 @@ import type {
|
|
|
40
40
|
ToolAPI,
|
|
41
41
|
} from "@mariozechner/pi-coding-agent";
|
|
42
42
|
|
|
43
|
+
/** Cache for available models (refreshed once per tool factory instantiation) */
|
|
44
|
+
let cachedModels: string[] | null = null;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get available models from `pi --list-models`.
|
|
48
|
+
* Caches the result for performance.
|
|
49
|
+
*/
|
|
50
|
+
function getAvailableModels(): string[] {
|
|
51
|
+
if (cachedModels !== null) return cachedModels;
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const result = spawnSync("pi", ["--list-models"], {
|
|
55
|
+
encoding: "utf-8",
|
|
56
|
+
timeout: 5000,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (result.status !== 0 || !result.stdout) {
|
|
60
|
+
cachedModels = [];
|
|
61
|
+
return cachedModels;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Parse output: skip header line, extract model column
|
|
65
|
+
const lines = result.stdout.trim().split("\n");
|
|
66
|
+
cachedModels = lines
|
|
67
|
+
.slice(1) // Skip header
|
|
68
|
+
.map((line) => {
|
|
69
|
+
const parts = line.trim().split(/\s+/);
|
|
70
|
+
return parts[1]; // Model name is second column
|
|
71
|
+
})
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
|
|
74
|
+
return cachedModels;
|
|
75
|
+
} catch {
|
|
76
|
+
cachedModels = [];
|
|
77
|
+
return cachedModels;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Resolve a fuzzy model pattern to an actual model name.
|
|
83
|
+
* Supports comma-separated patterns (e.g., "gpt, opus").
|
|
84
|
+
* Returns the first match found, or undefined if no match.
|
|
85
|
+
*/
|
|
86
|
+
function resolveModelPattern(
|
|
87
|
+
pattern: string,
|
|
88
|
+
availableModels?: string[],
|
|
89
|
+
): string | undefined {
|
|
90
|
+
if (!pattern || pattern === "default") return undefined;
|
|
91
|
+
|
|
92
|
+
const models = availableModels ?? getAvailableModels();
|
|
93
|
+
if (models.length === 0) {
|
|
94
|
+
// Fallback: return pattern as-is if we can't get available models
|
|
95
|
+
return pattern;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Split by comma, try each pattern in order
|
|
99
|
+
const patterns = pattern
|
|
100
|
+
.split(",")
|
|
101
|
+
.map((p) => p.trim().toLowerCase())
|
|
102
|
+
.filter(Boolean);
|
|
103
|
+
|
|
104
|
+
for (const p of patterns) {
|
|
105
|
+
const match = models.find((m) => m.toLowerCase().includes(p));
|
|
106
|
+
if (match) return match;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// No match found - return first pattern as-is (let pi handle the error)
|
|
110
|
+
return patterns[0];
|
|
111
|
+
}
|
|
112
|
+
|
|
43
113
|
const MAX_OUTPUT_LINES = 5000;
|
|
44
114
|
const MAX_OUTPUT_BYTES = 500_000;
|
|
45
115
|
const MAX_PARALLEL_TASKS = 32;
|
|
@@ -448,10 +518,13 @@ async function runSingleAgent(
|
|
|
448
518
|
|
|
449
519
|
const args: string[] = ["-p", "--no-session", "--mode", "json"];
|
|
450
520
|
|
|
451
|
-
|
|
521
|
+
// "default" means no model override - use pi's configured default
|
|
522
|
+
const modelOverride = options?.model;
|
|
523
|
+
const rawModel =
|
|
524
|
+
modelOverride === "default" ? undefined : (modelOverride ?? agent.model);
|
|
525
|
+
const modelToUse = rawModel ? resolveModelPattern(rawModel) : undefined;
|
|
452
526
|
if (modelToUse) {
|
|
453
|
-
|
|
454
|
-
args.push("--models", modelToUse);
|
|
527
|
+
args.push("--model", modelToUse);
|
|
455
528
|
}
|
|
456
529
|
|
|
457
530
|
if (agent.tools && agent.tools.length > 0) {
|
|
@@ -679,7 +752,7 @@ const TaskItem = Type.Object({
|
|
|
679
752
|
model: Type.Optional(
|
|
680
753
|
Type.String({
|
|
681
754
|
description:
|
|
682
|
-
|
|
755
|
+
'Override the model for this task (takes precedence over agent\'s default model), or "default" to use pi\'s default',
|
|
683
756
|
}),
|
|
684
757
|
),
|
|
685
758
|
});
|
|
@@ -803,7 +876,7 @@ function buildDescription(pi: ToolAPI): string {
|
|
|
803
876
|
" concurrent)",
|
|
804
877
|
);
|
|
805
878
|
lines.push(
|
|
806
|
-
' - model: (optional) Override the agent\'s default model
|
|
879
|
+
' - model: (optional) Override the agent\'s default model with fuzzy matching (e.g., "sonnet", "codex", "5.2"). Supports comma-separated fallbacks: "gpt, opus" tries gpt first, then opus. Use "default" for pi\'s default model',
|
|
807
880
|
);
|
|
808
881
|
lines.push(
|
|
809
882
|
"- context: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions",
|