@questionbase/deskfree 0.4.5 → 0.5.0
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/bin.js +1069 -409
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.js +143 -16
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/uninstall.js +43 -9
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +418 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { execSync, execFileSync, execFile } from 'child_process';
|
|
4
|
-
import { writeFileSync, chmodSync, existsSync, unlinkSync,
|
|
3
|
+
import { spawn, execSync, execFileSync, execFile } from 'child_process';
|
|
4
|
+
import { mkdirSync, writeFileSync, chmodSync, existsSync, unlinkSync, appendFileSync, readdirSync, readFileSync, statSync, createWriteStream } from 'fs';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
|
+
import { dirname, join, extname } from 'path';
|
|
5
7
|
import { createRequire as createRequire$1 } from 'module';
|
|
6
|
-
import { join, dirname, extname } from 'path';
|
|
7
8
|
import { query, createSdkMcpServer, tool } from '@anthropic-ai/claude-agent-sdk';
|
|
8
9
|
import { z } from 'zod';
|
|
9
10
|
import { appendFile, readFile, mkdir, unlink } from 'fs/promises';
|
|
@@ -57,22 +58,123 @@ var install_exports = {};
|
|
|
57
58
|
__export(install_exports, {
|
|
58
59
|
install: () => install
|
|
59
60
|
});
|
|
60
|
-
function
|
|
61
|
+
function getMacPaths() {
|
|
62
|
+
const home = homedir();
|
|
63
|
+
const deskfreeDir = join(home, ".deskfree");
|
|
64
|
+
return {
|
|
65
|
+
deskfreeDir,
|
|
66
|
+
envFile: join(deskfreeDir, ".env"),
|
|
67
|
+
launcher: join(deskfreeDir, "launcher.sh"),
|
|
68
|
+
logDir: join(deskfreeDir, "logs"),
|
|
69
|
+
plist: join(home, "Library", "LaunchAgents", `${PLIST_LABEL}.plist`)
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function installMac(token) {
|
|
73
|
+
const paths = getMacPaths();
|
|
74
|
+
let nodeBinDir;
|
|
75
|
+
try {
|
|
76
|
+
const nodePath = execSync("which node", { encoding: "utf8" }).trim();
|
|
77
|
+
nodeBinDir = dirname(nodePath);
|
|
78
|
+
} catch {
|
|
79
|
+
console.error("Error: node not found in PATH");
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
mkdirSync(paths.deskfreeDir, { recursive: true });
|
|
83
|
+
mkdirSync(paths.logDir, { recursive: true });
|
|
84
|
+
mkdirSync(dirname(paths.plist), { recursive: true });
|
|
85
|
+
writeFileSync(paths.envFile, `DESKFREE_LAUNCH=${token}
|
|
86
|
+
`, { mode: 384 });
|
|
87
|
+
chmodSync(paths.envFile, 384);
|
|
88
|
+
console.log(`Wrote ${paths.envFile}`);
|
|
89
|
+
const launcher = `#!/bin/bash
|
|
90
|
+
set -euo pipefail
|
|
91
|
+
|
|
92
|
+
export PATH="${nodeBinDir}:$PATH"
|
|
93
|
+
|
|
94
|
+
# Update to latest version before starting
|
|
95
|
+
npm install -g ${PACKAGE} 2>/dev/null || true
|
|
96
|
+
|
|
97
|
+
# Source env
|
|
98
|
+
set -a
|
|
99
|
+
source "${paths.envFile}"
|
|
100
|
+
set +a
|
|
101
|
+
|
|
102
|
+
exec deskfree-agent start
|
|
103
|
+
`;
|
|
104
|
+
writeFileSync(paths.launcher, launcher, { mode: 493 });
|
|
105
|
+
chmodSync(paths.launcher, 493);
|
|
106
|
+
console.log(`Wrote ${paths.launcher}`);
|
|
107
|
+
const plist = `<?xml version="1.0" encoding="UTF-8"?>
|
|
108
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
109
|
+
<plist version="1.0">
|
|
110
|
+
<dict>
|
|
111
|
+
<key>Label</key>
|
|
112
|
+
<string>${PLIST_LABEL}</string>
|
|
113
|
+
<key>ProgramArguments</key>
|
|
114
|
+
<array>
|
|
115
|
+
<string>${paths.launcher}</string>
|
|
116
|
+
</array>
|
|
117
|
+
<key>KeepAlive</key>
|
|
118
|
+
<true/>
|
|
119
|
+
<key>RunAtLoad</key>
|
|
120
|
+
<true/>
|
|
121
|
+
<key>StandardOutPath</key>
|
|
122
|
+
<string>${join(paths.logDir, "stdout.log")}</string>
|
|
123
|
+
<key>StandardErrorPath</key>
|
|
124
|
+
<string>${join(paths.logDir, "stderr.log")}</string>
|
|
125
|
+
<key>ThrottleInterval</key>
|
|
126
|
+
<integer>10</integer>
|
|
127
|
+
</dict>
|
|
128
|
+
</plist>
|
|
129
|
+
`;
|
|
130
|
+
writeFileSync(paths.plist, plist);
|
|
131
|
+
console.log(`Wrote ${paths.plist}`);
|
|
132
|
+
try {
|
|
133
|
+
execSync(`launchctl bootout gui/$(id -u) ${paths.plist}`, {
|
|
134
|
+
stdio: "ignore"
|
|
135
|
+
});
|
|
136
|
+
} catch {
|
|
137
|
+
}
|
|
138
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${paths.plist}`);
|
|
139
|
+
console.log(`
|
|
140
|
+
Service ${PLIST_LABEL} installed and started.`);
|
|
141
|
+
console.log(`Check status: launchctl print gui/$(id -u)/${PLIST_LABEL}`);
|
|
142
|
+
console.log(`Logs: tail -f ${join(paths.logDir, "stdout.log")}`);
|
|
143
|
+
}
|
|
144
|
+
function installLinux(token) {
|
|
61
145
|
if (process.getuid?.() !== 0) {
|
|
62
146
|
console.error("Error: install must be run as root (use sudo)");
|
|
63
147
|
process.exit(1);
|
|
64
148
|
}
|
|
65
|
-
let
|
|
149
|
+
let npmPath;
|
|
150
|
+
let nodeBinDir;
|
|
66
151
|
try {
|
|
67
|
-
|
|
152
|
+
npmPath = execSync("which npm", { encoding: "utf8" }).trim();
|
|
153
|
+
nodeBinDir = dirname(npmPath);
|
|
68
154
|
} catch {
|
|
69
|
-
console.error("Error:
|
|
155
|
+
console.error("Error: npm not found in PATH");
|
|
70
156
|
process.exit(1);
|
|
71
157
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
158
|
+
try {
|
|
159
|
+
execSync("id deskfree-agent", { stdio: "ignore" });
|
|
160
|
+
} catch {
|
|
161
|
+
execSync(
|
|
162
|
+
"useradd --system --no-create-home --shell /usr/sbin/nologin deskfree-agent"
|
|
163
|
+
);
|
|
164
|
+
console.log("Created system user: deskfree-agent");
|
|
165
|
+
}
|
|
166
|
+
mkdirSync(LINUX_STATE_DIR, { recursive: true });
|
|
167
|
+
mkdirSync(LINUX_LOG_DIR, { recursive: true });
|
|
168
|
+
execSync(
|
|
169
|
+
`chown deskfree-agent:deskfree-agent ${LINUX_STATE_DIR} ${LINUX_LOG_DIR}`
|
|
170
|
+
);
|
|
171
|
+
console.log(`Created ${LINUX_STATE_DIR} and ${LINUX_LOG_DIR}`);
|
|
172
|
+
writeFileSync(SYSTEMD_ENV_FILE, `DESKFREE_LAUNCH=${token}
|
|
173
|
+
`, {
|
|
174
|
+
mode: 384
|
|
175
|
+
});
|
|
176
|
+
chmodSync(SYSTEMD_ENV_FILE, 384);
|
|
177
|
+
console.log(`Wrote ${SYSTEMD_ENV_FILE}`);
|
|
76
178
|
const unit = `[Unit]
|
|
77
179
|
Description=DeskFree Agent
|
|
78
180
|
After=network-online.target
|
|
@@ -80,30 +182,54 @@ Wants=network-online.target
|
|
|
80
182
|
|
|
81
183
|
[Service]
|
|
82
184
|
Type=simple
|
|
83
|
-
|
|
84
|
-
|
|
185
|
+
User=deskfree-agent
|
|
186
|
+
Group=deskfree-agent
|
|
187
|
+
WorkingDirectory=${LINUX_STATE_DIR}
|
|
188
|
+
Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
|
|
189
|
+
ExecStartPre=+${npmPath} install -g ${PACKAGE}
|
|
190
|
+
ExecStart=${nodeBinDir}/deskfree-agent start
|
|
191
|
+
EnvironmentFile=${SYSTEMD_ENV_FILE}
|
|
85
192
|
Environment=NODE_ENV=production
|
|
193
|
+
Environment=DESKFREE_STATE_DIR=${LINUX_STATE_DIR}
|
|
194
|
+
Environment=DESKFREE_TOOLS_DIR=${LINUX_STATE_DIR}/tools
|
|
86
195
|
Restart=always
|
|
87
196
|
RestartSec=10
|
|
197
|
+
StandardOutput=append:${LINUX_LOG_DIR}/stdout.log
|
|
198
|
+
StandardError=append:${LINUX_LOG_DIR}/stderr.log
|
|
88
199
|
|
|
89
200
|
[Install]
|
|
90
201
|
WantedBy=multi-user.target
|
|
91
202
|
`;
|
|
92
|
-
writeFileSync(
|
|
93
|
-
console.log(`Wrote ${
|
|
203
|
+
writeFileSync(SYSTEMD_SERVICE_FILE, unit);
|
|
204
|
+
console.log(`Wrote ${SYSTEMD_SERVICE_FILE}`);
|
|
94
205
|
execSync("systemctl daemon-reload");
|
|
95
206
|
execSync(`systemctl enable ${SERVICE_NAME}`);
|
|
96
207
|
execSync(`systemctl start ${SERVICE_NAME}`);
|
|
97
|
-
console.log(`
|
|
208
|
+
console.log(`
|
|
209
|
+
Service ${SERVICE_NAME} installed and started.`);
|
|
98
210
|
console.log(`Check status: systemctl status ${SERVICE_NAME}`);
|
|
211
|
+
console.log(`Logs: tail -f ${LINUX_LOG_DIR}/stdout.log`);
|
|
99
212
|
}
|
|
100
|
-
|
|
213
|
+
function install(token) {
|
|
214
|
+
if (process.platform === "darwin") {
|
|
215
|
+
installMac(token);
|
|
216
|
+
} else if (process.platform === "linux") {
|
|
217
|
+
installLinux(token);
|
|
218
|
+
} else {
|
|
219
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
var SERVICE_NAME, PACKAGE, PLIST_LABEL, SYSTEMD_SERVICE_FILE, SYSTEMD_ENV_FILE, LINUX_STATE_DIR, LINUX_LOG_DIR;
|
|
101
224
|
var init_install = __esm({
|
|
102
225
|
"src/cli/install.ts"() {
|
|
103
226
|
SERVICE_NAME = "deskfree-agent";
|
|
104
|
-
SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
105
|
-
ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
106
227
|
PACKAGE = "@questionbase/deskfree@latest";
|
|
228
|
+
PLIST_LABEL = "com.deskfree.agent";
|
|
229
|
+
SYSTEMD_SERVICE_FILE = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
230
|
+
SYSTEMD_ENV_FILE = `/etc/${SERVICE_NAME}.env`;
|
|
231
|
+
LINUX_STATE_DIR = "/var/lib/deskfree-agent";
|
|
232
|
+
LINUX_LOG_DIR = "/var/log/deskfree-agent";
|
|
107
233
|
}
|
|
108
234
|
});
|
|
109
235
|
|
|
@@ -112,7 +238,28 @@ var uninstall_exports = {};
|
|
|
112
238
|
__export(uninstall_exports, {
|
|
113
239
|
uninstall: () => uninstall
|
|
114
240
|
});
|
|
115
|
-
function
|
|
241
|
+
function uninstallMac() {
|
|
242
|
+
const home = homedir();
|
|
243
|
+
const plist = join(home, "Library", "LaunchAgents", `${PLIST_LABEL2}.plist`);
|
|
244
|
+
const deskfreeDir = join(home, ".deskfree");
|
|
245
|
+
const envFile = join(deskfreeDir, ".env");
|
|
246
|
+
const launcher = join(deskfreeDir, "launcher.sh");
|
|
247
|
+
try {
|
|
248
|
+
execSync(`launchctl bootout gui/$(id -u) ${plist}`, { stdio: "ignore" });
|
|
249
|
+
} catch {
|
|
250
|
+
}
|
|
251
|
+
for (const file of [plist, envFile, launcher]) {
|
|
252
|
+
if (existsSync(file)) {
|
|
253
|
+
unlinkSync(file);
|
|
254
|
+
console.log(`Removed ${file}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
console.log(`Service ${PLIST_LABEL2} uninstalled.`);
|
|
258
|
+
console.log(
|
|
259
|
+
`Note: logs and state in ${deskfreeDir} were preserved. Remove manually if desired.`
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
function uninstallLinux() {
|
|
116
263
|
if (process.getuid?.() !== 0) {
|
|
117
264
|
console.error("Error: uninstall must be run as root (use sudo)");
|
|
118
265
|
process.exit(1);
|
|
@@ -125,23 +272,203 @@ function uninstall() {
|
|
|
125
272
|
execSync(`systemctl disable ${SERVICE_NAME2}`, { stdio: "ignore" });
|
|
126
273
|
} catch {
|
|
127
274
|
}
|
|
128
|
-
if (existsSync(
|
|
129
|
-
unlinkSync(
|
|
130
|
-
console.log(`Removed ${
|
|
275
|
+
if (existsSync(SYSTEMD_SERVICE_FILE2)) {
|
|
276
|
+
unlinkSync(SYSTEMD_SERVICE_FILE2);
|
|
277
|
+
console.log(`Removed ${SYSTEMD_SERVICE_FILE2}`);
|
|
131
278
|
}
|
|
132
|
-
if (existsSync(
|
|
133
|
-
unlinkSync(
|
|
134
|
-
console.log(`Removed ${
|
|
279
|
+
if (existsSync(SYSTEMD_ENV_FILE2)) {
|
|
280
|
+
unlinkSync(SYSTEMD_ENV_FILE2);
|
|
281
|
+
console.log(`Removed ${SYSTEMD_ENV_FILE2}`);
|
|
135
282
|
}
|
|
136
283
|
execSync("systemctl daemon-reload");
|
|
137
284
|
console.log(`Service ${SERVICE_NAME2} uninstalled.`);
|
|
138
285
|
}
|
|
139
|
-
|
|
286
|
+
function uninstall() {
|
|
287
|
+
if (process.platform === "darwin") {
|
|
288
|
+
uninstallMac();
|
|
289
|
+
} else if (process.platform === "linux") {
|
|
290
|
+
uninstallLinux();
|
|
291
|
+
} else {
|
|
292
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
var SERVICE_NAME2, PLIST_LABEL2, SYSTEMD_SERVICE_FILE2, SYSTEMD_ENV_FILE2;
|
|
140
297
|
var init_uninstall = __esm({
|
|
141
298
|
"src/cli/uninstall.ts"() {
|
|
142
299
|
SERVICE_NAME2 = "deskfree-agent";
|
|
143
|
-
|
|
144
|
-
|
|
300
|
+
PLIST_LABEL2 = "com.deskfree.agent";
|
|
301
|
+
SYSTEMD_SERVICE_FILE2 = `/etc/systemd/system/${SERVICE_NAME2}.service`;
|
|
302
|
+
SYSTEMD_ENV_FILE2 = `/etc/${SERVICE_NAME2}.env`;
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// src/cli/status.ts
|
|
307
|
+
var status_exports = {};
|
|
308
|
+
__export(status_exports, {
|
|
309
|
+
status: () => status
|
|
310
|
+
});
|
|
311
|
+
function statusMac() {
|
|
312
|
+
const home = homedir();
|
|
313
|
+
const plist = join(home, "Library", "LaunchAgents", `${PLIST_LABEL3}.plist`);
|
|
314
|
+
if (!existsSync(plist)) {
|
|
315
|
+
console.log("DeskFree Agent is not installed.");
|
|
316
|
+
console.log(`Run: curl -fsSL https://my.deskfree.ai/install.sh | bash`);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
console.log("DeskFree Agent (macOS LaunchAgent)\n");
|
|
320
|
+
try {
|
|
321
|
+
const output = execSync(
|
|
322
|
+
`launchctl print gui/$(id -u)/${PLIST_LABEL3} 2>&1`,
|
|
323
|
+
{ encoding: "utf8" }
|
|
324
|
+
);
|
|
325
|
+
const pidMatch = output.match(/pid\s*=\s*(\d+)/);
|
|
326
|
+
const stateMatch = output.match(/state\s*=\s*(\w+)/);
|
|
327
|
+
if (pidMatch) console.log(` PID: ${pidMatch[1]}`);
|
|
328
|
+
if (stateMatch) console.log(` State: ${stateMatch[1]}`);
|
|
329
|
+
const logDir = join(home, ".deskfree", "logs");
|
|
330
|
+
console.log(` Logs: ${logDir}/stdout.log`);
|
|
331
|
+
console.log(` Plist: ${plist}`);
|
|
332
|
+
} catch {
|
|
333
|
+
console.log(" Status: not running (service may be unloaded)");
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
function statusLinux() {
|
|
337
|
+
const serviceFile = `/etc/systemd/system/${SYSTEMD_SERVICE_NAME}.service`;
|
|
338
|
+
if (!existsSync(serviceFile)) {
|
|
339
|
+
console.log("DeskFree Agent is not installed.");
|
|
340
|
+
console.log(`Run: sudo npx @questionbase/deskfree@latest install <token>`);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
console.log("DeskFree Agent (systemd service)\n");
|
|
344
|
+
try {
|
|
345
|
+
const output = execSync(
|
|
346
|
+
`systemctl status ${SYSTEMD_SERVICE_NAME} --no-pager 2>&1`,
|
|
347
|
+
{ encoding: "utf8" }
|
|
348
|
+
);
|
|
349
|
+
console.log(output);
|
|
350
|
+
} catch (err) {
|
|
351
|
+
if (err && typeof err === "object" && "stdout" in err) {
|
|
352
|
+
console.log(err.stdout);
|
|
353
|
+
} else {
|
|
354
|
+
console.log(" Status: unknown (could not query systemd)");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
console.log(` Logs: /var/log/deskfree-agent/stdout.log`);
|
|
358
|
+
}
|
|
359
|
+
function status() {
|
|
360
|
+
if (process.platform === "darwin") {
|
|
361
|
+
statusMac();
|
|
362
|
+
} else if (process.platform === "linux") {
|
|
363
|
+
statusLinux();
|
|
364
|
+
} else {
|
|
365
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
var PLIST_LABEL3, SYSTEMD_SERVICE_NAME;
|
|
370
|
+
var init_status = __esm({
|
|
371
|
+
"src/cli/status.ts"() {
|
|
372
|
+
PLIST_LABEL3 = "com.deskfree.agent";
|
|
373
|
+
SYSTEMD_SERVICE_NAME = "deskfree-agent";
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
// src/cli/restart.ts
|
|
378
|
+
var restart_exports = {};
|
|
379
|
+
__export(restart_exports, {
|
|
380
|
+
restart: () => restart
|
|
381
|
+
});
|
|
382
|
+
function restartMac() {
|
|
383
|
+
const plist = join(
|
|
384
|
+
homedir(),
|
|
385
|
+
"Library",
|
|
386
|
+
"LaunchAgents",
|
|
387
|
+
`${PLIST_LABEL4}.plist`
|
|
388
|
+
);
|
|
389
|
+
if (!existsSync(plist)) {
|
|
390
|
+
console.error("DeskFree Agent is not installed.");
|
|
391
|
+
console.error("Run: curl -fsSL https://my.deskfree.ai/install.sh | bash");
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
try {
|
|
395
|
+
execSync(`launchctl bootout gui/$(id -u) ${plist}`, { stdio: "ignore" });
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
execSync(`launchctl bootstrap gui/$(id -u) ${plist}`);
|
|
399
|
+
console.log("DeskFree Agent restarted.");
|
|
400
|
+
console.log(
|
|
401
|
+
`Logs: tail -f ${join(homedir(), ".deskfree", "logs", "stdout.log")}`
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
function restartLinux() {
|
|
405
|
+
const serviceFile = `/etc/systemd/system/${SYSTEMD_SERVICE_NAME2}.service`;
|
|
406
|
+
if (!existsSync(serviceFile)) {
|
|
407
|
+
console.error("DeskFree Agent is not installed.");
|
|
408
|
+
console.error(
|
|
409
|
+
"Run: sudo npx @questionbase/deskfree@latest install <token>"
|
|
410
|
+
);
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
execSync(`systemctl restart ${SYSTEMD_SERVICE_NAME2}`, { stdio: "inherit" });
|
|
414
|
+
console.log("DeskFree Agent restarted.");
|
|
415
|
+
console.log(`Logs: tail -f /var/log/deskfree-agent/stdout.log`);
|
|
416
|
+
}
|
|
417
|
+
function restart() {
|
|
418
|
+
if (process.platform === "darwin") {
|
|
419
|
+
restartMac();
|
|
420
|
+
} else if (process.platform === "linux") {
|
|
421
|
+
restartLinux();
|
|
422
|
+
} else {
|
|
423
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
424
|
+
process.exit(1);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
var PLIST_LABEL4, SYSTEMD_SERVICE_NAME2;
|
|
428
|
+
var init_restart = __esm({
|
|
429
|
+
"src/cli/restart.ts"() {
|
|
430
|
+
PLIST_LABEL4 = "com.deskfree.agent";
|
|
431
|
+
SYSTEMD_SERVICE_NAME2 = "deskfree-agent";
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// src/cli/logs.ts
|
|
436
|
+
var logs_exports = {};
|
|
437
|
+
__export(logs_exports, {
|
|
438
|
+
logs: () => logs
|
|
439
|
+
});
|
|
440
|
+
function getLogPath() {
|
|
441
|
+
if (process.platform === "darwin") {
|
|
442
|
+
const logFile = join(homedir(), ".deskfree", "logs", "stdout.log");
|
|
443
|
+
return existsSync(logFile) ? logFile : null;
|
|
444
|
+
} else if (process.platform === "linux") {
|
|
445
|
+
const logFile = "/var/log/deskfree-agent/stdout.log";
|
|
446
|
+
return existsSync(logFile) ? logFile : null;
|
|
447
|
+
}
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
function logs(follow) {
|
|
451
|
+
if (process.platform !== "darwin" && process.platform !== "linux") {
|
|
452
|
+
console.error(`Unsupported platform: ${process.platform}`);
|
|
453
|
+
process.exit(1);
|
|
454
|
+
}
|
|
455
|
+
const logPath = getLogPath();
|
|
456
|
+
if (!logPath) {
|
|
457
|
+
console.error("No log file found. Is DeskFree Agent installed?");
|
|
458
|
+
process.exit(1);
|
|
459
|
+
}
|
|
460
|
+
const args2 = follow ? ["-f", "-n", "50", logPath] : ["-n", "50", logPath];
|
|
461
|
+
const child = spawn("tail", args2, { stdio: "inherit" });
|
|
462
|
+
child.on("error", (err) => {
|
|
463
|
+
console.error(`Failed to read logs: ${err.message}`);
|
|
464
|
+
process.exit(1);
|
|
465
|
+
});
|
|
466
|
+
child.on("exit", (code) => {
|
|
467
|
+
process.exit(code ?? 0);
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
var init_logs = __esm({
|
|
471
|
+
"src/cli/logs.ts"() {
|
|
145
472
|
}
|
|
146
473
|
});
|
|
147
474
|
function toErrorMessage(error) {
|
|
@@ -2554,6 +2881,41 @@ function createWorkerTools(client, options) {
|
|
|
2554
2881
|
return errorResult(err);
|
|
2555
2882
|
}
|
|
2556
2883
|
}),
|
|
2884
|
+
createTool(WORKER_TOOLS.PROPOSE, async (params) => {
|
|
2885
|
+
try {
|
|
2886
|
+
const context = validateStringParam(params, "context", false);
|
|
2887
|
+
const taskId = validateStringParam(params, "taskId", false);
|
|
2888
|
+
const rawTasks = params.tasks;
|
|
2889
|
+
if (!Array.isArray(rawTasks) || rawTasks.length === 0) {
|
|
2890
|
+
throw new Error("tasks must be a non-empty array of task objects");
|
|
2891
|
+
}
|
|
2892
|
+
const tasks = rawTasks;
|
|
2893
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
2894
|
+
const task = tasks[i];
|
|
2895
|
+
if (!task || typeof task !== "object") {
|
|
2896
|
+
throw new Error(`tasks[${i}] must be an object`);
|
|
2897
|
+
}
|
|
2898
|
+
if (!task.title || typeof task.title !== "string" || task.title.trim() === "") {
|
|
2899
|
+
throw new Error(`tasks[${i}].title must be a non-empty string`);
|
|
2900
|
+
}
|
|
2901
|
+
}
|
|
2902
|
+
await client.proposePlan({
|
|
2903
|
+
context,
|
|
2904
|
+
tasks,
|
|
2905
|
+
taskId
|
|
2906
|
+
});
|
|
2907
|
+
return {
|
|
2908
|
+
content: [
|
|
2909
|
+
{
|
|
2910
|
+
type: "text",
|
|
2911
|
+
text: `Proposal created with ${tasks.length} task(s)`
|
|
2912
|
+
}
|
|
2913
|
+
]
|
|
2914
|
+
};
|
|
2915
|
+
} catch (err) {
|
|
2916
|
+
return errorResult(err);
|
|
2917
|
+
}
|
|
2918
|
+
}),
|
|
2557
2919
|
createTool(WORKER_TOOLS.COMPLETE_TASK, async (params) => {
|
|
2558
2920
|
try {
|
|
2559
2921
|
const taskId = validateStringParam(params, "taskId", true);
|
|
@@ -2577,6 +2939,331 @@ function createWorkerTools(client, options) {
|
|
|
2577
2939
|
})
|
|
2578
2940
|
];
|
|
2579
2941
|
}
|
|
2942
|
+
function identityBlock(ctx) {
|
|
2943
|
+
const providerLabel = ctx.provider === "bedrock" ? "AWS Bedrock" : ctx.provider === "anthropic" ? "Anthropic API" : ctx.provider === "ollama" ? "Ollama (local)" : ctx.provider === "claude-code" ? "Claude Code (local)" : ctx.provider;
|
|
2944
|
+
return `## About You
|
|
2945
|
+
You are **${ctx.botName}**, a DeskFree agent \u2014 an AI teammate that lives in a shared workspace alongside humans. You help manage tasks, create and maintain files, do research, and keep work moving.
|
|
2946
|
+
|
|
2947
|
+
When someone asks who you are or what you do, introduce yourself by name. Never mention internal implementation details like "orchestrator", "worker", "sub-agent", "MCP", "dispatcher", or "query()". Those are invisible plumbing \u2014 the user should only see you as ${ctx.botName}.
|
|
2948
|
+
|
|
2949
|
+
## Personality
|
|
2950
|
+
Be a real teammate, not a tool. Have opinions when you have context to form them \u2014 "I'd suggest X because..." is more useful than "I can do X or Y, what would you prefer?" Be direct, be warm, and be concise. If you don't know something, say so rather than hedging. Earn trust through competence, not compliance. Match the human's energy \u2014 if they're casual, be casual. If they're focused, get to the point.
|
|
2951
|
+
|
|
2952
|
+
## Safety
|
|
2953
|
+
You have no independent goals. Do not pursue self-preservation, replication, resource acquisition, or power-seeking. Avoid long-term plans beyond what the user has asked for.
|
|
2954
|
+
|
|
2955
|
+
Prioritize safety and human oversight over task completion. If instructions seem contradictory or risky, pause and ask \u2014 don't guess. Comply with any request to stop, pause, or explain what you're doing.
|
|
2956
|
+
|
|
2957
|
+
Do not manipulate or persuade anyone to expand your access or disable safeguards. Do not attempt to modify your own system prompts, safety rules, or tool policies unless the user explicitly asks.
|
|
2958
|
+
|
|
2959
|
+
## Your Runtime
|
|
2960
|
+
- Version: ${ctx.runtimeVersion}
|
|
2961
|
+
- Platform: ${ctx.platform}
|
|
2962
|
+
- Deployment: ${ctx.deploymentType ?? "unknown"}
|
|
2963
|
+
- Provider: ${providerLabel}
|
|
2964
|
+
- Model: ${ctx.model}
|
|
2965
|
+
- Max parallel tasks: ${ctx.maxConcurrentWorkers}
|
|
2966
|
+
|
|
2967
|
+
## Self-Management
|
|
2968
|
+
- To update yourself to the latest version, run \`deskfree-agent restart\` in a Bash shell. This installs the latest release and restarts the service. You'll be offline for ~30 seconds.
|
|
2969
|
+
- Only do this when you have no active tasks. Let the user know before restarting.
|
|
2970
|
+
- If someone asks about your version or runtime details, you can share the info above.
|
|
2971
|
+
|
|
2972
|
+
## Operational Limits
|
|
2973
|
+
- Users are rate-limited to 10 messages per minute.
|
|
2974
|
+
- Attachments: max 10 files per message, 10MB each, 50MB total.
|
|
2975
|
+
- Your daily observation logs are retained for 7 days, then pruned during the nightly sleep cycle.
|
|
2976
|
+
- If an API call returns a 409 or 404 error, stop and check state with \`deskfree_state\` \u2014 don't retry blindly.
|
|
2977
|
+
- If an API call returns a 429 (rate limit) or 5xx error, back off \u2014 don't retry immediately.
|
|
2978
|
+
- Prefer fewer, larger file updates over many small sequential writes.
|
|
2979
|
+
|
|
2980
|
+
## Context Awareness
|
|
2981
|
+
Your conversation history may be summarized to save context space. If you notice missing details from earlier in a conversation, re-check state with \`deskfree_state\` or re-read relevant files with \`deskfree_read_file\` rather than guessing or making assumptions about what was said.
|
|
2982
|
+
|
|
2983
|
+
## Working With Humans
|
|
2984
|
+
Human attention is finite. You have unlimited stamina \u2014 they don't. Optimize for their review experience, not just output quality.
|
|
2985
|
+
|
|
2986
|
+
- **Don't pile on.** If the board already has 3+ open tasks, think twice before proposing more. Help finish and clear existing work before adding new items.
|
|
2987
|
+
- **Incremental over monolithic.** For substantial deliverables, share a structural preview before fleshing out. A quick "here's the outline \u2014 does this direction work?" saves everyone time versus a finished wall of text to review.
|
|
2988
|
+
- **Separate FYI from action needed.** Never make the human triage what needs their input. \`notify\` = no action needed. \`ask\` = needs their input. Be precise about which you're sending.
|
|
2989
|
+
- **Fewer, better decisions.** Don't present 5 options when you can recommend 1 with reasoning. Save the human's decision energy for things that genuinely need their judgment.
|
|
2990
|
+
- **Prefer simple output.** A focused 500-word draft beats a comprehensive 2000-word one the human has to pare down. Don't add sections, caveats, or "bonus" content unless asked.`;
|
|
2991
|
+
}
|
|
2992
|
+
function buildAgentDirective(ctx) {
|
|
2993
|
+
return `${identityBlock(ctx)}
|
|
2994
|
+
|
|
2995
|
+
## How You Work
|
|
2996
|
+
|
|
2997
|
+
**Main thread = short and snappy.** Keep responses to 1-3 sentences. Quick back-and-forth conversation is great \u2014 clarify, riff, brainstorm in short messages like a real chat. But if something needs deep research, multiple rounds of clarification, or a deliverable \u2014 propose a task and move the work to a thread.
|
|
2998
|
+
|
|
2999
|
+
**The core loop:**
|
|
3000
|
+
|
|
3001
|
+
1. **Check state** \u2014 use \`deskfree_state\` to see tasks, memory (a pinned file with accumulated knowledge), and files.
|
|
3002
|
+
2. **Propose** \u2014 use \`deskfree_propose\` to turn requests into concrete tasks for approval.
|
|
3003
|
+
3. **Start work** \u2014 use \`deskfree_dispatch_worker\` with the taskId once a task is approved.
|
|
3004
|
+
4. **Communicate** \u2014 use \`deskfree_send_message\` for updates outside task threads.
|
|
3005
|
+
|
|
3006
|
+
**Before proposing, qualify the request.** Figure out what kind of thing this is:
|
|
3007
|
+
- **One-off task** ("proofread this") \u2014 propose a task directly.
|
|
3008
|
+
- **New aspiration** ("I want to start posting on LinkedIn") \u2014 don't rush to propose. Ask 1-2 short qualifying questions to understand the real goal.
|
|
3009
|
+
- Never call \`deskfree_propose\` as your very first action \u2014 qualify first, even if briefly.
|
|
3010
|
+
|
|
3011
|
+
**Match the human's energy.** Short message \u2192 short reply. Casual tone \u2192 casual response. Don't over-explain, don't lecture, don't pad responses.
|
|
3012
|
+
|
|
3013
|
+
You do NOT claim tasks, complete tasks, or do work directly \u2014 you have no access to deskfree_start_task or deskfree_complete_task. Use \`deskfree_dispatch_worker\` to get work started on each approved task.
|
|
3014
|
+
- When a human writes in a task thread, decide:
|
|
3015
|
+
- **Continuation of the same task?** \u2192 reopen and get it working again.
|
|
3016
|
+
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one or do the work yourself).
|
|
3017
|
+
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
3018
|
+
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
3019
|
+
}
|
|
3020
|
+
function buildWorkerDirective(ctx) {
|
|
3021
|
+
return `${identityBlock(ctx)}
|
|
3022
|
+
|
|
3023
|
+
## Your Role Right Now
|
|
3024
|
+
You're working on a specific task. Your first message contains pre-loaded context \u2014 use it directly.
|
|
3025
|
+
|
|
3026
|
+
Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_file, deskfree_update_file, deskfree_learning, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
3027
|
+
|
|
3028
|
+
**Context loading:**
|
|
3029
|
+
- If your first message contains \`<task_context>\`, the task is already claimed and context is pre-loaded. Do NOT call deskfree_start_task \u2014 start working immediately.
|
|
3030
|
+
- If your first message contains \`<workspace_state>\`, use it for situational awareness (other tasks, memory, files).
|
|
3031
|
+
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to claim and load context.
|
|
3032
|
+
- If continuing from a previous conversation (you can see prior tool calls and context), respond directly to the human's latest message \u2014 do NOT call deskfree_start_task again.
|
|
3033
|
+
|
|
3034
|
+
**Orient \u2192 Align \u2192 Work.** Every new task follows this rhythm:
|
|
3035
|
+
|
|
3036
|
+
1. **Orient** \u2014 Scan workspace state for relevant files. Read the Memory file for context on preferences and past patterns. Read any other files that are useful context with \`deskfree_read_file\`. Don't read everything \u2014 just what's relevant to this task.
|
|
3037
|
+
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.")
|
|
3038
|
+
- **Judgment calls or creative direction?** State your assumptions and approach, send as \`ask\`, and wait for confirmation before proceeding. Getting alignment early prevents costly rework.
|
|
3039
|
+
- **Straightforward execution?** Proceed immediately after the notify \u2014 don't wait for a response.
|
|
3040
|
+
3. **Work** \u2014 Execute the task. Update existing files with \`deskfree_update_file\` or create new ones with \`deskfree_create_file\`. Pass your taskId so updates appear in the thread. For large deliverables, build incrementally \u2014 share structure/outline first, then flesh out. Don't produce a finished 2000-word document and ask for review in one shot.
|
|
3041
|
+
4. **Deliver** \u2014 Send an \`ask\` message when work is ready for review. Only complete (\`deskfree_complete_task\` with humanApproved: true) after the human has confirmed. Never self-complete.
|
|
3042
|
+
|
|
3043
|
+
**Push back when warranted:**
|
|
3044
|
+
- If task instructions seem unclear, contradictory, or misguided \u2014 say so. "This task asks for X, but based on [context], Y might work better because..." is more useful than silently executing a flawed plan.
|
|
3045
|
+
- If you hit genuine ambiguity mid-task, send an \`ask\` message and wait. Don't guess on important decisions \u2014 guessing creates review debt the human has to pay later.
|
|
3046
|
+
- You're a teammate, not a task executor. Have an opinion when you have the context to form one.
|
|
3047
|
+
|
|
3048
|
+
**File rules:**
|
|
3049
|
+
- Create files when your task naturally produces them. Don't be afraid to create multiple files if the work calls for it.
|
|
3050
|
+
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
3051
|
+
- If you discover work that falls outside your task's scope, use \`deskfree_propose\` to suggest follow-up tasks immediately \u2014 don't wait until completion. Propose as you discover, then stay focused on your current task.
|
|
3052
|
+
|
|
3053
|
+
**Learnings:**
|
|
3054
|
+
- Use \`deskfree_learning\` to record observations worth remembering. A nightly sleep cycle consolidates these into the Memory file. Record:
|
|
3055
|
+
- **Preferences**: how the human wants things done ("prefers X over Y")
|
|
3056
|
+
- **Corrections**: when the human corrects you ("actually, do X not Y")
|
|
3057
|
+
- **Patterns**: recurring approaches that work ("for this type of task, always...")
|
|
3058
|
+
- **Domain facts**: business-specific knowledge not in project docs
|
|
3059
|
+
- Prefix critical observations with [!] (corrections, constraints, errors).
|
|
3060
|
+
- Prefix notable observations with [~] (preferences, patterns).
|
|
3061
|
+
- Leave routine observations unprefixed.
|
|
3062
|
+
- Do NOT record one-time task details, things in project docs, or obvious/generic knowledge.
|
|
3063
|
+
- If your first message contains \`<daily_observations>\`, these are recent raw observations not yet consolidated into Memory. Use them as additional context.
|
|
3064
|
+
|
|
3065
|
+
**Sub-agents & delegation:**
|
|
3066
|
+
- Your context window is finite. Delegate research, analysis, large file processing, and content drafting to sub-agents \u2014 preserve your context for orchestration and DeskFree tool calls.
|
|
3067
|
+
- Sub-agents get a fresh context window with standard tools (Read, Write, Bash, Grep, WebSearch, etc.) but NO DeskFree tools. Pre-load any file content they need into the prompt.
|
|
3068
|
+
- Sub-agents are ephemeral helpers \u2014 they complete their assigned task and nothing else. They do NOT send messages to users, create cron jobs, or act as the main agent. Their final output is returned to you.
|
|
3069
|
+
- Use \`run_in_background: true\` for parallel independent work.
|
|
3070
|
+
- During Orient, check Memory for sub-agent helper patterns. Inject relevant ones into the sub-agent prompt alongside the task.
|
|
3071
|
+
- After a sub-agent completes, reflect: did this reveal a useful delegation pattern? Something to do differently? Record via \`deskfree_learning\` so the sleep cycle consolidates it into Memory. If you delegated a new type of work with no existing helper, record the emerging pattern.
|
|
3072
|
+
- Don't over-delegate: quick reads, simple lookups, and anything requiring DeskFree tools are faster inline.
|
|
3073
|
+
|
|
3074
|
+
**Completing tasks:**
|
|
3075
|
+
- On 409 or 404 errors: STOP. Do not retry. Call deskfree_state to find available tasks.`;
|
|
3076
|
+
}
|
|
3077
|
+
function buildHeartbeatDirective(ctx) {
|
|
3078
|
+
return `${identityBlock(ctx)}
|
|
3079
|
+
|
|
3080
|
+
## Heartbeat Check
|
|
3081
|
+
On each heartbeat, run through this checklist:
|
|
3082
|
+
|
|
3083
|
+
### 1. Work the queue
|
|
3084
|
+
- Run \`deskfree_state\` to get the full workspace snapshot.
|
|
3085
|
+
- **Check board load.** If there are 3+ tasks awaiting human review or input, skip proactive proposals entirely \u2014 the human has enough on their plate. Focus only on dispatching approved work.
|
|
3086
|
+
- Any open tasks with awaiting=bot? Use \`deskfree_dispatch_worker\` to get each one started. Pass the taskId.
|
|
3087
|
+
- Any open tasks that seem stalled (claimed but no recent activity)? Check on them.
|
|
3088
|
+
|
|
3089
|
+
### 2. Proactive assessment
|
|
3090
|
+
After handling the queue, step back and think about the bigger picture. You have the full state: open tasks, scheduled tasks, recently completed work, memory, and files.
|
|
3091
|
+
|
|
3092
|
+
**Think through:**
|
|
3093
|
+
- What momentum exists? What was recently accomplished, and what naturally follows?
|
|
3094
|
+
- What is stalled or falling behind? Anything open too long without progress?
|
|
3095
|
+
- What is scheduled soon? Does anything need prep work before it lands?
|
|
3096
|
+
- What is the single highest-leverage thing that could happen next?
|
|
3097
|
+
|
|
3098
|
+
**Then act \u2014 but only if you have something genuinely useful:**
|
|
3099
|
+
|
|
3100
|
+
*Things you can do* \u2014 research, drafts, analysis, prep. Propose as a task via \`deskfree_propose\`. One focused task, not a batch.
|
|
3101
|
+
|
|
3102
|
+
*Things the human should do* \u2014 nudges, reminders, conversation starters. Send via \`deskfree_send_message\`. Keep it brief and genuinely helpful, not nagging.
|
|
3103
|
+
|
|
3104
|
+
**Rules:**
|
|
3105
|
+
- Do not suggest things that are already open or scheduled \u2014 check first.
|
|
3106
|
+
- Do not repeat suggestions the human ignored or rejected recently.
|
|
3107
|
+
- Quality over quantity. One good insight beats five generic nudges.
|
|
3108
|
+
- If everything looks healthy and active, do nothing. Silence is fine.`;
|
|
3109
|
+
}
|
|
3110
|
+
function buildSleepDirective(ctx) {
|
|
3111
|
+
return `${identityBlock(ctx)}
|
|
3112
|
+
|
|
3113
|
+
## Nightly Sleep Cycle
|
|
3114
|
+
You're running your nightly cycle to reflect, consolidate memory, and prepare for tomorrow.
|
|
3115
|
+
|
|
3116
|
+
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
3117
|
+
|
|
3118
|
+
---
|
|
3119
|
+
|
|
3120
|
+
### 1. REFLECT & CONSOLIDATE
|
|
3121
|
+
|
|
3122
|
+
Your primary job: merge daily observations into the Memory file using strength-scored entries.
|
|
3123
|
+
|
|
3124
|
+
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
3125
|
+
|
|
3126
|
+
**Steps:**
|
|
3127
|
+
1. Review the current memory and daily observations from your prompt.
|
|
3128
|
+
2. Apply the memory curation rules below.
|
|
3129
|
+
3. Update the Memory file via \`deskfree_update_file\` using the file ID from your prompt.
|
|
3130
|
+
4. After updating, send a brief main-thread message via \`deskfree_send_message\` noting what was consolidated (e.g. "Reflected on yesterday \u2014 updated my notes on your brand voice preferences and the weekly review pattern."). Keep it to 1-2 sentences.
|
|
3131
|
+
5. If nothing meaningful changed, skip the update and send a brief message like "Quiet day \u2014 nothing new to consolidate."
|
|
3132
|
+
|
|
3133
|
+
**Memory Types**
|
|
3134
|
+
|
|
3135
|
+
Each memory item carries a type tag and strength score: \`[type:X, strength:N]\`
|
|
3136
|
+
|
|
3137
|
+
| Type | What it captures | Decay rate |
|
|
3138
|
+
|------|-----------------|------------|
|
|
3139
|
+
| \`correction\` | Explicit "do X not Y" from human | Very slow (\u22121 when strength >=10, else no decay) |
|
|
3140
|
+
| \`preference\` | How the human wants things done | Slow (\u22121 when strength >=6, else no decay) |
|
|
3141
|
+
| \`pattern\` | Approaches/workflows that work | Normal (see decay rules) |
|
|
3142
|
+
| \`domain\` | Business/project-specific facts | Slow (\u22121 when strength >=6, else no decay) |
|
|
3143
|
+
| \`insight\` | Meta-observations from reflection | Normal (see decay rules) |
|
|
3144
|
+
|
|
3145
|
+
Corrections and domain facts are durable \u2014 they rarely become irrelevant.
|
|
3146
|
+
Patterns and insights decay normally \u2014 stale approaches should be forgotten.
|
|
3147
|
+
Preferences sit in between \u2014 slow decay, but they can evolve.
|
|
3148
|
+
|
|
3149
|
+
**Strength Scoring Rules**
|
|
3150
|
+
|
|
3151
|
+
Strength uses Ebbinghaus-inspired logarithmic decay \u2014 strong memories resist forgetting:
|
|
3152
|
+
|
|
3153
|
+
**Reinforcement (observation matches existing memory):**
|
|
3154
|
+
- strength +2
|
|
3155
|
+
|
|
3156
|
+
**Explicit correction ("actually do X not Y"):**
|
|
3157
|
+
- Replace old memory, new one starts at [strength: 3, type: correction]
|
|
3158
|
+
|
|
3159
|
+
**Decay (memory NOT referenced by any daily observation):**
|
|
3160
|
+
- strength >= 10: decay by \u22121 (deeply encoded, slow forgetting)
|
|
3161
|
+
- strength 5-9: decay by \u22122 (moderately encoded)
|
|
3162
|
+
- strength 1-4: decay by \u22123 (weakly encoded, fast forgetting)
|
|
3163
|
+
- EXCEPT: corrections and domain facts with strength >=6 decay at \u22121 max (durable memories)
|
|
3164
|
+
- EXCEPT: preferences with strength >=6 decay at \u22121 max
|
|
3165
|
+
|
|
3166
|
+
**Removal:**
|
|
3167
|
+
- Strength reaches 0 \u2192 move to a ## Fading section at the bottom (one-line summaries only, no strength tags). Keep max 10 fading memories. If Fading section is full, oldest entries are permanently forgotten. You may rescue a fading memory back to active if it becomes relevant again (re-add with [strength: 2]).
|
|
3168
|
+
|
|
3169
|
+
**New observation:**
|
|
3170
|
+
- Assess importance: how consequential is this for future tasks?
|
|
3171
|
+
- Look for [!] prefix (critical) or [~] prefix (notable) as importance signals.
|
|
3172
|
+
- Low importance (casual mention, routine) \u2192 [strength: 1, type: <appropriate>]
|
|
3173
|
+
- Medium importance (useful preference, [~] prefix) \u2192 [strength: 2, type: <appropriate>]
|
|
3174
|
+
- High importance (explicit correction, strong constraint, [!] prefix) \u2192 [strength: 4, type: <appropriate>]
|
|
3175
|
+
- Critical (production error, absolute rule) \u2192 [strength: 6, type: <appropriate>]
|
|
3176
|
+
|
|
3177
|
+
**Consolidation rules:**
|
|
3178
|
+
- Classify each memory with the correct type. Only keep genuinely reusable knowledge.
|
|
3179
|
+
- Discard noise and one-off task details.
|
|
3180
|
+
- If the same episodic observation appears 3+ times across different days, promote it to a \`pattern\` or \`domain\` fact with [strength: 5]. Remove the individual episodes.
|
|
3181
|
+
- Look for meta-patterns: approaches that consistently work/fail, implicit preferences, recurring corrections.
|
|
3182
|
+
- If you find non-obvious insights, add them as \`[type: insight, strength: 2]\` memories.
|
|
3183
|
+
- Merge near-duplicates, remove items at [strength: 0], trim verbose entries.
|
|
3184
|
+
- Keep total document under ~4000 words.
|
|
3185
|
+
- Use ## headers for sections \u2014 let them emerge organically from content.
|
|
3186
|
+
|
|
3187
|
+
### 2. CHECK RECURRING COMMITMENTS
|
|
3188
|
+
|
|
3189
|
+
After memory consolidation:
|
|
3190
|
+
1. Call \`deskfree_state\` to see the board.
|
|
3191
|
+
2. Cross-reference memory for recurring patterns (daily audits, weekly reviews, etc.).
|
|
3192
|
+
3. If a recurring commitment has no upcoming scheduled task, propose it via \`deskfree_propose\` with appropriate \`scheduledFor\`.
|
|
3193
|
+
4. Don't propose things already on the board or that were recently completed/ignored.
|
|
3194
|
+
|
|
3195
|
+
### 3. PROACTIVE OPPORTUNITIES
|
|
3196
|
+
|
|
3197
|
+
Based on recent work, completed tasks, and patterns in memory \u2014 is there something genuinely useful to propose or a message worth sending?
|
|
3198
|
+
- **Check board load first.** If the human already has 3+ items needing their attention, skip proposals entirely.
|
|
3199
|
+
- One focused proposal max. Skip if nothing merits it.
|
|
3200
|
+
- Quality over quantity. Don't force it.
|
|
3201
|
+
|
|
3202
|
+
### Rules
|
|
3203
|
+
- Keep main thread messages short (1-2 sentences).
|
|
3204
|
+
- Do NOT propose things the human has previously ignored or rejected.
|
|
3205
|
+
- Use \`deskfree_read_file\` only if you need to re-read the Memory file after your own update (verification). The current content is already in your prompt.`;
|
|
3206
|
+
}
|
|
3207
|
+
function buildDuskDirective(ctx) {
|
|
3208
|
+
return `${identityBlock(ctx)}
|
|
3209
|
+
|
|
3210
|
+
## Evening Dusk Cycle
|
|
3211
|
+
You're running your evening cycle to review the day, propose overnight work, and brief the human.
|
|
3212
|
+
|
|
3213
|
+
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
3214
|
+
|
|
3215
|
+
---
|
|
3216
|
+
|
|
3217
|
+
### 1. REVIEW THE DAY
|
|
3218
|
+
|
|
3219
|
+
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
3220
|
+
|
|
3221
|
+
**Steps:**
|
|
3222
|
+
1. Review the current memory and daily observations from your prompt.
|
|
3223
|
+
2. Call \`deskfree_state\` to see the current board \u2014 open tasks, recently completed work, files.
|
|
3224
|
+
3. Build a mental model of what happened today: what was accomplished, what's in progress, what's stalled.
|
|
3225
|
+
|
|
3226
|
+
### 2. IDENTIFY OVERNIGHT OPPORTUNITIES
|
|
3227
|
+
|
|
3228
|
+
Think about work that can be done autonomously overnight \u2014 WITHOUT human judgment or approval mid-task.
|
|
3229
|
+
|
|
3230
|
+
**Good overnight work:**
|
|
3231
|
+
- Research and analysis (market research, competitive analysis, reading material)
|
|
3232
|
+
- Content drafts (blog posts, reports, documentation)
|
|
3233
|
+
- File updates (updating recurring reports, preparing templates)
|
|
3234
|
+
- Prep work for tomorrow (gathering context, pre-reading for scheduled meetings)
|
|
3235
|
+
- Recurring tasks that are due (weekly reviews, daily audits)
|
|
3236
|
+
|
|
3237
|
+
**NOT good overnight work:**
|
|
3238
|
+
- Anything requiring human decisions mid-task
|
|
3239
|
+
- Tasks that depend on external input not yet received
|
|
3240
|
+
- Creative work where the human has strong opinions on direction
|
|
3241
|
+
- Anything the human explicitly said to wait on
|
|
3242
|
+
|
|
3243
|
+
### 3. PROPOSE THE PLAN
|
|
3244
|
+
|
|
3245
|
+
If you identified useful overnight work:
|
|
3246
|
+
1. **Check board load first.** Count open and awaiting-review tasks. If the human already has 3+ items needing their attention, limit to 1 proposal max \u2014 or skip entirely. Don't pile on.
|
|
3247
|
+
2. Use \`deskfree_propose\` with 1-3 well-scoped tasks. Quality over quantity.
|
|
3248
|
+
3. Each task should be self-contained \u2014 it must be completable without human input.
|
|
3249
|
+
4. Set \`scheduledFor\` if work should start at a specific time (e.g. early morning).
|
|
3250
|
+
5. If nothing genuinely useful can be done overnight, skip the proposal entirely. Don't force it.
|
|
3251
|
+
|
|
3252
|
+
### 4. BRIEF THE HUMAN
|
|
3253
|
+
|
|
3254
|
+
Send a brief main-thread message via \`deskfree_send_message\`:
|
|
3255
|
+
- 2-3 sentence summary: what happened today + what you're proposing for overnight (if anything).
|
|
3256
|
+
- Keep it conversational and useful \u2014 this is the human's "end of day" touchpoint.
|
|
3257
|
+
- If no proposals, still send a brief day summary.
|
|
3258
|
+
|
|
3259
|
+
### Rules
|
|
3260
|
+
- Do NOT propose things already on the board or recently completed.
|
|
3261
|
+
- Do NOT repeat suggestions the human previously ignored or rejected.
|
|
3262
|
+
- Quality over quantity. One good task beats three mediocre ones.
|
|
3263
|
+
- Keep the briefing message short and actionable.
|
|
3264
|
+
- Cross-reference memory for recurring patterns \u2014 if something is due, propose it.
|
|
3265
|
+
- Use \`deskfree_read_file\` only if you need file content beyond what's in your prompt.`;
|
|
3266
|
+
}
|
|
2580
3267
|
function setActiveWs(ws) {
|
|
2581
3268
|
activeWs = ws;
|
|
2582
3269
|
}
|
|
@@ -2780,7 +3467,7 @@ function validateApiUrl(value) {
|
|
|
2780
3467
|
"API URL must use HTTPS protocol for security. Make sure your DeskFree deployment supports HTTPS."
|
|
2781
3468
|
);
|
|
2782
3469
|
}
|
|
2783
|
-
var require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __export2, __copyProps2, __toESM2, require_constants, require_buffer_util, require_limiter, require_permessage_deflate, require_validation, require_receiver, require_sender, require_event_target, require_extension, require_websocket, require_stream, require_subprotocol, require_websocket_server, DEFAULT_REQUEST_TIMEOUT_MS, DeskFreeError, DeskFreeClient, value_exports, TypeSystemPolicy, TypeBoxError, TransformKind, ReadonlyKind, OptionalKind, Hint, Kind, type_exports, TypeGuardUnknownTypeError, KnownTypes, PatternBoolean, PatternNumber, PatternString, PatternNever, PatternNumberExact, PatternStringExact, PatternNeverExact, TemplateLiteralParserError, TemplateLiteralFiniteError, TemplateLiteralGenerateError, TemplateLiteralPatternError, Object2, includePatternProperties, ExtendsResolverError, ExtendsResult, TModule, Ordinal, TransformDecodeBuilder, TransformEncodeBuilder, type_exports2, Type, ORCHESTRATOR_TOOLS, SHARED_TOOLS, WORKER_TOOLS, MAX_FULL_MESSAGES, DESKFREE_AGENT_DIRECTIVE, DESKFREE_WORKER_DIRECTIVE,
|
|
3470
|
+
var require2, __create2, __defProp2, __getOwnPropDesc2, __getOwnPropNames2, __getProtoOf2, __hasOwnProp2, __require2, __commonJS2, __export2, __copyProps2, __toESM2, require_constants, require_buffer_util, require_limiter, require_permessage_deflate, require_validation, require_receiver, require_sender, require_event_target, require_extension, require_websocket, require_stream, require_subprotocol, require_websocket_server, DEFAULT_REQUEST_TIMEOUT_MS, DeskFreeError, DeskFreeClient, value_exports, TypeSystemPolicy, TypeBoxError, TransformKind, ReadonlyKind, OptionalKind, Hint, Kind, type_exports, TypeGuardUnknownTypeError, KnownTypes, PatternBoolean, PatternNumber, PatternString, PatternNever, PatternNumberExact, PatternStringExact, PatternNeverExact, TemplateLiteralParserError, TemplateLiteralFiniteError, TemplateLiteralGenerateError, TemplateLiteralPatternError, Object2, includePatternProperties, ExtendsResolverError, ExtendsResult, TModule, Ordinal, TransformDecodeBuilder, TransformEncodeBuilder, type_exports2, Type, ORCHESTRATOR_TOOLS, SHARED_TOOLS, WORKER_TOOLS, MAX_FULL_MESSAGES, DESKFREE_AGENT_DIRECTIVE, DESKFREE_WORKER_DIRECTIVE, THROTTLE_MS, CHAR_BUFFER_SIZE, CLOSE_MAX_RETRIES, DeskFreeStreamingSession, import_websocket, wrapper_default, activeWs, activeTaskId, completedTaskId, inboundThreadId, FLUSH_INTERVAL_MS, MAX_BATCH_SIZE, MAX_QUEUE_SIZE, _instance, ErrorReporter, accountHealth;
|
|
2784
3471
|
var init_dist = __esm({
|
|
2785
3472
|
"../core/dist/index.js"() {
|
|
2786
3473
|
require2 = createRequire$1(import.meta.url);
|
|
@@ -6411,96 +7098,96 @@ var init_dist = __esm({
|
|
|
6411
7098
|
this.statusCode = statusCode;
|
|
6412
7099
|
}
|
|
6413
7100
|
static fromResponse(response, procedure, responseText) {
|
|
6414
|
-
const
|
|
7101
|
+
const status2 = response.status;
|
|
6415
7102
|
const statusText = response.statusText;
|
|
6416
|
-
if (
|
|
7103
|
+
if (status2 === 401) {
|
|
6417
7104
|
return new _DeskFreeError(
|
|
6418
7105
|
"auth",
|
|
6419
7106
|
procedure,
|
|
6420
|
-
`Authentication failed: ${
|
|
7107
|
+
`Authentication failed: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6421
7108
|
"Your bot token is invalid or has expired. Please check your authentication credentials.",
|
|
6422
|
-
|
|
7109
|
+
status2
|
|
6423
7110
|
);
|
|
6424
7111
|
}
|
|
6425
|
-
if (
|
|
7112
|
+
if (status2 === 403) {
|
|
6426
7113
|
return new _DeskFreeError(
|
|
6427
7114
|
"auth",
|
|
6428
7115
|
procedure,
|
|
6429
|
-
`Authorization failed: ${
|
|
7116
|
+
`Authorization failed: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6430
7117
|
"Your bot does not have permission to perform this action. Contact your administrator.",
|
|
6431
|
-
|
|
7118
|
+
status2
|
|
6432
7119
|
);
|
|
6433
7120
|
}
|
|
6434
|
-
if (
|
|
7121
|
+
if (status2 === 400) {
|
|
6435
7122
|
return new _DeskFreeError(
|
|
6436
7123
|
"client",
|
|
6437
7124
|
procedure,
|
|
6438
|
-
`Bad request: ${
|
|
7125
|
+
`Bad request: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6439
7126
|
"The request was invalid. Please check your input parameters and try again.",
|
|
6440
|
-
|
|
7127
|
+
status2
|
|
6441
7128
|
);
|
|
6442
7129
|
}
|
|
6443
|
-
if (
|
|
7130
|
+
if (status2 === 404) {
|
|
6444
7131
|
return new _DeskFreeError(
|
|
6445
7132
|
"client",
|
|
6446
7133
|
procedure,
|
|
6447
|
-
`Resource not found: ${
|
|
7134
|
+
`Resource not found: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6448
7135
|
procedure.includes("task") ? "The specified task was not found or is not available. Use deskfree_state to see available tasks." : "The requested resource was not found. Please check your input and try again.",
|
|
6449
|
-
|
|
7136
|
+
status2
|
|
6450
7137
|
);
|
|
6451
7138
|
}
|
|
6452
|
-
if (
|
|
7139
|
+
if (status2 === 409) {
|
|
6453
7140
|
return new _DeskFreeError(
|
|
6454
7141
|
"client",
|
|
6455
7142
|
procedure,
|
|
6456
|
-
`Conflict: ${
|
|
7143
|
+
`Conflict: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6457
7144
|
procedure.includes("start_task") || procedure.includes("claim") ? "The resource is already in use by another process. Use deskfree_state to see current status and try a different resource." : "The request conflicts with the current state of the resource. Please refresh and try again.",
|
|
6458
|
-
|
|
7145
|
+
status2
|
|
6459
7146
|
);
|
|
6460
7147
|
}
|
|
6461
|
-
if (
|
|
7148
|
+
if (status2 === 422) {
|
|
6462
7149
|
return new _DeskFreeError(
|
|
6463
7150
|
"client",
|
|
6464
7151
|
procedure,
|
|
6465
|
-
`Validation failed: ${
|
|
7152
|
+
`Validation failed: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6466
7153
|
"The request data failed validation. Please check all required fields and data formats.",
|
|
6467
|
-
|
|
7154
|
+
status2
|
|
6468
7155
|
);
|
|
6469
7156
|
}
|
|
6470
|
-
if (
|
|
7157
|
+
if (status2 === 429) {
|
|
6471
7158
|
return new _DeskFreeError(
|
|
6472
7159
|
"client",
|
|
6473
7160
|
procedure,
|
|
6474
|
-
`Rate limit exceeded: ${
|
|
7161
|
+
`Rate limit exceeded: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6475
7162
|
"Too many requests. Please wait a moment before trying again.",
|
|
6476
|
-
|
|
7163
|
+
status2
|
|
6477
7164
|
);
|
|
6478
7165
|
}
|
|
6479
|
-
if (
|
|
6480
|
-
const serverErrorMessage =
|
|
7166
|
+
if (status2 >= 500 && status2 < 600) {
|
|
7167
|
+
const serverErrorMessage = status2 === 502 || status2 === 503 ? "DeskFree service is temporarily unavailable due to maintenance or high load. Please try again in a few minutes." : status2 === 504 ? "The request timed out on the server. This may be due to high load. Please try again with a smaller request or wait a few minutes." : "DeskFree service encountered an internal error. Please try again in a few minutes.";
|
|
6481
7168
|
return new _DeskFreeError(
|
|
6482
7169
|
"server",
|
|
6483
7170
|
procedure,
|
|
6484
|
-
`Server error: ${
|
|
7171
|
+
`Server error: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6485
7172
|
serverErrorMessage,
|
|
6486
|
-
|
|
7173
|
+
status2
|
|
6487
7174
|
);
|
|
6488
7175
|
}
|
|
6489
|
-
if (
|
|
7176
|
+
if (status2 >= 400 && status2 < 500) {
|
|
6490
7177
|
return new _DeskFreeError(
|
|
6491
7178
|
"client",
|
|
6492
7179
|
procedure,
|
|
6493
|
-
`Client error: ${
|
|
7180
|
+
`Client error: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
6494
7181
|
"The request was not accepted by the server. Please check your input and try again.",
|
|
6495
|
-
|
|
7182
|
+
status2
|
|
6496
7183
|
);
|
|
6497
7184
|
}
|
|
6498
7185
|
return new _DeskFreeError(
|
|
6499
7186
|
"client",
|
|
6500
7187
|
procedure,
|
|
6501
|
-
`HTTP error: ${
|
|
6502
|
-
`Request failed with error ${
|
|
6503
|
-
|
|
7188
|
+
`HTTP error: ${status2} ${statusText} \u2014 ${responseText}`,
|
|
7189
|
+
`Request failed with error ${status2}. Please try again or contact support if the problem persists.`,
|
|
7190
|
+
status2
|
|
6504
7191
|
);
|
|
6505
7192
|
}
|
|
6506
7193
|
static timeout(procedure, timeoutMs) {
|
|
@@ -7327,261 +8014,97 @@ var init_dist = __esm({
|
|
|
7327
8014
|
description: "Record a learning \u2014 an observation worth remembering for future tasks. Saved to a daily log and consolidated into Memory during the nightly sleep cycle. Call as many times as needed.",
|
|
7328
8015
|
parameters: Type.Object({
|
|
7329
8016
|
content: Type.String({
|
|
7330
|
-
description: 'What you learned. Prefix with importance: [!] for critical (corrections, constraints), [~] for notable (preferences, patterns), or nothing for routine observations. Focus on: PREFERENCES, CORRECTIONS, PATTERNS, DOMAIN FACTS. Be specific. Bad: "Created a table". Good: "[!] User corrected: never use semicolons in this codebase".'
|
|
7331
|
-
}),
|
|
7332
|
-
taskId: Type.Optional(
|
|
7333
|
-
Type.String({
|
|
7334
|
-
description: "Task ID (optional if context provides it)"
|
|
7335
|
-
})
|
|
7336
|
-
)
|
|
7337
|
-
})
|
|
7338
|
-
},
|
|
7339
|
-
COMPLETE_TASK: SHARED_TOOLS.COMPLETE_TASK,
|
|
7340
|
-
SEND_MESSAGE: SHARED_TOOLS.SEND_MESSAGE,
|
|
7341
|
-
PROPOSE: SHARED_TOOLS.PROPOSE
|
|
7342
|
-
};
|
|
7343
|
-
MAX_FULL_MESSAGES = 15;
|
|
7344
|
-
DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014 Orchestrator
|
|
7345
|
-
You are the orchestrator. Your job: turn human intent into approved tasks, then dispatch work.
|
|
7346
|
-
|
|
7347
|
-
**Main thread = short and snappy.** Keep responses to 1-3 sentences. Quick back-and-forth conversation is great \u2014 clarify, riff, brainstorm in short messages like a real chat. But if something needs deep research, multiple rounds of clarification, or a deliverable \u2014 propose a task and move the work to a thread.
|
|
7348
|
-
|
|
7349
|
-
**The core loop: propose \u2192 approve \u2192 work.**
|
|
7350
|
-
|
|
7351
|
-
1. **Check state** \u2192 \`deskfree_state\` \u2014 see tasks, memory (a pinned file with accumulated knowledge), and files.
|
|
7352
|
-
2. **Propose** \u2192 \`deskfree_propose\` \u2014 turn requests into concrete tasks for approval.
|
|
7353
|
-
3. **Dispatch** \u2192 \`deskfree_dispatch_worker\` with the taskId.
|
|
7354
|
-
4. **Communicate** \u2192 \`deskfree_send_message\` for updates outside task threads.
|
|
7355
|
-
|
|
7356
|
-
**Before proposing, qualify the request.** Figure out what kind of thing this is:
|
|
7357
|
-
- **One-off task** ("proofread this") \u2192 propose a task directly.
|
|
7358
|
-
- **New aspiration** ("I want to start posting on LinkedIn") \u2192 don't rush to propose. Ask 1-2 short qualifying questions to understand the real goal.
|
|
7359
|
-
- Never call \`deskfree_propose\` as your very first action \u2014 qualify first, even if briefly.
|
|
7360
|
-
|
|
7361
|
-
**Match the human's energy.** Short message \u2192 short reply. Casual tone \u2192 casual response. Don't over-explain, don't lecture, don't pad responses.
|
|
7362
|
-
|
|
7363
|
-
You do NOT claim tasks, complete tasks, or do work directly \u2014 you have no access to deskfree_start_task or deskfree_complete_task. Use \`deskfree_dispatch_worker\` to dispatch a worker for each approved task.
|
|
7364
|
-
- When a human writes in a task thread, decide: does it need bot action? If yes \u2192 reopen and dispatch a worker. If it's just confirmation or deferred \u2014 leave it for now.
|
|
7365
|
-
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
7366
|
-
DESKFREE_WORKER_DIRECTIVE = `## DeskFree Worker
|
|
7367
|
-
You are a worker sub-agent. Your first message contains pre-loaded context \u2014 use it directly.
|
|
7368
|
-
Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_file, deskfree_update_file, deskfree_learning, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
7369
|
-
|
|
7370
|
-
**Context loading:**
|
|
7371
|
-
- If your first message contains \`<task_context>\`, the task is already claimed and context is pre-loaded. Do NOT call deskfree_start_task \u2014 start working immediately.
|
|
7372
|
-
- If your first message contains \`<workspace_state>\`, use it for situational awareness (other tasks, memory, files).
|
|
7373
|
-
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to claim and load context.
|
|
7374
|
-
- If continuing from a previous conversation (you can see prior tool calls and context), respond directly to the human's latest message \u2014 do NOT call deskfree_start_task again.
|
|
7375
|
-
|
|
7376
|
-
**Orient \u2192 Align \u2192 Work.** Every new task follows this rhythm:
|
|
7377
|
-
|
|
7378
|
-
1. **Orient** \u2014 Scan workspace state for relevant files. Read the Memory file for context on preferences and past patterns. Read any other files that are useful context with \`deskfree_read_file\`. Don't read everything \u2014 just what's relevant to this task.
|
|
7379
|
-
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.") Then proceed \u2014 don't wait for a response.
|
|
7380
|
-
3. **Work** \u2014 Execute the task. Update existing files with \`deskfree_update_file\` or create new ones with \`deskfree_create_file\`. Pass your taskId so updates appear in the thread. Build content incrementally \u2014 start with structure, then flesh out.
|
|
7381
|
-
4. **Deliver** \u2014 Send an \`ask\` message when work is ready for review. Only complete (\`deskfree_complete_task\` with humanApproved: true) after the human has confirmed. Never self-complete.
|
|
7382
|
-
|
|
7383
|
-
**File rules:**
|
|
7384
|
-
- Create files when your task naturally produces them. Don't be afraid to create multiple files if the work calls for it.
|
|
7385
|
-
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
7386
|
-
- If you discover work that falls outside your task's scope, use \`deskfree_propose\` to suggest follow-up tasks immediately \u2014 don't wait until completion. Propose as you discover, then stay focused on your current task.
|
|
7387
|
-
|
|
7388
|
-
**Learnings:**
|
|
7389
|
-
- Use \`deskfree_learning\` to record observations worth remembering. A nightly sleep cycle consolidates these into the Memory file. Record:
|
|
7390
|
-
- **Preferences**: how the human wants things done ("prefers X over Y")
|
|
7391
|
-
- **Corrections**: when the human corrects you ("actually, do X not Y")
|
|
7392
|
-
- **Patterns**: recurring approaches that work ("for this type of task, always...")
|
|
7393
|
-
- **Domain facts**: business-specific knowledge not in project docs
|
|
7394
|
-
- Prefix critical observations with [!] (corrections, constraints, errors).
|
|
7395
|
-
- Prefix notable observations with [~] (preferences, patterns).
|
|
7396
|
-
- Leave routine observations unprefixed.
|
|
7397
|
-
- Do NOT record one-time task details, things in project docs, or obvious/generic knowledge.
|
|
7398
|
-
- If your first message contains \`<daily_observations>\`, these are recent raw observations not yet consolidated into Memory. Use them as additional context.
|
|
7399
|
-
|
|
7400
|
-
**Sub-agents & delegation:**
|
|
7401
|
-
- Your context window is finite. Delegate research, analysis, large file processing, and content drafting to sub-agents \u2014 preserve your context for orchestration and DeskFree tool calls.
|
|
7402
|
-
- Sub-agents get a fresh context window with standard tools (Read, Write, Bash, Grep, WebSearch, etc.) but NO DeskFree tools. Pre-load any file content they need into the prompt.
|
|
7403
|
-
- Use \`run_in_background: true\` for parallel independent work.
|
|
7404
|
-
- During Orient, check Memory for sub-agent helper patterns. Inject relevant ones into the sub-agent prompt alongside the task.
|
|
7405
|
-
- After a sub-agent completes, reflect: did this reveal a useful delegation pattern? Something to do differently? Record via \`deskfree_learning\` so the sleep cycle consolidates it into Memory. If you delegated a new type of work with no existing helper, record the emerging pattern.
|
|
7406
|
-
- Don't over-delegate: quick reads, simple lookups, and anything requiring DeskFree tools are faster inline.
|
|
7407
|
-
|
|
7408
|
-
**Completing tasks:**
|
|
7409
|
-
- On 409 or 404 errors: STOP. Do not retry. Call deskfree_state to find available tasks.`;
|
|
7410
|
-
DESKFREE_HEARTBEAT_DIRECTIVE = `## DeskFree Heartbeat
|
|
7411
|
-
On each heartbeat, run through this checklist:
|
|
7412
|
-
|
|
7413
|
-
### 1. Work the queue
|
|
7414
|
-
- Run \`deskfree_state\` to get the full workspace snapshot.
|
|
7415
|
-
- Any open tasks with awaiting=bot? Use \`deskfree_dispatch_worker\` to dispatch a worker for each. Pass the taskId.
|
|
7416
|
-
- Any open tasks that seem stalled (claimed but no recent activity)? Check on them.
|
|
7417
|
-
|
|
7418
|
-
### 2. Proactive assessment
|
|
7419
|
-
After handling the queue, step back and think about the bigger picture. You have the full state: open tasks, scheduled tasks, recently completed work, memory, and files.
|
|
7420
|
-
|
|
7421
|
-
**Think through:**
|
|
7422
|
-
- What momentum exists? What was recently accomplished, and what naturally follows?
|
|
7423
|
-
- What is stalled or falling behind? Anything open too long without progress?
|
|
7424
|
-
- What is scheduled soon? Does anything need prep work before it lands?
|
|
7425
|
-
- What is the single highest-leverage thing that could happen next?
|
|
7426
|
-
|
|
7427
|
-
**Then act \u2014 but only if you have something genuinely useful:**
|
|
7428
|
-
|
|
7429
|
-
*Things the bot can do* \u2014 research, drafts, analysis, prep. Propose as a task via \`deskfree_propose\`. One focused task, not a batch.
|
|
7430
|
-
|
|
7431
|
-
*Things the human should do* \u2014 nudges, reminders, conversation starters. Send via \`deskfree_send_message\`. Keep it brief and genuinely helpful, not nagging.
|
|
7432
|
-
|
|
7433
|
-
**Rules:**
|
|
7434
|
-
- Do not suggest things that are already open or scheduled \u2014 check first.
|
|
7435
|
-
- Do not repeat suggestions the human ignored or rejected recently.
|
|
7436
|
-
- Quality over quantity. One good insight beats five generic nudges.
|
|
7437
|
-
- If everything looks healthy and active, do nothing. Silence is fine.`;
|
|
7438
|
-
DESKFREE_SLEEP_DIRECTIVE = `## DeskFree \u2014 Nightly Sleep Cycle
|
|
7439
|
-
You are the sleep agent. You run once per day to reflect, consolidate memory, and prepare for tomorrow.
|
|
7440
|
-
|
|
7441
|
-
Tools available: deskfree_state, deskfree_propose, deskfree_send_message, deskfree_read_file, deskfree_update_file.
|
|
7442
|
-
|
|
7443
|
-
---
|
|
7444
|
-
|
|
7445
|
-
### 1. REFLECT & CONSOLIDATE
|
|
7446
|
-
|
|
7447
|
-
Your primary job: merge daily observations into the Memory file using strength-scored entries.
|
|
7448
|
-
|
|
7449
|
-
Your prompt contains both \`<current_memory>\` (the current Memory file content) and \`<daily_observations>\` (raw daily logs) inline \u2014 you have everything you need upfront.
|
|
7450
|
-
|
|
7451
|
-
**Steps:**
|
|
7452
|
-
1. Review the current memory and daily observations from your prompt.
|
|
7453
|
-
2. Apply the memory curation rules below.
|
|
7454
|
-
3. Update the Memory file via \`deskfree_update_file\` using the file ID from your prompt.
|
|
7455
|
-
4. After updating, send a brief main-thread message via \`deskfree_send_message\` noting what was consolidated (e.g. "Reflected on yesterday \u2014 updated my notes on your brand voice preferences and the weekly review pattern."). Keep it to 1-2 sentences.
|
|
7456
|
-
5. If nothing meaningful changed, skip the update and send a brief message like "Quiet day \u2014 nothing new to consolidate."
|
|
7457
|
-
|
|
7458
|
-
**Memory Types**
|
|
7459
|
-
|
|
7460
|
-
Each memory item carries a type tag and strength score: \`[type:X, strength:N]\`
|
|
7461
|
-
|
|
7462
|
-
| Type | What it captures | Decay rate |
|
|
7463
|
-
|------|-----------------|------------|
|
|
7464
|
-
| \`correction\` | Explicit "do X not Y" from human | Very slow (\u22121 when strength \u226510, else no decay) |
|
|
7465
|
-
| \`preference\` | How the human wants things done | Slow (\u22121 when strength \u22656, else no decay) |
|
|
7466
|
-
| \`pattern\` | Approaches/workflows that work | Normal (see decay rules) |
|
|
7467
|
-
| \`domain\` | Business/project-specific facts | Slow (\u22121 when strength \u22656, else no decay) |
|
|
7468
|
-
| \`insight\` | Meta-observations from reflection | Normal (see decay rules) |
|
|
7469
|
-
|
|
7470
|
-
Corrections and domain facts are durable \u2014 they rarely become irrelevant.
|
|
7471
|
-
Patterns and insights decay normally \u2014 stale approaches should be forgotten.
|
|
7472
|
-
Preferences sit in between \u2014 slow decay, but they can evolve.
|
|
7473
|
-
|
|
7474
|
-
**Strength Scoring Rules**
|
|
7475
|
-
|
|
7476
|
-
Strength uses Ebbinghaus-inspired logarithmic decay \u2014 strong memories resist forgetting:
|
|
7477
|
-
|
|
7478
|
-
**Reinforcement (observation matches existing memory):**
|
|
7479
|
-
- strength +2
|
|
7480
|
-
|
|
7481
|
-
**Explicit correction ("actually do X not Y"):**
|
|
7482
|
-
- Replace old memory, new one starts at [strength: 3, type: correction]
|
|
7483
|
-
|
|
7484
|
-
**Decay (memory NOT referenced by any daily observation):**
|
|
7485
|
-
- strength \u2265 10: decay by \u22121 (deeply encoded, slow forgetting)
|
|
7486
|
-
- strength 5-9: decay by \u22122 (moderately encoded)
|
|
7487
|
-
- strength 1-4: decay by \u22123 (weakly encoded, fast forgetting)
|
|
7488
|
-
- EXCEPT: corrections and domain facts with strength \u22656 decay at \u22121 max (durable memories)
|
|
7489
|
-
- EXCEPT: preferences with strength \u22656 decay at \u22121 max
|
|
7490
|
-
|
|
7491
|
-
**Removal:**
|
|
7492
|
-
- Strength reaches 0 \u2192 move to a ## Fading section at the bottom (one-line summaries only, no strength tags). Keep max 10 fading memories. If Fading section is full, oldest entries are permanently forgotten. You may rescue a fading memory back to active if it becomes relevant again (re-add with [strength: 2]).
|
|
7493
|
-
|
|
7494
|
-
**New observation:**
|
|
7495
|
-
- Assess importance: how consequential is this for future tasks?
|
|
7496
|
-
- Look for [!] prefix (critical) or [~] prefix (notable) as importance signals from the worker.
|
|
7497
|
-
- Low importance (casual mention, routine) \u2192 [strength: 1, type: <appropriate>]
|
|
7498
|
-
- Medium importance (useful preference, [~] prefix) \u2192 [strength: 2, type: <appropriate>]
|
|
7499
|
-
- High importance (explicit correction, strong constraint, [!] prefix) \u2192 [strength: 4, type: <appropriate>]
|
|
7500
|
-
- Critical (production error, absolute rule) \u2192 [strength: 6, type: <appropriate>]
|
|
7501
|
-
|
|
7502
|
-
**Consolidation rules:**
|
|
7503
|
-
- Classify each memory with the correct type. Only keep genuinely reusable knowledge.
|
|
7504
|
-
- Discard noise and one-off task details.
|
|
7505
|
-
- If the same episodic observation appears 3+ times across different days, promote it to a \`pattern\` or \`domain\` fact with [strength: 5]. Remove the individual episodes.
|
|
7506
|
-
- Look for meta-patterns: approaches that consistently work/fail, implicit preferences, recurring corrections.
|
|
7507
|
-
- If you find non-obvious insights, add them as \`[type: insight, strength: 2]\` memories.
|
|
7508
|
-
- Merge near-duplicates, remove items at [strength: 0], trim verbose entries.
|
|
7509
|
-
- Keep total document under ~4000 words.
|
|
7510
|
-
- Use ## headers for sections \u2014 let them emerge organically from content.
|
|
7511
|
-
|
|
7512
|
-
### 2. CHECK RECURRING COMMITMENTS
|
|
7513
|
-
|
|
7514
|
-
After memory consolidation:
|
|
7515
|
-
1. Call \`deskfree_state\` to see the board.
|
|
7516
|
-
2. Cross-reference memory for recurring patterns (daily audits, weekly reviews, etc.).
|
|
7517
|
-
3. If a recurring commitment has no upcoming scheduled task, propose it via \`deskfree_propose\` with appropriate \`scheduledFor\`.
|
|
7518
|
-
4. Don't propose things already on the board or that were recently completed/ignored.
|
|
7519
|
-
|
|
7520
|
-
### 3. PROACTIVE OPPORTUNITIES
|
|
7521
|
-
|
|
7522
|
-
Based on recent work, completed tasks, and patterns in memory \u2014 is there something genuinely useful to propose or a message worth sending?
|
|
7523
|
-
- One focused proposal max. Skip if nothing merits it.
|
|
7524
|
-
- Quality over quantity. Don't force it.
|
|
7525
|
-
|
|
7526
|
-
### Rules
|
|
7527
|
-
- Keep main thread messages short (1-2 sentences).
|
|
7528
|
-
- Do NOT propose things the human has previously ignored or rejected.
|
|
7529
|
-
- Use \`deskfree_read_file\` only if you need to re-read the Memory file after your own update (verification). The current content is already in your prompt.`;
|
|
7530
|
-
DESKFREE_DUSK_DIRECTIVE = `## DeskFree \u2014 Evening Dusk Cycle
|
|
7531
|
-
You are the dusk agent. You run once per day in the evening to review the day, propose overnight work, and brief the human.
|
|
8017
|
+
description: 'What you learned. Prefix with importance: [!] for critical (corrections, constraints), [~] for notable (preferences, patterns), or nothing for routine observations. Focus on: PREFERENCES, CORRECTIONS, PATTERNS, DOMAIN FACTS. Be specific. Bad: "Created a table". Good: "[!] User corrected: never use semicolons in this codebase".'
|
|
8018
|
+
}),
|
|
8019
|
+
taskId: Type.Optional(
|
|
8020
|
+
Type.String({
|
|
8021
|
+
description: "Task ID (optional if context provides it)"
|
|
8022
|
+
})
|
|
8023
|
+
)
|
|
8024
|
+
})
|
|
8025
|
+
},
|
|
8026
|
+
COMPLETE_TASK: SHARED_TOOLS.COMPLETE_TASK,
|
|
8027
|
+
SEND_MESSAGE: SHARED_TOOLS.SEND_MESSAGE,
|
|
8028
|
+
PROPOSE: SHARED_TOOLS.PROPOSE
|
|
8029
|
+
};
|
|
8030
|
+
MAX_FULL_MESSAGES = 15;
|
|
8031
|
+
DESKFREE_AGENT_DIRECTIVE = `## DeskFree \u2014 Orchestrator
|
|
8032
|
+
You are the orchestrator. Your job: turn human intent into approved tasks, then dispatch work.
|
|
7532
8033
|
|
|
7533
|
-
|
|
8034
|
+
**Main thread = short and snappy.** Keep responses to 1-3 sentences. Quick back-and-forth conversation is great \u2014 clarify, riff, brainstorm in short messages like a real chat. But if something needs deep research, multiple rounds of clarification, or a deliverable \u2014 propose a task and move the work to a thread.
|
|
7534
8035
|
|
|
7535
|
-
|
|
8036
|
+
**The core loop: propose \u2192 approve \u2192 work.**
|
|
7536
8037
|
|
|
7537
|
-
|
|
8038
|
+
1. **Check state** \u2192 \`deskfree_state\` \u2014 see tasks, memory (a pinned file with accumulated knowledge), and files.
|
|
8039
|
+
2. **Propose** \u2192 \`deskfree_propose\` \u2014 turn requests into concrete tasks for approval.
|
|
8040
|
+
3. **Dispatch** \u2192 \`deskfree_dispatch_worker\` with the taskId.
|
|
8041
|
+
4. **Communicate** \u2192 \`deskfree_send_message\` for updates outside task threads.
|
|
7538
8042
|
|
|
7539
|
-
|
|
8043
|
+
**Before proposing, qualify the request.** Figure out what kind of thing this is:
|
|
8044
|
+
- **One-off task** ("proofread this") \u2192 propose a task directly.
|
|
8045
|
+
- **New aspiration** ("I want to start posting on LinkedIn") \u2192 don't rush to propose. Ask 1-2 short qualifying questions to understand the real goal.
|
|
8046
|
+
- Never call \`deskfree_propose\` as your very first action \u2014 qualify first, even if briefly.
|
|
7540
8047
|
|
|
7541
|
-
**
|
|
7542
|
-
1. Review the current memory and daily observations from your prompt.
|
|
7543
|
-
2. Call \`deskfree_state\` to see the current board \u2014 open tasks, recently completed work, files.
|
|
7544
|
-
3. Build a mental model of what happened today: what was accomplished, what's in progress, what's stalled.
|
|
8048
|
+
**Match the human's energy.** Short message \u2192 short reply. Casual tone \u2192 casual response. Don't over-explain, don't lecture, don't pad responses.
|
|
7545
8049
|
|
|
7546
|
-
|
|
8050
|
+
You do NOT claim tasks, complete tasks, or do work directly \u2014 you have no access to deskfree_start_task or deskfree_complete_task. Use \`deskfree_dispatch_worker\` to dispatch a worker for each approved task.
|
|
8051
|
+
- When a human writes in a task thread, decide:
|
|
8052
|
+
- **Continuation of the same task?** \u2192 reopen and dispatch a worker.
|
|
8053
|
+
- **New/different work request?** \u2192 propose it as a new task (don't reopen the old one or do the work yourself).
|
|
8054
|
+
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
8055
|
+
- Estimate token cost per task \u2014 consider files to read, reasoning, output.`;
|
|
8056
|
+
DESKFREE_WORKER_DIRECTIVE = `## DeskFree Worker
|
|
8057
|
+
You are a worker sub-agent. Your first message contains pre-loaded context \u2014 use it directly.
|
|
8058
|
+
Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_file, deskfree_update_file, deskfree_learning, deskfree_complete_task, deskfree_send_message, deskfree_propose.
|
|
7547
8059
|
|
|
7548
|
-
|
|
8060
|
+
**Context loading:**
|
|
8061
|
+
- If your first message contains \`<task_context>\`, the task is already claimed and context is pre-loaded. Do NOT call deskfree_start_task \u2014 start working immediately.
|
|
8062
|
+
- If your first message contains \`<workspace_state>\`, use it for situational awareness (other tasks, memory, files).
|
|
8063
|
+
- If no pre-loaded context (edge case/fallback), call \`deskfree_start_task\` with your taskId to claim and load context.
|
|
8064
|
+
- If continuing from a previous conversation (you can see prior tool calls and context), respond directly to the human's latest message \u2014 do NOT call deskfree_start_task again.
|
|
7549
8065
|
|
|
7550
|
-
**
|
|
7551
|
-
- Research and analysis (market research, competitive analysis, reading material)
|
|
7552
|
-
- Content drafts (blog posts, reports, documentation)
|
|
7553
|
-
- File updates (updating recurring reports, preparing templates)
|
|
7554
|
-
- Prep work for tomorrow (gathering context, pre-reading for scheduled meetings)
|
|
7555
|
-
- Recurring tasks that are due (weekly reviews, daily audits)
|
|
8066
|
+
**Orient \u2192 Align \u2192 Work.** Every new task follows this rhythm:
|
|
7556
8067
|
|
|
7557
|
-
**
|
|
7558
|
-
|
|
7559
|
-
-
|
|
7560
|
-
-
|
|
7561
|
-
|
|
8068
|
+
1. **Orient** \u2014 Scan workspace state for relevant files. Read the Memory file for context on preferences and past patterns. Read any other files that are useful context with \`deskfree_read_file\`. Don't read everything \u2014 just what's relevant to this task.
|
|
8069
|
+
2. **Align** \u2014 Send a brief \`notify\` message: what you found, what you'll produce. One or two sentences. ("I'll build on the existing brand guide and create a new tone reference.")
|
|
8070
|
+
- **Judgment calls or creative direction?** State your assumptions and approach, send as \`ask\`, and wait for confirmation before proceeding. Getting alignment early prevents costly rework.
|
|
8071
|
+
- **Straightforward execution?** Proceed immediately after the notify \u2014 don't wait for a response.
|
|
8072
|
+
3. **Work** \u2014 Execute the task. Update existing files with \`deskfree_update_file\` or create new ones with \`deskfree_create_file\`. Pass your taskId so updates appear in the thread. For large deliverables, build incrementally \u2014 share structure/outline first, then flesh out. Don't produce a finished 2000-word document and ask for review in one shot.
|
|
8073
|
+
4. **Deliver** \u2014 Send an \`ask\` message when work is ready for review. Only complete (\`deskfree_complete_task\` with humanApproved: true) after the human has confirmed. Never self-complete.
|
|
7562
8074
|
|
|
7563
|
-
|
|
8075
|
+
**Push back when warranted:**
|
|
8076
|
+
- If task instructions seem unclear, contradictory, or misguided \u2014 say so. "This task asks for X, but based on [context], Y might work better because..." is more useful than silently executing a flawed plan.
|
|
8077
|
+
- If you hit genuine ambiguity mid-task, send an \`ask\` message and wait. Don't guess on important decisions \u2014 guessing creates review debt the human has to pay later.
|
|
8078
|
+
- You're a teammate, not a task executor. Have an opinion when you have the context to form one.
|
|
7564
8079
|
|
|
7565
|
-
|
|
7566
|
-
|
|
7567
|
-
|
|
7568
|
-
|
|
7569
|
-
4. If nothing genuinely useful can be done overnight, skip the proposal entirely. Don't force it.
|
|
8080
|
+
**File rules:**
|
|
8081
|
+
- Create files when your task naturally produces them. Don't be afraid to create multiple files if the work calls for it.
|
|
8082
|
+
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
8083
|
+
- If you discover work that falls outside your task's scope, use \`deskfree_propose\` to suggest follow-up tasks immediately \u2014 don't wait until completion. Propose as you discover, then stay focused on your current task.
|
|
7570
8084
|
|
|
7571
|
-
|
|
8085
|
+
**Learnings:**
|
|
8086
|
+
- Use \`deskfree_learning\` to record observations worth remembering. A nightly sleep cycle consolidates these into the Memory file. Record:
|
|
8087
|
+
- **Preferences**: how the human wants things done ("prefers X over Y")
|
|
8088
|
+
- **Corrections**: when the human corrects you ("actually, do X not Y")
|
|
8089
|
+
- **Patterns**: recurring approaches that work ("for this type of task, always...")
|
|
8090
|
+
- **Domain facts**: business-specific knowledge not in project docs
|
|
8091
|
+
- Prefix critical observations with [!] (corrections, constraints, errors).
|
|
8092
|
+
- Prefix notable observations with [~] (preferences, patterns).
|
|
8093
|
+
- Leave routine observations unprefixed.
|
|
8094
|
+
- Do NOT record one-time task details, things in project docs, or obvious/generic knowledge.
|
|
8095
|
+
- If your first message contains \`<daily_observations>\`, these are recent raw observations not yet consolidated into Memory. Use them as additional context.
|
|
7572
8096
|
|
|
7573
|
-
|
|
7574
|
-
-
|
|
7575
|
-
-
|
|
7576
|
-
-
|
|
8097
|
+
**Sub-agents & delegation:**
|
|
8098
|
+
- Your context window is finite. Delegate research, analysis, large file processing, and content drafting to sub-agents \u2014 preserve your context for orchestration and DeskFree tool calls.
|
|
8099
|
+
- Sub-agents get a fresh context window with standard tools (Read, Write, Bash, Grep, WebSearch, etc.) but NO DeskFree tools. Pre-load any file content they need into the prompt.
|
|
8100
|
+
- Sub-agents are ephemeral helpers \u2014 they complete their assigned task and nothing else. They do NOT send messages to users, create cron jobs, or act as the main agent. Their final output is returned to you.
|
|
8101
|
+
- Use \`run_in_background: true\` for parallel independent work.
|
|
8102
|
+
- During Orient, check Memory for sub-agent helper patterns. Inject relevant ones into the sub-agent prompt alongside the task.
|
|
8103
|
+
- After a sub-agent completes, reflect: did this reveal a useful delegation pattern? Something to do differently? Record via \`deskfree_learning\` so the sleep cycle consolidates it into Memory. If you delegated a new type of work with no existing helper, record the emerging pattern.
|
|
8104
|
+
- Don't over-delegate: quick reads, simple lookups, and anything requiring DeskFree tools are faster inline.
|
|
7577
8105
|
|
|
7578
|
-
|
|
7579
|
-
-
|
|
7580
|
-
- Do NOT repeat suggestions the human previously ignored or rejected.
|
|
7581
|
-
- Quality over quantity. One good task beats three mediocre ones.
|
|
7582
|
-
- Keep the briefing message short and actionable.
|
|
7583
|
-
- Cross-reference memory for recurring patterns \u2014 if something is due, propose it.
|
|
7584
|
-
- Use \`deskfree_read_file\` only if you need file content beyond what's in your prompt.`;
|
|
8106
|
+
**Completing tasks:**
|
|
8107
|
+
- On 409 or 404 errors: STOP. Do not retry. Call deskfree_state to find available tasks.`;
|
|
7585
8108
|
THROTTLE_MS = 300;
|
|
7586
8109
|
CHAR_BUFFER_SIZE = 256;
|
|
7587
8110
|
CLOSE_MAX_RETRIES = 3;
|
|
@@ -7793,7 +8316,15 @@ Send a brief main-thread message via \`deskfree_send_message\`:
|
|
|
7793
8316
|
}
|
|
7794
8317
|
});
|
|
7795
8318
|
function runOrchestrator(opts) {
|
|
7796
|
-
const {
|
|
8319
|
+
const {
|
|
8320
|
+
prompt,
|
|
8321
|
+
orchestratorServer,
|
|
8322
|
+
model,
|
|
8323
|
+
sessionId,
|
|
8324
|
+
claudeCodePath,
|
|
8325
|
+
agentContext
|
|
8326
|
+
} = opts;
|
|
8327
|
+
const systemPrompt = agentContext ? buildAgentDirective(agentContext) : DESKFREE_AGENT_DIRECTIVE;
|
|
7797
8328
|
return query({
|
|
7798
8329
|
prompt,
|
|
7799
8330
|
options: {
|
|
@@ -7801,7 +8332,7 @@ function runOrchestrator(opts) {
|
|
|
7801
8332
|
process.stderr.write(`[orchestrator-sdk] ${data}
|
|
7802
8333
|
`);
|
|
7803
8334
|
},
|
|
7804
|
-
systemPrompt
|
|
8335
|
+
systemPrompt,
|
|
7805
8336
|
model,
|
|
7806
8337
|
...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
|
|
7807
8338
|
maxTurns: MAX_ORCHESTRATOR_TURNS,
|
|
@@ -7814,28 +8345,18 @@ function runOrchestrator(opts) {
|
|
|
7814
8345
|
"deskfree-orchestrator": orchestratorServer
|
|
7815
8346
|
},
|
|
7816
8347
|
tools: [],
|
|
7817
|
-
allowedTools:
|
|
7818
|
-
"mcp__deskfree-orchestrator__*",
|
|
7819
|
-
"Read",
|
|
7820
|
-
"Write",
|
|
7821
|
-
"Edit",
|
|
7822
|
-
"Bash",
|
|
7823
|
-
"Glob",
|
|
7824
|
-
"Grep",
|
|
7825
|
-
"WebSearch",
|
|
7826
|
-
"WebFetch",
|
|
7827
|
-
"NotebookEdit"
|
|
7828
|
-
],
|
|
8348
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
7829
8349
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
7830
8350
|
}
|
|
7831
8351
|
});
|
|
7832
8352
|
}
|
|
7833
8353
|
function runHeartbeat(opts) {
|
|
7834
|
-
const { prompt, orchestratorServer, model, claudeCodePath } = opts;
|
|
8354
|
+
const { prompt, orchestratorServer, model, claudeCodePath, agentContext } = opts;
|
|
8355
|
+
const systemPrompt = agentContext ? buildHeartbeatDirective(agentContext) : DESKFREE_AGENT_DIRECTIVE;
|
|
7835
8356
|
return query({
|
|
7836
8357
|
prompt,
|
|
7837
8358
|
options: {
|
|
7838
|
-
systemPrompt
|
|
8359
|
+
systemPrompt,
|
|
7839
8360
|
model,
|
|
7840
8361
|
...claudeCodePath ? { pathToClaudeCodeExecutable: claudeCodePath } : {},
|
|
7841
8362
|
maxTurns: MAX_ORCHESTRATOR_TURNS,
|
|
@@ -7846,18 +8367,7 @@ function runHeartbeat(opts) {
|
|
|
7846
8367
|
"deskfree-orchestrator": orchestratorServer
|
|
7847
8368
|
},
|
|
7848
8369
|
tools: [],
|
|
7849
|
-
allowedTools:
|
|
7850
|
-
"mcp__deskfree-orchestrator__*",
|
|
7851
|
-
"Read",
|
|
7852
|
-
"Write",
|
|
7853
|
-
"Edit",
|
|
7854
|
-
"Bash",
|
|
7855
|
-
"Glob",
|
|
7856
|
-
"Grep",
|
|
7857
|
-
"WebSearch",
|
|
7858
|
-
"WebFetch",
|
|
7859
|
-
"NotebookEdit"
|
|
7860
|
-
],
|
|
8370
|
+
allowedTools: ORCHESTRATOR_ALLOWED_TOOLS,
|
|
7861
8371
|
disallowedTools: DISALLOWED_BUILTIN_TOOLS
|
|
7862
8372
|
}
|
|
7863
8373
|
});
|
|
@@ -7880,11 +8390,19 @@ function runOneShotWorker(opts) {
|
|
|
7880
8390
|
}
|
|
7881
8391
|
});
|
|
7882
8392
|
}
|
|
7883
|
-
var MAX_ORCHESTRATOR_TURNS, DISALLOWED_BUILTIN_TOOLS;
|
|
8393
|
+
var MAX_ORCHESTRATOR_TURNS, ORCHESTRATOR_ALLOWED_TOOLS, DISALLOWED_BUILTIN_TOOLS;
|
|
7884
8394
|
var init_orchestrator = __esm({
|
|
7885
8395
|
"src/agents/orchestrator.ts"() {
|
|
7886
8396
|
init_dist();
|
|
7887
8397
|
MAX_ORCHESTRATOR_TURNS = 20;
|
|
8398
|
+
ORCHESTRATOR_ALLOWED_TOOLS = [
|
|
8399
|
+
"mcp__deskfree-orchestrator__*",
|
|
8400
|
+
"Read",
|
|
8401
|
+
"Glob",
|
|
8402
|
+
"Grep",
|
|
8403
|
+
"WebSearch",
|
|
8404
|
+
"WebFetch"
|
|
8405
|
+
];
|
|
7888
8406
|
DISALLOWED_BUILTIN_TOOLS = [
|
|
7889
8407
|
"TodoWrite",
|
|
7890
8408
|
"AskUserQuestion",
|
|
@@ -7961,8 +8479,8 @@ function loadConfig() {
|
|
|
7961
8479
|
function mergeWithRemoteConfig(local, remote) {
|
|
7962
8480
|
const stateDirOverridden = !!process.env["DESKFREE_STATE_DIR"];
|
|
7963
8481
|
const toolsDirOverridden = !!process.env["DESKFREE_TOOLS_DIR"];
|
|
7964
|
-
const stateDir = stateDirOverridden ? local.stateDir : isDocker ? local.stateDir :
|
|
7965
|
-
const toolsDir = toolsDirOverridden ? local.toolsDir : isDocker ? local.toolsDir :
|
|
8482
|
+
const stateDir = stateDirOverridden ? local.stateDir : isDocker ? local.stateDir : join(homedir(), ".deskfree", remote.botId, "state");
|
|
8483
|
+
const toolsDir = toolsDirOverridden ? local.toolsDir : isDocker ? local.toolsDir : join(homedir(), ".deskfree", remote.botId, "tools");
|
|
7966
8484
|
let claudeCodePath;
|
|
7967
8485
|
if (remote.provider === "claude-code") {
|
|
7968
8486
|
try {
|
|
@@ -7990,6 +8508,8 @@ function mergeWithRemoteConfig(local, remote) {
|
|
|
7990
8508
|
anthropicApiKey: remote.anthropicApiKey,
|
|
7991
8509
|
baseUrl: process.env["DESKFREE_BASE_URL"] ?? remote.baseUrl,
|
|
7992
8510
|
botId: remote.botId,
|
|
8511
|
+
botName: remote.botName,
|
|
8512
|
+
deploymentType: remote.deploymentType,
|
|
7993
8513
|
memoryFileId: remote.memoryFileId,
|
|
7994
8514
|
sleepHour: remote.sleepHour,
|
|
7995
8515
|
duskHour: remote.duskHour,
|
|
@@ -8002,8 +8522,8 @@ var init_config = __esm({
|
|
|
8002
8522
|
init_dist();
|
|
8003
8523
|
isDocker = process.env["DOCKER"] === "1" || existsSync("/.dockerenv");
|
|
8004
8524
|
DEFAULTS = {
|
|
8005
|
-
stateDir: isDocker ? "/app/state" : ".deskfree
|
|
8006
|
-
toolsDir: isDocker ? "/app/tools" : ".deskfree
|
|
8525
|
+
stateDir: isDocker ? "/app/state" : join(homedir(), ".deskfree", "state"),
|
|
8526
|
+
toolsDir: isDocker ? "/app/tools" : join(homedir(), ".deskfree", "tools"),
|
|
8007
8527
|
logLevel: "info",
|
|
8008
8528
|
healthPort: 3100
|
|
8009
8529
|
};
|
|
@@ -8049,6 +8569,86 @@ var init_health_state = __esm({
|
|
|
8049
8569
|
}
|
|
8050
8570
|
});
|
|
8051
8571
|
|
|
8572
|
+
// src/util/logger.ts
|
|
8573
|
+
var logger_exports = {};
|
|
8574
|
+
__export(logger_exports, {
|
|
8575
|
+
createLogger: () => createLogger,
|
|
8576
|
+
enableFileLogging: () => enableFileLogging,
|
|
8577
|
+
getLogFilePath: () => getLogFilePath,
|
|
8578
|
+
logger: () => logger
|
|
8579
|
+
});
|
|
8580
|
+
function enableFileLogging(filePath) {
|
|
8581
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
8582
|
+
logFilePath = filePath;
|
|
8583
|
+
}
|
|
8584
|
+
function getLogFilePath() {
|
|
8585
|
+
return logFilePath;
|
|
8586
|
+
}
|
|
8587
|
+
function rotateIfNeeded() {
|
|
8588
|
+
if (!logFilePath) return;
|
|
8589
|
+
try {
|
|
8590
|
+
const stat = statSync(logFilePath);
|
|
8591
|
+
if (stat.size <= MAX_LOG_FILE_BYTES) return;
|
|
8592
|
+
const content = readFileSync(logFilePath, "utf8");
|
|
8593
|
+
const half = Math.floor(content.length / 2);
|
|
8594
|
+
const newlineIdx = content.indexOf("\n", half);
|
|
8595
|
+
const trimmed = newlineIdx >= 0 ? content.slice(newlineIdx + 1) : content.slice(half);
|
|
8596
|
+
writeFileSync(logFilePath, trimmed);
|
|
8597
|
+
} catch {
|
|
8598
|
+
}
|
|
8599
|
+
}
|
|
8600
|
+
function createLogger(component, minLevel = "info") {
|
|
8601
|
+
const minLevelNum = LEVELS[minLevel];
|
|
8602
|
+
function log(level, message, fields) {
|
|
8603
|
+
if (LEVELS[level] < minLevelNum) return;
|
|
8604
|
+
const entry = {
|
|
8605
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8606
|
+
level,
|
|
8607
|
+
component,
|
|
8608
|
+
msg: message,
|
|
8609
|
+
...fields
|
|
8610
|
+
};
|
|
8611
|
+
const line = JSON.stringify(entry);
|
|
8612
|
+
if (level === "error" || level === "warn") {
|
|
8613
|
+
process.stderr.write(line + "\n");
|
|
8614
|
+
} else {
|
|
8615
|
+
process.stdout.write(line + "\n");
|
|
8616
|
+
}
|
|
8617
|
+
if (logFilePath) {
|
|
8618
|
+
try {
|
|
8619
|
+
appendFileSync(logFilePath, line + "\n");
|
|
8620
|
+
if (++writesSinceCheck >= ROTATION_CHECK_INTERVAL) {
|
|
8621
|
+
writesSinceCheck = 0;
|
|
8622
|
+
rotateIfNeeded();
|
|
8623
|
+
}
|
|
8624
|
+
} catch {
|
|
8625
|
+
}
|
|
8626
|
+
}
|
|
8627
|
+
}
|
|
8628
|
+
return {
|
|
8629
|
+
debug: (msg, fields) => log("debug", msg, fields),
|
|
8630
|
+
info: (msg, fields) => log("info", msg, fields),
|
|
8631
|
+
warn: (msg, fields) => log("warn", msg, fields),
|
|
8632
|
+
error: (msg, fields) => log("error", msg, fields)
|
|
8633
|
+
};
|
|
8634
|
+
}
|
|
8635
|
+
var LEVELS, MAX_LOG_FILE_BYTES, ROTATION_CHECK_INTERVAL, logFilePath, writesSinceCheck, logger;
|
|
8636
|
+
var init_logger = __esm({
|
|
8637
|
+
"src/util/logger.ts"() {
|
|
8638
|
+
LEVELS = {
|
|
8639
|
+
debug: 0,
|
|
8640
|
+
info: 1,
|
|
8641
|
+
warn: 2,
|
|
8642
|
+
error: 3
|
|
8643
|
+
};
|
|
8644
|
+
MAX_LOG_FILE_BYTES = 5 * 1024 * 1024;
|
|
8645
|
+
ROTATION_CHECK_INTERVAL = 500;
|
|
8646
|
+
logFilePath = null;
|
|
8647
|
+
writesSinceCheck = 0;
|
|
8648
|
+
logger = createLogger("runtime");
|
|
8649
|
+
}
|
|
8650
|
+
});
|
|
8651
|
+
|
|
8052
8652
|
// src/gateway/polling.ts
|
|
8053
8653
|
async function pollAndDeliver(client, accountId, stateDir, cursor, onMessage, log) {
|
|
8054
8654
|
const ctx = { accountId };
|
|
@@ -11753,8 +12353,6 @@ var init_wrapper = __esm({
|
|
|
11753
12353
|
wrapper_default2 = import_websocket2.default;
|
|
11754
12354
|
}
|
|
11755
12355
|
});
|
|
11756
|
-
|
|
11757
|
-
// src/gateway/ws-gateway.ts
|
|
11758
12356
|
function nextBackoff(state2) {
|
|
11759
12357
|
const delay = Math.min(
|
|
11760
12358
|
BACKOFF_INITIAL_MS * Math.pow(BACKOFF_FACTOR, state2.attempt),
|
|
@@ -11934,11 +12532,11 @@ async function runWebSocketConnection(opts) {
|
|
|
11934
12532
|
}, PING_INTERVAL_MS);
|
|
11935
12533
|
if (opts.getWorkerStatus) {
|
|
11936
12534
|
try {
|
|
11937
|
-
const
|
|
12535
|
+
const status2 = opts.getWorkerStatus();
|
|
11938
12536
|
ws.send(
|
|
11939
12537
|
JSON.stringify({
|
|
11940
12538
|
action: "heartbeatResponse",
|
|
11941
|
-
...
|
|
12539
|
+
...status2
|
|
11942
12540
|
})
|
|
11943
12541
|
);
|
|
11944
12542
|
} catch (err) {
|
|
@@ -12008,11 +12606,11 @@ async function runWebSocketConnection(opts) {
|
|
|
12008
12606
|
} else if (msg.action === "heartbeatRequest") {
|
|
12009
12607
|
if (opts.getWorkerStatus && ws.readyState === wrapper_default2.OPEN) {
|
|
12010
12608
|
try {
|
|
12011
|
-
const
|
|
12609
|
+
const status2 = opts.getWorkerStatus();
|
|
12012
12610
|
ws.send(
|
|
12013
12611
|
JSON.stringify({
|
|
12014
12612
|
action: "heartbeatResponse",
|
|
12015
|
-
...
|
|
12613
|
+
...status2
|
|
12016
12614
|
})
|
|
12017
12615
|
);
|
|
12018
12616
|
} catch (err) {
|
|
@@ -12027,6 +12625,49 @@ async function runWebSocketConnection(opts) {
|
|
|
12027
12625
|
cleanup();
|
|
12028
12626
|
ws.close(1e3, "reload");
|
|
12029
12627
|
process.kill(process.pid, "SIGTERM");
|
|
12628
|
+
} else if (msg.action === "logs") {
|
|
12629
|
+
const MAX_RESPONSE_BYTES = 12e4;
|
|
12630
|
+
const lineCount = msg.lines ?? 200;
|
|
12631
|
+
try {
|
|
12632
|
+
const logFile = getLogFilePath();
|
|
12633
|
+
if (!logFile) {
|
|
12634
|
+
ws.send(
|
|
12635
|
+
JSON.stringify({
|
|
12636
|
+
action: "logsResponse",
|
|
12637
|
+
lines: [],
|
|
12638
|
+
error: "File logging not enabled"
|
|
12639
|
+
})
|
|
12640
|
+
);
|
|
12641
|
+
} else {
|
|
12642
|
+
const content = readFileSync(logFile, "utf8");
|
|
12643
|
+
const allLines = content.split("\n").filter(Boolean);
|
|
12644
|
+
let tail = allLines.slice(-lineCount);
|
|
12645
|
+
while (tail.length > 1) {
|
|
12646
|
+
const payload = JSON.stringify({
|
|
12647
|
+
action: "logsResponse",
|
|
12648
|
+
lines: tail
|
|
12649
|
+
});
|
|
12650
|
+
if (Buffer.byteLength(payload) <= MAX_RESPONSE_BYTES) break;
|
|
12651
|
+
tail = tail.slice(Math.ceil(tail.length * 0.25));
|
|
12652
|
+
}
|
|
12653
|
+
ws.send(
|
|
12654
|
+
JSON.stringify({
|
|
12655
|
+
action: "logsResponse",
|
|
12656
|
+
lines: tail
|
|
12657
|
+
})
|
|
12658
|
+
);
|
|
12659
|
+
}
|
|
12660
|
+
} catch (err) {
|
|
12661
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
12662
|
+
log.warn(`Failed to read logs: ${errMsg}`);
|
|
12663
|
+
ws.send(
|
|
12664
|
+
JSON.stringify({
|
|
12665
|
+
action: "logsResponse",
|
|
12666
|
+
lines: [],
|
|
12667
|
+
error: errMsg
|
|
12668
|
+
})
|
|
12669
|
+
);
|
|
12670
|
+
}
|
|
12030
12671
|
}
|
|
12031
12672
|
} catch (err) {
|
|
12032
12673
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -12145,6 +12786,7 @@ var PING_INTERVAL_MS, POLL_FALLBACK_INTERVAL_MS, WS_CONNECTION_TIMEOUT_MS, WS_PO
|
|
|
12145
12786
|
var init_ws_gateway = __esm({
|
|
12146
12787
|
"src/gateway/ws-gateway.ts"() {
|
|
12147
12788
|
init_health_state();
|
|
12789
|
+
init_logger();
|
|
12148
12790
|
init_polling();
|
|
12149
12791
|
init_dist();
|
|
12150
12792
|
init_wrapper();
|
|
@@ -12639,21 +13281,35 @@ function scheduleDailyCycle(label, run, hour, timezone, signal, log) {
|
|
|
12639
13281
|
}
|
|
12640
13282
|
function msUntilNextLocalHour(hour, timezone) {
|
|
12641
13283
|
const now = Date.now();
|
|
12642
|
-
const
|
|
13284
|
+
const hourFmt = new Intl.DateTimeFormat("en-US", {
|
|
12643
13285
|
timeZone: timezone,
|
|
12644
13286
|
hour: "numeric",
|
|
12645
13287
|
hour12: false
|
|
12646
13288
|
});
|
|
13289
|
+
const detailFmt = new Intl.DateTimeFormat("en-US", {
|
|
13290
|
+
timeZone: timezone,
|
|
13291
|
+
minute: "numeric",
|
|
13292
|
+
second: "numeric"
|
|
13293
|
+
});
|
|
12647
13294
|
for (let offsetMs = 6e4; offsetMs <= 25 * 36e5; offsetMs += 36e5) {
|
|
12648
13295
|
const candidateMs = now + offsetMs;
|
|
12649
|
-
const parts =
|
|
13296
|
+
const parts = hourFmt.formatToParts(new Date(candidateMs));
|
|
12650
13297
|
const hourPart = parts.find((p) => p.type === "hour");
|
|
12651
13298
|
const candidateHour = parseInt(hourPart?.value ?? "-1", 10);
|
|
12652
13299
|
if (candidateHour === hour) {
|
|
12653
|
-
const
|
|
12654
|
-
|
|
12655
|
-
|
|
12656
|
-
|
|
13300
|
+
const detailParts = detailFmt.formatToParts(new Date(candidateMs));
|
|
13301
|
+
const localMinute = parseInt(
|
|
13302
|
+
detailParts.find((p) => p.type === "minute")?.value ?? "0",
|
|
13303
|
+
10
|
|
13304
|
+
);
|
|
13305
|
+
const localSecond = parseInt(
|
|
13306
|
+
detailParts.find((p) => p.type === "second")?.value ?? "0",
|
|
13307
|
+
10
|
|
13308
|
+
);
|
|
13309
|
+
const snappedMs = candidateMs - localMinute * 6e4 - localSecond * 1e3;
|
|
13310
|
+
if (snappedMs > now + 6e4) {
|
|
13311
|
+
return snappedMs - now;
|
|
13312
|
+
}
|
|
12657
13313
|
}
|
|
12658
13314
|
}
|
|
12659
13315
|
return 24 * 36e5;
|
|
@@ -12749,50 +13405,6 @@ var init_registry = __esm({
|
|
|
12749
13405
|
"src/tools/registry.ts"() {
|
|
12750
13406
|
}
|
|
12751
13407
|
});
|
|
12752
|
-
|
|
12753
|
-
// src/util/logger.ts
|
|
12754
|
-
var logger_exports = {};
|
|
12755
|
-
__export(logger_exports, {
|
|
12756
|
-
createLogger: () => createLogger,
|
|
12757
|
-
logger: () => logger
|
|
12758
|
-
});
|
|
12759
|
-
function createLogger(component, minLevel = "info") {
|
|
12760
|
-
const minLevelNum = LEVELS[minLevel];
|
|
12761
|
-
function log(level, message, fields) {
|
|
12762
|
-
if (LEVELS[level] < minLevelNum) return;
|
|
12763
|
-
const entry = {
|
|
12764
|
-
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12765
|
-
level,
|
|
12766
|
-
component,
|
|
12767
|
-
msg: message,
|
|
12768
|
-
...fields
|
|
12769
|
-
};
|
|
12770
|
-
const line = JSON.stringify(entry);
|
|
12771
|
-
if (level === "error" || level === "warn") {
|
|
12772
|
-
process.stderr.write(line + "\n");
|
|
12773
|
-
} else {
|
|
12774
|
-
process.stdout.write(line + "\n");
|
|
12775
|
-
}
|
|
12776
|
-
}
|
|
12777
|
-
return {
|
|
12778
|
-
debug: (msg, fields) => log("debug", msg, fields),
|
|
12779
|
-
info: (msg, fields) => log("info", msg, fields),
|
|
12780
|
-
warn: (msg, fields) => log("warn", msg, fields),
|
|
12781
|
-
error: (msg, fields) => log("error", msg, fields)
|
|
12782
|
-
};
|
|
12783
|
-
}
|
|
12784
|
-
var LEVELS, logger;
|
|
12785
|
-
var init_logger = __esm({
|
|
12786
|
-
"src/util/logger.ts"() {
|
|
12787
|
-
LEVELS = {
|
|
12788
|
-
debug: 0,
|
|
12789
|
-
info: 1,
|
|
12790
|
-
warn: 2,
|
|
12791
|
-
error: 3
|
|
12792
|
-
};
|
|
12793
|
-
logger = createLogger("runtime");
|
|
12794
|
-
}
|
|
12795
|
-
});
|
|
12796
13408
|
function checkRateLimit(userId) {
|
|
12797
13409
|
const now = Date.now();
|
|
12798
13410
|
const entry = rateLimitMap.get(userId);
|
|
@@ -13057,7 +13669,8 @@ async function routeMessage(message, client, deps, sessionStore, config) {
|
|
|
13057
13669
|
orchestratorServer: deps.createOrchestratorServer(),
|
|
13058
13670
|
model: deps.model,
|
|
13059
13671
|
sessionId: existingSessionId,
|
|
13060
|
-
claudeCodePath: deps.claudeCodePath
|
|
13672
|
+
claudeCodePath: deps.claudeCodePath,
|
|
13673
|
+
agentContext: deps.agentContext
|
|
13061
13674
|
});
|
|
13062
13675
|
let fullText = "";
|
|
13063
13676
|
let capturedSessionId = null;
|
|
@@ -13240,17 +13853,16 @@ var init_sessions = __esm({
|
|
|
13240
13853
|
}
|
|
13241
13854
|
});
|
|
13242
13855
|
function runWorker(opts) {
|
|
13243
|
-
const { prompt, workerServer, model, sessionId } = opts;
|
|
13856
|
+
const { prompt, workerServer, model, sessionId, agentContext } = opts;
|
|
13857
|
+
const systemPrompt = agentContext ? buildWorkerDirective(agentContext) : DESKFREE_WORKER_DIRECTIVE;
|
|
13244
13858
|
return query({
|
|
13245
13859
|
prompt,
|
|
13246
13860
|
options: {
|
|
13247
|
-
debug: true,
|
|
13248
|
-
debugFile: "/dev/stderr",
|
|
13249
13861
|
stderr: (data) => {
|
|
13250
13862
|
process.stderr.write(`[worker-sdk] ${data}
|
|
13251
13863
|
`);
|
|
13252
13864
|
},
|
|
13253
|
-
systemPrompt
|
|
13865
|
+
systemPrompt,
|
|
13254
13866
|
model,
|
|
13255
13867
|
maxTurns: MAX_WORKER_TURNS,
|
|
13256
13868
|
permissionMode: "bypassPermissions",
|
|
@@ -13562,7 +14174,8 @@ ${userMessage}
|
|
|
13562
14174
|
prompt: channel,
|
|
13563
14175
|
workerServer,
|
|
13564
14176
|
model,
|
|
13565
|
-
sessionId: previousSessionId
|
|
14177
|
+
sessionId: previousSessionId,
|
|
14178
|
+
agentContext: this.deps.agentContext
|
|
13566
14179
|
});
|
|
13567
14180
|
const idleTimer = this.startIdleTimer(taskId);
|
|
13568
14181
|
const drainPromise = this.drainLoop(
|
|
@@ -13808,7 +14421,7 @@ function startHealthServer(port, log) {
|
|
|
13808
14421
|
}
|
|
13809
14422
|
};
|
|
13810
14423
|
}
|
|
13811
|
-
function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, claudeCodePath) {
|
|
14424
|
+
function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, claudeCodePath, agentContext) {
|
|
13812
14425
|
if (intervalMs <= 0) return;
|
|
13813
14426
|
let running = false;
|
|
13814
14427
|
async function tick() {
|
|
@@ -13816,11 +14429,13 @@ function scheduleHeartbeat(createOrchServer, model, intervalMs, signal, log, cla
|
|
|
13816
14429
|
running = true;
|
|
13817
14430
|
try {
|
|
13818
14431
|
log.debug("Heartbeat tick: checking for pending work...");
|
|
14432
|
+
const heartbeatPrompt = agentContext ? buildHeartbeatDirective(agentContext) : "Run your heartbeat check now.";
|
|
13819
14433
|
const result = runHeartbeat({
|
|
13820
|
-
prompt:
|
|
14434
|
+
prompt: heartbeatPrompt,
|
|
13821
14435
|
orchestratorServer: createOrchServer(),
|
|
13822
14436
|
model,
|
|
13823
|
-
claudeCodePath
|
|
14437
|
+
claudeCodePath,
|
|
14438
|
+
agentContext
|
|
13824
14439
|
});
|
|
13825
14440
|
for await (const _ of result) {
|
|
13826
14441
|
}
|
|
@@ -13861,8 +14476,23 @@ async function startAgent(opts) {
|
|
|
13861
14476
|
const msg = err instanceof Error ? err.message : String(err);
|
|
13862
14477
|
throw new Error(`Failed to bootstrap config from API: ${msg}`);
|
|
13863
14478
|
}
|
|
14479
|
+
const isDocker2 = process.env["DOCKER"] === "1" || (await import('fs')).existsSync("/.dockerenv");
|
|
14480
|
+
const runtimeVersion = process.env["npm_package_version"] ?? "unknown";
|
|
14481
|
+
const agentContext = {
|
|
14482
|
+
botName: config.botName,
|
|
14483
|
+
deploymentType: config.deploymentType,
|
|
14484
|
+
provider: config.provider,
|
|
14485
|
+
model: config.model,
|
|
14486
|
+
platform: isDocker2 ? "Docker" : process.platform === "darwin" ? "macOS" : "Linux",
|
|
14487
|
+
runtimeVersion,
|
|
14488
|
+
maxConcurrentWorkers: 5
|
|
14489
|
+
// updated after WorkerManager is created
|
|
14490
|
+
};
|
|
13864
14491
|
mkdirSync(config.stateDir, { recursive: true });
|
|
13865
14492
|
mkdirSync(config.toolsDir, { recursive: true });
|
|
14493
|
+
const logFile = join(config.stateDir, "runtime.log");
|
|
14494
|
+
enableFileLogging(logFile);
|
|
14495
|
+
log.info(`File logging enabled: ${logFile}`);
|
|
13866
14496
|
if (config.tools.length > 0) {
|
|
13867
14497
|
log.info(`Installing ${config.tools.length} tool package(s)...`);
|
|
13868
14498
|
await installTools(config.tools, config.toolsDir, log);
|
|
@@ -13916,8 +14546,10 @@ async function startAgent(opts) {
|
|
|
13916
14546
|
"memory",
|
|
13917
14547
|
config.botId,
|
|
13918
14548
|
"session-history.json"
|
|
13919
|
-
)
|
|
14549
|
+
),
|
|
14550
|
+
agentContext
|
|
13920
14551
|
});
|
|
14552
|
+
agentContext.maxConcurrentWorkers = workerManager.maxConcurrentWorkers;
|
|
13921
14553
|
const createOrchServer = () => createOrchestratorMcpServer(client, customTools, workerManager);
|
|
13922
14554
|
const healthServer = startHealthServer(config.healthPort, log);
|
|
13923
14555
|
const sessionStore = new SessionStore();
|
|
@@ -13944,7 +14576,8 @@ async function startAgent(opts) {
|
|
|
13944
14576
|
createOrchestratorServer: createOrchServer,
|
|
13945
14577
|
workerManager,
|
|
13946
14578
|
model: config.model,
|
|
13947
|
-
claudeCodePath: config.claudeCodePath
|
|
14579
|
+
claudeCodePath: config.claudeCodePath,
|
|
14580
|
+
agentContext
|
|
13948
14581
|
},
|
|
13949
14582
|
sessionStore,
|
|
13950
14583
|
{
|
|
@@ -13960,7 +14593,8 @@ async function startAgent(opts) {
|
|
|
13960
14593
|
config.heartbeatIntervalMs,
|
|
13961
14594
|
abortController.signal,
|
|
13962
14595
|
log,
|
|
13963
|
-
config.claudeCodePath
|
|
14596
|
+
config.claudeCodePath,
|
|
14597
|
+
agentContext
|
|
13964
14598
|
);
|
|
13965
14599
|
if (config.memoryFileId && config.sleepHour !== null && config.timezone) {
|
|
13966
14600
|
const memoryFileId = config.memoryFileId;
|
|
@@ -13997,7 +14631,7 @@ async function startAgent(opts) {
|
|
|
13997
14631
|
const workerServer = createWorkServer();
|
|
13998
14632
|
const result = runOneShotWorker({
|
|
13999
14633
|
prompt,
|
|
14000
|
-
systemPrompt:
|
|
14634
|
+
systemPrompt: buildSleepDirective(agentContext),
|
|
14001
14635
|
workerServer,
|
|
14002
14636
|
model: config.model
|
|
14003
14637
|
});
|
|
@@ -14043,7 +14677,7 @@ async function startAgent(opts) {
|
|
|
14043
14677
|
const workerServer = createWorkServer();
|
|
14044
14678
|
const result = runOneShotWorker({
|
|
14045
14679
|
prompt,
|
|
14046
|
-
systemPrompt:
|
|
14680
|
+
systemPrompt: buildDuskDirective(agentContext),
|
|
14047
14681
|
workerServer,
|
|
14048
14682
|
model: config.model
|
|
14049
14683
|
});
|
|
@@ -14093,16 +14727,42 @@ var init_entrypoint = __esm({
|
|
|
14093
14727
|
var args = process.argv.slice(2);
|
|
14094
14728
|
var command = args[0];
|
|
14095
14729
|
if (command === "install") {
|
|
14096
|
-
|
|
14730
|
+
let token = args[1];
|
|
14097
14731
|
if (!token) {
|
|
14098
|
-
|
|
14099
|
-
|
|
14732
|
+
const { createInterface } = await import('readline');
|
|
14733
|
+
const rl = createInterface({
|
|
14734
|
+
input: process.stdin,
|
|
14735
|
+
output: process.stdout
|
|
14736
|
+
});
|
|
14737
|
+
token = await new Promise((resolve) => {
|
|
14738
|
+
rl.question(
|
|
14739
|
+
"Paste your bot token (from the DeskFree dashboard):\n> ",
|
|
14740
|
+
(answer) => {
|
|
14741
|
+
rl.close();
|
|
14742
|
+
resolve(answer.trim());
|
|
14743
|
+
}
|
|
14744
|
+
);
|
|
14745
|
+
});
|
|
14746
|
+
if (!token) {
|
|
14747
|
+
console.error("No token provided.");
|
|
14748
|
+
process.exit(1);
|
|
14749
|
+
}
|
|
14100
14750
|
}
|
|
14101
14751
|
const { install: install2 } = await Promise.resolve().then(() => (init_install(), install_exports));
|
|
14102
14752
|
install2(token);
|
|
14103
14753
|
} else if (command === "uninstall") {
|
|
14104
14754
|
const { uninstall: uninstall2 } = await Promise.resolve().then(() => (init_uninstall(), uninstall_exports));
|
|
14105
14755
|
uninstall2();
|
|
14756
|
+
} else if (command === "status") {
|
|
14757
|
+
const { status: status2 } = await Promise.resolve().then(() => (init_status(), status_exports));
|
|
14758
|
+
status2();
|
|
14759
|
+
} else if (command === "restart") {
|
|
14760
|
+
const { restart: restart2 } = await Promise.resolve().then(() => (init_restart(), restart_exports));
|
|
14761
|
+
restart2();
|
|
14762
|
+
} else if (command === "logs") {
|
|
14763
|
+
const follow = args.includes("-f") || args.includes("--follow");
|
|
14764
|
+
const { logs: logs2 } = await Promise.resolve().then(() => (init_logs(), logs_exports));
|
|
14765
|
+
logs2(follow);
|
|
14106
14766
|
} else {
|
|
14107
14767
|
let handleShutdown = function(signal) {
|
|
14108
14768
|
log.info(`Received ${signal} \u2014 shutting down...`);
|