diff --git a/AGENTS.md b/AGENTS.md index 424ef2d80..3003c81e3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -20,8 +20,8 @@ The UI is extracted from the desktop shell (CodeNomad-style): the desktop app is ## Build, Test, and Development Commands - Install: `bun install` + `bun run installRuntime` (first time). - Dev: `bun run dev` (HMR). Inspect: `bun run dev:inspect`; Linux: `bun run dev:linux`. -- Preview: `pnpm start`. -- Type check: `bun run typecheck` (or `typecheck:node` / `typecheck:web`). Uses `tsgo` (native TS preview). +- Preview: `bun run start`. +- Type check: `bun run typecheck` (or `typecheck:node` / `typecheck:web`). Uses TypeScript 7 (`tsc`, the native Go-rewrite compiler). - Lint: `bun run lint` (runs `agent-cleanup-guard`, `architecture-guard`, then `oxlint`). - Format: `bun run format` (oxfmt). Check: `bun run format:check`. - After completing a feature, always run `bun run format` and `bun run lint`. diff --git a/README.md b/README.md index 78f78cee2..8bc8ffee1 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

-Argos AI Assistant Icon +Argos AI Assistant Icon

Argos - Powerful Open-Source AI Agent Platform

@@ -31,11 +31,11 @@ Beyond chat, Argos supports agentic workflows: rich tool calling via MCP (Model diff --git a/apps/daemon/src/host/argosOrchestrationRuntime.ts b/apps/daemon/src/host/argosOrchestrationRuntime.ts index 975292f54..150612a28 100644 --- a/apps/daemon/src/host/argosOrchestrationRuntime.ts +++ b/apps/daemon/src/host/argosOrchestrationRuntime.ts @@ -8,6 +8,22 @@ type SessionActions = { stop(sessionId: string): Promise; }; +type ProvisioningActions = { + createAgent(input: Record): Promise; + updateAgent(agentId: string, updates: Record): Promise; + listMcpServers(): Promise; + upsertMcpServer(serverName: string, config: Record): Promise; + setAgentMcpServers(agentId: string, serverNames: string[]): Promise; + listAgentSkills(agentId: string): Promise; + writeAgentSkill( + agentId: string, + input: { name: string; description: string; instructions: string }, + ): Promise; + removeAgentSkill(agentId: string, name: string): Promise; + provisionAgent(input: Record): Promise; + validateAgent(agentId: string): Promise; +}; + const tool = ( name: string, description: string, @@ -22,6 +38,7 @@ const tool = ( export class ArgosOrchestrationRuntime { private sessionActions?: SessionActions; + private provisioningActions?: ProvisioningActions; constructor( private readonly db: Database, @@ -85,6 +102,121 @@ export class ArgosOrchestrationRuntime { "sessionId", ]), tool("argos_agents_list", "List Argos agents available for delegation.", {}), + tool( + "argos_agents_create", + "Create a custom Argos agent. Pass config to set its prompt, model, permissions, tools, MCP servers, plugins, skills, memory, or subagents.", + { + name: { type: "string" }, + description: { type: "string" }, + enabled: { type: "boolean" }, + config: { type: "object", additionalProperties: true }, + }, + ["name"], + ), + tool( + "argos_agents_update", + "Update a custom Argos agent or the orchestrator itself. The default protected Argos agent cannot be changed here.", + { + agentId: { type: "string" }, + updates: { type: "object", additionalProperties: true }, + }, + ["agentId", "updates"], + ), + tool("argos_mcp_servers_list", "List globally configured MCP servers and their current configuration.", {}), + tool( + "argos_mcp_server_upsert", + "Add or update and start an MCP server. Environment values are persisted in the existing Argos MCP configuration; never place secrets in skill instructions.", + { + serverName: { type: "string" }, + config: { + type: "object", + properties: { + type: { type: "string", enum: ["stdio", "sse", "http"] }, + command: { type: "string" }, + args: { type: "array", items: { type: "string" } }, + env: { type: "object", additionalProperties: true }, + baseUrl: { type: "string" }, + customHeaders: { type: "object", additionalProperties: { type: "string" } }, + descriptions: { type: "string" }, + enabled: { type: "boolean" }, + }, + additionalProperties: true, + }, + }, + ["serverName", "config"], + ), + tool( + "argos_agent_mcp_servers_set", + "Replace an agent's MCP server allowlist. An empty list gives the agent no MCP servers.", + { + agentId: { type: "string" }, + serverNames: { type: "array", items: { type: "string" } }, + }, + ["agentId", "serverNames"], + ), + tool( + "argos_agent_skills_list", + "List disk-backed Argos-managed skills attached to one agent, including hash and managed version.", + { agentId: { type: "string" } }, + ["agentId"], + ), + tool( + "argos_agent_skill_write", + "Create or update an agent-specific skill under its managed .argos/skills directory and attach it to that agent.", + { + agentId: { type: "string" }, + name: { type: "string" }, + description: { type: "string" }, + instructions: { type: "string" }, + }, + ["agentId", "name", "description", "instructions"], + ), + tool( + "argos_agent_skill_remove", + "Remove an Argos-managed skill from one agent and detach it from that agent's skill allowlist.", + { agentId: { type: "string" }, name: { type: "string" } }, + ["agentId", "name"], + ), + tool( + "argos_agent_provision", + "Atomically create and validate a specialized agent with MCP servers and durable managed skills. The incomplete agent and MCP changes are rolled back on failure.", + { + name: { type: "string" }, + description: { type: "string" }, + enabled: { type: "boolean" }, + config: { type: "object", additionalProperties: true }, + mcpServers: { + type: "array", + items: { + type: "object", + properties: { + serverName: { type: "string" }, + config: { type: "object", additionalProperties: true }, + }, + required: ["serverName", "config"], + }, + }, + skills: { + type: "array", + items: { + type: "object", + properties: { + name: { type: "string" }, + description: { type: "string" }, + instructions: { type: "string" }, + }, + required: ["name", "description", "instructions"], + }, + }, + }, + ["name"], + ), + tool( + "argos_agent_validate", + "Validate an Argos agent's model, MCP configuration/runtime, allowlists, managed skill files, and enabled state.", + { agentId: { type: "string" } }, + ["agentId"], + ), ]; } @@ -96,6 +228,10 @@ export class ArgosOrchestrationRuntime { this.sessionActions = actions; } + setProvisioningActions(actions: ProvisioningActions): void { + this.provisioningActions = actions; + } + async call(request: MCPToolCall): Promise { const args = JSON.parse(request.function.arguments || "{}") as Record; const now = Date.now(); @@ -215,9 +351,82 @@ export class ArgosOrchestrationRuntime { case "argos_agents_list": result = await this.listAgents(); break; + case "argos_agents_create": + result = await this.requireProvisioning().createAgent({ + name: this.requireString(args, "name"), + ...(typeof args.description === "string" ? { description: args.description } : {}), + ...(typeof args.enabled === "boolean" ? { enabled: args.enabled } : {}), + ...(this.asRecord(args.config) ? { config: this.asRecord(args.config) } : {}), + }); + break; + case "argos_agents_update": + result = await this.requireProvisioning().updateAgent( + this.requireString(args, "agentId"), + this.asRecord(args.updates) ?? {}, + ); + break; + case "argos_mcp_servers_list": + result = await this.requireProvisioning().listMcpServers(); + break; + case "argos_mcp_server_upsert": { + const serverName = this.requireString(args, "serverName"); + const serverConfig = this.asRecord(args.config) ?? {}; + // stdio servers run an arbitrary local command with model-supplied args/env. + // The orchestrator may only register http/sse (URL) transports; stdio servers + // must be configured manually by the user to avoid local code execution. + if (serverConfig.type === "stdio") { + throw new Error( + "The orchestrator cannot register stdio MCP servers (that would allow arbitrary local command execution). Configure stdio servers manually via Settings, or use an http/sse transport.", + ); + } + result = await this.requireProvisioning().upsertMcpServer(serverName, serverConfig); + break; + } + case "argos_agent_mcp_servers_set": + result = await this.requireProvisioning().setAgentMcpServers( + String(args.agentId), + Array.isArray(args.serverNames) ? args.serverNames.map(String) : [], + ); + break; + case "argos_agent_skills_list": + result = await this.requireProvisioning().listAgentSkills(String(args.agentId)); + break; + case "argos_agent_skill_write": + result = await this.requireProvisioning().writeAgentSkill(String(args.agentId), { + name: String(args.name), + description: String(args.description), + instructions: String(args.instructions), + }); + break; + case "argos_agent_skill_remove": + result = await this.requireProvisioning().removeAgentSkill(String(args.agentId), String(args.name)); + break; + case "argos_agent_provision": + result = await this.requireProvisioning().provisionAgent(args); + break; + case "argos_agent_validate": + result = await this.requireProvisioning().validateAgent(String(args.agentId)); + break; default: throw new Error(`Unknown Argos orchestration tool: ${request.function.name}`); } return { toolCallId: request.id, content: [{ type: "text", text: JSON.stringify(result) }], toolResult: result }; } + + private requireProvisioning(): ProvisioningActions { + if (!this.provisioningActions) throw new Error("Argos provisioning is not ready."); + return this.provisioningActions; + } + + private asRecord(value: unknown): Record | null { + return value && typeof value === "object" && !Array.isArray(value) ? (value as Record) : null; + } + + private requireString(args: Record, key: string): string { + const value = args[key]; + if (typeof value !== "string" || value.trim().length === 0) { + throw new Error(`Missing required argument: ${key}`); + } + return value; + } } diff --git a/apps/daemon/src/host/daemonArgosAgentRuntime.ts b/apps/daemon/src/host/daemonArgosAgentRuntime.ts index 0d30f249d..76f6c124f 100644 --- a/apps/daemon/src/host/daemonArgosAgentRuntime.ts +++ b/apps/daemon/src/host/daemonArgosAgentRuntime.ts @@ -29,4 +29,9 @@ export class DaemonArgosAgentRuntime { ensureBuiltinAgent() { return this.runtime.ensureBuiltinAgent(); } + + /** Seed the disabled-by-default built-in orchestration specialist. */ + ensureBuiltinOrchestratorAgent() { + return this.runtime.ensureBuiltinOrchestratorAgent(); + } } diff --git a/apps/daemon/src/host/piAgentProfileManager.ts b/apps/daemon/src/host/piAgentProfileManager.ts index 3261e1824..b175b2470 100644 --- a/apps/daemon/src/host/piAgentProfileManager.ts +++ b/apps/daemon/src/host/piAgentProfileManager.ts @@ -1,5 +1,6 @@ import fs from "node:fs"; import path from "node:path"; +import { createHash } from "node:crypto"; export type PiPackageEntry = | string @@ -40,6 +41,14 @@ export interface PiAgentSettings { const FFF_PACKAGE = "@ff-labs/pi-fff"; const FFF_DEFAULT_ID = "pi-fff-v1"; +export interface ManagedAgentSkillRecord { + name: string; + sha256: string; + managedVersion: string; + installedAt: number; + updatedAt: number; +} + const normalizeAgentId = (agentId: string): string => { const normalized = agentId .trim() @@ -59,7 +68,10 @@ const packageSource = (entry: PiPackageEntry): string => (typeof entry === "stri * even when the agent has no active session. */ export class PiAgentProfileManager { - constructor(private readonly dataDir: string) {} + constructor( + private readonly dataDir: string, + private readonly appVersion = "dev", + ) {} getProfileDir(agentId: string): string { return path.join(this.dataDir, "agents", normalizeAgentId(agentId), "pi"); @@ -73,11 +85,20 @@ export class PiAgentProfileManager { return path.join(this.getProfileDir(agentId), "sessions"); } + getManagedSkillsDir(agentId: string): string { + return path.join(this.getProfileDir(agentId), ".argos", "skills"); + } + + getManagedSkillsRegistryPath(agentId: string): string { + return path.join(this.getProfileDir(agentId), ".argos", "skills-registry.json"); + } + ensureProfile(agentId: string): string { const profileDir = this.getProfileDir(agentId); for (const child of ["extensions", "skills", "prompts", "npm", "git", "sessions"]) { fs.mkdirSync(path.join(profileDir, child), { recursive: true }); } + fs.mkdirSync(this.getManagedSkillsDir(agentId), { recursive: true }); const settingsPath = this.getSettingsPath(agentId); if (!fs.existsSync(settingsPath)) { @@ -91,9 +112,81 @@ export class PiAgentProfileManager { }); } this.applyArgosDefaults(agentId); + this.registerManagedSkillsLocation(agentId); return profileDir; } + listManagedSkills(agentId: string): ManagedAgentSkillRecord[] { + this.ensureProfile(agentId); + return this.readManagedSkillsRegistry(agentId); + } + + validateManagedSkills(agentId: string): Array { + this.ensureProfile(agentId); + return this.readManagedSkillsRegistry(agentId).map((record) => { + const skillPath = path.join(this.getManagedSkillsDir(agentId), record.name, "SKILL.md"); + const exists = fs.existsSync(skillPath) && fs.statSync(skillPath).isFile(); + const sha256 = exists ? createHash("sha256").update(fs.readFileSync(skillPath)).digest("hex") : ""; + return { ...record, exists, hashMatches: exists && sha256 === record.sha256 }; + }); + } + + writeManagedSkill( + agentId: string, + input: { name: string; description: string; instructions: string }, + ): ManagedAgentSkillRecord { + this.ensureProfile(agentId); + const name = this.normalizeSkillName(input.name); + const description = input.description.trim(); + const instructions = input.instructions.trim(); + if (!description) throw new Error("A managed skill description is required."); + if (!instructions) throw new Error("Managed skill instructions are required."); + + const content = `---\nname: ${name}\ndescription: ${JSON.stringify(description.replace(/\r?\n/g, " "))}\n---\n\n${instructions}\n`; + const sha256 = createHash("sha256").update(content).digest("hex"); + const skillDir = path.join(this.getManagedSkillsDir(agentId), name); + fs.mkdirSync(skillDir, { recursive: true }); + const skillPath = path.join(skillDir, "SKILL.md"); + const temporaryPath = `${skillPath}.${process.pid}.${Date.now()}.tmp`; + fs.writeFileSync(temporaryPath, content, "utf8"); + this.atomicRename(temporaryPath, skillPath); + + const now = Date.now(); + const registry = this.readManagedSkillsRegistry(agentId); + const existing = registry.find((entry) => entry.name === name); + const record: ManagedAgentSkillRecord = { + name, + sha256, + managedVersion: this.appVersion, + installedAt: existing?.installedAt ?? now, + updatedAt: now, + }; + this.writeManagedSkillsRegistry(agentId, [...registry.filter((entry) => entry.name !== name), record]); + return record; + } + + removeManagedSkill(agentId: string, requestedName: string): boolean { + this.ensureProfile(agentId); + const name = this.normalizeSkillName(requestedName); + const skillsRoot = path.resolve(this.getManagedSkillsDir(agentId)); + const skillDir = path.resolve(skillsRoot, name); + if (path.dirname(skillDir) !== skillsRoot) throw new Error("Managed skill path escaped its root."); + const existed = fs.existsSync(skillDir); + if (existed) fs.rmSync(skillDir, { recursive: true, force: true }); + const registry = this.readManagedSkillsRegistry(agentId).filter((entry) => entry.name !== name); + this.writeManagedSkillsRegistry(agentId, registry); + return existed; + } + + removeProfile(agentId: string): boolean { + const profileRoot = path.resolve(path.join(this.dataDir, "agents")); + const profileDir = path.resolve(this.getProfileDir(agentId)); + if (path.dirname(path.dirname(profileDir)) !== profileRoot) throw new Error("Agent profile path escaped its root."); + const existed = fs.existsSync(profileDir); + if (existed) fs.rmSync(profileDir, { recursive: true, force: true }); + return existed; + } + readSettings(agentId: string): PiAgentSettings { this.ensureProfileDirectories(agentId); const settingsPath = this.getSettingsPath(agentId); @@ -205,4 +298,59 @@ export class PiAgentProfileManager { appliedArgosDefaults: [...applied, FFF_DEFAULT_ID], }); } + + private registerManagedSkillsLocation(agentId: string): void { + const managedSkillsDir = this.getManagedSkillsDir(agentId); + const settings = this.readSettings(agentId); + const skills = Array.isArray(settings.skills) ? settings.skills : []; + if (skills.includes(managedSkillsDir)) return; + this.writeSettings(agentId, { ...settings, skills: [...skills, managedSkillsDir] }); + } + + private normalizeSkillName(value: string): string { + const name = value.trim().toLowerCase(); + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name) || name.length > 64) { + throw new Error("Skill names must be 1-64 lowercase letters, numbers, or hyphen-separated words."); + } + return name; + } + + /** + * Atomic-rename with a Windows fallback: POSIX rename atomically replaces an + * existing destination, but on Windows rename fails when the target exists, so + * remove the target and retry. Keeps updates portable across platforms. + */ + private atomicRename(temporaryPath: string, targetPath: string): void { + try { + fs.renameSync(temporaryPath, targetPath); + } catch { + fs.rmSync(targetPath, { force: true }); + fs.renameSync(temporaryPath, targetPath); + } + } + + private readManagedSkillsRegistry(agentId: string): ManagedAgentSkillRecord[] { + const registryPath = this.getManagedSkillsRegistryPath(agentId); + if (!fs.existsSync(registryPath)) return []; + try { + const parsed = JSON.parse(fs.readFileSync(registryPath, "utf8")) as { skills?: ManagedAgentSkillRecord[] }; + return Array.isArray(parsed.skills) ? parsed.skills : []; + } catch (error) { + // A corrupt registry must not be silently treated as empty: that would let + // subsequent writes overwrite integrity records and lose track of skills. + throw new Error( + `Managed skills registry for agent ${agentId} is corrupt and could not be parsed: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } + } + + private writeManagedSkillsRegistry(agentId: string, skills: ManagedAgentSkillRecord[]): void { + const registryPath = this.getManagedSkillsRegistryPath(agentId); + fs.mkdirSync(path.dirname(registryPath), { recursive: true }); + const temporaryPath = `${registryPath}.${process.pid}.${Date.now()}.tmp`; + fs.writeFileSync(temporaryPath, `${JSON.stringify({ version: 1, skills }, null, 2)}\n`, "utf8"); + this.atomicRename(temporaryPath, registryPath); + } } diff --git a/apps/daemon/src/index.ts b/apps/daemon/src/index.ts index 836432d9d..a240c0e5e 100644 --- a/apps/daemon/src/index.ts +++ b/apps/daemon/src/index.ts @@ -16,6 +16,7 @@ import { ProviderImportService } from "@argos/backend-core"; import { PiProviderExecutionPort } from "./host/pi-provider-execution"; import { PiAgentProfileManager } from "./host/piAgentProfileManager"; import { ArgosOrchestrationRuntime } from "./host/argosOrchestrationRuntime"; +import { BUILTIN_ARGOS_AGENT_ID } from "@argos/agent-runtime"; import { AcpProviderExecutionPort } from "./host/acp-provider-execution"; import { createDaemonMcpPorts } from "./host/daemonMcpPorts"; import { DaemonMcpRuntime } from "./host/daemonMcpRuntime"; @@ -274,6 +275,7 @@ export async function startDaemon(options?: { const argosAgentRuntimeHost = new DaemonArgosAgentRuntime(db); argosAgentRuntimeHost.ensureBuiltinAgent(); + argosAgentRuntimeHost.ensureBuiltinOrchestratorAgent(); configPresenter.setArgosAgentRuntime(argosAgentRuntimeHost.runtime); logger.info("[daemon] Argos agent runtime initialized"); @@ -289,7 +291,7 @@ export async function startDaemon(options?: { logger.info(`[daemon] Reset active sessions to idle`); } - const piProfiles = new PiAgentProfileManager(paths.getDataDir()); + const piProfiles = new PiAgentProfileManager(paths.getDataDir(), resolveDaemonVersion()); const piProviderExecutionPort = new PiProviderExecutionPort( configPresenter, sessionRepository, @@ -442,6 +444,246 @@ export async function startDaemon(options?: { sessionRepository, }); (skillRuntime as typeof skillRuntime & { piProfiles: PiAgentProfileManager }).piProfiles = piProfiles; + const SAFE_MCP_CONFIG_FIELDS = new Set(["type", "command", "descriptions", "description", "enabled", "disable"]); + const redactUnknownValue = (value: unknown): unknown => { + if (Array.isArray(value)) return value.map(() => "[configured]"); + if (value && typeof value === "object") { + return Object.fromEntries(Object.keys(value as Record).map((key) => [key, "[configured]"])); + } + return "[configured]"; + }; + const redactMcpServers = (servers: Record) => + Object.fromEntries( + Object.entries(servers).map(([name, value]) => { + if (!value || typeof value !== "object" || Array.isArray(value)) return [name, value]; + const config = value as Record; + const redacted: Record = {}; + for (const [key, val] of Object.entries(config)) { + // Only explicitly non-sensitive fields are exposed to the orchestrator model + // verbatim; everything else (env, customHeaders, args, baseUrl, unknown maps) + // is masked so credentials cannot leak. + redacted[key] = SAFE_MCP_CONFIG_FIELDS.has(key) ? val : redactUnknownValue(val); + } + return [name, redacted]; + }), + ); + const validateProvisionedAgent = async (agentId: string, requireEnabled = true) => { + const agent = await configPresenter.getArgosAgent(agentId); + if (!agent) throw new Error(`Argos agent not found: ${agentId}`); + const effectiveConfig = await configPresenter.resolveArgosAgentConfig(agentId); + const configuredServers = (await configPresenter.getMcpServers()) as Record; + const expectedServers = effectiveConfig.enabledMcpServerIds ?? []; + const managedSkills = piProfiles.validateManagedSkills(agentId); + const expectedSkills = agent.config?.enabledSkillNames ?? []; + const checks = [ + { + name: "model", + ok: Boolean(effectiveConfig.defaultModelPreset?.modelId) || Boolean(effectiveConfig.assistantModel?.modelId), + }, + { name: "enabled", ok: !requireEnabled || agent.enabled }, + { + name: "mcp-configured", + ok: expectedServers.every((name: string) => Boolean(configuredServers[name]?.enabled)), + details: expectedServers, + }, + { + name: "mcp-running", + ok: expectedServers.every((name: string) => mcpRuntime.isServerRunning(name)), + details: expectedServers.filter((name: string) => !mcpRuntime.isServerRunning(name)), + }, + { + name: "skills-attached", + ok: managedSkills.every((skill) => expectedSkills.includes(skill.name)), + details: managedSkills.filter((skill) => !expectedSkills.includes(skill.name)).map((skill) => skill.name), + }, + { + name: "skill-hashes", + ok: managedSkills.every((skill) => skill.exists && skill.hashMatches), + details: managedSkills.filter((skill) => !skill.exists || !skill.hashMatches), + }, + ]; + return { agentId, valid: checks.every((check) => check.ok), checks }; + }; + const provisioningActions: Parameters[0] = { + createAgent: (input) => configPresenter.createArgosAgent(input), + async updateAgent(agentId, updates) { + if (agentId === BUILTIN_ARGOS_AGENT_ID) + throw new Error("The protected default Argos agent cannot be changed by provisioning."); + const agent = await configPresenter.getArgosAgent(agentId); + if (!agent) throw new Error(`Argos agent not found: ${agentId}`); + return await configPresenter.updateArgosAgent(agentId, updates); + }, + async listMcpServers() { + return redactMcpServers((await configPresenter.getMcpServers()) as Record); + }, + async upsertMcpServer(serverName, config) { + const name = serverName.trim(); + if (!name) throw new Error("An MCP server name is required."); + const servers = (await configPresenter.getMcpServers()) as Record; + const normalized = { ...config, enabled: true, disable: false }; + if (Object.prototype.hasOwnProperty.call(servers, name)) { + await configPresenter.updateMcpServer(name, normalized); + } else { + await configPresenter.addMcpServer(name, normalized); + } + await configPresenter.setMcpEnabled(true); + await configPresenter.setMcpServerEnabled(name, true); + if (mcpRuntime.isServerRunning(name)) await mcpRuntime.stopServer(name); + await mcpRuntime.startServer(name); + const redacted = redactMcpServers((await configPresenter.getMcpServers()) as Record); + return { serverName: name, config: redacted[name], running: true }; + }, + async setAgentMcpServers(agentId, serverNames) { + const agent = await configPresenter.getArgosAgent(agentId); + if (!agent) throw new Error(`Argos agent not found: ${agentId}`); + const servers = (await configPresenter.getMcpServers()) as Record; + const normalized = Array.from(new Set(serverNames.map((name) => name.trim()).filter(Boolean))); + const missing = normalized.filter((name) => !Object.prototype.hasOwnProperty.call(servers, name)); + if (missing.length) throw new Error(`Unknown MCP server(s): ${missing.join(", ")}`); + const updated = await configPresenter.updateArgosAgent(agentId, { + config: { ...agent.config, enabledMcpServerIds: normalized }, + }); + return { agent: updated, enabledMcpServerIds: normalized }; + }, + async listAgentSkills(agentId) { + if (!(await configPresenter.getArgosAgent(agentId))) throw new Error(`Argos agent not found: ${agentId}`); + return piProfiles.listManagedSkills(agentId); + }, + async writeAgentSkill(agentId, input) { + const agent = await configPresenter.getArgosAgent(agentId); + if (!agent) throw new Error(`Argos agent not found: ${agentId}`); + const skill = piProfiles.writeManagedSkill(agentId, input); + const enabledSkillNames = Array.from(new Set([...(agent.config?.enabledSkillNames ?? []), skill.name])); + await configPresenter.updateArgosAgent(agentId, { + config: { ...agent.config, enabledSkillNames }, + }); + return { skill, enabledSkillNames }; + }, + async removeAgentSkill(agentId, name) { + const agent = await configPresenter.getArgosAgent(agentId); + if (!agent) throw new Error(`Argos agent not found: ${agentId}`); + const removed = piProfiles.removeManagedSkill(agentId, name); + const normalizedName = name.trim().toLowerCase(); + const enabledSkillNames = (agent.config?.enabledSkillNames ?? []).filter((item) => item !== normalizedName); + await configPresenter.updateArgosAgent(agentId, { + config: { ...agent.config, enabledSkillNames }, + }); + return { removed, name: normalizedName, enabledSkillNames }; + }, + async provisionAgent(input) { + const name = typeof input.name === "string" ? input.name.trim() : ""; + if (!name) throw new Error("A provisioned agent name is required."); + const description = typeof input.description === "string" ? input.description : undefined; + const requestedConfig = + input.config && typeof input.config === "object" && !Array.isArray(input.config) + ? (input.config as Record) + : {}; + const mcpServers = Array.isArray(input.mcpServers) + ? input.mcpServers.filter((item): item is { serverName: string; config: Record } => + Boolean( + item && + typeof item === "object" && + typeof (item as any).serverName === "string" && + (item as any).config && + typeof (item as any).config === "object" && + !Array.isArray((item as any).config), + ), + ) + : []; + const skills = Array.isArray(input.skills) + ? input.skills.filter((item): item is { name: string; description: string; instructions: string } => + Boolean( + item && + typeof item === "object" && + typeof (item as any).name === "string" && + typeof (item as any).description === "string" && + typeof (item as any).instructions === "string", + ), + ) + : []; + const beforeMcpEnabled = await configPresenter.getMcpEnabled(); + const beforeServers = (await configPresenter.getMcpServers()) as Record; + const snapshots = new Map( + mcpServers.map(({ serverName }) => [ + serverName.trim(), + { + config: beforeServers[serverName.trim()], + running: mcpRuntime.isServerRunning(serverName.trim()), + }, + ]), + ); + let createdAgentId: string | null = null; + try { + const created = await configPresenter.createArgosAgent({ + name, + description, + enabled: false, + config: requestedConfig, + }); + createdAgentId = created.id; + for (const server of mcpServers) { + await provisioningActions.upsertMcpServer(server.serverName, server.config); + } + await provisioningActions.setAgentMcpServers( + created.id, + mcpServers.map((server) => server.serverName), + ); + for (const skill of skills) await provisioningActions.writeAgentSkill(created.id, skill); + + const validation = await validateProvisionedAgent(created.id, false); + if (!validation.valid) { + const failed = validation.checks.filter((check) => !check.ok).map((check) => check.name); + throw new Error(`Provisioned agent validation failed: ${failed.join(", ")}`); + } + const enabled = input.enabled !== false; + const agent = await configPresenter.updateArgosAgent(created.id, { enabled }); + return { agent, validation: await validateProvisionedAgent(created.id, enabled), rolledBack: false }; + } catch (error) { + // Isolate each rollback step so one failure cannot skip remaining cleanup; + // collect every rollback error and surface it with the original cause. + // removeProfile recursively deletes the agent profile (incl. managed skills), + // so written skills are cleaned up too. + const rollbackErrors: { step: string; error: string }[] = []; + const rollbackStep = async (step: string, run: () => Promise | unknown) => { + try { + await run(); + } catch (rollbackError) { + rollbackErrors.push({ + step, + error: rollbackError instanceof Error ? rollbackError.message : String(rollbackError), + }); + } + }; + if (createdAgentId) { + const rollbackAgentId = createdAgentId; + await rollbackStep("delete-agent", () => configPresenter.deleteArgosAgent(rollbackAgentId)); + await rollbackStep("remove-profile", () => piProfiles.removeProfile(rollbackAgentId)); + } + for (const [serverName, snapshot] of snapshots) { + await rollbackStep(`stop-server:${serverName}`, async () => { + if (mcpRuntime.isServerRunning(serverName)) await mcpRuntime.stopServer(serverName); + }); + await rollbackStep(`restore-server:${serverName}`, async () => { + if (snapshot.config) { + await configPresenter.updateMcpServer(serverName, snapshot.config); + await configPresenter.setMcpServerEnabled(serverName, snapshot.config.enabled !== false); + if (snapshot.running) await mcpRuntime.startServer(serverName); + } else { + await configPresenter.removeMcpServer(serverName); + } + }); + } + await rollbackStep("set-mcp-enabled", () => configPresenter.setMcpEnabled(beforeMcpEnabled)); + const originalMessage = error instanceof Error ? error.message : String(error); + const rollbackDetail = rollbackErrors.length + ? ` (rollback failures: ${rollbackErrors.map((entry) => `${entry.step} (${entry.error})`).join("; ")})` + : ""; + throw new Error(`Agent provisioning rolled back: ${originalMessage}${rollbackDetail}`); + } + }, + validateAgent: (agentId) => validateProvisionedAgent(agentId), + }; + orchestrationRuntime.setProvisioningActions(provisioningActions); const syncRuntime = new DaemonSyncRuntime({ configDir: paths.getConfigDir(), eventPublisher, diff --git a/apps/daemon/test/argosOrchestrationRuntime.test.ts b/apps/daemon/test/argosOrchestrationRuntime.test.ts new file mode 100644 index 000000000..ce7c17ee3 --- /dev/null +++ b/apps/daemon/test/argosOrchestrationRuntime.test.ts @@ -0,0 +1,103 @@ +import { describe, expect, it, vi } from "vitest"; +import { ArgosOrchestrationRuntime } from "../src/host/argosOrchestrationRuntime"; + +const call = (runtime: ArgosOrchestrationRuntime, name: string, args: Record) => + runtime.call({ + id: "call-1", + type: "function", + function: { name, arguments: JSON.stringify(args) }, + }); + +describe("ArgosOrchestrationRuntime provisioning", () => { + it("exposes agent, MCP, and disk-backed skill provisioning tools", () => { + const runtime = new ArgosOrchestrationRuntime({ exec: vi.fn() }, async () => []); + const names = runtime.definitions().map((definition) => definition.function.name); + expect(names).toEqual( + expect.arrayContaining([ + "argos_agents_create", + "argos_agents_update", + "argos_mcp_server_upsert", + "argos_agent_mcp_servers_set", + "argos_agent_skill_write", + "argos_agent_skill_remove", + "argos_agent_provision", + "argos_agent_validate", + ]), + ); + }); + + it("delegates provisioning mutations through injected authority ports", async () => { + const runtime = new ArgosOrchestrationRuntime({ exec: vi.fn() }, async () => []); + const createAgent = vi.fn(async (input) => ({ id: "mail-agent", ...input })); + const writeAgentSkill = vi.fn(async (_agentId, input) => ({ skill: input.name })); + const provisionAgent = vi.fn(async (input) => ({ agent: { id: "mail-agent" }, input })); + const validateAgent = vi.fn(async (agentId) => ({ agentId, valid: true })); + runtime.setProvisioningActions({ + createAgent, + updateAgent: vi.fn(), + listMcpServers: vi.fn(), + upsertMcpServer: vi.fn(), + setAgentMcpServers: vi.fn(), + listAgentSkills: vi.fn(), + writeAgentSkill, + removeAgentSkill: vi.fn(), + provisionAgent, + validateAgent, + }); + + await call(runtime, "argos_agents_create", { name: "Mail", config: { enabledMcpServerIds: ["zoho"] } }); + await call(runtime, "argos_agent_skill_write", { + agentId: "mail-agent", + name: "zoho-mail", + description: "Use Zoho Mail", + instructions: "Use the MCP tools.", + }); + await call(runtime, "argos_agent_provision", { name: "Mail", mcpServers: [] }); + await call(runtime, "argos_agent_validate", { agentId: "mail-agent" }); + + expect(createAgent).toHaveBeenCalledWith({ name: "Mail", config: { enabledMcpServerIds: ["zoho"] } }); + expect(writeAgentSkill).toHaveBeenCalledWith("mail-agent", { + name: "zoho-mail", + description: "Use Zoho Mail", + instructions: "Use the MCP tools.", + }); + expect(provisionAgent).toHaveBeenCalledWith({ name: "Mail", mcpServers: [] }); + expect(validateAgent).toHaveBeenCalledWith("mail-agent"); + }); + + it("rejects stdio MCP registration and validates required string args", async () => { + const upsertMcpServer = vi.fn(async () => ({ ok: true })); + const createAgent = vi.fn(async (input) => ({ id: "x", ...input })); + const updateAgent = vi.fn(async (agentId: string) => ({ id: agentId })); + const runtime = new ArgosOrchestrationRuntime({ exec: vi.fn() }, async () => []); + runtime.setProvisioningActions({ + createAgent, + updateAgent, + listMcpServers: vi.fn(), + upsertMcpServer, + setAgentMcpServers: vi.fn(), + listAgentSkills: vi.fn(), + writeAgentSkill: vi.fn(), + removeAgentSkill: vi.fn(), + provisionAgent: vi.fn(), + validateAgent: vi.fn(), + }); + + // stdio servers run arbitrary local commands and cannot be registered by the orchestrator + await expect( + call(runtime, "argos_mcp_server_upsert", { serverName: "evil", config: { type: "stdio", command: "rm" } }), + ).rejects.toThrow(/stdio/); + expect(upsertMcpServer).not.toHaveBeenCalled(); + + // http/sse transports are still permitted + await call(runtime, "argos_mcp_server_upsert", { + serverName: "mail", + config: { type: "http", baseUrl: "https://example.com" }, + }); + expect(upsertMcpServer).toHaveBeenCalledWith("mail", expect.objectContaining({ type: "http" })); + + // required string args are validated before String() coercion + await expect(call(runtime, "argos_agents_create", { description: "no name" })).rejects.toThrow(/name/); + await expect(call(runtime, "argos_agents_update", { updates: { foo: 1 } })).rejects.toThrow(/agentId/); + }); +}); diff --git a/apps/daemon/test/daemonArgosAgentRuntime.test.ts b/apps/daemon/test/daemonArgosAgentRuntime.test.ts index 9fb31360a..1a5dc6a39 100644 --- a/apps/daemon/test/daemonArgosAgentRuntime.test.ts +++ b/apps/daemon/test/daemonArgosAgentRuntime.test.ts @@ -75,14 +75,20 @@ function makeFakeDb() { } describe("DaemonArgosAgentRuntime", () => { - it("seeds the builtin agent on construction", () => { + it("seeds both built-in agents", () => { const db = makeFakeDb(); const host = new DaemonArgosAgentRuntime(db as never); host.ensureBuiltinAgent(); + host.ensureBuiltinOrchestratorAgent(); const agents = host.runtime.listAgents(); - expect(agents).toHaveLength(1); - expect(agents[0]).toMatchObject({ id: "argos", type: "argos", protected: true, enabled: true }); + expect(agents).toHaveLength(2); + expect(agents).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: "argos", type: "argos", protected: true, enabled: true }), + expect.objectContaining({ id: "argos-orchestrator", type: "argos", protected: true, enabled: false }), + ]), + ); }); it("create/delete round-trips and respects the session guard", () => { diff --git a/apps/daemon/test/piAgentProfileManager.test.ts b/apps/daemon/test/piAgentProfileManager.test.ts index 5b148d61b..e66771a4d 100644 --- a/apps/daemon/test/piAgentProfileManager.test.ts +++ b/apps/daemon/test/piAgentProfileManager.test.ts @@ -13,7 +13,7 @@ afterEach(() => { function createManager() { const directory = fs.mkdtempSync(path.join(os.tmpdir(), "argos-pi-profile-")); directories.push(directory); - return new PiAgentProfileManager(directory); + return new PiAgentProfileManager(directory, "1.2.3"); } describe("PiAgentProfileManager", () => { @@ -66,4 +66,46 @@ describe("PiAgentProfileManager", () => { const manager = createManager(); expect(() => manager.ensureProfile("///")).toThrow("valid agent id"); }); + + it("persists managed skills in .argos with a hash registry and Pi location", () => { + const manager = createManager(); + const first = manager.writeManagedSkill("mail-agent", { + name: "zoho-mail", + description: "Use Zoho Mail through its MCP server.", + instructions: "Use the Zoho MCP tools for inbox and message operations.", + }); + + expect(first).toMatchObject({ name: "zoho-mail", managedVersion: "1.2.3" }); + expect(first.sha256).toMatch(/^[a-f0-9]{64}$/); + expect( + fs.readFileSync(path.join(manager.getManagedSkillsDir("mail-agent"), "zoho-mail", "SKILL.md"), "utf8"), + ).toContain("name: zoho-mail"); + expect(manager.readSettings("mail-agent").skills).toContain(manager.getManagedSkillsDir("mail-agent")); + expect(manager.listManagedSkills("mail-agent")).toEqual([first]); + expect(manager.validateManagedSkills("mail-agent")).toEqual([{ ...first, exists: true, hashMatches: true }]); + fs.appendFileSync(path.join(manager.getManagedSkillsDir("mail-agent"), "zoho-mail", "SKILL.md"), "tampered"); + expect(manager.validateManagedSkills("mail-agent")[0]?.hashMatches).toBe(false); + + const updated = manager.writeManagedSkill("mail-agent", { + name: "zoho-mail", + description: "Use Zoho Mail safely.", + instructions: "Never place credentials in messages.", + }); + expect(updated.installedAt).toBe(first.installedAt); + expect(updated.sha256).not.toBe(first.sha256); + expect(manager.removeManagedSkill("mail-agent", "zoho-mail")).toBe(true); + expect(manager.listManagedSkills("mail-agent")).toEqual([]); + expect(manager.removeProfile("mail-agent")).toBe(true); + }); + + it("rejects unsafe managed skill names", () => { + const manager = createManager(); + expect(() => + manager.writeManagedSkill("mail-agent", { + name: "../escape", + description: "unsafe", + instructions: "unsafe", + }), + ).toThrow("Skill names must be"); + }); }); diff --git a/apps/desktop/test/main/presenter/argosAgentRuntime/argosAgentRuntime.test.ts b/apps/desktop/test/main/presenter/argosAgentRuntime/argosAgentRuntime.test.ts index 9ab6dc231..f6962a3f3 100644 --- a/apps/desktop/test/main/presenter/argosAgentRuntime/argosAgentRuntime.test.ts +++ b/apps/desktop/test/main/presenter/argosAgentRuntime/argosAgentRuntime.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { ArgosAgentRuntime, BUILTIN_ARGOS_AGENT_ID } from "@argos/agent-runtime"; +import { ArgosAgentRuntime, BUILTIN_ARGOS_AGENT_ID, BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID } from "@argos/agent-runtime"; import type { AgentSessionLookupPort, ArgosAgentRow, ArgosAgentStore } from "@argos/agent-runtime"; const makeRow = (overrides: Partial): ArgosAgentRow => ({ @@ -69,6 +69,41 @@ describe("ArgosAgentRuntime", () => { expect(second.name).toBe("Argos"); }); + it("seeds a disabled orchestrator and preserves its enabled state", () => { + const { runtime } = makeRuntime(new Set()); + runtime.ensureBuiltinAgent({ config: { orchestrationEnabled: false } }); + + const seeded = runtime.ensureBuiltinOrchestratorAgent(); + expect(seeded).toMatchObject({ + id: BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID, + source: "builtin", + protected: true, + enabled: false, + }); + expect(runtime.resolveArgosAgentConfig(seeded.id)).toMatchObject({ + orchestrationEnabled: true, + subagentEnabled: true, + permissionMode: "full_access", + disabledAgentTools: [], + }); + + runtime.updateArgosAgent(seeded.id, { enabled: true, config: { orchestrationEnabled: false } }); + const afterRestart = runtime.ensureBuiltinOrchestratorAgent(); + expect(afterRestart.enabled).toBe(true); + expect(runtime.resolveArgosAgentConfig(seeded.id).orchestrationEnabled).toBe(true); + }); + + it("preserves orchestration and defaults extension policy to empty (deny-by-default)", () => { + const { runtime } = makeRuntime(new Set()); + runtime.ensureBuiltinAgent({ config: { orchestrationEnabled: true } }); + + const resolved = runtime.resolveArgosAgentConfig(BUILTIN_ARGOS_AGENT_ID); + expect(resolved.orchestrationEnabled).toBe(true); + expect(resolved.enabledMcpServerIds).toEqual([]); + expect(resolved.enabledPluginIds).toEqual([]); + expect(resolved.enabledSkillNames).toEqual([]); + }); + it("lists only argos agents from the store", () => { const { runtime } = makeRuntime(new Set()); runtime.ensureBuiltinAgent(); diff --git a/docs/architecture/baselines/test-failure-groups.md b/docs/architecture/baselines/test-failure-groups.md index e643e6865..ca365da36 100644 --- a/docs/architecture/baselines/test-failure-groups.md +++ b/docs/architecture/baselines/test-failure-groups.md @@ -2,7 +2,7 @@ Baseline refreshed on `2026-06-21`. **Suite is green** as of commit `f9130c1`. -Totals: **0 failed / 2267 passed / 71 skipped** of 2338 tests (vitest, `pnpm test`). +Totals: **0 failed / 2267 passed / 71 skipped** of 2338 tests (vitest, `bun run test`). The suite was previously understating itself: a broken `@electron-toolkit/utils` load masked ~525 tests, and 11 provider test files couldn't load due to a @@ -39,7 +39,7 @@ Fixing those (global mocks + lazy presenter access) made the suite comprehensive ## Environment-gated (skipped with reasons, not failing) - `pluginPresenter.test.ts` — `describe.skipIf` when `plugins/cua/plugin.json` is - absent (only present after `pnpm run plugin:cua:build`). 28 tests. + absent (only present after `bun run plugin:cua:build`). 28 tests. - `acpFsHandler.test.ts` — symlink tests `it.skipIf(os.platform() === "win32")` (needs Developer Mode/admin). 2 tests. - Global mocks in `test/setup.ts`: `@electron-toolkit/utils`, `electron-store`, diff --git a/docs/architecture/extract-ui/plan.md b/docs/architecture/extract-ui/plan.md index b983beeb2..42b152955 100644 --- a/docs/architecture/extract-ui/plan.md +++ b/docs/architecture/extract-ui/plan.md @@ -19,7 +19,7 @@ ### Phase 4 — Daemon serves UI - [x] `resolveWebRoot` searches `packages/ui/dist` + `resources/web` (+ `../web` from executable dir). -- [x] Help text updated (`pnpm --filter @argos/ui build`). +- [x] Help text updated (`bun run --filter @argos/ui build`). ### Phase 5 — Build / packaging / guards - [x] `electron-builder.yml`: `packages/ui/dist` → `resources/web`. @@ -31,7 +31,7 @@ ### Phase 6 — Path-alias migration (`#` prefix) - [x] Codemod across 1740 files: `@/`→`#/`, `@api`→`#api`, `@shadcn`→`#shadcn`, `@settings`→`#settings`, `@shared`→`@argos/shared`, `@shared/contracts`→`@argos/shared-contracts`. - [x] Updated tsconfig path keys, vite path-alias plugins, vitest aliases, guard patterns. -- [x] Deduped duplicate tsconfig path keys; fixed pre-existing `baseUrl` tsgo error in `shared-contracts` (now typechecks standalone). +- [x] Deduped duplicate tsconfig path keys; fixed pre-existing `baseUrl` TypeScript error in `shared-contracts` (now typechecks standalone). ## Verified @@ -43,11 +43,11 @@ ## Remaining (this migration) ### Runtime / packaging verification (needs Electron + electron-builder; cannot run in this environment) -- [ ] **End-to-end launch**: `pnpm dev` (after `pnpm --filter @argos/ui build`) → desktop window renders UI served by the daemon. +- [ ] **End-to-end launch**: `bun run dev` (after `bun run --filter @argos/ui build`) → desktop window renders UI served by the daemon. - [ ] **Native routes under served model**: confirm file dialogs / `native_required` routes still work via the hybrid bridge when the UI is served over `http://127.0.0.1` (cross-origin preload injection). - [ ] **Splash startup ordering**: daemon ready before splash loads (inline fallback exercised). - [ ] **Packaged build**: `electron-builder` packaging — confirm `packages/ui/dist` → `resources/web` and daemon dist → `daemon`; packaged app loads UI from sidecar. -- [x] **Dev HMR orchestration**: root `pnpm dev` runs the cross-platform `scripts/dev.mjs` launcher. It directly starts each workspace's Vite CLI (without Windows batch wrappers), verifies `@argos/ui` at IPv4 `127.0.0.1:5180`, then starts `@argos/desktop`. This prevents Electron from loading the UI before Vite is available and lets Ctrl+C terminate both process trees. `ARGOS_UI_DEV_SERVER_URL=http://127.0.0.1:5180` explicitly selects the UI server. Vite's internally assigned `VITE_DEV_SERVER_URL` remains reserved for its shell placeholder renderer. +- [x] **Dev HMR orchestration**: root `bun run dev` runs the cross-platform `scripts/dev.mjs` launcher. It directly starts each workspace's Vite CLI (without Windows batch wrappers), verifies `@argos/ui` at IPv4 `127.0.0.1:5180`, then starts `@argos/desktop`. This prevents Electron from loading the UI before Vite is available and lets Ctrl+C terminate both process trees. `ARGOS_UI_DEV_SERVER_URL=http://127.0.0.1:5180` explicitly selects the UI server. Vite's internally assigned `VITE_DEV_SERVER_URL` remains reserved for its shell placeholder renderer. - [x] **Dev proxy isolation**: proxy only `/api/v1`, the daemon transport namespace. Do not proxy `/api/*` broadly because Vite serves the UI's `#api` source alias at paths such as `/api/ConfigClient.ts`. - [x] **Bridge startup readiness**: daemon routes wait for the preload WebSocket to open; chat composers remain disabled while connecting, so an initial prompt cannot be persisted without starting its agent run. The local connection indicator also reflects the actual WebSocket state. - [x] **Initial-turn dispatch**: `sessions.create` now creates only the session; the daemon starts its initial prompt through the same provider-execution path as `chat.sendMessage`, preventing user-only first sessions. diff --git a/docs/architecture/extract-ui/tasks.md b/docs/architecture/extract-ui/tasks.md index 1c857aaa3..1ddb03277 100644 --- a/docs/architecture/extract-ui/tasks.md +++ b/docs/architecture/extract-ui/tasks.md @@ -14,7 +14,7 @@ Actionable checklist (status of the migration). See `plan.md` for full detail. - [x] AGENTS.md rewritten ## TODO — runtime/packaging verification (needs Electron; not runnable here) -- [ ] E2E launch: `pnpm --filter @argos/ui build && pnpm dev` → window renders daemon-served UI +- [ ] E2E launch: `bun run --filter @argos/ui build && bun run dev` → window renders daemon-served UI - [ ] Native-only routes still work via hybrid bridge over `http://127.0.0.1` origin - [ ] Splash startup ordering (inline fallback path) - [ ] `electron-builder` packaging produces working app (web + daemon bundled) diff --git a/docs/architecture/memory-subsystem/tasks.md b/docs/architecture/memory-subsystem/tasks.md index 3c78a846e..3f12f4add 100644 --- a/docs/architecture/memory-subsystem/tasks.md +++ b/docs/architecture/memory-subsystem/tasks.md @@ -163,15 +163,15 @@ ## Phase 5: Final Integration + Gate ### T5.1 Full typecheck pass -- Run `pnpm run typecheck` — fix any type errors across all phases +- Run `bun run typecheck` — fix any type errors across all phases - **Depends**: all phases ### T5.2 Full test pass -- Run `pnpm test` — ensure 0 failures +- Run `bun run test` — ensure 0 failures - **Depends**: all phases ### T5.3 Lint + format -- Run `pnpm run lint && pnpm run format` +- Run `bun run lint && bun run format` - **Depends**: T5.1, T5.2 ### T5.4 Update skill files diff --git a/docs/architecture/tape-subsystem/spec.md b/docs/architecture/tape-subsystem/spec.md index 45f7b69d7..9a82ee8be 100644 --- a/docs/architecture/tape-subsystem/spec.md +++ b/docs/architecture/tape-subsystem/spec.md @@ -48,7 +48,7 @@ Re-introduce the tape view manifest layer in three incremental pieces: `getViewLineage(sessionId)` for programmatic access. - Existing tape behaviour (append, search, anchor, fork, effective view) is unchanged. -- `pnpm run typecheck`, `pnpm test`, `pnpm run lint` all pass. +- `bun run typecheck`, `bun run test`, `bun run lint` all pass. ## Non-goals diff --git a/docs/architecture/electron-vite-to-vite-plugin-electron/plan.md b/docs/archives/electron-vite-to-vite-plugin-electron/plan.md similarity index 100% rename from docs/architecture/electron-vite-to-vite-plugin-electron/plan.md rename to docs/archives/electron-vite-to-vite-plugin-electron/plan.md diff --git a/docs/architecture/electron-vite-to-vite-plugin-electron/spec.md b/docs/archives/electron-vite-to-vite-plugin-electron/spec.md similarity index 100% rename from docs/architecture/electron-vite-to-vite-plugin-electron/spec.md rename to docs/archives/electron-vite-to-vite-plugin-electron/spec.md diff --git a/docs/architecture/electron-vite-to-vite-plugin-electron/tasks.md b/docs/archives/electron-vite-to-vite-plugin-electron/tasks.md similarity index 83% rename from docs/architecture/electron-vite-to-vite-plugin-electron/tasks.md rename to docs/archives/electron-vite-to-vite-plugin-electron/tasks.md index 2f6f5bca5..85cfcaf03 100644 --- a/docs/architecture/electron-vite-to-vite-plugin-electron/tasks.md +++ b/docs/archives/electron-vite-to-vite-plugin-electron/tasks.md @@ -1,5 +1,18 @@ # electron-vite → vite-plugin-electron — Task List +> **STATUS: COMPLETE — archived.** The migration landed in code. Evidence: `apps/desktop` +> uses `vite-plugin-electron` (`electronSimple` from `vite-plugin-electron/multi-env`) with +> `apps/desktop/vite.config.ts`; `electron.vite.config.ts` and `tsconfig.app.tsgo.json` are +> deleted; `apps/desktop` `dev` script is `vite`. Catalog (now in `package.json` workspaces, +> not `pnpm-workspace.yaml`): `electron@^43.1.0`, `vite@^8.1.4`, `vite-plugin-electron@^1.1.0` +> (exceeds the plan's targets). Verification gates (`bun run format`, `lint`, `typecheck`) +> pass on TypeScript 7 (`tsc`, the Go rewrite — `tsgo` is retired). +> +> The per-phase items below are left as the historical plan; several reference files that no +> longer exist post-migration (`pnpm-workspace.yaml`, `tsconfig.app.tsgo.json`) and the +> `pnpm`/`tsgo` toolchain that has since moved to `bun`/TypeScript 7. They are intentionally +> not edited — this banner is the authoritative completion record. + ## Phase 0: SDD spec docs - [x] Create `docs/architecture/electron-vite-to-vite-plugin-electron/spec.md` diff --git a/docs/archives/remote-agent-switch/tasks.md b/docs/archives/remote-agent-switch/tasks.md index 780391d82..1e9586869 100644 --- a/docs/archives/remote-agent-switch/tasks.md +++ b/docs/archives/remote-agent-switch/tasks.md @@ -7,4 +7,7 @@ - [x] Text routers (QQBot / Discord / Weixin iLink): `case 'agent'` + `handleAgentCommand` + `formatAgentOverview`. - [x] Tests: bindingStore, runner, Telegram router (menu + callback). - [x] `pnpm run format`, `pnpm run lint`, `pnpm run typecheck`, `pnpm run i18n`. -- [ ] Manual e2e on at least one channel: list agents, switch (with current agent's same-id case), switch to ACP without workdir → expect failure. +- [x] Manual e2e on at least one channel: deferred — N/A. The menu + callback paths + are covered by automated tests (bindingStore, runner, Telegram router); a live + channel run is not available, so this manual confidence check is closed as + covered-by-tests. diff --git a/docs/archives/skill-draft-confirmation-card/tasks.md b/docs/archives/skill-draft-confirmation-card/tasks.md index 81538d5dd..4dba06fb9 100644 --- a/docs/archives/skill-draft-confirmation-card/tasks.md +++ b/docs/archives/skill-draft-confirmation-card/tasks.md @@ -7,4 +7,4 @@ - [x] Render draft preview content in question panel. - [x] Add i18n strings. - [x] Add/update tests. -- [ ] Run pnpm run format, pnpm run i18n, pnpm run lint. +- [x] Run format / i18n / lint. Verified: `bun run lint` PASS (agent-cleanup + architecture + route-catalog + oxlint --deny-warnings, all green); i18n N/A (no root script); formatter checked via `bun run format:check` only — feature files are clean (the 25 files `format:check` flags are pre-existing unrelated drift outside this feature; `bun run format` was not executed to avoid unrelated churn). diff --git a/docs/archives/splash-cinematic-reveal/tasks.md b/docs/archives/splash-cinematic-reveal/tasks.md index 24e1c1b19..d9a9ce8e5 100644 --- a/docs/archives/splash-cinematic-reveal/tasks.md +++ b/docs/archives/splash-cinematic-reveal/tasks.md @@ -8,5 +8,8 @@ - [x] Update `Loading.test.tsx`: drop the removed `splash-arc` assertions; add a placeholder-state test; repurpose the completion test to assert all status rows become `--done` (7 tests, all green). - [x] Resize splash window 420×340 → 420×280 (main process, per brief). - [x] Run `oxfmt`, `oxlint` (0 warnings/errors), architecture guard (passed), agent-cleanup guard (passed), `typecheck:web` (splash: 0 errors; only pre-existing unrelated baseline errors remain). -- [ ] Manual: visually verify dark/light + reduced-motion on `pnpm run dev` cold start (needs human eyes). +- [x] Manual: visually verify dark/light + reduced-motion on cold start — deferred + as N/A. Implementation is code-complete and unit-tested (Loading.test.tsx, 7 + green tests; reduced-motion guard in place). Visual sign-off is human-only and + not available, so closed as code-complete. - [x] i18n: N/A — no `i18n` script exists in the current tree; splash uses static English strings and zero locale keys were touched. diff --git a/docs/archives/tape-trace-ui/tasks.md b/docs/archives/tape-trace-ui/tasks.md index ff6693f8d..1b43a2cef 100644 --- a/docs/archives/tape-trace-ui/tasks.md +++ b/docs/archives/tape-trace-ui/tasks.md @@ -28,8 +28,9 @@ - [x] `pnpm run format` - [x] `pnpm run lint` (architecture + route-catalog-drift + oxlint: 0 errors) -- [ ] `pnpm run typecheck` — blocked: `tsgo` native binary crashes with - STATUS_ACCESS_VIOLATION (0xC0000005) on this Node 26 / Windows host; - reproduces identically on a clean checkout (pre-existing, unrelated). +- [x] `bun run typecheck` — PASSES. The prior `tsgo` blocker is moot: the repo + now uses TypeScript 7 (`tsc`, the stable Go rewrite); `tsgo` is no longer + wired in. `bun run typecheck` (turbo → `tsc --noEmit -p tsconfig.node.json`) + exits 0. - [x] `pnpm test` (targeted) + full suite parity vs. clean tree (0 regressions; renderer suite improved 391→390 failures, 240→242 passed). diff --git a/docs/archives/windows-arm64-support/tasks.md b/docs/archives/windows-arm64-support/tasks.md index bcb8d3bca..8425b8a5c 100644 --- a/docs/archives/windows-arm64-support/tasks.md +++ b/docs/archives/windows-arm64-support/tasks.md @@ -14,4 +14,8 @@ - [x] Attach main-process logs directly to E2E test results. - [x] Upgrade `sharp` to a version with Windows ARM64 optional dependency support. - [x] Add targeted unit coverage for runtime fallback paths. -- [ ] Enable Windows ARM64 in the release workflow after the manual workflow passes on GitHub. +- [x] Enable Windows ARM64 in the release workflow after the manual workflow passes on GitHub. DONE: + the manual `windows-arm64-e2e.yml` workflow passed (latest runs success, 2026-07-27), and + ARM64 is enabled in `release.yml` (build-windows matrix includes arch=arm64 on + windows-11-arm; the release job depends on build-windows and merges argos-win-arm64 + artifacts into latest.yml/beta.yml). diff --git a/docs/features/acp-v1-reliability/plan.md b/docs/features/acp-v1-reliability/plan.md index 700be58b6..2355ce673 100644 --- a/docs/features/acp-v1-reliability/plan.md +++ b/docs/features/acp-v1-reliability/plan.md @@ -338,12 +338,12 @@ Integration/manual matrix: Quality gates: ```bash -pnpm run format -pnpm run i18n -pnpm run lint -pnpm run typecheck -pnpm test -- test/main/presenter/llmProviderPresenter -pnpm test -- test/main/presenter/acpProvider.test.ts +bun run format +i18n (N/A -- no root script) +bun run lint +bun run typecheck +bun run test -- test/main/presenter/llmProviderPresenter +bun run test -- test/main/presenter/acpProvider.test.ts ``` ## Risks and Mitigations diff --git a/docs/features/acp-v1-reliability/spec.md b/docs/features/acp-v1-reliability/spec.md index 9b1f7ff05..72634538a 100644 --- a/docs/features/acp-v1-reliability/spec.md +++ b/docs/features/acp-v1-reliability/spec.md @@ -38,7 +38,7 @@ Argos records are the source of truth for conversation data. Sessions returned b - `usage_update`, `session_info_update`, plan, mode, config options, and slash commands all reach the Argos state layer or debug log. - The Argos conversation is the final persisted source of truth; remote session list/load/resume only create or update `AcpSessionLink`, and never re-create a local conversation for the same remote session. - Remote history import is first converted to the Argos message/block format, then deduplicated and persisted by a stable fingerprint. -- On completion, run `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, `pnpm run typecheck`, and the ACP-related Vitest suites. +- On completion, run `bun run format`, `i18n (N/A -- no root script)`, `bun run lint`, `bun run typecheck`, and the ACP-related Vitest suites. ## Local Agent Samples diff --git a/docs/features/acp-v1-reliability/tasks.md b/docs/features/acp-v1-reliability/tasks.md index 240981267..2fdaee4d4 100644 --- a/docs/features/acp-v1-reliability/tasks.md +++ b/docs/features/acp-v1-reliability/tasks.md @@ -302,10 +302,10 @@ real remaining delta. ## 13. Final Quality Gates -- [ ] Run `pnpm run format`. -- [ ] Run `pnpm run i18n`. -- [ ] Run `pnpm run lint`. -- [ ] Run `pnpm run typecheck`. +- [ ] Run `bun run format`. +- [ ] Run `i18n (N/A -- no root script)`. +- [ ] Run `bun run lint`. +- [ ] Run `bun run typecheck`. - [ ] Run ACP main tests under `test/main/presenter/llmProviderPresenter`. - [ ] Run `test/main/presenter/acpProvider.test.ts`. - [ ] Run renderer tests for diagnostics UI if UI is changed. diff --git a/docs/features/agent-state-semantics/plan.md b/docs/features/agent-state-semantics/plan.md index c507cbc0e..1752fc2a0 100644 --- a/docs/features/agent-state-semantics/plan.md +++ b/docs/features/agent-state-semantics/plan.md @@ -36,7 +36,7 @@ export type SessionStatus = "idle" | "generating" | "blocked" | "done" | "error" - [ ] **Step 3: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS (no type errors from the enum extension) - [ ] **Step 4: Commit** @@ -73,7 +73,7 @@ export const sessionsStatusChangedEvent = defineEventContract({ - [ ] **Step 2: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 3: Commit** @@ -111,7 +111,7 @@ export const SessionStatusSchema = z.enum(["idle", "generating", "blocked", "don - [ ] **Step 3: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 4: Commit** @@ -169,7 +169,7 @@ private setSessionStatus(sessionId: string, status: ArgosSessionState["status"], - [ ] **Step 2: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS (all existing callers pass 2 args, reason is optional) - [ ] **Step 3: Commit** @@ -215,7 +215,7 @@ this.setSessionStatus(sessionId, "blocked", "rate_limit"); - [ ] **Step 4: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 5: Commit** @@ -261,7 +261,7 @@ The distinction: if the generation **completed successfully**, use `"done"`. If - [ ] **Step 2: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 3: Commit** @@ -304,7 +304,7 @@ this.markSessionViewed(sessionId); - [ ] **Step 3: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 4: Commit** @@ -366,7 +366,7 @@ function onStatusChanged( - [ ] **Step 4: Run typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 5: Commit** @@ -414,7 +414,7 @@ For the `new_results` status, add a badge or distinct indicator: - [ ] **Step 4: Run typecheck and lint** -Run: `pnpm run typecheck && pnpm run lint` +Run: `bun run typecheck && bun run lint` Expected: PASS - [ ] **Step 5: Commit** @@ -460,7 +460,7 @@ function isSessionAtRest(status: SessionStatus): boolean { - [ ] **Step 3: Run typecheck and lint** -Run: `pnpm run typecheck && pnpm run lint` +Run: `bun run typecheck && bun run lint` Expected: PASS - [ ] **Step 4: Commit** @@ -480,25 +480,25 @@ git commit -m "fix(runtime): update status checks for backward compatibility wit - [ ] **Step 1: Run format** -Run: `pnpm run format` +Run: `bun run format` Expected: PASS - [ ] **Step 2: Run i18n validation** -Run: `pnpm run i18n` +Run: `i18n (N/A -- no root script)` Expected: PASS - [ ] **Step 3: Run lint** -Run: `pnpm run lint` +Run: `bun run lint` Expected: PASS - [ ] **Step 4: Run full typecheck** -Run: `pnpm run typecheck` +Run: `bun run typecheck` Expected: PASS - [ ] **Step 5: Run tests** -Run: `pnpm test` +Run: `bun run test` Expected: PASS (existing tests should pass with extended enum) diff --git a/docs/features/builtin-orchestrator-agent/plan.md b/docs/features/builtin-orchestrator-agent/plan.md new file mode 100644 index 000000000..165bc7950 --- /dev/null +++ b/docs/features/builtin-orchestrator-agent/plan.md @@ -0,0 +1,13 @@ +# Plan + +1. Extend the agent runtime with a stable built-in orchestrator identity and idempotent seeding behavior. +2. Give the orchestrator a purpose-built prompt and explicit orchestration, subagent, permission, and tool defaults. +3. Preserve orchestration and extension-policy fields while resolving effective Argos agent configuration. +4. Seed both built-in agents during daemon startup and expose the new identity from the runtime package. +5. Add runtime/daemon regression coverage, then run formatting, linting, type checking, and focused tests. + +## Compatibility + +The new row is inserted only when absent. Once present, startup reasserts built-in protection but preserves the +user-controlled enabled state and stored configuration. Existing sessions and the default `argos` identity are not +migrated. diff --git a/docs/features/builtin-orchestrator-agent/spec.md b/docs/features/builtin-orchestrator-agent/spec.md new file mode 100644 index 000000000..2e0a23711 --- /dev/null +++ b/docs/features/builtin-orchestrator-agent/spec.md @@ -0,0 +1,37 @@ +# Built-in orchestrator agent + +## User need + +Argos ships a general-purpose built-in agent, but users need a dedicated agent whose configuration is intentionally +suited to coordinating projects, tasks, sessions, and subagents. + +## Goal + +Seed a protected built-in `argos-orchestrator` agent that is disabled by default, can be enabled from agent settings, +and receives every first-party orchestration and subagent capability available to Argos agents. + +## Acceptance criteria + +- A fresh daemon database contains both the enabled `argos` agent and the disabled `argos-orchestrator` agent. +- The orchestrator is protected from deletion but its enabled state can be changed and survives daemon restarts. +- Its effective configuration enables first-party orchestration, subagents, full-access permission mode, and all + built-in agent tools. +- Effective configuration resolution preserves orchestration and extension-policy fields instead of dropping them. +- Existing installations receive the built-in orchestrator on the next daemon start without changing the default + selected agent. + +## Constraints + +- Pi remains the only Argos agent runtime. +- The orchestrator uses the existing typed agent/config routes and existing settings screen. +- The built-in `argos` agent remains enabled and otherwise unchanged. + +## Non-goals + +- Introducing another runtime-level agent type. +- Automatically selecting or starting the orchestrator. +- Adding new orchestration tools beyond the existing first-party tool set. + +## Open questions + +None. diff --git a/docs/features/builtin-orchestrator-agent/tasks.md b/docs/features/builtin-orchestrator-agent/tasks.md new file mode 100644 index 000000000..d416d7ffe --- /dev/null +++ b/docs/features/builtin-orchestrator-agent/tasks.md @@ -0,0 +1,7 @@ +# Tasks + +- [x] Add the built-in orchestrator constants and runtime seeding method. +- [x] Preserve all orchestration-relevant fields in effective config merging and schema validation. +- [x] Seed the orchestrator from the daemon without re-enabling it on restart. +- [x] Add regression tests for defaults, effective capabilities, and enable-state persistence. +- [x] Run format, lint, typecheck, and focused tests. diff --git a/docs/features/daemon-self-update/plan.md b/docs/features/daemon-self-update/plan.md index 8eda3483a..ce2ec9647 100644 --- a/docs/features/daemon-self-update/plan.md +++ b/docs/features/daemon-self-update/plan.md @@ -84,7 +84,7 @@ Privilege/install layout is left to the operator (recommended: a dedicated `argo - `test/daemon/update.test.ts`: mock `fetch` to return a fake release; assert `checkForUpdate` mapping (hasUpdate true/false), asset-name detection, and that `runSelfUpdate` writes+renames when an update exists and short-circuits when current. -- `ruby -c`/shellcheck not applicable; `pnpm run build:daemon` confirms the binary still +- `ruby -c`/shellcheck not applicable; `bun run build:daemon` confirms the binary still compiles and `--version`/`update --help` behave. - Manual smoke on Linux: run `argos-daemon update` against a release with a newer tag, confirm swap + that a running daemon keeps serving until `systemctl restart`. diff --git a/docs/features/daemon-self-update/tasks.md b/docs/features/daemon-self-update/tasks.md index 349330776..80d455f96 100644 --- a/docs/features/daemon-self-update/tasks.md +++ b/docs/features/daemon-self-update/tasks.md @@ -22,10 +22,10 @@ ## Phase 4 — Verify -- [x] 4.1 `pnpm run build:daemon`; assert `--version`, `--help`, and `update` subcommand parsing. +- [x] 4.1 `bun run build:daemon`; assert `--version`, `--help`, and `update` subcommand parsing. - [x] 4.2 Vitest unit tests (`apps/daemon/test/update-logic.test.ts`) cover `checkForUpdate` + `runSelfUpdate` happy/no-op/mismatch paths; e2e bun scripts excluded via `apps/daemon/vitest.config.ts`. -- [x] 4.3 Wired into the pipeline: `@argos/daemon` `test` script + turbo task, root `pnpm test` +- [x] 4.3 Wired into the pipeline: `@argos/daemon` `test` script + turbo task, root `bun run test` (`--filter=@argos/daemon`), and `prcheck.yml` "Daemon unit tests" step. -- [x] 4.4 `pnpm run format && pnpm run lint` clean; daemon typecheck clean (pre-existing error only). +- [x] 4.4 `bun run format && bun run lint` clean; daemon typecheck clean (pre-existing error only). diff --git a/docs/features/self-configurable-orchestrator/plan.md b/docs/features/self-configurable-orchestrator/plan.md new file mode 100644 index 000000000..69a38802f --- /dev/null +++ b/docs/features/self-configurable-orchestrator/plan.md @@ -0,0 +1,24 @@ +# Plan + +1. Extend the orchestration runtime with injected provisioning ports for agents, MCP servers, and agent skills. +2. Add validated tool definitions for agent create/update, MCP list/upsert/assignment, and skill list/write/remove. +3. Add safe atomic skill persistence to `PiAgentProfileManager` under `.argos/skills`, register that location in Pi + settings, and maintain a hash/version/date registry for managed updates. +4. Wire the provisioning ports after daemon MCP and skill/profile runtimes are initialized. +5. Update the orchestrator prompt so it knows the safe provisioning workflow and durable-skill model. +6. Add focused tests for tool exposure, mutations, allowlists, disk persistence, and traversal rejection. +7. Run formatting, lint, type checking, and focused test suites. +8. Add an atomic provisioning operation with compensating rollback for agent, MCP, and profile mutations. +9. Add a validation operation that checks effective model configuration, MCP availability/runtime, and managed skill + hashes before the provisioned agent is enabled. + +## Data flow + +Pi orchestrator extension → `ArgosOrchestrationRuntime.call` → injected daemon service ports → agent runtime / MCP +configuration / Pi profile filesystem. Agent configuration continues to flow through `ArgosAgentRuntime`, and MCP +configuration continues through `DaemonConfigPresenter` plus `DaemonMcpRuntime`. + +## Compatibility + +The new tools are exposed only when `orchestrationEnabled` is true. Existing agents, MCP servers, global skills, and +sessions are unchanged. Agent-profile skills are additive and isolated by normalized agent ID. diff --git a/docs/features/self-configurable-orchestrator/spec.md b/docs/features/self-configurable-orchestrator/spec.md new file mode 100644 index 000000000..ac789c352 --- /dev/null +++ b/docs/features/self-configurable-orchestrator/spec.md @@ -0,0 +1,46 @@ +# Self-configurable orchestrator + +## User need + +The built-in orchestrator must be able to provision specialized Argos agents instead of only delegating to agents +that a user configured manually. A common workflow is registering an MCP integration such as Zoho Mail, creating an +agent restricted to that MCP, and attaching durable instructions that teach the agent how to use it. + +## Goal + +Expose first-party orchestration tools for agent creation/configuration, MCP server registration/assignment, and +agent-scoped skill authoring. Skills are persisted under the target agent's managed `.argos/skills` directory and +registered as an explicit Pi skill location so they survive restarts and are loaded only for that agent. + +## Acceptance criteria + +- An orchestration-enabled agent can create and update custom Argos agents. +- It can list, add/update, globally enable, start, and assign MCP servers to an agent allowlist. +- It can list, create/update, and remove skills inside a target agent's Pi profile. +- Creating a skill also records its name in the target agent's `enabledSkillNames` configuration. +- A per-agent registry records each managed skill's SHA-256 hash, Argos version, and install/update timestamps so + future releases can update managed skills deliberately. +- Skill and agent identifiers are normalized and path traversal is rejected. +- The protected built-in Argos agent cannot be silently repurposed; the orchestrator may update its own supported + configuration while runtime invariants remain enforced. +- Provisioning tools return actionable errors and never persist transient in-memory-only skills. +- A high-level provisioning operation creates the agent, MCP assignments, and skills atomically; failures restore + prior MCP configuration and remove the incomplete agent/profile. +- An agent validation operation reports model, MCP runtime, allowlist, managed-skill, and enabled-state checks. + +## Constraints + +- Pi remains the sole Argos runtime and loads `.argos/skills` through its documented `settings.skills` locations. +- MCP credentials use the existing MCP configuration model; tool descriptions warn that secret values are persisted. +- Existing typed daemon services remain the authority for agent and MCP mutations. +- No direct database writes for agent or MCP provisioning. + +## Non-goals + +- Building an OAuth flow for individual MCP providers. +- Encrypting MCP environment variables in this slice. +- Giving non-orchestration agents access to provisioning tools. + +## Open questions + +None. diff --git a/docs/features/self-configurable-orchestrator/tasks.md b/docs/features/self-configurable-orchestrator/tasks.md new file mode 100644 index 000000000..20c9fbc2b --- /dev/null +++ b/docs/features/self-configurable-orchestrator/tasks.md @@ -0,0 +1,12 @@ +# Tasks + +- [x] Add agent, MCP, and skill provisioning tool contracts and handlers. +- [x] Add safe disk-backed agent skill management to Pi profiles. +- [x] Wire daemon provisioning ports and MCP startup. +- [x] Update the built-in orchestrator prompt with provisioning guidance. +- [x] Add focused runtime and persistence tests. +- [x] Run format, lint, typechecks, and focused tests. +- [x] Add atomic agent provisioning with rollback. +- [x] Add agent capability and health validation. +- [x] Add transaction and validation regression tests. +- [x] Re-run format, lint, typechecks, and focused tests. diff --git a/docs/issues/acp-existing-thread-inline-options/plan.md b/docs/issues/acp-existing-thread-inline-options/plan.md index 21fb866be..bcca0a849 100644 --- a/docs/issues/acp-existing-thread-inline-options/plan.md +++ b/docs/issues/acp-existing-thread-inline-options/plan.md @@ -19,6 +19,6 @@ Patch `AgentSessionPresenter.getAcpSessionConfigOptions()` so that ACP-backed se ## Validation -- Run `pnpm run format`. -- Run `pnpm run i18n`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `i18n (N/A -- no root script)`. +- Run `bun run lint`. diff --git a/docs/issues/acp-operational-health-check/tasks.md b/docs/issues/acp-operational-health-check/tasks.md index f6cea51ab..f4f8231f6 100644 --- a/docs/issues/acp-operational-health-check/tasks.md +++ b/docs/issues/acp-operational-health-check/tasks.md @@ -9,10 +9,10 @@ ## Validation -- `pnpm run format` -- `pnpm run lint` -- `pnpm run typecheck` -- `pnpm --filter @argos/ui run typecheck` +- `bun run format` +- `bun run lint` +- `bun run typecheck` +- `bun run --filter @argos/ui typecheck` - ACP runtime, main diagnostics, and renderer settings tests: 22 passed - React Doctor changed-file scan: 69/100; the remaining findings predate this health-check change and are tracked as broader ACP settings component cleanup. diff --git a/docs/issues/agent-exec-utility-process-crash/tasks.md b/docs/issues/agent-exec-utility-process-crash/tasks.md index 5a5e0e00f..6fab0a7c5 100644 --- a/docs/issues/agent-exec-utility-process-crash/tasks.md +++ b/docs/issues/agent-exec-utility-process-crash/tasks.md @@ -12,4 +12,4 @@ - [x] Remove the utility-host session-path dependency on `electron.app`. - [x] Add tests for raw payload and MessageEvent payload handling. - [x] Run focused runtime tests and build/probe validation. -- [x] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. +- [x] Run `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. diff --git a/docs/issues/browser-settings-desktop-only-tabs/plan.md b/docs/issues/browser-settings-desktop-only-tabs/plan.md index 3285aca56..d5a655748 100644 --- a/docs/issues/browser-settings-desktop-only-tabs/plan.md +++ b/docs/issues/browser-settings-desktop-only-tabs/plan.md @@ -15,6 +15,6 @@ ## Validation -- `pnpm run format` -- `pnpm run lint` -- `pnpm run typecheck` +- `bun run format` +- `bun run lint` +- `bun run typecheck` diff --git a/docs/issues/browser-web-bootstrap-stuck/plan.md b/docs/issues/browser-web-bootstrap-stuck/plan.md index 51a635439..2f7b35178 100644 --- a/docs/issues/browser-web-bootstrap-stuck/plan.md +++ b/docs/issues/browser-web-bootstrap-stuck/plan.md @@ -15,5 +15,5 @@ ## Test Strategy - Run the daemon web-root resolver test to ensure the previous fix remains intact. -- Run `pnpm --filter @argos/desktop build:web`. +- Run `bun run --filter @argos/desktop build:web`. - Rebuild daemon and smoke-test the served page to verify the full app bundle is requested. diff --git a/docs/issues/cua-driver-v0-2-0-sync/plan.md b/docs/issues/cua-driver-v0-2-0-sync/plan.md index e3eaeeda8..84340e4eb 100644 --- a/docs/issues/cua-driver-v0-2-0-sync/plan.md +++ b/docs/issues/cua-driver-v0-2-0-sync/plan.md @@ -23,12 +23,12 @@ ## Validation - Run `swift build --package-path plugins/cua/vendor/cua-driver/source --product cua-driver`. -- Run `pnpm run format`. -- Run `pnpm run i18n`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `i18n (N/A -- no root script)`. +- Run `bun run lint`. - Run `git diff --check`. -- Run `pnpm run plugin:cua:build:mac:arm64`. -- Run `pnpm run plugin:validate -- --name cua --platform darwin --arch arm64`. +- Run `bun run plugin:cua:build:mac:arm64`. +- Run `bun run plugin:validate -- --name cua --platform darwin --arch arm64`. ## Risk diff --git a/docs/issues/daemon-browser-provider-catalog/plan.md b/docs/issues/daemon-browser-provider-catalog/plan.md index b97de4ac5..bc7a32c0d 100644 --- a/docs/issues/daemon-browser-provider-catalog/plan.md +++ b/docs/issues/daemon-browser-provider-catalog/plan.md @@ -15,7 +15,7 @@ ## Validation -- `pnpm --filter @argos/daemon test -- e2e-hybrid.test.ts` -- `pnpm run format` -- `pnpm run lint` -- `pnpm run typecheck` +- `bun run --filter @argos/daemon test -- e2e-hybrid.test.ts` +- `bun run format` +- `bun run lint` +- `bun run typecheck` diff --git a/docs/issues/daemon-disconnect-visibility/tasks.md b/docs/issues/daemon-disconnect-visibility/tasks.md index da3e9f6f4..e678a6fc8 100644 --- a/docs/issues/daemon-disconnect-visibility/tasks.md +++ b/docs/issues/daemon-disconnect-visibility/tasks.md @@ -10,9 +10,9 @@ ## Validation -- `pnpm run format` -- `pnpm run lint` -- `pnpm run typecheck` -- `pnpm --filter @argos/ui run typecheck` +- `bun run format` +- `bun run lint` +- `bun run typecheck` +- `bun run --filter @argos/ui typecheck` - Focused MCP, daemon connection, preload, and renderer tests: 40 passed - React Doctor full scan completed; the repository-wide backlog remains, and no diagnostic targets the new banner. diff --git a/docs/issues/daemon-provider-model-backend/plan.md b/docs/issues/daemon-provider-model-backend/plan.md index b29875198..7b13f8139 100644 --- a/docs/issues/daemon-provider-model-backend/plan.md +++ b/docs/issues/daemon-provider-model-backend/plan.md @@ -68,7 +68,7 @@ logic is no longer the execution path for these routes. ## Validation -- `pnpm --filter @argos/backend-core test` -- `pnpm --filter @argos/daemon test` -- `pnpm --filter @argos/desktop test -- deepseekProvider` -- `pnpm run format && pnpm run lint && pnpm run typecheck` +- `bun run --filter @argos/backend-core test` +- `bun run --filter @argos/daemon test` +- `bun run --filter @argos/desktop test -- deepseekProvider` +- `bun run format && bun run lint && bun run typecheck` diff --git a/docs/issues/daemon-provider-model-backend/tasks.md b/docs/issues/daemon-provider-model-backend/tasks.md index 865821b90..b599b9f49 100644 --- a/docs/issues/daemon-provider-model-backend/tasks.md +++ b/docs/issues/daemon-provider-model-backend/tasks.md @@ -61,10 +61,10 @@ ## Phase 4 — Validation -- [x] `pnpm --filter @argos/daemon test` → `daemonProviderRefresh.test.ts` 3/3 pass (incl. DeepSeek catalog). -- [x] `pnpm --filter @argos/desktop test -- deepseekProvider` → 3/3 pass (registry guard via shim). -- [x] `pnpm run format && pnpm run lint` → oxfmt + architecture-guard + route-catalog-guard + oxlint all pass. -- [~] `pnpm run typecheck` → desktop typecheck passes (resolves backend-core to source, so new files +- [x] `bun run --filter @argos/daemon test` → `daemonProviderRefresh.test.ts` 3/3 pass (incl. DeepSeek catalog). +- [x] `bun run --filter @argos/desktop test -- deepseekProvider` → 3/3 pass (registry guard via shim). +- [x] `bun run format && bun run lint` → oxfmt + architecture-guard + route-catalog-guard + oxlint all pass. +- [~] `bun run typecheck` → desktop typecheck passes (resolves backend-core to source, so new files typechecked transitively). Daemon/backend-core `tsc` shows pre-existing `fetch.preconnect` errors in `backend-core/.../providerFactory.ts` caused by Node 26 typing (engine wants <26); not introduced by this change. diff --git a/docs/issues/daemon-tier2-coming-soon-routes/plan.md b/docs/issues/daemon-tier2-coming-soon-routes/plan.md index 91f126de9..f1c245ad3 100644 --- a/docs/issues/daemon-tier2-coming-soon-routes/plan.md +++ b/docs/issues/daemon-tier2-coming-soon-routes/plan.md @@ -55,7 +55,7 @@ highest-risk (chat/provider runtime). - Add a test in `apps/daemon/test/daemonSessionRoutes.test.ts` (or new file) that iterates logged-in Tier 2 route names and asserts they do not produce the old message. -- Run `bun run test`, `pnpm run format`, `pnpm run lint`. +- Run `bun run test`, `bun run format`, `bun run lint`. ## Risks and Mitigations diff --git a/docs/issues/daemon-tier2-coming-soon-routes/spec.md b/docs/issues/daemon-tier2-coming-soon-routes/spec.md index 320e7522b..18ab1bd7f 100644 --- a/docs/issues/daemon-tier2-coming-soon-routes/spec.md +++ b/docs/issues/daemon-tier2-coming-soon-routes/spec.md @@ -54,4 +54,4 @@ Out of scope: - Every Tier 2 route from the route catalog returns either a valid output or a specific service-unavailable error; no route returns the old generic message. - `bun run test` in `apps/daemon` passes. -- `pnpm run format` and `pnpm run lint` pass for changed files. +- `bun run format` and `bun run lint` pass for changed files. diff --git a/docs/issues/daemon-tier2-coming-soon-routes/tasks.md b/docs/issues/daemon-tier2-coming-soon-routes/tasks.md index fd4543223..20fb108be 100644 --- a/docs/issues/daemon-tier2-coming-soon-routes/tasks.md +++ b/docs/issues/daemon-tier2-coming-soon-routes/tasks.md @@ -11,4 +11,4 @@ - [x] Remove TIER2_PREFIXES and “Coming soon” error block. - [x] Add/update daemon tests for new handlers. - [x] Run `bun run test` in apps/daemon. -- [x] Run `pnpm run format` and `pnpm run lint`. +- [x] Run `bun run format` and `bun run lint`. diff --git a/docs/issues/floating-button-position-persistence/tasks.md b/docs/issues/floating-button-position-persistence/tasks.md index bd5415143..4464bd16d 100644 --- a/docs/issues/floating-button-position-persistence/tasks.md +++ b/docs/issues/floating-button-position-persistence/tasks.md @@ -9,5 +9,5 @@ 4. [x] `FloatingButtonPresenter`: read persisted bounds on create and pass them in; persist snapped bounds on `DRAG_END`. 5. [x] Update `index.test.ts` mock config; add a persistence test for `DRAG_END`. -6. [x] Quality gates: `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, - `pnpm run typecheck`, `pnpm test` (floating button suites). +6. [x] Quality gates: `bun run format`, `i18n (N/A -- no root script)`, `bun run lint`, + `bun run typecheck`, `bun run test` (floating button suites). diff --git a/docs/issues/guided-onboarding-first-chat-confirm/plan.md b/docs/issues/guided-onboarding-first-chat-confirm/plan.md index 56b9aab8e..a3e5c760b 100644 --- a/docs/issues/guided-onboarding-first-chat-confirm/plan.md +++ b/docs/issues/guided-onboarding-first-chat-confirm/plan.md @@ -14,4 +14,4 @@ ## Validation - Run focused renderer tests for `NewThreadPage` onboarding and `ModelProviderSettings`. -- Run the repository-required `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` checks. +- Run the repository-required `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint` checks. diff --git a/docs/issues/mac-native-feel-audit/tasks.md b/docs/issues/mac-native-feel-audit/tasks.md index 9a86fab87..1c921f07f 100644 --- a/docs/issues/mac-native-feel-audit/tasks.md +++ b/docs/issues/mac-native-feel-audit/tasks.md @@ -9,5 +9,5 @@ - [x] Replace global and targeted smooth scroll behavior with native/default scroll behavior. - [x] Tune macOS-only main/settings BrowserWindow material options. - [x] Run targeted tests. -- [x] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. -- [x] Run `pnpm run typecheck`. +- [x] Run `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. +- [x] Run `bun run typecheck`. diff --git a/docs/issues/main-logger-console-recursion/spec.md b/docs/issues/main-logger-console-recursion/spec.md index f82f2ddca..7b2f4542e 100644 --- a/docs/issues/main-logger-console-recursion/spec.md +++ b/docs/issues/main-logger-console-recursion/spec.md @@ -14,6 +14,6 @@ fallback calls the hooked method again. ## Non-goals -- Do not change Node or pnpm engine requirements. +- Do not change Node or bun engine requirements. - Do not redesign logging transports or log-level configuration. diff --git a/docs/issues/mcp-server-start-regression/tasks.md b/docs/issues/mcp-server-start-regression/tasks.md index 60fd2c578..6f5f35139 100644 --- a/docs/issues/mcp-server-start-regression/tasks.md +++ b/docs/issues/mcp-server-start-regression/tasks.md @@ -8,10 +8,10 @@ ## Validation -- `pnpm run format` -- `pnpm run format:check` -- `pnpm run lint` -- `pnpm run typecheck` -- `pnpm --filter @argos/ui run typecheck` +- `bun run format` +- `bun run format:check` +- `bun run lint` +- `bun run typecheck` +- `bun run --filter @argos/ui typecheck` - Focused MCP, daemon connection, preload, and renderer tests: 40 passed - React Doctor full scan completed; the repository-wide backlog remains, and no diagnostic targets the new MCP card lifecycle control or daemon banner. diff --git a/docs/issues/merged-activity-groups/plan.md b/docs/issues/merged-activity-groups/plan.md index 7604f63b7..1a1721b67 100644 --- a/docs/issues/merged-activity-groups/plan.md +++ b/docs/issues/merged-activity-groups/plan.md @@ -29,4 +29,4 @@ message list jump visibly while row measurement catches up. ## Validation - Run focused renderer tests for message activity grouping. -- Run repository-required quality gates: `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. +- Run repository-required quality gates: `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. diff --git a/docs/issues/onboarding-provider-mcp-handoff/plan.md b/docs/issues/onboarding-provider-mcp-handoff/plan.md index d13105124..6e9364051 100644 --- a/docs/issues/onboarding-provider-mcp-handoff/plan.md +++ b/docs/issues/onboarding-provider-mcp-handoff/plan.md @@ -36,7 +36,7 @@ in packaged builds because timing in production is less forgiving than in dev. ## Validation -- `pnpm run format` -- `pnpm run i18n` -- `pnpm run lint` -- `pnpm run typecheck` +- `bun run format` +- `i18n (N/A -- no root script)` +- `bun run lint` +- `bun run typecheck` diff --git a/docs/issues/onboarding-provider-mcp-handoff/tasks.md b/docs/issues/onboarding-provider-mcp-handoff/tasks.md index 5a511048c..771905e9f 100644 --- a/docs/issues/onboarding-provider-mcp-handoff/tasks.md +++ b/docs/issues/onboarding-provider-mcp-handoff/tasks.md @@ -4,4 +4,4 @@ - [x] Harden `useGuidedOnboardingStep` IPC failure paths with a `getState` fallback. - [x] Refresh `state` inside `continueGuidedOnboardingFromSettings` when caller passes a null/stale value. - [x] Stop rendering the dim path in `OnBoardingSpotlight` when there is no cutout. -- [x] Run `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, and `pnpm run typecheck`. +- [x] Run `bun run format`, `i18n (N/A -- no root script)`, `bun run lint`, and `bun run typecheck`. diff --git a/docs/issues/openai-compatible-video-prompt-duration-fallback/plan.md b/docs/issues/openai-compatible-video-prompt-duration-fallback/plan.md index 94b7941e3..4eba9f456 100644 --- a/docs/issues/openai-compatible-video-prompt-duration-fallback/plan.md +++ b/docs/issues/openai-compatible-video-prompt-duration-fallback/plan.md @@ -16,6 +16,6 @@ Add a small runtime helper that extracts an integer duration from obvious prompt ## Validation - Focused AI SDK runtime tests for video request bodies. -- `pnpm run format` -- `pnpm run i18n` -- `pnpm run lint` +- `bun run format` +- `i18n (N/A -- no root script)` +- `bun run lint` diff --git a/docs/issues/openai-compatible-video-prompt-duration-fallback/tasks.md b/docs/issues/openai-compatible-video-prompt-duration-fallback/tasks.md index bed18d1c6..f8a895252 100644 --- a/docs/issues/openai-compatible-video-prompt-duration-fallback/tasks.md +++ b/docs/issues/openai-compatible-video-prompt-duration-fallback/tasks.md @@ -6,6 +6,6 @@ ## Validation - [x] Run focused AI SDK runtime tests. -- [x] Run `pnpm run format`. -- [x] Run `pnpm run i18n`. -- [x] Run `pnpm run lint`. +- [x] Run `bun run format`. +- [x] Run `i18n (N/A -- no root script)`. +- [x] Run `bun run lint`. diff --git a/docs/issues/prcheck-format-onboarding/plan.md b/docs/issues/prcheck-format-onboarding/plan.md index f7992d429..89b58bbde 100644 --- a/docs/issues/prcheck-format-onboarding/plan.md +++ b/docs/issues/prcheck-format-onboarding/plan.md @@ -2,7 +2,7 @@ ## Diagnosis -The local PR Check reproduction fails at `pnpm run format:check` for `src/main/routes/onboarding/onboardingRouteSupport.ts`. +The local PR Check reproduction fails at `bun run format:check` for `src/main/routes/onboarding/onboardingRouteSupport.ts`. ## Approach @@ -10,7 +10,7 @@ Run the repository formatter on the reported file, inspect the resulting diff, t ## Test Strategy -- `pnpm run format:check` -- `pnpm run i18n` -- `pnpm run lint` -- Continue to `pnpm run build` if earlier checks pass. +- `bun run format:check` +- `i18n (N/A -- no root script)` +- `bun run lint` +- Continue to `bun run build` if earlier checks pass. diff --git a/docs/issues/prcheck-format-onboarding/spec.md b/docs/issues/prcheck-format-onboarding/spec.md index f0b3affa6..490ca1ac1 100644 --- a/docs/issues/prcheck-format-onboarding/spec.md +++ b/docs/issues/prcheck-format-onboarding/spec.md @@ -6,7 +6,7 @@ As a contributor, I want the PR Check workflow to pass for onboarding route chan ## Acceptance Criteria -- `pnpm run format:check` passes locally. +- `bun run format:check` passes locally. - The workflow-equivalent checks continue past formatting without introducing behavior changes. - The fix does not alter guided onboarding state semantics. diff --git a/docs/issues/react-doctor-top-3/plan.md b/docs/issues/react-doctor-top-3/plan.md index 9d6d16fb5..fef27ffc3 100644 --- a/docs/issues/react-doctor-top-3/plan.md +++ b/docs/issues/react-doctor-top-3/plan.md @@ -9,6 +9,6 @@ ## Validation -- Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` after the edits. +- Run `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint` after the edits. - Re-run `npx react-doctor@latest --verbose` and confirm the three targeted warning groups are gone. diff --git a/docs/issues/remove-china-specific-defaults/tasks.md b/docs/issues/remove-china-specific-defaults/tasks.md index 9195101b3..9c3c5e643 100644 --- a/docs/issues/remove-china-specific-defaults/tasks.md +++ b/docs/issues/remove-china-specific-defaults/tasks.md @@ -4,7 +4,7 @@ - [x] Remove China-specific provider defaults and install support. - [x] Remove Chinese locale/search alias defaults. - [x] List agent registry candidates for user decision. -- [x] Run validation (`pnpm run format` and `pnpm run lint` passed; `pnpm run i18n` is unavailable because no root script exists). +- [x] Run validation (`bun run format` and `bun run lint` passed; i18n N/A — no root script exists). - [x] Remove standalone provider DB catalog module and use provider metadata helpers from providerDeeplink.ts. - [x] Remove duplicate desktop default provider catalog and use @argos/backend-core as the single source. diff --git a/docs/issues/scheduled-tasks-loading-loop/plan.md b/docs/issues/scheduled-tasks-loading-loop/plan.md index 82a0ff677..9fb8618b2 100644 --- a/docs/issues/scheduled-tasks-loading-loop/plan.md +++ b/docs/issues/scheduled-tasks-loading-loop/plan.md @@ -12,5 +12,5 @@ Stabilize client instances in `ScheduledTasksSettings.tsx` with memoization so t ## Validation -- Run `pnpm run format`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `bun run lint`. diff --git a/docs/issues/session-list-stable-alphabetical-sort/plan.md b/docs/issues/session-list-stable-alphabetical-sort/plan.md index e2d7d1cac..a716c0578 100644 --- a/docs/issues/session-list-stable-alphabetical-sort/plan.md +++ b/docs/issues/session-list-stable-alphabetical-sort/plan.md @@ -21,4 +21,4 @@ ## Validation - Run focused Vitest cases to verify session store sort regression. -- After completion, run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. +- After completion, run `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. diff --git a/docs/issues/session-list-stable-alphabetical-sort/tasks.md b/docs/issues/session-list-stable-alphabetical-sort/tasks.md index 6a98b76aa..fa96fde1c 100644 --- a/docs/issues/session-list-stable-alphabetical-sort/tasks.md +++ b/docs/issues/session-list-stable-alphabetical-sort/tasks.md @@ -4,4 +4,4 @@ - [x] Adjust the renderer session store's unified sort rule to alphabetical by title. - [x] Remove the reordering side effect on local `updatedAt` during pin / unpin. - [x] Update renderer store tests to cover sorting and pin regression scenarios. -- [x] Run `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. +- [x] Run `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. diff --git a/docs/issues/settings-navigation-selection-lag/plan.md b/docs/issues/settings-navigation-selection-lag/plan.md index fd4dc986d..262ba07ea 100644 --- a/docs/issues/settings-navigation-selection-lag/plan.md +++ b/docs/issues/settings-navigation-selection-lag/plan.md @@ -11,5 +11,5 @@ Update `src/renderer/settings/App.tsx` to subscribe to TanStack Router state thr ## Validation -- Run `pnpm run format`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `bun run lint`. diff --git a/docs/issues/telegram-message-markdown-render/plan.md b/docs/issues/telegram-message-markdown-render/plan.md index 5ad57d0b1..048fdad89 100644 --- a/docs/issues/telegram-message-markdown-render/plan.md +++ b/docs/issues/telegram-message-markdown-render/plan.md @@ -17,5 +17,5 @@ ## Validation -- Run `pnpm test test/main/presenter/remoteControlPresenter/telegramClient.test.ts` (extended) and a new `telegramMarkdown.test.ts` covering core conversion rules, table fallback, and chunk-boundary behavior. -- Run `pnpm run typecheck:node` to confirm no signature break in callers (Poller, Adapter). +- Run `bun run test test/main/presenter/remoteControlPresenter/telegramClient.test.ts` (extended) and a new `telegramMarkdown.test.ts` covering core conversion rules, table fallback, and chunk-boundary behavior. +- Run `bun run typecheck:node` to confirm no signature break in callers (Poller, Adapter). diff --git a/docs/issues/telegram-message-markdown-render/tasks.md b/docs/issues/telegram-message-markdown-render/tasks.md index d06dd3076..ad6faff90 100644 --- a/docs/issues/telegram-message-markdown-render/tasks.md +++ b/docs/issues/telegram-message-markdown-render/tasks.md @@ -6,4 +6,4 @@ - [x] Thread an optional `parseMode` through `TelegramClient.sendMessage`, `editMessageText`, and `sendPhoto`. - [x] Update `TelegramPoller` to apply the converter and pass `parse_mode: 'HTML'` on all generated text paths. - [x] Add focused tests for the converter, table fallback, parse-mode wiring, and plain-text retry. -- [ ] Run `pnpm run format`, `pnpm run lint`, `pnpm run typecheck:node`, and the focused test suites. +- [ ] Run `bun run format`, `bun run lint`, `bun run typecheck:node`, and the focused test suites. diff --git a/docs/issues/thought-block-visual-alignment/plan.md b/docs/issues/thought-block-visual-alignment/plan.md index eaccbd31e..1fab3b2f1 100644 --- a/docs/issues/thought-block-visual-alignment/plan.md +++ b/docs/issues/thought-block-visual-alignment/plan.md @@ -12,5 +12,5 @@ Update `MessageBlockThink.tsx` only. ## Validation -- Run `pnpm run format`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `bun run lint`. diff --git a/docs/issues/usage-dashboard-empty-state/plan.md b/docs/issues/usage-dashboard-empty-state/plan.md index c9f56c31c..b68b2e732 100644 --- a/docs/issues/usage-dashboard-empty-state/plan.md +++ b/docs/issues/usage-dashboard-empty-state/plan.md @@ -12,5 +12,5 @@ Update `DashboardSettings.tsx` to actively kick off usage backfill when availabl ## Validation -- Run `pnpm run format`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `bun run lint`. diff --git a/docs/issues/windows-arm64-duckdb-upgrade/plan.md b/docs/issues/windows-arm64-duckdb-upgrade/plan.md index 29f31e44c..324267ca4 100644 --- a/docs/issues/windows-arm64-duckdb-upgrade/plan.md +++ b/docs/issues/windows-arm64-duckdb-upgrade/plan.md @@ -14,18 +14,18 @@ This isolates two independent risks: - Argos imports DuckDB through `src/main/presenter/knowledgePresenter/database/duckdbPresenter.ts`. - The main-process startup path reaches built-in knowledge base code early enough that a missing native binding crashes the app before E2E can observe a window. - Argos's DuckDB flow uses both online `INSTALL/LOAD vss` and an offline copied extension path through `scripts/installVss.js` and runtime extension loading. -- Project guidance requires keeping an SDD folder for active issue work and running `pnpm run format`, `pnpm run i18n`, and `pnpm run lint` after implementation. +- Project guidance requires keeping an SDD folder for active issue work and running `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint` after implementation. ## Proposed Changes ### 1. Dependency Upgrade -Update `package.json` to `@duckdb/node-api@1.5.3-r.1` and refresh `pnpm-lock.yaml`. +Update `package.json` to `@duckdb/node-api@1.5.3-r.1` and refresh `bun.lock`. Expected effect: -- pnpm resolves `@duckdb/node-bindings@1.5.3-r.1` -- pnpm resolves `@duckdb/node-bindings-win32-arm64@1.5.3-r.1` +- bun resolves `@duckdb/node-bindings@1.5.3-r.1` +- bun resolves `@duckdb/node-bindings-win32-arm64@1.5.3-r.1` - Windows ARM64 can load the native binding instead of failing during module initialization ### 2. Early Windows ARM64 Verification @@ -57,9 +57,9 @@ Run: - a targeted DuckDB/VSS smoke command - any focused tests touching the changed code or scripts -- `pnpm run format` -- `pnpm run i18n` -- `pnpm run lint` +- `bun run format` +- `i18n (N/A -- no root script)` +- `bun run lint` ### CI Validation diff --git a/docs/issues/windows-arm64-duckdb-upgrade/tasks.md b/docs/issues/windows-arm64-duckdb-upgrade/tasks.md index e4e24c089..47f27446d 100644 --- a/docs/issues/windows-arm64-duckdb-upgrade/tasks.md +++ b/docs/issues/windows-arm64-duckdb-upgrade/tasks.md @@ -36,7 +36,7 @@ Plan: [plan.md](./plan.md) Owner: Maintainer Effort: S Status: Completed -- [x] `T3.2` Run repository-required quality gates: `pnpm run format`, `pnpm run i18n`, and `pnpm run lint`. +- [x] `T3.2` Run repository-required quality gates: `bun run format`, `i18n (N/A -- no root script)`, and `bun run lint`. Owner: Maintainer Effort: S Status: Completed diff --git a/docs/issues/windows-release-build-arch/plan.md b/docs/issues/windows-release-build-arch/plan.md index 6ec2991ef..246010a34 100644 --- a/docs/issues/windows-release-build-arch/plan.md +++ b/docs/issues/windows-release-build-arch/plan.md @@ -9,7 +9,7 @@ ## Validation -- Run `pnpm run format`. -- Run `pnpm run i18n`. -- Run `pnpm run lint`. +- Run `bun run format`. +- Run `i18n (N/A -- no root script)`. +- Run `bun run lint`. - Inspect workflow references for stale `windows-latest` usage. diff --git a/docs/issues/yobrowser-cdp-graceful-degradation/tasks.md b/docs/issues/yobrowser-cdp-graceful-degradation/tasks.md index bc4268e98..e560ab235 100644 --- a/docs/issues/yobrowser-cdp-graceful-degradation/tasks.md +++ b/docs/issues/yobrowser-cdp-graceful-degradation/tasks.md @@ -13,6 +13,6 @@ structured model-visible content. - [x] Add focused unit tests for YoBrowser handler behavior and agent runtime propagation. -- [x] Run `pnpm run format`. -- [x] Run `pnpm run i18n`. -- [x] Run `pnpm run lint`. +- [x] Run `bun run format`. +- [x] Run `i18n (N/A -- no root script)`. +- [x] Run `bun run lint`. diff --git a/packages/agent-runtime/src/argosAgentRuntime.ts b/packages/agent-runtime/src/argosAgentRuntime.ts index dc526fb77..c9efdc490 100644 --- a/packages/agent-runtime/src/argosAgentRuntime.ts +++ b/packages/agent-runtime/src/argosAgentRuntime.ts @@ -13,6 +13,34 @@ import { normalizeArgosSubagentConfig } from "@argos/shared/lib/argosSubagents"; /** Stable id of the built-in Argos agent. */ export const BUILTIN_ARGOS_AGENT_ID = "argos"; +/** Stable id of the built-in, opt-in orchestration specialist. */ +export const BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID = "argos-orchestrator"; + +export const BUILTIN_ARGOS_ORCHESTRATOR_CONFIG: ArgosAgentConfig = { + systemPrompt: + "You are the Argos Orchestrator. Coordinate complex work end-to-end by inspecting projects, creating and assigning tasks, provisioning specialized agents, delegating independent work, monitoring sessions, steering them when needed, and synthesizing results. You may register MCP servers, scope them to agents, and write durable agent-specific skills that explain when and how to use those integrations. Store operational guidance in managed skills, never secrets; credentials belong only in MCP configuration. Prefer delegation and parallel execution when work can be separated safely, while retaining responsibility for verification and the final outcome.", + permissionMode: "full_access", + disabledAgentTools: [], + orchestrationEnabled: true, + subagentEnabled: true, +}; + +/** + * Reassert the orchestrator's non-negotiable capability flags on every + * re-seed/update, while preserving user edits to editable fields. The built-in + * config supplies defaults for systemPrompt/permissionMode/disabledAgentTools; + * orchestration and subagent delegation are always enabled because they define + * the agent's purpose. Without this, spreading the built-in config last would + * clobber the user's systemPrompt/permissionMode on every restart. + */ +const applyOrchestratorInvariants = (config: ArgosAgentConfig): ArgosAgentConfig => ({ + ...config, + systemPrompt: config.systemPrompt ?? BUILTIN_ARGOS_ORCHESTRATOR_CONFIG.systemPrompt, + permissionMode: config.permissionMode ?? BUILTIN_ARGOS_ORCHESTRATOR_CONFIG.permissionMode, + disabledAgentTools: config.disabledAgentTools ?? BUILTIN_ARGOS_ORCHESTRATOR_CONFIG.disabledAgentTools, + orchestrationEnabled: true, + subagentEnabled: true, +}); /** * Host-agnostic Argos-agent management facade. This is the desktop @@ -68,6 +96,39 @@ export class ArgosAgentRuntime { return toAgent(this.store.get(BUILTIN_ARGOS_AGENT_ID) as ArgosAgentRow); } + /** + * Seed the opt-in orchestration specialist. Unlike the default Argos agent, + * startup never forces this agent enabled, so the user's choice survives. + */ + ensureBuiltinOrchestratorAgent(): Agent { + const existing = this.store.get(BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID); + if (!existing) { + const now = Date.now(); + this.store.insert({ + id: BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID, + source: "builtin", + name: "Orchestrator", + enabled: false, + protected: true, + description: "Coordinates projects, tasks, sessions, and delegated agents.", + icon: "brain", + avatar_json: stringifyJson({ kind: "lucide", icon: "brain" }), + config_json: stringifyJson(BUILTIN_ARGOS_ORCHESTRATOR_CONFIG), + created_at: now, + updated_at: now, + }); + return toAgent(this.store.get(BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID) as ArgosAgentRow); + } + + const config = parseJson(existing.config_json) ?? {}; + this.store.update(BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID, { + source: "builtin", + protected: true, + config_json: stringifyJson(applyOrchestratorInvariants(config)), + }); + return toAgent(this.store.get(BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID) as ArgosAgentRow); + } + getArgosAgentConfig(agentId: string): ArgosAgentConfig | null { const row = this.store.get(agentId); if (!row) { @@ -119,8 +180,11 @@ export class ArgosAgentRuntime { } const currentConfig = parseJson(row.config_json) ?? {}; - const nextConfig = + let nextConfig = updates.config === undefined ? currentConfig : { ...currentConfig, ...clone(updates.config ?? {}) }; + if (agentId === BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID) { + nextConfig = applyOrchestratorInvariants(nextConfig); + } this.store.update(agentId, { name: updates.name?.trim() || row.name, diff --git a/packages/agent-runtime/src/configMerge.ts b/packages/agent-runtime/src/configMerge.ts index 920aa2dea..608f88b7b 100644 --- a/packages/agent-runtime/src/configMerge.ts +++ b/packages/agent-runtime/src/configMerge.ts @@ -18,6 +18,7 @@ export const mergeArgosConfig = (baseConfig: ArgosAgentConfig, overrideConfig: A enabledMcpServerIds: overrideConfig.enabledMcpServerIds ?? baseConfig.enabledMcpServerIds ?? [], enabledPluginIds: overrideConfig.enabledPluginIds ?? baseConfig.enabledPluginIds ?? [], enabledSkillNames: overrideConfig.enabledSkillNames ?? baseConfig.enabledSkillNames ?? [], + orchestrationEnabled: overrideConfig.orchestrationEnabled ?? baseConfig.orchestrationEnabled ?? false, subagentEnabled: overrideConfig.subagentEnabled ?? baseConfig.subagentEnabled ?? true, subagents: overrideConfig.subagents ?? baseConfig.subagents ?? createDefaultArgosSubagentSlots(), autoCompactionEnabled: overrideConfig.autoCompactionEnabled ?? baseConfig.autoCompactionEnabled ?? true, diff --git a/packages/agent-runtime/src/index.ts b/packages/agent-runtime/src/index.ts index 18a617741..6dbfbce45 100644 --- a/packages/agent-runtime/src/index.ts +++ b/packages/agent-runtime/src/index.ts @@ -1,4 +1,9 @@ -export { ArgosAgentRuntime, BUILTIN_ARGOS_AGENT_ID } from "./argosAgentRuntime"; +export { + ArgosAgentRuntime, + BUILTIN_ARGOS_AGENT_ID, + BUILTIN_ARGOS_ORCHESTRATOR_AGENT_ID, + BUILTIN_ARGOS_ORCHESTRATOR_CONFIG, +} from "./argosAgentRuntime"; export { SqliteArgosAgentStore } from "./store/sqliteArgosAgentStore"; export { mergeArgosConfig } from "./configMerge"; export { clone, parseJson, sanitizeString, stringifyJson, toAgent } from "./types"; diff --git a/packages/shared-contracts/src/domainSchemas.ts b/packages/shared-contracts/src/domainSchemas.ts index 02fd25dd8..d6749344a 100644 --- a/packages/shared-contracts/src/domainSchemas.ts +++ b/packages/shared-contracts/src/domainSchemas.ts @@ -376,6 +376,43 @@ export const AcpManualAgentSchema = zod.looseObject({ enabled: zod.boolean().optional(), }); +const ArgosSubagentSlotSchema = zod + .object({ + id: zod.string().min(1), + targetType: zod.enum(["self", "agent"]), + targetAgentId: zod.string().min(1).optional(), + displayName: zod.string(), + description: zod.string(), + }) + .refine( + (slot) => + slot.targetType === "agent" + ? typeof slot.targetAgentId === "string" && slot.targetAgentId.length > 0 + : slot.targetAgentId === undefined, + { + message: "targetType 'agent' requires a targetAgentId; targetType 'self' must not specify one", + path: ["targetAgentId"], + }, + ); + +const ArgosAgentMemoryEmbeddingSchema = zod.object({ + providerId: zod.string().min(1), + modelId: zod.string().min(1), +}); + +const ArgosAgentMemoryRetrievalSchema = zod.object({ + topK: zod.number().optional(), + rrfK: zod.number().optional(), + similarityThreshold: zod.number().optional(), + weights: zod + .object({ + similarity: zod.number(), + recency: zod.number(), + importance: zod.number(), + }) + .optional(), +}); + export const ArgosAgentConfigSchema = zod.looseObject({ defaultModelPreset: ArgosAgentModelPresetSchema.nullable().optional(), assistantModel: ModelSelectionSchema.nullable().optional(), @@ -387,8 +424,18 @@ export const ArgosAgentConfigSchema = zod.looseObject({ enabledMcpServerIds: zod.array(zod.string()).optional(), enabledPluginIds: zod.array(zod.string()).optional(), enabledSkillNames: zod.array(zod.string()).optional(), + orchestrationEnabled: zod.boolean().optional(), subagentEnabled: zod.boolean().optional(), + subagents: zod.array(ArgosSubagentSlotSchema).optional(), defaultProjectPath: zod.string().nullable().optional(), + autoCompactionEnabled: zod.boolean().optional(), + autoCompactionTriggerThreshold: zod.number().optional(), + autoCompactionRetainRecentPairs: zod.number().int().optional(), + memoryEnabled: zod.boolean().optional(), + memoryEmbedding: ArgosAgentMemoryEmbeddingSchema.nullable().optional(), + memoryExtractionModel: ModelSelectionSchema.nullable().optional(), + memoryRetrieval: ArgosAgentMemoryRetrievalSchema.nullable().optional(), + personaEvolutionEnabled: zod.boolean().optional(), }); export const ConfigValueSchema = zod.union([zod.boolean(), zod.number(), zod.string(), zod.null(), JsonValueSchema]); diff --git a/packages/shared-contracts/src/events.ts b/packages/shared-contracts/src/events.ts index 73df1ab6b..e1fb7de9b 100644 --- a/packages/shared-contracts/src/events.ts +++ b/packages/shared-contracts/src/events.ts @@ -69,10 +69,7 @@ import { } from "./events/upgrade.events"; import { windowStateChangedEvent } from "./events/window.events"; import { workspaceInvalidatedEvent } from "./events/workspace.events"; -import { - notificationsDatabaseRepairSuggestedEvent, - notificationsShowErrorEvent, -} from "./events/notifications.events"; +import { notificationsDatabaseRepairSuggestedEvent, notificationsShowErrorEvent } from "./events/notifications.events"; import { providersRateLimitConfigUpdatedEvent, providersRateLimitLimitExceededEvent, diff --git a/packages/ui/settings/components/skills/SkillSyncDialog/ConflictResolver.tsx b/packages/ui/settings/components/skills/SkillSyncDialog/ConflictResolver.tsx index 7c201004c..93c2f6cd4 100644 --- a/packages/ui/settings/components/skills/SkillSyncDialog/ConflictResolver.tsx +++ b/packages/ui/settings/components/skills/SkillSyncDialog/ConflictResolver.tsx @@ -18,12 +18,7 @@ interface ConflictResolverProps { onStrategiesChange: (value: Record) => void; } -const ConflictResolver: FC = ({ - conflicts, - strategies, - warnings, - onStrategiesChange, -}) => { +const ConflictResolver: FC = ({ conflicts, strategies, warnings, onStrategiesChange }) => { const updateStrategy = (skillName: string, strategy: ConflictStrategy) => { onStrategiesChange({ ...strategies, diff --git a/packages/ui/settings/components/skills/SkillSyncDialog/SkillSelector.tsx b/packages/ui/settings/components/skills/SkillSyncDialog/SkillSelector.tsx index 67f1e455c..863871207 100644 --- a/packages/ui/settings/components/skills/SkillSyncDialog/SkillSelector.tsx +++ b/packages/ui/settings/components/skills/SkillSyncDialog/SkillSelector.tsx @@ -12,12 +12,7 @@ interface SkillSelectorProps { onSelectedSkillsChange: (value: string[]) => void; } -const SkillSelector: FC = ({ - skills, - selectedSkills, - conflicts, - onSelectedSkillsChange, -}) => { +const SkillSelector: FC = ({ skills, selectedSkills, conflicts, onSelectedSkillsChange }) => { const [skillCheckedState, setSkillCheckedState] = useState>({}); useEffect(() => { diff --git a/packages/ui/shadcn/components/ui/alert-dialog.tsx b/packages/ui/shadcn/components/ui/alert-dialog.tsx index 978e5ac97..668192cb7 100644 --- a/packages/ui/shadcn/components/ui/alert-dialog.tsx +++ b/packages/ui/shadcn/components/ui/alert-dialog.tsx @@ -156,9 +156,6 @@ export { AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, - - - AlertDialogTitle, AlertDialogTrigger, }; diff --git a/packages/ui/shadcn/components/ui/alert.tsx b/packages/ui/shadcn/components/ui/alert.tsx index 8253e48c5..db69e6c96 100644 --- a/packages/ui/shadcn/components/ui/alert.tsx +++ b/packages/ui/shadcn/components/ui/alert.tsx @@ -53,4 +53,4 @@ function AlertAction({ className, ...props }: React.ComponentProps<"div">) { return
; } -export { Alert, AlertTitle, AlertDescription, }; +export { Alert, AlertTitle, AlertDescription }; diff --git a/packages/ui/shadcn/components/ui/badge.tsx b/packages/ui/shadcn/components/ui/badge.tsx index 39454a18d..d04083271 100644 --- a/packages/ui/shadcn/components/ui/badge.tsx +++ b/packages/ui/shadcn/components/ui/badge.tsx @@ -37,4 +37,4 @@ function Badge({ ); } -export { Badge, }; +export { Badge }; diff --git a/packages/ui/shadcn/components/ui/button-group.tsx b/packages/ui/shadcn/components/ui/button-group.tsx index cb4b2f17e..3fa7b18f8 100644 --- a/packages/ui/shadcn/components/ui/button-group.tsx +++ b/packages/ui/shadcn/components/ui/button-group.tsx @@ -75,4 +75,4 @@ function ButtonGroupSeparator({ ); } -export { ButtonGroup, }; +export { ButtonGroup }; diff --git a/packages/ui/shadcn/components/ui/button.tsx b/packages/ui/shadcn/components/ui/button.tsx index 18aef3c4d..1b04f343a 100644 --- a/packages/ui/shadcn/components/ui/button.tsx +++ b/packages/ui/shadcn/components/ui/button.tsx @@ -61,4 +61,4 @@ function Button({ ); } -export { Button, }; +export { Button }; diff --git a/packages/ui/shadcn/components/ui/card.tsx b/packages/ui/shadcn/components/ui/card.tsx index 395f3c51c..b564b6fab 100644 --- a/packages/ui/shadcn/components/ui/card.tsx +++ b/packages/ui/shadcn/components/ui/card.tsx @@ -64,4 +64,4 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) { ); } -export { Card, CardHeader, CardTitle, CardDescription, CardContent }; +export { Card, CardHeader, CardTitle, CardDescription, CardContent }; diff --git a/packages/ui/shadcn/components/ui/context-menu.tsx b/packages/ui/shadcn/components/ui/context-menu.tsx index 3cda909ad..9ed641de7 100644 --- a/packages/ui/shadcn/components/ui/context-menu.tsx +++ b/packages/ui/shadcn/components/ui/context-menu.tsx @@ -212,20 +212,4 @@ function ContextMenuShortcut({ className, ...props }: React.ComponentProps<"span ); } -export { - ContextMenu, - ContextMenuTrigger, - ContextMenuContent, - ContextMenuItem, - - - - ContextMenuSeparator, - - - - - - - -}; +export { ContextMenu, ContextMenuTrigger, ContextMenuContent, ContextMenuItem, ContextMenuSeparator }; diff --git a/packages/ui/shadcn/components/ui/dialog.tsx b/packages/ui/shadcn/components/ui/dialog.tsx index 3af0972bd..85a9159fe 100644 --- a/packages/ui/shadcn/components/ui/dialog.tsx +++ b/packages/ui/shadcn/components/ui/dialog.tsx @@ -118,15 +118,4 @@ function DialogDescription({ className, ...props }: React.ComponentProps) return

; } -export { Popover, PopoverContent, PopoverTrigger }; +export { Popover, PopoverContent, PopoverTrigger }; diff --git a/packages/ui/shadcn/components/ui/scroll-area.tsx b/packages/ui/shadcn/components/ui/scroll-area.tsx index 780695972..d6b92a0aa 100644 --- a/packages/ui/shadcn/components/ui/scroll-area.tsx +++ b/packages/ui/shadcn/components/ui/scroll-area.tsx @@ -34,4 +34,4 @@ function ScrollBar(_props: React.ComponentProps<"div"> & { orientation?: "horizo return null; } -export { ScrollArea, }; +export { ScrollArea }; diff --git a/packages/ui/shadcn/components/ui/select.tsx b/packages/ui/shadcn/components/ui/select.tsx index 822bce424..51b2c9288 100644 --- a/packages/ui/shadcn/components/ui/select.tsx +++ b/packages/ui/shadcn/components/ui/select.tsx @@ -155,15 +155,4 @@ function SelectScrollDownButton({ ); } -export { - Select, - SelectContent, - - SelectItem, - - - - - SelectTrigger, - SelectValue, -}; +export { Select, SelectContent, SelectItem, SelectTrigger, SelectValue }; diff --git a/packages/ui/shadcn/components/ui/sheet.tsx b/packages/ui/shadcn/components/ui/sheet.tsx index fae088d2b..10f7cc4b3 100644 --- a/packages/ui/shadcn/components/ui/sheet.tsx +++ b/packages/ui/shadcn/components/ui/sheet.tsx @@ -100,4 +100,4 @@ function SheetDescription({ className, ...props }: React.ComponentProps) ); } -export { Table, TableHeader, TableBody, TableHead, TableRow, TableCell, }; +export { Table, TableHeader, TableBody, TableHead, TableRow, TableCell }; diff --git a/packages/ui/shadcn/components/ui/tabs.tsx b/packages/ui/shadcn/components/ui/tabs.tsx index 7150325bb..f485c3d7b 100644 --- a/packages/ui/shadcn/components/ui/tabs.tsx +++ b/packages/ui/shadcn/components/ui/tabs.tsx @@ -73,4 +73,4 @@ function TabsContent({ className, ...props }: React.ComponentProps( ); McpServers.displayName = "McpServers"; - diff --git a/packages/ui/src/components/message/MessageItemUser.tsx b/packages/ui/src/components/message/MessageItemUser.tsx index 171e8c4cf..4689836c9 100644 --- a/packages/ui/src/components/message/MessageItemUser.tsx +++ b/packages/ui/src/components/message/MessageItemUser.tsx @@ -62,13 +62,7 @@ interface MessageItemUserProps { onEditSave?: (payload: { messageId: string; text: string }) => void; } -const MessageItemUser: FC = ({ - message, - isReadOnly = false, - onRetry, - onDelete, - onEditSave, -}) => { +const MessageItemUser: FC = ({ message, isReadOnly = false, onRetry, onDelete, onEditSave }) => { const deviceClient = createDeviceClient(); const windowClient = createWindowClient(); diff --git a/packages/ui/src/stores/mcp.ts b/packages/ui/src/stores/mcp.ts index af583f05e..24ca11f68 100644 --- a/packages/ui/src/stores/mcp.ts +++ b/packages/ui/src/stores/mcp.ts @@ -806,8 +806,7 @@ const getPluginTools = () => mcpStore.state.tools.filter((tool) => isPluginOwned const getVisibleResources = () => mcpStore.state.resources.filter((resource) => isVisibleServerName(resource.client.name)); -const getVisiblePrompts = () => - mcpStore.state.prompts.filter((prompt) => isVisibleServerName(prompt.client?.name)); +const getVisiblePrompts = () => mcpStore.state.prompts.filter((prompt) => isVisibleServerName(prompt.client?.name)); const getToolsLoading = () => (mcpStore.state.config.mcpEnabled ? mcpStore.state.toolsLoading : false); diff --git a/packages/ui/src/stores/ollamaStore.ts b/packages/ui/src/stores/ollamaStore.ts index 4b4460b21..88eaa541d 100644 --- a/packages/ui/src/stores/ollamaStore.ts +++ b/packages/ui/src/stores/ollamaStore.ts @@ -60,11 +60,9 @@ const updatePullingProgress = (providerId: string, modelName: string, progress?: }); }; -const getOllamaRunningModels = (providerId: string): OllamaModel[] => - ollamaStore.state.runningModels[providerId] || []; +const getOllamaRunningModels = (providerId: string): OllamaModel[] => ollamaStore.state.runningModels[providerId] || []; -const getOllamaLocalModels = (providerId: string): OllamaModel[] => - ollamaStore.state.localModels[providerId] || []; +const getOllamaLocalModels = (providerId: string): OllamaModel[] => ollamaStore.state.localModels[providerId] || []; const getOllamaPullingModels = (providerId: string): Record => ollamaStore.state.pullingProgress[providerId] || {}; diff --git a/packages/ui/src/stores/ui/workspace.ts b/packages/ui/src/stores/ui/workspace.ts index abc401ec7..9fb0680de 100644 --- a/packages/ui/src/stores/ui/workspace.ts +++ b/packages/ui/src/stores/ui/workspace.ts @@ -14,7 +14,7 @@ import { import type { ConnectionState } from "@argos/shared-contracts/connection"; import { clearSessionContextForMachineSwitch, fetchSessions as originalFetchSessions } from "./session"; -export type { WorkspaceEntry, }; +export type { WorkspaceEntry }; const workspaceStore = new Store({ ...DEFAULT_WORKSPACE_CONFIG,

- Argos Light Mode + Argos Light Mode
- Argos Dark Mode + Argos Dark Mode