@hexabot-ai/api 3.2.2-alpha.15 → 3.2.2-alpha.17
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/mcp/guards/hexabot-mcp-token.guard.js +2 -2
- package/dist/mcp/guards/hexabot-mcp-token.guard.js.map +1 -1
- package/dist/mcp/tools/credential-mcp.tools.d.ts +2 -2
- package/dist/mcp/tools/hexabot-mcp.utils.d.ts +2 -1
- package/dist/mcp/tools/hexabot-mcp.utils.js +13 -1
- package/dist/mcp/tools/hexabot-mcp.utils.js.map +1 -1
- package/dist/mcp/tools/workflow-mcp.helper.d.ts +18 -3
- package/dist/mcp/tools/workflow-mcp.helper.js +22 -2
- package/dist/mcp/tools/workflow-mcp.helper.js.map +1 -1
- package/dist/mcp/tools/workflow-mcp.tools.d.ts +340 -18
- package/dist/mcp/tools/workflow-mcp.tools.js +12 -7
- package/dist/mcp/tools/workflow-mcp.tools.js.map +1 -1
- package/dist/mcp/tools/workflow-run-mcp.tools.d.ts +2 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.js +19 -6
- package/dist/mcp/tools/workflow-run-mcp.tools.js.map +1 -1
- package/dist/mcp/tools/workflow-version-mcp.tools.d.ts +84 -76
- package/dist/mcp/tools/workflow-version-mcp.tools.js +165 -8
- package/dist/mcp/tools/workflow-version-mcp.tools.js.map +1 -1
- package/dist/static/assets/{browser-ponyfill-D3QB7SUD.js → browser-ponyfill-CBXq65SP.js} +1 -1
- package/dist/static/assets/{cssMode-Bk0-m_Ij.js → cssMode-CN8f6H-j.js} +1 -1
- package/dist/static/assets/{freemarker2-D1tTqqt8.js → freemarker2-Dfrb95Lt.js} +1 -1
- package/dist/static/assets/{handlebars-D3VQYLDj.js → handlebars-B2cershf.js} +1 -1
- package/dist/static/assets/{html-Cmhr3-6_.js → html-CfjuUmTR.js} +1 -1
- package/dist/static/assets/{htmlMode-Bb5Kynv_.js → htmlMode-pl8CUTaA.js} +1 -1
- package/dist/static/assets/{index-JPQj6OiL.js → index-Cq8K5Poc.js} +2131 -2131
- package/dist/static/assets/{index-BI1BtkYv.css → index-D-b9KTZd.css} +1 -1
- package/dist/static/assets/{javascript-BkQdNyFT.js → javascript-Be45wN2i.js} +1 -1
- package/dist/static/assets/{jsonMode-CS1296LF.js → jsonMode-yPU1rg8t.js} +1 -1
- package/dist/static/assets/{liquid-C9ufUE-r.js → liquid-BMJibvBr.js} +1 -1
- package/dist/static/assets/{lspLanguageFeatures-Dxd2SsrE.js → lspLanguageFeatures-Cxz6b29K.js} +1 -1
- package/dist/static/assets/{mdx-Dm4bC48D.js → mdx-DpvXygi3.js} +1 -1
- package/dist/static/assets/{python-ZvBWpr_q.js → python-BHEnNcMm.js} +1 -1
- package/dist/static/assets/{razor-DBj-LyWR.js → razor-B6-tNhh_.js} +1 -1
- package/dist/static/assets/{tsMode-DzMlJB2i.js → tsMode-J4SJIY5C.js} +1 -1
- package/dist/static/assets/{typescript-Cfo3bzpy.js → typescript-Df82IAgp.js} +1 -1
- package/dist/static/assets/{xml-ZyceR0L3.js → xml-C8jr31yy.js} +1 -1
- package/dist/static/assets/{yaml-oyu_gOar.js → yaml-BEd1RzBW.js} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/websocket/websocket.gateway.d.ts +2 -1
- package/dist/websocket/websocket.gateway.js.map +1 -1
- package/dist/workflow/services/workflow.service.d.ts +2 -1
- package/dist/workflow/services/workflow.service.js +10 -0
- package/dist/workflow/services/workflow.service.js.map +1 -1
- package/package.json +4 -4
- package/src/mcp/README.md +67 -55
- package/src/mcp/guards/hexabot-mcp-token.guard.ts +2 -2
- package/src/mcp/tools/hexabot-mcp.utils.ts +18 -2
- package/src/mcp/tools/workflow-mcp.helper.ts +62 -9
- package/src/mcp/tools/workflow-mcp.tools.ts +25 -8
- package/src/mcp/tools/workflow-run-mcp.tools.ts +25 -7
- package/src/mcp/tools/workflow-version-mcp.tools.ts +245 -12
- package/src/websocket/websocket.gateway.ts +2 -1
- package/src/workflow/services/workflow.service.ts +9 -1
|
@@ -39,6 +39,9 @@ import {
|
|
|
39
39
|
paginationSchema,
|
|
40
40
|
uuidSchema,
|
|
41
41
|
} from './hexabot-mcp.schemas';
|
|
42
|
+
import { omitKeysDeep } from './hexabot-mcp.utils';
|
|
43
|
+
|
|
44
|
+
const WORKFLOW_DEFINITION_RESPONSE_KEYS = ['definitionYml'] as const;
|
|
42
45
|
|
|
43
46
|
@Injectable()
|
|
44
47
|
export class HexabotWorkflowRunMcpTools extends HexabotMcpToolBase {
|
|
@@ -120,11 +123,12 @@ export class HexabotWorkflowRunMcpTools extends HexabotMcpToolBase {
|
|
|
120
123
|
...(args.workflowId ? { workflow: { id: args.workflowId } } : {}),
|
|
121
124
|
...(args.status ? { status: args.status } : {}),
|
|
122
125
|
} as any;
|
|
123
|
-
|
|
124
|
-
return await this.listWithCount(
|
|
126
|
+
const result = await this.listWithCount(
|
|
125
127
|
this.workflowRunService,
|
|
126
128
|
this.findOptions<WorkflowRunOrmEntity>(args, where),
|
|
127
129
|
);
|
|
130
|
+
|
|
131
|
+
return this.sanitizeWorkflowRunResponse(result);
|
|
128
132
|
}
|
|
129
133
|
|
|
130
134
|
@McpPermission('workflowrun', Action.READ)
|
|
@@ -142,7 +146,7 @@ export class HexabotWorkflowRunMcpTools extends HexabotMcpToolBase {
|
|
|
142
146
|
throw new NotFoundException(`Workflow run ${args.id} not found`);
|
|
143
147
|
}
|
|
144
148
|
|
|
145
|
-
return run;
|
|
149
|
+
return this.withoutWorkflowDefinition(run);
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
@McpPermission('workflowrun', Action.READ)
|
|
@@ -186,18 +190,19 @@ export class HexabotWorkflowRunMcpTools extends HexabotMcpToolBase {
|
|
|
186
190
|
this.workflowRunService.count({ where: childWhere }),
|
|
187
191
|
])
|
|
188
192
|
: ([null, [], 0] as const);
|
|
193
|
+
const includeWorkflowDefinition = args.includeWorkflowDefinition ?? true;
|
|
189
194
|
const response: Record<string, unknown> = {
|
|
190
|
-
run,
|
|
195
|
+
run: this.withoutWorkflowDefinition(run),
|
|
191
196
|
relatedRuns: {
|
|
192
|
-
parent: parentRun,
|
|
193
|
-
children: childRuns,
|
|
197
|
+
parent: this.withoutWorkflowDefinition(parentRun),
|
|
198
|
+
children: this.withoutWorkflowDefinition(childRuns),
|
|
194
199
|
childRunTotal,
|
|
195
200
|
childRunsLimit: includeRelatedRuns ? childRunsLimit : 0,
|
|
196
201
|
},
|
|
197
202
|
summary: this.buildWorkflowRunDebugSummary(run, childRunTotal),
|
|
198
203
|
};
|
|
199
204
|
|
|
200
|
-
if (
|
|
205
|
+
if (includeWorkflowDefinition) {
|
|
201
206
|
response.workflowDefinitionYml =
|
|
202
207
|
run.workflowVersion?.definitionYml ?? null;
|
|
203
208
|
}
|
|
@@ -271,4 +276,17 @@ export class HexabotWorkflowRunMcpTools extends HexabotMcpToolBase {
|
|
|
271
276
|
childRunTotal,
|
|
272
277
|
};
|
|
273
278
|
}
|
|
279
|
+
|
|
280
|
+
private sanitizeWorkflowRunResponse<T extends { items: unknown[] }>(
|
|
281
|
+
response: T,
|
|
282
|
+
): T {
|
|
283
|
+
return {
|
|
284
|
+
...response,
|
|
285
|
+
items: this.withoutWorkflowDefinition(response.items),
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
private withoutWorkflowDefinition<T>(value: T): T {
|
|
290
|
+
return omitKeysDeep(value, WORKFLOW_DEFINITION_RESPONSE_KEYS);
|
|
291
|
+
}
|
|
274
292
|
}
|
|
@@ -5,8 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { validateWorkflow } from '@hexabot-ai/agentic';
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
Action,
|
|
10
|
+
type WorkflowVersion,
|
|
11
|
+
type WorkflowVersionFull,
|
|
12
|
+
} from '@hexabot-ai/types';
|
|
13
|
+
import {
|
|
14
|
+
BadRequestException,
|
|
15
|
+
Injectable,
|
|
16
|
+
NotFoundException,
|
|
17
|
+
} from '@nestjs/common';
|
|
10
18
|
import { Tool, ToolGuards } from '@rekog/mcp-nest';
|
|
11
19
|
import { z } from 'zod';
|
|
12
20
|
|
|
@@ -67,11 +75,14 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
67
75
|
request?: HexabotMcpRequest,
|
|
68
76
|
) {
|
|
69
77
|
const actorId = this.getActorId(request);
|
|
70
|
-
|
|
71
|
-
return await this.workflowHelper.commitWorkflowDefinition({
|
|
78
|
+
const version = await this.workflowHelper.commitWorkflowDefinition({
|
|
72
79
|
...args,
|
|
73
80
|
createdBy: actorId,
|
|
74
81
|
});
|
|
82
|
+
|
|
83
|
+
return this.workflowHelper.summarizeWorkflowVersion(version, {
|
|
84
|
+
includeDefinitionYmlByteLength: true,
|
|
85
|
+
});
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
@McpPermission('workflow', Action.READ)
|
|
@@ -108,7 +119,8 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
108
119
|
@ToolGuards([McpPermissionGuard])
|
|
109
120
|
@Tool({
|
|
110
121
|
name: 'hexabot_workflow_version_search',
|
|
111
|
-
description:
|
|
122
|
+
description:
|
|
123
|
+
'List compact workflow definition version metadata for a workflow, excluding YAML bodies.',
|
|
112
124
|
parameters: z.object({
|
|
113
125
|
workflowId: uuidSchema,
|
|
114
126
|
...paginationSchema,
|
|
@@ -118,18 +130,28 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
118
130
|
})
|
|
119
131
|
async searchWorkflowVersions(args: { workflowId: string } & PaginationArgs) {
|
|
120
132
|
const where = { workflow: { id: args.workflowId } } as any;
|
|
121
|
-
|
|
122
|
-
return await this.listWithCount(
|
|
133
|
+
const result = await this.listWithCount(
|
|
123
134
|
this.workflowVersionService,
|
|
124
135
|
this.findOptions<WorkflowVersionOrmEntity>(args, where),
|
|
125
136
|
);
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
...result,
|
|
140
|
+
items: result.items.map((version) =>
|
|
141
|
+
this.workflowHelper.summarizeWorkflowVersion(
|
|
142
|
+
version as WorkflowVersion,
|
|
143
|
+
{ includeDefinitionYmlByteLength: true },
|
|
144
|
+
),
|
|
145
|
+
),
|
|
146
|
+
};
|
|
126
147
|
}
|
|
127
148
|
|
|
128
149
|
@McpPermission('workflowversion', Action.READ)
|
|
129
150
|
@ToolGuards([McpPermissionGuard])
|
|
130
151
|
@Tool({
|
|
131
152
|
name: 'hexabot_workflow_version_get',
|
|
132
|
-
description:
|
|
153
|
+
description:
|
|
154
|
+
'Read compact workflow definition version metadata, excluding the YAML body.',
|
|
133
155
|
parameters: z.object({
|
|
134
156
|
id: uuidSchema,
|
|
135
157
|
workflowId: uuidSchema.optional(),
|
|
@@ -147,7 +169,57 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
147
169
|
throw new NotFoundException(`Workflow version ${args.id} not found`);
|
|
148
170
|
}
|
|
149
171
|
|
|
150
|
-
return version
|
|
172
|
+
return this.workflowHelper.summarizeWorkflowVersion(version, {
|
|
173
|
+
includeDefinitionYmlByteLength: true,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@McpPermission('workflowversion', Action.READ)
|
|
178
|
+
@ToolGuards([McpPermissionGuard])
|
|
179
|
+
@Tool({
|
|
180
|
+
name: 'hexabot_workflow_yaml_get',
|
|
181
|
+
description:
|
|
182
|
+
'Read workflow definition YAML by workflowId/current version or versionId, with checksum, byte length, and UTF-8 byte offset/limit chunking.',
|
|
183
|
+
parameters: z.object({
|
|
184
|
+
workflowId: uuidSchema.optional(),
|
|
185
|
+
versionId: uuidSchema.optional(),
|
|
186
|
+
offset: z.number().int().min(0).default(0),
|
|
187
|
+
limit: z.number().int().min(1).max(64000).default(16000),
|
|
188
|
+
}),
|
|
189
|
+
})
|
|
190
|
+
async getWorkflowYaml(args: {
|
|
191
|
+
workflowId?: string;
|
|
192
|
+
versionId?: string;
|
|
193
|
+
offset?: number;
|
|
194
|
+
limit?: number;
|
|
195
|
+
}) {
|
|
196
|
+
const version = await this.requireWorkflowVersionForYaml(args);
|
|
197
|
+
const definitionYml = version.definitionYml;
|
|
198
|
+
const offset = args.offset ?? 0;
|
|
199
|
+
const limit = args.limit ?? 16000;
|
|
200
|
+
const { chunk, chunkByteLength, endOffset, totalByteLength } =
|
|
201
|
+
this.sliceUtf8Chunk(definitionYml, offset, limit);
|
|
202
|
+
const workflowId = this.resolveRelationId(version.workflow);
|
|
203
|
+
|
|
204
|
+
return {
|
|
205
|
+
workflowId,
|
|
206
|
+
versionId: version.id,
|
|
207
|
+
version: version.version,
|
|
208
|
+
checksum: version.checksum,
|
|
209
|
+
definitionYmlByteLength: totalByteLength,
|
|
210
|
+
definitionYmlLength: definitionYml.length,
|
|
211
|
+
chunk: {
|
|
212
|
+
offset,
|
|
213
|
+
limit,
|
|
214
|
+
unit: 'utf8Bytes',
|
|
215
|
+
endOffset,
|
|
216
|
+
length: chunk.length,
|
|
217
|
+
byteLength: chunkByteLength,
|
|
218
|
+
hasMore: endOffset < totalByteLength,
|
|
219
|
+
nextOffset: endOffset < totalByteLength ? endOffset : null,
|
|
220
|
+
},
|
|
221
|
+
definitionYml: chunk,
|
|
222
|
+
};
|
|
151
223
|
}
|
|
152
224
|
|
|
153
225
|
@McpPermission('workflowversion', Action.UPDATE)
|
|
@@ -162,9 +234,13 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
162
234
|
}),
|
|
163
235
|
})
|
|
164
236
|
async updateWorkflowVersion(args: { id: string; message?: string | null }) {
|
|
165
|
-
|
|
237
|
+
const version = await this.workflowVersionService.updateOne(args.id, {
|
|
166
238
|
message: args.message ?? undefined,
|
|
167
239
|
});
|
|
240
|
+
|
|
241
|
+
return this.workflowHelper.summarizeWorkflowVersion(version, {
|
|
242
|
+
includeDefinitionYmlByteLength: true,
|
|
243
|
+
});
|
|
168
244
|
}
|
|
169
245
|
|
|
170
246
|
@McpPermission('workflowversion', Action.CREATE)
|
|
@@ -184,10 +260,14 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
184
260
|
_context: unknown,
|
|
185
261
|
request?: HexabotMcpRequest,
|
|
186
262
|
) {
|
|
187
|
-
|
|
263
|
+
const version = await this.workflowHelper.restoreWorkflowVersionSnapshot(
|
|
188
264
|
args,
|
|
189
265
|
this.getActorId(request),
|
|
190
266
|
);
|
|
267
|
+
|
|
268
|
+
return this.workflowHelper.summarizeWorkflowVersion(version, {
|
|
269
|
+
includeDefinitionYmlByteLength: true,
|
|
270
|
+
});
|
|
191
271
|
}
|
|
192
272
|
|
|
193
273
|
@McpPermission('workflowversion', Action.CREATE)
|
|
@@ -207,10 +287,14 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
207
287
|
_context: unknown,
|
|
208
288
|
request?: HexabotMcpRequest,
|
|
209
289
|
) {
|
|
210
|
-
|
|
290
|
+
const version = await this.workflowHelper.restoreWorkflowVersionSnapshot(
|
|
211
291
|
args,
|
|
212
292
|
this.getActorId(request),
|
|
213
293
|
);
|
|
294
|
+
|
|
295
|
+
return this.workflowHelper.summarizeWorkflowVersion(version, {
|
|
296
|
+
includeDefinitionYmlByteLength: true,
|
|
297
|
+
});
|
|
214
298
|
}
|
|
215
299
|
|
|
216
300
|
private getWorkflowValidationActions() {
|
|
@@ -223,4 +307,153 @@ export class HexabotWorkflowVersionMcpTools extends HexabotMcpToolBase {
|
|
|
223
307
|
),
|
|
224
308
|
);
|
|
225
309
|
}
|
|
310
|
+
|
|
311
|
+
private async requireWorkflowVersionForYaml(args: {
|
|
312
|
+
workflowId?: string;
|
|
313
|
+
versionId?: string;
|
|
314
|
+
}): Promise<WorkflowVersion | WorkflowVersionFull> {
|
|
315
|
+
if (!args.workflowId && !args.versionId) {
|
|
316
|
+
throw new BadRequestException(
|
|
317
|
+
'workflowId or versionId is required to read workflow YAML',
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (args.versionId) {
|
|
322
|
+
const version = await this.workflowVersionService.findOneAndPopulate({
|
|
323
|
+
where: {
|
|
324
|
+
id: args.versionId,
|
|
325
|
+
...(args.workflowId ? { workflow: { id: args.workflowId } } : {}),
|
|
326
|
+
} as any,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
if (!version) {
|
|
330
|
+
throw new NotFoundException(
|
|
331
|
+
`Workflow version ${args.versionId} not found`,
|
|
332
|
+
);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return version;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const workflowId = args.workflowId;
|
|
339
|
+
if (!workflowId) {
|
|
340
|
+
throw new BadRequestException(
|
|
341
|
+
'workflowId is required when versionId is not provided',
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
const workflow = await this.workflowHelper.requireWorkflow(workflowId);
|
|
345
|
+
const currentVersionId = this.resolveRelationId(workflow.currentVersion);
|
|
346
|
+
if (!currentVersionId) {
|
|
347
|
+
throw new NotFoundException(
|
|
348
|
+
`Workflow ${workflowId} has no current version`,
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const version = await this.workflowVersionService.findOneAndPopulate({
|
|
353
|
+
where: {
|
|
354
|
+
id: currentVersionId,
|
|
355
|
+
workflow: { id: workflowId },
|
|
356
|
+
} as any,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
if (!version) {
|
|
360
|
+
throw new NotFoundException(
|
|
361
|
+
`Workflow version ${currentVersionId} not found`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return version;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
private sliceUtf8Chunk(
|
|
369
|
+
value: string,
|
|
370
|
+
offset: number,
|
|
371
|
+
limit: number,
|
|
372
|
+
): {
|
|
373
|
+
chunk: string;
|
|
374
|
+
chunkByteLength: number;
|
|
375
|
+
endOffset: number;
|
|
376
|
+
totalByteLength: number;
|
|
377
|
+
} {
|
|
378
|
+
const totalByteLength = Buffer.byteLength(value, 'utf8');
|
|
379
|
+
if (offset > totalByteLength) {
|
|
380
|
+
throw new BadRequestException(
|
|
381
|
+
`Offset ${offset} exceeds workflow YAML byte length ${totalByteLength}`,
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const startIndex = this.findUtf8IndexForOffset(value, offset);
|
|
386
|
+
let endIndex = value.length;
|
|
387
|
+
let byteOffset = offset;
|
|
388
|
+
|
|
389
|
+
for (let index = startIndex; index < value.length; ) {
|
|
390
|
+
const char = this.readCodePoint(value, index);
|
|
391
|
+
const nextByteOffset = byteOffset + Buffer.byteLength(char, 'utf8');
|
|
392
|
+
|
|
393
|
+
if (nextByteOffset > offset + limit) {
|
|
394
|
+
if (index === startIndex) {
|
|
395
|
+
throw new BadRequestException(
|
|
396
|
+
`Limit ${limit} is too small to include the next UTF-8 character at offset ${offset}`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
endIndex = index;
|
|
400
|
+
break;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
byteOffset = nextByteOffset;
|
|
404
|
+
index += char.length;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const chunk = value.slice(startIndex, endIndex);
|
|
408
|
+
const chunkByteLength = Buffer.byteLength(chunk, 'utf8');
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
chunk,
|
|
412
|
+
chunkByteLength,
|
|
413
|
+
endOffset: offset + chunkByteLength,
|
|
414
|
+
totalByteLength,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
private findUtf8IndexForOffset(value: string, offset: number): number {
|
|
419
|
+
if (offset === 0) {
|
|
420
|
+
return 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
let byteOffset = 0;
|
|
424
|
+
for (let index = 0; index < value.length; ) {
|
|
425
|
+
const char = this.readCodePoint(value, index);
|
|
426
|
+
const nextByteOffset = byteOffset + Buffer.byteLength(char, 'utf8');
|
|
427
|
+
|
|
428
|
+
if (offset === nextByteOffset) {
|
|
429
|
+
return index + char.length;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (offset < nextByteOffset) {
|
|
433
|
+
throw new BadRequestException(
|
|
434
|
+
`Offset ${offset} is not aligned to a UTF-8 character boundary`,
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
byteOffset = nextByteOffset;
|
|
439
|
+
index += char.length;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
if (offset === byteOffset) {
|
|
443
|
+
return value.length;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
throw new BadRequestException(
|
|
447
|
+
`Offset ${offset} exceeds workflow YAML byte length ${byteOffset}`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private readCodePoint(value: string, index: number): string {
|
|
452
|
+
const codePoint = value.codePointAt(index);
|
|
453
|
+
if (codePoint === undefined) {
|
|
454
|
+
return '';
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
return String.fromCodePoint(codePoint);
|
|
458
|
+
}
|
|
226
459
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { type WorkflowEventMap } from '@hexabot-ai/agentic';
|
|
8
|
-
import { StdEventType, Subscriber } from '@hexabot-ai/types';
|
|
8
|
+
import { StdEventType, Subscriber, type WorkflowRun } from '@hexabot-ai/types';
|
|
9
9
|
import { ForbiddenException, Optional } from '@nestjs/common';
|
|
10
10
|
import { EventEmitter2, OnEvent } from '@nestjs/event-emitter';
|
|
11
11
|
import {
|
|
@@ -69,6 +69,7 @@ export class WebsocketGateway
|
|
|
69
69
|
Partial<Pick<WorkflowContextState, 'initiatorId'>> & {
|
|
70
70
|
workflowId: string;
|
|
71
71
|
threadId?: string;
|
|
72
|
+
workflowRun?: WorkflowRun;
|
|
72
73
|
workflowEvent: string;
|
|
73
74
|
t: number;
|
|
74
75
|
}): void {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Full terms: see LICENSE.md.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { WorkflowEventMap } from '@hexabot-ai/agentic';
|
|
7
|
+
import type { WorkflowEventMap } from '@hexabot-ai/agentic';
|
|
8
8
|
import { WorkflowFull, Workflow } from '@hexabot-ai/types';
|
|
9
9
|
import {
|
|
10
10
|
BadRequestException,
|
|
@@ -232,6 +232,7 @@ export class WorkflowService extends BaseOrmService<WorkflowOrmEntity> {
|
|
|
232
232
|
this.gateway.broadcastWorkflowEvent({
|
|
233
233
|
...payload,
|
|
234
234
|
t,
|
|
235
|
+
workflowRun,
|
|
235
236
|
workflowId,
|
|
236
237
|
initiatorId,
|
|
237
238
|
threadId: typeof threadId === 'string' ? threadId : undefined,
|
|
@@ -249,6 +250,13 @@ export class WorkflowService extends BaseOrmService<WorkflowOrmEntity> {
|
|
|
249
250
|
await this.sendWorkflowEvent('hook:workflow:finish', payload);
|
|
250
251
|
}
|
|
251
252
|
|
|
253
|
+
@OnEvent('hook:workflow:suspended')
|
|
254
|
+
async sendWorkflowSuspended(
|
|
255
|
+
payload: WorkflowEventMap[keyof WorkflowEventMap],
|
|
256
|
+
) {
|
|
257
|
+
await this.sendWorkflowEvent('hook:workflow:suspended', payload);
|
|
258
|
+
}
|
|
259
|
+
|
|
252
260
|
@OnEvent('hook:workflow:failure')
|
|
253
261
|
async sendWorkflowFailure(payload: WorkflowEventMap[keyof WorkflowEventMap]) {
|
|
254
262
|
await this.sendWorkflowEvent('hook:workflow:failure', payload);
|