@x12i/memorix-service 3.0.6 → 3.2.0

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 (178) hide show
  1. package/README.md +8 -1
  2. package/dist/ai-services/associate-prep.d.ts +37 -0
  3. package/dist/ai-services/associate-prep.d.ts.map +1 -0
  4. package/dist/ai-services/associate-prep.js +208 -0
  5. package/dist/ai-services/associate-prep.js.map +1 -0
  6. package/dist/ai-services/chunk-markdown.d.ts +22 -0
  7. package/dist/ai-services/chunk-markdown.d.ts.map +1 -0
  8. package/dist/ai-services/chunk-markdown.js +159 -0
  9. package/dist/ai-services/chunk-markdown.js.map +1 -0
  10. package/dist/ai-services/client.d.ts +31 -0
  11. package/dist/ai-services/client.d.ts.map +1 -0
  12. package/dist/ai-services/client.js +104 -0
  13. package/dist/ai-services/client.js.map +1 -0
  14. package/dist/ai-services/config.d.ts +17 -0
  15. package/dist/ai-services/config.d.ts.map +1 -0
  16. package/dist/ai-services/config.js +44 -0
  17. package/dist/ai-services/config.js.map +1 -0
  18. package/dist/ai-services/normalize-output.d.ts +29 -0
  19. package/dist/ai-services/normalize-output.d.ts.map +1 -0
  20. package/dist/ai-services/normalize-output.js +428 -0
  21. package/dist/ai-services/normalize-output.js.map +1 -0
  22. package/dist/ai-services/skill-execution.d.ts +24 -0
  23. package/dist/ai-services/skill-execution.d.ts.map +1 -0
  24. package/dist/ai-services/skill-execution.js +74 -0
  25. package/dist/ai-services/skill-execution.js.map +1 -0
  26. package/dist/ai-services/types.d.ts +62 -0
  27. package/dist/ai-services/types.d.ts.map +1 -0
  28. package/dist/ai-services/types.js +3 -0
  29. package/dist/ai-services/types.js.map +1 -0
  30. package/dist/app.d.ts.map +1 -1
  31. package/dist/app.js +41 -6
  32. package/dist/app.js.map +1 -1
  33. package/dist/assist/ask.d.ts +35 -0
  34. package/dist/assist/ask.d.ts.map +1 -0
  35. package/dist/assist/ask.js +346 -0
  36. package/dist/assist/ask.js.map +1 -0
  37. package/dist/assist/facts.d.ts +5 -0
  38. package/dist/assist/facts.d.ts.map +1 -0
  39. package/dist/assist/facts.js +237 -0
  40. package/dist/assist/facts.js.map +1 -0
  41. package/dist/assist/merge-ask-proposal.d.ts +9 -0
  42. package/dist/assist/merge-ask-proposal.d.ts.map +1 -0
  43. package/dist/assist/merge-ask-proposal.js +57 -0
  44. package/dist/assist/merge-ask-proposal.js.map +1 -0
  45. package/dist/assist/types.d.ts +60 -0
  46. package/dist/assist/types.d.ts.map +1 -0
  47. package/dist/assist/types.js +57 -0
  48. package/dist/assist/types.js.map +1 -0
  49. package/dist/assist/validate.d.ts +4 -0
  50. package/dist/assist/validate.d.ts.map +1 -0
  51. package/dist/assist/validate.js +319 -0
  52. package/dist/assist/validate.js.map +1 -0
  53. package/dist/cli.js +29 -6
  54. package/dist/cli.js.map +1 -1
  55. package/dist/connector-catalog.d.ts +68 -0
  56. package/dist/connector-catalog.d.ts.map +1 -0
  57. package/dist/connector-catalog.js +258 -0
  58. package/dist/connector-catalog.js.map +1 -0
  59. package/dist/connector-role.d.ts +22 -0
  60. package/dist/connector-role.d.ts.map +1 -0
  61. package/dist/connector-role.js +71 -0
  62. package/dist/connector-role.js.map +1 -0
  63. package/dist/destination-runtime.d.ts +8 -0
  64. package/dist/destination-runtime.d.ts.map +1 -0
  65. package/dist/destination-runtime.js +11 -0
  66. package/dist/destination-runtime.js.map +1 -0
  67. package/dist/execution-target.d.ts +141 -0
  68. package/dist/execution-target.d.ts.map +1 -0
  69. package/dist/execution-target.js +853 -0
  70. package/dist/execution-target.js.map +1 -0
  71. package/dist/generic-connectors.d.ts +41 -0
  72. package/dist/generic-connectors.d.ts.map +1 -0
  73. package/dist/generic-connectors.js +158 -0
  74. package/dist/generic-connectors.js.map +1 -0
  75. package/dist/isolation.d.ts +1 -1
  76. package/dist/isolation.d.ts.map +1 -1
  77. package/dist/jobs-scope.d.ts +21 -0
  78. package/dist/jobs-scope.d.ts.map +1 -0
  79. package/dist/jobs-scope.js +83 -0
  80. package/dist/jobs-scope.js.map +1 -0
  81. package/dist/journey/collect.js +2 -2
  82. package/dist/journey/collect.js.map +1 -1
  83. package/dist/journey/coverage.js +3 -3
  84. package/dist/journey/coverage.js.map +1 -1
  85. package/dist/known-packs.d.ts +1 -1
  86. package/dist/known-packs.d.ts.map +1 -1
  87. package/dist/known-packs.js +1 -1
  88. package/dist/known-packs.js.map +1 -1
  89. package/dist/live.d.ts +13 -1
  90. package/dist/live.d.ts.map +1 -1
  91. package/dist/live.js +866 -2
  92. package/dist/live.js.map +1 -1
  93. package/dist/magit/magit-pack-service.d.ts +112 -0
  94. package/dist/magit/magit-pack-service.d.ts.map +1 -1
  95. package/dist/magit/magit-pack-service.js +494 -12
  96. package/dist/magit/magit-pack-service.js.map +1 -1
  97. package/dist/magit/pack-to-patchset.d.ts +20 -1
  98. package/dist/magit/pack-to-patchset.d.ts.map +1 -1
  99. package/dist/magit/pack-to-patchset.js +82 -9
  100. package/dist/magit/pack-to-patchset.js.map +1 -1
  101. package/dist/magit/package-catalog-bridge.d.ts +58 -0
  102. package/dist/magit/package-catalog-bridge.d.ts.map +1 -0
  103. package/dist/magit/package-catalog-bridge.js +220 -0
  104. package/dist/magit/package-catalog-bridge.js.map +1 -0
  105. package/dist/magit/proposal-to-pack.d.ts +22 -0
  106. package/dist/magit/proposal-to-pack.d.ts.map +1 -0
  107. package/dist/magit/proposal-to-pack.js +68 -0
  108. package/dist/magit/proposal-to-pack.js.map +1 -0
  109. package/dist/mongo-metadata-store.d.ts.map +1 -1
  110. package/dist/mongo-metadata-store.js +9 -3
  111. package/dist/mongo-metadata-store.js.map +1 -1
  112. package/dist/openapi/openapi.json +6262 -3139
  113. package/dist/org-sources.d.ts +14 -0
  114. package/dist/org-sources.d.ts.map +1 -0
  115. package/dist/org-sources.js +31 -0
  116. package/dist/org-sources.js.map +1 -0
  117. package/dist/pipeline-compile.d.ts +67 -1
  118. package/dist/pipeline-compile.d.ts.map +1 -1
  119. package/dist/pipeline-compile.js +455 -29
  120. package/dist/pipeline-compile.js.map +1 -1
  121. package/dist/pipeline-invoke.d.ts +63 -0
  122. package/dist/pipeline-invoke.d.ts.map +1 -0
  123. package/dist/pipeline-invoke.js +114 -0
  124. package/dist/pipeline-invoke.js.map +1 -0
  125. package/dist/pipeline-ports.d.ts +13 -0
  126. package/dist/pipeline-ports.d.ts.map +1 -0
  127. package/dist/pipeline-ports.js +104 -0
  128. package/dist/pipeline-ports.js.map +1 -0
  129. package/dist/platform.d.ts +47 -3
  130. package/dist/platform.d.ts.map +1 -1
  131. package/dist/platform.js +309 -26
  132. package/dist/platform.js.map +1 -1
  133. package/dist/routes/ai-services.d.ts +4 -0
  134. package/dist/routes/ai-services.d.ts.map +1 -0
  135. package/dist/routes/ai-services.js +239 -0
  136. package/dist/routes/ai-services.js.map +1 -0
  137. package/dist/routes/assist.d.ts +4 -0
  138. package/dist/routes/assist.d.ts.map +1 -0
  139. package/dist/routes/assist.js +75 -0
  140. package/dist/routes/assist.js.map +1 -0
  141. package/dist/routes/connector-invocations.d.ts.map +1 -1
  142. package/dist/routes/connector-invocations.js +18 -21
  143. package/dist/routes/connector-invocations.js.map +1 -1
  144. package/dist/routes/data-journey.js +11 -11
  145. package/dist/routes/data-journey.js.map +1 -1
  146. package/dist/routes/intelligence.d.ts.map +1 -1
  147. package/dist/routes/intelligence.js +19 -1
  148. package/dist/routes/intelligence.js.map +1 -1
  149. package/dist/routes/jobs.d.ts +4 -0
  150. package/dist/routes/jobs.d.ts.map +1 -0
  151. package/dist/routes/jobs.js +888 -0
  152. package/dist/routes/jobs.js.map +1 -0
  153. package/dist/routes/memory.d.ts.map +1 -1
  154. package/dist/routes/memory.js +278 -80
  155. package/dist/routes/memory.js.map +1 -1
  156. package/dist/routes/metadata.d.ts.map +1 -1
  157. package/dist/routes/metadata.js +770 -21
  158. package/dist/routes/metadata.js.map +1 -1
  159. package/dist/routes/pipelines.d.ts.map +1 -1
  160. package/dist/routes/pipelines.js +98 -22
  161. package/dist/routes/pipelines.js.map +1 -1
  162. package/dist/routes/relationships.d.ts.map +1 -1
  163. package/dist/routes/relationships.js +109 -2
  164. package/dist/routes/relationships.js.map +1 -1
  165. package/dist/routes/simulation.d.ts +7 -0
  166. package/dist/routes/simulation.d.ts.map +1 -0
  167. package/dist/routes/simulation.js +22 -0
  168. package/dist/routes/simulation.js.map +1 -0
  169. package/dist/runx/client.d.ts +18 -0
  170. package/dist/runx/client.d.ts.map +1 -0
  171. package/dist/runx/client.js +59 -0
  172. package/dist/runx/client.js.map +1 -0
  173. package/dist/simulation.d.ts +26 -0
  174. package/dist/simulation.d.ts.map +1 -0
  175. package/dist/simulation.js +46 -0
  176. package/dist/simulation.js.map +1 -0
  177. package/openapi/openapi.json +6262 -3139
  178. package/package.json +18 -8
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Helpers for org-scoped runtime sources vs Magit/pack sources.
3
+ */
4
+ import type { MetadataPack, SourceDefinition } from "@x12i/memorix-metadata";
5
+ /** True for user/org folder sources that must never enter Magit packs. */
6
+ export declare function isOrgRuntimeFolderSource(entry: {
7
+ id?: string;
8
+ kind?: string;
9
+ connectorRef?: string;
10
+ } | null | undefined): boolean;
11
+ /** Strip org folder sources from a metadata pack (Magit / disk install hygiene). */
12
+ export declare function stripOrgRuntimeSourcesFromPack<T extends MetadataPack>(pack: T): T;
13
+ export declare function asSourceDefinition(raw: Record<string, unknown>, sourceId: string): SourceDefinition;
14
+ //# sourceMappingURL=org-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-sources.d.ts","sourceRoot":"","sources":["../src/org-sources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAE7E,0EAA0E;AAC1E,wBAAgB,wBAAwB,CACtC,KAAK,EAAE;IAAE,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,GAC9E,OAAO,CAUT;AAED,oFAAoF;AACpF,wBAAgB,8BAA8B,CAAC,CAAC,SAAS,YAAY,EACnE,IAAI,EAAE,CAAC,GACN,CAAC,CAKH;AAED,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5B,QAAQ,EAAE,MAAM,GACf,gBAAgB,CAOlB"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Helpers for org-scoped runtime sources vs Magit/pack sources.
3
+ */
4
+ /** True for user/org folder sources that must never enter Magit packs. */
5
+ export function isOrgRuntimeFolderSource(entry) {
6
+ if (!entry || typeof entry !== "object")
7
+ return false;
8
+ const kind = String(entry.kind ?? "");
9
+ const connectorRef = String(entry.connectorRef ?? "");
10
+ const id = String(entry.id ?? "");
11
+ return (kind === "knowx-folder" ||
12
+ connectorRef === "knowx-folder" ||
13
+ /^src_/i.test(id));
14
+ }
15
+ /** Strip org folder sources from a metadata pack (Magit / disk install hygiene). */
16
+ export function stripOrgRuntimeSourcesFromPack(pack) {
17
+ const sources = Array.isArray(pack.sources) ? pack.sources : [];
18
+ const kept = sources.filter((s) => !isOrgRuntimeFolderSource(s));
19
+ if (kept.length === sources.length)
20
+ return pack;
21
+ return { ...pack, sources: kept };
22
+ }
23
+ export function asSourceDefinition(raw, sourceId) {
24
+ return {
25
+ ...raw,
26
+ id: String(raw.id ?? sourceId).trim() || sourceId,
27
+ name: String(raw.name ?? sourceId),
28
+ kind: String(raw.kind ?? "knowx-folder"),
29
+ };
30
+ }
31
+ //# sourceMappingURL=org-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"org-sources.js","sourceRoot":"","sources":["../src/org-sources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,0EAA0E;AAC1E,MAAM,UAAU,wBAAwB,CACtC,KAA+E;IAE/E,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACtC,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,OAAO,CACL,IAAI,KAAK,cAAc;QACvB,YAAY,KAAK,cAAc;QAC/B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAClB,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,8BAA8B,CAC5C,IAAO;IAEP,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC;IACjE,IAAI,IAAI,CAAC,MAAM,KAAK,OAAO,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAChD,OAAO,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,GAA4B,EAC5B,QAAgB;IAEhB,OAAO;QACL,GAAI,GAAwB;QAC5B,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,EAAE,IAAI,QAAQ,CAAC,CAAC,IAAI,EAAE,IAAI,QAAQ;QACjD,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,QAAQ,CAAC;QAClC,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,cAAc,CAAC;KACzC,CAAC;AACJ,CAAC"}
@@ -1,19 +1,85 @@
1
1
  /**
2
2
  * Authoring pack pipeline → runtime v2 shape (pipelineId / stepId / stepKind / portConfig).
3
+ * Supports unified step `target` (service | ai-service | connector | pipeline | runx).
3
4
  */
