diff --git a/packages/coding-agent/src/modes/components/status-line.ts b/packages/coding-agent/src/modes/components/status-line.ts index ae8c6a03d..066cd5fbd 100644 --- a/packages/coding-agent/src/modes/components/status-line.ts +++ b/packages/coding-agent/src/modes/components/status-line.ts @@ -402,7 +402,7 @@ export class StatusLineComponent implements Component { * API. */ getCachedContextBreakdown(): { usedTokens: number; contextWindow: number } { - const messages = this.session.messages; + const messages = this.session.messages ?? []; const contextWindow = this.session.model?.contextWindow ?? 0; // 1) Non-message tokens (system prompt + tools + skills). Refresh only @@ -446,9 +446,9 @@ export class StatusLineComponent implements Component { * be cryptographically unique — only stable for the same inputs. */ #computeNonMessageInputsKey(): string { - const sp = this.session.systemPrompt; - const tools = this.session.agent.state.tools; - const skills = this.session.skills; + const sp = this.session.systemPrompt ?? []; + const tools = this.session.agent?.state?.tools ?? []; + const skills = this.session.skills ?? []; const modelId = this.session.model?.id ?? ""; return `${modelId}|${sp.length}:${sp[0]?.length ?? 0}|${tools.length}|${skills.length}`; } diff --git a/packages/coding-agent/src/modes/utils/context-usage.ts b/packages/coding-agent/src/modes/utils/context-usage.ts index 43cc99bde..2d9fb0abc 100644 --- a/packages/coding-agent/src/modes/utils/context-usage.ts +++ b/packages/coding-agent/src/modes/utils/context-usage.ts @@ -75,10 +75,11 @@ export function estimateToolSchemaTokens( * messages walked incrementally as new entries append. */ export function computeNonMessageTokens(session: AgentSession): number { - const skillsTokens = estimateSkillsTokens(session.skills); - const toolsTokens = estimateToolSchemaTokens(session.agent.state.tools); - const systemContextTokens = countTokens(session.systemPrompt.slice(1)); - const systemPromptTokens = Math.max(0, countTokens(session.systemPrompt[0] ?? "") - skillsTokens); + const skillsTokens = estimateSkillsTokens(session.skills ?? []); + const toolsTokens = estimateToolSchemaTokens(session.agent?.state?.tools ?? []); + const systemPromptParts = session.systemPrompt ?? []; + const systemContextTokens = countTokens(systemPromptParts.slice(1)); + const systemPromptTokens = Math.max(0, countTokens(systemPromptParts[0] ?? "") - skillsTokens); return systemPromptTokens + systemContextTokens + toolsTokens + skillsTokens; } diff --git a/packages/coding-agent/src/task/executor.ts b/packages/coding-agent/src/task/executor.ts index 93985ca3e..b4c369cbc 100644 --- a/packages/coding-agent/src/task/executor.ts +++ b/packages/coding-agent/src/task/executor.ts @@ -17,7 +17,7 @@ import { SETTINGS_SCHEMA, type SettingPath } from "../config/settings-schema"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { runExtensionCompact, runExtensionSetModel } from "../extensibility/extensions/compact-handler"; import { getSessionSlashCommands } from "../extensibility/extensions/get-commands-handler"; -import { type Skill, buildSkillPromptMessage } from "../extensibility/skills"; +import { buildSkillPromptMessage, type Skill } from "../extensibility/skills"; import type { HindsightSessionState } from "../hindsight/state"; import type { LocalProtocolOptions } from "../internal-urls"; import { callTool } from "../mcp/client"; diff --git a/packages/coding-agent/src/task/index.ts b/packages/coding-agent/src/task/index.ts index 56ab087fa..8c367507f 100644 --- a/packages/coding-agent/src/task/index.ts +++ b/packages/coding-agent/src/task/index.ts @@ -831,11 +831,12 @@ export class TaskTool implements AgentTool 0 - ? agent.autoloadSkills - .map(name => availableSkills.find(s => s.name === name)) - .filter((s): s is NonNullable => s !== undefined) - : []; + const resolvedAutoloadSkills = + agent.autoloadSkills?.length && availableSkills.length > 0 + ? agent.autoloadSkills + .map(name => availableSkills.find(s => s.name === name)) + .filter((s): s is NonNullable => s !== undefined) + : []; const contextFiles = this.session.contextFiles?.filter( file => path.basename(file.path).toLowerCase() !== "agents.md", ); diff --git a/packages/coding-agent/test/status-line-context-cache.test.ts b/packages/coding-agent/test/status-line-context-cache.test.ts index b62429445..45bfa275e 100644 --- a/packages/coding-agent/test/status-line-context-cache.test.ts +++ b/packages/coding-agent/test/status-line-context-cache.test.ts @@ -15,19 +15,19 @@ * (messages.length shrinks) resets the cache. */ import { afterAll, beforeAll, describe, expect, it } from "bun:test"; -import { _resetSettingsForTest, Settings } from "../src/config/settings"; +import { resetSettingsForTest, Settings } from "../src/config/settings"; import { StatusLineComponent } from "../src/modes/components/status-line"; import { initTheme } from "../src/modes/theme/theme"; import type { AgentSession } from "../src/session/agent-session"; beforeAll(async () => { - _resetSettingsForTest(); + resetSettingsForTest(); await Settings.init({ inMemory: true }); await initTheme(); }); afterAll(() => { - _resetSettingsForTest(); + resetSettingsForTest(); }); function makeSession(opts: { diff --git a/packages/coding-agent/test/task/autoload-skills.test.ts b/packages/coding-agent/test/task/autoload-skills.test.ts index b72f9b41b..aaa9a9b20 100644 --- a/packages/coding-agent/test/task/autoload-skills.test.ts +++ b/packages/coding-agent/test/task/autoload-skills.test.ts @@ -1,14 +1,14 @@ import { afterEach, describe, expect, it, vi } from "bun:test"; import { Settings } from "../../src/config/settings"; import type { Skill } from "../../src/extensibility/skills"; +import * as skillsModule from "../../src/extensibility/skills"; import type { CreateAgentSessionResult } from "../../src/sdk"; import * as sdkModule from "../../src/sdk"; -import { SKILL_PROMPT_MESSAGE_TYPE } from "../../src/session/messages"; import type { AgentSession, AgentSessionEvent, PromptOptions } from "../../src/session/agent-session"; +import { SKILL_PROMPT_MESSAGE_TYPE } from "../../src/session/messages"; import { runSubprocess } from "../../src/task/executor"; import type { AgentDefinition } from "../../src/task/types"; import { EventBus } from "../../src/utils/event-bus"; -import * as skillsModule from "../../src/extensibility/skills"; // ── Helpers ──────────────────────────────────────────────────────────────── @@ -110,11 +110,23 @@ describe("autoloadSkills in executor", () => { vi.spyOn(sdkModule, "createAgentSession").mockResolvedValue(createSessionResult(session)); const mockSkills: Skill[] = [ - { name: "user-created-skill-a", description: "Skill A", filePath: "/skills/user-created-skill-a/SKILL.md", baseDir: "/skills/user-created-skill-a", source: "user" }, - { name: "user-created-skill-b", description: "Skill B", filePath: "/skills/user-created-skill-b/SKILL.md", baseDir: "/skills/user-created-skill-b", source: "user" }, + { + name: "user-created-skill-a", + description: "Skill A", + filePath: "/skills/user-created-skill-a/SKILL.md", + baseDir: "/skills/user-created-skill-a", + source: "user", + }, + { + name: "user-created-skill-b", + description: "Skill B", + filePath: "/skills/user-created-skill-b/SKILL.md", + baseDir: "/skills/user-created-skill-b", + source: "user", + }, ]; - vi.spyOn(skillsModule, "buildSkillPromptMessage").mockImplementation(async (skill) => ({ + vi.spyOn(skillsModule, "buildSkillPromptMessage").mockImplementation(async skill => ({ message: `Content of ${skill.name}\n\n---\n\nSkill: ${skill.filePath}`, details: { name: skill.name, diff --git a/packages/coding-agent/test/tools/bash-command-fixup.test.ts b/packages/coding-agent/test/tools/bash-command-fixup.test.ts index 4ea44ed4c..7cfdeee1a 100644 --- a/packages/coding-agent/test/tools/bash-command-fixup.test.ts +++ b/packages/coding-agent/test/tools/bash-command-fixup.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "bun:test"; -import { applyBashFixups, type BashFixupResult, formatBashFixupNotice } from "../../src/tools/bash-command-fixup"; +import { applyBashFixups, type BashFixupResult } from "../../src/tools/bash-command-fixup"; function fixup(command: string): BashFixupResult { return applyBashFixups(command); @@ -123,22 +123,3 @@ describe("applyBashFixups — preserves semantics-bearing pipelines", () => { }); } }); - -describe("formatBashFixupNotice", () => { - it("returns undefined when nothing was stripped", () => { - expect(formatBashFixupNotice([])).toBeUndefined(); - }); - - it("embeds a single stripped segment in the notice", () => { - const notice = formatBashFixupNotice(["| head -5"]); - expect(notice).toContain("`| head -5`"); - expect(notice).toContain("stderr is already merged"); - }); - - it("joins multiple stripped segments with commas", () => { - const notice = formatBashFixupNotice(["| tail -3", "2>&1"]); - expect(notice).toContain("`| tail -3`"); - expect(notice).toContain("`2>&1`"); - expect(notice).toMatch(/`\| tail -3`,\s*`2>&1`/); - }); -}); diff --git a/packages/coding-agent/test/tools/bash-interceptor.test.ts b/packages/coding-agent/test/tools/bash-interceptor.test.ts index e3179c081..1de9464c1 100644 --- a/packages/coding-agent/test/tools/bash-interceptor.test.ts +++ b/packages/coding-agent/test/tools/bash-interceptor.test.ts @@ -95,7 +95,7 @@ describe("BashTool head/tail stripping", () => { return new BashTool(session); } - it("executes the stripped command and surfaces a notice", async () => { + it("executes the stripped command", async () => { const tool = createBashToolWithStrip(true); // `seq 1 100 | head -3` would emit "1\n2\n3"; stripped, it emits 1..100. // We assert on the tail of the output rather than head, so a successful @@ -105,8 +105,6 @@ describe("BashTool head/tail stripping", () => { } as AgentToolContext); const text = result.content.find(b => b.type === "text")?.text ?? ""; expect(text).toContain("100"); - expect(text).toContain(""); - expect(text).toContain("Stripped redundant `| head -3`"); }); it("does not strip when the setting is disabled", async () => {