Skip to content

Commit cb30093

Browse files
authored
Merge pull request #104 from scitix/feat/tui-investigation-feedback
feat: add TUI feedback prompt after deep investigation
2 parents c655c1f + af27353 commit cb30093

File tree

2 files changed

+70
-4
lines changed

2 files changed

+70
-4
lines changed

src/core/agent-factory.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ export async function createSiclawSession(
457457
}
458458
return parts;
459459
},
460-
extensionFactories: [contextPruningExtension, (api) => memoryFlushExtension(api, memoryIndexerRef.current), deepInvestigationExtension, (api) => setupExtension(api, credentialsDir)],
460+
extensionFactories: [contextPruningExtension, (api) => memoryFlushExtension(api, memoryIndexerRef.current), (api) => deepInvestigationExtension(api, memoryRef), (api) => setupExtension(api, credentialsDir)],
461461
additionalSkillPaths: skillsDirs,
462462
});
463463
await loader.reload();

src/core/extensions/deep-investigation.ts

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import {
88
createChecklist,
99
buildActivationMessage,
1010
} from "../../tools/dp-tools.js";
11+
import type { MemoryRef } from "../../tools/deep-search/tool.js";
12+
import { FEEDBACK_SIGNALS, type FeedbackStatus } from "../../memory/types.js";
1113

1214

1315
/**
@@ -156,10 +158,13 @@ function formatHypothesesWidget(text: string, theme: any): string[] {
156158

157159
// --- Extension ---
158160

159-
export default function deepInvestigationExtension(api: ExtensionAPI): void {
161+
export default function deepInvestigationExtension(api: ExtensionAPI, memoryRef?: MemoryRef): void {
160162
// --- Mode state ---
161163
let checklist: DpChecklist | null = null;
162164
let pendingActivation = false;
165+
let pendingFeedbackId: string | null = null;
166+
let deepSearchRan = false;
167+
let feedbackCleanup: (() => void) | null = null;
163168

164169
// --- Progress rendering state ---
165170
let activeUI: ExtensionUIContext | null = null;
@@ -209,13 +214,53 @@ export default function deepInvestigationExtension(api: ExtensionAPI): void {
209214
pendingActivation = true;
210215
}
211216

217+
/** Show feedback hint in status bar if deep_search ran. Can be called independently of DP mode exit. */
218+
function showFeedbackIfNeeded(ctx: ExtensionContext): void {
219+
if (!deepSearchRan || !ctx.hasUI) {
220+
deepSearchRan = false;
221+
pendingFeedbackId = null;
222+
return;
223+
}
224+
deepSearchRan = false;
225+
const id = pendingFeedbackId;
226+
pendingFeedbackId = null;
227+
228+
const cleanup = () => {
229+
ctx.ui.setStatus("dp-mode", undefined);
230+
unsubInput();
231+
clearTimeout(timer);
232+
feedbackCleanup = null;
233+
};
234+
235+
const timer = setTimeout(cleanup, 60_000);
236+
237+
const unsubInput = ctx.ui.onTerminalInput((data: string) => {
238+
if (ctx.ui.getEditorText().length > 0) return undefined;
239+
const keyMap: Record<string, FeedbackStatus> = { "1": "confirmed", "2": "corrected", "3": "rejected" };
240+
const status = keyMap[data];
241+
if (!status) return undefined;
242+
243+
if (id && memoryRef?.indexer) {
244+
memoryRef.indexer.updateInvestigationFeedback(id, FEEDBACK_SIGNALS[status], status);
245+
}
246+
ctx.ui.notify(`Feedback recorded: ${status}`);
247+
cleanup();
248+
return { consume: true };
249+
});
250+
251+
feedbackCleanup = cleanup;
252+
const hint = "Thanks! Rate: 1-\uD83D\uDC4D 2-\uD83D\uDC4E 3-\u274C";
253+
ctx.ui.setStatus("dp-mode", isThemeUsable(ctx) ? ctx.ui.theme.fg("muted", hint) : hint);
254+
}
255+
212256
function disableDpMode(ctx: ExtensionContext): void {
213257
if (!checklist) return;
214258
checklist = null;
215-
updateStatus(ctx);
216259
persistState();
217260
if (ctx.hasUI) ctx.ui.notify("Deep Investigation OFF");
218261
pendingActivation = false;
262+
showFeedbackIfNeeded(ctx);
263+
if (!feedbackCleanup) updateStatus(ctx); // only clear status if no feedback hint active
219264
}
220265

221266
function toggleDpMode(ctx: ExtensionContext): void {
@@ -552,6 +597,11 @@ export default function deepInvestigationExtension(api: ExtensionAPI): void {
552597
// --- session_start: restore persisted state ---
553598

554599
api.on("session_start", async (_event, ctx) => {
600+
// Clean up stale feedback prompt from previous session
601+
feedbackCleanup?.();
602+
deepSearchRan = false;
603+
pendingFeedbackId = null;
604+
555605
// Reset state — each session starts clean (prevents bleed from previous session)
556606
checklist = null;
557607

@@ -609,7 +659,7 @@ export default function deepInvestigationExtension(api: ExtensionAPI): void {
609659

610660
// --- tool_result: progress cleanup (no auto-mark) ---
611661

612-
api.on("tool_result", (event) => {
662+
api.on("tool_result", (event, ctx) => {
613663
if (event.toolName === "deep_search") {
614664
// Progress rendering cleanup
615665
if (activeUI) {
@@ -618,9 +668,25 @@ export default function deepInvestigationExtension(api: ExtensionAPI): void {
618668
}
619669
activeUI = null;
620670
resetProgressState();
671+
672+
// Flag that deep_search ran; capture investigationId if available
673+
deepSearchRan = true;
674+
const details = event.details as Record<string, unknown> | undefined;
675+
pendingFeedbackId = (details?.investigationId as string) ?? null;
676+
677+
// Standalone deep_search (no DP mode): show feedback immediately since
678+
// disableDpMode() won't fire. Guard with !checklist to avoid double-prompting.
679+
if (!checklist) {
680+
showFeedbackIfNeeded(ctx);
681+
}
621682
}
622683
});
623684

685+
// Clean up feedback hint when agent starts processing next message
686+
api.on("agent_start", () => {
687+
feedbackCleanup?.();
688+
});
689+
624690
// --- context: filter UI-only custom messages ---
625691

626692
// Custom types that are UI-only metadata — must never be sent to the LLM.

0 commit comments

Comments
 (0)