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.
Files changed (57) hide show
  1. package/LICENSE +71 -21
  2. package/README.md +9 -1
  3. package/cowork-plugin/skills/groom/SKILL.md +72 -0
  4. package/dist/config/schema.d.ts +21 -0
  5. package/dist/config/schema.js +4 -0
  6. package/dist/config/schema.js.map +1 -1
  7. package/dist/config/validator.js +1 -0
  8. package/dist/config/validator.js.map +1 -1
  9. package/dist/constants.d.ts +1 -1
  10. package/dist/constants.js +1 -0
  11. package/dist/constants.js.map +1 -1
  12. package/dist/database/groom.js +5 -4
  13. package/dist/database/groom.js.map +1 -1
  14. package/dist/database/queries-core.d.ts +28 -2
  15. package/dist/database/queries-core.js +134 -17
  16. package/dist/database/queries-core.js.map +1 -1
  17. package/dist/database/queries.d.ts +18 -0
  18. package/dist/database/queries.js +7 -0
  19. package/dist/database/queries.js.map +1 -1
  20. package/dist/database/schema.d.ts +5 -0
  21. package/dist/database/schema.js +156 -1
  22. package/dist/database/schema.js.map +1 -1
  23. package/dist/embedding/embed-on-write.d.ts +3 -0
  24. package/dist/embedding/embed-on-write.js.map +1 -1
  25. package/dist/embedding/provider.d.ts +3 -0
  26. package/dist/embedding/provider.js +38 -1
  27. package/dist/embedding/provider.js.map +1 -1
  28. package/dist/hook.js +12 -2
  29. package/dist/hook.js.map +1 -1
  30. package/dist/resources/config-guide-content.d.ts +1 -1
  31. package/dist/resources/config-guide-content.js +5 -0
  32. package/dist/resources/config-guide-content.js.map +1 -1
  33. package/dist/server-instructions.js +3 -2
  34. package/dist/server-instructions.js.map +1 -1
  35. package/dist/server.js +10 -1
  36. package/dist/server.js.map +1 -1
  37. package/dist/store/federation.d.ts +21 -0
  38. package/dist/store/federation.js +56 -0
  39. package/dist/store/federation.js.map +1 -1
  40. package/dist/store/sync-manager.d.ts +3 -0
  41. package/dist/store/sync-manager.js +50 -28
  42. package/dist/store/sync-manager.js.map +1 -1
  43. package/dist/tools/claim-tools.js +1 -1
  44. package/dist/tools/claim-tools.js.map +1 -1
  45. package/dist/tools/decision-tools.js +11 -8
  46. package/dist/tools/decision-tools.js.map +1 -1
  47. package/dist/tools/episode-tools.js +13 -9
  48. package/dist/tools/episode-tools.js.map +1 -1
  49. package/dist/tools/maintenance-tools.d.ts +1 -0
  50. package/dist/tools/maintenance-tools.js +117 -6
  51. package/dist/tools/maintenance-tools.js.map +1 -1
  52. package/dist/tools/query-tools.js +34 -4
  53. package/dist/tools/query-tools.js.map +1 -1
  54. package/dist/types.d.ts +65 -59
  55. package/dist/types.js +36 -28
  56. package/dist/types.js.map +1 -1
  57. 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, evidence, 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, @evidence, @falsifier, @l1_content, @search_summary, @l1_embedding, @source_tool, @source_session, @client_name, @client_version, 'active', @created_at, @updated_at)
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
- evidence: params.evidence ?? null,
71
- falsifier: params.falsifier ?? null,
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
- const allowedFields = ["l2_subject", "l2_predicate", "l2_object", "category", "scope", "confidence", "evidence", "falsifier", "l1_content", "search_summary"];
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.evidence LIKE @like_query OR c.search_summary LIKE @like_query)");
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.evidence LIKE @like_query OR c.search_summary LIKE @like_query)
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: evidence === null → 10点
1612
- if (row.evidence === null) {
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: falsifier設定済みだが falsifier_eval 未実施 → 20点
1622
- if (row.falsifier && row.has_falsifier_eval === 0) {
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
- evidence: row.evidence,
1663
- falsifier: row.falsifier,
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 の l2 フィールドを更新(2フェーズ書き込み Phase 2 */
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 の l2 フィールドを更新(2フェーズ書き込み Phase 2 */
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("evidence")} || ' ' || ${coal("falsifier")} || ' ' || ${coal("l1_content")} || ' ' || ${coal("search_summary")}`,
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 を記録する。