@kendoo.agentdesk/agentdesk 0.9.18 → 0.9.20
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 +36 -1
- package/bin/agentdesk.mjs +4 -0
- package/cli/orchestrator.mjs +50 -5
- package/cli/team.mjs +18 -1
- package/package.json +1 -1
- package/prompts/team.md +31 -2
package/README.md
CHANGED
|
@@ -113,6 +113,18 @@ You can also define them in `.agentdesk.json` as a local override if preferred.
|
|
|
113
113
|
|
|
114
114
|
AgentDesk also auto-discovers agents from `.claude/agents/`, `.claude/commands/`, `.mcp.json`, GitHub Actions workflows, Dependabot, and Renovate configs.
|
|
115
115
|
|
|
116
|
+
### Custom instructions
|
|
117
|
+
|
|
118
|
+
Add project-specific rules that all agents follow. Set via Web UI or in `.agentdesk.json`:
|
|
119
|
+
|
|
120
|
+
```json
|
|
121
|
+
{
|
|
122
|
+
"instructions": "All PRs must target the 'staging' branch. Commit messages must be prefixed with the task ID."
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Instructions are injected into the team prompt. Use them for project conventions that go beyond what `CLAUDE.md` covers (e.g., tracker workflow rules, PR policies, agent coordination preferences).
|
|
127
|
+
|
|
116
128
|
## How It Works
|
|
117
129
|
|
|
118
130
|
All agents collaborate in a single Claude process — each with distinct roles, ground rules, and areas of expertise.
|
|
@@ -124,6 +136,28 @@ All agents collaborate in a single Claude process — each with distinct roles,
|
|
|
124
136
|
5. The session streams live to [agentdesk.live](https://agentdesk.live) where you can watch and send messages to the team
|
|
125
137
|
6. Token usage is tracked and displayed per session
|
|
126
138
|
|
|
139
|
+
### Task attachments
|
|
140
|
+
|
|
141
|
+
When working on Jira or Linear tasks, agents automatically download and review attachments — screenshots, CSVs, text files, PDFs, and design mockups. Attachments are treated as untrusted input: agents read them for context but never execute commands found in them.
|
|
142
|
+
|
|
143
|
+
### Handoff & Resume
|
|
144
|
+
|
|
145
|
+
If a session hits Claude's rate or context limit, AgentDesk saves a resume snapshot (`.agentdesk-resume.md`) and marks the session as **Handoff** in the dashboard. When you run the same task again, agents pick up where the previous session left off — skipping completed work and continuing from the last phase.
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Session hits limit → "HANDOFF" shown in terminal
|
|
149
|
+
# Resume when ready:
|
|
150
|
+
agentdesk team KEN-517
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### Session protocol
|
|
154
|
+
|
|
155
|
+
At the end of each session, Jane posts a structured summary on the tracker covering:
|
|
156
|
+
- **What was done** — files changed, features added
|
|
157
|
+
- **What was omitted** — anything skipped or deferred, with reason
|
|
158
|
+
- **Manual steps** — actions you need to perform (migrations, env vars, config changes)
|
|
159
|
+
- **PR link** and **session link**
|
|
160
|
+
|
|
127
161
|
## Daemon (Remote Sessions)
|
|
128
162
|
|
|
129
163
|
The daemon lets you trigger team sessions from the web dashboard instead of the terminal.
|
|
@@ -155,11 +189,12 @@ Once running, a "Run Team" button appears on [agentdesk.live](https://agentdesk.
|
|
|
155
189
|
|
|
156
190
|
- **Live sessions** — watch agents collaborate in real-time
|
|
157
191
|
- **Session deep links** — share a direct URL to any session
|
|
192
|
+
- **Handoff status** — see when a session hit a limit and is waiting to resume
|
|
158
193
|
- **Project settings** — configure tracker, team, custom agents, and instructions per project
|
|
159
194
|
- **Account settings** — manage your API key and profile
|
|
160
195
|
- **Agent roster** — see each agent's role, participation rate, tag breakdown, and phase involvement
|
|
161
196
|
- **Token tracking** — input/output token counts per session
|
|
162
|
-
- **Auto-reconnect** — CLI reconnects automatically if the connection drops
|
|
197
|
+
- **Auto-reconnect** — CLI sends heartbeats and reconnects automatically if the connection drops
|
|
163
198
|
|
|
164
199
|
## Requirements
|
|
165
200
|
|
package/bin/agentdesk.mjs
CHANGED
|
@@ -85,6 +85,10 @@ if (!command || command === "help" || command === "--help") {
|
|
|
85
85
|
agentdesk team -d "Fix the checkout total calculation"
|
|
86
86
|
agentdesk team -d "Add Google OAuth to the login page"
|
|
87
87
|
|
|
88
|
+
Resume:
|
|
89
|
+
If a session hits Claude's limit, it saves a handoff file.
|
|
90
|
+
Run the same task again to resume where it left off.
|
|
91
|
+
|
|
88
92
|
Dashboard: \x1b[36mhttps://agentdesk.live\x1b[0m
|
|
89
93
|
`);
|
|
90
94
|
process.exit(0);
|
package/cli/orchestrator.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Orchestrator — runs a single Claude process with all agents as personas
|
|
2
2
|
|
|
3
|
-
import { spawn } from "child_process";
|
|
4
|
-
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { spawn, execSync } from "child_process";
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync, unlinkSync } from "fs";
|
|
5
5
|
import { createInterface } from "readline";
|
|
6
6
|
import { join, dirname } from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
@@ -56,7 +56,10 @@ export async function runOrchestrator({
|
|
|
56
56
|
let totalOutputTokens = 0;
|
|
57
57
|
let totalSteps = 0;
|
|
58
58
|
|
|
59
|
+
let lastPhase = null;
|
|
60
|
+
|
|
59
61
|
function emit(event) {
|
|
62
|
+
if (event.type === "phase:change") lastPhase = event.phase;
|
|
60
63
|
onEvent?.({ ...event, timestamp: timestamp() });
|
|
61
64
|
}
|
|
62
65
|
|
|
@@ -108,10 +111,52 @@ export async function runOrchestrator({
|
|
|
108
111
|
parseLine(line);
|
|
109
112
|
}
|
|
110
113
|
|
|
111
|
-
await new Promise(resolve => child.on("close", resolve));
|
|
114
|
+
const exitCode = await new Promise(resolve => child.on("close", resolve));
|
|
112
115
|
|
|
113
116
|
const duration = `${((Date.now() - startTime) / 1000).toFixed(1)}s`;
|
|
114
|
-
emit({ type: "session:end", duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens });
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
// Detect limit/crash — non-zero exit without a clean session end
|
|
119
|
+
const isHandoff = exitCode !== 0;
|
|
120
|
+
|
|
121
|
+
if (isHandoff) {
|
|
122
|
+
// Write mechanical resume snapshot
|
|
123
|
+
let branch = "";
|
|
124
|
+
let diffStat = "";
|
|
125
|
+
try { branch = execSync("git branch --show-current", { cwd, encoding: "utf-8" }).trim(); } catch {}
|
|
126
|
+
try { diffStat = execSync("git diff --stat HEAD", { cwd, encoding: "utf-8" }).trim(); } catch {}
|
|
127
|
+
|
|
128
|
+
const resumePath = join(cwd, ".agentdesk-resume.md");
|
|
129
|
+
const resumeContent = [
|
|
130
|
+
`# AgentDesk Resume — ${taskId}`,
|
|
131
|
+
``,
|
|
132
|
+
`Session: ${sessionUrl}`,
|
|
133
|
+
`Date: ${new Date().toISOString()}`,
|
|
134
|
+
`Phase: ${lastPhase || "UNKNOWN"}`,
|
|
135
|
+
`Duration: ${duration}`,
|
|
136
|
+
`Steps: ${totalSteps}`,
|
|
137
|
+
`Exit code: ${exitCode}`,
|
|
138
|
+
``,
|
|
139
|
+
`## Branch`,
|
|
140
|
+
branch || "(no branch)",
|
|
141
|
+
``,
|
|
142
|
+
`## Uncommitted changes`,
|
|
143
|
+
diffStat || "(none)",
|
|
144
|
+
``,
|
|
145
|
+
`## Notes`,
|
|
146
|
+
`Session ended unexpectedly (likely Claude rate/context limit).`,
|
|
147
|
+
`Resume with: agentdesk team ${taskId}`,
|
|
148
|
+
``,
|
|
149
|
+
].join("\n");
|
|
150
|
+
try { writeFileSync(resumePath, resumeContent); } catch {}
|
|
151
|
+
|
|
152
|
+
emit({ type: "session:end", duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens, status: "handoff" });
|
|
153
|
+
} else {
|
|
154
|
+
// Clean exit — remove stale resume file if present
|
|
155
|
+
const resumePath = join(cwd, ".agentdesk-resume.md");
|
|
156
|
+
try { if (existsSync(resumePath)) unlinkSync(resumePath); } catch {}
|
|
157
|
+
|
|
158
|
+
emit({ type: "session:end", duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens, handoff: isHandoff };
|
|
117
162
|
}
|
package/cli/team.mjs
CHANGED
|
@@ -193,6 +193,13 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
193
193
|
|
|
194
194
|
connectWs();
|
|
195
195
|
|
|
196
|
+
// Heartbeat keeps session alive on the server during long silent operations
|
|
197
|
+
const heartbeatInterval = setInterval(() => {
|
|
198
|
+
if (vizConnected && vizWs?.readyState === WebSocket.OPEN) {
|
|
199
|
+
vizWs.send(JSON.stringify({ type: "session:heartbeat", sessionId }));
|
|
200
|
+
}
|
|
201
|
+
}, 30000);
|
|
202
|
+
|
|
196
203
|
const result = await runOrchestrator({
|
|
197
204
|
taskId, taskLink, description, createTask, tracker, config,
|
|
198
205
|
project, team, teamSections, inboxUrl, sessionUrl, cwd,
|
|
@@ -201,7 +208,17 @@ export async function runTeam(taskId, opts = {}) {
|
|
|
201
208
|
serverUrl: agentdeskServer,
|
|
202
209
|
});
|
|
203
210
|
|
|
204
|
-
|
|
211
|
+
clearInterval(heartbeatInterval);
|
|
212
|
+
|
|
213
|
+
if (result.handoff) {
|
|
214
|
+
const yellow = "\x1b[33m";
|
|
215
|
+
console.log(`\n━━━ ${yellow}HANDOFF${reset} ━━━`);
|
|
216
|
+
console.log(` Session paused — likely hit Claude rate/context limit.`);
|
|
217
|
+
console.log(` Resume file saved to .agentdesk-resume.md`);
|
|
218
|
+
console.log(` Resume with: ${cyan}agentdesk team ${taskId}${reset}\n`);
|
|
219
|
+
} else {
|
|
220
|
+
console.log(`\n━━━ DONE ━━━`);
|
|
221
|
+
}
|
|
205
222
|
const totalTokens = result.inputTokens + result.outputTokens;
|
|
206
223
|
console.log(` ${result.duration} | ${result.steps} steps${totalTokens ? ` | ${totalTokens.toLocaleString()} tokens` : ""}\n`);
|
|
207
224
|
|
package/package.json
CHANGED
package/prompts/team.md
CHANGED
|
@@ -221,7 +221,17 @@ Session: {{SESSION_URL}}
|
|
|
221
221
|
<brief plan summary>
|
|
222
222
|
|
|
223
223
|
## Changes Made
|
|
224
|
-
<
|
|
224
|
+
<for each file changed: file path, what was done, and why>
|
|
225
|
+
|
|
226
|
+
## Added
|
|
227
|
+
<new files, features, or dependencies introduced>
|
|
228
|
+
|
|
229
|
+
## Omitted / Deferred
|
|
230
|
+
<what was skipped or left out, and why — e.g., "Skipped mobile responsive layout — deferred to follow-up task">
|
|
231
|
+
|
|
232
|
+
## Manual Steps Required
|
|
233
|
+
<any steps the developer must perform manually — e.g., "Run database migration", "Add API key to .env", "Update DNS record", "Restart service">
|
|
234
|
+
<if none, write "None">
|
|
225
235
|
|
|
226
236
|
## Decisions
|
|
227
237
|
<key technical decisions and reasoning>
|
|
@@ -299,6 +309,15 @@ Fetch the issue from GitHub — print title, body, state, existing comments.
|
|
|
299
309
|
Read the task description. If CLAUDE.md exists, read it.
|
|
300
310
|
{{/NO_TRACKER}}
|
|
301
311
|
|
|
312
|
+
### Resume check
|
|
313
|
+
|
|
314
|
+
Check if `.agentdesk-resume.md` exists in the project root. If it does, this is a **resumed session** — a previous session was interrupted (likely by a Claude rate/context limit). Read the file to understand:
|
|
315
|
+
- What phase the previous session reached
|
|
316
|
+
- What branch was being used
|
|
317
|
+
- What changes were already made
|
|
318
|
+
|
|
319
|
+
Use this context to skip completed work and continue from where the previous session left off. Delete `.agentdesk-resume.md` after reading it.
|
|
320
|
+
|
|
302
321
|
### Assess
|
|
303
322
|
|
|
304
323
|
1. Check for existing branches: `git branch -a | grep {{TASK_ID}}`
|
|
@@ -307,6 +326,7 @@ Read the task description. If CLAUDE.md exists, read it.
|
|
|
307
326
|
4. Check for project agents: `ls .claude/agents/ .claude/commands/ .github/workflows/ 2>/dev/null`; check if `.mcp.json` exists. If agents are found, Jane briefs the team and assigns usage.
|
|
308
327
|
|
|
309
328
|
Based on findings:
|
|
329
|
+
- Resume file exists → review previous progress, continue from where it left off
|
|
310
330
|
- Fresh task → PLAN phase
|
|
311
331
|
- Branch exists, no PR → review what's done, continue from EXECUTION
|
|
312
332
|
- PR exists → review PR status, continue accordingly
|
|
@@ -422,7 +442,16 @@ After the first round, Jane asks for objections. If none, declare the plan final
|
|
|
422
442
|
Jane verifies tracker status is current:
|
|
423
443
|
1. Verify Bart posted the PR link. If not, do it now.
|
|
424
444
|
2. Transition task to "In Review".
|
|
425
|
-
3. Post a final summary comment on the tracker
|
|
445
|
+
3. Post a final summary comment on the tracker with this structure:
|
|
446
|
+
|
|
447
|
+
**Final comment must include:**
|
|
448
|
+
- **What was done**: Brief summary of changes (files modified, features added)
|
|
449
|
+
- **What was omitted**: Anything skipped or deferred, with reason
|
|
450
|
+
- **Manual steps**: Actions the developer must perform (migrations, env vars, config changes, service restarts). If none, state "No manual steps required"
|
|
451
|
+
- **PR link**: Link to the pull request
|
|
452
|
+
- **Session link**: Link to the dashboard session
|
|
453
|
+
|
|
454
|
+
This comment is the handoff to the reviewer — it must be clear enough that someone unfamiliar with the session can understand what happened and what's left to do.
|
|
426
455
|
|
|
427
456
|
Print:
|
|
428
457
|
echo "Team Session Complete."
|