@henryqw/pi-subagent 27.10.0 → 27.10.1

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,12 +1,9 @@
1
1
  import { lstat, realpath } from "node:fs/promises";
2
- import { isAbsolute, normalize, relative, sep } from "node:path";
2
+ import { isAbsolute, normalize } from "node:path";
3
3
  import { runProcess as defaultRunProcess } from "./process.js";
4
+ import { isWithin } from "./worktree.js";
4
5
  const GIT_ROOT_TIMEOUT_CAP_MS = 30_000;
5
6
  const REVIEW_PROMPT_MAX_BYTES = 64 * 1024;
6
- function within(root, candidate) {
7
- const fromRoot = relative(root, candidate);
8
- return fromRoot === "" || (fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot));
9
- }
10
7
  function processFailure(result) {
11
8
  const detail = (result.stderr || result.stdout).trim().slice(0, 1_000);
12
9
  return new Error(`git rev-parse --show-toplevel failed with exit ${result.code}${detail ? `: ${detail}` : ""}`);
@@ -45,7 +42,7 @@ export function createCanonicalGitRootResolver(options = {}) {
45
42
  if (canonicalRoot !== root || !rootInfo.isDirectory()) {
46
43
  throw new Error("Git returned a non-canonical repository root.");
47
44
  }
48
- if (!within(root, canonicalCwd)) {
45
+ if (!isWithin(root, canonicalCwd)) {
49
46
  throw new Error("Git repository root does not identify the requested working directory.");
50
47
  }
51
48
  return root;
@@ -5,7 +5,7 @@ import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { setTimeout as sleep } from "node:timers/promises";
7
7
  import { createHerdrClient, hasHerdrErrorCode, herdrCommandFailure, startPiAgent } from "@henryqw/pi-herdr";
8
- import { assertPrivateLease, scanProcessLease } from "./process-lease.js";
8
+ import { assertPrivateLease, PI_SUBAGENT_PROCESS_LEASE, scanProcessLease } from "./process-lease.js";
9
9
  const OPERATION_MS = 30_000;
10
10
  const SESSION_LIMIT = 16 * 1024 * 1024;
11
11
  const ANSWER_LIMIT = 50 * 1024;
@@ -384,7 +384,7 @@ export function createDirectHerdr(pi, cwd, idleMs) {
384
384
  let tabId;
385
385
  let paneId;
386
386
  try {
387
- const response = await herdr.json(["tab", "create", "--workspace", workspaceId, "--cwd", workingDir, "--label", label, "--env", `PI_SUBAGENT_PROCESS_LEASE=${leasePath}`, "--no-focus"], options());
387
+ const response = await herdr.json(["tab", "create", "--workspace", workspaceId, "--cwd", workingDir, "--label", label, "--env", `${PI_SUBAGENT_PROCESS_LEASE}=${leasePath}`, "--no-focus"], options());
388
388
  const created = object(response.result, "tab allocation result");
389
389
  // Capture every returned ID before validation can fail. These are evidence,
390
390
  // not owned tabs; the caller retains this intent even without either ID.
@@ -1,5 +1,5 @@
1
1
  import { lstat, realpath } from "node:fs/promises";
2
- import { createChildWorktree, inspectIndexFlags, inspectWorktreeDirty, WorktreeSetupError, } from "./worktree.js";
2
+ import { createChildWorktree, inspectIndexFlags, inspectWorktreeDirty, isMissing, WorktreeSetupError, } from "./worktree.js";
3
3
  import { prepareExactReviewEvidence } from "./review-evidence.js";
4
4
  import { sameIdentity, } from "./schema.js";
5
5
  import { withTransientLaunch, } from "./runner.js";
@@ -41,16 +41,13 @@ function cloneWorktree(info) {
41
41
  baseCommit: info.baseCommit.toLowerCase(),
42
42
  };
43
43
  }
44
- function pathMissing(error) {
45
- return Boolean(error && typeof error === "object" && error.code === "ENOENT");
46
- }
47
44
  async function pathExists(path) {
48
45
  try {
49
46
  await lstat(path);
50
47
  return true;
51
48
  }
52
49
  catch (error) {
53
- if (pathMissing(error))
50
+ if (isMissing(error))
54
51
  return false;
55
52
  throw error;
56
53
  }
@@ -457,7 +454,7 @@ export class CheckedGitRuntime {
457
454
  return await realpath(path);
458
455
  }
459
456
  catch (error) {
460
- if (pathMissing(error))
457
+ if (isMissing(error))
461
458
  return path;
462
459
  throw error;
463
460
  }
@@ -9,7 +9,7 @@ import { isCleanCommitted, } from "./schema.js";
9
9
  import { buildChangesetTaskPrompt, withTransientLaunch, } from "./runner.js";
10
10
  import { runProcess as defaultRunProcess } from "./process.js";
11
11
  import { EXECUTION_BUDGET_ENV } from "./ephemeral.js";
12
- import { assertPrivateLease, scanProcessLease } from "./process-lease.js";
12
+ import { assertPrivateLease, PI_SUBAGENT_PROCESS_LEASE, scanProcessLease } from "./process-lease.js";
13
13
  import { exactDirectTerminalTurn } from "./direct-herdr.js";
14
14
  import { READABLE_BRANCH_PATTERN } from "./worktree.js";
15
15
  const MIN_HERDR_VERSION = [0, 9, 3];
@@ -25,7 +25,6 @@ const DIAGNOSTIC_LIMIT = 8 * 1024;
25
25
  const SESSION_LIMIT = 16 * 1024 * 1024;
26
26
  const LEASE_MODE = 0o600;
27
27
  const DIRECTORY_MODE = 0o700;
28
- const PROCESS_LEASE_ENV = "PI_SUBAGENT_PROCESS_LEASE";
29
28
  const CORRELATION_TOKEN_PATTERN = /^[A-Za-z0-9_-]{16,128}$/;
30
29
  const HERDR_AGENT_NAME_PATTERN = /^[a-z][a-z0-9_-]{0,31}$/;
31
30
  const SETTLED_AGENT_STATES = new Set(["idle", "done"]);
@@ -803,7 +802,7 @@ export class HerdrHostRuntime {
803
802
  const budget = this.executionBudget?.();
804
803
  const args = [
805
804
  "tab", "create", "--workspace", allocation.workspaceId, "--cwd", allocation.worktreeCwd,
806
- "--label", allocation.label, "--env", `${PROCESS_LEASE_ENV}=${allocation.leasePath}`,
805
+ "--label", allocation.label, "--env", `${PI_SUBAGENT_PROCESS_LEASE}=${allocation.leasePath}`,
807
806
  ...(budget ? ["--env", `${EXECUTION_BUDGET_ENV}=${JSON.stringify({ ...budget, startedAt: this.now() })}`] : []),
808
807
  "--no-focus",
809
808
  ];
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@ export { loadRoleMcpConfig, parseRoleMcpAllowlist } from "./mcp-role.ts";
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, 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 declare const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
8
+ export { PI_SUBAGENT_PROCESS_LEASE } from "./process-lease.ts";
9
9
  export declare const ROLE_MCP_POLICY_FLAG = "pi-subagent-role-mcps";
10
10
  export declare const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
11
11
  /** Read child MCP policy before Pi binds registered extension flag values. */
package/dist/index.js CHANGED
@@ -17,7 +17,7 @@ const MULTI_CODEX_EXTENSION = fileURLToPath(import.meta.resolve("@henryqw/pi-mul
17
17
  const ROLE_MCP_EXTENSION = fileURLToPath(new URL("../extensions/role-mcp.ts", import.meta.url));
18
18
  const ROLE_TOOLS_EXTENSION = fileURLToPath(new URL("../extensions/role-tools.ts", import.meta.url));
19
19
  const GIT_READ_EXTENSION = fileURLToPath(new URL("../extensions/git-read.ts", import.meta.url));
20
- export const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
20
+ export { PI_SUBAGENT_PROCESS_LEASE } from "./process-lease.js";
21
21
  export const ROLE_MCP_POLICY_FLAG = "pi-subagent-role-mcps";
22
22
  export const ROLE_TOOL_POLICY_FLAG = "pi-subagent-role-tools";
23
23
  const CODEMODE_TOOL = "codemode";
@@ -2,9 +2,10 @@ import { randomBytes } from "node:crypto";
2
2
  import { constants } from "node:fs";
3
3
  import { chmod, lstat, mkdtemp, open, realpath, rmdir, unlink } from "node:fs/promises";
4
4
  import { tmpdir } from "node:os";
5
- import { isAbsolute, join, normalize, relative, sep } from "node:path";
5
+ import { isAbsolute, join, normalize } from "node:path";
6
6
  import { resolveConfiguredRoleLaunch } from "./index.js";
7
7
  import { registerModelTask } from "@henryqw/pi-task-models";
8
+ import { isMissing, isWithin } from "./worktree.js";
8
9
  const PROMPT_FLAG = "--append-system-prompt";
9
10
  const DIRECTORY_MODE = 0o700;
10
11
  const PROMPT_MODE = 0o600;
@@ -14,13 +15,6 @@ export const ISOLATED_MODEL_TASK = {
14
15
  purpose: "Resolve one task-scoped isolated Role launch.",
15
16
  defaultProfile: "balanced",
16
17
  };
17
- function isMissing(error) {
18
- return Boolean(error && typeof error === "object" && error.code === "ENOENT");
19
- }
20
- function isWithin(root, candidate) {
21
- const fromRoot = relative(root, candidate);
22
- return fromRoot === "" || (fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot));
23
- }
24
18
  async function removeTransientLaunch(directory, promptPath, promptRequired) {
25
19
  const failures = [];
26
20
  try {
@@ -1,4 +1,5 @@
1
1
  import type { HerdrExecResult } from "@henryqw/pi-herdr";
2
+ export declare const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
2
3
  export declare function assertPrivateLease(path: string, allowMissing: boolean): Promise<boolean>;
3
4
  /** Shared exact process-lease evidence for direct and isolated workers. */
4
5
  export declare function scanProcessLease(path: string, execute: (args: string[]) => Promise<HerdrExecResult>, pid?: number, allowMissing?: boolean): Promise<number[]>;
@@ -1,4 +1,5 @@
1
1
  import { lstat } from "node:fs/promises";
2
+ export const PI_SUBAGENT_PROCESS_LEASE = "PI_SUBAGENT_PROCESS_LEASE";
2
3
  export async function assertPrivateLease(path, allowMissing) {
3
4
  let info;
4
5
  try {
package/dist/schema.d.ts CHANGED
@@ -298,7 +298,6 @@ export type CommandEvidence = Static<typeof CommandEvidenceSchema>;
298
298
  export type CheckBatchEvidence = Static<typeof CheckBatchEvidenceSchema>;
299
299
  export type ReusedCheckSource = NonNullable<CommandEvidence["reusedFrom"]>;
300
300
  export type ReviewEvidence = Static<typeof ReviewEvidenceSchema>;
301
- export type WorkerTermination = Static<typeof WorkerTerminationSchema>;
302
301
  export declare const CLEANUP_KINDS: readonly ["worker_tab", "workspace", "worktree", "branch"];
303
302
  export type CleanupStep = Static<typeof CleanupStepSchema>;
304
303
  export type CleanupKind = CleanupStep["kind"];
@@ -507,18 +506,6 @@ declare const ReviewEvidenceSchema: Type.TObject<{
507
506
  passed: Type.TBoolean;
508
507
  at: Type.TInteger;
509
508
  }>;
510
- declare const WorkerTerminationSchema: Type.TObject<{
511
- status: Type.TUnion<[Type.TLiteral<"terminating">, Type.TLiteral<"terminated">, Type.TLiteral<"unknown">]>;
512
- workerId: Type.TString;
513
- candidate: Type.TObject<{
514
- branch: Type.TString;
515
- head: Type.TString;
516
- index: Type.TString;
517
- tree: Type.TString;
518
- }>;
519
- at: Type.TOptional<Type.TInteger>;
520
- failure: Type.TOptional<Type.TString>;
521
- }>;
522
509
  declare const CleanupStepSchema: Type.TObject<{
523
510
  kind: Type.TUnion<[Type.TLiteral<"worker_tab">, Type.TLiteral<"workspace">, Type.TLiteral<"worktree">, Type.TLiteral<"branch">]>;
524
511
  status: Type.TUnion<[Type.TLiteral<"pending">, Type.TLiteral<"running">, Type.TLiteral<"completed">]>;
@@ -1304,30 +1291,6 @@ declare const IntegrationCandidateSchema: Type.TObject<{
1304
1291
  worker: Type.TUnion<[Type.TLiteral<"retained">, Type.TLiteral<"release_pending">, Type.TLiteral<"released">]>;
1305
1292
  decision: Type.TOptional<Type.TLiteral<"rejected">>;
1306
1293
  }>;
1307
- declare const IntegrationStageSchema: Type.TObject<{
1308
- taskId: Type.TString;
1309
- attempt: Type.TInteger;
1310
- source: Type.TObject<{
1311
- branch: Type.TString;
1312
- head: Type.TString;
1313
- index: Type.TString;
1314
- tree: Type.TString;
1315
- }>;
1316
- onto: Type.TObject<{
1317
- branch: Type.TString;
1318
- head: Type.TString;
1319
- index: Type.TString;
1320
- tree: Type.TString;
1321
- }>;
1322
- status: Type.TUnion<[Type.TLiteral<"pending">, Type.TLiteral<"staging">, Type.TLiteral<"staged">, Type.TLiteral<"conflict">]>;
1323
- tip: Type.TOptional<Type.TObject<{
1324
- branch: Type.TString;
1325
- head: Type.TString;
1326
- index: Type.TString;
1327
- tree: Type.TString;
1328
- }>>;
1329
- failure: Type.TOptional<Type.TString>;
1330
- }>;
1331
1294
  declare const IntegrationGenerationSchema: Type.TObject<{
1332
1295
  number: Type.TInteger;
1333
1296
  status: Type.TUnion<[Type.TLiteral<"staging">, Type.TLiteral<"conflict">, Type.TLiteral<"validating">, Type.TLiteral<"validation_failed">, Type.TLiteral<"ready">, Type.TLiteral<"promoting">, Type.TLiteral<"promoted">, Type.TLiteral<"promotion_unknown">, Type.TLiteral<"promotion_failed">, Type.TLiteral<"superseded">]>;
@@ -1788,7 +1751,6 @@ export declare const IntegrationStateSchema: Type.TObject<{
1788
1751
  }>>;
1789
1752
  }>;
1790
1753
  export type IntegrationCandidate = Static<typeof IntegrationCandidateSchema>;
1791
- export type IntegrationStage = Static<typeof IntegrationStageSchema>;
1792
1754
  export type IntegrationGeneration = Static<typeof IntegrationGenerationSchema>;
1793
1755
  export type IntegrationState = Static<typeof IntegrationStateSchema>;
1794
1756
  declare const TerminalRunStatusSchema: Type.TUnion<[Type.TLiteral<"completed">, Type.TLiteral<"final_failed">, Type.TLiteral<"superseded">, Type.TLiteral<"aborted">]>;
package/dist/store.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { realpathSync } from "node:fs";
3
3
  import { lstat, mkdir, open, readdir, realpath } from "node:fs/promises";
4
- import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
4
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
5
5
  import { extensionConfigDir, readTextFileBounded, writePrivateTextFileAtomically } from "@henryqw/pi-config-store";
6
6
  import { check, lock } from "proper-lockfile";
7
7
  import { parseRunState, RUN_STATE_VERSION } from "./schema.js";
8
8
  import { discardResources } from "./discard.js";
9
+ import { isMissing, isWithin } from "./worktree.js";
9
10
  const INITIAL_STATE_MAX_BYTES = 2 * 1024 * 1024;
10
11
  /*
11
12
  * create() caps the complete initial serialization at 2 MiB. Loaded requests
@@ -30,19 +31,12 @@ export class ProductiveRunLeaseBusyError extends Error {
30
31
  super("Another Pi Subagent productive request is active in this repository.");
31
32
  }
32
33
  }
33
- function isMissing(error) {
34
- return Boolean(error && typeof error === "object" && error.code === "ENOENT");
35
- }
36
34
  function isAlreadyPresent(error) {
37
35
  return Boolean(error && typeof error === "object" && error.code === "EEXIST");
38
36
  }
39
37
  function isLocked(error) {
40
38
  return Boolean(error && typeof error === "object" && error.code === "ELOCKED");
41
39
  }
42
- function isWithin(root, candidate) {
43
- const fromRoot = relative(root, candidate);
44
- return fromRoot === "" || (fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot));
45
- }
46
40
  async function canonicalPlannedPath(path) {
47
41
  let ancestor = resolve(path);
48
42
  const suffix = [];
@@ -1,4 +1,6 @@
1
1
  import { type GitRunner } from "./git-process.ts";
2
+ export declare function isMissing(error: unknown): boolean;
3
+ export declare function isWithin(root: string, candidate: string): boolean;
2
4
  export interface WorktreeInfo {
3
5
  path: string;
4
6
  cwd: string;
package/dist/worktree.js CHANGED
@@ -1,8 +1,15 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { existsSync, lstatSync } from "node:fs";
3
3
  import { appendFile, mkdir, readFile } from "node:fs/promises";
4
- import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { runGit } from "./git-process.js";
6
+ export function isMissing(error) {
7
+ return Boolean(error && typeof error === "object" && error.code === "ENOENT");
8
+ }
9
+ export function isWithin(root, candidate) {
10
+ const fromRoot = relative(root, candidate);
11
+ return fromRoot === "" || (fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`) && !isAbsolute(fromRoot));
12
+ }
6
13
  export const WORKTREES_DIRNAME = ".worktrees";
7
14
  const BRANCH_NAMESPACE = "pi-subagent";
8
15
  const NAME_COMPONENT_LIMIT = 24;
@@ -2,10 +2,8 @@ import { formatConfigIssues, type ConfigLoad } from "@henryqw/pi-config-store";
2
2
  import type { ExecutionPolicySnapshot } from "../dist/schema.js";
3
3
  import type { SubagentConfig } from "@henryqw/pi-subagent";
4
4
 
5
- export type EffectiveExecutionPolicy = ExecutionPolicySnapshot;
6
-
7
5
  /** Resolve one immutable policy. A disabled config blocks delegation. */
8
- export function resolveExecutionPolicy(path: string, loaded: ConfigLoad<SubagentConfig>): EffectiveExecutionPolicy {
6
+ export function resolveExecutionPolicy(path: string, loaded: ConfigLoad<SubagentConfig>): ExecutionPolicySnapshot {
9
7
  if (loaded.status === "disabled") {
10
8
  throw new Error(`${path}: ${formatConfigIssues(loaded.issues)}; delegation is blocked until this file is corrected.`);
11
9
  }
@@ -3,7 +3,6 @@ import type { JsonValue } from "@earendil-works/pi-ai";
3
3
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
4
4
  import { truncateToWidth } from "@earendil-works/pi-tui";
5
5
  import { capEphemeralSubagentOutput, utf8Prefix, type EphemeralSubagentExecutor } from "../dist/ephemeral.js";
6
- import type { EffectiveExecutionPolicy } from "./config.ts";
7
6
  import {
8
7
  createCanonicalGitRootResolver,
9
8
  createExactJudgmentExecutor,
@@ -31,6 +30,7 @@ import {
31
30
  StatusOutputSchema,
32
31
  type CheckBatchEvidence,
33
32
  type ExecuteRequest,
33
+ type ExecutionPolicySnapshot,
34
34
  type ModelClass,
35
35
  type ReviewEvidence,
36
36
  type RunState,
@@ -59,8 +59,8 @@ export type CreateIsolatedComponents = (options: {
59
59
  pi: ExtensionAPI;
60
60
  context(): ExtensionContext;
61
61
  executor: EphemeralSubagentExecutor;
62
- policy: EffectiveExecutionPolicy;
63
- currentPolicy(): EffectiveExecutionPolicy;
62
+ policy: ExecutionPolicySnapshot;
63
+ currentPolicy(): ExecutionPolicySnapshot;
64
64
  onStateSaved(state: RunState): void;
65
65
  }) => IsolatedExtensionComponents;
66
66
 
@@ -531,8 +531,8 @@ export interface IsolatedSurface {
531
531
 
532
532
  export interface RegisterIsolatedOptions {
533
533
  executor: EphemeralSubagentExecutor;
534
- policy: EffectiveExecutionPolicy;
535
- currentPolicy(): EffectiveExecutionPolicy;
534
+ policy: ExecutionPolicySnapshot;
535
+ currentPolicy(): ExecutionPolicySnapshot;
536
536
  /** Report new config issues, and throw when the config now blocks delegation, before each deferred Role launch. */
537
537
  beforeRoleLaunch?(ctx: ExtensionContext): void;
538
538
  componentsFactory?: CreateIsolatedComponents;
@@ -149,12 +149,12 @@ function sourceFor(entry: WorkflowTransportEntry): string | undefined {
149
149
  if (entry.status === "rejected") {
150
150
  return entry.partialOutput ? `${entry.failure}\nPartial output before failure (incomplete; not a success):\n${entry.partialOutput}` : entry.failure;
151
151
  }
152
- if (entry.status === "running" || entry.status === "succeeded") return entry.assistantOutput;
152
+ if (entry.status === "succeeded") return entry.assistantOutput;
153
153
  }
154
154
 
155
155
  function evidenceFor(entry: WorkflowTransportEntry, position: number, total: number): Evidence | undefined {
156
156
  const source = sourceFor(entry);
157
- if (source === undefined || !source) return;
157
+ if (!source) return;
158
158
  const [preview, remainder] = splitEvidence(source);
159
159
  return {
160
160
  heading: `- [${position}/${total}] ${entry.name} · ${entry.role} · ${entry.status === "rejected" ? "failure" : "result"}:`,
@@ -3,8 +3,8 @@ import type { ExtensionAPI, ExtensionContext, Theme } from "@earendil-works/pi-c
3
3
  import { type Component, Text, type TUI, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
4
4
  import { availableTaskModels, loadTaskModelsConfig, modelReference, type ProfileName, registerModelTask, registerTaskModelsConfigReporting, resolveAvailableModel, resolveConfiguredTaskRoute, type ResolvedTaskRoute, taskThinkingLevels } from "@henryqw/pi-task-models";
5
5
  import { registerConfigReporting } from "@henryqw/pi-config-store";
6
- import { capEphemeralSubagentOutput as capOutput, createEphemeralSubagentExecutor, createSubagentConfigStore, DEFAULT_SUBAGENT_CONFIG, DELEGATE_TASK, findRole, formatDuration, loadRoleCatalog, prepareRoleLaunch, resolveRolePackageResources, resolveTaskRoute, ROLE_TOOL_POLICY_FLAG, type Role } from "@henryqw/pi-subagent";
7
- import { resolveExecutionPolicy, type EffectiveExecutionPolicy } from "./config.ts";
6
+ import { capEphemeralSubagentOutput as capOutput, createEphemeralSubagentExecutor, createSubagentConfigStore, DEFAULT_SUBAGENT_CONFIG, DELEGATE_TASK, findRole, formatDuration, loadRoleCatalog, prepareRoleLaunch, resolveRolePackageResources, resolveTaskRoute, ROLE_TOOL_POLICY_FLAG, type ExecutionPolicySnapshot, type Role } from "@henryqw/pi-subagent";
7
+ import { resolveExecutionPolicy } from "./config.ts";
8
8
  import { registerCheckoutAdmission } from "./admission.ts";
9
9
  import { registerIsolatedExtension } from "./isolated.ts";
10
10
  import { directRef, registerSubagentCommand, type DirectDisplay, type DirectTask } from "./subagent-command.ts";
@@ -204,7 +204,7 @@ export default function subagentExtension(pi: ExtensionAPI): void {
204
204
  });
205
205
  // The isolated runner reads the policy lazily without a context, so each read reports through the session's.
206
206
  let latestCtx: ExtensionContext | undefined;
207
- const currentPolicy = (): EffectiveExecutionPolicy => {
207
+ const currentPolicy = (): ExecutionPolicySnapshot => {
208
208
  const loaded = configStore.loadSync();
209
209
  if (latestCtx) configReporter.report(latestCtx, configStore, loaded);
210
210
  const policy = resolveExecutionPolicy(configStore.path, loaded);
@@ -407,8 +407,8 @@ export default function subagentExtension(pi: ExtensionAPI): void {
407
407
  const resolved = role.modelClass === undefined ? resolveConfiguredTaskRoute(ctx, DELEGATE_TASK) : resolveTaskRoute(ctx, role.modelClass);
408
408
  route = { model: modelReference(resolved.model), thinking: resolved.thinkingLevel };
409
409
  } catch (error) { route = { error: message(error) }; }
410
- return { name: role.name, source: catalog.sources.get(role.name) ?? "unknown", modelClass: role.modelClass, route,
411
- tools: role.tools ?? [], extensions: role.extensions ?? [], skills: role.skills ?? [], mcps: role.mcps ?? [] };
410
+ return { name: role.name, source: catalog.sources.get(role.name)!, modelClass: role.modelClass, route,
411
+ tools: role.tools, extensions: role.extensions, skills: role.skills, mcps: role.mcps ?? [] };
412
412
  });
413
413
  return { roles, errors: catalog.errors.map((error) => error.message) };
414
414
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-subagent",
3
- "version": "27.10.0",
3
+ "version": "27.10.1",
4
4
  "description": "Delegate bounded direct work or durable checked isolated task graphs to Pi roles.",
5
5
  "keywords": [
6
6
  "pi-package",