@synergenius/flow-weaver-pack-weaver 0.9.200 → 0.9.201

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 (181) hide show
  1. package/dist/ai-chat-provider.js +5 -5
  2. package/dist/ai-chat-provider.js.map +1 -1
  3. package/dist/bot/acceptance-merge.d.ts +21 -0
  4. package/dist/bot/acceptance-merge.d.ts.map +1 -0
  5. package/dist/bot/acceptance-merge.js +46 -0
  6. package/dist/bot/acceptance-merge.js.map +1 -0
  7. package/dist/bot/ai-client.d.ts +14 -2
  8. package/dist/bot/ai-client.d.ts.map +1 -1
  9. package/dist/bot/ai-client.js +71 -24
  10. package/dist/bot/ai-client.js.map +1 -1
  11. package/dist/bot/assistant-tools.js +3 -3
  12. package/dist/bot/assistant-tools.js.map +1 -1
  13. package/dist/bot/audit-logger.d.ts.map +1 -1
  14. package/dist/bot/audit-logger.js +34 -14
  15. package/dist/bot/audit-logger.js.map +1 -1
  16. package/dist/bot/audit-trail.d.ts +67 -0
  17. package/dist/bot/audit-trail.d.ts.map +1 -0
  18. package/dist/bot/audit-trail.js +153 -0
  19. package/dist/bot/audit-trail.js.map +1 -0
  20. package/dist/bot/behavior-defaults.d.ts +1 -1
  21. package/dist/bot/behavior-defaults.d.ts.map +1 -1
  22. package/dist/bot/behavior-defaults.js +7 -3
  23. package/dist/bot/behavior-defaults.js.map +1 -1
  24. package/dist/bot/capability-registry.d.ts +9 -0
  25. package/dist/bot/capability-registry.d.ts.map +1 -1
  26. package/dist/bot/capability-registry.js +81 -27
  27. package/dist/bot/capability-registry.js.map +1 -1
  28. package/dist/bot/capability-types.d.ts +10 -0
  29. package/dist/bot/capability-types.d.ts.map +1 -1
  30. package/dist/bot/cli-provider.d.ts.map +1 -1
  31. package/dist/bot/cli-provider.js +8 -7
  32. package/dist/bot/cli-provider.js.map +1 -1
  33. package/dist/bot/preflight.d.ts +48 -0
  34. package/dist/bot/preflight.d.ts.map +1 -0
  35. package/dist/bot/preflight.js +247 -0
  36. package/dist/bot/preflight.js.map +1 -0
  37. package/dist/bot/provider-shim.d.ts +74 -0
  38. package/dist/bot/provider-shim.d.ts.map +1 -0
  39. package/dist/bot/provider-shim.js +176 -0
  40. package/dist/bot/provider-shim.js.map +1 -0
  41. package/dist/bot/runner.d.ts +2 -0
  42. package/dist/bot/runner.d.ts.map +1 -1
  43. package/dist/bot/runner.js +60 -17
  44. package/dist/bot/runner.js.map +1 -1
  45. package/dist/bot/step-executor.d.ts.map +1 -1
  46. package/dist/bot/step-executor.js +72 -115
  47. package/dist/bot/step-executor.js.map +1 -1
  48. package/dist/bot/swarm-controller.d.ts +2 -0
  49. package/dist/bot/swarm-controller.d.ts.map +1 -1
  50. package/dist/bot/swarm-controller.js +92 -20
  51. package/dist/bot/swarm-controller.js.map +1 -1
  52. package/dist/bot/task-create-handler.d.ts +37 -0
  53. package/dist/bot/task-create-handler.d.ts.map +1 -0
  54. package/dist/bot/task-create-handler.js +124 -0
  55. package/dist/bot/task-create-handler.js.map +1 -0
  56. package/dist/bot/task-store.d.ts +1 -0
  57. package/dist/bot/task-store.d.ts.map +1 -1
  58. package/dist/bot/task-store.js +61 -0
  59. package/dist/bot/task-store.js.map +1 -1
  60. package/dist/bot/types.d.ts +1 -1
  61. package/dist/bot/types.d.ts.map +1 -1
  62. package/dist/bot/weaver-tools.d.ts.map +1 -1
  63. package/dist/bot/weaver-tools.js +7 -39
  64. package/dist/bot/weaver-tools.js.map +1 -1
  65. package/dist/node-types/agent-execute.d.ts +25 -8
  66. package/dist/node-types/agent-execute.d.ts.map +1 -1
  67. package/dist/node-types/agent-execute.js +89 -23
  68. package/dist/node-types/agent-execute.js.map +1 -1
  69. package/dist/node-types/bot-report.d.ts.map +1 -1
  70. package/dist/node-types/bot-report.js +24 -3
  71. package/dist/node-types/bot-report.js.map +1 -1
  72. package/dist/node-types/plan-task.d.ts +8 -17
  73. package/dist/node-types/plan-task.d.ts.map +1 -1
  74. package/dist/node-types/plan-task.js +217 -256
  75. package/dist/node-types/plan-task.js.map +1 -1
  76. package/dist/node-types/review-result.js +8 -6
  77. package/dist/node-types/review-result.js.map +1 -1
  78. package/dist/palindrome.d.ts +9 -0
  79. package/dist/palindrome.d.ts.map +1 -0
  80. package/dist/palindrome.js +14 -0
  81. package/dist/palindrome.js.map +1 -0
  82. package/dist/ui/approval-card.js +91 -82
  83. package/dist/ui/bot-activity.js +73 -56
  84. package/dist/ui/bot-config.js +48 -31
  85. package/dist/ui/bot-dashboard.js +52 -36
  86. package/dist/ui/bot-panel.js +230 -228
  87. package/dist/ui/bot-slot-card.js +100 -90
  88. package/dist/ui/bot-status.js +37 -15
  89. package/dist/ui/budget-bar.js +57 -31
  90. package/dist/ui/capability-editor.js +447 -378
  91. package/dist/ui/chat-task-result.js +78 -71
  92. package/dist/ui/decision-log.js +68 -81
  93. package/dist/ui/genesis-block.js +86 -95
  94. package/dist/ui/instance-stream-view.js +722 -0
  95. package/dist/ui/profile-card.js +96 -221
  96. package/dist/ui/profile-editor.js +532 -575
  97. package/dist/ui/settings-section.js +41 -45
  98. package/dist/ui/swarm-controls.js +212 -135
  99. package/dist/ui/swarm-dashboard.js +3992 -2715
  100. package/dist/ui/task-detail-view.js +415 -521
  101. package/dist/ui/task-editor.js +339 -390
  102. package/dist/ui/task-pool-list.js +60 -55
  103. package/dist/workflows/src/palindrome.d.ts +11 -0
  104. package/dist/workflows/src/palindrome.d.ts.map +1 -0
  105. package/dist/workflows/src/palindrome.js +16 -0
  106. package/dist/workflows/src/palindrome.js.map +1 -0
  107. package/dist/workflows/tests/palindrome.test.d.ts +2 -0
  108. package/dist/workflows/tests/palindrome.test.d.ts.map +1 -0
  109. package/dist/workflows/tests/palindrome.test.js +41 -0
  110. package/dist/workflows/tests/palindrome.test.js.map +1 -0
  111. package/dist/workflows/weaver-bot-batch.js +1 -1
  112. package/dist/workflows/weaver-bot-batch.js.map +1 -1
  113. package/dist/workflows/weaver-bot.js +1 -1
  114. package/dist/workflows/weaver-bot.js.map +1 -1
  115. package/flowweaver.manifest.json +1 -1
  116. package/package.json +8 -2
  117. package/src/ai-chat-provider.ts +5 -5
  118. package/src/bot/acceptance-merge.ts +62 -0
  119. package/src/bot/ai-client.ts +77 -21
  120. package/src/bot/assistant-tools.ts +3 -3
  121. package/src/bot/audit-logger.ts +42 -14
  122. package/src/bot/audit-trail.ts +211 -0
  123. package/src/bot/behavior-defaults.ts +7 -2
  124. package/src/bot/capability-registry.ts +84 -28
  125. package/src/bot/capability-types.ts +11 -0
  126. package/src/bot/cli-provider.ts +8 -7
  127. package/src/bot/preflight.ts +285 -0
  128. package/src/bot/provider-shim.ts +218 -0
  129. package/src/bot/runner.ts +68 -20
  130. package/src/bot/step-executor.ts +69 -127
  131. package/src/bot/swarm-controller.ts +94 -20
  132. package/src/bot/task-create-handler.ts +164 -0
  133. package/src/bot/task-store.ts +76 -0
  134. package/src/bot/types.ts +4 -1
  135. package/src/bot/weaver-tools.ts +7 -45
  136. package/src/node-types/agent-execute.ts +102 -16
  137. package/src/node-types/bot-report.ts +24 -3
  138. package/src/node-types/plan-task.ts +238 -280
  139. package/src/node-types/review-result.ts +8 -6
  140. package/src/palindrome.ts +14 -0
  141. package/src/ui/approval-card.tsx +78 -62
  142. package/src/ui/bot-activity.tsx +12 -10
  143. package/src/ui/bot-config.tsx +12 -10
  144. package/src/ui/bot-dashboard.tsx +13 -11
  145. package/src/ui/bot-panel.tsx +189 -171
  146. package/src/ui/bot-slot-card.tsx +125 -70
  147. package/src/ui/bot-status.tsx +4 -4
  148. package/src/ui/budget-bar.tsx +86 -25
  149. package/src/ui/capability-editor.tsx +392 -257
  150. package/src/ui/chat-task-result.tsx +81 -78
  151. package/src/ui/decision-log.tsx +76 -73
  152. package/src/ui/genesis-block.tsx +91 -61
  153. package/src/ui/instance-stream-view.tsx +861 -0
  154. package/src/ui/profile-card.tsx +195 -168
  155. package/src/ui/profile-editor.tsx +453 -370
  156. package/src/ui/settings-section.tsx +46 -39
  157. package/src/ui/swarm-controls.tsx +252 -123
  158. package/src/ui/swarm-dashboard.tsx +999 -466
  159. package/src/ui/task-detail-view.tsx +485 -428
  160. package/src/ui/task-editor.tsx +329 -271
  161. package/src/ui/task-pool-list.tsx +68 -62
  162. package/src/workflows/src/palindrome.ts +16 -0
  163. package/src/workflows/tests/palindrome.test.ts +49 -0
  164. package/src/workflows/weaver-bot-batch.ts +1 -1
  165. package/src/workflows/weaver-bot.ts +1 -1
  166. package/dist/ui/bot-constants.d.ts +0 -14
  167. package/dist/ui/bot-constants.d.ts.map +0 -1
  168. package/dist/ui/bot-constants.js +0 -189
  169. package/dist/ui/bot-constants.js.map +0 -1
  170. package/dist/ui/steer-api.d.ts +0 -7
  171. package/dist/ui/steer-api.d.ts.map +0 -1
  172. package/dist/ui/steer-api.js +0 -11
  173. package/dist/ui/steer-api.js.map +0 -1
  174. package/dist/ui/trace-to-timeline.d.ts +0 -91
  175. package/dist/ui/trace-to-timeline.d.ts.map +0 -1
  176. package/dist/ui/trace-to-timeline.js +0 -116
  177. package/dist/ui/trace-to-timeline.js.map +0 -1
  178. package/dist/ui/use-stream-timeline.d.ts +0 -50
  179. package/dist/ui/use-stream-timeline.d.ts.map +0 -1
  180. package/dist/ui/use-stream-timeline.js +0 -245
  181. package/dist/ui/use-stream-timeline.js.map +0 -1
