@dzhechkov/harness-core 0.3.140 → 0.3.142
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/.dz-manifest.json +49 -21
- package/dist/agentdb-index.d.ts +36 -0
- package/dist/agentdb-index.d.ts.map +1 -1
- package/dist/agentdb-index.js +146 -2
- package/dist/agentdb-index.js.map +1 -1
- package/dist/backlog.d.ts +310 -0
- package/dist/backlog.d.ts.map +1 -0
- package/dist/backlog.js +755 -0
- package/dist/backlog.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +3 -0
- package/dist/registry.js.map +1 -1
- package/dist/vector-tier.d.ts +9 -0
- package/dist/vector-tier.d.ts.map +1 -1
- package/dist/vector-tier.js +38 -6
- package/dist/vector-tier.js.map +1 -1
- package/package.json +6 -6
- package/sbom.json +90 -20
- package/src/agentdb-index.ts +145 -2
- package/src/backlog.ts +978 -0
- package/src/index.ts +6 -1
- package/src/registry.ts +2 -0
- package/src/vector-tier.ts +47 -4
package/src/agentdb-index.ts
CHANGED
|
@@ -160,9 +160,21 @@ export async function indexPatternsToAgentdb(
|
|
|
160
160
|
/* READ half (dz-rvf-vector-bridge FR-3): semantic search + id scan */
|
|
161
161
|
/* ------------------------------------------------------------------ */
|
|
162
162
|
|
|
163
|
-
/**
|
|
163
|
+
/**
|
|
164
|
+
* The RECALL/search default task_types. Deliberately EXCLUDES `dz-backlog`: `dz recall` (and
|
|
165
|
+
* feature-adr Step-0) must never surface raw backlog ideas as if they were earned lessons (ADR-005).
|
|
166
|
+
*/
|
|
164
167
|
const DZ_TASK_TYPES = ['dz-teach', 'dz-learning'] as const;
|
|
165
168
|
|
|
169
|
+
/**
|
|
170
|
+
* The dz-owned task_types for LIFECYCLE scans (id enumeration + reindex ownership) — a SUPERSET of
|
|
171
|
+
* the recall default that ALSO owns `dz-backlog` (smart-backlog, ADR-001/005). Reindex must re-embed
|
|
172
|
+
* these on a model bump (else backlog rows rot in a stale embedding space), and id-scans must see them
|
|
173
|
+
* (mirror idempotency). Kept SEPARATE from {@link DZ_TASK_TYPES} so ownership never leaks ideas into
|
|
174
|
+
* lesson recall: search defaults to DZ_TASK_TYPES, lifecycle to DZ_OWNED_TASK_TYPES.
|
|
175
|
+
*/
|
|
176
|
+
export const DZ_OWNED_TASK_TYPES = ['dz-teach', 'dz-learning', 'dz-backlog'] as const;
|
|
177
|
+
|
|
166
178
|
/** Minimal READONLY better-sqlite3 surface used by the read half. */
|
|
167
179
|
interface ReadonlyDb {
|
|
168
180
|
pragma: (s: string) => void;
|
|
@@ -362,7 +374,9 @@ export async function listAgentdbDzIds(
|
|
|
362
374
|
const { db } = opened;
|
|
363
375
|
try {
|
|
364
376
|
if (!hasDzTables(db)) return { ids: [] };
|
|
365
|
-
|
|
377
|
+
// LIFECYCLE scan ⇒ the OWNED superset (incl. dz-backlog) so idea ids are visible for mirror
|
|
378
|
+
// idempotency + `dz vector status`. Recall/search still defaults to the narrower DZ_TASK_TYPES.
|
|
379
|
+
const taskTypes = opts.taskTypes ?? DZ_OWNED_TASK_TYPES;
|
|
366
380
|
const placeholders = taskTypes.map(() => '?').join(', ');
|
|
367
381
|
const rows = db
|
|
368
382
|
.prepare(`SELECT metadata FROM reasoning_patterns WHERE task_type IN (${placeholders})`)
|
|
@@ -380,6 +394,65 @@ export async function listAgentdbDzIds(
|
|
|
380
394
|
}
|
|
381
395
|
}
|
|
382
396
|
|
|
397
|
+
/**
|
|
398
|
+
* Read the rows that ACTUALLY EXIST IN THE STORE for a task_type, as re-indexable {@link AgentdbRow}s
|
|
399
|
+
* (their stored `approach` text + score/uses/reward/tags/metadata, dzId preserved). This is the correct
|
|
400
|
+
* source for a REINDEX: a reindex re-embeds what is physically in the store to the new model — reading it
|
|
401
|
+
* back from the store (not reconstructing from a sidecar file like ideas.jsonl) means an empty/unreadable
|
|
402
|
+
* sidecar can never leave real store rows un-re-embedded and stale under an advanced manifest (HIGH-G).
|
|
403
|
+
* Readonly, best-effort ({rows:[]} on absent/unavailable), never throws.
|
|
404
|
+
*/
|
|
405
|
+
export function readAgentdbRowsByTaskType(
|
|
406
|
+
projectRoot: string,
|
|
407
|
+
taskType: string,
|
|
408
|
+
opts: { dbPath?: string } = {},
|
|
409
|
+
): { rows: AgentdbRow[]; error?: string } {
|
|
410
|
+
const opened = openReadonly(projectRoot, opts.dbPath);
|
|
411
|
+
if ('absent' in opened) return { rows: [] };
|
|
412
|
+
if ('error' in opened) return { rows: [], error: opened.error };
|
|
413
|
+
const { db } = opened;
|
|
414
|
+
try {
|
|
415
|
+
if (!hasDzTables(db)) return { rows: [] };
|
|
416
|
+
const raw = db
|
|
417
|
+
.prepare('SELECT approach, success_rate, uses, avg_reward, tags, metadata FROM reasoning_patterns WHERE task_type = ?')
|
|
418
|
+
.all(taskType) as Array<{ approach: string; success_rate: number; uses: number; avg_reward: number; tags: string | null; metadata: string | null }>;
|
|
419
|
+
const rows: AgentdbRow[] = raw.map((r) => {
|
|
420
|
+
let metadata: Record<string, unknown> | undefined;
|
|
421
|
+
if (typeof r.metadata === 'string' && r.metadata !== '') {
|
|
422
|
+
try {
|
|
423
|
+
const m = JSON.parse(r.metadata) as unknown;
|
|
424
|
+
if (m !== null && typeof m === 'object') metadata = m as Record<string, unknown>;
|
|
425
|
+
} catch {
|
|
426
|
+
/* drop unparseable metadata */
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
let tags: string[] | undefined;
|
|
430
|
+
if (typeof r.tags === 'string' && r.tags !== '') {
|
|
431
|
+
try {
|
|
432
|
+
const t = JSON.parse(r.tags) as unknown;
|
|
433
|
+
if (Array.isArray(t)) tags = t.filter((x): x is string => typeof x === 'string');
|
|
434
|
+
} catch {
|
|
435
|
+
/* drop unparseable tags */
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
return {
|
|
439
|
+
taskType,
|
|
440
|
+
text: r.approach,
|
|
441
|
+
score: r.success_rate,
|
|
442
|
+
uses: r.uses,
|
|
443
|
+
avgReward: r.avg_reward,
|
|
444
|
+
...(tags !== undefined ? { tags } : {}),
|
|
445
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
446
|
+
};
|
|
447
|
+
});
|
|
448
|
+
return { rows };
|
|
449
|
+
} catch (err) {
|
|
450
|
+
return { rows: [], error: `row scan failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
451
|
+
} finally {
|
|
452
|
+
db.close();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
383
456
|
/* ------------------------------------------------------------------ */
|
|
384
457
|
/* IMPORT half (dz-vector-harmonize-import M0.4): upsert-by-dzId */
|
|
385
458
|
/* ------------------------------------------------------------------ */
|
|
@@ -437,6 +510,20 @@ export async function importVectorsToAgentdb(
|
|
|
437
510
|
const { default: Database } = (await import(sqliteUrl)) as { default: new (p: string) => UpsertDb };
|
|
438
511
|
const model = resolveEmbedModel(projectRoot);
|
|
439
512
|
if ('error' in model) return { imported: 0, error: model.error };
|
|
513
|
+
// MED-C: the store manifest guards the MODEL, but a per-vector guard was missing — a malformed or
|
|
514
|
+
// TOCTOU vector (wrong length / NaN / ±Infinity) would be stamped compatible. Fail CLOSED and LOUD:
|
|
515
|
+
// reject the whole batch if any vector's dimensionality ≠ the store dim or any component is non-finite,
|
|
516
|
+
// so a vector can only enter the store bound to the model+dim the manifest names.
|
|
517
|
+
for (const row of rows) {
|
|
518
|
+
if (!(row.vector instanceof Float32Array) || row.vector.length !== model.dim) {
|
|
519
|
+
return { imported: 0, error: `vector validation failed for ${row.dzId}: length ${row.vector?.length} != store dim ${model.dim}` };
|
|
520
|
+
}
|
|
521
|
+
for (let i = 0; i < row.vector.length; i += 1) {
|
|
522
|
+
if (!Number.isFinite(row.vector[i])) {
|
|
523
|
+
return { imported: 0, error: `vector validation failed for ${row.dzId}: non-finite component at index ${i}` };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
440
527
|
const dbFile = resolveAgentdbPath(projectRoot, opts.dbPath);
|
|
441
528
|
mkdirSync(dirname(dbFile), { recursive: true }); // better-sqlite3 won't create the parent dir
|
|
442
529
|
const db = new Database(dbFile);
|
|
@@ -530,6 +617,62 @@ export function clearAgentdbQuarantine(
|
|
|
530
617
|
}
|
|
531
618
|
}
|
|
532
619
|
|
|
620
|
+
/**
|
|
621
|
+
* DELETE mirrored rows by `metadata.dzId` (pattern + its embedding), optionally scoped to a task_type
|
|
622
|
+
* set. The write-half of a structured-store removal: when `harmonize --apply` drops ideas from
|
|
623
|
+
* `ideas.jsonl`, their `dz-backlog` vectors must be pruned too, or a later semantic search matches an
|
|
624
|
+
* ORPHAN dzId that no longer has a structured record (smart-backlog HIGH-A). Best-effort, same custody
|
|
625
|
+
* model as {@link clearAgentdbQuarantine} (missing db/deps ⇒ no-op). Never throws.
|
|
626
|
+
*/
|
|
627
|
+
export function deleteAgentdbByDzIds(
|
|
628
|
+
projectRoot: string,
|
|
629
|
+
dzIds: readonly string[],
|
|
630
|
+
opts: { dbPath?: string; taskTypes?: readonly string[] } = {},
|
|
631
|
+
): { deleted: number; error?: string } {
|
|
632
|
+
if (dzIds.length === 0) return { deleted: 0 };
|
|
633
|
+
const dbFile = resolveAgentdbPath(projectRoot, opts.dbPath);
|
|
634
|
+
if (!existsSync(dbFile)) return { deleted: 0 };
|
|
635
|
+
let Database: new (p: string) => UpsertDb;
|
|
636
|
+
try {
|
|
637
|
+
const req = createRequire(join(projectRoot, 'package.json'));
|
|
638
|
+
Database = req('better-sqlite3') as new (p: string) => UpsertDb;
|
|
639
|
+
} catch {
|
|
640
|
+
return { deleted: 0, error: DEPS_MISSING };
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
const db = new Database(dbFile);
|
|
644
|
+
try {
|
|
645
|
+
db.pragma('journal_mode = WAL');
|
|
646
|
+
db.pragma('busy_timeout = 5000');
|
|
647
|
+
db.exec(REASONING_BANK_SCHEMA);
|
|
648
|
+
const scope = opts.taskTypes !== undefined && opts.taskTypes.length > 0;
|
|
649
|
+
const scopeSql = scope ? ` AND task_type IN (${opts.taskTypes!.map(() => '?').join(', ')})` : '';
|
|
650
|
+
const findIds = db.prepare(`SELECT id FROM reasoning_patterns WHERE json_extract(metadata, '$.dzId') = ?${scopeSql}`) as unknown as {
|
|
651
|
+
all: (...a: unknown[]) => { id: number }[];
|
|
652
|
+
};
|
|
653
|
+
const delEmb = db.prepare('DELETE FROM pattern_embeddings WHERE pattern_id = ?');
|
|
654
|
+
const delPat = db.prepare('DELETE FROM reasoning_patterns WHERE id = ?');
|
|
655
|
+
const tx = db.transaction(() => {
|
|
656
|
+
let deleted = 0;
|
|
657
|
+
for (const dzId of dzIds) {
|
|
658
|
+
const rows = scope ? findIds.all(dzId, ...opts.taskTypes!) : findIds.all(dzId);
|
|
659
|
+
for (const { id } of rows) {
|
|
660
|
+
delEmb.run(id);
|
|
661
|
+
delPat.run(id);
|
|
662
|
+
deleted += 1;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return deleted;
|
|
666
|
+
});
|
|
667
|
+
return { deleted: tx() };
|
|
668
|
+
} finally {
|
|
669
|
+
db.close();
|
|
670
|
+
}
|
|
671
|
+
} catch (err) {
|
|
672
|
+
return { deleted: 0, error: `delete by dzId failed: ${err instanceof Error ? err.message : String(err)}` };
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
533
676
|
export function bumpAgentdbUses(
|
|
534
677
|
projectRoot: string,
|
|
535
678
|
dzIds: readonly string[],
|