kiokuko-dsh 0.1.77 → 0.1.78

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 (95) hide show
  1. package/PERMISSIONS.md +8 -0
  2. package/README.ja.md +1 -0
  3. package/dist/client.cjs +418 -0
  4. package/dist/client.d.ts +9 -0
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/diff-review/analyzer.d.ts +15 -0
  7. package/dist/diff-review/analyzer.d.ts.map +1 -0
  8. package/dist/diff-review/analyzer.js +180 -0
  9. package/dist/diff-review/analyzer.js.map +1 -0
  10. package/dist/diff-review/controller.d.ts +66 -0
  11. package/dist/diff-review/controller.d.ts.map +1 -0
  12. package/dist/diff-review/controller.js +264 -0
  13. package/dist/diff-review/controller.js.map +1 -0
  14. package/dist/diff-review/current-git-source.d.ts +77 -0
  15. package/dist/diff-review/current-git-source.d.ts.map +1 -0
  16. package/dist/diff-review/current-git-source.js +300 -0
  17. package/dist/diff-review/current-git-source.js.map +1 -0
  18. package/dist/diff-review/markdown.d.ts +3 -0
  19. package/dist/diff-review/markdown.d.ts.map +1 -0
  20. package/dist/diff-review/markdown.js +48 -0
  21. package/dist/diff-review/markdown.js.map +1 -0
  22. package/dist/diff-review/native-turn-source.d.ts +38 -0
  23. package/dist/diff-review/native-turn-source.d.ts.map +1 -0
  24. package/dist/diff-review/native-turn-source.js +43 -0
  25. package/dist/diff-review/native-turn-source.js.map +1 -0
  26. package/dist/diff-review/redaction.d.ts +5 -0
  27. package/dist/diff-review/redaction.d.ts.map +1 -0
  28. package/dist/diff-review/redaction.js +26 -0
  29. package/dist/diff-review/redaction.js.map +1 -0
  30. package/dist/diff-review/schema.d.ts +191 -0
  31. package/dist/diff-review/schema.d.ts.map +1 -0
  32. package/dist/diff-review/schema.js +45 -0
  33. package/dist/diff-review/schema.js.map +1 -0
  34. package/dist/dsh/composition.d.ts +2 -0
  35. package/dist/dsh/composition.d.ts.map +1 -1
  36. package/dist/dsh/composition.js +3 -0
  37. package/dist/dsh/composition.js.map +1 -1
  38. package/dist/dsh/config.d.ts +35 -0
  39. package/dist/dsh/config.d.ts.map +1 -1
  40. package/dist/dsh/config.js +14 -0
  41. package/dist/dsh/config.js.map +1 -1
  42. package/dist/dsh/core/host.d.ts +8 -0
  43. package/dist/dsh/core/host.d.ts.map +1 -1
  44. package/dist/dsh/core/index.d.ts +16 -0
  45. package/dist/dsh/core/index.d.ts.map +1 -1
  46. package/dist/dsh/decisions/config.d.ts +8 -0
  47. package/dist/dsh/decisions/config.d.ts.map +1 -1
  48. package/dist/dsh/decisions/config.js +6 -0
  49. package/dist/dsh/decisions/config.js.map +1 -1
  50. package/dist/dsh/decisions/contracts.d.ts +2 -0
  51. package/dist/dsh/decisions/contracts.d.ts.map +1 -1
  52. package/dist/dsh/decisions/contracts.js +9 -5
  53. package/dist/dsh/decisions/contracts.js.map +1 -1
  54. package/dist/dsh/decisions/memory-batches.d.ts.map +1 -1
  55. package/dist/dsh/decisions/memory-batches.js +70 -10
  56. package/dist/dsh/decisions/memory-batches.js.map +1 -1
  57. package/dist/dsh/decisions/providers.d.ts.map +1 -1
  58. package/dist/dsh/decisions/providers.js +3 -0
  59. package/dist/dsh/decisions/providers.js.map +1 -1
  60. package/dist/dsh/decisions/readiness.d.ts.map +1 -1
  61. package/dist/dsh/decisions/readiness.js +10 -5
  62. package/dist/dsh/decisions/readiness.js.map +1 -1
  63. package/dist/dsh/decisions/score-consistency.d.ts +3 -0
  64. package/dist/dsh/decisions/score-consistency.d.ts.map +1 -0
  65. package/dist/dsh/decisions/score-consistency.js +6 -0
  66. package/dist/dsh/decisions/score-consistency.js.map +1 -0
  67. package/dist/dsh/decisions/service.d.ts +9 -1
  68. package/dist/dsh/decisions/service.d.ts.map +1 -1
  69. package/dist/dsh/decisions/service.js +44 -5
  70. package/dist/dsh/decisions/service.js.map +1 -1
  71. package/dist/dsh/diff-review-surface.d.ts +6 -0
  72. package/dist/dsh/diff-review-surface.d.ts.map +1 -0
  73. package/dist/dsh/diff-review-surface.js +93 -0
  74. package/dist/dsh/diff-review-surface.js.map +1 -0
  75. package/dist/dsh/host-adapter.d.ts +2 -0
  76. package/dist/dsh/host-adapter.d.ts.map +1 -1
  77. package/dist/dsh/host-adapter.js +14 -0
  78. package/dist/dsh/host-adapter.js.map +1 -1
  79. package/dist/dsh/index.js +1 -1
  80. package/dist/dsh/index.js.map +1 -1
  81. package/dist/dsh/memory-reuse.d.ts.map +1 -1
  82. package/dist/dsh/memory-reuse.js +32 -5
  83. package/dist/dsh/memory-reuse.js.map +1 -1
  84. package/dist/dsh/session-review-context.d.ts +30 -0
  85. package/dist/dsh/session-review-context.d.ts.map +1 -0
  86. package/dist/dsh/session-review-context.js +102 -0
  87. package/dist/dsh/session-review-context.js.map +1 -0
  88. package/dist/dsh/typesafe/contracts.d.ts.map +1 -1
  89. package/dist/dsh/typesafe/contracts.js +3 -1
  90. package/dist/dsh/typesafe/contracts.js.map +1 -1
  91. package/docs/diff-review.ja.md +16 -0
  92. package/docs/memory-reuse.md +24 -8
  93. package/docs/typed-decisions.md +5 -1
  94. package/dsh/cordis.patch.yml +1 -1
  95. package/package.json +5 -1
