@pi-kaush/pi-agent-mode 0.1.1 → 0.2.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 +6 -0
- package/README.md +19 -4
- package/package.json +4 -1
- package/src/agent-discovery.ts +7 -0
- package/src/index.ts +85 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
- Add `profile` frontmatter: activate a named model profile from the shared machine-local `~/.pi/agent/profiles.yaml` (`@pi-kaush/pi-model-profiles`), walking candidates in order and applying the first available model and thinking level.
|
|
6
|
+
- Reject agents declaring both `profile` and `model`; precedence is explicit: profile first, then model, then the session default.
|
|
7
|
+
- Load `@pi-kaush/pi-model-profiles` lazily so a missing library degrades only profile support; `/agent` remains fully functional without it.
|
|
8
|
+
|
|
3
9
|
## 0.1.0
|
|
4
10
|
|
|
5
11
|
- Add the `/agent` command for activating a configured Pi agent as a persistent mode in the current session.
|
package/README.md
CHANGED
|
@@ -5,14 +5,13 @@ the session to the agent's model, thinking level, and tool set, appends the agen
|
|
|
5
5
|
prompt to Pi's base instructions, and restores the pre-activation baseline when cleared.
|
|
6
6
|
It does not spawn children and provides no delegated subagents.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
### Install
|
|
9
9
|
|
|
10
|
-
```
|
|
10
|
+
```fish
|
|
11
11
|
pi install npm:@pi-kaush/pi-agent-mode
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Restart Pi or run `/reload`.
|
|
15
|
-
`@0.1.0`.
|
|
14
|
+
Restart Pi or run `/reload`.
|
|
16
15
|
|
|
17
16
|
## Usage
|
|
18
17
|
|
|
@@ -37,11 +36,27 @@ description: ... # required
|
|
|
37
36
|
emoji: 🔍 # optional, shown in the footer status
|
|
38
37
|
tools: read, grep # optional allowlist; unavailable tools are reported and omitted
|
|
39
38
|
model: provider/model:high # optional; thinking suffix may be :off/:low/:medium/:high/:max
|
|
39
|
+
profile: coder # optional; named model candidates from ~/.pi/agent/profiles.yaml
|
|
40
40
|
confirmProjectAgents: false # project agents default to requiring confirmation
|
|
41
41
|
---
|
|
42
42
|
Prompt body appended to Pi's base instructions while the agent is active.
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
+
## Model selection precedence
|
|
46
|
+
|
|
47
|
+
Agent frontmatter may declare a `model` (exact model, optional thinking
|
|
48
|
+
suffix) or a `profile` — a named entry in the shared machine-local
|
|
49
|
+
`~/.pi/agent/profiles.yaml` maintained by
|
|
50
|
+
[@pi-kaush/pi-model-profiles](../pi-model-profiles). An agent must not declare
|
|
51
|
+
both. Profile candidates are walked in order at activation and the first
|
|
52
|
+
model that exists and is authenticated wins; once the session is running,
|
|
53
|
+
models never switch mid-request. Agents without either keep the current
|
|
54
|
+
session model.
|
|
55
|
+
|
|
56
|
+
`pi-agent-mode` loads `@pi-kaush/pi-model-profiles` lazily: if the library is
|
|
57
|
+
missing, only agents that declare `profile:` fail (with an install hint) and
|
|
58
|
+
every other `/agent` capability is unencumbered.
|
|
59
|
+
|
|
45
60
|
## Behavior
|
|
46
61
|
|
|
47
62
|
- The first activation captures the current model, thinking level, and active
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-kaush/pi-agent-mode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Activate a configured Pi agent as a persistent mode in the current session.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Kaushik Gopal",
|
|
@@ -33,6 +33,9 @@
|
|
|
33
33
|
"./src/index.ts"
|
|
34
34
|
]
|
|
35
35
|
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@pi-kaush/pi-model-profiles": "^0.1.0"
|
|
38
|
+
},
|
|
36
39
|
"peerDependencies": {
|
|
37
40
|
"@earendil-works/pi-coding-agent": ">=0.80.6",
|
|
38
41
|
"@earendil-works/pi-tui": ">=0.80.6"
|
package/src/agent-discovery.ts
CHANGED
|
@@ -23,6 +23,7 @@ export interface AgentConfig {
|
|
|
23
23
|
emoji?: string;
|
|
24
24
|
tools?: string[];
|
|
25
25
|
model?: string;
|
|
26
|
+
profile?: string;
|
|
26
27
|
confirmProjectAgents?: boolean;
|
|
27
28
|
systemPrompt: string;
|
|
28
29
|
source: "user" | "project";
|
|
@@ -35,6 +36,7 @@ interface AgentFrontmatter extends Record<string, unknown> {
|
|
|
35
36
|
emoji?: string;
|
|
36
37
|
tools?: string;
|
|
37
38
|
model?: string;
|
|
39
|
+
profile?: string;
|
|
38
40
|
confirmProjectAgents?: boolean;
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -105,6 +107,10 @@ function loadAgentsFromDir(
|
|
|
105
107
|
: undefined;
|
|
106
108
|
const model =
|
|
107
109
|
typeof frontmatter.model === "string" ? frontmatter.model : undefined;
|
|
110
|
+
const profile =
|
|
111
|
+
typeof frontmatter.profile === "string"
|
|
112
|
+
? frontmatter.profile.trim()
|
|
113
|
+
: undefined;
|
|
108
114
|
|
|
109
115
|
agents.push({
|
|
110
116
|
name,
|
|
@@ -112,6 +118,7 @@ function loadAgentsFromDir(
|
|
|
112
118
|
...(emoji ? { emoji } : {}),
|
|
113
119
|
...(tools && tools.length > 0 ? { tools } : {}),
|
|
114
120
|
...(model ? { model } : {}),
|
|
121
|
+
...(profile ? { profile } : {}),
|
|
115
122
|
...(typeof frontmatter.confirmProjectAgents === "boolean"
|
|
116
123
|
? { confirmProjectAgents: frontmatter.confirmProjectAgents }
|
|
117
124
|
: {}),
|
package/src/index.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
ExtensionAPI,
|
|
15
15
|
ExtensionContext,
|
|
16
16
|
} from "@earendil-works/pi-coding-agent";
|
|
17
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
18
|
+
import type { ModelProfilesConfig } from "@pi-kaush/pi-model-profiles";
|
|
17
19
|
import {
|
|
18
20
|
discoverAgents,
|
|
19
21
|
formatAgentDisplayName,
|
|
@@ -32,7 +34,7 @@ interface ModelReference {
|
|
|
32
34
|
id: string;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
|
-
export { parseAgentModelSpec };
|
|
37
|
+
export { isModuleUnavailable, parseAgentModelSpec };
|
|
36
38
|
export type { AgentModelSpec } from "./model-spec.ts";
|
|
37
39
|
|
|
38
40
|
// Durable session state is namespaced so it cannot collide with other
|
|
@@ -99,6 +101,15 @@ function isActiveAgentState(value: unknown): value is ActiveAgentState {
|
|
|
99
101
|
return true;
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
function isModuleUnavailable(error: unknown): boolean {
|
|
105
|
+
const code = (error as { code?: string } | undefined)?.code;
|
|
106
|
+
return (
|
|
107
|
+
code === "MODULE_NOT_FOUND" ||
|
|
108
|
+
code === "ERR_MODULE_NOT_FOUND" ||
|
|
109
|
+
(error instanceof Error && error.message.includes("Cannot find module"))
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
102
113
|
export function registerAgentMode(pi: ExtensionAPI) {
|
|
103
114
|
let activeAgent: ActiveAgent | undefined;
|
|
104
115
|
|
|
@@ -200,6 +211,67 @@ export function registerAgentMode(pi: ExtensionAPI) {
|
|
|
200
211
|
pi.setActiveTools(valid);
|
|
201
212
|
}
|
|
202
213
|
|
|
214
|
+
/**
|
|
215
|
+
* Walk the agent's profile candidates in order and switch to the first
|
|
216
|
+
* model that exists and is authenticated. Fallback happens only during
|
|
217
|
+
* activation; running sessions never switch models mid-request.
|
|
218
|
+
*/
|
|
219
|
+
async function applyProfileModels(
|
|
220
|
+
agent: AgentConfig,
|
|
221
|
+
ctx: ExtensionContext,
|
|
222
|
+
): Promise<{ thinkingLevel?: ThinkingLevel } | false> {
|
|
223
|
+
let profiles: ModelProfilesConfig;
|
|
224
|
+
try {
|
|
225
|
+
// Runtime-only import: a missing @pi-kaush/pi-model-profiles degrades
|
|
226
|
+
// just profile support instead of failing the whole extension load.
|
|
227
|
+
const profilesModule = await import("@pi-kaush/pi-model-profiles");
|
|
228
|
+
profiles = profilesModule.loadModelProfiles(
|
|
229
|
+
profilesModule.resolveProfilesPath(getAgentDir()),
|
|
230
|
+
);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
if (isModuleUnavailable(error)) {
|
|
233
|
+
ctx.ui.notify(
|
|
234
|
+
`Agent ${formatAgentDisplayName(agent)}: profile support unavailable; install it with pi install npm:@pi-kaush/pi-model-profiles.`,
|
|
235
|
+
"warning",
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
ctx.ui.notify(
|
|
239
|
+
`Agent ${formatAgentDisplayName(agent)}: ${error instanceof Error ? error.message : error}`,
|
|
240
|
+
"error",
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const profileName = agent.profile!.trim();
|
|
247
|
+
const profile = profiles.profiles[profileName];
|
|
248
|
+
if (!profile) {
|
|
249
|
+
ctx.ui.notify(
|
|
250
|
+
`Agent ${formatAgentDisplayName(agent)}: unknown profile "${profileName}". Available: ${Object.keys(profiles.profiles).join(", ")}.`,
|
|
251
|
+
"error",
|
|
252
|
+
);
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
for (const candidate of profile.candidates) {
|
|
257
|
+
const model = findModel(candidate.model, ctx);
|
|
258
|
+
if (!model) continue;
|
|
259
|
+
if (await pi.setModel(model)) {
|
|
260
|
+
return {
|
|
261
|
+
...(candidate.thinkingLevel !== undefined
|
|
262
|
+
? { thinkingLevel: candidate.thinkingLevel }
|
|
263
|
+
: {}),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
ctx.ui.notify(
|
|
269
|
+
`Agent ${formatAgentDisplayName(agent)}: profile "${profileName}" has no available candidate models. Configured: ${profile.candidates.map((candidate) => candidate.model).join(", ")}.`,
|
|
270
|
+
"error",
|
|
271
|
+
);
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
|
|
203
275
|
async function activateAgent(
|
|
204
276
|
agent: AgentConfig,
|
|
205
277
|
ctx: ExtensionContext,
|
|
@@ -211,7 +283,18 @@ export function registerAgentMode(pi: ExtensionAPI) {
|
|
|
211
283
|
thinkingLevel: baseline.thinkingLevel ?? pi.getThinkingLevel(),
|
|
212
284
|
};
|
|
213
285
|
let thinkingLevel = baseline.thinkingLevel;
|
|
214
|
-
if (agent.model) {
|
|
286
|
+
if (agent.profile && agent.model) {
|
|
287
|
+
ctx.ui.notify(
|
|
288
|
+
`Agent ${formatAgentDisplayName(agent)}: declare either "profile" or "model" in frontmatter, not both.`,
|
|
289
|
+
"error",
|
|
290
|
+
);
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
if (agent.profile) {
|
|
294
|
+
const applied = await applyProfileModels(agent, ctx);
|
|
295
|
+
if (!applied) return false;
|
|
296
|
+
thinkingLevel = applied.thinkingLevel ?? baseline.thinkingLevel;
|
|
297
|
+
} else if (agent.model) {
|
|
215
298
|
const modelSpec = parseAgentModelSpec(agent.model);
|
|
216
299
|
const model = findModel(modelSpec.model, ctx);
|
|
217
300
|
if (!model) {
|