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 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 (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":"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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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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 (evaluateMatch(getMatch(flow.steps[idx]), ctx)) {
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) return null;
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
- return {
271
- advice: generateAdvice(result.nextTool),
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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "taias",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "type": "module",
5
5
  "description": "Taias - Give your MCP server an opinion, guide your users to achieve outcomes",
6
6
  "license": "Apache-2.0",