package/PERMISSIONS.md CHANGED
@@ -17,6 +17,10 @@ Skill collisions before mounting its runtime; it does not rewrite `AGENTS.md`.
17
17
 
18
18
  - Reads configured Kiokuko SQLite state, registered project roots, and
19
19
  repository metadata.
20
+ - When a user opens Diff review, reads only the selected native session's
21
+ workspace identity and bounded repository status/diffs. Untracked file
22
+ content is read only after explicit selection. Review results remain in
23
+ process memory until expiry unless the user downloads them.
20
24
  - Writes the configured Kiokuko database and pre-migration backups, including
21
25
  DSH leases, receipts, retrieval state, and embedding state.
22
26
  - Stores the first native input batch of each governed turn in that database
@@ -59,6 +63,10 @@ Skill collisions before mounting its runtime; it does not rewrite `AGENTS.md`.
59
63
  subprocesses only when the corresponding Kiokuko operation explicitly
60
64
  requests them. The plugin does not provide an implicit model-facing shell
61
65
  tool.
66
+ - Diff review runs fixed-argument, read-only Git subprocesses. A user-started
67
+ analysis sends the selected, sanitized snapshot and limited Kiokuko context
68
+ to the exact DSH model connection chosen in the review UI. It supplies no
69
+ tools and does not automatically retry or choose another model.
62
70
  - Skill discovery and source retrieval can contact GitHub or skills.sh when
63
71
  enabled by configuration.
64
72
  - Remote embedding requests can contact the configured endpoint. Remote
package/README.ja.md CHANGED
@@ -14,6 +14,7 @@
14
14
  - **Continuity** — 既定では無効です。`continuity.mode: active` にすると、最近の実行記録を短くまとめてモデルへ渡します。[設定方法](docs/continuity.ja.md)
15
15
  - **記憶学習** — 完了した作業から再利用できる episode・教訓候補を作ります。[設定](docs/memory-evolution.md)
16
16
  - **OrcaReplay** — モデル・ツールの動作を記録し、HTML に出力できます。[設定とコマンド](docs/orca-recording.md)
17
+ - **Diff レビュー** — DSH の右ペインで、選択した差分と Kiokuko の文脈を根拠付きで確認できます。分析は明示操作です。[使い方と制限](docs/diff-review.ja.md)
17
18
  - **日本語出力** — 対応モデルへ自然な日本語を書く同梱 Skill を渡します。[詳細](docs/japanese-output.md)
18
19
 
19
20
  ## 導入と使い方
