@kendoo.agentdesk/agentdesk 0.28.1 → 0.28.3
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/CHANGELOG.md +16 -0
- package/cli/config.mjs +1 -1
- package/cli/engine/agents/index.mjs +1 -1
- package/cli/engine/events.mjs +49 -6
- package/cli/engine/phases/EXECUTION.md +1 -1
- package/cli/engine/phases/INTAKE.md +1 -1
- package/cli/engine/phases/PLAN.md +1 -1
- package/cli/engine/phases/REVIEW.md +1 -1
- package/cli/engine/phases/SOLO.md +1 -1
- package/cli/engine/phases/SUMMARY.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,22 @@ All user-facing changes to AgentDesk. Each entry is tagged:
|
|
|
8
8
|
|
|
9
9
|
Internal refactors, infrastructure changes, and architectural notes are not listed here.
|
|
10
10
|
|
|
11
|
+
## [0.28.3] — 2026-09-19
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- `[UI]` The docked team status bar now defaults to a compact one-line strip (avatars + current status) instead of the full grid, with a drag handle (and keyboard resize) to expand it.
|
|
15
|
+
- `[Both]` Session messages are filtered to the session's declared roster, so nested/ad-hoc subagents (e.g. a generic search delegate) no longer show their internal narration in the feed as if they were a team member.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
- `[CLI]` Every phase (INTAKE, PLAN, EXECUTION, REVIEW, SUMMARY, SOLO) told the model to answer with the raw handoff JSON as its final chat reply, so the model printed that JSON into the session feed on top of the SDK's separate structured-output channel. Phase prompts no longer ask for that, and a code-side guard strips it if a model still echoes it.
|
|
19
|
+
|
|
20
|
+
## [0.28.2] — 2026-09-18
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- `[UI]` The session feed no longer prints a line for every file read, search, and shell command. Consecutive tool actions collapse into a single expandable "N actions" line, so the feed reads as a team conversation instead of a tool-call transcript.
|
|
24
|
+
- `[UI]` The team status bar is now docked at the bottom of the session view and no longer scrolls away with the conversation. The message list scrolls independently above it.
|
|
25
|
+
- `[CLI]` Added Fable 5.1 as a selectable per-phase model, and updated the Opus/Sonnet model labels in project settings.
|
|
26
|
+
|
|
11
27
|
## [0.28.1] — 2026-09-17
|
|
12
28
|
|
|
13
29
|
### Fixed
|
package/cli/config.mjs
CHANGED
|
@@ -22,7 +22,7 @@ const DEFAULTS = {
|
|
|
22
22
|
projectAgents: [],
|
|
23
23
|
screenshots: true,
|
|
24
24
|
// Model per phase — override the phase default.
|
|
25
|
-
// Keys: INTAKE, PLAN, EXECUTION, REVIEW, SUMMARY. Values: "default" | "opus" | "sonnet" | "haiku".
|
|
25
|
+
// Keys: INTAKE, PLAN, EXECUTION, REVIEW, SUMMARY. Values: "default" | "opus" | "sonnet" | "haiku" | "fable".
|
|
26
26
|
// Missing entry or "default" falls back to the phase default
|
|
27
27
|
// (sonnet for INTAKE/PLAN/EXECUTION, haiku for REVIEW/SUMMARY).
|
|
28
28
|
phaseModels: {},
|
|
@@ -33,7 +33,7 @@ export const PHASE_ROSTER = Object.freeze({
|
|
|
33
33
|
const CUSTOM_AGENT_PHASES = new Set(["PLAN", "EXECUTION"]);
|
|
34
34
|
|
|
35
35
|
// Today's defaults: REVIEW and SUMMARY on haiku, the rest on the CLI default.
|
|
36
|
-
// `phaseModels` values are "opus" | "sonnet" | "haiku" | "default" | undefined.
|
|
36
|
+
// `phaseModels` values are "opus" | "sonnet" | "haiku" | "fable" | "default" | undefined.
|
|
37
37
|
export function modelForPhase(phase, phaseModels = {}) {
|
|
38
38
|
const choice = phaseModels?.[phase];
|
|
39
39
|
if (choice && choice !== "default") return choice;
|
package/cli/engine/events.mjs
CHANGED
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
// `phase:change` and `session:start|end|error` are emitted by the session
|
|
18
18
|
// loop, not here — phase boundaries are engine facts, not prose signals.
|
|
19
19
|
|
|
20
|
+
import { PHASE_OUTPUT_SCHEMAS } from "./schemas.mjs";
|
|
21
|
+
|
|
20
22
|
const TAG_RE = /\[(SAY|ACT|THINK|AGREE|ARGUE)\]\s*/gi;
|
|
21
23
|
const AGENT_TOOL_NAMES = new Set(["Agent", "Task"]);
|
|
22
24
|
|
|
@@ -37,6 +39,46 @@ export function stripTags(text) {
|
|
|
37
39
|
return String(text).replace(TAG_RE, "");
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
function isPhaseOutput(raw) {
|
|
43
|
+
let value;
|
|
44
|
+
try { value = JSON.parse(raw); } catch { return false; }
|
|
45
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
46
|
+
return Object.values(PHASE_OUTPUT_SCHEMAS).some(schema =>
|
|
47
|
+
schema.required.every(key => Object.hasOwn(value, key)) &&
|
|
48
|
+
Object.keys(value).every(key => Object.hasOwn(schema.properties, key)) &&
|
|
49
|
+
Object.entries(schema.properties).every(([key, spec]) =>
|
|
50
|
+
spec.type === "array" ? Array.isArray(value[key]) : typeof value[key] === spec.type));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Structured output travels separately in result.structured_output. Suppress
|
|
54
|
+
// complete copies of our known handoff shapes, not arbitrary JSON/code snippets.
|
|
55
|
+
export function stripStructuredOutput(text) {
|
|
56
|
+
const unfenced = String(text).replace(/```(?:json)?\s*\n([\s\S]*?)```/gi,
|
|
57
|
+
(block, body) => isPhaseOutput(body.trim()) ? "" : block);
|
|
58
|
+
let out = "", from = 0;
|
|
59
|
+
for (let start = 0; start < unfenced.length; start++) {
|
|
60
|
+
if (unfenced[start] !== "{") continue;
|
|
61
|
+
let depth = 0, quoted = false, escaped = false, end = start;
|
|
62
|
+
for (; end < unfenced.length; end++) {
|
|
63
|
+
const char = unfenced[end];
|
|
64
|
+
if (quoted) {
|
|
65
|
+
if (escaped) escaped = false;
|
|
66
|
+
else if (char === "\\") escaped = true;
|
|
67
|
+
else if (char === '"') quoted = false;
|
|
68
|
+
} else if (char === '"') quoted = true;
|
|
69
|
+
else if (char === "{") depth++;
|
|
70
|
+
else if (char === "}" && --depth === 0) break;
|
|
71
|
+
}
|
|
72
|
+
if (end === unfenced.length) break; // Partial/invalid text is kept verbatim.
|
|
73
|
+
if (isPhaseOutput(unfenced.slice(start, end + 1))) {
|
|
74
|
+
out += unfenced.slice(from, start);
|
|
75
|
+
from = end + 1;
|
|
76
|
+
}
|
|
77
|
+
start = end;
|
|
78
|
+
}
|
|
79
|
+
return (out + unfenced.slice(from)).trim();
|
|
80
|
+
}
|
|
81
|
+
|
|
40
82
|
// Human-readable one-liner for the dashboard's tool feed. The "Reading|Editing|
|
|
41
83
|
// Writing <path>" forms are load-bearing: cli/daemon.mjs derives
|
|
42
84
|
// filePathsTouched from them.
|
|
@@ -122,8 +164,9 @@ export function createEventMapper({ leadAgent = "Jane", onEvent } = {}) {
|
|
|
122
164
|
}
|
|
123
165
|
|
|
124
166
|
function handleText(agent, raw, { isLead }) {
|
|
125
|
-
const text =
|
|
126
|
-
|
|
167
|
+
const text = stripStructuredOutput(raw);
|
|
168
|
+
const message = stripTags(text).replace(/\*+/g, "").trim();
|
|
169
|
+
if (!message) return;
|
|
127
170
|
|
|
128
171
|
if (isLead) {
|
|
129
172
|
const taskId = text.match(/TASK_ID:\s*(\S+)/)?.[1];
|
|
@@ -131,18 +174,18 @@ export function createEventMapper({ leadAgent = "Jane", onEvent } = {}) {
|
|
|
131
174
|
if (taskId || title) emit("session:update", { ...(taskId && { taskId }), ...(title && { title }) });
|
|
132
175
|
}
|
|
133
176
|
|
|
134
|
-
emit("agent:message", { agent, tag: detectTag(text), message
|
|
177
|
+
emit("agent:message", { agent, tag: detectTag(text), message });
|
|
178
|
+
return true;
|
|
135
179
|
}
|
|
136
180
|
|
|
137
181
|
function handleAssistant(msg) {
|
|
138
182
|
reportModel(msg.message?.model);
|
|
139
183
|
const agent = agentFor(msg);
|
|
140
184
|
const isLead = !msg.parent_tool_use_id;
|
|
141
|
-
if (msg.parent_tool_use_id) sawText.add(msg.parent_tool_use_id);
|
|
142
185
|
|
|
143
186
|
for (const block of msg.message?.content || []) {
|
|
144
187
|
if (block.type === "text") {
|
|
145
|
-
handleText(agent, block.text, { isLead });
|
|
188
|
+
if (handleText(agent, block.text, { isLead }) && msg.parent_tool_use_id) sawText.add(msg.parent_tool_use_id);
|
|
146
189
|
} else if (block.type === "tool_use") {
|
|
147
190
|
steps++;
|
|
148
191
|
if (AGENT_TOOL_NAMES.has(block.name)) {
|
|
@@ -168,7 +211,7 @@ export function createEventMapper({ leadAgent = "Jane", onEvent } = {}) {
|
|
|
168
211
|
// dashboard still shows the agent said something.
|
|
169
212
|
const name = agents.get(block.tool_use_id);
|
|
170
213
|
if (name && !sawText.has(block.tool_use_id)) {
|
|
171
|
-
const text = blockText(block.content).trim();
|
|
214
|
+
const text = stripTags(stripStructuredOutput(blockText(block.content))).trim();
|
|
172
215
|
if (text) emit("agent:message", { agent: name, tag: "SAY", message: stripTags(text).slice(0, 1200) });
|
|
173
216
|
}
|
|
174
217
|
}
|
|
@@ -42,4 +42,4 @@ Drive the plan from session memory step by step. Delegate each step with the Age
|
|
|
42
42
|
5. **Bart reviews and publishes** — reads all changed files, checks edge cases and error handling, runs linter and build, captures screenshots if applicable, pushes and creates the PR, posts the PR link on the tracker, posts screenshots as a separate comment.
|
|
43
43
|
6. Ask Dennis, Sam and Bart to post their brief tracker comments (files changed & decisions; architecture findings or clean audit with evidence; PR link, test results, screenshots).
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
The structured output required by the schema is captured automatically — what was implemented, files changed, the PR URL (empty string if none), QA results, issues fixed, and what the reviewers should look at. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|
|
@@ -31,4 +31,4 @@ Task description:
|
|
|
31
31
|
3. **Assess scope.** Restate the task as user outcomes and acceptance criteria. If it is too large for one session, decompose it into subtasks (basic vs deferred) in product terms and have Dennis create them in the tracker.
|
|
32
32
|
4. Announce `SESSION_TITLE: <4-8 word title>` on its own line.
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
The structured output required by the schema is captured automatically — title, task summary, requirements, assessment (branches, PRs, patterns, resume context), subtasks, and what PLAN should focus on. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|
|
@@ -23,4 +23,4 @@ Task: {{TASK_ID}}
|
|
|
23
23
|
- Nora (only if user-facing behaviour changes): which docs/README/help surfaces must change.
|
|
24
24
|
3. Relay the substance of each report in a few lines. Ask for objections once. Resolve them and declare the plan final — do not brainstorm beyond two rounds.
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
The structured output required by the schema is captured automatically — approach, files to modify, decisions, risks, agent assignments, and the ordered implementation steps. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|
|
@@ -18,4 +18,4 @@ Task: {{TASK_ID}}
|
|
|
18
18
|
2. Weigh the reports. Be strict but not pedantic: only actual gaps against the task requirements and the plan — not stylistic preferences or speculative refactors.
|
|
19
19
|
3. Decide: `APPROVED` or `NEEDS_MORE_WORK`.
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The structured output required by the schema is captured automatically — the verdict, the findings (reviewer, title, detail, file, line), items explicitly out of scope, and any claims that were not backed by an observation. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|
|
@@ -112,4 +112,4 @@ This keeps everything in one branch — no conflicts, one PR to review.
|
|
|
112
112
|
If the task has no child items, just work on it normally as a single task.
|
|
113
113
|
{{/CHILD_TASKS}}
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
The structured output required by the schema is captured automatically — a summary of what was done, files changed, the PR URL (empty string if none), deferred items, and manual steps. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|
|
@@ -20,4 +20,4 @@ Task: {{TASK_ID}}
|
|
|
20
20
|
- **Session link**: {{SESSION_URL}}
|
|
21
21
|
2. Delegate the tracker writes to Dennis and require the command output for each: verify the PR link is attached (attach it if missing), transition the task to "In Review", post the final comment.
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
The structured output required by the schema is captured automatically — status, PR URL, deferred items, manual steps, and the summary comment exactly as posted. Do not repeat the JSON object in chat; keep any closing chat message brief and human-readable.
|