@cyber-dash-tech/revela 0.9.0 → 0.10.0
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/README.md +29 -4
- package/README.zh-CN.md +29 -4
- package/designs/monet/DESIGN.md +9 -9
- package/designs/starter/DESIGN.md +8 -8
- package/designs/summit/DESIGN.md +9 -9
- package/lib/commands/help.ts +2 -0
- package/lib/commands/inspect.ts +23 -0
- package/lib/commands/refine.ts +26 -0
- package/lib/commands/review.ts +8 -2
- package/lib/decks-state.ts +476 -6
- package/lib/inspect/open.ts +61 -0
- package/lib/inspect/prompt.ts +32 -0
- package/lib/inspect/request.ts +70 -0
- package/lib/inspect/requests.ts +86 -0
- package/lib/inspect/server.ts +1063 -0
- package/lib/inspect/slide-index.ts +12 -0
- package/lib/inspection-context/compile.ts +346 -0
- package/lib/inspection-context/match.ts +169 -0
- package/lib/inspection-context/project.ts +263 -0
- package/lib/inspection-context/result.ts +160 -0
- package/lib/refine/open.ts +68 -0
- package/lib/refine/server.ts +1581 -0
- package/package.json +1 -1
- package/plugin.ts +22 -0
- package/skill/SKILL.md +10 -5
- package/tools/decks.ts +12 -2
- package/tools/inspection-context.ts +22 -0
- package/tools/inspection-result.ts +63 -0
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import type { NarrativeBrief, NarrativeRole } from "../decks-state"
|
|
2
|
+
import type { InspectionContext, InspectionEvidenceTrace, InspectionGap } from "./compile"
|
|
3
|
+
import type { InspectionElementMatch, InspectionElementSnapshot, InspectionMatchConfidence } from "./match"
|
|
4
|
+
|
|
5
|
+
export interface InspectionPromptProjection {
|
|
6
|
+
version: 1
|
|
7
|
+
deck: InspectionProjectionDeck
|
|
8
|
+
selectedElement: InspectionProjectionElement
|
|
9
|
+
match: InspectionProjectionMatch
|
|
10
|
+
cards: {
|
|
11
|
+
source: InspectionSourceProjection
|
|
12
|
+
evidence: InspectionEvidenceProjection
|
|
13
|
+
caveats: InspectionCaveatsProjection
|
|
14
|
+
objective: InspectionObjectiveProjection
|
|
15
|
+
appendix: InspectionAppendixProjection
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface InspectionProjectionDeck {
|
|
20
|
+
slug: string
|
|
21
|
+
goal: string
|
|
22
|
+
audience?: string
|
|
23
|
+
language?: string
|
|
24
|
+
narrativeBrief?: NarrativeBrief
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface InspectionProjectionElement {
|
|
28
|
+
scope?: "element" | "selection" | "slide"
|
|
29
|
+
slideIndex?: number
|
|
30
|
+
text?: string
|
|
31
|
+
elements?: Array<{
|
|
32
|
+
text?: string
|
|
33
|
+
tagName?: string
|
|
34
|
+
classList: string[]
|
|
35
|
+
role?: string
|
|
36
|
+
}>
|
|
37
|
+
tagName?: string
|
|
38
|
+
classList: string[]
|
|
39
|
+
role?: string
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface InspectionProjectionMatch {
|
|
43
|
+
confidence: InspectionMatchConfidence
|
|
44
|
+
reason: string
|
|
45
|
+
slide?: {
|
|
46
|
+
index: number
|
|
47
|
+
title: string
|
|
48
|
+
purpose?: string
|
|
49
|
+
narrativeRole?: NarrativeRole
|
|
50
|
+
}
|
|
51
|
+
claim?: {
|
|
52
|
+
id: string
|
|
53
|
+
origin: string
|
|
54
|
+
text: string
|
|
55
|
+
evidenceSensitive: boolean
|
|
56
|
+
evidenceSupport: string
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface InspectionSourceProjection {
|
|
61
|
+
evidence: InspectionEvidenceProjectionTrace[]
|
|
62
|
+
missingSourceGaps: InspectionGapProjection[]
|
|
63
|
+
weakSourceGaps: InspectionGapProjection[]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface InspectionEvidenceProjection {
|
|
67
|
+
matchedClaim?: string
|
|
68
|
+
evidenceSupport?: string
|
|
69
|
+
traces: InspectionEvidenceProjectionTrace[]
|
|
70
|
+
gaps: InspectionGapProjection[]
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface InspectionCaveatsProjection {
|
|
74
|
+
caveats: string[]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface InspectionObjectiveProjection {
|
|
78
|
+
slidePurpose?: string
|
|
79
|
+
narrativeRole?: NarrativeRole
|
|
80
|
+
deckGoal: string
|
|
81
|
+
audience?: string
|
|
82
|
+
decisionOrAction?: string
|
|
83
|
+
audienceBeliefBefore?: string
|
|
84
|
+
audienceBeliefAfter?: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface InspectionAppendixProjection {
|
|
88
|
+
candidates: Array<{
|
|
89
|
+
slideIndex: number
|
|
90
|
+
slideTitle: string
|
|
91
|
+
reason: string
|
|
92
|
+
evidence: InspectionEvidenceProjectionTrace[]
|
|
93
|
+
}>
|
|
94
|
+
relatedRisks: string[]
|
|
95
|
+
relatedObjections: string[]
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface InspectionEvidenceProjectionTrace {
|
|
99
|
+
source: string
|
|
100
|
+
sourcePath?: string
|
|
101
|
+
findingsFile?: string
|
|
102
|
+
location?: string
|
|
103
|
+
page?: string
|
|
104
|
+
url?: string
|
|
105
|
+
quote?: string
|
|
106
|
+
caveat?: string
|
|
107
|
+
extractedTextPath?: string
|
|
108
|
+
extractedManifestPath?: string
|
|
109
|
+
hasDetail: boolean
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface InspectionGapProjection {
|
|
113
|
+
type: string
|
|
114
|
+
claimText: string
|
|
115
|
+
message: string
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function projectInspectionMatch(
|
|
119
|
+
context: InspectionContext,
|
|
120
|
+
match: InspectionElementMatch,
|
|
121
|
+
snapshot: InspectionElementSnapshot = {},
|
|
122
|
+
): InspectionPromptProjection {
|
|
123
|
+
const slide = match.slide
|
|
124
|
+
const claim = match.claim
|
|
125
|
+
const traces = match.evidence.map(projectEvidenceTrace)
|
|
126
|
+
const gaps = match.gaps.map(projectGap)
|
|
127
|
+
const narrativeBrief = context.narrativeBrief
|
|
128
|
+
|
|
129
|
+
return {
|
|
130
|
+
version: 1,
|
|
131
|
+
deck: {
|
|
132
|
+
slug: context.slug,
|
|
133
|
+
goal: truncate(context.goal, 320),
|
|
134
|
+
audience: truncateOptional(context.audience, 160),
|
|
135
|
+
language: truncateOptional(context.language, 80),
|
|
136
|
+
narrativeBrief: narrativeBrief ? projectNarrativeBrief(narrativeBrief) : undefined,
|
|
137
|
+
},
|
|
138
|
+
selectedElement: {
|
|
139
|
+
slideIndex: snapshot.slideIndex,
|
|
140
|
+
scope: snapshot.scope,
|
|
141
|
+
text: truncateOptional(snapshot.selectedText || snapshot.text, 700),
|
|
142
|
+
elements: snapshot.elements?.slice(0, 12).map((item) => ({
|
|
143
|
+
text: truncateOptional(item.text, 320),
|
|
144
|
+
tagName: truncateOptional(item.tagName, 40),
|
|
145
|
+
classList: (item.classList ?? []).slice(0, 8).map((className) => truncate(className, 80)),
|
|
146
|
+
role: truncateOptional(item.role, 80),
|
|
147
|
+
})),
|
|
148
|
+
tagName: truncateOptional(snapshot.tagName, 40),
|
|
149
|
+
classList: (snapshot.classList ?? []).slice(0, 12).map((item) => truncate(item, 80)),
|
|
150
|
+
role: truncateOptional(snapshot.role, 80),
|
|
151
|
+
},
|
|
152
|
+
match: {
|
|
153
|
+
confidence: match.confidence,
|
|
154
|
+
reason: match.reason,
|
|
155
|
+
slide: slide
|
|
156
|
+
? {
|
|
157
|
+
index: slide.index,
|
|
158
|
+
title: truncate(slide.title, 180),
|
|
159
|
+
purpose: truncateOptional(slide.purpose, 240),
|
|
160
|
+
narrativeRole: slide.narrativeRole,
|
|
161
|
+
}
|
|
162
|
+
: undefined,
|
|
163
|
+
claim: claim
|
|
164
|
+
? {
|
|
165
|
+
id: claim.id,
|
|
166
|
+
origin: claim.origin,
|
|
167
|
+
text: truncate(claim.text, 500),
|
|
168
|
+
evidenceSensitive: claim.evidenceSensitive,
|
|
169
|
+
evidenceSupport: claim.evidenceSupport,
|
|
170
|
+
}
|
|
171
|
+
: undefined,
|
|
172
|
+
},
|
|
173
|
+
cards: {
|
|
174
|
+
source: {
|
|
175
|
+
evidence: traces,
|
|
176
|
+
missingSourceGaps: gaps.filter((gap) => gap.type === "missing_evidence"),
|
|
177
|
+
weakSourceGaps: gaps.filter((gap) => gap.type === "weak_evidence"),
|
|
178
|
+
},
|
|
179
|
+
evidence: {
|
|
180
|
+
matchedClaim: claim ? truncate(claim.text, 500) : undefined,
|
|
181
|
+
evidenceSupport: claim?.evidenceSupport,
|
|
182
|
+
traces,
|
|
183
|
+
gaps,
|
|
184
|
+
},
|
|
185
|
+
caveats: {
|
|
186
|
+
caveats: match.caveats.map((item) => truncate(item, 280)).slice(0, 8),
|
|
187
|
+
},
|
|
188
|
+
objective: {
|
|
189
|
+
slidePurpose: truncateOptional(slide?.purpose, 240),
|
|
190
|
+
narrativeRole: slide?.narrativeRole,
|
|
191
|
+
deckGoal: truncate(context.goal, 320),
|
|
192
|
+
audience: truncateOptional(context.audience, 160),
|
|
193
|
+
decisionOrAction: truncateOptional(narrativeBrief?.decisionOrAction, 240),
|
|
194
|
+
audienceBeliefBefore: truncateOptional(narrativeBrief?.audienceBeliefBefore, 240),
|
|
195
|
+
audienceBeliefAfter: truncateOptional(narrativeBrief?.audienceBeliefAfter, 240),
|
|
196
|
+
},
|
|
197
|
+
appendix: {
|
|
198
|
+
candidates: match.appendixCandidates.slice(0, 5).map((candidate) => ({
|
|
199
|
+
slideIndex: candidate.slideIndex,
|
|
200
|
+
slideTitle: truncate(candidate.slideTitle, 180),
|
|
201
|
+
reason: truncate(candidate.reason, 240),
|
|
202
|
+
evidence: candidate.evidence.map(projectEvidenceTrace),
|
|
203
|
+
})),
|
|
204
|
+
relatedRisks: relatedNarrativeText(context.riskContext, slide?.index),
|
|
205
|
+
relatedObjections: relatedNarrativeText(context.objectionContext, slide?.index),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function projectEvidenceTrace(trace: InspectionEvidenceTrace): InspectionEvidenceProjectionTrace {
|
|
212
|
+
return {
|
|
213
|
+
source: truncate(trace.source, 180),
|
|
214
|
+
sourcePath: truncateOptional(trace.sourcePath, 220),
|
|
215
|
+
findingsFile: truncateOptional(trace.findingsFile, 220),
|
|
216
|
+
location: truncateOptional(trace.location, 120),
|
|
217
|
+
page: truncateOptional(trace.page, 80),
|
|
218
|
+
url: truncateOptional(trace.url, 240),
|
|
219
|
+
quote: truncateOptional(trace.quote, 500),
|
|
220
|
+
caveat: truncateOptional(trace.caveat, 280),
|
|
221
|
+
extractedTextPath: truncateOptional(trace.extractedTextPath, 220),
|
|
222
|
+
extractedManifestPath: truncateOptional(trace.extractedManifestPath, 220),
|
|
223
|
+
hasDetail: trace.hasDetail,
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function projectGap(gap: InspectionGap): InspectionGapProjection {
|
|
228
|
+
return {
|
|
229
|
+
type: gap.type,
|
|
230
|
+
claimText: truncate(gap.claimText, 500),
|
|
231
|
+
message: truncate(gap.message, 280),
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function projectNarrativeBrief(brief: NarrativeBrief): NarrativeBrief {
|
|
236
|
+
return {
|
|
237
|
+
audienceBeliefBefore: truncateOptional(brief.audienceBeliefBefore, 240),
|
|
238
|
+
audienceBeliefAfter: truncateOptional(brief.audienceBeliefAfter, 240),
|
|
239
|
+
decisionOrAction: truncateOptional(brief.decisionOrAction, 240),
|
|
240
|
+
narrativeArc: truncateOptional(brief.narrativeArc, 240),
|
|
241
|
+
keyClaims: brief.keyClaims.map((item) => truncate(item, 240)).slice(0, 8),
|
|
242
|
+
objections: brief.objections.map((item) => truncate(item, 240)).slice(0, 8),
|
|
243
|
+
risks: brief.risks.map((item) => truncate(item, 240)).slice(0, 8),
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function relatedNarrativeText(items: InspectionContext["riskContext"], slideIndex: number | undefined): string[] {
|
|
248
|
+
return items
|
|
249
|
+
.filter((item) => item.slideIndex === undefined || item.slideIndex === slideIndex)
|
|
250
|
+
.map((item) => truncate(item.text, 240))
|
|
251
|
+
.slice(0, 6)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function truncateOptional(value: string | undefined, max: number): string | undefined {
|
|
255
|
+
if (value === undefined) return undefined
|
|
256
|
+
return truncate(value, max)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function truncate(value: string, max: number): string {
|
|
260
|
+
const text = String(value ?? "").replace(/\s+/g, " ").trim()
|
|
261
|
+
if (text.length <= max) return text
|
|
262
|
+
return text.slice(0, Math.max(0, max - 3)).trimEnd() + "..."
|
|
263
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { InspectionMatchConfidence } from "./match"
|
|
2
|
+
import type { InspectionPromptProjection } from "./project"
|
|
3
|
+
|
|
4
|
+
export type InspectionResultStatus = "success" | "no_match"
|
|
5
|
+
export type InspectionPurposeStatus = "clear" | "weak" | "misplaced" | "unknown"
|
|
6
|
+
export type InspectionSourceStatus = "supported" | "weak" | "unsupported" | "not_needed" | "unknown"
|
|
7
|
+
|
|
8
|
+
export interface InspectionResult {
|
|
9
|
+
version: 1
|
|
10
|
+
requestId?: string
|
|
11
|
+
status: InspectionResultStatus
|
|
12
|
+
selectedText?: string
|
|
13
|
+
slide?: {
|
|
14
|
+
index: number
|
|
15
|
+
title: string
|
|
16
|
+
}
|
|
17
|
+
matchConfidence: InspectionMatchConfidence
|
|
18
|
+
cards: {
|
|
19
|
+
purpose: PurposeCard
|
|
20
|
+
source: SourceCard
|
|
21
|
+
}
|
|
22
|
+
stale?: {
|
|
23
|
+
stale: boolean
|
|
24
|
+
reason?: string
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface SupportSourceItem {
|
|
29
|
+
source: string
|
|
30
|
+
sourcePath?: string
|
|
31
|
+
findingsFile?: string
|
|
32
|
+
location?: string
|
|
33
|
+
page?: string
|
|
34
|
+
url?: string
|
|
35
|
+
quote?: string
|
|
36
|
+
caveat?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PurposeCard {
|
|
40
|
+
status: InspectionPurposeStatus
|
|
41
|
+
role?: string
|
|
42
|
+
rationale: string
|
|
43
|
+
whyItMatters: string
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SourceCard {
|
|
47
|
+
status: InspectionSourceStatus
|
|
48
|
+
matchedClaim?: string
|
|
49
|
+
sources: SupportSourceItem[]
|
|
50
|
+
warnings: string[]
|
|
51
|
+
gaps: string[]
|
|
52
|
+
caveats: string[]
|
|
53
|
+
rationale: string
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function buildDeterministicInspectionResult(
|
|
57
|
+
projection: InspectionPromptProjection,
|
|
58
|
+
options: { requestId?: string; staleReason?: string } = {},
|
|
59
|
+
): InspectionResult {
|
|
60
|
+
const slide = projection.match.slide
|
|
61
|
+
const evidence = projection.cards.source.evidence
|
|
62
|
+
const gaps = projection.cards.evidence.gaps
|
|
63
|
+
const missingGaps = projection.cards.source.missingSourceGaps
|
|
64
|
+
const weakGaps = projection.cards.source.weakSourceGaps
|
|
65
|
+
const noMatch = projection.match.confidence === "none" || !slide
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
version: 1,
|
|
69
|
+
requestId: options.requestId,
|
|
70
|
+
status: noMatch ? "no_match" : "success",
|
|
71
|
+
selectedText: projection.selectedElement.text,
|
|
72
|
+
slide: slide ? { index: slide.index, title: slide.title } : undefined,
|
|
73
|
+
matchConfidence: projection.match.confidence,
|
|
74
|
+
cards: {
|
|
75
|
+
purpose: {
|
|
76
|
+
status: purposeStatus(projection, noMatch),
|
|
77
|
+
role: projection.cards.objective.narrativeRole,
|
|
78
|
+
rationale: purposeRationale(projection, noMatch),
|
|
79
|
+
whyItMatters: purposeWhyItMatters(projection, noMatch),
|
|
80
|
+
},
|
|
81
|
+
source: {
|
|
82
|
+
status: sourceStatus(projection, noMatch),
|
|
83
|
+
matchedClaim: projection.cards.evidence.matchedClaim,
|
|
84
|
+
sources: evidence.map((item) => ({
|
|
85
|
+
source: item.source,
|
|
86
|
+
sourcePath: item.sourcePath,
|
|
87
|
+
findingsFile: item.findingsFile,
|
|
88
|
+
location: item.location,
|
|
89
|
+
page: item.page,
|
|
90
|
+
url: item.url,
|
|
91
|
+
quote: item.quote,
|
|
92
|
+
caveat: item.caveat,
|
|
93
|
+
})),
|
|
94
|
+
warnings: sourceWarnings(missingGaps.length, weakGaps.length, noMatch),
|
|
95
|
+
gaps: gaps.map((gap) => gap.message),
|
|
96
|
+
caveats: sourceCaveats(projection),
|
|
97
|
+
rationale: sourceRationale(projection, noMatch),
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
stale: options.staleReason ? { stale: true, reason: options.staleReason } : undefined,
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function purposeStatus(projection: InspectionPromptProjection, noMatch: boolean): InspectionPurposeStatus {
|
|
105
|
+
if (noMatch) return "unknown"
|
|
106
|
+
if (projection.cards.objective.narrativeRole || projection.cards.objective.slidePurpose) return "clear"
|
|
107
|
+
return "unknown"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function purposeRationale(projection: InspectionPromptProjection, noMatch: boolean): string {
|
|
111
|
+
if (noMatch) return "No matched slide is available to explain why this selection appears here."
|
|
112
|
+
const role = projection.cards.objective.narrativeRole
|
|
113
|
+
const purpose = projection.cards.objective.slidePurpose
|
|
114
|
+
if (role && purpose) return `The selection appears inside a ${role} slide whose recorded purpose is: ${purpose}`
|
|
115
|
+
if (purpose) return `The selection appears on a slide whose recorded purpose is: ${purpose}`
|
|
116
|
+
if (role) return `The selection appears inside a slide with recorded narrative role: ${role}`
|
|
117
|
+
return "The deterministic fallback cannot explain placement because the slide has no recorded purpose or narrative role."
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function purposeWhyItMatters(projection: InspectionPromptProjection, noMatch: boolean): string {
|
|
121
|
+
if (noMatch) return "Without a matched slide, the inspector cannot connect this selection to the deck narrative."
|
|
122
|
+
const deckGoal = projection.cards.objective.deckGoal
|
|
123
|
+
const audience = projection.cards.objective.audience
|
|
124
|
+
if (deckGoal && audience) return `It matters because this selection contributes to the deck goal (${deckGoal}) for ${audience}.`
|
|
125
|
+
if (deckGoal) return `It matters because this selection contributes to the deck goal: ${deckGoal}`
|
|
126
|
+
if (audience) return `It matters because this selection is part of the message being shaped for ${audience}.`
|
|
127
|
+
return "It matters as part of this slide's communication job, but the deterministic fallback has limited deck-level intent metadata."
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function sourceStatus(projection: InspectionPromptProjection, noMatch: boolean): InspectionSourceStatus {
|
|
131
|
+
if (noMatch) return "unknown"
|
|
132
|
+
if (projection.cards.source.missingSourceGaps.length > 0) return "unsupported"
|
|
133
|
+
if (projection.cards.source.weakSourceGaps.length > 0) return "weak"
|
|
134
|
+
if (projection.cards.evidence.traces.some((item) => item.hasDetail)) return "supported"
|
|
135
|
+
return projection.match.claim?.evidenceSensitive ? "unsupported" : "not_needed"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function sourceWarnings(missingGapCount: number, weakGapCount: number, noMatch: boolean): string[] {
|
|
139
|
+
if (noMatch) return ["No slide or claim matched the selected element."]
|
|
140
|
+
const warnings: string[] = []
|
|
141
|
+
if (missingGapCount > 0) warnings.push("Matched evidence-sensitive claim has no slide-level evidence trace.")
|
|
142
|
+
if (weakGapCount > 0) warnings.push("Matched evidence is source-only and lacks quote, location, URL, caveat, findings file, or source path detail.")
|
|
143
|
+
return warnings
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function sourceRationale(projection: InspectionPromptProjection, noMatch: boolean): string {
|
|
147
|
+
if (noMatch) return "No matched claim is available for support assessment."
|
|
148
|
+
if (projection.cards.source.missingSourceGaps.length > 0) return "The selected evidence-sensitive wording is unsupported in the current slide state."
|
|
149
|
+
if (projection.cards.source.weakSourceGaps.length > 0) return "The selected wording has evidence, but the trace is source-only or missing inspection detail."
|
|
150
|
+
if (projection.cards.evidence.traces.some((item) => item.hasDetail)) return "The selected wording has detailed slide-level evidence trace, including source detail where available."
|
|
151
|
+
return "The selection is not clearly evidence-sensitive, so source support is not needed by the deterministic fallback."
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function sourceCaveats(projection: InspectionPromptProjection): string[] {
|
|
155
|
+
return [
|
|
156
|
+
...projection.cards.caveats.caveats,
|
|
157
|
+
...projection.cards.appendix.relatedRisks,
|
|
158
|
+
...projection.cards.appendix.relatedObjections,
|
|
159
|
+
].slice(0, 10)
|
|
160
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { existsSync } from "fs"
|
|
2
|
+
import { ACTIVE_PROMPT_FILE } from "../config"
|
|
3
|
+
import { ctx } from "../ctx"
|
|
4
|
+
import { seedBuiltinDesigns } from "../design/designs"
|
|
5
|
+
import { seedBuiltinDomains } from "../domain/domains"
|
|
6
|
+
import { ensureEditableDeckState } from "../edit/deck-state"
|
|
7
|
+
import { openUrl } from "../edit/open"
|
|
8
|
+
import { resolveEditableDeck, type EditableDeck } from "../edit/resolve-deck"
|
|
9
|
+
import { buildPrompt } from "../prompt-builder"
|
|
10
|
+
import { startRefineServer, type RefineMode } from "./server"
|
|
11
|
+
|
|
12
|
+
export interface OpenRefineDeckResult {
|
|
13
|
+
deck: EditableDeck
|
|
14
|
+
url: string
|
|
15
|
+
source: string
|
|
16
|
+
stateNote: string
|
|
17
|
+
preflightChanged: boolean
|
|
18
|
+
reusedSession: boolean
|
|
19
|
+
liveSession: boolean
|
|
20
|
+
openedBrowser: boolean
|
|
21
|
+
mode: RefineMode
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface OpenRefineDeckOptions {
|
|
25
|
+
client: any
|
|
26
|
+
sessionID: string
|
|
27
|
+
workspaceRoot: string
|
|
28
|
+
mode?: RefineMode
|
|
29
|
+
openBrowser?: boolean
|
|
30
|
+
openUrl?: (url: string) => void
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function openRefineDeck(target: string, options: OpenRefineDeckOptions): OpenRefineDeckResult {
|
|
34
|
+
const deck = resolveEditableDeck(options.workspaceRoot, target)
|
|
35
|
+
const preflight = ensureEditableDeckState(options.workspaceRoot, deck)
|
|
36
|
+
const mode = options.mode ?? "edit"
|
|
37
|
+
|
|
38
|
+
ctx.enabled = true
|
|
39
|
+
if (!existsSync(ACTIVE_PROMPT_FILE)) {
|
|
40
|
+
seedBuiltinDesigns()
|
|
41
|
+
seedBuiltinDomains()
|
|
42
|
+
buildPrompt()
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const refineServer = startRefineServer()
|
|
46
|
+
const session = refineServer.getOrCreateSession({
|
|
47
|
+
client: options.client,
|
|
48
|
+
sessionID: options.sessionID,
|
|
49
|
+
workspaceRoot: options.workspaceRoot,
|
|
50
|
+
deck,
|
|
51
|
+
mode,
|
|
52
|
+
})
|
|
53
|
+
const url = `${refineServer.baseUrl}/refine?token=${encodeURIComponent(session.token)}`
|
|
54
|
+
const shouldOpen = options.openBrowser !== false
|
|
55
|
+
if (shouldOpen) (options.openUrl ?? openUrl)(url)
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
deck,
|
|
59
|
+
url,
|
|
60
|
+
source: deck.source === "decks-state" ? "DECKS.json" : deck.source === "file-path" ? "file path" : "fallback path",
|
|
61
|
+
stateNote: preflight.changed ? "Deck state was prepared in DECKS.json for refinement." : "Deck state already points to this refinement target.",
|
|
62
|
+
preflightChanged: preflight.changed,
|
|
63
|
+
reusedSession: session.reused,
|
|
64
|
+
liveSession: session.live,
|
|
65
|
+
openedBrowser: shouldOpen,
|
|
66
|
+
mode,
|
|
67
|
+
}
|
|
68
|
+
}
|