@kendoo.agentdesk/agentdesk 0.9.19 → 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 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);
@@ -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
- return { duration, steps: totalSteps, inputTokens: totalInputTokens, outputTokens: totalOutputTokens };
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
@@ -210,7 +210,15 @@ export async function runTeam(taskId, opts = {}) {
210
210
 
211
211
  clearInterval(heartbeatInterval);
212
212
 
213
- console.log(`\n━━━ DONE ━━━`);
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
+ }
214
222
  const totalTokens = result.inputTokens + result.outputTokens;
215
223
  console.log(` ${result.duration} | ${result.steps} steps${totalTokens ? ` | ${totalTokens.toLocaleString()} tokens` : ""}\n`);
216
224
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kendoo.agentdesk/agentdesk",
3
- "version": "0.9.19",
3
+ "version": "0.9.20",
4
4
  "description": "AI team orchestrator for Claude Code — run collaborative agent sessions from your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/prompts/team.md CHANGED
@@ -309,6 +309,15 @@ Fetch the issue from GitHub — print title, body, state, existing comments.
309
309
  Read the task description. If CLAUDE.md exists, read it.
310
310
  {{/NO_TRACKER}}
311
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
+
312
321
  ### Assess
313
322
 
314
323
  1. Check for existing branches: `git branch -a | grep {{TASK_ID}}`
@@ -317,6 +326,7 @@ Read the task description. If CLAUDE.md exists, read it.
317
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.
318
327
 
319
328
  Based on findings:
329
+ - Resume file exists → review previous progress, continue from where it left off
320
330
  - Fresh task → PLAN phase
321
331
  - Branch exists, no PR → review what's done, continue from EXECUTION
322
332
  - PR exists → review PR status, continue accordingly