plugin-agent-orchestrator 1.0.20 → 1.0.21

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.
Files changed (98) hide show
  1. package/dist/client/hooks/useRunEventStream.d.ts +22 -0
  2. package/dist/client/index.d.ts +1 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/externalVersion.js +6 -6
  5. package/dist/server/collections/agent-execution-spans.js +24 -0
  6. package/dist/server/collections/agent-loop-runs.js +36 -0
  7. package/dist/server/collections/orchestrator-config.js +14 -0
  8. package/dist/server/migrations/20260601000000-add-token-fields.d.ts +7 -0
  9. package/dist/server/migrations/20260601000000-add-token-fields.js +101 -0
  10. package/dist/server/plugin.js +47 -0
  11. package/dist/server/resources/agent-loop.js +33 -25
  12. package/dist/server/resources/tracing.js +5 -8
  13. package/dist/server/services/AgentHarness.d.ts +2 -0
  14. package/dist/server/services/AgentHarness.js +56 -90
  15. package/dist/server/services/AgentLoopController.d.ts +33 -20
  16. package/dist/server/services/AgentLoopController.js +164 -125
  17. package/dist/server/services/AgentLoopRepository.js +16 -34
  18. package/dist/server/services/AgentLoopService.d.ts +28 -18
  19. package/dist/server/services/AgentLoopService.js +7 -1
  20. package/dist/server/services/AgentPlannerService.js +5 -25
  21. package/dist/server/services/AgentRegistryService.d.ts +8 -0
  22. package/dist/server/services/AgentRegistryService.js +34 -24
  23. package/dist/server/services/CircuitBreaker.d.ts +40 -0
  24. package/dist/server/services/CircuitBreaker.js +120 -0
  25. package/dist/server/services/ContextAggregator.d.ts +45 -0
  26. package/dist/server/services/ContextAggregator.js +201 -0
  27. package/dist/server/services/ExecutionSpanService.js +2 -5
  28. package/dist/server/services/RunEventBus.d.ts +9 -0
  29. package/dist/server/services/RunEventBus.js +73 -0
  30. package/dist/server/services/TokenTracker.d.ts +62 -0
  31. package/dist/server/services/TokenTracker.js +173 -0
  32. package/dist/server/tools/agent-loop.d.ts +8 -8
  33. package/dist/server/tools/agent-loop.js +30 -63
  34. package/dist/server/tools/delegate-task.js +14 -72
  35. package/dist/server/tools/orchestrator-plan.d.ts +6 -6
  36. package/dist/server/tools/orchestrator-plan.js +10 -47
  37. package/dist/server/types.d.ts +47 -0
  38. package/dist/server/types.js +24 -0
  39. package/dist/server/utils/ctx-utils.d.ts +30 -0
  40. package/dist/server/utils/ctx-utils.js +152 -0
  41. package/dist/server/utils/logging.d.ts +6 -0
  42. package/dist/server/utils/logging.js +86 -0
  43. package/package.json +44 -44
  44. package/src/client/AgentRunsTab.tsx +764 -764
  45. package/src/client/HarnessProfilesTab.tsx +247 -247
  46. package/src/client/OrchestratorSettings.tsx +106 -106
  47. package/src/client/RulesTab.tsx +716 -716
  48. package/src/client/hooks/useRunEventStream.ts +76 -0
  49. package/src/client/index.tsx +2 -1
  50. package/src/client/plugin.tsx +27 -27
  51. package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
  52. package/src/client/skill-hub/index.tsx +51 -51
  53. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +99 -99
  54. package/src/client/skill-hub/tools/SkillHubCard.tsx +109 -109
  55. package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
  56. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -58
  57. package/src/client/tools/PlanApprovalCard.tsx +175 -175
  58. package/src/client/tools/registerOrchestratorCards.ts +7 -7
  59. package/src/server/__tests__/agent-loop-controller.test.ts +375 -0
  60. package/src/server/__tests__/circuit-breaker.test.ts +169 -0
  61. package/src/server/__tests__/context-aggregator.test.ts +222 -0
  62. package/src/server/__tests__/parallel-execution.test.ts +318 -0
  63. package/src/server/__tests__/smoke.test.ts +120 -0
  64. package/src/server/collections/agent-execution-spans.ts +24 -0
  65. package/src/server/collections/agent-harness-profiles.ts +59 -59
  66. package/src/server/collections/agent-loop-events.ts +71 -71
  67. package/src/server/collections/agent-loop-runs.ts +38 -1
  68. package/src/server/collections/agent-loop-steps.ts +144 -144
  69. package/src/server/collections/orchestrator-config.ts +14 -0
  70. package/src/server/collections/skill-executions.ts +106 -106
  71. package/src/server/collections/skill-loop-configs.ts +65 -65
  72. package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
  73. package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -142
  74. package/src/server/migrations/20260601000000-add-token-fields.ts +89 -0
  75. package/src/server/plugin.ts +53 -0
  76. package/src/server/resources/agent-loop.ts +21 -12
  77. package/src/server/resources/tracing.ts +3 -7
  78. package/src/server/services/AgentHarness.ts +78 -116
  79. package/src/server/services/AgentLoopController.ts +197 -122
  80. package/src/server/services/AgentLoopRepository.ts +9 -25
  81. package/src/server/services/AgentLoopService.ts +13 -1
  82. package/src/server/services/AgentPlanValidator.ts +73 -73
  83. package/src/server/services/AgentPlannerService.ts +2 -25
  84. package/src/server/services/AgentRegistryService.ts +40 -31
  85. package/src/server/services/CircuitBreaker.ts +116 -0
  86. package/src/server/services/ContextAggregator.ts +239 -0
  87. package/src/server/services/ExecutionSpanService.ts +2 -4
  88. package/src/server/services/RunEventBus.ts +45 -0
  89. package/src/server/services/TokenTracker.ts +209 -0
  90. package/src/server/skill-hub/plugin.ts +898 -898
  91. package/src/server/skill-hub/tasks/SkillExecutionTask.ts +460 -460
  92. package/src/server/tools/agent-loop.ts +18 -57
  93. package/src/server/tools/delegate-task.ts +11 -93
  94. package/src/server/tools/orchestrator-plan.ts +26 -50
  95. package/src/server/tools/skill-execute.ts +160 -160
  96. package/src/server/types.ts +55 -0
  97. package/src/server/utils/ctx-utils.ts +118 -0
  98. package/src/server/utils/logging.ts +63 -0
