@axiom-lattice/gateway 2.1.77 → 2.1.79

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axiom-lattice/gateway",
3
- "version": "2.1.77",
3
+ "version": "2.1.79",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -40,11 +40,11 @@
40
40
  "redis": "^5.0.1",
41
41
  "uuid": "^9.0.1",
42
42
  "zod": "3.25.76",
43
- "@axiom-lattice/agent-eval": "2.1.61",
44
- "@axiom-lattice/core": "2.1.67",
45
- "@axiom-lattice/pg-stores": "1.0.57",
46
- "@axiom-lattice/protocols": "2.1.34",
47
- "@axiom-lattice/queue-redis": "1.0.33"
43
+ "@axiom-lattice/agent-eval": "2.1.63",
44
+ "@axiom-lattice/core": "2.1.69",
45
+ "@axiom-lattice/pg-stores": "1.0.59",
46
+ "@axiom-lattice/protocols": "2.1.36",
47
+ "@axiom-lattice/queue-redis": "1.0.35"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@types/jest": "^29.5.14",
@@ -66,7 +66,7 @@ export const larkChannelAdapter: ChannelAdapter<LarkChannelInstallationConfig> =
66
66
  installation: ChannelInstallation<LarkChannelInstallationConfig>,
67
67
  ): Promise<void> {
68
68
  const { createLarkSender } = await import("./sender");
69
- const sender = createLarkSender(installation.config);
69
+ const sender = await createLarkSender(installation.config);
70
70
  await sender.sendTextReply({
71
71
  chatId: replyTarget.rawTarget.chatId as string,
72
72
  text: message.text,
@@ -1,5 +1,5 @@
1
1
  import type { FastifyReply, FastifyRequest } from "fastify";
2
- import type { ChannelInstallationStore, LarkChannelInstallationConfig } from "@axiom-lattice/protocols";
2
+ import type { ChannelInstallation, ChannelInstallationStore, LarkChannelInstallationConfig } from "@axiom-lattice/protocols";
3
3
  import { larkChannelAdapter } from "./LarkChannelAdapter";
4
4
  import type { MessageRouter } from "../../router/MessageRouter";
5
5
  import { parseLarkRequestBody } from "./verification";
@@ -23,14 +23,15 @@ export function createLarkEventHandler(deps: {
23
23
  return;
24
24
  }
25
25
 
26
- const body = parseLarkRequestBody(request.body, installation.config.encryptKey);
26
+ const larkInstallation = installation as ChannelInstallation<LarkChannelInstallationConfig>;
27
+ const body = parseLarkRequestBody(request.body, larkInstallation.config.encryptKey);
27
28
 
28
29
  if (body.type === "url_verification" && body.challenge) {
29
30
  reply.status(200).send({ challenge: body.challenge });
30
31
  return;
31
32
  }
32
33
 
33
- const inboundMessage = await larkChannelAdapter.receive(request.body, installation);
34
+ const inboundMessage = await larkChannelAdapter.receive(request.body, larkInstallation);
34
35
  if (!inboundMessage) {
35
36
  reply.status(200).send();
36
37
  return;
@@ -37,14 +37,6 @@ interface SandboxParams {
37
37
  threadId: string;
38
38
  }
39
39
 
40
- interface ProxyParams extends SandboxParams {
41
- "*": string;
42
- }
43
-
44
- interface ResourceParams extends SandboxParams {
45
- resourcePath: string;
46
- }
47
-
48
40
  export function registerSandboxProxyRoutes(app: FastifyInstance): void {
49
41
  // Register uploadfile route FIRST before wildcard routes to ensure it matches
50
42
  app.post<{ Params: SandboxParams }>(
@@ -59,14 +51,18 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
59
51
  return reply.status(500).send({ error: "Assistant sandbox config not found" });
60
52
  }
61
53
 
62
- const sandboxName = sandboxService.computeSandboxName(
63
- assistantId,
64
- threadId,
65
- vmIsolation
66
- );
54
+ const workspaceId = request.headers["x-workspace-id"] as string;
55
+ const projectId = request.headers["x-project-id"] as string;
67
56
 
68
- const sandboxManager = getSandBoxManager()
69
- const sandbox = await sandboxManager.createSandbox(sandboxName)
57
+ const sandboxManager = getSandBoxManager();
58
+ const sandbox = await sandboxManager.getSandboxFromConfig({
59
+ assistant_id: assistantId,
60
+ thread_id: threadId,
61
+ tenantId,
62
+ workspaceId,
63
+ projectId,
64
+ vmIsolation,
65
+ });
70
66
 
71
67
  try {
72
68
  const data = await request.file();
@@ -122,14 +118,18 @@ export function registerSandboxProxyRoutes(app: FastifyInstance): void {
122
118
  return reply.status(500).send({ error: "Assistant filesystem vmIsolation not found" });
123
119
  }
124
120
 
125
- const sandboxName = sandboxService.computeSandboxName(
126
- assistantId,
127
- threadId,
128
- vmIsolation
129
- );
121
+ const workspaceId = request.headers["x-workspace-id"] as string;
122
+ const projectId = request.headers["x-project-id"] as string;
130
123
 
131
124
  const sandboxManager = getSandBoxManager();
132
- const sandbox = await sandboxManager.createSandbox(sandboxName);
125
+ const sandbox = await sandboxManager.getSandboxFromConfig({
126
+ assistant_id: assistantId,
127
+ thread_id: threadId,
128
+ tenantId,
129
+ workspaceId,
130
+ projectId,
131
+ vmIsolation,
132
+ });
133
133
 
134
134
  try {
135
135
  // Normalize path to /-prefixed absolute path
@@ -1,5 +1,5 @@
1
1
  import { FastifyRequest, FastifyReply } from "fastify";
2
- import { getStoreLattice, agentInstanceManager } from "@axiom-lattice/core";
2
+ import { getStoreLattice, agentInstanceManager, ThreadStatus } from "@axiom-lattice/core";
3
3
  import type { WorkflowTrackingStore, WorkflowRun, RunStep } from "@axiom-lattice/protocols";
4
4
  import { MessageChunkTypes } from "@axiom-lattice/protocols";
5
5
 
@@ -39,6 +39,7 @@ async function getDefinitionsFromAssistants(tenantId: string): Promise<Array<{
39
39
  const results: Array<{
40
40
  assistantId: string;
41
41
  assistantName: string;
42
+ description?: string;
42
43
  topologyEdges: { from: string; to: string; purpose: string }[];
43
44
  totalEdges: number;
44
45
  }> = [];
@@ -331,6 +332,51 @@ export async function getWorkflowRun(
331
332
  }
332
333
  }
333
334
 
335
+ export async function deleteWorkflowRun(
336
+ request: FastifyRequest<{ Params: { runId: string } }>,
337
+ reply: FastifyReply
338
+ ): Promise<ApiResponse> {
339
+ const { runId } = request.params;
340
+ const tenantId = getTenantId(request);
341
+
342
+ try {
343
+ const store = getTrackingStore();
344
+ if (!store) {
345
+ return reply.status(404).send({ success: false, message: "No workflow tracking store configured" });
346
+ }
347
+
348
+ const run = await store.getWorkflowRun(runId);
349
+ if (!run) {
350
+ return reply.status(404).send({ success: false, message: "Workflow run not found" });
351
+ }
352
+
353
+ // If run is still running, abort the agent first
354
+ if (run.status === "running") {
355
+ try {
356
+ const workspace_id = request.headers["x-workspace-id"] as string;
357
+ const project_id = request.headers["x-project-id"] as string;
358
+ const agent = agentInstanceManager.getAgent({
359
+ assistant_id: run.assistantId,
360
+ thread_id: run.threadId,
361
+ tenant_id: tenantId,
362
+ workspace_id,
363
+ project_id,
364
+ });
365
+ await agent.abort();
366
+ } catch (err) {
367
+ request.log.warn({ runId, error: (err as Error).message }, "Failed to abort agent, deleting tracking records anyway");
368
+ }
369
+ }
370
+
371
+ await store.deleteWorkflowRun(runId);
372
+
373
+ return { success: true, message: "Workflow run deleted" };
374
+ } catch (error) {
375
+ request.log.error(error, "Failed to delete workflow run");
376
+ return reply.status(500).send({ success: false, message: "Failed to delete workflow run" });
377
+ }
378
+ }
379
+
334
380
  export async function getRunSteps(
335
381
  request: FastifyRequest<{ Params: { runId: string }; Querystring: { step_type?: string; status?: string } }>,
336
382
  reply: FastifyReply
@@ -435,7 +481,17 @@ export async function replyInboxTask(
435
481
  project_id,
436
482
  });
437
483
 
438
- // 3. 直接在后台触发 resume 执行(不等待,不阻塞 HTTP 响应)
484
+ // 3. 检查 graph 是否处于中断状态
485
+ const runStatus = await agent.getRunStatus();
486
+ if (runStatus !== ThreadStatus.INTERRUPTED) {
487
+ return reply.status(409).send({
488
+ success: false,
489
+ message: `Cannot resume: graph is not interrupted (current status: ${runStatus})`,
490
+ data: { runId: run.id, assistantId: run.assistantId, threadId: run.threadId, status: runStatus },
491
+ });
492
+ }
493
+
494
+ // 4. 直接在后台触发 resume 执行(不等待,不阻塞 HTTP 响应)
439
495
  agent.addMessage({
440
496
  input: { message: "Clarification answers submitted" },
441
497
  command: {
@@ -462,7 +518,7 @@ export async function replyInboxTask(
462
518
  request.log.error(error, "Background resume failed");
463
519
  });
464
520
 
465
- // 4. 立即返回成功(不等待 agent 执行完成)
521
+ // 5. 立即返回成功(不等待 agent 执行完成)
466
522
  return reply.status(200).send({
467
523
  success: true,
468
524
  message: "Resume request submitted successfully",
@@ -51,11 +51,21 @@ export class MessageRouter {
51
51
 
52
52
  try {
53
53
  await this.runMiddlewares(ctx, async () => {
54
+ // Resolve tenantId from installation if not provided
55
+ const tenantId = message.tenantId
56
+ || (await this.installationStore.getInstallationById(message.channelInstallationId))?.tenantId;
57
+
58
+ if (!tenantId) {
59
+ throw new Error(
60
+ "tenantId is required: provide it in the message or ensure the channelInstallation has a tenantId"
61
+ );
62
+ }
63
+
54
64
  let binding = await this.bindingRegistry.resolve({
55
65
  channel: message.channel,
56
66
  senderId: message.sender.id,
57
67
  channelInstallationId: message.channelInstallationId,
58
- tenantId: message.tenantId,
68
+ tenantId,
59
69
  });
60
70
 
61
71
  if (!binding) {
@@ -74,11 +84,11 @@ export class MessageRouter {
74
84
  id: "fallback",
75
85
  channel: message.channel,
76
86
  channelInstallationId: message.channelInstallationId,
77
- tenantId: message.tenantId,
87
+ tenantId,
78
88
  senderId: message.sender.id,
79
89
  agentId: installation.fallbackAgentId,
80
90
  threadId: undefined,
81
- threadMode: "fixed",
91
+ threadMode: "fixed" as const,
82
92
  enabled: true,
83
93
  createdAt: new Date(),
84
94
  updatedAt: new Date(),
@@ -103,7 +113,7 @@ export class MessageRouter {
103
113
  const threadStore = getStoreLattice("default", "thread").store;
104
114
  const newThreadId = randomUUID();
105
115
  const newThread = await threadStore.createThread(
106
- message.tenantId,
116
+ tenantId,
107
117
  ctx.binding.agentId,
108
118
  newThreadId,
109
119
  {
@@ -125,7 +135,7 @@ export class MessageRouter {
125
135
  }
126
136
 
127
137
  const agent = agentInstanceManager.getAgent({
128
- tenant_id: message.tenantId,
138
+ tenant_id: tenantId,
129
139
  assistant_id: ctx.binding.agentId,
130
140
  thread_id: threadId,
131
141
  workspace_id: ctx.binding.workspaceId || "",
@@ -378,7 +378,7 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
378
378
  const inboundMessage = {
379
379
  channel: msg.channel as string,
380
380
  channelInstallationId: (msg.channelInstallationId || "") as string,
381
- tenantId: (msg.tenantId || "default") as string,
381
+ tenantId: msg.tenantId as string | undefined,
382
382
  sender: {
383
383
  id: (msg.sender as Record<string, unknown>).id as string,
384
384
  displayName: (msg.sender as Record<string, unknown>).displayName as string | undefined,
@@ -437,6 +437,10 @@ export const registerLatticeRoutes = (app: FastifyInstance, channelDeps?: { rout
437
437
  Params: { runId: string };
438
438
  }>("/api/workflows/runs/:runId", workflowTrackingController.getWorkflowRun);
439
439
 
440
+ app.delete<{
441
+ Params: { runId: string };
442
+ }>("/api/workflows/runs/:runId", workflowTrackingController.deleteWorkflowRun);
443
+
440
444
  app.get<{
441
445
  Params: { runId: string };
442
446
  Querystring: { step_type?: string; status?: string };