@@ -1,15 +1,12 @@
1
1
  /**
2
2
  * ProfileEditor — unified component for creating and editing bot profiles.
3
3
  * Supports the full ProfileBehavior system: phase config, escalation, scope.
4
- *
5
- * Pattern: CommonJS require for platform deps, React.createElement throughout.
6
4
  */
7
- const React = require('react');
8
- const { useState, useEffect, useCallback } = React;
9
- const {
10
- Flex, Typography, Input, Button, IconButton, Icon, SectionTitle, Checkbox, Chip, Field, Table,
11
- IconPicker, ColorPicker, Switch, ToggleGroup, CollapsibleSection, ScrollArea, toast, usePackWorkspace,
12
- } = require('@fw/plugin-ui-kit');
5
+ import React, { useState, useEffect, useCallback } from 'react';
6
+ import {
7
+ Flex, Typography, Input, Button, IconButton, Icon, Checkbox, Field, Table,
8
+ IconPicker, ColorPicker, Switch, ToggleGroup, ScrollArea, toast, usePackWorkspace,
9
+ } from '@fw/plugin-ui-kit';
13
10
 
14
11
  import { ICON_CATALOG, BOT_COLORS } from './bot-constants';
15
12
  import { BUILT_IN_CAPABILITIES } from '../bot/capability-registry';
