@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
@@ -23,6 +23,10 @@ const {
23
23
  alreadyResponded,
24
24
  buildRespondPayload
25
25
  } = require('./test-runner-workhub-answers');
26
+ const {
27
+ shouldApplyBidAfterWait,
28
+ applyCaseBidViaBuilderApi
29
+ } = require('./test-runner-bid-apply');
26
30
 
27
31
  const TERMINAL_TASK = new Set(['completed', 'failed', 'cancelled', 'canceled']);
28
32
  const PLAN_LOST_RE =
@@ -189,7 +193,16 @@ function _approveGateDecision(ctx) {
189
193
  return 'skip';
190
194
  }
191
195
  const approvalState = String(ctx.taskRow?.approvalState || '').trim().toLowerCase();
192
- if (approvalState && approvalState !== 'pending') {
196
+ // Treat empty / pending* as still actionable. Stop only after a terminal decision.
197
+ const terminalDecision = new Set([
198
+ 'approved',
199
+ 'rejected',
200
+ 'cancelled',
201
+ 'canceled',
202
+ 'notrequired',
203
+ 'not_required'
204
+ ]);
205
+ if (approvalState && terminalDecision.has(approvalState)) {
193
206
  return 'stop';
194
207
  }
195
208
  if ((Number(ctx.approveAttempts) || 0) >= 8) {
@@ -225,46 +238,114 @@ async function maybeApproveTask(dataplaneUrl, authConfig, taskId, ctx) {
225
238
  await _attemptApproveTask(dataplaneUrl, authConfig, taskId, ctx);
226
239
  }
227
240
 
241
+ function recordSkippedResponse(interactions, peId, fingerprint, wait) {
242
+ interactions.push({
243
+ type: 'respond-skipped',
244
+ processExecutionId: peId,
245
+ fingerprint,
246
+ questionKey: wait.questionKey || wait.requestKey || null,
247
+ objectRefKind: wait.objectRef?.kind || null,
248
+ reason: 'no-matching-answer-or-synthesis'
249
+ });
250
+ }
251
+
252
+ async function applyBidAfterWait(authConfig, testCase, interactions, peId) {
253
+ try {
254
+ const { loadCaseBidDocument } = require('./test-runner-workhub-answers');
255
+ const emittedBid = loadCaseBidDocument(testCase);
256
+ if (emittedBid && emittedBid.meta && emittedBid.meta.suiteRunId) {
257
+ interactions.push({
258
+ type: 'bid-emit',
259
+ processExecutionId: peId,
260
+ suiteRunId: emittedBid.meta.suiteRunId,
261
+ definitionId: emittedBid.definitionId,
262
+ definitionHash: emittedBid.definitionHash
263
+ });
264
+ }
265
+ const applied = await applyCaseBidViaBuilderApi(authConfig, testCase);
266
+ interactions.push({
267
+ type: 'bid-apply',
268
+ processExecutionId: peId,
269
+ systemKey: applied && applied.systemKey,
270
+ definitionHash: applied && applied.definitionHash,
271
+ suiteRunId: applied && applied.suiteRunId,
272
+ status: applied && applied.status
273
+ });
274
+ } catch (applyErr) {
275
+ interactions.push({
276
+ type: 'bid-apply-error',
277
+ processExecutionId: peId,
278
+ error: applyErr.message || String(applyErr)
279
+ });
280
+ }
281
+ }
282
+
283
+ async function respondToWaitingExecution(
284
+ dataplaneUrl,
285
+ authConfig,
286
+ peId,
287
+ testCase,
288
+ interactions,
289
+ statusRow
290
+ ) {
291
+ const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
292
+ const fingerprint = waitRespondFingerprint(wait);
293
+ if (alreadyResponded(interactions, fingerprint)) {
294
+ return;
295
+ }
296
+ const payload = buildRespondPayload(
297
+ peId,
298
+ wait,
299
+ testCase,
300
+ statusRow,
301
+ interactions,
302
+ resolveObjective(testCase)
303
+ );
304
+ if (!payload) {
305
+ recordSkippedResponse(interactions, peId, fingerprint, wait);
306
+ return;
307
+ }
308
+ await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
309
+ await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
310
+ interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
311
+ if (shouldApplyBidAfterWait(testCase, wait)) {
312
+ await applyBidAfterWait(authConfig, testCase, interactions, peId);
313
+ }
314
+ }
315
+
228
316
  async function maybeRespondWaiting(dataplaneUrl, authConfig, taskRow, testCase, interactions) {
229
317
  const peId = extractPeFromTask(taskRow);
230
318
  if (!peId) {
231
319
  return;
232
320
  }
233
321
  try {
322
+ testCase._activeProcessExecutionId = peId;
234
323
  const statusRow = await getWorkExecutionStatus(dataplaneUrl, authConfig, peId);
235
324
  if (String(statusRow?.status || '').toLowerCase() !== 'waiting') {
236
325
  return;
237
326
  }
238
- const wait = statusRow.wait && typeof statusRow.wait === 'object' ? statusRow.wait : {};
239
- const fingerprint = waitRespondFingerprint(wait);
240
- if (alreadyResponded(interactions, fingerprint)) {
241
- return;
242
- }
243
- const payload = buildRespondPayload(
327
+ await respondToWaitingExecution(
328
+ dataplaneUrl,
329
+ authConfig,
244
330
  peId,
245
- wait,
246
331
  testCase,
247
- statusRow,
248
332
  interactions,
249
- resolveObjective(testCase)
333
+ statusRow
250
334
  );
251
- if (!payload) {
252
- return;
253
- }
254
- await respondWorkExecution(dataplaneUrl, authConfig, peId, payload);
255
- await continueWorkExecution(dataplaneUrl, authConfig, peId, {});
256
- interactions.push({ type: 'respond', processExecutionId: peId, fingerprint });
257
335
  } catch (_err) {
258
336
  // Soft: continue polling task.
259
337
  }
260
338
  }
261
339
 
262
- function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
340
+ function assertTerminalWorkHubTask(last, taskId, timeoutMs, options = {}) {
263
341
  if (!last) {
264
342
  throw new Error(`Work Hub task ${taskId} produced no status rows.`);
265
343
  }
266
344
  const finalStatusToken = String(last?.status || last?.taskStatus || '').trim().toLowerCase();
267
345
  if (!TERMINAL_TASK.has(finalStatusToken)) {
346
+ if (options.soft === true) {
347
+ return last;
348
+ }
268
349
  throw new Error(
269
350
  `Work Hub task ${taskId} did not reach a terminal status within ${timeoutMs}ms (last=${finalStatusToken || 'unknown'}).`
270
351
  );
@@ -275,6 +356,7 @@ function assertTerminalWorkHubTask(last, taskId, timeoutMs) {
275
356
  async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, options) {
276
357
  const timeoutMs = Number(options.pollTimeoutMs) || 180000;
277
358
  const intervalMs = Number(options.pollIntervalMs) || 3000;
359
+ const soft = testCase?.expected?.soft === true;
278
360
  const started = Date.now();
279
361
  let last = null;
280
362
  const timeline = [];
@@ -301,7 +383,7 @@ async function pollWorkHubTask(dataplaneUrl, authConfig, taskId, testCase, optio
301
383
  }
302
384
 
303
385
  return {
304
- finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs),
386
+ finalTask: assertTerminalWorkHubTask(last, taskId, timeoutMs, { soft }),
305
387
  timeline,
306
388
  interactions: ctx.interactions
307
389
  };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Role Assistant suite discovery for multi-case CLI runs.
3
+ * @fileoverview Discover YAML cases under tests/role-assistant/ra-* suite folders
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const fs = require('../internal/fs-real-sync');
10
+ const { bareRoleKey } = require('../utils/role-assistant-paths');
11
+
12
+ function loadNodeFs() {
13
+ if (global.__AIFABRIX_NODE_FS_UNMOCKED__) {
14
+ return global.__AIFABRIX_NODE_FS_UNMOCKED__;
15
+ }
16
+ return typeof jest !== 'undefined' && typeof jest.requireActual === 'function'
17
+ ? jest.requireActual('node:fs')
18
+ : require('node:fs');
19
+ }
20
+
21
+ const nodeFs = loadNodeFs();
22
+
23
+ function exists(p) {
24
+ return nodeFs.existsSync(p) || fs.existsSync(p);
25
+ }
26
+
27
+ function readdir(p) {
28
+ if (nodeFs.existsSync(p)) {
29
+ return nodeFs.readdirSync(p, { withFileTypes: true });
30
+ }
31
+ return fs.readdirSync(p).map((name) => ({
32
+ name,
33
+ isDirectory: () => fs.statSync(path.join(p, name)).isDirectory()
34
+ }));
35
+ }
36
+
37
+ /**
38
+ * Resolve cases root for a role: prefer tests/role-assistant/ra-{bare}, else package tests/.
39
+ * @param {string} roleKey
40
+ * @param {{ casesRoot?: string, cwd?: string }} [options]
41
+ * @returns {string}
42
+ */
43
+ function resolveCasesRoot(roleKey, options = {}) {
44
+ if (options.casesRoot) {
45
+ return path.resolve(String(options.casesRoot));
46
+ }
47
+ const cwd = options.cwd || process.cwd();
48
+ const bare = bareRoleKey(roleKey);
49
+ const preferred = path.join(cwd, 'tests', 'role-assistant', `ra-${bare}`);
50
+ if (exists(preferred)) {
51
+ return preferred;
52
+ }
53
+ const packageTests = path.join(cwd, 'role-assistant', `ra-${bare}`, 'tests');
54
+ if (exists(packageTests)) {
55
+ return packageTests;
56
+ }
57
+ return preferred;
58
+ }
59
+
60
+ /**
61
+ * @param {string} casesRoot
62
+ * @param {{ suite?: string }} [options]
63
+ * @returns {Array<{ suite: string, file: string, absPath: string, caseIdHint: string }>}
64
+ */
65
+ function discoverSuiteCaseFiles(casesRoot, options = {}) {
66
+ if (!exists(casesRoot)) {
67
+ return [];
68
+ }
69
+ const suiteFilter = options.suite ? String(options.suite).trim() : '';
70
+ const rows = [];
71
+ for (const ent of readdir(casesRoot)) {
72
+ if (!ent.isDirectory() || String(ent.name).startsWith('.')) {
73
+ continue;
74
+ }
75
+ if (suiteFilter && ent.name !== suiteFilter) {
76
+ continue;
77
+ }
78
+ const suitePath = path.join(casesRoot, ent.name);
79
+ for (const fileEnt of readdir(suitePath)) {
80
+ const name = fileEnt.name || fileEnt;
81
+ if (typeof name !== 'string' || !/\.(ya?ml)$/i.test(name)) {
82
+ continue;
83
+ }
84
+ const absPath = path.join(suitePath, name);
85
+ rows.push({
86
+ suite: ent.name,
87
+ file: name,
88
+ absPath,
89
+ caseIdHint: path.basename(name, path.extname(name))
90
+ });
91
+ }
92
+ }
93
+ return rows.sort((a, b) => a.absPath.localeCompare(b.absPath));
94
+ }
95
+
96
+ /**
97
+ * Filter discovered rows by --case id or basename.
98
+ * @param {Array<{ absPath: string, caseIdHint: string, file: string }>} rows
99
+ * @param {string} caseSel
100
+ * @returns {Array}
101
+ */
102
+ function filterDiscoveredCases(rows, caseSel) {
103
+ const sel = String(caseSel || '').trim();
104
+ if (!sel) {
105
+ return rows;
106
+ }
107
+ return rows.filter((row) => {
108
+ if (row.absPath === path.resolve(sel) || row.file === sel) {
109
+ return true;
110
+ }
111
+ return row.caseIdHint === sel || row.caseIdHint.endsWith(`-${sel}`) || sel === row.caseIdHint;
112
+ });
113
+ }
114
+
115
+ module.exports = {
116
+ resolveCasesRoot,
117
+ discoverSuiteCaseFiles,
118
+ filterDiscoveredCases
119
+ };
@@ -7,12 +7,12 @@
7
7
  "key": "application-schema",
8
8
  "name": "Application Configuration Schema",
9
9
  "description": "JSON schema for validating AI Fabrix application configuration files",
10
- "version": "1.6.1",
10
+ "version": "1.6.2",
11
11
  "type": "schema",
12
12
  "category": "infrastructure",
13
13
  "author": "AI Fabrix Team",
14
14
  "createdAt": "2024-01-01T00:00:00Z",
15
- "updatedAt": "2026-08-03T00:00:00Z",
15
+ "updatedAt": "2026-08-06T00:00:00Z",
16
16
  "compatibility": {
17
17
  "minVersion": "1.0.0",
18
18
  "maxVersion": "2.0.0",
@@ -30,6 +30,14 @@
30
30
  "external-datasource.schema.json"
31
31
  ],
32
32
  "changelog": [
33
+ {
34
+ "version": "1.6.2",
35
+ "date": "2026-08-06T00:00:00Z",
36
+ "changes": [
37
+ "portalInput.options items may be plain strings (including empty for env-default) or {label, value} objects (matches miso-ui Configuration tab and miso-controller types)."
38
+ ],
39
+ "breaking": false
40
+ },
33
41
  {
34
42
  "version": "1.6.1",
35
43
  "date": "2026-08-03T00:00:00Z",
@@ -281,9 +289,30 @@
281
289
  },
282
290
  "options": {
283
291
  "type": "array",
284
- "description": "Options for select fields",
292
+ "description": "Options for select fields (plain string or {label, value}; empty string allowed for env-default)",
285
293
  "items": {
286
- "type": "string"
294
+ "oneOf": [
295
+ {
296
+ "type": "string"
297
+ },
298
+ {
299
+ "type": "object",
300
+ "required": [
301
+ "label",
302
+ "value"
303
+ ],
304
+ "properties": {
305
+ "label": {
306
+ "type": "string",
307
+ "minLength": 1
308
+ },
309
+ "value": {
310
+ "type": "string"
311
+ }
312
+ },
313
+ "additionalProperties": false
314
+ }
315
+ ]
287
316
  }
288
317
  }
289
318
  },
@@ -399,9 +428,30 @@
399
428
  },
400
429
  "options": {
401
430
  "type": "array",
402
- "description": "Options for select fields",
431
+ "description": "Options for select fields (plain string or {label, value}; empty string allowed for env-default)",
403
432
  "items": {
404
- "type": "string"
433
+ "oneOf": [
434
+ {
435
+ "type": "string"
436
+ },
437
+ {
438
+ "type": "object",
439
+ "required": [
440
+ "label",
441
+ "value"
442
+ ],
443
+ "properties": {
444
+ "label": {
445
+ "type": "string",
446
+ "minLength": 1
447
+ },
448
+ "value": {
449
+ "type": "string"
450
+ }
451
+ },
452
+ "additionalProperties": false
453
+ }
454
+ ]
405
455
  }
406
456
  }
407
457
  },
@@ -7,12 +7,12 @@
7
7
  "key":"external-datasource-schema",
8
8
  "name":"External Data Source Configuration Schema",
9
9
  "description":"JSON schema for validating ExternalDataSource configuration files",
10
- "version":"2.9.5",
10
+ "version":"2.9.6",
11
11
  "type":"schema",
12
12
  "category":"integration",
13
13
  "author":"AI Fabrix Team",
14
14
  "createdAt":"2024-01-01T00:00:00Z",
15
- "updatedAt":"2026-07-31T00:00:00Z",
15
+ "updatedAt":"2026-08-07T00:00:00Z",
16
16
  "compatibility":{
17
17
  "minVersion":"1.0.0",
18
18
  "maxVersion":"3.0.0",
@@ -390,6 +390,12 @@
390
390
  "enum":["documentStorage","vectorStore","recordStorage","messageService","serviceContract","none"],
391
391
  "description":"Entity type identifier. Defines storage semantics and behavior: 'documentStorage' (creates document storage service with vector-storage), 'vectorStore' (external vector storage system), 'recordStorage' (creates record-based system with metadata sync and access rights), 'messageService' (message service for notifications - Slack, Teams, Email), 'serviceContract' (normalized executable service adapter without persistence — requires metadataSchema, fieldMappings, exposed), 'none' (orchestration/composition only). Required field. Canonical values are camelCase."
392
392
  },
393
+ "storageType":{
394
+ "type":"string",
395
+ "enum":["table","view"],
396
+ "default":"table",
397
+ "description":"recordStorage persistence mode. 'table' (default) is ordinary synced record storage. 'view' is calculated read-only CIP perspectives with fetch.source viewpoint SQL; not a permanent PostgreSQL VIEW object."
398
+ },
393
399
  "resourceType":{
394
400
  "type":"string",
395
401
  "pattern":"^[a-z0-9-]+$",
@@ -1307,6 +1313,33 @@
1307
1313
  ]
1308
1314
  }
1309
1315
  },
1316
+ {
1317
+ "if":{
1318
+ "properties":{
1319
+ "storageType":{
1320
+ "const":"view"
1321
+ }
1322
+ },
1323
+ "required":[
1324
+ "storageType"
1325
+ ]
1326
+ },
1327
+ "then":{
1328
+ "properties":{
1329
+ "entityType":{
1330
+ "const":"recordStorage"
1331
+ },
1332
+ "execution":{
1333
+ "type":"object",
1334
+ "properties":{
1335
+ "engine":{
1336
+ "const":"cip"
1337
+ }
1338
+ }
1339
+ }
1340
+ }
1341
+ }
1342
+ },
1310
1343
  {
1311
1344
  "if":{
1312
1345
  "properties":{
@@ -2498,11 +2531,12 @@
2498
2531
  "source":{
2499
2532
  "type":"string",
2500
2533
  "enum":[
2501
- "openapi",
2534
+ "openapi",
2502
2535
  "http",
2503
- "datasource"
2536
+ "datasource",
2537
+ "viewpoint"
2504
2538
  ],
2505
- "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation."
2539
+ "description":"If 'openapi', refer to openapi.operations.*. If 'http', use explicit method/path. If 'datasource', execute another datasource operation. If 'viewpoint', execute saved SQL in fetch.query against records storage (storageType view)."
2506
2540
  },
2507
2541
  "openapiRef":{
2508
2542
  "type":"string",
@@ -2555,16 +2589,7 @@
2555
2589
  "description":"When true, append fetch items to the pipeline accumulator for a following merge step."
2556
2590
  },
2557
2591
  "query":{
2558
- "type":"object",
2559
- "description":"Static query parameters. ABAC/runtime can still override or append at runtime.",
2560
- "additionalProperties":{
2561
- "type":[
2562
- "string",
2563
- "number",
2564
- "boolean",
2565
- "null"
2566
- ]
2567
- }
2592
+ "description":"When source is openapi/http/datasource: static query parameter object. When source is viewpoint: non-empty SQL SELECT string. ABAC/runtime may still append supported filters at execution for viewpoint."
2568
2593
  },
2569
2594
  "bodyTemplate":{
2570
2595
  "description":"Optional static request body or template for POST/PUT/PATCH fetches.",
@@ -2606,7 +2631,20 @@
2606
2631
  "operationId"
2607
2632
  ]
2608
2633
  }
2609
- ]
2634
+ ],
2635
+ "properties":{
2636
+ "query":{
2637
+ "type":"object",
2638
+ "additionalProperties":{
2639
+ "type":[
2640
+ "string",
2641
+ "number",
2642
+ "boolean",
2643
+ "null"
2644
+ ]
2645
+ }
2646
+ }
2647
+ }
2610
2648
  }
