wolfpack-mcp 1.0.88 → 1.0.90

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.
@@ -10,6 +10,26 @@ const ORG_SLUG_PROP = {
10
10
  description: 'Organisation slug. Required when you belong to multiple organisations. Use list_organisations to find slugs.',
11
11
  },
12
12
  };
13
+ /**
14
+ * The per-task model pin, shared by create_agent_task and update_agent_task so
15
+ * both describe it the same way. Null on update clears the pin and the task
16
+ * falls back to the agent's own model; omitting a field leaves it unchanged.
17
+ */
18
+ const TASK_MODEL_PROPS = {
19
+ llm_provider: {
20
+ type: 'string',
21
+ description: 'Pin this task to an LLM provider ID (e.g. "anthropic") instead of the agent\'s own model. ' +
22
+ 'Use list_llm_models for IDs. Set to null to unpin.',
23
+ },
24
+ llm_model: {
25
+ type: 'string',
26
+ description: 'Pin this task to a model ID (e.g. "claude-sonnet-4-6"). Set to null for family auto-select.',
27
+ },
28
+ llm_family: {
29
+ type: 'string',
30
+ description: 'Pin this task to a model family (e.g. "sonnet"); the task auto-upgrades to the latest in the family.',
31
+ },
32
+ };
13
33
  export const AGENT_BUILDER_TOOLS = [
14
34
  // ─── Group 0: Organisations ─────────────────────────────────────────────
15
35
  {
@@ -338,6 +358,7 @@ export const AGENT_BUILDER_TOOLS = [
338
358
  type: 'number',
339
359
  description: 'Order within scheduled batch (lower = first)',
340
360
  },
361
+ ...TASK_MODEL_PROPS,
341
362
  disabled_tools: {
342
363
  type: 'array',
343
364
  items: { type: 'string' },
@@ -363,7 +384,7 @@ export const AGENT_BUILDER_TOOLS = [
363
384
  },
364
385
  {
365
386
  name: 'update_agent_task',
366
- description: "Update a task's prompt, schedule settings, or disabled tools/MCP servers.",
387
+ description: "Update a task's prompt, schedule settings, model pin, or disabled tools/MCP servers.",
367
388
  inputSchema: {
368
389
  type: 'object',
369
390
  properties: {
@@ -374,6 +395,7 @@ export const AGENT_BUILDER_TOOLS = [
374
395
  scheduled_enabled: { type: 'boolean', description: 'Include in scheduled runs' },
375
396
  sort_order: { type: 'number', description: 'Order within scheduled batch' },
376
397
  is_active: { type: 'boolean', description: 'Whether this task is active' },
398
+ ...TASK_MODEL_PROPS,
377
399
  disabled_tools: {
378
400
  type: 'array',
379
401
  items: { type: 'string' },
@@ -406,6 +428,12 @@ export const AGENT_BUILDER_TOOLS = [
406
428
  properties: {
407
429
  agent_id: { type: 'string', description: 'Agent profile ID' },
408
430
  task_id: { type: 'string', description: 'Task ID to run' },
431
+ prompt: {
432
+ type: 'string',
433
+ description: "Extra instructions for this run only, appended to the task's saved prompt. " +
434
+ 'The task still runs as written — this is added after it, and the task is not changed. ' +
435
+ 'Omit it to run the task exactly as saved.',
436
+ },
409
437
  project_slug: { type: 'string', description: 'Project to scope the session to (optional)' },
410
438
  git_ref: {
411
439
  type: 'string',
@@ -747,7 +775,8 @@ export const AGENT_BUILDER_TOOLS = [
747
775
  {
748
776
  name: 'list_llm_models',
749
777
  description: 'List LLM providers and models available to the organisation. ' +
750
- 'Use provider and model IDs when calling create_agent or update_agent.',
778
+ 'Use provider, model and family IDs when calling create_agent, update_agent, ' +
779
+ 'create_agent_task or update_agent_task.',
751
780
  inputSchema: { type: 'object', properties: { ...ORG_SLUG_PROP } },
752
781
  },
753
782
  ];
@@ -1014,6 +1043,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1014
1043
  prompt: z.string(),
1015
1044
  scheduled_enabled: z.boolean().optional(),
1016
1045
  sort_order: z.number().optional(),
1046
+ llm_provider: z.string().nullable().optional(),
1047
+ llm_model: z.string().nullable().optional(),
1048
+ llm_family: z.string().nullable().optional(),
1017
1049
  disabled_tools: z.array(z.string()).optional(),
1018
1050
  disabled_mcp_servers: z.array(z.string()).optional(),
1019
1051
  clone_repository: z.boolean().optional(),
@@ -1025,6 +1057,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1025
1057
  prompt: parsed.prompt,
1026
1058
  scheduledEnabled: parsed.scheduled_enabled,
1027
1059
  sortOrder: parsed.sort_order,
1060
+ llmProvider: parsed.llm_provider,
1061
+ llmModel: parsed.llm_model,
1062
+ llmFamily: parsed.llm_family,
1028
1063
  disabledTools: parsed.disabled_tools,
1029
1064
  disabledMcpServers: parsed.disabled_mcp_servers,
1030
1065
  cloneRepository: parsed.clone_repository,
@@ -1041,6 +1076,9 @@ export async function handleAgentBuilderTool(name, args, client) {
1041
1076
  scheduled_enabled: z.boolean().optional(),
1042
1077
  sort_order: z.number().optional(),
1043
1078
  is_active: z.boolean().optional(),
1079
+ llm_provider: z.string().nullable().optional(),
1080
+ llm_model: z.string().nullable().optional(),
1081
+ llm_family: z.string().nullable().optional(),
1044
1082
  disabled_tools: z.array(z.string()).nullable().optional(),
1045
1083
  disabled_mcp_servers: z.array(z.string()).nullable().optional(),
1046
1084
  clone_repository: z.boolean().optional(),
@@ -1058,6 +1096,12 @@ export async function handleAgentBuilderTool(name, args, client) {
1058
1096
  fields.sortOrder = parsed.sort_order;
1059
1097
  if (parsed.is_active !== undefined)
1060
1098
  fields.isActive = parsed.is_active;
1099
+ if (parsed.llm_provider !== undefined)
1100
+ fields.llmProvider = parsed.llm_provider;
1101
+ if (parsed.llm_model !== undefined)
1102
+ fields.llmModel = parsed.llm_model;
1103
+ if (parsed.llm_family !== undefined)
1104
+ fields.llmFamily = parsed.llm_family;
1061
1105
  if (parsed.disabled_tools !== undefined)
1062
1106
  fields.disabledTools = parsed.disabled_tools;
1063
1107
  if (parsed.disabled_mcp_servers !== undefined)
@@ -1072,13 +1116,14 @@ export async function handleAgentBuilderTool(name, args, client) {
1072
1116
  .object({
1073
1117
  agent_id: z.string(),
1074
1118
  task_id: z.string(),
1119
+ prompt: z.string().optional(),
1075
1120
  project_slug: z.string().optional(),
1076
1121
  git_ref: z.string().optional(),
1077
1122
  pr_number: z.number().int().positive().optional(),
1078
1123
  org_slug: orgSlugField,
1079
1124
  })
1080
1125
  .parse(args);
1081
- const result = await client.runAgentTask(parsed.agent_id, parsed.task_id, parsed.project_slug, resolveOrg(parsed), parsed.git_ref, parsed.pr_number);
1126
+ const result = await client.runAgentTask(parsed.agent_id, parsed.task_id, parsed.project_slug, resolveOrg(parsed), parsed.git_ref, parsed.pr_number, parsed.prompt);
1082
1127
  return {
1083
1128
  content: [
1084
1129
  {
@@ -0,0 +1,100 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { AGENT_BUILDER_TOOLS, handleAgentBuilderTool } from './agentBuilderTools.js';
3
+ const toolNamed = (name) => {
4
+ const tool = AGENT_BUILDER_TOOLS.find((t) => t.name === name);
5
+ if (!tool)
6
+ throw new Error(`No tool named ${name}`);
7
+ return tool;
8
+ };
9
+ const MODEL_FIELDS = ['llm_provider', 'llm_model', 'llm_family'];
10
+ /** A client that records the one call each test makes and returns a bare task. */
11
+ function fakeClient() {
12
+ const createAgentTask = vi.fn().mockResolvedValue({ id: 'task-1', name: 'Analyse' });
13
+ const updateAgentTask = vi.fn().mockResolvedValue({ id: 'task-1', name: 'Analyse' });
14
+ return {
15
+ client: { createAgentTask, updateAgentTask },
16
+ createAgentTask,
17
+ updateAgentTask,
18
+ };
19
+ }
20
+ describe('per-task model pin over MCP (#2034)', () => {
21
+ it.each(['create_agent_task', 'update_agent_task'])('%s advertises the model fields', (name) => {
22
+ const properties = toolNamed(name).inputSchema.properties;
23
+ expect(Object.keys(properties)).toEqual(expect.arrayContaining(MODEL_FIELDS));
24
+ });
25
+ it('sends the model pin when creating a task', async () => {
26
+ const { client, createAgentTask } = fakeClient();
27
+ await handleAgentBuilderTool('create_agent_task', {
28
+ agent_id: 'agent-1',
29
+ name: 'Analyse',
30
+ prompt: 'Analyse the transcript.',
31
+ llm_provider: 'anthropic',
32
+ llm_model: 'claude-sonnet-4-6',
33
+ llm_family: 'sonnet',
34
+ }, client);
35
+ expect(createAgentTask.mock.calls[0][0]).toBe('agent-1');
36
+ expect(createAgentTask.mock.calls[0][1]).toMatchObject({
37
+ llmProvider: 'anthropic',
38
+ llmModel: 'claude-sonnet-4-6',
39
+ llmFamily: 'sonnet',
40
+ });
41
+ });
42
+ it('sends the model pin when updating a task', async () => {
43
+ const { client, updateAgentTask } = fakeClient();
44
+ await handleAgentBuilderTool('update_agent_task', {
45
+ agent_id: 'agent-1',
46
+ task_id: 'task-1',
47
+ llm_provider: 'anthropic',
48
+ llm_model: 'claude-opus-4-1',
49
+ }, client);
50
+ expect(updateAgentTask.mock.calls[0].slice(0, 3)).toEqual([
51
+ 'agent-1',
52
+ 'task-1',
53
+ { llmProvider: 'anthropic', llmModel: 'claude-opus-4-1' },
54
+ ]);
55
+ });
56
+ it('unpins a task when the model fields are sent as null', async () => {
57
+ const { client, updateAgentTask } = fakeClient();
58
+ await handleAgentBuilderTool('update_agent_task', {
59
+ agent_id: 'agent-1',
60
+ task_id: 'task-1',
61
+ llm_provider: null,
62
+ llm_model: null,
63
+ llm_family: null,
64
+ }, client);
65
+ expect(updateAgentTask.mock.calls[0][2]).toEqual({
66
+ llmProvider: null,
67
+ llmModel: null,
68
+ llmFamily: null,
69
+ });
70
+ });
71
+ it('leaves an existing pin alone when updating something else', async () => {
72
+ const { client, updateAgentTask } = fakeClient();
73
+ await handleAgentBuilderTool('update_agent_task', { agent_id: 'agent-1', task_id: 'task-1', prompt: 'Analyse the transcript twice.' }, client);
74
+ expect(updateAgentTask.mock.calls[0][2]).toEqual({ prompt: 'Analyse the transcript twice.' });
75
+ });
76
+ });
77
+ // #2048 — run_agent_task can add instructions for one run without editing the
78
+ // task. The prompt is the 7th argument of client.runAgentTask, so these assert
79
+ // on the position as well as the value.
80
+ describe('extra instructions on a task run over MCP (#2048)', () => {
81
+ const runClient = () => {
82
+ const runAgentTask = vi.fn().mockResolvedValue({ entryId: 'e1', position: 0, executing: true });
83
+ return { client: { runAgentTask }, runAgentTask };
84
+ };
85
+ it('advertises the prompt argument as an addition, not a replacement', () => {
86
+ const properties = toolNamed('run_agent_task').inputSchema.properties;
87
+ expect(properties.prompt.description).toContain("appended to the task's saved prompt");
88
+ });
89
+ it('sends the extra instructions with the run', async () => {
90
+ const { client, runAgentTask } = runClient();
91
+ await handleAgentBuilderTool('run_agent_task', { agent_id: 'agent-1', task_id: 'task-1', prompt: 'Only cover last week.' }, client);
92
+ expect(runAgentTask.mock.calls[0][1]).toBe('task-1');
93
+ expect(runAgentTask.mock.calls[0][6]).toBe('Only cover last week.');
94
+ });
95
+ it('sends none when the caller only wants the task as saved', async () => {
96
+ const { client, runAgentTask } = runClient();
97
+ await handleAgentBuilderTool('run_agent_task', { agent_id: 'agent-1', task_id: 'task-1' }, client);
98
+ expect(runAgentTask.mock.calls[0][6]).toBeUndefined();
99
+ });
100
+ });
package/dist/client.js CHANGED
@@ -748,8 +748,9 @@ export class WolfpackClient {
748
748
  async updateAgentTask(agentId, taskId, body, orgSlug) {
749
749
  return this.api.patch(this.withOrgSlug(`/agents/${agentId}/tasks/${taskId}`, orgSlug), body);
750
750
  }
751
- async runAgentTask(agentId, taskId, teamSlug, orgSlug, gitRef, prNumber) {
752
- return this.api.post(this.withOrgSlug(`/agents/${agentId}/tasks/${taskId}/run`, orgSlug), { teamSlug, gitRef, prNumber });
751
+ /** `prompt` is appended to the task's saved prompt for this run only (#2048). */
752
+ async runAgentTask(agentId, taskId, teamSlug, orgSlug, gitRef, prNumber, prompt) {
753
+ return this.api.post(this.withOrgSlug(`/agents/${agentId}/tasks/${taskId}/run`, orgSlug), { teamSlug, gitRef, prNumber, prompt });
753
754
  }
754
755
  // ─── Agent Builder: Queue ──────────────────────────────────────────────────
755
756
  async listAgentQueue(agentId, status, orgSlug) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wolfpack-mcp",
3
- "version": "1.0.88",
3
+ "version": "1.0.90",
4
4
  "description": "MCP server for Wolfpack AI-enhanced software delivery tools",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",