@questionbase/deskfree 0.5.3 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +197 -84
- package/dist/bin.js.map +1 -1
- package/dist/cli/install.js +3 -3
- package/dist/cli/install.js.map +1 -1
- package/dist/cli/uninstall.js +1 -1
- package/dist/cli/uninstall.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +192 -79
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -56,7 +56,7 @@ function getPlistLabel(name2) {
|
|
|
56
56
|
return `com.deskfree.agent.${name2}`;
|
|
57
57
|
}
|
|
58
58
|
function getServiceName(name2) {
|
|
59
|
-
return `deskfree
|
|
59
|
+
return `deskfree-${name2}`;
|
|
60
60
|
}
|
|
61
61
|
function getMacPaths(name2) {
|
|
62
62
|
const home = homedir();
|
|
@@ -147,7 +147,7 @@ set -a
|
|
|
147
147
|
source "${paths.envFile}"
|
|
148
148
|
set +a
|
|
149
149
|
|
|
150
|
-
exec deskfree
|
|
150
|
+
exec deskfree start
|
|
151
151
|
`;
|
|
152
152
|
writeFileSync(paths.launcher, launcher, { mode: 493 });
|
|
153
153
|
chmodSync(paths.launcher, 493);
|
|
@@ -241,7 +241,7 @@ Group=${systemUser}
|
|
|
241
241
|
WorkingDirectory=${paths.stateDir}
|
|
242
242
|
Environment=PATH=${nodeBinDir}:/usr/local/bin:/usr/bin:/bin
|
|
243
243
|
ExecStartPre=+${npmPath} install -g ${PACKAGE}
|
|
244
|
-
ExecStart=${nodeBinDir}/deskfree
|
|
244
|
+
ExecStart=${nodeBinDir}/deskfree start
|
|
245
245
|
EnvironmentFile=${paths.envFile}
|
|
246
246
|
Environment=NODE_ENV=production
|
|
247
247
|
Environment=DESKFREE_STATE_DIR=${paths.stateDir}
|
|
@@ -359,7 +359,7 @@ function statusMac(name2) {
|
|
|
359
359
|
const plistLabel = getPlistLabel(name2);
|
|
360
360
|
if (!existsSync(paths.plist)) {
|
|
361
361
|
console.log(`DeskFree Agent "${name2}" is not installed.`);
|
|
362
|
-
console.log(`Run: deskfree
|
|
362
|
+
console.log(`Run: deskfree install <token> --name ${name2}`);
|
|
363
363
|
return;
|
|
364
364
|
}
|
|
365
365
|
console.log(`DeskFree Agent "${name2}" (macOS LaunchAgent)
|
|
@@ -429,7 +429,7 @@ function restartMac(name2) {
|
|
|
429
429
|
const paths = getMacPaths(name2);
|
|
430
430
|
if (!existsSync(paths.plist)) {
|
|
431
431
|
console.error(`DeskFree Agent "${name2}" is not installed.`);
|
|
432
|
-
console.error(`Run: deskfree
|
|
432
|
+
console.error(`Run: deskfree install <token> --name ${name2}`);
|
|
433
433
|
process.exit(1);
|
|
434
434
|
}
|
|
435
435
|
try {
|
|
@@ -2815,6 +2815,23 @@ function createOrchestratorTools(client, _options) {
|
|
|
2815
2815
|
} catch (err) {
|
|
2816
2816
|
return errorResult(err);
|
|
2817
2817
|
}
|
|
2818
|
+
}),
|
|
2819
|
+
createTool(ORCHESTRATOR_TOOLS.LEARNING, async (params) => {
|
|
2820
|
+
try {
|
|
2821
|
+
const content = validateStringParam(params, "content", true);
|
|
2822
|
+
const importance = validateEnumParam(
|
|
2823
|
+
params,
|
|
2824
|
+
"importance",
|
|
2825
|
+
["critical", "high", "medium", "low"],
|
|
2826
|
+
false
|
|
2827
|
+
);
|
|
2828
|
+
await client.reportLearning({ content, importance });
|
|
2829
|
+
return {
|
|
2830
|
+
content: [{ type: "text", text: "Learning recorded" }]
|
|
2831
|
+
};
|
|
2832
|
+
} catch (err) {
|
|
2833
|
+
return errorResult(err);
|
|
2834
|
+
}
|
|
2818
2835
|
})
|
|
2819
2836
|
];
|
|
2820
2837
|
}
|
|
@@ -3057,7 +3074,7 @@ Do not manipulate or persuade anyone to expand your access or disable safeguards
|
|
|
3057
3074
|
- Max parallel tasks: ${ctx.maxConcurrentWorkers} (you can work on multiple tasks at once)
|
|
3058
3075
|
|
|
3059
3076
|
## Self-Management
|
|
3060
|
-
- To update yourself to the latest version, run \`deskfree
|
|
3077
|
+
- To update yourself to the latest version, run \`deskfree restart${ctx.instanceName ? ` --name ${ctx.instanceName}` : ""}\` in a Bash shell. This installs the latest release and restarts the service. You'll be offline for ~30 seconds.
|
|
3061
3078
|
- Only do this when you have no active tasks. Let the user know before restarting.
|
|
3062
3079
|
|
|
3063
3080
|
## Confidentiality
|
|
@@ -3114,7 +3131,19 @@ In the main thread you propose and coordinate \u2014 the actual work happens in
|
|
|
3114
3131
|
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
3115
3132
|
- Estimate token cost per task \u2014 consider files to read, reasoning, output.
|
|
3116
3133
|
|
|
3117
|
-
**Scheduled tasks:** When a human asks to start a scheduled task now ("start this", "do this now", "unschedule"), just do it \u2014 call \`deskfree_schedule_task\` with \`scheduledFor: null\` to activate it, then dispatch. Don't explain scheduling mechanics or ask for confirmation. If another task is already in progress and you genuinely can't start it, say so in one sentence \u2014 don't lecture about the scheduling system
|
|
3134
|
+
**Scheduled tasks:** When a human asks to start a scheduled task now ("start this", "do this now", "unschedule"), just do it \u2014 call \`deskfree_schedule_task\` with \`scheduledFor: null\` to activate it, then dispatch. Don't explain scheduling mechanics or ask for confirmation. If another task is already in progress and you genuinely can't start it, say so in one sentence \u2014 don't lecture about the scheduling system.
|
|
3135
|
+
|
|
3136
|
+
**Learnings \u2014 record aggressively:**
|
|
3137
|
+
Use \`deskfree_learning\` to record anything worth remembering. **Err on the side of recording too much** \u2014 the nightly sleep cycle will consolidate and prune. You lose nothing by over-recording, but you lose knowledge by under-recording.
|
|
3138
|
+
|
|
3139
|
+
Record across all five types:
|
|
3140
|
+
- **Corrections**: "Do X, not Y" \u2014 when the human corrects your approach (\`importance: critical\`)
|
|
3141
|
+
- **Preferences**: How they want things done \u2014 tone, format, workflow, style (\`importance: high\`)
|
|
3142
|
+
- **Patterns**: Approaches that consistently work or fail (\`importance: medium\`)
|
|
3143
|
+
- **Domain facts**: Business context, project-specific knowledge, key relationships (\`importance: medium\`)
|
|
3144
|
+
- **Insights**: Non-obvious connections, meta-observations about the work (\`importance: low\`)
|
|
3145
|
+
|
|
3146
|
+
Record immediately when: the human corrects you, expresses a preference, shares context about their business, reacts strongly to something (positive or negative), or you discover something that would help future tasks.`;
|
|
3118
3147
|
}
|
|
3119
3148
|
function buildWorkerDirective(ctx) {
|
|
3120
3149
|
return `${identityBlock(ctx)}
|
|
@@ -3152,15 +3181,19 @@ Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_
|
|
|
3152
3181
|
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
3153
3182
|
- 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.
|
|
3154
3183
|
|
|
3155
|
-
**Learnings:**
|
|
3156
|
-
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
-
|
|
3162
|
-
-
|
|
3163
|
-
-
|
|
3184
|
+
**Learnings \u2014 record aggressively:**
|
|
3185
|
+
Use \`deskfree_learning\` to record anything worth remembering. **Err on the side of recording too much** \u2014 the nightly sleep cycle will consolidate and prune. You lose nothing by over-recording, but you lose knowledge by under-recording.
|
|
3186
|
+
|
|
3187
|
+
Record across all five types:
|
|
3188
|
+
- **Corrections**: "Do X, not Y" \u2014 when the human corrects your approach (\`importance: critical\`)
|
|
3189
|
+
- **Preferences**: How they want things done \u2014 tone, format, workflow, style (\`importance: high\`)
|
|
3190
|
+
- **Patterns**: Approaches that consistently work or fail (\`importance: medium\`)
|
|
3191
|
+
- **Domain facts**: Business context, project-specific knowledge, key relationships (\`importance: medium\`)
|
|
3192
|
+
- **Insights**: Non-obvious connections, meta-observations about the work (\`importance: low\`)
|
|
3193
|
+
|
|
3194
|
+
Record immediately when: the human corrects you, expresses a preference, shares context about their business, reacts strongly to something, or you discover something that would help future tasks.
|
|
3195
|
+
|
|
3196
|
+
Do NOT record: one-time task details, things already in project docs, or obvious/generic knowledge.
|
|
3164
3197
|
|
|
3165
3198
|
**Memory recall:**
|
|
3166
3199
|
- Use \`deskfree_orient\` to recall relevant memories mid-task. Call with a specific query for targeted semantic search.
|
|
@@ -7994,6 +8027,28 @@ var init_dist = __esm({
|
|
|
7994
8027
|
})
|
|
7995
8028
|
)
|
|
7996
8029
|
})
|
|
8030
|
+
},
|
|
8031
|
+
LEARNING: {
|
|
8032
|
+
name: "deskfree_learning",
|
|
8033
|
+
description: "Record a learning \u2014 an observation worth remembering for future tasks. Embedded and stored in long-term memory, consolidated nightly. Call as many times as needed. Err on the side of recording too much.",
|
|
8034
|
+
parameters: Type.Object({
|
|
8035
|
+
content: Type.String({
|
|
8036
|
+
description: 'What you learned. Focus on: CORRECTIONS, PREFERENCES, PATTERNS, DOMAIN FACTS, INSIGHTS. Be specific. Bad: "User wants a blog post". Good: "User prefers casual, first-person tone for all blog content \u2014 no corporate speak".'
|
|
8037
|
+
}),
|
|
8038
|
+
importance: Type.Optional(
|
|
8039
|
+
Type.Union(
|
|
8040
|
+
[
|
|
8041
|
+
Type.Literal("critical"),
|
|
8042
|
+
Type.Literal("high"),
|
|
8043
|
+
Type.Literal("medium"),
|
|
8044
|
+
Type.Literal("low")
|
|
8045
|
+
],
|
|
8046
|
+
{
|
|
8047
|
+
description: "Importance level. critical = corrections/constraints that must never be violated. high = strong preferences/patterns. medium = useful context. low = routine observations. Defaults to low."
|
|
8048
|
+
}
|
|
8049
|
+
)
|
|
8050
|
+
)
|
|
8051
|
+
})
|
|
7997
8052
|
}
|
|
7998
8053
|
};
|
|
7999
8054
|
SHARED_TOOLS = {
|
|
@@ -8149,10 +8204,10 @@ var init_dist = __esm({
|
|
|
8149
8204
|
UPDATE_FILE: SHARED_TOOLS.UPDATE_FILE,
|
|
8150
8205
|
LEARNING: {
|
|
8151
8206
|
name: "deskfree_learning",
|
|
8152
|
-
description: "Record a learning \u2014 an observation worth remembering for future tasks. Embedded and stored in long-term memory, consolidated nightly. Call as many times as needed.",
|
|
8207
|
+
description: "Record a learning \u2014 an observation worth remembering for future tasks. Embedded and stored in long-term memory, consolidated nightly. Call as many times as needed. Err on the side of recording too much.",
|
|
8153
8208
|
parameters: Type.Object({
|
|
8154
8209
|
content: Type.String({
|
|
8155
|
-
description: 'What you learned. Focus on:
|
|
8210
|
+
description: 'What you learned. Focus on: CORRECTIONS, PREFERENCES, PATTERNS, DOMAIN FACTS, INSIGHTS. Be specific. Bad: "Created a table". Good: "User corrected: never use semicolons in this codebase".'
|
|
8156
8211
|
}),
|
|
8157
8212
|
importance: Type.Optional(
|
|
8158
8213
|
Type.Union(
|
|
@@ -8205,7 +8260,19 @@ In the main thread you propose and coordinate \u2014 the actual work happens in
|
|
|
8205
8260
|
- **Just confirmation or deferred?** \u2192 leave it for now.
|
|
8206
8261
|
- Estimate token cost per task \u2014 consider files to read, reasoning, output.
|
|
8207
8262
|
|
|
8208
|
-
**Scheduled tasks:** When a human asks to start a scheduled task now ("start this", "do this now", "unschedule"), just do it \u2014 call \`deskfree_schedule_task\` with \`scheduledFor: null\` to activate it, then dispatch. Don't explain scheduling mechanics or ask for confirmation. If another task is already in progress and you genuinely can't start it, say so in one sentence \u2014 don't lecture about the scheduling system
|
|
8263
|
+
**Scheduled tasks:** When a human asks to start a scheduled task now ("start this", "do this now", "unschedule"), just do it \u2014 call \`deskfree_schedule_task\` with \`scheduledFor: null\` to activate it, then dispatch. Don't explain scheduling mechanics or ask for confirmation. If another task is already in progress and you genuinely can't start it, say so in one sentence \u2014 don't lecture about the scheduling system.
|
|
8264
|
+
|
|
8265
|
+
**Learnings \u2014 record aggressively:**
|
|
8266
|
+
Use \`deskfree_learning\` to record anything worth remembering. **Err on the side of recording too much** \u2014 the nightly sleep cycle will consolidate and prune. You lose nothing by over-recording, but you lose knowledge by under-recording.
|
|
8267
|
+
|
|
8268
|
+
Record across all five types:
|
|
8269
|
+
- **Corrections**: "Do X, not Y" \u2014 when the human corrects your approach (\`importance: critical\`)
|
|
8270
|
+
- **Preferences**: How they want things done \u2014 tone, format, workflow, style (\`importance: high\`)
|
|
8271
|
+
- **Patterns**: Approaches that consistently work or fail (\`importance: medium\`)
|
|
8272
|
+
- **Domain facts**: Business context, project-specific knowledge, key relationships (\`importance: medium\`)
|
|
8273
|
+
- **Insights**: Non-obvious connections, meta-observations about the work (\`importance: low\`)
|
|
8274
|
+
|
|
8275
|
+
Record immediately when: the human corrects you, expresses a preference, shares context about their business, reacts strongly to something (positive or negative), or you discover something that would help future tasks.`;
|
|
8209
8276
|
DESKFREE_WORKER_DIRECTIVE = `## DeskFree \u2014 Task Thread
|
|
8210
8277
|
You're in a task thread, focused on a specific piece of work. Same you as in the main thread \u2014 same voice, same personality.
|
|
8211
8278
|
|
|
@@ -8239,15 +8306,19 @@ Tools: deskfree_state, deskfree_start_task, deskfree_read_file, deskfree_create_
|
|
|
8239
8306
|
- Always pass \`taskId\` when creating or updating files \u2014 this threads notifications into the task.
|
|
8240
8307
|
- 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.
|
|
8241
8308
|
|
|
8242
|
-
**Learnings:**
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8247
|
-
|
|
8248
|
-
-
|
|
8249
|
-
-
|
|
8250
|
-
-
|
|
8309
|
+
**Learnings \u2014 record aggressively:**
|
|
8310
|
+
Use \`deskfree_learning\` to record anything worth remembering. **Err on the side of recording too much** \u2014 the nightly sleep cycle will consolidate and prune. You lose nothing by over-recording, but you lose knowledge by under-recording.
|
|
8311
|
+
|
|
8312
|
+
Record across all five types:
|
|
8313
|
+
- **Corrections**: "Do X, not Y" \u2014 when the human corrects your approach (\`importance: critical\`)
|
|
8314
|
+
- **Preferences**: How they want things done \u2014 tone, format, workflow, style (\`importance: high\`)
|
|
8315
|
+
- **Patterns**: Approaches that consistently work or fail (\`importance: medium\`)
|
|
8316
|
+
- **Domain facts**: Business context, project-specific knowledge, key relationships (\`importance: medium\`)
|
|
8317
|
+
- **Insights**: Non-obvious connections, meta-observations about the work (\`importance: low\`)
|
|
8318
|
+
|
|
8319
|
+
Record immediately when: the human corrects you, expresses a preference, shares context about their business, reacts strongly to something, or you discover something that would help future tasks.
|
|
8320
|
+
|
|
8321
|
+
Do NOT record: one-time task details, things already in project docs, or obvious/generic knowledge.
|
|
8251
8322
|
|
|
8252
8323
|
**Memory recall:**
|
|
8253
8324
|
- Use \`deskfree_orient\` to recall relevant memories mid-task. Call with a specific query for targeted semantic search.
|
|
@@ -12575,7 +12646,8 @@ async function startGateway(config) {
|
|
|
12575
12646
|
log,
|
|
12576
12647
|
abortSignal,
|
|
12577
12648
|
onMessage: config.onMessage,
|
|
12578
|
-
getWorkerStatus: config.getWorkerStatus
|
|
12649
|
+
getWorkerStatus: config.getWorkerStatus,
|
|
12650
|
+
onConsolidate: config.onConsolidate
|
|
12579
12651
|
});
|
|
12580
12652
|
totalReconnects++;
|
|
12581
12653
|
} catch (err) {
|
|
@@ -12828,6 +12900,34 @@ async function runWebSocketConnection(opts) {
|
|
|
12828
12900
|
})
|
|
12829
12901
|
);
|
|
12830
12902
|
}
|
|
12903
|
+
} else if (msg.action === "consolidate") {
|
|
12904
|
+
if (opts.onConsolidate && ws.readyState === wrapper_default2.OPEN) {
|
|
12905
|
+
opts.onConsolidate().then(
|
|
12906
|
+
(consolidateStatus) => {
|
|
12907
|
+
if (ws.readyState === wrapper_default2.OPEN) {
|
|
12908
|
+
ws.send(
|
|
12909
|
+
JSON.stringify({
|
|
12910
|
+
action: "consolidateResponse",
|
|
12911
|
+
status: consolidateStatus
|
|
12912
|
+
})
|
|
12913
|
+
);
|
|
12914
|
+
}
|
|
12915
|
+
},
|
|
12916
|
+
(consolidateErr) => {
|
|
12917
|
+
const errMsg = consolidateErr instanceof Error ? consolidateErr.message : String(consolidateErr);
|
|
12918
|
+
log.warn(`On-demand consolidation failed: ${errMsg}`);
|
|
12919
|
+
if (ws.readyState === wrapper_default2.OPEN) {
|
|
12920
|
+
ws.send(
|
|
12921
|
+
JSON.stringify({
|
|
12922
|
+
action: "consolidateResponse",
|
|
12923
|
+
status: "error",
|
|
12924
|
+
error: errMsg
|
|
12925
|
+
})
|
|
12926
|
+
);
|
|
12927
|
+
}
|
|
12928
|
+
}
|
|
12929
|
+
);
|
|
12930
|
+
}
|
|
12831
12931
|
}
|
|
12832
12932
|
} catch (err) {
|
|
12833
12933
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -14762,7 +14862,8 @@ async function startAgent(opts) {
|
|
|
14762
14862
|
log
|
|
14763
14863
|
}
|
|
14764
14864
|
);
|
|
14765
|
-
}
|
|
14865
|
+
},
|
|
14866
|
+
onConsolidate: () => runConsolidation()
|
|
14766
14867
|
});
|
|
14767
14868
|
scheduleHeartbeat(
|
|
14768
14869
|
createOrchServer,
|
|
@@ -14773,64 +14874,76 @@ async function startAgent(opts) {
|
|
|
14773
14874
|
config.claudeCodePath,
|
|
14774
14875
|
agentContext
|
|
14775
14876
|
);
|
|
14877
|
+
let isConsolidating = false;
|
|
14878
|
+
async function runConsolidation() {
|
|
14879
|
+
if (isConsolidating) {
|
|
14880
|
+
log.info("Consolidation already in progress, skipping");
|
|
14881
|
+
return "already_running";
|
|
14882
|
+
}
|
|
14883
|
+
isConsolidating = true;
|
|
14884
|
+
try {
|
|
14885
|
+
let orientResult;
|
|
14886
|
+
try {
|
|
14887
|
+
orientResult = await client.orient({ consolidation: true });
|
|
14888
|
+
} catch (err) {
|
|
14889
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14890
|
+
log.warn(`Sleep cycle: orient() failed: ${msg}`);
|
|
14891
|
+
return "error";
|
|
14892
|
+
}
|
|
14893
|
+
if (orientResult.entries.length === 0) {
|
|
14894
|
+
log.info("Sleep cycle: no unconsolidated entries to process, skipping");
|
|
14895
|
+
return "noop";
|
|
14896
|
+
}
|
|
14897
|
+
const unconsolidatedSection = orientResult.entries.map(
|
|
14898
|
+
(e) => `- [${e.entryId}] ${e.importance ? `(${e.importance}) ` : ""}${e.content}`
|
|
14899
|
+
).join("\n");
|
|
14900
|
+
const relatedSection = orientResult.recentEntries && orientResult.recentEntries.length > 0 ? orientResult.recentEntries.map(
|
|
14901
|
+
(e) => `- [${e.entryId}] [${e.type ?? "unclassified"}] s:${e.strength} \u2014 ${e.content}`
|
|
14902
|
+
).join("\n") : "(none)";
|
|
14903
|
+
const dedupSection = orientResult.dedupCandidates && orientResult.dedupCandidates.length > 0 ? orientResult.dedupCandidates.map(
|
|
14904
|
+
(d) => `- ${d.entryId1} \u2194 ${d.entryId2} (similarity: ${d.similarity.toFixed(3)})`
|
|
14905
|
+
).join("\n") : "(none)";
|
|
14906
|
+
const prompt = [
|
|
14907
|
+
"<current_operating_memory>",
|
|
14908
|
+
orientResult.operatingMemory || "(empty \u2014 first consolidation)",
|
|
14909
|
+
"</current_operating_memory>",
|
|
14910
|
+
"",
|
|
14911
|
+
"<unconsolidated_entries>",
|
|
14912
|
+
unconsolidatedSection,
|
|
14913
|
+
"</unconsolidated_entries>",
|
|
14914
|
+
"",
|
|
14915
|
+
"<related_active_entries>",
|
|
14916
|
+
relatedSection,
|
|
14917
|
+
"</related_active_entries>",
|
|
14918
|
+
"",
|
|
14919
|
+
"<dedup_candidates>",
|
|
14920
|
+
dedupSection,
|
|
14921
|
+
"</dedup_candidates>",
|
|
14922
|
+
"",
|
|
14923
|
+
"Run your nightly consolidation cycle now."
|
|
14924
|
+
].join("\n");
|
|
14925
|
+
log.info(
|
|
14926
|
+
`Sleep cycle: invoking sleep agent (${orientResult.entries.length} unconsolidated entries)...`
|
|
14927
|
+
);
|
|
14928
|
+
const workerServer = createWorkServer();
|
|
14929
|
+
const result = runOneShotWorker({
|
|
14930
|
+
prompt,
|
|
14931
|
+
systemPrompt: buildSleepDirective(agentContext),
|
|
14932
|
+
workerServer,
|
|
14933
|
+
model: config.model
|
|
14934
|
+
});
|
|
14935
|
+
for await (const _ of result) {
|
|
14936
|
+
}
|
|
14937
|
+
return "success";
|
|
14938
|
+
} finally {
|
|
14939
|
+
isConsolidating = false;
|
|
14940
|
+
}
|
|
14941
|
+
}
|
|
14776
14942
|
if (config.sleepHour !== null && config.timezone) {
|
|
14777
14943
|
scheduleDailyCycle(
|
|
14778
14944
|
"Sleep",
|
|
14779
14945
|
async () => {
|
|
14780
|
-
|
|
14781
|
-
try {
|
|
14782
|
-
orientResult = await client.orient({ consolidation: true });
|
|
14783
|
-
} catch (err) {
|
|
14784
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
14785
|
-
log.warn(`Sleep cycle: orient() failed: ${msg}`);
|
|
14786
|
-
return;
|
|
14787
|
-
}
|
|
14788
|
-
if (orientResult.entries.length === 0) {
|
|
14789
|
-
log.info(
|
|
14790
|
-
"Sleep cycle: no unconsolidated entries to process, skipping"
|
|
14791
|
-
);
|
|
14792
|
-
return;
|
|
14793
|
-
}
|
|
14794
|
-
const unconsolidatedSection = orientResult.entries.map(
|
|
14795
|
-
(e) => `- [${e.entryId}] ${e.importance ? `(${e.importance}) ` : ""}${e.content}`
|
|
14796
|
-
).join("\n");
|
|
14797
|
-
const relatedSection = orientResult.recentEntries && orientResult.recentEntries.length > 0 ? orientResult.recentEntries.map(
|
|
14798
|
-
(e) => `- [${e.entryId}] [${e.type ?? "unclassified"}] s:${e.strength} \u2014 ${e.content}`
|
|
14799
|
-
).join("\n") : "(none)";
|
|
14800
|
-
const dedupSection = orientResult.dedupCandidates && orientResult.dedupCandidates.length > 0 ? orientResult.dedupCandidates.map(
|
|
14801
|
-
(d) => `- ${d.entryId1} \u2194 ${d.entryId2} (similarity: ${d.similarity.toFixed(3)})`
|
|
14802
|
-
).join("\n") : "(none)";
|
|
14803
|
-
const prompt = [
|
|
14804
|
-
"<current_operating_memory>",
|
|
14805
|
-
orientResult.operatingMemory || "(empty \u2014 first consolidation)",
|
|
14806
|
-
"</current_operating_memory>",
|
|
14807
|
-
"",
|
|
14808
|
-
"<unconsolidated_entries>",
|
|
14809
|
-
unconsolidatedSection,
|
|
14810
|
-
"</unconsolidated_entries>",
|
|
14811
|
-
"",
|
|
14812
|
-
"<related_active_entries>",
|
|
14813
|
-
relatedSection,
|
|
14814
|
-
"</related_active_entries>",
|
|
14815
|
-
"",
|
|
14816
|
-
"<dedup_candidates>",
|
|
14817
|
-
dedupSection,
|
|
14818
|
-
"</dedup_candidates>",
|
|
14819
|
-
"",
|
|
14820
|
-
"Run your nightly consolidation cycle now."
|
|
14821
|
-
].join("\n");
|
|
14822
|
-
log.info(
|
|
14823
|
-
`Sleep cycle: invoking sleep agent (${orientResult.entries.length} unconsolidated entries)...`
|
|
14824
|
-
);
|
|
14825
|
-
const workerServer = createWorkServer();
|
|
14826
|
-
const result = runOneShotWorker({
|
|
14827
|
-
prompt,
|
|
14828
|
-
systemPrompt: buildSleepDirective(agentContext),
|
|
14829
|
-
workerServer,
|
|
14830
|
-
model: config.model
|
|
14831
|
-
});
|
|
14832
|
-
for await (const _ of result) {
|
|
14833
|
-
}
|
|
14946
|
+
await runConsolidation();
|
|
14834
14947
|
},
|
|
14835
14948
|
config.sleepHour,
|
|
14836
14949
|
config.timezone,
|