2611
2649
  },
2612
2650
  {
@@ -2621,7 +2659,20 @@
2621
2659
  "required":[
2622
2660
  "method",
2623
2661
  "path"
2624
- ]
2662
+ ],
2663
+ "properties":{
2664
+ "query":{
2665
+ "type":"object",
2666
+ "additionalProperties":{
2667
+ "type":[
2668
+ "string",
2669
+ "number",
2670
+ "boolean",
2671
+ "null"
2672
+ ]
2673
+ }
2674
+ }
2675
+ }
2625
2676
  }
2626
2677
  },
2627
2678
  {
@@ -2637,9 +2688,44 @@
2637
2688
  "datasource":{
2638
2689
  "type":"string",
2639
2690
  "description":"Target child datasource key. Omitted when parent manifest composition.datasources has two or more entries; runtime resolves via selector input."
2691
+ },
2692
+ "query":{
2693
+ "type":"object",
2694
+ "additionalProperties":{
2695
+ "type":[
2696
+ "string",
2697
+ "number",
2698
+ "boolean",
2699
+ "null"
2700
+ ]
2701
+ }
2640
2702
  }
2641
2703
  }
2642
2704
  }
2705
+ },
2706
+ {
2707
+ "if":{
2708
+ "properties":{
2709
+ "source":{
2710
+ "const":"viewpoint"
2711
+ }
2712
+ },
2713
+ "required":[
2714
+ "source"
2715
+ ]
2716
+ },
2717
+ "then":{
2718
+ "required":[
2719
+ "query"
2720
+ ],
2721
+ "properties":{
2722
+ "query":{
2723
+ "type":"string",
2724
+ "minLength":1,
2725
+ "description":"Saved read-only SQL SELECT for viewpoint fetch. Parameter-free in first implementation."
2726
+ }
2727
+ }
2728
+ }
2643
2729
  }
