opencode-swarm-plugin 0.2.0 → 0.4.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/.beads/issues.jsonl +115 -5
- package/README.md +38 -1
- package/bun.lock +23 -0
- package/dist/index.js +9697 -2
- package/dist/plugin.js +9695 -2
- package/package.json +2 -3
- package/src/agent-mail.ts +134 -0
- package/src/index.ts +2 -0
- package/src/rate-limiter.integration.test.ts +466 -0
- package/src/rate-limiter.ts +656 -0
- package/src/swarm.integration.test.ts +126 -0
- package/src/swarm.ts +278 -0
- package/src/tool-availability.ts +3 -2
- package/Dockerfile +0 -30
- package/docker/agent-mail/Dockerfile +0 -23
- package/docker/agent-mail/__pycache__/server.cpython-314.pyc +0 -0
- package/docker/agent-mail/requirements.txt +0 -3
- package/docker/agent-mail/server.py +0 -879
- package/docker-compose.yml +0 -45
- package/scripts/docker-entrypoint.sh +0 -54
|
@@ -16,6 +16,8 @@ import {
|
|
|
16
16
|
swarm_complete,
|
|
17
17
|
swarm_subtask_prompt,
|
|
18
18
|
swarm_evaluation_prompt,
|
|
19
|
+
formatSubtaskPromptV2,
|
|
20
|
+
SUBTASK_PROMPT_V2,
|
|
19
21
|
} from "./swarm";
|
|
20
22
|
import { mcpCall, setState, clearState, AGENT_MAIL_URL } from "./agent-mail";
|
|
21
23
|
|
|
@@ -952,3 +954,127 @@ describe("Graceful Degradation", () => {
|
|
|
952
954
|
expect(result).toContain("Report progress");
|
|
953
955
|
});
|
|
954
956
|
});
|
|
957
|
+
|
|
958
|
+
// ============================================================================
|
|
959
|
+
// Coordinator-Centric Swarm Tools (V2)
|
|
960
|
+
// ============================================================================
|
|
961
|
+
|
|
962
|
+
describe("Coordinator-Centric Swarm Tools", () => {
|
|
963
|
+
describe("formatSubtaskPromptV2", () => {
|
|
964
|
+
it("generates correct prompt with all fields", () => {
|
|
965
|
+
const result = formatSubtaskPromptV2({
|
|
966
|
+
subtask_title: "Add OAuth provider",
|
|
967
|
+
subtask_description: "Configure Google OAuth in the auth config",
|
|
968
|
+
files: ["src/auth/google.ts", "src/auth/config.ts"],
|
|
969
|
+
shared_context: "We are using NextAuth.js v5",
|
|
970
|
+
});
|
|
971
|
+
|
|
972
|
+
// Check title is included
|
|
973
|
+
expect(result).toContain("Add OAuth provider");
|
|
974
|
+
|
|
975
|
+
// Check description is included
|
|
976
|
+
expect(result).toContain("Configure Google OAuth in the auth config");
|
|
977
|
+
|
|
978
|
+
// Check files are formatted as list
|
|
979
|
+
expect(result).toContain("- `src/auth/google.ts`");
|
|
980
|
+
expect(result).toContain("- `src/auth/config.ts`");
|
|
981
|
+
|
|
982
|
+
// Check shared context is included
|
|
983
|
+
expect(result).toContain("We are using NextAuth.js v5");
|
|
984
|
+
|
|
985
|
+
// Check expected sections exist
|
|
986
|
+
expect(result).toContain("## Your Task");
|
|
987
|
+
expect(result).toContain("## Files to Modify");
|
|
988
|
+
expect(result).toContain("## Context");
|
|
989
|
+
expect(result).toContain("## Instructions");
|
|
990
|
+
expect(result).toContain("## When Complete");
|
|
991
|
+
});
|
|
992
|
+
|
|
993
|
+
it("handles missing optional fields", () => {
|
|
994
|
+
const result = formatSubtaskPromptV2({
|
|
995
|
+
subtask_title: "Simple task",
|
|
996
|
+
subtask_description: "",
|
|
997
|
+
files: [],
|
|
998
|
+
});
|
|
999
|
+
|
|
1000
|
+
// Check title is included
|
|
1001
|
+
expect(result).toContain("Simple task");
|
|
1002
|
+
|
|
1003
|
+
// Check fallback for empty description
|
|
1004
|
+
expect(result).toContain("(see title)");
|
|
1005
|
+
|
|
1006
|
+
// Check fallback for empty files
|
|
1007
|
+
expect(result).toContain(
|
|
1008
|
+
"(no specific files assigned - use your judgment)",
|
|
1009
|
+
);
|
|
1010
|
+
|
|
1011
|
+
// Check fallback for missing context
|
|
1012
|
+
expect(result).toContain("(none provided)");
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
it("handles files with special characters", () => {
|
|
1016
|
+
const result = formatSubtaskPromptV2({
|
|
1017
|
+
subtask_title: "Handle paths",
|
|
1018
|
+
subtask_description: "Test file paths",
|
|
1019
|
+
files: [
|
|
1020
|
+
"src/components/[slug]/page.tsx",
|
|
1021
|
+
"src/api/users/[id]/route.ts",
|
|
1022
|
+
],
|
|
1023
|
+
});
|
|
1024
|
+
|
|
1025
|
+
expect(result).toContain("- `src/components/[slug]/page.tsx`");
|
|
1026
|
+
expect(result).toContain("- `src/api/users/[id]/route.ts`");
|
|
1027
|
+
});
|
|
1028
|
+
});
|
|
1029
|
+
|
|
1030
|
+
describe("SUBTASK_PROMPT_V2", () => {
|
|
1031
|
+
it("contains expected sections", () => {
|
|
1032
|
+
// Check all main sections are present in the template
|
|
1033
|
+
expect(SUBTASK_PROMPT_V2).toContain("## Your Task");
|
|
1034
|
+
expect(SUBTASK_PROMPT_V2).toContain("{subtask_title}");
|
|
1035
|
+
expect(SUBTASK_PROMPT_V2).toContain("{subtask_description}");
|
|
1036
|
+
|
|
1037
|
+
expect(SUBTASK_PROMPT_V2).toContain("## Files to Modify");
|
|
1038
|
+
expect(SUBTASK_PROMPT_V2).toContain("{file_list}");
|
|
1039
|
+
|
|
1040
|
+
expect(SUBTASK_PROMPT_V2).toContain("## Context");
|
|
1041
|
+
expect(SUBTASK_PROMPT_V2).toContain("{shared_context}");
|
|
1042
|
+
|
|
1043
|
+
expect(SUBTASK_PROMPT_V2).toContain("## Instructions");
|
|
1044
|
+
expect(SUBTASK_PROMPT_V2).toContain("Read first");
|
|
1045
|
+
expect(SUBTASK_PROMPT_V2).toContain("Plan your approach");
|
|
1046
|
+
expect(SUBTASK_PROMPT_V2).toContain("Make the changes");
|
|
1047
|
+
expect(SUBTASK_PROMPT_V2).toContain("Verify");
|
|
1048
|
+
|
|
1049
|
+
expect(SUBTASK_PROMPT_V2).toContain("## When Complete");
|
|
1050
|
+
expect(SUBTASK_PROMPT_V2).toContain('"success"');
|
|
1051
|
+
expect(SUBTASK_PROMPT_V2).toContain('"summary"');
|
|
1052
|
+
expect(SUBTASK_PROMPT_V2).toContain('"files_modified"');
|
|
1053
|
+
});
|
|
1054
|
+
|
|
1055
|
+
it("does NOT contain Agent Mail instructions", () => {
|
|
1056
|
+
// V2 prompt is for coordinator-centric model where subagents don't use Agent Mail
|
|
1057
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("Agent Mail");
|
|
1058
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("agentmail_");
|
|
1059
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("agent_name");
|
|
1060
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("send_message");
|
|
1061
|
+
});
|
|
1062
|
+
|
|
1063
|
+
it("does NOT contain beads instructions", () => {
|
|
1064
|
+
// V2 prompt is for coordinator-centric model where subagents don't manage beads
|
|
1065
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("bead_id");
|
|
1066
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("epic_id");
|
|
1067
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("bd update");
|
|
1068
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("bd close");
|
|
1069
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("swarm_progress");
|
|
1070
|
+
expect(SUBTASK_PROMPT_V2).not.toContain("swarm_complete");
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
it("expects structured JSON response from subagent", () => {
|
|
1074
|
+
// The prompt should instruct agents to return structured JSON
|
|
1075
|
+
expect(SUBTASK_PROMPT_V2).toContain("```json");
|
|
1076
|
+
expect(SUBTASK_PROMPT_V2).toContain('"success"');
|
|
1077
|
+
expect(SUBTASK_PROMPT_V2).toContain('"blocker"');
|
|
1078
|
+
});
|
|
1079
|
+
});
|
|
1080
|
+
});
|
package/src/swarm.ts
CHANGED
|
@@ -359,6 +359,92 @@ Before writing code:
|
|
|
359
359
|
|
|
360
360
|
Begin work on your subtask now.`;
|
|
361
361
|
|
|
362
|
+
/**
|
|
363
|
+
* Simplified subtask prompt for Task subagents (V2 - coordinator-centric)
|
|
364
|
+
*
|
|
365
|
+
* This prompt is designed for agents that DON'T have access to Agent Mail or beads tools.
|
|
366
|
+
* The coordinator handles all coordination - subagents just do the work and return results.
|
|
367
|
+
*
|
|
368
|
+
* Key differences from V1:
|
|
369
|
+
* - No Agent Mail instructions (subagents can't use it)
|
|
370
|
+
* - No beads instructions (subagents can't use it)
|
|
371
|
+
* - Expects structured JSON response for coordinator to process
|
|
372
|
+
*/
|
|
373
|
+
export const SUBTASK_PROMPT_V2 = `You are working on a subtask as part of a larger project.
|
|
374
|
+
|
|
375
|
+
## Your Task
|
|
376
|
+
**Title**: {subtask_title}
|
|
377
|
+
|
|
378
|
+
{subtask_description}
|
|
379
|
+
|
|
380
|
+
## Files to Modify
|
|
381
|
+
{file_list}
|
|
382
|
+
|
|
383
|
+
**IMPORTANT**: Only modify the files listed above. Do not create new files unless absolutely necessary for the task.
|
|
384
|
+
|
|
385
|
+
## Context
|
|
386
|
+
{shared_context}
|
|
387
|
+
|
|
388
|
+
## Instructions
|
|
389
|
+
|
|
390
|
+
1. **Read first** - Understand the current state of the files before making changes
|
|
391
|
+
2. **Plan your approach** - Think through what changes are needed
|
|
392
|
+
3. **Make the changes** - Implement the required functionality
|
|
393
|
+
4. **Verify** - Check that your changes work (run tests/typecheck if applicable)
|
|
394
|
+
|
|
395
|
+
## When Complete
|
|
396
|
+
|
|
397
|
+
After finishing your work, provide a summary in this format:
|
|
398
|
+
|
|
399
|
+
\`\`\`json
|
|
400
|
+
{
|
|
401
|
+
"success": true,
|
|
402
|
+
"summary": "Brief description of what you accomplished",
|
|
403
|
+
"files_modified": ["list", "of", "files", "you", "changed"],
|
|
404
|
+
"files_created": ["any", "new", "files"],
|
|
405
|
+
"issues_found": ["any problems or concerns discovered"],
|
|
406
|
+
"tests_passed": true,
|
|
407
|
+
"notes": "Any additional context for the coordinator"
|
|
408
|
+
}
|
|
409
|
+
\`\`\`
|
|
410
|
+
|
|
411
|
+
If you encounter a blocker you cannot resolve, return:
|
|
412
|
+
|
|
413
|
+
\`\`\`json
|
|
414
|
+
{
|
|
415
|
+
"success": false,
|
|
416
|
+
"summary": "What you attempted",
|
|
417
|
+
"blocker": "Description of what's blocking you",
|
|
418
|
+
"files_modified": [],
|
|
419
|
+
"suggestions": ["possible", "solutions"]
|
|
420
|
+
}
|
|
421
|
+
\`\`\`
|
|
422
|
+
|
|
423
|
+
Begin work now.`;
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Format the V2 subtask prompt for a specific agent
|
|
427
|
+
*/
|
|
428
|
+
export function formatSubtaskPromptV2(params: {
|
|
429
|
+
subtask_title: string;
|
|
430
|
+
subtask_description: string;
|
|
431
|
+
files: string[];
|
|
432
|
+
shared_context?: string;
|
|
433
|
+
}): string {
|
|
434
|
+
const fileList =
|
|
435
|
+
params.files.length > 0
|
|
436
|
+
? params.files.map((f) => `- \`${f}\``).join("\n")
|
|
437
|
+
: "(no specific files assigned - use your judgment)";
|
|
438
|
+
|
|
439
|
+
return SUBTASK_PROMPT_V2.replace("{subtask_title}", params.subtask_title)
|
|
440
|
+
.replace(
|
|
441
|
+
"{subtask_description}",
|
|
442
|
+
params.subtask_description || "(see title)",
|
|
443
|
+
)
|
|
444
|
+
.replace("{file_list}", fileList)
|
|
445
|
+
.replace("{shared_context}", params.shared_context || "(none provided)");
|
|
446
|
+
}
|
|
447
|
+
|
|
362
448
|
/**
|
|
363
449
|
* Prompt for self-evaluation before completing a subtask.
|
|
364
450
|
*
|
|
@@ -1418,6 +1504,196 @@ export const swarm_subtask_prompt = tool({
|
|
|
1418
1504
|
},
|
|
1419
1505
|
});
|
|
1420
1506
|
|
|
1507
|
+
/**
|
|
1508
|
+
* Prepare a subtask for spawning with Task tool (V2 prompt)
|
|
1509
|
+
*
|
|
1510
|
+
* This is a simplified tool for coordinators that generates a prompt using
|
|
1511
|
+
* the V2 template (no Agent Mail/beads instructions - coordinator handles coordination).
|
|
1512
|
+
* Returns JSON that can be directly used with Task tool.
|
|
1513
|
+
*/
|
|
1514
|
+
export const swarm_spawn_subtask = tool({
|
|
1515
|
+
description:
|
|
1516
|
+
"Prepare a subtask for spawning with Task tool. Returns prompt and metadata for coordinator to use.",
|
|
1517
|
+
args: {
|
|
1518
|
+
bead_id: tool.schema.string().describe("Subtask bead ID"),
|
|
1519
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
1520
|
+
subtask_description: tool.schema
|
|
1521
|
+
.string()
|
|
1522
|
+
.optional()
|
|
1523
|
+
.describe("Detailed subtask instructions"),
|
|
1524
|
+
files: tool.schema
|
|
1525
|
+
.array(tool.schema.string())
|
|
1526
|
+
.describe("Files assigned to this subtask"),
|
|
1527
|
+
shared_context: tool.schema
|
|
1528
|
+
.string()
|
|
1529
|
+
.optional()
|
|
1530
|
+
.describe("Context shared across all agents"),
|
|
1531
|
+
},
|
|
1532
|
+
async execute(args) {
|
|
1533
|
+
const prompt = formatSubtaskPromptV2({
|
|
1534
|
+
subtask_title: args.subtask_title,
|
|
1535
|
+
subtask_description: args.subtask_description || "",
|
|
1536
|
+
files: args.files,
|
|
1537
|
+
shared_context: args.shared_context,
|
|
1538
|
+
});
|
|
1539
|
+
|
|
1540
|
+
return JSON.stringify(
|
|
1541
|
+
{
|
|
1542
|
+
prompt,
|
|
1543
|
+
bead_id: args.bead_id,
|
|
1544
|
+
files: args.files,
|
|
1545
|
+
},
|
|
1546
|
+
null,
|
|
1547
|
+
2,
|
|
1548
|
+
);
|
|
1549
|
+
},
|
|
1550
|
+
});
|
|
1551
|
+
|
|
1552
|
+
/**
|
|
1553
|
+
* Schema for task agent result
|
|
1554
|
+
*/
|
|
1555
|
+
const TaskResultSchema = z.object({
|
|
1556
|
+
success: z.boolean(),
|
|
1557
|
+
summary: z.string(),
|
|
1558
|
+
files_modified: z.array(z.string()).optional().default([]),
|
|
1559
|
+
files_created: z.array(z.string()).optional().default([]),
|
|
1560
|
+
issues_found: z.array(z.string()).optional().default([]),
|
|
1561
|
+
tests_passed: z.boolean().optional(),
|
|
1562
|
+
notes: z.string().optional(),
|
|
1563
|
+
blocker: z.string().optional(),
|
|
1564
|
+
suggestions: z.array(z.string()).optional(),
|
|
1565
|
+
});
|
|
1566
|
+
|
|
1567
|
+
type TaskResult = z.infer<typeof TaskResultSchema>;
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* Handle subtask completion from a Task agent
|
|
1571
|
+
*
|
|
1572
|
+
* This tool is for coordinators to process the result after a Task subagent
|
|
1573
|
+
* returns. It parses the JSON result, closes the bead on success, and
|
|
1574
|
+
* creates new beads for any issues discovered.
|
|
1575
|
+
*
|
|
1576
|
+
* @example
|
|
1577
|
+
* // Task agent returns JSON:
|
|
1578
|
+
* // { "success": true, "summary": "Added auth", "files_modified": ["src/auth.ts"], "issues_found": ["Missing tests"] }
|
|
1579
|
+
* //
|
|
1580
|
+
* // Coordinator calls:
|
|
1581
|
+
* swarm_complete_subtask(bead_id="bd-123.1", task_result=<agent_response>)
|
|
1582
|
+
*/
|
|
1583
|
+
export const swarm_complete_subtask = tool({
|
|
1584
|
+
description:
|
|
1585
|
+
"Handle subtask completion after Task agent returns. Parses result JSON, closes bead on success, creates new beads for issues found.",
|
|
1586
|
+
args: {
|
|
1587
|
+
bead_id: z.string().describe("Subtask bead ID to close"),
|
|
1588
|
+
task_result: z
|
|
1589
|
+
.string()
|
|
1590
|
+
.describe("JSON result from the Task agent (TaskResult schema)"),
|
|
1591
|
+
files_touched: z
|
|
1592
|
+
.array(z.string())
|
|
1593
|
+
.optional()
|
|
1594
|
+
.describe(
|
|
1595
|
+
"Override files touched (uses task_result.files_modified if not provided)",
|
|
1596
|
+
),
|
|
1597
|
+
},
|
|
1598
|
+
async execute(args) {
|
|
1599
|
+
// Parse the task result JSON
|
|
1600
|
+
let result: TaskResult;
|
|
1601
|
+
try {
|
|
1602
|
+
const parsed = JSON.parse(args.task_result);
|
|
1603
|
+
result = TaskResultSchema.parse(parsed);
|
|
1604
|
+
} catch (error) {
|
|
1605
|
+
// Handle parse errors gracefully
|
|
1606
|
+
const errorMessage =
|
|
1607
|
+
error instanceof SyntaxError
|
|
1608
|
+
? `Invalid JSON: ${error.message}`
|
|
1609
|
+
: error instanceof z.ZodError
|
|
1610
|
+
? `Schema validation failed: ${error.issues.map((i) => i.message).join(", ")}`
|
|
1611
|
+
: String(error);
|
|
1612
|
+
|
|
1613
|
+
return JSON.stringify(
|
|
1614
|
+
{
|
|
1615
|
+
success: false,
|
|
1616
|
+
error: "Failed to parse task result",
|
|
1617
|
+
details: errorMessage,
|
|
1618
|
+
hint: "Task agent should return JSON matching TaskResult schema: { success, summary, files_modified?, issues_found?, ... }",
|
|
1619
|
+
},
|
|
1620
|
+
null,
|
|
1621
|
+
2,
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const filesTouched = args.files_touched ?? [
|
|
1626
|
+
...result.files_modified,
|
|
1627
|
+
...result.files_created,
|
|
1628
|
+
];
|
|
1629
|
+
const issuesCreated: Array<{ title: string; id?: string }> = [];
|
|
1630
|
+
|
|
1631
|
+
// If task failed, don't close the bead - return info for coordinator to handle
|
|
1632
|
+
if (!result.success) {
|
|
1633
|
+
return JSON.stringify(
|
|
1634
|
+
{
|
|
1635
|
+
success: false,
|
|
1636
|
+
bead_id: args.bead_id,
|
|
1637
|
+
task_failed: true,
|
|
1638
|
+
summary: result.summary,
|
|
1639
|
+
blocker: result.blocker,
|
|
1640
|
+
suggestions: result.suggestions,
|
|
1641
|
+
files_touched: filesTouched,
|
|
1642
|
+
action_needed:
|
|
1643
|
+
"Task failed - review blocker and decide whether to retry or close as failed",
|
|
1644
|
+
},
|
|
1645
|
+
null,
|
|
1646
|
+
2,
|
|
1647
|
+
);
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// Task succeeded - close the bead
|
|
1651
|
+
const closeReason = result.summary.slice(0, 200); // Truncate for safety
|
|
1652
|
+
await Bun.$`bd close ${args.bead_id} -r "${closeReason}"`.quiet().nothrow();
|
|
1653
|
+
|
|
1654
|
+
// Create new beads for each issue found
|
|
1655
|
+
if (result.issues_found.length > 0) {
|
|
1656
|
+
for (const issue of result.issues_found) {
|
|
1657
|
+
const issueTitle = issue.slice(0, 100); // Truncate long titles
|
|
1658
|
+
const createResult = await Bun.$`bd create "${issueTitle}" -t bug`
|
|
1659
|
+
.quiet()
|
|
1660
|
+
.nothrow();
|
|
1661
|
+
|
|
1662
|
+
if (createResult.exitCode === 0) {
|
|
1663
|
+
// Try to parse the bead ID from output
|
|
1664
|
+
const output = createResult.stdout.toString();
|
|
1665
|
+
const idMatch = output.match(/bd-[a-z0-9]+/);
|
|
1666
|
+
issuesCreated.push({
|
|
1667
|
+
title: issueTitle,
|
|
1668
|
+
id: idMatch?.[0],
|
|
1669
|
+
});
|
|
1670
|
+
} else {
|
|
1671
|
+
issuesCreated.push({
|
|
1672
|
+
title: issueTitle,
|
|
1673
|
+
id: undefined, // Failed to create
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
return JSON.stringify(
|
|
1680
|
+
{
|
|
1681
|
+
success: true,
|
|
1682
|
+
bead_id: args.bead_id,
|
|
1683
|
+
bead_closed: true,
|
|
1684
|
+
summary: result.summary,
|
|
1685
|
+
files_touched: filesTouched,
|
|
1686
|
+
tests_passed: result.tests_passed,
|
|
1687
|
+
notes: result.notes,
|
|
1688
|
+
issues_created: issuesCreated.length > 0 ? issuesCreated : undefined,
|
|
1689
|
+
issues_count: issuesCreated.length,
|
|
1690
|
+
},
|
|
1691
|
+
null,
|
|
1692
|
+
2,
|
|
1693
|
+
);
|
|
1694
|
+
},
|
|
1695
|
+
});
|
|
1696
|
+
|
|
1421
1697
|
/**
|
|
1422
1698
|
* Generate self-evaluation prompt
|
|
1423
1699
|
*/
|
|
@@ -1560,5 +1836,7 @@ export const swarmTools = {
|
|
|
1560
1836
|
swarm_complete: swarm_complete,
|
|
1561
1837
|
swarm_record_outcome: swarm_record_outcome,
|
|
1562
1838
|
swarm_subtask_prompt: swarm_subtask_prompt,
|
|
1839
|
+
swarm_spawn_subtask: swarm_spawn_subtask,
|
|
1840
|
+
swarm_complete_subtask: swarm_complete_subtask,
|
|
1563
1841
|
swarm_evaluation_prompt: swarm_evaluation_prompt,
|
|
1564
1842
|
};
|
package/src/tool-availability.ts
CHANGED
|
@@ -55,14 +55,15 @@ async function commandExists(cmd: string): Promise<boolean> {
|
|
|
55
55
|
*/
|
|
56
56
|
async function urlReachable(
|
|
57
57
|
url: string,
|
|
58
|
-
timeoutMs: number =
|
|
58
|
+
timeoutMs: number = 2000,
|
|
59
59
|
): Promise<boolean> {
|
|
60
60
|
try {
|
|
61
61
|
const controller = new AbortController();
|
|
62
62
|
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
63
63
|
|
|
64
|
+
// Use GET instead of HEAD - some servers don't support HEAD
|
|
64
65
|
const response = await fetch(url, {
|
|
65
|
-
method: "
|
|
66
|
+
method: "GET",
|
|
66
67
|
signal: controller.signal,
|
|
67
68
|
});
|
|
68
69
|
|
package/Dockerfile
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
# Test runner container for opencode-swarm-plugin integration tests
|
|
2
|
-
FROM oven/bun:latest
|
|
3
|
-
|
|
4
|
-
# Install git (required for beads) and curl (for healthchecks)
|
|
5
|
-
RUN apt-get update && apt-get install -y \
|
|
6
|
-
git \
|
|
7
|
-
curl \
|
|
8
|
-
&& rm -rf /var/lib/apt/lists/*
|
|
9
|
-
|
|
10
|
-
# Download bd CLI (beads issue tracker) for linux/amd64
|
|
11
|
-
ARG BD_VERSION=0.2.8
|
|
12
|
-
RUN curl -fsSL "https://github.com/beads-ai/beads/releases/download/v${BD_VERSION}/bd-linux-amd64" \
|
|
13
|
-
-o /usr/local/bin/bd \
|
|
14
|
-
&& chmod +x /usr/local/bin/bd
|
|
15
|
-
|
|
16
|
-
WORKDIR /app
|
|
17
|
-
|
|
18
|
-
# Copy package files and install dependencies
|
|
19
|
-
COPY package.json bun.lock* ./
|
|
20
|
-
RUN bun install --frozen-lockfile
|
|
21
|
-
|
|
22
|
-
# Copy source code
|
|
23
|
-
COPY . .
|
|
24
|
-
|
|
25
|
-
# Copy entrypoint script
|
|
26
|
-
COPY scripts/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
27
|
-
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
28
|
-
|
|
29
|
-
ENTRYPOINT ["docker-entrypoint.sh"]
|
|
30
|
-
CMD ["bun", "run", "test:integration"]
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
FROM python:3.11-slim
|
|
2
|
-
|
|
3
|
-
WORKDIR /app
|
|
4
|
-
|
|
5
|
-
# Install dependencies
|
|
6
|
-
COPY requirements.txt .
|
|
7
|
-
RUN pip install --no-cache-dir -r requirements.txt
|
|
8
|
-
|
|
9
|
-
# Copy server code
|
|
10
|
-
COPY server.py .
|
|
11
|
-
|
|
12
|
-
# Create data directory for SQLite
|
|
13
|
-
RUN mkdir -p /data
|
|
14
|
-
|
|
15
|
-
# Expose port
|
|
16
|
-
EXPOSE 8765
|
|
17
|
-
|
|
18
|
-
# Health check
|
|
19
|
-
HEALTHCHECK --interval=10s --timeout=3s --start-period=5s --retries=3 \
|
|
20
|
-
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8765/health/liveness')" || exit 1
|
|
21
|
-
|
|
22
|
-
# Run server
|
|
23
|
-
CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "8765"]
|
|
Binary file
|