agent-orchestrator-mcp-server 0.4.7 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,22 +21,22 @@ MCP server for PulseMCP's agent-orchestrator: a Claude Code + MCP-powered agent-
21
21
 
22
22
  ### Tools
23
23
 
24
- | Tool | Tool Group | Read/Write | Description |
25
- | -------------------------- | ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
26
- | `quick_search_sessions` | sessions | read | Quick title-based search/list sessions with optional ID lookup, title query, and status filter |
27
- | `get_session` | sessions | read | Get detailed session info with optional logs, transcripts, and transcript format (text/json) |
28
- | `get_configs` | sessions | read | Fetch all static configuration (MCP servers, agent roots, stop conditions) |
29
- | `get_transcript_archive` | sessions | read | Get download URL and metadata for the transcript archive zip file |
30
- | `start_session` | sessions | write | Create and start a new agent session |
31
- | `action_session` | sessions | write | Perform actions: follow_up, pause, restart, archive, unarchive, change_mcp_servers, fork, refresh, refresh_all, update_notes, toggle_favorite, bulk_archive |
32
- | `manage_enqueued_messages` | sessions | write | Manage session message queue: list, get, create, update, delete, reorder, interrupt |
33
- | `get_notifications` | notifications | read | Get/list notifications and badge count |
34
- | `send_push_notification` | notifications | write | Send a push notification about a session needing human attention |
35
- | `action_notification` | notifications | write | Mark read, mark all read, dismiss, dismiss all read notifications |
36
- | `search_triggers` | triggers | read | Search/list automation triggers with optional channel info |
37
- | `action_trigger` | triggers | write | Create, update, delete, toggle automation triggers |
38
- | `get_system_health` | health | read | Get system health report and optional CLI status |
39
- | `action_health` | health | write | System maintenance: cleanup_processes, retry_sessions, archive_old, cli_refresh, cli_clear_cache |
24
+ | Tool | Tool Group | Read/Write | Description |
25
+ | -------------------------- | ------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
26
+ | `quick_search_sessions` | sessions | read | Quick title-based search/list sessions with optional ID lookup, title query, and status filter |
27
+ | `get_session` | sessions | read | Get detailed session info with optional logs, transcripts, and transcript format (text/json) |
28
+ | `get_configs` | sessions | read | Fetch all static configuration (MCP servers, agent roots, stop conditions) |
29
+ | `get_transcript_archive` | sessions | read | Get download URL and metadata for the transcript archive zip file |
30
+ | `start_session` | sessions | write | Create and start a new agent session |
31
+ | `action_session` | sessions | write | Perform actions: follow_up, pause, restart, archive, unarchive, change_mcp_servers, change_model, fork, refresh, refresh_all, update_notes, update_title, toggle_favorite, bulk_archive |
32
+ | `manage_enqueued_messages` | sessions | write | Manage session message queue: list, get, create, update, delete, reorder, interrupt |
33
+ | `get_notifications` | notifications | read | Get/list notifications and badge count |
34
+ | `send_push_notification` | notifications | write | Send a push notification about a session needing human attention |
35
+ | `action_notification` | notifications | write | Mark read, mark all read, dismiss, dismiss all read notifications |
36
+ | `search_triggers` | triggers | read | Search/list automation triggers with optional channel info |
37
+ | `action_trigger` | triggers | write | Create, update, delete, toggle automation triggers |
38
+ | `get_system_health` | health | read | Get system health report and optional CLI status |
39
+ | `action_health` | health | write | System maintenance: cleanup_processes, retry_sessions, archive_old, cli_refresh, cli_clear_cache |
40
40
 
41
41
  ### Resources
42
42
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-orchestrator-mcp-server",
3
- "version": "0.4.7",
3
+ "version": "0.6.0",
4
4
  "description": "Local implementation of agent-orchestrator MCP server",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