4
- import { type PipelineDefinition } from "@x12i/memorix-pipeline/v2";
5
+ import { type PipelineDefinition, type StepKind } from "@x12i/memorix-pipeline/v2";
5
6
  export type ServiceDefSlice = {
6
7
  id?: string;
7
8
  serviceId?: string;
9
+ capabilities?: string[];
8
10
  memorixAccess?: unknown;
11
+ /** AI-services/v1 binding (type + item); when set, compile stamps FR invoke config. */
12
+ aiService?: {
13
+ aiServiceType?: string;
14
+ itemId?: string;
15
+ endpointEnv?: string;
16
+ endpoint?: string;
17
+ };
9
18
  invocation?: {
10
19
  operation?: string;
20
+ /** Env var name holding a per-service worker base URL (e.g. KNOWX_EXTRACT_WORKER_URL). */
21
+ endpointEnv?: string;
22
+ /** Alias under invocation for pack authors. */
23
+ aiServiceType?: string;
24
+ itemId?: string;
11
25
  };
12
26
  };
27
+ /** Authoring step target — same surface as Jobs execution targets + runx. */
28
+ export type AuthoringStepTarget = {
29
+ kind: "service";
30
+ serviceId: string;
31
+ } | {
32
+ kind: "ai-service";
33
+ aiServiceType: string;
34
+ itemId: string;
35
+ } | {
36
+ kind: "connector";
37
+ connectorId: string;
38
+ sourceId: string;
39
+ } | {
40
+ kind: "pipeline";
41
+ pipelineId: string;
42
+ } | {
43
+ kind: "runx";
44
+ capabilityId: string;
45
+ };
13
46
  export declare function resolveKnowxWorkerBaseUrl(): string;
