@acorex/modules 21.0.0-next.54 → 21.0.0-next.55

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 (27) hide show
  1. package/ai-management/README.md +3 -2
  2. package/fesm2022/{acorex-modules-ai-management-agent.entity-D6-0_Ms3.mjs → acorex-modules-ai-management-agent.entity-X51GLTdi.mjs} +69 -6
  3. package/fesm2022/acorex-modules-ai-management-agent.entity-X51GLTdi.mjs.map +1 -0
  4. package/fesm2022/{acorex-modules-ai-management-assist.entity-CnyoIO-Z.mjs → acorex-modules-ai-management-assist.entity-2h5KE9UH.mjs} +58 -4
  5. package/fesm2022/acorex-modules-ai-management-assist.entity-2h5KE9UH.mjs.map +1 -0
  6. package/fesm2022/acorex-modules-ai-management-rule.entity-CQNx4QEB.mjs +121 -0
  7. package/fesm2022/acorex-modules-ai-management-rule.entity-CQNx4QEB.mjs.map +1 -0
  8. package/fesm2022/acorex-modules-ai-management.mjs +864 -945
  9. package/fesm2022/acorex-modules-ai-management.mjs.map +1 -1
  10. package/fesm2022/{acorex-modules-conversation-acorex-modules-conversation-Bnjyq-wp.mjs → acorex-modules-conversation-acorex-modules-conversation-UNhA-qi5.mjs} +71 -41
  11. package/fesm2022/acorex-modules-conversation-acorex-modules-conversation-UNhA-qi5.mjs.map +1 -0
  12. package/fesm2022/{acorex-modules-conversation-assist-delegated-agent-detail-popup.component-Df5LmYZI.mjs → acorex-modules-conversation-assist-delegated-agent-detail-popup.component-Be58gcns.mjs} +6 -6
  13. package/fesm2022/acorex-modules-conversation-assist-delegated-agent-detail-popup.component-Be58gcns.mjs.map +1 -0
  14. package/fesm2022/{acorex-modules-conversation-comments-page.component-BXI4smIr.mjs → acorex-modules-conversation-comments-page.component-3XBLeMW8.mjs} +2 -2
  15. package/fesm2022/{acorex-modules-conversation-comments-page.component-BXI4smIr.mjs.map → acorex-modules-conversation-comments-page.component-3XBLeMW8.mjs.map} +1 -1
  16. package/fesm2022/{acorex-modules-conversation-send-assist-chat-message.command-B5qJnpCK.mjs → acorex-modules-conversation-send-assist-chat-message.command-CSu-lJuu.mjs} +2 -2
  17. package/fesm2022/{acorex-modules-conversation-send-assist-chat-message.command-B5qJnpCK.mjs.map → acorex-modules-conversation-send-assist-chat-message.command-CSu-lJuu.mjs.map} +1 -1
  18. package/fesm2022/{acorex-modules-conversation-start-assist-chat.command-DI3LAaDF.mjs → acorex-modules-conversation-start-assist-chat.command-CyoncQB1.mjs} +2 -2
  19. package/fesm2022/{acorex-modules-conversation-start-assist-chat.command-DI3LAaDF.mjs.map → acorex-modules-conversation-start-assist-chat.command-CyoncQB1.mjs.map} +1 -1
  20. package/fesm2022/acorex-modules-conversation.mjs +1 -1
  21. package/package.json +2 -2
  22. package/types/acorex-modules-ai-management.d.ts +721 -582
  23. package/types/acorex-modules-conversation.d.ts +2 -0
  24. package/fesm2022/acorex-modules-ai-management-agent.entity-D6-0_Ms3.mjs.map +0 -1
  25. package/fesm2022/acorex-modules-ai-management-assist.entity-CnyoIO-Z.mjs.map +0 -1
  26. package/fesm2022/acorex-modules-conversation-acorex-modules-conversation-Bnjyq-wp.mjs.map +0 -1
  27. package/fesm2022/acorex-modules-conversation-assist-delegated-agent-detail-popup.component-Df5LmYZI.mjs.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { AXPSessionService, AXP_PERMISSION_DEFINITION_PROVIDER } from '@acorex/platform/auth';
2
- import { AXPSettingsService, AXPRegionalSetting, AXPFileStorageStatus, AXPFileStorageService, AXP_MENU_PROVIDER, AXP_SETTING_DEFINITION_PROVIDER } from '@acorex/platform/common';
2
+ import { AXPOutputContractTranscriptSegmentService, AXPSettingsService, AXPRegionalSetting, AXPFileStorageStatus, AXPFileStorageService, AXP_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES, AXP_MENU_PROVIDER, AXP_SETTING_DEFINITION_PROVIDER, AXP_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_PROVIDER } from '@acorex/platform/common';
3
3
  import * as i1$1 from '@acorex/platform/core';
4
4
  import { objectKeyValueTransforms, AXPTagProvider, AXPPlatformScope, AXPComponentSlotModule, AXP_MODULE_MANIFEST_PROVIDER, AXP_DATASOURCE_DEFINITION_PROVIDER, AXP_TAG_PROVIDER } from '@acorex/platform/core';
5
- import { AXPEntityService, AXP_ENTITY_DEFINITION_LOADER, axpCreateEntityAiToolInputDefaults } from '@acorex/platform/layout/entity';
5
+ import { AXPEntityService, AXP_ENTITY_DEFINITION_LOADER } from '@acorex/platform/layout/entity';
6
6
  import { AXPWidgetsCatalog, AXPWidgetGroupEnum, AXP_WIDGETS_EDITOR_CATEGORY, AXP_WIDGET_DEFINITION_PROVIDER } from '@acorex/platform/layout/widget-core';
7
7
  import { AXP_NAME_PROPERTY, AXP_DATA_PATH_PROPERTY, AXPWidgetsModule } from '@acorex/platform/layout/widgets';
8
8
  import * as i0 from '@angular/core';
@@ -75,6 +75,13 @@ const RootConfig = {
75
75
  titlePlural: '@ai-management:open-ai-endpoints.entities.endpoint.plural',
76
76
  icon: 'fa-light fa-plug',
77
77
  },
78
+ rule: {
79
+ name: 'AiRule',
80
+ fullName: `${config.module}.AiRule`,
81
+ title: '@ai-management:rules.entities.rule.title',
82
+ titlePlural: '@ai-management:rules.entities.rule.plural',
83
+ icon: 'fa-light fa-list-check',
84
+ },
78
85
  },
79
86
  };
80
87
  //#endregion
@@ -113,11 +120,13 @@ class AXMAiManagementEntityProvider {
113
120
  case RootConfig.entities.model.name:
114
121
  return (await import('./acorex-modules-ai-management-model.entity-DiDaXVa3.mjs')).factory();
115
122
  case RootConfig.entities.assist.name:
116
- return (await import('./acorex-modules-ai-management-assist.entity-CnyoIO-Z.mjs')).assistFactory();
123
+ return (await import('./acorex-modules-ai-management-assist.entity-2h5KE9UH.mjs')).assistFactory();
117
124
  case RootConfig.entities.agent.name:
118
- return (await import('./acorex-modules-ai-management-agent.entity-D6-0_Ms3.mjs')).agentFactory();
125
+ return (await import('./acorex-modules-ai-management-agent.entity-X51GLTdi.mjs')).agentFactory();
119
126
  case RootConfig.entities.openAiEndpoint.name:
120
127
  return (await import('./acorex-modules-ai-management-open-ai-endpoint.entity-CZLNKtl0.mjs')).openAiEndpointFactory();
128
+ case RootConfig.entities.rule.name:
129
+ return (await import('./acorex-modules-ai-management-rule.entity-CQNx4QEB.mjs')).ruleFactory();
121
130
  default:
122
131
  return null;
123
132
  }
@@ -147,6 +156,8 @@ const AXPAiManagementMenuKeys = {
147
156
  Assists: 'AiManagement:Menu:Assists',
148
157
  /** Delegated specialist agents (supervisor routing). */
149
158
  Agents: 'AiManagement:Menu:Agents',
159
+ /** Reusable system-prompt rules (Markdown fragments). */
160
+ Rules: 'AiManagement:Menu:Rules',
150
161
  };
151
162
  //#endregion
152
163
 
@@ -228,6 +239,14 @@ class AXMAiManagementMenuProvider {
228
239
  priority: 1,
229
240
  policy: aiMenuPolicy,
230
241
  },
242
+ {
243
+ name: AXPAiManagementMenuKeys.Rules,
244
+ text: '@ai-management:module.menus.rules.title',
245
+ path: this.entityService.createPath(module.name, RootConfig.entities.rule.name),
246
+ icon: RootConfig.entities.rule.icon,
247
+ priority: 3,
248
+ policy: aiMenuPolicy,
249
+ },
231
250
  ]);
232
251
  //#endregion
233
252
  }
@@ -644,30 +663,26 @@ const AI_MODEL_SPEECH_CATALOG_DATASOURCE_NAME = 'ai-management-model-catalog-spe
644
663
  * Registry name: {@link AI_MODEL_TTS_CATALOG_DATASOURCE_NAME}.
645
664
  */
646
665
  const AI_MODEL_TTS_CATALOG_DATASOURCE_NAME = 'ai-management-model-catalog-tts';
647
- /** Normalizes `modelPurposes` (and legacy `modelPurpose`) to unique non-empty slugs. */
648
- function normalizeModelPurposes(row) {
666
+ /** Reads `modelPurposes` from a catalog row (API/mock must provide the array). */
667
+ function readModelPurposes(row) {
649
668
  if (!row) {
650
- return ['chat'];
669
+ return [];
651
670
  }
652
671
  const multi = row['modelPurposes'];
653
- if (Array.isArray(multi) && multi.length > 0) {
654
- const out = multi
655
- .map((x) => String(x).trim())
656
- .filter((x) => x.length > 0);
657
- return [...new Set(out)];
658
- }
659
- const legacy = row['modelPurpose'];
660
- if (legacy == null || legacy === '') {
661
- return ['chat'];
672
+ if (!Array.isArray(multi)) {
673
+ return [];
662
674
  }
663
- return [String(legacy)];
675
+ const out = multi
676
+ .map((x) => String(x).trim())
677
+ .filter((x) => x.length > 0);
678
+ return [...new Set(out)];
664
679
  }
665
680
  function modelRowHasPurpose(row, purpose) {
666
- return normalizeModelPurposes(row).includes(purpose);
681
+ return readModelPurposes(row).includes(purpose);
667
682
  }
668
683
  /** Rows usable in chat / completion pickers: include `chat` or `vision`; not image/speech/tts-only. */
669
684
  function isChatPurposeModelRow(row) {
670
- const p = normalizeModelPurposes(row);
685
+ const p = readModelPurposes(row);
671
686
  return p.includes('chat') || p.includes('vision');
672
687
  }
673
688
  function isImagePurposeModelRow(row) {
@@ -822,6 +837,65 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
822
837
  type: Injectable
823
838
  }] });
824
839
 
840
+ //#region ---- Imports ----
841
+ //#endregion
842
+ /** Registry name for transcript-segment presets ({@link AXP_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_PROVIDER}). */
843
+ const AI_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_DATASOURCE_NAME = 'ai-management-output-contract-transcript-segment';
844
+ //#endregion
845
+ //#region ---- Provider ----
846
+ class AXMOutputContractTranscriptSegmentDataSourceDefinition {
847
+ constructor() {
848
+ this.segmentService = inject(AXPOutputContractTranscriptSegmentService);
849
+ }
850
+ items() {
851
+ const columns = [
852
+ { name: 'id', title: 'ID', datatype: 'string', type: AXPWidgetsList.Editors.TextBox },
853
+ { name: 'title', title: 'Title', datatype: 'string', type: AXPWidgetsList.Editors.TextBox },
854
+ ];
855
+ return Promise.resolve([
856
+ {
857
+ name: AI_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_DATASOURCE_NAME,
858
+ title: 'AI output contract (transcript segment)',
859
+ source: () => new AXDataSource({
860
+ key: 'id',
861
+ load: async () => {
862
+ const definitions = await this.segmentService.getAll();
863
+ const items = definitions.map((d) => ({
864
+ id: d.name,
865
+ name: d.name,
866
+ title: d.title,
867
+ ...(d.description ? { description: d.description } : {}),
868
+ }));
869
+ return { items, total: items.length };
870
+ },
871
+ byKey: async (key) => {
872
+ const definition = await this.segmentService.get(key);
873
+ if (!definition) {
874
+ return null;
875
+ }
876
+ return {
877
+ id: definition.name,
878
+ name: definition.name,
879
+ title: definition.title,
880
+ ...(definition.description ? { description: definition.description } : {}),
881
+ };
882
+ },
883
+ pageSize: 100,
884
+ }),
885
+ columns,
886
+ filters: [],
887
+ textField: { name: 'title', title: 'Title' },
888
+ valueField: { name: 'id', title: 'ID' },
889
+ },
890
+ ]);
891
+ }
892
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMOutputContractTranscriptSegmentDataSourceDefinition, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
893
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMOutputContractTranscriptSegmentDataSourceDefinition }); }
894
+ }
895
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXMOutputContractTranscriptSegmentDataSourceDefinition, decorators: [{
896
+ type: Injectable
897
+ }] });
898
+
825
899
  //#region ---- Imports ----
