@desplega.ai/agent-swarm 1.87.0 → 1.89.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 +5 -1
- package/openapi.json +53 -1
- package/package.json +6 -5
- package/plugin/skills/composio/SKILL.md +98 -0
- package/src/be/db.ts +374 -9
- package/src/be/migrations/080_skill_system_defaults.sql +8 -0
- package/src/be/migrations/081_metrics.sql +39 -0
- package/src/be/migrations/082_user_audit_fields.sql +120 -0
- package/src/be/modelsdev-cache.json +3825 -2417
- package/src/be/seed/registry.ts +3 -2
- package/src/be/seed-skills/index.ts +179 -0
- package/src/cli.tsx +51 -4
- package/src/commands/e2b-stack-wizard.tsx +394 -0
- package/src/commands/e2b.ts +1352 -53
- package/src/commands/onboard/dashboard-url.ts +29 -0
- package/src/commands/onboard/steps/post-dashboard.tsx +3 -1
- package/src/commands/onboard.tsx +3 -1
- package/src/commands/runner.ts +154 -22
- package/src/commands/x.ts +118 -0
- package/src/e2b/dispatch.ts +234 -18
- package/src/github/handlers.ts +40 -1
- package/src/heartbeat/heartbeat.ts +26 -5
- package/src/http/active-sessions.ts +32 -1
- package/src/http/auth.ts +36 -0
- package/src/http/core.ts +20 -16
- package/src/http/db-query.ts +20 -0
- package/src/http/index.ts +2 -0
- package/src/http/memory.ts +13 -1
- package/src/http/metrics.ts +447 -0
- package/src/http/operator-actor.ts +9 -0
- package/src/http/poll.ts +11 -1
- package/src/http/skills.ts +53 -0
- package/src/http/tasks.ts +4 -1
- package/src/http/webhooks.ts +75 -0
- package/src/http/workflows.ts +5 -1
- package/src/integrations/kapso/client.ts +82 -0
- package/src/memory/automatic-task-gate.ts +47 -0
- package/src/metrics/version.ts +26 -0
- package/src/prompts/base-prompt.ts +24 -1
- package/src/prompts/session-templates.ts +74 -0
- package/src/providers/claude-adapter.ts +19 -0
- package/src/providers/codex-adapter.ts +22 -0
- package/src/providers/ctx-mode-env.ts +10 -0
- package/src/providers/opencode-adapter.ts +72 -7
- package/src/server.ts +10 -1
- package/src/slack/blocks.ts +12 -4
- package/src/slack/watcher.ts +3 -3
- package/src/telemetry.ts +14 -1
- package/src/templates.d.ts +4 -0
- package/src/tests/base-prompt.test.ts +76 -0
- package/src/tests/budget-claim-gate.test.ts +26 -0
- package/src/tests/claude-adapter.test.ts +86 -1
- package/src/tests/codex-adapter.test.ts +89 -0
- package/src/tests/core-auth.test.ts +8 -1
- package/src/tests/e2b-dispatch.test.ts +603 -11
- package/src/tests/events-http.test.ts +6 -2
- package/src/tests/github-handlers-cancel-config.test.ts +262 -0
- package/src/tests/heartbeat.test.ts +84 -3
- package/src/tests/http-api-integration.test.ts +116 -1
- package/src/tests/kapso-client.test.ts +74 -1
- package/src/tests/kapso-inbound.test.ts +60 -2
- package/src/tests/metrics-http.test.ts +247 -0
- package/src/tests/opencode-adapter.test.ts +185 -30
- package/src/tests/prompt-template-session.test.ts +4 -2
- package/src/tests/runner-repo-autostash.test.ts +117 -0
- package/src/tests/runner-requester-profile.test.ts +25 -0
- package/src/tests/runner-skills-refresh.test.ts +1 -1
- package/src/tests/self-improvement.test.ts +89 -0
- package/src/tests/skill-update-scope.test.ts +88 -1
- package/src/tests/slack-blocks.test.ts +15 -0
- package/src/tests/swarm-x-tool.test.ts +90 -0
- package/src/tests/system-default-skills.test.ts +122 -0
- package/src/tests/telemetry-init.test.ts +86 -0
- package/src/tests/ui-logs-parser.test.ts +271 -0
- package/src/tests/user-token-rest-auth.test.ts +129 -0
- package/src/tests/workflow-async-v2.test.ts +23 -0
- package/src/tests/x-composio.test.ts +122 -0
- package/src/tools/create-metric.ts +191 -0
- package/src/tools/skills/skill-delete.ts +14 -0
- package/src/tools/skills/skill-update.ts +14 -0
- package/src/tools/store-progress.ts +19 -5
- package/src/tools/swarm-x.ts +116 -0
- package/src/tools/tool-config.ts +6 -0
- package/src/types.ts +121 -0
- package/src/utils/request-auth-context.ts +28 -0
- package/src/utils/skills-refresh.ts +2 -2
- package/src/workflows/engine.ts +24 -2
- package/src/workflows/executors/agent-task.ts +2 -0
- package/src/x/composio.ts +295 -0
- package/templates/skills/artifacts/config.json +1 -0
- package/templates/skills/attio-interaction/SKILL.md +279 -0
- package/templates/skills/attio-interaction/config.json +14 -0
- package/templates/skills/attio-interaction/content.md +272 -0
- package/templates/skills/kv-storage/config.json +1 -0
- package/templates/skills/pages/config.json +1 -0
- package/templates/skills/scheduled-task-resilience/config.json +1 -0
- package/templates/skills/swarm-scripts/SKILL.md +91 -0
- package/templates/skills/swarm-scripts/config.json +14 -0
- package/templates/skills/swarm-scripts/content.md +86 -0
- package/templates/skills/workflow-iterate/config.json +1 -0
- package/templates/skills/workflow-structured-output/config.json +1 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "skill",
|
|
3
|
+
"name": "attio-interaction",
|
|
4
|
+
"displayName": "Attio Interaction",
|
|
5
|
+
"slug": "attio-interaction",
|
|
6
|
+
"title": "Attio Interaction",
|
|
7
|
+
"description": "Generic Attio CRM REST API v2 recipes for querying records, upserting companies/people/deals, writing notes/tasks/comments, managing lists, and handling webhooks.",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"category": "skills",
|
|
10
|
+
"placeholders": ["ATTIO_API_KEY"],
|
|
11
|
+
"runAllSeedersCandidate": true,
|
|
12
|
+
"systemDefault": true,
|
|
13
|
+
"tags": ["attio", "crm", "sales"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
# Attio Interaction (Read + Write)
|
|
2
|
+
|
|
3
|
+
Use this skill to read and write your Attio CRM through the REST API v2. Every read or write is a direct API call; agent-swarm does not maintain a separate Attio sync.
|
|
4
|
+
|
|
5
|
+
## TL;DR
|
|
6
|
+
|
|
7
|
+
1. Resolve `ATTIO_API_KEY` from swarm config before making calls.
|
|
8
|
+
2. Base URL: `https://api.attio.com/v2/`
|
|
9
|
+
3. Use `Authorization: Bearer $ATTIO_API_KEY`, `Content-Type: application/json`, and `Accept: application/json`.
|
|
10
|
+
4. Prefer upsert over create for People, Companies, and Deals: `PUT /v2/objects/{slug}/records` with `matching_attribute`.
|
|
11
|
+
5. Rate limits: 100 reads/sec, 25 writes/sec. Pace write bursts to roughly 15-20/sec.
|
|
12
|
+
6. Attribute values are arrays, even for scalar values: `[{ "value": 42 }]`, never `42`.
|
|
13
|
+
|
|
14
|
+
## Authentication
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
ATTIO_API_KEY=$(get-config key="ATTIO_API_KEY" includeSecrets=true)
|
|
18
|
+
|
|
19
|
+
curl -sS "https://api.attio.com/v2/objects" \
|
|
20
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
21
|
+
-H "Accept: application/json"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If calls return `401`, re-fetch the key from config. If it still fails, notify the Lead; the key may need rotation. Do not retry silently.
|
|
25
|
+
|
|
26
|
+
## Core object slugs
|
|
27
|
+
|
|
28
|
+
| Object | API slug | Primary matching attribute |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Companies | `companies` | `domains` |
|
|
31
|
+
| People | `people` | `email_addresses` |
|
|
32
|
+
| Deals | `deals` | Usually no global dedupe key; link to company/person records |
|
|
33
|
+
|
|
34
|
+
Custom objects use their configured API slug. Discover them with `GET /v2/objects`.
|
|
35
|
+
|
|
36
|
+
## Common operations
|
|
37
|
+
|
|
38
|
+
### 1. Discover objects and slugs
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
curl -sS "https://api.attio.com/v2/objects" \
|
|
42
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
43
|
+
-H "Accept: application/json" \
|
|
44
|
+
| jq '.data[] | {slug: .api_slug, name: .title}'
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Query records with filters and pagination
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
curl -sS -X POST "https://api.attio.com/v2/objects/companies/records/query" \
|
|
51
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
52
|
+
-H "Content-Type: application/json" \
|
|
53
|
+
-d '{
|
|
54
|
+
"filter": {
|
|
55
|
+
"stage": { "$not_equal": "Won" }
|
|
56
|
+
},
|
|
57
|
+
"limit": 100,
|
|
58
|
+
"offset": 0
|
|
59
|
+
}' | jq '.data[] | {record_id: .id.record_id, name: .values.name[0].value}'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Paginate by increasing `offset` until `data` is empty. For no filter, omit the `filter` key.
|
|
63
|
+
|
|
64
|
+
### 3. Get a single record
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
curl -sS "https://api.attio.com/v2/objects/companies/records/{RECORD_ID}" \
|
|
68
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
69
|
+
-H "Accept: application/json" \
|
|
70
|
+
| jq '.data.values'
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### 4. Upsert a company by domain
|
|
74
|
+
|
|
75
|
+
Use `PUT` with `matching_attribute`. It creates if not found and updates if found, so it is safe to call repeatedly.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
curl -sS -X PUT "https://api.attio.com/v2/objects/companies/records" \
|
|
79
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
80
|
+
-H "Content-Type: application/json" \
|
|
81
|
+
-d '{
|
|
82
|
+
"data": {
|
|
83
|
+
"values": {
|
|
84
|
+
"name": [{ "value": "Acme Corp" }],
|
|
85
|
+
"domains": [{ "domain": "acme.com" }],
|
|
86
|
+
"employee_count": [{ "value": 150 }]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"matching_attribute": "domains"
|
|
90
|
+
}' | jq '{record_id: .data.id.record_id}'
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 5. Upsert a person by email
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
curl -sS -X PUT "https://api.attio.com/v2/objects/people/records" \
|
|
97
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
98
|
+
-H "Content-Type: application/json" \
|
|
99
|
+
-d '{
|
|
100
|
+
"data": {
|
|
101
|
+
"values": {
|
|
102
|
+
"name": [{ "first_name": "Jane", "last_name": "Doe" }],
|
|
103
|
+
"email_addresses": [{ "email_address": "jane@acme.com" }],
|
|
104
|
+
"job_title": [{ "value": "CTO" }]
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
"matching_attribute": "email_addresses"
|
|
108
|
+
}' | jq '{record_id: .data.id.record_id}'
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 6. Update specific attributes on an existing record
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
curl -sS -X PATCH "https://api.attio.com/v2/objects/companies/records/{RECORD_ID}" \
|
|
115
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
116
|
+
-H "Content-Type: application/json" \
|
|
117
|
+
-d '{
|
|
118
|
+
"data": {
|
|
119
|
+
"values": {
|
|
120
|
+
"icp_score": [{ "value": 85 }],
|
|
121
|
+
"icp_tier": [{ "value": "Tier 1" }]
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 7. Write a note to a record
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
curl -sS -X POST "https://api.attio.com/v2/notes" \
|
|
131
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
132
|
+
-H "Content-Type: application/json" \
|
|
133
|
+
-d '{
|
|
134
|
+
"data": {
|
|
135
|
+
"parent_object": "companies",
|
|
136
|
+
"parent_record_id": "{RECORD_ID}",
|
|
137
|
+
"title": "Enrichment - 2026-06-02",
|
|
138
|
+
"content": "Employee count: 150. Funding stage: Series A. Tech stack: Node.js, React."
|
|
139
|
+
}
|
|
140
|
+
}' | jq '{note_id: .data.id.note_id}'
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 8. Create a task linked to a record
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
curl -sS "https://api.attio.com/v2/workspace_members" \
|
|
147
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
148
|
+
-H "Accept: application/json" \
|
|
149
|
+
| jq '.data[] | {member_id: .id.workspace_member_id, name: .name, email: .email_address}'
|
|
150
|
+
|
|
151
|
+
curl -sS -X POST "https://api.attio.com/v2/tasks" \
|
|
152
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
153
|
+
-H "Content-Type: application/json" \
|
|
154
|
+
-d '{
|
|
155
|
+
"data": {
|
|
156
|
+
"content": "Follow up - no contact in 21 days",
|
|
157
|
+
"is_completed": false,
|
|
158
|
+
"assignees": [
|
|
159
|
+
{ "referenced_actor_type": "workspace-member", "referenced_actor_id": "{MEMBER_ID}" }
|
|
160
|
+
],
|
|
161
|
+
"linked_records": [
|
|
162
|
+
{ "target_object": "deals", "target_record_id": "{DEAL_RECORD_ID}" }
|
|
163
|
+
]
|
|
164
|
+
}
|
|
165
|
+
}' | jq '{task_id: .data.id.task_id}'
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 9. Post a comment on a record
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
curl -sS -X POST "https://api.attio.com/v2/comments" \
|
|
172
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
173
|
+
-H "Content-Type: application/json" \
|
|
174
|
+
-d '{
|
|
175
|
+
"data": {
|
|
176
|
+
"record": { "target_object": "companies", "target_record_id": "{RECORD_ID}" },
|
|
177
|
+
"content": [
|
|
178
|
+
{ "type": "text", "text": "Possible duplicate of acme-corp-old - please review and merge." }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
}' | jq '{comment_id: .data.id.comment_id}'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### 10. Query a list or pipeline view
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
curl -sS "https://api.attio.com/v2/lists" \
|
|
188
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
189
|
+
-H "Accept: application/json" \
|
|
190
|
+
| jq '.data[] | {list_id: .id.list_id, name: .title}'
|
|
191
|
+
|
|
192
|
+
curl -sS -X POST "https://api.attio.com/v2/lists/{LIST_ID}/entries/query" \
|
|
193
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
194
|
+
-H "Content-Type: application/json" \
|
|
195
|
+
-d '{ "limit": 100, "offset": 0 }' \
|
|
196
|
+
| jq '.data[] | {entry_id: .id.entry_id, record_id: .record_id}'
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 11. Global search across objects
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
curl -sS -X POST "https://api.attio.com/v2/records/search" \
|
|
203
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
204
|
+
-H "Content-Type: application/json" \
|
|
205
|
+
-d '{ "query": "acme", "limit": 10 }' \
|
|
206
|
+
| jq '.data[] | {object: .object_type, record_id: .id.record_id}'
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Webhooks
|
|
210
|
+
|
|
211
|
+
Attio delivers webhooks at least once. Payloads contain IDs only, so always re-fetch the full record via `GET` before acting.
|
|
212
|
+
|
|
213
|
+
Key event types:
|
|
214
|
+
|
|
215
|
+
- `record.created`, `record.updated`, `record.deleted`
|
|
216
|
+
- `list-entry.created`, `list-entry.updated`, `list-entry.deleted`
|
|
217
|
+
- `note.created`
|
|
218
|
+
- `task.created`, `task.completed`
|
|
219
|
+
|
|
220
|
+
Webhook timeout is 5 seconds. Respond `200` immediately and do async processing in a follow-up swarm task.
|
|
221
|
+
|
|
222
|
+
## Rate limits
|
|
223
|
+
|
|
224
|
+
| Operation | Hard limit | Safe working rate |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| Reads | 100 req/sec | ~80 req/sec |
|
|
227
|
+
| Writes | 25 req/sec | ~15-20 req/sec |
|
|
228
|
+
|
|
229
|
+
Add `sleep 0.05` between write calls in loops. Attio does not provide a native batch endpoint for these operations.
|
|
230
|
+
|
|
231
|
+
## Operational rules
|
|
232
|
+
|
|
233
|
+
- Upsert first. Use `PUT /records` with `matching_attribute` for create-or-update. `POST /records` can create duplicates.
|
|
234
|
+
- Re-fetch webhook records. Webhook payloads are event hints, not full source-of-truth records.
|
|
235
|
+
- Values are arrays. Every attribute value must be wrapped in an array.
|
|
236
|
+
- No merge endpoint. Attio has no API-level record merge; dedupe agents should flag duplicates as comments or tasks for human review.
|
|
237
|
+
- Check config first. Fetch `ATTIO_API_KEY` via `get-config includeSecrets=true`; never hardcode it.
|
|
238
|
+
|
|
239
|
+
## Error handling
|
|
240
|
+
|
|
241
|
+
| Status | Likely cause | Action |
|
|
242
|
+
|---|---|---|
|
|
243
|
+
| 401 | API key invalid or expired | Re-fetch from config. If still failing, notify Lead for rotation. |
|
|
244
|
+
| 403 | Key lacks permission | Check the Attio API key's workspace permissions. |
|
|
245
|
+
| 404 | Wrong object slug or record ID | Re-discover slugs with `GET /v2/objects`. |
|
|
246
|
+
| 400 | Malformed body | Ensure attribute values are wrapped in arrays. |
|
|
247
|
+
| 422 | Validation or conflict error | Read the `errors` array for field-level details. |
|
|
248
|
+
| 429 | Rate-limited | Back off and retry after `Retry-After` if provided. |
|
|
249
|
+
|
|
250
|
+
## Worked example: stale deal reactivation
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
ATTIO_API_KEY=$(get-config key="ATTIO_API_KEY" includeSecrets=true)
|
|
254
|
+
|
|
255
|
+
DEALS=$(curl -sS -X POST "https://api.attio.com/v2/objects/deals/records/query" \
|
|
256
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
257
|
+
-H "Content-Type: application/json" \
|
|
258
|
+
-d '{"filter": {"stage": {"$not_equal": "Won"}}, "limit": 200}')
|
|
259
|
+
|
|
260
|
+
echo "$DEALS" | jq -r '.data[] | .id.record_id' | while read -r RECORD_ID; do
|
|
261
|
+
RECORD=$(curl -sS "https://api.attio.com/v2/objects/deals/records/$RECORD_ID" \
|
|
262
|
+
-H "Authorization: Bearer $ATTIO_API_KEY" \
|
|
263
|
+
-H "Accept: application/json")
|
|
264
|
+
# Compute staleness from the relevant date attribute. If stale, create a task.
|
|
265
|
+
sleep 0.05
|
|
266
|
+
done
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Related references
|
|
270
|
+
|
|
271
|
+
- Official Attio REST API docs: https://developers.attio.com/reference
|
|
272
|
+
- Official Attio MCP overview: https://docs.attio.com/mcp/overview
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: swarm-scripts
|
|
3
|
+
description: Use swarm scripts for bulk SDK calls, repetitive fan-out, and context-efficient data processing.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Swarm Scripts
|
|
7
|
+
|
|
8
|
+
Use swarm scripts when direct tool calls would create repetitive work, flood the context window, or require deterministic data processing across many records. Scripts run out-of-process with a typed Swarm SDK and return only the final result to your context.
|
|
9
|
+
|
|
10
|
+
## Decision Rubric
|
|
11
|
+
|
|
12
|
+
| Situation | Use |
|
|
13
|
+
|---|---|
|
|
14
|
+
| 1-10 SDK calls, result fits in context | Direct tool call |
|
|
15
|
+
| 10+ items or bulk fan-out SDK operations | Script |
|
|
16
|
+
| Heavy fetch, parse, transform, or aggregation | Script or context-mode |
|
|
17
|
+
| Single expensive web fetch | `ctx_fetch_and_index` |
|
|
18
|
+
| Multi-agent parallel work | Workflow |
|
|
19
|
+
| Logic needed across sessions or agents | Named script |
|
|
20
|
+
|
|
21
|
+
## Loading Script Tools
|
|
22
|
+
|
|
23
|
+
The script tools are deferred. Before authoring or running a script, load the relevant tools with ToolSearch:
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
script-query-types
|
|
27
|
+
script-upsert
|
|
28
|
+
script-run
|
|
29
|
+
script-search
|
|
30
|
+
script-delete
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Use `script-query-types` before non-trivial work so the script matches the live `swarm-sdk.d.ts` and stdlib signatures.
|
|
34
|
+
|
|
35
|
+
## Inline Script Pattern
|
|
36
|
+
|
|
37
|
+
Use `script-run` with inline source for one-off work:
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
export default async function main(args: { status: string; limit: number }, ctx) {
|
|
41
|
+
const { swarm, logger } = ctx;
|
|
42
|
+
const result = await swarm.task_list({ status: args.status, limit: args.limit });
|
|
43
|
+
logger.info(`Fetched ${result.tasks.length} tasks`);
|
|
44
|
+
return {
|
|
45
|
+
total: result.tasks.length,
|
|
46
|
+
tasks: result.tasks.map((task) => ({
|
|
47
|
+
id: task.id,
|
|
48
|
+
status: task.status,
|
|
49
|
+
title: task.task.slice(0, 120),
|
|
50
|
+
})),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Keep logs useful but compact. The value returned from `main` is what comes back to your context.
|
|
56
|
+
|
|
57
|
+
## Named Script Pattern
|
|
58
|
+
|
|
59
|
+
Use `script-upsert` when the same logic is likely to be reused by another task or agent. Give the script a searchable name, a concrete description, and an intent that explains when to choose it.
|
|
60
|
+
|
|
61
|
+
Good named scripts:
|
|
62
|
+
|
|
63
|
+
- Aggregate failures by agent, schedule, or error family.
|
|
64
|
+
- Fetch and normalize a third-party API response.
|
|
65
|
+
- Fan out over many swarm tasks, memories, repos, or schedules.
|
|
66
|
+
- Convert noisy JSON or HTML into a compact summary.
|
|
67
|
+
|
|
68
|
+
## SDK And Context Gotchas
|
|
69
|
+
|
|
70
|
+
- `agentId` is propagated to scripts via the `X-Agent-ID` header, so SDK calls run as the invoking agent.
|
|
71
|
+
- `taskId` is not ambient. If a script needs to call `ctx.swarm.task_storeProgress`, pass `taskId` explicitly in `args`.
|
|
72
|
+
- Scripts invoked from a workflow script node may run with a workflow identity rather than a human or worker agent identity.
|
|
73
|
+
- Return compact structured data. Do not return raw logs, full HTML, huge JSON arrays, or large file contents.
|
|
74
|
+
- For a single large web fetch, prefer context-mode `ctx_fetch_and_index`; for repeated fetch/parse/aggregate work, prefer a script.
|
|
75
|
+
|
|
76
|
+
## Progress Updates From Scripts
|
|
77
|
+
|
|
78
|
+
Thread task identity explicitly:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
export default async function main(args: { taskId: string; items: string[] }, ctx) {
|
|
82
|
+
const { swarm } = ctx;
|
|
83
|
+
await swarm.task_storeProgress({
|
|
84
|
+
taskId: args.taskId,
|
|
85
|
+
progress: `Processing ${args.items.length} items with a script`,
|
|
86
|
+
});
|
|
87
|
+
return { processed: args.items.length };
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Do not assume the runtime can infer the current task.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "skill",
|
|
3
|
+
"name": "swarm-scripts",
|
|
4
|
+
"displayName": "Swarm Scripts",
|
|
5
|
+
"slug": "swarm-scripts",
|
|
6
|
+
"title": "Swarm Scripts",
|
|
7
|
+
"description": "Use swarm scripts for bulk SDK calls, repetitive fan-out, and context-efficient data processing.",
|
|
8
|
+
"version": "1.0.0",
|
|
9
|
+
"category": "skills",
|
|
10
|
+
"placeholders": [],
|
|
11
|
+
"runAllSeedersCandidate": true,
|
|
12
|
+
"systemDefault": true,
|
|
13
|
+
"tags": ["scripts", "automation", "context"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Swarm Scripts
|
|
2
|
+
|
|
3
|
+
Use swarm scripts when direct tool calls would create repetitive work, flood the context window, or require deterministic data processing across many records. Scripts run out-of-process with a typed Swarm SDK and return only the final result to your context.
|
|
4
|
+
|
|
5
|
+
## Decision Rubric
|
|
6
|
+
|
|
7
|
+
| Situation | Use |
|
|
8
|
+
|---|---|
|
|
9
|
+
| 1-10 SDK calls, result fits in context | Direct tool call |
|
|
10
|
+
| 10+ items or bulk fan-out SDK operations | Script |
|
|
11
|
+
| Heavy fetch, parse, transform, or aggregation | Script or context-mode |
|
|
12
|
+
| Single expensive web fetch | `ctx_fetch_and_index` |
|
|
13
|
+
| Multi-agent parallel work | Workflow |
|
|
14
|
+
| Logic needed across sessions or agents | Named script |
|
|
15
|
+
|
|
16
|
+
## Loading Script Tools
|
|
17
|
+
|
|
18
|
+
The script tools are deferred. Before authoring or running a script, load the relevant tools with ToolSearch:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
script-query-types
|
|
22
|
+
script-upsert
|
|
23
|
+
script-run
|
|
24
|
+
script-search
|
|
25
|
+
script-delete
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `script-query-types` before non-trivial work so the script matches the live `swarm-sdk.d.ts` and stdlib signatures.
|
|
29
|
+
|
|
30
|
+
## Inline Script Pattern
|
|
31
|
+
|
|
32
|
+
Use `script-run` with inline source for one-off work:
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
export default async function main(args: { status: string; limit: number }, ctx) {
|
|
36
|
+
const { swarm, logger } = ctx;
|
|
37
|
+
const result = await swarm.task_list({ status: args.status, limit: args.limit });
|
|
38
|
+
logger.info(`Fetched ${result.tasks.length} tasks`);
|
|
39
|
+
return {
|
|
40
|
+
total: result.tasks.length,
|
|
41
|
+
tasks: result.tasks.map((task) => ({
|
|
42
|
+
id: task.id,
|
|
43
|
+
status: task.status,
|
|
44
|
+
title: task.task.slice(0, 120),
|
|
45
|
+
})),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Keep logs useful but compact. The value returned from `main` is what comes back to your context.
|
|
51
|
+
|
|
52
|
+
## Named Script Pattern
|
|
53
|
+
|
|
54
|
+
Use `script-upsert` when the same logic is likely to be reused by another task or agent. Give the script a searchable name, a concrete description, and an intent that explains when to choose it.
|
|
55
|
+
|
|
56
|
+
Good named scripts:
|
|
57
|
+
|
|
58
|
+
- Aggregate failures by agent, schedule, or error family.
|
|
59
|
+
- Fetch and normalize a third-party API response.
|
|
60
|
+
- Fan out over many swarm tasks, memories, repos, or schedules.
|
|
61
|
+
- Convert noisy JSON or HTML into a compact summary.
|
|
62
|
+
|
|
63
|
+
## SDK And Context Gotchas
|
|
64
|
+
|
|
65
|
+
- `agentId` is propagated to scripts via the `X-Agent-ID` header, so SDK calls run as the invoking agent.
|
|
66
|
+
- `taskId` is not ambient. If a script needs to call `ctx.swarm.task_storeProgress`, pass `taskId` explicitly in `args`.
|
|
67
|
+
- Scripts invoked from a workflow script node may run with a workflow identity rather than a human or worker agent identity.
|
|
68
|
+
- Return compact structured data. Do not return raw logs, full HTML, huge JSON arrays, or large file contents.
|
|
69
|
+
- For a single large web fetch, prefer context-mode `ctx_fetch_and_index`; for repeated fetch/parse/aggregate work, prefer a script.
|
|
70
|
+
|
|
71
|
+
## Progress Updates From Scripts
|
|
72
|
+
|
|
73
|
+
Thread task identity explicitly:
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
export default async function main(args: { taskId: string; items: string[] }, ctx) {
|
|
77
|
+
const { swarm } = ctx;
|
|
78
|
+
await swarm.task_storeProgress({
|
|
79
|
+
taskId: args.taskId,
|
|
80
|
+
progress: `Processing ${args.items.length} items with a script`,
|
|
81
|
+
});
|
|
82
|
+
return { processed: args.items.length };
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Do not assume the runtime can infer the current task.
|