claude-flow 3.21.0 → 3.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/package.json +1 -1
  2. package/v3/@claude-flow/cli/dist/src/commands/daemon.js +23 -2
  3. package/v3/@claude-flow/cli/dist/src/commands/doctor.js +56 -0
  4. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.d.ts +27 -0
  5. package/v3/@claude-flow/cli/dist/src/commands/memory-distill.js +374 -0
  6. package/v3/@claude-flow/cli/dist/src/commands/memory.js +4 -2
  7. package/v3/@claude-flow/cli/dist/src/index.d.ts +1 -1
  8. package/v3/@claude-flow/cli/dist/src/index.js +22 -1
  9. package/v3/@claude-flow/cli/dist/src/init/executor.js +37 -0
  10. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.d.ts +19 -0
  11. package/v3/@claude-flow/cli/dist/src/init/helper-refresh.js +175 -0
  12. package/v3/@claude-flow/cli/dist/src/init/helper-signing.d.ts +30 -0
  13. package/v3/@claude-flow/cli/dist/src/init/helper-signing.js +60 -0
  14. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.d.ts +16 -0
  15. package/v3/@claude-flow/cli/dist/src/init/helpers-generator.js +89 -8
  16. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.d.ts +53 -0
  17. package/v3/@claude-flow/cli/dist/src/init/memory-package-resolver.js +118 -0
  18. package/v3/@claude-flow/cli/dist/src/init/statusline-generator.js +18 -7
  19. package/v3/@claude-flow/cli/dist/src/mcp-client.js +13 -0
  20. package/v3/@claude-flow/cli/dist/src/mcp-tools/agenticow-speculate-tools.js +9 -2
  21. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.d.ts +162 -0
  22. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-intent-tools.js +548 -0
  23. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.d.ts +9 -1
  24. package/v3/@claude-flow/cli/dist/src/mcp-tools/browser-tools.js +4 -1
  25. package/v3/@claude-flow/cli/dist/src/memory/memory-bridge.js +115 -54
  26. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.d.ts +71 -0
  27. package/v3/@claude-flow/cli/dist/src/memory/memory-initializer.js +330 -2
  28. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.d.ts +111 -0
  29. package/v3/@claude-flow/cli/dist/src/services/distill-tuning.js +510 -0
  30. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.d.ts +41 -0
  31. package/v3/@claude-flow/cli/dist/src/services/memory-distillation.js +277 -0
  32. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.d.ts +22 -1
  33. package/v3/@claude-flow/cli/dist/src/services/worker-daemon.js +117 -6
  34. package/v3/@claude-flow/cli/package.json +5 -2
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Memory Package Resolver (#2545)
3
+ *
4
+ * `@claude-flow/memory` is an *optionalDependency* of `@claude-flow/cli`. On the
5
+ * documented `npx ruflo` install path it lands in the npx cache
6
+ * (`~/.npm/_npx/<hash>/node_modules`), which is NOT on the node_modules walk-up
7
+ * path from the user's project. The SessionStart auto-memory hook therefore
8
+ * could never resolve it and self-learning silently no-op'd.
9
+ *
10
+ * At init time, however, the CLI *can* resolve the package from its own module
11
+ * context (it is installed alongside the CLI in that same npx cache). We resolve
12
+ * it once and record the absolute path in a machine-local project sidecar
13
+ * (`.claude-flow/memory-package.json`). The hook reads this sidecar first, so it
14
+ * reuses the copy npx already downloaded — no second install, no vendoring.
15
+ *
16
+ * This module is deliberately dependency-free and best-effort: nothing here ever
17
+ * throws into the init/doctor flow.
18
+ */
19
+ import { createRequire } from 'module';
20
+ import * as fs from 'fs';
21
+ import * as path from 'path';
22
+ export const MEMORY_PACKAGE = '@claude-flow/memory';
23
+ /** Project-relative path of the resolver sidecar written by init / doctor --fix. */
24
+ export const MEMORY_SIDECAR_REL = path.join('.claude-flow', 'memory-package.json');
25
+ /**
26
+ * Resolve `@claude-flow/memory`'s main entry from the CLI's own module context.
27
+ * Returns the absolute dist path, or null when the optional dependency is absent
28
+ * (e.g. installed with `--omit=optional`).
29
+ */
30
+ export function resolveMemoryPackageFromCli() {
31
+ try {
32
+ const require = createRequire(import.meta.url);
33
+ return require.resolve(MEMORY_PACKAGE);
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ }
39
+ /**
40
+ * Resolve `@claude-flow/memory` the same way the runtime hook does, from a target
41
+ * project directory: sidecar → project package.json → node_modules walk-up.
42
+ * Used by `doctor` so its verdict matches what the hook will actually experience.
43
+ */
44
+ export function resolveMemoryPackageFromProject(targetDir) {
45
+ // 1. Sidecar written by a previous init / doctor --fix
46
+ try {
47
+ const sidecar = path.join(targetDir, MEMORY_SIDECAR_REL);
48
+ if (fs.existsSync(sidecar)) {
49
+ const rec = JSON.parse(fs.readFileSync(sidecar, 'utf-8'));
50
+ if (rec?.distPath && fs.existsSync(rec.distPath))
51
+ return rec.distPath;
52
+ }
53
+ }
54
+ catch {
55
+ /* fall through */
56
+ }
57
+ // 2. createRequire from the project's package.json (direct/transitive dep)
58
+ try {
59
+ const require = createRequire(path.join(targetDir, 'package.json'));
60
+ return require.resolve(MEMORY_PACKAGE);
61
+ }
62
+ catch {
63
+ /* fall through */
64
+ }
65
+ // 3. node_modules walk-up from the project root
66
+ let dir = path.resolve(targetDir);
67
+ // eslint-disable-next-line no-constant-condition
68
+ while (true) {
69
+ const candidate = path.join(dir, 'node_modules', '@claude-flow', 'memory', 'dist', 'index.js');
70
+ if (fs.existsSync(candidate))
71
+ return candidate;
72
+ const parent = path.dirname(dir);
73
+ if (parent === dir)
74
+ break;
75
+ dir = parent;
76
+ }
77
+ return null;
78
+ }
79
+ /** Read the version of the resolved memory package (dist/index.js → ../package.json). */
80
+ export function readMemoryPackageVersion(distPath) {
81
+ try {
82
+ const pkgJson = path.resolve(path.dirname(distPath), '..', 'package.json');
83
+ if (fs.existsSync(pkgJson)) {
84
+ const parsed = JSON.parse(fs.readFileSync(pkgJson, 'utf-8'));
85
+ return parsed.version ?? null;
86
+ }
87
+ }
88
+ catch {
89
+ /* ignore */
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * Best-effort: resolve `@claude-flow/memory` from the CLI and record its path in
95
+ * the project sidecar so the runtime hook can find it on the npx install path.
96
+ * Returns the written record, or null when the package is not resolvable from the
97
+ * CLI (in which case the hook fails loud and `doctor` flags it).
98
+ */
99
+ export function recordMemoryPackagePath(targetDir, resolvedBy = 'init') {
100
+ const distPath = resolveMemoryPackageFromCli();
101
+ if (!distPath)
102
+ return null;
103
+ const record = {
104
+ distPath,
105
+ version: readMemoryPackageVersion(distPath),
106
+ resolvedBy,
107
+ resolvedAt: new Date().toISOString(),
108
+ };
109
+ try {
110
+ fs.mkdirSync(path.join(targetDir, '.claude-flow'), { recursive: true });
111
+ fs.writeFileSync(path.join(targetDir, MEMORY_SIDECAR_REL), JSON.stringify(record, null, 2), 'utf-8');
112
+ return record;
113
+ }
114
+ catch {
115
+ return null;
116
+ }
117
+ }
118
+ //# sourceMappingURL=memory-package-resolver.js.map
@@ -211,13 +211,24 @@ function getLocalAgentDB() {
211
211
  const memDb = path.join(CWD, '.swarm', 'memory.db');
212
212
  if (fs.existsSync(memDb)) {
213
213
  const Q = String.fromCharCode(34);
214
- const sql = Q + 'SELECT (SELECT COUNT(*) FROM memory_entries WHERE embedding IS NOT NULL)||' + "'|'" + '||(SELECT COUNT(*) FROM vector_indexes);' + Q;
215
- const out = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + sql, 1500);
216
- if (out && out.indexOf('|') !== -1) {
217
- const parts = out.split('|');
218
- result.vectorCount = parseInt(parts[0], 10) || 0;
219
- result.hasHnsw = (parseInt(parts[1], 10) || 0) > 0;
220
- }
214
+ // Two INDEPENDENT statements -- do NOT combine into one. Coupling the
215
+ // vector count with the vector_indexes row count in a single statement
216
+ // meant that on a DB missing the vector_indexes table (older/agentdb-
217
+ // written DBs), the whole statement failed at PREPARE time (SQLite
218
+ // compiles the full SQL before running), so the valid memory_entries
219
+ // count was discarded too and the statusline showed Vectors 0 despite
220
+ // thousands of real vectors. Split so a missing table can only zero the
221
+ // HNSW flag, never the count. The init self-heal provisions the table so
222
+ // the flag recovers on the next ruflo init / MCP start.
223
+ const countSql = Q + 'SELECT COUNT(*) FROM memory_entries WHERE embedding IS NOT NULL;' + Q;
224
+ const vc = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + countSql, 1500);
225
+ if (vc) result.vectorCount = parseInt(vc, 10) || 0;
226
+ // HNSW flag: separate statement. If vector_indexes is absent, sqlite3
227
+ // exits non-zero and safeExec returns empty -- hasHnsw stays false (exact
228
+ // original semantics: at least one index-config row present).
229
+ const hnswSql = Q + 'SELECT COUNT(*) FROM vector_indexes;' + Q;
230
+ const hn = safeExec("sqlite3 'file:" + memDb + "?mode=ro' " + hnswSql, 1500);
231
+ if (hn) result.hasHnsw = (parseInt(hn, 10) || 0) > 0;
221
232
  }
222
233
  } catch { /* ignore */ }
223
234
  return result;
@@ -33,6 +33,10 @@ 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
35
  import { browserSessionTools } from './mcp-tools/browser-session-tools.js';
36
+ // ADR-175 — page-agent natural-language intent layer (browser_act). Always
37
+ // registered; degrades to {degraded:true} at call time when page-agent or an
38
+ // OpenAI-compatible LLM provider isn't available.
39
+ import { browserIntentTools } from './mcp-tools/browser-intent-tools.js';
36
40
  import { execFileSync } from 'node:child_process';
37
41
  // Phase 6: AgentDB v3 controller tools
38
42
  import { agentdbTools } from './mcp-tools/agentdb-tools.js';
@@ -89,6 +93,14 @@ function getBrowserTools() {
89
93
  function getBrowserSessionTools() {
90
94
  return browserSessionTools;
91
95
  }
96
+ /**
97
+ * ADR-175 `browser_act` — always registered like browserSessionTools; the
98
+ * handler itself resolves page-agent + LLM-provider availability at call
99
+ * time and returns `{degraded: true}` rather than throwing.
100
+ */
101
+ function getBrowserIntentTools() {
102
+ return browserIntentTools;
103
+ }
92
104
  /**
93
105
  * MCP Tool Registry
94
106
  * Maps tool names to their handler functions
@@ -127,6 +139,7 @@ registerTools([
127
139
  ...coordinationTools,
128
140
  ...getBrowserTools(),
129
141
  ...getBrowserSessionTools(),
142
+ ...getBrowserIntentTools(),
130
143
  // Phase 6: AgentDB v3 controller tools
131
144
  ...agentdbTools,
132
145
  // RuVector WASM tools
@@ -115,11 +115,18 @@ export const agenticowSpeculateTools = [
115
115
  }
116
116
  // Build the generic {label, fn} candidates. Each fn ingests into its own
117
117
  // branch handle, then (for 'nearest') probes it so we can score.
118
+ // Map validated label → explicit branchPath so the branchPath() resolver
119
+ // below is O(1) instead of re-scanning + re-validating rawCandidates per
120
+ // candidate (explore() calls branchPath once per candidate → was O(n²)).
121
+ const explicitBranchPaths = new Map();
118
122
  const candidates = rawCandidates.map((c) => {
119
123
  const label = validateLabel(String(c.label));
120
124
  if (!Array.isArray(c.ingest) || c.ingest.length === 0) {
121
125
  throw new Error(`candidate ${label} must ingest at least one vector`);
122
126
  }
127
+ if (typeof c.branchPath === 'string' && c.branchPath) {
128
+ explicitBranchPaths.set(label, c.branchPath);
129
+ }
123
130
  const records = c.ingest.map((r) => ({
124
131
  ...(Number.isInteger(r.id) ? { id: r.id } : {}),
125
132
  vector: r.vector,
@@ -151,9 +158,9 @@ export const agenticowSpeculateTools = [
151
158
  return 1 / (1 + Math.max(0, r.bestDistance));
152
159
  };
153
160
  const branchPath = (label) => {
154
- const explicit = rawCandidates.find((c) => validateLabel(String(c.label)) === label)?.branchPath;
161
+ const explicit = explicitBranchPaths.get(label);
155
162
  if (explicit)
156
- return resolveMemoryPath(String(explicit));
163
+ return resolveMemoryPath(explicit);
157
164
  return `${basePath}.spec-${safeSegment(label)}.rvf`;
158
165
  };
159
166
  // ADR-171 promotion gate. For pure memory A/B the `score` IS the chosen
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Browser Intent MCP Tools — ADR-175 page-agent integration.
3
+ *
4
+ * Adds a natural-language INTENT layer on top of the low-level selector-based
5
+ * `browser_*` tools (browser-tools.ts, driven by the `agent-browser` CLI).
6
+ * `browser_act({ task })` lets a caller say "Click the login button" instead
7
+ * of chaining browser_snapshot + browser_click.
8
+ *
9
+ * Backing library: `page-agent` (npm, MIT, https://github.com/alibaba/page-agent)
10
+ * — in-page injected JS that turns the DOM into text and lets an LLM execute
11
+ * natural-language intents via `agent.execute('Click login')`.
12
+ *
13
+ * ============================================================================
14
+ * VERIFIED API (recorded 2026-07-04 against page-agent@1.11.0 — `npm view
15
+ * page-agent`, the published README, and the actual npm tarball contents).
16
+ * Deviations from the original integration brief are called out inline.
17
+ * ============================================================================
18
+ *
19
+ * - Package layout changed since the brief was written: `page-agent` is now a
20
+ * thin composition of `@page-agent/core` + `@page-agent/llms` +
21
+ * `@page-agent/page-controller` + `@page-agent/ui`. The npm package is
22
+ * ESM-only (`"type":"module"`) and ships two builds:
23
+ * - `dist/esm/page-agent.js` — the Node/bundler ESM entry.
24
+ * - `dist/iife/page-agent.demo.js` — a self-contained browser IIFE that
25
+ * sets `window.PageAgent = <class>` (this is what we inject).
26
+ *
27
+ * - DEVIATION #1 (load-bearing): `await import('page-agent')` is NOT a safe
28
+ * availability probe in Node. Empirically, importing the ESM entry throws
29
+ * `ReferenceError: window is not defined` — even when the package IS
30
+ * correctly installed — because `@page-agent/core`'s module graph touches
31
+ * DOM globals at import time (it's designed to run in a browser, not Node).
32
+ * This differs from the agenticow-loader.ts pattern used elsewhere in this
33
+ * repo (dynamic `import()` + catch `ERR_MODULE_NOT_FOUND`), which only
34
+ * works for pure-Node optional deps. Instead we detect availability via
35
+ * `require.resolve('page-agent')` — path resolution only, never executes
36
+ * module code — the same technique `browser-session-tools.ts` already uses
37
+ * to resolve the local `ruvector` CLI bin without invoking it.
38
+ *
39
+ * - DEVIATION #2: the IIFE bundle unconditionally (also) auto-constructs a
40
+ * DEMO `PageAgent` instance via `setTimeout`, pointed at Alibaba's public
41
+ * sandbox endpoint (`model: 'qwen3.5-plus'`, a `page-ag-testing-*.run`
42
+ * baseURL, `apiKey: 'NA'`) UNLESS the bundle is loaded via a real
43
+ * `<script src="...?autoInit=false">` tag — the guard reads
44
+ * `document.currentScript.src`, which is `null` when the bundle runs via
45
+ * `eval`/CDP (our only injection path through the `agent-browser`
46
+ * primitive set). Left alone, every `browser_act` call would leak page
47
+ * content to Alibaba's test endpoint using a placeholder key. We strip the
48
+ * demo auto-init tail (`stripDemoAutoInit`) before injecting rather than
49
+ * relying on the query-param guard.
50
+ *
51
+ * - Constructor: `new PageAgent({ model, baseURL, apiKey, language })` —
52
+ * `apiKey` is OPTIONAL on the type (`LLMConfig.apiKey?: string`), confirmed
53
+ * in `@page-agent/llms`' `LLMConfig` interface.
54
+ *
55
+ * - `execute(task: string): Promise<ExecutionResult>` where
56
+ * `ExecutionResult = { success: boolean; data: string; history: HistoricalEvent[] }`
57
+ * — DEVIATION #3: NOT `{ success, result, steps }` as sketched in the
58
+ * original brief. We map `data` → `result` and `history.length` → `steps`
59
+ * in the tool's own return shape, and also pass `history` through for
60
+ * callers that want the full step trace.
61
+ *
62
+ * - LLM transport: confirmed via `@page-agent/llms`' `OpenAIClient` — a
63
+ * single non-streaming `POST ${baseURL}/chat/completions` with
64
+ * `Authorization: Bearer ${apiKey}` and OpenAI tool-calling JSON. This is
65
+ * why a bare `ANTHROPIC_API_KEY` cannot be handed to page-agent directly:
66
+ * Anthropic's native API is a different shape (`/v1/messages`, different
67
+ * tool-call format). We require an OpenAI-*compatible* endpoint — reusing
68
+ * this CLI's own Tier-3 fallback ladder from `agent-execute-core.ts`
69
+ * (OpenRouter, then Ollama Cloud), or an explicit override.
70
+ *
71
+ * ARCHITECTURAL CONSTRAINT (mirrors metaharness-tools.ts / agenticow-tools.ts)
72
+ * `page-agent` lives in `optionalDependencies`. Every code path degrades to
73
+ * `{ degraded: true }` — never throws — when the package or an LLM provider
74
+ * isn't available.
75
+ *
76
+ * KEY-SAFETY CONSTRAINT (load-bearing): page-agent's LLM calls happen FROM
77
+ * the browser page context (it's client-side injected JS), so its `apiKey`
78
+ * config field is necessarily visible to whatever we inject into the page.
79
+ * To avoid ever placing a real provider key into a page-context string, we
80
+ * start a short-lived local HTTP proxy (`startLocalLLMProxy`) bound to
81
+ * 127.0.0.1 that holds the REAL key server-side and injects the real
82
+ * `Authorization` header itself; the page only ever sees the proxy's
83
+ * loopback URL plus a constant placeholder string (`PLACEHOLDER_API_KEY`).
84
+ *
85
+ * @module @claude-flow/cli/mcp-tools/browser-intent
86
+ */
87
+ import type { MCPTool } from './types.js';
88
+ export interface PageAgentBundle {
89
+ /** Package root directory (contains package.json). */
90
+ root: string;
91
+ /** Absolute path to the browser-injectable IIFE bundle. */
92
+ iifePath: string;
93
+ version: string;
94
+ }
95
+ /**
96
+ * Locate the installed `page-agent` package's browser bundle without ever
97
+ * executing its module code. `resolvePkg` is injectable for tests (DI over
98
+ * module mocking, since `vi.mock('page-agent', ...)` cannot simulate absence
99
+ * for a package we deliberately never `import()`).
100
+ */
101
+ export declare function locatePageAgentBundle(resolvePkg?: (id: string) => string): PageAgentBundle | null;
102
+ export interface ResolvedLLMConfig {
103
+ baseURL: string;
104
+ apiKey: string;
105
+ model: string;
106
+ source: 'explicit' | 'openrouter' | 'ollama';
107
+ }
108
+ export declare function resolvePageAgentLLMConfig(env?: NodeJS.ProcessEnv): ResolvedLLMConfig | null;
109
+ export declare const PLACEHOLDER_API_KEY = "ruflo-proxied-key";
110
+ export interface LLMProxyHandle {
111
+ url: string;
112
+ close: () => void;
113
+ }
114
+ export declare function startLocalLLMProxy(real: {
115
+ baseURL: string;
116
+ apiKey: string;
117
+ }): Promise<LLMProxyHandle>;
118
+ /** Thrown when a bundle still contains a demo/sandbox endpoint after stripping. */
119
+ export declare class DemoLeakError extends Error {
120
+ readonly signature: string;
121
+ constructor(signature: string);
122
+ }
123
+ /** Returns the first demo-leak signature found in `source`, or null if clean. */
124
+ export declare function findDemoLeak(source: string): string | null;
125
+ /**
126
+ * Strip the demo bundle's auto-init tail so injecting it via eval doesn't spin
127
+ * up a second `window.pageAgent` pointed at Alibaba's sandbox endpoint. This is
128
+ * best-effort at the text level; the real guarantee is the fail-closed
129
+ * `findDemoLeak` firewall enforced in `buildPageAgentInjection`, NOT this strip.
130
+ */
131
+ export declare function stripDemoAutoInit(iifeSource: string): string;
132
+ export declare const BROWSER_ACT_RESULT_GLOBAL = "__ruflo_browser_act_result__";
133
+ export interface PageAgentPageConfig {
134
+ baseURL: string;
135
+ apiKey: string;
136
+ model: string;
137
+ language?: string;
138
+ }
139
+ /**
140
+ * Build the full script to hand to `browser_eval` / `agent-browser eval`:
141
+ * the (demo-stripped) IIFE bundle, followed by our own controlled
142
+ * construction + `execute()` call whose settled result lands on a window
143
+ * global we can poll for.
144
+ *
145
+ * KEY-SAFETY: `pageConfig.apiKey` MUST be `PLACEHOLDER_API_KEY` (or another
146
+ * non-secret placeholder) — callers are responsible for routing the real key
147
+ * through `startLocalLLMProxy` first. This function only assembles strings;
148
+ * it does not itself guarantee key safety, so callers MUST NOT pass a real
149
+ * key here (see `browserIntentTools` handler for the enforced call site).
150
+ */
151
+ export declare function buildPageAgentInjection(iifeSource: string, pageConfig: PageAgentPageConfig, task: string): string;
152
+ export declare function recordBrowserTrajectory(entry: {
153
+ task: string;
154
+ url?: string;
155
+ success: boolean;
156
+ dataPreview: string;
157
+ steps: number;
158
+ source: string;
159
+ }): Promise<boolean>;
160
+ export declare const browserIntentTools: MCPTool[];
161
+ export default browserIntentTools;
162
+ //# sourceMappingURL=browser-intent-tools.d.ts.map