826
900
  //#endregion
827
901
  /**
@@ -896,6 +970,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
896
970
  type: Injectable
897
971
  }] });
898
972
 
973
+ //#region ---- Imports ----
974
+ //#endregion
899
975
  //#region ---- Structured output ----
900
976
  /** Default max completion tokens when the AiModel row has no `maxOutputTokens` and the caller omits `maxTokens`. */
901
977
  const AXPAiAssistDefaultStructuredMaxTokens = 128_000;
@@ -935,815 +1011,87 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
935
1011
 
936
1012
  //#region ---- Imports ----
937
1013
  //#endregion
938
- //#region ---- Constants ----
1014
+ //#region ---- Supervisor tool naming (display / routing) ----
939
1015
  /** Prefix for supervisor-delegated specialist tools (`${AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX}${agentId}`). */
940
1016
  const AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX = 'agent:';
941
- /** Catalog `name` of the conversation follow-up delegated agent (engine auto-invoke). */
942
- const AXPAI_CONVERSATION_FOLLOW_UP_AGENT_NAME = 'follow-up-prompt-generator';
943
- /** Default cap for main assist / direct-agent chat tool rounds (one provider call per step). */
944
- const AXPAI_ENGINE_DEFAULT_MAX_STEPS = 16;
945
- /** Default cap for delegated specialist sub-runs when the agent row has no positive `maxSteps`. */
946
- const AXPAI_DELEGATED_AGENT_DEFAULT_MAX_STEPS = 8;
947
- /** System prompt for vision OCR / document text extraction. */
948
- const AXPAI_DOCUMENT_VISION_EXTRACTION_SYSTEM = 'You are a precise document-reading assistant. Extract readable plain text from the attached file. Preserve headings and lists where obvious. If the content is unreadable, return a short explanation. Output plain text only, no markdown code fences.';
949
- /** Default user line when the caller passes an empty instruction for vision extraction. */
950
- const AXPAI_DOCUMENT_VISION_DEFAULT_USER_INSTRUCTION = 'Extract all readable text from this document. Return plain text only.';
951
- //#endregion
952
- //#region ---- Transcript helpers ----
953
- /**
954
- * Reads a stored-file artifact from a normalized tool handler body (`{ success, data }`) or a flat `data` object.
955
- */
956
- function axpAiExtractStoredFileArtifactFromUnknown(content) {
957
- if (content == null || typeof content !== 'object' || Array.isArray(content)) {
958
- return null;
959
- }
960
- const o = content;
961
- const dataRaw = o['data'];
962
- if (o['success'] === true && dataRaw != null && typeof dataRaw === 'object' && !Array.isArray(dataRaw)) {
963
- return axpAiExtractStoredFileArtifactFromDataRecord(dataRaw);
964
- }
965
- return axpAiExtractStoredFileArtifactFromDataRecord(o);
966
- }
967
- function axpAiExtractStoredFileArtifactFromDataRecord(d) {
968
- const fileId = typeof d['fileId'] === 'string' ? d['fileId'].trim() : '';
969
- const mimeType = typeof d['mimeType'] === 'string' ? d['mimeType'].trim() : '';
970
- if (!fileId || !mimeType) {
971
- return null;
972
- }
973
- const nameRaw = d['name'];
974
- const name = typeof nameRaw === 'string' && nameRaw.trim() ? nameRaw.trim() : undefined;
975
- return { fileId, mimeType, ...(name ? { name } : {}) };
976
- }
977
- /**
978
- * Returns the last stored-file artifact announced in any `tool` → `tool_result` segment (delegated sub-runs, main chat).
979
- */
980
- function axpAiExtractLastStoredFileArtifactFromChatMessages(messages) {
981
- let last = null;
982
- for (const m of messages) {
983
- if (m.role !== 'tool') {
984
- continue;
985
- }
986
- for (const r of m.responses) {
987
- if (r.type !== 'tool_result') {
988
- continue;
989
- }
990
- const hit = axpAiExtractStoredFileArtifactFromUnknown(r.content);
991
- if (hit) {
992
- last = hit;
993
- }
994
- }
995
- }
996
- return last;
997
- }
998
- function axpAiCloneChatMessages(messages) {
999
- try {
1000
- return structuredClone(messages);
1001
- }
1002
- catch {
1003
- return JSON.parse(JSON.stringify(messages));
1004
- }
1005
- }
1006
- /**
1007
- * Ensures the delegated specialist transcript replays stored files in assistant `responses`
1008
- * (not only prose that points at tool JSON).
1009
- */
1010
- function axpAiDelegatedSubTranscriptAppendFileArtifactFromToolResults(messages) {
1011
- const artifact = axpAiExtractLastStoredFileArtifactFromChatMessages(messages);
1012
- if (!artifact) {
1013
- return messages;
1014
- }
1015
- const out = axpAiCloneChatMessages(messages);
1016
- let lastAssistant = -1;
1017
- for (let i = out.length - 1; i >= 0; i--) {
1018
- if (out[i].role === 'assistant') {
1019
- lastAssistant = i;
1020
- break;
1021
- }
1022
- }
1023
- if (lastAssistant < 0) {
1024
- return out;
1025
- }
1026
- const msg = out[lastAssistant];
1027
- const duplicate = msg.responses.some((r) => r.type === 'file' && r.content.fileId === artifact.fileId && r.content.mimeType === artifact.mimeType);
1028
- if (duplicate) {
1029
- return out;
1030
- }
1031
- out[lastAssistant] = {
1032
- ...msg,
1033
- responses: [...msg.responses, { type: 'file', content: artifact }],
1034
- };
1035
- return out;
1036
- }
1037
- function axpAiIsDelegatedAgentResultSegment(value) {
1038
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
1039
- return false;
1040
- }
1041
- const o = value;
1042
- const t = o['type'];
1043
- if (t === 'text' && typeof o['content'] === 'string') {
1044
- return true;
1045
- }
1046
- if (t === 'think' && typeof o['content'] === 'string') {
1047
- return true;
1048
- }
1049
- if (t === 'node') {
1050
- return 'content' in o;
1051
- }
1052
- if (t === 'file' && o['content'] != null && typeof o['content'] === 'object' && !Array.isArray(o['content'])) {
1053
- const c = o['content'];
1054
- return typeof c['fileId'] === 'string' && typeof c['mimeType'] === 'string';
1055
- }
1056
- if (t === 'followUp' && Array.isArray(o['content'])) {
1057
- return axpAiNormalizeFollowUpContent(o['content']).length > 0;
1058
- }
1059
- return false;
1060
- }
1061
- /**
1062
- * Parses a JSON-like array of specialist segments (e.g. from persisted tool output). Invalid entries are skipped.
1063
- */
1064
- function axpAiParseDelegatedAgentResultSegmentsFromUnknown(raw) {
1065
- if (!Array.isArray(raw)) {
1066
- return [];
1067
- }
1068
- const out = [];
1069
- for (const item of raw) {
1070
- if (axpAiIsDelegatedAgentResultSegment(item)) {
1071
- out.push(item);
1072
- }
1073
- }
1074
- return out;
1075
- }
1076
- /**
1077
- * Collects ordered {@link AXPAiDelegatedAgentResultSegment} from a delegated sub-run transcript
1078
- * (excludes {@link AXPAiChatMessage.delegatedReflection} lines).
1079
- */
1080
- function axpAiCollectDelegatedAgentOutcomeResponses(messages) {
1081
- const out = [];
1082
- for (const m of messages) {
1083
- if (m.role !== 'assistant' || axpAiChatMessageIsDelegatedReflectionExcluded(m)) {
1084
- continue;
1085
- }
1086
- for (const r of m.responses) {
1087
- if (r.type === 'text' || r.type === 'think' || r.type === 'file' || r.type === 'node' || r.type === 'followUp') {
1088
- out.push(r);
1089
- }
1090
- }
1091
- }
1092
- return out;
1093
- }
1094
- /**
1095
- * Plain text from {@link AXPAiDelegatedAgentResultSegment} (text + think only; for provider hints / markdown fallbacks).
1096
- */
1097
- function axpAiDelegatedAgentOutcomeResponsesPlainText(segments) {
1098
- const parts = [];
1099
- for (const s of segments) {
1100
- if (s.type === 'text' || s.type === 'think') {
1101
- const t = s.content.trim();
1102
- if (t.length > 0) {
1103
- parts.push(t);
1104
- }
1105
- }
1106
- }
1107
- return parts.join('\n\n').trim();
1108
- }
1109
- function axpAiChatTextMessage(role, text) {
1110
- return {
1111
- role,
1112
- responses: [{ type: 'text', content: text }],
1113
- };
1114
- }
1115
- function axpAiChatUserMessage(responses) {
1116
- return {
1117
- role: 'user',
1118
- responses,
1119
- };
1120
- }
1121
- function axpAiChatMessageGetText(message) {
1122
- const parts = [];
1123
- for (const r of message.responses) {
1124
- if (r.type === 'text') {
1125
- parts.push(r.content);
1126
- }
1127
- else if (r.type === 'node' || r.type === 'followUp') {
1128
- try {
1129
- parts.push(JSON.stringify({ type: r.type, content: r.content }));
1130
- }
1131
- catch {
1132
- parts.push(`[${r.type}]`);
1133
- }
1134
- }
1135
- }
1136
- return parts.join('\n\n');
1137
- }
1138
- /**
1139
- * Planning-only assistant output is always internal reasoning. Provider text may be untagged;
1140
- * the engine maps `text` responses to `think` so the transcript and UI are correct without
1141
- * requiring `<think>` / `<think>` in the model output.
1142
- */
1143
- function axpAiCoercePlanningAssistantTextToThink(planningMessages) {
1144
- return planningMessages.map((m) => {
1145
- if (m.role !== 'assistant') {
1146
- return m;
1147
- }
1148
- return {
1149
- ...m,
1150
- responses: m.responses.map((r) => {
1151
- if (r.type === 'text') {
1152
- return { type: 'think', content: r.content };
1153
- }
1154
- return r;
1155
- }),
1156
- };
1157
- });
1158
- }
1159
- //#region ---- Assistant plain text: embedded node JSON envelopes ----
1160
- function isAssistantNodeTypeEnvelope(o) {
1161
- if (!o || typeof o !== 'object' || Array.isArray(o)) {
1162
- return false;
1163
- }
1164
- const r = o;
1165
- return r['type'] === 'node' && 'content' in r;
1166
- }
1167
- function isAssistantFollowUpTypeEnvelope(o) {
1168
- return axpAiIsDelegatedAgentResultSegment(o) && o.type === 'followUp';
1169
- }
1170
- /**
1171
- * Parses one balanced `{ ... }` JSON object starting at `start` (must point at `{`).
1172
- */
1173
- function tryParseBalancedJsonObjectAt$1(s, start) {
1174
- if (s[start] !== '{') {
1175
- return null;
1176
- }
1177
- let depth = 0;
1178
- let inString = false;
1179
- let escape = false;
1180
- for (let i = start; i < s.length; i++) {
1181
- const c = s[i];
1182
- if (inString) {
1183
- if (escape) {
1184
- escape = false;
1185
- continue;
1186
- }
1187
- if (c === '\\') {
1188
- escape = true;
1189
- continue;
1190
- }
1191
- if (c === '"') {
1192
- inString = false;
1193
- }
1194
- continue;
1195
- }
1196
- if (c === '"') {
1197
- inString = true;
1198
- continue;
1199
- }
1200
- if (c === '{') {
1201
- depth++;
1202
- }
1203
- else if (c === '}') {
1204
- depth--;
1205
- if (depth === 0) {
1206
- const slice = s.slice(start, i + 1);
1207
- try {
1208
- return { value: JSON.parse(slice), end: i + 1 };
1209
- }
1210
- catch {
1211
- return null;
1212
- }
1213
- }
1214
- }
1215
- }
1216
- return null;
1217
- }
1218
- function mergeAdjacentAssistantTextResponses(seq) {
1219
- const out = [];
1220
- for (const r of seq) {
1221
- if (r.type !== 'text') {
1222
- out.push(r);
1223
- continue;
1224
- }
1225
- const prev = out[out.length - 1];
1226
- const v = r.content.trim();
1227
- if (!v.length) {
1228
- continue;
1229
- }
1230
- if (prev && prev.type === 'text') {
1231
- const joined = `${prev.content}\n\n${v}`.trim();
1232
- out[out.length - 1] = { type: 'text', content: joined };
1233
- }
1234
- else {
1235
- out.push({ type: 'text', content: v });
1236
- }
1237
- }
1238
- return out;
1239
- }
1240
- /**
1241
- * Splits raw assistant prose into ordered `text` / `node` responses by detecting JSON objects
1242
- * shaped like `{ "type": "node", "content": <AXPWidgetNode> }` anywhere in the string.
1243
- */
1244
- /** Removes markdown ```json fences so specialist-only JSON replies still parse as envelopes. */
1245
- function axpAiStripMarkdownJsonFences(raw) {
1246
- return raw.replace(/```(?:json)?\s*([\s\S]*?)```/gi, (_match, inner) => inner.trim());
1247
- }
1248
- function axpAiExpandPlainAssistantTextToResponses(raw) {
1249
- const normalized = axpAiStripMarkdownJsonFences(raw);
1250
- const parts = [];
1251
- let i = 0;
1252
- const pushText = (value) => {
1253
- const v = value.trim();
1254
- if (v.length > 0) {
1255
- parts.push({ type: 'text', content: v });
1256
- }
1257
- };
1258
- while (i < normalized.length) {
1259
- const braceIdx = normalized.indexOf('{', i);
1260
- if (braceIdx < 0) {
1261
- pushText(normalized.slice(i));
1262
- break;
1263
- }
1264
- pushText(normalized.slice(i, braceIdx));
1265
- const parsed = tryParseBalancedJsonObjectAt$1(normalized, braceIdx);
1266
- if (parsed && isAssistantFollowUpTypeEnvelope(parsed.value)) {
1267
- const normalized = axpAiNormalizeFollowUpContent(parsed.value.content);
1268
- if (normalized.length > 0) {
1269
- parts.push({ type: 'followUp', content: normalized });
1270
- }
1271
- i = parsed.end;
1272
- }
1273
- else if (parsed && isAssistantNodeTypeEnvelope(parsed.value)) {
1274
- parts.push({ type: 'node', content: parsed.value.content });
1275
- i = parsed.end;
1276
- }
1277
- else if (parsed) {
1278
- pushText(normalized.slice(braceIdx, parsed.end));
1279
- i = parsed.end;
1280
- }
1281
- else {
1282
- pushText(normalized.slice(braceIdx, braceIdx + 1));
1283
- i = braceIdx + 1;
1284
- }
1285
- }
1286
- return mergeAdjacentAssistantTextResponses(parts);
1287
- }
1288
- //#endregion
1289
- function axpAiChatMessagesFromProviderAssistant(text, toolCalls) {
1290
- const normalizeThinkingTags = (raw) => raw
1291
- .replace(/&lt;(\/?redacted_thinking)&gt;/gi, '<$1>')
1292
- .replace(/&lt;(\/?think)&gt;/gi, '<$1>');
1293
- const splitAssistantResponses = (raw) => {
1294
- const normalized = normalizeThinkingTags(raw ?? '');
1295
- if (!normalized) {
1296
- return [];
1297
- }
1298
- const out = [];
1299
- const appendPlainSlice = (value) => {
1300
- out.push(...axpAiExpandPlainAssistantTextToResponses(value));
1301
- };
1302
- const pushThink = (value) => {
1303
- const v = value.trim();
1304
- if (v.length > 0) {
1305
- out.push({ type: 'think', content: v });
1306
- }
1307
- };
1308
- const thinkRe = /<(redacted_thinking|think)>([\s\S]*?)<\/\1>/gi;
1309
- let last = 0;
1310
- let m;
1311
- while ((m = thinkRe.exec(normalized)) !== null) {
1312
- appendPlainSlice(normalized.slice(last, m.index));
1313
- pushThink(m[2] ?? '');
1314
- last = m.index + m[0].length;
1315
- }
1316
- appendPlainSlice(normalized.slice(last));
1317
- return mergeAdjacentAssistantTextResponses(out);
1318
- };
1319
- const parsedResponses = splitAssistantResponses(text);
1320
- const calls = toolCalls ?? [];
1321
- const out = [];
1322
- if (parsedResponses.length > 0) {
1323
- out.push({
1324
- role: 'assistant',
1325
- responses: parsedResponses,
1326
- });
1327
- }
1328
- for (const tc of calls) {
1329
- const isDelegatedAgent = axpAiParseSupervisorAgentToolName(tc.name) != null;
1330
- out.push({
1331
- role: 'assistant',
1332
- responses: [
1333
- isDelegatedAgent
1334
- ? {
1335
- type: 'agent',
1336
- content: {
1337
- command: tc.name,
1338
- arguments: tc.arguments,
1339
- },
1340
- callId: tc.id,
1341
- }
1342
- : {
1343
- type: 'tool',
1344
- content: {
1345
- command: tc.name,
1346
- arguments: tc.arguments,
1347
- },
1348
- callId: tc.id,
1349
- },
1350
- ],
1351
- });
1352
- }
1353
- if (out.length === 0) {
1354
- out.push(axpAiChatTextMessage('assistant', ''));
1355
- }
1356
- return out;
1357
- }
1358
- function axpAiToolCallFromAssistantToolMessage(message) {
1359
- if (message.role !== 'assistant') {
1360
- return null;
1361
- }
1362
- for (const r of message.responses) {
1363
- if (r.type === 'tool' || r.type === 'agent') {
1364
- const callId = r.callId?.trim() ?? '';
1365
- const command = r.content.command?.trim() ?? '';
1366
- if (!command) {
1367
- return null;
1368
- }
1369
- return {
1370
- id: callId || `call_${command}`,
1371
- name: command,
1372
- arguments: r.content.arguments,
1373
- };
1374
- }
1375
- }
1376
- return null;
1377
- }
1378
- function axpAiChatToolResultMessage(callId, content, toolName) {
1379
- const id = callId.trim();
1380
- return {
1381
- role: 'tool',
1382
- ...(toolName?.trim() ? { name: toolName.trim() } : {}),
1383
- responses: [
1384
- {
1385
- type: 'tool_result',
1386
- callId: id,
1387
- content,
1388
- },
1389
- ],
1390
- };
1391
- }
1392
- function axpAiChatAgentResultMessage(callId, content, toolName) {
1393
- const id = callId.trim();
1394
- return {
1395
- role: 'tool',
1396
- ...(toolName?.trim() ? { name: toolName.trim() } : {}),
1397
- responses: [
1398
- {
1399
- type: 'agent_result',
1400
- callId: id,
1401
- content,
1402
- },
1403
- ],
1404
- };
1405
- }
1406
- function axpAiNormalizeHandlerResultToToolBody(handlerResult) {
1407
- if (handlerResult === undefined || handlerResult === null) {
1408
- return { success: false, error: 'Tool returned no result.' };
1409
- }
1410
- if (typeof handlerResult === 'string') {
1411
- const t = handlerResult.trim();
1412
- if (!t) {
1413
- return { success: false, error: 'Tool returned an empty string.' };
1414
- }
1415
- try {
1416
- return JSON.parse(t);
1417
- }
1418
- catch {
1419
- return { success: true, data: handlerResult };
1420
- }
1421
- }
1422
- if (typeof handlerResult === 'object' && !Array.isArray(handlerResult)) {
1423
- return handlerResult;
1424
- }
1425
- return { success: true, data: handlerResult };
1426
- }
1427
- function axpAiChatToolOrAgentResultBodyJson(message) {
1428
- const tr = message.responses.find((r) => r.type === 'tool_result' || r.type === 'agent_result');
1429
- if (!tr || (tr.type !== 'tool_result' && tr.type !== 'agent_result')) {
1430
- return '';
1431
- }
1432
- try {
1433
- return JSON.stringify(tr.content);
1434
- }
1435
- catch {
1436
- return String(tr.content);
1437
- }
1438
- }
1439
- /**
1440
- * Compact JSON string of a tool-role message’s `tool_result` body (transcript tail / logs).
1441
- */
1442
- function axpAiChatToolResultBodyJson(message) {
1443
- return axpAiChatToolOrAgentResultBodyJson(message);
1444
- }
1445
- //#endregion
1446
- //#region ---- Supervisor delegated-agent helpers ----
1447
- function axpAiSupervisorAgentToolName(agentCatalogId) {
1448
- return `${AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX}${agentCatalogId.trim()}`;
1449
- }
1450
1017
  function axpAiParseSupervisorAgentToolName(toolName) {
1451
1018
  const n = toolName.trim();
1452
1019
  if (!n.startsWith(AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX)) {
1453
- return null;
1454
- }
1455
- const id = n.slice(AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX.length).trim();
1456
- return id.length > 0 ? id : null;
1457
- }
1458
- //#endregion
1459
- //#region ---- Assistant message transforms (delegated sub-run) ----
1460
- function axpAiMergePlainAssistantTextTurn(messages) {
1461
- const chunks = [];
1462
- for (const m of messages) {
1463
- if (m.role !== 'assistant') {
1464
- continue;
1465
- }
1466
- const textParts = m.responses
1467
- .filter((r) => r.type === 'text')
1468
- .map((r) => r.content.trim())
1469
- .filter((s) => s.length > 0);
1470
- const thinkParts = m.responses
1471
- .filter((r) => r.type === 'think')
1472
- .map((r) => r.content.trim())
1473
- .filter((s) => s.length > 0);
1474
- const serialized = axpAiChatMessageGetText(m).trim();
1475
- const t = textParts.join('\n\n').trim() ||
1476
- thinkParts.join('\n\n').trim() ||
1477
- serialized;
1478
- if (t.length > 0) {
1479
- chunks.push(t);
1480
- }
1481
- }
1482
- const joined = chunks.join('\n\n').trim();
1483
- if (!joined) {
1484
- return [];
1485
- }
1486
- return axpAiChatMessagesFromProviderAssistant(joined, undefined);
1487
- }
1488
- //#region ---- Follow-up normalization ----
1489
- /** Normalizes one follow-up chip command object from model JSON. */
1490
- function axpAiNormalizeFollowUpCommand(raw) {
1491
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
1492
- return null;
1493
- }
1494
- const o = raw;
1495
- const name = typeof o['name'] === 'string' ? o['name'].trim() : '';
1496
- if (!name) {
1497
- return null;
1498
- }
1499
- if (!('options' in o)) {
1500
- return { name };
1501
- }
1502
- return { name, options: o['options'] };
1503
- }
1504
- /** Normalizes one follow-up chip row (`text` + `command`). */
1505
- function axpAiNormalizeFollowUpItem(raw) {
1506
- if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
1507
- return null;
1508
- }
1509
- const o = raw;
1510
- const text = typeof o['text'] === 'string' ? o['text'].trim() : '';
1511
- const command = axpAiNormalizeFollowUpCommand(o['command']);
1512
- if (!text || !command) {
1513
- return null;
1514
- }
1515
- return { text, command };
1516
- }
1517
- /** Normalizes follow-up `content` arrays; invalid rows are skipped. */
1518
- function axpAiNormalizeFollowUpContent(raw) {
1519
- if (!Array.isArray(raw)) {
1520
- return [];
1521
- }
1522
- const out = [];
1523
- for (const item of raw) {
1524
- const row = axpAiNormalizeFollowUpItem(item);
1525
- if (row) {
1526
- out.push(row);
1527
- }
1528
- }
1529
- return out;
1530
- }
1531
- //#endregion
1532
- //#region ---- Follow-up auto-invoke (supervisor turn) ----
1533
- function axpAiLastUserTranscriptTextLocal(messages) {
1534
- for (let i = messages.length - 1; i >= 0; i--) {
1535
- if (messages[i].role === 'user') {
1536
- return axpAiChatMessageGetText(messages[i]).trim();
1537
- }
1538
- }
1539
- return '';
1540
- }
1541
- function axpAiIndexOfLastUserMessage(messages) {
1542
- for (let i = messages.length - 1; i >= 0; i--) {
1543
- if (messages[i].role === 'user') {
1544
- return i;
1545
- }
1020
+ return null;
1546
1021
  }
1547
- return -1;
1022
+ const id = n.slice(AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX.length).trim();
1023
+ return id.length > 0 ? id : null;
1548
1024
  }