@@ -366,11 +363,13 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }: Pr
366
363
  }, [profileId, callTool, onDelete, ctx]);
367
364
 
368
365
  if (loading) {
369
- return React.createElement(Flex, {
370
- variant: 'column-center-center-nowrap-12',
371
- style: { padding: '24px 16px' },
372
- },
373
- React.createElement(Typography, { variant: 'caption-regular', color: 'color-text-subtle' }, 'Loading profile...'),
366
+ return (
367
+ <Flex
368
+ variant="column-center-center-nowrap-12"
369
+ style={{ padding: '24px 16px' }}
370
+ >
371
+ <Typography variant="caption-regular" color="color-text-subtle">Loading profile...</Typography>
372
+ </Flex>
374
373
  );
375
374
  }
376
375
 
@@ -380,369 +379,453 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }: Pr
380
379
  const currentTier = phase.tier ?? 'standard';
381
380
  const ro = !isCustom;
382
381
 
383
- return React.createElement(Flex, {
384
- key: phaseName,
385
- variant: 'row-center-space-between-nowrap-8',
386
- style: { minHeight: 30, padding: '2px 0' },
387
- },
388
- React.createElement(Flex, { variant: 'row-center-start-nowrap-8', style: { minWidth: 100 } },
389
- React.createElement(Switch, {
390
- checked: phase.enabled,
391
- size: 'small',
392
- readOnly: ro,
393
- onChange: ro ? undefined : (v: boolean) => updatePhase(phaseName as keyof BehaviorState, 'enabled', v),
394
- }),
395
- React.createElement(Typography, {
396
- variant: 'smallCaption-regular',
397
- color: phase.enabled ? 'color-text-high' : 'color-text-subtle',
398
- }, label),
399
- ),
400
- isLlmPhase && React.createElement(ToggleGroup, {
401
- options: [
402
- { label: 'Fast', value: 'fast', icon: 'speed', selected: currentTier === 'fast', disabled: !phase.enabled },
403
- { label: 'Mid', value: 'standard', icon: 'balance', selected: currentTier === 'standard', disabled: !phase.enabled },
404
- { label: 'Smart', value: 'powerful', icon: 'psychology', selected: currentTier === 'powerful', disabled: !phase.enabled },
405
- ],
406
- onSelect: (v: string) => updatePhase(phaseName as keyof BehaviorState, 'tier', v),
407
- readOnly: ro,
408
- size: 'tiny',
409
- }),
382
+ return (
383
+ <Flex
384
+ key={phaseName}
385
+ variant="row-center-space-between-nowrap-8"
386
+ style={{ minHeight: 30, padding: '2px 0' }}
387
+ >
388
+ <Flex variant="row-center-start-nowrap-8" style={{ minWidth: 100 }}>
389
+ <Switch
390
+ checked={phase.enabled}
391
+ size="small"
392
+ readOnly={ro}
393
+ onChange={ro ? undefined : (v: boolean) => updatePhase(phaseName as keyof BehaviorState, 'enabled', v)}
394
+ />
395
+ <Typography
396
+ variant="smallCaption-regular"
397
+ color={phase.enabled ? 'color-text-high' : 'color-text-subtle'}
398
+ >
399
+ {label}
400
+ </Typography>
401
+ </Flex>
402
+ {isLlmPhase && (
403
+ <ToggleGroup
404
+ options={[
405
+ { label: 'Fast', value: 'fast', icon: 'speed', selected: currentTier === 'fast', disabled: !phase.enabled },
406
+ { label: 'Mid', value: 'standard', icon: 'balance', selected: currentTier === 'standard', disabled: !phase.enabled },
407
+ { label: 'Smart', value: 'powerful', icon: 'psychology', selected: currentTier === 'powerful', disabled: !phase.enabled },
408
+ ]}
409
+ onSelect={(v: string) => updatePhase(phaseName as keyof BehaviorState, 'tier', v)}
410
+ readOnly={ro}
411
+ size="tiny"
412
+ />
413
+ )}
414
+ </Flex>
410
415
  );
411
416
  };
412
417
 
413
418
  // --- Render ---
