@x12i/ai-gateway 9.1.5 → 9.1.6

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.
package/dist/index.d.ts CHANGED
@@ -20,6 +20,8 @@ export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatReq
20
20
  export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike } from './gateway-utils.js';
21
21
  export { mergeGatewayAndRequestTemplateRenderOptions, mergeTemplateRenderOptions } from './template-render-merge.js';
22
22
  export type { GatewayTemplateRenderRequestSlice } from './template-render-merge.js';
23
+ export { GATEWAY_DUAL_MEMORY_ROOTS, buildMemoryResolutionRootFromWorkingMemory, coalesceMergedInputBucket, extractCallerInputsBag, mapSmartInputPathsInputsToInput, parseLooseJsonObject, prepareWorkingMemoryForTemplateRender, resolveGatewayMemoryPathValue } from './memory-path-resolution.js';
24
+ export type { GatewayDualMemoryRoot } from './memory-path-resolution.js';
23
25
  export type { UsageTier } from './types.js';
24
26
  export { Activix } from '@x12i/activix';
25
27
  export type { ActivixRunContext, FindByRunContextCriteria, GetJobActivitiesInput, GetJobActivitiesResult } from '@x12i/activix';
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ export { InstructionNotFoundError, InstructionBackendError } from './instruction
19
19
  export { autoRegisterProviders } from './gateway-provider-auto-register.js';
20
20
  export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike } from './gateway-utils.js';
21
21
  export { mergeGatewayAndRequestTemplateRenderOptions, mergeTemplateRenderOptions } from './template-render-merge.js';
22
+ export { GATEWAY_DUAL_MEMORY_ROOTS, buildMemoryResolutionRootFromWorkingMemory, coalesceMergedInputBucket, extractCallerInputsBag, mapSmartInputPathsInputsToInput, parseLooseJsonObject, prepareWorkingMemoryForTemplateRender, resolveGatewayMemoryPathValue } from './memory-path-resolution.js';
22
23
  // Usage tracking: UsageTracker class methods are available but consumption calculation is disabled
23
24
  // (x-models was previously used for RPM/TPM tracking but is no longer integrated)
