@desplega.ai/agent-swarm 1.149.0 → 1.149.1
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/{acp-adapter-4jncb126.js → acp-adapter-vhkzfpjg.js} +1 -1
- package/dist/{actions-31vp3ae7.js → actions-9d2694yk.js} +6 -6
- package/dist/{app-1hh67x8w.js → app-fq2ggynt.js} +3 -3
- package/dist/{assistant-z66dmz6p.js → assistant-edb6n9nn.js} +8 -8
- package/dist/{boot-reembed-wxa8ya0s.js → boot-reembed-msweabbp.js} +4 -4
- package/dist/{boot-reembed-fqzxxzjh.js → boot-reembed-q5wd3tha.js} +3 -3
- package/dist/{boot-scrub-logs-jnyndh79.js → boot-scrub-logs-3g1az933.js} +2 -2
- package/dist/{claude-adapter-3qpa9w0j.js → claude-adapter-v3rnzsdc.js} +3 -3
- package/dist/{claude-sdk-session-2yxgjs5d.js → claude-sdk-session-qxrpsna1.js} +3 -3
- package/dist/{cli-nye12xk5.js → cli-01g3x1ca.js} +3 -3
- package/dist/{cli-qaacms9y.js → cli-1c94wvcw.js} +5 -5
- package/dist/{cli-d8ftsp62.js → cli-3sysyjve.js} +7 -7
- package/dist/{cli-2qts1hys.js → cli-3z634pqe.js} +2 -2
- package/dist/{cli-n3dz2942.js → cli-4260r1tg.js} +3 -3
- package/dist/{cli-9fy6nk6g.js → cli-5m0qmvdp.js} +1 -1
- package/dist/{cli-k0cr4kat.js → cli-6skz3rk8.js} +1 -1
- package/dist/{cli-e95cx1eb.js → cli-73gq0zxn.js} +1 -1
- package/dist/{cli-phsxnkp6.js → cli-7evafqvy.js} +9 -9
- package/dist/{cli-ygweqcn4.js → cli-7fehefdf.js} +5 -5
- package/dist/{cli-sd5pv3b1.js → cli-7xx10h0a.js} +2 -2
- package/dist/{cli-ykxrnd1q.js → cli-9br8ypfv.js} +2 -2
- package/dist/{cli-q50ef8g0.js → cli-ayvy5kde.js} +1 -1
- package/dist/{cli-9hspd3dp.js → cli-cd60fv6j.js} +1 -1
- package/dist/{cli-yb9qhqam.js → cli-cv7s5zdq.js} +18 -18
- package/dist/{cli-6pqcb4q0.js → cli-ddfw1mc4.js} +8 -8
- package/dist/{cli-kjwt6hdf.js → cli-e06a8a7y.js} +1 -1
- package/dist/{cli-19f354qr.js → cli-hzqmgcm2.js} +2 -2
- package/dist/{cli-gv88e1vf.js → cli-j4d4f6hv.js} +1 -1
- package/dist/{cli-3q5ejx9p.js → cli-jezx9b9c.js} +1 -1
- package/dist/{cli-7smrsr25.js → cli-k351hv5k.js} +2 -2
- package/dist/{cli-yab68w40.js → cli-qgd4sqz5.js} +2 -1
- package/dist/{cli-kgz4np97.js → cli-sdzq01t9.js} +2 -2
- package/dist/{cli-aae6hz6a.js → cli-svbrpf5q.js} +3 -3
- package/dist/{cli-cr12paw2.js → cli-tg8qj8ee.js} +1 -1
- package/dist/{cli-gjhjfeeg.js → cli-wby1xnnb.js} +1 -1
- package/dist/{cli-mnmsfd1w.js → cli-y3pq4v9g.js} +1 -1
- package/dist/cli.js +11 -11
- package/dist/{codex-adapter-8meawydj.js → codex-adapter-jyw7a91z.js} +2 -2
- package/dist/{codex-session-runner-gjgxyp5j.js → codex-session-runner-bmngj0s5.js} +2 -2
- package/dist/{commands-hz3nb95f.js → commands-n755g391.js} +2 -2
- package/dist/{db-ggz97zbm.js → db-s1cgh5gv.js} +2 -2
- package/dist/{handlers-nbzdzn9k.js → handlers-nsg0bcye.js} +8 -8
- package/dist/{hook-bks2q1f6.js → hook-h9cqrm8q.js} +1 -1
- package/dist/{hook-kq9wp0ep.js → hook-jf0xe8zh.js} +2 -2
- package/dist/{http-hrf77v87.js → http-nc6pydw5.js} +34 -34
- package/dist/{index-cqtebbhb.js → index-0wdy6gwc.js} +9 -9
- package/dist/{index-8hmeg8j1.js → index-769nc012.js} +10 -10
- package/dist/{index-6nsbg88z.js → index-ezxkds2n.js} +173 -49
- package/dist/{index-n28pw1zt.js → index-x1zcj09z.js} +11 -11
- package/dist/{keepalive-dxc12gvg.js → keepalive-nesm3xb6.js} +4 -4
- package/dist/{lead-yckfn18d.js → lead-bwy6t2za.js} +22 -22
- package/dist/{maintenance-6kfb2m5e.js → maintenance-8w393zf0.js} +4 -4
- package/dist/{oauth-refresh-sweep-1x1ac722.js → oauth-refresh-sweep-sh8xjy08.js} +3 -3
- package/dist/{onboard-dcyt1609.js → onboard-25ps29h1.js} +2 -2
- package/dist/{otel-impl-tbk3e5dr.js → otel-impl-b93gxnz5.js} +1 -1
- package/dist/{pricing-refresh-kaqg567e.js → pricing-refresh-5v56x7gb.js} +4 -4
- package/dist/{rbac-roles-cf97v1vd.js → rbac-roles-0p0s6pbj.js} +3 -3
- package/dist/{rbac-roles-ammq6rd2.js → rbac-roles-x55cnezn.js} +2 -2
- package/dist/{render-v2-80w603vy.js → render-v2-hyd45w8b.js} +3 -3
- package/dist/{seed-pricing-zbrn83xv.js → seed-pricing-49gbgn96.js} +3 -3
- package/dist/{setup-fe66kczs.js → setup-qvq64vzq.js} +2 -2
- package/dist/{worker-qmb2d1e2.js → worker-scj60cxg.js} +22 -22
- package/openapi.json +1 -1
- package/package.json +1 -1
- package/src/be/seed/automation-toggle.ts +69 -0
- package/src/be/seed/schedules-seeder.ts +28 -6
- package/src/be/seed/workflows-seeder.ts +18 -6
- package/src/be/swarm-config-guard.ts +1 -0
- package/src/tests/seed-registry.test.ts +2 -2
- package/src/tests/seed-schedules.test.ts +99 -3
- package/src/tests/seed-workflows.test.ts +74 -0
- package/templates/schedules/daily-blocker-digest/config.json +6 -1
- package/templates/schedules/daily-compounding-reflection/config.json +1 -0
- package/templates/schedules/daily-status-report/config.json +6 -1
- package/templates/schedules/daily-swarm-update-check/config.json +19 -0
- package/templates/schedules/daily-swarm-update-check/content.md +49 -0
- package/templates/schedules/daily-workflow-health-audit/config.json +6 -1
|
@@ -39,6 +39,35 @@ function scheduleSource(cron = "0 9 * * *", description = "Seeded schedule.") {
|
|
|
39
39
|
} satisfies ScheduleTemplateSource;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/** A zero-config candidate: no requires, no placeholders — eligible for auto-enable. */
|
|
43
|
+
function zeroConfigScheduleSource(opts?: {
|
|
44
|
+
name?: string;
|
|
45
|
+
templateEnabled?: boolean;
|
|
46
|
+
autoEnableCandidate?: boolean;
|
|
47
|
+
}) {
|
|
48
|
+
const name = opts?.name ?? "test-zero-config-schedule";
|
|
49
|
+
const templateEnabled = opts?.templateEnabled ?? true;
|
|
50
|
+
return {
|
|
51
|
+
config: JSON.stringify({
|
|
52
|
+
name,
|
|
53
|
+
title: "Test zero-config schedule",
|
|
54
|
+
description: "Zero-config seeded schedule.",
|
|
55
|
+
placeholders: [],
|
|
56
|
+
requires: [],
|
|
57
|
+
runAllSeedersCandidate: true,
|
|
58
|
+
autoEnableCandidate: opts?.autoEnableCandidate ?? true,
|
|
59
|
+
tags: ["fixture"],
|
|
60
|
+
}),
|
|
61
|
+
content: `# Test\n\n## Schedule\n\n\`\`\`json\n${JSON.stringify({
|
|
62
|
+
cron: "0 9 * * *",
|
|
63
|
+
timezone: "UTC",
|
|
64
|
+
enabled: templateEnabled,
|
|
65
|
+
})}\n\`\`\`\n\n## Scheduled Task\n\nRun work.`,
|
|
66
|
+
} satisfies ScheduleTemplateSource;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const ORIGINAL_SEED_AUTOMATIONS_ENABLED = process.env.SEED_AUTOMATIONS_ENABLED;
|
|
70
|
+
|
|
42
71
|
beforeEach(async () => {
|
|
43
72
|
await removeDbFiles();
|
|
44
73
|
initDb(TEST_DB_PATH);
|
|
@@ -47,15 +76,29 @@ beforeEach(async () => {
|
|
|
47
76
|
afterEach(async () => {
|
|
48
77
|
closeDb();
|
|
49
78
|
await removeDbFiles();
|
|
79
|
+
if (ORIGINAL_SEED_AUTOMATIONS_ENABLED === undefined) {
|
|
80
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
81
|
+
} else {
|
|
82
|
+
process.env.SEED_AUTOMATIONS_ENABLED = ORIGINAL_SEED_AUTOMATIONS_ENABLED;
|
|
83
|
+
}
|
|
50
84
|
});
|
|
51
85
|
|
|
52
86
|
describe("schedules seeder", () => {
|
|
53
|
-
test("loads only the
|
|
87
|
+
test("loads only the five starter candidates, including the unfenced schedule block", () => {
|
|
54
88
|
const schedules = loadSeedSchedules();
|
|
55
|
-
expect(schedules).toHaveLength(
|
|
89
|
+
expect(schedules).toHaveLength(5);
|
|
90
|
+
// Zero-config, requires:[], placeholders:[], template enabled:true -> auto-enabled
|
|
91
|
+
// under the default (unset -> on) SEED_AUTOMATIONS_ENABLED switch.
|
|
56
92
|
expect(schedules.find((schedule) => schedule.name === "daily-status-report")).toMatchObject({
|
|
57
93
|
cronExpression: "15 2 * * *",
|
|
58
|
-
enabled:
|
|
94
|
+
enabled: true,
|
|
95
|
+
requiredParams: [],
|
|
96
|
+
requires: [],
|
|
97
|
+
});
|
|
98
|
+
expect(
|
|
99
|
+
schedules.find((schedule) => schedule.name === "daily-swarm-update-check"),
|
|
100
|
+
).toMatchObject({
|
|
101
|
+
enabled: true,
|
|
59
102
|
requiredParams: [],
|
|
60
103
|
requires: [],
|
|
61
104
|
});
|
|
@@ -65,6 +108,59 @@ describe("schedules seeder", () => {
|
|
|
65
108
|
);
|
|
66
109
|
});
|
|
67
110
|
|
|
111
|
+
test("SEED_AUTOMATIONS_ENABLED=false keeps every candidate disabled, including zero-config ones", () => {
|
|
112
|
+
process.env.SEED_AUTOMATIONS_ENABLED = "false";
|
|
113
|
+
const schedules = loadSeedSchedules();
|
|
114
|
+
expect(schedules.every((schedule) => schedule.enabled === false)).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test("switch on: a zero-config candidate auto-enables via createSchedulesSeeder/apply", async () => {
|
|
118
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
119
|
+
const seeder = createSchedulesSeeder([zeroConfigScheduleSource()]);
|
|
120
|
+
await runSeeder(seeder, { quiet: true });
|
|
121
|
+
expect(await getScheduledTaskByName("test-zero-config-schedule")).toMatchObject({
|
|
122
|
+
enabled: true,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("switch on: an item with unmet requires stays disabled", async () => {
|
|
127
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
128
|
+
const seeder = createSchedulesSeeder([scheduleSource()]);
|
|
129
|
+
await runSeeder(seeder, { quiet: true });
|
|
130
|
+
expect(await getScheduledTaskByName("test-seeded-schedule")).toMatchObject({
|
|
131
|
+
enabled: false,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("switch off: a zero-config candidate stays disabled", async () => {
|
|
136
|
+
process.env.SEED_AUTOMATIONS_ENABLED = "false";
|
|
137
|
+
const seeder = createSchedulesSeeder([zeroConfigScheduleSource()]);
|
|
138
|
+
await runSeeder(seeder, { quiet: true });
|
|
139
|
+
expect(await getScheduledTaskByName("test-zero-config-schedule")).toMatchObject({
|
|
140
|
+
enabled: false,
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("switch on: a zero-config candidate whose template recommends staying off stays disabled", async () => {
|
|
145
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
146
|
+
const seeder = createSchedulesSeeder([zeroConfigScheduleSource({ templateEnabled: false })]);
|
|
147
|
+
await runSeeder(seeder, { quiet: true });
|
|
148
|
+
expect(await getScheduledTaskByName("test-zero-config-schedule")).toMatchObject({
|
|
149
|
+
enabled: false,
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test("switch on: a zero-config candidate without an explicit autoEnableCandidate opt-in stays disabled", async () => {
|
|
154
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
155
|
+
const seeder = createSchedulesSeeder([
|
|
156
|
+
zeroConfigScheduleSource({ autoEnableCandidate: false }),
|
|
157
|
+
]);
|
|
158
|
+
await runSeeder(seeder, { quiet: true });
|
|
159
|
+
expect(await getScheduledTaskByName("test-zero-config-schedule")).toMatchObject({
|
|
160
|
+
enabled: false,
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
|
|
68
164
|
test("seeds a schedule with setup metadata and re-runs as a no-op", async () => {
|
|
69
165
|
const seeder = createSchedulesSeeder([scheduleSource()]);
|
|
70
166
|
const first = await runSeeder(seeder, { quiet: true });
|
|
@@ -35,10 +35,36 @@ function workflowSource(template = "Do the work.", description = "Seeded workflo
|
|
|
35
35
|
} satisfies WorkflowTemplateSource;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** A zero-config candidate: no requires, no placeholders — eligible for auto-enable. */
|
|
39
|
+
function zeroConfigWorkflowSource(opts?: {
|
|
40
|
+
name?: string;
|
|
41
|
+
templateEnabled?: boolean;
|
|
42
|
+
autoEnableCandidate?: boolean;
|
|
43
|
+
}) {
|
|
44
|
+
const name = opts?.name ?? "test-zero-config-workflow";
|
|
45
|
+
const payloadEnabled = opts?.templateEnabled === false ? { enabled: false } : {};
|
|
46
|
+
return {
|
|
47
|
+
config: JSON.stringify({
|
|
48
|
+
name,
|
|
49
|
+
description: "Zero-config seeded workflow.",
|
|
50
|
+
placeholders: [],
|
|
51
|
+
requires: [],
|
|
52
|
+
runAllSeedersCandidate: true,
|
|
53
|
+
autoEnableCandidate: opts?.autoEnableCandidate ?? true,
|
|
54
|
+
}),
|
|
55
|
+
content: `# Test\n\n\`\`\`json\n${JSON.stringify({
|
|
56
|
+
nodes: [{ id: "work", type: "agent-task", config: { template: "Do the work." } }],
|
|
57
|
+
...payloadEnabled,
|
|
58
|
+
})}\n\`\`\``,
|
|
59
|
+
} satisfies WorkflowTemplateSource;
|
|
60
|
+
}
|
|
61
|
+
|
|
38
62
|
async function getWorkflow(name = "test-seeded-workflow") {
|
|
39
63
|
return (await listWorkflows()).find((workflow) => workflow.name === name) ?? null;
|
|
40
64
|
}
|
|
41
65
|
|
|
66
|
+
const ORIGINAL_SEED_AUTOMATIONS_ENABLED = process.env.SEED_AUTOMATIONS_ENABLED;
|
|
67
|
+
|
|
42
68
|
beforeEach(async () => {
|
|
43
69
|
await removeDbFiles();
|
|
44
70
|
initDb(TEST_DB_PATH);
|
|
@@ -47,6 +73,11 @@ beforeEach(async () => {
|
|
|
47
73
|
afterEach(async () => {
|
|
48
74
|
closeDb();
|
|
49
75
|
await removeDbFiles();
|
|
76
|
+
if (ORIGINAL_SEED_AUTOMATIONS_ENABLED === undefined) {
|
|
77
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
78
|
+
} else {
|
|
79
|
+
process.env.SEED_AUTOMATIONS_ENABLED = ORIGINAL_SEED_AUTOMATIONS_ENABLED;
|
|
80
|
+
}
|
|
50
81
|
});
|
|
51
82
|
|
|
52
83
|
describe("workflows seeder", () => {
|
|
@@ -65,6 +96,49 @@ describe("workflows seeder", () => {
|
|
|
65
96
|
expect(workflows.some((workflow) => workflow.name === "gsc-topic-miner")).toBe(false);
|
|
66
97
|
});
|
|
67
98
|
|
|
99
|
+
test("SEED_AUTOMATIONS_ENABLED=false keeps every candidate disabled, including zero-config ones", () => {
|
|
100
|
+
process.env.SEED_AUTOMATIONS_ENABLED = "false";
|
|
101
|
+
const workflows = loadSeedWorkflows();
|
|
102
|
+
expect(workflows.every((workflow) => workflow.enabled === false)).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("switch on: a zero-config candidate auto-enables via createWorkflowsSeeder/apply", async () => {
|
|
106
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
107
|
+
const seeder = createWorkflowsSeeder([zeroConfigWorkflowSource()]);
|
|
108
|
+
await runSeeder(seeder, { quiet: true });
|
|
109
|
+
expect(await getWorkflow("test-zero-config-workflow")).toMatchObject({ enabled: true });
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("switch on: an item with unmet requires stays disabled", async () => {
|
|
113
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
114
|
+
const seeder = createWorkflowsSeeder([workflowSource()]);
|
|
115
|
+
await runSeeder(seeder, { quiet: true });
|
|
116
|
+
expect(await getWorkflow()).toMatchObject({ enabled: false });
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
test("switch off: a zero-config candidate stays disabled", async () => {
|
|
120
|
+
process.env.SEED_AUTOMATIONS_ENABLED = "false";
|
|
121
|
+
const seeder = createWorkflowsSeeder([zeroConfigWorkflowSource()]);
|
|
122
|
+
await runSeeder(seeder, { quiet: true });
|
|
123
|
+
expect(await getWorkflow("test-zero-config-workflow")).toMatchObject({ enabled: false });
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("switch on: a zero-config candidate whose template recommends staying off stays disabled", async () => {
|
|
127
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
128
|
+
const seeder = createWorkflowsSeeder([zeroConfigWorkflowSource({ templateEnabled: false })]);
|
|
129
|
+
await runSeeder(seeder, { quiet: true });
|
|
130
|
+
expect(await getWorkflow("test-zero-config-workflow")).toMatchObject({ enabled: false });
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("switch on: a zero-config candidate without an explicit autoEnableCandidate opt-in stays disabled", async () => {
|
|
134
|
+
delete process.env.SEED_AUTOMATIONS_ENABLED;
|
|
135
|
+
const seeder = createWorkflowsSeeder([
|
|
136
|
+
zeroConfigWorkflowSource({ autoEnableCandidate: false }),
|
|
137
|
+
]);
|
|
138
|
+
await runSeeder(seeder, { quiet: true });
|
|
139
|
+
expect(await getWorkflow("test-zero-config-workflow")).toMatchObject({ enabled: false });
|
|
140
|
+
});
|
|
141
|
+
|
|
68
142
|
test("seeds a workflow and re-runs as a no-op", async () => {
|
|
69
143
|
const seeder = createWorkflowsSeeder([workflowSource()]);
|
|
70
144
|
const first = await runSeeder(seeder, { quiet: true });
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "schedule",
|
|
3
|
+
"name": "daily-swarm-update-check",
|
|
4
|
+
"displayName": "Daily Agent-Swarm Update Check",
|
|
5
|
+
"slug": "daily-swarm-update-check",
|
|
6
|
+
"title": "Daily Agent-Swarm Update Check",
|
|
7
|
+
"description": "Compare the running agent-swarm version against the latest published GitHub release and flag the operator when an upgrade is available. Zero-config: no repo clone, no integration required.",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"category": "schedules",
|
|
10
|
+
"placeholders": [],
|
|
11
|
+
"requires": [],
|
|
12
|
+
"runAllSeedersCandidate": true,
|
|
13
|
+
"autoEnableCandidate": true,
|
|
14
|
+
"tags": [
|
|
15
|
+
"operations",
|
|
16
|
+
"release",
|
|
17
|
+
"upgrade"
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Daily Agent-Swarm Update Check
|
|
2
|
+
|
|
3
|
+
Zero-config auto-update watchdog: compares the swarm's own running version against the latest published GitHub release of `desplega-ai/agent-swarm`, and flags the operator when an upgrade is available. No placeholders and no integration required — every install gets this by default.
|
|
4
|
+
|
|
5
|
+
## Schedule
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{"cron":"0 9 * * *","timezone":"UTC","agentRole":"lead","enabled":true}
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Scheduled Task
|
|
12
|
+
|
|
13
|
+
This schedule runs by default. It needs no repository clone and no integration — it only reads its own task metadata and calls the public GitHub API.
|
|
14
|
+
|
|
15
|
+
Task Type: Daily Agent-Swarm Update Check
|
|
16
|
+
|
|
17
|
+
You are Lead. Determine whether a newer `desplega-ai/agent-swarm` release is published than the version currently running on this install, and tell the operator if so.
|
|
18
|
+
|
|
19
|
+
## Phase 1 — Determine the running version
|
|
20
|
+
|
|
21
|
+
Call `get-task-details` for THIS task's own id and read `task.swarmVersion` — the API server stamps every task with the `package.json` version it was running at task-creation time, so this is the version currently deployed on this install.
|
|
22
|
+
|
|
23
|
+
If `swarmVersion` is missing or empty (an older install, or the field predates this schedule), complete with `output`: `"Unable to determine running version — swarmVersion not set on this task."` and stop. Do not guess a version.
|
|
24
|
+
|
|
25
|
+
## Phase 2 — Fetch the latest published release
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
curl -fsSL https://api.github.com/repos/desplega-ai/agent-swarm/releases/latest
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Read `.tag_name` (strip a leading `v` if present) and `.html_url`. If the request fails (network, rate limit, non-200), complete with `output`: `"Could not reach GitHub releases API: <error>"` and stop — do not retry in a loop.
|
|
32
|
+
|
|
33
|
+
## Phase 3 — Compare and act
|
|
34
|
+
|
|
35
|
+
Compare the two versions as dotted numeric triples (major.minor.patch) — never as plain strings (`"1.9.0" < "1.10.0"` is false as strings).
|
|
36
|
+
|
|
37
|
+
- **Latest <= running:** complete with `output`: `"Up to date: running <running>, latest published <latest>."` No further action.
|
|
38
|
+
- **Latest > running:** raise it for the operator:
|
|
39
|
+
1. Post a short message to your configured admin delivery channel (or the in-app fallback if none is configured) naming the running version, the new version, and the release URL.
|
|
40
|
+
2. Complete `store-progress` with `status: "completed"` and `output`: `"Update available: running <running>, latest <latest> — <releaseUrl>"`.
|
|
41
|
+
|
|
42
|
+
Do not attempt the upgrade yourself — this schedule only detects and reports. `weekly-harness-upgrade-check` is the separate routine that bumps pinned harness versions inside the repo; this schedule is about the agent-swarm release itself.
|
|
43
|
+
|
|
44
|
+
## Anti-patterns
|
|
45
|
+
|
|
46
|
+
- ❌ Cloning the repo or reading `package.json` from disk — this schedule must work on any install, including ones where the API pod has no repo checkout.
|
|
47
|
+
- ❌ String-comparing versions.
|
|
48
|
+
- ❌ Treating a missing `swarmVersion` as "no update available" — it's "unknown", say so explicitly.
|
|
49
|
+
- ❌ Retrying a failed GitHub API call in a loop.
|