@axiom-lattice/gateway 3.0.2 → 3.0.3

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 (155) hide show
  1. package/dist/index.js +31 -9
  2. package/dist/index.js.map +1 -1
  3. package/dist/index.mjs +31 -9
  4. package/dist/index.mjs.map +1 -1
  5. package/package.json +9 -6
  6. package/.eslintrc.json +0 -22
  7. package/.turbo/turbo-build.log +0 -46
  8. package/AGENTS.md +0 -112
  9. package/CHANGELOG.md +0 -1190
  10. package/LOGGER_CONFIG.md +0 -135
  11. package/RESUME_STREAM_CONTENT_BASED.md +0 -326
  12. package/RESUME_STREAM_README.md +0 -388
  13. package/jest.config.js +0 -72
  14. package/public/sdk/README.md +0 -695
  15. package/public/sdk/dashboard-engine-skill.md +0 -1122
  16. package/public/sdk/dashboard-single-file-spec.md +0 -357
  17. package/public/sdk/data-query-sdk-skill.md +0 -307
  18. package/public/sdk/data-query-sdk.d.ts +0 -252
  19. package/public/sdk/data-query-sdk.js +0 -970
  20. package/public/sdk/occupancy-dashboard.html +0 -363
  21. package/public/sdk/test-dashboard.html +0 -690
  22. package/scripts/seed-lark-installation.ts +0 -54
  23. package/src/__tests__/__mocks__/e2b.ts +0 -1
  24. package/src/__tests__/a2a.test.ts +0 -428
  25. package/src/__tests__/channel-installations.test.ts +0 -199
  26. package/src/__tests__/data-query.test.ts +0 -77
  27. package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
  28. package/src/__tests__/setup.ts +0 -5
  29. package/src/__tests__/workspace.test.ts +0 -400
  30. package/src/channels/__tests__/routes.test.ts +0 -50
  31. package/src/channels/lark/LarkChannelAdapter.ts +0 -217
  32. package/src/channels/lark/README.md +0 -187
  33. package/src/channels/lark/__tests__/controller.test.ts +0 -136
  34. package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
  35. package/src/channels/lark/__tests__/parser.test.ts +0 -72
  36. package/src/channels/lark/__tests__/sender.test.ts +0 -37
  37. package/src/channels/lark/__tests__/verification.test.ts +0 -157
  38. package/src/channels/lark/controller.ts +0 -48
  39. package/src/channels/lark/mapping-service.ts +0 -138
  40. package/src/channels/lark/parser.ts +0 -68
  41. package/src/channels/lark/routes.ts +0 -22
  42. package/src/channels/lark/sender.ts +0 -58
  43. package/src/channels/lark/types.ts +0 -20
  44. package/src/channels/lark/verification.ts +0 -32
  45. package/src/channels/registry.ts +0 -20
  46. package/src/channels/routes.ts +0 -30
  47. package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
  48. package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
  49. package/src/channels/wechat/context-store.ts +0 -54
  50. package/src/channels/wechat/controller.ts +0 -59
  51. package/src/channels/wechat/routes.ts +0 -7
  52. package/src/channels/wechat/types.ts +0 -62
  53. package/src/channels/wechat/wechat-client.ts +0 -162
  54. package/src/config/local-a2a-templates.ts +0 -59
  55. package/src/config.ts +0 -124
  56. package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
  57. package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
  58. package/src/controllers/__tests__/mime.test.ts +0 -74
  59. package/src/controllers/__tests__/resources.test.ts +0 -122
  60. package/src/controllers/__tests__/run.test.ts +0 -115
  61. package/src/controllers/__tests__/tasks.test.ts +0 -215
  62. package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
  63. package/src/controllers/agent_task.ts +0 -72
  64. package/src/controllers/assistant.ts +0 -490
  65. package/src/controllers/auth.ts +0 -524
  66. package/src/controllers/channel-bindings.ts +0 -135
  67. package/src/controllers/channel-installations.ts +0 -409
  68. package/src/controllers/collections.ts +0 -176
  69. package/src/controllers/config.ts +0 -126
  70. package/src/controllers/connections.ts +0 -181
  71. package/src/controllers/data-query.ts +0 -236
  72. package/src/controllers/database-configs.ts +0 -440
  73. package/src/controllers/embeddings.ts +0 -22
  74. package/src/controllers/entries.ts +0 -167
  75. package/src/controllers/eval.ts +0 -513
  76. package/src/controllers/export-import.ts +0 -166
  77. package/src/controllers/health.ts +0 -36
  78. package/src/controllers/local-a2a-templates.ts +0 -18
  79. package/src/controllers/local-a2a.ts +0 -149
  80. package/src/controllers/mcp-configs.ts +0 -673
  81. package/src/controllers/memory.ts +0 -257
  82. package/src/controllers/menu-items.ts +0 -114
  83. package/src/controllers/metrics-configs.ts +0 -1061
  84. package/src/controllers/middleware-types.ts +0 -15
  85. package/src/controllers/models.ts +0 -199
  86. package/src/controllers/personal-assistant.ts +0 -191
  87. package/src/controllers/resources.ts +0 -477
  88. package/src/controllers/run.ts +0 -338
  89. package/src/controllers/sandbox.ts +0 -255
  90. package/src/controllers/schedules.ts +0 -336
  91. package/src/controllers/skills.ts +0 -256
  92. package/src/controllers/tasks.ts +0 -213
  93. package/src/controllers/tenants.ts +0 -121
  94. package/src/controllers/thread_status.ts +0 -66
  95. package/src/controllers/threads.ts +0 -260
  96. package/src/controllers/tools.ts +0 -139
  97. package/src/controllers/users.ts +0 -135
  98. package/src/controllers/workflow-tracking.ts +0 -693
  99. package/src/controllers/workspace.ts +0 -879
  100. package/src/export_registrations/a2a-api-key.registration.ts +0 -89
  101. package/src/export_registrations/agent.registration.ts +0 -80
  102. package/src/export_registrations/binding.registration.ts +0 -106
  103. package/src/export_registrations/channel-installation.registration.ts +0 -88
  104. package/src/export_registrations/collection.registration.ts +0 -85
  105. package/src/export_registrations/connection.registration.ts +0 -97
  106. package/src/export_registrations/database-config.registration.ts +0 -94
  107. package/src/export_registrations/eval.registration.ts +0 -334
  108. package/src/export_registrations/index.ts +0 -31
  109. package/src/export_registrations/mcp-config.registration.ts +0 -97
  110. package/src/export_registrations/menu.registration.ts +0 -91
  111. package/src/export_registrations/metrics-config.registration.ts +0 -94
  112. package/src/export_registrations/skill.registration.ts +0 -88
  113. package/src/export_registrations/task.registration.ts +0 -99
  114. package/src/index.ts +0 -497
  115. package/src/logger/Logger.ts +0 -186
  116. package/src/router/MessageContext.ts +0 -14
  117. package/src/router/MessageRouter.ts +0 -490
  118. package/src/router/middlewares/auditLogger.ts +0 -34
  119. package/src/router/middlewares/deduplication.ts +0 -38
  120. package/src/router/middlewares/index.ts +0 -3
  121. package/src/router/middlewares/rateLimit.ts +0 -46
  122. package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
  123. package/src/routes/a2a-bridge.ts +0 -266
  124. package/src/routes/a2a-test-connection.ts +0 -126
  125. package/src/routes/a2a.ts +0 -851
  126. package/src/routes/channel-bindings.ts +0 -11
  127. package/src/routes/channel-installations.ts +0 -33
  128. package/src/routes/index.ts +0 -640
  129. package/src/routes/menu-items.ts +0 -10
  130. package/src/routes/resource-routes.ts +0 -35
  131. package/src/routes/stt.ts +0 -102
  132. package/src/schemas/data-query.ts +0 -69
  133. package/src/schemas/index.ts +0 -852
  134. package/src/services/ExportImportService.ts +0 -296
  135. package/src/services/__tests__/ExportImportService.test.ts +0 -130
  136. package/src/services/__tests__/eval-runner.test.ts +0 -131
  137. package/src/services/__tests__/local-a2a.test.ts +0 -124
  138. package/src/services/agent_task_consumer.ts +0 -348
  139. package/src/services/collection_service.ts +0 -258
  140. package/src/services/eval-runner.ts +0 -283
  141. package/src/services/local-a2a.ts +0 -354
  142. package/src/services/queue_service.ts +0 -104
  143. package/src/services/sandbox_service.ts +0 -223
  144. package/src/services/skill_service.ts +0 -97
  145. package/src/services/supabase.ts +0 -60
  146. package/src/swagger.ts +0 -77
  147. package/src/types/index.ts +0 -118
  148. package/src/types/koa.d.ts +0 -7
  149. package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
  150. package/src/utils/__tests__/path-containment.test.ts +0 -35
  151. package/src/utils/a2a-url.ts +0 -20
  152. package/src/utils/local-a2a-runtime.ts +0 -107
  153. package/src/utils/mime.ts +0 -44
  154. package/src/utils/path-containment.ts +0 -38
  155. package/tsconfig.json +0 -26
