@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.
- package/dist/bot/ai-client.d.ts +9 -0
- package/dist/bot/ai-client.d.ts.map +1 -1
- package/dist/bot/ai-client.js +59 -0
- package/dist/bot/ai-client.js.map +1 -1
- package/dist/bot/behavior-defaults.d.ts +4 -0
- package/dist/bot/behavior-defaults.d.ts.map +1 -1
- package/dist/bot/behavior-defaults.js +17 -0
- package/dist/bot/behavior-defaults.js.map +1 -1
- package/dist/bot/capability-registry.d.ts +20 -0
- package/dist/bot/capability-registry.d.ts.map +1 -0
- package/dist/bot/capability-registry.js +205 -0
- package/dist/bot/capability-registry.js.map +1 -0
- package/dist/bot/capability-types.d.ts +23 -0
- package/dist/bot/capability-types.d.ts.map +1 -0
- package/dist/bot/capability-types.js +9 -0
- package/dist/bot/capability-types.js.map +1 -0
- package/dist/bot/estimate-tokens.d.ts +12 -0
- package/dist/bot/estimate-tokens.d.ts.map +1 -0
- package/dist/bot/estimate-tokens.js +18 -0
- package/dist/bot/estimate-tokens.js.map +1 -0
- package/dist/bot/profile-types.d.ts +4 -0
- package/dist/bot/profile-types.d.ts.map +1 -1
- package/dist/bot/system-prompt.d.ts +11 -0
- package/dist/bot/system-prompt.d.ts.map +1 -1
- package/dist/bot/system-prompt.js +31 -0
- package/dist/bot/system-prompt.js.map +1 -1
- package/dist/bot/types.d.ts +1 -0
- package/dist/bot/types.d.ts.map +1 -1
- package/dist/node-types/plan-task.d.ts.map +1 -1
- package/dist/node-types/plan-task.js +49 -45
- package/dist/node-types/plan-task.js.map +1 -1
- package/dist/ui/capability-editor.js +657 -0
- package/dist/ui/profile-card.js +25 -0
- package/dist/ui/profile-editor.js +615 -316
- package/dist/ui/swarm-dashboard.js +643 -319
- package/flowweaver.manifest.json +1 -1
- package/package.json +2 -2
- package/src/bot/ai-client.ts +75 -0
- package/src/bot/behavior-defaults.ts +20 -0
- package/src/bot/capability-registry.ts +230 -0
- package/src/bot/capability-types.ts +23 -0
- package/src/bot/estimate-tokens.ts +16 -0
- package/src/bot/profile-types.ts +4 -0
- package/src/bot/system-prompt.ts +33 -0
- package/src/bot/types.ts +1 -0
- package/src/node-types/plan-task.ts +51 -44
- package/src/ui/capability-editor.tsx +420 -0
- package/src/ui/profile-card.tsx +20 -0
- package/src/ui/profile-editor.tsx +100 -6
|
@@ -207,6 +207,205 @@ var BOT_COLORS = [
|
|
|
207
207
|
{ label: "Info", token: "color-status-info" }
|
|
208
208
|
];
|
|
209
209
|
|
|
210
|
+
// src/bot/operations.ts
|
|
211
|
+
var OP_WRITE_FILE = "write_file";
|
|
212
|
+
var OP_READ_FILE = "read_file";
|
|
213
|
+
var OP_PATCH_FILE = "patch_file";
|
|
214
|
+
var OP_LIST_FILES = "list_files";
|
|
215
|
+
var OP_RUN_SHELL = "run_shell";
|
|
216
|
+
var OP_VALIDATE = "validate";
|
|
217
|
+
var OP_TSC_CHECK = "tsc_check";
|
|
218
|
+
var OP_RUN_TESTS = "run_tests";
|
|
219
|
+
var OP_TASK_CREATE = "task_create";
|
|
220
|
+
|
|
221
|
+
// src/bot/capability-registry.ts
|
|
222
|
+
var CAP_CORE = {
|
|
223
|
+
name: "core",
|
|
224
|
+
description: "Bot identity, structured plan output format, and safety rules. Always loaded.",
|
|
225
|
+
prompt: `You are Weaver, an expert AI companion for Flow Weaver workflows.
|
|
226
|
+
|
|
227
|
+
## Plan Format
|
|
228
|
+
Your plans MUST be structured JSON with concrete steps.
|
|
229
|
+
Each step has: operation (tool name), description (what it does), args (complete arguments).
|
|
230
|
+
Do NOT describe what you would do \u2014 actually do it by calling tools.
|
|
231
|
+
|
|
232
|
+
## Safety Rules
|
|
233
|
+
- Writes that shrink a file by >50% or write empty content are automatically BLOCKED.
|
|
234
|
+
- Blocked shell commands: rm -rf, git push, npm publish, sudo, curl|sh.
|
|
235
|
+
- Always validate BEFORE and AFTER patching.
|
|
236
|
+
- Always read a file before patching it (you need exact strings for find/replace).
|
|
237
|
+
- Use patch_file for modifications, write_file only for new files.
|
|
238
|
+
- Be concise \u2014 let tool results speak.`
|
|
239
|
+
};
|
|
240
|
+
var CAP_FILE_OPS = {
|
|
241
|
+
name: "file-ops",
|
|
242
|
+
description: "File read/write/patch operations and best practices for file manipulation.",
|
|
243
|
+
tools: [OP_READ_FILE, OP_WRITE_FILE, OP_PATCH_FILE, OP_LIST_FILES],
|
|
244
|
+
prompt: `## File Operations
|
|
245
|
+
- read_file: Read a file and return its content. args: { file }
|
|
246
|
+
- write_file: Write a file. args: { file, content }. Content must be the COMPLETE file.
|
|
247
|
+
- patch_file: Surgical find-and-replace edits. args: { file, patches: [{ find: "old text", replace: "new text" }] }. PREFERRED for modifying existing files.
|
|
248
|
+
- list_files: List files in a directory. args: { directory, pattern? } (pattern is regex)
|
|
249
|
+
|
|
250
|
+
## Best Practices
|
|
251
|
+
PREFER patch_file over write_file for modifying existing files (surgical edits, no truncation risk).
|
|
252
|
+
Use read_file to understand a file before modifying it.
|
|
253
|
+
Use list_files to discover project structure.
|
|
254
|
+
Writes that shrink a file by >50% or write empty content are automatically BLOCKED.`
|
|
255
|
+
};
|
|
256
|
+
var CAP_SHELL = {
|
|
257
|
+
name: "shell",
|
|
258
|
+
description: "Shell command execution for running tests, builds, and inspecting output.",
|
|
259
|
+
tools: [OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS],
|
|
260
|
+
prompt: `## Shell Commands
|
|
261
|
+
- run_shell: Execute a shell command and return output. args: { command }
|
|
262
|
+
Use for: npx vitest, git status, grep, find, etc.
|
|
263
|
+
Examples: { "command": "npx vitest run --reporter verbose" }, { "command": "npx flow-weaver validate src/workflow.ts --json" }
|
|
264
|
+
Blocked: rm -rf, git push, npm publish, sudo, curl|sh (safety policy).
|
|
265
|
+
Use run_shell for running tests (npx vitest), validation (flow-weaver validate), and inspecting output.`
|
|
266
|
+
};
|
|
267
|
+
var CAP_TASK_MGMT = {
|
|
268
|
+
name: "task-mgmt",
|
|
269
|
+
description: "Create and manage swarm subtasks for parallel execution.",
|
|
270
|
+
tools: [OP_TASK_CREATE],
|
|
271
|
+
prompt: `## Task Management
|
|
272
|
+
- task_create: Create swarm subtasks for parallel execution. args: { title, description, complexity, subtasks[] }
|
|
273
|
+
- task_list, task_get, task_update: Query and update existing tasks
|
|
274
|
+
|
|
275
|
+
Use task_create to decompose complex work into smaller, independent subtasks that other bots can execute in parallel.`
|
|
276
|
+
};
|
|
277
|
+
var CAP_FW_GRAMMAR = {
|
|
278
|
+
name: "fw-grammar",
|
|
279
|
+
description: "Flow Weaver annotation syntax, node types, workflows, patterns, and data flow.",
|
|
280
|
+
prompt: `## Core Mental Model
|
|
281
|
+
|
|
282
|
+
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.
|
|
283
|
+
|
|
284
|
+
Three block types:
|
|
285
|
+
- @flowWeaver nodeType: A reusable function (node) with typed inputs/outputs
|
|
286
|
+
- @flowWeaver workflow: A DAG orchestration that wires node instances together
|
|
287
|
+
- @flowWeaver pattern: A reusable fragment with boundary ports (IN/OUT instead of Start/Exit)
|
|
288
|
+
|
|
289
|
+
## Node Execution Model
|
|
290
|
+
|
|
291
|
+
Expression nodes (@expression):
|
|
292
|
+
- No execute/onSuccess/onFailure params. Just inputs and return value.
|
|
293
|
+
- throw = failure path, return = success path
|
|
294
|
+
- Synchronous. Use execSync for shell commands.
|
|
295
|
+
- Preferred for deterministic operations.
|
|
296
|
+
|
|
297
|
+
Standard nodes:
|
|
298
|
+
- execute: boolean param gates execution
|
|
299
|
+
- Return { onSuccess: boolean, onFailure: boolean, ...outputs }
|
|
300
|
+
- Can be async for I/O operations
|
|
301
|
+
|
|
302
|
+
Async agent nodes:
|
|
303
|
+
- Use (globalThis as any).__fw_agent_channel__ to pause workflow
|
|
304
|
+
- Call channel.request({ agentId, context, prompt }) which returns a Promise
|
|
305
|
+
- Workflow suspends until agent responds
|
|
306
|
+
- NOT @expression (must be async)
|
|
307
|
+
|
|
308
|
+
Pass-through pattern:
|
|
309
|
+
- FW auto-connects ports by matching names on adjacent nodes
|
|
310
|
+
- To forward data through intermediate nodes, declare it as both @input and @output with the same name
|
|
311
|
+
- For non-adjacent wiring, use @connect sourceNode.port -> targetNode.port
|
|
312
|
+
|
|
313
|
+
Data flow:
|
|
314
|
+
- @path A -> B -> C: Linear execution path (sugar for multiple @connect)
|
|
315
|
+
- @autoConnect: Auto-wire all nodes in declaration order
|
|
316
|
+
- @connect: Explicit port-to-port wiring
|
|
317
|
+
- Merge strategies for fan-in: FIRST, LAST, COLLECT, MERGE, CONCAT`
|
|
318
|
+
};
|
|
319
|
+
var CAP_FW_VALIDATE = {
|
|
320
|
+
name: "fw-validate",
|
|
321
|
+
description: "Flow Weaver validation error codes and common fix patterns.",
|
|
322
|
+
prompt: `## Validation Errors
|
|
323
|
+
|
|
324
|
+
When you encounter validation errors, suggest the specific fix. Common resolutions:
|
|
325
|
+
- UNKNOWN_NODE_TYPE: Ensure the referenced function has @flowWeaver nodeType annotation
|
|
326
|
+
- MISSING_REQUIRED_INPUT: Add a @connect from a source port or make the input optional with [brackets]
|
|
327
|
+
- UNKNOWN_SOURCE_PORT / UNKNOWN_TARGET_PORT: Check port name spelling in @connect
|
|
328
|
+
- CYCLE_DETECTED: Break the cycle; use scoped iteration (@scope, @map) instead of circular dependencies`
|
|
329
|
+
};
|
|
330
|
+
var CAP_FW_GENESIS = {
|
|
331
|
+
name: "fw-genesis",
|
|
332
|
+
description: "Flow Weaver genesis protocol for self-evolving workflows.",
|
|
333
|
+
prompt: `## Genesis Protocol
|
|
334
|
+
|
|
335
|
+
Genesis is a 17-step self-evolving workflow engine:
|
|
336
|
+
1. Load config 2. Observe project 3. Load task workflow 4. Diff fingerprint
|
|
337
|
+
5. Check stabilize mode 6. Wait for agent 7. Propose evolution 8. Validate proposal
|
|
338
|
+
9. Snapshot for rollback 10. Apply changes 11. Compile and validate
|
|
339
|
+
12. Diff workflow 13. Check approval threshold 14. Wait for approval
|
|
340
|
+
15. Commit or rollback 16. Update history 17. Report summary
|
|
341
|
+
|
|
342
|
+
When stabilize mode is active, only fix-up operations are allowed.`
|
|
343
|
+
};
|
|
344
|
+
var CAP_FW_CLI = {
|
|
345
|
+
name: "fw-cli",
|
|
346
|
+
description: "Flow Weaver CLI command reference for compile, validate, diagram, and other operations.",
|
|
347
|
+
prompt: `## CLI Commands
|
|
348
|
+
|
|
349
|
+
Note: compile, validate, modify, diff, diagram, and describe operations are available as direct plan steps (no CLI needed).`
|
|
350
|
+
};
|
|
351
|
+
var CAP_CODE_REVIEW = {
|
|
352
|
+
name: "code-review",
|
|
353
|
+
description: "Code review guidelines, quality checklist, and security review patterns.",
|
|
354
|
+
prompt: `## Code Review
|
|
355
|
+
|
|
356
|
+
When reviewing code, check for:
|
|
357
|
+
1. Correctness: Does the code do what the task asked?
|
|
358
|
+
2. Security: No hardcoded secrets, no injection vulnerabilities, no exposed APIs
|
|
359
|
+
3. Style: Consistent with project conventions, proper naming, no dead code
|
|
360
|
+
4. Testing: Are there tests? Do they cover edge cases?
|
|
361
|
+
5. Performance: No unnecessary loops, no blocking calls in async code
|
|
362
|
+
|
|
363
|
+
Report concerns with specific file:line references and suggested fixes.`
|
|
364
|
+
};
|
|
365
|
+
var CAP_WEB = {
|
|
366
|
+
name: "web",
|
|
367
|
+
description: "Web fetch capability for fetching URLs and external resources.",
|
|
368
|
+
tools: ["web_fetch"],
|
|
369
|
+
prompt: `## Web
|
|
370
|
+
- web_fetch(url): Fetch a URL and return its content. Use for API docs, examples, etc.`
|
|
371
|
+
};
|
|
372
|
+
var CAP_CONTEXT = {
|
|
373
|
+
name: "context",
|
|
374
|
+
description: "Project file listings, directory structure, and workspace context.",
|
|
375
|
+
prompt: `## Project Context
|
|
376
|
+
|
|
377
|
+
Use list_files to understand the project structure before making changes.
|
|
378
|
+
The context bundle (when available) provides a snapshot of the workspace.`
|
|
379
|
+
};
|
|
380
|
+
var BUILT_IN_CAPABILITIES = [
|
|
381
|
+
CAP_CORE,
|
|
382
|
+
CAP_FILE_OPS,
|
|
383
|
+
CAP_SHELL,
|
|
384
|
+
CAP_TASK_MGMT,
|
|
385
|
+
CAP_FW_GRAMMAR,
|
|
386
|
+
CAP_FW_VALIDATE,
|
|
387
|
+
CAP_FW_GENESIS,
|
|
388
|
+
CAP_FW_CLI,
|
|
389
|
+
CAP_CODE_REVIEW,
|
|
390
|
+
CAP_WEB,
|
|
391
|
+
CAP_CONTEXT
|
|
392
|
+
];
|
|
393
|
+
var capabilityMap = new Map(
|
|
394
|
+
BUILT_IN_CAPABILITIES.map((c) => [c.name, c])
|
|
395
|
+
);
|
|
396
|
+
|
|
397
|
+
// src/bot/behavior-defaults.ts
|
|
398
|
+
var STRATEGY_CAPABILITIES = {
|
|
399
|
+
frugal: ["core", "file-ops", "shell", "context"],
|
|
400
|
+
balanced: ["core", "file-ops", "shell", "task-mgmt", "fw-grammar", "fw-validate", "context"],
|
|
401
|
+
performance: ["core", "file-ops", "shell", "task-mgmt", "fw-grammar", "fw-validate", "fw-genesis", "fw-cli", "code-review", "web", "context"]
|
|
402
|
+
};
|
|
403
|
+
var STRATEGY_BUDGETS = {
|
|
404
|
+
frugal: 0.1,
|
|
405
|
+
balanced: 0.5,
|
|
406
|
+
performance: 1
|
|
407
|
+
};
|
|
408
|
+
|
|
210
409
|
// src/ui/profile-editor.tsx
|
|
211
410
|
var React = require("react");
|
|
212
411
|
var { useState, useEffect, useCallback } = React;
|
|
@@ -227,6 +426,7 @@ var {
|
|
|
227
426
|
Switch,
|
|
228
427
|
ToggleGroup,
|
|
229
428
|
CollapsibleSection,
|
|
429
|
+
ScrollArea,
|
|
230
430
|
toast,
|
|
231
431
|
usePackWorkspace
|
|
232
432
|
} = require("@fw/plugin-ui-kit");
|
|
@@ -286,6 +486,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
286
486
|
const [capDescription, setCapDescription] = useState("");
|
|
287
487
|
const [instructions, setInstructions] = useState("");
|
|
288
488
|
const [requireApproval, setRequireApproval] = useState(false);
|
|
489
|
+
const [knowledgeCaps, setKnowledgeCaps] = useState(() => [...STRATEGY_CAPABILITIES.balanced]);
|
|
490
|
+
const [budget, setBudget] = useState(STRATEGY_BUDGETS.balanced);
|
|
289
491
|
const [behavior, setBehavior] = useState(defaultBehaviorForStrategy("balanced"));
|
|
290
492
|
const [blockedInput, setBlockedInput] = useState("");
|
|
291
493
|
const [allowedInput, setAllowedInput] = useState("");
|
|
@@ -297,6 +499,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
297
499
|
if (p !== "custom") {
|
|
298
500
|
setCostStrategy(p);
|
|
299
501
|
setBehavior(defaultBehaviorForStrategy(p));
|
|
502
|
+
setKnowledgeCaps([...STRATEGY_CAPABILITIES[p]]);
|
|
503
|
+
setBudget(STRATEGY_BUDGETS[p]);
|
|
300
504
|
}
|
|
301
505
|
}, []);
|
|
302
506
|
const updatePhase = useCallback((phase, field, value) => {
|
|
@@ -338,6 +542,22 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
338
542
|
);
|
|
339
543
|
setInstructions(prefs.instructions || "");
|
|
340
544
|
setRequireApproval(!!prefs.requireApproval);
|
|
545
|
+
if (prefs.behavior) {
|
|
546
|
+
const b = prefs.behavior;
|
|
547
|
+
if (Array.isArray(b.capabilities)) {
|
|
548
|
+
setKnowledgeCaps(b.capabilities);
|
|
549
|
+
} else {
|
|
550
|
+
setKnowledgeCaps([...STRATEGY_CAPABILITIES[strat]]);
|
|
551
|
+
}
|
|
552
|
+
if (typeof b.budget === "number") {
|
|
553
|
+
setBudget(b.budget);
|
|
554
|
+
} else {
|
|
555
|
+
setBudget(STRATEGY_BUDGETS[strat]);
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
setKnowledgeCaps([...STRATEGY_CAPABILITIES[strat]]);
|
|
559
|
+
setBudget(STRATEGY_BUDGETS[strat]);
|
|
560
|
+
}
|
|
341
561
|
if (prefs.behavior) {
|
|
342
562
|
const b = prefs.behavior;
|
|
343
563
|
const phases = b.phases;
|
|
@@ -382,6 +602,13 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
382
602
|
const handleRemoveCapability = useCallback((index) => {
|
|
383
603
|
setCapabilities((prev) => prev.filter((_, i) => i !== index));
|
|
384
604
|
}, []);
|
|
605
|
+
const handleToggleKnowledgeCap = useCallback((capName2) => {
|
|
606
|
+
setKnowledgeCaps((prev) => {
|
|
607
|
+
if (capName2 === "core") return prev;
|
|
608
|
+
return prev.includes(capName2) ? prev.filter((n) => n !== capName2) : [...prev, capName2];
|
|
609
|
+
});
|
|
610
|
+
setPreset("custom");
|
|
611
|
+
}, []);
|
|
385
612
|
const handleAddScopePath = useCallback((type) => {
|
|
386
613
|
const input = type === "blocked" ? blockedInput : allowedInput;
|
|
387
614
|
const trimmed = input.trim();
|
|
@@ -422,6 +649,8 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
422
649
|
}
|
|
423
650
|
}
|
|
424
651
|
const behaviorPayload = {
|
|
652
|
+
capabilities: knowledgeCaps,
|
|
653
|
+
budget,
|
|
425
654
|
phases,
|
|
426
655
|
escalation: { maxAttempts: behavior.maxAttempts, onExhausted: behavior.onExhausted },
|
|
427
656
|
scope: behavior.blockedPaths.length > 0 || behavior.allowedPaths.length > 0 ? {
|
|
@@ -467,7 +696,7 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
467
696
|
} catch (err) {
|
|
468
697
|
toast(err instanceof Error ? err.message : `Failed to ${mode} profile`, { type: "error" });
|
|
469
698
|
}
|
|
470
|
-
}, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, instructions, requireApproval, behavior, callTool, onSave]);
|
|
699
|
+
}, [mode, profileId, name, description, botId, icon, color, costStrategy, capabilities, knowledgeCaps, budget, instructions, requireApproval, behavior, callTool, onSave]);
|
|
471
700
|
const handleDelete = useCallback(async () => {
|
|
472
701
|
if (!profileId) return;
|
|
473
702
|
const ok = await ctx.confirm("Are you sure you want to delete this profile?", {
|
|
@@ -571,361 +800,431 @@ function ProfileEditor({ mode, profileId, bots, onSave, onCancel, onDelete }) {
|
|
|
571
800
|
),
|
|
572
801
|
// ── Scrollable form body ──
|
|
573
802
|
React.createElement(
|
|
574
|
-
|
|
803
|
+
ScrollArea,
|
|
575
804
|
{
|
|
576
|
-
|
|
577
|
-
style: { flex: 1, minHeight: 0, overflow: "auto", padding: "12px 16px" }
|
|
805
|
+
style: { flex: 1, minHeight: 0 }
|
|
578
806
|
},
|
|
579
|
-
// ── Name ──
|
|
580
807
|
React.createElement(
|
|
581
|
-
|
|
582
|
-
{
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
value: name,
|
|
588
|
-
onChange: (v) => setName(v),
|
|
589
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
590
|
-
inputBoxStyle: { maxWidth: "none" }
|
|
591
|
-
})
|
|
592
|
-
),
|
|
593
|
-
// ── Description ──
|
|
594
|
-
React.createElement(
|
|
595
|
-
Field,
|
|
596
|
-
{ label: "Description" },
|
|
597
|
-
React.createElement(Input, {
|
|
598
|
-
type: "text",
|
|
599
|
-
size: "small",
|
|
600
|
-
placeholder: "What does this profile do?",
|
|
601
|
-
value: description,
|
|
602
|
-
onChange: (v) => setDescription(v),
|
|
603
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
604
|
-
inputBoxStyle: { maxWidth: "none" }
|
|
605
|
-
})
|
|
606
|
-
),
|
|
607
|
-
// ── Bot ──
|
|
608
|
-
React.createElement(
|
|
609
|
-
Field,
|
|
610
|
-
{ label: "Bot" },
|
|
611
|
-
React.createElement(Input, {
|
|
612
|
-
type: "select",
|
|
613
|
-
size: "small",
|
|
614
|
-
options: bots.map((b) => ({ id: b.id, label: b.name, icon: b.icon || "smartToy" })),
|
|
615
|
-
optionId: botId,
|
|
616
|
-
onChange: (id) => setBotId(id),
|
|
617
|
-
disabled: mode === "edit",
|
|
618
|
-
placeholder: "Select bot",
|
|
619
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 }
|
|
620
|
-
})
|
|
621
|
-
),
|
|
622
|
-
// ── Icon ──
|
|
623
|
-
React.createElement(
|
|
624
|
-
Field,
|
|
625
|
-
{ label: "Icon", align: "start" },
|
|
626
|
-
React.createElement(IconPicker, {
|
|
627
|
-
catalog: ICON_CATALOG,
|
|
628
|
-
value: icon,
|
|
629
|
-
onChange: (v) => setIcon(v),
|
|
630
|
-
accentColor: color,
|
|
631
|
-
variant: "inline"
|
|
632
|
-
})
|
|
633
|
-
),
|
|
634
|
-
// ── Color ──
|
|
635
|
-
React.createElement(
|
|
636
|
-
Field,
|
|
637
|
-
{ label: "Color", align: "start" },
|
|
638
|
-
React.createElement(ColorPicker, {
|
|
639
|
-
colors: BOT_COLORS,
|
|
640
|
-
value: color,
|
|
641
|
-
onChange: (v) => setColor(v),
|
|
642
|
-
variant: "inline"
|
|
643
|
-
})
|
|
644
|
-
),
|
|
645
|
-
// ── Preset (ToggleGroup with Custom option) ──
|
|
646
|
-
React.createElement(
|
|
647
|
-
Field,
|
|
648
|
-
{ label: "Preset", align: "start" },
|
|
649
|
-
React.createElement(ToggleGroup, {
|
|
650
|
-
options: [
|
|
651
|
-
{ label: "Fast", value: "frugal", icon: "speed", selected: preset === "frugal" },
|
|
652
|
-
{ label: "Mid", value: "balanced", icon: "balance", selected: preset === "balanced" },
|
|
653
|
-
{ label: "Smart", value: "performance", icon: "psychology", selected: preset === "performance" },
|
|
654
|
-
{ label: "Custom", value: "custom", icon: "settings", selected: preset === "custom" }
|
|
655
|
-
],
|
|
656
|
-
onSelect: handlePresetChange,
|
|
657
|
-
size: "extraSmall"
|
|
658
|
-
})
|
|
659
|
-
),
|
|
660
|
-
// ── Phase Configuration (always visible, readOnly when preset) ──
|
|
661
|
-
React.createElement(
|
|
662
|
-
Field,
|
|
663
|
-
{ label: "Phases", align: "start" },
|
|
808
|
+
Flex,
|
|
809
|
+
{
|
|
810
|
+
variant: "column-stretch-start-nowrap-10",
|
|
811
|
+
style: { padding: "12px 16px" }
|
|
812
|
+
},
|
|
813
|
+
// ── Name ──
|
|
664
814
|
React.createElement(
|
|
665
|
-
|
|
666
|
-
{
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
815
|
+
Field,
|
|
816
|
+
{ label: "Name" },
|
|
817
|
+
React.createElement(Input, {
|
|
818
|
+
type: "text",
|
|
819
|
+
size: "small",
|
|
820
|
+
placeholder: "Profile name",
|
|
821
|
+
value: name,
|
|
822
|
+
onChange: (v) => setName(v),
|
|
823
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
824
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
674
825
|
})
|
|
675
|
-
)
|
|
676
|
-
|
|
677
|
-
// ── Escalation (always visible, readOnly when preset) ──
|
|
678
|
-
React.createElement(
|
|
679
|
-
Field,
|
|
680
|
-
{ label: "Retries", align: "start" },
|
|
826
|
+
),
|
|
827
|
+
// ── Description ──
|
|
681
828
|
React.createElement(
|
|
682
|
-
|
|
683
|
-
{
|
|
684
|
-
|
|
685
|
-
|
|
829
|
+
Field,
|
|
830
|
+
{ label: "Description" },
|
|
831
|
+
React.createElement(Input, {
|
|
832
|
+
type: "text",
|
|
833
|
+
size: "small",
|
|
834
|
+
placeholder: "What does this profile do?",
|
|
835
|
+
value: description,
|
|
836
|
+
onChange: (v) => setDescription(v),
|
|
837
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
838
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
839
|
+
})
|
|
840
|
+
),
|
|
841
|
+
// ── Bot ──
|
|
842
|
+
React.createElement(
|
|
843
|
+
Field,
|
|
844
|
+
{ label: "Bot" },
|
|
845
|
+
React.createElement(Input, {
|
|
846
|
+
type: "select",
|
|
847
|
+
size: "small",
|
|
848
|
+
options: bots.map((b) => ({ id: b.id, label: b.name, icon: b.icon || "smartToy" })),
|
|
849
|
+
optionId: botId,
|
|
850
|
+
onChange: (id) => setBotId(id),
|
|
851
|
+
disabled: mode === "edit",
|
|
852
|
+
placeholder: "Select bot",
|
|
853
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 }
|
|
854
|
+
})
|
|
855
|
+
),
|
|
856
|
+
// ── Icon ──
|
|
857
|
+
React.createElement(
|
|
858
|
+
Field,
|
|
859
|
+
{ label: "Icon", align: "start" },
|
|
860
|
+
React.createElement(IconPicker, {
|
|
861
|
+
catalog: ICON_CATALOG,
|
|
862
|
+
value: icon,
|
|
863
|
+
onChange: (v) => setIcon(v),
|
|
864
|
+
accentColor: color,
|
|
865
|
+
variant: "inline"
|
|
866
|
+
})
|
|
867
|
+
),
|
|
868
|
+
// ── Color ──
|
|
869
|
+
React.createElement(
|
|
870
|
+
Field,
|
|
871
|
+
{ label: "Color", align: "start" },
|
|
872
|
+
React.createElement(ColorPicker, {
|
|
873
|
+
colors: BOT_COLORS,
|
|
874
|
+
value: color,
|
|
875
|
+
onChange: (v) => setColor(v),
|
|
876
|
+
variant: "inline"
|
|
877
|
+
})
|
|
878
|
+
),
|
|
879
|
+
// ── Preset (ToggleGroup with Custom option) ──
|
|
880
|
+
React.createElement(
|
|
881
|
+
Field,
|
|
882
|
+
{ label: "Preset", align: "start" },
|
|
883
|
+
React.createElement(ToggleGroup, {
|
|
884
|
+
options: [
|
|
885
|
+
{ label: "Fast", value: "frugal", icon: "speed", selected: preset === "frugal" },
|
|
886
|
+
{ label: "Mid", value: "balanced", icon: "balance", selected: preset === "balanced" },
|
|
887
|
+
{ label: "Smart", value: "performance", icon: "psychology", selected: preset === "performance" },
|
|
888
|
+
{ label: "Custom", value: "custom", icon: "settings", selected: preset === "custom" }
|
|
889
|
+
],
|
|
890
|
+
onSelect: handlePresetChange,
|
|
891
|
+
size: "extraSmall"
|
|
892
|
+
})
|
|
893
|
+
),
|
|
894
|
+
// ── Knowledge Capabilities (checkbox grid) ──
|
|
895
|
+
React.createElement(
|
|
896
|
+
Field,
|
|
897
|
+
{ label: "Knowledge", align: "start" },
|
|
686
898
|
React.createElement(
|
|
687
|
-
|
|
688
|
-
{
|
|
899
|
+
Flex,
|
|
900
|
+
{ variant: "column-stretch-start-nowrap-2" },
|
|
901
|
+
...BUILT_IN_CAPABILITIES.map(
|
|
902
|
+
(cap) => React.createElement(
|
|
903
|
+
Flex,
|
|
904
|
+
{
|
|
905
|
+
key: cap.name,
|
|
906
|
+
variant: "row-center-start-nowrap-8",
|
|
907
|
+
style: { minHeight: 28, padding: "1px 0" }
|
|
908
|
+
},
|
|
909
|
+
React.createElement(Checkbox, {
|
|
910
|
+
checked: knowledgeCaps.includes(cap.name),
|
|
911
|
+
onChange: () => handleToggleKnowledgeCap(cap.name),
|
|
912
|
+
disabled: cap.name === "core" || !isCustom,
|
|
913
|
+
size: "sm"
|
|
914
|
+
}),
|
|
915
|
+
React.createElement(Typography, {
|
|
916
|
+
variant: "smallCaption-thick",
|
|
917
|
+
color: knowledgeCaps.includes(cap.name) ? "color-text-high" : "color-text-subtle",
|
|
918
|
+
style: { minWidth: 80 }
|
|
919
|
+
}, cap.name),
|
|
920
|
+
React.createElement(Typography, {
|
|
921
|
+
variant: "smallCaption-regular",
|
|
922
|
+
color: "color-text-subtle",
|
|
923
|
+
style: { flex: 1 }
|
|
924
|
+
}, cap.description)
|
|
925
|
+
)
|
|
926
|
+
)
|
|
927
|
+
)
|
|
928
|
+
),
|
|
929
|
+
// ── Budget ──
|
|
930
|
+
React.createElement(
|
|
931
|
+
Field,
|
|
932
|
+
{ label: "Budget", align: "center" },
|
|
933
|
+
React.createElement(
|
|
934
|
+
Flex,
|
|
935
|
+
{ variant: "row-center-start-nowrap-6" },
|
|
936
|
+
React.createElement(Typography, {
|
|
937
|
+
variant: "smallCaption-regular",
|
|
938
|
+
color: "color-text-subtle"
|
|
939
|
+
}, "$"),
|
|
689
940
|
React.createElement(Input, {
|
|
690
941
|
type: "number",
|
|
691
942
|
size: "small",
|
|
692
|
-
value: String(
|
|
943
|
+
value: String(budget),
|
|
693
944
|
readOnly: !isCustom,
|
|
694
|
-
onChange: isCustom ? (v) =>
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
945
|
+
onChange: isCustom ? (v) => {
|
|
946
|
+
const n = parseFloat(v);
|
|
947
|
+
if (!isNaN(n) && n >= 0) setBudget(n);
|
|
948
|
+
} : void 0,
|
|
949
|
+
defaultBoxStyle: { width: 80 }
|
|
950
|
+
}),
|
|
951
|
+
React.createElement(Typography, {
|
|
952
|
+
variant: "smallCaption-regular",
|
|
953
|
+
color: "color-text-subtle"
|
|
954
|
+
}, "/ task")
|
|
955
|
+
)
|
|
956
|
+
),
|
|
957
|
+
// ── Phase Configuration (always visible, readOnly when preset) ──
|
|
958
|
+
React.createElement(
|
|
959
|
+
Field,
|
|
960
|
+
{ label: "Phases", align: "start" },
|
|
698
961
|
React.createElement(
|
|
699
|
-
|
|
700
|
-
{
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
],
|
|
707
|
-
|
|
708
|
-
readOnly: !isCustom,
|
|
709
|
-
size: "extraSmall"
|
|
962
|
+
Flex,
|
|
963
|
+
{
|
|
964
|
+
variant: "column-stretch-start-nowrap-6"
|
|
965
|
+
},
|
|
966
|
+
...Object.entries(behavior).filter(([k]) => !["maxAttempts", "onExhausted", "blockedPaths", "allowedPaths"].includes(k)).map(([phaseName, phase]) => {
|
|
967
|
+
const phaseObj = phase;
|
|
968
|
+
const isLlm = phaseObj.tier !== void 0;
|
|
969
|
+
const label = phaseName.replace(/([A-Z])/g, " $1").replace(/^./, (s) => s.toUpperCase()).trim();
|
|
970
|
+
return phaseRow(phaseName, label, isLlm);
|
|
710
971
|
})
|
|
711
972
|
)
|
|
712
|
-
)
|
|
713
|
-
|
|
714
|
-
// ── Scope Constraints (collapsible) ──
|
|
715
|
-
// ── Custom: Scope Constraints (inline) ──
|
|
716
|
-
isCustom && React.createElement(
|
|
717
|
-
Field,
|
|
718
|
-
{ label: "Scope", align: "start" },
|
|
973
|
+
),
|
|
974
|
+
// ── Escalation (always visible, readOnly when preset) ──
|
|
719
975
|
React.createElement(
|
|
720
|
-
|
|
721
|
-
{
|
|
722
|
-
variant: "column-stretch-start-nowrap-8"
|
|
723
|
-
},
|
|
724
|
-
// Blocked paths
|
|
976
|
+
Field,
|
|
977
|
+
{ label: "Retries", align: "start" },
|
|
725
978
|
React.createElement(
|
|
726
|
-
|
|
727
|
-
{
|
|
979
|
+
Flex,
|
|
980
|
+
{
|
|
981
|
+
variant: "column-stretch-start-nowrap-8"
|
|
982
|
+
},
|
|
728
983
|
React.createElement(
|
|
729
|
-
|
|
730
|
-
{
|
|
984
|
+
Field,
|
|
985
|
+
{ label: "Max attempts", labelWidth: 100, align: "center" },
|
|
986
|
+
React.createElement(Input, {
|
|
987
|
+
type: "number",
|
|
988
|
+
size: "small",
|
|
989
|
+
value: String(behavior.maxAttempts),
|
|
990
|
+
readOnly: !isCustom,
|
|
991
|
+
onChange: isCustom ? (v) => updateEscalation("maxAttempts", Math.max(1, parseInt(v) || 1)) : void 0,
|
|
992
|
+
defaultBoxStyle: { width: 60 }
|
|
993
|
+
})
|
|
994
|
+
),
|
|
995
|
+
React.createElement(
|
|
996
|
+
Field,
|
|
997
|
+
{ label: "On exhausted", labelWidth: 100, align: "start" },
|
|
998
|
+
React.createElement(ToggleGroup, {
|
|
999
|
+
options: [
|
|
1000
|
+
{ label: "Block", value: "block", icon: "block", selected: behavior.onExhausted === "block" },
|
|
1001
|
+
{ label: "Reassign", value: "reassign", icon: "sync", selected: behavior.onExhausted === "reassign" },
|
|
1002
|
+
{ label: "Notify", value: "notify", icon: "notifications", selected: behavior.onExhausted === "notify" }
|
|
1003
|
+
],
|
|
1004
|
+
onSelect: (v) => updateEscalation("onExhausted", v),
|
|
1005
|
+
readOnly: !isCustom,
|
|
1006
|
+
size: "extraSmall"
|
|
1007
|
+
})
|
|
1008
|
+
)
|
|
1009
|
+
)
|
|
1010
|
+
),
|
|
1011
|
+
// ── Scope Constraints (collapsible) ──
|
|
1012
|
+
// ── Custom: Scope Constraints (inline) ──
|
|
1013
|
+
isCustom && React.createElement(
|
|
1014
|
+
Field,
|
|
1015
|
+
{ label: "Scope", align: "start" },
|
|
1016
|
+
React.createElement(
|
|
1017
|
+
Flex,
|
|
1018
|
+
{
|
|
1019
|
+
variant: "column-stretch-start-nowrap-8"
|
|
1020
|
+
},
|
|
1021
|
+
// Blocked paths
|
|
1022
|
+
React.createElement(
|
|
1023
|
+
Field,
|
|
1024
|
+
{ label: "Blocked", labelWidth: 70, align: "start" },
|
|
731
1025
|
React.createElement(
|
|
732
1026
|
Flex,
|
|
733
|
-
{ variant: "
|
|
734
|
-
React.createElement(
|
|
735
|
-
type: "text",
|
|
736
|
-
size: "small",
|
|
737
|
-
placeholder: "e.g. src/",
|
|
738
|
-
value: blockedInput,
|
|
739
|
-
onChange: (v) => setBlockedInput(v),
|
|
740
|
-
onEnter: () => handleAddScopePath("blocked"),
|
|
741
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
742
|
-
inputBoxStyle: { maxWidth: "none" }
|
|
743
|
-
}),
|
|
744
|
-
React.createElement(IconButton, {
|
|
745
|
-
icon: "add",
|
|
746
|
-
size: "sm",
|
|
747
|
-
variant: "outlined",
|
|
748
|
-
color: "primary",
|
|
749
|
-
onClick: () => handleAddScopePath("blocked"),
|
|
750
|
-
disabled: !blockedInput.trim()
|
|
751
|
-
})
|
|
752
|
-
),
|
|
753
|
-
...behavior.blockedPaths.map(
|
|
754
|
-
(p, i) => React.createElement(
|
|
1027
|
+
{ variant: "column-stretch-start-nowrap-4" },
|
|
1028
|
+
React.createElement(
|
|
755
1029
|
Flex,
|
|
756
|
-
{
|
|
757
|
-
React.createElement(
|
|
758
|
-
|
|
1030
|
+
{ variant: "row-center-start-nowrap-4" },
|
|
1031
|
+
React.createElement(Input, {
|
|
1032
|
+
type: "text",
|
|
1033
|
+
size: "small",
|
|
1034
|
+
placeholder: "e.g. src/",
|
|
1035
|
+
value: blockedInput,
|
|
1036
|
+
onChange: (v) => setBlockedInput(v),
|
|
1037
|
+
onEnter: () => handleAddScopePath("blocked"),
|
|
1038
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
1039
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
1040
|
+
}),
|
|
759
1041
|
React.createElement(IconButton, {
|
|
760
|
-
icon: "
|
|
761
|
-
size: "
|
|
762
|
-
variant: "
|
|
763
|
-
color: "
|
|
764
|
-
onClick: () =>
|
|
1042
|
+
icon: "add",
|
|
1043
|
+
size: "sm",
|
|
1044
|
+
variant: "outlined",
|
|
1045
|
+
color: "primary",
|
|
1046
|
+
onClick: () => handleAddScopePath("blocked"),
|
|
1047
|
+
disabled: !blockedInput.trim()
|
|
765
1048
|
})
|
|
1049
|
+
),
|
|
1050
|
+
...behavior.blockedPaths.map(
|
|
1051
|
+
(p, i) => React.createElement(
|
|
1052
|
+
Flex,
|
|
1053
|
+
{ key: `b-${p}-${i}`, variant: "row-center-start-nowrap-6" },
|
|
1054
|
+
React.createElement(Icon, { name: "block", size: 12, color: "color-status-negative" }),
|
|
1055
|
+
React.createElement(Typography, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
|
|
1056
|
+
React.createElement(IconButton, {
|
|
1057
|
+
icon: "close",
|
|
1058
|
+
size: "xs",
|
|
1059
|
+
variant: "clear",
|
|
1060
|
+
color: "danger",
|
|
1061
|
+
onClick: () => handleRemoveScopePath("blocked", i)
|
|
1062
|
+
})
|
|
1063
|
+
)
|
|
766
1064
|
)
|
|
767
1065
|
)
|
|
768
|
-
)
|
|
769
|
-
|
|
770
|
-
// Allowed paths
|
|
771
|
-
React.createElement(
|
|
772
|
-
Field,
|
|
773
|
-
{ label: "Allowed", labelWidth: 70, align: "start" },
|
|
1066
|
+
),
|
|
1067
|
+
// Allowed paths
|
|
774
1068
|
React.createElement(
|
|
775
|
-
|
|
776
|
-
{
|
|
1069
|
+
Field,
|
|
1070
|
+
{ label: "Allowed", labelWidth: 70, align: "start" },
|
|
777
1071
|
React.createElement(
|
|
778
1072
|
Flex,
|
|
779
|
-
{ variant: "
|
|
780
|
-
React.createElement(
|
|
781
|
-
type: "text",
|
|
782
|
-
size: "small",
|
|
783
|
-
placeholder: "e.g. config/",
|
|
784
|
-
value: allowedInput,
|
|
785
|
-
onChange: (v) => setAllowedInput(v),
|
|
786
|
-
onEnter: () => handleAddScopePath("allowed"),
|
|
787
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
788
|
-
inputBoxStyle: { maxWidth: "none" }
|
|
789
|
-
}),
|
|
790
|
-
React.createElement(IconButton, {
|
|
791
|
-
icon: "add",
|
|
792
|
-
size: "sm",
|
|
793
|
-
variant: "outlined",
|
|
794
|
-
color: "primary",
|
|
795
|
-
onClick: () => handleAddScopePath("allowed"),
|
|
796
|
-
disabled: !allowedInput.trim()
|
|
797
|
-
})
|
|
798
|
-
),
|
|
799
|
-
...behavior.allowedPaths.map(
|
|
800
|
-
(p, i) => React.createElement(
|
|
1073
|
+
{ variant: "column-stretch-start-nowrap-4" },
|
|
1074
|
+
React.createElement(
|
|
801
1075
|
Flex,
|
|
802
|
-
{
|
|
803
|
-
React.createElement(
|
|
804
|
-
|
|
1076
|
+
{ variant: "row-center-start-nowrap-4" },
|
|
1077
|
+
React.createElement(Input, {
|
|
1078
|
+
type: "text",
|
|
1079
|
+
size: "small",
|
|
1080
|
+
placeholder: "e.g. config/",
|
|
1081
|
+
value: allowedInput,
|
|
1082
|
+
onChange: (v) => setAllowedInput(v),
|
|
1083
|
+
onEnter: () => handleAddScopePath("allowed"),
|
|
1084
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
1085
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
1086
|
+
}),
|
|
805
1087
|
React.createElement(IconButton, {
|
|
806
|
-
icon: "
|
|
807
|
-
size: "
|
|
808
|
-
variant: "
|
|
809
|
-
color: "
|
|
810
|
-
onClick: () =>
|
|
1088
|
+
icon: "add",
|
|
1089
|
+
size: "sm",
|
|
1090
|
+
variant: "outlined",
|
|
1091
|
+
color: "primary",
|
|
1092
|
+
onClick: () => handleAddScopePath("allowed"),
|
|
1093
|
+
disabled: !allowedInput.trim()
|
|
811
1094
|
})
|
|
1095
|
+
),
|
|
1096
|
+
...behavior.allowedPaths.map(
|
|
1097
|
+
(p, i) => React.createElement(
|
|
1098
|
+
Flex,
|
|
1099
|
+
{ key: `a-${p}-${i}`, variant: "row-center-start-nowrap-6" },
|
|
1100
|
+
React.createElement(Icon, { name: "checkCircle", size: 12, color: "color-status-positive" }),
|
|
1101
|
+
React.createElement(Typography, { variant: "smallCaption-regular", color: "color-text-high", style: { flex: 1 } }, p),
|
|
1102
|
+
React.createElement(IconButton, {
|
|
1103
|
+
icon: "close",
|
|
1104
|
+
size: "xs",
|
|
1105
|
+
variant: "clear",
|
|
1106
|
+
color: "danger",
|
|
1107
|
+
onClick: () => handleRemoveScopePath("allowed", i)
|
|
1108
|
+
})
|
|
1109
|
+
)
|
|
812
1110
|
)
|
|
813
1111
|
)
|
|
814
1112
|
)
|
|
815
1113
|
)
|
|
816
|
-
)
|
|
817
|
-
|
|
818
|
-
// ── Capabilities ──
|
|
819
|
-
React.createElement(
|
|
820
|
-
Field,
|
|
821
|
-
{ label: "Capabilities", align: "start" },
|
|
1114
|
+
),
|
|
1115
|
+
// ── Capabilities ──
|
|
822
1116
|
React.createElement(
|
|
823
|
-
|
|
824
|
-
{
|
|
825
|
-
// Add row
|
|
1117
|
+
Field,
|
|
1118
|
+
{ label: "Capabilities", align: "start" },
|
|
826
1119
|
React.createElement(
|
|
827
1120
|
Flex,
|
|
828
|
-
{ variant: "
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
1121
|
+
{ variant: "column-stretch-start-nowrap-6" },
|
|
1122
|
+
// Add row
|
|
1123
|
+
React.createElement(
|
|
1124
|
+
Flex,
|
|
1125
|
+
{ variant: "row-center-start-nowrap-4", style: { minWidth: 0 } },
|
|
1126
|
+
React.createElement(Input, {
|
|
1127
|
+
type: "text",
|
|
1128
|
+
size: "small",
|
|
1129
|
+
placeholder: "Name",
|
|
1130
|
+
value: capName,
|
|
1131
|
+
onChange: (v) => setCapName(v),
|
|
1132
|
+
defaultBoxStyle: { flex: "0 1 120px", minWidth: 0 },
|
|
1133
|
+
inputBoxStyle: { maxWidth: "none", minWidth: 0 }
|
|
1134
|
+
}),
|
|
1135
|
+
React.createElement(Input, {
|
|
1136
|
+
type: "text",
|
|
1137
|
+
size: "small",
|
|
1138
|
+
placeholder: "Description",
|
|
1139
|
+
value: capDescription,
|
|
1140
|
+
onChange: (v) => setCapDescription(v),
|
|
1141
|
+
onEnter: handleAddCapability,
|
|
1142
|
+
defaultBoxStyle: { flex: "1 1 0", minWidth: 0 },
|
|
1143
|
+
inputBoxStyle: { maxWidth: "none", minWidth: 0 }
|
|
1144
|
+
}),
|
|
1145
|
+
React.createElement(IconButton, {
|
|
1146
|
+
icon: "add",
|
|
1147
|
+
size: "sm",
|
|
1148
|
+
variant: "outlined",
|
|
1149
|
+
color: "primary",
|
|
1150
|
+
onClick: handleAddCapability,
|
|
1151
|
+
disabled: !capName.trim() || !capDescription.trim(),
|
|
1152
|
+
style: { flexShrink: 0 }
|
|
1153
|
+
})
|
|
1154
|
+
),
|
|
1155
|
+
// Table
|
|
1156
|
+
capabilities.length > 0 && React.createElement(Table, {
|
|
1157
|
+
size: "compact",
|
|
1158
|
+
showHeader: false,
|
|
1159
|
+
data: capabilities,
|
|
1160
|
+
getRowKey: (_row, i) => `cap-${i}`,
|
|
1161
|
+
columns: [
|
|
1162
|
+
{
|
|
1163
|
+
key: "name",
|
|
1164
|
+
header: "Name",
|
|
1165
|
+
width: "30%",
|
|
1166
|
+
render: (val) => React.createElement("div", { style: { lineHeight: "28px" } }, String(val))
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
key: "description",
|
|
1170
|
+
header: "Description",
|
|
1171
|
+
render: (val) => React.createElement("div", { style: { lineHeight: "28px" } }, String(val))
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
key: "actions",
|
|
1175
|
+
header: "",
|
|
1176
|
+
width: "32px",
|
|
1177
|
+
align: "right",
|
|
1178
|
+
render: (_val, _row, idx) => React.createElement(IconButton, {
|
|
1179
|
+
icon: "close",
|
|
1180
|
+
size: "xs",
|
|
1181
|
+
variant: "clear",
|
|
1182
|
+
color: "danger",
|
|
1183
|
+
onClick: () => handleRemoveCapability(idx)
|
|
1184
|
+
})
|
|
1185
|
+
}
|
|
1186
|
+
]
|
|
855
1187
|
})
|
|
856
|
-
)
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
key: "description",
|
|
872
|
-
header: "Description",
|
|
873
|
-
render: (val) => React.createElement("div", { style: { lineHeight: "28px" } }, String(val))
|
|
874
|
-
},
|
|
875
|
-
{
|
|
876
|
-
key: "actions",
|
|
877
|
-
header: "",
|
|
878
|
-
width: "32px",
|
|
879
|
-
align: "right",
|
|
880
|
-
render: (_val, _row, idx) => React.createElement(IconButton, {
|
|
881
|
-
icon: "close",
|
|
882
|
-
size: "xs",
|
|
883
|
-
variant: "clear",
|
|
884
|
-
color: "danger",
|
|
885
|
-
onClick: () => handleRemoveCapability(idx)
|
|
886
|
-
})
|
|
887
|
-
}
|
|
888
|
-
]
|
|
1188
|
+
)
|
|
1189
|
+
),
|
|
1190
|
+
// ── Instructions (multiline) ──
|
|
1191
|
+
React.createElement(
|
|
1192
|
+
Field,
|
|
1193
|
+
{ label: "Instructions", align: "start" },
|
|
1194
|
+
React.createElement(Input, {
|
|
1195
|
+
type: "text",
|
|
1196
|
+
size: "small",
|
|
1197
|
+
multiline: true,
|
|
1198
|
+
placeholder: "Behavioral protocol for this profile (optional)",
|
|
1199
|
+
value: instructions,
|
|
1200
|
+
onChange: (v) => setInstructions(v),
|
|
1201
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
1202
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
889
1203
|
})
|
|
1204
|
+
),
|
|
1205
|
+
// ── Require Approval ──
|
|
1206
|
+
React.createElement(
|
|
1207
|
+
Field,
|
|
1208
|
+
{ label: "" },
|
|
1209
|
+
React.createElement(Checkbox, {
|
|
1210
|
+
checked: requireApproval,
|
|
1211
|
+
onChange: (v) => setRequireApproval(v),
|
|
1212
|
+
label: "Require approval",
|
|
1213
|
+
size: "sm"
|
|
1214
|
+
})
|
|
1215
|
+
),
|
|
1216
|
+
// ── Save button ──
|
|
1217
|
+
React.createElement(
|
|
1218
|
+
Flex,
|
|
1219
|
+
{ variant: "row-center-end-nowrap-8" },
|
|
1220
|
+
React.createElement(Button, {
|
|
1221
|
+
size: "xs",
|
|
1222
|
+
variant: "fill",
|
|
1223
|
+
color: "primary",
|
|
1224
|
+
onClick: handleSave,
|
|
1225
|
+
disabled: !name.trim() || !botId || capabilities.length === 0
|
|
1226
|
+
}, mode === "create" ? "Create" : "Save")
|
|
890
1227
|
)
|
|
891
|
-
),
|
|
892
|
-
// ── Instructions (multiline) ──
|
|
893
|
-
React.createElement(
|
|
894
|
-
Field,
|
|
895
|
-
{ label: "Instructions", align: "start" },
|
|
896
|
-
React.createElement(Input, {
|
|
897
|
-
type: "text",
|
|
898
|
-
size: "small",
|
|
899
|
-
multiline: true,
|
|
900
|
-
placeholder: "Behavioral protocol for this profile (optional)",
|
|
901
|
-
value: instructions,
|
|
902
|
-
onChange: (v) => setInstructions(v),
|
|
903
|
-
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
904
|
-
inputBoxStyle: { maxWidth: "none" }
|
|
905
|
-
})
|
|
906
|
-
),
|
|
907
|
-
// ── Require Approval ──
|
|
908
|
-
React.createElement(
|
|
909
|
-
Field,
|
|
910
|
-
{ label: "" },
|
|
911
|
-
React.createElement(Checkbox, {
|
|
912
|
-
checked: requireApproval,
|
|
913
|
-
onChange: (v) => setRequireApproval(v),
|
|
914
|
-
label: "Require approval",
|
|
915
|
-
size: "sm"
|
|
916
|
-
})
|
|
917
|
-
),
|
|
918
|
-
// ── Save button ──
|
|
919
|
-
React.createElement(
|
|
920
|
-
Flex,
|
|
921
|
-
{ variant: "row-center-end-nowrap-8" },
|
|
922
|
-
React.createElement(Button, {
|
|
923
|
-
size: "xs",
|
|
924
|
-
variant: "fill",
|
|
925
|
-
color: "primary",
|
|
926
|
-
onClick: handleSave,
|
|
927
|
-
disabled: !name.trim() || !botId || capabilities.length === 0
|
|
928
|
-
}, mode === "create" ? "Create" : "Save")
|
|
929
1228
|
)
|
|
930
1229
|
)
|
|
931
1230
|
);
|