@wrongstack/core 0.308.6 → 0.309.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/coordination/agents/index.js +1 -0
- package/dist/coordination/agents/role-skills.d.ts +1 -0
- package/dist/coordination/director/director-toolset.d.ts +2 -2
- package/dist/coordination/director-mutation-test-tool.d.ts +29 -0
- package/dist/coordination/director-tools.d.ts +2 -0
- package/dist/coordination/director.d.ts +9 -0
- package/dist/coordination/explore-companion.d.ts +191 -0
- package/dist/coordination/fleet.d.ts +26 -0
- package/dist/coordination/index.d.ts +2 -1
- package/dist/coordination/index.js +1396 -370
- package/dist/coordination/mail-tools.d.ts +10 -6
- package/dist/coordination/mailbox-codecs.d.ts +31 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +14 -0
- package/dist/coordination/multi-agent-timeout.d.ts +11 -1
- package/dist/coordination/mutation-engine.d.ts +74 -0
- package/dist/coordination/subagent-budget.d.ts +54 -0
- package/dist/coordination/subagent-finish.d.ts +78 -0
- package/dist/core/index.js +19 -4
- package/dist/defaults/index.js +731 -52
- package/dist/execution/compaction-core.d.ts +1 -1
- package/dist/execution/compaction-elision.d.ts +0 -10
- package/dist/execution/index.js +269 -16
- package/dist/goal/index.js +54 -27
- package/dist/goal/phase-orchestrator.d.ts +7 -0
- package/dist/goal/types.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1280 -201
- package/dist/kernel/events/agent-events.d.ts +31 -2
- package/dist/models/index.js +11 -1
- package/dist/plugin/discovery.d.ts +73 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.js +270 -29
- package/dist/plugin/loader.d.ts +5 -1
- package/dist/plugin/trust.d.ts +78 -0
- package/dist/tools/index.js +1 -0
- package/dist/types/config/mcp-features.d.ts +21 -0
- package/dist/types/config/skills-fleet-brain.d.ts +18 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.js +14 -0
- package/dist/types/multi-agent.d.ts +15 -0
- package/dist/types/provider.d.ts +29 -1
- package/instructions/agents/chaos-monkey.md +57 -0
- package/instructions/agents/explore-companion.md +35 -0
- package/package.json +3 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { estimateMessageTokens } from '../utils/token-estimate.js';
|
|
2
2
|
export { buildSmartDigest, type ContentScore, extractText, hasLargeToolResult, hasToolUse, scoreMessage, } from './compaction-scoring.js';
|
|
3
|
-
export {
|
|
3
|
+
export { collapseAcknowledgedToolReceipts, eliseAcknowledgedToolResults, eliseOldToolResults, type EliseResult, findPreserveStart, isElidedResultContent, isElidedToolInput, normalizePathKey, readPathOf, setCompactionDebugLogger, summarizeToolResultElision, summarizeToolUseInputElision, } from './compaction-elision.js';
|
|
4
4
|
export { buildLosslessDigest, type DedupResult, dedupStaleReads, elideMessageToolIo, enforceHardBudget, findExchangeStart, findSafeBoundary, type HardBudgetResult, hasTextContent, headTailTruncate, truncateMessageText, } from './compaction-budget.js';
|
|
5
5
|
export { CompactionSummaryCache, type CompactionSummaryCacheOptions, compactionSummaryKey, isPlaceholderSummary, PLACEHOLDER_SUMMARIES, } from './compaction-summary-cache.js';
|
|
6
6
|
export declare const estimateMessages: typeof estimateMessageTokens;
|
|
@@ -11,9 +11,7 @@ export interface CompactionMetrics {
|
|
|
11
11
|
tokensSaved: number;
|
|
12
12
|
changed: boolean;
|
|
13
13
|
}
|
|
14
|
-
export declare function compactionDebugEnabled(): boolean;
|
|
15
14
|
export declare function setCompactionDebugLogger(logger: Logger | undefined): void;
|
|
16
|
-
export declare function emitCompactionMetrics(event: string, metrics: CompactionMetrics): void;
|
|
17
15
|
export interface EliseResult {
|
|
18
16
|
messages: Message[];
|
|
19
17
|
saved: number;
|
|
@@ -30,13 +28,8 @@ export interface FileToolLifecycle {
|
|
|
30
28
|
activeReadIds: Set<string>;
|
|
31
29
|
staleReadPaths: Map<string, string>;
|
|
32
30
|
}
|
|
33
|
-
export declare function isReadToolName(name: string): boolean;
|
|
34
|
-
export declare function isFileMutationToolName(name: string): boolean;
|
|
35
|
-
export declare function didFileMutationRun(use: ToolUseBlock): boolean;
|
|
36
|
-
export declare function sameFilePath(a: string, b: string): boolean;
|
|
37
31
|
export declare function readPathOf(input: Record<string, unknown> | undefined): string | undefined;
|
|
38
32
|
export declare function normalizePathKey(p: string): string;
|
|
39
|
-
export declare function analyzeFileToolLifecycle(messages: readonly Message[], acknowledgedBefore: number): FileToolLifecycle;
|
|
40
33
|
export declare function isElidedResultContent(content: string): boolean;
|
|
41
34
|
export declare function isElidedToolInput(input: Record<string, unknown> | undefined): boolean;
|
|
42
35
|
export declare function eliseAcknowledgedToolResults(messages: readonly Message[], opts: {
|
|
@@ -52,7 +45,4 @@ export declare function eliseOldToolResults(messages: readonly Message[], opts:
|
|
|
52
45
|
}, findPreserveStartFn?: (messages: readonly Message[], preserveK: number) => number): EliseResult;
|
|
53
46
|
export declare function summarizeToolUseInputElision(block: ToolUseBlock, tokens: number): Record<string, unknown>;
|
|
54
47
|
export declare function summarizeToolResultElision(block: ToolResultBlock, tokens: number): string;
|
|
55
|
-
export declare function safeToolResultString(content: unknown): string;
|
|
56
|
-
export declare function extractPathHints(content: unknown): string[];
|
|
57
|
-
export declare function firstErrorLine(content: unknown): string | undefined;
|
|
58
48
|
//# sourceMappingURL=compaction-elision.d.ts.map
|
package/dist/execution/index.js
CHANGED
|
@@ -11592,7 +11592,7 @@ function resolveReasoningForRequest(settings, rc, warnings) {
|
|
|
11592
11592
|
const cfg = settings.reasoning;
|
|
11593
11593
|
if (!cfg) return void 0;
|
|
11594
11594
|
const capKnown = rc !== void 0;
|
|
11595
|
-
const supportsReasoning = rc ? rc.default !== "disabled" || rc.disableSupported || rc.effortSupported : false;
|
|
11595
|
+
const supportsReasoning = rc ? rc.default !== "disabled" || rc.disableSupported || rc.effortSupported !== false : false;
|
|
11596
11596
|
const out = {};
|
|
11597
11597
|
if (cfg.mode === "off") {
|
|
11598
11598
|
if (capKnown && rc?.disableSupported) {
|
|
@@ -11614,14 +11614,17 @@ function resolveReasoningForRequest(settings, rc, warnings) {
|
|
|
11614
11614
|
}
|
|
11615
11615
|
const effort = cfg.effort;
|
|
11616
11616
|
if (effort !== void 0) {
|
|
11617
|
-
if (capKnown
|
|
11618
|
-
|
|
11619
|
-
|
|
11617
|
+
if (!capKnown) {
|
|
11618
|
+
} else if (rc?.effortSupported === false) {
|
|
11619
|
+
warnings.push(
|
|
11620
|
+
`reasoning effort "${effort}" requested, but this model does not support effort control; the setting was omitted.`
|
|
11621
|
+
);
|
|
11622
|
+
} else if (rc?.effortSupported === true && rc.effortLevels.length > 0 && !rc.effortLevels.includes(effort)) {
|
|
11620
11623
|
warnings.push(
|
|
11621
11624
|
`reasoning effort "${effort}" not supported by this model (supported: ${rc.effortLevels.join(", ")}); the setting was omitted.`
|
|
11622
11625
|
);
|
|
11623
|
-
} else
|
|
11624
|
-
|
|
11626
|
+
} else {
|
|
11627
|
+
out.effort = effort;
|
|
11625
11628
|
}
|
|
11626
11629
|
}
|
|
11627
11630
|
if (cfg.preserve !== void 0) {
|
|
@@ -11973,6 +11976,46 @@ function asTextBlocks(system) {
|
|
|
11973
11976
|
// src/execution/parallel-eternal-engine.ts
|
|
11974
11977
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
11975
11978
|
|
|
11979
|
+
// src/core/btw.ts
|
|
11980
|
+
var META_KEY2 = "_btwNotes";
|
|
11981
|
+
var MAX_PENDING = 20;
|
|
11982
|
+
function readQueue(ctx) {
|
|
11983
|
+
const raw = ctx.meta[META_KEY2];
|
|
11984
|
+
return Array.isArray(raw) ? raw : [];
|
|
11985
|
+
}
|
|
11986
|
+
function setBtwNote(ctx, text) {
|
|
11987
|
+
const trimmed = text.trim();
|
|
11988
|
+
if (!trimmed) return readQueue(ctx).length;
|
|
11989
|
+
const next = [...readQueue(ctx), trimmed].slice(-MAX_PENDING);
|
|
11990
|
+
ctx.meta[META_KEY2] = next;
|
|
11991
|
+
return next.length;
|
|
11992
|
+
}
|
|
11993
|
+
|
|
11994
|
+
// src/coordination/subagent-finish.ts
|
|
11995
|
+
var SUBAGENT_FINISH_REQUESTED_EVENT = "subagent.finish_requested";
|
|
11996
|
+
var DEFAULT_SUBAGENT_FINISH_GRACE_MS = 12e4;
|
|
11997
|
+
function resolveGracefulFinish(config) {
|
|
11998
|
+
const raw = config.gracefulFinish;
|
|
11999
|
+
if (raw === void 0 || raw === false) return void 0;
|
|
12000
|
+
if (raw === true) return { graceMs: DEFAULT_SUBAGENT_FINISH_GRACE_MS };
|
|
12001
|
+
const graceMs = typeof raw.graceMs === "number" && Number.isFinite(raw.graceMs) && raw.graceMs > 0 ? Math.floor(raw.graceMs) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
12002
|
+
return { graceMs };
|
|
12003
|
+
}
|
|
12004
|
+
function buildSubagentFinishNotice(input) {
|
|
12005
|
+
const localTime = new Date(input.deadlineMs).toISOString();
|
|
12006
|
+
const seconds = Math.max(1, Math.round(input.graceMs / 1e3));
|
|
12007
|
+
const timeLeft = input.graceMs > 0 ? `You have roughly ${seconds} seconds (until ${localTime}) of legitimate working time left.` : `Your working-time window is already spent (deadline was ${localTime}) \u2014 finish now.`;
|
|
12008
|
+
return [
|
|
12009
|
+
"[SUBAGENT FINISH] The leader agent has finished its work.",
|
|
12010
|
+
`Reason: ${input.reason}`,
|
|
12011
|
+
timeLeft,
|
|
12012
|
+
"Finish your task now, in this turn: complete the thought you are working on, stop",
|
|
12013
|
+
"starting new tool calls unless one is strictly required to finish, and write your",
|
|
12014
|
+
"final answer or report as your final output, then end your turn.",
|
|
12015
|
+
"Do not restart the task and do not begin new work."
|
|
12016
|
+
].join("\n");
|
|
12017
|
+
}
|
|
12018
|
+
|
|
11976
12019
|
// src/coordination/subagent-budget.ts
|
|
11977
12020
|
var TIMEOUT_PREEMPT_FRACTION = 0.85;
|
|
11978
12021
|
var DECISION_TIMEOUT_MS = 6e4;
|
|
@@ -12030,6 +12073,82 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
12030
12073
|
this.limits.idleTimeoutMs = ext.idleTimeoutMs;
|
|
12031
12074
|
}
|
|
12032
12075
|
}
|
|
12076
|
+
/**
|
|
12077
|
+
* Graceful-finish state (see coordination/subagent-finish.ts).
|
|
12078
|
+
* `_finishNotified` guards the single in-band emission; `_grace` records a
|
|
12079
|
+
* granted working-time extension past the original wall-clock deadline.
|
|
12080
|
+
* They are separate because the two callers want different semantics:
|
|
12081
|
+
* the watchdog grants grace at the deadline crossing (notify + extend),
|
|
12082
|
+
* while an explicit leader-finished request only notifies — a subagent
|
|
12083
|
+
* well inside its budget keeps its full legitimate working time and simply
|
|
12084
|
+
* accelerates.
|
|
12085
|
+
*/
|
|
12086
|
+
_finishNotified = false;
|
|
12087
|
+
_grace = null;
|
|
12088
|
+
/** True once the in-band finish notification has been emitted. */
|
|
12089
|
+
get finishNotified() {
|
|
12090
|
+
return this._finishNotified;
|
|
12091
|
+
}
|
|
12092
|
+
/** True once a grace window has been granted past the original deadline. */
|
|
12093
|
+
get graceGranted() {
|
|
12094
|
+
return this._grace !== null;
|
|
12095
|
+
}
|
|
12096
|
+
/**
|
|
12097
|
+
* Notify the subagent in-band to finish its task in its own turn:
|
|
12098
|
+
* `subagent.finish_requested` is emitted on the wired EventBus and the
|
|
12099
|
+
* agent loop folds the notice into the conversation between tool batches.
|
|
12100
|
+
* Nothing aborts — this is a notification, never an interrupt.
|
|
12101
|
+
*
|
|
12102
|
+
* `opts.graceMs` additionally extends the wall-clock ceiling by that window
|
|
12103
|
+
* (used by the watchdog at a deadline crossing, so the model gets working
|
|
12104
|
+
* time instead of a kill). Omit it to notify without touching the budget —
|
|
12105
|
+
* the subagent keeps its existing time budget and just accelerates.
|
|
12106
|
+
*
|
|
12107
|
+
* Returns `true` when this call did something (emitted the notification
|
|
12108
|
+
* and/or granted grace); `false` when there was nothing to do (already
|
|
12109
|
+
* notified, grace already granted, no EventBus wired, budget not started).
|
|
12110
|
+
*/
|
|
12111
|
+
notifyFinish(reason, opts, now = Date.now) {
|
|
12112
|
+
if (!this._events) return false;
|
|
12113
|
+
if (this.startTime === null) return false;
|
|
12114
|
+
const shouldEmit = !this._finishNotified;
|
|
12115
|
+
const rawGrace = opts?.graceMs;
|
|
12116
|
+
const shouldGrant = rawGrace !== void 0 && this._grace === null;
|
|
12117
|
+
if (!shouldEmit && !shouldGrant) return false;
|
|
12118
|
+
let grantedGraceMs = 0;
|
|
12119
|
+
let graceDeadlineMs;
|
|
12120
|
+
if (shouldGrant && rawGrace !== void 0) {
|
|
12121
|
+
grantedGraceMs = Number.isFinite(rawGrace) && rawGrace > 0 ? Math.floor(rawGrace) : DEFAULT_SUBAGENT_FINISH_GRACE_MS;
|
|
12122
|
+
graceDeadlineMs = now() + grantedGraceMs;
|
|
12123
|
+
this._grace = { deadlineMs: graceDeadlineMs, graceMs: grantedGraceMs };
|
|
12124
|
+
this.patchLimits({ timeoutMs: graceDeadlineMs - this.startTime });
|
|
12125
|
+
}
|
|
12126
|
+
if (shouldEmit) {
|
|
12127
|
+
this._finishNotified = true;
|
|
12128
|
+
const effectiveDeadlineMs = graceDeadlineMs ?? (this.limits.timeoutMs !== void 0 ? this.startTime + this.limits.timeoutMs : now() + DEFAULT_SUBAGENT_FINISH_GRACE_MS);
|
|
12129
|
+
const effectiveGraceMs = Math.max(0, effectiveDeadlineMs - now());
|
|
12130
|
+
const subagentId = this._subagentId;
|
|
12131
|
+
this._events.emit(SUBAGENT_FINISH_REQUESTED_EVENT, {
|
|
12132
|
+
// Omitted entirely when the budget was built without an id — an
|
|
12133
|
+
// empty string is an address that matches nothing.
|
|
12134
|
+
...subagentId !== void 0 ? { subagentId } : {},
|
|
12135
|
+
reason,
|
|
12136
|
+
deadlineMs: effectiveDeadlineMs,
|
|
12137
|
+
graceMs: effectiveGraceMs,
|
|
12138
|
+
notice: buildSubagentFinishNotice({
|
|
12139
|
+
reason,
|
|
12140
|
+
deadlineMs: effectiveDeadlineMs,
|
|
12141
|
+
graceMs: effectiveGraceMs
|
|
12142
|
+
})
|
|
12143
|
+
});
|
|
12144
|
+
}
|
|
12145
|
+
return true;
|
|
12146
|
+
}
|
|
12147
|
+
/** Epoch ms by which the subagent should have produced its final output,
|
|
12148
|
+
* once a grace window was granted. Undefined before that. */
|
|
12149
|
+
get finishDeadlineMs() {
|
|
12150
|
+
return this._grace?.deadlineMs;
|
|
12151
|
+
}
|
|
12033
12152
|
iterations = 0;
|
|
12034
12153
|
toolCalls = 0;
|
|
12035
12154
|
tokenInput = 0;
|
|
@@ -12045,6 +12164,10 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
12045
12164
|
lastActivityTime = null;
|
|
12046
12165
|
_onThreshold;
|
|
12047
12166
|
_sessionId;
|
|
12167
|
+
/** Owning subagent id — used to address the graceful-finish event. */
|
|
12168
|
+
_subagentId;
|
|
12169
|
+
/** True when only the coordinator watchdog may enforce wall-clock limits. */
|
|
12170
|
+
_wallClockWatchdogOwned;
|
|
12048
12171
|
/**
|
|
12049
12172
|
* Hard cap on how long `_negotiateExtension` waits for the coordinator to
|
|
12050
12173
|
* respond before defaulting to 'stop'. Without this fallback an absent
|
|
@@ -12116,6 +12239,8 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
12116
12239
|
constructor(limits = {}, mode = "auto", options = {}) {
|
|
12117
12240
|
this._mode = mode;
|
|
12118
12241
|
this._sessionId = options.sessionId;
|
|
12242
|
+
this._subagentId = options.subagentId;
|
|
12243
|
+
this._wallClockWatchdogOwned = options.wallClockWatchdogOwned === true;
|
|
12119
12244
|
this.limits = { ...limits };
|
|
12120
12245
|
}
|
|
12121
12246
|
currentSessionId() {
|
|
@@ -12194,7 +12319,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
12194
12319
|
if (this.limits.idleTimeoutMs !== void 0 && idle > this.limits.idleTimeoutMs) {
|
|
12195
12320
|
exceeded.push({ kind: "idle_timeout", used: idle, limit: this.limits.idleTimeoutMs });
|
|
12196
12321
|
}
|
|
12197
|
-
const wallOwnedByWatchdog = this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
12322
|
+
const wallOwnedByWatchdog = this._wallClockWatchdogOwned || this._onThreshold !== void 0 && this._watchdogActive === this.limits.timeoutMs;
|
|
12198
12323
|
if (this.limits.timeoutMs !== void 0 && elapsedMs > this.limits.timeoutMs && !wallOwnedByWatchdog) {
|
|
12199
12324
|
exceeded.push({ kind: "timeout", used: elapsedMs, limit: this.limits.timeoutMs });
|
|
12200
12325
|
}
|
|
@@ -12393,7 +12518,7 @@ var SubagentBudget = class _SubagentBudget {
|
|
|
12393
12518
|
if (timeoutMs === void 0 && idleTimeoutMs === void 0) return;
|
|
12394
12519
|
const elapsed = Date.now() - this.startTime;
|
|
12395
12520
|
const wallSkipped = this._onThreshold !== void 0 && this._watchdogActive !== void 0 && timeoutMs !== void 0 && this._watchdogActive === timeoutMs;
|
|
12396
|
-
const wallTripped = wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
12521
|
+
const wallTripped = this._wallClockWatchdogOwned || wallSkipped ? false : timeoutMs !== void 0 && elapsed > timeoutMs;
|
|
12397
12522
|
const idleTripped = idleTimeoutMs !== void 0 && this.idleMs() > idleTimeoutMs;
|
|
12398
12523
|
if (!wallTripped && !idleTripped) return;
|
|
12399
12524
|
void this.checkLimits(elapsed);
|
|
@@ -12769,6 +12894,14 @@ function makeAgentSubagentRunner(opts) {
|
|
|
12769
12894
|
);
|
|
12770
12895
|
const onParentAbort = () => aborter.abort();
|
|
12771
12896
|
ctx.signal.addEventListener("abort", onParentAbort);
|
|
12897
|
+
if (resolveGracefulFinish(ctx.config)) {
|
|
12898
|
+
unsub.push(
|
|
12899
|
+
events.on("subagent.finish_requested", (e) => {
|
|
12900
|
+
if (e.subagentId && e.subagentId !== ctx.subagentId) return;
|
|
12901
|
+
setBtwNote(agent.ctx, e.notice);
|
|
12902
|
+
})
|
|
12903
|
+
);
|
|
12904
|
+
}
|
|
12772
12905
|
let result;
|
|
12773
12906
|
try {
|
|
12774
12907
|
result = await agent.run(format(task, ctx.config), { signal: aborter.signal });
|
|
@@ -13481,6 +13614,7 @@ function inferRuntimeCapabilities(toolNames) {
|
|
|
13481
13614
|
var skillSet = (...names) => names;
|
|
13482
13615
|
var ROLE_SKILL_SETS = {
|
|
13483
13616
|
explore: skillSet("research-web", "node-modern", "typescript-strict"),
|
|
13617
|
+
"explore-companion": skillSet("node-modern", "typescript-strict"),
|
|
13484
13618
|
search: skillSet("bug-hunter", "typescript-strict", "research-web"),
|
|
13485
13619
|
research: skillSet("research-web", "tech-stack", "security-scanner", "api-design"),
|
|
13486
13620
|
analyst: skillSet("sdd", "api-design", "testing", "security-scanner"),
|
|
@@ -16937,6 +17071,42 @@ var SHADOW_AGENT = {
|
|
|
16937
17071
|
...defineAgent("shadow-agent", "Shadow"),
|
|
16938
17072
|
skillNames: [...SHADOW_AGENT_SKILLS]
|
|
16939
17073
|
};
|
|
17074
|
+
var EXPLORE_COMPANION_AGENT = {
|
|
17075
|
+
...defineAgent("explore-companion", "Explore Companion"),
|
|
17076
|
+
tools: [...TOOLS.read, ...TOOLS.index],
|
|
17077
|
+
// Read-only, triple-enforced: allowlist has no write/bash, and the
|
|
17078
|
+
// disabled list blocks the escape hatches explicitly.
|
|
17079
|
+
disabledTools: [
|
|
17080
|
+
"write",
|
|
17081
|
+
"edit",
|
|
17082
|
+
"replace",
|
|
17083
|
+
"patch",
|
|
17084
|
+
"bash",
|
|
17085
|
+
"exec",
|
|
17086
|
+
"delegate",
|
|
17087
|
+
"spawn_subagent",
|
|
17088
|
+
"assign_task"
|
|
17089
|
+
],
|
|
17090
|
+
skillNames: [...ROLE_SKILL_SETS["explore-companion"]],
|
|
17091
|
+
spawnBudgetExempt: true,
|
|
17092
|
+
// Findings travel via mailbox + submit_result, not the leader's stream.
|
|
17093
|
+
textStream: "silent",
|
|
17094
|
+
toolStream: "silent"
|
|
17095
|
+
};
|
|
17096
|
+
var CHAOS_MONKEY_AGENT = {
|
|
17097
|
+
...defineAgent("chaos-monkey", "Chaos Monkey"),
|
|
17098
|
+
tools: [...TOOLS.build],
|
|
17099
|
+
skillNames: ["testing", "typescript-strict"],
|
|
17100
|
+
spawnBudgetExempt: true,
|
|
17101
|
+
// Follow fleet worktree policy (NOT 'required'): mutation targets are
|
|
17102
|
+
// often freshly written and uncommitted — a worktree spawned from HEAD
|
|
17103
|
+
// would not contain them and every mutant would drift. Callers pass
|
|
17104
|
+
// `worktree: 'off'` in the mutation_test input for uncommitted targets.
|
|
17105
|
+
worktree: "auto",
|
|
17106
|
+
// Report travels via submit_result + final text, not the leader's stream.
|
|
17107
|
+
textStream: "silent",
|
|
17108
|
+
toolStream: "silent"
|
|
17109
|
+
};
|
|
16940
17110
|
var CRITIC_AGENT = defineAgent("critic", "Critic");
|
|
16941
17111
|
var GENERIC_AGENT = defineAgent("generic", "Generic Project Agent");
|
|
16942
17112
|
function withDispatchMetadata(definition) {
|
|
@@ -16955,6 +17125,8 @@ var FLEET_ROSTER = {
|
|
|
16955
17125
|
critic: CRITIC_AGENT,
|
|
16956
17126
|
generic: GENERIC_AGENT,
|
|
16957
17127
|
"shadow-agent": SHADOW_AGENT,
|
|
17128
|
+
"explore-companion": EXPLORE_COMPANION_AGENT,
|
|
17129
|
+
"chaos-monkey": CHAOS_MONKEY_AGENT,
|
|
16958
17130
|
...Object.fromEntries(
|
|
16959
17131
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, withDispatchMetadata(d)])
|
|
16960
17132
|
)
|
|
@@ -16978,6 +17150,23 @@ var FLEET_ROSTER_BUDGETS = {
|
|
|
16978
17150
|
maxTokens: 96e3,
|
|
16979
17151
|
maxCostUsd: 0.5
|
|
16980
17152
|
},
|
|
17153
|
+
"explore-companion": {
|
|
17154
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
17155
|
+
maxIterations: 3e3,
|
|
17156
|
+
maxToolCalls: 8e3,
|
|
17157
|
+
maxTokens: 96e3,
|
|
17158
|
+
maxCostUsd: 0.5
|
|
17159
|
+
},
|
|
17160
|
+
"chaos-monkey": {
|
|
17161
|
+
// A mutation pass is many short apply/run/restore cycles — per-mutant
|
|
17162
|
+
// work is tiny, but a large plan (25 mutants/file × N files) needs
|
|
17163
|
+
// headroom. Idle-based reaping covers a stalled pass.
|
|
17164
|
+
idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS,
|
|
17165
|
+
maxIterations: 2e3,
|
|
17166
|
+
maxToolCalls: 6e3,
|
|
17167
|
+
maxTokens: 96e3,
|
|
17168
|
+
maxCostUsd: 0.5
|
|
17169
|
+
},
|
|
16981
17170
|
...Object.fromEntries(
|
|
16982
17171
|
ALL_AGENT_DEFINITIONS.map((d) => [d.config.role, d.budget])
|
|
16983
17172
|
)
|
|
@@ -17147,7 +17336,8 @@ async function executeSubagentWithTimeout({
|
|
|
17147
17336
|
budget,
|
|
17148
17337
|
preemptFraction = TIMEOUT_PREEMPT_FRACTION,
|
|
17149
17338
|
abortSubagent,
|
|
17150
|
-
currentSessionId
|
|
17339
|
+
currentSessionId,
|
|
17340
|
+
gracefulFinish
|
|
17151
17341
|
}) {
|
|
17152
17342
|
const initialTimeoutMs = budget.limits.timeoutMs;
|
|
17153
17343
|
const idleLimitMs = budget.limits.idleTimeoutMs;
|
|
@@ -17178,9 +17368,17 @@ async function executeSubagentWithTimeout({
|
|
|
17178
17368
|
const scheduleNext = () => {
|
|
17179
17369
|
const wallLimit = budget.limits.timeoutMs ?? initialTimeoutMs;
|
|
17180
17370
|
const wallRemaining = initialTimeoutMs === void 0 ? Number.POSITIVE_INFINITY : wallLimit - (Date.now() - start);
|
|
17181
|
-
const idleRemaining = idleLimitMs === void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
17182
|
-
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
17183
|
-
|
|
17371
|
+
const idleRemaining = idleLimitMs === void 0 || gracefulFinish !== void 0 && initialTimeoutMs !== void 0 ? Number.POSITIVE_INFINITY : (budget.limits.idleTimeoutMs ?? idleLimitMs) - budget.idleMs();
|
|
17372
|
+
const preemptRemaining = initialTimeoutMs === void 0 || preemptedCeiling === wallLimit || gracefulFinish !== void 0 ? Number.POSITIVE_INFINITY : wallLimit * preemptFraction - (Date.now() - start);
|
|
17373
|
+
const next = Math.min(wallRemaining, idleRemaining, preemptRemaining);
|
|
17374
|
+
if (!Number.isFinite(next)) {
|
|
17375
|
+
if (timer) {
|
|
17376
|
+
clearTimeout(timer);
|
|
17377
|
+
timer = null;
|
|
17378
|
+
}
|
|
17379
|
+
return;
|
|
17380
|
+
}
|
|
17381
|
+
armFor(Math.max(25, next));
|
|
17184
17382
|
};
|
|
17185
17383
|
const negotiateTimeout = async (used, limit) => {
|
|
17186
17384
|
const handler = budget.onThreshold;
|
|
@@ -17229,6 +17427,10 @@ async function executeSubagentWithTimeout({
|
|
|
17229
17427
|
const wallExceeded = wallLimit !== void 0 && elapsed >= wallLimit;
|
|
17230
17428
|
const idleExceeded = idleLimit !== void 0 && budget.idleMs() >= idleLimit;
|
|
17231
17429
|
if (idleExceeded && !wallExceeded) {
|
|
17430
|
+
if (gracefulFinish !== void 0 && initialTimeoutMs !== void 0) {
|
|
17431
|
+
scheduleNext();
|
|
17432
|
+
return;
|
|
17433
|
+
}
|
|
17232
17434
|
const sessionId = currentSessionId();
|
|
17233
17435
|
budget._events?.emit("budget.threshold_reached", {
|
|
17234
17436
|
...sessionId ? { sessionId } : {},
|
|
@@ -17245,7 +17447,7 @@ async function executeSubagentWithTimeout({
|
|
|
17245
17447
|
reject(new BudgetExceededError("idle_timeout", idleLimit ?? 0, budget.idleMs()));
|
|
17246
17448
|
return;
|
|
17247
17449
|
}
|
|
17248
|
-
if (wallLimit !== void 0 && !wallExceeded && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
17450
|
+
if (wallLimit !== void 0 && !wallExceeded && gracefulFinish === void 0 && budget.onThreshold && preemptState === "active" /* ACTIVE */ && elapsed >= wallLimit * preemptFraction) {
|
|
17249
17451
|
const activityTs = Date.now() - budget.idleMs();
|
|
17250
17452
|
if (activityTs <= lastGrantActivityTs) {
|
|
17251
17453
|
preemptState = "locked" /* LOCKED */;
|
|
@@ -17279,6 +17481,22 @@ async function executeSubagentWithTimeout({
|
|
|
17279
17481
|
return;
|
|
17280
17482
|
}
|
|
17281
17483
|
const limit = wallLimit ?? 0;
|
|
17484
|
+
if (gracefulFinish !== void 0) {
|
|
17485
|
+
if (!budget.graceGranted) {
|
|
17486
|
+
const reason = `wall-clock budget of ${Math.round(limit / 1e3)}s reached`;
|
|
17487
|
+
if (budget.notifyFinish(reason, { graceMs: gracefulFinish.graceMs })) {
|
|
17488
|
+
scheduleNext();
|
|
17489
|
+
return;
|
|
17490
|
+
}
|
|
17491
|
+
abortSubagent(ctx.subagentId);
|
|
17492
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
17493
|
+
return;
|
|
17494
|
+
} else {
|
|
17495
|
+
abortSubagent(ctx.subagentId);
|
|
17496
|
+
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
17497
|
+
return;
|
|
17498
|
+
}
|
|
17499
|
+
}
|
|
17282
17500
|
if (!budget.onThreshold) {
|
|
17283
17501
|
abortSubagent(ctx.subagentId);
|
|
17284
17502
|
reject(new BudgetExceededError("timeout", limit, elapsed));
|
|
@@ -17663,6 +17881,32 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
17663
17881
|
completeTask(result) {
|
|
17664
17882
|
this.recordCompletion(result);
|
|
17665
17883
|
}
|
|
17884
|
+
/**
|
|
17885
|
+
* Ask every RUNNING subagent that opted into `gracefulFinish` to finish its
|
|
17886
|
+
* task in its own turn (see coordination/subagent-finish.ts). This is the
|
|
17887
|
+
* leader-side entry point for "the leader agent has finished": it delivers
|
|
17888
|
+
* an in-band notification between tool batches — never an interrupt, never
|
|
17889
|
+
* an abort. Each notified subagent keeps its existing time budget and
|
|
17890
|
+
* accelerates; the watchdog still bounds the maximum lifetime.
|
|
17891
|
+
*
|
|
17892
|
+
* Subagents without the policy opted in are deliberately untouched — their
|
|
17893
|
+
* lifecycle remains the legacy watchdog contract.
|
|
17894
|
+
*
|
|
17895
|
+
* Returns the number of subagents actually notified.
|
|
17896
|
+
*/
|
|
17897
|
+
requestFinish(reason) {
|
|
17898
|
+
let notified = 0;
|
|
17899
|
+
for (const subagent of this.subagents.values()) {
|
|
17900
|
+
if (subagent.status !== "running") continue;
|
|
17901
|
+
if (!resolveGracefulFinish(subagent.config)) continue;
|
|
17902
|
+
const budget = subagent.activeBudget;
|
|
17903
|
+
if (!budget) continue;
|
|
17904
|
+
const usage = budget.usage();
|
|
17905
|
+
if (usage.iterations === 0 && usage.toolCalls === 0) continue;
|
|
17906
|
+
if (budget.notifyFinish(reason)) notified++;
|
|
17907
|
+
}
|
|
17908
|
+
return notified;
|
|
17909
|
+
}
|
|
17666
17910
|
// --- internal dispatching ---------------------------------------------
|
|
17667
17911
|
tryDispatchNext() {
|
|
17668
17912
|
while (this.canDispatch()) {
|
|
@@ -17836,7 +18080,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
17836
18080
|
idleTimeoutMs: rawIdleTimeoutMs ?? this.config.defaultBudget?.idleTimeoutMs ?? configWithRosterDefaults.idleTimeoutMs
|
|
17837
18081
|
},
|
|
17838
18082
|
"auto",
|
|
17839
|
-
{
|
|
18083
|
+
{
|
|
18084
|
+
sessionId: () => this.currentSessionId(),
|
|
18085
|
+
subagentId,
|
|
18086
|
+
// Graceful-finish runs own wall-clock enforcement to the watchdog so
|
|
18087
|
+
// the notify-then-bound lifecycle cannot be raced by tool.progress
|
|
18088
|
+
// heartbeats calling checkTimeout() (see subagent-budget.ts).
|
|
18089
|
+
...resolveGracefulFinish(subagent.config) ? { wallClockWatchdogOwned: true } : {}
|
|
18090
|
+
}
|
|
17840
18091
|
);
|
|
17841
18092
|
subagent.activeBudget = budget;
|
|
17842
18093
|
if (!this.runner) {
|
|
@@ -17869,7 +18120,8 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
17869
18120
|
task,
|
|
17870
18121
|
runCtx,
|
|
17871
18122
|
budget,
|
|
17872
|
-
subagent.config.preemptFraction
|
|
18123
|
+
subagent.config.preemptFraction,
|
|
18124
|
+
resolveGracefulFinish(subagent.config)
|
|
17873
18125
|
);
|
|
17874
18126
|
result = {
|
|
17875
18127
|
subagentId,
|
|
@@ -17899,13 +18151,14 @@ var DefaultMultiAgentCoordinator = class _DefaultMultiAgentCoordinator extends E
|
|
|
17899
18151
|
}
|
|
17900
18152
|
this.recordCompletion(result);
|
|
17901
18153
|
}
|
|
17902
|
-
async executeWithTimeout(runner, task, ctx, budget, preemptFraction) {
|
|
18154
|
+
async executeWithTimeout(runner, task, ctx, budget, preemptFraction, gracefulFinish) {
|
|
17903
18155
|
return executeSubagentWithTimeout({
|
|
17904
18156
|
runner,
|
|
17905
18157
|
task,
|
|
17906
18158
|
ctx,
|
|
17907
18159
|
budget,
|
|
17908
18160
|
preemptFraction,
|
|
18161
|
+
gracefulFinish,
|
|
17909
18162
|
abortSubagent: (subagentId) => this.subagents.get(subagentId)?.abortController.abort(),
|
|
17910
18163
|
currentSessionId: () => this.currentSessionId()
|
|
17911
18164
|
});
|
package/dist/goal/index.js
CHANGED
|
@@ -1427,6 +1427,13 @@ var PhaseOrchestrator = class {
|
|
|
1427
1427
|
events;
|
|
1428
1428
|
stopped = false;
|
|
1429
1429
|
paused = false;
|
|
1430
|
+
/**
|
|
1431
|
+
* Run-wide abort source. stop() aborts it; every in-flight
|
|
1432
|
+
* ctx.executeTask call observes the abort through its per-task signal
|
|
1433
|
+
* (composed from this controller and the task's own timeout controller).
|
|
1434
|
+
* Recreated by start() so a stopped orchestrator can be reused.
|
|
1435
|
+
*/
|
|
1436
|
+
stopController = new AbortController();
|
|
1430
1437
|
runningPhases = /* @__PURE__ */ new Set();
|
|
1431
1438
|
tickInterval = null;
|
|
1432
1439
|
trackerCache = /* @__PURE__ */ new Map();
|
|
@@ -1469,6 +1476,7 @@ var PhaseOrchestrator = class {
|
|
|
1469
1476
|
async start() {
|
|
1470
1477
|
this.stopped = false;
|
|
1471
1478
|
this.paused = false;
|
|
1479
|
+
this.stopController = new AbortController();
|
|
1472
1480
|
this.normalizeForResume();
|
|
1473
1481
|
this.graph.startedAt = Date.now();
|
|
1474
1482
|
this.graph.updatedAt = Date.now();
|
|
@@ -1528,6 +1536,7 @@ var PhaseOrchestrator = class {
|
|
|
1528
1536
|
/** Stop completely, including active phases. */
|
|
1529
1537
|
stop() {
|
|
1530
1538
|
this.stopped = true;
|
|
1539
|
+
this.stopController.abort();
|
|
1531
1540
|
if (this.tickInterval) {
|
|
1532
1541
|
clearInterval(this.tickInterval);
|
|
1533
1542
|
this.tickInterval = null;
|
|
@@ -1604,6 +1613,7 @@ var PhaseOrchestrator = class {
|
|
|
1604
1613
|
return;
|
|
1605
1614
|
}
|
|
1606
1615
|
await this.executePhaseTasks(phase);
|
|
1616
|
+
if (this.stopped) return;
|
|
1607
1617
|
const failedTasks = this.getFailedTaskCount(phase);
|
|
1608
1618
|
const completedTasks = this.getCompletedTaskCount(phase);
|
|
1609
1619
|
this.emit("phase.allTasksDone", {
|
|
@@ -1763,34 +1773,47 @@ var PhaseOrchestrator = class {
|
|
|
1763
1773
|
agentName: task.assignee
|
|
1764
1774
|
});
|
|
1765
1775
|
const handle = this.phaseWorktrees.get(phase.id);
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1776
|
+
const timeoutController = this.opts.taskTimeoutMs > 0 ? new AbortController() : void 0;
|
|
1777
|
+
const signal = timeoutController ? AbortSignal.any([this.stopController.signal, timeoutController.signal]) : this.stopController.signal;
|
|
1778
|
+
const taskPromise = this.ctx.executeTask(
|
|
1779
|
+
task,
|
|
1780
|
+
phase.id,
|
|
1781
|
+
{ cwd: handle?.dir, branch: handle?.branch },
|
|
1782
|
+
signal
|
|
1783
|
+
);
|
|
1784
|
+
if (!timeoutController) return taskPromise;
|
|
1785
|
+
const timeoutMs = this.opts.taskTimeoutMs;
|
|
1786
|
+
const timedOut = /* @__PURE__ */ Symbol("timed_out");
|
|
1787
|
+
const result = await Promise.race([
|
|
1788
|
+
taskPromise,
|
|
1789
|
+
new Promise((resolve4) => {
|
|
1790
|
+
const timer = setTimeout(() => {
|
|
1791
|
+
timeoutController.abort();
|
|
1792
|
+
resolve4(timedOut);
|
|
1793
|
+
}, timeoutMs);
|
|
1794
|
+
taskPromise.then(() => clearTimeout(timer)).catch(() => clearTimeout(timer));
|
|
1795
|
+
})
|
|
1796
|
+
]);
|
|
1797
|
+
if (result !== timedOut) return result;
|
|
1798
|
+
this.emit("phase.taskTimedOut", {
|
|
1799
|
+
phaseId: phase.id,
|
|
1800
|
+
taskId: task.id,
|
|
1801
|
+
taskTitle: task.title,
|
|
1802
|
+
timeoutMs
|
|
1769
1803
|
});
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
taskId: task.id,
|
|
1784
|
-
taskTitle: task.title,
|
|
1785
|
-
timeoutMs
|
|
1786
|
-
});
|
|
1787
|
-
throw new Error(
|
|
1788
|
-
`Task "${task.title}" (${task.id}) exceeded timeout of ${timeoutMs} ms`
|
|
1789
|
-
);
|
|
1790
|
-
}
|
|
1791
|
-
return result;
|
|
1792
|
-
}
|
|
1793
|
-
return taskPromise;
|
|
1804
|
+
const settled = taskPromise.then(
|
|
1805
|
+
() => void 0,
|
|
1806
|
+
() => void 0
|
|
1807
|
+
);
|
|
1808
|
+
const grace = new Promise((resolve4) => {
|
|
1809
|
+
const timer = setTimeout(resolve4, 5e3);
|
|
1810
|
+
timer.unref?.();
|
|
1811
|
+
void settled.then(() => clearTimeout(timer));
|
|
1812
|
+
});
|
|
1813
|
+
await Promise.race([settled, grace]);
|
|
1814
|
+
throw new Error(
|
|
1815
|
+
`Task "${task.title}" (${task.id}) exceeded timeout of ${timeoutMs} ms`
|
|
1816
|
+
);
|
|
1794
1817
|
}
|
|
1795
1818
|
markTaskCompleted(phase, task) {
|
|
1796
1819
|
const tracker = this.getTrackerForPhase(phase);
|
|
@@ -1805,6 +1828,10 @@ var PhaseOrchestrator = class {
|
|
|
1805
1828
|
const tracker = this.getTrackerForPhase(phase);
|
|
1806
1829
|
const taskKey = `${phase.id}:${task.id}`;
|
|
1807
1830
|
const currentRetries = this.taskRetryCounts.get(taskKey) ?? 0;
|
|
1831
|
+
if (this.stopped) {
|
|
1832
|
+
tracker.updateNodeStatus(task.id, "pending", "Stopped before completion");
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1808
1835
|
if (currentRetries < this.opts.maxRetries) {
|
|
1809
1836
|
this.taskRetryCounts.set(taskKey, currentRetries + 1);
|
|
1810
1837
|
tracker.updateNodeStatus(
|
|
@@ -19,6 +19,13 @@ export declare class PhaseOrchestrator {
|
|
|
19
19
|
private events;
|
|
20
20
|
private stopped;
|
|
21
21
|
private paused;
|
|
22
|
+
/**
|
|
23
|
+
* Run-wide abort source. stop() aborts it; every in-flight
|
|
24
|
+
* ctx.executeTask call observes the abort through its per-task signal
|
|
25
|
+
* (composed from this controller and the task's own timeout controller).
|
|
26
|
+
* Recreated by start() so a stopped orchestrator can be reused.
|
|
27
|
+
*/
|
|
28
|
+
private stopController;
|
|
22
29
|
private runningPhases;
|
|
23
30
|
private tickInterval;
|
|
24
31
|
private trackerCache;
|
package/dist/goal/types.d.ts
CHANGED
|
@@ -223,7 +223,7 @@ export interface PhaseExecutionContext {
|
|
|
223
223
|
executeTask: (task: TaskNode, phaseId: string, env?: {
|
|
224
224
|
cwd?: string | undefined;
|
|
225
225
|
branch?: string | undefined;
|
|
226
|
-
}) => Promise<unknown>;
|
|
226
|
+
}, signal?: AbortSignal | undefined) => Promise<unknown>;
|
|
227
227
|
/**
|
|
228
228
|
* Optional verification gate. Called after all tasks in a phase finish,
|
|
229
229
|
* but before the phase is marked "completed" and its worktree is merged
|
package/dist/index.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export { DEPENDENCY_FILE_PATTERNS, type DependencyWatcherConfig, type DepWatchEn
|
|
|
16
16
|
export { attachDepWatcherBridge, type DepWatcherBridgeOptions, } from './coordination/dep-watcher-bridge.js';
|
|
17
17
|
export { Director, FleetCostCapError, FleetSpawnBudgetError, FleetTokenCapError, type TaskResultNotification, } from './coordination/director.js';
|
|
18
18
|
export { type DirectorSessionFactory, type DirectorSessionFactoryOptions, makeDirectorSessionFactory, } from './coordination/director-session.js';
|
|
19
|
-
export { makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeFleetEmitTool, makeFleetTool, makeKanbanQueueTool, makeQualityGateTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, } from './coordination/director-tools.js';
|
|
19
|
+
export { makeAskTool, makeAssignTool, makeAwaitTasksTool, makeCollabDebugTool, makeFleetEmitTool, makeFleetTool, makeKanbanQueueTool, makeMutationTestTool, makeQualityGateTool, makeRollUpTool, makeSpawnTool, makeTerminateTool, } from './coordination/director-tools.js';
|
|
20
20
|
export { DEFAULT_DISPATCH_ROLE, type DispatchCandidate, type DispatchClassifier, type DispatchMethod, type DispatchOptions, type DispatchResult, dispatchAgent, makeLLMClassifier, scoreAgents, } from './coordination/dispatcher.js';
|
|
21
21
|
export { compactLog, type FileAuthorEntry, type FileAuthorLog, type FileAuthorTrackerOptions, getFileHistory, getFilesByAgent, getFullLog, getLastAuthor, recordFileAction, } from './coordination/file-author-tracker.js';
|
|
22
22
|
export { ACP_AGENTS, ALL_FLEET_AGENTS, applyRosterBudget, FLEET_ROSTER, FLEET_ROSTER_BUDGETS, FLEET_ROSTER_WITHACP, type FleetRosterBudget, } from './coordination/fleet.js';
|