@youngjurry/pi-agents 0.7.2 → 0.7.4
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 +13 -0
- package/README.md +4 -0
- package/control.ts +22 -4
- package/package.json +1 -1
- package/roles.ts +18 -1
- package/tools.ts +9 -1
- package/types.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.4 - 2026-09-05
|
|
4
|
+
|
|
5
|
+
- Match Pi's progressive Skill disclosure for sub-agent Roles instead of eagerly injecting complete `SKILL.md` files.
|
|
6
|
+
- Let an omitted `skills` field inherit all discoverable Skills, an explicit list expose only those Skills, and `skills: []` expose none.
|
|
7
|
+
- Preserve explicit unknown-Skill validation and report inherited Role skills as `*` through on-demand Role discovery.
|
|
8
|
+
|
|
9
|
+
## 0.7.3 - 2026-09-05
|
|
10
|
+
|
|
11
|
+
- Fold the full `wait_agent` status tree by default and reuse Pi's `Ctrl+O` expansion state to reveal it on demand.
|
|
12
|
+
- Keep provider-facing wait results limited to newly queued mailbox notices while avoiding overwhelming TUI output.
|
|
13
|
+
- Add optional Role `skills` frontmatter that injects selected complete `SKILL.md` instructions into child system prompts.
|
|
14
|
+
- Report configured Role skills through on-demand Role discovery and fail clearly when a selected Skill is unavailable.
|
|
15
|
+
|
|
3
16
|
## 0.7.2 - 2026-09-02
|
|
4
17
|
|
|
5
18
|
- Keep execution slots reserved until an `AgentSession` is fully settled and safe to evict.
|
package/README.md
CHANGED
|
@@ -125,6 +125,7 @@ Role format:
|
|
|
125
125
|
name: reviewer
|
|
126
126
|
description: Review code without editing
|
|
127
127
|
tools: read, grep, find, ls, bash
|
|
128
|
+
skills: [document]
|
|
128
129
|
model: openai/gpt-5.4
|
|
129
130
|
thinking: high
|
|
130
131
|
nickname_candidates: [Ada, Grace]
|
|
@@ -133,6 +134,8 @@ nickname_candidates: [Ada, Grace]
|
|
|
133
134
|
Review carefully and return findings with exact paths.
|
|
134
135
|
```
|
|
135
136
|
|
|
137
|
+
`skills` optionally filters Pi's normal progressive Skill disclosure. Omit the field to inherit all discoverable Skills, specify names such as `skills: [document]` to expose only those Skill names/descriptions/paths, or use `skills: []` to expose none. The child still reads a matching `SKILL.md` on demand rather than placing complete Skill instructions in every prompt. As in Pi's main Agent, the catalog is shown only when `read` or `bash` is active. Unknown explicitly selected Skill names fail clearly instead of being silently ignored.
|
|
138
|
+
|
|
136
139
|
## Model configuration
|
|
137
140
|
|
|
138
141
|
Global sub-agent settings live outside the installed package so updates cannot overwrite them:
|
|
@@ -177,6 +180,7 @@ The settings file is optional, but spawning requires a model from either the tas
|
|
|
177
180
|
- Referenced legacy flat child files are migrated when their main session is resumed
|
|
178
181
|
- Parents receive a compact completion notice instead of the full answer; use `list_agents(view="results")` or read the result file on demand
|
|
179
182
|
- Notices to a busy agent are queued safely: `wait_agent` returns them in its own result, and any leftovers are delivered right after a successful recipient turn
|
|
183
|
+
- `wait_agent` sends only newly queued mailbox notices to the model; its full status tree is folded in the TUI by default and can be toggled with `Ctrl+O`
|
|
180
184
|
- Failed notice delivery is re-queued instead of silently discarded
|
|
181
185
|
- Notices pending when a turn is aborted or errors are deferred to the next explicit turn without restarting the interrupted agent
|
|
182
186
|
- The extension never inserts messages between an assistant tool call and its tool result, keeping session history protocol-valid for strict gateways
|
package/control.ts
CHANGED
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
rootAgentInstructions,
|
|
24
24
|
sanitizeForkMessages,
|
|
25
25
|
} from "./context.ts";
|
|
26
|
-
import { discoverRoles, resolveRole } from "./roles.ts";
|
|
26
|
+
import { discoverRoles, resolveRole, selectRoleSkills } from "./roles.ts";
|
|
27
27
|
import {
|
|
28
28
|
DEFAULT_CHILD_THINKING_LEVEL,
|
|
29
29
|
DEFAULT_MAX_CONCURRENT_SUBAGENTS,
|
|
@@ -587,7 +587,12 @@ export class AgentControl {
|
|
|
587
587
|
await this.transcriptToolDefinitionsPromise;
|
|
588
588
|
}
|
|
589
589
|
|
|
590
|
-
private async createLoader(
|
|
590
|
+
private async createLoader(
|
|
591
|
+
cwd: string,
|
|
592
|
+
settingsManager: SettingsManager,
|
|
593
|
+
instructions: string,
|
|
594
|
+
assignedSkillNames?: readonly string[],
|
|
595
|
+
): Promise<DefaultResourceLoader> {
|
|
591
596
|
const selfPath = path.resolve(this.selfExtensionPath);
|
|
592
597
|
const loader = new DefaultResourceLoader({
|
|
593
598
|
cwd,
|
|
@@ -597,7 +602,14 @@ export class AgentControl {
|
|
|
597
602
|
...base,
|
|
598
603
|
extensions: base.extensions.filter((extension) => path.resolve(extension.resolvedPath) !== selfPath),
|
|
599
604
|
}),
|
|
600
|
-
|
|
605
|
+
...(assignedSkillNames === undefined ? {} : {
|
|
606
|
+
skillsOverride: (base: ReturnType<DefaultResourceLoader["getSkills"]>) => ({
|
|
607
|
+
...base,
|
|
608
|
+
skills: selectRoleSkills(assignedSkillNames, base.skills),
|
|
609
|
+
}),
|
|
610
|
+
}),
|
|
611
|
+
systemPromptOverride: (base) => [base || "You are a coding agent.", instructions]
|
|
612
|
+
.filter((part) => part.trim()).join("\n\n"),
|
|
601
613
|
});
|
|
602
614
|
await loader.reload();
|
|
603
615
|
this.captureTranscriptToolDefinitions(loader);
|
|
@@ -1141,6 +1153,7 @@ export class AgentControl {
|
|
|
1141
1153
|
model: role.model || settings.defaultModel,
|
|
1142
1154
|
thinkingLevel: role.thinkingLevel ?? settings.defaultThinkingLevel ?? DEFAULT_CHILD_THINKING_LEVEL,
|
|
1143
1155
|
tools: role.tools,
|
|
1156
|
+
skills: role.skills ?? ["*"],
|
|
1144
1157
|
source: role.source,
|
|
1145
1158
|
}));
|
|
1146
1159
|
}
|
|
@@ -1275,7 +1288,12 @@ export class AgentControl {
|
|
|
1275
1288
|
const forkContext = this.forkContextFromSessionManager(sessionManager);
|
|
1276
1289
|
const role = resolveRole(this.root.cwd, this.root.ctx.isProjectTrusted(), record.role);
|
|
1277
1290
|
const settingsManager = SettingsManager.create(this.root.cwd, getAgentDir());
|
|
1278
|
-
const loader = await this.createLoader(
|
|
1291
|
+
const loader = await this.createLoader(
|
|
1292
|
+
this.root.cwd,
|
|
1293
|
+
settingsManager,
|
|
1294
|
+
this.childInstructions(record, role.systemPrompt),
|
|
1295
|
+
role.skills,
|
|
1296
|
+
);
|
|
1279
1297
|
const runtime = await this.getModelRuntime(this.root.ctx);
|
|
1280
1298
|
const model = runtime.getModel(record.modelProvider, record.modelId) || this.root.model;
|
|
1281
1299
|
if (!model) throw new Error(`model ${record.modelProvider}/${record.modelId} is unavailable`);
|
package/package.json
CHANGED
package/roles.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
4
|
-
import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
4
|
+
import { CONFIG_DIR_NAME, getAgentDir, parseFrontmatter, type Skill } from "@earendil-works/pi-coding-agent";
|
|
5
5
|
import type { AgentRole } from "./types.ts";
|
|
6
6
|
|
|
7
7
|
const BUILTIN_ROLES: AgentRole[] = [
|
|
@@ -31,6 +31,7 @@ type RoleFrontmatter = {
|
|
|
31
31
|
name?: unknown;
|
|
32
32
|
description?: unknown;
|
|
33
33
|
tools?: unknown;
|
|
34
|
+
skills?: unknown;
|
|
34
35
|
model?: unknown;
|
|
35
36
|
thinking?: unknown;
|
|
36
37
|
nickname_candidates?: unknown;
|
|
@@ -44,6 +45,11 @@ function stringList(value: unknown): string[] | undefined {
|
|
|
44
45
|
return result.length > 0 ? result : undefined;
|
|
45
46
|
}
|
|
46
47
|
|
|
48
|
+
function optionalStringList(value: unknown): string[] | undefined {
|
|
49
|
+
if (value === undefined) return undefined;
|
|
50
|
+
return stringList(value) ?? [];
|
|
51
|
+
}
|
|
52
|
+
|
|
47
53
|
function loadDirectory(directory: string, source: "user" | "project"): AgentRole[] {
|
|
48
54
|
if (!fs.existsSync(directory)) return [];
|
|
49
55
|
let entries: fs.Dirent[];
|
|
@@ -67,6 +73,7 @@ function loadDirectory(directory: string, source: "user" | "project"): AgentRole
|
|
|
67
73
|
description: frontmatter.description.trim(),
|
|
68
74
|
systemPrompt: body.trim(),
|
|
69
75
|
tools: stringList(frontmatter.tools),
|
|
76
|
+
skills: optionalStringList(frontmatter.skills),
|
|
70
77
|
model: typeof frontmatter.model === "string" ? frontmatter.model.trim() : undefined,
|
|
71
78
|
thinkingLevel: thinking,
|
|
72
79
|
nicknameCandidates: stringList(frontmatter.nickname_candidates),
|
|
@@ -114,3 +121,13 @@ export function resolveRole(cwd: string, projectTrusted: boolean, name?: string)
|
|
|
114
121
|
}
|
|
115
122
|
return role;
|
|
116
123
|
}
|
|
124
|
+
|
|
125
|
+
export function selectRoleSkills(requestedNames: readonly string[], discoveredSkills: readonly Skill[]): Skill[] {
|
|
126
|
+
const skillsByName = new Map(discoveredSkills.map((skill) => [skill.name, skill]));
|
|
127
|
+
const missing = requestedNames.filter((name) => !skillsByName.has(name));
|
|
128
|
+
if (missing.length > 0) {
|
|
129
|
+
const available = [...skillsByName.keys()].sort().join(", ") || "none";
|
|
130
|
+
throw new Error(`Role references unknown skill(s): ${missing.join(", ")}. Available skills: ${available}`);
|
|
131
|
+
}
|
|
132
|
+
return [...new Set(requestedNames)].map((name) => skillsByName.get(name)!);
|
|
133
|
+
}
|
package/tools.ts
CHANGED
|
@@ -71,7 +71,14 @@ function renderCollaborationResult(
|
|
|
71
71
|
}
|
|
72
72
|
const icon = data.timedOut ? theme.fg("warning", "◷") : theme.fg("success", "✓");
|
|
73
73
|
const lines = [`${icon} ${theme.fg("toolTitle", data.tool)}`];
|
|
74
|
-
|
|
74
|
+
if (data.tool === "wait_agent" && data.targets.length > 0 && !options.expanded) {
|
|
75
|
+
const counts = new Map<string, number>();
|
|
76
|
+
for (const agent of data.targets) counts.set(agent.status, (counts.get(agent.status) ?? 0) + 1);
|
|
77
|
+
const summary = [...counts.entries()].map(([status, count]) => `${count} ${status}`).join(" · ");
|
|
78
|
+
lines.push(` ${theme.fg("muted", `${data.targets.length} agents hidden · ${summary} · Ctrl+O to expand`)}`);
|
|
79
|
+
} else {
|
|
80
|
+
for (const agent of data.targets) lines.push(` ${theme.fg("accent", compactStatus(agent))}`);
|
|
81
|
+
}
|
|
75
82
|
if (data.roles?.length) {
|
|
76
83
|
lines.push(` ${theme.fg("muted", "Roles:")} ${data.roles.map((role) => role.name).join(", ")}`);
|
|
77
84
|
if (options.expanded) {
|
|
@@ -81,6 +88,7 @@ function renderCollaborationResult(
|
|
|
81
88
|
role.model ? `model: ${role.model}` : undefined,
|
|
82
89
|
role.thinkingLevel ? `thinking: ${role.thinkingLevel}` : undefined,
|
|
83
90
|
`tools: ${role.tools?.join(", ") || "default set"}`,
|
|
91
|
+
`skills: ${role.skills.length > 0 ? role.skills.join(", ") : "none"}`,
|
|
84
92
|
].filter(Boolean).join(" · ");
|
|
85
93
|
lines.push(` ${theme.fg("accent", role.name)} — ${role.description}`);
|
|
86
94
|
lines.push(` ${theme.fg("dim", configuration)}`);
|
package/types.ts
CHANGED
|
@@ -146,6 +146,7 @@ export interface AgentRoleView {
|
|
|
146
146
|
model?: string;
|
|
147
147
|
thinkingLevel?: ThinkingLevel;
|
|
148
148
|
tools?: string[];
|
|
149
|
+
skills: string[];
|
|
149
150
|
source: "builtin" | "user" | "project";
|
|
150
151
|
}
|
|
151
152
|
|
|
@@ -154,6 +155,7 @@ export interface AgentRole {
|
|
|
154
155
|
description: string;
|
|
155
156
|
systemPrompt: string;
|
|
156
157
|
tools?: string[];
|
|
158
|
+
skills?: string[];
|
|
157
159
|
model?: string;
|
|
158
160
|
thinkingLevel?: ThinkingLevel;
|
|
159
161
|
nicknameCandidates?: string[];
|