@exellix/graph-engine 8.5.0 → 8.7.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 (38) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +2 -2
  3. package/dist/src/contract/graphRunContract.d.ts +37 -0
  4. package/dist/src/contract/graphRunContract.js +130 -0
  5. package/dist/src/contract/persistencyDefaults.d.ts +2 -0
  6. package/dist/src/contract/persistencyDefaults.js +2 -0
  7. package/dist/src/index.d.ts +7 -4
  8. package/dist/src/index.js +6 -2
  9. package/dist/src/inspection/contractInspection.js +7 -16
  10. package/dist/src/inspection/controlInspection.js +2 -2
  11. package/dist/src/inspection/nodeInspection.js +9 -10
  12. package/dist/src/inspection/types.d.ts +4 -13
  13. package/dist/src/integrations/activixExellixShared.js +2 -2
  14. package/dist/src/runtime/ExellixGraphRuntime.js +6 -3
  15. package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +3 -8
  16. package/dist/src/runtime/buildAiTasksRunTaskRequest.js +1 -16
  17. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +3 -3
  18. package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +3 -7
  19. package/dist/src/runtime/executionMatrixHost.d.ts +2 -2
  20. package/dist/src/runtime/executionMatrixHost.js +2 -2
  21. package/dist/src/runtime/graphEngineLogMeta.js +2 -2
  22. package/dist/src/runtime/graphEngineLogxer.js +2 -2
  23. package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +3 -7
  24. package/dist/src/runtime/resolveNarrixForTaskNode.js +4 -7
  25. package/dist/src/runtime/runTaskNodePlan.d.ts +10 -0
  26. package/dist/src/runtime/runTaskNodePlan.js +132 -0
  27. package/dist/src/runtime/runtimeObjects.d.ts +5 -5
  28. package/dist/src/runtime/runtimeObjects.js +5 -5
  29. package/dist/src/runtime/taskNodeRunTaskPreflight.js +6 -3
  30. package/dist/src/runtime/validateCanonicalGraphDocument.js +6 -7
  31. package/dist/src/types/aiTaskProfile.d.ts +12 -17
  32. package/dist/src/types/aiTaskProfile.js +12 -4
  33. package/dist/src/types/narrix.d.ts +33 -24
  34. package/dist/src/types/taskNodeConfiguration.d.ts +3 -2
  35. package/docs/handoff/graphs-studio-graphenix-2.7.3.md +13 -0
  36. package/package.json +22 -20
  37. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +0 -15
  38. package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +0 -43
@@ -1,7 +1,7 @@
1
1
  /**
2
- * Builds the `RunTaskRequest.taskConfiguration` blob for `@exellix/ai-tasks` ≥ 7.7 compile-at-invoke.
3
- * Root-level fields already lifted by graph-engine are omitted; compile-only authoring (e.g.
4
- * `aiTaskStrategies`, `aiTaskProfile`) remains on the blob.
2
+ * Builds the `RunTaskRequest.taskConfiguration` blob for legacy compile-at-invoke (no `nodePlan`).
3
+ * With graphenix node plans, narrix / narrixMode / narrixInput / narrixScope live on
4
+ * `nodePlan.invokeContract.pipeline` instead see {@link patchNodePlanRuntimePipeline}.
5
5
  */