@@ -26,15 +26,11 @@ export interface AgentRootValidationResult {
26
26
  * Validate a start_session request against the allowed agent roots constraints.
27
27
  *
28
28
  * When ALLOWED_AGENT_ROOTS is set:
29
- * - git_root (and optionally branch/subdirectory) must match one of the allowed agent roots
29
+ * - agent_root must be provided and must be one of the allowed agent root names
30
30
  * - mcp_servers must exactly match the default_mcp_servers of that agent root
31
31
  * (no more, no less — any deviation is rejected)
32
32
  *
33
- * When multiple allowed agent roots share the same git_root, branch and subdirectory
34
- * are used to disambiguate. This is critical for monorepo setups where multiple agent
35
- * roots point to the same repository but different subdirectories.
36
- *
37
33
  * Returns { valid: true } if the request is allowed, or { valid: false, error: string } if not.
38
34
  */
39
- export declare function validateAgentRootConstraints(allowedRoots: string[] | null, agentRoots: AgentRootInfo[], gitRoot?: string, mcpServers?: string[], branch?: string, subdirectory?: string): AgentRootValidationResult;
35
+ export declare function validateAgentRootConstraints(allowedRoots: string[] | null, agentRoots: AgentRootInfo[], agentRootName?: string, mcpServers?: string[]): AgentRootValidationResult;
40
36
  //# sourceMappingURL=allowed-agent-roots.d.ts.map
@@ -39,46 +39,37 @@ export function filterAgentRoots(agentRoots, allowedRoots) {
39
39
  * Validate a start_session request against the allowed agent roots constraints.
40
40
  *
41
41
  * When ALLOWED_AGENT_ROOTS is set:
42
- * - git_root (and optionally branch/subdirectory) must match one of the allowed agent roots
42
+ * - agent_root must be provided and must be one of the allowed agent root names
43
43
  * - mcp_servers must exactly match the default_mcp_servers of that agent root
44
44
  * (no more, no less — any deviation is rejected)
45
45
  *
46
- * When multiple allowed agent roots share the same git_root, branch and subdirectory
47
- * are used to disambiguate. This is critical for monorepo setups where multiple agent
48
- * roots point to the same repository but different subdirectories.
49
- *
50
46
  * Returns { valid: true } if the request is allowed, or { valid: false, error: string } if not.
51
47
  */
52
- export function validateAgentRootConstraints(allowedRoots, agentRoots, gitRoot, mcpServers, branch, subdirectory) {
48
+ export function validateAgentRootConstraints(allowedRoots, agentRoots, agentRootName, mcpServers) {
53
49
  if (allowedRoots === null) {
54
50
  return { valid: true };
55
51
  }
56
- // Find all allowed agent roots that match by git_root
57
- const candidates = agentRoots.filter((root) => allowedRoots.includes(root.name) && root.git_root === gitRoot);
58
- // When multiple candidates share the same git_root, disambiguate using branch and subdirectory
59
- let matchingRoot;
60
- if (candidates.length > 1) {
61
- matchingRoot = candidates.find((root) => {
62
- const branchMatch = !branch || (root.default_branch ?? 'main') === branch;
63
- const subdirMatch = !subdirectory || root.default_subdirectory === subdirectory;
64
- return branchMatch && subdirMatch;
65
- });
52
+ if (!agentRootName) {
53
+ return {
54
+ valid: false,
55
+ error: `ALLOWED_AGENT_ROOTS is set — agent_root is required. ` +
56
+ `Allowed agent roots: ${allowedRoots.join(', ')}`,
57
+ };
66
58
  }
67
- else {
68
- matchingRoot = candidates[0];
59
+ if (!allowedRoots.includes(agentRootName)) {
60
+ return {
61
+ valid: false,
62
+ error: `ALLOWED_AGENT_ROOTS is set — agent_root "${agentRootName}" is not permitted. ` +
63
+ `Allowed agent roots: ${allowedRoots.join(', ')}`,
64
+ };
69
65
  }
66
+ // Find the matching agent root config to validate mcp_servers
67
+ const matchingRoot = agentRoots.find((root) => root.name === agentRootName);
70
68
  if (!matchingRoot) {
71
- const allowedNames = allowedRoots.join(', ');
72
- const allowedGitRoots = agentRoots
73
- .filter((root) => allowedRoots.includes(root.name))
74
- .map((root) => root.git_root);
75
69
  return {
76
70
  valid: false,
77
- error: `ALLOWED_AGENT_ROOTS is set only the following agent roots are permitted: ${allowedNames}. ` +
78
- `The provided git_root "${gitRoot || '(not provided)'}" does not match any allowed agent root. ` +
79
- (allowedGitRoots.length > 0
80
- ? `Allowed git_root values: ${allowedGitRoots.join(', ')}`
81
- : 'No matching agent roots found in configuration.'),
71
+ error: `Agent root "${agentRootName}" is in the allowed list but was not found in the configuration. ` +
72
+ `Available agent roots: ${agentRoots.map((r) => r.name).join(', ')}`,
82
73
  };
83
74
  }
84
75
  // Validate mcp_servers matches the default_mcp_servers exactly
@@ -17,6 +17,7 @@ export interface RawAgentRoot {
17
17
  default?: boolean;
18
18
  default_mcp_servers?: string[];
19
19
  default_skills?: string[];
20
+ default_model?: string;
20
21
  }
21
22
  /**
22
23
  * Maps a raw API agent root to the normalized AgentRootInfo interface.
@@ -53,6 +54,7 @@ export interface IAgentOrchestratorClient {
53
54
  pauseSession(id: string | number): Promise<SessionActionResponse>;
54
55
  restartSession(id: string | number): Promise<SessionActionResponse>;
55
56
  changeMcpServers(id: string | number, mcp_servers: string[]): Promise<Session>;
57
+ changeModel(id: string | number, model: string): Promise<Session>;
56
58
  listLogs(sessionId: string | number, options?: {
57
59
  level?: LogLevel;
58
60
  page?: number;
@@ -175,6 +177,7 @@ export declare class AgentOrchestratorClient implements IAgentOrchestratorClient
175
177
  pauseSession(id: string | number): Promise<SessionActionResponse>;
176
178
  restartSession(id: string | number): Promise<SessionActionResponse>;
177
179
  changeMcpServers(id: string | number, mcp_servers: string[]): Promise<Session>;
180
+ changeModel(id: string | number, model: string): Promise<Session>;
178
181
  listLogs(sessionId: string | number, options?: {
179
182
  level?: LogLevel;
180
183
  page?: number;
@@ -247,6 +247,15 @@ export function createIntegrationMockOrchestratorClient(initialMockData) {
247
247
  session.updated_at = new Date().toISOString();
248
248
  return session;
249
249
  },
250
+ async changeModel(id, model) {
251
+ const session = mockData.sessions?.find((s) => s.id === Number(id) || s.slug === String(id));
252
+ if (!session) {
253
+ throw new Error(`API Error (404): Session not found`);
254
+ }
255
+ session.config = { ...session.config, model };
256
+ session.updated_at = new Date().toISOString();
257
+ return session;
258
+ },
250
259
  async listLogs(sessionId, options) {
251
260
  let logs = (mockData.logs || []).filter((l) => l.session_id === Number(sessionId));
252
261
  if (options?.level) {
@@ -19,6 +19,7 @@ export function mapAgentRoot(raw) {
19
19
  default_stop_condition: raw.default_stop_condition,
20
20
  default_mcp_servers: raw.default_mcp_servers,
21
21
  default_skills: raw.default_skills,
22
+ default_model: raw.default_model,
22
23
  };
23
24
  }
24
25
  /** Default timeout for API requests in milliseconds */
@@ -177,7 +178,15 @@ export class AgentOrchestratorClient {
177
178
  return response.session;
178
179
  }
179
180
  async createSession(data) {
180
- const response = await this.request('POST', '/sessions', data);
181
+ // Remap `skills` to `catalog_skills` and `plugins` to `catalog_plugins`
182
+ // for the API (Rails strong params expects `catalog_skills`/`catalog_plugins`)
183
+ const { skills, plugins, ...rest } = data;
184
+ const body = {
185
+ ...rest,
186
+ ...(skills !== undefined && { catalog_skills: skills }),
187
+ ...(plugins !== undefined && { catalog_plugins: plugins }),
188
+ };
189
+ const response = await this.request('POST', '/sessions', body);
181
190
  return response.session;
182
191
  }
183
192
  async updateSession(id, data) {
@@ -210,6 +219,12 @@ export class AgentOrchestratorClient {
210
219
  });
211
220
  return response.session;
212
221
  }
222
+ async changeModel(id, model) {
223
+ const response = await this.request('PATCH', `/sessions/${id}/model`, {
224
+ model,
225
+ });
226
+ return response.session;
227
+ }
213
228
  // Logs
214
229
  async listLogs(sessionId, options) {
215
230
  return this.request('GET', `/sessions/${sessionId}/logs`, undefined, options);
@@ -3,25 +3,31 @@ import { z } from 'zod';
3
3
  import type { IAgentOrchestratorClient } from '../orchestrator-client/orchestrator-client.js';
4
4
  export declare const ActionSessionSchema: z.ZodObject<{
5
5
  session_id: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodNumber]>>;
6
- action: z.ZodEnum<["follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "fork", "refresh", "refresh_all", "update_notes", "toggle_favorite", "bulk_archive"]>;
6
+ action: z.ZodEnum<["follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "change_model", "fork", "refresh", "refresh_all", "update_notes", "update_title", "toggle_favorite", "bulk_archive"]>;
7
7
  prompt: z.ZodOptional<z.ZodString>;
8
8
  mcp_servers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ model: z.ZodOptional<z.ZodString>;
9
10
  message_index: z.ZodOptional<z.ZodNumber>;
10
11
  session_notes: z.ZodOptional<z.ZodString>;
11
12
  session_ids: z.ZodOptional<z.ZodArray<z.ZodNumber, "many">>;
13
+ title: z.ZodOptional<z.ZodString>;
12
14
  }, "strip", z.ZodTypeAny, {
13
- action: "follow_up" | "pause" | "restart" | "archive" | "unarchive" | "change_mcp_servers" | "fork" | "refresh" | "refresh_all" | "update_notes" | "toggle_favorite" | "bulk_archive";
14
- mcp_servers?: string[] | undefined;
15
+ action: "follow_up" | "pause" | "restart" | "archive" | "unarchive" | "change_mcp_servers" | "change_model" | "fork" | "refresh" | "refresh_all" | "update_notes" | "update_title" | "toggle_favorite" | "bulk_archive";
15
16
  prompt?: string | undefined;
17
+ title?: string | undefined;
18
+ mcp_servers?: string[] | undefined;
16
19
  session_id?: string | number | undefined;
20
+ model?: string | undefined;
17
21
  message_index?: number | undefined;
18
22
  session_notes?: string | undefined;
19
23
  session_ids?: number[] | undefined;
20
24
  }, {
21
- action: "follow_up" | "pause" | "restart" | "archive" | "unarchive" | "change_mcp_servers" | "fork" | "refresh" | "refresh_all" | "update_notes" | "toggle_favorite" | "bulk_archive";
22
- mcp_servers?: string[] | undefined;
25
+ action: "follow_up" | "pause" | "restart" | "archive" | "unarchive" | "change_mcp_servers" | "change_model" | "fork" | "refresh" | "refresh_all" | "update_notes" | "update_title" | "toggle_favorite" | "bulk_archive";
23
26
  prompt?: string | undefined;
27
+ title?: string | undefined;
28
+ mcp_servers?: string[] | undefined;
24
29
  session_id?: string | number | undefined;
30
+ model?: string | undefined;
25
31
  message_index?: number | undefined;
26
32
  session_notes?: string | undefined;
27
33
  session_ids?: number[] | undefined;
@@ -40,8 +46,8 @@ export declare function actionSessionTool(_server: Server, clientFactory: () =>
40
46
  };
41
47
  action: {
42
48
  type: string;
43
- enum: readonly ["follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "fork", "refresh", "refresh_all", "update_notes", "toggle_favorite", "bulk_archive"];
44
- description: "Action to perform: \"follow_up\", \"pause\", \"restart\", \"archive\", \"unarchive\", \"change_mcp_servers\", \"fork\", \"refresh\", \"refresh_all\", \"update_notes\", \"toggle_favorite\", \"bulk_archive\"";
49
+ enum: readonly ["follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "change_model", "fork", "refresh", "refresh_all", "update_notes", "update_title", "toggle_favorite", "bulk_archive"];
50
+ description: "Action to perform: \"follow_up\", \"pause\", \"restart\", \"archive\", \"unarchive\", \"change_mcp_servers\", \"change_model\", \"fork\", \"refresh\", \"refresh_all\", \"update_notes\", \"update_title\", \"toggle_favorite\", \"bulk_archive\"";
45
51
  };
46
52
  prompt: {
47
53
  type: string;
@@ -54,6 +60,10 @@ export declare function actionSessionTool(_server: Server, clientFactory: () =>
54
60
  };
55
61
  description: "Required for \"change_mcp_servers\" action. Array of MCP server names to set for the session.";
56
62
  };
63
+ model: {
64
+ type: string;
65
+ description: "Required for \"change_model\" action. The model identifier to use (e.g., \"opus\", \"sonnet\").";
66
+ };
57
67
  message_index: {
58
68
  type: string;
59
69
  description: "Required for \"fork\" action. The transcript message index to fork from.";
@@ -69,6 +79,10 @@ export declare function actionSessionTool(_server: Server, clientFactory: () =>
69
79
  };
70
80
  description: "Required for \"bulk_archive\" action. Array of session IDs to archive.";
71
81
  };
82
+ title: {
83
+ type: string;
84
+ description: "Required for \"update_title\" action. The new title for the session.";
85
+ };
72
86
  };
73
87
  required: string[];
74
88
  };
@@ -2,12 +2,14 @@ import { z } from 'zod';
2
2
  import { parseAllowedAgentRoots } from '../allowed-agent-roots.js';
3
3
  const PARAM_DESCRIPTIONS = {
4
4
  session_id: 'Session ID (numeric) or slug (string). Required for most actions. Not required for "refresh_all" and "bulk_archive".',
5
- action: 'Action to perform: "follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "fork", "refresh", "refresh_all", "update_notes", "toggle_favorite", "bulk_archive"',
5
+ action: 'Action to perform: "follow_up", "pause", "restart", "archive", "unarchive", "change_mcp_servers", "change_model", "fork", "refresh", "refresh_all", "update_notes", "update_title", "toggle_favorite", "bulk_archive"',
6
6
  prompt: 'Required for "follow_up" action. The prompt to send to the agent. Not used for other actions.',
7
7
  mcp_servers: 'Required for "change_mcp_servers" action. Array of MCP server names to set for the session.',
8
+ model: 'Required for "change_model" action. The model identifier to use (e.g., "opus", "sonnet").',
8
9
  message_index: 'Required for "fork" action. The transcript message index to fork from.',
9
10
  session_notes: 'Required for "update_notes" action. The notes text to set on the session.',
10
11
  session_ids: 'Required for "bulk_archive" action. Array of session IDs to archive.',
12
+ title: 'Required for "update_title" action. The new title for the session.',
11
13
  };
12
14
  const ACTION_ENUM = [
13
15
  'follow_up',
@@ -16,10 +18,12 @@ const ACTION_ENUM = [
16
18
  'archive',
17
19
  'unarchive',
18
20
  'change_mcp_servers',
21
+ 'change_model',
19
22
  'fork',
20
23
  'refresh',
21
24
  'refresh_all',
22
25
  'update_notes',
26
+ 'update_title',
23
27
  'toggle_favorite',
24
28
  'bulk_archive',
25
29
  ];
@@ -28,9 +32,11 @@ export const ActionSessionSchema = z.object({
28
32
  action: z.enum(ACTION_ENUM).describe(PARAM_DESCRIPTIONS.action),
29
33
  prompt: z.string().optional().describe(PARAM_DESCRIPTIONS.prompt),
30
34
  mcp_servers: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.mcp_servers),
35
+ model: z.string().optional().describe(PARAM_DESCRIPTIONS.model),
31
36
  message_index: z.number().optional().describe(PARAM_DESCRIPTIONS.message_index),
32
37
  session_notes: z.string().optional().describe(PARAM_DESCRIPTIONS.session_notes),
33
38
  session_ids: z.array(z.number()).optional().describe(PARAM_DESCRIPTIONS.session_ids),
39
+ title: z.string().optional().describe(PARAM_DESCRIPTIONS.title),
34
40
  });
35
41
  const TOOL_DESCRIPTION = `Perform an action on an agent session.
36
42
 
@@ -41,18 +47,21 @@ const TOOL_DESCRIPTION = `Perform an action on an agent session.
41
47
  - **archive**: Archive a session (marks as completed)
42
48
  - **unarchive**: Restore an archived session to idle "needs_input" status
43
49
  - **change_mcp_servers**: Update the MCP servers for a session (requires "mcp_servers" parameter)
50
+ - **change_model**: Update the model for a session (requires "model" parameter, e.g., "opus", "sonnet")
44
51
  - **fork**: Fork a session from a specific transcript message (requires "message_index")
45
52
  - **refresh**: Refresh a single session's status from the execution provider
46
53
  - **refresh_all**: Refresh all active sessions (no session_id needed)
47
54
  - **update_notes**: Update the notes on a session (requires "session_notes")
55
+ - **update_title**: Update the title of a session (requires "title")
48
56
  - **toggle_favorite**: Toggle favorite status on a session
49
57
  - **bulk_archive**: Archive multiple sessions at once (requires "session_ids", no session_id needed)
50
58
 
51
59
  **Use cases:**
52
60
  - Provide additional instructions to an agent
53
61
  - Control session lifecycle (pause, restart, fork, refresh)
54
- - Organize sessions (archive, unarchive, bulk_archive, toggle_favorite, update_notes)
55
- - Reconfigure session MCP server access`;
62
+ - Organize sessions (archive, unarchive, bulk_archive, toggle_favorite, update_notes, update_title)
63
+ - Reconfigure session MCP server access
64
+ - Change the model used by a session`;
56
65
  export function actionSessionTool(_server, clientFactory) {
57
66
  return {
58
67
  name: 'action_session',
@@ -78,6 +87,10 @@ export function actionSessionTool(_server, clientFactory) {
78
87
  items: { type: 'string' },
79
88
  description: PARAM_DESCRIPTIONS.mcp_servers,
80
89
  },
90
+ model: {
91
+ type: 'string',
92
+ description: PARAM_DESCRIPTIONS.model,
93
+ },
81
94
  message_index: {
82
95
  type: 'number',
83
96
  description: PARAM_DESCRIPTIONS.message_index,
@@ -91,6 +104,10 @@ export function actionSessionTool(_server, clientFactory) {
91
104
  items: { type: 'number' },
92
105
  description: PARAM_DESCRIPTIONS.session_ids,
93
106
  },
107
+ title: {
108
+ type: 'string',
109
+ description: PARAM_DESCRIPTIONS.title,
110
+ },
94
111
  },
95
112
  required: ['action'],
96
113
  },
@@ -98,7 +115,7 @@ export function actionSessionTool(_server, clientFactory) {
98
115
  try {
99
116
  const validatedArgs = ActionSessionSchema.parse(args);
100
117
  const client = clientFactory();
101
- const { session_id, action, prompt, mcp_servers, message_index, session_notes, session_ids, } = validatedArgs;
118
+ const { session_id, action, prompt, mcp_servers, model, message_index, session_notes, session_ids, title, } = validatedArgs;
102
119
  // Actions that require session_id
103
120
  const requiresSessionId = [
104
121
  'follow_up',
@@ -107,9 +124,11 @@ export function actionSessionTool(_server, clientFactory) {
107
124
  'archive',
108
125
  'unarchive',
109
126
  'change_mcp_servers',
127
+ 'change_model',
110
128
  'fork',
111
129
  'refresh',
112
130
  'update_notes',
131
+ 'update_title',
113
132
  'toggle_favorite',
114
133
  ];
115
134
  if (requiresSessionId.includes(action) && !session_id) {
@@ -147,6 +166,18 @@ export function actionSessionTool(_server, clientFactory) {
147
166
  isError: true,
148
167
  };
149
168
  }
169
+ // Validate that model is provided for change_model action
170
+ if (action === 'change_model' && !model) {
171
+ return {
172
+ content: [
173
+ {
174
+ type: 'text',
175
+ text: 'Error: The "model" parameter is required for the "change_model" action.',
176
+ },
177
+ ],
178
+ isError: true,
179
+ };
180
+ }
150
181
  // Block change_mcp_servers when ALLOWED_AGENT_ROOTS is active
151
182
  if (action === 'change_mcp_servers' && parseAllowedAgentRoots() !== null) {
152
183
  return {
@@ -183,6 +214,18 @@ export function actionSessionTool(_server, clientFactory) {
183
214
  isError: true,
184
215
  };
185
216
  }
217
+ // Validate update_title requires title
218
+ if (action === 'update_title' && !title) {
219
+ return {
220
+ content: [
221
+ {
222
+ type: 'text',
223
+ text: 'Error: The "title" parameter is required for the "update_title" action.',
224
+ },
225
+ ],
226
+ isError: true,
227
+ };
228
+ }
186
229
  // Validate bulk_archive requires session_ids
187
230
  if (action === 'bulk_archive' && (!session_ids || session_ids.length === 0)) {
188
231
  return {
@@ -285,6 +328,19 @@ export function actionSessionTool(_server, clientFactory) {
285
328
  result = lines.join('\n');
286
329
  break;
287
330
  }
331
+ case 'change_model': {
332
+ const session = await client.changeModel(session_id, model);
333
+ const sessionModel = session.config?.model;
334
+ const lines = [
335
+ `## Model Updated`,
336
+ '',
337
+ `- **Session ID:** ${session.id}`,
338
+ `- **Title:** ${session.title}`,
339
+ `- **Model:** ${sessionModel || '(default)'}`,
340
+ ];
341
+ result = lines.join('\n');
342
+ break;
343
+ }
288
344
  case 'fork': {
289
345
  const response = await client.forkSession(session_id, message_index);
290
346
  const lines = [
@@ -336,6 +392,17 @@ export function actionSessionTool(_server, clientFactory) {
336
392
  result = lines.join('\n');
337
393
  break;
338
394
  }
395
+ case 'update_title': {
396
+ const session = await client.updateSession(session_id, { title: title });
397
+ const lines = [
398
+ `## Session Title Updated`,
399
+ '',
400
+ `- **Session ID:** ${session.id}`,
401
+ `- **Title:** ${session.title}`,
402
+ ];
403
+ result = lines.join('\n');
404
+ break;
405
+ }
339
406
  case 'toggle_favorite': {
340
407
  const session = await client.toggleFavorite(session_id);
341
408
  const lines = [
@@ -16,11 +16,11 @@ export declare const ActionTriggerSchema: z.ZodObject<{
16
16
  }, "strip", z.ZodTypeAny, {
17
17
  action: "create" | "update" | "delete" | "toggle";
18
18
  status?: "enabled" | "disabled" | undefined;
19
+ stop_condition?: string | undefined;
19
20
  mcp_servers?: string[] | undefined;
20
21
  trigger_type?: "slack" | "schedule" | undefined;
21
22
  name?: string | undefined;
22
23
  id?: number | undefined;
23
- stop_condition?: string | undefined;
24
24
  agent_root_name?: string | undefined;
25
25
  prompt_template?: string | undefined;
26
26
  reuse_session?: boolean | undefined;
@@ -28,11 +28,11 @@ export declare const ActionTriggerSchema: z.ZodObject<{
28
28
  }, {
29
29
  action: "create" | "update" | "delete" | "toggle";
30
30
  status?: "enabled" | "disabled" | undefined;
31
+ stop_condition?: string | undefined;
31
32
  mcp_servers?: string[] | undefined;
32
33
  trigger_type?: "slack" | "schedule" | undefined;
33
34
  name?: string | undefined;
34
35
  id?: number | undefined;
35
- stop_condition?: string | undefined;
36
36
  agent_root_name?: string | undefined;
37
37
  prompt_template?: string | undefined;
38
38
  reuse_session?: boolean | undefined;
@@ -160,6 +160,9 @@ function formatAgentRoot(lines, root) {
160
160
  if (root.default_skills && root.default_skills.length > 0) {
161
161
  lines.push(`- **Default Skills:** ${root.default_skills.map((s) => `\`${s}\``).join(', ')}`);
162
162
  }
163
+ if (root.default_model) {
164
+ lines.push(`- **Default Model:** \`${root.default_model}\``);
165
+ }
163
166
  lines.push('');
164
167
  }
165
168
  function formatStopCondition(lines, condition) {
@@ -74,6 +74,12 @@ function formatSessionDetails(session, includeTranscript) {
74
74
  if (session.mcp_servers && session.mcp_servers.length > 0) {
75
75
  lines.push(`- **MCP Servers:** ${session.mcp_servers.join(', ')}`);
76
76
  }
77
+ if (session.catalog_skills && session.catalog_skills.length > 0) {
78
+ lines.push(`- **Skills:** ${session.catalog_skills.join(', ')}`);
79
+ }
80
+ if (session.catalog_plugins && session.catalog_plugins.length > 0) {
81
+ lines.push(`- **Plugins:** ${session.catalog_plugins.join(', ')}`);
82
+ }
77
83
  if (session.prompt) {
78
84
  lines.push('');
79
85
  lines.push('### Current Prompt');
@@ -15,8 +15,8 @@ export declare const ManageEnqueuedMessagesSchema: z.ZodObject<{
15
15
  action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt";
16
16
  per_page?: number | undefined;
17
17
  page?: number | undefined;
18
- content?: string | undefined;
19
18
  stop_condition?: string | undefined;
19
+ content?: string | undefined;
20
20
  message_id?: number | undefined;
21
21
  position?: number | undefined;
22
22
  }, {
@@ -24,8 +24,8 @@ export declare const ManageEnqueuedMessagesSchema: z.ZodObject<{
24
24
  action: "list" | "get" | "create" | "update" | "delete" | "reorder" | "interrupt";
25
25
  per_page?: number | undefined;
26
26
  page?: number | undefined;
27
- content?: string | undefined;
28
27
  stop_condition?: string | undefined;
28
+ content?: string | undefined;
29
29
  message_id?: number | undefined;
30
30
  position?: number | undefined;
31
31
  }>;
@@ -4,43 +4,40 @@ import type { IAgentOrchestratorClient } from '../orchestrator-client/orchestrat
4
4
  export declare const StartSessionSchema: z.ZodObject<{
5
5
  agent_type: z.ZodOptional<z.ZodString>;
6
6
  prompt: z.ZodOptional<z.ZodString>;
7
- git_root: z.ZodOptional<z.ZodString>;
8
- branch: z.ZodOptional<z.ZodString>;
9
- subdirectory: z.ZodOptional<z.ZodString>;
7
+ agent_root: z.ZodOptional<z.ZodString>;
10
8
  title: z.ZodOptional<z.ZodString>;
11
9
  slug: z.ZodOptional<z.ZodString>;
12
10
  stop_condition: z.ZodOptional<z.ZodString>;
13
11
  execution_provider: z.ZodOptional<z.ZodEnum<["local_filesystem", "remote_sandbox"]>>;
14
12
  mcp_servers: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
15
13
  skills: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
14
+ plugins: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
16
15
  config: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
17
16
  custom_metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
18
17
  }, "strip", z.ZodTypeAny, {
19
18
  agent_type?: string | undefined;
20
- mcp_servers?: string[] | undefined;
21
- title?: string | undefined;
19
+ skills?: string[] | undefined;
20
+ plugins?: string[] | undefined;
22
21
  prompt?: string | undefined;
23
- git_root?: string | undefined;
24
- branch?: string | undefined;
25
- subdirectory?: string | undefined;
22
+ title?: string | undefined;
26
23
  slug?: string | undefined;
27
24
  stop_condition?: string | undefined;
28
25
  execution_provider?: "local_filesystem" | "remote_sandbox" | undefined;
29
- skills?: string[] | undefined;
26
+ mcp_servers?: string[] | undefined;
27
+ agent_root?: string | undefined;
30
28
  config?: Record<string, unknown> | undefined;
31
29
  custom_metadata?: Record<string, unknown> | undefined;
32
30
  }, {
33
31
  agent_type?: string | undefined;
34
- mcp_servers?: string[] | undefined;
35
- title?: string | undefined;
32
+ skills?: string[] | undefined;
33
+ plugins?: string[] | undefined;
36
34
  prompt?: string | undefined;
37
- git_root?: string | undefined;
38
- branch?: string | undefined;
39
- subdirectory?: string | undefined;
35
+ title?: string | undefined;
40
36
  slug?: string | undefined;
41
37
  stop_condition?: string | undefined;
42
38
  execution_provider?: "local_filesystem" | "remote_sandbox" | undefined;
43
- skills?: string[] | undefined;
39
+ mcp_servers?: string[] | undefined;
40
+ agent_root?: string | undefined;
44
41
  config?: Record<string, unknown> | undefined;
45
42
  custom_metadata?: Record<string, unknown> | undefined;
46
43
  }>;
@@ -58,17 +55,9 @@ export declare function startSessionTool(_server: Server, clientFactory: () => I
58
55
  type: string;
59
56
  description: "Initial prompt for the agent. If provided, the agent job is automatically queued. Omit for a clone-only session.";
60
57
  };
61
- git_root: {
62
- type: string;
63
- description: "Repository URL or local path. Examples: \"https://github.com/example/repo.git\", \"/path/to/repo\"";
64
- };
65
- branch: {
58
+ agent_root: {
66
59
  type: string;
67
- description: "Git branch to work on. Default: \"main\"";
68
- };
69
- subdirectory: {
70
- type: string;
71
- description: string;
60
+ description: "Agent root name from get_configs. The API resolves git_root, branch, subdirectory, default_model, and other defaults from the agent root configuration. Always pass this so the session inherits the correct repository, model, and settings.";
72
61
  };
73
62
  title: {
74
63
  type: string;
@@ -99,7 +88,14 @@ export declare function startSessionTool(_server: Server, clientFactory: () => I
99
88
  items: {
100
89
  type: string;
101
90
  };
102
- description: "List of skill names to enable for this session. Example: [\"discovery-classify\", \"publish-and-pr\"]";
91
+ description: "List of skill names to enable for this session. Always include the agent root's default_skills from get_configs as the starting point — omitting skills means the session gets none. Add extras as needed; removing a default should be rare and intentional. Example: [\"discovery-classify\", \"publish-and-pr\"]";
92
+ };
93
+ plugins: {
94
+ type: string;
95
+ items: {
96
+ type: string;
97
+ };
98
+ description: "List of plugin names to enable for this session. Plugins extend agent capabilities with additional integrations. Example: [\"my-plugin\"]";
103
99
  };
104
100
  config: {
105
101
  type: string;
@@ -4,14 +4,7 @@ import { getConfigsCache, setConfigsCache } from '../cache/configs-cache.js';
4
4
  const PARAM_DESCRIPTIONS = {
5
5
  agent_type: 'Agent type for the session. Currently only "claude_code" is supported. Default: "claude_code"',
6
6
  prompt: 'Initial prompt for the agent. If provided, the agent job is automatically queued. Omit for a clone-only session.',
7
- git_root: 'Repository URL or local path. Examples: "https://github.com/example/repo.git", "/path/to/repo"',
8
- branch: 'Git branch to work on. Default: "main"',
9
- subdirectory: 'Subdirectory within the repository to use as the agent working directory. ' +
10
- 'This should match a preconfigured agent root default_subdirectory from get_configs — it defines ' +
11
- 'the root scope for the agent session. Do NOT use this to point at internal package directories ' +
12
- '(e.g. "experimental/gcs" in a monorepo) as this blinds the agent to root-level configuration ' +
13
- 'like CLAUDE.md, build scripts, CI workflows, and monorepo tooling. If no agent root defines ' +
14
- 'a default_subdirectory, leave this unset.',
7
+ agent_root: 'Agent root name from get_configs. The API resolves git_root, branch, subdirectory, default_model, and other defaults from the agent root configuration. Always pass this so the session inherits the correct repository, model, and settings.',
15
8
  title: 'STRONGLY RECOMMENDED: Always set a title — treat it as effectively required. ' +
16
9
  'The title appears in the AO web UI and push notifications, making sessions identifiable at a glance. ' +
17
10
  'Compose a short, descriptive title (under 70 characters) that captures what the session is doing ' +
@@ -21,16 +14,15 @@ const PARAM_DESCRIPTIONS = {
21
14
  stop_condition: 'Stop condition ID from get_configs (e.g. "pr_merged"). The description is automatically resolved and passed to the agent as context.',
22
15
  execution_provider: 'Execution environment. Options: "local_filesystem" (runs locally), "remote_sandbox" (runs in isolated sandbox). Default: "local_filesystem"',
23
16
  mcp_servers: 'List of MCP server names to enable for this session. Example: ["github-development", "slack"]',
24
- skills: 'List of skill names to enable for this session. Example: ["discovery-classify", "publish-and-pr"]',
17
+ skills: 'List of skill names to enable for this session. Always include the agent root\'s default_skills from get_configs as the starting point — omitting skills means the session gets none. Add extras as needed; removing a default should be rare and intentional. Example: ["discovery-classify", "publish-and-pr"]',
18
+ plugins: 'List of plugin names to enable for this session. Plugins extend agent capabilities with additional integrations. Example: ["my-plugin"]',
25
19
  config: 'Additional configuration as a JSON object.',
26
20
  custom_metadata: 'User-defined metadata as a JSON object. Useful for tracking tickets, projects, etc.',
27
21
  };
28
22
  export const StartSessionSchema = z.object({
29
23
  agent_type: z.string().optional().describe(PARAM_DESCRIPTIONS.agent_type),
30
24
  prompt: z.string().optional().describe(PARAM_DESCRIPTIONS.prompt),
31
- git_root: z.string().optional().describe(PARAM_DESCRIPTIONS.git_root),
32
- branch: z.string().optional().describe(PARAM_DESCRIPTIONS.branch),
33
- subdirectory: z.string().optional().describe(PARAM_DESCRIPTIONS.subdirectory),
25
+ agent_root: z.string().optional().describe(PARAM_DESCRIPTIONS.agent_root),
34
26
  title: z.string().optional().describe(PARAM_DESCRIPTIONS.title),
35
27
  slug: z.string().optional().describe(PARAM_DESCRIPTIONS.slug),
36
28
  stop_condition: z.string().optional().describe(PARAM_DESCRIPTIONS.stop_condition),
@@ -40,12 +32,13 @@ export const StartSessionSchema = z.object({
40
32
  .describe(PARAM_DESCRIPTIONS.execution_provider),
41
33
  mcp_servers: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.mcp_servers),
42
34
  skills: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.skills),
35
+ plugins: z.array(z.string()).optional().describe(PARAM_DESCRIPTIONS.plugins),
43
36
  config: z.record(z.unknown()).optional().describe(PARAM_DESCRIPTIONS.config),
44
37
  custom_metadata: z.record(z.unknown()).optional().describe(PARAM_DESCRIPTIONS.custom_metadata),
45
38
  });
46
39
  const TOOL_DESCRIPTION = `Start a new agent session in the Agent Orchestrator.
47
40
 
48
- **IMPORTANT:** Before starting a session, call get_configs to discover available MCP servers, stop conditions, and preconfigured agent roots.
41
+ **IMPORTANT:** Before starting a session, call get_configs to discover available agent roots, MCP servers, stop conditions, and their defaults.
49
42
 
50
43
  **Returns:** The created session with its ID, status, and configuration.
51
44
 
@@ -53,7 +46,12 @@ const TOOL_DESCRIPTION = `Start a new agent session in the Agent Orchestrator.
53
46
  - If a prompt is provided, the agent job is automatically queued to start
54
47
  - If no prompt is provided, creates a clone-only session that can be started later with action_session
55
48
 
56
- **Defaults from Agent Roots:** When starting a session that matches a preconfigured agent root (from \`get_configs\`), the agent root defines \`default_mcp_servers\` and \`default_skills\`. In most cases, you should pass these defaults through — omitting them means the session won't have MCP servers or skills configured. You can reduce the set (e.g., drop an MCP server you don't need), but when \`ALLOWED_AGENT_ROOTS\` is active, you cannot add servers beyond the defaults. Skills are not constrained — you can augment with additional skills as needed. As a rule of thumb: always pass the defaults unless you have a specific reason to remove something.
49
+ **Agent Roots:** Use \`agent_root\` to specify which preconfigured agent root to use. The API resolves git_root, branch, subdirectory, default_model, and other defaults from the agent root configuration.
50
+
51
+ **Defaults from Agent Roots:** The agent root defines \`default_mcp_servers\`, \`default_skills\`, and optionally a \`default_stop_condition\`. Omitting \`mcp_servers\` or \`skills\` means the session gets NONE — there is no automatic fallback to defaults.
52
+
53
+ - **MCP servers:** Start with \`default_mcp_servers\`. Drop servers the task doesn't need (least-privilege). Add extras when the task requires tools beyond the defaults. When \`ALLOWED_AGENT_ROOTS\` is active, you cannot add servers beyond the defaults.
54
+ - **Skills:** Start with \`default_skills\`. You can freely add skills beyond the defaults. Removing a default skill should be rare and intentional — only when you have a specific reason, like replacing a skill with a more capable variant that covers the same ground. Skills are lightweight text files with no blast radius, so keeping all defaults costs nothing.
57
55
 
58
56
  **Use cases:**
59
57
  - Start a new agent task on a repository
@@ -75,17 +73,9 @@ export function startSessionTool(_server, clientFactory) {
75
73
  type: 'string',
76
74
  description: PARAM_DESCRIPTIONS.prompt,
77
75
  },
78
- git_root: {
76
+ agent_root: {
79
77
  type: 'string',
80
- description: PARAM_DESCRIPTIONS.git_root,
81
- },
82
- branch: {
83
- type: 'string',
84
- description: PARAM_DESCRIPTIONS.branch,
85
- },
86
- subdirectory: {
87
- type: 'string',
88
- description: PARAM_DESCRIPTIONS.subdirectory,
78
+ description: PARAM_DESCRIPTIONS.agent_root,
89
79
  },
90
80
  title: {
91
81
  type: 'string',
@@ -114,6 +104,11 @@ export function startSessionTool(_server, clientFactory) {
114
104
  items: { type: 'string' },
115
105
  description: PARAM_DESCRIPTIONS.skills,
116
106
  },
107
+ plugins: {
108
+ type: 'array',
109
+ items: { type: 'string' },
110
+ description: PARAM_DESCRIPTIONS.plugins,
111
+ },
117
112
  config: {
118
113
  type: 'object',
119
114
  description: PARAM_DESCRIPTIONS.config,
@@ -138,7 +133,7 @@ export function startSessionTool(_server, clientFactory) {
138
133
  configs = await client.getConfigs();
139
134
  setConfigsCache(configs);
140
135
  }
141
- const validation = validateAgentRootConstraints(allowedRoots, configs.agent_roots, validatedArgs.git_root, validatedArgs.mcp_servers, validatedArgs.branch, validatedArgs.subdirectory);
136
+ const validation = validateAgentRootConstraints(allowedRoots, configs.agent_roots, validatedArgs.agent_root, validatedArgs.mcp_servers);
142
137
  if (!validation.valid) {
143
138
  return {
144
139
  content: [{ type: 'text', text: `Error starting session: ${validation.error}` }],
package/shared/types.d.ts CHANGED
@@ -23,6 +23,8 @@ export interface Session {
23
23
  execution_provider: string;
24
24
  stop_condition: string | null;
25
25
  mcp_servers: string[];
26
+ catalog_skills?: string[];
27
+ catalog_plugins?: string[];
26
28
  config: Record<string, unknown>;
27
29
  metadata: Record<string, unknown>;
28
30
  custom_metadata: Record<string, unknown>;
@@ -115,6 +117,7 @@ export interface AgentRootInfo {
115
117
  default_stop_condition?: string;
116
118
  default_mcp_servers?: string[];
117
119
  default_skills?: string[];
120
+ default_model?: string;
118
121
  }
119
122
  export interface StopConditionInfo {
120
123
  id: string;
@@ -143,6 +146,8 @@ export interface CreateSessionRequest {
143
146
  execution_provider?: string;
144
147
  mcp_servers?: string[];
145
148
  skills?: string[];
149
+ plugins?: string[];
150
+ agent_root?: string;
146
151
  config?: Record<string, unknown>;
147
152
  custom_metadata?: Record<string, unknown>;
148
153
  }