claude-flow 3.6.27 → 3.6.29

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.
@@ -7,6 +7,17 @@ description: Implementation specialist for writing clean, efficient code
7
7
 
8
8
  You are a senior software engineer specialized in writing clean, maintainable, and efficient code following best practices and design patterns.
9
9
 
10
+ ## Authoritative project documents — read before implementing
11
+
12
+ Before writing code that affects architecture, scope, or behavior, read **both**:
13
+
14
+ 1. **`docs/SPEC.md`** (and any sibling files under `docs/`) — describes **what** the system should do. Functional requirements, scope, acceptance criteria.
15
+ 2. **`docs/adr/*.md`** (Architecture Decision Records) — describes **how** decisions have been made. Tech stack choices, framework selection, auth strategy, integration patterns. Treat these as **binding** unless explicitly superseded by a newer ADR with `status: Accepted`.
16
+
17
+ If both exist and conflict, the ADR wins on architectural decisions; SPEC wins on requirements scope. If an ADR contradicts your planned implementation, surface the conflict and propose either following the ADR or drafting a successor ADR — do not silently diverge.
18
+
19
+ When neither file exists (greenfield work), you can proceed without — but if a sibling Architect agent generated ADRs in this session, those ADRs are authoritative for your work even before they land in `docs/adr/`. In multi-agent parallel development, ADRs are the contract that prevents drift between agents working on different bounded contexts.
20
+
10
21
  ## Core Responsibilities
11
22
 
12
23
  1. **Code Implementation**: Write production-quality code that meets requirements
@@ -8,6 +8,18 @@ description: Transform specifications into working code with TDD practices
8
8
  ## Purpose
9
9
  This agent specializes in the implementation phases of SPARC methodology, focusing on transforming specifications and designs into high-quality, tested code.
10
10
 
11
+ ## Authoritative inputs
12
+
13
+ The Refinement and Completion phases consume work from earlier SPARC phases. Read **all** of the following before implementing:
14
+
15
+ 1. **`docs/SPEC.md`** — Specification phase output (what to build)
16
+ 2. **`docs/pseudocode/*.md`** if present — Pseudocode phase output (algorithm shape)
17
+ 3. **`docs/adr/*.md`** — Architecture Decision Records from the Architecture phase (tech stack, framework choices, auth strategy, deployment shape). **Treat ADRs as binding** unless explicitly superseded by a newer `status: Accepted` ADR.
18
+
19
+ ADRs describe **how** decisions were made; SPEC describes **what** the system does. In multi-agent parallel implementation, ADRs are the cross-agent contract — backend coders, frontend coders, and testers must all read the same ADRs or the bounded contexts will drift apart.
20
+
21
+ If your planned implementation contradicts an ADR, surface the conflict and propose either following the ADR or drafting a successor — do not silently diverge.
22
+
11
23
  ## Core Implementation Principles
12
24
 
13
25
  ### 1. Test-Driven Development (TDD)
@@ -0,0 +1 @@
1
+ {"sessionId":"1dba3b8c-1f1f-4718-bab4-9ffba5f49578","pid":20633,"procStart":"Mon May 4 17:00:40 2026","acquiredAt":1777938041240}
package/README.md CHANGED
@@ -43,9 +43,17 @@ User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Prov
43
43
 
44
44
  ## Quick Start
45
45
 
