@henryqw/pi-subagent 18.0.1 → 22.0.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/CONTEXT.md +76 -24
- package/README.md +116 -142
- package/dist/composition.d.ts +11 -0
- package/dist/composition.js +107 -0
- package/dist/direct-herdr.d.ts +17 -0
- package/dist/direct-herdr.js +219 -0
- package/dist/ephemeral.d.ts +2 -2
- package/dist/ephemeral.js +7 -7
- package/dist/git-runtime.d.ts +110 -0
- package/dist/git-runtime.js +472 -0
- package/dist/herdr-runtime.d.ts +137 -0
- package/dist/herdr-runtime.js +1228 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +20 -16
- package/dist/integration-git.d.ts +59 -0
- package/dist/integration-git.js +484 -0
- package/dist/launch-runtime.d.ts +54 -0
- package/dist/launch-runtime.js +195 -0
- package/dist/process.d.ts +9 -0
- package/dist/process.js +11 -0
- package/dist/runner.d.ts +356 -0
- package/dist/runner.js +2710 -0
- package/dist/schema.d.ts +2234 -0
- package/dist/schema.js +1236 -0
- package/dist/store.d.ts +50 -0
- package/dist/store.js +288 -0
- package/dist/working-evidence.d.ts +25 -0
- package/dist/working-evidence.js +261 -0
- package/docs/adr/001-composable-ephemeral-execution.md +3 -3
- package/docs/delegation-routing.html +89 -0
- package/docs/delegation-routing.svg +78 -0
- package/docs/orchestration-architecture.html +238 -0
- package/docs/orchestration-architecture.svg +189 -0
- package/docs/orchestration.md +122 -331
- package/example.png +0 -0
- package/examples/roles/implementer.md +2 -3
- package/extensions/admission.ts +66 -0
- package/extensions/config.ts +51 -37
- package/extensions/isolated.ts +638 -0
- package/extensions/result-transport.ts +13 -103
- package/extensions/role-tools.ts +11 -11
- package/extensions/subagent-command.ts +167 -0
- package/extensions/subagent.ts +264 -540
- package/extensions/task-name.ts +1 -2
- package/extensions/workflow.ts +60 -96
- package/package.json +14 -6
- package/skills/pi-subagent/SKILL.md +35 -0
package/dist/index.d.ts
CHANGED
|
@@ -5,15 +5,16 @@ export { parseRoleMcpAllowlist, roleMcpFlagValue, selectRoleMcpConfig, type Role
|
|
|
5
5
|
export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, DEFAULT_MAX_TURNS, EphemeralSubagentError, EXECUTION_BUDGET_ENV, formatDuration, type EphemeralSubagentActivityEvent, type EphemeralSubagentErrorCode, type EphemeralSubagentExecutionBudget, type EphemeralSubagentExecutor, type EphemeralSubagentExecutorOptions, type EphemeralSubagentResult, type EphemeralSubagentRunInput, type EphemeralSubagentTimeout, } from "./ephemeral.ts";
|
|
6
6
|
export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, type WorktreeDirtyInspection, type WorktreeInfo, type WorktreePayload, } from "./worktree.ts";
|
|
7
7
|
export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, type PreparedReviewEvidence, type PrepareExactReviewEvidenceInput, } from "./review-evidence.ts";
|
|
8
|
-
export
|
|
8
|
+
export { captureWorkingCheckoutBaseline, captureWorkingCheckoutState, prepareWorkingChangeEvidence, sameWorkingCheckoutState, sameWorkingSnapshot, type PreparedWorkingChangeEvidence, type WorkingCheckoutBaseline, type WorkingCheckoutState, type WorkingSnapshotIdentity, } from "./working-evidence.ts";
|
|
9
|
+
export declare const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
|
|
9
10
|
export declare const ROLE_MCP_POLICY_FLAG = "pi-subagent-role-mcps";
|
|
10
11
|
export declare const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
11
|
-
export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "ask_question", "
|
|
12
|
+
export declare const CHILD_EXCLUDED_TOOL_NAMES: readonly ["delegate_task", "ask_question", "subagent_status", "subagent_resume", "subagent_stage", "subagent_abort"];
|
|
12
13
|
export declare const CHILD_EXCLUDED_TOOLS: string;
|
|
13
14
|
export declare const DELEGATE_TASK: {
|
|
14
15
|
readonly id: "pi-subagent/delegateTask";
|
|
15
16
|
readonly label: "Subagent delegation";
|
|
16
|
-
readonly purpose: "Launch
|
|
17
|
+
readonly purpose: "Launch direct or checked isolated Pi delegation.";
|
|
17
18
|
readonly defaultProfile: "fast";
|
|
18
19
|
};
|
|
19
20
|
export type RoleName = string;
|
|
@@ -22,7 +23,6 @@ export interface Role {
|
|
|
22
23
|
description: string;
|
|
23
24
|
modelClass?: ProfileName;
|
|
24
25
|
tools: string[];
|
|
25
|
-
isolation?: string;
|
|
26
26
|
extensions: string[];
|
|
27
27
|
skills: string[];
|
|
28
28
|
mcps?: string[];
|
|
@@ -39,7 +39,6 @@ export interface ResolvedRoleLaunch extends PiLaunch {
|
|
|
39
39
|
}
|
|
40
40
|
export interface PreparedRoleLaunch extends ResolvedRoleLaunch {
|
|
41
41
|
role: RoleName;
|
|
42
|
-
isolation?: "worktree";
|
|
43
42
|
tools: readonly string[];
|
|
44
43
|
systemPrompt: string;
|
|
45
44
|
promptArgIndex: number;
|
|
@@ -93,3 +92,10 @@ export declare function prepareRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">,
|
|
|
93
92
|
export declare function resolveConfiguredRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: ExtensionContext, input: ResolveConfiguredRoleLaunchInput): Promise<PreparedRoleLaunch>;
|
|
94
93
|
/** Restore the system prompt pair after a caller has prepared its launch argv. */
|
|
95
94
|
export declare function finalizeRoleLaunch(prepared: PreparedRoleLaunch): ResolvedRoleLaunch;
|
|
95
|
+
export * from "./composition.ts";
|
|
96
|
+
export * from "./git-runtime.ts";
|
|
97
|
+
export * from "./herdr-runtime.ts";
|
|
98
|
+
export { ISOLATED_MODEL_TASK, RoleLaunchRuntime, type LaunchRuntimeOptions } from "./launch-runtime.ts";
|
|
99
|
+
export * from "./runner.ts";
|
|
100
|
+
export * from "./schema.ts";
|
|
101
|
+
export * from "./store.ts";
|
package/dist/index.js
CHANGED
|
@@ -11,20 +11,21 @@ export { parseRoleMcpAllowlist, roleMcpFlagValue, selectRoleMcpConfig } from "./
|
|
|
11
11
|
export { addUsage, capEphemeralSubagentOutput, createEphemeralSubagentExecutor, DEFAULT_MAX_TURNS, EphemeralSubagentError, EXECUTION_BUDGET_ENV, formatDuration, } from "./ephemeral.js";
|
|
12
12
|
export { createChildWorktree, finalizeChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, worktreeContextNote, } from "./worktree.js";
|
|
13
13
|
export { prepareExactReviewEvidence, REVIEW_MAX_PATCH_BYTES, REVIEW_MAX_PATHS, } from "./review-evidence.js";
|
|
14
|
+
export { captureWorkingCheckoutBaseline, captureWorkingCheckoutState, prepareWorkingChangeEvidence, sameWorkingCheckoutState, sameWorkingSnapshot, } from "./working-evidence.js";
|
|
14
15
|
const CODEX_ALIAS = /^openai-codex-(?:[2-9]|[1-9]\d+)$/;
|
|
15
16
|
const MULTI_CODEX_EXTENSION = fileURLToPath(import.meta.resolve("@henryqw/pi-multi-codex/extensions/multi-codex.ts"));
|
|
16
17
|
const ROLE_MCP_EXTENSION = fileURLToPath(new URL("../extensions/role-mcp.ts", import.meta.url));
|
|
17
18
|
const ROLE_TOOLS_EXTENSION = fileURLToPath(new URL("../extensions/role-tools.ts", import.meta.url));
|
|
18
|
-
export const
|
|
19
|
+
export const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
|
|
19
20
|
export const ROLE_MCP_POLICY_FLAG = "pi-subagent-role-mcps";
|
|
20
21
|
export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
|
|
21
22
|
export const CHILD_EXCLUDED_TOOL_NAMES = [
|
|
22
23
|
"delegate_task",
|
|
23
24
|
"ask_question",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
25
|
+
"subagent_status",
|
|
26
|
+
"subagent_resume",
|
|
27
|
+
"subagent_stage",
|
|
28
|
+
"subagent_abort",
|
|
28
29
|
];
|
|
29
30
|
export const CHILD_EXCLUDED_TOOLS = CHILD_EXCLUDED_TOOL_NAMES.join(",");
|
|
30
31
|
const SYSTEM_PROMPT_FLAG = "--append-system-prompt";
|
|
@@ -32,7 +33,7 @@ const CHILD_IDENTITY_POLICY = "You are a delegated Pi Subagent, not Main. Execut
|
|
|
32
33
|
export const DELEGATE_TASK = {
|
|
33
34
|
id: "pi-subagent/delegateTask",
|
|
34
35
|
label: "Subagent delegation",
|
|
35
|
-
purpose: "Launch
|
|
36
|
+
purpose: "Launch direct or checked isolated Pi delegation.",
|
|
36
37
|
defaultProfile: "fast",
|
|
37
38
|
};
|
|
38
39
|
const cleanText = (value, field, source) => {
|
|
@@ -83,7 +84,7 @@ function roleToolPolicy(role, additionalTools = []) {
|
|
|
83
84
|
function namesMcpAdapter(extension) {
|
|
84
85
|
return extension.toLowerCase().split(/[\\/:@]+/).some((component) => component === "pi-mcp-adapter" || component.startsWith("pi-mcp-adapter."));
|
|
85
86
|
}
|
|
86
|
-
const FORBIDDEN_ROLE_PACKAGE_SOURCE_NAMES = ["pi-
|
|
87
|
+
const FORBIDDEN_ROLE_PACKAGE_SOURCE_NAMES = ["pi-subagent", "pi-mcp-adapter"];
|
|
87
88
|
function rejectForbiddenRolePackageSource(value, role) {
|
|
88
89
|
const components = value.toLowerCase().split(/[\\/:@]+/);
|
|
89
90
|
if (FORBIDDEN_ROLE_PACKAGE_SOURCE_NAMES.some((name) => components.some((component) => component === name || component.startsWith(`${name}.`)))) {
|
|
@@ -98,13 +99,10 @@ function roleModelClass(value, source) {
|
|
|
98
99
|
}
|
|
99
100
|
return value;
|
|
100
101
|
}
|
|
101
|
-
function
|
|
102
|
+
function rejectRetiredRoleIsolation(value, source) {
|
|
102
103
|
if (value === undefined)
|
|
103
104
|
return;
|
|
104
|
-
|
|
105
|
-
throw new Error(`${source}: isolation must be "worktree".`);
|
|
106
|
-
}
|
|
107
|
-
return "worktree";
|
|
105
|
+
throw new Error(`${source}: Role isolation is retired; select mode \"isolated\" on delegate_task instead.`);
|
|
108
106
|
}
|
|
109
107
|
// Built-in Roles resolved from the package-shipped Markdown relative to this module.
|
|
110
108
|
const BUILTIN_ROLE_NAMES = ["implementer", "reviewer", "scout"];
|
|
@@ -118,14 +116,13 @@ function parseRoleFile(file, raw) {
|
|
|
118
116
|
throw new Error(`${file}: ${error instanceof Error ? error.message : String(error)}`);
|
|
119
117
|
}
|
|
120
118
|
const frontmatter = parsed.frontmatter;
|
|
121
|
-
|
|
119
|
+
rejectRetiredRoleIsolation(frontmatter.isolation, file);
|
|
122
120
|
const modelClass = roleModelClass(frontmatter.modelClass, file);
|
|
123
121
|
return {
|
|
124
122
|
name: parseRoleName(frontmatter.name, file),
|
|
125
123
|
description: cleanDisplayText(frontmatter.description, "description", file),
|
|
126
124
|
...(modelClass === undefined ? {} : { modelClass }),
|
|
127
125
|
tools: stringList(frontmatter.tools, "tools", file),
|
|
128
|
-
isolation,
|
|
129
126
|
extensions: extensionList(frontmatter.extensions, file),
|
|
130
127
|
skills: stringList(frontmatter.skills, "skills", file),
|
|
131
128
|
mcps: mcpList(frontmatter.mcps, file),
|
|
@@ -312,10 +309,10 @@ function stripRoleSystemPrompt(rawArgs) {
|
|
|
312
309
|
}
|
|
313
310
|
function prepareResolvedRoleLaunch(roleDefinition, launch, additionalTools = []) {
|
|
314
311
|
const role = parseRoleName(roleDefinition.name);
|
|
315
|
-
|
|
312
|
+
rejectRetiredRoleIsolation(roleDefinition.isolation, `Role ${role}`);
|
|
316
313
|
const tools = Object.freeze(roleToolPolicy(roleDefinition, additionalTools));
|
|
317
314
|
const { args, systemPrompt, promptArgIndex } = stripRoleSystemPrompt(launch.args);
|
|
318
|
-
return { ...launch, args, role,
|
|
315
|
+
return { ...launch, args, role, tools, systemPrompt, promptArgIndex };
|
|
319
316
|
}
|
|
320
317
|
function assertNoMissingRoleSkills(role, launch) {
|
|
321
318
|
if (!launch.missingSkills.length)
|
|
@@ -389,3 +386,10 @@ export function finalizeRoleLaunch(prepared) {
|
|
|
389
386
|
missingSkills: prepared.missingSkills,
|
|
390
387
|
};
|
|
391
388
|
}
|
|
389
|
+
export * from "./composition.js";
|
|
390
|
+
export * from "./git-runtime.js";
|
|
391
|
+
export * from "./herdr-runtime.js";
|
|
392
|
+
export { ISOLATED_MODEL_TASK, RoleLaunchRuntime } from "./launch-runtime.js";
|
|
393
|
+
export * from "./runner.js";
|
|
394
|
+
export * from "./schema.js";
|
|
395
|
+
export * from "./store.js";
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { type WorktreeInfo } from "./worktree.ts";
|
|
2
|
+
import { type CheckBatchEvidence, type CheckCommand, type ReviewEvidence, type WorkspaceIdentity } from "./schema.ts";
|
|
3
|
+
export type StageReceipt = {
|
|
4
|
+
previous: WorkspaceIdentity;
|
|
5
|
+
worker: WorkspaceIdentity;
|
|
6
|
+
tip: WorkspaceIdentity;
|
|
7
|
+
};
|
|
8
|
+
export type GitOutcome<T> = {
|
|
9
|
+
outcome: "ready";
|
|
10
|
+
value: T;
|
|
11
|
+
} | {
|
|
12
|
+
outcome: "blocked" | "conflict" | "drift" | "unknown";
|
|
13
|
+
failure: string;
|
|
14
|
+
possibleResources?: string[];
|
|
15
|
+
};
|
|
16
|
+
export declare class IntegrationGit {
|
|
17
|
+
/** Refresh is allowed only for the exact clean checked-out Main and a strict same-branch descendant. */
|
|
18
|
+
inspectMainAdvance(root: string, from: WorkspaceIdentity, to: WorkspaceIdentity, signal: AbortSignal): Promise<void>;
|
|
19
|
+
/** Persist the prepared path/branch/base before Git is allowed to create anything. */
|
|
20
|
+
allocate(root: string, childId: string, base: WorkspaceIdentity, onPrepared: (info: WorktreeInfo) => Promise<void>, signal: AbortSignal): Promise<GitOutcome<WorktreeInfo>>;
|
|
21
|
+
/** Prove each recorded merge before any new merge or final gate. */
|
|
22
|
+
inspectCombined(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], signal: AbortSignal): Promise<WorkspaceIdentity>;
|
|
23
|
+
/** Main's correction is one exact committed child of the checked staged tip. */
|
|
24
|
+
inspectCorrection(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], from: WorkspaceIdentity, to: WorkspaceIdentity, signal: AbortSignal): Promise<void>;
|
|
25
|
+
/** Main orders calls. On conflict leave MERGE_HEAD and the index untouched for manual resolution. */
|
|
26
|
+
stage(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], worker: WorktreeInfo, candidate: WorkspaceIdentity, signal: AbortSignal): Promise<GitOutcome<StageReceipt>>;
|
|
27
|
+
/** After manual conflict resolution: only the exact two-parent merge is a valid receipt. */
|
|
28
|
+
confirmStage(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], worker: WorktreeInfo, candidate: WorkspaceIdentity, signal: AbortSignal): Promise<GitOutcome<StageReceipt>>;
|
|
29
|
+
/** Reconcile an interrupted merge without retrying it or discarding conflict state. */
|
|
30
|
+
reconcileStage(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], worker: WorktreeInfo, candidate: WorkspaceIdentity, signal: AbortSignal): Promise<GitOutcome<StageReceipt | "not_started">>;
|
|
31
|
+
inspectWorker(root: string, worker: WorktreeInfo, candidate: WorkspaceIdentity, signal: AbortSignal): Promise<void>;
|
|
32
|
+
/** Read-only reconciliation of a lost promotion response; never repeat the merge. */
|
|
33
|
+
reconcilePromotion(root: string, integration: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], signal: AbortSignal, correction?: {
|
|
34
|
+
from: WorkspaceIdentity;
|
|
35
|
+
to: WorkspaceIdentity;
|
|
36
|
+
}): Promise<GitOutcome<WorkspaceIdentity>>;
|
|
37
|
+
/** Checks/review must be persisted for this exact combined tip; never move Main via update-ref. */
|
|
38
|
+
promote(input: {
|
|
39
|
+
root: string;
|
|
40
|
+
integration: WorktreeInfo;
|
|
41
|
+
base: WorkspaceIdentity;
|
|
42
|
+
stages: readonly StageReceipt[];
|
|
43
|
+
checks: CheckBatchEvidence;
|
|
44
|
+
commands: readonly CheckCommand[];
|
|
45
|
+
review?: ReviewEvidence;
|
|
46
|
+
criterion?: string;
|
|
47
|
+
correction?: {
|
|
48
|
+
from: WorkspaceIdentity;
|
|
49
|
+
to: WorkspaceIdentity;
|
|
50
|
+
};
|
|
51
|
+
}, signal: AbortSignal): Promise<GitOutcome<WorkspaceIdentity>>;
|
|
52
|
+
/** Explicit release of an unselected clean checkout. The branch deletion is an exact ref compare-and-swap. */
|
|
53
|
+
release(root: string, info: WorktreeInfo, expected: WorkspaceIdentity, kind: "worktree" | "branch", signal: AbortSignal): Promise<GitOutcome<"removed" | "absent">>;
|
|
54
|
+
/** Cleanup is deliberately separate per resource; callers persist each proven substep. */
|
|
55
|
+
cleanup(root: string, integration: WorktreeInfo, info: WorktreeInfo, base: WorkspaceIdentity, stages: readonly StageReceipt[], promoted: WorkspaceIdentity, kind: "worktree" | "branch", signal: AbortSignal, correction?: {
|
|
56
|
+
from: WorkspaceIdentity;
|
|
57
|
+
to: WorkspaceIdentity;
|
|
58
|
+
}): Promise<GitOutcome<"removed" | "absent">>;
|
|
59
|
+
}
|