@exellix/graph-engine 7.0.1 → 7.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 (31) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/dist/src/errors/exellixGraphErrorCodes.d.ts +8 -4
  3. package/dist/src/errors/exellixGraphErrorCodes.js +7 -3
  4. package/dist/src/index.d.ts +8 -2
  5. package/dist/src/index.js +8 -2
  6. package/dist/src/inspection/contractInspection.js +12 -2
  7. package/dist/src/runtime/ExellixGraphRuntime.d.ts +3 -1
  8. package/dist/src/runtime/ExellixGraphRuntime.js +10 -5
  9. package/dist/src/runtime/aiTasksStrategyPhases.d.ts +1 -0
  10. package/dist/src/runtime/aiTasksStrategyPhases.js +4 -1
  11. package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +4 -2
  12. package/dist/src/runtime/canonicalModelUsed.js +6 -1
  13. package/dist/src/runtime/graphAiModelConfig.d.ts +37 -0
  14. package/dist/src/runtime/graphAiModelConfig.js +119 -0
  15. package/dist/src/runtime/modelAliasContract.d.ts +8 -44
  16. package/dist/src/runtime/modelAliasContract.js +8 -127
  17. package/dist/src/runtime/modelConfigSelection.js +8 -5
  18. package/dist/src/runtime/resolveModelConfigForNode.d.ts +6 -5
  19. package/dist/src/runtime/resolveModelConfigForNode.js +9 -9
  20. package/dist/src/runtime/stepRetry.d.ts +5 -0
  21. package/dist/src/runtime/stepRetry.js +16 -10
  22. package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +4 -4
  23. package/dist/src/runtime/taskNodeRunTaskPreflight.js +8 -14
  24. package/dist/src/runtime/validateCanonicalGraphDocument.js +7 -8
  25. package/dist/src/types/options.d.ts +2 -2
  26. package/dist/src/types/refs.d.ts +9 -4
  27. package/dist/testkit/RealTasksClient.js +5 -1
  28. package/dist/testkit/testModelAliasRuntime.d.ts +7 -7
  29. package/dist/testkit/testModelAliasRuntime.js +10 -20
  30. package/package.json +9 -6
  31. package/scripts/patch-ai-tasks-xynthesis-export.mjs +22 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.1.2
4
+
5
+ ### Fixed
6
+
7
+ - **Step retry token bump:** Reads `llmCall.maxTokensCap` (ai-tasks v8) before legacy `maxTokens` / `max_tokens`, and writes bumps back as `maxTokensCap` on retry.
8
+ - **Dependency workaround:** `postinstall` patches `@exellix/ai-tasks@8.0.7` broken re-export of `MODEL_CAPABILITIES` from `@exellix/xynthesis` (removed in xynthesis 4.x) until upstream publishes a fix.
9
+
10
+ ## 7.1.0
11
+
12
+ ### Breaking
13
+
14
+ - **Three-phase `GraphAiModelConfig`:** `{ preActionModel, skillModel, postActionModel }` replaces `{ xynthesisModel, skillModel }`. `postActionModel` is required; POST utilities no longer reuse `skillModel`.
15
+ - **AI profile resolution:** Graph JSON profile names resolve via `@x12i/ai-profiles` (bundled registry). `runtime.aliasConfig` is no longer required.
16
+ - **Phase routing:** PRE / MAIN / POST `runTask` calls receive independent models on the ai-tasks wire (`toRunTaskModelConfigForPhase`).
17
+ - **Preflight async:** `buildTaskNodeRunTaskRequest` and `validateTaskNodeRunTaskConfig` are async.
18
+
19
+ ### Added
20
+
21
+ - Dependency `@x12i/ai-profiles`.
22
+ - Exports: `resolveGraphAiModelConfig`, `toRunTaskModelConfigForPhase`, `DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG`, `isGraphAiProfileName`, `MODEL_CONFIG_INCOMPLETE`, `MODEL_PROFILE_UNRESOLVED`.
23
+
24
+ ## 7.0.3
25
+
26
+ ### Added
27
+
28
+ - **Default model ids (JSON-serializable):** `DEFAULT_GRAPH_AI_MODEL_IDS` (`google/gemma-4-31b-it` synthesis, `deepseek/deepseek-r1` skills), `DEFAULT_RUNTIME_ALIAS_CONFIG`, and `DEFAULT_SYNTHESIS_MODEL_ALIAS` / `DEFAULT_SKILL_MODEL_ALIAS` for implicit fallback (`default-xynthesis`, `default-skill`). Testkit `buildTestAliasConfig` merges `DEFAULT_RUNTIME_ALIAS_CONFIG` automatically.
29
+
3
30
  ## 7.0.0
4
31
 
5
32
  ### Breaking
@@ -28,10 +28,14 @@ export declare enum ExellixGraphErrorCode {
28
28
  GRAPH_ENTRY_DATA_FILTERS_REJECTED = "GRAPH_ENTRY_DATA_FILTERS_REJECTED",
29
29
  /** MAIN readiness guard rejected empty execution input or synthesis context before runTask. */
30
30
  MAIN_READINESS_FAILED = "MAIN_READINESS_FAILED",
31
- /** `executeGraph` called without a non-empty `runtime.aliasConfig`. */
31
+ /** @deprecated Legacy alias map unused since 7.1. */
32
32
  MODEL_ALIAS_CONFIG_REQUIRED = "MODEL_ALIAS_CONFIG_REQUIRED",
33
- /** Selected model alias has no concrete model in the merged runtime alias map. */
33
+ /** @deprecated Legacy alias map unused since 7.1. */
34
34
  MODEL_ALIAS_UNRESOLVED = "MODEL_ALIAS_UNRESOLVED",
35
- /** Graph JSON `modelConfig` contains a provider model id instead of an alias name. */
36
- NON_CANONICAL_MODEL_CONFIG = "NON_CANONICAL_MODEL_CONFIG"
35
+ /** Graph JSON `modelConfig` contains a provider model id instead of an AI profile name. */
36
+ NON_CANONICAL_MODEL_CONFIG = "NON_CANONICAL_MODEL_CONFIG",
37
+ /** `modelConfig` is missing or not exactly `{ preActionModel, skillModel, postActionModel }`. */
38
+ MODEL_CONFIG_INCOMPLETE = "MODEL_CONFIG_INCOMPLETE",
39
+ /** An AI profile name could not be resolved via `@x12i/ai-profiles`. */
40
+ MODEL_PROFILE_UNRESOLVED = "MODEL_PROFILE_UNRESOLVED"
37
41
  }
@@ -29,10 +29,14 @@ export var ExellixGraphErrorCode;
29
29
  ExellixGraphErrorCode["GRAPH_ENTRY_DATA_FILTERS_REJECTED"] = "GRAPH_ENTRY_DATA_FILTERS_REJECTED";
30
30
  /** MAIN readiness guard rejected empty execution input or synthesis context before runTask. */
31
31
  ExellixGraphErrorCode["MAIN_READINESS_FAILED"] = "MAIN_READINESS_FAILED";
32
- /** `executeGraph` called without a non-empty `runtime.aliasConfig`. */
32
+ /** @deprecated Legacy alias map unused since 7.1. */
33
33
  ExellixGraphErrorCode["MODEL_ALIAS_CONFIG_REQUIRED"] = "MODEL_ALIAS_CONFIG_REQUIRED";
34
- /** Selected model alias has no concrete model in the merged runtime alias map. */
34
+ /** @deprecated Legacy alias map unused since 7.1. */
35
35
  ExellixGraphErrorCode["MODEL_ALIAS_UNRESOLVED"] = "MODEL_ALIAS_UNRESOLVED";
36
- /** Graph JSON `modelConfig` contains a provider model id instead of an alias name. */
36
+ /** Graph JSON `modelConfig` contains a provider model id instead of an AI profile name. */
37
37
  ExellixGraphErrorCode["NON_CANONICAL_MODEL_CONFIG"] = "NON_CANONICAL_MODEL_CONFIG";
38
+ /** `modelConfig` is missing or not exactly `{ preActionModel, skillModel, postActionModel }`. */
39
+ ExellixGraphErrorCode["MODEL_CONFIG_INCOMPLETE"] = "MODEL_CONFIG_INCOMPLETE";
40
+ /** An AI profile name could not be resolved via `@x12i/ai-profiles`. */
41
+ ExellixGraphErrorCode["MODEL_PROFILE_UNRESOLVED"] = "MODEL_PROFILE_UNRESOLVED";
38
42
  })(ExellixGraphErrorCode || (ExellixGraphErrorCode = {}));
@@ -38,8 +38,14 @@ export type { PathSegment } from './runtime/pathExpr.js';
38
38
  export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
39
39
  export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
40
40
  export type { TaskNodeConditionsEvalDeps, TaskNodeConditionsEvalResult, TaskNodeConditionEvalContext, } from './runtime/taskNodeConditionsEvaluation.js';
41
- export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, mergeAliasConfigs, } from './runtime/resolveModelConfigForNode.js';
42
- export { DEFAULT_MODEL_ALIAS, DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, isModelAliasString, looksLikeConcreteModelId, assertGraphModelAliasString, collectModelAliasesUsedInGraph, assertRuntimeAliasConfigPresent, assertRuntimeAliasConfigCoversGraph, } from './runtime/modelAliasContract.js';
41
+ export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
42
+ export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, type RunTaskModelConfigWire, type EngineModelPhase, } from './runtime/graphAiModelConfig.js';
43
+ /** @deprecated Use {@link resolveGraphAiModelConfig}. */
44
+ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
45
+ /** @deprecated Use {@link isGraphAiProfileName}. */
46
+ export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
47
+ /** @deprecated Use {@link assertGraphAiProfileNameString}. */
48
+ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
43
49
  export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
