@wrongstack/core 0.260.0 → 0.265.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.
- package/dist/{agent-bridge-BbskZ7HH.d.ts → agent-bridge-DrkBxszZ.d.ts} +1 -1
- package/dist/{agent-subagent-runner-BNIGZx18.d.ts → agent-subagent-runner-DM2pP-B6.d.ts} +116 -12
- package/dist/{brain-C2yDd7Lw.d.ts → brain-BXd_61kQ.d.ts} +32 -3
- package/dist/{compactor-t0R_AIt_.d.ts → compactor-B8pOf45Y.d.ts} +1 -1
- package/dist/{config-FG6As4H5.d.ts → config-BMCj_XDs.d.ts} +86 -12
- package/dist/{context-JFOVvu6z.d.ts → context-MRk5PhNv.d.ts} +26 -1
- package/dist/coordination/index.d.ts +1737 -15
- package/dist/coordination/index.js +3152 -494
- package/dist/coordination/index.js.map +1 -1
- package/dist/{default-config-CXsDvOmP.d.ts → default-config-B0cj-Hry.d.ts} +11 -1
- package/dist/defaults/index.d.ts +28 -28
- package/dist/defaults/index.js +1804 -1363
- package/dist/defaults/index.js.map +1 -1
- package/dist/dispatcher-types.d-BBeXBQgS.d.ts +66 -0
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +933 -672
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +1 -1
- package/dist/execution/prompt-enhancer.js +7 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/extension/index.js.map +1 -1
- package/dist/{goal-preamble-B1IXJtLX.d.ts → goal-preamble-DvHDSKSe.d.ts} +26 -10
- package/dist/{goal-store-CPXz6Mml.d.ts → goal-store-DtLMySNb.d.ts} +1 -1
- package/dist/{index-CebbJB94.d.ts → index-B-ch8K9C.d.ts} +8 -8
- package/dist/{index-BPcg4N3M.d.ts → index-CEDeNodM.d.ts} +5 -5
- package/dist/index.d.ts +189 -104
- package/dist/index.js +24693 -21162
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +12 -8
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +7 -2
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DXxI2tlu.d.ts → llm-selector-C0tfTCUe.d.ts} +14 -2
- package/dist/{mcp-servers-OwNHo43-.d.ts → mcp-servers-2x4w6Jn9.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +80 -31
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-Djlmq4uB.d.ts → models-registry-DmJlKuNp.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CEmrSCMJ.d.ts → multi-agent-coordinator-DyCkCZnU.d.ts} +2 -2
- package/dist/{null-fleet-bus-DT92xqgJ.d.ts → null-fleet-bus-CG9QY2aP.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/observability/index.js +8 -3
- package/dist/observability/index.js.map +1 -1
- package/dist/{parallel-eternal-engine-0SItuq5r.d.ts → parallel-eternal-engine-Jw9uhEoT.d.ts} +9 -9
- package/dist/{path-resolver-DKBh6Jlo.d.ts → path-resolver-Dy2ej-gE.d.ts} +3 -3
- package/dist/{permission-BJ7eO9Vl.d.ts → permission-B9SB45lp.d.ts} +1 -1
- package/dist/{permission-policy-DEXOfnpm.d.ts → permission-policy-CkjSXabK.d.ts} +2 -2
- package/dist/{pipeline-zflkI2dp.d.ts → pipeline-DPDxH_7m.d.ts} +59 -4
- package/dist/{plan-templates-BFXyRkEK.d.ts → plan-templates-CzD9GnAU.d.ts} +32 -8
- package/dist/{provider-runner-BC-uywtT.d.ts → provider-runner-DMa70ODu.d.ts} +3 -3
- package/dist/{retry-policy-Cavrzmtk.d.ts → retry-policy-CN0khdlj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +313 -122
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CDvDYXWX.d.ts → secret-vault-B2yw84VT.d.ts} +43 -4
- package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
- package/dist/security/index.d.ts +5 -5
- package/dist/security/index.js +411 -225
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-B7AivHsu.d.ts → selector-CzHh_igB.d.ts} +1 -1
- package/dist/{session-event-bridge-BmIDxdJd.d.ts → session-event-bridge-BUI6Jf-4.d.ts} +8 -2
- package/dist/{session-reader-DtofsB-2.d.ts → session-reader-CMgdMSRP.d.ts} +1 -1
- package/dist/skills/index.js +67 -64
- package/dist/skills/index.js.map +1 -1
- package/dist/storage/index.d.ts +132 -16
- package/dist/storage/index.js +851 -432
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +57 -0
- package/dist/tools/index.js +411 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +928 -711
- package/dist/types/index.js.map +1 -1
- package/dist/utils/error.d.ts +7 -0
- package/dist/utils/error.js +8 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/index.d.ts +8 -68
- package/dist/utils/index.js +20 -10
- package/dist/utils/index.js.map +1 -1
- package/dist/{wstack-paths-CJjEwPXn.d.ts → wstack-paths-hOpNLmvf.d.ts} +2 -0
- package/package.json +5 -1
- package/skills/api-design/SKILL.md +1 -1
- package/skills/audit-log/SKILL.md +6 -6
- package/skills/bug-hunter/SKILL.md +5 -5
- package/skills/chimera/SKILL.md +4 -4
- package/skills/docker-deploy/SKILL.md +1 -1
- package/skills/git-flow/SKILL.md +3 -3
- package/skills/multi-agent/SKILL.md +3 -3
- package/skills/node-modern/SKILL.md +1 -0
- package/skills/observability/SKILL.md +2 -2
- package/skills/output-standards/SKILL.md +51 -28
- package/skills/refactor-planner/SKILL.md +3 -3
- package/skills/security-scanner/SKILL.md +4 -3
- package/skills/tech-stack/SKILL.md +1 -2
- package/dist/package-outdated-watcher-C70ag2G9.d.ts +0 -581
- package/dist/secret-vault-BJDY28ev.d.ts +0 -25
package/dist/sdd/index.js
CHANGED
|
@@ -80,7 +80,12 @@ async function withFileLock(targetPath, fn, opts = {}) {
|
|
|
80
80
|
await handle.writeFile(`${process.pid}:${Date.now()}`);
|
|
81
81
|
break;
|
|
82
82
|
} catch (err) {
|
|
83
|
-
|
|
83
|
+
const code = err.code;
|
|
84
|
+
if (code === "ENOENT") {
|
|
85
|
+
await fs.mkdir(dir, { recursive: true });
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (code !== "EEXIST") throw err;
|
|
84
89
|
try {
|
|
85
90
|
const stat2 = await fs.stat(lockPath);
|
|
86
91
|
if (Date.now() - stat2.mtimeMs > staleMs) {
|
|
@@ -622,6 +627,35 @@ function topologicalSort(graph) {
|
|
|
622
627
|
return result;
|
|
623
628
|
}
|
|
624
629
|
|
|
630
|
+
// src/utils/error.ts
|
|
631
|
+
function toErrorMessage(err) {
|
|
632
|
+
return err instanceof Error ? err.message : String(err);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// src/utils/string.ts
|
|
636
|
+
function truncate(s, max) {
|
|
637
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
// src/utils/expect-defined.ts
|
|
641
|
+
function expectDefined(value, label) {
|
|
642
|
+
if (value === null || value === void 0) {
|
|
643
|
+
const err = new Error("Expected value to be defined");
|
|
644
|
+
err.name = "ExpectDefinedError";
|
|
645
|
+
throw err;
|
|
646
|
+
}
|
|
647
|
+
return value;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/utils/assert-never.ts
|
|
651
|
+
function assertNever(x, message) {
|
|
652
|
+
const err = new Error(
|
|
653
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
654
|
+
);
|
|
655
|
+
err.name = "AssertNeverError";
|
|
656
|
+
throw err;
|
|
657
|
+
}
|
|
658
|
+
|
|
625
659
|
// src/types/errors.ts
|
|
626
660
|
var ERROR_CODES = {
|
|
627
661
|
// Provider
|
|
@@ -929,7 +963,7 @@ var TaskTracker = class {
|
|
|
929
963
|
this.opts.onPersistError ? this.opts.onPersistError(err) : console.warn(JSON.stringify({
|
|
930
964
|
level: "warn",
|
|
931
965
|
event: "task_tracker.save_graph_failed",
|
|
932
|
-
message:
|
|
966
|
+
message: toErrorMessage(err),
|
|
933
967
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
934
968
|
}));
|
|
935
969
|
});
|
|
@@ -1358,16 +1392,6 @@ var TaskGraphStore = class {
|
|
|
1358
1392
|
}
|
|
1359
1393
|
};
|
|
1360
1394
|
|
|
1361
|
-
// src/utils/expect-defined.ts
|
|
1362
|
-
function expectDefined(value, label) {
|
|
1363
|
-
if (value === null || value === void 0) {
|
|
1364
|
-
const err = new Error("Expected value to be defined");
|
|
1365
|
-
err.name = "ExpectDefinedError";
|
|
1366
|
-
throw err;
|
|
1367
|
-
}
|
|
1368
|
-
return value;
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
1395
|
// src/sdd/spec-builder.ts
|
|
1372
1396
|
function buildQuestioningPrompt(session, min, max) {
|
|
1373
1397
|
const answered = session.answers.length;
|
|
@@ -1609,7 +1633,7 @@ var AISpecBuilder = class {
|
|
|
1609
1633
|
* ENOSPC / EACCES doesn't silently strand session edits in memory. */
|
|
1610
1634
|
autoSave() {
|
|
1611
1635
|
this.saveSession().catch((err) => {
|
|
1612
|
-
const detail =
|
|
1636
|
+
const detail = toErrorMessage(err);
|
|
1613
1637
|
process.emitWarning(
|
|
1614
1638
|
`SpecBuilder autoSave failed: ${detail}`,
|
|
1615
1639
|
"SpecBuilderWarning"
|
|
@@ -2021,11 +2045,6 @@ function templateToMarkdown(template, title) {
|
|
|
2021
2045
|
return lines.join("\n");
|
|
2022
2046
|
}
|
|
2023
2047
|
|
|
2024
|
-
// src/utils/string.ts
|
|
2025
|
-
function truncate(s, max) {
|
|
2026
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
2027
|
-
}
|
|
2028
|
-
|
|
2029
2048
|
// src/sdd/task-visualizer.ts
|
|
2030
2049
|
var STATUS_ICON = {
|
|
2031
2050
|
pending: "\u25CB",
|
|
@@ -2348,15 +2367,6 @@ function computeParallelGroups(graph, blockedByMap) {
|
|
|
2348
2367
|
return groups;
|
|
2349
2368
|
}
|
|
2350
2369
|
|
|
2351
|
-
// src/utils/assert-never.ts
|
|
2352
|
-
function assertNever(x, message) {
|
|
2353
|
-
const err = new Error(
|
|
2354
|
-
`Unhandled case: ${JSON.stringify(x)}`
|
|
2355
|
-
);
|
|
2356
|
-
err.name = "AssertNeverError";
|
|
2357
|
-
throw err;
|
|
2358
|
-
}
|
|
2359
|
-
|
|
2360
2370
|
// src/sdd/spec-versioning.ts
|
|
2361
2371
|
var SpecVersioning = class {
|
|
2362
2372
|
versions = /* @__PURE__ */ new Map();
|
|
@@ -2782,6 +2792,7 @@ var SddTaskDecomposer = class {
|
|
|
2782
2792
|
|
|
2783
2793
|
// src/coordination/subagent-budget.ts
|
|
2784
2794
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
2795
|
+
var DECISION_TIMEOUT_MS = 6e4;
|
|
2785
2796
|
var BudgetExceededError = class extends Error {
|
|
2786
2797
|
kind;
|
|
2787
2798
|
limit;
|
|
@@ -2811,6 +2822,31 @@ var BudgetThresholdSignal = class extends Error {
|
|
|
2811
2822
|
};
|
|
2812
2823
|
var SubagentBudget = class _SubagentBudget {
|
|
2813
2824
|
limits;
|
|
2825
|
+
/** Patch one or more budget limits in-place after construction.
|
|
2826
|
+
* Used by the coordinator watchdog when granting an extension.
|
|
2827
|
+
* All fields are optional — only provided fields are updated.
|
|
2828
|
+
* This is the single write path for limit mutations so that future
|
|
2829
|
+
* validation or side-effects live in one place (M1). */
|
|
2830
|
+
patchLimits(ext) {
|
|
2831
|
+
if (ext.maxIterations !== void 0) {
|
|
2832
|
+
this.limits.maxIterations = ext.maxIterations;
|
|
2833
|
+
}
|
|
2834
|
+
if (ext.maxToolCalls !== void 0) {
|
|
2835
|
+
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
2836
|
+
}
|
|
2837
|
+
if (ext.maxTokens !== void 0) {
|
|
2838
|
+
this.limits.maxTokens = ext.maxTokens;
|
|
2839
|
+
}
|
|
2840
|
+
if (ext.maxCostUsd !== void 0) {
|
|
2841
|
+
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
2842
|
+
}
|
|
2843
|
+
if (ext.timeoutMs !== void 0) {
|
|
2844
|
+
this.limits.timeoutMs = ext.timeoutMs;
|
|
2845
|
+
}
|
|
2846
|
+
if (ext.idleTimeoutMs !== void 0) {
|
|
2847
|
+
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2814
2850
|
iterations = 0;
|
|
2815
2851
|
toolCalls = 0;
|
|
2816
2852
|
tokenInput = 0;
|
|
@@ -2831,12 +2867,44 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2831
2867
|
* or hung listener (Director not built / event filter detached mid-run)
|
|
2832
2868
|
* leaves the budget over-limit and never enforces anything.
|
|
2833
2869
|
*/
|
|
2834
|
-
static DECISION_TIMEOUT_MS =
|
|
2870
|
+
static DECISION_TIMEOUT_MS = DECISION_TIMEOUT_MS;
|
|
2835
2871
|
/**
|
|
2836
2872
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
2837
2873
|
* Used to emit `budget.threshold_reached` events in `'auto'` mode.
|
|
2838
2874
|
*/
|
|
2839
2875
|
_events;
|
|
2876
|
+
/**
|
|
2877
|
+
* Guard against dual-path races between the coordinator watchdog
|
|
2878
|
+
* (`executeWithTimeout`) and the budget's own `checkTimeout()`.
|
|
2879
|
+
* Both paths detect `elapsed >= timeoutMs` and can emit
|
|
2880
|
+
* `budget.threshold_reached` for kind `'timeout'` simultaneously.
|
|
2881
|
+
* Set to the current `timeoutMs` ceiling by the coordinator BEFORE
|
|
2882
|
+
* calling `onThreshold`, and cleared after the negotiation resolves.
|
|
2883
|
+
* `checkTimeout()` skips its wall-clock check while this is set so
|
|
2884
|
+
* the coordinator's watchdog is the sole source of wall-clock timeout
|
|
2885
|
+
* events — `checkTimeout()` focuses exclusively on `idle_timeout`.
|
|
2886
|
+
*/
|
|
2887
|
+
_watchdogActive;
|
|
2888
|
+
/** Returns the timeout ceiling currently being negotiated by the watchdog,
|
|
2889
|
+
* or `undefined` when no wall-clock negotiation is in flight.
|
|
2890
|
+
* Used by `executeWithTimeout` to detect a stale lock (M3). */
|
|
2891
|
+
get watchdogActive() {
|
|
2892
|
+
return this._watchdogActive;
|
|
2893
|
+
}
|
|
2894
|
+
/** Called by the coordinator watchdog BEFORE calling `onThreshold` so that
|
|
2895
|
+
* `checkTimeout()` skips its wall-clock check for this ceiling. Prevents
|
|
2896
|
+
* the budget's own `checkTimeout()` from emitting a second
|
|
2897
|
+
* `budget.threshold_reached` event while the watchdog is already
|
|
2898
|
+
* negotiating the same wall-clock deadline (C1). */
|
|
2899
|
+
setWatchdogNegotiation(timeoutMs) {
|
|
2900
|
+
this._watchdogActive = timeoutMs;
|
|
2901
|
+
}
|
|
2902
|
+
/** Clears the watchdog guard after negotiation resolves. Called in the
|
|
2903
|
+
* `finally` block of both the pre-empt and deadline branches so it fires
|
|
2904
|
+
* on every exit path: grant, deny, throw, or error. */
|
|
2905
|
+
clearWatchdogNegotiation() {
|
|
2906
|
+
this._watchdogActive = void 0;
|
|
2907
|
+
}
|
|
2840
2908
|
/**
|
|
2841
2909
|
* Negotiation mode — controls whether a threshold hit tries to emit
|
|
2842
2910
|
* `budget.threshold_reached` and wait for a coordinator decision, or
|
|
@@ -2937,7 +3005,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2937
3005
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
2938
3006
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
2939
3007
|
}
|
|
2940
|
-
|
|
3008
|
+
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
3009
|
+
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
2941
3010
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
2942
3011
|
}
|
|
2943
3012
|
}
|
|
@@ -2951,19 +3020,99 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2951
3020
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
2952
3021
|
}
|
|
2953
3022
|
const bus = this._events;
|
|
2954
|
-
if (!bus
|
|
3023
|
+
if (!bus) {
|
|
2955
3024
|
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
2956
3025
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
2957
3026
|
}
|
|
3027
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
3028
|
+
if (bus.hasListenerFor("budget.threshold_reached")) {
|
|
3029
|
+
for (const entry of exceeded) {
|
|
3030
|
+
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
3031
|
+
this._pendingNegotiations.set(entry.kind, this._negotiateExtension(entry));
|
|
3032
|
+
}
|
|
3033
|
+
const decision = this._pendingNegotiations.get(first.kind);
|
|
3034
|
+
if (!decision) throw new Error(`No pending negotiation for ${first.kind}`);
|
|
3035
|
+
throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
|
|
3036
|
+
}
|
|
3037
|
+
let hardStop = null;
|
|
2958
3038
|
for (const entry of exceeded) {
|
|
2959
3039
|
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
2960
|
-
const
|
|
2961
|
-
this._pendingNegotiations.set(entry.kind,
|
|
3040
|
+
const marker = Promise.resolve("stop");
|
|
3041
|
+
this._pendingNegotiations.set(entry.kind, marker);
|
|
3042
|
+
void marker.finally(() => this._pendingNegotiations.delete(entry.kind));
|
|
3043
|
+
const sync = this._invokeHandlerSync(entry);
|
|
3044
|
+
if (!sync) hardStop ??= new BudgetExceededError(entry.kind, entry.limit, entry.used);
|
|
2962
3045
|
}
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
3046
|
+
if (hardStop) throw hardStop;
|
|
3047
|
+
return exceeded;
|
|
3048
|
+
}
|
|
3049
|
+
/**
|
|
3050
|
+
* Invoke `onThreshold` once for `entry` on the NO-LISTENER path and report
|
|
3051
|
+
* whether it decided synchronously. Returns `true` when the handler returned
|
|
3052
|
+
* a synchronous decision (already honored — an `extend` patched the limits),
|
|
3053
|
+
* or `false` when it returned a Promise (async; the caller hard-stops, since
|
|
3054
|
+
* there is no listener to resolve the negotiation). The handler is given the
|
|
3055
|
+
* full info shape (`requestDecision` plus direct `extend`/`deny`) so both
|
|
3056
|
+
* recording handlers and policy handlers work without a wired listener.
|
|
3057
|
+
*/
|
|
3058
|
+
_invokeHandlerSync(entry) {
|
|
3059
|
+
const handler = this._onThreshold;
|
|
3060
|
+
if (!handler) return false;
|
|
3061
|
+
let extendArg;
|
|
3062
|
+
const result = handler({
|
|
3063
|
+
kind: entry.kind,
|
|
3064
|
+
used: entry.used,
|
|
3065
|
+
limit: entry.limit,
|
|
3066
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
3067
|
+
// Direct hooks for synchronous policy/recording handlers.
|
|
3068
|
+
extend: (extra) => {
|
|
3069
|
+
extendArg = extra;
|
|
3070
|
+
},
|
|
3071
|
+
deny: () => {
|
|
3072
|
+
}
|
|
3073
|
+
});
|
|
3074
|
+
if (result && typeof result.then === "function") return false;
|
|
3075
|
+
if (result === "throw") return false;
|
|
3076
|
+
if (result && typeof result === "object" && "extend" in result) {
|
|
3077
|
+
extendArg = result.extend;
|
|
3078
|
+
}
|
|
3079
|
+
if (extendArg) this.patchLimits(extendArg);
|
|
3080
|
+
return true;
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* Emit `budget.threshold_reached` and resolve to the listener's verdict.
|
|
3084
|
+
* Resolves to `'stop'` immediately when there is no listener (or no bus) so
|
|
3085
|
+
* no negotiation can hang and no fallback timer leaks. Mirrors the
|
|
3086
|
+
* coordinator watchdog's own request path so both agree on the no-listener
|
|
3087
|
+
* default.
|
|
3088
|
+
*/
|
|
3089
|
+
_busRequestDecision(entry) {
|
|
3090
|
+
const bus = this._events;
|
|
3091
|
+
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
3092
|
+
return Promise.resolve("stop");
|
|
3093
|
+
}
|
|
3094
|
+
return new Promise((resolve) => {
|
|
3095
|
+
let resolved = false;
|
|
3096
|
+
const respond = (d) => {
|
|
3097
|
+
if (resolved) return;
|
|
3098
|
+
resolved = true;
|
|
3099
|
+
clearTimeout(fallback);
|
|
3100
|
+
resolve(d);
|
|
3101
|
+
};
|
|
3102
|
+
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
3103
|
+
bus.emit("budget.threshold_reached", {
|
|
3104
|
+
kind: entry.kind,
|
|
3105
|
+
used: entry.used,
|
|
3106
|
+
limit: entry.limit,
|
|
3107
|
+
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
3108
|
+
// deny() wins over a same-dispatch extend(): a listener that both grants
|
|
3109
|
+
// and denies (or two listeners disagreeing) is resolved as a stop. The
|
|
3110
|
+
// grant is deferred a microtask so a synchronous deny in the same emit
|
|
3111
|
+
// pre-empts it; async grants still resolve normally.
|
|
3112
|
+
extend: (extra) => queueMicrotask(() => respond({ extend: extra })),
|
|
3113
|
+
deny: () => respond("stop")
|
|
3114
|
+
});
|
|
3115
|
+
});
|
|
2967
3116
|
}
|
|
2968
3117
|
/**
|
|
2969
3118
|
* Per-kind in-flight negotiation Promises. Each budget kind can have its
|
|
@@ -2983,77 +3132,33 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2983
3132
|
* `{ extend: {} }` — keep going without patching; next overrun fires
|
|
2984
3133
|
* a fresh signal.
|
|
2985
3134
|
*/
|
|
2986
|
-
async _negotiateExtension(
|
|
3135
|
+
async _negotiateExtension(entry) {
|
|
2987
3136
|
if (!this._onThreshold) {
|
|
2988
3137
|
return "stop";
|
|
2989
3138
|
}
|
|
2990
3139
|
try {
|
|
2991
|
-
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
2992
3140
|
const result = this._onThreshold({
|
|
2993
|
-
kind:
|
|
2994
|
-
used:
|
|
2995
|
-
limit:
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
if (resolved) return;
|
|
3005
|
-
resolved = true;
|
|
3006
|
-
resolve(d);
|
|
3007
|
-
};
|
|
3008
|
-
const fallback = setTimeout(
|
|
3009
|
-
() => respond("stop"),
|
|
3010
|
-
_SubagentBudget.DECISION_TIMEOUT_MS
|
|
3011
|
-
);
|
|
3012
|
-
for (const { kind: kind2, used, limit } of exceeded) {
|
|
3013
|
-
bus.emit("budget.threshold_reached", {
|
|
3014
|
-
kind: kind2,
|
|
3015
|
-
used,
|
|
3016
|
-
limit,
|
|
3017
|
-
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
3018
|
-
extend: (extra) => {
|
|
3019
|
-
clearTimeout(fallback);
|
|
3020
|
-
respond({ extend: extra });
|
|
3021
|
-
},
|
|
3022
|
-
deny: () => {
|
|
3023
|
-
clearTimeout(fallback);
|
|
3024
|
-
respond("stop");
|
|
3025
|
-
}
|
|
3026
|
-
});
|
|
3027
|
-
}
|
|
3028
|
-
});
|
|
3141
|
+
kind: entry.kind,
|
|
3142
|
+
used: entry.used,
|
|
3143
|
+
limit: entry.limit,
|
|
3144
|
+
// One event for THIS kind only — each exceeded kind has its own
|
|
3145
|
+
// negotiation (and its own resolve), so there is no cross-kind
|
|
3146
|
+
// first-wins drop and no O(N^2) re-emission.
|
|
3147
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
3148
|
+
extend: (extra) => {
|
|
3149
|
+
this.patchLimits(extra);
|
|
3150
|
+
},
|
|
3151
|
+
deny: () => {
|
|
3029
3152
|
}
|
|
3030
3153
|
});
|
|
3031
3154
|
if (result === "throw") return "stop";
|
|
3032
3155
|
if (result === "continue") return { extend: {} };
|
|
3033
3156
|
const decision = await result;
|
|
3034
3157
|
if (decision === "stop") return "stop";
|
|
3035
|
-
|
|
3036
|
-
if (ext.maxIterations !== void 0) {
|
|
3037
|
-
this.limits.maxIterations = ext.maxIterations;
|
|
3038
|
-
}
|
|
3039
|
-
if (ext.maxToolCalls !== void 0) {
|
|
3040
|
-
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
3041
|
-
}
|
|
3042
|
-
if (ext.maxTokens !== void 0) {
|
|
3043
|
-
this.limits.maxTokens = ext.maxTokens;
|
|
3044
|
-
}
|
|
3045
|
-
if (ext.maxCostUsd !== void 0) {
|
|
3046
|
-
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
3047
|
-
}
|
|
3048
|
-
if (ext.timeoutMs !== void 0) {
|
|
3049
|
-
this.limits.timeoutMs = ext.timeoutMs;
|
|
3050
|
-
}
|
|
3051
|
-
if (ext.idleTimeoutMs !== void 0) {
|
|
3052
|
-
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
3053
|
-
}
|
|
3158
|
+
this.patchLimits(decision.extend);
|
|
3054
3159
|
return decision;
|
|
3055
3160
|
} finally {
|
|
3056
|
-
this._pendingNegotiations.delete(kind);
|
|
3161
|
+
this._pendingNegotiations.delete(entry.kind);
|
|
3057
3162
|
}
|
|
3058
3163
|
}
|
|
3059
3164
|
recordIteration() {
|
|
@@ -3096,7 +3201,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
3096
3201
|
const { timeoutMs, idleTimeoutMs } = this.limits;
|
|
3097
3202
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
3098
3203
|
const elapsed = Date.now() - this.startTime;
|
|
3099
|
-
const
|
|
3204
|
+
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
3205
|
+
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
3100
3206
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
3101
3207
|
if (!wallTripped && !idleTripped) return;
|
|
3102
3208
|
void this.checkLimits(elapsed);
|
|
@@ -3416,7 +3522,7 @@ function classifySubagentError(err, hints = {}) {
|
|
|
3416
3522
|
const baseMessage2 = err.describe();
|
|
3417
3523
|
return providerErrorToSubagentError(err, baseMessage2, cause);
|
|
3418
3524
|
}
|
|
3419
|
-
const baseMessage =
|
|
3525
|
+
const baseMessage = toErrorMessage(err);
|
|
3420
3526
|
if (err instanceof BudgetExceededError) {
|
|
3421
3527
|
const map = {
|
|
3422
3528
|
iterations: "budget_iterations",
|
|
@@ -6316,6 +6422,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6316
6422
|
terminating = /* @__PURE__ */ new Set();
|
|
6317
6423
|
constructor(config, options = {}) {
|
|
6318
6424
|
super();
|
|
6425
|
+
this.setMaxListeners(0);
|
|
6319
6426
|
this.coordinatorId = config.coordinatorId;
|
|
6320
6427
|
this.config = config;
|
|
6321
6428
|
this.runner = options.runner;
|
|
@@ -6710,7 +6817,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6710
6817
|
let result;
|
|
6711
6818
|
budget.start();
|
|
6712
6819
|
try {
|
|
6713
|
-
const outcome = await this.executeWithTimeout(
|
|
6820
|
+
const outcome = await this.executeWithTimeout(
|
|
6821
|
+
this.runner,
|
|
6822
|
+
task,
|
|
6823
|
+
runCtx,
|
|
6824
|
+
budget,
|
|
6825
|
+
subagent.config.preemptFraction
|
|
6826
|
+
);
|
|
6714
6827
|
result = {
|
|
6715
6828
|
subagentId,
|
|
6716
6829
|
taskId: task.id,
|
|
@@ -6737,7 +6850,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6737
6850
|
}
|
|
6738
6851
|
this.recordCompletion(result);
|
|
6739
6852
|
}
|
|
6740
|
-
async executeWithTimeout(runner, task, ctx, budget) {
|
|
6853
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction = TIMEOUT_PREEMPT_FRACTION) {
|
|
6741
6854
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
6742
6855
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
6743
6856
|
if (initialTimeoutMs === void 0 && idleLimitMs === void 0) {
|
|
@@ -6745,8 +6858,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6745
6858
|
}
|
|
6746
6859
|
const start = Date.now();
|
|
6747
6860
|
let timer = null;
|
|
6748
|
-
let
|
|
6861
|
+
let PreemptState;
|
|
6862
|
+
((PreemptState2) => {
|
|
6863
|
+
PreemptState2["ACTIVE"] = "active";
|
|
6864
|
+
PreemptState2["LOCKED"] = "locked";
|
|
6865
|
+
})(PreemptState || (PreemptState = {}));
|
|
6866
|
+
let preemptedCeiling = null;
|
|
6867
|
+
let preemptState = "active" /* ACTIVE */;
|
|
6868
|
+
let lastGrantActivityTs = -1;
|
|
6749
6869
|
const timeoutPromise = new Promise((_, reject) => {
|
|
6870
|
+
const terminate = (kind, limit, used) => {
|
|
6871
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6872
|
+
reject(
|
|
6873
|
+
budget._events?.hasListenerFor("budget.threshold_reached") ? new Error(`subagent stopped: budget ${kind} (limit=${limit}, used=${used})`) : new BudgetExceededError(kind, limit, used)
|
|
6874
|
+
);
|
|
6875
|
+
};
|
|
6750
6876
|
const armFor = (ms) => {
|
|
6751
6877
|
if (timer) clearTimeout(timer);
|
|
6752
6878
|
timer = setTimeout(onTick, Math.max(0, ms));
|
|
@@ -6755,7 +6881,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6755
6881
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
6756
6882
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
6757
6883
|
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
6758
|
-
const preemptRemaining = initialTimeoutMs === void 0 ||
|
|
6884
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
6759
6885
|
armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
|
|
6760
6886
|
};
|
|
6761
6887
|
const negotiateTimeout = async (used, limit) => {
|
|
@@ -6765,16 +6891,42 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6765
6891
|
kind: "timeout",
|
|
6766
6892
|
used,
|
|
6767
6893
|
limit,
|
|
6768
|
-
requestDecision: () =>
|
|
6769
|
-
budget._events?.
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6894
|
+
requestDecision: () => {
|
|
6895
|
+
if (!budget._events?.hasListenerFor("budget.threshold_reached")) {
|
|
6896
|
+
return Promise.resolve("stop");
|
|
6897
|
+
}
|
|
6898
|
+
return new Promise((resolveDecision) => {
|
|
6899
|
+
let settled = false;
|
|
6900
|
+
const resolve = (d) => {
|
|
6901
|
+
if (settled) return;
|
|
6902
|
+
settled = true;
|
|
6903
|
+
resolveDecision(d);
|
|
6904
|
+
};
|
|
6905
|
+
const fallback = setTimeout(() => resolve("stop"), DECISION_TIMEOUT_MS);
|
|
6906
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6907
|
+
kind: "timeout",
|
|
6908
|
+
used,
|
|
6909
|
+
limit,
|
|
6910
|
+
// Informational: the budget's own decision deadline. Listeners may use
|
|
6911
|
+
// this to display a countdown. The coordinator does NOT enforce it —
|
|
6912
|
+
// it is the budget's own `setTimeout(fallback)` that races against
|
|
6913
|
+
// the listener's `extend()`/`deny()` call to guarantee progress.
|
|
6914
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6915
|
+
// deny() wins over a same-dispatch extend(): defer the grant a
|
|
6916
|
+
// microtask so a synchronous deny in the same emit pre-empts it
|
|
6917
|
+
// (a listener that both grants and denies, or two listeners
|
|
6918
|
+
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
6919
|
+
extend: (extra) => {
|
|
6920
|
+
clearTimeout(fallback);
|
|
6921
|
+
queueMicrotask(() => resolve({ extend: extra }));
|
|
6922
|
+
},
|
|
6923
|
+
deny: () => {
|
|
6924
|
+
clearTimeout(fallback);
|
|
6925
|
+
resolve("stop");
|
|
6926
|
+
}
|
|
6927
|
+
});
|
|
6776
6928
|
});
|
|
6777
|
-
}
|
|
6929
|
+
}
|
|
6778
6930
|
});
|
|
6779
6931
|
return typeof result === "string" ? result : await result;
|
|
6780
6932
|
};
|
|
@@ -6785,21 +6937,45 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6785
6937
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
6786
6938
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
6787
6939
|
if (idleExceeded && !wallExceeded) {
|
|
6940
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6941
|
+
kind: "idle_timeout",
|
|
6942
|
+
used: budget.idleMs(),
|
|
6943
|
+
limit: idleLimit ?? 0,
|
|
6944
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6945
|
+
extend: () => {
|
|
6946
|
+
},
|
|
6947
|
+
deny: () => {
|
|
6948
|
+
}
|
|
6949
|
+
});
|
|
6788
6950
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6789
|
-
reject(new BudgetExceededError("
|
|
6951
|
+
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
6790
6952
|
return;
|
|
6791
6953
|
}
|
|
6792
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold &&
|
|
6954
|
+
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
6955
|
+
const activityTs = Date.now() - budget.idleMs();
|
|
6956
|
+
if (activityTs <= lastGrantActivityTs) {
|
|
6957
|
+
preemptState = "locked" /* LOCKED */;
|
|
6958
|
+
preemptedCeiling = wallLimit;
|
|
6959
|
+
scheduleNext();
|
|
6960
|
+
return;
|
|
6961
|
+
}
|
|
6962
|
+
budget.setWatchdogNegotiation(wallLimit);
|
|
6793
6963
|
try {
|
|
6794
6964
|
const decision = await negotiateTimeout(elapsed, wallLimit);
|
|
6795
6965
|
if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
|
|
6796
|
-
budget.
|
|
6797
|
-
|
|
6966
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6967
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6968
|
+
preemptState = "active" /* ACTIVE */;
|
|
6969
|
+
preemptedCeiling = null;
|
|
6798
6970
|
} else {
|
|
6799
|
-
|
|
6971
|
+
preemptState = "locked" /* LOCKED */;
|
|
6972
|
+
preemptedCeiling = wallLimit;
|
|
6800
6973
|
}
|
|
6801
6974
|
} catch {
|
|
6802
|
-
|
|
6975
|
+
preemptState = "locked" /* LOCKED */;
|
|
6976
|
+
preemptedCeiling = wallLimit;
|
|
6977
|
+
} finally {
|
|
6978
|
+
budget.clearWatchdogNegotiation();
|
|
6803
6979
|
}
|
|
6804
6980
|
scheduleNext();
|
|
6805
6981
|
return;
|
|
@@ -6814,26 +6990,41 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6814
6990
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
6815
6991
|
return;
|
|
6816
6992
|
}
|
|
6993
|
+
budget.setWatchdogNegotiation(limit);
|
|
6817
6994
|
try {
|
|
6818
6995
|
const decision = await negotiateTimeout(elapsed, limit);
|
|
6819
|
-
if (decision === "
|
|
6820
|
-
|
|
6996
|
+
if (decision === "throw") {
|
|
6997
|
+
terminate("timeout", limit, elapsed);
|
|
6998
|
+
return;
|
|
6999
|
+
}
|
|
7000
|
+
if (decision === "continue") {
|
|
7001
|
+
preemptState = "locked" /* LOCKED */;
|
|
7002
|
+
preemptedCeiling = wallLimit;
|
|
6821
7003
|
armFor(Math.max(1e3, limit));
|
|
6822
7004
|
return;
|
|
6823
7005
|
}
|
|
7006
|
+
if (decision === "stop") {
|
|
7007
|
+
terminate("timeout", limit, elapsed);
|
|
7008
|
+
return;
|
|
7009
|
+
}
|
|
6824
7010
|
if (decision.extend.timeoutMs !== void 0) {
|
|
6825
|
-
budget.
|
|
6826
|
-
|
|
7011
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
7012
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
7013
|
+
preemptState = "active" /* ACTIVE */;
|
|
7014
|
+
preemptedCeiling = null;
|
|
6827
7015
|
scheduleNext();
|
|
6828
7016
|
return;
|
|
6829
7017
|
}
|
|
6830
|
-
|
|
6831
|
-
|
|
7018
|
+
terminate("timeout", limit, elapsed);
|
|
7019
|
+
return;
|
|
6832
7020
|
} catch (err) {
|
|
6833
7021
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6834
7022
|
reject(
|
|
6835
7023
|
err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
|
|
6836
7024
|
);
|
|
7025
|
+
return;
|
|
7026
|
+
} finally {
|
|
7027
|
+
budget.clearWatchdogNegotiation();
|
|
6837
7028
|
}
|
|
6838
7029
|
};
|
|
6839
7030
|
scheduleNext();
|
|
@@ -7117,7 +7308,7 @@ var SddParallelRun = class {
|
|
|
7117
7308
|
const failCount = results.length - successCount;
|
|
7118
7309
|
for (let i = 0; i < results.length; i++) {
|
|
7119
7310
|
const result = expectDefined(results[i]);
|
|
7120
|
-
const taskId = expectDefined(
|
|
7311
|
+
const taskId = expectDefined(tasks[i]).id;
|
|
7121
7312
|
if (result.status === "success") {
|
|
7122
7313
|
this.opts.tracker.updateNodeStatus(taskId, "completed");
|
|
7123
7314
|
this.retryMap.delete(taskId);
|