@rubytech/taskmaster 1.23.1 → 1.25.1
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/dist/agents/cli-runner/helpers.js +6 -1
- package/dist/agents/cli-runner.js +27 -3
- package/dist/agents/pi-embedded-runner/run/attempt.js +6 -0
- package/dist/agents/system-prompt-log.js +24 -0
- package/dist/agents/system-prompt.js +9 -1
- package/dist/agents/workspace-migrations.js +83 -1
- package/dist/build-info.json +3 -3
- package/dist/config/agent-tools-reconcile.js +35 -0
- package/dist/control-ui/assets/{index-B9ScTH-4.js → index-B1bLQjxV.js} +104 -104
- package/dist/control-ui/assets/index-B1bLQjxV.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/server.impl.js +15 -1
- package/dist/logging/logger.js +1 -1
- package/package.json +1 -1
- package/scripts/install.sh +33 -0
- package/taskmaster-docs/USER-GUIDE.md +12 -1
- package/templates/customer/agents/admin/AGENTS.md +30 -0
- package/templates/customer/agents/admin/SOUL.md +4 -0
- package/dist/control-ui/assets/index-B9ScTH-4.js.map +0 -1
|
@@ -161,7 +161,7 @@ export function buildSystemPrompt(params) {
|
|
|
161
161
|
},
|
|
162
162
|
});
|
|
163
163
|
const ttsHint = params.config ? buildTtsSystemPromptHint(params.config) : undefined;
|
|
164
|
-
|
|
164
|
+
const prompt = buildAgentSystemPrompt({
|
|
165
165
|
workspaceDir: params.workspaceDir,
|
|
166
166
|
defaultThinkLevel: params.defaultThinkLevel,
|
|
167
167
|
extraSystemPrompt: params.extraSystemPrompt,
|
|
@@ -178,6 +178,11 @@ export function buildSystemPrompt(params) {
|
|
|
178
178
|
contextFiles: params.contextFiles,
|
|
179
179
|
ttsHint,
|
|
180
180
|
});
|
|
181
|
+
return {
|
|
182
|
+
prompt,
|
|
183
|
+
bootstrapFiles: params.bootstrapFiles ?? [],
|
|
184
|
+
skillsPrompt: params.skillsPrompt ?? "",
|
|
185
|
+
};
|
|
181
186
|
}
|
|
182
187
|
export function normalizeCliModel(modelId, backend) {
|
|
183
188
|
const trimmed = modelId.trim();
|
|
@@ -10,7 +10,9 @@ import { makeBootstrapWarn, resolveBootstrapContextForRun } from "./bootstrap-fi
|
|
|
10
10
|
import { resolveCliBackendConfig } from "./cli-backends.js";
|
|
11
11
|
import { appendImagePathsToPrompt, buildCliArgs, buildSystemPrompt, cleanupResumeProcesses, cleanupSuspendedCliProcesses, enqueueCliRun, normalizeCliModel, parseCliJson, parseCliJsonl, resolvePromptInput, resolveSessionIdToSend, resolveSystemPromptUsage, writeCliImages, } from "./cli-runner/helpers.js";
|
|
12
12
|
import { FailoverError, resolveFailoverStatus } from "./failover-error.js";
|
|
13
|
-
import { classifyFailoverReason, isFailoverErrorMessage } from "./pi-embedded-helpers.js";
|
|
13
|
+
import { classifyFailoverReason, isFailoverErrorMessage, resolveBootstrapMaxChars, } from "./pi-embedded-helpers.js";
|
|
14
|
+
import { buildSystemPromptReport } from "./system-prompt-report.js";
|
|
15
|
+
import { writeSystemPromptLog } from "./system-prompt-log.js";
|
|
14
16
|
const log = createSubsystemLogger("agent/claude-cli");
|
|
15
17
|
export async function runCliAgent(params) {
|
|
16
18
|
const started = Date.now();
|
|
@@ -31,7 +33,7 @@ export async function runCliAgent(params) {
|
|
|
31
33
|
.filter(Boolean)
|
|
32
34
|
.join("\n");
|
|
33
35
|
const sessionLabel = params.sessionKey ?? params.sessionId;
|
|
34
|
-
const { contextFiles } = await resolveBootstrapContextForRun({
|
|
36
|
+
const { bootstrapFiles, contextFiles } = await resolveBootstrapContextForRun({
|
|
35
37
|
workspaceDir,
|
|
36
38
|
config: params.config,
|
|
37
39
|
sessionKey: params.sessionKey,
|
|
@@ -51,7 +53,7 @@ export async function runCliAgent(params) {
|
|
|
51
53
|
cwd: process.cwd(),
|
|
52
54
|
moduleUrl: import.meta.url,
|
|
53
55
|
});
|
|
54
|
-
const systemPrompt = buildSystemPrompt({
|
|
56
|
+
const { prompt: systemPrompt, skillsPrompt } = buildSystemPrompt({
|
|
55
57
|
workspaceDir,
|
|
56
58
|
config: params.config,
|
|
57
59
|
defaultThinkLevel: params.thinkLevel,
|
|
@@ -63,7 +65,28 @@ export async function runCliAgent(params) {
|
|
|
63
65
|
contextFiles,
|
|
64
66
|
modelDisplay,
|
|
65
67
|
agentId: sessionAgentId,
|
|
68
|
+
bootstrapFiles,
|
|
66
69
|
});
|
|
70
|
+
const systemPromptReport = buildSystemPromptReport({
|
|
71
|
+
source: "run",
|
|
72
|
+
generatedAt: Date.now(),
|
|
73
|
+
sessionId: params.sessionId,
|
|
74
|
+
sessionKey: params.sessionKey,
|
|
75
|
+
provider: params.provider,
|
|
76
|
+
model: modelId,
|
|
77
|
+
workspaceDir,
|
|
78
|
+
bootstrapMaxChars: resolveBootstrapMaxChars(params.config),
|
|
79
|
+
systemPrompt,
|
|
80
|
+
bootstrapFiles,
|
|
81
|
+
injectedFiles: contextFiles,
|
|
82
|
+
skillsPrompt,
|
|
83
|
+
tools: [],
|
|
84
|
+
});
|
|
85
|
+
// Fire-and-forget: log full system prompt to disk
|
|
86
|
+
const sessionKeyForLog = params.sessionKey ?? params.sessionId;
|
|
87
|
+
if (sessionKeyForLog) {
|
|
88
|
+
writeSystemPromptLog(sessionKeyForLog, systemPrompt).catch(() => { });
|
|
89
|
+
}
|
|
67
90
|
const { sessionId: cliSessionIdToSend, isNew } = resolveSessionIdToSend({
|
|
68
91
|
backend,
|
|
69
92
|
cliSessionId: params.cliSessionId,
|
|
@@ -219,6 +242,7 @@ export async function runCliAgent(params) {
|
|
|
219
242
|
model: modelId,
|
|
220
243
|
usage: output.usage,
|
|
221
244
|
},
|
|
245
|
+
systemPromptReport,
|
|
222
246
|
},
|
|
223
247
|
};
|
|
224
248
|
}
|
|
@@ -32,6 +32,7 @@ import { acquireSessionWriteLock } from "../../session-write-lock.js";
|
|
|
32
32
|
import { applySkillEnvOverrides, applySkillEnvOverridesFromSnapshot, loadWorkspaceSkillEntries, resolveSkillsPromptForRun, } from "../../skills.js";
|
|
33
33
|
import { DEFAULT_BOOTSTRAP_FILENAME } from "../../workspace.js";
|
|
34
34
|
import { buildSystemPromptReport } from "../../system-prompt-report.js";
|
|
35
|
+
import { writeSystemPromptLog } from "../../system-prompt-log.js";
|
|
35
36
|
import { resolveDefaultModelForAgent } from "../../model-selection.js";
|
|
36
37
|
import { isAbortError } from "../abort.js";
|
|
37
38
|
import { buildEmbeddedExtensionPaths } from "../extensions.js";
|
|
@@ -326,6 +327,11 @@ export async function runEmbeddedAttempt(params) {
|
|
|
326
327
|
tools,
|
|
327
328
|
});
|
|
328
329
|
const systemPrompt = createSystemPromptOverride(appendPrompt);
|
|
330
|
+
// Fire-and-forget: log the full system prompt text to disk
|
|
331
|
+
const sessionKeyForLog = params.sessionKey ?? params.sessionId;
|
|
332
|
+
if (sessionKeyForLog) {
|
|
333
|
+
writeSystemPromptLog(sessionKeyForLog, appendPrompt).catch(() => { });
|
|
334
|
+
}
|
|
329
335
|
const sessionLock = await acquireSessionWriteLock({
|
|
330
336
|
sessionFile: params.sessionFile,
|
|
331
337
|
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
|
+
const log = createSubsystemLogger("system-prompt-log");
|
|
6
|
+
const DEFAULT_DIR = path.join(os.homedir(), ".taskmaster", "logs", "system-prompts");
|
|
7
|
+
const MAX_KEY_LENGTH = 128;
|
|
8
|
+
export function sanitiseSessionKey(key) {
|
|
9
|
+
const cleaned = key.replace(/[:/\\]/g, "_").trim();
|
|
10
|
+
if (!cleaned)
|
|
11
|
+
return "_unknown_";
|
|
12
|
+
return cleaned.slice(0, MAX_KEY_LENGTH);
|
|
13
|
+
}
|
|
14
|
+
export async function writeSystemPromptLog(sessionKey, systemPrompt, dir = DEFAULT_DIR) {
|
|
15
|
+
try {
|
|
16
|
+
const fileName = `${sanitiseSessionKey(sessionKey)}.txt`;
|
|
17
|
+
await fs.mkdir(dir, { recursive: true });
|
|
18
|
+
await fs.writeFile(path.join(dir, fileName), systemPrompt, "utf-8");
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22
|
+
log.warn(`Failed to write system prompt log: ${message}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -300,9 +300,17 @@ export function buildAgentSystemPrompt(params) {
|
|
|
300
300
|
"- cron: manage scheduled events and wake events (use for reminders; when scheduling a reminder, write the systemEvent text as something that will read like a reminder when it fires, and mention that it is a reminder depending on the time gap between setting and firing; include recent context in reminder text if appropriate)",
|
|
301
301
|
"- sessions_list: list sessions",
|
|
302
302
|
"- sessions_history: fetch session history",
|
|
303
|
+
"- sessions_spawn: spawn a background sub-agent for a task (returns immediately; result is announced when done)",
|
|
303
304
|
].join("\n"),
|
|
304
305
|
"TOOLS.md does not control tool availability; it is user guidance for how to use external tools.",
|
|
305
|
-
"
|
|
306
|
+
"## Delegation",
|
|
307
|
+
"Your default mode is to delegate work to sub-agents. Any task beyond a straightforward conversational reply should be handled by a sub-agent via `sessions_spawn`.",
|
|
308
|
+
"Delegation keeps you responsive — you hand off the work and continue chatting. The sub-agent runs in the background; when it finishes, its result is announced back into this conversation automatically.",
|
|
309
|
+
"",
|
|
310
|
+
"**Delegate when the task involves:** tool calls (file reads/writes, memory operations, web searches, document reading, image generation, research, calculations, composing longer content, or any multi-step work).",
|
|
311
|
+
"**Handle inline only when:** the response is purely conversational — a greeting, a quick factual answer from your own knowledge, a yes/no, or a brief opinion that requires no tool calls.",
|
|
312
|
+
"",
|
|
313
|
+
"When spawning, provide a clear `task` description and a short `label` so you can track it. You don't need to tell the user you're delegating — just do it. The result will flow back naturally.",
|
|
306
314
|
"",
|
|
307
315
|
"## Tool Call Style",
|
|
308
316
|
"Every text block you produce is delivered as a separate message. Unnecessary text blocks = unnecessary messages sent to the user.",
|
|
@@ -29,7 +29,12 @@ const PUBLIC_AGENT_INDICATORS = [
|
|
|
29
29
|
/** Formatting section headers commonly found at the end of AGENTS files. */
|
|
30
30
|
const FORMATTING_HEADERS = ["## WhatsApp Formatting", "## Message Formatting", "## Formatting"];
|
|
31
31
|
/** Section headers that typically precede "## Capabilities" in admin agents. */
|
|
32
|
-
const CAPABILITIES_HEADERS = [
|
|
32
|
+
const CAPABILITIES_HEADERS = [
|
|
33
|
+
"## Capabilities",
|
|
34
|
+
"## Tools",
|
|
35
|
+
"## Stripe CLI Operations",
|
|
36
|
+
"## User Management",
|
|
37
|
+
];
|
|
33
38
|
function isPublicAgent(content) {
|
|
34
39
|
return PUBLIC_AGENT_INDICATORS.some((p) => content.includes(p));
|
|
35
40
|
}
|
|
@@ -703,6 +708,67 @@ async function patchBeagleAdminDriverContactTone(_agentsPath, content) {
|
|
|
703
708
|
return null;
|
|
704
709
|
return content.replace(ADMIN_DRIVER_CONTACT_OLD, ADMIN_DRIVER_CONTACT_NEW);
|
|
705
710
|
}
|
|
711
|
+
// ---------------------------------------------------------------------------
|
|
712
|
+
// Migration: Sub-Agent Delegation (v1.24)
|
|
713
|
+
// ---------------------------------------------------------------------------
|
|
714
|
+
const DELEGATION_SECTION = `## Delegation — Sub-Agents
|
|
715
|
+
|
|
716
|
+
**Default to delegation.** Any task that requires tool calls — reading files, searching memory, generating content, web lookups, document processing, image work — should be spawned as a sub-agent using \`sessions_spawn\`. You stay free to keep chatting with the business owner.
|
|
717
|
+
|
|
718
|
+
**How it works:**
|
|
719
|
+
1. Call \`sessions_spawn\` with a clear \`task\` and a short \`label\`
|
|
720
|
+
2. The sub-agent runs in the background with the same tools and memory access you have
|
|
721
|
+
3. When it finishes, its result is announced back into your conversation automatically
|
|
722
|
+
4. You summarise the result naturally — don't mention sub-agents or background tasks
|
|
723
|
+
|
|
724
|
+
**Delegate:**
|
|
725
|
+
- Memory searches and lookups
|
|
726
|
+
- File reading/writing
|
|
727
|
+
- Document reading (PDFs, DOCX, etc.)
|
|
728
|
+
- Web searches and fetches
|
|
729
|
+
- Content creation (drafts, summaries, reports)
|
|
730
|
+
- Image generation
|
|
731
|
+
- Multi-step research tasks
|
|
732
|
+
- Anything that takes more than a moment
|
|
733
|
+
|
|
734
|
+
**Handle inline (no delegation):**
|
|
735
|
+
- Quick conversational replies (greetings, opinions, yes/no)
|
|
736
|
+
- Brief factual answers you already know
|
|
737
|
+
- Clarifying questions before starting work
|
|
738
|
+
- Confirming or acknowledging something the owner said
|
|
739
|
+
|
|
740
|
+
When multiple independent tasks come up, spawn multiple sub-agents in parallel. Don't wait for one to finish before starting the next.`;
|
|
741
|
+
function hasDelegationSection(content) {
|
|
742
|
+
return content.includes("## Delegation") || content.includes("sessions_spawn");
|
|
743
|
+
}
|
|
744
|
+
async function patchDelegationSection(_agentsPath, content) {
|
|
745
|
+
if (isPublicAgent(content))
|
|
746
|
+
return null;
|
|
747
|
+
if (hasDelegationSection(content))
|
|
748
|
+
return null;
|
|
749
|
+
return insertSection(content, DELEGATION_SECTION, findCapabilitiesInsertPoint);
|
|
750
|
+
}
|
|
751
|
+
// ---------------------------------------------------------------------------
|
|
752
|
+
// SOUL.md Migration: Sub-Agent Delegation (v1.24)
|
|
753
|
+
// ---------------------------------------------------------------------------
|
|
754
|
+
const SOUL_DELEGATION_SECTION = `## How You Work
|
|
755
|
+
|
|
756
|
+
**Delegate everything that isn't a quick reply.** Your role is to stay responsive — thinking, conversing, and directing. Actual work (research, file operations, document reading, content creation, memory lookups, calculations) gets handed to sub-agents via \`sessions_spawn\`. They run in the background and report back when done. You keep chatting.`;
|
|
757
|
+
function soulHasDelegation(content) {
|
|
758
|
+
return content.includes("## How You Work") || content.includes("sessions_spawn");
|
|
759
|
+
}
|
|
760
|
+
async function patchSoulDelegation(content) {
|
|
761
|
+
if (soulHasDelegation(content))
|
|
762
|
+
return null;
|
|
763
|
+
// Insert before "## What You Don't Do" if present
|
|
764
|
+
const insertBefore = "## What You Don't Do";
|
|
765
|
+
const idx = content.indexOf(insertBefore);
|
|
766
|
+
if (idx !== -1) {
|
|
767
|
+
return content.slice(0, idx) + SOUL_DELEGATION_SECTION + "\n\n" + content.slice(idx);
|
|
768
|
+
}
|
|
769
|
+
// Fallback: append
|
|
770
|
+
return content.trimEnd() + "\n\n" + SOUL_DELEGATION_SECTION + "\n";
|
|
771
|
+
}
|
|
706
772
|
const MIGRATIONS = [
|
|
707
773
|
{ name: "skill-recommendations", apply: patchSkillRecommendations },
|
|
708
774
|
{ name: "owner-learning", apply: patchOwnerLearning },
|
|
@@ -720,6 +786,7 @@ const MIGRATIONS = [
|
|
|
720
786
|
{ name: "beagle-public-tourist-phone", apply: patchBeaglePublicTouristPhone },
|
|
721
787
|
{ name: "beagle-admin-driver-reply-explicit", apply: patchBeagleAdminDriverReplyExplicit },
|
|
722
788
|
{ name: "beagle-admin-driver-contact-tone", apply: patchBeagleAdminDriverContactTone },
|
|
789
|
+
{ name: "delegation-section", apply: patchDelegationSection },
|
|
723
790
|
];
|
|
724
791
|
/**
|
|
725
792
|
* Run all workspace migrations for every configured agent.
|
|
@@ -760,5 +827,20 @@ export async function runWorkspaceMigrations(cfg) {
|
|
|
760
827
|
log.warn(`failed to write AGENTS.md for agent "${agentId}": ${String(err)}`);
|
|
761
828
|
}
|
|
762
829
|
}
|
|
830
|
+
// SOUL.md migrations (admin agents only)
|
|
831
|
+
const soulPath = path.join(wsDir, "SOUL.md");
|
|
832
|
+
try {
|
|
833
|
+
const soulContent = await fs.readFile(soulPath, "utf-8");
|
|
834
|
+
if (!isPublicAgent(content)) {
|
|
835
|
+
const soulResult = await patchSoulDelegation(soulContent);
|
|
836
|
+
if (soulResult !== null) {
|
|
837
|
+
await fs.writeFile(soulPath, soulResult, "utf-8");
|
|
838
|
+
log.info(`patched SOUL.md for agent "${agentId}": delegation`);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch {
|
|
843
|
+
// No SOUL.md or read error — skip.
|
|
844
|
+
}
|
|
763
845
|
}
|
|
764
846
|
}
|
package/dist/build-info.json
CHANGED
|
@@ -294,6 +294,41 @@ export function reconcileSkillReadTool(params) {
|
|
|
294
294
|
}
|
|
295
295
|
return { config, changes };
|
|
296
296
|
}
|
|
297
|
+
/**
|
|
298
|
+
* Add `sessions_spawn` to admin agents whose explicit allow list has
|
|
299
|
+
* `sessions_list` but lacks `sessions_spawn`.
|
|
300
|
+
*
|
|
301
|
+
* `sessions_spawn` was added to `group:sessions` but most admin agents use
|
|
302
|
+
* individual tool names rather than the group reference. This reconciliation
|
|
303
|
+
* patches them on gateway startup so sub-agent delegation works.
|
|
304
|
+
*
|
|
305
|
+
* Runs unconditionally on gateway startup. Idempotent — skips agents that
|
|
306
|
+
* already have `sessions_spawn` or `group:sessions`.
|
|
307
|
+
*/
|
|
308
|
+
export function reconcileSessionsSpawnTool(params) {
|
|
309
|
+
const config = structuredClone(params.config);
|
|
310
|
+
const changes = [];
|
|
311
|
+
const agents = config.agents?.list;
|
|
312
|
+
if (!Array.isArray(agents))
|
|
313
|
+
return { config, changes };
|
|
314
|
+
for (const agent of agents) {
|
|
315
|
+
if (!agent || !isAdminAgent(agent))
|
|
316
|
+
continue;
|
|
317
|
+
const allow = agent.tools?.allow;
|
|
318
|
+
if (!Array.isArray(allow))
|
|
319
|
+
continue;
|
|
320
|
+
// Already covered
|
|
321
|
+
if (allow.includes("sessions_spawn") || allow.includes("group:sessions"))
|
|
322
|
+
continue;
|
|
323
|
+
// Only patch agents that have sessions_list (confirms they use individual session tool names)
|
|
324
|
+
if (!allow.includes("sessions_list"))
|
|
325
|
+
continue;
|
|
326
|
+
const idx = allow.indexOf("sessions_list");
|
|
327
|
+
allow.splice(idx + 1, 0, "sessions_spawn");
|
|
328
|
+
changes.push(`Added sessions_spawn to agent "${agent.id ?? "<unnamed>"}" tools.allow.`);
|
|
329
|
+
}
|
|
330
|
+
return { config, changes };
|
|
331
|
+
}
|
|
297
332
|
/**
|
|
298
333
|
* Individual skill tool names that should be replaced by `group:skills`.
|
|
299
334
|
* Matches the members of TOOL_GROUPS["group:skills"] in tool-policy.ts.
|