2644
2730
  ],
2645
2731
  "additionalProperties":false
@@ -7,12 +7,12 @@
7
7
  "key": "external-system-schema",
8
8
  "name": "External System Configuration Schema",
9
9
  "description": "JSON schema for validating ExternalSystem configuration files",
10
- "version": "1.13.1",
10
+ "version": "1.14.0",
11
11
  "type": "schema",
12
12
  "category": "integration",
13
13
  "author": "AI Fabrix Team",
14
14
  "createdAt": "2024-01-01T00:00:00Z",
15
- "updatedAt": "2026-07-22T00:00:00Z",
15
+ "updatedAt": "2026-08-07T00:00:00Z",
16
16
  "compatibility": {
17
17
  "minVersion": "1.4.0",
18
18
  "maxVersion": "2.0.0",
@@ -27,6 +27,14 @@
27
27
  ],
28
28
  "dependencies": [],
29
29
  "changelog": [
30
+ {
31
+ "version": "1.14.0",
32
+ "date": "2026-08-07T00:00:00Z",
33
+ "changes": [
34
+ "Added authentication.authType system for platform-managed downstream auth (no credential/apiProvider; ownership only — not autonomous system actor)."
35
+ ],
36
+ "breaking": false
37
+ },
30
38
  {
31
39
  "version": "1.13.1",
32
40
  "date": "2026-07-22T00:00:00Z",
@@ -806,6 +814,7 @@
806
814
  "enum": [
807
815
  "credential",
808
816
  "apiProvider",
817
+ "system",
809
818
  "none"
810
819
  ]
811
820
  },
@@ -845,6 +854,36 @@
845
854
  "apiProvider"
846
855
  ]