44
50
  export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
45
51
  export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
package/dist/src/index.js CHANGED
@@ -28,8 +28,14 @@ export { mergeMemory } from './runtime/memory.js';
28
28
  export { selectByPath, writeByPath, parsePath } from './runtime/pathExpr.js';
29
29
  export { evaluateStructuredDataFilters, evaluateDataFilterPredicate, getStructuredDataFilterPathViolations, isStructuredDataFiltersV1, } from './runtime/dataFiltersEvaluation.js';
30
30
  export { evaluateTaskNodeConditions, evaluateConditionWhen, applyConditionNegate, } from './runtime/taskNodeConditionsEvaluation.js';
31
- export { resolveModelConfigForNode, resolveGraphAiModelConfigAliases, mergeAliasConfigs, } from './runtime/resolveModelConfigForNode.js';
32
- export { DEFAULT_MODEL_ALIAS, DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, isModelAliasString, looksLikeConcreteModelId, assertGraphModelAliasString, collectModelAliasesUsedInGraph, assertRuntimeAliasConfigPresent, assertRuntimeAliasConfigCoversGraph, } from './runtime/modelAliasContract.js';
31
+ export { resolveModelConfigForNode, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './runtime/resolveModelConfigForNode.js';
32
+ export { looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, } from './runtime/graphAiModelConfig.js';
33
+ /** @deprecated Use {@link resolveGraphAiModelConfig}. */
34
+ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './runtime/graphAiModelConfig.js';
35
+ /** @deprecated Use {@link isGraphAiProfileName}. */
36
+ export { isGraphAiProfileName as isModelAliasString } from './runtime/graphAiModelConfig.js';
37
+ /** @deprecated Use {@link assertGraphAiProfileNameString}. */
38
+ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './runtime/graphAiModelConfig.js';
33
39
  export { isGraphAiModelConfig, isModelConfigSelection, isEmptyConditionWhen, conditionWhenSignature, countDefaultModelConfigCases, } from './runtime/modelConfigSelection.js';
34
40
  export { GRAPH_ENGINE_MEMORY_PATH_ROOTS, splitGraphEngineMemoryPath, isAllowedGraphEngineMemoryPath, graphEngineMemoryPathValidationMessage, } from './runtime/graphEngineMemoryPaths.js';
35
41
  export { buildRunTaskMainInput, extractCallerInputsBag, buildGraphEngineMemoryResolutionRoot, buildGraphEngineMemoryResolutionRootFromWorkingMemory, resolveGraphEngineMemoryPathValue, } from './runtime/resolveGraphEngineMemoryPaths.js';
@@ -293,9 +293,19 @@ function buildOutputContract(node) {
293
293
  const tc = task.taskConfiguration;
294
294
  const webCfg = tc?.aiTaskProfile;
295
295
  const webScoping = webCfg?.webScoping;
296
+ const narrixCfg = tc?.narrix;
296
297
  const webWrites = [];
297
- if (webScoping && webScoping.enabled === true) {
298
- webWrites.push({ path: 'webContext', source: 'taskConfiguration.aiTaskProfile.webScoping → executeNode (runtime-known)' });
298
+ if (narrixCfg?.enableWebScope === true) {
299
+ webWrites.push({
300
+ path: 'webContext',
301
+ source: 'taskConfiguration.narrix.enableWebScope → @exellix/ai-tasks (runtime-known)',
302
+ });
303
+ }
304
+ else if (webScoping && webScoping.enabled === true) {
305
+ webWrites.push({
306
+ path: 'webContext',
307
+ source: 'taskConfiguration.aiTaskProfile.webScoping → executeNode (runtime-known)',
308
+ });
299
309
  }
300
310
  const synthWrites = [];
301
311
  const inputSynth = webCfg?.inputSynthesis;
@@ -91,7 +91,9 @@ export interface GraphRuntimeObject extends HostExecuteGraphRunOptions {
91
91
  taskVariables?: Record<string, unknown>;
92
92
  /** Runtime default model selection (`cases`); overrides GraphModelObject.modelConfig. */
93
93
  modelConfig?: ModelConfigSelection;
94
- /** Runtime alias map used to resolve modelConfig values before runTask. */
94
+ /**
95
+ * @deprecated Unused since 7.1 — model profiles resolve via `@x12i/ai-profiles`.
96
+ */
95
97
  aliasConfig?: GraphModelAliasConfig;
96
98
  /** Task-node runtime overrides keyed by node id. */
97
99
  nodes?: Record<string, TaskNodeRuntimeObject>;
@@ -34,7 +34,7 @@ import { evaluateGraphPredicate } from "./predicates.js";
34
34
  import { evaluateStructuredDataFilters } from "./dataFiltersEvaluation.js";
35
35
  import { evaluateTaskNodeConditions } from "./taskNodeConditionsEvaluation.js";
36
36
  import { resolveModelConfigForNode } from "./resolveModelConfigForNode.js";
37
- import { assertRuntimeAliasConfigCoversGraph } from "./modelAliasContract.js";
37
+ import { toRunTaskModelConfigForPhase } from "./graphAiModelConfig.js";
38
38
  import { createRunx } from "@x12i/runx";
39
39
  import { selectByPath } from "./pathExpr.js";
40
40
  import { createGraphStartEvent, createGraphCompleteEvent, createGraphFailEvent, createNodeStartEvent, createNodeCompleteEvent, createNodeFailEvent, } from "./events.js";
@@ -393,7 +393,11 @@ export function createExellixGraphRuntime(opts) {
393
393
  : {}),
394
394
  kind: "finalizer",
395
395
  },
396
- ...(input.modelConfig != null ? { modelConfig: input.modelConfig } : {}),
396
+ ...(input.modelConfig != null
397
+ ? {
398
+ modelConfig: toRunTaskModelConfigForPhase(input.modelConfig, 'pre'),
399
+ }
400
+ : {}),
397
401
  ...(effectiveLlmCall != null ? { llmCall: effectiveLlmCall } : {}),
398
402
  ...(forwardRunTaskTrace ? { executionMode: "trace" } : {}),
399
403
  ...((input.runTaskDiagnostics ?? opts.runTaskDiagnostics) != null
@@ -603,6 +607,9 @@ export function createExellixGraphRuntime(opts) {
603
607
  input.eventEmitter.emit(createNodeStartEvent(lifecycleJobId, input.graphId, graphRunTaskId, input.node, skillKey, undefined, input.jobCorrelation));
604
608
  }
605
609
  const effectiveModelConfig = input.modelConfig;
610
+ const mainRunTaskModelConfig = effectiveModelConfig != null
611
+ ? toRunTaskModelConfigForPhase(effectiveModelConfig, 'main')
612
+ : undefined;
606
613
  // DEBUG: Verify execution object before sending
607
614
  if (process.env.DEBUG_EXECUTION_MEMORY === 'true') {
608
615
  console.log(`[DEBUG] Node ${input.node?.id}: execution object =`, JSON.stringify(input.execution, null, 2));
@@ -830,7 +837,7 @@ export function createExellixGraphRuntime(opts) {
830
837
  ? input.node.taskConfiguration.aiTasksOutputValidation
831
838
  : undefined,
832
839
  diagnostics: (input.runTaskDiagnostics ?? opts.runTaskDiagnostics),
833
- modelConfig: effectiveModelConfig ?? undefined,
840
+ modelConfig: mainRunTaskModelConfig,
834
841
  llmCall: effectiveLlmCall,
835
842
  identity: buildRunTaskIdentityEnvelope({
836
843
  base: runTaskIdentityBase,
@@ -1152,7 +1159,6 @@ export function createExellixGraphRuntime(opts) {
1152
1159
  }
1153
1160
  const resolvedGraphId = String(resolvedGraphIdRaw);
1154
1161
  assertCanonicalGraphDocument(graph, { jobId, graphId: resolvedGraphId });
1155
- assertRuntimeAliasConfigCoversGraph(graph, merged.aliasConfig, { modelConfig: merged.modelConfig, nodes: merged.nodes }, { jobId, graphId: resolvedGraphId });
1156
1162
  let runxClient = opts.runx;
1157
1163
  if (graphNeedsRunxClient(graph, merged.modelConfig)) {
1158
1164
  if (!runxClient) {
@@ -1569,7 +1575,6 @@ export function createExellixGraphRuntime(opts) {
1569
1575
  nodeModelConfig: node.taskConfiguration?.modelConfig,
1570
1576
  runtimeModelConfig: merged.modelConfig,
1571
1577
  graphModelConfig: graph.modelConfig,
1572
- runtimeAliasConfig: merged.aliasConfig,
1573
1578
  conditionCtx: buildPredicateEvalContextForNode({
1574
1579
  executionMemory: currentExecution,
1575
1580
  jobMemory: currentJobMemory,
@@ -25,6 +25,7 @@ export type RunEngineAiTasksStrategyPhaseArgs = {
25
25
  taskMemory?: unknown;
26
26
  jobContext?: Record<string, unknown>;
27
27
  prevNodeId?: string;
28
+ /** Resolved three-phase config; routed to the correct ai-tasks slot for this strategy phase. */
28
29
  modelConfig?: GraphAiModelConfig;
29
30
  llmCall?: RunTaskRequest['llmCall'];
30
31
  runTaskIdentity?: Record<string, unknown>;
@@ -1,3 +1,4 @@
1
+ import { toRunTaskModelConfigForPhase } from './graphAiModelConfig.js';
1
2
  import { buildAiTasksRunTaskRequest } from './buildAiTasksRunTaskRequest.js';
2
3
  import { buildRunTaskIdentityEnvelope } from './runTaskAugments.js';
3
4
  import { runTaskResponseSucceeded } from './runTaskResponse.js';
@@ -53,7 +54,9 @@ export async function runEngineAiTasksStrategyPhase(args) {
53
54
  prevNodeId: args.prevNodeId,
54
55
  executionPipeline: [{ phase: 'main', type: 'direct' }],
55
56
  executionStrategies: [],
56
- modelConfig: args.modelConfig,
57
+ modelConfig: args.modelConfig != null
58
+ ? toRunTaskModelConfigForPhase(args.modelConfig, args.phase)
59
+ : undefined,
57
60
  llmCall: args.llmCall ?? undefined,
58
61
  identity,
59
62
  ...(args.forwardRunTaskTrace ? { executionMode: 'trace' } : {}),
@@ -9,7 +9,8 @@
9
9
  */
10
10
  import type { LlmCallConfig, RunTaskRequest } from '@exellix/ai-tasks';
11
11
  import type { SmartInputConfig, XynthesizedMemory } from '../types/aiTasksDerivedTypes.js';
12
- import type { GraphAiModelConfig, Job } from '../types/refs.js';
12
+ import type { Job } from '../types/refs.js';
13
+ import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
13
14
  import type { ExecutionStepOption } from '../types/options.js';
14
15
  import type { ResolvedNarrixWirePayload } from '../types/narrix.js';
15
16
  /** Reads task-node `taskConfiguration` keys forwarded to `@exellix/ai-tasks` `RunTaskRequest` (runtime strategy / Narrix wiring). */
@@ -45,7 +46,8 @@ export type BuildAiTasksRunTaskRequestArgs = {
45
46
  narrix?: ResolvedNarrixWirePayload;
46
47
  outputValidation?: RunTaskRequest['outputValidation'];
47
48
  diagnostics?: RunTaskRequest['diagnostics'];
48
- modelConfig?: GraphAiModelConfig;
49
+ /** Resolved ai-tasks slot pair (`xynthesisModel` / `skillModel`) for this runTask phase. */
50
+ modelConfig?: RunTaskModelConfigWire;
49
51
  llmCall?: LlmCallConfig;
50
52
  identity?: Record<string, unknown>;
51
53
  executionMode?: 'default' | 'trace';
@@ -14,7 +14,12 @@ export function resolveCanonicalModelUsed(response) {
14
14
  return direct.trim();
15
15
  const rawConfig = meta.rawConfig;
16
16
  if (isPlainObject(rawConfig)) {
17
- const fromRaw = rawConfig.modelUsed ?? rawConfig.model ?? rawConfig.skillModel ?? rawConfig.xynthesisModel;
17
+ const fromRaw = rawConfig.modelUsed ??
18
+ rawConfig.model ??
19
+ rawConfig.skillModel ??
20
+ rawConfig.preActionModel ??
21
+ rawConfig.postActionModel ??
22
+ rawConfig.xynthesisModel;
18
23
  if (typeof fromRaw === 'string' && fromRaw.trim() !== '')
19
24
  return fromRaw.trim();
20
25
  }
@@ -0,0 +1,37 @@
1
+ import type { GraphAiModelConfig } from '../types/refs.js';
2
+ /** Wire shape expected by `@exellix/ai-tasks` `RunTaskRequest.modelConfig`. */
3
+ export type RunTaskModelConfigWire = {
4
+ xynthesisModel: string;
5
+ skillModel: string;
6
+ };
7
+ /** Profile names used when no modelConfig tier resolves. */
8
+ export declare const DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG: GraphAiModelConfig;
9
+ /**
10
+ * Canonical default concrete provider model ids (JSON-serializable).
11
+ * Derived from bundled `@x12i/ai-profiles` defaults for {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
12
+ */
13
+ export declare const DEFAULT_GRAPH_AI_MODEL_IDS: GraphAiModelConfig;
14
+ export type GraphAiModelConfigResolveContext = {
15
+ jobId?: string;
16
+ graphId?: string;
17
+ nodeId?: string;
18
+ };
19
+ /**
20
+ * True when a value looks like a concrete provider model id (host-resolved / post profile resolution).
21
+ */
22
+ export declare function looksLikeConcreteModelId(value: string): boolean;
23
+ /**
24
+ * True when a string is a valid graph-authored AI profile name (not a provider model id).
25
+ */
26
+ export declare function isGraphAiProfileName(value: unknown): value is string;
27
+ export declare function assertGraphAiProfileNameString(value: unknown, path: string, context?: GraphAiModelConfigResolveContext): asserts value is string;
28
+ /**
29
+ * Resolves graph/runtime {@link GraphAiModelConfig} to concrete provider model ids.
30
+ * Profile names resolve via bundled `@x12i/ai-profiles`; concrete ids pass through unchanged.
31
+ */
32
+ export declare function resolveGraphAiModelConfig(modelConfig: GraphAiModelConfig | undefined, context?: GraphAiModelConfigResolveContext): Promise<GraphAiModelConfig>;
33
+ export type EngineModelPhase = 'pre' | 'main' | 'post';
34
+ /**
35
+ * Maps resolved three-phase graph config to ai-tasks slot pair for a single runTask phase.
36
+ */
37
+ export declare function toRunTaskModelConfigForPhase(config: GraphAiModelConfig, phase: EngineModelPhase): RunTaskModelConfigWire;
@@ -0,0 +1,119 @@
1
+ import { resolveAIProfile } from '@x12i/ai-profiles';
2
+ import { ExellixGraphError } from '../errors/ExellixGraphError.js';
3
+ import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
4
+ import { isGraphAiModelConfig } from './modelConfigSelection.js';
5
+ /** Profile names used when no modelConfig tier resolves. */
6
+ export const DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG = {
7
+ preActionModel: 'cheap',
8
+ skillModel: 'balanced',
9
+ postActionModel: 'cheap',
10
+ };
11
+ /**
12
+ * Canonical default concrete provider model ids (JSON-serializable).
13
+ * Derived from bundled `@x12i/ai-profiles` defaults for {@link DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG}.
14
+ */
15
+ export const DEFAULT_GRAPH_AI_MODEL_IDS = {
16
+ preActionModel: 'google/gemini-2.5-flash-lite',
17
+ skillModel: 'openai/gpt-5.4',
18
+ postActionModel: 'google/gemini-2.5-flash-lite',
19
+ };
20
+ const CONCRETE_MODEL_PREFIXES = [
21
+ 'openrouter/',
22
+ 'anthropic/',
23
+ 'openai/',
24
+ 'google/',
25
+ 'x-ai/',
26
+ 'meta-llama/',
27
+ 'mistral/',
28
+ 'cohere/',
29
+ 'deepseek/',
30
+ 'groq/',
31
+ ];
32
+ /**
33
+ * True when a value looks like a concrete provider model id (host-resolved / post profile resolution).
34
+ */
35
+ export function looksLikeConcreteModelId(value) {
36
+ const trimmed = value.trim();
37
+ if (trimmed === '')
38
+ return false;
39
+ if (trimmed.includes('/'))
40
+ return true;
41
+ const lower = trimmed.toLowerCase();
42
+ return CONCRETE_MODEL_PREFIXES.some((p) => lower.startsWith(p));
43
+ }
44
+ /**
45
+ * True when a string is a valid graph-authored AI profile name (not a provider model id).
46
+ */
47
+ export function isGraphAiProfileName(value) {
48
+ if (typeof value !== 'string')
49
+ return false;
50
+ const trimmed = value.trim();
51
+ if (trimmed === '')
52
+ return false;
53
+ return !looksLikeConcreteModelId(trimmed);
54
+ }
55
+ export function assertGraphAiProfileNameString(value, path, context) {
56
+ if (!isGraphAiProfileName(value)) {
57
+ throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be an AI profile name (non-empty string without "/" or provider prefixes); got ${JSON.stringify(value)}`, context);
58
+ }
59
+ }
60
+ function formatResolvedModelId(provider, modelId) {
61
+ const mid = modelId.trim();
62
+ if (mid.includes('/'))
63
+ return mid;
64
+ const prov = provider.trim();
65
+ return prov ? `${prov}/${mid}` : mid;
66
+ }
67
+ async function resolveModelSlot(slot, context) {
68
+ const trimmed = slot.trim();
69
+ if (trimmed === '') {
70
+ throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_CONFIG_INCOMPLETE, 'modelConfig slot must be a non-empty string', context);
71
+ }
72
+ if (looksLikeConcreteModelId(trimmed))
73
+ return trimmed;
74
+ try {
75
+ const resolved = await resolveAIProfile(trimmed, { source: 'bundled' });
76
+ return formatResolvedModelId(resolved.provider, resolved.modelId);
77
+ }
78
+ catch (err) {
79
+ const code = err?.code ?? 'UNKNOWN_PROFILE';
80
+ throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_PROFILE_UNRESOLVED, `Unresolved AI profile "${trimmed}" (${String(code)})`, { ...context, profile: trimmed });
81
+ }
82
+ }
83
+ /**
84
+ * Resolves graph/runtime {@link GraphAiModelConfig} to concrete provider model ids.
85
+ * Profile names resolve via bundled `@x12i/ai-profiles`; concrete ids pass through unchanged.
86
+ */
87
+ export async function resolveGraphAiModelConfig(modelConfig, context) {
88
+ if (!isGraphAiModelConfig(modelConfig)) {
89
+ throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_CONFIG_INCOMPLETE, 'modelConfig must be { preActionModel: string, skillModel: string, postActionModel: string } with non-empty values', context);
90
+ }
91
+ const [preActionModel, skillModel, postActionModel] = await Promise.all([
92
+ resolveModelSlot(modelConfig.preActionModel, context),
93
+ resolveModelSlot(modelConfig.skillModel, context),
94
+ resolveModelSlot(modelConfig.postActionModel, context),
95
+ ]);
96
+ return { preActionModel, skillModel, postActionModel };
97
+ }
98
+ /**
99
+ * Maps resolved three-phase graph config to ai-tasks slot pair for a single runTask phase.
100
+ */
101
+ export function toRunTaskModelConfigForPhase(config, phase) {
102
+ switch (phase) {
103
+ case 'pre':
104
+ return {
105
+ xynthesisModel: config.preActionModel,
106
+ skillModel: config.preActionModel,
107
+ };
108
+ case 'main':
109
+ return {
110
+ xynthesisModel: config.preActionModel,
111
+ skillModel: config.skillModel,
112
+ };
113
+ case 'post':
114
+ return {
115
+ xynthesisModel: config.postActionModel,
116
+ skillModel: config.postActionModel,
117
+ };
118
+ }
119
+ }
@@ -1,46 +1,10 @@
1
- import type { Graph, GraphAiModelConfig, GraphModelAliasConfig, ModelConfigSelection } from '../types/refs.js';
2
- /** Fallback alias pair when no modelConfig tier resolves. */
3
- export declare const DEFAULT_MODEL_ALIAS = "default";
4
- export declare const DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG: GraphAiModelConfig;
5
1
  /**
6
- * True when a string is a valid graph-authored model alias (not a provider model id).
2
+ * @deprecated Legacy alias-map exports removed in 7.1. Use {@link graphAiModelConfig.js} and `@x12i/ai-profiles`.
7
3
  */
8
- export declare function isModelAliasString(value: unknown): value is string;
9
- /** True when a value looks like a concrete provider model id (forbidden in graph JSON). */
10
- export declare function looksLikeConcreteModelId(value: string): boolean;
11
- export declare function assertGraphModelAliasString(value: unknown, path: string, context?: {
12
- jobId?: string;
13
- graphId?: string;
14
- nodeId?: string;
15
- }): asserts value is string;
16
- export declare function mergeAliasConfigs(root?: GraphModelAliasConfig, node?: GraphModelAliasConfig): GraphModelAliasConfig;
17
- /**
18
- * Collects every alias name referenced by graph + optional runtime overrides (before resolution).
19
- * Always includes `default` because resolution may fall back to it.
20
- */
21
- export declare function collectModelAliasesUsedInGraph(graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtime?: {
22
- modelConfig?: ModelConfigSelection;
23
- nodes?: Record<string, {
24
- modelConfig?: GraphAiModelConfig;
25
- aliasConfig?: GraphModelAliasConfig;
26
- }>;
27
- }): Set<string>;
28
- export declare function assertRuntimeAliasConfigPresent(aliasConfig: GraphModelAliasConfig | undefined, context?: {
29
- jobId?: string;
30
- graphId?: string;
31
- }): asserts aliasConfig is GraphModelAliasConfig;
32
- /**
33
- * Ensures merged runtime alias map covers every alias used by the graph (+ runtime overrides).
34
- */
35
- export declare function assertRuntimeAliasConfigCoversGraph(graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, aliasConfig: GraphModelAliasConfig | undefined, runtime?: Parameters<typeof collectModelAliasesUsedInGraph>[1], context?: {
36
- jobId?: string;
37
- graphId?: string;
38
- }): void;
39
- /**
40
- * Resolves alias-only {@link GraphAiModelConfig} to concrete provider model ids via runtime map.
41
- */
42
- export declare function resolveGraphAiModelConfigAliases(modelConfig: GraphAiModelConfig | undefined, aliasConfig: GraphModelAliasConfig | undefined, context?: {
43
- jobId?: string;
44
- graphId?: string;
45
- nodeId?: string;
46
- }): GraphAiModelConfig | undefined;
4
+ export { DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, type RunTaskModelConfigWire, type EngineModelPhase, type GraphAiModelConfigResolveContext, } from './graphAiModelConfig.js';
5
+ /** @deprecated Use {@link isGraphAiProfileName}. */
6
+ export { isGraphAiProfileName as isModelAliasString } from './graphAiModelConfig.js';
7
+ /** @deprecated Use {@link assertGraphAiProfileNameString}. */
8
+ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './graphAiModelConfig.js';
9
+ /** @deprecated Use {@link resolveGraphAiModelConfig}. */
10
+ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './graphAiModelConfig.js';
@@ -1,129 +1,10 @@
1
- import { ExellixGraphError } from '../errors/ExellixGraphError.js';
2
- import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
3
- import { isGraphAiModelConfig, isModelConfigSelection } from './modelConfigSelection.js';
4
- /** Fallback alias pair when no modelConfig tier resolves. */
5
- export const DEFAULT_MODEL_ALIAS = 'default';
6
- export const DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG = {
7
- xynthesisModel: DEFAULT_MODEL_ALIAS,
8
- skillModel: DEFAULT_MODEL_ALIAS,
9
- };
10
- const CONCRETE_MODEL_PREFIXES = ['openrouter/', 'anthropic/', 'openai/', 'google/', 'x-ai/', 'meta-llama/'];
11
1
  /**
12
- * True when a string is a valid graph-authored model alias (not a provider model id).
2
+ * @deprecated Legacy alias-map exports removed in 7.1. Use {@link graphAiModelConfig.js} and `@x12i/ai-profiles`.
13
3
  */
14
- export function isModelAliasString(value) {
15
- if (typeof value !== 'string')
16
- return false;
17
- const trimmed = value.trim();
18
- if (trimmed === '')
19
- return false;
20
- return !looksLikeConcreteModelId(trimmed);
21
- }
22
- /** True when a value looks like a concrete provider model id (forbidden in graph JSON). */
23
- export function looksLikeConcreteModelId(value) {
24
- const trimmed = value.trim();
25
- if (trimmed.includes('/'))
26
- return true;
27
- const lower = trimmed.toLowerCase();
28
- return CONCRETE_MODEL_PREFIXES.some((p) => lower.startsWith(p));
29
- }
30
- export function assertGraphModelAliasString(value, path, context) {
31
- if (!isModelAliasString(value)) {
32
- throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_MODEL_CONFIG, `${path} must be a model alias name (non-empty string without "/" or provider prefixes); got ${JSON.stringify(value)}`, context);
33
- }
34
- }
35
- export function mergeAliasConfigs(root, node) {
36
- const aliases = {};
37
- for (const source of [root, node]) {
38
- if (source == null || typeof source !== 'object' || Array.isArray(source))
39
- continue;
40
- for (const [alias, model] of Object.entries(source)) {
41
- if (alias.trim() !== '' && typeof model === 'string' && model.trim() !== '') {
42
- aliases[alias] = model;
43
- }
44
- }
45
- }
46
- return aliases;
47
- }
48
- function collectAliasesFromGraphAiModelConfig(config, out) {
49
- if (!isGraphAiModelConfig(config))
50
- return;
51
- out.add(config.xynthesisModel.trim());
52
- out.add(config.skillModel.trim());
53
- }
54
- function collectAliasesFromModelConfigSelection(selection, out) {
55
- if (!isModelConfigSelection(selection))
56
- return;
57
- for (const c of selection.cases) {
58
- collectAliasesFromGraphAiModelConfig(c.modelConfig, out);
59
- }
60
- }
61
- /**
62
- * Collects every alias name referenced by graph + optional runtime overrides (before resolution).
63
- * Always includes `default` because resolution may fall back to it.
64
- */
65
- export function collectModelAliasesUsedInGraph(graph, runtime) {
66
- const out = new Set([DEFAULT_MODEL_ALIAS]);
67
- collectAliasesFromModelConfigSelection(graph.modelConfig, out);
68
- for (const node of graph.nodes ?? []) {
69
- const tn = node;
70
- if (tn.type === 'finalizer')
71
- continue;
72
- collectAliasesFromModelConfigSelection(tn.taskConfiguration?.modelConfig, out);
73
- }
74
- if (runtime != null) {
75
- collectAliasesFromModelConfigSelection(runtime.modelConfig, out);
76
- if (runtime.nodes != null && typeof runtime.nodes === 'object') {
77
- for (const nodeRuntime of Object.values(runtime.nodes)) {
78
- if (nodeRuntime == null || typeof nodeRuntime !== 'object')
79
- continue;
80
- collectAliasesFromGraphAiModelConfig(nodeRuntime.modelConfig, out);
81
- }
82
- }
83
- }
84
- return out;
85
- }
86
- export function assertRuntimeAliasConfigPresent(aliasConfig, context) {
87
- if (aliasConfig == null ||
88
- typeof aliasConfig !== 'object' ||
89
- Array.isArray(aliasConfig) ||
90
- Object.keys(aliasConfig).length === 0) {
91
- throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_CONFIG_REQUIRED, 'runtime.aliasConfig is required and must be a non-empty map of alias name → concrete model id', context);
92
- }
93
- }
94
- /**
95
- * Ensures merged runtime alias map covers every alias used by the graph (+ runtime overrides).
96
- */
97
- export function assertRuntimeAliasConfigCoversGraph(graph, aliasConfig, runtime, context) {
98
- assertRuntimeAliasConfigPresent(aliasConfig, context);
99
- const needed = collectModelAliasesUsedInGraph(graph, runtime);
100
- const missing = [];
101
- for (const alias of needed) {
102
- const concrete = aliasConfig[alias];
103
- if (typeof concrete !== 'string' || concrete.trim() === '') {
104
- missing.push(alias);
105
- }
106
- }
107
- if (missing.length > 0) {
108
- throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_UNRESOLVED, `runtime.aliasConfig is missing concrete models for alias(es): ${missing.join(', ')}`, { ...context, graphId: context?.graphId ?? graph.id, missingAliases: missing });
109
- }
110
- }
111
- function resolveModelAliasStrict(alias, aliasConfig, context) {
112
- const concrete = aliasConfig[alias];
113
- if (typeof concrete !== 'string' || concrete.trim() === '') {
114
- throw new ExellixGraphError(ExellixGraphErrorCode.MODEL_ALIAS_UNRESOLVED, `Unresolved model alias "${alias}" — add it to runtime.aliasConfig (and runtime.nodes[nodeId].aliasConfig when scoped per node)`, { ...context, alias });
115
- }
116
- return concrete.trim();
117
- }
118
- /**
119
- * Resolves alias-only {@link GraphAiModelConfig} to concrete provider model ids via runtime map.
120
- */
121
- export function resolveGraphAiModelConfigAliases(modelConfig, aliasConfig, context) {
122
- if (!isGraphAiModelConfig(modelConfig))
123
- return undefined;
124
- assertRuntimeAliasConfigPresent(aliasConfig, context);
125
- return {
126
- xynthesisModel: resolveModelAliasStrict(modelConfig.xynthesisModel, aliasConfig, context),
127
- skillModel: resolveModelAliasStrict(modelConfig.skillModel, aliasConfig, context),
128
- };
129
- }
4
+ export { DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, looksLikeConcreteModelId, isGraphAiProfileName, assertGraphAiProfileNameString, resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, } from './graphAiModelConfig.js';
5
+ /** @deprecated Use {@link isGraphAiProfileName}. */
6
+ export { isGraphAiProfileName as isModelAliasString } from './graphAiModelConfig.js';
7
+ /** @deprecated Use {@link assertGraphAiProfileNameString}. */
8
+ export { assertGraphAiProfileNameString as assertGraphModelAliasString } from './graphAiModelConfig.js';
9
+ /** @deprecated Use {@link resolveGraphAiModelConfig}. */
10
+ export { resolveGraphAiModelConfig as resolveGraphAiModelConfigAliases } from './graphAiModelConfig.js';
@@ -3,13 +3,16 @@ export function isGraphAiModelConfig(value) {
3
3
  return false;
4
4
  const o = value;
5
5
  const keys = Object.keys(o);
6
- return (keys.length === 2 &&
7
- keys.includes('xynthesisModel') &&
6
+ return (keys.length === 3 &&
7
+ keys.includes('preActionModel') &&
8
8
  keys.includes('skillModel') &&
9
- typeof o.xynthesisModel === 'string' &&
10
- o.xynthesisModel.trim() !== '' &&
9
+ keys.includes('postActionModel') &&
10
+ typeof o.preActionModel === 'string' &&
11
+ o.preActionModel.trim() !== '' &&
11
12
  typeof o.skillModel === 'string' &&
12
- o.skillModel.trim() !== '');
13
+ o.skillModel.trim() !== '' &&
14
+ typeof o.postActionModel === 'string' &&
15
+ o.postActionModel.trim() !== '');
13
16
  }
14
17
  export function isEmptyConditionWhen(when) {
15
18
  if (when == null)
@@ -1,5 +1,5 @@
1
1
  import type { RunxClient } from '@x12i/runx';
2
- import type { GraphAiModelConfig, GraphModelAliasConfig, ModelConfigSelection, TaskNodeRuntimeObject } from '../types/refs.js';
2
+ import type { GraphAiModelConfig, ModelConfigSelection, TaskNodeRuntimeObject } from '../types/refs.js';
3
3
  import { conditionWhenSignature, isEmptyConditionWhen } from './modelConfigSelection.js';
4
4
  import { type TaskNodeConditionEvalContext } from './taskNodeConditionsEvaluation.js';
5
5
  export type ResolveModelConfigForNodeArgs = {
@@ -7,7 +7,6 @@ export type ResolveModelConfigForNodeArgs = {
7
7
  nodeModelConfig?: unknown;
8
8
  runtimeModelConfig?: ModelConfigSelection;
9
9
  graphModelConfig?: ModelConfigSelection;
10
- runtimeAliasConfig?: GraphModelAliasConfig;
11
10
  conditionCtx: TaskNodeConditionEvalContext;
12
11
  executionInput: Record<string, unknown>;
13
12
  runx?: RunxClient;
@@ -16,9 +15,11 @@ export type ResolveModelConfigForNodeArgs = {
16
15
  jobId?: string;
17
16
  };
18
17
  /**
19
- * Resolves effective model config for a task node (precedence + conditional cases + strict alias resolution).
20
- * Returns concrete provider model ids for `RunTaskRequest.modelConfig`.
18
+ * Resolves effective model config for a task node (precedence + conditional cases).
19
+ * Returns profile names (or host-resolved concrete ids from runtime overrides) for
20
+ * {@link toRunTaskModelConfigForPhase} / ai-tasks wire — not pre-resolved provider ids.
21
+ * Use {@link resolveGraphAiModelConfig} when concrete ids are required (observability, docs).
21
22
  */
22
23
  export declare function resolveModelConfigForNode(args: ResolveModelConfigForNodeArgs): Promise<GraphAiModelConfig>;
23
24
  export { conditionWhenSignature, isEmptyConditionWhen };
24
- export { resolveGraphAiModelConfigAliases, mergeAliasConfigs } from './modelAliasContract.js';
25
+ export { resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
@@ -1,6 +1,6 @@
1
1
  import { conditionWhenSignature, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
2
2
  import { evaluateConditionWhen } from './taskNodeConditionsEvaluation.js';
3
- import { DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, mergeAliasConfigs, resolveGraphAiModelConfigAliases, } from './modelAliasContract.js';
3
+ import { DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
4
4
  async function selectFromCases(selection, ctx, executionInput, runx) {
5
5
  const cases = selection.cases;
6
6
  if (!Array.isArray(cases) || cases.length === 0)
@@ -34,24 +34,24 @@ async function resolveTier(value, ctx, executionInput, runx) {
34
34
  return undefined;
35
35
  }
36
36
  /**
37
- * Resolves effective model config for a task node (precedence + conditional cases + strict alias resolution).
38
- * Returns concrete provider model ids for `RunTaskRequest.modelConfig`.
37
+ * Resolves effective model config for a task node (precedence + conditional cases).
38
+ * Returns profile names (or host-resolved concrete ids from runtime overrides) for
39
+ * {@link toRunTaskModelConfigForPhase} / ai-tasks wire — not pre-resolved provider ids.
40
+ * Use {@link resolveGraphAiModelConfig} when concrete ids are required (observability, docs).
39
41
  */
40
42
  export async function resolveModelConfigForNode(args) {
41
- const aliasConfig = mergeAliasConfigs(args.runtimeAliasConfig, args.runtimeNodeConfig?.aliasConfig);
42
- const resolveCtx = { graphId: args.graphId, nodeId: args.nodeId, jobId: args.jobId };
43
43
  const staticHost = args.runtimeNodeConfig?.modelConfig;
44
44
  if (isGraphAiModelConfig(staticHost)) {
45
- return resolveGraphAiModelConfigAliases(staticHost, aliasConfig, resolveCtx);
45
+ return staticHost;
46
46
  }
47
47
  const tiers = [args.nodeModelConfig, args.runtimeModelConfig, args.graphModelConfig];
48
48
  for (const tier of tiers) {
49
49
  const selected = await resolveTier(tier, args.conditionCtx, args.executionInput, args.runx);
50
50
  if (selected != null) {
51
- return resolveGraphAiModelConfigAliases(selected, aliasConfig, resolveCtx);
51
+ return selected;
52
52
  }
53
53
  }
54
- return resolveGraphAiModelConfigAliases(DEFAULT_GRAPH_AI_MODEL_ALIAS_CONFIG, aliasConfig, resolveCtx);
54
+ return DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG;
55
55
  }
56
56
  export { conditionWhenSignature, isEmptyConditionWhen };
57
- export { resolveGraphAiModelConfigAliases, mergeAliasConfigs } from './modelAliasContract.js';
57
+ export { resolveGraphAiModelConfig, toRunTaskModelConfigForPhase, DEFAULT_GRAPH_AI_MODEL_IDS, DEFAULT_GRAPH_AI_MODEL_PROFILE_CONFIG, } from './graphAiModelConfig.js';
@@ -4,6 +4,11 @@ import type { TaskNode } from '../types/refs.js';
4
4
  import type { StepAttemptRecord } from '../types/results.js';
5
5
  export type ResolvedStepRetryPolicy = Required<Pick<StepRetryPolicy, 'maxAttempts' | 'retryOnTimeout' | 'retryOnTokenLimit' | 'tokenBumpMultiplier' | 'tokenBumpCap' | 'baseMaxTokensFallback'>>;
6
6
  export declare function resolveStepRetryPolicy(graphPolicy: StepRetryPolicy | undefined, node: Pick<TaskNode, 'taskConfiguration'> | undefined): ResolvedStepRetryPolicy;
7
+ /**
8
+ * Reads the effective completion cap from outbound `llmCall`.
9
+ * ai-tasks v8 canonical field is `maxTokensCap`; legacy `maxTokens` / `max_tokens` still honored.
10
+ */
11
+ export declare function readMaxTokensFromRunTaskRequest(req: RunTaskRequest): number | undefined;
7
12
  export type RunTaskWithRetryResult = {
8
13
  response: RunTaskResponse;
9
14
  attempts: StepAttemptRecord[];
@@ -25,9 +25,14 @@ export function resolveStepRetryPolicy(graphPolicy, node) {
25
25
  function readNum(v) {
26
26
  return typeof v === 'number' && Number.isFinite(v) ? v : undefined;
27
27
  }
28
- function readMaxTokensFromRequest(req) {
28
+ /**
29
+ * Reads the effective completion cap from outbound `llmCall`.
30
+ * ai-tasks v8 canonical field is `maxTokensCap`; legacy `maxTokens` / `max_tokens` still honored.
31
+ */
32
+ export function readMaxTokensFromRunTaskRequest(req) {
29
33
  const llm = req.llmCall;
30
- return (readNum(llm?.maxTokens) ??
34
+ return (readNum(llm?.maxTokensCap) ??
35
+ readNum(llm?.maxTokens) ??
31
36
  readNum(llm?.max_tokens));
32
37
  }
33
38
  function shallowCloneRunTaskRequest(req) {
@@ -45,12 +50,13 @@ function shallowCloneRunTaskRequest(req) {
45
50
  };
46
51
  }
47
52
  function applyTokenBump(workingReq, policy) {
48
- const current = readMaxTokensFromRequest(workingReq) ?? policy.baseMaxTokensFallback;
53
+ const current = readMaxTokensFromRunTaskRequest(workingReq) ?? policy.baseMaxTokensFallback;
49
54
  const after = Math.min(policy.tokenBumpCap, Math.floor(current * policy.tokenBumpMultiplier));
50
- workingReq.llmCall =
51
- workingReq.llmCall != null && typeof workingReq.llmCall === 'object' && !Array.isArray(workingReq.llmCall)
52
- ? { ...workingReq.llmCall, maxTokens: after }
53
- : { maxTokens: after };
55
+ const bumped = workingReq.llmCall != null && typeof workingReq.llmCall === 'object' && !Array.isArray(workingReq.llmCall)
56
+ ? { ...workingReq.llmCall }
57
+ : {};
58
+ bumped.maxTokensCap = after;
59
+ workingReq.llmCall = bumped;
54
60
  return { before: current, after };
55
61
  }
56
62
  function classifyThrownError(err) {
@@ -134,7 +140,7 @@ export async function runRunTaskWithRetry(args) {
134
140
  }
135
141
  const endedAt = Date.now();
136
142
  const durationMs = endedAt - startedAt;
137
- const maxTok = readMaxTokensFromRequest(workingReq);
143
+ const maxTok = readMaxTokensFromRunTaskRequest(workingReq);
138
144
  if (thrown != null) {
139
145
  const c = classifyThrownError(thrown);
140
146
  attempts.push({
@@ -171,7 +177,7 @@ export async function runRunTaskWithRetry(args) {
171
177
  nodeId,
172
178
  skillKey,
173
179
  level: 'info',
174
- message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokens ${before} -> ${after}`,
180
+ message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokensCap ${before} -> ${after}`,
175
181
  data: { classification: c, maxTokensBefore: before, maxTokensAfter: after, attempt: attemptIndex + 1 },
176
182
  });
177
183
  }
@@ -226,7 +232,7 @@ export async function runRunTaskWithRetry(args) {
226
232
  nodeId,
227
233
  skillKey,
228
234
  level: 'info',
229
- message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokens ${before} -> ${after}`,
235
+ message: `step retry ${attemptIndex + 2}/${policy.maxAttempts}: token-limit bump maxTokensCap ${before} -> ${after}`,
230
236
  data: { classification: c, maxTokensBefore: before, maxTokensAfter: after, attempt: attemptIndex + 1 },
231
237
  });
232
238
  }
@@ -30,9 +30,9 @@ export type BuildTaskNodeRunTaskRequestArgs = {
30
30
  graphRunTaskId?: string;
31
31
  /** Host job id on `job.jobId` / `job.id`; defaults from `job` when omitted. */
32
32
  runTaskJobId?: string;
33
- /** Alias-only selection; resolved via `aliasConfig` before building the request. */
33
+ /** Resolved or profile-name selection; resolved via `@x12i/ai-profiles` when building the request. */
34
34
  modelConfig?: GraphAiModelConfig;
35
- /** Required when resolving `modelConfig` aliases for preflight (mirrors `runtime.aliasConfig`). */
35
+ /** @deprecated Unused since 7.1. */
36
36
  aliasConfig?: GraphModelAliasConfig;
37
37
  llmCall?: Record<string, unknown>;
38
38
  /** Runtime default merged with per-node `taskConfiguration.llmCall`. */
@@ -50,7 +50,7 @@ export type BuildTaskNodeRunTaskRequestArgs = {
50
50
  * Materialize the outbound `RunTaskRequest` graph-engine would send for a task node
51
51
  * (mirrors `executeNode` request assembly; does not run PRE strategy phases or `runTask`).
52
52
  */
53
- export declare function buildTaskNodeRunTaskRequest(args: BuildTaskNodeRunTaskRequestArgs): BuildTaskNodeRunTaskRequestResult;
53
+ export declare function buildTaskNodeRunTaskRequest(args: BuildTaskNodeRunTaskRequestArgs): Promise<BuildTaskNodeRunTaskRequestResult>;
54
54
  export type ValidateTaskNodeRunTaskConfigArgs = BuildTaskNodeRunTaskRequestArgs;
55
55
  export type ValidateTaskNodeRunTaskConfigResult = {
56
56
  skipped: true;
@@ -62,7 +62,7 @@ export type ValidateTaskNodeRunTaskConfigResult = {
62
62
  request: RunTaskRequest;
63
63
  } & RunTaskValidationResult);
64
64
  /** Static ai-tasks validation on the graph-built {@link RunTaskRequest}. */
65
- export declare function validateTaskNodeRunTaskConfig(args: ValidateTaskNodeRunTaskConfigArgs): ValidateTaskNodeRunTaskConfigResult;
65
+ export declare function validateTaskNodeRunTaskConfig(args: ValidateTaskNodeRunTaskConfigArgs): Promise<ValidateTaskNodeRunTaskConfigResult>;
66
66
  export type ValidateTaskNodeRunTaskInvokeArgs = BuildTaskNodeRunTaskRequestArgs & Omit<ValidateRunTaskInvokeParams, 'request'>;
67
67
  export type ValidateTaskNodeRunTaskInvokeResult = {
68
68
  skipped: true;
@@ -1,5 +1,5 @@
1
1
  import { analyzeRunTaskRequest, validateRunTaskConfig, validateRunTaskInvoke, } from '@exellix/ai-tasks';
2
- import { resolveGraphAiModelConfigAliases } from './modelAliasContract.js';
2
+ import { toRunTaskModelConfigForPhase } from './graphAiModelConfig.js';
3
3
  import { resolveTaskKey } from './resolveTaskKey.js';
4
4
  import { isLocalSkillKey } from './localSkills/index.js';
5
5
  import { buildAiTasksRunTaskRequest, extractRunTaskStrategyOverrides, } from './buildAiTasksRunTaskRequest.js';
@@ -35,7 +35,7 @@ function mergeKnowledgePatchIntoRunTaskMemory(memory, patch) {
35
35
  * Materialize the outbound `RunTaskRequest` graph-engine would send for a task node
36
36
  * (mirrors `executeNode` request assembly; does not run PRE strategy phases or `runTask`).
37
37
  */
38
- export function buildTaskNodeRunTaskRequest(args) {
38
+ export async function buildTaskNodeRunTaskRequest(args) {
39
39
  const node = args.node;
40
40
  if (node.type === 'finalizer') {
41
41
  return { runnable: false, skillKey: 'finalizer', reason: 'finalizer' };
@@ -122,13 +122,7 @@ export function buildTaskNodeRunTaskRequest(args) {
122
122
  });
123
123
  const metaStrats = node.taskConfiguration?.executionStrategies;
124
124
  const ov = node.taskConfiguration?.aiTasksOutputValidation;
125
- const resolvedModelConfig = args.modelConfig != null && args.aliasConfig != null
126
- ? resolveGraphAiModelConfigAliases(args.modelConfig, args.aliasConfig, {
127
- graphId: args.graphId,
128
- nodeId: String(node.id),
129
- jobId: lifecycleJobId,
130
- })
131
- : args.modelConfig;
125
+ const mainRunTaskModelConfig = args.modelConfig != null ? toRunTaskModelConfigForPhase(args.modelConfig, 'main') : undefined;
132
126
  const request = buildAiTasksRunTaskRequest({
133
127
  skillKey,
134
128
  job: args.job,
@@ -153,7 +147,7 @@ export function buildTaskNodeRunTaskRequest(args) {
153
147
  ? ov
154
148
  : undefined,
155
149
  diagnostics: args.runTaskDiagnostics,
156
- modelConfig: resolvedModelConfig ?? undefined,
150
+ modelConfig: mainRunTaskModelConfig,
157
151
  llmCall: effectiveLlmCall,
158
152
  identity: buildRunTaskIdentityEnvelope({
159
153
  base: args.runTaskIdentity,
@@ -173,8 +167,8 @@ export function buildTaskNodeRunTaskRequest(args) {
173
167
  return { runnable: true, skillKey, request };
174
168
  }
175
169
  /** Static ai-tasks validation on the graph-built {@link RunTaskRequest}. */
176
- export function validateTaskNodeRunTaskConfig(args) {
177
- const built = buildTaskNodeRunTaskRequest(args);
170
+ export async function validateTaskNodeRunTaskConfig(args) {
171
+ const built = await buildTaskNodeRunTaskRequest(args);
178
172
  if (!built.runnable) {
179
173
  return { skipped: true, skillKey: built.skillKey, reason: built.reason };
180
174
  }
@@ -183,7 +177,7 @@ export function validateTaskNodeRunTaskConfig(args) {
183
177
  }
184
178
  /** Config + payload + template path validation via ai-tasks {@link validateRunTaskInvoke}. */
185
179
  export async function validateTaskNodeRunTaskInvoke(args) {
186
- const built = buildTaskNodeRunTaskRequest(args);
180
+ const built = await buildTaskNodeRunTaskRequest(args);
187
181
  if (!built.runnable) {
188
182
  return { skipped: true, skillKey: built.skillKey, reason: built.reason };
189
183
  }
@@ -203,7 +197,7 @@ export async function validateTaskNodeRunTaskInvoke(args) {
203
197
  }
204
198
  /** Catalox-backed skill request analysis on the graph-built {@link RunTaskRequest}. */
205
199
  export async function analyzeTaskNodeRunTaskRequest(args) {
206
- const built = buildTaskNodeRunTaskRequest(args);
200
+ const built = await buildTaskNodeRunTaskRequest(args);
207
201
  if (!built.runnable) {
208
202
  return { skipped: true, skillKey: built.skillKey, reason: built.reason };
209
203
  }
@@ -4,7 +4,7 @@ import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
4
4
  import { EXELLIX_VIRTUAL_BOUNDARY_NODE_METADATA_KEY } from '../inspection/types.js';
5
5
  import { getStructuredDataFilterPathViolations } from './dataFiltersEvaluation.js';
6
6
  import { conditionWhenSignature, countDefaultModelConfigCases, isEmptyConditionWhen, isGraphAiModelConfig, isModelConfigSelection, } from './modelConfigSelection.js';
7
- import { assertGraphModelAliasString } from './modelAliasContract.js';
7
+ import { assertGraphAiProfileNameString } from './graphAiModelConfig.js';
8
8
  /** Top-level keys permitted on a canonical exellix-graph executable graph JSON document. */
9
9
  export const CANONICAL_GRAPH_TOP_LEVEL_KEYS = [
10
10
  'id',
@@ -279,7 +279,7 @@ function assertModelConfigSelection(value, path, context) {
279
279
  ? ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE
280
280
  : ExellixGraphErrorCode.NON_CANONICAL_GRAPH_DOCUMENT;
281
281
  if (isGraphAiModelConfig(value)) {
282
- throw new ExellixGraphError(code, `${path}: flat { xynthesisModel, skillModel } was removed. Use { cases: [{ modelConfig: { xynthesisModel, skillModel } }] } (one case with no \`when\` for a single default).`, context);
282
+ throw new ExellixGraphError(code, `${path}: flat modelConfig was removed. Use { cases: [{ modelConfig: { preActionModel, skillModel, postActionModel } }] } (one case with no \`when\` for a single default).`, context);
283
283
  }
284
284
  if (!isModelConfigSelection(value)) {
285
285
  throw new ExellixGraphError(code, `${path} must be { cases: ModelConfigCase[] } where each case has modelConfig and optional when.`, context);
@@ -297,10 +297,11 @@ function assertModelConfigSelection(value, path, context) {
297
297
  throw new ExellixGraphError(code, `${casePath} must be a plain object.`, context);
298
298
  }
299
299
  if (!isGraphAiModelConfig(c.modelConfig)) {
300
- throw new ExellixGraphError(code, `${casePath}.modelConfig must be { xynthesisModel: string, skillModel: string } with non-empty alias values.`, context);
300
+ throw new ExellixGraphError(code, `${casePath}.modelConfig must be { preActionModel: string, skillModel: string, postActionModel: string } with non-empty AI profile names.`, context);
301
301
  }
302
- assertGraphModelAliasString(c.modelConfig.xynthesisModel, `${casePath}.modelConfig.xynthesisModel`, context);
303
- assertGraphModelAliasString(c.modelConfig.skillModel, `${casePath}.modelConfig.skillModel`, context);
302
+ assertGraphAiProfileNameString(c.modelConfig.preActionModel, `${casePath}.modelConfig.preActionModel`, context);
303
+ assertGraphAiProfileNameString(c.modelConfig.skillModel, `${casePath}.modelConfig.skillModel`, context);
304
+ assertGraphAiProfileNameString(c.modelConfig.postActionModel, `${casePath}.modelConfig.postActionModel`, context);
304
305
  if (isEmptyConditionWhen(c.when)) {
305
306
  continue;
306
307
  }
@@ -321,10 +322,8 @@ function assertOptionalRuntimeFlatModelConfig(value, path, context) {
321
322
  if (value === undefined)
322
323
  return;
323
324
  if (!isGraphAiModelConfig(value)) {
324
- throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly { xynthesisModel: string, skillModel: string } alias names (runtime host override does not use cases).`, context);
325
+ throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `${path} must be exactly { preActionModel: string, skillModel: string, postActionModel: string } (runtime host override may use concrete provider ids or profile names).`, context);
325
326
  }
326
- assertGraphModelAliasString(value.xynthesisModel, `${path}.xynthesisModel`, context);
327
- assertGraphModelAliasString(value.skillModel, `${path}.skillModel`, context);
328
327
  }
329
328
  function assertContextualKnowledgeScope(scope, nodeId, context) {
330
329
  if (scope === undefined)
@@ -37,8 +37,8 @@ export interface StepRetryPolicy {
37
37
  /** Upper bound for `maxTokens` after bump (default **16000**). */
38
38
  tokenBumpCap?: number;
39
39
  /**
40
- * Used when neither `llmCall.maxTokens` nor `llmCall.max_tokens` is set on the outbound request
41
- * (default **2000**). `modelConfig` is reserved for model selection.
40
+ * Used when `llmCall.maxTokensCap` (or legacy `maxTokens` / `max_tokens`) is unset on the outbound request
41
+ * before the first token-limit retry bump (default **2000**).
42
42
  */
43
43
  baseMaxTokensFallback?: number;
44
44
  }
@@ -13,18 +13,23 @@ export type TaskNodeExecutionPipelineStep = {
13
13
  * Type references for graph execution
14
14
  */
15
15
  /**
16
- * Alias-only AI model slot selection on graph model / node `modelConfig`.
17
- * Concrete provider model ids are supplied only via {@link GraphModelAliasConfig} at execution.
16
+ * Three-phase AI model selection on graph model / node `modelConfig`.
17
+ * Values are `@x12i/ai-profiles` profile names (`cheap`, `balanced`, `deep`, …) in graph JSON,
18
+ * or host-resolved concrete provider ids on runtime overrides.
18
19
  */
19
20
  export type GraphAiModelConfig = {
20
- xynthesisModel: string;
21
+ preActionModel: string;
21
22
  skillModel: string;
23
+ postActionModel: string;
22
24
  };
23
- /** Runtime map: alias name → concrete provider model id (required on every `executeGraph`). */
25
+ /**
26
+ * @deprecated Legacy alias map — no longer required for execute. Hosts may omit `runtime.aliasConfig`.
27
+ */
24
28
  export type GraphModelAliasConfig = Record<string, string>;
25
29
  /** Runtime overrides for a single task node, stored at GraphRuntimeObject.nodes[nodeId]. */
26
30
  export type TaskNodeRuntimeObject = {
27
31
  modelConfig?: GraphAiModelConfig;
32
+ /** @deprecated Unused since 7.1 — profile resolution uses `@x12i/ai-profiles`. */
28
33
  aliasConfig?: GraphModelAliasConfig;
29
34
  };
30
35
  /** Backwards-readable alias for per-node runtime overrides keyed by task node id. */
@@ -82,7 +82,11 @@ export class RealTasksClient {
82
82
  if (process.env.DEBUG_AI_PROVIDER === 'true') {
83
83
  const mc = aiTasksRequest.modelConfig;
84
84
  const effective = mc
85
- ? { xynthesisModel: mc.xynthesisModel, skillModel: mc.skillModel }
85
+ ? {
86
+ preActionModel: mc.preActionModel ?? mc.xynthesisModel,
87
+ skillModel: mc.skillModel,
88
+ postActionModel: mc.postActionModel ?? mc.skillModel,
89
+ }
86
90
  : aiTasksRequest.config
87
91
  ? {
88
92
  provider: aiTasksRequest.config.provider,
@@ -1,14 +1,14 @@
1
1
  import type { Graph } from '../src/types/refs.js';
2
2
  import { createExellixGraphRuntime, type GraphRuntimeObject } from '../src/runtime/ExellixGraphRuntime.js';
3
- /** Maps every alias used by a graph (plus `default`) to `openrouter/<alias>` for unit tests. */
4
- export declare function buildTestAliasConfig(graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtimePartial?: Pick<GraphRuntimeObject, 'modelConfig' | 'nodes'> & {
5
- aliasConfig?: Record<string, string>;
6
- }): Record<string, string>;
7
- /** Merges test alias bindings into a runtime object (required for executeGraph in 7.x). */
8
- export declare function withTestAliasRuntime(graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, runtime: Partial<GraphRuntimeObject> & Pick<GraphRuntimeObject, 'jobId' | 'job'>): GraphRuntimeObject;
9
3
  type CreateOpts = Parameters<typeof createExellixGraphRuntime>[0];
10
4
  /**
11
- * Testkit runtime that auto-injects `runtime.aliasConfig` on every `executeGraph` call.
5
+ * Testkit runtime for graphs using `@x12i/ai-profiles` (no `runtime.aliasConfig` required).
12
6
  */
13
7
  export declare function createTestExellixGraphRuntime(opts: CreateOpts): ReturnType<typeof createExellixGraphRuntime>;
8
+ /** @deprecated Alias map test helper removed in 7.1 — profiles resolve via `@x12i/ai-profiles`. */
9
+ export declare function buildTestAliasConfig(_graph: Pick<Graph, 'modelConfig' | 'nodes'>, runtimePartial?: Pick<GraphRuntimeObject, 'modelConfig' | 'nodes'> & {
10
+ aliasConfig?: Record<string, string>;
11
+ }): Record<string, string>;
12
+ /** @deprecated No-op — `runtime.aliasConfig` is not required in 7.1. */
13
+ export declare function withTestAliasRuntime(_graph: Pick<Graph, 'id' | 'modelConfig' | 'nodes'>, runtime: Partial<GraphRuntimeObject> & Pick<GraphRuntimeObject, 'jobId' | 'job'>): GraphRuntimeObject;
14
14
  export {};
@@ -1,24 +1,6 @@
1
1
  import { createExellixGraphRuntime, } from '../src/runtime/ExellixGraphRuntime.js';
2
- import { collectModelAliasesUsedInGraph } from '../src/runtime/modelAliasContract.js';
3
- /** Maps every alias used by a graph (plus `default`) to `openrouter/<alias>` for unit tests. */
4
- export function buildTestAliasConfig(graph, runtimePartial) {
5
- const needed = collectModelAliasesUsedInGraph(graph, runtimePartial);
6
- const map = {};
7
- for (const alias of needed) {
8
- map[alias] = `openrouter/${alias}`;
9
- }
10
- return { ...map, ...(runtimePartial?.aliasConfig ?? {}) };
11
- }
12
- /** Merges test alias bindings into a runtime object (required for executeGraph in 7.x). */
13
- export function withTestAliasRuntime(graph, runtime) {
14
- const aliasConfig = buildTestAliasConfig(graph, runtime);
15
- return {
16
- ...runtime,
17
- aliasConfig,
18
- };
19
- }
20
2
  /**
21
- * Testkit runtime that auto-injects `runtime.aliasConfig` on every `executeGraph` call.
3
+ * Testkit runtime for graphs using `@x12i/ai-profiles` (no `runtime.aliasConfig` required).
22
4
  */
23
5
  export function createTestExellixGraphRuntime(opts) {
24
6
  const runtime = createExellixGraphRuntime(opts);
@@ -33,8 +15,16 @@ export function createTestExellixGraphRuntime(opts) {
33
15
  : { agentId: 'test-agent', jobType: 'unit-test' };
34
16
  return executeGraph({
35
17
  ...input,
36
- runtime: withTestAliasRuntime(input.model, { ...base, jobId, job }),
18
+ runtime: { ...base, jobId, job },
37
19
  });
38
20
  };
39
21
  return runtime;
40
22
  }
23
+ /** @deprecated Alias map test helper removed in 7.1 — profiles resolve via `@x12i/ai-profiles`. */
24
+ export function buildTestAliasConfig(_graph, runtimePartial) {
25
+ return { ...(runtimePartial?.aliasConfig ?? {}) };
26
+ }
27
+ /** @deprecated No-op — `runtime.aliasConfig` is not required in 7.1. */
28
+ export function withTestAliasRuntime(_graph, runtime) {
29
+ return runtime;
30
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exellix/graph-engine",
3
- "version": "7.0.1",
3
+ "version": "7.2.0",
4
4
  "type": "module",
5
5
  "description": "Graph executor SDK",
6
6
  "main": "dist/src/index.js",
@@ -18,6 +18,7 @@
18
18
  },
19
19
  "files": [
20
20
  "dist/**",
21
+ "scripts/patch-ai-tasks-xynthesis-export.mjs",
21
22
  ".env.example",
22
23
  "CHANGELOG.md",
23
24
  "README.md"
@@ -25,11 +26,12 @@
25
26
  "scripts": {
26
27
  "prebuild": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true, maxRetries: 10, retryDelay: 100 })\"",
27
28
  "build": "tsc",
28
- "test": "npm run build && npm run check:no-legacy && node --test --test-force-exit tests/model-alias-contract.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/passthrough-parity.test.ts",
29
- "test:full": "npm run build && npm run check:no-legacy && tsx --test --test-force-exit tests/graph-engine.test.ts tests/passthrough-parity.test.ts tests/task-node-run-task-preflight.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/model-alias-contract.test.ts",
29
+ "test": "npm run build && npm run check:no-legacy && node scripts/patch-ai-tasks-xynthesis-export.mjs && node --test --test-force-exit tests/model-alias-contract.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/passthrough-parity.test.ts tests/step-retry-llm-call.test.ts",
30
+ "test:full": "npm run build && npm run check:no-legacy && node scripts/patch-ai-tasks-xynthesis-export.mjs && tsx --test --test-force-exit tests/graph-engine.test.ts tests/passthrough-parity.test.ts tests/task-node-run-task-preflight.test.ts tests/reports-fixtures-pre-synthesis.test.ts tests/model-alias-contract.test.ts tests/step-retry-llm-call.test.ts",
30
31
  "run:pre-synthesis": "npm run build && node --env-file=.env scripts/run-pre-synthesis-graph.mjs",
31
32
  "check:no-legacy": "node scripts/check-no-legacy.mjs",
32
33
  "lint": "eslint src testkit --ext .ts",
34
+ "postinstall": "node scripts/patch-ai-tasks-xynthesis-export.mjs",
33
35
  "prepublishOnly": "npm run build"
34
36
  },
35
37
  "keywords": [
@@ -49,11 +51,12 @@
49
51
  "access": "public"
50
52
  },
51
53
  "dependencies": {
52
- "@exellix/ai-tasks": "^8.0.0",
53
- "@x12i/activix": "^7.1.0",
54
+ "@exellix/ai-tasks": "^8.0.7",
55
+ "@x12i/activix": "^8.0.0",
56
+ "@x12i/ai-profiles": "^1.2.0",
54
57
  "@x12i/catalox": "^5.1.1",
55
58
  "@x12i/env": "^4.0.1",
56
- "@x12i/funcx": "^4.0.2",
59
+ "@x12i/funcx": "^4.2.0",
57
60
  "@x12i/graphenix": "^2.5.0",
58
61
  "@x12i/graphenix-format": "^2.0.0",
59
62
  "@x12i/logxer": "^4.3.6",
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Workaround: @exellix/ai-tasks@8.0.7 re-exports MODEL_CAPABILITIES from @exellix/xynthesis,
3
+ * but xynthesis 4.0.x removed that export (capabilities come from getModelCapabilities / ai-profiles).
4
+ * Remove the broken re-export so graph-engine and tests can import @exellix/ai-tasks.
5
+ */
6
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+
9
+ const aiTasksRoot = join(process.cwd(), 'node_modules', '@exellix', 'ai-tasks', 'dist');
10
+ const broken =
11
+ 'export { buildInvokeAttemptSummary, ACTION_OUTPUT_DEFAULTS, MODEL_CAPABILITIES } from "@exellix/xynthesis";';
12
+ const fixed =
13
+ 'export { buildInvokeAttemptSummary, ACTION_OUTPUT_DEFAULTS } from "@exellix/xynthesis";';
14
+
15
+ for (const file of ['index.js', 'index.d.ts']) {
16
+ const path = join(aiTasksRoot, file);
17
+ if (!existsSync(path)) continue;
18
+ const text = readFileSync(path, 'utf8');
19
+ if (!text.includes(broken)) continue;
20
+ writeFileSync(path, text.replace(broken, fixed), 'utf8');
21
+ console.log(`[patch-ai-tasks-xynthesis-export] patched ${file}`);
22
+ }