@marcoscale98/pi-extensible-workflows 5.17.0-fork.1 → 6.2.1-fork.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.
Files changed (128) hide show
  1. package/CHANGELOG.md +145 -0
  2. package/README.md +2 -2
  3. package/dist/src/agent-execution.d.ts +43 -10
  4. package/dist/src/agent-execution.js +442 -162
  5. package/dist/src/background-widget.d.ts +5 -1
  6. package/dist/src/background-widget.js +5 -10
  7. package/dist/src/decoders.d.ts +2 -3
  8. package/dist/src/decoders.js +59 -37
  9. package/dist/src/execution.d.ts +6 -2
  10. package/dist/src/execution.js +48 -7
  11. package/dist/src/host-delivery.js +5 -8
  12. package/dist/src/host-navigator.js +5 -2
  13. package/dist/src/host-phases.d.ts +1 -0
  14. package/dist/src/host-recovery.d.ts +11 -8
  15. package/dist/src/host-recovery.js +14 -22
  16. package/dist/src/host-runtime.js +56 -5
  17. package/dist/src/host-view.d.ts +6 -1
  18. package/dist/src/host-view.js +46 -20
  19. package/dist/src/host.d.ts +2 -2
  20. package/dist/src/host.js +246 -118
  21. package/dist/src/index.d.ts +1 -2
  22. package/dist/src/index.js +1985 -1690
  23. package/dist/src/index.js.map +3 -3
  24. package/dist/src/paths.d.ts +8 -5
  25. package/dist/src/paths.js +24 -13
  26. package/dist/src/persistence.d.ts +1 -0
  27. package/dist/src/persistence.js +1 -0
  28. package/dist/src/pi-runtime-runner.js +4 -1
  29. package/dist/src/registry.d.ts +9 -7
  30. package/dist/src/registry.js +29 -71
  31. package/dist/src/runtime/agent-runner.d.ts +2 -0
  32. package/dist/src/script-tools.d.ts +27 -0
  33. package/dist/src/script-tools.js +87 -0
  34. package/dist/src/session-lease.d.ts +1 -1
  35. package/dist/src/session-lease.js +45 -11
  36. package/dist/src/settings.d.ts +19 -3
  37. package/dist/src/settings.js +63 -50
  38. package/dist/src/store.d.ts +3 -1
  39. package/dist/src/store.js +17 -1
  40. package/dist/src/trajectory.d.ts +12 -3
  41. package/dist/src/trajectory.js +32 -11
  42. package/dist/src/types.d.ts +129 -45
  43. package/dist/src/types.js +5 -8
  44. package/dist/src/utils.d.ts +21 -1
  45. package/dist/src/utils.js +46 -6
  46. package/dist/src/validation.d.ts +14 -3
  47. package/dist/src/validation.js +143 -87
  48. package/dist/starter/index.js +8 -11
  49. package/dist/starter/index.js.map +1 -1
  50. package/dist/starter/review-loop.d.ts +0 -13
  51. package/dist/starter/review-loop.js +8 -9
  52. package/dist/subagents/index.js +904 -956
  53. package/dist/subagents/index.js.map +3 -3
  54. package/dist/subagents/src/contracts.d.ts +48 -31
  55. package/dist/subagents/src/contracts.js +20 -9
  56. package/dist/subagents/src/index.d.ts +1 -1
  57. package/dist/subagents/src/index.js +14 -8
  58. package/dist/subagents/src/manager.js +52 -73
  59. package/dist/subagents/src/navigator.js +3 -14
  60. package/dist/subagents/src/view.d.ts +1 -1
  61. package/dist/subagents/src/view.js +15 -68
  62. package/dist/subagents/src/worktree.js +1 -1
  63. package/dist/trajectory/assets/index.html +35 -13
  64. package/dist/trajectory/index.js +576 -932
  65. package/dist/trajectory/index.js.map +3 -3
  66. package/dist/trajectory/src/assets/index.html +35 -13
  67. package/dist/trajectory/src/index.js +134 -104
  68. package/dist/trajectory/src/server.js +268 -27
  69. package/dist/trajectory/src/server.js.map +3 -3
  70. package/dist/trajectory/src/startup-mutex.d.ts +17 -0
  71. package/dist/trajectory/src/startup-mutex.js +172 -0
  72. package/examples/workflow-extension-template/README.md +4 -7
  73. package/examples/workflow-extension-template/extension.test.mjs +4 -8
  74. package/examples/workflow-extension-template/index.js +0 -3
  75. package/package.json +10 -10
  76. package/skills/pi-extensible-workflows/SKILL.md +12 -8
  77. package/src/agent-execution.ts +412 -164
  78. package/src/background-widget.ts +5 -11
  79. package/src/decoders.ts +51 -37
  80. package/src/execution.ts +46 -9
  81. package/src/host-delivery.ts +5 -8
  82. package/src/host-navigator.ts +4 -2
  83. package/src/host-phases.ts +1 -1
  84. package/src/host-recovery.ts +23 -28
  85. package/src/host-runtime.ts +44 -6
  86. package/src/host-view.ts +41 -16
  87. package/src/host.ts +229 -142
  88. package/src/index.ts +1 -2
  89. package/src/paths.ts +18 -9
  90. package/src/persistence.ts +1 -0
  91. package/src/pi-runtime-runner.ts +3 -1
  92. package/src/registry.ts +28 -64
  93. package/src/runtime/agent-runner.ts +2 -0
  94. package/src/script-tools.ts +76 -0
  95. package/src/session-lease.ts +34 -6
  96. package/src/settings.ts +56 -50
  97. package/src/store.ts +18 -2
  98. package/src/trajectory.ts +32 -15
  99. package/src/types.ts +82 -34
  100. package/src/utils.ts +42 -5
  101. package/src/validation.ts +121 -80
  102. package/starter/index.ts +0 -1
  103. package/starter/review-loop.ts +8 -9
  104. package/starter/roles/developer.md +0 -1
  105. package/starter/roles/oracle.md +0 -1
  106. package/starter/roles/researcher.md +0 -1
  107. package/starter/roles/reviewer.md +0 -1
  108. package/starter/roles/scout.md +0 -1
  109. package/subagents/README.md +10 -16
  110. package/subagents/src/contracts.ts +21 -11
  111. package/subagents/src/index.ts +17 -8
  112. package/subagents/src/manager.ts +48 -128
  113. package/subagents/src/navigator.ts +4 -13
  114. package/subagents/src/view.ts +16 -63
  115. package/subagents/src/worktree.ts +1 -1
  116. package/trajectory/src/assets/index.html +35 -13
  117. package/trajectory/src/index.ts +112 -91
  118. package/trajectory/src/server.ts +46 -10
  119. package/trajectory/src/startup-mutex.ts +127 -0
  120. package/dist/src/roles.d.ts +0 -70
  121. package/dist/src/roles.js +0 -325
  122. package/dist/starter/roles/developer.md +0 -6
  123. package/dist/starter/roles/oracle.md +0 -18
  124. package/dist/starter/roles/researcher.md +0 -20
  125. package/dist/starter/roles/reviewer.md +0 -19
  126. package/dist/starter/roles/scout.md +0 -18
  127. package/examples/workflow-extension-template/roles/reviewer.md +0 -4
  128. package/src/roles.ts +0 -326