package/dist/client.cjs CHANGED
@@ -23,6 +23,8 @@ window.__ModuleLoader__.load({
23
23
  'review.copy': 'Copy',
24
24
  'review.copied': 'Copied',
25
25
  'review.footnotes': 'Footnotes',
26
+ 'review.title': 'Diff review',
27
+ 'review.description': 'Review changes with repository facts and Kiokuko context.',
26
28
  };
27
29
  const ja = {
28
30
  'header.action': 'Session log',
@@ -37,6 +39,8 @@ window.__ModuleLoader__.load({
37
39
  'review.copy': 'コピー',
38
40
  'review.copied': 'コピーしました',
39
41
  'review.footnotes': '脚注',
42
+ 'review.title': 'Diff レビュー',
43
+ 'review.description': '差分をリポジトリの事実と Kiokuko の文脈で確認します。',
40
44
  };
41
45
  const zh = {
42
46
  'header.action': 'Session 日志',
@@ -51,6 +55,8 @@ window.__ModuleLoader__.load({
51
55
  'review.copy': '复制',
52
56
  'review.copied': '已复制',
53
57
  'review.footnotes': '脚注',
58
+ 'review.title': 'Diff 审查',
59
+ 'review.description': '结合仓库事实和 Kiokuko 上下文查看更改。',
54
60
  };
55
61
  function hostBase() {
56
62
  const origin = globalThis.location?.origin;
@@ -716,8 +722,420 @@ window.__ModuleLoader__.load({
716
722
  document.head.appendChild(style);
717
723
  return () => style.remove();
718
724
  }
725
+ const REVIEW_NS = 'kiokuko-session-log-download';
726
+ const REVIEW_KIND = 'kiokuko-diff-review';
727
+ const REVIEW_TAB_ID = 'kiokuko-dsh/diff-review';
728
+ const REVIEW_ERRORS = {
729
+ repository_changed: '差分の取得中または取得後に変更がありました。取得し直してください。',
730
+ file_changed: '現在のファイルは取得済みの差分と一致しません。差分内の内容を確認してください。',
731
+ file_link_unavailable: 'この差分から現在のファイルへは移動できません。',
732
+ turn_snapshot_unavailable: 'このターンの差分記録は利用できません。現在の差分を選んで取得してください。',
733
+ repo_unavailable: 'このセッションの Git リポジトリを確認できません。',
734
+ session_unavailable: '選択中のセッションを確認できません。開き直してください。',
735
+ session_workspace_mismatch: 'セッションと作業場所が一致しません。',
736
+ diff_too_large: '差分が上限を超えました。対象を絞って取得してください。',
737
+ git_timeout: 'Git の読み取りが時間内に終わりませんでした。',
738
+ git_unavailable: 'Git の読み取りサービスを利用できません。',
739
+ model_unavailable: '選択したモデルを利用できません。モデルの設定を確認してください。',
740
+ analysis_in_progress: 'このセッションでは分析が進行中です。完了または停止後に再試行してください。',
741
+ capture_in_progress: '差分を取得中です。完了後に再試行してください。',
742
+ review_expired: '結果の保存期間が過ぎました。差分を取得し直してください。',
743
+ unmerged_conflict: '競合中のファイルがあります。解決してから分析してください。',
744
+ unsafe_review_input: 'レビューの目的に秘密情報らしい内容があります。削除して再試行してください。',
745
+ analysis_timeout: '分析が時間内に終わりませんでした。取得済みの差分は表示しています。',
746
+ provider_failure: 'モデルへの接続に失敗しました。取得済みの差分は表示しています。',
747
+ invalid_model_json: 'モデルの出力を読み取れませんでした。取得済みの差分は表示しています。',
748
+ invalid_model_schema: 'モデルの出力形式が一致しません。取得済みの差分は表示しています。',
749
+ model_incomplete: 'モデルの回答が完了しませんでした。取得済みの差分は表示しています。',
750
+ invalid_evidence_reference: '存在しない根拠を参照する説明を除外しました。',
751
+ context_limit: '文脈が入力上限を超え、分析できませんでした。',
752
+ chunk_or_hunk_limit: '入力上限により一部の変更を分析していません。',
753
+ cancelled: '分析を停止しました。取得済みの差分は表示しています。',
754
+ };
755
+ function reviewErrorMessage(code) { return REVIEW_ERRORS[code] ?? `レビューを続行できません(${code})。`; }
756
+ const REVIEW_STATES = { 'facts-only': '差分のみ', analyzing: '分析中', analyzed: '分析完了', partial: '一部未分析', cancelled: '停止', failed: '分析失敗' };
757
+ const REVIEW_CONTEXT_SOURCES = { 'current-run': '実行中のタスク', 'completed-run': '完了済みタスク', 'review-input': '利用者入力', unavailable: '取得不可' };
758
+ const REVIEW_MEMORY_STATES = { available: '取得済み', empty: '該当なし', unavailable: '取得不可', withheld: '保留', mismatch: '不一致' };
759
+ const REVIEW_FRESHNESS = { current: '現在と一致', stale: '取得後に変更あり', unknown: '現在との一致は未確認' };
760
+ function initialReviewSession() {
761
+ return { loading: false, busy: false, error: '', availability: 'unknown', models: [], untracked: [], turns: [], mode: 'current',
762
+ selected: [], selectedUntracked: [], modelKey: '', purpose: '', runId: '', activeFileId: '', fileListOpen: true };
763
+ }
764
+ class DiffReviewClientController {
765
+ store = createSnapshotStore({ bySession: {} });
766
+ timers = new Map();
767
+ generations = new Map();
768
+ closed = false;
769
+ nextGeneration(sessionId) {
770
+ const next = (this.generations.get(sessionId) ?? 0) + 1;
771
+ this.generations.set(sessionId, next);
772
+ return next;
773
+ }
774
+ current(sessionId) { return this.store.getSnapshot().bySession[sessionId] ?? initialReviewSession(); }
775
+ publish(sessionId, next) {
776
+ if (this.closed)
777
+ return;
778
+ this.store.update(state => { state.bySession = { ...state.bySession, [sessionId]: next }; });
779
+ }
780
+ change(sessionId, changes) { this.publish(sessionId, { ...this.current(sessionId), ...changes }); }
781
+ url(sessionId, reviewId) {
782
+ const url = new URL('/api/kiokuko.diff-review', hostBase());
783
+ url.searchParams.set('sessionId', sessionId);
784
+ if (reviewId)
785
+ url.searchParams.set('reviewId', reviewId);
786
+ return url;
787
+ }
788
+ async json(url, body) {
789
+ const response = await fetch(url, body ? { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) } : {});
790
+ const value = await response.json();
791
+ if (!response.ok)
792
+ throw new Error(typeof value.code === 'string' ? reviewErrorMessage(value.code) : `HTTP ${response.status}`);
793
+ return value;
794
+ }
795
+ async load(sessionId) {
796
+ const generation = this.nextGeneration(sessionId);
797
+ this.change(sessionId, { loading: true, error: '' });
798
+ try {
799
+ const response = await this.json(this.url(sessionId));
800
+ if (this.generations.get(sessionId) !== generation)
801
+ return;
802
+ const review = response.review;
803
+ const models = response.models ?? [];
804
+ const current = this.current(sessionId);
805
+ this.change(sessionId, { loading: false, availability: String(response.availability), models, review,
806
+ untracked: response.untracked ?? [], turns: response.turns ?? [],
807
+ ...(review ? { selected: current.review?.reviewId === review.reviewId && current.selected.length ? current.selected : review.snapshot.files.filter(file => file.kind === 'text').map(file => file.fileId),
808
+ activeFileId: current.review?.reviewId === review.reviewId && current.activeFileId ? current.activeFileId : review.snapshot.files[0]?.fileId || '' } : {}),
809
+ modelKey: current.modelKey || (models[0] ? JSON.stringify([models[0].provider, models[0].model]) : ''),
810
+ error: !review && current.review ? '保存期間が過ぎました。差分を取得し直してください。' : '' });
811
+ if (review?.state === 'analyzing')
812
+ this.poll(sessionId, review.reviewId);
813
+ }
814
+ catch (error) {
815
+ if (this.generations.get(sessionId) === generation)
816
+ this.change(sessionId, { loading: false, error: messageOf(error) });
817
+ }
818
+ }
819
+ async capture(sessionId) {
820
+ const current = this.current(sessionId);
821
+ if (current.busy)
822
+ return;
823
+ const generation = this.nextGeneration(sessionId);
824
+ this.change(sessionId, { busy: true, error: '' });
825
+ try {
826
+ const review = await this.json(this.url(sessionId), { action: 'capture', sessionId, mode: current.mode,
827
+ ...(current.mode === 'turn' ? { turnSeq: current.turnSeq } : {}), untracked: current.selectedUntracked, requestId: crypto.randomUUID() });
828
+ if (this.generations.get(sessionId) !== generation)
829
+ return;
830
+ this.change(sessionId, { loading: false, busy: false, review, selected: review.snapshot.files.filter(file => file.kind === 'text').map(file => file.fileId),
831
+ activeFileId: review.snapshot.files[0]?.fileId ?? '', fileListOpen: false });
832
+ }
833
+ catch (error) {
834
+ if (this.generations.get(sessionId) === generation)
835
+ this.change(sessionId, { loading: false, busy: false, error: messageOf(error) });
836
+ }
837
+ }
838
+ async analyze(sessionId) {
839
+ const current = this.current(sessionId);
840
+ if (current.busy || !current.review || !current.modelKey)
841
+ return;
842
+ let provider, model;
843
+ try {
844
+ [provider, model] = JSON.parse(current.modelKey);
845
+ }
846
+ catch {
847
+ return;
848
+ }
849
+ if (!provider || !model)
850
+ return;
851
+ const generation = this.nextGeneration(sessionId);
852
+ this.change(sessionId, { busy: true, error: '' });
853
+ try {
854
+ const review = await this.json(this.url(sessionId), { action: 'analyze', sessionId, reviewId: current.review.reviewId,
855
+ requestId: crypto.randomUUID(), selectedFileIds: current.selected, provider, model,
856
+ ...(current.purpose.trim() ? { purpose: current.purpose.trim() } : {}), ...(current.runId ? { runId: current.runId } : {}) });
857
+ if (this.generations.get(sessionId) !== generation)
858
+ return;
859
+ this.change(sessionId, { loading: false, review });
860
+ this.poll(sessionId, review.reviewId);
861
+ }
862
+ catch (error) {
863
+ if (this.generations.get(sessionId) === generation)
864
+ this.change(sessionId, { busy: false, error: messageOf(error) });
865
+ }
866
+ }
867
+ poll(sessionId, reviewId) {
868
+ clearTimeout(this.timers.get(sessionId));
869
+ const tick = async () => {
870
+ try {
871
+ const review = await this.json(this.url(sessionId, reviewId));
872
+ if (this.current(sessionId).review?.reviewId !== reviewId)
873
+ return;
874
+ this.change(sessionId, { review, busy: review.state === 'analyzing', error: '' });
875
+ if (review.state === 'analyzing')
876
+ this.timers.set(sessionId, setTimeout(() => void tick(), document.hidden ? 5000 : 1000));
877
+ }
878
+ catch (error) {
879
+ this.change(sessionId, { busy: false, error: messageOf(error) });
880
+ }
881
+ };
882
+ this.timers.set(sessionId, setTimeout(() => void tick(), 500));
883
+ }
884
+ async refreshReview(sessionId, reviewId) {
885
+ const generation = this.generations.get(sessionId);
886
+ try {
887
+ const review = await this.json(this.url(sessionId, reviewId));
888
+ if (this.generations.get(sessionId) === generation && this.current(sessionId).review?.reviewId === reviewId)
889
+ this.change(sessionId, { review });
890
+ }
891
+ catch (error) {
892
+ if (this.generations.get(sessionId) === generation && this.current(sessionId).review?.reviewId === reviewId)
893
+ this.change(sessionId, { error: messageOf(error) });
894
+ }
895
+ }
896
+ async cancel(sessionId, reviewId) {
897
+ try {
898
+ const review = await this.json(this.url(sessionId), { action: 'cancel', sessionId, reviewId });
899
+ this.change(sessionId, { review, busy: false });
900
+ }
901
+ catch (error) {
902
+ this.change(sessionId, { error: messageOf(error) });
903
+ }
904
+ }
905
+ exportUrl(sessionId, reviewId, format) {
906
+ const url = this.url(sessionId, reviewId);
907
+ url.searchParams.set('format', format);
908
+ return url.toString();
909
+ }
910
+ async openCurrentFile(sessionId, reviewId, fileId, openFile) {
911
+ try {
912
+ const url = this.url(sessionId, reviewId);
913
+ url.searchParams.set('format', 'file-link');
914
+ url.searchParams.set('fileId', fileId);
915
+ const response = await this.json(url);
916
+ if (typeof response.address !== 'string' || !response.address.startsWith('dsh-resource://file/session/') || !openFile(response.address)) {
917
+ throw new Error('ファイルタブを開けません。取得済みの差分を確認してください。');
918
+ }
919
+ }
920
+ catch (error) {
921
+ this.change(sessionId, { error: messageOf(error) });
922
+ }
923
+ }
924
+ async dispose() {
925
+ this.closed = true;
926
+ for (const timer of this.timers.values())
927
+ clearTimeout(timer);
928
+ this.timers.clear();
929
+ }
930
+ }
931
+ function SnapshotDiff(props) {
932
+ return jsxs('div', { className: 'kiokuko-review-diff', children: props.file.hunks.map(hunk => {
933
+ let oldLine = hunk.oldStart, newLine = hunk.newStart;
934
+ return jsxs('section', { id: `kiokuko-review-${props.file.fileId}-${hunk.id}`, children: [
935
+ jsx('h4', { children: `@@ -${hunk.oldStart} +${hunk.newStart} @@` }),
936
+ jsx('pre', { children: hunk.lines.map((line, index) => {
937
+ const prefix = line[0];
938
+ const old = prefix === '+' || prefix === '\\' ? '' : String(oldLine++);
939
+ const next = prefix === '-' || prefix === '\\' ? '' : String(newLine++);
940
+ return jsx('div', { className: prefix === '+' ? 'added' : prefix === '-' ? 'removed' : '',
941
+ 'data-snapshot-id': props.snapshotId, 'data-file-id': props.file.fileId, 'data-hunk-id': hunk.id,
942
+ ...(old ? { 'data-old-line': old } : {}), ...(next ? { 'data-new-line': next } : {}),
943
+ children: `${old.padStart(5)} ${next.padStart(5)} ${line}` }, index);
944
+ }) }),
945
+ ] }, hunk.id);
946
+ }) });
947
+ }
948
+ function DiffReviewTab(props) {
949
+ const sessionId = String(props.sessionId);
950
+ const useReview = props.useDiffReview;
951
+ const controller = props.reviewController;
952
+ const openFile = props.openCurrentFile;
953
+ const useTabInfo = props.useTabInfo;
954
+ const { tab } = useTabInfo();
955
+ const state = useReview(value => value.bySession[sessionId]) ?? initialReviewSession();
956
+ useEffect(() => { void controller.load(sessionId); }, [sessionId]);
957
+ useEffect(() => { if (tab.visible && state.review)
958
+ void controller.refreshReview(sessionId, state.review.reviewId); }, [sessionId, tab.visible, state.review?.reviewId]);
959
+ useEffect(() => {
960
+ const onClose = () => { const reviewId = controller.store.getSnapshot().bySession[sessionId]?.review?.reviewId; if (reviewId)
961
+ void controller.cancel(sessionId, reviewId); };
962
+ tab.signal.addEventListener('abort', onClose, { once: true });
963
+ return () => tab.signal.removeEventListener('abort', onClose);
964
+ }, [sessionId, tab.signal]);
965
+ const review = state.review;
966
+ const active = review?.snapshot.files.find(file => file.fileId === state.activeFileId) ?? review?.snapshot.files[0];
967
+ const selectFile = (fileId) => controller.change(sessionId, { selected: state.selected.includes(fileId)
968
+ ? state.selected.filter(id => id !== fileId) : [...state.selected, fileId] });
969
+ const visitEvidence = (id, anchor) => {
970
+ const [fileId, hunkId] = id.split(':');
971
+ if (!fileId || !review?.snapshot.files.some(file => file.fileId === fileId))
972
+ return;
973
+ controller.change(sessionId, { activeFileId: fileId, fileListOpen: false });
974
+ if (hunkId)
975
+ requestAnimationFrame(() => {
976
+ const section = document.getElementById(`kiokuko-review-${fileId}-${hunkId}`);
977
+ const line = anchor?.fileId === fileId && anchor.hunkId === hunkId
978
+ ? [...(section?.querySelectorAll('[data-old-line],[data-new-line]') ?? [])].find(element => element.dataset[anchor.side === 'old' ? 'oldLine' : 'newLine'] === String(anchor.startLine)) : undefined;
979
+ (line ?? section)?.scrollIntoView({ block: 'center' });
980
+ });
981
+ };
982
+ const analysisGroups = review?.analysis ? [
983
+ { label: '影響', items: review.analysis.impact },
984
+ { label: '破壊的変更', items: review.analysis.breakingChanges },
985
+ { label: 'テストの不足', items: review.analysis.testGaps },
986
+ { label: '過去判断との不一致', items: review.analysis.memoryConflicts },
987
+ { label: '仮定', items: review.analysis.assumptions },
988
+ ] : [];
989
+ return jsxs('section', { className: 'kiokuko-review', 'aria-label': 'Diff レビュー', children: [
990
+ jsx('h2', { children: 'Diff レビュー' }),
991
+ jsxs('div', { className: 'kiokuko-review-controls', children: [
992
+ jsxs('label', { children: ['比較対象', jsx('select', { value: state.mode, onChange: (event) => controller.change(sessionId, { mode: event.target.value }), children: [
993
+ jsx('option', { value: 'current', children: '未コミット全体' }), jsx('option', { value: 'staged', children: 'ステージ済み' }),
994
+ jsx('option', { value: 'unstaged', children: '未ステージ' }), jsx('option', { value: 'turn', children: 'このターン' }),
995
+ ] })] }),
996
+ ...(state.mode === 'turn' ? [jsxs('label', { children: ['ターン差分', jsx('select', { value: String(state.turnSeq ?? ''), onChange: (event) => controller.change(sessionId, { turnSeq: event.target.value === '' ? undefined : Number(event.target.value) }),
997
+ children: [jsx('option', { value: '', children: '選択' }), ...state.turns.map(seq => jsx('option', { value: String(seq), children: `seq ${seq}` }, seq))] })] }, 'turn')] : []),
998
+ jsx('button', { type: 'button', disabled: state.busy || state.loading || state.mode === 'turn' && state.turnSeq === undefined, onClick: () => void controller.capture(sessionId), children: review ? '取得し直す' : '差分を取得' }),
999
+ review ? jsx('button', { type: 'button', disabled: state.busy, onClick: () => void controller.refreshReview(sessionId, review.reviewId), children: '鮮度を確認' }) : null,
1000
+ ] }),
1001
+ state.untracked.length ? jsxs('fieldset', { children: [jsx('legend', { children: '未追跡ファイル(明示選択)' }), ...state.untracked.map(path => jsxs('label', { children: [jsx('input', { type: 'checkbox', checked: state.selectedUntracked.includes(path), onChange: () => controller.change(sessionId, { selectedUntracked: state.selectedUntracked.includes(path) ? state.selectedUntracked.filter(item => item !== path) : [...state.selectedUntracked, path] }) }), path] }, path))] }) : null,
1002
+ jsx('p', { role: 'status', 'aria-live': 'polite', children: state.loading ? '確認中' : state.busy ? '処理中' : state.error || (state.availability === 'repo_unavailable' ? 'Git リポジトリを確認できません' : '') }),
1003
+ review ? jsxs(Fragment, { children: [
1004
+ jsx('p', { children: `${review.snapshot.capturedAt} / ${review.snapshot.mode} / ${REVIEW_STATES[review.state] ?? review.state} / 鮮度: ${REVIEW_FRESHNESS[review.freshness] ?? review.freshness}` }),
1005
+ jsx('p', { children: `文脈: ${REVIEW_CONTEXT_SOURCES[review.context.source] ?? review.context.source} / メモリ: ${REVIEW_MEMORY_STATES[review.context.memory] ?? review.context.memory}${review.context.reason ? ` (${review.context.reason})` : ''}` }),
1006
+ review.context.task ? jsx('p', { children: `タスク: ${review.context.task}` }) : null,
1007
+ review.context.reviewInput ? jsx('p', { children: `このレビューの目的(利用者入力): ${review.context.reviewInput}` }) : null,
1008
+ ...(review.context.candidates?.length ? [jsxs('label', { children: ['タスク', jsx('select', { value: state.runId, onChange: (event) => controller.change(sessionId, { runId: event.target.value }), children: [jsx('option', { value: '', children: '指定なし' }), ...review.context.candidates.map(item => jsx('option', { value: item.runId, children: `${item.task} (${item.status})` }, item.runId))] })] }, 'runs')] : []),
1009
+ jsx('p', { children: `変更ファイル: ${review.snapshot.files.length} / ${review.snapshot.totalFiles}` }),
1010
+ review.snapshot.totalFiles > review.snapshot.files.length ? jsx('p', { role: 'status', children: `${review.snapshot.totalFiles - review.snapshot.files.length} 件は取得上限で未収集です。全体リスクは未確定です。` }) : null,
1011
+ jsx('p', { children: `送信対象: ${state.selected.length} ファイル / ${review.snapshot.files.filter(file => file.kind !== 'text').length} ファイルはテキスト分析対象外` }),
1012
+ jsxs('div', { className: 'kiokuko-review-analysis-controls', children: [
1013
+ jsxs('label', { children: ['モデル', jsx('select', { value: state.modelKey, onChange: (event) => controller.change(sessionId, { modelKey: event.target.value }),
1014
+ children: [jsx('option', { value: '', children: '選択' }), ...state.models.map(item => jsx('option', { value: JSON.stringify([item.provider, item.model]), children: `${item.provider}/${item.model}` }, `${item.provider}:${item.model}`))] })] }),
1015
+ jsxs('label', { children: ['レビューの目的(任意)', jsx('textarea', { value: state.purpose, maxLength: 4000, onChange: (event) => controller.change(sessionId, { purpose: event.target.value }) })] }),
1016
+ jsx('button', { type: 'button', disabled: state.busy || !state.selected.length || !state.modelKey, onClick: () => void controller.analyze(sessionId), children: '分析する' }),
1017
+ review.state === 'analyzing' ? jsx('button', { type: 'button', onClick: () => void controller.cancel(sessionId, review.reviewId), children: '停止' }) : null,
1018
+ ] }),
1019
+ jsx('button', { type: 'button', className: 'kiokuko-review-list-toggle', onClick: () => {
1020
+ controller.change(sessionId, { fileListOpen: !state.fileListOpen });
1021
+ requestAnimationFrame(() => document.getElementById(`kiokuko-review-${review.reviewId}-${state.fileListOpen ? 'detail' : 'files'}`)?.focus());
1022
+ },
1023
+ children: state.fileListOpen ? '差分に戻る' : '変更ファイル一覧' }),
1024
+ jsxs('div', { className: 'kiokuko-review-main', 'data-list-open': String(state.fileListOpen), children: [
1025
+ jsxs('nav', { id: `kiokuko-review-${review.reviewId}-files`, tabIndex: -1, 'aria-label': '変更ファイル', children: [jsx('h3', { children: '変更ファイル' }), ...review.snapshot.files.map(file => jsxs('div', { className: 'kiokuko-review-file-row', children: [
1026
+ jsx('input', { type: 'checkbox', 'aria-label': `${file.displayPath} を分析対象にする`, checked: state.selected.includes(file.fileId), disabled: file.kind !== 'text', onChange: () => selectFile(file.fileId) }),
1027
+ jsx('button', { type: 'button', 'aria-current': active?.fileId === file.fileId ? 'true' : undefined, onClick: () => {
1028
+ controller.change(sessionId, { activeFileId: file.fileId, fileListOpen: false });
1029
+ requestAnimationFrame(() => document.getElementById(`kiokuko-review-${review.reviewId}-detail`)?.focus());
1030
+ }, children: `${file.displayPath} (${file.layer}, ${file.kind})` }),
1031
+ ] }, file.fileId))] }),
1032
+ active ? jsxs('article', { id: `kiokuko-review-${review.reviewId}-detail`, tabIndex: -1, children: [jsx('h3', { children: active.displayPath }),
1033
+ active.kind === 'text' && active.newDigest && review.snapshot.mode !== 'turn' ? jsx('button', { type: 'button',
1034
+ onClick: () => void controller.openCurrentFile(sessionId, review.reviewId, active.fileId, openFile), children: '現在のファイルへ' }) : null,
1035
+ active.reason ? jsx('p', { children: `除外・制限: ${active.reason}` }) : null,
1036
+ jsx(SnapshotDiff, { file: active, snapshotId: review.snapshot.snapshotId })] }) : jsx('p', { children: '変更はありません' }),
1037
+ ] }),
1038
+ review.context.execution?.length ? jsxs('section', { children: [jsx('h3', { children: '記録された実行証拠' }), ...review.context.execution.map((item, index) => jsx('p', { children: `${item.command}: ${item.status} / 今回の差分との一致: ${item.snapshotMatch}` }, index))] }) : null,
1039
+ jsxs('section', { children: [jsx('h3', { children: 'AI の解釈' }), jsx('p', { children: review.summary || '分析なし' }),
1040
+ review.analysis ? jsx('p', { children: `全体リスク: ${review.analysis.overallRisk}` }) : null,
1041
+ ...analysisGroups.filter(group => group.items.length).map(group => jsxs('section', { children: [jsx('h4', { children: group.label }), jsx('ul', { children: group.items.map((item, index) => jsx('li', { children: item }, index)) })] }, group.label)),
1042
+ ...review.claims.map((claim, index) => jsxs('p', { children: [claim.text, ` (確信度: ${claim.confidence}) `,
1043
+ ...claim.evidenceIds.map(id => jsx('button', { type: 'button', onClick: () => visitEvidence(id, claim.anchor), children: `根拠 ${id}` }, id)),
1044
+ ...(claim.unverifiedAssumptions.length ? [jsx('span', { children: ` 未検証の仮定: ${claim.unverifiedAssumptions.join('、')}` })] : [])] }, index))] }),
1045
+ review.unanalyzedFileIds?.length ? jsx('p', { children: `未分析: ${review.unanalyzedFileIds.map(id => review.snapshot.files.find(file => file.fileId === id)?.displayPath ?? id).join('、')}` }) : null,
1046
+ review.errors.length ? jsx('p', { role: 'status', children: `未検証: ${review.errors.map(reviewErrorMessage).join(' ')}` }) : null,
1047
+ jsxs('div', { className: 'kiokuko-review-exports', children: [jsx('a', { href: controller.exportUrl(sessionId, review.reviewId, 'markdown'), children: 'Markdown を保存' }), jsx('a', { href: controller.exportUrl(sessionId, review.reviewId, 'json'), children: 'JSON を保存' })] }),
1048
+ ] }) : null,
1049
+ ] });
1050
+ }
1051
+ function DiffReviewHeaderAction(props) {
1052
+ const openPane = props.openDiffReview;
1053
+ const [open, setOpen] = useState(false);
1054
+ const dialog = useRef(null);
1055
+ const trigger = useRef(null);
1056
+ const fallbackAbort = useRef(new AbortController());
1057
+ useEffect(() => {
1058
+ if (open && dialog.current && !dialog.current.open)
1059
+ dialog.current.showModal();
1060
+ if (!open && dialog.current?.open) {
1061
+ dialog.current.close();
1062
+ trigger.current?.focus();
1063
+ }
1064
+ }, [open]);
1065
+ const close = () => { fallbackAbort.current.abort(); setOpen(false); trigger.current?.focus(); };
1066
+ const openReview = () => {
1067
+ if (openPane())
1068
+ return;
1069
+ fallbackAbort.current = new AbortController();
1070
+ setOpen(true);
1071
+ };
1072
+ return jsxs(Fragment, { children: [
1073
+ jsx('button', { ref: trigger, type: 'button', className: 'kiokuko-session-log-button', onClick: openReview, children: 'Diff レビュー' }),
1074
+ open ? jsxs('dialog', { ref: dialog, onCancel: close, onClose: close, 'aria-label': 'Diff レビュー',
1075
+ className: 'kiokuko-review-fallback', children: [
1076
+ jsx('button', { type: 'button', onClick: close, children: '閉じる' }),
1077
+ jsx(DiffReviewTab, { ...props, useTabInfo: () => ({ tab: { signal: fallbackAbort.current.signal, visible: true } }) }),
1078
+ ] }) : null,
1079
+ ] });
1080
+ }
1081
+ function installReviewStyle() {
1082
+ const style = document.createElement('style');
1083
+ style.dataset.pluginCss = 'kiokuko-diff-review';
1084
+ style.textContent = `.kiokuko-review-fallback{width:min(1000px,95vw);height:min(85vh,900px);padding:8px;box-sizing:border-box}.kiokuko-review{box-sizing:border-box;height:100%;overflow:auto;padding:12px;color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-alias-background-primary,Canvas);font:13px/1.5 var(--dsw-font-family,system-ui)}.kiokuko-review h2{margin:0 0 12px;font-size:17px}.kiokuko-review h3{font-size:14px}.kiokuko-review button,.kiokuko-review select,.kiokuko-review textarea{font:inherit;color:inherit;background:transparent;border:1px solid var(--dsw-alias-border-l4,#888);border-radius:6px;padding:6px;min-height:36px}.kiokuko-review button{cursor:pointer}.kiokuko-review button:disabled{opacity:.55;cursor:default}.kiokuko-review :focus-visible{outline:2px solid Highlight;outline-offset:2px}.kiokuko-review-controls,.kiokuko-review-analysis-controls,.kiokuko-review-exports{display:flex;flex-wrap:wrap;gap:8px;align-items:end;margin:8px 0}.kiokuko-review label{display:flex;flex-direction:column;gap:3px}.kiokuko-review fieldset label{display:inline-flex;flex-direction:row;align-items:center;margin:4px 8px}.kiokuko-review-main{display:grid;grid-template-columns:minmax(130px,32%) minmax(0,1fr);gap:10px;min-width:0}.kiokuko-review-main nav{overflow:auto;min-width:0;max-height:55vh}.kiokuko-review-main article{overflow:auto;min-width:0}.kiokuko-review-file-row{display:flex;align-items:center;overflow-wrap:anywhere}.kiokuko-review-file-row button{text-align:left;border:0;overflow-wrap:anywhere}.kiokuko-review-file-row button[aria-current=true]{font-weight:700;text-decoration:underline}.kiokuko-review-diff pre{font:12px/1.4 ui-monospace,monospace;overflow:auto;white-space:pre}.kiokuko-review-diff .added{background:color-mix(in srgb,green 15%,transparent)}.kiokuko-review-diff .removed{background:color-mix(in srgb,red 15%,transparent)}.kiokuko-review-exports a{color:inherit;text-decoration:underline}@media(max-width:600px){.kiokuko-review-main{grid-template-columns:1fr}.kiokuko-review-main nav{max-height:180px}}`;
1085
+ style.textContent += '.kiokuko-review{container-type:inline-size}.kiokuko-review-list-toggle{display:none}@container (max-width:600px){.kiokuko-review-main{grid-template-columns:1fr}.kiokuko-review-list-toggle{display:block}.kiokuko-review-main[data-list-open=false] nav{display:none}.kiokuko-review-main[data-list-open=true] article{display:none}}';
1086
+ document.head.appendChild(style);
1087
+ return () => style.remove();
1088
+ }
719
1089
  /** Register Kiokuko's streaming Session-export browser surface. */
720
1090
  function apply(ctx) {
1091
+ const reviewController = new DiffReviewClientController();
1092
+ let rightSidebar;
1093
+ ctx.effect(() => async () => reviewController.dispose(), 'kiokuko-dsh: diff review browser lifecycle');
1094
+ ctx.effect(installReviewStyle, 'kiokuko-dsh: diff review style');
1095
+ ctx.inject?.(['sidebarRightTabs', 'sidebarRight'], scope => {
1096
+ const t = scope.locale.bind?.(REVIEW_NS) ?? ((key) => key);
1097
+ scope.effect(() => { rightSidebar = scope.sidebarRight; return () => { if (rightSidebar === scope.sidebarRight)
1098
+ rightSidebar = undefined; }; }, 'kiokuko-dsh: diff review pane availability');
1099
+ scope.effect(() => {
1100
+ const dispose = scope.sidebarRightTabs.register({
1101
+ id: REVIEW_TAB_ID, kind: REVIEW_KIND, priority: 'extension', title: () => t('review.title'),
1102
+ guide: [{ id: 'open', order: 30, title: () => t('review.title'), description: () => t('review.description') }],
1103
+ });
1104
+ return () => { void dispose(); };
1105
+ }, 'kiokuko-dsh: diff review page');
1106
+ scope.effect(() => scope.slots.inject('sidebar.right.pane.tab', () => scope.slots.register({
1107
+ name: 'sidebar.right.pane.tab', key: REVIEW_TAB_ID, locale: REVIEW_NS,
1108
+ inject: () => ({ hooks: { diffReview: reviewController.store }, reviewController,
1109
+ openCurrentFile: (address) => { if (!rightSidebar?.openResource)
1110
+ return false; try {
1111
+ rightSidebar.openResource(address);
1112
+ return true;
1113
+ }
1114
+ catch {
1115
+ return false;
1116
+ } } }),
1117
+ }, DiffReviewTab)), 'kiokuko-dsh: diff review tab body');
1118
+ });
1119
+ ctx.slots.inject('conversation.session.header.utilities', () => ctx.slots.register({
1120
+ name: 'conversation.session.header.utilities', id: 'kiokuko-diff-review-open', locale: REVIEW_NS,
1121
+ inject: () => ({ hooks: { diffReview: reviewController.store }, reviewController,
1122
+ openDiffReview: () => { if (!rightSidebar)
1123
+ return false; try {
1124
+ rightSidebar.openTab(REVIEW_KIND);
1125
+ return true;
1126
+ }
1127
+ catch {
1128
+ return false;
1129
+ } },
1130
+ openCurrentFile: (address) => { if (!rightSidebar?.openResource)
1131
+ return false; try {
1132
+ rightSidebar.openResource(address);
1133
+ return true;
1134
+ }
1135
+ catch {
1136
+ return false;
1137
+ } } }),
1138
+ }, DiffReviewHeaderAction));
721
1139
  ctx.slots.inject('conversation.input.left', () => ctx.slots.register({
722
1140
  name: 'conversation.input.left', id: 'kiokuko-lisp-status', locale: LOCALE_NAMESPACE,
723
1141
  }, LispSessionStatus));
package/dist/client.d.ts CHANGED
@@ -21,7 +21,16 @@ interface DshClientContext {
21
21
  };
22
22
  readonly locale: {
23
23
  register(namespace: string, dictionaries: Record<string, Record<string, string>>): unknown;
24
+ bind?(namespace: string): (key: string) => string;
24
25
  };
26
+ readonly sidebarRightTabs?: {
27
+ register(definition: unknown): () => unknown;
28
+ };
29
+ readonly sidebarRight?: {
30
+ openTab(kind: string): void;
31
+ openResource?(address: string): void;
32
+ };
33
+ inject?(services: readonly string[], callback: (scope: DshClientContext) => void): unknown;
25
34
  readonly slots: {
26
35
  inject(name: string, register: () => unknown): unknown;
27
36
  register(definition: {
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,UAAU,wBAAwB;IAChC,kBAAkB,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;QAAE,cAAc,IAAI,OAAO,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,CAAA;KAAE,CAAC,CAAA;IAC7G,QAAQ,EAAE;QAAE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;CACxC;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAA;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,wBAAwB,CAAA;CAC3C;AAYD,UAAU,gBAAgB;IACxB,QAAQ,CAAC,cAAc,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE;YAAE,QAAQ,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAAA;SAAE,CAAA;KAAE,CAAA;IACxF,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAAA;KAC3F,CAAA;IACD,QAAQ,CAAC,KAAK,EAAE;QACd,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,GAAG,OAAO,CAAA;QACtD,QAAQ,CACN,UAAU,EAAE;YACV,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;YACrB,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAA;YACpB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;YACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;YAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAA;YAC7D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;YACvB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAChD,EACD,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,GACrD,OAAO,CAAA;KACX,CAAA;IACD,MAAM,CAAC,KAAK,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAA;IAChF,EAAE,CAAC,KAAK,EAAE,kBAAkB,EAAE,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,OAAO,CAAA;CACtI;AAuFD,+EAA+E;AAC/E,wBAAsB,qBAAqB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,UAAU,GAAG,WAAW,CAAC,CAqBjH;AAuHD,eAAO,MAAM,MAAM,gDAAiD,CAAA;AA6apE,mEAAmE;AACnE,wBAAgB,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAmCjD"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { DshLlm } from '../dsh/session-memory-finalizer.js';
2
+ import { type DiffReview } from './schema.js';
3
+ export interface AnalyzerLimits {
4
+ maxInputBytes: number;
5
+ maxChunks: number;
6
+ maxOutputTokens: number;
7
+ deadlineMs: number;
8
+ }
9
+ export interface ModelChoice {
10
+ provider: string;
11
+ model: string;
12
+ }
13
+ /** The server, never the model, binds claims to snapshot evidence. */
14
+ export declare function analyzeReview(review: DiffReview, llm: DshLlm, choice: ModelChoice, selectedFileIds: readonly string[], purpose: string | undefined, limits: AnalyzerLimits, signal: AbortSignal): Promise<DiffReview>;
15
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/diff-review/analyzer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,oCAAoC,CAAA;AAIhE,OAAO,EAAkC,KAAK,UAAU,EAAoB,MAAM,aAAa,CAAA;AAe/F,MAAM,WAAW,cAAc;IAAG,aAAa,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE;AACzH,MAAM,WAAW,WAAW;IAAG,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AAmDhE,sEAAsE;AACtE,wBAAsB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,SAAS,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,UAAU,CAAC,CAqE3N"}