mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-19 16:03:35 +00:00
Enable more biome lints and fix things
This commit is contained in:
parent
9c18439c4d
commit
d5fd685901
57 changed files with 151 additions and 199 deletions
|
|
@ -7,11 +7,7 @@
|
||||||
"style": {
|
"style": {
|
||||||
"noNonNullAssertion": "off",
|
"noNonNullAssertion": "off",
|
||||||
"useConst": "error",
|
"useConst": "error",
|
||||||
"useNodejsImportProtocol": "off",
|
"useNodejsImportProtocol": "off"
|
||||||
"useTemplate": "off"
|
|
||||||
},
|
|
||||||
"correctness": {
|
|
||||||
"noUnusedVariables": "off"
|
|
||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noExplicitAny": "off",
|
"noExplicitAny": "off",
|
||||||
|
|
|
||||||
|
|
@ -177,6 +177,7 @@ async function streamAssistantResponse(
|
||||||
systemPrompt: context.systemPrompt,
|
systemPrompt: context.systemPrompt,
|
||||||
messages: [...processedMessages].map((m) => {
|
messages: [...processedMessages].map((m) => {
|
||||||
if (m.role === "toolResult") {
|
if (m.role === "toolResult") {
|
||||||
|
// biome-ignore lint/correctness/noUnusedVariables: fine here
|
||||||
const { details, ...rest } = m;
|
const { details, ...rest } = m;
|
||||||
return rest;
|
return rest;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ export interface CalculateResult extends AgentToolResult<undefined> {
|
||||||
|
|
||||||
export function calculate(expression: string): CalculateResult {
|
export function calculate(expression: string): CalculateResult {
|
||||||
try {
|
try {
|
||||||
const result = new Function("return " + expression)();
|
const result = new Function(`return ${expression}`)();
|
||||||
return { content: [{ type: "text", text: `${expression} = ${result}` }], details: undefined };
|
return { content: [{ type: "text", text: `${expression} = ${result}` }], details: undefined };
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
throw new Error(e.message || String(e));
|
throw new Error(e.message || String(e));
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ export async function getCurrentTime(timezone?: string): Promise<GetCurrentTimeR
|
||||||
content: [{ type: "text", text: timeStr }],
|
content: [{ type: "text", text: timeStr }],
|
||||||
details: { utcTimestamp: date.getTime() },
|
details: { utcTimestamp: date.getTime() },
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
throw new Error(`Invalid timezone: ${timezone}. Current UTC time: ${date.toISOString()}`);
|
throw new Error(`Invalid timezone: ${timezone}. Current UTC time: ${date.toISOString()}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||||
currentItem = item;
|
currentItem = item;
|
||||||
currentBlock = {
|
currentBlock = {
|
||||||
type: "toolCall",
|
type: "toolCall",
|
||||||
id: item.call_id + "|" + item.id,
|
id: `${item.call_id}|${item.id}`,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
arguments: {},
|
arguments: {},
|
||||||
partialJson: item.arguments || "",
|
partialJson: item.arguments || "",
|
||||||
|
|
@ -251,7 +251,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||||
} else if (item.type === "function_call") {
|
} else if (item.type === "function_call") {
|
||||||
const toolCall: ToolCall = {
|
const toolCall: ToolCall = {
|
||||||
type: "toolCall",
|
type: "toolCall",
|
||||||
id: item.call_id + "|" + item.id,
|
id: `${item.call_id}|${item.id}`,
|
||||||
name: item.name,
|
name: item.name,
|
||||||
arguments: JSON.parse(item.arguments),
|
arguments: JSON.parse(item.arguments),
|
||||||
};
|
};
|
||||||
|
|
@ -462,9 +462,9 @@ function convertMessages(model: Model<"openai-responses">, context: Context): Re
|
||||||
// OpenAI requires id to be max 64 characters
|
// OpenAI requires id to be max 64 characters
|
||||||
let msgId = textBlock.textSignature;
|
let msgId = textBlock.textSignature;
|
||||||
if (!msgId) {
|
if (!msgId) {
|
||||||
msgId = "msg_" + msgIndex;
|
msgId = `msg_${msgIndex}`;
|
||||||
} else if (msgId.length > 64) {
|
} else if (msgId.length > 64) {
|
||||||
msgId = "msg_" + shortHash(msgId);
|
msgId = `msg_${shortHash(msgId)}`;
|
||||||
}
|
}
|
||||||
output.push({
|
output.push({
|
||||||
type: "message",
|
type: "message",
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,6 @@ const SCOPES = [
|
||||||
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
||||||
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
const TOKEN_URL = "https://oauth2.googleapis.com/token";
|
||||||
|
|
||||||
// Antigravity uses sandbox endpoint
|
|
||||||
const CODE_ASSIST_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com";
|
|
||||||
|
|
||||||
// Fallback project ID when discovery fails
|
// Fallback project ID when discovery fails
|
||||||
const DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
const DEFAULT_PROJECT_ID = "rising-fact-p41fc";
|
||||||
|
|
||||||
|
|
@ -115,13 +112,6 @@ interface LoadCodeAssistPayload {
|
||||||
allowedTiers?: Array<{ id?: string; isDefault?: boolean }>;
|
allowedTiers?: Array<{ id?: string; isDefault?: boolean }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait helper for onboarding retries
|
|
||||||
*/
|
|
||||||
function wait(ms: number): Promise<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Discover or provision a project for the user
|
* Discover or provision a project for the user
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ if (!isBrowserExtension) {
|
||||||
strict: false,
|
strict: false,
|
||||||
});
|
});
|
||||||
addFormats(ajv);
|
addFormats(ajv);
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
// AJV initialization failed (likely CSP restriction)
|
// AJV initialization failed (likely CSP restriction)
|
||||||
console.warn("AJV validation disabled due to CSP restrictions");
|
console.warn("AJV validation disabled due to CSP restrictions");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -215,7 +215,7 @@ async function abortTest<TApi extends Api>(model: Model<TApi>, options: OptionsF
|
||||||
const stream = agentLoop(userPrompt, context, config, abortController.signal);
|
const stream = agentLoop(userPrompt, context, config, abortController.signal);
|
||||||
|
|
||||||
// Abort after first tool execution
|
// Abort after first tool execution
|
||||||
const abortPromise = (async () => {
|
(async () => {
|
||||||
for await (const event of stream) {
|
for await (const event of stream) {
|
||||||
events.push(event);
|
events.push(event);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ describe("Context overflow error handling", () => {
|
||||||
console.log("Pulling gpt-oss:20b model for Ollama overflow tests...");
|
console.log("Pulling gpt-oss:20b model for Ollama overflow tests...");
|
||||||
try {
|
try {
|
||||||
execSync("ollama pull gpt-oss:20b", { stdio: "inherit" });
|
execSync("ollama pull gpt-oss:20b", { stdio: "inherit" });
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
console.warn("Failed to pull gpt-oss:20b model, tests will be skipped");
|
console.warn("Failed to pull gpt-oss:20b model, tests will be skipped");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -554,7 +554,7 @@ describe("Cross-Provider Handoff Tests", () => {
|
||||||
let successCount = 0;
|
let successCount = 0;
|
||||||
const totalTests = contextTests.length;
|
const totalTests = contextTests.length;
|
||||||
|
|
||||||
for (const { label, context, sourceModel } of contextTests) {
|
for (const { label, context } of contextTests) {
|
||||||
const success = await testProviderHandoff(model, label, context);
|
const success = await testProviderHandoff(model, label, context);
|
||||||
if (success) successCount++;
|
if (success) successCount++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -866,7 +866,7 @@ describe("Generate E2E Tests", () => {
|
||||||
console.log("Pulling gpt-oss:20b model for Ollama tests...");
|
console.log("Pulling gpt-oss:20b model for Ollama tests...");
|
||||||
try {
|
try {
|
||||||
execSync("ollama pull gpt-oss:20b", { stdio: "inherit" });
|
execSync("ollama pull gpt-oss:20b", { stdio: "inherit" });
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
console.warn("Failed to pull gpt-oss:20b model, tests will be skipped");
|
console.warn("Failed to pull gpt-oss:20b model, tests will be skipped");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
import { Type } from "@sinclair/typebox";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { getModel } from "../src/models.js";
|
import { getModel } from "../src/models.js";
|
||||||
import { complete, resolveApiKey } from "../src/stream.js";
|
import { complete, resolveApiKey } from "../src/stream.js";
|
||||||
|
|
@ -18,8 +18,6 @@ const calculateSchema = Type.Object({
|
||||||
expression: Type.String({ description: "The mathematical expression to evaluate" }),
|
expression: Type.String({ description: "The mathematical expression to evaluate" }),
|
||||||
});
|
});
|
||||||
|
|
||||||
type CalculateParams = Static<typeof calculateSchema>;
|
|
||||||
|
|
||||||
const calculateTool: Tool = {
|
const calculateTool: Tool = {
|
||||||
name: "calculate",
|
name: "calculate",
|
||||||
description: "Evaluate mathematical expressions",
|
description: "Evaluate mathematical expressions",
|
||||||
|
|
|
||||||
|
|
@ -420,7 +420,7 @@ export async function compact(
|
||||||
generateTurnPrefixSummary(turnPrefixMessages, model, settings.reserveTokens, apiKey, signal),
|
generateTurnPrefixSummary(turnPrefixMessages, model, settings.reserveTokens, apiKey, signal),
|
||||||
]);
|
]);
|
||||||
// Merge into single summary
|
// Merge into single summary
|
||||||
summary = historyResult + "\n\n---\n\n**Turn Context (split turn):**\n\n" + turnPrefixResult;
|
summary = `${historyResult}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult}`;
|
||||||
} else {
|
} else {
|
||||||
// Just generate history summary
|
// Just generate history summary
|
||||||
summary = await generateSummary(
|
summary = await generateSummary(
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ function escapeHtml(text: string): string {
|
||||||
|
|
||||||
function shortenPath(path: string): string {
|
function shortenPath(path: string): string {
|
||||||
const home = homedir();
|
const home = homedir();
|
||||||
return path.startsWith(home) ? "~" + path.slice(home.length) : path;
|
return path.startsWith(home) ? `~${path.slice(home.length)}` : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceTabs(text: string): string {
|
function replaceTabs(text: string): string {
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ export function isBashExecutionMessage(msg: AppMessage | Message): msg is BashEx
|
||||||
export function bashExecutionToText(msg: BashExecutionMessage): string {
|
export function bashExecutionToText(msg: BashExecutionMessage): string {
|
||||||
let text = `Ran \`${msg.command}\`\n`;
|
let text = `Ran \`${msg.command}\`\n`;
|
||||||
if (msg.output) {
|
if (msg.output) {
|
||||||
text += "```\n" + msg.output + "\n```";
|
text += `\`\`\`\n${msg.output}\n\`\`\``;
|
||||||
} else {
|
} else {
|
||||||
text += "(no output)";
|
text += "(no output)";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,6 @@ const ModelsConfigSchema = Type.Object({
|
||||||
});
|
});
|
||||||
|
|
||||||
type ModelsConfig = Static<typeof ModelsConfigSchema>;
|
type ModelsConfig = Static<typeof ModelsConfigSchema>;
|
||||||
type ProviderConfig = Static<typeof ProviderConfigSchema>;
|
|
||||||
type ModelDefinition = Static<typeof ModelDefinitionSchema>;
|
|
||||||
|
|
||||||
// Custom provider API key mappings (provider name -> apiKey config)
|
// Custom provider API key mappings (provider name -> apiKey config)
|
||||||
const customProviderApiKeys: Map<string, string> = new Map();
|
const customProviderApiKeys: Map<string, string> = new Map();
|
||||||
|
|
|
||||||
|
|
@ -243,7 +243,7 @@ export class SessionManager {
|
||||||
private getSessionDirectory(): string {
|
private getSessionDirectory(): string {
|
||||||
const cwd = process.cwd();
|
const cwd = process.cwd();
|
||||||
// Replace all path separators and colons (for Windows drive letters) with dashes
|
// Replace all path separators and colons (for Windows drive letters) with dashes
|
||||||
const safePath = "--" + cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-") + "--";
|
const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`;
|
||||||
|
|
||||||
const configDir = getAgentDir();
|
const configDir = getAgentDir();
|
||||||
const sessionDir = join(configDir, "sessions", safePath);
|
const sessionDir = join(configDir, "sessions", safePath);
|
||||||
|
|
@ -325,9 +325,9 @@ export class SessionManager {
|
||||||
|
|
||||||
// Write to file only if enabled
|
// Write to file only if enabled
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
for (const memEntry of this.inMemoryEntries.slice(1)) {
|
for (const memEntry of this.inMemoryEntries.slice(1)) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(memEntry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(memEntry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -346,7 +346,7 @@ export class SessionManager {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
// Write to file only if enabled
|
// Write to file only if enabled
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -365,7 +365,7 @@ export class SessionManager {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
// Write to file only if enabled
|
// Write to file only if enabled
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -385,7 +385,7 @@ export class SessionManager {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
// Write to file only if enabled
|
// Write to file only if enabled
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -395,7 +395,7 @@ export class SessionManager {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
// Write to file only if enabled
|
// Write to file only if enabled
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -625,7 +625,7 @@ export class SessionManager {
|
||||||
thinkingLevel: state.thinkingLevel,
|
thinkingLevel: state.thinkingLevel,
|
||||||
branchedFrom: this.sessionFile,
|
branchedFrom: this.sessionFile,
|
||||||
};
|
};
|
||||||
appendFileSync(newSessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(newSessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
|
|
||||||
// Write messages up to and including the branch point (if >= 0)
|
// Write messages up to and including the branch point (if >= 0)
|
||||||
if (branchFromIndex >= 0) {
|
if (branchFromIndex >= 0) {
|
||||||
|
|
@ -636,7 +636,7 @@ export class SessionManager {
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
message,
|
message,
|
||||||
};
|
};
|
||||||
appendFileSync(newSessionFile, JSON.stringify(messageEntry) + "\n");
|
appendFileSync(newSessionFile, `${JSON.stringify(messageEntry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -675,7 +675,7 @@ export class SessionManager {
|
||||||
if (this.enabled) {
|
if (this.enabled) {
|
||||||
// Write to file
|
// Write to file
|
||||||
for (const entry of newEntries) {
|
for (const entry of newEntries) {
|
||||||
appendFileSync(newSessionFile, JSON.stringify(entry) + "\n");
|
appendFileSync(newSessionFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
return newSessionFile;
|
return newSessionFile;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -153,12 +153,12 @@ function loadCommandsFromDir(dir: string, source: "user" | "project", subdir: st
|
||||||
content,
|
content,
|
||||||
source: sourceStr,
|
source: sourceStr,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
// Silently skip files that can't be read
|
// Silently skip files that can't be read
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
// Silently skip directories that can't be read
|
// Silently skip directories that can't be read
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ export const grepTool: AgentTool<typeof grepSchema> = {
|
||||||
let searchStat: Stats;
|
let searchStat: Stats;
|
||||||
try {
|
try {
|
||||||
searchStat = statSync(searchPath);
|
searchStat = statSync(searchPath);
|
||||||
} catch (err) {
|
} catch (_err) {
|
||||||
settle(() => reject(new Error(`Path not found: ${searchPath}`)));
|
settle(() => reject(new Error(`Path not found: ${searchPath}`)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@ export const readTool: AgentTool<typeof readSchema> = {
|
||||||
details = { truncation };
|
details = { truncation };
|
||||||
} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
|
} else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) {
|
||||||
// User specified limit, there's more content, but no truncation
|
// User specified limit, there's more content, but no truncation
|
||||||
const endLineDisplay = startLineDisplay + userLimitedLines - 1;
|
|
||||||
const remaining = allLines.length - (startLine + userLimitedLines);
|
const remaining = allLines.length - (startLine + userLimitedLines);
|
||||||
const nextOffset = startLine + userLimitedLines + 1;
|
const nextOffset = startLine + userLimitedLines + 1;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -261,5 +261,5 @@ export function truncateLine(
|
||||||
if (line.length <= maxChars) {
|
if (line.length <= maxChars) {
|
||||||
return { text: line, wasTruncated: false };
|
return { text: line, wasTruncated: false };
|
||||||
}
|
}
|
||||||
return { text: line.slice(0, maxChars) + "... [truncated]", wasTruncated: true };
|
return { text: `${line.slice(0, maxChars)}... [truncated]`, wasTruncated: true };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,13 +95,13 @@ export class ArminComponent implements Component {
|
||||||
// Clip row to available width before applying color
|
// Clip row to available width before applying color
|
||||||
const clipped = row.slice(0, availableWidth).join("");
|
const clipped = row.slice(0, availableWidth).join("");
|
||||||
const padRight = Math.max(0, width - padding - clipped.length);
|
const padRight = Math.max(0, width - padding - clipped.length);
|
||||||
return " " + theme.fg("accent", clipped) + " ".repeat(padRight);
|
return ` ${theme.fg("accent", clipped)}${" ".repeat(padRight)}`;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add "ARMIN SAYS HI" at the end
|
// Add "ARMIN SAYS HI" at the end
|
||||||
const message = "ARMIN SAYS HI";
|
const message = "ARMIN SAYS HI";
|
||||||
const msgPadRight = Math.max(0, width - padding - message.length);
|
const msgPadRight = Math.max(0, width - padding - message.length);
|
||||||
this.cachedLines.push(" " + theme.fg("accent", message) + " ".repeat(msgPadRight));
|
this.cachedLines.push(` ${theme.fg("accent", message)}${" ".repeat(msgPadRight)}`);
|
||||||
|
|
||||||
this.cachedWidth = width;
|
this.cachedWidth = width;
|
||||||
this.cachedVersion = this.gridVersion;
|
this.cachedVersion = this.gridVersion;
|
||||||
|
|
|
||||||
|
|
@ -132,11 +132,11 @@ export class BashExecutionComponent extends Container {
|
||||||
if (this.expanded) {
|
if (this.expanded) {
|
||||||
// Show all lines
|
// Show all lines
|
||||||
const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n");
|
const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n");
|
||||||
this.contentContainer.addChild(new Text("\n" + displayText, 1, 0));
|
this.contentContainer.addChild(new Text(`\n${displayText}`, 1, 0));
|
||||||
} else {
|
} else {
|
||||||
// Render preview lines, then cap at PREVIEW_LINES visual lines
|
// Render preview lines, then cap at PREVIEW_LINES visual lines
|
||||||
const tempText = new Text(
|
const tempText = new Text(
|
||||||
"\n" + previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n"),
|
`\n${previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n")}`,
|
||||||
1,
|
1,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
@ -170,7 +170,7 @@ export class BashExecutionComponent extends Container {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (statusParts.length > 0) {
|
if (statusParts.length > 0) {
|
||||||
this.contentContainer.addChild(new Text("\n" + statusParts.join("\n"), 1, 0));
|
this.contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -168,17 +168,17 @@ export class FooterComponent implements Component {
|
||||||
// Format token counts (similar to web-ui)
|
// Format token counts (similar to web-ui)
|
||||||
const formatTokens = (count: number): string => {
|
const formatTokens = (count: number): string => {
|
||||||
if (count < 1000) return count.toString();
|
if (count < 1000) return count.toString();
|
||||||
if (count < 10000) return (count / 1000).toFixed(1) + "k";
|
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
||||||
if (count < 1000000) return Math.round(count / 1000) + "k";
|
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
||||||
if (count < 10000000) return (count / 1000000).toFixed(1) + "M";
|
if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;
|
||||||
return Math.round(count / 1000000) + "M";
|
return `${Math.round(count / 1000000)}M`;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Replace home directory with ~
|
// Replace home directory with ~
|
||||||
let pwd = process.cwd();
|
let pwd = process.cwd();
|
||||||
const home = process.env.HOME || process.env.USERPROFILE;
|
const home = process.env.HOME || process.env.USERPROFILE;
|
||||||
if (home && pwd.startsWith(home)) {
|
if (home && pwd.startsWith(home)) {
|
||||||
pwd = "~" + pwd.slice(home.length);
|
pwd = `~${pwd.slice(home.length)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add git branch if available
|
// Add git branch if available
|
||||||
|
|
@ -247,7 +247,7 @@ export class FooterComponent implements Component {
|
||||||
if (statsLeftWidth > width) {
|
if (statsLeftWidth > width) {
|
||||||
// Truncate statsLeft to fit width (no room for right side)
|
// Truncate statsLeft to fit width (no room for right side)
|
||||||
const plainStatsLeft = statsLeft.replace(/\x1b\[[0-9;]*m/g, "");
|
const plainStatsLeft = statsLeft.replace(/\x1b\[[0-9;]*m/g, "");
|
||||||
statsLeft = plainStatsLeft.substring(0, width - 3) + "...";
|
statsLeft = `${plainStatsLeft.substring(0, width - 3)}...`;
|
||||||
statsLeftWidth = visibleWidth(statsLeft);
|
statsLeftWidth = visibleWidth(statsLeft);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ export class HookSelectorComponent extends Container {
|
||||||
if (isSelected) {
|
if (isSelected) {
|
||||||
text = theme.fg("accent", "→ ") + theme.fg("accent", option);
|
text = theme.fg("accent", "→ ") + theme.fg("accent", option);
|
||||||
} else {
|
} else {
|
||||||
text = " " + theme.fg("text", option);
|
text = ` ${theme.fg("text", option)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listContainer.addChild(new Text(text, 1, 0));
|
this.listContainer.addChild(new Text(text, 1, 0));
|
||||||
|
|
|
||||||
|
|
@ -175,12 +175,12 @@ export class ModelSelectorComponent extends Container {
|
||||||
const modelText = `${item.id}`;
|
const modelText = `${item.id}`;
|
||||||
const providerBadge = theme.fg("muted", `[${item.provider}]`);
|
const providerBadge = theme.fg("muted", `[${item.provider}]`);
|
||||||
const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
|
const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
|
||||||
line = prefix + theme.fg("accent", modelText) + " " + providerBadge + checkmark;
|
line = `${prefix + theme.fg("accent", modelText)} ${providerBadge}${checkmark}`;
|
||||||
} else {
|
} else {
|
||||||
const modelText = ` ${item.id}`;
|
const modelText = ` ${item.id}`;
|
||||||
const providerBadge = theme.fg("muted", `[${item.provider}]`);
|
const providerBadge = theme.fg("muted", `[${item.provider}]`);
|
||||||
const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
|
const checkmark = isCurrent ? theme.fg("success", " ✓") : "";
|
||||||
line = modelText + " " + providerBadge + checkmark;
|
line = `${modelText} ${providerBadge}${checkmark}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.listContainer.addChild(new Text(line, 0, 0));
|
this.listContainer.addChild(new Text(line, 0, 0));
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { renderDiff } from "./diff.js";
|
||||||
function shortenPath(path: string): string {
|
function shortenPath(path: string): string {
|
||||||
const home = os.homedir();
|
const home = os.homedir();
|
||||||
if (path.startsWith(home)) {
|
if (path.startsWith(home)) {
|
||||||
return "~" + path.slice(home.length);
|
return `~${path.slice(home.length)}`;
|
||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
@ -269,7 +269,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
text += "\n" + theme.fg("warning", `[${warnings.join(". ")}]`);
|
text += `\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.toolName === "read") {
|
} else if (this.toolName === "read") {
|
||||||
|
|
@ -284,7 +284,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
text = theme.fg("toolTitle", theme.bold("read")) + " " + pathDisplay;
|
text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`;
|
||||||
|
|
||||||
if (this.result) {
|
if (this.result) {
|
||||||
const output = this.getTextOutput();
|
const output = this.getTextOutput();
|
||||||
|
|
@ -377,17 +377,17 @@ export class ToolExecutionComponent extends Container {
|
||||||
if (this.result.isError) {
|
if (this.result.isError) {
|
||||||
const errorText = this.getTextOutput();
|
const errorText = this.getTextOutput();
|
||||||
if (errorText) {
|
if (errorText) {
|
||||||
text += "\n\n" + theme.fg("error", errorText);
|
text += `\n\n${theme.fg("error", errorText)}`;
|
||||||
}
|
}
|
||||||
} else if (this.result.details?.diff) {
|
} else if (this.result.details?.diff) {
|
||||||
text += "\n\n" + renderDiff(this.result.details.diff, { filePath: rawPath });
|
text += `\n\n${renderDiff(this.result.details.diff, { filePath: rawPath })}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.toolName === "ls") {
|
} else if (this.toolName === "ls") {
|
||||||
const path = shortenPath(this.args?.path || ".");
|
const path = shortenPath(this.args?.path || ".");
|
||||||
const limit = this.args?.limit;
|
const limit = this.args?.limit;
|
||||||
|
|
||||||
text = theme.fg("toolTitle", theme.bold("ls")) + " " + theme.fg("accent", path);
|
text = `${theme.fg("toolTitle", theme.bold("ls"))} ${theme.fg("accent", path)}`;
|
||||||
if (limit !== undefined) {
|
if (limit !== undefined) {
|
||||||
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
text += theme.fg("toolOutput", ` (limit ${limit})`);
|
||||||
}
|
}
|
||||||
|
|
@ -400,7 +400,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
const displayLines = lines.slice(0, maxLines);
|
const displayLines = lines.slice(0, maxLines);
|
||||||
const remaining = lines.length - maxLines;
|
const remaining = lines.length - maxLines;
|
||||||
|
|
||||||
text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n");
|
text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`;
|
||||||
if (remaining > 0) {
|
if (remaining > 0) {
|
||||||
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
||||||
}
|
}
|
||||||
|
|
@ -416,7 +416,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
if (truncation?.truncated) {
|
if (truncation?.truncated) {
|
||||||
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
||||||
}
|
}
|
||||||
text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`);
|
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.toolName === "find") {
|
} else if (this.toolName === "find") {
|
||||||
|
|
@ -441,7 +441,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
const displayLines = lines.slice(0, maxLines);
|
const displayLines = lines.slice(0, maxLines);
|
||||||
const remaining = lines.length - maxLines;
|
const remaining = lines.length - maxLines;
|
||||||
|
|
||||||
text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n");
|
text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`;
|
||||||
if (remaining > 0) {
|
if (remaining > 0) {
|
||||||
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
||||||
}
|
}
|
||||||
|
|
@ -457,7 +457,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
if (truncation?.truncated) {
|
if (truncation?.truncated) {
|
||||||
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`);
|
||||||
}
|
}
|
||||||
text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`);
|
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (this.toolName === "grep") {
|
} else if (this.toolName === "grep") {
|
||||||
|
|
@ -486,7 +486,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
const displayLines = lines.slice(0, maxLines);
|
const displayLines = lines.slice(0, maxLines);
|
||||||
const remaining = lines.length - maxLines;
|
const remaining = lines.length - maxLines;
|
||||||
|
|
||||||
text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n");
|
text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`;
|
||||||
if (remaining > 0) {
|
if (remaining > 0) {
|
||||||
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
text += theme.fg("toolOutput", `\n... (${remaining} more lines)`);
|
||||||
}
|
}
|
||||||
|
|
@ -506,7 +506,7 @@ export class ToolExecutionComponent extends Container {
|
||||||
if (linesTruncated) {
|
if (linesTruncated) {
|
||||||
warnings.push("some lines truncated");
|
warnings.push("some lines truncated");
|
||||||
}
|
}
|
||||||
text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`);
|
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -514,10 +514,10 @@ export class ToolExecutionComponent extends Container {
|
||||||
text = theme.fg("toolTitle", theme.bold(this.toolName));
|
text = theme.fg("toolTitle", theme.bold(this.toolName));
|
||||||
|
|
||||||
const content = JSON.stringify(this.args, null, 2);
|
const content = JSON.stringify(this.args, null, 2);
|
||||||
text += "\n\n" + content;
|
text += `\n\n${content}`;
|
||||||
const output = this.getTextOutput();
|
const output = this.getTextOutput();
|
||||||
if (output) {
|
if (output) {
|
||||||
text += "\n" + output;
|
text += `\n${output}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,7 @@ export class InteractiveMode {
|
||||||
"\n" +
|
"\n" +
|
||||||
theme.fg("dim", "drop files") +
|
theme.fg("dim", "drop files") +
|
||||||
theme.fg("muted", " to attach");
|
theme.fg("muted", " to attach");
|
||||||
const header = new Text(logo + "\n" + instructions, 1, 0);
|
const header = new Text(`${logo}\n${instructions}`, 1, 0);
|
||||||
|
|
||||||
// Setup UI layout
|
// Setup UI layout
|
||||||
this.ui.addChild(new Spacer(1));
|
this.ui.addChild(new Spacer(1));
|
||||||
|
|
@ -1340,7 +1340,7 @@ export class InteractiveMode {
|
||||||
if (queuedMessages.length > 0) {
|
if (queuedMessages.length > 0) {
|
||||||
this.pendingMessagesContainer.addChild(new Spacer(1));
|
this.pendingMessagesContainer.addChild(new Spacer(1));
|
||||||
for (const message of queuedMessages) {
|
for (const message of queuedMessages) {
|
||||||
const queuedText = theme.fg("dim", "Queued: " + message);
|
const queuedText = theme.fg("dim", `Queued: ${message}`);
|
||||||
this.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0));
|
this.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1808,7 +1808,7 @@ export class InteractiveMode {
|
||||||
|
|
||||||
this.chatContainer.addChild(new Spacer(1));
|
this.chatContainer.addChild(new Spacer(1));
|
||||||
this.chatContainer.addChild(
|
this.chatContainer.addChild(
|
||||||
new Text(theme.fg("accent", "✓ Context cleared") + "\n" + theme.fg("muted", "Started fresh session"), 1, 1),
|
new Text(`${theme.fg("accent", "✓ Context cleared")}\n${theme.fg("muted", "Started fresh session")}`, 1, 1),
|
||||||
);
|
);
|
||||||
this.ui.requestRender();
|
this.ui.requestRender();
|
||||||
}
|
}
|
||||||
|
|
@ -1840,7 +1840,7 @@ export class InteractiveMode {
|
||||||
|
|
||||||
this.chatContainer.addChild(new Spacer(1));
|
this.chatContainer.addChild(new Spacer(1));
|
||||||
this.chatContainer.addChild(
|
this.chatContainer.addChild(
|
||||||
new Text(theme.fg("accent", "✓ Debug log written") + "\n" + theme.fg("muted", debugLogPath), 1, 1),
|
new Text(`${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1),
|
||||||
);
|
);
|
||||||
this.ui.requestRender();
|
this.ui.requestRender();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -534,7 +534,7 @@ export function initTheme(themeName?: string, enableWatcher: boolean = false): v
|
||||||
if (enableWatcher) {
|
if (enableWatcher) {
|
||||||
startThemeWatcher();
|
startThemeWatcher();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
// Theme is invalid - fall back to dark theme silently
|
// Theme is invalid - fall back to dark theme silently
|
||||||
currentThemeName = "dark";
|
currentThemeName = "dark";
|
||||||
theme = loadTheme("dark");
|
theme = loadTheme("dark");
|
||||||
|
|
@ -598,7 +598,7 @@ function startThemeWatcher(): void {
|
||||||
if (onThemeChangeCallback) {
|
if (onThemeChangeCallback) {
|
||||||
onThemeChangeCallback();
|
onThemeChangeCallback();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
// Ignore errors (file might be in invalid state while being edited)
|
// Ignore errors (file might be in invalid state while being edited)
|
||||||
}
|
}
|
||||||
}, 100);
|
}, 100);
|
||||||
|
|
@ -619,7 +619,7 @@ function startThemeWatcher(): void {
|
||||||
}, 100);
|
}, 100);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
// Ignore errors starting watcher
|
// Ignore errors starting watcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -456,7 +456,7 @@ export class RpcClient {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
this.process!.stdin!.write(JSON.stringify(fullCommand) + "\n");
|
this.process!.stdin!.write(`${JSON.stringify(fullCommand)}\n`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ export function getShellConfig(): { shell: string; args: string[] } {
|
||||||
return cachedShellConfig;
|
return cachedShellConfig;
|
||||||
}
|
}
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Custom shell path not found: ${customShellPath}\n` + `Please update shellPath in ~/.pi/agent/settings.json`,
|
`Custom shell path not found: ${customShellPath}\nPlease update shellPath in ~/.pi/agent/settings.json`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ describe("skills", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should load all skills from fixture directory", () => {
|
it("should load all skills from fixture directory", () => {
|
||||||
const { skills, warnings } = loadSkillsFromDir({
|
const { skills } = loadSkillsFromDir({
|
||||||
dir: fixturesDir,
|
dir: fixturesDir,
|
||||||
source: "test",
|
source: "test",
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -20,26 +20,6 @@ import { createMomTools, setUploadFunction } from "./tools/index.js";
|
||||||
// Hardcoded model for now - TODO: make configurable (issue #63)
|
// Hardcoded model for now - TODO: make configurable (issue #63)
|
||||||
const model = getModel("anthropic", "claude-sonnet-4-5");
|
const model = getModel("anthropic", "claude-sonnet-4-5");
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert Date.now() to Slack timestamp format (seconds.microseconds)
|
|
||||||
* Uses a monotonic counter to ensure ordering even within the same millisecond
|
|
||||||
*/
|
|
||||||
let lastTsMs = 0;
|
|
||||||
let tsCounter = 0;
|
|
||||||
|
|
||||||
function toSlackTs(): string {
|
|
||||||
const now = Date.now();
|
|
||||||
if (now === lastTsMs) {
|
|
||||||
tsCounter++;
|
|
||||||
} else {
|
|
||||||
lastTsMs = now;
|
|
||||||
tsCounter = 0;
|
|
||||||
}
|
|
||||||
const seconds = Math.floor(now / 1000);
|
|
||||||
const micros = (now % 1000) * 1000 + tsCounter;
|
|
||||||
return `${seconds}.${micros.toString().padStart(6, "0")}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PendingMessage {
|
export interface PendingMessage {
|
||||||
userName: string;
|
userName: string;
|
||||||
text: string;
|
text: string;
|
||||||
|
|
@ -85,7 +65,7 @@ function getMemory(channelDir: string): string {
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(workspaceMemoryPath, "utf-8").trim();
|
const content = readFileSync(workspaceMemoryPath, "utf-8").trim();
|
||||||
if (content) {
|
if (content) {
|
||||||
parts.push("### Global Workspace Memory\n" + content);
|
parts.push(`### Global Workspace Memory\n${content}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.logWarning("Failed to read workspace memory", `${workspaceMemoryPath}: ${error}`);
|
log.logWarning("Failed to read workspace memory", `${workspaceMemoryPath}: ${error}`);
|
||||||
|
|
@ -98,7 +78,7 @@ function getMemory(channelDir: string): string {
|
||||||
try {
|
try {
|
||||||
const content = readFileSync(channelMemoryPath, "utf-8").trim();
|
const content = readFileSync(channelMemoryPath, "utf-8").trim();
|
||||||
if (content) {
|
if (content) {
|
||||||
parts.push("### Channel-Specific Memory\n" + content);
|
parts.push(`### Channel-Specific Memory\n${content}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
log.logWarning("Failed to read channel memory", `${channelMemoryPath}: ${error}`);
|
log.logWarning("Failed to read channel memory", `${channelMemoryPath}: ${error}`);
|
||||||
|
|
@ -340,7 +320,7 @@ Each tool requires a "label" parameter (shown to user).
|
||||||
|
|
||||||
function truncate(text: string, maxLen: number): string {
|
function truncate(text: string, maxLen: number): string {
|
||||||
if (text.length <= maxLen) return text;
|
if (text.length <= maxLen) return text;
|
||||||
return text.substring(0, maxLen - 3) + "...";
|
return `${text.substring(0, maxLen - 3)}...`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractToolResultText(result: unknown): string {
|
function extractToolResultText(result: unknown): string {
|
||||||
|
|
@ -530,8 +510,8 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi
|
||||||
let threadMessage = `*${agentEvent.isError ? "✗" : "✓"} ${agentEvent.toolName}*`;
|
let threadMessage = `*${agentEvent.isError ? "✗" : "✓"} ${agentEvent.toolName}*`;
|
||||||
if (label) threadMessage += `: ${label}`;
|
if (label) threadMessage += `: ${label}`;
|
||||||
threadMessage += ` (${duration}s)\n`;
|
threadMessage += ` (${duration}s)\n`;
|
||||||
if (argsFormatted) threadMessage += "```\n" + argsFormatted + "\n```\n";
|
if (argsFormatted) threadMessage += `\`\`\`\n${argsFormatted}\n\`\`\`\n`;
|
||||||
threadMessage += "*Result:*\n```\n" + resultStr + "\n```";
|
threadMessage += `*Result:*\n\`\`\`\n${resultStr}\n\`\`\``;
|
||||||
|
|
||||||
queue.enqueueMessage(threadMessage, "thread", "tool result thread", false);
|
queue.enqueueMessage(threadMessage, "thread", "tool result thread", false);
|
||||||
|
|
||||||
|
|
@ -804,7 +784,7 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi
|
||||||
try {
|
try {
|
||||||
const mainText =
|
const mainText =
|
||||||
finalText.length > SLACK_MAX_LENGTH
|
finalText.length > SLACK_MAX_LENGTH
|
||||||
? finalText.substring(0, SLACK_MAX_LENGTH - 50) + "\n\n_(see thread for full response)_"
|
? `${finalText.substring(0, SLACK_MAX_LENGTH - 50)}\n\n_(see thread for full response)_`
|
||||||
: finalText;
|
: finalText;
|
||||||
await ctx.replaceMessage(mainText);
|
await ctx.replaceMessage(mainText);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export class MomSessionManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -215,7 +215,7 @@ export class MomSessionManager {
|
||||||
};
|
};
|
||||||
|
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,9 +270,9 @@ export class MomSessionManager {
|
||||||
this.pendingEntries = [];
|
this.pendingEntries = [];
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
for (const memEntry of this.inMemoryEntries.slice(1)) {
|
for (const memEntry of this.inMemoryEntries.slice(1)) {
|
||||||
appendFileSync(this.contextFile, JSON.stringify(memEntry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(memEntry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,7 +287,7 @@ export class MomSessionManager {
|
||||||
this.pendingEntries.push(entry);
|
this.pendingEntries.push(entry);
|
||||||
} else {
|
} else {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -302,7 +302,7 @@ export class MomSessionManager {
|
||||||
this.pendingEntries.push(entry);
|
this.pendingEntries.push(entry);
|
||||||
} else {
|
} else {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -318,13 +318,13 @@ export class MomSessionManager {
|
||||||
this.pendingEntries.push(entry);
|
this.pendingEntries.push(entry);
|
||||||
} else {
|
} else {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveCompaction(entry: CompactionEntry): void {
|
saveCompaction(entry: CompactionEntry): void {
|
||||||
this.inMemoryEntries.push(entry);
|
this.inMemoryEntries.push(entry);
|
||||||
appendFileSync(this.contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Load session with compaction support */
|
/** Load session with compaction support */
|
||||||
|
|
@ -587,7 +587,6 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n
|
||||||
if (logMessages.length === 0) return 0;
|
if (logMessages.length === 0) return 0;
|
||||||
|
|
||||||
// Read existing timestamps from context.jsonl
|
// Read existing timestamps from context.jsonl
|
||||||
const existingTs = new Set<string>();
|
|
||||||
if (existsSync(contextFile)) {
|
if (existsSync(contextFile)) {
|
||||||
const contextContent = readFileSync(contextFile, "utf-8");
|
const contextContent = readFileSync(contextFile, "utf-8");
|
||||||
const contextLines = contextContent.trim().split("\n").filter(Boolean);
|
const contextLines = contextContent.trim().split("\n").filter(Boolean);
|
||||||
|
|
@ -656,7 +655,7 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n
|
||||||
mkdirSync(channelDir, { recursive: true });
|
mkdirSync(channelDir, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
appendFileSync(contextFile, JSON.stringify(entry) + "\n");
|
appendFileSync(contextFile, `${JSON.stringify(entry)}\n`);
|
||||||
existingMessages.add(content); // Track to avoid duplicates within this sync
|
existingMessages.add(content); // Track to avoid duplicates within this sync
|
||||||
syncedCount++;
|
syncedCount++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ function formatContext(ctx: LogContext): string {
|
||||||
|
|
||||||
function truncate(text: string, maxLen: number): string {
|
function truncate(text: string, maxLen: number): string {
|
||||||
if (text.length <= maxLen) return text;
|
if (text.length <= maxLen) return text;
|
||||||
return text.substring(0, maxLen) + `\n(truncated at ${maxLen} chars)`;
|
return `${text.substring(0, maxLen)}\n(truncated at ${maxLen} chars)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatToolArgs(args: Record<string, unknown>): string {
|
function formatToolArgs(args: Record<string, unknown>): string {
|
||||||
|
|
@ -200,9 +200,9 @@ export function logUsageSummary(
|
||||||
): string {
|
): string {
|
||||||
const formatTokens = (count: number): string => {
|
const formatTokens = (count: number): string => {
|
||||||
if (count < 1000) return count.toString();
|
if (count < 1000) return count.toString();
|
||||||
if (count < 10000) return (count / 1000).toFixed(1) + "k";
|
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
||||||
if (count < 1000000) return Math.round(count / 1000) + "k";
|
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
||||||
return (count / 1000000).toFixed(1) + "M";
|
return `${(count / 1000000).toFixed(1)}M`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,7 @@ function createSlackContext(event: SlackEvent, slack: SlackBot, state: ChannelSt
|
||||||
|
|
||||||
respond: async (text: string, shouldLog = true) => {
|
respond: async (text: string, shouldLog = true) => {
|
||||||
updatePromise = updatePromise.then(async () => {
|
updatePromise = updatePromise.then(async () => {
|
||||||
accumulatedText = accumulatedText ? accumulatedText + "\n" + text : text;
|
accumulatedText = accumulatedText ? `${accumulatedText}\n${text}` : text;
|
||||||
const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;
|
const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText;
|
||||||
|
|
||||||
if (messageTs) {
|
if (messageTs) {
|
||||||
|
|
|
||||||
|
|
@ -220,7 +220,7 @@ export class SlackBot {
|
||||||
logToFile(channel: string, entry: object): void {
|
logToFile(channel: string, entry: object): void {
|
||||||
const dir = join(this.workingDir, channel);
|
const dir = join(this.workingDir, channel);
|
||||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||||
appendFileSync(join(dir, "log.jsonl"), JSON.stringify(entry) + "\n");
|
appendFileSync(join(dir, "log.jsonl"), `${JSON.stringify(entry)}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ export class ChannelStore {
|
||||||
message.date = date.toISOString();
|
message.date = date.toISOString();
|
||||||
}
|
}
|
||||||
|
|
||||||
const line = JSON.stringify(message) + "\n";
|
const line = `${JSON.stringify(message)}\n`;
|
||||||
await appendFile(logPath, line, "utf-8");
|
await appendFile(logPath, line, "utf-8");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ export const startModel = async (
|
||||||
.replace("{{VLLM_ARGS}}", vllmArgs.join(" "));
|
.replace("{{VLLM_ARGS}}", vllmArgs.join(" "));
|
||||||
|
|
||||||
// Upload customized script
|
// Upload customized script
|
||||||
const result = await sshExec(
|
await sshExec(
|
||||||
pod.ssh,
|
pod.ssh,
|
||||||
`cat > /tmp/model_run_${name}.sh << 'EOF'
|
`cat > /tmp/model_run_${name}.sh << 'EOF'
|
||||||
${scriptContent}
|
${scriptContent}
|
||||||
|
|
@ -341,7 +341,7 @@ WRAPPER
|
||||||
|
|
||||||
if (startupFailed) {
|
if (startupFailed) {
|
||||||
// Model failed to start - clean up and report error
|
// Model failed to start - clean up and report error
|
||||||
console.log("\n" + chalk.red(`✗ Model failed to start: ${failureReason}`));
|
console.log(`\n${chalk.red(`✗ Model failed to start: ${failureReason}`)}`);
|
||||||
|
|
||||||
// Remove the failed model from config
|
// Remove the failed model from config
|
||||||
const config = loadConfig();
|
const config = loadConfig();
|
||||||
|
|
@ -352,7 +352,7 @@ WRAPPER
|
||||||
|
|
||||||
// Provide helpful suggestions based on failure reason
|
// Provide helpful suggestions based on failure reason
|
||||||
if (failureReason.includes("OOM") || failureReason.includes("memory")) {
|
if (failureReason.includes("OOM") || failureReason.includes("memory")) {
|
||||||
console.log("\n" + chalk.bold("Suggestions:"));
|
console.log(`\n${chalk.bold("Suggestions:")}`);
|
||||||
console.log(" • Try reducing GPU memory utilization: --memory 50%");
|
console.log(" • Try reducing GPU memory utilization: --memory 50%");
|
||||||
console.log(" • Use a smaller context window: --context 4k");
|
console.log(" • Use a smaller context window: --context 4k");
|
||||||
console.log(" • Use a quantized version of the model (e.g., FP8)");
|
console.log(" • Use a quantized version of the model (e.g., FP8)");
|
||||||
|
|
@ -360,24 +360,24 @@ WRAPPER
|
||||||
console.log(" • Try a smaller model variant");
|
console.log(" • Try a smaller model variant");
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("\n" + chalk.cyan('Check full logs: pi ssh "tail -100 ~/.vllm_logs/' + name + '.log"'));
|
console.log(`\n${chalk.cyan(`Check full logs: pi ssh "tail -100 ~/.vllm_logs/${name}.log"`)}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
} else if (startupComplete) {
|
} else if (startupComplete) {
|
||||||
// Model started successfully - output connection details
|
// Model started successfully - output connection details
|
||||||
console.log("\n" + chalk.green("✓ Model started successfully!"));
|
console.log(`\n${chalk.green("✓ Model started successfully!")}`);
|
||||||
console.log("\n" + chalk.bold("Connection Details:"));
|
console.log(`\n${chalk.bold("Connection Details:")}`);
|
||||||
console.log(chalk.cyan("─".repeat(50)));
|
console.log(chalk.cyan("─".repeat(50)));
|
||||||
console.log(chalk.white("Base URL: ") + chalk.yellow(`http://${host}:${port}/v1`));
|
console.log(chalk.white("Base URL: ") + chalk.yellow(`http://${host}:${port}/v1`));
|
||||||
console.log(chalk.white("Model: ") + chalk.yellow(modelId));
|
console.log(chalk.white("Model: ") + chalk.yellow(modelId));
|
||||||
console.log(chalk.white("API Key: ") + chalk.yellow(process.env.PI_API_KEY || "(not set)"));
|
console.log(chalk.white("API Key: ") + chalk.yellow(process.env.PI_API_KEY || "(not set)"));
|
||||||
console.log(chalk.cyan("─".repeat(50)));
|
console.log(chalk.cyan("─".repeat(50)));
|
||||||
|
|
||||||
console.log("\n" + chalk.bold("Export for shell:"));
|
console.log(`\n${chalk.bold("Export for shell:")}`);
|
||||||
console.log(chalk.gray(`export OPENAI_BASE_URL="http://${host}:${port}/v1"`));
|
console.log(chalk.gray(`export OPENAI_BASE_URL="http://${host}:${port}/v1"`));
|
||||||
console.log(chalk.gray(`export OPENAI_API_KEY="${process.env.PI_API_KEY || "your-api-key"}"`));
|
console.log(chalk.gray(`export OPENAI_API_KEY="${process.env.PI_API_KEY || "your-api-key"}"`));
|
||||||
console.log(chalk.gray(`export OPENAI_MODEL="${modelId}"`));
|
console.log(chalk.gray(`export OPENAI_MODEL="${modelId}"`));
|
||||||
|
|
||||||
console.log("\n" + chalk.bold("Example usage:"));
|
console.log(`\n${chalk.bold("Example usage:")}`);
|
||||||
console.log(
|
console.log(
|
||||||
chalk.gray(`
|
chalk.gray(`
|
||||||
# Python
|
# Python
|
||||||
|
|
|
||||||
|
|
@ -202,7 +202,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
const isSlashCommand = prefix.startsWith("/") && beforePrefix.trim() === "" && !prefix.slice(1).includes("/");
|
const isSlashCommand = prefix.startsWith("/") && beforePrefix.trim() === "" && !prefix.slice(1).includes("/");
|
||||||
if (isSlashCommand) {
|
if (isSlashCommand) {
|
||||||
// This is a command name completion
|
// This is a command name completion
|
||||||
const newLine = beforePrefix + "/" + item.value + " " + afterCursor;
|
const newLine = `${beforePrefix}/${item.value} ${afterCursor}`;
|
||||||
const newLines = [...lines];
|
const newLines = [...lines];
|
||||||
newLines[cursorLine] = newLine;
|
newLines[cursorLine] = newLine;
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
// Check if we're completing a file attachment (prefix starts with "@")
|
// Check if we're completing a file attachment (prefix starts with "@")
|
||||||
if (prefix.startsWith("@")) {
|
if (prefix.startsWith("@")) {
|
||||||
// This is a file attachment completion
|
// This is a file attachment completion
|
||||||
const newLine = beforePrefix + item.value + " " + afterCursor;
|
const newLine = `${beforePrefix + item.value} ${afterCursor}`;
|
||||||
const newLines = [...lines];
|
const newLines = [...lines];
|
||||||
newLines[cursorLine] = newLine;
|
newLines[cursorLine] = newLine;
|
||||||
|
|
||||||
|
|
@ -299,7 +299,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
if (path.startsWith("~/")) {
|
if (path.startsWith("~/")) {
|
||||||
const expandedPath = join(homedir(), path.slice(2));
|
const expandedPath = join(homedir(), path.slice(2));
|
||||||
// Preserve trailing slash if original path had one
|
// Preserve trailing slash if original path had one
|
||||||
return path.endsWith("/") && !expandedPath.endsWith("/") ? expandedPath + "/" : expandedPath;
|
return path.endsWith("/") && !expandedPath.endsWith("/") ? `${expandedPath}/` : expandedPath;
|
||||||
} else if (path === "~") {
|
} else if (path === "~") {
|
||||||
return homedir();
|
return homedir();
|
||||||
}
|
}
|
||||||
|
|
@ -387,20 +387,20 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
if (isAtPrefix) {
|
if (isAtPrefix) {
|
||||||
const pathWithoutAt = expandedPrefix;
|
const pathWithoutAt = expandedPrefix;
|
||||||
if (pathWithoutAt.endsWith("/")) {
|
if (pathWithoutAt.endsWith("/")) {
|
||||||
relativePath = "@" + pathWithoutAt + name;
|
relativePath = `@${pathWithoutAt}${name}`;
|
||||||
} else if (pathWithoutAt.includes("/")) {
|
} else if (pathWithoutAt.includes("/")) {
|
||||||
if (pathWithoutAt.startsWith("~/")) {
|
if (pathWithoutAt.startsWith("~/")) {
|
||||||
const homeRelativeDir = pathWithoutAt.slice(2); // Remove ~/
|
const homeRelativeDir = pathWithoutAt.slice(2); // Remove ~/
|
||||||
const dir = dirname(homeRelativeDir);
|
const dir = dirname(homeRelativeDir);
|
||||||
relativePath = "@~/" + (dir === "." ? name : join(dir, name));
|
relativePath = `@~/${dir === "." ? name : join(dir, name)}`;
|
||||||
} else {
|
} else {
|
||||||
relativePath = "@" + join(dirname(pathWithoutAt), name);
|
relativePath = `@${join(dirname(pathWithoutAt), name)}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (pathWithoutAt.startsWith("~")) {
|
if (pathWithoutAt.startsWith("~")) {
|
||||||
relativePath = "@~/" + name;
|
relativePath = `@~/${name}`;
|
||||||
} else {
|
} else {
|
||||||
relativePath = "@" + name;
|
relativePath = `@${name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (prefix.endsWith("/")) {
|
} else if (prefix.endsWith("/")) {
|
||||||
|
|
@ -411,14 +411,14 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
if (prefix.startsWith("~/")) {
|
if (prefix.startsWith("~/")) {
|
||||||
const homeRelativeDir = prefix.slice(2); // Remove ~/
|
const homeRelativeDir = prefix.slice(2); // Remove ~/
|
||||||
const dir = dirname(homeRelativeDir);
|
const dir = dirname(homeRelativeDir);
|
||||||
relativePath = "~/" + (dir === "." ? name : join(dir, name));
|
relativePath = `~/${dir === "." ? name : join(dir, name)}`;
|
||||||
} else if (prefix.startsWith("/")) {
|
} else if (prefix.startsWith("/")) {
|
||||||
// Absolute path - construct properly
|
// Absolute path - construct properly
|
||||||
const dir = dirname(prefix);
|
const dir = dirname(prefix);
|
||||||
if (dir === "/") {
|
if (dir === "/") {
|
||||||
relativePath = "/" + name;
|
relativePath = `/${name}`;
|
||||||
} else {
|
} else {
|
||||||
relativePath = dir + "/" + name;
|
relativePath = `${dir}/${name}`;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
relativePath = join(dirname(prefix), name);
|
relativePath = join(dirname(prefix), name);
|
||||||
|
|
@ -426,14 +426,14 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
} else {
|
} else {
|
||||||
// For standalone entries, preserve ~/ if original prefix was ~/
|
// For standalone entries, preserve ~/ if original prefix was ~/
|
||||||
if (prefix.startsWith("~")) {
|
if (prefix.startsWith("~")) {
|
||||||
relativePath = "~/" + name;
|
relativePath = `~/${name}`;
|
||||||
} else {
|
} else {
|
||||||
relativePath = name;
|
relativePath = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
value: isDirectory ? relativePath + "/" : relativePath,
|
value: isDirectory ? `${relativePath}/` : relativePath,
|
||||||
label: name,
|
label: name,
|
||||||
description: isDirectory ? "directory" : "file",
|
description: isDirectory ? "directory" : "file",
|
||||||
});
|
});
|
||||||
|
|
@ -449,7 +449,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
return suggestions;
|
return suggestions;
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
// Directory doesn't exist or not accessible
|
// Directory doesn't exist or not accessible
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -509,7 +509,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider {
|
||||||
const entryName = basename(pathWithoutSlash);
|
const entryName = basename(pathWithoutSlash);
|
||||||
|
|
||||||
suggestions.push({
|
suggestions.push({
|
||||||
value: "@" + entryPath,
|
value: `@${entryPath}`,
|
||||||
label: entryName + (isDirectory ? "/" : ""),
|
label: entryName + (isDirectory ? "/" : ""),
|
||||||
description: pathWithoutSlash,
|
description: pathWithoutSlash,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1310,10 +1310,6 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/
|
||||||
// Always create new SelectList to ensure update
|
// Always create new SelectList to ensure update
|
||||||
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList);
|
||||||
} else {
|
} else {
|
||||||
// No matches - check if we're still in a valid context before cancelling
|
|
||||||
const currentLine = this.state.lines[this.state.cursorLine] || "";
|
|
||||||
const textBeforeCursor = currentLine.slice(0, this.state.cursorCol);
|
|
||||||
|
|
||||||
this.cancelAutocomplete();
|
this.cancelAutocomplete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ export class Markdown implements Component {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case "heading": {
|
case "heading": {
|
||||||
const headingLevel = token.depth;
|
const headingLevel = token.depth;
|
||||||
const headingPrefix = "#".repeat(headingLevel) + " ";
|
const headingPrefix = `${"#".repeat(headingLevel)} `;
|
||||||
const headingText = this.renderInlineTokens(token.tokens || []);
|
const headingText = this.renderInlineTokens(token.tokens || []);
|
||||||
let styledHeading: string;
|
let styledHeading: string;
|
||||||
if (headingLevel === 1) {
|
if (headingLevel === 1) {
|
||||||
|
|
@ -263,17 +263,17 @@ export class Markdown implements Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
case "code": {
|
case "code": {
|
||||||
lines.push(this.theme.codeBlockBorder("```" + (token.lang || "")));
|
lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`));
|
||||||
if (this.theme.highlightCode) {
|
if (this.theme.highlightCode) {
|
||||||
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
||||||
for (const hlLine of highlightedLines) {
|
for (const hlLine of highlightedLines) {
|
||||||
lines.push(" " + hlLine);
|
lines.push(` ${hlLine}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Split code by newlines and style each line
|
// Split code by newlines and style each line
|
||||||
const codeLines = token.text.split("\n");
|
const codeLines = token.text.split("\n");
|
||||||
for (const codeLine of codeLines) {
|
for (const codeLine of codeLines) {
|
||||||
lines.push(" " + this.theme.codeBlock(codeLine));
|
lines.push(` ${this.theme.codeBlock(codeLine)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.push(this.theme.codeBlockBorder("```"));
|
lines.push(this.theme.codeBlockBorder("```"));
|
||||||
|
|
@ -443,7 +443,7 @@ export class Markdown implements Component {
|
||||||
lines.push(line);
|
lines.push(line);
|
||||||
} else {
|
} else {
|
||||||
// Regular content - add parent indent + 2 spaces for continuation
|
// Regular content - add parent indent + 2 spaces for continuation
|
||||||
lines.push(indent + " " + line);
|
lines.push(`${indent} ${line}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -478,16 +478,16 @@ export class Markdown implements Component {
|
||||||
lines.push(text);
|
lines.push(text);
|
||||||
} else if (token.type === "code") {
|
} else if (token.type === "code") {
|
||||||
// Code block in list item
|
// Code block in list item
|
||||||
lines.push(this.theme.codeBlockBorder("```" + (token.lang || "")));
|
lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`));
|
||||||
if (this.theme.highlightCode) {
|
if (this.theme.highlightCode) {
|
||||||
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
const highlightedLines = this.theme.highlightCode(token.text, token.lang);
|
||||||
for (const hlLine of highlightedLines) {
|
for (const hlLine of highlightedLines) {
|
||||||
lines.push(" " + hlLine);
|
lines.push(` ${hlLine}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const codeLines = token.text.split("\n");
|
const codeLines = token.text.split("\n");
|
||||||
for (const codeLine of codeLines) {
|
for (const codeLine of codeLines) {
|
||||||
lines.push(" " + this.theme.codeBlock(codeLine));
|
lines.push(` ${this.theme.codeBlock(codeLine)}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lines.push(this.theme.codeBlockBorder("```"));
|
lines.push(this.theme.codeBlockBorder("```"));
|
||||||
|
|
@ -587,7 +587,7 @@ export class Markdown implements Component {
|
||||||
|
|
||||||
// Render top border
|
// Render top border
|
||||||
const topBorderCells = columnWidths.map((w) => "─".repeat(w));
|
const topBorderCells = columnWidths.map((w) => "─".repeat(w));
|
||||||
lines.push("┌─" + topBorderCells.join("─┬─") + "─┐");
|
lines.push(`┌─${topBorderCells.join("─┬─")}─┐`);
|
||||||
|
|
||||||
// Render header with wrapping
|
// Render header with wrapping
|
||||||
const headerCellLines: string[][] = token.header.map((cell, i) => {
|
const headerCellLines: string[][] = token.header.map((cell, i) => {
|
||||||
|
|
@ -602,12 +602,12 @@ export class Markdown implements Component {
|
||||||
const padded = text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text)));
|
const padded = text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text)));
|
||||||
return this.theme.bold(padded);
|
return this.theme.bold(padded);
|
||||||
});
|
});
|
||||||
lines.push("│ " + rowParts.join(" │ ") + " │");
|
lines.push(`│ ${rowParts.join(" │ ")} │`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render separator
|
// Render separator
|
||||||
const separatorCells = columnWidths.map((w) => "─".repeat(w));
|
const separatorCells = columnWidths.map((w) => "─".repeat(w));
|
||||||
lines.push("├─" + separatorCells.join("─┼─") + "─┤");
|
lines.push(`├─${separatorCells.join("─┼─")}─┤`);
|
||||||
|
|
||||||
// Render rows with wrapping
|
// Render rows with wrapping
|
||||||
for (const row of token.rows) {
|
for (const row of token.rows) {
|
||||||
|
|
@ -622,13 +622,13 @@ export class Markdown implements Component {
|
||||||
const text = cellLines[lineIdx] || "";
|
const text = cellLines[lineIdx] || "";
|
||||||
return text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text)));
|
return text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text)));
|
||||||
});
|
});
|
||||||
lines.push("│ " + rowParts.join(" │ ") + " │");
|
lines.push(`│ ${rowParts.join(" │ ")} │`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render bottom border
|
// Render bottom border
|
||||||
const bottomBorderCells = columnWidths.map((w) => "─".repeat(w));
|
const bottomBorderCells = columnWidths.map((w) => "─".repeat(w));
|
||||||
lines.push("└─" + bottomBorderCells.join("─┴─") + "─┘");
|
lines.push(`└─${bottomBorderCells.join("─┴─")}─┘`);
|
||||||
|
|
||||||
lines.push(""); // Add spacing after table
|
lines.push(""); // Add spacing after table
|
||||||
return lines;
|
return lines;
|
||||||
|
|
|
||||||
|
|
@ -90,16 +90,16 @@ export class SelectList implements Component {
|
||||||
if (remainingWidth > 10) {
|
if (remainingWidth > 10) {
|
||||||
const truncatedDesc = truncateToWidth(item.description, remainingWidth, "");
|
const truncatedDesc = truncateToWidth(item.description, remainingWidth, "");
|
||||||
// Apply selectedText to entire line content
|
// Apply selectedText to entire line content
|
||||||
line = this.theme.selectedText("→ " + truncatedValue + spacing + truncatedDesc);
|
line = this.theme.selectedText(`→ ${truncatedValue}${spacing}${truncatedDesc}`);
|
||||||
} else {
|
} else {
|
||||||
// Not enough space for description
|
// Not enough space for description
|
||||||
const maxWidth = width - prefixWidth - 2;
|
const maxWidth = width - prefixWidth - 2;
|
||||||
line = this.theme.selectedText("→ " + truncateToWidth(displayValue, maxWidth, ""));
|
line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No description or not enough width
|
// No description or not enough width
|
||||||
const maxWidth = width - prefixWidth - 2;
|
const maxWidth = width - prefixWidth - 2;
|
||||||
line = this.theme.selectedText("→ " + truncateToWidth(displayValue, maxWidth, ""));
|
line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const displayValue = item.label || item.value;
|
const displayValue = item.label || item.value;
|
||||||
|
|
|
||||||
|
|
@ -565,5 +565,5 @@ export function truncateToWidth(text: string, maxWidth: number, ellipsis: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add reset code before ellipsis to prevent styling leaking into it
|
// Add reset code before ellipsis to prevent styling leaking into it
|
||||||
return result + "\x1b[0m" + ellipsis;
|
return `${result}\x1b[0m${ellipsis}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ console.log("Loading image from:", testImagePath);
|
||||||
let imageBuffer: Buffer;
|
let imageBuffer: Buffer;
|
||||||
try {
|
try {
|
||||||
imageBuffer = readFileSync(testImagePath);
|
imageBuffer = readFileSync(testImagePath);
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
console.error(`Failed to load image: ${testImagePath}`);
|
console.error(`Failed to load image: ${testImagePath}`);
|
||||||
console.error("Usage: npx tsx test/image-test.ts [path-to-image.png]");
|
console.error("Usage: npx tsx test/image-test.ts [path-to-image.png]");
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ describe("TruncatedText component", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("preserves ANSI codes in output and pads correctly", () => {
|
it("preserves ANSI codes in output and pads correctly", () => {
|
||||||
const styledText = chalk.red("Hello") + " " + chalk.blue("world");
|
const styledText = `${chalk.red("Hello")} ${chalk.blue("world")}`;
|
||||||
const text = new TruncatedText(styledText, 1, 0);
|
const text = new TruncatedText(styledText, 1, 0);
|
||||||
const lines = text.render(40);
|
const lines = text.render(40);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ function defaultMessageTransformer(messages: AppMessage[]): Message[] {
|
||||||
.map((m) => {
|
.map((m) => {
|
||||||
if (m.role === "user") {
|
if (m.role === "user") {
|
||||||
// Strip attachments field (app-specific)
|
// Strip attachments field (app-specific)
|
||||||
|
|
||||||
|
// biome-ignore lint/correctness/noUnusedVariables: fine here
|
||||||
const { attachments, ...rest } = m as any;
|
const { attachments, ...rest } = m as any;
|
||||||
return rest as Message;
|
return rest as Message;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,6 @@ export class AttachmentTile extends LitElement {
|
||||||
const hasPreview = !!this.attachment.preview;
|
const hasPreview = !!this.attachment.preview;
|
||||||
const isImage = this.attachment.type === "image";
|
const isImage = this.attachment.type === "image";
|
||||||
const isPdf = this.attachment.mimeType === "application/pdf";
|
const isPdf = this.attachment.mimeType === "application/pdf";
|
||||||
const isDocx =
|
|
||||||
this.attachment.mimeType?.includes("wordprocessingml") ||
|
|
||||||
this.attachment.fileName.toLowerCase().endsWith(".docx");
|
|
||||||
const isPptx =
|
|
||||||
this.attachment.mimeType?.includes("presentationml") ||
|
|
||||||
this.attachment.fileName.toLowerCase().endsWith(".pptx");
|
|
||||||
const isExcel =
|
const isExcel =
|
||||||
this.attachment.mimeType?.includes("spreadsheetml") ||
|
this.attachment.mimeType?.includes("spreadsheetml") ||
|
||||||
this.attachment.fileName.toLowerCase().endsWith(".xlsx") ||
|
this.attachment.fileName.toLowerCase().endsWith(".xlsx") ||
|
||||||
|
|
@ -84,7 +78,7 @@ export class AttachmentTile extends LitElement {
|
||||||
<div class="text-[10px] text-center truncate w-full">
|
<div class="text-[10px] text-center truncate w-full">
|
||||||
${
|
${
|
||||||
this.attachment.fileName.length > 10
|
this.attachment.fileName.length > 10
|
||||||
? this.attachment.fileName.substring(0, 8) + "..."
|
? `${this.attachment.fileName.substring(0, 8)}...`
|
||||||
: this.attachment.fileName
|
: this.attachment.fileName
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { action, filename, content, mimeType } = message;
|
const { action, filename, content } = message;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
|
|
||||||
|
|
@ -40,18 +40,18 @@ export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider {
|
||||||
|
|
||||||
(window as any).readTextAttachment = (attachmentId: string) => {
|
(window as any).readTextAttachment = (attachmentId: string) => {
|
||||||
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
||||||
if (!a) throw new Error("Attachment not found: " + attachmentId);
|
if (!a) throw new Error(`Attachment not found: ${attachmentId}`);
|
||||||
if (a.extractedText) return a.extractedText;
|
if (a.extractedText) return a.extractedText;
|
||||||
try {
|
try {
|
||||||
return atob(a.content);
|
return atob(a.content);
|
||||||
} catch {
|
} catch {
|
||||||
throw new Error("Failed to decode text content for: " + attachmentId);
|
throw new Error(`Failed to decode text content for: ${attachmentId}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
(window as any).readBinaryAttachment = (attachmentId: string) => {
|
(window as any).readBinaryAttachment = (attachmentId: string) => {
|
||||||
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId);
|
||||||
if (!a) throw new Error("Attachment not found: " + attachmentId);
|
if (!a) throw new Error(`Attachment not found: ${attachmentId}`);
|
||||||
const bin = atob(a.content);
|
const bin = atob(a.content);
|
||||||
const bytes = new Uint8Array(bin.length);
|
const bytes = new Uint8Array(bin.length);
|
||||||
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
||||||
|
|
|
||||||
|
|
@ -92,8 +92,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
||||||
// Error handlers - track errors but don't log them
|
// Error handlers - track errors but don't log them
|
||||||
// (they'll be shown via execution-error message)
|
// (they'll be shown via execution-error message)
|
||||||
window.addEventListener("error", (e) => {
|
window.addEventListener("error", (e) => {
|
||||||
const text =
|
const text = `${e.error?.stack || e.message || String(e)} at line ${e.lineno || "?"}:${e.colno || "?"}`;
|
||||||
(e.error?.stack || e.message || String(e)) + " at line " + (e.lineno || "?") + ":" + (e.colno || "?");
|
|
||||||
|
|
||||||
lastError = {
|
lastError = {
|
||||||
message: e.error?.message || e.message || String(e),
|
message: e.error?.message || e.message || String(e),
|
||||||
|
|
@ -102,7 +101,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("unhandledrejection", (e) => {
|
window.addEventListener("unhandledrejection", (e) => {
|
||||||
const text = "Unhandled promise rejection: " + (e.reason?.message || e.reason || "Unknown error");
|
const text = `Unhandled promise rejection: ${e.reason?.message || e.reason || "Unknown error"}`;
|
||||||
|
|
||||||
lastError = {
|
lastError = {
|
||||||
message: e.reason?.message || String(e.reason) || "Unhandled promise rejection",
|
message: e.reason?.message || String(e.reason) || "Unhandled promise rejection",
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ export class ProvidersModelsTab extends SettingsTab {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.providerStatus.set(provider.id, { modelCount: models.length, status: "connected" });
|
this.providerStatus.set(provider.id, { modelCount: models.length, status: "connected" });
|
||||||
} catch (error) {
|
} catch (_error) {
|
||||||
this.providerStatus.set(provider.id, { modelCount: 0, status: "disconnected" });
|
this.providerStatus.set(provider.id, { modelCount: 0, status: "disconnected" });
|
||||||
}
|
}
|
||||||
this.requestUpdate();
|
this.requestUpdate();
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ export class IndexedDBStorageBackend implements StorageBackend {
|
||||||
|
|
||||||
if (prefix) {
|
if (prefix) {
|
||||||
// Use IDBKeyRange for efficient prefix filtering
|
// Use IDBKeyRange for efficient prefix filtering
|
||||||
const range = IDBKeyRange.bound(prefix, prefix + "\uffff", false, false);
|
const range = IDBKeyRange.bound(prefix, `${prefix}\uffff`, false, false);
|
||||||
const keys = await this.promisifyRequest(store.getAllKeys(range));
|
const keys = await this.promisifyRequest(store.getAllKeys(range));
|
||||||
return keys.map((k) => String(k));
|
return keys.map((k) => String(k));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ export function createExtractDocumentTool(): AgentTool<typeof extractDocumentSch
|
||||||
const urlParts = url.split("/");
|
const urlParts = url.split("/");
|
||||||
let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
|
let fileName = urlParts[urlParts.length - 1]?.split("?")[0] || "document";
|
||||||
if (url.startsWith("https://arxiv.org/")) {
|
if (url.startsWith("https://arxiv.org/")) {
|
||||||
fileName = fileName + ".pdf";
|
fileName = `${fileName}.pdf`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use loadAttachment to process the document
|
// Use loadAttachment to process the document
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ export async function executeJavaScript(
|
||||||
// Add console output - result.console contains { type: string, text: string } from sandbox.js
|
// Add console output - result.console contains { type: string, text: string } from sandbox.js
|
||||||
if (result.console && result.console.length > 0) {
|
if (result.console && result.console.length > 0) {
|
||||||
for (const entry of result.console) {
|
for (const entry of result.console) {
|
||||||
output += entry.text + "\n";
|
output += `${entry.text}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -230,7 +230,7 @@ export const javascriptReplRenderer: ToolRenderer<JavaScriptReplParams, JavaScri
|
||||||
if (isTextBased && f.contentBase64) {
|
if (isTextBased && f.contentBase64) {
|
||||||
try {
|
try {
|
||||||
extractedText = atob(f.contentBase64);
|
extractedText = atob(f.contentBase64);
|
||||||
} catch (e) {
|
} catch (_e) {
|
||||||
console.warn("Failed to decode base64 content for", f.fileName);
|
console.warn("Failed to decode base64 content for", f.fileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -343,7 +343,7 @@ function extractTextFromElement(element: any): string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (tableTexts.length > 0) {
|
if (tableTexts.length > 0) {
|
||||||
text = "\n[Table]\n" + tableTexts.join("\n") + "\n[/Table]\n";
|
text = `\n[Table]\n${tableTexts.join("\n")}\n[/Table]\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +397,7 @@ async function processPptx(arrayBuffer: ArrayBuffer, fileName: string): Promise<
|
||||||
.filter((t) => t.trim());
|
.filter((t) => t.trim());
|
||||||
|
|
||||||
if (slideTexts.length > 0) {
|
if (slideTexts.length > 0) {
|
||||||
extractedText += "\n" + slideTexts.join("\n");
|
extractedText += `\n${slideTexts.join("\n")}`;
|
||||||
}
|
}
|
||||||
extractedText += "\n</slide>";
|
extractedText += "\n</slide>";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,6 @@ export function formatUsage(usage: Usage) {
|
||||||
|
|
||||||
export function formatTokenCount(count: number): string {
|
export function formatTokenCount(count: number): string {
|
||||||
if (count < 1000) return count.toString();
|
if (count < 1000) return count.toString();
|
||||||
if (count < 10000) return (count / 1000).toFixed(1) + "k";
|
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
||||||
return Math.round(count / 1000) + "k";
|
return `${Math.round(count / 1000)}k`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue