@hexabot-ai/api 3.3.0 → 3.3.2

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 (48) hide show
  1. package/dist/bindings/index.d.ts +1 -0
  2. package/dist/bindings/index.js +1 -0
  3. package/dist/bindings/index.js.map +1 -1
  4. package/dist/extensions/actions/ai/ai-base.action.d.ts +4 -10
  5. package/dist/extensions/actions/ai/ai-base.action.js +8 -162
  6. package/dist/extensions/actions/ai/ai-base.action.js.map +1 -1
  7. package/dist/extensions/actions/ai/ai-prompt.helpers.d.ts +19 -0
  8. package/dist/extensions/actions/ai/ai-prompt.helpers.js +179 -0
  9. package/dist/extensions/actions/ai/ai-prompt.helpers.js.map +1 -0
  10. package/dist/extensions/actions/memory/update-memory.action.d.ts +1 -1
  11. package/dist/extensions/index.d.ts +2 -0
  12. package/dist/extensions/index.js +2 -0
  13. package/dist/extensions/index.js.map +1 -1
  14. package/dist/license/types/license-quota.js +1 -1
  15. package/dist/license/types/license-quota.js.map +1 -1
  16. package/dist/static/assets/{cssMode-CVfTJuzy.js → cssMode-CibBXGrW.js} +1 -1
  17. package/dist/static/assets/{freemarker2-BA7GKqB7.js → freemarker2-7NvEeTKe.js} +1 -1
  18. package/dist/static/assets/{handlebars-BCwvKzIX.js → handlebars-BbfYBNXO.js} +1 -1
  19. package/dist/static/assets/{html-f6_JN1YY.js → html-DLG6BvlD.js} +1 -1
  20. package/dist/static/assets/{htmlMode-f4asMwRs.js → htmlMode-iXpl7d5V.js} +1 -1
  21. package/dist/static/assets/{index-X6nbc-0C.js → index-BPjGVz_f.js} +392 -392
  22. package/dist/static/assets/index-DHPA03eu.css +1 -0
  23. package/dist/static/assets/{javascript-DaS6vtZ9.js → javascript-BuG8vffM.js} +1 -1
  24. package/dist/static/assets/{jsonMode-Ct41J50t.js → jsonMode-DP5Vig9S.js} +1 -1
  25. package/dist/static/assets/{liquid-4giXfRN2.js → liquid-CRTQV_4V.js} +1 -1
  26. package/dist/static/assets/{lspLanguageFeatures-CqJlQWTJ.js → lspLanguageFeatures-BnMkhqT9.js} +1 -1
  27. package/dist/static/assets/{mdx-DcO2npTr.js → mdx-CQ43QJie.js} +1 -1
  28. package/dist/static/assets/{python-D7O1gNbd.js → python-CQ9HRuTn.js} +1 -1
  29. package/dist/static/assets/{razor-K5r5YQ4D.js → razor-BY29I0xJ.js} +1 -1
  30. package/dist/static/assets/{tsMode-CvIEOKtw.js → tsMode-DnpMBMFq.js} +1 -1
  31. package/dist/static/assets/{typescript-BKjAKJn9.js → typescript-BrYZsZrw.js} +1 -1
  32. package/dist/static/assets/{xml-BpIa2Zv1.js → xml-DbNzxxky.js} +1 -1
  33. package/dist/static/assets/{yaml-BJzMbZhP.js → yaml-LdVsxyvH.js} +1 -1
  34. package/dist/static/index.html +2 -2
  35. package/dist/static/locales/en/translation.json +2 -1
  36. package/dist/static/locales/fr/translation.json +2 -1
  37. package/dist/tsconfig.build.tsbuildinfo +1 -1
  38. package/dist/workflow/index.d.ts +2 -0
  39. package/dist/workflow/index.js +2 -0
  40. package/dist/workflow/index.js.map +1 -1
  41. package/package.json +4 -4
  42. package/src/bindings/index.ts +2 -0
  43. package/src/extensions/actions/ai/ai-base.action.ts +19 -246
  44. package/src/extensions/actions/ai/ai-prompt.helpers.ts +299 -0
  45. package/src/extensions/index.ts +4 -0
  46. package/src/license/types/license-quota.ts +1 -1
  47. package/src/workflow/index.ts +4 -0
  48. package/dist/static/assets/index-CLS5CNoN.css +0 -1
@@ -23,6 +23,8 @@ export * from './repositories/memory-definition.repository';
23
23
  export * from './repositories/memory-record.repository';
24
24
  export * from './repositories/mcp-server.repository';
25
25
  export * from './contexts/conversational-workflow.context';
26
+ export * from './contexts/workflow-runtime.context';
27
+ export * from './types';
26
28
  export * from './services/workflow.service';
27
29
  export * from './services/workflow-version.service';
28
30
  export * from './services/workflow-run.service';
@@ -39,6 +39,8 @@ __exportStar(require("./repositories/memory-definition.repository"), exports);
39
39
  __exportStar(require("./repositories/memory-record.repository"), exports);
40
40
  __exportStar(require("./repositories/mcp-server.repository"), exports);
41
41
  __exportStar(require("./contexts/conversational-workflow.context"), exports);
42
+ __exportStar(require("./contexts/workflow-runtime.context"), exports);
43
+ __exportStar(require("./types"), exports);
42
44
  __exportStar(require("./services/workflow.service"), exports);
43
45
  __exportStar(require("./services/workflow-version.service"), exports);
44
46
  __exportStar(require("./services/workflow-run.service"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAMA,oDAAkC;AAElC,6EAA2D;AAE3D,sEAAoD;AAEpD,oEAAkD;AAElD,4EAA0D;AAE1D,wEAAsD;AAEtD,qDAAmC;AAEnC,6DAA2C;AAE3C,yDAAuC;AAEvC,8DAA4C;AAE5C,0DAAwC;AAExC,uDAAqC;AAErC,6DAA2C;AAE3C,qEAAmD;AAEnD,iEAA+C;AAE/C,sEAAoD;AAEpD,kEAAgD;AAEhD,+DAA6C;AAE7C,qEAAmD;AAEnD,6EAA2D;AAE3D,yEAAuD;AAEvD,8EAA4D;AAE5D,0EAAwD;AAExD,uEAAqD;AAErD,6EAA2D;AAE3D,8DAA4C;AAE5C,sEAAoD;AAEpD,kEAAgD;AAEhD,uEAAqD;AAErD,mEAAiD;AAEjD,4DAA0C;AAE1C,6DAA2C;AAE3C,gEAA8C;AAE9C,qEAAmD;AAEnD,uEAAqD;AAErD,2EAAyD;AAEzD,mFAAiE;AAEjE,iFAA+D;AAE/D,iFAA+D;AAE/D,sEAAoD;AAEpD,wEAAsD;AAEtD,8DAA4C"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAMA,oDAAkC;AAElC,6EAA2D;AAE3D,sEAAoD;AAEpD,oEAAkD;AAElD,4EAA0D;AAE1D,wEAAsD;AAEtD,qDAAmC;AAEnC,6DAA2C;AAE3C,yDAAuC;AAEvC,8DAA4C;AAE5C,0DAAwC;AAExC,uDAAqC;AAErC,6DAA2C;AAE3C,qEAAmD;AAEnD,iEAA+C;AAE/C,sEAAoD;AAEpD,kEAAgD;AAEhD,+DAA6C;AAE7C,qEAAmD;AAEnD,6EAA2D;AAE3D,yEAAuD;AAEvD,8EAA4D;AAE5D,0EAAwD;AAExD,uEAAqD;AAErD,6EAA2D;AAE3D,sEAAoD;AAEpD,0CAAwB;AAExB,8DAA4C;AAE5C,sEAAoD;AAEpD,kEAAgD;AAEhD,uEAAqD;AAErD,mEAAiD;AAEjD,4DAA0C;AAE1C,6DAA2C;AAE3C,gEAA8C;AAE9C,qEAAmD;AAEnD,uEAAqD;AAErD,2EAAyD;AAEzD,mFAAiE;AAEjE,iFAA+D;AAE/D,iFAA+D;AAE/D,sEAAoD;AAEpD,wEAAsD;AAEtD,8DAA4C"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hexabot-ai/api",
3
3
  "private": false,
4
- "version": "3.3.0",
4
+ "version": "3.3.2",
5
5
  "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
6
6
  "author": "Hexastack",
7
7
  "license": "FCL-1.0-ALv2",
@@ -93,8 +93,8 @@
93
93
  "typeorm": "^0.3.29",
94
94
  "yaml": "^2.8.3",
95
95
  "zod": "^4.3.6",
96
- "@hexabot-ai/agentic": "3.2.0",
97
- "@hexabot-ai/types": "3.1.0"
96
+ "@hexabot-ai/agentic": "3.2.2",
97
+ "@hexabot-ai/types": "3.1.2"
98
98
  },
99
99
  "devDependencies": {
100
100
  "@compodoc/compodoc": "^1.2.1",
@@ -143,7 +143,7 @@
143
143
  "tsconfig-paths": "^4.2.0",
144
144
  "tsconfig-paths-jest": "^0.0.1",
145
145
  "typescript": "^5.8.3",
146
- "@hexabot-ai/frontend": "3.3.0"
146
+ "@hexabot-ai/frontend": "3.3.2"
147
147
  },
148
148
  "optionalDependencies": {
149
149
  "@css-inline/css-inline-linux-arm64-musl": "^0.14.1",
@@ -8,4 +8,6 @@ export * from './bindings.module';
8
8
 
9
9
  export * from './base-binding-kind';
10
10
 
11
+ export * from './runtime-bindings';
12
+
11
13
  export * from './create-binding-kind';
@@ -7,17 +7,10 @@
7
7
  import { createOpenAI } from '@ai-sdk/openai';
8
8
  import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
9
9
  import { ProviderV2, ProviderV3 } from '@ai-sdk/provider';
10
- import {
11
- IncomingMessageType,
12
- Message,
13
- StdIncomingMessage,
14
- StdOutgoingMessage,
15
- Thread,
16
- } from '@hexabot-ai/types';
10
+ import { StdIncomingMessage, StdOutgoingMessage } from '@hexabot-ai/types';
17
11
  import {
18
12
  LanguageModel,
19
13
  LanguageModelUsage,
20
- ModelMessage,
21
14
  ToolSet,
22
15
  hasToolCall,
23
16
  stepCountIs,
@@ -31,11 +24,15 @@ import { WorkflowRuntimeContext } from '@/workflow/contexts/workflow-runtime.con
31
24
  import { McpToolBindingDefinitions } from '@/workflow/types';
32
25
 
33
26
  import {
34
- AiCommonSettings,
35
- AiPromptInput,
36
- DEFAULT_AI_MESSAGES_LIMIT,
37
- DEFAULT_AI_PROMPT,
38
- } from './ai-schemas';
27
+ buildMemoryPrompt,
28
+ buildPrompt,
29
+ formatMemoryValue,
30
+ normalizeMessagesForModel,
31
+ resolveMemoryBindingSlugs,
32
+ resolveMessageContent,
33
+ type PromptPayload,
34
+ } from './ai-prompt.helpers';
35
+ import { AiCommonSettings, AiPromptInput } from './ai-schemas';
39
36
 
40
37
  export type { AiCommonSettings, AiPromptInput } from './ai-schemas';
41
38
 
@@ -55,16 +52,6 @@ export type LanguageModelProvider =
55
52
  | ProviderV3
56
53
  | ProviderV2;
57
54
 
58
- type PromptPayload =
59
- | {
60
- prompt: string;
61
- system?: string;
62
- }
63
- | {
64
- messages: ModelMessage[];
65
- system?: string;
66
- };
67
-
68
55
  export abstract class AiBaseAction<
69
56
  I,
70
57
  O,
@@ -374,254 +361,40 @@ export abstract class AiBaseAction<
374
361
  protected resolveMessageContent(
375
362
  payload: StdOutgoingMessage | StdIncomingMessage,
376
363
  ) {
377
- if (!payload) {
378
- return undefined;
379
- }
380
-
381
- const data = payload.data as Record<string, unknown>;
382
-
383
- if (typeof data.text === 'string') {
384
- return data.text;
385
- }
386
-
387
- if (typeof data.serializedText === 'string') {
388
- return data.serializedText;
389
- }
390
-
391
- if (
392
- payload.type === IncomingMessageType.location &&
393
- typeof data.coordinates === 'object' &&
394
- data.coordinates !== null &&
395
- 'lat' in data.coordinates &&
396
- 'lon' in data.coordinates
397
- ) {
398
- const { lat, lon } = data.coordinates as { lat: number; lon: number };
399
-
400
- return `location:${lat},${lon}`;
401
- }
402
-
403
- try {
404
- return JSON.stringify(data);
405
- } catch {
406
- return String(payload);
407
- }
364
+ return resolveMessageContent(payload);
408
365
  }
409
366
 
410
367
  protected normalizeMessagesForModel(
411
- messages: Message[],
368
+ messages: Parameters<typeof normalizeMessagesForModel>[0],
412
369
  subscriberId: string,
413
- ): ModelMessage[] {
414
- type ConversationMessage = {
415
- role: Extract<ModelMessage['role'], 'user' | 'assistant'>;
416
- content: string;
417
- createdAt: Date;
418
- };
419
-
420
- const normalized: ConversationMessage[] = messages
421
- .map((message) => {
422
- const content = this.resolveMessageContent(message.message);
423
-
424
- if (!content) {
425
- return undefined;
426
- }
427
-
428
- const role: ConversationMessage['role'] =
429
- message.sender === subscriberId ? 'user' : 'assistant';
430
-
431
- return {
432
- role,
433
- content,
434
- createdAt: message.createdAt,
435
- };
436
- })
437
- .filter((message): message is ConversationMessage => Boolean(message));
438
-
439
- return normalized
440
- .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
441
- .map(({ role, content }) => ({ role, content }));
370
+ ) {
371
+ return normalizeMessagesForModel(messages, subscriberId);
442
372
  }
443
373
 
444
- protected async buildPrompt(
374
+ protected buildPrompt(
445
375
  input: AiPromptInput,
446
376
  context: C,
447
377
  selectedMemorySlugs: string[] = [],
448
378
  ): Promise<PromptPayload> {
449
- const promptPayload = { system: input.system };
450
- const memoryPrompt = this.buildMemoryPrompt(context, selectedMemorySlugs);
451
- if (memoryPrompt) {
452
- promptPayload.system = promptPayload.system
453
- ? `${promptPayload.system}\n\n${memoryPrompt}`
454
- : memoryPrompt;
455
- }
456
-
457
- if (input.input_mode === 'prompt') {
458
- const prompt = input.prompt ?? DEFAULT_AI_PROMPT;
459
-
460
- return {
461
- prompt,
462
- system: promptPayload.system,
463
- };
464
- }
465
-
466
- if (input.input_mode === 'history') {
467
- const messagesLimit = input.messages_limit ?? DEFAULT_AI_MESSAGES_LIMIT;
468
-
469
- if (messagesLimit < 1) {
470
- throw new Error(
471
- 'Input mode "history" requires a positive "messages_limit" value.',
472
- );
473
- }
474
-
475
- const subscriberId = context.initiatorId;
476
- if (!subscriberId) {
477
- throw new Error(
478
- 'A subscriber id is required to load previous messages for this action.',
479
- );
480
- }
481
- const threadId = context.threadId;
482
- if (!threadId) {
483
- throw new Error(
484
- 'A thread id is required to load previous messages for this action.',
485
- );
486
- }
487
-
488
- const messageService = context.services.message;
489
- if (!messageService) {
490
- throw new Error(
491
- 'Message service is unavailable in the workflow context.',
492
- );
493
- }
494
-
495
- const history = await messageService.findLastMessages(
496
- { id: threadId } as Thread,
497
- messagesLimit,
498
- );
499
- const messages = this.normalizeMessagesForModel(history, subscriberId);
500
-
501
- return { messages, system: promptPayload.system };
502
- }
503
-
504
- throw new Error(
505
- 'An "input_mode" of either "prompt" or "history" is required to build the model request.',
506
- );
379
+ return buildPrompt(input, context, selectedMemorySlugs);
507
380
  }
508
381
 
509
382
  protected resolveMemoryBindingSlugs(
510
383
  context: C,
511
384
  memoryBindings?: RuntimeBindings['memory'],
512
385
  ): string[] {
513
- if (!memoryBindings || Object.keys(memoryBindings).length === 0) {
514
- return [];
515
- }
516
-
517
- const memoryStore = context.memoryStore;
518
- if (!memoryStore) {
519
- return [];
520
- }
521
-
522
- const idToSlug = new Map<string, string>();
523
- for (const [slug, definition] of memoryStore.definitionCache.entries()) {
524
- if (definition.id) {
525
- idToSlug.set(definition.id, slug);
526
- }
527
- }
528
-
529
- const selectedSlugs = new Set<string>();
530
- for (const [defName, binding] of Object.entries(memoryBindings)) {
531
- const definitionId = binding.settings?.definition_id;
532
- const slug =
533
- typeof definitionId === 'string'
534
- ? idToSlug.get(definitionId)
535
- : undefined;
536
- if (!slug) {
537
- throw new Error(
538
- `Unable to resolve memory definition "${String(definitionId)}" from bindings.memory.${defName}.settings.definition_id.`,
539
- );
540
- }
541
-
542
- selectedSlugs.add(slug);
543
- }
544
-
545
- return Array.from(selectedSlugs);
386
+ return resolveMemoryBindingSlugs(context, memoryBindings);
546
387
  }
547
388
 
548
389
  protected buildMemoryPrompt(
549
390
  context: C,
550
391
  selectedMemorySlugs: string[] = [],
551
392
  ): string | undefined {
552
- if (selectedMemorySlugs.length === 0) {
553
- return undefined;
554
- }
555
-
556
- const memoryStore = context.memoryStore;
557
- if (!memoryStore) {
558
- return undefined;
559
- }
560
-
561
- const { definitionCache, instances } = memoryStore;
562
- if (!definitionCache || definitionCache.size === 0) {
563
- return undefined;
564
- }
565
-
566
- const sections: string[] = [];
567
- const selectedSlugSet = new Set(selectedMemorySlugs);
568
- for (const [slug, definition] of definitionCache.entries()) {
569
- if (!selectedSlugSet.has(slug)) {
570
- continue;
571
- }
572
-
573
- const instance = instances[slug];
574
- if (!instance) {
575
- continue;
576
- }
577
-
578
- const lines: string[] = [];
579
- for (const field of instance.fields({ includeAdditional: true })) {
580
- if (field.value === undefined) {
581
- continue;
582
- }
583
-
584
- const label = (field.title ?? field.name).trim();
585
- const value = this.formatMemoryValue(field.value);
586
- lines.push(`- ${label}: ${value}`);
587
- }
588
-
589
- if (lines.length === 0) {
590
- continue;
591
- }
592
-
593
- sections.push(`## ${definition.name}\n${lines.join('\n')}`);
594
- }
595
-
596
- if (sections.length === 0) {
597
- return undefined;
598
- }
599
-
600
- return `# Working Memory\n${sections.join('\n\n')}`;
393
+ return buildMemoryPrompt(context, selectedMemorySlugs);
601
394
  }
602
395
 
603
396
  protected formatMemoryValue(value: unknown): string {
604
- if (typeof value === 'string') {
605
- return value;
606
- }
607
-
608
- if (typeof value === 'number' || typeof value === 'boolean') {
609
- return String(value);
610
- }
611
-
612
- if (value === null) {
613
- return 'null';
614
- }
615
-
616
- if (value instanceof Date) {
617
- return value.toISOString();
618
- }
619
-
620
- try {
621
- return JSON.stringify(value);
622
- } catch {
623
- return String(value);
624
- }
397
+ return formatMemoryValue(value);
625
398
  }
626
399
 
627
400
  protected safeStringify(value: unknown): string {
@@ -0,0 +1,299 @@
1
+ /*
2
+ * Hexabot — Fair Core License (FCL-1.0-ALv2)
3
+ * Copyright (c) 2026 Hexastack.
4
+ * Full terms: see LICENSE.md.
5
+ */
6
+
7
+ import {
8
+ IncomingMessageType,
9
+ Message,
10
+ StdIncomingMessage,
11
+ StdOutgoingMessage,
12
+ Thread,
13
+ } from '@hexabot-ai/types';
14
+ import { ModelMessage } from 'ai';
15
+
16
+ import { RuntimeBindings } from '@/bindings/runtime-bindings';
17
+ import { WorkflowRuntimeContext } from '@/workflow/contexts/workflow-runtime.context';
18
+
19
+ import {
20
+ AiPromptInput,
21
+ DEFAULT_AI_MESSAGES_LIMIT,
22
+ DEFAULT_AI_PROMPT,
23
+ } from './ai-schemas';
24
+
25
+ /**
26
+ * Shared prompt + working-memory builders used by every AI-flavored action
27
+ * (`ai_agent`, `generate_*`, and the sandboxed `ai_coding_agent`) so they build
28
+ * the model request from a direct prompt or conversation history, and inject
29
+ * selected memory, in exactly the same way. `AiBaseAction` delegates to these;
30
+ * actions that do not extend it (the coding agent runs on a different engine)
31
+ * import them directly.
32
+ */
33
+
34
+ export type PromptPayload =
35
+ | { prompt: string; system?: string }
36
+ | { messages: ModelMessage[]; system?: string };
37
+
38
+ export function resolveMessageContent(
39
+ payload: StdOutgoingMessage | StdIncomingMessage,
40
+ ): string | undefined {
41
+ if (!payload) {
42
+ return undefined;
43
+ }
44
+
45
+ const data = payload.data as Record<string, unknown>;
46
+
47
+ if (typeof data.text === 'string') {
48
+ return data.text;
49
+ }
50
+
51
+ if (typeof data.serializedText === 'string') {
52
+ return data.serializedText;
53
+ }
54
+
55
+ if (
56
+ payload.type === IncomingMessageType.location &&
57
+ typeof data.coordinates === 'object' &&
58
+ data.coordinates !== null &&
59
+ 'lat' in data.coordinates &&
60
+ 'lon' in data.coordinates
61
+ ) {
62
+ const { lat, lon } = data.coordinates as { lat: number; lon: number };
63
+
64
+ return `location:${lat},${lon}`;
65
+ }
66
+
67
+ try {
68
+ return JSON.stringify(data);
69
+ } catch {
70
+ return String(payload);
71
+ }
72
+ }
73
+
74
+ export function normalizeMessagesForModel(
75
+ messages: Message[],
76
+ subscriberId: string,
77
+ ): ModelMessage[] {
78
+ type ConversationMessage = {
79
+ role: Extract<ModelMessage['role'], 'user' | 'assistant'>;
80
+ content: string;
81
+ createdAt: Date;
82
+ };
83
+
84
+ const normalized: ConversationMessage[] = messages
85
+ .map((message) => {
86
+ const content = resolveMessageContent(message.message);
87
+
88
+ if (!content) {
89
+ return undefined;
90
+ }
91
+
92
+ const role: ConversationMessage['role'] =
93
+ message.sender === subscriberId ? 'user' : 'assistant';
94
+
95
+ return {
96
+ role,
97
+ content,
98
+ createdAt: message.createdAt,
99
+ };
100
+ })
101
+ .filter((message): message is ConversationMessage => Boolean(message));
102
+
103
+ return normalized
104
+ .sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
105
+ .map(({ role, content }) => ({ role, content }));
106
+ }
107
+
108
+ export function formatMemoryValue(value: unknown): string {
109
+ if (typeof value === 'string') {
110
+ return value;
111
+ }
112
+
113
+ if (typeof value === 'number' || typeof value === 'boolean') {
114
+ return String(value);
115
+ }
116
+
117
+ if (value === null) {
118
+ return 'null';
119
+ }
120
+
121
+ if (value instanceof Date) {
122
+ return value.toISOString();
123
+ }
124
+
125
+ try {
126
+ return JSON.stringify(value);
127
+ } catch {
128
+ return String(value);
129
+ }
130
+ }
131
+
132
+ export function buildMemoryPrompt(
133
+ context: WorkflowRuntimeContext,
134
+ selectedMemorySlugs: string[] = [],
135
+ ): string | undefined {
136
+ if (selectedMemorySlugs.length === 0) {
137
+ return undefined;
138
+ }
139
+
140
+ const memoryStore = context.memoryStore;
141
+ if (!memoryStore) {
142
+ return undefined;
143
+ }
144
+
145
+ const { definitionCache, instances } = memoryStore;
146
+ if (!definitionCache || definitionCache.size === 0) {
147
+ return undefined;
148
+ }
149
+
150
+ const sections: string[] = [];
151
+ const selectedSlugSet = new Set(selectedMemorySlugs);
152
+ for (const [slug, definition] of definitionCache.entries()) {
153
+ if (!selectedSlugSet.has(slug)) {
154
+ continue;
155
+ }
156
+
157
+ const instance = instances[slug];
158
+ if (!instance) {
159
+ continue;
160
+ }
161
+
162
+ const lines: string[] = [];
163
+ for (const field of instance.fields({ includeAdditional: true })) {
164
+ if (field.value === undefined) {
165
+ continue;
166
+ }
167
+
168
+ const label = (field.title ?? field.name).trim();
169
+ const value = formatMemoryValue(field.value);
170
+ lines.push(`- ${label}: ${value}`);
171
+ }
172
+
173
+ if (lines.length === 0) {
174
+ continue;
175
+ }
176
+
177
+ sections.push(`## ${definition.name}\n${lines.join('\n')}`);
178
+ }
179
+
180
+ if (sections.length === 0) {
181
+ return undefined;
182
+ }
183
+
184
+ return `# Working Memory\n${sections.join('\n\n')}`;
185
+ }
186
+
187
+ export function resolveMemoryBindingSlugs(
188
+ context: WorkflowRuntimeContext,
189
+ memoryBindings?: RuntimeBindings['memory'],
190
+ ): string[] {
191
+ if (!memoryBindings || Object.keys(memoryBindings).length === 0) {
192
+ return [];
193
+ }
194
+
195
+ const memoryStore = context.memoryStore;
196
+ if (!memoryStore) {
197
+ return [];
198
+ }
199
+
200
+ const idToSlug = new Map<string, string>();
201
+ for (const [slug, definition] of memoryStore.definitionCache.entries()) {
202
+ if (definition.id) {
203
+ idToSlug.set(definition.id, slug);
204
+ }
205
+ }
206
+
207
+ const selectedSlugs = new Set<string>();
208
+ for (const [defName, binding] of Object.entries(memoryBindings)) {
209
+ const definitionId = binding.settings?.definition_id;
210
+ const slug =
211
+ typeof definitionId === 'string' ? idToSlug.get(definitionId) : undefined;
212
+ if (!slug) {
213
+ throw new Error(
214
+ `Unable to resolve memory definition "${String(definitionId)}" from bindings.memory.${defName}.settings.definition_id.`,
215
+ );
216
+ }
217
+
218
+ selectedSlugs.add(slug);
219
+ }
220
+
221
+ return Array.from(selectedSlugs);
222
+ }
223
+
224
+ /**
225
+ * Merge an optional working-memory section into the system prompt so selected
226
+ * memory is presented consistently regardless of the action's input mode.
227
+ */
228
+ export function mergeMemoryIntoSystem(
229
+ context: WorkflowRuntimeContext,
230
+ system: string | undefined,
231
+ selectedMemorySlugs: string[] = [],
232
+ ): string | undefined {
233
+ const memoryPrompt = buildMemoryPrompt(context, selectedMemorySlugs);
234
+ if (!memoryPrompt) {
235
+ return system;
236
+ }
237
+
238
+ return system ? `${system}\n\n${memoryPrompt}` : memoryPrompt;
239
+ }
240
+
241
+ export async function buildPrompt(
242
+ input: AiPromptInput,
243
+ context: WorkflowRuntimeContext,
244
+ selectedMemorySlugs: string[] = [],
245
+ ): Promise<PromptPayload> {
246
+ const system = mergeMemoryIntoSystem(
247
+ context,
248
+ input.system,
249
+ selectedMemorySlugs,
250
+ );
251
+
252
+ if (input.input_mode === 'prompt') {
253
+ const prompt = input.prompt ?? DEFAULT_AI_PROMPT;
254
+
255
+ return { prompt, system };
256
+ }
257
+
258
+ if (input.input_mode === 'history') {
259
+ const messagesLimit = input.messages_limit ?? DEFAULT_AI_MESSAGES_LIMIT;
260
+
261
+ if (messagesLimit < 1) {
262
+ throw new Error(
263
+ 'Input mode "history" requires a positive "messages_limit" value.',
264
+ );
265
+ }
266
+
267
+ const subscriberId = context.initiatorId;
268
+ if (!subscriberId) {
269
+ throw new Error(
270
+ 'A subscriber id is required to load previous messages for this action.',
271
+ );
272
+ }
273
+ const threadId = context.threadId;
274
+ if (!threadId) {
275
+ throw new Error(
276
+ 'A thread id is required to load previous messages for this action.',
277
+ );
278
+ }
279
+
280
+ const messageService = context.services.message;
281
+ if (!messageService) {
282
+ throw new Error(
283
+ 'Message service is unavailable in the workflow context.',
284
+ );
285
+ }
286
+
287
+ const history = await messageService.findLastMessages(
288
+ { id: threadId } as Thread,
289
+ messagesLimit,
290
+ );
291
+ const messages = normalizeMessagesForModel(history, subscriberId);
292
+
293
+ return { messages, system };
294
+ }
295
+
296
+ throw new Error(
297
+ 'An "input_mode" of either "prompt" or "history" is required to build the model request.',
298
+ );
299
+ }
@@ -20,6 +20,10 @@ export * from './channels/web/inbound';
20
20
 
21
21
  export * from './helpers/local-storage/index.helper';
22
22
 
23
+ export * from './actions/ai/ai-prompt.helpers';
24
+
25
+ export * from './actions/ai/ai-schemas';
26
+
23
27
  export * from './actions/ai/model.binding';
24
28
 
25
29
  export * from './actions/ai/tools.binding';
@@ -45,7 +45,7 @@ export const LICENSE_QUOTA_LIMITS: Record<
45
45
  unlimited: 25,
46
46
  },
47
47
  workflows: {
48
- community: 3,
48
+ community: 10,
49
49
  starter: 25,
50
50
  pro: 150,
51
51
  unlimited: null,