@@ -1,693 +0,0 @@
1
- import { FastifyRequest, FastifyReply } from "fastify";
2
- import { getStoreLattice, agentInstanceManager, ThreadStatus, abortWorkflowRun as signalAbort } from "@axiom-lattice/core";
3
- import type { WorkflowTrackingStore, WorkflowRun, RunStep } from "@axiom-lattice/protocols";
4
- import { MessageChunkTypes } from "@axiom-lattice/protocols";
5
-
6
- function getTenantId(request: FastifyRequest): string {
7
- const userTenantId = (request as any).user?.tenantId;
8
- if (userTenantId) return userTenantId;
9
- return (request.headers["x-tenant-id"] as string) || "default";
10
- }
11
-
12
- function getTrackingStore(): WorkflowTrackingStore | null {
13
- try {
14
- const storeLattice = getStoreLattice("default", "workflowTracking");
15
- return storeLattice.store;
16
- } catch {
17
- return null;
18
- }
19
- }
20
-
21
- interface ApiResponse<T = any> {
22
- success: boolean;
23
- message: string;
24
- data?: T;
25
- }
26
-
27
- async function getDefinitionsFromAssistants(tenantId: string): Promise<Array<{
28
- assistantId: string;
29
- assistantName: string;
30
- description?: string;
31
- topologyEdges: { from: string; to: string; purpose: string }[];
32
- totalEdges: number;
33
- }>> {
34
- try {
35
- const storeLattice = getStoreLattice("default", "assistant");
36
- const assistantStore = storeLattice.store;
37
- const assistants = await assistantStore.getAllAssistants(tenantId);
38
-
39
- const results: Array<{
40
- assistantId: string;
41
- assistantName: string;
42
- description?: string;
43
- topologyEdges: { from: string; to: string; purpose: string }[];
44
- totalEdges: number;
45
- }> = [];
46
-
47
- for (const a of assistants) {
48
- const def = a.graphDefinition;
49
- if (!def || def.type !== "processing") continue;
50
- if (!def.middleware) continue;
51
- for (const mw of def.middleware) {
52
- if (mw.type === "topology" && mw.enabled) {
53
- results.push({
54
- assistantId: a.id,
55
- assistantName: a.name,
56
- description: a.description,
57
- topologyEdges: mw.config.edges,
58
- totalEdges: mw.config.edges.length,
59
- });
60
- }
61
- }
62
- }
63
- return results;
64
- } catch {
65
- return [];
66
- }
67
- }
68
-
69
- export async function getAllWorkflowDefinitions(
70
- request: FastifyRequest,
71
- reply: FastifyReply
72
- ): Promise<ApiResponse<{ records: any[] }>> {
73
- const tenantId = getTenantId(request);
74
-
75
- try {
76
- // Get definitions from assistant configs (always available, even before first run)
77
- const configDefs = await getDefinitionsFromAssistants(tenantId);
78
-
79
- if (configDefs.length === 0) {
80
- return {
81
- success: true,
82
- message: "No workflow definitions found",
83
- data: { records: [] },
84
- };
85
- }
86
-
87
- // Merge in run-derived data (lastRunAt, runCount) if tracking store exists
88
- const runMap = new Map<string, { lastRunAt?: string; runCount: number }>();
89
- try {
90
- const store = getTrackingStore();
91
- if (store) {
92
- const runs = await store.getWorkflowRunsByTenantId(tenantId);
93
- for (const r of runs) {
94
- const entry = runMap.get(r.assistantId) || { runCount: 0 };
95
- entry.runCount++;
96
- if (!entry.lastRunAt || new Date(r.startedAt) > new Date(entry.lastRunAt)) {
97
- entry.lastRunAt = r.startedAt instanceof Date ? r.startedAt.toISOString() : String(r.startedAt);
98
- }
99
- runMap.set(r.assistantId, entry);
100
- }
101
- }
102
- } catch {
103
- // tracking store unavailable, definitions from config only
104
- }
105
-
106
- const definitions = configDefs.map((d) => {
107
- const runInfo = runMap.get(d.assistantId);
108
- return {
109
- ...d,
110
- lastRunAt: runInfo?.lastRunAt || null,
111
- runCount: runInfo?.runCount || 0,
112
- };
113
- });
114
-
115
- return {
116
- success: true,
117
- message: "Successfully retrieved workflow definitions",
118
- data: { records: definitions },
119
- };
120
- } catch (error) {
121
- request.log.error(error, "Failed to get workflow definitions");
122
- return reply.status(500).send({ success: false, message: "Failed to retrieve workflow definitions" });
123
- }
124
- }
125
-
126
- export async function getAllWorkflowRuns(
127
- request: FastifyRequest<{ Querystring: { assistantId?: string; status?: string } }>,
128
- reply: FastifyReply
129
- ): Promise<ApiResponse<{ records: any[]; total: number }>> {
130
- const tenantId = getTenantId(request);
131
- const { assistantId, status } = request.query;
132
-
133
- try {
134
- const store = getTrackingStore();
135
- if (!store) {
136
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
137
- }
138
-
139
- // Resolve assistant names
140
- const nameMap: Record<string, string> = {};
141
- try {
142
- const asStoreLattice = getStoreLattice("default", "assistant");
143
- const assistantStore = asStoreLattice.store;
144
- const assistants = await assistantStore.getAllAssistants(tenantId);
145
- for (const a of assistants) {
146
- nameMap[a.id] = a.name;
147
- }
148
- } catch {
149
- // names unavailable, will fall back to ID
150
- }
151
-
152
- let runs: WorkflowRun[];
153
- if (assistantId) {
154
- runs = await store.getWorkflowRunsByAssistantId(tenantId, assistantId);
155
- } else {
156
- runs = await store.getWorkflowRunsByTenantId(tenantId);
157
- }
158
-
159
- if (status) {
160
- runs = runs.filter(r => r.status === status);
161
- }
162
-
163
- // Enrich runs with step counts (more accurate than edge-based counts for branching workflows)
164
- const enrichedRuns = await Promise.all(
165
- runs.map(async (run) => {
166
- try {
167
- const steps = await store.getRunSteps(run.id);
168
- const totalSteps = steps.length;
169
- const completedSteps = steps.filter(s => s.status === 'completed').length;
170
- return { ...run, totalSteps, completedSteps, assistantName: nameMap[run.assistantId] || run.assistantId };
171
- } catch {
172
- return { ...run, totalSteps: 0, completedSteps: 0, assistantName: nameMap[run.assistantId] || run.assistantId };
173
- }
174
- })
175
- );
176
-
177
- return {
178
- success: true,
179
- message: "Successfully retrieved workflow runs",
180
- data: { records: enrichedRuns, total: enrichedRuns.length },
181
- };
182
- } catch (error) {
183
- request.log.error({ tenantId, assistantId, status, error: (error as Error).stack || (error as Error).message }, "Failed to get workflow runs");
184
- return reply.status(500).send({
185
- success: false,
186
- message: `Failed to retrieve workflow runs: ${(error as Error).message}`,
187
- });
188
- }
189
- }
190
-
191
- // GET /api/workflows/inbox
192
- // Pagination semantics:
193
- // - No page/pageSize → legacy mode: all interrupted items, data = { records } (unchanged shape).
194
- // - page and/or pageSize → paged mode: store-level pagination over interrupted runs,
195
- // data = { records, total, page, pageSize }. total = number of interrupted RUNS
196
- // (each run expands to 0..N inbox items after the agent-state check).
197
- const INBOX_DEFAULT_PAGE_SIZE = 20;
198
- const INBOX_MAX_PAGE_SIZE = 100;
199
-
200
- export async function getInboxItems(
201
- request: FastifyRequest<{ Querystring: { page?: string; pageSize?: string } }>,
202
- reply: FastifyReply
203
- ): Promise<ApiResponse<{ records: any[]; total?: number; page?: number; pageSize?: number }>> {
204
- const tenantId = getTenantId(request);
205
-
206
- try {
207
- const store = getTrackingStore();
208
- if (!store) {
209
- return { success: true, message: "No tracking store configured", data: { records: [] } };
210
- }
211
-
212
- // Resolve assistant names
213
- const nameMap: Record<string, string> = {};
214
- try {
215
- const asStoreLattice = getStoreLattice("default", "assistant");
216
- const assistantStore = asStoreLattice.store;
217
- const assistants = await assistantStore.getAllAssistants(tenantId);
218
- for (const a of assistants) {
219
- nameMap[a.id] = a.name;
220
- }
221
- } catch {
222
- // names unavailable, will fall back to ID
223
- }
224
-
225
- const { page: pageParam, pageSize: pageSizeParam } = request.query;
226
- const paged = pageParam !== undefined || pageSizeParam !== undefined;
227
- const page = Math.max(1, parseInt(pageParam ?? "", 10) || 1);
228
- const pageSize = Math.min(INBOX_MAX_PAGE_SIZE, Math.max(1, parseInt(pageSizeParam ?? "", 10) || INBOX_DEFAULT_PAGE_SIZE));
229
-
230
- let pendingRuns: WorkflowRun[];
231
- let total: number | undefined;
232
- if (paged) {
233
- const result = await store.queryWorkflowRuns(tenantId, {
234
- status: "interrupted",
235
- limit: pageSize,
236
- offset: (page - 1) * pageSize,
237
- });
238
- pendingRuns = result.records;
239
- total = result.total;
240
- } else {
241
- const result = await store.queryWorkflowRuns(tenantId, { status: "interrupted" });
242
- pendingRuns = result.records;
243
- }
244
-
245
- if (pendingRuns.length === 0) {
246
- return {
247
- success: true,
248
- message: "No pending workflows",
249
- data: paged ? { records: [], total, page, pageSize } : { records: [] },
250
- };
251
- }
252
-
253
- // Parallelize agent checks across interrupted workflows
254
- const checkPromises = pendingRuns.map(async (r) => {
255
- try {
256
- const [steps, agent] = await Promise.all([
257
- store.getRunSteps(r.id).catch(() => []),
258
- (async () => {
259
- try {
260
- return agentInstanceManager.getAgent({
261
- assistant_id: r.assistantId,
262
- thread_id: r.threadId,
263
- tenant_id: r.tenantId,
264
- });
265
- } catch {
266
- return null;
267
- }
268
- })(),
269
- ]);
270
-
271
- if (!agent) {
272
- // Agent not available — still return raw item so UI knows a human step exists
273
- return [{
274
- runId: r.id,
275
- assistantId: r.assistantId,
276
- assistantName: nameMap[r.assistantId] || r.assistantId,
277
- threadId: r.threadId,
278
- tenantId: r.tenantId,
279
- status: r.status,
280
- startedAt: r.startedAt,
281
- totalEdges: r.totalEdges,
282
- completedEdges: r.completedEdges,
283
- steps,
284
- }];
285
- }
286
-
287
- const runStatus = await agent.getRunStatus();
288
- if (runStatus !== "interrupted") return [];
289
-
290
- const state = await agent.getCurrentState();
291
- const interrupts = state.tasks
292
- ?.flatMap((t: any) => t.interrupts || []) || [];
293
-
294
- return interrupts.map((i: any) => ({
295
- runId: r.id,
296
- assistantId: r.assistantId,
297
- assistantName: nameMap[r.assistantId] || r.assistantId,
298
- threadId: r.threadId,
299
- tenantId: r.tenantId,
300
- interruptId: i.id,
301
- interruptValue: i.value,
302
- status: r.status,
303
- startedAt: r.startedAt,
304
- totalEdges: r.totalEdges,
305
- completedEdges: r.completedEdges,
306
- steps,
307
- }));
308
- } catch (err) {
309
- request.log.warn({ runId: r.id, error: (err as Error).message }, "Agent check skipped");
310
- return [];
311
- }
312
- });
313
-
314
- const results = await Promise.all(checkPromises);
315
- const inboxItems = results.flat();
316
- // Runs are already sorted by updatedAt DESC, id DESC from queryWorkflowRuns.
317
- // flatMap preserves run order within each run.
318
-
319
- return {
320
- success: true,
321
- message: "Successfully retrieved inbox items",
322
- data: paged ? { records: inboxItems, total, page, pageSize } : { records: inboxItems },
323
- };
324
- } catch (error) {
325
- request.log.error(error, "Failed to get inbox items");
326
- return reply.status(500).send({ success: false, message: "Failed to retrieve inbox items" });
327
- }
328
- }
329
-
330
- export async function getWorkflowDefinitions(
331
- request: FastifyRequest<{ Params: { assistantId: string } }>,
332
- reply: FastifyReply
333
- ): Promise<ApiResponse<{ records: any[] }>> {
334
- const { assistantId } = request.params;
335
- const tenantId = getTenantId(request);
336
-
337
- try {
338
- const store = getTrackingStore();
339
- if (!store) {
340
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
341
- }
342
-
343
- const runs = await store.getWorkflowRunsByAssistantId(tenantId, assistantId);
344
- const seen = new Set<string>();
345
- const definitions = runs
346
- .filter(r => {
347
- const key = JSON.stringify(r.topologyEdges);
348
- if (seen.has(key)) return false;
349
- seen.add(key);
350
- return true;
351
- })
352
- .map(r => ({
353
- assistantId: r.assistantId,
354
- topologyEdges: r.topologyEdges,
355
- totalEdges: r.totalEdges,
356
- lastRunAt: r.startedAt,
357
- }));
358
-
359
- return {
360
- success: true,
361
- message: "Successfully retrieved workflow definitions",
362
- data: { records: definitions },
363
- };
364
- } catch (error) {
365
- request.log.error(error, "Failed to get workflow definitions");
366
- return reply.status(500).send({ success: false, message: "Failed to retrieve workflow definitions" });
367
- }
368
- }
369
-
370
- export async function getWorkflowRuns(
371
- request: FastifyRequest<{ Params: { assistantId: string; threadId: string } }>,
372
- reply: FastifyReply
373
- ): Promise<ApiResponse<{ records: WorkflowRun[]; total: number }>> {
374
- const { threadId } = request.params;
375
- const tenantId = getTenantId(request);
376
-
377
- try {
378
- const store = getTrackingStore();
379
- if (!store) {
380
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
381
- }
382
-
383
- const runs = await store.getWorkflowRunsByThreadId(tenantId, threadId);
384
- return {
385
- success: true,
386
- message: "Successfully retrieved workflow runs",
387
- data: { records: runs, total: runs.length },
388
- };
389
- } catch (error) {
390
- request.log.error(error, "Failed to get workflow runs");
391
- return reply.status(500).send({ success: false, message: "Failed to retrieve workflow runs" });
392
- }
393
- }
394
-
395
- export async function getWorkflowRun(
396
- request: FastifyRequest<{ Params: { runId: string } }>,
397
- reply: FastifyReply
398
- ): Promise<ApiResponse<WorkflowRun>> {
399
- const { runId } = request.params;
400
-
401
- try {
402
- const store = getTrackingStore();
403
- if (!store) {
404
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
405
- }
406
-
407
- const run = await store.getWorkflowRun(runId);
408
- if (!run) {
409
- return reply.status(404).send({ success: false, message: "Workflow run not found" });
410
- }
411
-
412
- return { success: true, message: "Successfully retrieved workflow run", data: run };
413
- } catch (error) {
414
- request.log.error(error, "Failed to get workflow run");
415
- return reply.status(500).send({ success: false, message: "Failed to retrieve workflow run" });
416
- }
417
- }
418
-
419
- export async function deleteWorkflowRun(
420
- request: FastifyRequest<{ Params: { runId: string } }>,
421
- reply: FastifyReply
422
- ): Promise<ApiResponse> {
423
- const { runId } = request.params;
424
- const tenantId = getTenantId(request);
425
-
426
- try {
427
- const store = getTrackingStore();
428
- if (!store) {
429
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
430
- }
431
-
432
- const run = await store.getWorkflowRun(runId);
433
- if (!run) {
434
- return reply.status(404).send({ success: false, message: "Workflow run not found" });
435
- }
436
-
437
- // If run is still running, abort the agent first
438
- if (run.status === "running") {
439
- try {
440
- const workspace_id = request.headers["x-workspace-id"] as string;
441
- const project_id = request.headers["x-project-id"] as string;
442
- const agent = agentInstanceManager.getAgent({
443
- assistant_id: run.assistantId,
444
- thread_id: run.threadId,
445
- tenant_id: tenantId,
446
- workspace_id,
447
- project_id,
448
- });
449
- await agent.abort();
450
- } catch (err) {
451
- request.log.warn({ runId, error: (err as Error).message }, "Failed to abort agent, deleting tracking records anyway");
452
- }
453
- }
454
-
455
- await store.deleteWorkflowRun(runId);
456
-
457
- return { success: true, message: "Workflow run deleted" };
458
- } catch (error) {
459
- request.log.error(error, "Failed to delete workflow run");
460
- return reply.status(500).send({ success: false, message: "Failed to delete workflow run" });
461
- }
462
- }
463
-
464
- export async function getRunSteps(
465
- request: FastifyRequest<{ Params: { runId: string }; Querystring: { step_type?: string; status?: string } }>,
466
- reply: FastifyReply
467
- ): Promise<ApiResponse<{ records: RunStep[]; total: number }>> {
468
- const { runId } = request.params;
469
- const { step_type, status: stepStatus } = request.query;
470
-
471
- try {
472
- const store = getTrackingStore();
473
- if (!store) {
474
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
475
- }
476
-
477
- let steps: RunStep[];
478
- if (step_type) {
479
- steps = await store.getRunStepsByType(runId, step_type as any);
480
- } else {
481
- steps = await store.getRunSteps(runId);
482
- }
483
-
484
- if (stepStatus) {
485
- steps = steps.filter(s => s.status === stepStatus);
486
- }
487
-
488
- return {
489
- success: true,
490
- message: "Successfully retrieved run steps",
491
- data: { records: steps, total: steps.length },
492
- };
493
- } catch (error) {
494
- request.log.error({ runId, error: (error as Error).stack || (error as Error).message }, "Failed to get run steps");
495
- return reply.status(500).send({
496
- success: false,
497
- message: `Failed to retrieve run steps: ${(error as Error).message}`,
498
- });
499
- }
500
- }
501
-
502
- export async function getRunTasks(
503
- request: FastifyRequest<{ Params: { runId: string } }>,
504
- reply: FastifyReply
505
- ): Promise<ApiResponse<{ records: RunStep[]; total: number }>> {
506
- const { runId } = request.params;
507
-
508
- try {
509
- const store = getTrackingStore();
510
- if (!store) {
511
- return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
512
- }
513
-
514
- const steps = await store.getInterruptedSteps(runId);
515
- return {
516
- success: true,
517
- message: "Successfully retrieved user tasks",
518
- data: { records: steps, total: steps.length },
519
- };
520
- } catch (error) {
521
- request.log.error(error, "Failed to get run tasks");
522
- return reply.status(500).send({ success: false, message: "Failed to retrieve user tasks" });
523
- }
524
- }
525
-
526
- // 简化的 Inbox 任务回复接口
527
- interface ReplyInboxRequest {
528
- runId: string;
529
- answers: Array<{
530
- questionIndex: number;
531
- selectedOptions: string[];
532
- }>;
533
- }
534
-
535
- export async function replyInboxTask(
536
- request: FastifyRequest<{ Body: ReplyInboxRequest }>,
537
- reply: FastifyReply
538
- ): Promise<void> {
539
- const { runId, answers } = request.body;
540
- const tenantId = getTenantId(request);
541
-
542
- try {
543
- // 1. 获取 WorkflowRun 信息
544
- const store = getTrackingStore();
545
- if (!store) {
546
- return reply.status(404).send({
547
- success: false,
548
- message: "No workflow tracking store configured"
549
- });
550
- }
551
-
552
- const run = await store.getWorkflowRun(runId);
553
- if (!run) {
554
- return reply.status(404).send({
555
- success: false,
556
- message: "Workflow run not found"
557
- });
558
- }
559
-
560
- // 2. 获取 Agent 实例(补齐参数,与 createRun 一致)
561
- const workspace_id = request.headers["x-workspace-id"] as string;
562
- const project_id = request.headers["x-project-id"] as string;
563
- const agent = agentInstanceManager.getAgent({
564
- assistant_id: run.assistantId,
565
- thread_id: run.threadId,
566
- tenant_id: tenantId,
567
- workspace_id,
568
- project_id,
569
- });
570
-
571
- // 3. 检查 graph 是否处于中断状态
572
- const runStatus = await agent.getRunStatus();
573
- if (runStatus !== ThreadStatus.INTERRUPTED) {
574
- return reply.status(409).send({
575
- success: false,
576
- message: `Cannot resume: graph is not interrupted (current status: ${runStatus})`,
577
- data: { runId: run.id, assistantId: run.assistantId, threadId: run.threadId, status: runStatus },
578
- });
579
- }
580
-
581
- // 4. 直接在后台触发 resume 执行(不等待,不阻塞 HTTP 响应)
582
- agent.addMessage({
583
- input: { message: "Clarification answers submitted" },
584
- command: {
585
- resume: {
586
- action: "submit",
587
- data: { answers },
588
- message: "Clarification answers submitted",
589
- },
590
- },
591
- }).then((result) => {
592
- // addMessage 成功后,触发 chunkStream 消费(驱动执行)
593
- // 不 await stream,让它在后台运行
594
- const stream = agent.chunkStream(
595
- result.messageId,
596
- [MessageChunkTypes.MESSAGE_COMPLETED]
597
- );
598
- // 消费 stream 但不阻塞(后台驱动 agent 执行)
599
- (async () => {
600
- for await (const _ of stream) { /* 丢弃 chunks */ }
601
- })().catch((error) => {
602
- request.log.error(error, "Background chunk stream error");
603
- });
604
- }).catch((error) => {
605
- request.log.error(error, "Background resume failed");
606
- });
607
-
608
- // 5. 立即返回成功(不等待 agent 执行完成)
609
- return reply.status(200).send({
610
- success: true,
611
- message: "Resume request submitted successfully",
612
- data: {
613
- runId: run.id,
614
- assistantId: run.assistantId,
615
- threadId: run.threadId,
616
- status: "resuming",
617
- },
618
- });
619
-
620
- } catch (error) {
621
- request.log.error(error, "Failed to reply inbox task");
622
- return reply.status(500).send({
623
- success: false,
624
- message: "Failed to submit resume request"
625
- });
626
- }
627
- }
628
-
629
- /**
630
- * Abort a running workflow.
631
- *
632
- * Stops the main graph execution AND signals all in-progress
633
- * sub-agent invokes to cancel.
634
- */
635
- export async function abortWorkflowRun(
636
- request: FastifyRequest,
637
- reply: FastifyReply
638
- ): Promise<void> {
639
- try {
640
- const { runId } = request.params as { runId: string };
641
- const tenantId = getTenantId(request);
642
-
643
- const store = getTrackingStore();
644
- if (!store) {
645
- return reply.status(404).send({
646
- success: false,
647
- message: "No workflow tracking store configured",
648
- });
649
- }
650
-
651
- const run = await store.getWorkflowRun(runId);
652
- if (!run) {
653
- return reply.status(404).send({
654
- success: false,
655
- message: "Workflow run not found",
656
- });
657
- }
658
-
659
- // Persist abort intent FIRST — prevents restart after server crash
660
- await store.updateWorkflowRun(runId, {
661
- status: "cancelled",
662
- errorMessage: "Aborted by user",
663
- completedAt: new Date(),
664
- }).catch(() => {});
665
-
666
- // Stop the main graph execution
667
- const workspace_id = request.headers["x-workspace-id"] as string;
668
- const project_id = request.headers["x-project-id"] as string;
669
- const agent = agentInstanceManager.getAgent({
670
- assistant_id: run.assistantId,
671
- thread_id: run.threadId,
672
- tenant_id: tenantId,
673
- workspace_id,
674
- project_id,
675
- });
676
- await agent.abort();
677
-
678
- // Signal sub-agents via AbortController registry
679
- signalAbort(runId);
680
-
681
- return reply.status(200).send({
682
- success: true,
683
- message: "Workflow aborted",
684
- data: { runId, assistantId: run.assistantId, threadId: run.threadId },
685
- });
686
- } catch (error) {
687
- request.log.error(error, "Failed to abort workflow run");
688
- return reply.status(500).send({
689
- success: false,
690
- message: `Abort failed: ${(error as Error).message}`,
691
- });
692
- }
693
- }