1549
- function axpAiTranscriptTailSinceLastUser(messages) {
1550
- const idx = axpAiIndexOfLastUserMessage(messages);
1551
- return idx < 0 ? messages : messages.slice(idx);
1025
+ //#endregion
1026
+ //#region ---- Message builders (outbound / debug) ----
1027
+ function axpAiChatTextMessage(role, text) {
1028
+ return {
1029
+ role,
1030
+ responses: [{ type: 'text', content: text }],
1031
+ };
1552
1032
  }
1553
- /**
1554
- * Whether the current user turn already has a usable {@code followUp} segment from the follow-up specialist.
1555
- */
1556
- function axpAiTranscriptAlreadyHasFollowUpInTurn(messages, followUpAgentId) {
1557
- const tail = axpAiTranscriptTailSinceLastUser(messages);
1558
- for (const m of tail) {
1559
- if (m.role === 'assistant' && m.responses.some((r) => r.type === 'followUp')) {
1560
- return true;
1561
- }
1562
- if (m.role !== 'tool') {
1563
- continue;
1033
+ function axpAiChatUserMessage(responses) {
1034
+ return {
1035
+ role: 'user',
1036
+ responses,
1037
+ };
1038
+ }
1039
+ //#endregion
1040
+ //#region ---- Transcript read helpers (display only) ----
1041
+ function axpAiChatMessageGetText(message) {
1042
+ const parts = [];
1043
+ for (const r of message.responses) {
1044
+ if (r.type === 'text') {
1045
+ parts.push(r.content);
1564
1046
  }
1565
- for (const r of m.responses) {
1566
- if (r.type !== 'agent_result') {
1567
- continue;
1568
- }
1569
- const payload = r.content;
1570
- if (payload.success !== true || payload.data?.responses == null) {
1571
- continue;
1572
- }
1573
- const agentId = payload.data.agentId?.trim() ?? '';
1574
- const agentName = payload.data.agentName?.trim() ?? '';
1575
- const isFollowUpAgent = agentId === followUpAgentId.trim() || agentName === AXPAI_CONVERSATION_FOLLOW_UP_AGENT_NAME;
1576
- if (!isFollowUpAgent) {
1577
- continue;
1047
+ else if (r.type === 'node' || r.type === 'followUp') {
1048
+ try {
1049
+ parts.push(JSON.stringify({ type: r.type, content: r.content }));
1578
1050
  }
1579
- if (payload.data.responses.some((s) => s.type === 'followUp')) {
1580
- return true;
1051
+ catch {
1052
+ parts.push(`[${r.type}]`);
1581
1053
  }
1582
1054
  }
1583
1055
  }
1584
- return false;
1056
+ return parts.join('\n\n');
1585
1057
  }
1586
- const AXPAI_GREETING_ONLY_USER_RE = /^(?:hi|hello|hey|howdy|thanks|thank\s*you|ok(?:ay)?|bye|goodbye|سلام|درود|وقت\s*بخیر|ممنون|متشکرم|مرسی)(?:[!.\s?،]*)*$/iu;
1587
- /**
1588
- * Engine may auto-call the follow-up specialist after the supervisor finishes, unless the turn is trivial.
1589
- */
1590
- function axpAiShouldAutoInvokeFollowUpAgent(messages) {
1591
- const userText = axpAiLastUserTranscriptTextLocal(messages).trim();
1592
- if (userText.length === 0) {
1593
- return false;
1594
- }
1595
- if (AXPAI_GREETING_ONLY_USER_RE.test(userText)) {
1596
- return false;
1597
- }
1598
- const tail = axpAiTranscriptTailSinceLastUser(messages);
1599
- for (const m of tail) {
1600
- if (m.role !== 'assistant' || axpAiChatMessageIsDelegatedReflectionExcluded(m)) {
1601
- continue;
1602
- }
1603
- const plain = m.responses
1604
- .filter((r) => r.type === 'text')
1605
- .map((r) => r.content.trim())
1606
- .join('\n')
1607
- .trim();
1608
- const serialized = axpAiChatMessageGetText(m).trim();
1609
- if (plain.length > 0 || serialized.length > 0) {
1610
- return true;
1611
- }
1612
- if (m.responses.some((r) => r.type === 'node' || r.type === 'file')) {
1613
- return true;
1614
- }
1615
- }
1616
- return false;
1058
+ function axpAiChatMessageIsDelegatedReflectionExcluded(message) {
1059
+ return message.delegatedReflection === true;
1617
1060
  }
1618
- /** Builds the `query` argument for an automatic follow-up specialist sub-run. */
1619
- function axpAiBuildAutoFollowUpAgentQuery(messages) {
1620
- const userText = axpAiLastUserTranscriptTextLocal(messages);
1621
- const tail = axpAiTranscriptTailSinceLastUser(messages);
1622
- const answerParts = [];
1623
- for (const m of tail) {
1624
- if (m.role !== 'assistant' || axpAiChatMessageIsDelegatedReflectionExcluded(m)) {
1625
- continue;
1626
- }
1627
- const t = axpAiChatMessageGetText(m).trim();
1628
- if (t.length > 0) {
1629
- answerParts.push(t);
1630
- }
1061
+ /** Reads typed specialist segments from a normalized {@code agent_result} payload. */
1062
+ function axpAiDelegatedAgentResultSegments(payload) {
1063
+ if (payload.success !== true || !Array.isArray(payload.data?.responses)) {
1064
+ return [];
1631
1065
  }
1632
- const answer = answerParts.join('\n\n').trim().slice(0, 4000);
1633
- return [
1634
- 'Propose 2–5 short next-step chips for the user after this exchange.',
1635
- '',
1636
- `User message: ${userText}`,
1637
- '',
1638
- answer.length > 0 ? `Assistant answer so far:\n${answer}` : 'Assistant answer so far: (none yet)',
1639
- ].join('\n');
1066
+ return payload.data.responses;
1640
1067
  }
1641
- //#endregion
1642
- /**
1643
- * Appends delegated {@code followUp} / {@code node} / {@code file} segments from tool {@code agent_result}
1644
- * lines onto the final supervisor assistant message when the model omitted them.
1645
- */
1646
- function axpAiReconcileSupervisorFinalAssistantWithDelegatedOutcomes(messages) {
1647
- let lastAssistantIdx = -1;
1648
- for (let i = messages.length - 1; i >= 0; i--) {
1649
- const m = messages[i];
1650
- if (m.role !== 'assistant' || axpAiChatMessageIsDelegatedReflectionExcluded(m)) {
1651
- continue;
1652
- }
1653
- if (m.responses.some((r) => r.type === 'text' || r.type === 'node' || r.type === 'file' || r.type === 'followUp')) {
1654
- lastAssistantIdx = i;
1655
- break;
1656
- }
1657
- }
1658
- if (lastAssistantIdx < 0) {
1659
- return messages;
1660
- }
1661
- const delegatedStructured = [];
1662
- for (const m of messages) {
1663
- if (m.role !== 'tool') {
1664
- continue;
1665
- }
1666
- for (const r of m.responses) {
1667
- if (r.type !== 'agent_result') {
1668
- continue;
1669
- }
1670
- const payload = r.content;
1671
- if (payload.success !== true || payload.data?.responses == null) {
1672
- continue;
1673
- }
1674
- for (const seg of payload.data.responses) {
1675
- if (seg.type === 'followUp' || seg.type === 'node' || seg.type === 'file') {
1676
- delegatedStructured.push(seg);
1677
- }
1068
+ /** Plain text from specialist segments (text + think) for debug / preview UI. */
1069
+ function axpAiDelegatedAgentOutcomeResponsesPlainText(segments) {
1070
+ const parts = [];
1071
+ for (const s of segments) {
1072
+ if (s.type === 'text' || s.type === 'think') {
1073
+ const t = s.content.trim();
1074
+ if (t.length > 0) {
1075
+ parts.push(t);
1678
1076
  }
1679
1077
  }
1680
1078
  }
1681
- if (delegatedStructured.length === 0) {
1682
- return messages;
1683
- }
1684
- const last = messages[lastAssistantIdx];
1685
- let followUpPresent = last.responses.some((r) => r.type === 'followUp');
1686
- let nodePresent = last.responses.some((r) => r.type === 'node');
1687
- const fileIds = new Set(last.responses
1688
- .filter((r) => r.type === 'file')
1689
- .map((r) => r.content.fileId));
1690
- const toAppend = [];
1691
- for (const seg of delegatedStructured) {
1692
- if (seg.type === 'followUp' && !followUpPresent) {
1693
- toAppend.push(seg);
1694
- followUpPresent = true;
1695
- }
1696
- else if (seg.type === 'node' && !nodePresent) {
1697
- toAppend.push(seg);
1698
- nodePresent = true;
1699
- }
1700
- else if (seg.type === 'file' && !fileIds.has(seg.content.fileId)) {
1701
- toAppend.push(seg);
1702
- fileIds.add(seg.content.fileId);
1703
- }
1079
+ return parts.join('\n\n').trim();
1080
+ }
1081
+ function axpAiChatToolResultBodyJson(message) {
1082
+ return axpAiChatToolOrAgentResultBodyJson(message);
1083
+ }
1084
+ function axpAiChatToolOrAgentResultBodyJson(message) {
1085
+ const tr = message.responses.find((r) => r.type === 'tool_result' || r.type === 'agent_result');
1086
+ if (!tr || (tr.type !== 'tool_result' && tr.type !== 'agent_result')) {
1087
+ return '';
1704
1088
  }
1705
- if (toAppend.length === 0) {
1706
- return messages;
1089
+ try {
1090
+ return JSON.stringify(tr.content);
1707
1091
  }
1708
- const out = [...messages];
1709
- out[lastAssistantIdx] = {
1710
- ...last,
1711
- responses: [...last.responses, ...toAppend],
1712
- };
1713
- return out;
1714
- }
1715
- function axpAiPruneAssistantPiecesToFirstToolOnly(messages) {
1716
- const out = [];
1717
- let keptTool = false;
1718
- for (const m of messages) {
1719
- if (m.role !== 'assistant') {
1720
- out.push(m);
1721
- continue;
1722
- }
1723
- const idx = m.responses.findIndex((r) => r.type === 'tool' || r.type === 'agent');
1724
- if (idx < 0) {
1725
- out.push(m);
1726
- continue;
1727
- }
1728
- if (!keptTool) {
1729
- out.push({
1730
- ...m,
1731
- responses: m.responses.slice(0, idx + 1),
1732
- });
1733
- keptTool = true;
1734
- }
1092
+ catch {
1093
+ return String(tr.content);
1735
1094
  }
1736
- return out;
1737
- }
1738
- function axpAiChatDelegatedReflectionAssistantMessage(reflectionText) {
1739
- return {
1740
- role: 'assistant',
1741
- delegatedReflection: true,
1742
- responses: [{ type: 'text', content: reflectionText }],
1743
- };
1744
- }
1745
- function axpAiChatMessageIsDelegatedReflectionExcluded(message) {
1746
- return message.delegatedReflection === true;
1747
1095
  }
1748
1096
  //#endregion
1749
1097
 
@@ -2737,6 +2085,211 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
2737
2085
  args: [{ providedIn: 'root' }]
2738
2086
  }] });
2739
2087
 
2088
+ //#region ---- Imports ----
2089
+ //#endregion
2090
+ const STRUCTURED_OUTPUT_KINDS$1 = new Set(['object', 'array']);
2091
+ function usesStructuredFinalize(kind) {
2092
+ return STRUCTURED_OUTPUT_KINDS$1.has(kind);
2093
+ }
2094
+ //#endregion
2095
+ //#region ---- Retry defaults ----
2096
+ /** Default structured retry count for a contract kind when `structuredRetries` is omitted. */
2097
+ function defaultRetriesForKind(kind) {
2098
+ if (kind === 'string') {
2099
+ return 0;
2100
+ }
2101
+ return AXPAI_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES;
2102
+ }
2103
+ //#endregion
2104
+ //#region ---- Derive provider format ----
2105
+ function isJsonSchemaLike(value) {
2106
+ return value != null && typeof value === 'object' && !Array.isArray(value);
2107
+ }
2108
+ function parseResponseFormat(raw) {
2109
+ if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
2110
+ return undefined;
2111
+ }
2112
+ const o = raw;
2113
+ const t = o['type'];
2114
+ if (t === 'text') {
2115
+ return { type: 'text' };
2116
+ }
2117
+ if (t === 'json_object') {
2118
+ return { type: 'json_object' };
2119
+ }
2120
+ if (t === 'json_schema' && isJsonSchemaLike(o['schema'])) {
2121
+ const name = typeof o['name'] === 'string' ? o['name'].trim() : undefined;
2122
+ const strict = o['strict'] === true;
2123
+ return {
2124
+ type: 'json_schema',
2125
+ schema: o['schema'],
2126
+ ...(name && name.length > 0 ? { name } : {}),
2127
+ ...(strict ? { strict: true } : {}),
2128
+ };
2129
+ }
2130
+ return undefined;
2131
+ }
2132
+ /**
2133
+ * Maps an effective output contract to a provider request hint.
2134
+ * Explicit {@link AXPAiAgentOutputContract.providerResponseFormat} wins; otherwise derived from `kind` + `schema`.
2135
+ */
2136
+ function deriveProviderResponseFormat(contract) {
2137
+ if (contract.providerResponseFormat) {
2138
+ return contract.providerResponseFormat;
2139
+ }
2140
+ if (!usesStructuredFinalize(contract.kind)) {
2141
+ return undefined;
2142
+ }
2143
+ if (contract.schema && isJsonSchemaLike(contract.schema)) {
2144
+ const segment = resolveOutputTranscriptSegment(contract);
2145
+ const name = contract.schemaName?.trim() ||
2146
+ (segment === 'node' ? 'widget_layout' : segment === 'followUp' ? 'follow_up' : 'structured_output');
2147
+ return {
2148
+ type: 'json_schema',
2149
+ schema: contract.schema,
2150
+ name,
2151
+ strict: contract.schemaStrict === true,
2152
+ };
2153
+ }
2154
+ return { type: 'json_object' };
2155
+ }
2156
+ function parseJsonSchemaLike(raw) {
2157
+ if (!isJsonSchemaLike(raw)) {
2158
+ return undefined;
2159
+ }
2160
+ return raw;
2161
+ }
2162
+ //#endregion
2163
+
2164
+ //#region ---- Imports ----
2165
+ /** @see {@link AXP_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES} */
2166
+ const AXPAI_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES = AXP_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES;
2167
+ //#endregion
2168
+ //#region ---- Resolve helpers ----
2169
+ const STRUCTURED_OUTPUT_KINDS = new Set(['object', 'array']);
2170
+ function isOutputKind(value) {
2171
+ return value === 'string' || value === 'object' || value === 'array';
2172
+ }
2173
+ function isTranscriptSegment(value) {
2174
+ return value === 'text' || value === 'node' || value === 'followUp';
2175
+ }
2176
+ /** Resolved transcript mapping for an object contract (defaults to `text`). */
2177
+ function resolveOutputTranscriptSegment(contract) {
2178
+ if (contract.kind !== 'object') {
2179
+ return 'text';
2180
+ }
2181
+ return contract.transcriptSegment ?? 'text';
2182
+ }
2183
+ //#endregion
2184
+ //#region ---- Parser ----
2185
+ /**
2186
+ * Normalizes persisted `outputContract` (object or JSON string from entity forms).
2187
+ * Returns `null` when absent or invalid (engine treats as implicit `string`).
2188
+ */
2189
+ function parseAgentOutputContract(raw) {
2190
+ let value = raw;
2191
+ if (typeof raw === 'string') {
2192
+ const t = raw.trim();
2193
+ if (!t.length) {
2194
+ return null;
2195
+ }
2196
+ try {
2197
+ value = JSON.parse(t);
2198
+ }
2199
+ catch {
2200
+ return null;
2201
+ }
2202
+ }
2203
+ if (value == null || typeof value !== 'object' || Array.isArray(value)) {
2204
+ return null;
2205
+ }
2206
+ const o = value;
2207
+ const kind = o['kind'];
2208
+ if (!isOutputKind(kind)) {
2209
+ return null;
2210
+ }
2211
+ const instruction = typeof o['instruction'] === 'string' ? o['instruction'].trim() : undefined;
2212
+ const schemaSummary = typeof o['schemaSummary'] === 'string' ? o['schemaSummary'].trim() : undefined;
2213
+ const validationCommandName = typeof o['validationCommandName'] === 'string' ? o['validationCommandName'].trim() : undefined;
2214
+ const schema = parseJsonSchemaLike(o['schema']);
2215
+ const schemaName = typeof o['schemaName'] === 'string' ? o['schemaName'].trim() : undefined;
2216
+ const schemaStrict = o['schemaStrict'] === true;
2217
+ const providerResponseFormat = parseResponseFormat(o['providerResponseFormat']);
2218
+ const transcriptSegment = kind === 'object' && isTranscriptSegment(o['transcriptSegment']) ? o['transcriptSegment'] : undefined;
2219
+ let structuredRetries;
2220
+ if (typeof o['structuredRetries'] === 'number' && !Number.isNaN(o['structuredRetries'])) {
2221
+ structuredRetries = Math.max(0, Math.min(10, Math.floor(o['structuredRetries'])));
2222
+ }
2223
+ return {
2224
+ kind,
2225
+ ...(transcriptSegment ? { transcriptSegment } : {}),
2226
+ ...(instruction && instruction.length > 0 ? { instruction } : {}),
2227
+ ...(schemaSummary && schemaSummary.length > 0 ? { schemaSummary } : {}),
2228
+ ...(schema ? { schema } : {}),
2229
+ ...(schemaName && schemaName.length > 0 ? { schemaName } : {}),
2230
+ ...(schemaStrict ? { schemaStrict: true } : {}),
2231
+ ...(validationCommandName && validationCommandName.length > 0 ? { validationCommandName } : {}),
2232
+ ...(structuredRetries !== undefined ? { structuredRetries } : {}),
2233
+ ...(providerResponseFormat ? { providerResponseFormat } : {}),
2234
+ };
2235
+ }
2236
+ /** Resolved contract for engine (never null; defaults to string). */
2237
+ function resolveAgentOutputContract(raw) {
2238
+ const parsed = parseAgentOutputContract(raw);
2239
+ if (parsed) {
2240
+ return parsed;
2241
+ }
2242
+ return { kind: 'string' };
2243
+ }
2244
+ function axpAiAgentOutputContractUsesStructuredFinalize(contract) {
2245
+ return STRUCTURED_OUTPUT_KINDS.has(contract.kind);
2246
+ }
2247
+ /** Structured kinds omit the string role baseline (Markdown prose hint) in the mock engine. */
2248
+ function axpAiAgentOutputContractOmitsRoleBaseline(contract) {
2249
+ return axpAiAgentOutputContractUsesStructuredFinalize(contract);
2250
+ }
2251
+ function axpAiAgentOutputContractStructuredRetryCap(contract) {
2252
+ if (!axpAiAgentOutputContractUsesStructuredFinalize(contract)) {
2253
+ return 0;
2254
+ }
2255
+ if (typeof contract.structuredRetries === 'number') {
2256
+ return Math.max(0, Math.min(10, Math.floor(contract.structuredRetries)));
2257
+ }
2258
+ return AXPAI_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES;
2259
+ }
2260
+ //#endregion
2261
+
2262
+ //#region ---- Imports ----
2263
+ //#endregion
2264
+ //#region ---- Legacy → contract ----
2265
+ /**
2266
+ * Maps legacy `expectStructuredOutput` flags to an {@link AXPAiAgentOutputContract}, or returns explicit `outputContract`.
2267
+ */
2268
+ function outputContractFromAssistRunInput(input) {
2269
+ if (input.outputContract) {
2270
+ const parsed = parseAgentOutputContract(input.outputContract);
2271
+ if (parsed) {
2272
+ return parsed;
2273
+ }
2274
+ }
2275
+ if (!input.expectStructuredOutput) {
2276
+ return null;
2277
+ }
2278
+ const instruction = input.structuredOutputInstruction?.trim();
2279
+ const schemaSummary = input.structuredOutputSchemaSummary?.trim();
2280
+ let structuredRetries;
2281
+ if (typeof input.maxStructuredRetries === 'number' && !Number.isNaN(input.maxStructuredRetries)) {
2282
+ structuredRetries = Math.max(0, Math.min(10, Math.floor(input.maxStructuredRetries)));
2283
+ }
2284
+ return {
2285
+ kind: 'object',
2286
+ ...(instruction && instruction.length > 0 ? { instruction } : {}),
2287
+ ...(schemaSummary && schemaSummary.length > 0 ? { schemaSummary } : {}),
2288
+ ...(structuredRetries !== undefined ? { structuredRetries } : {}),
2289
+ };
2290
+ }
2291
+ //#endregion
2292
+
2740
2293
  //#region ---- Transcript ----
2741
2294
  /**
2742
2295
  * Text segments from the last assistant message (`type: 'text'` only).
@@ -3101,82 +2654,248 @@ function parseStructuredAssistantWithLocalRepair(raw) {
3101
2654
  if (v !== undefined) {
3102
2655
  return v;
3103
2656
  }
3104
- return undefined;
2657
+ return undefined;
2658
+ }
2659
+ /**
2660
+ * Tries combined assistant text, then each text segment from last to first (models often put JSON only in the last paragraph).
2661
+ */
2662
+ function parseStructuredAssistantWithSegmentFallback(combined, segments) {
2663
+ let v = parseStructuredAssistantWithLocalRepair(combined);
2664
+ if (v !== undefined) {
2665
+ return v;
2666
+ }
2667
+ const seen = new Set();
2668
+ const mark = stripAssistantThinkingXml(combined).trim();
2669
+ if (mark.length > 0) {
2670
+ seen.add(mark);
2671
+ }
2672
+ for (let i = segments.length - 1; i >= 0; i--) {
2673
+ const seg = stripAssistantThinkingXml(segments[i] ?? '').trim();
2674
+ if (!seg.length || seen.has(seg)) {
2675
+ continue;
2676
+ }
2677
+ seen.add(seg);
2678
+ v = parseStructuredAssistantWithLocalRepair(seg);
2679
+ if (v !== undefined) {
2680
+ return v;
2681
+ }
2682
+ }
2683
+ return undefined;
2684
+ }
2685
+ //#endregion
2686
+
2687
+ //#region ---- Imports ----
2688
+ //#endregion
2689
+ //#region ---- Validator ----
2690
+ function typeMatches(schemaType, value) {
2691
+ if (schemaType === undefined) {
2692
+ return true;
2693
+ }
2694
+ switch (schemaType) {
2695
+ case 'object':
2696
+ return value != null && typeof value === 'object' && !Array.isArray(value);
2697
+ case 'array':
2698
+ return Array.isArray(value);
2699
+ case 'string':
2700
+ return typeof value === 'string';
2701
+ case 'number':
2702
+ return typeof value === 'number' && !Number.isNaN(value);
2703
+ case 'integer':
2704
+ return typeof value === 'number' && Number.isInteger(value);
2705
+ case 'boolean':
2706
+ return typeof value === 'boolean';
2707
+ case 'null':
2708
+ return value === null;
2709
+ default:
2710
+ return true;
2711
+ }
2712
+ }
2713
+ function validateNode(schema, value, path, errors) {
2714
+ if (schema.enum && schema.enum.length > 0) {
2715
+ if (!schema.enum.some((e) => Object.is(e, value))) {
2716
+ errors.push({ path, message: `Value is not one of the allowed enum values.` });
2717
+ return;
2718
+ }
2719
+ }
2720
+ if (!typeMatches(schema.type, value)) {
2721
+ errors.push({
2722
+ path,
2723
+ message: `Expected type "${schema.type ?? 'any'}", got ${value === null ? 'null' : typeof value}.`,
2724
+ });
2725
+ return;
2726
+ }
2727
+ if (schema.type === 'object' && value != null && typeof value === 'object' && !Array.isArray(value)) {
2728
+ const o = value;
2729
+ const props = schema.properties ?? {};
2730
+ const required = schema.required ?? [];
2731
+ for (const key of required) {
2732
+ if (!(key in o) || o[key] === undefined) {
2733
+ errors.push({ path: path ? `${path}.${key}` : key, message: 'Required property is missing.' });
2734
+ }
2735
+ }
2736
+ for (const [key, propSchema] of Object.entries(props)) {
2737
+ if (key in o && o[key] !== undefined) {
2738
+ validateNode(propSchema, o[key], path ? `${path}.${key}` : key, errors);
2739
+ }
2740
+ }
2741
+ if (schema.additionalProperties === false) {
2742
+ for (const key of Object.keys(o)) {
2743
+ if (!(key in props)) {
2744
+ errors.push({
2745
+ path: path ? `${path}.${key}` : key,
2746
+ message: 'Additional property is not allowed.',
2747
+ });
2748
+ }
2749
+ }
2750
+ }
2751
+ else if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
2752
+ for (const [key, val] of Object.entries(o)) {
2753
+ if (!(key in props)) {
2754
+ validateNode(schema.additionalProperties, val, path ? `${path}.${key}` : key, errors);
2755
+ }
2756
+ }
2757
+ }
2758
+ }
2759
+ if (schema.type === 'array' && Array.isArray(value) && schema.items) {
2760
+ const itemSchema = Array.isArray(schema.items) ? schema.items[0] : schema.items;
2761
+ if (itemSchema) {
2762
+ value.forEach((item, index) => {
2763
+ validateNode(itemSchema, item, `${path}[${index}]`, errors);
2764
+ });
2765
+ }
2766
+ }
3105
2767
  }
3106
2768
  /**
3107
- * Tries combined assistant text, then each text segment from last to first (models often put JSON only in the last paragraph).
2769
+ * Validates a value against a minimal JSON Schema subset (types, required, enum, properties, items).
3108
2770
  */
3109
- function parseStructuredAssistantWithSegmentFallback(combined, segments) {
3110
- let v = parseStructuredAssistantWithLocalRepair(combined);
3111
- if (v !== undefined) {
3112
- return v;
3113
- }
3114
- const seen = new Set();
3115
- const mark = stripAssistantThinkingXml(combined).trim();
3116
- if (mark.length > 0) {
3117
- seen.add(mark);
3118
- }
3119
- for (let i = segments.length - 1; i >= 0; i--) {
3120
- const seg = stripAssistantThinkingXml(segments[i] ?? '').trim();
3121
- if (!seg.length || seen.has(seg)) {
3122
- continue;
3123
- }
3124
- seen.add(seg);
3125
- v = parseStructuredAssistantWithLocalRepair(seg);
3126
- if (v !== undefined) {
3127
- return v;
3128
- }
2771
+ function validateJsonSchemaMini(schema, value) {
2772
+ const errors = [];
2773
+ validateNode(schema, value, '', errors);
2774
+ if (errors.length > 0) {
2775
+ return { ok: false, errors };
3129
2776
  }
3130
- return undefined;
2777
+ return { ok: true };
3131
2778
  }
3132
2779
  //#endregion
3133
2780
 
3134
2781
  //#region ---- Imports ----
3135
- function hasAnyStructuredContent(combined, segments) {
3136
- if (combined.trim().length > 0) {
3137
- return true;
2782
+ //#endregion
2783
+ //#region ---- Parse + validate ----
2784
+ function defaultParseCommandEnvelope(raw) {
2785
+ if (raw == null || typeof raw !== 'object' || Array.isArray(raw)) {
2786
+ return { valid: false, errorMessage: 'Validation command returned an invalid envelope.' };
3138
2787
  }
3139
- if (!segments?.length) {
3140
- return false;
2788
+ const o = raw;
2789
+ if (o['success'] !== true) {
2790
+ const err = typeof o['error'] === 'string' ? o['error'] : 'Validation command failed.';
2791
+ return { valid: false, errorMessage: err };
3141
2792
  }
3142
- return segments.some((s) => (s ?? '').trim().length > 0);
2793
+ const data = o['data'];
2794
+ if (!data?.valid) {
2795
+ const errs = Array.isArray(data?.errors) ? data.errors.join('; ') : 'Layout validation failed.';
2796
+ return { valid: false, errorMessage: errs };
2797
+ }
2798
+ const normalized = typeof data.normalizedJson === 'string' ? data.normalizedJson.trim() : '';
2799
+ return { valid: true, ...(normalized.length > 0 ? { normalizedJson: normalized } : {}) };
3143
2800
  }
3144
2801
  /**
3145
- * Parses JSON from assistant text (including lightweight local repair), then optionally runs a custom validator.
3146
- *
3147
- * @param textSegmentsFallback - When provided, each segment is tried after combined text (last segment first); helps when prose and JSON sit in separate assistant text blocks.
2802
+ * Parses assistant text and validates against contract schema, registry command, and optional custom validator.
3148
2803
  */
3149
- async function validateStructuredAssistantResponse(assistantPlainText, structuredValidator, textSegmentsFallback) {
3150
- if (!hasAnyStructuredContent(assistantPlainText, textSegmentsFallback)) {
2804
+ async function validateAgainstContract(params) {
2805
+ const { contract, assistantPlainText, textSegmentsFallback } = params;
2806
+ if (!axpAiAgentOutputContractUsesStructuredFinalize(contract)) {
2807
+ const trimmed = stripAssistantThinkingXml(assistantPlainText).trim();
2808
+ if (!trimmed.length && !(textSegmentsFallback?.some((s) => (s ?? '').trim().length > 0))) {
2809
+ return {
2810
+ ok: false,
2811
+ errors: [{ phase: 'parse', message: 'Assistant returned empty output.' }],
2812
+ };
2813
+ }
2814
+ return { ok: true, value: trimmed };
2815
+ }
2816
+ const combined = stripAssistantThinkingXml(assistantPlainText);
2817
+ const parsed = parseStructuredAssistantWithSegmentFallback(combined, textSegmentsFallback ?? []);
2818
+ if (parsed === undefined) {
3151
2819
  return {
3152
2820
  ok: false,
3153
- errorMessage: 'Assistant returned empty output.',
3154
- failurePhase: 'parse',
2821
+ errors: [
2822
+ {
2823
+ phase: 'parse',
2824
+ message: 'Could not parse assistant output as JSON (expected a JSON object or array; optional markdown fence; segment fallback and local repair also failed).',
2825
+ },
2826
+ ],
3155
2827
  };
3156
2828
  }
3157
- const parsed = parseStructuredAssistantWithSegmentFallback(assistantPlainText, textSegmentsFallback ?? []);
3158
- if (parsed === undefined) {
2829
+ let value = parsed;
2830
+ if (contract.kind === 'array' && !Array.isArray(value)) {
3159
2831
  return {
3160
2832
  ok: false,
3161
- errorMessage: 'Could not parse assistant output as JSON (expected a JSON object or array; optional markdown fence; segment fallback, local repair, and truncation repair also failed).',
3162
- failurePhase: 'parse',
2833
+ errors: [{ phase: 'validator', message: 'Expected a JSON array as the root value.' }],
3163
2834
  };
3164
2835
  }
3165
- if (!structuredValidator) {
3166
- return { ok: true, value: parsed };
3167
- }
3168
- try {
3169
- const value = await structuredValidator(parsed);
3170
- return { ok: true, value };
3171
- }
3172
- catch (err) {
3173
- const msg = err instanceof Error ? err.message : String(err);
2836
+ if (contract.kind === 'object' && (value == null || typeof value !== 'object' || Array.isArray(value))) {
3174
2837
  return {
3175
2838
  ok: false,
3176
- errorMessage: msg.trim().length > 0 ? msg : 'Structured validation failed.',
3177
- failurePhase: 'validator',
2839
+ errors: [{ phase: 'validator', message: 'Expected a JSON object as the root value.' }],
3178
2840
  };
3179
2841
  }
2842
+ if (contract.schema) {
2843
+ const schemaResult = validateJsonSchemaMini(contract.schema, value);
2844
+ if (!schemaResult.ok) {
2845
+ const message = schemaResult.errors.map((e) => `${e.path || '(root)'}: ${e.message}`).join('; ');
2846
+ return {
2847
+ ok: false,
2848
+ errors: [{ phase: 'validator', message, details: schemaResult.errors }],
2849
+ };
2850
+ }
2851
+ }
2852
+ const cmd = contract.validationCommandName?.trim();
2853
+ if (cmd && params.commandExecutor) {
2854
+ try {
2855
+ const raw = await params.commandExecutor.execute(cmd, { json: value });
2856
+ const parseEnvelope = params.parseCommandEnvelope ?? defaultParseCommandEnvelope;
2857
+ const envelope = parseEnvelope(raw);
2858
+ if (!envelope.valid) {
2859
+ return {
2860
+ ok: false,
2861
+ errors: [
2862
+ {
2863
+ phase: 'validator',
2864
+ message: envelope.errorMessage ?? 'Validation command failed.',
2865
+ },
2866
+ ],
2867
+ };
2868
+ }
2869
+ if (envelope.normalizedJson) {
2870
+ try {
2871
+ value = JSON.parse(envelope.normalizedJson);
2872
+ }
2873
+ catch {
2874
+ value = envelope.normalizedJson;
2875
+ }
2876
+ }
2877
+ }
2878
+ catch (err) {
2879
+ const msg = err instanceof Error ? err.message : String(err);
2880
+ return {
2881
+ ok: false,
2882
+ errors: [{ phase: 'validator', message: msg.trim().length > 0 ? msg : 'Validation command failed.' }],
2883
+ };
2884
+ }
2885
+ }
2886
+ if (params.structuredValidator) {
2887
+ try {
2888
+ value = await params.structuredValidator(value);
2889
+ }
2890
+ catch (err) {
2891
+ const msg = err instanceof Error ? err.message : String(err);
2892
+ return {
2893
+ ok: false,
2894
+ errors: [{ phase: 'validator', message: msg.trim().length > 0 ? msg : 'Structured validation failed.' }],
2895
+ };
2896
+ }
2897
+ }
2898
+ return { ok: true, value };
3180
2899
  }
3181
2900
  //#endregion
3182
2901
 
@@ -3224,9 +2943,9 @@ function truncateForStructuredOutputError(text) {
3224
2943
  * Runs the engine, validates JSON (+ optional custom validator), and appends user retry messages on failure.
3225
2944
  */
3226
2945
  async function runStructuredOutputPipeline(params) {
3227
- const maxAttempts = params.retryOnInvalidStructure
3228
- ? 1 + Math.max(0, params.maxStructuredRetries)
3229
- : 1;
2946
+ const retryCap = params.maxStructuredRetries ?? axpAiAgentOutputContractStructuredRetryCap(params.contract);
2947
+ const maxAttempts = params.retryOnInvalidStructure ? 1 + Math.max(0, retryCap) : 1;
2948
+ const schemaSummary = params.structuredOutputSchemaSummary?.trim() || params.contract.schemaSummary?.trim();
3230
2949
  let messages = params.initialMessages;
3231
2950
  let mergedUsage;
3232
2951
  let lastProvider;
@@ -3261,7 +2980,13 @@ async function runStructuredOutputPipeline(params) {
3261
2980
  code: 'duplicate_assistant_output',
3262
2981
  });
3263
2982
  }
3264
- const validation = await validateStructuredAssistantResponse(structuredParts.combined, params.structuredValidator, structuredParts.segments);
2983
+ const validation = await validateAgainstContract({
2984
+ contract: params.contract,
2985
+ assistantPlainText: structuredParts.combined,
2986
+ textSegmentsFallback: structuredParts.segments,
2987
+ structuredValidator: params.structuredValidator,
2988
+ commandExecutor: params.commandExecutor,
2989
+ });
3265
2990
  if (validation.ok) {
3266
2991
  const structuredRunRaw = {
3267
2992
  kind: 'structured-output',
@@ -3271,26 +2996,31 @@ async function runStructuredOutputPipeline(params) {
3271
2996
  lastProviderResult: lastProvider,
3272
2997
  };
3273
2998
  return {
2999
+ success: true,
3000
+ outputKind: params.contract.kind,
3274
3001
  text: displayText,
3002
+ data: validation.value,
3275
3003
  structured: validation.value,
3004
+ messages: result.messages,
3276
3005
  structuredRunRaw,
3277
3006
  };
3278
3007
  }
3008
+ const firstError = validation.errors[0];
3279
3009
  prevFailureStructuredFingerprint = structuredFingerprint;
3280
- prevFailurePhase = validation.failurePhase;
3010
+ prevFailurePhase = firstError?.phase;
3281
3011
  const canRetry = params.retryOnInvalidStructure && attempt < maxAttempts;
3282
3012
  if (!canRetry) {
3283
3013
  throw new AXPAiStructuredOutputError({
3284
3014
  message: `Structured output validation failed after ${attempt} attempt(s).`,
3285
3015
  attempts: attempt,
3286
3016
  lastAssistantText: truncateForStructuredOutputError(displayText),
3287
- validationMessage: validation.errorMessage,
3017
+ validationMessage: firstError?.message ?? 'Validation failed.',
3288
3018
  code: 'validation_exhausted',
3289
3019
  });
3290
3020
  }
3291
3021
  messages = [
3292
3022
  ...result.messages,
3293
- axpAiChatTextMessage('user', buildStructuredOutputRetryUserText(validation.errorMessage, params.structuredOutputSchemaSummary, validation.failurePhase)),
3023
+ axpAiChatTextMessage('user', buildStructuredOutputRetryUserText(firstError?.message ?? 'Validation failed.', schemaSummary, firstError?.phase ?? 'parse')),
3294
3024
  ];
3295
3025
  }
3296
3026
  throw new AXPAiStructuredOutputError({
@@ -3303,6 +3033,76 @@ async function runStructuredOutputPipeline(params) {
3303
3033
  }
3304
3034
  //#endregion
3305
3035
 
3036
+ //#region ---- Imports ----
3037
+ //#endregion
3038
+ //#region ---- Segment id ----
3039
+ function isOutputContractSegmentId(value) {
3040
+ return value === 'text' || value === 'node' || value === 'followUp';
3041
+ }
3042
+ //#endregion
3043
+ class AXPAiOutputContractResolveService {
3044
+ constructor() {
3045
+ //#region ---- Dependencies ----
3046
+ this.segmentService = inject(AXPOutputContractTranscriptSegmentService);
3047
+ }
3048
+ //#endregion
3049
+ //#region ---- Public API ----
3050
+ /**
3051
+ * Resolves a persisted agent/assist `outputContract` value (segment id string)
3052
+ * or a full contract object (runtime / programmatic).
3053
+ */
3054
+ async resolveFromPersisted(raw) {
3055
+ if (raw == null) {
3056
+ return null;
3057
+ }
3058
+ if (typeof raw === 'string') {
3059
+ const id = raw.trim();
3060
+ if (!id.length) {
3061
+ return null;
3062
+ }
3063
+ if (isOutputContractSegmentId(id)) {
3064
+ return (await this.segmentService.getContractPreset(id)) ?? null;
3065
+ }
3066
+ return parseAgentOutputContract(raw);
3067
+ }
3068
+ if (typeof raw === 'object' && !Array.isArray(raw)) {
3069
+ return parseAgentOutputContract(raw);
3070
+ }
3071
+ return null;
3072
+ }
3073
+ /**
3074
+ * Effective contract for one engine turn.
3075
+ * Priority: runtime > input > agent > assist > `{ kind: 'string' }`.
3076
+ */
3077
+ async resolveEffective(params) {
3078
+ const runtime = await this.resolveFromPersisted(params.runtime);
3079
+ if (runtime) {
3080
+ return runtime;
3081
+ }
3082
+ const input = await this.resolveFromPersisted(params.input);
3083
+ if (input) {
3084
+ return input;
3085
+ }
3086
+ const agent = await this.resolveFromPersisted(params.agent);
3087
+ if (agent) {
3088
+ return agent;
3089
+ }
3090
+ const assist = await this.resolveFromPersisted(params.assist);
3091
+ if (assist) {
3092
+ return assist;
3093
+ }
3094
+ return resolveAgentOutputContract(null);
3095
+ }
3096
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAiOutputContractResolveService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3097
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAiOutputContractResolveService, providedIn: 'root' }); }
3098
+ }
3099
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: AXPAiOutputContractResolveService, decorators: [{
3100
+ type: Injectable,
3101
+ args: [{
3102
+ providedIn: 'root',
3103
+ }]
3104
+ }] });
3105
+
3306
3106
  //#region ---- Imports ----
3307
3107
  //#endregion
3308
3108
  //#region ---- Assist prompt run loading dialog ----
@@ -3383,6 +3183,7 @@ class AXPAiManagerService extends AXPAiAssistService {
3383
3183
  super(...arguments);
3384
3184
  //#region ---- Services & Dependencies ----
3385
3185
  this.aiEngine = inject(AXPAiEngine);
3186
+ this.outputContractResolve = inject(AXPAiOutputContractResolveService);
3386
3187
  this.entityService = inject(AXPEntityService);
3387
3188
  this.settings = inject(AXPSettingsService);
3388
3189
  this.runtimeContextBuilder = inject(AXPAiPlatformRuntimeContextBuilder);
@@ -3448,6 +3249,22 @@ class AXPAiManagerService extends AXPAiAssistService {
3448
3249
  }
3449
3250
  return this.getEffectiveAssist();
3450
3251
  }
3252
+ /**
3253
+ * Primary catalog model transport for an assist (`modelId` row).
3254
+ */
3255
+ async getAssistPrimaryChatTransport(assistId) {
3256
+ const assist = await this.getAssistForChat(assistId);
3257
+ const modelEntityId = assist.modelId?.trim();
3258
+ if (!modelEntityId) {
3259
+ return undefined;
3260
+ }
3261
+ const model = await this.modelEntityData.byKey(modelEntityId);
3262
+ const raw = String(model?.chatTransport ?? '').toLowerCase();
3263
+ if (raw === 'openai' || raw === 'gemini' || raw === 'demis') {
3264
+ return raw;
3265
+ }
3266
+ return undefined;
3267
+ }
3451
3268
  //#endregion
3452
3269
  //#region ---- Engine delegation ----
3453
3270
  /**
@@ -3499,32 +3316,48 @@ class AXPAiManagerService extends AXPAiAssistService {
3499
3316
  const platformRuntimeContext = mergePlatformRuntimeContext(baseCtx, input.platformRuntimeNotes ?? null);
3500
3317
  const directAgentId = input.directAgentId?.trim();
3501
3318
  let assistId = input.assistId?.trim();
3319
+ const assistRow = !directAgentId && !assistId
3320
+ ? await this.getEffectiveAssist()
3321
+ : await this.getAssistForChat(assistId);
3502
3322
  if (!directAgentId && !assistId) {
3503
- const assist = await this.getEffectiveAssist();
3504
- assistId = assist.id;
3505
- }
3323
+ assistId = assistRow.id;
3324
+ }
3325
+ const agentRow = directAgentId ? await this.agentEntityData.byKey(directAgentId) : null;
3326
+ const inputContract = outputContractFromAssistRunInput(input);
3327
+ const effectiveOutputContract = await this.outputContractResolve.resolveEffective({
3328
+ input: inputContract,
3329
+ assist: assistRow.outputContract,
3330
+ agent: agentRow?.outputContract,
3331
+ });
3332
+ const usesStructuredPipeline = input.expectStructuredOutput === true ||
3333
+ axpAiAgentOutputContractUsesStructuredFinalize(effectiveOutputContract);
3506
3334
  const messages = buildChatMessages(input);
3507
3335
  const catalogMax = await this.resolveCatalogMaxOutputTokens(assistId, directAgentId);
3508
3336
  const effectiveMaxTokens = input.maxTokens ?? catalogMax ?? AXPAiAssistDefaultStructuredMaxTokens;
3509
3337
  const runOptions = {
3510
3338
  platformRuntimeContext,
3339
+ outputContract: effectiveOutputContract,
3511
3340
  ...(directAgentId ? { directAgentId } : { assistId: assistId }),
3512
3341
  ...(input.maxSteps !== undefined ? { maxSteps: input.maxSteps } : {}),
3513
3342
  ...(input.temperature !== undefined ? { temperature: input.temperature } : {}),
3514
3343
  maxTokens: effectiveMaxTokens,
3515
3344
  };
3516
- if (!input.expectStructuredOutput) {
3345
+ if (!usesStructuredPipeline) {
3517
3346
  const result = await this.runEngine({ ...runOptions, messages });
3518
3347
  const text = extractLastAssistantPlainText(result.messages);
3519
3348
  return {
3349
+ success: true,
3350
+ outputKind: effectiveOutputContract.kind,
3520
3351
  text,
3521
3352
  raw: { usageTotals: result.usageTotals, lastProviderResult: result.lastProviderResult },
3353
+ messages: result.messages,
3522
3354
  };
3523
3355
  }
3524
3356
  const retryOnInvalidStructure = input.retryOnInvalidStructure !== false;
3525
3357
  const maxStructuredRetries = input.maxStructuredRetries ?? AXPAiAssistDefaultMaxStructuredRetries;
3526
3358
  const structuredMaxTokens = input.structuredOutputMaxTokens ?? effectiveMaxTokens;
3527
3359
  const pipelineResult = await runStructuredOutputPipeline({
3360
+ contract: effectiveOutputContract,
3528
3361
  initialMessages: messages,
3529
3362
  executeRun: (nextMessages) => this.runEngine({ ...runOptions, maxTokens: structuredMaxTokens, messages: nextMessages }),
3530
3363
  maxStructuredRetries,
@@ -3533,8 +3366,13 @@ class AXPAiManagerService extends AXPAiAssistService {
3533
3366
  structuredOutputSchemaSummary: input.structuredOutputSchemaSummary,
3534
3367
  });
3535
3368
  return {
3369
+ success: pipelineResult.success,
3370
+ outputKind: pipelineResult.outputKind,
3536
3371
  text: pipelineResult.text,
3537
3372
  structured: pipelineResult.structured,
3373
+ data: pipelineResult.data,
3374
+ validationErrors: pipelineResult.validationErrors,
3375
+ messages: pipelineResult.messages,
3538
3376
  raw: pipelineResult.structuredRunRaw,
3539
3377
  };
3540
3378
  }
@@ -3561,6 +3399,7 @@ class AXPAiManagerService extends AXPAiAssistService {
3561
3399
  maxSteps: options.maxSteps,
3562
3400
  temperature: options.temperature,
3563
3401
  maxTokens: options.maxTokens,
3402
+ outputContract: options.outputContract,
3564
3403
  expectStructuredOutput: options.expectStructuredOutput,
3565
3404
  structuredOutputInstruction: options.structuredOutputInstruction,
3566
3405
  platformRuntimeNotes: options.platformRuntimeNotes,
@@ -3795,6 +3634,51 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3795
3634
  type: Injectable
3796
3635
  }] });
3797
3636
 
3637
+ //#region ---- Imports ----
3638
+ //#endregion
3639
+ //#region ---- Built-in segment definitions ----
3640
+ /** Single source for entity select options and DI segment presets. */
3641
+ const AXM_BUILTIN_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_DEFINITIONS = [
3642
+ {
3643
+ name: 'text',
3644
+ title: '@ai-management:agents.entities.agent.fields.output-contract.transcript-segment.options.text',
3645
+ applicableKinds: ['object'],
3646
+ contract: {
3647
+ kind: 'object',
3648
+ transcriptSegment: 'text',
3649
+ structuredRetries: 2,
3650
+ },
3651
+ },
3652
+ {
3653
+ name: 'node',
3654
+ title: '@ai-management:agents.entities.agent.fields.output-contract.transcript-segment.options.node',
3655
+ applicableKinds: ['object'],
3656
+ contract: {
3657
+ kind: 'object',
3658
+ transcriptSegment: 'node',
3659
+ structuredRetries: 3,
3660
+ },
3661
+ },
3662
+ {
3663
+ name: 'followUp',
3664
+ title: '@ai-management:agents.entities.agent.fields.output-contract.transcript-segment.options.follow-up',
3665
+ applicableKinds: ['object'],
3666
+ contract: {
3667
+ kind: 'object',
3668
+ transcriptSegment: 'followUp',
3669
+ structuredRetries: 2,
3670
+ },
3671
+ },
3672
+ ];
3673
+ //#endregion
3674
+ //#region ---- Provider ----
3675
+ class AXMBuiltinOutputContractTranscriptSegmentProvider {
3676
+ provide() {
3677
+ return AXM_BUILTIN_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_DEFINITIONS;
3678
+ }
3679
+ }
3680
+ //#endregion
3681
+
3798
3682
  //#region ---- Imports ----
3799
3683
  //#endregion
3800
3684
  //#region ---- Routes ----
@@ -3837,11 +3721,21 @@ class AXMAiManagementModule {
3837
3721
  useClass: AXMAiManagementSettingProvider,
3838
3722
  multi: true,
3839
3723
  },
3724
+ {
3725
+ provide: AXP_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_PROVIDER,
3726
+ useClass: AXMBuiltinOutputContractTranscriptSegmentProvider,
3727
+ multi: true,
3728
+ },
3840
3729
  {
3841
3730
  provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3842
3731
  useClass: AXMAiModelCatalogDataSourceDefinition,
3843
3732
  multi: true,
3844
3733
  },
3734
+ {
3735
+ provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3736
+ useClass: AXMOutputContractTranscriptSegmentDataSourceDefinition,
3737
+ multi: true,
3738
+ },
3845
3739
  {
3846
3740
  provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3847
3741
  useClass: AXMAiAgentCatalogDataSourceDefinition,
@@ -3912,11 +3806,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
3912
3806
  useClass: AXMAiManagementSettingProvider,
3913
3807
  multi: true,
3914
3808
  },
3809
+ {
3810
+ provide: AXP_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_PROVIDER,
3811
+ useClass: AXMBuiltinOutputContractTranscriptSegmentProvider,
3812
+ multi: true,
3813
+ },
3915
3814
  {
3916
3815
  provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3917
3816
  useClass: AXMAiModelCatalogDataSourceDefinition,
3918
3817
  multi: true,
3919
3818
  },
3819
+ {
3820
+ provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3821
+ useClass: AXMOutputContractTranscriptSegmentDataSourceDefinition,
3822
+ multi: true,
3823
+ },
3920
3824
  {
3921
3825
  provide: AXP_DATASOURCE_DEFINITION_PROVIDER,
3922
3826
  useClass: AXMAiAgentCatalogDataSourceDefinition,
@@ -4020,82 +3924,6 @@ function axpAiDelegatedAgentPromptPreview(argumentsValue) {
4020
3924
  }
4021
3925
  //#endregion
4022
3926
 
4023
- //#region ---- AI tool command args ----
4024
- /**
4025
- * Recursively parses string values that look like JSON objects or arrays, then
4026
- * normalizes nested values the same way.
4027
- *
4028
- * LLM tool calls often stringify nested payloads (e.g. `__context__` for
4029
- * `Entity:Create`). Use this before passing args to the command or query service
4030
- * from AI paths only — not inside generic command infrastructure.
4031
- *
4032
- * @param toolName When set, command-specific AI defaults from command definitions are merged (e.g. `Entity:Create` redirect).
4033
- */
4034
- function normalizeAiToolCommandArgs(value, toolName) {
4035
- const normalized = normalizeLikelyJsonStringValuesRecursive(value);
4036
- return applyAiToolInputDefaultsByCommandName(toolName, normalized);
4037
- }
4038
- function normalizeLikelyJsonStringValuesRecursive(value) {
4039
- if (value === null || value === undefined) {
4040
- return value;
4041
- }
4042
- if (typeof value === 'string') {
4043
- const trimmed = value.trim();
4044
- if ((trimmed.startsWith('{') && trimmed.endsWith('}')) ||
4045
- (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
4046
- try {
4047
- const parsed = JSON.parse(trimmed);
4048
- return normalizeLikelyJsonStringValuesRecursive(parsed);
4049
- }
4050
- catch {
4051
- return value;
4052
- }
4053
- }
4054
- return value;
4055
- }
4056
- if (Array.isArray(value)) {
4057
- return value.map((item) => normalizeLikelyJsonStringValuesRecursive(item));
4058
- }
4059
- if (typeof value === 'object') {
4060
- const obj = value;
4061
- const out = {};
4062
- for (const key of Object.keys(obj)) {
4063
- out[key] = normalizeLikelyJsonStringValuesRecursive(obj[key]);
4064
- }
4065
- return out;
4066
- }
4067
- return value;
4068
- }
4069
- function applyAiToolInputDefaultsByCommandName(toolName, payload) {
4070
- if (toolName !== 'Entity:Create') {
4071
- return payload;
4072
- }
4073
- return mergeEntityCreateAiToolDefaults(payload);
4074
- }
4075
- /** Applies `axpCreateEntityAiToolInputDefaults` so AI flows do not trigger post-create navigation. */
4076
- function mergeEntityCreateAiToolDefaults(payload) {
4077
- if (payload == null || typeof payload !== 'object' || Array.isArray(payload)) {
4078
- return payload;
4079
- }
4080
- const p = payload;
4081
- const ctx = p['__context__'] ?? {};
4082
- const opts = ctx['options'] ?? {};
4083
- const proc = opts['process'] ?? {};
4084
- const defProcess = axpCreateEntityAiToolInputDefaults.__context__.options.process;
4085
- p['__context__'] = {
4086
- ...ctx,
4087
- options: {
4088
- ...opts,
4089
- process: {
4090
- ...proc,
4091
- ...defProcess,
4092
- },
4093
- },
4094
- };
4095
- return p;
4096
- }
4097
- //#endregion
4098
-
4099
3927
  //#region ---- Imports ----
4100
3928
  //#endregion
4101
3929
  /** Maximum length for assist initial transcript text after HTML strip (characters). */
@@ -4558,6 +4386,97 @@ function getAiChatCommandLlmParametersOverride(commandName) {
4558
4386
  }
4559
4387
  //#endregion
4560
4388
 
4389
+ //#region ---- Imports ----
4390
+ //#endregion
4391
+
4392
+ //#region ---- Imports ----
4393
+ function contractFromUnknown(raw) {
4394
+ if (raw == null) {
4395
+ return null;
4396
+ }
4397
+ if (typeof raw === 'object' && !Array.isArray(raw) && 'kind' in raw) {
4398
+ const parsed = parseAgentOutputContract(raw);
4399
+ if (parsed) {
4400
+ return parsed;
4401
+ }
4402
+ }
4403
+ return parseAgentOutputContract(raw);
4404
+ }
4405
+ /**
4406
+ * Resolves the effective output contract for one engine turn.
4407
+ *
4408
+ * Priority: runtime > input > agent > assist > `{ kind: 'string' }`.
4409
+ */
4410
+ function resolveEffectiveOutputContract(params) {
4411
+ const runtime = contractFromUnknown(params.runtime);
4412
+ if (runtime) {
4413
+ return runtime;
4414
+ }
4415
+ const input = contractFromUnknown(params.input);
4416
+ if (input) {
4417
+ return input;
4418
+ }
4419
+ const agent = contractFromUnknown(params.agent);
4420
+ if (agent) {
4421
+ return agent;
4422
+ }
4423
+ const assist = contractFromUnknown(params.assist);
4424
+ if (assist) {
4425
+ return assist;
4426
+ }
4427
+ return resolveAgentOutputContract(null);
4428
+ }
4429
+ //#endregion
4430
+
4431
+ //#region ---- Imports ----
4432
+ function hasAnyStructuredContent(combined, segments) {
4433
+ if (combined.trim().length > 0) {
4434
+ return true;
4435
+ }
4436
+ if (!segments?.length) {
4437
+ return false;
4438
+ }
4439
+ return segments.some((s) => (s ?? '').trim().length > 0);
4440
+ }
4441
+ /**
4442
+ * Parses JSON from assistant text (including lightweight local repair), then optionally runs a custom validator.
4443
+ *
4444
+ * @param textSegmentsFallback - When provided, each segment is tried after combined text (last segment first); helps when prose and JSON sit in separate assistant text blocks.
4445
+ */
4446
+ async function validateStructuredAssistantResponse(assistantPlainText, structuredValidator, textSegmentsFallback) {
4447
+ if (!hasAnyStructuredContent(assistantPlainText, textSegmentsFallback)) {
4448
+ return {
4449
+ ok: false,
4450
+ errorMessage: 'Assistant returned empty output.',
4451
+ failurePhase: 'parse',
4452
+ };
4453
+ }
4454
+ const parsed = parseStructuredAssistantWithSegmentFallback(assistantPlainText, textSegmentsFallback ?? []);
4455
+ if (parsed === undefined) {
4456
+ return {
4457
+ ok: false,
4458
+ errorMessage: 'Could not parse assistant output as JSON (expected a JSON object or array; optional markdown fence; segment fallback, local repair, and truncation repair also failed).',
4459
+ failurePhase: 'parse',
4460
+ };
4461
+ }
4462
+ if (!structuredValidator) {
4463
+ return { ok: true, value: parsed };
4464
+ }
4465
+ try {
4466
+ const value = await structuredValidator(parsed);
4467
+ return { ok: true, value };
4468
+ }
4469
+ catch (err) {
4470
+ const msg = err instanceof Error ? err.message : String(err);
4471
+ return {
4472
+ ok: false,
4473
+ errorMessage: msg.trim().length > 0 ? msg : 'Structured validation failed.',
4474
+ failurePhase: 'validator',
4475
+ };
4476
+ }
4477
+ }
4478
+ //#endregion
4479
+
4561
4480
  //#region ---- Imports ----
4562
4481
  //#endregion
4563
4482
  /**
@@ -4592,7 +4511,7 @@ class AXPAiAssistChatModelCatalogService {
4592
4511
  id: x.id,
4593
4512
  name: x.name?.trim() ?? '',
4594
4513
  title: resolveMultiLanguageString((x.title ?? x.name ?? x.id), this.localeId).trim() || x.id,
4595
- modelPurposes: normalizeModelPurposes(x),
4514
+ modelPurposes: readModelPurposes(x),
4596
4515
  chatTransport: x.chatTransport,
4597
4516
  maxContextTokens: typeof x.maxContextTokens === 'number' && x.maxContextTokens > 0
4598
4517
  ? x.maxContextTokens
@@ -4688,5 +4607,5 @@ function modelItemSupportsChatCatalogPurpose(m, purpose) {
4688
4607
  * Generated bundle index. Do not edit.
4689
4608
  */
4690
4609
 
4691
- export { AIMANAGEMENT_CHAT_GENERATE_IMAGE_COMMAND_KEY, AIMANAGEMENT_CHAT_SYNTHESIZE_SPEECH_COMMAND_KEY, AIMANAGEMENT_CHAT_TRANSCRIBE_SPEECH_COMMAND_KEY, AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY, AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, AI_AGENT_CATALOG_DATASOURCE_NAME, AI_CHAT_ATTACHMENT_CATEGORY, AI_CHAT_ATTACHMENT_REF_TYPE, AI_CHAT_GENERATED_IMAGE_REF_TYPE, AI_CHAT_GENERATED_SPEECH_REF_TYPE, AI_COMMAND_REGISTRY_CATALOG_DATASOURCE_NAME, AI_GENERATED_IMAGE_MAX_REMOTE_URL_IN_TOOL, AI_MODEL_CATALOG_DATASOURCE_NAME, AI_MODEL_IMAGE_CATALOG_DATASOURCE_NAME, AI_MODEL_SPEECH_CATALOG_DATASOURCE_NAME, AI_MODEL_TTS_CATALOG_DATASOURCE_NAME, AI_QUERY_REGISTRY_CATALOG_DATASOURCE_NAME, AXMAiAgentCatalogDataSourceDefinition, AXMAiAssistPermissionKeys, AXMAiAssistPromptBoxComponent, AXMAiAssistPromptBoxService, AXMAiCommandRegistryCatalogDataSourceDefinition, AXMAiManagementEntityProvider, AXMAiManagementMenuProvider, AXMAiManagementModule, AXMAiModelCatalogDataSourceDefinition, AXMAiQueryRegistryCatalogDataSourceDefinition, AXMPermissionDefinitionProvider, AXMPermissionsKeys, AXPAI_CONVERSATION_FOLLOW_UP_AGENT_NAME, AXPAI_DELEGATED_AGENT_DEFAULT_MAX_STEPS, AXPAI_DOCUMENT_VISION_DEFAULT_USER_INSTRUCTION, AXPAI_DOCUMENT_VISION_EXTRACTION_SYSTEM, AXPAI_ENGINE_DEFAULT_MAX_STEPS, AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX, AXPAiAssistChatModelCatalogService, AXPAiAssistDefaultMaxStructuredRetries, AXPAiAssistDefaultStructuredMaxTokens, AXPAiAssistService, AXPAiChatToolRunContextService, AXPAiDefaultModelPickerService, AXPAiEngine, AXPAiManagementMenuKeys, AXPAiManagementSettings, AXPAiManagerService, AXPAiPlatformRuntimeContextBuilder, AXPAiStructuredOutputError, AiManagementManifest, RootConfig, axpAiAssistInitialMessagesToTranscript, axpAiAssistStarterPromptLabels, axpAiAssistStarterPromptTexts, axpAiBuildAutoFollowUpAgentQuery, axpAiChatAgentResultMessage, axpAiChatDelegatedReflectionAssistantMessage, axpAiChatMessageGetText, axpAiChatMessageIsDelegatedReflectionExcluded, axpAiChatMessagesFromProviderAssistant, axpAiChatTextMessage, axpAiChatToolOrAgentResultBodyJson, axpAiChatToolResultBodyJson, axpAiChatToolResultMessage, axpAiChatUserMessage, axpAiCoercePlanningAssistantTextToThink, axpAiCollectDelegatedAgentOutcomeResponses, axpAiDelegatedAgentOutcomeResponsesPlainText, axpAiDelegatedAgentPromptPreview, axpAiDelegatedSubTranscriptAppendFileArtifactFromToolResults, axpAiExtractLastStoredFileArtifactFromChatMessages, axpAiExtractStoredFileArtifactFromUnknown, axpAiMergePlainAssistantTextTurn, axpAiNormalizeFollowUpCommand, axpAiNormalizeFollowUpContent, axpAiNormalizeFollowUpItem, axpAiNormalizeHandlerResultToToolBody, axpAiParseDelegatedAgentResultSegmentsFromUnknown, axpAiParseSupervisorAgentToolName, axpAiPruneAssistantPiecesToFirstToolOnly, axpAiReconcileSupervisorFinalAssistantWithDelegatedOutcomes, axpAiShouldAutoInvokeFollowUpAgent, axpAiStripAssistInitialHtmlToPlain, axpAiSupervisorAgentToolName, axpAiToolCallFromAssistantToolMessage, axpAiTranscriptAlreadyHasFollowUpInTurn, blobOrFileFromImageResult, getAiChatCommandLlmParametersOverride, isChatPurposeModelRow, isImagePurposeModelRow, isSpeechPurposeModelRow, isTtsPurposeModelRow, loadAssistPermissionRows, modelRowHasPurpose, normalizeAiToolCommandArgs, normalizeModelPurposes, persistAiChatAttachmentImage, persistAiGeneratedImage, persistAiGeneratedSpeech };
4610
+ export { AIMANAGEMENT_CHAT_GENERATE_IMAGE_COMMAND_KEY, AIMANAGEMENT_CHAT_SYNTHESIZE_SPEECH_COMMAND_KEY, AIMANAGEMENT_CHAT_TRANSCRIBE_SPEECH_COMMAND_KEY, AIMANAGEMENT_EXTRACT_DOCUMENT_TEXT_COMMAND_KEY, AIMANAGEMENT_STRUCTURED_TEXT_COMPLETION_COMMAND_KEY, AI_AGENT_CATALOG_DATASOURCE_NAME, AI_CHAT_ATTACHMENT_CATEGORY, AI_CHAT_ATTACHMENT_REF_TYPE, AI_CHAT_GENERATED_IMAGE_REF_TYPE, AI_CHAT_GENERATED_SPEECH_REF_TYPE, AI_COMMAND_REGISTRY_CATALOG_DATASOURCE_NAME, AI_GENERATED_IMAGE_MAX_REMOTE_URL_IN_TOOL, AI_MODEL_CATALOG_DATASOURCE_NAME, AI_MODEL_IMAGE_CATALOG_DATASOURCE_NAME, AI_MODEL_SPEECH_CATALOG_DATASOURCE_NAME, AI_MODEL_TTS_CATALOG_DATASOURCE_NAME, AI_OUTPUT_CONTRACT_TRANSCRIPT_SEGMENT_DATASOURCE_NAME, AI_QUERY_REGISTRY_CATALOG_DATASOURCE_NAME, AXMAiAgentCatalogDataSourceDefinition, AXMAiAssistPermissionKeys, AXMAiAssistPromptBoxComponent, AXMAiAssistPromptBoxService, AXMAiCommandRegistryCatalogDataSourceDefinition, AXMAiManagementEntityProvider, AXMAiManagementMenuProvider, AXMAiManagementModule, AXMAiModelCatalogDataSourceDefinition, AXMAiQueryRegistryCatalogDataSourceDefinition, AXMOutputContractTranscriptSegmentDataSourceDefinition, AXMPermissionDefinitionProvider, AXMPermissionsKeys, AXPAI_AGENT_OUTPUT_CONTRACT_DEFAULT_STRUCTURED_RETRIES, AXPAI_SUPERVISOR_AGENT_TOOL_PREFIX, AXPAiAssistChatModelCatalogService, AXPAiAssistDefaultMaxStructuredRetries, AXPAiAssistDefaultStructuredMaxTokens, AXPAiAssistService, AXPAiChatToolRunContextService, AXPAiDefaultModelPickerService, AXPAiEngine, AXPAiManagementMenuKeys, AXPAiManagementSettings, AXPAiManagerService, AXPAiOutputContractResolveService, AXPAiPlatformRuntimeContextBuilder, AXPAiStructuredOutputError, AiManagementManifest, RootConfig, axpAiAgentOutputContractOmitsRoleBaseline, axpAiAgentOutputContractStructuredRetryCap, axpAiAgentOutputContractUsesStructuredFinalize, axpAiAssistInitialMessagesToTranscript, axpAiAssistStarterPromptLabels, axpAiAssistStarterPromptTexts, axpAiChatMessageGetText, axpAiChatMessageIsDelegatedReflectionExcluded, axpAiChatTextMessage, axpAiChatToolOrAgentResultBodyJson, axpAiChatToolResultBodyJson, axpAiChatUserMessage, axpAiDelegatedAgentOutcomeResponsesPlainText, axpAiDelegatedAgentPromptPreview, axpAiDelegatedAgentResultSegments, axpAiParseSupervisorAgentToolName, axpAiStripAssistInitialHtmlToPlain, blobOrFileFromImageResult, buildStructuredOutputRetryUserText, defaultRetriesForKind, deriveProviderResponseFormat, extractLastAssistantPlainText, extractLastAssistantStructuredParts, extractLastAssistantTextSegments, getAiChatCommandLlmParametersOverride, isChatPurposeModelRow, isImagePurposeModelRow, isSpeechPurposeModelRow, isTtsPurposeModelRow, loadAssistPermissionRows, modelRowHasPurpose, parseAgentOutputContract, parseJsonSchemaLike, parseResponseFormat, persistAiChatAttachmentImage, persistAiGeneratedImage, persistAiGeneratedSpeech, readModelPurposes, resolveAgentOutputContract, resolveEffectiveOutputContract, resolveOutputTranscriptSegment, validateAgainstContract, validateJsonSchemaMini, validateStructuredAssistantResponse };
4692
4611
  //# sourceMappingURL=acorex-modules-ai-management.mjs.map