@desplega.ai/agent-swarm 1.92.2 → 1.94.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 +2 -2
- package/openapi.json +242 -3
- package/package.json +5 -5
- package/src/be/db.ts +152 -11
- package/src/be/memory/boot-reembed.ts +0 -1
- package/src/be/memory/providers/sqlite-store.ts +42 -25
- package/src/be/memory/raters/llm-client.ts +12 -5
- package/src/be/memory/types.ts +3 -0
- package/src/be/migrations/088_script_runs_list_indexes.sql +10 -0
- package/src/be/migrations/089_harness_variant.sql +2 -0
- package/src/be/migrations/090_model_tiers.sql +2 -0
- package/src/be/migrations/091_seed_swarm_operations_metrics.sql +12 -0
- package/src/be/migrations/092_metrics_dashboard_combobox_filters.sql +68 -0
- package/src/be/migrations/093_slack_message_tracking.sql +6 -0
- package/src/be/migrations/runner.ts +52 -0
- package/src/be/modelsdev-cache.json +3264 -1166
- package/src/be/scripts/boot-reembed.ts +74 -0
- package/src/be/scripts/db.ts +19 -3
- package/src/be/seed/index.ts +1 -1
- package/src/be/seed/registry.ts +2 -2
- package/src/be/seed/runner.ts +5 -5
- package/src/be/seed/types.ts +6 -1
- package/src/be/seed-pricing.ts +2 -0
- package/src/be/seed-scripts/catalog/boot-triage.inline.ts +221 -0
- package/src/be/seed-scripts/catalog/catalog-report.inline.ts +457 -0
- package/src/be/seed-scripts/catalog/compound-insights.inline.ts +863 -0
- package/src/be/seed-scripts/catalog/ops-catalog-audit.inline.ts +506 -0
- package/src/be/seed-scripts/index.ts +8 -7
- package/src/be/skill-sync.ts +28 -179
- package/src/commands/runner.ts +197 -10
- package/src/http/api-keys.ts +42 -0
- package/src/http/index.ts +13 -2
- package/src/http/mcp-bridge.ts +1 -1
- package/src/http/memory.ts +23 -24
- package/src/http/metrics.ts +55 -6
- package/src/http/schedules.ts +16 -15
- package/src/http/script-runs.ts +7 -1
- package/src/http/scripts.ts +147 -1
- package/src/http/tasks.ts +17 -6
- package/src/model-tiers.ts +140 -0
- package/src/providers/claude-adapter.ts +33 -1
- package/src/providers/claude-managed-adapter.ts +3 -0
- package/src/providers/claude-managed-models.ts +16 -0
- package/src/providers/codex-adapter.ts +8 -1
- package/src/providers/codex-models.ts +1 -0
- package/src/providers/codex-oauth/auth-json.ts +1 -0
- package/src/providers/harness-version.ts +7 -0
- package/src/providers/opencode-adapter.ts +12 -4
- package/src/providers/pi-mono-adapter.ts +90 -8
- package/src/providers/types.ts +2 -0
- package/src/scheduler/scheduler.ts +22 -34
- package/src/scripts-runtime/egress-secrets.ts +83 -0
- package/src/scripts-runtime/eval-harness.ts +4 -0
- package/src/scripts-runtime/executors/types.ts +7 -0
- package/src/scripts-runtime/loader.ts +2 -0
- package/src/server-user.ts +8 -2
- package/src/slack/channel-join.ts +41 -0
- package/src/slack/responses.ts +39 -11
- package/src/slack/watcher.ts +121 -8
- package/src/tests/additive-buffer.test.ts +0 -1
- package/src/tests/agents-list-model-display.test.ts +13 -0
- package/src/tests/api-key-tracking.test.ts +113 -0
- package/src/tests/approval-requests.test.ts +0 -6
- package/src/tests/aws-error-classifier.test.ts +148 -0
- package/src/tests/claude-managed-adapter.test.ts +12 -0
- package/src/tests/claude-managed-setup.test.ts +0 -4
- package/src/tests/codex-pool.test.ts +2 -6
- package/src/tests/context-window.test.ts +7 -0
- package/src/tests/http-api-integration.test.ts +23 -6
- package/src/tests/memory-edges.test.ts +0 -2
- package/src/tests/memory-rate-endpoint.test.ts +0 -2
- package/src/tests/memory-rater-e2e.test.ts +0 -2
- package/src/tests/memory-store.test.ts +19 -1
- package/src/tests/memory.test.ts +51 -0
- package/src/tests/metrics-http.test.ts +137 -3
- package/src/tests/migration-046-budgets.test.ts +33 -0
- package/src/tests/migration-runner-regressions.test.ts +69 -0
- package/src/tests/model-control.test.ts +162 -46
- package/src/tests/opencode-adapter.test.ts +9 -0
- package/src/tests/pi-mono-adapter.test.ts +319 -0
- package/src/tests/providers/pi-cost.test.ts +9 -0
- package/src/tests/reload-config.test.ts +33 -17
- package/src/tests/runner-fallback-output.test.ts +50 -0
- package/src/tests/runner-skills-refresh.test.ts +216 -46
- package/src/tests/script-runs-http.test.ts +7 -1
- package/src/tests/scripts-boot-reembed.test.ts +163 -0
- package/src/tests/scripts-embeddings.test.ts +90 -0
- package/src/tests/scripts-runtime-secret-egress.test.ts +129 -0
- package/src/tests/seed-scripts.test.ts +13 -1
- package/src/tests/seed.test.ts +26 -1
- package/src/tests/session-attach.test.ts +6 -6
- package/src/tests/session-costs-model-key-normalize.test.ts +2 -0
- package/src/tests/skill-fs-writer.test.ts +250 -0
- package/src/tests/slack-attachments-block.test.ts +0 -1
- package/src/tests/slack-blocks.test.ts +0 -1
- package/src/tests/slack-channel-join.test.ts +80 -0
- package/src/tests/slack-identity-resolution.test.ts +0 -1
- package/src/tests/slack-watcher.test.ts +66 -0
- package/src/tests/structured-output.test.ts +0 -2
- package/src/tests/use-dismissible-card.test.ts +0 -4
- package/src/tests/workflow-agent-task.test.ts +5 -2
- package/src/tests/workflow-validation-port-routing.test.ts +181 -0
- package/src/tools/memory-get.ts +11 -0
- package/src/tools/memory-search.ts +18 -0
- package/src/tools/schedules/create-schedule.ts +71 -70
- package/src/tools/schedules/update-schedule.ts +43 -31
- package/src/tools/send-task.ts +16 -5
- package/src/tools/slack-post.ts +18 -15
- package/src/tools/slack-read.ts +9 -11
- package/src/tools/slack-reply.ts +18 -15
- package/src/tools/slack-start-thread.ts +17 -14
- package/src/tools/task-action.ts +11 -3
- package/src/types.ts +40 -0
- package/src/utils/aws-error-classifier.ts +97 -0
- package/src/utils/context-window.ts +5 -0
- package/src/utils/credentials.test.ts +68 -0
- package/src/utils/credentials.ts +66 -5
- package/src/utils/pretty-print.ts +25 -10
- package/src/utils/skill-fs-writer.ts +220 -0
- package/src/utils/skills-refresh.ts +123 -40
- package/src/workflows/engine.ts +3 -2
- package/src/workflows/executors/agent-task.ts +3 -1
|
@@ -10,6 +10,39 @@ import {
|
|
|
10
10
|
import { mergeScheduleTiming, validateRecurringTiming } from "@/be/schedules/validate";
|
|
11
11
|
import { calculateNextRun } from "@/scheduler";
|
|
12
12
|
import { createToolRegistrar } from "@/tools/utils";
|
|
13
|
+
import { ModelTierSchema, splitLegacyModelAlias } from "../../model-tiers";
|
|
14
|
+
|
|
15
|
+
export const updateScheduleInputSchema = z.object({
|
|
16
|
+
scheduleId: z.string().uuid().optional().describe("Schedule ID to update"),
|
|
17
|
+
name: z.string().optional().describe("Schedule name to update (alternative to ID)"),
|
|
18
|
+
newName: z.string().min(1).max(100).optional().describe("New name for the schedule"),
|
|
19
|
+
taskTemplate: z.string().min(1).optional().describe("New task template"),
|
|
20
|
+
cronExpression: z.string().nullable().optional().describe("New cron expression (null to clear)"),
|
|
21
|
+
intervalMs: z
|
|
22
|
+
.number()
|
|
23
|
+
.int()
|
|
24
|
+
.positive()
|
|
25
|
+
.nullable()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("New interval in milliseconds (null to clear)"),
|
|
28
|
+
description: z.string().optional().describe("New description"),
|
|
29
|
+
taskType: z.string().max(50).optional().describe("New task type"),
|
|
30
|
+
tags: z.array(z.string()).optional().describe("New tags"),
|
|
31
|
+
priority: z.number().int().min(0).max(100).optional().describe("New priority"),
|
|
32
|
+
targetAgentId: z.string().uuid().nullable().optional().describe("New target agent ID"),
|
|
33
|
+
timezone: z.string().optional().describe("New timezone"),
|
|
34
|
+
enabled: z.boolean().optional().describe("Enable or disable the schedule"),
|
|
35
|
+
model: z
|
|
36
|
+
.string()
|
|
37
|
+
.trim()
|
|
38
|
+
.min(1)
|
|
39
|
+
.nullable()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Concrete model override for tasks created by this schedule. Set to null to clear."),
|
|
42
|
+
modelTier: ModelTierSchema.nullable()
|
|
43
|
+
.optional()
|
|
44
|
+
.describe("Portable model tier for tasks created by this schedule. Set to null to clear."),
|
|
45
|
+
});
|
|
13
46
|
|
|
14
47
|
export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
15
48
|
createToolRegistrar(server)(
|
|
@@ -19,36 +52,7 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
19
52
|
annotations: { idempotentHint: true },
|
|
20
53
|
description:
|
|
21
54
|
"Update an existing scheduled task. Only the creator or lead agent can update schedules.",
|
|
22
|
-
inputSchema:
|
|
23
|
-
scheduleId: z.string().uuid().optional().describe("Schedule ID to update"),
|
|
24
|
-
name: z.string().optional().describe("Schedule name to update (alternative to ID)"),
|
|
25
|
-
newName: z.string().min(1).max(100).optional().describe("New name for the schedule"),
|
|
26
|
-
taskTemplate: z.string().min(1).optional().describe("New task template"),
|
|
27
|
-
cronExpression: z
|
|
28
|
-
.string()
|
|
29
|
-
.nullable()
|
|
30
|
-
.optional()
|
|
31
|
-
.describe("New cron expression (null to clear)"),
|
|
32
|
-
intervalMs: z
|
|
33
|
-
.number()
|
|
34
|
-
.int()
|
|
35
|
-
.positive()
|
|
36
|
-
.nullable()
|
|
37
|
-
.optional()
|
|
38
|
-
.describe("New interval in milliseconds (null to clear)"),
|
|
39
|
-
description: z.string().optional().describe("New description"),
|
|
40
|
-
taskType: z.string().max(50).optional().describe("New task type"),
|
|
41
|
-
tags: z.array(z.string()).optional().describe("New tags"),
|
|
42
|
-
priority: z.number().int().min(0).max(100).optional().describe("New priority"),
|
|
43
|
-
targetAgentId: z.string().uuid().nullable().optional().describe("New target agent ID"),
|
|
44
|
-
timezone: z.string().optional().describe("New timezone"),
|
|
45
|
-
enabled: z.boolean().optional().describe("Enable or disable the schedule"),
|
|
46
|
-
model: z
|
|
47
|
-
.enum(["haiku", "sonnet", "opus"])
|
|
48
|
-
.nullable()
|
|
49
|
-
.optional()
|
|
50
|
-
.describe("Model to use for tasks created by this schedule. Set to null to clear."),
|
|
51
|
-
}),
|
|
55
|
+
inputSchema: updateScheduleInputSchema,
|
|
52
56
|
outputSchema: z.object({
|
|
53
57
|
yourAgentId: z.string().uuid().optional(),
|
|
54
58
|
success: z.boolean(),
|
|
@@ -71,6 +75,7 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
71
75
|
createdByAgentId: z.string().optional(),
|
|
72
76
|
timezone: z.string(),
|
|
73
77
|
model: z.string().optional(),
|
|
78
|
+
modelTier: ModelTierSchema.optional(),
|
|
74
79
|
scheduleType: z.string(),
|
|
75
80
|
createdAt: z.string(),
|
|
76
81
|
lastUpdatedAt: z.string(),
|
|
@@ -94,6 +99,7 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
94
99
|
timezone,
|
|
95
100
|
enabled,
|
|
96
101
|
model,
|
|
102
|
+
modelTier,
|
|
97
103
|
},
|
|
98
104
|
requestInfo,
|
|
99
105
|
_meta,
|
|
@@ -217,7 +223,13 @@ export const registerUpdateScheduleTool = (server: McpServer) => {
|
|
|
217
223
|
if (targetAgentId !== undefined) updateData.targetAgentId = targetAgentId;
|
|
218
224
|
if (timezone !== undefined) updateData.timezone = timezone;
|
|
219
225
|
if (enabled !== undefined) updateData.enabled = enabled;
|
|
220
|
-
if (model !== undefined
|
|
226
|
+
if (model !== undefined || modelTier !== undefined) {
|
|
227
|
+
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
228
|
+
if (model !== undefined) updateData.model = normalizedModel.model ?? null;
|
|
229
|
+
if (modelTier !== undefined || normalizedModel.modelTier) {
|
|
230
|
+
updateData.modelTier = normalizedModel.modelTier ?? null;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
221
233
|
|
|
222
234
|
// Recalculate nextRunAt based on schedule type
|
|
223
235
|
if (schedule.scheduleType === "one_time") {
|
package/src/tools/send-task.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { findDuplicateTask } from "@/tools/task-dedup";
|
|
|
15
15
|
import { ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
16
16
|
import { createToolRegistrar } from "@/tools/utils";
|
|
17
17
|
import { AgentTaskSchema, FollowUpConfigSchema } from "@/types";
|
|
18
|
+
import { ModelTierSchema, splitLegacyModelAlias } from "../model-tiers";
|
|
18
19
|
|
|
19
20
|
export const sendTaskInputSchema = z.object({
|
|
20
21
|
agentId: z
|
|
@@ -54,11 +55,16 @@ export const sendTaskInputSchema = z.object({
|
|
|
54
55
|
"VCS repo identifier (e.g., 'desplega-ai/agent-swarm' for GitHub or 'group/project' for GitLab). Links the task to a registered repo for workspace context.",
|
|
55
56
|
),
|
|
56
57
|
model: z
|
|
57
|
-
.
|
|
58
|
+
.string()
|
|
59
|
+
.trim()
|
|
60
|
+
.min(1)
|
|
58
61
|
.optional()
|
|
59
62
|
.describe(
|
|
60
|
-
"
|
|
63
|
+
"Concrete model override for this task, interpreted by the assignee's harness/provider. This does not switch providers. Prefer modelTier for portable intent.",
|
|
61
64
|
),
|
|
65
|
+
modelTier: ModelTierSchema.optional().describe(
|
|
66
|
+
"Portable model tier for this task: 'smol', 'regular', 'smart', or 'ultra'. Resolved at claim/run time using the assignee's harness/provider. Legacy model shortnames map as haiku→smol, sonnet→regular, opus→smart, fable→ultra.",
|
|
67
|
+
),
|
|
62
68
|
allowDuplicate: z
|
|
63
69
|
.boolean()
|
|
64
70
|
.default(false)
|
|
@@ -111,6 +117,7 @@ export async function sendTaskHandler(
|
|
|
111
117
|
parentTaskId,
|
|
112
118
|
vcsRepo,
|
|
113
119
|
model,
|
|
120
|
+
modelTier,
|
|
114
121
|
allowDuplicate,
|
|
115
122
|
slackChannelId,
|
|
116
123
|
slackThreadTs,
|
|
@@ -160,6 +167,7 @@ export async function sendTaskHandler(
|
|
|
160
167
|
}
|
|
161
168
|
|
|
162
169
|
const effectiveVcsRepo = vcsRepo;
|
|
170
|
+
const normalizedModel = splitLegacyModelAlias({ model, modelTier });
|
|
163
171
|
|
|
164
172
|
// Auto-default parentTaskId to caller's current task for tree tracking
|
|
165
173
|
const effectiveParentTaskId = parentTaskId ?? sourceTaskId;
|
|
@@ -263,7 +271,8 @@ export async function sendTaskHandler(
|
|
|
263
271
|
dir,
|
|
264
272
|
parentTaskId: effectiveParentTaskId,
|
|
265
273
|
vcsRepo: effectiveVcsRepo,
|
|
266
|
-
model,
|
|
274
|
+
model: normalizedModel.model,
|
|
275
|
+
modelTier: normalizedModel.modelTier,
|
|
267
276
|
slackChannelId,
|
|
268
277
|
slackThreadTs,
|
|
269
278
|
slackUserId,
|
|
@@ -316,7 +325,8 @@ export async function sendTaskHandler(
|
|
|
316
325
|
dir,
|
|
317
326
|
parentTaskId: effectiveParentTaskId,
|
|
318
327
|
vcsRepo: effectiveVcsRepo,
|
|
319
|
-
model,
|
|
328
|
+
model: normalizedModel.model,
|
|
329
|
+
modelTier: normalizedModel.modelTier,
|
|
320
330
|
slackChannelId,
|
|
321
331
|
slackThreadTs,
|
|
322
332
|
slackUserId,
|
|
@@ -343,7 +353,8 @@ export async function sendTaskHandler(
|
|
|
343
353
|
dir,
|
|
344
354
|
parentTaskId: effectiveParentTaskId,
|
|
345
355
|
vcsRepo: effectiveVcsRepo,
|
|
346
|
-
model,
|
|
356
|
+
model: normalizedModel.model,
|
|
357
|
+
modelTier: normalizedModel.modelTier,
|
|
347
358
|
slackChannelId,
|
|
348
359
|
slackThreadTs,
|
|
349
360
|
slackUserId,
|
package/src/tools/slack-post.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { getAgentById } from "@/be/db";
|
|
4
4
|
import { getSlackApp } from "@/slack/app";
|
|
5
|
+
import { withAutoJoin } from "@/slack/channel-join";
|
|
5
6
|
import { markdownToSlack } from "@/slack/responses";
|
|
6
7
|
import { createToolRegistrar } from "@/tools/utils";
|
|
7
8
|
|
|
@@ -68,22 +69,24 @@ export const registerSlackPostTool = (server: McpServer) => {
|
|
|
68
69
|
try {
|
|
69
70
|
const slackMessage = markdownToSlack(message);
|
|
70
71
|
|
|
71
|
-
const result = await app.client
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
72
|
+
const result = await withAutoJoin(app.client, channelId, () =>
|
|
73
|
+
app.client.chat.postMessage({
|
|
74
|
+
channel: channelId,
|
|
75
|
+
text: slackMessage, // Fallback for notifications
|
|
76
|
+
username: agent.name,
|
|
77
|
+
icon_emoji: ":crown:",
|
|
78
|
+
...(threadTs ? { thread_ts: threadTs } : {}),
|
|
79
|
+
blocks: [
|
|
80
|
+
{
|
|
81
|
+
type: "section",
|
|
82
|
+
text: {
|
|
83
|
+
type: "mrkdwn",
|
|
84
|
+
text: slackMessage,
|
|
85
|
+
},
|
|
83
86
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
],
|
|
88
|
+
}),
|
|
89
|
+
);
|
|
87
90
|
|
|
88
91
|
const messageTs = result.ts;
|
|
89
92
|
|
package/src/tools/slack-read.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { getAgentById, getInboxMessageById, getTaskById } from "@/be/db";
|
|
4
4
|
import { getSlackApp } from "@/slack/app";
|
|
5
|
+
import { withAutoJoin } from "@/slack/channel-join";
|
|
5
6
|
import { downloadFile } from "@/slack/files";
|
|
6
7
|
import { extractSlackMessageText } from "@/slack/message-text";
|
|
7
8
|
import { createToolRegistrar } from "@/tools/utils";
|
|
@@ -216,19 +217,16 @@ export const registerSlackReadTool = (server: McpServer) => {
|
|
|
216
217
|
let rawMessages: RawMessage[] = [];
|
|
217
218
|
|
|
218
219
|
if (slackThreadTs) {
|
|
219
|
-
// Fetch thread replies
|
|
220
|
-
const result = await client
|
|
221
|
-
channel: slackChannelId,
|
|
222
|
-
|
|
223
|
-
limit,
|
|
224
|
-
});
|
|
220
|
+
// Fetch thread replies — auto-join public channels on not_in_channel
|
|
221
|
+
const result = await withAutoJoin(client, slackChannelId, () =>
|
|
222
|
+
client.conversations.replies({ channel: slackChannelId, ts: slackThreadTs!, limit }),
|
|
223
|
+
);
|
|
225
224
|
rawMessages = (result.messages || []) as RawMessage[];
|
|
226
225
|
} else {
|
|
227
|
-
// Fetch channel history
|
|
228
|
-
const result = await client
|
|
229
|
-
channel: slackChannelId,
|
|
230
|
-
|
|
231
|
-
});
|
|
226
|
+
// Fetch channel history — auto-join public channels on not_in_channel
|
|
227
|
+
const result = await withAutoJoin(client, slackChannelId, () =>
|
|
228
|
+
client.conversations.history({ channel: slackChannelId, limit }),
|
|
229
|
+
);
|
|
232
230
|
rawMessages = (result.messages || []) as RawMessage[];
|
|
233
231
|
}
|
|
234
232
|
|
package/src/tools/slack-reply.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
markTaskSlackReplySent,
|
|
9
9
|
} from "@/be/db";
|
|
10
10
|
import { getSlackApp } from "@/slack/app";
|
|
11
|
+
import { withAutoJoin } from "@/slack/channel-join";
|
|
11
12
|
import { markdownToSlack } from "@/slack/responses";
|
|
12
13
|
import { createToolRegistrar } from "@/tools/utils";
|
|
13
14
|
|
|
@@ -118,22 +119,24 @@ export const registerSlackReplyTool = (server: McpServer) => {
|
|
|
118
119
|
try {
|
|
119
120
|
const slackMessage = markdownToSlack(message);
|
|
120
121
|
|
|
121
|
-
await app.client
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
122
|
+
await withAutoJoin(app.client, slackChannelId, () =>
|
|
123
|
+
app.client.chat.postMessage({
|
|
124
|
+
channel: slackChannelId,
|
|
125
|
+
thread_ts: slackThreadTs,
|
|
126
|
+
text: slackMessage, // Fallback for notifications
|
|
127
|
+
username: agent.name,
|
|
128
|
+
icon_emoji: agent.isLead ? ":crown:" : ":robot_face:",
|
|
129
|
+
blocks: [
|
|
130
|
+
{
|
|
131
|
+
type: "section",
|
|
132
|
+
text: {
|
|
133
|
+
type: "mrkdwn",
|
|
134
|
+
text: slackMessage,
|
|
135
|
+
},
|
|
133
136
|
},
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
],
|
|
138
|
+
}),
|
|
139
|
+
);
|
|
137
140
|
|
|
138
141
|
// After successful postMessage, mark task as having a Slack reply
|
|
139
142
|
if (taskId) {
|
|
@@ -2,6 +2,7 @@ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import * as z from "zod";
|
|
3
3
|
import { getAgentById } from "@/be/db";
|
|
4
4
|
import { getSlackApp } from "@/slack/app";
|
|
5
|
+
import { withAutoJoin } from "@/slack/channel-join";
|
|
5
6
|
import { markdownToSlack } from "@/slack/responses";
|
|
6
7
|
import { createToolRegistrar } from "@/tools/utils";
|
|
7
8
|
|
|
@@ -62,21 +63,23 @@ export const registerSlackStartThreadTool = (server: McpServer) => {
|
|
|
62
63
|
try {
|
|
63
64
|
const slackMessage = markdownToSlack(message);
|
|
64
65
|
|
|
65
|
-
const result = await app.client
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
const result = await withAutoJoin(app.client, channelId, () =>
|
|
67
|
+
app.client.chat.postMessage({
|
|
68
|
+
channel: channelId,
|
|
69
|
+
text: slackMessage, // Fallback for notifications
|
|
70
|
+
username: agent.name,
|
|
71
|
+
icon_emoji: ":crown:",
|
|
72
|
+
blocks: [
|
|
73
|
+
{
|
|
74
|
+
type: "section",
|
|
75
|
+
text: {
|
|
76
|
+
type: "mrkdwn",
|
|
77
|
+
text: slackMessage,
|
|
78
|
+
},
|
|
76
79
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
+
],
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
80
83
|
|
|
81
84
|
const ts = result.ts;
|
|
82
85
|
const resolvedChannelId = result.channel ?? channelId;
|
package/src/tools/task-action.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import { assertOwnsTask, ownerCtx, type ToolCtx } from "@/tools/task-tool-ctx";
|
|
29
29
|
import { createToolRegistrar } from "@/tools/utils";
|
|
30
30
|
import { AgentTaskSchema, BudgetRefusalCauseSchema } from "@/types";
|
|
31
|
+
import { ModelTierSchema, splitLegacyModelAlias } from "../model-tiers";
|
|
31
32
|
|
|
32
33
|
export const TaskActionSchema = z.enum([
|
|
33
34
|
"create",
|
|
@@ -66,11 +67,16 @@ export const taskActionInputSchema = z.object({
|
|
|
66
67
|
"Working directory (absolute path) for the agent to start in. Only used with 'create' action.",
|
|
67
68
|
),
|
|
68
69
|
model: z
|
|
69
|
-
.
|
|
70
|
+
.string()
|
|
71
|
+
.trim()
|
|
72
|
+
.min(1)
|
|
70
73
|
.optional()
|
|
71
74
|
.describe(
|
|
72
|
-
"
|
|
75
|
+
"Concrete model override for the created task, interpreted by the claiming worker's harness/provider. This does not switch providers. Only used with 'create' action.",
|
|
73
76
|
),
|
|
77
|
+
modelTier: ModelTierSchema.optional().describe(
|
|
78
|
+
"Portable model tier for the created task: 'smol', 'regular', 'smart', or 'ultra'. Resolved when a worker claims/runs the task. Only used with 'create' action.",
|
|
79
|
+
),
|
|
74
80
|
});
|
|
75
81
|
|
|
76
82
|
export const taskActionOutputSchema = z.object({
|
|
@@ -144,6 +150,7 @@ export async function taskActionHandler(
|
|
|
144
150
|
input: TaskActionArgs,
|
|
145
151
|
): Promise<CallToolResult> {
|
|
146
152
|
const { action, task, taskType, tags, priority, dependsOn, taskId, reason, dir, model } = input;
|
|
153
|
+
const normalizedModel = splitLegacyModelAlias({ model, modelTier: input.modelTier });
|
|
147
154
|
|
|
148
155
|
if (ctx.kind === "user") {
|
|
149
156
|
if (action !== "to_backlog" && action !== "from_backlog") {
|
|
@@ -229,7 +236,8 @@ export async function taskActionHandler(
|
|
|
229
236
|
priority,
|
|
230
237
|
dependsOn,
|
|
231
238
|
dir,
|
|
232
|
-
model,
|
|
239
|
+
model: normalizedModel.model,
|
|
240
|
+
modelTier: normalizedModel.modelTier,
|
|
233
241
|
});
|
|
234
242
|
return {
|
|
235
243
|
success: true,
|
package/src/types.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as z from "zod";
|
|
2
|
+
import { ModelTierSchema } from "./model-tiers";
|
|
2
3
|
|
|
3
4
|
// Task status - includes new unassigned and offered states
|
|
4
5
|
export const AgentTaskStatusSchema = z.enum([
|
|
@@ -170,6 +171,8 @@ export const AgentTaskSchema = z.object({
|
|
|
170
171
|
slackThreadTs: z.string().optional(),
|
|
171
172
|
slackUserId: z.string().optional(),
|
|
172
173
|
slackReplySent: z.boolean().default(false),
|
|
174
|
+
slackProgressMessageTs: z.string().optional(),
|
|
175
|
+
slackTreeRootMessageTs: z.string().optional(),
|
|
173
176
|
|
|
174
177
|
// VCS metadata (GitHub / GitLab — provider-agnostic)
|
|
175
178
|
vcsProvider: z.enum(["github", "gitlab"]).optional(),
|
|
@@ -200,7 +203,10 @@ export const AgentTaskSchema = z.object({
|
|
|
200
203
|
|
|
201
204
|
// Model selection (optional — provider-specific; can be "opus", "gpt-4o",
|
|
202
205
|
// "openrouter/openai/gpt-5-nano", etc. depending on HARNESS_PROVIDER).
|
|
206
|
+
// Prefer modelTier for portable task intent; model is a concrete override
|
|
207
|
+
// interpreted by the claiming worker's harness and never switches provider.
|
|
203
208
|
model: z.string().optional(),
|
|
209
|
+
modelTier: ModelTierSchema.optional(),
|
|
204
210
|
|
|
205
211
|
// Schedule linking (optional — set when task was created by a schedule)
|
|
206
212
|
scheduleId: z.uuid().optional(),
|
|
@@ -247,6 +253,10 @@ export const AgentTaskSchema = z.object({
|
|
|
247
253
|
provider: ProviderNameSchema.optional(),
|
|
248
254
|
providerMeta: z.record(z.string(), z.unknown()).optional(),
|
|
249
255
|
|
|
256
|
+
// Harness variant — sub-variant within a provider (e.g. "bridge" vs "stock" for claude)
|
|
257
|
+
harnessVariant: z.string().optional(),
|
|
258
|
+
harnessVariantMeta: z.record(z.string(), z.unknown()).optional(),
|
|
259
|
+
|
|
250
260
|
// Aggregated session cost for task list/read models. Undefined means no
|
|
251
261
|
// session cost rows have been recorded for this task.
|
|
252
262
|
totalCostUsd: z.number().min(0).optional(),
|
|
@@ -870,6 +880,7 @@ export const ScheduledTaskSchema = z
|
|
|
870
880
|
lastErrorAt: z.iso.datetime().optional(),
|
|
871
881
|
lastErrorMessage: z.string().optional(),
|
|
872
882
|
model: z.string().optional(),
|
|
883
|
+
modelTier: ModelTierSchema.optional(),
|
|
873
884
|
scheduleType: z.enum(["recurring", "one_time"]).default("recurring"),
|
|
874
885
|
createdAt: z.iso.datetime(),
|
|
875
886
|
lastUpdatedAt: z.iso.datetime(),
|
|
@@ -1371,6 +1382,7 @@ export type AgentTaskSummary = Pick<
|
|
|
1371
1382
|
| "parentTaskId"
|
|
1372
1383
|
| "scheduleId"
|
|
1373
1384
|
| "model"
|
|
1385
|
+
| "modelTier"
|
|
1374
1386
|
| "provider"
|
|
1375
1387
|
| "requestedByUserId"
|
|
1376
1388
|
| "progress"
|
|
@@ -1434,6 +1446,13 @@ export const MetricVariableSchema = z.object({
|
|
|
1434
1446
|
}),
|
|
1435
1447
|
)
|
|
1436
1448
|
.optional(),
|
|
1449
|
+
optionsQuery: z
|
|
1450
|
+
.object({
|
|
1451
|
+
sql: z.string().min(1).max(10_000),
|
|
1452
|
+
valueKey: z.string().min(1),
|
|
1453
|
+
labelKey: z.string().min(1).optional(),
|
|
1454
|
+
})
|
|
1455
|
+
.optional(),
|
|
1437
1456
|
});
|
|
1438
1457
|
export type MetricVariable = z.infer<typeof MetricVariableSchema>;
|
|
1439
1458
|
|
|
@@ -1463,6 +1482,8 @@ export const MetricWidgetSchema = z.object({
|
|
|
1463
1482
|
description: z.string().optional(),
|
|
1464
1483
|
query: MetricQuerySchema,
|
|
1465
1484
|
viz: MetricVizConfigSchema,
|
|
1485
|
+
colSpan: z.number().int().min(1).max(4).optional(),
|
|
1486
|
+
rowSpan: z.number().int().min(1).max(4).optional(),
|
|
1466
1487
|
});
|
|
1467
1488
|
export type MetricWidget = z.infer<typeof MetricWidgetSchema>;
|
|
1468
1489
|
|
|
@@ -1579,6 +1600,13 @@ export const ScriptRunSchema = z.object({
|
|
|
1579
1600
|
});
|
|
1580
1601
|
export type ScriptRun = z.infer<typeof ScriptRunSchema>;
|
|
1581
1602
|
|
|
1603
|
+
export const ScriptRunListItemSchema = ScriptRunSchema.omit({
|
|
1604
|
+
source: true,
|
|
1605
|
+
args: true,
|
|
1606
|
+
output: true,
|
|
1607
|
+
});
|
|
1608
|
+
export type ScriptRunListItem = z.infer<typeof ScriptRunListItemSchema>;
|
|
1609
|
+
|
|
1582
1610
|
export const ScriptRunJournalEntrySchema = z.object({
|
|
1583
1611
|
id: z.string().uuid(),
|
|
1584
1612
|
runId: z.string().uuid(),
|
|
@@ -1746,6 +1774,18 @@ export const ScriptVersionRecordSchema = z.object({
|
|
|
1746
1774
|
});
|
|
1747
1775
|
export type ScriptVersionRecord = z.infer<typeof ScriptVersionRecordSchema>;
|
|
1748
1776
|
|
|
1777
|
+
/** Lean projection served by `GET /api/scripts` — omits `source` (payload size) and raw JSON blobs. */
|
|
1778
|
+
export type ScriptListItem = Omit<
|
|
1779
|
+
ScriptRecord,
|
|
1780
|
+
"source" | "signatureJson" | "argsJsonSchema" | "contentHash"
|
|
1781
|
+
>;
|
|
1782
|
+
|
|
1783
|
+
/** Full record served by `GET /api/scripts/{id}` — includes `source` plus parsed `signature`/`argsJsonSchema`. */
|
|
1784
|
+
export type ScriptDetail = Omit<ScriptRecord, "argsJsonSchema"> & {
|
|
1785
|
+
signature: unknown;
|
|
1786
|
+
argsJsonSchema: unknown;
|
|
1787
|
+
};
|
|
1788
|
+
|
|
1749
1789
|
// ============================================================================
|
|
1750
1790
|
// Skill Types
|
|
1751
1791
|
// ============================================================================
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AWS SDK error classifier for the pi-mono/Bedrock path.
|
|
3
|
+
*
|
|
4
|
+
* Provides a single shared matcher (`classifyAwsSdkError`) consumed by:
|
|
5
|
+
* - `src/providers/pi-mono-adapter.ts` — emits ProviderEvent {type:'error'} mid-stream
|
|
6
|
+
* - `src/commands/runner.ts` — backstop in the no-schema/no-progress branch
|
|
7
|
+
*
|
|
8
|
+
* The regex set lives here exactly once so the two sites never diverge.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export type AwsErrorCategory = "aws-auth" | "aws-throttle" | "aws-access" | "aws-model";
|
|
12
|
+
|
|
13
|
+
export interface AwsErrorClassification {
|
|
14
|
+
category: AwsErrorCategory;
|
|
15
|
+
/** Human-readable, actionable error message for the session-chat red box and task failureReason. */
|
|
16
|
+
message: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface AwsErrorRule {
|
|
20
|
+
patterns: RegExp[];
|
|
21
|
+
category: AwsErrorCategory;
|
|
22
|
+
message: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Priority-ordered rules. The first matching rule wins, so the most critical
|
|
27
|
+
* category (auth) takes precedence over the more generic ones (model errors).
|
|
28
|
+
*/
|
|
29
|
+
const AWS_ERROR_RULES: AwsErrorRule[] = [
|
|
30
|
+
{
|
|
31
|
+
// Expired / missing / invalid credentials
|
|
32
|
+
patterns: [
|
|
33
|
+
/ExpiredToken(?:Exception)?/,
|
|
34
|
+
/CredentialsProviderError/,
|
|
35
|
+
/Unable to locate credentials/,
|
|
36
|
+
/security token.*expired/i,
|
|
37
|
+
/expired.*security token/i,
|
|
38
|
+
/InvalidSignatureException/,
|
|
39
|
+
/UnrecognizedClientException/,
|
|
40
|
+
],
|
|
41
|
+
category: "aws-auth",
|
|
42
|
+
message:
|
|
43
|
+
"AWS credentials have expired or are missing. " +
|
|
44
|
+
"Run `aws sso login` (or refresh your credentials via `aws configure`) and retry.",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
// Rate limits and quota exceeded
|
|
48
|
+
patterns: [
|
|
49
|
+
/ThrottlingException/,
|
|
50
|
+
/TooManyRequestsException/,
|
|
51
|
+
/ServiceQuotaExceededException/,
|
|
52
|
+
/Rate exceeded/,
|
|
53
|
+
],
|
|
54
|
+
category: "aws-throttle",
|
|
55
|
+
message:
|
|
56
|
+
"AWS Bedrock request was throttled (rate limit or quota exceeded). " +
|
|
57
|
+
"Wait and retry, or request a quota increase in the AWS Service Quotas console.",
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
// IAM / authorization denials
|
|
61
|
+
patterns: [/AccessDeniedException/, /not authorized to perform/i],
|
|
62
|
+
category: "aws-access",
|
|
63
|
+
message:
|
|
64
|
+
"AWS authorization denied for Bedrock. " +
|
|
65
|
+
"Verify the IAM role/user has the `bedrock:InvokeModel` permission for the target model ARN and region.",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
// Bad model ID, region mismatch, model not ready
|
|
69
|
+
patterns: [
|
|
70
|
+
/ValidationException/,
|
|
71
|
+
/ResourceNotFoundException/,
|
|
72
|
+
/ModelTimeoutException/,
|
|
73
|
+
/ModelNotReadyException/,
|
|
74
|
+
],
|
|
75
|
+
category: "aws-model",
|
|
76
|
+
message:
|
|
77
|
+
"AWS Bedrock model error: the model ID may be invalid, unavailable in this region, or not yet ready. " +
|
|
78
|
+
"Verify MODEL_OVERRIDE and the AWS region in your environment.",
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Classify an error message string against known AWS SDK error patterns.
|
|
84
|
+
*
|
|
85
|
+
* Returns the first matching `{category, message}` pair (priority order:
|
|
86
|
+
* aws-auth → aws-throttle → aws-access → aws-model), or `null` if no
|
|
87
|
+
* known AWS SDK signature is found in `text`.
|
|
88
|
+
*/
|
|
89
|
+
export function classifyAwsSdkError(text: string): AwsErrorClassification | null {
|
|
90
|
+
if (!text) return null;
|
|
91
|
+
for (const rule of AWS_ERROR_RULES) {
|
|
92
|
+
if (rule.patterns.some((re) => re.test(text))) {
|
|
93
|
+
return { category: rule.category, message: rule.message };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
@@ -26,6 +26,9 @@
|
|
|
26
26
|
export const CONTEXT_FORMULA = "input-cache-output" as const;
|
|
27
27
|
|
|
28
28
|
const CONTEXT_WINDOW_DEFAULTS: Record<string, number> = {
|
|
29
|
+
// Anthropic Fable / Mythos tier
|
|
30
|
+
"claude-fable-5": 1_000_000,
|
|
31
|
+
"claude-mythos-5": 1_000_000,
|
|
29
32
|
// Anthropic 4.x family
|
|
30
33
|
"claude-opus-4-8": 1_000_000,
|
|
31
34
|
"claude-opus-4-7": 1_000_000,
|
|
@@ -45,6 +48,8 @@ const CONTEXT_WINDOW_DEFAULTS: Record<string, number> = {
|
|
|
45
48
|
"claude-3-sonnet": 200_000,
|
|
46
49
|
"claude-3-haiku": 200_000,
|
|
47
50
|
// Shortnames used by the local-CLI adapter and pi-mono OpenRouter mirror.
|
|
51
|
+
fable: 1_000_000,
|
|
52
|
+
mythos: 1_000_000,
|
|
48
53
|
opus: 1_000_000,
|
|
49
54
|
sonnet: 1_000_000,
|
|
50
55
|
haiku: 200_000,
|