agent-relay-server 0.106.0 → 0.108.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/docs/openapi.json +503 -1
- package/package.json +6 -5
- package/public/assets/{MessageBubble-CPYQQLDI.js → MessageBubble-DYJezSjU.js} +2 -2
- package/public/assets/{MessageBubble-CPYQQLDI.js.map → MessageBubble-DYJezSjU.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-B7xZt5DI.js → PlanGraphPanel-MWcVw38W.js} +2 -2
- package/public/assets/{PlanGraphPanel-B7xZt5DI.js.map → PlanGraphPanel-MWcVw38W.js.map} +1 -1
- package/public/assets/{activity-UKQrQ8_0.js → activity-Db3X3dg8.js} +2 -2
- package/public/assets/{activity-UKQrQ8_0.js.map → activity-Db3X3dg8.js.map} +1 -1
- package/public/assets/{agent-profiles-Zr_5nNEJ.js → agent-profiles-Di2w5U1o.js} +2 -2
- package/public/assets/{agent-profiles-Zr_5nNEJ.js.map → agent-profiles-Di2w5U1o.js.map} +1 -1
- package/public/assets/{agents-FdrfmJsj.js → agents-GSJXvmU1.js} +2 -2
- package/public/assets/{agents-FdrfmJsj.js.map → agents-GSJXvmU1.js.map} +1 -1
- package/public/assets/{analytics-VximqXQ9.js → analytics-B9USQLl5.js} +18 -18
- package/public/assets/{analytics-VximqXQ9.js.map → analytics-B9USQLl5.js.map} +1 -1
- package/public/assets/{automation-DlEcJvgu.js → automation-XRePs1dT.js} +2 -2
- package/public/assets/{automation-DlEcJvgu.js.map → automation-XRePs1dT.js.map} +1 -1
- package/public/assets/{branch-state-badge-CcLZ_u86.js → branch-state-badge-C_VNyER3.js} +2 -2
- package/public/assets/{branch-state-badge-CcLZ_u86.js.map → branch-state-badge-C_VNyER3.js.map} +1 -1
- package/public/assets/{channels-CcSnLzCG.js → channels-DACvNoC2.js} +2 -2
- package/public/assets/{channels-CcSnLzCG.js.map → channels-DACvNoC2.js.map} +1 -1
- package/public/assets/{chat-D1bunuI-.js → chat-D_0uHgB6.js} +2 -2
- package/public/assets/{chat-D1bunuI-.js.map → chat-D_0uHgB6.js.map} +1 -1
- package/public/assets/{connectors-DbPOlQhL.js → connectors-C0B38LUK.js} +2 -2
- package/public/assets/{connectors-DbPOlQhL.js.map → connectors-C0B38LUK.js.map} +1 -1
- package/public/assets/display-1J__Ti1K.js +3 -0
- package/public/assets/display-1J__Ti1K.js.map +1 -0
- package/public/assets/{formatted-body-impl-4v9SkUD1.js → formatted-body-impl-BKWCAjBd.js} +2 -2
- package/public/assets/{formatted-body-impl-4v9SkUD1.js.map → formatted-body-impl-BKWCAjBd.js.map} +1 -1
- package/public/assets/index-DlBSuzd2.js +23 -0
- package/public/assets/index-DlBSuzd2.js.map +1 -0
- package/public/assets/{insights-CntgHcTp.js → insights-BzNoYvDe.js} +2 -2
- package/public/assets/{insights-CntgHcTp.js.map → insights-BzNoYvDe.js.map} +1 -1
- package/public/assets/{integrations-hkSW8CzM.js → integrations-DLzJpt9G.js} +2 -2
- package/public/assets/{integrations-hkSW8CzM.js.map → integrations-DLzJpt9G.js.map} +1 -1
- package/public/assets/{maintenance-Dw7frQtu.js → maintenance-B6_0-ECm.js} +2 -2
- package/public/assets/{maintenance-Dw7frQtu.js.map → maintenance-B6_0-ECm.js.map} +1 -1
- package/public/assets/{managed-agents-Bj--bFmZ.js → managed-agents-CpzKXDtV.js} +2 -2
- package/public/assets/{managed-agents-Bj--bFmZ.js.map → managed-agents-CpzKXDtV.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-DGLCocU8.js → markdown-preview-impl-DxBnlJ2k.js} +2 -2
- package/public/assets/{markdown-preview-impl-DGLCocU8.js.map → markdown-preview-impl-DxBnlJ2k.js.map} +1 -1
- package/public/assets/{memory-yj2MIxUA.js → memory-Bnm7ILWL.js} +2 -2
- package/public/assets/{memory-yj2MIxUA.js.map → memory-Bnm7ILWL.js.map} +1 -1
- package/public/assets/{messages-BT5mL7iz.js → messages-D9LkocTc.js} +2 -2
- package/public/assets/{messages-BT5mL7iz.js.map → messages-D9LkocTc.js.map} +1 -1
- package/public/assets/{orchestrators-BVDAhrPk.js → orchestrators-C__JfrNr.js} +2 -2
- package/public/assets/{orchestrators-BVDAhrPk.js.map → orchestrators-C__JfrNr.js.map} +1 -1
- package/public/assets/{overview-DlqJIqYK.js → overview-BQ0b0V7Q.js} +2 -2
- package/public/assets/{overview-DlqJIqYK.js.map → overview-BQ0b0V7Q.js.map} +1 -1
- package/public/assets/{pairs-DQ4tNCo3.js → pairs-ChyeBs0Z.js} +2 -2
- package/public/assets/{pairs-DQ4tNCo3.js.map → pairs-ChyeBs0Z.js.map} +1 -1
- package/public/assets/{projects-vt63bwHN.js → projects-v5Kp1Oaq.js} +2 -2
- package/public/assets/{projects-vt63bwHN.js.map → projects-v5Kp1Oaq.js.map} +1 -1
- package/public/assets/{prompt-settings-DlsqOP0C.js → prompt-settings-YhAOlYc6.js} +2 -2
- package/public/assets/{prompt-settings-DlsqOP0C.js.map → prompt-settings-YhAOlYc6.js.map} +1 -1
- package/public/assets/{registry-Cw2fdWrn.js → registry-DZoAfrzx.js} +2 -2
- package/public/assets/{registry-Cw2fdWrn.js.map → registry-DZoAfrzx.js.map} +1 -1
- package/public/assets/{security-ucr7IT1V.js → security-DRCj00eA.js} +2 -2
- package/public/assets/{security-ucr7IT1V.js.map → security-DRCj00eA.js.map} +1 -1
- package/public/assets/{select-D8UWCtYZ.js → select-ZOpLw2Fd.js} +2 -2
- package/public/assets/{select-D8UWCtYZ.js.map → select-ZOpLw2Fd.js.map} +1 -1
- package/public/assets/settings-DGgwr9WH.js +6 -0
- package/public/assets/settings-DGgwr9WH.js.map +1 -0
- package/public/assets/{store-DUfrGvOE.js → store-Bnag1kY-.js} +2 -2
- package/public/assets/{store-DUfrGvOE.js.map → store-Bnag1kY-.js.map} +1 -1
- package/public/assets/{tasks-jRUvxUd5.js → tasks-DUTu8ZhA.js} +2 -2
- package/public/assets/{tasks-jRUvxUd5.js.map → tasks-DUTu8ZhA.js.map} +1 -1
- package/public/assets/{teams-Dn0yggD-.js → teams-BJpaoG2m.js} +2 -2
- package/public/assets/{teams-Dn0yggD-.js.map → teams-BJpaoG2m.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-fLly20IP.js → terminal-viewer-impl-CQhh6oXa.js} +2 -2
- package/public/assets/{terminal-viewer-impl-fLly20IP.js.map → terminal-viewer-impl-CQhh6oXa.js.map} +1 -1
- package/public/assets/types-BiJFuTiL.js +2 -0
- package/public/assets/types-BiJFuTiL.js.map +1 -0
- package/public/assets/{user-avatar-BWLUHt2e.js → user-avatar-BYGrUx1U.js} +2 -2
- package/public/assets/{user-avatar-BWLUHt2e.js.map → user-avatar-BYGrUx1U.js.map} +1 -1
- package/public/assets/{work-queue-BAnNY0xE.js → work-queue-BH2-mARc.js} +2 -2
- package/public/assets/{work-queue-BAnNY0xE.js.map → work-queue-BH2-mARc.js.map} +1 -1
- package/public/assets/{workspaces-CIsQqQby.js → workspaces-DldYyXZR.js} +2 -2
- package/public/assets/{workspaces-CIsQqQby.js.map → workspaces-DldYyXZR.js.map} +1 -1
- package/public/index.html +4 -4
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/runner/plugins/claude/monitors/relay-monitor.provisioned.mjs +6 -0
- package/src/branch-landed.ts +93 -4
- package/src/config-store.ts +12 -1
- package/src/db/agents.ts +3 -2
- package/src/db/continuations.ts +13 -3
- package/src/db/index.ts +3 -0
- package/src/db/issue-lifecycle.ts +12 -0
- package/src/db/mappers.ts +17 -0
- package/src/db/messages.ts +9 -4
- package/src/db/migrations.ts +91 -1
- package/src/db/notification-subscriptions.ts +1 -1
- package/src/db/prompt-templates.ts +19 -0
- package/src/db/provider-quotas.ts +6 -0
- package/src/db/schema.ts +67 -1
- package/src/db/stats.ts +3 -2
- package/src/db/task-comment-through.ts +112 -0
- package/src/db/task-core.ts +14 -1
- package/src/db/task-entity.ts +629 -0
- package/src/db/task-maintenance.ts +4 -3
- package/src/db/task-memory.ts +180 -0
- package/src/db/tasks.ts +24 -10
- package/src/issue-tracker/adapter.ts +15 -0
- package/src/issue-tracker/github.ts +40 -2
- package/src/issue-tracker/index.ts +1 -0
- package/src/mcp/index.ts +12 -1
- package/src/mcp/tools-spawn.ts +4 -0
- package/src/mcp-dispatch-tools.ts +164 -0
- package/src/mcp-notification-subscription-tools.ts +62 -4
- package/src/mcp-plan-tools.ts +30 -3
- package/src/mcp-release-tools.ts +33 -0
- package/src/mcp-task-memory-tools.ts +132 -0
- package/src/mcp-task-tools.ts +232 -0
- package/src/mcp-task-weave-tools.ts +66 -0
- package/src/notification-router.ts +24 -2
- package/src/notification-rules.ts +4 -0
- package/src/notification-types.ts +25 -0
- package/src/prompt-resolver.ts +66 -3
- package/src/routes/agents-spawn.ts +10 -0
- package/src/routes/commands.ts +17 -1
- package/src/routes/index.ts +16 -2
- package/src/routes/plans.ts +10 -2
- package/src/routes/provider-quotas.ts +13 -1
- package/src/routes/tasks.ts +276 -2
- package/src/routing-policy-store.ts +147 -2
- package/src/security.ts +2 -0
- package/src/self-resume.ts +13 -2
- package/src/services/issue-lifecycle.ts +9 -1
- package/src/services/plan-graph.ts +107 -9
- package/src/services/release-readiness.ts +142 -0
- package/src/services/send-message.ts +31 -0
- package/src/services/spawn-agent.ts +81 -4
- package/src/services/task-context-hydration.ts +361 -0
- package/src/services/task-entity.ts +149 -0
- package/src/services/task-issue-weave.ts +558 -0
- package/src/services/task-lifecycle.ts +465 -0
- package/src/services/task-plan-view.ts +178 -0
- package/src/services/task-semantic-events.ts +137 -0
- package/src/services/transient-agent-reaper.ts +71 -23
- package/src/spawn-targets.ts +27 -2
- package/src/task-dispatcher.ts +461 -0
- package/src/upgrade-install-verify.ts +20 -1
- package/public/assets/display-BWi8DQCp.js +0 -3
- package/public/assets/display-BWi8DQCp.js.map +0 -1
- package/public/assets/index-Vmhw442d.js +0 -23
- package/public/assets/index-Vmhw442d.js.map +0 -1
- package/public/assets/settings-Dvf_ps1B.js +0 -6
- package/public/assets/settings-Dvf_ps1B.js.map +0 -1
- package/public/assets/types-DorDDvEb.js +0 -2
- package/public/assets/types-DorDDvEb.js.map +0 -1
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
// #839 — the declarative task dispatcher.
|
|
2
|
+
//
|
|
3
|
+
// A pure function from (Task work-shape + live system state) → a concrete spawn
|
|
4
|
+
// (provider / model / effort) plus a human-readable rationale. The work-shape is the
|
|
5
|
+
// small, stable taxonomy a coordinator declares on the Task's `dispatch` field
|
|
6
|
+
// ({complexity, type, contextFootprint, criticality}); the routing heuristics that map
|
|
7
|
+
// it onto an agent live as INSPECTABLE, EDITABLE policy (RoutingPolicy.dispatch, with a
|
|
8
|
+
// built-in default) — never hardcoded here, never tribal coordinator knowledge.
|
|
9
|
+
//
|
|
10
|
+
// Guarantees this module is built to keep (the #839 GUARD):
|
|
11
|
+
// 1. Every decision reports WHY (rationale string + structured factors).
|
|
12
|
+
// 2. Every decision is overridable by an explicit model/provider pin.
|
|
13
|
+
// 3. The function is PURE — no DB writes, no spawning. The MCP entrypoint
|
|
14
|
+
// (mcp-dispatch-tools.ts) consumes the decision; #838 (spawn-with-task) will
|
|
15
|
+
// hydrate + actually spawn. Auto-review surfaces as a PLAN the caller may enact.
|
|
16
|
+
import type {
|
|
17
|
+
DispatchAutoReview,
|
|
18
|
+
DispatchMatch,
|
|
19
|
+
DispatchModelTarget,
|
|
20
|
+
DispatchPolicy,
|
|
21
|
+
DispatchRule,
|
|
22
|
+
SpawnProvider,
|
|
23
|
+
TaskDispatch,
|
|
24
|
+
} from "agent-relay-sdk";
|
|
25
|
+
import type { ProviderCatalogEntry, ProviderModelCatalogEntry } from "agent-relay-sdk/provider-catalog";
|
|
26
|
+
import { effectiveProviderCatalogList } from "./provider-catalog-store";
|
|
27
|
+
import { getDispatchPolicy } from "./routing-policy-store";
|
|
28
|
+
import { providerQuotaBands, type ProviderQuotaBand, type QuotaBand } from "./spawn-targets";
|
|
29
|
+
|
|
30
|
+
/** An explicit per-case override. Any field set wins over the policy-derived target. */
|
|
31
|
+
export interface DispatchPin {
|
|
32
|
+
provider?: SpawnProvider;
|
|
33
|
+
model?: string;
|
|
34
|
+
effort?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Live-state inputs. All optional + injectable so the dispatcher is unit-testable without a
|
|
39
|
+
* running fleet; each defaults to the real source (policy store / quota bands / catalog).
|
|
40
|
+
*/
|
|
41
|
+
export interface DispatchContext {
|
|
42
|
+
policy?: DispatchPolicy;
|
|
43
|
+
bands?: ProviderQuotaBand[];
|
|
44
|
+
catalog?: ProviderCatalogEntry[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface DispatchTarget {
|
|
48
|
+
provider: SpawnProvider;
|
|
49
|
+
model?: string;
|
|
50
|
+
effort?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Structured trace of the inputs that produced the decision (the machine-readable rationale). */
|
|
54
|
+
export interface DispatchFactors {
|
|
55
|
+
complexity?: string;
|
|
56
|
+
type?: string;
|
|
57
|
+
contextFootprint?: string;
|
|
58
|
+
criticality?: string;
|
|
59
|
+
/** Label of the matched rule, or a "fallback"/"pin" marker. */
|
|
60
|
+
matchedRule: string;
|
|
61
|
+
/** The final provider's live quota band. */
|
|
62
|
+
band: QuotaBand;
|
|
63
|
+
/** Context window of the chosen model, when known. */
|
|
64
|
+
contextWindowTokens?: number;
|
|
65
|
+
/** Model was escalated to a bigger-window family member for a large footprint. */
|
|
66
|
+
escalatedForFootprint?: boolean;
|
|
67
|
+
/** Provider was rerouted away from a scarce/blocked pool; original provider recorded here. */
|
|
68
|
+
reroutedFromProvider?: SpawnProvider;
|
|
69
|
+
/** Which target fields were set by an explicit pin. */
|
|
70
|
+
pinnedFields?: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** A dependent review Task the caller may create (high criticality / complex work). */
|
|
74
|
+
export interface ReviewPlan {
|
|
75
|
+
/** Work-shape for the review Task. */
|
|
76
|
+
dispatch: TaskDispatch;
|
|
77
|
+
target: DispatchTarget;
|
|
78
|
+
rationale: string;
|
|
79
|
+
/** Why the review was triggered (e.g. "criticality=high"). */
|
|
80
|
+
reason: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface DispatchDecision {
|
|
84
|
+
target: DispatchTarget;
|
|
85
|
+
/** True when any target field came from an explicit pin. */
|
|
86
|
+
pinned: boolean;
|
|
87
|
+
/** Human-readable explanation, e.g. `opus-4.8 high: type=build, complexity=complex; claude band=normal`. */
|
|
88
|
+
rationale: string;
|
|
89
|
+
factors: DispatchFactors;
|
|
90
|
+
/** Non-fatal advisories (e.g. scarce pool, no model meets the footprint). */
|
|
91
|
+
warnings: string[];
|
|
92
|
+
/** Present when the policy's auto-review threshold is met. */
|
|
93
|
+
review?: ReviewPlan;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// Public entrypoint
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Map a work-shape + live state onto a concrete spawn. `pin` overrides any field per-case;
|
|
102
|
+
* the rationale always explains the choice (including why a pin or reroute happened).
|
|
103
|
+
*/
|
|
104
|
+
export function dispatchTask(shape: TaskDispatch | undefined, pin: DispatchPin = {}, ctx: DispatchContext = {}): DispatchDecision {
|
|
105
|
+
const policy = ctx.policy ?? getDispatchPolicy();
|
|
106
|
+
const catalog = ctx.catalog ?? effectiveProviderCatalogList();
|
|
107
|
+
const bands = ctx.bands ?? providerQuotaBands();
|
|
108
|
+
const work: TaskDispatch = shape ?? {};
|
|
109
|
+
const warnings: string[] = [];
|
|
110
|
+
|
|
111
|
+
const matched = resolvePolicyTarget(work, policy);
|
|
112
|
+
let target: DispatchTarget = { ...matched.target };
|
|
113
|
+
const factors: DispatchFactors = {
|
|
114
|
+
...(work.complexity ? { complexity: work.complexity } : {}),
|
|
115
|
+
...(work.type ? { type: work.type } : {}),
|
|
116
|
+
...(work.contextFootprint ? { contextFootprint: work.contextFootprint } : {}),
|
|
117
|
+
...(work.criticality ? { criticality: work.criticality } : {}),
|
|
118
|
+
matchedRule: matched.label,
|
|
119
|
+
band: "normal",
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const pinnedFields = applyPin(target, pin);
|
|
123
|
+
const providerPinned = pinnedFields.includes("provider");
|
|
124
|
+
const modelPinned = pinnedFields.includes("model");
|
|
125
|
+
const effortPinned = pinnedFields.includes("effort");
|
|
126
|
+
if (pinnedFields.length) factors.pinnedFields = pinnedFields;
|
|
127
|
+
|
|
128
|
+
// Footprint escalation (skip when the model is explicitly pinned — respect the human choice).
|
|
129
|
+
if (!modelPinned) escalateForFootprint(target, work, policy, catalog, factors, warnings);
|
|
130
|
+
|
|
131
|
+
// Band-aware rerouting (skip when the provider is explicitly pinned). Protects scarce pools
|
|
132
|
+
// and load-shifts large/blocked work onto an abundant one. Re-validates footprint + a pinned
|
|
133
|
+
// effort on the rerouted provider so neither is silently dropped.
|
|
134
|
+
if (!providerPinned) rerouteForBand(target, work, policy, bands, catalog, factors, warnings, { effortPinned });
|
|
135
|
+
|
|
136
|
+
// Record the FINAL provider's band + the chosen model's window for transparency.
|
|
137
|
+
factors.band = bandFor(target.provider, bands);
|
|
138
|
+
const finalWindow = contextWindowOf(catalog, target.provider, target.model);
|
|
139
|
+
if (finalWindow !== undefined) factors.contextWindowTokens = finalWindow;
|
|
140
|
+
collectBandWarnings(target.provider, work, bands, providerPinned, warnings);
|
|
141
|
+
|
|
142
|
+
const review = planAutoReview(work, policy, bands, catalog);
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
target,
|
|
146
|
+
pinned: pinnedFields.length > 0,
|
|
147
|
+
rationale: buildRationale(target, factors, warnings),
|
|
148
|
+
factors,
|
|
149
|
+
warnings,
|
|
150
|
+
...(review ? { review } : {}),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Policy rule matching
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
function resolvePolicyTarget(work: TaskDispatch, policy: DispatchPolicy): { target: DispatchModelTarget; label: string } {
|
|
159
|
+
for (const rule of policy.rules) {
|
|
160
|
+
if (rule.enabled === false) continue;
|
|
161
|
+
if (matchesShape(rule.match, work)) {
|
|
162
|
+
return { target: rule.target, label: rule.label ?? "rule" };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return { target: policy.fallback, label: "fallback (no rule matched)" };
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function matchesShape(match: DispatchMatch, work: TaskDispatch): boolean {
|
|
169
|
+
return (
|
|
170
|
+
dimensionMatches(match.complexity, work.complexity) &&
|
|
171
|
+
dimensionMatches(match.type, work.type) &&
|
|
172
|
+
dimensionMatches(match.contextFootprint, work.contextFootprint) &&
|
|
173
|
+
dimensionMatches(match.criticality, work.criticality)
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// An unset criterion is a wildcard. A set criterion needs a present work value that is one of
|
|
178
|
+
// the (possibly array) allowed values.
|
|
179
|
+
function dimensionMatches(criterion: string | string[] | undefined, value: string | undefined): boolean {
|
|
180
|
+
if (criterion === undefined) return true;
|
|
181
|
+
if (value === undefined) return false;
|
|
182
|
+
const allowed = Array.isArray(criterion) ? criterion : [criterion];
|
|
183
|
+
return allowed.includes(value);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Pin application
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
function applyPin(target: DispatchTarget, pin: DispatchPin): string[] {
|
|
191
|
+
const fields: string[] = [];
|
|
192
|
+
if (pin.provider) {
|
|
193
|
+
// A provider change invalidates a provider-specific policy model/effort unless the pin
|
|
194
|
+
// also restates them; clear them so we fall back to that provider's defaults.
|
|
195
|
+
if (pin.provider !== target.provider) {
|
|
196
|
+
if (!pin.model) target.model = undefined;
|
|
197
|
+
if (!pin.effort) target.effort = undefined;
|
|
198
|
+
}
|
|
199
|
+
target.provider = pin.provider;
|
|
200
|
+
fields.push("provider");
|
|
201
|
+
}
|
|
202
|
+
if (pin.model) {
|
|
203
|
+
target.model = pin.model;
|
|
204
|
+
fields.push("model");
|
|
205
|
+
}
|
|
206
|
+
if (pin.effort) {
|
|
207
|
+
target.effort = pin.effort;
|
|
208
|
+
fields.push("effort");
|
|
209
|
+
}
|
|
210
|
+
return fields;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// Footprint → context-window escalation
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
function escalateForFootprint(
|
|
218
|
+
target: DispatchTarget,
|
|
219
|
+
work: TaskDispatch,
|
|
220
|
+
policy: DispatchPolicy,
|
|
221
|
+
catalog: ProviderCatalogEntry[],
|
|
222
|
+
factors: DispatchFactors,
|
|
223
|
+
warnings: string[],
|
|
224
|
+
): void {
|
|
225
|
+
const footprint = work.contextFootprint;
|
|
226
|
+
if (!footprint) return;
|
|
227
|
+
const minTokens = policy.footprintMinContextTokens?.[footprint];
|
|
228
|
+
if (!minTokens) return;
|
|
229
|
+
const entry = catalogEntry(catalog, target.provider);
|
|
230
|
+
if (!entry) return;
|
|
231
|
+
const alias = target.model ?? entry.defaultModel;
|
|
232
|
+
const current = alias ? contextWindowFromEntry(entry, alias) : undefined;
|
|
233
|
+
if (current !== undefined && current >= minTokens) return; // already big enough
|
|
234
|
+
|
|
235
|
+
// Prefer widening the SAME model family (opus-4.8 → opus-4.8[1m]); fall back to any other
|
|
236
|
+
// sufficient model on this provider before giving up (e.g. after a reroute reset us to a small
|
|
237
|
+
// default). Smallest sufficient window wins so we don't over-allocate.
|
|
238
|
+
const pick = biggestSufficient(entry, minTokens, alias);
|
|
239
|
+
if (pick && pick.alias !== alias) {
|
|
240
|
+
target.model = pick.alias;
|
|
241
|
+
factors.escalatedForFootprint = true;
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
if (!pick && current === undefined) return; // unknown window AND no sufficient alternative — can't assert a gap
|
|
245
|
+
// Nothing on this provider satisfies the footprint — surface it rather than silently downgrade.
|
|
246
|
+
warnings.push(
|
|
247
|
+
`contextFootprint=${footprint} wants ≥${minTokens} tokens but ${target.provider}/${alias ?? "default"} offers ${current ?? "unknown"}; no model on ${target.provider} meets it — review the pick.`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// The smallest-window model on this provider whose window meets the requirement. Prefers the
|
|
252
|
+
// same model family as `alias` (e.g. opus-4.8 → opus-4.8[1m]) when one qualifies, else any family.
|
|
253
|
+
function biggestSufficient(
|
|
254
|
+
entry: ProviderCatalogEntry,
|
|
255
|
+
minTokens: number,
|
|
256
|
+
alias: string | undefined,
|
|
257
|
+
): ProviderModelCatalogEntry | undefined {
|
|
258
|
+
const sufficient = entry.models
|
|
259
|
+
.filter((m) => !m.disabled && !m.unavailable)
|
|
260
|
+
.map((m) => ({ model: m, window: m.limits?.contextWindowTokens?.value }))
|
|
261
|
+
.filter((c): c is { model: ProviderModelCatalogEntry; window: number } => typeof c.window === "number" && c.window >= minTokens)
|
|
262
|
+
.sort((a, b) => a.window - b.window);
|
|
263
|
+
const base = alias ? familyBase(alias) : undefined;
|
|
264
|
+
const sameFamily = base ? sufficient.find((c) => familyBase(c.model.alias) === base) : undefined;
|
|
265
|
+
return (sameFamily ?? sufficient[0])?.model;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function familyBase(alias: string): string {
|
|
269
|
+
return alias.trim().toLowerCase().replace(/\[[^\]]+\]$/, "");
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ---------------------------------------------------------------------------
|
|
273
|
+
// Quota-band aware rerouting
|
|
274
|
+
// ---------------------------------------------------------------------------
|
|
275
|
+
|
|
276
|
+
function rerouteForBand(
|
|
277
|
+
target: DispatchTarget,
|
|
278
|
+
work: TaskDispatch,
|
|
279
|
+
policy: DispatchPolicy,
|
|
280
|
+
bands: ProviderQuotaBand[],
|
|
281
|
+
catalog: ProviderCatalogEntry[],
|
|
282
|
+
factors: DispatchFactors,
|
|
283
|
+
warnings: string[],
|
|
284
|
+
opts: { effortPinned: boolean },
|
|
285
|
+
): void {
|
|
286
|
+
const band = bandFor(target.provider, bands);
|
|
287
|
+
const blocked = isBlocked(target.provider, bands);
|
|
288
|
+
const isLarge = work.contextFootprint === "large";
|
|
289
|
+
// Reroute when the pool is at capacity / hard-avoid, or a large job lands on an avoid-large pool.
|
|
290
|
+
const mustMove = blocked || band === "hard-avoid" || (isLarge && band === "avoid-large");
|
|
291
|
+
if (!mustMove) return;
|
|
292
|
+
|
|
293
|
+
const alt = healthiestAlternative(target.provider, bands, catalog);
|
|
294
|
+
if (!alt) {
|
|
295
|
+
warnings.push(`${target.provider} band=${band}${blocked ? " (at capacity)" : ""} and no healthier provider is available — keeping it; expect contention.`);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
const altEntry = catalogEntry(catalog, alt.provider)!;
|
|
299
|
+
factors.reroutedFromProvider = target.provider;
|
|
300
|
+
warnings.push(`Rerouted ${target.provider} (band=${band}${blocked ? ", at capacity" : ""}) → ${alt.provider} (band=${alt.band}) to ${alt.band === "prefer" ? "use abundant quota" : "avoid the scarce pool"}.`);
|
|
301
|
+
const prevEffort = target.effort;
|
|
302
|
+
target.provider = alt.provider;
|
|
303
|
+
// Provider changed → model is provider-specific; reset to the new provider's default, then
|
|
304
|
+
// RE-VALIDATE the footprint on the new provider (it may widen to a 1M model, or warn if it
|
|
305
|
+
// cannot meet the footprint) so a large job is never silently downgraded onto a small window.
|
|
306
|
+
target.model = altEntry.defaultModel;
|
|
307
|
+
// Re-derive escalation against the NEW provider — the prior provider's escalation flag is stale.
|
|
308
|
+
delete factors.escalatedForFootprint;
|
|
309
|
+
escalateForFootprint(target, work, policy, catalog, factors, warnings);
|
|
310
|
+
// Effort: a pinned effort is authoritative and survives the reroute (warn if the rerouted model
|
|
311
|
+
// doesn't list it, rather than silently dropping it). An unpinned effort is kept only if supported.
|
|
312
|
+
if (opts.effortPinned) {
|
|
313
|
+
target.effort = prevEffort;
|
|
314
|
+
if (prevEffort && effortIfSupported(altEntry, target.model, prevEffort) === undefined) {
|
|
315
|
+
warnings.push(`Pinned effort '${prevEffort}' isn't listed for ${alt.provider}/${target.model ?? "default"}; honoring the pin anyway.`);
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
target.effort = effortIfSupported(altEntry, target.model, prevEffort);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Best healthy provider other than `avoid`, considering EVERY spawnable provider in the live
|
|
323
|
+
// catalog — not only those with a quota advisory. A provider with no quota record defaults to the
|
|
324
|
+
// "normal" band (via bandFor), so a healthy catalog provider is never skipped just because it has
|
|
325
|
+
// no burn data yet. Prefer "prefer" band, then "normal"; never a blocked one.
|
|
326
|
+
function healthiestAlternative(
|
|
327
|
+
avoid: SpawnProvider,
|
|
328
|
+
bands: ProviderQuotaBand[],
|
|
329
|
+
catalog: ProviderCatalogEntry[],
|
|
330
|
+
): { provider: SpawnProvider; band: QuotaBand } | undefined {
|
|
331
|
+
const ranked = catalog
|
|
332
|
+
.map((c) => c.provider)
|
|
333
|
+
.filter((provider) => provider !== avoid)
|
|
334
|
+
.map((provider) => ({ provider, band: bandFor(provider, bands), blockedNow: isBlocked(provider, bands) }))
|
|
335
|
+
.filter((c) => !c.blockedNow && (c.band === "prefer" || c.band === "normal"))
|
|
336
|
+
.sort((a, b) => bandRank(a.band) - bandRank(b.band));
|
|
337
|
+
return ranked[0];
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function collectBandWarnings(provider: SpawnProvider, work: TaskDispatch, bands: ProviderQuotaBand[], providerPinned: boolean, warnings: string[]): void {
|
|
341
|
+
if (!providerPinned) return; // non-pinned scarce pools were already handled by reroute
|
|
342
|
+
const band = bandFor(provider, bands);
|
|
343
|
+
if (band === "hard-avoid" || isBlocked(provider, bands)) {
|
|
344
|
+
warnings.push(`Pinned provider ${provider} is band=${band}${isBlocked(provider, bands) ? " (at capacity)" : ""}; honoring the pin but it may be slow or blocked.`);
|
|
345
|
+
} else if (band === "avoid-large" && work.contextFootprint === "large") {
|
|
346
|
+
warnings.push(`Pinned provider ${provider} is band=avoid-large for a large job; honoring the pin.`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// ---------------------------------------------------------------------------
|
|
351
|
+
// Auto-review planning
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
|
|
354
|
+
function planAutoReview(
|
|
355
|
+
work: TaskDispatch,
|
|
356
|
+
policy: DispatchPolicy,
|
|
357
|
+
bands: ProviderQuotaBand[],
|
|
358
|
+
catalog: ProviderCatalogEntry[],
|
|
359
|
+
): ReviewPlan | undefined {
|
|
360
|
+
const cfg = policy.autoReview;
|
|
361
|
+
if (!cfg) return undefined;
|
|
362
|
+
const reason = autoReviewReason(work, cfg);
|
|
363
|
+
if (!reason) return undefined;
|
|
364
|
+
// The review Task's own work-shape: a review, inheriting the parent's criticality. Its target
|
|
365
|
+
// is resolved through the SAME policy (so review routing stays single-source) — either the
|
|
366
|
+
// explicit autoReview.target or whatever the review-type rules pick. No pin, no recursion into
|
|
367
|
+
// auto-review (a review of a review isn't wanted).
|
|
368
|
+
const reviewShape: TaskDispatch = {
|
|
369
|
+
type: "review",
|
|
370
|
+
...(work.criticality ? { criticality: work.criticality } : {}),
|
|
371
|
+
...(work.complexity ? { complexity: work.complexity } : {}),
|
|
372
|
+
};
|
|
373
|
+
const reviewPolicy: DispatchPolicy = cfg.target
|
|
374
|
+
? { ...policy, rules: [{ label: "auto-review target", match: { type: "review" }, target: cfg.target }, ...policy.rules], autoReview: undefined }
|
|
375
|
+
: { ...policy, autoReview: undefined };
|
|
376
|
+
const decision = dispatchTask(reviewShape, {}, { policy: reviewPolicy, bands, catalog });
|
|
377
|
+
return {
|
|
378
|
+
dispatch: reviewShape,
|
|
379
|
+
target: decision.target,
|
|
380
|
+
rationale: decision.rationale,
|
|
381
|
+
reason,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function autoReviewReason(work: TaskDispatch, cfg: DispatchAutoReview): string | undefined {
|
|
386
|
+
if (work.criticality && cfg.whenCriticality?.includes(work.criticality)) return `criticality=${work.criticality}`;
|
|
387
|
+
if (work.complexity && cfg.whenComplexity?.includes(work.complexity)) return `complexity=${work.complexity}`;
|
|
388
|
+
return undefined;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ---------------------------------------------------------------------------
|
|
392
|
+
// Rationale rendering
|
|
393
|
+
// ---------------------------------------------------------------------------
|
|
394
|
+
|
|
395
|
+
function buildRationale(target: DispatchTarget, factors: DispatchFactors, warnings: string[]): string {
|
|
396
|
+
const head = `${target.model ?? `${target.provider} default`}${target.effort ? ` ${target.effort}` : ""}`;
|
|
397
|
+
const shape = [
|
|
398
|
+
factors.type ? `type=${factors.type}` : undefined,
|
|
399
|
+
factors.complexity ? `complexity=${factors.complexity}` : undefined,
|
|
400
|
+
factors.contextFootprint ? `contextFootprint=${factors.contextFootprint}` : undefined,
|
|
401
|
+
factors.criticality ? `criticality=${factors.criticality}` : undefined,
|
|
402
|
+
].filter(Boolean).join(", ");
|
|
403
|
+
const parts = [`${head}:`];
|
|
404
|
+
if (shape) parts.push(shape + ";");
|
|
405
|
+
parts.push(`matched ${factors.matchedRule};`);
|
|
406
|
+
parts.push(`${capitalize(target.provider)} band=${factors.band}`);
|
|
407
|
+
if (factors.escalatedForFootprint) parts.push(`(escalated to ${target.model} for footprint)`);
|
|
408
|
+
if (factors.reroutedFromProvider) parts.push(`(rerouted from ${factors.reroutedFromProvider})`);
|
|
409
|
+
if (factors.pinnedFields?.length) parts.push(`(pinned: ${factors.pinnedFields.join(", ")})`);
|
|
410
|
+
return parts.join(" ");
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function capitalize(value: string): string {
|
|
414
|
+
return value ? value[0]!.toUpperCase() + value.slice(1) : value;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ---------------------------------------------------------------------------
|
|
418
|
+
// Catalog / band helpers
|
|
419
|
+
// ---------------------------------------------------------------------------
|
|
420
|
+
|
|
421
|
+
function catalogEntry(catalog: ProviderCatalogEntry[], provider: SpawnProvider): ProviderCatalogEntry | undefined {
|
|
422
|
+
return catalog.find((c) => c.provider === provider);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function contextWindowOf(catalog: ProviderCatalogEntry[], provider: SpawnProvider, model: string | undefined): number | undefined {
|
|
426
|
+
const entry = catalogEntry(catalog, provider);
|
|
427
|
+
if (!entry) return undefined;
|
|
428
|
+
const alias = model ?? entry.defaultModel;
|
|
429
|
+
return alias ? contextWindowFromEntry(entry, alias) : undefined;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function contextWindowFromEntry(entry: ProviderCatalogEntry, alias: string): number | undefined {
|
|
433
|
+
const model = entry.models.find((m) => m.alias === alias) ?? entry.models.find((m) => m.providerModel === alias);
|
|
434
|
+
return model?.limits?.contextWindowTokens?.value;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function effortIfSupported(entry: ProviderCatalogEntry, alias: string | undefined, effort: string | undefined): string | undefined {
|
|
438
|
+
if (!effort || !alias) return undefined;
|
|
439
|
+
const model = entry.models.find((m) => m.alias === alias);
|
|
440
|
+
return model && model.efforts.includes(effort) ? effort : undefined;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function bandFor(provider: SpawnProvider, bands: ProviderQuotaBand[]): QuotaBand {
|
|
444
|
+
return bands.find((b) => b.provider === provider)?.band ?? "normal";
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function isBlocked(provider: SpawnProvider, bands: ProviderQuotaBand[]): boolean {
|
|
448
|
+
return bands.find((b) => b.provider === provider)?.blockedNow ?? false;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Lower = healthier. Mirrors the bandRank ordering used elsewhere (prefer is the best state).
|
|
452
|
+
function bandRank(band: QuotaBand): number {
|
|
453
|
+
switch (band) {
|
|
454
|
+
case "prefer": return 0;
|
|
455
|
+
case "normal": return 1;
|
|
456
|
+
case "caution": return 2;
|
|
457
|
+
case "avoid-large": return 3;
|
|
458
|
+
case "hard-avoid": return 4;
|
|
459
|
+
default: return 1;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmodSync, existsSync, readFileSync, rmSync } from "node:fs";
|
|
1
|
+
import { chmodSync, existsSync, readFileSync, realpathSync, rmSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -143,10 +143,29 @@ export const defaultInstallIo: InstallIo = {
|
|
|
143
143
|
: bin && typeof bin === "object"
|
|
144
144
|
? Object.values(bin as Record<string, unknown>).filter((v): v is string => typeof v === "string")
|
|
145
145
|
: [];
|
|
146
|
+
// Resolve the real node_modules path once so we can guard against workspace
|
|
147
|
+
// symlinks that point back into the repo working tree (#825).
|
|
148
|
+
let realNodeModules: string;
|
|
149
|
+
try {
|
|
150
|
+
realNodeModules = realpathSync(join(prefix, "node_modules"));
|
|
151
|
+
} catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
146
154
|
const restored: string[] = [];
|
|
147
155
|
for (const rel of targets) {
|
|
148
156
|
const file = join(pkgDir, rel);
|
|
149
157
|
if (!existsSync(file)) continue;
|
|
158
|
+
// Skip if the realpath resolves outside node_modules (e.g. a workspace symlink
|
|
159
|
+
// node_modules/pkg → ../pkg points into the tracked source tree). Chmodding
|
|
160
|
+
// through such a symlink flips the source file's mode and makes the working
|
|
161
|
+
// tree appear dirty, mis-routing the landed path into the WIP-preserve path.
|
|
162
|
+
let realFile: string;
|
|
163
|
+
try {
|
|
164
|
+
realFile = realpathSync(file);
|
|
165
|
+
} catch {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (!realFile.startsWith(realNodeModules + "/")) continue;
|
|
150
169
|
try {
|
|
151
170
|
chmodSync(file, 0o755);
|
|
152
171
|
restored.push(file);
|
|
@@ -1,3 +0,0 @@
|
|
|
1
|
-
import{c as e,d as t,n,o as r,t as i,u as a}from"./types-DorDDvEb.js";var o=`user`,s=o,c=5e3,l=new Set([`done`,`failed`,`canceled`]),u=new Set([`open`,`blocked`]),d={online:0,idle:0,busy:2,offline:3},f={idle:0,online:0,busy:2,offline:4},p={from:``,to:``,body:``,channel:``,subject:``,claimable:!1},m={toMode:`agent`,to:``,body:``,channel:``,subject:``,claimable:!1},h={pairId:``,from:``,body:``,subject:``},ee={requesterId:``,targetId:``,objective:``},te={user:`User`,system:`Server`,channel:`MessagesSquare`,agent:`Bot`},ne={user:`Human operator`,system:`System`,channel:`Channel`,agent:`Agent`};({...te,...Object.fromEntries(e().map(e=>[e,`Bot`]))}),{...ne,...Object.fromEntries(e().map(e=>[e,`${a(e)} agent`]))};var re=[{key:`overview`,label:`Overview`,icon:`LayoutDashboard`},{key:`chat`,label:`Chat`,icon:`MessageCircle`},{key:`agents`,label:`Agents`,icon:`Bot`},{key:`managed`,label:`Managed Agents`,icon:`Workflow`},{key:`teams`,label:`Teams`,icon:`Users`},{key:`profiles`,label:`Agent Profiles`,icon:`UserCog`},{key:`orchestrators`,label:`Orchestrators`,icon:`Server`},{key:`workspaces`,label:`Workspaces`,icon:`GitBranch`},{key:`files`,label:`Files`,icon:`FolderTree`},{key:`channels`,label:`Channels`,icon:`MessagesSquare`},{key:`connectors`,label:`Connectors`,icon:`Plug`},{key:`integrations`,label:`Integrations`,icon:`PlugZap`},{key:`security`,label:`Security`,icon:`Shield`},{key:`memory`,label:`Memory`,icon:`BrainCircuit`},{key:`activity`,label:`Activity`,icon:`Activity`},{key:`pairs`,label:`Pairs`,icon:`Link`},{key:`messages`,label:`Messages`,icon:`Mail`},{key:`work`,label:`Work Queue`,icon:`ListChecks`},{key:`tasks`,label:`Tasks`,icon:`ClipboardList`},{key:`automation`,label:`Automation`,icon:`CalendarClock`},{key:`analytics`,label:`Analytics`,icon:`AreaChart`},{key:`insights`,label:`Insights`,icon:`Lightbulb`},{key:`projects`,label:`Projects`,icon:`Boxes`},{key:`prompts`,label:`Prompts`,icon:`FileText`},{key:`registry`,label:`Registry`,icon:`PackagePlus`},{key:`maintenance`,label:`Maintenance`,icon:`Wrench`},{key:`settings`,label:`Settings`,icon:`Settings`}],ie={critical:`text-red-400 bg-red-500/10`,warning:`text-yellow-400 bg-yellow-500/10`,info:`text-blue-400 bg-blue-500/10`},ae={active:`bg-emerald-500/20 text-emerald-400`,pending:`bg-yellow-500/20 text-yellow-400`,ended:`bg-zinc-500/20 text-zinc-400`,rejected:`bg-red-500/20 text-red-400`,expired:`bg-red-500/20 text-red-400`};function g(e){let t=typeof e==`number`?e:new Date(e||0).getTime();return Number.isFinite(t)?t:0}var _=[`lifecycle.`,`commands.`,`capabilities.`],v={"+1":`👍`,":+1:":`👍`,thumbsup:`👍`,":thumbsup:":`👍`,"thumbs-up":`👍`,":thumbs-up:":`👍`,thumbs_up:`👍`,":thumbs_up:":`👍`,thumb_up:`👍`,":thumb_up:":`👍`,like:`👍`,":like:":`👍`,heart:`❤️`,":heart:":`❤️`,redheart:`❤️`,red_heart:`❤️`,":red_heart:":`❤️`,check:`✅`,":check:":`✅`,checkmark:`✅`,":checkmark:":`✅`,white_check_mark:`✅`,":white_check_mark:":`✅`,eyes:`👀`,":eyes:":`👀`};function y(e){let t=e.trim();return v[t.toLowerCase()]??t}function oe(e){return e.filter(e=>!_.some(t=>e.startsWith(t)))}function se(e,t=2){if(!e)return``;let n=e.replace(/\/+$/,``).split(`/`);return n.length<=t?e:n.slice(-t).join(`/`)}function b(e){return e.trim().replace(/\\/g,`/`).replace(/\/+$/,``)||`/`}function ce(e,t){if(!e?.trim()||!t?.trim())return!1;let n=b(e),r=b(t);return r===`/`?n.startsWith(`/`):n===`/`?!1:n===r||n.startsWith(r+`/`)}function x(e){return e?.meta?.builtin===!0||e?.id===`user`||e?.id===`system`}function S(e){let t=e?.tags||[],n=e?.capabilities||[],r=typeof e?.meta?.policyName==`string`?e.meta.policyName:``;return e?.kind===`channel`||e?.meta?.kind===`channel`||t.includes(`channel`)||t.some(e=>e.startsWith(`channel:`))||n.includes(`channel`)||n.some(e=>e.startsWith(`channel:`))||t.includes(`telegram`)||n.includes(`telegram`)||r===`telegram-main`}function C(e){return!!(typeof e?.meta?.automationId==`string`||typeof e?.meta?.automationRunId==`string`||e?.tags?.includes(`automation`))}function w(e){return x(e)||S(e)||C(e)}function T(t){if(t?.id===`user`)return`user`;if(t?.id===`system`)return`system`;if(S(t))return`channel`;let n=e(),r=typeof t?.meta?.provider==`string`?t.meta.provider.toLowerCase():void 0;if(r&&n.includes(r))return r;let i=[...t?.tags||[],t?.meta?.client,t?.meta?.runtime,t?.meta?.agentType].filter(e=>typeof e==`string`).map(e=>e.toLowerCase());for(let e of n)if(i.some(t=>t.includes(e)))return e;return`agent`}function E(e,t){if(!t?.lastSeen||t.status===`offline`||t.id===`user`||t.id===`system`)return!1;let n=t.meta?.transport;if(r(n)&&n.connected===!0)return!1;let i=new Date(t.lastSeen).getTime();return Number.isFinite(i)?e-i>6e4:!1}function D(e,t){if(!e||x(e)||S(e)||t===`resume`)return!1;if(t===`interrupt`)return e.providerCapabilities?.liveSession?.interrupt===!0&&e.status===`busy`;if(k(e)&&(t===`restart`||t===`shutdown`||t===`compact`||t===`clearContext`)&&!O(e))return!1;let n=e.providerCapabilities?.lifecycle;if(n){if(t===`restart`)return n.restartHard===!0;if(t===`shutdown`)return n.shutdownHard===!0;if(t===`reconnect`)return n.reconnect===!0}return t===`compact`?e.providerCapabilities?.context?.compact===!0:t===`clearContext`?e.providerCapabilities?.context?.clear===!0:e.meta?.runnerManaged===!0&&(t===`restart`||t===`shutdown`)}function O(e){return typeof e.meta?.tmuxSession==`string`&&e.meta.tmuxSession.trim().length>0}function k(e){return(typeof e?.meta?.provider==`string`?t(e.meta.provider):void 0)?.capabilityDefaults?.terminal?.mode===`live`}function A(e){return D(e,`restart`)||D(e,`shutdown`)||D(e,`reconnect`)||D(e,`compact`)||D(e,`clearContext`)}function j(e){return!!(e&&!x(e)&&!S(e)&&!A(e)&&(e.status===`offline`||e.status===`stale`))}function M(e,t){if(e.status!==`offline`)return!1;let n=typeof e.meta?.policyName==`string`?e.meta.policyName:``;if(!n)return!1;let r=t.find(e=>e.policy.name===n);return!!(r?.state.agentId&&r.state.agentId!==e.id)}function N(e,t,n=[]){let r=e.filter(e=>!M(e,n));return t?r:r.filter(e=>!w(e))}function P(e){return e?e.label||e.name||e.id.slice(-12):`?`}function F(e,t){let r=n(e);return r?t[r]:void 0}function I(e,t){let n=F(e,t);return n?.displayName?.trim()||n?.handle?.trim()||`User`}var L=[`text-sky-300`,`text-amber-300`,`text-emerald-300`,`text-violet-300`,`text-rose-300`,`text-cyan-300`];function R(e){let t=n(e)||``,r=0;for(let e=0;e<t.length;e++)r=r*31+t.charCodeAt(e)>>>0;return L[r%L.length]}function z(e,t){if(!e)return`?`;if(e===`broadcast`)return`broadcast`;if(e.startsWith(`tag:`))return`#`+e.slice(4);if(e.startsWith(`cap:`))return e.slice(4);if(e.startsWith(`label:`))return e.slice(6);if(e.startsWith(`team:`)){let n=e.slice(5),r=Object.values(t).find(e=>e.teamId===n);return r?`team:${P(r)}`:`team:${n.slice(-8)}`}let n=t[e];return n?P(n):e.slice(-8)}function B(e){if(!e)return``;let t=e.payload||{},n=t.message;if(n&&typeof n==`object`&&typeof n.text==`string`&&n.text.trim())return n.text;let r=t.interaction;if(r&&typeof r==`object`){let e=typeof r.title==`string`?r.title.trim():``,t=typeof r.description==`string`?r.description.trim():``;if(e&&t)return e+`
|
|
2
|
-
`+t;if(e)return e;if(t)return t}let i=t.reaction;if(i&&typeof i==`object`){let e=typeof i.name==`string`?i.name:``,t=typeof i.emoji==`string`?i.emoji:``,n=typeof i.value==`string`?i.value:``;return[`Reaction`,y(t||e||n)].filter(Boolean).join(`: `)}let a=t.activity;return a&&typeof a==`object`?[typeof a.kind==`string`?a.kind:`activity`,typeof a.state==`string`?a.state:``].filter(Boolean).join(` `):typeof t.text==`string`&&t.text.trim()?t.text:typeof t.message==`string`&&t.message.trim()?t.message:U(e.body||``)??(e.body||``)}function V(e){if(!e?.payload)return!1;let t=e.payload.event;return e.payload.reactionNotification===!0||t?.type===`message.reaction`}function H(e){if(!e)return null;let t=e.trim();if(!t.startsWith(`{`))return null;try{let e=JSON.parse(t);if(typeof e!=`object`||!e||Array.isArray(e))return null;let n=typeof e.text==`string`?e.text:typeof e.message==`string`?e.message:null;if(!n?.trim())return null;let r=new Set([`text`,`message`]);return{text:n,meta:Object.entries(e).filter(([e,t])=>!r.has(e)&&(typeof t==`string`||typeof t==`number`||typeof t==`boolean`)).map(([e,t])=>[e,String(t)]),raw:e}}catch{return null}}function U(e){return H(e)?.text??null}function W(e){let t=e?.subject||B(e)||``;return t.length>90?t.slice(0,90)+`...`:t}function G(e){return i(e.from)&&e.to?e.to:i(e.to)&&e.from?e.from:``}function K(e){return i(e.to)&&!i(e.from)}function le(e){if(e.kind!==`session`)return!1;let t=e.payload?.session?.type;return t===`reasoning`||t===`tool`}function q(e){return e.kind===`session`&&e.payload?.session?.final===!0}function ue(e,t){let n=e.trim(),r=t.trim();return r?n===r?!0:n.startsWith(`${r}\n\n`)||n.endsWith(`\n\n${r}`)||n.includes(`\n\n${r}\n\n`):!1}function de(e,t,n){if(!K(n)||!q(n)||(n.readBy||[]).includes(`user`))return!1;let r=Number(e[t]||0);return n.id>(Number.isFinite(r)?r:0)}function J(e){return u.has(e.status)&&!e.claimedBy}function fe(e){return!!(e.claimable&&!e.claimedBy&&!(e.kind===`task`&&Number.isSafeInteger(e.payload?.taskId)))}function pe(e,t){return!e||!t?!1:e===`broadcast`||e===t.id?!0:e.startsWith(`tag:`)?(t.tags||[]).includes(e.slice(4)):e.startsWith(`cap:`)?(t.capabilities||[]).includes(e.slice(4)):e.startsWith(`label:`)?t.label===e.slice(6):e.startsWith(`team:`)?t.teamId===e.slice(5):!1}function me(e,t){return e?t.from===e||t.to===e||t.resolvedToAgent===e:!1}function he(e,t){if(!e||!t)return!1;let n=[t.id,t.type,...t.topicChannels||[]].filter(Boolean);if(e.channel&&n.includes(e.channel))return!0;let r=e.payload?.channel;if(r&&typeof r==`object`){let e=[r.agentId,r.provider,r.accountId].filter(Boolean);if(e.includes(t.id)||e.includes(t.type)||t.accountId&&e.includes(t.accountId))return!0}return!!(t.agentId&&(e.from===t.agentId||e.to===t.agentId))}function Y(e){let t=e?.meta?.providerState;return!r(t)||typeof t.state!=`string`?null:{state:t.state,reason:typeof t.reason==`string`?t.reason:void 0,label:typeof t.label==`string`&&t.label?t.label:t.state,recommendedAction:typeof t.recommendedAction==`string`?t.recommendedAction:void 0,source:typeof t.source==`string`?t.source:void 0,raw:t.raw,updatedAt:typeof t.updatedAt==`number`?t.updatedAt:void 0,pendingApproval:ge(t.pendingApproval)}}function ge(e){if(!r(e)||typeof e.id!=`string`)return;let t=Array.isArray(e.choices)?e.choices.filter(r).map(e=>({id:e.id,label:typeof e.label==`string`?e.label:String(e.id||``)})).filter(e=>(e.id===`approve`||e.id===`approve-session`||e.id===`deny`||e.id===`abort`)&&!!e.label):[],n=Array.isArray(e.questions)?e.questions.filter(r).map(e=>({question:typeof e.question==`string`?e.question:``,header:typeof e.header==`string`?e.header:void 0,multiSelect:e.multiSelect===!0,options:Array.isArray(e.options)?e.options.filter(r).map(e=>({label:typeof e.label==`string`?e.label:String(e.label??``),description:typeof e.description==`string`?e.description:void 0})).filter(e=>!!e.label):[]})).filter(e=>!!e.question&&e.options.length>0):void 0;return{id:e.id,provider:typeof e.provider==`string`?e.provider:void 0,kind:typeof e.kind==`string`?e.kind:void 0,title:typeof e.title==`string`&&e.title?e.title:`Permission request`,body:typeof e.body==`string`?e.body:``,choices:t,...n&&n.length?{questions:n}:{}}}function X(e){let t=Y(e);return t?.state===`blocked`?t:null}function _e(e){return!!e?.id}function ve(e){return _e(X(e)?.pendingApproval)}function Z(e){let t=e?.meta?.activeSubagents;if(!Array.isArray(t)){let t=typeof e?.meta?.activeSubagentCount==`number`?e.meta.activeSubagentCount:0;return t>0?Array.from({length:t},(e,t)=>({id:`subagent-${t+1}`,label:`Subagent ${t+1}`})):[]}return t.filter(r).map((e,t)=>{let n=typeof e.id==`string`&&e.id?e.id:`subagent-${t+1}`,r=typeof e.role==`string`&&e.role?e.role:void 0;return{id:n,label:typeof e.label==`string`&&e.label?e.label:r||n,role:r,startedAt:typeof e.startedAt==`number`?e.startedAt:void 0}})}function Q(e){let t=e?.meta?.activeWork;if(!Array.isArray(t))return null;let n=t.find(e=>r(e)&&e.kind===`background-script`);return!n||!r(n)?null:typeof n.label==`string`&&n.label?n.label:`background script`}function ye(e,t,n,r){let i=typeof t?.meta?.lifecycleAction==`string`?t.meta.lifecycleAction:``,a=E(e,t),o=t?.status!==`offline`&&!t?.ready&&a,s=t?.status!==`offline`&&!t?.ready&&!a,c=n.unread>0&&t?.status!==`busy`,l=X(t),u=be(t,n,r);if(t?.status===`offline`)return{label:`offline`,tone:`secondary`,icon:`PlugZap`,stale:!1,reconnecting:!1,badges:u};if(i.startsWith(`finalizing-`))return{label:`wrapping up (${i.slice(11)})`,tone:`warning`,icon:`Hourglass`,stale:a,reconnecting:o,badges:u};if(i===`shutting-down`)return{label:`shutting down`,tone:`warning`,icon:`Power`,stale:a,reconnecting:o,badges:u};if(i===`killing`)return{label:`killing`,tone:`danger`,icon:`Power`,stale:a,reconnecting:o,badges:u};if(i===`restarting`)return{label:`restarting`,tone:`warning`,icon:`RefreshCw`,stale:a,reconnecting:o,badges:u};if(t?.status===`stale`)return{label:`stale`,tone:`danger`,icon:`RefreshCw`,stale:!0,reconnecting:!0,badges:u};if(o)return{label:`reconnecting`,tone:`danger`,icon:`RefreshCw`,stale:a,reconnecting:o,badges:u};if(s)return{label:`online, not ready`,tone:`warning`,icon:`Loader`,stale:a,reconnecting:o,badges:u};if(l)return{label:`blocked: ${l.label}`,tone:`danger`,icon:`AlertCircle`,stale:a,reconnecting:o,badges:u};if(t?.status===`busy`){let e=Array.isArray(t?.meta?.busyReasons)?t.meta.busyReasons:[];return Q(t)&&!e.includes(`provider-turn`)&&!e.includes(`subagent`)?{label:`running background work`,tone:`warning`,icon:`Play`,stale:a,reconnecting:o,badges:u}:{label:`busy in turn`,tone:`warning`,icon:`Play`,stale:a,reconnecting:o,badges:u}}return r?.status===`active`?{label:`paired`,tone:`success`,icon:`Link`,stale:a,reconnecting:o,badges:u}:c?{label:`idle, unread`,tone:`danger`,icon:`Bell`,stale:a,reconnecting:o,badges:u}:{label:t?.status===`idle`?`idle`:`ready`,tone:`success`,icon:`CircleCheck`,stale:a,reconnecting:o,badges:u}}function be(e,t,n){let r=[],i=X(e),a=Z(e).length;i&&r.push({label:i.reason?`blocked: ${i.reason}`:`blocked`,className:`bg-red-500/20 text-red-400`}),a&&r.push({label:a===1?`1 subagent`:a+` subagents`,className:`bg-cyan-500/20 text-cyan-300`});let o=Q(e);return o&&r.push({label:o,className:`bg-indigo-500/20 text-indigo-300`}),n?.status===`active`&&r.push({label:`paired`,className:`bg-emerald-500/20 text-emerald-400`}),n?.status===`pending`&&r.push({label:`pair invite`,className:`bg-yellow-500/20 text-yellow-400`}),t.needsHumanResponse&&r.push({label:`needs response`,className:`bg-amber-500/20 text-amber-300`}),t.unread&&r.push({label:t.unread+` unread`,className:`bg-red-500/20 text-red-400`}),t.claimableTasks&&r.push({label:t.claimableTasks+` claimable`,className:`bg-orange-500/20 text-orange-400`}),r}function xe(){return{unread:0,needsHumanResponse:!1,pendingPairInvite:!1,claimableTasks:0,total:0,score:0}}function $(e){return!e||e.status===`offline`?!1:e.targetHealth?.status===`ok`?!0:e.targetHealth?.status===`error`||e.targetHealth?.status===`warning`?!1:e.ready}function Se(e){return e?e.targetHealth?.status===`error`?{label:`target broken`,tone:`danger`,icon:`AlertTriangle`,badges:[]}:e.targetHealth?.status===`warning`?{label:`target warning`,tone:`warning`,icon:`AlertCircle`,badges:[]}:e.status===`offline`?{label:`offline`,tone:`secondary`,icon:`PlugZap`,badges:[]}:e.status===`busy`?{label:`busy`,tone:`warning`,icon:`Activity`,badges:[]}:$(e)?{label:`ready`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`not ready`,tone:`warning`,icon:`Loader`,badges:[]}:{label:`unknown`,tone:`secondary`,icon:`PlugZap`,badges:[]}}function Ce(e){let t=e?.runtime||{};return t.status===`error`?{label:`error`,tone:`danger`,icon:`AlertTriangle`,badges:[]}:t.status===`warn`?{label:`warning`,tone:`warning`,icon:`AlertCircle`,badges:[]}:t.running?{label:`running`,tone:`success`,icon:`CircleCheck`,badges:[]}:t.enabled===!1?{label:`disabled`,tone:`secondary`,icon:`Pause`,badges:[]}:t.status===`ok`?{label:`ok`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`unknown`,tone:`secondary`,icon:`HelpCircle`,badges:[]}}function we(e){let t=e?.taskStats||{};return(t.waitingTasks||0)>0?{label:`waiting`,tone:`warning`,icon:`AlertCircle`,badges:[]}:(t.openTasks||0)>0?{label:`active`,tone:`info`,icon:`Activity`,badges:[]}:e?.configured?e.observed?{label:`quiet`,tone:`success`,icon:`CircleCheck`,badges:[]}:{label:`configured`,tone:`primary`,icon:`PlugZap`,badges:[]}:{label:`observed only`,tone:`secondary`,icon:`Eye`,badges:[]}}function Te(e){let t=e?.health;return!t||t.status===`ok`?e?.version||e?.package?.version?`compatible`:`unknown version`:t.status===`upgrade-required`?`upgrade required`:t.status===`restart-required`?`restart required`:t.status===`unknown`?`unknown contract`:(t.issues||[]).some(e=>e.code===`protocol-mismatch`)?`protocol mismatch`:(t.issues||[]).some(e=>e.code===`package-drift`)?`package drift`:t.restartRequired?`restart required`:t.status}function Ee(e){let t=e?.health?.status;return t===`upgrade-required`?`text-red-400 bg-red-500/10`:t===`warn`||t===`restart-required`?`text-yellow-400 bg-yellow-500/10`:t===`unknown`?`text-zinc-400 bg-zinc-500/10`:`text-emerald-400 bg-emerald-500/10`}function De(e,t){if(!t)return``;let n=new Date(t).getTime();if(!Number.isFinite(n))return``;let r=Math.max(0,(e-n)/1e3);return r<60?Math.floor(r)+`s ago`:r<3600?Math.floor(r/60)+`m ago`:r<86400?Math.floor(r/3600)+`h ago`:Math.floor(r/86400)+`d ago`}function Oe(e){return e?new Date(e).toLocaleString():``}function ke(e){return String(e||`export`).replace(/[^a-z0-9._-]+/gi,`-`).replace(/^-+|-+$/g,``).slice(0,80)||`export`}function Ae(e){return e.filter(e=>e.status!==`ok`).map(e=>{let t={name:e.name,status:e.status,detail:e.detail||e.name,impact:je(e),subjects:e.subjects||[],actions:[{label:`Inspect logs`,icon:`FileSearch`,copy:`agent-relay daemon logs`},{label:`Restart daemon`,icon:`RefreshCw`,copy:`agent-relay daemon restart`},{label:`Copy env`,icon:`Copy`,copy:`agent-relay doctor`}]};return e.name===`stale-live-agents`?t.actions.unshift({label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`},{label:`Show stale`,icon:`Filter`,view:`agents`,preset:`offline_stale`}):e.name===`channel-delivery-targets`?t.actions.unshift({label:`Open channels`,icon:`MessagesSquare`,view:`channels`},{label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`}):[`expired-message-claims`,`expired-task-claims`,`offline-claimed-tasks`].includes(e.name)&&t.actions.unshift({label:`Run reaper`,icon:`Eraser`,api:`POST`,path:`/system/reap`},{label:`Open work`,icon:`ListChecks`,view:`work`}),t})}function je(e){return{database:`Relay persistence is unavailable; messages, state, and audit writes may fail.`,"stale-live-agents":`Agents may look online even though their heartbeat has stopped.`,"expired-message-claims":`Claimable messages may be stuck until the reaper releases expired claims.`,"expired-task-claims":`Tasks can appear owned by agents that no longer hold a live lease.`,"offline-claimed-tasks":`Offline agents are still shown as owners for active work.`,"channel-delivery-targets":`Inbound channel messages may be accepted but routed to no live delivery agent.`}[e.name]||`Relay health is degraded for this check.`}function Me(e){return{success:`text-emerald-400`,warning:`text-yellow-400`,danger:`text-red-400`,info:`text-blue-400`,primary:`text-blue-400`,secondary:`text-zinc-400`}[e]||`text-zinc-400`}function Ne(e,t){return!e||e.status===`offline`?`bg-zinc-500 opacity-50`:typeof e.meta?.lifecycleAction==`string`&&e.meta.lifecycleAction.startsWith(`finalizing-`)?`bg-amber-500 animate-pulse`:e.meta?.lifecycleAction===`shutting-down`||e.meta?.lifecycleAction===`restarting`?`bg-yellow-500 animate-pulse`:e.meta?.lifecycleAction===`killing`||X(e)?`bg-red-500 animate-pulse`:e.status===`busy`?`bg-yellow-500`:t&&E(t,e)?`bg-red-500 animate-pulse`:e.ready?`bg-emerald-500 shadow-[0_0_6px_theme(colors.emerald.500)]`:`bg-emerald-500 animate-pulse`}function Pe(e,t,n){if(typeof document>`u`||typeof URL>`u`||typeof Blob>`u`)return;let r=URL.createObjectURL(new Blob([t],{type:n})),i=document.createElement(`a`);i.href=r,i.download=e,i.click(),URL.revokeObjectURL(r)}export{p as $,me as A,Y as B,fe as C,le as D,V as E,Ee as F,Ne as G,ue as H,Te as I,Me as J,De as K,H as L,W as M,b as N,de as O,y as P,l as Q,ce as R,q as S,K as T,ke as U,F as V,se as W,N as X,oe as Y,f as Z,R as _,D as a,c as at,we as b,Se as c,ie as ct,P as d,m as et,z as f,Ae as g,Oe as h,ye as i,s as it,he as j,B as k,Ce as l,d as lt,xe as m,j as n,h as nt,T as o,re as ot,Pe as p,g as q,ve as r,o as rt,$ as s,ae as st,Z as t,ee as tt,pe as u,I as v,J as w,E as x,G as y,X as z};
|
|
3
|
-
//# sourceMappingURL=display-BWi8DQCp.js.map
|