@synergenius/flow-weaver-pack-weaver 0.9.134 → 0.9.136

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 (49) hide show
  1. package/dist/bot/ai-client.d.ts +9 -0
  2. package/dist/bot/ai-client.d.ts.map +1 -1
  3. package/dist/bot/ai-client.js +59 -0
  4. package/dist/bot/ai-client.js.map +1 -1
  5. package/dist/bot/behavior-defaults.d.ts +4 -0
  6. package/dist/bot/behavior-defaults.d.ts.map +1 -1
  7. package/dist/bot/behavior-defaults.js +17 -0
  8. package/dist/bot/behavior-defaults.js.map +1 -1
  9. package/dist/bot/capability-registry.d.ts +20 -0
  10. package/dist/bot/capability-registry.d.ts.map +1 -0
  11. package/dist/bot/capability-registry.js +205 -0
  12. package/dist/bot/capability-registry.js.map +1 -0
  13. package/dist/bot/capability-types.d.ts +23 -0
  14. package/dist/bot/capability-types.d.ts.map +1 -0
  15. package/dist/bot/capability-types.js +9 -0
  16. package/dist/bot/capability-types.js.map +1 -0
  17. package/dist/bot/estimate-tokens.d.ts +12 -0
  18. package/dist/bot/estimate-tokens.d.ts.map +1 -0
  19. package/dist/bot/estimate-tokens.js +18 -0
  20. package/dist/bot/estimate-tokens.js.map +1 -0
  21. package/dist/bot/profile-types.d.ts +4 -0
  22. package/dist/bot/profile-types.d.ts.map +1 -1
  23. package/dist/bot/system-prompt.d.ts +11 -0
  24. package/dist/bot/system-prompt.d.ts.map +1 -1
  25. package/dist/bot/system-prompt.js +31 -0
  26. package/dist/bot/system-prompt.js.map +1 -1
  27. package/dist/bot/types.d.ts +1 -0
  28. package/dist/bot/types.d.ts.map +1 -1
  29. package/dist/node-types/plan-task.d.ts.map +1 -1
  30. package/dist/node-types/plan-task.js +49 -45
  31. package/dist/node-types/plan-task.js.map +1 -1
  32. package/dist/ui/capability-editor.js +657 -0
  33. package/dist/ui/profile-card.js +25 -0
  34. package/dist/ui/profile-editor.js +615 -316
  35. package/dist/ui/swarm-dashboard.js +643 -319
  36. package/flowweaver.manifest.json +1 -1
  37. package/package.json +2 -2
  38. package/src/bot/ai-client.ts +75 -0
  39. package/src/bot/behavior-defaults.ts +20 -0
  40. package/src/bot/capability-registry.ts +230 -0
  41. package/src/bot/capability-types.ts +23 -0
  42. package/src/bot/estimate-tokens.ts +16 -0
  43. package/src/bot/profile-types.ts +4 -0
  44. package/src/bot/system-prompt.ts +33 -0
  45. package/src/bot/types.ts +1 -0
  46. package/src/node-types/plan-task.ts +51 -44
  47. package/src/ui/capability-editor.tsx +420 -0
  48. package/src/ui/profile-card.tsx +20 -0
  49. package/src/ui/profile-editor.tsx +100 -6
@@ -2318,6 +2318,31 @@ function ProfileCard({ profile, activeInstances, onEdit, onDelete }) {
2318
2318
  )
2319
2319
  )
2320
2320
  ),
2321
+ // Knowledge capabilities (from behavior)
2322
+ behavior?.capabilities && behavior.capabilities.length > 0 && React8.createElement(
2323
+ Field2,
2324
+ { label: "Knowledge", labelWidth: 90, align: "start" },
2325
+ React8.createElement(
2326
+ Flex7,
2327
+ { variant: "row-center-start-wrap-4" },
2328
+ ...behavior.capabilities.map(
2329
+ (capName) => React8.createElement(
2330
+ "span",
2331
+ { key: capName },
2332
+ React8.createElement(Chip4, { label: capName, size: "small", color: "color-status-info" })
2333
+ )
2334
+ )
2335
+ )
2336
+ ),
2337
+ // Budget (from behavior)
2338
+ behavior?.budget && React8.createElement(
2339
+ Field2,
2340
+ { label: "Budget", labelWidth: 90, align: "center" },
2341
+ React8.createElement(Typography7, {
2342
+ variant: "smallCaption-regular",
2343
+ color: "color-text-high"
2344
+ }, `$${behavior.budget.toFixed(2)}/task`)
2345
+ ),
2321
2346
  // Strategy