47
+ /**
48
+ * Explicit test-only switch for the APS AI Services Simulator.
49
+ * Default OFF — production / normal local stack always uses the real KnowX worker
50
+ * for knowx-extract. Set KNOWX_AI_SIM=1 (or true/on/yes) to enable routing.
51
+ */
52
+ export declare function isKnowxAiSimEnabled(): boolean;
53
+ /**
54
+ * AI-sim base URL for knowx-extract — only when {@link isKnowxAiSimEnabled}.
55
+ * Optional override: KNOWX_EXTRACT_WORKER_URL / MEMORIX_KNOWX_EXTRACT_WORKER_URL.
56
+ */
57
+ export declare function resolveKnowxExtractWorkerUrl(): string | undefined;
58
+ /**
59
+ * Resolve analyze-step worker endpoint.
60
+ * knowx-extract → APS AI sim only when KNOWX_AI_SIM=1 (test-only); otherwise
61
+ * all services use the KnowX control-plane worker.
62
+ */
63
+ export declare function resolveKnowxServiceEndpoint(serviceId: string, opts?: {
64
+ workerBaseUrl?: string;
65
+ endpointEnv?: string;
66
+ }): string;
67
+ /** Prefer behavioral step kind from service capabilities. */
68
+ export declare function preferredStepKindFromCapabilities(caps: string[] | undefined): StepKind;
69
+ /**
70
+ * Build a directed call graph of pipelines that invoke other pipelines via
71
+ * `target.pipeline` / `run-pipeline` steps. Detects cycles when ids are known.
72
+ */
73
+ export declare function detectPipelineCallCycles(pipelines: Array<{
74
+ id?: string;
75
+ pipelineId?: string;
76
+ steps?: Array<Record<string, unknown>>;
77
+ }>): string[];
14
78
  export declare function compileAuthoringPipeline(input: unknown, opts?: {
15
79
  knownServiceIds?: Set<string>;
16
80
  workerBaseUrl?: string;
17
81
  serviceDefs?: ServiceDefSlice[];
82
+ /** When set, validate nested pipeline targets exist. */
83
+ knownPipelineIds?: Set<string>;
18
84
  }): PipelineDefinition;
