auto-name pi sessions with kimi k2.5 after 3 messages
This commit is contained in:
@@ -13,9 +13,9 @@
|
||||
"vscode-languageserver-protocol": "^3.17.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@mariozechner/pi-ai": "^0.56.0",
|
||||
"@mariozechner/pi-coding-agent": "^0.56.0",
|
||||
"@mariozechner/pi-tui": "^0.56.0",
|
||||
"@mariozechner/pi-ai": "^0.56.3",
|
||||
"@mariozechner/pi-coding-agent": "^0.56.3",
|
||||
"@mariozechner/pi-tui": "^0.56.3",
|
||||
"@types/node": "^25.3.3",
|
||||
"typescript": "^5.7.0"
|
||||
},
|
||||
|
||||
Generated
+47
-43
@@ -34,14 +34,14 @@ importers:
|
||||
version: 3.17.5
|
||||
devDependencies:
|
||||
'@mariozechner/pi-ai':
|
||||
specifier: ^0.56.0
|
||||
version: 0.56.0(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: ^0.56.3
|
||||
version: 0.56.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-coding-agent':
|
||||
specifier: ^0.56.0
|
||||
version: 0.56.0(ws@8.19.0)(zod@3.25.76)
|
||||
specifier: ^0.56.3
|
||||
version: 0.56.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-tui':
|
||||
specifier: ^0.56.0
|
||||
version: 0.56.0
|
||||
specifier: ^0.56.3
|
||||
version: 0.56.3
|
||||
'@types/node':
|
||||
specifier: ^25.3.3
|
||||
version: 25.3.3
|
||||
@@ -289,26 +289,26 @@ packages:
|
||||
resolution: {integrity: sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==}
|
||||
hasBin: true
|
||||
|
||||
'@mariozechner/pi-agent-core@0.56.0':
|
||||
resolution: {integrity: sha512-p8lEhONkQJgnALbkgpYd9haXcWEB32lXExvB32Y6b7JUTIdU/HIGwe8+NFVmrLrnhORbAE1ORY+3AtwtiogD4g==}
|
||||
'@mariozechner/pi-agent-core@0.56.3':
|
||||
resolution: {integrity: sha512-TsI1zENf3wqqKPaERnj486Q4i6Y/y6lAZipLNcfDYUDxDrLwNfQ9EW9xukkbJfTZ8zjG3VZ2pBZe3C7wM51dVQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@mariozechner/pi-ai@0.56.0':
|
||||
resolution: {integrity: sha512-4YvTpPodywMFBMsKJfxjWJN5KcQYYc3WVvfa7mofk9Xnb6HZdFKez8wxznGWX5B6vMizvTnD4cyt/XuMcBLRFw==}
|
||||
'@mariozechner/pi-ai@0.56.3':
|
||||
resolution: {integrity: sha512-l4J+cVyVeBLAlGOY/osGDvsbTz0DySCQmR171G6SdbPvIeLGhIi6siZ+zHwq91GJYjv/wtu/08M08ag2mGZKeA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@mariozechner/pi-coding-agent@0.56.0':
|
||||
resolution: {integrity: sha512-jnBLaA5z0IhUgohfIrfeGQFhFwpKtbrc9xr4qD573afzjC9xoa7lZX9+Z1Uuh54BVVnVOW9kn3C65ITU0+6SuQ==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
'@mariozechner/pi-coding-agent@0.56.3':
|
||||
resolution: {integrity: sha512-yHgnadye+TT/4NWKBirZUjw/LWdNWTa7M4HJdX2RxRbwuj4q7RZ0Aqy+lQbOHEPDQYhxK3kZb9hjiAbbGficZQ==}
|
||||
engines: {node: '>=20.6.0'}
|
||||
hasBin: true
|
||||
|
||||
'@mariozechner/pi-tui@0.56.0':
|
||||
resolution: {integrity: sha512-FZnvYvyvKJenFQqIs3iW0MGzrbOrTcAV6jFN9SFFqrjS7RDn8PkZ0iS3wZcey+2sT+YAf8AKu4f4BOXUJNB+IQ==}
|
||||
'@mariozechner/pi-tui@0.56.3':
|
||||
resolution: {integrity: sha512-eZ1P9QRKHp78hwx+lITr/mujZqe+eCwL/bOS9vXXkFP070RW4VYum0j7TJ4BrFEH/nNkXRS1tYCXYU05une1bA==}
|
||||
engines: {node: '>=20.0.0'}
|
||||
|
||||
'@mistralai/mistralai@1.10.0':
|
||||
resolution: {integrity: sha512-tdIgWs4Le8vpvPiUEWne6tK0qbVc+jMenujnvTqOjogrJUsCSQhus0tHTU1avDDh5//Rq2dFgP9mWRAdIEoBqg==}
|
||||
'@mistralai/mistralai@1.14.1':
|
||||
resolution: {integrity: sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==}
|
||||
|
||||
'@mixmark-io/domino@2.2.0':
|
||||
resolution: {integrity: sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw==}
|
||||
@@ -981,8 +981,8 @@ packages:
|
||||
once@1.4.0:
|
||||
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
|
||||
|
||||
openai@6.10.0:
|
||||
resolution: {integrity: sha512-ITxOGo7rO3XRMiKA5l7tQ43iNNu+iXGFAcf2t+aWVzzqRaS0i7m1K2BhxNdaveB+5eENhO0VY1FkiZzhBk4v3A==}
|
||||
openai@6.26.0:
|
||||
resolution: {integrity: sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
ws: ^8.18.0
|
||||
@@ -1262,18 +1262,18 @@ packages:
|
||||
peerDependencies:
|
||||
zod: ^3.25 || ^4
|
||||
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
zod@4.3.6:
|
||||
resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==}
|
||||
|
||||
snapshots:
|
||||
|
||||
'@anthropic-ai/sdk@0.52.0': {}
|
||||
|
||||
'@anthropic-ai/sdk@0.73.0(zod@3.25.76)':
|
||||
'@anthropic-ai/sdk@0.73.0(zod@4.3.6)':
|
||||
dependencies:
|
||||
json-schema-to-ts: 3.1.1
|
||||
optionalDependencies:
|
||||
zod: 3.25.76
|
||||
zod: 4.3.6
|
||||
|
||||
'@aws-crypto/crc32@5.2.0':
|
||||
dependencies:
|
||||
@@ -1722,9 +1722,9 @@ snapshots:
|
||||
std-env: 3.10.0
|
||||
yoctocolors: 2.1.2
|
||||
|
||||
'@mariozechner/pi-agent-core@0.56.0(ws@8.19.0)(zod@3.25.76)':
|
||||
'@mariozechner/pi-agent-core@0.56.3(ws@8.19.0)(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@mariozechner/pi-ai': 0.56.0(ws@8.19.0)(zod@3.25.76)
|
||||
'@mariozechner/pi-ai': 0.56.3(ws@8.19.0)(zod@4.3.6)
|
||||
transitivePeerDependencies:
|
||||
- '@modelcontextprotocol/sdk'
|
||||
- aws-crt
|
||||
@@ -1734,21 +1734,21 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@mariozechner/pi-ai@0.56.0(ws@8.19.0)(zod@3.25.76)':
|
||||
'@mariozechner/pi-ai@0.56.3(ws@8.19.0)(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.73.0(zod@3.25.76)
|
||||
'@anthropic-ai/sdk': 0.73.0(zod@4.3.6)
|
||||
'@aws-sdk/client-bedrock-runtime': 3.1002.0
|
||||
'@google/genai': 1.43.0
|
||||
'@mistralai/mistralai': 1.10.0
|
||||
'@mistralai/mistralai': 1.14.1
|
||||
'@sinclair/typebox': 0.34.48
|
||||
ajv: 8.18.0
|
||||
ajv-formats: 3.0.1(ajv@8.18.0)
|
||||
chalk: 5.6.2
|
||||
openai: 6.10.0(ws@8.19.0)(zod@3.25.76)
|
||||
openai: 6.26.0(ws@8.19.0)(zod@4.3.6)
|
||||
partial-json: 0.1.7
|
||||
proxy-agent: 6.5.0
|
||||
undici: 7.22.0
|
||||
zod-to-json-schema: 3.25.1(zod@3.25.76)
|
||||
zod-to-json-schema: 3.25.1(zod@4.3.6)
|
||||
transitivePeerDependencies:
|
||||
- '@modelcontextprotocol/sdk'
|
||||
- aws-crt
|
||||
@@ -1758,12 +1758,12 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@mariozechner/pi-coding-agent@0.56.0(ws@8.19.0)(zod@3.25.76)':
|
||||
'@mariozechner/pi-coding-agent@0.56.3(ws@8.19.0)(zod@4.3.6)':
|
||||
dependencies:
|
||||
'@mariozechner/jiti': 2.6.5
|
||||
'@mariozechner/pi-agent-core': 0.56.0(ws@8.19.0)(zod@3.25.76)
|
||||
'@mariozechner/pi-ai': 0.56.0(ws@8.19.0)(zod@3.25.76)
|
||||
'@mariozechner/pi-tui': 0.56.0
|
||||
'@mariozechner/pi-agent-core': 0.56.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-ai': 0.56.3(ws@8.19.0)(zod@4.3.6)
|
||||
'@mariozechner/pi-tui': 0.56.3
|
||||
'@silvia-odwyer/photon-node': 0.3.4
|
||||
chalk: 5.6.2
|
||||
cli-highlight: 2.1.11
|
||||
@@ -1790,7 +1790,7 @@ snapshots:
|
||||
- ws
|
||||
- zod
|
||||
|
||||
'@mariozechner/pi-tui@0.56.0':
|
||||
'@mariozechner/pi-tui@0.56.3':
|
||||
dependencies:
|
||||
'@types/mime-types': 2.1.4
|
||||
chalk: 5.6.2
|
||||
@@ -1800,10 +1800,14 @@ snapshots:
|
||||
optionalDependencies:
|
||||
koffi: 2.15.1
|
||||
|
||||
'@mistralai/mistralai@1.10.0':
|
||||
'@mistralai/mistralai@1.14.1':
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
zod-to-json-schema: 3.25.1(zod@3.25.76)
|
||||
ws: 8.19.0
|
||||
zod: 4.3.6
|
||||
zod-to-json-schema: 3.25.1(zod@4.3.6)
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- utf-8-validate
|
||||
|
||||
'@mixmark-io/domino@2.2.0': {}
|
||||
|
||||
@@ -2581,10 +2585,10 @@ snapshots:
|
||||
dependencies:
|
||||
wrappy: 1.0.2
|
||||
|
||||
openai@6.10.0(ws@8.19.0)(zod@3.25.76):
|
||||
openai@6.26.0(ws@8.19.0)(zod@4.3.6):
|
||||
optionalDependencies:
|
||||
ws: 8.19.0
|
||||
zod: 3.25.76
|
||||
zod: 4.3.6
|
||||
|
||||
p-limit@6.2.0:
|
||||
dependencies:
|
||||
@@ -2844,8 +2848,8 @@ snapshots:
|
||||
|
||||
yoctocolors@2.1.2: {}
|
||||
|
||||
zod-to-json-schema@3.25.1(zod@3.25.76):
|
||||
zod-to-json-schema@3.25.1(zod@4.3.6):
|
||||
dependencies:
|
||||
zod: 3.25.76
|
||||
zod: 4.3.6
|
||||
|
||||
zod@3.25.76: {}
|
||||
zod@4.3.6: {}
|
||||
|
||||
@@ -1,27 +1,348 @@
|
||||
/**
|
||||
* Session naming example.
|
||||
* Enhanced session naming with AI-powered auto-naming.
|
||||
*
|
||||
* Shows setSessionName/getSessionName to give sessions friendly names
|
||||
* that appear in the session selector instead of the first message.
|
||||
* Features:
|
||||
* - Manual naming: /session-name [name]
|
||||
* - Auto-naming command: /session-name --auto
|
||||
* - Automatic naming: triggers after 3 messages if no name set
|
||||
*
|
||||
* Usage: /session-name [name] - set or show session name
|
||||
* Auto-naming analyzes the conversation history and generates a concise,
|
||||
* descriptive name for the session using a cheap model (MiniMax).
|
||||
*/
|
||||
|
||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||
import { complete, type Message } from "@mariozechner/pi-ai";
|
||||
import { getModel } from "@mariozechner/pi-ai";
|
||||
import type { ExtensionAPI, SessionEntry } from "@mariozechner/pi-coding-agent";
|
||||
import {
|
||||
BorderedLoader,
|
||||
convertToLlm,
|
||||
serializeConversation,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
import * as os from "node:os";
|
||||
|
||||
const SYSTEM_PROMPT = `You are a session naming assistant. Given a conversation history, generate a short, descriptive session name (2-5 words) that captures the main topic or task.
|
||||
|
||||
Guidelines:
|
||||
- Be concise but specific
|
||||
- Use kebab-case or natural language
|
||||
- Focus on the core task/question
|
||||
- Avoid generic names like "discussion" or "conversation"
|
||||
- No quotes, no punctuation at the end
|
||||
|
||||
Examples:
|
||||
- "fix auth bug" -> "fix-auth-bug" or "authentication fix"
|
||||
- "how do I deploy to vercel" -> "vercel deployment"
|
||||
- "explain react hooks" -> "react hooks explanation"
|
||||
- "optimize database queries" -> "db query optimization"
|
||||
|
||||
Output ONLY the session name, nothing else.`;
|
||||
|
||||
// Cheap model for auto-naming: Minimax from OpenCode Go
|
||||
const AUTO_NAME_MODEL = getModel("opencode-go", "minimax-m2.5");
|
||||
|
||||
// Number of messages before auto-naming kicks in
|
||||
const AUTO_NAME_THRESHOLD = 3;
|
||||
|
||||
// Debug log file
|
||||
const LOG_FILE = path.join(os.homedir(), ".pi", "session-name-debug.log");
|
||||
|
||||
function log(message: string) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const entry = `[${timestamp}] ${message}\n`;
|
||||
try {
|
||||
fs.appendFileSync(LOG_FILE, entry);
|
||||
} catch (e) {
|
||||
// ignore write errors
|
||||
}
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
pi.registerCommand("session-name", {
|
||||
description: "Set or show session name (usage: /session-name [new name])",
|
||||
handler: async (args, ctx) => {
|
||||
const name = args.trim();
|
||||
// Track if we've already attempted auto-naming for this session
|
||||
let autoNamedAttempted = false;
|
||||
|
||||
if (name) {
|
||||
pi.setSessionName(name);
|
||||
ctx.ui.notify(`Session named: ${name}`, "info");
|
||||
} else {
|
||||
const current = pi.getSessionName();
|
||||
ctx.ui.notify(current ? `Session: ${current}` : "No session name set", "info");
|
||||
}
|
||||
},
|
||||
});
|
||||
// Listen for agent_end to auto-name sessions (non-blocking)
|
||||
pi.on("agent_end", (_event, ctx) => {
|
||||
log("=== agent_end triggered ===");
|
||||
log(`hasUI: ${ctx.hasUI}`);
|
||||
log(`current session name: ${pi.getSessionName()}`);
|
||||
log(`autoNamedAttempted: ${autoNamedAttempted}`);
|
||||
|
||||
// Skip if already has a name or already attempted
|
||||
if (pi.getSessionName() || autoNamedAttempted) {
|
||||
log("Skipping: already has name or already attempted");
|
||||
return;
|
||||
}
|
||||
|
||||
// Count user messages in the branch
|
||||
const branch = ctx.sessionManager.getBranch();
|
||||
const userMessages = branch.filter(
|
||||
(entry): entry is SessionEntry & { type: "message" } =>
|
||||
entry.type === "message" && entry.message.role === "user",
|
||||
);
|
||||
|
||||
log(`Total entries in branch: ${branch.length}`);
|
||||
log(`User messages: ${userMessages.length}`);
|
||||
log(`Threshold: ${AUTO_NAME_THRESHOLD}`);
|
||||
|
||||
// Only auto-name after threshold is reached
|
||||
if (userMessages.length < AUTO_NAME_THRESHOLD) {
|
||||
log("Skipping: below threshold");
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark as attempted so we don't try again
|
||||
autoNamedAttempted = true;
|
||||
log("Threshold reached, attempting auto-name");
|
||||
|
||||
// Only auto-name in interactive mode
|
||||
if (!ctx.hasUI) {
|
||||
log("Skipping: no UI (non-interactive mode)");
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather conversation context
|
||||
const messages = branch
|
||||
.filter(
|
||||
(entry): entry is SessionEntry & { type: "message" } =>
|
||||
entry.type === "message",
|
||||
)
|
||||
.map((entry) => entry.message);
|
||||
|
||||
log(`Total messages to analyze: ${messages.length}`);
|
||||
|
||||
if (messages.length === 0) {
|
||||
log("No messages found, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to LLM format and serialize
|
||||
const llmMessages = convertToLlm(messages);
|
||||
const conversationText = serializeConversation(llmMessages);
|
||||
|
||||
log(`Conversation text length: ${conversationText.length}`);
|
||||
|
||||
// Truncate if too long (keep costs low)
|
||||
const maxChars = 4000;
|
||||
const truncatedText =
|
||||
conversationText.length > maxChars
|
||||
? conversationText.slice(0, maxChars) + "\n..."
|
||||
: conversationText;
|
||||
|
||||
log(`Truncated text length: ${truncatedText.length}`);
|
||||
log("Starting background auto-name...");
|
||||
|
||||
// Fire-and-forget: run auto-naming in background without blocking
|
||||
const doAutoName = async () => {
|
||||
const apiKey = await ctx.modelRegistry.getApiKey(AUTO_NAME_MODEL);
|
||||
log(`Got API key: ${apiKey ? "yes" : "no"}`);
|
||||
|
||||
if (!apiKey) {
|
||||
log("No API key available, aborting");
|
||||
return;
|
||||
}
|
||||
|
||||
const userMessage: Message = {
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `## Conversation History\n\n${truncatedText}\n\nGenerate a concise session name for this conversation.`,
|
||||
},
|
||||
],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const response = await complete(
|
||||
AUTO_NAME_MODEL,
|
||||
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
||||
{ apiKey },
|
||||
);
|
||||
|
||||
log(`Response received, stopReason: ${response.stopReason}`);
|
||||
|
||||
if (response.stopReason === "aborted") {
|
||||
log("Request was aborted");
|
||||
return;
|
||||
}
|
||||
|
||||
const name = response.content
|
||||
.filter((c): c is { type: "text"; text: string } => c.type === "text")
|
||||
.map((c) => c.text.trim())
|
||||
.join(" ")
|
||||
.replace(/^[\"']|[\"']$/g, ""); // Remove surrounding quotes
|
||||
|
||||
log(`Generated name: "${name}"`);
|
||||
|
||||
// Clean up the generated name
|
||||
const cleanName = name
|
||||
.replace(/\n/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, 50); // Max 50 chars
|
||||
|
||||
log(`Cleaned name: "${cleanName}"`);
|
||||
|
||||
if (cleanName) {
|
||||
pi.setSessionName(cleanName);
|
||||
ctx.ui.notify(`Auto-named: ${cleanName}`, "info");
|
||||
log(`Successfully set session name to: ${cleanName}`);
|
||||
} else {
|
||||
log("Cleaned name was empty, not setting");
|
||||
}
|
||||
};
|
||||
|
||||
// Run in background without awaiting - don't block the agent
|
||||
doAutoName().catch((err) => {
|
||||
log(`ERROR: ${err}`);
|
||||
console.error("Auto-naming failed:", err);
|
||||
});
|
||||
});
|
||||
|
||||
// Reset flag on new session
|
||||
pi.on("session_start", () => {
|
||||
log("=== session_start ===");
|
||||
autoNamedAttempted = false;
|
||||
log("Reset autoNamedAttempted to false");
|
||||
});
|
||||
|
||||
pi.on("session_switch", () => {
|
||||
log("=== session_switch ===");
|
||||
autoNamedAttempted = false;
|
||||
log("Reset autoNamedAttempted to false");
|
||||
});
|
||||
|
||||
// Manual command for setting/getting session name
|
||||
pi.registerCommand("session-name", {
|
||||
description:
|
||||
"Set, show, or auto-generate session name (usage: /session-name [name] or /session-name --auto)",
|
||||
handler: async (args, ctx) => {
|
||||
const trimmedArgs = args.trim();
|
||||
|
||||
// Show current name if no args
|
||||
if (!trimmedArgs) {
|
||||
const current = pi.getSessionName();
|
||||
ctx.ui.notify(
|
||||
current ? `Session: ${current}` : "No session name set",
|
||||
"info",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Auto-generate name using AI
|
||||
if (trimmedArgs === "--auto" || trimmedArgs === "-a") {
|
||||
if (!ctx.hasUI) {
|
||||
ctx.ui.notify("Auto-naming requires interactive mode", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Gather conversation context
|
||||
const branch = ctx.sessionManager.getBranch();
|
||||
const messages = branch
|
||||
.filter(
|
||||
(entry): entry is SessionEntry & { type: "message" } =>
|
||||
entry.type === "message",
|
||||
)
|
||||
.map((entry) => entry.message);
|
||||
|
||||
if (messages.length === 0) {
|
||||
ctx.ui.notify("No conversation to analyze", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
// Convert to LLM format and serialize
|
||||
const llmMessages = convertToLlm(messages);
|
||||
const conversationText = serializeConversation(llmMessages);
|
||||
|
||||
// Truncate if too long (keep costs low)
|
||||
const maxChars = 4000;
|
||||
const truncatedText =
|
||||
conversationText.length > maxChars
|
||||
? conversationText.slice(0, maxChars) + "\n..."
|
||||
: conversationText;
|
||||
|
||||
// Generate name with loader UI
|
||||
const result = await ctx.ui.custom<string | null>(
|
||||
(tui, theme, _kb, done) => {
|
||||
const loader = new BorderedLoader(
|
||||
tui,
|
||||
theme,
|
||||
"Generating session name...",
|
||||
);
|
||||
loader.onAbort = () => done(null);
|
||||
|
||||
const doGenerate = async () => {
|
||||
const apiKey = await ctx.modelRegistry.getApiKey(AUTO_NAME_MODEL);
|
||||
|
||||
const userMessage: Message = {
|
||||
role: "user",
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `## Conversation History\n\n${truncatedText}\n\nGenerate a concise session name for this conversation.`,
|
||||
},
|
||||
],
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
const response = await complete(
|
||||
AUTO_NAME_MODEL,
|
||||
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
||||
{ apiKey, signal: loader.signal },
|
||||
);
|
||||
|
||||
if (response.stopReason === "aborted") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const name = response.content
|
||||
.filter(
|
||||
(c): c is { type: "text"; text: string } => c.type === "text",
|
||||
)
|
||||
.map((c) => c.text.trim())
|
||||
.join(" ")
|
||||
.replace(/^[\"']|[\"']$/g, ""); // Remove surrounding quotes
|
||||
|
||||
return name;
|
||||
};
|
||||
|
||||
doGenerate()
|
||||
.then(done)
|
||||
.catch((err) => {
|
||||
console.error("Auto-naming failed:", err);
|
||||
done(null);
|
||||
});
|
||||
|
||||
return loader;
|
||||
},
|
||||
);
|
||||
|
||||
if (result === null) {
|
||||
ctx.ui.notify("Auto-naming cancelled", "info");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up the generated name
|
||||
const cleanName = result
|
||||
.replace(/\n/g, " ")
|
||||
.replace(/\s+/g, " ")
|
||||
.trim()
|
||||
.slice(0, 50); // Max 50 chars
|
||||
|
||||
if (!cleanName) {
|
||||
ctx.ui.notify("Failed to generate name", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
pi.setSessionName(cleanName);
|
||||
ctx.ui.notify(`Session auto-named: ${cleanName}`, "info");
|
||||
return;
|
||||
}
|
||||
|
||||
// Manual naming
|
||||
pi.setSessionName(trimmedArgs);
|
||||
ctx.ui.notify(`Session named: ${trimmedArgs}`, "info");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"lastChangelogVersion": "0.56.1",
|
||||
"defaultProvider": "openrouter",
|
||||
"defaultModel": "openai/gpt-5.3-codex",
|
||||
"lastChangelogVersion": "0.56.3",
|
||||
"defaultProvider": "opencode-go",
|
||||
"defaultModel": "kimi-k2.5",
|
||||
"defaultThinkingLevel": "high",
|
||||
"theme": "matugen",
|
||||
"lsp": {
|
||||
|
||||
Reference in New Issue
Block a user