@aifabrix/builder 2.52.0 → 2.52.1

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 (134) hide show
  1. package/jest.isolated-projects.js +25 -2
  2. package/jest.projects.js +4 -1
  3. package/lib/api/datasources-extended.api.js +8 -8
  4. package/lib/api/keycloak.api.js +92 -0
  5. package/lib/api/token-providers.api.js +77 -0
  6. package/lib/api/types/keycloak.types.js +23 -0
  7. package/lib/api/types/token-providers.types.js +65 -0
  8. package/lib/app/down.js +16 -1
  9. package/lib/channels/add-channel.js +126 -19
  10. package/lib/channels/approval-guides/chatgpt.js +1 -1
  11. package/lib/channels/approval-guides/microsoft-copilot.js +16 -12
  12. package/lib/channels/channel-add-ensure.js +128 -0
  13. package/lib/channels/channel-artifact-generator.js +11 -1
  14. package/lib/channels/channel-branding.js +94 -0
  15. package/lib/channels/channel-credential.js +8 -0
  16. package/lib/channels/channel-discovery.js +330 -0
  17. package/lib/channels/channel-external-system.js +1 -1
  18. package/lib/channels/channel-runtime-resolver.js +103 -14
  19. package/lib/channels/channel-status-display.js +7 -1
  20. package/lib/channels/microsoft-copilot-channel-add.js +138 -0
  21. package/lib/channels/microsoft-copilot-entra-sso-form.js +180 -0
  22. package/lib/channels/microsoft-copilot-entra-sso.js +264 -0
  23. package/lib/channels/microsoft-copilot-icons.js +74 -0
  24. package/lib/channels/microsoft-copilot-limits.js +84 -0
  25. package/lib/channels/microsoft-copilot-package.js +130 -49
  26. package/lib/channels/microsoft-copilot-validate.js +197 -0
  27. package/lib/cli/index.js +2 -0
  28. package/lib/cli/infra-guided.js +13 -6
  29. package/lib/cli/setup-infra.js +31 -4
  30. package/lib/commands/channel-add-setup.js +28 -9
  31. package/lib/commands/channel.js +14 -10
  32. package/lib/commands/integration-definition.js +158 -0
  33. package/lib/commands/repair-auth-apply.js +19 -5
  34. package/lib/commands/repair-datasource-sync-operations.js +4 -1
  35. package/lib/commands/role-assistant-test-output.js +200 -0
  36. package/lib/commands/role-assistant.js +125 -166
  37. package/lib/datasource/capability/capability-resolve.js +15 -5
  38. package/lib/datasource/capability/copy-operations.js +14 -4
  39. package/lib/infrastructure/index.js +19 -4
  40. package/lib/integration-definition/apply.js +478 -0
  41. package/lib/integration-definition/canonical-json.js +51 -0
  42. package/lib/integration-definition/emit-suite-bid.js +94 -0
  43. package/lib/integration-definition/enrich-crm-field-mappings.js +240 -0
  44. package/lib/integration-definition/enrich-openapi-operations.js +434 -0
  45. package/lib/integration-definition/from-discovery.js +339 -0
  46. package/lib/integration-definition/hash.js +37 -0
  47. package/lib/integration-definition/index.js +37 -0
  48. package/lib/integration-definition/openapi-enrichment/hubspot-crm-companies.json +205 -0
  49. package/lib/integration-definition/resource-type-catalog.js +59 -0
  50. package/lib/integration-definition/system-kind-enum.js +40 -0
  51. package/lib/integration-definition/validate.js +148 -0
  52. package/lib/programmatic/builder-api-request.js +5 -1
  53. package/lib/programmatic/builder-help-governance.js +3 -2
  54. package/lib/programmatic/capability-registry.js +19 -0
  55. package/lib/programmatic/openapi-component-schemas.js +6 -1
  56. package/lib/programmatic/openapi-descriptions.js +40 -0
  57. package/lib/programmatic/openapi-envelope-examples.js +269 -0
  58. package/lib/programmatic/openapi-operation-bindings.js +45 -0
  59. package/lib/programmatic/openapi-request-schemas.js +14 -0
  60. package/lib/programmatic/openapi-result-schemas-overflow.js +101 -0
  61. package/lib/programmatic/openapi-result-schemas.js +46 -99
  62. package/lib/programmatic/openapi-schema-descriptions-core.js +7 -4
  63. package/lib/programmatic/openapi-schema-descriptions-help.js +1 -1
  64. package/lib/programmatic/openapi-schema-descriptions-operations.js +24 -0
  65. package/lib/programmatic/openapi-schema-helpers.js +9 -9
  66. package/lib/programmatic/openapi-spec.js +11 -1
  67. package/lib/programmatic/orchestration-openapi-meta.js +10 -0
  68. package/lib/programmatic/route-handler-map.js +29 -1
  69. package/lib/programmatic/run-channel-package.js +236 -0
  70. package/lib/programmatic/run-integration-definition.js +69 -0
  71. package/lib/role-assistant/knowledge-sync-logging.js +58 -0
  72. package/lib/role-assistant/knowledge-sync.js +88 -93
  73. package/lib/role-assistant/test-cases-bid.js +155 -0
  74. package/lib/role-assistant/test-cases-evaluate.js +9 -1
  75. package/lib/role-assistant/test-cases-honesty.js +24 -10
  76. package/lib/role-assistant/test-cases.js +28 -0
  77. package/lib/role-assistant/test-runner-bid-apply.js +122 -0
  78. package/lib/role-assistant/test-runner-bid-document.js +278 -0
  79. package/lib/role-assistant/test-runner-workhub-answers.js +245 -20
  80. package/lib/role-assistant/test-runner-workhub.js +100 -18
  81. package/lib/role-assistant/test-suite.js +119 -0
  82. package/lib/schema/application-schema.json +56 -6
  83. package/lib/schema/external-datasource.schema.json +103 -17
  84. package/lib/schema/external-system.schema.json +54 -3
  85. package/lib/schema/integration-definition.schema.json +195 -0
  86. package/lib/utils/authentication-shape.js +6 -0
  87. package/lib/utils/docker-build.js +3 -3
  88. package/lib/utils/help-builder.js +1 -0
  89. package/lib/utils/promote-dev-docker-image.js +97 -18
  90. package/lib/utils/transient-developer-id.js +28 -0
  91. package/package.json +1 -1
  92. package/scripts/pnpm/af.mjs +24 -9
  93. package/scripts/pnpm/help.mjs +1 -1
  94. package/templates/applications/dataplane/application.yaml +3 -2
  95. package/templates/applications/dataplane/env.template +7 -6
  96. package/templates/applications/miso-controller/application.yaml +23 -9
  97. package/templates/applications/miso-controller/env.template +8 -2
  98. package/templates/channels/README.md +11 -1
  99. package/templates/channels/brand/color-192.png +0 -0
  100. package/templates/channels/brand/dark/128.png +0 -0
  101. package/templates/channels/brand/dark/192.png +0 -0
  102. package/templates/channels/brand/dark/256.png +0 -0
  103. package/templates/channels/brand/dark/32.png +0 -0
  104. package/templates/channels/brand/dark/512.png +0 -0
  105. package/templates/channels/brand/dark/64.png +0 -0
  106. package/templates/channels/brand/light/128.png +0 -0
  107. package/templates/channels/brand/light/192.png +0 -0
  108. package/templates/channels/brand/light/256.png +0 -0
  109. package/templates/channels/brand/light/32.png +0 -0
  110. package/templates/channels/brand/light/512.png +0 -0
  111. package/templates/channels/brand/light/64.png +0 -0
  112. package/templates/channels/brand/mono-white/128.png +0 -0
  113. package/templates/channels/brand/mono-white/192.png +0 -0
  114. package/templates/channels/brand/mono-white/256.png +0 -0
  115. package/templates/channels/brand/mono-white/32.png +0 -0
  116. package/templates/channels/brand/mono-white/512.png +0 -0
  117. package/templates/channels/brand/mono-white/64.png +0 -0
  118. package/templates/channels/brand/outline-32.png +0 -0
  119. package/templates/channels/chatgpt/install.template.md.hbs +6 -4
  120. package/templates/channels/chatgpt/integration/README.md +4 -5
  121. package/templates/channels/chatgpt/integration/chatgpt-system.json +1 -16
  122. package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +10 -16
  123. package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +5 -3
  124. package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +27 -0
  125. package/templates/channels/microsoft-copilot/install.template.md.hbs +39 -20
  126. package/templates/channels/microsoft-copilot/integration/README.md +2 -3
  127. package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +1 -22
  128. package/templates/channels/microsoft-copilot/manifest.template.json.hbs +7 -7
  129. package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +11 -0
  130. package/templates/channels/slack/integration/README.md +2 -4
  131. package/templates/channels/teams-bot/install.template.md.hbs +4 -0
  132. package/templates/channels/teams-bot/integration/README.md +3 -5
  133. package/templates/channels/web-embed/integration/web-embed-system.json +1 -16
  134. /package/.cursor/plans/{Done → Archive}/157.1-security-quality-violations-by-type.csv +0 -0
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Builder API channel packaging handlers (plan 167.0).
3
+ *
4
+ * @fileoverview Package build / branding / zip for workspace channel systemKeys
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const fs = require('../internal/fs-real-sync');
12
+ const path = require('path');
13
+ const { ensureIntegrationDirectory } = require('./workspace-context');
14
+ const { resolveChannelRuntimeValues } = require('../channels/channel-runtime-resolver');
15
+ const { generateChannelArtifact } = require('../channels/channel-artifact-generator');
16
+ const { loadChannelPackageContext } = require('../channels/channel-discovery');
17
+ const {
18
+ readChannelBranding,
19
+ writeChannelBranding,
20
+ mergeChannelBranding
21
+ } = require('../channels/channel-branding');
22
+ const { isChannelTargetKey } = require('../channels/channel-targets');
23
+
24
+ /**
25
+ * @param {import('./workspace-context').WorkspaceContext} ctx
26
+ * @returns {string}
27
+ */
28
+ function workspaceSystemDir(ctx) {
29
+ return ctx.getIntegrationPath();
30
+ }
31
+
32
+ /**
33
+ * Channel target equals workspace systemKey for packaging apps.
34
+ * @param {string} systemKey
35
+ * @returns {string}
36
+ */
37
+ function requireChannelTarget(systemKey) {
38
+ if (!isChannelTargetKey(systemKey)) {
39
+ throw new Error(
40
+ `systemKey "${systemKey}" is not a supported channel packaging target`
41
+ );
42
+ }
43
+ return systemKey;
44
+ }
45
+
46
+ /**
47
+ * @param {import('./workspace-context').WorkspaceContext} ctx
48
+ * @returns {{ type: string, token: string }}
49
+ */
50
+ function authConfigFromContext(ctx) {
51
+ return { type: 'bearer', token: ctx.bearerToken };
52
+ }
53
+
54
+ /**
55
+ * @param {Object} branding
56
+ * @param {string} channelTarget
57
+ * @returns {Object}
58
+ */
59
+ function fallbackPackageContext(branding, channelTarget) {
60
+ return {
61
+ displayName: branding.displayName || channelTarget,
62
+ description: branding.description || '',
63
+ governedToolNames: [],
64
+ mcpServerUrl: null,
65
+ installReady: false,
66
+ blocker: 'discoveryUnavailable',
67
+ authentication: null
68
+ };
69
+ }
70
+
71
+ /**
72
+ * @param {import('./workspace-context').WorkspaceContext} ctx
73
+ * @param {Object} body
74
+ * @param {string} channelTarget
75
+ * @param {Object} branding
76
+ * @param {string} dataplaneUrl
77
+ * @param {Object} packageContext
78
+ * @returns {Object}
79
+ */
80
+ function generatePackageArtifact(ctx, body, channelTarget, branding, dataplaneUrl, packageContext) {
81
+ const appName =
82
+ branding.displayName || body.name || packageContext.displayName || channelTarget;
83
+ const description =
84
+ branding.description || body.description || packageContext.description || '';
85
+ const runtimeValues = resolveChannelRuntimeValues({
86
+ dataplaneBaseUrl: dataplaneUrl,
87
+ channelTarget,
88
+ appName,
89
+ baseName: body.baseName || appName,
90
+ environment: body.environment || ctx.environment || 'dev',
91
+ appDescription: description,
92
+ layerBToolNames: packageContext.governedToolNames || [],
93
+ packageContext,
94
+ iconColorPath: branding.iconColorPath,
95
+ iconOutlinePath: branding.iconOutlinePath
96
+ });
97
+ return generateChannelArtifact({
98
+ projectRoot: ctx.getUserRoot(),
99
+ channelTarget,
100
+ runtimeValues,
101
+ dryRun: body.dryRun === true
102
+ });
103
+ }
104
+
105
+ /**
106
+ * @param {import('./workspace-context').WorkspaceContext} ctx
107
+ * @param {Object} body
108
+ * @returns {Promise<Object>}
109
+ */
110
+ async function buildChannelPackage(ctx, body = {}) {
111
+ const channelTarget = requireChannelTarget(ctx.systemKey);
112
+ ensureIntegrationDirectory(ctx);
113
+ const dir = workspaceSystemDir(ctx);
114
+ const branding = mergeChannelBranding(readChannelBranding(dir), body.branding || {});
115
+ if (body.branding && typeof body.branding === 'object') {
116
+ writeChannelBranding(dir, branding);
117
+ }
118
+ const dataplaneUrl = ctx.dataplaneUrl || body.dataplaneUrl;
119
+ if (!dataplaneUrl) {
120
+ throw new Error('dataplaneUrl is required to build a channel package');
121
+ }
122
+ const packageContext = await loadChannelPackageContext({
123
+ dataplaneBaseUrl: dataplaneUrl,
124
+ authConfig: authConfigFromContext(ctx),
125
+ channelTarget,
126
+ branding
127
+ }).catch(() => fallbackPackageContext(branding, channelTarget));
128
+ const artifact = generatePackageArtifact(
129
+ ctx,
130
+ body,
131
+ channelTarget,
132
+ branding,
133
+ dataplaneUrl,
134
+ packageContext
135
+ );
136
+ return {
137
+ channelTarget,
138
+ installReady: artifact.installReady === true,
139
+ blocker: artifact.blocker || packageContext.blocker || null,
140
+ artifactHash: artifact.artifactHash,
141
+ links: artifact.links || {
142
+ readme: artifact.installPath,
143
+ zip: artifact.zipPath || null
144
+ },
145
+ summary: artifact.content ? JSON.parse(artifact.content) : null
146
+ };
147
+ }
148
+
149
+ /**
150
+ * @param {import('./workspace-context').WorkspaceContext} ctx
151
+ * @returns {Object}
152
+ */
153
+ function getChannelPackageSummary(ctx) {
154
+ const channelTarget = requireChannelTarget(ctx.systemKey);
155
+ const projectRoot = ctx.getUserRoot();
156
+ const summaryPath = path.join(
157
+ projectRoot,
158
+ 'dist',
159
+ 'channels',
160
+ channelTarget,
161
+ `channel-${channelTarget}.json`
162
+ );
163
+ if (!fs.existsSync(summaryPath)) {
164
+ throw new Error(`Channel package summary not found: ${summaryPath}`);
165
+ }
166
+ const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8'));
167
+ return {
168
+ channelTarget,
169
+ summary,
170
+ links: summary.links || null,
171
+ installReady: summary.installReady === true,
172
+ blocker: summary.blocker || null
173
+ };
174
+ }
175
+
176
+ /**
177
+ * @param {import('./workspace-context').WorkspaceContext} ctx
178
+ * @returns {{ fileName: string, contentType: string, byteLength: number, zipBase64: string }}
179
+ */
180
+ function getChannelPackageZip(ctx) {
181
+ const channelTarget = requireChannelTarget(ctx.systemKey);
182
+ const summary = getChannelPackageSummary(ctx).summary;
183
+ const zipPath =
184
+ typeof summary?.links?.zip === 'string' && summary.links.zip
185
+ ? summary.links.zip
186
+ : path.join(
187
+ ctx.getUserRoot(),
188
+ 'dist',
189
+ 'channels',
190
+ channelTarget,
191
+ 'build',
192
+ `appPackage.${summary.environment || 'dev'}.zip`
193
+ );
194
+ if (!fs.existsSync(zipPath)) {
195
+ throw new Error(`Channel package ZIP not found: ${zipPath}`);
196
+ }
197
+ const buffer = fs.readFileSync(zipPath);
198
+ return {
199
+ fileName: path.basename(zipPath),
200
+ contentType: 'application/zip',
201
+ byteLength: buffer.length,
202
+ zipBase64: buffer.toString('base64')
203
+ };
204
+ }
205
+
206
+ /**
207
+ * @param {import('./workspace-context').WorkspaceContext} ctx
208
+ * @param {Object} body
209
+ * @returns {Object}
210
+ */
211
+ function patchChannelBranding(ctx, body = {}) {
212
+ requireChannelTarget(ctx.systemKey);
213
+ ensureIntegrationDirectory(ctx);
214
+ const branding = writeChannelBranding(workspaceSystemDir(ctx), body || {});
215
+ return { systemKey: ctx.systemKey, branding };
216
+ }
217
+
218
+ /**
219
+ * @param {import('./workspace-context').WorkspaceContext} ctx
220
+ * @returns {Object}
221
+ */
222
+ function getChannelBranding(ctx) {
223
+ requireChannelTarget(ctx.systemKey);
224
+ return {
225
+ systemKey: ctx.systemKey,
226
+ branding: readChannelBranding(workspaceSystemDir(ctx))
227
+ };
228
+ }
229
+
230
+ module.exports = {
231
+ buildChannelPackage,
232
+ getChannelPackageSummary,
233
+ getChannelPackageZip,
234
+ patchChannelBranding,
235
+ getChannelBranding
236
+ };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Apply Builder Integration Definition (BID) into a user workspace.
3
+ *
4
+ * @fileoverview POST …/integration-definition/apply
5
+ * @author AI Fabrix Team
6
+ * @version 1.0.0
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const path = require('path');
12
+ const {
13
+ applyIntegrationDefinition,
14
+ withDefinitionHash
15
+ } = require('../integration-definition');
16
+ const {
17
+ runWithWorkspaceContext,
18
+ ensureIntegrationDirectory,
19
+ WORKSPACE_INTEGRATION_FORMAT
20
+ } = require('./workspace-context');
21
+ const { touchWorkspaceMeta } = require('./workspace-meta');
22
+
23
+ const OPENAPI_ENRICHMENT_DIR = path.join(
24
+ __dirname,
25
+ '../integration-definition/openapi-enrichment'
26
+ );
27
+
28
+ /**
29
+ * @param {import('./workspace-context').WorkspaceContext} ctx
30
+ * @param {Object} body
31
+ * @param {Object} [body.integrationDefinition]
32
+ * @param {boolean} [body.force]
33
+ * @param {boolean} [body.dryRun]
34
+ * @returns {Promise<Object>}
35
+ */
36
+ async function applyIntegrationDefinitionInWorkspace(ctx, body = {}) {
37
+ const bidInput = body.integrationDefinition || body.bid || body;
38
+ if (!bidInput || typeof bidInput !== 'object') {
39
+ throw new Error('integrationDefinition object is required in request body');
40
+ }
41
+ const force = body.force === true;
42
+ const dryRun = body.dryRun === true;
43
+ return runWithWorkspaceContext(ctx, async() => {
44
+ ensureIntegrationDirectory(ctx);
45
+ const bid = withDefinitionHash(bidInput);
46
+ const identity = bid.packageIdentity || {};
47
+ const bidKey = String(identity.systemKey || '').trim();
48
+ if (bidKey && bidKey !== ctx.systemKey) {
49
+ throw new Error(
50
+ `BID packageIdentity.systemKey (${bidKey}) must match path systemKey (${ctx.systemKey})`
51
+ );
52
+ }
53
+ const out = await applyIntegrationDefinition(bid, {
54
+ outputDir: ctx.getIntegrationPath(),
55
+ force,
56
+ dryRun,
57
+ openapiBaseDir: OPENAPI_ENRICHMENT_DIR
58
+ });
59
+ touchWorkspaceMeta(ctx, {
60
+ lastOperation: 'integration-definition-apply',
61
+ format: WORKSPACE_INTEGRATION_FORMAT,
62
+ definitionId: out.definitionId,
63
+ definitionHash: out.definitionHash
64
+ });
65
+ return out;
66
+ });
67
+ }
68
+
69
+ module.exports = { applyIntegrationDefinitionInWorkspace };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ const logger = require('../utils/logger');
4
+ const { formatSuccessLine, formatWarningLine, metadata } = require('../utils/cli-test-layout-chalk');
5
+
6
+ function logKnowledgeImportSummary(systemKey, result, options = {}) {
7
+ if (result.skipped) {
8
+ if (options.verbose === true) {
9
+ logger.log(metadata(`Knowledge sync skipped (${systemKey}): ${result.reason}`));
10
+ }
11
+ return;
12
+ }
13
+ if (result.dryRun) {
14
+ logger.log(formatWarningLine(
15
+ `Role assistant knowledge dry-run: ${result.fileCount} file(s), ${result.recordCount} record(s), datasource ${result.datasourceKey}`
16
+ ));
17
+ return;
18
+ }
19
+ logger.log(formatSuccessLine(
20
+ `Role assistant knowledge synced (${result.datasourceKey}): ${result.recordCount} record(s) from ${result.fileCount} file(s).`
21
+ ));
22
+ }
23
+
24
+ function logDocumentStorageSettingsPushSummary(systemKey, result, options = {}) {
25
+ if (result.skipped) {
26
+ if (options.verbose === true) {
27
+ logger.log(metadata(`Knowledge document storage settings skipped (${systemKey}): ${result.reason}`));
28
+ }
29
+ return;
30
+ }
31
+ if (result.dryRun) {
32
+ logger.log(formatWarningLine(
33
+ `Knowledge document storage settings dry-run (${systemKey}): ${result.documentStorageKey || '(clear)'}`
34
+ ));
35
+ return;
36
+ }
37
+ logger.log(formatSuccessLine(
38
+ `Knowledge document storage settings updated (${systemKey}): ${result.documentStorageKey || '(cleared)'}`
39
+ ));
40
+ }
41
+
42
+ function logKnowledgeExportSummary(systemKey, result, options = {}) {
43
+ if (result.skipped) {
44
+ if (options.verbose === true) {
45
+ logger.log(metadata(`Knowledge export skipped (${systemKey}): ${result.reason}`));
46
+ }
47
+ return;
48
+ }
49
+ logger.log(formatSuccessLine(
50
+ `Role assistant knowledge exported (${result.datasourceKey}): ${result.count} document(s) to ${result.knowledgePath}.`
51
+ ));
52
+ }
53
+
54
+ module.exports = {
55
+ logKnowledgeImportSummary,
56
+ logDocumentStorageSettingsPushSummary,
57
+ logKnowledgeExportSummary
58
+ };
@@ -12,8 +12,6 @@ const path = require('path');
12
12
  const crypto = require('crypto');