2322
2347
  React8.createElement(
2323
2348
  Field2,
@@ -2637,6 +2662,205 @@ var BOT_COLORS = [
2637
2662
  { label: "Info", token: "color-status-info" }
2638
2663
  ];
2639
2664
 
2665
+ // src/bot/operations.ts
2666
+ var OP_WRITE_FILE = "write_file";
2667
+ var OP_READ_FILE = "read_file";
2668
+ var OP_PATCH_FILE = "patch_file";
2669
+ var OP_LIST_FILES = "list_files";
2670
+ var OP_RUN_SHELL = "run_shell";
2671
+ var OP_VALIDATE = "validate";
2672
+ var OP_TSC_CHECK = "tsc_check";
2673
+ var OP_RUN_TESTS = "run_tests";
2674
+ var OP_TASK_CREATE = "task_create";
2675
+
2676
+ // src/bot/capability-registry.ts
2677
+ var CAP_CORE = {
2678
+ name: "core",
2679
+ description: "Bot identity, structured plan output format, and safety rules. Always loaded.",
2680
+ prompt: `You are Weaver, an expert AI companion for Flow Weaver workflows.
2681
+
2682
+ ## Plan Format
2683
+ Your plans MUST be structured JSON with concrete steps.
2684
+ Each step has: operation (tool name), description (what it does), args (complete arguments).
2685
+ Do NOT describe what you would do \u2014 actually do it by calling tools.
2686
+
2687
+ ## Safety Rules
2688
+ - Writes that shrink a file by >50% or write empty content are automatically BLOCKED.
2689
+ - Blocked shell commands: rm -rf, git push, npm publish, sudo, curl|sh.
2690
+ - Always validate BEFORE and AFTER patching.
2691
+ - Always read a file before patching it (you need exact strings for find/replace).
2692
+ - Use patch_file for modifications, write_file only for new files.
2693
+ - Be concise \u2014 let tool results speak.`
2694
+ };
2695
+ var CAP_FILE_OPS = {
2696
+ name: "file-ops",
2697
+ description: "File read/write/patch operations and best practices for file manipulation.",
2698
+ tools: [OP_READ_FILE, OP_WRITE_FILE, OP_PATCH_FILE, OP_LIST_FILES],
2699
+ prompt: `## File Operations
2700
+ - read_file: Read a file and return its content. args: { file }
2701
+ - write_file: Write a file. args: { file, content }. Content must be the COMPLETE file.
2702
+ - patch_file: Surgical find-and-replace edits. args: { file, patches: [{ find: "old text", replace: "new text" }] }. PREFERRED for modifying existing files.
2703
+ - list_files: List files in a directory. args: { directory, pattern? } (pattern is regex)
2704
+
2705
+ ## Best Practices
2706
+ PREFER patch_file over write_file for modifying existing files (surgical edits, no truncation risk).
2707
+ Use read_file to understand a file before modifying it.
2708
+ Use list_files to discover project structure.
2709
+ Writes that shrink a file by >50% or write empty content are automatically BLOCKED.`
2710
+ };
2711
+ var CAP_SHELL = {
2712
+ name: "shell",
2713
+ description: "Shell command execution for running tests, builds, and inspecting output.",
2714
+ tools: [OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS],
2715
+ prompt: `## Shell Commands
2716
+ - run_shell: Execute a shell command and return output. args: { command }
2717
+ Use for: npx vitest, git status, grep, find, etc.
2718
+ Examples: { "command": "npx vitest run --reporter verbose" }, { "command": "npx flow-weaver validate src/workflow.ts --json" }
2719
+ Blocked: rm -rf, git push, npm publish, sudo, curl|sh (safety policy).
2720
+ Use run_shell for running tests (npx vitest), validation (flow-weaver validate), and inspecting output.`
2721
+ };
2722
+ var CAP_TASK_MGMT = {
2723
+ name: "task-mgmt",
2724
+ description: "Create and manage swarm subtasks for parallel execution.",
2725
+ tools: [OP_TASK_CREATE],
2726
+ prompt: `## Task Management
2727
+ - task_create: Create swarm subtasks for parallel execution. args: { title, description, complexity, subtasks[] }
2728
+ - task_list, task_get, task_update: Query and update existing tasks
2729
+
2730
+ Use task_create to decompose complex work into smaller, independent subtasks that other bots can execute in parallel.`
2731
+ };
2732
+ var CAP_FW_GRAMMAR = {
2733
+ name: "fw-grammar",
2734
+ description: "Flow Weaver annotation syntax, node types, workflows, patterns, and data flow.",
2735
+ prompt: `## Core Mental Model
2736
+
2737
+ The code IS the workflow. Flow Weaver workflows are plain TypeScript files with JSDoc annotations above functions. The compiler reads annotations and generates deterministic execution code between @flow-weaver-body-start/end markers. Compiled code is standalone with no runtime dependency on flow-weaver.
2738
+
2739
+ Three block types:
2740
+ - @flowWeaver nodeType: A reusable function (node) with typed inputs/outputs
2741
+ - @flowWeaver workflow: A DAG orchestration that wires node instances together
2742
+ - @flowWeaver pattern: A reusable fragment with boundary ports (IN/OUT instead of Start/Exit)
2743
+
2744
+ ## Node Execution Model
2745
+
2746
+ Expression nodes (@expression):
2747
+ - No execute/onSuccess/onFailure params. Just inputs and return value.
2748
+ - throw = failure path, return = success path
2749
+ - Synchronous. Use execSync for shell commands.
2750
+ - Preferred for deterministic operations.
2751
+
2752
+ Standard nodes:
2753
+ - execute: boolean param gates execution
2754
+ - Return { onSuccess: boolean, onFailure: boolean, ...outputs }
2755
+ - Can be async for I/O operations
2756
+
2757
+ Async agent nodes:
2758
+ - Use (globalThis as any).__fw_agent_channel__ to pause workflow
2759
+ - Call channel.request({ agentId, context, prompt }) which returns a Promise
2760
+ - Workflow suspends until agent responds
2761
+ - NOT @expression (must be async)
2762
+
2763
+ Pass-through pattern:
2764
+ - FW auto-connects ports by matching names on adjacent nodes
2765
+ - To forward data through intermediate nodes, declare it as both @input and @output with the same name
2766
+ - For non-adjacent wiring, use @connect sourceNode.port -> targetNode.port
2767
+
2768
+ Data flow:
2769
+ - @path A -> B -> C: Linear execution path (sugar for multiple @connect)
2770
+ - @autoConnect: Auto-wire all nodes in declaration order
2771
+ - @connect: Explicit port-to-port wiring
2772
+ - Merge strategies for fan-in: FIRST, LAST, COLLECT, MERGE, CONCAT`
2773
+ };
2774
+ var CAP_FW_VALIDATE = {
2775
+ name: "fw-validate",
2776
+ description: "Flow Weaver validation error codes and common fix patterns.",
2777
+ prompt: `## Validation Errors
2778
+
2779
+ When you encounter validation errors, suggest the specific fix. Common resolutions:
2780
+ - UNKNOWN_NODE_TYPE: Ensure the referenced function has @flowWeaver nodeType annotation
2781
+ - MISSING_REQUIRED_INPUT: Add a @connect from a source port or make the input optional with [brackets]
2782
+ - UNKNOWN_SOURCE_PORT / UNKNOWN_TARGET_PORT: Check port name spelling in @connect
2783
+ - CYCLE_DETECTED: Break the cycle; use scoped iteration (@scope, @map) instead of circular dependencies`
2784
+ };
2785
+ var CAP_FW_GENESIS = {
2786
+ name: "fw-genesis",
2787
+ description: "Flow Weaver genesis protocol for self-evolving workflows.",
2788
+ prompt: `## Genesis Protocol
2789
+
2790
+ Genesis is a 17-step self-evolving workflow engine:
2791
+ 1. Load config 2. Observe project 3. Load task workflow 4. Diff fingerprint
2792
+ 5. Check stabilize mode 6. Wait for agent 7. Propose evolution 8. Validate proposal
2793
+ 9. Snapshot for rollback 10. Apply changes 11. Compile and validate
2794
+ 12. Diff workflow 13. Check approval threshold 14. Wait for approval
2795
+ 15. Commit or rollback 16. Update history 17. Report summary
2796
+
2797
+ When stabilize mode is active, only fix-up operations are allowed.`
2798
+ };
2799
+ var CAP_FW_CLI = {
2800
+ name: "fw-cli",
2801
+ description: "Flow Weaver CLI command reference for compile, validate, diagram, and other operations.",
2802
+ prompt: `## CLI Commands
2803
+
2804
+ Note: compile, validate, modify, diff, diagram, and describe operations are available as direct plan steps (no CLI needed).`
2805
+ };
2806
+ var CAP_CODE_REVIEW = {
2807
+ name: "code-review",
2808
+ description: "Code review guidelines, quality checklist, and security review patterns.",
2809
+ prompt: `## Code Review
2810
+
2811
+ When reviewing code, check for:
2812
+ 1. Correctness: Does the code do what the task asked?
2813
+ 2. Security: No hardcoded secrets, no injection vulnerabilities, no exposed APIs
2814
+ 3. Style: Consistent with project conventions, proper naming, no dead code
2815
+ 4. Testing: Are there tests? Do they cover edge cases?
2816
+ 5. Performance: No unnecessary loops, no blocking calls in async code
2817
+
2818
+ Report concerns with specific file:line references and suggested fixes.`
2819
+ };
2820
+ var CAP_WEB = {
2821
+ name: "web",
2822
+ description: "Web fetch capability for fetching URLs and external resources.",
2823
+ tools: ["web_fetch"],
2824
+ prompt: `## Web
2825
+ - web_fetch(url): Fetch a URL and return its content. Use for API docs, examples, etc.`
2826
+ };
2827
+ var CAP_CONTEXT = {
2828
+ name: "context",
2829
+ description: "Project file listings, directory structure, and workspace context.",
2830
+ prompt: `## Project Context
2831
+
2832
+ Use list_files to understand the project structure before making changes.
2833
+ The context bundle (when available) provides a snapshot of the workspace.`
2834
+ };
2835
+ var BUILT_IN_CAPABILITIES = [
2836
+ CAP_CORE,
2837
+ CAP_FILE_OPS,
2838
+ CAP_SHELL,
2839
+ CAP_TASK_MGMT,
2840
+ CAP_FW_GRAMMAR,
2841
+ CAP_FW_VALIDATE,
2842
+ CAP_FW_GENESIS,
2843
+ CAP_FW_CLI,
2844
+ CAP_CODE_REVIEW,
2845
+ CAP_WEB,
2846
+ CAP_CONTEXT
2847
+ ];
2848
+ var capabilityMap = new Map(
2849
+ BUILT_IN_CAPABILITIES.map((c) => [c.name, c])
2850
+ );
2851
+
2852
+ // src/bot/behavior-defaults.ts
2853
+ var STRATEGY_CAPABILITIES = {
2854
+ frugal: ["core", "file-ops", "shell", "context"],
2855
+ balanced: ["core", "file-ops", "shell", "task-mgmt", "fw-grammar", "fw-validate", "context"],
2856
+ performance: ["core", "file-ops", "shell", "task-mgmt", "fw-grammar", "fw-validate", "fw-genesis", "fw-cli", "code-review", "web", "context"]
2857
+ };
2858
+ var STRATEGY_BUDGETS = {
2859
+ frugal: 0.1,
2860
+ balanced: 0.5,
2861
+ performance: 1
2862
+ };
2863
+
2640
2864
  // src/ui/profile-editor.tsx
2641
2865
  var React9 = require("react");
2642
2866
  var { useState: useState6, useEffect: useEffect4, useCallback: useCallback4 } = React9;
@@ -2657,6 +2881,7 @@ var {
2657
2881
  Switch,
2658
2882
  ToggleGroup,
2659
2883
  CollapsibleSection: CollapsibleSection2,
2884
+ ScrollArea: ScrollArea3,
2660
2885
  toast: toast4,
2661
2886
  usePackWorkspace: usePackWorkspace4
2662
2887
  } = require("@fw/plugin-ui-kit");
@@ -2716,6 +2941,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2716
2941
  const [capDescription, setCapDescription] = useState6("");
2717
2942
  const [instructions, setInstructions] = useState6("");
2718
2943
  const [requireApproval, setRequireApproval] = useState6(false);
2944
+ const [knowledgeCaps, setKnowledgeCaps] = useState6(() => [...STRATEGY_CAPABILITIES.balanced]);
2945
+ const [budget, setBudget] = useState6(STRATEGY_BUDGETS.balanced);
2719
2946
  const [behavior, setBehavior] = useState6(defaultBehaviorForStrategy("balanced"));
2720
2947
  const [blockedInput, setBlockedInput] = useState6("");
2721
2948
  const [allowedInput, setAllowedInput] = useState6("");
@@ -2727,6 +2954,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2727
2954
  if (p !== "custom") {
2728
2955
  setCostStrategy(p);
2729
2956
  setBehavior(defaultBehaviorForStrategy(p));
2957
+ setKnowledgeCaps([...STRATEGY_CAPABILITIES[p]]);
2958
+ setBudget(STRATEGY_BUDGETS[p]);
2730
2959
  }
2731
2960
  }, []);
