@synergenius/flow-weaver-pack-weaver 0.9.140 → 0.9.142
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/assistant-tools.d.ts.map +1 -1
- package/dist/bot/assistant-tools.js +5 -11
- package/dist/bot/assistant-tools.js.map +1 -1
- package/dist/bot/capability-registry.d.ts.map +1 -1
- package/dist/bot/capability-registry.js +185 -15
- package/dist/bot/capability-registry.js.map +1 -1
- package/dist/bot/dashboard.js +3 -3
- package/dist/bot/dashboard.js.map +1 -1
- package/dist/bot/hierarchy-event-log.d.ts +37 -0
- package/dist/bot/hierarchy-event-log.d.ts.map +1 -0
- package/dist/bot/hierarchy-event-log.js +58 -0
- package/dist/bot/hierarchy-event-log.js.map +1 -0
- package/dist/bot/operations.d.ts +2 -0
- package/dist/bot/operations.d.ts.map +1 -1
- package/dist/bot/operations.js +5 -0
- package/dist/bot/operations.js.map +1 -1
- package/dist/bot/profile-store.d.ts.map +1 -1
- package/dist/bot/profile-store.js +39 -0
- package/dist/bot/profile-store.js.map +1 -1
- package/dist/bot/runner.d.ts.map +1 -1
- package/dist/bot/runner.js +7 -17
- package/dist/bot/runner.js.map +1 -1
- package/dist/bot/step-executor.d.ts.map +1 -1
- package/dist/bot/step-executor.js +33 -1
- package/dist/bot/step-executor.js.map +1 -1
- package/dist/bot/swarm-controller.d.ts +1 -0
- package/dist/bot/swarm-controller.d.ts.map +1 -1
- package/dist/bot/swarm-controller.js +59 -5
- package/dist/bot/swarm-controller.js.map +1 -1
- package/dist/bot/task-store.d.ts +1 -1
- package/dist/bot/task-store.d.ts.map +1 -1
- package/dist/bot/task-store.js +21 -36
- package/dist/bot/task-store.js.map +1 -1
- package/dist/bot/task-types.d.ts +5 -1
- package/dist/bot/task-types.d.ts.map +1 -1
- package/dist/node-types/bot-report.js +2 -2
- package/dist/node-types/bot-report.js.map +1 -1
- package/dist/node-types/build-context.d.ts.map +1 -1
- package/dist/node-types/build-context.js +32 -0
- package/dist/node-types/build-context.js.map +1 -1
- package/dist/node-types/report.d.ts +1 -1
- package/dist/node-types/report.d.ts.map +1 -1
- package/dist/node-types/report.js +58 -8
- package/dist/node-types/report.js.map +1 -1
- package/dist/ui/capability-editor.js +184 -15
- package/dist/ui/profile-editor.js +184 -15
- package/dist/ui/swarm-dashboard.js +244 -44
- package/dist/ui/task-detail-view.js +60 -29
- package/dist/ui/use-stream-timeline.d.ts.map +1 -1
- package/dist/ui/use-stream-timeline.js +69 -29
- package/dist/ui/use-stream-timeline.js.map +1 -1
- package/flowweaver.manifest.json +1 -1
- package/package.json +1 -1
- package/src/bot/assistant-tools.ts +5 -11
- package/src/bot/capability-registry.ts +196 -18
- package/src/bot/dashboard.ts +3 -3
- package/src/bot/hierarchy-event-log.ts +64 -0
- package/src/bot/operations.ts +7 -0
- package/src/bot/profile-store.ts +39 -0
- package/src/bot/runner.ts +8 -19
- package/src/bot/step-executor.ts +29 -1
- package/src/bot/swarm-controller.ts +62 -5
- package/src/bot/task-store.ts +22 -38
- package/src/bot/task-types.ts +7 -1
- package/src/node-types/bot-report.ts +2 -2
- package/src/node-types/build-context.ts +32 -0
- package/src/node-types/report.ts +56 -8
- package/src/ui/use-stream-timeline.ts +73 -33
|
@@ -556,6 +556,7 @@ function useStreamTimeline(events, isDone) {
|
|
|
556
556
|
const entries = [];
|
|
557
557
|
const nodeEntryIndex = /* @__PURE__ */ new Map();
|
|
558
558
|
const nodeStarts = /* @__PURE__ */ new Map();
|
|
559
|
+
const completedNodes = /* @__PURE__ */ new Set();
|
|
559
560
|
let idCounter = 0;
|
|
560
561
|
for (const event of events) {
|
|
561
562
|
const d = event.data ?? {};
|
|
@@ -572,6 +573,11 @@ function useStreamTimeline(events, isDone) {
|
|
|
572
573
|
case "node-start": {
|
|
573
574
|
const nodeId = d.nodeId;
|
|
574
575
|
if (nodeId) nodeStarts.set(nodeId, event.timestamp);
|
|
576
|
+
const existingStartIdx = nodeEntryIndex.get(nodeId);
|
|
577
|
+
if (existingStartIdx != null && entries[existingStartIdx]?.type === "node-started") {
|
|
578
|
+
break;
|
|
579
|
+
}
|
|
580
|
+
completedNodes.delete(nodeId);
|
|
575
581
|
const idx = entries.length;
|
|
576
582
|
nodeEntryIndex.set(nodeId, idx);
|
|
577
583
|
entries.push({
|
|
@@ -587,54 +593,79 @@ function useStreamTimeline(events, isDone) {
|
|
|
587
593
|
}
|
|
588
594
|
case "node-complete": {
|
|
589
595
|
const nodeId = d.nodeId;
|
|
596
|
+
if (completedNodes.has(nodeId)) break;
|
|
590
597
|
const startTs = nodeStarts.get(nodeId);
|
|
591
598
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
|
|
592
599
|
if (nodeId) nodeStarts.delete(nodeId);
|
|
593
|
-
|
|
594
|
-
const completed = {
|
|
595
|
-
id: `s-${idCounter++}`,
|
|
596
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
597
|
-
type: "node-completed",
|
|
598
|
-
nodeId,
|
|
599
|
-
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
600
|
-
duration,
|
|
601
|
-
color: d.color,
|
|
602
|
-
icon: d.icon,
|
|
603
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
604
|
-
};
|
|
600
|
+
completedNodes.add(nodeId);
|
|
605
601
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
606
602
|
if (existingIdx != null && entries[existingIdx]) {
|
|
607
|
-
|
|
603
|
+
const rawOutputs = d.outputs;
|
|
604
|
+
entries[existingIdx] = {
|
|
605
|
+
id: entries[existingIdx].id,
|
|
606
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
607
|
+
type: "node-completed",
|
|
608
|
+
nodeId,
|
|
609
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
610
|
+
duration,
|
|
611
|
+
color: d.color,
|
|
612
|
+
icon: d.icon,
|
|
613
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
614
|
+
};
|
|
608
615
|
nodeEntryIndex.delete(nodeId);
|
|
609
616
|
} else {
|
|
610
|
-
|
|
617
|
+
const rawOutputs = d.outputs;
|
|
618
|
+
entries.push({
|
|
619
|
+
id: `s-${idCounter++}`,
|
|
620
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
621
|
+
type: "node-completed",
|
|
622
|
+
nodeId,
|
|
623
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
624
|
+
duration,
|
|
625
|
+
color: d.color,
|
|
626
|
+
icon: d.icon,
|
|
627
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
628
|
+
});
|
|
611
629
|
}
|
|
612
630
|
break;
|
|
613
631
|
}
|
|
614
632
|
case "node-error": {
|
|
615
633
|
const nodeId = d.nodeId;
|
|
634
|
+
if (completedNodes.has(nodeId)) break;
|
|
616
635
|
const startTs = nodeStarts.get(nodeId);
|
|
617
636
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
|
|
618
637
|
if (nodeId) nodeStarts.delete(nodeId);
|
|
619
|
-
|
|
620
|
-
const failed = {
|
|
621
|
-
id: `s-${idCounter++}`,
|
|
622
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
623
|
-
type: "node-failed",
|
|
624
|
-
nodeId,
|
|
625
|
-
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
626
|
-
detail: d.error,
|
|
627
|
-
duration,
|
|
628
|
-
color: d.color,
|
|
629
|
-
icon: d.icon,
|
|
630
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
631
|
-
};
|
|
638
|
+
completedNodes.add(nodeId);
|
|
632
639
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
633
640
|
if (existingIdx != null && entries[existingIdx]) {
|
|
634
|
-
|
|
641
|
+
const rawOutputs = d.outputs;
|
|
642
|
+
entries[existingIdx] = {
|
|
643
|
+
id: entries[existingIdx].id,
|
|
644
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
645
|
+
type: "node-failed",
|
|
646
|
+
nodeId,
|
|
647
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
648
|
+
detail: d.error,
|
|
649
|
+
duration,
|
|
650
|
+
color: d.color,
|
|
651
|
+
icon: d.icon,
|
|
652
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
653
|
+
};
|
|
635
654
|
nodeEntryIndex.delete(nodeId);
|
|
636
655
|
} else {
|
|
637
|
-
|
|
656
|
+
const rawOutputs = d.outputs;
|
|
657
|
+
entries.push({
|
|
658
|
+
id: `s-${idCounter++}`,
|
|
659
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
660
|
+
type: "node-failed",
|
|
661
|
+
nodeId,
|
|
662
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
663
|
+
detail: d.error,
|
|
664
|
+
duration,
|
|
665
|
+
color: d.color,
|
|
666
|
+
icon: d.icon,
|
|
667
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
668
|
+
});
|
|
638
669
|
}
|
|
639
670
|
break;
|
|
640
671
|
}
|
|
@@ -2672,6 +2703,8 @@ var OP_VALIDATE = "validate";
|
|
|
2672
2703
|
var OP_TSC_CHECK = "tsc_check";
|
|
2673
2704
|
var OP_RUN_TESTS = "run_tests";
|
|
2674
2705
|
var OP_TASK_CREATE = "task_create";
|
|
2706
|
+
var OP_REMEMBER = "remember";
|
|
2707
|
+
var OP_RECALL = "recall";
|
|
2675
2708
|
|
|
2676
2709
|
// src/bot/capability-registry.ts
|
|
2677
2710
|
var CAP_CORE = {
|
|
@@ -2721,13 +2754,31 @@ Use run_shell for running tests (npx vitest), validation (flow-weaver validate),
|
|
|
2721
2754
|
};
|
|
2722
2755
|
var CAP_TASK_MGMT = {
|
|
2723
2756
|
name: "task-mgmt",
|
|
2724
|
-
description: "Create and manage swarm subtasks for parallel execution.",
|
|
2757
|
+
description: "Create and manage swarm subtasks for parallel execution, with decomposition and review nudges.",
|
|
2725
2758
|
tools: [OP_TASK_CREATE],
|
|
2726
|
-
prompt: `## Task Management
|
|
2727
|
-
|
|
2728
|
-
-
|
|
2759
|
+
prompt: `## Task Management & Decomposition
|
|
2760
|
+
|
|
2761
|
+
- task_create: Create swarm subtasks. args: { title, description, complexity, subtasks[], dependsOn[], assignedProfile? }
|
|
2729
2762
|
|
|
2730
|
-
|
|
2763
|
+
### Decomposition
|
|
2764
|
+
When you encounter a broad objective (multi-file, multi-concern), decompose into subtasks:
|
|
2765
|
+
- If the task is bigger than a single file change, create subtasks instead of doing it all yourself.
|
|
2766
|
+
- Minimize dependencies between subtasks to maximize parallel execution.
|
|
2767
|
+
- Set complexity per subtask: trivial | simple | moderate | complex.
|
|
2768
|
+
- Use dependsOn to express blocking relationships (e.g., setup before code, code before tests).
|
|
2769
|
+
|
|
2770
|
+
### Review Task Creation
|
|
2771
|
+
After creating or modifying multiple files, create a review task:
|
|
2772
|
+
- title: "Review: [what was changed]"
|
|
2773
|
+
- description: List the files modified and what to check
|
|
2774
|
+
- assignedProfile: "reviewer"
|
|
2775
|
+
- complexity: "simple"
|
|
2776
|
+
Skip review for trivial single-file tasks.
|
|
2777
|
+
|
|
2778
|
+
### Dependency Guidelines
|
|
2779
|
+
- BAD: A \u2192 B \u2192 C \u2192 D (serial, slow)
|
|
2780
|
+
- GOOD: A \u2192 [B + C + D] (A blocks all, but B/C/D run in parallel)
|
|
2781
|
+
Structure as: setup \u2192 independent implementations \u2192 integration/testing.`
|
|
2731
2782
|
};
|
|
2732
2783
|
var CAP_FW_GRAMMAR = {
|
|
2733
2784
|
name: "fw-grammar",
|
|
@@ -2805,17 +2856,41 @@ Note: compile, validate, modify, diff, diagram, and describe operations are avai
|
|
|
2805
2856
|
};
|
|
2806
2857
|
var CAP_CODE_REVIEW = {
|
|
2807
2858
|
name: "code-review",
|
|
2808
|
-
description: "
|
|
2809
|
-
|
|
2859
|
+
description: "Comprehensive code review with correctness, security, style, testing, and performance checks.",
|
|
2860
|
+
tools: [OP_READ_FILE, OP_PATCH_FILE, OP_RUN_SHELL],
|
|
2861
|
+
prompt: `## Code Review Checklist
|
|
2862
|
+
|
|
2863
|
+
### 1. Correctness
|
|
2864
|
+
- Does the code do what the task asked?
|
|
2865
|
+
- Edge cases handled (empty input, null, invalid types)?
|
|
2866
|
+
- Error paths covered (try/catch, validation)?
|
|
2867
|
+
- Return types match function signature?
|
|
2810
2868
|
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2869
|
+
### 2. Security
|
|
2870
|
+
- NO hardcoded API keys, passwords, or tokens (use env vars)
|
|
2871
|
+
- NO shell: true in child_process (command injection risk)
|
|
2872
|
+
- NO eval() or Function() with untrusted input
|
|
2873
|
+
- User input validated and sanitized before use
|
|
2874
|
+
- File paths validated (no ../ traversal)
|
|
2817
2875
|
|
|
2818
|
-
|
|
2876
|
+
### 3. Style
|
|
2877
|
+
- Naming is clear and consistent with project conventions
|
|
2878
|
+
- No dead code (unused variables, unreachable branches)
|
|
2879
|
+
- No debug statements left in (console.log, debugger)
|
|
2880
|
+
- Imports organized, no duplicates
|
|
2881
|
+
|
|
2882
|
+
### 4. Testing
|
|
2883
|
+
- Unit tests exist for new/changed functions
|
|
2884
|
+
- Tests cover happy path AND edge cases
|
|
2885
|
+
- Error cases have tests
|
|
2886
|
+
- Code coverage adequate (aim for 80%+ of changed code)
|
|
2887
|
+
|
|
2888
|
+
### 5. Performance
|
|
2889
|
+
- No O(n\xB2) loops where O(n) is possible
|
|
2890
|
+
- No blocking I/O in async code
|
|
2891
|
+
- No memory leaks (listeners removed, timers cleared)
|
|
2892
|
+
|
|
2893
|
+
Report findings as: FILE:LINE | SEVERITY (critical/high/medium/low) | ISSUE \u2192 Fix suggestion`
|
|
2819
2894
|
};
|
|
2820
2895
|
var CAP_WEB = {
|
|
2821
2896
|
name: "web",
|
|
@@ -2832,6 +2907,123 @@ var CAP_CONTEXT = {
|
|
|
2832
2907
|
Use list_files to understand the project structure before making changes.
|
|
2833
2908
|
The context bundle (when available) provides a snapshot of the workspace.`
|
|
2834
2909
|
};
|
|
2910
|
+
var CAP_VERIFICATION = {
|
|
2911
|
+
name: "verification",
|
|
2912
|
+
description: "Post-write verification: run tsc and tests to catch errors before delivery.",
|
|
2913
|
+
tools: [OP_RUN_SHELL],
|
|
2914
|
+
prompt: `## Verification
|
|
2915
|
+
|
|
2916
|
+
After writing or patching code, ALWAYS verify your work:
|
|
2917
|
+
1. Run \`npx tsc --noEmit\` in the project root to catch TypeScript errors
|
|
2918
|
+
2. If package.json has a "test" script, run \`npm test\` to validate functionality
|
|
2919
|
+
3. If verification fails, read the errors, fix the code, and re-verify
|
|
2920
|
+
|
|
2921
|
+
Include verification as explicit steps in your plan. Verification is NOT optional.
|
|
2922
|
+
Do NOT deliver code that hasn't been verified.`
|
|
2923
|
+
};
|
|
2924
|
+
var CAP_CROSS_FILE_CHECK = {
|
|
2925
|
+
name: "cross-file-check",
|
|
2926
|
+
description: "Verify imports, exports, module paths, and cross-file dependencies.",
|
|
2927
|
+
tools: [OP_READ_FILE, OP_LIST_FILES, OP_RUN_SHELL],
|
|
2928
|
+
prompt: `## Cross-File Dependency Checks
|
|
2929
|
+
|
|
2930
|
+
When modifying code that affects multiple files:
|
|
2931
|
+
1. If you rename an export, grep for all imports of it and update them
|
|
2932
|
+
2. Verify relative import paths resolve correctly (../types vs ./types)
|
|
2933
|
+
3. Check for circular dependencies (A imports B imports A)
|
|
2934
|
+
4. If you change a function signature, update all callers
|
|
2935
|
+
5. Use \`run_shell\` with grep to search: grep -r "functionName" src/
|
|
2936
|
+
|
|
2937
|
+
Do NOT move or rename exports without verifying all dependents.`
|
|
2938
|
+
};
|
|
2939
|
+
var CAP_PROJECT_SETUP = {
|
|
2940
|
+
name: "project-setup",
|
|
2941
|
+
description: "Initialize new projects with correct structure, config, and dependencies.",
|
|
2942
|
+
tools: [OP_WRITE_FILE, OP_RUN_SHELL],
|
|
2943
|
+
prompt: `## Project Setup
|
|
2944
|
+
|
|
2945
|
+
When initializing a project:
|
|
2946
|
+
1. Create package.json with name, type: "module", main, scripts (build, test)
|
|
2947
|
+
2. Create tsconfig.json with strict: true, module: "esnext", target: "ES2020"
|
|
2948
|
+
3. Create standard directories: src/, tests/
|
|
2949
|
+
4. Install dependencies with run_shell: npm install <deps>
|
|
2950
|
+
5. Create .gitignore excluding node_modules/, dist/
|
|
2951
|
+
6. Verify setup: run tsc --noEmit to ensure TypeScript compiles`
|
|
2952
|
+
};
|
|
2953
|
+
var CAP_SECURITY = {
|
|
2954
|
+
name: "security",
|
|
2955
|
+
description: "Audit code for vulnerabilities, secrets, and security best practices.",
|
|
2956
|
+
tools: [OP_READ_FILE, OP_LIST_FILES, OP_RUN_SHELL],
|
|
2957
|
+
prompt: `## Security Audit
|
|
2958
|
+
|
|
2959
|
+
Check for:
|
|
2960
|
+
1. **Secrets**: NO hardcoded API keys, passwords, tokens. Use env vars.
|
|
2961
|
+
grep -r "password\\|secret\\|apiKey\\|token" src/ to find leaks.
|
|
2962
|
+
2. **Injection**: NO string concatenation in SQL. NO shell: true in child_process. NO eval().
|
|
2963
|
+
3. **Dependencies**: Run npm audit to check for known CVEs.
|
|
2964
|
+
4. **File paths**: Validate paths to prevent ../ traversal attacks.
|
|
2965
|
+
5. **Data handling**: Validate user input (type, length, format). Sanitize before logging.
|
|
2966
|
+
|
|
2967
|
+
Report findings with severity: critical | high | medium | low.`
|
|
2968
|
+
};
|
|
2969
|
+
var CAP_DECOMPOSITION = {
|
|
2970
|
+
name: "decomposition",
|
|
2971
|
+
description: "Break complex objectives into subtask DAGs with dependencies for parallel execution.",
|
|
2972
|
+
tools: [OP_TASK_CREATE],
|
|
2973
|
+
prompt: `## Task Decomposition
|
|
2974
|
+
|
|
2975
|
+
When given a large objective, break it into smaller subtasks:
|
|
2976
|
+
1. Identify all work items (files, features, tests)
|
|
2977
|
+
2. Group by dependency: what must happen first?
|
|
2978
|
+
3. Create subtasks with task_create, each focused on one responsibility
|
|
2979
|
+
4. Set dependencies with dependsOn to model blocking relationships
|
|
2980
|
+
5. Minimize dependencies to maximize parallel execution
|
|
2981
|
+
6. Estimate complexity per subtask: trivial | simple | moderate | complex
|
|
2982
|
+
|
|
2983
|
+
Example: "Implement auth module"
|
|
2984
|
+
- Task A: Extract shared auth types (simple)
|
|
2985
|
+
- Task B: Rewrite login endpoint (moderate, depends on A)
|
|
2986
|
+
- Task C: Add login tests (moderate, depends on B)
|
|
2987
|
+
- Task D: Update auth docs (simple, independent \u2014 runs in parallel with B)
|
|
2988
|
+
|
|
2989
|
+
Assign profiles: code tasks \u2192 developer, review tasks \u2192 reviewer, infra \u2192 ops.`
|
|
2990
|
+
};
|
|
2991
|
+
var CAP_ROUTING = {
|
|
2992
|
+
name: "routing",
|
|
2993
|
+
description: "Route tasks to appropriate bot profiles based on capabilities and complexity.",
|
|
2994
|
+
tools: [OP_TASK_CREATE],
|
|
2995
|
+
prompt: `## Task Routing
|
|
2996
|
+
|
|
2997
|
+
When creating subtasks, assign the right profile:
|
|
2998
|
+
- Code writing, file creation, bug fixes \u2192 developer profile
|
|
2999
|
+
- Code review, quality checks \u2192 reviewer profile
|
|
3000
|
+
- Shell commands, project setup, infrastructure \u2192 ops profile
|
|
3001
|
+
- Leave assignedProfile empty for auto-triage when unsure
|
|
3002
|
+
|
|
3003
|
+
Match complexity to profile capabilities:
|
|
3004
|
+
- trivial/simple tasks: any profile (prefer cheapest)
|
|
3005
|
+
- moderate tasks: specialist profiles
|
|
3006
|
+
- complex tasks: profiles with full capability sets`
|
|
3007
|
+
};
|
|
3008
|
+
var CAP_MEMORY = {
|
|
3009
|
+
name: "memory",
|
|
3010
|
+
description: "Remember and recall project conventions for continuity across sessions.",
|
|
3011
|
+
tools: [OP_REMEMBER, OP_RECALL],
|
|
3012
|
+
prompt: `## Project Memory
|
|
3013
|
+
|
|
3014
|
+
Persist project conventions for future sessions:
|
|
3015
|
+
- remember: Save a convention. args: { key: "naming", value: "kebab-case for files" }
|
|
3016
|
+
- recall: Load all saved conventions. args: {} \u2014 returns project memory.
|
|
3017
|
+
|
|
3018
|
+
What to remember:
|
|
3019
|
+
- Naming conventions (file names, variable names)
|
|
3020
|
+
- Architecture decisions (Result pattern, Zod for validation)
|
|
3021
|
+
- Test patterns (where tests go, what framework)
|
|
3022
|
+
- Common dependencies and their usage
|
|
3023
|
+
|
|
3024
|
+
Before planning, recall project memory to follow established patterns.
|
|
3025
|
+
When you discover a new convention, remember it for future bots.`
|
|
3026
|
+
};
|
|
2835
3027
|
var BUILT_IN_CAPABILITIES = [
|
|
2836
3028
|
CAP_CORE,
|
|
2837
3029
|
CAP_FILE_OPS,
|
|
@@ -2843,7 +3035,15 @@ var BUILT_IN_CAPABILITIES = [
|
|
|
2843
3035
|
CAP_FW_CLI,
|
|
2844
3036
|
CAP_CODE_REVIEW,
|
|
2845
3037
|
CAP_WEB,
|
|
2846
|
-
CAP_CONTEXT
|
|
3038
|
+
CAP_CONTEXT,
|
|
3039
|
+
// Swarm improvement capabilities
|
|
3040
|
+
CAP_VERIFICATION,
|
|
3041
|
+
CAP_CROSS_FILE_CHECK,
|
|
3042
|
+
CAP_PROJECT_SETUP,
|
|
3043
|
+
CAP_SECURITY,
|
|
3044
|
+
CAP_DECOMPOSITION,
|
|
3045
|
+
CAP_ROUTING,
|
|
3046
|
+
CAP_MEMORY
|
|
2847
3047
|
];
|
|
2848
3048
|
var capabilityMap = new Map(
|
|
2849
3049
|
BUILT_IN_CAPABILITIES.map((c) => [c.name, c])
|
|
@@ -50,6 +50,7 @@ function useStreamTimeline(events, isDone) {
|
|
|
50
50
|
const entries = [];
|
|
51
51
|
const nodeEntryIndex = /* @__PURE__ */ new Map();
|
|
52
52
|
const nodeStarts = /* @__PURE__ */ new Map();
|
|
53
|
+
const completedNodes = /* @__PURE__ */ new Set();
|
|
53
54
|
let idCounter = 0;
|
|
54
55
|
for (const event of events) {
|
|
55
56
|
const d = event.data ?? {};
|
|
@@ -66,6 +67,11 @@ function useStreamTimeline(events, isDone) {
|
|
|
66
67
|
case "node-start": {
|
|
67
68
|
const nodeId = d.nodeId;
|
|
68
69
|
if (nodeId) nodeStarts.set(nodeId, event.timestamp);
|
|
70
|
+
const existingStartIdx = nodeEntryIndex.get(nodeId);
|
|
71
|
+
if (existingStartIdx != null && entries[existingStartIdx]?.type === "node-started") {
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
completedNodes.delete(nodeId);
|
|
69
75
|
const idx = entries.length;
|
|
70
76
|
nodeEntryIndex.set(nodeId, idx);
|
|
71
77
|
entries.push({
|
|
@@ -81,54 +87,79 @@ function useStreamTimeline(events, isDone) {
|
|
|
81
87
|
}
|
|
82
88
|
case "node-complete": {
|
|
83
89
|
const nodeId = d.nodeId;
|
|
90
|
+
if (completedNodes.has(nodeId)) break;
|
|
84
91
|
const startTs = nodeStarts.get(nodeId);
|
|
85
92
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
|
|
86
93
|
if (nodeId) nodeStarts.delete(nodeId);
|
|
87
|
-
|
|
88
|
-
const completed = {
|
|
89
|
-
id: `s-${idCounter++}`,
|
|
90
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
91
|
-
type: "node-completed",
|
|
92
|
-
nodeId,
|
|
93
|
-
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
94
|
-
duration,
|
|
95
|
-
color: d.color,
|
|
96
|
-
icon: d.icon,
|
|
97
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
98
|
-
};
|
|
94
|
+
completedNodes.add(nodeId);
|
|
99
95
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
100
96
|
if (existingIdx != null && entries[existingIdx]) {
|
|
101
|
-
|
|
97
|
+
const rawOutputs = d.outputs;
|
|
98
|
+
entries[existingIdx] = {
|
|
99
|
+
id: entries[existingIdx].id,
|
|
100
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
101
|
+
type: "node-completed",
|
|
102
|
+
nodeId,
|
|
103
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
104
|
+
duration,
|
|
105
|
+
color: d.color,
|
|
106
|
+
icon: d.icon,
|
|
107
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
108
|
+
};
|
|
102
109
|
nodeEntryIndex.delete(nodeId);
|
|
103
110
|
} else {
|
|
104
|
-
|
|
111
|
+
const rawOutputs = d.outputs;
|
|
112
|
+
entries.push({
|
|
113
|
+
id: `s-${idCounter++}`,
|
|
114
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
115
|
+
type: "node-completed",
|
|
116
|
+
nodeId,
|
|
117
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
118
|
+
duration,
|
|
119
|
+
color: d.color,
|
|
120
|
+
icon: d.icon,
|
|
121
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
122
|
+
});
|
|
105
123
|
}
|
|
106
124
|
break;
|
|
107
125
|
}
|
|
108
126
|
case "node-error": {
|
|
109
127
|
const nodeId = d.nodeId;
|
|
128
|
+
if (completedNodes.has(nodeId)) break;
|
|
110
129
|
const startTs = nodeStarts.get(nodeId);
|
|
111
130
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : void 0);
|
|
112
131
|
if (nodeId) nodeStarts.delete(nodeId);
|
|
113
|
-
|
|
114
|
-
const failed = {
|
|
115
|
-
id: `s-${idCounter++}`,
|
|
116
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
117
|
-
type: "node-failed",
|
|
118
|
-
nodeId,
|
|
119
|
-
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
120
|
-
detail: d.error,
|
|
121
|
-
duration,
|
|
122
|
-
color: d.color,
|
|
123
|
-
icon: d.icon,
|
|
124
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
125
|
-
};
|
|
132
|
+
completedNodes.add(nodeId);
|
|
126
133
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
127
134
|
if (existingIdx != null && entries[existingIdx]) {
|
|
128
|
-
|
|
135
|
+
const rawOutputs = d.outputs;
|
|
136
|
+
entries[existingIdx] = {
|
|
137
|
+
id: entries[existingIdx].id,
|
|
138
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
139
|
+
type: "node-failed",
|
|
140
|
+
nodeId,
|
|
141
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
142
|
+
detail: d.error,
|
|
143
|
+
duration,
|
|
144
|
+
color: d.color,
|
|
145
|
+
icon: d.icon,
|
|
146
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
147
|
+
};
|
|
129
148
|
nodeEntryIndex.delete(nodeId);
|
|
130
149
|
} else {
|
|
131
|
-
|
|
150
|
+
const rawOutputs = d.outputs;
|
|
151
|
+
entries.push({
|
|
152
|
+
id: `s-${idCounter++}`,
|
|
153
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
154
|
+
type: "node-failed",
|
|
155
|
+
nodeId,
|
|
156
|
+
label: d.label ?? d.nodeType ?? nodeId ?? "Node",
|
|
157
|
+
detail: d.error,
|
|
158
|
+
duration,
|
|
159
|
+
color: d.color,
|
|
160
|
+
icon: d.icon,
|
|
161
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : void 0
|
|
162
|
+
});
|
|
132
163
|
}
|
|
133
164
|
break;
|
|
134
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use-stream-timeline.d.ts","sourceRoot":"","sources":["../../src/ui/use-stream-timeline.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,cAAc,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,GAAG,aAAa,GAAG,gBAAgB,GAAG,aAAa,CAAC;IAC9I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;IAClE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACtE,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,mBAAmB,
|
|
1
|
+
{"version":3,"file":"use-stream-timeline.d.ts","sourceRoot":"","sources":["../../src/ui/use-stream-timeline.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,IAAI,CAAC;IAChB,IAAI,EAAE,cAAc,GAAG,cAAc,GAAG,gBAAgB,GAAG,aAAa,GAAG,eAAe,GAAG,aAAa,GAAG,gBAAgB,GAAG,aAAa,CAAC;IAC9I,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;IAClE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE;QACJ,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,KAAK,CAAC;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACtE,GAAG,IAAI,CAAC;IACT,gBAAgB,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,WAAW,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,mBAAmB,CAqP7F"}
|
|
@@ -38,6 +38,9 @@ export function useStreamTimeline(events, isDone) {
|
|
|
38
38
|
// Map nodeId → index in entries[] so we can replace start with complete
|
|
39
39
|
const nodeEntryIndex = new Map();
|
|
40
40
|
const nodeStarts = new Map();
|
|
41
|
+
// Track completed/failed nodes to skip duplicate completion events
|
|
42
|
+
// (FW runtime emits 2x STATUS_CHANGED per node transition)
|
|
43
|
+
const completedNodes = new Set();
|
|
41
44
|
let idCounter = 0;
|
|
42
45
|
for (const event of events) {
|
|
43
46
|
const d = event.data ?? {};
|
|
@@ -55,6 +58,14 @@ export function useStreamTimeline(events, isDone) {
|
|
|
55
58
|
const nodeId = d.nodeId;
|
|
56
59
|
if (nodeId)
|
|
57
60
|
nodeStarts.set(nodeId, event.timestamp);
|
|
61
|
+
// If we already have an in-flight entry for this node (duplicate
|
|
62
|
+
// STATUS_CHANGED from the FW runtime), skip the duplicate.
|
|
63
|
+
const existingStartIdx = nodeEntryIndex.get(nodeId);
|
|
64
|
+
if (existingStartIdx != null && entries[existingStartIdx]?.type === 'node-started') {
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
// Clear completed tracking so retry loops work (node can re-enter)
|
|
68
|
+
completedNodes.delete(nodeId);
|
|
58
69
|
const idx = entries.length;
|
|
59
70
|
nodeEntryIndex.set(nodeId, idx);
|
|
60
71
|
entries.push({
|
|
@@ -70,60 +81,89 @@ export function useStreamTimeline(events, isDone) {
|
|
|
70
81
|
}
|
|
71
82
|
case 'node-complete': {
|
|
72
83
|
const nodeId = d.nodeId;
|
|
84
|
+
// Skip duplicate completion events for the same node
|
|
85
|
+
if (completedNodes.has(nodeId))
|
|
86
|
+
break;
|
|
73
87
|
const startTs = nodeStarts.get(nodeId);
|
|
74
88
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : undefined);
|
|
75
89
|
if (nodeId)
|
|
76
90
|
nodeStarts.delete(nodeId);
|
|
77
|
-
|
|
78
|
-
const completed = {
|
|
79
|
-
id: `s-${idCounter++}`,
|
|
80
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
81
|
-
type: 'node-completed',
|
|
82
|
-
nodeId,
|
|
83
|
-
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
84
|
-
duration,
|
|
85
|
-
color: d.color,
|
|
86
|
-
icon: d.icon,
|
|
87
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
88
|
-
};
|
|
91
|
+
completedNodes.add(nodeId);
|
|
89
92
|
// Replace the node-started entry in-place
|
|
90
93
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
91
94
|
if (existingIdx != null && entries[existingIdx]) {
|
|
92
|
-
|
|
95
|
+
const rawOutputs = d.outputs;
|
|
96
|
+
entries[existingIdx] = {
|
|
97
|
+
id: entries[existingIdx].id,
|
|
98
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
99
|
+
type: 'node-completed',
|
|
100
|
+
nodeId,
|
|
101
|
+
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
102
|
+
duration,
|
|
103
|
+
color: d.color,
|
|
104
|
+
icon: d.icon,
|
|
105
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
106
|
+
};
|
|
93
107
|
nodeEntryIndex.delete(nodeId);
|
|
94
108
|
}
|
|
95
109
|
else {
|
|
96
|
-
|
|
110
|
+
// No matching start entry — standalone complete (e.g. Start node)
|
|
111
|
+
const rawOutputs = d.outputs;
|
|
112
|
+
entries.push({
|
|
113
|
+
id: `s-${idCounter++}`,
|
|
114
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
115
|
+
type: 'node-completed',
|
|
116
|
+
nodeId,
|
|
117
|
+
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
118
|
+
duration,
|
|
119
|
+
color: d.color,
|
|
120
|
+
icon: d.icon,
|
|
121
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
122
|
+
});
|
|
97
123
|
}
|
|
98
124
|
break;
|
|
99
125
|
}
|
|
100
126
|
case 'node-error': {
|
|
101
127
|
const nodeId = d.nodeId;
|
|
128
|
+
if (completedNodes.has(nodeId))
|
|
129
|
+
break; // skip duplicate
|
|
102
130
|
const startTs = nodeStarts.get(nodeId);
|
|
103
131
|
const duration = d.durationMs ?? (startTs ? event.timestamp - startTs : undefined);
|
|
104
132
|
if (nodeId)
|
|
105
133
|
nodeStarts.delete(nodeId);
|
|
106
|
-
|
|
107
|
-
const failed = {
|
|
108
|
-
id: `s-${idCounter++}`,
|
|
109
|
-
timestamp: new Date(startTs ?? event.timestamp),
|
|
110
|
-
type: 'node-failed',
|
|
111
|
-
nodeId,
|
|
112
|
-
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
113
|
-
detail: d.error,
|
|
114
|
-
duration,
|
|
115
|
-
color: d.color,
|
|
116
|
-
icon: d.icon,
|
|
117
|
-
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
118
|
-
};
|
|
134
|
+
completedNodes.add(nodeId);
|
|
119
135
|
// Replace the node-started entry in-place
|
|
120
136
|
const existingIdx = nodeEntryIndex.get(nodeId);
|
|
121
137
|
if (existingIdx != null && entries[existingIdx]) {
|
|
122
|
-
|
|
138
|
+
const rawOutputs = d.outputs;
|
|
139
|
+
entries[existingIdx] = {
|
|
140
|
+
id: entries[existingIdx].id,
|
|
141
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
142
|
+
type: 'node-failed',
|
|
143
|
+
nodeId,
|
|
144
|
+
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
145
|
+
detail: d.error,
|
|
146
|
+
duration,
|
|
147
|
+
color: d.color,
|
|
148
|
+
icon: d.icon,
|
|
149
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
150
|
+
};
|
|
123
151
|
nodeEntryIndex.delete(nodeId);
|
|
124
152
|
}
|
|
125
153
|
else {
|
|
126
|
-
|
|
154
|
+
const rawOutputs = d.outputs;
|
|
155
|
+
entries.push({
|
|
156
|
+
id: `s-${idCounter++}`,
|
|
157
|
+
timestamp: new Date(startTs ?? event.timestamp),
|
|
158
|
+
type: 'node-failed',
|
|
159
|
+
nodeId,
|
|
160
|
+
label: d.label ?? d.nodeType ?? nodeId ?? 'Node',
|
|
161
|
+
detail: d.error,
|
|
162
|
+
duration,
|
|
163
|
+
color: d.color,
|
|
164
|
+
icon: d.icon,
|
|
165
|
+
outputs: rawOutputs && rawOutputs.length > 0 ? rawOutputs : undefined,
|
|
166
|
+
});
|
|
127
167
|
}
|
|
128
168
|
break;
|
|
129
169
|
}
|