pi-subagents 0.50.0 → 0.52.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/CHANGELOG.md +103 -0
- package/agents/oracle.md +3 -1
- package/agents/reviewer.md +1 -0
- package/agents/scout.md +2 -2
- package/agents/worker.md +2 -1
- package/async-retention-discovery-worker.mjs +180 -0
- package/docs/agents.md +40 -2
- package/docs/configuration.md +30 -12
- package/docs/extension-api.md +42 -1
- package/docs/models.md +2 -0
- package/docs/observability.md +41 -5
- package/docs/tool-reference.md +38 -39
- package/docs/workflows.md +171 -3
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +6 -4
- package/skills/pi-subagents/references/constraints-and-recipes.md +12 -6
- package/skills/pi-subagents/references/execution-controls.md +29 -15
- package/skills/pi-subagents/references/management-authoring-rpc.md +3 -3
- package/skills/pi-subagents/references/prompting-and-roles.md +4 -2
- package/src/agents/agent-management.ts +124 -351
- package/src/agents/agents.ts +157 -31
- package/src/agents/skills.ts +1 -1
- package/src/api/external-job-provider.ts +185 -0
- package/src/api/preflight.ts +36 -8
- package/src/api/shared-types.ts +2 -0
- package/src/extension/config.ts +3 -3
- package/src/extension/doctor.ts +3 -6
- package/src/extension/fanout-child.ts +2 -2
- package/src/extension/index.ts +166 -88
- package/src/extension/public-execution.ts +31 -2
- package/src/extension/schemas.ts +12 -35
- package/src/extension/tool-description.ts +35 -24
- package/src/inspectors/herdr/actions.ts +2 -2
- package/src/inspectors/herdr/inspector-runner.ts +2 -1
- package/src/inspectors/herdr/project-panes.ts +2 -2
- package/src/intercom/native-supervisor-channel.ts +32 -10
- package/src/missions/lifecycle.ts +6 -1
- package/src/missions/store.ts +4 -9
- package/src/missions/workflow-state.ts +2 -2
- package/src/profiles/profiles.ts +3 -1
- package/src/runs/background/active-run-index.ts +31 -8
- package/src/runs/background/async-execution.ts +68 -40
- package/src/runs/background/async-job-tracker.ts +20 -4
- package/src/runs/background/async-resume.ts +47 -15
- package/src/runs/background/async-retention.ts +886 -0
- package/src/runs/background/async-status.ts +39 -53
- package/src/runs/background/chain-append.ts +3 -33
- package/src/runs/background/completion-dedupe.ts +5 -1
- package/src/runs/background/completion-replay.ts +22 -12
- package/src/runs/background/control-channel.ts +14 -68
- package/src/runs/background/fleet-view.ts +68 -19
- package/src/runs/background/index-segment.ts +59 -0
- package/src/runs/background/inspect-rpc.ts +443 -0
- package/src/runs/background/notify.ts +31 -5
- package/src/runs/background/result-files.ts +158 -90
- package/src/runs/background/result-watcher.ts +116 -20
- package/src/runs/background/resume-guidance.ts +8 -5
- package/src/runs/background/retained-children.ts +13 -3
- package/src/runs/background/run-id-query.ts +7 -0
- package/src/runs/background/run-id-resolver.ts +11 -9
- package/src/runs/background/run-status.ts +27 -18
- package/src/runs/background/scheduled-runs.ts +24 -4
- package/src/runs/background/stale-run-reconciler.ts +6 -3
- package/src/runs/background/steering.ts +11 -1
- package/src/runs/background/subagent-runner.ts +253 -140
- package/src/runs/background/subagent-wait.ts +8 -8
- package/src/runs/background/terminal-run-index.ts +129 -0
- package/src/runs/background/wait-completions.ts +21 -4
- package/src/runs/background/wait-subscriptions.ts +81 -2
- package/src/runs/foreground/async-steering-action.ts +21 -14
- package/src/runs/foreground/execution.ts +3 -1
- package/src/runs/foreground/subagent-executor.ts +666 -1579
- package/src/runs/foreground/workflow-detach-reconcile.ts +194 -0
- package/src/runs/foreground/workflow-foreground-steering.ts +6 -5
- package/src/runs/shared/acceptance.ts +4 -4
- package/src/runs/shared/chain-outputs.ts +1 -3
- package/src/runs/shared/external-job-bridge.ts +444 -0
- package/src/runs/shared/external-job-runner.ts +286 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +14 -0
- package/src/runs/shared/model-fallback.ts +98 -38
- package/src/runs/shared/orca-progress-tabs.ts +84 -22
- package/src/runs/shared/parallel-handoff.ts +46 -4
- package/src/runs/shared/parallel-utils.ts +4 -15
- package/src/runs/shared/permissions.ts +5 -1
- package/src/runs/shared/pi-args.ts +8 -1
- package/src/runs/shared/session-lease.ts +0 -6
- package/src/runs/shared/subagent-control.ts +26 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
- package/src/runs/shared/workflow-graph.ts +1 -23
- package/src/runs/shared/worktree.ts +14 -2
- package/src/shared/atomic-json.ts +22 -2
- package/src/shared/capacity-resilient-json.ts +102 -0
- package/src/shared/completion-owner.ts +14 -0
- package/src/shared/file-system-retry.ts +49 -1
- package/src/shared/fork-context.ts +42 -0
- package/src/shared/prompt-resources.ts +0 -40
- package/src/shared/settings.ts +3 -27
- package/src/shared/types.ts +71 -26
- package/src/shared/utils.ts +8 -0
- package/src/shared/watch-strategy.ts +10 -0
- package/src/slash/slash-bridge.ts +2 -1
- package/src/slash/slash-commands.ts +29 -3
- package/src/tui/fleet.ts +63 -15
- package/src/watchdog/change-signature.ts +1 -1
- package/src/watchdog/lsp-diagnostics.ts +1 -0
- package/src/workflows/chat-progress.ts +2 -2
- package/src/workflows/scripted-workflow.ts +220 -67
- package/src/runs/foreground/chain-clarify.ts +0 -1354
- package/src/runs/foreground/chain-execution.ts +0 -1581
package/src/api/preflight.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { discoverAgents, discoverAgentsAll, resolveAgentName, type AgentConfig, type AgentScope, type AgentSource } from "../agents/agents.ts";
|
|
5
|
+
import { discoverAgents, discoverAgentsAll, findBlockingAgentDiagnostic, resolveAgentName, type AgentConfig, type AgentScope, type AgentSource } from "../agents/agents.ts";
|
|
6
6
|
import { resolveExecutionAgentScope } from "../agents/agent-scope.ts";
|
|
7
7
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../agents/skills.ts";
|
|
8
8
|
import { buildAgentMemoryInjection } from "../agents/agent-memory.ts";
|
|
9
|
-
import { buildModelCandidates, resolveEffectiveSubagentModel, type AvailableModelInfo, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
9
|
+
import { buildModelCandidates, inheritsParentModel, resolveEffectiveSubagentModel, type AvailableModelInfo, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
10
10
|
import { applyThinkingSuffix, resolvePiLaunchToolPlan, type PiLaunchToolPlan } from "../runs/shared/pi-args.ts";
|
|
11
11
|
import { injectOutputPathSystemPrompt, normalizeSingleOutputOverride, resolveSingleOutputPath } from "../runs/shared/single-output.ts";
|
|
12
12
|
import { getArtifactPaths, getArtifactsDir } from "../shared/artifacts.ts";
|
|
@@ -17,6 +17,8 @@ import { appendTurnBudgetSystemPrompt } from "../runs/shared/turn-budget.ts";
|
|
|
17
17
|
import type { ResolvedTurnBudget } from "../shared/types.ts";
|
|
18
18
|
import type { ResolvedMcpDirectToolSelection } from "../runs/shared/mcp-direct-tool-allowlist.ts";
|
|
19
19
|
import { resolveStepBehavior } from "../shared/settings.ts";
|
|
20
|
+
import { canPreferForkFromSnapshot, resolveSubagentLaunchContext } from "../shared/fork-context.ts";
|
|
21
|
+
import { loadConfig } from "../extension/config.ts";
|
|
20
22
|
import { agentDefinitionDigest, AGENT_DEFINITION_PROJECTION_VERSION, launchBindingDigest } from "../shared/launch-contract.ts";
|
|
21
23
|
import { DIRS, TEMP_ROOT_DIR } from "../shared/types.ts";
|
|
22
24
|
import { processTerminalCandidatePath, processTerminalPath } from "../runs/background/process-terminal.ts";
|
|
@@ -60,6 +62,8 @@ export interface SubagentLaunchContractInput {
|
|
|
60
62
|
artifacts?: boolean;
|
|
61
63
|
artifactDir?: ArtifactDirPreference;
|
|
62
64
|
parentSessionFile?: string | null;
|
|
65
|
+
/** Current parent leaf required before an implicit `defaultContext: fork` stays `fork`. */
|
|
66
|
+
parentLeafId?: string | null;
|
|
63
67
|
sessionRoot?: string;
|
|
64
68
|
sessionDir?: string;
|
|
65
69
|
runId?: string;
|
|
@@ -190,6 +194,18 @@ function normalizeAvailableModels(models: SubagentLaunchContractInput["available
|
|
|
190
194
|
return (models ?? []).map((model) => ({ ...model, fullId: model.fullId ?? `${model.provider}/${model.id}` }));
|
|
191
195
|
}
|
|
192
196
|
|
|
197
|
+
function resolveLaunchContractContext(input: SubagentLaunchContractInput, agent: AgentConfig): "fresh" | "fork" {
|
|
198
|
+
return resolveSubagentLaunchContext({
|
|
199
|
+
explicitContext: input.context,
|
|
200
|
+
agentDefaultContext: agent.defaultContext,
|
|
201
|
+
defaultSubagentContext: loadConfig().defaultSubagentContext,
|
|
202
|
+
canUseImplicitFork: canPreferForkFromSnapshot({
|
|
203
|
+
parentSessionFile: input.parentSessionFile,
|
|
204
|
+
leafId: input.parentLeafId,
|
|
205
|
+
}),
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
193
209
|
function candidateList(inputAgent: string, selected: AgentConfig | undefined, cwd: string): SubagentLaunchContractAgentCandidate[] {
|
|
194
210
|
const all = discoverAgentsAll(cwd);
|
|
195
211
|
return [...all.builtin, ...all.package, ...all.user, ...all.project]
|
|
@@ -222,12 +238,17 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
222
238
|
if (input.artifactDir !== undefined && input.artifactDir !== "project" && input.artifactDir !== "session" && input.artifactDir !== "temp") {
|
|
223
239
|
return { ok: false, code: "invalid_artifact_dir", message: `Unsupported artifactDir '${String(input.artifactDir)}'; expected 'project', 'session', or 'temp'.`, diagnostics };
|
|
224
240
|
}
|
|
225
|
-
if (input.context === "fork") {
|
|
226
|
-
diagnostics.push({ code: "host_required", severity: "host-required", message: "Exact fork session branching and fork-thinking downgrade checks require Pi host session and model-registry snapshots." });
|
|
227
|
-
}
|
|
228
241
|
const scope = resolveExecutionAgentScope(input.agentScope);
|
|
229
242
|
const discovered = discoverAgents(effectiveCwd, scope);
|
|
230
243
|
const resolvedAgent = resolveAgentName(input.agent, discovered.agents);
|
|
244
|
+
const ambiguousCandidates = resolvedAgent.error
|
|
245
|
+
? discovered.agents.filter((agent) => resolveAgentName(input.agent, [agent]).agent)
|
|
246
|
+
: resolvedAgent.agent;
|
|
247
|
+
const invalidAgent = findBlockingAgentDiagnostic(input.agent, ambiguousCandidates, discovered.agentDiagnostics);
|
|
248
|
+
if (invalidAgent) {
|
|
249
|
+
const message = `Agent '${input.agent}' has invalid configuration: ${invalidAgent.error}`;
|
|
250
|
+
return { ok: false, code: "missing_agent", message, diagnostics: [{ code: "missing_agent", severity: "error", message }] };
|
|
251
|
+
}
|
|
231
252
|
if (resolvedAgent.error) {
|
|
232
253
|
return { ok: false, code: "ambiguous_agent", message: resolvedAgent.error, diagnostics };
|
|
233
254
|
}
|
|
@@ -235,6 +256,10 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
235
256
|
return { ok: false, code: "missing_agent", message: `Unknown agent: ${input.agent}`, diagnostics };
|
|
236
257
|
}
|
|
237
258
|
const agent = resolvedAgent.agent;
|
|
259
|
+
const context = resolveLaunchContractContext(input, agent);
|
|
260
|
+
if (context === "fork") {
|
|
261
|
+
diagnostics.push({ code: "host_required", severity: "host-required", message: "Exact fork session branching and fork-thinking downgrade checks require Pi host session and model-registry snapshots." });
|
|
262
|
+
}
|
|
238
263
|
const effectiveCapabilityCeiling = intersectSubagentCapabilityCeilings(input.capabilityCeiling, input.inheritedCapabilityCeiling);
|
|
239
264
|
const restrictionMessage = capabilityCeilingAgentRestrictionMessage(agent.name, effectiveCapabilityCeiling);
|
|
240
265
|
if (restrictionMessage) return { ok: false, code: "restricted_agent", message: restrictionMessage, diagnostics };
|
|
@@ -260,7 +285,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
260
285
|
}
|
|
261
286
|
if (resolvedSkills.missing.length > 0) diagnostics.push({ code: "missing_skill", severity: "error", message: `Missing skills: ${resolvedSkills.missing.join(", ")}` });
|
|
262
287
|
|
|
263
|
-
const externalRunner = agent.runner?.type === "external-cli";
|
|
288
|
+
const externalRunner = agent.runner?.type === "external-cli" || agent.runner?.type === "external-job";
|
|
264
289
|
const availableModels = normalizeAvailableModels(input.availableModels);
|
|
265
290
|
const preferredProvider = input.preferredProvider ?? input.parentModel?.provider;
|
|
266
291
|
const primaryModel = externalRunner
|
|
@@ -270,7 +295,10 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
270
295
|
const model = externalRunner ? undefined : applyThinkingSuffix(primaryModel, effectiveThinkingConfig, input.thinking !== undefined);
|
|
271
296
|
const modelCandidates = externalRunner
|
|
272
297
|
? []
|
|
273
|
-
: buildModelCandidates(primaryModel, agent.fallbackModels, availableModels, preferredProvider, {
|
|
298
|
+
: buildModelCandidates(primaryModel, agent.fallbackModels, availableModels, preferredProvider, {
|
|
299
|
+
scope: discovered.modelScope,
|
|
300
|
+
primaryModelFromParent: inheritsParentModel(input.model, agent.model, input.parentModel),
|
|
301
|
+
})
|
|
274
302
|
.map((candidate) => applyThinkingSuffix(candidate, effectiveThinkingConfig, input.thinking !== undefined) ?? candidate);
|
|
275
303
|
let toolPlan: PiLaunchToolPlan;
|
|
276
304
|
try {
|
|
@@ -335,7 +363,7 @@ export async function resolveSubagentLaunchContract(input: SubagentLaunchContrac
|
|
|
335
363
|
definitionDigest,
|
|
336
364
|
shadowedCandidates,
|
|
337
365
|
},
|
|
338
|
-
context
|
|
366
|
+
context,
|
|
339
367
|
...(model ? { model } : {}),
|
|
340
368
|
modelCandidates,
|
|
341
369
|
...(resolveEffectiveThinking(model, effectiveThinkingConfig) ? { thinking: resolveEffectiveThinking(model, effectiveThinkingConfig) } : {}),
|
package/src/api/shared-types.ts
CHANGED
package/src/extension/config.ts
CHANGED
|
@@ -94,6 +94,9 @@ function validateMainWindowRendererConfig(value: unknown): void {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function validateConfig(config: Record<string, unknown>): void {
|
|
97
|
+
if (config.defaultSubagentContext !== undefined && config.defaultSubagentContext !== "fresh" && config.defaultSubagentContext !== "fork") {
|
|
98
|
+
throw new Error('config.defaultSubagentContext must be "fresh" or "fork"');
|
|
99
|
+
}
|
|
97
100
|
if (config.foregroundDetachShortcut !== undefined
|
|
98
101
|
&& (typeof config.foregroundDetachShortcut !== "string" || !isValidKeyId(config.foregroundDetachShortcut))) {
|
|
99
102
|
throw new Error("config.foregroundDetachShortcut must be a valid keybinding string such as \"ctrl+b\"");
|
|
@@ -101,9 +104,6 @@ function validateConfig(config: Record<string, unknown>): void {
|
|
|
101
104
|
if (config.artifactDir !== undefined && !ARTIFACT_DIR_PREFERENCES.has(config.artifactDir as ArtifactDirPreference)) {
|
|
102
105
|
throw new Error(`config.artifactDir must be "project", "session", or "temp"`);
|
|
103
106
|
}
|
|
104
|
-
if (config.legacyChainControls !== undefined && typeof config.legacyChainControls !== "boolean") {
|
|
105
|
-
throw new Error("config.legacyChainControls must be a boolean");
|
|
106
|
-
}
|
|
107
107
|
if (config.maxActiveAsyncRunsPerSession !== undefined
|
|
108
108
|
&& (typeof config.maxActiveAsyncRunsPerSession !== "number"
|
|
109
109
|
|| !Number.isInteger(config.maxActiveAsyncRunsPerSession)
|
package/src/extension/doctor.ts
CHANGED
|
@@ -134,7 +134,7 @@ function formatSessionLines(input: DoctorReportInput): string[] {
|
|
|
134
134
|
|
|
135
135
|
function formatDiscovery(input: DoctorReportInput, deps: DoctorDeps): string[] {
|
|
136
136
|
return [
|
|
137
|
-
lineFromCheck("agents
|
|
137
|
+
lineFromCheck("agents", () => {
|
|
138
138
|
const discovered = deps.discoverAgentsAll(input.cwd);
|
|
139
139
|
const agentCounts = {
|
|
140
140
|
builtin: discovered.builtin.length,
|
|
@@ -142,13 +142,10 @@ function formatDiscovery(input: DoctorReportInput, deps: DoctorDeps): string[] {
|
|
|
142
142
|
user: discovered.user.length,
|
|
143
143
|
project: discovered.project.length,
|
|
144
144
|
};
|
|
145
|
-
const
|
|
146
|
-
counts[chain.source] += 1;
|
|
147
|
-
return counts;
|
|
148
|
-
}, { builtin: 0, package: 0, user: 0, project: 0 });
|
|
145
|
+
const diagnostics = discovered.agentDiagnostics ?? [];
|
|
149
146
|
return [
|
|
150
147
|
`- agents: total ${agentCounts.builtin + agentCounts.package + agentCounts.user + agentCounts.project} (${formatSourceCounts(agentCounts)})`,
|
|
151
|
-
`-
|
|
148
|
+
...diagnostics.map((diagnostic) => `- invalid agent ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
152
149
|
].join("\n");
|
|
153
150
|
}),
|
|
154
151
|
lineFromCheck("skills", () => {
|
|
@@ -171,13 +171,13 @@ export default function registerFanoutChildSubagentExtension(pi: ExtensionAPI):
|
|
|
171
171
|
allowMutatingManagementActions: false,
|
|
172
172
|
});
|
|
173
173
|
|
|
174
|
-
const params = createSubagentParamsSchema(
|
|
174
|
+
const params = createSubagentParamsSchema();
|
|
175
175
|
const tool: ToolDefinition<typeof params, Details> = {
|
|
176
176
|
name: "subagent",
|
|
177
177
|
label: "Subagent",
|
|
178
178
|
description: [
|
|
179
179
|
"Delegate to subagents from child-safe fanout mode.",
|
|
180
|
-
|
|
180
|
+
"Allowed management/control actions: list, get, status, interrupt, resume, steer, doctor.",
|
|
181
181
|
"Mutating management actions (create, update, delete, eject, disable, enable, reset, grant-spawn-budget) are blocked in this mode.",
|
|
182
182
|
].join("\n"),
|
|
183
183
|
parameters: params,
|
package/src/extension/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* Toggle: async parameter (default: true; set asyncByDefault:false in config.json to opt out)
|
|
10
10
|
*
|
|
11
11
|
* Config file: ~/.pi/agent/extensions/subagent/config.json
|
|
12
|
-
* { "asyncByDefault": true, "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
|
|
12
|
+
* { "asyncByDefault": true, "defaultSubagentContext": "fork", "forceTopLevelAsync": true, "maxSubagentDepth": 1, "intercomBridge": { "mode": "always", "instructionFile": "./intercom-bridge.md" }, "worktreeSetupHook": "./scripts/setup-worktree.mjs" }
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
@@ -23,6 +23,7 @@ import { discoverAgents } from "../agents/agents.ts";
|
|
|
23
23
|
import { ensureAccessibleDir } from "../shared/accessible-dir.ts";
|
|
24
24
|
import { cleanupAllArtifactDirs, cleanupOldArtifacts, getArtifactsDir } from "../shared/artifacts.ts";
|
|
25
25
|
import { resolveCurrentSessionId } from "../shared/session-identity.ts";
|
|
26
|
+
import { currentCompletionOwnerId } from "../shared/completion-owner.ts";
|
|
26
27
|
import { cleanupOldChainDirs } from "../shared/settings.ts";
|
|
27
28
|
import { clearLegacyResultAnimationTimer, renderSubagentResult, renderSubagentSummary } from "../tui/render.ts";
|
|
28
29
|
import { openSubagentFleet } from "../tui/fleet.ts";
|
|
@@ -31,7 +32,8 @@ import { createSubagentParamsSchema } from "./schemas.ts";
|
|
|
31
32
|
import { createSubagentExecutor, type SubagentParamsLike } from "../runs/foreground/subagent-executor.ts";
|
|
32
33
|
import { createAsyncJobTracker } from "../runs/background/async-job-tracker.ts";
|
|
33
34
|
import { getActiveAsyncCapacitySnapshot, resolveMaxActiveAsyncRunsPerSession } from "../runs/background/active-async-capacity.ts";
|
|
34
|
-
import { cleanupResultIndexes } from "../runs/background/result-files.ts";
|
|
35
|
+
import { cleanupResultIndexes, missionObserverResultCandidateFiles } from "../runs/background/result-files.ts";
|
|
36
|
+
import { ASYNC_RETENTION_DELAY_MS, cleanupAsyncRetention } from "../runs/background/async-retention.ts";
|
|
35
37
|
import { createResultWatcher } from "../runs/background/result-watcher.ts";
|
|
36
38
|
import { createScheduledRunManager } from "../runs/background/scheduled-runs.ts";
|
|
37
39
|
import { registerSlashCommands } from "../slash/slash-commands.ts";
|
|
@@ -53,7 +55,7 @@ import { SUBAGENT_CHILD_ENV, SUBAGENT_PARENT_SESSION_ENV } from "../runs/shared/
|
|
|
53
55
|
import { resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
54
56
|
import { formatDuration, shortenPath } from "../shared/formatters.ts";
|
|
55
57
|
import { loadConfig, resolveAsyncByDefault, resolveScheduledStoreRoot } from "./config.ts";
|
|
56
|
-
import { buildSubagentToolDescription } from "./tool-description.ts";
|
|
58
|
+
import { buildSubagentToolDescription, buildSubagentToolPromptMetadata } from "./tool-description.ts";
|
|
57
59
|
import { collectGoalContinuationNotices } from "../missions/goal-driver.ts";
|
|
58
60
|
import { restoreForegroundRunHistory } from "../runs/foreground/foreground-history.ts";
|
|
59
61
|
import { resolveMissionStoreLocation } from "../missions/store.ts";
|
|
@@ -84,6 +86,37 @@ import {
|
|
|
84
86
|
export { loadConfig, resolveAsyncByDefault } from "./config.ts";
|
|
85
87
|
|
|
86
88
|
const SLOW_RELOAD_PHASE_MS = 250;
|
|
89
|
+
const RUNTIME_REGISTRY_STORE_KEY = "__piSubagentRuntimeRegistry";
|
|
90
|
+
|
|
91
|
+
interface SubagentRuntimeEntry {
|
|
92
|
+
cleanup(): void;
|
|
93
|
+
sessionManager: object | null;
|
|
94
|
+
visibleControlNotices: Set<string>;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
interface SubagentRuntimeRegistry {
|
|
98
|
+
bySessionManager: WeakMap<object, SubagentRuntimeEntry>;
|
|
99
|
+
visibleControlNoticesBySessionManager: WeakMap<object, Set<string>>;
|
|
100
|
+
activeEntries: Set<SubagentRuntimeEntry>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function getRuntimeRegistry(): SubagentRuntimeRegistry {
|
|
104
|
+
const globalStore = globalThis as Record<string, unknown>;
|
|
105
|
+
const existing = globalStore[RUNTIME_REGISTRY_STORE_KEY] as Partial<SubagentRuntimeRegistry> | undefined;
|
|
106
|
+
if (existing?.bySessionManager instanceof WeakMap && existing.activeEntries instanceof Set) {
|
|
107
|
+
if (!(existing.visibleControlNoticesBySessionManager instanceof WeakMap)) {
|
|
108
|
+
existing.visibleControlNoticesBySessionManager = new WeakMap();
|
|
109
|
+
}
|
|
110
|
+
return existing as SubagentRuntimeRegistry;
|
|
111
|
+
}
|
|
112
|
+
const registry: SubagentRuntimeRegistry = {
|
|
113
|
+
bySessionManager: new WeakMap(),
|
|
114
|
+
visibleControlNoticesBySessionManager: new WeakMap(),
|
|
115
|
+
activeEntries: new Set(),
|
|
116
|
+
};
|
|
117
|
+
globalStore[RUNTIME_REGISTRY_STORE_KEY] = registry;
|
|
118
|
+
return registry;
|
|
119
|
+
}
|
|
87
120
|
|
|
88
121
|
function logSlowPhase(label: string, startedAt: number): void {
|
|
89
122
|
const elapsed = Date.now() - startedAt;
|
|
@@ -358,16 +391,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
358
391
|
if (process.env[SUBAGENT_CHILD_ENV] === "1") {
|
|
359
392
|
return;
|
|
360
393
|
}
|
|
361
|
-
const
|
|
362
|
-
const runtimeCleanupStoreKey = "__piSubagentRuntimeCleanup";
|
|
363
|
-
const previousRuntimeCleanup = globalStore[runtimeCleanupStoreKey];
|
|
364
|
-
if (typeof previousRuntimeCleanup === "function") {
|
|
365
|
-
try {
|
|
366
|
-
previousRuntimeCleanup();
|
|
367
|
-
} catch {
|
|
368
|
-
// Best effort cleanup for stale timers from an older reload.
|
|
369
|
-
}
|
|
370
|
-
}
|
|
394
|
+
const runtimeRegistry = getRuntimeRegistry();
|
|
371
395
|
|
|
372
396
|
DIRS.results = ensureAccessibleDir(DIRS.results);
|
|
373
397
|
DIRS.async = ensureAccessibleDir(DIRS.async);
|
|
@@ -395,6 +419,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
395
419
|
const state: SubagentState = {
|
|
396
420
|
baseCwd: "",
|
|
397
421
|
currentSessionId: null,
|
|
422
|
+
completionOwnerId: currentCompletionOwnerId(),
|
|
398
423
|
artifactDirPreference: config.artifactDir ?? DEFAULT_ARTIFACT_CONFIG.dir,
|
|
399
424
|
...(config.authorityPolicy ? { authorityPolicy: config.authorityPolicy } : {}),
|
|
400
425
|
...(config.missions ? { missionStoreConfig: config.missions } : {}),
|
|
@@ -440,6 +465,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
440
465
|
: undefined;
|
|
441
466
|
let executorScheduled: ((id: string, params: SubagentParamsLike, signal: AbortSignal, ctx: ExtensionContext) => Promise<AgentToolResult<Details>>) | undefined;
|
|
442
467
|
let goalTurnId = 0;
|
|
468
|
+
let parentSessionEnvValue: string | null = null;
|
|
443
469
|
const scheduledStoreRoot = config.scheduledRuns?.storeRoot === undefined ? undefined : resolveScheduledStoreRoot(config.scheduledRuns.storeRoot);
|
|
444
470
|
const scheduledRunManager = createScheduledRunManager({
|
|
445
471
|
config,
|
|
@@ -456,10 +482,18 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
456
482
|
},
|
|
457
483
|
resolveCapabilityCeiling: (sessionId) => resolveCurrentSubagentCapabilityCeiling(sessionId),
|
|
458
484
|
});
|
|
485
|
+
let refreshResultDelivery = () => {};
|
|
486
|
+
const hasResultDeliveryDemand = () => {
|
|
487
|
+
if ([...state.asyncJobs.values()].some((job) => job.status === "queued" || job.status === "running")) return true;
|
|
488
|
+
if (state.foregroundControls.size > 0) return true;
|
|
489
|
+
if (scheduledRunManager.observedCompletionRunIds().size > 0) return true;
|
|
490
|
+
return missionObserverResultCandidateFiles(DIRS.results).length > 0;
|
|
491
|
+
};
|
|
459
492
|
const { ensurePoller, refreshWidget, handleStarted, handleComplete, resetJobs, restoreActiveJobs, dispose: disposeAsyncJobTracker } = createAsyncJobTracker(pi, state, DIRS.async, {
|
|
460
493
|
widgetEnabled: asyncWidgetEnabled,
|
|
494
|
+
onJobTerminal: () => refreshResultDelivery(),
|
|
461
495
|
});
|
|
462
|
-
const
|
|
496
|
+
const resultWatcher = createResultWatcher(
|
|
463
497
|
pi,
|
|
464
498
|
state,
|
|
465
499
|
DIRS.results,
|
|
@@ -468,23 +502,30 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
468
502
|
notifier: completionNotifier,
|
|
469
503
|
observeCompletion: (result) => scheduledRunManager.handleAsyncCompletion(result),
|
|
470
504
|
observedCompletionRunIds: () => scheduledRunManager.observedCompletionRunIds(),
|
|
505
|
+
hasDeliveryDemand: hasResultDeliveryDemand,
|
|
471
506
|
deliverIntercomResults: config.intercomBridge?.resultDelivery === true,
|
|
472
507
|
},
|
|
473
508
|
);
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
509
|
+
const { startResultWatcher, primeExistingResults, stopResultWatcher } = resultWatcher;
|
|
510
|
+
refreshResultDelivery = resultWatcher.refreshResultDelivery;
|
|
511
|
+
const asyncRetentionAbort = new AbortController();
|
|
512
|
+
const asyncRetentionTimer = setTimeout(async () => {
|
|
513
|
+
try {
|
|
514
|
+
await cleanupAsyncRetention({
|
|
515
|
+
asyncDirRoot: DIRS.async,
|
|
516
|
+
resultsDir: DIRS.results,
|
|
517
|
+
signal: asyncRetentionAbort.signal,
|
|
518
|
+
protectedRunIds: new Set([
|
|
519
|
+
...state.asyncJobs.keys(),
|
|
520
|
+
...(state.workflowControllers?.keys() ?? []),
|
|
521
|
+
...scheduledRunManager.referencedAsyncRunIds(),
|
|
522
|
+
]),
|
|
523
|
+
});
|
|
524
|
+
} catch (error) {
|
|
525
|
+
console.error("Failed to clean retained async subagent state:", error);
|
|
526
|
+
}
|
|
527
|
+
}, ASYNC_RETENTION_DELAY_MS);
|
|
528
|
+
asyncRetentionTimer.unref?.();
|
|
488
529
|
|
|
489
530
|
const executor = createSubagentExecutor({
|
|
490
531
|
pi,
|
|
@@ -498,6 +539,8 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
498
539
|
getSubagentSessionRoot,
|
|
499
540
|
expandTilde,
|
|
500
541
|
discoverAgents,
|
|
542
|
+
activateSupervisorTransport: () => supervisorChannel.activateTransport(),
|
|
543
|
+
refreshResultDelivery: () => refreshResultDelivery(),
|
|
501
544
|
});
|
|
502
545
|
executorScheduled = executor.executeScheduled;
|
|
503
546
|
|
|
@@ -568,6 +611,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
568
611
|
|
|
569
612
|
const slashBridge = registerSlashSubagentBridge({
|
|
570
613
|
events: pi.events,
|
|
614
|
+
asyncByDefault,
|
|
571
615
|
getContext: () => state.lastUiContext,
|
|
572
616
|
execute: (id, params, signal, onUpdate, ctx) =>
|
|
573
617
|
executeSubagentCollapsed(id, params, signal, onUpdate, ctx),
|
|
@@ -592,11 +636,12 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
592
636
|
});
|
|
593
637
|
|
|
594
638
|
|
|
595
|
-
const parameters = createSubagentParamsSchema(
|
|
639
|
+
const parameters = createSubagentParamsSchema();
|
|
596
640
|
const tool: ToolDefinition<typeof parameters, Details> = {
|
|
597
641
|
name: "subagent",
|
|
598
642
|
label: "Subagent",
|
|
599
643
|
description: buildSubagentToolDescription(config),
|
|
644
|
+
...buildSubagentToolPromptMetadata(config),
|
|
600
645
|
parameters,
|
|
601
646
|
|
|
602
647
|
execute(id, params, signal, onUpdate, ctx) {
|
|
@@ -607,7 +652,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
607
652
|
const gap = " ".repeat(config.mainWindowRenderer?.horizontalSpacing ?? 1);
|
|
608
653
|
const title = theme.fg("toolTitle", theme.bold("subagent"));
|
|
609
654
|
if (args.action) {
|
|
610
|
-
const target = args.agent ||
|
|
655
|
+
const target = args.agent || "";
|
|
611
656
|
return new Text(
|
|
612
657
|
`${title}${gap}${args.action}${target ? `${gap}${theme.fg("accent", target)}` : ""}`,
|
|
613
658
|
0, 0,
|
|
@@ -667,22 +712,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
667
712
|
foregroundDetachShortcut: config.foregroundDetachShortcut,
|
|
668
713
|
});
|
|
669
714
|
|
|
670
|
-
|
|
671
|
-
const controlNoticeSeenStoreKey = "__piSubagentVisibleControlNotices";
|
|
672
|
-
const previousEventUnsubscribes = globalStore[eventUnsubscribeStoreKey];
|
|
673
|
-
if (Array.isArray(previousEventUnsubscribes)) {
|
|
674
|
-
for (const unsubscribe of previousEventUnsubscribes) {
|
|
675
|
-
if (typeof unsubscribe !== "function") continue;
|
|
676
|
-
try {
|
|
677
|
-
unsubscribe();
|
|
678
|
-
} catch {
|
|
679
|
-
// Best effort cleanup for stale handlers from an older reload.
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
const existingVisibleControlNotices = globalStore[controlNoticeSeenStoreKey];
|
|
684
|
-
const visibleControlNotices = existingVisibleControlNotices instanceof Set ? existingVisibleControlNotices as Set<string> : new Set<string>();
|
|
685
|
-
globalStore[controlNoticeSeenStoreKey] = visibleControlNotices;
|
|
715
|
+
let visibleControlNotices = new Set<string>();
|
|
686
716
|
const activeHerdrRuns = () => projectActiveHerdrRuns(state);
|
|
687
717
|
const herdrStatusBridge = registerHerdrStatusBridge({
|
|
688
718
|
events: pi.events,
|
|
@@ -704,10 +734,13 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
704
734
|
};
|
|
705
735
|
const asyncStartedHandler = (payload: unknown) => {
|
|
706
736
|
handleStarted(payload);
|
|
737
|
+
supervisorChannel.activateTransport();
|
|
738
|
+
refreshResultDelivery();
|
|
707
739
|
fleetStatus?.refresh();
|
|
708
740
|
};
|
|
709
741
|
const asyncCompleteHandler = (payload: unknown) => {
|
|
710
742
|
handleComplete(payload);
|
|
743
|
+
refreshResultDelivery();
|
|
711
744
|
refreshActiveAsyncCapacity();
|
|
712
745
|
scheduledRunManager.handleAsyncCompletion(payload);
|
|
713
746
|
fleetStatus?.refresh();
|
|
@@ -724,7 +757,6 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
724
757
|
herdrStatusBridge.dispose,
|
|
725
758
|
rpcBridge.dispose,
|
|
726
759
|
];
|
|
727
|
-
globalStore[eventUnsubscribeStoreKey] = eventUnsubscribes;
|
|
728
760
|
|
|
729
761
|
pi.on("tool_result", (event, ctx) => {
|
|
730
762
|
if (event.toolName !== "subagent") return;
|
|
@@ -802,6 +834,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
802
834
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
803
835
|
if (sessionId) {
|
|
804
836
|
process.env[SUBAGENT_PARENT_SESSION_ENV] = sessionId;
|
|
837
|
+
parentSessionEnvValue = sessionId;
|
|
805
838
|
}
|
|
806
839
|
}
|
|
807
840
|
state.lastUiContext = ctx;
|
|
@@ -840,6 +873,89 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
840
873
|
fleetStatus?.setContext(ctx);
|
|
841
874
|
};
|
|
842
875
|
|
|
876
|
+
let runtimeCleaned = false;
|
|
877
|
+
const runtimeEntry: SubagentRuntimeEntry = {
|
|
878
|
+
sessionManager: null,
|
|
879
|
+
visibleControlNotices,
|
|
880
|
+
cleanup() {
|
|
881
|
+
if (runtimeCleaned) return;
|
|
882
|
+
runtimeCleaned = true;
|
|
883
|
+
const shuttingDownParentSession = parentSessionEnvValue;
|
|
884
|
+
clearTimeout(resultIndexCleanupTimer);
|
|
885
|
+
clearTimeout(asyncRetentionTimer);
|
|
886
|
+
asyncRetentionAbort.abort();
|
|
887
|
+
stopResultWatcher();
|
|
888
|
+
completionNotifier.dispose();
|
|
889
|
+
mainWatchdog.dispose();
|
|
890
|
+
scheduledRunManager.stop();
|
|
891
|
+
supervisorChannel.dispose();
|
|
892
|
+
waitSubscriptionManager.dispose();
|
|
893
|
+
fleetStatus?.dispose();
|
|
894
|
+
disposeAsyncJobTracker();
|
|
895
|
+
for (const timer of state.cleanupTimers.values()) clearTimeout(timer);
|
|
896
|
+
state.cleanupTimers.clear();
|
|
897
|
+
state.asyncJobs.clear();
|
|
898
|
+
for (const unsubscribe of eventUnsubscribes) {
|
|
899
|
+
try {
|
|
900
|
+
unsubscribe();
|
|
901
|
+
} catch {
|
|
902
|
+
// Best effort cleanup during shutdown or reload.
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
slashBridge.cancelAll();
|
|
906
|
+
slashBridge.dispose();
|
|
907
|
+
promptTemplateBridge.cancelAll();
|
|
908
|
+
promptTemplateBridge.dispose();
|
|
909
|
+
state.widgetsSuspended = false;
|
|
910
|
+
state.currentSessionId = null;
|
|
911
|
+
state.parentSessionFile = null;
|
|
912
|
+
parentSessionEnvValue = null;
|
|
913
|
+
if (shuttingDownParentSession && process.env[SUBAGENT_PARENT_SESSION_ENV] === shuttingDownParentSession) {
|
|
914
|
+
delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
915
|
+
}
|
|
916
|
+
if (runtimeEntry.sessionManager && runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
917
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
918
|
+
}
|
|
919
|
+
runtimeRegistry.activeEntries.delete(runtimeEntry);
|
|
920
|
+
if (runtimeRegistry.activeEntries.size === 0) clearSlashSnapshots();
|
|
921
|
+
try {
|
|
922
|
+
if (state.lastUiContext?.hasUI) state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
923
|
+
} catch (error) {
|
|
924
|
+
if (!isStaleExtensionContextError(error)) throw error;
|
|
925
|
+
}
|
|
926
|
+
state.lastUiContext = null;
|
|
927
|
+
},
|
|
928
|
+
};
|
|
929
|
+
|
|
930
|
+
const installRuntime = (ctx: ExtensionContext) => {
|
|
931
|
+
if (runtimeCleaned) {
|
|
932
|
+
throw new Error("Cannot restart a cleaned pi-subagents extension runtime; register a new extension instance.");
|
|
933
|
+
}
|
|
934
|
+
const sessionManager = ctx.sessionManager as object;
|
|
935
|
+
const previousRuntime = runtimeRegistry.bySessionManager.get(sessionManager);
|
|
936
|
+
const existingVisibleControlNotices = runtimeRegistry.visibleControlNoticesBySessionManager.get(sessionManager);
|
|
937
|
+
if (existingVisibleControlNotices) {
|
|
938
|
+
visibleControlNotices = existingVisibleControlNotices;
|
|
939
|
+
} else {
|
|
940
|
+
runtimeRegistry.visibleControlNoticesBySessionManager.set(sessionManager, visibleControlNotices);
|
|
941
|
+
}
|
|
942
|
+
runtimeEntry.visibleControlNotices = visibleControlNotices;
|
|
943
|
+
if (runtimeEntry.sessionManager && runtimeEntry.sessionManager !== sessionManager
|
|
944
|
+
&& runtimeRegistry.bySessionManager.get(runtimeEntry.sessionManager) === runtimeEntry) {
|
|
945
|
+
runtimeRegistry.bySessionManager.delete(runtimeEntry.sessionManager);
|
|
946
|
+
}
|
|
947
|
+
runtimeEntry.sessionManager = sessionManager;
|
|
948
|
+
runtimeRegistry.bySessionManager.set(sessionManager, runtimeEntry);
|
|
949
|
+
runtimeRegistry.activeEntries.add(runtimeEntry);
|
|
950
|
+
if (previousRuntime && previousRuntime !== runtimeEntry) {
|
|
951
|
+
try {
|
|
952
|
+
previousRuntime.cleanup();
|
|
953
|
+
} catch {
|
|
954
|
+
// Best effort cleanup for stale resources from the replaced runtime.
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
};
|
|
958
|
+
|
|
843
959
|
pi.on("agent_start", () => {
|
|
844
960
|
resumeWidgetsAfterCompaction();
|
|
845
961
|
herdrStatusBridge.agentStarted();
|
|
@@ -867,6 +983,7 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
867
983
|
});
|
|
868
984
|
|
|
869
985
|
pi.on("session_start", (event, ctx) => {
|
|
986
|
+
installRuntime(ctx);
|
|
870
987
|
const recovering = event.reason === "startup" || event.reason === "reload" || event.reason === "resume";
|
|
871
988
|
resetSessionState(ctx, recovering);
|
|
872
989
|
herdrStatusBridge.sessionStarted({
|
|
@@ -875,50 +992,11 @@ export default function registerSubagentExtension(pi: ExtensionAPI): void {
|
|
|
875
992
|
});
|
|
876
993
|
rpcBridge.emitReady(ctx);
|
|
877
994
|
supervisorChannel.start();
|
|
995
|
+
supervisorChannel.activateTransport();
|
|
878
996
|
});
|
|
879
997
|
|
|
880
998
|
pi.on("session_shutdown", async () => {
|
|
881
|
-
|
|
882
|
-
clearTimeout(resultIndexCleanupTimer);
|
|
883
|
-
stopResultWatcher();
|
|
884
|
-
state.currentSessionId = null;
|
|
885
|
-
state.parentSessionFile = null;
|
|
886
|
-
completionNotifier.dispose();
|
|
887
|
-
delete process.env[SUBAGENT_PARENT_SESSION_ENV];
|
|
888
|
-
for (const unsubscribe of eventUnsubscribes) {
|
|
889
|
-
try {
|
|
890
|
-
unsubscribe();
|
|
891
|
-
} catch {
|
|
892
|
-
// Best effort cleanup during shutdown.
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
if (globalStore[eventUnsubscribeStoreKey] === eventUnsubscribes) {
|
|
896
|
-
delete globalStore[eventUnsubscribeStoreKey];
|
|
897
|
-
}
|
|
898
|
-
scheduledRunManager.stop();
|
|
899
|
-
disposeAsyncJobTracker();
|
|
900
|
-
for (const timer of state.cleanupTimers.values()) {
|
|
901
|
-
clearTimeout(timer);
|
|
902
|
-
}
|
|
903
|
-
state.cleanupTimers.clear();
|
|
904
|
-
state.asyncJobs.clear();
|
|
905
|
-
clearSlashSnapshots();
|
|
906
|
-
slashBridge.cancelAll();
|
|
907
|
-
slashBridge.dispose();
|
|
908
|
-
promptTemplateBridge.cancelAll();
|
|
909
|
-
promptTemplateBridge.dispose();
|
|
910
|
-
supervisorChannel.dispose();
|
|
911
|
-
fleetStatus?.dispose();
|
|
912
|
-
if (globalStore[runtimeCleanupStoreKey] === runtimeCleanup) {
|
|
913
|
-
delete globalStore[runtimeCleanupStoreKey];
|
|
914
|
-
}
|
|
915
|
-
try {
|
|
916
|
-
if (state.lastUiContext?.hasUI) {
|
|
917
|
-
state.lastUiContext.ui.setWidget(WIDGET_KEY, undefined);
|
|
918
|
-
}
|
|
919
|
-
} catch (error) {
|
|
920
|
-
if (!isStaleExtensionContextError(error)) throw error;
|
|
921
|
-
}
|
|
999
|
+
runtimeEntry.cleanup();
|
|
922
1000
|
await herdrStatusBridge.flush();
|
|
923
1001
|
});
|
|
924
1002
|
}
|