pi-extended-teams 2.5.1 → 2.5.2
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.
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AgentSession } from "@mariozechner/pi-coding-agent";
|
|
1
|
+
import type { AgentSession, ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
type SpawnResourcePlan,
|
|
37
37
|
} from "../resources/spawn-resource-plan";
|
|
38
38
|
import { loadPiRuntimeApi } from "../internal/pi-runtime-api";
|
|
39
|
+
import { createNativeExtensionFactories, isNativeExtensionPath } from "../resources/native-extensions";
|
|
39
40
|
import { preparePrivateAgentSessionDirectory } from "../internal/agent-session-files";
|
|
40
41
|
import { isEligibleNestedReadParent, NESTED_DELEGATION_TOOL_NAMES } from "../runtime/nested-read-agents";
|
|
41
42
|
import { CHILD_AGENT_LIFECYCLE_PROBE, type NestedReadAgentToolBinding } from "../tools/team-tools";
|
|
@@ -1019,6 +1020,7 @@ export async function runReadAgentInProcess(
|
|
|
1019
1020
|
}, 5000);
|
|
1020
1021
|
}
|
|
1021
1022
|
|
|
1023
|
+
const piRuntimeApi = await loadPiRuntimeApi();
|
|
1022
1024
|
const {
|
|
1023
1025
|
createAgentSession,
|
|
1024
1026
|
createEventBus,
|
|
@@ -1026,7 +1028,7 @@ export async function runReadAgentInProcess(
|
|
|
1026
1028
|
getAgentDir,
|
|
1027
1029
|
SessionManager,
|
|
1028
1030
|
SettingsManager,
|
|
1029
|
-
} =
|
|
1031
|
+
} = piRuntimeApi;
|
|
1030
1032
|
const agentDir = getAgentDir();
|
|
1031
1033
|
const projectTrusted = parentProjectTrustForSpawn(ctx, member.cwd);
|
|
1032
1034
|
const resourcePlan = await (options.createResourcePlan ?? createSpawnResourcePlan)({
|
|
@@ -1054,6 +1056,11 @@ export async function runReadAgentInProcess(
|
|
|
1054
1056
|
: [];
|
|
1055
1057
|
const nestedReadDelegationEnabled = nestedReadAgentTools.length > 0;
|
|
1056
1058
|
const childEventBus = createEventBus();
|
|
1059
|
+
const nativeExtensions = createNativeExtensionFactories(piRuntimeApi, resourcePlan.extensionPaths);
|
|
1060
|
+
const extensionFactories = [
|
|
1061
|
+
(pi: Parameters<ExtensionFactory>[0]) => registerSpawnedAgentCommunicationGuard(pi, () => submittedFinalReport !== undefined),
|
|
1062
|
+
...nativeExtensions,
|
|
1063
|
+
];
|
|
1057
1064
|
const loader = new DefaultResourceLoader({
|
|
1058
1065
|
eventBus: childEventBus,
|
|
1059
1066
|
cwd: member.cwd,
|
|
@@ -1061,7 +1068,8 @@ export async function runReadAgentInProcess(
|
|
|
1061
1068
|
settingsManager: childSettingsManager,
|
|
1062
1069
|
noExtensions: true,
|
|
1063
1070
|
additionalExtensionPaths: [...resourcePlan.extensionPaths],
|
|
1064
|
-
|
|
1071
|
+
// Named builtin descriptors exist only on capable hosts; legacy SDK types accept functions only.
|
|
1072
|
+
extensionFactories: extensionFactories as ExtensionFactory[],
|
|
1065
1073
|
noSkills: false,
|
|
1066
1074
|
appendSystemPrompt: [
|
|
1067
1075
|
`You are ${roleLabel} agent '${member.name}' in Pi session '${readTeamName}', running in-process so the lead can follow and control you from Pi.`,
|
|
@@ -1143,9 +1151,14 @@ export async function runReadAgentInProcess(
|
|
|
1143
1151
|
const communicationToolNames = communicationTools.map(tool => tool.name);
|
|
1144
1152
|
const communicationToolNameSet = new Set(communicationToolNames);
|
|
1145
1153
|
const delegationToolNameSet = new Set<string>(NESTED_DELEGATION_TOOL_NAMES);
|
|
1146
|
-
const
|
|
1154
|
+
const loadedExtensions = loader.getExtensions().extensions;
|
|
1155
|
+
const nativeToolNames = new Set(loadedExtensions
|
|
1156
|
+
.filter(extension => isNativeExtensionPath(extension.path))
|
|
1157
|
+
.flatMap(extension => Array.from(extension.tools.keys())));
|
|
1158
|
+
const extensionToolNames = loadedExtensions.flatMap(extension => {
|
|
1147
1159
|
return Array.from(extension.tools.keys()).filter(name =>
|
|
1148
|
-
!communicationToolNameSet.has(name) && !delegationToolNameSet.has(name)
|
|
1160
|
+
!communicationToolNameSet.has(name) && !delegationToolNameSet.has(name)
|
|
1161
|
+
&& !USER_INTERACTION_TOOLS.includes(name) && !nativeToolNames.has(name)
|
|
1149
1162
|
);
|
|
1150
1163
|
});
|
|
1151
1164
|
const nestedReadAgentToolNames = nestedReadAgentTools.map(tool => tool.name);
|
|
@@ -1186,8 +1199,12 @@ export async function runReadAgentInProcess(
|
|
|
1186
1199
|
// sessions retain custom providers and runtime-scoped credentials.
|
|
1187
1200
|
modelRuntime: parentModelRuntime,
|
|
1188
1201
|
modelRegistry: ctx.modelRegistry,
|
|
1189
|
-
tools
|
|
1190
|
-
|
|
1202
|
+
// An SDK allowlist would hide MCP tools registered later during session_start.
|
|
1203
|
+
...(nativeExtensions.length ? {} : { tools: activeToolNames }),
|
|
1204
|
+
excludeTools: [
|
|
1205
|
+
...USER_INTERACTION_TOOLS,
|
|
1206
|
+
...(nativeExtensions.length ? NESTED_DELEGATION_TOOL_NAMES.filter(name => !nestedReadAgentToolNames.includes(name)) : []),
|
|
1207
|
+
],
|
|
1191
1208
|
customTools: [...communicationTools, ...nestedReadAgentTools],
|
|
1192
1209
|
resourceLoader: loader,
|
|
1193
1210
|
settingsManager: childSettingsManager,
|
|
@@ -1195,9 +1212,21 @@ export async function runReadAgentInProcess(
|
|
|
1195
1212
|
} as Parameters<typeof createAgentSession>[0] & { modelRuntime?: unknown });
|
|
1196
1213
|
|
|
1197
1214
|
state.session = session;
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1215
|
+
if (nativeExtensions.length) {
|
|
1216
|
+
const activeNativeTools = session.getActiveToolNames().filter(name => nativeToolNames.has(name));
|
|
1217
|
+
session.setActiveToolsByName([...activeToolNames, ...activeNativeTools]);
|
|
1218
|
+
}
|
|
1219
|
+
// Hosts capture these public hooks when a prompt starts. Never abort from the reporting tool.
|
|
1220
|
+
if (session.agent && "finishTurn" in session.agent) {
|
|
1221
|
+
const agent = session.agent as typeof session.agent & {
|
|
1222
|
+
finishTurn?: (...args: unknown[]) => unknown;
|
|
1223
|
+
};
|
|
1224
|
+
const previous = agent.finishTurn;
|
|
1225
|
+
agent.finishTurn = async (...args) => {
|
|
1226
|
+
const decision = await previous?.(...args);
|
|
1227
|
+
return submittedFinalReport !== undefined ? { action: "end" } : decision;
|
|
1228
|
+
};
|
|
1229
|
+
} else if (session.agent && "shouldStopAfterTurn" in session.agent) {
|
|
1201
1230
|
const agent = session.agent as typeof session.agent & {
|
|
1202
1231
|
shouldStopAfterTurn?: (...args: unknown[]) => boolean | Promise<boolean>;
|
|
1203
1232
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import * as nodeFs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
+
import { isNativeExtensionPath } from "../resources/native-extensions";
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* Build the command used to relaunch pi for teammate processes.
|
|
@@ -40,6 +41,7 @@ export function shellQuote(value: string): string {
|
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
function extensionPathIdentity(source: string): string {
|
|
44
|
+
if (isNativeExtensionPath(source)) return source;
|
|
43
45
|
const absolute = path.resolve(source);
|
|
44
46
|
try {
|
|
45
47
|
return nodeFs.realpathSync(absolute);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ExtensionFactory } from "@mariozechner/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
const NATIVE_FACTORIES = {
|
|
4
|
+
"builtin:mcp": "createMcpExtension",
|
|
5
|
+
"builtin:codemode": "createCodemodeExtension",
|
|
6
|
+
"builtin:tool-search": "createToolSearchExtension",
|
|
7
|
+
} as const;
|
|
8
|
+
|
|
9
|
+
type NativeExtensionPath = keyof typeof NATIVE_FACTORIES;
|
|
10
|
+
|
|
11
|
+
export function isNativeExtensionPath(value: string): value is NativeExtensionPath {
|
|
12
|
+
return Object.hasOwn(NATIVE_FACTORIES, value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NativeExtensionFactory {
|
|
16
|
+
name: string;
|
|
17
|
+
factory: ExtensionFactory;
|
|
18
|
+
builtin: true;
|
|
19
|
+
replaceable: true;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Only selected builtins are instantiated, using exports from the running host. */
|
|
23
|
+
export function createNativeExtensionFactories(api: object, paths: readonly string[]): NativeExtensionFactory[] {
|
|
24
|
+
return paths.filter(isNativeExtensionPath).map(identity => {
|
|
25
|
+
const createFactory: unknown = Reflect.get(api, NATIVE_FACTORIES[identity]);
|
|
26
|
+
if (typeof createFactory !== "function") {
|
|
27
|
+
throw new Error(`Selected extension ${identity} is not supported by this Pi host.`);
|
|
28
|
+
}
|
|
29
|
+
return {
|
|
30
|
+
name: identity.slice("builtin:".length),
|
|
31
|
+
factory: createFactory(),
|
|
32
|
+
builtin: true,
|
|
33
|
+
replaceable: true,
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { loadSettings, type PiExtendedTeamsSettings } from "../../src/utils/settings";
|
|
4
|
+
import { isNativeExtensionPath } from "./native-extensions";
|
|
4
5
|
|
|
5
6
|
const SELF_COMMAND_NAME = "agents-extensions";
|
|
6
7
|
export const EXTENSIONS_COMMAND_DESCRIPTION = "Select which observable loaded Pi extensions spawned agents receive.";
|
|
@@ -19,7 +20,7 @@ export interface ExtensionSourceInfo {
|
|
|
19
20
|
export interface LeadExtensionSnapshot {
|
|
20
21
|
/** Stable, human-readable selector when unique in the current lead snapshot. */
|
|
21
22
|
name: string;
|
|
22
|
-
/** Canonical filesystem identity used for
|
|
23
|
+
/** Canonical filesystem path or native builtin identity used for deduplication. */
|
|
23
24
|
identity: string;
|
|
24
25
|
/** Pi's registered extension entrypoint path, passed to the child unchanged. */
|
|
25
26
|
path: string;
|
|
@@ -81,6 +82,7 @@ export interface CreateSpawnResourcePlanOptions {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
function canonicalPath(filePath: string): string {
|
|
85
|
+
if (isNativeExtensionPath(filePath)) return filePath;
|
|
84
86
|
const absolute = path.resolve(filePath);
|
|
85
87
|
try {
|
|
86
88
|
return fs.realpathSync(absolute);
|
|
@@ -104,7 +106,8 @@ function usableSourceInfo(sourceInfo: ExtensionSourceInfo | undefined): sourceIn
|
|
|
104
106
|
return !!sourceInfo
|
|
105
107
|
&& typeof sourceInfo.path === "string"
|
|
106
108
|
&& sourceInfo.path.length > 0
|
|
107
|
-
&& !sourceInfo.path.startsWith("<")
|
|
109
|
+
&& !sourceInfo.path.startsWith("<")
|
|
110
|
+
&& (!sourceInfo.path.startsWith("builtin:") || isNativeExtensionPath(sourceInfo.path));
|
|
108
111
|
}
|
|
109
112
|
|
|
110
113
|
/**
|
|
@@ -130,7 +133,8 @@ export function snapshotLeadExtensions(pi: LeadRegistrationApi): readonly LeadEx
|
|
|
130
133
|
.filter((command) => command.source === "extension")
|
|
131
134
|
.map((command) => command.sourceInfo),
|
|
132
135
|
...tools
|
|
133
|
-
.filter((tool) => !GENERIC_TOOL_SOURCES.has(tool.sourceInfo?.source ?? "")
|
|
136
|
+
.filter((tool) => !GENERIC_TOOL_SOURCES.has(tool.sourceInfo?.source ?? "")
|
|
137
|
+
|| isNativeExtensionPath(tool.sourceInfo?.path ?? ""))
|
|
134
138
|
.map((tool) => tool.sourceInfo),
|
|
135
139
|
].filter(usableSourceInfo);
|
|
136
140
|
|
|
@@ -13,8 +13,14 @@ export function requireLeadRecipient(recipient: string): void {
|
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
export function registerSpawnedAgentCommunicationGuard(
|
|
16
|
+
export function registerSpawnedAgentCommunicationGuard(
|
|
17
|
+
pi: Pick<ExtensionAPI, "on">,
|
|
18
|
+
reportAccepted: () => boolean = () => false,
|
|
19
|
+
): void {
|
|
17
20
|
pi.on("tool_call", event => {
|
|
21
|
+
if (reportAccepted()) {
|
|
22
|
+
return { block: true, reason: "This agent's final report has been accepted. No further tool calls are allowed." };
|
|
23
|
+
}
|
|
18
24
|
if (USER_INTERACTION_TOOLS.includes(event.toolName)) {
|
|
19
25
|
return { block: true, reason: "Spawned agents cannot contact the user. Send your question to team-lead using send_message." };
|
|
20
26
|
}
|