feat(agents): parameterized agent selection via agentName#349
Conversation
Add full protocol support for entry-point-specific agent selection. **Flow:** 1. Client passes agentName in send message options 2. Server sets MITZO_AGENT_NAME env var 3. Harness hook reads env var and requests agent-specific boot context 4. ContexGin compiles context per agent definition **Changes:** - packages/protocol/src/ws-schemas-v2.ts — add agentName to V2SendMessage - server/ws-handler-v2.ts — pass agentName to startChat() - server/chat.ts — set MITZO_AGENT_NAME env var, use in recipe lookup - packages/harness/src/session-registry.ts — add agentName to ManagedSession - packages/client/src/store.ts — add agentName to SendMessageOptions/PendingSession - frontend/src/pages/ChatView.tsx — forward agentName from pending session Defaults to 'mitzo-conversational' when not specified. Enables future UI entry points (Telos, calendar) to request specialized agents. Companion PR: mgmt#<number> Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Wire TodoView to pass agentName: 'mitzo-telos' when starting a session from a Telos task. This triggers the specialized Telos agent with task-specific context blocks and governance. Calendar integration not yet implemented (CalendarView is read-only). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 5 issue(s) (1 critical) (3 warning).
server/chat.ts
The core plumbing is clean and follows the established telosTaskId pattern, but the user-controlled agentName is used in file path construction (join(cwd, '.agents', agentName + '.yaml')) without any validation — this is a path traversal vulnerability that needs a format constraint at the protocol schema level.
- 🔴 unsafe_assumptions (L781): Path traversal vulnerability:
agentNameoriginates from unauthenticated WebSocket input (z.string().optional()— no format constraint) and is interpolated directly into a file path:join(cwd, '.agents', \${agentName}.yaml`). A malicious client can sendagentName: '../../etc/passwd'to read arbitrary YAML-parseable files. The try-catch silences failures butloadAgentDefinitionmay still parse and return data from out-of-bounds paths. Add a Zod regex constraint (e.g.,z.string().regex(/^[a-zA-Z0-9_-]+$/)) inws-schemas-v2.tsor validate withpath.basename()before constructingrecipePath.[fixable]` - 🟡 unsafe_assumptions (L729):
agentNameis also injected into the process environment asMITZO_AGENT_NAMEwithout sanitization. While less dangerous than the path traversal, an attacker-controlled env var value containing special characters could affect downstream shell commands or child processes that read this variable.[fixable] - 🟡 missing_tests (L781): No test coverage for the
agentNameparameter. The existingws-handler-v2.test.ts(2745 lines) has noagentNametests. At minimum: (1) verifyagentNameis forwarded from WS message tostartChat, (2) verify the default fallback tomitzo-conversational, (3) verify path traversal attempts are rejected.[fixable] - 🔵 style (L728): The default agent name
'mitzo-conversational'is a magic string. Consider extracting it toconstants.ts(where other server-wide defaults live) since it appears in an env var assignment and a file path construction — a single source of truth prevents drift.[fixable]
packages/harness/src/session-registry.ts
The core plumbing is clean and follows the established telosTaskId pattern, but the user-controlled agentName is used in file path construction (join(cwd, '.agents', agentName + '.yaml')) without any validation — this is a path traversal vulnerability that needs a format constraint at the protocol schema level.
- 🟡 regressions (L50):
agentNameis added to theManagedSessioninterface and stored at registration (chat.ts:682), but is never read back anywhere in the codebase (session.agentNamehas zero grep hits). This is dead metadata — either it should be consumed (e.g., for session restore, reattach, or auditing) or removed from the interface to avoid confusion.[fixable]
| try { | ||
| if (compileModule.loadAgentDefinition) { | ||
| const recipePath = join(cwd, '.agents', 'mitzo-conversational.yaml'); | ||
| const recipePath = join(cwd, '.agents', `${agentName}.yaml`); |
There was a problem hiding this comment.
🔴 unsafe_assumptions: Path traversal vulnerability: agentName originates from unauthenticated WebSocket input (z.string().optional() — no format constraint) and is interpolated directly into a file path: join(cwd, '.agents', \${agentName}.yaml`). A malicious client can send agentName: '../../etc/passwd'to read arbitrary YAML-parseable files. The try-catch silences failures butloadAgentDefinitionmay still parse and return data from out-of-bounds paths. Add a Zod regex constraint (e.g.,z.string().regex(/^[a-zA-Z0-9_-]+$/)) in ws-schemas-v2.tsor validate withpath.basename()before constructingrecipePath. [fixable]`
| const sessionEnv = sdkEnv(); | ||
| sessionEnv.MITZO_SESSION_ID = wtId; | ||
| const agentName = options.agentName || 'mitzo-conversational'; | ||
| sessionEnv.MITZO_AGENT_NAME = agentName; |
There was a problem hiding this comment.
🟡 unsafe_assumptions: agentName is also injected into the process environment as MITZO_AGENT_NAME without sanitization. While less dangerous than the path traversal, an attacker-controlled env var value containing special characters could affect downstream shell commands or child processes that read this variable. [fixable]
| try { | ||
| if (compileModule.loadAgentDefinition) { | ||
| const recipePath = join(cwd, '.agents', 'mitzo-conversational.yaml'); | ||
| const recipePath = join(cwd, '.agents', `${agentName}.yaml`); |
There was a problem hiding this comment.
🟡 missing_tests: No test coverage for the agentName parameter. The existing ws-handler-v2.test.ts (2745 lines) has no agentName tests. At minimum: (1) verify agentName is forwarded from WS message to startChat, (2) verify the default fallback to mitzo-conversational, (3) verify path traversal attempts are rejected. [fixable]
| // Build session env with worktree paths for the agent (all repos including primary) | ||
| const sessionEnv = sdkEnv(); | ||
| sessionEnv.MITZO_SESSION_ID = wtId; | ||
| const agentName = options.agentName || 'mitzo-conversational'; |
There was a problem hiding this comment.
🔵 style: The default agent name 'mitzo-conversational' is a magic string. Consider extracting it to constants.ts (where other server-wide defaults live) since it appears in an env var assignment and a file path construction — a single source of truth prevents drift. [fixable]
| /** Active subagent task IDs — task_id → tool_use_id (parent_tool_use_id). */ | ||
| activeTaskIds: Map<string, string>; | ||
| /** Agent definition name for ContexGin boot context (e.g., mitzo-conversational, mitzo-telos). */ | ||
| agentName?: string; |
There was a problem hiding this comment.
🟡 regressions: agentName is added to the ManagedSession interface and stored at registration (chat.ts:682), but is never read back anywhere in the codebase (session.agentName has zero grep hits). This is dead metadata — either it should be consumed (e.g., for session restore, reattach, or auditing) or removed from the interface to avoid confusion. [fixable]
Add "Prep for this meeting" button to calendar event cards. Clicking initiates a session with the mitzo-calendar agent and pre-populated meeting context. **Changes:** - EventCard.tsx — add prep button + navigation handler - calendar-utils.ts — prompt and context builders for meeting prep - calendar.css — styles for action button **Flow:** 1. User taps event to expand 2. Clicks "Prep for this meeting" 3. Session starts with mitzo-calendar agent 4. Context includes attendees, time, doc links Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
**Security fixes:** - Add regex validation to agentName (/^[a-zA-Z0-9_-]+$/) - prevents path traversal - Extract DEFAULT_AGENT_NAME constant to avoid magic strings **Changes:** - packages/protocol/src/ws-schemas-v2.ts - add regex constraint to agentName - server/constants.ts - add DEFAULT_AGENT_NAME constant - server/chat.ts - use constant instead of magic string Addresses Centaur review findings (critical + style issues). Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Remove unused session.agentName field from ManagedSession interface - Add comprehensive test coverage for agentName parameter (4 tests) - Test forwarding to startChat - Test default behavior when omitted - Test path traversal rejection via schema validation - Test valid agent name patterns - Replace require() with ES6 import for @mitzo/protocol in tests All tests passing (109/109). Addresses all red/yellow/style issues from Centaur review on PR #349. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
|
centaur review |
|
/centaur review |
dimakis
left a comment
There was a problem hiding this comment.
Centaur Review
Found 5 issue(s) (1 critical) (1 warning).
vitest.setup.ts
Core agentName plumbing is solid with proper Zod regex validation preventing path traversal. Main issues: vitest.setup.ts is dead code (not wired into config), and the new calendar-utils module lacks the TDD-mandated test file.
- 🔴 bugs (L1): vitest.setup.ts is created but never wired into vitest.config.ts. Without
setupFiles: ['./vitest.setup.ts']in the test config, the EventSource mock is dead code and any test relying on it will fail with a ReferenceError.[fixable]
frontend/src/lib/calendar-utils.ts
Core agentName plumbing is solid with proper Zod regex validation preventing path traversal. Main issues: vitest.setup.ts is dead code (not wired into config), and the new calendar-utils module lacks the TDD-mandated test file.
- 🟡 missing_tests: New pure utility file with three functions (buildMeetingPrepPrompt, buildMeetingContext, formatEventTime) has no test file. These are easily unit-testable and the project mandates TDD — tests should be the first artifact.
[fixable] - 🔵 style (L6): Multi-line JSDoc comments on buildMeetingPrepPrompt and buildMeetingContext restate what the function name already says. CLAUDE.md says 'default to writing no comments' and 'don't explain WHAT the code does'. Remove the doc blocks.
[fixable]
server/chat.ts
Core agentName plumbing is solid with proper Zod regex validation preventing path traversal. Main issues: vitest.setup.ts is dead code (not wired into config), and the new calendar-utils module lacks the TDD-mandated test file.
- 🔵 unsafe_assumptions (L728):
options.agentName || DEFAULT_AGENT_NAMEtreats empty string as falsy and falls back to the default. This is fine behavior-wise, but??would be more intentional since Zod's regex already rejects empty strings — using||obscures whether empty-string fallback is deliberate.[fixable]
frontend/src/pages/TodoView.tsx
Core agentName plumbing is solid with proper Zod regex validation preventing path traversal. Main issues: vitest.setup.ts is dead code (not wired into config), and the new calendar-utils module lacks the TDD-mandated test file.
- 🔵 regressions (L191): Adding
telosTaskId: item.idis a behavioral change unrelated to the agentName feature — previously TodoView did not set telosTaskId on the pending session. If this was already handled elsewhere (e.g. by ChatView or the store), this could cause duplicate assignment. Confirm this is intentional and not already set by another path.
| @@ -0,0 +1,23 @@ | |||
| /** | |||
There was a problem hiding this comment.
🔴 bugs: vitest.setup.ts is created but never wired into vitest.config.ts. Without setupFiles: ['./vitest.setup.ts'] in the test config, the EventSource mock is dead code and any test relying on it will fail with a ReferenceError. [fixable]
| /** | ||
| * Build a meeting prep prompt for calendar events. | ||
| */ | ||
| export function buildMeetingPrepPrompt(event: CalendarEvent): string { |
There was a problem hiding this comment.
🔵 style: Multi-line JSDoc comments on buildMeetingPrepPrompt and buildMeetingContext restate what the function name already says. CLAUDE.md says 'default to writing no comments' and 'don't explain WHAT the code does'. Remove the doc blocks. [fixable]
| // Build session env with worktree paths for the agent (all repos including primary) | ||
| const sessionEnv = sdkEnv(); | ||
| sessionEnv.MITZO_SESSION_ID = wtId; | ||
| const agentName = options.agentName || DEFAULT_AGENT_NAME; |
There was a problem hiding this comment.
🔵 unsafe_assumptions: options.agentName || DEFAULT_AGENT_NAME treats empty string as falsy and falls back to the default. This is fine behavior-wise, but ?? would be more intentional since Zod's regex already rejects empty strings — using || obscures whether empty-string fallback is deliberate. [fixable]
| @@ -190,6 +190,8 @@ export function TodoView() { | |||
| setPendingSession({ | |||
| prompt: buildPrompt(item), | |||
There was a problem hiding this comment.
🔵 regressions: Adding telosTaskId: item.id is a behavioral change unrelated to the agentName feature — previously TodoView did not set telosTaskId on the pending session. If this was already handled elsewhere (e.g. by ChatView or the store), this could cause duplicate assignment. Confirm this is intentional and not already set by another path.
- Remove dead vitest.setup.ts (not wired into vitest.config.ts) - Add calendar-utils unit tests (10 tests for all 3 functions) - Remove redundant JSDoc comments from calendar-utils - Use ?? instead of || for agentName fallback (intentional nullish check) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Summary
Add full protocol support for entry-point-specific agent selection, plus UI integration for Telos and Calendar entry points.
Flow:
agentNamein send message optionsMITZO_AGENT_NAMEenv varChanges
Protocol:
packages/protocol/src/ws-schemas-v2.ts— addagentNametoV2SendMessageServer:
server/ws-handler-v2.ts— passagentNametostartChat()server/chat.ts— setMITZO_AGENT_NAMEenv var, use in recipe lookuppackages/harness/src/session-registry.ts— addagentNametoManagedSessionClient:
packages/client/src/store.ts— addagentNametoSendMessageOptionsandPendingSessionfrontend/src/pages/ChatView.tsx— forwardagentNamefrom pending sessionUI Integration:
frontend/src/pages/TodoView.tsx— Telos tasks usemitzo-telosagent ✅frontend/src/components/EventCard.tsx— "Prep for this meeting" button ✅frontend/src/lib/calendar-utils.ts— meeting prep prompt and context builders ✅frontend/src/styles/calendar.css— action button styles ✅Behavior
'mitzo-conversational'when not specifiedmitzo-telosagentmitzo-calendaragentNext Steps
mitzo-pr-review,mitzo-email)Companion PR: dimakis/mgmt#127
Test Plan
mitzo-conversationalagentName: 'mitzo-telos'agentName: 'mitzo-calendar'🤖 Generated with Claude Code