@wrongstack/core 0.264.0 → 0.267.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/dist/{agent-bridge-D8sa1vtv.d.ts → agent-bridge-STJ3JwwK.d.ts} +1 -1
- package/dist/{agent-subagent-runner-c9DLkaas.d.ts → agent-subagent-runner-CzPGP3jA.d.ts} +131 -11
- package/dist/{brain-O1IdKPaK.d.ts → brain-Cdg77tVN.d.ts} +103 -2
- package/dist/{compactor-BBy0rCtB.d.ts → compactor-iMZ84CXq.d.ts} +19 -1
- package/dist/{config-Dz2F3H2K.d.ts → config-Du3pYYln.d.ts} +132 -13
- package/dist/{context-BGSpZNSE.d.ts → context-dT5Ueund.d.ts} +90 -12
- package/dist/coordination/index.d.ts +78 -22
- package/dist/coordination/index.js +695 -273
- 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 +2327 -965
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +1500 -371
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/prompt-enhancer.d.ts +2 -2
- package/dist/execution/prompt-enhancer.js +1 -1
- package/dist/execution/prompt-enhancer.js.map +1 -1
- package/dist/extension/index.d.ts +6 -6
- package/dist/{goal-preamble-DzjFuN3p.d.ts → goal-preamble-SulMTowG.d.ts} +33 -12
- package/dist/{goal-store-CxWmCGbH.d.ts → goal-store-CABDwdFE.d.ts} +1 -1
- package/dist/{index-CbLSI66_.d.ts → index-Bms0m4oy.d.ts} +5 -5
- package/dist/{index-CYIQrXVF.d.ts → index-DtCVWel4.d.ts} +8 -8
- package/dist/index-IEuxQd-E.d.ts +82 -0
- package/dist/index.d.ts +261 -57
- package/dist/index.js +4799 -2212
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/index.d.ts +6 -6
- package/dist/infrastructure/index.js +84 -9
- package/dist/infrastructure/index.js.map +1 -1
- package/dist/kernel/index.d.ts +9 -9
- package/dist/kernel/index.js +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{mcp-servers-DC4QRPUI.d.ts → mcp-servers-C2cBTxUR.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +104 -31
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-B_siPxqN.d.ts → models-registry-BqGZNJQ-.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CK5Jdj9K.d.ts → multi-agent-coordinator-B8R43uPz.d.ts} +1 -1
- package/dist/{null-fleet-bus-DgvD4SCO.d.ts → null-fleet-bus-CnXa5oTH.d.ts} +14 -9
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-bK0JQBR_.d.ts → parallel-eternal-engine-DdNnw9BQ.d.ts} +11 -9
- package/dist/{path-resolver-BPEDlN38.d.ts → path-resolver-COIMLCQL.d.ts} +3 -3
- package/dist/{permission-4yvGmMRB.d.ts → permission-B75JAi3-.d.ts} +1 -1
- package/dist/{permission-policy-C6XpsBOy.d.ts → permission-policy-DlR9eJAM.d.ts} +2 -2
- package/dist/{pipeline-CXCeMz8J.d.ts → pipeline-BfD2k1rT.d.ts} +3 -3
- package/dist/{plan-templates-BvzRBkJc.d.ts → plan-templates-DSIKCXZN.d.ts} +32 -8
- package/dist/provider-model-resolve-BNRsNuJx.d.ts +107 -0
- package/dist/{provider-runner-C5aQpDWE.d.ts → provider-runner-CX7iIvox.d.ts} +3 -3
- package/dist/{retry-policy-CFhdtRzz.d.ts → retry-policy-BilV1ujH.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +286 -105
- package/dist/sdd/index.js.map +1 -1
- package/dist/secret-vault-BAKpgFw_.d.ts +57 -0
- package/dist/{secret-vault-CxiVLbt1.d.ts → secret-vault-gkvEZZfE.d.ts} +43 -4
- package/dist/security/index.d.ts +6 -68
- package/dist/security/index.js +296 -95
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-gIuhRTkN.d.ts → selector-Bc7eWtT3.d.ts} +1 -1
- package/dist/{session-event-bridge-DkvvrpDt.d.ts → session-event-bridge-D-araDEz.d.ts} +1 -1
- package/dist/{session-reader-KdfVwkKP.d.ts → session-reader-D7Dapswh.d.ts} +1 -1
- package/dist/storage/index.d.ts +112 -15
- package/dist/storage/index.js +491 -156
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +1523 -450
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +455 -407
- package/dist/utils/index.js +2191 -1203
- 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 +1 -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/llm-selector-DzxuZnNz.d.ts +0 -58
- package/dist/secret-vault-BJDY28ev.d.ts +0 -25
package/dist/sdd/index.js
CHANGED
|
@@ -627,16 +627,20 @@ function topologicalSort(graph) {
|
|
|
627
627
|
return result;
|
|
628
628
|
}
|
|
629
629
|
|
|
630
|
+
// src/utils/assert-never.ts
|
|
631
|
+
function assertNever(x, message) {
|
|
632
|
+
const err = new Error(
|
|
633
|
+
`Unhandled case: ${JSON.stringify(x)}`
|
|
634
|
+
);
|
|
635
|
+
err.name = "AssertNeverError";
|
|
636
|
+
throw err;
|
|
637
|
+
}
|
|
638
|
+
|
|
630
639
|
// src/utils/error.ts
|
|
631
640
|
function toErrorMessage(err) {
|
|
632
641
|
return err instanceof Error ? err.message : String(err);
|
|
633
642
|
}
|
|
634
643
|
|
|
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
644
|
// src/utils/expect-defined.ts
|
|
641
645
|
function expectDefined(value, label) {
|
|
642
646
|
if (value === null || value === void 0) {
|
|
@@ -647,13 +651,9 @@ function expectDefined(value, label) {
|
|
|
647
651
|
return value;
|
|
648
652
|
}
|
|
649
653
|
|
|
650
|
-
// src/utils/
|
|
651
|
-
function
|
|
652
|
-
|
|
653
|
-
`Unhandled case: ${JSON.stringify(x)}`
|
|
654
|
-
);
|
|
655
|
-
err.name = "AssertNeverError";
|
|
656
|
-
throw err;
|
|
654
|
+
// src/utils/string.ts
|
|
655
|
+
function truncate(s, max) {
|
|
656
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// src/types/errors.ts
|
|
@@ -2792,6 +2792,7 @@ var SddTaskDecomposer = class {
|
|
|
2792
2792
|
|
|
2793
2793
|
// src/coordination/subagent-budget.ts
|
|
2794
2794
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
2795
|
+
var DECISION_TIMEOUT_MS = 6e4;
|
|
2795
2796
|
var BudgetExceededError = class extends Error {
|
|
2796
2797
|
kind;
|
|
2797
2798
|
limit;
|
|
@@ -2821,6 +2822,31 @@ var BudgetThresholdSignal = class extends Error {
|
|
|
2821
2822
|
};
|
|
2822
2823
|
var SubagentBudget = class _SubagentBudget {
|
|
2823
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
|
+
}
|
|
2824
2850
|
iterations = 0;
|
|
2825
2851
|
toolCalls = 0;
|
|
2826
2852
|
tokenInput = 0;
|
|
@@ -2841,12 +2867,44 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2841
2867
|
* or hung listener (Director not built / event filter detached mid-run)
|
|
2842
2868
|
* leaves the budget over-limit and never enforces anything.
|
|
2843
2869
|
*/
|
|
2844
|
-
static DECISION_TIMEOUT_MS =
|
|
2870
|
+
static DECISION_TIMEOUT_MS = DECISION_TIMEOUT_MS;
|
|
2845
2871
|
/**
|
|
2846
2872
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
2847
2873
|
* Used to emit `budget.threshold_reached` events in `'auto'` mode.
|
|
2848
2874
|
*/
|
|
2849
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
|
+
}
|
|
2850
2908
|
/**
|
|
2851
2909
|
* Negotiation mode — controls whether a threshold hit tries to emit
|
|
2852
2910
|
* `budget.threshold_reached` and wait for a coordinator decision, or
|
|
@@ -2947,7 +3005,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2947
3005
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
2948
3006
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
2949
3007
|
}
|
|
2950
|
-
|
|
3008
|
+
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
3009
|
+
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
2951
3010
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
2952
3011
|
}
|
|
2953
3012
|
}
|
|
@@ -2961,19 +3020,99 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2961
3020
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
2962
3021
|
}
|
|
2963
3022
|
const bus = this._events;
|
|
2964
|
-
if (!bus
|
|
3023
|
+
if (!bus) {
|
|
2965
3024
|
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
2966
3025
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
2967
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;
|
|
2968
3038
|
for (const entry of exceeded) {
|
|
2969
3039
|
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
2970
|
-
const
|
|
2971
|
-
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);
|
|
2972
3045
|
}
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
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
|
+
});
|
|
2977
3116
|
}
|
|
2978
3117
|
/**
|
|
2979
3118
|
* Per-kind in-flight negotiation Promises. Each budget kind can have its
|
|
@@ -2993,77 +3132,33 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
2993
3132
|
* `{ extend: {} }` — keep going without patching; next overrun fires
|
|
2994
3133
|
* a fresh signal.
|
|
2995
3134
|
*/
|
|
2996
|
-
async _negotiateExtension(
|
|
3135
|
+
async _negotiateExtension(entry) {
|
|
2997
3136
|
if (!this._onThreshold) {
|
|
2998
3137
|
return "stop";
|
|
2999
3138
|
}
|
|
3000
3139
|
try {
|
|
3001
|
-
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
3002
3140
|
const result = this._onThreshold({
|
|
3003
|
-
kind:
|
|
3004
|
-
used:
|
|
3005
|
-
limit:
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
if (resolved) return;
|
|
3015
|
-
resolved = true;
|
|
3016
|
-
resolve(d);
|
|
3017
|
-
};
|
|
3018
|
-
const fallback = setTimeout(
|
|
3019
|
-
() => respond("stop"),
|
|
3020
|
-
_SubagentBudget.DECISION_TIMEOUT_MS
|
|
3021
|
-
);
|
|
3022
|
-
for (const { kind: kind2, used, limit } of exceeded) {
|
|
3023
|
-
bus.emit("budget.threshold_reached", {
|
|
3024
|
-
kind: kind2,
|
|
3025
|
-
used,
|
|
3026
|
-
limit,
|
|
3027
|
-
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
3028
|
-
extend: (extra) => {
|
|
3029
|
-
clearTimeout(fallback);
|
|
3030
|
-
respond({ extend: extra });
|
|
3031
|
-
},
|
|
3032
|
-
deny: () => {
|
|
3033
|
-
clearTimeout(fallback);
|
|
3034
|
-
respond("stop");
|
|
3035
|
-
}
|
|
3036
|
-
});
|
|
3037
|
-
}
|
|
3038
|
-
});
|
|
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: () => {
|
|
3039
3152
|
}
|
|
3040
3153
|
});
|
|
3041
3154
|
if (result === "throw") return "stop";
|
|
3042
3155
|
if (result === "continue") return { extend: {} };
|
|
3043
3156
|
const decision = await result;
|
|
3044
3157
|
if (decision === "stop") return "stop";
|
|
3045
|
-
|
|
3046
|
-
if (ext.maxIterations !== void 0) {
|
|
3047
|
-
this.limits.maxIterations = ext.maxIterations;
|
|
3048
|
-
}
|
|
3049
|
-
if (ext.maxToolCalls !== void 0) {
|
|
3050
|
-
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
3051
|
-
}
|
|
3052
|
-
if (ext.maxTokens !== void 0) {
|
|
3053
|
-
this.limits.maxTokens = ext.maxTokens;
|
|
3054
|
-
}
|
|
3055
|
-
if (ext.maxCostUsd !== void 0) {
|
|
3056
|
-
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
3057
|
-
}
|
|
3058
|
-
if (ext.timeoutMs !== void 0) {
|
|
3059
|
-
this.limits.timeoutMs = ext.timeoutMs;
|
|
3060
|
-
}
|
|
3061
|
-
if (ext.idleTimeoutMs !== void 0) {
|
|
3062
|
-
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
3063
|
-
}
|
|
3158
|
+
this.patchLimits(decision.extend);
|
|
3064
3159
|
return decision;
|
|
3065
3160
|
} finally {
|
|
3066
|
-
this._pendingNegotiations.delete(kind);
|
|
3161
|
+
this._pendingNegotiations.delete(entry.kind);
|
|
3067
3162
|
}
|
|
3068
3163
|
}
|
|
3069
3164
|
recordIteration() {
|
|
@@ -3106,7 +3201,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
3106
3201
|
const { timeoutMs, idleTimeoutMs } = this.limits;
|
|
3107
3202
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
3108
3203
|
const elapsed = Date.now() - this.startTime;
|
|
3109
|
-
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;
|
|
3110
3206
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
3111
3207
|
if (!wallTripped && !idleTripped) return;
|
|
3112
3208
|
void this.checkLimits(elapsed);
|
|
@@ -6326,6 +6422,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6326
6422
|
terminating = /* @__PURE__ */ new Set();
|
|
6327
6423
|
constructor(config, options = {}) {
|
|
6328
6424
|
super();
|
|
6425
|
+
this.setMaxListeners(0);
|
|
6329
6426
|
this.coordinatorId = config.coordinatorId;
|
|
6330
6427
|
this.config = config;
|
|
6331
6428
|
this.runner = options.runner;
|
|
@@ -6720,7 +6817,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6720
6817
|
let result;
|
|
6721
6818
|
budget.start();
|
|
6722
6819
|
try {
|
|
6723
|
-
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
|
+
);
|
|
6724
6827
|
result = {
|
|
6725
6828
|
subagentId,
|
|
6726
6829
|
taskId: task.id,
|
|
@@ -6747,7 +6850,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6747
6850
|
}
|
|
6748
6851
|
this.recordCompletion(result);
|
|
6749
6852
|
}
|
|
6750
|
-
async executeWithTimeout(runner, task, ctx, budget) {
|
|
6853
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction = TIMEOUT_PREEMPT_FRACTION) {
|
|
6751
6854
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
6752
6855
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
6753
6856
|
if (initialTimeoutMs === void 0 && idleLimitMs === void 0) {
|
|
@@ -6755,8 +6858,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6755
6858
|
}
|
|
6756
6859
|
const start = Date.now();
|
|
6757
6860
|
let timer = null;
|
|
6758
|
-
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;
|
|
6759
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
|
+
};
|
|
6760
6876
|
const armFor = (ms) => {
|
|
6761
6877
|
if (timer) clearTimeout(timer);
|
|
6762
6878
|
timer = setTimeout(onTick, Math.max(0, ms));
|
|
@@ -6765,7 +6881,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6765
6881
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
6766
6882
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
6767
6883
|
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
6768
|
-
const preemptRemaining = initialTimeoutMs === void 0 ||
|
|
6884
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
6769
6885
|
armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
|
|
6770
6886
|
};
|
|
6771
6887
|
const negotiateTimeout = async (used, limit) => {
|
|
@@ -6775,16 +6891,42 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6775
6891
|
kind: "timeout",
|
|
6776
6892
|
used,
|
|
6777
6893
|
limit,
|
|
6778
|
-
requestDecision: () =>
|
|
6779
|
-
budget._events?.
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
|
|
6784
|
-
|
|
6785
|
-
|
|
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
|
+
});
|
|
6786
6928
|
});
|
|
6787
|
-
}
|
|
6929
|
+
}
|
|
6788
6930
|
});
|
|
6789
6931
|
return typeof result === "string" ? result : await result;
|
|
6790
6932
|
};
|
|
@@ -6795,21 +6937,45 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6795
6937
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
6796
6938
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
6797
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
|
+
});
|
|
6798
6950
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6799
|
-
reject(new BudgetExceededError("
|
|
6951
|
+
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
6800
6952
|
return;
|
|
6801
6953
|
}
|
|
6802
|
-
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);
|
|
6803
6963
|
try {
|
|
6804
6964
|
const decision = await negotiateTimeout(elapsed, wallLimit);
|
|
6805
6965
|
if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
|
|
6806
|
-
budget.
|
|
6807
|
-
|
|
6966
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6967
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6968
|
+
preemptState = "active" /* ACTIVE */;
|
|
6969
|
+
preemptedCeiling = null;
|
|
6808
6970
|
} else {
|
|
6809
|
-
|
|
6971
|
+
preemptState = "locked" /* LOCKED */;
|
|
6972
|
+
preemptedCeiling = wallLimit;
|
|
6810
6973
|
}
|
|
6811
6974
|
} catch {
|
|
6812
|
-
|
|
6975
|
+
preemptState = "locked" /* LOCKED */;
|
|
6976
|
+
preemptedCeiling = wallLimit;
|
|
6977
|
+
} finally {
|
|
6978
|
+
budget.clearWatchdogNegotiation();
|
|
6813
6979
|
}
|
|
6814
6980
|
scheduleNext();
|
|
6815
6981
|
return;
|
|
@@ -6824,26 +6990,41 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6824
6990
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
6825
6991
|
return;
|
|
6826
6992
|
}
|
|
6993
|
+
budget.setWatchdogNegotiation(limit);
|
|
6827
6994
|
try {
|
|
6828
6995
|
const decision = await negotiateTimeout(elapsed, limit);
|
|
6829
|
-
if (decision === "
|
|
6830
|
-
|
|
6996
|
+
if (decision === "throw") {
|
|
6997
|
+
terminate("timeout", limit, elapsed);
|
|
6998
|
+
return;
|
|
6999
|
+
}
|
|
7000
|
+
if (decision === "continue") {
|
|
7001
|
+
preemptState = "locked" /* LOCKED */;
|
|
7002
|
+
preemptedCeiling = wallLimit;
|
|
6831
7003
|
armFor(Math.max(1e3, limit));
|
|
6832
7004
|
return;
|
|
6833
7005
|
}
|
|
7006
|
+
if (decision === "stop") {
|
|
7007
|
+
terminate("timeout", limit, elapsed);
|
|
7008
|
+
return;
|
|
7009
|
+
}
|
|
6834
7010
|
if (decision.extend.timeoutMs !== void 0) {
|
|
6835
|
-
budget.
|
|
6836
|
-
|
|
7011
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
7012
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
7013
|
+
preemptState = "active" /* ACTIVE */;
|
|
7014
|
+
preemptedCeiling = null;
|
|
6837
7015
|
scheduleNext();
|
|
6838
7016
|
return;
|
|
6839
7017
|
}
|
|
6840
|
-
|
|
6841
|
-
|
|
7018
|
+
terminate("timeout", limit, elapsed);
|
|
7019
|
+
return;
|
|
6842
7020
|
} catch (err) {
|
|
6843
7021
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6844
7022
|
reject(
|
|
6845
7023
|
err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
|
|
6846
7024
|
);
|
|
7025
|
+
return;
|
|
7026
|
+
} finally {
|
|
7027
|
+
budget.clearWatchdogNegotiation();
|
|
6847
7028
|
}
|
|
6848
7029
|
};
|
|
6849
7030
|
scheduleNext();
|