@axiom-lattice/gateway 3.0.2 → 3.0.4
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/dist/index.js +37 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +37 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -6
- package/.eslintrc.json +0 -22
- package/.turbo/turbo-build.log +0 -46
- package/AGENTS.md +0 -112
- package/CHANGELOG.md +0 -1190
- package/LOGGER_CONFIG.md +0 -135
- package/RESUME_STREAM_CONTENT_BASED.md +0 -326
- package/RESUME_STREAM_README.md +0 -388
- package/jest.config.js +0 -72
- package/public/sdk/README.md +0 -695
- package/public/sdk/dashboard-engine-skill.md +0 -1122
- package/public/sdk/dashboard-single-file-spec.md +0 -357
- package/public/sdk/data-query-sdk-skill.md +0 -307
- package/public/sdk/data-query-sdk.d.ts +0 -252
- package/public/sdk/data-query-sdk.js +0 -970
- package/public/sdk/occupancy-dashboard.html +0 -363
- package/public/sdk/test-dashboard.html +0 -690
- package/scripts/seed-lark-installation.ts +0 -54
- package/src/__tests__/__mocks__/e2b.ts +0 -1
- package/src/__tests__/a2a.test.ts +0 -428
- package/src/__tests__/channel-installations.test.ts +0 -199
- package/src/__tests__/data-query.test.ts +0 -77
- package/src/__tests__/sandbox-provider-registration.test.ts +0 -74
- package/src/__tests__/setup.ts +0 -5
- package/src/__tests__/workspace.test.ts +0 -400
- package/src/channels/__tests__/routes.test.ts +0 -50
- package/src/channels/lark/LarkChannelAdapter.ts +0 -217
- package/src/channels/lark/README.md +0 -187
- package/src/channels/lark/__tests__/controller.test.ts +0 -136
- package/src/channels/lark/__tests__/mapping-service.test.ts +0 -118
- package/src/channels/lark/__tests__/parser.test.ts +0 -72
- package/src/channels/lark/__tests__/sender.test.ts +0 -37
- package/src/channels/lark/__tests__/verification.test.ts +0 -157
- package/src/channels/lark/controller.ts +0 -48
- package/src/channels/lark/mapping-service.ts +0 -138
- package/src/channels/lark/parser.ts +0 -68
- package/src/channels/lark/routes.ts +0 -22
- package/src/channels/lark/sender.ts +0 -58
- package/src/channels/lark/types.ts +0 -20
- package/src/channels/lark/verification.ts +0 -32
- package/src/channels/registry.ts +0 -20
- package/src/channels/routes.ts +0 -30
- package/src/channels/wechat/WechatChannelAdapter.ts +0 -294
- package/src/channels/wechat/__tests__/context-store.test.ts +0 -148
- package/src/channels/wechat/context-store.ts +0 -54
- package/src/channels/wechat/controller.ts +0 -59
- package/src/channels/wechat/routes.ts +0 -7
- package/src/channels/wechat/types.ts +0 -62
- package/src/channels/wechat/wechat-client.ts +0 -162
- package/src/config/local-a2a-templates.ts +0 -59
- package/src/config.ts +0 -124
- package/src/controllers/__tests__/assistant-local-a2a.test.ts +0 -147
- package/src/controllers/__tests__/local-a2a-templates.test.ts +0 -112
- package/src/controllers/__tests__/mime.test.ts +0 -74
- package/src/controllers/__tests__/resources.test.ts +0 -122
- package/src/controllers/__tests__/run.test.ts +0 -115
- package/src/controllers/__tests__/tasks.test.ts +0 -215
- package/src/controllers/__tests__/workflow-tracking.test.ts +0 -113
- package/src/controllers/agent_task.ts +0 -72
- package/src/controllers/assistant.ts +0 -490
- package/src/controllers/auth.ts +0 -524
- package/src/controllers/channel-bindings.ts +0 -135
- package/src/controllers/channel-installations.ts +0 -409
- package/src/controllers/collections.ts +0 -176
- package/src/controllers/config.ts +0 -126
- package/src/controllers/connections.ts +0 -181
- package/src/controllers/data-query.ts +0 -236
- package/src/controllers/database-configs.ts +0 -440
- package/src/controllers/embeddings.ts +0 -22
- package/src/controllers/entries.ts +0 -167
- package/src/controllers/eval.ts +0 -513
- package/src/controllers/export-import.ts +0 -166
- package/src/controllers/health.ts +0 -36
- package/src/controllers/local-a2a-templates.ts +0 -18
- package/src/controllers/local-a2a.ts +0 -149
- package/src/controllers/mcp-configs.ts +0 -673
- package/src/controllers/memory.ts +0 -257
- package/src/controllers/menu-items.ts +0 -114
- package/src/controllers/metrics-configs.ts +0 -1061
- package/src/controllers/middleware-types.ts +0 -15
- package/src/controllers/models.ts +0 -199
- package/src/controllers/personal-assistant.ts +0 -191
- package/src/controllers/resources.ts +0 -477
- package/src/controllers/run.ts +0 -338
- package/src/controllers/sandbox.ts +0 -255
- package/src/controllers/schedules.ts +0 -336
- package/src/controllers/skills.ts +0 -256
- package/src/controllers/tasks.ts +0 -213
- package/src/controllers/tenants.ts +0 -121
- package/src/controllers/thread_status.ts +0 -66
- package/src/controllers/threads.ts +0 -260
- package/src/controllers/tools.ts +0 -139
- package/src/controllers/users.ts +0 -135
- package/src/controllers/workflow-tracking.ts +0 -693
- package/src/controllers/workspace.ts +0 -879
- package/src/export_registrations/a2a-api-key.registration.ts +0 -89
- package/src/export_registrations/agent.registration.ts +0 -80
- package/src/export_registrations/binding.registration.ts +0 -106
- package/src/export_registrations/channel-installation.registration.ts +0 -88
- package/src/export_registrations/collection.registration.ts +0 -85
- package/src/export_registrations/connection.registration.ts +0 -97
- package/src/export_registrations/database-config.registration.ts +0 -94
- package/src/export_registrations/eval.registration.ts +0 -334
- package/src/export_registrations/index.ts +0 -31
- package/src/export_registrations/mcp-config.registration.ts +0 -97
- package/src/export_registrations/menu.registration.ts +0 -91
- package/src/export_registrations/metrics-config.registration.ts +0 -94
- package/src/export_registrations/skill.registration.ts +0 -88
- package/src/export_registrations/task.registration.ts +0 -99
- package/src/index.ts +0 -497
- package/src/logger/Logger.ts +0 -186
- package/src/router/MessageContext.ts +0 -14
- package/src/router/MessageRouter.ts +0 -490
- package/src/router/middlewares/auditLogger.ts +0 -34
- package/src/router/middlewares/deduplication.ts +0 -38
- package/src/router/middlewares/index.ts +0 -3
- package/src/router/middlewares/rateLimit.ts +0 -46
- package/src/routes/__tests__/a2a-test-connection.test.ts +0 -177
- package/src/routes/a2a-bridge.ts +0 -266
- package/src/routes/a2a-test-connection.ts +0 -126
- package/src/routes/a2a.ts +0 -851
- package/src/routes/channel-bindings.ts +0 -11
- package/src/routes/channel-installations.ts +0 -33
- package/src/routes/index.ts +0 -640
- package/src/routes/menu-items.ts +0 -10
- package/src/routes/resource-routes.ts +0 -35
- package/src/routes/stt.ts +0 -102
- package/src/schemas/data-query.ts +0 -69
- package/src/schemas/index.ts +0 -852
- package/src/services/ExportImportService.ts +0 -296
- package/src/services/__tests__/ExportImportService.test.ts +0 -130
- package/src/services/__tests__/eval-runner.test.ts +0 -131
- package/src/services/__tests__/local-a2a.test.ts +0 -124
- package/src/services/agent_task_consumer.ts +0 -348
- package/src/services/collection_service.ts +0 -258
- package/src/services/eval-runner.ts +0 -283
- package/src/services/local-a2a.ts +0 -354
- package/src/services/queue_service.ts +0 -104
- package/src/services/sandbox_service.ts +0 -223
- package/src/services/skill_service.ts +0 -97
- package/src/services/supabase.ts +0 -60
- package/src/swagger.ts +0 -77
- package/src/types/index.ts +0 -118
- package/src/types/koa.d.ts +0 -7
- package/src/utils/__tests__/local-a2a-runtime.test.ts +0 -77
- package/src/utils/__tests__/path-containment.test.ts +0 -35
- package/src/utils/a2a-url.ts +0 -20
- package/src/utils/local-a2a-runtime.ts +0 -107
- package/src/utils/mime.ts +0 -44
- package/src/utils/path-containment.ts +0 -38
- 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
|
-
}
|