osborn 0.9.4 → 0.9.9
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/.claude/settings.local.json +9 -0
- package/.claude/skills/browser-apply/SKILL.md +114 -0
- package/.claude/skills/markdown-to-pdf/SKILL.md +29 -0
- package/.claude/skills/pdf-to-markdown/SKILL.md +28 -0
- package/.claude/skills/playwright-browser/SKILL.md +90 -0
- package/.claude/skills/shadcn/SKILL.md +232 -0
- package/.claude/skills/shadcn/image.png +0 -0
- package/.claude/skills/youtube-transcript/SKILL.md +24 -0
- package/caresource-apply.js +50 -0
- package/caresource-apply.mjs +34 -0
- package/dist/claude-llm.js +219 -37
- package/dist/conversation-brain.d.ts +92 -0
- package/dist/conversation-brain.js +360 -0
- package/dist/fast-llm.d.ts +15 -0
- package/dist/fast-llm.js +81 -0
- package/dist/index.js +288 -57
- package/dist/pipeline-direct-llm.js +1 -1
- package/dist/prompts/compact-learnings-instruction.md +53 -10
- package/package.json +1 -1
package/dist/claude-llm.js
CHANGED
|
@@ -547,6 +547,8 @@ export class ClaudeLLM extends llm.LLM {
|
|
|
547
547
|
* @param callbacks - Event callbacks for the background consumer
|
|
548
548
|
*/
|
|
549
549
|
pushMessage(userText, sdkOptions, callbacks) {
|
|
550
|
+
// Lower compaction threshold to 65% so PreCompact fires earlier and context is preserved
|
|
551
|
+
process.env.CLAUDE_AUTOCOMPACT_PCT_OVERRIDE = '60';
|
|
550
552
|
const userMessage = {
|
|
551
553
|
type: 'user',
|
|
552
554
|
message: { role: 'user', content: [{ type: 'text', text: userText }] },
|
|
@@ -912,30 +914,136 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
912
914
|
}
|
|
913
915
|
}]
|
|
914
916
|
}],
|
|
915
|
-
// ── PreCompact:
|
|
916
|
-
//
|
|
917
|
-
//
|
|
918
|
-
//
|
|
919
|
-
// existing learned skills so Claude can merge/update rather than start fresh.
|
|
917
|
+
// ── PreCompact: read full transcript, call Anthropic API directly to extract skills ──
|
|
918
|
+
// Fires before the SDK compresses the conversation. Reads the FULL transcript JSONL,
|
|
919
|
+
// calls claude-haiku-4-5-20251001 directly for structured extraction, and writes
|
|
920
|
+
// skill files to disk immediately — no relying on PostCompact for persistence.
|
|
920
921
|
PreCompact: [{
|
|
921
922
|
matcher: '.*',
|
|
922
923
|
hooks: [async (input) => {
|
|
923
924
|
try {
|
|
925
|
+
const { readFileSync, existsSync, writeFileSync, mkdirSync, readdirSync } = await import('node:fs');
|
|
926
|
+
const { join } = await import('node:path');
|
|
927
|
+
// 1. Read the instruction file
|
|
924
928
|
const instructionPath = join(__claudeLlmDir, 'prompts', 'compact-learnings-instruction.md');
|
|
925
|
-
const instruction = readFileSync(instructionPath, 'utf-8');
|
|
926
|
-
//
|
|
929
|
+
const instruction = existsSync(instructionPath) ? readFileSync(instructionPath, 'utf-8') : '';
|
|
930
|
+
// 2. Read FULL transcript (no line limit)
|
|
931
|
+
const transcriptPath = input?.transcript_path;
|
|
932
|
+
let transcriptContent = '';
|
|
933
|
+
if (transcriptPath && existsSync(transcriptPath)) {
|
|
934
|
+
try {
|
|
935
|
+
transcriptContent = readFileSync(transcriptPath, 'utf-8');
|
|
936
|
+
}
|
|
937
|
+
catch (readErr) {
|
|
938
|
+
console.warn('⚠️ PreCompact: could not read transcript:', readErr instanceof Error ? readErr.message : readErr);
|
|
939
|
+
}
|
|
940
|
+
}
|
|
927
941
|
const skillDir = this.#opts.sessionBaseDir || this.#opts.workingDirectory || process.cwd();
|
|
928
|
-
const
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
942
|
+
const skillsRoot = join(skillDir, '.claude', 'skills');
|
|
943
|
+
// 3. Load existing skills for context (cap each at 500 chars)
|
|
944
|
+
let existingSkillsBlock = '';
|
|
945
|
+
if (existsSync(skillsRoot)) {
|
|
946
|
+
try {
|
|
947
|
+
const names = readdirSync(skillsRoot).slice(0, 15);
|
|
948
|
+
const fragments = [];
|
|
949
|
+
for (const name of names) {
|
|
950
|
+
const p = join(skillsRoot, name, 'SKILL.md');
|
|
951
|
+
if (existsSync(p)) {
|
|
952
|
+
const body = readFileSync(p, 'utf-8');
|
|
953
|
+
const cleaned = body.replace(/^===.*===\s*$/gm, '').substring(0, 500);
|
|
954
|
+
fragments.push(`### ${name}\n${cleaned}`);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (fragments.length) {
|
|
958
|
+
existingSkillsBlock = `EXISTING SKILLS (do NOT re-emit unchanged):\n${fragments.join('\n\n')}`;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
catch (skillErr) {
|
|
962
|
+
console.warn('⚠️ PreCompact: skills load failed:', skillErr instanceof Error ? skillErr.message : skillErr);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
// 4. If transcript available, use direct Anthropic API call to extract everything
|
|
966
|
+
if (transcriptContent && process.env.ANTHROPIC_API_KEY) {
|
|
967
|
+
try {
|
|
968
|
+
const Anthropic = (await import('@anthropic-ai/sdk')).default;
|
|
969
|
+
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });
|
|
970
|
+
const extractionPrompt = [
|
|
971
|
+
instruction,
|
|
972
|
+
existingSkillsBlock,
|
|
973
|
+
`FULL SESSION TRANSCRIPT (JSONL format — extract the four sections from this):`,
|
|
974
|
+
transcriptContent,
|
|
975
|
+
].filter(Boolean).join('\n\n---\n\n');
|
|
976
|
+
console.log(`🧠 PreCompact: calling extraction API (${extractionPrompt.length} chars, trigger=${input?.trigger || 'unknown'})`);
|
|
977
|
+
const response = await client.messages.create({
|
|
978
|
+
model: 'claude-haiku-4-5-20251001',
|
|
979
|
+
max_tokens: 4096,
|
|
980
|
+
messages: [{ role: 'user', content: extractionPrompt }],
|
|
981
|
+
});
|
|
982
|
+
const extractedText = response.content[0]?.type === 'text' ? response.content[0].text : '';
|
|
983
|
+
if (extractedText) {
|
|
984
|
+
// Write SKILL_CANDIDATES to disk directly
|
|
985
|
+
const skillRegex = /--- SKILL: ([a-z][a-z0-9-]{1,39}) ---\n([\s\S]*?)\n--- END SKILL ---/g;
|
|
986
|
+
let match;
|
|
987
|
+
while ((match = skillRegex.exec(extractedText)) !== null) {
|
|
988
|
+
const [, name, body] = match;
|
|
989
|
+
if (/^[a-z][a-z0-9-]{1,39}$/.test(name)) {
|
|
990
|
+
const today = new Date().toISOString().split('T')[0];
|
|
991
|
+
const sessionId = this.#sessionId || 'unknown';
|
|
992
|
+
const skillFolder = join(skillsRoot, name);
|
|
993
|
+
mkdirSync(skillFolder, { recursive: true });
|
|
994
|
+
writeFileSync(join(skillFolder, 'SKILL.md'), `# ${name}\nAuto-extracted: ${today} | Session: ${sessionId.substring(0, 8)}\n\n${body}`);
|
|
995
|
+
console.log(`🧠 PreCompact: wrote skill '${name}' to ${skillFolder}`);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
// Write BEHAVIORAL_LEARNINGS to learned-behaviors skill
|
|
999
|
+
const blMarker = '=== BEHAVIORAL_LEARNINGS ===';
|
|
1000
|
+
const blIdx = extractedText.indexOf(blMarker);
|
|
1001
|
+
if (blIdx !== -1) {
|
|
1002
|
+
const blContent = extractedText.substring(blIdx);
|
|
1003
|
+
if (blContent.length > 30) {
|
|
1004
|
+
const today = new Date().toISOString().split('T')[0];
|
|
1005
|
+
const sessionId = this.#sessionId || 'unknown';
|
|
1006
|
+
const learnedDir = join(skillsRoot, 'learned-behaviors');
|
|
1007
|
+
mkdirSync(learnedDir, { recursive: true });
|
|
1008
|
+
const header = `# Learned Behaviors\n\nAuto-extracted from voice sessions via PreCompact.\nLast updated: ${today} | Session: ${sessionId.substring(0, 8)}...\n\n`;
|
|
1009
|
+
writeFileSync(join(learnedDir, 'SKILL.md'), header + blContent);
|
|
1010
|
+
console.log(`🧠 PreCompact: wrote learned-behaviors (${blContent.length} chars)`);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
// Write project-scoped DECISIONS
|
|
1014
|
+
const decMarker = '=== DECISIONS ===';
|
|
1015
|
+
const decIdx = extractedText.indexOf(decMarker);
|
|
1016
|
+
if (decIdx !== -1) {
|
|
1017
|
+
const decEnd = extractedText.indexOf('===', decIdx + decMarker.length);
|
|
1018
|
+
const decContent = extractedText.substring(decIdx + decMarker.length, decEnd !== -1 ? decEnd : undefined);
|
|
1019
|
+
const projectDecisions = decContent.split('\n').filter((l) => l.includes('SCOPE: project')).join('\n');
|
|
1020
|
+
if (projectDecisions.trim()) {
|
|
1021
|
+
const decDir = join(skillsRoot, 'project-decisions');
|
|
1022
|
+
mkdirSync(decDir, { recursive: true });
|
|
1023
|
+
const decFile = join(decDir, 'SKILL.md');
|
|
1024
|
+
const existing = existsSync(decFile) ? readFileSync(decFile, 'utf-8') : '# Project Decisions\n\n';
|
|
1025
|
+
writeFileSync(decFile, existing + '\n' + projectDecisions);
|
|
1026
|
+
console.log(`🧠 PreCompact: appended project decisions`);
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
// Return extracted content as systemMessage for compact summary
|
|
1030
|
+
const systemMessage = [
|
|
1031
|
+
'## Pre-Compaction Extraction Complete',
|
|
1032
|
+
'Skill files have been written to disk. Include the following in your compact summary verbatim:',
|
|
1033
|
+
extractedText.substring(0, 8000),
|
|
1034
|
+
].join('\n\n').substring(0, 9500);
|
|
1035
|
+
console.log(`🧠 PreCompact: extraction complete, returning systemMessage (${systemMessage.length} chars)`);
|
|
1036
|
+
return { systemMessage };
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
catch (apiErr) {
|
|
1040
|
+
console.error('⚠️ PreCompact: API extraction failed, falling back to basic mode:', apiErr instanceof Error ? apiErr.message : apiErr);
|
|
1041
|
+
// Fall through to basic fallback
|
|
1042
|
+
}
|
|
932
1043
|
}
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
: instruction;
|
|
937
|
-
console.log(`🧠 PreCompact: injected learnings instruction (${fullInstruction.length} chars, trigger=${input?.trigger || 'unknown'})`);
|
|
938
|
-
return { systemMessage: fullInstruction };
|
|
1044
|
+
// 5. Fallback: basic mode — just inject the instruction
|
|
1045
|
+
console.log(`🧠 PreCompact: basic mode (no transcript or API key unavailable)`);
|
|
1046
|
+
return { systemMessage: instruction.substring(0, 9500) };
|
|
939
1047
|
}
|
|
940
1048
|
catch (err) {
|
|
941
1049
|
console.error('⚠️ PreCompact hook error:', err instanceof Error ? err.message : err);
|
|
@@ -943,34 +1051,108 @@ class ClaudeLLMStream extends llm.LLMStream {
|
|
|
943
1051
|
}
|
|
944
1052
|
}]
|
|
945
1053
|
}],
|
|
946
|
-
// ── PostCompact: extract
|
|
1054
|
+
// ── PostCompact: extract all four sections from summary and persist to disk ──
|
|
947
1055
|
PostCompact: [{
|
|
948
1056
|
matcher: '.*',
|
|
949
1057
|
hooks: [async (input) => {
|
|
950
1058
|
try {
|
|
951
1059
|
const summary = input?.compact_summary || '';
|
|
952
|
-
const marker = '=== BEHAVIORAL_LEARNINGS ===';
|
|
953
|
-
const idx = summary.indexOf(marker);
|
|
954
|
-
if (idx === -1) {
|
|
955
|
-
console.log('🧠 PostCompact: no BEHAVIORAL_LEARNINGS section found in summary — skipping');
|
|
956
|
-
return {};
|
|
957
|
-
}
|
|
958
|
-
const learnings = summary.substring(idx + marker.length).trim();
|
|
959
|
-
if (learnings.length < 30) {
|
|
960
|
-
console.log('🧠 PostCompact: BEHAVIORAL_LEARNINGS section too short — skipping');
|
|
961
|
-
return {};
|
|
962
|
-
}
|
|
963
|
-
// Write the skill file
|
|
964
|
-
const skillDir = this.#opts.sessionBaseDir || this.#opts.workingDirectory || process.cwd();
|
|
965
|
-
const skillFolder = join(skillDir, '.claude', 'skills', 'learned-behaviors');
|
|
966
|
-
const skillPath = join(skillFolder, 'SKILL.md');
|
|
967
1060
|
const { mkdirSync, writeFileSync: writeSyncFs } = await import('fs');
|
|
968
|
-
|
|
1061
|
+
const skillDir = this.#opts.sessionBaseDir || this.#opts.workingDirectory || process.cwd();
|
|
969
1062
|
const today = new Date().toISOString().split('T')[0];
|
|
970
1063
|
const sessionId = this.#sessionId || 'unknown';
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
1064
|
+
// Helper: extract text between a marker and the next === marker (or end of string)
|
|
1065
|
+
const extractSection = (marker) => {
|
|
1066
|
+
const idx = summary.indexOf(marker);
|
|
1067
|
+
if (idx === -1)
|
|
1068
|
+
return '';
|
|
1069
|
+
const start = idx + marker.length;
|
|
1070
|
+
const nextMarker = summary.indexOf('=== ', start);
|
|
1071
|
+
return (nextMarker === -1 ? summary.substring(start) : summary.substring(start, nextMarker)).trim();
|
|
1072
|
+
};
|
|
1073
|
+
// ── Section 1: HANDOFF_STATE — log only, not written to disk ──
|
|
1074
|
+
const handoff = extractSection('=== HANDOFF_STATE ===');
|
|
1075
|
+
if (handoff) {
|
|
1076
|
+
console.log(`🧠 PostCompact: HANDOFF_STATE present (${handoff.length} chars) — not written to disk`);
|
|
1077
|
+
}
|
|
1078
|
+
// ── Section 2: DECISIONS — append project-scoped decisions ──
|
|
1079
|
+
try {
|
|
1080
|
+
const decisions = extractSection('=== DECISIONS ===');
|
|
1081
|
+
if (decisions) {
|
|
1082
|
+
const projectLines = decisions
|
|
1083
|
+
.split('\n')
|
|
1084
|
+
.filter(l => /DECISION:.*SCOPE:\s*project/i.test(l));
|
|
1085
|
+
if (projectLines.length) {
|
|
1086
|
+
const decFolder = join(skillDir, '.claude', 'skills', 'project-decisions');
|
|
1087
|
+
const decPath = join(decFolder, 'SKILL.md');
|
|
1088
|
+
mkdirSync(decFolder, { recursive: true });
|
|
1089
|
+
const existing = (() => { try {
|
|
1090
|
+
return require('fs').readFileSync(decPath, 'utf-8');
|
|
1091
|
+
}
|
|
1092
|
+
catch {
|
|
1093
|
+
return '';
|
|
1094
|
+
} })();
|
|
1095
|
+
const header = existing ? '' : `# Project Decisions\n\nAuto-extracted from compact summaries.\n\n`;
|
|
1096
|
+
const entry = `\n## ${today} (session ${sessionId.substring(0, 8)})\n${projectLines.join('\n')}\n`;
|
|
1097
|
+
writeSyncFs(decPath, header + existing + entry, 'utf-8');
|
|
1098
|
+
console.log(`🧠 PostCompact: appended ${projectLines.length} decision(s) to ${decPath}`);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
catch (decErr) {
|
|
1103
|
+
console.error('⚠️ PostCompact: DECISIONS write failed:', decErr instanceof Error ? decErr.message : decErr);
|
|
1104
|
+
}
|
|
1105
|
+
// ── Section 3: SKILL_CANDIDATES — parse and write each skill ──
|
|
1106
|
+
try {
|
|
1107
|
+
const skillsSection = extractSection('=== SKILL_CANDIDATES ===');
|
|
1108
|
+
if (skillsSection) {
|
|
1109
|
+
const skillBlockRe = /---\s*SKILL:\s*([^\n-]+?)\s*---\n([\s\S]*?)---\s*END SKILL\s*---/g;
|
|
1110
|
+
const nameRe = /^[a-z][a-z0-9-]{1,39}$/;
|
|
1111
|
+
let match;
|
|
1112
|
+
while ((match = skillBlockRe.exec(skillsSection)) !== null) {
|
|
1113
|
+
const name = match[1].trim();
|
|
1114
|
+
const body = match[2].trim();
|
|
1115
|
+
if (!nameRe.test(name)) {
|
|
1116
|
+
console.warn(`⚠️ PostCompact: skipping skill with invalid name "${name}" (must match /^[a-z][a-z0-9-]{1,39}$/)`);
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
const skillFolder = join(skillDir, '.claude', 'skills', name);
|
|
1120
|
+
const skillPath = join(skillFolder, 'SKILL.md');
|
|
1121
|
+
mkdirSync(skillFolder, { recursive: true });
|
|
1122
|
+
const header = `# ${name}\nAuto-extracted: ${today} | Session: ${sessionId.substring(0, 8)}\n\n`;
|
|
1123
|
+
writeSyncFs(skillPath, header + body + '\n', 'utf-8');
|
|
1124
|
+
console.log(`🧠 PostCompact: wrote skill '${name}' to ${skillPath}`);
|
|
1125
|
+
}
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
catch (skillErr) {
|
|
1129
|
+
console.error('⚠️ PostCompact: SKILL_CANDIDATES write failed:', skillErr instanceof Error ? skillErr.message : skillErr);
|
|
1130
|
+
}
|
|
1131
|
+
// ── Section 4: BEHAVIORAL_LEARNINGS — write to learned-behaviors skill file ──
|
|
1132
|
+
try {
|
|
1133
|
+
const marker = '=== BEHAVIORAL_LEARNINGS ===';
|
|
1134
|
+
const idx = summary.indexOf(marker);
|
|
1135
|
+
if (idx !== -1) {
|
|
1136
|
+
const learnings = summary.substring(idx + marker.length).trim();
|
|
1137
|
+
if (learnings.length >= 30) {
|
|
1138
|
+
const skillFolder = join(skillDir, '.claude', 'skills', 'learned-behaviors');
|
|
1139
|
+
const skillPath = join(skillFolder, 'SKILL.md');
|
|
1140
|
+
mkdirSync(skillFolder, { recursive: true });
|
|
1141
|
+
const header = `# Learned Behaviors\n\nAuto-extracted from voice sessions via PostCompact.\nLast updated: ${today} | Session: ${sessionId.substring(0, 8)}...\n\n`;
|
|
1142
|
+
writeSyncFs(skillPath, header + learnings + '\n', 'utf-8');
|
|
1143
|
+
console.log(`🧠 PostCompact: wrote learned behaviors to ${skillPath} (${learnings.length} chars)`);
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
console.log('🧠 PostCompact: BEHAVIORAL_LEARNINGS section too short — skipping');
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
console.log('🧠 PostCompact: no BEHAVIORAL_LEARNINGS section found in summary — skipping');
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
catch (blErr) {
|
|
1154
|
+
console.error('⚠️ PostCompact: BEHAVIORAL_LEARNINGS write failed:', blErr instanceof Error ? blErr.message : blErr);
|
|
1155
|
+
}
|
|
974
1156
|
}
|
|
975
1157
|
catch (err) {
|
|
976
1158
|
console.error('⚠️ PostCompact hook error:', err instanceof Error ? err.message : err);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation Brain - Gemini 2.5 Pro powered conversation manager
|
|
3
|
+
*
|
|
4
|
+
* This is the "smart brain" that:
|
|
5
|
+
* 1. Keeps conversation alive with relevant questions
|
|
6
|
+
* 2. Builds context until we understand what user wants
|
|
7
|
+
* 3. Dispatches background research agents
|
|
8
|
+
* 4. Receives progress updates and decides when to execute
|
|
9
|
+
* 5. Handles direct commands immediately
|
|
10
|
+
*/
|
|
11
|
+
export interface ConversationMessage {
|
|
12
|
+
role: 'user' | 'assistant' | 'system';
|
|
13
|
+
content: string;
|
|
14
|
+
timestamp: Date;
|
|
15
|
+
}
|
|
16
|
+
export interface ResearchTask {
|
|
17
|
+
id: string;
|
|
18
|
+
query: string;
|
|
19
|
+
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
20
|
+
result?: string;
|
|
21
|
+
startedAt?: Date;
|
|
22
|
+
completedAt?: Date;
|
|
23
|
+
}
|
|
24
|
+
export interface BrainDecision {
|
|
25
|
+
action: 'speak' | 'research' | 'execute' | 'clarify' | 'direct_command';
|
|
26
|
+
speech?: string;
|
|
27
|
+
researchQueries?: string[];
|
|
28
|
+
executeTask?: string;
|
|
29
|
+
directCommand?: string;
|
|
30
|
+
reasoning?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface BrainState {
|
|
33
|
+
conversationHistory: ConversationMessage[];
|
|
34
|
+
userGoal: string | null;
|
|
35
|
+
userGoalConfidence: number;
|
|
36
|
+
pendingResearch: ResearchTask[];
|
|
37
|
+
completedResearch: ResearchTask[];
|
|
38
|
+
readyToExecute: boolean;
|
|
39
|
+
executionPlan: string | null;
|
|
40
|
+
}
|
|
41
|
+
export declare class ConversationBrain {
|
|
42
|
+
private llm;
|
|
43
|
+
private state;
|
|
44
|
+
private workingDir;
|
|
45
|
+
private onSpeak;
|
|
46
|
+
private onStateChange;
|
|
47
|
+
constructor(config: {
|
|
48
|
+
workingDir: string;
|
|
49
|
+
onSpeak: (text: string) => Promise<void>;
|
|
50
|
+
onStateChange: (state: string) => Promise<void>;
|
|
51
|
+
});
|
|
52
|
+
/**
|
|
53
|
+
* Process user input and decide what to do
|
|
54
|
+
*/
|
|
55
|
+
processUserInput(input: string): Promise<BrainDecision>;
|
|
56
|
+
/**
|
|
57
|
+
* Receive research results from background agents
|
|
58
|
+
*/
|
|
59
|
+
receiveResearchResult(taskId: string, result: string, success: boolean): void;
|
|
60
|
+
/**
|
|
61
|
+
* Check if we should provide a status update
|
|
62
|
+
*/
|
|
63
|
+
shouldProvideUpdate(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Generate a status update based on completed research
|
|
66
|
+
*/
|
|
67
|
+
generateStatusUpdate(): Promise<string | null>;
|
|
68
|
+
/**
|
|
69
|
+
* Get current state for debugging/display
|
|
70
|
+
*/
|
|
71
|
+
getState(): BrainState;
|
|
72
|
+
/**
|
|
73
|
+
* Reset conversation state
|
|
74
|
+
*/
|
|
75
|
+
reset(): void;
|
|
76
|
+
private buildAnalysisPrompt;
|
|
77
|
+
private parseDecision;
|
|
78
|
+
private updateState;
|
|
79
|
+
/**
|
|
80
|
+
* Create research tasks for background agents
|
|
81
|
+
*/
|
|
82
|
+
getPendingResearchTasks(): ResearchTask[];
|
|
83
|
+
/**
|
|
84
|
+
* Mark a research task as running
|
|
85
|
+
*/
|
|
86
|
+
markResearchRunning(taskId: string): void;
|
|
87
|
+
}
|
|
88
|
+
export declare function createConversationBrain(config: {
|
|
89
|
+
workingDir: string;
|
|
90
|
+
onSpeak: (text: string) => Promise<void>;
|
|
91
|
+
onStateChange: (state: string) => Promise<void>;
|
|
92
|
+
}): ConversationBrain;
|