24
25
  // Re-export activity tracking primitives (Activix)
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Dual memory roots `input` (merged MAIN payload) and `inputs` (caller / graph-entry bag).
3
+ * Aligned with @exellix/graph-engine ≥ 5.5.0 resolution rules for template / smart-input paths.
4
+ */
5
+ export declare const GATEWAY_DUAL_MEMORY_ROOTS: readonly ["input", "inputs"];
6
+ export type GatewayDualMemoryRoot = (typeof GATEWAY_DUAL_MEMORY_ROOTS)[number];
7
+ /**
8
+ * Parse JSON object shapes from strings (matches graph-engine `parseLooseJsonObject`).
9
+ */
10
+ export declare function parseLooseJsonObject(value: unknown): Record<string, unknown> | undefined;
11
+ /** Caller / graph-entry bag from `workingMemory.inputs`. */
12
+ export declare function extractCallerInputsBag(workingMemory: unknown): Record<string, unknown> | undefined;
13
+ /** Merged MAIN bucket from `workingMemory.input` (object or parsed JSON string). */
14
+ export declare function coalesceMergedInputBucket(workingMemory: unknown): unknown;
15
+ /**
16
+ * Resolve a dotted path against working memory with dual-root rules:
17
+ * - `inputs` / `inputs.*` → caller bag first, then merged `input`
18
+ * - `input` / `input.*` → merged `input` first, then caller `inputs`
19
+ * - other paths → direct lookup on working memory
20
+ */
21
+ export declare function resolveGatewayMemoryPathValue(workingMemory: unknown, path: string): unknown;
22
+ /**
23
+ * Working-memory view for Rendrix / smart-input: preserves all keys but overlays
24
+ * `input` and `inputs` with dual-root merge views (does not rewrite authored paths).
25
+ */
26
+ export declare function buildMemoryResolutionRootFromWorkingMemory(workingMemory: unknown): Record<string, unknown>;
27
+ /**
28
+ * When WM carries `input` and/or `inputs`, return a resolution root for template rendering.
29
+ * Otherwise returns the original reference unchanged.
30
+ */
31
+ export declare function prepareWorkingMemoryForTemplateRender(workingMemory: unknown): unknown;
32
+ /** Optional migration: rewrite `inputs.*` smart-input / memory paths to `input.*`. */
33
+ export declare function mapSmartInputPathsInputsToInput(paths: string[]): string[];
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Dual memory roots `input` (merged MAIN payload) and `inputs` (caller / graph-entry bag).
3
+ * Aligned with @exellix/graph-engine ≥ 5.5.0 resolution rules for template / smart-input paths.
4
+ */
5
+ export const GATEWAY_DUAL_MEMORY_ROOTS = ['input', 'inputs'];
6
+ function isPlainObject(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+ /**
10
+ * Parse JSON object shapes from strings (matches graph-engine `parseLooseJsonObject`).
11
+ */
12
+ export function parseLooseJsonObject(value) {
13
+ if (isPlainObject(value))
14
+ return value;
15
+ if (typeof value !== 'string')
16
+ return undefined;
17
+ const trimmed = value.trim();
18
+ if (!trimmed.startsWith('{'))
19
+ return undefined;
20
+ try {
21
+ const parsed = JSON.parse(trimmed);
22
+ return isPlainObject(parsed) ? parsed : undefined;
23
+ }
24
+ catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ /** Caller / graph-entry bag from `workingMemory.inputs`. */
29
+ export function extractCallerInputsBag(workingMemory) {
30
+ if (!isPlainObject(workingMemory))
31
+ return undefined;
32
+ const inputs = workingMemory.inputs;
33
+ return isPlainObject(inputs) ? inputs : undefined;
34
+ }
35
+ /** Merged MAIN bucket from `workingMemory.input` (object or parsed JSON string). */
36
+ export function coalesceMergedInputBucket(workingMemory) {
37
+ if (!isPlainObject(workingMemory))
38
+ return undefined;
39
+ const raw = workingMemory.input;
40
+ if (raw === undefined || raw === null)
41
+ return undefined;
42
+ const parsed = parseLooseJsonObject(raw);
43
+ return parsed !== undefined ? parsed : raw;
44
+ }
45
+ function getValueAtPath(obj, path) {
46
+ if (obj == null)
47
+ return undefined;
48
+ if (path === '' || path === 'this' || path === '.')
49
+ return obj;
50
+ const parts = path.split('.');
51
+ let cur = obj;
52
+ for (const part of parts) {
53
+ if (cur == null || typeof cur !== 'object')
54
+ return undefined;
55
+ cur = cur[part];
56
+ }
57
+ return cur;
58
+ }
59
+ function asObjectBucket(value) {
60
+ const parsed = parseLooseJsonObject(value);
61
+ if (parsed)
62
+ return parsed;
63
+ return isPlainObject(value) ? value : undefined;
64
+ }
65
+ /**
66
+ * Shallow-deep merge for template lookup: primary wins per key; nested plain objects merge recursively.
67
+ */
68
+ function mergeBucketViews(primary, fallback) {
69
+ const pObj = asObjectBucket(primary);
70
+ const fObj = asObjectBucket(fallback);
71
+ if (!pObj && !fObj) {
72
+ if (primary !== undefined && primary !== null)
73
+ return primary;
74
+ return fallback;
75
+ }
76
+ if (!pObj)
77
+ return { ...fObj };
78
+ if (!fObj)
79
+ return { ...pObj };
80
+ const keys = new Set([...Object.keys(pObj), ...Object.keys(fObj)]);
81
+ const view = {};
82
+ for (const key of keys) {
83
+ const pv = pObj[key];
84
+ const fv = fObj[key];
85
+ if (isPlainObject(pv) && isPlainObject(fv)) {
86
+ view[key] = mergeBucketViews(pv, fv);
87
+ }
88
+ else {
89
+ view[key] = pv !== undefined ? pv : fv;
90
+ }
91
+ }
92
+ return view;
93
+ }
94
+ /**
95
+ * Resolve a dotted path against working memory with dual-root rules:
96
+ * - `inputs` / `inputs.*` → caller bag first, then merged `input`
97
+ * - `input` / `input.*` → merged `input` first, then caller `inputs`
98
+ * - other paths → direct lookup on working memory
99
+ */
100
+ export function resolveGatewayMemoryPathValue(workingMemory, path) {
101
+ const trimmed = path.trim();
102
+ if (!trimmed)
103
+ return undefined;
104
+ const dot = trimmed.indexOf('.');
105
+ const root = dot >= 0 ? trimmed.slice(0, dot) : trimmed;
106
+ const tail = dot >= 0 ? trimmed.slice(dot + 1) : '';
107
+ if (root === 'inputs') {
108
+ const primary = extractCallerInputsBag(workingMemory);
109
+ const fallback = coalesceMergedInputBucket(workingMemory);
110
+ if (!tail) {
111
+ if (primary && Object.keys(primary).length > 0)
112
+ return primary;
113
+ return fallback;
114
+ }
115
+ const fromPrimary = primary ? getValueAtPath(primary, tail) : undefined;
116
+ if (fromPrimary !== undefined)
117
+ return fromPrimary;
118
+ return fallback !== undefined ? getValueAtPath(fallback, tail) : undefined;
119
+ }
120
+ if (root === 'input') {
121
+ const primary = coalesceMergedInputBucket(workingMemory);
122
+ const fallback = extractCallerInputsBag(workingMemory);
123
+ if (!tail) {
124
+ if (primary !== undefined && primary !== null)
125
+ return primary;
126
+ return fallback;
127
+ }
128
+ const fromPrimary = primary !== undefined ? getValueAtPath(primary, tail) : undefined;
129
+ if (fromPrimary !== undefined)
130
+ return fromPrimary;
131
+ return fallback ? getValueAtPath(fallback, tail) : undefined;
132
+ }
133
+ return getValueAtPath(workingMemory, trimmed);
134
+ }
135
+ /**
136
+ * Working-memory view for Rendrix / smart-input: preserves all keys but overlays
137
+ * `input` and `inputs` with dual-root merge views (does not rewrite authored paths).
138
+ */
139
+ export function buildMemoryResolutionRootFromWorkingMemory(workingMemory) {
140
+ if (!isPlainObject(workingMemory))
141
+ return {};
142
+ const inputsBag = extractCallerInputsBag(workingMemory);
143
+ const mergedInput = coalesceMergedInputBucket(workingMemory);
144
+ return {
145
+ ...workingMemory,
146
+ input: mergeBucketViews(mergedInput, inputsBag),
147
+ inputs: mergeBucketViews(inputsBag, mergedInput)
148
+ };
149
+ }
150
+ /**
151
+ * When WM carries `input` and/or `inputs`, return a resolution root for template rendering.
152
+ * Otherwise returns the original reference unchanged.
153
+ */
154
+ export function prepareWorkingMemoryForTemplateRender(workingMemory) {
155
+ if (!isPlainObject(workingMemory))
156
+ return workingMemory;
157
+ if (workingMemory.input === undefined && workingMemory.inputs === undefined) {
158
+ return workingMemory;
159
+ }
160
+ return buildMemoryResolutionRootFromWorkingMemory(workingMemory);
161
+ }
162
+ /** Optional migration: rewrite `inputs.*` smart-input / memory paths to `input.*`. */
163
+ export function mapSmartInputPathsInputsToInput(paths) {
164
+ return paths.map((p) => {
165
+ const t = p.trim();
166
+ if (t === 'inputs')
167
+ return 'input';
168
+ if (t.startsWith('inputs.'))
169
+ return `input.${t.slice('inputs.'.length)}`;
170
+ return p;
171
+ });
172
+ }
@@ -4,6 +4,7 @@
4
4
  * Uses @x12i/rendrix (v4+) to parse templates with workingMemory.
5
5
  * TemplateResolutionError from the parser is rethrown; other errors fall back to the raw template.
6
6
  */
7
+ import { prepareWorkingMemoryForTemplateRender } from './memory-path-resolution.js';
7
8
  let rendrixModule = null;
8
9
  let parserLoadPromise = null;
9
10
  async function loadRendrix() {
@@ -48,6 +49,7 @@ export async function parseTemplate(template, workingMemory, taskConfig, shortTe
48
49
  if (!workingMemory) {
49
50
  return template;
50
51
  }
52
+ const wmForRender = prepareWorkingMemoryForTemplateRender(workingMemory);
51
53
  await loadRendrix();
52
54
  if (!rendrixModule) {
53
55
  return template;
@@ -57,18 +59,18 @@ export async function parseTemplate(template, workingMemory, taskConfig, shortTe
57
59
  ? rendrixModule
58
60
  : rendrixModule.default || rendrixModule;
59
61
  if (typeof api.render === 'function') {
60
- return await api.render(template, workingMemory, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
62
+ return await api.render(template, wmForRender, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
61
63
  undefined, // choiceOptions
62
64
  undefined, // filesContent
63
65
  templateRenderOptions);
64
66
  }
65
67
  if (typeof api.parse === 'function') {
66
- return await api.parse(template, workingMemory);
68
+ return await api.parse(template, wmForRender);
67
69
  }
68
70
  else if (typeof api === 'function') {
69
71
  const parser = new api();
70
72
  if (typeof parser.parse === 'function') {
71
- return await parser.parse(template, workingMemory);
73
+ return await parser.parse(template, wmForRender);
72
74
  }
73
75
  }
74
76
  return template;
package/dist/types.d.ts CHANGED
@@ -626,6 +626,10 @@ interface BaseLLMRequest extends Omit<LLMRequest, 'messages' | 'input' | 'reques
626
626
  * Working memory (optional) - Affects all template parsing
627
627
  * Passed to Rendrix (v4+) for template variable substitution.
628
628
  * Plain {{path}} tokens are MUST paths: undefined after merge throws TemplateResolutionError (rethrown by the gateway).
629
+ *
630
+ * Dual roots (graph-engine / ai-tasks contract): `inputs` is the caller / graph-entry bag;
631
+ * `input` is the merged MAIN payload (object or JSON string). Smart-input and `input.*` / `inputs.*`
632
+ * paths resolve with cross-root fallback before render (see `resolveGatewayMemoryPathValue`).
629
633
  */
630
634
  workingMemory?: unknown;
631
635
  /**
@@ -21,7 +21,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
21
21
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
22
22
  };
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.resetObjectTypesLibrary = exports.getObjectTypesLibrary = exports.initializeObjectTypesLibrary = exports.getObjectTypesForAgent = exports.getObjectType = exports.OBJECT_TYPES_LIBRARY = exports.assertValidAIRequest = exports.formatDiagnostic = exports.runValidationTests = exports.createValidationTestCases = exports.createTestAIRequest = exports.supportsJSONMode = exports.diagnoseResponse = exports.diagnoseRequest = exports.validateResponse = exports.extractJSON = exports.validateJSON = exports.validateAIRequest = exports.DEFAULT_RATE_LIMIT_ENABLED = exports.DEFAULT_RATE_LIMIT_MIN_INTERVAL_MS = exports.GatewayRateLimiter = exports.runtimeObjects = exports.DebugLogAbstract = exports.createLogxer = exports.gatewayLogDebug = exports.withActivityIdentity = exports.activityIdentityToLogMeta = exports.ensureGatewayRequestIdentity = exports.ActivityManager = exports.Activix = exports.mergeTemplateRenderOptions = exports.mergeGatewayAndRequestTemplateRenderOptions = exports.pickRequestIdsFromRouterLike = exports.tryExtractRouterLikePayloadFromErrorChain = exports.buildInvokeRejectionMetadata = exports.attachGatewayInvokeRejectionMetadata = exports.autoRegisterProviders = exports.InstructionBackendError = exports.InstructionNotFoundError = exports.AIGateway = exports.FallbackExhaustedError = exports.ProviderNotFoundError = exports.createRouterFromConfig = exports.createRouter = exports.LLMProviderRouter = void 0;
24
+ exports.getObjectTypesForAgent = exports.getObjectType = exports.OBJECT_TYPES_LIBRARY = exports.assertValidAIRequest = exports.formatDiagnostic = exports.runValidationTests = exports.createValidationTestCases = exports.createTestAIRequest = exports.supportsJSONMode = exports.diagnoseResponse = exports.diagnoseRequest = exports.validateResponse = exports.extractJSON = exports.validateJSON = exports.validateAIRequest = exports.DEFAULT_RATE_LIMIT_ENABLED = exports.DEFAULT_RATE_LIMIT_MIN_INTERVAL_MS = exports.GatewayRateLimiter = exports.runtimeObjects = exports.DebugLogAbstract = exports.createLogxer = exports.gatewayLogDebug = exports.withActivityIdentity = exports.activityIdentityToLogMeta = exports.ensureGatewayRequestIdentity = exports.ActivityManager = exports.Activix = exports.resolveGatewayMemoryPathValue = exports.prepareWorkingMemoryForTemplateRender = exports.parseLooseJsonObject = exports.mapSmartInputPathsInputsToInput = exports.extractCallerInputsBag = exports.coalesceMergedInputBucket = exports.buildMemoryResolutionRootFromWorkingMemory = exports.GATEWAY_DUAL_MEMORY_ROOTS = exports.mergeTemplateRenderOptions = exports.mergeGatewayAndRequestTemplateRenderOptions = exports.pickRequestIdsFromRouterLike = exports.tryExtractRouterLikePayloadFromErrorChain = exports.buildInvokeRejectionMetadata = exports.attachGatewayInvokeRejectionMetadata = exports.autoRegisterProviders = exports.InstructionBackendError = exports.InstructionNotFoundError = exports.AIGateway = exports.FallbackExhaustedError = exports.ProviderNotFoundError = exports.createRouterFromConfig = exports.createRouter = exports.LLMProviderRouter = void 0;
25
+ exports.resetObjectTypesLibrary = exports.getObjectTypesLibrary = exports.initializeObjectTypesLibrary = void 0;
25
26
  // Re-export router class and types (base functionality)
26
27
  var ai_providers_router_1 = require("@x12i/ai-providers-router");
27
28
  Object.defineProperty(exports, "LLMProviderRouter", { enumerable: true, get: function () { return ai_providers_router_1.LLMProviderRouter; } });
@@ -51,6 +52,15 @@ Object.defineProperty(exports, "pickRequestIdsFromRouterLike", { enumerable: tru
51
52
  var template_render_merge_js_1 = require("./template-render-merge.cjs");
52
53
  Object.defineProperty(exports, "mergeGatewayAndRequestTemplateRenderOptions", { enumerable: true, get: function () { return template_render_merge_js_1.mergeGatewayAndRequestTemplateRenderOptions; } });
53
54
  Object.defineProperty(exports, "mergeTemplateRenderOptions", { enumerable: true, get: function () { return template_render_merge_js_1.mergeTemplateRenderOptions; } });
55
+ var memory_path_resolution_js_1 = require("./memory-path-resolution.cjs");
56
+ Object.defineProperty(exports, "GATEWAY_DUAL_MEMORY_ROOTS", { enumerable: true, get: function () { return memory_path_resolution_js_1.GATEWAY_DUAL_MEMORY_ROOTS; } });
57
+ Object.defineProperty(exports, "buildMemoryResolutionRootFromWorkingMemory", { enumerable: true, get: function () { return memory_path_resolution_js_1.buildMemoryResolutionRootFromWorkingMemory; } });
58
+ Object.defineProperty(exports, "coalesceMergedInputBucket", { enumerable: true, get: function () { return memory_path_resolution_js_1.coalesceMergedInputBucket; } });
59
+ Object.defineProperty(exports, "extractCallerInputsBag", { enumerable: true, get: function () { return memory_path_resolution_js_1.extractCallerInputsBag; } });
60
+ Object.defineProperty(exports, "mapSmartInputPathsInputsToInput", { enumerable: true, get: function () { return memory_path_resolution_js_1.mapSmartInputPathsInputsToInput; } });
61
+ Object.defineProperty(exports, "parseLooseJsonObject", { enumerable: true, get: function () { return memory_path_resolution_js_1.parseLooseJsonObject; } });
62
+ Object.defineProperty(exports, "prepareWorkingMemoryForTemplateRender", { enumerable: true, get: function () { return memory_path_resolution_js_1.prepareWorkingMemoryForTemplateRender; } });
63
+ Object.defineProperty(exports, "resolveGatewayMemoryPathValue", { enumerable: true, get: function () { return memory_path_resolution_js_1.resolveGatewayMemoryPathValue; } });
54
64
  // Usage tracking: UsageTracker class methods are available but consumption calculation is disabled
55
65
  // (x-models was previously used for RPM/TPM tracking but is no longer integrated)
56
66
  // Re-export activity tracking primitives (Activix)
@@ -20,6 +20,8 @@ export type { GatewayConfig, ProviderModelRef, ModelConfig, RetryConfig, ChatReq
20
20
  export { attachGatewayInvokeRejectionMetadata, buildInvokeRejectionMetadata, tryExtractRouterLikePayloadFromErrorChain, pickRequestIdsFromRouterLike } from './gateway-utils.js';
21
21
  export { mergeGatewayAndRequestTemplateRenderOptions, mergeTemplateRenderOptions } from './template-render-merge.js';
22
22
  export type { GatewayTemplateRenderRequestSlice } from './template-render-merge.js';
23
+ export { GATEWAY_DUAL_MEMORY_ROOTS, buildMemoryResolutionRootFromWorkingMemory, coalesceMergedInputBucket, extractCallerInputsBag, mapSmartInputPathsInputsToInput, parseLooseJsonObject, prepareWorkingMemoryForTemplateRender, resolveGatewayMemoryPathValue } from './memory-path-resolution.js';
24
+ export type { GatewayDualMemoryRoot } from './memory-path-resolution.js';
23
25
  export type { UsageTier } from './types.js';
24
26
  export { Activix } from '@x12i/activix';
25
27
  export type { ActivixRunContext, FindByRunContextCriteria, GetJobActivitiesInput, GetJobActivitiesResult } from '@x12i/activix';
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ /**
3
+ * Dual memory roots `input` (merged MAIN payload) and `inputs` (caller / graph-entry bag).
4
+ * Aligned with @exellix/graph-engine ≥ 5.5.0 resolution rules for template / smart-input paths.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.GATEWAY_DUAL_MEMORY_ROOTS = void 0;
8
+ exports.parseLooseJsonObject = parseLooseJsonObject;
9
+ exports.extractCallerInputsBag = extractCallerInputsBag;
10
+ exports.coalesceMergedInputBucket = coalesceMergedInputBucket;
11
+ exports.resolveGatewayMemoryPathValue = resolveGatewayMemoryPathValue;
12
+ exports.buildMemoryResolutionRootFromWorkingMemory = buildMemoryResolutionRootFromWorkingMemory;
13
+ exports.prepareWorkingMemoryForTemplateRender = prepareWorkingMemoryForTemplateRender;
14
+ exports.mapSmartInputPathsInputsToInput = mapSmartInputPathsInputsToInput;
15
+ exports.GATEWAY_DUAL_MEMORY_ROOTS = ['input', 'inputs'];
16
+ function isPlainObject(value) {
17
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
18
+ }
19
+ /**
20
+ * Parse JSON object shapes from strings (matches graph-engine `parseLooseJsonObject`).
21
+ */
22
+ function parseLooseJsonObject(value) {
23
+ if (isPlainObject(value))
24
+ return value;
25
+ if (typeof value !== 'string')
26
+ return undefined;
27
+ const trimmed = value.trim();
28
+ if (!trimmed.startsWith('{'))
29
+ return undefined;
30
+ try {
31
+ const parsed = JSON.parse(trimmed);
32
+ return isPlainObject(parsed) ? parsed : undefined;
33
+ }
34
+ catch {
35
+ return undefined;
36
+ }
37
+ }
38
+ /** Caller / graph-entry bag from `workingMemory.inputs`. */
39
+ function extractCallerInputsBag(workingMemory) {
40
+ if (!isPlainObject(workingMemory))
41
+ return undefined;
42
+ const inputs = workingMemory.inputs;
43
+ return isPlainObject(inputs) ? inputs : undefined;
44
+ }
45
+ /** Merged MAIN bucket from `workingMemory.input` (object or parsed JSON string). */
46
+ function coalesceMergedInputBucket(workingMemory) {
47
+ if (!isPlainObject(workingMemory))
48
+ return undefined;
49
+ const raw = workingMemory.input;
50
+ if (raw === undefined || raw === null)
51
+ return undefined;
52
+ const parsed = parseLooseJsonObject(raw);
53
+ return parsed !== undefined ? parsed : raw;
54
+ }
55
+ function getValueAtPath(obj, path) {
56
+ if (obj == null)
57
+ return undefined;
58
+ if (path === '' || path === 'this' || path === '.')
59
+ return obj;
60
+ const parts = path.split('.');
61
+ let cur = obj;
62
+ for (const part of parts) {
63
+ if (cur == null || typeof cur !== 'object')
64
+ return undefined;
65
+ cur = cur[part];
66
+ }
67
+ return cur;
68
+ }
69
+ function asObjectBucket(value) {
70
+ const parsed = parseLooseJsonObject(value);
71
+ if (parsed)
72
+ return parsed;
73
+ return isPlainObject(value) ? value : undefined;
74
+ }
75
+ /**
76
+ * Shallow-deep merge for template lookup: primary wins per key; nested plain objects merge recursively.
77
+ */
78
+ function mergeBucketViews(primary, fallback) {
79
+ const pObj = asObjectBucket(primary);
80
+ const fObj = asObjectBucket(fallback);
81
+ if (!pObj && !fObj) {
82
+ if (primary !== undefined && primary !== null)
83
+ return primary;
84
+ return fallback;
85
+ }
86
+ if (!pObj)
87
+ return { ...fObj };
88
+ if (!fObj)
89
+ return { ...pObj };
90
+ const keys = new Set([...Object.keys(pObj), ...Object.keys(fObj)]);
91
+ const view = {};
92
+ for (const key of keys) {
93
+ const pv = pObj[key];
94
+ const fv = fObj[key];
95
+ if (isPlainObject(pv) && isPlainObject(fv)) {
96
+ view[key] = mergeBucketViews(pv, fv);
97
+ }
98
+ else {
99
+ view[key] = pv !== undefined ? pv : fv;
100
+ }
101
+ }
102
+ return view;
103
+ }
104
+ /**
105
+ * Resolve a dotted path against working memory with dual-root rules:
106
+ * - `inputs` / `inputs.*` → caller bag first, then merged `input`
107
+ * - `input` / `input.*` → merged `input` first, then caller `inputs`
108
+ * - other paths → direct lookup on working memory
109
+ */
110
+ function resolveGatewayMemoryPathValue(workingMemory, path) {
111
+ const trimmed = path.trim();
112
+ if (!trimmed)
113
+ return undefined;
114
+ const dot = trimmed.indexOf('.');
115
+ const root = dot >= 0 ? trimmed.slice(0, dot) : trimmed;
116
+ const tail = dot >= 0 ? trimmed.slice(dot + 1) : '';
117
+ if (root === 'inputs') {
118
+ const primary = extractCallerInputsBag(workingMemory);
119
+ const fallback = coalesceMergedInputBucket(workingMemory);
120
+ if (!tail) {
121
+ if (primary && Object.keys(primary).length > 0)
122
+ return primary;
123
+ return fallback;
124
+ }
125
+ const fromPrimary = primary ? getValueAtPath(primary, tail) : undefined;
126
+ if (fromPrimary !== undefined)
127
+ return fromPrimary;
128
+ return fallback !== undefined ? getValueAtPath(fallback, tail) : undefined;
129
+ }
130
+ if (root === 'input') {
131
+ const primary = coalesceMergedInputBucket(workingMemory);
132
+ const fallback = extractCallerInputsBag(workingMemory);
133
+ if (!tail) {
134
+ if (primary !== undefined && primary !== null)
135
+ return primary;
136
+ return fallback;
137
+ }
138
+ const fromPrimary = primary !== undefined ? getValueAtPath(primary, tail) : undefined;
139
+ if (fromPrimary !== undefined)
140
+ return fromPrimary;
141
+ return fallback ? getValueAtPath(fallback, tail) : undefined;
142
+ }
143
+ return getValueAtPath(workingMemory, trimmed);
144
+ }
145
+ /**
146
+ * Working-memory view for Rendrix / smart-input: preserves all keys but overlays
147
+ * `input` and `inputs` with dual-root merge views (does not rewrite authored paths).
148
+ */
149
+ function buildMemoryResolutionRootFromWorkingMemory(workingMemory) {
150
+ if (!isPlainObject(workingMemory))
151
+ return {};
152
+ const inputsBag = extractCallerInputsBag(workingMemory);
153
+ const mergedInput = coalesceMergedInputBucket(workingMemory);
154
+ return {
155
+ ...workingMemory,
156
+ input: mergeBucketViews(mergedInput, inputsBag),
157
+ inputs: mergeBucketViews(inputsBag, mergedInput)
158
+ };
159
+ }
160
+ /**
161
+ * When WM carries `input` and/or `inputs`, return a resolution root for template rendering.
162
+ * Otherwise returns the original reference unchanged.
163
+ */
164
+ function prepareWorkingMemoryForTemplateRender(workingMemory) {
165
+ if (!isPlainObject(workingMemory))
166
+ return workingMemory;
167
+ if (workingMemory.input === undefined && workingMemory.inputs === undefined) {
168
+ return workingMemory;
169
+ }
170
+ return buildMemoryResolutionRootFromWorkingMemory(workingMemory);
171
+ }
172
+ /** Optional migration: rewrite `inputs.*` smart-input / memory paths to `input.*`. */
173
+ function mapSmartInputPathsInputsToInput(paths) {
174
+ return paths.map((p) => {
175
+ const t = p.trim();
176
+ if (t === 'inputs')
177
+ return 'input';
178
+ if (t.startsWith('inputs.'))
179
+ return `input.${t.slice('inputs.'.length)}`;
180
+ return p;
181
+ });
182
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Dual memory roots `input` (merged MAIN payload) and `inputs` (caller / graph-entry bag).
3
+ * Aligned with @exellix/graph-engine ≥ 5.5.0 resolution rules for template / smart-input paths.
4
+ */
5
+ export declare const GATEWAY_DUAL_MEMORY_ROOTS: readonly ["input", "inputs"];
6
+ export type GatewayDualMemoryRoot = (typeof GATEWAY_DUAL_MEMORY_ROOTS)[number];
7
+ /**
8
+ * Parse JSON object shapes from strings (matches graph-engine `parseLooseJsonObject`).
9
+ */
10
+ export declare function parseLooseJsonObject(value: unknown): Record<string, unknown> | undefined;
11
+ /** Caller / graph-entry bag from `workingMemory.inputs`. */
12
+ export declare function extractCallerInputsBag(workingMemory: unknown): Record<string, unknown> | undefined;
13
+ /** Merged MAIN bucket from `workingMemory.input` (object or parsed JSON string). */
14
+ export declare function coalesceMergedInputBucket(workingMemory: unknown): unknown;
15
+ /**
16
+ * Resolve a dotted path against working memory with dual-root rules:
17
+ * - `inputs` / `inputs.*` → caller bag first, then merged `input`
18
+ * - `input` / `input.*` → merged `input` first, then caller `inputs`
19
+ * - other paths → direct lookup on working memory
20
+ */
21
+ export declare function resolveGatewayMemoryPathValue(workingMemory: unknown, path: string): unknown;
22
+ /**
23
+ * Working-memory view for Rendrix / smart-input: preserves all keys but overlays
24
+ * `input` and `inputs` with dual-root merge views (does not rewrite authored paths).
25
+ */
26
+ export declare function buildMemoryResolutionRootFromWorkingMemory(workingMemory: unknown): Record<string, unknown>;
27
+ /**
28
+ * When WM carries `input` and/or `inputs`, return a resolution root for template rendering.
29
+ * Otherwise returns the original reference unchanged.
30
+ */
31
+ export declare function prepareWorkingMemoryForTemplateRender(workingMemory: unknown): unknown;
32
+ /** Optional migration: rewrite `inputs.*` smart-input / memory paths to `input.*`. */
33
+ export declare function mapSmartInputPathsInputsToInput(paths: string[]): string[];
@@ -41,6 +41,7 @@ var __importStar = (this && this.__importStar) || (function () {
41
41
  Object.defineProperty(exports, "__esModule", { value: true });
42
42
  exports.parseTemplate = parseTemplate;
43
43
  exports.isParserAvailable = isParserAvailable;
44
+ const memory_path_resolution_js_1 = require("./memory-path-resolution.cjs");
44
45
  let rendrixModule = null;
45
46
  let parserLoadPromise = null;
46
47
  async function loadRendrix() {
@@ -85,6 +86,7 @@ async function parseTemplate(template, workingMemory, taskConfig, shortTermMemor
85
86
  if (!workingMemory) {
86
87
  return template;
87
88
  }
89
+ const wmForRender = (0, memory_path_resolution_js_1.prepareWorkingMemoryForTemplateRender)(workingMemory);
88
90
  await loadRendrix();
89
91
  if (!rendrixModule) {
90
92
  return template;
@@ -94,18 +96,18 @@ async function parseTemplate(template, workingMemory, taskConfig, shortTermMemor
94
96
  ? rendrixModule
95
97
  : rendrixModule.default || rendrixModule;
96
98
  if (typeof api.render === 'function') {
97
- return await api.render(template, workingMemory, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
99
+ return await api.render(template, wmForRender, shortTermMemory, experienceMemory, knowledgeMemory, undefined, // functionsMap
98
100
  undefined, // choiceOptions
99
101
  undefined, // filesContent
100
102
  templateRenderOptions);
101
103
  }
102
104
  if (typeof api.parse === 'function') {
103
- return await api.parse(template, workingMemory);
105
+ return await api.parse(template, wmForRender);
104
106
  }
105
107
  else if (typeof api === 'function') {
106
108
  const parser = new api();
107
109
  if (typeof parser.parse === 'function') {
108
- return await parser.parse(template, workingMemory);
110
+ return await parser.parse(template, wmForRender);
109
111
  }
110
112
  }
111
113
  return template;
@@ -626,6 +626,10 @@ interface BaseLLMRequest extends Omit<LLMRequest, 'messages' | 'input' | 'reques
626
626
  * Working memory (optional) - Affects all template parsing
627
627
  * Passed to Rendrix (v4+) for template variable substitution.
628
628
  * Plain {{path}} tokens are MUST paths: undefined after merge throws TemplateResolutionError (rethrown by the gateway).
629
+ *
630
+ * Dual roots (graph-engine / ai-tasks contract): `inputs` is the caller / graph-entry bag;
631
+ * `input` is the merged MAIN payload (object or JSON string). Smart-input and `input.*` / `inputs.*`
632
+ * paths resolve with cross-root fallback before render (see `resolveGatewayMemoryPathValue`).
629
633
  */
630
634
  workingMemory?: unknown;
631
635
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-gateway",
3
- "version": "9.1.5",
3
+ "version": "9.1.6",
4
4
  "description": "AI Gateway - Unified interface for LLM provider routing and management",
5
5
  "type": "module",
6
6
  "exports": {