@selesai/code 0.13.22 → 0.13.24
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 +20 -0
- package/dist/core/exec.d.ts +2 -0
- package/dist/core/exec.js +1 -0
- package/dist/extensions/capability-gateway/index.ts +15 -4
- package/dist/extensions/capability-gateway/integration.test.ts +15 -0
- package/dist/extensions/package.json +2 -0
- package/dist/extensions/pi-graft/cli.test.ts +431 -0
- package/dist/extensions/pi-graft/cli.ts +548 -0
- package/dist/extensions/pi-graft/commands.test.ts +480 -0
- package/dist/extensions/pi-graft/commands.ts +392 -0
- package/dist/extensions/pi-graft/graft.integration.test.ts +153 -0
- package/dist/extensions/pi-graft/index.test.ts +167 -0
- package/dist/extensions/pi-graft/index.ts +621 -0
- package/dist/extensions/pi-graft/lifecycle.test.ts +653 -0
- package/dist/extensions/pi-graft/prompt.test.ts +213 -0
- package/dist/extensions/pi-graft/prompt.ts +216 -0
- package/dist/extensions/pi-graft/state.test.ts +258 -0
- package/dist/extensions/pi-graft/state.ts +335 -0
- package/dist/extensions/pi-graft/test-support.ts +199 -0
- package/dist/extensions/pi-graft/tools.test.ts +330 -0
- package/dist/extensions/pi-graft/tools.ts +334 -0
- package/dist/extensions/pi-graft/tsconfig.json +16 -0
- package/dist/extensions/pi-subagents/src/agents/builtin-agent-augmentations.ts +71 -0
- package/dist/extensions/pi-subagents/src/extension/index.ts +9 -3
- package/dist/extensions/pi-subagents/test/unit/builtin-agent-augmentations.test.ts +44 -0
- package/package.json +2 -2
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@selesai/code";
|
|
2
|
+
import type { AgentConfig } from "./agents.ts";
|
|
3
|
+
|
|
4
|
+
/** Public event emitted by pi-subagents while its parent extension is loading. */
|
|
5
|
+
export const BUILTIN_AGENT_AUGMENTATION_REQUEST_EVENT = "pi-subagents:request-builtin-agent-augmentations";
|
|
6
|
+
|
|
7
|
+
export interface BuiltinAgentAugmentation {
|
|
8
|
+
id: string;
|
|
9
|
+
agentNames: readonly string[];
|
|
10
|
+
addTools?: readonly string[];
|
|
11
|
+
subagentOnlyExtensions?: readonly string[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface BuiltinAgentAugmentationRequest {
|
|
15
|
+
register(augmentation: BuiltinAgentAugmentation): void;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function strings(value: unknown, field: string): string[] {
|
|
19
|
+
if (!Array.isArray(value) || value.length === 0 || value.some((entry) => typeof entry !== "string" || !entry.trim() || entry.trim() !== entry)) {
|
|
20
|
+
throw new Error(`Builtin agent augmentation ${field} must be a non-empty array of trimmed strings.`);
|
|
21
|
+
}
|
|
22
|
+
return [...new Set(value)];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function validate(augmentation: BuiltinAgentAugmentation): BuiltinAgentAugmentation {
|
|
26
|
+
if (!augmentation || typeof augmentation !== "object") throw new Error("Builtin agent augmentation must be an object.");
|
|
27
|
+
if (typeof augmentation.id !== "string" || !augmentation.id.trim() || augmentation.id.trim() !== augmentation.id) throw new Error("Builtin agent augmentation id must be a non-empty trimmed string.");
|
|
28
|
+
const agentNames = strings(augmentation.agentNames, "agentNames");
|
|
29
|
+
const addTools = augmentation.addTools === undefined ? undefined : strings(augmentation.addTools, "addTools");
|
|
30
|
+
const subagentOnlyExtensions = augmentation.subagentOnlyExtensions === undefined ? undefined : strings(augmentation.subagentOnlyExtensions, "subagentOnlyExtensions");
|
|
31
|
+
if (!addTools?.length && !subagentOnlyExtensions?.length) throw new Error("Builtin agent augmentation must add tools or child extensions.");
|
|
32
|
+
return { id: augmentation.id, agentNames, ...(addTools ? { addTools } : {}), ...(subagentOnlyExtensions ? { subagentOnlyExtensions } : {}) };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Ask already-loaded extensions for additive patches to pi-subagents builtins. */
|
|
36
|
+
export function requestBuiltinAgentAugmentations(pi: ExtensionAPI): BuiltinAgentAugmentation[] {
|
|
37
|
+
const byId = new Map<string, BuiltinAgentAugmentation>();
|
|
38
|
+
const request: BuiltinAgentAugmentationRequest = {
|
|
39
|
+
register(augmentation) {
|
|
40
|
+
const parsed = validate(augmentation);
|
|
41
|
+
if (byId.has(parsed.id)) throw new Error(`Builtin agent augmentation '${parsed.id}' was registered more than once.`);
|
|
42
|
+
byId.set(parsed.id, parsed);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
pi.events.emit(BUILTIN_AGENT_AUGMENTATION_REQUEST_EVENT, request);
|
|
46
|
+
return [...byId.values()];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Apply patches only to selected builtins. User/project profiles and explicit
|
|
51
|
+
* builtin overrides retain precedence over an extension's defaults.
|
|
52
|
+
*/
|
|
53
|
+
export function applyBuiltinAgentAugmentations(agents: AgentConfig[], augmentations: readonly BuiltinAgentAugmentation[]): AgentConfig[] {
|
|
54
|
+
return agents.map((agent) => {
|
|
55
|
+
if (agent.source !== "builtin") return agent;
|
|
56
|
+
const matches = augmentations.filter((augmentation) => augmentation.agentNames.includes(agent.name));
|
|
57
|
+
if (matches.length === 0) return agent;
|
|
58
|
+
const overridden = new Set(agent.override?.fields ?? []);
|
|
59
|
+
const tools = overridden.has("tools")
|
|
60
|
+
? agent.tools
|
|
61
|
+
: [...new Set([...(agent.tools ?? []), ...matches.flatMap((augmentation) => augmentation.addTools ?? [])])];
|
|
62
|
+
const subagentOnlyExtensions = overridden.has("subagentOnlyExtensions")
|
|
63
|
+
? agent.subagentOnlyExtensions
|
|
64
|
+
: [...new Set([...(agent.subagentOnlyExtensions ?? []), ...matches.flatMap((augmentation) => augmentation.subagentOnlyExtensions ?? [])])];
|
|
65
|
+
return {
|
|
66
|
+
...agent,
|
|
67
|
+
...(tools ? { tools } : {}),
|
|
68
|
+
...(subagentOnlyExtensions ? { subagentOnlyExtensions } : {}),
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -20,6 +20,7 @@ import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
|
20
20
|
import { keyText, type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@selesai/code";
|
|
21
21
|
import { Box, Container, Spacer, Text, truncateToWidth, visibleWidth, wrapTextWithAnsi, type Component } from "@earendil-works/pi-tui";
|
|
22
22
|
import { clearAgentDiscoveryCache, discoverAgentSnapshot, discoverAgents, type AgentConfig, type AgentScope } from "../agents/agents.ts";
|
|
23
|
+
import { applyBuiltinAgentAugmentations, requestBuiltinAgentAugmentations } from "../agents/builtin-agent-augmentations.ts";
|
|
23
24
|
import { appendAdvertisedAgentPrompt, buildAdvertisedAgentPrompt } from "../agents/advertised-agent-prompt.ts";
|
|
24
25
|
import { clearRuntimeAgentsForPi, listRuntimeAgentConfigs, mergeRuntimeAgents } from "../agents/runtime-agent-registry.ts";
|
|
25
26
|
import { registerRuntimeAgentEventListener } from "../agents/runtime-agent-events.ts";
|
|
@@ -420,6 +421,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
420
421
|
return;
|
|
421
422
|
}
|
|
422
423
|
const runtimeRegistry = getRuntimeRegistry();
|
|
424
|
+
const builtinAgentAugmentations = requestBuiltinAgentAugmentations(pi);
|
|
423
425
|
|
|
424
426
|
DIRS.results = ensureAccessibleDir(DIRS.results);
|
|
425
427
|
DIRS.async = ensureAccessibleDir(DIRS.async);
|
|
@@ -540,7 +542,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
540
542
|
advertisedAgents = [];
|
|
541
543
|
if (!advertisedContext) return;
|
|
542
544
|
clearAgentDiscoveryCache();
|
|
543
|
-
advertisedAgents =
|
|
545
|
+
advertisedAgents = discoverAgentsForRuntime(advertisedContext.cwd, "both", advertisedContext.model?.provider).agents
|
|
544
546
|
.filter((agent) => agent.advertise === true);
|
|
545
547
|
};
|
|
546
548
|
const hasResultDeliveryDemand = () => {
|
|
@@ -550,9 +552,13 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
550
552
|
return missionObserverResultCandidateFiles(DIRS.results).length > 0;
|
|
551
553
|
};
|
|
552
554
|
const discoverAgentsForRuntime = (cwd: string, scope: AgentScope, preferredModelProvider?: string) => {
|
|
553
|
-
|
|
555
|
+
const augment = <T extends ReturnType<typeof discoverAgents>>(discovered: T): T => ({
|
|
556
|
+
...discovered,
|
|
557
|
+
agents: applyBuiltinAgentAugmentations(discovered.agents, builtinAgentAugmentations),
|
|
558
|
+
});
|
|
559
|
+
if (listRuntimeAgentConfigs(pi).length === 0) return augment(discoverAgents(cwd, scope, preferredModelProvider));
|
|
554
560
|
const snapshot = discoverAgentSnapshot(cwd, scope, preferredModelProvider, { includeChains: false });
|
|
555
|
-
const discovered = snapshot.effective;
|
|
561
|
+
const discovered = augment(snapshot.effective);
|
|
556
562
|
const all = snapshot.all;
|
|
557
563
|
const configuredAgents: AgentConfig[] = [
|
|
558
564
|
...all.builtin,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { describe, it } from "node:test";
|
|
3
|
+
import { applyBuiltinAgentAugmentations, type BuiltinAgentAugmentation } from "../../src/agents/builtin-agent-augmentations.ts";
|
|
4
|
+
import type { AgentConfig } from "../../src/agents/agents.ts";
|
|
5
|
+
|
|
6
|
+
const graft: BuiltinAgentAugmentation = {
|
|
7
|
+
id: "pi-graft",
|
|
8
|
+
agentNames: ["worker"],
|
|
9
|
+
addTools: ["graft_find_code"],
|
|
10
|
+
subagentOnlyExtensions: ["/extensions/pi-graft/index.ts"],
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function agent(source: AgentConfig["source"], fields: Partial<AgentConfig> = {}): AgentConfig {
|
|
14
|
+
return {
|
|
15
|
+
name: "worker",
|
|
16
|
+
description: "Worker",
|
|
17
|
+
systemPrompt: "Work.",
|
|
18
|
+
systemPromptMode: "replace",
|
|
19
|
+
inheritProjectContext: false,
|
|
20
|
+
inheritGlobalContext: false,
|
|
21
|
+
inheritSkills: false,
|
|
22
|
+
filePath: "/agents/worker.md",
|
|
23
|
+
source,
|
|
24
|
+
tools: ["read"],
|
|
25
|
+
...fields,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
describe("builtin agent augmentations", () => {
|
|
30
|
+
it("augments only an uncustomized selected builtin", () => {
|
|
31
|
+
const [builtin, user, overridden] = applyBuiltinAgentAugmentations([
|
|
32
|
+
agent("builtin"),
|
|
33
|
+
agent("user"),
|
|
34
|
+
agent("builtin", { override: { scope: "user", path: "/settings.json", fields: ["tools", "subagentOnlyExtensions"] } }),
|
|
35
|
+
], [graft]);
|
|
36
|
+
|
|
37
|
+
assert.deepEqual(builtin?.tools, ["read", "graft_find_code"]);
|
|
38
|
+
assert.deepEqual(builtin?.subagentOnlyExtensions, ["/extensions/pi-graft/index.ts"]);
|
|
39
|
+
assert.deepEqual(user?.tools, ["read"]);
|
|
40
|
+
assert.equal(user?.subagentOnlyExtensions, undefined);
|
|
41
|
+
assert.deepEqual(overridden?.tools, ["read"]);
|
|
42
|
+
assert.equal(overridden?.subagentOnlyExtensions, undefined);
|
|
43
|
+
});
|
|
44
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selesai/code",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.24",
|
|
4
4
|
"description": "Maintained, extension-first Pi coding agent with built-in workflows, subagents, web research, questions, skills, and an enhanced terminal UI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"clean": "shx rm -rf dist",
|
|
53
53
|
"dev": "tsx src/cli.ts",
|
|
54
54
|
"dev:print": "tsx src/cli.ts --print",
|
|
55
|
-
"test": "vitest run src/extensions/undo.test.ts src/extensions/tps.test.ts src/extensions/agent-browser.test.ts src/extensions/auto-session-name.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
|
|
55
|
+
"test": "vitest run src/extensions/undo.test.ts src/extensions/tps.test.ts src/extensions/pi-graft src/extensions/agent-browser.test.ts src/extensions/auto-session-name.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
|
|
56
56
|
"test:coverage": "vitest run --coverage src/extensions/undo.test.ts src/extensions/tps.test.ts src/extensions/agent-browser.test.ts src/extensions/auto-session-name.test.ts src/extensions/context-compaction-reminder.test.ts src/extensions/copy-turn.test.ts src/extensions/grep-app/index.test.ts src/extensions/inline-skills.test.ts src/extensions/rtk.test.ts src/extensions/handoff-new.test.ts src/extensions/question/tests src/extensions/ponytail/test src/__tests__/tokenin-onboarding.test.ts src/__tests__/model-registry-defaults.test.ts src/cli/args.test.ts",
|
|
57
57
|
"prepare": "npm run build",
|
|
58
58
|
"build": "npm run clean && tsgo -p tsconfig.build.json && shx chmod +x dist/cli.js dist/rpc-entry.js && npm run copy-assets",
|