@@ -1,7 +1,7 @@
1
1
  import { join } from "node:path";
2
2
  import { defineTool, getAgentDir, type AgentToolResult, type ExtensionAPI, type ExtensionContext, type ToolDefinition } from "@earendil-works/pi-coding-agent";
3
3
  import { Value } from "typebox/value";
4
- import { WorkflowError, loadingRegistry } from "../../src/index.js";
4
+ import { WorkflowError, loadCodemodeToolsSetting, loadingRegistry, reachableTools, workflowToolExposure, type JsonValue } from "../../src/index.js";
5
5
  import { clearSubagentManager, setSubagentManager } from "../../src/subagent-manager-handle.js";
6
6
  import type { SubagentIdRequest, SubagentInspectRequest, SubagentManager, SubagentManagerContext, SubagentNotification, SubagentsExtension, SubagentsExtensionOptions, SubagentRunRequest, SubagentStatus, SubagentSteerRequest } from "./contracts.js";
7
7
  import { createSubagentManager } from "./manager.js";
@@ -9,10 +9,13 @@ import { registerSubagentNavigator } from "./navigator.js";
9
9
  import { createSubagentBackgroundWidget, renderSubagentCall, renderSubagentControlCall, renderSubagentControlResult, renderSubagentInspectCall, renderSubagentInspectResult, renderSubagentResult } from "./view.js";
10
10
  import {
11
11
  normalizeSubagentRunRequest,
12
+ SUBAGENT_STATUS_OUTPUT,
12
13
  SUBAGENTS_ID_PARAMETERS,
14
+ SUBAGENTS_INSPECT_OUTPUT,
13
15
  SUBAGENTS_INSPECT_PARAMETERS,
14
16
  SUBAGENTS_RETRY_PARAMETERS,
15
17
  SUBAGENTS_RUN_PARAMETERS,
18
+ SUBAGENTS_STEER_OUTPUT,
16
19
  SUBAGENTS_STEER_PARAMETERS,
17
20
  SUBAGENTS_STOP_PARAMETERS,
18
21
  } from "./contracts.js";
@@ -23,7 +26,7 @@ export { createSubagentManager, createUnavailableSubagentManager } from "./manag
23
26
  export { createRunStoreWorktreeAdapter, defaultWorktreeHome } from "./worktree.js";
24
27
  export type { SubagentWorktreeAdapter, SubagentWorktreeContext, SubagentWorktreeHandle, SubagentWorktreeRunStore } from "./worktree.js";
25
28
 
26
- type SubagentsExtensionAPI = Pick<ExtensionAPI, "registerTool"> & Partial<Pick<ExtensionAPI, "getActiveTools" | "on" | "sendMessage" | "registerCommand" | "appendEntry" | "registerEntryRenderer">>;
29
+ type SubagentsExtensionAPI = Pick<ExtensionAPI, "registerTool"> & Partial<Pick<ExtensionAPI, "events" | "getActiveTools" | "getAllTools" | "on" | "sendMessage" | "registerCommand" | "appendEntry" | "registerEntryRenderer">>;
27
30
 