847
856
  }
857
+ },
858
+ {
859
+ "if": {
860
+ "properties": {
861
+ "authType": {
862
+ "const": "system"
863
+ }
864
+ }
865
+ },
866
+ "then": {
867
+ "properties": {
868
+ "credential": false,
869
+ "apiProvider": false
870
+ }
871
+ }
872
+ },
873
+ {
874
+ "if": {
875
+ "properties": {
876
+ "authType": {
877
+ "const": "none"
878
+ }
879
+ }
880
+ },
881
+ "then": {
882
+ "properties": {
883
+ "credential": false,
884
+ "apiProvider": false
885
+ }
886
+ }
848
887
  }
849
888
  ]
850
889
  }
@@ -1039,7 +1078,7 @@
1039
1078
  "default": false
1040
1079
  },
1041
1080
  "authentication": {
1042
- "description": "Nested authentication: authType credential (local template), apiProvider (Miso delegated), or none. Deprecated — prefer authentications[] with usage.",
1081
+ "description": "Nested authentication: authType credential (local template), apiProvider (Miso delegated), system (platform-managed downstream auth ownership), or none (downstream anonymous). Deprecated — prefer authentications[] with usage.",
1043
1082
  "oneOf": [
1044
1083
  {
1045
1084
  "type": "object",
@@ -1073,6 +1112,18 @@
1073
1112
  },
1074
1113
  "additionalProperties": false
1075
1114
  },
1115
+ {
1116
+ "type": "object",
1117
+ "required": [
1118
+ "authType"
1119
+ ],
1120
+ "properties": {
1121
+ "authType": {
1122
+ "const": "system"
1123
+ }
1124
+ },
1125
+ "additionalProperties": false
1126
+ },
1076
1127
  {
1077
1128
  "type": "object",
1078
1129
  "required": [