46
- ### Claude Code Plugin (Recommended)
46
+ There are **two different install paths** with very different surface areas. Pick based on what you need (#1744):
47
47
 
48
- Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, and MCP tools directly:
48
+ | | **Claude Code Plugin** | **CLI install (`npx ruflo init`)** |
49
+ |---|---|---|
50
+ | What it gives you | Slash commands + a few skills + agent definitions per-plugin | Full Ruflo loop — 98 agents, 60+ commands, 30 skills, MCP server, hooks, daemon |
51
+ | Files in your workspace | **Zero** | `.claude/`, `.claude-flow/`, `CLAUDE.md`, helpers, settings |
52
+ | MCP server registered | **No** (`memory_store`, `swarm_init`, etc. unavailable to Claude) | Yes |
53
+ | Hooks installed | No | Yes |
54
+ | Best for | Try a single plugin's commands without committing to the full install | Production use — everything works as documented |
55
+
56
+ ### Path A — Claude Code Plugins (lite, slash commands only)
49
57
 
50
58
  ```bash
51
59
  # Add the marketplace
@@ -58,6 +66,8 @@ Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, a
58
66
  /plugin install ruflo-federation@ruflo
59
67
  ```
60
68
 
69
+ This adds slash commands and agent definitions only. The Ruflo MCP server is NOT registered, so `memory_store`, `swarm_init`, `agent_spawn`, etc. won't be callable from Claude. For the full loop, use Path B below.
70
+
61
71
  <details>
62
72
  <summary><strong>All 32 plugins</strong></summary>
63
73
 
@@ -146,8 +156,11 @@ Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, a
146
156
  # One-line install
147
157
  curl -fsSL https://cdn.jsdelivr.net/gh/ruvnet/ruflo@main/scripts/install.sh | bash
148
158
 
149
- # Or via npx
150
- npx ruflo@latest init --wizard
159
+ # Or via npx (interactive setup)
160
+ npx ruflo@latest init wizard
161
+
162
+ # Quick non-interactive init
163
+ # npx ruflo@latest init
151
164
 
152
165
  # Or install globally
153
166
  npm install -g ruflo@latest
@@ -156,8 +169,8 @@ npm install -g ruflo@latest
156
169
  ### MCP Server
157
170
 
158
171
  ```bash
159
- # Add Ruflo as an MCP server in Claude Code
160
- claude mcp add ruflo -- npx -y @claude-flow/cli@latest
172
+ # Add Ruflo as an MCP server in Claude Code (canonical form, matches USERGUIDE.md)
173
+ claude mcp add ruflo -- npx ruflo@latest mcp start
161
174
  ```
162
175
 
163
176
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-flow",
3
- "version": "3.6.27",
3
+ "version": "3.6.29",
4
4
  "description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -43,9 +43,17 @@ User --> Ruflo (CLI/MCP) --> Router --> Swarm --> Agents --> Memory --> LLM Prov
43
43
 
44
44
  ## Quick Start
45
45
 
46
- ### Claude Code Plugin (Recommended)
46
+ There are **two different install paths** with very different surface areas. Pick based on what you need (#1744):
47
47
 
48
- Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, and MCP tools directly:
48
+ | | **Claude Code Plugin** | **CLI install (`npx ruflo init`)** |
49
+ |---|---|---|
50
+ | What it gives you | Slash commands + a few skills + agent definitions per-plugin | Full Ruflo loop — 98 agents, 60+ commands, 30 skills, MCP server, hooks, daemon |
51
+ | Files in your workspace | **Zero** | `.claude/`, `.claude-flow/`, `CLAUDE.md`, helpers, settings |
52
+ | MCP server registered | **No** (`memory_store`, `swarm_init`, etc. unavailable to Claude) | Yes |
53
+ | Hooks installed | No | Yes |
54
+ | Best for | Try a single plugin's commands without committing to the full install | Production use — everything works as documented |
55
+
56
+ ### Path A — Claude Code Plugins (lite, slash commands only)
49
57
 
50
58
  ```bash
51
59
  # Add the marketplace
@@ -58,6 +66,8 @@ Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, a
58
66
  /plugin install ruflo-federation@ruflo
59
67
  ```
60
68
 
69
+ This adds slash commands and agent definitions only. The Ruflo MCP server is NOT registered, so `memory_store`, `swarm_init`, `agent_spawn`, etc. won't be callable from Claude. For the full loop, use Path B below.
70
+
61
71
  <details>
62
72
  <summary><strong>All 32 plugins</strong></summary>
63
73
 
@@ -146,8 +156,11 @@ Install Ruflo as a native Claude Code plugin -- adds skills, commands, agents, a
146
156
  # One-line install
147
157
  curl -fsSL https://cdn.jsdelivr.net/gh/ruvnet/ruflo@main/scripts/install.sh | bash
148
158
 
149
- # Or via npx
150
- npx ruflo@latest init --wizard
159
+ # Or via npx (interactive setup)
160
+ npx ruflo@latest init wizard
161
+
162
+ # Quick non-interactive init
163
+ # npx ruflo@latest init
151
164
 
152
165
  # Or install globally
153
166
  npm install -g ruflo@latest
@@ -156,8 +169,8 @@ npm install -g ruflo@latest
156
169
  ### MCP Server
157
170
 
158
171
  ```bash
159
- # Add Ruflo as an MCP server in Claude Code
160
- claude mcp add ruflo -- npx -y @claude-flow/cli@latest
172
+ # Add Ruflo as an MCP server in Claude Code (canonical form, matches USERGUIDE.md)
173
+ claude mcp add ruflo -- npx ruflo@latest mcp start
161
174
  ```
162
175
 
163
176
  ---
@@ -10,6 +10,7 @@ import { select, confirm, input } from '../prompt.js';
10
10
  import { callMCPTool, MCPClientError } from '../mcp-client.js';
11
11
  import { spawn as childSpawn, execSync } from 'child_process';
12
12
  import { mkdir, writeFile } from 'fs/promises';
13
+ import { existsSync } from 'fs';
13
14
  import { join } from 'path';
14
15
  // Hive topologies
15
16
  const TOPOLOGIES = [
@@ -200,6 +201,40 @@ async function spawnClaudeCodeInstance(swarmId, swarmName, objective, workers, f
200
201
  if (claudeAvailable && !dryRun) {
201
202
  // Build arguments - flags first, then prompt
202
203
  const claudeArgs = [];
204
+ // #1748 Issue 2 — pass --mcp-config so the spawned worker actually has
205
+ // mcp__ruflo__* tools registered. Before this, the coordination prompt
206
+ // referenced tools the worker didn't know about and exited silently.
207
+ // Resolution order:
208
+ // 1. explicit --mcp-config <path> flag passed by the caller
209
+ // 2. ./.mcp.json in cwd (project-local Ruflo MCP config)
210
+ // 3. ~/.claude.json or ~/.claude/mcp.json (user-global)
211
+ // If none found, we still spawn but warn — that's the pre-fix behavior
212
+ // and the user's debug log will surface the missing tools.
213
+ const explicitMcpConfig = flags['mcp-config'];
214
+ let mcpConfigPath = explicitMcpConfig;
215
+ if (!mcpConfigPath) {
216
+ const candidates = [
217
+ join(process.cwd(), '.mcp.json'),
218
+ join(process.env.HOME || process.env.USERPROFILE || '', '.claude.json'),
219
+ join(process.env.HOME || process.env.USERPROFILE || '', '.claude', 'mcp.json'),
220
+ ];
221
+ for (const c of candidates) {
222
+ try {
223
+ if (c && existsSync(c)) {
224
+ mcpConfigPath = c;
225
+ break;
226
+ }
227
+ }
228
+ catch { /* continue */ }
229
+ }
230
+ }
231
+ if (mcpConfigPath) {
232
+ claudeArgs.push('--mcp-config', mcpConfigPath);
233
+ output.printInfo(`Spawned worker MCP config: ${mcpConfigPath}`);
234
+ }
235
+ else {
236
+ output.printWarning('No .mcp.json or ~/.claude.json found — spawned worker will not have mcp__ruflo__* tools (#1748 Issue 2). Pass --mcp-config <path> or run "ruflo init" to generate one.');
237
+ }
203
238
  // Check for non-interactive mode
204
239
  const isNonInteractive = flags['non-interactive'] || flags.nonInteractive;
205
240
  if (isNonInteractive) {
@@ -492,6 +527,11 @@ const spawnCommand = {
492
527
  description: 'Run Claude Code in non-interactive mode',
493
528
  type: 'boolean',
494
529
  default: false
530
+ },
531
+ {
532
+ name: 'mcp-config',
533
+ description: 'Path to .mcp.json for the spawned worker (auto-detects ./.mcp.json or ~/.claude.json if omitted) — fixes #1748 Issue 2',
534
+ type: 'string'
495
535
  }
496
536
  ],
497
537
  examples: [
@@ -145,6 +145,7 @@ const initAction = async (ctx) => {
145
145
  const full = ctx.flags.full;
146
146
  const skipClaude = ctx.flags['skip-claude'];
147
147
  const onlyClaude = ctx.flags['only-claude'];
148
+ const noGlobal = ctx.flags['no-global'];
148
149
  const codexMode = ctx.flags.codex;
149
150
  const dualMode = ctx.flags.dual;
150
151
  const cwd = ctx.cwd;
@@ -203,6 +204,12 @@ const initAction = async (ctx) => {
203
204
  if (onlyClaude) {
204
205
  options.components.runtime = false;
205
206
  }
207
+ // #1744 — opt-out of the user-global ~/.claude/CLAUDE.md "Ruflo Integration"
208
+ // pointer block. Default behavior (off) preserves current install for users
209
+ // who rely on it; opting in via --no-global keeps the global file pristine.
210
+ if (noGlobal) {
211
+ options.skipGlobalClaudeMd = true;
212
+ }
206
213
  // Create spinner
207
214
  const spinner = output.createSpinner({ text: 'Initializing...' });
208
215
  spinner.start();
@@ -919,6 +926,12 @@ export const initCommand = {
919
926
  type: 'boolean',
920
927
  default: false,
921
928
  },
929
+ {
930
+ name: 'no-global',
931
+ description: 'Skip the ~/.claude/CLAUDE.md "Ruflo Integration" pointer block (#1744)',
932
+ type: 'boolean',
933
+ default: false,
934
+ },
922
935
  {
923
936
  name: 'start-all',
924
937
  description: 'Auto-start daemon, memory, and swarm after init',
@@ -498,7 +498,7 @@ const optimizeCommand = {
498
498
  data: [
499
499
  { priority: output.error('P0'), area: 'Memory', recommendation: 'Enable HNSW index quantization', impact: '+50% reduction' },
500
500
  { priority: output.warning('P1'), area: 'CPU', recommendation: 'Enable WASM SIMD acceleration', impact: '+4x speedup' },
501
- { priority: output.warning('P1'), area: 'Latency', recommendation: 'Enable Flash Attention', impact: '+2.49x speedup' },
501
+ { priority: output.warning('P1'), area: 'Latency', recommendation: 'Flash Attention WASM (in progress, currently JS reference)', impact: '+2.49x target' },
502
502
  { priority: output.info('P2'), area: 'Cache', recommendation: 'Increase pattern cache size', impact: '+15% hit rate' },
503
503
  { priority: output.info('P2'), area: 'Network', recommendation: 'Enable request batching', impact: '-30% latency' },
504
504
  ],
@@ -536,7 +536,7 @@ const bottleneckCommand = {
536
536
  ],
537
537
  data: [
538
538
  { component: 'Vector Search', bottleneck: 'Linear scan O(n)', severity: output.error('High'), solution: 'Enable HNSW indexing' },
539
- { component: 'Neural Inference', bottleneck: 'Sequential attention', severity: output.warning('Medium'), solution: 'Enable Flash Attention' },
539
+ { component: 'Neural Inference', bottleneck: 'Sequential attention', severity: output.warning('Medium'), solution: 'Flash Attention WASM (in progress)' },
540
540
  { component: 'Memory Store', bottleneck: 'Lock contention', severity: output.info('Low'), solution: 'Use sharded storage' },
541
541
  ],
542
542
  });
@@ -571,7 +571,7 @@ export const performanceCommand = {
571
571
  output.writeln('Performance Targets:');
572
572
  output.printList([
573
573
  'HNSW Search: 150x-12,500x faster than brute force',
574
- 'Flash Attention: 2.49x-7.47x speedup',
574
+ 'Flash Attention: 2.49x-7.47x target (in progress; ships JS reference impl)',
575
575
  'Memory: 50-75% reduction with quantization',
576
576
  ]);
577
577
  output.writeln();
@@ -1662,9 +1662,11 @@ async function writeClaudeMd(targetDir, options, result) {
1662
1662
  fs.writeFileSync(claudeMdPath, content, 'utf-8');
1663
1663
  result.created.files.push('CLAUDE.md');
1664
1664
  }
1665
- // Also write/append global ~/.claude/CLAUDE.md so ruflo tools are used automatically (#1497)
1665
+ // Also write/append global ~/.claude/CLAUDE.md so ruflo tools are used automatically (#1497).
1666
+ // Opt-out via --no-global / options.skipGlobalClaudeMd (#1744 — keeps global rules file pristine
1667
+ // for users who don't want a per-machine pointer block).
1666
1668
  const homeDir = process.env.HOME || process.env.USERPROFILE || '';
1667
- if (homeDir) {
1669
+ if (homeDir && !options.skipGlobalClaudeMd) {
1668
1670
  const globalClaudeDir = path.join(homeDir, '.claude');
1669
1671
  const globalClaudeMd = path.join(globalClaudeDir, 'CLAUDE.md');
1670
1672
  const rufloBlock = [
@@ -1695,6 +1697,9 @@ async function writeClaudeMd(targetDir, options, result) {
1695
1697
  // Non-critical — global CLAUDE.md is best-effort
1696
1698
  }
1697
1699
  }
1700
+ else if (options.skipGlobalClaudeMd) {
1701
+ result.skipped.push('~/.claude/CLAUDE.md (--no-global)');
1702
+ }
1698
1703
  }
1699
1704
  /**
1700
1705
  * Find source directory for skills/commands/agents
@@ -8,8 +8,13 @@ import { detectPlatform } from './types.js';
8
8
  */
9
9
  export function generateSettings(options) {
10
10
  const settings = {};
11
- // Add hooks if enabled
12
- if (options.components.settings) {
11
+ // Add hooks if enabled. CRITICAL (#1744 #3): only emit the hooks block when
12
+ // the helpers directory will also be bundled. The hook commands point at
13
+ // .claude/helpers/hook-handler.cjs; if that file isn't created (as in
14
+ // --minimal where components.helpers=false), every hook fires and silently
15
+ // fails to find its handler. Either bundle the helpers OR drop the hooks —
16
+ // the option this fix takes is the latter (minimal stays minimal).
17
+ if (options.components.settings && options.components.helpers) {
13
18
  settings.hooks = generateHooksConfig(options.hooks);
14
19
  }
15
20
  // Add statusLine configuration if enabled
@@ -260,6 +260,12 @@ export interface InitOptions {
260
260
  runtime: RuntimeConfig;
261
261
  /** Embeddings configuration */
262
262
  embeddings: EmbeddingsConfig;
263
+ /**
264
+ * Skip the user-global ~/.claude/CLAUDE.md "Ruflo Integration" pointer block.
265
+ * Defaults to false (current behavior — block is appended once, idempotent).
266
+ * Set true via --no-global to keep the global Claude rules file pristine (#1744).
267
+ */
268
+ skipGlobalClaudeMd?: boolean;
263
269
  }
264
270
  /**
265
271
  * Default init options - full V3 setup
@@ -32,6 +32,7 @@ import { githubTools } from './mcp-tools/github-tools.js';
32
32
  import { daaTools } from './mcp-tools/daa-tools.js';
33
33
  import { coordinationTools } from './mcp-tools/coordination-tools.js';
34
34
  import { browserTools } from './mcp-tools/browser-tools.js';
35
+ import { browserSessionTools } from './mcp-tools/browser-session-tools.js';
35
36
  import { execFileSync } from 'node:child_process';
36
37
  // Phase 6: AgentDB v3 controller tools
37
38
  import { agentdbTools } from './mcp-tools/agentdb-tools.js';
@@ -54,6 +55,15 @@ function getBrowserTools() {
54
55
  }
55
56
  return _browserAvailable ? browserTools : [];
56
57
  }
58
+ /**
59
+ * Lifecycle MCP tools for ruflo-browser session-as-skill architecture
60
+ * (ADR-0001 ruflo-browser §7). Always registered: their handlers shell out
61
+ * to ruvector + agent-browser + claude-flow memory and degrade gracefully
62
+ * when those CLIs are missing.
63
+ */
64
+ function getBrowserSessionTools() {
65
+ return browserSessionTools;
66
+ }
57
67
  /**
58
68
  * MCP Tool Registry
59
69
  * Maps tool names to their handler functions
@@ -91,6 +101,7 @@ registerTools([
91
101
  ...daaTools,
92
102
  ...coordinationTools,
93
103
  ...getBrowserTools(),
104
+ ...getBrowserSessionTools(),
94
105
  // Phase 6: AgentDB v3 controller tools
95
106
  ...agentdbTools,
96
107
  // RuVector WASM tools
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Browser Session Lifecycle MCP Tools (ADR-0001 ruflo-browser §7).
3
+ *
4
+ * Five lifecycle tools that wrap the 23 raw `browser_*` interaction tools
5
+ * with RVF cognitive containers, ruvector trajectory recording, AgentDB
6
+ * indexing, and AIDefence gates. Implements the contract from
7
+ * `plugins/ruflo-browser/docs/adrs/0001-browser-skills-architecture.md`.
8
+ *
9
+ * Design notes:
10
+ * - These tools orchestrate at the *primitive* level — they shell out to
11
+ * the existing `agent-browser` CLI (for browser actions), `ruvector` CLI
12
+ * (for trajectory hooks + RVF), and the bridged `memory` namespace (for
13
+ * AgentDB index). They do not inline a replay engine; replay
14
+ * enumerates trajectory steps and returns them for the caller to dispatch.
15
+ * - Pinned to ruvector@0.2.25 to match `ruflo-ruvector` ADR-0001.
16
+ * - Best-effort: missing dependencies (no `ruvector`, no `agent-browser`,
17
+ * no AgentDB controller) degrade gracefully with a structured error
18
+ * rather than a process crash.
19
+ */
20
+ import type { MCPTool } from './types.js';
21
+ export declare const browserSessionTools: MCPTool[];
22
+ export default browserSessionTools;
23
+ //# sourceMappingURL=browser-session-tools.d.ts.map
@@ -0,0 +1,313 @@
1
+ /**
2
+ * Browser Session Lifecycle MCP Tools (ADR-0001 ruflo-browser §7).
3
+ *
4
+ * Five lifecycle tools that wrap the 23 raw `browser_*` interaction tools
5
+ * with RVF cognitive containers, ruvector trajectory recording, AgentDB
6
+ * indexing, and AIDefence gates. Implements the contract from
7
+ * `plugins/ruflo-browser/docs/adrs/0001-browser-skills-architecture.md`.
8
+ *
9
+ * Design notes:
10
+ * - These tools orchestrate at the *primitive* level — they shell out to
11
+ * the existing `agent-browser` CLI (for browser actions), `ruvector` CLI
12
+ * (for trajectory hooks + RVF), and the bridged `memory` namespace (for
13
+ * AgentDB index). They do not inline a replay engine; replay
14
+ * enumerates trajectory steps and returns them for the caller to dispatch.
15
+ * - Pinned to ruvector@0.2.25 to match `ruflo-ruvector` ADR-0001.
16
+ * - Best-effort: missing dependencies (no `ruvector`, no `agent-browser`,
17
+ * no AgentDB controller) degrade gracefully with a structured error
18
+ * rather than a process crash.
19
+ */
20
+ import { validateIdentifier, validateText } from './validate-input.js';
21
+ const RUVECTOR_PIN = 'ruvector@0.2.25';
22
+ const RVF_DIR_DEFAULT = '.ruflo/browser-sessions';
23
+ async function shell(cmd, args, opts = {}) {
24
+ const { execFile } = await import('node:child_process');
25
+ const { promisify } = await import('node:util');
26
+ const run = promisify(execFile);
27
+ try {
28
+ const { stdout, stderr } = await run(cmd, args, {
29
+ timeout: opts.timeout ?? 30000,
30
+ encoding: 'utf-8',
31
+ });
32
+ return { success: true, stdout, stderr };
33
+ }
34
+ catch (error) {
35
+ const err = error;
36
+ return {
37
+ success: false,
38
+ error: err.code === 'ENOENT' ? `command not found: ${cmd}` : err.message,
39
+ stdout: err.stdout,
40
+ stderr: err.stderr,
41
+ };
42
+ }
43
+ }
44
+ async function ensureSessionsDir() {
45
+ const { mkdir } = await import('node:fs/promises');
46
+ const path = await import('node:path');
47
+ const dir = path.resolve(process.cwd(), RVF_DIR_DEFAULT);
48
+ await mkdir(dir, { recursive: true });
49
+ return dir;
50
+ }
51
+ function makeSessionId(taskSlug) {
52
+ const stamp = new Date().toISOString().replace(/[-:T]/g, '').slice(0, 14);
53
+ const slug = taskSlug.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 32) || 'session';
54
+ return `${stamp}-${slug}`;
55
+ }
56
+ function ok(payload) {
57
+ return { content: [{ type: 'text', text: JSON.stringify({ success: true, ...payload }, null, 2) }] };
58
+ }
59
+ function fail(error, extra = {}) {
60
+ return {
61
+ content: [{ type: 'text', text: JSON.stringify({ success: false, error, ...extra }, null, 2) }],
62
+ isError: true,
63
+ };
64
+ }
65
+ export const browserSessionTools = [
66
+ // ==========================================================================
67
+ // browser_session_record — open a recorded session
68
+ // ==========================================================================
69
+ {
70
+ name: 'browser_session_record',
71
+ description: 'Open a named, traced browser session: allocate an RVF cognitive container, begin a ruvector trajectory, then open the URL via agent-browser. Returns the session id and rvf path.',
72
+ category: 'browser-session',
73
+ tags: ['session', 'rvf', 'trajectory', 'lifecycle'],
74
+ inputSchema: {
75
+ type: 'object',
76
+ properties: {
77
+ url: { type: 'string', description: 'Target URL to open' },
78
+ task: { type: 'string', description: 'Human-readable task description (recorded in trajectory)' },
79
+ session: { type: 'string', description: 'Optional explicit session id; otherwise auto-generated' },
80
+ rvf_dir: { type: 'string', description: 'Override the default .ruflo/browser-sessions directory' },
81
+ },
82
+ required: ['url', 'task'],
83
+ },
84
+ handler: async (input) => {
85
+ const vUrl = validateText(input.url, 'url');
86
+ if (!vUrl.valid)
87
+ return fail(vUrl.error || 'invalid url');
88
+ const vTask = validateText(input.task, 'task');
89
+ if (!vTask.valid)
90
+ return fail(vTask.error || 'invalid task');
91
+ const path = await import('node:path');
92
+ const explicitSession = input.session;
93
+ if (explicitSession) {
94
+ const v = validateIdentifier(explicitSession, 'session');
95
+ if (!v.valid)
96
+ return fail(v.error || 'invalid session');
97
+ }
98
+ const sessionId = explicitSession ?? makeSessionId(input.task);
99
+ const dir = input.rvf_dir ?? (await ensureSessionsDir());
100
+ const rvfPath = path.join(dir, `${sessionId}.rvf`);
101
+ // 1. RVF allocate
102
+ const rvf = await shell('npx', ['-y', RUVECTOR_PIN, 'rvf', 'create', rvfPath, '--kind', 'browser-session'], { timeout: 60000 });
103
+ if (!rvf.success)
104
+ return fail('rvf create failed', { detail: rvf.error, stderr: rvf.stderr, sessionId, rvfPath });
105
+ // 2. trajectory-begin
106
+ const tb = await shell('npx', ['-y', RUVECTOR_PIN, 'hooks', 'trajectory-begin', '--session-id', sessionId, '--task', input.task]);
107
+ if (!tb.success)
108
+ return fail('trajectory-begin failed', { detail: tb.error, stderr: tb.stderr, sessionId, rvfPath });
109
+ // 3. browser_open via agent-browser
110
+ const bo = await shell('agent-browser', ['--session', sessionId, '--json', 'open', input.url], { timeout: 30000 });
111
+ if (!bo.success) {
112
+ const npxBo = await shell('npx', ['--yes', 'agent-browser', '--session', sessionId, '--json', 'open', input.url], { timeout: 60000 });
113
+ if (!npxBo.success) {
114
+ return fail('browser open failed', { detail: npxBo.error, stderr: npxBo.stderr, sessionId, rvfPath });
115
+ }
116
+ }
117
+ // 4. log the open as the first trajectory step
118
+ await shell('npx', ['-y', RUVECTOR_PIN, 'hooks', 'trajectory-step',
119
+ '--session-id', sessionId,
120
+ '--action', 'browser_open',
121
+ '--args', JSON.stringify({ url: input.url }),
122
+ '--result', 'ok']);
123
+ return ok({
124
+ sessionId,
125
+ rvfPath,
126
+ url: input.url,
127
+ task: input.task,
128
+ ruvectorPin: RUVECTOR_PIN,
129
+ });
130
+ },
131
+ },
132
+ // ==========================================================================
133
+ // browser_session_end — commit a recorded session
134
+ // ==========================================================================
135
+ {
136
+ name: 'browser_session_end',
137
+ description: 'End a recorded browser session: trajectory-end with verdict, rvf compact, AIDefence pre-store gate (best-effort), and AgentDB index in the browser-sessions namespace.',
138
+ category: 'browser-session',
139
+ tags: ['session', 'rvf', 'trajectory', 'lifecycle', 'agentdb'],
140
+ inputSchema: {
141
+ type: 'object',
142
+ properties: {
143
+ session: { type: 'string', description: 'Session id (returned from browser_session_record)' },
144
+ rvf_path: { type: 'string', description: 'Path to the .rvf container' },
145
+ verdict: { type: 'string', enum: ['pass', 'fail', 'partial'], description: 'Outcome verdict' },
146
+ host: { type: 'string', description: 'Host (for namespace key); inferred from manifest if omitted' },
147
+ task: { type: 'string', description: 'Task description (recorded for index)' },
148
+ tags: { type: 'array', items: { type: 'string' }, description: 'Optional tags for AgentDB index' },
149
+ },
150
+ required: ['session', 'rvf_path', 'verdict'],
151
+ },
152
+ handler: async (input) => {
153
+ const vS = validateIdentifier(input.session, 'session');
154
+ if (!vS.valid)
155
+ return fail(vS.error || 'invalid session');
156
+ const verdict = input.verdict;
157
+ if (!['pass', 'fail', 'partial'].includes(verdict))
158
+ return fail(`invalid verdict: ${verdict}`);
159
+ // 1. trajectory-end
160
+ const te = await shell('npx', ['-y', RUVECTOR_PIN, 'hooks', 'trajectory-end',
161
+ '--session-id', input.session,
162
+ '--verdict', verdict]);
163
+ if (!te.success)
164
+ return fail('trajectory-end failed', { detail: te.error, stderr: te.stderr });
165
+ // 2. rvf compact
166
+ const compact = await shell('npx', ['-y', RUVECTOR_PIN, 'rvf', 'compact', input.rvf_path]);
167
+ if (!compact.success)
168
+ return fail('rvf compact failed', { detail: compact.error, stderr: compact.stderr });
169
+ // 3. AgentDB index — best-effort via memory store (claude-flow bridges)
170
+ const indexValue = JSON.stringify({
171
+ rvf_id: input.session,
172
+ rvf_path: input.rvf_path,
173
+ host: input.host ?? null,
174
+ task: input.task ?? null,
175
+ verdict,
176
+ tags: input.tags ?? [],
177
+ ended_at: new Date().toISOString(),
178
+ });
179
+ const idx = await shell('npx', ['-y', '@claude-flow/cli@latest', 'memory', 'store',
180
+ '--namespace', 'browser-sessions',
181
+ '--key', input.session,
182
+ '--value', indexValue], { timeout: 60000 });
183
+ // Index failure is non-fatal — the RVF container is the source of truth.
184
+ return ok({
185
+ sessionId: input.session,
186
+ rvfPath: input.rvf_path,
187
+ verdict,
188
+ indexed: idx.success,
189
+ indexError: idx.success ? undefined : (idx.stderr || idx.error),
190
+ });
191
+ },
192
+ },
193
+ // ==========================================================================
194
+ // browser_session_replay — load a trajectory for caller-level dispatch
195
+ // ==========================================================================
196
+ {
197
+ name: 'browser_session_replay',
198
+ description: 'Load a recorded session trajectory and return its steps so the caller can dispatch them through the 23 browser_* tools. Does NOT itself drive the browser — replay execution is caller-orchestrated to keep this tool a primitive (ADR-0001 §7).',
199
+ category: 'browser-session',
200
+ tags: ['session', 'replay', 'trajectory', 'lifecycle'],
201
+ inputSchema: {
202
+ type: 'object',
203
+ properties: {
204
+ session: { type: 'string', description: 'Source session id to replay' },
205
+ rvf_path: { type: 'string', description: 'Path to source .rvf container' },
206
+ url_override: { type: 'string', description: 'Optional URL to use instead of the original' },
207
+ derive: { type: 'boolean', description: 'Derive a new RVF child container for the replay run (default true)' },
208
+ },
209
+ required: ['session', 'rvf_path'],
210
+ },
211
+ handler: async (input) => {
212
+ const vS = validateIdentifier(input.session, 'session');
213
+ if (!vS.valid)
214
+ return fail(vS.error || 'invalid session');
215
+ // 1. Verify RVF container exists
216
+ const status = await shell('npx', ['-y', RUVECTOR_PIN, 'rvf', 'status', input.rvf_path]);
217
+ if (!status.success)
218
+ return fail('rvf status failed', { detail: status.error, stderr: status.stderr });
219
+ // 2. Derive child container if requested
220
+ let replayId = null;
221
+ let replayPath = null;
222
+ const derive = input.derive !== false;
223
+ if (derive) {
224
+ const path = await import('node:path');
225
+ const dir = path.dirname(input.rvf_path);
226
+ replayId = `${input.session}-replay-${Date.now()}`;
227
+ replayPath = path.join(dir, `${replayId}.rvf`);
228
+ const dr = await shell('npx', ['-y', RUVECTOR_PIN, 'rvf', 'derive', input.rvf_path, replayPath]);
229
+ if (!dr.success)
230
+ return fail('rvf derive failed', { detail: dr.error, stderr: dr.stderr });
231
+ }
232
+ // 3. Surface the trajectory steps from the segments listing — the caller is
233
+ // expected to read trajectory.ndjson from the RVF container and dispatch.
234
+ const segments = await shell('npx', ['-y', RUVECTOR_PIN, 'rvf', 'segments', input.rvf_path]);
235
+ return ok({
236
+ sourceSession: input.session,
237
+ sourceRvfPath: input.rvf_path,
238
+ replaySession: replayId,
239
+ replayRvfPath: replayPath,
240
+ urlOverride: input.url_override ?? null,
241
+ rvfStatus: status.stdout?.slice(0, 4000) ?? null,
242
+ rvfSegments: segments.stdout?.slice(0, 4000) ?? null,
243
+ nextStep: 'Caller MUST: (a) read trajectory.ndjson from the source RVF container, (b) for each step, dispatch the matching browser_* MCP tool, (c) on selector miss, query browser-selectors AgentDB namespace and retry, (d) call browser_session_end with verdict aggregate.',
244
+ });
245
+ },
246
+ },
247
+ // ==========================================================================
248
+ // browser_template_apply — fetch a stored template
249
+ // ==========================================================================
250
+ {
251
+ name: 'browser_template_apply',
252
+ description: 'Fetch a recipe from the browser-templates AgentDB namespace and return it for caller-level execution.',
253
+ category: 'browser-session',
254
+ tags: ['template', 'agentdb', 'extract'],
255
+ inputSchema: {
256
+ type: 'object',
257
+ properties: {
258
+ name: { type: 'string', description: 'Template name (key in browser-templates namespace)' },
259
+ },
260
+ required: ['name'],
261
+ },
262
+ handler: async (input) => {
263
+ const vN = validateText(input.name, 'name');
264
+ if (!vN.valid)
265
+ return fail(vN.error || 'invalid name');
266
+ const r = await shell('npx', ['-y', '@claude-flow/cli@latest', 'memory', 'retrieve',
267
+ '--namespace', 'browser-templates',
268
+ '--key', input.name], { timeout: 60000 });
269
+ if (!r.success)
270
+ return fail('template fetch failed', { detail: r.error, stderr: r.stderr });
271
+ return ok({
272
+ templateName: input.name,
273
+ recipe: r.stdout,
274
+ nextStep: 'Caller dispatches the recipe via browser_* tools; persist updated selectors to browser-selectors on success.',
275
+ });
276
+ },
277
+ },
278
+ // ==========================================================================
279
+ // browser_cookie_use — fetch a vaulted cookie handle
280
+ // ==========================================================================
281
+ {
282
+ name: 'browser_cookie_use',
283
+ description: 'Fetch a vault handle for a host from the browser-cookies AgentDB namespace. Raw cookie values are NEVER returned — only the opaque handle plus expiry / AIDefence verdict.',
284
+ category: 'browser-session',
285
+ tags: ['cookie', 'agentdb', 'aidefence', 'auth'],
286
+ inputSchema: {
287
+ type: 'object',
288
+ properties: {
289
+ host: { type: 'string', description: 'Host (e.g. "example.com") to look up' },
290
+ },
291
+ required: ['host'],
292
+ },
293
+ handler: async (input) => {
294
+ const vH = validateText(input.host, 'host');
295
+ if (!vH.valid)
296
+ return fail(vH.error || 'invalid host');
297
+ const r = await shell('npx', ['-y', '@claude-flow/cli@latest', 'memory', 'retrieve',
298
+ '--namespace', 'browser-cookies',
299
+ '--key', input.host], { timeout: 60000 });
300
+ if (!r.success)
301
+ return fail('cookie lookup failed', { detail: r.error, stderr: r.stderr });
302
+ // The contract: the value blob includes a vault_handle, expiry, aidefence_verdict.
303
+ // Raw values do not enter this namespace (browser-login is responsible).
304
+ return ok({
305
+ host: input.host,
306
+ vault: r.stdout,
307
+ nextStep: 'Caller mounts the handle via the browser runner; the raw cookie is materialized only inside the browser process, never returned to the model.',
308
+ });
309
+ },
310
+ },
311
+ ];
312
+ export default browserSessionTools;
313
+ //# sourceMappingURL=browser-session-tools.js.map
@@ -63,25 +63,11 @@ try {
63
63
  }
64
64
  }
65
65
  }
66
- // Tier 4: mock fallback (last resort embeddings are not semantic)
67
- if (!realEmbeddings) {
68
- const embeddingsModule = await import('@claude-flow/embeddings').catch(() => null);
69
- if (embeddingsModule?.createEmbeddingService) {
70
- try {
71
- const service = embeddingsModule.createEmbeddingService({ provider: 'mock' });
72
- realEmbeddings = {
73
- embed: async (text) => {
74
- const result = await service.embed(text);
75
- return Array.from(result.embedding);
76
- },
77
- };
78
- embeddingServiceName = 'mock-fallback';
79
- }
80
- catch {
81
- // No embedding service available at all
82
- }
83
- }
84
- }
66
+ // No Tier 4 mock fallback. If Tier 1 (agentic-flow) and Tier 3 (onnx)
67
+ // both failed to import, leave realEmbeddings null and let downstream
68
+ // code use the explicit hash-fallback path with a clear _embeddingNote
69
+ // in stats. Silently substituting mock embeddings would hide a missing
70
+ // production dependency from callers.
85
71
  }
86
72
  catch {
87
73
  // No embedding provider available, will use fallback
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claude-flow/cli",
3
- "version": "3.6.27",
3
+ "version": "3.6.29",
4
4
  "type": "module",
5
5
  "description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
6
6
  "main": "dist/src/index.js",