poly-weaver 0.15.0 → 0.15.2

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.
Files changed (136) hide show
  1. package/dist/agents/executors/types.d.ts +3 -1
  2. package/dist/agents/executors/types.d.ts.map +1 -1
  3. package/dist/agents/implementors/adapter.d.ts +2 -1
  4. package/dist/agents/implementors/adapter.d.ts.map +1 -1
  5. package/dist/agents/implementors/adapter.js +2 -2
  6. package/dist/agents/implementors/adapter.js.map +1 -1
  7. package/dist/agents/implementors/index.d.ts.map +1 -1
  8. package/dist/agents/implementors/index.js +1 -1
  9. package/dist/agents/implementors/index.js.map +1 -1
  10. package/dist/agents/implementors/prompts.d.ts.map +1 -1
  11. package/dist/agents/implementors/prompts.js +7 -3
  12. package/dist/agents/implementors/prompts.js.map +1 -1
  13. package/dist/agents/interviewers/adapter.d.ts +2 -1
  14. package/dist/agents/interviewers/adapter.d.ts.map +1 -1
  15. package/dist/agents/interviewers/adapter.js +2 -2
  16. package/dist/agents/interviewers/adapter.js.map +1 -1
  17. package/dist/agents/interviewers/index.d.ts.map +1 -1
  18. package/dist/agents/interviewers/index.js +1 -1
  19. package/dist/agents/interviewers/index.js.map +1 -1
  20. package/dist/agents/planners/adapter.d.ts +2 -1
  21. package/dist/agents/planners/adapter.d.ts.map +1 -1
  22. package/dist/agents/planners/adapter.js +2 -2
  23. package/dist/agents/planners/adapter.js.map +1 -1
  24. package/dist/agents/planners/index.d.ts.map +1 -1
  25. package/dist/agents/planners/index.js +1 -1
  26. package/dist/agents/planners/index.js.map +1 -1
  27. package/dist/agents/planners/prompts.d.ts.map +1 -1
  28. package/dist/agents/planners/prompts.js +3 -0
  29. package/dist/agents/planners/prompts.js.map +1 -1
  30. package/dist/agents/registry.d.ts +3 -0
  31. package/dist/agents/registry.d.ts.map +1 -1
  32. package/dist/agents/registry.js.map +1 -1
  33. package/dist/agents/reviewers/adapter.d.ts +2 -1
  34. package/dist/agents/reviewers/adapter.d.ts.map +1 -1
  35. package/dist/agents/reviewers/adapter.js +2 -2
  36. package/dist/agents/reviewers/adapter.js.map +1 -1
  37. package/dist/agents/reviewers/index.d.ts.map +1 -1
  38. package/dist/agents/reviewers/index.js +1 -1
  39. package/dist/agents/reviewers/index.js.map +1 -1
  40. package/dist/agents/runner.d.ts +8 -1
  41. package/dist/agents/runner.d.ts.map +1 -1
  42. package/dist/agents/runner.js +10 -2
  43. package/dist/agents/runner.js.map +1 -1
  44. package/dist/agents/simplifiers/adapter.d.ts +2 -1
  45. package/dist/agents/simplifiers/adapter.d.ts.map +1 -1
  46. package/dist/agents/simplifiers/adapter.js +2 -2
  47. package/dist/agents/simplifiers/adapter.js.map +1 -1
  48. package/dist/agents/simplifiers/index.d.ts.map +1 -1
  49. package/dist/agents/simplifiers/index.js +1 -1
  50. package/dist/agents/simplifiers/index.js.map +1 -1
  51. package/dist/config.d.ts +3 -1
  52. package/dist/config.d.ts.map +1 -1
  53. package/dist/config.js.map +1 -1
  54. package/dist/crosscheck/coordinator.d.ts.map +1 -1
  55. package/dist/crosscheck/coordinator.js +19 -7
  56. package/dist/crosscheck/coordinator.js.map +1 -1
  57. package/dist/flow/built-in/crosscheck-factory.d.ts.map +1 -1
  58. package/dist/flow/built-in/crosscheck-factory.js +8 -0
  59. package/dist/flow/built-in/crosscheck-factory.js.map +1 -1
  60. package/dist/flow/built-in/default-factory.d.ts.map +1 -1
  61. package/dist/flow/built-in/default-factory.js +8 -0
  62. package/dist/flow/built-in/default-factory.js.map +1 -1
  63. package/dist/flow/built-in/simplify-factory.d.ts.map +1 -1
  64. package/dist/flow/built-in/simplify-factory.js +6 -0
  65. package/dist/flow/built-in/simplify-factory.js.map +1 -1
  66. package/dist/flow/index.d.ts +2 -2
  67. package/dist/flow/index.d.ts.map +1 -1
  68. package/dist/flow/index.js +1 -1
  69. package/dist/flow/index.js.map +1 -1
  70. package/dist/flow/params.d.ts +14 -6
  71. package/dist/flow/params.d.ts.map +1 -1
  72. package/dist/flow/params.js +111 -8
  73. package/dist/flow/params.js.map +1 -1
  74. package/dist/flow/types.d.ts +13 -1
  75. package/dist/flow/types.d.ts.map +1 -1
  76. package/dist/flow/types.js.map +1 -1
  77. package/dist/flow-editor/tui.d.ts.map +1 -1
  78. package/dist/flow-editor/tui.js +4 -1
  79. package/dist/flow-editor/tui.js.map +1 -1
  80. package/dist/managed-spec.d.ts.map +1 -1
  81. package/dist/managed-spec.js +149 -29
  82. package/dist/managed-spec.js.map +1 -1
  83. package/dist/offscreen/resolve.d.ts.map +1 -1
  84. package/dist/offscreen/resolve.js +2 -0
  85. package/dist/offscreen/resolve.js.map +1 -1
  86. package/dist/orchestrator.d.ts.map +1 -1
  87. package/dist/orchestrator.js +15 -5
  88. package/dist/orchestrator.js.map +1 -1
  89. package/dist/preview-panel.d.ts +9 -2
  90. package/dist/preview-panel.d.ts.map +1 -1
  91. package/dist/preview-panel.js +329 -54
  92. package/dist/preview-panel.js.map +1 -1
  93. package/dist/providers/base-completion.d.ts.map +1 -1
  94. package/dist/providers/base-completion.js +41 -0
  95. package/dist/providers/base-completion.js.map +1 -1
  96. package/dist/providers/claude/strategy.d.ts.map +1 -1
  97. package/dist/providers/claude/strategy.js +24 -9
  98. package/dist/providers/claude/strategy.js.map +1 -1
  99. package/dist/providers/codex/strategy.d.ts +2 -0
  100. package/dist/providers/codex/strategy.d.ts.map +1 -1
  101. package/dist/providers/codex/strategy.js +48 -15
  102. package/dist/providers/codex/strategy.js.map +1 -1
  103. package/dist/providers/configuration-presentation.d.ts +16 -0
  104. package/dist/providers/configuration-presentation.d.ts.map +1 -0
  105. package/dist/providers/configuration-presentation.js +31 -0
  106. package/dist/providers/configuration-presentation.js.map +1 -0
  107. package/dist/providers/copilot/strategy.d.ts +2 -0
  108. package/dist/providers/copilot/strategy.d.ts.map +1 -1
  109. package/dist/providers/copilot/strategy.js +51 -21
  110. package/dist/providers/copilot/strategy.js.map +1 -1
  111. package/dist/providers/fork.d.ts +5 -1
  112. package/dist/providers/fork.d.ts.map +1 -1
  113. package/dist/providers/fork.js.map +1 -1
  114. package/dist/providers/index.d.ts +2 -1
  115. package/dist/providers/index.d.ts.map +1 -1
  116. package/dist/providers/index.js +1 -0
  117. package/dist/providers/index.js.map +1 -1
  118. package/dist/providers/live.d.ts.map +1 -1
  119. package/dist/providers/live.js +2 -0
  120. package/dist/providers/live.js.map +1 -1
  121. package/dist/providers/option-catalog.d.ts +7 -0
  122. package/dist/providers/option-catalog.d.ts.map +1 -1
  123. package/dist/providers/option-catalog.js +20 -0
  124. package/dist/providers/option-catalog.js.map +1 -1
  125. package/dist/providers/types.d.ts +17 -0
  126. package/dist/providers/types.d.ts.map +1 -1
  127. package/dist/providers/types.js +3 -0
  128. package/dist/providers/types.js.map +1 -1
  129. package/dist/startup-tui.d.ts.map +1 -1
  130. package/dist/startup-tui.js +33 -29
  131. package/dist/startup-tui.js.map +1 -1
  132. package/dist/text-editing/emacs-input.d.ts +1 -0
  133. package/dist/text-editing/emacs-input.d.ts.map +1 -1
  134. package/dist/text-editing/emacs-input.js +8 -4
  135. package/dist/text-editing/emacs-input.js.map +1 -1
  136. package/package.json +1 -1
