kibi-opencode 0.14.0 → 0.15.1

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 (62) hide show
  1. package/README.md +18 -12
  2. package/dist/config.d.ts +0 -3
  3. package/dist/config.js +0 -9
  4. package/dist/e2e-coverage-signals.js +1 -3
  5. package/dist/enforcement-policy.d.ts +2 -0
  6. package/dist/enforcement-policy.js +18 -3
  7. package/dist/enforcement-scope.js +1 -3
  8. package/dist/file-entity-links.js +2 -6
  9. package/dist/file-operation-reminders.d.ts +4 -4
  10. package/dist/file-operation-state.js +4 -2
  11. package/dist/init-kibi-alias.js +0 -1
  12. package/dist/init-kibi-capability.js +1 -5
  13. package/dist/kb-freshness-state.d.ts +62 -0
  14. package/dist/kb-freshness-state.js +215 -0
  15. package/dist/kibi-checkpoint-runner.d.ts +1 -1
  16. package/dist/kibi-checkpoint-runner.js +6 -3
  17. package/dist/knowledge-classifier.js +2 -1
  18. package/dist/meaningful-change-classifier.d.ts +30 -0
  19. package/dist/meaningful-change-classifier.js +103 -0
  20. package/dist/plugin-startup.js +0 -1
  21. package/dist/plugin.js +101 -391
  22. package/dist/prompt.d.ts +6 -5
  23. package/dist/prompt.js +48 -84
  24. package/dist/reconcile-engine.d.ts +15 -1
  25. package/dist/reconcile-engine.js +1 -3
  26. package/dist/scheduler.js +16 -4
  27. package/dist/session-edit-state.js +2 -1
  28. package/dist/source-linked-guidance.js +3 -1
  29. package/dist/startup-notifier.d.ts +7 -1
  30. package/dist/startup-notifier.js +51 -17
  31. package/dist/tui.js +1 -403
  32. package/dist/tui.jsx +1 -113
  33. package/dist/version-metadata.d.ts +13 -0
  34. package/dist/version-metadata.js +78 -0
  35. package/dist/version-metadata.json +1 -0
  36. package/package.json +5 -7
  37. package/dist/brief-delivery-reasons.d.ts +0 -12
  38. package/dist/brief-delivery-reasons.js +0 -132
  39. package/dist/brief-intent.d.ts +0 -41
  40. package/dist/brief-intent.js +0 -142
  41. package/dist/briefing-runtime.d.ts +0 -24
  42. package/dist/briefing-runtime.js +0 -277
  43. package/dist/graph-narrator.d.ts +0 -25
  44. package/dist/graph-narrator.js +0 -408
  45. package/dist/idle-brief-audit.d.ts +0 -36
  46. package/dist/idle-brief-audit.js +0 -186
  47. package/dist/idle-brief-paths.d.ts +0 -6
  48. package/dist/idle-brief-paths.js +0 -120
  49. package/dist/idle-brief-reader.d.ts +0 -37
  50. package/dist/idle-brief-reader.js +0 -163
  51. package/dist/idle-brief-runtime.d.ts +0 -55
  52. package/dist/idle-brief-runtime.js +0 -496
  53. package/dist/idle-brief-store.d.ts +0 -113
  54. package/dist/idle-brief-store.js +0 -262
  55. package/dist/session-fingerprint.d.ts +0 -11
  56. package/dist/session-fingerprint.js +0 -21
  57. package/dist/tui-brief-delivery.d.ts +0 -67
  58. package/dist/tui-brief-delivery.js +0 -279
  59. package/dist/tui-brief-view-model.d.ts +0 -63
  60. package/dist/tui-brief-view-model.js +0 -209
  61. package/dist/utils/brief-marker.d.ts +0 -19
  62. package/dist/utils/brief-marker.js +0 -101
