heyiam 0.1.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/app/dist/assets/index-BGQkmy7q.css +1 -0
- package/app/dist/assets/index-CbLHxerW.js +14 -0
- package/app/dist/favicon.svg +1 -0
- package/app/dist/icons.svg +24 -0
- package/app/dist/index.html +20 -0
- package/dist/analyzer.d.ts +96 -0
- package/dist/analyzer.js +249 -0
- package/dist/analyzer.js.map +1 -0
- package/dist/auth.d.ts +34 -0
- package/dist/auth.js +99 -0
- package/dist/auth.js.map +1 -0
- package/dist/bridge.d.ts +32 -0
- package/dist/bridge.js +211 -0
- package/dist/bridge.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.js +5 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +129 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic-provider.d.ts +11 -0
- package/dist/llm/anthropic-provider.js +16 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/index.d.ts +16 -0
- package/dist/llm/index.js +25 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/project-enhance.d.ts +71 -0
- package/dist/llm/project-enhance.js +236 -0
- package/dist/llm/project-enhance.js.map +1 -0
- package/dist/llm/proxy-provider.d.ts +16 -0
- package/dist/llm/proxy-provider.js +60 -0
- package/dist/llm/proxy-provider.js.map +1 -0
- package/dist/llm/triage.d.ts +66 -0
- package/dist/llm/triage.js +283 -0
- package/dist/llm/triage.js.map +1 -0
- package/dist/llm/types.d.ts +19 -0
- package/dist/llm/types.js +2 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/machine-key.d.ts +10 -0
- package/dist/machine-key.js +51 -0
- package/dist/machine-key.js.map +1 -0
- package/dist/parsers/claude.d.ts +18 -0
- package/dist/parsers/claude.js +265 -0
- package/dist/parsers/claude.js.map +1 -0
- package/dist/parsers/index.d.ts +28 -0
- package/dist/parsers/index.js +124 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/types.d.ts +81 -0
- package/dist/parsers/types.js +2 -0
- package/dist/parsers/types.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +857 -0
- package/dist/server.js.map +1 -0
- package/dist/settings.d.ts +92 -0
- package/dist/settings.js +160 -0
- package/dist/settings.js.map +1 -0
- package/dist/summarize.d.ts +72 -0
- package/dist/summarize.js +312 -0
- package/dist/summarize.js.map +1 -0
- package/package.json +47 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import { getAnthropicApiKey } from '../settings.js';
|
|
3
|
+
// ── Prompts ──────────────────────────────────────────────────────
|
|
4
|
+
const PROJECT_ENHANCE_SYSTEM = `You are building a project narrative from multiple coding sessions for a developer portfolio on heyi.am.
|
|
5
|
+
|
|
6
|
+
Your job:
|
|
7
|
+
1. Synthesize a 2-3 sentence project description that captures what was built and why it matters. Write in third person about the project, not the developer. No fluff — every word earns its place.
|
|
8
|
+
2. Identify 4-7 project phases (the "arc") that show how the project evolved. Each phase should have a short title and one-sentence description.
|
|
9
|
+
3. Deduplicate and rank skills across all sessions.
|
|
10
|
+
4. Group sessions into timeline periods (e.g., "Week 1", "Days 1-3") with labels describing what happened in each period. Mark featured sessions (the most interesting ones) vs background sessions.
|
|
11
|
+
5. Generate 2-3 context-aware questions based on patterns you detect in the sessions (see instructions below).
|
|
12
|
+
|
|
13
|
+
For questions, look for these signals and generate questions that reference specific data:
|
|
14
|
+
- High correction counts → ask about override strategy, referencing the count
|
|
15
|
+
- Longest sessions → ask why that area was worth the time investment, referencing duration
|
|
16
|
+
- Zero file overlap between sessions → ask about isolation decisions
|
|
17
|
+
- Technology switches or unusual tool usage → ask about the choice
|
|
18
|
+
- Architectural keywords → ask about design trade-offs
|
|
19
|
+
|
|
20
|
+
Each question must have:
|
|
21
|
+
- A category: "pattern" (behavioral signals), "architecture" (design decisions), or "evolution" (how the project changed)
|
|
22
|
+
- A specific, non-generic question that references actual data from the sessions
|
|
23
|
+
- Context explaining why you're asking (what signal triggered it)
|
|
24
|
+
|
|
25
|
+
Return valid JSON matching this exact structure:
|
|
26
|
+
{
|
|
27
|
+
"narrative": "2-3 sentence project description",
|
|
28
|
+
"arc": [{ "phase": 1, "title": "...", "description": "..." }],
|
|
29
|
+
"skills": ["skill1", "skill2"],
|
|
30
|
+
"timeline": [{
|
|
31
|
+
"period": "Week 1",
|
|
32
|
+
"label": "Foundation and setup",
|
|
33
|
+
"sessions": [{
|
|
34
|
+
"sessionId": "uuid",
|
|
35
|
+
"title": "Session title",
|
|
36
|
+
"featured": true,
|
|
37
|
+
"tag": "key decision"
|
|
38
|
+
}]
|
|
39
|
+
}],
|
|
40
|
+
"questions": [{
|
|
41
|
+
"id": "q1",
|
|
42
|
+
"category": "pattern",
|
|
43
|
+
"question": "You overrode the AI 4 times across sessions. Was that a conscious strategy?",
|
|
44
|
+
"context": "High correction count detected across auth and sealing sessions"
|
|
45
|
+
}]
|
|
46
|
+
}`;
|
|
47
|
+
const REFINE_NARRATIVE_SYSTEM = `You are refining a project narrative by incorporating the developer's own perspective. You have the draft narrative and timeline, plus the developer's answers to context-aware questions.
|
|
48
|
+
|
|
49
|
+
Weave their answers naturally into the existing narrative — don't quote them verbatim, make it sound like the developer wrote it. The narrative should feel like a developer thinking out loud, not an AI explaining.
|
|
50
|
+
|
|
51
|
+
Rules:
|
|
52
|
+
- Keep the narrative to 2-4 sentences. Quality over quantity.
|
|
53
|
+
- If an answer reveals motivation, fold it into the narrative.
|
|
54
|
+
- If an answer explains a decision, strengthen the relevant arc phase or timeline label.
|
|
55
|
+
- Don't add fluff words (leverage, utilize, robust, comprehensive, cutting-edge).
|
|
56
|
+
- Return the same JSON structure with updated narrative and timeline.
|
|
57
|
+
|
|
58
|
+
Return valid JSON:
|
|
59
|
+
{
|
|
60
|
+
"narrative": "refined 2-4 sentence description",
|
|
61
|
+
"timeline": [same structure as input, with updated labels where answers add context]
|
|
62
|
+
}`;
|
|
63
|
+
// ── Core functions ───────────────────────────────────────────────
|
|
64
|
+
function createClient() {
|
|
65
|
+
const apiKey = getAnthropicApiKey();
|
|
66
|
+
return apiKey ? new Anthropic({ apiKey }) : new Anthropic();
|
|
67
|
+
}
|
|
68
|
+
function extractJson(text) {
|
|
69
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
70
|
+
if (!match)
|
|
71
|
+
throw new Error('No JSON object found in LLM response');
|
|
72
|
+
return JSON.parse(match[0]);
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Generate a project narrative, arc, timeline, and context-aware questions
|
|
76
|
+
* from enhanced session summaries. Streams narrative chunks via onProgress
|
|
77
|
+
* as the LLM generates the response.
|
|
78
|
+
*/
|
|
79
|
+
export async function enhanceProject(sessions, skippedSessions, onProgress) {
|
|
80
|
+
const client = createClient();
|
|
81
|
+
const input = {
|
|
82
|
+
sessions: sessions.map((s) => ({
|
|
83
|
+
sessionId: s.sessionId,
|
|
84
|
+
title: s.title,
|
|
85
|
+
developerTake: s.developerTake,
|
|
86
|
+
skills: s.skills,
|
|
87
|
+
executionSteps: s.executionSteps.map((e) => e.title),
|
|
88
|
+
duration: s.duration,
|
|
89
|
+
loc: s.loc,
|
|
90
|
+
turns: s.turns,
|
|
91
|
+
files: s.files,
|
|
92
|
+
date: s.date,
|
|
93
|
+
correctionCount: s.correctionCount,
|
|
94
|
+
})),
|
|
95
|
+
skippedSessions,
|
|
96
|
+
totalSessions: sessions.length + skippedSessions.length,
|
|
97
|
+
};
|
|
98
|
+
let fullText = '';
|
|
99
|
+
// State machine for detecting and streaming the "narrative" JSON string value
|
|
100
|
+
let streamPhase = 'scanning';
|
|
101
|
+
let scanBuffer = '';
|
|
102
|
+
let escaped = false; // tracks whether the previous char was an unresolved backslash
|
|
103
|
+
const stream = client.messages.stream({
|
|
104
|
+
model: 'claude-sonnet-4-6',
|
|
105
|
+
max_tokens: 4096,
|
|
106
|
+
system: PROJECT_ENHANCE_SYSTEM,
|
|
107
|
+
messages: [{
|
|
108
|
+
role: 'user',
|
|
109
|
+
content: JSON.stringify(input),
|
|
110
|
+
}],
|
|
111
|
+
});
|
|
112
|
+
for await (const event of stream) {
|
|
113
|
+
if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') {
|
|
114
|
+
const chunk = event.delta.text;
|
|
115
|
+
fullText += chunk;
|
|
116
|
+
if (!onProgress || streamPhase === 'past_narrative')
|
|
117
|
+
continue;
|
|
118
|
+
for (const char of chunk) {
|
|
119
|
+
if (streamPhase === 'scanning') {
|
|
120
|
+
scanBuffer += char;
|
|
121
|
+
// Detect "narrative":<whitespace>" — the opening of the value string
|
|
122
|
+
const marker = '"narrative"';
|
|
123
|
+
const markerIdx = scanBuffer.indexOf(marker);
|
|
124
|
+
if (markerIdx >= 0) {
|
|
125
|
+
const afterMarker = scanBuffer.slice(markerIdx + marker.length);
|
|
126
|
+
// Match : then optional whitespace then opening "
|
|
127
|
+
const valueStart = afterMarker.match(/^\s*:\s*"/);
|
|
128
|
+
if (valueStart) {
|
|
129
|
+
streamPhase = 'in_narrative';
|
|
130
|
+
// Anything after the opening quote is narrative content
|
|
131
|
+
const remainder = afterMarker.slice(valueStart[0].length);
|
|
132
|
+
if (remainder.length > 0) {
|
|
133
|
+
for (const rc of remainder) {
|
|
134
|
+
// Process remainder through the same escape logic below
|
|
135
|
+
if (escaped) {
|
|
136
|
+
escaped = false;
|
|
137
|
+
const decoded = rc === 'n' ? '\n' : rc === 't' ? '\t' : rc;
|
|
138
|
+
onProgress({ type: 'narrative_chunk', text: decoded });
|
|
139
|
+
}
|
|
140
|
+
else if (rc === '\\') {
|
|
141
|
+
escaped = true;
|
|
142
|
+
}
|
|
143
|
+
else if (rc === '"') {
|
|
144
|
+
streamPhase = 'past_narrative';
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
onProgress({ type: 'narrative_chunk', text: rc });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
scanBuffer = '';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Prevent unbounded buffer growth
|
|
156
|
+
if (scanBuffer.length > 500) {
|
|
157
|
+
scanBuffer = scanBuffer.slice(-200);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (streamPhase === 'in_narrative') {
|
|
161
|
+
if (escaped) {
|
|
162
|
+
escaped = false;
|
|
163
|
+
const decoded = char === 'n' ? '\n' : char === 't' ? '\t' : char;
|
|
164
|
+
onProgress({ type: 'narrative_chunk', text: decoded });
|
|
165
|
+
}
|
|
166
|
+
else if (char === '\\') {
|
|
167
|
+
escaped = true;
|
|
168
|
+
}
|
|
169
|
+
else if (char === '"') {
|
|
170
|
+
// Unescaped quote — end of narrative string
|
|
171
|
+
streamPhase = 'past_narrative';
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
onProgress({ type: 'narrative_chunk', text: char });
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const result = extractJson(fullText);
|
|
182
|
+
// Validate required fields
|
|
183
|
+
if (!result.narrative || !Array.isArray(result.arc) || !Array.isArray(result.skills)) {
|
|
184
|
+
throw new Error('LLM returned incomplete project enhance result');
|
|
185
|
+
}
|
|
186
|
+
// Ensure questions have IDs
|
|
187
|
+
if (result.questions) {
|
|
188
|
+
result.questions = result.questions.map((q, i) => ({
|
|
189
|
+
...q,
|
|
190
|
+
id: q.id || `q${i + 1}`,
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
result.questions = [];
|
|
195
|
+
}
|
|
196
|
+
return result;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Refine a project narrative by weaving in the developer's answers
|
|
200
|
+
* to context-aware questions.
|
|
201
|
+
*/
|
|
202
|
+
export async function refineNarrative(draftNarrative, draftTimeline, answers) {
|
|
203
|
+
const client = createClient();
|
|
204
|
+
const input = {
|
|
205
|
+
draftNarrative,
|
|
206
|
+
draftTimeline,
|
|
207
|
+
answers: answers.filter((a) => a.answer.trim().length > 0),
|
|
208
|
+
};
|
|
209
|
+
// If no answers provided, return draft as-is
|
|
210
|
+
if (input.answers.length === 0) {
|
|
211
|
+
return { narrative: draftNarrative, timeline: draftTimeline };
|
|
212
|
+
}
|
|
213
|
+
const response = await client.messages.create({
|
|
214
|
+
model: 'claude-sonnet-4-6',
|
|
215
|
+
max_tokens: 4096,
|
|
216
|
+
system: REFINE_NARRATIVE_SYSTEM,
|
|
217
|
+
messages: [{
|
|
218
|
+
role: 'user',
|
|
219
|
+
content: JSON.stringify(input),
|
|
220
|
+
}],
|
|
221
|
+
});
|
|
222
|
+
const text = response.content
|
|
223
|
+
.filter((b) => b.type === 'text')
|
|
224
|
+
.map((b) => b.text)
|
|
225
|
+
.join('');
|
|
226
|
+
const result = extractJson(text);
|
|
227
|
+
if (!result.narrative) {
|
|
228
|
+
throw new Error('LLM returned incomplete refined narrative');
|
|
229
|
+
}
|
|
230
|
+
// Fall back to draft timeline if LLM didn't return one
|
|
231
|
+
if (!Array.isArray(result.timeline)) {
|
|
232
|
+
result.timeline = draftTimeline;
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=project-enhance.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project-enhance.js","sourceRoot":"","sources":["../../src/llm/project-enhance.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Session } from '../analyzer.js';
|
|
2
|
+
import type { EnhancementResult } from '../summarize.js';
|
|
3
|
+
import type { LLMProvider } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Proxy provider — sends session data to Phoenix backend for server-side enhancement.
|
|
6
|
+
* Used when user has no local ANTHROPIC_API_KEY but is authenticated.
|
|
7
|
+
*/
|
|
8
|
+
export declare class ProxyProvider implements LLMProvider {
|
|
9
|
+
name: "proxy";
|
|
10
|
+
enhance(session: Session): Promise<EnhancementResult>;
|
|
11
|
+
}
|
|
12
|
+
export declare class ProxyError extends Error {
|
|
13
|
+
code: string;
|
|
14
|
+
resetsAt?: string;
|
|
15
|
+
constructor(code: string, message: string, resetsAt?: string);
|
|
16
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { getAuthToken } from '../auth.js';
|
|
2
|
+
import { API_URL } from '../config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Proxy provider — sends session data to Phoenix backend for server-side enhancement.
|
|
5
|
+
* Used when user has no local ANTHROPIC_API_KEY but is authenticated.
|
|
6
|
+
*/
|
|
7
|
+
export class ProxyProvider {
|
|
8
|
+
name = 'proxy';
|
|
9
|
+
async enhance(session) {
|
|
10
|
+
const auth = getAuthToken();
|
|
11
|
+
if (!auth?.token) {
|
|
12
|
+
throw new ProxyError('AUTH_EXPIRED', 'Not authenticated. Run: heyiam login');
|
|
13
|
+
}
|
|
14
|
+
const response = await fetch(`${API_URL}/api/enhance`, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: {
|
|
17
|
+
'Content-Type': 'application/json',
|
|
18
|
+
'Authorization': `Bearer ${auth.token}`,
|
|
19
|
+
},
|
|
20
|
+
body: JSON.stringify({ session: sessionToPayload(session) }),
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
const errorBody = await response.json().catch(() => ({
|
|
24
|
+
error: { code: 'PROXY_UNREACHABLE', message: 'Could not reach enhancement server' },
|
|
25
|
+
}));
|
|
26
|
+
throw new ProxyError(errorBody.error?.code ?? 'PROXY_ERROR', errorBody.error?.message ?? 'Enhancement failed', errorBody.error?.resets_at);
|
|
27
|
+
}
|
|
28
|
+
const data = await response.json();
|
|
29
|
+
return data.result;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class ProxyError extends Error {
|
|
33
|
+
code;
|
|
34
|
+
resetsAt;
|
|
35
|
+
constructor(code, message, resetsAt) {
|
|
36
|
+
super(message);
|
|
37
|
+
this.name = 'ProxyError';
|
|
38
|
+
this.code = code;
|
|
39
|
+
this.resetsAt = resetsAt;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Extracts the session fields that Phoenix expects for enhancement.
|
|
44
|
+
*/
|
|
45
|
+
function sessionToPayload(session) {
|
|
46
|
+
return {
|
|
47
|
+
title: session.title,
|
|
48
|
+
projectName: session.projectName,
|
|
49
|
+
durationMinutes: session.durationMinutes,
|
|
50
|
+
turns: session.turns,
|
|
51
|
+
linesOfCode: session.linesOfCode,
|
|
52
|
+
skills: session.skills,
|
|
53
|
+
toolBreakdown: session.toolBreakdown,
|
|
54
|
+
filesChanged: session.filesChanged,
|
|
55
|
+
executionPath: session.executionPath,
|
|
56
|
+
turnTimeline: session.turnTimeline,
|
|
57
|
+
rawLog: session.rawLog,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=proxy-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-provider.js","sourceRoot":"","sources":["../../src/llm/proxy-provider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC;;;GAGG;AACH,MAAM,OAAO,aAAa;IACxB,IAAI,GAAG,OAAgB,CAAC;IAExB,KAAK,CAAC,OAAO,CAAC,OAAgB;QAC5B,MAAM,IAAI,GAAG,YAAY,EAAE,CAAC;QAC5B,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;YACjB,MAAM,IAAI,UAAU,CAAC,cAAc,EAAE,sCAAsC,CAAC,CAAC;QAC/E,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;YACrD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,IAAI,CAAC,KAAK,EAAE;aACxC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;SAC7D,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC;gBACnD,KAAK,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE,OAAO,EAAE,oCAAoC,EAAE;aACpF,CAAC,CAAuB,CAAC;YAE1B,MAAM,IAAI,UAAU,CAClB,SAAS,CAAC,KAAK,EAAE,IAAI,IAAI,aAAa,EACtC,SAAS,CAAC,KAAK,EAAE,OAAO,IAAI,oBAAoB,EAChD,SAAS,CAAC,KAAK,EAAE,SAAS,CAC3B,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAA0B,CAAC;QAC3D,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,IAAI,CAAS;IACb,QAAQ,CAAU;IAElB,YAAY,IAAY,EAAE,OAAe,EAAE,QAAiB;QAC1D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,OAAgB;IACxC,OAAO;QACL,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,MAAM,EAAE,OAAO,CAAC,MAAM;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
export interface SessionSignals {
|
|
2
|
+
correctionCount: number;
|
|
3
|
+
avgUserExplanationLength: number;
|
|
4
|
+
errorRetryCount: number;
|
|
5
|
+
userToAiRatio: number;
|
|
6
|
+
toolDiversity: number;
|
|
7
|
+
multiDirScope: number;
|
|
8
|
+
architecturalKeywords: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Extract cheap signals from a session's raw JSONL file.
|
|
12
|
+
* Scans user messages for patterns without full parsing.
|
|
13
|
+
*/
|
|
14
|
+
export declare function extractSignals(sessionPath: string): Promise<SessionSignals>;
|
|
15
|
+
export interface SessionMetaWithStats {
|
|
16
|
+
sessionId: string;
|
|
17
|
+
path: string;
|
|
18
|
+
title: string;
|
|
19
|
+
duration: number;
|
|
20
|
+
loc: number;
|
|
21
|
+
turns: number;
|
|
22
|
+
files: number;
|
|
23
|
+
skills: string[];
|
|
24
|
+
date: string;
|
|
25
|
+
}
|
|
26
|
+
export interface TriageResult {
|
|
27
|
+
selected: Array<{
|
|
28
|
+
sessionId: string;
|
|
29
|
+
reason: string;
|
|
30
|
+
}>;
|
|
31
|
+
skipped: Array<{
|
|
32
|
+
sessionId: string;
|
|
33
|
+
reason: string;
|
|
34
|
+
}>;
|
|
35
|
+
triageMethod: 'llm' | 'scoring' | 'auto-select';
|
|
36
|
+
autoSelected?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export type TriageProgressEvent = {
|
|
39
|
+
type: 'scanning';
|
|
40
|
+
total: number;
|
|
41
|
+
} | {
|
|
42
|
+
type: 'hard_floor';
|
|
43
|
+
sessionId: string;
|
|
44
|
+
title: string;
|
|
45
|
+
passed: boolean;
|
|
46
|
+
reason?: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'extracting_signals';
|
|
49
|
+
sessionId: string;
|
|
50
|
+
title: string;
|
|
51
|
+
} | {
|
|
52
|
+
type: 'signals_done';
|
|
53
|
+
sessionId: string;
|
|
54
|
+
signals: SessionSignals;
|
|
55
|
+
} | {
|
|
56
|
+
type: 'llm_ranking';
|
|
57
|
+
sessionCount: number;
|
|
58
|
+
} | {
|
|
59
|
+
type: 'scoring_fallback';
|
|
60
|
+
sessionCount: number;
|
|
61
|
+
} | {
|
|
62
|
+
type: 'done';
|
|
63
|
+
selected: number;
|
|
64
|
+
skipped: number;
|
|
65
|
+
};
|
|
66
|
+
export declare function triageSessions(sessions: SessionMetaWithStats[], useLLM?: boolean, onProgress?: (event: TriageProgressEvent) => void): Promise<TriageResult>;
|