taias 0.8.0 → 0.9.1

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/LICENSE CHANGED
@@ -186,7 +186,7 @@ Apache License
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright 2025 Taias (github.com/taias-mcp)
189
+ Copyright 2025 Taias (github.com/taias-xyz)
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  <div align="center">
2
2
 
3
3
  <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/taias-mcp/taias/main/static/taias_dark.png">
5
- <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/taias-mcp/taias/main/static/taias_light.png">
6
- <img width="300" alt="Taias logo" src="https://raw.githubusercontent.com/taias-mcp/taias/main/static/taias_light.png">
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/taias-xyz/taias/main/static/taias_dark.png">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/taias-xyz/taias/main/static/taias_light.png">
6
+ <img width="300" alt="Taias logo" src="https://raw.githubusercontent.com/taias-xyz/taias/main/static/taias_light.png">
7
7
  </picture>
8
8
 
9
9
  <br/>
@@ -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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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) return null;
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
- return {
300
- advice: generateAdvice(result.nextTool),
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,
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACiCO,SAAS,WACd,QACA,SACgB;AAChB,QAAM,QAAoB,CAAC;AAE3B,QAAM,cAA2B;AAAA,IAC/B,KAAK,OAAuB,OAAwB;AAClD,UAAI,OAAO,UAAU,YAAY;AAC/B,cAAM,KAAK,EAAE,MAAM,WAAW,OAAO,SAAS,MAAM,CAAC;AAAA,MACvD,OAAO;AACL,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,WAAW;AAAA,YACT;AAAA,YACA,UAAU;AAAA,UACZ;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,WAAW;AAEnB,SAAO;AAAA,IACL,IAAI;AAAA,IACJ;AAAA,EACF;AACF;;;ACRO,SAAS,iBAAiB,OAA8B;AAC7D,MAAI,cAAc,MAAO,QAAO,EAAE,KAAK,YAAY,OAAO,MAAM,SAAS;AACzE,SAAO,EAAE,KAAK,MAAM,KAAK,OAAO,MAAM,MAAM;AAC9C;AAMO,SAAS,eAAe,MAAc,SAA0B;AACrE,SAAO,GAAG,IAAI,KAAK,QAAQ,GAAG,KAAK,QAAQ,KAAK;AAClD;;;AC3CO,SAAS,mBACd,UACkB;AAClB,QAAM,eAAe,oBAAI,IAAmC;AAC5D,QAAM,QAAQ,oBAAI,IAAO;AACzB,QAAM,aAAa,oBAAI,IAAe;AAEtC,MAAI,CAAC,SAAU,QAAO,EAAE,cAAc,OAAO,WAAW;AAExD,aAAW,KAAK,SAAS,SAAS;AAChC,UAAM,IAAI,EAAE,IAAI;AAGhB,UAAM,cAAc,WAAW,IAAI,EAAE,IAAI;AACzC,QAAI,eAAe,gBAAgB,EAAE,QAAQ,KAAK;AAChD,YAAM,IAAI;AAAA,QACR,iBAAiB,EAAE,IAAI,2CAA2C,WAAW,UAAU,EAAE,QAAQ,GAAG;AAAA,MAEtG;AAAA,IACF;AACA,eAAW,IAAI,EAAE,MAAM,EAAE,QAAQ,GAAG;AAEpC,iBAAa,IAAI,eAAe,EAAE,MAAM,EAAE,OAAO,GAAG,CAAC;AAAA,EACvD;AAEA,SAAO,EAAE,cAAc,OAAO,WAAW;AAC3C;;;ACjCO,SAAS,oBACd,UACA,OACA,OAAsB,CAAC,GACN;AACjB,QAAM,UAAU,CAAC,CAAC,KAAK;AACvB,QAAM,OAAO,KAAK,WAAW,MAAM;AAAA,EAAC;AAEpC,QAAM,aAA8B,CAAC;AAErC,aAAW,QAAQ,MAAM,OAAO;AAE9B,UAAM,QAAQ,MAAM,WAAW,IAAI,IAAI;AACvC,QAAI,CAAC,MAAO;AAEZ,UAAM,QAAQ,SAAS,KAAK;AAC5B,QAAI,CAAC,MAAO;AAEZ,UAAM,IAAI,eAAe,MAAM,EAAE,KAAK,OAAO,MAAM,CAAC;AACpD,UAAM,SAAS,MAAM,aAAa,IAAI,CAAC;AAEvC,QAAI,CAAC,QAAQ;AACX,UAAI,QAAS,MAAK,mCAAmC,IAAI,UAAU,KAAK,KAAK,KAAK,GAAG;AACrF;AAAA,IACF;AAEA,IAAC,WAAuC,IAAI,IAAI;AAAA,MAC9C,UAAU,OAAO;AAAA,MACjB,SAAS,OAAO;AAAA,IAClB;AAAA,EACF;AAEA,SAAO;AACT;;;ACvCA,SAAS,eAAe,UAA0B;AAChD,SAAO,0DAA0D,QAAQ;AAC3E;AASA,SAAS,kBAAkB,WAAsB,OAAyB;AACxE,MAAI,QAAQ,UAAW,QAAO,UAAU,UAAU;AAClD,MAAI,WAAW,UAAW,QAAO,UAAU,UAAU;AACrD,SAAO;AACT;AAaA,SAAS,cAAc,OAAuB,KAA4B;AACxE,MAAI,MAAM,YAAY,CAAC,kBAAkB,MAAM,UAAU,IAAI,QAAQ,EAAG,QAAO;AAE/E,MAAI,MAAM,QAAQ;AAChB,QAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,eAAW,CAAC,KAAK,SAAS,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC3D,UAAI,CAAC,kBAAkB,WAAW,IAAI,OAAO,GAAG,CAAC,EAAG,QAAO;AAAA,IAC7D;AAAA,EACF;AAEA,MAAI,MAAM,QAAQ;AAChB,QAAI,CAAC,IAAI,OAAQ,QAAO;AACxB,eAAW,CAAC,KAAK,SAAS,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AAC3D,UAAI,CAAC,kBAAkB,WAAW,IAAI,OAAO,GAAG,CAAC,EAAG,QAAO;AAAA,IAC7D;AAAA,EACF;AAEA,SAAO;AACT;AAcA,SAAS,oBAAoB,OAAgE;AAC3F,QAAM,aAAsD,CAAC;AAC7D,MAAI,MAAM,YAAY,QAAQ,MAAM,UAAU;AAC5C,eAAW,KAAK,EAAE,MAAM,YAAY,OAAO,MAAM,SAAS,GAAG,CAAC;AAAA,EAChE;AACA,MAAI,MAAM,QAAQ;AAChB,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AACtD,UAAI,QAAQ,KAAM,YAAW,KAAK,EAAE,MAAM,UAAU,GAAG,IAAI,OAAO,KAAK,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AACA,MAAI,MAAM,QAAQ;AAChB,eAAW,CAAC,KAAK,IAAI,KAAK,OAAO,QAAQ,MAAM,MAAM,GAAG;AACtD,UAAI,QAAQ,KAAM,YAAW,KAAK,EAAE,MAAM,UAAU,GAAG,IAAI,OAAO,KAAK,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AACA,SAAO;AACT;AAKA,SAAS,oBAAoB,OAAuB,MAAuB;AACzE,MAAI,SAAS,WAAY,QAAO,CAAC,CAAC,MAAM;AACxC,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO,CAAC,CAAC,MAAM,SAAS,KAAK,MAAM,CAAC,CAAC;AACrE,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO,CAAC,CAAC,MAAM,SAAS,KAAK,MAAM,CAAC,CAAC;AACrE,SAAO;AACT;AAKA,SAAS,gBAAgB,KAAmB,MAAuB;AACjE,MAAI,SAAS,WAAY,QAAO,IAAI;AACpC,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO,IAAI,SAAS,KAAK,MAAM,CAAC,CAAC;AACjE,MAAI,KAAK,WAAW,SAAS,EAAG,QAAO,IAAI,SAAS,KAAK,MAAM,CAAC,CAAC;AACjE,SAAO;AACT;AAYA,SAAS,SAAS,MAAgC;AAChD,SAAO,KAAK,SAAS,UAAU,KAAK,UAAU,QAAQ,KAAK;AAC7D;AAKA,SAAS,eAAe,OAA+B;AACrD,SAAO,KAAK,UAAU,KAAK;AAC7B;AA6BO,SAAS,YACd,SACU;AACV,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,OAAO,WAAW,CAAC,QAAgB,QAAQ,KAAK,GAAG;AAGzD,MAAI,SAAS;AACX,UAAM,WAAW,oBAAI,IAAY;AACjC,eAAW,QAAQ,KAAK,OAAO;AAC7B,YAAM,MAAM,eAAe,SAAS,IAAI,CAAC;AACzC,UAAI,SAAS,IAAI,GAAG,GAAG;AACrB,cAAM,IAAI;AAAA,UACR,6CAA6C,KAAK,EAAE,+DAA+D,GAAG;AAAA,QACxH;AAAA,MACF;AACA,eAAS,IAAI,GAAG;AAAA,IAClB;AAAA,EACF;AAMA,QAAM,eAAe,oBAAI,IAAwB;AACjD,QAAM,uBAAiC,CAAC;AACxC,QAAM,mBAA6B,CAAC;AAEpC,WAAS,IAAI,GAAG,IAAI,KAAK,MAAM,QAAQ,KAAK;AAC1C,UAAM,QAAQ,SAAS,KAAK,MAAM,CAAC,CAAC;AACpC,UAAM,eAAe,oBAAoB,KAAK;AAE9C,QAAI,aAAa,WAAW,GAAG;AAC7B,uBAAiB,KAAK,CAAC;AACvB;AAAA,IACF;AAEA,yBAAqB,KAAK,CAAC;AAE3B,eAAW,EAAE,MAAM,MAAM,KAAK,cAAc;AAC1C,UAAI,aAAa,aAAa,IAAI,IAAI;AACtC,UAAI,CAAC,YAAY;AACf,qBAAa,EAAE,UAAU,oBAAI,IAAI,GAAG,eAAe,CAAC,EAAE;AACtD,qBAAa,IAAI,MAAM,UAAU;AAAA,MACnC;AACA,UAAI,WAAW,WAAW,SAAS,IAAI,KAAK;AAC5C,UAAI,CAAC,UAAU;AACb,mBAAW,CAAC;AACZ,mBAAW,SAAS,IAAI,OAAO,QAAQ;AAAA,MACzC;AACA,eAAS,KAAK,CAAC;AAAA,IACjB;AAAA,EACF;AAIA,aAAW,CAAC,WAAW,UAAU,KAAK,cAAc;AAClD,eAAW,KAAK,sBAAsB;AACpC,UAAI,CAAC,oBAAoB,SAAS,KAAK,MAAM,CAAC,CAAC,GAAG,SAAS,GAAG;AAC5D,mBAAW,cAAc,KAAK,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAAgB,iBAAiB,SAAS;AAGhD,QAAM,gBAAgB,mBAAsB,WAAW;AAEvD,SAAO;AAAA,IACL,MAAM,QAAQ,KAAmD;AAC/D,UAAI;AAGJ,YAAM,uBAAiC,CAAC;AACxC,iBAAW,aAAa,aAAa,KAAK,GAAG;AAC3C,cAAM,WAAW,gBAAgB,KAAK,SAAS;AAC/C,YAAI,aAAa,QAAW;AAC1B,+BAAqB,KAAK,SAAS;AAAA,QACrC;AAAA,MACF;AAEA,UAAI,qBAAqB,SAAS,GAAG;AAEnC,YAAI,aAAiC;AAErC,mBAAW,aAAa,sBAAsB;AAC5C,gBAAM,aAAa,aAAa,IAAI,SAAS;AAC7C,gBAAM,WAAW,gBAAgB,KAAK,SAAS;AAE/C,gBAAM,kBAAkB,oBAAI,IAAY;AAGxC,gBAAM,UAAU,WAAW,SAAS,IAAI,QAAQ;AAChD,cAAI,SAAS;AACX,uBAAW,OAAO,QAAS,iBAAgB,IAAI,GAAG;AAAA,UACpD;AAGA,qBAAW,OAAO,WAAW,eAAe;AAC1C,4BAAgB,IAAI,GAAG;AAAA,UACzB;AAEA,cAAI,eAAe,MAAM;AACvB,yBAAa;AAAA,UACf,OAAO;AAEL,uBAAW,OAAO,YAAY;AAC5B,kBAAI,CAAC,gBAAgB,IAAI,GAAG,EAAG,YAAW,OAAO,GAAG;AAAA,YACtD;AAAA,UACF;AAAA,QACF;AAGA,YAAI,cAAc,WAAW,OAAO,GAAG;AACrC,gBAAM,SAAS,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AACnD,qBAAW,OAAO,QAAQ;AACxB,gBAAI,cAAc,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG;AACjD,4BAAc,KAAK,MAAM,GAAG;AAC5B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF,WAAW,qBAAqB,SAAS,GAAG;AAK1C,mBAAW,OAAO,sBAAsB;AACtC,cAAI,cAAc,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG;AACjD,0BAAc,KAAK,MAAM,GAAG;AAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAGA,UAAI,CAAC,eAAe,eAAe;AACjC,mBAAW,OAAO,kBAAkB;AAClC,cAAI,cAAc,SAAS,KAAK,MAAM,GAAG,CAAC,GAAG,GAAG,GAAG;AACjD,0BAAc,KAAK,MAAM,GAAG;AAC5B;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAEA,UAAI,CAAC,aAAa;AAChB,wBAAgB,GAAG;AACnB,eAAO;AAAA,MACT;AAGA,UAAI;AAEJ,UAAI,YAAY,SAAS,SAAS;AAChC,iBAAS,YAAY,UAAU;AAAA,MACjC,OAAO;AACL,iBAAS,MAAM,YAAY,QAAQ,GAAG;AAAA,MACxC;AAEA,UAAI,CAAC,OAAQ,QAAO;AAEpB,UAAI,WAAW,OAAO,aAAa,IAAI;AACrC,aAAK,6BAA6B,IAAI,QAAQ,aAAa;AAAA,MAC7D;AAEA,YAAM,WAAqB,EAAE,GAAG,OAAO;AAEvC,YAAM,aAAa,oBAAuB,UAAU,eAAe;AAAA,QACjE;AAAA,QACA,QAAQ;AAAA,MACV,CAAC;AAED,aAAO;AAAA,QACL,QAAQ,eAAe,OAAO,QAAQ;AAAA,QACtC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AChTO,SAAS,kBACd,SACuB;AACvB,QAAM,UAAmC,CAAC;AAI1C,QAAM,YAAY,IAAI,MAAM,CAAC,GAA6B;AAAA,IACxD,IAAI,GAAG,MAAc;AACnB,aAAO,CAAC,UAAkB,YAA0B;AAClD,gBAAQ,KAAK;AAAA,UACX;AAAA,UACA;AAAA,UACA,SAAS,iBAAiB,OAAO;AAAA,QACnC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AAED,UAAQ,SAAS;AACjB,SAAO,EAAE,QAAQ;AACnB;;;ACnCO,SAAS,iBACd,YACA,OAAgC,CAAC,GACV;AACvB,QAAM,UAAU,CAAC,CAAC,KAAK;AACvB,QAAM,OAAO,KAAK,WAAW,MAAM;AAAA,EAAC;AAEpC,QAAM,SAAgC,EAAE,SAAS,WAAW,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE;AAEtF,MAAI,CAAC,QAAS,QAAO;AAGrB,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,KAAK,OAAO,SAAS;AAC9B,QAAI,cAAc,IAAI,EAAE,QAAQ,GAAG;AACjC,YAAM,IAAI,MAAM,+BAA+B,EAAE,QAAQ,GAAG;AAAA,IAC9D;AACA,kBAAc,IAAI,EAAE,QAAQ;AAAA,EAC9B;AAGA,QAAM,cAAc,oBAAI,IAAY;AACpC,aAAW,KAAK,OAAO,SAAS;AAC9B,UAAM,IAAI,eAAe,EAAE,MAAM,EAAE,OAAO;AAC1C,QAAI,YAAY,IAAI,CAAC,GAAG;AACtB,YAAM,IAAI;AAAA,QACR,uCAAuC,EAAE,IAAI,oCAAoC,EAAE,QAAQ,GAAG,KAAK,EAAE,QAAQ,KAAK;AAAA,MACpH;AAAA,IACF;AACA,gBAAY,IAAI,CAAC;AAAA,EACnB;AAEA,OAAK,kBAAkB,OAAO,QAAQ,MAAM,wBAAwB;AACpE,SAAO;AACT;","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":[]}