@toolproof-core/lib 1.0.36 → 1.0.37
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/integrations/firebase/firebaseAdminHelpers.d.ts +1 -2
- package/dist/integrations/firebase/firebaseAdminHelpers.js +5 -14
- package/dist/lookups/lookups.d.ts +17 -24
- package/dist/lookups/lookups.js +10 -0
- package/dist/types/types.d.ts +10 -6
- package/dist/utils/cosmosDataExtraction.d.ts +5 -0
- package/dist/utils/cosmosDataExtraction.js +15 -0
- package/dist/utils/identifierGeneration.d.ts +2 -0
- package/dist/utils/identifierGeneration.js +5 -0
- package/dist/utils/mutableStrategyOverlay.d.ts +55 -0
- package/dist/utils/mutableStrategyOverlay.js +115 -0
- package/dist/utils/resourceCreation.d.ts +11 -0
- package/dist/utils/resourceCreation.js +32 -0
- package/dist/utils/stepCreation.d.ts +9 -0
- package/dist/utils/stepCreation.js +79 -0
- package/dist/utils/stepParallelization.d.ts +2 -0
- package/dist/utils/stepParallelization.js +129 -0
- package/dist/utils/strategyAssembly.d.ts +3 -0
- package/dist/utils/strategyAssembly.js +6 -0
- package/dist/utils/strategyCanonicalization.d.ts +4 -0
- package/dist/utils/strategyCanonicalization.js +215 -0
- package/dist/utils/strategyExtraction.d.ts +17 -0
- package/dist/utils/strategyExtraction.js +88 -0
- package/dist/utils/strategyStateResolution.d.ts +17 -0
- package/dist/utils/{resolveStrategyStateChain.js → strategyStateResolution.js} +5 -4
- package/dist/utils/strategyThreading.d.ts +2 -0
- package/dist/utils/strategyThreading.js +10 -0
- package/dist/utils/toolStepPaths.d.ts +2 -0
- package/dist/utils/toolStepPaths.js +21 -0
- package/package.json +41 -21
- package/src/integrations/firebase/firebaseAdminHelpers.ts +5 -17
- package/src/lookups/lookups.ts +16 -6
- package/src/types/types.ts +10 -4
- package/src/utils/cosmosDataExtraction.ts +25 -0
- package/src/utils/identifierGeneration.ts +12 -0
- package/src/utils/mutableStrategyOverlay.ts +286 -0
- package/src/utils/resourceCreation.ts +87 -0
- package/src/utils/stepCreation.ts +114 -0
- package/src/utils/stepParallelization.ts +181 -0
- package/src/utils/strategyAssembly.ts +14 -0
- package/src/utils/strategyCanonicalization.ts +294 -0
- package/src/utils/strategyExtraction.ts +150 -0
- package/src/utils/strategyStateResolution.ts +57 -0
- package/src/utils/strategyThreading.ts +27 -0
- package/src/utils/toolStepPaths.ts +34 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/integrations/firebase/createStep.d.ts +0 -10
- package/dist/integrations/firebase/createStep.js +0 -17
- package/dist/integrations/firebase/createThreadedStrategy.d.ts +0 -2
- package/dist/integrations/firebase/createThreadedStrategy.js +0 -8
- package/dist/utils/creation/resourceCreation.d.ts +0 -21
- package/dist/utils/creation/resourceCreation.js +0 -67
- package/dist/utils/creation/stepCreation.d.ts +0 -10
- package/dist/utils/creation/stepCreation.js +0 -64
- package/dist/utils/creation/threadedStrategyCreation.d.ts +0 -18
- package/dist/utils/creation/threadedStrategyCreation.js +0 -16
- package/dist/utils/extractData.d.ts +0 -12
- package/dist/utils/extractData.js +0 -78
- package/dist/utils/parallelizeSteps.d.ts +0 -3
- package/dist/utils/parallelizeSteps.js +0 -159
- package/dist/utils/resolveStrategyStateChain.d.ts +0 -20
- package/dist/utils/roleSpec.d.ts +0 -16
- package/dist/utils/roleSpec.js +0 -57
- package/dist/utils/strategyState.d.ts +0 -12
- package/dist/utils/strategyState.js +0 -58
- package/dist/utils_2/threadedStrategyCreation.d.ts +0 -5
- package/dist/utils_2/threadedStrategyCreation.js +0 -12
- package/src/integrations/firebase/createStep.ts +0 -41
- package/src/integrations/firebase/createThreadedStrategy.ts +0 -19
- package/src/utils/creation/resourceCreation.ts +0 -140
- package/src/utils/creation/stepCreation.ts +0 -95
- package/src/utils/creation/threadedStrategyCreation.ts +0 -42
- package/src/utils/extractData.ts +0 -137
- package/src/utils/parallelizeSteps.ts +0 -239
- package/src/utils/resolveStrategyStateChain.ts +0 -58
- package/src/utils/roleSpec.ts +0 -84
- package/src/utils/strategyState.ts +0 -128
- package/src/utils_2/threadedStrategyCreation.ts +0 -19
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MutableBranchStep,
|
|
3
|
+
MutableForStep,
|
|
4
|
+
MutableStep,
|
|
5
|
+
MutableStrategyState,
|
|
6
|
+
MutableToolStep,
|
|
7
|
+
MutableToolStepKey,
|
|
8
|
+
MutableWhileStep,
|
|
9
|
+
} from './mutableStrategyOverlay.js';
|
|
10
|
+
import { CONSTANTS } from '../lookups/lookups.js';
|
|
11
|
+
|
|
12
|
+
export function getIndependentThreads<
|
|
13
|
+
TKey extends string = MutableToolStepKey,
|
|
14
|
+
TStep extends MutableStep<TKey> = MutableStep<TKey>,
|
|
15
|
+
>(
|
|
16
|
+
steps: TStep[],
|
|
17
|
+
strategyState: MutableStrategyState<TKey>,
|
|
18
|
+
): TStep[][] {
|
|
19
|
+
type OwnerIndex = number;
|
|
20
|
+
|
|
21
|
+
const getOwnerLabel = (ownerIndex: OwnerIndex) => {
|
|
22
|
+
const step = steps[ownerIndex];
|
|
23
|
+
return `steps[${ownerIndex}] stepKind=${step?.stepKind ?? 'unknown'}`;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const toolStepKeyToOwner = new Map<TKey, OwnerIndex>();
|
|
27
|
+
const toolStepByKey = new Map<TKey, MutableToolStep<TKey>>();
|
|
28
|
+
|
|
29
|
+
const addToolStep = (
|
|
30
|
+
toolStep: MutableToolStep<TKey> | undefined,
|
|
31
|
+
ownerIndex: OwnerIndex,
|
|
32
|
+
) => {
|
|
33
|
+
if (!toolStep) return;
|
|
34
|
+
|
|
35
|
+
const existingOwner = toolStepKeyToOwner.get(toolStep.toolStepKey);
|
|
36
|
+
if (existingOwner !== undefined) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Duplicate toolStepKey '${toolStep.toolStepKey}' found in ${getOwnerLabel(ownerIndex)} and ${getOwnerLabel(existingOwner)}`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
toolStepKeyToOwner.set(toolStep.toolStepKey, ownerIndex);
|
|
43
|
+
toolStepByKey.set(toolStep.toolStepKey, toolStep);
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
steps.forEach((step, ownerIndex) => {
|
|
47
|
+
if (step.stepKind === CONSTANTS.Enums.StepKind.tool) {
|
|
48
|
+
addToolStep(step as MutableToolStep<TKey>, ownerIndex);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (step.stepKind === CONSTANTS.Enums.StepKind.for) {
|
|
53
|
+
const loop = step as MutableForStep<TKey>;
|
|
54
|
+
addToolStep(loop.case?.what, ownerIndex);
|
|
55
|
+
addToolStep(loop.case?.when, ownerIndex);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (step.stepKind === CONSTANTS.Enums.StepKind.while) {
|
|
60
|
+
const loop = step as MutableWhileStep<TKey>;
|
|
61
|
+
addToolStep(loop.case?.what, ownerIndex);
|
|
62
|
+
addToolStep(loop.case?.when, ownerIndex);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (step.stepKind === CONSTANTS.Enums.StepKind.branch) {
|
|
67
|
+
const branch = step as MutableBranchStep<TKey>;
|
|
68
|
+
for (const stepCase of branch.cases) {
|
|
69
|
+
addToolStep(stepCase?.what, ownerIndex);
|
|
70
|
+
addToolStep(stepCase?.when, ownerIndex);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const ownerAdj = new Map<OwnerIndex, Set<OwnerIndex>>();
|
|
76
|
+
const ownerDeps = new Map<OwnerIndex, Set<OwnerIndex>>();
|
|
77
|
+
|
|
78
|
+
const ensureOwner = (ownerIndex: OwnerIndex) => {
|
|
79
|
+
if (!ownerAdj.has(ownerIndex)) ownerAdj.set(ownerIndex, new Set());
|
|
80
|
+
if (!ownerDeps.has(ownerIndex)) ownerDeps.set(ownerIndex, new Set());
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
for (let index = 0; index < steps.length; index++) {
|
|
84
|
+
ensureOwner(index);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
for (const [toolStepKey, ownerIndex] of toolStepKeyToOwner) {
|
|
88
|
+
ensureOwner(ownerIndex);
|
|
89
|
+
|
|
90
|
+
const toolStep = toolStepByKey.get(toolStepKey);
|
|
91
|
+
const inputBindings = toolStep?.roleBindingSpec?.inputBindings ?? [];
|
|
92
|
+
const bucket = strategyState[toolStepKey];
|
|
93
|
+
|
|
94
|
+
for (const inputRoleId of inputBindings) {
|
|
95
|
+
const entry = bucket?.[inputRoleId];
|
|
96
|
+
if (!entry || entry.strategyStateInputKind !== 'internalInputPotential') continue;
|
|
97
|
+
|
|
98
|
+
const producerToolStepKey = entry.toolStepRoleAddress?.toolStepKey;
|
|
99
|
+
if (typeof producerToolStepKey !== 'string') {
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Unresolvable internalInputPotential in toolStep '${toolStepKey}' (${getOwnerLabel(ownerIndex)}): missing toolStepRoleAddress.toolStepKey for role '${inputRoleId}'`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const producerOwner = toolStepKeyToOwner.get(producerToolStepKey as TKey);
|
|
106
|
+
if (producerOwner === undefined) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Unresolvable internalInputPotential in toolStep '${toolStepKey}' (${getOwnerLabel(ownerIndex)}): source toolStepKey '${producerToolStepKey}' not found in strategy steps`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
ensureOwner(producerOwner);
|
|
113
|
+
ownerAdj.get(ownerIndex)?.add(producerOwner);
|
|
114
|
+
ownerAdj.get(producerOwner)?.add(ownerIndex);
|
|
115
|
+
ownerDeps.get(ownerIndex)?.add(producerOwner);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const visited = new Set<OwnerIndex>();
|
|
120
|
+
const components: OwnerIndex[][] = [];
|
|
121
|
+
|
|
122
|
+
for (let ownerIndex = 0; ownerIndex < steps.length; ownerIndex++) {
|
|
123
|
+
if (visited.has(ownerIndex)) continue;
|
|
124
|
+
|
|
125
|
+
const component: OwnerIndex[] = [];
|
|
126
|
+
const queue: OwnerIndex[] = [ownerIndex];
|
|
127
|
+
visited.add(ownerIndex);
|
|
128
|
+
|
|
129
|
+
while (queue.length > 0) {
|
|
130
|
+
const node = queue.shift();
|
|
131
|
+
if (node === undefined) break;
|
|
132
|
+
component.push(node);
|
|
133
|
+
|
|
134
|
+
for (const neighbor of ownerAdj.get(node) ?? []) {
|
|
135
|
+
if (visited.has(neighbor)) continue;
|
|
136
|
+
visited.add(neighbor);
|
|
137
|
+
queue.push(neighbor);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
components.push(component);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (const component of components) {
|
|
145
|
+
const componentOwners = new Set(component);
|
|
146
|
+
const ownersInOrder = steps.map((_, index) => index).filter((index) => componentOwners.has(index));
|
|
147
|
+
const position = new Map<OwnerIndex, number>();
|
|
148
|
+
|
|
149
|
+
ownersInOrder.forEach((owner, index) => position.set(owner, index));
|
|
150
|
+
|
|
151
|
+
for (const consumerOwner of ownersInOrder) {
|
|
152
|
+
const deps = ownerDeps.get(consumerOwner);
|
|
153
|
+
if (!deps || deps.size === 0) continue;
|
|
154
|
+
|
|
155
|
+
const consumerPos = position.get(consumerOwner);
|
|
156
|
+
if (consumerPos === undefined) continue;
|
|
157
|
+
|
|
158
|
+
for (const producerOwner of deps) {
|
|
159
|
+
const producerPos = position.get(producerOwner);
|
|
160
|
+
if (producerPos === undefined) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Internal error: dependency producer ${getOwnerLabel(producerOwner)} missing from computed thread for ${getOwnerLabel(consumerOwner)}`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (producerPos >= consumerPos) {
|
|
167
|
+
throw new Error(
|
|
168
|
+
`Invalid step order in thread: ${getOwnerLabel(consumerOwner)} depends on ${getOwnerLabel(producerOwner)}, but producer is not scheduled before consumer`
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return components
|
|
176
|
+
.map((component) => {
|
|
177
|
+
const ownerSet = new Set(component);
|
|
178
|
+
return steps.filter((_step, index) => ownerSet.has(index));
|
|
179
|
+
})
|
|
180
|
+
.filter((group) => group.length > 0);
|
|
181
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Strategy } from '@toolproof-core/genesis';
|
|
2
|
+
import type { MutableStrategy } from './mutableStrategyOverlay.js';
|
|
3
|
+
import { toExecutionMutableStrategy } from './strategyThreading.js';
|
|
4
|
+
import { mutableStrategyToStrategy } from './strategyCanonicalization.js';
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function composeStrategyForExecution<TKey extends string>(
|
|
8
|
+
mutableStrategy: MutableStrategy<TKey>,
|
|
9
|
+
): Strategy {
|
|
10
|
+
const executionStrategy =
|
|
11
|
+
toExecutionMutableStrategy(mutableStrategy);
|
|
12
|
+
|
|
13
|
+
return mutableStrategyToStrategy(executionStrategy);
|
|
14
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BranchStep,
|
|
3
|
+
Case,
|
|
4
|
+
ExternalInputPotential,
|
|
5
|
+
ForStep,
|
|
6
|
+
InputResource,
|
|
7
|
+
InternalInputPotential,
|
|
8
|
+
RoleName,
|
|
9
|
+
Step,
|
|
10
|
+
Strategy,
|
|
11
|
+
StrategyStateInputEntry,
|
|
12
|
+
ToolStep,
|
|
13
|
+
ToolStepPath,
|
|
14
|
+
WhileStep,
|
|
15
|
+
} from '@toolproof-core/genesis';
|
|
16
|
+
import { CONSTANTS } from '../lookups/lookups.js';
|
|
17
|
+
import type {
|
|
18
|
+
MutableBranchStep,
|
|
19
|
+
MutableCase,
|
|
20
|
+
MutableForStep,
|
|
21
|
+
MutableInternalInputPotential,
|
|
22
|
+
MutableStrategy,
|
|
23
|
+
MutableStep,
|
|
24
|
+
MutableStrategyStateInputEntry,
|
|
25
|
+
MutableToolStepKey,
|
|
26
|
+
MutableWhileStep,
|
|
27
|
+
} from './mutableStrategyOverlay.js';
|
|
28
|
+
import { generateIdentifier } from './identifierGeneration.js';
|
|
29
|
+
|
|
30
|
+
function stripMutableCase(stepCase: MutableCase): Case {
|
|
31
|
+
const { when, what, ...rest } = stepCase;
|
|
32
|
+
const { toolStepKey: _whenToolStepKey, ...canonicalWhen } = when;
|
|
33
|
+
const { toolStepKey: _whatToolStepKey, ...canonicalWhat } = what;
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
...rest,
|
|
37
|
+
when: canonicalWhen,
|
|
38
|
+
what: canonicalWhat,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function buildExecutionPathMap(strategy: MutableStrategy): Map<MutableToolStepKey, ToolStepPath> {
|
|
43
|
+
const executionPathByKey = new Map<MutableToolStepKey, ToolStepPath>();
|
|
44
|
+
|
|
45
|
+
const registerStep = (step: MutableStep, pathPrefix: string) => {
|
|
46
|
+
switch (step.stepKind) {
|
|
47
|
+
case CONSTANTS.Enums.StepKind.tool:
|
|
48
|
+
executionPathByKey.set(step.toolStepKey, `${pathPrefix}/self` as ToolStepPath);
|
|
49
|
+
return;
|
|
50
|
+
case CONSTANTS.Enums.StepKind.branch:
|
|
51
|
+
step.cases.forEach((stepCase, caseIndex) => {
|
|
52
|
+
executionPathByKey.set(stepCase.when.toolStepKey, `${pathPrefix}/cases/${caseIndex}/when` as ToolStepPath);
|
|
53
|
+
executionPathByKey.set(stepCase.what.toolStepKey, `${pathPrefix}/cases/${caseIndex}/what` as ToolStepPath);
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
case CONSTANTS.Enums.StepKind.while:
|
|
57
|
+
case CONSTANTS.Enums.StepKind.for:
|
|
58
|
+
executionPathByKey.set(step.case.when.toolStepKey, `${pathPrefix}/case/when` as ToolStepPath);
|
|
59
|
+
executionPathByKey.set(step.case.what.toolStepKey, `${pathPrefix}/case/what` as ToolStepPath);
|
|
60
|
+
return;
|
|
61
|
+
default:
|
|
62
|
+
throw new Error('Unsupported step kind while projecting strategy.');
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
strategy.stepsByThreadIndex.forEach((steps, threadIndex) => {
|
|
67
|
+
steps.forEach((step, stepIndex) => {
|
|
68
|
+
registerStep(step, `/threads/${threadIndex}/steps/${stepIndex}`);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return executionPathByKey;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function toCanonicalStrategyStateEntry(
|
|
76
|
+
entry: MutableStrategyStateInputEntry,
|
|
77
|
+
executionPathByKey: ReadonlyMap<MutableToolStepKey, ToolStepPath>,
|
|
78
|
+
): StrategyStateInputEntry {
|
|
79
|
+
if (entry.strategyStateInputKind !== 'internalInputPotential') {
|
|
80
|
+
return entry as InputResource | ExternalInputPotential;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const sourceToolStepPath = executionPathByKey.get(entry.toolStepRoleAddress.toolStepKey);
|
|
84
|
+
if (!sourceToolStepPath) {
|
|
85
|
+
throw new Error(`Cannot project strategy state: no execution path found for toolStepKey '${entry.toolStepRoleAddress.toolStepKey}'.`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
strategyStateInputKind: 'internalInputPotential',
|
|
90
|
+
toolStepRoleAddress: {
|
|
91
|
+
toolStepPath: sourceToolStepPath,
|
|
92
|
+
roleName: entry.toolStepRoleAddress.roleName,
|
|
93
|
+
},
|
|
94
|
+
} satisfies InternalInputPotential;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function toCanonicalStep(step: MutableStep): Step {
|
|
98
|
+
switch (step.stepKind) {
|
|
99
|
+
case CONSTANTS.Enums.StepKind.tool: {
|
|
100
|
+
const { toolStepKey: _toolStepKey, ...canonicalToolStep } = step;
|
|
101
|
+
return canonicalToolStep;
|
|
102
|
+
}
|
|
103
|
+
case CONSTANTS.Enums.StepKind.branch: {
|
|
104
|
+
const { macroStepKey: _macroStepKey, cases, ...rest } = step;
|
|
105
|
+
return {
|
|
106
|
+
...rest,
|
|
107
|
+
cases: cases.map(stripMutableCase) as [Case, ...Case[]],
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
case CONSTANTS.Enums.StepKind.while:
|
|
111
|
+
case CONSTANTS.Enums.StepKind.for: {
|
|
112
|
+
const { macroStepKey: _macroStepKey, case: stepCase, ...rest } = step;
|
|
113
|
+
return {
|
|
114
|
+
...rest,
|
|
115
|
+
case: stripMutableCase(stepCase),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
default:
|
|
119
|
+
throw new Error('Unsupported step kind while projecting steps.');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function toMutableStrategyStateEntry(
|
|
124
|
+
entry: StrategyStateInputEntry,
|
|
125
|
+
executionKeyByPath: ReadonlyMap<ToolStepPath, MutableToolStepKey>,
|
|
126
|
+
): MutableStrategyStateInputEntry {
|
|
127
|
+
if (entry.strategyStateInputKind !== 'internalInputPotential') {
|
|
128
|
+
return entry as InputResource | ExternalInputPotential;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const sourceToolStepKey = executionKeyByPath.get(entry.toolStepRoleAddress.toolStepPath);
|
|
132
|
+
if (!sourceToolStepKey) {
|
|
133
|
+
throw new Error(`Cannot deproject strategy state: no mutable key found for toolStepPath '${entry.toolStepRoleAddress.toolStepPath}'.`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
strategyStateInputKind: 'internalInputPotential',
|
|
138
|
+
toolStepRoleAddress: {
|
|
139
|
+
toolStepKey: sourceToolStepKey,
|
|
140
|
+
roleName: entry.toolStepRoleAddress.roleName,
|
|
141
|
+
},
|
|
142
|
+
} satisfies MutableInternalInputPotential;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function toMutableToolStep(step: ToolStep) {
|
|
146
|
+
return {
|
|
147
|
+
...step,
|
|
148
|
+
toolStepKey: generateIdentifier('ToolStepKey'),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function toMutableCase(stepCase: Case): MutableCase {
|
|
153
|
+
const { when, what, ...rest } = stepCase;
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
...rest,
|
|
157
|
+
when: toMutableToolStep(when),
|
|
158
|
+
what: toMutableToolStep(what),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function toMutableBranchStep(step: BranchStep): MutableBranchStep {
|
|
163
|
+
const { cases, ...rest } = step;
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
...rest,
|
|
167
|
+
macroStepKey: generateIdentifier('BranchStepKey'),
|
|
168
|
+
cases: cases.map(toMutableCase) as [MutableCase, ...MutableCase[]],
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function toMutableWhileStep(step: WhileStep): MutableWhileStep {
|
|
173
|
+
const { case: stepCase, ...rest } = step;
|
|
174
|
+
|
|
175
|
+
return {
|
|
176
|
+
...rest,
|
|
177
|
+
macroStepKey: generateIdentifier('WhileStepKey'),
|
|
178
|
+
case: toMutableCase(stepCase),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function toMutableForStep(step: ForStep): MutableForStep {
|
|
183
|
+
const { case: stepCase, ...rest } = step;
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...rest,
|
|
187
|
+
macroStepKey: generateIdentifier('ForStepKey'),
|
|
188
|
+
case: toMutableCase(stepCase),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function toMutableStep(step: Step): MutableStep {
|
|
193
|
+
switch (step.stepKind) {
|
|
194
|
+
case CONSTANTS.Enums.StepKind.tool:
|
|
195
|
+
return toMutableToolStep(step);
|
|
196
|
+
case CONSTANTS.Enums.StepKind.branch:
|
|
197
|
+
return toMutableBranchStep(step);
|
|
198
|
+
case CONSTANTS.Enums.StepKind.while:
|
|
199
|
+
return toMutableWhileStep(step);
|
|
200
|
+
case CONSTANTS.Enums.StepKind.for:
|
|
201
|
+
return toMutableForStep(step);
|
|
202
|
+
default:
|
|
203
|
+
throw new Error('Unsupported step kind while deprojecting steps.');
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function buildExecutionKeyMap(strategy: MutableStrategy): Map<ToolStepPath, MutableToolStepKey> {
|
|
208
|
+
const executionPathByKey = buildExecutionPathMap(strategy);
|
|
209
|
+
const executionKeyByPath = new Map<ToolStepPath, MutableToolStepKey>();
|
|
210
|
+
|
|
211
|
+
for (const [toolStepKey, toolStepPath] of executionPathByKey.entries()) {
|
|
212
|
+
executionKeyByPath.set(toolStepPath, toolStepKey);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return executionKeyByPath;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export function strategyToMutableStrategy(strategy: Strategy): MutableStrategy {
|
|
219
|
+
const stepsByThreadIndex = strategy.stepsByThreadIndex.map((steps) => steps.map(toMutableStep));
|
|
220
|
+
|
|
221
|
+
const seededMutableStrategy: MutableStrategy = {
|
|
222
|
+
...strategy,
|
|
223
|
+
stepsByThreadIndex,
|
|
224
|
+
strategyState: {},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const executionKeyByPath = buildExecutionKeyMap(seededMutableStrategy);
|
|
228
|
+
const strategyState: MutableStrategy['strategyState'] = {};
|
|
229
|
+
|
|
230
|
+
for (const [toolStepPath, inputEntryByRoleName] of Object.entries(strategy.strategyState)) {
|
|
231
|
+
const toolStepKey = executionKeyByPath.get(toolStepPath as ToolStepPath);
|
|
232
|
+
if (!toolStepKey) {
|
|
233
|
+
throw new Error(`Cannot deproject strategy state: no mutable key found for toolStepPath '${toolStepPath}'.`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
strategyState[toolStepKey] = Object.fromEntries(
|
|
237
|
+
Object.entries(inputEntryByRoleName).map(([roleName, entry]) => {
|
|
238
|
+
if (entry === undefined) {
|
|
239
|
+
throw new Error(
|
|
240
|
+
`Cannot deproject strategy state: role '${roleName}' on toolStepPath '${toolStepPath}' is explicitly undefined.`
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return [
|
|
245
|
+
roleName as RoleName,
|
|
246
|
+
toMutableStrategyStateEntry(entry, executionKeyByPath),
|
|
247
|
+
] as const;
|
|
248
|
+
})
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
...strategy,
|
|
254
|
+
stepsByThreadIndex,
|
|
255
|
+
strategyState,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export function mutableStrategyToStrategy(mutableStrategy: MutableStrategy): Strategy {
|
|
260
|
+
const executionPathByKey = buildExecutionPathMap(mutableStrategy);
|
|
261
|
+
const strategyState: Strategy['strategyState'] = {};
|
|
262
|
+
|
|
263
|
+
for (const [toolStepKey, inputEntryByRoleName] of Object.entries(mutableStrategy.strategyState)) {
|
|
264
|
+
if (!inputEntryByRoleName) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const toolStepPath = executionPathByKey.get(toolStepKey);
|
|
269
|
+
if (!toolStepPath) {
|
|
270
|
+
throw new Error(`Cannot project strategy state: no execution path found for toolStepKey '${toolStepKey}'.`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
strategyState[toolStepPath] = Object.fromEntries(
|
|
274
|
+
Object.entries(inputEntryByRoleName).map(([roleName, entry]) => {
|
|
275
|
+
if (entry === undefined) {
|
|
276
|
+
throw new Error(
|
|
277
|
+
`Cannot project strategy state: role '${roleName}' on toolStepKey '${toolStepKey}' is explicitly undefined.`
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return [
|
|
282
|
+
roleName,
|
|
283
|
+
toCanonicalStrategyStateEntry(entry, executionPathByKey),
|
|
284
|
+
] as const;
|
|
285
|
+
})
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return {
|
|
290
|
+
...mutableStrategy,
|
|
291
|
+
stepsByThreadIndex: mutableStrategy.stepsByThreadIndex.map((steps) => steps.map(toCanonicalStep)),
|
|
292
|
+
strategyState,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
RoleName,
|
|
3
|
+
RoleSpec,
|
|
4
|
+
ToolHandle,
|
|
5
|
+
Tool,
|
|
6
|
+
ResourceTypeHandle,
|
|
7
|
+
RoleValue,
|
|
8
|
+
} from '@toolproof-core/genesis';
|
|
9
|
+
import type {
|
|
10
|
+
MutableStrategy,
|
|
11
|
+
MutableStep,
|
|
12
|
+
MutableToolStep
|
|
13
|
+
} from './mutableStrategyOverlay.js';
|
|
14
|
+
import { CONSTANTS } from '../lookups/lookups.js';
|
|
15
|
+
|
|
16
|
+
export type RoleSpecEntry = [RoleName, RoleValue];
|
|
17
|
+
|
|
18
|
+
type RoleSpecDirectionKeys = keyof Pick<
|
|
19
|
+
RoleSpec,
|
|
20
|
+
'inputRoleValueByName' | 'outputRoleValueByName'
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
export type RoleSpecByDirection = {
|
|
24
|
+
[K in RoleSpecDirectionKeys]: Map<RoleName, RoleValue>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function getInputRoleSpecEntriesFromTool(tool: Tool): RoleSpecEntry[] {
|
|
28
|
+
return Object.entries(tool.roleSpec.inputRoleValueByName) as RoleSpecEntry[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getOutputRoleSpecEntriesFromTool(tool: Tool): RoleSpecEntry[] {
|
|
32
|
+
return Object.entries(tool.roleSpec.outputRoleValueByName) as RoleSpecEntry[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function getInputRoleNamesFromTool(tool: Tool): RoleName[] {
|
|
36
|
+
return getInputRoleSpecEntriesFromTool(tool).map(([roleName]) => roleName);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getOutputRoleNamesFromTool(tool: Tool): RoleName[] {
|
|
40
|
+
return getOutputRoleSpecEntriesFromTool(tool).map(([roleName]) => roleName);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function extractResourceTypeHandleFromRoleValue(
|
|
44
|
+
roleValue: RoleValue,
|
|
45
|
+
): ResourceTypeHandle | null {
|
|
46
|
+
const typeRef = roleValue.typeRef;
|
|
47
|
+
|
|
48
|
+
if ('resourceTypeHandle' in typeRef) {
|
|
49
|
+
return typeRef.resourceTypeHandle;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function extractToolStepsFromStrategy(strategy: MutableStrategy): MutableToolStep[] {
|
|
56
|
+
const toolSteps: MutableToolStep[] = [];
|
|
57
|
+
|
|
58
|
+
const visitStep = (step: MutableStep) => {
|
|
59
|
+
switch (step.stepKind) {
|
|
60
|
+
case 'tool': {
|
|
61
|
+
toolSteps.push(step);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
case 'branch': {
|
|
65
|
+
for (const conditional of step.cases) {
|
|
66
|
+
toolSteps.push(conditional.when, conditional.what);
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
case 'while':
|
|
71
|
+
case 'for': {
|
|
72
|
+
toolSteps.push(step.case.when, step.case.what);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
default: {
|
|
76
|
+
const _exhaustive: never = step;
|
|
77
|
+
return _exhaustive;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
for (const threadSteps of strategy.stepsByThreadIndex) {
|
|
83
|
+
for (const step of threadSteps) {
|
|
84
|
+
visitStep(step);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return toolSteps;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function extractToolsFromStrategy(
|
|
92
|
+
strategy: MutableStrategy,
|
|
93
|
+
toolMap: Map<ToolHandle, Tool>,
|
|
94
|
+
): Tool[] {
|
|
95
|
+
const toolSteps = extractToolStepsFromStrategy(strategy);
|
|
96
|
+
const toolHandles = new Set<ToolHandle>();
|
|
97
|
+
const tools: Tool[] = [];
|
|
98
|
+
|
|
99
|
+
for (const toolStep of toolSteps) {
|
|
100
|
+
toolHandles.add(toolStep.toolHandle);
|
|
101
|
+
}
|
|
102
|
+
for (const toolHandle of toolHandles) {
|
|
103
|
+
const tool = toolMap.get(toolHandle);
|
|
104
|
+
if (!tool) {
|
|
105
|
+
throw new Error(`Tool with handle '${toolHandle}' not found in toolMap.`);
|
|
106
|
+
}
|
|
107
|
+
tools.push(tool);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return tools;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function extractRoleSpecByDirectionFromTool(tool: Tool): RoleSpecByDirection {
|
|
114
|
+
return {
|
|
115
|
+
inputRoleValueByName: new Map(getInputRoleSpecEntriesFromTool(tool)),
|
|
116
|
+
outputRoleValueByName: new Map(getOutputRoleSpecEntriesFromTool(tool)),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export function extractRoleSpecByDirectionByToolHandleFromStrategy(
|
|
121
|
+
strategy: MutableStrategy,
|
|
122
|
+
toolMap: Map<ToolHandle, Tool>,
|
|
123
|
+
): Map<ToolHandle, RoleSpecByDirection> {
|
|
124
|
+
const roleSpecByDirectionByToolHandle = new Map<ToolHandle, RoleSpecByDirection>();
|
|
125
|
+
|
|
126
|
+
for (const tool of extractToolsFromStrategy(strategy, toolMap)) {
|
|
127
|
+
roleSpecByDirectionByToolHandle.set(
|
|
128
|
+
tool.handle,
|
|
129
|
+
extractRoleSpecByDirectionFromTool(tool),
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return roleSpecByDirectionByToolHandle;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function extractSingleBranchableOutputResourceTypeHandle(
|
|
137
|
+
tool: Tool,
|
|
138
|
+
): ResourceTypeHandle | null {
|
|
139
|
+
const outputRoleSpecEntries = getOutputRoleSpecEntriesFromTool(tool).filter(
|
|
140
|
+
([roleName]) => roleName !== CONSTANTS.Cosmos.ErrorOutputRoleName,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
if (!outputRoleSpecEntries[0] || outputRoleSpecEntries.length !== 1) {
|
|
144
|
+
throw new Error(
|
|
145
|
+
`Tool ${tool.handle} must have exactly one branchable output resource type to be branchable/loopable`,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return extractResourceTypeHandleFromRoleValue(outputRoleSpecEntries[0][1]);
|
|
150
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
MutableInputResource,
|
|
3
|
+
MutableExternalInputPotential,
|
|
4
|
+
MutableInternalInputPotential,
|
|
5
|
+
MutableStrategyState,
|
|
6
|
+
MutableStrategyStateInputEntry,
|
|
7
|
+
MutableToolStepKey,
|
|
8
|
+
MutableToolStepRoleAddress,
|
|
9
|
+
} from './mutableStrategyOverlay.js';
|
|
10
|
+
import { getStrategyStateInputEntry } from './mutableStrategyOverlay.js';
|
|
11
|
+
|
|
12
|
+
export type ResolveResult<TKey extends string = MutableToolStepKey> =
|
|
13
|
+
| { status: 'inputResource'; entry: MutableInputResource; path: MutableToolStepRoleAddress<TKey>[] }
|
|
14
|
+
| { status: 'externalInputPotential'; entry: MutableExternalInputPotential; path: MutableToolStepRoleAddress<TKey>[] }
|
|
15
|
+
| {
|
|
16
|
+
status: 'unresolved';
|
|
17
|
+
reason: 'not-found' | 'cycle' | 'depth-exceeded';
|
|
18
|
+
path: MutableToolStepRoleAddress<TKey>[];
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function resolveStrategyStateChain<TKey extends string, TStrategyState extends MutableStrategyState<TKey>>(
|
|
22
|
+
strategyState: TStrategyState,
|
|
23
|
+
start: MutableToolStepRoleAddress<TKey>,
|
|
24
|
+
opts?: { maxDepth?: number },
|
|
25
|
+
): ResolveResult<TKey> {
|
|
26
|
+
const maxDepth = opts?.maxDepth ?? 50;
|
|
27
|
+
const visited = new Set<string>();
|
|
28
|
+
const path: MutableToolStepRoleAddress<TKey>[] = [];
|
|
29
|
+
let current: MutableToolStepRoleAddress<TKey> = start;
|
|
30
|
+
|
|
31
|
+
for (let depth = 0; depth <= maxDepth; depth++) {
|
|
32
|
+
path.push(current);
|
|
33
|
+
|
|
34
|
+
const visitKey = `${current.toolStepKey}::${current.roleName}`;
|
|
35
|
+
if (visited.has(visitKey)) {
|
|
36
|
+
return { status: 'unresolved', reason: 'cycle', path };
|
|
37
|
+
}
|
|
38
|
+
visited.add(visitKey);
|
|
39
|
+
|
|
40
|
+
const entry = getStrategyStateInputEntry(strategyState, current) as MutableStrategyStateInputEntry<TKey> | undefined;
|
|
41
|
+
if (!entry) {
|
|
42
|
+
return { status: 'unresolved', reason: 'not-found', path };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (entry.strategyStateInputKind === 'inputResource') {
|
|
46
|
+
return { status: 'inputResource', entry, path };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (entry.strategyStateInputKind === 'externalInputPotential') {
|
|
50
|
+
return { status: 'externalInputPotential', entry, path };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
current = (entry as MutableInternalInputPotential<TKey>).toolStepRoleAddress;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return { status: 'unresolved', reason: 'depth-exceeded', path };
|
|
57
|
+
}
|