@exellix/graph-engine 6.0.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.
- package/.env.example +3 -0
- package/CHANGELOG.md +208 -0
- package/README.md +827 -0
- package/dist/src/errors/ExellixGraphError.d.ts +38 -0
- package/dist/src/errors/ExellixGraphError.js +21 -0
- package/dist/src/errors/exellixGraphErrorCodes.d.ts +31 -0
- package/dist/src/errors/exellixGraphErrorCodes.js +32 -0
- package/dist/src/index.d.ts +100 -0
- package/dist/src/index.js +75 -0
- package/dist/src/inspection/contractInspection.d.ts +21 -0
- package/dist/src/inspection/contractInspection.js +526 -0
- package/dist/src/inspection/contractTypes.d.ts +137 -0
- package/dist/src/inspection/contractTypes.js +1 -0
- package/dist/src/inspection/controlInspection.d.ts +22 -0
- package/dist/src/inspection/controlInspection.js +130 -0
- package/dist/src/inspection/graphInspection.d.ts +51 -0
- package/dist/src/inspection/graphInspection.js +467 -0
- package/dist/src/inspection/index.d.ts +21 -0
- package/dist/src/inspection/index.js +17 -0
- package/dist/src/inspection/nodeInspection.d.ts +42 -0
- package/dist/src/inspection/nodeInspection.js +474 -0
- package/dist/src/inspection/types.d.ts +321 -0
- package/dist/src/inspection/types.js +14 -0
- package/dist/src/inspection/validateAiTasksNodeExtensions.d.ts +12 -0
- package/dist/src/inspection/validateAiTasksNodeExtensions.js +119 -0
- package/dist/src/inspection/validateCatalogPlanning.d.ts +21 -0
- package/dist/src/inspection/validateCatalogPlanning.js +187 -0
- package/dist/src/integrations/ActivityTrackerIntegration.d.ts +86 -0
- package/dist/src/integrations/ActivityTrackerIntegration.js +134 -0
- package/dist/src/integrations/ActivixGraphRunIntegration.d.ts +34 -0
- package/dist/src/integrations/ActivixGraphRunIntegration.js +338 -0
- package/dist/src/integrations/ActivixNodeActivityIntegration.d.ts +33 -0
- package/dist/src/integrations/ActivixNodeActivityIntegration.js +220 -0
- package/dist/src/integrations/cataloxGraphCatalog.d.ts +21 -0
- package/dist/src/integrations/cataloxGraphCatalog.js +30 -0
- package/dist/src/integrations/createActivixExellixIntegration.d.ts +14 -0
- package/dist/src/integrations/createActivixExellixIntegration.js +16 -0
- package/dist/src/integrations/createActivixFromEnv.d.ts +31 -0
- package/dist/src/integrations/createActivixFromEnv.js +53 -0
- package/dist/src/loaders/FileGraphLoader.d.ts +23 -0
- package/dist/src/loaders/FileGraphLoader.js +31 -0
- package/dist/src/playground/PlaygroundReporter.d.ts +40 -0
- package/dist/src/playground/PlaygroundReporter.js +480 -0
- package/dist/src/playground/index.d.ts +1 -0
- package/dist/src/playground/index.js +1 -0
- package/dist/src/runtime/ExellixGraphRuntime.d.ts +263 -0
- package/dist/src/runtime/ExellixGraphRuntime.js +1716 -0
- package/dist/src/runtime/GraphEngine.d.ts +33 -0
- package/dist/src/runtime/GraphEngine.js +4 -0
- package/dist/src/runtime/aiTasksObservability.d.ts +6 -0
- package/dist/src/runtime/aiTasksObservability.js +37 -0
- package/dist/src/runtime/aiTasksStrategyPhases.d.ts +46 -0
- package/dist/src/runtime/aiTasksStrategyPhases.js +93 -0
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.d.ts +17 -0
- package/dist/src/runtime/applyAiTaskProfileWebScopingToNarrix.js +46 -0
- package/dist/src/runtime/buildAiTasksRunTaskRequest.d.ts +67 -0
- package/dist/src/runtime/buildAiTasksRunTaskRequest.js +164 -0
- package/dist/src/runtime/buildRunLog.d.ts +27 -0
- package/dist/src/runtime/buildRunLog.js +234 -0
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.d.ts +9 -0
- package/dist/src/runtime/buildRunTaskTaskConfigurationForward.js +80 -0
- package/dist/src/runtime/buildTaskNodeJobContext.d.ts +11 -0
- package/dist/src/runtime/buildTaskNodeJobContext.js +30 -0
- package/dist/src/runtime/canonicalModelUsed.d.ts +6 -0
- package/dist/src/runtime/canonicalModelUsed.js +36 -0
- package/dist/src/runtime/contextualScope.d.ts +7 -0
- package/dist/src/runtime/contextualScope.js +121 -0
- package/dist/src/runtime/dataFiltersEvaluation.d.ts +60 -0
- package/dist/src/runtime/dataFiltersEvaluation.js +169 -0
- package/dist/src/runtime/deepMerge.d.ts +5 -0
- package/dist/src/runtime/deepMerge.js +22 -0
- package/dist/src/runtime/events.d.ts +92 -0
- package/dist/src/runtime/events.js +122 -0
- package/dist/src/runtime/executionMatrixHost.d.ts +98 -0
- package/dist/src/runtime/executionMatrixHost.js +134 -0
- package/dist/src/runtime/executionVariableBuckets.d.ts +67 -0
- package/dist/src/runtime/executionVariableBuckets.js +96 -0
- package/dist/src/runtime/finalizers/errors.d.ts +9 -0
- package/dist/src/runtime/finalizers/errors.js +10 -0
- package/dist/src/runtime/finalizers/executeFinalizer.d.ts +40 -0
- package/dist/src/runtime/finalizers/executeFinalizer.js +471 -0
- package/dist/src/runtime/finalizers/schema.d.ts +18 -0
- package/dist/src/runtime/finalizers/schema.js +63 -0
- package/dist/src/runtime/finalizers/validateFinalizer.d.ts +16 -0
- package/dist/src/runtime/finalizers/validateFinalizer.js +534 -0
- package/dist/src/runtime/graphDocumentFingerprint.d.ts +8 -0
- package/dist/src/runtime/graphDocumentFingerprint.js +21 -0
- package/dist/src/runtime/graphEngineMemoryPaths.d.ts +12 -0
- package/dist/src/runtime/graphEngineMemoryPaths.js +55 -0
- package/dist/src/runtime/graphResponseMapping.d.ts +23 -0
- package/dist/src/runtime/graphResponseMapping.js +156 -0
- package/dist/src/runtime/graphResponseMigration.d.ts +7 -0
- package/dist/src/runtime/graphResponseMigration.js +44 -0
- package/dist/src/runtime/graphRunExecutionSeed.d.ts +29 -0
- package/dist/src/runtime/graphRunExecutionSeed.js +61 -0
- package/dist/src/runtime/graphRunIdentity.d.ts +7 -0
- package/dist/src/runtime/graphRunIdentity.js +18 -0
- package/dist/src/runtime/localSkills/deterministicRule.d.ts +137 -0
- package/dist/src/runtime/localSkills/deterministicRule.js +196 -0
- package/dist/src/runtime/localSkills/index.d.ts +12 -0
- package/dist/src/runtime/localSkills/index.js +14 -0
- package/dist/src/runtime/localSkills/memorixItemToScopedOutput.d.ts +7 -0
- package/dist/src/runtime/localSkills/memorixItemToScopedOutput.js +104 -0
- package/dist/src/runtime/localSkills/memorixRuntime.d.ts +9 -0
- package/dist/src/runtime/localSkills/memorixRuntime.js +70 -0
- package/dist/src/runtime/localSkills/memorixScopedConfig.d.ts +16 -0
- package/dist/src/runtime/localSkills/memorixScopedConfig.js +18 -0
- package/dist/src/runtime/localSkills/scopedAnswerAssembler.d.ts +23 -0
- package/dist/src/runtime/localSkills/scopedAnswerAssembler.js +35 -0
- package/dist/src/runtime/localSkills/scopedAnswerFields.d.ts +12 -0
- package/dist/src/runtime/localSkills/scopedAnswerFields.js +66 -0
- package/dist/src/runtime/localSkills/scopedAnswerWriter.d.ts +32 -0
- package/dist/src/runtime/localSkills/scopedAnswerWriter.js +156 -0
- package/dist/src/runtime/localSkills/scopedDataReader.d.ts +47 -0
- package/dist/src/runtime/localSkills/scopedDataReader.js +89 -0
- package/dist/src/runtime/localSkills/utils.d.ts +12 -0
- package/dist/src/runtime/localSkills/utils.js +39 -0
- package/dist/src/runtime/materializeStructuredRunTaskInput.d.ts +9 -0
- package/dist/src/runtime/materializeStructuredRunTaskInput.js +34 -0
- package/dist/src/runtime/memory.d.ts +51 -0
- package/dist/src/runtime/memory.js +250 -0
- package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.d.ts +18 -0
- package/dist/src/runtime/mergeExellixGraphRuntimeInvocation.js +32 -0
- package/dist/src/runtime/modelConfigSelection.d.ts +7 -0
- package/dist/src/runtime/modelConfigSelection.js +37 -0
- package/dist/src/runtime/narrixIngestEnv.d.ts +9 -0
- package/dist/src/runtime/narrixIngestEnv.js +18 -0
- package/dist/src/runtime/pathExpr.d.ts +36 -0
- package/dist/src/runtime/pathExpr.js +131 -0
- package/dist/src/runtime/predicates.d.ts +14 -0
- package/dist/src/runtime/predicates.js +86 -0
- package/dist/src/runtime/readTaskNodeInputsConfig.d.ts +23 -0
- package/dist/src/runtime/readTaskNodeInputsConfig.js +27 -0
- package/dist/src/runtime/resolveExecutionPipelineForTaskNode.d.ts +11 -0
- package/dist/src/runtime/resolveExecutionPipelineForTaskNode.js +93 -0
- package/dist/src/runtime/resolveGraphEngineMemoryPaths.d.ts +63 -0
- package/dist/src/runtime/resolveGraphEngineMemoryPaths.js +213 -0
- package/dist/src/runtime/resolveModelConfigForNode.d.ts +20 -0
- package/dist/src/runtime/resolveModelConfigForNode.js +69 -0
- package/dist/src/runtime/resolveNarrixForTaskNode.d.ts +14 -0
- package/dist/src/runtime/resolveNarrixForTaskNode.js +19 -0
- package/dist/src/runtime/resolveTaskKey.d.ts +11 -0
- package/dist/src/runtime/resolveTaskKey.js +28 -0
- package/dist/src/runtime/resolveTaskNodeInputs.d.ts +25 -0
- package/dist/src/runtime/resolveTaskNodeInputs.js +140 -0
- package/dist/src/runtime/runTaskAugments.d.ts +17 -0
- package/dist/src/runtime/runTaskAugments.js +37 -0
- package/dist/src/runtime/runTaskResponse.d.ts +4 -0
- package/dist/src/runtime/runTaskResponse.js +13 -0
- package/dist/src/runtime/runtimeObjects.d.ts +85 -0
- package/dist/src/runtime/runtimeObjects.js +50 -0
- package/dist/src/runtime/smartInputPaths.d.ts +13 -0
- package/dist/src/runtime/smartInputPaths.js +38 -0
- package/dist/src/runtime/stepRetry.d.ts +21 -0
- package/dist/src/runtime/stepRetry.js +238 -0
- package/dist/src/runtime/synthesizedContextPipeline.d.ts +12 -0
- package/dist/src/runtime/synthesizedContextPipeline.js +28 -0
- package/dist/src/runtime/taskNodeConditionsEvaluation.d.ts +27 -0
- package/dist/src/runtime/taskNodeConditionsEvaluation.js +140 -0
- package/dist/src/runtime/taskNodeMainReadiness.d.ts +45 -0
- package/dist/src/runtime/taskNodeMainReadiness.js +164 -0
- package/dist/src/runtime/taskNodeRunTaskPreflight.d.ts +89 -0
- package/dist/src/runtime/taskNodeRunTaskPreflight.js +204 -0
- package/dist/src/runtime/validateCanonicalGraphDocument.d.ts +25 -0
- package/dist/src/runtime/validateCanonicalGraphDocument.js +567 -0
- package/dist/src/runtime/variables.d.ts +2 -0
- package/dist/src/runtime/variables.js +1 -0
- package/dist/src/runtime/withTimeout.d.ts +5 -0
- package/dist/src/runtime/withTimeout.js +20 -0
- package/dist/src/types/aiTaskProfile.d.ts +41 -0
- package/dist/src/types/aiTaskProfile.js +6 -0
- package/dist/src/types/aiTasksDerivedTypes.d.ts +5 -0
- package/dist/src/types/aiTasksDerivedTypes.js +1 -0
- package/dist/src/types/events.d.ts +23 -0
- package/dist/src/types/events.js +1 -0
- package/dist/src/types/job.d.ts +9 -0
- package/dist/src/types/job.js +1 -0
- package/dist/src/types/narrix.d.ts +60 -0
- package/dist/src/types/narrix.js +1 -0
- package/dist/src/types/options.d.ts +122 -0
- package/dist/src/types/options.js +1 -0
- package/dist/src/types/refs.d.ts +747 -0
- package/dist/src/types/refs.js +12 -0
- package/dist/src/types/results.d.ts +103 -0
- package/dist/src/types/results.js +1 -0
- package/dist/src/types/runLog.d.ts +72 -0
- package/dist/src/types/runLog.js +18 -0
- package/dist/src/types/taskNodeConfiguration.d.ts +95 -0
- package/dist/src/types/taskNodeConfiguration.js +3 -0
- package/dist/src/util/packageVersion.d.ts +2 -0
- package/dist/src/util/packageVersion.js +12 -0
- package/dist/testkit/RealTasksClient.d.ts +16 -0
- package/dist/testkit/RealTasksClient.js +143 -0
- package/dist/testkit/depGraphEngineFactory.d.ts +6 -0
- package/dist/testkit/depGraphEngineFactory.js +54 -0
- package/dist/testkit/exellixRuntimeObjects.d.ts +7 -0
- package/dist/testkit/exellixRuntimeObjects.js +25 -0
- package/dist/testkit/inMemoryGraphLoader.d.ts +6 -0
- package/dist/testkit/inMemoryGraphLoader.js +12 -0
- package/dist/testkit/index.d.ts +4 -0
- package/dist/testkit/index.js +4 -0
- package/package.json +70 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deterministic-rule — local deterministic skill.
|
|
3
|
+
*
|
|
4
|
+
* Evaluates a rule table against executionMemory (or a custom root path) and returns
|
|
5
|
+
* the first matching rule's output, a full evaluation trace, and optional sensitivity
|
|
6
|
+
* analysis for UNKNOWN-tagged inputs (DQ-1 swing calculation).
|
|
7
|
+
*
|
|
8
|
+
* Node config (in node.taskConfiguration):
|
|
9
|
+
* rules : Rule[] — ordered list of rules
|
|
10
|
+
* firstMatchWins : boolean — default true; when false all matching rules fire
|
|
11
|
+
* defaultOutput : object? — emitted when no rule matches
|
|
12
|
+
* sensitivityAnalysis : SensitivityAnalysisConfig? — only used by DQ-1
|
|
13
|
+
*
|
|
14
|
+
* Each rule:
|
|
15
|
+
* id : string
|
|
16
|
+
* label? : string — human-readable description
|
|
17
|
+
* condition : Condition — nested logical condition
|
|
18
|
+
* output : Record<string,unknown> — merged into result when this rule fires
|
|
19
|
+
* stopOnMatch? : boolean — default true (respected when firstMatchWins=true)
|
|
20
|
+
*
|
|
21
|
+
* Condition operators (leaf):
|
|
22
|
+
* { path, eq } — strict equality
|
|
23
|
+
* { path, in } — value in array
|
|
24
|
+
* { path, gte } — >= number
|
|
25
|
+
* { path, gt } — > number
|
|
26
|
+
* { path, lte } — <= number
|
|
27
|
+
* { path, lt } — < number
|
|
28
|
+
* { path, exists } — boolean (truthy check when true, falsy check when false)
|
|
29
|
+
* { path, contains } — string or array contains value
|
|
30
|
+
* { path, regex } — string matches pattern
|
|
31
|
+
* Compound:
|
|
32
|
+
* { all: Condition[] }
|
|
33
|
+
* { any: Condition[] }
|
|
34
|
+
* { not: Condition }
|
|
35
|
+
*
|
|
36
|
+
* Output:
|
|
37
|
+
* {
|
|
38
|
+
* matched : boolean
|
|
39
|
+
* rule_id : string | null
|
|
40
|
+
* rule_label : string | null
|
|
41
|
+
* output : Record<string,unknown>
|
|
42
|
+
* evaluation_trace : { ruleId, matched, shortCircuited }[]
|
|
43
|
+
* sensitivity? : SensitivityResult — only when sensitivityAnalysis is configured
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
import { resolveDotPath } from './utils.js';
|
|
47
|
+
// ─── Condition evaluation ──────────────────────────────────────────────────────
|
|
48
|
+
function evalCondition(cond, root, pathOpts) {
|
|
49
|
+
if ('all' in cond) {
|
|
50
|
+
return cond.all.every((c) => evalCondition(c, root, pathOpts));
|
|
51
|
+
}
|
|
52
|
+
if ('any' in cond) {
|
|
53
|
+
return cond.any.some((c) => evalCondition(c, root, pathOpts));
|
|
54
|
+
}
|
|
55
|
+
if ('not' in cond) {
|
|
56
|
+
return !evalCondition(cond.not, root, pathOpts);
|
|
57
|
+
}
|
|
58
|
+
const leaf = cond;
|
|
59
|
+
const val = resolveDotPath(root, leaf.path, pathOpts);
|
|
60
|
+
if ('exists' in leaf) {
|
|
61
|
+
const e = leaf.exists;
|
|
62
|
+
return e ? val != null : val == null;
|
|
63
|
+
}
|
|
64
|
+
if ('eq' in leaf)
|
|
65
|
+
return val === leaf.eq;
|
|
66
|
+
if ('in' in leaf) {
|
|
67
|
+
const arr = leaf.in;
|
|
68
|
+
return Array.isArray(arr) && arr.includes(val);
|
|
69
|
+
}
|
|
70
|
+
if ('gte' in leaf)
|
|
71
|
+
return typeof val === 'number' && val >= leaf.gte;
|
|
72
|
+
if ('gt' in leaf)
|
|
73
|
+
return typeof val === 'number' && val > leaf.gt;
|
|
74
|
+
if ('lte' in leaf)
|
|
75
|
+
return typeof val === 'number' && val <= leaf.lte;
|
|
76
|
+
if ('lt' in leaf)
|
|
77
|
+
return typeof val === 'number' && val < leaf.lt;
|
|
78
|
+
if ('contains' in leaf) {
|
|
79
|
+
const target = leaf.contains;
|
|
80
|
+
if (Array.isArray(val))
|
|
81
|
+
return val.includes(target);
|
|
82
|
+
if (typeof val === 'string' && typeof target === 'string')
|
|
83
|
+
return val.includes(target);
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
if ('regex' in leaf) {
|
|
87
|
+
if (typeof val !== 'string')
|
|
88
|
+
return false;
|
|
89
|
+
return new RegExp(leaf.regex).test(val);
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
// ─── Rule evaluation (single pass) ────────────────────────────────────────────
|
|
94
|
+
function evaluateRules(rules, root, firstMatchWins, pathOpts) {
|
|
95
|
+
const trace = [];
|
|
96
|
+
let mergedOutput = {};
|
|
97
|
+
let matchedId = null;
|
|
98
|
+
let matchedLabel = null;
|
|
99
|
+
let anyMatched = false;
|
|
100
|
+
for (const rule of rules) {
|
|
101
|
+
const hit = evalCondition(rule.condition, root, pathOpts);
|
|
102
|
+
trace.push({ ruleId: rule.id, matched: hit });
|
|
103
|
+
if (hit) {
|
|
104
|
+
anyMatched = true;
|
|
105
|
+
matchedId = rule.id;
|
|
106
|
+
matchedLabel = rule.label ?? null;
|
|
107
|
+
mergedOutput = { ...mergedOutput, ...rule.output };
|
|
108
|
+
if (firstMatchWins) {
|
|
109
|
+
// Mark remaining rules as short-circuited
|
|
110
|
+
for (let i = trace.length; i < rules.length; i++) {
|
|
111
|
+
trace.push({ ruleId: rules[i].id, matched: false, shortCircuited: true });
|
|
112
|
+
}
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
matched: anyMatched,
|
|
119
|
+
rule_id: matchedId,
|
|
120
|
+
rule_label: matchedLabel,
|
|
121
|
+
output: mergedOutput,
|
|
122
|
+
trace,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// ─── Sensitivity analysis ──────────────────────────────────────────────────────
|
|
126
|
+
function runSensitivity(cfg, rules, baseRoot, firstMatchWins, basePriority, pathOpts) {
|
|
127
|
+
const unknowns = (resolveDotPath(baseRoot, cfg.unknownsPath, pathOpts) ?? []);
|
|
128
|
+
const rankOf = (p) => {
|
|
129
|
+
if (p == null)
|
|
130
|
+
return cfg.priorityRank.length;
|
|
131
|
+
const idx = cfg.priorityRank.indexOf(p);
|
|
132
|
+
return idx === -1 ? cfg.priorityRank.length : idx;
|
|
133
|
+
};
|
|
134
|
+
const entries = [];
|
|
135
|
+
for (const u of unknowns) {
|
|
136
|
+
if (u.tag !== 'UNKNOWN' || !u.path)
|
|
137
|
+
continue;
|
|
138
|
+
const rootStr = JSON.stringify(baseRoot);
|
|
139
|
+
const applySubstitute = (rootObj, path, value) => {
|
|
140
|
+
const parts = path.split('.');
|
|
141
|
+
const clone = JSON.parse(JSON.stringify(rootObj));
|
|
142
|
+
let cur = clone;
|
|
143
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
144
|
+
if (cur[parts[i]] == null)
|
|
145
|
+
cur[parts[i]] = {};
|
|
146
|
+
cur = cur[parts[i]];
|
|
147
|
+
}
|
|
148
|
+
cur[parts[parts.length - 1]] = value;
|
|
149
|
+
return clone;
|
|
150
|
+
};
|
|
151
|
+
const upRoot = applySubstitute(JSON.parse(rootStr), u.path, u.up_substitute);
|
|
152
|
+
const downRoot = applySubstitute(JSON.parse(rootStr), u.path, u.down_substitute);
|
|
153
|
+
const upResult = evaluateRules(rules, upRoot, firstMatchWins, pathOpts);
|
|
154
|
+
const downResult = evaluateRules(rules, downRoot, firstMatchWins, pathOpts);
|
|
155
|
+
const upPriority = upResult.matched
|
|
156
|
+
? (String(upResult.output[cfg.priorityField] ?? '') || null)
|
|
157
|
+
: null;
|
|
158
|
+
const downPriority = downResult.matched
|
|
159
|
+
? (String(downResult.output[cfg.priorityField] ?? '') || null)
|
|
160
|
+
: null;
|
|
161
|
+
const swing = Math.abs(rankOf(upPriority) - rankOf(downPriority));
|
|
162
|
+
entries.push({
|
|
163
|
+
unknown_id: u.id,
|
|
164
|
+
up_substitute: u.up_substitute,
|
|
165
|
+
down_substitute: u.down_substitute,
|
|
166
|
+
up_priority: upPriority,
|
|
167
|
+
down_priority: downPriority,
|
|
168
|
+
swing,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
any_high_swing: entries.some((e) => e.swing >= 2),
|
|
173
|
+
entries,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
// ─── Public entry point ────────────────────────────────────────────────────────
|
|
177
|
+
export function runDeterministicRule(cfg, executionMemory, pathOpts) {
|
|
178
|
+
const rules = Array.isArray(cfg.rules) ? cfg.rules : [];
|
|
179
|
+
const firstMatchWins = cfg.firstMatchWins !== false;
|
|
180
|
+
const { matched, rule_id, rule_label, output, trace } = evaluateRules(rules, executionMemory, firstMatchWins, pathOpts);
|
|
181
|
+
const finalOutput = matched ? output : (cfg.defaultOutput ?? {});
|
|
182
|
+
const result = {
|
|
183
|
+
matched,
|
|
184
|
+
rule_id,
|
|
185
|
+
rule_label,
|
|
186
|
+
output: finalOutput,
|
|
187
|
+
evaluation_trace: trace,
|
|
188
|
+
};
|
|
189
|
+
if (cfg.sensitivityAnalysis) {
|
|
190
|
+
const basePriority = matched
|
|
191
|
+
? (String(output[cfg.sensitivityAnalysis.priorityField] ?? '') || null)
|
|
192
|
+
: null;
|
|
193
|
+
result.sensitivity = runSensitivity(cfg.sensitivityAnalysis, rules, executionMemory, firstMatchWins, basePriority, pathOpts);
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { resolveDotPath } from './utils.js';
|
|
2
|
+
export { runScopedDataReader, scopedAnswerDocToFlatFields, runScopedDataReaderPack } from './scopedDataReader.js';
|
|
3
|
+
export type { ScopedDataReaderConfig, ScopedDataReaderOutput, ScopedDataReaderPackOutput, ScopedDataReaderPackSlot, } from './scopedDataReader.js';
|
|
4
|
+
export { runDeterministicRule } from './deterministicRule.js';
|
|
5
|
+
export type { DeterministicRuleConfig, DeterministicRuleOutput, Rule, Condition, SensitivityAnalysisConfig, SensitivityResult, } from './deterministicRule.js';
|
|
6
|
+
export { runScopedAnswerWriter } from './scopedAnswerWriter.js';
|
|
7
|
+
export type { ScopedAnswerWriterConfig, ScopedAnswerWriterOutput, } from './scopedAnswerWriter.js';
|
|
8
|
+
export { runScopedAnswerAssembler } from './scopedAnswerAssembler.js';
|
|
9
|
+
export type { ScopedAnswerAssemblerConfig, ScopedAnswerAssemblerOutput, } from './scopedAnswerAssembler.js';
|
|
10
|
+
export declare const LOCAL_SKILL_KEYS: readonly ["scoped-data-reader", "deterministic-rule", "scoped-answer-writer", "scoped-answer-assembler"];
|
|
11
|
+
export type LocalSkillKey = (typeof LOCAL_SKILL_KEYS)[number];
|
|
12
|
+
export declare function isLocalSkillKey(key: string): key is LocalSkillKey;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { resolveDotPath } from './utils.js';
|
|
2
|
+
export { runScopedDataReader, scopedAnswerDocToFlatFields, runScopedDataReaderPack } from './scopedDataReader.js';
|
|
3
|
+
export { runDeterministicRule } from './deterministicRule.js';
|
|
4
|
+
export { runScopedAnswerWriter } from './scopedAnswerWriter.js';
|
|
5
|
+
export { runScopedAnswerAssembler } from './scopedAnswerAssembler.js';
|
|
6
|
+
export const LOCAL_SKILL_KEYS = [
|
|
7
|
+
'scoped-data-reader',
|
|
8
|
+
'deterministic-rule',
|
|
9
|
+
'scoped-answer-writer',
|
|
10
|
+
'scoped-answer-assembler',
|
|
11
|
+
];
|
|
12
|
+
export function isLocalSkillKey(key) {
|
|
13
|
+
return LOCAL_SKILL_KEYS.includes(key);
|
|
14
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FetchMemorixItemResponse } from '@x12i/memorix-retrieval';
|
|
2
|
+
import type { ScopedDataReaderOutput } from './scopedDataReader.js';
|
|
3
|
+
/**
|
|
4
|
+
* Flatten {@link fetchMemorixItem} sections into graph `fields.*` paths.
|
|
5
|
+
* Works across content types — whatever the item descriptor surfaces in sections.
|
|
6
|
+
*/
|
|
7
|
+
export declare function memorixItemToScopedOutput(item: FetchMemorixItemResponse, entityId: string): ScopedDataReaderOutput;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
function isPlainObject(v) {
|
|
2
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
3
|
+
}
|
|
4
|
+
function looksLikeFieldEnvelope(v) {
|
|
5
|
+
return isPlainObject(v) && 'value' in v;
|
|
6
|
+
}
|
|
7
|
+
/** Expand a Memorix content-type object map into flat execution fields. */
|
|
8
|
+
function mergeContentTypeBucket(bucketName, raw, fields, unknowns) {
|
|
9
|
+
for (const [leafKey, leafVal] of Object.entries(raw)) {
|
|
10
|
+
const outKey = `${bucketName}.${leafKey}`;
|
|
11
|
+
if (looksLikeFieldEnvelope(leafVal)) {
|
|
12
|
+
const f = leafVal;
|
|
13
|
+
fields[outKey] = {
|
|
14
|
+
value: f.value,
|
|
15
|
+
fieldPath: typeof f.fieldPath === 'string' ? f.fieldPath : outKey,
|
|
16
|
+
...(f.description != null ? { description: f.description } : {}),
|
|
17
|
+
...(f.reasoning != null ? { reasoning: f.reasoning } : {}),
|
|
18
|
+
...(f.linked != null ? { linked: f.linked } : {}),
|
|
19
|
+
};
|
|
20
|
+
if (f.value == null)
|
|
21
|
+
unknowns.push(outKey);
|
|
22
|
+
}
|
|
23
|
+
else if (leafVal !== undefined) {
|
|
24
|
+
fields[outKey] = { value: leafVal, fieldPath: outKey };
|
|
25
|
+
if (leafVal == null)
|
|
26
|
+
unknowns.push(outKey);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function addScalarOrEnvelope(key, raw, fields, unknowns) {
|
|
31
|
+
if (looksLikeFieldEnvelope(raw)) {
|
|
32
|
+
const f = raw;
|
|
33
|
+
fields[key] = {
|
|
34
|
+
value: f.value,
|
|
35
|
+
fieldPath: typeof f.fieldPath === 'string' ? f.fieldPath : '',
|
|
36
|
+
...(f.description != null ? { description: f.description } : {}),
|
|
37
|
+
...(f.reasoning != null ? { reasoning: f.reasoning } : {}),
|
|
38
|
+
...(f.linked != null ? { linked: f.linked } : {}),
|
|
39
|
+
};
|
|
40
|
+
if (f.value == null)
|
|
41
|
+
unknowns.push(key);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (raw !== undefined) {
|
|
45
|
+
fields[key] = { value: raw, fieldPath: '' };
|
|
46
|
+
if (raw == null)
|
|
47
|
+
unknowns.push(key);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Flatten {@link fetchMemorixItem} sections into graph `fields.*` paths.
|
|
52
|
+
* Works across content types — whatever the item descriptor surfaces in sections.
|
|
53
|
+
*/
|
|
54
|
+
export function memorixItemToScopedOutput(item, entityId) {
|
|
55
|
+
const errors = item.issues?.filter((i) => i.severity === 'error') ?? [];
|
|
56
|
+
if (errors.length > 0) {
|
|
57
|
+
return {
|
|
58
|
+
answer_status: 'error',
|
|
59
|
+
fields: {},
|
|
60
|
+
freshness: null,
|
|
61
|
+
unknowns: [],
|
|
62
|
+
entity_id: entityId,
|
|
63
|
+
error: errors.map((e) => e.message).join('; '),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const fields = {};
|
|
67
|
+
const unknowns = [];
|
|
68
|
+
for (const section of item.sections) {
|
|
69
|
+
for (const [key, raw] of Object.entries(section.fields)) {
|
|
70
|
+
if (isPlainObject(raw) && !looksLikeFieldEnvelope(raw)) {
|
|
71
|
+
const keys = Object.keys(raw);
|
|
72
|
+
const looksLikeBucket = keys.length > 0 &&
|
|
73
|
+
(key === 'inferences' ||
|
|
74
|
+
key === 'decisions' ||
|
|
75
|
+
keys.every((k) => looksLikeFieldEnvelope(raw[k]) || raw[k] === null || typeof raw[k] !== 'object'));
|
|
76
|
+
if (looksLikeBucket) {
|
|
77
|
+
mergeContentTypeBucket(key, raw, fields, unknowns);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
addScalarOrEnvelope(key, raw, fields, unknowns);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const hasData = Object.keys(fields).length > 0;
|
|
85
|
+
if (!hasData) {
|
|
86
|
+
return {
|
|
87
|
+
answer_status: 'not_found',
|
|
88
|
+
fields: {},
|
|
89
|
+
freshness: null,
|
|
90
|
+
unknowns: [],
|
|
91
|
+
entity_id: entityId,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
const flat = item.sections.flatMap((s) => Object.entries(s.fields));
|
|
95
|
+
const scopedAtRaw = flat.find(([k]) => k === 'scopedAt')?.[1] ?? flat.find(([k]) => k === 'modifiedAt')?.[1];
|
|
96
|
+
const scopedAt = typeof scopedAtRaw === 'string' ? scopedAtRaw : undefined;
|
|
97
|
+
return {
|
|
98
|
+
answer_status: 'answered',
|
|
99
|
+
fields,
|
|
100
|
+
freshness: scopedAt ? { scopedAt } : null,
|
|
101
|
+
unknowns,
|
|
102
|
+
entity_id: entityId,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy Memorix retrieval + writer clients for local scoped-data skills.
|
|
3
|
+
*/
|
|
4
|
+
import { type MemorixRetrievalClient } from '@x12i/memorix-retrieval';
|
|
5
|
+
import { type MemorixWriterClient } from '@x12i/memorix-writer';
|
|
6
|
+
export declare function getMemorixRetrievalClient(): Promise<MemorixRetrievalClient>;
|
|
7
|
+
export declare function getMemorixWriterClient(): Promise<MemorixWriterClient>;
|
|
8
|
+
/** Closes Memorix retrieval + writer clients. Call on shutdown. */
|
|
9
|
+
export declare function shutdownMemorixRuntime(): Promise<void>;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lazy Memorix retrieval + writer clients for local scoped-data skills.
|
|
3
|
+
*/
|
|
4
|
+
import { applyMemorixEnvBridgeToProcess, createMemorixRetrievalStackFromEnv, fetchMemorixItem, } from '@x12i/memorix-retrieval';
|
|
5
|
+
import { createMemorixWriterFromEnv, } from '@x12i/memorix-writer';
|
|
6
|
+
function cataloxForWriter(retrievalCatalox, appId) {
|
|
7
|
+
return {
|
|
8
|
+
async getCatalogItem(catalogId, itemId) {
|
|
9
|
+
const result = await retrievalCatalox.getCatalogItem({ appId }, catalogId, itemId);
|
|
10
|
+
if (result.outcome !== 'found')
|
|
11
|
+
return null;
|
|
12
|
+
return { data: result.item.data };
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
let _stack = null;
|
|
17
|
+
let _stackPromise = null;
|
|
18
|
+
let _writer = null;
|
|
19
|
+
let _writerPromise = null;
|
|
20
|
+
async function getOrInitStack() {
|
|
21
|
+
if (_stack)
|
|
22
|
+
return _stack;
|
|
23
|
+
if (_stackPromise)
|
|
24
|
+
return _stackPromise;
|
|
25
|
+
_stackPromise = (async () => {
|
|
26
|
+
applyMemorixEnvBridgeToProcess();
|
|
27
|
+
const stack = await createMemorixRetrievalStackFromEnv();
|
|
28
|
+
_stack = stack;
|
|
29
|
+
_stackPromise = null;
|
|
30
|
+
return stack;
|
|
31
|
+
})();
|
|
32
|
+
return _stackPromise;
|
|
33
|
+
}
|
|
34
|
+
export async function getMemorixRetrievalClient() {
|
|
35
|
+
const stack = await getOrInitStack();
|
|
36
|
+
return stack.client;
|
|
37
|
+
}
|
|
38
|
+
export async function getMemorixWriterClient() {
|
|
39
|
+
if (_writer)
|
|
40
|
+
return _writer;
|
|
41
|
+
if (_writerPromise)
|
|
42
|
+
return _writerPromise;
|
|
43
|
+
_writerPromise = (async () => {
|
|
44
|
+
const stack = await getOrInitStack();
|
|
45
|
+
const retrievalClient = stack.client;
|
|
46
|
+
const writer = await createMemorixWriterFromEnv({
|
|
47
|
+
appId: stack.appId,
|
|
48
|
+
catalox: cataloxForWriter(stack.catalox, stack.appId),
|
|
49
|
+
processEnv: stack.client.processEnv,
|
|
50
|
+
retrieval: {
|
|
51
|
+
fetchMemorixItem: (args) => fetchMemorixItem(retrievalClient, args),
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
_writer = writer;
|
|
55
|
+
_writerPromise = null;
|
|
56
|
+
return writer;
|
|
57
|
+
})();
|
|
58
|
+
return _writerPromise;
|
|
59
|
+
}
|
|
60
|
+
/** Closes Memorix retrieval + writer clients. Call on shutdown. */
|
|
61
|
+
export async function shutdownMemorixRuntime() {
|
|
62
|
+
const writer = _writer;
|
|
63
|
+
const stack = _stack;
|
|
64
|
+
_writer = null;
|
|
65
|
+
_writerPromise = null;
|
|
66
|
+
_stack = null;
|
|
67
|
+
_stackPromise = null;
|
|
68
|
+
await writer?.close?.().catch(() => undefined);
|
|
69
|
+
await stack?.client.close?.().catch(() => undefined);
|
|
70
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memorix descriptor ids for scoped local skills (Catalox-backed).
|
|
3
|
+
*/
|
|
4
|
+
export declare const DEFAULT_SCOPED_CONTENT_TYPES: readonly ["inferences", "decisions"];
|
|
5
|
+
export type ScopedContentType = (typeof DEFAULT_SCOPED_CONTENT_TYPES)[number];
|
|
6
|
+
export type MemorixScopedSkillConfig = {
|
|
7
|
+
memorixItemDescriptorId?: string;
|
|
8
|
+
itemDescriptorId?: string;
|
|
9
|
+
/** Per content-type write descriptor ids (default: content type name, e.g. `inferences`). */
|
|
10
|
+
memorixWriteDescriptors?: Partial<Record<string, string>>;
|
|
11
|
+
/** @deprecated Use {@link memorixWriteDescriptors}. */
|
|
12
|
+
memorixWriteDescriptorId?: string;
|
|
13
|
+
writeDescriptorId?: string;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveMemorixItemDescriptorId(cfg: MemorixScopedSkillConfig): string | undefined;
|
|
16
|
+
export declare function resolveMemorixWriteDescriptorId(cfg: MemorixScopedSkillConfig, contentType: string): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memorix descriptor ids for scoped local skills (Catalox-backed).
|
|
3
|
+
*/
|
|
4
|
+
export const DEFAULT_SCOPED_CONTENT_TYPES = ['inferences', 'decisions'];
|
|
5
|
+
export function resolveMemorixItemDescriptorId(cfg) {
|
|
6
|
+
return cfg.memorixItemDescriptorId?.trim() || cfg.itemDescriptorId?.trim() || undefined;
|
|
7
|
+
}
|
|
8
|
+
export function resolveMemorixWriteDescriptorId(cfg, contentType) {
|
|
9
|
+
const mapped = cfg.memorixWriteDescriptors?.[contentType]?.trim();
|
|
10
|
+
if (mapped)
|
|
11
|
+
return mapped;
|
|
12
|
+
if (contentType === 'inferences' || contentType === 'decisions') {
|
|
13
|
+
const legacy = cfg.memorixWriteDescriptorId?.trim() || cfg.writeDescriptorId?.trim();
|
|
14
|
+
if (legacy && contentType === 'inferences')
|
|
15
|
+
return legacy;
|
|
16
|
+
}
|
|
17
|
+
return contentType;
|
|
18
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scoped-answer-assembler — local skill.
|
|
3
|
+
*
|
|
4
|
+
* Builds the object **`{ inferences, decisions }`** from executionMemory paths
|
|
5
|
+
* before **`scoped-answer-writer`** persists it to Memorix entity content types. Names reflect
|
|
6
|
+
* that we store **learned inferences** and **decisions**, not “verdicts” alone.
|
|
7
|
+
*
|
|
8
|
+
* Metadata:
|
|
9
|
+
* mergeInferencePaths?: string[] — dot-paths to plain objects (shallow-merge keys into inferences)
|
|
10
|
+
* mergeDecisionPaths?: string[] — same for decisions (later paths win on key collision)
|
|
11
|
+
*
|
|
12
|
+
* Skill key: **`scoped-answer-assembler`**.
|
|
13
|
+
*/
|
|
14
|
+
import { type ResolveDotPathOpts } from './utils.js';
|
|
15
|
+
export type ScopedAnswerAssemblerConfig = {
|
|
16
|
+
mergeInferencePaths?: string[];
|
|
17
|
+
mergeDecisionPaths?: string[];
|
|
18
|
+
};
|
|
19
|
+
export type ScopedAnswerAssemblerOutput = {
|
|
20
|
+
inferences: Record<string, unknown>;
|
|
21
|
+
decisions: Record<string, unknown>;
|
|
22
|
+
};
|
|
23
|
+
export declare function runScopedAnswerAssembler(cfg: ScopedAnswerAssemblerConfig, executionMemory: unknown, pathOpts?: ResolveDotPathOpts): ScopedAnswerAssemblerOutput;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scoped-answer-assembler — local skill.
|
|
3
|
+
*
|
|
4
|
+
* Builds the object **`{ inferences, decisions }`** from executionMemory paths
|
|
5
|
+
* before **`scoped-answer-writer`** persists it to Memorix entity content types. Names reflect
|
|
6
|
+
* that we store **learned inferences** and **decisions**, not “verdicts” alone.
|
|
7
|
+
*
|
|
8
|
+
* Metadata:
|
|
9
|
+
* mergeInferencePaths?: string[] — dot-paths to plain objects (shallow-merge keys into inferences)
|
|
10
|
+
* mergeDecisionPaths?: string[] — same for decisions (later paths win on key collision)
|
|
11
|
+
*
|
|
12
|
+
* Skill key: **`scoped-answer-assembler`**.
|
|
13
|
+
*/
|
|
14
|
+
import { resolveDotPath } from './utils.js';
|
|
15
|
+
function isPlainObject(v) {
|
|
16
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
17
|
+
}
|
|
18
|
+
function mergeFromPaths(executionMemory, paths, bucket, pathOpts) {
|
|
19
|
+
for (const p of paths ?? []) {
|
|
20
|
+
const v = resolveDotPath(executionMemory, p, pathOpts);
|
|
21
|
+
if (!isPlainObject(v))
|
|
22
|
+
continue;
|
|
23
|
+
for (const [k, val] of Object.entries(v)) {
|
|
24
|
+
if (val !== undefined)
|
|
25
|
+
bucket[k] = val;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function runScopedAnswerAssembler(cfg, executionMemory, pathOpts) {
|
|
30
|
+
const inferences = {};
|
|
31
|
+
const decisions = {};
|
|
32
|
+
mergeFromPaths(executionMemory, cfg.mergeInferencePaths, inferences, pathOpts);
|
|
33
|
+
mergeFromPaths(executionMemory, cfg.mergeDecisionPaths, decisions, pathOpts);
|
|
34
|
+
return { inferences, decisions };
|
|
35
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type ScopedAnswerField = {
|
|
2
|
+
value: unknown;
|
|
3
|
+
fieldPath: string;
|
|
4
|
+
description?: string;
|
|
5
|
+
reasoning?: string;
|
|
6
|
+
linked?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/** Flatten nested or legacy `answer` documents into graph execution field maps. */
|
|
9
|
+
export declare function scopedAnswerDocToFlatFields(rawAnswer: Record<string, unknown>): {
|
|
10
|
+
fields: Record<string, ScopedAnswerField>;
|
|
11
|
+
unknowns: string[];
|
|
12
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
function isPlainObject(v) {
|
|
2
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
3
|
+
}
|
|
4
|
+
function looksLikeFieldEnvelope(v) {
|
|
5
|
+
return isPlainObject(v) && 'value' in v;
|
|
6
|
+
}
|
|
7
|
+
function isNestedAnswerShape(raw) {
|
|
8
|
+
const inf = raw.inferences;
|
|
9
|
+
const dec = raw.decisions;
|
|
10
|
+
if (inf !== undefined && isPlainObject(inf))
|
|
11
|
+
return true;
|
|
12
|
+
if (dec !== undefined && isPlainObject(dec))
|
|
13
|
+
return true;
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
function mergeBucketIntoFields(fields, unknowns, bucket, bucketObj) {
|
|
17
|
+
const disambig = new Set(Object.keys(fields));
|
|
18
|
+
for (const [leafKey, field] of Object.entries(bucketObj)) {
|
|
19
|
+
if (!looksLikeFieldEnvelope(field))
|
|
20
|
+
continue;
|
|
21
|
+
const f = field;
|
|
22
|
+
let outKey = leafKey;
|
|
23
|
+
if (disambig.has(leafKey)) {
|
|
24
|
+
outKey = `${bucket}_${leafKey}`;
|
|
25
|
+
}
|
|
26
|
+
disambig.add(outKey);
|
|
27
|
+
fields[outKey] = {
|
|
28
|
+
value: f.value,
|
|
29
|
+
fieldPath: typeof f.fieldPath === 'string' ? f.fieldPath : '',
|
|
30
|
+
...(f.description != null ? { description: f.description } : {}),
|
|
31
|
+
...(f.reasoning != null ? { reasoning: f.reasoning } : {}),
|
|
32
|
+
...(f.linked != null ? { linked: f.linked } : {}),
|
|
33
|
+
};
|
|
34
|
+
if (f.value == null) {
|
|
35
|
+
unknowns.push(outKey);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Flatten nested or legacy `answer` documents into graph execution field maps. */
|
|
40
|
+
export function scopedAnswerDocToFlatFields(rawAnswer) {
|
|
41
|
+
const fields = {};
|
|
42
|
+
const unknowns = [];
|
|
43
|
+
if (isNestedAnswerShape(rawAnswer)) {
|
|
44
|
+
const inf = isPlainObject(rawAnswer.inferences) ? rawAnswer.inferences : {};
|
|
45
|
+
const dec = isPlainObject(rawAnswer.decisions) ? rawAnswer.decisions : {};
|
|
46
|
+
mergeBucketIntoFields(fields, unknowns, 'decisions', dec);
|
|
47
|
+
mergeBucketIntoFields(fields, unknowns, 'inferences', inf);
|
|
48
|
+
return { fields, unknowns };
|
|
49
|
+
}
|
|
50
|
+
for (const [key, field] of Object.entries(rawAnswer)) {
|
|
51
|
+
if (!looksLikeFieldEnvelope(field))
|
|
52
|
+
continue;
|
|
53
|
+
const f = field;
|
|
54
|
+
fields[key] = {
|
|
55
|
+
value: f.value,
|
|
56
|
+
fieldPath: typeof f.fieldPath === 'string' ? f.fieldPath : '',
|
|
57
|
+
...(f.description != null ? { description: f.description } : {}),
|
|
58
|
+
...(f.reasoning != null ? { reasoning: f.reasoning } : {}),
|
|
59
|
+
...(f.linked != null ? { linked: f.linked } : {}),
|
|
60
|
+
};
|
|
61
|
+
if (f.value == null) {
|
|
62
|
+
unknowns.push(key);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return { fields, unknowns };
|
|
66
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scoped-answer-writer — local skill.
|
|
3
|
+
*
|
|
4
|
+
* Persists execution-memory payloads into Memorix **entity** collections, one
|
|
5
|
+
* **content type** per write (`inferences`, `decisions`, …) via `writeMemorixRecord`.
|
|
6
|
+
*
|
|
7
|
+
* Node config:
|
|
8
|
+
* entityIdPath — required
|
|
9
|
+
* payloadPath — dot-path to `{ inferences?, decisions? }` or flat decisions map
|
|
10
|
+
* memorixWriteDescriptors — optional map of contentType → Catalox write descriptor id
|
|
11
|
+
* (default: descriptor id equals content type name)
|
|
12
|
+
*/
|
|
13
|
+
import { type MemorixScopedSkillConfig } from './memorixScopedConfig.js';
|
|
14
|
+
import { type ResolveDotPathOpts } from './utils.js';
|
|
15
|
+
export type ScopedAnswerWriterConfig = MemorixScopedSkillConfig & {
|
|
16
|
+
entityIdPath: string;
|
|
17
|
+
entityTypePath?: string;
|
|
18
|
+
staticEntityType?: string;
|
|
19
|
+
payloadPath?: string;
|
|
20
|
+
questionTitle?: string;
|
|
21
|
+
phase?: 'A' | 'B';
|
|
22
|
+
/** Content types to persist when present in payload (default: inferences, decisions). */
|
|
23
|
+
memorixContentTypes?: string[];
|
|
24
|
+
};
|
|
25
|
+
export type ScopedAnswerWriterOutput = {
|
|
26
|
+
write_status: 'ok' | 'error';
|
|
27
|
+
entity_id: string;
|
|
28
|
+
content_types_written: string[];
|
|
29
|
+
written_keys: string[];
|
|
30
|
+
error?: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function runScopedAnswerWriter(cfg: ScopedAnswerWriterConfig, executionMemory: unknown, pathOpts?: ResolveDotPathOpts): Promise<ScopedAnswerWriterOutput>;
|