Skip to content

Commit 7ae5279

Browse files
committed
Fix reflection chat payload and ground Pine continuity
1 parent a1a683d commit 7ae5279

10 files changed

+222
-12
lines changed

scripts/test-bug-fixes.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,27 @@ test('system prompt includes Pine diagnostics guidance', () => {
305305
assert(systemPromptContent.includes('TradingView shortcut profile rule'), 'System prompt should include TradingView shortcut-profile guidance');
306306
});
307307

308+
test('reflection trigger builds provider-compatible chat messages', () => {
309+
const reflectionTriggerPath = path.join(__dirname, '..', 'src', 'main', 'telemetry', 'reflection-trigger.js');
310+
const reflectionTrigger = require(reflectionTriggerPath);
311+
312+
assert(typeof reflectionTrigger.buildReflectionMessages === 'function', 'Reflection trigger should expose chat-message builder');
313+
const messages = reflectionTrigger.buildReflectionMessages([
314+
{
315+
task: 'Open TradingView alert dialog',
316+
phase: 'execution',
317+
actions: [{ type: 'key', key: 'alt+a' }],
318+
verifier: { exitCode: 1, stderr: 'dialog not observed' },
319+
context: { failedCount: 1 }
320+
}
321+
]);
322+
323+
assert(Array.isArray(messages), 'Reflection trigger should return a message array');
324+
assertEqual(messages[0].role, 'system', 'Reflection messages should begin with a system instruction');
325+
assertEqual(messages[1].role, 'user', 'Reflection messages should include a user payload for providers that reject system-only chat requests');
326+
assert(/Open TradingView alert dialog/i.test(messages[1].content), 'Reflection user payload should contain summarized failure context');
327+
});
328+
308329
test('rewriteActionsForReliability does not reinterpret passive TradingView open-state prompts as app launches', () => {
309330
const aiServicePath = path.join(__dirname, '..', 'src', 'main', 'ai-service.js');
310331
const aiService = require(aiServicePath);
@@ -397,6 +418,8 @@ test('ai-service gates TradingView follow-up typing on post-key observation chec
397418
assert(systemPromptContent.includes('safe new-script / bounded-edit paths'), 'system prompt should guide Pine authoring toward safe new-script flows');
398419
assert(observationCheckpointContent.includes('active Pine Editor surface before continuing'), 'Observation checkpoint failures should explain missing active Pine Editor state');
399420
assert(tradingViewPineContent.includes('requiresEditorActivation'), 'TradingView Pine workflows should distinguish editor activation from generic panel visibility');
421+
assert(tradingViewPineContent.includes("messageMentionsTradingViewShortcut(raw, 'open-pine-editor')"), 'TradingView Pine workflows should use shortcut-profile aliases for Pine Editor phrasing');
422+
assert(tradingViewPineContent.includes('getPineSurfaceMatchTerms'), 'TradingView Pine workflows should expose alias-aware Pine surface match terms');
400423
assert(tradingViewVerificationContent.includes('pine editor'), 'TradingView checkpoints should ground Pine Editor workflows');
401424
assert(tradingViewVerificationContent.includes('depth of market'), 'TradingView checkpoints should ground DOM workflows');
402425
assert(tradingViewVerificationContent.includes('paper trading'), 'TradingView checkpoints should ground Paper Trading workflows');

scripts/test-session-intent-state.js

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,58 @@ test('session intent continuity recommends targeted edits under Pine line-budget
506506

507507
fs.rmSync(tempDir, { recursive: true, force: true });
508508
});
509+
510+
test('session intent continuity surfaces Pine provenance summaries for continuation context', () => {
511+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'liku-session-intent-'));
512+
const stateFile = path.join(tempDir, 'session-intent-state.json');
513+
const store = createSessionIntentStateStore({ stateFile });
514+
515+
const recorded = store.recordExecutedTurn({
516+
userMessage: 'open pine version history in tradingview and summarize the top visible revision metadata',
517+
executionIntent: 'Inspect visible Pine Version History provenance.',
518+
committedSubgoal: 'Inspect top visible Pine Version History metadata',
519+
actionPlan: [
520+
{ type: 'focus_window', title: 'TradingView', processName: 'tradingview' },
521+
{ type: 'key', key: 'alt+h', verifyKind: 'panel-visible', verifyTarget: 'pine-version-history' },
522+
{ type: 'get_text', text: 'Pine Version History' }
523+
],
524+
results: [
525+
{ type: 'focus_window', success: true, message: 'focused' },
526+
{ type: 'key', success: true, message: 'version history opened' },
527+
{
528+
type: 'get_text',
529+
success: true,
530+
message: 'provenance inspected',
531+
pineStructuredSummary: {
532+
evidenceMode: 'provenance-summary',
533+
compactSummary: 'latest=Revision 12 | revisions=3 | recency=recent-visible',
534+
latestVisibleRevisionLabel: 'Revision 12',
535+
latestVisibleRevisionNumber: 12,
536+
latestVisibleRelativeTime: '5 minutes ago',
537+
visibleRevisionCount: 3,
538+
visibleRecencySignal: 'recent-visible',
539+
topVisibleRevisions: [
540+
{ label: 'Revision 12', relativeTime: '5 minutes ago', revisionNumber: 12 },
541+
{ label: 'Revision 11', relativeTime: '1 hour ago', revisionNumber: 11 }
542+
]
543+
}
544+
}
545+
],
546+
success: true,
547+
verification: { status: 'verified' }
548+
}, {
549+
cwd: path.join(__dirname, '..')
550+
});
551+
552+
const continuityContext = formatChatContinuityContext(recorded);
553+
assert(continuityContext.includes('pineEvidenceMode: provenance-summary'));
554+
assert(continuityContext.includes('pineCompactSummary: latest=Revision 12 | revisions=3 | recency=recent-visible'));
555+
assert(continuityContext.includes('pineLatestVisibleRevisionLabel: Revision 12'));
556+
assert(continuityContext.includes('pineLatestVisibleRevisionNumber: 12'));
557+
assert(continuityContext.includes('pineLatestVisibleRelativeTime: 5 minutes ago'));
558+
assert(continuityContext.includes('pineVisibleRevisionCount: 3'));
559+
assert(continuityContext.includes('pineVisibleRecencySignal: recent-visible'));
560+
assert(continuityContext.includes('pineTopVisibleRevisions: Revision 12 5 minutes ago #12 | Revision 11 1 hour ago #11'));
561+
562+
fs.rmSync(tempDir, { recursive: true, force: true });
563+
});

