@swarm-agent/sdk 0.1.52 → 0.1.53
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 +17 -5
- package/dist/activity.d.ts +33 -0
- package/dist/activity.js +83 -0
- package/dist/agents.d.ts +432 -17
- package/dist/agents.js +529 -37
- package/dist/auth.d.ts +18 -3
- package/dist/auth.js +30 -2
- package/dist/client.d.ts +11 -0
- package/dist/client.js +14 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/job-automations.d.ts +343 -0
- package/dist/job-automations.js +74 -0
- package/dist/limits.d.ts +16 -0
- package/dist/limits.js +12 -0
- package/dist/projects.d.ts +11 -1
- package/dist/projects.js +30 -0
- package/dist/sessions.js +9 -1
- package/dist/types.d.ts +158 -0
- package/dist/workers.js +2 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ CommonJS `require` and non-Node runtimes are not validated by this packaging ste
|
|
|
60
60
|
|
|
61
61
|
On the Swarm box, `http://127.0.0.1:7783` is the scoped-token listener. The AI
|
|
62
62
|
key the installer wrote to `~/.config/swarm/ai-key` (a new one:
|
|
63
|
-
`sudo bash /var/lib/swarm-headless/
|
|
63
|
+
`sudo bash /var/lib/swarm-headless/install.sh connect-ai`)
|
|
64
64
|
works only through its MCP, `http://127.0.0.1:7783/mcp`, with any MCP client;
|
|
65
65
|
it reaches no SDK route. For the session routes there, use a client key (a full
|
|
66
66
|
AI key's `swarm_create_client_key` mints one); **do not** use Desktop bootstrap
|
|
@@ -73,9 +73,18 @@ explicit refresh, not timer polling. Live agent verification is a separate step.
|
|
|
73
73
|
|
|
74
74
|
## Quick Start
|
|
75
75
|
|
|
76
|
-
###
|
|
76
|
+
### Workers and jobs
|
|
77
77
|
|
|
78
|
-
|
|
78
|
+
Start with a project and its authorized folders, then give jobs to workers:
|
|
79
|
+
coder for code changes, designer for UI/design, finder for research, or reviewer
|
|
80
|
+
for independent read-only review of another job. Reviewers require `review_job`,
|
|
81
|
+
use detached disposable checkouts, and report without delivering. Worker models
|
|
82
|
+
follow account role settings unless pinned. Read each job's result before
|
|
83
|
+
delivery (local merge, configured pull request, or queued landing).
|
|
84
|
+
|
|
85
|
+
### Advanced: sessions, plans and orchestration
|
|
86
|
+
|
|
87
|
+
For conversational interfaces or structured multi-stage work:
|
|
79
88
|
|
|
80
89
|
1. **Interactive Chat Flow (`client.chat` / `client.sessions`)**:
|
|
81
90
|
- Best for conversational assistants, interactive Q&A, and direct tool calling.
|
|
@@ -84,7 +93,7 @@ Swarm supports two distinct modes of execution depending on your application goa
|
|
|
84
93
|
|
|
85
94
|
2. **Autonomous Orchestration Flow (`client.projects` / `client.workers` / `client.deploy`)**:
|
|
86
95
|
- Best for multi-agent background tasks, codebase modifications, and durable pipelines.
|
|
87
|
-
-
|
|
96
|
+
- Organizes worker jobs across authorized folders with structured plans and checkpoint contracts.
|
|
88
97
|
- **Project → orchestrator conversations → delegated tasks → task sessions.** Project Markdown and linked workspaces provide high-level context; each deployed task resolves its own authorized execution workspace.
|
|
89
98
|
- Use `client.projects.listConversations(projectId)` and `createConversation(projectId)` for the session picker and New Conversation button. These call canonical `/v3/projects/{id}/sessions`, not generic workspace session creation. `getOrchestratorSession` reuses an existing conversation or creates one; it does not mean there can only be one.
|
|
90
99
|
- Use `client.projects.createTask()` / `listTasks()` for custom task organization. Task-linked sessions must be continued through `reopenTask` with exact revision guards, not ordinary chat messages.
|
|
@@ -390,8 +399,11 @@ console.log(`Authenticated as user ${session.user_id}`);
|
|
|
390
399
|
const { token, record } = await client.auth.createScopedToken({
|
|
391
400
|
name: 'GitHub Actions Trigger',
|
|
392
401
|
scopes: ['automations:trigger'],
|
|
393
|
-
expires_in_seconds: 86400, // 24 hours
|
|
394
402
|
});
|
|
403
|
+
// Every key works while it is used and stops after 90 days unused; nothing to
|
|
404
|
+
// renew. Pass expires_in_seconds only for a fixed end date (at most 365 days).
|
|
405
|
+
// client.auth.createKey({ kind: 'app' | 'bot' | 'ai', ... }), listKeys() and
|
|
406
|
+
// revokeKey(id) cover all three kinds.
|
|
395
407
|
console.log(`Created deploy token: ${token} (hint: ${record.token_hint})`);
|
|
396
408
|
|
|
397
409
|
// List active tokens
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SwarmTransport } from './transport.js';
|
|
2
|
+
export interface ActivityEvent {
|
|
3
|
+
seq: number;
|
|
4
|
+
at: number;
|
|
5
|
+
kind: string;
|
|
6
|
+
session_id: string;
|
|
7
|
+
job_id: string;
|
|
8
|
+
worker_id: string;
|
|
9
|
+
worker_name: string;
|
|
10
|
+
project_id: string;
|
|
11
|
+
state: string;
|
|
12
|
+
summary: string;
|
|
13
|
+
tool_name?: string;
|
|
14
|
+
file_path?: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ActivityGap {
|
|
17
|
+
kind: 'gap';
|
|
18
|
+
from: number;
|
|
19
|
+
to: number;
|
|
20
|
+
cursor: string;
|
|
21
|
+
}
|
|
22
|
+
export interface ActivityStreamOptions {
|
|
23
|
+
cursor?: string;
|
|
24
|
+
project?: string;
|
|
25
|
+
worker?: string;
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
}
|
|
28
|
+
export declare class SwarmActivityNamespace {
|
|
29
|
+
private readonly transport;
|
|
30
|
+
constructor(transport: SwarmTransport);
|
|
31
|
+
/** Metadata only. Gap records must be surfaced to users; they are not activity events. */
|
|
32
|
+
stream(options?: ActivityStreamOptions): AsyncGenerator<ActivityEvent | ActivityGap>;
|
|
33
|
+
}
|
package/dist/activity.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export class SwarmActivityNamespace {
|
|
2
|
+
transport;
|
|
3
|
+
constructor(transport) {
|
|
4
|
+
this.transport = transport;
|
|
5
|
+
}
|
|
6
|
+
/** Metadata only. Gap records must be surfaced to users; they are not activity events. */
|
|
7
|
+
async *stream(options = {}) {
|
|
8
|
+
let cursor = options.cursor ?? '0';
|
|
9
|
+
while (!options.signal?.aborted) {
|
|
10
|
+
const config = this.transport.getConfig();
|
|
11
|
+
if (config.socketPath)
|
|
12
|
+
throw new Error('Activity SSE requires an HTTP connection');
|
|
13
|
+
const query = new URLSearchParams({ cursor });
|
|
14
|
+
if (options.project)
|
|
15
|
+
query.set('project', options.project);
|
|
16
|
+
if (options.worker)
|
|
17
|
+
query.set('worker', options.worker);
|
|
18
|
+
const headers = { ...config.defaultHeaders, Accept: 'text/event-stream' };
|
|
19
|
+
if (config.token && !headers.Authorization && !headers.authorization)
|
|
20
|
+
headers.Authorization = `Bearer ${config.token}`;
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch(`${config.baseUrl.replace(/\/+$/, '')}/v3/activity/stream?${query}`, { headers, signal: options.signal, redirect: 'error' });
|
|
23
|
+
if (!response.ok || !response.body)
|
|
24
|
+
throw new Error(`Activity stream failed: ${response.status}`);
|
|
25
|
+
const reader = response.body.getReader();
|
|
26
|
+
const decoder = new TextDecoder();
|
|
27
|
+
let pending = '';
|
|
28
|
+
try {
|
|
29
|
+
for (;;) {
|
|
30
|
+
const chunk = await reader.read();
|
|
31
|
+
if (chunk.done)
|
|
32
|
+
break;
|
|
33
|
+
pending += decoder.decode(chunk.value, { stream: true }).replace(/\r\n/g, '\n');
|
|
34
|
+
if (pending.length > 1024 * 1024)
|
|
35
|
+
throw new Error('Activity frame too large');
|
|
36
|
+
let end;
|
|
37
|
+
while ((end = pending.indexOf('\n\n')) >= 0) {
|
|
38
|
+
const frame = pending.slice(0, end);
|
|
39
|
+
pending = pending.slice(end + 2);
|
|
40
|
+
const kind = frame.split('\n').find(line => line.startsWith('event:'))?.slice(6).trim();
|
|
41
|
+
const data = frame.split('\n').filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\n');
|
|
42
|
+
if (!data)
|
|
43
|
+
continue;
|
|
44
|
+
const value = JSON.parse(data);
|
|
45
|
+
if (kind === 'cursor')
|
|
46
|
+
cursor = value.cursor;
|
|
47
|
+
else if (kind === 'gap') {
|
|
48
|
+
cursor = value.cursor;
|
|
49
|
+
yield { ...value, kind: 'gap' };
|
|
50
|
+
}
|
|
51
|
+
else if (kind === 'activity') {
|
|
52
|
+
cursor = String(value.seq);
|
|
53
|
+
yield value;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
finally {
|
|
59
|
+
try {
|
|
60
|
+
await reader.cancel();
|
|
61
|
+
}
|
|
62
|
+
catch { /* The disconnected transport is already closed. */ }
|
|
63
|
+
reader.releaseLock();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
if (options.signal?.aborted)
|
|
68
|
+
return;
|
|
69
|
+
// fetch and stream transport failures are TypeErrors; malformed frames
|
|
70
|
+
// and HTTP authorization failures are not retried.
|
|
71
|
+
if (!(error instanceof TypeError))
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
// Bound reconnect frequency; this is reconnection, not polling for activity.
|
|
75
|
+
if (!options.signal?.aborted)
|
|
76
|
+
await new Promise(resolve => {
|
|
77
|
+
const finish = () => { clearTimeout(timer); options.signal?.removeEventListener('abort', finish); resolve(); };
|
|
78
|
+
const timer = setTimeout(finish, 1000);
|
|
79
|
+
options.signal?.addEventListener('abort', finish, { once: true });
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|