taias 0.7.1 → 0.9.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/README.md +40 -6
- package/dist/index.cjs +354 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +117 -37
- package/dist/index.d.ts +117 -37
- package/dist/index.js +353 -47
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,8 +31,8 @@ npm install taias
|
|
|
31
31
|
import { defineFlow, createTaias } from "taias";
|
|
32
32
|
|
|
33
33
|
const flow = defineFlow("onboard", (flow) => {
|
|
34
|
-
flow.step({ toolName: "scan_repo" }, { nextTool: "configure_app" });
|
|
35
|
-
flow.step({ toolName: "configure_app" }, { nextTool: "deploy" });
|
|
34
|
+
flow.step({ toolName: { is: "scan_repo" } }, { nextTool: "configure_app" });
|
|
35
|
+
flow.step({ toolName: { is: "configure_app" } }, { nextTool: "deploy" });
|
|
36
36
|
});
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -80,13 +80,20 @@ return {
|
|
|
80
80
|
|
|
81
81
|
### `defineFlow(flowId, builder)`
|
|
82
82
|
|
|
83
|
-
Creates a flow definition. Each step is a logic statement: a match condition paired with a decision. Match
|
|
83
|
+
Creates a flow definition. Each step is a logic statement: a match condition paired with a decision. Match conditions use explicit operators (`{ is: ... }`, `{ isNot: ... }`) and can match on `toolName`, `params`, and `result`.
|
|
84
84
|
|
|
85
85
|
```ts
|
|
86
86
|
const myFlow = defineFlow("my_flow", (flow) => {
|
|
87
|
-
flow.step({ toolName: { is: "tool_a" } }, { nextTool: "tool_b" });
|
|
88
|
-
flow.step({ toolName: { isNot: "abort" } }, { nextTool: "continue" });
|
|
89
|
-
flow.step(
|
|
87
|
+
flow.step({ toolName: { is: "tool_a" } }, { nextTool: "tool_b" });
|
|
88
|
+
flow.step({ toolName: { isNot: "abort" } }, { nextTool: "continue" });
|
|
89
|
+
flow.step(
|
|
90
|
+
{ toolName: { is: "scan_repo" }, params: { language: { is: "python" } } },
|
|
91
|
+
{ nextTool: "configure_python" },
|
|
92
|
+
);
|
|
93
|
+
flow.step(
|
|
94
|
+
{ result: { hasConfig: { is: true } } },
|
|
95
|
+
{ nextTool: "review_config" },
|
|
96
|
+
);
|
|
90
97
|
});
|
|
91
98
|
```
|
|
92
99
|
|
|
@@ -113,6 +120,8 @@ const taias = createTaias({
|
|
|
113
120
|
**Options:**
|
|
114
121
|
- `flow` - A `FlowDefinition` created by `defineFlow`
|
|
115
122
|
- `devMode` (optional) - Enable development mode checks
|
|
123
|
+
- `debug` (optional) - `true` or `{ format, logger }` -- enable built-in debug output
|
|
124
|
+
- `tracing` (optional) - `"summary"` (default) or `"detailed"` -- controls trace depth
|
|
116
125
|
- `onMissingStep` (optional) - Callback invoked when no step matches
|
|
117
126
|
|
|
118
127
|
**Returns:** `Taias` instance
|
|
@@ -143,6 +152,31 @@ const affordances = await taias.resolve({
|
|
|
143
152
|
|
|
144
153
|
See the [full documentation](https://taias.xyz/docs) for complete API reference and types.
|
|
145
154
|
|
|
155
|
+
## Observability
|
|
156
|
+
|
|
157
|
+
Enable debug output with a single option:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
const taias = createTaias({ flow, debug: true });
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Every `resolve()` call prints a formatted breakdown to the console -- what went in, how the decision was reached, and what was produced.
|
|
164
|
+
|
|
165
|
+
For compact single-line output:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
const taias = createTaias({ flow, debug: { format: "compact" } });
|
|
169
|
+
// [Taias] scan_repo → nextTool=configure_app (indexed, step 0, 1 evaluated)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Enable detailed tracing for per-step evaluation breakdowns (verbose -- best used when debugging specific resolve calls):
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const taias = createTaias({ flow, debug: true, tracing: "detailed" });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
For programmatic access to resolve events, use the event emitter API (`taias.on` / `taias.off`). See the [full documentation](https://taias.xyz/docs) for details.
|
|
179
|
+
|
|
146
180
|
## Dev Mode
|
|
147
181
|
|
|
148
182
|
<details>
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
createDebugSubscriber: () => createDebugSubscriber,
|
|
23
24
|
createTaias: () => createTaias,
|
|
24
25
|
defineAffordances: () => defineAffordances,
|
|
25
26
|
defineFlow: () => defineFlow,
|
|
@@ -32,14 +33,13 @@ function defineFlow(flowId, builder) {
|
|
|
32
33
|
const steps = [];
|
|
33
34
|
const flowBuilder = {
|
|
34
35
|
step(match, input) {
|
|
35
|
-
const condition = typeof match === "string" ? { toolName: match } : match;
|
|
36
36
|
if (typeof input === "function") {
|
|
37
|
-
steps.push({ kind: "handler", match
|
|
37
|
+
steps.push({ kind: "handler", match, handler: input });
|
|
38
38
|
} else {
|
|
39
39
|
steps.push({
|
|
40
40
|
kind: "logic",
|
|
41
41
|
statement: {
|
|
42
|
-
match
|
|
42
|
+
match,
|
|
43
43
|
decision: input
|
|
44
44
|
}
|
|
45
45
|
});
|
|
@@ -107,98 +107,396 @@ function selectUiAffordances(decision, index, opts = {}) {
|
|
|
107
107
|
return selections;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// src/debugSubscriber.ts
|
|
111
|
+
function formatContext(context) {
|
|
112
|
+
const parts = [];
|
|
113
|
+
if (context.toolName) parts.push(`toolName=${context.toolName}`);
|
|
114
|
+
if (context.params) parts.push(`params=${JSON.stringify(context.params)}`);
|
|
115
|
+
if (context.result) parts.push(`result=${JSON.stringify(context.result)}`);
|
|
116
|
+
return parts.length > 0 ? parts.join(", ") : "(empty)";
|
|
117
|
+
}
|
|
118
|
+
function formatContextLabel(context) {
|
|
119
|
+
if (context.toolName) return context.toolName;
|
|
120
|
+
if (context.params) return `params:${JSON.stringify(context.params)}`;
|
|
121
|
+
if (context.result) return `result:${JSON.stringify(context.result)}`;
|
|
122
|
+
return "(empty)";
|
|
123
|
+
}
|
|
124
|
+
function createDebugSubscriber(options) {
|
|
125
|
+
const format = options?.format ?? "default";
|
|
126
|
+
const log = options?.logger ?? console.log;
|
|
127
|
+
if (format === "compact") {
|
|
128
|
+
return (event) => {
|
|
129
|
+
const { trace, context } = event;
|
|
130
|
+
const label = formatContextLabel(context);
|
|
131
|
+
if (!trace.matched) {
|
|
132
|
+
log(`[Taias] ${label} \u2192 no match (${trace.candidatesEvaluated} evaluated)`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
const decisionSummary = event.decision ? Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(", ") : "null";
|
|
136
|
+
log(
|
|
137
|
+
`[Taias] ${label} \u2192 ${decisionSummary} (${trace.phase}, step ${trace.matchedStepIndex}, ${trace.candidatesEvaluated} evaluated)`
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return (event) => {
|
|
142
|
+
const { trace, context } = event;
|
|
143
|
+
const lines = [];
|
|
144
|
+
lines.push("\u250C\u2500 Taias Resolve \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
145
|
+
lines.push(`\u2502 Flow: ${event.flowId}`);
|
|
146
|
+
lines.push(`\u2502 Context: ${formatContext(context)}`);
|
|
147
|
+
lines.push("\u2502");
|
|
148
|
+
if (!trace.matched) {
|
|
149
|
+
lines.push("\u2502 Result: NO MATCH");
|
|
150
|
+
lines.push(`\u2502 Candidates evaluated: ${trace.candidatesEvaluated}`);
|
|
151
|
+
} else {
|
|
152
|
+
lines.push(`\u2502 Matched: step ${trace.matchedStepIndex} (${trace.matchedStepKind})`);
|
|
153
|
+
lines.push(`\u2502 Phase: ${trace.phase}`);
|
|
154
|
+
if (trace.resolutionPath.length > 0) {
|
|
155
|
+
lines.push(`\u2502 Resolution path: ${trace.resolutionPath.join(" \u2192 ")}`);
|
|
156
|
+
}
|
|
157
|
+
lines.push(`\u2502 Candidates evaluated: ${trace.candidatesEvaluated}`);
|
|
158
|
+
if (trace.matchedStepMatch) {
|
|
159
|
+
lines.push(`\u2502 Match condition: ${JSON.stringify(trace.matchedStepMatch)}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
lines.push("\u2502");
|
|
163
|
+
if (event.decision) {
|
|
164
|
+
const fields = Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
165
|
+
lines.push(`\u2502 Decision: ${fields}`);
|
|
166
|
+
} else {
|
|
167
|
+
lines.push("\u2502 Decision: null");
|
|
168
|
+
}
|
|
169
|
+
if (event.affordances) {
|
|
170
|
+
lines.push(`\u2502 Advice: ${event.affordances.advice.slice(0, 80)}${event.affordances.advice.length > 80 ? "..." : ""}`);
|
|
171
|
+
}
|
|
172
|
+
lines.push(`\u2502 Duration: ${event.durationMs.toFixed(2)}ms`);
|
|
173
|
+
if (trace.evaluations) {
|
|
174
|
+
lines.push("\u2502");
|
|
175
|
+
lines.push("\u2502 Detailed evaluations:");
|
|
176
|
+
for (const evaluation of trace.evaluations) {
|
|
177
|
+
const icon = evaluation.result === "matched" ? "\u2713" : "\u2717";
|
|
178
|
+
lines.push(`\u2502 ${icon} step ${evaluation.stepIndex}: ${evaluation.result}`);
|
|
179
|
+
if (evaluation.fieldResults) {
|
|
180
|
+
for (const [field, fr] of Object.entries(evaluation.fieldResults)) {
|
|
181
|
+
const fieldIcon = fr.passed ? "\u2713" : "\u2717";
|
|
182
|
+
lines.push(`\u2502 ${fieldIcon} ${field}: ${JSON.stringify(fr.condition)} vs ${JSON.stringify(fr.actual)}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
lines.push("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500");
|
|
188
|
+
log(lines.join("\n"));
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
110
192
|
// src/createTaias.ts
|
|
111
193
|
function generateAdvice(nextTool) {
|
|
112
194
|
return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;
|
|
113
195
|
}
|
|
114
|
-
function normalizeFieldCondition(field) {
|
|
115
|
-
return typeof field === "string" ? { is: field } : field;
|
|
116
|
-
}
|
|
117
196
|
function evaluateCondition(condition, value) {
|
|
118
197
|
if ("is" in condition) return value === condition.is;
|
|
119
198
|
if ("isNot" in condition) return value !== condition.isNot;
|
|
120
199
|
return false;
|
|
121
200
|
}
|
|
122
201
|
function evaluateMatch(match, ctx) {
|
|
123
|
-
|
|
124
|
-
|
|
202
|
+
if (match.toolName && !evaluateCondition(match.toolName, ctx.toolName)) return false;
|
|
203
|
+
if (match.params) {
|
|
204
|
+
if (!ctx.params) return false;
|
|
205
|
+
for (const [key, condition] of Object.entries(match.params)) {
|
|
206
|
+
if (!evaluateCondition(condition, ctx.params[key])) return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
if (match.result) {
|
|
210
|
+
if (!ctx.result) return false;
|
|
211
|
+
for (const [key, condition] of Object.entries(match.result)) {
|
|
212
|
+
if (!evaluateCondition(condition, ctx.result[key])) return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
125
216
|
}
|
|
126
|
-
function
|
|
127
|
-
|
|
128
|
-
|
|
217
|
+
function evaluateMatchDetailed(match, ctx) {
|
|
218
|
+
const fieldResults = {};
|
|
219
|
+
let allPassed = true;
|
|
220
|
+
if (match.toolName) {
|
|
221
|
+
const actual = ctx.toolName;
|
|
222
|
+
const passed = evaluateCondition(match.toolName, actual);
|
|
223
|
+
fieldResults["toolName"] = { condition: match.toolName, actual, passed };
|
|
224
|
+
if (!passed) allPassed = false;
|
|
225
|
+
}
|
|
226
|
+
if (match.params) {
|
|
227
|
+
if (!ctx.params) {
|
|
228
|
+
for (const [key, condition] of Object.entries(match.params)) {
|
|
229
|
+
fieldResults[`params.${key}`] = { condition, actual: void 0, passed: false };
|
|
230
|
+
}
|
|
231
|
+
allPassed = false;
|
|
232
|
+
} else {
|
|
233
|
+
for (const [key, condition] of Object.entries(match.params)) {
|
|
234
|
+
const actual = ctx.params[key];
|
|
235
|
+
const passed = evaluateCondition(condition, actual);
|
|
236
|
+
fieldResults[`params.${key}`] = { condition, actual, passed };
|
|
237
|
+
if (!passed) allPassed = false;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (match.result) {
|
|
242
|
+
if (!ctx.result) {
|
|
243
|
+
for (const [key, condition] of Object.entries(match.result)) {
|
|
244
|
+
fieldResults[`result.${key}`] = { condition, actual: void 0, passed: false };
|
|
245
|
+
}
|
|
246
|
+
allPassed = false;
|
|
247
|
+
} else {
|
|
248
|
+
for (const [key, condition] of Object.entries(match.result)) {
|
|
249
|
+
const actual = ctx.result[key];
|
|
250
|
+
const passed = evaluateCondition(condition, actual);
|
|
251
|
+
fieldResults[`result.${key}`] = { condition, actual, passed };
|
|
252
|
+
if (!passed) allPassed = false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return { passed: allPassed, fieldResults };
|
|
129
257
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
if ("is" in
|
|
133
|
-
|
|
258
|
+
function extractIsConditions(match) {
|
|
259
|
+
const conditions = [];
|
|
260
|
+
if (match.toolName && "is" in match.toolName) {
|
|
261
|
+
conditions.push({ path: "toolName", value: match.toolName.is });
|
|
262
|
+
}
|
|
263
|
+
if (match.params) {
|
|
264
|
+
for (const [key, cond] of Object.entries(match.params)) {
|
|
265
|
+
if ("is" in cond) conditions.push({ path: `params.${key}`, value: cond.is });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (match.result) {
|
|
269
|
+
for (const [key, cond] of Object.entries(match.result)) {
|
|
270
|
+
if ("is" in cond) conditions.push({ path: `result.${key}`, value: cond.is });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return conditions;
|
|
274
|
+
}
|
|
275
|
+
function hasConditionOnField(match, path) {
|
|
276
|
+
if (path === "toolName") return !!match.toolName;
|
|
277
|
+
if (path.startsWith("params.")) return !!match.params?.[path.slice(7)];
|
|
278
|
+
if (path.startsWith("result.")) return !!match.result?.[path.slice(7)];
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
function getContextValue(ctx, path) {
|
|
282
|
+
if (path === "toolName") return ctx.toolName;
|
|
283
|
+
if (path.startsWith("params.")) return ctx.params?.[path.slice(7)];
|
|
284
|
+
if (path.startsWith("result.")) return ctx.result?.[path.slice(7)];
|
|
285
|
+
return void 0;
|
|
134
286
|
}
|
|
135
287
|
function getMatch(step) {
|
|
136
288
|
return step.kind === "logic" ? step.statement.match : step.match;
|
|
137
289
|
}
|
|
138
290
|
function serializeMatch(match) {
|
|
139
|
-
|
|
140
|
-
return JSON.stringify({ toolName: normalized });
|
|
291
|
+
return JSON.stringify(match);
|
|
141
292
|
}
|
|
142
293
|
function createTaias(options) {
|
|
143
294
|
const {
|
|
144
295
|
flow,
|
|
145
296
|
affordances,
|
|
146
297
|
devMode = false,
|
|
298
|
+
debug = false,
|
|
299
|
+
tracing = "summary",
|
|
147
300
|
onMissingStep,
|
|
148
301
|
onWarn
|
|
149
302
|
} = options;
|
|
150
303
|
const warn = onWarn ?? ((msg) => console.warn(msg));
|
|
304
|
+
const detailed = tracing === "detailed";
|
|
151
305
|
if (devMode) {
|
|
152
306
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
153
307
|
for (const step of flow.steps) {
|
|
154
308
|
const key = serializeMatch(getMatch(step));
|
|
155
309
|
if (seenKeys.has(key)) {
|
|
156
|
-
const match = getMatch(step);
|
|
157
|
-
const normalized = normalizeFieldCondition(match.toolName);
|
|
158
|
-
const label = "is" in normalized ? normalized.is : `isNot:${normalized.isNot}`;
|
|
159
310
|
throw new Error(
|
|
160
|
-
`Taias: Duplicate match condition
|
|
311
|
+
`Taias: Duplicate match condition in flow '${flow.id}'. Each step must have a unique match condition. Duplicate: ${key}`
|
|
161
312
|
);
|
|
162
313
|
}
|
|
163
314
|
seenKeys.add(key);
|
|
164
315
|
}
|
|
165
316
|
}
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
317
|
+
const fieldIndexes = /* @__PURE__ */ new Map();
|
|
318
|
+
const indexableStepIndices = [];
|
|
319
|
+
const broadStepIndices = [];
|
|
320
|
+
for (let i = 0; i < flow.steps.length; i++) {
|
|
321
|
+
const match = getMatch(flow.steps[i]);
|
|
322
|
+
const isConditions = extractIsConditions(match);
|
|
323
|
+
if (isConditions.length === 0) {
|
|
324
|
+
broadStepIndices.push(i);
|
|
325
|
+
continue;
|
|
326
|
+
}
|
|
327
|
+
indexableStepIndices.push(i);
|
|
328
|
+
for (const { path, value } of isConditions) {
|
|
329
|
+
let fieldIndex = fieldIndexes.get(path);
|
|
330
|
+
if (!fieldIndex) {
|
|
331
|
+
fieldIndex = { valueMap: /* @__PURE__ */ new Map(), unconstrained: [] };
|
|
332
|
+
fieldIndexes.set(path, fieldIndex);
|
|
333
|
+
}
|
|
334
|
+
let stepList = fieldIndex.valueMap.get(value);
|
|
335
|
+
if (!stepList) {
|
|
336
|
+
stepList = [];
|
|
337
|
+
fieldIndex.valueMap.set(value, stepList);
|
|
338
|
+
}
|
|
339
|
+
stepList.push(i);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
for (const [fieldPath, fieldIndex] of fieldIndexes) {
|
|
343
|
+
for (const i of indexableStepIndices) {
|
|
344
|
+
if (!hasConditionOnField(getMatch(flow.steps[i]), fieldPath)) {
|
|
345
|
+
fieldIndex.unconstrained.push(i);
|
|
346
|
+
}
|
|
174
347
|
}
|
|
175
348
|
}
|
|
176
|
-
const hasBroadSteps =
|
|
349
|
+
const hasBroadSteps = broadStepIndices.length > 0;
|
|
177
350
|
const registryIndex = buildRegistryIndex(affordances);
|
|
351
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
352
|
+
function emit(event, data) {
|
|
353
|
+
const handlers = listeners.get(event);
|
|
354
|
+
if (handlers) {
|
|
355
|
+
for (const handler of handlers) handler(data);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function on(event, handler) {
|
|
359
|
+
let set = listeners.get(event);
|
|
360
|
+
if (!set) {
|
|
361
|
+
set = /* @__PURE__ */ new Set();
|
|
362
|
+
listeners.set(event, set);
|
|
363
|
+
}
|
|
364
|
+
set.add(handler);
|
|
365
|
+
}
|
|
366
|
+
function off(event, handler) {
|
|
367
|
+
listeners.get(event)?.delete(handler);
|
|
368
|
+
}
|
|
369
|
+
if (debug) {
|
|
370
|
+
const debugOpts = typeof debug === "object" ? debug : void 0;
|
|
371
|
+
on("resolve", createDebugSubscriber(debugOpts));
|
|
372
|
+
}
|
|
178
373
|
return {
|
|
179
374
|
async resolve(ctx) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
375
|
+
const startTime = performance.now();
|
|
376
|
+
const timestamp = Date.now();
|
|
377
|
+
let matchedStep;
|
|
378
|
+
let matchedStepIndex = null;
|
|
379
|
+
let matchPhase = null;
|
|
380
|
+
const resolutionPath = [];
|
|
381
|
+
let candidatesEvaluated = 0;
|
|
382
|
+
const evaluations = detailed ? [] : void 0;
|
|
383
|
+
function tryMatch(idx) {
|
|
384
|
+
candidatesEvaluated++;
|
|
385
|
+
const step = flow.steps[idx];
|
|
386
|
+
const match = getMatch(step);
|
|
387
|
+
if (detailed) {
|
|
388
|
+
const { passed, fieldResults } = evaluateMatchDetailed(match, ctx);
|
|
389
|
+
evaluations.push({
|
|
390
|
+
stepIndex: idx,
|
|
391
|
+
match,
|
|
392
|
+
result: passed ? "matched" : "no-match",
|
|
393
|
+
fieldResults
|
|
394
|
+
});
|
|
395
|
+
return passed;
|
|
396
|
+
}
|
|
397
|
+
return evaluateMatch(match, ctx);
|
|
398
|
+
}
|
|
399
|
+
const applicableFieldPaths = [];
|
|
400
|
+
for (const fieldPath of fieldIndexes.keys()) {
|
|
401
|
+
const ctxValue = getContextValue(ctx, fieldPath);
|
|
402
|
+
if (ctxValue !== void 0) {
|
|
403
|
+
applicableFieldPaths.push(fieldPath);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
resolutionPath.push(...applicableFieldPaths);
|
|
407
|
+
if (applicableFieldPaths.length > 0) {
|
|
408
|
+
let candidates = null;
|
|
409
|
+
for (const fieldPath of applicableFieldPaths) {
|
|
410
|
+
const fieldIndex = fieldIndexes.get(fieldPath);
|
|
411
|
+
const ctxValue = getContextValue(ctx, fieldPath);
|
|
412
|
+
const fieldCandidates = /* @__PURE__ */ new Set();
|
|
413
|
+
const indexed = fieldIndex.valueMap.get(ctxValue);
|
|
414
|
+
if (indexed) {
|
|
415
|
+
for (const idx of indexed) fieldCandidates.add(idx);
|
|
416
|
+
}
|
|
417
|
+
for (const idx of fieldIndex.unconstrained) {
|
|
418
|
+
fieldCandidates.add(idx);
|
|
419
|
+
}
|
|
420
|
+
if (candidates === null) {
|
|
421
|
+
candidates = fieldCandidates;
|
|
422
|
+
} else {
|
|
423
|
+
for (const idx of candidates) {
|
|
424
|
+
if (!fieldCandidates.has(idx)) candidates.delete(idx);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (candidates && candidates.size > 0) {
|
|
429
|
+
const sorted = [...candidates].sort((a, b) => a - b);
|
|
430
|
+
for (const idx of sorted) {
|
|
431
|
+
if (tryMatch(idx)) {
|
|
432
|
+
matchedStep = flow.steps[idx];
|
|
433
|
+
matchedStepIndex = idx;
|
|
434
|
+
matchPhase = "indexed";
|
|
435
|
+
break;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
} else if (indexableStepIndices.length > 0) {
|
|
440
|
+
for (const idx of indexableStepIndices) {
|
|
441
|
+
if (tryMatch(idx)) {
|
|
442
|
+
matchedStep = flow.steps[idx];
|
|
443
|
+
matchedStepIndex = idx;
|
|
444
|
+
matchPhase = "indexed";
|
|
445
|
+
break;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (!matchedStep && hasBroadSteps) {
|
|
450
|
+
for (const idx of broadStepIndices) {
|
|
451
|
+
if (tryMatch(idx)) {
|
|
452
|
+
matchedStep = flow.steps[idx];
|
|
453
|
+
matchedStepIndex = idx;
|
|
454
|
+
matchPhase = "broad";
|
|
187
455
|
break;
|
|
188
456
|
}
|
|
189
457
|
}
|
|
190
458
|
}
|
|
191
|
-
|
|
459
|
+
const trace = {
|
|
460
|
+
matched: !!matchedStep,
|
|
461
|
+
matchedStepIndex,
|
|
462
|
+
matchedStepKind: matchedStep?.kind ?? null,
|
|
463
|
+
matchedStepMatch: matchedStep ? getMatch(matchedStep) : null,
|
|
464
|
+
phase: matchPhase,
|
|
465
|
+
resolutionPath,
|
|
466
|
+
candidatesEvaluated,
|
|
467
|
+
...evaluations !== void 0 ? { evaluations } : {}
|
|
468
|
+
};
|
|
469
|
+
if (!matchedStep) {
|
|
192
470
|
onMissingStep?.(ctx);
|
|
471
|
+
emit("resolve", {
|
|
472
|
+
flowId: flow.id,
|
|
473
|
+
timestamp,
|
|
474
|
+
durationMs: performance.now() - startTime,
|
|
475
|
+
context: ctx,
|
|
476
|
+
trace,
|
|
477
|
+
decision: null,
|
|
478
|
+
affordances: null
|
|
479
|
+
});
|
|
193
480
|
return null;
|
|
194
481
|
}
|
|
195
482
|
let result;
|
|
196
|
-
if (
|
|
197
|
-
result =
|
|
483
|
+
if (matchedStep.kind === "logic") {
|
|
484
|
+
result = matchedStep.statement.decision;
|
|
198
485
|
} else {
|
|
199
|
-
result = await
|
|
486
|
+
result = await matchedStep.handler(ctx);
|
|
487
|
+
}
|
|
488
|
+
if (!result) {
|
|
489
|
+
emit("resolve", {
|
|
490
|
+
flowId: flow.id,
|
|
491
|
+
timestamp,
|
|
492
|
+
durationMs: performance.now() - startTime,
|
|
493
|
+
context: ctx,
|
|
494
|
+
trace,
|
|
495
|
+
decision: null,
|
|
496
|
+
affordances: null
|
|
497
|
+
});
|
|
498
|
+
return null;
|
|
200
499
|
}
|
|
201
|
-
if (!result) return null;
|
|
202
500
|
if (devMode && result.nextTool === "") {
|
|
203
501
|
warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);
|
|
204
502
|
}
|
|
@@ -207,12 +505,20 @@ function createTaias(options) {
|
|
|
207
505
|
devMode,
|
|
208
506
|
onWarn: warn
|
|
209
507
|
});
|
|
210
|
-
|
|
211
|
-
|
|
508
|
+
const advice = generateAdvice(result.nextTool);
|
|
509
|
+
emit("resolve", {
|
|
510
|
+
flowId: flow.id,
|
|
511
|
+
timestamp,
|
|
512
|
+
durationMs: performance.now() - startTime,
|
|
513
|
+
context: ctx,
|
|
514
|
+
trace,
|
|
212
515
|
decision,
|
|
213
|
-
selections
|
|
214
|
-
};
|
|
215
|
-
|
|
516
|
+
affordances: { advice, selections }
|
|
517
|
+
});
|
|
518
|
+
return { advice, decision, selections };
|
|
519
|
+
},
|
|
520
|
+
on,
|
|
521
|
+
off
|
|
216
522
|
};
|
|
217
523
|
}
|
|
218
524
|
|
|
@@ -263,6 +569,7 @@ function mergeAffordances(registries, opts = {}) {
|
|
|
263
569
|
}
|
|
264
570
|
// Annotate the CommonJS export names for ESM import in node:
|
|
265
571
|
0 && (module.exports = {
|
|
572
|
+
createDebugSubscriber,
|
|
266
573
|
createTaias,
|
|
267
574
|
defineAffordances,
|
|
268
575
|
defineFlow,
|