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.
- package/dist/agentBuilderTools.js +48 -3
- package/dist/agentBuilderTools.test.js +100 -0
- package/dist/client.js +3 -2
- package/package.json +1 -1
|
@@ -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
|
|
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
|
-
|
|
752
|
-
|
|
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) {
|