13
13
  const fs = require('../internal/fs-real-sync');
14
14
  const yaml = require('js-yaml');
15
- const logger = require('../utils/logger');
16
- const { formatSuccessLine, formatWarningLine, metadata } = require('../utils/cli-test-layout-chalk');
17
15
  const { listDocuments, validateDocuments, bulkDocuments } = require('../api/datasources-extended.api');
18
16
  const {
19
17
  extractDocumentStorageKeyFromAssistantDetails,
@@ -32,6 +30,11 @@ const {
32
30
  knowledgeSearchMetadataFromFrontmatter,
33
31
  splitMarkdownFrontmatter
34
32
  } = require('./knowledge-markdown-frontmatter');
33
+ const {
34
+ logKnowledgeImportSummary,
35
+ logDocumentStorageSettingsPushSummary,
36
+ logKnowledgeExportSummary
37
+ } = require('./knowledge-sync-logging');
35
38
 
36
39
  function resolveSystemNode(input) {
37
40
  if (!input || typeof input !== 'object') {
@@ -80,53 +83,91 @@ async function resolveDocumentStorageKeyFromDataplane(dataplaneUrl, authConfig,
80
83
  }
81
84
 
82
85
  /**
83
- * Push role-assistant.yaml knowledge.documentStorageKey to dataplane Assistants settings.
86
+ * Map role-assistant.yaml debug.* → Assistants settings PATCH fields.
87
+ * includePromptTrace → fullDebugEnabled (LLM prompt capture gate).
88
+ * enabled → debugEnabled (runtime debug traces).
89
+ * @param {Object|null|undefined} debugSection
90
+ * @returns {{ debugEnabled?: boolean, fullDebugEnabled?: boolean }}
91
+ */
92
+ function mapRoleAssistantDebugToAssistantsSettings(debugSection) {
93
+ if (!debugSection || typeof debugSection !== 'object') {
94
+ return {};
95
+ }
96
+ const body = {};
97
+ if (typeof debugSection.enabled === 'boolean') {
98
+ body.debugEnabled = debugSection.enabled;
99
+ }
100
+ if (typeof debugSection.includePromptTrace === 'boolean') {
101
+ body.fullDebugEnabled = debugSection.includePromptTrace;
102
+ }
103
+ return body;
104
+ }
105
+
106
+ /**
107
+ * Push role-assistant.yaml knowledge + debug settings to dataplane Assistants.
84
108
  * Called on upload/deploy so the operator UI and CLI stay aligned.
85
109
  */
86
- async function pushRoleAssistantDocumentStorageSettings(params) {
110
+ function resolveAssistantSettingsPush(params) {
87
111
  const {
88
112
  systemKey,
89
- dataplaneUrl,
90
- authConfig,
91
113
  environment = 'dev',
92
- pathOptions = {},
93
- dryRun = false
114
+ pathOptions = {}
94
115
  } = params;
95
116
  const settingsEnv = resolveRoleAssistantSettingsEnvKey(environment);
96
117
  const settings = readRoleAssistantSettings(systemKey, { required: false, pathOptions });
97
118
  if (!settings) {
98
- return { skipped: true, reason: 'missing-role-assistant-settings' };
119
+ return { result: { skipped: true, reason: 'missing-role-assistant-settings' } };
99
120
  }
100
121
  const section = resolveRoleAssistantEnvironmentSettings(settings, settingsEnv, {
101
122
  required: false
102
123
  });
103
124
  if (!section) {
104
- return { skipped: true, reason: 'missing-env-settings' };
125
+ return { result: { skipped: true, reason: 'missing-env-settings' } };
105
126
  }
106
127
  const documentStorageKey =
107
128
  resolveDocumentStorageKeyFromRoleAssistantSettings(settings, settingsEnv) || '';
108
- if (dryRun) {
109
- return {
110
- skipped: false,
111
- dryRun: true,
112
- documentStorageKey: documentStorageKey || null
113
- };
129
+ const debugBody = mapRoleAssistantDebugToAssistantsSettings(section.debug);
130
+ if (!documentStorageKey && Object.keys(debugBody).length === 0) {
131
+ return { result: { skipped: true, reason: 'no-document-storage-key', documentStorageKey: null } };
114
132
  }
115
- if (documentStorageKey === '') {
116
- // No Knowledge binding in role-assistant.yaml — skip Assistants settings write.
117
- // Draft / not-yet-catalogued digital workers would 404 on PATCH otherwise.
133
+ const patchBody = { ...debugBody };
134
+ if (documentStorageKey) {
135
+ patchBody.documentStorageKey = documentStorageKey;
136
+ }
137
+ return { documentStorageKey, debugBody, patchBody };
138
+ }
139
+
140
+ async function applyAssistantSettingsPush(dataplaneUrl, systemKey, authConfig, settingsPush) {
141
+ const { documentStorageKey, debugBody, patchBody } = settingsPush;
142
+ const hasDocumentStorageKey = documentStorageKey !== '';
143
+ const hasDebugSettings = Object.keys(debugBody).length > 0;
144
+ let response = await updateAssistantSettings(
145
+ dataplaneUrl,
146
+ systemKey,
147
+ authConfig,
148
+ patchBody
149
+ );
150
+ if (response && response.success === false && hasDocumentStorageKey && hasDebugSettings) {
151
+ response = await updateAssistantSettings(
152
+ dataplaneUrl,
153
+ systemKey,
154
+ authConfig,
155
+ debugBody
156
+ );
157
+ if (response && response.success === false) {
158
+ throw new Error(
159
+ response.formattedError ||
160
+ response.error ||
161
+ 'Failed to update Assistants debug settings'
162
+ );
163
+ }
118
164
  return {
119
- skipped: true,
120
- reason: 'no-document-storage-key',
121
- documentStorageKey: null
165
+ skipped: false,
166
+ documentStorageKey: null,
167
+ documentStoragePushFailed: true,
168
+ ...debugBody
122
169
  };
123
170
  }
124
- if (!dataplaneUrl || !authConfig) {
125
- return { skipped: true, reason: 'missing-dataplane-auth' };
126
- }
127
- const response = await updateAssistantSettings(dataplaneUrl, systemKey, authConfig, {
128
- documentStorageKey
129
- });
130
171
  if (response && response.success === false) {
131
172
  throw new Error(
132
173
  response.formattedError ||
@@ -136,10 +177,27 @@ async function pushRoleAssistantDocumentStorageSettings(params) {
136
177
  }
137
178
  return {
138
179
  skipped: false,
139
- documentStorageKey: documentStorageKey || null
180
+ documentStorageKey: documentStorageKey || null,
181
+ ...debugBody
140
182
  };
141
183
  }
142
184
 
185
+ async function pushRoleAssistantDocumentStorageSettings(params) {
186
+ const { systemKey, dataplaneUrl, authConfig, dryRun = false } = params;
187
+ const settingsPush = resolveAssistantSettingsPush(params);
188
+ if (settingsPush.result) {
189
+ return settingsPush.result;
190
+ }
191
+ const { documentStorageKey, debugBody } = settingsPush;
192
+ if (dryRun) {
193
+ return { skipped: false, dryRun: true, documentStorageKey: documentStorageKey || null, ...debugBody };
194
+ }
195
+ if (!dataplaneUrl || !authConfig) {
196
+ return { skipped: true, reason: 'missing-dataplane-auth' };
197
+ }
198
+ return applyAssistantSettingsPush(dataplaneUrl, systemKey, authConfig, settingsPush);
199
+ }
200
+
143
201
  /**
144
202
  * Resolve Knowledge document storage key.
145
203
  * Order: role-assistant.yaml → dataplane Assistants settings → package dataSources.
@@ -408,74 +466,11 @@ async function exportRoleAssistantKnowledge(params) {
408
466
  return { skipped: false, datasourceKey, count: rows.length, knowledgePath, settingsPath };
409
467
  }
410
468
 
411
- function logKnowledgeImportSummary(systemKey, result, options = {}) {
412
- if (result.skipped) {
413
- if (options.verbose === true) {
414
- logger.log(metadata(`Knowledge sync skipped (${systemKey}): ${result.reason}`));
415
- }
416
- return;
417
- }
418
- if (result.dryRun) {
419
- logger.log(
420
- formatWarningLine(
421
- `Role assistant knowledge dry-run: ${result.fileCount} file(s), ${result.recordCount} record(s), datasource ${result.datasourceKey}`
422
- )
423
- );
424
- return;
425
- }
426
- logger.log(
427
- formatSuccessLine(
428
- `Role assistant knowledge synced (${result.datasourceKey}): ${result.recordCount} record(s) from ${result.fileCount} file(s).`
429
- )
430
- );
431
- }
432
-
433
- function logDocumentStorageSettingsPushSummary(systemKey, result, options = {}) {
434
- if (result.skipped) {
435
- if (options.verbose === true) {
436
- logger.log(
437
- metadata(`Knowledge document storage settings skipped (${systemKey}): ${result.reason}`)
438
- );
439
- }
440
- return;
441
- }
442
- if (result.dryRun) {
443
- logger.log(
444
- formatWarningLine(
445
- `Knowledge document storage settings dry-run (${systemKey}): ${
446
- result.documentStorageKey || '(clear)'
447
- }`
448
- )
449
- );
450
- return;
451
- }
452
- logger.log(
453
- formatSuccessLine(
454
- `Knowledge document storage settings updated (${systemKey}): ${
455
- result.documentStorageKey || '(cleared)'
456
- }`
457
- )
458
- );
459
- }
460
-
461
- function logKnowledgeExportSummary(systemKey, result, options = {}) {
462
- if (result.skipped) {
463
- if (options.verbose === true) {
464
- logger.log(metadata(`Knowledge export skipped (${systemKey}): ${result.reason}`));
465
- }
466
- return;
467
- }
468
- logger.log(
469
- formatSuccessLine(
470
- `Role assistant knowledge exported (${result.datasourceKey}): ${result.count} document(s) to ${result.knowledgePath}.`
471
- )
472
- );
473
- }
474
-
475
469
  module.exports = {
476
470
  isDigitalWorkerModel,
477
471
  resolveRoleAssistantSettingsEnvKey,
478
472
  resolveRoleAssistantDocumentStorageKey,
473
+ mapRoleAssistantDebugToAssistantsSettings,
479
474
  pushRoleAssistantDocumentStorageSettings,
480
475
  importRoleAssistantKnowledge,
481
476
  exportRoleAssistantKnowledge,