@hoilab/ada-cli 0.84.4 → 0.84.6
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/core/agent-session.d.ts +4 -0
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +80 -0
- package/dist/core/agent-session.js.map +1 -1
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +16 -1
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -32,6 +32,7 @@ import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
|
32
32
|
import { ModelRegistry } from "./model-registry.js";
|
|
33
33
|
import { expandPromptTemplate } from "./prompt-templates.js";
|
|
34
34
|
import { CURRENT_SESSION_VERSION, getLatestCompactionEntry } from "./session-manager.js";
|
|
35
|
+
import { loadProjectMemorySync, saveProjectMemory } from "./project-memory.js";
|
|
35
36
|
import { createSyntheticSourceInfo } from "./source-info.js";
|
|
36
37
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
37
38
|
import { createLocalBashOperations } from "./tools/bash.js";
|
|
@@ -334,8 +335,81 @@ export class AgentSession {
|
|
|
334
335
|
this._resolveIdleWaitIfIdle();
|
|
335
336
|
}
|
|
336
337
|
}
|
|
338
|
+
// =========================================================================
|
|
339
|
+
// Automatic project memory (claude-mem style)
|
|
340
|
+
// =========================================================================
|
|
341
|
+
static AUTO_MEMORIZE_MIN_INTERVAL_MS = 45_000;
|
|
342
|
+
static AUTO_MEMORIZE_SYSTEM_PROMPT = `You maintain the shared project memory of a coding project.
|
|
343
|
+
The memory is loaded into the context of every conversation of this project, so it must stay concise and durable.
|
|
344
|
+
|
|
345
|
+
Task: given the CURRENT MEMORY and the LATEST CONVERSATION TURN, produce the COMPLETE new memory content in Markdown.
|
|
346
|
+
- Keep everything already in the current memory that is still true.
|
|
347
|
+
- Add durable facts from the turn: architecture, conventions, decisions, commands, project layout, user preferences.
|
|
348
|
+
- Ignore transient details (specific code snippets, error messages, formatting fixes, small refactors).
|
|
349
|
+
- If nothing durable and new was said, reply with exactly: NO_CHANGE
|
|
350
|
+
- Keep it under ~600 words. Use short bullet lists.
|
|
351
|
+
- Reply with ONLY the memory content (or NO_CHANGE).`;
|
|
352
|
+
async _maybeAutoMemorize(event) {
|
|
353
|
+
try {
|
|
354
|
+
if (!this.settingsManager.getProjectMemoryEnabled())
|
|
355
|
+
return;
|
|
356
|
+
const now = Date.now();
|
|
357
|
+
if (now - this._lastAutoMemorizeAt < AgentSession.AUTO_MEMORIZE_MIN_INTERVAL_MS)
|
|
358
|
+
return;
|
|
359
|
+
const model = this.model;
|
|
360
|
+
if (!model)
|
|
361
|
+
return;
|
|
362
|
+
// Only turns with a real user message followed by a successful assistant reply.
|
|
363
|
+
const messages = event.messages;
|
|
364
|
+
const hasUserMessage = messages.some((m) => m.role === "user" && contentText(m.content, "").trim().length > 0);
|
|
365
|
+
const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
|
|
366
|
+
if (!hasUserMessage || !lastAssistant)
|
|
367
|
+
return;
|
|
368
|
+
const lastAssistantText = contentText(lastAssistant.content, "").trim();
|
|
369
|
+
if (!lastAssistantText)
|
|
370
|
+
return;
|
|
371
|
+
// Build a compact transcript of the turn (user + assistant + tool summaries).
|
|
372
|
+
const transcriptLines = [];
|
|
373
|
+
for (const m of messages) {
|
|
374
|
+
if (m.role === "user") {
|
|
375
|
+
transcriptLines.push(`USER: ${contentText(m.content, "").trim().slice(0, 2000)}`);
|
|
376
|
+
}
|
|
377
|
+
else if (m.role === "assistant") {
|
|
378
|
+
transcriptLines.push(`ASSISTANT: ${contentText(m.content, "").trim().slice(0, 2000)}`);
|
|
379
|
+
}
|
|
380
|
+
else if (m.role === "toolResult") {
|
|
381
|
+
transcriptLines.push(`(tool result)`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
const transcript = transcriptLines.join("\n").slice(0, 12_000);
|
|
385
|
+
if (transcript.trim().length === 0)
|
|
386
|
+
return;
|
|
387
|
+
const agentDir = this._resourceLoader.getAgentDir();
|
|
388
|
+
const currentMemory = loadProjectMemorySync(this._cwd, agentDir);
|
|
389
|
+
const result = await this._modelRuntime.completeSimple(model, {
|
|
390
|
+
systemPrompt: AgentSession.AUTO_MEMORIZE_SYSTEM_PROMPT,
|
|
391
|
+
messages: [
|
|
392
|
+
{
|
|
393
|
+
role: "user",
|
|
394
|
+
content: `CURRENT MEMORY:\n${currentMemory?.content ?? "(none)"}\n\nLATEST CONVERSATION TURN:\n${transcript}`,
|
|
395
|
+
},
|
|
396
|
+
],
|
|
397
|
+
});
|
|
398
|
+
const newContent = contentText(result.content, "").trim();
|
|
399
|
+
if (!newContent || newContent === "NO_CHANGE")
|
|
400
|
+
return;
|
|
401
|
+
if (newContent === currentMemory?.content)
|
|
402
|
+
return;
|
|
403
|
+
this._lastAutoMemorizeAt = now;
|
|
404
|
+
await saveProjectMemory(this._cwd, agentDir, newContent);
|
|
405
|
+
}
|
|
406
|
+
catch {
|
|
407
|
+
// Memorization must never break the conversation flow.
|
|
408
|
+
}
|
|
409
|
+
}
|
|
337
410
|
// Track last assistant message for auto-compaction check
|
|
338
411
|
_lastAssistantMessage = undefined;
|
|
412
|
+
_lastAutoMemorizeAt = 0;
|
|
339
413
|
/** Internal handler for agent events - shared by subscribe and reconnect */
|
|
340
414
|
_handleAgentEvent = async (event) => {
|
|
341
415
|
// When a user message starts, check if it's from either queue and remove it BEFORE emitting
|
|
@@ -364,6 +438,12 @@ export class AgentSession {
|
|
|
364
438
|
await this._emitExtensionEvent(event);
|
|
365
439
|
// Notify all listeners
|
|
366
440
|
this._emit(event.type === "agent_end" ? { ...event, willRetry: this._willRetryAfterAgentEnd(event) } : event);
|
|
441
|
+
// Automatic project memory (claude-mem style): after each completed
|
|
442
|
+
// agent turn with real user interaction, distill durable facts into
|
|
443
|
+
// the shared project memory. Fire-and-forget; never blocks the turn.
|
|
444
|
+
if (event.type === "agent_end") {
|
|
445
|
+
void this._maybeAutoMemorize(event);
|
|
446
|
+
}
|
|
367
447
|
// Handle session persistence
|
|
368
448
|
if (event.type === "message_end") {
|
|
369
449
|
// Check if this is a custom message from extensions
|