6
6
  const RUN_TASK_LIFTED_TASK_CONFIGURATION_KEYS = new Set([
7
7
  'taskTypeId',
@@ -10,10 +10,6 @@ const RUN_TASK_LIFTED_TASK_CONFIGURATION_KEYS = new Set([
10
10
  'modelConfig',
11
11
  'llmCall',
12
12
  'timeoutMs',
13
- 'narrix',
14
- 'narrixMode',
15
- 'narrixInput',
16
- 'narrixScope',
17
13
  'inputStrategyKey',
18
14
  'aiTasksOutputValidation',
19
15
  'taskKind',
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Host helpers for execution-matrix style orchestrators (e.g. `@exellix/exellix-runtime`).
2
+ * Host helpers for execution-matrix style orchestrators (e.g. `@x12i/exellix-runtime`).
3
3
  *
4
4
  * Graph-engine does not call matrix claim APIs; the host injects `executeGraph` and supplies
5
5
  * a `{ plan, runtime }` request with per-graph `GraphEntryContract` + seeded
@@ -83,7 +83,7 @@ export declare function buildMatrixJobForGraphRun(input: {
83
83
  export declare function isExecuteGraphResultFailed(result: ExecuteGraphResult): boolean;
84
84
  /**
85
85
  * Creates a long-lived graph runtime and exposes `executeGraph` for injection into matrix claim
86
- * processors (`ProcessMatrixClaimDeps.executeGraph` in `@exellix/exellix-runtime`, etc.).
86
+ * processors (`ProcessMatrixClaimDeps.executeGraph` in `@x12i/exellix-runtime`, etc.).
87
87
  */
88
88
  export declare function createMatrixHostGraphExecutor(options: ExellixGraphRuntimeOptions): {
89
89
  executeGraph: (input: ExecuteGraphInput) => Promise<ExecuteGraphResult>;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Host helpers for execution-matrix style orchestrators (e.g. `@exellix/exellix-runtime`).
2
+ * Host helpers for execution-matrix style orchestrators (e.g. `@x12i/exellix-runtime`).
3
3
  *
4
4
  * Graph-engine does not call matrix claim APIs; the host injects `executeGraph` and supplies
5
5
  * a `{ plan, runtime }` request with per-graph `GraphEntryContract` + seeded
@@ -92,7 +92,7 @@ export function isExecuteGraphResultFailed(result) {
92
92
  }
93
93
  /**
94
94
  * Creates a long-lived graph runtime and exposes `executeGraph` for injection into matrix claim
95
- * processors (`ProcessMatrixClaimDeps.executeGraph` in `@exellix/exellix-runtime`, etc.).
95
+ * processors (`ProcessMatrixClaimDeps.executeGraph` in `@x12i/exellix-runtime`, etc.).
96
96
  */
97
97
  export function createMatrixHostGraphExecutor(options) {
98
98
  const { executeGraph } = createExellixGraphRuntime(options);
@@ -1,10 +1,10 @@
1
- import { EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME } from './runtimeObjects.js';
1
+ import { X12I_GRAPH_RUNTIME_PACKAGE_NAME } from './runtimeObjects.js';
2
2
  /** Canonical filter key for graph-engine's own log lines. */
3
3
  export const GRAPH_ENGINE_RUNTIME_SERVICE = 'graph-engine';
4
4
  /** Wrapper attribution when graph-engine proxies a downstream Logxer record. */
5
5
  export const GRAPH_ENGINE_PROXY_RUNTIME_IDENTITY = {
6
6
  service: GRAPH_ENGINE_RUNTIME_SERVICE,
7
- libraryPackage: EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME,
7
+ libraryPackage: X12I_GRAPH_RUNTIME_PACKAGE_NAME,
8
8
  };
9
9
  function isRecord(value) {
10
10
  return value != null && typeof value === 'object' && !Array.isArray(value);
@@ -1,6 +1,6 @@
1
1
  const logxer = await import('@x12i/logxer');
2
2
  import { ExellixGraphErrorCode } from '../errors/exellixGraphErrorCodes.js';
3
- import { EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME } from './runtimeObjects.js';
3
+ import { X12I_GRAPH_RUNTIME_PACKAGE_NAME } from './runtimeObjects.js';
4
4
  import { normalizeGraphEngineLogMeta, } from './graphEngineLogMeta.js';
5
5
  export { GRAPH_ENGINE_RUNTIME_SERVICE, GRAPH_ENGINE_PROXY_RUNTIME_IDENTITY, extractOriginRuntimeIdentity, normalizeGraphEngineLogMeta, } from './graphEngineLogMeta.js';
6
6
  /** Env prefix for package-level log level: `GRAPH_ENGINE_LOGS_LEVEL` (canonical). */
@@ -116,7 +116,7 @@ function wrapGraphEngineLogxer(inner) {
116
116
  /** Factory for a graph-engine Logxer instance (logxer 4.5+ stack pass-through). */
117
117
  export function createGraphEngineLogxer(options) {
118
118
  const inner = logxer.createLogxer({
119
- packageName: EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME,
119
+ packageName: X12I_GRAPH_RUNTIME_PACKAGE_NAME,
120
120
  envPrefix: GRAPH_ENGINE_LOGXER_ENV_PREFIX,
121
121
  debugNamespace: 'graph-engine',
122
122
  }, buildGraphEngineLogxerConfig(options?.logging));
@@ -1,14 +1,10 @@
1
1
  import type { NarrixPreProcessorConfig, ResolvedNarrixWirePayload } from '../types/narrix.js';
2
- import type { AiTaskProfileMetadata } from '../types/aiTaskProfile.js';
3
2
  /**
4
- * Resolves outbound `RunTaskRequest.narrix`: starts from node `taskConfiguration.narrix`,
5
- * shallow-merges graph-level `variables.narrix`, then applies `enableWebScope` / `webScopeQuestions`
6
- * via {@link applyAiTaskProfileWebScopingToNarrix}.
7
- *
8
- * Shared by {@link ExellixGraphRuntime}.
3
+ * Resolves discovery narrix for runtime plan patching: shallow-merge of node `taskConfiguration.narrix`
4
+ * with graph-level `variables.narrix`. Web scope is a PRE `webScope` unit compiled from
5
+ * `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.x+).
9
6
  */
10
7
  export declare function resolveNarrixForTaskNode(args: {
11
8
  nodeMetadataNarrix: NarrixPreProcessorConfig | undefined;
12
9
  graphVariablesNarrix: Partial<NarrixPreProcessorConfig> | undefined;
13
- aiTaskProfile: AiTaskProfileMetadata | undefined;
14
10
  }): ResolvedNarrixWirePayload | undefined;
@@ -1,10 +1,7 @@
1
- import { applyAiTaskProfileWebScopingToNarrix } from './applyAiTaskProfileWebScopingToNarrix.js';
2
1
  /**
3
- * Resolves outbound `RunTaskRequest.narrix`: starts from node `taskConfiguration.narrix`,
4
- * shallow-merges graph-level `variables.narrix`, then applies `enableWebScope` / `webScopeQuestions`
5
- * via {@link applyAiTaskProfileWebScopingToNarrix}.
6
- *
7
- * Shared by {@link ExellixGraphRuntime}.
2
+ * Resolves discovery narrix for runtime plan patching: shallow-merge of node `taskConfiguration.narrix`
3
+ * with graph-level `variables.narrix`. Web scope is a PRE `webScope` unit compiled from
4
+ * `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.x+).
8
5
  */
9
6
  export function resolveNarrixForTaskNode(args) {
10
7
  let resolved = args.nodeMetadataNarrix != null ? { ...args.nodeMetadataNarrix } : undefined;
@@ -15,5 +12,5 @@ export function resolveNarrixForTaskNode(args) {
15
12
  ...(resolved ?? {}),
16
13
  };
17
14
  }
18
- return applyAiTaskProfileWebScopingToNarrix(resolved, args.aiTaskProfile);
15
+ return resolved;
19
16
  }
@@ -1,4 +1,5 @@
1
1
  import type { FinalizerExecutionPlan, NodeExecutionPlan } from '@x12i/graphenix-executable-contracts';
2
+ import type { NarrixPreProcessorConfig } from '../types/narrix.js';
2
3
  import type { RunTaskModelConfigWire } from './graphAiModelConfig.js';
3
4
  /** Resolve the frozen node plan required by `@exellix/ai-tasks` ≥ 9. */
4
5
  export declare function resolveRunTaskNodePlan(args: {
@@ -10,6 +11,15 @@ export declare function resolveRunTaskNodePlan(args: {
10
11
  }): NodeExecutionPlan;
11
12
  /** Standalone {@link executeNode} without compile: patch MAIN unit selection from resolved graph profiles. */
12
13
  export declare function patchNodePlanMainModelFromWire(plan: NodeExecutionPlan, wire: RunTaskModelConfigWire): NodeExecutionPlan;
14
+ /**
15
+ * Patches graphenix node-plan invoke pipeline with runtime narrix resolution (graph `variables.narrix`
16
+ * merge) and optional `taskConfiguration` narrixScope / mode / input when absent from compile.
17
+ */
18
+ export declare function patchNodePlanRuntimePipeline(args: {
19
+ plan: NodeExecutionPlan;
20
+ resolvedNarrix?: NarrixPreProcessorConfig;
21
+ taskConfiguration?: Record<string, unknown>;
22
+ }): NodeExecutionPlan;
13
23
  export declare function nodePlanFromFinalizerPlan(args: {
14
24
  finalizerId: string;
15
25
  utilityKey: string;
@@ -1,4 +1,7 @@
1
1
  import { minimalNodePlanForSkillKey } from '@exellix/ai-tasks';
2
+ function isPlainRecord(v) {
3
+ return v != null && typeof v === 'object' && !Array.isArray(v);
4
+ }
2
5
  function mainSkillUnit(plan) {
3
6
  return plan.executionUnits.find((u) => u.unitKind === 'mainSkill');
4
7
  }
@@ -7,6 +10,102 @@ function selectionFromWireSlot(value) {
7
10
  return undefined;
8
11
  return { kind: 'profileChoice', key: value.trim() };
9
12
  }
13
+ function resolveNarrativeMode(pipeline, tc) {
14
+ const explicit = pipeline.narrixMode ?? tc?.narrixMode;
15
+ if (explicit === 'preprocessor' || explicit === 'handler')
16
+ return explicit;
17
+ if (explicit === 'off')
18
+ return 'off';
19
+ if (pipeline.narrixInput != null || tc?.narrixInput != null)
20
+ return 'handler';
21
+ if (pipeline.narrix != null || tc?.narrix != null)
22
+ return 'preprocessor';
23
+ return 'off';
24
+ }
25
+ function planHasNarrativeUnit(plan) {
26
+ return plan.executionUnits.some((u) => u.unitKind === 'narrativePreprocessor' || u.unitKind === 'narrativeHandler');
27
+ }
28
+ function defaultPreActionSelection(plan) {
29
+ const preUtility = plan.executionUnits.find((u) => u.unitKind === 'externalPreUtility');
30
+ if (preUtility?.modelSelection) {
31
+ return { selection: preUtility.modelSelection, source: preUtility.modelSource ?? 'graphDefault' };
32
+ }
33
+ return {
34
+ selection: { kind: 'profileChoice', key: 'cheap/default' },
35
+ source: 'graphDefault',
36
+ };
37
+ }
38
+ /** Standalone executeNode path: mirror Graphenix `buildNarrativeUnits` when plan was not compiled. */
39
+ function injectRuntimeNarrativeUnitsIfAbsent(plan, pipeline, taskConfiguration) {
40
+ if (planHasNarrativeUnit(plan))
41
+ return plan;
42
+ const mode = resolveNarrativeMode(pipeline, taskConfiguration);
43
+ if (mode === 'off')
44
+ return plan;
45
+ const narrix = isPlainRecord(pipeline.narrix) ? pipeline.narrix : {};
46
+ const narrixInput = isPlainRecord(pipeline.narrixInput) ? pipeline.narrixInput : {};
47
+ const attachToField = (typeof narrix.attachToField === 'string' && narrix.attachToField) ||
48
+ (typeof narrixInput.attachToField === 'string' && narrixInput.attachToField) ||
49
+ undefined;
50
+ const narrativeWriteKey = attachToField ?? 'executionMemory._narrative';
51
+ const datasetId = (typeof narrix.datasetId === 'string' && narrix.datasetId) ||
52
+ (typeof narrixInput.datasetId === 'string' && narrixInput.datasetId) ||
53
+ undefined;
54
+ const preModel = defaultPreActionSelection(plan);
55
+ const nodeId = plan.nodeId;
56
+ const narrativeContract = {
57
+ reads: ['input'],
58
+ writes: [
59
+ {
60
+ destination: 'execution',
61
+ key: narrativeWriteKey,
62
+ mode: 'merge',
63
+ },
64
+ ],
65
+ };
66
+ const bumpedUnits = plan.executionUnits.map((u) => ({
67
+ ...u,
68
+ order: u.order + 1,
69
+ }));
70
+ let narrativeUnit;
71
+ if (mode === 'preprocessor') {
72
+ narrativeUnit = {
73
+ unitId: `unit:${nodeId}:narrative:pre`,
74
+ nodeId,
75
+ unitKind: 'narrativePreprocessor',
76
+ invokeMode: 'narrativePreprocessor',
77
+ order: 0,
78
+ strategyKey: 'narrative-preprocessor',
79
+ modelSlot: 'preActionModel',
80
+ modelSelection: preModel.selection,
81
+ modelSource: preModel.source,
82
+ unitParams: { datasetId, attachToField },
83
+ invokeContract: narrativeContract,
84
+ sourcePath: '/runtime/narrativePreprocessor',
85
+ };
86
+ }
87
+ else {
88
+ narrativeUnit = {
89
+ unitId: `unit:${nodeId}:narrative:handler`,
90
+ nodeId,
91
+ unitKind: 'narrativeHandler',
92
+ invokeMode: 'narrativeHandler',
93
+ order: 0,
94
+ strategyKey: 'narrative-handler',
95
+ modelSlot: 'preActionModel',
96
+ modelSelection: preModel.selection,
97
+ modelSource: preModel.source,
98
+ terminal: true,
99
+ unitParams: { datasetId, attachToField, narrixInput },
100
+ invokeContract: narrativeContract,
101
+ sourcePath: '/runtime/narrativeHandler',
102
+ };
103
+ }
104
+ return {
105
+ ...plan,
106
+ executionUnits: [narrativeUnit, ...bumpedUnits],
107
+ };
108
+ }
10
109
  /** Resolve the frozen node plan required by `@exellix/ai-tasks` ≥ 9. */
11
110
  export function resolveRunTaskNodePlan(args) {
12
111
  if (args.nodePlan)
@@ -27,6 +126,39 @@ export function patchNodePlanMainModelFromWire(plan, wire) {
27
126
  }
28
127
  return next;
29
128
  }
129
+ /**
130
+ * Patches graphenix node-plan invoke pipeline with runtime narrix resolution (graph `variables.narrix`
131
+ * merge) and optional `taskConfiguration` narrixScope / mode / input when absent from compile.
132
+ */
133
+ export function patchNodePlanRuntimePipeline(args) {
134
+ const tc = args.taskConfiguration;
135
+ const narrixScope = tc?.narrixScope;
136
+ const narrixMode = tc?.narrixMode;
137
+ const narrixInput = tc?.narrixInput;
138
+ const hasPatch = args.resolvedNarrix != null ||
139
+ (narrixScope != null && typeof narrixScope === 'object' && !Array.isArray(narrixScope)) ||
140
+ (typeof narrixMode === 'string' && narrixMode.trim() !== '') ||
141
+ narrixInput != null;
142
+ if (!hasPatch)
143
+ return args.plan;
144
+ const next = structuredClone(args.plan);
145
+ const ic = next.invokeContract ?? {};
146
+ const pipeline = { ...(isPlainRecord(ic.pipeline) ? ic.pipeline : {}) };
147
+ if (args.resolvedNarrix != null) {
148
+ pipeline.narrix = { ...args.resolvedNarrix };
149
+ }
150
+ if (narrixScope != null && typeof narrixScope === 'object' && !Array.isArray(narrixScope)) {
151
+ pipeline.narrixScope = narrixScope;
152
+ }
153
+ if (pipeline.narrixMode === undefined && typeof narrixMode === 'string' && narrixMode.trim() !== '') {
154
+ pipeline.narrixMode = narrixMode.trim();
155
+ }
156
+ if (pipeline.narrixInput === undefined && narrixInput != null) {
157
+ pipeline.narrixInput = narrixInput;
158
+ }
159
+ next.invokeContract = { ...ic, pipeline };
160
+ return injectRuntimeNarrativeUnitsIfAbsent(next, pipeline, tc);
161
+ }
30
162
  export function nodePlanFromFinalizerPlan(args) {
31
163
  const slots = args.finalizerPlan?.modelSlots;
32
164
  const plan = minimalNodePlanForSkillKey(args.utilityKey, args.finalizerId);
@@ -4,9 +4,9 @@
4
4
  */
5
5
  import type { GetJobLogsInput, GetJobLogsResult, QueryableLogLine } from '@x12i/logxer';
6
6
  /** Published npm name of this package (root layer in composed observability). */
7
- export declare const EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME: "@exellix/graph-engine";
7
+ export declare const X12I_GRAPH_RUNTIME_PACKAGE_NAME: "@exellix/graph-engine";
8
8
  /** Task layer package name in {@link RuntimeObjects.packagesRuntimeObjects}. */
9
- export declare const EXELLIX_AI_TASKS_PACKAGE_NAME: "@exellix/ai-tasks";
9
+ export declare const X12I_AI_TASKS_PACKAGE_NAME: "@exellix/ai-tasks";
10
10
  export type ActivixQueryableClient = {
11
11
  getJobActivities(input: {
12
12
  jobId: string;
@@ -43,15 +43,15 @@ export type BuildExellixGraphRuntimeObjectsInput = {
43
43
  graphLogxerClient?: LogxerQueryableClient;
44
44
  /**
45
45
  * Partial subtree from `@exellix/ai-tasks` when that package exports `runtimeObjects`.
46
- * Nested `packagesRuntimeObjects` are appended after the ai-tasks row without overwriting entries.
46
+ * Nested `packagesRuntimeObjects` are appended after the x12i-tasks row without overwriting entries.
47
47
  */
48
48
  aiTasksRuntimeObjects?: Pick<RuntimeObjects, 'activixClient' | 'logxerClient'> & {
49
49
  packagesRuntimeObjects?: PackageRuntimeObjects[];
50
50
  };
51
51
  };
52
52
  /**
53
- * Builds the composed {@link RuntimeObjects} for exellix-graph: graph-level clients at the root,
54
- * then an `@exellix/ai-tasks` package row plus any nested package rows from ai-tasks (flattened as siblings).
53
+ * Builds the composed {@link RuntimeObjects} for x12i-graph: graph-level clients at the root,
54
+ * then an `@exellix/ai-tasks` package row plus any nested package rows from x12i-tasks (flattened as siblings).
55
55
  */
56
56
  export declare function buildExellixGraphRuntimeObjects(input: BuildExellixGraphRuntimeObjectsInput): RuntimeObjects;
57
57
  /**
@@ -1,17 +1,17 @@
1
1
  /** Published npm name of this package (root layer in composed observability). */
2
- export const EXELLIX_GRAPH_RUNTIME_PACKAGE_NAME = '@exellix/graph-engine';
2
+ export const X12I_GRAPH_RUNTIME_PACKAGE_NAME = '@exellix/graph-engine';
3
3
  /** Task layer package name in {@link RuntimeObjects.packagesRuntimeObjects}. */
4
- export const EXELLIX_AI_TASKS_PACKAGE_NAME = '@exellix/ai-tasks';
4
+ export const X12I_AI_TASKS_PACKAGE_NAME = '@exellix/ai-tasks';
5
5
  /**
6
- * Builds the composed {@link RuntimeObjects} for exellix-graph: graph-level clients at the root,
7
- * then an `@exellix/ai-tasks` package row plus any nested package rows from ai-tasks (flattened as siblings).
6
+ * Builds the composed {@link RuntimeObjects} for x12i-graph: graph-level clients at the root,
7
+ * then an `@exellix/ai-tasks` package row plus any nested package rows from x12i-tasks (flattened as siblings).
8
8
  */
9
9
  export function buildExellixGraphRuntimeObjects(input) {
10
10
  const ai = input.aiTasksRuntimeObjects;
11
11
  const tail = [...(ai?.packagesRuntimeObjects ?? [])];
12
12
  const packagesRuntimeObjects = [
13
13
  {
14
- name: EXELLIX_AI_TASKS_PACKAGE_NAME,
14
+ name: X12I_AI_TASKS_PACKAGE_NAME,
15
15
  activixClient: ai?.activixClient,
16
16
  logxerClient: ai?.logxerClient,
17
17
  },
@@ -12,7 +12,7 @@ import { resolveNarrixForTaskNode } from './resolveNarrixForTaskNode.js';
12
12
  import { buildRunTaskIdentityEnvelope, shouldForwardRunTaskTraceMode, } from './runTaskAugments.js';
13
13
  import { readExecutionVariableBuckets } from './executionVariableBuckets.js';
14
14
  import { newGraphRunTaskId } from './graphRunIdentity.js';
15
- import { patchNodePlanMainModelFromWire, resolveRunTaskNodePlan } from './runTaskNodePlan.js';
15
+ import { patchNodePlanMainModelFromWire, patchNodePlanRuntimePipeline, resolveRunTaskNodePlan } from './runTaskNodePlan.js';
16
16
  function isPlainRecord(v) {
17
17
  return v != null && typeof v === 'object' && !Array.isArray(v);
18
18
  }
@@ -81,7 +81,11 @@ export async function buildTaskNodeRunTaskRequest(args) {
81
81
  ? { ...node.taskConfiguration.narrix }
82
82
  : undefined,
83
83
  graphVariablesNarrix: jobVariables?.narrix,
84
- aiTaskProfile: node.taskConfiguration?.aiTaskProfile,
84
+ });
85
+ runTaskNodePlan = patchNodePlanRuntimePipeline({
86
+ plan: runTaskNodePlan,
87
+ resolvedNarrix: narrix ?? undefined,
88
+ taskConfiguration: node.taskConfiguration,
85
89
  });
86
90
  const metaTk = node.taskConfiguration?.taskKind;
87
91
  const taskKindForward = metaTk === 'decision' || metaTk === 'utility' || metaTk === 'content' ? metaTk : undefined;
@@ -141,7 +145,6 @@ export async function buildTaskNodeRunTaskRequest(args) {
141
145
  executionMemory: executionMemoryForWire,
142
146
  jobContext,
143
147
  prevNodeId: args.prevNodeId,
144
- narrix: narrix ?? undefined,
145
148
  ...extractRunTaskStrategyOverrides(node.taskConfiguration),
146
149
  outputValidation: outputValidationFromPlan ??
147
150
  (ov != null && typeof ov === 'object' && !Array.isArray(ov) && 'schema' in ov
@@ -98,8 +98,9 @@ const LEGACY_METADATA_TO_TASK_CONFIGURATION = {
98
98
  executionMemory: 'runtime.executionMemory',
99
99
  outputsMemory: 'runtime.outputsMemory',
100
100
  };
101
- /** Web-scope keys are not allowed under `taskConfiguration.narrix` except `enableWebScope` (use `taskConfiguration.aiTaskProfile.webScoping` for questions). */
101
+ /** Web-scope keys are not allowed under `taskConfiguration.narrix` (use `taskConfiguration.aiTaskProfile.webQueryTemplate`). */
102
102
  const FORBIDDEN_NARRIX_WEB_KEYS = [
103
+ 'enableWebScope',
103
104
  'forceWebScope',
104
105
  'webScopeQuestions',
105
106
  'webScoping',
@@ -550,18 +551,16 @@ export function assertCanonicalTaskNode(node, context, options) {
550
551
  }
551
552
  const narrixTc = tc && isPlainObject(tc.narrix) ? tc.narrix : undefined;
552
553
  if (narrixTc) {
553
- if ('enableWebScope' in narrixTc &&
554
- narrixTc.enableWebScope != null &&
555
- typeof narrixTc.enableWebScope !== 'boolean') {
556
- throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.enableWebScope must be a boolean when present.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id), narrixWebKey: 'enableWebScope' });
557
- }
558
554
  for (const k of FORBIDDEN_NARRIX_WEB_KEYS) {
559
555
  if (k in narrixTc) {
560
- throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.${k} is forbidden — author web scope under taskConfiguration.aiTaskProfile.webScoping.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id), narrixWebKey: k });
556
+ throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.narrix.${k} is forbidden — author web scope under taskConfiguration.aiTaskProfile.webQueryTemplate.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id), narrixWebKey: k });
561
557
  }
562
558
  }
563
559
  }
564
560
  const profile = tc && isPlainObject(tc.aiTaskProfile) ? tc.aiTaskProfile : undefined;
561
+ if (profile && 'webScoping' in profile && profile.webScoping != null) {
562
+ throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.aiTaskProfile.webScoping is not allowed — use webQueryTemplate (Rendrix string).`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
563
+ }
565
564
  const inputSynthesis = profile && isPlainObject(profile.inputSynthesis) ? profile.inputSynthesis : undefined;
566
565
  if (inputSynthesis && 'alsoWriteLegacySynthesizedContext' in inputSynthesis) {
567
566
  throw new ExellixGraphError(ExellixGraphErrorCode.NON_CANONICAL_TASK_NODE, `Task node "${String(node.id)}": taskConfiguration.aiTaskProfile.inputSynthesis.alsoWriteLegacySynthesizedContext is removed in 5.x.`, { jobId: context?.jobId, graphId: context?.graphId, nodeId: String(node.id) });
@@ -1,13 +1,7 @@
1
1
  /**
2
- * Optional task metadata aligned with `@x12i/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
3
- * Exellix-graph-engine merges {@link AiTaskProfileMetadata.webScoping} into `taskConfiguration.narrix` for execution
4
- * when `enabled: true` and `questions` is non-empty (see `applyAiTaskProfileWebScopingToNarrix`).
2
+ * Optional task metadata aligned with `@exellix/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
3
+ * Web scope is authored via {@link AiTaskProfileMetadata.webQueryTemplate} (Graphenix 2.7.3+ PRE `webScope` unit).
5
4
  */
6
- export type AiTaskProfileWebScoping = {
7
- enabled: boolean;
8
- /** When `enabled` is `true`, must be a non-empty array of non-empty strings (graph-composer contract). */
9
- questions?: string[];
10
- };
11
5
  export type AiTaskProfileInputSynthesis = {
12
6
  enabled: boolean;
13
7
  /** Memory paths fed to PRE `synthesized-context` when `enabled` (validated against graph-engine allowlist). */
@@ -23,19 +17,20 @@ export type AiTaskProfileInputSynthesis = {
23
17
  strategyKey?: string;
24
18
  };
25
19
  /**
26
- * PRE/POST strategies, optional web scoping, optional input synthesis for AI task nodes.
27
- * `@x12i/graph-composer` requires `preStrategyKey` / `postStrategyKey` for validated AI nodes.
28
- * Exellix-graph-engine merges `webScoping` into the resolved narrix payload for execution when active.
29
- *
30
- * **Runtime (graph-engine ≥5):** When `preStrategyKey` / `postStrategyKey` are non-empty strings, the executor issues
31
- * additional `@exellix/ai-tasks` **`runTask`** calls (utility `skillKey`) **before** and **after** the MAIN node task — graph-engine does **not** call the Xynthesis package directly. Results are merged under **`execution.xynthesis.pre`** / **`execution.xynthesis.post`** (historical execution-memory slot names). This is separate from `executionPipeline` PRE `synthesized-context`, which still runs inside a single ai-tasks `runTask`.
20
+ * PRE/POST strategies, optional web query template, optional input synthesis for AI task nodes.
21
+ * `@exellix/graph-composer` requires `preStrategyKey` / `postStrategyKey` for validated AI nodes.
32
22
  *
33
- * **`inputSynthesis` runtime:** When `enabled`, graph-engine translates this block into an ai-tasks PRE
34
- * `synthesized-context` step inside the **same** `runTask` call (distinct from engine-level PRE/POST strategy utilities).
23
+ * **Web scope (Graphenix 2.7.3+):** Non-empty `webQueryTemplate` (or `webQueryTemplates[]`) compiles to a PRE
24
+ * `webScope` unit; graph-engine does not merge web intent into `taskConfiguration.narrix`.
35
25
  */
36
26
  export type AiTaskProfileMetadata = {
37
27
  preStrategyKey?: string;
38
28
  postStrategyKey?: string;
39
- webScoping?: AiTaskProfileWebScoping;
29
+ /** Rendrix template for PRE webScope (e.g. `Patch status for {{input.cveId}}?`). */
30
+ webQueryTemplate?: string;
31
+ /** Pack-mode web query templates (`webQueryTemplate` optional when non-empty). */
32
+ webQueryTemplates?: string[];
33
+ webScopeOptions?: Record<string, unknown>;
40
34
  inputSynthesis?: AiTaskProfileInputSynthesis;
41
35
  };
36
+ export declare function hasWebScopeAuthoring(profile: AiTaskProfileMetadata | undefined): boolean;
@@ -1,6 +1,14 @@
1
1
  /**
2
- * Optional task metadata aligned with `@x12i/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
3
- * Exellix-graph-engine merges {@link AiTaskProfileMetadata.webScoping} into `taskConfiguration.narrix` for execution
4
- * when `enabled: true` and `questions` is non-empty (see `applyAiTaskProfileWebScopingToNarrix`).
2
+ * Optional task metadata aligned with `@exellix/graph-composer` authoring / `reportTaskNodeProtocolGaps`.
3
+ * Web scope is authored via {@link AiTaskProfileMetadata.webQueryTemplate} (Graphenix 2.7.3+ PRE `webScope` unit).
5
4
  */
6
- export {};
5
+ export function hasWebScopeAuthoring(profile) {
6
+ if (profile == null)
7
+ return false;
8
+ if (typeof profile.webQueryTemplate === 'string' && profile.webQueryTemplate.trim().length > 0) {
9
+ return true;
10
+ }
11
+ const pack = profile.webQueryTemplates;
12
+ return (Array.isArray(pack) &&
13
+ pack.some((entry) => typeof entry === 'string' && entry.trim().length > 0));
14
+ }
@@ -1,22 +1,39 @@
1
- import type { RunTaskRequest } from '@exellix/ai-tasks';
2
- /** Same as `RunTaskRequest['narrix']` (ai-tasks exports `RunTaskRequest` but not this alias on the root barrel). */
3
- type AITasksNarrixPreProcessorConfig = NonNullable<RunTaskRequest['narrix']>;
1
+ /** Canonical Narrix invocation modes (Graphenix plan / ai-tasks narrative units). */
2
+ export type NarrixModeKey = 'off' | 'preprocessor' | 'handler';
3
+ /** Filters Narrix handler output before writing to task memory. */
4
+ export type NarrixScope = {
5
+ includeSignals?: string[];
6
+ excludeSignals?: string[];
7
+ includeStories?: string[];
8
+ excludeStories?: string[];
9
+ [key: string]: unknown;
10
+ };
11
+ /** Structured Narrix handler input (ai-tasks `NarrixRunInput` shape). */
12
+ export type NarrixRunInput = ({
13
+ medium: 'record';
14
+ datasetId: string;
15
+ record: Record<string, unknown>;
16
+ } & Record<string, unknown>) | ({
17
+ medium: 'text';
18
+ datasetId: string;
19
+ text: string;
20
+ } & Record<string, unknown>) | ({
21
+ medium: 'docs';
22
+ datasetId: string;
23
+ document: Record<string, unknown>;
24
+ } & Record<string, unknown>) | ({
25
+ medium: 'chat';
26
+ datasetId: string;
27
+ thread: Record<string, unknown>;
28
+ } & Record<string, unknown>);
4
29
  /**
5
- * Graph-engine wire shape for the resolved `RunTaskRequest.narrix` payload after merging
6
- * `taskConfiguration.narrix` with graph variables and optional `taskConfiguration.aiTaskProfile.webScoping`.
30
+ * Resolved discovery narrix after merging node `taskConfiguration.narrix` with graph-level
31
+ * `variables.narrix`. Patched onto `nodePlan.invokeContract.pipeline.narrix` before `runTask`.
7
32
  */
8
- export type ResolvedNarrixWirePayload = AITasksNarrixPreProcessorConfig;
9
- /** One web-scope question on the resolved narrix wire payload; produced from `aiTaskProfile.webScoping.questions`. */
10
- export type WebScopeQuestion = {
11
- id: string;
12
- question: string;
13
- purpose?: string;
14
- };
33
+ export type ResolvedNarrixWirePayload = NarrixPreProcessorConfig;
15
34
  /**
16
- * Authoring shape for `taskConfiguration.narrix` on a graph task node.
17
- *
18
- * Discovery fields plus `enableWebScope` (forwarded to `RunTaskRequest.narrix.enableWebScope`).
19
- * Question packs for web scoping may also be supplied via `taskConfiguration.aiTaskProfile.webScoping`.
35
+ * Authoring shape for `taskConfiguration.narrix` on a graph task node (discovery fields only).
36
+ * Web scope is authored on `taskConfiguration.aiTaskProfile.webQueryTemplate` (Graphenix 2.7.3+).
20
37
  */
21
38
  export type NarrixPreProcessorConfig = {
22
39
  /** Which pack and entity type the engine runs (e.g. `"network.vuln.instances"`). */
@@ -30,13 +47,6 @@ export type NarrixPreProcessorConfig = {
30
47
  narrativeTypeIds?: string[];
31
48
  /** Framework question this node answers (e.g. q0, q1, q2, q3, q5, q6). */
32
49
  questionId?: string;
33
- /** When true, ai-tasks runs `@exellix/narrix-web-scoper` after CNI and writes `executionMemory.webContext`. */
34
- enableWebScope?: boolean;
35
- /**
36
- * When true with `enableWebScope`, skip internal web-scoper if `executionMemory.webContextMarkdown` is already set.
37
- * See `@exellix/ai-tasks` `NarrixPreProcessorConfig.skipWebScopeWhenExternalWebMarkdownPresent`.
38
- */
39
- skipWebScopeWhenExternalWebMarkdownPresent?: boolean;
40
50
  /** Execution memory key for the Narrix attachment (default `_narrix` in ai-tasks). */
41
51
  attachToField?: string;
42
52
  };
@@ -63,4 +73,3 @@ export interface LocalTaskContext {
63
73
  masterSkillId?: string;
64
74
  masterSkillActivityId?: string;
65
75
  }
66
- export {};
@@ -1,4 +1,5 @@
1
- import type { InputStrategyKey, LlmCallConfig, NarrixModeKey, NarrixRunInput, RunTaskRequest } from '@exellix/ai-tasks';
1
+ import type { InputStrategyKey, LlmCallConfig, RunTaskRequest } from '@exellix/ai-tasks';
2
+ import type { NarrixModeKey, NarrixRunInput, NarrixScope } from './narrix.js';
2
3
  import type { ExecutionStrategyInvocation, TaskStrategyItemData } from './aiTasksDerivedTypes.js';
3
4
  import type { AiTaskProfileMetadata } from './aiTaskProfile.js';
4
5
  import type { ModelConfigSelection } from './refs.js';
@@ -49,7 +50,7 @@ export type TaskNodeTaskConfiguration = {
49
50
  identity?: Record<string, unknown>;
50
51
  runTaskIdentity?: Record<string, unknown>;
51
52
  memoryKey?: string;
52
- narrixScope?: RunTaskRequest['narrixScope'];
53
+ narrixScope?: NarrixScope;
53
54
  aiScoping?: RunTaskRequest['aiScoping'];
54
55
  aiScopingOptions?: RunTaskRequest['aiScopingOptions'];
55
56
  timeoutMs?: number;
@@ -0,0 +1,13 @@
1
+ # graphs-studio — Graphenix 2.7.3 alignment handoff
2
+
3
+ Shipped with **@exellix/graph-engine@8.6.0** / **@exellix/graph-composer@2.12.0**.
4
+
5
+ Full checklist: see Exellix monorepo `docs/handoff/graphs-studio-graphenix-2.7.3.md` (canonical copy). Summary:
6
+
7
+ 1. **Web scope:** `taskConfiguration.aiTaskProfile.webQueryTemplate` — remove `webScoping` and narrix web keys.
8
+ 2. **Graph entry:** no `graphEntry.inputs` (CR-006); use `requiredExecutionPaths` + jobs-ui data sources.
9
+ 3. **Persistency:** `response.persistency` with `newRecord` + optional `link` (Graphenix 2.7.3).
10
+ 4. **Preflight:** show `nodePlan.invokeContract.pipeline`, not root `RunTaskRequest.narrix`.
11
+ 5. **CRS-FRS-005:** PRE synthesis export parity still pending in studio (separate track).
12
+
13
+ Pin: `graph-engine ^8.6.0`, `graph-composer ^2.12.0`, `ai-tasks ^10.0.12+`, `graphenix ^2.7.3`.