moflo 4.13.3 → 4.13.5
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/bin/generate-code-map.mjs +13 -123
- package/bin/index-guidance.mjs +37 -4
- package/bin/index-patterns.mjs +15 -25
- package/bin/lib/index-fingerprint.mjs +15 -2
- package/bin/lib/source-scope.mjs +219 -0
- package/bin/session-start-launcher.mjs +21 -0
- package/dist/src/cli/commands/memory-audit-learnings.js +5 -39
- package/dist/src/cli/commands/memory.js +5 -0
- package/dist/src/cli/memory/bridge-embedder.js +5 -0
- package/dist/src/cli/memory/learnings-audit-state.js +53 -0
- package/dist/src/cli/memory/learnings-audit.js +16 -1
- package/dist/src/cli/services/durable-key-rules.js +70 -0
- package/dist/src/cli/services/durable-store-io.js +32 -2
- package/dist/src/cli/services/durable-sync.js +21 -6
- package/dist/src/cli/services/ephemeral-namespace-purge.js +61 -24
- package/dist/src/cli/services/snapshot-restore.js +1 -1
- package/dist/src/cli/services/team-artifact-sync.js +26 -7
- package/dist/src/cli/version.js +1 -1
- package/package.json +2 -2
|
@@ -72,6 +72,7 @@ import { isDurableNamespace } from './cherry-pick-learnings.js';
|
|
|
72
72
|
import { planReconcile, reconcileId, splitReconcileId, isPrunableTombstone, recordStamp, TOMBSTONE_TTL_MS, } from './durable-reconcile.js';
|
|
73
73
|
import { readDurableSnapshot, applyDurableActions, } from './durable-store-io.js';
|
|
74
74
|
import { detectToolCallMarkup } from '../memory/tool-call-markup.js';
|
|
75
|
+
import { isMisfiledDurable } from './durable-key-rules.js';
|
|
75
76
|
/** Allowed `type` values — the schema CHECK set. An out-of-set value would make
|
|
76
77
|
* INSERT OR IGNORE silently drop a hand-edited artifact row, so we coerce. */
|
|
77
78
|
const VALID_TYPES = new Set([
|
|
@@ -208,8 +209,9 @@ function readArtifact(artifactPath) {
|
|
|
208
209
|
const lines = new Map();
|
|
209
210
|
const records = new Map();
|
|
210
211
|
let malformed = 0;
|
|
212
|
+
let misfiled = 0;
|
|
211
213
|
if (!fs.existsSync(artifactPath))
|
|
212
|
-
return { lines, records, malformed, existed: false };
|
|
214
|
+
return { lines, records, malformed, misfiled, existed: false };
|
|
213
215
|
const raw = fs.readFileSync(artifactPath, 'utf-8');
|
|
214
216
|
for (const line of raw.split(/\r?\n/)) {
|
|
215
217
|
const trimmed = line.trim();
|
|
@@ -222,6 +224,13 @@ function readArtifact(artifactPath) {
|
|
|
222
224
|
}
|
|
223
225
|
const id = lineId(parsed);
|
|
224
226
|
const record = lineToRecord(parsed);
|
|
227
|
+
// Dropped at the read both directions share, so import never applies one
|
|
228
|
+
// and export rewrites the file without it — the artifact shrinks instead of
|
|
229
|
+
// re-seeding every teammate's `learnings` on their next session (#1495).
|
|
230
|
+
if (isMisfiledDurable(record.namespace, record.key)) {
|
|
231
|
+
misfiled++;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
225
234
|
const existing = records.get(id);
|
|
226
235
|
// Newest wins, ties keep the first. Built on the merge rule's own
|
|
227
236
|
// comparison basis so the two cannot disagree about which of a live line
|
|
@@ -231,7 +240,7 @@ function readArtifact(artifactPath) {
|
|
|
231
240
|
lines.set(id, parsed);
|
|
232
241
|
records.set(id, record);
|
|
233
242
|
}
|
|
234
|
-
return { lines, records, malformed, existed: true };
|
|
243
|
+
return { lines, records, malformed, misfiled, existed: true };
|
|
235
244
|
}
|
|
236
245
|
/**
|
|
237
246
|
* Serialise lines to JSONL, sorted by the (namespace, key) each line is about —
|
|
@@ -296,8 +305,15 @@ function payloadFromLine(id, line) {
|
|
|
296
305
|
tags: line.tags ? JSON.stringify(line.tags) : null,
|
|
297
306
|
metadata: JSON.stringify({ provenance: line.provenance, sharedFrom: 'team-artifact' }),
|
|
298
307
|
ownerId: line.provenance?.author || null,
|
|
299
|
-
// created_at/updated_at are INTEGER NOT NULL — never bind null.
|
|
300
|
-
|
|
308
|
+
// created_at/updated_at are INTEGER NOT NULL — never bind null. A line with
|
|
309
|
+
// no created_at falls back to its edit time, never the import time: stamping
|
|
310
|
+
// now makes a re-imported old entry read as a new learning, which is what
|
|
311
|
+
// inflated growth metrics after every re-seed (#1495).
|
|
312
|
+
createdAt: typeof line.created_at === 'number'
|
|
313
|
+
? line.created_at
|
|
314
|
+
: typeof line.updated_at === 'number'
|
|
315
|
+
? line.updated_at
|
|
316
|
+
: Date.now(),
|
|
301
317
|
// The row gets the best timestamp available even when the RECORD compared
|
|
302
318
|
// as 0 (a pre-#1463 line): 0 governs only who wins the merge, while the row
|
|
303
319
|
// itself should carry the most accurate time we have.
|
|
@@ -326,7 +342,7 @@ export function exportTeamArtifact(opts) {
|
|
|
326
342
|
const parsedSharedAt = Date.parse(opts.sharedAt);
|
|
327
343
|
const now = opts.now ?? (Number.isNaN(parsedSharedAt) ? Date.now() : parsedSharedAt);
|
|
328
344
|
const ttlMs = opts.tombstoneTtlMs ?? TOMBSTONE_TTL_MS;
|
|
329
|
-
const { lines, records: target, malformed, existed } = readArtifact(opts.artifactPath);
|
|
345
|
+
const { lines, records: target, malformed, misfiled, existed } = readArtifact(opts.artifactPath);
|
|
330
346
|
const provenance = {
|
|
331
347
|
author: resolveAuthor(projectRoot),
|
|
332
348
|
source: resolveSource(),
|
|
@@ -418,7 +434,7 @@ export function exportTeamArtifact(opts) {
|
|
|
418
434
|
}
|
|
419
435
|
// Skip the write when the merge changed nothing AND the file already exists:
|
|
420
436
|
// a git-tracked artifact should not show up as modified after a no-op run.
|
|
421
|
-
const changed = actions.length > 0 || prunedTombstones > 0 || backfilled > 0;
|
|
437
|
+
const changed = actions.length > 0 || prunedTombstones > 0 || backfilled > 0 || misfiled > 0;
|
|
422
438
|
const wrote = changed || !existed;
|
|
423
439
|
if (wrote) {
|
|
424
440
|
fs.mkdirSync(path.dirname(opts.artifactPath), { recursive: true });
|
|
@@ -436,6 +452,7 @@ export function exportTeamArtifact(opts) {
|
|
|
436
452
|
backfilled,
|
|
437
453
|
skippedMalformed: malformed,
|
|
438
454
|
skippedCorrupt,
|
|
455
|
+
droppedMisfiled: misfiled,
|
|
439
456
|
total: live,
|
|
440
457
|
tombstones,
|
|
441
458
|
wrote,
|
|
@@ -467,9 +484,11 @@ export function importTeamArtifact(opts) {
|
|
|
467
484
|
skippedMalformed: 0,
|
|
468
485
|
skippedNonDurable: 0,
|
|
469
486
|
skippedCorrupt: 0,
|
|
487
|
+
skippedMisfiled: 0,
|
|
470
488
|
};
|
|
471
|
-
const { lines, records: parsed, malformed } = readArtifact(opts.artifactPath);
|
|
489
|
+
const { lines, records: parsed, malformed, misfiled } = readArtifact(opts.artifactPath);
|
|
472
490
|
report.skippedMalformed = malformed;
|
|
491
|
+
report.skippedMisfiled = misfiled;
|
|
473
492
|
if (lines.size === 0)
|
|
474
493
|
return report;
|
|
475
494
|
const source = new Map();
|
package/dist/src/cli/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "moflo",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.5",
|
|
4
4
|
"description": "MoFlo — AI agent orchestration for Claude Code. A standalone, opinionated toolkit with semantic memory, learned routing, gates, spells, and the /flo issue-execution skill.",
|
|
5
5
|
"main": "dist/src/cli/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
"@typescript-eslint/parser": "^8.65.0",
|
|
100
100
|
"eslint": "^10.8.0",
|
|
101
101
|
"glob": "^11.1.0",
|
|
102
|
-
"moflo": "^4.13.
|
|
102
|
+
"moflo": "^4.13.4",
|
|
103
103
|
"tsx": "^4.21.0",
|
|
104
104
|
"typescript": "^5.9.3",
|
|
105
105
|
"vitest": "^4.0.0"
|