28
31
  function validateSubagentRunRequest(value: unknown): SubagentRunRequest {
29
32
  return normalizeSubagentRunRequest(value);
@@ -55,7 +58,7 @@ function serialize(value: unknown): string {
55
58
  }
56
59
 
57
60
  function toolResult(value: unknown): AgentToolResult<unknown> {
58
- return { content: [{ type: "text", text: serialize(value) }], details: value };
61
+ return { content: [{ type: "text", text: serialize(value) }], details: value, structuredContent: value as JsonValue };
59
62
  }
60
63
 
61
64
  function managerContext(toolCallId: string, signal: AbortSignal | undefined, onUpdate: ((value: AgentToolResult<unknown>) => void) | undefined, context: ExtensionContext): SubagentManagerContext {
@@ -78,6 +81,7 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
78
81
  "Do not poll a running ID; use subagents_inspect({ id }) when you need its current status or terminal result.",
79
82
  ],
80
83
  parameters: SUBAGENTS_RUN_PARAMETERS,
84
+ outputSchema: SUBAGENT_STATUS_OUTPUT,
81
85
  async execute(toolCallId, params, signal, onUpdate, context) {
82
86
  return toolResult(await manager.run(validateSubagentRunRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
83
87
  },
@@ -87,8 +91,9 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
87
91
  defineTool({
88
92
  name: "subagents_inspect",
89
93
  label: "Subagents Inspect",
90
- description: "Inspect durable subagent runs. Omit id for ordered run summaries, or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
94
+ description: "Inspect durable subagent runs. Omit id for ordered run summaries of the current session (scope all for every session), or provide id for detailed status, progress, activity, accounting, tool calls, timestamps, worktree metadata, and a terminal value or error when available.",
91
95
  parameters: SUBAGENTS_INSPECT_PARAMETERS,
96
+ outputSchema: SUBAGENTS_INSPECT_OUTPUT,
92
97
  async execute(toolCallId, params, signal, onUpdate, context) {
93
98
  return toolResult(await manager.inspect(validateSubagentInspectRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
94
99
  },
@@ -100,6 +105,7 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
100
105
  label: "Subagents Steer",
101
106
  description: "Send a message to a running subagent. The message is queued safely if its steering handler is not ready; settled runs cannot be steered.",
102
107
  parameters: SUBAGENTS_STEER_PARAMETERS,
108
+ outputSchema: SUBAGENTS_STEER_OUTPUT,
103
109
  async execute(toolCallId, params, signal, onUpdate, context) {
104
110
  return toolResult(await manager.steer(validateSubagentSteerRequest(params), managerContext(toolCallId, signal, onUpdate, context)));
105
111
  },
@@ -111,6 +117,7 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
111
117
  label: "Subagents Stop",
112
118
  description: "Stop one running subagent, abort its active session, persist state \"stopped\", and clean its worktree without affecting sibling runs.",
113
119
  parameters: SUBAGENTS_STOP_PARAMETERS,
120
+ outputSchema: SUBAGENT_STATUS_OUTPUT,
114
121
  async execute(toolCallId, params, signal, onUpdate, context) {
115
122
  return toolResult(await manager.stop(validateSubagentIdRequest(params, "subagents_stop"), managerContext(toolCallId, signal, onUpdate, context)));
116
123
  },
@@ -122,6 +129,7 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
122
129
  label: "Subagents Retry",
123
130
  description: "Start a fresh run from a failed or stopped subagent's persisted request. The new run gets a new ID and preserves its original background or foreground mode.",
124
131
  parameters: SUBAGENTS_RETRY_PARAMETERS,
132
+ outputSchema: SUBAGENT_STATUS_OUTPUT,
125
133
  async execute(toolCallId, params, signal, onUpdate, context) {
126
134
  return toolResult(await manager.retry(validateSubagentIdRequest(params, "subagents_retry"), managerContext(toolCallId, signal, onUpdate, context)));
127
135
  },
@@ -133,8 +141,7 @@ export function createSubagentTools(manager: SubagentManager): readonly ToolDefi
133
141
 
134
142
  function notificationContent(notification: SubagentNotification): string {
135
143
  const label = notification.label?.trim();
136
- const role = notification.role?.trim() || "none";
137
- const identity = label ? `${label} role=${role} (${notification.id})` : `${notification.id} role=${role}`;
144
+ const identity = label ? `${label} (${notification.id})` : notification.id;
138
145
  if (notification.state === "completed") return `Subagent ${identity} completed. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
139
146
  return `Subagent ${identity} failed: ${notification.error?.message ?? "unknown error"}. Inspect it with subagents_inspect({ id: "${notification.id}" }).`;
140
147
  }
@@ -162,7 +169,8 @@ export function createSubagentsExtension(options: SubagentsExtensionOptions = {}
162
169
 
163
170
  export function registerSubagentsExtension(pi: SubagentsExtensionAPI, options: SubagentsExtensionOptions = {}): SubagentsExtension {
164
171
  const getActiveTools = pi.getActiveTools;
165
- const activeTools = getActiveTools === undefined ? undefined : () => getActiveTools.call(pi);
172
+ const getAllTools = pi.getAllTools;
173
+ const activeTools = getActiveTools === undefined ? undefined : () => reachableTools({ getActiveTools: () => getActiveTools.call(pi), ...(getAllTools === undefined ? {} : { getAllTools: () => getAllTools.call(pi) }) });
166
174
  const sendMessage = pi.sendMessage;
167
175
  const notify = sendMessage === undefined ? undefined : (notification: SubagentNotification): void => {
168
176
  sendMessage.call(pi, { customType: "subagents", content: notificationContent(notification), display: true, details: notification }, { deliverAs: "steer", triggerTurn: true });
@@ -173,7 +181,8 @@ export function registerSubagentsExtension(pi: SubagentsExtensionAPI, options: S
173
181
  const appendEntry = pi.appendEntry;
174
182
  const widget = createSubagentBackgroundWidget({ ...(appendEntry === undefined ? {} : { appendEntry: (customType, data) => { appendEntry.call(pi, customType, data); } }), ...(pi.registerEntryRenderer === undefined ? {} : { registerEntryRenderer: pi.registerEntryRenderer.bind(pi) }) });
175
183
  const extension = createSubagentsExtension(options, activeTools, notify, (status, request) => { widget.update(status, request); const registry = loadingRegistry(); if (typeof registry.observeSubagentStatus === "function") registry.observeSubagentStatus(status, request); }, onResourceWarning);
176
- for (const tool of extension.tools) pi.registerTool(tool);
184
+ const codemodeTools = loadCodemodeToolsSetting(options.managerDependencies?.agentDir);
185
+ for (const tool of extension.tools) pi.registerTool({ ...tool, ...workflowToolExposure(tool.name, codemodeTools) });
177
186
  if (pi.registerCommand !== undefined) registerSubagentNavigator(pi.registerCommand.bind(pi), extension.manager, storageDirectory(options), options.clipboard);
178
187
  if (pi.on !== undefined) {
179
188
  setSubagentManager(extension.manager);
@@ -2,67 +2,11 @@ import { randomUUID } from "node:crypto";
2
2
  import { chmod, link, mkdir, open, readFile, readdir, rename, rm, stat } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
4
  import { getAgentDir } from "@earendil-works/pi-coding-agent";
5
- import {
6
- addAccounting,
7
- deepFreeze,
8
- errorCode,
9
- errorText,
10
- finiteNumber,
11
- isNodeError,
12
- isThinkingLevel,
13
- jsonValue,
14
- object,
15
- loadingRegistry,
16
- localAgentTransport,
17
- resolveAgentResourcePolicy,
18
- resolveWorkflowSettings,
19
- roleNameOf,
20
- sanitizeDisplayText,
21
- sumAccounting,
22
- structuralPath,
23
- validateModelAliasAvailability,
24
- WorkflowAgentExecutor,
25
- WorkflowError,
26
- workflowProjectSettingsPath,
27
- workflowSettingsPath,
28
- type AgentAccounting,
29
- type AgentAttempt,
30
- type AgentAttemptSummary,
31
- type AgentDefinition,
32
- type AgentExecutionOptions,
33
- type AgentExecutionRoot,
34
- type AgentProgress,
35
- type AgentToolCallProgress,
36
- type JsonSchema,
37
- type JsonValue,
38
- type ModelSpec,
39
- type WorkflowAgentSessionState,
40
- type WorkflowExtensionSettings,
41
- type WorkflowRunContext,
42
- SerialLane,
43
- } from "../../src/index.js";
44
- import { decodeAgentDefinition, decodeWorkflowExtensions } from "../../src/decoders.js";
45
- import { loadAgentDefinitions } from "../../src/roles.js";
5
+ import { addAccounting, errorCode, errorText, finiteNumber, isNodeError, isThinkingLevel, jsonValue, object, loadingRegistry, localAgentTransport, resolveAgentResourcePolicy, resolveWorkflowSettings, sanitizeDisplayText, sumAccounting, structuralPath, validateModelAliasAvailability, WorkflowAgentExecutor, WorkflowError, workflowSettingsPath, type AgentAccounting, type AgentAttempt, type AgentAttemptSummary, type PreparedAgentConfiguration, type AgentExecutionOptions, type AgentExecutionRoot, type AgentProgress, type AgentToolCallProgress, type JsonSchema, type JsonValue, type ModelSpec, type WorkflowAgentSessionState, type WorkflowRunContext, SerialLane } from "../../src/index.js";
6
+ import { decodePreparedAgentConfiguration } from "../../src/decoders.js";
46
7
  import { atomicJson, json as readJson, processAlive } from "../../src/persistence.js";
47
8
  import { accountingValue, activityValue, legacyAccountingValue, worktreeValue } from "./decode.js";
48
- import {
49
- SUBAGENT_ATTEMPT_DETAILS_LIMIT,
50
- SUBAGENT_MAX_RETRIES,
51
- SUBAGENT_SYSTEM_PROMPT_LIMIT,
52
- SUBAGENTS_TOOL_NAMES,
53
- normalizeSubagentRunRequest,
54
- type SubagentInspectRequest,
55
- type SubagentLiveness,
56
- type SubagentAttemptActionData,
57
- type SubagentManager,
58
- type SubagentManagerContext,
59
- type SubagentManagerDependencies,
60
- type SubagentNotification,
61
- type SubagentOwnerMarker,
62
- type SubagentProgress,
63
- type SubagentRunRequest,
64
- type SubagentStatus,
65
- } from "./contracts.js";
9
+ import { SUBAGENT_ATTEMPT_DETAILS_LIMIT, SUBAGENT_MAX_RETRIES, SUBAGENT_SYSTEM_PROMPT_LIMIT, SUBAGENTS_TOOL_NAMES, normalizeSubagentRunRequest, type SubagentInspectRequest, type SubagentLiveness, type SubagentAttemptActionData, type SubagentManager, type SubagentManagerContext, type SubagentManagerDependencies, type SubagentNotification, type SubagentOwnerMarker, type SubagentProgress, type SubagentRunRequest, type SubagentStatus } from "./contracts.js";
66
10
  import { createRunStoreWorktreeAdapter, defaultWorktreeHome, type SubagentWorktreeContext, type SubagentWorktreeHandle } from "./worktree.js";
67
11
 
68
12
  const WORKFLOW_NAME = "subagents";
@@ -77,12 +21,7 @@ const MAX_PERSISTED_ATTEMPT_ARRAY_ITEMS = SUBAGENT_MAX_RETRIES + 1;
77
21
  const MAX_PERSISTED_ATTEMPT_STRING_CHARS = 4096;
78
22
  const MAX_PERSISTED_ATTEMPT_LOCATOR_CHARS = 16 * 1024;
79
23
  const SUBAGENT_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
80
- type StandaloneExternalConfiguration = Readonly<{
81
- projectTrusted: boolean;
82
- globalExtensionSettings?: Readonly<WorkflowExtensionSettings>;
83
- projectExtensionSettings?: Readonly<WorkflowExtensionSettings>;
84
- agentDefinitions: Readonly<Record<string, AgentDefinition>>;
85
- }>;
24
+ type StandaloneExternalConfiguration = Readonly<PreparedAgentConfiguration>;
86
25
  class InvalidPersistedSubagentStatusError extends WorkflowError {
87
26
  constructor() { super("INTERNAL_ERROR", "Persisted subagent status is invalid"); }
88
27
  }
@@ -169,22 +108,11 @@ function rootModel(context: Readonly<SubagentManagerContext>): ModelSpec {
169
108
  const thinking = context.extensionContext.thinkingLevel;
170
109
  return { provider: model.provider, model: model.id, ...(thinking === undefined ? {} : { thinking }) };
171
110
  }
172
- function standaloneExtensionSettingsPath(cwd: string, agentDir: string, trustedProject: boolean, external: StandaloneExternalConfiguration): string {
173
- return trustedProject && external.projectExtensionSettings !== undefined ? workflowProjectSettingsPath(cwd) : workflowSettingsPath(agentDir);
174
- }
175
- function validateStandaloneExtensionSources(context: Readonly<SubagentManagerContext>, external: StandaloneExternalConfiguration, registry: ReturnType<typeof loadingRegistry>, agentDir: string): void {
176
- const cwd = context.extensionContext.cwd;
177
- const globalSettingsPath = workflowSettingsPath(agentDir);
178
- const trustedProject = context.extensionContext.isProjectTrusted();
179
- if (external.globalExtensionSettings !== undefined) registry.validateExtensionSettings(external.globalExtensionSettings, { source: "global", cwd, projectTrusted: trustedProject, settingsPath: globalSettingsPath });
180
- if (trustedProject && external.projectExtensionSettings !== undefined) registry.validateExtensionSettings(external.projectExtensionSettings, { source: "project", cwd, projectTrusted: true, settingsPath: workflowProjectSettingsPath(cwd) });
181
- }
182
- function executionRoot(context: Readonly<SubagentManagerContext>, dependencies: Readonly<SubagentManagerDependencies>, signal: AbortSignal, runId: string, worktree: SubagentWorktreeHandle | undefined, external: StandaloneExternalConfiguration): AgentExecutionRoot {
111
+ function executionRoot(context: Readonly<SubagentManagerContext>, dependencies: Readonly<SubagentManagerDependencies>, signal: AbortSignal, runId: string, worktree: SubagentWorktreeHandle | undefined): AgentExecutionRoot {
183
112
  const extensionContext = context.extensionContext;
184
113
  const model = rootModel(context);
185
114
  const agentDir = dependencies.agentDir ?? getAgentDir();
186
115
  const currentTrustedProject = extensionContext.isProjectTrusted();
187
- if (external.projectTrusted && !currentTrustedProject) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
188
116
  const trustedProject = currentTrustedProject;
189
117
  const settingsPath = workflowSettingsPath(agentDir);
190
118
  const settings = resolveWorkflowSettings(extensionContext.cwd, trustedProject, settingsPath);
@@ -207,11 +135,13 @@ function executionRoot(context: Readonly<SubagentManagerContext>, dependencies:
207
135
  signal,
208
136
  };
209
137
  const registry = loadingRegistry();
210
- validateStandaloneExtensionSources(context, external, registry, agentDir);
211
- const extensionSettings = trustedProject && external.projectExtensionSettings !== undefined ? external.projectExtensionSettings : external.globalExtensionSettings;
212
- const extensionSettingsPath = standaloneExtensionSettingsPath(extensionContext.cwd, agentDir, trustedProject, external);
138
+ if (settings.global.extensionSettings !== undefined) registry.validateExtensionSettings(settings.global.extensionSettings, { source: "global", cwd: extensionContext.cwd, projectTrusted: trustedProject, settingsPath: settings.globalSettingsPath });
139
+ if (trustedProject && settings.project.extensionSettings !== undefined) registry.validateExtensionSettings(settings.project.extensionSettings, { source: "project", cwd: extensionContext.cwd, projectTrusted: true, settingsPath: settings.projectSettingsPath });
140
+ const extensionSettings = settings.effective.extensionSettings;
141
+ const extensionSettingsPath = settings.sources.extensionSettings ?? settingsPath;
213
142
  return {
214
143
  cwd: extensionContext.cwd,
144
+ projectTrusted: trustedProject,
215
145
  model,
216
146
  tools,
217
147
  resourceSelectors: resourcePolicy.effective,
@@ -221,9 +151,10 @@ function executionRoot(context: Readonly<SubagentManagerContext>, dependencies:
221
151
  availableModels,
222
152
  knownModels,
223
153
  ...(Object.keys(staticAliases).length ? { modelAliases: staticAliases } : {}),
154
+ dynamicModelAliasNames: [],
224
155
  settingsPath: settings.sources.modelAliases,
225
156
  extensionSettingsPath,
226
- agentDefinitions: external.agentDefinitions,
157
+ agentPreparationHooks: registry.agentPreparationHooks(),
227
158
  agentSetupHooks: registry.agentSetupHooks(),
228
159
  validateExtensionSettings: registry.validateExtensionSettings,
229
160
  agentResourcePolicy: () => structuredClone(resourcePolicy),
@@ -240,12 +171,11 @@ async function addDynamicAliases(context: Readonly<SubagentManagerContext>, sign
240
171
  const trustedProject = resourcePolicy?.projectTrusted ?? context.extensionContext.isProjectTrusted();
241
172
  const dynamicAliases = await registry.resolveModelAliases({ cwd: context.extensionContext.cwd, projectTrusted: trustedProject, rootModel: root.model, knownModels: root.knownModels ?? new Set(), availableModels: root.availableModels ?? new Set(), signal }, new Set(Object.keys(staticAliases)));
242
173
  validateModelAliasAvailability(dynamicAliases, Object.keys(dynamicAliases), root.availableModels ?? new Set(), root.knownModels ?? new Set(), root.settingsPath);
243
- return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases } };
174
+ return { ...root, modelAliases: { ...dynamicAliases, ...staticAliases }, dynamicModelAliasNames: Object.keys(dynamicAliases) };
244
175
  }
245
176
 
246
177
  function executionOptions(request: Readonly<SubagentRunRequest>, onAttempt: NonNullable<AgentExecutionOptions["onAttempt"]>, onProgress: NonNullable<AgentExecutionOptions["onProgress"]>): AgentExecutionOptions {
247
- const role = request.role;
248
- const label = request.label ?? roleNameOf(role) ?? "subagent";
178
+ const label = request.label ?? "subagent";
249
179
  return {
250
180
  label,
251
181
  workflowName: WORKFLOW_NAME,
@@ -254,11 +184,11 @@ function executionOptions(request: Readonly<SubagentRunRequest>, onAttempt: NonN
254
184
  ...(request.skills === undefined ? {} : { skills: request.skills }),
255
185
  ...(request.extensions === undefined ? {} : { extensions: request.extensions }),
256
186
  ...(request.contextFiles === undefined ? {} : { contextFiles: request.contextFiles as NonNullable<AgentExecutionOptions["contextFiles"]> }),
257
- ...(role === undefined ? {} : { role }),
258
187
  ...(request.worktree === undefined ? {} : { worktreeOwner: structuralPath("worktree", "named", request.worktree) }),
259
188
  ...(request.outputSchema === undefined ? {} : { schema: request.outputSchema as JsonSchema }),
260
189
  ...(request.retries === undefined ? {} : { retries: request.retries }),
261
190
  ...(request.timeoutMs === undefined ? {} : { timeoutMs: request.timeoutMs }),
191
+ agentOptions: Object.fromEntries(Object.entries(request).filter(([key]) => !["prompt", "mode", "worktree"].includes(key))) as Record<string, JsonValue>,
262
192
  onAttempt,
263
193
  onProgress,
264
194
  };
@@ -398,32 +328,8 @@ function checkedRequest(request: unknown): SubagentRunRequest {
398
328
  return normalizeSubagentRunRequest(request);
399
329
  }
400
330
  function decodePersistedStandaloneExternalConfiguration(value: unknown): StandaloneExternalConfiguration | undefined {
401
- if (!object(value) || !jsonValue(value) || typeof value.projectTrusted !== "boolean") return undefined;
402
- const globalExtensionSettings = value.globalExtensionSettings === undefined ? undefined : decodeWorkflowExtensions(value.globalExtensionSettings);
403
- const projectExtensionSettings = value.projectExtensionSettings === undefined ? undefined : decodeWorkflowExtensions(value.projectExtensionSettings);
404
- if (value.globalExtensionSettings !== undefined && globalExtensionSettings === undefined || value.projectExtensionSettings !== undefined && projectExtensionSettings === undefined) return undefined;
405
- if (!object(value.agentDefinitions)) return undefined;
406
- const agentDefinitions = Object.create(null) as Record<string, AgentDefinition>;
407
- for (const [name, definition] of Object.entries(value.agentDefinitions)) {
408
- const decoded = decodeAgentDefinition(definition);
409
- if (decoded === undefined) return undefined;
410
- agentDefinitions[name] = decoded;
411
- }
412
- return deepFreeze({ projectTrusted: value.projectTrusted, ...(globalExtensionSettings === undefined ? {} : { globalExtensionSettings }), ...(projectExtensionSettings === undefined ? {} : { projectExtensionSettings }), agentDefinitions });
413
- }
414
- function standaloneExternalConfiguration(context: Readonly<SubagentManagerContext>, dependencies: Readonly<SubagentManagerDependencies>): StandaloneExternalConfiguration {
415
- const cwd = context.extensionContext.cwd;
416
- const trustedProject = context.extensionContext.isProjectTrusted();
417
- const agentDir = dependencies.agentDir ?? getAgentDir();
418
- const resolution = resolveWorkflowSettings(cwd, trustedProject, workflowSettingsPath(agentDir));
419
- const globalExtensionSettings = resolution.global.extensionSettings;
420
- const projectExtensionSettings = resolution.project.extensionSettings;
421
- return deepFreeze({
422
- projectTrusted: trustedProject,
423
- ...(globalExtensionSettings === undefined ? {} : { globalExtensionSettings: structuredClone(globalExtensionSettings) }),
424
- ...(projectExtensionSettings === undefined ? {} : { projectExtensionSettings: structuredClone(projectExtensionSettings) }),
425
- agentDefinitions: structuredClone(loadAgentDefinitions(cwd, agentDir, trustedProject)),
426
- });
331
+ if (!object(value) || value.version !== 1) return undefined;
332
+ return decodePreparedAgentConfiguration(value.configuration);
427
333
  }
428
334
 
429
335
  function checkedId(request: Readonly<{ id: string }>): string {
@@ -719,7 +625,7 @@ function emitUpdate(run: LiveRun): void {
719
625
  try { run.observe?.(publicStatus(live, true, true)); } catch { /* A widget failure is display-only. */ }
720
626
  }
721
627
 
722
- async function createRunStorage(root: string, id: string, request: Readonly<SubagentRunRequest>, status: PersistedSubagentStatus, external: StandaloneExternalConfiguration): Promise<string> {
628
+ async function createRunStorage(root: string, id: string, request: Readonly<SubagentRunRequest>, status: PersistedSubagentStatus, external: StandaloneExternalConfiguration | undefined): Promise<string> {
723
629
  await secureDirectory(root);
724
630
  const directory = runDirectory(root, id);
725
631
  let created = false;
@@ -728,7 +634,7 @@ async function createRunStorage(root: string, id: string, request: Readonly<Suba
728
634
  created = true;
729
635
  await chmod(directory, 0o700);
730
636
  await atomicJson(requestPath(directory), request);
731
- await atomicJson(configurationPath(directory), external);
637
+ if (external !== undefined) await atomicJson(configurationPath(directory), { version: 1, configuration: external });
732
638
  await atomicJson(statusPath(directory), status);
733
639
  return directory;
734
640
  } catch (error) {
@@ -918,6 +824,7 @@ function unavailable(operation: string): { ok: false; error: { code: "SUBAGENTS_
918
824
 
919
825
  class PersistentSubagentManager implements SubagentManager {
920
826
  private readonly activeRuns = new Map<string, LiveRun>();
827
+ private readonly pendingStarts = new Set<Promise<void>>();
921
828
  private activeRunCount = 0;
922
829
  private readonly terminalSummaries = new Map<string, TerminalSummary>();
923
830
  private readonly reconciliationErrors = new Map<string, WorkflowError>();
@@ -968,8 +875,8 @@ class PersistentSubagentManager implements SubagentManager {
968
875
  await this.ensureInitialized();
969
876
  if (this.disposed) throw new WorkflowError("CANCELLED", "Subagent manager is disposed");
970
877
  if (context.signal?.aborted) throw new WorkflowError("CANCELLED", "Subagent cancelled");
971
- const external = frozenExternal ?? standaloneExternalConfiguration(context, this.dependencies);
972
- if (external.projectTrusted && !context.extensionContext.isProjectTrusted()) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
878
+ const external = frozenExternal;
879
+ if (external?.projectTrusted && !context.extensionContext.isProjectTrusted()) throw new WorkflowError("RESUME_INCOMPATIBLE", "Cannot retry standalone configuration from an untrusted project");
973
880
  const id = randomUUID();
974
881
  const startedAt = Date.now();
975
882
  const sessionId = context.extensionContext.sessionManager.getSessionId();
@@ -982,13 +889,23 @@ class PersistentSubagentManager implements SubagentManager {
982
889
  this.activeRunCount += 1;
983
890
  const controller = new AbortController();
984
891
  const initialStatus: PersistedSubagentStatus = { id, sessionId, state: "running", startedAt, owner: { ...owner } };
892
+ let finishStart!: () => void;
893
+ const pendingStart = new Promise<void>((resolve) => { finishStart = resolve; });
894
+ this.pendingStarts.add(pendingStart);
985
895
  let directory: string;
986
896
  try {
987
897
  directory = await createRunStorage(storageDirectory(this.dependencies), id, snapshot, initialStatus, external);
898
+ if (this.disposePromise !== undefined || context.signal?.aborted) {
899
+ await atomicJson(statusPath(directory), reconciledStatus(initialStatus, "stopped", Date.now()));
900
+ throw new WorkflowError("CANCELLED", "Subagent cancelled");
901
+ }
988
902
  } catch (error) {
989
903
  this.activeRunCount -= 1;
990
904
  if (error instanceof WorkflowError) throw error;
991
905
  throw internalStorageError(error, `Unable to start subagent ${id}`);
906
+ } finally {
907
+ this.pendingStarts.delete(pendingStart);
908
+ finishStart();
992
909
  }
993
910
  const current: { run?: LiveRun } = {};
994
911
  let resolveTerminal!: (result: ForegroundResult) => void;
@@ -1019,12 +936,9 @@ class PersistentSubagentManager implements SubagentManager {
1019
936
  throw new WorkflowError("CANCELLED", "Subagent cancelled");
1020
937
  }
1021
938
  }
1022
- const baseRoot = executionRoot(context, this.dependencies, controller.signal, id, worktree, external);
1023
- const root = loadingRegistry().modelAliases().length === 0 ? baseRoot : await addDynamicAliases(context, controller.signal, baseRoot);
939
+ const baseRoot = executionRoot(context, this.dependencies, controller.signal, id, worktree);
940
+ const root = external !== undefined || loadingRegistry().modelAliases().length === 0 ? baseRoot : await addDynamicAliases(context, controller.signal, baseRoot);
1024
941
  const transport = this.dependencies.transport ?? localAgentTransport;
1025
- const injectedExecutor = this.dependencies.createExecutor?.(root, transport);
1026
- executorOwnership.default = injectedExecutor === undefined;
1027
- if (current.run) current.run.executorOwnsSession = executorOwnership.default;
1028
942
  const setSteer = (handler: SteerHandler): void => {
1029
943
  const run = current.run;
1030
944
  if (run) this.registerSteerHandler(run, handler);
@@ -1066,6 +980,13 @@ class PersistentSubagentManager implements SubagentManager {
1066
980
  if (run.state !== "running") return;
1067
981
  emitUpdate(run);
1068
982
  }, (progress) => this.recordProgress(current.run, progress));
983
+ if (external !== undefined) options.configuration = structuredClone(external);
984
+ options.onConfiguration = (configuration) => atomicJson(configurationPath(directory), { version: 1, configuration });
985
+ const cwd = worktree?.cwd ?? root.cwd;
986
+ options.configuration = await new WorkflowAgentExecutor(root, transport).prepare(options, cwd, controller.signal);
987
+ const injectedExecutor = this.dependencies.createExecutor?.({ ...root, extensionSettings: options.configuration.settings }, transport);
988
+ executorOwnership.default = injectedExecutor === undefined;
989
+ if (current.run) current.run.executorOwnsSession = executorOwnership.default;
1069
990
  if (injectedExecutor) return injectedExecutor.execute(snapshot.prompt, options, controller.signal, setSteer);
1070
991
  return new WorkflowAgentExecutor(root, transport).execute(snapshot.prompt, options, controller.signal, [], setSteer);
1071
992
  });
@@ -1113,9 +1034,9 @@ class PersistentSubagentManager implements SubagentManager {
1113
1034
  }
1114
1035
  return inspection;
1115
1036
  }
1116
- return this.inspectList();
1037
+ return this.inspectList(request.scope === "all" ? undefined : context.extensionContext.sessionManager.getSessionId());
1117
1038
  }
1118
- private async inspectList(): Promise<unknown> {
1039
+ private async inspectList(sessionId: string | undefined): Promise<unknown> {
1119
1040
  const root = storageDirectory(this.dependencies);
1120
1041
  await secureDirectory(root);
1121
1042
  const entries = await readdir(root, { withFileTypes: true });
@@ -1127,7 +1048,7 @@ class PersistentSubagentManager implements SubagentManager {
1127
1048
  try {
1128
1049
  const active = this.activeRuns.get(entry.name);
1129
1050
  const status = publicStatus(active ? persistedStatus(active) : this.terminalSummaries.get(entry.name) ?? await loadPersistedStatus(root, entry.name, false));
1130
- if (status.startedAt === undefined) continue;
1051
+ if (status.startedAt === undefined || (sessionId !== undefined && status.sessionId !== sessionId)) continue;
1131
1052
  statuses.push({ status, startedAt: status.startedAt });
1132
1053
  } catch (error) {
1133
1054
  if (error instanceof WorkflowError && (error.code === "RUN_NOT_FOUND" || error instanceof InvalidPersistedSubagentStatusError)) continue;
@@ -1176,11 +1097,11 @@ class PersistentSubagentManager implements SubagentManager {
1176
1097
 
1177
1098
  async dispose(): Promise<void> {
1178
1099
  if (this.disposePromise) return this.disposePromise;
1100
+ this.disposed = true;
1179
1101
  this.disposePromise = (async () => {
1180
1102
  await this.initialization;
1181
- this.disposed = true;
1182
1103
  const runs = [...this.activeRuns.values()];
1183
- await Promise.allSettled(runs.map((run) => this.stopRun(run, true)));
1104
+ await Promise.allSettled([...this.pendingStarts, ...runs.map((run) => this.stopRun(run, true))]);
1184
1105
  await Promise.allSettled([...this.notificationPromises]);
1185
1106
  const owner = this.storageOwner;
1186
1107
  this.storageOwner = undefined;
@@ -1533,9 +1454,8 @@ class PersistentSubagentManager implements SubagentManager {
1533
1454
  const notify = this.dependencies.notify;
1534
1455
  if (!notify || run.notificationSent || run.disposed) return;
1535
1456
  run.notificationSent = true;
1536
- const role = roleNameOf(run.request.role) ?? "none";
1537
- const label = run.request.label?.trim() || (role === "none" ? "subagent" : role);
1538
- const notification: SubagentNotification = { id: run.id, label, role, state: run.state as "completed" | "failed", ...(run.error === undefined ? {} : { error: run.error }) };
1457
+ const label = run.request.label?.trim() || "subagent";
1458
+ const notification: SubagentNotification = { id: run.id, label, state: run.state as "completed" | "failed", ...(run.error === undefined ? {} : { error: run.error }) };
1539
1459
  const pending = Promise.resolve().then(() => notify(notification));
1540
1460
  this.notificationPromises.add(pending);
1541
1461
  void pending.then(() => { this.notificationPromises.delete(pending); }, () => { this.notificationPromises.delete(pending); });
@@ -71,16 +71,10 @@ async function inspectEntry(manager: SubagentManager, storageDirectory: string,
71
71
  };
72
72
  }
73
73
 
74
- function requestRole(request: SubagentRunRequest | undefined): string {
75
- const role: unknown = request?.role;
76
- if (typeof role === "string" && role.trim()) return role.trim();
77
- const override = objectValue(role);
78
- return typeof override?.name === "string" && override.name.trim() ? override.name.trim() : "none";
79
- }
74
+
80
75
  function shortId(id: string): string { return id.length > 12 ? id.slice(0, 8) : id; }
81
76
  function entryName(entry: NavigatorEntry): string {
82
- const role = requestRole(entry.request);
83
- return boundedText(entry.request?.label?.trim() || (role === "none" ? shortId(entry.status.id) : role), 256);
77
+ return boundedText(entry.request?.label?.trim() || shortId(entry.status.id), 256);
84
78
  }
85
79
  /** Names for a list of runs; a repeated name carries its short ID, as in the `/workflow` picker, so every row stays unique. */
86
80
  function listNames(entries: readonly NavigatorEntry[]): string[] {
@@ -113,7 +107,7 @@ function usageAccounting(status: SubagentStatus): SubagentProgress["accounting"]
113
107
  return status.progress?.accounting;
114
108
  }
115
109
  function detailPresentation(inspection: Inspection): AgentDetailPresentation {
116
- const { status, request } = inspection.entry;
110
+ const { status } = inspection.entry;
117
111
  const attempt = latestAttempt(status);
118
112
  const state = status.progress?.state;
119
113
  const activity = status.progress?.activity;
@@ -122,14 +116,12 @@ function detailPresentation(inspection: Inspection): AgentDetailPresentation {
122
116
  const lastEventAt = status.progress?.lastEventAt;
123
117
  const attempts = status.attempts ?? attempt?.attempt;
124
118
  const accounting = usageAccounting(status);
125
- const role = requestRole(request);
126
119
  const error = status.error ?? attempt?.error;
127
120
  return {
128
121
  state: status.state,
129
122
  ...(activity === undefined ? {} : { activity: { kind: activity.kind, text: boundedText(activity.text) } }),
130
123
  ...(lastEventAt === undefined ? {} : { lastEventAt }),
131
124
  ...(model === undefined ? {} : { model: { provider: boundedText(model.provider, 256), model: boundedText(model.model, 256), ...(model.thinking === undefined ? {} : { thinking: model.thinking }) } }),
132
- ...(role === "none" ? {} : { role: boundedText(role, 256) }),
133
125
  ...(tools === undefined ? {} : { tools: tools.slice(0, 256).map((tool) => boundedText(tool, 256)) }),
134
126
  ...(attempts === undefined ? {} : { attempts }),
135
127
  ...(status.startedAt === undefined ? {} : { startedAt: status.startedAt }),
@@ -186,8 +178,7 @@ function standaloneActionContext(manager: SubagentManager, inspection: Inspectio
186
178
  const session = attempt.session;
187
179
  const live = liveData?.liveSession && session && liveData.liveSession.reference.transport === session.transport && liveData.liveSession.reference.sessionId === session.sessionId ? liveData.liveSession : undefined;
188
180
  const label = request?.label?.trim();
189
- const role = requestRole(request);
190
- const name = label || (role === "none" ? "subagent" : role);
181
+ const name = label || "subagent";
191
182
  const ui = {
192
183
  notify: (message: string, level: "info" | "warning" | "error" = "info") => { context.ui.notify(message, level); },
193
184
  confirm: (title: string, message: string) => context.ui.confirm(title, message),