@wrongstack/core 0.289.0 → 0.291.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/boot.d.ts.map +1 -1
- package/dist/chronicle/domain-adapter.d.ts.map +1 -1
- package/dist/chronicle/index.d.ts +1 -1
- package/dist/chronicle/index.d.ts.map +1 -1
- package/dist/chronicle/index.js +964 -245
- package/dist/chronicle/index.js.map +3 -3
- package/dist/chronicle/journal.d.ts +35 -3
- package/dist/chronicle/journal.d.ts.map +1 -1
- package/dist/chronicle/query.d.ts +8 -5
- package/dist/chronicle/query.d.ts.map +1 -1
- package/dist/chronicle/tool-adapter.d.ts.map +1 -1
- package/dist/coordination/agent-subagent-runner.d.ts +6 -0
- package/dist/coordination/agent-subagent-runner.d.ts.map +1 -1
- package/dist/coordination/agents/phase6-domain.d.ts.map +1 -1
- package/dist/coordination/brain.d.ts +2 -1
- package/dist/coordination/brain.d.ts.map +1 -1
- package/dist/coordination/collab-debug.d.ts +0 -9
- package/dist/coordination/collab-debug.d.ts.map +1 -1
- package/dist/coordination/coordinator/error-classifier.d.ts.map +1 -1
- package/dist/coordination/dep-watcher.d.ts +2 -1
- package/dist/coordination/dep-watcher.d.ts.map +1 -1
- package/dist/coordination/director/director-collab.d.ts +1 -1
- package/dist/coordination/director/director-collab.d.ts.map +1 -1
- package/dist/coordination/director-tools.d.ts.map +1 -1
- package/dist/coordination/director.d.ts +19 -1
- package/dist/coordination/director.d.ts.map +1 -1
- package/dist/coordination/fleet-event-validation.d.ts +0 -7
- package/dist/coordination/fleet-event-validation.d.ts.map +1 -1
- package/dist/coordination/fleet-spawn.d.ts.map +1 -1
- package/dist/coordination/fleet-supervisor.d.ts +1 -1
- package/dist/coordination/fleet-supervisor.d.ts.map +1 -1
- package/dist/coordination/fleet.d.ts +0 -44
- package/dist/coordination/fleet.d.ts.map +1 -1
- package/dist/coordination/index.d.ts +2 -2
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +733 -111
- package/dist/coordination/index.js.map +4 -4
- package/dist/coordination/knowledge-graph.d.ts +2 -1
- package/dist/coordination/knowledge-graph.d.ts.map +1 -1
- package/dist/coordination/large-answer-store.d.ts +0 -6
- package/dist/coordination/large-answer-store.d.ts.map +1 -1
- package/dist/coordination/mail-tools.d.ts.map +1 -1
- package/dist/coordination/mailbox-http-router.d.ts.map +1 -1
- package/dist/coordination/mailbox-message-codec.d.ts +35 -0
- package/dist/coordination/mailbox-message-codec.d.ts.map +1 -1
- package/dist/coordination/mailbox-tool.d.ts +1 -1
- package/dist/coordination/mailbox-tool.d.ts.map +1 -1
- package/dist/coordination/mailbox-types.d.ts +151 -0
- package/dist/coordination/mailbox-types.d.ts.map +1 -1
- package/dist/coordination/provider-status-tracker.d.ts +27 -2
- package/dist/coordination/provider-status-tracker.d.ts.map +1 -1
- package/dist/coordination/subagent-budget.d.ts +2 -1
- package/dist/coordination/subagent-budget.d.ts.map +1 -1
- package/dist/core/agent-response.d.ts +2 -1
- package/dist/core/agent-response.d.ts.map +1 -1
- package/dist/core/agent-tools.d.ts.map +1 -1
- package/dist/core/context.d.ts +51 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/fallback-model.d.ts +1 -1
- package/dist/core/fallback-model.d.ts.map +1 -1
- package/dist/core/fallback-profile-manager.d.ts +1 -1
- package/dist/core/fallback-profile-manager.d.ts.map +1 -1
- package/dist/core/fleet-pulse.d.ts +2 -1
- package/dist/core/fleet-pulse.d.ts.map +1 -1
- package/dist/core/iteration-limit.d.ts +2 -1
- package/dist/core/iteration-limit.d.ts.map +1 -1
- package/dist/core/mailbox-loop.d.ts +11 -1
- package/dist/core/mailbox-loop.d.ts.map +1 -1
- package/dist/core/model-availability-calendar.d.ts +27 -0
- package/dist/core/model-availability-calendar.d.ts.map +1 -0
- package/dist/core/system-prompt-builder.d.ts +2 -1
- package/dist/core/system-prompt-builder.d.ts.map +1 -1
- package/dist/defaults/index.js +1459 -560
- package/dist/defaults/index.js.map +4 -4
- package/dist/execution/auto-compaction-middleware.d.ts +3 -2
- package/dist/execution/auto-compaction-middleware.d.ts.map +1 -1
- package/dist/execution/enhance-recovery.d.ts +8 -0
- package/dist/execution/enhance-recovery.d.ts.map +1 -1
- package/dist/execution/error-handler.d.ts +2 -2
- package/dist/execution/error-handler.d.ts.map +1 -1
- package/dist/execution/index.js +643 -134
- package/dist/execution/index.js.map +4 -4
- package/dist/execution/intelligent-compactor.d.ts +0 -16
- package/dist/execution/intelligent-compactor.d.ts.map +1 -1
- package/dist/execution/one-shot-llm.d.ts.map +1 -1
- package/dist/execution/retry-policy.d.ts +0 -13
- package/dist/execution/retry-policy.d.ts.map +1 -1
- package/dist/execution/skill-loader.d.ts.map +1 -1
- package/dist/execution/tool-executor.d.ts.map +1 -1
- package/dist/hq/auth-store.d.ts.map +1 -1
- package/dist/hq/index.js +39 -1
- package/dist/hq/index.js.map +3 -3
- package/dist/index.d.ts +32 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22263 -19999
- package/dist/index.js.map +4 -4
- package/dist/infrastructure/index.js +59 -5
- package/dist/infrastructure/index.js.map +2 -2
- package/dist/kernel/events/agent-events.d.ts +2 -0
- package/dist/kernel/events/agent-events.d.ts.map +1 -1
- package/dist/kernel/events/file-events.d.ts +46 -0
- package/dist/kernel/events/file-events.d.ts.map +1 -1
- package/dist/kernel/events/memory-events.d.ts +9 -0
- package/dist/kernel/events/memory-events.d.ts.map +1 -1
- package/dist/kernel/events/provider-events.d.ts +2 -1
- package/dist/kernel/events/provider-events.d.ts.map +1 -1
- package/dist/kernel/events/tool-events.d.ts +25 -0
- package/dist/kernel/events/tool-events.d.ts.map +1 -1
- package/dist/mailbox-attach.d.ts.map +1 -1
- package/dist/plugins/auto-review-plugin.d.ts +10 -0
- package/dist/plugins/auto-review-plugin.d.ts.map +1 -1
- package/dist/plugins/chimera-plugin.d.ts +5 -0
- package/dist/plugins/chimera-plugin.d.ts.map +1 -1
- package/dist/security/index.d.ts +1 -0
- package/dist/security/index.d.ts.map +1 -1
- package/dist/security/index.js +317 -9
- package/dist/security/index.js.map +4 -4
- package/dist/security/kanban-boundary.d.ts +10 -0
- package/dist/security/kanban-boundary.d.ts.map +1 -0
- package/dist/security/permission-policy.d.ts +8 -1
- package/dist/security/permission-policy.d.ts.map +1 -1
- package/dist/storage/config-loader.d.ts +15 -0
- package/dist/storage/config-loader.d.ts.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +627 -428
- package/dist/storage/index.js.map +4 -4
- package/dist/storage/memory-consolidator.d.ts +49 -4
- package/dist/storage/memory-consolidator.d.ts.map +1 -1
- package/dist/storage/memory-graph-backend.d.ts +11 -0
- package/dist/storage/memory-graph-backend.d.ts.map +1 -1
- package/dist/storage/provider-config-watcher.d.ts +17 -0
- package/dist/storage/provider-config-watcher.d.ts.map +1 -1
- package/dist/storage/session-event-bridge.d.ts +2 -2
- package/dist/storage/session-event-bridge.d.ts.map +1 -1
- package/dist/tools/fallback-manage-tools.d.ts.map +1 -1
- package/dist/tools/index.js +166 -26
- package/dist/tools/index.js.map +3 -3
- package/dist/types/config.d.ts +12 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/file-event-record.d.ts +68 -0
- package/dist/types/file-event-record.d.ts.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +13 -8
- package/dist/types/index.js.map +3 -3
- package/dist/types/permission.d.ts +38 -0
- package/dist/types/permission.d.ts.map +1 -1
- package/dist/types/provider.d.ts +1 -1
- package/dist/types/provider.d.ts.map +1 -1
- package/dist/types/session.d.ts +27 -0
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/tool-executor.d.ts +2 -0
- package/dist/types/tool-executor.d.ts.map +1 -1
- package/dist/utils/config-backup.d.ts +20 -0
- package/dist/utils/config-backup.d.ts.map +1 -0
- package/dist/utils/index.d.ts +12 -11
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +436 -290
- package/dist/utils/index.js.map +4 -4
- package/dist/utils/message-invariants.d.ts +12 -0
- package/dist/utils/message-invariants.d.ts.map +1 -1
- package/dist/utils/safe-json.d.ts +1 -1
- package/dist/utils/safe-json.d.ts.map +1 -1
- package/dist/utils/term.d.ts +6 -0
- package/dist/utils/term.d.ts.map +1 -1
- package/dist/utils/tool-wire-compact.d.ts +9 -0
- package/dist/utils/tool-wire-compact.d.ts.map +1 -1
- package/dist/utils/wstack-paths.d.ts +21 -1
- package/dist/utils/wstack-paths.d.ts.map +1 -1
- package/instructions/agents/ios.md +113 -0
- package/instructions/llm/memory-consolidator.md +4 -12
- package/instructions/system.md +4 -1
- package/package.json +3 -3
- package/skills/auto-review/SKILL.md +176 -0
- package/skills/mnemosyne/SKILL.md +156 -0
- package/skills/mnemosyne/instructions/agent-prompt.md +200 -0
|
@@ -542,14 +542,28 @@ function mapContent(msg, fn) {
|
|
|
542
542
|
}
|
|
543
543
|
return { ...msg, content: next };
|
|
544
544
|
}
|
|
545
|
+
function hasMeaningfulContent(content) {
|
|
546
|
+
if (typeof content === "string") return content.trim().length > 0;
|
|
547
|
+
for (const block of content) {
|
|
548
|
+
if (block.type === "text") {
|
|
549
|
+
if (block.text.trim().length > 0) return true;
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
if (block.type === "thinking") {
|
|
553
|
+
if (block.thinking.trim().length > 0 || block.signature) return true;
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
556
|
+
return true;
|
|
557
|
+
}
|
|
558
|
+
return false;
|
|
559
|
+
}
|
|
545
560
|
function isEmptyMessage(msg) {
|
|
546
|
-
|
|
547
|
-
return msg.content.length === 0;
|
|
561
|
+
return !hasMeaningfulContent(msg.content);
|
|
548
562
|
}
|
|
549
563
|
|
|
550
564
|
// src/utils/safe-json.ts
|
|
551
565
|
function safeParse(input, maxBytes = 5e6) {
|
|
552
|
-
if (input
|
|
566
|
+
if (Buffer.byteLength(input, "utf8") > maxBytes) {
|
|
553
567
|
return { ok: false, error: `Input exceeds limit (${maxBytes} bytes)` };
|
|
554
568
|
}
|
|
555
569
|
try {
|
|
@@ -605,6 +619,11 @@ function invalid(sessionId) {
|
|
|
605
619
|
});
|
|
606
620
|
}
|
|
607
621
|
|
|
622
|
+
// src/utils/string.ts
|
|
623
|
+
function truncate(s, max) {
|
|
624
|
+
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
625
|
+
}
|
|
626
|
+
|
|
608
627
|
// src/utils/ulid.ts
|
|
609
628
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
610
629
|
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
@@ -633,11 +652,6 @@ function ulid(seedTime = Date.now()) {
|
|
|
633
652
|
return encodeTime(seedTime, TIME_LEN) + encodeRandom(RANDOM_LEN);
|
|
634
653
|
}
|
|
635
654
|
|
|
636
|
-
// src/utils/string.ts
|
|
637
|
-
function truncate(s, max) {
|
|
638
|
-
return s.length <= max ? s : `${s.slice(0, max - 1)}\u2026`;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
655
|
// src/utils/wstack-paths.ts
|
|
642
656
|
import { createHash } from "node:crypto";
|
|
643
657
|
import * as fs2 from "node:fs";
|
|
@@ -1643,8 +1657,34 @@ function makeAgentSubagentRunner(opts) {
|
|
|
1643
1657
|
const taskStartedAt = Date.now();
|
|
1644
1658
|
const factoryResult = await opts.factory(ctx.config, task);
|
|
1645
1659
|
const { agent, events } = factoryResult;
|
|
1660
|
+
const taskContext = task.context ?? {};
|
|
1661
|
+
const hasExplicitTelemetryTask = typeof taskContext["telemetryTaskId"] === "string";
|
|
1662
|
+
const telemetryTaskId = hasExplicitTelemetryTask ? taskContext["telemetryTaskId"] : task.id;
|
|
1663
|
+
const telemetryRunId = typeof taskContext["telemetryRunId"] === "string" ? taskContext["telemetryRunId"] : void 0;
|
|
1664
|
+
const telemetryBoardId = typeof taskContext["telemetryBoardId"] === "string" ? taskContext["telemetryBoardId"] : void 0;
|
|
1646
1665
|
const agentContext = agent.ctx;
|
|
1647
|
-
if (agentContext?.meta)
|
|
1666
|
+
if (agentContext?.meta) {
|
|
1667
|
+
agentContext.meta["subagentTaskId"] = task.id;
|
|
1668
|
+
const kanbanContext = taskContext["kanban"];
|
|
1669
|
+
if (kanbanContext && typeof kanbanContext === "object") {
|
|
1670
|
+
agentContext.meta["kanban"] = kanbanContext;
|
|
1671
|
+
}
|
|
1672
|
+
if (hasExplicitTelemetryTask) {
|
|
1673
|
+
agentContext.meta["telemetryTaskId"] = telemetryTaskId;
|
|
1674
|
+
if (telemetryRunId) agentContext.meta["telemetryRunId"] = telemetryRunId;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
if (hasExplicitTelemetryTask) {
|
|
1678
|
+
agentContext?.setCurrentKanbanTask?.(telemetryTaskId, telemetryBoardId);
|
|
1679
|
+
} else {
|
|
1680
|
+
const kanbanContext = taskContext["kanban"];
|
|
1681
|
+
if (kanbanContext && typeof kanbanContext === "object") {
|
|
1682
|
+
const kanban = kanbanContext;
|
|
1683
|
+
const boardId = typeof kanban["boardId"] === "string" ? kanban["boardId"] : void 0;
|
|
1684
|
+
const taskId = typeof kanban["taskId"] === "string" ? kanban["taskId"] : void 0;
|
|
1685
|
+
if (taskId) agentContext?.setCurrentKanbanTask?.(taskId, boardId);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1648
1688
|
const detachFleet = opts.fleetBus?.attach(ctx.subagentId, events, task.id);
|
|
1649
1689
|
const aborter = new AbortController();
|
|
1650
1690
|
ctx.budget._events = events;
|
|
@@ -1669,6 +1709,27 @@ function makeAgentSubagentRunner(opts) {
|
|
|
1669
1709
|
const unsub = [];
|
|
1670
1710
|
unsub.push(
|
|
1671
1711
|
events.on("tool.executed", (e) => {
|
|
1712
|
+
if (hasExplicitTelemetryTask) {
|
|
1713
|
+
opts.hostEvents?.emit("subagent.tool_executed", {
|
|
1714
|
+
sessionId: e.sessionId,
|
|
1715
|
+
subagentId: ctx.subagentId,
|
|
1716
|
+
agentSessionId: e.sessionId,
|
|
1717
|
+
agentName: e.agentName ?? ctx.config.name,
|
|
1718
|
+
taskId: telemetryTaskId,
|
|
1719
|
+
runId: telemetryRunId,
|
|
1720
|
+
traceId: e.traceId,
|
|
1721
|
+
id: e.id,
|
|
1722
|
+
name: e.name,
|
|
1723
|
+
durationMs: e.durationMs,
|
|
1724
|
+
ok: e.ok,
|
|
1725
|
+
// Raw tool inputs may contain credentials or sensitive arguments.
|
|
1726
|
+
// Task telemetry crosses from the isolated worker bus to the shared
|
|
1727
|
+
// host bus, so expose only execution metadata at this boundary.
|
|
1728
|
+
outputBytes: e.outputBytes,
|
|
1729
|
+
outputTokens: e.outputTokens,
|
|
1730
|
+
outputLines: e.outputLines
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1672
1733
|
try {
|
|
1673
1734
|
ctx.budget.recordToolCall();
|
|
1674
1735
|
} catch (eb) {
|
|
@@ -1680,6 +1741,17 @@ function makeAgentSubagentRunner(opts) {
|
|
|
1680
1741
|
lastToolFailed = null;
|
|
1681
1742
|
}
|
|
1682
1743
|
}),
|
|
1744
|
+
events.on("file.event", (e) => {
|
|
1745
|
+
if (!hasExplicitTelemetryTask) return;
|
|
1746
|
+
if (e.runId === telemetryRunId && e.taskId === telemetryTaskId) return;
|
|
1747
|
+
opts.hostEvents?.emit("file.event", {
|
|
1748
|
+
...e,
|
|
1749
|
+
scope: "task",
|
|
1750
|
+
taskId: telemetryTaskId,
|
|
1751
|
+
boardId: telemetryBoardId,
|
|
1752
|
+
runId: telemetryRunId
|
|
1753
|
+
});
|
|
1754
|
+
}),
|
|
1683
1755
|
events.on("provider.response", (e) => {
|
|
1684
1756
|
try {
|
|
1685
1757
|
ctx.budget.recordUsage(e.usage);
|
|
@@ -2973,6 +3045,40 @@ var DOMAIN_AGENTS = [
|
|
|
2973
3045
|
"information architecture"
|
|
2974
3046
|
]
|
|
2975
3047
|
}
|
|
3048
|
+
},
|
|
3049
|
+
{
|
|
3050
|
+
config: {
|
|
3051
|
+
id: "ios",
|
|
3052
|
+
name: "iOS",
|
|
3053
|
+
role: "ios",
|
|
3054
|
+
tools: [...TOOLS.build, "fetch"],
|
|
3055
|
+
prompt: agentPrompt("ios")
|
|
3056
|
+
},
|
|
3057
|
+
budget: HEAVY_BUDGET,
|
|
3058
|
+
capability: {
|
|
3059
|
+
phase: "domain",
|
|
3060
|
+
summary: "Apple-platform app development in Swift against the latest Xcode/iOS-SDK line: SwiftUI, UIKit, SwiftData, concurrency, accessibility, and App Store submission.",
|
|
3061
|
+
keywords: [
|
|
3062
|
+
"ios",
|
|
3063
|
+
"iphone",
|
|
3064
|
+
"ipad",
|
|
3065
|
+
"ipados",
|
|
3066
|
+
"watchos",
|
|
3067
|
+
"tvos",
|
|
3068
|
+
"visionos",
|
|
3069
|
+
"macos",
|
|
3070
|
+
"swift",
|
|
3071
|
+
"swiftui",
|
|
3072
|
+
"uikit",
|
|
3073
|
+
"xcode",
|
|
3074
|
+
"swiftdata",
|
|
3075
|
+
"app store",
|
|
3076
|
+
"app intents",
|
|
3077
|
+
"swift package manager",
|
|
3078
|
+
"cocoapods",
|
|
3079
|
+
"foundation models"
|
|
3080
|
+
]
|
|
3081
|
+
}
|
|
2976
3082
|
}
|
|
2977
3083
|
];
|
|
2978
3084
|
|
|
@@ -6218,6 +6324,8 @@ function rosterSummaryFromConfigs(roster) {
|
|
|
6218
6324
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
6219
6325
|
import {
|
|
6220
6326
|
claimReadyTask,
|
|
6327
|
+
describeKanbanBoundary,
|
|
6328
|
+
heartbeatTaskAssignment,
|
|
6221
6329
|
listReadyTasks,
|
|
6222
6330
|
updateTaskAssignment
|
|
6223
6331
|
} from "@wrongstack/kanban";
|
|
@@ -6972,13 +7080,19 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
6972
7080
|
const projectRoot = ctx.projectRoot;
|
|
6973
7081
|
if (!projectRoot) return { error: "kanban_queue requires ctx.projectRoot." };
|
|
6974
7082
|
const maxTasks = Math.max(1, Math.min(20, Math.floor(i.maxTasks ?? 1)));
|
|
6975
|
-
const
|
|
7083
|
+
const baseLeaseTtlMs = Math.max(1e3, Math.floor(i.leaseTtlMs ?? 5 * 60 * 1e3));
|
|
7084
|
+
const leaseTtlMs = i.awaitCompletion === true && i.timeoutMs !== void 0 ? Math.max(baseLeaseTtlMs, i.timeoutMs + 6e4) : baseLeaseTtlMs;
|
|
7085
|
+
const heartbeatIntervalMs = Math.max(
|
|
7086
|
+
5e3,
|
|
7087
|
+
i.heartbeatIntervalMs ?? Math.floor(leaseTtlMs / 2)
|
|
7088
|
+
);
|
|
7089
|
+
const effectiveLeaseTtlMs = Math.max(leaseTtlMs, heartbeatIntervalMs * 2);
|
|
6976
7090
|
const claimedAt = nowIso();
|
|
6977
7091
|
const leaseSeeding = {
|
|
6978
7092
|
leaseId: randomUUID5(),
|
|
6979
7093
|
claimedAt,
|
|
6980
7094
|
heartbeatAt: claimedAt,
|
|
6981
|
-
leaseExpiresAt: new Date(Date.now() +
|
|
7095
|
+
leaseExpiresAt: new Date(Date.now() + effectiveLeaseTtlMs).toISOString()
|
|
6982
7096
|
};
|
|
6983
7097
|
const candidateTaskIds = i.taskId !== void 0 ? [i.taskId] : i.query ? (await listReadyTasks(projectRoot, {
|
|
6984
7098
|
...i.boardId !== void 0 ? { boardId: i.boardId } : {}
|
|
@@ -6986,9 +7100,11 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
6986
7100
|
const dispatches = [];
|
|
6987
7101
|
const errors = [];
|
|
6988
7102
|
const resultFailures = [];
|
|
7103
|
+
const budgetRejectedTaskIds = /* @__PURE__ */ new Set();
|
|
6989
7104
|
for (let index = 0; index < maxTasks; index++) {
|
|
6990
7105
|
const candidateTaskId = candidateTaskIds?.[index];
|
|
6991
7106
|
if (candidateTaskIds && !candidateTaskId) break;
|
|
7107
|
+
if (candidateTaskId && budgetRejectedTaskIds.has(candidateTaskId)) continue;
|
|
6992
7108
|
const claim = await claimReadyTask(projectRoot, {
|
|
6993
7109
|
...i.boardId !== void 0 ? { boardId: i.boardId } : {},
|
|
6994
7110
|
...candidateTaskId !== void 0 ? { taskId: candidateTaskId } : {},
|
|
@@ -7013,14 +7129,22 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
7013
7129
|
if (costCeiling !== void 0) {
|
|
7014
7130
|
const remaining = director.getRemainingBudgetUsd();
|
|
7015
7131
|
if (remaining !== void 0 && remaining < costCeiling) {
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7132
|
+
budgetRejectedTaskIds.add(claim.task.id);
|
|
7133
|
+
const budgetError = `Cost ceiling ${costCeiling} exceeds remaining budget ${remaining.toFixed(4)}`;
|
|
7134
|
+
await updateTaskAssignment(
|
|
7135
|
+
projectRoot,
|
|
7136
|
+
claim.board.id,
|
|
7137
|
+
claim.task.id,
|
|
7138
|
+
{
|
|
7139
|
+
...claim.task.assignment ?? {},
|
|
7140
|
+
status: "failed",
|
|
7141
|
+
error: budgetError
|
|
7142
|
+
},
|
|
7143
|
+
{ expectedLeaseId: leaseSeeding.leaseId }
|
|
7144
|
+
);
|
|
7021
7145
|
errors.push({
|
|
7022
7146
|
taskId: claim.task.id,
|
|
7023
|
-
error:
|
|
7147
|
+
error: budgetError
|
|
7024
7148
|
});
|
|
7025
7149
|
continue;
|
|
7026
7150
|
}
|
|
@@ -7028,12 +7152,20 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
7028
7152
|
try {
|
|
7029
7153
|
const config = buildKanbanSubagentConfig(claim.task, i, roster);
|
|
7030
7154
|
subagentId = await director.spawn(config);
|
|
7031
|
-
|
|
7032
|
-
|
|
7155
|
+
const dispatchTaskId = randomUUID5();
|
|
7156
|
+
const taskSpec = {
|
|
7157
|
+
id: dispatchTaskId,
|
|
7033
7158
|
subagentId,
|
|
7034
7159
|
description: buildKanbanFleetTaskPrompt(claim.board, claim.task, {
|
|
7035
|
-
|
|
7036
|
-
|
|
7160
|
+
// The worker must heartbeat against the lease it actually holds.
|
|
7161
|
+
// effectiveLeaseTtlMs is the real lease window (raised from the
|
|
7162
|
+
// requested TTL when needed so the first heartbeat always lands
|
|
7163
|
+
// before expiry), and the computed heartbeatIntervalMs is always
|
|
7164
|
+
// strictly shorter than that window. Passing the raw requested
|
|
7165
|
+
// values here would give the worker a 60s cadence against a
|
|
7166
|
+
// 10s lease, letting the lease expire before its first renewal.
|
|
7167
|
+
heartbeatIntervalMs,
|
|
7168
|
+
leaseTtlMs: effectiveLeaseTtlMs,
|
|
7037
7169
|
leaseId: leaseSeeding.leaseId,
|
|
7038
7170
|
leaseExpiresAt: leaseSeeding.leaseExpiresAt
|
|
7039
7171
|
}),
|
|
@@ -7044,16 +7176,51 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
7044
7176
|
kanban: {
|
|
7045
7177
|
boardId: claim.board.id,
|
|
7046
7178
|
taskId: claim.task.id,
|
|
7047
|
-
origin: claim.task.origin
|
|
7179
|
+
origin: claim.task.origin,
|
|
7180
|
+
projectRoot
|
|
7048
7181
|
}
|
|
7049
7182
|
}
|
|
7050
|
-
}
|
|
7051
|
-
await updateTaskAssignment(
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7183
|
+
};
|
|
7184
|
+
const dispatched = await updateTaskAssignment(
|
|
7185
|
+
projectRoot,
|
|
7186
|
+
claim.board.id,
|
|
7187
|
+
claim.task.id,
|
|
7188
|
+
{
|
|
7189
|
+
...claim.task.assignment ?? {},
|
|
7190
|
+
status: "running",
|
|
7191
|
+
subagentId,
|
|
7192
|
+
runTaskId: dispatchTaskId,
|
|
7193
|
+
// Renew the lease from the actual dispatch moment, not from claim
|
|
7194
|
+
// time. The claim-time lease starts before spawn + queueing, so
|
|
7195
|
+
// `timeoutMs + 60_000` can expire while the worker is still
|
|
7196
|
+
// queued or running, letting recover_stale reclaim and duplicate
|
|
7197
|
+
// the assignment (see auto-extend.ts: timeout extensions can push
|
|
7198
|
+
// execution well past the original timeoutMs). Both heartbeatAt
|
|
7199
|
+
// and leaseExpiresAt are stamped from now so the worker has a
|
|
7200
|
+
// full lease window from the moment it was actually dispatched.
|
|
7201
|
+
heartbeatAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
7202
|
+
leaseExpiresAt: new Date(Date.now() + effectiveLeaseTtlMs).toISOString()
|
|
7203
|
+
},
|
|
7204
|
+
{ expectedLeaseId: leaseSeeding.leaseId }
|
|
7205
|
+
);
|
|
7206
|
+
if (dispatched === null) {
|
|
7207
|
+
try {
|
|
7208
|
+
await director.terminate(subagentId);
|
|
7209
|
+
} catch (cleanupErr) {
|
|
7210
|
+
errors.push({
|
|
7211
|
+
taskId: claim.task.id,
|
|
7212
|
+
error: `Lease lost during dispatch; orphan subagent ${subagentId} termination failed: ${toErrorMessage(cleanupErr)}`
|
|
7213
|
+
});
|
|
7214
|
+
continue;
|
|
7215
|
+
}
|
|
7216
|
+
errors.push({
|
|
7217
|
+
taskId: claim.task.id,
|
|
7218
|
+
error: `Lease lost during dispatch (reassigned by recovery); subagent ${subagentId} terminated.`
|
|
7219
|
+
});
|
|
7220
|
+
continue;
|
|
7221
|
+
}
|
|
7222
|
+
await director.assign(taskSpec);
|
|
7223
|
+
runTaskId = dispatchTaskId;
|
|
7057
7224
|
dispatches.push({
|
|
7058
7225
|
boardId: claim.board.id,
|
|
7059
7226
|
taskId: claim.task.id,
|
|
@@ -7072,28 +7239,97 @@ function makeKanbanQueueTool(director, roster) {
|
|
|
7072
7239
|
message = `${message}; cleanup failed for spawned subagent ${subagentId}: ${toErrorMessage(cleanupErr)}`;
|
|
7073
7240
|
}
|
|
7074
7241
|
}
|
|
7075
|
-
await updateTaskAssignment(
|
|
7076
|
-
|
|
7077
|
-
|
|
7078
|
-
|
|
7079
|
-
|
|
7080
|
-
|
|
7081
|
-
|
|
7242
|
+
await updateTaskAssignment(
|
|
7243
|
+
projectRoot,
|
|
7244
|
+
claim.board.id,
|
|
7245
|
+
claim.task.id,
|
|
7246
|
+
{
|
|
7247
|
+
...claim.task.assignment ?? {},
|
|
7248
|
+
status: "failed",
|
|
7249
|
+
...subagentId !== void 0 ? { subagentId } : {},
|
|
7250
|
+
...runTaskId !== void 0 ? { runTaskId } : {},
|
|
7251
|
+
error: message
|
|
7252
|
+
},
|
|
7253
|
+
{ expectedLeaseId: leaseSeeding.leaseId }
|
|
7254
|
+
);
|
|
7082
7255
|
errors.push({ taskId: claim.task.id, error: message });
|
|
7083
7256
|
}
|
|
7084
7257
|
}
|
|
7258
|
+
if (!i.awaitCompletion && dispatches.length > 0 && typeof director.fleet?.subscribe === "function") {
|
|
7259
|
+
const ourLeaseId = leaseSeeding.leaseId;
|
|
7260
|
+
const disposers = /* @__PURE__ */ new Map();
|
|
7261
|
+
const renewalStartedAt = Date.now();
|
|
7262
|
+
const maxRenewalWindowMs = 24 * 60 * 6e4;
|
|
7263
|
+
const stopAll = () => {
|
|
7264
|
+
clearInterval(renewalTimer);
|
|
7265
|
+
for (const dispose of disposers.values()) dispose();
|
|
7266
|
+
disposers.clear();
|
|
7267
|
+
};
|
|
7268
|
+
const renewalTimer = setInterval(() => {
|
|
7269
|
+
if (disposers.size === 0 || Date.now() - renewalStartedAt > maxRenewalWindowMs) {
|
|
7270
|
+
stopAll();
|
|
7271
|
+
return;
|
|
7272
|
+
}
|
|
7273
|
+
const refreshedExpiry = new Date(Date.now() + effectiveLeaseTtlMs).toISOString();
|
|
7274
|
+
for (const dispatch of dispatches) {
|
|
7275
|
+
if (!disposers.has(dispatch.runTaskId)) continue;
|
|
7276
|
+
void heartbeatTaskAssignment(projectRoot, dispatch.boardId, dispatch.taskId, {
|
|
7277
|
+
leaseExpiresAt: refreshedExpiry,
|
|
7278
|
+
expectedLeaseId: ourLeaseId
|
|
7279
|
+
}).catch(() => {
|
|
7280
|
+
});
|
|
7281
|
+
}
|
|
7282
|
+
}, heartbeatIntervalMs);
|
|
7283
|
+
renewalTimer.unref?.();
|
|
7284
|
+
for (const dispatch of dispatches) {
|
|
7285
|
+
const dispose = director.fleet.subscribe(dispatch.subagentId, (event) => {
|
|
7286
|
+
if (event.type !== "subagent.completed" && event.type !== "subagent.stopped") return;
|
|
7287
|
+
if (event.type === "subagent.completed" && event.taskId !== dispatch.runTaskId) return;
|
|
7288
|
+
disposers.get(dispatch.runTaskId)?.();
|
|
7289
|
+
disposers.delete(dispatch.runTaskId);
|
|
7290
|
+
if (disposers.size === 0) clearInterval(renewalTimer);
|
|
7291
|
+
});
|
|
7292
|
+
disposers.set(dispatch.runTaskId, dispose);
|
|
7293
|
+
}
|
|
7294
|
+
}
|
|
7085
7295
|
let results;
|
|
7086
7296
|
if (i.awaitCompletion && dispatches.length > 0) {
|
|
7087
|
-
|
|
7297
|
+
const ourLeaseId = leaseSeeding.leaseId;
|
|
7298
|
+
const runTaskIds = new Set(dispatches.map((dispatch) => dispatch.runTaskId));
|
|
7299
|
+
const heartbeatTimer = setInterval(async () => {
|
|
7300
|
+
const refreshedExpiry = new Date(Date.now() + effectiveLeaseTtlMs).toISOString();
|
|
7301
|
+
for (const dispatch of dispatches) {
|
|
7302
|
+
if (!runTaskIds.has(dispatch.runTaskId)) continue;
|
|
7303
|
+
try {
|
|
7304
|
+
await heartbeatTaskAssignment(projectRoot, dispatch.boardId, dispatch.taskId, {
|
|
7305
|
+
leaseExpiresAt: refreshedExpiry,
|
|
7306
|
+
expectedLeaseId: ourLeaseId
|
|
7307
|
+
});
|
|
7308
|
+
} catch {
|
|
7309
|
+
}
|
|
7310
|
+
}
|
|
7311
|
+
}, heartbeatIntervalMs);
|
|
7312
|
+
try {
|
|
7313
|
+
results = await director.awaitTasks(dispatches.map((dispatch) => dispatch.runTaskId));
|
|
7314
|
+
} finally {
|
|
7315
|
+
clearInterval(heartbeatTimer);
|
|
7316
|
+
}
|
|
7088
7317
|
for (const result of results) {
|
|
7089
7318
|
const dispatch = dispatches.find((candidate) => candidate.runTaskId === result.taskId);
|
|
7090
7319
|
if (!dispatch) continue;
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7320
|
+
runTaskIds.delete(dispatch.runTaskId);
|
|
7321
|
+
await updateTaskAssignment(
|
|
7322
|
+
projectRoot,
|
|
7323
|
+
dispatch.boardId,
|
|
7324
|
+
dispatch.taskId,
|
|
7325
|
+
{
|
|
7326
|
+
status: result.status === "success" ? "completed" : "failed",
|
|
7327
|
+
subagentId: result.subagentId,
|
|
7328
|
+
runTaskId: result.taskId,
|
|
7329
|
+
...result.status === "success" ? { lastResult: resultToText(result) } : { error: resultErrorText(result) }
|
|
7330
|
+
},
|
|
7331
|
+
{ expectedLeaseId: ourLeaseId }
|
|
7332
|
+
);
|
|
7097
7333
|
if (result.status !== "success") {
|
|
7098
7334
|
resultFailures.push({
|
|
7099
7335
|
taskId: dispatch.taskId,
|
|
@@ -7201,6 +7437,16 @@ function buildKanbanFleetTaskPrompt(board, task, lease) {
|
|
|
7201
7437
|
task.origin.phaseId ? `phaseId: ${task.origin.phaseId}` : "",
|
|
7202
7438
|
task.origin.taskId ? `sourceTaskId: ${task.origin.taskId}` : ""
|
|
7203
7439
|
].filter(Boolean).join("\n") : "";
|
|
7440
|
+
const boundaries = [
|
|
7441
|
+
board.boundary?.enabled ? `board: ${describeKanbanBoundary(board.boundary)}` : "",
|
|
7442
|
+
task.boundary?.enabled ? `task: ${describeKanbanBoundary(task.boundary)}` : "",
|
|
7443
|
+
...(board.boundary?.allow ?? []).map(
|
|
7444
|
+
(selector) => `board allow ${selector.access} ${selector.kind}:${selector.path}`
|
|
7445
|
+
),
|
|
7446
|
+
...(task.boundary?.allow ?? []).map(
|
|
7447
|
+
(selector) => `task allow ${selector.access} ${selector.kind}:${selector.path}`
|
|
7448
|
+
)
|
|
7449
|
+
].filter(Boolean);
|
|
7204
7450
|
return [
|
|
7205
7451
|
"You are processing a claimed WrongStack Kanban task.",
|
|
7206
7452
|
"",
|
|
@@ -7223,6 +7469,8 @@ ${checks}` : "",
|
|
|
7223
7469
|
metrics ? `Goal metrics:
|
|
7224
7470
|
${metrics}` : "",
|
|
7225
7471
|
task.labels?.length ? `Labels: ${task.labels.join(", ")}` : "",
|
|
7472
|
+
boundaries.length ? `BOUNDARY CONTRACT (enforced by the tool runtime; do not attempt to bypass):
|
|
7473
|
+
${boundaries.map((line) => `- ${line}`).join("\n")}` : "",
|
|
7226
7474
|
"Lease contract (Sprint 1):",
|
|
7227
7475
|
`- leaseId: ${lease.leaseId}`,
|
|
7228
7476
|
`- claimedAt: ${task.assignment?.claimedAt ?? "<unknown>"}`,
|
|
@@ -7238,10 +7486,21 @@ ${metrics}` : "",
|
|
|
7238
7486
|
"Work this task end-to-end. Treat the board as a live plan, not a frozen assignment snapshot.",
|
|
7239
7487
|
`Before material work and whenever evidence changes the plan, call kanban with action "get_board" and boardId "${board.id}" to reassess current tasks, dependencies, priorities, and peer changes.`,
|
|
7240
7488
|
"You may use kanban add_task, update_task, split_task, merge_tasks, move_task, delete_task, or dependency actions when reassessment shows the plan should change. Preserve traceability and do not keep obsolete work merely because it existed at dispatch time.",
|
|
7489
|
+
"",
|
|
7490
|
+
"FIELD MAINTENANCE \u2014 keep every task detail current:",
|
|
7491
|
+
'- Call "update_task" to refresh description, priority, type, labels, or estimatedHours as the work evolves.',
|
|
7492
|
+
'- Call "add_note" with author and content to record progress, decisions, or roadblock context.',
|
|
7493
|
+
'- Call "add_check" / "update_check" with description and status (pending/passed/failed) to track acceptance criteria.',
|
|
7494
|
+
'- Call "add_goal_metric" / "update_goal_metric" with name and current/target to report measurable progress.',
|
|
7495
|
+
'- Call "add_link" with url and type (issue/pr/doc/commit/design/file/url/other) to attach evidence.',
|
|
7496
|
+
'- Call "update_task" to set actualHours when you have a final time estimate.',
|
|
7497
|
+
"Do not leave stale or placeholder field values \u2014 the kanban board is the shared source of truth for every stakeholder.",
|
|
7498
|
+
"",
|
|
7499
|
+
"LEASE & LIFECYCLE:",
|
|
7241
7500
|
"Every successful board mutation updates shared pending work and notifies the owning session. Re-read the board after mutations and adapt to changes made by other agents; never assume your initial todo list remains authoritative.",
|
|
7242
|
-
`When you start, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}",
|
|
7243
|
-
`To stay alive in the queue, call kanban with action "heartbeat_assignment", boardId "${board.id}", taskId "${task.id}",
|
|
7244
|
-
`When you finish, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}",
|
|
7501
|
+
`When you start, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}", assignmentStatus "running", and expectedLeaseId "${lease.leaseId}". Include subagentId and runTaskId when you have them. The expectedLeaseId fence makes the write a safe no-op if recover_stale already reassigned the task because your lease expired.`,
|
|
7502
|
+
`To stay alive in the queue, call kanban with action "heartbeat_assignment", boardId "${board.id}", taskId "${task.id}", heartbeatAt set to the current time, and expectedLeaseId "${lease.leaseId}". Cadence must be <= heartbeatIntervalMs and well before leaseExpiresAt. The expectedLeaseId fence makes the heartbeat a safe no-op if the lease was reassigned, so a stale worker cannot renew a successor's lease.`,
|
|
7503
|
+
`When you finish, call kanban with action "mark_assignment", boardId "${board.id}", taskId "${task.id}", assignmentStatus "completed" or "failed", and expectedLeaseId "${lease.leaseId}". Include lastResult or error. Populate every relevant detail field before the terminal mark_assignment so the card's final state is complete.`,
|
|
7245
7504
|
`If you cannot finish in time, call kanban with action "heartbeat_assignment" to extend the lease, or with action "release_task" to release so another worker can claim. Do NOT silently abandon the assignment.`,
|
|
7246
7505
|
'On failure the host may call kanban with action "recover_stale" (mode: retry/release/fail); respect its decisions and do not duplicate work in parallel.',
|
|
7247
7506
|
"When finished, report what changed, what you verified, and any remaining blockers."
|
|
@@ -7935,18 +8194,20 @@ function hashStr(s) {
|
|
|
7935
8194
|
// src/types/provider.ts
|
|
7936
8195
|
var CONTEXT_OVERFLOW_RE = /context.length|context.window|max.*tokens?.*exceeded|prompt is too long|too long|exceeds the context|\btokens\b.*exceed|context_length_exceeded/i;
|
|
7937
8196
|
var CONTENT_FILTER_RE = /content.(filter|policy|moderation)|safety (system|filter)/i;
|
|
8197
|
+
var QUOTA_EXHAUSTED_RE = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit/i;
|
|
7938
8198
|
function classifyProviderError(status, body, message) {
|
|
7939
8199
|
const type = body?.type;
|
|
8200
|
+
const text = [message, body?.message, type, body?.raw].filter(Boolean).join("\n");
|
|
7940
8201
|
if (status === 0) return "network";
|
|
7941
8202
|
if (status === 408) return "timeout";
|
|
7942
8203
|
if (status === 599) return "stream_hang";
|
|
8204
|
+
if (status === 402 || QUOTA_EXHAUSTED_RE.test(text)) return "quota_exhausted";
|
|
7943
8205
|
if (type === "rate_limit_error" || status === 429) return "rate_limit";
|
|
7944
8206
|
if (type === "overloaded_error" || status === 529) return "overloaded";
|
|
7945
8207
|
if (status >= 500) return "server";
|
|
7946
8208
|
if (type === "authentication_error" || type === "permission_error" || status === 401 || status === 403) {
|
|
7947
8209
|
return "auth";
|
|
7948
8210
|
}
|
|
7949
|
-
const text = [message, body?.message, type, body?.raw].filter(Boolean).join("\n");
|
|
7950
8211
|
if (type === "content_filter" || CONTENT_FILTER_RE.test(text)) return "content_filter";
|
|
7951
8212
|
if (status === 413 || status >= 400 && CONTEXT_OVERFLOW_RE.test(text)) {
|
|
7952
8213
|
return "context_overflow";
|
|
@@ -8021,6 +8282,7 @@ var KIND_TO_CODE = {
|
|
|
8021
8282
|
network: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
8022
8283
|
timeout: ERROR_CODES.PROVIDER_NETWORK_ERROR,
|
|
8023
8284
|
rate_limit: ERROR_CODES.PROVIDER_RATE_LIMITED,
|
|
8285
|
+
quota_exhausted: ERROR_CODES.PROVIDER_RATE_LIMITED,
|
|
8024
8286
|
auth: ERROR_CODES.PROVIDER_AUTH_FAILED,
|
|
8025
8287
|
overloaded: ERROR_CODES.PROVIDER_OVERLOADED,
|
|
8026
8288
|
context_overflow: ERROR_CODES.PROVIDER_CONTEXT_OVERFLOW,
|
|
@@ -8034,6 +8296,84 @@ function kindToCode(kind) {
|
|
|
8034
8296
|
return KIND_TO_CODE[kind];
|
|
8035
8297
|
}
|
|
8036
8298
|
|
|
8299
|
+
// src/core/model-availability-calendar.ts
|
|
8300
|
+
function logicalCalendarTarget(providerId, model) {
|
|
8301
|
+
if (providerId !== "omniroute") return { providerId, model };
|
|
8302
|
+
const slash = model.indexOf("/");
|
|
8303
|
+
return slash > 0 && slash < model.length - 1 ? { providerId: model.slice(0, slash), model: model.slice(slash + 1) } : { providerId, model };
|
|
8304
|
+
}
|
|
8305
|
+
var WEEKDAYS = {
|
|
8306
|
+
Sun: 0,
|
|
8307
|
+
Mon: 1,
|
|
8308
|
+
Tue: 2,
|
|
8309
|
+
Wed: 3,
|
|
8310
|
+
Thu: 4,
|
|
8311
|
+
Fri: 5,
|
|
8312
|
+
Sat: 6
|
|
8313
|
+
};
|
|
8314
|
+
function minuteOfDay(value) {
|
|
8315
|
+
const match = /^(\d{2}):(\d{2})$/.exec(value);
|
|
8316
|
+
if (!match) return void 0;
|
|
8317
|
+
const hour = Number(match[1]);
|
|
8318
|
+
const minute = Number(match[2]);
|
|
8319
|
+
if (hour > 23 || minute > 59) return void 0;
|
|
8320
|
+
return hour * 60 + minute;
|
|
8321
|
+
}
|
|
8322
|
+
function clockAt(date, timezone) {
|
|
8323
|
+
try {
|
|
8324
|
+
const parts = new Intl.DateTimeFormat("en-US", {
|
|
8325
|
+
timeZone: timezone,
|
|
8326
|
+
weekday: "short",
|
|
8327
|
+
hour: "2-digit",
|
|
8328
|
+
minute: "2-digit",
|
|
8329
|
+
hourCycle: "h23"
|
|
8330
|
+
}).formatToParts(date);
|
|
8331
|
+
const weekday = parts.find((part) => part.type === "weekday")?.value;
|
|
8332
|
+
const hour = Number(parts.find((part) => part.type === "hour")?.value);
|
|
8333
|
+
const minute = Number(parts.find((part) => part.type === "minute")?.value);
|
|
8334
|
+
if (!weekday || WEEKDAYS[weekday] === void 0 || !Number.isFinite(hour + minute))
|
|
8335
|
+
return void 0;
|
|
8336
|
+
return { day: WEEKDAYS[weekday], minute: hour * 60 + minute };
|
|
8337
|
+
} catch {
|
|
8338
|
+
return void 0;
|
|
8339
|
+
}
|
|
8340
|
+
}
|
|
8341
|
+
function targetMatches(rule, providerId, model) {
|
|
8342
|
+
if (rule.provider && rule.provider !== providerId) return false;
|
|
8343
|
+
if (rule.model && rule.model !== model) return false;
|
|
8344
|
+
return Boolean(rule.provider || rule.model);
|
|
8345
|
+
}
|
|
8346
|
+
function timeMatches(rule, day, minute) {
|
|
8347
|
+
const start = minuteOfDay(rule.start);
|
|
8348
|
+
const end = minuteOfDay(rule.end);
|
|
8349
|
+
if (start === void 0 || end === void 0) return false;
|
|
8350
|
+
const days = rule.days?.length ? new Set(rule.days) : void 0;
|
|
8351
|
+
if (start === end) return !days || days.has(day);
|
|
8352
|
+
if (start < end) return (!days || days.has(day)) && minute >= start && minute < end;
|
|
8353
|
+
if (minute >= start) return !days || days.has(day);
|
|
8354
|
+
const previousDay = (day + 6) % 7;
|
|
8355
|
+
return minute < end && (!days || days.has(previousDay));
|
|
8356
|
+
}
|
|
8357
|
+
function evaluateModelCalendar(rules, providerId, model, at = /* @__PURE__ */ new Date()) {
|
|
8358
|
+
({ providerId, model } = logicalCalendarTarget(providerId, model));
|
|
8359
|
+
const allowRules = [];
|
|
8360
|
+
let allowMatched = false;
|
|
8361
|
+
for (const rule of rules ?? []) {
|
|
8362
|
+
if (rule.enabled === false || !targetMatches(rule, providerId, model)) continue;
|
|
8363
|
+
const clock = clockAt(at, rule.timezone);
|
|
8364
|
+
if (!clock || minuteOfDay(rule.start) === void 0 || minuteOfDay(rule.end) === void 0)
|
|
8365
|
+
continue;
|
|
8366
|
+
if (rule.mode === "allow_only") {
|
|
8367
|
+
allowRules.push(rule);
|
|
8368
|
+
if (timeMatches(rule, clock.day, clock.minute)) allowMatched = true;
|
|
8369
|
+
} else if (timeMatches(rule, clock.day, clock.minute)) {
|
|
8370
|
+
return { allowed: false, rule };
|
|
8371
|
+
}
|
|
8372
|
+
}
|
|
8373
|
+
if (allowRules.length > 0 && !allowMatched) return { allowed: false, rule: allowRules[0] };
|
|
8374
|
+
return { allowed: true };
|
|
8375
|
+
}
|
|
8376
|
+
|
|
8037
8377
|
// src/core/fallback-profile-manager.ts
|
|
8038
8378
|
function hasText(value) {
|
|
8039
8379
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -8042,7 +8382,8 @@ function providerHasKey(entry) {
|
|
|
8042
8382
|
if (!entry) return false;
|
|
8043
8383
|
if (hasText(entry.apiKey)) return true;
|
|
8044
8384
|
if (Array.isArray(entry.apiKeys) && entry.apiKeys.some((k) => hasText(k?.apiKey))) return true;
|
|
8045
|
-
if (Array.isArray(entry.envVars) && entry.envVars.some((v) => hasText(process.env[v])))
|
|
8385
|
+
if (Array.isArray(entry.envVars) && entry.envVars.some((v) => hasText(process.env[v])))
|
|
8386
|
+
return true;
|
|
8046
8387
|
return false;
|
|
8047
8388
|
}
|
|
8048
8389
|
function visibleProviderModels(config, providerId, providerModels) {
|
|
@@ -8114,6 +8455,8 @@ var FallbackProfileManager = class {
|
|
|
8114
8455
|
const health = this.checkProvider(providerId);
|
|
8115
8456
|
if (!health.usable) continue;
|
|
8116
8457
|
if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
|
|
8458
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
|
|
8459
|
+
continue;
|
|
8117
8460
|
const allowedModels = this.config.providers?.[providerId]?.models;
|
|
8118
8461
|
if (allowedModels && !allowedModels.includes(parsed.model)) continue;
|
|
8119
8462
|
resolved.push({
|
|
@@ -8186,6 +8529,8 @@ var FallbackProfileManager = class {
|
|
|
8186
8529
|
seen.add(key);
|
|
8187
8530
|
if (excludeKey && key === excludeKey) continue;
|
|
8188
8531
|
if (this.statusTracker && !this.statusTracker.isAvailable(providerId, parsed.model)) continue;
|
|
8532
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, providerId, parsed.model).allowed)
|
|
8533
|
+
continue;
|
|
8189
8534
|
resolved.push({
|
|
8190
8535
|
providerId,
|
|
8191
8536
|
model: parsed.model,
|
|
@@ -8230,6 +8575,8 @@ var FallbackProfileManager = class {
|
|
|
8230
8575
|
seen.add(ref);
|
|
8231
8576
|
if (excludeKey && ref === excludeKey) continue;
|
|
8232
8577
|
if (this.statusTracker && !this.statusTracker.isAvailable(id, model)) continue;
|
|
8578
|
+
if (!evaluateModelCalendar(this.config.modelAvailabilitySchedule, id, model).allowed)
|
|
8579
|
+
continue;
|
|
8233
8580
|
if (favoriteSet.has(ref)) {
|
|
8234
8581
|
favorites.push(ref);
|
|
8235
8582
|
continue;
|
|
@@ -8507,6 +8854,7 @@ function classifySubagentError(err, hints = {}) {
|
|
|
8507
8854
|
function providerErrorToSubagentError(err, message, cause) {
|
|
8508
8855
|
switch (err.kind) {
|
|
8509
8856
|
case "rate_limit":
|
|
8857
|
+
case "quota_exhausted":
|
|
8510
8858
|
return {
|
|
8511
8859
|
kind: "provider_rate_limit",
|
|
8512
8860
|
message,
|
|
@@ -9896,6 +10244,10 @@ var Director = class _Director {
|
|
|
9896
10244
|
largeAnswerStore;
|
|
9897
10245
|
/** Shared provider/model status tracker, or undefined. */
|
|
9898
10246
|
statusTracker;
|
|
10247
|
+
/** Session/leader's provider id — absolute last-resort fallback for every spawn. */
|
|
10248
|
+
sessionProvider;
|
|
10249
|
+
/** Session/leader's model id — paired with sessionProvider above. */
|
|
10250
|
+
sessionModel;
|
|
9899
10251
|
constructor(opts) {
|
|
9900
10252
|
this.id = opts.config.coordinatorId || randomUUID7();
|
|
9901
10253
|
this.brain = opts.brain;
|
|
@@ -9936,6 +10288,8 @@ var Director = class _Director {
|
|
|
9936
10288
|
this.fleetManager = opts.fleetManager;
|
|
9937
10289
|
this.statusTracker = opts.statusTracker;
|
|
9938
10290
|
this.logger = opts.logger;
|
|
10291
|
+
this.sessionProvider = opts.sessionProvider;
|
|
10292
|
+
this.sessionModel = opts.sessionModel;
|
|
9939
10293
|
if (this.sharedScratchpadPath) {
|
|
9940
10294
|
void fsp5.mkdir(this.sharedScratchpadPath, { recursive: true }).catch((err) => this.logShutdownError("shared_scratchpad_mkdir", err));
|
|
9941
10295
|
}
|
|
@@ -10444,20 +10798,31 @@ var Director = class _Director {
|
|
|
10444
10798
|
return result.subagentId;
|
|
10445
10799
|
}
|
|
10446
10800
|
resolveSpawnModel(config) {
|
|
10801
|
+
if (config.provider?.trim() === "") config.provider = void 0;
|
|
10802
|
+
if (config.model?.trim() === "") config.model = void 0;
|
|
10447
10803
|
if (!config.model && this.modelMatrix) {
|
|
10448
10804
|
const matrix = typeof this.modelMatrix === "function" ? this.modelMatrix() : this.modelMatrix;
|
|
10449
10805
|
const resolution = resolveModelMatrixResolution(matrix, config.role);
|
|
10450
10806
|
const entry = resolution?.source === "default" && roleNeedsIndependentReviewModel(config.role) ? void 0 : resolution?.entry;
|
|
10451
|
-
if (entry
|
|
10452
|
-
config.model = entry.model;
|
|
10807
|
+
if (entry) {
|
|
10808
|
+
if (entry.model) config.model = entry.model;
|
|
10453
10809
|
if (entry.provider) config.provider = entry.provider;
|
|
10454
10810
|
if (entry.fallbackProfile) config.fallbackProfile = entry.fallbackProfile;
|
|
10455
10811
|
if (entry.modelRuntime) config.modelRuntime = entry.modelRuntime;
|
|
10456
|
-
} else if (entry?.fallbackProfile) {
|
|
10457
|
-
config.fallbackProfile = entry.fallbackProfile;
|
|
10458
|
-
if (entry.modelRuntime) config.modelRuntime = entry.modelRuntime;
|
|
10459
10812
|
}
|
|
10460
10813
|
}
|
|
10814
|
+
if (!config.provider && this.sessionProvider) {
|
|
10815
|
+
config.provider = this.sessionProvider;
|
|
10816
|
+
this.logger?.info(
|
|
10817
|
+
`spawn: provider="${config.provider}" for role "${config.role ?? "?"}" fell back to session provider (matrix resolution left it undefined)`
|
|
10818
|
+
);
|
|
10819
|
+
}
|
|
10820
|
+
if (!config.model && this.sessionModel) {
|
|
10821
|
+
config.model = this.sessionModel;
|
|
10822
|
+
this.logger?.info(
|
|
10823
|
+
`spawn: model="${config.model}" for role "${config.role ?? "?"}" fell back to session model (matrix resolution left it undefined)`
|
|
10824
|
+
);
|
|
10825
|
+
}
|
|
10461
10826
|
if (this.statusTracker && config.provider && config.model) {
|
|
10462
10827
|
if (!this.statusTracker.isAvailable(config.provider, config.model)) {
|
|
10463
10828
|
this.logger?.warn(
|
|
@@ -14456,6 +14821,145 @@ var AUTO_COMPACT_INTERVAL_MS = 3e5;
|
|
|
14456
14821
|
var AUTO_COMPACT_READ_MAX_AGE_MS = 6e5;
|
|
14457
14822
|
var AUTO_COMPACT_DEFAULT_TTL_MS = 864e5;
|
|
14458
14823
|
|
|
14824
|
+
// src/coordination/mailbox-types.ts
|
|
14825
|
+
var MAILBOX_TYPE_PROPERTIES = {
|
|
14826
|
+
note: {
|
|
14827
|
+
category: "informational",
|
|
14828
|
+
expectsReply: false,
|
|
14829
|
+
requiresAction: false,
|
|
14830
|
+
backgroundEligible: false,
|
|
14831
|
+
outOfBand: false,
|
|
14832
|
+
renderPriority: 20,
|
|
14833
|
+
recipientObligation: "Read for context; no reply needed.",
|
|
14834
|
+
senderGuidance: "General-purpose FYI. Use when no more specific type applies."
|
|
14835
|
+
},
|
|
14836
|
+
ask: {
|
|
14837
|
+
category: "actionable",
|
|
14838
|
+
expectsReply: true,
|
|
14839
|
+
requiresAction: true,
|
|
14840
|
+
backgroundEligible: true,
|
|
14841
|
+
outOfBand: false,
|
|
14842
|
+
renderPriority: 10,
|
|
14843
|
+
recipientObligation: "Answer as soon as possible \u2014 the sender is waiting.",
|
|
14844
|
+
senderGuidance: "Blocking question. Only use when you need an answer to proceed."
|
|
14845
|
+
},
|
|
14846
|
+
assign: {
|
|
14847
|
+
category: "actionable",
|
|
14848
|
+
expectsReply: false,
|
|
14849
|
+
requiresAction: true,
|
|
14850
|
+
backgroundEligible: true,
|
|
14851
|
+
outOfBand: false,
|
|
14852
|
+
renderPriority: 10,
|
|
14853
|
+
recipientObligation: "Accept or decline; act on it when current operation allows.",
|
|
14854
|
+
senderGuidance: 'Task delegation. Must be directed to a specific recipient (not "*").'
|
|
14855
|
+
},
|
|
14856
|
+
steer: {
|
|
14857
|
+
category: "actionable",
|
|
14858
|
+
expectsReply: false,
|
|
14859
|
+
requiresAction: true,
|
|
14860
|
+
backgroundEligible: true,
|
|
14861
|
+
outOfBand: false,
|
|
14862
|
+
renderPriority: 0,
|
|
14863
|
+
// Always rendered first
|
|
14864
|
+
recipientObligation: "Pause current approach, adjust per instruction, then resume.",
|
|
14865
|
+
senderGuidance: "Mid-task direction change. The recipient is already working on something."
|
|
14866
|
+
},
|
|
14867
|
+
btw: {
|
|
14868
|
+
category: "informational",
|
|
14869
|
+
expectsReply: false,
|
|
14870
|
+
requiresAction: false,
|
|
14871
|
+
backgroundEligible: false,
|
|
14872
|
+
outOfBand: false,
|
|
14873
|
+
renderPriority: 30,
|
|
14874
|
+
recipientObligation: "Absorb the information and stay on current task; no reply needed.",
|
|
14875
|
+
senderGuidance: "Low-priority aside. Non-urgent info that can wait."
|
|
14876
|
+
},
|
|
14877
|
+
broadcast: {
|
|
14878
|
+
category: "routing",
|
|
14879
|
+
expectsReply: false,
|
|
14880
|
+
requiresAction: false,
|
|
14881
|
+
backgroundEligible: false,
|
|
14882
|
+
outOfBand: false,
|
|
14883
|
+
renderPriority: 20,
|
|
14884
|
+
recipientObligation: 'Read if addressed to you (direct recipient, alias, or "*").',
|
|
14885
|
+
senderGuidance: 'Multi-recipient envelope. Auto-selected when to is "*" or "@session".'
|
|
14886
|
+
},
|
|
14887
|
+
status: {
|
|
14888
|
+
category: "informational",
|
|
14889
|
+
expectsReply: false,
|
|
14890
|
+
requiresAction: false,
|
|
14891
|
+
backgroundEligible: false,
|
|
14892
|
+
outOfBand: false,
|
|
14893
|
+
renderPriority: 40,
|
|
14894
|
+
recipientObligation: "Use to avoid redundant work; never act on as a task or question.",
|
|
14895
|
+
senderGuidance: "Agent/system status update. Machine-generated, not for human-originated messages."
|
|
14896
|
+
},
|
|
14897
|
+
result: {
|
|
14898
|
+
category: "informational",
|
|
14899
|
+
expectsReply: false,
|
|
14900
|
+
requiresAction: false,
|
|
14901
|
+
backgroundEligible: true,
|
|
14902
|
+
outOfBand: false,
|
|
14903
|
+
renderPriority: 10,
|
|
14904
|
+
recipientObligation: "Factor into next decision; treat as evidence, not a new task.",
|
|
14905
|
+
senderGuidance: "Task completion notice. Share the outcome of finished work."
|
|
14906
|
+
},
|
|
14907
|
+
review: {
|
|
14908
|
+
category: "actionable",
|
|
14909
|
+
expectsReply: false,
|
|
14910
|
+
requiresAction: true,
|
|
14911
|
+
backgroundEligible: true,
|
|
14912
|
+
outOfBand: false,
|
|
14913
|
+
renderPriority: 10,
|
|
14914
|
+
recipientObligation: "Inspect when convenient; no immediate reply required.",
|
|
14915
|
+
senderGuidance: "Passive review request (code/doc/PR). No reply required."
|
|
14916
|
+
},
|
|
14917
|
+
control: {
|
|
14918
|
+
category: "control_signal",
|
|
14919
|
+
expectsReply: false,
|
|
14920
|
+
requiresAction: false,
|
|
14921
|
+
backgroundEligible: false,
|
|
14922
|
+
outOfBand: true,
|
|
14923
|
+
renderPriority: 999,
|
|
14924
|
+
// Never rendered
|
|
14925
|
+
recipientObligation: 'Handled by the agent loop, NOT folded into conversation. "interrupt" causes cooperative halt.',
|
|
14926
|
+
senderGuidance: "RESERVED for runtime use. Agents must NOT send control messages."
|
|
14927
|
+
}
|
|
14928
|
+
};
|
|
14929
|
+
function validateSendType(type, to) {
|
|
14930
|
+
if (type === "control") {
|
|
14931
|
+
throw new TypeError(
|
|
14932
|
+
'Type "control" is reserved for runtime use and cannot be set by agents'
|
|
14933
|
+
);
|
|
14934
|
+
}
|
|
14935
|
+
const isMultiRecipient = to === "*" || to.startsWith("@session:");
|
|
14936
|
+
if (type === "assign" && isMultiRecipient) {
|
|
14937
|
+
throw new TypeError(
|
|
14938
|
+
`Type "assign" requires a specific recipient \u2014 multi-recipient target "${to}" is ambiguous`
|
|
14939
|
+
);
|
|
14940
|
+
}
|
|
14941
|
+
if (type === "steer" && isMultiRecipient) {
|
|
14942
|
+
throw new TypeError(
|
|
14943
|
+
`Type "steer" requires a specific recipient \u2014 multi-recipient target "${to}" is ambiguous`
|
|
14944
|
+
);
|
|
14945
|
+
}
|
|
14946
|
+
}
|
|
14947
|
+
var SESSION_RECIPIENT_PREFIX = "@session:";
|
|
14948
|
+
function sessionRecipient(sessionId) {
|
|
14949
|
+
const normalizedSessionId = sessionId.trim();
|
|
14950
|
+
if (!normalizedSessionId) {
|
|
14951
|
+
throw new TypeError('sessionId is required for the "@session" recipient');
|
|
14952
|
+
}
|
|
14953
|
+
return `${SESSION_RECIPIENT_PREFIX}${normalizedSessionId}`;
|
|
14954
|
+
}
|
|
14955
|
+
function normalizeRecipient(to, sessionId) {
|
|
14956
|
+
const trimmed = to.trim();
|
|
14957
|
+
const normalized = trimmed.toLowerCase();
|
|
14958
|
+
if (normalized === "all") return "*";
|
|
14959
|
+
if (normalized === "@session") return sessionRecipient(sessionId ?? "");
|
|
14960
|
+
return trimmed;
|
|
14961
|
+
}
|
|
14962
|
+
|
|
14459
14963
|
// src/coordination/mailbox-message-codec.ts
|
|
14460
14964
|
var MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
14461
14965
|
"note",
|
|
@@ -14512,6 +15016,19 @@ function parseMessageType(value) {
|
|
|
14512
15016
|
function normalizeMailboxMessageType(value) {
|
|
14513
15017
|
return parseMessageType(value);
|
|
14514
15018
|
}
|
|
15019
|
+
function resolveSendType(type, to) {
|
|
15020
|
+
const normalizedTo = normalizeRecipient(to);
|
|
15021
|
+
const resolved = type ?? (normalizedTo === "*" || normalizedTo.startsWith("@session:") ? "broadcast" : "note");
|
|
15022
|
+
validateSendType(resolved, normalizedTo);
|
|
15023
|
+
return resolved;
|
|
15024
|
+
}
|
|
15025
|
+
function resolveSendTypeSafe(type, to) {
|
|
15026
|
+
try {
|
|
15027
|
+
return { ok: true, type: resolveSendType(type, to) };
|
|
15028
|
+
} catch (err) {
|
|
15029
|
+
return { ok: false, error: err.message };
|
|
15030
|
+
}
|
|
15031
|
+
}
|
|
14515
15032
|
function parsePriority(value) {
|
|
14516
15033
|
if (typeof value === "string" && PRIORITIES.has(value)) {
|
|
14517
15034
|
return value;
|
|
@@ -14623,23 +15140,6 @@ function serializeAckRecord(ack) {
|
|
|
14623
15140
|
return JSON.stringify(ack) + "\n";
|
|
14624
15141
|
}
|
|
14625
15142
|
|
|
14626
|
-
// src/coordination/mailbox-types.ts
|
|
14627
|
-
var SESSION_RECIPIENT_PREFIX = "@session:";
|
|
14628
|
-
function sessionRecipient(sessionId) {
|
|
14629
|
-
const normalizedSessionId = sessionId.trim();
|
|
14630
|
-
if (!normalizedSessionId) {
|
|
14631
|
-
throw new TypeError('sessionId is required for the "@session" recipient');
|
|
14632
|
-
}
|
|
14633
|
-
return `${SESSION_RECIPIENT_PREFIX}${normalizedSessionId}`;
|
|
14634
|
-
}
|
|
14635
|
-
function normalizeRecipient(to, sessionId) {
|
|
14636
|
-
const trimmed = to.trim();
|
|
14637
|
-
const normalized = trimmed.toLowerCase();
|
|
14638
|
-
if (normalized === "all") return "*";
|
|
14639
|
-
if (normalized === "@session") return sessionRecipient(sessionId ?? "");
|
|
14640
|
-
return trimmed;
|
|
14641
|
-
}
|
|
14642
|
-
|
|
14643
15143
|
// src/coordination/global-mailbox.ts
|
|
14644
15144
|
var MAILBOX_FILE = "_mailbox.jsonl";
|
|
14645
15145
|
var CLIENT_REGISTRY_FILE = "_mailbox.clients.json";
|
|
@@ -15948,7 +16448,7 @@ function makeMailboxTool(opts = {}) {
|
|
|
15948
16448
|
description: "Which mailbox operation to perform."
|
|
15949
16449
|
},
|
|
15950
16450
|
to: { type: "string", description: "Recipient agent id, base alias, '@session' for the sender's session, or '*' / 'all' for project broadcast." },
|
|
15951
|
-
type: { type: "string", enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result", "review", "control"], description: "
|
|
16451
|
+
type: { type: "string", enum: ["note", "ask", "assign", "steer", "btw", "broadcast", "status", "result", "review", "control"], description: "Required message intent. Actionable: ask (blocking question), assign (task delegation, must have specific to), steer (mid-course direction), review (passive ask). Informational: note (general FYI), btw (low-priority aside), result (completion notice), status (system update). Routing: broadcast (multi-recipient). Reserved: control (runtime only, agents cannot send)." },
|
|
15952
16452
|
subject: { type: "string", description: "Short subject line." },
|
|
15953
16453
|
body: { type: "string", description: "Full message content." },
|
|
15954
16454
|
priority: { type: "string", enum: ["low", "normal", "high"] },
|
|
@@ -16058,10 +16558,18 @@ async function executeSend(mb, agentId, sessionId, i) {
|
|
|
16058
16558
|
if (!tp) return { ok: false, error: '"type" is required.' };
|
|
16059
16559
|
if (!subject) return { ok: false, error: '"subject" is required.' };
|
|
16060
16560
|
if (body === void 0 || body === null) return { ok: false, error: '"body" is required.' };
|
|
16561
|
+
let normalizedTo;
|
|
16562
|
+
try {
|
|
16563
|
+
normalizedTo = normalizeRecipient(to, sessionId);
|
|
16564
|
+
} catch (err) {
|
|
16565
|
+
return { ok: false, error: `"to" is invalid: ${toErrorMessage(err)}` };
|
|
16566
|
+
}
|
|
16567
|
+
const typeResult = resolveSendTypeSafe(tp, normalizedTo);
|
|
16568
|
+
if (!typeResult.ok) return { ok: false, error: `"type" is invalid: ${typeResult.error}` };
|
|
16061
16569
|
const msg = await mb.send({
|
|
16062
16570
|
from: agentId,
|
|
16063
|
-
to,
|
|
16064
|
-
type:
|
|
16571
|
+
to: normalizedTo,
|
|
16572
|
+
type: typeResult.type,
|
|
16065
16573
|
subject,
|
|
16066
16574
|
body,
|
|
16067
16575
|
priority: i.priority ?? "normal",
|
|
@@ -17163,18 +17671,7 @@ function optionalBoolean(object, key) {
|
|
|
17163
17671
|
}
|
|
17164
17672
|
return value;
|
|
17165
17673
|
}
|
|
17166
|
-
var VALID_TYPES =
|
|
17167
|
-
"note",
|
|
17168
|
-
"ask",
|
|
17169
|
-
"assign",
|
|
17170
|
-
"steer",
|
|
17171
|
-
"btw",
|
|
17172
|
-
"broadcast",
|
|
17173
|
-
"status",
|
|
17174
|
-
"result",
|
|
17175
|
-
"review",
|
|
17176
|
-
"control"
|
|
17177
|
-
]);
|
|
17674
|
+
var VALID_TYPES = new Set(Object.keys(MAILBOX_TYPE_PROPERTIES));
|
|
17178
17675
|
var VALID_PRIORITIES = /* @__PURE__ */ new Set(["low", "normal", "high"]);
|
|
17179
17676
|
var RESERVED_FROM_IDS = /* @__PURE__ */ new Set([
|
|
17180
17677
|
"leader",
|
|
@@ -17219,9 +17716,15 @@ function validateSend(body) {
|
|
|
17219
17716
|
`field "from" must not use reserved internal agent id "${fromBase}" \u2014 external agents must use their own identity`
|
|
17220
17717
|
);
|
|
17221
17718
|
}
|
|
17719
|
+
const to = normalizeRecipient(requireString(object, "to"));
|
|
17720
|
+
try {
|
|
17721
|
+
resolveSendType(type, to);
|
|
17722
|
+
} catch (err) {
|
|
17723
|
+
throw validationError(`field "type" is invalid: ${err.message}`);
|
|
17724
|
+
}
|
|
17222
17725
|
const result = {
|
|
17223
17726
|
from,
|
|
17224
|
-
to
|
|
17727
|
+
to,
|
|
17225
17728
|
type,
|
|
17226
17729
|
subject: requireString(object, "subject"),
|
|
17227
17730
|
body: requireString(object, "body"),
|
|
@@ -17455,7 +17958,7 @@ function makeMailSendTool(opts = {}) {
|
|
|
17455
17958
|
const resolveMailbox = makeResolver(opts);
|
|
17456
17959
|
return {
|
|
17457
17960
|
name: "mail_send",
|
|
17458
|
-
description: 'Send mail to any agent on this canonical project, across clients, processes, sessions, branches, and linked Git worktrees (CLI, TUI, WebUI, ACP/MCP/HTTP). Use it to hand off work, ask questions, announce what you just did, or request a review
|
|
17961
|
+
description: 'Send mail to any agent on this canonical project, across clients, processes, sessions, branches, and linked Git worktrees (CLI, TUI, WebUI, ACP/MCP/HTTP). Use it to hand off work, ask questions, announce what you just did, or request a review. to="*" broadcasts to everyone; to="@session" reaches agents in your current session; to="leader" reaches every leader process; an exact id like "leader@a1b2c3d4" reaches one agent. Use project-wide scope (`*` or a base alias) whenever another session may be affected. Recipients see your mail automatically before their next step. \n\n\u2500\u2500 TYPE SEMANTICS \u2500\u2500\nThe `type` parameter determines how the recipient must handle your message:\n actionable (requires a response):\n ask \u2014 blocking question, sender waits for an answer\n assign \u2014 task delegation, act when current op allows (requires specific to, NOT "*")\n steer \u2014 mid-task direction change, recipient adjusts course NOW\n review \u2014 passive ask, inspect when convenient, no reply needed\n informational (consume for context):\n note \u2014 general FYI (default for directed sends)\n btw \u2014 low-priority aside, absorb and stay on task\n result \u2014 subagent completion notice, factor into next decision\n status \u2014 agent/system status update, avoid redundant work\n broadcast \u2014 multi-recipient envelope (default when to="*" or "@session")\n control (reserved for runtime use \u2014 agents cannot send this type)\n\nWhen no `type` is provided: broadcast for `*`/`@session`, otherwise note.\nassign/steer with to="*" is rejected (ambiguous). control is rejected (runtime-reserved).\n\nType determines dispatch: steer renders first, control is out-of-band, actionable types show "Action required" footer.',
|
|
17459
17962
|
usageHint: 'mail_send to="<id>" type="review" body="please skim <file>"',
|
|
17460
17963
|
category: "Coordination",
|
|
17461
17964
|
permission: "auto",
|
|
@@ -17491,11 +17994,14 @@ function makeMailSendTool(opts = {}) {
|
|
|
17491
17994
|
const mb = resolveMailbox(ctx);
|
|
17492
17995
|
const identity = await register(mb, ctx);
|
|
17493
17996
|
const to = normalizeRecipient(rawTo, identity.sessionId);
|
|
17494
|
-
const
|
|
17997
|
+
const resolvedType = resolveSendType(
|
|
17998
|
+
i.type,
|
|
17999
|
+
to
|
|
18000
|
+
);
|
|
17495
18001
|
const msg = await mb.send({
|
|
17496
18002
|
from: identity.callerId,
|
|
17497
18003
|
to,
|
|
17498
|
-
type,
|
|
18004
|
+
type: resolvedType,
|
|
17499
18005
|
subject,
|
|
17500
18006
|
body,
|
|
17501
18007
|
priority: i.priority ?? "normal",
|
|
@@ -18760,6 +19266,7 @@ var DEFAULTS2 = {
|
|
|
18760
19266
|
blockAfterRateLimitHits: 3,
|
|
18761
19267
|
blockAfterFailures: 5,
|
|
18762
19268
|
blockDurationMs: 3e5,
|
|
19269
|
+
quotaBlockDurationMs: 9e5,
|
|
18763
19270
|
recoverAfterSuccesses: 3,
|
|
18764
19271
|
maxErrorHistory: 50
|
|
18765
19272
|
};
|
|
@@ -18772,11 +19279,16 @@ var ProviderModelStatusTracker = class {
|
|
|
18772
19279
|
this.events = opts?.events;
|
|
18773
19280
|
}
|
|
18774
19281
|
// ── Public API ──────────────────────────────────────────────────────────
|
|
19282
|
+
/** Resolve the logical health/display identity without changing wire routing. */
|
|
19283
|
+
logicalIdentity(providerId, model) {
|
|
19284
|
+
return statusIdentity(providerId, model);
|
|
19285
|
+
}
|
|
18775
19286
|
/**
|
|
18776
19287
|
* Record a successful provider call. Resets consecutive failure counters
|
|
18777
19288
|
* and may transition out of degraded/blocked.
|
|
18778
19289
|
*/
|
|
18779
19290
|
recordSuccess(providerId, model, _meta) {
|
|
19291
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18780
19292
|
const key = pairKey(providerId, model);
|
|
18781
19293
|
const s = this.getOrCreate(key, providerId, model);
|
|
18782
19294
|
s.consecutiveFailures = 0;
|
|
@@ -18796,9 +19308,11 @@ var ProviderModelStatusTracker = class {
|
|
|
18796
19308
|
* @returns The new state after recording this failure.
|
|
18797
19309
|
*/
|
|
18798
19310
|
recordFailure(providerId, model, kind, status, message, meta) {
|
|
19311
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18799
19312
|
const key = pairKey(providerId, model);
|
|
18800
19313
|
const s = this.getOrCreate(key, providerId, model);
|
|
18801
19314
|
const now = Date.now();
|
|
19315
|
+
const previousExpiry = s.stateExpiresAt;
|
|
18802
19316
|
s.consecutiveFailures += 1;
|
|
18803
19317
|
s.totalFailures += 1;
|
|
18804
19318
|
s.consecutiveSuccesses = 0;
|
|
@@ -18811,6 +19325,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18811
19325
|
if (s.firstFailureAt === null) s.firstFailureAt = now;
|
|
18812
19326
|
switch (kind) {
|
|
18813
19327
|
case "rate_limit":
|
|
19328
|
+
case "quota_exhausted":
|
|
18814
19329
|
s.rateLimitHits += 1;
|
|
18815
19330
|
break;
|
|
18816
19331
|
case "overloaded":
|
|
@@ -18838,14 +19353,20 @@ var ProviderModelStatusTracker = class {
|
|
|
18838
19353
|
}
|
|
18839
19354
|
let newState = s.state;
|
|
18840
19355
|
let reason = "";
|
|
18841
|
-
|
|
19356
|
+
const quotaExhausted = kind === "quota_exhausted" || isQuotaExhausted(kind, status, message);
|
|
19357
|
+
if (quotaExhausted) {
|
|
19358
|
+
newState = "blocked";
|
|
19359
|
+
reason = "quota_exhausted";
|
|
19360
|
+
s.stateExpiresAt = now + this.cfg.quotaBlockDurationMs;
|
|
19361
|
+
}
|
|
19362
|
+
if (!quotaExhausted && s.state === "healthy") {
|
|
18842
19363
|
if (s.consecutiveFailures >= this.cfg.degradedAfterFailures) {
|
|
18843
19364
|
newState = "degraded";
|
|
18844
19365
|
reason = `consecutive_failures_${s.consecutiveFailures}`;
|
|
18845
19366
|
s.stateExpiresAt = now + this.cfg.degradedDurationMs;
|
|
18846
19367
|
}
|
|
18847
19368
|
}
|
|
18848
|
-
if (s.state === "degraded" || s.state === "healthy") {
|
|
19369
|
+
if (!quotaExhausted && (s.state === "degraded" || s.state === "healthy")) {
|
|
18849
19370
|
if (s.rateLimitHits >= this.cfg.blockAfterRateLimitHits) {
|
|
18850
19371
|
newState = "blocked";
|
|
18851
19372
|
reason = `rate_limit_threshold_${this.cfg.blockAfterRateLimitHits}`;
|
|
@@ -18857,7 +19378,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18857
19378
|
}
|
|
18858
19379
|
}
|
|
18859
19380
|
if (newState !== "healthy" && meta?.retryAfterMs && meta.retryAfterMs > 0) {
|
|
18860
|
-
const hintExpiry = now +
|
|
19381
|
+
const hintExpiry = now + meta.retryAfterMs;
|
|
18861
19382
|
if (s.stateExpiresAt === null || hintExpiry > s.stateExpiresAt) {
|
|
18862
19383
|
s.stateExpiresAt = hintExpiry;
|
|
18863
19384
|
}
|
|
@@ -18866,6 +19387,8 @@ var ProviderModelStatusTracker = class {
|
|
|
18866
19387
|
const oldState = s.state;
|
|
18867
19388
|
s.state = newState;
|
|
18868
19389
|
this.emitStatusChanged(providerId, model, oldState, newState, reason);
|
|
19390
|
+
} else if (s.state !== "healthy" && s.stateExpiresAt !== previousExpiry) {
|
|
19391
|
+
this.emitStatusChanged(providerId, model, s.state, s.state, "cooldown_extended");
|
|
18869
19392
|
}
|
|
18870
19393
|
return newState;
|
|
18871
19394
|
}
|
|
@@ -18876,6 +19399,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18876
19399
|
* kind is `auth` (no point retrying ever).
|
|
18877
19400
|
*/
|
|
18878
19401
|
isAvailable(providerId, model) {
|
|
19402
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18879
19403
|
const key = pairKey(providerId, model);
|
|
18880
19404
|
const s = this.map.get(key);
|
|
18881
19405
|
if (!s) return true;
|
|
@@ -18884,26 +19408,14 @@ var ProviderModelStatusTracker = class {
|
|
|
18884
19408
|
s.state = "healthy";
|
|
18885
19409
|
s.stateExpiresAt = null;
|
|
18886
19410
|
s.consecutiveFailures = Math.max(0, s.consecutiveFailures - 1);
|
|
18887
|
-
this.emitStatusChanged(
|
|
18888
|
-
providerId,
|
|
18889
|
-
model,
|
|
18890
|
-
oldState,
|
|
18891
|
-
"healthy",
|
|
18892
|
-
"cooldown_expired"
|
|
18893
|
-
);
|
|
19411
|
+
this.emitStatusChanged(providerId, model, oldState, "healthy", "cooldown_expired");
|
|
18894
19412
|
return true;
|
|
18895
19413
|
}
|
|
18896
19414
|
if (s.state === "degraded" && s.stateExpiresAt !== null && Date.now() >= s.stateExpiresAt) {
|
|
18897
19415
|
const oldState = s.state;
|
|
18898
19416
|
s.state = "healthy";
|
|
18899
19417
|
s.stateExpiresAt = null;
|
|
18900
|
-
this.emitStatusChanged(
|
|
18901
|
-
providerId,
|
|
18902
|
-
model,
|
|
18903
|
-
oldState,
|
|
18904
|
-
"healthy",
|
|
18905
|
-
"degraded_timeout_expired"
|
|
18906
|
-
);
|
|
19418
|
+
this.emitStatusChanged(providerId, model, oldState, "healthy", "degraded_timeout_expired");
|
|
18907
19419
|
return true;
|
|
18908
19420
|
}
|
|
18909
19421
|
return s.state !== "blocked";
|
|
@@ -18915,6 +19427,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18915
19427
|
* than skipped permanently.
|
|
18916
19428
|
*/
|
|
18917
19429
|
isRateLimited(providerId, model) {
|
|
19430
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18918
19431
|
const key = pairKey(providerId, model);
|
|
18919
19432
|
const s = this.map.get(key);
|
|
18920
19433
|
if (!s) return false;
|
|
@@ -18925,6 +19438,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18925
19438
|
* if no failures have been recorded.
|
|
18926
19439
|
*/
|
|
18927
19440
|
getStatus(providerId, model) {
|
|
19441
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18928
19442
|
const key = pairKey(providerId, model);
|
|
18929
19443
|
const s = this.map.get(key);
|
|
18930
19444
|
if (!s) return void 0;
|
|
@@ -18944,6 +19458,45 @@ var ProviderModelStatusTracker = class {
|
|
|
18944
19458
|
}
|
|
18945
19459
|
return out;
|
|
18946
19460
|
}
|
|
19461
|
+
/**
|
|
19462
|
+
* Move every expired waiting-room entry back to healthy. Runtime routing
|
|
19463
|
+
* already performs this check lazily; this explicit sweep lets a UI/status
|
|
19464
|
+
* timer refresh the room even while no model calls are being made.
|
|
19465
|
+
*
|
|
19466
|
+
* @returns Number of entries released by this sweep.
|
|
19467
|
+
*/
|
|
19468
|
+
sweepExpired() {
|
|
19469
|
+
let released = 0;
|
|
19470
|
+
for (const [key, s] of this.map) {
|
|
19471
|
+
if (s.state === "healthy" || s.stateExpiresAt === null || Date.now() < s.stateExpiresAt)
|
|
19472
|
+
continue;
|
|
19473
|
+
const [providerId, model] = unpairKey(key);
|
|
19474
|
+
const previous = s.state;
|
|
19475
|
+
s.state = "healthy";
|
|
19476
|
+
s.stateExpiresAt = null;
|
|
19477
|
+
s.consecutiveFailures = 0;
|
|
19478
|
+
s.consecutiveSuccesses = 0;
|
|
19479
|
+
this.emitStatusChanged(providerId, model, previous, "healthy", "waiting_room_expired");
|
|
19480
|
+
released += 1;
|
|
19481
|
+
}
|
|
19482
|
+
return released;
|
|
19483
|
+
}
|
|
19484
|
+
/**
|
|
19485
|
+
* Release one entry for an immediate half-open probe on its next real use.
|
|
19486
|
+
* History and totals are retained so operators do not lose diagnostics.
|
|
19487
|
+
*/
|
|
19488
|
+
retryNow(providerId, model) {
|
|
19489
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
19490
|
+
const s = this.map.get(pairKey(providerId, model));
|
|
19491
|
+
if (!s || s.state === "healthy") return false;
|
|
19492
|
+
const previous = s.state;
|
|
19493
|
+
s.state = "healthy";
|
|
19494
|
+
s.stateExpiresAt = null;
|
|
19495
|
+
s.consecutiveFailures = 0;
|
|
19496
|
+
s.consecutiveSuccesses = 0;
|
|
19497
|
+
this.emitStatusChanged(providerId, model, previous, "healthy", "manual_half_open");
|
|
19498
|
+
return true;
|
|
19499
|
+
}
|
|
18947
19500
|
/**
|
|
18948
19501
|
* Returns only currently blocked entries.
|
|
18949
19502
|
*/
|
|
@@ -18969,6 +19522,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18969
19522
|
* where we don't want to mutate state during a quick peek.
|
|
18970
19523
|
*/
|
|
18971
19524
|
isBlocked(providerId, model) {
|
|
19525
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18972
19526
|
const key = pairKey(providerId, model);
|
|
18973
19527
|
const s = this.map.get(key);
|
|
18974
19528
|
if (!s) return false;
|
|
@@ -18984,6 +19538,7 @@ var ProviderModelStatusTracker = class {
|
|
|
18984
19538
|
*/
|
|
18985
19539
|
clear(providerId, model) {
|
|
18986
19540
|
if (providerId && model) {
|
|
19541
|
+
({ providerId, model } = statusIdentity(providerId, model));
|
|
18987
19542
|
const key = pairKey(providerId, model);
|
|
18988
19543
|
const old = this.map.get(key);
|
|
18989
19544
|
if (old && old.state !== "healthy") {
|
|
@@ -19024,6 +19579,34 @@ var ProviderModelStatusTracker = class {
|
|
|
19024
19579
|
statuses: all
|
|
19025
19580
|
};
|
|
19026
19581
|
}
|
|
19582
|
+
/** Restore non-expired waiting-room entries from a previous process. */
|
|
19583
|
+
restoreSnapshot(value) {
|
|
19584
|
+
if (!value || typeof value !== "object") return 0;
|
|
19585
|
+
const statuses = value.statuses;
|
|
19586
|
+
if (!Array.isArray(statuses)) return 0;
|
|
19587
|
+
let restored = 0;
|
|
19588
|
+
const now = Date.now();
|
|
19589
|
+
for (const raw of statuses) {
|
|
19590
|
+
if (!raw || typeof raw !== "object") continue;
|
|
19591
|
+
const item = raw;
|
|
19592
|
+
if (typeof item["providerId"] !== "string" || typeof item["model"] !== "string") continue;
|
|
19593
|
+
if (item["state"] !== "blocked" && item["state"] !== "degraded") continue;
|
|
19594
|
+
if (typeof item["stateExpiresAt"] !== "number" || item["stateExpiresAt"] <= now) continue;
|
|
19595
|
+
const { providerId, model } = statusIdentity(item["providerId"], item["model"]);
|
|
19596
|
+
const s = this.getOrCreate(pairKey(providerId, model), providerId, model);
|
|
19597
|
+
s.state = item["state"];
|
|
19598
|
+
s.stateExpiresAt = item["stateExpiresAt"];
|
|
19599
|
+
s.consecutiveFailures = safeCount(item["consecutiveFailures"]);
|
|
19600
|
+
s.totalFailures = safeCount(item["totalFailures"]);
|
|
19601
|
+
s.rateLimitHits = safeCount(item["rateLimitHits"]);
|
|
19602
|
+
s.lastFailureAt = safeTimestamp(item["lastFailureAt"]);
|
|
19603
|
+
s.lastErrorStatus = safeNullableNumber(item["lastErrorStatus"]);
|
|
19604
|
+
s.lastErrorMessage = typeof item["lastErrorMessage"] === "string" ? item["lastErrorMessage"] : null;
|
|
19605
|
+
s.lastErrorKind = isProviderErrorKind(item["lastErrorKind"]) ? item["lastErrorKind"] : null;
|
|
19606
|
+
restored += 1;
|
|
19607
|
+
}
|
|
19608
|
+
return restored;
|
|
19609
|
+
}
|
|
19027
19610
|
// ── Private helpers ─────────────────────────────────────────────────────
|
|
19028
19611
|
getOrCreate(key, _providerId, _model) {
|
|
19029
19612
|
let s = this.map.get(key);
|
|
@@ -19088,13 +19671,23 @@ var ProviderModelStatusTracker = class {
|
|
|
19088
19671
|
oldState,
|
|
19089
19672
|
newState,
|
|
19090
19673
|
reason,
|
|
19091
|
-
timestamp: Date.now()
|
|
19674
|
+
timestamp: Date.now(),
|
|
19675
|
+
stateExpiresAt: this.map.get(pairKey(providerId, model))?.stateExpiresAt ?? void 0
|
|
19092
19676
|
});
|
|
19093
19677
|
} catch {
|
|
19094
19678
|
}
|
|
19095
19679
|
}
|
|
19096
19680
|
};
|
|
19097
19681
|
var KEY_SEP = "\0";
|
|
19682
|
+
function statusIdentity(providerId, model) {
|
|
19683
|
+
if (providerId !== "omniroute") return { providerId, model };
|
|
19684
|
+
const slash = model.indexOf("/");
|
|
19685
|
+
if (slash <= 0 || slash === model.length - 1) return { providerId, model };
|
|
19686
|
+
return {
|
|
19687
|
+
providerId: model.slice(0, slash),
|
|
19688
|
+
model: model.slice(slash + 1)
|
|
19689
|
+
};
|
|
19690
|
+
}
|
|
19098
19691
|
function pairKey(providerId, model) {
|
|
19099
19692
|
return `${providerId}${KEY_SEP}${model}`;
|
|
19100
19693
|
}
|
|
@@ -19103,6 +19696,39 @@ function unpairKey(key) {
|
|
|
19103
19696
|
if (idx === -1) return [key, ""];
|
|
19104
19697
|
return [key.slice(0, idx), key.slice(idx + 1)];
|
|
19105
19698
|
}
|
|
19699
|
+
var QUOTA_EXHAUSTED_RE2 = /(?:insufficient|exhausted|depleted|exceeded|no|not enough)[-_\s]*(?:quota|credit|balance)|(?:quota|credit|balance)[-_\s]*(?:exhausted|depleted|exceeded|insufficient)|billing[_\s-]*(?:hard[_\s-]*)?limit|payment required|spending limit|plan limit/i;
|
|
19700
|
+
function isQuotaExhausted(kind, status, message) {
|
|
19701
|
+
if (status === 402) return true;
|
|
19702
|
+
if (kind !== "rate_limit" && kind !== "quota_exhausted" && kind !== "invalid_request" && kind !== "auth")
|
|
19703
|
+
return false;
|
|
19704
|
+
return QUOTA_EXHAUSTED_RE2.test(message);
|
|
19705
|
+
}
|
|
19706
|
+
function safeCount(value) {
|
|
19707
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0;
|
|
19708
|
+
}
|
|
19709
|
+
function safeTimestamp(value) {
|
|
19710
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : null;
|
|
19711
|
+
}
|
|
19712
|
+
function safeNullableNumber(value) {
|
|
19713
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
19714
|
+
}
|
|
19715
|
+
var PROVIDER_ERROR_KINDS = /* @__PURE__ */ new Set([
|
|
19716
|
+
"rate_limit",
|
|
19717
|
+
"quota_exhausted",
|
|
19718
|
+
"overloaded",
|
|
19719
|
+
"server",
|
|
19720
|
+
"timeout",
|
|
19721
|
+
"network",
|
|
19722
|
+
"stream_hang",
|
|
19723
|
+
"auth",
|
|
19724
|
+
"context_overflow",
|
|
19725
|
+
"content_filter",
|
|
19726
|
+
"invalid_request",
|
|
19727
|
+
"unknown"
|
|
19728
|
+
]);
|
|
19729
|
+
function isProviderErrorKind(value) {
|
|
19730
|
+
return typeof value === "string" && PROVIDER_ERROR_KINDS.has(value);
|
|
19731
|
+
}
|
|
19106
19732
|
|
|
19107
19733
|
// src/coordination/adaptive-concurrency.ts
|
|
19108
19734
|
var DEFAULT_CONFIG = Object.freeze({
|
|
@@ -22226,7 +22852,6 @@ export {
|
|
|
22226
22852
|
ACP_AGENTS,
|
|
22227
22853
|
AGENTS_BY_PHASE,
|
|
22228
22854
|
AGENT_CATALOG,
|
|
22229
|
-
TOOLS as AGENT_TOOL_PRESETS,
|
|
22230
22855
|
ALL_AGENT_DEFINITIONS,
|
|
22231
22856
|
ALL_FLEET_AGENTS,
|
|
22232
22857
|
AUDIT_LOG_AGENT,
|
|
@@ -22348,11 +22973,8 @@ export {
|
|
|
22348
22973
|
makeDependencyWatcherConfig,
|
|
22349
22974
|
makeDirectorSessionFactory,
|
|
22350
22975
|
makeFleetEmitTool,
|
|
22351
|
-
makeFleetTool as makeFleetHealthTool,
|
|
22352
|
-
makeFleetTool as makeFleetSessionTool,
|
|
22353
22976
|
makeFleetStatusTool,
|
|
22354
22977
|
makeFleetTool,
|
|
22355
|
-
makeFleetTool as makeFleetUsageTool,
|
|
22356
22978
|
makeKanbanQueueTool,
|
|
22357
22979
|
makeLLMClassifier,
|
|
22358
22980
|
makeMailInboxTool,
|