@@ -3,7 +3,8 @@ import { breakKillRun } from "./text-editing/kill-ring.js";
3
3
  import { availableDriversForRole } from "./agents/registry.js";
4
4
  import { diagramHeight, renderFlowDiagram, computeMinWidth, detailedDiagramHeight, renderDetailedFlowDiagram, computeDetailedMinWidth } from "./flow/diagram.js";
5
5
  import { configurationGroupOf, getConfigurationGroupModelEffortKeys, getProviderForConfigurationGroup, } from "./flow/params.js";
6
- import { defaultEffort, defaultModel, defaultSubagentModel, isKnownEffort, isKnownModel, isKnownSubagentModel, providerEfforts, providerModels, subagentModels, } from "./providers/option-catalog.js";
6
+ import { defaultEffort, defaultFast, defaultModel, defaultSubagentModel, isFastAvailable, defaultContext, isKnownContext, isKnownEffort, isKnownModel, isKnownSubagentModel, providerContexts, providerEfforts, providerModels, subagentModels, } from "./providers/option-catalog.js";
7
+ import { compactProviderConfigurationTokens, } from "./providers/configuration-presentation.js";
7
8
  import { visibleLength, padToWidth, dim, bold, reset, green, red } from "./ansi.js";
8
9
  import { renderWrappedLine } from "./text-editing/soft-wrap.js";
