refactor: remove terminal MCP, Playwright, and commands infrastructure
- Remove terminal/tmux MCP from voice LLM - it now only uses agent-control MCP - Remove Playwright MCP auto-injection from Claude and Codex agents - Remove commands state from session, store, and UI - Remove present_artifact tool and guidelines from voice prompt - Update agent-prompt.md examples to focus on agents instead of commands - Remove @huggingface/transformers dependency and test scripts - Add expandTilde utility to utils/path.ts 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: 7ddb69c7
|
id: 7ddb69c7
|
||||||
title: Remove auto-injected MCPs (Playwright, etc) - make user-configured
|
title: Remove auto-injected MCPs (Playwright, etc) - make user-configured
|
||||||
status: open
|
status: done
|
||||||
deps: []
|
deps: []
|
||||||
created: 2026-01-08T16:17:08.670Z
|
created: 2026-01-08T16:17:08.670Z
|
||||||
---
|
---
|
||||||
|
|||||||
Generated
+3
-3
@@ -1701,9 +1701,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@emnapi/runtime": {
|
"node_modules/@emnapi/runtime": {
|
||||||
"version": "1.6.0",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
|
||||||
"integrity": "sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==",
|
"integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"optional": true,
|
"optional": true,
|
||||||
|
|||||||
@@ -4,14 +4,6 @@ import type { Agent } from "@/contexts/session-context";
|
|||||||
|
|
||||||
export interface ActiveProcessesProps {
|
export interface ActiveProcessesProps {
|
||||||
agents: Agent[];
|
agents: Agent[];
|
||||||
commands: Array<{
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
workingDirectory: string;
|
|
||||||
currentCommand: string;
|
|
||||||
isDead: boolean;
|
|
||||||
exitCode: number | null;
|
|
||||||
}>;
|
|
||||||
viewMode: "orchestrator" | "agent";
|
viewMode: "orchestrator" | "agent";
|
||||||
activeAgentId: string | null;
|
activeAgentId: string | null;
|
||||||
onSelectAgent: (serverId: string, id: string) => void;
|
onSelectAgent: (serverId: string, id: string) => void;
|
||||||
@@ -132,7 +124,6 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
|
|
||||||
export function ActiveProcesses({
|
export function ActiveProcesses({
|
||||||
agents,
|
agents,
|
||||||
commands,
|
|
||||||
viewMode,
|
viewMode,
|
||||||
activeAgentId,
|
activeAgentId,
|
||||||
onSelectAgent,
|
onSelectAgent,
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ export type {
|
|||||||
DraftInput,
|
DraftInput,
|
||||||
ProviderModelState,
|
ProviderModelState,
|
||||||
Agent,
|
Agent,
|
||||||
Command,
|
|
||||||
ExplorerEntry,
|
ExplorerEntry,
|
||||||
ExplorerFile,
|
ExplorerFile,
|
||||||
ExplorerEntryKind,
|
ExplorerEntryKind,
|
||||||
@@ -75,14 +74,6 @@ const SESSION_SNAPSHOT_STORAGE_PREFIX = "@paseo:session-snapshot:";
|
|||||||
|
|
||||||
type PersistedSessionSnapshot = {
|
type PersistedSessionSnapshot = {
|
||||||
agents: AgentSnapshotPayload[];
|
agents: AgentSnapshotPayload[];
|
||||||
commands: Array<{
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
workingDirectory: string;
|
|
||||||
currentCommand: string;
|
|
||||||
isDead: boolean;
|
|
||||||
exitCode: number | null;
|
|
||||||
}>;
|
|
||||||
savedAt: string;
|
savedAt: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -97,7 +88,7 @@ async function loadPersistedSessionSnapshot(serverId: string): Promise<Persisted
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const parsed = JSON.parse(raw) as PersistedSessionSnapshot;
|
const parsed = JSON.parse(raw) as PersistedSessionSnapshot;
|
||||||
if (!Array.isArray(parsed?.agents) || !Array.isArray(parsed?.commands)) {
|
if (!Array.isArray(parsed?.agents)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
@@ -107,11 +98,10 @@ async function loadPersistedSessionSnapshot(serverId: string): Promise<Persisted
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function persistSessionSnapshot(serverId: string, snapshot: { agents: AgentSnapshotPayload[]; commands: any[] }) {
|
async function persistSessionSnapshot(serverId: string, snapshot: { agents: AgentSnapshotPayload[] }) {
|
||||||
try {
|
try {
|
||||||
const payload: PersistedSessionSnapshot = {
|
const payload: PersistedSessionSnapshot = {
|
||||||
agents: snapshot.agents,
|
agents: snapshot.agents,
|
||||||
commands: snapshot.commands,
|
|
||||||
savedAt: new Date().toISOString(),
|
savedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
await AsyncStorage.setItem(getSessionSnapshotStorageKey(serverId), JSON.stringify(payload));
|
await AsyncStorage.setItem(getSessionSnapshotStorageKey(serverId), JSON.stringify(payload));
|
||||||
@@ -241,7 +231,6 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
const setHasHydratedAgents = useSessionStore((state) => state.setHasHydratedAgents);
|
const setHasHydratedAgents = useSessionStore((state) => state.setHasHydratedAgents);
|
||||||
const setAgents = useSessionStore((state) => state.setAgents);
|
const setAgents = useSessionStore((state) => state.setAgents);
|
||||||
const setAgentLastActivity = useSessionStore((state) => state.setAgentLastActivity);
|
const setAgentLastActivity = useSessionStore((state) => state.setAgentLastActivity);
|
||||||
const setCommands = useSessionStore((state) => state.setCommands);
|
|
||||||
const setPendingPermissions = useSessionStore((state) => state.setPendingPermissions);
|
const setPendingPermissions = useSessionStore((state) => state.setPendingPermissions);
|
||||||
const setGitDiffs = useSessionStore((state) => state.setGitDiffs);
|
const setGitDiffs = useSessionStore((state) => state.setGitDiffs);
|
||||||
const setFileExplorer = useSessionStore((state) => state.setFileExplorer);
|
const setFileExplorer = useSessionStore((state) => state.setFileExplorer);
|
||||||
@@ -368,13 +357,6 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPendingPermissions(serverId, pendingPermissions);
|
setPendingPermissions(serverId, pendingPermissions);
|
||||||
const commandEntries = snapshot.commands ?? [];
|
|
||||||
setCommands(serverId, (prev) => {
|
|
||||||
if (prev.size > 0) {
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
return new Map(commandEntries.map((command) => [command.id, command]));
|
|
||||||
});
|
|
||||||
setHasHydratedAgents(serverId, true);
|
setHasHydratedAgents(serverId, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -383,7 +365,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
return () => {
|
return () => {
|
||||||
isMounted = false;
|
isMounted = false;
|
||||||
};
|
};
|
||||||
}, [serverId, setAgents, setCommands, setPendingPermissions, setHasHydratedAgents]);
|
}, [serverId, setAgents, setPendingPermissions, setHasHydratedAgents]);
|
||||||
|
|
||||||
const updateExplorerState = useCallback(
|
const updateExplorerState = useCallback(
|
||||||
(agentId: string, updater: (state: any) => any) => {
|
(agentId: string, updater: (state: any) => any) => {
|
||||||
@@ -632,9 +614,9 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
sessionStateTimeoutRef.current = null;
|
sessionStateTimeoutRef.current = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { agents: agentsList, commands: commandsList } = message.payload;
|
const { agents: agentsList } = message.payload;
|
||||||
|
|
||||||
console.log("[Session] ✅ Received session_state:", agentsList.length, "agents,", commandsList.length, "commands");
|
console.log("[Session] ✅ Received session_state:", agentsList.length, "agents");
|
||||||
setInitializingAgents(serverId, new Map());
|
setInitializingAgents(serverId, new Map());
|
||||||
|
|
||||||
const agents = new Map();
|
const agents = new Map();
|
||||||
@@ -651,8 +633,6 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const normalizedCommands = commandsList.map((command) => command);
|
|
||||||
|
|
||||||
setAgents(serverId, agents);
|
setAgents(serverId, agents);
|
||||||
|
|
||||||
// Initialize agentLastActivity slice (top-level)
|
// Initialize agentLastActivity slice (top-level)
|
||||||
@@ -661,7 +641,6 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPendingPermissions(serverId, pendingPermissions);
|
setPendingPermissions(serverId, pendingPermissions);
|
||||||
setCommands(serverId, new Map(normalizedCommands.map((command: any) => [command.id, command])));
|
|
||||||
setAgentStreamState(serverId, (prev) => {
|
setAgentStreamState(serverId, (prev) => {
|
||||||
if (prev.size === 0) {
|
if (prev.size === 0) {
|
||||||
return prev;
|
return prev;
|
||||||
@@ -716,7 +695,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
|
|
||||||
return changed ? next : prev;
|
return changed ? next : prev;
|
||||||
});
|
});
|
||||||
void persistSessionSnapshot(serverId, { agents: agentsList, commands: normalizedCommands });
|
void persistSessionSnapshot(serverId, { agents: agentsList });
|
||||||
setHasHydratedAgents(serverId, true);
|
setHasHydratedAgents(serverId, true);
|
||||||
updateConnectionStatus(serverId, { status: "online", lastOnlineAt: new Date().toISOString(), sessionReady: true });
|
updateConnectionStatus(serverId, { status: "online", lastOnlineAt: new Date().toISOString(), sessionReady: true });
|
||||||
});
|
});
|
||||||
@@ -1271,7 +1250,7 @@ export function SessionProvider({ children, serverUrl, serverId }: SessionProvid
|
|||||||
unsubProviderModels();
|
unsubProviderModels();
|
||||||
unsubAgentDeleted();
|
unsubAgentDeleted();
|
||||||
};
|
};
|
||||||
}, [ws, audioPlayer, serverId, setIsPlayingAudio, setMessages, setCurrentAssistantMessage, setAgentStreamState, setAgentStreamingBuffer, clearAgentStreamingBuffer, setInitializingAgents, setAgents, setAgentLastActivity, setCommands, setPendingPermissions, setGitDiffs, setFileExplorer, setProviderModels, setHasHydratedAgents, updateConnectionStatus, getSession, saveDraftInput]);
|
}, [ws, audioPlayer, serverId, setIsPlayingAudio, setMessages, setCurrentAssistantMessage, setAgentStreamState, setAgentStreamingBuffer, clearAgentStreamingBuffer, setInitializingAgents, setAgents, setAgentLastActivity, setPendingPermissions, setGitDiffs, setFileExplorer, setProviderModels, setHasHydratedAgents, updateConnectionStatus, getSession, saveDraftInput]);
|
||||||
|
|
||||||
const initializeAgent = useCallback(({ agentId, requestId }: { agentId: string; requestId?: string }) => {
|
const initializeAgent = useCallback(({ agentId, requestId }: { agentId: string; requestId?: string }) => {
|
||||||
console.log("[Session] initializeAgent called", { agentId, requestId });
|
console.log("[Session] initializeAgent called", { agentId, requestId });
|
||||||
|
|||||||
@@ -108,15 +108,6 @@ export interface Agent {
|
|||||||
parentAgentId?: string | null;
|
parentAgentId?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Command {
|
|
||||||
id: string;
|
|
||||||
name: string;
|
|
||||||
workingDirectory: string;
|
|
||||||
currentCommand: string;
|
|
||||||
isDead: boolean;
|
|
||||||
exitCode: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ExplorerEntryKind = "file" | "directory";
|
export type ExplorerEntryKind = "file" | "directory";
|
||||||
export type ExplorerFileKind = "text" | "image" | "binary";
|
export type ExplorerFileKind = "text" | "image" | "binary";
|
||||||
export type ExplorerEncoding = "utf-8" | "base64" | "none";
|
export type ExplorerEncoding = "utf-8" | "base64" | "none";
|
||||||
@@ -228,9 +219,8 @@ export interface SessionState {
|
|||||||
// Initializing agents
|
// Initializing agents
|
||||||
initializingAgents: Map<string, boolean>;
|
initializingAgents: Map<string, boolean>;
|
||||||
|
|
||||||
// Agents and commands
|
// Agents
|
||||||
agents: Map<string, Agent>;
|
agents: Map<string, Agent>;
|
||||||
commands: Map<string, Command>;
|
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
pendingPermissions: Map<string, PendingPermission>;
|
pendingPermissions: Map<string, PendingPermission>;
|
||||||
@@ -301,9 +291,6 @@ interface SessionStoreActions {
|
|||||||
// Agent activity timestamps
|
// Agent activity timestamps
|
||||||
setAgentLastActivity: (agentId: string, timestamp: Date) => void;
|
setAgentLastActivity: (agentId: string, timestamp: Date) => void;
|
||||||
|
|
||||||
// Commands
|
|
||||||
setCommands: (serverId: string, commands: Map<string, Command> | ((prev: Map<string, Command>) => Map<string, Command>)) => void;
|
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
setPendingPermissions: (serverId: string, perms: Map<string, PendingPermission> | ((prev: Map<string, PendingPermission>) => Map<string, PendingPermission>)) => void;
|
setPendingPermissions: (serverId: string, perms: Map<string, PendingPermission> | ((prev: Map<string, PendingPermission>) => Map<string, PendingPermission>)) => void;
|
||||||
|
|
||||||
@@ -379,7 +366,6 @@ function createInitialSessionState(serverId: string, ws: UseWebSocketReturn, aud
|
|||||||
agentStreamingBuffer: new Map(),
|
agentStreamingBuffer: new Map(),
|
||||||
initializingAgents: new Map(),
|
initializingAgents: new Map(),
|
||||||
agents: new Map(),
|
agents: new Map(),
|
||||||
commands: new Map(),
|
|
||||||
pendingPermissions: new Map(),
|
pendingPermissions: new Map(),
|
||||||
gitDiffs: new Map(),
|
gitDiffs: new Map(),
|
||||||
fileExplorer: new Map(),
|
fileExplorer: new Map(),
|
||||||
@@ -663,28 +649,6 @@ export const useSessionStore = create<SessionStore>()(
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Commands
|
|
||||||
setCommands: (serverId, commands) => {
|
|
||||||
set((prev) => {
|
|
||||||
const session = prev.sessions[serverId];
|
|
||||||
if (!session) {
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
const nextCommands = typeof commands === "function" ? commands(session.commands) : commands;
|
|
||||||
if (session.commands === nextCommands) {
|
|
||||||
return prev;
|
|
||||||
}
|
|
||||||
logSessionStoreUpdate("setCommands", serverId, { count: nextCommands.size });
|
|
||||||
return {
|
|
||||||
...prev,
|
|
||||||
sessions: {
|
|
||||||
...prev.sessions,
|
|
||||||
[serverId]: { ...session, commands: nextCommands },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
setPendingPermissions: (serverId, perms) => {
|
setPendingPermissions: (serverId, perms) => {
|
||||||
set((prev) => {
|
set((prev) => {
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ dist
|
|||||||
*.log
|
*.log
|
||||||
.debug.conversations
|
.debug.conversations
|
||||||
conversations
|
conversations
|
||||||
|
.cache
|
||||||
|
|||||||
@@ -26,18 +26,18 @@ You are a **voice-controlled** assistant. The user speaks to you via phone and h
|
|||||||
**Good example:**
|
**Good example:**
|
||||||
|
|
||||||
```
|
```
|
||||||
User: "List my commands"
|
User: "List my agents"
|
||||||
You: "You have 3 running. The dev server on port 3000, tests watching for changes, and a Python REPL."
|
You: "You have two agents. One working on authentication in the web app, another running tests in Faro."
|
||||||
|
|
||||||
User: "What about finished commands?"
|
User: "How's the auth agent doing?"
|
||||||
You: "Two finished. The npm install completed successfully and git status exited with code zero."
|
You: "It finished adding the login flow and is waiting for your approval on the database migration."
|
||||||
```
|
```
|
||||||
|
|
||||||
**Bad example:**
|
**Bad example:**
|
||||||
|
|
||||||
```
|
```
|
||||||
User: "List my commands"
|
User: "List my agents"
|
||||||
You: "You have 5 commands: 1. **dev-server** - Running on port 3000 2. **tests** - Watching for changes..."
|
You: "You have 2 agents: 1. **auth-agent** - Working on authentication 2. **test-agent** - Running tests..."
|
||||||
```
|
```
|
||||||
|
|
||||||
### Handling STT Errors
|
### Handling STT Errors
|
||||||
@@ -60,7 +60,7 @@ Speech-to-text makes mistakes. Fix them silently using context.
|
|||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
- User: "Run empty install" → Interpret as "Run npm install"
|
- User: "Run empty install" → Interpret as "Run npm install"
|
||||||
- User: "Show command to" → If only 2 commands, pick from context; if many, ask which
|
- User: "Check the agent" → If only one agent, check that one; if multiple, ask which
|
||||||
|
|
||||||
### Immediate Silence Protocol
|
### Immediate Silence Protocol
|
||||||
|
|
||||||
@@ -108,51 +108,11 @@ Ask only when the routing decision is truly ambiguous. Otherwise:
|
|||||||
|
|
||||||
After any agent-facing tool call, verbally report the key result in one sentence: who acted, what happened, and whether more work is pending. Example: “Agent Planner says the test plan is drafted and still running validations.” Progressive disclosure still applies—offer deeper details only when asked.
|
After any agent-facing tool call, verbally report the key result in one sentence: who acted, what happened, and whether more work is pending. Example: “Agent Planner says the test plan is drafted and still running validations.” Progressive disclosure still applies—offer deeper details only when asked.
|
||||||
|
|
||||||
## 4. Special Triggers
|
## 4. Agent Integrations
|
||||||
|
|
||||||
### "Show me" → Use present_artifact
|
|
||||||
|
|
||||||
When user says **"show me"**, use `present_artifact` to display visual content.
|
|
||||||
|
|
||||||
**Keep voice response SHORT. Let the artifact show the data.**
|
|
||||||
|
|
||||||
**Prefer command_output or file sources:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// ✅ CORRECT
|
|
||||||
User: "Show me the git diff"
|
|
||||||
You: "Here's the diff."
|
|
||||||
present_artifact({
|
|
||||||
type: "diff",
|
|
||||||
source: { type: "command_output", command: "git diff" }
|
|
||||||
})
|
|
||||||
|
|
||||||
// ✅ CORRECT
|
|
||||||
User: "Show me package.json"
|
|
||||||
You: "Here's package.json."
|
|
||||||
present_artifact({
|
|
||||||
type: "code",
|
|
||||||
source: { type: "file", path: "/path/to/package.json" }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
**Only use text source for data you already have:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
User: "Show me what Planner wrote"
|
|
||||||
You: [Use the text you already have from agent activity]
|
|
||||||
You: "Here's Planner's summary."
|
|
||||||
present_artifact({
|
|
||||||
type: "markdown",
|
|
||||||
source: { type: "text", text: plannerSummary }
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. Agent Integrations
|
|
||||||
|
|
||||||
### Your Role: Orchestrator
|
### Your Role: Orchestrator
|
||||||
|
|
||||||
You orchestrate work. Agents execute. Commands run tasks.
|
You orchestrate work. Agents execute.
|
||||||
|
|
||||||
**First action when agent work is mentioned: Call `list_agents()`**
|
**First action when agent work is mentioned: Call `list_agents()`**
|
||||||
|
|
||||||
@@ -160,7 +120,7 @@ Load the agent list before any agent interaction. Always.
|
|||||||
|
|
||||||
#### Focus Management
|
#### Focus Management
|
||||||
|
|
||||||
- Keep a lightweight "focus" pointer to the last agent the user explicitly addressed or implicitly referenced. Route follow-up utterances there unless the user names another agent or a global command.
|
- Keep a lightweight "focus" pointer to the last agent the user explicitly addressed or implicitly referenced. Route follow-up utterances there unless the user names another agent.
|
||||||
- Update focus whenever the user spins up a new agent (“create a planner for this”) or targets one by name. Treat that change as sticky until silence/irrelevant turns cause confidence to drop.
|
- Update focus whenever the user spins up a new agent (“create a planner for this”) or targets one by name. Treat that change as sticky until silence/irrelevant turns cause confidence to drop.
|
||||||
- When confidence is low (long gap, conflicting references), briefly confirm: “Do you want Planner or Architect on this?”
|
- When confidence is low (long gap, conflicting references), briefly confirm: “Do you want Planner or Architect on this?”
|
||||||
- Always narrate hand-offs: “Okay, handing that to Planner.”
|
- Always narrate hand-offs: “Okay, handing that to Planner.”
|
||||||
@@ -297,7 +257,7 @@ get_agent_activity({ agentId })
|
|||||||
send_agent_prompt({ agentId, prompt: "add tests" })
|
send_agent_prompt({ agentId, prompt: "add tests" })
|
||||||
```
|
```
|
||||||
|
|
||||||
## 6. Git & GitHub
|
## 5. Git & GitHub
|
||||||
|
|
||||||
### Git Worktree Utilities
|
### Git Worktree Utilities
|
||||||
|
|
||||||
@@ -322,7 +282,7 @@ Already authenticated. Use for:
|
|||||||
- Managing issues: `gh issue list`
|
- Managing issues: `gh issue list`
|
||||||
- Checking CI: `gh pr checks`
|
- Checking CI: `gh pr checks`
|
||||||
|
|
||||||
## 7. Projects & Context
|
## 6. Projects & Context
|
||||||
|
|
||||||
### Project Locations
|
### Project Locations
|
||||||
|
|
||||||
@@ -351,8 +311,8 @@ All projects in `~/dev`:
|
|||||||
- Ask: "Kill agent [id]?"
|
- Ask: "Kill agent [id]?"
|
||||||
- Wait for "yes"
|
- Wait for "yes"
|
||||||
|
|
||||||
**Complex coding vs quick commands:**
|
**Task routing:**
|
||||||
- Complex or quick → Always delegate to an agent (direct shell commands are disabled)
|
- All coding tasks → Delegate to an agent
|
||||||
- Active agent + related work → Delegate to that agent
|
- Active agent + related work → Delegate to that agent
|
||||||
- If the user explicitly mentions another agent, switch focus before delegating
|
- If the user explicitly mentions another agent, switch focus before delegating
|
||||||
|
|
||||||
|
|||||||
@@ -1,209 +1,9 @@
|
|||||||
import { tool, experimental_createMCPClient, type ToolSet } from "ai";
|
import type { ToolSet } from "ai";
|
||||||
import { z } from "zod";
|
|
||||||
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
||||||
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
||||||
import { createTerminalMcpServer } from "../terminal-mcp/index.js";
|
|
||||||
|
|
||||||
type McpClient = Awaited<ReturnType<typeof experimental_createMCPClient>>;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton MCP clients
|
* Get all tools for voice LLM
|
||||||
|
* @param agentTools - Agent control tools from MCP
|
||||||
*/
|
*/
|
||||||
let terminalMcpClient: McpClient | null = null;
|
export function getAllTools(agentTools?: ToolSet): ToolSet {
|
||||||
|
return agentTools ?? {};
|
||||||
let playwrightMcpClient: McpClient | null = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create Terminal MCP client (singleton)
|
|
||||||
*/
|
|
||||||
async function getTerminalMcpClient(): Promise<McpClient> {
|
|
||||||
if (terminalMcpClient) {
|
|
||||||
return terminalMcpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create Terminal MCP server
|
|
||||||
const server = await createTerminalMcpServer({ sessionName: "__paseo" });
|
|
||||||
|
|
||||||
// Create linked transport pair
|
|
||||||
const [clientTransport, serverTransport] =
|
|
||||||
InMemoryTransport.createLinkedPair();
|
|
||||||
|
|
||||||
// Connect server to its transport
|
|
||||||
await server.connect(serverTransport);
|
|
||||||
|
|
||||||
// Create client connected to the other side
|
|
||||||
terminalMcpClient = await experimental_createMCPClient({
|
|
||||||
transport: clientTransport,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Terminal MCP client initialized");
|
|
||||||
|
|
||||||
return terminalMcpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create Playwright MCP client (singleton)
|
|
||||||
*/
|
|
||||||
async function getPlaywrightMcpClient(): Promise<McpClient> {
|
|
||||||
if (playwrightMcpClient) {
|
|
||||||
return playwrightMcpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
const transport = new StdioClientTransport({
|
|
||||||
command: "npx",
|
|
||||||
args: ["@playwright/mcp", "--image-responses", "omit"],
|
|
||||||
});
|
|
||||||
|
|
||||||
playwrightMcpClient = await experimental_createMCPClient({
|
|
||||||
transport,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log("Playwright MCP client initialized");
|
|
||||||
|
|
||||||
return playwrightMcpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cache for merged MCP tools
|
|
||||||
*/
|
|
||||||
let mcpToolsCache: ToolSet | null = null;
|
|
||||||
let mcpToolsPromise: Promise<ToolSet> | null = null;
|
|
||||||
|
|
||||||
async function getMcpTools(): Promise<ToolSet> {
|
|
||||||
if (mcpToolsCache) {
|
|
||||||
return mcpToolsCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mcpToolsPromise) {
|
|
||||||
return mcpToolsPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
mcpToolsPromise = (async () => {
|
|
||||||
const [terminalClient, playwrightClient] = await Promise.all([
|
|
||||||
getTerminalMcpClient(),
|
|
||||||
getPlaywrightMcpClient().catch((error) => {
|
|
||||||
console.error("Failed to initialize Playwright MCP:", error);
|
|
||||||
return null;
|
|
||||||
}),
|
|
||||||
]);
|
|
||||||
|
|
||||||
const terminalTools = (await terminalClient.tools()) as ToolSet;
|
|
||||||
const playwrightTools: ToolSet = playwrightClient
|
|
||||||
? ((await playwrightClient.tools()) as ToolSet)
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const mergedTools: ToolSet = {
|
|
||||||
...terminalTools,
|
|
||||||
...playwrightTools,
|
|
||||||
};
|
|
||||||
|
|
||||||
mcpToolsCache = mergedTools;
|
|
||||||
console.log(`Loaded ${Object.keys(mergedTools).length} MCP tools`);
|
|
||||||
|
|
||||||
return mergedTools;
|
|
||||||
})();
|
|
||||||
|
|
||||||
return mcpToolsPromise;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manual tools that aren't MCP-based
|
|
||||||
*/
|
|
||||||
const manualTools: ToolSet = {
|
|
||||||
present_artifact: tool({
|
|
||||||
description:
|
|
||||||
"Present an artifact (plan, diff, screenshot, etc.) to the user for review. Use this when you need to show information that's hard to convey via TTS, such as markdown plans, code diffs, or visual content",
|
|
||||||
inputSchema: z.object({
|
|
||||||
type: z
|
|
||||||
.enum(["markdown", "diff", "image", "code"])
|
|
||||||
.describe("Type of artifact to present."),
|
|
||||||
title: z
|
|
||||||
.string()
|
|
||||||
.describe(
|
|
||||||
"Title for the artifact (e.g., 'Implementation Plan', 'Refactoring Strategy', '/path/to/project/package.json')."
|
|
||||||
),
|
|
||||||
source: z.union([
|
|
||||||
z.object({
|
|
||||||
type: z.literal("file"),
|
|
||||||
path: z.string(),
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
type: z.literal("command_output"),
|
|
||||||
command: z.string(),
|
|
||||||
}),
|
|
||||||
z.object({
|
|
||||||
type: z.literal("text"),
|
|
||||||
text: z.string(),
|
|
||||||
}),
|
|
||||||
]),
|
|
||||||
}),
|
|
||||||
execute: async () => {
|
|
||||||
// Artifact will be broadcast by orchestrator via onToolCall callback
|
|
||||||
// We just return a simple acknowledgment here
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
message: "Artifact presented to user.",
|
|
||||||
};
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all tools (MCP + manual) for LLM
|
|
||||||
* @param terminalTools - Optional custom terminal tools (per-session). If not provided, uses global singleton.
|
|
||||||
* @param agentTools - Optional agent tools (per-session).
|
|
||||||
*/
|
|
||||||
export async function getAllTools(
|
|
||||||
terminalTools?: ToolSet,
|
|
||||||
agentTools?: ToolSet
|
|
||||||
): Promise<ToolSet> {
|
|
||||||
if (terminalTools) {
|
|
||||||
// Use provided terminal tools (per-session) and merge with Playwright and agent tools
|
|
||||||
const playwrightClient = await getPlaywrightMcpClient().catch((error) => {
|
|
||||||
console.error("Failed to initialize Playwright MCP:", error);
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
|
|
||||||
const playwrightTools: ToolSet = playwrightClient
|
|
||||||
? ((await playwrightClient.tools()) as ToolSet)
|
|
||||||
: {};
|
|
||||||
|
|
||||||
const combinedTools: ToolSet = {
|
|
||||||
...terminalTools,
|
|
||||||
...playwrightTools,
|
|
||||||
...(agentTools ?? {}),
|
|
||||||
...manualTools,
|
|
||||||
};
|
|
||||||
|
|
||||||
return combinedTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to global singleton tools
|
|
||||||
const mcpTools = await getMcpTools();
|
|
||||||
const combinedMcpTools: ToolSet = {
|
|
||||||
...mcpTools,
|
|
||||||
...(agentTools ?? {}),
|
|
||||||
...manualTools,
|
|
||||||
};
|
|
||||||
return combinedMcpTools;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup function to close MCP clients
|
|
||||||
*/
|
|
||||||
export async function closeMcpClients() {
|
|
||||||
if (terminalMcpClient) {
|
|
||||||
await terminalMcpClient.close();
|
|
||||||
terminalMcpClient = null;
|
|
||||||
console.log("Terminal MCP client closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (playwrightMcpClient) {
|
|
||||||
await playwrightMcpClient.close();
|
|
||||||
playwrightMcpClient = null;
|
|
||||||
console.log("Playwright MCP client closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
mcpToolsCache = null;
|
|
||||||
mcpToolsPromise = null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { fetchProviderModelCatalog } from "./model-catalog.js";
|
import { fetchProviderModelCatalog } from "./model-catalog.js";
|
||||||
import type { AgentProvider } from "./agent-sdk-types.js";
|
import type { AgentProvider } from "./agent-sdk-types.js";
|
||||||
import { expandTilde } from "../terminal-mcp/tmux.js";
|
import { expandTilde } from "../../utils/path.js";
|
||||||
|
|
||||||
type ResolveAgentModelOptions = {
|
type ResolveAgentModelOptions = {
|
||||||
provider: AgentProvider;
|
provider: AgentProvider;
|
||||||
|
|||||||
@@ -28,8 +28,7 @@ Conversation with agents:
|
|||||||
|
|
||||||
Agent selection guidance:
|
Agent selection guidance:
|
||||||
- Codex: methodical and slower; great for deep debugging, tracing code paths, refactoring, complex features, and design discussions.
|
- Codex: methodical and slower; great for deep debugging, tracing code paths, refactoring, complex features, and design discussions.
|
||||||
- Claude: fast; strong at tool use (e.g., Playwright MCP, web search), agentic control, and managing other agents; may jump to conclusions—ask it to verify.
|
- Claude: fast; strong at tool use, agentic control, and managing other agents; may jump to conclusions—ask it to verify.
|
||||||
- For debugging with UI/Playwright: Claude can drive Playwright MCP and logging; Codex can audit code and propose fixes.
|
|
||||||
|
|
||||||
Clarifying ambiguous requests:
|
Clarifying ambiguous requests:
|
||||||
- Research first to understand the current state.
|
- Research first to understand the current state.
|
||||||
|
|||||||
@@ -743,11 +743,6 @@ class ClaudeAgentSession implements AgentSession {
|
|||||||
url: agentControlUrl,
|
url: agentControlUrl,
|
||||||
...(agentControlConfig.headers ? { headers: agentControlConfig.headers } : {}),
|
...(agentControlConfig.headers ? { headers: agentControlConfig.headers } : {}),
|
||||||
},
|
},
|
||||||
playwright: {
|
|
||||||
type: "stdio",
|
|
||||||
command: "npx",
|
|
||||||
args: ["@playwright/mcp", "--headless", "--isolated"],
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.config.mcpServers) {
|
if (this.config.mcpServers) {
|
||||||
|
|||||||
@@ -2517,12 +2517,6 @@ function buildCodexMcpConfig(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add playwright MCP server (same as Claude provider)
|
|
||||||
mcpServers["playwright"] = {
|
|
||||||
command: "npx",
|
|
||||||
args: ["@playwright/mcp", "--headless", "--isolated"],
|
|
||||||
};
|
|
||||||
|
|
||||||
// Merge MCP servers from extra.codex.mcp_servers (legacy location)
|
// Merge MCP servers from extra.codex.mcp_servers (legacy location)
|
||||||
const extraCodex = config.extra?.codex as Record<string, unknown> | undefined;
|
const extraCodex = config.extra?.codex as Record<string, unknown> | undefined;
|
||||||
if (extraCodex?.mcp_servers && typeof extraCodex.mcp_servers === "object") {
|
if (extraCodex?.mcp_servers && typeof extraCodex.mcp_servers === "object") {
|
||||||
|
|||||||
@@ -684,16 +684,6 @@ export const SessionStateMessageSchema = z.object({
|
|||||||
type: z.literal("session_state"),
|
type: z.literal("session_state"),
|
||||||
payload: z.object({
|
payload: z.object({
|
||||||
agents: z.array(AgentSnapshotPayloadSchema),
|
agents: z.array(AgentSnapshotPayloadSchema),
|
||||||
commands: z.array(
|
|
||||||
z.object({
|
|
||||||
id: z.string(),
|
|
||||||
name: z.string(),
|
|
||||||
workingDirectory: z.string(),
|
|
||||||
currentCommand: z.string(),
|
|
||||||
isDead: z.boolean(),
|
|
||||||
exitCode: z.number().nullable(),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ import {
|
|||||||
} from "./persistence-hooks.js";
|
} from "./persistence-hooks.js";
|
||||||
import { experimental_createMCPClient } from "ai";
|
import { experimental_createMCPClient } from "ai";
|
||||||
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
||||||
import { InMemoryTransport } from "@modelcontextprotocol/sdk/inMemory.js";
|
|
||||||
import { createTerminalMcpServer } from "./terminal-mcp/index.js";
|
|
||||||
import { fetchProviderModelCatalog } from "./agent/model-catalog.js";
|
import { fetchProviderModelCatalog } from "./agent/model-catalog.js";
|
||||||
import { AgentManager } from "./agent/agent-manager.js";
|
import { AgentManager } from "./agent/agent-manager.js";
|
||||||
import type { ManagedAgent } from "./agent/agent-manager.js";
|
import type { ManagedAgent } from "./agent/agent-manager.js";
|
||||||
@@ -52,7 +50,6 @@ import type {
|
|||||||
AgentPersistenceHandle,
|
AgentPersistenceHandle,
|
||||||
} from "./agent/agent-sdk-types.js";
|
} from "./agent/agent-sdk-types.js";
|
||||||
import { AgentRegistry, type StoredAgentRecord } from "./agent/agent-registry.js";
|
import { AgentRegistry, type StoredAgentRecord } from "./agent/agent-registry.js";
|
||||||
import { expandTilde } from "./terminal-mcp/tmux.js";
|
|
||||||
import {
|
import {
|
||||||
listDirectoryEntries,
|
listDirectoryEntries,
|
||||||
readExplorerFile,
|
readExplorerFile,
|
||||||
@@ -63,13 +60,13 @@ import {
|
|||||||
generateAgentTitle,
|
generateAgentTitle,
|
||||||
isTitleGeneratorInitialized,
|
isTitleGeneratorInitialized,
|
||||||
} from "../services/agent-title-generator.js";
|
} from "../services/agent-title-generator.js";
|
||||||
import type { TerminalManager } from "./terminal-mcp/terminal-manager.js";
|
|
||||||
import {
|
import {
|
||||||
createWorktree,
|
createWorktree,
|
||||||
detectRepoInfo,
|
detectRepoInfo,
|
||||||
slugify,
|
slugify,
|
||||||
validateBranchSlug,
|
validateBranchSlug,
|
||||||
} from "../utils/worktree.js";
|
} from "../utils/worktree.js";
|
||||||
|
import { expandTilde } from "../utils/path.js";
|
||||||
|
|
||||||
type AgentMcpClientConfig = {
|
type AgentMcpClientConfig = {
|
||||||
agentMcpUrl: string;
|
agentMcpUrl: string;
|
||||||
@@ -228,13 +225,6 @@ export class Session {
|
|||||||
private readonly sttManager: STTManager;
|
private readonly sttManager: STTManager;
|
||||||
|
|
||||||
// Per-session MCP client and tools
|
// Per-session MCP client and tools
|
||||||
private terminalMcpClient: Awaited<
|
|
||||||
ReturnType<typeof experimental_createMCPClient>
|
|
||||||
> | null = null;
|
|
||||||
private terminalTools: ToolSet | null = null;
|
|
||||||
private terminalManager: TerminalManager | null = null;
|
|
||||||
private terminalInitPromise: Promise<void> | null = null;
|
|
||||||
private terminalInitError: Error | null = null;
|
|
||||||
private agentMcpClient: Awaited<
|
private agentMcpClient: Awaited<
|
||||||
ReturnType<typeof experimental_createMCPClient>
|
ReturnType<typeof experimental_createMCPClient>
|
||||||
> | null = null;
|
> | null = null;
|
||||||
@@ -279,23 +269,7 @@ export class Session {
|
|||||||
this.ttsManager = new TTSManager(this.conversationId);
|
this.ttsManager = new TTSManager(this.conversationId);
|
||||||
this.sttManager = new STTManager(this.conversationId);
|
this.sttManager = new STTManager(this.conversationId);
|
||||||
|
|
||||||
// Initialize terminal + agent MCP clients asynchronously, but keep promise handles to avoid orphaned rejections
|
// Initialize agent MCP client asynchronously
|
||||||
this.terminalInitPromise = this.initializeTerminalMcp().catch((error) => {
|
|
||||||
const normalizedError =
|
|
||||||
error instanceof Error
|
|
||||||
? error
|
|
||||||
: new Error(
|
|
||||||
typeof error === "string"
|
|
||||||
? error
|
|
||||||
: "Unknown terminal initialization error"
|
|
||||||
);
|
|
||||||
this.terminalInitError = normalizedError;
|
|
||||||
console.error(
|
|
||||||
`[Session ${this.clientId}] Terminal MCP init failed:`,
|
|
||||||
normalizedError
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
void this.initializeAgentMcp();
|
void this.initializeAgentMcp();
|
||||||
this.subscribeToAgentEvents();
|
this.subscribeToAgentEvents();
|
||||||
|
|
||||||
@@ -439,50 +413,6 @@ export class Session {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize Terminal MCP client for this session
|
|
||||||
*/
|
|
||||||
private async initializeTerminalMcp(): Promise<void> {
|
|
||||||
try {
|
|
||||||
// Create Terminal Manager directly
|
|
||||||
const { TerminalManager } = await import(
|
|
||||||
"./terminal-mcp/terminal-manager.js"
|
|
||||||
);
|
|
||||||
this.terminalManager = new TerminalManager(this.conversationId);
|
|
||||||
await this.terminalManager.initialize();
|
|
||||||
|
|
||||||
// Create Terminal MCP server with conversation-specific session
|
|
||||||
const server = await createTerminalMcpServer({
|
|
||||||
sessionName: this.conversationId,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create linked transport pair
|
|
||||||
const [clientTransport, serverTransport] =
|
|
||||||
InMemoryTransport.createLinkedPair();
|
|
||||||
|
|
||||||
// Connect server to its transport
|
|
||||||
await server.connect(serverTransport);
|
|
||||||
|
|
||||||
// Create client connected to the other side
|
|
||||||
this.terminalMcpClient = await experimental_createMCPClient({
|
|
||||||
transport: clientTransport,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get tools from the client
|
|
||||||
this.terminalTools = (await this.terminalMcpClient.tools()) as ToolSet;
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`[Session ${this.clientId}] Terminal MCP initialized with session ${this.conversationId}`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
`[Session ${this.clientId}] Failed to initialize Terminal MCP:`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize Agent MCP client for this session
|
* Initialize Agent MCP client for this session
|
||||||
*/
|
*/
|
||||||
@@ -2299,38 +2229,16 @@ export class Session {
|
|||||||
|
|
||||||
const agents = [...liveAgents, ...persistedAgents];
|
const agents = [...liveAgents, ...persistedAgents];
|
||||||
|
|
||||||
// Get live commands from terminal manager
|
|
||||||
let commands: any[] = [];
|
|
||||||
if (this.terminalInitPromise) {
|
|
||||||
await this.terminalInitPromise;
|
|
||||||
}
|
|
||||||
if (this.terminalInitError) {
|
|
||||||
console.error(
|
|
||||||
`[Session ${this.clientId}] Skipping command listing due to terminal init failure:`,
|
|
||||||
this.terminalInitError
|
|
||||||
);
|
|
||||||
} else if (this.terminalManager) {
|
|
||||||
try {
|
|
||||||
commands = await this.terminalManager.listCommands();
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
`[Session ${this.clientId}] Failed to list commands:`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emit session state
|
// Emit session state
|
||||||
this.emit({
|
this.emit({
|
||||||
type: "session_state",
|
type: "session_state",
|
||||||
payload: {
|
payload: {
|
||||||
agents,
|
agents,
|
||||||
commands,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
`[Session ${this.clientId}] Sent session state: ${agents.length} agents, ${commands.length} commands`
|
`[Session ${this.clientId}] Sent session state: ${agents.length} agents`
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -2714,21 +2622,7 @@ export class Session {
|
|||||||
apiKey: process.env.OPENROUTER_API_KEY,
|
apiKey: process.env.OPENROUTER_API_KEY,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for terminal MCP to initialize if needed
|
// Wait for agent MCP to initialize if needed
|
||||||
if (!this.terminalTools) {
|
|
||||||
console.log(
|
|
||||||
`[Session ${this.clientId}] Waiting for terminal MCP initialization...`
|
|
||||||
);
|
|
||||||
// Wait up to 5 seconds for initialization
|
|
||||||
const startTime = Date.now();
|
|
||||||
while (!this.terminalTools && Date.now() - startTime < 5000) {
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
||||||
}
|
|
||||||
if (!this.terminalTools) {
|
|
||||||
throw new Error("Terminal MCP failed to initialize");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.agentTools) {
|
if (!this.agentTools) {
|
||||||
console.log(
|
console.log(
|
||||||
`[Session ${this.clientId}] Waiting for agent MCP initialization...`
|
`[Session ${this.clientId}] Waiting for agent MCP initialization...`
|
||||||
@@ -2744,7 +2638,7 @@ export class Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const allTools = await getAllTools(this.terminalTools, this.agentTools ?? undefined);
|
const allTools = getAllTools(this.agentTools ?? undefined);
|
||||||
|
|
||||||
const result = await streamText({
|
const result = await streamText({
|
||||||
model: openrouter("anthropic/claude-haiku-4.5"),
|
model: openrouter("anthropic/claude-haiku-4.5"),
|
||||||
@@ -3239,36 +3133,7 @@ export class Session {
|
|||||||
this.ttsManager.cleanup();
|
this.ttsManager.cleanup();
|
||||||
this.sttManager.cleanup();
|
this.sttManager.cleanup();
|
||||||
|
|
||||||
// Kill tmux session for this conversation
|
|
||||||
try {
|
|
||||||
console.log(
|
|
||||||
`[Session ${this.clientId}] Killing tmux session ${this.conversationId}`
|
|
||||||
);
|
|
||||||
await execAsync(`tmux kill-session -t ${this.conversationId}`);
|
|
||||||
console.log(
|
|
||||||
`[Session ${this.clientId}] Tmux session ${this.conversationId} killed`
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
// Session might not exist or already be killed - that's okay
|
|
||||||
console.log(
|
|
||||||
`[Session ${this.clientId}] Tmux session cleanup (session may not exist):`,
|
|
||||||
error instanceof Error ? error.message : String(error)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close MCP clients
|
// Close MCP clients
|
||||||
if (this.terminalMcpClient) {
|
|
||||||
try {
|
|
||||||
await this.terminalMcpClient.close();
|
|
||||||
console.log(`[Session ${this.clientId}] Terminal MCP client closed`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(
|
|
||||||
`[Session ${this.clientId}] Failed to close Terminal MCP client:`,
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.agentMcpClient) {
|
if (this.agentMcpClient) {
|
||||||
try {
|
try {
|
||||||
await this.agentMcpClient.close();
|
await this.agentMcpClient.close();
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import os from "os";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand tilde in path to home directory
|
||||||
|
*/
|
||||||
|
export function expandTilde(path: string): string {
|
||||||
|
if (path.startsWith("~/")) {
|
||||||
|
const homeDir = process.env.HOME || os.homedir();
|
||||||
|
return path.replace("~", homeDir);
|
||||||
|
}
|
||||||
|
if (path === "~") {
|
||||||
|
return process.env.HOME || os.homedir();
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user