moflo 4.12.4 → 4.12.6
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/guidance/shipped/moflo-agent-rules.md +1 -0
- package/.claude/guidance/shipped/moflo-claude-swarm-cohesion.md +1 -1
- package/.claude/guidance/shipped/moflo-cli-reference.md +4 -4
- package/.claude/guidance/shipped/moflo-core-guidance.md +4 -4
- package/.claude/guidance/shipped/moflo-guidance-rules.md +21 -0
- package/.claude/guidance/shipped/moflo-inline-fixes.md +98 -0
- package/.claude/guidance/shipped/moflo-root-cause-discipline.md +10 -10
- package/.claude/guidance/shipped/moflo-sdd.md +5 -5
- package/.claude/guidance/shipped/moflo-spell-engine.md +1 -1
- package/.claude/guidance/shipped/moflo-yaml-reference.md +8 -8
- package/.claude/helpers/gate.cjs +46 -7
- package/.claude/helpers/pr-create-command.cjs +440 -0
- package/.claude/skills/eldar/SKILL.md +1 -1
- package/.claude/skills/fl/SKILL.md +6 -6
- package/.claude/skills/fl/execution-modes.md +3 -3
- package/.claude/skills/fl/phases.md +8 -8
- package/.claude/skills/fl/sdd.md +6 -6
- package/.claude/skills/guidance/SKILL.md +3 -0
- package/.claude/skills/verify/SKILL.md +4 -4
- package/bin/gate.cjs +46 -7
- package/bin/hooks.mjs +4 -1
- package/bin/lib/retired-files.mjs +38 -6
- package/bin/lib/shipped-scripts.json +2 -1
- package/bin/lib/skill-categories.mjs +81 -17
- package/bin/pr-create-command.cjs +440 -0
- package/bin/session-start-launcher.mjs +11 -6
- package/dist/src/cli/aidefence/domain/entities/threat.js +2 -1
- package/dist/src/cli/commands/doctor-checks-deep.js +3 -2
- package/dist/src/cli/commands/doctor-checks-memory-access.js +3 -2
- package/dist/src/cli/commands/doctor-checks-swarm.js +3 -2
- package/dist/src/cli/commands/doctor-fixes.js +119 -20
- package/dist/src/cli/commands/hooks.js +2 -1
- package/dist/src/cli/commands/mcp.js +3 -2
- package/dist/src/cli/commands/memory.js +2 -1
- package/dist/src/cli/commands/performance.js +2 -1
- package/dist/src/cli/commands/retire.js +1 -1
- package/dist/src/cli/commands/spell-schedule.js +2 -1
- package/dist/src/cli/commands/swarm.js +216 -104
- package/dist/src/cli/guidance/headless.js +5 -8
- package/dist/src/cli/guidance/retriever.js +27 -6
- package/dist/src/cli/guidance/ruvbot-integration.js +2 -1
- package/dist/src/cli/guidance/uncertainty.js +2 -1
- package/dist/src/cli/hooks/bridge/official-hooks-bridge.js +2 -1
- package/dist/src/cli/hooks/mcp/index.js +3 -2
- package/dist/src/cli/hooks/reasoningbank/index.js +2 -1
- package/dist/src/cli/hooks/registry/index.js +2 -1
- package/dist/src/cli/hooks/swarm/index.js +6 -5
- package/dist/src/cli/init/claudemd-generator.js +2 -1
- package/dist/src/cli/init/helpers-generator.js +1 -1
- package/dist/src/cli/init/moflo-yaml-template.js +4 -4
- package/dist/src/cli/mcp-client.js +5 -2
- package/dist/src/cli/mcp-tools/agent-tools.js +4 -1
- package/dist/src/cli/mcp-tools/hive-mind-tools.js +4 -3
- package/dist/src/cli/mcp-tools/hooks-tools.js +6 -5
- package/dist/src/cli/mcp-tools/json-store.js +3 -2
- package/dist/src/cli/mcp-tools/neural-tools.js +2 -1
- package/dist/src/cli/mcp-tools/performance-tools.js +2 -1
- package/dist/src/cli/mcp-tools/session-tools.js +4 -2
- package/dist/src/cli/mcp-tools/spell-tools.js +2 -1
- package/dist/src/cli/memory/bridge-core.js +8 -2
- package/dist/src/cli/memory/controllers/_shared.js +8 -2
- package/dist/src/cli/memory/controllers/nightly-learner.js +2 -1
- package/dist/src/cli/memory/domain/services/memory-domain-service.js +2 -1
- package/dist/src/cli/memory/entries-write.js +2 -1
- package/dist/src/cli/memory/intelligence.js +2 -1
- package/dist/src/cli/memory/persistent-sona.js +1 -3
- package/dist/src/cli/memory/types.js +2 -3
- package/dist/src/cli/memory/verify.js +3 -2
- package/dist/src/cli/movector/graph-analyzer.js +14 -3
- package/dist/src/cli/neural/pattern-learner.js +4 -2
- package/dist/src/cli/neural/reasoning-bank.js +2 -1
- package/dist/src/cli/neural/reasoningbank-adapter.js +2 -1
- package/dist/src/cli/neural/sona-manager.js +4 -3
- package/dist/src/cli/production/monitoring.js +2 -1
- package/dist/src/cli/services/daemon-service.js +57 -46
- package/dist/src/cli/services/daemon-spell-executor.js +3 -2
- package/dist/src/cli/services/headless-worker-executor.js +4 -2
- package/dist/src/cli/services/learning-service.js +2 -1
- package/dist/src/cli/services/project-root.js +17 -1
- package/dist/src/cli/services/worker-daemon.js +2 -1
- package/dist/src/cli/shared/hooks/example-usage.js +3 -2
- package/dist/src/cli/shared/hooks/safety/git-commit.js +11 -2
- package/dist/src/cli/shared/hooks/session-hooks.js +3 -2
- package/dist/src/cli/shared/hooks/task-hooks.js +2 -1
- package/dist/src/cli/shared/mcp/session-manager.js +2 -1
- package/dist/src/cli/shared/plugins/official/hive-mind-plugin.js +2 -1
- package/dist/src/cli/shared/plugins/official/maestro-plugin.js +3 -2
- package/dist/src/cli/shared/security/index.js +6 -2
- package/dist/src/cli/shared/utils/atomic-file-write.js +9 -1
- package/dist/src/cli/shared/utils/id.js +54 -0
- package/dist/src/cli/shared/utils/platform.js +36 -5
- package/dist/src/cli/spells/commands/destructive-pattern-checker.js +6 -1
- package/dist/src/cli/spells/core/dry-run-validator.js +2 -1
- package/dist/src/cli/spells/core/interpolation.js +8 -3
- package/dist/src/cli/spells/core/permission-resolver.js +2 -2
- package/dist/src/cli/spells/core/runner.js +2 -1
- package/dist/src/cli/spells/core/shell.js +7 -8
- package/dist/src/cli/spells/factory/runner-bridge.js +3 -2
- package/dist/src/cli/spells/factory/runner-factory.js +3 -2
- package/dist/src/cli/spells/scheduler/scheduler.js +2 -1
- package/dist/src/cli/swarm/consensus/gossip.js +2 -1
- package/dist/src/cli/swarm/federation-hub.js +5 -4
- package/dist/src/cli/swarm/queen-coordinator.js +5 -4
- package/dist/src/cli/swarm/shared/events.js +2 -1
- package/dist/src/cli/swarm/unified-coordinator.js +3 -2
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
- package/retired-files.json +0 -72
- package/dist/src/cli/mcp-tools/coordination-tools.js +0 -86
- package/dist/src/cli/shared/security/secure-random.js +0 -142
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
import { EventEmitter } from 'node:events';
|
|
11
11
|
import { reasoningBank } from '../reasoningbank/index.js';
|
|
12
12
|
import { MessageBus } from '../../swarm/message-bus/message-bus.js';
|
|
13
|
+
import { generateId } from '../../shared/utils/id.js';
|
|
13
14
|
const DEFAULT_CONFIG = {
|
|
14
|
-
agentId:
|
|
15
|
+
agentId: generateId('agent', { separator: '_' }),
|
|
15
16
|
agentName: 'anonymous',
|
|
16
17
|
messageRetention: 3600000, // 1 hour
|
|
17
18
|
consensusTimeout: 30000, // 30 seconds
|
|
@@ -256,7 +257,7 @@ export class SwarmCommunication extends EventEmitter {
|
|
|
256
257
|
async broadcastPattern(pattern, targetAgents) {
|
|
257
258
|
await this.ensureInitialized();
|
|
258
259
|
const broadcast = {
|
|
259
|
-
id:
|
|
260
|
+
id: generateId('bc', { separator: '_' }),
|
|
260
261
|
sourceAgent: this.config.agentId,
|
|
261
262
|
pattern,
|
|
262
263
|
broadcastTime: Date.now(),
|
|
@@ -348,7 +349,7 @@ export class SwarmCommunication extends EventEmitter {
|
|
|
348
349
|
async initiateConsensus(question, options, timeout) {
|
|
349
350
|
await this.ensureInitialized();
|
|
350
351
|
const request = {
|
|
351
|
-
id:
|
|
352
|
+
id: generateId('cons', { separator: '_' }),
|
|
352
353
|
initiator: this.config.agentId,
|
|
353
354
|
question,
|
|
354
355
|
options,
|
|
@@ -477,8 +478,8 @@ export class SwarmCommunication extends EventEmitter {
|
|
|
477
478
|
async initiateHandoff(toAgent, taskDescription, context) {
|
|
478
479
|
await this.ensureInitialized();
|
|
479
480
|
const handoff = {
|
|
480
|
-
id:
|
|
481
|
-
taskId:
|
|
481
|
+
id: generateId('ho', { separator: '_' }),
|
|
482
|
+
taskId: generateId('task', { separator: '_' }),
|
|
482
483
|
description: taskDescription,
|
|
483
484
|
fromAgent: this.config.agentId,
|
|
484
485
|
toAgent,
|
|
@@ -50,11 +50,12 @@ After \`npm install\` touches moflo, check \`.moflo/restart-pending.json\` — i
|
|
|
50
50
|
|
|
51
51
|
### Monorepos
|
|
52
52
|
|
|
53
|
-
Moflo state lives at the monorepo root \`.moflo/\` — never run \`flo init\` inside a sub-workspace of an existing moflo project, or the MCP server and CLI silently bind to different daemons
|
|
53
|
+
Moflo state lives at the monorepo root \`.moflo/\` — never run \`flo init\` inside a sub-workspace of an existing moflo project, or the MCP server and CLI silently bind to different daemons.
|
|
54
54
|
|
|
55
55
|
### Full Reference
|
|
56
56
|
|
|
57
57
|
- Universal agent rules (memory protocol, git/PR conventions, file org, build/test): \`.claude/guidance/moflo-agent-rules.md\`
|
|
58
|
+
- Fix small defects in the PR you are already in — don't file them: \`.claude/guidance/moflo-inline-fixes.md\`
|
|
58
59
|
- Subagent spawn protocol: \`.claude/guidance/moflo-subagents.md\`
|
|
59
60
|
- Task + swarm coordination: \`.claude/guidance/moflo-claude-swarm-cohesion.md\`
|
|
60
61
|
- CLI, hooks, swarm, memory, moflo.yaml: \`.claude/guidance/moflo-core-guidance.md\`
|
|
@@ -983,7 +983,7 @@ switch (command) {
|
|
|
983
983
|
if (!/(?:^|&&\\s*|\\|\\|\\s*|;\\s*)\\s*(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*gh\\s+pr\\s+create\\b/.test(cmd)) break;
|
|
984
984
|
var s = readState();
|
|
985
985
|
var missing = [];
|
|
986
|
-
if (config.testing_gate && !s.testsRun) missing.push('tests have not run green since the last code edit (run npm test, vitest, jest, pytest, or similar — a run whose output reports failures does not count
|
|
986
|
+
if (config.testing_gate && !s.testsRun) missing.push('tests have not run green since the last code edit (run npm test, vitest, jest, pytest, or similar — a run whose output reports failures does not count)');
|
|
987
987
|
if (config.simplify_gate && !s.simplifyRun) missing.push('/flo-simplify (or /distill) has not run since the last code edit');
|
|
988
988
|
if (config.learnings_gate && !s.learningsStored) missing.push('learnings have not been stored (call mcp__moflo__memory_store)');
|
|
989
989
|
if (missing.length === 0) break;
|
|
@@ -214,7 +214,7 @@ gates:
|
|
|
214
214
|
memory_first: ${gates}
|
|
215
215
|
task_create_first: ${gates}
|
|
216
216
|
context_tracking: ${gates}
|
|
217
|
-
verify_before_done: true #
|
|
217
|
+
verify_before_done: true # Run /verify before 'gh pr create'. On by default; opt out with false or per-run --no-verify
|
|
218
218
|
|
|
219
219
|
# Auto-index on session start
|
|
220
220
|
auto_index:
|
|
@@ -233,7 +233,7 @@ session_continuity:
|
|
|
233
233
|
inject: true
|
|
234
234
|
max_age_hours: 72 # ignore digests older than this when injecting
|
|
235
235
|
|
|
236
|
-
# Auto-meditate
|
|
236
|
+
# Auto-meditate — the automatic counterpart to /meditate. When enabled,
|
|
237
237
|
# moflo recognizes durable lessons in the LIVE session (a tiny answer-first note
|
|
238
238
|
# on course-corrections / errors / decisions) and distills them into long-term
|
|
239
239
|
# memory at the next session-start via a cheap headless Haiku pass — deduped.
|
|
@@ -241,12 +241,12 @@ session_continuity:
|
|
|
241
241
|
auto_meditate:
|
|
242
242
|
enabled: true
|
|
243
243
|
|
|
244
|
-
# Spec-Driven Development
|
|
244
|
+
# Spec-Driven Development — spec -> plan -> implement -> verify.
|
|
245
245
|
# When default is true, every /flo run uses the SDD cycle unless --no-sdd.
|
|
246
246
|
sdd:
|
|
247
247
|
default: false
|
|
248
248
|
|
|
249
|
-
# Auto-merge the PR at the end of a full /flo run once preconditions are met
|
|
249
|
+
# Auto-merge the PR at the end of a full /flo run once preconditions are met.
|
|
250
250
|
# When auto is true, a full /flo run awaits required checks + MERGEABLE state then
|
|
251
251
|
# merges and deletes the branch, instead of stopping at "PR opened". Opt-in;
|
|
252
252
|
# override per-run with --merge / --no-merge.
|
|
@@ -23,7 +23,11 @@ import { performanceTools } from './mcp-tools/performance-tools.js';
|
|
|
23
23
|
// #1353 — `github-tools.ts` is gone. Its four tools made no GitHub API call;
|
|
24
24
|
// they wrote local JSON and returned `Math.random()` counts, so a `merged: true`
|
|
25
25
|
// described nothing that happened. Use the `gh` CLI or a GitHub MCP server.
|
|
26
|
-
|
|
26
|
+
// #1428 — `coordination-tools.ts` is gone. Its single tool, `coordination_sync`,
|
|
27
|
+
// incremented a counter, slept 50ms under `// Simulate sync`, wrote a local JSON
|
|
28
|
+
// store and returned `synchronized` — it never touched UnifiedSwarmCoordinator.
|
|
29
|
+
// Its agent-side callers were retired by #932; nothing in the tree invoked it.
|
|
30
|
+
// Same disposition as the `github_*` tools in #1353.
|
|
27
31
|
import { moflodbTools } from './mcp-tools/moflodb-tools.js';
|
|
28
32
|
// #1349 — handlers for tool names shipped CLI commands were already calling.
|
|
29
33
|
import { progressTools } from './mcp-tools/progress-tools.js';
|
|
@@ -56,7 +60,6 @@ registerTools([
|
|
|
56
60
|
...systemTools,
|
|
57
61
|
...neuralTools,
|
|
58
62
|
...performanceTools,
|
|
59
|
-
...coordinationTools,
|
|
60
63
|
...moflodbTools,
|
|
61
64
|
...progressTools,
|
|
62
65
|
...coverageTools,
|
|
@@ -187,7 +187,10 @@ export const agentTools = [
|
|
|
187
187
|
}
|
|
188
188
|
const task = input.task || config.task || undefined;
|
|
189
189
|
const routingResult = await determineAgentModel(agentType, config, task);
|
|
190
|
-
// Math.random()
|
|
190
|
+
// Base-36 slicing of Math.random() was observably colliding under burst
|
|
191
|
+
// spawns (Story #801). Kept inline rather than routed through
|
|
192
|
+
// shared/utils/id.ts because this ID interpolates the agent type in place
|
|
193
|
+
// of a timestamp; every other site now uses the shared minter (#1423).
|
|
191
194
|
const agentId = `agent-${agentType}-${randomBytes(12).toString('hex')}`;
|
|
192
195
|
const capabilities = Array.isArray(config.capabilities)
|
|
193
196
|
? config.capabilities.filter((c) => typeof c === 'string')
|
|
@@ -14,6 +14,7 @@ import { getSwarmCoordinator } from './swarm-coordinator-singleton.js';
|
|
|
14
14
|
import { SUBAGENT_BOOTSTRAP_DIRECTIVE } from '../services/subagent-bootstrap.js';
|
|
15
15
|
import { validateAgentType } from './agent-tools.js';
|
|
16
16
|
import { SWARM_CONSTANTS } from '../swarm/types.js';
|
|
17
|
+
import { generateId } from '../shared/utils/id.js';
|
|
17
18
|
// Namespace constants — avoids hardcoded strings scattered across handlers
|
|
18
19
|
const HIVE_NS = 'hive-mind';
|
|
19
20
|
const HIVE_MEMORY_NS = 'hive-mind-memory';
|
|
@@ -308,8 +309,8 @@ export const hiveMindTools = [
|
|
|
308
309
|
const bus = await getMessageBus();
|
|
309
310
|
// Initialize write-through adapter (configures hive-mind namespace)
|
|
310
311
|
await getWriteThroughAdapter();
|
|
311
|
-
const hiveId =
|
|
312
|
-
const queenId = input.queenId ||
|
|
312
|
+
const hiveId = generateId('hive');
|
|
313
|
+
const queenId = input.queenId || generateId('queen');
|
|
313
314
|
const config = {
|
|
314
315
|
consensus: resolveConsensus(input.consensus),
|
|
315
316
|
maxAgents: resolveMaxAgents(input.maxAgents),
|
|
@@ -535,7 +536,7 @@ export const hiveMindTools = [
|
|
|
535
536
|
const action = input.action;
|
|
536
537
|
const bus = await getMessageBus();
|
|
537
538
|
if (action === 'propose') {
|
|
538
|
-
const proposalId =
|
|
539
|
+
const proposalId = generateId('proposal');
|
|
539
540
|
const proposal = {
|
|
540
541
|
proposalId,
|
|
541
542
|
type: input.type || 'general',
|
|
@@ -8,6 +8,7 @@ import { errorDetail } from '../shared/utils/error-detail.js';
|
|
|
8
8
|
import { findProjectRoot } from '../services/project-root.js';
|
|
9
9
|
import { withSyntheticNotice } from './synthetic.js';
|
|
10
10
|
import { getMofloVersion } from '../services/moflo-version.js';
|
|
11
|
+
import { generateId } from '../shared/utils/id.js';
|
|
11
12
|
// Real vector search functions - lazy loaded to avoid circular imports
|
|
12
13
|
let searchEntriesFn = null;
|
|
13
14
|
async function getRealSearchFunction() {
|
|
@@ -583,7 +584,7 @@ export const hooksPostEdit = {
|
|
|
583
584
|
try {
|
|
584
585
|
const bridge = await import('../memory/memory-bridge.js');
|
|
585
586
|
feedbackResult = await bridge.bridgeRecordFeedback({
|
|
586
|
-
taskId: `edit-${filePath}
|
|
587
|
+
taskId: generateId(`edit-${filePath}`),
|
|
587
588
|
success,
|
|
588
589
|
quality: success ? 0.85 : 0.3,
|
|
589
590
|
agent,
|
|
@@ -1060,7 +1061,7 @@ export const hooksPostTask = {
|
|
|
1060
1061
|
learningUpdates: {
|
|
1061
1062
|
patternsUpdated: feedbackResult?.updated || (success ? 2 : 1),
|
|
1062
1063
|
newPatterns: success ? 1 : 0,
|
|
1063
|
-
trajectoryId:
|
|
1064
|
+
trajectoryId: generateId('traj'),
|
|
1064
1065
|
controller: feedbackResult?.controller || 'none',
|
|
1065
1066
|
outcomePersisted,
|
|
1066
1067
|
},
|
|
@@ -1803,7 +1804,7 @@ export const hooksSessionStart = {
|
|
|
1803
1804
|
},
|
|
1804
1805
|
},
|
|
1805
1806
|
handler: async (params) => {
|
|
1806
|
-
const sessionId = params.sessionId ||
|
|
1807
|
+
const sessionId = params.sessionId || generateId('session');
|
|
1807
1808
|
const restoreLatest = params.restoreLatest;
|
|
1808
1809
|
const shouldStartDaemon = params.startDaemon !== false;
|
|
1809
1810
|
// Auto-start daemon if enabled
|
|
@@ -1968,7 +1969,7 @@ export const hooksSessionRestore = {
|
|
|
1968
1969
|
const restoreAgents = params.restoreAgents !== false;
|
|
1969
1970
|
const restoreTasks = params.restoreTasks !== false;
|
|
1970
1971
|
const originalSessionId = requestedId === 'latest' ? `session-${Date.now() - 86400000}` : requestedId;
|
|
1971
|
-
const newSessionId =
|
|
1972
|
+
const newSessionId = generateId('session');
|
|
1972
1973
|
// Get real memory entry count
|
|
1973
1974
|
const store = loadMemoryStore();
|
|
1974
1975
|
const memoryEntryCount = Object.keys(store.entries).length;
|
|
@@ -2141,7 +2142,7 @@ export const hooksPatternStore = {
|
|
|
2141
2142
|
const confidence = params.confidence || 0.8;
|
|
2142
2143
|
const metadata = params.metadata;
|
|
2143
2144
|
const timestamp = new Date().toISOString();
|
|
2144
|
-
const patternId =
|
|
2145
|
+
const patternId = generateId('pattern');
|
|
2145
2146
|
// Try pattern bridge first (HNSW-indexed raw SQL)
|
|
2146
2147
|
let bridgeResult = null;
|
|
2147
2148
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tiny JSON-on-disk store
|
|
3
|
-
*
|
|
2
|
+
* Tiny JSON-on-disk store backing the trimmed MCP-tool surfaces. `performance-tools`
|
|
3
|
+
* is the sole remaining consumer — `coordination-tools` and `system-tools` were the
|
|
4
|
+
* others until their stores were removed.
|
|
4
5
|
*
|
|
5
6
|
* Anchors files at `<projectRoot>/.moflo/<subdir>/<file>` via {@link mofloDir},
|
|
6
7
|
* with the root resolved by {@link findProjectRoot} so an MCP server launched
|
|
@@ -16,6 +16,7 @@ import { join } from 'node:path';
|
|
|
16
16
|
import { MOFLO_DIR as STORAGE_DIR } from '../services/moflo-paths.js';
|
|
17
17
|
import { findProjectRoot } from '../services/project-root.js';
|
|
18
18
|
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
19
|
+
import { generateId } from '../shared/utils/id.js';
|
|
19
20
|
let realEmbeddings = undefined;
|
|
20
21
|
let embeddingServiceName = 'none';
|
|
21
22
|
let embeddingsInitPromise = null;
|
|
@@ -212,7 +213,7 @@ const rawNeuralTools = [
|
|
|
212
213
|
return { success: true, pattern };
|
|
213
214
|
}
|
|
214
215
|
if (action === 'store') {
|
|
215
|
-
const patternId =
|
|
216
|
+
const patternId = generateId('pattern');
|
|
216
217
|
const patternName = input.name || 'Unnamed pattern';
|
|
217
218
|
// Generate embedding from pattern name/content
|
|
218
219
|
const embedding = await generateEmbedding(patternName, 384);
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import * as os from 'node:os';
|
|
17
17
|
import { createJsonStore } from './json-store.js';
|
|
18
18
|
import { readCpuUsagePercent, readLoadAverage, NOT_MEASURED } from '../shared/utils/load-average.js';
|
|
19
|
+
import { generateId } from '../shared/utils/id.js';
|
|
19
20
|
/**
|
|
20
21
|
* Cross-platform CPU usage, or null where it cannot be measured (Rule #1).
|
|
21
22
|
*
|
|
@@ -212,7 +213,7 @@ const rawPerformanceTools = [
|
|
|
212
213
|
const opsPerSec = Math.round((iterations / durationMs) * 1000);
|
|
213
214
|
const avgLatencyMs = durationMs / iterations;
|
|
214
215
|
const memoryDelta = Math.round((memAfter - memBefore) / 1024);
|
|
215
|
-
const id = `bench-${suiteName}
|
|
216
|
+
const id = generateId(`bench-${suiteName}`);
|
|
216
217
|
const result = {
|
|
217
218
|
id,
|
|
218
219
|
name: suiteName,
|
|
@@ -4,9 +4,11 @@
|
|
|
4
4
|
* Tool definitions for session management with file persistence.
|
|
5
5
|
*/
|
|
6
6
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
|
|
7
|
+
import { randomBytes } from 'node:crypto';
|
|
7
8
|
import { join } from 'node:path';
|
|
8
9
|
import { MOFLO_DIR as STORAGE_DIR } from '../services/moflo-paths.js';
|
|
9
10
|
import { findProjectRoot } from '../services/project-root.js';
|
|
11
|
+
import { generateId } from '../shared/utils/id.js';
|
|
10
12
|
// Storage paths
|
|
11
13
|
const SESSION_DIR = 'sessions';
|
|
12
14
|
function storeDir(...parts) {
|
|
@@ -112,7 +114,7 @@ export const sessionTools = [
|
|
|
112
114
|
required: ['name'],
|
|
113
115
|
},
|
|
114
116
|
handler: async (input) => {
|
|
115
|
-
const sessionId = `session-${Date.now()}-${
|
|
117
|
+
const sessionId = `session-${Date.now()}-${randomBytes(6).toString('hex')}`;
|
|
116
118
|
// Load related data based on options
|
|
117
119
|
const data = loadRelatedStores({
|
|
118
120
|
includeMemory: input.includeMemory,
|
|
@@ -364,7 +366,7 @@ export const sessionTools = [
|
|
|
364
366
|
const importedAt = new Date().toISOString();
|
|
365
367
|
// A fresh ID keeps an import from silently overwriting a local session
|
|
366
368
|
// that happens to share the source's ID.
|
|
367
|
-
const sessionId =
|
|
369
|
+
const sessionId = generateId('imported');
|
|
368
370
|
const agentsImported = incoming.stats?.agents ?? countOf(incoming.data?.agents);
|
|
369
371
|
const tasksImported = incoming.stats?.tasks ?? countOf(incoming.data?.tasks);
|
|
370
372
|
const memoryEntriesImported = incoming.stats?.memoryEntries ?? countOf(incoming.data?.memory);
|
|
@@ -13,6 +13,7 @@ import { buildGrimoire } from '../services/grimoire-builder.js';
|
|
|
13
13
|
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
14
14
|
import { inferSpellTier } from '../spells/core/spell-tier.js';
|
|
15
15
|
import { getSharedMemoryAccessor } from '../services/daemon-dashboard.js';
|
|
16
|
+
import { generateId } from '../shared/utils/id.js';
|
|
16
17
|
// ============================================================================
|
|
17
18
|
// Constants
|
|
18
19
|
// ============================================================================
|
|
@@ -61,7 +62,7 @@ function trackResult(tracked, result) {
|
|
|
61
62
|
// (one cold init per process — see #1020).
|
|
62
63
|
/** Execute a definition via the engine with tracking and error handling. */
|
|
63
64
|
async function executeAndTrack(engine, definition, args, options = {}) {
|
|
64
|
-
const spellId =
|
|
65
|
+
const spellId = generateId('sp');
|
|
65
66
|
const tracked = trackStart(spellId, definition.name, definition.description);
|
|
66
67
|
try {
|
|
67
68
|
const projectRoot = findProjectRoot();
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import * as fs from 'fs';
|
|
9
|
-
import * as crypto from 'crypto';
|
|
10
9
|
import { atomicWriteFileSync } from '../services/atomic-file-write.js';
|
|
11
10
|
import { legacyMemoryDbPath, memoryDbPath, MOFLO_DIR, } from '../services/moflo-paths.js';
|
|
12
11
|
import { resolveStateRoot } from '../services/project-root.js';
|
|
@@ -77,6 +76,7 @@ function getProjectRoot() {
|
|
|
77
76
|
}
|
|
78
77
|
import { ControllerRegistry } from './controller-registry.js';
|
|
79
78
|
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
79
|
+
import { generateId as mintId } from '../shared/utils/id.js';
|
|
80
80
|
let registryPromise = null;
|
|
81
81
|
// Sync handle populated once the promise resolves. Lets sync callers
|
|
82
82
|
// (refreshVectorStatsCache) read the registry without awaiting.
|
|
@@ -233,8 +233,14 @@ export function resolveBridgeDbPath(root, customPath) {
|
|
|
233
233
|
function getDbPath(customPath) {
|
|
234
234
|
return resolveBridgeDbPath(getProjectRoot(), customPath);
|
|
235
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Delegates to the canonical minter, preserving this module's exact ID format
|
|
238
|
+
* (`_` separator, 8 bytes of entropy). The format is pinned rather than
|
|
239
|
+
* normalised because these IDs are persisted to `.moflo/moflo.db` — reshaping
|
|
240
|
+
* them would churn consumer state for no benefit (#1423, Rule #2).
|
|
241
|
+
*/
|
|
236
242
|
export function generateId(prefix) {
|
|
237
|
-
return
|
|
243
|
+
return mintId(prefix, { separator: '_', bytes: 8 });
|
|
238
244
|
}
|
|
239
245
|
/**
|
|
240
246
|
* Returns null if the memory module cannot be loaded or sql.js fails to open —
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Vector-store utilities kept in one place so reflexion / skills /
|
|
5
5
|
* hierarchical-memory don't each reinvent cosine and BLOB serialization.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
7
|
+
import { generateId as mintId } from '../../shared/utils/id.js';
|
|
8
8
|
export function toFloat32(vec) {
|
|
9
9
|
return vec instanceof Float32Array ? vec : Float32Array.from(vec);
|
|
10
10
|
}
|
|
@@ -118,8 +118,14 @@ function keywordOverlap(lowerQuery, lowerContent) {
|
|
|
118
118
|
hits++;
|
|
119
119
|
return hits / tokens.size;
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Delegates to the canonical minter, preserving this module's exact ID format
|
|
123
|
+
* (`-` separator, base36 timestamp, 6 bytes of entropy). The format is pinned
|
|
124
|
+
* rather than normalised because these IDs are persisted to `.moflo/moflo.db` —
|
|
125
|
+
* reshaping them would churn consumer state for no benefit (#1423, Rule #2).
|
|
126
|
+
*/
|
|
121
127
|
export function generateId(prefix) {
|
|
122
|
-
return
|
|
128
|
+
return mintId(prefix, { base36Time: true });
|
|
123
129
|
}
|
|
124
130
|
/**
|
|
125
131
|
* Bounded-value helpers shared by controllers. `clamp01` is the common
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { hasMethod } from './_shared.js';
|
|
17
17
|
import { errorDetail } from '../../shared/utils/error-detail.js';
|
|
18
|
+
import { generateId } from '../../shared/utils/id.js';
|
|
18
19
|
export class NightlyLearner {
|
|
19
20
|
consolidation;
|
|
20
21
|
reflexion;
|
|
@@ -91,7 +92,7 @@ export class NightlyLearner {
|
|
|
91
92
|
const self = this;
|
|
92
93
|
this.timer = setInterval(() => {
|
|
93
94
|
// Fire-and-forget; consolidate swallows its own errors.
|
|
94
|
-
void self.consolidate({ sessionId:
|
|
95
|
+
void self.consolidate({ sessionId: generateId('cron') });
|
|
95
96
|
}, intervalMs);
|
|
96
97
|
if (typeof this.timer.unref === 'function') {
|
|
97
98
|
this.timer.unref();
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module v3/memory/domain/services
|
|
8
8
|
*/
|
|
9
9
|
import { MemoryEntry } from '../entities/memory-entry.js';
|
|
10
|
+
import { generateId } from '../../../shared/utils/id.js';
|
|
10
11
|
/**
|
|
11
12
|
* Memory Domain Service
|
|
12
13
|
*
|
|
@@ -246,7 +247,7 @@ export class MemoryDomainService {
|
|
|
246
247
|
// Create merged entry
|
|
247
248
|
return MemoryEntry.create({
|
|
248
249
|
namespace: primary.namespace,
|
|
249
|
-
key:
|
|
250
|
+
key: generateId('merged', { separator: '_' }),
|
|
250
251
|
value: {
|
|
251
252
|
primary: primary.value,
|
|
252
253
|
related: sorted.slice(1).map((e) => e.value),
|
|
@@ -26,6 +26,7 @@ import { serialiseMetadata } from './bridge-entries.js';
|
|
|
26
26
|
import { logRoutingFault, writeVectorStatsCache } from './entries-shared.js';
|
|
27
27
|
import { writeThroughDurable } from '../services/durable-sync.js';
|
|
28
28
|
import { resolveStateRoot } from '../services/project-root.js';
|
|
29
|
+
import { generateId } from '../shared/utils/id.js';
|
|
29
30
|
/**
|
|
30
31
|
* Propagate a just-persisted durable write to the shared store (#1232). The
|
|
31
32
|
* caller passes the project root the row actually landed under so the flush
|
|
@@ -148,7 +149,7 @@ export async function storeEntry(options) {
|
|
|
148
149
|
// Ensure schema has all required columns (migration for older DBs)
|
|
149
150
|
await ensureSchemaColumns(dbPath);
|
|
150
151
|
const db = openDaemonDatabase(dbPath);
|
|
151
|
-
const id =
|
|
152
|
+
const id = generateId('entry', { separator: '_' });
|
|
152
153
|
const now = Date.now();
|
|
153
154
|
// generateEmbedding() throws on embed failure; the outer try/catch returns
|
|
154
155
|
// success:false rather than inserting a null-embedded row. Opt-out rows
|
|
@@ -15,6 +15,7 @@ import { join } from 'node:path';
|
|
|
15
15
|
import { errorDetail } from '../shared/utils/error-detail.js';
|
|
16
16
|
import { findProjectRoot } from '../services/project-root.js';
|
|
17
17
|
import { MOFLO_DIR, mofloHomeDir } from '../services/moflo-paths.js';
|
|
18
|
+
import { generateId } from '../shared/utils/id.js';
|
|
18
19
|
// ============================================================================
|
|
19
20
|
// Persistence Configuration
|
|
20
21
|
// ============================================================================
|
|
@@ -509,7 +510,7 @@ export async function recordStep(step) {
|
|
|
509
510
|
// Store in ReasoningBank for retrieval
|
|
510
511
|
if (reasoningBank) {
|
|
511
512
|
reasoningBank.store({
|
|
512
|
-
id:
|
|
513
|
+
id: generateId('step', { separator: '_' }),
|
|
513
514
|
type: step.type,
|
|
514
515
|
embedding,
|
|
515
516
|
content: step.content,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* @module cli/memory/persistent-sona
|
|
13
13
|
*/
|
|
14
14
|
import { RvfLearningStore, } from './rvf-learning-store.js';
|
|
15
|
+
import { generateId } from '../shared/utils/id.js';
|
|
15
16
|
// ===== Constants =====
|
|
16
17
|
const DEFAULT_PATTERN_THRESHOLD = 0.85;
|
|
17
18
|
const DEFAULT_MAX_TRAJECTORY_BUFFER = 1000;
|
|
@@ -41,9 +42,6 @@ function cosineSimilarity(a, b) {
|
|
|
41
42
|
const denom = Math.sqrt(normA) * Math.sqrt(normB);
|
|
42
43
|
return denom > 0 ? dot / denom : 0;
|
|
43
44
|
}
|
|
44
|
-
function generateId(prefix) {
|
|
45
|
-
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
46
|
-
}
|
|
47
45
|
// ===== PersistentSonaCoordinator =====
|
|
48
46
|
/**
|
|
49
47
|
* Coordinates SONA learning with persistent storage.
|
|
@@ -6,13 +6,12 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module v3/memory/types
|
|
8
8
|
*/
|
|
9
|
+
import { generateId } from '../shared/utils/id.js';
|
|
9
10
|
/**
|
|
10
11
|
* Generates a unique memory ID
|
|
11
12
|
*/
|
|
12
13
|
export function generateMemoryId() {
|
|
13
|
-
|
|
14
|
-
const random = Math.random().toString(36).substring(2, 10);
|
|
15
|
-
return `mem_${timestamp}_${random}`;
|
|
14
|
+
return generateId('mem', { separator: '_', base36Time: true });
|
|
16
15
|
}
|
|
17
16
|
/**
|
|
18
17
|
* Creates a default memory entry
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { openDaemonDatabase } from './daemon-backend.js';
|
|
12
12
|
import { generateEmbedding } from './embedding-model.js';
|
|
13
|
+
import { generateId } from '../shared/utils/id.js';
|
|
13
14
|
/**
|
|
14
15
|
* Verify memory initialization works correctly
|
|
15
16
|
* Tests: write, read, search, patterns
|
|
@@ -33,7 +34,7 @@ export async function verifyMemoryInit(dbPath, options) {
|
|
|
33
34
|
});
|
|
34
35
|
// Test 2: Write entry
|
|
35
36
|
const writeStart = Date.now();
|
|
36
|
-
const testId =
|
|
37
|
+
const testId = generateId('test', { separator: '_' });
|
|
37
38
|
const testKey = 'verification_test';
|
|
38
39
|
const testValue = 'This is a verification test entry for memory initialization';
|
|
39
40
|
try {
|
|
@@ -104,7 +105,7 @@ export async function verifyMemoryInit(dbPath, options) {
|
|
|
104
105
|
// Test 5: Pattern storage
|
|
105
106
|
const patternStart = Date.now();
|
|
106
107
|
try {
|
|
107
|
-
const patternId =
|
|
108
|
+
const patternId = generateId('pattern', { separator: '_' });
|
|
108
109
|
db.run(`
|
|
109
110
|
INSERT INTO patterns (id, name, pattern_type, condition, action, confidence, created_at, updated_at)
|
|
110
111
|
VALUES (?, 'test-pattern', 'task-routing', 'test condition', 'test action', 0.5, ?, ?)
|
|
@@ -62,8 +62,15 @@ async function loadMovector() {
|
|
|
62
62
|
*/
|
|
63
63
|
function extractImports(content, _filePath) {
|
|
64
64
|
const imports = [];
|
|
65
|
-
// ES6 import statements
|
|
66
|
-
|
|
65
|
+
// ES6 import statements. Clause items are separated by a REQUIRED comma
|
|
66
|
+
// rather than the old optional `\s*,?\s*`: with the separator optional, the
|
|
67
|
+
// outer `*` could split a run of word characters many ways, and the regex
|
|
68
|
+
// backtracked exponentially on a pathological source file during indexing
|
|
69
|
+
// (#1418).
|
|
70
|
+
// `(?:type\s+)?` is explicit because the old optional separator absorbed the
|
|
71
|
+
// `type` modifier by accident; without it `import type { T } from 'x'` — the
|
|
72
|
+
// dominant form in this codebase — would stop producing a graph edge.
|
|
73
|
+
const esImportRegex = /import\s+(?:type\s+)?(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)(?:\s*,\s*(?:\{[^}]*\}|\*\s+as\s+\w+|\w+))*\s*from\s*['"]([^'"]+)['"]/g;
|
|
67
74
|
let match;
|
|
68
75
|
while ((match = esImportRegex.exec(content)) !== null) {
|
|
69
76
|
imports.push({ path: match[1], type: 'import' });
|
|
@@ -892,5 +899,9 @@ export function exportToDot(result, options = {}) {
|
|
|
892
899
|
// ============================================================================
|
|
893
900
|
// Exports
|
|
894
901
|
// ============================================================================
|
|
895
|
-
export { loadMovector, fallbackMinCut, fallbackLouvain,
|
|
902
|
+
export { loadMovector, fallbackMinCut, fallbackLouvain,
|
|
903
|
+
// Exported for the #1418 ReDoS regression test: the import regex is only
|
|
904
|
+
// reachable through file I/O otherwise, and a timing assertion needs to call
|
|
905
|
+
// it directly.
|
|
906
|
+
extractImports, };
|
|
896
907
|
//# sourceMappingURL=graph-analyzer.js.map
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Implements pattern extraction, matching, and evolution for
|
|
5
5
|
* continuous learning from agent experiences.
|
|
6
6
|
*/
|
|
7
|
+
import { generateId } from '../shared/utils/id.js';
|
|
8
|
+
import { randomSuffix } from '../shared/utils/id.js';
|
|
7
9
|
/**
|
|
8
10
|
* Default Pattern Learner configuration
|
|
9
11
|
*/
|
|
@@ -106,7 +108,7 @@ export class PatternLearner {
|
|
|
106
108
|
}
|
|
107
109
|
// Create new pattern
|
|
108
110
|
const pattern = {
|
|
109
|
-
patternId:
|
|
111
|
+
patternId: generateId('pat', { separator: '_' }),
|
|
110
112
|
name: this.generatePatternName(trajectory),
|
|
111
113
|
domain: trajectory.domain,
|
|
112
114
|
embedding,
|
|
@@ -247,7 +249,7 @@ export class PatternLearner {
|
|
|
247
249
|
newEmbedding[j] = pattern.embedding[j] + noise;
|
|
248
250
|
}
|
|
249
251
|
const newPattern = {
|
|
250
|
-
patternId: `pat_${Date.now()}_${i}_${
|
|
252
|
+
patternId: `pat_${Date.now()}_${i}_${randomSuffix()}`,
|
|
251
253
|
name: `${pattern.name}_split_${i}`,
|
|
252
254
|
domain: pattern.domain,
|
|
253
255
|
embedding: newEmbedding,
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// neural/index → here would leave MofloDbAdapter undefined at module load.
|
|
15
15
|
import { MofloDbAdapter } from '../memory/moflo-db-adapter.js';
|
|
16
16
|
import { createDefaultEntry } from '../memory/types.js';
|
|
17
|
+
import { generateId } from '../shared/utils/id.js';
|
|
17
18
|
/**
|
|
18
19
|
* Default ReasoningBank configuration
|
|
19
20
|
*/
|
|
@@ -284,7 +285,7 @@ export class ReasoningBank {
|
|
|
284
285
|
// Compute aggregated embedding
|
|
285
286
|
const embedding = this.computeAggregateEmbedding(trajectory);
|
|
286
287
|
const memory = {
|
|
287
|
-
memoryId:
|
|
288
|
+
memoryId: generateId('mem', { separator: '_' }),
|
|
288
289
|
trajectoryId: trajectory.trajectoryId,
|
|
289
290
|
strategy,
|
|
290
291
|
keyLearnings,
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { createSONAManager } from './sona-manager.js';
|
|
13
13
|
import { createPatternLearner } from './pattern-learner.js';
|
|
14
|
+
import { generateId } from '../shared/utils/id.js';
|
|
14
15
|
// ============================================================================
|
|
15
16
|
// ReasoningBank Adapter Implementation
|
|
16
17
|
// ============================================================================
|
|
@@ -149,7 +150,7 @@ export class ReasoningBankAdapter {
|
|
|
149
150
|
const patterns = this.extractPatternsFromTrajectory(trajectory, verdict);
|
|
150
151
|
for (let i = 0; i < Math.min(patterns.length, maxItems); i++) {
|
|
151
152
|
const pattern = patterns[i];
|
|
152
|
-
const id =
|
|
153
|
+
const id = generateId('mem', { separator: '_' });
|
|
153
154
|
const rbPattern = {
|
|
154
155
|
id,
|
|
155
156
|
type: 'reasoning_memory',
|
|
@@ -15,6 +15,7 @@ import { BalancedMode } from './modes/balanced.js';
|
|
|
15
15
|
import { ResearchMode } from './modes/research.js';
|
|
16
16
|
import { EdgeMode } from './modes/edge.js';
|
|
17
17
|
import { BatchMode } from './modes/batch.js';
|
|
18
|
+
import { generateId } from '../shared/utils/id.js';
|
|
18
19
|
/**
|
|
19
20
|
* Default mode configurations
|
|
20
21
|
*/
|
|
@@ -210,7 +211,7 @@ export class SONAManager {
|
|
|
210
211
|
*/
|
|
211
212
|
beginTrajectory(context, domain = 'general') {
|
|
212
213
|
const startTime = performance.now();
|
|
213
|
-
const trajectoryId =
|
|
214
|
+
const trajectoryId = generateId('traj', { separator: '_' });
|
|
214
215
|
const trajectory = {
|
|
215
216
|
trajectoryId,
|
|
216
217
|
context,
|
|
@@ -313,7 +314,7 @@ export class SONAManager {
|
|
|
313
314
|
const startTime = performance.now();
|
|
314
315
|
const fullPattern = {
|
|
315
316
|
...pattern,
|
|
316
|
-
patternId:
|
|
317
|
+
patternId: generateId('pat', { separator: '_' }),
|
|
317
318
|
createdAt: Date.now(),
|
|
318
319
|
updatedAt: Date.now(),
|
|
319
320
|
};
|
|
@@ -399,7 +400,7 @@ export class SONAManager {
|
|
|
399
400
|
initializeLoRAWeights(domain = 'default') {
|
|
400
401
|
const config = this.getLoRAConfig();
|
|
401
402
|
const weights = {
|
|
402
|
-
adapterId: `lora_${domain}_
|
|
403
|
+
adapterId: generateId(`lora_${domain}`, { separator: '_' }),
|
|
403
404
|
A: new Map(),
|
|
404
405
|
B: new Map(),
|
|
405
406
|
createdAt: Date.now(),
|