@wrongstack/core 0.6.5 → 0.6.7
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/dist/{agent-bridge-BBXK_ppx.d.ts → agent-bridge-BCVxmrOn.d.ts} +1 -1
- package/dist/{agent-subagent-runner-DsSm9lKN.d.ts → agent-subagent-runner-BTloSwjE.d.ts} +3 -3
- package/dist/{compactor-C8NhpSt5.d.ts → compactor-BUJ8550X.d.ts} +1 -1
- package/dist/{config-DfC6g6KV.d.ts → config-BHkYGzUT.d.ts} +1 -1
- package/dist/{context-DN5v-uQX.d.ts → context-C8mBUzBv.d.ts} +3 -5
- package/dist/coordination/index.d.ts +10 -10
- package/dist/coordination/index.js +62 -30
- package/dist/coordination/index.js.map +1 -1
- package/dist/defaults/index.d.ts +21 -21
- package/dist/defaults/index.js +130 -32
- package/dist/defaults/index.js.map +1 -1
- package/dist/{events-CJqwQl8G.d.ts → events-DounT6pP.d.ts} +10 -1
- package/dist/execution/index.d.ts +56 -14
- package/dist/execution/index.js +74 -18
- package/dist/execution/index.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-store-_Er467ya.d.ts → goal-store-HHgaq5ue.d.ts} +1 -1
- package/dist/{index-CXnWsGBp.d.ts → index-BlVITqm-.d.ts} +6 -9
- package/dist/{index-DcnXDPdY.d.ts → index-DiIGZh5I.d.ts} +20 -5
- package/dist/index.d.ts +28 -28
- package/dist/index.js +251 -131
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-CevFHHM1.d.ts → mcp-servers-CXFfDN_d.d.ts} +3 -3
- package/dist/models/index.d.ts +2 -2
- package/dist/{multi-agent-D5IbASk_.d.ts → multi-agent-Iy8YUe3v.d.ts} +4 -4
- package/dist/observability/index.d.ts +2 -2
- package/dist/{path-resolver-CBx_q1HA.d.ts → path-resolver-DwJO5XBn.d.ts} +2 -2
- package/dist/{plan-templates-BEOllUJV.d.ts → plan-templates-CG_pInJV.d.ts} +4 -4
- package/dist/{provider-runner-Byh5TcJs.d.ts → provider-runner-DoohzfyD.d.ts} +3 -3
- package/dist/{retry-policy-BZSIMxrJ.d.ts → retry-policy-DOnwHAyz.d.ts} +1 -1
- package/dist/sdd/index.d.ts +9 -4
- package/dist/sdd/index.js +21 -0
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-scrubber-CT7wefiO.d.ts → secret-scrubber-C9UJxFCJ.d.ts} +1 -1
- package/dist/{secret-scrubber-I0QHY_ob.d.ts → secret-scrubber-DcTK9JHC.d.ts} +1 -1
- package/dist/security/index.d.ts +3 -3
- package/dist/{selector-DDb_mq9X.d.ts → selector-DVo3eT-P.d.ts} +1 -1
- package/dist/{session-reader-B9nVkziM.d.ts → session-reader-BXWxFAhS.d.ts} +1 -1
- package/dist/storage/index.d.ts +6 -6
- package/dist/storage/index.js +3 -2
- package/dist/storage/index.js.map +1 -1
- package/dist/{system-prompt-gL06H9P4.d.ts → system-prompt-BBGles-N.d.ts} +1 -1
- package/dist/{task-graph-BITvWt4t.d.ts → task-graph-D1YQbpxF.d.ts} +1 -0
- package/dist/{tool-executor-BF7QfYVE.d.ts → tool-executor-CwpjhwHC.d.ts} +4 -4
- package/dist/types/index.d.ts +16 -16
- package/dist/types/index.js +1 -3
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/package.json +1 -1
- package/skills/sdd/SKILL.md +122 -106
package/dist/index.js
CHANGED
|
@@ -2558,10 +2558,8 @@ var InMemoryAgentBridge = class {
|
|
|
2558
2558
|
this.pendingRequests.delete(correlationId);
|
|
2559
2559
|
reject(new Error(`Request ${correlationId} timed out after ${timeout}ms`));
|
|
2560
2560
|
}, timeout);
|
|
2561
|
-
if (this.
|
|
2561
|
+
if (!this.inflightGuards.has(correlationId)) {
|
|
2562
2562
|
clearTimeout(timer);
|
|
2563
|
-
this.inflightGuards.delete(correlationId);
|
|
2564
|
-
this.pendingRequests.delete(correlationId);
|
|
2565
2563
|
reject(new Error("Bridge stopped"));
|
|
2566
2564
|
return;
|
|
2567
2565
|
}
|
|
@@ -5745,7 +5743,7 @@ var DirectorStateCheckpoint = class {
|
|
|
5745
5743
|
this.timer = null;
|
|
5746
5744
|
}
|
|
5747
5745
|
await this.persist();
|
|
5748
|
-
|
|
5746
|
+
while (this.rewriteRequested) {
|
|
5749
5747
|
this.rewriteRequested = false;
|
|
5750
5748
|
await this.persist();
|
|
5751
5749
|
}
|
|
@@ -7428,7 +7426,8 @@ function formatGoal(goal, journalLimit = 10) {
|
|
|
7428
7426
|
lines.push(`Set: ${goal.setAt}`);
|
|
7429
7427
|
lines.push(`Last activity: ${goal.lastActivityAt}`);
|
|
7430
7428
|
lines.push(`Iterations: ${goal.iterations}`);
|
|
7431
|
-
|
|
7429
|
+
const stateLabel = goal.goalState ?? "active";
|
|
7430
|
+
lines.push(`State: ${stateLabel}${goal.iterations > 0 ? ` (iteration #${goal.iterations})` : ""}`);
|
|
7432
7431
|
lines.push(`Engine: ${goal.engineState}`);
|
|
7433
7432
|
const usage = summarizeUsage(goal);
|
|
7434
7433
|
if (usage.iterationsWithUsage > 0) {
|
|
@@ -7453,7 +7452,8 @@ function formatGoal(goal, journalLimit = 10) {
|
|
|
7453
7452
|
// src/execution/eternal-autonomy.ts
|
|
7454
7453
|
var execFileP = promisify(execFile);
|
|
7455
7454
|
var BRAINSTORM_DONE = /* @__PURE__ */ Symbol("brainstorm-done");
|
|
7456
|
-
var GOAL_COMPLETE_MARKER = /^\s*\[goal[_\s-]
|
|
7455
|
+
var GOAL_COMPLETE_MARKER = /^\s*\[goal[_\s-]*complete\]\s*$/im;
|
|
7456
|
+
var GOAL_CLEAR_MARKER = /^\s*\[\/?goal\s*clear\]\s*$/im;
|
|
7457
7457
|
var EternalAutonomyEngine = class {
|
|
7458
7458
|
constructor(opts) {
|
|
7459
7459
|
this.opts = opts;
|
|
@@ -7535,23 +7535,33 @@ var EternalAutonomyEngine = class {
|
|
|
7535
7535
|
* `agent.run()` avoids race conditions on the shared Context.
|
|
7536
7536
|
*/
|
|
7537
7537
|
async runOneIteration() {
|
|
7538
|
+
const emit = (stage) => {
|
|
7539
|
+
this.opts.onStage?.(stage);
|
|
7540
|
+
};
|
|
7538
7541
|
const goal = await loadGoal(this.goalPath);
|
|
7539
7542
|
if (!goal) {
|
|
7543
|
+
emit({ phase: "stopped" });
|
|
7540
7544
|
this.stopRequested = true;
|
|
7541
7545
|
return false;
|
|
7542
7546
|
}
|
|
7543
7547
|
const missionState = goal.goalState ?? "active";
|
|
7544
7548
|
if (missionState !== "active") {
|
|
7549
|
+
emit({ phase: missionState === "paused" ? "paused" : "stopped" });
|
|
7545
7550
|
this.stopRequested = true;
|
|
7546
7551
|
return false;
|
|
7547
7552
|
}
|
|
7553
|
+
emit({ phase: "decide", reason: "picking next task" });
|
|
7548
7554
|
const action = await this.decide(goal);
|
|
7549
7555
|
if (!action) {
|
|
7550
7556
|
if (!this.stopRequested) {
|
|
7557
|
+
emit({ phase: "sleep", ms: 5e3 });
|
|
7551
7558
|
await sleep(5e3);
|
|
7559
|
+
} else {
|
|
7560
|
+
emit({ phase: "stopped" });
|
|
7552
7561
|
}
|
|
7553
7562
|
return false;
|
|
7554
7563
|
}
|
|
7564
|
+
emit({ phase: "execute", task: action.task });
|
|
7555
7565
|
const ctrl = new AbortController();
|
|
7556
7566
|
this.currentCtrl = ctrl;
|
|
7557
7567
|
const timer = setTimeout(
|
|
@@ -7629,6 +7639,7 @@ var EternalAutonomyEngine = class {
|
|
|
7629
7639
|
tokens,
|
|
7630
7640
|
costUsd
|
|
7631
7641
|
});
|
|
7642
|
+
emit({ phase: "reflect", status, note });
|
|
7632
7643
|
let iterationIndex = 0;
|
|
7633
7644
|
try {
|
|
7634
7645
|
const reloaded = await loadGoal(this.goalPath);
|
|
@@ -7650,6 +7661,7 @@ var EternalAutonomyEngine = class {
|
|
|
7650
7661
|
this.consecutiveTransientRetries++;
|
|
7651
7662
|
const delay = this.computeTransientBackoffMs();
|
|
7652
7663
|
if (delay > 0) {
|
|
7664
|
+
emit({ phase: "sleep", ms: delay });
|
|
7653
7665
|
await this.sleepInterruptible(delay);
|
|
7654
7666
|
}
|
|
7655
7667
|
return false;
|
|
@@ -7663,8 +7675,16 @@ var EternalAutonomyEngine = class {
|
|
|
7663
7675
|
return false;
|
|
7664
7676
|
}
|
|
7665
7677
|
this.consecutiveTransientRetries = 0;
|
|
7678
|
+
const cycleGapMs = this.opts.cycleGapMs ?? 1e3;
|
|
7679
|
+
emit({ phase: "sleep", ms: cycleGapMs });
|
|
7680
|
+
await sleep(cycleGapMs);
|
|
7666
7681
|
if (GOAL_COMPLETE_MARKER.test(finalText)) {
|
|
7667
|
-
await this.
|
|
7682
|
+
await this.clearGoalManually(finalText);
|
|
7683
|
+
this.stopRequested = true;
|
|
7684
|
+
return true;
|
|
7685
|
+
}
|
|
7686
|
+
if (GOAL_CLEAR_MARKER.test(finalText)) {
|
|
7687
|
+
await this.clearGoalManually(finalText);
|
|
7668
7688
|
this.stopRequested = true;
|
|
7669
7689
|
return true;
|
|
7670
7690
|
}
|
|
@@ -7964,6 +7984,30 @@ ${recentJournal}` : "No prior iterations.",
|
|
|
7964
7984
|
});
|
|
7965
7985
|
await saveGoal(this.goalPath, withEntry);
|
|
7966
7986
|
}
|
|
7987
|
+
/**
|
|
7988
|
+
* Manually clear the goal — equivalent to `/goal clear` typed by the user.
|
|
7989
|
+
* Sets goalState to `abandoned`, removes the goal file, and fires
|
|
7990
|
+
* `onEternalStop` so the REPL returns to normal mode.
|
|
7991
|
+
*/
|
|
7992
|
+
async clearGoalManually(note) {
|
|
7993
|
+
const current = await loadGoal(this.goalPath);
|
|
7994
|
+
if (current) {
|
|
7995
|
+
const abandoned = { ...current, goalState: "abandoned" };
|
|
7996
|
+
await saveGoal(this.goalPath, abandoned);
|
|
7997
|
+
}
|
|
7998
|
+
try {
|
|
7999
|
+
const { unlink: unlink10 } = await import('fs/promises');
|
|
8000
|
+
await unlink10(this.goalPath);
|
|
8001
|
+
} catch {
|
|
8002
|
+
}
|
|
8003
|
+
this.opts.onEternalStop?.();
|
|
8004
|
+
void this.appendIterationEntry({
|
|
8005
|
+
source: "manual",
|
|
8006
|
+
task: "goal cleared",
|
|
8007
|
+
status: "success",
|
|
8008
|
+
note: note.slice(0, 240)
|
|
8009
|
+
});
|
|
8010
|
+
}
|
|
7967
8011
|
async appendFailure(task, note) {
|
|
7968
8012
|
await this.appendIterationEntry({ source: "manual", task, status: "failure", note });
|
|
7969
8013
|
}
|
|
@@ -8031,10 +8075,10 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
8031
8075
|
* or hung listener (Director not built / event filter detached mid-run)
|
|
8032
8076
|
* leaves the budget over-limit and never enforces anything, since
|
|
8033
8077
|
* `checkLimit` returns synchronously via `void this.checkLimitAsync`.
|
|
8034
|
-
*
|
|
8035
|
-
*
|
|
8078
|
+
* Raised from 30s to 60s to give subagents more headroom before
|
|
8079
|
+
* the threshold negotiation times out and triggers a hard stop.
|
|
8036
8080
|
*/
|
|
8037
|
-
static DECISION_TIMEOUT_MS =
|
|
8081
|
+
static DECISION_TIMEOUT_MS = 6e4;
|
|
8038
8082
|
/**
|
|
8039
8083
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
8040
8084
|
* Used by `checkLimitAsync` to emit `budget.threshold_reached` events.
|
|
@@ -8565,13 +8609,19 @@ var FLEET_ROSTER = {
|
|
|
8565
8609
|
"security-scanner": SECURITY_SCANNER_AGENT
|
|
8566
8610
|
};
|
|
8567
8611
|
var FLEET_ROSTER_BUDGETS = {
|
|
8568
|
-
"audit-log": { timeoutMs:
|
|
8569
|
-
"bug-hunter": { timeoutMs:
|
|
8570
|
-
"refactor-planner": { timeoutMs:
|
|
8571
|
-
"security-scanner": { timeoutMs:
|
|
8612
|
+
"audit-log": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 5e3, maxToolCalls: 15e3 },
|
|
8613
|
+
"bug-hunter": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 },
|
|
8614
|
+
"refactor-planner": { timeoutMs: 7.5 * 60 * 60 * 1e3, maxIterations: 6e3, maxToolCalls: 18e3 },
|
|
8615
|
+
"security-scanner": { timeoutMs: 10 * 60 * 60 * 1e3, maxIterations: 8e3, maxToolCalls: 2e4 }
|
|
8616
|
+
};
|
|
8617
|
+
var GENERIC_SUBAGENT_BUDGET = {
|
|
8618
|
+
timeoutMs: 3 * 60 * 60 * 1e3,
|
|
8619
|
+
maxIterations: 5e3,
|
|
8620
|
+
maxToolCalls: 15e3
|
|
8572
8621
|
};
|
|
8573
8622
|
function applyRosterBudget(cfg) {
|
|
8574
|
-
const
|
|
8623
|
+
const roleBudget = cfg.role ? FLEET_ROSTER_BUDGETS[cfg.role] : void 0;
|
|
8624
|
+
const defaultBudget = roleBudget ?? (cfg.name ? GENERIC_SUBAGENT_BUDGET : void 0);
|
|
8575
8625
|
if (!defaultBudget) return cfg;
|
|
8576
8626
|
return {
|
|
8577
8627
|
...cfg,
|
|
@@ -8643,7 +8693,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
8643
8693
|
// hasParentBridge() — the type now reflects this.
|
|
8644
8694
|
parentBridge: null,
|
|
8645
8695
|
doneCondition: this.config.doneCondition,
|
|
8646
|
-
maxConcurrent: this.config.maxConcurrent ??
|
|
8696
|
+
maxConcurrent: this.config.maxConcurrent ?? 16
|
|
8647
8697
|
};
|
|
8648
8698
|
this.subagents.set(id, {
|
|
8649
8699
|
config: { ...subagent, id },
|
|
@@ -8770,7 +8820,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
8770
8820
|
}
|
|
8771
8821
|
}
|
|
8772
8822
|
canDispatch() {
|
|
8773
|
-
const max = this.config.maxConcurrent ??
|
|
8823
|
+
const max = this.config.maxConcurrent ?? 16;
|
|
8774
8824
|
return this.inFlight < max && this.pendingTasks.length > 0;
|
|
8775
8825
|
}
|
|
8776
8826
|
takeNextDispatchableTask() {
|
|
@@ -8864,13 +8914,18 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
8864
8914
|
task.subagentId = subagentId;
|
|
8865
8915
|
subagent.context.tasks.push(task);
|
|
8866
8916
|
this.emit("task.assigned", { task, subagentId });
|
|
8917
|
+
const rawMaxIterations = subagent.config.maxIterations;
|
|
8918
|
+
const rawMaxToolCalls = subagent.config.maxToolCalls;
|
|
8919
|
+
const rawMaxTokens = subagent.config.maxTokens;
|
|
8920
|
+
const rawMaxCostUsd = subagent.config.maxCostUsd;
|
|
8921
|
+
const rawTimeoutMs = subagent.config.timeoutMs;
|
|
8867
8922
|
const configWithRosterDefaults = applyRosterBudget(subagent.config);
|
|
8868
8923
|
const budget = new SubagentBudget({
|
|
8869
|
-
maxIterations:
|
|
8870
|
-
maxToolCalls:
|
|
8871
|
-
maxTokens:
|
|
8872
|
-
maxCostUsd:
|
|
8873
|
-
timeoutMs:
|
|
8924
|
+
maxIterations: rawMaxIterations ?? this.config.defaultBudget?.maxIterations ?? configWithRosterDefaults.maxIterations,
|
|
8925
|
+
maxToolCalls: rawMaxToolCalls ?? this.config.defaultBudget?.maxToolCalls ?? configWithRosterDefaults.maxToolCalls,
|
|
8926
|
+
maxTokens: rawMaxTokens ?? this.config.defaultBudget?.maxTokens ?? configWithRosterDefaults.maxTokens,
|
|
8927
|
+
maxCostUsd: rawMaxCostUsd ?? this.config.defaultBudget?.maxCostUsd ?? configWithRosterDefaults.maxCostUsd,
|
|
8928
|
+
timeoutMs: rawTimeoutMs ?? this.config.defaultBudget?.timeoutMs ?? configWithRosterDefaults.timeoutMs
|
|
8874
8929
|
});
|
|
8875
8930
|
subagent.activeBudget = budget;
|
|
8876
8931
|
if (!this.runner) {
|
|
@@ -8941,7 +8996,7 @@ var DefaultMultiAgentCoordinator = class extends EventEmitter {
|
|
|
8941
8996
|
kind: "timeout",
|
|
8942
8997
|
used: elapsed,
|
|
8943
8998
|
limit,
|
|
8944
|
-
timeoutMs:
|
|
8999
|
+
timeoutMs: 6e4,
|
|
8945
9000
|
extend: (extra) => resolveDecision({ extend: extra }),
|
|
8946
9001
|
deny: () => resolveDecision("stop")
|
|
8947
9002
|
});
|
|
@@ -10371,7 +10426,12 @@ var Director = class {
|
|
|
10371
10426
|
}
|
|
10372
10427
|
}
|
|
10373
10428
|
}
|
|
10374
|
-
|
|
10429
|
+
let result;
|
|
10430
|
+
try {
|
|
10431
|
+
result = await this.coordinator.spawn(config);
|
|
10432
|
+
} catch (err) {
|
|
10433
|
+
throw err;
|
|
10434
|
+
}
|
|
10375
10435
|
if (this.fleetManager) {
|
|
10376
10436
|
this.fleetManager.recordSpawn(result.subagentId, config, priceLookup);
|
|
10377
10437
|
} else {
|
|
@@ -10819,7 +10879,7 @@ var Director = class {
|
|
|
10819
10879
|
}
|
|
10820
10880
|
};
|
|
10821
10881
|
function createDelegateTool(opts) {
|
|
10822
|
-
const defaultTimeoutMs = opts.defaultTimeoutMs ??
|
|
10882
|
+
const defaultTimeoutMs = opts.defaultTimeoutMs ?? 30 * 60 * 1e3;
|
|
10823
10883
|
const rosterIds = opts.roster ? Object.keys(opts.roster) : [];
|
|
10824
10884
|
const inputSchema = {
|
|
10825
10885
|
type: "object",
|
|
@@ -10835,7 +10895,7 @@ function createDelegateTool(opts) {
|
|
|
10835
10895
|
},
|
|
10836
10896
|
name: {
|
|
10837
10897
|
type: "string",
|
|
10838
|
-
description: "Display name for
|
|
10898
|
+
description: "Display name for free-form subagents (not using a roster role). The subagent gets a large default budget (3h, 5000 iter, 15000 tool calls). Required when `role` is omitted."
|
|
10839
10899
|
},
|
|
10840
10900
|
provider: {
|
|
10841
10901
|
type: "string",
|
|
@@ -10866,8 +10926,8 @@ function createDelegateTool(opts) {
|
|
|
10866
10926
|
};
|
|
10867
10927
|
return {
|
|
10868
10928
|
name: "delegate",
|
|
10869
|
-
description: "Hand a discrete piece of work to a dedicated subagent and wait for its result. The subagent has its own context, its own LLM call, and its own budget \u2014 use this when a task is self-contained, would otherwise blow up your context, or benefits from a specialized role (bug-hunter, security-scanner, refactor-planner, audit-log). YOU decide how big the budget is: pass `timeoutMs`, `maxIterations`, and `maxToolCalls` sized to the actual work. There is no hidden cap forcing a 3-minute / 80-iteration limit \u2014 if a monorepo audit needs 2 hours and 500 tool calls, ask for that. Call multiple delegates in parallel through the provider's parallel-tool-call surface to fan work out across roles.",
|
|
10870
|
-
usageHint: "Set `task` to a complete instruction. Either pick `role` from the roster or pass `name`
|
|
10929
|
+
description: "Hand a discrete piece of work to a dedicated subagent and wait for its result. The subagent has its own context, its own LLM call, and its own budget \u2014 use this when a task is self-contained, would otherwise blow up your context, or benefits from a specialized role (bug-hunter, security-scanner, refactor-planner, audit-log). For free-form coding tasks (not tied to a pre-defined role), pass `name` + `task` \u2014 the subagent runs as a general-purpose coding agent with a large default budget. YOU decide how big the budget is: pass `timeoutMs`, `maxIterations`, and `maxToolCalls` sized to the actual work. There is no hidden cap forcing a 3-minute / 80-iteration limit \u2014 if a monorepo audit needs 2 hours and 500 tool calls, ask for that. Call multiple delegates in parallel through the provider's parallel-tool-call surface to fan work out across roles.",
|
|
10930
|
+
usageHint: "Set `task` to a complete instruction. Either pick `role` from the roster (audit-log, bug-hunter, refactor-planner, security-scanner) or pass `name` to run a free-form coding agent. For non-trivial work, also pass `timeoutMs`, `maxIterations`, and `maxToolCalls`. Returns the subagent's `TaskResult` \u2014 including the textual `result`, iteration count, tool count, and duration. Auto-promotes the host into director mode on first call.",
|
|
10871
10931
|
permission: "auto",
|
|
10872
10932
|
mutating: false,
|
|
10873
10933
|
inputSchema,
|
|
@@ -10915,6 +10975,7 @@ function createDelegateTool(opts) {
|
|
|
10915
10975
|
model: i.model,
|
|
10916
10976
|
systemPromptOverride: i.systemPromptOverride
|
|
10917
10977
|
};
|
|
10978
|
+
cfg = applyRosterBudget({ ...cfg, name: i.name });
|
|
10918
10979
|
}
|
|
10919
10980
|
if (typeof i.maxIterations === "number" && i.maxIterations > 0) {
|
|
10920
10981
|
cfg.maxIterations = i.maxIterations;
|
|
@@ -10922,7 +10983,7 @@ function createDelegateTool(opts) {
|
|
|
10922
10983
|
if (typeof i.maxToolCalls === "number" && i.maxToolCalls > 0) {
|
|
10923
10984
|
cfg.maxToolCalls = i.maxToolCalls;
|
|
10924
10985
|
}
|
|
10925
|
-
const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ??
|
|
10986
|
+
const SUBAGENT_TIMEOUT_BUFFER_MS = opts.subagentTimeoutBufferMs ?? 6e4;
|
|
10926
10987
|
const desiredSubTimeout = Math.max(3e4, timeoutMs - SUBAGENT_TIMEOUT_BUFFER_MS);
|
|
10927
10988
|
if (!cfg.timeoutMs || cfg.timeoutMs > desiredSubTimeout) {
|
|
10928
10989
|
cfg.timeoutMs = desiredSubTimeout;
|
|
@@ -10959,6 +11020,7 @@ function createDelegateTool(opts) {
|
|
|
10959
11020
|
const errorKind = result.error?.kind;
|
|
10960
11021
|
const retryable = result.error?.retryable;
|
|
10961
11022
|
const backoffMs = result.error?.backoffMs;
|
|
11023
|
+
const summary = buildDelegateSummary(i.role, result);
|
|
10962
11024
|
return {
|
|
10963
11025
|
ok: result.status === "success",
|
|
10964
11026
|
status: result.status,
|
|
@@ -10974,7 +11036,10 @@ function createDelegateTool(opts) {
|
|
|
10974
11036
|
toolCalls: result.toolCalls,
|
|
10975
11037
|
durationMs: result.durationMs,
|
|
10976
11038
|
...partial ? { partial } : {},
|
|
10977
|
-
...hintForKind(errorKind, retryable, backoffMs, partial) ? { hint: hintForKind(errorKind, retryable, backoffMs, partial) } : {}
|
|
11039
|
+
...hintForKind(errorKind, retryable, backoffMs, partial) ? { hint: hintForKind(errorKind, retryable, backoffMs, partial) } : {},
|
|
11040
|
+
// Summary is included so callers (TUI, CLI renderer) can surface
|
|
11041
|
+
// it as a chat history line — the LLM also sees it for continuity.
|
|
11042
|
+
summary
|
|
10978
11043
|
};
|
|
10979
11044
|
} catch (err) {
|
|
10980
11045
|
return {
|
|
@@ -10987,10 +11052,11 @@ function createDelegateTool(opts) {
|
|
|
10987
11052
|
};
|
|
10988
11053
|
}
|
|
10989
11054
|
function instantiateRosterConfig2(role, base) {
|
|
11055
|
+
const withBudget = applyRosterBudget({ ...base, role });
|
|
10990
11056
|
return {
|
|
10991
|
-
...
|
|
10992
|
-
//
|
|
10993
|
-
//
|
|
11057
|
+
...withBudget,
|
|
11058
|
+
// Give each spawn a fresh id so parallel or repeated delegates
|
|
11059
|
+
// can use the same role safely.
|
|
10994
11060
|
id: `${role}-${randomUUID().slice(0, 8)}`
|
|
10995
11061
|
};
|
|
10996
11062
|
}
|
|
@@ -11050,6 +11116,18 @@ ${partial.lastAssistantText}`;
|
|
|
11050
11116
|
return retryable ? "Failure classified as retryable. Try again with the same input." : void 0;
|
|
11051
11117
|
}
|
|
11052
11118
|
}
|
|
11119
|
+
function buildDelegateSummary(role, result) {
|
|
11120
|
+
const roleLabel = role ?? "subagent";
|
|
11121
|
+
const ms = result.durationMs;
|
|
11122
|
+
const duration = ms < 6e4 ? `${Math.round(ms / 1e3)}s` : ms < 36e5 ? `${Math.round(ms / 6e4)}m` : `${(ms / 36e5).toFixed(1)}h`;
|
|
11123
|
+
if (result.status === "success") {
|
|
11124
|
+
const preview = typeof result.result === "string" ? result.result.trim().slice(0, 120).replace(/\n+/g, " ") : null;
|
|
11125
|
+
const tail = preview ? ` \u2014 ${preview}` : "";
|
|
11126
|
+
return `[${roleLabel}] done in ${duration} (${result.iterations} iter, ${result.toolCalls} tools)${tail}`;
|
|
11127
|
+
}
|
|
11128
|
+
const errLabel = result.error?.kind ?? result.status;
|
|
11129
|
+
return `[${roleLabel}] ${result.status} after ${duration} (${result.iterations} iter, ${result.toolCalls} tools) \u2014 ${errLabel}`;
|
|
11130
|
+
}
|
|
11053
11131
|
async function readSubagentPartial(opts, subagentId) {
|
|
11054
11132
|
if (!opts.sessionsRoot) return void 0;
|
|
11055
11133
|
const candidates = [];
|
|
@@ -11715,6 +11793,10 @@ var TaskTracker = class {
|
|
|
11715
11793
|
node.updatedAt = now;
|
|
11716
11794
|
if (status === "completed") {
|
|
11717
11795
|
node.completedAt = now;
|
|
11796
|
+
node.startedAt = node.startedAt ?? now;
|
|
11797
|
+
}
|
|
11798
|
+
if (status === "in_progress") {
|
|
11799
|
+
node.startedAt = now;
|
|
11718
11800
|
}
|
|
11719
11801
|
this.transitions.push({ from, to: status, timestamp: now, reason });
|
|
11720
11802
|
if (status === "completed") {
|
|
@@ -11726,6 +11808,23 @@ var TaskTracker = class {
|
|
|
11726
11808
|
this.graph.updatedAt = now;
|
|
11727
11809
|
this.persist();
|
|
11728
11810
|
}
|
|
11811
|
+
/**
|
|
11812
|
+
* Update node fields (title, description, priority, estimateHours, tags).
|
|
11813
|
+
* Does NOT change status. Use updateNodeStatus for status changes.
|
|
11814
|
+
*/
|
|
11815
|
+
updateNode(id, patch) {
|
|
11816
|
+
if (!this.graph) throw new Error("No graph loaded");
|
|
11817
|
+
const node = this.graph.nodes.get(id);
|
|
11818
|
+
if (!node) throw new Error(`Node ${id} not found`);
|
|
11819
|
+
if (patch.title !== void 0) node.title = patch.title;
|
|
11820
|
+
if (patch.description !== void 0) node.description = patch.description;
|
|
11821
|
+
if (patch.priority !== void 0) node.priority = patch.priority;
|
|
11822
|
+
if (patch.estimateHours !== void 0) node.estimateHours = patch.estimateHours;
|
|
11823
|
+
if (patch.tags !== void 0) node.tags = patch.tags;
|
|
11824
|
+
node.updatedAt = Date.now();
|
|
11825
|
+
this.graph.updatedAt = node.updatedAt;
|
|
11826
|
+
this.persist();
|
|
11827
|
+
}
|
|
11729
11828
|
getNode(id) {
|
|
11730
11829
|
return this.graph?.nodes.get(id);
|
|
11731
11830
|
}
|
|
@@ -17950,10 +18049,15 @@ var Agent = class {
|
|
|
17950
18049
|
});
|
|
17951
18050
|
let finalText = "";
|
|
17952
18051
|
let iterations = 0;
|
|
18052
|
+
const delegateSummaries = [];
|
|
17953
18053
|
let effectiveLimit = opts.maxIterations ?? this.maxIterations;
|
|
17954
18054
|
const hasHardLimit = effectiveLimit > 0 && Number.isFinite(effectiveLimit);
|
|
17955
18055
|
let recoveryRetries = 0;
|
|
17956
18056
|
const autonomousContinue = opts.autonomousContinue ?? this.autonomousContinue;
|
|
18057
|
+
const onSubagentDone = ({ summary, ok }) => {
|
|
18058
|
+
delegateSummaries.push({ summary, ok });
|
|
18059
|
+
};
|
|
18060
|
+
const offSubagentDone = this.events.on("subagent.done", onSubagentDone);
|
|
17957
18061
|
const diRunner = this.container.has(TOKENS.ProviderRunner) ? this.container.resolve(TOKENS.ProviderRunner) : null;
|
|
17958
18062
|
const baseRunner = diRunner ? (ctx, req) => diRunner.run({
|
|
17959
18063
|
provider: ctx.provider,
|
|
@@ -17975,116 +18079,122 @@ var Agent = class {
|
|
|
17975
18079
|
tracer: this.tracer
|
|
17976
18080
|
});
|
|
17977
18081
|
const customRunner = this.extensions.wrapProviderRunner(baseRunner);
|
|
17978
|
-
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
return { status: "aborted", iterations };
|
|
17982
|
-
}
|
|
17983
|
-
if (autonomousContinue) {
|
|
17984
|
-
consumeAutonomousContinue(this.ctx);
|
|
17985
|
-
}
|
|
17986
|
-
const limitCheck = await this.checkIterationLimit(
|
|
17987
|
-
i,
|
|
17988
|
-
effectiveLimit,
|
|
17989
|
-
hasHardLimit,
|
|
17990
|
-
iterations
|
|
17991
|
-
);
|
|
17992
|
-
effectiveLimit = limitCheck.limit;
|
|
17993
|
-
if (limitCheck.exit) {
|
|
17994
|
-
return { ...limitCheck.exit, finalText };
|
|
17995
|
-
}
|
|
17996
|
-
await this.extensions.runBeforeIteration(this.ctx, i);
|
|
17997
|
-
this.events.emit("iteration.started", { ctx: this.ctx, index: i });
|
|
17998
|
-
const req = await this.buildAndRunRequestPipeline(opts);
|
|
17999
|
-
let res;
|
|
18000
|
-
try {
|
|
18001
|
-
res = await customRunner(this.ctx, req);
|
|
18002
|
-
recoveryRetries = 0;
|
|
18003
|
-
} catch (err) {
|
|
18082
|
+
try {
|
|
18083
|
+
for (let i = 0; ; i++) {
|
|
18084
|
+
iterations = i + 1;
|
|
18004
18085
|
if (controller.signal.aborted) {
|
|
18005
|
-
|
|
18006
|
-
return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
|
|
18086
|
+
return { status: "aborted", iterations };
|
|
18007
18087
|
}
|
|
18008
|
-
|
|
18009
|
-
|
|
18010
|
-
|
|
18088
|
+
if (autonomousContinue) {
|
|
18089
|
+
consumeAutonomousContinue(this.ctx);
|
|
18090
|
+
}
|
|
18091
|
+
const limitCheck = await this.checkIterationLimit(
|
|
18092
|
+
i,
|
|
18093
|
+
effectiveLimit,
|
|
18094
|
+
hasHardLimit,
|
|
18095
|
+
iterations,
|
|
18096
|
+
delegateSummaries
|
|
18097
|
+
);
|
|
18098
|
+
effectiveLimit = limitCheck.limit;
|
|
18099
|
+
if (limitCheck.exit) {
|
|
18100
|
+
return { ...limitCheck.exit, finalText };
|
|
18101
|
+
}
|
|
18102
|
+
await this.extensions.runBeforeIteration(this.ctx, i);
|
|
18103
|
+
this.events.emit("iteration.started", { ctx: this.ctx, index: i });
|
|
18104
|
+
const req = await this.buildAndRunRequestPipeline(opts);
|
|
18105
|
+
let res;
|
|
18106
|
+
try {
|
|
18107
|
+
res = await customRunner(this.ctx, req);
|
|
18108
|
+
recoveryRetries = 0;
|
|
18109
|
+
} catch (err) {
|
|
18110
|
+
if (controller.signal.aborted) {
|
|
18011
18111
|
this.events.emit("error", { err: toError(err), phase: "provider" });
|
|
18012
|
-
return { status: "
|
|
18112
|
+
return { status: "aborted", iterations, error: toWrongStackError(err, "AGENT_ABORTED") };
|
|
18013
18113
|
}
|
|
18014
|
-
|
|
18015
|
-
|
|
18016
|
-
|
|
18114
|
+
const extDecision = await this.extensions.runOnError(this.ctx, err, "provider", i);
|
|
18115
|
+
if (extDecision) {
|
|
18116
|
+
if (extDecision.action === "fail") {
|
|
18117
|
+
this.events.emit("error", { err: toError(err), phase: "provider" });
|
|
18118
|
+
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
18119
|
+
}
|
|
18120
|
+
if (extDecision.action === "continue") {
|
|
18121
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
18122
|
+
continue;
|
|
18123
|
+
}
|
|
18124
|
+
if (extDecision.action === "retry") {
|
|
18125
|
+
recoveryRetries++;
|
|
18126
|
+
if (recoveryRetries > 2) {
|
|
18127
|
+
this.events.emit("error", { err: toError(err), phase: "provider" });
|
|
18128
|
+
return { status: "failed", iterations, error: toWrongStackError(err), delegateSummaries };
|
|
18129
|
+
}
|
|
18130
|
+
if (extDecision.model) this.ctx.model = extDecision.model;
|
|
18131
|
+
this.logger.info("Extension requested retry; retrying turn");
|
|
18132
|
+
continue;
|
|
18133
|
+
}
|
|
18134
|
+
}
|
|
18135
|
+
const recovered = await this.errorHandler.recover(err, this.ctx);
|
|
18136
|
+
if (!recovered || recovered.action === "fail") {
|
|
18137
|
+
this.events.emit("error", { err: toError(err), phase: "provider" });
|
|
18138
|
+
return {
|
|
18139
|
+
status: "failed",
|
|
18140
|
+
iterations,
|
|
18141
|
+
error: toWrongStackError(recovered?.error ?? err),
|
|
18142
|
+
delegateSummaries
|
|
18143
|
+
};
|
|
18017
18144
|
}
|
|
18018
|
-
if (
|
|
18145
|
+
if (recovered.action === "retry") {
|
|
18019
18146
|
recoveryRetries++;
|
|
18020
18147
|
if (recoveryRetries > 2) {
|
|
18021
18148
|
this.events.emit("error", { err: toError(err), phase: "provider" });
|
|
18022
18149
|
return { status: "failed", iterations, error: toWrongStackError(err) };
|
|
18023
18150
|
}
|
|
18024
|
-
if (
|
|
18025
|
-
this.logger.info(
|
|
18151
|
+
if (recovered.model) this.ctx.model = recovered.model;
|
|
18152
|
+
this.logger.info(`Recovered provider error via ${recovered.reason}; retrying turn`);
|
|
18026
18153
|
continue;
|
|
18027
18154
|
}
|
|
18155
|
+
recoveryRetries = 0;
|
|
18156
|
+
res = recovered.response;
|
|
18028
18157
|
}
|
|
18029
|
-
const
|
|
18030
|
-
if (
|
|
18031
|
-
|
|
18032
|
-
return {
|
|
18033
|
-
status: "failed",
|
|
18034
|
-
iterations,
|
|
18035
|
-
error: toWrongStackError(recovered?.error ?? err)
|
|
18036
|
-
};
|
|
18158
|
+
const responseResult = await this.processResponse(res, req);
|
|
18159
|
+
if (responseResult.aborted) {
|
|
18160
|
+
return { status: "aborted", iterations, finalText: responseResult.finalText, delegateSummaries };
|
|
18037
18161
|
}
|
|
18038
|
-
if (
|
|
18039
|
-
|
|
18040
|
-
|
|
18041
|
-
|
|
18042
|
-
|
|
18162
|
+
if (responseResult.done) {
|
|
18163
|
+
return { status: "done", iterations, finalText: responseResult.finalText, delegateSummaries };
|
|
18164
|
+
}
|
|
18165
|
+
finalText = responseResult.finalText;
|
|
18166
|
+
const toolUses = res.content.filter(isToolUseBlock);
|
|
18167
|
+
if (toolUses.length === 0) {
|
|
18168
|
+
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
18169
|
+
if (autonomousContinue && responseResult.directive === "continue") {
|
|
18170
|
+
await this.compactContextIfNeeded();
|
|
18171
|
+
await this.extensions.runAfterIteration(this.ctx, i);
|
|
18172
|
+
continue;
|
|
18043
18173
|
}
|
|
18044
|
-
if (
|
|
18045
|
-
|
|
18046
|
-
|
|
18174
|
+
if (autonomousContinue && responseResult.directive === "stop") {
|
|
18175
|
+
return { status: "done", iterations, finalText, delegateSummaries };
|
|
18176
|
+
}
|
|
18177
|
+
return { status: "done", iterations, finalText, delegateSummaries };
|
|
18047
18178
|
}
|
|
18048
|
-
|
|
18049
|
-
|
|
18050
|
-
|
|
18051
|
-
const responseResult = await this.processResponse(res, req);
|
|
18052
|
-
if (responseResult.aborted) {
|
|
18053
|
-
return { status: "aborted", iterations, finalText: responseResult.finalText };
|
|
18054
|
-
}
|
|
18055
|
-
if (responseResult.done) {
|
|
18056
|
-
return { status: "done", iterations, finalText: responseResult.finalText };
|
|
18057
|
-
}
|
|
18058
|
-
finalText = responseResult.finalText;
|
|
18059
|
-
const toolUses = res.content.filter(isToolUseBlock);
|
|
18060
|
-
if (toolUses.length === 0) {
|
|
18061
|
-
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
18062
|
-
if (autonomousContinue && responseResult.directive === "continue") {
|
|
18179
|
+
await this.executeTools(toolUses);
|
|
18180
|
+
if (autonomousContinue && consumeAutonomousContinue(this.ctx)) {
|
|
18181
|
+
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
18063
18182
|
await this.compactContextIfNeeded();
|
|
18064
18183
|
await this.extensions.runAfterIteration(this.ctx, i);
|
|
18065
18184
|
continue;
|
|
18066
18185
|
}
|
|
18067
|
-
if (autonomousContinue && responseResult.directive === "stop") {
|
|
18068
|
-
return { status: "done", iterations, finalText };
|
|
18069
|
-
}
|
|
18070
|
-
return { status: "done", iterations, finalText };
|
|
18071
|
-
}
|
|
18072
|
-
await this.executeTools(toolUses);
|
|
18073
|
-
if (autonomousContinue && consumeAutonomousContinue(this.ctx)) {
|
|
18074
18186
|
this.events.emit("iteration.completed", { ctx: this.ctx, index: i });
|
|
18075
18187
|
await this.compactContextIfNeeded();
|
|
18076
18188
|
await this.extensions.runAfterIteration(this.ctx, i);
|
|
18077
|
-
continue
|
|
18078
|
-
|
|
18079
|
-
|
|
18080
|
-
|
|
18081
|
-
|
|
18082
|
-
|
|
18083
|
-
continue;
|
|
18084
|
-
}
|
|
18085
|
-
if (autonomousContinue && responseResult.directive === "stop") {
|
|
18086
|
-
return { status: "done", iterations, finalText };
|
|
18189
|
+
if (autonomousContinue && responseResult.directive === "continue") {
|
|
18190
|
+
continue;
|
|
18191
|
+
}
|
|
18192
|
+
if (autonomousContinue && responseResult.directive === "stop") {
|
|
18193
|
+
return { status: "done", iterations, finalText, delegateSummaries };
|
|
18194
|
+
}
|
|
18087
18195
|
}
|
|
18196
|
+
} finally {
|
|
18197
|
+
offSubagentDone();
|
|
18088
18198
|
}
|
|
18089
18199
|
}
|
|
18090
18200
|
/**
|
|
@@ -18093,7 +18203,7 @@ var Agent = class {
|
|
|
18093
18203
|
* the loop should exit. Returns `{ limit }` with no result when the
|
|
18094
18204
|
* iteration may proceed.
|
|
18095
18205
|
*/
|
|
18096
|
-
async checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations) {
|
|
18206
|
+
async checkIterationLimit(iterationIndex, limit, hasHardLimit, currentIterations, delegateSummaries) {
|
|
18097
18207
|
if (hasHardLimit && iterationIndex >= limit) {
|
|
18098
18208
|
const extendBy = await requestLimitExtension({
|
|
18099
18209
|
events: this.events,
|
|
@@ -18106,7 +18216,7 @@ var Agent = class {
|
|
|
18106
18216
|
this.logger.info(`Iteration limit extended by ${extendBy} (new limit: ${newLimit})`);
|
|
18107
18217
|
return { limit: newLimit };
|
|
18108
18218
|
}
|
|
18109
|
-
return { limit, exit: { status: "max_iterations", iterations: currentIterations } };
|
|
18219
|
+
return { limit, exit: { status: "max_iterations", iterations: currentIterations, delegateSummaries } };
|
|
18110
18220
|
}
|
|
18111
18221
|
return { limit };
|
|
18112
18222
|
}
|
|
@@ -18513,11 +18623,9 @@ var Context = class {
|
|
|
18513
18623
|
return this._state;
|
|
18514
18624
|
}
|
|
18515
18625
|
/**
|
|
18516
|
-
* Register a teardown hook tied to the current run's abort signal.
|
|
18517
|
-
*
|
|
18518
|
-
*
|
|
18519
|
-
* and fired when the next run ends; there is no "immediate fire" when no
|
|
18520
|
-
* run is active.
|
|
18626
|
+
* Register a teardown hook tied to the current run's abort signal.
|
|
18627
|
+
* Hooks registered before a run starts are stored and fired when the
|
|
18628
|
+
* next run ends; there is no immediate fire when no run is active.
|
|
18521
18629
|
*
|
|
18522
18630
|
* **Scope:** these hooks fire on the **whole agent run's** abort, not on
|
|
18523
18631
|
* an individual tool call. For per-tool teardown of resources owned by
|
|
@@ -19096,7 +19204,7 @@ ${mem}`);
|
|
|
19096
19204
|
};
|
|
19097
19205
|
|
|
19098
19206
|
// src/registry/tool-registry.ts
|
|
19099
|
-
var ToolRegistry = class {
|
|
19207
|
+
var ToolRegistry = class _ToolRegistry {
|
|
19100
19208
|
tools = /* @__PURE__ */ new Map();
|
|
19101
19209
|
register(tool, owner = "core") {
|
|
19102
19210
|
if (this.tools.has(tool.name)) {
|
|
@@ -19218,6 +19326,15 @@ var ToolRegistry = class {
|
|
|
19218
19326
|
clear() {
|
|
19219
19327
|
this.tools.clear();
|
|
19220
19328
|
}
|
|
19329
|
+
/**
|
|
19330
|
+
* Return a new ToolRegistry with the same registered tools and owners.
|
|
19331
|
+
* Useful for creating filtered copies in multi-agent scenarios.
|
|
19332
|
+
*/
|
|
19333
|
+
clone() {
|
|
19334
|
+
const copy = new _ToolRegistry();
|
|
19335
|
+
for (const { tool, owner } of this.listWithOwner()) copy.register(tool, owner);
|
|
19336
|
+
return copy;
|
|
19337
|
+
}
|
|
19221
19338
|
};
|
|
19222
19339
|
|
|
19223
19340
|
// src/registry/provider-registry.ts
|
|
@@ -19282,9 +19399,7 @@ var SlashCommandRegistry = class {
|
|
|
19282
19399
|
}
|
|
19283
19400
|
}
|
|
19284
19401
|
if (this.cmds.has(fullName)) {
|
|
19285
|
-
if (!isPlugin)
|
|
19286
|
-
throw new Error(`Built-in slash command "${fullName}" is already registered.`);
|
|
19287
|
-
}
|
|
19402
|
+
if (!isPlugin) return;
|
|
19288
19403
|
this.cmds.set(fullName, { cmd, owner });
|
|
19289
19404
|
for (const a of cmd.aliases ?? []) {
|
|
19290
19405
|
this.cmds.set(`${owner}:${a}`, { cmd, owner });
|
|
@@ -19679,7 +19794,12 @@ async function unloadPlugins(loadedPlugins, opts) {
|
|
|
19679
19794
|
for (const plugin of ordered) {
|
|
19680
19795
|
if (typeof plugin.teardown !== "function") continue;
|
|
19681
19796
|
try {
|
|
19682
|
-
const api = pluginApiMap.get(plugin)
|
|
19797
|
+
const api = pluginApiMap.get(plugin);
|
|
19798
|
+
if (!api) {
|
|
19799
|
+
throw new Error(
|
|
19800
|
+
`Plugin "${plugin.name}" API not found in pluginApiMap \u2014 was setup() called?`
|
|
19801
|
+
);
|
|
19802
|
+
}
|
|
19683
19803
|
await plugin.teardown(api);
|
|
19684
19804
|
pluginApiMap.delete(plugin);
|
|
19685
19805
|
opts.log.info(`Plugin "${plugin.name}" torn down`);
|