features: /changes, /search, /fork, web_search, prompt memoization, per-turn spend cap, workspace refresh
ci / check (macos-latest) (push) Canceled after 0s
ci / check (ubuntu-latest) (push) Canceled after 0s
ci / check (windows-latest) (push) Canceled after 0s

- /changes diffs the last turn's file snapshot (added/modified/deleted), reusing undo infra via SnapshotStack.peek()
- system prompt memoized behind version counters (notebook/memory/skills/plugins/tools/workspace); hit-rate in /cost, foundation for provider caching
- web_search: DuckDuckGo Lite, keyless, 5 results, SSRF-filtered, in the net set with ask permission
- /search <query>: full-text grep over saved sessions incl. tool-input JSON
- workspace file list re-walks at a turn boundary after writes
- maxSpendPerTurn: per-turn cap stops a runaway step with a notice
- /fork: branch the session at the last turn boundary, original untouched

821 tests pass, typecheck clean, build green
This commit is contained in:
asepharyana committed 2026-09-09 18:06:31 +07:00
1 parent 0bdf642672
commit e709737df1
24 files changed
+720 -41

No files matched your search

+25
View File
@@ -57,6 +57,14 @@ export function costPanel(
`- ceiling: ${ceiling.usd === undefined ? 'unpriced' : formatUsd(ceiling.usd)} of ${formatUsd(ceiling.ceiling)}`,
);
}
const perTurn = session.maxSpendPerTurn();
if (perTurn !== undefined) {
lines.push(`- per-turn cap: ${formatUsd(perTurn)}`);
}
const cache = session.promptCacheStats();
if (cache.hits + cache.misses > 0) {
lines.push(`- prompt cache: ${cache.hits} hits / ${cache.misses} misses`);
}
lines.push(`- context: ~${session.estimatedTokens()} tokens (est.)`, `- agent: \`${info.agent}\` thinking \`${info.thinking}\``);
lines.push(`- pricing verified: ${PRICING_VERIFIED_AT} (est., verify before billing)`);
@@ -77,3 +85,20 @@ export const todosPanel = (session: Session): Panel => ({
title: 'task list',
body: todoLines(session.notebook.state().todos),
});
const renderChangeList = (kind: string, paths: string[]): string =>
paths.length === 0 ? '' : `${kind}:\n${paths.map((p) => `- ${p}`).join('\n')}`;
/** What the last turn changed on disk — the file side of /undo, without undoing. */
export function changesPanel(session: Session): Panel {
const summary = session.lastTurnSummary();
if (!summary) return { title: 'changes', body: 'the last turn changed no files (bash effects are not tracked)' };
const body = [
renderChangeList('added', summary.added),
renderChangeList('modified', summary.modified),
renderChangeList('deleted', summary.deleted),
]
.filter(Boolean)
.join('\n');
return { title: 'changes', body };
}