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
@@ -11,12 +11,12 @@ module.exports = {
11
11
  "react": "18.2.0",
12
12
  "antd": "5.24.2",
13
13
  "@ant-design/icons": "5.6.1",
14
- "@nocobase/client": "2.0.56",
15
- "@nocobase/server": "2.0.56",
16
- "@nocobase/database": "2.0.56",
14
+ "@nocobase/client": "2.0.60",
15
+ "@nocobase/server": "2.0.60",
16
+ "@nocobase/database": "2.0.60",
17
17
  "@langchain/langgraph": "0.2.74",
18
18
  "@langchain/core": "0.3.80",
19
- "@nocobase/actions": "2.0.56",
20
- "@nocobase/plugin-ai": "2.0.56",
21
- "@nocobase/ai": "2.0.56"
19
+ "@nocobase/actions": "2.0.60",
20
+ "@nocobase/plugin-ai": "2.0.60",
21
+ "@nocobase/ai": "2.0.60"
22
22
  };
@@ -101,6 +101,30 @@ var agent_execution_spans_default = (0, import_database.defineCollection)({
101
101
  name: "durationMs",
102
102
  type: "integer"
103
103
  },
104
+ {
105
+ name: "inputTokens",
106
+ type: "integer",
107
+ defaultValue: 0,
108
+ comment: "Number of input/prompt tokens consumed by this span"
109
+ },
110
+ {
111
+ name: "outputTokens",
112
+ type: "integer",
113
+ defaultValue: 0,
114
+ comment: "Number of output/completion tokens generated by this span"
115
+ },
116
+ {
117
+ name: "totalTokens",
118
+ type: "integer",
119
+ defaultValue: 0,
120
+ comment: "Total tokens consumed by this span (input + output)"
121
+ },
122
+ {
123
+ name: "cost",
124
+ type: "float",
125
+ defaultValue: 0,
126
+ comment: "Estimated cost in USD for this span"
127
+ },
104
128
  {
105
129
  name: "startedAt",
106
130
  type: "date"
@@ -143,6 +143,42 @@ var agent_loop_runs_default = (0, import_database.defineCollection)({
143
143
  name: "summary",
144
144
  type: "text"
145
145
  },
146
+ {
147
+ name: "totalInputTokens",
148
+ type: "integer",
149
+ defaultValue: 0,
150
+ comment: "Accumulated input/prompt tokens across all spans in this run"
151
+ },
152
+ {
153
+ name: "totalOutputTokens",
154
+ type: "integer",
155
+ defaultValue: 0,
156
+ comment: "Accumulated output/completion tokens across all spans in this run"
157
+ },
158
+ {
159
+ name: "totalTokens",
160
+ type: "integer",
161
+ defaultValue: 0,
162
+ comment: "Total accumulated tokens (input + output) across all spans in this run"
163
+ },
164
+ {
165
+ name: "totalCost",
166
+ type: "float",
167
+ defaultValue: 0,
168
+ comment: "Estimated total cost in USD across all spans in this run"
169
+ },
170
+ {
171
+ name: "budgetMaxTokens",
172
+ type: "integer",
173
+ allowNull: true,
174
+ comment: "Maximum allowed tokens for this run (null = unlimited)"
175
+ },
176
+ {
177
+ name: "budgetMaxCost",
178
+ type: "float",
179
+ allowNull: true,
180
+ comment: "Maximum allowed cost in USD for this run (null = unlimited)"
181
+ },
146
182
  {
147
183
  name: "metadata",
148
184
  type: "json",
@@ -75,6 +75,20 @@ var orchestrator_config_default = (0, import_database.defineCollection)({
75
75
  defaultValue: 50,
76
76
  comment: "Max LangGraph reasoning steps (tool-call + LLM-step iterations) per delegation. Lower = safer; higher = more complex multi-step tasks. Default 50."
77
77
  },
78
+ {
79
+ name: "budgetMaxTokens",
80
+ type: "integer",
81
+ defaultValue: 0,
82
+ allowNull: true,
83
+ comment: "Maximum tokens allowed per sub-agent invocation (0 = unlimited)"
84
+ },
85
+ {
86
+ name: "budgetMaxCost",
87
+ type: "float",
88
+ defaultValue: 0,
89
+ allowNull: true,
90
+ comment: "Maximum cost in USD allowed per sub-agent invocation (0 = unlimited)"
91
+ },
78
92
  {
79
93
  name: "harnessTag",
80
94
  type: "string",
@@ -0,0 +1,7 @@
1
+ import { Migration } from '@nocobase/server';
2
+ export default class AddTokenFieldsMigration extends Migration {
3
+ on: string;
4
+ appVersion: string;
5
+ up(): Promise<void>;
6
+ down(): Promise<void>;
7
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
14
+ var __export = (target, all) => {
15
+ for (var name in all)
16
+ __defProp(target, name, { get: all[name], enumerable: true });
17
+ };
18
+ var __copyProps = (to, from, except, desc) => {
19
+ if (from && typeof from === "object" || typeof from === "function") {
20
+ for (let key of __getOwnPropNames(from))
21
+ if (!__hasOwnProp.call(to, key) && key !== except)
22
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
23
+ }
24
+ return to;
25
+ };
26
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
27
+ var add_token_fields_exports = {};
28
+ __export(add_token_fields_exports, {
29
+ default: () => AddTokenFieldsMigration
30
+ });
31
+ module.exports = __toCommonJS(add_token_fields_exports);
32
+ var import_server = require("@nocobase/server");
33
+ class AddTokenFieldsMigration extends import_server.Migration {
34
+ on = "afterLoad";
35
+ appVersion = "<=2.x";
36
+ async up() {
37
+ const queryInterface = this.db.sequelize.getQueryInterface();
38
+ const DataTypes = this.db.sequelize.constructor["DataTypes"];
39
+ const prefix = this.db.options.tablePrefix || "";
40
+ const spansTable = `${prefix}agentExecutionSpans`;
41
+ const spansExists = await queryInterface.describeTable(spansTable).then(() => true).catch(() => false);
42
+ if (spansExists) {
43
+ const spansCols = await queryInterface.describeTable(spansTable);
44
+ const addSpanIfMissing = async (name, definition) => {
45
+ if (!spansCols[name]) {
46
+ await queryInterface.addColumn(spansTable, name, definition);
47
+ }
48
+ };
49
+ await addSpanIfMissing("inputTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
50
+ await addSpanIfMissing("outputTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
51
+ await addSpanIfMissing("totalTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
52
+ await addSpanIfMissing("cost", { type: DataTypes.FLOAT, allowNull: false, defaultValue: 0 });
53
+ }
54
+ const runsTable = `${prefix}agentLoopRuns`;
55
+ const runsExists = await queryInterface.describeTable(runsTable).then(() => true).catch(() => false);
56
+ if (runsExists) {
57
+ const runsCols = await queryInterface.describeTable(runsTable);
58
+ const addRunIfMissing = async (name, definition) => {
59
+ if (!runsCols[name]) {
60
+ await queryInterface.addColumn(runsTable, name, definition);
61
+ }
62
+ };
63
+ await addRunIfMissing("totalInputTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
64
+ await addRunIfMissing("totalOutputTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
65
+ await addRunIfMissing("totalTokens", { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 });
66
+ await addRunIfMissing("totalCost", { type: DataTypes.FLOAT, allowNull: false, defaultValue: 0 });
67
+ await addRunIfMissing("budgetMaxTokens", { type: DataTypes.INTEGER, allowNull: true });
68
+ await addRunIfMissing("budgetMaxCost", { type: DataTypes.FLOAT, allowNull: true });
69
+ }
70
+ const configTable = `${prefix}orchestratorConfig`;
71
+ const configExists = await queryInterface.describeTable(configTable).then(() => true).catch(() => false);
72
+ if (configExists) {
73
+ const configCols = await queryInterface.describeTable(configTable);
74
+ const addConfigIfMissing = async (name, definition) => {
75
+ if (!configCols[name]) {
76
+ await queryInterface.addColumn(configTable, name, definition);
77
+ }
78
+ };
79
+ await addConfigIfMissing("budgetMaxTokens", { type: DataTypes.INTEGER, allowNull: true, defaultValue: 0 });
80
+ await addConfigIfMissing("budgetMaxCost", { type: DataTypes.FLOAT, allowNull: true, defaultValue: 0 });
81
+ }
82
+ }
83
+ async down() {
84
+ const queryInterface = this.db.sequelize.getQueryInterface();
85
+ const prefix = this.db.options.tablePrefix || "";
86
+ const tables = [
87
+ { name: `${prefix}agentExecutionSpans`, cols: ["inputTokens", "outputTokens", "totalTokens", "cost"] },
88
+ {
89
+ name: `${prefix}agentLoopRuns`,
90
+ cols: ["totalInputTokens", "totalOutputTokens", "totalTokens", "totalCost", "budgetMaxTokens", "budgetMaxCost"]
91
+ },
92
+ { name: `${prefix}orchestratorConfig`, cols: ["budgetMaxTokens", "budgetMaxCost"] }
93
+ ];
94
+ for (const { name, cols } of tables) {
95
+ for (const col of cols) {
96
+ await queryInterface.removeColumn(name, col).catch(() => {
97
+ });
98
+ }
99
+ }
100
+ }
101
+ }
@@ -47,6 +47,7 @@ var import_external_rag_search = require("./tools/external-rag-search");
47
47
  var import_orchestrator_plan = require("./tools/orchestrator-plan");
48
48
  var import_tracing = require("./resources/tracing");
49
49
  var import_agent_loop = require("./resources/agent-loop");
50
+ var import_RunEventBus = require("./services/RunEventBus");
50
51
  var import_plugin = __toESM(require("./skill-hub/plugin"));
51
52
  var import_AgentLoopService = require("./services/AgentLoopService");
52
53
  class PluginAgentOrchestratorServer extends import_server.Plugin {
@@ -75,6 +76,7 @@ class PluginAgentOrchestratorServer extends import_server.Plugin {
75
76
  "agentLoopRuns:*",
76
77
  "agentLoopSteps:*",
77
78
  "agentLoopEvents:*",
79
+ "agentLoopEventsStream:*",
78
80
  "agentHarnessProfiles:*",
79
81
  "agentExecutionSpans:*",
80
82
  "skillDefinitions:*",
@@ -89,6 +91,51 @@ class PluginAgentOrchestratorServer extends import_server.Plugin {
89
91
  toolsManager.registerTools((0, import_external_rag_search.createExternalRagSearchTool)(this));
90
92
  toolsManager.registerDynamicTools((0, import_delegate_task.createDelegateToolsProvider)(this));
91
93
  (0, import_agent_loop.registerAgentLoopResource)(this, this.agentLoopService);
94
+ this.app.resource({
95
+ name: "agentLoopEventsStream",
96
+ actions: {
97
+ async stream(ctx, next) {
98
+ var _a, _b, _c;
99
+ const runId = ((_a = ctx.action.params) == null ? void 0 : _a.runId) || ((_b = ctx.query) == null ? void 0 : _b.runId) || ((_c = ctx.request.query) == null ? void 0 : _c.runId);
100
+ if (!runId) {
101
+ ctx.throw(400, "runId query parameter is required.");
102
+ return;
103
+ }
104
+ ctx.type = "text/event-stream";
105
+ ctx.set("Cache-Control", "no-cache");
106
+ ctx.set("Connection", "keep-alive");
107
+ ctx.set("X-Accel-Buffering", "no");
108
+ const unsubscribe = (0, import_RunEventBus.getRunEventBus)().subscribe(runId, (event) => {
109
+ try {
110
+ ctx.res.write(`data: ${JSON.stringify(event)}
111
+
112
+ `);
113
+ } catch {
114
+ unsubscribe();
115
+ }
116
+ });
117
+ const keepalive = setInterval(() => {
118
+ try {
119
+ ctx.res.write(": keepalive\n\n");
120
+ } catch {
121
+ clearInterval(keepalive);
122
+ unsubscribe();
123
+ }
124
+ }, 15e3);
125
+ ctx.req.on("close", () => {
126
+ clearInterval(keepalive);
127
+ unsubscribe();
128
+ });
129
+ ctx.req.on("error", () => {
130
+ clearInterval(keepalive);
131
+ unsubscribe();
132
+ });
133
+ ctx.res.writeHead(200);
134
+ ctx.res.write(": connected\n\n");
135
+ await next();
136
+ }
137
+ }
138
+ });
92
139
  (0, import_tracing.registerTracingResource)(this);
93
140
  this.app.cronJobManager.addJob({
94
141
  cronTime: "0 30 2 * * *",
@@ -29,20 +29,9 @@ __export(agent_loop_exports, {
29
29
  registerAgentLoopResource: () => registerAgentLoopResource
30
30
  });
31
31
  module.exports = __toCommonJS(agent_loop_exports);
32
- function toPlain(record) {
33
- var _a;
34
- return ((_a = record == null ? void 0 : record.toJSON) == null ? void 0 : _a.call(record)) || record;
35
- }
36
- function currentUserId(ctx) {
37
- var _a, _b, _c, _d;
38
- return ((_b = (_a = ctx == null ? void 0 : ctx.state) == null ? void 0 : _a.currentUser) == null ? void 0 : _b.id) || ((_d = (_c = ctx == null ? void 0 : ctx.auth) == null ? void 0 : _c.user) == null ? void 0 : _d.id);
39
- }
40
- function values(ctx) {
41
- var _a, _b, _c;
42
- return ((_a = ctx.request) == null ? void 0 : _a.body) || ((_c = (_b = ctx.action) == null ? void 0 : _b.params) == null ? void 0 : _c.values) || {};
43
- }
32
+ var import_ctx_utils = require("../utils/ctx-utils");
44
33
  function formatRunRow(raw) {
45
- const row = toPlain(raw);
34
+ const row = (0, import_ctx_utils.toPlain)(raw);
46
35
  return {
47
36
  id: row.id,
48
37
  rootRunId: row.rootRunId,
@@ -105,7 +94,7 @@ function registerAgentLoopResource(plugin, service) {
105
94
  await next();
106
95
  },
107
96
  async resume(ctx, next) {
108
- const body = values(ctx);
97
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
109
98
  const runId = body.runId || ctx.action.params.filterByTk;
110
99
  if (!runId) {
111
100
  ctx.throw(400, "runId is required");
@@ -116,14 +105,14 @@ function registerAgentLoopResource(plugin, service) {
116
105
  stepId: body.stepId,
117
106
  approved: body.approved !== false,
118
107
  editedInput: body.editedInput,
119
- userId: currentUserId(ctx),
108
+ userId: (0, import_ctx_utils.currentUserId)(ctx),
120
109
  ctx
121
110
  })
122
111
  };
123
112
  await next();
124
113
  },
125
114
  async approvePlan(ctx, next) {
126
- const body = values(ctx);
115
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
127
116
  const runId = body.runId || ctx.action.params.filterByTk;
128
117
  if (!runId) {
129
118
  ctx.throw(400, "runId is required");
@@ -131,7 +120,7 @@ function registerAgentLoopResource(plugin, service) {
131
120
  }
132
121
  ctx.body = {
133
122
  data: await service.approvePlanAndExecute(runId, {
134
- userId: currentUserId(ctx),
123
+ userId: (0, import_ctx_utils.currentUserId)(ctx),
135
124
  ctx,
136
125
  reason: body.reason
137
126
  })
@@ -139,7 +128,7 @@ function registerAgentLoopResource(plugin, service) {
139
128
  await next();
140
129
  },
141
130
  async rejectPlan(ctx, next) {
142
- const body = values(ctx);
131
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
143
132
  const runId = body.runId || ctx.action.params.filterByTk;
144
133
  if (!runId) {
145
134
  ctx.throw(400, "runId is required");
@@ -148,13 +137,13 @@ function registerAgentLoopResource(plugin, service) {
148
137
  ctx.body = {
149
138
  data: await service.rejectPlan(runId, {
150
139
  reason: body.reason,
151
- userId: currentUserId(ctx)
140
+ userId: (0, import_ctx_utils.currentUserId)(ctx)
152
141
  })
153
142
  };
154
143
  await next();
155
144
  },
156
145
  async requestPlanChanges(ctx, next) {
157
- const body = values(ctx);
146
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
158
147
  const runId = body.runId || ctx.action.params.filterByTk;
159
148
  if (!runId) {
160
149
  ctx.throw(400, "runId is required");
@@ -163,13 +152,13 @@ function registerAgentLoopResource(plugin, service) {
163
152
  ctx.body = {
164
153
  data: await service.requestPlanChanges(runId, {
165
154
  feedback: body.feedback,
166
- userId: currentUserId(ctx)
155
+ userId: (0, import_ctx_utils.currentUserId)(ctx)
167
156
  })
168
157
  };
169
158
  await next();
170
159
  },
171
160
  async cancel(ctx, next) {
172
- const body = values(ctx);
161
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
173
162
  const runId = body.runId || ctx.action.params.filterByTk;
174
163
  if (!runId) {
175
164
  ctx.throw(400, "runId is required");
@@ -178,23 +167,42 @@ function registerAgentLoopResource(plugin, service) {
178
167
  ctx.body = {
179
168
  data: await service.cancelRun(runId, {
180
169
  reason: body.reason,
181
- userId: currentUserId(ctx)
170
+ userId: (0, import_ctx_utils.currentUserId)(ctx)
182
171
  })
183
172
  };
184
173
  await next();
185
174
  },
186
175
  async retryStep(ctx, next) {
187
- const body = values(ctx);
176
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
188
177
  if (!body.stepId) {
189
178
  ctx.throw(400, "stepId is required");
190
179
  return;
191
180
  }
192
181
  ctx.body = {
193
182
  data: await service.retryStep(body.stepId, {
194
- userId: currentUserId(ctx)
183
+ userId: (0, import_ctx_utils.currentUserId)(ctx)
195
184
  })
196
185
  };
197
186
  await next();
187
+ },
188
+ async stepFeedback(ctx, next) {
189
+ const body = (0, import_ctx_utils.valuesFromCtx)(ctx);
190
+ if (!body.stepId || !body.rating) {
191
+ ctx.throw(400, "stepId and rating are required");
192
+ return;
193
+ }
194
+ if (!["positive", "negative"].includes(body.rating)) {
195
+ ctx.throw(400, 'rating must be "positive" or "negative"');
196
+ return;
197
+ }
198
+ ctx.body = {
199
+ data: await service.stepFeedback(
200
+ body.stepId,
201
+ { rating: body.rating, comment: body.comment, category: body.category },
202
+ { userId: (0, import_ctx_utils.currentUserId)(ctx) }
203
+ )
204
+ };
205
+ await next();
198
206
  }
199
207
  }
200
208
  });
@@ -29,10 +29,7 @@ __export(tracing_exports, {
29
29
  registerTracingResource: () => registerTracingResource
30
30
  });
31
31
  module.exports = __toCommonJS(tracing_exports);
32
- function toPlain(row) {
33
- var _a;
34
- return ((_a = row == null ? void 0 : row.toJSON) == null ? void 0 : _a.call(row)) || row;
35
- }
32
+ var import_ctx_utils = require("../utils/ctx-utils");
36
33
  function normalizeSpanFilter(filter = {}) {
37
34
  const next = { ...filter };
38
35
  if (next.subAgentUsername) {
@@ -47,7 +44,7 @@ function spanTitle(row) {
47
44
  return row.toolName || row.type || "span";
48
45
  }
49
46
  function formatSpanListRow(raw) {
50
- const row = toPlain(raw);
47
+ const row = (0, import_ctx_utils.toPlain)(raw);
51
48
  const metadata = row.metadata || {};
52
49
  const input = row.input || {};
53
50
  return {
@@ -74,7 +71,7 @@ function formatSpanListRow(raw) {
74
71
  };
75
72
  }
76
73
  function buildSpanTree(rows) {
77
- const plainRows = rows.map(toPlain);
74
+ const plainRows = rows.map(import_ctx_utils.toPlain);
78
75
  const byId = /* @__PURE__ */ new Map();
79
76
  const roots = [];
80
77
  for (const row of plainRows) {
@@ -98,7 +95,7 @@ function buildSpanTree(rows) {
98
95
  return roots;
99
96
  }
100
97
  function flattenSpanTimeline(rows) {
101
- return rows.map(toPlain).sort(
98
+ return rows.map(import_ctx_utils.toPlain).sort(
102
99
  (a, b) => new Date(a.startedAt || a.createdAt || 0).getTime() - new Date(b.startedAt || b.createdAt || 0).getTime()
103
100
  ).map((row) => {
104
101
  const input = row.input || {};
@@ -211,7 +208,7 @@ function registerTracingResource(plugin) {
211
208
  filter: { id: filterByTk }
212
209
  });
213
210
  if (span) {
214
- const plainSpan = toPlain(span);
211
+ const plainSpan = (0, import_ctx_utils.toPlain)(span);
215
212
  const rows = await spanRepo.find({
216
213
  filter: { rootRunId: plainSpan.rootRunId },
217
214
  sort: ["createdAt"]
@@ -3,6 +3,8 @@ export declare class AgentHarness {
3
3
  private readonly plugin;
4
4
  private readonly registryService;
5
5
  private readonly spanService;
6
+ private readonly tokenTracker;
7
+ private readonly contextAggregator;
6
8
  constructor(plugin: any, registryService: AgentRegistryService);
7
9
  get db(): any;
8
10
  get app(): any;