@dougbots/pi-agents 0.1.0 → 0.1.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 +13 -0
- package/extensions/agents/index.ts +21 -8
- package/extensions/agents/model-selection.ts +29 -0
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -69,6 +69,19 @@ PI_AGENT=jockey pi -c
|
|
|
69
69
|
|
|
70
70
|
When `avenor_spawn(agent: "reviewer", backend: "pi")` is called, avenor spawns `pi --mode rpc` with `PI_AGENT=reviewer`. The agents extension applies the full profile (model, systemPrompt, tools, permissions) automatically.
|
|
71
71
|
|
|
72
|
+
Pass `model` as well to override only the profile's default model while retaining its prompt, tools, and permissions:
|
|
73
|
+
|
|
74
|
+
```text
|
|
75
|
+
avenor_spawn(
|
|
76
|
+
agent: "reviewer",
|
|
77
|
+
model: "anthropic/claude-opus-4",
|
|
78
|
+
backend: "pi",
|
|
79
|
+
prompt: "Review the outage recovery changes",
|
|
80
|
+
)
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Explicit `--model` selection takes precedence over the profile default. Avenor forwards its `model` option to the pi subprocess as `--model`, so no agent config changes are needed for a one-off override.
|
|
84
|
+
|
|
72
85
|
Requires [avenor](https://github.com/sdougbrown/avenor) with the pi backend (v0.3.3+). Model resolution falls back to `~/.pi/agent/agents.json` when the agent is not found in opencode config.
|
|
73
86
|
|
|
74
87
|
## Profile vs. agent
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
3
|
-
import { Type } from "typebox";
|
|
4
3
|
import { readFileSync, existsSync } from "node:fs";
|
|
5
4
|
import { join } from "node:path";
|
|
5
|
+
import { findCliModelOverride, parseModelSpecifier } from "./model-selection.ts";
|
|
6
6
|
|
|
7
7
|
/* ------------------------------------------------------------------ */
|
|
8
8
|
/* Types */
|
|
@@ -99,6 +99,10 @@ function checkBashPermission(
|
|
|
99
99
|
|
|
100
100
|
export default async function (pi: ExtensionAPI) {
|
|
101
101
|
const agents = loadAgentsConfig();
|
|
102
|
+
// Built-in flags are not exposed through pi.getFlag(). Avenor forwards its
|
|
103
|
+
// model option to pi as --model, so argv is the extension-level signal that
|
|
104
|
+
// the caller's model should take precedence over the profile default.
|
|
105
|
+
const cliModelOverride = findCliModelOverride(process.argv.slice(2));
|
|
102
106
|
let activeAgent: { name: string; profile: AgentProfile } | null = null;
|
|
103
107
|
|
|
104
108
|
/* ---- Apply agent profile to the current session ---- */
|
|
@@ -109,14 +113,23 @@ export default async function (pi: ExtensionAPI) {
|
|
|
109
113
|
return false;
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if (!
|
|
116
|
-
|
|
117
|
-
|
|
116
|
+
// Explicit CLI selection wins over the profile default. This preserves
|
|
117
|
+
// `avenor_spawn(agent: ..., model: ..., backend: "pi")` overrides because
|
|
118
|
+
// Avenor forwards model to the pi subprocess as --model.
|
|
119
|
+
if (!cliModelOverride) {
|
|
120
|
+
const modelSpecifier = parseModelSpecifier(profile.model);
|
|
121
|
+
if (!modelSpecifier) {
|
|
122
|
+
ctx.ui.notify(`Invalid model "${profile.model}"; expected provider/model-id`, "error");
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const model = ctx.modelRegistry.find(modelSpecifier.provider, modelSpecifier.modelId);
|
|
127
|
+
if (!model) {
|
|
128
|
+
ctx.ui.notify(`Model "${profile.model}" not found in registry`, "error");
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
await pi.setModel(model);
|
|
118
132
|
}
|
|
119
|
-
await pi.setModel(model);
|
|
120
133
|
|
|
121
134
|
// Thinking level
|
|
122
135
|
if (profile.thinkingLevel) {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ModelSpecifier {
|
|
2
|
+
provider: string;
|
|
3
|
+
modelId: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Return the model explicitly supplied to pi, if any.
|
|
8
|
+
*
|
|
9
|
+
* Pi consumes --model itself, so extension flags cannot expose this value via
|
|
10
|
+
* pi.getFlag(). Inspecting argv lets agent profiles respect normal CLI
|
|
11
|
+
* precedence, including models forwarded by Avenor.
|
|
12
|
+
*/
|
|
13
|
+
export function findCliModelOverride(argv: readonly string[]): string | undefined {
|
|
14
|
+
for (let index = 0; index < argv.length - 1; index += 1) {
|
|
15
|
+
if (argv[index] === "--model") return argv[index + 1];
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/** Split provider/model-id at the first slash so model IDs may contain slashes. */
|
|
21
|
+
export function parseModelSpecifier(specifier: string): ModelSpecifier | undefined {
|
|
22
|
+
const separator = specifier.indexOf("/");
|
|
23
|
+
if (separator <= 0 || separator === specifier.length - 1) return undefined;
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
provider: specifier.slice(0, separator),
|
|
27
|
+
modelId: specifier.slice(separator + 1),
|
|
28
|
+
};
|
|
29
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dougbots/pi-agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Agent profiles for pi — preconfigured model, system prompt, tool restrictions, and permission gates",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"test": "node --experimental-strip-types --test tests/*.test.mts"
|
|
7
|
+
},
|
|
5
8
|
"keywords": ["pi-package", "pi", "pi-extension", "agents"],
|
|
6
9
|
"license": "MIT",
|
|
7
10
|
"repository": {
|