@@ -1,132 +0,0 @@
1
- // Inlined from kibi-cli/operational-artifacts to avoid heavy module resolution
2
- function isOperationalArtifactPath(pathLike) {
3
- const normalized = pathLike.replaceAll("\\", "/");
4
- return /(^|\/)\.sisyphus\//.test(normalized);
5
- }
6
- const ORDER = {
7
- conflict_detected: 0,
8
- validation_issue: 1,
9
- entity_modified: 2,
10
- entity_added: 3,
11
- entity_removed: 4,
12
- relationship_changed: 5,
13
- };
14
- const TYPE_NAMES = {
15
- REQ: "requirement",
16
- FACT: "fact",
17
- TEST: "test",
18
- SCEN: "scenario",
19
- SYM: "code traceability",
20
- ADR: "decision",
21
- FLAG: "runtime flag",
22
- EVENT: "event",
23
- };
24
- function prefixName(id) {
25
- const prefix = id.split("-")[0]?.toUpperCase() ?? id;
26
- return TYPE_NAMES[prefix] ?? prefix;
27
- }
28
- function mk(kind, text, entityIds) {
29
- return { kind, text, entityIds };
30
- }
31
- function entityItems(kind, ids) {
32
- if (!ids.length)
33
- return [];
34
- const verb = kind === "entity_added" ? "Added" : kind === "entity_modified" ? "Updated" : "Removed";
35
- const grouped = new Map();
36
- for (const id of [...ids].sort()) {
37
- const prefix = id.split("-")[0]?.toUpperCase() ?? id;
38
- const group = grouped.get(prefix);
39
- if (group) {
40
- group.push(id);
41
- }
42
- else {
43
- grouped.set(prefix, [id]);
44
- }
45
- }
46
- return [...grouped.entries()].map(([, groupedIds]) => {
47
- const noun = prefixName(groupedIds[0] ?? "");
48
- const text = groupedIds.length === 1
49
- ? `${verb} ${noun} ${groupedIds[0]}`
50
- : `${verb} ${groupedIds.length} ${noun}s (${groupedIds.join(", ")})`;
51
- return mk(kind, text, groupedIds);
52
- });
53
- }
54
- function toastSummary(items) {
55
- const first = items[0]?.text?.trim() ?? "";
56
- const second = items[1]?.text?.trim() ?? "";
57
- if (first && second)
58
- return `${first}, ${second}`;
59
- return first || second || undefined;
60
- }
61
- function toastWhy(items) {
62
- if (items.some((i) => i.kind === "conflict_detected"))
63
- return "There is a knowledge conflict to resolve before using the brief.";
64
- if (items.some((i) => i.kind === "validation_issue"))
65
- return "Validation issues need attention before the update is treated as settled.";
66
- const hasEntities = items.some((i) => i.kind === "entity_added" || i.kind === "entity_modified" || i.kind === "entity_removed");
67
- const hasRelationships = items.some((i) => i.kind === "relationship_changed");
68
- if (hasEntities && hasRelationships)
69
- return "Requirements and facts were updated.";
70
- if (hasEntities)
71
- return "Entities were updated.";
72
- if (hasRelationships)
73
- return "Relationships were updated.";
74
- return undefined;
75
- }
76
- export function buildDeliveryReasons(input) {
77
- const items = [];
78
- if (input.conflictReasons?.length)
79
- items.push(mk("conflict_detected", input.conflictReasons[0]?.trim() || "Conflict detected", []));
80
- if (input.validationCount > 0)
81
- items.push(mk("validation_issue", `${input.validationCount} validation issue${input.validationCount === 1 ? "" : "s"} detected`, []));
82
- items.push(...entityItems("entity_modified", input.entitiesModified));
83
- items.push(...entityItems("entity_added", input.entitiesAdded));
84
- items.push(...entityItems("entity_removed", input.entitiesRemoved));
85
- if (input.relationshipsChanged > 0)
86
- items.push(mk("relationship_changed", `Updated ${input.relationshipsChanged} relationships`, []));
87
- if (!items.length)
88
- return undefined;
89
- items.sort((a, b) => ORDER[a.kind] - ORDER[b.kind]);
90
- return { version: 1, items, toast: { title: "Kibi Knowledge Update", summary: toastSummary(items) ?? "", whyItMatters: toastWhy(items) ?? "" } };
91
- }
92
- function isOperationalByEntityIds(item) {
93
- if (item.entityIds.length === 0)
94
- return false;
95
- return item.entityIds.every((id) => {
96
- const dashIdx = id.indexOf("-");
97
- if (dashIdx < 0)
98
- return false;
99
- const name = id.slice(dashIdx + 1);
100
- // Entity names with file extensions are likely from operational artifact files
101
- return /\.[a-zA-Z0-9]+$/.test(name);
102
- });
103
- }
104
- function isOperationalItem(item, allItems) {
105
- if (item.kind === "relationship_changed") {
106
- // relationship_changed items have no entityIds; treat as operational when
107
- // all entity-level items in the set are operational (they're relationship side-effects)
108
- const entityItems = allItems.filter((i) => i.kind === "entity_added" || i.kind === "entity_modified" || i.kind === "entity_removed");
109
- return entityItems.length > 0 && entityItems.every(isOperationalByEntityIds);
110
- }
111
- return isOperationalByEntityIds(item);
112
- }
113
- export function renderToastSummary(reasons) {
114
- const domainItems = reasons.items.filter((i) => !isOperationalItem(i, reasons.items));
115
- if (domainItems.length === 0) {
116
- return undefined; // suppress: specific-or-silent policy
117
- }
118
- return {
119
- title: "Kibi Knowledge Update",
120
- summary: toastSummary(domainItems) ?? "",
121
- whyItMatters: toastWhy(domainItems) ?? "",
122
- };
123
- }
124
- export function renderFullBriefReasons(reasons) {
125
- const domainItems = reasons.items.filter((i) => !isOperationalItem(i, reasons.items));
126
- const lines = ["## What changed", ...domainItems.map((r) => `- ${r.text}`)];
127
- const whyItMatters = toastWhy(domainItems);
128
- if (whyItMatters) {
129
- lines.push("", "## Why it matters", whyItMatters);
130
- }
131
- return lines.join("\n");
132
- }
@@ -1,41 +0,0 @@
1
- import type { RepoPosture } from "./repo-posture.js";
2
- import type { RiskClass } from "./risk-classifier.js";
3
- export interface BriefIntentParams {
4
- riskClass: RiskClass;
5
- posture: RepoPosture;
6
- maintenanceDegraded: boolean;
7
- workspaceRoot: string;
8
- branch: string;
9
- sourceFiles: string[];
10
- focusFilePath?: string;
11
- seedIds?: string[];
12
- }
13
- export interface BriefIntentResult {
14
- eligible: boolean;
15
- reason: string;
16
- fingerprint: string;
17
- sourceFiles: string[];
18
- seedIds: string[];
19
- }
20
- export interface BriefIntentInputs {
21
- riskClass: RiskClass;
22
- posture: RepoPosture;
23
- maintenanceDegraded: boolean;
24
- worktreeRoot: string;
25
- branch: string;
26
- sourceFiles: string[];
27
- focusFilePath?: string;
28
- seedIds?: string[];
29
- }
30
- export declare function deriveBriefIntent(params: BriefIntentParams): BriefIntentResult;
31
- export declare function computeBriefIntent(inputs: BriefIntentInputs): BriefIntentResult;
32
- export interface BriefingContextParams {
33
- sourceFiles: string[];
34
- seedIds?: string[];
35
- changedEntityIds?: string[];
36
- }
37
- export interface BriefingContextResult {
38
- sourceFiles: string[];
39
- seedIds: string[];
40
- }
41
- export declare function buildBriefingContext(params: BriefingContextParams): BriefingContextResult;
@@ -1,142 +0,0 @@
1
- // implements REQ-opencode-kibi-briefing-v2, REQ-opencode-smart-enforcement-v1
2
- // Inlined from kibi-cli/operational-artifacts to avoid heavy module resolution
3
- function isOperationalArtifactPath(pathLike) {
4
- const normalized = pathLike.replaceAll("\\", "/");
5
- return /(^|\/)\.sisyphus\//.test(normalized);
6
- }
7
- import { getSourceLinkedRequirementIds } from "./source-linked-guidance.js";
8
- const ELIGIBLE_RISK_CLASSES = new Set([
9
- "behavior_candidate",
10
- "traceability_candidate",
11
- ]);
12
- const STRICT_ELIGIBLE_POSTURES = new Set([
13
- "root_active",
14
- "hybrid_root_plus_vendored",
15
- ]);
16
- function sortAndDedup(files) {
17
- return [...new Set(files)].sort();
18
- }
19
- function deriveSeedIds(params) {
20
- if (params.seedIds !== undefined && params.seedIds.length > 0) {
21
- return buildBriefingContext({
22
- sourceFiles: params.sourceFiles,
23
- seedIds: params.seedIds,
24
- }).seedIds.slice(0, 3);
25
- }
26
- const focusFile = params.focusFilePath ?? params.sourceFiles[0];
27
- if (!focusFile) {
28
- return [];
29
- }
30
- return buildBriefingContext({
31
- sourceFiles: params.sourceFiles,
32
- seedIds: getSourceLinkedRequirementIds(params.workspaceRoot, focusFile),
33
- }).seedIds.slice(0, 3);
34
- }
35
- // implements REQ-opencode-kibi-briefing-v2, REQ-opencode-smart-enforcement-v1
36
- export function deriveBriefIntent(params) {
37
- const sortedSourceFiles = sortAndDedup(params.sourceFiles);
38
- const nonOperationalSourceFiles = sortedSourceFiles.filter((f) => !isOperationalArtifactPath(f));
39
- const fingerprint = `brief:${params.workspaceRoot}\0${params.branch}\0${params.riskClass}\0${nonOperationalSourceFiles.join("\0")}`;
40
- const seedIds = deriveSeedIds(params);
41
- if (sortedSourceFiles.length === 0) {
42
- return {
43
- eligible: false,
44
- reason: "Ineligible: no source files in session",
45
- fingerprint,
46
- sourceFiles: sortedSourceFiles,
47
- seedIds: [],
48
- };
49
- }
50
- if (nonOperationalSourceFiles.length === 0) {
51
- return {
52
- eligible: false,
53
- reason: "All source changes are operational task-tracking artifacts",
54
- fingerprint,
55
- sourceFiles: sortedSourceFiles,
56
- seedIds: [],
57
- };
58
- }
59
- if (!ELIGIBLE_RISK_CLASSES.has(params.riskClass)) {
60
- return {
61
- eligible: false,
62
- reason: `Ineligible: riskClass ${params.riskClass} is not auto-brief eligible`,
63
- fingerprint,
64
- sourceFiles: sortedSourceFiles,
65
- seedIds,
66
- };
67
- }
68
- if (!STRICT_ELIGIBLE_POSTURES.has(params.posture)) {
69
- return {
70
- eligible: false,
71
- reason: `Ineligible: posture ${params.posture} is not authoritative`,
72
- fingerprint,
73
- sourceFiles: sortedSourceFiles,
74
- seedIds,
75
- };
76
- }
77
- if (params.maintenanceDegraded) {
78
- return {
79
- eligible: false,
80
- reason: "Ineligible: maintenance is degraded",
81
- fingerprint,
82
- sourceFiles: sortedSourceFiles,
83
- seedIds,
84
- };
85
- }
86
- return {
87
- eligible: true,
88
- reason: "Eligible for auto-briefing",
89
- fingerprint,
90
- sourceFiles: nonOperationalSourceFiles,
91
- seedIds,
92
- };
93
- }
94
- export function computeBriefIntent(
95
- // implements REQ-opencode-kibi-briefing-v2
96
- inputs) {
97
- return deriveBriefIntent({
98
- riskClass: inputs.riskClass,
99
- posture: inputs.posture,
100
- maintenanceDegraded: inputs.maintenanceDegraded,
101
- workspaceRoot: inputs.worktreeRoot,
102
- branch: inputs.branch,
103
- sourceFiles: inputs.sourceFiles,
104
- ...(inputs.focusFilePath !== undefined
105
- ? { focusFilePath: inputs.focusFilePath }
106
- : {}),
107
- ...(inputs.seedIds !== undefined ? { seedIds: inputs.seedIds } : {}),
108
- });
109
- }
110
- export function buildBriefingContext(
111
- // implements REQ-opencode-kibi-briefing-v6
112
- params) {
113
- const sourceFiles = [...new Set(params.sourceFiles)].sort();
114
- const seen = new Set();
115
- const seeds = [];
116
- // Take first 3 changed entity IDs in original order, dedupe, sort
117
- if (params.changedEntityIds) {
118
- for (const id of params.changedEntityIds.slice(0, 3)) {
119
- if (!seen.has(id)) {
120
- seeds.push(id);
121
- seen.add(id);
122
- }
123
- }
124
- }
125
- // Fill remaining slots from seedIds in original order
126
- if (params.seedIds) {
127
- for (const id of params.seedIds) {
128
- if (seeds.length >= 5)
129
- break;
130
- if (!seen.has(id)) {
131
- seeds.push(id);
132
- seen.add(id);
133
- }
134
- }
135
- }
136
- // Sort final seedIds alphabetically
137
- seeds.sort((a, b) => a.localeCompare(b));
138
- return {
139
- sourceFiles,
140
- seedIds: seeds,
141
- };
142
- }
@@ -1,24 +0,0 @@
1
- import type { BriefIntentResult } from "./brief-intent.js";
2
- export type BriefingWorkspaceCtx = {
3
- workspaceRoot: string;
4
- branch: string;
5
- directory?: string;
6
- workspace?: string;
7
- ttlMs?: number;
8
- };
9
- export type BriefingCitation = {
10
- id: string;
11
- type?: string;
12
- title?: string;
13
- source?: string;
14
- textRef?: string;
15
- };
16
- export type BriefingRuntimeResult = {
17
- state: "ready" | "tldr_fallback" | "no_briefing";
18
- promptBlock: string;
19
- tldr: string;
20
- citations: BriefingCitation[];
21
- showManualCue: boolean;
22
- toastMessage: string;
23
- };
24
- export declare function fetchBriefingResult(client: unknown, workspaceCtx: BriefingWorkspaceCtx, intentResult: BriefIntentResult): Promise<BriefingRuntimeResult>;
@@ -1,277 +0,0 @@
1
- // implements REQ-opencode-kibi-briefing-v2
2
- const DEFAULT_TTL_MS = 300_000;
3
- const WORKER_TITLE = "Kibi Auto Brief Worker";
4
- const READY_TOAST = "Kibi brief ready — summary added to guidance.";
5
- const TLDR_FALLBACK_TOAST = "Kibi brief summary added — use /brief-kibi for full details.";
6
- const UNAVAILABLE_TOAST = "Kibi brief unavailable — keeping /brief-kibi manual path.";
7
- const DEFAULT_WHY_IT_MATTERS = "This update changes how current project knowledge should be interpreted.";
8
- const PROMPT_INSTRUCTION = "Call only kb_briefing_generate once with the provided sourceFiles and seedIds. If briefingState is ready, copy only cited fields. If briefingState is no_briefing, return empty promptBlock/citations and keep manual cue availability. Never invent claims.";
9
- const PROMPT_FORMAT = {
10
- type: "json_schema",
11
- schema: {
12
- type: "object",
13
- properties: {
14
- briefingState: { type: "string" },
15
- tldr: { type: "string" },
16
- promptBlock: { type: "string" },
17
- citations: { type: "array", items: { type: "object" } },
18
- activationState: { type: "string" },
19
- confidence: { type: "string" },
20
- freshness: { type: "string" },
21
- },
22
- required: ["briefingState"],
23
- },
24
- };
25
- const workerSessionIds = new Map();
26
- const workerSessionPromises = new Map();
27
- const resultCache = new Map();
28
- const inFlightResults = new Map();
29
- function noBriefingResult() {
30
- return {
31
- state: "no_briefing",
32
- promptBlock: "",
33
- tldr: "",
34
- citations: [],
35
- showManualCue: true,
36
- toastMessage: UNAVAILABLE_TOAST,
37
- };
38
- }
39
- function workspaceSessionKey(workspaceCtx) {
40
- return `${workspaceCtx.workspaceRoot}\0${workspaceCtx.branch}`;
41
- }
42
- function asRecord(value) {
43
- return typeof value === "object" && value !== null
44
- ? value
45
- : null;
46
- }
47
- function asString(value) {
48
- return typeof value === "string" ? value : "";
49
- }
50
- function sanitizePromptBlock(value) {
51
- return asString(value).trim();
52
- }
53
- function sanitizeCitations(value) {
54
- if (!Array.isArray(value)) {
55
- return [];
56
- }
57
- const citations = [];
58
- for (const item of value) {
59
- const record = asRecord(item);
60
- if (!record) {
61
- continue;
62
- }
63
- const id = asString(record.id).trim();
64
- if (!id) {
65
- continue;
66
- }
67
- const citation = { id };
68
- const type = asString(record.type).trim();
69
- const title = asString(record.title).trim();
70
- const source = asString(record.source).trim();
71
- const textRef = asString(record.textRef).trim();
72
- if (type) {
73
- citation.type = type;
74
- }
75
- if (title) {
76
- citation.title = title;
77
- }
78
- if (source) {
79
- citation.source = source;
80
- }
81
- if (textRef) {
82
- citation.textRef = textRef;
83
- }
84
- citations.push(citation);
85
- }
86
- return citations;
87
- }
88
- function hasBriefingState(value) {
89
- const record = asRecord(value);
90
- return record !== null && "briefingState" in record;
91
- }
92
- function extractParts(response) {
93
- const root = asRecord(response);
94
- if (!root) {
95
- return [];
96
- }
97
- const data = asRecord(root.data);
98
- const parts = data?.parts ?? root.parts;
99
- return Array.isArray(parts) ? parts : [];
100
- }
101
- function parsePromptPayload(response) {
102
- const parts = extractParts(response);
103
- for (let index = parts.length - 1; index >= 0; index -= 1) {
104
- const part = asRecord(parts[index]);
105
- if (!part || part.type !== "text") {
106
- continue;
107
- }
108
- const text = asString(part.text);
109
- if (!text) {
110
- continue;
111
- }
112
- try {
113
- const parsed = JSON.parse(text);
114
- if (hasBriefingState(parsed)) {
115
- return parsed;
116
- }
117
- }
118
- catch {
119
- // Ignore malformed text parts and continue searching from the end.
120
- }
121
- }
122
- return null;
123
- }
124
- function normalizeResult(payload) {
125
- if (!payload) {
126
- return noBriefingResult();
127
- }
128
- const briefingState = asString(payload.briefingState).trim();
129
- const tldr = asString(payload.tldr).trim();
130
- const promptBlock = sanitizePromptBlock(payload.promptBlock);
131
- if (briefingState === "ready" && promptBlock) {
132
- return {
133
- state: "ready",
134
- promptBlock,
135
- tldr,
136
- citations: sanitizeCitations(payload.citations),
137
- showManualCue: false,
138
- toastMessage: READY_TOAST,
139
- };
140
- }
141
- if (briefingState === "ready" && tldr) {
142
- return {
143
- state: "tldr_fallback",
144
- promptBlock: `- What changed: ${tldr}\n- Why it matters: ${DEFAULT_WHY_IT_MATTERS}`,
145
- tldr,
146
- citations: [],
147
- showManualCue: true,
148
- toastMessage: TLDR_FALLBACK_TOAST,
149
- };
150
- }
151
- return noBriefingResult();
152
- }
153
- function getSessionApi(client) {
154
- const root = asRecord(client);
155
- const session = asRecord(root?.session);
156
- if (!session) {
157
- return null;
158
- }
159
- const create = session.create;
160
- const prompt = session.prompt;
161
- if (typeof create !== "function" || typeof prompt !== "function") {
162
- return null;
163
- }
164
- return {
165
- create: create,
166
- prompt: prompt,
167
- };
168
- }
169
- function extractSessionId(response) {
170
- const root = asRecord(response);
171
- if (!root) {
172
- return null;
173
- }
174
- const directId = asString(root.id).trim();
175
- if (directId) {
176
- return directId;
177
- }
178
- const data = asRecord(root.data);
179
- const dataId = asString(data?.id).trim();
180
- return dataId || null;
181
- }
182
- async function getWorkerSessionId(sessionApi, workspaceCtx) {
183
- const key = workspaceSessionKey(workspaceCtx);
184
- const existing = workerSessionIds.get(key);
185
- if (existing) {
186
- return existing;
187
- }
188
- const pending = workerSessionPromises.get(key);
189
- if (pending) {
190
- return pending;
191
- }
192
- const promise = (async () => {
193
- const response = await sessionApi.create({
194
- directory: workspaceCtx.workspaceRoot,
195
- title: WORKER_TITLE,
196
- });
197
- const sessionId = extractSessionId(response);
198
- if (!sessionId) {
199
- throw new Error("Failed to resolve worker session ID");
200
- }
201
- workerSessionIds.set(key, sessionId);
202
- return sessionId;
203
- })().finally(() => {
204
- workerSessionPromises.delete(key);
205
- });
206
- workerSessionPromises.set(key, promise);
207
- return promise;
208
- }
209
- async function loadBriefingResult(sessionApi, workspaceCtx, intentResult) {
210
- const sessionID = await getWorkerSessionId(sessionApi, workspaceCtx);
211
- const response = await sessionApi.prompt({
212
- sessionID,
213
- tools: { kb_briefing_generate: true },
214
- format: PROMPT_FORMAT,
215
- parts: [
216
- {
217
- type: "text",
218
- text: PROMPT_INSTRUCTION,
219
- },
220
- {
221
- type: "text",
222
- text: JSON.stringify({
223
- sourceFiles: intentResult.sourceFiles,
224
- seedIds: intentResult.seedIds,
225
- }),
226
- },
227
- ],
228
- });
229
- return normalizeResult(parsePromptPayload(response));
230
- }
231
- // implements REQ-opencode-kibi-briefing-v2
232
- export async function fetchBriefingResult(client, workspaceCtx, intentResult) {
233
- const ttlMs = workspaceCtx.ttlMs ?? DEFAULT_TTL_MS;
234
- const cached = resultCache.get(intentResult.fingerprint);
235
- const now = Date.now();
236
- if (cached && now - cached.timestamp <= ttlMs) {
237
- return cached.result;
238
- }
239
- if (cached) {
240
- resultCache.delete(intentResult.fingerprint);
241
- }
242
- const pending = inFlightResults.get(intentResult.fingerprint);
243
- if (pending) {
244
- return pending;
245
- }
246
- const sessionApi = getSessionApi(client);
247
- if (!sessionApi || !intentResult.eligible) {
248
- const result = noBriefingResult();
249
- resultCache.set(intentResult.fingerprint, {
250
- result,
251
- timestamp: now,
252
- });
253
- return result;
254
- }
255
- const promise = (async () => {
256
- try {
257
- const result = await loadBriefingResult(sessionApi, workspaceCtx, intentResult);
258
- resultCache.set(intentResult.fingerprint, {
259
- result,
260
- timestamp: Date.now(),
261
- });
262
- return result;
263
- }
264
- catch {
265
- const result = noBriefingResult();
266
- resultCache.set(intentResult.fingerprint, {
267
- result,
268
- timestamp: Date.now(),
269
- });
270
- return result;
271
- }
272
- })().finally(() => {
273
- inFlightResults.delete(intentResult.fingerprint);
274
- });
275
- inFlightResults.set(intentResult.fingerprint, promise);
276
- return promise;
277
- }
@@ -1,25 +0,0 @@
1
- export interface GraphNarrativeResult {
2
- headline: string;
3
- tldr: string;
4
- domains: Array<{
5
- name: string;
6
- changes: string[];
7
- }>;
8
- relationshipChanges: string[];
9
- validationStatus: string;
10
- }
11
- export declare function generateGraphNarrative(client: unknown, workspaceCtx: {
12
- workspaceRoot: string;
13
- branch: string;
14
- }, changedEntityIds: string[], changedRelationships: Array<{
15
- from: string;
16
- to: string;
17
- type: string;
18
- }>, checkResult: {
19
- count: number;
20
- violations: Array<{
21
- rule: string;
22
- entityId: string;
23
- description: string;
24
- }>;
25
- }): Promise<GraphNarrativeResult | null>;