2732
2961
  const updatePhase = useCallback4((phase, field, value) => {
@@ -2768,6 +2997,22 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2768
2997
  );
2769
2998
  setInstructions(prefs.instructions || "");
2770
2999
  setRequireApproval(!!prefs.requireApproval);
3000
+ if (prefs.behavior) {
3001
+ const b = prefs.behavior;
3002
+ if (Array.isArray(b.capabilities)) {
3003
+ setKnowledgeCaps(b.capabilities);
3004
+ } else {
3005
+ setKnowledgeCaps([...STRATEGY_CAPABILITIES[strat]]);
3006
+ }
3007
+ if (typeof b.budget === "number") {
3008
+ setBudget(b.budget);
3009
+ } else {
3010
+ setBudget(STRATEGY_BUDGETS[strat]);
3011
+ }
3012
+ } else {
3013
+ setKnowledgeCaps([...STRATEGY_CAPABILITIES[strat]]);
3014
+ setBudget(STRATEGY_BUDGETS[strat]);
3015
+ }
2771
3016
  if (prefs.behavior) {
2772
3017
  const b = prefs.behavior;
2773
3018
  const phases = b.phases;
@@ -2812,6 +3057,13 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2812
3057
  const handleRemoveCapability = useCallback4((index) => {
2813
3058
  setCapabilities((prev) => prev.filter((_, i) => i !== index));
2814
3059
  }, []);
3060
+ const handleToggleKnowledgeCap = useCallback4((capName2) => {
3061
+ setKnowledgeCaps((prev) => {
3062
+ if (capName2 === "core") return prev;
3063
+ return prev.includes(capName2) ? prev.filter((n) => n !== capName2) : [...prev, capName2];
3064
+ });
3065
+ setPreset("custom");
3066
+ }, []);
2815
3067
  const handleAddScopePath = useCallback4((type) => {
2816
3068
  const input = type === "blocked" ? blockedInput : allowedInput;
2817
3069
  const trimmed = input.trim();
@@ -2852,6 +3104,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2852
3104
  }
2853
3105
  }
2854
3106
  const behaviorPayload = {
3107
+ capabilities: knowledgeCaps,
3108
+ budget,
2855
3109
  phases,
2856
3110
  escalation: { maxAttempts: behavior.maxAttempts, onExhausted: behavior.onExhausted },
2857
3111
  scope: behavior.blockedPaths.length > 0 || behavior.allowedPaths.length > 0 ? {
@@ -2897,7 +3151,7 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
2897
3151
  } catch (err) {
2898
3152
  toast4(err instanceof Error ? err.message : `Failed to ${mode} profile`, { type: "error" });
2899
3153
  }
2900
- }, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, instructions, requireApproval, behavior, callTool, onSave]);
3154
+ }, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, knowledgeCaps, budget, instructions, requireApproval, behavior, callTool, onSave]);
2901
3155
  const handleDelete = useCallback4(async () => {
2902
3156
  if (!profileId) return;
2903
3157
  const ok = await ctx.confirm("Are you sure you want to delete this profile?", {
@@ -3001,361 +3255,431 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
3001
3255
  ),
3002
3256
  // ── Scrollable form body ──
3003
3257
  React9.createElement(
3004
- Flex8,
3258
+ ScrollArea3,
3005
3259
  {
3006
- variant: "column-stretch-start-nowrap-10",
3007
- style: { flex: 1, minHeight: 0, overflow: "auto", padding: "12px 16px" }
3260
+ style: { flex: 1, minHeight: 0 }
3008
3261
  },
3009
- // ── Name ──
3010
- React9.createElement(
3011
- Field3,
3012
- { label: "Name" },
3013
- React9.createElement(Input2, {
3014
- type: "text",
3015
- size: "small",
3016
- placeholder: "Profile name",
3017
- value: name,
3018
- onChange: (v) => setName(v),
3019
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3020
- inputBoxStyle: { maxWidth: "none" }
3021
- })
3022
- ),
3023
- // ── Description ──
3024
3262
  React9.createElement(
3025
- Field3,
3026
- { label: "Description" },
3027
- React9.createElement(Input2, {
3028
- type: "text",
3029
- size: "small",
3030
- placeholder: "What does this profile do?",
3031
- value: description,
3032
- onChange: (v) => setDescription(v),
3033
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3034
- inputBoxStyle: { maxWidth: "none" }
3035
- })
3036
- ),
3037
- // ── Bot ──
3038
- React9.createElement(
3039
- Field3,
3040
- { label: "Bot" },
3041
- React9.createElement(Input2, {
3042
- type: "select",
3043
- size: "small",
3044
- options: bots.map((b) => ({ id: b.id, label: b.name, icon: b.icon || "smartToy" })),
3045
- optionId: botId,
3046
- onChange: (id) => setBotId(id),
3047
- disabled: mode === "edit",
3048
- placeholder: "Select bot",
3049
- defaultBoxStyle: { flex: 1, minWidth: 0 }
3050
- })
3051
- ),
3052
- // ── Icon ──
3053
- React9.createElement(
3054
- Field3,
3055
- { label: "Icon", align: "start" },
3056
- React9.createElement(IconPicker, {
3057
- catalog: ICON_CATALOG,
3058
- value: icon,
3059
- onChange: (v) => setIcon(v),
3060
- accentColor: color,
3061
- variant: "inline"
3062
- })
3063
- ),
3064
- // ── Color ──
3065
- React9.createElement(
3066
- Field3,
3067
- { label: "Color", align: "start" },
3068
- React9.createElement(ColorPicker, {
3069
- colors: BOT_COLORS,
3070
- value: color,
3071
- onChange: (v) => setColor(v),
3072
- variant: "inline"
3073
- })
3074
- ),
3075
- // ── Preset (ToggleGroup with Custom option) ──
3076
- React9.createElement(
3077
- Field3,
3078
- { label: "Preset", align: "start" },
3079
- React9.createElement(ToggleGroup, {
3080
- options: [
3081
- { label: "Fast", value: "frugal", icon: "speed", selected: preset === "frugal" },
3082
- { label: "Mid", value: "balanced", icon: "balance", selected: preset === "balanced" },
3083
- { label: "Smart", value: "performance", icon: "psychology", selected: preset === "performance" },
3084
- { label: "Custom", value: "custom", icon: "settings", selected: preset === "custom" }
3085
- ],
3086
- onSelect: handlePresetChange,
3087
- size: "extraSmall"
3088
- })
3089
- ),
3090
- // ── Phase Configuration (always visible, readOnly when preset) ──
3091
- React9.createElement(
3092
- Field3,
3093
- { label: "Phases", align: "start" },
3263
+ Flex8,
3264
+ {
3265
+ variant: "column-stretch-start-nowrap-10",
3266
+ style: { padding: "12px 16px" }
3267
+ },
3268
+ // ── Name ──
3094
3269
  React9.createElement(
3095
- Flex8,
3096
- {
3097
- variant: "column-stretch-start-nowrap-6"
3098
- },
3099
- ...Object.entries(behavior).filter(([k]) => !["maxAttempts", "onExhausted", "blockedPaths", "allowedPaths"].includes(k)).map(([phaseName, phase]) => {
3100
- const phaseObj = phase;
3101
- const isLlm = phaseObj.tier !== void 0;
3102
- const label = phaseName.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
3103
- return phaseRow(phaseName, label, isLlm);
3270
+ Field3,
3271
+ { label: "Name" },
3272
+ React9.createElement(Input2, {
3273
+ type: "text",
3274
+ size: "small",
3275
+ placeholder: "Profile name",
3276
+ value: name,
3277
+ onChange: (v) => setName(v),
3278
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
3279
+ inputBoxStyle: { maxWidth: "none" }
3104
3280
  })
3105
- )
3106
- ),
3107
- // ── Escalation (always visible, readOnly when preset) ──
3108
- React9.createElement(
3109
- Field3,
3110
- { label: "Retries", align: "start" },
3281
+ ),
3282
+ // ── Description ──
3111
3283
  React9.createElement(
3112
- Flex8,
3113
- {
3114
- variant: "column-stretch-start-nowrap-8"
3115
- },
3284
+ Field3,
3285
+ { label: "Description" },
3286
+ React9.createElement(Input2, {
3287
+ type: "text",
3288
+ size: "small",
3289
+ placeholder: "What does this profile do?",
3290
+ value: description,
3291
+ onChange: (v) => setDescription(v),
3292
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
3293
+ inputBoxStyle: { maxWidth: "none" }
3294
+ })
3295
+ ),
3296
+ // ── Bot ──
3297
+ React9.createElement(
3298
+ Field3,
3299
+ { label: "Bot" },
3300
+ React9.createElement(Input2, {
3301
+ type: "select",
3302
+ size: "small",
3303
+ options: bots.map((b) => ({ id: b.id, label: b.name, icon: b.icon || "smartToy" })),
3304
+ optionId: botId,
3305
+ onChange: (id) => setBotId(id),
3306
+ disabled: mode === "edit",
3307
+ placeholder: "Select bot",
3308
+ defaultBoxStyle: { flex: 1, minWidth: 0 }
3309
+ })
3310
+ ),
3311
+ // ── Icon ──
3312
+ React9.createElement(
3313
+ Field3,
3314
+ { label: "Icon", align: "start" },
3315
+ React9.createElement(IconPicker, {
3316
+ catalog: ICON_CATALOG,
3317
+ value: icon,
3318
+ onChange: (v) => setIcon(v),
3319
+ accentColor: color,
3320
+ variant: "inline"
3321
+ })
3322
+ ),
3323
+ // ── Color ──
3324
+ React9.createElement(
3325
+ Field3,
3326
+ { label: "Color", align: "start" },
3327
+ React9.createElement(ColorPicker, {
3328
+ colors: BOT_COLORS,
3329
+ value: color,
3330
+ onChange: (v) => setColor(v),
3331
+ variant: "inline"
3332
+ })
3333
+ ),
3334
+ // ── Preset (ToggleGroup with Custom option) ──
3335
+ React9.createElement(
3336
+ Field3,
3337
+ { label: "Preset", align: "start" },
3338
+ React9.createElement(ToggleGroup, {
3339
+ options: [
3340
+ { label: "Fast", value: "frugal", icon: "speed", selected: preset === "frugal" },
3341
+ { label: "Mid", value: "balanced", icon: "balance", selected: preset === "balanced" },
3342
+ { label: "Smart", value: "performance", icon: "psychology", selected: preset === "performance" },
3343
+ { label: "Custom", value: "custom", icon: "settings", selected: preset === "custom" }
3344
+ ],
3345
+ onSelect: handlePresetChange,
3346
+ size: "extraSmall"
3347
+ })
3348
+ ),
3349
+ // ── Knowledge Capabilities (checkbox grid) ──
3350
+ React9.createElement(
3351
+ Field3,
3352
+ { label: "Knowledge", align: "start" },
3353
+ React9.createElement(
3354
+ Flex8,
3355
+ { variant: "column-stretch-start-nowrap-2" },
3356
+ ...BUILT_IN_CAPABILITIES.map(
3357
+ (cap) => React9.createElement(
3358
+ Flex8,
3359
+ {
3360
+ key: cap.name,
3361
+ variant: "row-center-start-nowrap-8",
3362
+ style: { minHeight: 28, padding: "1px 0" }
3363
+ },
3364
+ React9.createElement(Checkbox2, {
3365
+ checked: knowledgeCaps.includes(cap.name),
3366
+ onChange: () => handleToggleKnowledgeCap(cap.name),
3367
+ disabled: cap.name === "core" || !isCustom,
3368
+ size: "sm"
3369
+ }),
3370
+ React9.createElement(Typography8, {
3371
+ variant: "smallCaption-thick",
3372
+ color: knowledgeCaps.includes(cap.name) ? "color-text-high" : "color-text-subtle",
3373
+ style: { minWidth: 80 }
3374
+ }, cap.name),
3375
+ React9.createElement(Typography8, {
3376
+ variant: "smallCaption-regular",
3377
+ color: "color-text-subtle",
3378
+ style: { flex: 1 }
3379
+ }, cap.description)
3380
+ )
3381
+ )
3382
+ )
3383
+ ),
3384
+ // ── Budget ──
3385
+ React9.createElement(
3386
+ Field3,
3387
+ { label: "Budget", align: "center" },
3116
3388
  React9.createElement(
3117
- Field3,
3118
- { label: "Max attempts", labelWidth: 100, align: "center" },
3389
+ Flex8,
3390
+ { variant: "row-center-start-nowrap-6" },
3391
+ React9.createElement(Typography8, {
3392
+ variant: "smallCaption-regular",
3393
+ color: "color-text-subtle"
3394
+ }, "$"),
3119
3395
  React9.createElement(Input2, {
3120
3396
  type: "number",
3121
3397
  size: "small",
3122
- value: String(behavior.maxAttempts),
3398
+ value: String(budget),
3123
3399
  readOnly: !isCustom,
3124
- onChange: isCustom ? (v) => updateEscalation("maxAttempts", Math.max(1, parseInt(v) || 1)) : void 0,
3125
- defaultBoxStyle: { width: 60 }
3126
- })
3127
- ),
3400
+ onChange: isCustom ? (v) => {
3401
+ const n = parseFloat(v);
3402
+ if (!isNaN(n) && n >= 0) setBudget(n);
3403
+ } : void 0,
3404
+ defaultBoxStyle: { width: 80 }
3405
+ }),
3406
+ React9.createElement(Typography8, {
3407
+ variant: "smallCaption-regular",
3408
+ color: "color-text-subtle"
3409
+ }, "/ task")
3410
+ )
3411
+ ),
3412
+ // ── Phase Configuration (always visible, readOnly when preset) ──
3413
+ React9.createElement(
3414
+ Field3,
3415
+ { label: "Phases", align: "start" },
3128
3416
  React9.createElement(
3129
- Field3,
3130
- { label: "On exhausted", labelWidth: 100, align: "start" },
3131
- React9.createElement(ToggleGroup, {
3132
- options: [
3133
- { label: "Block", value: "block", icon: "block", selected: behavior.onExhausted === "block" },
3134
- { label: "Reassign", value: "reassign", icon: "sync", selected: behavior.onExhausted === "reassign" },
3135
- { label: "Notify", value: "notify", icon: "notifications", selected: behavior.onExhausted === "notify" }
3136
- ],
3137
- onSelect: (v) => updateEscalation("onExhausted", v),
3138
- readOnly: !isCustom,
3139
- size: "extraSmall"
3417
+ Flex8,
3418
+ {
3419
+ variant: "column-stretch-start-nowrap-6"
3420
+ },
3421
+ ...Object.entries(behavior).filter(([k]) => !["maxAttempts", "onExhausted", "blockedPaths", "allowedPaths"].includes(k)).map(([phaseName, phase]) => {
3422
+ const phaseObj = phase;
3423
+ const isLlm = phaseObj.tier !== void 0;
3424
+ const label = phaseName.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
3425
+ return phaseRow(phaseName, label, isLlm);
3140
3426
  })
3141
3427
  )
3142
- )
3143
- ),
3144
- // ── Scope Constraints (collapsible) ──
3145
- // ── Custom: Scope Constraints (inline) ──
3146
- isCustom && React9.createElement(
3147
- Field3,
3148
- { label: "Scope", align: "start" },
3428
+ ),
3429
+ // ── Escalation (always visible, readOnly when preset) ──
3149
3430
  React9.createElement(
3150
- Flex8,
3151
- {
3152
- variant: "column-stretch-start-nowrap-8"
3153
- },
3154
- // Blocked paths
3431
+ Field3,
3432
+ { label: "Retries", align: "start" },
3155
3433
  React9.createElement(
3156
- Field3,
3157
- { label: "Blocked", labelWidth: 70, align: "start" },
3434
+ Flex8,
3435
+ {
3436
+ variant: "column-stretch-start-nowrap-8"
3437
+ },
3158
3438
  React9.createElement(
3159
- Flex8,
3160
- { variant: "column-stretch-start-nowrap-4" },
3439
+ Field3,
3440
+ { label: "Max attempts", labelWidth: 100, align: "center" },
3441
+ React9.createElement(Input2, {
3442
+ type: "number",
3443
+ size: "small",
3444
+ value: String(behavior.maxAttempts),
3445
+ readOnly: !isCustom,
3446
+ onChange: isCustom ? (v) => updateEscalation("maxAttempts", Math.max(1, parseInt(v) || 1)) : void 0,
3447
+ defaultBoxStyle: { width: 60 }
3448
+ })
3449
+ ),
3450
+ React9.createElement(
3451
+ Field3,
3452
+ { label: "On exhausted", labelWidth: 100, align: "start" },
3453
+ React9.createElement(ToggleGroup, {
3454
+ options: [
3455
+ { label: "Block", value: "block", icon: "block", selected: behavior.onExhausted === "block" },
3456
+ { label: "Reassign", value: "reassign", icon: "sync", selected: behavior.onExhausted === "reassign" },
3457
+ { label: "Notify", value: "notify", icon: "notifications", selected: behavior.onExhausted === "notify" }
3458
+ ],
3459
+ onSelect: (v) => updateEscalation("onExhausted", v),
3460
+ readOnly: !isCustom,
3461
+ size: "extraSmall"
3462
+ })
3463
+ )
3464
+ )
3465
+ ),
3466
+ // ── Scope Constraints (collapsible) ──
3467
+ // ── Custom: Scope Constraints (inline) ──
3468
+ isCustom && React9.createElement(
3469
+ Field3,
3470
+ { label: "Scope", align: "start" },
3471
+ React9.createElement(
3472
+ Flex8,
3473
+ {
3474
+ variant: "column-stretch-start-nowrap-8"
3475
+ },
3476
+ // Blocked paths
3477
+ React9.createElement(
3478
+ Field3,
3479
+ { label: "Blocked", labelWidth: 70, align: "start" },
3161
3480
  React9.createElement(
3162
3481
  Flex8,
3163
- { variant: "row-center-start-nowrap-4" },
3164
- React9.createElement(Input2, {
3165
- type: "text",
3166
- size: "small",
3167
- placeholder: "e.g. src/",
3168
- value: blockedInput,
3169
- onChange: (v) => setBlockedInput(v),
3170
- onEnter: () => handleAddScopePath("blocked"),
3171
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3172
- inputBoxStyle: { maxWidth: "none" }
3173
- }),
3174
- React9.createElement(IconButton6, {
3175
- icon: "add",
3176
- size: "sm",
3177
- variant: "outlined",
3178
- color: "primary",
3179
- onClick: () => handleAddScopePath("blocked"),
3180
- disabled: !blockedInput.trim()
3181
- })
3182
- ),
3183
- ...behavior.blockedPaths.map(
3184
- (p, i) => React9.createElement(
3482
+ { variant: "column-stretch-start-nowrap-4" },
3483
+ React9.createElement(
3185
3484
  Flex8,
3186
- { key: `b-${p}-${i}`, variant: "row-center-start-nowrap-6" },
3187
- React9.createElement(Icon5, { name: "block", size: 12, color: "color-status-negative" }),
3188
- React9.createElement(Typography8, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
3485
+ { variant: "row-center-start-nowrap-4" },
3486
+ React9.createElement(Input2, {
3487
+ type: "text",
3488
+ size: "small",
3489
+ placeholder: "e.g. src/",
3490
+ value: blockedInput,
3491
+ onChange: (v) => setBlockedInput(v),
3492
+ onEnter: () => handleAddScopePath("blocked"),
3493
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
3494
+ inputBoxStyle: { maxWidth: "none" }
3495
+ }),
3189
3496
  React9.createElement(IconButton6, {
3190
- icon: "close",
3191
- size: "xs",
3192
- variant: "clear",
3193
- color: "danger",
3194
- onClick: () => handleRemoveScopePath("blocked", i)
3497
+ icon: "add",
3498
+ size: "sm",
3499
+ variant: "outlined",
3500
+ color: "primary",
3501
+ onClick: () => handleAddScopePath("blocked"),
3502
+ disabled: !blockedInput.trim()
3195
3503
  })
3504
+ ),
3505
+ ...behavior.blockedPaths.map(
3506
+ (p, i) => React9.createElement(
3507
+ Flex8,
3508
+ { key: `b-${p}-${i}`, variant: "row-center-start-nowrap-6" },
3509
+ React9.createElement(Icon5, { name: "block", size: 12, color: "color-status-negative" }),
3510
+ React9.createElement(Typography8, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
3511
+ React9.createElement(IconButton6, {
3512
+ icon: "close",
3513
+ size: "xs",
3514
+ variant: "clear",
3515
+ color: "danger",
3516
+ onClick: () => handleRemoveScopePath("blocked", i)
3517
+ })
3518
+ )
3196
3519
  )
3197
3520
  )
3198
- )
3199
- ),
3200
- // Allowed paths
3201
- React9.createElement(
3202
- Field3,
3203
- { label: "Allowed", labelWidth: 70, align: "start" },
3521
+ ),
3522
+ // Allowed paths
3204
3523
  React9.createElement(
3205
- Flex8,
3206
- { variant: "column-stretch-start-nowrap-4" },
3524
+ Field3,
3525
+ { label: "Allowed", labelWidth: 70, align: "start" },
3207
3526
  React9.createElement(
3208
3527
  Flex8,
3209
- { variant: "row-center-start-nowrap-4" },
3210
- React9.createElement(Input2, {
3211
- type: "text",
3212
- size: "small",
3213
- placeholder: "e.g. config/",
3214
- value: allowedInput,
3215
- onChange: (v) => setAllowedInput(v),
3216
- onEnter: () => handleAddScopePath("allowed"),
3217
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3218
- inputBoxStyle: { maxWidth: "none" }
3219
- }),
3220
- React9.createElement(IconButton6, {
3221
- icon: "add",
3222
- size: "sm",
3223
- variant: "outlined",
3224
- color: "primary",
3225
- onClick: () => handleAddScopePath("allowed"),
3226
- disabled: !allowedInput.trim()
3227
- })
3228
- ),
3229
- ...behavior.allowedPaths.map(
3230
- (p, i) => React9.createElement(
3528
+ { variant: "column-stretch-start-nowrap-4" },
3529
+ React9.createElement(
3231
3530
  Flex8,
3232
- { key: `a-${p}-${i}`, variant: "row-center-start-nowrap-6" },
3233
- React9.createElement(Icon5, { name: "checkCircle", size: 12, color: "color-status-positive" }),
3234
- React9.createElement(Typography8, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
3531
+ { variant: "row-center-start-nowrap-4" },
3532
+ React9.createElement(Input2, {
3533
+ type: "text",
3534
+ size: "small",
3535
+ placeholder: "e.g. config/",
3536
+ value: allowedInput,
3537
+ onChange: (v) => setAllowedInput(v),
3538
+ onEnter: () => handleAddScopePath("allowed"),
3539
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
3540
+ inputBoxStyle: { maxWidth: "none" }
3541
+ }),
3235
3542
  React9.createElement(IconButton6, {
3236
- icon: "close",
3237
- size: "xs",
3238
- variant: "clear",
3239
- color: "danger",
3240
- onClick: () => handleRemoveScopePath("allowed", i)
3543
+ icon: "add",
3544
+ size: "sm",
3545
+ variant: "outlined",
3546
+ color: "primary",
3547
+ onClick: () => handleAddScopePath("allowed"),
3548
+ disabled: !allowedInput.trim()
3241
3549
  })
3550
+ ),
3551
+ ...behavior.allowedPaths.map(
3552
+ (p, i) => React9.createElement(
3553
+ Flex8,
3554
+ { key: `a-${p}-${i}`, variant: "row-center-start-nowrap-6" },
3555
+ React9.createElement(Icon5, { name: "checkCircle", size: 12, color: "color-status-positive" }),
3556
+ React9.createElement(Typography8, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
3557
+ React9.createElement(IconButton6, {
3558
+ icon: "close",
3559
+ size: "xs",
3560
+ variant: "clear",
3561
+ color: "danger",
3562
+ onClick: () => handleRemoveScopePath("allowed", i)
3563
+ })
3564
+ )
3242
3565
  )
3243
3566
  )
3244
3567
  )
3245
3568
  )
3246
- )
3247
- ),
3248
- // ── Capabilities ──
3249
- React9.createElement(
3250
- Field3,
3251
- { label: "Capabilities", align: "start" },
3569
+ ),
3570
+ // ── Capabilities ──
3252
3571
  React9.createElement(
3253
- Flex8,
3254
- { variant: "column-stretch-start-nowrap-6" },
3255
- // Add row
3572
+ Field3,
3573
+ { label: "Capabilities", align: "start" },
3256
3574
  React9.createElement(
3257
3575
  Flex8,
3258
- { variant: "row-center-start-nowrap-4", style: { overflow: "hidden" } },
3259
- React9.createElement(Input2, {
3260
- type: "text",
3261
- size: "small",
3262
- placeholder: "Name",
3263
- value: capName,
3264
- onChange: (v) => setCapName(v),
3265
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3266
- inputBoxStyle: { maxWidth: "none" }
3267
- }),
3268
- React9.createElement(Input2, {
3269
- type: "text",
3270
- size: "small",
3271
- placeholder: "Description",
3272
- value: capDescription,
3273
- onChange: (v) => setCapDescription(v),
3274
- onEnter: handleAddCapability,
3275
- defaultBoxStyle: { flex: 2, minWidth: 0 },
3276
- inputBoxStyle: { maxWidth: "none" }
3277
- }),
3278
- React9.createElement(IconButton6, {
3279
- icon: "add",
3280
- size: "sm",
3281
- variant: "outlined",
3282
- color: "primary",
3283
- onClick: handleAddCapability,
3284
- disabled: !capName.trim() || !capDescription.trim()
3576
+ { variant: "column-stretch-start-nowrap-6" },
3577
+ // Add row
3578
+ React9.createElement(
3579
+ Flex8,
3580
+ { variant: "row-center-start-nowrap-4", style: { minWidth: 0 } },
3581
+ React9.createElement(Input2, {
3582
+ type: "text",
3583
+ size: "small",
3584
+ placeholder: "Name",
3585
+ value: capName,
3586
+ onChange: (v) => setCapName(v),
3587
+ defaultBoxStyle: { flex: "0 1 120px", minWidth: 0 },
3588
+ inputBoxStyle: { maxWidth: "none", minWidth: 0 }
3589
+ }),
3590
+ React9.createElement(Input2, {
3591
+ type: "text",
3592
+ size: "small",
3593
+ placeholder: "Description",
3594
+ value: capDescription,
3595
+ onChange: (v) => setCapDescription(v),
3596
+ onEnter: handleAddCapability,
3597
+ defaultBoxStyle: { flex: "1 1 0", minWidth: 0 },
3598
+ inputBoxStyle: { maxWidth: "none", minWidth: 0 }
3599
+ }),
3600
+ React9.createElement(IconButton6, {
3601
+ icon: "add",
3602
+ size: "sm",
3603
+ variant: "outlined",
3604
+ color: "primary",
3605
+ onClick: handleAddCapability,
3606
+ disabled: !capName.trim() || !capDescription.trim(),
3607
+ style: { flexShrink: 0 }
3608
+ })
3609
+ ),
3610
+ // Table
3611
+ capabilities.length > 0 && React9.createElement(Table2, {
3612
+ size: "compact",
3613
+ showHeader: false,
3614
+ data: capabilities,
3615
+ getRowKey: (_row, i) => `cap-${i}`,
3616
+ columns: [
3617
+ {
3618
+ key: "name",
3619
+ header: "Name",
3620
+ width: "30%",
3621
+ render: (val) => React9.createElement("div", { style: { lineHeight: "28px" } }, String(val))
3622
+ },
3623
+ {
3624
+ key: "description",
3625
+ header: "Description",
3626
+ render: (val) => React9.createElement("div", { style: { lineHeight: "28px" } }, String(val))
3627
+ },
3628
+ {
3629
+ key: "actions",
3630
+ header: "",
3631
+ width: "32px",
3632
+ align: "right",
3633
+ render: (_val, _row, idx) => React9.createElement(IconButton6, {
3634
+ icon: "close",
3635
+ size: "xs",
3636
+ variant: "clear",
3637
+ color: "danger",
3638
+ onClick: () => handleRemoveCapability(idx)
3639
+ })
3640
+ }
3641
+ ]
3285
3642
  })
3286
- ),
3287
- // Table
3288
- capabilities.length > 0 && React9.createElement(Table2, {
3289
- size: "compact",
3290
- showHeader: false,
3291
- data: capabilities,
3292
- getRowKey: (_row, i) => `cap-${i}`,
3293
- columns: [
3294
- {
3295
- key: "name",
3296
- header: "Name",
3297
- width: "30%",
3298
- render: (val) => React9.createElement("div", { style: { lineHeight: "28px" } }, String(val))
3299
- },
3300
- {
3301
- key: "description",
3302
- header: "Description",
3303
- render: (val) => React9.createElement("div", { style: { lineHeight: "28px" } }, String(val))
3304
- },
3305
- {
3306
- key: "actions",
3307
- header: "",
3308
- width: "32px",
3309
- align: "right",
3310
- render: (_val, _row, idx) => React9.createElement(IconButton6, {
3311
- icon: "close",
3312
- size: "xs",
3313
- variant: "clear",
3314
- color: "danger",
3315
- onClick: () => handleRemoveCapability(idx)
3316
- })
3317
- }
3318
- ]
3643
+ )
3644
+ ),
3645
+ // ── Instructions (multiline) ──
3646
+ React9.createElement(
3647
+ Field3,
3648
+ { label: "Instructions", align: "start" },
3649
+ React9.createElement(Input2, {
3650
+ type: "text",
3651
+ size: "small",
3652
+ multiline: true,
3653
+ placeholder: "Behavioral protocol for this profile (optional)",
3654
+ value: instructions,
3655
+ onChange: (v) => setInstructions(v),
3656
+ defaultBoxStyle: { flex: 1, minWidth: 0 },
3657
+ inputBoxStyle: { maxWidth: "none" }
3658
+ })
3659
+ ),
3660
+ // ── Require Approval ──
3661
+ React9.createElement(
3662
+ Field3,
3663
+ { label: "" },
3664
+ React9.createElement(Checkbox2, {
3665
+ checked: requireApproval,
3666
+ onChange: (v) => setRequireApproval(v),
3667
+ label: "Require approval",
3668
+ size: "sm"
3319
3669
  })
3670
+ ),
3671
+ // ── Save button ──
3672
+ React9.createElement(
3673
+ Flex8,
3674
+ { variant: "row-center-end-nowrap-8" },
3675
+ React9.createElement(Button4, {
3676
+ size: "xs",
3677
+ variant: "fill",
3678
+ color: "primary",
3679
+ onClick: handleSave,
3680
+ disabled: !name.trim() || !botId || capabilities.length === 0
3681
+ }, mode === "create" ? "Create" : "Save")
3320
3682
  )
3321
- ),
3322
- // ── Instructions (multiline) ──
3323
- React9.createElement(
3324
- Field3,
3325
- { label: "Instructions", align: "start" },
3326
- React9.createElement(Input2, {
3327
- type: "text",
3328
- size: "small",
3329
- multiline: true,
3330
- placeholder: "Behavioral protocol for this profile (optional)",
3331
- value: instructions,
3332
- onChange: (v) => setInstructions(v),
3333
- defaultBoxStyle: { flex: 1, minWidth: 0 },
3334
- inputBoxStyle: { maxWidth: "none" }
3335
- })
3336
- ),
3337
- // ── Require Approval ──
3338
- React9.createElement(
3339
- Field3,
3340
- { label: "" },
3341
- React9.createElement(Checkbox2, {
3342
- checked: requireApproval,
3343
- onChange: (v) => setRequireApproval(v),
3344
- label: "Require approval",
3345
- size: "sm"
3346
- })
3347
- ),
3348
- // ── Save button ──
3349
- React9.createElement(
3350
- Flex8,
3351
- { variant: "row-center-end-nowrap-8" },
3352
- React9.createElement(Button4, {
3353
- size: "xs",
3354
- variant: "fill",
3355
- color: "primary",
3356
- onClick: handleSave,
3357
- disabled: !name.trim() || !botId || capabilities.length === 0
3358
- }, mode === "create" ? "Create" : "Save")
3359
3683
  )
3360
3684
  )
3361
3685
  );
@@ -3368,7 +3692,7 @@ var React10 = require("react");
3368
3692
  var {
3369
3693
  Flex: Flex9,
3370
3694
  Typography: Typography9,
3371
- ScrollArea: ScrollArea3,
3695
+ ScrollArea: ScrollArea4,
3372
3696
  Chip: Chip6,
3373
3697
  SectionTitle: SectionTitle2
3374
3698
  } = require("@fw/plugin-ui-kit");
@@ -3415,7 +3739,7 @@ function DecisionLog({ decisions }) {
3415
3739
  },
3416
3740
  React10.createElement(SectionTitle2, null, "Decision Log"),
3417
3741
  React10.createElement(
3418
- ScrollArea3,
3742
+ ScrollArea4,
3419
3743
  {
3420
3744
  style: { maxHeight: "260px" }
3421
3745
  },
@@ -3481,7 +3805,7 @@ var React11 = require("react");
3481
3805
  var { useState: useState7, useEffect: useEffect5, useCallback: useCallback5, useRef: useRef3 } = React11;
3482
3806
  var {
3483
3807
  Flex: Flex10,
3484
- ScrollArea: ScrollArea4,
3808
+ ScrollArea: ScrollArea5,
3485
3809
  EmptyState: EmptyState3,
3486
3810
  Typography: Typography10,
3487
3811
  StatusIcon: StatusIcon4,