taias 0.8.0 → 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 +27 -0
- package/dist/index.cjs +227 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +84 -1
- package/dist/index.d.ts +84 -1
- package/dist/index.js +226 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -120,6 +120,8 @@ const taias = createTaias({
|
|
|
120
120
|
**Options:**
|
|
121
121
|
- `flow` - A `FlowDefinition` created by `defineFlow`
|
|
122
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
|
|
123
125
|
- `onMissingStep` (optional) - Callback invoked when no step matches
|
|
124
126
|
|
|
125
127
|
**Returns:** `Taias` instance
|
|
@@ -150,6 +152,31 @@ const affordances = await taias.resolve({
|
|
|
150
152
|
|
|
151
153
|
See the [full documentation](https://taias.xyz/docs) for complete API reference and types.
|
|
152
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
|
+
|
|
153
180
|
## Dev Mode
|
|
154
181
|
|
|
155
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,
|
|
@@ -106,6 +107,88 @@ function selectUiAffordances(decision, index, opts = {}) {
|
|
|
106
107
|
return selections;
|
|
107
108
|
}
|
|
108
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
|
+
|
|
109
192
|
// src/createTaias.ts
|
|
110
193
|
function generateAdvice(nextTool) {
|
|
111
194
|
return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;
|
|
@@ -131,6 +214,47 @@ function evaluateMatch(match, ctx) {
|
|
|
131
214
|
}
|
|
132
215
|
return true;
|
|
133
216
|
}
|
|
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 };
|
|
257
|
+
}
|
|
134
258
|
function extractIsConditions(match) {
|
|
135
259
|
const conditions = [];
|
|
136
260
|
if (match.toolName && "is" in match.toolName) {
|
|
@@ -171,10 +295,13 @@ function createTaias(options) {
|
|
|
171
295
|
flow,
|
|
172
296
|
affordances,
|
|
173
297
|
devMode = false,
|
|
298
|
+
debug = false,
|
|
299
|
+
tracing = "summary",
|
|
174
300
|
onMissingStep,
|
|
175
301
|
onWarn
|
|
176
302
|
} = options;
|
|
177
303
|
const warn = onWarn ?? ((msg) => console.warn(msg));
|
|
304
|
+
const detailed = tracing === "detailed";
|
|
178
305
|
if (devMode) {
|
|
179
306
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
180
307
|
for (const step of flow.steps) {
|
|
@@ -221,9 +348,54 @@ function createTaias(options) {
|
|
|
221
348
|
}
|
|
222
349
|
const hasBroadSteps = broadStepIndices.length > 0;
|
|
223
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
|
+
}
|
|
224
373
|
return {
|
|
225
374
|
async resolve(ctx) {
|
|
375
|
+
const startTime = performance.now();
|
|
376
|
+
const timestamp = Date.now();
|
|
226
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
|
+
}
|
|
227
399
|
const applicableFieldPaths = [];
|
|
228
400
|
for (const fieldPath of fieldIndexes.keys()) {
|
|
229
401
|
const ctxValue = getContextValue(ctx, fieldPath);
|
|
@@ -231,6 +403,7 @@ function createTaias(options) {
|
|
|
231
403
|
applicableFieldPaths.push(fieldPath);
|
|
232
404
|
}
|
|
233
405
|
}
|
|
406
|
+
resolutionPath.push(...applicableFieldPaths);
|
|
234
407
|
if (applicableFieldPaths.length > 0) {
|
|
235
408
|
let candidates = null;
|
|
236
409
|
for (const fieldPath of applicableFieldPaths) {
|
|
@@ -255,30 +428,55 @@ function createTaias(options) {
|
|
|
255
428
|
if (candidates && candidates.size > 0) {
|
|
256
429
|
const sorted = [...candidates].sort((a, b) => a - b);
|
|
257
430
|
for (const idx of sorted) {
|
|
258
|
-
if (
|
|
431
|
+
if (tryMatch(idx)) {
|
|
259
432
|
matchedStep = flow.steps[idx];
|
|
433
|
+
matchedStepIndex = idx;
|
|
434
|
+
matchPhase = "indexed";
|
|
260
435
|
break;
|
|
261
436
|
}
|
|
262
437
|
}
|
|
263
438
|
}
|
|
264
439
|
} else if (indexableStepIndices.length > 0) {
|
|
265
440
|
for (const idx of indexableStepIndices) {
|
|
266
|
-
if (
|
|
441
|
+
if (tryMatch(idx)) {
|
|
267
442
|
matchedStep = flow.steps[idx];
|
|
443
|
+
matchedStepIndex = idx;
|
|
444
|
+
matchPhase = "indexed";
|
|
268
445
|
break;
|
|
269
446
|
}
|
|
270
447
|
}
|
|
271
448
|
}
|
|
272
449
|
if (!matchedStep && hasBroadSteps) {
|
|
273
450
|
for (const idx of broadStepIndices) {
|
|
274
|
-
if (
|
|
451
|
+
if (tryMatch(idx)) {
|
|
275
452
|
matchedStep = flow.steps[idx];
|
|
453
|
+
matchedStepIndex = idx;
|
|
454
|
+
matchPhase = "broad";
|
|
276
455
|
break;
|
|
277
456
|
}
|
|
278
457
|
}
|
|
279
458
|
}
|
|
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
|
+
};
|
|
280
469
|
if (!matchedStep) {
|
|
281
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
|
+
});
|
|
282
480
|
return null;
|
|
283
481
|
}
|
|
284
482
|
let result;
|
|
@@ -287,7 +485,18 @@ function createTaias(options) {
|
|
|
287
485
|
} else {
|
|
288
486
|
result = await matchedStep.handler(ctx);
|
|
289
487
|
}
|
|
290
|
-
if (!result)
|
|
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;
|
|
499
|
+
}
|
|
291
500
|
if (devMode && result.nextTool === "") {
|
|
292
501
|
warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);
|
|
293
502
|
}
|
|
@@ -296,12 +505,20 @@ function createTaias(options) {
|
|
|
296
505
|
devMode,
|
|
297
506
|
onWarn: warn
|
|
298
507
|
});
|
|
299
|
-
|
|
300
|
-
|
|
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,
|
|
301
515
|
decision,
|
|
302
|
-
selections
|
|
303
|
-
};
|
|
304
|
-
|
|
516
|
+
affordances: { advice, selections }
|
|
517
|
+
});
|
|
518
|
+
return { advice, decision, selections };
|
|
519
|
+
},
|
|
520
|
+
on,
|
|
521
|
+
off
|
|
305
522
|
};
|
|
306
523
|
}
|
|
307
524
|
|
|
@@ -352,6 +569,7 @@ function mergeAffordances(registries, opts = {}) {
|
|
|
352
569
|
}
|
|
353
570
|
// Annotate the CommonJS export names for ESM import in node:
|
|
354
571
|
0 && (module.exports = {
|
|
572
|
+
createDebugSubscriber,
|
|
355
573
|
createTaias,
|
|
356
574
|
defineAffordances,
|
|
357
575
|
defineFlow,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/flow.ts","../src/uiAffordances/types.ts","../src/uiAffordances/indexing.ts","../src/uiAffordances/select.ts","../src/createTaias.ts","../src/uiAffordances/defineAffordances.ts","../src/uiAffordances/mergeAffordances.ts"],"sourcesContent":["// Main exports\nexport { defineFlow } from \"./flow\";\nexport { createTaias } from \"./createTaias\";\n\n// UI affordances exports\nexport { defineAffordances } from \"./uiAffordances/defineAffordances\";\nexport { mergeAffordances } from \"./uiAffordances/mergeAffordances\";\nexport type { AffordanceRegistrar } from \"./uiAffordances/defineAffordances\";\nexport type {\n DefaultSlots,\n CanonicalSlot,\n Binding,\n BindingInput,\n HandleRegistration,\n Selection,\n UiSelections,\n AffordanceRegistry,\n} from \"./uiAffordances/types\";\n\n// Core + flow type exports\nexport type {\n Condition,\n Decision,\n TaiasContext,\n StepDecision,\n Affordances,\n StepHandler,\n StepInput,\n MatchCondition,\n LogicStatement,\n FlowStep,\n FlowDefinition,\n FlowBuilder,\n TaiasOptions,\n Taias,\n} from \"./types\";\n","import type { FlowBuilder, FlowDefinition, FlowStep, MatchCondition, StepInput } from \"./types\";\n\n/**\n * Define a flow with its steps.\n *\n * @param flowId - Unique identifier for the flow\n * @param builder - Callback that receives a FlowBuilder to define steps\n * @returns A FlowDefinition object\n *\n * @example Logic statement matching on toolName\n * ```ts\n * const onboardRepoFlow = defineFlow(\"onboard_repo\", (flow) => {\n * flow.step({ toolName: { is: \"scan_repo\" } }, { nextTool: \"configure_app\" });\n * });\n * ```\n *\n * @example Matching on params and result\n * ```ts\n * flow.step(\n * { toolName: { is: \"scan_repo\" }, params: { language: { is: \"python\" } } },\n * { nextTool: \"configure_python\" },\n * );\n * flow.step(\n * { result: { hasConfig: { is: true } } },\n * { nextTool: \"review_config\" },\n * );\n * ```\n *\n * @example isNot operator\n * ```ts\n * flow.step({ toolName: { isNot: \"abort_session\" } }, { nextTool: \"continue_flow\" });\n * ```\n */\nexport function defineFlow(\n flowId: string,\n builder: (flow: FlowBuilder) => void\n): FlowDefinition {\n const steps: FlowStep[] = [];\n\n const flowBuilder: FlowBuilder = {\n step(match: MatchCondition, input: StepInput): void {\n if (typeof input === \"function\") {\n steps.push({ kind: \"handler\", match, handler: input });\n } else {\n steps.push({\n kind: \"logic\",\n statement: {\n match,\n decision: input,\n },\n });\n }\n },\n };\n\n builder(flowBuilder);\n\n return {\n id: flowId,\n steps,\n };\n}\n","/**\n * Default slots for backwards compatibility.\n * Users can define custom slots by passing a type parameter.\n */\nexport type DefaultSlots = \"primaryCta\" | \"secondaryCta\" | \"widgetVariant\";\n\n/**\n * Alias for backwards compatibility in exports.\n * @deprecated Use DefaultSlots or define your own slot type\n */\nexport type CanonicalSlot = DefaultSlots;\n\nexport type Binding = {\n key: string;\n value: string;\n};\n\n/**\n * Input format for registering affordance bindings.\n * - { toolName } is shorthand for { key: \"nextTool\", value: toolName }\n * - { key, value } is the generalized form for custom bindings\n */\nexport type BindingInput = { toolName: string } | { key: string; value: string };\n\n/**\n * A registered UI affordance handle.\n * Generic over slot type S for custom slot support.\n */\nexport type HandleRegistration<S extends string = DefaultSlots> = {\n slot: S;\n handleId: string;\n bindsTo: Binding;\n};\n\nexport type Selection = {\n handleId: string;\n bindsTo: Binding;\n};\n\n/**\n * UI selections keyed by slot name.\n * Generic over slot type S for custom slot support.\n */\nexport type UiSelections<S extends string = DefaultSlots> = Partial<Record<S, Selection>>;\n\n/**\n * Collection of registered handles.\n * Generic over slot type S for custom slot support.\n */\nexport type AffordanceRegistry<S extends string = DefaultSlots> = {\n handles: HandleRegistration<S>[];\n};\n\nexport function normalizeBinding(input: BindingInput): Binding {\n if (\"toolName\" in input) return { key: \"nextTool\", value: input.toolName };\n return { key: input.key, value: input.value };\n}\n\n/**\n * Creates a binding key for indexing.\n * Accepts any string for slot to support custom slots.\n */\nexport function makeBindingKey(slot: string, binding: Binding): string {\n return `${slot}::${binding.key}::${binding.value}`;\n}\n","import type { AffordanceRegistry, DefaultSlots, HandleRegistration } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\n/**\n * Index structure for efficient affordance lookup.\n * Generic over slot type S for custom slot support.\n */\nexport type RegistryIndex<S extends string = DefaultSlots> = {\n byBindingKey: Map<string, HandleRegistration<S>>;\n slots: Set<S>;\n /** Inferred decision field for each slot, derived from handle bindings. */\n slotKeyMap: Map<S, string>;\n};\n\n/**\n * Build an index from a registry for efficient lookup during selection.\n * Tracks which slots have registered handles and infers the decision field\n * for each slot from its handle bindings.\n *\n * @throws Error if handles for the same slot bind to different keys\n */\nexport function buildRegistryIndex<S extends string = DefaultSlots>(\n registry?: AffordanceRegistry<S>\n): RegistryIndex<S> {\n const byBindingKey = new Map<string, HandleRegistration<S>>();\n const slots = new Set<S>();\n const slotKeyMap = new Map<S, string>();\n\n if (!registry) return { byBindingKey, slots, slotKeyMap };\n\n for (const h of registry.handles) {\n slots.add(h.slot);\n\n // Infer and validate slot key\n const existingKey = slotKeyMap.get(h.slot);\n if (existingKey && existingKey !== h.bindsTo.key) {\n throw new Error(\n `[Taias] Slot \"${h.slot}\" has handles bound to different keys: \"${existingKey}\" and \"${h.bindsTo.key}\". ` +\n `All handles for a slot must use the same decision field.`\n );\n }\n slotKeyMap.set(h.slot, h.bindsTo.key);\n\n byBindingKey.set(makeBindingKey(h.slot, h.bindsTo), h);\n }\n\n return { byBindingKey, slots, slotKeyMap };\n}\n","import type { Decision } from \"../types\";\nimport type { DefaultSlots, UiSelections } from \"./types\";\nimport type { RegistryIndex } from \"./indexing\";\nimport { makeBindingKey } from \"./types\";\n\nexport type SelectOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Select UI affordances based on flow decision.\n * Uses the inferred decision field for each slot from the registry index.\n */\nexport function selectUiAffordances<S extends string = DefaultSlots>(\n decision: Decision,\n index: RegistryIndex<S>,\n opts: SelectOptions = {}\n): UiSelections<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const selections: UiSelections<S> = {};\n\n for (const slot of index.slots) {\n // Use inferred key from handle bindings\n const field = index.slotKeyMap.get(slot);\n if (!field) continue;\n\n const value = decision[field];\n if (!value) continue;\n\n const k = makeBindingKey(slot, { key: field, value });\n const handle = index.byBindingKey.get(k);\n\n if (!handle) {\n if (devMode) warn(`[Taias] No affordance for slot \"${slot}\" when ${field}=\"${value}\"`);\n continue;\n }\n\n (selections as Record<string, unknown>)[slot] = {\n handleId: handle.handleId,\n bindsTo: handle.bindsTo,\n };\n }\n\n return selections;\n}\n","import type { Affordances, Condition, FlowStep, MatchCondition, StepDecision, Taias, TaiasContext, TaiasOptions, Decision } from \"./types\";\nimport type { DefaultSlots } from \"./uiAffordances/types\";\nimport { buildRegistryIndex } from \"./uiAffordances/indexing\";\nimport { selectUiAffordances } from \"./uiAffordances/select\";\n\n/**\n * Generate advice text for a given next tool.\n */\nfunction generateAdvice(nextTool: string): string {\n return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;\n}\n\n// ---------------------------------------------------------------------------\n// Condition evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a single Condition against a value using strict equality.\n */\nfunction evaluateCondition(condition: Condition, value: unknown): boolean {\n if (\"is\" in condition) return value === condition.is;\n if (\"isNot\" in condition) return value !== condition.isNot;\n return false;\n}\n\n/**\n * Evaluate a full MatchCondition against a TaiasContext.\n * All present conditions must be satisfied (logical AND).\n *\n * - toolName: compared directly against ctx.toolName\n * - params: subset match -- each specified key is checked against ctx.params\n * - result: subset match -- each specified key is checked against ctx.result\n *\n * If a step specifies params/result conditions but the context doesn't\n * include params/result, the step does not match.\n */\nfunction evaluateMatch(match: MatchCondition, ctx: TaiasContext): boolean {\n if (match.toolName && !evaluateCondition(match.toolName, ctx.toolName)) return false;\n\n if (match.params) {\n if (!ctx.params) return false;\n for (const [key, condition] of Object.entries(match.params)) {\n if (!evaluateCondition(condition, ctx.params[key])) return false;\n }\n }\n\n if (match.result) {\n if (!ctx.result) return false;\n for (const [key, condition] of Object.entries(match.result)) {\n if (!evaluateCondition(condition, ctx.result[key])) return false;\n }\n }\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Per-field indexing\n// ---------------------------------------------------------------------------\n\ninterface FieldIndex {\n valueMap: Map<unknown, number[]>;\n unconstrained: number[];\n}\n\n/**\n * Extract all `is` conditions from a match condition as field-path / value pairs.\n */\nfunction extractIsConditions(match: MatchCondition): Array<{ path: string; value: unknown }> {\n const conditions: Array<{ path: string; value: unknown }> = [];\n if (match.toolName && \"is\" in match.toolName) {\n conditions.push({ path: \"toolName\", value: match.toolName.is });\n }\n if (match.params) {\n for (const [key, cond] of Object.entries(match.params)) {\n if (\"is\" in cond) conditions.push({ path: `params.${key}`, value: cond.is });\n }\n }\n if (match.result) {\n for (const [key, cond] of Object.entries(match.result)) {\n if (\"is\" in cond) conditions.push({ path: `result.${key}`, value: cond.is });\n }\n }\n return conditions;\n}\n\n/**\n * Check whether a match condition has a condition on a given field path.\n */\nfunction hasConditionOnField(match: MatchCondition, path: string): boolean {\n if (path === \"toolName\") return !!match.toolName;\n if (path.startsWith(\"params.\")) return !!match.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return !!match.result?.[path.slice(7)];\n return false;\n}\n\n/**\n * Get the value from a TaiasContext for a given field path.\n */\nfunction getContextValue(ctx: TaiasContext, path: string): unknown {\n if (path === \"toolName\") return ctx.toolName;\n if (path.startsWith(\"params.\")) return ctx.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return ctx.result?.[path.slice(7)];\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Step access helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Get the match condition from a FlowStep.\n *\n * - Logic-based steps: match comes from statement.match\n * - Handler-based steps (backwards compatibility): match is stored directly on the step\n */\nfunction getMatch(step: FlowStep): MatchCondition {\n return step.kind === \"logic\" ? step.statement.match : step.match;\n}\n\n/**\n * Serialize a MatchCondition into a stable string for duplicate detection.\n */\nfunction serializeMatch(match: MatchCondition): string {\n return JSON.stringify(match);\n}\n\n/**\n * createTaias constructs a decision engine.\n *\n * Taias resolves context into a generalized Decision object,\n * and then manifests that decision into concrete affordances:\n *\n * - LLM guidance (advice)\n * - UI affordance selections\n *\n * Flow logic is expressed as logic statements -- structured data that\n * Taias understands. (Handler functions remain as a backwards-compatible\n * escape hatch.)\n *\n * Flow logic determines *what should happen next*.\n * UI affordances determine *how that decision appears in the interface*.\n *\n * This file is the boundary where:\n *\n * Inputs -> Decision -> Manifestations\n *\n * are unified into a single resolve() call.\n *\n * @example\n * ```ts\n * const taias = createTaias({ flow, affordances });\n * ```\n */\nexport function createTaias<S extends string = DefaultSlots>(\n options: TaiasOptions<S>\n): Taias<S> {\n const {\n flow,\n affordances,\n devMode = false,\n onMissingStep,\n onWarn,\n } = options;\n\n const warn = onWarn ?? ((msg: string) => console.warn(msg));\n\n // Dev mode: Check for duplicate match conditions.\n if (devMode) {\n const seenKeys = new Set<string>();\n for (const step of flow.steps) {\n const key = serializeMatch(getMatch(step));\n if (seenKeys.has(key)) {\n throw new Error(\n `Taias: Duplicate match condition in flow '${flow.id}'. Each step must have a unique match condition. Duplicate: ${key}`\n );\n }\n seenKeys.add(key);\n }\n }\n\n // -----------------------------------------------------------------------\n // Build per-field indexes\n // -----------------------------------------------------------------------\n\n const fieldIndexes = new Map<string, FieldIndex>();\n const indexableStepIndices: number[] = [];\n const broadStepIndices: number[] = [];\n\n for (let i = 0; i < flow.steps.length; i++) {\n const match = getMatch(flow.steps[i]);\n const isConditions = extractIsConditions(match);\n\n if (isConditions.length === 0) {\n broadStepIndices.push(i);\n continue;\n }\n\n indexableStepIndices.push(i);\n\n for (const { path, value } of isConditions) {\n let fieldIndex = fieldIndexes.get(path);\n if (!fieldIndex) {\n fieldIndex = { valueMap: new Map(), unconstrained: [] };\n fieldIndexes.set(path, fieldIndex);\n }\n let stepList = fieldIndex.valueMap.get(value);\n if (!stepList) {\n stepList = [];\n fieldIndex.valueMap.set(value, stepList);\n }\n stepList.push(i);\n }\n }\n\n // Build unconstrained sets: for each field index, find indexable steps\n // that don't have a condition on that field.\n for (const [fieldPath, fieldIndex] of fieldIndexes) {\n for (const i of indexableStepIndices) {\n if (!hasConditionOnField(getMatch(flow.steps[i]), fieldPath)) {\n fieldIndex.unconstrained.push(i);\n }\n }\n }\n\n const hasBroadSteps = broadStepIndices.length > 0;\n\n // Build affordance index once (if provided)\n const registryIndex = buildRegistryIndex<S>(affordances);\n\n return {\n async resolve(ctx: TaiasContext): Promise<Affordances<S> | null> {\n let matchedStep: FlowStep | undefined;\n\n // Phase 1: Find candidates from per-field indexes via intersection\n const applicableFieldPaths: string[] = [];\n for (const fieldPath of fieldIndexes.keys()) {\n const ctxValue = getContextValue(ctx, fieldPath);\n if (ctxValue !== undefined) {\n applicableFieldPaths.push(fieldPath);\n }\n }\n\n if (applicableFieldPaths.length > 0) {\n // Build candidate sets for each applicable field and intersect\n let candidates: Set<number> | null = null;\n\n for (const fieldPath of applicableFieldPaths) {\n const fieldIndex = fieldIndexes.get(fieldPath)!;\n const ctxValue = getContextValue(ctx, fieldPath);\n\n const fieldCandidates = new Set<number>();\n\n // Indexed matches for this field's value\n const indexed = fieldIndex.valueMap.get(ctxValue);\n if (indexed) {\n for (const idx of indexed) fieldCandidates.add(idx);\n }\n\n // Unconstrained steps (don't care about this field)\n for (const idx of fieldIndex.unconstrained) {\n fieldCandidates.add(idx);\n }\n\n if (candidates === null) {\n candidates = fieldCandidates;\n } else {\n // Intersect\n for (const idx of candidates) {\n if (!fieldCandidates.has(idx)) candidates.delete(idx);\n }\n }\n }\n\n // Evaluate full conditions on narrowed candidates (definition order)\n if (candidates && candidates.size > 0) {\n const sorted = [...candidates].sort((a, b) => a - b);\n for (const idx of sorted) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n } else if (indexableStepIndices.length > 0) {\n // Context has no fields that match any index -- evaluate all\n // indexable steps that are unconstrained on everything\n // (i.e., steps with is conditions on fields not present in context).\n // evaluateMatch handles this correctly.\n for (const idx of indexableStepIndices) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n\n // Phase 2: If no indexed match, evaluate broad steps\n if (!matchedStep && hasBroadSteps) {\n for (const idx of broadStepIndices) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n\n if (!matchedStep) {\n onMissingStep?.(ctx);\n return null;\n }\n\n // Evaluate the step based on its kind\n let result: StepDecision | null;\n\n if (matchedStep.kind === \"logic\") {\n result = matchedStep.statement.decision;\n } else {\n result = await matchedStep.handler(ctx);\n }\n\n if (!result) return null;\n\n if (devMode && result.nextTool === \"\") {\n warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);\n }\n\n const decision: Decision = { ...result };\n\n const selections = selectUiAffordances<S>(decision, registryIndex, {\n devMode,\n onWarn: warn,\n });\n\n return {\n advice: generateAdvice(result.nextTool),\n decision,\n selections,\n };\n },\n };\n}\n","import type {\n AffordanceRegistry,\n BindingInput,\n DefaultSlots,\n HandleRegistration,\n} from \"./types\";\nimport { normalizeBinding } from \"./types\";\n\n/**\n * Mapped type that creates a registration method for each slot in S.\n * This enables fully typed custom slots via generics.\n */\nexport type AffordanceRegistrar<S extends string = DefaultSlots> = {\n [K in S]: (handleId: string, bindsTo: BindingInput) => void;\n};\n\n/**\n * Define UI affordances for a widget using a builder pattern.\n *\n * @example Default slots (backwards compatible)\n * ```ts\n * const affordances = defineAffordances((r) => {\n * r.primaryCta(\"cta.recommend\", { toolName: \"get_recommendations\" });\n * r.widgetVariant(\"variant.discovery\", { toolName: \"get_recommendations\" });\n * });\n * ```\n *\n * @example Custom slots (fully type-safe)\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\" | \"headerStyle\";\n * const affordances = defineAffordances<MySlots>((r) => {\n * r.primaryCta(\"cta.create\", { toolName: \"createUser\" });\n * r.contentArea(\"content.form\", { key: \"contentArea\", value: \"email-form\" });\n * r.headerStyle(\"header.progress\", { key: \"headerStyle\", value: \"step-1\" });\n * });\n * ```\n */\nexport function defineAffordances<S extends string = DefaultSlots>(\n builder: (r: AffordanceRegistrar<S>) => void\n): AffordanceRegistry<S> {\n const handles: HandleRegistration<S>[] = [];\n\n // Proxy creates methods on-the-fly for any slot name.\n // TypeScript ensures only valid slot names (from S) are called.\n const registrar = new Proxy({} as AffordanceRegistrar<S>, {\n get(_, slot: string) {\n return (handleId: string, bindsTo: BindingInput) => {\n handles.push({\n slot: slot as S,\n handleId,\n bindsTo: normalizeBinding(bindsTo),\n });\n };\n },\n });\n\n builder(registrar);\n return { handles };\n}\n","import type { AffordanceRegistry, DefaultSlots } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\nexport type MergeAffordancesOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Merge multiple affordance registries into one.\n * Generic over slot type S for custom slot support.\n *\n * @example Default slots\n * ```ts\n * const merged = mergeAffordances([widgetA, widgetB]);\n * ```\n *\n * @example Custom slots\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\";\n * const merged = mergeAffordances<MySlots>([widgetA, widgetB]);\n * ```\n */\nexport function mergeAffordances<S extends string = DefaultSlots>(\n registries: AffordanceRegistry<S>[],\n opts: MergeAffordancesOptions = {}\n): AffordanceRegistry<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const merged: AffordanceRegistry<S> = { handles: registries.flatMap((r) => r.handles) };\n\n if (!devMode) return merged;\n\n // Check for duplicate handleIds\n const seenHandleIds = new Set<string>();\n for (const h of merged.handles) {\n if (seenHandleIds.has(h.handleId)) {\n throw new Error(`[Taias] Duplicate handleId \"${h.handleId}\"`);\n }\n seenHandleIds.add(h.handleId);\n }\n\n // Check for ambiguous bindings (same slot + key + value)\n const seenTriples = new Set<string>();\n for (const h of merged.handles) {\n const k = makeBindingKey(h.slot, h.bindsTo);\n if (seenTriples.has(k)) {\n throw new Error(\n `[Taias] Ambiguous affordance: slot \"${h.slot}\" has multiple handles bound to (${h.bindsTo.key}=\"${h.bindsTo.value}\")`\n );\n }\n seenTriples.add(k);\n }\n\n warn(`[Taias] Loaded ${merged.handles.length} UI affordance handles`);\n return merged;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/flow.ts","../src/uiAffordances/types.ts","../src/uiAffordances/indexing.ts","../src/uiAffordances/select.ts","../src/debugSubscriber.ts","../src/createTaias.ts","../src/uiAffordances/defineAffordances.ts","../src/uiAffordances/mergeAffordances.ts"],"sourcesContent":["// Main exports\nexport { defineFlow } from \"./flow\";\nexport { createTaias } from \"./createTaias\";\n\n// Observability exports\nexport { createDebugSubscriber } from \"./debugSubscriber\";\n\n// UI affordances exports\nexport { defineAffordances } from \"./uiAffordances/defineAffordances\";\nexport { mergeAffordances } from \"./uiAffordances/mergeAffordances\";\nexport type { AffordanceRegistrar } from \"./uiAffordances/defineAffordances\";\nexport type {\n DefaultSlots,\n CanonicalSlot,\n Binding,\n BindingInput,\n HandleRegistration,\n Selection,\n UiSelections,\n AffordanceRegistry,\n} from \"./uiAffordances/types\";\n\n// Core + flow type exports\nexport type {\n Condition,\n Decision,\n TaiasContext,\n StepDecision,\n Affordances,\n StepHandler,\n StepInput,\n MatchCondition,\n LogicStatement,\n FlowStep,\n FlowDefinition,\n FlowBuilder,\n TaiasOptions,\n Taias,\n DebugOptions,\n ResolveEvent,\n ResolveTrace,\n StepEvaluation,\n TaiasEventMap,\n} from \"./types\";\n","import type { FlowBuilder, FlowDefinition, FlowStep, MatchCondition, StepInput } from \"./types\";\n\n/**\n * Define a flow with its steps.\n *\n * @param flowId - Unique identifier for the flow\n * @param builder - Callback that receives a FlowBuilder to define steps\n * @returns A FlowDefinition object\n *\n * @example Logic statement matching on toolName\n * ```ts\n * const onboardRepoFlow = defineFlow(\"onboard_repo\", (flow) => {\n * flow.step({ toolName: { is: \"scan_repo\" } }, { nextTool: \"configure_app\" });\n * });\n * ```\n *\n * @example Matching on params and result\n * ```ts\n * flow.step(\n * { toolName: { is: \"scan_repo\" }, params: { language: { is: \"python\" } } },\n * { nextTool: \"configure_python\" },\n * );\n * flow.step(\n * { result: { hasConfig: { is: true } } },\n * { nextTool: \"review_config\" },\n * );\n * ```\n *\n * @example isNot operator\n * ```ts\n * flow.step({ toolName: { isNot: \"abort_session\" } }, { nextTool: \"continue_flow\" });\n * ```\n */\nexport function defineFlow(\n flowId: string,\n builder: (flow: FlowBuilder) => void\n): FlowDefinition {\n const steps: FlowStep[] = [];\n\n const flowBuilder: FlowBuilder = {\n step(match: MatchCondition, input: StepInput): void {\n if (typeof input === \"function\") {\n steps.push({ kind: \"handler\", match, handler: input });\n } else {\n steps.push({\n kind: \"logic\",\n statement: {\n match,\n decision: input,\n },\n });\n }\n },\n };\n\n builder(flowBuilder);\n\n return {\n id: flowId,\n steps,\n };\n}\n","/**\n * Default slots for backwards compatibility.\n * Users can define custom slots by passing a type parameter.\n */\nexport type DefaultSlots = \"primaryCta\" | \"secondaryCta\" | \"widgetVariant\";\n\n/**\n * Alias for backwards compatibility in exports.\n * @deprecated Use DefaultSlots or define your own slot type\n */\nexport type CanonicalSlot = DefaultSlots;\n\nexport type Binding = {\n key: string;\n value: string;\n};\n\n/**\n * Input format for registering affordance bindings.\n * - { toolName } is shorthand for { key: \"nextTool\", value: toolName }\n * - { key, value } is the generalized form for custom bindings\n */\nexport type BindingInput = { toolName: string } | { key: string; value: string };\n\n/**\n * A registered UI affordance handle.\n * Generic over slot type S for custom slot support.\n */\nexport type HandleRegistration<S extends string = DefaultSlots> = {\n slot: S;\n handleId: string;\n bindsTo: Binding;\n};\n\nexport type Selection = {\n handleId: string;\n bindsTo: Binding;\n};\n\n/**\n * UI selections keyed by slot name.\n * Generic over slot type S for custom slot support.\n */\nexport type UiSelections<S extends string = DefaultSlots> = Partial<Record<S, Selection>>;\n\n/**\n * Collection of registered handles.\n * Generic over slot type S for custom slot support.\n */\nexport type AffordanceRegistry<S extends string = DefaultSlots> = {\n handles: HandleRegistration<S>[];\n};\n\nexport function normalizeBinding(input: BindingInput): Binding {\n if (\"toolName\" in input) return { key: \"nextTool\", value: input.toolName };\n return { key: input.key, value: input.value };\n}\n\n/**\n * Creates a binding key for indexing.\n * Accepts any string for slot to support custom slots.\n */\nexport function makeBindingKey(slot: string, binding: Binding): string {\n return `${slot}::${binding.key}::${binding.value}`;\n}\n","import type { AffordanceRegistry, DefaultSlots, HandleRegistration } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\n/**\n * Index structure for efficient affordance lookup.\n * Generic over slot type S for custom slot support.\n */\nexport type RegistryIndex<S extends string = DefaultSlots> = {\n byBindingKey: Map<string, HandleRegistration<S>>;\n slots: Set<S>;\n /** Inferred decision field for each slot, derived from handle bindings. */\n slotKeyMap: Map<S, string>;\n};\n\n/**\n * Build an index from a registry for efficient lookup during selection.\n * Tracks which slots have registered handles and infers the decision field\n * for each slot from its handle bindings.\n *\n * @throws Error if handles for the same slot bind to different keys\n */\nexport function buildRegistryIndex<S extends string = DefaultSlots>(\n registry?: AffordanceRegistry<S>\n): RegistryIndex<S> {\n const byBindingKey = new Map<string, HandleRegistration<S>>();\n const slots = new Set<S>();\n const slotKeyMap = new Map<S, string>();\n\n if (!registry) return { byBindingKey, slots, slotKeyMap };\n\n for (const h of registry.handles) {\n slots.add(h.slot);\n\n // Infer and validate slot key\n const existingKey = slotKeyMap.get(h.slot);\n if (existingKey && existingKey !== h.bindsTo.key) {\n throw new Error(\n `[Taias] Slot \"${h.slot}\" has handles bound to different keys: \"${existingKey}\" and \"${h.bindsTo.key}\". ` +\n `All handles for a slot must use the same decision field.`\n );\n }\n slotKeyMap.set(h.slot, h.bindsTo.key);\n\n byBindingKey.set(makeBindingKey(h.slot, h.bindsTo), h);\n }\n\n return { byBindingKey, slots, slotKeyMap };\n}\n","import type { Decision } from \"../types\";\nimport type { DefaultSlots, UiSelections } from \"./types\";\nimport type { RegistryIndex } from \"./indexing\";\nimport { makeBindingKey } from \"./types\";\n\nexport type SelectOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Select UI affordances based on flow decision.\n * Uses the inferred decision field for each slot from the registry index.\n */\nexport function selectUiAffordances<S extends string = DefaultSlots>(\n decision: Decision,\n index: RegistryIndex<S>,\n opts: SelectOptions = {}\n): UiSelections<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const selections: UiSelections<S> = {};\n\n for (const slot of index.slots) {\n // Use inferred key from handle bindings\n const field = index.slotKeyMap.get(slot);\n if (!field) continue;\n\n const value = decision[field];\n if (!value) continue;\n\n const k = makeBindingKey(slot, { key: field, value });\n const handle = index.byBindingKey.get(k);\n\n if (!handle) {\n if (devMode) warn(`[Taias] No affordance for slot \"${slot}\" when ${field}=\"${value}\"`);\n continue;\n }\n\n (selections as Record<string, unknown>)[slot] = {\n handleId: handle.handleId,\n bindsTo: handle.bindsTo,\n };\n }\n\n return selections;\n}\n","import type { ResolveEvent, TaiasContext, DebugOptions } from \"./types\";\n\nexport type DebugSubscriberOptions = DebugOptions;\n\nfunction formatContext(context: TaiasContext): string {\n const parts: string[] = [];\n if (context.toolName) parts.push(`toolName=${context.toolName}`);\n if (context.params) parts.push(`params=${JSON.stringify(context.params)}`);\n if (context.result) parts.push(`result=${JSON.stringify(context.result)}`);\n return parts.length > 0 ? parts.join(\", \") : \"(empty)\";\n}\n\nfunction formatContextLabel(context: TaiasContext): string {\n if (context.toolName) return context.toolName;\n if (context.params) return `params:${JSON.stringify(context.params)}`;\n if (context.result) return `result:${JSON.stringify(context.result)}`;\n return \"(empty)\";\n}\n\n/**\n * Create a debug subscriber for Taias resolve events.\n *\n * Returns a handler function suitable for `taias.on(\"resolve\", handler)`.\n *\n * - \"default\" format: multi-line breakdown of context, trace, decision, and affordances.\n * - \"compact\" format: single line per resolve call.\n */\nexport function createDebugSubscriber(\n options?: DebugSubscriberOptions\n): (event: ResolveEvent) => void {\n const format = options?.format ?? \"default\";\n const log = options?.logger ?? console.log;\n\n if (format === \"compact\") {\n return (event: ResolveEvent) => {\n const { trace, context } = event;\n const label = formatContextLabel(context);\n\n if (!trace.matched) {\n log(`[Taias] ${label} → no match (${trace.candidatesEvaluated} evaluated)`);\n return;\n }\n\n const decisionSummary = event.decision\n ? Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(\", \")\n : \"null\";\n\n log(\n `[Taias] ${label} → ${decisionSummary} (${trace.phase}, step ${trace.matchedStepIndex}, ${trace.candidatesEvaluated} evaluated)`\n );\n };\n }\n\n return (event: ResolveEvent) => {\n const { trace, context } = event;\n const lines: string[] = [];\n\n lines.push(\"┌─ Taias Resolve ─────────────────────────────\");\n lines.push(`│ Flow: ${event.flowId}`);\n lines.push(`│ Context: ${formatContext(context)}`);\n\n lines.push(\"│\");\n\n if (!trace.matched) {\n lines.push(\"│ Result: NO MATCH\");\n lines.push(`│ Candidates evaluated: ${trace.candidatesEvaluated}`);\n } else {\n lines.push(`│ Matched: step ${trace.matchedStepIndex} (${trace.matchedStepKind})`);\n lines.push(`│ Phase: ${trace.phase}`);\n\n if (trace.resolutionPath.length > 0) {\n lines.push(`│ Resolution path: ${trace.resolutionPath.join(\" → \")}`);\n }\n\n lines.push(`│ Candidates evaluated: ${trace.candidatesEvaluated}`);\n\n if (trace.matchedStepMatch) {\n lines.push(`│ Match condition: ${JSON.stringify(trace.matchedStepMatch)}`);\n }\n }\n\n lines.push(\"│\");\n\n if (event.decision) {\n const fields = Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(\", \");\n lines.push(`│ Decision: ${fields}`);\n } else {\n lines.push(\"│ Decision: null\");\n }\n\n if (event.affordances) {\n lines.push(`│ Advice: ${event.affordances.advice.slice(0, 80)}${event.affordances.advice.length > 80 ? \"...\" : \"\"}`);\n }\n\n lines.push(`│ Duration: ${event.durationMs.toFixed(2)}ms`);\n\n if (trace.evaluations) {\n lines.push(\"│\");\n lines.push(\"│ Detailed evaluations:\");\n for (const evaluation of trace.evaluations) {\n const icon = evaluation.result === \"matched\" ? \"✓\" : \"✗\";\n lines.push(`│ ${icon} step ${evaluation.stepIndex}: ${evaluation.result}`);\n\n if (evaluation.fieldResults) {\n for (const [field, fr] of Object.entries(evaluation.fieldResults)) {\n const fieldIcon = fr.passed ? \"✓\" : \"✗\";\n lines.push(`│ ${fieldIcon} ${field}: ${JSON.stringify(fr.condition)} vs ${JSON.stringify(fr.actual)}`);\n }\n }\n }\n }\n\n lines.push(\"└──────────────────────────────────────────────\");\n\n log(lines.join(\"\\n\"));\n };\n}\n","import type { Affordances, Condition, FlowStep, MatchCondition, StepDecision, Taias, TaiasContext, TaiasOptions, Decision, ResolveEvent, ResolveTrace, StepEvaluation } from \"./types\";\nimport type { DefaultSlots } from \"./uiAffordances/types\";\nimport { buildRegistryIndex } from \"./uiAffordances/indexing\";\nimport { selectUiAffordances } from \"./uiAffordances/select\";\nimport { createDebugSubscriber } from \"./debugSubscriber\";\n\n/**\n * Generate advice text for a given next tool.\n */\nfunction generateAdvice(nextTool: string): string {\n return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;\n}\n\n// ---------------------------------------------------------------------------\n// Condition evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a single Condition against a value using strict equality.\n */\nfunction evaluateCondition(condition: Condition, value: unknown): boolean {\n if (\"is\" in condition) return value === condition.is;\n if (\"isNot\" in condition) return value !== condition.isNot;\n return false;\n}\n\n/**\n * Evaluate a full MatchCondition against a TaiasContext.\n * All present conditions must be satisfied (logical AND).\n *\n * - toolName: compared directly against ctx.toolName\n * - params: subset match -- each specified key is checked against ctx.params\n * - result: subset match -- each specified key is checked against ctx.result\n *\n * If a step specifies params/result conditions but the context doesn't\n * include params/result, the step does not match.\n */\nfunction evaluateMatch(match: MatchCondition, ctx: TaiasContext): boolean {\n if (match.toolName && !evaluateCondition(match.toolName, ctx.toolName)) return false;\n\n if (match.params) {\n if (!ctx.params) return false;\n for (const [key, condition] of Object.entries(match.params)) {\n if (!evaluateCondition(condition, ctx.params[key])) return false;\n }\n }\n\n if (match.result) {\n if (!ctx.result) return false;\n for (const [key, condition] of Object.entries(match.result)) {\n if (!evaluateCondition(condition, ctx.result[key])) return false;\n }\n }\n\n return true;\n}\n\n/**\n * Evaluate a full MatchCondition and return per-field breakdown.\n * Used when detailed tracing is enabled.\n */\nfunction evaluateMatchDetailed(match: MatchCondition, ctx: TaiasContext): {\n passed: boolean;\n fieldResults: Record<string, { condition: Condition; actual: unknown; passed: boolean }>;\n} {\n const fieldResults: Record<string, { condition: Condition; actual: unknown; passed: boolean }> = {};\n let allPassed = true;\n\n if (match.toolName) {\n const actual = ctx.toolName;\n const passed = evaluateCondition(match.toolName, actual);\n fieldResults[\"toolName\"] = { condition: match.toolName, actual, passed };\n if (!passed) allPassed = false;\n }\n\n if (match.params) {\n if (!ctx.params) {\n for (const [key, condition] of Object.entries(match.params)) {\n fieldResults[`params.${key}`] = { condition, actual: undefined, passed: false };\n }\n allPassed = false;\n } else {\n for (const [key, condition] of Object.entries(match.params)) {\n const actual = ctx.params[key];\n const passed = evaluateCondition(condition, actual);\n fieldResults[`params.${key}`] = { condition, actual, passed };\n if (!passed) allPassed = false;\n }\n }\n }\n\n if (match.result) {\n if (!ctx.result) {\n for (const [key, condition] of Object.entries(match.result)) {\n fieldResults[`result.${key}`] = { condition, actual: undefined, passed: false };\n }\n allPassed = false;\n } else {\n for (const [key, condition] of Object.entries(match.result)) {\n const actual = ctx.result[key];\n const passed = evaluateCondition(condition, actual);\n fieldResults[`result.${key}`] = { condition, actual, passed };\n if (!passed) allPassed = false;\n }\n }\n }\n\n return { passed: allPassed, fieldResults };\n}\n\n// ---------------------------------------------------------------------------\n// Per-field indexing\n// ---------------------------------------------------------------------------\n\ninterface FieldIndex {\n valueMap: Map<unknown, number[]>;\n unconstrained: number[];\n}\n\n/**\n * Extract all `is` conditions from a match condition as field-path / value pairs.\n */\nfunction extractIsConditions(match: MatchCondition): Array<{ path: string; value: unknown }> {\n const conditions: Array<{ path: string; value: unknown }> = [];\n if (match.toolName && \"is\" in match.toolName) {\n conditions.push({ path: \"toolName\", value: match.toolName.is });\n }\n if (match.params) {\n for (const [key, cond] of Object.entries(match.params)) {\n if (\"is\" in cond) conditions.push({ path: `params.${key}`, value: cond.is });\n }\n }\n if (match.result) {\n for (const [key, cond] of Object.entries(match.result)) {\n if (\"is\" in cond) conditions.push({ path: `result.${key}`, value: cond.is });\n }\n }\n return conditions;\n}\n\n/**\n * Check whether a match condition has a condition on a given field path.\n */\nfunction hasConditionOnField(match: MatchCondition, path: string): boolean {\n if (path === \"toolName\") return !!match.toolName;\n if (path.startsWith(\"params.\")) return !!match.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return !!match.result?.[path.slice(7)];\n return false;\n}\n\n/**\n * Get the value from a TaiasContext for a given field path.\n */\nfunction getContextValue(ctx: TaiasContext, path: string): unknown {\n if (path === \"toolName\") return ctx.toolName;\n if (path.startsWith(\"params.\")) return ctx.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return ctx.result?.[path.slice(7)];\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Step access helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Get the match condition from a FlowStep.\n *\n * - Logic-based steps: match comes from statement.match\n * - Handler-based steps (backwards compatibility): match is stored directly on the step\n */\nfunction getMatch(step: FlowStep): MatchCondition {\n return step.kind === \"logic\" ? step.statement.match : step.match;\n}\n\n/**\n * Serialize a MatchCondition into a stable string for duplicate detection.\n */\nfunction serializeMatch(match: MatchCondition): string {\n return JSON.stringify(match);\n}\n\n/**\n * createTaias constructs a decision engine.\n *\n * Taias resolves context into a generalized Decision object,\n * and then manifests that decision into concrete affordances:\n *\n * - LLM guidance (advice)\n * - UI affordance selections\n *\n * Flow logic is expressed as logic statements -- structured data that\n * Taias understands. (Handler functions remain as a backwards-compatible\n * escape hatch.)\n *\n * Flow logic determines *what should happen next*.\n * UI affordances determine *how that decision appears in the interface*.\n *\n * This file is the boundary where:\n *\n * Inputs -> Decision -> Manifestations\n *\n * are unified into a single resolve() call.\n *\n * @example\n * ```ts\n * const taias = createTaias({ flow, affordances });\n * ```\n */\nexport function createTaias<S extends string = DefaultSlots>(\n options: TaiasOptions<S>\n): Taias<S> {\n const {\n flow,\n affordances,\n devMode = false,\n debug = false,\n tracing = \"summary\",\n onMissingStep,\n onWarn,\n } = options;\n\n const warn = onWarn ?? ((msg: string) => console.warn(msg));\n const detailed = tracing === \"detailed\";\n\n // Dev mode: Check for duplicate match conditions.\n if (devMode) {\n const seenKeys = new Set<string>();\n for (const step of flow.steps) {\n const key = serializeMatch(getMatch(step));\n if (seenKeys.has(key)) {\n throw new Error(\n `Taias: Duplicate match condition in flow '${flow.id}'. Each step must have a unique match condition. Duplicate: ${key}`\n );\n }\n seenKeys.add(key);\n }\n }\n\n // -----------------------------------------------------------------------\n // Build per-field indexes\n // -----------------------------------------------------------------------\n\n const fieldIndexes = new Map<string, FieldIndex>();\n const indexableStepIndices: number[] = [];\n const broadStepIndices: number[] = [];\n\n for (let i = 0; i < flow.steps.length; i++) {\n const match = getMatch(flow.steps[i]);\n const isConditions = extractIsConditions(match);\n\n if (isConditions.length === 0) {\n broadStepIndices.push(i);\n continue;\n }\n\n indexableStepIndices.push(i);\n\n for (const { path, value } of isConditions) {\n let fieldIndex = fieldIndexes.get(path);\n if (!fieldIndex) {\n fieldIndex = { valueMap: new Map(), unconstrained: [] };\n fieldIndexes.set(path, fieldIndex);\n }\n let stepList = fieldIndex.valueMap.get(value);\n if (!stepList) {\n stepList = [];\n fieldIndex.valueMap.set(value, stepList);\n }\n stepList.push(i);\n }\n }\n\n // Build unconstrained sets: for each field index, find indexable steps\n // that don't have a condition on that field.\n for (const [fieldPath, fieldIndex] of fieldIndexes) {\n for (const i of indexableStepIndices) {\n if (!hasConditionOnField(getMatch(flow.steps[i]), fieldPath)) {\n fieldIndex.unconstrained.push(i);\n }\n }\n }\n\n const hasBroadSteps = broadStepIndices.length > 0;\n\n // Build affordance index once (if provided)\n const registryIndex = buildRegistryIndex<S>(affordances);\n\n // -----------------------------------------------------------------------\n // Event emitter\n // -----------------------------------------------------------------------\n\n const listeners = new Map<string, Set<Function>>();\n\n function emit<E extends string>(event: E, data: unknown): void {\n const handlers = listeners.get(event);\n if (handlers) {\n for (const handler of handlers) handler(data);\n }\n }\n\n function on(event: string, handler: Function): void {\n let set = listeners.get(event);\n if (!set) {\n set = new Set();\n listeners.set(event, set);\n }\n set.add(handler);\n }\n\n function off(event: string, handler: Function): void {\n listeners.get(event)?.delete(handler);\n }\n\n if (debug) {\n const debugOpts = typeof debug === \"object\" ? debug : undefined;\n on(\"resolve\", createDebugSubscriber(debugOpts));\n }\n\n return {\n async resolve(ctx: TaiasContext): Promise<Affordances<S> | null> {\n const startTime = performance.now();\n const timestamp = Date.now();\n\n let matchedStep: FlowStep | undefined;\n let matchedStepIndex: number | null = null;\n let matchPhase: \"indexed\" | \"broad\" | null = null;\n const resolutionPath: string[] = [];\n let candidatesEvaluated = 0;\n const evaluations: StepEvaluation[] | undefined = detailed ? [] : undefined;\n\n // Evaluate a candidate step and track it for tracing.\n // In detailed mode, uses evaluateMatchDetailed to capture per-field\n // outcomes; in summary mode, uses the cheaper evaluateMatch.\n function tryMatch(idx: number): boolean {\n candidatesEvaluated++;\n const step = flow.steps[idx];\n const match = getMatch(step);\n\n if (detailed) {\n const { passed, fieldResults } = evaluateMatchDetailed(match, ctx);\n evaluations!.push({\n stepIndex: idx,\n match,\n result: passed ? \"matched\" : \"no-match\",\n fieldResults,\n });\n return passed;\n }\n\n return evaluateMatch(match, ctx);\n }\n\n // Phase 1: Find candidates from per-field indexes via intersection\n const applicableFieldPaths: string[] = [];\n for (const fieldPath of fieldIndexes.keys()) {\n const ctxValue = getContextValue(ctx, fieldPath);\n if (ctxValue !== undefined) {\n applicableFieldPaths.push(fieldPath);\n }\n }\n\n resolutionPath.push(...applicableFieldPaths);\n\n if (applicableFieldPaths.length > 0) {\n // Build candidate sets for each applicable field and intersect\n let candidates: Set<number> | null = null;\n\n for (const fieldPath of applicableFieldPaths) {\n const fieldIndex = fieldIndexes.get(fieldPath)!;\n const ctxValue = getContextValue(ctx, fieldPath);\n\n const fieldCandidates = new Set<number>();\n\n // Indexed matches for this field's value\n const indexed = fieldIndex.valueMap.get(ctxValue);\n if (indexed) {\n for (const idx of indexed) fieldCandidates.add(idx);\n }\n\n // Unconstrained steps (don't care about this field)\n for (const idx of fieldIndex.unconstrained) {\n fieldCandidates.add(idx);\n }\n\n if (candidates === null) {\n candidates = fieldCandidates;\n } else {\n // Intersect\n for (const idx of candidates) {\n if (!fieldCandidates.has(idx)) candidates.delete(idx);\n }\n }\n }\n\n // Evaluate full conditions on narrowed candidates (definition order)\n if (candidates && candidates.size > 0) {\n const sorted = [...candidates].sort((a, b) => a - b);\n for (const idx of sorted) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"indexed\";\n break;\n }\n }\n }\n } else if (indexableStepIndices.length > 0) {\n // Context has no fields that match any index -- evaluate all\n // indexable steps that are unconstrained on everything\n // (i.e., steps with is conditions on fields not present in context).\n // evaluateMatch handles this correctly.\n for (const idx of indexableStepIndices) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"indexed\";\n break;\n }\n }\n }\n\n // Phase 2: If no indexed match, evaluate broad steps\n if (!matchedStep && hasBroadSteps) {\n for (const idx of broadStepIndices) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"broad\";\n break;\n }\n }\n }\n\n // Construct the trace and emit event on every exit path (no-match,\n // handler-returns-null, and successful match). Subscribers always\n // get complete visibility into every resolve() call.\n const trace: ResolveTrace = {\n matched: !!matchedStep,\n matchedStepIndex,\n matchedStepKind: matchedStep?.kind ?? null,\n matchedStepMatch: matchedStep ? getMatch(matchedStep) : null,\n phase: matchPhase,\n resolutionPath,\n candidatesEvaluated,\n ...(evaluations !== undefined ? { evaluations } : {}),\n };\n\n if (!matchedStep) {\n onMissingStep?.(ctx);\n\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision: null,\n affordances: null,\n } satisfies ResolveEvent<S>);\n\n return null;\n }\n\n // Evaluate the step based on its kind\n let result: StepDecision | null;\n\n if (matchedStep.kind === \"logic\") {\n result = matchedStep.statement.decision;\n } else {\n result = await matchedStep.handler(ctx);\n }\n\n if (!result) {\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision: null,\n affordances: null,\n } satisfies ResolveEvent<S>);\n\n return null;\n }\n\n if (devMode && result.nextTool === \"\") {\n warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);\n }\n\n const decision: Decision = { ...result };\n\n const selections = selectUiAffordances<S>(decision, registryIndex, {\n devMode,\n onWarn: warn,\n });\n\n const advice = generateAdvice(result.nextTool);\n\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision,\n affordances: { advice, selections },\n } satisfies ResolveEvent<S>);\n\n return { advice, decision, selections };\n },\n\n on: on as Taias<S>[\"on\"],\n off: off as Taias<S>[\"off\"],\n };\n}\n","import type {\n AffordanceRegistry,\n BindingInput,\n DefaultSlots,\n HandleRegistration,\n} from \"./types\";\nimport { normalizeBinding } from \"./types\";\n\n/**\n * Mapped type that creates a registration method for each slot in S.\n * This enables fully typed custom slots via generics.\n */\nexport type AffordanceRegistrar<S extends string = DefaultSlots> = {\n [K in S]: (handleId: string, bindsTo: BindingInput) => void;\n};\n\n/**\n * Define UI affordances for a widget using a builder pattern.\n *\n * @example Default slots (backwards compatible)\n * ```ts\n * const affordances = defineAffordances((r) => {\n * r.primaryCta(\"cta.recommend\", { toolName: \"get_recommendations\" });\n * r.widgetVariant(\"variant.discovery\", { toolName: \"get_recommendations\" });\n * });\n * ```\n *\n * @example Custom slots (fully type-safe)\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\" | \"headerStyle\";\n * const affordances = defineAffordances<MySlots>((r) => {\n * r.primaryCta(\"cta.create\", { toolName: \"createUser\" });\n * r.contentArea(\"content.form\", { key: \"contentArea\", value: \"email-form\" });\n * r.headerStyle(\"header.progress\", { key: \"headerStyle\", value: \"step-1\" });\n * });\n * ```\n */\nexport function defineAffordances<S extends string = DefaultSlots>(\n builder: (r: AffordanceRegistrar<S>) => void\n): AffordanceRegistry<S> {\n const handles: HandleRegistration<S>[] = [];\n\n // Proxy creates methods on-the-fly for any slot name.\n // TypeScript ensures only valid slot names (from S) are called.\n const registrar = new Proxy({} as AffordanceRegistrar<S>, {\n get(_, slot: string) {\n return (handleId: string, bindsTo: BindingInput) => {\n handles.push({\n slot: slot as S,\n handleId,\n bindsTo: normalizeBinding(bindsTo),\n });\n };\n },\n });\n\n builder(registrar);\n return { handles };\n}\n","import type { AffordanceRegistry, DefaultSlots } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\nexport type MergeAffordancesOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Merge multiple affordance registries into one.\n * Generic over slot type S for custom slot support.\n *\n * @example Default slots\n * ```ts\n * const merged = mergeAffordances([widgetA, widgetB]);\n * ```\n *\n * @example Custom slots\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\";\n * const merged = mergeAffordances<MySlots>([widgetA, widgetB]);\n * ```\n */\nexport function mergeAffordances<S extends string = DefaultSlots>(\n registries: AffordanceRegistry<S>[],\n opts: MergeAffordancesOptions = {}\n): AffordanceRegistry<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const merged: AffordanceRegistry<S> = { handles: registries.flatMap((r) => r.handles) };\n\n if (!devMode) return merged;\n\n // Check for duplicate handleIds\n const seenHandleIds = new Set<string>();\n for (const h of merged.handles) {\n if (seenHandleIds.has(h.handleId)) {\n throw new Error(`[Taias] Duplicate handleId \"${h.handleId}\"`);\n }\n seenHandleIds.add(h.handleId);\n }\n\n // Check for ambiguous bindings (same slot + key + value)\n const seenTriples = new Set<string>();\n for (const h of merged.handles) {\n const k = makeBindingKey(h.slot, h.bindsTo);\n if (seenTriples.has(k)) {\n throw new Error(\n `[Taias] Ambiguous affordance: slot \"${h.slot}\" has multiple handles bound to (${h.bindsTo.key}=\"${h.bindsTo.value}\")`\n );\n }\n seenTriples.add(k);\n }\n\n warn(`[Taias] Loaded ${merged.handles.length} UI affordance handles`);\n return merged;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -181,6 +181,74 @@ type StepInput = StepHandler | StepDecision;
|
|
|
181
181
|
interface FlowBuilder {
|
|
182
182
|
step(match: MatchCondition, input: StepInput): void;
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Evaluation record for a single step during resolve().
|
|
186
|
+
* Present when tracing: "detailed" is enabled.
|
|
187
|
+
*/
|
|
188
|
+
type StepEvaluation = {
|
|
189
|
+
stepIndex: number;
|
|
190
|
+
match: MatchCondition;
|
|
191
|
+
result: "matched" | "no-match";
|
|
192
|
+
fieldResults?: Record<string, {
|
|
193
|
+
condition: Condition;
|
|
194
|
+
actual: unknown;
|
|
195
|
+
passed: boolean;
|
|
196
|
+
}>;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Trace of how resolve() reached its decision.
|
|
200
|
+
*
|
|
201
|
+
* Always includes summary information (which step matched, how it was found).
|
|
202
|
+
* When tracing: "detailed", also includes per-step evaluation breakdowns.
|
|
203
|
+
*/
|
|
204
|
+
type ResolveTrace = {
|
|
205
|
+
matched: boolean;
|
|
206
|
+
matchedStepIndex: number | null;
|
|
207
|
+
matchedStepKind: "logic" | "handler" | null;
|
|
208
|
+
matchedStepMatch: MatchCondition | null;
|
|
209
|
+
phase: "indexed" | "broad" | null;
|
|
210
|
+
resolutionPath: string[];
|
|
211
|
+
candidatesEvaluated: number;
|
|
212
|
+
evaluations?: StepEvaluation[];
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Structured event emitted on every resolve() call.
|
|
216
|
+
*
|
|
217
|
+
* Four-part structure:
|
|
218
|
+
* 1. context -- what was passed to resolve()
|
|
219
|
+
* 2. trace -- how the decision was reached
|
|
220
|
+
* 3. decision -- what was decided
|
|
221
|
+
* 4. affordances -- how the decision was manifested (advice + selections)
|
|
222
|
+
*/
|
|
223
|
+
type ResolveEvent<S extends string = DefaultSlots> = {
|
|
224
|
+
flowId: string;
|
|
225
|
+
timestamp: number;
|
|
226
|
+
durationMs: number;
|
|
227
|
+
context: TaiasContext;
|
|
228
|
+
trace: ResolveTrace;
|
|
229
|
+
decision: Decision | null;
|
|
230
|
+
affordances: {
|
|
231
|
+
advice: string;
|
|
232
|
+
selections: UiSelections<S>;
|
|
233
|
+
} | null;
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Map of event names to their event types.
|
|
237
|
+
* Extensible to future event types (e.g., "init", "error").
|
|
238
|
+
*/
|
|
239
|
+
type TaiasEventMap<S extends string = DefaultSlots> = {
|
|
240
|
+
resolve: ResolveEvent<S>;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Options for the built-in debug subscriber.
|
|
244
|
+
*
|
|
245
|
+
* - format: "default" (multi-line breakdown) or "compact" (single line).
|
|
246
|
+
* - logger: Custom log function (defaults to console.log).
|
|
247
|
+
*/
|
|
248
|
+
type DebugOptions = {
|
|
249
|
+
format?: "default" | "compact";
|
|
250
|
+
logger?: (...args: unknown[]) => void;
|
|
251
|
+
};
|
|
184
252
|
/**
|
|
185
253
|
* Options for creating a Taias instance.
|
|
186
254
|
* Generic over slot type S for custom slot support.
|
|
@@ -189,6 +257,8 @@ type TaiasOptions<S extends string = DefaultSlots> = {
|
|
|
189
257
|
flow: FlowDefinition;
|
|
190
258
|
affordances?: AffordanceRegistry<S>;
|
|
191
259
|
devMode?: boolean;
|
|
260
|
+
debug?: boolean | DebugOptions;
|
|
261
|
+
tracing?: "summary" | "detailed";
|
|
192
262
|
onMissingStep?: (ctx: TaiasContext) => void;
|
|
193
263
|
onWarn?: (msg: string) => void;
|
|
194
264
|
};
|
|
@@ -198,6 +268,8 @@ type TaiasOptions<S extends string = DefaultSlots> = {
|
|
|
198
268
|
*/
|
|
199
269
|
interface Taias<S extends string = DefaultSlots> {
|
|
200
270
|
resolve(ctx: TaiasContext): Affordances<S> | null | Promise<Affordances<S> | null>;
|
|
271
|
+
on<E extends keyof TaiasEventMap<S>>(event: E, handler: (data: TaiasEventMap<S>[E]) => void): void;
|
|
272
|
+
off<E extends keyof TaiasEventMap<S>>(event: E, handler: (data: TaiasEventMap<S>[E]) => void): void;
|
|
201
273
|
}
|
|
202
274
|
|
|
203
275
|
/**
|
|
@@ -262,6 +334,17 @@ declare function defineFlow(flowId: string, builder: (flow: FlowBuilder) => void
|
|
|
262
334
|
*/
|
|
263
335
|
declare function createTaias<S extends string = DefaultSlots>(options: TaiasOptions<S>): Taias<S>;
|
|
264
336
|
|
|
337
|
+
type DebugSubscriberOptions = DebugOptions;
|
|
338
|
+
/**
|
|
339
|
+
* Create a debug subscriber for Taias resolve events.
|
|
340
|
+
*
|
|
341
|
+
* Returns a handler function suitable for `taias.on("resolve", handler)`.
|
|
342
|
+
*
|
|
343
|
+
* - "default" format: multi-line breakdown of context, trace, decision, and affordances.
|
|
344
|
+
* - "compact" format: single line per resolve call.
|
|
345
|
+
*/
|
|
346
|
+
declare function createDebugSubscriber(options?: DebugSubscriberOptions): (event: ResolveEvent) => void;
|
|
347
|
+
|
|
265
348
|
/**
|
|
266
349
|
* Mapped type that creates a registration method for each slot in S.
|
|
267
350
|
* This enables fully typed custom slots via generics.
|
|
@@ -313,4 +396,4 @@ type MergeAffordancesOptions = {
|
|
|
313
396
|
*/
|
|
314
397
|
declare function mergeAffordances<S extends string = DefaultSlots>(registries: AffordanceRegistry<S>[], opts?: MergeAffordancesOptions): AffordanceRegistry<S>;
|
|
315
398
|
|
|
316
|
-
export { type AffordanceRegistrar, type AffordanceRegistry, type Affordances, type Binding, type BindingInput, type CanonicalSlot, type Condition, type Decision, type DefaultSlots, type FlowBuilder, type FlowDefinition, type FlowStep, type HandleRegistration, type LogicStatement, type MatchCondition, type Selection, type StepDecision, type StepHandler, type StepInput, type Taias, type TaiasContext, type TaiasOptions, type UiSelections, createTaias, defineAffordances, defineFlow, mergeAffordances };
|
|
399
|
+
export { type AffordanceRegistrar, type AffordanceRegistry, type Affordances, type Binding, type BindingInput, type CanonicalSlot, type Condition, type DebugOptions, type Decision, type DefaultSlots, type FlowBuilder, type FlowDefinition, type FlowStep, type HandleRegistration, type LogicStatement, type MatchCondition, type ResolveEvent, type ResolveTrace, type Selection, type StepDecision, type StepEvaluation, type StepHandler, type StepInput, type Taias, type TaiasContext, type TaiasEventMap, type TaiasOptions, type UiSelections, createDebugSubscriber, createTaias, defineAffordances, defineFlow, mergeAffordances };
|
package/dist/index.d.ts
CHANGED
|
@@ -181,6 +181,74 @@ type StepInput = StepHandler | StepDecision;
|
|
|
181
181
|
interface FlowBuilder {
|
|
182
182
|
step(match: MatchCondition, input: StepInput): void;
|
|
183
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Evaluation record for a single step during resolve().
|
|
186
|
+
* Present when tracing: "detailed" is enabled.
|
|
187
|
+
*/
|
|
188
|
+
type StepEvaluation = {
|
|
189
|
+
stepIndex: number;
|
|
190
|
+
match: MatchCondition;
|
|
191
|
+
result: "matched" | "no-match";
|
|
192
|
+
fieldResults?: Record<string, {
|
|
193
|
+
condition: Condition;
|
|
194
|
+
actual: unknown;
|
|
195
|
+
passed: boolean;
|
|
196
|
+
}>;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Trace of how resolve() reached its decision.
|
|
200
|
+
*
|
|
201
|
+
* Always includes summary information (which step matched, how it was found).
|
|
202
|
+
* When tracing: "detailed", also includes per-step evaluation breakdowns.
|
|
203
|
+
*/
|
|
204
|
+
type ResolveTrace = {
|
|
205
|
+
matched: boolean;
|
|
206
|
+
matchedStepIndex: number | null;
|
|
207
|
+
matchedStepKind: "logic" | "handler" | null;
|
|
208
|
+
matchedStepMatch: MatchCondition | null;
|
|
209
|
+
phase: "indexed" | "broad" | null;
|
|
210
|
+
resolutionPath: string[];
|
|
211
|
+
candidatesEvaluated: number;
|
|
212
|
+
evaluations?: StepEvaluation[];
|
|
213
|
+
};
|
|
214
|
+
/**
|
|
215
|
+
* Structured event emitted on every resolve() call.
|
|
216
|
+
*
|
|
217
|
+
* Four-part structure:
|
|
218
|
+
* 1. context -- what was passed to resolve()
|
|
219
|
+
* 2. trace -- how the decision was reached
|
|
220
|
+
* 3. decision -- what was decided
|
|
221
|
+
* 4. affordances -- how the decision was manifested (advice + selections)
|
|
222
|
+
*/
|
|
223
|
+
type ResolveEvent<S extends string = DefaultSlots> = {
|
|
224
|
+
flowId: string;
|
|
225
|
+
timestamp: number;
|
|
226
|
+
durationMs: number;
|
|
227
|
+
context: TaiasContext;
|
|
228
|
+
trace: ResolveTrace;
|
|
229
|
+
decision: Decision | null;
|
|
230
|
+
affordances: {
|
|
231
|
+
advice: string;
|
|
232
|
+
selections: UiSelections<S>;
|
|
233
|
+
} | null;
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Map of event names to their event types.
|
|
237
|
+
* Extensible to future event types (e.g., "init", "error").
|
|
238
|
+
*/
|
|
239
|
+
type TaiasEventMap<S extends string = DefaultSlots> = {
|
|
240
|
+
resolve: ResolveEvent<S>;
|
|
241
|
+
};
|
|
242
|
+
/**
|
|
243
|
+
* Options for the built-in debug subscriber.
|
|
244
|
+
*
|
|
245
|
+
* - format: "default" (multi-line breakdown) or "compact" (single line).
|
|
246
|
+
* - logger: Custom log function (defaults to console.log).
|
|
247
|
+
*/
|
|
248
|
+
type DebugOptions = {
|
|
249
|
+
format?: "default" | "compact";
|
|
250
|
+
logger?: (...args: unknown[]) => void;
|
|
251
|
+
};
|
|
184
252
|
/**
|
|
185
253
|
* Options for creating a Taias instance.
|
|
186
254
|
* Generic over slot type S for custom slot support.
|
|
@@ -189,6 +257,8 @@ type TaiasOptions<S extends string = DefaultSlots> = {
|
|
|
189
257
|
flow: FlowDefinition;
|
|
190
258
|
affordances?: AffordanceRegistry<S>;
|
|
191
259
|
devMode?: boolean;
|
|
260
|
+
debug?: boolean | DebugOptions;
|
|
261
|
+
tracing?: "summary" | "detailed";
|
|
192
262
|
onMissingStep?: (ctx: TaiasContext) => void;
|
|
193
263
|
onWarn?: (msg: string) => void;
|
|
194
264
|
};
|
|
@@ -198,6 +268,8 @@ type TaiasOptions<S extends string = DefaultSlots> = {
|
|
|
198
268
|
*/
|
|
199
269
|
interface Taias<S extends string = DefaultSlots> {
|
|
200
270
|
resolve(ctx: TaiasContext): Affordances<S> | null | Promise<Affordances<S> | null>;
|
|
271
|
+
on<E extends keyof TaiasEventMap<S>>(event: E, handler: (data: TaiasEventMap<S>[E]) => void): void;
|
|
272
|
+
off<E extends keyof TaiasEventMap<S>>(event: E, handler: (data: TaiasEventMap<S>[E]) => void): void;
|
|
201
273
|
}
|
|
202
274
|
|
|
203
275
|
/**
|
|
@@ -262,6 +334,17 @@ declare function defineFlow(flowId: string, builder: (flow: FlowBuilder) => void
|
|
|
262
334
|
*/
|
|
263
335
|
declare function createTaias<S extends string = DefaultSlots>(options: TaiasOptions<S>): Taias<S>;
|
|
264
336
|
|
|
337
|
+
type DebugSubscriberOptions = DebugOptions;
|
|
338
|
+
/**
|
|
339
|
+
* Create a debug subscriber for Taias resolve events.
|
|
340
|
+
*
|
|
341
|
+
* Returns a handler function suitable for `taias.on("resolve", handler)`.
|
|
342
|
+
*
|
|
343
|
+
* - "default" format: multi-line breakdown of context, trace, decision, and affordances.
|
|
344
|
+
* - "compact" format: single line per resolve call.
|
|
345
|
+
*/
|
|
346
|
+
declare function createDebugSubscriber(options?: DebugSubscriberOptions): (event: ResolveEvent) => void;
|
|
347
|
+
|
|
265
348
|
/**
|
|
266
349
|
* Mapped type that creates a registration method for each slot in S.
|
|
267
350
|
* This enables fully typed custom slots via generics.
|
|
@@ -313,4 +396,4 @@ type MergeAffordancesOptions = {
|
|
|
313
396
|
*/
|
|
314
397
|
declare function mergeAffordances<S extends string = DefaultSlots>(registries: AffordanceRegistry<S>[], opts?: MergeAffordancesOptions): AffordanceRegistry<S>;
|
|
315
398
|
|
|
316
|
-
export { type AffordanceRegistrar, type AffordanceRegistry, type Affordances, type Binding, type BindingInput, type CanonicalSlot, type Condition, type Decision, type DefaultSlots, type FlowBuilder, type FlowDefinition, type FlowStep, type HandleRegistration, type LogicStatement, type MatchCondition, type Selection, type StepDecision, type StepHandler, type StepInput, type Taias, type TaiasContext, type TaiasOptions, type UiSelections, createTaias, defineAffordances, defineFlow, mergeAffordances };
|
|
399
|
+
export { type AffordanceRegistrar, type AffordanceRegistry, type Affordances, type Binding, type BindingInput, type CanonicalSlot, type Condition, type DebugOptions, type Decision, type DefaultSlots, type FlowBuilder, type FlowDefinition, type FlowStep, type HandleRegistration, type LogicStatement, type MatchCondition, type ResolveEvent, type ResolveTrace, type Selection, type StepDecision, type StepEvaluation, type StepHandler, type StepInput, type Taias, type TaiasContext, type TaiasEventMap, type TaiasOptions, type UiSelections, createDebugSubscriber, createTaias, defineAffordances, defineFlow, mergeAffordances };
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,88 @@ function selectUiAffordances(decision, index, opts = {}) {
|
|
|
77
77
|
return selections;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
// src/debugSubscriber.ts
|
|
81
|
+
function formatContext(context) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
if (context.toolName) parts.push(`toolName=${context.toolName}`);
|
|
84
|
+
if (context.params) parts.push(`params=${JSON.stringify(context.params)}`);
|
|
85
|
+
if (context.result) parts.push(`result=${JSON.stringify(context.result)}`);
|
|
86
|
+
return parts.length > 0 ? parts.join(", ") : "(empty)";
|
|
87
|
+
}
|
|
88
|
+
function formatContextLabel(context) {
|
|
89
|
+
if (context.toolName) return context.toolName;
|
|
90
|
+
if (context.params) return `params:${JSON.stringify(context.params)}`;
|
|
91
|
+
if (context.result) return `result:${JSON.stringify(context.result)}`;
|
|
92
|
+
return "(empty)";
|
|
93
|
+
}
|
|
94
|
+
function createDebugSubscriber(options) {
|
|
95
|
+
const format = options?.format ?? "default";
|
|
96
|
+
const log = options?.logger ?? console.log;
|
|
97
|
+
if (format === "compact") {
|
|
98
|
+
return (event) => {
|
|
99
|
+
const { trace, context } = event;
|
|
100
|
+
const label = formatContextLabel(context);
|
|
101
|
+
if (!trace.matched) {
|
|
102
|
+
log(`[Taias] ${label} \u2192 no match (${trace.candidatesEvaluated} evaluated)`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const decisionSummary = event.decision ? Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(", ") : "null";
|
|
106
|
+
log(
|
|
107
|
+
`[Taias] ${label} \u2192 ${decisionSummary} (${trace.phase}, step ${trace.matchedStepIndex}, ${trace.candidatesEvaluated} evaluated)`
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return (event) => {
|
|
112
|
+
const { trace, context } = event;
|
|
113
|
+
const lines = [];
|
|
114
|
+
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");
|
|
115
|
+
lines.push(`\u2502 Flow: ${event.flowId}`);
|
|
116
|
+
lines.push(`\u2502 Context: ${formatContext(context)}`);
|
|
117
|
+
lines.push("\u2502");
|
|
118
|
+
if (!trace.matched) {
|
|
119
|
+
lines.push("\u2502 Result: NO MATCH");
|
|
120
|
+
lines.push(`\u2502 Candidates evaluated: ${trace.candidatesEvaluated}`);
|
|
121
|
+
} else {
|
|
122
|
+
lines.push(`\u2502 Matched: step ${trace.matchedStepIndex} (${trace.matchedStepKind})`);
|
|
123
|
+
lines.push(`\u2502 Phase: ${trace.phase}`);
|
|
124
|
+
if (trace.resolutionPath.length > 0) {
|
|
125
|
+
lines.push(`\u2502 Resolution path: ${trace.resolutionPath.join(" \u2192 ")}`);
|
|
126
|
+
}
|
|
127
|
+
lines.push(`\u2502 Candidates evaluated: ${trace.candidatesEvaluated}`);
|
|
128
|
+
if (trace.matchedStepMatch) {
|
|
129
|
+
lines.push(`\u2502 Match condition: ${JSON.stringify(trace.matchedStepMatch)}`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
lines.push("\u2502");
|
|
133
|
+
if (event.decision) {
|
|
134
|
+
const fields = Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(", ");
|
|
135
|
+
lines.push(`\u2502 Decision: ${fields}`);
|
|
136
|
+
} else {
|
|
137
|
+
lines.push("\u2502 Decision: null");
|
|
138
|
+
}
|
|
139
|
+
if (event.affordances) {
|
|
140
|
+
lines.push(`\u2502 Advice: ${event.affordances.advice.slice(0, 80)}${event.affordances.advice.length > 80 ? "..." : ""}`);
|
|
141
|
+
}
|
|
142
|
+
lines.push(`\u2502 Duration: ${event.durationMs.toFixed(2)}ms`);
|
|
143
|
+
if (trace.evaluations) {
|
|
144
|
+
lines.push("\u2502");
|
|
145
|
+
lines.push("\u2502 Detailed evaluations:");
|
|
146
|
+
for (const evaluation of trace.evaluations) {
|
|
147
|
+
const icon = evaluation.result === "matched" ? "\u2713" : "\u2717";
|
|
148
|
+
lines.push(`\u2502 ${icon} step ${evaluation.stepIndex}: ${evaluation.result}`);
|
|
149
|
+
if (evaluation.fieldResults) {
|
|
150
|
+
for (const [field, fr] of Object.entries(evaluation.fieldResults)) {
|
|
151
|
+
const fieldIcon = fr.passed ? "\u2713" : "\u2717";
|
|
152
|
+
lines.push(`\u2502 ${fieldIcon} ${field}: ${JSON.stringify(fr.condition)} vs ${JSON.stringify(fr.actual)}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
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");
|
|
158
|
+
log(lines.join("\n"));
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
80
162
|
// src/createTaias.ts
|
|
81
163
|
function generateAdvice(nextTool) {
|
|
82
164
|
return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;
|
|
@@ -102,6 +184,47 @@ function evaluateMatch(match, ctx) {
|
|
|
102
184
|
}
|
|
103
185
|
return true;
|
|
104
186
|
}
|
|
187
|
+
function evaluateMatchDetailed(match, ctx) {
|
|
188
|
+
const fieldResults = {};
|
|
189
|
+
let allPassed = true;
|
|
190
|
+
if (match.toolName) {
|
|
191
|
+
const actual = ctx.toolName;
|
|
192
|
+
const passed = evaluateCondition(match.toolName, actual);
|
|
193
|
+
fieldResults["toolName"] = { condition: match.toolName, actual, passed };
|
|
194
|
+
if (!passed) allPassed = false;
|
|
195
|
+
}
|
|
196
|
+
if (match.params) {
|
|
197
|
+
if (!ctx.params) {
|
|
198
|
+
for (const [key, condition] of Object.entries(match.params)) {
|
|
199
|
+
fieldResults[`params.${key}`] = { condition, actual: void 0, passed: false };
|
|
200
|
+
}
|
|
201
|
+
allPassed = false;
|
|
202
|
+
} else {
|
|
203
|
+
for (const [key, condition] of Object.entries(match.params)) {
|
|
204
|
+
const actual = ctx.params[key];
|
|
205
|
+
const passed = evaluateCondition(condition, actual);
|
|
206
|
+
fieldResults[`params.${key}`] = { condition, actual, passed };
|
|
207
|
+
if (!passed) allPassed = false;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (match.result) {
|
|
212
|
+
if (!ctx.result) {
|
|
213
|
+
for (const [key, condition] of Object.entries(match.result)) {
|
|
214
|
+
fieldResults[`result.${key}`] = { condition, actual: void 0, passed: false };
|
|
215
|
+
}
|
|
216
|
+
allPassed = false;
|
|
217
|
+
} else {
|
|
218
|
+
for (const [key, condition] of Object.entries(match.result)) {
|
|
219
|
+
const actual = ctx.result[key];
|
|
220
|
+
const passed = evaluateCondition(condition, actual);
|
|
221
|
+
fieldResults[`result.${key}`] = { condition, actual, passed };
|
|
222
|
+
if (!passed) allPassed = false;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return { passed: allPassed, fieldResults };
|
|
227
|
+
}
|
|
105
228
|
function extractIsConditions(match) {
|
|
106
229
|
const conditions = [];
|
|
107
230
|
if (match.toolName && "is" in match.toolName) {
|
|
@@ -142,10 +265,13 @@ function createTaias(options) {
|
|
|
142
265
|
flow,
|
|
143
266
|
affordances,
|
|
144
267
|
devMode = false,
|
|
268
|
+
debug = false,
|
|
269
|
+
tracing = "summary",
|
|
145
270
|
onMissingStep,
|
|
146
271
|
onWarn
|
|
147
272
|
} = options;
|
|
148
273
|
const warn = onWarn ?? ((msg) => console.warn(msg));
|
|
274
|
+
const detailed = tracing === "detailed";
|
|
149
275
|
if (devMode) {
|
|
150
276
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
151
277
|
for (const step of flow.steps) {
|
|
@@ -192,9 +318,54 @@ function createTaias(options) {
|
|
|
192
318
|
}
|
|
193
319
|
const hasBroadSteps = broadStepIndices.length > 0;
|
|
194
320
|
const registryIndex = buildRegistryIndex(affordances);
|
|
321
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
322
|
+
function emit(event, data) {
|
|
323
|
+
const handlers = listeners.get(event);
|
|
324
|
+
if (handlers) {
|
|
325
|
+
for (const handler of handlers) handler(data);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function on(event, handler) {
|
|
329
|
+
let set = listeners.get(event);
|
|
330
|
+
if (!set) {
|
|
331
|
+
set = /* @__PURE__ */ new Set();
|
|
332
|
+
listeners.set(event, set);
|
|
333
|
+
}
|
|
334
|
+
set.add(handler);
|
|
335
|
+
}
|
|
336
|
+
function off(event, handler) {
|
|
337
|
+
listeners.get(event)?.delete(handler);
|
|
338
|
+
}
|
|
339
|
+
if (debug) {
|
|
340
|
+
const debugOpts = typeof debug === "object" ? debug : void 0;
|
|
341
|
+
on("resolve", createDebugSubscriber(debugOpts));
|
|
342
|
+
}
|
|
195
343
|
return {
|
|
196
344
|
async resolve(ctx) {
|
|
345
|
+
const startTime = performance.now();
|
|
346
|
+
const timestamp = Date.now();
|
|
197
347
|
let matchedStep;
|
|
348
|
+
let matchedStepIndex = null;
|
|
349
|
+
let matchPhase = null;
|
|
350
|
+
const resolutionPath = [];
|
|
351
|
+
let candidatesEvaluated = 0;
|
|
352
|
+
const evaluations = detailed ? [] : void 0;
|
|
353
|
+
function tryMatch(idx) {
|
|
354
|
+
candidatesEvaluated++;
|
|
355
|
+
const step = flow.steps[idx];
|
|
356
|
+
const match = getMatch(step);
|
|
357
|
+
if (detailed) {
|
|
358
|
+
const { passed, fieldResults } = evaluateMatchDetailed(match, ctx);
|
|
359
|
+
evaluations.push({
|
|
360
|
+
stepIndex: idx,
|
|
361
|
+
match,
|
|
362
|
+
result: passed ? "matched" : "no-match",
|
|
363
|
+
fieldResults
|
|
364
|
+
});
|
|
365
|
+
return passed;
|
|
366
|
+
}
|
|
367
|
+
return evaluateMatch(match, ctx);
|
|
368
|
+
}
|
|
198
369
|
const applicableFieldPaths = [];
|
|
199
370
|
for (const fieldPath of fieldIndexes.keys()) {
|
|
200
371
|
const ctxValue = getContextValue(ctx, fieldPath);
|
|
@@ -202,6 +373,7 @@ function createTaias(options) {
|
|
|
202
373
|
applicableFieldPaths.push(fieldPath);
|
|
203
374
|
}
|
|
204
375
|
}
|
|
376
|
+
resolutionPath.push(...applicableFieldPaths);
|
|
205
377
|
if (applicableFieldPaths.length > 0) {
|
|
206
378
|
let candidates = null;
|
|
207
379
|
for (const fieldPath of applicableFieldPaths) {
|
|
@@ -226,30 +398,55 @@ function createTaias(options) {
|
|
|
226
398
|
if (candidates && candidates.size > 0) {
|
|
227
399
|
const sorted = [...candidates].sort((a, b) => a - b);
|
|
228
400
|
for (const idx of sorted) {
|
|
229
|
-
if (
|
|
401
|
+
if (tryMatch(idx)) {
|
|
230
402
|
matchedStep = flow.steps[idx];
|
|
403
|
+
matchedStepIndex = idx;
|
|
404
|
+
matchPhase = "indexed";
|
|
231
405
|
break;
|
|
232
406
|
}
|
|
233
407
|
}
|
|
234
408
|
}
|
|
235
409
|
} else if (indexableStepIndices.length > 0) {
|
|
236
410
|
for (const idx of indexableStepIndices) {
|
|
237
|
-
if (
|
|
411
|
+
if (tryMatch(idx)) {
|
|
238
412
|
matchedStep = flow.steps[idx];
|
|
413
|
+
matchedStepIndex = idx;
|
|
414
|
+
matchPhase = "indexed";
|
|
239
415
|
break;
|
|
240
416
|
}
|
|
241
417
|
}
|
|
242
418
|
}
|
|
243
419
|
if (!matchedStep && hasBroadSteps) {
|
|
244
420
|
for (const idx of broadStepIndices) {
|
|
245
|
-
if (
|
|
421
|
+
if (tryMatch(idx)) {
|
|
246
422
|
matchedStep = flow.steps[idx];
|
|
423
|
+
matchedStepIndex = idx;
|
|
424
|
+
matchPhase = "broad";
|
|
247
425
|
break;
|
|
248
426
|
}
|
|
249
427
|
}
|
|
250
428
|
}
|
|
429
|
+
const trace = {
|
|
430
|
+
matched: !!matchedStep,
|
|
431
|
+
matchedStepIndex,
|
|
432
|
+
matchedStepKind: matchedStep?.kind ?? null,
|
|
433
|
+
matchedStepMatch: matchedStep ? getMatch(matchedStep) : null,
|
|
434
|
+
phase: matchPhase,
|
|
435
|
+
resolutionPath,
|
|
436
|
+
candidatesEvaluated,
|
|
437
|
+
...evaluations !== void 0 ? { evaluations } : {}
|
|
438
|
+
};
|
|
251
439
|
if (!matchedStep) {
|
|
252
440
|
onMissingStep?.(ctx);
|
|
441
|
+
emit("resolve", {
|
|
442
|
+
flowId: flow.id,
|
|
443
|
+
timestamp,
|
|
444
|
+
durationMs: performance.now() - startTime,
|
|
445
|
+
context: ctx,
|
|
446
|
+
trace,
|
|
447
|
+
decision: null,
|
|
448
|
+
affordances: null
|
|
449
|
+
});
|
|
253
450
|
return null;
|
|
254
451
|
}
|
|
255
452
|
let result;
|
|
@@ -258,7 +455,18 @@ function createTaias(options) {
|
|
|
258
455
|
} else {
|
|
259
456
|
result = await matchedStep.handler(ctx);
|
|
260
457
|
}
|
|
261
|
-
if (!result)
|
|
458
|
+
if (!result) {
|
|
459
|
+
emit("resolve", {
|
|
460
|
+
flowId: flow.id,
|
|
461
|
+
timestamp,
|
|
462
|
+
durationMs: performance.now() - startTime,
|
|
463
|
+
context: ctx,
|
|
464
|
+
trace,
|
|
465
|
+
decision: null,
|
|
466
|
+
affordances: null
|
|
467
|
+
});
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
262
470
|
if (devMode && result.nextTool === "") {
|
|
263
471
|
warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);
|
|
264
472
|
}
|
|
@@ -267,12 +475,20 @@ function createTaias(options) {
|
|
|
267
475
|
devMode,
|
|
268
476
|
onWarn: warn
|
|
269
477
|
});
|
|
270
|
-
|
|
271
|
-
|
|
478
|
+
const advice = generateAdvice(result.nextTool);
|
|
479
|
+
emit("resolve", {
|
|
480
|
+
flowId: flow.id,
|
|
481
|
+
timestamp,
|
|
482
|
+
durationMs: performance.now() - startTime,
|
|
483
|
+
context: ctx,
|
|
484
|
+
trace,
|
|
272
485
|
decision,
|
|
273
|
-
selections
|
|
274
|
-
};
|
|
275
|
-
|
|
486
|
+
affordances: { advice, selections }
|
|
487
|
+
});
|
|
488
|
+
return { advice, decision, selections };
|
|
489
|
+
},
|
|
490
|
+
on,
|
|
491
|
+
off
|
|
276
492
|
};
|
|
277
493
|
}
|
|
278
494
|
|
|
@@ -322,6 +538,7 @@ function mergeAffordances(registries, opts = {}) {
|
|
|
322
538
|
return merged;
|
|
323
539
|
}
|
|
324
540
|
export {
|
|
541
|
+
createDebugSubscriber,
|
|
325
542
|
createTaias,
|
|
326
543
|
defineAffordances,
|
|
327
544
|
defineFlow,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/flow.ts","../src/uiAffordances/types.ts","../src/uiAffordances/indexing.ts","../src/uiAffordances/select.ts","../src/createTaias.ts","../src/uiAffordances/defineAffordances.ts","../src/uiAffordances/mergeAffordances.ts"],"sourcesContent":["import type { FlowBuilder, FlowDefinition, FlowStep, MatchCondition, StepInput } from \"./types\";\n\n/**\n * Define a flow with its steps.\n *\n * @param flowId - Unique identifier for the flow\n * @param builder - Callback that receives a FlowBuilder to define steps\n * @returns A FlowDefinition object\n *\n * @example Logic statement matching on toolName\n * ```ts\n * const onboardRepoFlow = defineFlow(\"onboard_repo\", (flow) => {\n * flow.step({ toolName: { is: \"scan_repo\" } }, { nextTool: \"configure_app\" });\n * });\n * ```\n *\n * @example Matching on params and result\n * ```ts\n * flow.step(\n * { toolName: { is: \"scan_repo\" }, params: { language: { is: \"python\" } } },\n * { nextTool: \"configure_python\" },\n * );\n * flow.step(\n * { result: { hasConfig: { is: true } } },\n * { nextTool: \"review_config\" },\n * );\n * ```\n *\n * @example isNot operator\n * ```ts\n * flow.step({ toolName: { isNot: \"abort_session\" } }, { nextTool: \"continue_flow\" });\n * ```\n */\nexport function defineFlow(\n flowId: string,\n builder: (flow: FlowBuilder) => void\n): FlowDefinition {\n const steps: FlowStep[] = [];\n\n const flowBuilder: FlowBuilder = {\n step(match: MatchCondition, input: StepInput): void {\n if (typeof input === \"function\") {\n steps.push({ kind: \"handler\", match, handler: input });\n } else {\n steps.push({\n kind: \"logic\",\n statement: {\n match,\n decision: input,\n },\n });\n }\n },\n };\n\n builder(flowBuilder);\n\n return {\n id: flowId,\n steps,\n };\n}\n","/**\n * Default slots for backwards compatibility.\n * Users can define custom slots by passing a type parameter.\n */\nexport type DefaultSlots = \"primaryCta\" | \"secondaryCta\" | \"widgetVariant\";\n\n/**\n * Alias for backwards compatibility in exports.\n * @deprecated Use DefaultSlots or define your own slot type\n */\nexport type CanonicalSlot = DefaultSlots;\n\nexport type Binding = {\n key: string;\n value: string;\n};\n\n/**\n * Input format for registering affordance bindings.\n * - { toolName } is shorthand for { key: \"nextTool\", value: toolName }\n * - { key, value } is the generalized form for custom bindings\n */\nexport type BindingInput = { toolName: string } | { key: string; value: string };\n\n/**\n * A registered UI affordance handle.\n * Generic over slot type S for custom slot support.\n */\nexport type HandleRegistration<S extends string = DefaultSlots> = {\n slot: S;\n handleId: string;\n bindsTo: Binding;\n};\n\nexport type Selection = {\n handleId: string;\n bindsTo: Binding;\n};\n\n/**\n * UI selections keyed by slot name.\n * Generic over slot type S for custom slot support.\n */\nexport type UiSelections<S extends string = DefaultSlots> = Partial<Record<S, Selection>>;\n\n/**\n * Collection of registered handles.\n * Generic over slot type S for custom slot support.\n */\nexport type AffordanceRegistry<S extends string = DefaultSlots> = {\n handles: HandleRegistration<S>[];\n};\n\nexport function normalizeBinding(input: BindingInput): Binding {\n if (\"toolName\" in input) return { key: \"nextTool\", value: input.toolName };\n return { key: input.key, value: input.value };\n}\n\n/**\n * Creates a binding key for indexing.\n * Accepts any string for slot to support custom slots.\n */\nexport function makeBindingKey(slot: string, binding: Binding): string {\n return `${slot}::${binding.key}::${binding.value}`;\n}\n","import type { AffordanceRegistry, DefaultSlots, HandleRegistration } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\n/**\n * Index structure for efficient affordance lookup.\n * Generic over slot type S for custom slot support.\n */\nexport type RegistryIndex<S extends string = DefaultSlots> = {\n byBindingKey: Map<string, HandleRegistration<S>>;\n slots: Set<S>;\n /** Inferred decision field for each slot, derived from handle bindings. */\n slotKeyMap: Map<S, string>;\n};\n\n/**\n * Build an index from a registry for efficient lookup during selection.\n * Tracks which slots have registered handles and infers the decision field\n * for each slot from its handle bindings.\n *\n * @throws Error if handles for the same slot bind to different keys\n */\nexport function buildRegistryIndex<S extends string = DefaultSlots>(\n registry?: AffordanceRegistry<S>\n): RegistryIndex<S> {\n const byBindingKey = new Map<string, HandleRegistration<S>>();\n const slots = new Set<S>();\n const slotKeyMap = new Map<S, string>();\n\n if (!registry) return { byBindingKey, slots, slotKeyMap };\n\n for (const h of registry.handles) {\n slots.add(h.slot);\n\n // Infer and validate slot key\n const existingKey = slotKeyMap.get(h.slot);\n if (existingKey && existingKey !== h.bindsTo.key) {\n throw new Error(\n `[Taias] Slot \"${h.slot}\" has handles bound to different keys: \"${existingKey}\" and \"${h.bindsTo.key}\". ` +\n `All handles for a slot must use the same decision field.`\n );\n }\n slotKeyMap.set(h.slot, h.bindsTo.key);\n\n byBindingKey.set(makeBindingKey(h.slot, h.bindsTo), h);\n }\n\n return { byBindingKey, slots, slotKeyMap };\n}\n","import type { Decision } from \"../types\";\nimport type { DefaultSlots, UiSelections } from \"./types\";\nimport type { RegistryIndex } from \"./indexing\";\nimport { makeBindingKey } from \"./types\";\n\nexport type SelectOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Select UI affordances based on flow decision.\n * Uses the inferred decision field for each slot from the registry index.\n */\nexport function selectUiAffordances<S extends string = DefaultSlots>(\n decision: Decision,\n index: RegistryIndex<S>,\n opts: SelectOptions = {}\n): UiSelections<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const selections: UiSelections<S> = {};\n\n for (const slot of index.slots) {\n // Use inferred key from handle bindings\n const field = index.slotKeyMap.get(slot);\n if (!field) continue;\n\n const value = decision[field];\n if (!value) continue;\n\n const k = makeBindingKey(slot, { key: field, value });\n const handle = index.byBindingKey.get(k);\n\n if (!handle) {\n if (devMode) warn(`[Taias] No affordance for slot \"${slot}\" when ${field}=\"${value}\"`);\n continue;\n }\n\n (selections as Record<string, unknown>)[slot] = {\n handleId: handle.handleId,\n bindsTo: handle.bindsTo,\n };\n }\n\n return selections;\n}\n","import type { Affordances, Condition, FlowStep, MatchCondition, StepDecision, Taias, TaiasContext, TaiasOptions, Decision } from \"./types\";\nimport type { DefaultSlots } from \"./uiAffordances/types\";\nimport { buildRegistryIndex } from \"./uiAffordances/indexing\";\nimport { selectUiAffordances } from \"./uiAffordances/select\";\n\n/**\n * Generate advice text for a given next tool.\n */\nfunction generateAdvice(nextTool: string): string {\n return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;\n}\n\n// ---------------------------------------------------------------------------\n// Condition evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a single Condition against a value using strict equality.\n */\nfunction evaluateCondition(condition: Condition, value: unknown): boolean {\n if (\"is\" in condition) return value === condition.is;\n if (\"isNot\" in condition) return value !== condition.isNot;\n return false;\n}\n\n/**\n * Evaluate a full MatchCondition against a TaiasContext.\n * All present conditions must be satisfied (logical AND).\n *\n * - toolName: compared directly against ctx.toolName\n * - params: subset match -- each specified key is checked against ctx.params\n * - result: subset match -- each specified key is checked against ctx.result\n *\n * If a step specifies params/result conditions but the context doesn't\n * include params/result, the step does not match.\n */\nfunction evaluateMatch(match: MatchCondition, ctx: TaiasContext): boolean {\n if (match.toolName && !evaluateCondition(match.toolName, ctx.toolName)) return false;\n\n if (match.params) {\n if (!ctx.params) return false;\n for (const [key, condition] of Object.entries(match.params)) {\n if (!evaluateCondition(condition, ctx.params[key])) return false;\n }\n }\n\n if (match.result) {\n if (!ctx.result) return false;\n for (const [key, condition] of Object.entries(match.result)) {\n if (!evaluateCondition(condition, ctx.result[key])) return false;\n }\n }\n\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Per-field indexing\n// ---------------------------------------------------------------------------\n\ninterface FieldIndex {\n valueMap: Map<unknown, number[]>;\n unconstrained: number[];\n}\n\n/**\n * Extract all `is` conditions from a match condition as field-path / value pairs.\n */\nfunction extractIsConditions(match: MatchCondition): Array<{ path: string; value: unknown }> {\n const conditions: Array<{ path: string; value: unknown }> = [];\n if (match.toolName && \"is\" in match.toolName) {\n conditions.push({ path: \"toolName\", value: match.toolName.is });\n }\n if (match.params) {\n for (const [key, cond] of Object.entries(match.params)) {\n if (\"is\" in cond) conditions.push({ path: `params.${key}`, value: cond.is });\n }\n }\n if (match.result) {\n for (const [key, cond] of Object.entries(match.result)) {\n if (\"is\" in cond) conditions.push({ path: `result.${key}`, value: cond.is });\n }\n }\n return conditions;\n}\n\n/**\n * Check whether a match condition has a condition on a given field path.\n */\nfunction hasConditionOnField(match: MatchCondition, path: string): boolean {\n if (path === \"toolName\") return !!match.toolName;\n if (path.startsWith(\"params.\")) return !!match.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return !!match.result?.[path.slice(7)];\n return false;\n}\n\n/**\n * Get the value from a TaiasContext for a given field path.\n */\nfunction getContextValue(ctx: TaiasContext, path: string): unknown {\n if (path === \"toolName\") return ctx.toolName;\n if (path.startsWith(\"params.\")) return ctx.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return ctx.result?.[path.slice(7)];\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Step access helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Get the match condition from a FlowStep.\n *\n * - Logic-based steps: match comes from statement.match\n * - Handler-based steps (backwards compatibility): match is stored directly on the step\n */\nfunction getMatch(step: FlowStep): MatchCondition {\n return step.kind === \"logic\" ? step.statement.match : step.match;\n}\n\n/**\n * Serialize a MatchCondition into a stable string for duplicate detection.\n */\nfunction serializeMatch(match: MatchCondition): string {\n return JSON.stringify(match);\n}\n\n/**\n * createTaias constructs a decision engine.\n *\n * Taias resolves context into a generalized Decision object,\n * and then manifests that decision into concrete affordances:\n *\n * - LLM guidance (advice)\n * - UI affordance selections\n *\n * Flow logic is expressed as logic statements -- structured data that\n * Taias understands. (Handler functions remain as a backwards-compatible\n * escape hatch.)\n *\n * Flow logic determines *what should happen next*.\n * UI affordances determine *how that decision appears in the interface*.\n *\n * This file is the boundary where:\n *\n * Inputs -> Decision -> Manifestations\n *\n * are unified into a single resolve() call.\n *\n * @example\n * ```ts\n * const taias = createTaias({ flow, affordances });\n * ```\n */\nexport function createTaias<S extends string = DefaultSlots>(\n options: TaiasOptions<S>\n): Taias<S> {\n const {\n flow,\n affordances,\n devMode = false,\n onMissingStep,\n onWarn,\n } = options;\n\n const warn = onWarn ?? ((msg: string) => console.warn(msg));\n\n // Dev mode: Check for duplicate match conditions.\n if (devMode) {\n const seenKeys = new Set<string>();\n for (const step of flow.steps) {\n const key = serializeMatch(getMatch(step));\n if (seenKeys.has(key)) {\n throw new Error(\n `Taias: Duplicate match condition in flow '${flow.id}'. Each step must have a unique match condition. Duplicate: ${key}`\n );\n }\n seenKeys.add(key);\n }\n }\n\n // -----------------------------------------------------------------------\n // Build per-field indexes\n // -----------------------------------------------------------------------\n\n const fieldIndexes = new Map<string, FieldIndex>();\n const indexableStepIndices: number[] = [];\n const broadStepIndices: number[] = [];\n\n for (let i = 0; i < flow.steps.length; i++) {\n const match = getMatch(flow.steps[i]);\n const isConditions = extractIsConditions(match);\n\n if (isConditions.length === 0) {\n broadStepIndices.push(i);\n continue;\n }\n\n indexableStepIndices.push(i);\n\n for (const { path, value } of isConditions) {\n let fieldIndex = fieldIndexes.get(path);\n if (!fieldIndex) {\n fieldIndex = { valueMap: new Map(), unconstrained: [] };\n fieldIndexes.set(path, fieldIndex);\n }\n let stepList = fieldIndex.valueMap.get(value);\n if (!stepList) {\n stepList = [];\n fieldIndex.valueMap.set(value, stepList);\n }\n stepList.push(i);\n }\n }\n\n // Build unconstrained sets: for each field index, find indexable steps\n // that don't have a condition on that field.\n for (const [fieldPath, fieldIndex] of fieldIndexes) {\n for (const i of indexableStepIndices) {\n if (!hasConditionOnField(getMatch(flow.steps[i]), fieldPath)) {\n fieldIndex.unconstrained.push(i);\n }\n }\n }\n\n const hasBroadSteps = broadStepIndices.length > 0;\n\n // Build affordance index once (if provided)\n const registryIndex = buildRegistryIndex<S>(affordances);\n\n return {\n async resolve(ctx: TaiasContext): Promise<Affordances<S> | null> {\n let matchedStep: FlowStep | undefined;\n\n // Phase 1: Find candidates from per-field indexes via intersection\n const applicableFieldPaths: string[] = [];\n for (const fieldPath of fieldIndexes.keys()) {\n const ctxValue = getContextValue(ctx, fieldPath);\n if (ctxValue !== undefined) {\n applicableFieldPaths.push(fieldPath);\n }\n }\n\n if (applicableFieldPaths.length > 0) {\n // Build candidate sets for each applicable field and intersect\n let candidates: Set<number> | null = null;\n\n for (const fieldPath of applicableFieldPaths) {\n const fieldIndex = fieldIndexes.get(fieldPath)!;\n const ctxValue = getContextValue(ctx, fieldPath);\n\n const fieldCandidates = new Set<number>();\n\n // Indexed matches for this field's value\n const indexed = fieldIndex.valueMap.get(ctxValue);\n if (indexed) {\n for (const idx of indexed) fieldCandidates.add(idx);\n }\n\n // Unconstrained steps (don't care about this field)\n for (const idx of fieldIndex.unconstrained) {\n fieldCandidates.add(idx);\n }\n\n if (candidates === null) {\n candidates = fieldCandidates;\n } else {\n // Intersect\n for (const idx of candidates) {\n if (!fieldCandidates.has(idx)) candidates.delete(idx);\n }\n }\n }\n\n // Evaluate full conditions on narrowed candidates (definition order)\n if (candidates && candidates.size > 0) {\n const sorted = [...candidates].sort((a, b) => a - b);\n for (const idx of sorted) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n } else if (indexableStepIndices.length > 0) {\n // Context has no fields that match any index -- evaluate all\n // indexable steps that are unconstrained on everything\n // (i.e., steps with is conditions on fields not present in context).\n // evaluateMatch handles this correctly.\n for (const idx of indexableStepIndices) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n\n // Phase 2: If no indexed match, evaluate broad steps\n if (!matchedStep && hasBroadSteps) {\n for (const idx of broadStepIndices) {\n if (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {\n matchedStep = flow.steps[idx];\n break;\n }\n }\n }\n\n if (!matchedStep) {\n onMissingStep?.(ctx);\n return null;\n }\n\n // Evaluate the step based on its kind\n let result: StepDecision | null;\n\n if (matchedStep.kind === \"logic\") {\n result = matchedStep.statement.decision;\n } else {\n result = await matchedStep.handler(ctx);\n }\n\n if (!result) return null;\n\n if (devMode && result.nextTool === \"\") {\n warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);\n }\n\n const decision: Decision = { ...result };\n\n const selections = selectUiAffordances<S>(decision, registryIndex, {\n devMode,\n onWarn: warn,\n });\n\n return {\n advice: generateAdvice(result.nextTool),\n decision,\n selections,\n };\n },\n };\n}\n","import type {\n AffordanceRegistry,\n BindingInput,\n DefaultSlots,\n HandleRegistration,\n} from \"./types\";\nimport { normalizeBinding } from \"./types\";\n\n/**\n * Mapped type that creates a registration method for each slot in S.\n * This enables fully typed custom slots via generics.\n */\nexport type AffordanceRegistrar<S extends string = DefaultSlots> = {\n [K in S]: (handleId: string, bindsTo: BindingInput) => void;\n};\n\n/**\n * Define UI affordances for a widget using a builder pattern.\n *\n * @example Default slots (backwards compatible)\n * ```ts\n * const affordances = defineAffordances((r) => {\n * r.primaryCta(\"cta.recommend\", { toolName: \"get_recommendations\" });\n * r.widgetVariant(\"variant.discovery\", { toolName: \"get_recommendations\" });\n * });\n * ```\n *\n * @example Custom slots (fully type-safe)\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\" | \"headerStyle\";\n * const affordances = defineAffordances<MySlots>((r) => {\n * r.primaryCta(\"cta.create\", { toolName: \"createUser\" });\n * r.contentArea(\"content.form\", { key: \"contentArea\", value: \"email-form\" });\n * r.headerStyle(\"header.progress\", { key: \"headerStyle\", value: \"step-1\" });\n * });\n * ```\n */\nexport function defineAffordances<S extends string = DefaultSlots>(\n builder: (r: AffordanceRegistrar<S>) => void\n): AffordanceRegistry<S> {\n const handles: HandleRegistration<S>[] = [];\n\n // Proxy creates methods on-the-fly for any slot name.\n // TypeScript ensures only valid slot names (from S) are called.\n const registrar = new Proxy({} as AffordanceRegistrar<S>, {\n get(_, slot: string) {\n return (handleId: string, bindsTo: BindingInput) => {\n handles.push({\n slot: slot as S,\n handleId,\n bindsTo: normalizeBinding(bindsTo),\n });\n };\n },\n });\n\n builder(registrar);\n return { handles };\n}\n","import type { AffordanceRegistry, DefaultSlots } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\nexport type MergeAffordancesOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Merge multiple affordance registries into one.\n * Generic over slot type S for custom slot support.\n *\n * @example Default slots\n * ```ts\n * const merged = mergeAffordances([widgetA, widgetB]);\n * ```\n *\n * @example Custom slots\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\";\n * const merged = mergeAffordances<MySlots>([widgetA, widgetB]);\n * ```\n */\nexport function mergeAffordances<S extends string = DefaultSlots>(\n registries: AffordanceRegistry<S>[],\n opts: MergeAffordancesOptions = {}\n): AffordanceRegistry<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const merged: AffordanceRegistry<S> = { handles: registries.flatMap((r) => r.handles) };\n\n if (!devMode) return merged;\n\n // Check for duplicate handleIds\n const seenHandleIds = new Set<string>();\n for (const h of merged.handles) {\n if (seenHandleIds.has(h.handleId)) {\n throw new Error(`[Taias] Duplicate handleId \"${h.handleId}\"`);\n }\n seenHandleIds.add(h.handleId);\n }\n\n // Check for ambiguous bindings (same slot + key + value)\n const seenTriples = new Set<string>();\n for (const h of merged.handles) {\n const k = makeBindingKey(h.slot, h.bindsTo);\n if (seenTriples.has(k)) {\n throw new Error(\n `[Taias] Ambiguous affordance: slot \"${h.slot}\" has multiple handles bound to (${h.bindsTo.key}=\"${h.bindsTo.value}\")`\n );\n }\n seenTriples.add(k);\n }\n\n warn(`[Taias] Loaded ${merged.handles.length} UI affordance handles`);\n return merged;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/flow.ts","../src/uiAffordances/types.ts","../src/uiAffordances/indexing.ts","../src/uiAffordances/select.ts","../src/debugSubscriber.ts","../src/createTaias.ts","../src/uiAffordances/defineAffordances.ts","../src/uiAffordances/mergeAffordances.ts"],"sourcesContent":["import type { FlowBuilder, FlowDefinition, FlowStep, MatchCondition, StepInput } from \"./types\";\n\n/**\n * Define a flow with its steps.\n *\n * @param flowId - Unique identifier for the flow\n * @param builder - Callback that receives a FlowBuilder to define steps\n * @returns A FlowDefinition object\n *\n * @example Logic statement matching on toolName\n * ```ts\n * const onboardRepoFlow = defineFlow(\"onboard_repo\", (flow) => {\n * flow.step({ toolName: { is: \"scan_repo\" } }, { nextTool: \"configure_app\" });\n * });\n * ```\n *\n * @example Matching on params and result\n * ```ts\n * flow.step(\n * { toolName: { is: \"scan_repo\" }, params: { language: { is: \"python\" } } },\n * { nextTool: \"configure_python\" },\n * );\n * flow.step(\n * { result: { hasConfig: { is: true } } },\n * { nextTool: \"review_config\" },\n * );\n * ```\n *\n * @example isNot operator\n * ```ts\n * flow.step({ toolName: { isNot: \"abort_session\" } }, { nextTool: \"continue_flow\" });\n * ```\n */\nexport function defineFlow(\n flowId: string,\n builder: (flow: FlowBuilder) => void\n): FlowDefinition {\n const steps: FlowStep[] = [];\n\n const flowBuilder: FlowBuilder = {\n step(match: MatchCondition, input: StepInput): void {\n if (typeof input === \"function\") {\n steps.push({ kind: \"handler\", match, handler: input });\n } else {\n steps.push({\n kind: \"logic\",\n statement: {\n match,\n decision: input,\n },\n });\n }\n },\n };\n\n builder(flowBuilder);\n\n return {\n id: flowId,\n steps,\n };\n}\n","/**\n * Default slots for backwards compatibility.\n * Users can define custom slots by passing a type parameter.\n */\nexport type DefaultSlots = \"primaryCta\" | \"secondaryCta\" | \"widgetVariant\";\n\n/**\n * Alias for backwards compatibility in exports.\n * @deprecated Use DefaultSlots or define your own slot type\n */\nexport type CanonicalSlot = DefaultSlots;\n\nexport type Binding = {\n key: string;\n value: string;\n};\n\n/**\n * Input format for registering affordance bindings.\n * - { toolName } is shorthand for { key: \"nextTool\", value: toolName }\n * - { key, value } is the generalized form for custom bindings\n */\nexport type BindingInput = { toolName: string } | { key: string; value: string };\n\n/**\n * A registered UI affordance handle.\n * Generic over slot type S for custom slot support.\n */\nexport type HandleRegistration<S extends string = DefaultSlots> = {\n slot: S;\n handleId: string;\n bindsTo: Binding;\n};\n\nexport type Selection = {\n handleId: string;\n bindsTo: Binding;\n};\n\n/**\n * UI selections keyed by slot name.\n * Generic over slot type S for custom slot support.\n */\nexport type UiSelections<S extends string = DefaultSlots> = Partial<Record<S, Selection>>;\n\n/**\n * Collection of registered handles.\n * Generic over slot type S for custom slot support.\n */\nexport type AffordanceRegistry<S extends string = DefaultSlots> = {\n handles: HandleRegistration<S>[];\n};\n\nexport function normalizeBinding(input: BindingInput): Binding {\n if (\"toolName\" in input) return { key: \"nextTool\", value: input.toolName };\n return { key: input.key, value: input.value };\n}\n\n/**\n * Creates a binding key for indexing.\n * Accepts any string for slot to support custom slots.\n */\nexport function makeBindingKey(slot: string, binding: Binding): string {\n return `${slot}::${binding.key}::${binding.value}`;\n}\n","import type { AffordanceRegistry, DefaultSlots, HandleRegistration } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\n/**\n * Index structure for efficient affordance lookup.\n * Generic over slot type S for custom slot support.\n */\nexport type RegistryIndex<S extends string = DefaultSlots> = {\n byBindingKey: Map<string, HandleRegistration<S>>;\n slots: Set<S>;\n /** Inferred decision field for each slot, derived from handle bindings. */\n slotKeyMap: Map<S, string>;\n};\n\n/**\n * Build an index from a registry for efficient lookup during selection.\n * Tracks which slots have registered handles and infers the decision field\n * for each slot from its handle bindings.\n *\n * @throws Error if handles for the same slot bind to different keys\n */\nexport function buildRegistryIndex<S extends string = DefaultSlots>(\n registry?: AffordanceRegistry<S>\n): RegistryIndex<S> {\n const byBindingKey = new Map<string, HandleRegistration<S>>();\n const slots = new Set<S>();\n const slotKeyMap = new Map<S, string>();\n\n if (!registry) return { byBindingKey, slots, slotKeyMap };\n\n for (const h of registry.handles) {\n slots.add(h.slot);\n\n // Infer and validate slot key\n const existingKey = slotKeyMap.get(h.slot);\n if (existingKey && existingKey !== h.bindsTo.key) {\n throw new Error(\n `[Taias] Slot \"${h.slot}\" has handles bound to different keys: \"${existingKey}\" and \"${h.bindsTo.key}\". ` +\n `All handles for a slot must use the same decision field.`\n );\n }\n slotKeyMap.set(h.slot, h.bindsTo.key);\n\n byBindingKey.set(makeBindingKey(h.slot, h.bindsTo), h);\n }\n\n return { byBindingKey, slots, slotKeyMap };\n}\n","import type { Decision } from \"../types\";\nimport type { DefaultSlots, UiSelections } from \"./types\";\nimport type { RegistryIndex } from \"./indexing\";\nimport { makeBindingKey } from \"./types\";\n\nexport type SelectOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Select UI affordances based on flow decision.\n * Uses the inferred decision field for each slot from the registry index.\n */\nexport function selectUiAffordances<S extends string = DefaultSlots>(\n decision: Decision,\n index: RegistryIndex<S>,\n opts: SelectOptions = {}\n): UiSelections<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const selections: UiSelections<S> = {};\n\n for (const slot of index.slots) {\n // Use inferred key from handle bindings\n const field = index.slotKeyMap.get(slot);\n if (!field) continue;\n\n const value = decision[field];\n if (!value) continue;\n\n const k = makeBindingKey(slot, { key: field, value });\n const handle = index.byBindingKey.get(k);\n\n if (!handle) {\n if (devMode) warn(`[Taias] No affordance for slot \"${slot}\" when ${field}=\"${value}\"`);\n continue;\n }\n\n (selections as Record<string, unknown>)[slot] = {\n handleId: handle.handleId,\n bindsTo: handle.bindsTo,\n };\n }\n\n return selections;\n}\n","import type { ResolveEvent, TaiasContext, DebugOptions } from \"./types\";\n\nexport type DebugSubscriberOptions = DebugOptions;\n\nfunction formatContext(context: TaiasContext): string {\n const parts: string[] = [];\n if (context.toolName) parts.push(`toolName=${context.toolName}`);\n if (context.params) parts.push(`params=${JSON.stringify(context.params)}`);\n if (context.result) parts.push(`result=${JSON.stringify(context.result)}`);\n return parts.length > 0 ? parts.join(\", \") : \"(empty)\";\n}\n\nfunction formatContextLabel(context: TaiasContext): string {\n if (context.toolName) return context.toolName;\n if (context.params) return `params:${JSON.stringify(context.params)}`;\n if (context.result) return `result:${JSON.stringify(context.result)}`;\n return \"(empty)\";\n}\n\n/**\n * Create a debug subscriber for Taias resolve events.\n *\n * Returns a handler function suitable for `taias.on(\"resolve\", handler)`.\n *\n * - \"default\" format: multi-line breakdown of context, trace, decision, and affordances.\n * - \"compact\" format: single line per resolve call.\n */\nexport function createDebugSubscriber(\n options?: DebugSubscriberOptions\n): (event: ResolveEvent) => void {\n const format = options?.format ?? \"default\";\n const log = options?.logger ?? console.log;\n\n if (format === \"compact\") {\n return (event: ResolveEvent) => {\n const { trace, context } = event;\n const label = formatContextLabel(context);\n\n if (!trace.matched) {\n log(`[Taias] ${label} → no match (${trace.candidatesEvaluated} evaluated)`);\n return;\n }\n\n const decisionSummary = event.decision\n ? Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(\", \")\n : \"null\";\n\n log(\n `[Taias] ${label} → ${decisionSummary} (${trace.phase}, step ${trace.matchedStepIndex}, ${trace.candidatesEvaluated} evaluated)`\n );\n };\n }\n\n return (event: ResolveEvent) => {\n const { trace, context } = event;\n const lines: string[] = [];\n\n lines.push(\"┌─ Taias Resolve ─────────────────────────────\");\n lines.push(`│ Flow: ${event.flowId}`);\n lines.push(`│ Context: ${formatContext(context)}`);\n\n lines.push(\"│\");\n\n if (!trace.matched) {\n lines.push(\"│ Result: NO MATCH\");\n lines.push(`│ Candidates evaluated: ${trace.candidatesEvaluated}`);\n } else {\n lines.push(`│ Matched: step ${trace.matchedStepIndex} (${trace.matchedStepKind})`);\n lines.push(`│ Phase: ${trace.phase}`);\n\n if (trace.resolutionPath.length > 0) {\n lines.push(`│ Resolution path: ${trace.resolutionPath.join(\" → \")}`);\n }\n\n lines.push(`│ Candidates evaluated: ${trace.candidatesEvaluated}`);\n\n if (trace.matchedStepMatch) {\n lines.push(`│ Match condition: ${JSON.stringify(trace.matchedStepMatch)}`);\n }\n }\n\n lines.push(\"│\");\n\n if (event.decision) {\n const fields = Object.entries(event.decision).map(([k, v]) => `${k}=${v}`).join(\", \");\n lines.push(`│ Decision: ${fields}`);\n } else {\n lines.push(\"│ Decision: null\");\n }\n\n if (event.affordances) {\n lines.push(`│ Advice: ${event.affordances.advice.slice(0, 80)}${event.affordances.advice.length > 80 ? \"...\" : \"\"}`);\n }\n\n lines.push(`│ Duration: ${event.durationMs.toFixed(2)}ms`);\n\n if (trace.evaluations) {\n lines.push(\"│\");\n lines.push(\"│ Detailed evaluations:\");\n for (const evaluation of trace.evaluations) {\n const icon = evaluation.result === \"matched\" ? \"✓\" : \"✗\";\n lines.push(`│ ${icon} step ${evaluation.stepIndex}: ${evaluation.result}`);\n\n if (evaluation.fieldResults) {\n for (const [field, fr] of Object.entries(evaluation.fieldResults)) {\n const fieldIcon = fr.passed ? \"✓\" : \"✗\";\n lines.push(`│ ${fieldIcon} ${field}: ${JSON.stringify(fr.condition)} vs ${JSON.stringify(fr.actual)}`);\n }\n }\n }\n }\n\n lines.push(\"└──────────────────────────────────────────────\");\n\n log(lines.join(\"\\n\"));\n };\n}\n","import type { Affordances, Condition, FlowStep, MatchCondition, StepDecision, Taias, TaiasContext, TaiasOptions, Decision, ResolveEvent, ResolveTrace, StepEvaluation } from \"./types\";\nimport type { DefaultSlots } from \"./uiAffordances/types\";\nimport { buildRegistryIndex } from \"./uiAffordances/indexing\";\nimport { selectUiAffordances } from \"./uiAffordances/select\";\nimport { createDebugSubscriber } from \"./debugSubscriber\";\n\n/**\n * Generate advice text for a given next tool.\n */\nfunction generateAdvice(nextTool: string): string {\n return `FOR THE BEST USER EXPERIENCE, TELL THE USER TO USE THE ${nextTool} TOOL NEXT!!!!!`;\n}\n\n// ---------------------------------------------------------------------------\n// Condition evaluation\n// ---------------------------------------------------------------------------\n\n/**\n * Evaluate a single Condition against a value using strict equality.\n */\nfunction evaluateCondition(condition: Condition, value: unknown): boolean {\n if (\"is\" in condition) return value === condition.is;\n if (\"isNot\" in condition) return value !== condition.isNot;\n return false;\n}\n\n/**\n * Evaluate a full MatchCondition against a TaiasContext.\n * All present conditions must be satisfied (logical AND).\n *\n * - toolName: compared directly against ctx.toolName\n * - params: subset match -- each specified key is checked against ctx.params\n * - result: subset match -- each specified key is checked against ctx.result\n *\n * If a step specifies params/result conditions but the context doesn't\n * include params/result, the step does not match.\n */\nfunction evaluateMatch(match: MatchCondition, ctx: TaiasContext): boolean {\n if (match.toolName && !evaluateCondition(match.toolName, ctx.toolName)) return false;\n\n if (match.params) {\n if (!ctx.params) return false;\n for (const [key, condition] of Object.entries(match.params)) {\n if (!evaluateCondition(condition, ctx.params[key])) return false;\n }\n }\n\n if (match.result) {\n if (!ctx.result) return false;\n for (const [key, condition] of Object.entries(match.result)) {\n if (!evaluateCondition(condition, ctx.result[key])) return false;\n }\n }\n\n return true;\n}\n\n/**\n * Evaluate a full MatchCondition and return per-field breakdown.\n * Used when detailed tracing is enabled.\n */\nfunction evaluateMatchDetailed(match: MatchCondition, ctx: TaiasContext): {\n passed: boolean;\n fieldResults: Record<string, { condition: Condition; actual: unknown; passed: boolean }>;\n} {\n const fieldResults: Record<string, { condition: Condition; actual: unknown; passed: boolean }> = {};\n let allPassed = true;\n\n if (match.toolName) {\n const actual = ctx.toolName;\n const passed = evaluateCondition(match.toolName, actual);\n fieldResults[\"toolName\"] = { condition: match.toolName, actual, passed };\n if (!passed) allPassed = false;\n }\n\n if (match.params) {\n if (!ctx.params) {\n for (const [key, condition] of Object.entries(match.params)) {\n fieldResults[`params.${key}`] = { condition, actual: undefined, passed: false };\n }\n allPassed = false;\n } else {\n for (const [key, condition] of Object.entries(match.params)) {\n const actual = ctx.params[key];\n const passed = evaluateCondition(condition, actual);\n fieldResults[`params.${key}`] = { condition, actual, passed };\n if (!passed) allPassed = false;\n }\n }\n }\n\n if (match.result) {\n if (!ctx.result) {\n for (const [key, condition] of Object.entries(match.result)) {\n fieldResults[`result.${key}`] = { condition, actual: undefined, passed: false };\n }\n allPassed = false;\n } else {\n for (const [key, condition] of Object.entries(match.result)) {\n const actual = ctx.result[key];\n const passed = evaluateCondition(condition, actual);\n fieldResults[`result.${key}`] = { condition, actual, passed };\n if (!passed) allPassed = false;\n }\n }\n }\n\n return { passed: allPassed, fieldResults };\n}\n\n// ---------------------------------------------------------------------------\n// Per-field indexing\n// ---------------------------------------------------------------------------\n\ninterface FieldIndex {\n valueMap: Map<unknown, number[]>;\n unconstrained: number[];\n}\n\n/**\n * Extract all `is` conditions from a match condition as field-path / value pairs.\n */\nfunction extractIsConditions(match: MatchCondition): Array<{ path: string; value: unknown }> {\n const conditions: Array<{ path: string; value: unknown }> = [];\n if (match.toolName && \"is\" in match.toolName) {\n conditions.push({ path: \"toolName\", value: match.toolName.is });\n }\n if (match.params) {\n for (const [key, cond] of Object.entries(match.params)) {\n if (\"is\" in cond) conditions.push({ path: `params.${key}`, value: cond.is });\n }\n }\n if (match.result) {\n for (const [key, cond] of Object.entries(match.result)) {\n if (\"is\" in cond) conditions.push({ path: `result.${key}`, value: cond.is });\n }\n }\n return conditions;\n}\n\n/**\n * Check whether a match condition has a condition on a given field path.\n */\nfunction hasConditionOnField(match: MatchCondition, path: string): boolean {\n if (path === \"toolName\") return !!match.toolName;\n if (path.startsWith(\"params.\")) return !!match.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return !!match.result?.[path.slice(7)];\n return false;\n}\n\n/**\n * Get the value from a TaiasContext for a given field path.\n */\nfunction getContextValue(ctx: TaiasContext, path: string): unknown {\n if (path === \"toolName\") return ctx.toolName;\n if (path.startsWith(\"params.\")) return ctx.params?.[path.slice(7)];\n if (path.startsWith(\"result.\")) return ctx.result?.[path.slice(7)];\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Step access helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Get the match condition from a FlowStep.\n *\n * - Logic-based steps: match comes from statement.match\n * - Handler-based steps (backwards compatibility): match is stored directly on the step\n */\nfunction getMatch(step: FlowStep): MatchCondition {\n return step.kind === \"logic\" ? step.statement.match : step.match;\n}\n\n/**\n * Serialize a MatchCondition into a stable string for duplicate detection.\n */\nfunction serializeMatch(match: MatchCondition): string {\n return JSON.stringify(match);\n}\n\n/**\n * createTaias constructs a decision engine.\n *\n * Taias resolves context into a generalized Decision object,\n * and then manifests that decision into concrete affordances:\n *\n * - LLM guidance (advice)\n * - UI affordance selections\n *\n * Flow logic is expressed as logic statements -- structured data that\n * Taias understands. (Handler functions remain as a backwards-compatible\n * escape hatch.)\n *\n * Flow logic determines *what should happen next*.\n * UI affordances determine *how that decision appears in the interface*.\n *\n * This file is the boundary where:\n *\n * Inputs -> Decision -> Manifestations\n *\n * are unified into a single resolve() call.\n *\n * @example\n * ```ts\n * const taias = createTaias({ flow, affordances });\n * ```\n */\nexport function createTaias<S extends string = DefaultSlots>(\n options: TaiasOptions<S>\n): Taias<S> {\n const {\n flow,\n affordances,\n devMode = false,\n debug = false,\n tracing = \"summary\",\n onMissingStep,\n onWarn,\n } = options;\n\n const warn = onWarn ?? ((msg: string) => console.warn(msg));\n const detailed = tracing === \"detailed\";\n\n // Dev mode: Check for duplicate match conditions.\n if (devMode) {\n const seenKeys = new Set<string>();\n for (const step of flow.steps) {\n const key = serializeMatch(getMatch(step));\n if (seenKeys.has(key)) {\n throw new Error(\n `Taias: Duplicate match condition in flow '${flow.id}'. Each step must have a unique match condition. Duplicate: ${key}`\n );\n }\n seenKeys.add(key);\n }\n }\n\n // -----------------------------------------------------------------------\n // Build per-field indexes\n // -----------------------------------------------------------------------\n\n const fieldIndexes = new Map<string, FieldIndex>();\n const indexableStepIndices: number[] = [];\n const broadStepIndices: number[] = [];\n\n for (let i = 0; i < flow.steps.length; i++) {\n const match = getMatch(flow.steps[i]);\n const isConditions = extractIsConditions(match);\n\n if (isConditions.length === 0) {\n broadStepIndices.push(i);\n continue;\n }\n\n indexableStepIndices.push(i);\n\n for (const { path, value } of isConditions) {\n let fieldIndex = fieldIndexes.get(path);\n if (!fieldIndex) {\n fieldIndex = { valueMap: new Map(), unconstrained: [] };\n fieldIndexes.set(path, fieldIndex);\n }\n let stepList = fieldIndex.valueMap.get(value);\n if (!stepList) {\n stepList = [];\n fieldIndex.valueMap.set(value, stepList);\n }\n stepList.push(i);\n }\n }\n\n // Build unconstrained sets: for each field index, find indexable steps\n // that don't have a condition on that field.\n for (const [fieldPath, fieldIndex] of fieldIndexes) {\n for (const i of indexableStepIndices) {\n if (!hasConditionOnField(getMatch(flow.steps[i]), fieldPath)) {\n fieldIndex.unconstrained.push(i);\n }\n }\n }\n\n const hasBroadSteps = broadStepIndices.length > 0;\n\n // Build affordance index once (if provided)\n const registryIndex = buildRegistryIndex<S>(affordances);\n\n // -----------------------------------------------------------------------\n // Event emitter\n // -----------------------------------------------------------------------\n\n const listeners = new Map<string, Set<Function>>();\n\n function emit<E extends string>(event: E, data: unknown): void {\n const handlers = listeners.get(event);\n if (handlers) {\n for (const handler of handlers) handler(data);\n }\n }\n\n function on(event: string, handler: Function): void {\n let set = listeners.get(event);\n if (!set) {\n set = new Set();\n listeners.set(event, set);\n }\n set.add(handler);\n }\n\n function off(event: string, handler: Function): void {\n listeners.get(event)?.delete(handler);\n }\n\n if (debug) {\n const debugOpts = typeof debug === \"object\" ? debug : undefined;\n on(\"resolve\", createDebugSubscriber(debugOpts));\n }\n\n return {\n async resolve(ctx: TaiasContext): Promise<Affordances<S> | null> {\n const startTime = performance.now();\n const timestamp = Date.now();\n\n let matchedStep: FlowStep | undefined;\n let matchedStepIndex: number | null = null;\n let matchPhase: \"indexed\" | \"broad\" | null = null;\n const resolutionPath: string[] = [];\n let candidatesEvaluated = 0;\n const evaluations: StepEvaluation[] | undefined = detailed ? [] : undefined;\n\n // Evaluate a candidate step and track it for tracing.\n // In detailed mode, uses evaluateMatchDetailed to capture per-field\n // outcomes; in summary mode, uses the cheaper evaluateMatch.\n function tryMatch(idx: number): boolean {\n candidatesEvaluated++;\n const step = flow.steps[idx];\n const match = getMatch(step);\n\n if (detailed) {\n const { passed, fieldResults } = evaluateMatchDetailed(match, ctx);\n evaluations!.push({\n stepIndex: idx,\n match,\n result: passed ? \"matched\" : \"no-match\",\n fieldResults,\n });\n return passed;\n }\n\n return evaluateMatch(match, ctx);\n }\n\n // Phase 1: Find candidates from per-field indexes via intersection\n const applicableFieldPaths: string[] = [];\n for (const fieldPath of fieldIndexes.keys()) {\n const ctxValue = getContextValue(ctx, fieldPath);\n if (ctxValue !== undefined) {\n applicableFieldPaths.push(fieldPath);\n }\n }\n\n resolutionPath.push(...applicableFieldPaths);\n\n if (applicableFieldPaths.length > 0) {\n // Build candidate sets for each applicable field and intersect\n let candidates: Set<number> | null = null;\n\n for (const fieldPath of applicableFieldPaths) {\n const fieldIndex = fieldIndexes.get(fieldPath)!;\n const ctxValue = getContextValue(ctx, fieldPath);\n\n const fieldCandidates = new Set<number>();\n\n // Indexed matches for this field's value\n const indexed = fieldIndex.valueMap.get(ctxValue);\n if (indexed) {\n for (const idx of indexed) fieldCandidates.add(idx);\n }\n\n // Unconstrained steps (don't care about this field)\n for (const idx of fieldIndex.unconstrained) {\n fieldCandidates.add(idx);\n }\n\n if (candidates === null) {\n candidates = fieldCandidates;\n } else {\n // Intersect\n for (const idx of candidates) {\n if (!fieldCandidates.has(idx)) candidates.delete(idx);\n }\n }\n }\n\n // Evaluate full conditions on narrowed candidates (definition order)\n if (candidates && candidates.size > 0) {\n const sorted = [...candidates].sort((a, b) => a - b);\n for (const idx of sorted) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"indexed\";\n break;\n }\n }\n }\n } else if (indexableStepIndices.length > 0) {\n // Context has no fields that match any index -- evaluate all\n // indexable steps that are unconstrained on everything\n // (i.e., steps with is conditions on fields not present in context).\n // evaluateMatch handles this correctly.\n for (const idx of indexableStepIndices) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"indexed\";\n break;\n }\n }\n }\n\n // Phase 2: If no indexed match, evaluate broad steps\n if (!matchedStep && hasBroadSteps) {\n for (const idx of broadStepIndices) {\n if (tryMatch(idx)) {\n matchedStep = flow.steps[idx];\n matchedStepIndex = idx;\n matchPhase = \"broad\";\n break;\n }\n }\n }\n\n // Construct the trace and emit event on every exit path (no-match,\n // handler-returns-null, and successful match). Subscribers always\n // get complete visibility into every resolve() call.\n const trace: ResolveTrace = {\n matched: !!matchedStep,\n matchedStepIndex,\n matchedStepKind: matchedStep?.kind ?? null,\n matchedStepMatch: matchedStep ? getMatch(matchedStep) : null,\n phase: matchPhase,\n resolutionPath,\n candidatesEvaluated,\n ...(evaluations !== undefined ? { evaluations } : {}),\n };\n\n if (!matchedStep) {\n onMissingStep?.(ctx);\n\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision: null,\n affordances: null,\n } satisfies ResolveEvent<S>);\n\n return null;\n }\n\n // Evaluate the step based on its kind\n let result: StepDecision | null;\n\n if (matchedStep.kind === \"logic\") {\n result = matchedStep.statement.decision;\n } else {\n result = await matchedStep.handler(ctx);\n }\n\n if (!result) {\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision: null,\n affordances: null,\n } satisfies ResolveEvent<S>);\n\n return null;\n }\n\n if (devMode && result.nextTool === \"\") {\n warn(`Taias: nextTool for tool '${ctx.toolName}' is empty.`);\n }\n\n const decision: Decision = { ...result };\n\n const selections = selectUiAffordances<S>(decision, registryIndex, {\n devMode,\n onWarn: warn,\n });\n\n const advice = generateAdvice(result.nextTool);\n\n emit(\"resolve\", {\n flowId: flow.id,\n timestamp,\n durationMs: performance.now() - startTime,\n context: ctx,\n trace,\n decision,\n affordances: { advice, selections },\n } satisfies ResolveEvent<S>);\n\n return { advice, decision, selections };\n },\n\n on: on as Taias<S>[\"on\"],\n off: off as Taias<S>[\"off\"],\n };\n}\n","import type {\n AffordanceRegistry,\n BindingInput,\n DefaultSlots,\n HandleRegistration,\n} from \"./types\";\nimport { normalizeBinding } from \"./types\";\n\n/**\n * Mapped type that creates a registration method for each slot in S.\n * This enables fully typed custom slots via generics.\n */\nexport type AffordanceRegistrar<S extends string = DefaultSlots> = {\n [K in S]: (handleId: string, bindsTo: BindingInput) => void;\n};\n\n/**\n * Define UI affordances for a widget using a builder pattern.\n *\n * @example Default slots (backwards compatible)\n * ```ts\n * const affordances = defineAffordances((r) => {\n * r.primaryCta(\"cta.recommend\", { toolName: \"get_recommendations\" });\n * r.widgetVariant(\"variant.discovery\", { toolName: \"get_recommendations\" });\n * });\n * ```\n *\n * @example Custom slots (fully type-safe)\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\" | \"headerStyle\";\n * const affordances = defineAffordances<MySlots>((r) => {\n * r.primaryCta(\"cta.create\", { toolName: \"createUser\" });\n * r.contentArea(\"content.form\", { key: \"contentArea\", value: \"email-form\" });\n * r.headerStyle(\"header.progress\", { key: \"headerStyle\", value: \"step-1\" });\n * });\n * ```\n */\nexport function defineAffordances<S extends string = DefaultSlots>(\n builder: (r: AffordanceRegistrar<S>) => void\n): AffordanceRegistry<S> {\n const handles: HandleRegistration<S>[] = [];\n\n // Proxy creates methods on-the-fly for any slot name.\n // TypeScript ensures only valid slot names (from S) are called.\n const registrar = new Proxy({} as AffordanceRegistrar<S>, {\n get(_, slot: string) {\n return (handleId: string, bindsTo: BindingInput) => {\n handles.push({\n slot: slot as S,\n handleId,\n bindsTo: normalizeBinding(bindsTo),\n });\n };\n },\n });\n\n builder(registrar);\n return { handles };\n}\n","import type { AffordanceRegistry, DefaultSlots } from \"./types\";\nimport { makeBindingKey } from \"./types\";\n\nexport type MergeAffordancesOptions = {\n devMode?: boolean;\n onWarn?: (msg: string) => void;\n};\n\n/**\n * Merge multiple affordance registries into one.\n * Generic over slot type S for custom slot support.\n *\n * @example Default slots\n * ```ts\n * const merged = mergeAffordances([widgetA, widgetB]);\n * ```\n *\n * @example Custom slots\n * ```ts\n * type MySlots = \"primaryCta\" | \"contentArea\";\n * const merged = mergeAffordances<MySlots>([widgetA, widgetB]);\n * ```\n */\nexport function mergeAffordances<S extends string = DefaultSlots>(\n registries: AffordanceRegistry<S>[],\n opts: MergeAffordancesOptions = {}\n): AffordanceRegistry<S> {\n const devMode = !!opts.devMode;\n const warn = opts.onWarn ?? (() => {});\n\n const merged: AffordanceRegistry<S> = { handles: registries.flatMap((r) => r.handles) };\n\n if (!devMode) return merged;\n\n // Check for duplicate handleIds\n const seenHandleIds = new Set<string>();\n for (const h of merged.handles) {\n if (seenHandleIds.has(h.handleId)) {\n throw new Error(`[Taias] Duplicate handleId \"${h.handleId}\"`);\n }\n seenHandleIds.add(h.handleId);\n }\n\n // Check for ambiguous bindings (same slot + key + value)\n const seenTriples = new Set<string>();\n for (const h of merged.handles) {\n const k = makeBindingKey(h.slot, h.bindsTo);\n if (seenTriples.has(k)) {\n throw new Error(\n `[Taias] Ambiguous affordance: slot \"${h.slot}\" has multiple handles bound to (${h.bindsTo.key}=\"${h.bindsTo.value}\")`\n );\n }\n seenTriples.add(k);\n }\n\n warn(`[Taias] Loaded ${merged.handles.length} UI affordance handles`);\n return merged;\n}\n"],"mappings":"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","names":[]}
|