@frumu/tandem-panel 0.4.8 → 0.4.10

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.
@@ -0,0 +1,147 @@
1
+ export function mapOrchestratorPath(pathname) {
2
+ const path = String(pathname || "").trim();
3
+ if (path.startsWith("/api/orchestrator")) {
4
+ return `/api/swarm${path.slice("/api/orchestrator".length)}`;
5
+ }
6
+ return path;
7
+ }
8
+
9
+ function toNumber(value) {
10
+ const parsed = Number(value);
11
+ return Number.isFinite(parsed) ? parsed : 0;
12
+ }
13
+
14
+ function pickNumeric(...values) {
15
+ for (const value of values) {
16
+ const parsed = Number(value);
17
+ if (Number.isFinite(parsed) && parsed > 0) return parsed;
18
+ }
19
+ return 0;
20
+ }
21
+
22
+ const DERIVED_MAX_ITERATIONS = 500;
23
+ const DERIVED_MAX_TOKENS = 400000;
24
+ const DERIVED_MAX_WALL_TIME_SECS = 7 * 24 * 60 * 60;
25
+ const DERIVED_MAX_SUBAGENT_RUNS = 2000;
26
+
27
+ function hasExplicitBudgetLimits(run) {
28
+ return (
29
+ pickNumeric(
30
+ run?.budget?.max_iterations,
31
+ run?.config?.max_iterations,
32
+ run?.budget?.max_tokens,
33
+ run?.budget?.max_total_tokens,
34
+ run?.config?.max_total_tokens,
35
+ run?.budget?.max_wall_time_secs,
36
+ run?.config?.max_wall_time_secs,
37
+ run?.budget?.max_subagent_runs,
38
+ run?.config?.max_subagent_runs
39
+ ) > 0
40
+ );
41
+ }
42
+
43
+ export function deriveRunBudget(run, events, tasks) {
44
+ const startedAtMs = toNumber(run?.started_at_ms);
45
+ const updatedAtMs = toNumber(run?.updated_at_ms || Date.now());
46
+ const wallTimeSecs =
47
+ startedAtMs > 0 && updatedAtMs >= startedAtMs
48
+ ? Math.round((updatedAtMs - startedAtMs) / 1000)
49
+ : 0;
50
+ const iterationsUsed = Array.isArray(events)
51
+ ? events.filter((row) =>
52
+ String(row?.type || "")
53
+ .toLowerCase()
54
+ .includes("task_")
55
+ ).length
56
+ : 0;
57
+ const tokenEvents = Array.isArray(events) ? events : [];
58
+ let tokensUsed = 0;
59
+ for (const event of tokenEvents) {
60
+ const payload = event?.payload && typeof event.payload === "object" ? event.payload : {};
61
+ const total = pickNumeric(
62
+ payload?.total_tokens,
63
+ payload?.tokens_total,
64
+ payload?.token_count,
65
+ payload?.usage_total_tokens
66
+ );
67
+ if (total > 0) tokensUsed = Math.max(tokensUsed, total);
68
+ const prompt = toNumber(payload?.prompt_tokens || payload?.input_tokens);
69
+ const completion = toNumber(payload?.completion_tokens || payload?.output_tokens);
70
+ if (prompt + completion > tokensUsed) tokensUsed = prompt + completion;
71
+ }
72
+ const explicitBudget = hasExplicitBudgetLimits(run);
73
+ const maxIterations = pickNumeric(
74
+ run?.budget?.max_iterations,
75
+ run?.config?.max_iterations,
76
+ DERIVED_MAX_ITERATIONS
77
+ );
78
+ const maxTokens = pickNumeric(
79
+ run?.budget?.max_tokens,
80
+ run?.budget?.max_total_tokens,
81
+ run?.config?.max_total_tokens,
82
+ DERIVED_MAX_TOKENS
83
+ );
84
+ const maxWallTimeSecs = pickNumeric(
85
+ run?.budget?.max_wall_time_secs,
86
+ run?.config?.max_wall_time_secs,
87
+ DERIVED_MAX_WALL_TIME_SECS
88
+ );
89
+ const maxSubagentRuns = pickNumeric(
90
+ run?.budget?.max_subagent_runs,
91
+ run?.config?.max_subagent_runs,
92
+ Math.max(DERIVED_MAX_SUBAGENT_RUNS, tasks.length * 6)
93
+ );
94
+ const subagentRunsUsed = Array.isArray(events)
95
+ ? events.filter((row) =>
96
+ String(row?.type || "")
97
+ .toLowerCase()
98
+ .includes("task_completed")
99
+ ).length
100
+ : 0;
101
+ const measuredExceeded =
102
+ iterationsUsed >= maxIterations ||
103
+ tokensUsed >= maxTokens ||
104
+ wallTimeSecs >= maxWallTimeSecs ||
105
+ subagentRunsUsed >= maxSubagentRuns;
106
+ const exceeded = explicitBudget
107
+ ? Boolean(run?.budget?.exceeded) || measuredExceeded
108
+ : Boolean(run?.budget?.exceeded);
109
+ const exceededReason = explicitBudget
110
+ ? String(
111
+ run?.budget?.exceeded_reason || (exceeded ? "One or more execution limits exceeded." : "")
112
+ )
113
+ : "";
114
+ return {
115
+ max_iterations: maxIterations,
116
+ iterations_used: iterationsUsed,
117
+ max_tokens: maxTokens,
118
+ tokens_used: tokensUsed,
119
+ max_wall_time_secs: maxWallTimeSecs,
120
+ wall_time_secs: wallTimeSecs,
121
+ max_subagent_runs: maxSubagentRuns,
122
+ subagent_runs_used: subagentRunsUsed,
123
+ exceeded,
124
+ exceeded_reason: exceededReason,
125
+ limits_enforced: explicitBudget,
126
+ source: explicitBudget ? "run" : "derived",
127
+ };
128
+ }
129
+
130
+ export function inferStatusFromEvents(status, events) {
131
+ const normalized = String(status || "")
132
+ .trim()
133
+ .toLowerCase();
134
+ if (normalized && normalized !== "planning") return normalized;
135
+ const rows = Array.isArray(events) ? events : [];
136
+ let sawPlanReady = false;
137
+ let sawPlanApproved = false;
138
+ for (const row of rows) {
139
+ const type = String(row?.type || "")
140
+ .trim()
141
+ .toLowerCase();
142
+ if (type === "plan_ready_for_approval") sawPlanReady = true;
143
+ if (type === "plan_approved") sawPlanApproved = true;
144
+ }
145
+ if (sawPlanReady && !sawPlanApproved) return "awaiting_approval";
146
+ return normalized || "idle";
147
+ }