9
10
  // ─── Constants ───────────────────────────────────────────────────────────
@@ -23,7 +24,7 @@ export function buildFieldsForTemplate(template, defaults, existingFields) {
23
24
  const fields = [];
24
25
  const groupBuckets = new Map();
25
26
  for (const param of template.params) {
26
- if (param.type !== "driver" && param.type !== "model" && param.type !== "effort") {
27
+ if (!isProviderConfigurationParam(param)) {
27
28
  continue;
28
29
  }
29
30
  const group = configurationGroupOf(param);
@@ -34,6 +35,10 @@ export function buildFieldsForTemplate(template, defaults, existingFields) {
34
35
  bucket.model = param;
35
36
  if (param.type === "effort")
36
37
  bucket.effort = param;
38
+ if (param.type === "fast")
39
+ bucket.fast = param;
40
+ if (param.type === "context")
41
+ bucket.context = param;
37
42
  groupBuckets.set(group, bucket);
38
43
  }
39
44
  const groupedConfigurations = new Set([...groupBuckets.entries()]
@@ -41,16 +46,18 @@ export function buildFieldsForTemplate(template, defaults, existingFields) {
41
46
  .map(([group]) => group));
42
47
  const emittedGroups = new Set();
43
48
  for (const param of template.params) {
44
- if (param.type === "driver" || param.type === "model" || param.type === "effort") {
49
+ if (isProviderConfigurationParam(param)) {
45
50
  const configurationGroup = configurationGroupOf(param);
46
51
  const bucket = groupBuckets.get(configurationGroup);
47
52
  const isGroupedParam = groupedConfigurations.has(configurationGroup) && bucket && (param.key === bucket.driver?.key ||
48
53
  param.key === bucket.model?.key ||
49
- param.key === bucket.effort?.key);
54
+ param.key === bucket.effort?.key ||
55
+ param.key === bucket.fast?.key ||
56
+ param.key === bucket.context?.key);
50
57
  if (isGroupedParam) {
51
58
  if (emittedGroups.has(configurationGroup))
52
59
  continue;
53
- fields.push(buildRoleGroupField(param.role, configurationGroup, bucket.driver, bucket.model, bucket.effort, template, defaults, existingFields));
60
+ fields.push(buildRoleGroupField(param.role, configurationGroup, bucket.driver, bucket.model, bucket.effort, bucket.fast, bucket.context, template, defaults, existingFields));
54
61
  emittedGroups.add(configurationGroup);
55
62
  continue;
56
63
  }
@@ -113,6 +120,38 @@ export function buildFieldsForTemplate(template, defaults, existingFields) {
113
120
  });
114
121
  break;
115
122
  }
123
+ case "fast": {
124
+ const fp = param;
125
+ const provider = getAvailableProviderForGroup(configurationGroupOf(fp), fp.role, template, defaults, existingFields);
126
+ fields.push({
127
+ kind: "toggle",
128
+ label: fp.label,
129
+ key: fp.key,
130
+ value: defaultVal ?? defaultFast(provider),
131
+ hidden: true,
132
+ });
133
+ break;
134
+ }
135
+ case "context": {
136
+ const cp = param;
137
+ const provider = getAvailableProviderForGroup(configurationGroupOf(cp), cp.role, template, defaults, existingFields);
138
+ const options = providerContexts(provider);
139
+ const requested = defaultVal;
140
+ const value = requested && isKnownContext(provider, requested)
141
+ ? requested
142
+ : defaultContext(provider) ?? "default";
143
+ fields.push({
144
+ kind: "select",
145
+ label: cp.label,
146
+ key: cp.key,
147
+ options: [...options],
148
+ value,
149
+ expanded: false,
150
+ expandedIndex: 0,
151
+ hidden: options.length === 0,
152
+ });
153
+ break;
154
+ }
116
155
  case "number": {
117
156
  const np = param;
118
157
  fields.push({
@@ -188,7 +227,7 @@ function getAvailableModelForGroup(configurationGroup, template, defaults, exist
188
227
  return value;
189
228
  return defaultModel(provider);
190
229
  }
191
- function buildRoleGroupField(role, configurationGroup, driverParam, modelParam, effortParam, template, defaults, existingFields) {
230
+ function buildRoleGroupField(role, configurationGroup, driverParam, modelParam, effortParam, fastParam, contextParam, template, defaults, existingFields) {
192
231
  const existingProvider = existingFields ? findFieldByKey(existingFields, driverParam.key) : undefined;
193
232
  const previous = existingFields?.find((f) => f.kind === "group" && f.providerKey === driverParam.key);
194
233
  const availableProviders = availableDriverOptions(role);
@@ -198,8 +237,22 @@ function buildRoleGroupField(role, configurationGroup, driverParam, modelParam,
198
237
  driverParam.default));
199
238
  const existingModel = existingFields ? findFieldByKey(existingFields, modelParam.key) : undefined;
200
239
  const existingEffort = existingFields ? findFieldByKey(existingFields, effortParam.key) : undefined;
240
+ const existingFast = fastParam && existingFields ? findFieldByKey(existingFields, fastParam.key) : undefined;
241
+ const existingContext = contextParam && existingFields ? findFieldByKey(existingFields, contextParam.key) : undefined;
201
242
  const model = (existingModel?.value ?? defaults[modelParam.key] ?? defaultModel(provider));
202
243
  const effort = (existingEffort?.value ?? defaults[effortParam.key] ?? defaultEffort(provider));
244
+ const rawFast = fastParam
245
+ ? existingFast?.value
246
+ ?? defaults[fastParam.key]
247
+ ?? defaultFast(provider)
248
+ : undefined;
249
+ const context = contextParam
250
+ ? existingContext?.value
251
+ ?? defaults[contextParam.key]
252
+ ?? defaultContext(provider)
253
+ ?? "default"
254
+ : undefined;
255
+ const focusKey = previous?.children[previous.focusedChild]?.key;
203
256
  const group = {
204
257
  kind: "group",
205
258
  key: driverParam.key,
@@ -210,6 +263,8 @@ function buildRoleGroupField(role, configurationGroup, driverParam, modelParam,
210
263
  providerKey: driverParam.key,
211
264
  modelKey: modelParam.key,
212
265
  effortKey: effortParam.key,
266
+ fastKey: fastParam?.key,
267
+ contextKey: contextParam?.key,
213
268
  children: [
214
269
  {
215
270
  kind: "select",
@@ -238,11 +293,27 @@ function buildRoleGroupField(role, configurationGroup, driverParam, modelParam,
238
293
  expanded: false,
239
294
  expandedIndex: 0,
240
295
  },
296
+ ...(contextParam ? [{
297
+ kind: "select",
298
+ label: "Context",
299
+ key: contextParam.key,
300
+ options: [],
301
+ value: context,
302
+ expanded: false,
303
+ expandedIndex: 0,
304
+ }] : []),
305
+ ...(fastParam ? [{
306
+ kind: "toggle",
307
+ label: "Fast",
308
+ key: fastParam.key,
309
+ value: rawFast,
310
+ }] : []),
241
311
  ],
242
312
  groupExpanded: previous?.groupExpanded ?? false,
243
313
  focusedChild: previous?.focusedChild ?? 0,
244
314
  };
245
- reconcileModelEffortForGroup(group);
315
+ reconcileProviderSettingsForGroup(group);
316
+ restoreGroupFocus(group, focusKey);
246
317
  return group;
247
318
  }
248
319
  // ─── Field Utilities ─────────────────────────────────────────────────────
@@ -279,6 +350,93 @@ function selectedOptionLabel(field) {
279
350
  function formatEffortValue(value, label = value) {
280
351
  return value === "ultra" ? `${red}${label}${reset}` : label;
281
352
  }
353
+ function renderCompactProviderConfigurationToken(token) {
354
+ const text = token.kind === "effort"
355
+ ? formatEffortValue(token.text)
356
+ : token.text;
357
+ return token.dimmed ? `${dim}${text}${reset}` : text;
358
+ }
359
+ function selectChild(group, key) {
360
+ const field = group.children.find((child) => child.kind === "select" && child.key === key);
361
+ if (!field)
362
+ throw new Error(`Missing select child "${key}" in ${group.key}`);
363
+ return field;
364
+ }
365
+ function optionalSelectChild(group, key) {
366
+ if (!key)
367
+ return undefined;
368
+ return group.children.find((child) => child.kind === "select" && child.key === key);
369
+ }
370
+ function toggleChild(group, key) {
371
+ const field = group.children.find((child) => child.kind === "toggle" && child.key === key);
372
+ if (!field)
373
+ throw new Error(`Missing toggle child "${key}" in ${group.key}`);
374
+ return field;
375
+ }
376
+ function optionalToggleChild(group, key) {
377
+ if (!key)
378
+ return undefined;
379
+ return group.children.find((child) => child.kind === "toggle" && child.key === key);
380
+ }
381
+ function providerChild(group) {
382
+ return selectChild(group, group.providerKey);
383
+ }
384
+ function modelChild(group) {
385
+ return selectChild(group, group.modelKey);
386
+ }
387
+ function effortChild(group) {
388
+ return selectChild(group, group.effortKey);
389
+ }
390
+ function fastChild(group) {
391
+ return optionalToggleChild(group, group.fastKey);
392
+ }
393
+ function contextChild(group) {
394
+ return optionalSelectChild(group, group.contextKey);
395
+ }
396
+ function visibleGroupChildIndexes(group) {
397
+ return group.children
398
+ .map((child, index) => child.hidden ? -1 : index)
399
+ .filter((index) => index >= 0);
400
+ }
401
+ function firstVisibleGroupChildIndex(group) {
402
+ return visibleGroupChildIndexes(group)[0] ?? 0;
403
+ }
404
+ function nextVisibleGroupChildIndex(group, direction) {
405
+ const visible = visibleGroupChildIndexes(group);
406
+ if (visible.length === 0)
407
+ return 0;
408
+ const currentIndex = visible.indexOf(group.focusedChild);
409
+ const current = currentIndex >= 0 ? currentIndex : 0;
410
+ return visible[(current + direction + visible.length) % visible.length];
411
+ }
412
+ function ensureGroupFocusVisible(group) {
413
+ if (!group.children[group.focusedChild]?.hidden)
414
+ return;
415
+ group.focusedChild = firstVisibleGroupChildIndex(group);
416
+ }
417
+ function restoreGroupFocus(group, focusKey) {
418
+ if (focusKey) {
419
+ const next = group.children.findIndex((child) => child.key === focusKey);
420
+ if (next >= 0)
421
+ group.focusedChild = next;
422
+ }
423
+ ensureGroupFocusVisible(group);
424
+ }
425
+ export function snapshotGroupToDriverMemory(memory, group, providerOverride) {
426
+ const provider = providerOverride ?? providerChild(group).value;
427
+ const fast = fastChild(group);
428
+ const entry = {
429
+ model: modelChild(group).value,
430
+ effort: effortChild(group).value,
431
+ ...(fast ? { fast: fast.value } : {}),
432
+ };
433
+ const context = contextChild(group);
434
+ if (context && isKnownContext(provider, context.value)) {
435
+ Object.assign(entry, { context: context.value });
436
+ }
437
+ memory[group.configurationGroup] ??= {};
438
+ memory[group.configurationGroup][provider] = entry;
439
+ }
282
440
  function findFieldOwnerByKey(fields, key) {
283
441
  for (const owner of fields) {
284
442
  if (owner.kind === "group") {
@@ -311,41 +469,80 @@ function defaultParamValue(param) {
311
469
  return param.default;
312
470
  case "model":
313
471
  case "effort":
472
+ case "fast":
473
+ case "context":
314
474
  return undefined;
315
475
  }
316
476
  }
317
- export function reconcileModelEffortForGroup(group, driverMemory) {
318
- const provider = group.children[0].value;
477
+ export function reconcileProviderSettingsForGroup(group, driverMemory, opts = {}) {
478
+ const provider = providerChild(group).value;
319
479
  group.value = provider;
320
- const model = group.children[1];
321
- const effort = group.children[2];
480
+ const model = modelChild(group);
481
+ const effort = effortChild(group);
482
+ const fast = fastChild(group);
483
+ const context = contextChild(group);
322
484
  model.options = [...providerModels(provider)];
323
485
  const memory = driverMemory?.[group.configurationGroup]?.[provider];
324
486
  if (memory?.model && isKnownModel(provider, memory.model)) {
325
487
  model.value = memory.model;
326
488
  }
327
- else if (!isKnownModel(provider, model.value)) {
489
+ else if (opts.providerChanged || !isKnownModel(provider, model.value)) {
328
490
  model.value = defaultModel(provider);
329
491
  }
330
492
  effort.options = [...providerEfforts(provider, model.value)];
331
493
  if (memory?.effort && isKnownEffort(provider, memory.effort, model.value)) {
332
494
  effort.value = memory.effort;
333
495
  }
334
- else if (!isKnownEffort(provider, effort.value, model.value)) {
496
+ else if (opts.providerChanged || !isKnownEffort(provider, effort.value, model.value)) {
335
497
  effort.value = defaultEffort(provider);
336
498
  }
499
+ if (fast) {
500
+ if (typeof memory?.fast === "boolean") {
501
+ fast.value = memory.fast;
502
+ }
503
+ else if (opts.providerChanged) {
504
+ fast.value = defaultFast(provider);
505
+ }
506
+ fast.hidden = !isFastAvailable(provider, model.value);
507
+ }
508
+ if (context) {
509
+ const options = providerContexts(provider);
510
+ context.options = [...options];
511
+ if (options.length === 0) {
512
+ context.hidden = true;
513
+ }
514
+ else {
515
+ context.hidden = false;
516
+ if (memory?.context && isKnownContext(provider, memory.context)) {
517
+ context.value = memory.context;
518
+ }
519
+ else if (opts.providerChanged ||
520
+ !isKnownContext(provider, context.value)) {
521
+ context.value = defaultContext(provider) ?? "default";
522
+ }
523
+ context.expandedIndex = Math.max(0, selectOptionIndex(context, context.value));
524
+ }
525
+ }
337
526
  model.expandedIndex = Math.max(0, selectOptionIndex(model, model.value));
338
527
  effort.expandedIndex = Math.max(0, selectOptionIndex(effort, effort.value));
528
+ ensureGroupFocusVisible(group);
339
529
  }
340
- function reconcileEffortForCurrentModel(group) {
341
- const provider = group.children[0].value;
342
- const model = group.children[1];
343
- const effort = group.children[2];
530
+ export function reconcileModelEffortForGroup(group, driverMemory) {
531
+ reconcileProviderSettingsForGroup(group, driverMemory);
532
+ }
533
+ function reconcileSettingsForCurrentModel(group) {
534
+ const provider = providerChild(group).value;
535
+ const model = modelChild(group);
536
+ const effort = effortChild(group);
344
537
  effort.options = [...providerEfforts(provider, model.value)];
345
538
  if (!isKnownEffort(provider, effort.value, model.value)) {
346
539
  effort.value = defaultEffort(provider);
347
540
  }
348
541
  effort.expandedIndex = Math.max(0, selectOptionIndex(effort, effort.value));
542
+ const fast = fastChild(group);
543
+ if (fast)
544
+ fast.hidden = !isFastAvailable(provider, model.value);
545
+ ensureGroupFocusVisible(group);
349
546
  }
350
547
  /**
351
548
  * Mutate `field.hidden` in place based on each template param's
@@ -390,8 +587,8 @@ export function configRowCount(fields) {
390
587
  rows += f.options.length;
391
588
  }
392
589
  else if (f.kind === "group" && f.groupExpanded) {
393
- rows += f.children.length;
394
- const expandedChild = f.children.find((c) => c.expanded);
590
+ rows += f.children.filter((child) => !child.hidden).length;
591
+ const expandedChild = f.children.find((c) => c.kind === "select" && !c.hidden && c.expanded);
395
592
  if (expandedChild)
396
593
  rows += expandedChild.options.length;
397
594
  }
@@ -402,12 +599,33 @@ export function extractFlowValues(fields, template) {
402
599
  const values = {};
403
600
  for (const param of template.params) {
404
601
  const field = findFieldByKey(fields, param.key);
602
+ if (param.type === "context" && !isApplicableContextField(fields, param)) {
603
+ continue;
604
+ }
405
605
  const value = field?.value ?? defaultParamValue(param);
406
606
  if (value !== undefined)
407
607
  values[param.key] = value;
408
608
  }
409
609
  return values;
410
610
  }
611
+ function isApplicableContextField(fields, param) {
612
+ const group = fields.find((field) => field.kind === "group" &&
613
+ field.contextKey === param.key);
614
+ if (!group)
615
+ return false;
616
+ const provider = providerChild(group).value;
617
+ return defaultContext(provider) !== undefined;
618
+ }
619
+ function isProviderConfigurationParam(param) {
620
+ return param.type === "driver" ||
621
+ param.type === "model" ||
622
+ param.type === "effort" ||
623
+ param.type === "fast" ||
624
+ param.type === "context";
625
+ }
626
+ function isDriverName(value) {
627
+ return value === "claude" || value === "codex" || value === "copilot";
628
+ }
411
629
  export function diagramWidth(flow, termCols, detailed) {
412
630
  const minW = detailed ? computeDetailedMinWidth(flow.steps) : computeMinWidth(flow.steps);
413
631
  return Math.min(minW + DIAGRAM_PADDING, termCols - 4);
@@ -518,62 +736,95 @@ function handleExpandedSelectEvent(field, event) {
518
736
  }
519
737
  }
520
738
  function handleGroupFieldEvent(group, event, driverMemory) {
521
- const beforeProvider = group.children[0].value;
522
- const beforeModel = group.children[1].value;
739
+ ensureGroupFocusVisible(group);
740
+ const provider = providerChild(group);
741
+ const model = modelChild(group);
742
+ const effort = effortChild(group);
743
+ const beforeProvider = provider.value;
744
+ const beforeModel = model.value;
523
745
  let consumed = false;
524
746
  if (!group.groupExpanded) {
747
+ const isArrow = event.type === "arrow";
748
+ const ctrl = isArrow && event.ctrl === true;
749
+ const alt = isArrow && event.alt === true;
525
750
  if (event.type === "enter") {
526
751
  group.groupExpanded = true;
527
- group.focusedChild = 0;
752
+ group.focusedChild = firstVisibleGroupChildIndex(group);
753
+ consumed = true;
754
+ }
755
+ else if (isArrow
756
+ && ctrl
757
+ && (event.direction === "left" || event.direction === "right")) {
758
+ clampSelectValue(effort, event.direction === "left" ? -1 : 1);
759
+ consumed = true;
760
+ }
761
+ else if (isArrow
762
+ && ctrl
763
+ && (event.direction === "up" || event.direction === "down")) {
764
+ cycleSelectValue(model, event.direction === "up" ? -1 : 1);
528
765
  consumed = true;
529
766
  }
530
- else if (event.type === "arrow"
531
- && event.ctrl
767
+ else if (isArrow
768
+ && alt
532
769
  && (event.direction === "left" || event.direction === "right")) {
533
- clampSelectValue(group.children[2], event.direction === "left" ? -1 : 1);
770
+ const context = contextChild(group);
771
+ if (context && !context.hidden) {
772
+ cycleSelectValue(context, event.direction === "left" ? -1 : 1);
773
+ }
534
774
  consumed = true;
535
775
  }
536
- else if (event.type === "arrow"
537
- && event.ctrl
776
+ else if (isArrow
777
+ && alt
538
778
  && (event.direction === "up" || event.direction === "down")) {
539
- cycleSelectValue(group.children[1], event.direction === "up" ? -1 : 1);
779
+ const fast = fastChild(group);
780
+ if (fast && !fast.hidden) {
781
+ fast.value = !fast.value;
782
+ }
540
783
  consumed = true;
541
784
  }
542
- else if (event.type === "arrow"
785
+ else if (isArrow
786
+ && !ctrl
787
+ && !alt
543
788
  && (event.direction === "left" || event.direction === "right")) {
544
- cycleSelectValue(group.children[0], event.direction === "left" ? -1 : 1);
789
+ cycleSelectValue(provider, event.direction === "left" ? -1 : 1);
545
790
  consumed = true;
546
791
  }
547
792
  }
548
793
  else {
549
794
  const child = group.children[group.focusedChild];
550
- if (child.expanded) {
795
+ if (child?.kind === "select" && child.expanded) {
551
796
  consumed = handleExpandedSelectEvent(child, event);
552
797
  }
553
798
  else {
554
799
  switch (event.type) {
555
800
  case "arrow":
556
801
  if (event.direction === "up") {
557
- group.focusedChild = (group.focusedChild + group.children.length - 1) %
558
- group.children.length;
802
+ group.focusedChild = nextVisibleGroupChildIndex(group, -1);
559
803
  }
560
804
  else if (event.direction === "down") {
561
- group.focusedChild = (group.focusedChild + 1) % group.children.length;
805
+ group.focusedChild = nextVisibleGroupChildIndex(group, 1);
562
806
  }
563
- else {
564
- cycleSelectValue(child, event.direction === "left" ? -1 : 1);
807
+ else if (child) {
808
+ handleFieldAdjust(child, event.direction === "left" ? -1 : 1);
565
809
  }
566
810
  consumed = true;
567
811
  break;
568
812
  case "enter":
569
- child.expanded = true;
570
- child.expandedIndex = Math.max(0, selectOptionIndex(child, child.value));
813
+ if (child?.kind === "select") {
814
+ child.expanded = true;
815
+ child.expandedIndex = Math.max(0, selectOptionIndex(child, child.value));
816
+ }
817
+ else if (child?.kind === "toggle") {
818
+ child.value = !child.value;
819
+ }
571
820
  consumed = true;
572
821
  break;
573
822
  case "escape":
574
823
  group.groupExpanded = false;
575
- for (const c of group.children)
576
- c.expanded = false;
824
+ for (const c of group.children) {
825
+ if (c.kind === "select")
826
+ c.expanded = false;
827
+ }
577
828
  consumed = true;
578
829
  break;
579
830
  case "tab":
@@ -582,13 +833,16 @@ function handleGroupFieldEvent(group, event, driverMemory) {
582
833
  }
583
834
  }
584
835
  }
585
- if (group.children[0].value !== beforeProvider) {
586
- reconcileModelEffortForGroup(group, driverMemory);
836
+ if (provider.value !== beforeProvider) {
837
+ if (driverMemory && isDriverName(beforeProvider)) {
838
+ snapshotGroupToDriverMemory(driverMemory, group, beforeProvider);
839
+ }
840
+ reconcileProviderSettingsForGroup(group, driverMemory, { providerChanged: true });
587
841
  }
588
- else if (group.children[1].value !== beforeModel) {
589
- reconcileEffortForCurrentModel(group);
842
+ else if (model.value !== beforeModel) {
843
+ reconcileSettingsForCurrentModel(group);
590
844
  }
591
- group.value = group.children[0].value;
845
+ group.value = provider.value;
592
846
  return consumed;
593
847
  }
594
848
  function handleNumberEditingEvent(field, event) {
@@ -706,9 +960,20 @@ export function renderConfigFields(fields, focusedIndex, isConfigFocused, cols)
706
960
  : `${dim}off${reset}`;
707
961
  }
708
962
  else {
709
- const [provider, model, effort] = field.children;
963
+ const provider = providerChild(field);
964
+ const model = modelChild(field);
965
+ const effort = effortChild(field);
966
+ const fast = fastChild(field);
967
+ const context = contextChild(field);
968
+ const summary = compactProviderConfigurationTokens({
969
+ driver: provider.value,
970
+ model: model.value,
971
+ effort: effort.value,
972
+ fast: fast?.value,
973
+ context: context && !context.hidden ? context.value : undefined,
974
+ }).map(renderCompactProviderConfigurationToken).join(` ${dim}/${reset} `);
710
975
  const marker = field.groupExpanded ? `${bold}▴${reset}` : `${dim}▾${reset}`;
711
- valueStr = `${provider.value} ${dim}/${reset} ${model.value} ${dim}/${reset} ${formatEffortValue(effort.value)} ${marker}`;
976
+ valueStr = `${summary} ${marker}`;
712
977
  }
713
978
  lines.push(padToWidth(` ${indicator} ${dimWrap}${labelStr}${dimEnd}${valueStr}`, cols));
714
979
  // Expanded dropdown options
@@ -723,17 +988,27 @@ export function renderConfigFields(fields, focusedIndex, isConfigFocused, cols)
723
988
  else if (field.kind === "group" && field.groupExpanded) {
724
989
  for (let childIdx = 0; childIdx < field.children.length; childIdx++) {
725
990
  const child = field.children[childIdx];
991
+ if (child.hidden)
992
+ continue;
726
993
  const childFocused = focused && field.focusedChild === childIdx;
727
994
  const childIndicator = childFocused ? `${bold}▸${reset}` : " ";
728
995
  const childLabel = child.label.padEnd(LABEL_WIDTH - 2);
729
- const childDisplayValue = child.key === field.effortKey
730
- ? formatEffortValue(child.value)
731
- : child.value;
732
- const childValue = child.expanded
733
- ? `${childDisplayValue} ${bold}▴${reset}`
734
- : `${childDisplayValue} ${dim}▾${reset}`;
996
+ let childValue;
997
+ if (child.kind === "toggle") {
998
+ childValue = child.value
999
+ ? `${green}on${reset}`
1000
+ : `${dim}off${reset}`;
1001
+ }
1002
+ else {
1003
+ const childDisplayValue = child.key === field.effortKey
1004
+ ? formatEffortValue(child.value)
1005
+ : child.value;
1006
+ childValue = child.expanded
1007
+ ? `${childDisplayValue} ${bold}▴${reset}`
1008
+ : `${childDisplayValue} ${dim}▾${reset}`;
1009
+ }
735
1010
  lines.push(padToWidth(` ${childIndicator} ${dimWrap}${childLabel}${dimEnd}${childValue}`, cols));
736
- if (child.expanded) {
1011
+ if (child.kind === "select" && child.expanded) {
737
1012
  for (let j = 0; j < child.options.length; j++) {
738
1013
  const opt = child.options[j];
739
1014
  const optIndicator = j === child.expandedIndex ? `${bold}▸${reset}` : " ";