@rubytech/taskmaster 1.24.0 → 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/build-info.json +3 -3
- 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
|
@@ -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
|
+
}
|
package/dist/build-info.json
CHANGED
package/dist/logging/logger.js
CHANGED
|
@@ -12,7 +12,7 @@ export const DEFAULT_LOG_DIR = "/tmp/taskmaster";
|
|
|
12
12
|
export const DEFAULT_LOG_FILE = path.join(DEFAULT_LOG_DIR, "taskmaster.log"); // legacy single-file path
|
|
13
13
|
const LOG_PREFIX = "taskmaster";
|
|
14
14
|
const LOG_SUFFIX = ".log";
|
|
15
|
-
const MAX_LOG_AGE_MS = 24 * 60 * 60 * 1000; //
|
|
15
|
+
const MAX_LOG_AGE_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
16
16
|
const requireConfig = createRequire(import.meta.url);
|
|
17
17
|
const externalTransports = new Set();
|
|
18
18
|
function attachExternalTransport(logger, transport) {
|
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -214,6 +214,39 @@ WATCHTMR
|
|
|
214
214
|
as_root systemctl enable --now avahi-watchdog.timer 2>/dev/null || true
|
|
215
215
|
echo " mDNS watchdog enabled"
|
|
216
216
|
|
|
217
|
+
# Wi-Fi power management off — prevents radio stalls under sustained traffic (e.g. Tailscale)
|
|
218
|
+
as_root tee /etc/udev/rules.d/70-wifi-powersave.rules > /dev/null << 'UDEVRULE'
|
|
219
|
+
ACTION=="add", SUBSYSTEM=="net", KERNEL=="wlan*", RUN+="/usr/sbin/iw dev $name set power_save off"
|
|
220
|
+
UDEVRULE
|
|
221
|
+
iw dev wlan0 set power_save off 2>/dev/null || true
|
|
222
|
+
echo " Wi-Fi power save disabled"
|
|
223
|
+
|
|
224
|
+
# Wi-Fi watchdog — reconnects wlan0 if the radio stalls (interface up but no connectivity)
|
|
225
|
+
as_root tee /etc/systemd/system/wifi-watchdog.service > /dev/null << 'WIFISVC'
|
|
226
|
+
[Unit]
|
|
227
|
+
Description=Reconnect Wi-Fi if connectivity is lost
|
|
228
|
+
|
|
229
|
+
[Service]
|
|
230
|
+
Type=oneshot
|
|
231
|
+
ExecCondition=/bin/sh -c '[ -d /sys/class/net/wlan0 ] && ! ping -c 1 -W 5 1.1.1.1 >/dev/null 2>&1'
|
|
232
|
+
ExecStart=/usr/bin/nmcli device disconnect wlan0
|
|
233
|
+
ExecStart=/usr/bin/nmcli device connect wlan0
|
|
234
|
+
WIFISVC
|
|
235
|
+
as_root tee /etc/systemd/system/wifi-watchdog.timer > /dev/null << 'WIFITMR'
|
|
236
|
+
[Unit]
|
|
237
|
+
Description=Check Wi-Fi connectivity every 2 minutes
|
|
238
|
+
|
|
239
|
+
[Timer]
|
|
240
|
+
OnBootSec=3min
|
|
241
|
+
OnUnitActiveSec=2min
|
|
242
|
+
|
|
243
|
+
[Install]
|
|
244
|
+
WantedBy=timers.target
|
|
245
|
+
WIFITMR
|
|
246
|
+
as_root systemctl daemon-reload
|
|
247
|
+
as_root systemctl enable --now wifi-watchdog.timer 2>/dev/null || true
|
|
248
|
+
echo " Wi-Fi watchdog enabled"
|
|
249
|
+
|
|
217
250
|
# Tailscale (for optional internet access via Funnel)
|
|
218
251
|
if ! command -v tailscale >/dev/null 2>&1; then
|
|
219
252
|
curl -fsSL https://tailscale.com/install.sh | as_root sh >/dev/null 2>&1 \
|
|
@@ -1145,7 +1145,18 @@ If your control panel URL suddenly stops working and you notice the hostname has
|
|
|
1145
1145
|
|
|
1146
1146
|
If you can reach your device by IP address (e.g. `http://192.168.88.11:18789`) but the `.local` address has stopped resolving, the mDNS service (Avahi) on the Pi has likely lost its network registration. This can happen after a Wi-Fi reconnection — the service is still running but has stopped responding to name lookups.
|
|
1147
1147
|
|
|
1148
|
-
Taskmaster includes an mDNS watchdog that detects this and restarts the service automatically every 5 minutes. If your device is on v1.
|
|
1148
|
+
Taskmaster includes an mDNS watchdog that detects this and restarts the service automatically every 5 minutes. If your device is on v1.23 or later, this should self-heal within a few minutes. If it doesn't, reboot the device — the watchdog will be active after the reboot.
|
|
1149
|
+
|
|
1150
|
+
#### Device completely unreachable (not even by IP)
|
|
1151
|
+
|
|
1152
|
+
If your Pi is powered on and connected to Wi-Fi but you can't reach it at all — not by `.local` name, not by IP address, and not even by ping — the Wi-Fi radio has likely stalled. This is a known Raspberry Pi hardware issue where the Wi-Fi chipset stops passing packets while still reporting as "connected". It can be triggered by sustained network traffic, including Tailscale's always-on tunnel.
|
|
1153
|
+
|
|
1154
|
+
Taskmaster v1.23+ includes two protections that prevent this:
|
|
1155
|
+
|
|
1156
|
+
- **Wi-Fi power save is disabled** — this prevents most radio stalls by keeping the chipset active
|
|
1157
|
+
- **A Wi-Fi watchdog** checks connectivity every 2 minutes and automatically reconnects Wi-Fi if traffic has stopped flowing
|
|
1158
|
+
|
|
1159
|
+
If your device is on v1.23 or later, connectivity should self-heal within 2 minutes. If you're on an older version, updating (`taskmaster update`) will install these protections automatically.
|
|
1149
1160
|
|
|
1150
1161
|
### Static IP Address
|
|
1151
1162
|
|