414
- return React.createElement(Flex, {
415
- variant: 'column-stretch-start-nowrap-0',
416
- style: { width: '100%', height: '100%', overflow: 'hidden' },
417
- },
418
- // ── Header bar ──
419
- React.createElement(Flex, {
420
- variant: 'row-center-space-between-nowrap-8',
421
- style: { padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' },
422
- },
423
- React.createElement(Flex, { variant: 'row-center-start-nowrap-8' },
424
- React.createElement(IconButton, {
425
- icon: 'back', size: 'xs', variant: 'clear', onClick: onCancel, title: 'Back',
426
- }),
427
- React.createElement(Typography, { variant: 'caption-thick', color: 'color-text-high' },
428
- mode === 'create' ? 'Create Profile' : 'Edit Profile'),
429
- ),
430
- mode === 'edit' && onDelete && React.createElement(IconButton, {
431
- icon: 'outlinedDelete', size: 'sm', variant: 'clear', color: 'danger',
432
- onClick: handleDelete, title: 'Delete profile',
433
- }),
434
- ),
435
-
436
- // ── Scrollable form body ──
437
- React.createElement(ScrollArea, {
438
- style: { flex: 1, minHeight: 0 },
439
- },
440
- React.createElement(Flex, {
441
- variant: 'column-stretch-start-nowrap-10',
442
- style: { padding: '12px 16px' },
443
- },
444
-
445
- // ── Name ──
446
- React.createElement(Field, { label: 'Name' },
447
- React.createElement(Input, {
448
- type: 'text', size: 'small', placeholder: 'Profile name',
449
- value: name, onChange: (v: string) => setName(v),
450
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
451
- }),
452
- ),
453
-
454
- // ── Description ──
455
- React.createElement(Field, { label: 'Description' },
456
- React.createElement(Input, {
457
- type: 'text', size: 'small', placeholder: 'What does this profile do?',
458
- value: description, onChange: (v: string) => setDescription(v),
459
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
460
- }),
461
- ),
462
-
463
- // ── Bot ──
464
- React.createElement(Field, { label: 'Bot' },
465
- React.createElement(Input, {
466
- type: 'select', size: 'small',
467
- options: bots.map((b: { id: string; name: string; icon?: string }) => ({ id: b.id, label: b.name, icon: b.icon || 'smartToy' })),
468
- optionId: botId, onChange: (id: string) => setBotId(id),
469
- disabled: mode === 'edit', placeholder: 'Select bot',
470
- defaultBoxStyle: { flex: 1, minWidth: 0 },
471
- }),
472
- ),
473
-
474
- // ── Icon ──
475
- React.createElement(Field, { label: 'Icon', align: 'start' },
476
- React.createElement(IconPicker, {
477
- catalog: ICON_CATALOG, value: icon,
478
- onChange: (v: string) => setIcon(v), accentColor: color, variant: 'inline',
479
- }),
480
- ),
481
-
482
- // ── Color ──
483
- React.createElement(Field, { label: 'Color', align: 'start' },
484
- React.createElement(ColorPicker, {
485
- colors: BOT_COLORS, value: color,
486
- onChange: (v: string) => setColor(v), variant: 'inline',
487
- }),
488
- ),
489
-
490
- // ── Preset (ToggleGroup with Custom option) ──
491
- React.createElement(Field, { label: 'Preset', align: 'start' },
492
- React.createElement(ToggleGroup, {
493
- options: [
494
- { label: 'Fast', value: 'frugal', icon: 'speed', selected: preset === 'frugal' },
495
- { label: 'Mid', value: 'balanced', icon: 'balance', selected: preset === 'balanced' },
496
- { label: 'Smart', value: 'performance', icon: 'psychology', selected: preset === 'performance' },
497
- { label: 'Custom', value: 'custom', icon: 'settings', selected: preset === 'custom' },
498
- ],
499
- onSelect: handlePresetChange,
500
- size: 'extraSmall',
501
- }),
502
- ),
503
-
504
- // ── Knowledge Capabilities (checkbox grid) ──
505
- React.createElement(Field, { label: 'Knowledge', align: 'start' },
506
- React.createElement(Flex, { variant: 'column-stretch-start-nowrap-2' },
507
- ...BUILT_IN_CAPABILITIES.map((cap: { name: string; description: string }) =>
508
- React.createElement(Flex, {
509
- key: cap.name,
510
- variant: 'row-center-start-nowrap-8',
511
- style: { minHeight: 28, padding: '1px 0' },
512
- },
513
- React.createElement(Checkbox, {
514
- checked: knowledgeCaps.includes(cap.name),
515
- onChange: () => handleToggleKnowledgeCap(cap.name),
516
- disabled: cap.name === 'core' || !isCustom,
517
- size: 'sm',
518
- }),
519
- React.createElement(Typography, {
520
- variant: 'smallCaption-thick',
521
- color: knowledgeCaps.includes(cap.name) ? 'color-text-high' : 'color-text-subtle',
522
- style: { minWidth: 80 },
523
- }, cap.name),
524
- React.createElement(Typography, {
525
- variant: 'smallCaption-regular',
526
- color: 'color-text-subtle',
527
- style: { flex: 1 },
528
- }, cap.description),
529
- ),
530
- ),
531
- ),
532
- ),
533
-
534
- // ── Budget ──
535
- React.createElement(Field, { label: 'Budget', align: 'center' },
536
- React.createElement(Flex, { variant: 'row-center-start-nowrap-6' },
537
- React.createElement(Typography, {
538
- variant: 'smallCaption-regular', color: 'color-text-subtle',
539
- }, '$'),
540
- React.createElement(Input, {
541
- type: 'number', size: 'small',
542
- value: String(budget),
543
- readOnly: !isCustom,
544
- onChange: isCustom ? (v: string) => {
545
- const n = parseFloat(v);
546
- if (!isNaN(n) && n >= 0) setBudget(n);
547
- } : undefined,
548
- defaultBoxStyle: { width: 80 },
549
- }),
550
- React.createElement(Typography, {
551
- variant: 'smallCaption-regular', color: 'color-text-subtle',
552
- }, '/ task'),
553
- ),
554
- ),
555
-
556
- // ── Phase Configuration (always visible, readOnly when preset) ──
557
- React.createElement(Field, { label: 'Phases', align: 'start' },
558
- React.createElement(Flex, {
559
- variant: 'column-stretch-start-nowrap-6',
560
- },
561
- ...Object.entries(behavior).filter(([k]) => !['maxAttempts', 'onExhausted', 'blockedPaths', 'allowedPaths'].includes(k)).map(([phaseName, phase]) => {
562
- const phaseObj = phase as { enabled?: boolean; tier?: string };
563
- const isLlm = phaseObj.tier !== undefined;
564
- const label = phaseName.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase()).trim();
565
- return phaseRow(phaseName, label, isLlm);
566
- }),
567
- ),
568
- ),
569
-
570
- // ── Escalation (always visible, readOnly when preset) ──
571
- React.createElement(Field, { label: 'Retries', align: 'start' },
572
- React.createElement(Flex, {
573
- variant: 'column-stretch-start-nowrap-8',
574
- },
575
- React.createElement(Field, { label: 'Max attempts', labelWidth: 100, align: 'center' },
576
- React.createElement(Input, {
577
- type: 'number', size: 'small',
578
- value: String(behavior.maxAttempts),
579
- readOnly: !isCustom,
580
- onChange: isCustom ? (v: string) => updateEscalation('maxAttempts', Math.max(1, parseInt(v) || 1)) : undefined,
581
- defaultBoxStyle: { width: 60 },
582
- }),
583
- ),
584
- React.createElement(Field, { label: 'On exhausted', labelWidth: 100, align: 'start' },
585
- React.createElement(ToggleGroup, {
586
- options: [
587
- { label: 'Block', value: 'block', icon: 'block', selected: behavior.onExhausted === 'block' },
588
- { label: 'Reassign', value: 'reassign', icon: 'sync', selected: behavior.onExhausted === 'reassign' },
589
- { label: 'Notify', value: 'notify', icon: 'notifications', selected: behavior.onExhausted === 'notify' },
590
- ],
591
- onSelect: (v: string) => updateEscalation('onExhausted', v),
592
- readOnly: !isCustom,
593
- size: 'extraSmall',
594
- }),
595
- ),
596
- ),
597
- ),
598
-
599
- // ── Scope Constraints (collapsible) ──
600
- // ── Custom: Scope Constraints (inline) ──
601
- isCustom && React.createElement(Field, { label: 'Scope', align: 'start' },
602
- React.createElement(Flex, {
603
- variant: 'column-stretch-start-nowrap-8',
604
- },
605
- // Blocked paths
606
- React.createElement(Field, { label: 'Blocked', labelWidth: 70, align: 'start' },
607
- React.createElement(Flex, { variant: 'column-stretch-start-nowrap-4' },
608
- React.createElement(Flex, { variant: 'row-center-start-nowrap-4' },
609
- React.createElement(Input, {
610
- type: 'text', size: 'small', placeholder: 'e.g. src/',
611
- value: blockedInput, onChange: (v: string) => setBlockedInput(v),
612
- onEnter: () => handleAddScopePath('blocked'),
613
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
614
- }),
615
- React.createElement(IconButton, {
616
- icon: 'add', size: 'sm', variant: 'outlined', color: 'primary',
617
- onClick: () => handleAddScopePath('blocked'), disabled: !blockedInput.trim(),
618
- }),
619
- ),
620
- ...behavior.blockedPaths.map((p: string, i: number) =>
621
- React.createElement(Flex, { key: `b-${p}-${i}`, variant: 'row-center-start-nowrap-6' },
622
- React.createElement(Icon, { name: 'block', size: 12, color: 'color-status-negative' }),
623
- React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high', style: { flex: 1 } }, p),
624
- React.createElement(IconButton, {
625
- icon: 'close', size: 'xs', variant: 'clear', color: 'danger',
626
- onClick: () => handleRemoveScopePath('blocked', i),
627
- }),
628
- ),
629
- ),
630
- ),
631
- ),
632
-
633
- // Allowed paths
634
- React.createElement(Field, { label: 'Allowed', labelWidth: 70, align: 'start' },
635
- React.createElement(Flex, { variant: 'column-stretch-start-nowrap-4' },
636
- React.createElement(Flex, { variant: 'row-center-start-nowrap-4' },
637
- React.createElement(Input, {
638
- type: 'text', size: 'small', placeholder: 'e.g. config/',
639
- value: allowedInput, onChange: (v: string) => setAllowedInput(v),
640
- onEnter: () => handleAddScopePath('allowed'),
641
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
642
- }),
643
- React.createElement(IconButton, {
644
- icon: 'add', size: 'sm', variant: 'outlined', color: 'primary',
645
- onClick: () => handleAddScopePath('allowed'), disabled: !allowedInput.trim(),
646
- }),
647
- ),
648
- ...behavior.allowedPaths.map((p: string, i: number) =>
649
- React.createElement(Flex, { key: `a-${p}-${i}`, variant: 'row-center-start-nowrap-6' },
650
- React.createElement(Icon, { name: 'checkCircle', size: 12, color: 'color-status-positive' }),
651
- React.createElement(Typography, { variant: 'smallCaption-regular', color: 'color-text-high', style: { flex: 1 } }, p),
652
- React.createElement(IconButton, {
653
- icon: 'close', size: 'xs', variant: 'clear', color: 'danger',
654
- onClick: () => handleRemoveScopePath('allowed', i),
655
- }),
656
- ),
657
- ),
658
- ),
659
- ),
660
- ),
661
- ),
662
-
663
- // ── Capabilities ──
664
- React.createElement(Field, { label: 'Capabilities', align: 'start' },
665
- React.createElement(Flex, { variant: 'column-stretch-start-nowrap-6' },
666
- // Add row
667
- React.createElement(Flex, { variant: 'row-center-start-nowrap-4', style: { minWidth: 0 } },
668
- React.createElement(Input, {
669
- type: 'text', size: 'small', placeholder: 'Name',
670
- value: capName, onChange: (v: string) => setCapName(v),
671
- defaultBoxStyle: { flex: '0 1 120px', minWidth: 0 }, inputBoxStyle: { maxWidth: 'none', minWidth: 0 },
672
- }),
673
- React.createElement(Input, {
674
- type: 'text', size: 'small', placeholder: 'Description',
675
- value: capDescription, onChange: (v: string) => setCapDescription(v),
676
- onEnter: handleAddCapability,
677
- defaultBoxStyle: { flex: '1 1 0', minWidth: 0 }, inputBoxStyle: { maxWidth: 'none', minWidth: 0 },
678
- }),
679
- React.createElement(IconButton, {
680
- icon: 'add', size: 'sm', variant: 'outlined', color: 'primary',
681
- onClick: handleAddCapability, disabled: !capName.trim() || !capDescription.trim(),
682
- style: { flexShrink: 0 },
683
- }),
684
- ),
685
- // Table
686
- capabilities.length > 0 && React.createElement(Table, {
687
- size: 'compact',
688
- showHeader: false,
689
- data: capabilities,
690
- getRowKey: (_row: { name: string }, i: number) => `cap-${i}`,
691
- columns: [
692
- {
693
- key: 'name', header: 'Name', width: '30%',
694
- render: (val: unknown) => React.createElement('div', { style: { lineHeight: '28px' } }, String(val)),
695
- },
696
- {
697
- key: 'description', header: 'Description',
698
- render: (val: unknown) => React.createElement('div', { style: { lineHeight: '28px' } }, String(val)),
699
- },
700
- {
701
- key: 'actions', header: '', width: '32px', align: 'right',
702
- render: (_val: unknown, _row: { name: string; description: string }, idx: number) =>
703
- React.createElement(IconButton, {
704
- icon: 'close', size: 'xs', variant: 'clear', color: 'danger',
705
- onClick: () => handleRemoveCapability(idx),
706
- }),
707
- },
708
- ],
709
- }),
710
- ),
711
- ),
712
-
713
- // ── Instructions (multiline) ──
714
- React.createElement(Field, { label: 'Instructions', align: 'start' },
715
- React.createElement(Input, {
716
- type: 'text', size: 'small', multiline: true,
717
- placeholder: 'Behavioral protocol for this profile (optional)',
718
- value: instructions, onChange: (v: string) => setInstructions(v),
719
- defaultBoxStyle: { flex: 1, minWidth: 0 }, inputBoxStyle: { maxWidth: 'none' },
720
- }),
721
- ),
722
-
723
- // ── Require Approval ──
724
- React.createElement(Field, { label: '' },
725
- React.createElement(Checkbox, {
726
- checked: requireApproval,
727
- onChange: (v: boolean) => setRequireApproval(v),
728
- label: 'Require approval',
729
- size: 'sm',
730
- }),
731
- ),
732
-
733
- // ── Save button ──
734
- React.createElement(Flex, { variant: 'row-center-end-nowrap-8' },
735
- React.createElement(Button, {
736
- size: 'xs', variant: 'fill', color: 'primary',
737
- onClick: handleSave,
738
- disabled: !name.trim() || !botId || capabilities.length === 0,
739
- }, mode === 'create' ? 'Create' : 'Save'),
740
- ),
741
- ),
742
- ),
419
+ return (
420
+ <Flex
421
+ variant="column-stretch-start-nowrap-0"
422
+ style={{ width: '100%', height: '100%', overflow: 'hidden' }}
423
+ >
424
+ {/* Header bar */}
425
+ <Flex
426
+ variant="row-center-space-between-nowrap-8"
427
+ style={{ padding: '8px 16px', flexShrink: 0, borderBottom: '1px solid var(--color-border-default)' }}
428
+ >
429
+ <Flex variant="row-center-start-nowrap-8">
430
+ <IconButton
431
+ icon="back"
432
+ size="xs"
433
+ variant="clear"
434
+ onClick={onCancel}
435
+ title="Back"
436
+ />
437
+ <Typography variant="caption-thick" color="color-text-high">
438
+ {mode === 'create' ? 'Create Profile' : 'Edit Profile'}
439
+ </Typography>
440
+ </Flex>
441
+ {mode === 'edit' && onDelete && (
442
+ <IconButton
443
+ icon="outlinedDelete"
444
+ size="sm"
445
+ variant="clear"
446
+ color="danger"
447
+ onClick={handleDelete}
448
+ title="Delete profile"
449
+ />
450
+ )}
451
+ </Flex>
452
+
453
+ {/* Scrollable form body */}
454
+ <ScrollArea style={{ flex: 1, minHeight: 0 }}>
455
+ <Flex
456
+ variant="column-stretch-start-nowrap-10"
457
+ style={{ padding: '12px 16px' }}
458
+ >
459
+ {/* Name */}
460
+ <Field label="Name">
461
+ <Input
462
+ type="text"
463
+ size="small"
464
+ placeholder="Profile name"
465
+ value={name}
466
+ onChange={(v: string) => setName(v)}
467
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
468
+ inputBoxStyle={{ maxWidth: 'none' }}
469
+ />
470
+ </Field>
471
+
472
+ {/* Description */}
473
+ <Field label="Description">
474
+ <Input
475
+ type="text"
476
+ size="small"
477
+ placeholder="What does this profile do?"
478
+ value={description}
479
+ onChange={(v: string) => setDescription(v)}
480
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
481
+ inputBoxStyle={{ maxWidth: 'none' }}
482
+ />
483
+ </Field>
484
+
485
+ {/* Bot */}
486
+ <Field label="Bot">
487
+ <Input
488
+ type="select"
489
+ size="small"
490
+ options={bots.map((b: { id: string; name: string; icon?: string }) => ({ id: b.id, label: b.name, icon: b.icon || 'smartToy' }))}
491
+ optionId={botId}
492
+ onChange={(id: string) => setBotId(id)}
493
+ disabled={mode === 'edit'}
494
+ placeholder="Select bot"
495
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
496
+ />
497
+ </Field>
498
+
499
+ {/* Icon */}
500
+ <Field label="Icon" align="start">
501
+ <IconPicker
502
+ catalog={ICON_CATALOG}
503
+ value={icon}
504
+ onChange={(v: string) => setIcon(v)}
505
+ accentColor={color}
506
+ variant="inline"
507
+ />
508
+ </Field>
509
+
510
+ {/* Color */}
511
+ <Field label="Color" align="start">
512
+ <ColorPicker
513
+ colors={BOT_COLORS}
514
+ value={color}
515
+ onChange={(v: string) => setColor(v)}
516
+ variant="inline"
517
+ />
518
+ </Field>
519
+
520
+ {/* Preset (ToggleGroup with Custom option) */}
521
+ <Field label="Preset" align="start">
522
+ <ToggleGroup
523
+ options={[
524
+ { label: 'Fast', value: 'frugal', icon: 'speed', selected: preset === 'frugal' },
525
+ { label: 'Mid', value: 'balanced', icon: 'balance', selected: preset === 'balanced' },
526
+ { label: 'Smart', value: 'performance', icon: 'psychology', selected: preset === 'performance' },
527
+ { label: 'Custom', value: 'custom', icon: 'settings', selected: preset === 'custom' },
528
+ ]}
529
+ onSelect={handlePresetChange}
530
+ size="extraSmall"
531
+ />
532
+ </Field>
533
+
534
+ {/* Capabilities (checkbox grid) */}
535
+ <Field label="Capabilities" align="start">
536
+ <Flex variant="column-stretch-start-nowrap-2">
537
+ {BUILT_IN_CAPABILITIES.map((cap: { name: string; description: string }) => (
538
+ <Flex
539
+ key={cap.name}
540
+ variant="row-center-start-nowrap-8"
541
+ style={{ minHeight: 28, padding: '1px 0' }}
542
+ >
543
+ <Checkbox
544
+ checked={knowledgeCaps.includes(cap.name)}
545
+ onChange={() => handleToggleKnowledgeCap(cap.name)}
546
+ disabled={cap.name === 'core' || !isCustom}
547
+ size="sm"
548
+ />
549
+ <Typography
550
+ variant="smallCaption-thick"
551
+ color={knowledgeCaps.includes(cap.name) ? 'color-text-high' : 'color-text-subtle'}
552
+ style={{ minWidth: 80 }}
553
+ >
554
+ {cap.name}
555
+ </Typography>
556
+ <Typography
557
+ variant="smallCaption-regular"
558
+ color="color-text-subtle"
559
+ style={{ flex: 1 }}
560
+ >
561
+ {cap.description}
562
+ </Typography>
563
+ </Flex>
564
+ ))}
565
+ </Flex>
566
+ </Field>
567
+
568
+ {/* Budget */}
569
+ <Field label="Budget" align="center">
570
+ <Flex variant="row-center-start-nowrap-6">
571
+ <Typography variant="smallCaption-regular" color="color-text-subtle">
572
+ $
573
+ </Typography>
574
+ <Input
575
+ type="number"
576
+ size="small"
577
+ value={String(budget)}
578
+ readOnly={!isCustom}
579
+ onChange={isCustom ? (v: string) => {
580
+ const n = parseFloat(v);
581
+ if (!isNaN(n) && n >= 0) setBudget(n);
582
+ } : undefined}
583
+ defaultBoxStyle={{ width: 80 }}
584
+ />
585
+ <Typography variant="smallCaption-regular" color="color-text-subtle">
586
+ / task
587
+ </Typography>
588
+ </Flex>
589
+ </Field>
590
+
591
+ {/* Phase Configuration (always visible, readOnly when preset) */}
592
+ <Field label="Phases" align="start">
593
+ <Flex variant="column-stretch-start-nowrap-6">
594
+ {Object.entries(behavior).filter(([k]) => !['maxAttempts', 'onExhausted', 'blockedPaths', 'allowedPaths'].includes(k)).map(([phaseName, phase]) => {
595
+ const phaseObj = phase as { enabled?: boolean; tier?: string };
596
+ const isLlm = phaseObj.tier !== undefined;
597
+ const label = phaseName.replace(/([A-Z])/g, ' $1').replace(/^./, s => s.toUpperCase()).trim();
598
+ return phaseRow(phaseName, label, isLlm);
599
+ })}
600
+ </Flex>
601
+ </Field>
602
+
603
+ {/* Escalation (always visible, readOnly when preset) */}
604
+ <Field label="Retries" align="start">
605
+ <Flex variant="column-stretch-start-nowrap-8">
606
+ <Field label="Max attempts" labelWidth={100} align="center">
607
+ <Input
608
+ type="number"
609
+ size="small"
610
+ value={String(behavior.maxAttempts)}
611
+ readOnly={!isCustom}
612
+ onChange={isCustom ? (v: string) => updateEscalation('maxAttempts', Math.max(1, parseInt(v) || 1)) : undefined}
613
+ defaultBoxStyle={{ width: 60 }}
614
+ />
615
+ </Field>
616
+ <Field label="On exhausted" labelWidth={100} align="start">
617
+ <ToggleGroup
618
+ options={[
619
+ { label: 'Block', value: 'block', icon: 'block', selected: behavior.onExhausted === 'block' },
620
+ { label: 'Reassign', value: 'reassign', icon: 'sync', selected: behavior.onExhausted === 'reassign' },
621
+ { label: 'Notify', value: 'notify', icon: 'notifications', selected: behavior.onExhausted === 'notify' },
622
+ ]}
623
+ onSelect={(v: string) => updateEscalation('onExhausted', v)}
624
+ readOnly={!isCustom}
625
+ size="extraSmall"
626
+ />
627
+ </Field>
628
+ </Flex>
629
+ </Field>
630
+
631
+ {/* Custom: Scope Constraints (inline) */}
632
+ {isCustom && (
633
+ <Field label="Scope" align="start">
634
+ <Flex variant="column-stretch-start-nowrap-8">
635
+ {/* Blocked paths */}
636
+ <Field label="Blocked" labelWidth={70} align="start">
637
+ <Flex variant="column-stretch-start-nowrap-4">
638
+ <Flex variant="row-center-start-nowrap-4">
639
+ <Input
640
+ type="text"
641
+ size="small"
642
+ placeholder="e.g. src/"
643
+ value={blockedInput}
644
+ onChange={(v: string) => setBlockedInput(v)}
645
+ onEnter={() => handleAddScopePath('blocked')}
646
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
647
+ inputBoxStyle={{ maxWidth: 'none' }}
648
+ />
649
+ <IconButton
650
+ icon="add"
651
+ size="sm"
652
+ variant="outlined"
653
+ color="primary"
654
+ onClick={() => handleAddScopePath('blocked')}
655
+ disabled={!blockedInput.trim()}
656
+ />
657
+ </Flex>
658
+ {behavior.blockedPaths.map((p: string, i: number) => (
659
+ <Flex key={`b-${p}-${i}`} variant="row-center-start-nowrap-6">
660
+ <Icon name="block" size={12} color="color-status-negative" />
661
+ <Typography variant="smallCaption-regular" color="color-text-high" style={{ flex: 1 }}>
662
+ {p}
663
+ </Typography>
664
+ <IconButton
665
+ icon="close"
666
+ size="xs"
667
+ variant="clear"
668
+ color="danger"
669
+ onClick={() => handleRemoveScopePath('blocked', i)}
670
+ />
671
+ </Flex>
672
+ ))}
673
+ </Flex>
674
+ </Field>
675
+
676
+ {/* Allowed paths */}
677
+ <Field label="Allowed" labelWidth={70} align="start">
678
+ <Flex variant="column-stretch-start-nowrap-4">
679
+ <Flex variant="row-center-start-nowrap-4">
680
+ <Input
681
+ type="text"
682
+ size="small"
683
+ placeholder="e.g. config/"
684
+ value={allowedInput}
685
+ onChange={(v: string) => setAllowedInput(v)}
686
+ onEnter={() => handleAddScopePath('allowed')}
687
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
688
+ inputBoxStyle={{ maxWidth: 'none' }}
689
+ />
690
+ <IconButton
691
+ icon="add"
692
+ size="sm"
693
+ variant="outlined"
694
+ color="primary"
695
+ onClick={() => handleAddScopePath('allowed')}
696
+ disabled={!allowedInput.trim()}
697
+ />
698
+ </Flex>
699
+ {behavior.allowedPaths.map((p: string, i: number) => (
700
+ <Flex key={`a-${p}-${i}`} variant="row-center-start-nowrap-6">
701
+ <Icon name="checkCircle" size={12} color="color-status-positive" />
702
+ <Typography variant="smallCaption-regular" color="color-text-high" style={{ flex: 1 }}>
703
+ {p}
704
+ </Typography>
705
+ <IconButton
706
+ icon="close"
707
+ size="xs"
708
+ variant="clear"
709
+ color="danger"
710
+ onClick={() => handleRemoveScopePath('allowed', i)}
711
+ />
712
+ </Flex>
713
+ ))}
714
+ </Flex>
715
+ </Field>
716
+ </Flex>
717
+ </Field>
718
+ )}
719
+
720
+ {/* Capabilities */}
721
+ <Field label="Capabilities" align="start">
722
+ <Flex variant="column-stretch-start-nowrap-6">
723
+ {/* Add row */}
724
+ <Flex variant="row-center-start-nowrap-4" style={{ minWidth: 0 }}>
725
+ <Input
726
+ type="text"
727
+ size="small"
728
+ placeholder="Name"
729
+ value={capName}
730
+ onChange={(v: string) => setCapName(v)}
731
+ defaultBoxStyle={{ flex: '0 1 120px', minWidth: 0 }}
732
+ inputBoxStyle={{ maxWidth: 'none', minWidth: 0 }}
733
+ />
734
+ <Input
735
+ type="text"
736
+ size="small"
737
+ placeholder="Description"
738
+ value={capDescription}
739
+ onChange={(v: string) => setCapDescription(v)}
740
+ onEnter={handleAddCapability}
741
+ defaultBoxStyle={{ flex: '1 1 0', minWidth: 0 }}
742
+ inputBoxStyle={{ maxWidth: 'none', minWidth: 0 }}
743
+ />
744
+ <IconButton
745
+ icon="add"
746
+ size="sm"
747
+ variant="outlined"
748
+ color="primary"
749
+ onClick={handleAddCapability}
750
+ disabled={!capName.trim() || !capDescription.trim()}
751
+ style={{ flexShrink: 0 }}
752
+ />
753
+ </Flex>
754
+ {/* Table */}
755
+ {capabilities.length > 0 && (
756
+ <Table
757
+ size="compact"
758
+ showHeader={false}
759
+ data={capabilities}
760
+ getRowKey={(_row: { name: string }, i: number) => `cap-${i}`}
761
+ columns={[
762
+ {
763
+ key: 'name', header: 'Name', width: '30%',
764
+ render: (val: unknown) => <div style={{ lineHeight: '28px' }}>{String(val)}</div>,
765
+ },
766
+ {
767
+ key: 'description', header: 'Description',
768
+ render: (val: unknown) => <div style={{ lineHeight: '28px' }}>{String(val)}</div>,
769
+ },
770
+ {
771
+ key: 'actions', header: '', width: '32px', align: 'right',
772
+ render: (_val: unknown, _row: { name: string; description: string }, idx: number) => (
773
+ <IconButton
774
+ icon="close"
775
+ size="xs"
776
+ variant="clear"
777
+ color="danger"
778
+ onClick={() => handleRemoveCapability(idx)}
779
+ />
780
+ ),
781
+ },
782
+ ]}
783
+ />
784
+ )}
785
+ </Flex>
786
+ </Field>
787
+
788
+ {/* Instructions (multiline) */}
789
+ <Field label="Instructions" align="start">
790
+ <Input
791
+ type="text"
792
+ size="small"
793
+ multiline={true}
794
+ placeholder="Behavioral protocol for this profile (optional)"
795
+ value={instructions}
796
+ onChange={(v: string) => setInstructions(v)}
797
+ defaultBoxStyle={{ flex: 1, minWidth: 0 }}
798
+ inputBoxStyle={{ maxWidth: 'none' }}
799
+ />
800
+ </Field>
801
+
802
+ {/* Require Approval */}
803
+ <Field label="">
804
+ <Checkbox
805
+ checked={requireApproval}
806
+ onChange={(v: boolean) => setRequireApproval(v)}
807
+ label="Require approval"
808
+ size="sm"
809
+ />
810
+ </Field>
811
+
812
+ {/* Save button */}
813
+ <Flex variant="row-center-end-nowrap-8">
814
+ <Button
815
+ size="xs"
816
+ variant="fill"
817
+ color="primary"
818
+ onClick={handleSave}
819
+ disabled={!name.trim() || !botId || capabilities.length === 0}
820
+ >
821
+ {mode === 'create' ? 'Create' : 'Save'}
822
+ </Button>
823
+ </Flex>
824
+ </Flex>
825
+ </ScrollArea>
826
+ </Flex>
743
827
  );
744
828
  }
745
829
 
746
830
  export { ProfileEditor };
747
831
  export default ProfileEditor;
748
- module.exports = ProfileEditor;