19
85
  //# sourceMappingURL=pipeline-compile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline-compile.d.ts","sourceRoot":"","sources":["../src/pipeline-compile.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAGL,KAAK,kBAAkB,EAExB,MAAM,2BAA2B,CAAC;AA8BnC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,UAAU,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CACrC,CAAC;AAEF,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;IACL,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;CACjC,GACA,kBAAkB,CAuEpB"}
1
+ {"version":3,"file":"pipeline-compile.d.ts","sourceRoot":"","sources":["../src/pipeline-compile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,QAAQ,EACd,MAAM,2BAA2B,CAAC;AA+CnC,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,uFAAuF;IACvF,SAAS,CAAC,EAAE;QACV,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,UAAU,CAAC,EAAE;QACX,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,0FAA0F;QAC1F,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,+CAA+C;QAC/C,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,6EAA6E;AAC7E,MAAM,MAAM,mBAAmB,GAC3B;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC7D;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3C,wBAAgB,yBAAyB,IAAI,MAAM,CAMlD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAO7C;AAID;;;GAGG;AACH,wBAAgB,4BAA4B,IAAI,MAAM,GAAG,SAAS,CAMjE;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,MAAM,EACjB,IAAI,CAAC,EAAE;IACL,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GACA,MAAM,CAiBR;AAQD,6DAA6D;AAC7D,wBAAgB,iCAAiC,CAC/C,IAAI,EAAE,MAAM,EAAE,GAAG,SAAS,GACzB,QAAQ,CAOV;AA2ND;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,KAAK,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACxC,CAAC,GACD,MAAM,EAAE,CAoDV;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;IACL,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,eAAe,EAAE,CAAC;IAChC,wDAAwD;IACxD,gBAAgB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAChC,GACA,kBAAkB,CAmNpB"}
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Authoring pack pipeline → runtime v2 shape (pipelineId / stepId / stepKind / portConfig).
3
+ * Supports unified step `target` (service | ai-service | connector | pipeline | runx).
3
4
  */
4
5
  import { validatePipelineDefinition, STEP_KINDS, } from "@x12i/memorix-pipeline/v2";
6
+ import { defaultEndpointEnvForType, resolveAiServiceOriginUrl, } from "./ai-services/config.js";
7
+ import { defaultSkillExecutionForItem } from "./ai-services/skill-execution.js";
5
8
  const STEP_ALIASES = {
6
9
  "memory-pull": "memory-pull",
7
10
  pull: "memory-pull",
@@ -20,6 +23,10 @@ const STEP_ALIASES = {
20
23
  validate: "validate",
21
24
  "map-normalize": "map-normalize",
22
25
  "identity-dedupe": "identity-dedupe",
26
+ "run-pipeline": "run-pipeline",
27
+ pipeline: "run-pipeline",
28
+ "call-pipeline": "run-pipeline",
29
+ runx: "runx",
23
30
  };
24
31
  const MUTATING = new Set([
25
32
  "persist-raw",
@@ -27,12 +34,302 @@ const MUTATING = new Set([
27
34
  "relationship-write",
28
35
  "workflow",
29
36
  "narrative",
37
+ "run-pipeline",
38
+ ]);
39
+ const BEHAVIORAL = new Set([
40
+ "enrich",
41
+ "analyze",
42
+ "decide",
43
+ "narrative",
30
44
  ]);
31
45
  export function resolveKnowxWorkerBaseUrl() {
32
46
  return (process.env.KNOWX_WORKER_URL?.trim() ||
33
47
  process.env.MEMORIX_KNOWX_WORKER_URL?.trim() ||
34
48
  `http://127.0.0.1:${process.env.KNOWX_PORT?.trim() || "5110"}`).replace(/\/+$/, "");
35
49
  }
50
+ /**
51
+ * Explicit test-only switch for the APS AI Services Simulator.
52
+ * Default OFF — production / normal local stack always uses the real KnowX worker
53
+ * for knowx-extract. Set KNOWX_AI_SIM=1 (or true/on/yes) to enable routing.
54
+ */
55
+ export function isKnowxAiSimEnabled() {
56
+ const raw = (process.env.KNOWX_AI_SIM?.trim() ||
57
+ process.env.MEMORIX_KNOWX_AI_SIM?.trim() ||
58
+ "").toLowerCase();
59
+ return raw === "1" || raw === "true" || raw === "on" || raw === "yes";
60
+ }
61
+ const KNOWX_AI_SIM_DEFAULT_URL = "http://127.0.0.1:5112";
62
+ /**
63
+ * AI-sim base URL for knowx-extract — only when {@link isKnowxAiSimEnabled}.
64
+ * Optional override: KNOWX_EXTRACT_WORKER_URL / MEMORIX_KNOWX_EXTRACT_WORKER_URL.
65
+ */
66
+ export function resolveKnowxExtractWorkerUrl() {
67
+ if (!isKnowxAiSimEnabled())
68
+ return undefined;
69
+ const fromEnv = process.env.KNOWX_EXTRACT_WORKER_URL?.trim() ||
70
+ process.env.MEMORIX_KNOWX_EXTRACT_WORKER_URL?.trim();
71
+ return (fromEnv || KNOWX_AI_SIM_DEFAULT_URL).replace(/\/+$/, "");
72
+ }
73
+ /**
74
+ * Resolve analyze-step worker endpoint.
75
+ * knowx-extract → APS AI sim only when KNOWX_AI_SIM=1 (test-only); otherwise
76
+ * all services use the KnowX control-plane worker.
77
+ */
78
+ export function resolveKnowxServiceEndpoint(serviceId, opts) {
79
+ const workerDefault = (opts?.workerBaseUrl ?? resolveKnowxWorkerBaseUrl()).replace(/\/+$/, "");
80
+ if (serviceId === "knowx-extract") {
81
+ const extract = resolveKnowxExtractWorkerUrl();
82
+ if (extract)
83
+ return extract;
84
+ return workerDefault;
85
+ }
86
+ // Non-extract services: optional per-service URL env (not the AI-sim toggle).
87
+ if (opts?.endpointEnv && opts.endpointEnv !== "KNOWX_EXTRACT_WORKER_URL") {
88
+ const named = process.env[opts.endpointEnv]?.trim();
89
+ if (named)
90
+ return named.replace(/\/+$/, "");
91
+ }
92
+ return workerDefault;
93
+ }
94
+ function asRecord(value) {
95
+ return value && typeof value === "object" && !Array.isArray(value)
96
+ ? value
97
+ : null;
98
+ }
99
+ /** Prefer behavioral step kind from service capabilities. */
100
+ export function preferredStepKindFromCapabilities(caps) {
101
+ if (!caps?.length)
102
+ return "analyze";
103
+ if (caps.includes("enrich"))
104
+ return "enrich";
105
+ if (caps.includes("decide"))
106
+ return "decide";
107
+ if (caps.includes("narrative"))
108
+ return "narrative";
109
+ if (caps.includes("analyze"))
110
+ return "analyze";
111
+ return "analyze";
112
+ }
113
+ function parseStepTarget(step, rawKind) {
114
+ const target = asRecord(step.target);
115
+ if (target) {
116
+ const kind = String(target.kind ?? "").trim();
117
+ if (kind === "service") {
118
+ const serviceId = String(target.serviceId ?? "").trim();
119
+ if (serviceId)
120
+ return { kind: "service", serviceId };
121
+ }
122
+ if (kind === "ai-service" || kind === "ai") {
123
+ const aiServiceType = String(target.aiServiceType ?? "").trim();
124
+ const itemId = String(target.itemId ?? "").trim();
125
+ if (aiServiceType && itemId) {
126
+ return { kind: "ai-service", aiServiceType, itemId };
127
+ }
128
+ }
129
+ if (kind === "connector") {
130
+ const connectorId = String(target.connectorId ?? "").trim();
131
+ const sourceId = String(target.sourceId ?? "").trim();
132
+ if (connectorId && sourceId) {
133
+ return { kind: "connector", connectorId, sourceId };
134
+ }
135
+ }
136
+ if (kind === "pipeline") {
137
+ const pipelineId = String(target.pipelineId ?? "").trim();
138
+ if (pipelineId)
139
+ return { kind: "pipeline", pipelineId };
140
+ }
141
+ if (kind === "runx") {
142
+ const capabilityId = String(target.capabilityId ?? "").trim();
143
+ if (capabilityId)
144
+ return { kind: "runx", capabilityId };
145
+ }
146
+ }
147
+ // Shorthand fields on the step.
148
+ const pipelineId = String(step.pipelineId ?? "").trim();
149
+ if (pipelineId &&
150
+ (rawKind === "run-pipeline" ||
151
+ rawKind === "pipeline" ||
152
+ rawKind === "call-pipeline" ||
153
+ rawKind === "")) {
154
+ return { kind: "pipeline", pipelineId };
155
+ }
156
+ const capabilityId = String(step.capabilityId ??
157
+ asRecord(step.portConfig)?.capabilityId ??
158
+ "").trim();
159
+ if (capabilityId && (rawKind === "runx" || rawKind === "")) {
160
+ return { kind: "runx", capabilityId };
161
+ }
162
+ const sourceId = String(step.sourceId ?? "").trim();
163
+ const connectorId = String(step.connectorId ??
164
+ asRecord(step.portConfig)?.connectorId ??
165
+ "").trim();
166
+ if (sourceId && connectorId) {
167
+ return { kind: "connector", connectorId, sourceId };
168
+ }
169
+ if (sourceId && (rawKind === "memory-pull" || rawKind === "pull" || rawKind === "collect")) {
170
+ return {
171
+ kind: "connector",
172
+ connectorId: connectorId || "default",
173
+ sourceId,
174
+ };
175
+ }
176
+ const stepAi = asRecord(step.aiService);
177
+ if (stepAi) {
178
+ const aiServiceType = String(stepAi.aiServiceType ?? "").trim();
179
+ const itemId = String(stepAi.itemId ?? "").trim();
180
+ if (aiServiceType && itemId) {
181
+ return { kind: "ai-service", aiServiceType, itemId };
182
+ }
183
+ }
184
+ const serviceId = String(step.serviceId ?? "").trim();
185
+ if (serviceId) {
186
+ return { kind: "service", serviceId };
187
+ }
188
+ return null;
189
+ }
190
+ function stampAiPortConfig(step, opts) {
191
+ const endpoint = resolveAiServiceOriginUrl(opts.aiServiceType, {
192
+ endpoint: opts.endpoint || undefined,
193
+ endpointEnv: opts.endpointEnv,
194
+ }) ?? "";
195
+ if (!endpoint)
196
+ return false;
197
+ const existingPc = step.portConfig ?? {};
198
+ const existingExec = existingPc.execution && typeof existingPc.execution === "object"
199
+ ? existingPc.execution
200
+ : {};
201
+ const skillExec = opts.aiServiceType === "skill"
202
+ ? defaultSkillExecutionForItem(opts.itemId, {
203
+ stepId: String(step.stepId ?? step.id ?? ""),
204
+ })
205
+ : undefined;
206
+ step.portConfig = {
207
+ ...existingPc,
208
+ serviceId: opts.serviceId,
209
+ operation: opts.operation,
210
+ ...(opts.memorixAccess ? { memorixAccess: opts.memorixAccess } : {}),
211
+ endpoint,
212
+ aiServiceType: opts.aiServiceType,
213
+ itemId: opts.itemId,
214
+ invokeProtocol: "ai-services/1",
215
+ ...(skillExec || Object.keys(existingExec).length
216
+ ? {
217
+ execution: {
218
+ ...skillExec,
219
+ ...existingExec,
220
+ },
221
+ }
222
+ : {}),
223
+ };
224
+ return true;
225
+ }
226
+ function stampServicePortConfig(step, svc, serviceId, opts) {
227
+ const operation = (typeof svc?.invocation?.operation === "string"
228
+ ? svc.invocation.operation
229
+ : undefined) ?? serviceId;
230
+ const memorixAccess = Array.isArray(svc?.memorixAccess)
231
+ ? svc.memorixAccess
232
+ : undefined;
233
+ const stepAi = step.aiService && typeof step.aiService === "object"
234
+ ? step.aiService
235
+ : null;
236
+ const aiServiceType = String(stepAi?.aiServiceType ??
237
+ svc?.aiService?.aiServiceType ??
238
+ svc?.invocation?.aiServiceType ??
239
+ "").trim();
240
+ const itemId = String(stepAi?.itemId ?? svc?.aiService?.itemId ?? svc?.invocation?.itemId ?? "").trim();
241
+ if (aiServiceType && itemId) {
242
+ const endpointEnv = String(stepAi?.endpointEnv ??
243
+ svc?.aiService?.endpointEnv ??
244
+ svc?.invocation?.endpointEnv ??
245
+ defaultEndpointEnvForType(aiServiceType) ??
246
+ "").trim() || undefined;
247
+ const explicitEndpoint = String(stepAi?.endpoint ?? svc?.aiService?.endpoint ?? "").trim();
248
+ if (stampAiPortConfig(step, {
249
+ serviceId,
250
+ operation,
251
+ memorixAccess,
252
+ aiServiceType,
253
+ itemId,
254
+ endpointEnv,
255
+ endpoint: explicitEndpoint || undefined,
256
+ })) {
257
+ return;
258
+ }
259
+ // No companion URL — fall through to worker invocation (offline / CI).
260
+ }
261
+ const endpointEnv = typeof svc?.invocation?.endpointEnv === "string"
262
+ ? svc.invocation.endpointEnv.trim()
263
+ : serviceId === "knowx-extract"
264
+ ? "KNOWX_EXTRACT_WORKER_URL"
265
+ : undefined;
266
+ const endpoint = resolveKnowxServiceEndpoint(serviceId, {
267
+ workerBaseUrl: opts?.workerBaseUrl,
268
+ endpointEnv,
269
+ });
270
+ step.portConfig = {
271
+ ...(step.portConfig ?? {}),
272
+ serviceId,
273
+ operation,
274
+ ...(memorixAccess ? { memorixAccess } : {}),
275
+ endpoint,
276
+ };
277
+ }
278
+ /**
279
+ * Build a directed call graph of pipelines that invoke other pipelines via
280
+ * `target.pipeline` / `run-pipeline` steps. Detects cycles when ids are known.
281
+ */
282
+ export function detectPipelineCallCycles(pipelines) {
283
+ const graph = new Map();
284
+ for (const p of pipelines) {
285
+ const id = String(p.pipelineId ?? p.id ?? "").trim();
286
+ if (!id)
287
+ continue;
288
+ if (!graph.has(id))
289
+ graph.set(id, new Set());
290
+ for (const step of p.steps ?? []) {
291
+ const rawKind = String(step.stepKind ?? step.kind ?? step.type ?? "");
292
+ const target = parseStepTarget(step, rawKind);
293
+ if (target?.kind === "pipeline") {
294
+ graph.get(id).add(target.pipelineId);
295
+ }
296
+ if (rawKind === "run-pipeline" ||
297
+ rawKind === "pipeline" ||
298
+ rawKind === "call-pipeline") {
299
+ const child = String(step.pipelineId ??
300
+ asRecord(step.portConfig)?.pipelineId ??
301
+ "").trim();
302
+ if (child)
303
+ graph.get(id).add(child);
304
+ }
305
+ }
306
+ }
307
+ const issues = [];
308
+ const visiting = new Set();
309
+ const visited = new Set();
310
+ const path = [];
311
+ function dfs(node) {
312
+ if (visiting.has(node)) {
313
+ const cycleStart = path.indexOf(node);
314
+ const cycle = [...path.slice(cycleStart), node].join(" → ");
315
+ issues.push(`pipeline call cycle: ${cycle}`);
316
+ return;
317
+ }
318
+ if (visited.has(node))
319
+ return;
320
+ visiting.add(node);
321
+ path.push(node);
322
+ for (const next of graph.get(node) ?? []) {
323
+ dfs(next);
324
+ }
325
+ path.pop();
326
+ visiting.delete(node);
327
+ visited.add(node);
328
+ }
329
+ for (const id of graph.keys())
330
+ dfs(id);
331
+ return issues;
332
+ }
36
333
  export function compileAuthoringPipeline(input, opts) {
37
334
  const raw = structuredClone(input);
38
335
  if (typeof raw.pipelineId !== "string" || !raw.pipelineId.trim()) {
@@ -50,48 +347,177 @@ export function compileAuthoringPipeline(input, opts) {
50
347
  }
51
348
  const issues = [];
52
349
  for (const step of raw.steps ?? []) {
53
- const rawKind = String(step.stepKind ?? step.kind ?? step.type ?? "");
54
- if (rawKind === "invoke-service") {
55
- issues.push(`step ${String(step.stepId ?? step.id)} uses unsupported invoke-service`);
56
- continue;
57
- }
58
- const canonical = STEP_ALIASES[rawKind] ??
59
- (STEP_KINDS.includes(rawKind) ? rawKind : null);
60
- if (!canonical) {
61
- issues.push(`step ${String(step.stepId ?? step.id)} has unknown stepKind "${rawKind}"`);
62
- continue;
63
- }
64
- step.stepKind = canonical;
350
+ const stepLabel = String(step.stepId ?? step.id ?? "step");
65
351
  if (typeof step.stepId !== "string" && typeof step.id === "string") {
66
352
  step.stepId = step.id;
67
353
  }
68
- if (MUTATING.has(canonical) && typeof step.idempotencyKeyExpr !== "string") {
69
- step.idempotencyKeyExpr = `{{runId}}:${String(step.stepId ?? "step")}`;
354
+ let rawKind = String(step.stepKind ?? step.kind ?? step.type ?? "").trim();
355
+ const target = parseStepTarget(step, rawKind);
356
+ // invoke-service alias: only when target/service can be resolved.
357
+ if (rawKind === "invoke-service") {
358
+ if (!target) {
359
+ issues.push(`step ${stepLabel} uses unsupported invoke-service without target/serviceId`);
360
+ continue;
361
+ }
362
+ rawKind = "";
70
363
  }
71
- const serviceId = typeof step.serviceId === "string" ? step.serviceId.trim() : "";
72
- if (serviceId) {
73
- const svc = serviceById.get(serviceId);
74
- const operation = (typeof svc?.invocation?.operation === "string"
75
- ? svc.invocation.operation
76
- : undefined) ?? serviceId;
77
- const memorixAccess = Array.isArray(svc?.memorixAccess)
78
- ? svc.memorixAccess
79
- : undefined;
80
- const endpoint = (opts?.workerBaseUrl ?? resolveKnowxWorkerBaseUrl()).replace(/\/+$/, "");
364
+ // Target-driven kind selection.
365
+ if (target?.kind === "pipeline") {
366
+ step.stepKind = "run-pipeline";
81
367
  step.portConfig = {
82
368
  ...(step.portConfig ?? {}),
83
- serviceId,
84
- operation,
85
- ...(memorixAccess ? { memorixAccess } : {}),
86
- endpoint,
369
+ pipelineId: target.pipelineId,
370
+ wait: true,
371
+ };
372
+ if (opts?.knownPipelineIds && !opts.knownPipelineIds.has(target.pipelineId)) {
373
+ issues.push(`step ${stepLabel} references unknown pipeline "${target.pipelineId}"`);
374
+ }
375
+ }
376
+ else if (target?.kind === "runx") {
377
+ step.stepKind = "runx";
378
+ step.portConfig = {
379
+ ...(step.portConfig ?? {}),
380
+ capabilityId: target.capabilityId,
381
+ invokeProtocol: "runx/1",
382
+ };
383
+ }
384
+ else if (target?.kind === "connector") {
385
+ step.stepKind = "memory-pull";
386
+ if (typeof step.sourceId !== "string")
387
+ step.sourceId = target.sourceId;
388
+ step.portConfig = {
389
+ ...(step.portConfig ?? {}),
390
+ sourceId: target.sourceId,
391
+ connectorId: target.connectorId,
392
+ mode: asRecord(step.portConfig)?.mode ?? "knowledge",
393
+ trigger: asRecord(step.portConfig)?.trigger ??
394
+ "on-demand",
87
395
  };
88
396
  }
397
+ else if (target?.kind === "ai-service") {
398
+ step.stepKind = "analyze";
399
+ const serviceId = String(step.serviceId ?? "").trim() ||
400
+ `ai:${target.aiServiceType}:${target.itemId}`;
401
+ step.serviceId = serviceId;
402
+ const stamped = stampAiPortConfig(step, {
403
+ serviceId,
404
+ operation: serviceId,
405
+ aiServiceType: target.aiServiceType,
406
+ itemId: target.itemId,
407
+ endpointEnv: defaultEndpointEnvForType(target.aiServiceType),
408
+ });
409
+ if (!stamped) {
410
+ step.portConfig = {
411
+ ...(step.portConfig ?? {}),
412
+ serviceId,
413
+ aiServiceType: target.aiServiceType,
414
+ itemId: target.itemId,
415
+ invokeProtocol: "ai-services/1",
416
+ };
417
+ }
418
+ }
419
+ else if (target?.kind === "service") {
420
+ const svc = serviceById.get(target.serviceId);
421
+ const preferred = preferredStepKindFromCapabilities(svc?.capabilities);
422
+ let canonical = null;
423
+ if (rawKind && STEP_ALIASES[rawKind]) {
424
+ canonical = STEP_ALIASES[rawKind];
425
+ }
426
+ else if (rawKind && STEP_KINDS.includes(rawKind)) {
427
+ canonical = rawKind;
428
+ }
429
+ else if (!rawKind || rawKind === "invoke-service") {
430
+ canonical = preferred;
431
+ }
432
+ else {
433
+ // Unknown kind with serviceId → prefer capabilities.
434
+ canonical = preferred;
435
+ }
436
+ // If author picked a non-behavioral kind with a service, keep it only
437
+ // when it's an existing non-target kind; otherwise use preferred.
438
+ if (canonical && !BEHAVIORAL.has(canonical) && rawKind && STEP_ALIASES[rawKind]) {
439
+ // e.g. write-record + serviceId is invalid remote; still allow pass-through kinds.
440
+ }
441
+ else if (!canonical || (rawKind === "" && preferred)) {
442
+ canonical = preferred;
443
+ }
444
+ if (!BEHAVIORAL.has(canonical) && target.kind === "service" && (!rawKind || rawKind === "invoke-service")) {
445
+ canonical = preferred;
446
+ }
447
+ step.stepKind = canonical ?? preferred;
448
+ step.serviceId = target.serviceId;
449
+ stampServicePortConfig(step, svc, target.serviceId, {
450
+ workerBaseUrl: opts?.workerBaseUrl,
451
+ });
452
+ }
453
+ else {
454
+ // Legacy: stepKind only
455
+ const canonical = STEP_ALIASES[rawKind] ??
456
+ (STEP_KINDS.includes(rawKind) ? rawKind : null);
457
+ if (!canonical) {
458
+ issues.push(`step ${stepLabel} has unknown stepKind "${rawKind}"`);
459
+ continue;
460
+ }
461
+ step.stepKind = canonical;
462
+ // memory-pull with sourceId only
463
+ const sourceId = String(step.sourceId ?? "").trim();
464
+ if (canonical === "memory-pull" && sourceId) {
465
+ step.portConfig = {
466
+ ...(step.portConfig ?? {}),
467
+ sourceId,
468
+ mode: asRecord(step.portConfig)?.mode ?? "knowledge",
469
+ trigger: asRecord(step.portConfig)?.trigger ??
470
+ "on-demand",
471
+ };
472
+ }
473
+ // run-pipeline with pipelineId on step
474
+ if (canonical === "run-pipeline") {
475
+ const childId = String(step.pipelineId ??
476
+ asRecord(step.portConfig)?.pipelineId ??
477
+ "").trim();
478
+ if (childId) {
479
+ step.portConfig = {
480
+ ...(step.portConfig ?? {}),
481
+ pipelineId: childId,
482
+ wait: true,
483
+ };
484
+ }
485
+ }
486
+ // runx with capabilityId on step
487
+ if (canonical === "runx") {
488
+ const capId = String(step.capabilityId ??
489
+ asRecord(step.portConfig)?.capabilityId ??
490
+ "").trim();
491
+ if (capId) {
492
+ step.portConfig = {
493
+ ...(step.portConfig ?? {}),
494
+ capabilityId: capId,
495
+ invokeProtocol: "runx/1",
496
+ };
497
+ }
498
+ }
499
+ const serviceId = typeof step.serviceId === "string" ? step.serviceId.trim() : "";
500
+ if (serviceId) {
501
+ stampServicePortConfig(step, serviceById.get(serviceId), serviceId, { workerBaseUrl: opts?.workerBaseUrl });
502
+ }
503
+ }
504
+ const finalKind = step.stepKind;
505
+ if (MUTATING.has(finalKind) && typeof step.idempotencyKeyExpr !== "string") {
506
+ step.idempotencyKeyExpr = `{{runId}}:${String(step.stepId ?? "step")}`;
507
+ }
89
508
  }
90
509
  if (issues.length) {
91
510
  throw new Error(`compilePipeline failed: ${issues.join("; ")}`);
92
511
  }
512
+ // Allow synthetic AI ids and runx through service validation.
513
+ const known = new Set(opts?.knownServiceIds ?? []);
514
+ for (const step of raw.steps ?? []) {
515
+ const sid = String(step.serviceId ?? "").trim();
516
+ if (sid)
517
+ known.add(sid);
518
+ }
93
519
  validatePipelineDefinition(raw, {
94
- knownServiceIds: opts?.knownServiceIds,
520
+ knownServiceIds: known,
95
521
  });
96
522
  return raw;
97
523
  }