@claude-flow/cli 3.57.2 → 3.58.1
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/.claude/helpers/helpers.manifest.json +2 -2
- package/README.md +1 -0
- package/catalog-manifest.json +2 -2
- package/dist/src/commands/agent.js +4 -1
- package/dist/src/commands/mcp.js +66 -6
- package/dist/src/commands/memory.js +53 -2
- package/dist/src/mcp-server.d.ts +5 -0
- package/dist/src/mcp-server.js +39 -0
- package/dist/src/mcp-tools/agent-tools.js +28 -0
- package/dist/src/mcp-tools/claims-tools.js +25 -15
- package/dist/src/mcp-tools/hooks-tools.js +13 -3
- package/dist/src/memory/memory-initializer.js +43 -4
- package/dist/src/memory/memory-peek.d.ts +28 -0
- package/dist/src/memory/memory-peek.js +222 -0
- package/dist/src/memory/model-cache-dir.d.ts +11 -0
- package/dist/src/memory/model-cache-dir.js +17 -0
- package/dist/src/parser.js +3 -2
- package/dist/src/ruvector/task-embedder.js +38 -4
- package/node_modules/@claude-flow/codex/README.md +78 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts +3 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js +115 -0
- package/node_modules/@claude-flow/codex/dist/activity/cli.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts +14 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js +142 -0
- package/node_modules/@claude-flow/codex/dist/activity/files.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js +179 -0
- package/node_modules/@claude-flow/codex/dist/activity/parser.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts +20 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js +106 -0
- package/node_modules/@claude-flow/codex/dist/activity/reader.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts +4 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js +27 -0
- package/node_modules/@claude-flow/codex/dist/activity/sanitize.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts +31 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js +5 -0
- package/node_modules/@claude-flow/codex/dist/activity/types.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts +21 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.d.ts.map +1 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js +168 -0
- package/node_modules/@claude-flow/codex/dist/activity/viewer.js.map +1 -0
- package/node_modules/@claude-flow/codex/dist/cli.js +3 -0
- package/node_modules/@claude-flow/codex/dist/cli.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/server.js +5 -3
- package/node_modules/@claude-flow/mcp/dist/server.js.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.d.ts +11 -0
- package/node_modules/@claude-flow/mcp/dist/types.d.ts.map +1 -1
- package/node_modules/@claude-flow/mcp/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/plugins/ruflo-console/dist/tui.mjs +124 -124
|
@@ -10,6 +10,7 @@ import { createRequire } from 'node:module';
|
|
|
10
10
|
import { readFileMaybeEncrypted, writeDbImagePreservingFormat, writeFileRestricted } from '../fs-secure.js';
|
|
11
11
|
import { restoreMemoryDbFromBackup } from '../services/memory-backup.js';
|
|
12
12
|
import { validateIdentifier } from '../mcp-tools/validate-input.js';
|
|
13
|
+
import { resolveModelCacheDir } from './model-cache-dir.js';
|
|
13
14
|
/**
|
|
14
15
|
* ADR-323 — typed memory provenance. Distinguishes WHO/WHAT wrote a memory
|
|
15
16
|
* entry (a user's stated claim vs an agent's own output vs a tool result vs
|
|
@@ -2009,6 +2010,8 @@ export async function applyTemporalDecay(dbPath) {
|
|
|
2009
2010
|
};
|
|
2010
2011
|
}
|
|
2011
2012
|
}
|
|
2013
|
+
/** ADR-454: minimum interval before a 'retryable' degraded state re-attempts real init. */
|
|
2014
|
+
const MODEL_INIT_RETRY_INTERVAL_MS = 60_000;
|
|
2012
2015
|
/**
|
|
2013
2016
|
* State of the LOCAL embedding chain only (transformers.js / agentic-flow /
|
|
2014
2017
|
* ruvector ONNX / hash). #3375: the AgentDB bridge's result is cached
|
|
@@ -2058,8 +2061,15 @@ export async function loadEmbeddingModel(options) {
|
|
|
2058
2061
|
*/
|
|
2059
2062
|
async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now()) {
|
|
2060
2063
|
if (embeddingModelState?.loaded) {
|
|
2061
|
-
|
|
2064
|
+
const isRetryableDue = embeddingModelState.degradedReason === 'retryable' &&
|
|
2065
|
+
Date.now() - (embeddingModelState.lastAttemptAt ?? 0) >= MODEL_INIT_RETRY_INTERVAL_MS;
|
|
2066
|
+
if (!isRetryableDue) {
|
|
2067
|
+
return { success: true, dimensions: embeddingModelState.dimensions, modelName: 'cached', loadTime: 0 };
|
|
2068
|
+
}
|
|
2069
|
+
// Fall through to re-attempt real initialization — the retryable
|
|
2070
|
+
// failure (e.g. EACCES on the model cache dir) may now be fixed.
|
|
2062
2071
|
}
|
|
2072
|
+
let sawRetryableFailure = false;
|
|
2063
2073
|
try {
|
|
2064
2074
|
// ADR-094: prefer @huggingface/transformers (clears protobufjs <7.5.5
|
|
2065
2075
|
// critical RCE chain), fall back to legacy @xenova/transformers.
|
|
@@ -2068,6 +2078,7 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2068
2078
|
// @claude-flow/embeddings/src/transformers-loader.ts.
|
|
2069
2079
|
let transformersSource = null;
|
|
2070
2080
|
let pipelineFn = null;
|
|
2081
|
+
let transformersModule = null;
|
|
2071
2082
|
{
|
|
2072
2083
|
const tryLoad = async (specifier) => {
|
|
2073
2084
|
try {
|
|
@@ -2081,12 +2092,14 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2081
2092
|
if (hf && typeof hf.pipeline === 'function') {
|
|
2082
2093
|
pipelineFn = hf.pipeline;
|
|
2083
2094
|
transformersSource = '@huggingface/transformers';
|
|
2095
|
+
transformersModule = hf;
|
|
2084
2096
|
}
|
|
2085
2097
|
else {
|
|
2086
2098
|
const xen = await tryLoad('@xenova/transformers');
|
|
2087
2099
|
if (xen && typeof xen.pipeline === 'function') {
|
|
2088
2100
|
pipelineFn = xen.pipeline;
|
|
2089
2101
|
transformersSource = '@xenova/transformers';
|
|
2102
|
+
transformersModule = xen;
|
|
2090
2103
|
}
|
|
2091
2104
|
}
|
|
2092
2105
|
}
|
|
@@ -2103,6 +2116,14 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2103
2116
|
if (verbose) {
|
|
2104
2117
|
console.log(`Loading ONNX embedding model via ${transformersSource} (all-MiniLM-L6-v2)...`);
|
|
2105
2118
|
}
|
|
2119
|
+
// ADR-454: route model downloads to a user-writable cache dir —
|
|
2120
|
+
// the package-relative default is root-owned under a system-prefix
|
|
2121
|
+
// global install, which throws EACCES for the runtime user.
|
|
2122
|
+
const cacheDir = resolveModelCacheDir();
|
|
2123
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
2124
|
+
const env = transformersModule?.env;
|
|
2125
|
+
if (env)
|
|
2126
|
+
env.cacheDir = cacheDir;
|
|
2106
2127
|
const embedder = await pipelineFn('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
|
|
2107
2128
|
embeddingModelState = {
|
|
2108
2129
|
loaded: true,
|
|
@@ -2118,6 +2139,10 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2118
2139
|
};
|
|
2119
2140
|
}
|
|
2120
2141
|
catch (err) {
|
|
2142
|
+
// ADR-454: a real backend was importable and callable — this failure
|
|
2143
|
+
// (EACCES, ENOSPC, transient network error, etc.) is environmental,
|
|
2144
|
+
// not a missing dependency, so it must not be memoized permanently.
|
|
2145
|
+
sawRetryableFailure = true;
|
|
2121
2146
|
if (verbose) {
|
|
2122
2147
|
console.warn(`${transformersSource} pipeline init failed (${err instanceof Error ? err.message : String(err)}); ` +
|
|
2123
2148
|
'falling through to ruvector ONNX / agentic-flow / hash fallback.');
|
|
@@ -2186,12 +2211,18 @@ async function loadLocalEmbeddingChain(verbose = false, startTime = Date.now())
|
|
|
2186
2211
|
}
|
|
2187
2212
|
// No agentic-flow root-entry fallback: no published root exports
|
|
2188
2213
|
// `embeddings`, and importing it runs its CLI main() in 3.0.0-alpha.x.
|
|
2189
|
-
// No ONNX model available - use fallback
|
|
2214
|
+
// No ONNX model available - use fallback.
|
|
2215
|
+
// ADR-454: mark whether this is a permanent (no backend importable at
|
|
2216
|
+
// all) or retryable (a backend existed but init failed for an
|
|
2217
|
+
// environmental reason) degraded state, so a later call can re-attempt
|
|
2218
|
+
// real initialization once the condition is fixed.
|
|
2190
2219
|
embeddingModelState = {
|
|
2191
2220
|
loaded: true,
|
|
2192
2221
|
model: null, // Will use simple hash-based fallback
|
|
2193
2222
|
tokenizer: null,
|
|
2194
|
-
dimensions: 128 // Smaller fallback dimensions
|
|
2223
|
+
dimensions: 128, // Smaller fallback dimensions
|
|
2224
|
+
degradedReason: sawRetryableFailure ? 'retryable' : 'permanent',
|
|
2225
|
+
lastAttemptAt: Date.now()
|
|
2195
2226
|
};
|
|
2196
2227
|
return {
|
|
2197
2228
|
success: true,
|
|
@@ -2257,7 +2288,15 @@ export async function generateLocalEmbedding(text) {
|
|
|
2257
2288
|
// loadEmbeddingModel(), which is bridge-first — when the bridge answered
|
|
2258
2289
|
// there, no local model was ever loaded and this function always returned
|
|
2259
2290
|
// the hash fallback.
|
|
2260
|
-
|
|
2291
|
+
//
|
|
2292
|
+
// ADR-454: also re-attempt when the memoized state is a 'retryable'
|
|
2293
|
+
// degraded fallback whose retry interval has elapsed — otherwise this
|
|
2294
|
+
// guard short-circuits past loadLocalEmbeddingChain()'s own retry check
|
|
2295
|
+
// and the fix (e.g. a now-writable cache dir) never gets exercised.
|
|
2296
|
+
const shouldRetryLoad = !embeddingModelState?.loaded ||
|
|
2297
|
+
(embeddingModelState.degradedReason === 'retryable' &&
|
|
2298
|
+
Date.now() - (embeddingModelState.lastAttemptAt ?? 0) >= MODEL_INIT_RETRY_INTERVAL_MS);
|
|
2299
|
+
if (shouldRetryLoad) {
|
|
2261
2300
|
await loadLocalEmbeddingChain();
|
|
2262
2301
|
}
|
|
2263
2302
|
// #2461: loadEmbeddingModel() can leave embeddingModelState null when an
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type MemoryRootSource = 'path' | 'CLAUDE_FLOW_DB_PATH' | 'CLAUDE_FLOW_MEMORY_PATH' | 'config' | 'default';
|
|
2
|
+
export interface MemoryPeekStore {
|
|
3
|
+
role: 'cli' | 'mcp';
|
|
4
|
+
path: string;
|
|
5
|
+
exists: boolean;
|
|
6
|
+
rows: number | null;
|
|
7
|
+
lastWrite: string | null;
|
|
8
|
+
sizeBytes: number | null;
|
|
9
|
+
walBytes: number | null;
|
|
10
|
+
encrypted?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface MemoryPeekResult {
|
|
13
|
+
cwd: string;
|
|
14
|
+
root: {
|
|
15
|
+
path: string;
|
|
16
|
+
source: MemoryRootSource;
|
|
17
|
+
};
|
|
18
|
+
stores: MemoryPeekStore[];
|
|
19
|
+
sharedKeys: number | null;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Resolve the route and report both stores, read-only. Never creates,
|
|
23
|
+
* initializes, or migrates anything, on an empty project or any other.
|
|
24
|
+
*/
|
|
25
|
+
export declare function memoryPeek(opts?: {
|
|
26
|
+
path?: string;
|
|
27
|
+
}): Promise<MemoryPeekResult>;
|
|
28
|
+
//# sourceMappingURL=memory-peek.d.ts.map
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #3508: read-only memory route/peek.
|
|
3
|
+
*
|
|
4
|
+
* A health check needs to know which memory store files a cwd resolves to,
|
|
5
|
+
* and what is in them, without changing anything to find out. `memory stats`
|
|
6
|
+
* (even after #3463/#3448) still calls `ensureInitialized()` when no explicit
|
|
7
|
+
* path is given, and answers for one file per call. This module reports
|
|
8
|
+
* BOTH files — the sql.js `memory.db` the CLI writes and its native
|
|
9
|
+
* `agentdb-memory.db` sibling the MCP/bridge path writes — in one read-only
|
|
10
|
+
* pass, and says which resolution rule picked the root.
|
|
11
|
+
*
|
|
12
|
+
* Contract (do not relax without re-reading #3508):
|
|
13
|
+
* - never creates, initializes, or migrates anything;
|
|
14
|
+
* - never opens a file that does not exist (`existsSync` gated before any
|
|
15
|
+
* open attempt, for both engines);
|
|
16
|
+
* - every open is `readonly`/`fileMustExist` (native) or a one-shot byte
|
|
17
|
+
* buffer handed to sql.js, which is never written back to disk;
|
|
18
|
+
* - `rows: null` means "could not read" and is never a count of 0, so an
|
|
19
|
+
* empty store and an unreadable one never look the same;
|
|
20
|
+
* - an encrypted-at-rest `memory.db` reports `encrypted: true` and
|
|
21
|
+
* `rows: null`, but its size is still reported.
|
|
22
|
+
*/
|
|
23
|
+
import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from 'node:fs';
|
|
24
|
+
import path from 'node:path';
|
|
25
|
+
import { getMemoryRoot, resolveDbPath } from './memory-initializer.js';
|
|
26
|
+
import { siblingAgentDbPath } from './memory-bridge.js';
|
|
27
|
+
import { isEncryptedBlob } from '../encryption/vault.js';
|
|
28
|
+
// Mirrors the `ACTIVE_MEMORY_ROW_SQL` convention in memory-initializer.ts:
|
|
29
|
+
// legacy rows with a NULL status predate soft-delete semantics and count.
|
|
30
|
+
const ACTIVE_ROW_SQL = `(status = 'active' OR status IS NULL)`;
|
|
31
|
+
const ENCRYPTION_SNIFF_LEN = 64; // RFE1 magic(4)+iv(12)+tag(16); 64 gives headroom.
|
|
32
|
+
function safeStatSize(filePath) {
|
|
33
|
+
try {
|
|
34
|
+
return statSync(filePath).size;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function readHeaderBytes(filePath, len) {
|
|
41
|
+
const fd = openSync(filePath, 'r');
|
|
42
|
+
try {
|
|
43
|
+
const buf = Buffer.alloc(len);
|
|
44
|
+
const bytesRead = readSync(fd, buf, 0, len, 0);
|
|
45
|
+
return buf.subarray(0, bytesRead);
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
closeSync(fd);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function latestMillis(values) {
|
|
52
|
+
let latest = -Infinity;
|
|
53
|
+
for (const raw of values) {
|
|
54
|
+
const millis = typeof raw === 'number' ? raw : Number(raw);
|
|
55
|
+
if (Number.isFinite(millis))
|
|
56
|
+
latest = Math.max(latest, millis);
|
|
57
|
+
}
|
|
58
|
+
return latest;
|
|
59
|
+
}
|
|
60
|
+
/** sql.js is read as a one-shot in-memory byte buffer — nothing is ever
|
|
61
|
+
* written back, so reading this file has no on-disk side effect. */
|
|
62
|
+
async function probeSqlJsStore(dbPath) {
|
|
63
|
+
try {
|
|
64
|
+
if (isEncryptedBlob(readHeaderBytes(dbPath, ENCRYPTION_SNIFF_LEN))) {
|
|
65
|
+
return { rows: null, lastWrite: null, keys: null, encrypted: true };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
/* header read failed (permissions/race) — fall through to the open attempt */
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
const initSqlJs = (await import('sql.js')).default ?? (await import('sql.js'));
|
|
73
|
+
const SQL = await (typeof initSqlJs === 'function' ? initSqlJs() : initSqlJs.default());
|
|
74
|
+
const buf = readFileSync(dbPath);
|
|
75
|
+
const db = new SQL.Database(new Uint8Array(buf));
|
|
76
|
+
try {
|
|
77
|
+
const tables = db.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_entries'");
|
|
78
|
+
if (!tables.length)
|
|
79
|
+
return { rows: null, lastWrite: null, keys: null };
|
|
80
|
+
const result = db.exec(`SELECT namespace, key, created_at, updated_at FROM memory_entries WHERE ${ACTIVE_ROW_SQL}`);
|
|
81
|
+
const values = result[0]?.values ?? [];
|
|
82
|
+
const keys = new Set();
|
|
83
|
+
let last = -Infinity;
|
|
84
|
+
for (const [namespace, key, createdAt, updatedAt] of values) {
|
|
85
|
+
keys.add(`${String(namespace)}\u0000${String(key)}`);
|
|
86
|
+
last = Math.max(last, latestMillis([createdAt, updatedAt]));
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
rows: values.length,
|
|
90
|
+
lastWrite: Number.isFinite(last) ? new Date(last).toISOString() : null,
|
|
91
|
+
keys,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
try {
|
|
96
|
+
db.close();
|
|
97
|
+
}
|
|
98
|
+
catch { /* best effort */ }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return { rows: null, lastWrite: null, keys: null };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** better-sqlite3 is an optional native dependency — absence must degrade
|
|
106
|
+
* to "could not read", never throw. Opened `readonly`/`fileMustExist`, same
|
|
107
|
+
* as `countSiblingStoreRows` in sibling-store.ts. */
|
|
108
|
+
async function probeNativeStore(dbPath) {
|
|
109
|
+
try {
|
|
110
|
+
const require = (await import('node:module')).createRequire(import.meta.url);
|
|
111
|
+
const Database = require('better-sqlite3');
|
|
112
|
+
const db = new Database(dbPath, { readonly: true, fileMustExist: true });
|
|
113
|
+
try {
|
|
114
|
+
const table = db
|
|
115
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='memory_entries'")
|
|
116
|
+
.get();
|
|
117
|
+
if (!table)
|
|
118
|
+
return { rows: null, lastWrite: null, keys: null };
|
|
119
|
+
const rows = db
|
|
120
|
+
.prepare(`SELECT namespace, key, created_at, updated_at FROM memory_entries WHERE ${ACTIVE_ROW_SQL}`)
|
|
121
|
+
.all();
|
|
122
|
+
const keys = new Set();
|
|
123
|
+
let last = -Infinity;
|
|
124
|
+
for (const row of rows) {
|
|
125
|
+
keys.add(`${String(row.namespace)}\u0000${String(row.key)}`);
|
|
126
|
+
last = Math.max(last, latestMillis([row.created_at, row.updated_at]));
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
rows: rows.length,
|
|
130
|
+
lastWrite: Number.isFinite(last) ? new Date(last).toISOString() : null,
|
|
131
|
+
keys,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
try {
|
|
136
|
+
db.close();
|
|
137
|
+
}
|
|
138
|
+
catch { /* best effort */ }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return { rows: null, lastWrite: null, keys: null };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/** Same precedence `getMemoryRoot()`/`resolveDbPath()` already own — this
|
|
146
|
+
* only *labels* which rule decided, it never re-derives the path itself. */
|
|
147
|
+
function decideSource(cliFlag) {
|
|
148
|
+
if (cliFlag && cliFlag.trim().length > 0)
|
|
149
|
+
return 'path';
|
|
150
|
+
if (process.env.CLAUDE_FLOW_DB_PATH && process.env.CLAUDE_FLOW_DB_PATH.trim().length > 0) {
|
|
151
|
+
return 'CLAUDE_FLOW_DB_PATH';
|
|
152
|
+
}
|
|
153
|
+
if (process.env.CLAUDE_FLOW_MEMORY_PATH && process.env.CLAUDE_FLOW_MEMORY_PATH.trim().length > 0) {
|
|
154
|
+
return 'CLAUDE_FLOW_MEMORY_PATH';
|
|
155
|
+
}
|
|
156
|
+
const configCandidates = [
|
|
157
|
+
path.resolve(process.cwd(), 'claude-flow.config.json'),
|
|
158
|
+
path.resolve(process.cwd(), '.claude-flow', 'config.json'),
|
|
159
|
+
];
|
|
160
|
+
for (const configPath of configCandidates) {
|
|
161
|
+
if (!existsSync(configPath))
|
|
162
|
+
continue;
|
|
163
|
+
try {
|
|
164
|
+
const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
165
|
+
const fromConfig = raw?.memory?.persistPath ?? raw?.memory?.path;
|
|
166
|
+
if (typeof fromConfig === 'string' && fromConfig.trim().length > 0)
|
|
167
|
+
return 'config';
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
/* malformed config — getMemoryRoot() falls through to default too */
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return 'default';
|
|
174
|
+
}
|
|
175
|
+
async function describeStore(role, storePath, probe) {
|
|
176
|
+
if (!existsSync(storePath)) {
|
|
177
|
+
return {
|
|
178
|
+
store: { role, path: storePath, exists: false, rows: null, lastWrite: null, sizeBytes: null, walBytes: null },
|
|
179
|
+
keys: null,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const result = await probe(storePath);
|
|
183
|
+
const sizeBytes = safeStatSize(storePath);
|
|
184
|
+
const walBytes = safeStatSize(`${storePath}-wal`) ?? 0;
|
|
185
|
+
return {
|
|
186
|
+
store: {
|
|
187
|
+
role,
|
|
188
|
+
path: storePath,
|
|
189
|
+
exists: true,
|
|
190
|
+
rows: result.rows,
|
|
191
|
+
lastWrite: result.lastWrite,
|
|
192
|
+
sizeBytes,
|
|
193
|
+
walBytes,
|
|
194
|
+
...(result.encrypted ? { encrypted: true } : {}),
|
|
195
|
+
},
|
|
196
|
+
keys: result.keys,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Resolve the route and report both stores, read-only. Never creates,
|
|
201
|
+
* initializes, or migrates anything, on an empty project or any other.
|
|
202
|
+
*/
|
|
203
|
+
export async function memoryPeek(opts) {
|
|
204
|
+
const cliFlag = opts?.path;
|
|
205
|
+
const cliPath = resolveDbPath(cliFlag);
|
|
206
|
+
const mcpPath = siblingAgentDbPath(cliPath);
|
|
207
|
+
const root = { path: cliFlag ? path.dirname(cliPath) : getMemoryRoot(), source: decideSource(cliFlag) };
|
|
208
|
+
const cliDescribed = await describeStore('cli', cliPath, probeSqlJsStore);
|
|
209
|
+
const stores = [cliDescribed.store];
|
|
210
|
+
let mcpKeys = null;
|
|
211
|
+
if (mcpPath) {
|
|
212
|
+
const mcpDescribed = await describeStore('mcp', mcpPath, probeNativeStore);
|
|
213
|
+
stores.push(mcpDescribed.store);
|
|
214
|
+
mcpKeys = mcpDescribed.keys;
|
|
215
|
+
}
|
|
216
|
+
const cliKeys = cliDescribed.keys;
|
|
217
|
+
const sharedKeys = cliKeys && mcpKeys
|
|
218
|
+
? [...cliKeys].filter((key) => mcpKeys.has(key)).length
|
|
219
|
+
: null;
|
|
220
|
+
return { cwd: process.cwd(), root, stores, sharedKeys };
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=memory-peek.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a writable cache directory for ONNX model downloads.
|
|
3
|
+
*
|
|
4
|
+
* ADR-454: duplicated intentionally from
|
|
5
|
+
* `@claude-flow/embeddings/src/transformers-loader.ts`'s
|
|
6
|
+
* `resolveModelCacheDir()` rather than imported, since `@claude-flow/cli`
|
|
7
|
+
* has no dependency on `@claude-flow/embeddings` (see the circular
|
|
8
|
+
* optional-dep note in `memory-initializer.ts`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveModelCacheDir(): string;
|
|
11
|
+
//# sourceMappingURL=model-cache-dir.d.ts.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as os from 'node:os';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Resolve a writable cache directory for ONNX model downloads.
|
|
5
|
+
*
|
|
6
|
+
* ADR-454: duplicated intentionally from
|
|
7
|
+
* `@claude-flow/embeddings/src/transformers-loader.ts`'s
|
|
8
|
+
* `resolveModelCacheDir()` rather than imported, since `@claude-flow/cli`
|
|
9
|
+
* has no dependency on `@claude-flow/embeddings` (see the circular
|
|
10
|
+
* optional-dep note in `memory-initializer.ts`).
|
|
11
|
+
*/
|
|
12
|
+
export function resolveModelCacheDir() {
|
|
13
|
+
return (process.env.TRANSFORMERS_CACHE ||
|
|
14
|
+
process.env.HF_HOME ||
|
|
15
|
+
path.join(os.homedir(), '.cache', 'ruflo', 'models'));
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=model-cache-dir.js.map
|
package/dist/src/parser.js
CHANGED
|
@@ -184,8 +184,9 @@ export class CommandParser {
|
|
|
184
184
|
let commandSlotAvailable = true;
|
|
185
185
|
while (i < args.length) {
|
|
186
186
|
const arg = args[i];
|
|
187
|
-
// Check for end of flags marker
|
|
188
|
-
|
|
187
|
+
// Check for end of flags marker. Only the first "--" changes parser
|
|
188
|
+
// state; a later literal "--" is an ordinary positional (#4310).
|
|
189
|
+
if (parsingFlags && arg === '--') {
|
|
189
190
|
parsingFlags = false;
|
|
190
191
|
i++;
|
|
191
192
|
continue;
|
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
*
|
|
20
20
|
* @module task-embedder
|
|
21
21
|
*/
|
|
22
|
+
import * as fs from 'node:fs';
|
|
23
|
+
import { resolveModelCacheDir } from '../memory/model-cache-dir.js';
|
|
22
24
|
// ============================================================================
|
|
23
25
|
// FNV-1a-32 hash (matches scripts/gen-seed-corpus.mjs + router-trajectory.ts)
|
|
24
26
|
// ============================================================================
|
|
@@ -38,18 +40,48 @@ function cacheKey(task) {
|
|
|
38
40
|
// same function (xenova's pipeline returns a callable that accepts either),
|
|
39
41
|
// so we don't pay the ONNX model-load cost twice.
|
|
40
42
|
let _extractorPromise = null;
|
|
43
|
+
/**
|
|
44
|
+
* ADR-454: distinguishes a null-extractor result caused by a genuinely
|
|
45
|
+
* missing dependency (permanent — nothing changes without a reinstall) from
|
|
46
|
+
* one caused by an environmental/fixable failure during `pipeline(...)`
|
|
47
|
+
* init, such as EACCES on the model cache dir (retryable).
|
|
48
|
+
*/
|
|
49
|
+
let _extractorDegradedReason = null;
|
|
50
|
+
let _extractorLastAttemptAt = 0;
|
|
51
|
+
const EXTRACTOR_RETRY_INTERVAL_MS = 60_000;
|
|
41
52
|
function loadExtractor() {
|
|
42
|
-
if (_extractorPromise !== null)
|
|
43
|
-
|
|
53
|
+
if (_extractorPromise !== null) {
|
|
54
|
+
const isRetryableDue = _extractorDegradedReason === 'retryable' &&
|
|
55
|
+
Date.now() - _extractorLastAttemptAt >= EXTRACTOR_RETRY_INTERVAL_MS;
|
|
56
|
+
if (!isRetryableDue)
|
|
57
|
+
return _extractorPromise;
|
|
58
|
+
// Fall through to re-attempt — the retryable failure may now be fixed.
|
|
59
|
+
}
|
|
60
|
+
_extractorLastAttemptAt = Date.now();
|
|
44
61
|
_extractorPromise = (async () => {
|
|
62
|
+
let packageImportable = false;
|
|
45
63
|
try {
|
|
46
64
|
const specifier = '@xenova/transformers';
|
|
47
65
|
const mod = await import(/* @vite-ignore */ specifier).catch(() => null);
|
|
48
|
-
if (!mod || typeof mod.pipeline !== 'function')
|
|
66
|
+
if (!mod || typeof mod.pipeline !== 'function') {
|
|
67
|
+
_extractorDegradedReason = 'permanent';
|
|
49
68
|
return null;
|
|
50
|
-
|
|
69
|
+
}
|
|
70
|
+
packageImportable = true;
|
|
71
|
+
// ADR-454: route model downloads to a user-writable cache dir — the
|
|
72
|
+
// package-relative default is root-owned under a system-prefix
|
|
73
|
+
// global install, which throws EACCES for the runtime user.
|
|
74
|
+
const cacheDir = resolveModelCacheDir();
|
|
75
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
76
|
+
const env = mod.env;
|
|
77
|
+
if (env)
|
|
78
|
+
env.cacheDir = cacheDir;
|
|
79
|
+
const extractor = await mod.pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2', { quantized: true });
|
|
80
|
+
_extractorDegradedReason = null;
|
|
81
|
+
return extractor;
|
|
51
82
|
}
|
|
52
83
|
catch {
|
|
84
|
+
_extractorDegradedReason = packageImportable ? 'retryable' : 'permanent';
|
|
53
85
|
return null;
|
|
54
86
|
}
|
|
55
87
|
})();
|
|
@@ -233,5 +265,7 @@ export function __resetTaskEmbedderForTests() {
|
|
|
233
265
|
_hits = 0;
|
|
234
266
|
_misses = 0;
|
|
235
267
|
_extractorPromise = null;
|
|
268
|
+
_extractorDegradedReason = null;
|
|
269
|
+
_extractorLastAttemptAt = 0;
|
|
236
270
|
}
|
|
237
271
|
//# sourceMappingURL=task-embedder.js.map
|
|
@@ -1042,6 +1042,84 @@ npx claude-flow memory optimize --rebuild-index
|
|
|
1042
1042
|
|
|
1043
1043
|
---
|
|
1044
1044
|
|
|
1045
|
+
## Inspect an existing Codex session
|
|
1046
|
+
|
|
1047
|
+
The `activity` command lets you pick a helper and read its assignment, recorded
|
|
1048
|
+
state, public progress messages, and tool calls/results. It reads local rollout
|
|
1049
|
+
files without starting model sessions or sending commands to agents.
|
|
1050
|
+
|
|
1051
|
+
From a checkout containing this change:
|
|
1052
|
+
|
|
1053
|
+
```bash
|
|
1054
|
+
npm install --prefix v3/@claude-flow/codex --workspaces=false --ignore-scripts --legacy-peer-deps --package-lock=false
|
|
1055
|
+
npm run build --prefix v3/@claude-flow/codex
|
|
1056
|
+
node v3/@claude-flow/codex/dist/cli.js activity \
|
|
1057
|
+
--session-file /path/to/sessions/parent-rollout.jsonl \
|
|
1058
|
+
--sessions-dir /path/to/sessions
|
|
1059
|
+
```
|
|
1060
|
+
|
|
1061
|
+
Choose the parent rollout explicitly. Only helpers whose recorded parent chain
|
|
1062
|
+
leads back to that session appear. The selected directory must contain both the
|
|
1063
|
+
parent and helper files; use the smallest directory that contains them all.
|
|
1064
|
+
|
|
1065
|
+
Use **j/k** or the arrow keys to select a helper, then **Enter** to open it.
|
|
1066
|
+
In the detail view, **j/k** scroll, **Space** moves down a page, **g** goes to the
|
|
1067
|
+
top, **G** follows the latest activity, and **b** or **Escape** returns to the list.
|
|
1068
|
+
Press **q** to quit; the Codex session keeps running.
|
|
1069
|
+
|
|
1070
|
+
Use `--agent <thread-id>` to open a specific helper, `--once` to print a snapshot
|
|
1071
|
+
without an interactive terminal, or `--interval <seconds>` to change the refresh
|
|
1072
|
+
interval (default: 2 seconds; range: 0.2–60).
|
|
1073
|
+
|
|
1074
|
+
These screenshots use made-up session records:
|
|
1075
|
+
|
|
1076
|
+

|
|
1077
|
+
|
|
1078
|
+

|
|
1079
|
+
|
|
1080
|
+
### What the viewer can read
|
|
1081
|
+
|
|
1082
|
+
The parser supports CLI rollout files with a `session_meta` header, helper ancestry
|
|
1083
|
+
in `source.subagent.thread_spawn.parent_thread_id`, and an own-thread marker
|
|
1084
|
+
(`payload.thread_id`) before helper activity. Public message, function/custom tool
|
|
1085
|
+
call and result records, and supported task/item events are shown. Formats without
|
|
1086
|
+
these attribution fields may produce an empty list or unavailable activity.
|
|
1087
|
+
|
|
1088
|
+
Copied parent history, reasoning, and system/developer messages are excluded.
|
|
1089
|
+
Ambiguous identities and paths through symlinks are rejected. The viewer strips
|
|
1090
|
+
terminal controls and masks common credential patterns before shortening previews.
|
|
1091
|
+
Masking cannot catch every secret in arbitrary command output: check the visible
|
|
1092
|
+
content before sharing a screenshot.
|
|
1093
|
+
|
|
1094
|
+
State is the last **recorded** state, not a heartbeat. Discovery stops after 4,000
|
|
1095
|
+
entries or eight directory levels; ancestry is limited to 64 links. Each detail
|
|
1096
|
+
refresh reads at most 1 MiB, lines are limited to 64 KiB, previews to 2,400 characters,
|
|
1097
|
+
and history to 80 events. The metadata header must include its terminating newline
|
|
1098
|
+
within the first 64 KiB; an oversized or incomplete header excludes that file.
|
|
1099
|
+
The screen labels incomplete scans, omitted records and logs that are still being
|
|
1100
|
+
read. Missing data stays unavailable.
|
|
1101
|
+
|
|
1102
|
+
### Check the viewer locally
|
|
1103
|
+
|
|
1104
|
+
```bash
|
|
1105
|
+
cd v3/@claude-flow/codex
|
|
1106
|
+
npm test -- --run tests/activity-files.test.ts tests/activity-parser.test.ts tests/activity-viewer.test.ts
|
|
1107
|
+
cd ../../..
|
|
1108
|
+
node scripts/smoke-codex-activity.mjs
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
The Node smoke check runs the built CLI against synthetic files and checks the
|
|
1112
|
+
picker, detail output, history isolation, invalid input and read-only access.
|
|
1113
|
+
The screenshots above were captured from the interactive viewer using synthetic
|
|
1114
|
+
records; screenshot generation is not part of CI.
|
|
1115
|
+
|
|
1116
|
+
### Release integration
|
|
1117
|
+
|
|
1118
|
+
Ship the viewer in `@claude-flow/codex` with the next coordinated Ruflo release.
|
|
1119
|
+
At release, bump the package version and exported `VERSION` together, update the
|
|
1120
|
+
bundled dependency pins and lockfile, and include the built package in the release
|
|
1121
|
+
train.
|
|
1122
|
+
|
|
1045
1123
|
## Related Packages
|
|
1046
1124
|
|
|
1047
1125
|
| Package | Description |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/activity/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAwB,MAAM,WAAW,CAAC;AAO1D,wBAAgB,qBAAqB,IAAI,OAAO,CA6B/C"}
|