@phuetz/code-buddy 0.1.25 → 0.2.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/README.md +1049 -741
- package/dist/agent/codebuddy-agent.d.ts +5 -0
- package/dist/agent/codebuddy-agent.js +46 -1
- package/dist/agent/codebuddy-agent.js.map +1 -1
- package/dist/agent/execution/agent-executor.d.ts +12 -0
- package/dist/agent/execution/agent-executor.js +147 -6
- package/dist/agent/execution/agent-executor.js.map +1 -1
- package/dist/agent/lessons-tracker.d.ts +50 -0
- package/dist/agent/lessons-tracker.js +234 -0
- package/dist/agent/lessons-tracker.js.map +1 -0
- package/dist/agent/message-queue.d.ts +39 -2
- package/dist/agent/message-queue.js +67 -2
- package/dist/agent/message-queue.js.map +1 -1
- package/dist/agent/middleware/index.d.ts +1 -0
- package/dist/agent/middleware/index.js +1 -0
- package/dist/agent/middleware/index.js.map +1 -1
- package/dist/agent/middleware/workflow-guard.d.ts +21 -0
- package/dist/agent/middleware/workflow-guard.js +94 -0
- package/dist/agent/middleware/workflow-guard.js.map +1 -0
- package/dist/agent/orchestrator/supervisor-agent.d.ts +9 -0
- package/dist/agent/orchestrator/supervisor-agent.js +21 -1
- package/dist/agent/orchestrator/supervisor-agent.js.map +1 -1
- package/dist/agent/repo-profiler.d.ts +61 -0
- package/dist/agent/repo-profiler.js +295 -0
- package/dist/agent/repo-profiler.js.map +1 -0
- package/dist/agent/response-constraint.d.ts +61 -0
- package/dist/agent/response-constraint.js +91 -0
- package/dist/agent/response-constraint.js.map +1 -0
- package/dist/agent/todo-tracker.d.ts +67 -0
- package/dist/agent/todo-tracker.js +245 -0
- package/dist/agent/todo-tracker.js.map +1 -0
- package/dist/agent/tool-handler.d.ts +11 -0
- package/dist/agent/tool-handler.js +79 -1
- package/dist/agent/tool-handler.js.map +1 -1
- package/dist/agent/types.d.ts +20 -2
- package/dist/agent/wide-research.d.ts +93 -0
- package/dist/agent/wide-research.js +232 -0
- package/dist/agent/wide-research.js.map +1 -0
- package/dist/channels/index.d.ts +2 -0
- package/dist/channels/index.js +2 -0
- package/dist/channels/index.js.map +1 -1
- package/dist/channels/pro/callback-router.d.ts +54 -0
- package/dist/channels/pro/callback-router.js +178 -0
- package/dist/channels/pro/callback-router.js.map +1 -0
- package/dist/channels/pro/ci-watcher.d.ts +86 -0
- package/dist/channels/pro/ci-watcher.js +343 -0
- package/dist/channels/pro/ci-watcher.js.map +1 -0
- package/dist/channels/pro/diff-first.d.ts +63 -0
- package/dist/channels/pro/diff-first.js +187 -0
- package/dist/channels/pro/diff-first.js.map +1 -0
- package/dist/channels/pro/enhanced-commands.d.ts +83 -0
- package/dist/channels/pro/enhanced-commands.js +218 -0
- package/dist/channels/pro/enhanced-commands.js.map +1 -0
- package/dist/channels/pro/index.d.ts +19 -0
- package/dist/channels/pro/index.js +21 -0
- package/dist/channels/pro/index.js.map +1 -0
- package/dist/channels/pro/pro-features.d.ts +79 -0
- package/dist/channels/pro/pro-features.js +203 -0
- package/dist/channels/pro/pro-features.js.map +1 -0
- package/dist/channels/pro/run-commands.d.ts +59 -0
- package/dist/channels/pro/run-commands.js +122 -0
- package/dist/channels/pro/run-commands.js.map +1 -0
- package/dist/channels/pro/run-tracker.d.ts +74 -0
- package/dist/channels/pro/run-tracker.js +252 -0
- package/dist/channels/pro/run-tracker.js.map +1 -0
- package/dist/channels/pro/scoped-auth.d.ts +97 -0
- package/dist/channels/pro/scoped-auth.js +340 -0
- package/dist/channels/pro/scoped-auth.js.map +1 -0
- package/dist/channels/pro/text-formatter.d.ts +27 -0
- package/dist/channels/pro/text-formatter.js +269 -0
- package/dist/channels/pro/text-formatter.js.map +1 -0
- package/dist/channels/pro/types.d.ts +242 -0
- package/dist/channels/pro/types.js +14 -0
- package/dist/channels/pro/types.js.map +1 -0
- package/dist/channels/streaming-policy.d.ts +66 -0
- package/dist/channels/streaming-policy.js +266 -0
- package/dist/channels/streaming-policy.js.map +1 -0
- package/dist/channels/telegram/ci-watcher.d.ts +5 -0
- package/dist/channels/telegram/ci-watcher.js +5 -0
- package/dist/channels/telegram/ci-watcher.js.map +1 -0
- package/dist/channels/telegram/client.d.ts +28 -0
- package/dist/channels/telegram/client.js +147 -1
- package/dist/channels/telegram/client.js.map +1 -1
- package/dist/channels/telegram/diff-first.d.ts +5 -0
- package/dist/channels/telegram/diff-first.js +5 -0
- package/dist/channels/telegram/diff-first.js.map +1 -0
- package/dist/channels/telegram/enhanced-commands.d.ts +6 -0
- package/dist/channels/telegram/enhanced-commands.js +6 -0
- package/dist/channels/telegram/enhanced-commands.js.map +1 -0
- package/dist/channels/telegram/index.d.ts +6 -0
- package/dist/channels/telegram/index.js +6 -0
- package/dist/channels/telegram/index.js.map +1 -1
- package/dist/channels/telegram/pro-formatter.d.ts +30 -0
- package/dist/channels/telegram/pro-formatter.js +276 -0
- package/dist/channels/telegram/pro-formatter.js.map +1 -0
- package/dist/channels/telegram/run-commands.d.ts +5 -0
- package/dist/channels/telegram/run-commands.js +6 -0
- package/dist/channels/telegram/run-commands.js.map +1 -0
- package/dist/channels/telegram/run-tracker.d.ts +5 -0
- package/dist/channels/telegram/run-tracker.js +5 -0
- package/dist/channels/telegram/run-tracker.js.map +1 -0
- package/dist/channels/telegram/scoped-auth.d.ts +6 -0
- package/dist/channels/telegram/scoped-auth.js +5 -0
- package/dist/channels/telegram/scoped-auth.js.map +1 -0
- package/dist/channels/telegram/types.d.ts +34 -0
- package/dist/codebuddy/client.js +14 -1
- package/dist/codebuddy/client.js.map +1 -1
- package/dist/commands/dev/index.d.ts +12 -0
- package/dist/commands/dev/index.js +231 -0
- package/dist/commands/dev/index.js.map +1 -0
- package/dist/commands/dev/workflows.d.ts +31 -0
- package/dist/commands/dev/workflows.js +214 -0
- package/dist/commands/dev/workflows.js.map +1 -0
- package/dist/commands/execpolicy.d.ts +17 -0
- package/dist/commands/execpolicy.js +155 -0
- package/dist/commands/execpolicy.js.map +1 -0
- package/dist/commands/knowledge.d.ts +13 -0
- package/dist/commands/knowledge.js +142 -0
- package/dist/commands/knowledge.js.map +1 -0
- package/dist/commands/lessons.d.ts +11 -0
- package/dist/commands/lessons.js +129 -0
- package/dist/commands/lessons.js.map +1 -0
- package/dist/commands/pairing.d.ts +14 -0
- package/dist/commands/pairing.js +132 -0
- package/dist/commands/pairing.js.map +1 -0
- package/dist/commands/research/index.d.ts +13 -0
- package/dist/commands/research/index.js +91 -0
- package/dist/commands/research/index.js.map +1 -0
- package/dist/commands/run-cli/index.d.ts +11 -0
- package/dist/commands/run-cli/index.js +49 -0
- package/dist/commands/run-cli/index.js.map +1 -0
- package/dist/commands/slash/builtin-commands.js +70 -2
- package/dist/commands/slash/builtin-commands.js.map +1 -1
- package/dist/commands/todos.d.ts +9 -0
- package/dist/commands/todos.js +119 -0
- package/dist/commands/todos.js.map +1 -0
- package/dist/config/toml-config.d.ts +21 -0
- package/dist/config/toml-config.js +15 -0
- package/dist/config/toml-config.js.map +1 -1
- package/dist/context/enhanced-compression.js +12 -1
- package/dist/context/enhanced-compression.js.map +1 -1
- package/dist/context/observation-variator.d.ts +44 -0
- package/dist/context/observation-variator.js +83 -0
- package/dist/context/observation-variator.js.map +1 -0
- package/dist/context/precompaction-flush.d.ts +40 -0
- package/dist/context/precompaction-flush.js +134 -0
- package/dist/context/precompaction-flush.js.map +1 -0
- package/dist/context/restorable-compression.d.ts +80 -0
- package/dist/context/restorable-compression.js +228 -0
- package/dist/context/restorable-compression.js.map +1 -0
- package/dist/daemon/daily-reset.d.ts +77 -0
- package/dist/daemon/daily-reset.js +175 -0
- package/dist/daemon/daily-reset.js.map +1 -0
- package/dist/daemon/index.d.ts +1 -0
- package/dist/daemon/index.js +1 -0
- package/dist/daemon/index.js.map +1 -1
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -1
- package/dist/knowledge/knowledge-manager.d.ts +77 -0
- package/dist/knowledge/knowledge-manager.js +244 -0
- package/dist/knowledge/knowledge-manager.js.map +1 -0
- package/dist/memory/semantic-memory-search.d.ts +7 -0
- package/dist/memory/semantic-memory-search.js +49 -5
- package/dist/memory/semantic-memory-search.js.map +1 -1
- package/dist/observability/run-store.d.ts +133 -0
- package/dist/observability/run-store.js +419 -0
- package/dist/observability/run-store.js.map +1 -0
- package/dist/observability/run-viewer.d.ts +33 -0
- package/dist/observability/run-viewer.js +254 -0
- package/dist/observability/run-viewer.js.map +1 -0
- package/dist/optimization/cache-breakpoints.d.ts +52 -0
- package/dist/optimization/cache-breakpoints.js +97 -0
- package/dist/optimization/cache-breakpoints.js.map +1 -0
- package/dist/persistence/session-store.d.ts +3 -1
- package/dist/persistence/session-store.js +1 -1
- package/dist/persistence/session-store.js.map +1 -1
- package/dist/prompts/system-base.js +51 -7
- package/dist/prompts/system-base.js.map +1 -1
- package/dist/prompts/variation-injector.d.ts +55 -0
- package/dist/prompts/variation-injector.js +171 -0
- package/dist/prompts/variation-injector.js.map +1 -0
- package/dist/prompts/workflow-rules.d.ts +10 -0
- package/dist/prompts/workflow-rules.js +79 -0
- package/dist/prompts/workflow-rules.js.map +1 -0
- package/dist/sandbox/execpolicy.d.ts +45 -0
- package/dist/sandbox/execpolicy.js +80 -0
- package/dist/sandbox/execpolicy.js.map +1 -1
- package/dist/sandbox/os-sandbox.d.ts +25 -0
- package/dist/sandbox/os-sandbox.js +73 -0
- package/dist/sandbox/os-sandbox.js.map +1 -1
- package/dist/scheduler/cron-scheduler.d.ts +9 -0
- package/dist/scheduler/cron-scheduler.js +17 -1
- package/dist/scheduler/cron-scheduler.js.map +1 -1
- package/dist/security/security-audit.d.ts +10 -0
- package/dist/security/security-audit.js +116 -0
- package/dist/security/security-audit.js.map +1 -1
- package/dist/security/shell-env-policy.d.ts +45 -0
- package/dist/security/shell-env-policy.js +141 -0
- package/dist/security/shell-env-policy.js.map +1 -0
- package/dist/security/ssrf-guard.d.ts +61 -0
- package/dist/security/ssrf-guard.js +382 -0
- package/dist/security/ssrf-guard.js.map +1 -0
- package/dist/security/write-policy.d.ts +57 -0
- package/dist/security/write-policy.js +117 -0
- package/dist/security/write-policy.js.map +1 -0
- package/dist/services/prompt-builder.js +37 -0
- package/dist/services/prompt-builder.js.map +1 -1
- package/dist/themes/theme-schema.d.ts +10 -10
- package/dist/tools/ask-human-tool.d.ts +62 -0
- package/dist/tools/ask-human-tool.js +112 -0
- package/dist/tools/ask-human-tool.js.map +1 -0
- package/dist/tools/bash/bash-tool.d.ts +15 -0
- package/dist/tools/bash/bash-tool.js +62 -0
- package/dist/tools/bash/bash-tool.js.map +1 -1
- package/dist/tools/bash/command-validator.d.ts +1 -0
- package/dist/tools/bash/command-validator.js +5 -0
- package/dist/tools/bash/command-validator.js.map +1 -1
- package/dist/tools/create-skill-tool.d.ts +87 -0
- package/dist/tools/create-skill-tool.js +142 -0
- package/dist/tools/create-skill-tool.js.map +1 -0
- package/dist/tools/fetch-tool.js +5 -3
- package/dist/tools/fetch-tool.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/plan-tool.d.ts +22 -0
- package/dist/tools/plan-tool.js +128 -0
- package/dist/tools/plan-tool.js.map +1 -0
- package/dist/tools/registry/attention-tools.d.ts +32 -0
- package/dist/tools/registry/attention-tools.js +225 -0
- package/dist/tools/registry/attention-tools.js.map +1 -0
- package/dist/tools/registry/index.d.ts +9 -1
- package/dist/tools/registry/index.js +30 -2
- package/dist/tools/registry/index.js.map +1 -1
- package/dist/tools/registry/knowledge-tools.d.ts +46 -0
- package/dist/tools/registry/knowledge-tools.js +293 -0
- package/dist/tools/registry/knowledge-tools.js.map +1 -0
- package/dist/tools/registry/lessons-tools.d.ts +48 -0
- package/dist/tools/registry/lessons-tools.js +359 -0
- package/dist/tools/registry/lessons-tools.js.map +1 -0
- package/dist/tools/registry/plan-tools.d.ts +2 -0
- package/dist/tools/registry/plan-tools.js +7 -0
- package/dist/tools/registry/plan-tools.js.map +1 -0
- package/dist/tools/registry/script-tools.d.ts +2 -0
- package/dist/tools/registry/script-tools.js +7 -0
- package/dist/tools/registry/script-tools.js.map +1 -0
- package/dist/tools/registry/tool-aliases.d.ts +44 -0
- package/dist/tools/registry/tool-aliases.js +130 -0
- package/dist/tools/registry/tool-aliases.js.map +1 -0
- package/dist/tools/run-script-tool.d.ts +13 -0
- package/dist/tools/run-script-tool.js +146 -0
- package/dist/tools/run-script-tool.js.map +1 -0
- package/dist/tools/web-search.d.ts +25 -0
- package/dist/tools/web-search.js +68 -6
- package/dist/tools/web-search.js.map +1 -1
- package/dist/utils/config-validation/schema.d.ts +2 -2
- package/dist/utils/debug-logger.d.ts +1 -1
- package/dist/utils/stable-json.d.ts +27 -0
- package/dist/utils/stable-json.js +50 -0
- package/dist/utils/stable-json.js.map +1 -0
- package/dist/webhooks/webhook-manager.d.ts +7 -0
- package/dist/webhooks/webhook-manager.js +29 -0
- package/dist/webhooks/webhook-manager.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-compaction Memory Flush — OpenClaw-inspired NO_REPLY pattern
|
|
3
|
+
*
|
|
4
|
+
* Before the context manager compacts (summarises/drops) old messages,
|
|
5
|
+
* this module runs a silent background LLM turn that asks the model to
|
|
6
|
+
* extract and save important facts to MEMORY.md.
|
|
7
|
+
*
|
|
8
|
+
* The `NO_REPLY` sentinel at the start of the response suppresses
|
|
9
|
+
* user-facing delivery, preventing notification spam. Only the extracted
|
|
10
|
+
* facts are written to disk; the LLM output is never shown to the user.
|
|
11
|
+
*
|
|
12
|
+
* This prevents the information loss that normally occurs when old turns
|
|
13
|
+
* are summarised away or dropped from the context window.
|
|
14
|
+
*
|
|
15
|
+
* Ref: OpenClaw session management compaction docs
|
|
16
|
+
* https://docs.openclaw.ai/reference/session-management-compaction
|
|
17
|
+
*/
|
|
18
|
+
export interface FlushMessage {
|
|
19
|
+
role: 'system' | 'user' | 'assistant';
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
export interface FlushResult {
|
|
23
|
+
/** Whether any facts were extracted and saved */
|
|
24
|
+
flushed: boolean;
|
|
25
|
+
/** Number of fact lines saved */
|
|
26
|
+
factsCount: number;
|
|
27
|
+
/** Path written to (or null if nothing written) */
|
|
28
|
+
writtenTo: string | null;
|
|
29
|
+
/** Whether the LLM returned NO_REPLY sentinel */
|
|
30
|
+
suppressed: boolean;
|
|
31
|
+
}
|
|
32
|
+
export declare class PrecompactionFlusher {
|
|
33
|
+
/** Run a silent flush before context compaction. */
|
|
34
|
+
flush(messages: FlushMessage[],
|
|
35
|
+
/** Simple chat function: (messages) → string */
|
|
36
|
+
chatFn: (msgs: FlushMessage[]) => Promise<string>, workDir?: string): Promise<FlushResult>;
|
|
37
|
+
private buildSnapshot;
|
|
38
|
+
private saveFacts;
|
|
39
|
+
}
|
|
40
|
+
export declare function getPrecompactionFlusher(): PrecompactionFlusher;
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pre-compaction Memory Flush — OpenClaw-inspired NO_REPLY pattern
|
|
3
|
+
*
|
|
4
|
+
* Before the context manager compacts (summarises/drops) old messages,
|
|
5
|
+
* this module runs a silent background LLM turn that asks the model to
|
|
6
|
+
* extract and save important facts to MEMORY.md.
|
|
7
|
+
*
|
|
8
|
+
* The `NO_REPLY` sentinel at the start of the response suppresses
|
|
9
|
+
* user-facing delivery, preventing notification spam. Only the extracted
|
|
10
|
+
* facts are written to disk; the LLM output is never shown to the user.
|
|
11
|
+
*
|
|
12
|
+
* This prevents the information loss that normally occurs when old turns
|
|
13
|
+
* are summarised away or dropped from the context window.
|
|
14
|
+
*
|
|
15
|
+
* Ref: OpenClaw session management compaction docs
|
|
16
|
+
* https://docs.openclaw.ai/reference/session-management-compaction
|
|
17
|
+
*/
|
|
18
|
+
import * as fs from 'fs';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { logger } from '../utils/logger.js';
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// Constants
|
|
23
|
+
// ============================================================================
|
|
24
|
+
const NO_REPLY_SENTINEL = 'NO_REPLY';
|
|
25
|
+
const ACK_MAX_CHARS = 300;
|
|
26
|
+
const FLUSH_SYSTEM_PROMPT = `You are a memory archivist. Your ONLY job is to extract
|
|
27
|
+
important, durable facts from a conversation that is about to be compressed.
|
|
28
|
+
|
|
29
|
+
OUTPUT FORMAT:
|
|
30
|
+
- Start with exactly "${NO_REPLY_SENTINEL}" on the first line if there is nothing worth saving.
|
|
31
|
+
- Otherwise output a compact Markdown bullet list of facts to remember. Each bullet should be
|
|
32
|
+
a self-contained statement under 120 chars. No meta-commentary. No repetition.
|
|
33
|
+
|
|
34
|
+
SAVE if: decisions made, user preferences stated, key file paths, API contracts,
|
|
35
|
+
architectural choices, project goals, error patterns discovered, credentials
|
|
36
|
+
configuration (NOT the secret values), important URLs.
|
|
37
|
+
|
|
38
|
+
SKIP if: small talk, debugging tangents, transient data, already-known facts.`;
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// PrecompactionFlusher
|
|
41
|
+
// ============================================================================
|
|
42
|
+
export class PrecompactionFlusher {
|
|
43
|
+
/** Run a silent flush before context compaction. */
|
|
44
|
+
async flush(messages,
|
|
45
|
+
/** Simple chat function: (messages) → string */
|
|
46
|
+
chatFn, workDir = process.cwd()) {
|
|
47
|
+
if (messages.length < 4) {
|
|
48
|
+
// Not enough history to bother flushing
|
|
49
|
+
return { flushed: false, factsCount: 0, writtenTo: null, suppressed: false };
|
|
50
|
+
}
|
|
51
|
+
// Build a compact snapshot of the conversation to flush
|
|
52
|
+
const snapshot = this.buildSnapshot(messages);
|
|
53
|
+
const flushMessages = [
|
|
54
|
+
{ role: 'system', content: FLUSH_SYSTEM_PROMPT },
|
|
55
|
+
{ role: 'user', content: `Conversation to analyse:\n\n${snapshot}` },
|
|
56
|
+
];
|
|
57
|
+
let response;
|
|
58
|
+
try {
|
|
59
|
+
response = await chatFn(flushMessages);
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
logger.debug('PrecompactionFlusher: LLM call failed', { err });
|
|
63
|
+
return { flushed: false, factsCount: 0, writtenTo: null, suppressed: false };
|
|
64
|
+
}
|
|
65
|
+
const trimmed = response.trim();
|
|
66
|
+
// Detect NO_REPLY sentinel
|
|
67
|
+
if (trimmed.startsWith(NO_REPLY_SENTINEL) &&
|
|
68
|
+
trimmed.length - NO_REPLY_SENTINEL.length <= ACK_MAX_CHARS) {
|
|
69
|
+
return { flushed: false, factsCount: 0, writtenTo: null, suppressed: true };
|
|
70
|
+
}
|
|
71
|
+
// Strip NO_REPLY prefix if present with additional content
|
|
72
|
+
const content = trimmed.startsWith(NO_REPLY_SENTINEL)
|
|
73
|
+
? trimmed.slice(NO_REPLY_SENTINEL.length).trim()
|
|
74
|
+
: trimmed;
|
|
75
|
+
if (!content) {
|
|
76
|
+
return { flushed: false, factsCount: 0, writtenTo: null, suppressed: true };
|
|
77
|
+
}
|
|
78
|
+
// Save facts to MEMORY.md
|
|
79
|
+
const writtenTo = await this.saveFacts(content, workDir);
|
|
80
|
+
const factsCount = content.split('\n').filter(l => l.startsWith('-')).length;
|
|
81
|
+
return { flushed: true, factsCount, writtenTo, suppressed: false };
|
|
82
|
+
}
|
|
83
|
+
// --------------------------------------------------------------------------
|
|
84
|
+
// Helpers
|
|
85
|
+
// --------------------------------------------------------------------------
|
|
86
|
+
buildSnapshot(messages) {
|
|
87
|
+
// Take last 60 messages at most, stripping tool call internals
|
|
88
|
+
const slice = messages.slice(-60);
|
|
89
|
+
return slice
|
|
90
|
+
.filter(m => m.role !== 'system')
|
|
91
|
+
.map(m => {
|
|
92
|
+
const prefix = m.role === 'user' ? 'User' : 'Assistant';
|
|
93
|
+
const body = typeof m.content === 'string'
|
|
94
|
+
? m.content.slice(0, 800)
|
|
95
|
+
: '[non-text content]';
|
|
96
|
+
return `**${prefix}:** ${body}`;
|
|
97
|
+
})
|
|
98
|
+
.join('\n\n---\n\n');
|
|
99
|
+
}
|
|
100
|
+
async saveFacts(content, workDir) {
|
|
101
|
+
const memoryPath = path.join(workDir, 'MEMORY.md');
|
|
102
|
+
const datestamp = new Date().toISOString().split('T')[0];
|
|
103
|
+
const header = `\n\n## Facts extracted ${datestamp} (pre-compaction flush)\n\n`;
|
|
104
|
+
const block = header + content + '\n';
|
|
105
|
+
try {
|
|
106
|
+
fs.appendFileSync(memoryPath, block, 'utf-8');
|
|
107
|
+
logger.debug('PrecompactionFlusher: facts saved', { memoryPath });
|
|
108
|
+
return memoryPath;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
// Try global fallback
|
|
112
|
+
const globalPath = path.join(process.env.HOME ?? process.env.USERPROFILE ?? '~', '.codebuddy', 'MEMORY.md');
|
|
113
|
+
try {
|
|
114
|
+
fs.mkdirSync(path.dirname(globalPath), { recursive: true });
|
|
115
|
+
fs.appendFileSync(globalPath, block, 'utf-8');
|
|
116
|
+
return globalPath;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
logger.debug('PrecompactionFlusher: could not write facts', { err });
|
|
120
|
+
return memoryPath; // report intended path even on failure
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// ============================================================================
|
|
126
|
+
// Singleton
|
|
127
|
+
// ============================================================================
|
|
128
|
+
let _instance = null;
|
|
129
|
+
export function getPrecompactionFlusher() {
|
|
130
|
+
if (!_instance)
|
|
131
|
+
_instance = new PrecompactionFlusher();
|
|
132
|
+
return _instance;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=precompaction-flush.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"precompaction-flush.js","sourceRoot":"","sources":["../../src/context/precompaction-flush.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restorable Compression — Manus AI context engineering pattern
|
|
3
|
+
*
|
|
4
|
+
* Instead of lossy summarisation (which discards content permanently),
|
|
5
|
+
* this module extracts structural identifiers (file paths, URLs, tool
|
|
6
|
+
* call IDs, line ranges) from messages that are about to be dropped,
|
|
7
|
+
* then stores the original content indexed by those identifiers.
|
|
8
|
+
*
|
|
9
|
+
* The agent can later call `restore_context(identifier)` to re-fetch
|
|
10
|
+
* the full content on demand, making context compression reversible.
|
|
11
|
+
*
|
|
12
|
+
* This is complementary to summarisation: a short summary of a long
|
|
13
|
+
* file-read result is kept in the context, while the full content is
|
|
14
|
+
* recoverable via its file path identifier.
|
|
15
|
+
*
|
|
16
|
+
* Ref: "Context Engineering for AI Agents: Lessons from Building Manus"
|
|
17
|
+
* https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus
|
|
18
|
+
*/
|
|
19
|
+
export interface CompressibleMessage {
|
|
20
|
+
role: string;
|
|
21
|
+
content: string | null;
|
|
22
|
+
tool_call_id?: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface CompressionResult {
|
|
26
|
+
/** Compressed messages (identifiers preserved, full content dropped) */
|
|
27
|
+
messages: CompressibleMessage[];
|
|
28
|
+
/** Identifiers that were extracted and stored */
|
|
29
|
+
identifiers: string[];
|
|
30
|
+
/** Number of tokens saved (estimated) */
|
|
31
|
+
tokensSaved: number;
|
|
32
|
+
}
|
|
33
|
+
export interface RestoreResult {
|
|
34
|
+
found: boolean;
|
|
35
|
+
content: string;
|
|
36
|
+
identifier: string;
|
|
37
|
+
}
|
|
38
|
+
export declare class RestorableCompressor {
|
|
39
|
+
/** identifier → original content */
|
|
40
|
+
private store;
|
|
41
|
+
/**
|
|
42
|
+
* Compress a slice of messages that are about to be dropped.
|
|
43
|
+
*
|
|
44
|
+
* For each message, identifiers are extracted and the full content is
|
|
45
|
+
* stored. The message content is replaced with a compact stub listing
|
|
46
|
+
* the available identifiers.
|
|
47
|
+
*/
|
|
48
|
+
compress(messages: CompressibleMessage[]): CompressionResult;
|
|
49
|
+
/**
|
|
50
|
+
* Restore the original content for an identifier.
|
|
51
|
+
*
|
|
52
|
+
* For file path identifiers, attempts to read from disk as a fallback.
|
|
53
|
+
* For URLs, returns a hint to use web_fetch.
|
|
54
|
+
*/
|
|
55
|
+
restore(identifier: string): RestoreResult;
|
|
56
|
+
/**
|
|
57
|
+
* Persist a tool result to disk under `.codebuddy/tool-results/<callId>.txt`.
|
|
58
|
+
* This gives the restore_context tool a reliable disk-backed source and enables
|
|
59
|
+
* the compact/full dual-representation pattern (Manus AI #19).
|
|
60
|
+
*
|
|
61
|
+
* @param callId - Tool call ID (e.g. call_abc123 or toolu_xyz)
|
|
62
|
+
* @param content - Full tool output
|
|
63
|
+
* @param workDir - Working directory (defaults to process.cwd())
|
|
64
|
+
*/
|
|
65
|
+
writeToolResult(callId: string, content: string, workDir?: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Read a tool result from disk (`.codebuddy/tool-results/<callId>.txt`).
|
|
68
|
+
* Used by restore_context when the in-memory store has been evicted.
|
|
69
|
+
*/
|
|
70
|
+
private readToolResultFromDisk;
|
|
71
|
+
/** List all stored identifiers */
|
|
72
|
+
listIdentifiers(): string[];
|
|
73
|
+
/** Total number of bytes stored */
|
|
74
|
+
storeSize(): number;
|
|
75
|
+
/** Evict oldest entries if store exceeds maxBytes (default 10 MB) */
|
|
76
|
+
evict(maxBytes?: number): void;
|
|
77
|
+
}
|
|
78
|
+
export declare function getRestorableCompressor(): RestorableCompressor;
|
|
79
|
+
/** Reset singleton (for tests) */
|
|
80
|
+
export declare function resetRestorableCompressor(): void;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Restorable Compression — Manus AI context engineering pattern
|
|
3
|
+
*
|
|
4
|
+
* Instead of lossy summarisation (which discards content permanently),
|
|
5
|
+
* this module extracts structural identifiers (file paths, URLs, tool
|
|
6
|
+
* call IDs, line ranges) from messages that are about to be dropped,
|
|
7
|
+
* then stores the original content indexed by those identifiers.
|
|
8
|
+
*
|
|
9
|
+
* The agent can later call `restore_context(identifier)` to re-fetch
|
|
10
|
+
* the full content on demand, making context compression reversible.
|
|
11
|
+
*
|
|
12
|
+
* This is complementary to summarisation: a short summary of a long
|
|
13
|
+
* file-read result is kept in the context, while the full content is
|
|
14
|
+
* recoverable via its file path identifier.
|
|
15
|
+
*
|
|
16
|
+
* Ref: "Context Engineering for AI Agents: Lessons from Building Manus"
|
|
17
|
+
* https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus
|
|
18
|
+
*/
|
|
19
|
+
import * as fs from 'fs';
|
|
20
|
+
import * as path from 'path';
|
|
21
|
+
import { logger } from '../utils/logger.js';
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Identifier extractors
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// File paths: absolute or relative, with extensions
|
|
26
|
+
const FILE_PATH_RE = /(?:^|\s|["'`(])(\/?(?:[\w.-]+\/)*[\w.-]+\.(?:ts|js|py|json|md|txt|yaml|yml|sh|go|rs|java|cpp|c|h|rb|php|swift|kt|cs|html|css|sql|env|toml|cfg|conf|xml)(?::\d+(?:-\d+)?)?)/g;
|
|
27
|
+
// URLs
|
|
28
|
+
const URL_RE = /https?:\/\/[^\s"'<>)]+/g;
|
|
29
|
+
// Tool call IDs (Anthropic/OpenAI style)
|
|
30
|
+
const TOOL_CALL_ID_RE = /\b(call_[a-zA-Z0-9]+|toolu_[a-zA-Z0-9]+)\b/g;
|
|
31
|
+
function extractIdentifiers(text) {
|
|
32
|
+
const ids = new Set();
|
|
33
|
+
for (const m of text.matchAll(FILE_PATH_RE)) {
|
|
34
|
+
const raw = m[1].trim().replace(/['"`:]/g, '');
|
|
35
|
+
if (raw.length > 3)
|
|
36
|
+
ids.add(raw);
|
|
37
|
+
}
|
|
38
|
+
for (const m of text.matchAll(URL_RE)) {
|
|
39
|
+
const url = m[0].replace(/[.,;)]+$/, ''); // strip trailing punctuation
|
|
40
|
+
ids.add(url);
|
|
41
|
+
}
|
|
42
|
+
for (const m of text.matchAll(TOOL_CALL_ID_RE)) {
|
|
43
|
+
ids.add(m[1]);
|
|
44
|
+
}
|
|
45
|
+
return [...ids];
|
|
46
|
+
}
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// RestorableCompressor
|
|
49
|
+
// ============================================================================
|
|
50
|
+
export class RestorableCompressor {
|
|
51
|
+
/** identifier → original content */
|
|
52
|
+
store = new Map();
|
|
53
|
+
/**
|
|
54
|
+
* Compress a slice of messages that are about to be dropped.
|
|
55
|
+
*
|
|
56
|
+
* For each message, identifiers are extracted and the full content is
|
|
57
|
+
* stored. The message content is replaced with a compact stub listing
|
|
58
|
+
* the available identifiers.
|
|
59
|
+
*/
|
|
60
|
+
compress(messages) {
|
|
61
|
+
const compressed = [];
|
|
62
|
+
const allIdentifiers = [];
|
|
63
|
+
let tokensSaved = 0;
|
|
64
|
+
for (const msg of messages) {
|
|
65
|
+
const content = msg.content ?? '';
|
|
66
|
+
if (!content || content.length < 200) {
|
|
67
|
+
// Short messages: keep as-is
|
|
68
|
+
compressed.push(msg);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const ids = extractIdentifiers(content);
|
|
72
|
+
if (ids.length === 0) {
|
|
73
|
+
// No identifiers to preserve — keep original
|
|
74
|
+
compressed.push(msg);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
// Store original content indexed by each identifier
|
|
78
|
+
for (const id of ids) {
|
|
79
|
+
if (!this.store.has(id)) {
|
|
80
|
+
this.store.set(id, content);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
allIdentifiers.push(...ids);
|
|
84
|
+
tokensSaved += Math.floor(content.length / 4); // rough token estimate
|
|
85
|
+
// Replace with a compact stub
|
|
86
|
+
const stub = `[Content compressed — identifiers: ${ids.slice(0, 5).join(', ')}${ids.length > 5 ? ` +${ids.length - 5} more` : ''}. Use restore_context(identifier) to retrieve.]`;
|
|
87
|
+
compressed.push({ ...msg, content: stub });
|
|
88
|
+
logger.debug('RestorableCompressor: compressed message', {
|
|
89
|
+
identifiers: ids.length,
|
|
90
|
+
originalLen: content.length,
|
|
91
|
+
stubLen: stub.length,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
messages: compressed,
|
|
96
|
+
identifiers: [...new Set(allIdentifiers)],
|
|
97
|
+
tokensSaved,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Restore the original content for an identifier.
|
|
102
|
+
*
|
|
103
|
+
* For file path identifiers, attempts to read from disk as a fallback.
|
|
104
|
+
* For URLs, returns a hint to use web_fetch.
|
|
105
|
+
*/
|
|
106
|
+
restore(identifier) {
|
|
107
|
+
// 1. Check in-memory store
|
|
108
|
+
const stored = this.store.get(identifier);
|
|
109
|
+
if (stored) {
|
|
110
|
+
return { found: true, content: stored, identifier };
|
|
111
|
+
}
|
|
112
|
+
// 2. Tool call ID — check disk-backed store
|
|
113
|
+
if (identifier.startsWith('call_') || identifier.startsWith('toulu_') || identifier.startsWith('toolu_')) {
|
|
114
|
+
const diskContent = this.readToolResultFromDisk(identifier);
|
|
115
|
+
if (diskContent) {
|
|
116
|
+
return { found: true, content: diskContent, identifier };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// 3. File path fallback — try reading from disk
|
|
120
|
+
if (!identifier.startsWith('http') && !identifier.startsWith('call_') && !identifier.startsWith('toolu_')) {
|
|
121
|
+
try {
|
|
122
|
+
// Strip line range if present (file.ts:10-50 → file.ts)
|
|
123
|
+
const filePath = identifier.split(':')[0];
|
|
124
|
+
if (fs.existsSync(filePath)) {
|
|
125
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
126
|
+
this.store.set(identifier, content); // cache for future
|
|
127
|
+
return { found: true, content, identifier };
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
// ignore
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 3. URL hint
|
|
135
|
+
if (identifier.startsWith('http')) {
|
|
136
|
+
return {
|
|
137
|
+
found: false,
|
|
138
|
+
content: `URL content not cached. Use web_fetch("${identifier}") to retrieve it.`,
|
|
139
|
+
identifier,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
found: false,
|
|
144
|
+
content: `Identifier "${identifier}" not found in restoration store.`,
|
|
145
|
+
identifier,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Persist a tool result to disk under `.codebuddy/tool-results/<callId>.txt`.
|
|
150
|
+
* This gives the restore_context tool a reliable disk-backed source and enables
|
|
151
|
+
* the compact/full dual-representation pattern (Manus AI #19).
|
|
152
|
+
*
|
|
153
|
+
* @param callId - Tool call ID (e.g. call_abc123 or toolu_xyz)
|
|
154
|
+
* @param content - Full tool output
|
|
155
|
+
* @param workDir - Working directory (defaults to process.cwd())
|
|
156
|
+
*/
|
|
157
|
+
writeToolResult(callId, content, workDir = process.cwd()) {
|
|
158
|
+
try {
|
|
159
|
+
const dir = path.join(workDir, '.codebuddy', 'tool-results');
|
|
160
|
+
if (!fs.existsSync(dir)) {
|
|
161
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
162
|
+
}
|
|
163
|
+
const filePath = path.join(dir, `${callId}.txt`);
|
|
164
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
165
|
+
// Also store in memory for fast access
|
|
166
|
+
this.store.set(callId, content);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
// Non-critical: disk write failure should not break tool execution
|
|
170
|
+
logger.debug('RestorableCompressor: failed to write tool result to disk', { callId, err });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Read a tool result from disk (`.codebuddy/tool-results/<callId>.txt`).
|
|
175
|
+
* Used by restore_context when the in-memory store has been evicted.
|
|
176
|
+
*/
|
|
177
|
+
readToolResultFromDisk(callId, workDir = process.cwd()) {
|
|
178
|
+
try {
|
|
179
|
+
const filePath = path.join(workDir, '.codebuddy', 'tool-results', `${callId}.txt`);
|
|
180
|
+
if (fs.existsSync(filePath)) {
|
|
181
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
182
|
+
this.store.set(callId, content); // cache back into memory
|
|
183
|
+
return content;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
// ignore
|
|
188
|
+
}
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
/** List all stored identifiers */
|
|
192
|
+
listIdentifiers() {
|
|
193
|
+
return [...this.store.keys()];
|
|
194
|
+
}
|
|
195
|
+
/** Total number of bytes stored */
|
|
196
|
+
storeSize() {
|
|
197
|
+
let total = 0;
|
|
198
|
+
for (const v of this.store.values())
|
|
199
|
+
total += v.length;
|
|
200
|
+
return total;
|
|
201
|
+
}
|
|
202
|
+
/** Evict oldest entries if store exceeds maxBytes (default 10 MB) */
|
|
203
|
+
evict(maxBytes = 10 * 1024 * 1024) {
|
|
204
|
+
while (this.storeSize() > maxBytes && this.store.size > 0) {
|
|
205
|
+
const firstKey = this.store.keys().next().value;
|
|
206
|
+
if (firstKey !== undefined) {
|
|
207
|
+
this.store.delete(firstKey);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ============================================================================
|
|
216
|
+
// Singleton
|
|
217
|
+
// ============================================================================
|
|
218
|
+
let _instance = null;
|
|
219
|
+
export function getRestorableCompressor() {
|
|
220
|
+
if (!_instance)
|
|
221
|
+
_instance = new RestorableCompressor();
|
|
222
|
+
return _instance;
|
|
223
|
+
}
|
|
224
|
+
/** Reset singleton (for tests) */
|
|
225
|
+
export function resetRestorableCompressor() {
|
|
226
|
+
_instance = null;
|
|
227
|
+
}
|
|
228
|
+
//# sourceMappingURL=restorable-compression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"restorable-compression.js","sourceRoot":"","sources":["../../src/context/restorable-compression.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daily Session Reset — OpenClaw-inspired context boundary
|
|
3
|
+
*
|
|
4
|
+
* Automatically resets the conversation context at a configurable time
|
|
5
|
+
* each day (default: 04:00 local time). This prevents unbounded context
|
|
6
|
+
* growth in long-running daemon sessions and mirrors the daily boundary
|
|
7
|
+
* reset pattern in OpenClaw.
|
|
8
|
+
*
|
|
9
|
+
* What is reset:
|
|
10
|
+
* - In-memory conversation history (messages array)
|
|
11
|
+
* - Cached tool selection results
|
|
12
|
+
*
|
|
13
|
+
* What is PRESERVED:
|
|
14
|
+
* - MEMORY.md (durable facts)
|
|
15
|
+
* - HEARTBEAT.md (task checklist)
|
|
16
|
+
* - Session metadata (model, cost counters)
|
|
17
|
+
* - All files on disk (todo.md, PLAN.md, etc.)
|
|
18
|
+
*
|
|
19
|
+
* After reset the agent posts a summary message noting the daily boundary
|
|
20
|
+
* so the conversation log remains intelligible.
|
|
21
|
+
*
|
|
22
|
+
* Ref: OpenClaw session management compaction docs
|
|
23
|
+
* https://docs.openclaw.ai/reference/session-management-compaction
|
|
24
|
+
*/
|
|
25
|
+
import { EventEmitter } from 'events';
|
|
26
|
+
export interface DailyResetConfig {
|
|
27
|
+
/** Hour of the day for the reset (0-23). Default: 4 */
|
|
28
|
+
resetHour: number;
|
|
29
|
+
/** Minute of the reset (0-59). Default: 0 */
|
|
30
|
+
resetMinute: number;
|
|
31
|
+
/** IANA timezone identifier. Default: system local */
|
|
32
|
+
timezone?: string;
|
|
33
|
+
/** Whether the daily reset is enabled. Default: true */
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
/** Post a summary message after reset. Default: true */
|
|
36
|
+
postSummary: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface ResetResult {
|
|
39
|
+
triggeredAt: Date;
|
|
40
|
+
/** Number of messages cleared */
|
|
41
|
+
messagesCleared: number;
|
|
42
|
+
/** Summary message posted to conversation (or null if postSummary is false) */
|
|
43
|
+
summaryMessage: string | null;
|
|
44
|
+
}
|
|
45
|
+
export declare class DailyResetManager extends EventEmitter {
|
|
46
|
+
private config;
|
|
47
|
+
private timer;
|
|
48
|
+
private lastResetDate;
|
|
49
|
+
constructor(config?: Partial<DailyResetConfig>);
|
|
50
|
+
/**
|
|
51
|
+
* Start the daily reset scheduler.
|
|
52
|
+
* Call this once when the daemon starts.
|
|
53
|
+
*/
|
|
54
|
+
start(): void;
|
|
55
|
+
stop(): void;
|
|
56
|
+
/** Returns milliseconds until the next reset window. */
|
|
57
|
+
msUntilNextReset(): number;
|
|
58
|
+
private scheduleNext;
|
|
59
|
+
/**
|
|
60
|
+
* Perform the daily reset on the provided messages array (modified in-place).
|
|
61
|
+
*
|
|
62
|
+
* @param messages - The agent's LLM messages array to clear
|
|
63
|
+
* @param systemMessage - Optional system message to keep at position [0]
|
|
64
|
+
*/
|
|
65
|
+
runReset(messages: Array<{
|
|
66
|
+
role: string;
|
|
67
|
+
content: string | null;
|
|
68
|
+
}>, systemMessage?: {
|
|
69
|
+
role: string;
|
|
70
|
+
content: string;
|
|
71
|
+
}): Promise<ResetResult>;
|
|
72
|
+
private buildSummaryMessage;
|
|
73
|
+
getConfig(): Readonly<DailyResetConfig>;
|
|
74
|
+
isEnabled(): boolean;
|
|
75
|
+
}
|
|
76
|
+
export declare function getDailyResetManager(config?: Partial<DailyResetConfig>): DailyResetManager;
|
|
77
|
+
export declare function resetDailyResetManager(): void;
|