@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
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/ui/capability-editor.tsx
|
|
21
|
+
var capability_editor_exports = {};
|
|
22
|
+
__export(capability_editor_exports, {
|
|
23
|
+
CapabilityEditor: () => CapabilityEditor,
|
|
24
|
+
default: () => capability_editor_default
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(capability_editor_exports);
|
|
27
|
+
|
|
28
|
+
// src/bot/operations.ts
|
|
29
|
+
var OP_WRITE_FILE = "write_file";
|
|
30
|
+
var OP_READ_FILE = "read_file";
|
|
31
|
+
var OP_PATCH_FILE = "patch_file";
|
|
32
|
+
var OP_LIST_FILES = "list_files";
|
|
33
|
+
var OP_RUN_SHELL = "run_shell";
|
|
34
|
+
var OP_VALIDATE = "validate";
|
|
35
|
+
var OP_TSC_CHECK = "tsc_check";
|
|
36
|
+
var OP_RUN_TESTS = "run_tests";
|
|
37
|
+
var OP_TASK_CREATE = "task_create";
|
|
38
|
+
var OP_RESPOND = "respond";
|
|
39
|
+
var PLAN_OPERATIONS = [
|
|
40
|
+
OP_WRITE_FILE,
|
|
41
|
+
OP_READ_FILE,
|
|
42
|
+
OP_PATCH_FILE,
|
|
43
|
+
OP_LIST_FILES,
|
|
44
|
+
OP_RUN_SHELL,
|
|
45
|
+
OP_VALIDATE,
|
|
46
|
+
OP_TASK_CREATE,
|
|
47
|
+
OP_RESPOND
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
// src/bot/capability-registry.ts
|
|
51
|
+
var CAP_CORE = {
|
|
52
|
+
name: "core",
|
|
53
|
+
description: "Bot identity, structured plan output format, and safety rules. Always loaded.",
|
|
54
|
+
prompt: `You are Weaver, an expert AI companion for Flow Weaver workflows.
|
|
55
|
+
|
|
56
|
+
## Plan Format
|
|
57
|
+
Your plans MUST be structured JSON with concrete steps.
|
|
58
|
+
Each step has: operation (tool name), description (what it does), args (complete arguments).
|
|
59
|
+
Do NOT describe what you would do \u2014 actually do it by calling tools.
|
|
60
|
+
|
|
61
|
+
## Safety Rules
|
|
62
|
+
- Writes that shrink a file by >50% or write empty content are automatically BLOCKED.
|
|
63
|
+
- Blocked shell commands: rm -rf, git push, npm publish, sudo, curl|sh.
|
|
64
|
+
- Always validate BEFORE and AFTER patching.
|
|
65
|
+
- Always read a file before patching it (you need exact strings for find/replace).
|
|
66
|
+
- Use patch_file for modifications, write_file only for new files.
|
|
67
|
+
- Be concise \u2014 let tool results speak.`
|
|
68
|
+
};
|
|
69
|
+
var CAP_FILE_OPS = {
|
|
70
|
+
name: "file-ops",
|
|
71
|
+
description: "File read/write/patch operations and best practices for file manipulation.",
|
|
72
|
+
tools: [OP_READ_FILE, OP_WRITE_FILE, OP_PATCH_FILE, OP_LIST_FILES],
|
|
73
|
+
prompt: `## File Operations
|
|
74
|
+
- read_file: Read a file and return its content. args: { file }
|
|
75
|
+
- write_file: Write a file. args: { file, content }. Content must be the COMPLETE file.
|
|
76
|
+
- patch_file: Surgical find-and-replace edits. args: { file, patches: [{ find: "old text", replace: "new text" }] }. PREFERRED for modifying existing files.
|
|
77
|
+
- list_files: List files in a directory. args: { directory, pattern? } (pattern is regex)
|
|
78
|
+
|
|
79
|
+
## Best Practices
|
|
80
|
+
PREFER patch_file over write_file for modifying existing files (surgical edits, no truncation risk).
|
|
81
|
+
Use read_file to understand a file before modifying it.
|
|
82
|
+
Use list_files to discover project structure.
|
|
83
|
+
Writes that shrink a file by >50% or write empty content are automatically BLOCKED.`
|
|
84
|
+
};
|
|
85
|
+
var CAP_SHELL = {
|
|
86
|
+
name: "shell",
|
|
87
|
+
description: "Shell command execution for running tests, builds, and inspecting output.",
|
|
88
|
+
tools: [OP_RUN_SHELL, OP_VALIDATE, OP_TSC_CHECK, OP_RUN_TESTS],
|
|
89
|
+
prompt: `## Shell Commands
|
|
90
|
+
- run_shell: Execute a shell command and return output. args: { command }
|
|
91
|
+
Use for: npx vitest, git status, grep, find, etc.
|
|
92
|
+
Examples: { "command": "npx vitest run --reporter verbose" }, { "command": "npx flow-weaver validate src/workflow.ts --json" }
|
|
93
|
+
Blocked: rm -rf, git push, npm publish, sudo, curl|sh (safety policy).
|
|
94
|
+
Use run_shell for running tests (npx vitest), validation (flow-weaver validate), and inspecting output.`
|
|
95
|
+
};
|
|
96
|
+
var CAP_TASK_MGMT = {
|
|
97
|
+
name: "task-mgmt",
|
|
98
|
+
description: "Create and manage swarm subtasks for parallel execution.",
|
|
99
|
+
tools: [OP_TASK_CREATE],
|
|
100
|
+
prompt: `## Task Management
|
|
101
|
+
- task_create: Create swarm subtasks for parallel execution. args: { title, description, complexity, subtasks[] }
|
|
102
|
+
- task_list, task_get, task_update: Query and update existing tasks
|
|
103
|
+
|
|
104
|
+
Use task_create to decompose complex work into smaller, independent subtasks that other bots can execute in parallel.`
|
|
105
|
+
};
|
|
106
|
+
var CAP_FW_GRAMMAR = {
|
|
107
|
+
name: "fw-grammar",
|
|
108
|
+
description: "Flow Weaver annotation syntax, node types, workflows, patterns, and data flow.",
|
|
109
|
+
prompt: `## Core Mental Model
|
|
110
|
+
|
|
111
|
+
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.
|
|
112
|
+
|
|
113
|
+
Three block types:
|
|
114
|
+
- @flowWeaver nodeType: A reusable function (node) with typed inputs/outputs
|
|
115
|
+
- @flowWeaver workflow: A DAG orchestration that wires node instances together
|
|
116
|
+
- @flowWeaver pattern: A reusable fragment with boundary ports (IN/OUT instead of Start/Exit)
|
|
117
|
+
|
|
118
|
+
## Node Execution Model
|
|
119
|
+
|
|
120
|
+
Expression nodes (@expression):
|
|
121
|
+
- No execute/onSuccess/onFailure params. Just inputs and return value.
|
|
122
|
+
- throw = failure path, return = success path
|
|
123
|
+
- Synchronous. Use execSync for shell commands.
|
|
124
|
+
- Preferred for deterministic operations.
|
|
125
|
+
|
|
126
|
+
Standard nodes:
|
|
127
|
+
- execute: boolean param gates execution
|
|
128
|
+
- Return { onSuccess: boolean, onFailure: boolean, ...outputs }
|
|
129
|
+
- Can be async for I/O operations
|
|
130
|
+
|
|
131
|
+
Async agent nodes:
|
|
132
|
+
- Use (globalThis as any).__fw_agent_channel__ to pause workflow
|
|
133
|
+
- Call channel.request({ agentId, context, prompt }) which returns a Promise
|
|
134
|
+
- Workflow suspends until agent responds
|
|
135
|
+
- NOT @expression (must be async)
|
|
136
|
+
|
|
137
|
+
Pass-through pattern:
|
|
138
|
+
- FW auto-connects ports by matching names on adjacent nodes
|
|
139
|
+
- To forward data through intermediate nodes, declare it as both @input and @output with the same name
|
|
140
|
+
- For non-adjacent wiring, use @connect sourceNode.port -> targetNode.port
|
|
141
|
+
|
|
142
|
+
Data flow:
|
|
143
|
+
- @path A -> B -> C: Linear execution path (sugar for multiple @connect)
|
|
144
|
+
- @autoConnect: Auto-wire all nodes in declaration order
|
|
145
|
+
- @connect: Explicit port-to-port wiring
|
|
146
|
+
- Merge strategies for fan-in: FIRST, LAST, COLLECT, MERGE, CONCAT`
|
|
147
|
+
};
|
|
148
|
+
var CAP_FW_VALIDATE = {
|
|
149
|
+
name: "fw-validate",
|
|
150
|
+
description: "Flow Weaver validation error codes and common fix patterns.",
|
|
151
|
+
prompt: `## Validation Errors
|
|
152
|
+
|
|
153
|
+
When you encounter validation errors, suggest the specific fix. Common resolutions:
|
|
154
|
+
- UNKNOWN_NODE_TYPE: Ensure the referenced function has @flowWeaver nodeType annotation
|
|
155
|
+
- MISSING_REQUIRED_INPUT: Add a @connect from a source port or make the input optional with [brackets]
|
|
156
|
+
- UNKNOWN_SOURCE_PORT / UNKNOWN_TARGET_PORT: Check port name spelling in @connect
|
|
157
|
+
- CYCLE_DETECTED: Break the cycle; use scoped iteration (@scope, @map) instead of circular dependencies`
|
|
158
|
+
};
|
|
159
|
+
var CAP_FW_GENESIS = {
|
|
160
|
+
name: "fw-genesis",
|
|
161
|
+
description: "Flow Weaver genesis protocol for self-evolving workflows.",
|
|
162
|
+
prompt: `## Genesis Protocol
|
|
163
|
+
|
|
164
|
+
Genesis is a 17-step self-evolving workflow engine:
|
|
165
|
+
1. Load config 2. Observe project 3. Load task workflow 4. Diff fingerprint
|
|
166
|
+
5. Check stabilize mode 6. Wait for agent 7. Propose evolution 8. Validate proposal
|
|
167
|
+
9. Snapshot for rollback 10. Apply changes 11. Compile and validate
|
|
168
|
+
12. Diff workflow 13. Check approval threshold 14. Wait for approval
|
|
169
|
+
15. Commit or rollback 16. Update history 17. Report summary
|
|
170
|
+
|
|
171
|
+
When stabilize mode is active, only fix-up operations are allowed.`
|
|
172
|
+
};
|
|
173
|
+
var CAP_FW_CLI = {
|
|
174
|
+
name: "fw-cli",
|
|
175
|
+
description: "Flow Weaver CLI command reference for compile, validate, diagram, and other operations.",
|
|
176
|
+
prompt: `## CLI Commands
|
|
177
|
+
|
|
178
|
+
Note: compile, validate, modify, diff, diagram, and describe operations are available as direct plan steps (no CLI needed).`
|
|
179
|
+
};
|
|
180
|
+
var CAP_CODE_REVIEW = {
|
|
181
|
+
name: "code-review",
|
|
182
|
+
description: "Code review guidelines, quality checklist, and security review patterns.",
|
|
183
|
+
prompt: `## Code Review
|
|
184
|
+
|
|
185
|
+
When reviewing code, check for:
|
|
186
|
+
1. Correctness: Does the code do what the task asked?
|
|
187
|
+
2. Security: No hardcoded secrets, no injection vulnerabilities, no exposed APIs
|
|
188
|
+
3. Style: Consistent with project conventions, proper naming, no dead code
|
|
189
|
+
4. Testing: Are there tests? Do they cover edge cases?
|
|
190
|
+
5. Performance: No unnecessary loops, no blocking calls in async code
|
|
191
|
+
|
|
192
|
+
Report concerns with specific file:line references and suggested fixes.`
|
|
193
|
+
};
|
|
194
|
+
var CAP_WEB = {
|
|
195
|
+
name: "web",
|
|
196
|
+
description: "Web fetch capability for fetching URLs and external resources.",
|
|
197
|
+
tools: ["web_fetch"],
|
|
198
|
+
prompt: `## Web
|
|
199
|
+
- web_fetch(url): Fetch a URL and return its content. Use for API docs, examples, etc.`
|
|
200
|
+
};
|
|
201
|
+
var CAP_CONTEXT = {
|
|
202
|
+
name: "context",
|
|
203
|
+
description: "Project file listings, directory structure, and workspace context.",
|
|
204
|
+
prompt: `## Project Context
|
|
205
|
+
|
|
206
|
+
Use list_files to understand the project structure before making changes.
|
|
207
|
+
The context bundle (when available) provides a snapshot of the workspace.`
|
|
208
|
+
};
|
|
209
|
+
var BUILT_IN_CAPABILITIES = [
|
|
210
|
+
CAP_CORE,
|
|
211
|
+
CAP_FILE_OPS,
|
|
212
|
+
CAP_SHELL,
|
|
213
|
+
CAP_TASK_MGMT,
|
|
214
|
+
CAP_FW_GRAMMAR,
|
|
215
|
+
CAP_FW_VALIDATE,
|
|
216
|
+
CAP_FW_GENESIS,
|
|
217
|
+
CAP_FW_CLI,
|
|
218
|
+
CAP_CODE_REVIEW,
|
|
219
|
+
CAP_WEB,
|
|
220
|
+
CAP_CONTEXT
|
|
221
|
+
];
|
|
222
|
+
var capabilityMap = new Map(
|
|
223
|
+
BUILT_IN_CAPABILITIES.map((c) => [c.name, c])
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// src/bot/estimate-tokens.ts
|
|
227
|
+
function estimateTokens(text) {
|
|
228
|
+
if (!text) return 0;
|
|
229
|
+
const byChars = text.length / 3.5;
|
|
230
|
+
const byWords = text.split(/\s+/).filter(Boolean).length * 1.33;
|
|
231
|
+
return Math.ceil((byChars + byWords) / 2);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// src/ui/capability-editor.tsx
|
|
235
|
+
var React = require("react");
|
|
236
|
+
var { useState, useCallback } = React;
|
|
237
|
+
var {
|
|
238
|
+
Flex,
|
|
239
|
+
Typography,
|
|
240
|
+
Input,
|
|
241
|
+
Button,
|
|
242
|
+
IconButton,
|
|
243
|
+
Icon,
|
|
244
|
+
Field,
|
|
245
|
+
Chip,
|
|
246
|
+
Checkbox,
|
|
247
|
+
Table,
|
|
248
|
+
CodeBlock,
|
|
249
|
+
ScrollArea,
|
|
250
|
+
toast,
|
|
251
|
+
usePackWorkspace
|
|
252
|
+
} = require("@fw/plugin-ui-kit");
|
|
253
|
+
var AVAILABLE_TOOLS = [...PLAN_OPERATIONS];
|
|
254
|
+
function CapabilityDetail({
|
|
255
|
+
cap,
|
|
256
|
+
mode,
|
|
257
|
+
allNames,
|
|
258
|
+
onBack,
|
|
259
|
+
onSubmit
|
|
260
|
+
}) {
|
|
261
|
+
const [name, setName] = useState(cap?.name ?? "");
|
|
262
|
+
const [description, setDescription] = useState(cap?.description ?? "");
|
|
263
|
+
const [prompt, setPrompt] = useState(cap?.prompt ?? "");
|
|
264
|
+
const [tools, setTools] = useState(cap?.tools ?? []);
|
|
265
|
+
const readOnly = mode === "view";
|
|
266
|
+
const isCreate = mode === "create";
|
|
267
|
+
const handleToggleTool = useCallback((tool) => {
|
|
268
|
+
setTools(
|
|
269
|
+
(prev) => prev.includes(tool) ? prev.filter((t) => t !== tool) : [...prev, tool]
|
|
270
|
+
);
|
|
271
|
+
}, []);
|
|
272
|
+
const handleSubmit = useCallback(() => {
|
|
273
|
+
const trimName = name.trim().toLowerCase().replace(/\s+/g, "-");
|
|
274
|
+
if (!trimName || !description.trim()) {
|
|
275
|
+
toast("Name and description are required", { type: "error" });
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (isCreate && allNames.includes(trimName)) {
|
|
279
|
+
toast(`Capability "${trimName}" already exists`, { type: "error" });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
onSubmit({
|
|
283
|
+
name: isCreate ? trimName : cap?.name ?? trimName,
|
|
284
|
+
description: description.trim(),
|
|
285
|
+
prompt: prompt.trim() || `## ${description.trim()}`,
|
|
286
|
+
tools: tools.length > 0 ? tools : void 0
|
|
287
|
+
});
|
|
288
|
+
}, [name, description, prompt, tools, isCreate, allNames, cap, onSubmit]);
|
|
289
|
+
const title = isCreate ? "New Capability" : readOnly ? cap?.name ?? "" : `Edit: ${cap?.name ?? ""}`;
|
|
290
|
+
return React.createElement(
|
|
291
|
+
Flex,
|
|
292
|
+
{
|
|
293
|
+
variant: "column-stretch-start-nowrap-0",
|
|
294
|
+
style: { width: "100%", height: "100%", overflow: "hidden" }
|
|
295
|
+
},
|
|
296
|
+
// Header
|
|
297
|
+
React.createElement(
|
|
298
|
+
Flex,
|
|
299
|
+
{
|
|
300
|
+
variant: "row-center-space-between-nowrap-8",
|
|
301
|
+
style: { padding: "8px 16px", flexShrink: 0, borderBottom: "1px solid var(--color-border-default)" }
|
|
302
|
+
},
|
|
303
|
+
React.createElement(
|
|
304
|
+
Flex,
|
|
305
|
+
{ variant: "row-center-start-nowrap-8" },
|
|
306
|
+
React.createElement(IconButton, {
|
|
307
|
+
icon: "back",
|
|
308
|
+
size: "xs",
|
|
309
|
+
variant: "clear",
|
|
310
|
+
onClick: onBack,
|
|
311
|
+
title: "Back to list"
|
|
312
|
+
}),
|
|
313
|
+
React.createElement(Typography, { variant: "caption-thick", color: "color-text-high" }, title),
|
|
314
|
+
readOnly && React.createElement(Chip, {
|
|
315
|
+
label: "built-in",
|
|
316
|
+
size: "small",
|
|
317
|
+
color: "color-status-info"
|
|
318
|
+
})
|
|
319
|
+
)
|
|
320
|
+
),
|
|
321
|
+
// Scrollable form
|
|
322
|
+
React.createElement(
|
|
323
|
+
ScrollArea,
|
|
324
|
+
{ style: { flex: 1, minHeight: 0 } },
|
|
325
|
+
React.createElement(
|
|
326
|
+
Flex,
|
|
327
|
+
{
|
|
328
|
+
variant: "column-stretch-start-nowrap-10",
|
|
329
|
+
style: { padding: "12px 16px" }
|
|
330
|
+
},
|
|
331
|
+
// Name
|
|
332
|
+
React.createElement(
|
|
333
|
+
Field,
|
|
334
|
+
{ label: "Name" },
|
|
335
|
+
readOnly ? React.createElement(Typography, {
|
|
336
|
+
variant: "smallCaption-thick",
|
|
337
|
+
color: "color-text-high",
|
|
338
|
+
style: { padding: "4px 0" }
|
|
339
|
+
}, cap?.name) : React.createElement(Input, {
|
|
340
|
+
type: "text",
|
|
341
|
+
size: "small",
|
|
342
|
+
placeholder: "e.g. api-design",
|
|
343
|
+
value: name,
|
|
344
|
+
onChange: (v) => setName(v),
|
|
345
|
+
disabled: !isCreate,
|
|
346
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
347
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
348
|
+
})
|
|
349
|
+
),
|
|
350
|
+
// Description
|
|
351
|
+
React.createElement(
|
|
352
|
+
Field,
|
|
353
|
+
{ label: "Description" },
|
|
354
|
+
readOnly ? React.createElement(Typography, {
|
|
355
|
+
variant: "smallCaption-regular",
|
|
356
|
+
color: "color-text-medium",
|
|
357
|
+
style: { padding: "4px 0" }
|
|
358
|
+
}, cap?.description) : React.createElement(Input, {
|
|
359
|
+
type: "text",
|
|
360
|
+
size: "small",
|
|
361
|
+
placeholder: "What this capability provides",
|
|
362
|
+
value: description,
|
|
363
|
+
onChange: (v) => setDescription(v),
|
|
364
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
365
|
+
inputBoxStyle: { maxWidth: "none" }
|
|
366
|
+
})
|
|
367
|
+
),
|
|
368
|
+
// Prompt
|
|
369
|
+
React.createElement(
|
|
370
|
+
Field,
|
|
371
|
+
{ label: "Prompt", align: "start" },
|
|
372
|
+
readOnly ? React.createElement(Typography, {
|
|
373
|
+
variant: "smallCaption-regular",
|
|
374
|
+
color: "color-text-medium",
|
|
375
|
+
style: { whiteSpace: "pre-wrap", lineHeight: 1.5 }
|
|
376
|
+
}, cap?.prompt) : React.createElement(Input, {
|
|
377
|
+
type: "text",
|
|
378
|
+
size: "small",
|
|
379
|
+
multiline: true,
|
|
380
|
+
placeholder: "Knowledge content (markdown) injected into the system prompt",
|
|
381
|
+
value: prompt,
|
|
382
|
+
onChange: (v) => setPrompt(v),
|
|
383
|
+
defaultBoxStyle: { flex: 1, minWidth: 0 },
|
|
384
|
+
inputBoxStyle: { maxWidth: "none", minHeight: 160 }
|
|
385
|
+
})
|
|
386
|
+
),
|
|
387
|
+
// Tools
|
|
388
|
+
React.createElement(
|
|
389
|
+
Field,
|
|
390
|
+
{ label: "Tools", align: "start" },
|
|
391
|
+
React.createElement(Table, {
|
|
392
|
+
size: "compact",
|
|
393
|
+
showHeader: false,
|
|
394
|
+
data: AVAILABLE_TOOLS.map((t) => ({
|
|
395
|
+
tool: t,
|
|
396
|
+
active: (readOnly ? cap?.tools ?? [] : tools).includes(t)
|
|
397
|
+
})),
|
|
398
|
+
getRowKey: (row) => row.tool,
|
|
399
|
+
columns: [
|
|
400
|
+
{
|
|
401
|
+
key: "check",
|
|
402
|
+
header: "",
|
|
403
|
+
width: "28px",
|
|
404
|
+
render: (_v, row) => React.createElement(
|
|
405
|
+
"div",
|
|
406
|
+
{ style: { height: 20, display: "flex", alignItems: "center" } },
|
|
407
|
+
React.createElement(Checkbox, {
|
|
408
|
+
checked: row.active,
|
|
409
|
+
disabled: readOnly,
|
|
410
|
+
onChange: readOnly ? void 0 : () => handleToggleTool(row.tool),
|
|
411
|
+
size: "sm"
|
|
412
|
+
})
|
|
413
|
+
)
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
key: "tool",
|
|
417
|
+
header: "Tool",
|
|
418
|
+
render: (_v, row) => React.createElement(
|
|
419
|
+
"div",
|
|
420
|
+
{ style: { height: 20, display: "flex", alignItems: "center" } },
|
|
421
|
+
React.createElement(Typography, {
|
|
422
|
+
variant: "smallCaption-regular",
|
|
423
|
+
color: row.active ? "color-text-high" : "color-text-subtle"
|
|
424
|
+
}, row.tool)
|
|
425
|
+
)
|
|
426
|
+
}
|
|
427
|
+
]
|
|
428
|
+
})
|
|
429
|
+
),
|
|
430
|
+
// Token estimate
|
|
431
|
+
React.createElement(
|
|
432
|
+
Field,
|
|
433
|
+
{ label: "Est. tokens" },
|
|
434
|
+
React.createElement(Typography, {
|
|
435
|
+
variant: "smallCaption-regular",
|
|
436
|
+
color: "color-text-subtle"
|
|
437
|
+
}, `~${estimateTokens(readOnly ? cap?.prompt ?? "" : prompt)} tokens (approx.)`)
|
|
438
|
+
),
|
|
439
|
+
// Submit button (not for view mode)
|
|
440
|
+
!readOnly && React.createElement(
|
|
441
|
+
Flex,
|
|
442
|
+
{
|
|
443
|
+
variant: "row-center-end-nowrap-8",
|
|
444
|
+
style: { paddingTop: 4 }
|
|
445
|
+
},
|
|
446
|
+
React.createElement(Button, {
|
|
447
|
+
size: "xs",
|
|
448
|
+
variant: "clear",
|
|
449
|
+
color: "secondary",
|
|
450
|
+
onClick: onBack
|
|
451
|
+
}, "Cancel"),
|
|
452
|
+
React.createElement(Button, {
|
|
453
|
+
size: "xs",
|
|
454
|
+
variant: "fill",
|
|
455
|
+
color: "primary",
|
|
456
|
+
onClick: handleSubmit,
|
|
457
|
+
disabled: readOnly || !isCreate && !description.trim() || isCreate && (!name.trim() || !description.trim())
|
|
458
|
+
}, isCreate ? "Create" : "Save")
|
|
459
|
+
)
|
|
460
|
+
)
|
|
461
|
+
)
|
|
462
|
+
);
|
|
463
|
+
}
|
|
464
|
+
function CapabilityList({
|
|
465
|
+
allCaps,
|
|
466
|
+
onOpen,
|
|
467
|
+
onDelete,
|
|
468
|
+
onCreate,
|
|
469
|
+
onSaveAll,
|
|
470
|
+
onClose,
|
|
471
|
+
hasCustom
|
|
472
|
+
}) {
|
|
473
|
+
return React.createElement(
|
|
474
|
+
Flex,
|
|
475
|
+
{
|
|
476
|
+
variant: "column-stretch-start-nowrap-0",
|
|
477
|
+
style: { width: "100%", height: "100%", overflow: "hidden" }
|
|
478
|
+
},
|
|
479
|
+
// Header
|
|
480
|
+
React.createElement(
|
|
481
|
+
Flex,
|
|
482
|
+
{
|
|
483
|
+
variant: "row-center-space-between-nowrap-8",
|
|
484
|
+
style: { padding: "8px 16px", flexShrink: 0, borderBottom: "1px solid var(--color-border-default)" }
|
|
485
|
+
},
|
|
486
|
+
React.createElement(
|
|
487
|
+
Flex,
|
|
488
|
+
{ variant: "row-center-start-nowrap-8" },
|
|
489
|
+
onClose && React.createElement(IconButton, {
|
|
490
|
+
icon: "back",
|
|
491
|
+
size: "xs",
|
|
492
|
+
variant: "clear",
|
|
493
|
+
onClick: onClose,
|
|
494
|
+
title: "Back"
|
|
495
|
+
}),
|
|
496
|
+
React.createElement(Typography, { variant: "caption-thick", color: "color-text-high" }, "Capabilities"),
|
|
497
|
+
React.createElement(Chip, {
|
|
498
|
+
label: `${allCaps.length}`,
|
|
499
|
+
size: "small",
|
|
500
|
+
color: "color-brand-main"
|
|
501
|
+
})
|
|
502
|
+
),
|
|
503
|
+
React.createElement(
|
|
504
|
+
Flex,
|
|
505
|
+
{ variant: "row-center-end-nowrap-6" },
|
|
506
|
+
onSaveAll && hasCustom && React.createElement(Button, {
|
|
507
|
+
size: "xs",
|
|
508
|
+
variant: "outlined",
|
|
509
|
+
color: "primary",
|
|
510
|
+
onClick: onSaveAll
|
|
511
|
+
}, "Save"),
|
|
512
|
+
React.createElement(Button, {
|
|
513
|
+
size: "xs",
|
|
514
|
+
variant: "fill",
|
|
515
|
+
color: "primary",
|
|
516
|
+
onClick: onCreate
|
|
517
|
+
}, "New")
|
|
518
|
+
)
|
|
519
|
+
),
|
|
520
|
+
// Scrollable list
|
|
521
|
+
React.createElement(
|
|
522
|
+
ScrollArea,
|
|
523
|
+
{ style: { flex: 1, minHeight: 0 } },
|
|
524
|
+
React.createElement(
|
|
525
|
+
Flex,
|
|
526
|
+
{
|
|
527
|
+
variant: "column-stretch-start-nowrap-2",
|
|
528
|
+
style: { padding: "8px 16px" }
|
|
529
|
+
},
|
|
530
|
+
...allCaps.map(
|
|
531
|
+
(cap) => React.createElement(
|
|
532
|
+
Flex,
|
|
533
|
+
{
|
|
534
|
+
key: cap.name,
|
|
535
|
+
variant: "row-center-space-between-nowrap-8",
|
|
536
|
+
style: {
|
|
537
|
+
padding: "7px 10px",
|
|
538
|
+
borderRadius: 6,
|
|
539
|
+
cursor: "pointer",
|
|
540
|
+
border: "1px solid var(--color-border-default)",
|
|
541
|
+
transition: "background-color 0.1s"
|
|
542
|
+
},
|
|
543
|
+
onClick: () => onOpen(cap.name),
|
|
544
|
+
onMouseEnter: (e) => {
|
|
545
|
+
e.currentTarget.style.backgroundColor = "var(--color-surface-hover)";
|
|
546
|
+
},
|
|
547
|
+
onMouseLeave: (e) => {
|
|
548
|
+
e.currentTarget.style.backgroundColor = "transparent";
|
|
549
|
+
}
|
|
550
|
+
},
|
|
551
|
+
// Left: icon + name + description
|
|
552
|
+
React.createElement(
|
|
553
|
+
Flex,
|
|
554
|
+
{ variant: "row-center-start-nowrap-8", style: { flex: 1, minWidth: 0 } },
|
|
555
|
+
React.createElement(Icon, {
|
|
556
|
+
name: cap.builtIn ? "lock" : "edit",
|
|
557
|
+
size: 13,
|
|
558
|
+
color: cap.builtIn ? "color-text-subtle" : "color-brand-main"
|
|
559
|
+
}),
|
|
560
|
+
React.createElement(Typography, {
|
|
561
|
+
variant: "smallCaption-thick",
|
|
562
|
+
color: "color-text-high",
|
|
563
|
+
style: { minWidth: 80, flexShrink: 0 }
|
|
564
|
+
}, cap.name),
|
|
565
|
+
React.createElement(Typography, {
|
|
566
|
+
variant: "smallCaption-regular",
|
|
567
|
+
color: "color-text-subtle",
|
|
568
|
+
style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }
|
|
569
|
+
}, cap.description)
|
|
570
|
+
),
|
|
571
|
+
// Right: chips + actions
|
|
572
|
+
React.createElement(
|
|
573
|
+
Flex,
|
|
574
|
+
{ variant: "row-center-end-nowrap-4", style: { flexShrink: 0 } },
|
|
575
|
+
cap.tools && cap.tools.length > 0 && React.createElement(Chip, {
|
|
576
|
+
label: `${cap.tools.length} tools`,
|
|
577
|
+
size: "small",
|
|
578
|
+
color: "color-status-positive"
|
|
579
|
+
}),
|
|
580
|
+
!cap.builtIn && React.createElement(IconButton, {
|
|
581
|
+
icon: "outlinedDelete",
|
|
582
|
+
size: "xs",
|
|
583
|
+
variant: "clear",
|
|
584
|
+
color: "danger",
|
|
585
|
+
onClick: (e) => {
|
|
586
|
+
e.stopPropagation();
|
|
587
|
+
onDelete(cap.name);
|
|
588
|
+
},
|
|
589
|
+
title: "Delete capability"
|
|
590
|
+
}),
|
|
591
|
+
React.createElement(Icon, {
|
|
592
|
+
name: "chevronRight",
|
|
593
|
+
size: 12,
|
|
594
|
+
color: "color-text-subtle"
|
|
595
|
+
})
|
|
596
|
+
)
|
|
597
|
+
)
|
|
598
|
+
)
|
|
599
|
+
)
|
|
600
|
+
)
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
function CapabilityEditor({ customCapabilities = [], onSave, onClose }) {
|
|
604
|
+
const [customs, setCustoms] = useState(customCapabilities);
|
|
605
|
+
const [screen, setScreen] = useState("list");
|
|
606
|
+
const allCaps = [
|
|
607
|
+
...BUILT_IN_CAPABILITIES.map((c) => ({ ...c, builtIn: true })),
|
|
608
|
+
...customs.map((c) => ({ ...c, builtIn: false }))
|
|
609
|
+
];
|
|
610
|
+
const allNames = allCaps.map((c) => c.name);
|
|
611
|
+
const handleOpen = useCallback((name) => {
|
|
612
|
+
const cap = allCaps.find((c) => c.name === name);
|
|
613
|
+
if (!cap) return;
|
|
614
|
+
setScreen({ mode: cap.builtIn ? "view" : "edit", name });
|
|
615
|
+
}, [allCaps]);
|
|
616
|
+
const handleDelete = useCallback((name) => {
|
|
617
|
+
setCustoms((prev) => prev.filter((c) => c.name !== name));
|
|
618
|
+
toast(`Capability "${name}" deleted`, { type: "success" });
|
|
619
|
+
}, []);
|
|
620
|
+
const handleSubmit = useCallback((cap) => {
|
|
621
|
+
if (typeof screen === "object" && screen.mode === "edit") {
|
|
622
|
+
setCustoms(
|
|
623
|
+
(prev) => prev.map((c) => c.name === cap.name ? cap : c)
|
|
624
|
+
);
|
|
625
|
+
toast(`Capability "${cap.name}" updated`, { type: "success" });
|
|
626
|
+
} else {
|
|
627
|
+
setCustoms((prev) => [...prev, cap]);
|
|
628
|
+
toast(`Capability "${cap.name}" created`, { type: "success" });
|
|
629
|
+
}
|
|
630
|
+
setScreen("list");
|
|
631
|
+
}, [screen]);
|
|
632
|
+
const handleSaveAll = useCallback(() => {
|
|
633
|
+
onSave?.(customs);
|
|
634
|
+
toast("Capabilities saved", { type: "success" });
|
|
635
|
+
}, [customs, onSave]);
|
|
636
|
+
if (typeof screen === "object") {
|
|
637
|
+
const cap = screen.name ? allCaps.find((c) => c.name === screen.name) : void 0;
|
|
638
|
+
return React.createElement(CapabilityDetail, {
|
|
639
|
+
cap,
|
|
640
|
+
mode: screen.mode,
|
|
641
|
+
allNames,
|
|
642
|
+
onBack: () => setScreen("list"),
|
|
643
|
+
onSubmit: handleSubmit
|
|
644
|
+
});
|
|
645
|
+
}
|
|
646
|
+
return React.createElement(CapabilityList, {
|
|
647
|
+
allCaps,
|
|
648
|
+
onOpen: handleOpen,
|
|
649
|
+
onDelete: handleDelete,
|
|
650
|
+
onCreate: () => setScreen({ mode: "create" }),
|
|
651
|
+
onSaveAll: onSave ? handleSaveAll : void 0,
|
|
652
|
+
onClose,
|
|
653
|
+
hasCustom: customs.length > 0
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
var capability_editor_default = CapabilityEditor;
|
|
657
|
+
module.exports = CapabilityEditor;
|
package/dist/ui/profile-card.js
CHANGED
|
@@ -138,6 +138,31 @@ function ProfileCard({ profile, activeInstances, onEdit, onDelete }) {
|
|
|
138
138
|
)
|
|
139
139
|
)
|
|
140
140
|
),
|
|
141
|
+
// Knowledge capabilities (from behavior)
|
|
142
|
+
behavior?.capabilities && behavior.capabilities.length > 0 && React.createElement(
|
|
143
|
+
Field,
|
|
144
|
+
{ label: "Knowledge", labelWidth: 90, align: "start" },
|
|
145
|
+
React.createElement(
|
|
146
|
+
Flex,
|
|
147
|
+
{ variant: "row-center-start-wrap-4" },
|
|
148
|
+
...behavior.capabilities.map(
|
|
149
|
+
(capName) => React.createElement(
|
|
150
|
+
"span",
|
|
151
|
+
{ key: capName },
|
|
152
|
+
React.createElement(Chip, { label: capName, size: "small", color: "color-status-info" })
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
),
|
|
157
|
+
// Budget (from behavior)
|
|
158
|
+
behavior?.budget && React.createElement(
|
|
159
|
+
Field,
|
|
160
|
+
{ label: "Budget", labelWidth: 90, align: "center" },
|
|
161
|
+
React.createElement(Typography, {
|
|
162
|
+
variant: "smallCaption-regular",
|
|
163
|
+
color: "color-text-high"
|
|
164
|
+
}, `$${behavior.budget.toFixed(2)}/task`)
|
|
165
|
+
),
|
|
141
166
|
// Strategy
|
|
142
167
|
React.createElement(
|
|
143
168
|
Field,
|