watashi-db 0.0.11 → 0.0.13
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/LICENSE +71 -21
- package/README.md +9 -1
- package/cowork-plugin/skills/groom/SKILL.md +72 -0
- package/dist/config/schema.d.ts +21 -0
- package/dist/config/schema.js +4 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/config/validator.js +1 -0
- package/dist/config/validator.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/constants.js.map +1 -1
- package/dist/database/groom.js +5 -4
- package/dist/database/groom.js.map +1 -1
- package/dist/database/queries-core.d.ts +28 -2
- package/dist/database/queries-core.js +134 -17
- package/dist/database/queries-core.js.map +1 -1
- package/dist/database/queries.d.ts +18 -0
- package/dist/database/queries.js +7 -0
- package/dist/database/queries.js.map +1 -1
- package/dist/database/schema.d.ts +5 -0
- package/dist/database/schema.js +156 -1
- package/dist/database/schema.js.map +1 -1
- package/dist/embedding/embed-on-write.d.ts +3 -0
- package/dist/embedding/embed-on-write.js.map +1 -1
- package/dist/embedding/provider.d.ts +3 -0
- package/dist/embedding/provider.js +38 -1
- package/dist/embedding/provider.js.map +1 -1
- package/dist/hook.js +12 -2
- package/dist/hook.js.map +1 -1
- package/dist/resources/config-guide-content.d.ts +1 -1
- package/dist/resources/config-guide-content.js +5 -0
- package/dist/resources/config-guide-content.js.map +1 -1
- package/dist/server-instructions.js +3 -2
- package/dist/server-instructions.js.map +1 -1
- package/dist/server.js +10 -1
- package/dist/server.js.map +1 -1
- package/dist/store/federation.d.ts +21 -0
- package/dist/store/federation.js +56 -0
- package/dist/store/federation.js.map +1 -1
- package/dist/store/sync-manager.d.ts +3 -0
- package/dist/store/sync-manager.js +50 -28
- package/dist/store/sync-manager.js.map +1 -1
- package/dist/tools/claim-tools.js +1 -1
- package/dist/tools/claim-tools.js.map +1 -1
- package/dist/tools/decision-tools.js +11 -8
- package/dist/tools/decision-tools.js.map +1 -1
- package/dist/tools/episode-tools.js +13 -9
- package/dist/tools/episode-tools.js.map +1 -1
- package/dist/tools/maintenance-tools.d.ts +1 -0
- package/dist/tools/maintenance-tools.js +117 -6
- package/dist/tools/maintenance-tools.js.map +1 -1
- package/dist/tools/query-tools.js +34 -4
- package/dist/tools/query-tools.js.map +1 -1
- package/dist/types.d.ts +65 -59
- package/dist/types.js +36 -28
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -57,8 +57,8 @@ export function insertClaim(db, params) {
|
|
|
57
57
|
const id = ulid();
|
|
58
58
|
const now = new Date().toISOString();
|
|
59
59
|
db.prepare(`
|
|
60
|
-
INSERT INTO claims (id, l2_subject, l2_predicate, l2_object, category, scope, confidence,
|
|
61
|
-
VALUES (@id, @l2_subject, @l2_predicate, @l2_object, @category, @scope, @confidence, @
|
|
60
|
+
INSERT INTO claims (id, l2_subject, l2_predicate, l2_object, category, scope, confidence, l2_evidence, l2_falsifier, l1_content, search_summary, l1_embedding, source_tool, source_session, client_name, client_version, status, created_at, updated_at)
|
|
61
|
+
VALUES (@id, @l2_subject, @l2_predicate, @l2_object, @category, @scope, @confidence, @l2_evidence, @l2_falsifier, @l1_content, @search_summary, @l1_embedding, @source_tool, @source_session, @client_name, @client_version, 'active', @created_at, @updated_at)
|
|
62
62
|
`).run({
|
|
63
63
|
id,
|
|
64
64
|
l2_subject: params.subject,
|
|
@@ -67,8 +67,8 @@ export function insertClaim(db, params) {
|
|
|
67
67
|
category: params.category,
|
|
68
68
|
scope: params.scope,
|
|
69
69
|
confidence: params.confidence,
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
l2_evidence: params.evidence ?? null,
|
|
71
|
+
l2_falsifier: params.falsifier ?? null,
|
|
72
72
|
l1_content: params.l1_content ?? null,
|
|
73
73
|
search_summary: params.search_summary ?? null,
|
|
74
74
|
l1_embedding: params.l1_embedding ?? null,
|
|
@@ -98,7 +98,8 @@ export function updateClaim(db, id, updates, reason) {
|
|
|
98
98
|
// 2026-02-22 修正 (Issue #22): search_summary を追加
|
|
99
99
|
// 元の実装: search_summary は store 時のみ設定可能だった
|
|
100
100
|
// 2026-03-03 修正 (Issue #64): l1_content を追加
|
|
101
|
-
|
|
101
|
+
// 2026-03-13 修正: evidence → l2_evidence, falsifier → l2_falsifier
|
|
102
|
+
const allowedFields = ["l2_subject", "l2_predicate", "l2_object", "category", "scope", "confidence", "l2_evidence", "l2_falsifier", "l1_content", "search_summary"];
|
|
102
103
|
const transaction = db.transaction(() => {
|
|
103
104
|
for (const [field, newValue] of Object.entries(mappedUpdates)) {
|
|
104
105
|
if (!allowedFields.includes(field))
|
|
@@ -258,7 +259,7 @@ export function searchClaims(db, params) {
|
|
|
258
259
|
const likeValues = { ...values, limit: params.limit };
|
|
259
260
|
delete likeValues.query;
|
|
260
261
|
likeValues.like_query = `%${params.query}%`;
|
|
261
|
-
likeConditions.push("(c.l2_subject LIKE @like_query OR c.l2_predicate LIKE @like_query OR c.l2_object LIKE @like_query OR c.
|
|
262
|
+
likeConditions.push("(c.l2_subject LIKE @like_query OR c.l2_predicate LIKE @like_query OR c.l2_object LIKE @like_query OR c.l2_evidence LIKE @like_query OR c.search_summary LIKE @like_query)");
|
|
262
263
|
const likeWhere = likeConditions.length > 0
|
|
263
264
|
? "WHERE " + likeConditions.join(" AND ")
|
|
264
265
|
: "";
|
|
@@ -437,7 +438,7 @@ function queryClaimContext(db, topic, scope, limit) {
|
|
|
437
438
|
return db.prepare(`
|
|
438
439
|
SELECT c.* FROM claims c
|
|
439
440
|
WHERE c.status = 'active'
|
|
440
|
-
AND (c.l2_subject LIKE @like_query OR c.l2_predicate LIKE @like_query OR c.l2_object LIKE @like_query OR c.
|
|
441
|
+
AND (c.l2_subject LIKE @like_query OR c.l2_predicate LIKE @like_query OR c.l2_object LIKE @like_query OR c.l2_evidence LIKE @like_query OR c.search_summary LIKE @like_query)
|
|
441
442
|
${scopeFilter}
|
|
442
443
|
ORDER BY c.updated_at DESC
|
|
443
444
|
LIMIT @limit
|
|
@@ -995,6 +996,82 @@ export function markEpisodesGroomed(db, episodeIds) {
|
|
|
995
996
|
transaction();
|
|
996
997
|
return markedIds;
|
|
997
998
|
}
|
|
999
|
+
// === Episode/Decision アーカイブ ===
|
|
1000
|
+
/** Episode をアーカイブ(蒸留済み + 最新N件を除外) */
|
|
1001
|
+
export function archiveEpisodes(db, params) {
|
|
1002
|
+
const keepLatest = params.keepLatest ?? 20;
|
|
1003
|
+
if (params.episodeIds && params.episodeIds.length > 0) {
|
|
1004
|
+
// 個別指定モード: 指定IDのみアーカイブ
|
|
1005
|
+
const updated = [];
|
|
1006
|
+
const stmt = db.prepare("UPDATE episodes SET status = 'archived', updated_at = @now WHERE id = @id AND status = 'active'");
|
|
1007
|
+
const now = new Date().toISOString();
|
|
1008
|
+
for (const id of params.episodeIds) {
|
|
1009
|
+
const result = stmt.run({ id, now });
|
|
1010
|
+
if (result.changes > 0)
|
|
1011
|
+
updated.push(id);
|
|
1012
|
+
}
|
|
1013
|
+
return { archivedIds: updated, archivedCount: updated.length };
|
|
1014
|
+
}
|
|
1015
|
+
// 自動モード: groomed_at IS NOT NULL かつ最新 keepLatest 件を除外
|
|
1016
|
+
const scopeFilter = params.scope ? " AND scope = @scope" : "";
|
|
1017
|
+
const scopeValues = params.scope ? { scope: params.scope } : {};
|
|
1018
|
+
// 最新 keepLatest 件の ID を取得(保護対象)
|
|
1019
|
+
const protectedIds = db.prepare(`
|
|
1020
|
+
SELECT id FROM episodes
|
|
1021
|
+
WHERE status = 'active'${scopeFilter}
|
|
1022
|
+
ORDER BY created_at DESC
|
|
1023
|
+
LIMIT @keepLatest
|
|
1024
|
+
`).all({ ...scopeValues, keepLatest });
|
|
1025
|
+
const protectedSet = new Set(protectedIds.map(r => r.id));
|
|
1026
|
+
// groomed_at IS NOT NULL かつ保護対象外をアーカイブ
|
|
1027
|
+
const candidates = db.prepare(`
|
|
1028
|
+
SELECT id FROM episodes
|
|
1029
|
+
WHERE status = 'active' AND groomed_at IS NOT NULL${scopeFilter}
|
|
1030
|
+
`).all(scopeValues);
|
|
1031
|
+
const toArchive = candidates.filter(r => !protectedSet.has(r.id));
|
|
1032
|
+
const now = new Date().toISOString();
|
|
1033
|
+
const stmt = db.prepare("UPDATE episodes SET status = 'archived', updated_at = @now WHERE id = @id");
|
|
1034
|
+
for (const r of toArchive) {
|
|
1035
|
+
stmt.run({ id: r.id, now });
|
|
1036
|
+
}
|
|
1037
|
+
return { archivedIds: toArchive.map(r => r.id), archivedCount: toArchive.length };
|
|
1038
|
+
}
|
|
1039
|
+
/** Decision をアーカイブ(最新N件を除外) */
|
|
1040
|
+
export function archiveDecisions(db, params) {
|
|
1041
|
+
const keepLatest = params.keepLatest ?? 20;
|
|
1042
|
+
if (params.decisionIds && params.decisionIds.length > 0) {
|
|
1043
|
+
const updated = [];
|
|
1044
|
+
const stmt = db.prepare("UPDATE decisions SET status = 'archived', updated_at = @now WHERE id = @id AND status = 'active'");
|
|
1045
|
+
const now = new Date().toISOString();
|
|
1046
|
+
for (const id of params.decisionIds) {
|
|
1047
|
+
const result = stmt.run({ id, now });
|
|
1048
|
+
if (result.changes > 0)
|
|
1049
|
+
updated.push(id);
|
|
1050
|
+
}
|
|
1051
|
+
return { archivedIds: updated, archivedCount: updated.length };
|
|
1052
|
+
}
|
|
1053
|
+
// 自動モード: 最新 keepLatest 件を除外してアーカイブ
|
|
1054
|
+
const scopeFilter = params.scope ? " AND scope = @scope" : "";
|
|
1055
|
+
const scopeValues = params.scope ? { scope: params.scope } : {};
|
|
1056
|
+
const protectedIds = db.prepare(`
|
|
1057
|
+
SELECT id FROM decisions
|
|
1058
|
+
WHERE status = 'active'${scopeFilter}
|
|
1059
|
+
ORDER BY created_at DESC
|
|
1060
|
+
LIMIT @keepLatest
|
|
1061
|
+
`).all({ ...scopeValues, keepLatest });
|
|
1062
|
+
const protectedSet = new Set(protectedIds.map(r => r.id));
|
|
1063
|
+
const candidates = db.prepare(`
|
|
1064
|
+
SELECT id FROM decisions
|
|
1065
|
+
WHERE status = 'active'${scopeFilter}
|
|
1066
|
+
`).all(scopeValues);
|
|
1067
|
+
const toArchive = candidates.filter(r => !protectedSet.has(r.id));
|
|
1068
|
+
const now = new Date().toISOString();
|
|
1069
|
+
const stmt = db.prepare("UPDATE decisions SET status = 'archived', updated_at = @now WHERE id = @id");
|
|
1070
|
+
for (const r of toArchive) {
|
|
1071
|
+
stmt.run({ id: r.id, now });
|
|
1072
|
+
}
|
|
1073
|
+
return { archivedIds: toArchive.map(r => r.id), archivedCount: toArchive.length };
|
|
1074
|
+
}
|
|
998
1075
|
// === Issue #35: トピック集計 ===
|
|
999
1076
|
// 2026-02-22 修正 (Codex P2指摘): scope パラメータを追加し、スコープ絞り込みに対応
|
|
1000
1077
|
export function getTopicSummary(db, tagLimit = 10, decisionLimit = 5, scope) {
|
|
@@ -1608,8 +1685,8 @@ export function getStaleClaims(db, params) {
|
|
|
1608
1685
|
reasons.push("never_hit");
|
|
1609
1686
|
score += 10;
|
|
1610
1687
|
}
|
|
1611
|
-
// no_evidence:
|
|
1612
|
-
if (row.
|
|
1688
|
+
// no_evidence: l2_evidence === null → 10点
|
|
1689
|
+
if (row.l2_evidence === null) {
|
|
1613
1690
|
reasons.push("no_evidence");
|
|
1614
1691
|
score += 10;
|
|
1615
1692
|
}
|
|
@@ -1618,8 +1695,8 @@ export function getStaleClaims(db, params) {
|
|
|
1618
1695
|
reasons.push("no_checks");
|
|
1619
1696
|
score += 15;
|
|
1620
1697
|
}
|
|
1621
|
-
// falsifier_unevaluated:
|
|
1622
|
-
if (row.
|
|
1698
|
+
// falsifier_unevaluated: l2_falsifier設定済みだが falsifier_eval 未実施 → 20点
|
|
1699
|
+
if (row.l2_falsifier && row.has_falsifier_eval === 0) {
|
|
1623
1700
|
reasons.push("falsifier_unevaluated");
|
|
1624
1701
|
score += 20;
|
|
1625
1702
|
}
|
|
@@ -1659,8 +1736,8 @@ export function getStaleClaims(db, params) {
|
|
|
1659
1736
|
category: row.category,
|
|
1660
1737
|
scope: row.scope,
|
|
1661
1738
|
confidence: row.confidence,
|
|
1662
|
-
|
|
1663
|
-
|
|
1739
|
+
l2_evidence: row.l2_evidence,
|
|
1740
|
+
l2_falsifier: row.l2_falsifier,
|
|
1664
1741
|
hit_count: row.hit_count,
|
|
1665
1742
|
updated_at: row.updated_at,
|
|
1666
1743
|
staleness_score: score,
|
|
@@ -1749,7 +1826,7 @@ export function updateDecisionL1Embedding(db, id, l1_embedding) {
|
|
|
1749
1826
|
export function updateEpisodeL1Embedding(db, id, l1_embedding) {
|
|
1750
1827
|
db.prepare("UPDATE episodes SET l1_embedding = @l1_embedding WHERE id = @id").run({ id, l1_embedding });
|
|
1751
1828
|
}
|
|
1752
|
-
/** Episode
|
|
1829
|
+
/** Episode のフィールドを更新(2フェーズ書き込み Phase 2: l1_content 補完) */
|
|
1753
1830
|
export function updateEpisode(db, id, updates) {
|
|
1754
1831
|
const existing = getEpisodeById(db, id);
|
|
1755
1832
|
if (!existing)
|
|
@@ -1757,7 +1834,7 @@ export function updateEpisode(db, id, updates) {
|
|
|
1757
1834
|
const now = new Date().toISOString();
|
|
1758
1835
|
const setClauses = ["updated_at = @updated_at"];
|
|
1759
1836
|
const values = { id, updated_at: now };
|
|
1760
|
-
const stringFields = ["l2_context", "l2_trigger", "search_summary"];
|
|
1837
|
+
const stringFields = ["l1_content", "l2_context", "l2_trigger", "search_summary"];
|
|
1761
1838
|
for (const field of stringFields) {
|
|
1762
1839
|
if (updates[field] !== undefined) {
|
|
1763
1840
|
setClauses.push(`${field} = @${field}`);
|
|
@@ -1774,7 +1851,7 @@ export function updateEpisode(db, id, updates) {
|
|
|
1774
1851
|
db.prepare(`UPDATE episodes SET ${setClauses.join(", ")} WHERE id = @id`).run(values);
|
|
1775
1852
|
return getEpisodeById(db, id);
|
|
1776
1853
|
}
|
|
1777
|
-
/** Decision
|
|
1854
|
+
/** Decision のフィールドを更新(2フェーズ書き込み Phase 2: l1_content 補完) */
|
|
1778
1855
|
export function updateDecision(db, id, updates) {
|
|
1779
1856
|
const existing = getDecisionById(db, id);
|
|
1780
1857
|
if (!existing)
|
|
@@ -1782,6 +1859,10 @@ export function updateDecision(db, id, updates) {
|
|
|
1782
1859
|
const now = new Date().toISOString();
|
|
1783
1860
|
const setClauses = ["updated_at = @updated_at"];
|
|
1784
1861
|
const values = { id, updated_at: now };
|
|
1862
|
+
if (updates.l1_content !== undefined) {
|
|
1863
|
+
setClauses.push("l1_content = @l1_content");
|
|
1864
|
+
values.l1_content = updates.l1_content;
|
|
1865
|
+
}
|
|
1785
1866
|
if (updates.l2_reasoning !== undefined) {
|
|
1786
1867
|
setClauses.push("l2_reasoning = @l2_reasoning");
|
|
1787
1868
|
values.l2_reasoning = updates.l2_reasoning;
|
|
@@ -2675,7 +2756,7 @@ export function rebuildUnifiedSearch(db) {
|
|
|
2675
2756
|
type: "claim", table: "claims",
|
|
2676
2757
|
categoryExpr: "category",
|
|
2677
2758
|
titleExpr: "l2_subject || ' ' || l2_predicate || ' ' || l2_object",
|
|
2678
|
-
searchExpr: `l2_subject || ' ' || l2_predicate || ' ' || l2_object || ' ' || ${coal("
|
|
2759
|
+
searchExpr: `l2_subject || ' ' || l2_predicate || ' ' || l2_object || ' ' || ${coal("l2_evidence")} || ' ' || ${coal("l2_falsifier")} || ' ' || ${coal("l1_content")} || ' ' || ${coal("search_summary")}`,
|
|
2679
2760
|
searchSummary: "search_summary",
|
|
2680
2761
|
tagsExpr: "'[]'",
|
|
2681
2762
|
},
|
|
@@ -2779,6 +2860,42 @@ export function updateSearchSummary(db, entityType, entityId, searchSummary) {
|
|
|
2779
2860
|
const result = db.prepare(`UPDATE ${table} SET search_summary = @searchSummary, updated_at = @now WHERE id = @entityId`).run({ searchSummary, now, entityId });
|
|
2780
2861
|
return result.changes;
|
|
2781
2862
|
}
|
|
2863
|
+
// === archived 含む検索(フォールバック用) ===
|
|
2864
|
+
/** archived を含む FTS 検索(unified_search_items ではなくソーステーブルの FTS を直接クエリ) */
|
|
2865
|
+
export function searchIncludingArchived(db, params) {
|
|
2866
|
+
const results = [];
|
|
2867
|
+
const targets = [
|
|
2868
|
+
{ fts: "episodes_fts", table: "episodes", type: "episode", titleExpr: "t.title" },
|
|
2869
|
+
{ fts: "decisions_fts", table: "decisions", type: "decision", titleExpr: "t.title" },
|
|
2870
|
+
{ fts: "claims_fts", table: "claims", type: "claim", titleExpr: "t.l2_subject || ' ' || t.l2_predicate || ' ' || t.l2_object" },
|
|
2871
|
+
];
|
|
2872
|
+
for (const target of targets) {
|
|
2873
|
+
try {
|
|
2874
|
+
const scopeFilter = params.scope
|
|
2875
|
+
? " AND (t.scope = @scope OR t.scope = 'global')"
|
|
2876
|
+
: "";
|
|
2877
|
+
const rows = db.prepare(`
|
|
2878
|
+
SELECT t.id as entity_id, '${target.type}' as entity_type,
|
|
2879
|
+
${target.titleExpr} as title_summary, t.scope,
|
|
2880
|
+
COALESCE(t.search_summary, '') as search_summary,
|
|
2881
|
+
'[]' as tags, '' as search_text,
|
|
2882
|
+
NULL as l1_embedding,
|
|
2883
|
+
t.created_at, t.updated_at,
|
|
2884
|
+
NULL as _fts_rank, NULL as category
|
|
2885
|
+
FROM ${target.table} t
|
|
2886
|
+
JOIN ${target.fts} f ON f.rowid = t.rowid
|
|
2887
|
+
WHERE ${target.fts} MATCH @query${scopeFilter}
|
|
2888
|
+
ORDER BY rank
|
|
2889
|
+
LIMIT @limit
|
|
2890
|
+
`).all({ query: params.query, scope: params.scope, limit: params.limit });
|
|
2891
|
+
results.push(...rows);
|
|
2892
|
+
}
|
|
2893
|
+
catch {
|
|
2894
|
+
// FTS テーブル未存在等はスキップ
|
|
2895
|
+
}
|
|
2896
|
+
}
|
|
2897
|
+
return results.slice(0, params.limit);
|
|
2898
|
+
}
|
|
2782
2899
|
// === Issue #63: 物理削除 + tombstone ===
|
|
2783
2900
|
/**
|
|
2784
2901
|
* エンティティを物理削除し、tombstone を記録する。
|