@wrongstack/core 0.264.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-D8sa1vtv.d.ts → agent-bridge-DrkBxszZ.d.ts} +1 -1
- package/dist/{agent-subagent-runner-c9DLkaas.d.ts → agent-subagent-runner-DM2pP-B6.d.ts} +113 -11
- package/dist/{brain-O1IdKPaK.d.ts → brain-BXd_61kQ.d.ts} +31 -2
- package/dist/{compactor-BBy0rCtB.d.ts → compactor-B8pOf45Y.d.ts} +1 -1
- package/dist/{config-Dz2F3H2K.d.ts → config-BMCj_XDs.d.ts} +80 -12
- package/dist/{context-BGSpZNSE.d.ts → context-MRk5PhNv.d.ts} +26 -12
- package/dist/coordination/index.d.ts +77 -21
- package/dist/coordination/index.js +557 -159
- 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 +609 -195
- package/dist/defaults/index.js.map +1 -1
- package/dist/execution/index.d.ts +16 -16
- package/dist/execution/index.js +394 -155
- 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-DvHDSKSe.d.ts} +14 -10
- package/dist/{goal-store-CxWmCGbH.d.ts → goal-store-DtLMySNb.d.ts} +1 -1
- package/dist/{index-CYIQrXVF.d.ts → index-B-ch8K9C.d.ts} +8 -8
- package/dist/{index-CbLSI66_.d.ts → index-CEDeNodM.d.ts} +5 -5
- package/dist/index.d.ts +183 -52
- package/dist/index.js +1779 -673
- 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 +1 -1
- package/dist/kernel/index.js.map +1 -1
- package/dist/{llm-selector-DzxuZnNz.d.ts → llm-selector-C0tfTCUe.d.ts} +14 -2
- package/dist/{mcp-servers-DC4QRPUI.d.ts → mcp-servers-2x4w6Jn9.d.ts} +3 -3
- package/dist/models/index.d.ts +5 -5
- package/dist/models/index.js +74 -30
- package/dist/models/index.js.map +1 -1
- package/dist/{models-registry-B_siPxqN.d.ts → models-registry-DmJlKuNp.d.ts} +1 -1
- package/dist/{multi-agent-coordinator-CK5Jdj9K.d.ts → multi-agent-coordinator-DyCkCZnU.d.ts} +1 -1
- package/dist/{null-fleet-bus-DgvD4SCO.d.ts → null-fleet-bus-CG9QY2aP.d.ts} +6 -6
- package/dist/observability/index.d.ts +2 -2
- package/dist/{parallel-eternal-engine-bK0JQBR_.d.ts → parallel-eternal-engine-Jw9uhEoT.d.ts} +9 -9
- package/dist/{path-resolver-BPEDlN38.d.ts → path-resolver-Dy2ej-gE.d.ts} +3 -3
- package/dist/{permission-4yvGmMRB.d.ts → permission-B9SB45lp.d.ts} +1 -1
- package/dist/{permission-policy-C6XpsBOy.d.ts → permission-policy-CkjSXabK.d.ts} +2 -2
- package/dist/{pipeline-CXCeMz8J.d.ts → pipeline-DPDxH_7m.d.ts} +3 -3
- package/dist/{plan-templates-BvzRBkJc.d.ts → plan-templates-CzD9GnAU.d.ts} +32 -8
- package/dist/{provider-runner-C5aQpDWE.d.ts → provider-runner-DMa70ODu.d.ts} +3 -3
- package/dist/{retry-policy-CFhdtRzz.d.ts → retry-policy-CN0khdlj.d.ts} +1 -1
- package/dist/sdd/index.d.ts +8 -8
- package/dist/sdd/index.js +274 -93
- package/dist/sdd/index.js.map +1 -1
- package/dist/{secret-vault-CxiVLbt1.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 +204 -23
- package/dist/security/index.js.map +1 -1
- package/dist/{selector-gIuhRTkN.d.ts → selector-CzHh_igB.d.ts} +1 -1
- package/dist/{session-event-bridge-DkvvrpDt.d.ts → session-event-bridge-BUI6Jf-4.d.ts} +1 -1
- package/dist/{session-reader-KdfVwkKP.d.ts → session-reader-CMgdMSRP.d.ts} +1 -1
- package/dist/storage/index.d.ts +112 -15
- package/dist/storage/index.js +419 -81
- package/dist/storage/index.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/types/index.d.ts +21 -21
- package/dist/types/index.js +261 -53
- package/dist/types/index.js.map +1 -1
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/index.js +3 -5
- 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/secret-vault-BJDY28ev.d.ts +0 -25
|
@@ -5089,6 +5089,7 @@ function resolveModelMatrix(matrix, role) {
|
|
|
5089
5089
|
|
|
5090
5090
|
// src/coordination/subagent-budget.ts
|
|
5091
5091
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
5092
|
+
var DECISION_TIMEOUT_MS = 6e4;
|
|
5092
5093
|
var BudgetExceededError = class extends Error {
|
|
5093
5094
|
kind;
|
|
5094
5095
|
limit;
|
|
@@ -5118,6 +5119,31 @@ var BudgetThresholdSignal = class extends Error {
|
|
|
5118
5119
|
};
|
|
5119
5120
|
var SubagentBudget = class _SubagentBudget {
|
|
5120
5121
|
limits;
|
|
5122
|
+
/** Patch one or more budget limits in-place after construction.
|
|
5123
|
+
* Used by the coordinator watchdog when granting an extension.
|
|
5124
|
+
* All fields are optional — only provided fields are updated.
|
|
5125
|
+
* This is the single write path for limit mutations so that future
|
|
5126
|
+
* validation or side-effects live in one place (M1). */
|
|
5127
|
+
patchLimits(ext) {
|
|
5128
|
+
if (ext.maxIterations !== void 0) {
|
|
5129
|
+
this.limits.maxIterations = ext.maxIterations;
|
|
5130
|
+
}
|
|
5131
|
+
if (ext.maxToolCalls !== void 0) {
|
|
5132
|
+
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
5133
|
+
}
|
|
5134
|
+
if (ext.maxTokens !== void 0) {
|
|
5135
|
+
this.limits.maxTokens = ext.maxTokens;
|
|
5136
|
+
}
|
|
5137
|
+
if (ext.maxCostUsd !== void 0) {
|
|
5138
|
+
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
5139
|
+
}
|
|
5140
|
+
if (ext.timeoutMs !== void 0) {
|
|
5141
|
+
this.limits.timeoutMs = ext.timeoutMs;
|
|
5142
|
+
}
|
|
5143
|
+
if (ext.idleTimeoutMs !== void 0) {
|
|
5144
|
+
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5121
5147
|
iterations = 0;
|
|
5122
5148
|
toolCalls = 0;
|
|
5123
5149
|
tokenInput = 0;
|
|
@@ -5138,12 +5164,44 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5138
5164
|
* or hung listener (Director not built / event filter detached mid-run)
|
|
5139
5165
|
* leaves the budget over-limit and never enforces anything.
|
|
5140
5166
|
*/
|
|
5141
|
-
static DECISION_TIMEOUT_MS =
|
|
5167
|
+
static DECISION_TIMEOUT_MS = DECISION_TIMEOUT_MS;
|
|
5142
5168
|
/**
|
|
5143
5169
|
* Injected by the runner when wiring the budget to its EventBus.
|
|
5144
5170
|
* Used to emit `budget.threshold_reached` events in `'auto'` mode.
|
|
5145
5171
|
*/
|
|
5146
5172
|
_events;
|
|
5173
|
+
/**
|
|
5174
|
+
* Guard against dual-path races between the coordinator watchdog
|
|
5175
|
+
* (`executeWithTimeout`) and the budget's own `checkTimeout()`.
|
|
5176
|
+
* Both paths detect `elapsed >= timeoutMs` and can emit
|
|
5177
|
+
* `budget.threshold_reached` for kind `'timeout'` simultaneously.
|
|
5178
|
+
* Set to the current `timeoutMs` ceiling by the coordinator BEFORE
|
|
5179
|
+
* calling `onThreshold`, and cleared after the negotiation resolves.
|
|
5180
|
+
* `checkTimeout()` skips its wall-clock check while this is set so
|
|
5181
|
+
* the coordinator's watchdog is the sole source of wall-clock timeout
|
|
5182
|
+
* events — `checkTimeout()` focuses exclusively on `idle_timeout`.
|
|
5183
|
+
*/
|
|
5184
|
+
_watchdogActive;
|
|
5185
|
+
/** Returns the timeout ceiling currently being negotiated by the watchdog,
|
|
5186
|
+
* or `undefined` when no wall-clock negotiation is in flight.
|
|
5187
|
+
* Used by `executeWithTimeout` to detect a stale lock (M3). */
|
|
5188
|
+
get watchdogActive() {
|
|
5189
|
+
return this._watchdogActive;
|
|
5190
|
+
}
|
|
5191
|
+
/** Called by the coordinator watchdog BEFORE calling `onThreshold` so that
|
|
5192
|
+
* `checkTimeout()` skips its wall-clock check for this ceiling. Prevents
|
|
5193
|
+
* the budget's own `checkTimeout()` from emitting a second
|
|
5194
|
+
* `budget.threshold_reached` event while the watchdog is already
|
|
5195
|
+
* negotiating the same wall-clock deadline (C1). */
|
|
5196
|
+
setWatchdogNegotiation(timeoutMs) {
|
|
5197
|
+
this._watchdogActive = timeoutMs;
|
|
5198
|
+
}
|
|
5199
|
+
/** Clears the watchdog guard after negotiation resolves. Called in the
|
|
5200
|
+
* `finally` block of both the pre-empt and deadline branches so it fires
|
|
5201
|
+
* on every exit path: grant, deny, throw, or error. */
|
|
5202
|
+
clearWatchdogNegotiation() {
|
|
5203
|
+
this._watchdogActive = void 0;
|
|
5204
|
+
}
|
|
5147
5205
|
/**
|
|
5148
5206
|
* Negotiation mode — controls whether a threshold hit tries to emit
|
|
5149
5207
|
* `budget.threshold_reached` and wait for a coordinator decision, or
|
|
@@ -5244,7 +5302,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5244
5302
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
5245
5303
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
5246
5304
|
}
|
|
5247
|
-
|
|
5305
|
+
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
5306
|
+
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
5248
5307
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
5249
5308
|
}
|
|
5250
5309
|
}
|
|
@@ -5258,19 +5317,99 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5258
5317
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
5259
5318
|
}
|
|
5260
5319
|
const bus = this._events;
|
|
5261
|
-
if (!bus
|
|
5320
|
+
if (!bus) {
|
|
5262
5321
|
const first2 = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5263
5322
|
throw new BudgetExceededError(first2.kind, first2.limit, first2.used);
|
|
5264
5323
|
}
|
|
5324
|
+
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5325
|
+
if (bus.hasListenerFor("budget.threshold_reached")) {
|
|
5326
|
+
for (const entry of exceeded) {
|
|
5327
|
+
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
5328
|
+
this._pendingNegotiations.set(entry.kind, this._negotiateExtension(entry));
|
|
5329
|
+
}
|
|
5330
|
+
const decision = this._pendingNegotiations.get(first.kind);
|
|
5331
|
+
if (!decision) throw new Error(`No pending negotiation for ${first.kind}`);
|
|
5332
|
+
throw new BudgetThresholdSignal(first.kind, first.limit, first.used, decision);
|
|
5333
|
+
}
|
|
5334
|
+
let hardStop = null;
|
|
5265
5335
|
for (const entry of exceeded) {
|
|
5266
5336
|
if (this._pendingNegotiations.has(entry.kind)) continue;
|
|
5267
|
-
const
|
|
5268
|
-
this._pendingNegotiations.set(entry.kind,
|
|
5337
|
+
const marker = Promise.resolve("stop");
|
|
5338
|
+
this._pendingNegotiations.set(entry.kind, marker);
|
|
5339
|
+
void marker.finally(() => this._pendingNegotiations.delete(entry.kind));
|
|
5340
|
+
const sync = this._invokeHandlerSync(entry);
|
|
5341
|
+
if (!sync) hardStop ??= new BudgetExceededError(entry.kind, entry.limit, entry.used);
|
|
5342
|
+
}
|
|
5343
|
+
if (hardStop) throw hardStop;
|
|
5344
|
+
return exceeded;
|
|
5345
|
+
}
|
|
5346
|
+
/**
|
|
5347
|
+
* Invoke `onThreshold` once for `entry` on the NO-LISTENER path and report
|
|
5348
|
+
* whether it decided synchronously. Returns `true` when the handler returned
|
|
5349
|
+
* a synchronous decision (already honored — an `extend` patched the limits),
|
|
5350
|
+
* or `false` when it returned a Promise (async; the caller hard-stops, since
|
|
5351
|
+
* there is no listener to resolve the negotiation). The handler is given the
|
|
5352
|
+
* full info shape (`requestDecision` plus direct `extend`/`deny`) so both
|
|
5353
|
+
* recording handlers and policy handlers work without a wired listener.
|
|
5354
|
+
*/
|
|
5355
|
+
_invokeHandlerSync(entry) {
|
|
5356
|
+
const handler = this._onThreshold;
|
|
5357
|
+
if (!handler) return false;
|
|
5358
|
+
let extendArg;
|
|
5359
|
+
const result = handler({
|
|
5360
|
+
kind: entry.kind,
|
|
5361
|
+
used: entry.used,
|
|
5362
|
+
limit: entry.limit,
|
|
5363
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
5364
|
+
// Direct hooks for synchronous policy/recording handlers.
|
|
5365
|
+
extend: (extra) => {
|
|
5366
|
+
extendArg = extra;
|
|
5367
|
+
},
|
|
5368
|
+
deny: () => {
|
|
5369
|
+
}
|
|
5370
|
+
});
|
|
5371
|
+
if (result && typeof result.then === "function") return false;
|
|
5372
|
+
if (result === "throw") return false;
|
|
5373
|
+
if (result && typeof result === "object" && "extend" in result) {
|
|
5374
|
+
extendArg = result.extend;
|
|
5269
5375
|
}
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5376
|
+
if (extendArg) this.patchLimits(extendArg);
|
|
5377
|
+
return true;
|
|
5378
|
+
}
|
|
5379
|
+
/**
|
|
5380
|
+
* Emit `budget.threshold_reached` and resolve to the listener's verdict.
|
|
5381
|
+
* Resolves to `'stop'` immediately when there is no listener (or no bus) so
|
|
5382
|
+
* no negotiation can hang and no fallback timer leaks. Mirrors the
|
|
5383
|
+
* coordinator watchdog's own request path so both agree on the no-listener
|
|
5384
|
+
* default.
|
|
5385
|
+
*/
|
|
5386
|
+
_busRequestDecision(entry) {
|
|
5387
|
+
const bus = this._events;
|
|
5388
|
+
if (!bus || !bus.hasListenerFor("budget.threshold_reached")) {
|
|
5389
|
+
return Promise.resolve("stop");
|
|
5390
|
+
}
|
|
5391
|
+
return new Promise((resolve3) => {
|
|
5392
|
+
let resolved = false;
|
|
5393
|
+
const respond = (d) => {
|
|
5394
|
+
if (resolved) return;
|
|
5395
|
+
resolved = true;
|
|
5396
|
+
clearTimeout(fallback);
|
|
5397
|
+
resolve3(d);
|
|
5398
|
+
};
|
|
5399
|
+
const fallback = setTimeout(() => respond("stop"), _SubagentBudget.DECISION_TIMEOUT_MS);
|
|
5400
|
+
bus.emit("budget.threshold_reached", {
|
|
5401
|
+
kind: entry.kind,
|
|
5402
|
+
used: entry.used,
|
|
5403
|
+
limit: entry.limit,
|
|
5404
|
+
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
5405
|
+
// deny() wins over a same-dispatch extend(): a listener that both grants
|
|
5406
|
+
// and denies (or two listeners disagreeing) is resolved as a stop. The
|
|
5407
|
+
// grant is deferred a microtask so a synchronous deny in the same emit
|
|
5408
|
+
// pre-empts it; async grants still resolve normally.
|
|
5409
|
+
extend: (extra) => queueMicrotask(() => respond({ extend: extra })),
|
|
5410
|
+
deny: () => respond("stop")
|
|
5411
|
+
});
|
|
5412
|
+
});
|
|
5274
5413
|
}
|
|
5275
5414
|
/**
|
|
5276
5415
|
* Per-kind in-flight negotiation Promises. Each budget kind can have its
|
|
@@ -5290,77 +5429,33 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5290
5429
|
* `{ extend: {} }` — keep going without patching; next overrun fires
|
|
5291
5430
|
* a fresh signal.
|
|
5292
5431
|
*/
|
|
5293
|
-
async _negotiateExtension(
|
|
5432
|
+
async _negotiateExtension(entry) {
|
|
5294
5433
|
if (!this._onThreshold) {
|
|
5295
5434
|
return "stop";
|
|
5296
5435
|
}
|
|
5297
5436
|
try {
|
|
5298
|
-
const first = exceeded[0] ?? { kind: "iterations", limit: 0, used: 0 };
|
|
5299
5437
|
const result = this._onThreshold({
|
|
5300
|
-
kind:
|
|
5301
|
-
used:
|
|
5302
|
-
limit:
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
if (resolved) return;
|
|
5312
|
-
resolved = true;
|
|
5313
|
-
resolve3(d);
|
|
5314
|
-
};
|
|
5315
|
-
const fallback = setTimeout(
|
|
5316
|
-
() => respond("stop"),
|
|
5317
|
-
_SubagentBudget.DECISION_TIMEOUT_MS
|
|
5318
|
-
);
|
|
5319
|
-
for (const { kind: kind2, used, limit } of exceeded) {
|
|
5320
|
-
bus.emit("budget.threshold_reached", {
|
|
5321
|
-
kind: kind2,
|
|
5322
|
-
used,
|
|
5323
|
-
limit,
|
|
5324
|
-
timeoutMs: _SubagentBudget.DECISION_TIMEOUT_MS,
|
|
5325
|
-
extend: (extra) => {
|
|
5326
|
-
clearTimeout(fallback);
|
|
5327
|
-
respond({ extend: extra });
|
|
5328
|
-
},
|
|
5329
|
-
deny: () => {
|
|
5330
|
-
clearTimeout(fallback);
|
|
5331
|
-
respond("stop");
|
|
5332
|
-
}
|
|
5333
|
-
});
|
|
5334
|
-
}
|
|
5335
|
-
});
|
|
5438
|
+
kind: entry.kind,
|
|
5439
|
+
used: entry.used,
|
|
5440
|
+
limit: entry.limit,
|
|
5441
|
+
// One event for THIS kind only — each exceeded kind has its own
|
|
5442
|
+
// negotiation (and its own resolve), so there is no cross-kind
|
|
5443
|
+
// first-wins drop and no O(N^2) re-emission.
|
|
5444
|
+
requestDecision: () => this._busRequestDecision(entry),
|
|
5445
|
+
extend: (extra) => {
|
|
5446
|
+
this.patchLimits(extra);
|
|
5447
|
+
},
|
|
5448
|
+
deny: () => {
|
|
5336
5449
|
}
|
|
5337
5450
|
});
|
|
5338
5451
|
if (result === "throw") return "stop";
|
|
5339
5452
|
if (result === "continue") return { extend: {} };
|
|
5340
5453
|
const decision = await result;
|
|
5341
5454
|
if (decision === "stop") return "stop";
|
|
5342
|
-
|
|
5343
|
-
if (ext.maxIterations !== void 0) {
|
|
5344
|
-
this.limits.maxIterations = ext.maxIterations;
|
|
5345
|
-
}
|
|
5346
|
-
if (ext.maxToolCalls !== void 0) {
|
|
5347
|
-
this.limits.maxToolCalls = ext.maxToolCalls;
|
|
5348
|
-
}
|
|
5349
|
-
if (ext.maxTokens !== void 0) {
|
|
5350
|
-
this.limits.maxTokens = ext.maxTokens;
|
|
5351
|
-
}
|
|
5352
|
-
if (ext.maxCostUsd !== void 0) {
|
|
5353
|
-
this.limits.maxCostUsd = ext.maxCostUsd;
|
|
5354
|
-
}
|
|
5355
|
-
if (ext.timeoutMs !== void 0) {
|
|
5356
|
-
this.limits.timeoutMs = ext.timeoutMs;
|
|
5357
|
-
}
|
|
5358
|
-
if (ext.idleTimeoutMs !== void 0) {
|
|
5359
|
-
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
5360
|
-
}
|
|
5455
|
+
this.patchLimits(decision.extend);
|
|
5361
5456
|
return decision;
|
|
5362
5457
|
} finally {
|
|
5363
|
-
this._pendingNegotiations.delete(kind);
|
|
5458
|
+
this._pendingNegotiations.delete(entry.kind);
|
|
5364
5459
|
}
|
|
5365
5460
|
}
|
|
5366
5461
|
recordIteration() {
|
|
@@ -5403,7 +5498,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
5403
5498
|
const { timeoutMs, idleTimeoutMs } = this.limits;
|
|
5404
5499
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
5405
5500
|
const elapsed = Date.now() - this.startTime;
|
|
5406
|
-
const
|
|
5501
|
+
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
5502
|
+
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
5407
5503
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
5408
5504
|
if (!wallTripped && !idleTripped) return;
|
|
5409
5505
|
void this.checkLimits(elapsed);
|
|
@@ -6025,6 +6121,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6025
6121
|
terminating = /* @__PURE__ */ new Set();
|
|
6026
6122
|
constructor(config, options = {}) {
|
|
6027
6123
|
super();
|
|
6124
|
+
this.setMaxListeners(0);
|
|
6028
6125
|
this.coordinatorId = config.coordinatorId;
|
|
6029
6126
|
this.config = config;
|
|
6030
6127
|
this.runner = options.runner;
|
|
@@ -6419,7 +6516,13 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6419
6516
|
let result;
|
|
6420
6517
|
budget.start();
|
|
6421
6518
|
try {
|
|
6422
|
-
const outcome = await this.executeWithTimeout(
|
|
6519
|
+
const outcome = await this.executeWithTimeout(
|
|
6520
|
+
this.runner,
|
|
6521
|
+
task,
|
|
6522
|
+
runCtx,
|
|
6523
|
+
budget,
|
|
6524
|
+
subagent.config.preemptFraction
|
|
6525
|
+
);
|
|
6423
6526
|
result = {
|
|
6424
6527
|
subagentId,
|
|
6425
6528
|
taskId: task.id,
|
|
@@ -6446,7 +6549,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6446
6549
|
}
|
|
6447
6550
|
this.recordCompletion(result);
|
|
6448
6551
|
}
|
|
6449
|
-
async executeWithTimeout(runner, task, ctx, budget) {
|
|
6552
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction = TIMEOUT_PREEMPT_FRACTION) {
|
|
6450
6553
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
6451
6554
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
6452
6555
|
if (initialTimeoutMs === void 0 && idleLimitMs === void 0) {
|
|
@@ -6454,8 +6557,21 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6454
6557
|
}
|
|
6455
6558
|
const start = Date.now();
|
|
6456
6559
|
let timer = null;
|
|
6457
|
-
let
|
|
6560
|
+
let PreemptState;
|
|
6561
|
+
((PreemptState2) => {
|
|
6562
|
+
PreemptState2["ACTIVE"] = "active";
|
|
6563
|
+
PreemptState2["LOCKED"] = "locked";
|
|
6564
|
+
})(PreemptState || (PreemptState = {}));
|
|
6565
|
+
let preemptedCeiling = null;
|
|
6566
|
+
let preemptState = "active" /* ACTIVE */;
|
|
6567
|
+
let lastGrantActivityTs = -1;
|
|
6458
6568
|
const timeoutPromise = new Promise((_, reject) => {
|
|
6569
|
+
const terminate = (kind, limit, used) => {
|
|
6570
|
+
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6571
|
+
reject(
|
|
6572
|
+
budget._events?.hasListenerFor("budget.threshold_reached") ? new Error(`subagent stopped: budget ${kind} (limit=${limit}, used=${used})`) : new BudgetExceededError(kind, limit, used)
|
|
6573
|
+
);
|
|
6574
|
+
};
|
|
6459
6575
|
const armFor = (ms) => {
|
|
6460
6576
|
if (timer) clearTimeout(timer);
|
|
6461
6577
|
timer = setTimeout(onTick, Math.max(0, ms));
|
|
@@ -6464,7 +6580,7 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6464
6580
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
6465
6581
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
6466
6582
|
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
6467
|
-
const preemptRemaining = initialTimeoutMs === void 0 ||
|
|
6583
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
6468
6584
|
armFor(Math.max(25, Math.min(wallRemaining, idleRemaining, preemptRemaining)));
|
|
6469
6585
|
};
|
|
6470
6586
|
const negotiateTimeout = async (used, limit) => {
|
|
@@ -6474,16 +6590,42 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6474
6590
|
kind: "timeout",
|
|
6475
6591
|
used,
|
|
6476
6592
|
limit,
|
|
6477
|
-
requestDecision: () =>
|
|
6478
|
-
budget._events?.
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6484
|
-
|
|
6593
|
+
requestDecision: () => {
|
|
6594
|
+
if (!budget._events?.hasListenerFor("budget.threshold_reached")) {
|
|
6595
|
+
return Promise.resolve("stop");
|
|
6596
|
+
}
|
|
6597
|
+
return new Promise((resolveDecision) => {
|
|
6598
|
+
let settled = false;
|
|
6599
|
+
const resolve3 = (d) => {
|
|
6600
|
+
if (settled) return;
|
|
6601
|
+
settled = true;
|
|
6602
|
+
resolveDecision(d);
|
|
6603
|
+
};
|
|
6604
|
+
const fallback = setTimeout(() => resolve3("stop"), DECISION_TIMEOUT_MS);
|
|
6605
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6606
|
+
kind: "timeout",
|
|
6607
|
+
used,
|
|
6608
|
+
limit,
|
|
6609
|
+
// Informational: the budget's own decision deadline. Listeners may use
|
|
6610
|
+
// this to display a countdown. The coordinator does NOT enforce it —
|
|
6611
|
+
// it is the budget's own `setTimeout(fallback)` that races against
|
|
6612
|
+
// the listener's `extend()`/`deny()` call to guarantee progress.
|
|
6613
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6614
|
+
// deny() wins over a same-dispatch extend(): defer the grant a
|
|
6615
|
+
// microtask so a synchronous deny in the same emit pre-empts it
|
|
6616
|
+
// (a listener that both grants and denies, or two listeners
|
|
6617
|
+
// disagreeing, resolves as a stop). Async grants still resolve.
|
|
6618
|
+
extend: (extra) => {
|
|
6619
|
+
clearTimeout(fallback);
|
|
6620
|
+
queueMicrotask(() => resolve3({ extend: extra }));
|
|
6621
|
+
},
|
|
6622
|
+
deny: () => {
|
|
6623
|
+
clearTimeout(fallback);
|
|
6624
|
+
resolve3("stop");
|
|
6625
|
+
}
|
|
6626
|
+
});
|
|
6485
6627
|
});
|
|
6486
|
-
}
|
|
6628
|
+
}
|
|
6487
6629
|
});
|
|
6488
6630
|
return typeof result === "string" ? result : await result;
|
|
6489
6631
|
};
|
|
@@ -6494,21 +6636,45 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6494
6636
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
6495
6637
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
6496
6638
|
if (idleExceeded && !wallExceeded) {
|
|
6639
|
+
budget._events?.emit("budget.threshold_reached", {
|
|
6640
|
+
kind: "idle_timeout",
|
|
6641
|
+
used: budget.idleMs(),
|
|
6642
|
+
limit: idleLimit ?? 0,
|
|
6643
|
+
timeoutMs: DECISION_TIMEOUT_MS,
|
|
6644
|
+
extend: () => {
|
|
6645
|
+
},
|
|
6646
|
+
deny: () => {
|
|
6647
|
+
}
|
|
6648
|
+
});
|
|
6497
6649
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6498
|
-
reject(new BudgetExceededError("
|
|
6650
|
+
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
6499
6651
|
return;
|
|
6500
6652
|
}
|
|
6501
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold &&
|
|
6653
|
+
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
6654
|
+
const activityTs = Date.now() - budget.idleMs();
|
|
6655
|
+
if (activityTs <= lastGrantActivityTs) {
|
|
6656
|
+
preemptState = "locked" /* LOCKED */;
|
|
6657
|
+
preemptedCeiling = wallLimit;
|
|
6658
|
+
scheduleNext();
|
|
6659
|
+
return;
|
|
6660
|
+
}
|
|
6661
|
+
budget.setWatchdogNegotiation(wallLimit);
|
|
6502
6662
|
try {
|
|
6503
6663
|
const decision = await negotiateTimeout(elapsed, wallLimit);
|
|
6504
6664
|
if (typeof decision !== "string" && decision.extend.timeoutMs !== void 0) {
|
|
6505
|
-
budget.
|
|
6506
|
-
|
|
6665
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6666
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6667
|
+
preemptState = "active" /* ACTIVE */;
|
|
6668
|
+
preemptedCeiling = null;
|
|
6507
6669
|
} else {
|
|
6508
|
-
|
|
6670
|
+
preemptState = "locked" /* LOCKED */;
|
|
6671
|
+
preemptedCeiling = wallLimit;
|
|
6509
6672
|
}
|
|
6510
6673
|
} catch {
|
|
6511
|
-
|
|
6674
|
+
preemptState = "locked" /* LOCKED */;
|
|
6675
|
+
preemptedCeiling = wallLimit;
|
|
6676
|
+
} finally {
|
|
6677
|
+
budget.clearWatchdogNegotiation();
|
|
6512
6678
|
}
|
|
6513
6679
|
scheduleNext();
|
|
6514
6680
|
return;
|
|
@@ -6523,26 +6689,41 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
6523
6689
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
6524
6690
|
return;
|
|
6525
6691
|
}
|
|
6692
|
+
budget.setWatchdogNegotiation(limit);
|
|
6526
6693
|
try {
|
|
6527
6694
|
const decision = await negotiateTimeout(elapsed, limit);
|
|
6528
|
-
if (decision === "
|
|
6529
|
-
|
|
6695
|
+
if (decision === "throw") {
|
|
6696
|
+
terminate("timeout", limit, elapsed);
|
|
6697
|
+
return;
|
|
6698
|
+
}
|
|
6699
|
+
if (decision === "continue") {
|
|
6700
|
+
preemptState = "locked" /* LOCKED */;
|
|
6701
|
+
preemptedCeiling = wallLimit;
|
|
6530
6702
|
armFor(Math.max(1e3, limit));
|
|
6531
6703
|
return;
|
|
6532
6704
|
}
|
|
6705
|
+
if (decision === "stop") {
|
|
6706
|
+
terminate("timeout", limit, elapsed);
|
|
6707
|
+
return;
|
|
6708
|
+
}
|
|
6533
6709
|
if (decision.extend.timeoutMs !== void 0) {
|
|
6534
|
-
budget.
|
|
6535
|
-
|
|
6710
|
+
budget.patchLimits({ timeoutMs: decision.extend.timeoutMs });
|
|
6711
|
+
lastGrantActivityTs = Date.now() - budget.idleMs();
|
|
6712
|
+
preemptState = "active" /* ACTIVE */;
|
|
6713
|
+
preemptedCeiling = null;
|
|
6536
6714
|
scheduleNext();
|
|
6537
6715
|
return;
|
|
6538
6716
|
}
|
|
6539
|
-
|
|
6540
|
-
|
|
6717
|
+
terminate("timeout", limit, elapsed);
|
|
6718
|
+
return;
|
|
6541
6719
|
} catch (err) {
|
|
6542
6720
|
this.subagents.get(ctx.subagentId)?.abortController.abort();
|
|
6543
6721
|
reject(
|
|
6544
6722
|
err instanceof BudgetExceededError ? err : new BudgetExceededError("timeout", limit, elapsed)
|
|
6545
6723
|
);
|
|
6724
|
+
return;
|
|
6725
|
+
} finally {
|
|
6726
|
+
budget.clearWatchdogNegotiation();
|
|
6546
6727
|
}
|
|
6547
6728
|
};
|
|
6548
6729
|
scheduleNext();
|
|
@@ -8516,11 +8697,34 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8516
8697
|
dir;
|
|
8517
8698
|
events;
|
|
8518
8699
|
secretScrubber;
|
|
8700
|
+
/**
|
|
8701
|
+
* In-memory cache for load() results, keyed by session ID. The cache is
|
|
8702
|
+
* invalidated when the file's mtimeMs or size changes (indicating the
|
|
8703
|
+
* file was written to). This eliminates redundant full-file reads and
|
|
8704
|
+
* JSON parses when the same session is loaded multiple times within the
|
|
8705
|
+
* store's lifetime (e.g., webui session detail views, list() fallbacks).
|
|
8706
|
+
*
|
|
8707
|
+
* Max size is capped to prevent unbounded memory growth in long-running
|
|
8708
|
+
* processes. When the limit is reached, the oldest entry is evicted.
|
|
8709
|
+
*/
|
|
8710
|
+
_loadCache = /* @__PURE__ */ new Map();
|
|
8711
|
+
static LOAD_CACHE_MAX_ENTRIES = 50;
|
|
8519
8712
|
constructor(opts) {
|
|
8520
8713
|
this.dir = opts.dir;
|
|
8521
8714
|
this.events = opts.events;
|
|
8522
8715
|
this.secretScrubber = opts.secretScrubber;
|
|
8523
8716
|
}
|
|
8717
|
+
/**
|
|
8718
|
+
* Clear the load() cache. Useful for testing or when the caller knows
|
|
8719
|
+
* the file has changed externally (e.g., another process wrote to it).
|
|
8720
|
+
*/
|
|
8721
|
+
clearLoadCache(sessionId) {
|
|
8722
|
+
if (sessionId !== void 0) {
|
|
8723
|
+
this._loadCache.delete(sessionId);
|
|
8724
|
+
} else {
|
|
8725
|
+
this._loadCache.clear();
|
|
8726
|
+
}
|
|
8727
|
+
}
|
|
8524
8728
|
// ── Storage event helpers ───────────────────────────────────────────────────
|
|
8525
8729
|
emitRead(sessionId, filePath, operation, outcome, durationMs, error) {
|
|
8526
8730
|
this.events?.emit("storage.read", {
|
|
@@ -8663,7 +8867,20 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8663
8867
|
const t0 = Date.now();
|
|
8664
8868
|
let outcome = "success";
|
|
8665
8869
|
let errorMsg;
|
|
8870
|
+
let cacheHit = false;
|
|
8666
8871
|
try {
|
|
8872
|
+
let stat6;
|
|
8873
|
+
try {
|
|
8874
|
+
const s = await fsp6.stat(file);
|
|
8875
|
+
stat6 = { mtimeMs: s.mtimeMs, size: s.size };
|
|
8876
|
+
} catch (err) {
|
|
8877
|
+
throw err;
|
|
8878
|
+
}
|
|
8879
|
+
const cached = this._loadCache.get(id);
|
|
8880
|
+
if (cached && cached.mtimeMs === stat6.mtimeMs && cached.size === stat6.size) {
|
|
8881
|
+
cacheHit = true;
|
|
8882
|
+
return cached.data;
|
|
8883
|
+
}
|
|
8667
8884
|
const raw = await fsp6.readFile(file, "utf8");
|
|
8668
8885
|
const lines = raw.split("\n").filter((l) => l.trim());
|
|
8669
8886
|
const events = [];
|
|
@@ -8679,13 +8896,30 @@ var DefaultSessionStore = class _DefaultSessionStore {
|
|
|
8679
8896
|
const meta = this.metaFromEvents(id, events);
|
|
8680
8897
|
const { messages, usage } = this.replay(events, id);
|
|
8681
8898
|
const toolCallEnds = extractToolCallEnds(events);
|
|
8682
|
-
|
|
8899
|
+
const data = { metadata: meta, events, messages, usage, toolCallEnds };
|
|
8900
|
+
if (this._loadCache.size >= _DefaultSessionStore.LOAD_CACHE_MAX_ENTRIES) {
|
|
8901
|
+
const oldest = this._loadCache.keys().next().value;
|
|
8902
|
+
if (oldest !== void 0) {
|
|
8903
|
+
this._loadCache.delete(oldest);
|
|
8904
|
+
}
|
|
8905
|
+
}
|
|
8906
|
+
this._loadCache.set(id, { mtimeMs: stat6.mtimeMs, size: stat6.size, data });
|
|
8907
|
+
return data;
|
|
8683
8908
|
} catch (err) {
|
|
8684
8909
|
outcome = "failure";
|
|
8685
8910
|
errorMsg = toErrorMessage(err);
|
|
8686
8911
|
throw err;
|
|
8687
8912
|
} finally {
|
|
8688
8913
|
this.emitRead(id, file, "load", outcome, Date.now() - t0, errorMsg);
|
|
8914
|
+
if (cacheHit) {
|
|
8915
|
+
this.events?.emit("storage.cache_hit", {
|
|
8916
|
+
sessionId: id,
|
|
8917
|
+
store: "session",
|
|
8918
|
+
filePath: file,
|
|
8919
|
+
operation: "load",
|
|
8920
|
+
durationMs: Date.now() - t0
|
|
8921
|
+
});
|
|
8922
|
+
}
|
|
8689
8923
|
}
|
|
8690
8924
|
}
|
|
8691
8925
|
async list(limit = 20) {
|
|
@@ -9692,6 +9926,7 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
9692
9926
|
const extendCounts = /* @__PURE__ */ new Map();
|
|
9693
9927
|
let progress = 0;
|
|
9694
9928
|
let lastTimeoutProgress = -1;
|
|
9929
|
+
let lastSeenKey = null;
|
|
9695
9930
|
const unsubs = [
|
|
9696
9931
|
events.on("tool.executed", () => {
|
|
9697
9932
|
progress++;
|
|
@@ -9701,6 +9936,9 @@ function attachAutoExtend(events, policy = {}) {
|
|
|
9701
9936
|
}),
|
|
9702
9937
|
events.on("budget.threshold_reached", (e) => {
|
|
9703
9938
|
const { kind, limit, extend, deny } = e;
|
|
9939
|
+
const key = `${kind}:${limit}`;
|
|
9940
|
+
if (key === lastSeenKey) return;
|
|
9941
|
+
lastSeenKey = key;
|
|
9704
9942
|
if (kind === "timeout" || kind === "idle_timeout") {
|
|
9705
9943
|
if (progress > lastTimeoutProgress) {
|
|
9706
9944
|
lastTimeoutProgress = progress;
|
|
@@ -10410,7 +10648,8 @@ var BrainMonitor = class {
|
|
|
10410
10648
|
id: "steer",
|
|
10411
10649
|
label: "Steer the agent with corrective guidance",
|
|
10412
10650
|
consequence: "A steer message is injected before its next step.",
|
|
10413
|
-
risk: "low"
|
|
10651
|
+
risk: "low",
|
|
10652
|
+
recommended: true
|
|
10414
10653
|
},
|
|
10415
10654
|
{
|
|
10416
10655
|
id: "continue",
|
|
@@ -10419,9 +10658,9 @@ var BrainMonitor = class {
|
|
|
10419
10658
|
}
|
|
10420
10659
|
],
|
|
10421
10660
|
risk: "medium",
|
|
10422
|
-
//
|
|
10423
|
-
//
|
|
10424
|
-
fallback: "
|
|
10661
|
+
// 'ask_human' routes to the LLM-backed autonomous layer via
|
|
10662
|
+
// createTieredBrainArbiter before any human escalation.
|
|
10663
|
+
fallback: "ask_human"
|
|
10425
10664
|
};
|
|
10426
10665
|
const decision = await this.opts.brain.decide(request);
|
|
10427
10666
|
const intervened = await this.maybeIntervene(kind, request, decision);
|
|
@@ -11518,6 +11757,10 @@ function makeDependencyWatcherConfig(opts) {
|
|
|
11518
11757
|
return {
|
|
11519
11758
|
watchPaths: unique,
|
|
11520
11759
|
debounceMs,
|
|
11760
|
+
dispose() {
|
|
11761
|
+
for (const t of pending.values()) clearTimeout(t);
|
|
11762
|
+
pending.clear();
|
|
11763
|
+
},
|
|
11521
11764
|
async onChange(entry) {
|
|
11522
11765
|
if (entry.event === "delete") return;
|
|
11523
11766
|
if (!matchesPattern(entry.path)) return;
|
|
@@ -11935,6 +12178,10 @@ var KnowledgeGraph = class {
|
|
|
11935
12178
|
pendingDeliveries = /* @__PURE__ */ new Map();
|
|
11936
12179
|
filePath;
|
|
11937
12180
|
graphFilePath;
|
|
12181
|
+
/** Exposed for unit-testing only: read current index contents. */
|
|
12182
|
+
getIndex() {
|
|
12183
|
+
return this.index;
|
|
12184
|
+
}
|
|
11938
12185
|
constructor(sessionDir) {
|
|
11939
12186
|
this.filePath = path5.join(sessionDir, "_knowledge_graph");
|
|
11940
12187
|
this.graphFilePath = path5.join(this.filePath, "graph.jsonl");
|
|
@@ -11947,7 +12194,7 @@ var KnowledgeGraph = class {
|
|
|
11947
12194
|
async add(node) {
|
|
11948
12195
|
const full = { id: randomUUID(), ...node };
|
|
11949
12196
|
this.nodes.set(full.id, full);
|
|
11950
|
-
this.
|
|
12197
|
+
this._addToIndex(full, this._indexKeys(full));
|
|
11951
12198
|
await this._persist(full);
|
|
11952
12199
|
this._deliver(full);
|
|
11953
12200
|
return full;
|
|
@@ -11956,8 +12203,10 @@ var KnowledgeGraph = class {
|
|
|
11956
12203
|
async update(id, patch) {
|
|
11957
12204
|
const existing = this.nodes.get(id);
|
|
11958
12205
|
if (!existing) return null;
|
|
12206
|
+
this._removeFromIndex(existing, this._indexKeys(existing));
|
|
11959
12207
|
const updated = { ...existing, ...patch };
|
|
11960
12208
|
this.nodes.set(id, updated);
|
|
12209
|
+
this._addToIndex(updated, this._indexKeys(updated));
|
|
11961
12210
|
this._deliver(updated);
|
|
11962
12211
|
await this._append(updated);
|
|
11963
12212
|
return updated;
|
|
@@ -12043,15 +12292,10 @@ var KnowledgeGraph = class {
|
|
|
12043
12292
|
return { passed: checks.every((c) => c.passed), checks };
|
|
12044
12293
|
}
|
|
12045
12294
|
// ── Private ────────────────────────────────────────────────────────────
|
|
12046
|
-
|
|
12047
|
-
|
|
12048
|
-
|
|
12049
|
-
|
|
12050
|
-
set = /* @__PURE__ */ new Set();
|
|
12051
|
-
this.index.set(key, set);
|
|
12052
|
-
}
|
|
12053
|
-
set.add(node.id);
|
|
12054
|
-
};
|
|
12295
|
+
/** Pure: compute the set of index keys a node would belong to. */
|
|
12296
|
+
_indexKeys(node) {
|
|
12297
|
+
const keys = /* @__PURE__ */ new Set();
|
|
12298
|
+
const add = (key) => keys.add(key);
|
|
12055
12299
|
add(`type:${node.type}`);
|
|
12056
12300
|
if (node.type === "fact") {
|
|
12057
12301
|
const f = node;
|
|
@@ -12060,6 +12304,8 @@ var KnowledgeGraph = class {
|
|
|
12060
12304
|
add(`by:${f.discoveredBy}`);
|
|
12061
12305
|
for (const tag of f.tags) add(`tag:${tag}`);
|
|
12062
12306
|
add(`key:${f.key}`);
|
|
12307
|
+
add(`subject:${f.subject}`);
|
|
12308
|
+
if (f.detail) add(`detail:${f.detail}`);
|
|
12063
12309
|
}
|
|
12064
12310
|
if (node.type === "goal") {
|
|
12065
12311
|
const g = node;
|
|
@@ -12074,6 +12320,24 @@ var KnowledgeGraph = class {
|
|
|
12074
12320
|
add(`by:${c.proposedBy}`);
|
|
12075
12321
|
for (const g of c.satisfiesGoals) add(`goal:${g}`);
|
|
12076
12322
|
}
|
|
12323
|
+
return keys;
|
|
12324
|
+
}
|
|
12325
|
+
/** Mutate the index: add a node's id to every set for the given keys. */
|
|
12326
|
+
_addToIndex(node, keys) {
|
|
12327
|
+
for (const key of keys) {
|
|
12328
|
+
let set = this.index.get(key);
|
|
12329
|
+
if (!set) {
|
|
12330
|
+
set = /* @__PURE__ */ new Set();
|
|
12331
|
+
this.index.set(key, set);
|
|
12332
|
+
}
|
|
12333
|
+
set.add(node.id);
|
|
12334
|
+
}
|
|
12335
|
+
}
|
|
12336
|
+
/** Remove a node's id from all index sets for the given keys. */
|
|
12337
|
+
_removeFromIndex(node, keys) {
|
|
12338
|
+
for (const key of keys) {
|
|
12339
|
+
this.index.get(key)?.delete(node.id);
|
|
12340
|
+
}
|
|
12077
12341
|
}
|
|
12078
12342
|
_matches(node, f) {
|
|
12079
12343
|
if (f.type && node.type !== f.type) return false;
|
|
@@ -12123,11 +12387,15 @@ var KnowledgeGraph = class {
|
|
|
12123
12387
|
try {
|
|
12124
12388
|
const parsed = JSON.parse(line);
|
|
12125
12389
|
if (parsed.op === "update") {
|
|
12390
|
+
const oldNode = this.nodes.get(parsed.node.id);
|
|
12391
|
+
if (oldNode) {
|
|
12392
|
+
this._removeFromIndex(oldNode, this._indexKeys(oldNode));
|
|
12393
|
+
}
|
|
12126
12394
|
this.nodes.set(parsed.node.id, parsed.node);
|
|
12127
|
-
this.
|
|
12395
|
+
this._addToIndex(parsed.node, this._indexKeys(parsed.node));
|
|
12128
12396
|
} else {
|
|
12129
12397
|
this.nodes.set(parsed.id, parsed);
|
|
12130
|
-
this.
|
|
12398
|
+
this._addToIndex(parsed, this._indexKeys(parsed));
|
|
12131
12399
|
}
|
|
12132
12400
|
} catch {
|
|
12133
12401
|
}
|
|
@@ -12428,7 +12696,7 @@ var TaskDAG = class {
|
|
|
12428
12696
|
if (visited.has(current)) continue;
|
|
12429
12697
|
visited.add(current);
|
|
12430
12698
|
const node = this.nodes.get(current);
|
|
12431
|
-
if (node) stack.push(...node.
|
|
12699
|
+
if (node) stack.push(...node.deps);
|
|
12432
12700
|
}
|
|
12433
12701
|
return false;
|
|
12434
12702
|
}
|
|
@@ -12531,8 +12799,10 @@ var ConsensusProtocol = class {
|
|
|
12531
12799
|
return this._resolve(changeId, change.votes, eligible);
|
|
12532
12800
|
}
|
|
12533
12801
|
// ── Private ───────────────────────────────────────────────────────────
|
|
12534
|
-
_eligibleVoters(
|
|
12535
|
-
return Array.from(this.voters.keys())
|
|
12802
|
+
_eligibleVoters(change) {
|
|
12803
|
+
return Array.from(this.voters.keys()).filter(
|
|
12804
|
+
(agentId) => agentId !== change.proposedBy
|
|
12805
|
+
);
|
|
12536
12806
|
}
|
|
12537
12807
|
_resolve(changeId, votes, eligible) {
|
|
12538
12808
|
const totalEligible = eligible.length;
|
|
@@ -12577,7 +12847,7 @@ var ConsensusProtocol = class {
|
|
|
12577
12847
|
if (!quorumMet) {
|
|
12578
12848
|
return {
|
|
12579
12849
|
changeId,
|
|
12580
|
-
outcome: "
|
|
12850
|
+
outcome: "quorum_not_met",
|
|
12581
12851
|
votes,
|
|
12582
12852
|
approveCount: approve.length,
|
|
12583
12853
|
rejectCount: reject.length,
|
|
@@ -12954,7 +13224,7 @@ var AutonomousBrain = class {
|
|
|
12954
13224
|
}
|
|
12955
13225
|
return { type: "deny", reason: `Brain LLM failed: ${String(err)}` };
|
|
12956
13226
|
}
|
|
12957
|
-
|
|
13227
|
+
this._recordDecision({
|
|
12958
13228
|
id,
|
|
12959
13229
|
decisionType,
|
|
12960
13230
|
question,
|
|
@@ -12963,6 +13233,7 @@ var AutonomousBrain = class {
|
|
|
12963
13233
|
rationale: result.rationale,
|
|
12964
13234
|
madeBy: "autonomous-brain",
|
|
12965
13235
|
context: JSON.stringify(context)
|
|
13236
|
+
}).catch(() => {
|
|
12966
13237
|
});
|
|
12967
13238
|
if (requiresConsensus) {
|
|
12968
13239
|
this._emit("brain.decision", { id, decisionType, optionId: result.optionId, rationale: result.rationale, consensusRequired: true });
|
|
@@ -13241,10 +13512,16 @@ var TaskAuctioneer = class {
|
|
|
13241
13512
|
maxTasksPerAgent;
|
|
13242
13513
|
minConfidence;
|
|
13243
13514
|
// minimum dispatcher confidence to accept a bid
|
|
13515
|
+
maxBidRetries;
|
|
13516
|
+
// max republished attempts before marking task failed
|
|
13244
13517
|
/** Pending bids keyed by taskId. */
|
|
13245
13518
|
pendingBids = /* @__PURE__ */ new Map();
|
|
13246
13519
|
/** Active bid windows keyed by taskId. */
|
|
13247
13520
|
bidTimers = /* @__PURE__ */ new Map();
|
|
13521
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
13522
|
+
unsubs = [];
|
|
13523
|
+
/** How many times a task has been republished with no bids received. */
|
|
13524
|
+
bidRetryCounts = /* @__PURE__ */ new Map();
|
|
13248
13525
|
/** Agent → current task count (from graph + in-flight). */
|
|
13249
13526
|
agentTaskCounts = /* @__PURE__ */ new Map();
|
|
13250
13527
|
constructor(opts) {
|
|
@@ -13255,8 +13532,26 @@ var TaskAuctioneer = class {
|
|
|
13255
13532
|
this.bidWindowMs = opts.bidWindowMs ?? 3e4;
|
|
13256
13533
|
this.maxTasksPerAgent = opts.maxTasksPerAgent ?? 3;
|
|
13257
13534
|
this.minConfidence = opts.minConfidence ?? 0.3;
|
|
13258
|
-
this.
|
|
13259
|
-
this.fleet?.filter("task:
|
|
13535
|
+
this.maxBidRetries = opts.maxBidRetries ?? 3;
|
|
13536
|
+
const offBid = this.fleet?.filter("task:bid", (e) => this._onBidEvent(e));
|
|
13537
|
+
const offClaimed = this.fleet?.filter("task:claimed", (e) => this._onClaimedEvent(e));
|
|
13538
|
+
if (offBid) this.unsubs.push(offBid);
|
|
13539
|
+
if (offClaimed) this.unsubs.push(offClaimed);
|
|
13540
|
+
}
|
|
13541
|
+
/**
|
|
13542
|
+
* Detach all FleetBus subscriptions and cancel any open bid-window timers.
|
|
13543
|
+
* Call when the owning coordinator stops/restarts so handlers and timers
|
|
13544
|
+
* don't accumulate across cycles.
|
|
13545
|
+
*/
|
|
13546
|
+
dispose() {
|
|
13547
|
+
for (const off of this.unsubs.splice(0)) {
|
|
13548
|
+
try {
|
|
13549
|
+
off();
|
|
13550
|
+
} catch {
|
|
13551
|
+
}
|
|
13552
|
+
}
|
|
13553
|
+
for (const t of this.bidTimers.values()) clearTimeout(t);
|
|
13554
|
+
this.bidTimers.clear();
|
|
13260
13555
|
}
|
|
13261
13556
|
// ── Publish a task ────────────────────────────────────────────────────
|
|
13262
13557
|
/**
|
|
@@ -13266,14 +13561,16 @@ var TaskAuctioneer = class {
|
|
|
13266
13561
|
* If `targetAgent` is specified, the task is assigned directly without auction.
|
|
13267
13562
|
*/
|
|
13268
13563
|
async publishTask(input) {
|
|
13564
|
+
const blockedBy = input.blockedBy ?? [];
|
|
13565
|
+
const hasOpenBlockers = blockedBy.length > 0 && blockedBy.some((id) => this.graph.get(id)?.status !== "done");
|
|
13269
13566
|
const goal = await this.graph.add({
|
|
13270
13567
|
type: "goal",
|
|
13271
13568
|
title: input.title,
|
|
13272
13569
|
description: input.description,
|
|
13273
|
-
status: input.targetAgent ? "in_progress" : "pending",
|
|
13570
|
+
status: input.targetAgent ? "in_progress" : hasOpenBlockers ? "blocked" : "pending",
|
|
13274
13571
|
priority: input.priority ?? "medium",
|
|
13275
13572
|
assignee: input.targetAgent,
|
|
13276
|
-
blockedBy
|
|
13573
|
+
blockedBy,
|
|
13277
13574
|
dependsOn: input.satisfiesGoals ?? [],
|
|
13278
13575
|
createdBy: this.selfAgentId,
|
|
13279
13576
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13290,6 +13587,12 @@ var TaskAuctioneer = class {
|
|
|
13290
13587
|
});
|
|
13291
13588
|
}
|
|
13292
13589
|
}
|
|
13590
|
+
for (const blockerId of blockedBy) {
|
|
13591
|
+
const blocker = this.graph.get(blockerId);
|
|
13592
|
+
if (blocker && !blocker.children.includes(goal.id)) {
|
|
13593
|
+
await this.graph.update(blockerId, { children: [...blocker.children, goal.id] });
|
|
13594
|
+
}
|
|
13595
|
+
}
|
|
13293
13596
|
if (input.targetAgent) {
|
|
13294
13597
|
await this._assignDirect(goal.id, input.targetAgent);
|
|
13295
13598
|
} else {
|
|
@@ -13401,9 +13704,12 @@ Priority: ${goal.priority}`,
|
|
|
13401
13704
|
status: "done",
|
|
13402
13705
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13403
13706
|
});
|
|
13707
|
+
this.bidRetryCounts.delete(taskId);
|
|
13708
|
+
this.pendingBids.delete(taskId);
|
|
13709
|
+
this._cancelBidWindow(taskId);
|
|
13404
13710
|
for (const childId of goal.children) {
|
|
13405
13711
|
const child = this.graph.get(childId);
|
|
13406
|
-
if (child) {
|
|
13712
|
+
if (child && child.status === "blocked") {
|
|
13407
13713
|
const allUnblocked = child.blockedBy.every((blockedId) => {
|
|
13408
13714
|
const blocked = this.graph.get(blockedId);
|
|
13409
13715
|
return blocked?.status === "done";
|
|
@@ -13412,6 +13718,7 @@ Priority: ${goal.priority}`,
|
|
|
13412
13718
|
await this.graph.update(childId, { status: "pending", updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
13413
13719
|
const unblockedGoal = this.graph.get(childId);
|
|
13414
13720
|
await this._broadcastTask(unblockedGoal);
|
|
13721
|
+
this._startBidWindow(childId);
|
|
13415
13722
|
}
|
|
13416
13723
|
}
|
|
13417
13724
|
}
|
|
@@ -13437,6 +13744,9 @@ ${_result ?? "No result provided."}`
|
|
|
13437
13744
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13438
13745
|
result: error
|
|
13439
13746
|
});
|
|
13747
|
+
this.bidRetryCounts.delete(taskId);
|
|
13748
|
+
this.pendingBids.delete(taskId);
|
|
13749
|
+
this._cancelBidWindow(taskId);
|
|
13440
13750
|
this._emit("task:failed", { taskId, agentId, error });
|
|
13441
13751
|
await this._mailboxPublish({
|
|
13442
13752
|
type: "note",
|
|
@@ -13515,7 +13825,7 @@ Assignee: ${agentId}`
|
|
|
13515
13825
|
priority: goal.priority,
|
|
13516
13826
|
tags: goal.tags
|
|
13517
13827
|
});
|
|
13518
|
-
|
|
13828
|
+
this._mailboxPublish({
|
|
13519
13829
|
type: "broadcast",
|
|
13520
13830
|
subject: `[task] ${goal.title} (${goal.priority})`,
|
|
13521
13831
|
body: `New task available: "${goal.title}"
|
|
@@ -13526,6 +13836,7 @@ Task ID: ${goal.id}
|
|
|
13526
13836
|
Tags: ${goal.tags.join(", ") || "none"}
|
|
13527
13837
|
|
|
13528
13838
|
Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
13839
|
+
}).catch(() => {
|
|
13529
13840
|
});
|
|
13530
13841
|
}
|
|
13531
13842
|
async _mailboxPublish(msg) {
|
|
@@ -13559,9 +13870,10 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13559
13870
|
}
|
|
13560
13871
|
_startBidWindow(taskId) {
|
|
13561
13872
|
this._cancelBidWindow(taskId);
|
|
13562
|
-
const timer = setTimeout(
|
|
13873
|
+
const timer = setTimeout(() => {
|
|
13563
13874
|
this.bidTimers.delete(taskId);
|
|
13564
|
-
|
|
13875
|
+
void this._evaluateBids(taskId).catch(() => {
|
|
13876
|
+
});
|
|
13565
13877
|
}, this.bidWindowMs);
|
|
13566
13878
|
this.bidTimers.set(taskId, timer);
|
|
13567
13879
|
}
|
|
@@ -13575,6 +13887,13 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13575
13887
|
async _evaluateBids(taskId) {
|
|
13576
13888
|
const bids = this.pendingBids.get(taskId);
|
|
13577
13889
|
if (!bids || bids.length === 0) {
|
|
13890
|
+
const retryCount = (this.bidRetryCounts.get(taskId) ?? 0) + 1;
|
|
13891
|
+
this.bidRetryCounts.set(taskId, retryCount);
|
|
13892
|
+
if (retryCount >= this.maxBidRetries) {
|
|
13893
|
+
await this.fail(taskId, `No bids received after ${this.maxBidRetries} attempts`);
|
|
13894
|
+
this.bidRetryCounts.delete(taskId);
|
|
13895
|
+
return;
|
|
13896
|
+
}
|
|
13578
13897
|
const goal = this.graph.get(taskId);
|
|
13579
13898
|
if (goal) {
|
|
13580
13899
|
await this._broadcastTask(goal);
|
|
@@ -13583,7 +13902,15 @@ Bid by calling taskAuctioneer.bid("${goal.id}", ...)`
|
|
|
13583
13902
|
return;
|
|
13584
13903
|
}
|
|
13585
13904
|
bids.sort((a, b) => b.score - a.score);
|
|
13586
|
-
const winner = bids
|
|
13905
|
+
const winner = bids.find((b) => this._getAgentTaskCount(b.agentId) < this.maxTasksPerAgent);
|
|
13906
|
+
if (!winner) {
|
|
13907
|
+
const goal = this.graph.get(taskId);
|
|
13908
|
+
if (goal) {
|
|
13909
|
+
await this._broadcastTask(goal);
|
|
13910
|
+
this._startBidWindow(taskId);
|
|
13911
|
+
}
|
|
13912
|
+
return;
|
|
13913
|
+
}
|
|
13587
13914
|
await this.claim(taskId, winner.agentId, winner.agentName);
|
|
13588
13915
|
}
|
|
13589
13916
|
async _assignDirect(taskId, agentId) {
|
|
@@ -13630,16 +13957,24 @@ var AutonomousCoordinator = class {
|
|
|
13630
13957
|
selfAgentId;
|
|
13631
13958
|
fleet;
|
|
13632
13959
|
fleetManager;
|
|
13960
|
+
director;
|
|
13633
13961
|
mailbox;
|
|
13634
13962
|
events;
|
|
13963
|
+
onCoordinatorEvent;
|
|
13635
13964
|
running = false;
|
|
13636
13965
|
iterationCount = 0;
|
|
13966
|
+
/** Tasks already handled by _onSubagentTerminated (to avoid double goal:failed on fleet event). */
|
|
13967
|
+
_handledBySubagent = /* @__PURE__ */ new Set();
|
|
13968
|
+
/** FleetBus subscription disposers, detached in dispose(). */
|
|
13969
|
+
unsubs = [];
|
|
13637
13970
|
constructor(opts) {
|
|
13638
13971
|
this.selfAgentId = opts.selfAgentId;
|
|
13639
13972
|
this.fleet = opts.fleet ?? void 0;
|
|
13640
13973
|
this.fleetManager = opts.fleetManager ?? void 0;
|
|
13974
|
+
this.director = opts.director ?? void 0;
|
|
13641
13975
|
this.mailbox = opts.mailbox ?? void 0;
|
|
13642
13976
|
this.events = opts.events ?? void 0;
|
|
13977
|
+
this.onCoordinatorEvent = opts.onCoordinatorEvent;
|
|
13643
13978
|
this.graph = new KnowledgeGraph(opts.sessionDir);
|
|
13644
13979
|
this.dag = new TaskDAG();
|
|
13645
13980
|
this.auction = new TaskAuctioneer({
|
|
@@ -13675,9 +14010,19 @@ var AutonomousCoordinator = class {
|
|
|
13675
14010
|
this.dag.onEvent((event) => {
|
|
13676
14011
|
this._onDagEvent(event);
|
|
13677
14012
|
});
|
|
13678
|
-
this.fleet?.filter("subagent.
|
|
14013
|
+
const offCompleted = this.fleet?.filter("subagent.completed", (e) => {
|
|
13679
14014
|
this._onSubagentTerminated(e);
|
|
13680
14015
|
});
|
|
14016
|
+
if (offCompleted) this.unsubs.push(offCompleted);
|
|
14017
|
+
const offFailed = this.fleet?.filter("task:failed", (e) => {
|
|
14018
|
+
const payload = e.payload;
|
|
14019
|
+
const taskId = payload?.taskId;
|
|
14020
|
+
if (!taskId || this._handledBySubagent.has(taskId)) return;
|
|
14021
|
+
this._handledBySubagent.add(taskId);
|
|
14022
|
+
this._emit({ type: "goal:failed", goalId: taskId, text: payload?.error ?? "Task failed" });
|
|
14023
|
+
});
|
|
14024
|
+
if (offFailed) this.unsubs.push(offFailed);
|
|
14025
|
+
this._emit({ type: "coordinator:mode", mode: this.fleet ? "fleet" : "standalone" });
|
|
13681
14026
|
}
|
|
13682
14027
|
// ── Public API ───────────────────────────────────────────────────────
|
|
13683
14028
|
/**
|
|
@@ -13691,11 +14036,13 @@ var AutonomousCoordinator = class {
|
|
|
13691
14036
|
const maxIterations = opts.maxIterations ?? 100;
|
|
13692
14037
|
const goal = opts.goal ?? "Improve the codebase";
|
|
13693
14038
|
const maxCost = opts.maxCostUsd;
|
|
13694
|
-
await this.graph.load();
|
|
13695
14039
|
try {
|
|
14040
|
+
await this.graph.load();
|
|
13696
14041
|
const goalConfigs = await this._decomposeGoal(goal);
|
|
13697
14042
|
for (const g of goalConfigs) {
|
|
13698
|
-
await this.auction.publishTask(g);
|
|
14043
|
+
const goalId = await this.auction.publishTask(g);
|
|
14044
|
+
this.dag.addNode(goalId, g.description, []);
|
|
14045
|
+
this._emit({ type: "goal:added", goalId, title: g.title, text: g.description });
|
|
13699
14046
|
}
|
|
13700
14047
|
while (this.running) {
|
|
13701
14048
|
this.iterationCount++;
|
|
@@ -13722,6 +14069,7 @@ var AutonomousCoordinator = class {
|
|
|
13722
14069
|
const blocked = this.dag.getBlocked();
|
|
13723
14070
|
if (blocked.length > 0 && this.dag.hasDeadlock()) {
|
|
13724
14071
|
(this.events?.emit)("autonomous:deadlock", { blocked });
|
|
14072
|
+
this._emit({ type: "deadlock:detected", goalId: blocked[0]?.id ?? "", text: `Deadlock detected: ${blocked.map((n) => n.id).join(", ")}` });
|
|
13725
14073
|
this.running = false;
|
|
13726
14074
|
}
|
|
13727
14075
|
break;
|
|
@@ -13738,7 +14086,15 @@ var AutonomousCoordinator = class {
|
|
|
13738
14086
|
}
|
|
13739
14087
|
const pendingChanges = this.changes.getPendingReviews();
|
|
13740
14088
|
for (const change of pendingChanges) {
|
|
13741
|
-
|
|
14089
|
+
try {
|
|
14090
|
+
await this._handlePendingChange(change);
|
|
14091
|
+
} catch (err) {
|
|
14092
|
+
this._emit({
|
|
14093
|
+
type: "goal:failed",
|
|
14094
|
+
goalId: change.id,
|
|
14095
|
+
text: `Consensus handling failed: ${err instanceof Error ? err.message : String(err)}`
|
|
14096
|
+
});
|
|
14097
|
+
}
|
|
13742
14098
|
}
|
|
13743
14099
|
}
|
|
13744
14100
|
} finally {
|
|
@@ -13748,7 +14104,26 @@ var AutonomousCoordinator = class {
|
|
|
13748
14104
|
}
|
|
13749
14105
|
/** Stop the autonomous loop. */
|
|
13750
14106
|
stop() {
|
|
14107
|
+
if (!this.running) return;
|
|
13751
14108
|
this.running = false;
|
|
14109
|
+
console.error(`[AutonomousCoordinator] stop signal received \u2014 shutting down (iteration ${this.iterationCount})`);
|
|
14110
|
+
}
|
|
14111
|
+
/**
|
|
14112
|
+
* Tear down the coordinator for good: stop the loop and detach all FleetBus
|
|
14113
|
+
* subscriptions (this coordinator's + the auctioneer's) plus any open bid
|
|
14114
|
+
* timers. Call this when discarding the instance (e.g. `/coordinator stop`
|
|
14115
|
+
* that recreates a fresh coordinator on the next start) so handlers and
|
|
14116
|
+
* timers don't accumulate across cycles. `stop()` only pauses the loop.
|
|
14117
|
+
*/
|
|
14118
|
+
dispose() {
|
|
14119
|
+
this.stop();
|
|
14120
|
+
for (const off of this.unsubs.splice(0)) {
|
|
14121
|
+
try {
|
|
14122
|
+
off();
|
|
14123
|
+
} catch {
|
|
14124
|
+
}
|
|
14125
|
+
}
|
|
14126
|
+
this.auction.dispose();
|
|
13752
14127
|
}
|
|
13753
14128
|
/** Get a stats snapshot. */
|
|
13754
14129
|
getStats() {
|
|
@@ -13803,6 +14178,7 @@ var AutonomousCoordinator = class {
|
|
|
13803
14178
|
body: `**${input.category}**${input.file ? ` in ${input.file}${input.line ? `:${input.line}` : ""}` : ""}
|
|
13804
14179
|
${input.detail}`
|
|
13805
14180
|
});
|
|
14181
|
+
this._emit({ type: "knowledge:added", knowledgeId: fact.id, title: input.subject, text: input.detail });
|
|
13806
14182
|
return fact;
|
|
13807
14183
|
}
|
|
13808
14184
|
// ── Goal creation helpers ────────────────────────────────────────────
|
|
@@ -13815,7 +14191,10 @@ ${input.detail}`
|
|
|
13815
14191
|
title: input.title,
|
|
13816
14192
|
description: input.description,
|
|
13817
14193
|
priority: resolvedPriority,
|
|
13818
|
-
...input.tags ? { tags: input.tags } : {}
|
|
14194
|
+
...input.tags ? { tags: input.tags } : {},
|
|
14195
|
+
// Mirror the dependency edges into the auction so blocked goals aren't
|
|
14196
|
+
// biddable until their deps complete (the DAG tracks the same edges).
|
|
14197
|
+
...input.deps && input.deps.length > 0 ? { blockedBy: input.deps } : {}
|
|
13819
14198
|
});
|
|
13820
14199
|
const goal = this.graph.get(goalId);
|
|
13821
14200
|
for (const depId of input.deps ?? []) {
|
|
@@ -13856,30 +14235,34 @@ ${input.detail}`
|
|
|
13856
14235
|
async _processGoal(goalId) {
|
|
13857
14236
|
const ready = this.dag.getReady();
|
|
13858
14237
|
if (ready.length === 0) return;
|
|
13859
|
-
const
|
|
13860
|
-
this.dag.start(
|
|
13861
|
-
const
|
|
13862
|
-
if (!
|
|
13863
|
-
|
|
13864
|
-
|
|
13865
|
-
|
|
13866
|
-
|
|
13867
|
-
|
|
14238
|
+
const dagNode = ready.find((n) => n.id === goalId) ?? ready[0];
|
|
14239
|
+
this.dag.start(dagNode.id, "auctioneer");
|
|
14240
|
+
const goalNode = this.graph.get(goalId);
|
|
14241
|
+
if (!goalNode) return;
|
|
14242
|
+
const title = goalNode.title || dagNode.description;
|
|
14243
|
+
const taskId = await this.auction.publishTask({
|
|
14244
|
+
title,
|
|
14245
|
+
description: goalNode.description,
|
|
14246
|
+
priority: this._dagPriorityToGoal(dagNode.priority),
|
|
14247
|
+
tags: dagNode.tags
|
|
14248
|
+
});
|
|
14249
|
+
this._emit({ type: "task:ready", goalId, taskId, title });
|
|
14250
|
+
if (this.director) {
|
|
14251
|
+
const config = {
|
|
14252
|
+
name: `worker-${goalId.slice(0, 8)}`,
|
|
14253
|
+
role: "general",
|
|
14254
|
+
maxIterations: 100,
|
|
14255
|
+
timeoutMs: 6e5
|
|
14256
|
+
// 10 minutes per goal
|
|
14257
|
+
};
|
|
14258
|
+
const subagentId = await this.director.spawn(config);
|
|
14259
|
+
await this.auction.claim(taskId, subagentId, config.name);
|
|
14260
|
+
await this.director.assign({
|
|
14261
|
+
id: goalId,
|
|
14262
|
+
subagentId,
|
|
14263
|
+
description: goalNode.description
|
|
13868
14264
|
});
|
|
13869
14265
|
}
|
|
13870
|
-
await this._waitForClaim(next.id);
|
|
13871
|
-
}
|
|
13872
|
-
async _waitForClaim(taskId) {
|
|
13873
|
-
const maxWait = 6e4;
|
|
13874
|
-
const pollInterval = 2e3;
|
|
13875
|
-
const start = Date.now();
|
|
13876
|
-
while (Date.now() - start < maxWait) {
|
|
13877
|
-
const goal = this.graph.get(taskId);
|
|
13878
|
-
if (goal?.status === "in_progress" || goal?.status === "done") {
|
|
13879
|
-
return;
|
|
13880
|
-
}
|
|
13881
|
-
await this._sleep(pollInterval);
|
|
13882
|
-
}
|
|
13883
14266
|
}
|
|
13884
14267
|
async _handlePendingChange(change) {
|
|
13885
14268
|
const result = this.consensus.getStatus(change.id);
|
|
@@ -13893,6 +14276,17 @@ ${input.detail}`
|
|
|
13893
14276
|
);
|
|
13894
14277
|
if (voteResult.outcome === "approved") {
|
|
13895
14278
|
await this.changes.markApplied(change.id, (/* @__PURE__ */ new Date()).toISOString());
|
|
14279
|
+
this._emit({ type: "consensus:reached", goalId: change.id, text: "Change approved and applied" });
|
|
14280
|
+
}
|
|
14281
|
+
} else {
|
|
14282
|
+
const voteResult = await this.consensus.castVote(
|
|
14283
|
+
change.id,
|
|
14284
|
+
this.selfAgentId,
|
|
14285
|
+
"reject",
|
|
14286
|
+
`Quality gate failed: ${change.qualityGate.checks.map((c) => `${c.name}=${c.passed}`).join(", ")}`
|
|
14287
|
+
);
|
|
14288
|
+
if (voteResult.outcome === "rejected" || voteResult.outcome === "vetoed") {
|
|
14289
|
+
this._emit({ type: "consensus:reached", goalId: change.id, text: "Change rejected by quality gate" });
|
|
13896
14290
|
}
|
|
13897
14291
|
}
|
|
13898
14292
|
}
|
|
@@ -13903,10 +14297,6 @@ ${input.detail}`
|
|
|
13903
14297
|
(this.events?.emit)("autonomous:task_ready", { taskId: event.nodeId, description: node.description });
|
|
13904
14298
|
}
|
|
13905
14299
|
}
|
|
13906
|
-
if (event.type === "deadlock") {
|
|
13907
|
-
(this.events?.emit)("autonomous:deadlock", { blocked: event.blocked });
|
|
13908
|
-
this.running = false;
|
|
13909
|
-
}
|
|
13910
14300
|
if (event.type === "graph:done") {
|
|
13911
14301
|
(this.events?.emit)("autonomous:all_done", this.getStats());
|
|
13912
14302
|
}
|
|
@@ -13914,13 +14304,16 @@ ${input.detail}`
|
|
|
13914
14304
|
_onSubagentTerminated(e) {
|
|
13915
14305
|
const payload = e.payload;
|
|
13916
14306
|
const subagentId = payload?.subagentId ?? e.subagentId;
|
|
13917
|
-
const stopReason = payload?.stopReason ?? "unknown";
|
|
14307
|
+
const stopReason = payload?.stopReason ?? (payload?.status === "ok" ? "end_turn" : payload?.status ?? "unknown");
|
|
13918
14308
|
const tasks = this.auction.getTasksForAgent(subagentId);
|
|
13919
14309
|
for (const task of tasks) {
|
|
14310
|
+
this._handledBySubagent.add(task.id);
|
|
13920
14311
|
if (stopReason === "end_turn") {
|
|
13921
14312
|
void this.auction.complete(task.id, "Subagent completed successfully");
|
|
14313
|
+
this._emit({ type: "task:completed", goalId: task.id, taskId: task.id, text: "Subagent completed successfully" });
|
|
13922
14314
|
} else {
|
|
13923
14315
|
void this.auction.fail(task.id, `Subagent terminated: ${stopReason}`);
|
|
14316
|
+
this._emit({ type: "goal:failed", goalId: task.id, text: `Subagent terminated: ${stopReason}` });
|
|
13924
14317
|
}
|
|
13925
14318
|
}
|
|
13926
14319
|
}
|
|
@@ -13934,6 +14327,10 @@ ${input.detail}`
|
|
|
13934
14327
|
}
|
|
13935
14328
|
_buildVoters() {
|
|
13936
14329
|
return [
|
|
14330
|
+
// The coordinator itself casts the quality-gate auto-vote in
|
|
14331
|
+
// _handlePendingChange — it MUST be a registered, eligible voter or
|
|
14332
|
+
// castVote throws "unknown voter" and tears down the run() loop.
|
|
14333
|
+
{ agentId: this.selfAgentId, agentName: "Coordinator", role: "coordinator", weight: 1 },
|
|
13937
14334
|
{ agentId: "critic", agentName: "Critic", role: "critic", weight: 2, veto: true },
|
|
13938
14335
|
{ agentId: "bug-hunter", agentName: "Bug Hunter", role: "bug-hunter", weight: 1.5 },
|
|
13939
14336
|
{ agentId: "security-scanner", agentName: "Security Scanner", role: "security-scanner", weight: 1.5 },
|
|
@@ -13971,11 +14368,12 @@ ${input.detail}`
|
|
|
13971
14368
|
} catch {
|
|
13972
14369
|
}
|
|
13973
14370
|
}
|
|
13974
|
-
|
|
13975
|
-
|
|
14371
|
+
/** Emit a CoordinatorEvent to the subscriber (e.g. TUI panel timeline). */
|
|
14372
|
+
_emit(event) {
|
|
14373
|
+
this.onCoordinatorEvent?.(event);
|
|
13976
14374
|
}
|
|
13977
14375
|
};
|
|
13978
14376
|
|
|
13979
|
-
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
14377
|
+
export { ACP_AGENTS, AGENTS_BY_PHASE, AGENT_CATALOG, TOOLS as AGENT_TOOL_PRESETS, ALL_AGENT_DEFINITIONS, ALL_FLEET_AGENTS, AUDIT_LOG_AGENT, AutonomousBrain, AutonomousCoordinator, BUG_HUNTER_AGENT, BUILD_AGENTS, BrainDecisionQueue, BrainMonitor, BudgetExceededError, BudgetThresholdSignal, ChangeManager, CollabSession, ConsensusProtocol, DECISION_TIMEOUT_MS, DEFAULT_DIRECTOR_PREAMBLE, DEFAULT_DISPATCH_ROLE, DEFAULT_QUALITY_CHECKS, DEFAULT_SUBAGENT_BASELINE, DELIVERY_AGENTS, DEPENDENCY_FILE_PATTERNS, DISCOVERY_AGENTS, DOMAIN_AGENTS, DefaultBrainArbiter, DefaultMailbox, DefaultMultiAgentCoordinator, Director, DirectorAlertLevel, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, FleetBus, FleetCostCapError, FleetManager, FleetSpawnBudgetError, FleetUsageAggregator, GlobalMailbox, HEAVY_BUDGET, HumanEscalatingBrainArbiter, InMemoryAgentBridge, InMemoryBridgeTransport, KNOWLEDGE_AGENTS, KnowledgeGraph, LIGHT_BUDGET, LargeAnswerStore, MEDIUM_BUDGET, META_AGENTS, NULL_FLEET_BUS, ObservableBrainArbiter, PLANNING_AGENTS, REFACTOR_PLANNER_AGENT, REVIEW_AGENTS, SECURITY_SCANNER_AGENT, SubagentBudget, TIMEOUT_PREEMPT_FRACTION, TaskAuctioneer, TaskDAG, VERIFY_AGENTS, applyRosterBudget, attachAutoExtend, attachDepWatcherBridge, composeDirectorPrompt, composeSubagentPrompt, createDelegateTool, createMailboxHooks, createMessage, detectEcosystem, dispatchAgent, formatHumanPrompt, getAgentDefinition, getFullPackageLog, getManifestPackages, getPackageAuthor, getPackagesByAgent, mailboxSessionTag, makeAgentSubagentRunner, makeAskResultTool, makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeDependencyWatcherConfig, makeDirectorSessionFactory, makeFleetEmitTool, makeFleetHealthTool, makeFleetSessionTool, makeFleetStatusTool, makeFleetUsageTool, makeLLMClassifier, makeMailInboxTool, makeMailSendTool, makeMailboxTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, makeWorkCompleteTool, normalizeRecipient, recordPackageAction, resolveMailboxIdentity, resolveProjectDir, rosterSummaryFromConfigs, scoreAgents, startPackageOutdatedWatcher, updatePackageOutdatedStatus, withDisabledToolFiltering };
|
|
13980
14378
|
//# sourceMappingURL=index.js.map
|
|
13981
14379
|
//# sourceMappingURL=index.js.map
|