@@ -0,0 +1,45 @@
1
+ type EventCallback = (event: any) => void;
2
+
3
+ class RunEventBusImpl {
4
+ private listeners = new Map<string | number, Set<EventCallback>>();
5
+
6
+ subscribe(runId: string | number, callback: EventCallback) {
7
+ let set = this.listeners.get(runId);
8
+ if (!set) {
9
+ set = new Set();
10
+ this.listeners.set(runId, set);
11
+ }
12
+ set.add(callback);
13
+ return () => {
14
+ set?.delete(callback);
15
+ if (set?.size === 0) {
16
+ this.listeners.delete(runId);
17
+ }
18
+ };
19
+ }
20
+
21
+ emit(runId: string | number, event: any) {
22
+ const set = this.listeners.get(runId);
23
+ if (!set) return;
24
+ for (const callback of set) {
25
+ try {
26
+ callback(event);
27
+ } catch {
28
+ // ignore per-listener errors
29
+ }
30
+ }
31
+ }
32
+
33
+ listenerCount(runId: string | number): number {
34
+ return this.listeners.get(runId)?.size || 0;
35
+ }
36
+ }
37
+
38
+ let instance: RunEventBusImpl | null = null;
39
+
40
+ export function getRunEventBus(): RunEventBusImpl {
41
+ if (!instance) {
42
+ instance = new RunEventBusImpl();
43
+ }
44
+ return instance;
45
+ }
@@ -0,0 +1,209 @@
1
+ export interface TokenUsage {
2
+ inputTokens: number;
3
+ outputTokens: number;
4
+ totalTokens: number;
5
+ cost: number;
6
+ }
7
+
8
+ export interface BudgetConfig {
9
+ budgetMaxTokens?: number;
10
+ budgetMaxCost?: number;
11
+ }
12
+
13
+ export interface BudgetCheckResult {
14
+ allowed: boolean;
15
+ reason?: string;
16
+ }
17
+
18
+ /**
19
+ * Default pricing per 1K tokens (USD).
20
+ * These are conservative estimates; override via env vars:
21
+ * ORCHESTRATOR_PRICE_PER_1K_INPUT
22
+ * ORCHESTRATOR_PRICE_PER_1K_OUTPUT
23
+ */
24
+ const PRICE_PER_1K_INPUT = Number(process.env.ORCHESTRATOR_PRICE_PER_1K_INPUT || 0.003);
25
+ const PRICE_PER_1K_OUTPUT = Number(process.env.ORCHESTRATOR_PRICE_PER_1K_OUTPUT || 0.015);
26
+
27
+ /**
28
+ * Estimate cost based on token counts.
29
+ */
30
+ function estimateCost(inputTokens: number, outputTokens: number): number {
31
+ return (inputTokens / 1000) * PRICE_PER_1K_INPUT + (outputTokens / 1000) * PRICE_PER_1K_OUTPUT;
32
+ }
33
+
34
+ /**
35
+ * Extract usage_metadata from the final state returned by createReactAgent.invoke().
36
+ *
37
+ * LangChain's createReactAgent returns a final state object with a `messages` array.
38
+ * The last AIMessage in that array carries `usage_metadata` populated by the
39
+ * LLM provider after the final generation step. This is the standard LangChain
40
+ * approach — no private API access needed.
41
+ *
42
+ * Expected shape (from @langchain/core/messages/ai):
43
+ * AIMessage.usage_metadata = {
44
+ * input_tokens: number,
45
+ * output_tokens: number,
46
+ * total_tokens: number,
47
+ * }
48
+ */
49
+ export function extractTokenUsage(finalState: any): TokenUsage | null {
50
+ if (!finalState?.messages || !Array.isArray(finalState.messages)) return null;
51
+
52
+ // Accumulate usage across all AI messages in the run (each LLM call adds usage)
53
+ let totalInput = 0;
54
+ let totalOutput = 0;
55
+ let totalAll = 0;
56
+
57
+ for (const msg of finalState.messages) {
58
+ if (msg?.usage_metadata) {
59
+ const um = msg.usage_metadata;
60
+ totalInput += um.input_tokens || 0;
61
+ totalOutput += um.output_tokens || 0;
62
+ totalAll += um.total_tokens || 0;
63
+ }
64
+ }
65
+
66
+ if (totalAll === 0) return null;
67
+
68
+ return {
69
+ inputTokens: totalInput,
70
+ outputTokens: totalOutput,
71
+ totalTokens: totalAll,
72
+ cost: estimateCost(totalInput, totalOutput),
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Service for tracking token consumption across agent runs and spans.
78
+ *
79
+ * Responsibilities:
80
+ * 1. Parse token usage from LangGraph execution results
81
+ * 2. Persist token counts to agentExecutionSpans
82
+ * 3. Accumulate totals at the agentLoopRuns level
83
+ * 4. Enforce budget limits (max tokens / max cost per run)
84
+ */
85
+ export class TokenTracker {
86
+ constructor(private readonly plugin: any) {}
87
+
88
+ get db() {
89
+ return this.plugin.db;
90
+ }
91
+
92
+ get app() {
93
+ return this.plugin.app;
94
+ }
95
+
96
+ /**
97
+ * Track token usage for a single execution span.
98
+ * Updates the span record and accumulates into the parent run.
99
+ *
100
+ * @param spanId - The agentExecutionSpans.id to update
101
+ * @param usage - Parsed token usage from extractTokenUsage()
102
+ * @param runId - Optional agentLoopRuns.id to accumulate totals
103
+ */
104
+ async trackSpan(spanId: string | number | undefined, usage: TokenUsage, runId?: string | number): Promise<void> {
105
+ if (!spanId) return;
106
+
107
+ try {
108
+ const repo = this.db.getRepository('agentExecutionSpans');
109
+ if (!repo) return;
110
+
111
+ await repo.update({
112
+ filterByTk: spanId,
113
+ values: {
114
+ inputTokens: usage.inputTokens,
115
+ outputTokens: usage.outputTokens,
116
+ totalTokens: usage.totalTokens,
117
+ cost: usage.cost,
118
+ },
119
+ });
120
+
121
+ if (runId != null) {
122
+ await this.accumulateToRun(runId);
123
+ }
124
+ } catch (e: any) {
125
+ this.app.log?.warn?.('[TokenTracker] Failed to track span tokens', e);
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Recalculate total token/cost for a run by summing all its spans.
131
+ */
132
+ async accumulateToRun(runId: string | number): Promise<void> {
133
+ try {
134
+ const spansRepo = this.db.getRepository('agentExecutionSpans');
135
+ if (!spansRepo) return;
136
+
137
+ const spans = await spansRepo.find({
138
+ filter: { 'metadata.agentLoopRunId': String(runId) },
139
+ });
140
+
141
+ let totalInput = 0;
142
+ let totalOutput = 0;
143
+ let totalCost = 0;
144
+
145
+ for (const span of spans) {
146
+ totalInput += Number(span.get?.('inputTokens') || span.inputTokens || 0);
147
+ totalOutput += Number(span.get?.('outputTokens') || span.outputTokens || 0);
148
+ totalCost += Number(span.get?.('cost') || span.cost || 0);
149
+ }
150
+
151
+ const runsRepo = this.db.getRepository('agentLoopRuns');
152
+ if (!runsRepo) return;
153
+
154
+ await runsRepo.update({
155
+ filterByTk: runId,
156
+ values: {
157
+ totalInputTokens: totalInput,
158
+ totalOutputTokens: totalOutput,
159
+ totalTokens: totalInput + totalOutput,
160
+ totalCost,
161
+ },
162
+ });
163
+ } catch (e: any) {
164
+ this.app.log?.warn?.('[TokenTracker] Failed to accumulate run totals', e);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * Check if a run has exceeded its budget limits.
170
+ */
171
+ async checkBudget(runId: string | number): Promise<BudgetCheckResult> {
172
+ try {
173
+ const repo = this.db.getRepository('agentLoopRuns');
174
+ if (!repo) return { allowed: true };
175
+
176
+ const run = await repo.findOne({ filter: { id: runId } });
177
+ if (!run) return { allowed: true };
178
+
179
+ const budgetMaxTokens = Number(run.get?.('budgetMaxTokens') ?? 0);
180
+ const budgetMaxCost = Number(run.get?.('budgetMaxCost') ?? 0);
181
+
182
+ if (budgetMaxTokens <= 0 && budgetMaxCost <= 0) return { allowed: true };
183
+
184
+ const totalTokens = Number(run.get?.('totalTokens') || 0);
185
+ const totalCost = Number(run.get?.('totalCost') || 0);
186
+
187
+ if (budgetMaxTokens > 0 && totalTokens >= budgetMaxTokens) {
188
+ return {
189
+ allowed: false,
190
+ reason: `Budget exceeded: ${totalTokens}/${budgetMaxTokens} tokens used. Maximum allowed tokens for this run: ${budgetMaxTokens}.`,
191
+ };
192
+ }
193
+
194
+ if (budgetMaxCost > 0 && totalCost >= budgetMaxCost) {
195
+ return {
196
+ allowed: false,
197
+ reason: `Budget exceeded: $${totalCost.toFixed(4)}/$${budgetMaxCost.toFixed(
198
+ 4,
199
+ )} spent. Maximum allowed cost for this run: $${budgetMaxCost}.`,
200
+ };
201
+ }
202
+
203
+ return { allowed: true };
204
+ } catch (e: any) {
205
+ this.app.log?.warn?.('[TokenTracker] Budget check failed, allowing', e);
206
+ return { allowed: true };
207
+ }
208
+ }
209
+ }