scripts/test-tradingview-pine-data-workflows.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ test('pine workflow recognizes pine editor status-output requests', () => {
4242
assert.strictEqual(intent.wantsEvidenceReadback, true);
4343
});
4444

45+
test('pine workflow recognizes pine-editor alias phrasing', () => {
46+
const intent = inferTradingViewPineIntent('open pine script editor in tradingview and read the visible compiler status', [
47+
{ type: 'key', key: 'ctrl+e' }
48+
]);
49+
50+
assert(intent, 'intent should be inferred');
51+
assert.strictEqual(intent.surfaceTarget, 'pine-editor');
52+
assert.strictEqual(intent.wantsEvidenceReadback, true);
53+
});
54+
4555
test('pine workflow recognizes compile-result requests', () => {
4656
const intent = inferTradingViewPineIntent('open pine editor in tradingview and summarize the compile result', [
4757
{ type: 'key', key: 'ctrl+e' }
@@ -84,6 +94,16 @@ test('pine workflow recognizes pine profiler evidence-gathering requests', () =>
8494
assert.strictEqual(intent.wantsEvidenceReadback, true);
8595
});
8696

97+
test('pine workflow recognizes pine profiler alias phrasing', () => {
98+
const intent = inferTradingViewPineIntent('open performance profiler in tradingview and summarize the metrics', [
99+
{ type: 'key', key: 'ctrl+shift+p' }
100+
]);
101+
102+
assert(intent, 'intent should be inferred');
103+
assert.strictEqual(intent.surfaceTarget, 'pine-profiler');
104+
assert.strictEqual(intent.wantsEvidenceReadback, true);
105+
});
106+
87107
test('pine workflow recognizes pine version history provenance requests', () => {
88108
const intent = inferTradingViewPineIntent('open pine version history in tradingview and read the latest visible revisions', [
89109
{ type: 'key', key: 'alt+h' }
@@ -94,6 +114,16 @@ test('pine workflow recognizes pine version history provenance requests', () =>
94114
assert.strictEqual(intent.wantsEvidenceReadback, true);
95115
});
96116

117+
test('pine workflow recognizes revision-history alias phrasing', () => {
118+
const intent = inferTradingViewPineIntent('open revision history in tradingview and read the latest visible revisions', [
119+
{ type: 'key', key: 'alt+h' }
120+
]);
121+
122+
assert(intent, 'intent should be inferred');
123+
assert.strictEqual(intent.surfaceTarget, 'pine-version-history');
124+
assert.strictEqual(intent.wantsEvidenceReadback, true);
125+
});
126+
97127
test('pine workflow classifies version history metadata summary requests', () => {
98128
const mode = inferPineVersionHistoryEvidenceMode('open pine version history in tradingview and summarize the top visible revision metadata');
99129

scripts/test-tradingview-shortcut-profile.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,13 @@ test('shortcut profile resolves aliases and documents official shortcut referenc
110110
test('shortcut profile exposes reusable phrase matching helpers for workflow inference', () => {
111111
const indicatorTerms = getTradingViewShortcutMatchTerms('indicator-search');
112112
const alertTerms = getTradingViewShortcutMatchTerms('create-alert');
113+
const pineEditorTerms = getTradingViewShortcutMatchTerms('open-pine-editor');
113114

114115
assert(indicatorTerms.includes('study search'));
115116
assert(indicatorTerms.includes('indicators menu'));
116117
assert(alertTerms.includes('new alert'));
118+
assert(pineEditorTerms.includes('pine script editor'));
117119
assert(messageMentionsTradingViewShortcut('open the study search in tradingview', 'indicator-search'));
118120
assert(messageMentionsTradingViewShortcut('open a new alert in tradingview', 'create-alert'));
121+
assert(messageMentionsTradingViewShortcut('open the pine script editor in tradingview', 'open-pine-editor'));
119122
});

scripts/test-windows-observation-flow.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,18 @@ async function run() {
469469
assert.strictEqual(rewritten[4].pineEvidenceMode, 'compile-result');
470470
});
471471

472+
await testAsync('low-signal TradingView pine-script-editor alias request rewrites to panel verification plus get_text', async () => {
473+
const rewritten = aiService.rewriteActionsForReliability([
474+
{ type: 'key', key: 'ctrl+e' }
475+
], {
476+
userMessage: 'open pine script editor in tradingview and read the visible compiler status'
477+
});
478+
479+
assert(Array.isArray(rewritten), 'pine editor alias rewrite should return an action array');
480+
assert.strictEqual(rewritten[2].verify.target, 'pine-editor');
481+
assert.strictEqual(rewritten[4].text, 'Pine Editor');
482+
});
483+
472484
await testAsync('low-signal TradingView Pine diagnostics request rewrites to panel verification plus diagnostics get_text', async () => {
473485
const rewritten = aiService.rewriteActionsForReliability([
474486
{ type: 'key', key: 'ctrl+e' }
@@ -531,6 +543,18 @@ async function run() {
531543
assert.strictEqual(rewritten[4].text, 'Pine Profiler');
532544
});
533545

546+
await testAsync('low-signal TradingView performance-profiler alias request rewrites to panel verification plus get_text', async () => {
547+
const rewritten = aiService.rewriteActionsForReliability([
548+
{ type: 'key', key: 'ctrl+shift+p' }
549+
], {
550+
userMessage: 'open performance profiler in tradingview and summarize the visible metrics'
551+
});
552+
553+
assert(Array.isArray(rewritten), 'pine profiler alias rewrite should return an action array');
554+
assert.strictEqual(rewritten[2].verify.target, 'pine-profiler');
555+
assert.strictEqual(rewritten[4].text, 'Pine Profiler');
556+
});
557+
534558
await testAsync('low-signal TradingView Pine Version History request rewrites to panel verification plus get_text', async () => {
535559
const rewritten = aiService.rewriteActionsForReliability([
536560
{ type: 'key', key: 'alt+h' }
@@ -546,6 +570,18 @@ async function run() {
546570
assert.strictEqual(rewritten[4].text, 'Pine Version History');
547571
});
548572

573+
await testAsync('low-signal TradingView revision-history alias request rewrites to panel verification plus get_text', async () => {
574+
const rewritten = aiService.rewriteActionsForReliability([
575+
{ type: 'key', key: 'alt+h' }
576+
], {
577+
userMessage: 'open revision history in tradingview and summarize the latest visible revisions'
578+
});
579+
580+
assert(Array.isArray(rewritten), 'revision-history alias rewrite should return an action array');
581+
assert.strictEqual(rewritten[2].verify.target, 'pine-version-history');
582+
assert.strictEqual(rewritten[4].text, 'Pine Version History');
583+
});
584+
549585
await testAsync('low-signal TradingView Pine Version History metadata request rewrites to provenance-summary get_text', async () => {
550586
const rewritten = aiService.rewriteActionsForReliability([
551587
{ type: 'key', key: 'alt+h' }

src/main/ai-service.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4813,13 +4813,11 @@ async function executeActions(actionData, onAction = null, onScreenshot = null,
48134813
while (evaluation.shouldReflect && reflectionIteration < MAX_REFLECTION_ITERATIONS) {
48144814
reflectionIteration++;
48154815
console.log(`[AI-SERVICE] Reflection triggered (iteration ${reflectionIteration}/${MAX_REFLECTION_ITERATIONS}): ${evaluation.reason}`);
4816-
const reflectionPrompt = reflectionTrigger.buildReflectionPrompt(evaluation.failures);
4816+
const reflectionMessages = reflectionTrigger.buildReflectionMessages(evaluation.failures);
48174817

48184818
try {
48194819
const reflectionResult = await providerOrchestrator.requestWithFallback(
4820-
[
4821-
{ role: 'system', content: reflectionPrompt }
4822-
],
4820+
reflectionMessages,
48234821
reflectionModelOverride, // N6: use reasoning model for reflection when configured
48244822
{ phase: 'reflection' }
48254823
);

src/main/session-intent-state.js

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ function isBroadAdvisoryPivotInput(message) {
626626
if (!text) return false;
627627

628628
const hasAdvisorySignal = /\b(what would help|what should i|how can i|confidence|invest|investing|visualizations|indicators|data|catalyst|fundamental|fundamentals|what matters|what should i watch|what should i use)\b/i.test(text);
629-
const hasExplicitExecutionSignal = /\b(continue|apply|add|open|show|set|switch|change|draw|place|capture|screenshot|pine logs|pine editor|volume profile|rsi|macd|bollinger|alert|timeframe|watchlist)\b/i.test(text);
629+
const hasExplicitExecutionSignal = /\b(continue|apply|add|open|show|set|switch|change|draw|place|capture|screenshot|pine logs|pine editor|pine script editor|pine profiler|performance profiler|pine version history|revision history|script history|volume profile|rsi|macd|bollinger|alert|timeframe|watchlist)\b/i.test(text);
630630
return hasAdvisorySignal && !hasExplicitExecutionSignal;
631631
}
632632

@@ -710,6 +710,8 @@ function formatChatContinuityContext(state, options = {}) {
710710
lines.push(`- pineVisibleSignals: ${pineStructuredSummary.visibleSignals.join(' | ')}`);
711711
}
712712
}
713+
if (pineStructuredSummary?.evidenceMode) lines.push(`- pineEvidenceMode: ${pineStructuredSummary.evidenceMode}`);
714+
if (pineStructuredSummary?.compactSummary) lines.push(`- pineCompactSummary: ${pineStructuredSummary.compactSummary}`);
713715
if (pineStructuredSummary?.compileStatus) {
714716
lines.push(`- pineCompileStatus: ${pineStructuredSummary.compileStatus}`);
715717
if (pineStructuredSummary.errorCountEstimate !== null && pineStructuredSummary.errorCountEstimate !== undefined) {
@@ -726,6 +728,22 @@ function formatChatContinuityContext(state, options = {}) {
726728
lines.push(`- pineTopVisibleDiagnostics: ${pineStructuredSummary.topVisibleDiagnostics.join(' | ')}`);
727729
}
728730
}
731+
if (pineStructuredSummary?.latestVisibleRevisionLabel) lines.push(`- pineLatestVisibleRevisionLabel: ${pineStructuredSummary.latestVisibleRevisionLabel}`);
732+
if (pineStructuredSummary?.latestVisibleRevisionNumber !== null && pineStructuredSummary?.latestVisibleRevisionNumber !== undefined) {
733+
lines.push(`- pineLatestVisibleRevisionNumber: ${pineStructuredSummary.latestVisibleRevisionNumber}`);
734+
}
735+
if (pineStructuredSummary?.latestVisibleRelativeTime) lines.push(`- pineLatestVisibleRelativeTime: ${pineStructuredSummary.latestVisibleRelativeTime}`);
736+
if (pineStructuredSummary?.visibleRevisionCount !== null && pineStructuredSummary?.visibleRevisionCount !== undefined) {
737+
lines.push(`- pineVisibleRevisionCount: ${pineStructuredSummary.visibleRevisionCount}`);
738+
}
739+
if (pineStructuredSummary?.visibleRecencySignal) lines.push(`- pineVisibleRecencySignal: ${pineStructuredSummary.visibleRecencySignal}`);
740+
if (Array.isArray(pineStructuredSummary?.topVisibleRevisions) && pineStructuredSummary.topVisibleRevisions.length > 0) {
741+
const revisions = pineStructuredSummary.topVisibleRevisions
742+
.map((entry) => [entry.label, entry.relativeTime, entry.revisionNumber !== null && entry.revisionNumber !== undefined ? `#${entry.revisionNumber}` : null].filter(Boolean).join(' '))
743+
.filter(Boolean)
744+
.join(' | ');
745+
if (revisions) lines.push(`- pineTopVisibleRevisions: ${revisions}`);
746+
}
729747
if (lastTurn?.executionResult?.popupFollowUp?.attempted) {
730748
const popup = lastTurn.executionResult.popupFollowUp;
731749
lines.push(`- popupFollowUp: ${popup.recipeId || 'recipe'} attempted=${popup.attempted ? 'yes' : 'no'} completed=${popup.completed ? 'yes' : 'no'}`);

src/main/telemetry/reflection-trigger.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ function buildReflectionPrompt(failures) {
9696
return `Failure ${i + 1}:\n task: ${f.task}\n phase: ${f.phase}\n${actions}\n${verifier}${context}`;
9797
}).join('\n\n');
9898

99-
return `You are the Reflection Agent for Liku CLI. Analyze these recent failures and respond with ONLY a JSON object:
99+
return `Analyze these recent failures and respond with ONLY a JSON object:
100100
101101
${failureSummary}
102102
@@ -117,6 +117,19 @@ Respond with exactly this JSON structure:
117117
}`;
118118
}
119119

120+
function buildReflectionMessages(failures) {
121+
return [
122+
{
123+
role: 'system',
124+
content: 'You are the Reflection Agent for Liku CLI. Analyze recent failures and respond with ONLY a JSON object.'
125+
},
126+
{
127+
role: 'user',
128+
content: buildReflectionPrompt(failures)
129+
}
130+
];
131+
}
132+
120133
/**
121134
* Parse the reflection response and apply the recommended action.
122135
*
@@ -222,6 +235,7 @@ function resetSession() {
222235

223236
module.exports = {
224237
evaluateOutcome,
238+
buildReflectionMessages,
225239
buildReflectionPrompt,
226240
applyReflectionResult,
227241
resetSession,

0 commit comments

Comments
 (0)