0agent 1.0.44 → 1.0.46
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/bin/chat.js +129 -93
- package/dist/daemon.mjs +50 -26
- package/package.json +1 -1
package/bin/chat.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* /model to switch. /key to add provider keys. Never forgets previous keys.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import { createInterface, emitKeypressEvents
|
|
10
|
+
import { createInterface, emitKeypressEvents } from 'node:readline';
|
|
11
11
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
12
12
|
import { resolve } from 'node:path';
|
|
13
13
|
import { homedir } from 'node:os';
|
|
@@ -836,61 +836,114 @@ async function handleCommand(input) {
|
|
|
836
836
|
}
|
|
837
837
|
}
|
|
838
838
|
|
|
839
|
-
// ───
|
|
840
|
-
//
|
|
841
|
-
//
|
|
842
|
-
|
|
839
|
+
// ─── Command palette ──────────────────────────────────────────────────────────
|
|
840
|
+
// Fully takes over stdin when open. No cursor tricks — pauses readline,
|
|
841
|
+
// reads raw bytes directly, draws with \x1b[NA\x1b[0J (up + clear-to-end).
|
|
842
|
+
// Returns the selected command string, or null if cancelled.
|
|
843
843
|
|
|
844
|
-
|
|
845
|
-
if (pendingResolve) { _clearMenu(); return; } // don't show while session running
|
|
844
|
+
let _paletteOpen = false;
|
|
846
845
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
).slice(0, 10);
|
|
846
|
+
async function openPalette(initialFilter = '') {
|
|
847
|
+
if (_paletteOpen || pendingResolve) return null;
|
|
848
|
+
_paletteOpen = true;
|
|
851
849
|
|
|
852
|
-
//
|
|
853
|
-
|
|
850
|
+
// Pause readline so it stops consuming stdin events
|
|
851
|
+
rl.pause();
|
|
852
|
+
// Resume the raw stream so our data handler receives bytes
|
|
853
|
+
process.stdin.resume();
|
|
854
|
+
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
854
855
|
|
|
855
|
-
|
|
856
|
+
let filter = initialFilter.toLowerCase();
|
|
857
|
+
let idx = 0;
|
|
858
|
+
let drawn = 0; // number of lines we've printed so far
|
|
856
859
|
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
moveCursor(process.stdout, 0, existingLines);
|
|
861
|
-
for (let i = 0; i < existingLines; i++) {
|
|
862
|
-
rlClearLine(process.stdout, 0);
|
|
863
|
-
if (i < existingLines - 1) moveCursor(process.stdout, 0, -1);
|
|
864
|
-
}
|
|
865
|
-
moveCursor(process.stdout, 0, -(existingLines - 1));
|
|
866
|
-
}
|
|
860
|
+
const getItems = () => SLASH_COMMANDS.filter(c =>
|
|
861
|
+
!filter || c.cmd.slice(1).startsWith(filter)
|
|
862
|
+
);
|
|
867
863
|
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
process.stdout.write(
|
|
872
|
-
` ${fmt(C.cyan, m.cmd.padEnd(20))} ${fmt(C.dim, m.desc)}\x1b[K\n`
|
|
873
|
-
);
|
|
874
|
-
}
|
|
864
|
+
const paint = () => {
|
|
865
|
+
// Erase previous draw: move up `drawn` lines, clear everything below
|
|
866
|
+
if (drawn > 0) process.stdout.write(`\x1b[${drawn}A\x1b[0J`);
|
|
875
867
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
moveCursor(process.stdout, 999, 0);
|
|
868
|
+
const items = getItems();
|
|
869
|
+
const show = items.slice(0, 14);
|
|
870
|
+
if (idx >= items.length) idx = Math.max(0, items.length - 1);
|
|
880
871
|
|
|
881
|
-
|
|
882
|
-
}
|
|
872
|
+
const lines = [];
|
|
883
873
|
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
874
|
+
// Top border
|
|
875
|
+
lines.push(` \x1b[2m${'─'.repeat(58)}\x1b[0m`);
|
|
876
|
+
|
|
877
|
+
if (show.length === 0) {
|
|
878
|
+
lines.push(` \x1b[2m no commands match "/${filter}"\x1b[0m`);
|
|
879
|
+
} else {
|
|
880
|
+
for (let i = 0; i < show.length; i++) {
|
|
881
|
+
const m = show[i];
|
|
882
|
+
const sel = i === idx;
|
|
883
|
+
if (sel) {
|
|
884
|
+
lines.push(
|
|
885
|
+
` \x1b[36;1m›\x1b[0m \x1b[36;1m${m.cmd.padEnd(22)}\x1b[0m \x1b[0m${m.desc}\x1b[0m`
|
|
886
|
+
);
|
|
887
|
+
} else {
|
|
888
|
+
lines.push(
|
|
889
|
+
` \x1b[36m${m.cmd.padEnd(22)}\x1b[0m \x1b[2m${m.desc}\x1b[0m`
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
if (items.length > 14) lines.push(` \x1b[2m …${items.length - 14} more\x1b[0m`);
|
|
896
|
+
|
|
897
|
+
// Bottom: search input line
|
|
898
|
+
lines.push(` \x1b[2m${'─'.repeat(58)}\x1b[0m`);
|
|
899
|
+
lines.push(` ${fmt(C.cyan, '/')}${filter}\x1b[K \x1b[2m↑↓ navigate · Enter select · Esc cancel\x1b[0m`);
|
|
900
|
+
|
|
901
|
+
const out = lines.join('\n') + '\n';
|
|
902
|
+
process.stdout.write(out);
|
|
903
|
+
drawn = lines.length;
|
|
904
|
+
};
|
|
905
|
+
|
|
906
|
+
paint();
|
|
907
|
+
|
|
908
|
+
return new Promise((resolve) => {
|
|
909
|
+
const onData = (chunk) => {
|
|
910
|
+
const s = chunk.toString();
|
|
911
|
+
|
|
912
|
+
if (s === '\r' || s === '\n') { // Enter — select
|
|
913
|
+
const sel = getItems()[idx]?.cmd ?? null;
|
|
914
|
+
finish(sel);
|
|
915
|
+
} else if (s === '\x1b' || s === '\x03') { // Esc / Ctrl-C — cancel
|
|
916
|
+
finish(null);
|
|
917
|
+
} else if (s === '\x1b[A') { // Up arrow
|
|
918
|
+
idx = Math.max(0, idx - 1);
|
|
919
|
+
paint();
|
|
920
|
+
} else if (s === '\x1b[B') { // Down arrow
|
|
921
|
+
idx = Math.min(getItems().length - 1, idx + 1);
|
|
922
|
+
paint();
|
|
923
|
+
} else if (s === '\x7f' || s === '\x08') { // Backspace
|
|
924
|
+
filter = filter.slice(0, -1);
|
|
925
|
+
idx = 0;
|
|
926
|
+
paint();
|
|
927
|
+
} else if (/^[a-z0-9\-_]$/i.test(s)) { // Printable letter/digit/hyphen
|
|
928
|
+
filter += s.toLowerCase();
|
|
929
|
+
idx = 0;
|
|
930
|
+
paint();
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
const finish = (selected) => {
|
|
935
|
+
process.stdin.removeListener('data', onData);
|
|
936
|
+
// Erase the palette
|
|
937
|
+
if (drawn > 0) process.stdout.write(`\x1b[${drawn}A\x1b[0J`);
|
|
938
|
+
drawn = 0;
|
|
939
|
+
_paletteOpen = false;
|
|
940
|
+
// Hand control back to readline
|
|
941
|
+
rl.resume();
|
|
942
|
+
resolve(selected);
|
|
943
|
+
};
|
|
944
|
+
|
|
945
|
+
process.stdin.on('data', onData);
|
|
946
|
+
});
|
|
894
947
|
}
|
|
895
948
|
|
|
896
949
|
// ─── Main REPL ────────────────────────────────────────────────────────────────
|
|
@@ -902,50 +955,31 @@ const rl = createInterface({
|
|
|
902
955
|
completer: (line, callback) => {
|
|
903
956
|
if (!line.startsWith('/')) return callback(null, [[], line]);
|
|
904
957
|
|
|
905
|
-
const filter
|
|
906
|
-
const matches = SLASH_COMMANDS.filter(c =>
|
|
907
|
-
!filter || c.cmd.slice(1).startsWith(filter)
|
|
908
|
-
);
|
|
909
|
-
|
|
910
|
-
if (matches.length === 0) return callback(null, [[], line]);
|
|
958
|
+
const filter = line.slice(1).toLowerCase();
|
|
959
|
+
const matches = SLASH_COMMANDS.filter(c => !filter || c.cmd.slice(1).startsWith(filter));
|
|
911
960
|
|
|
912
|
-
// Single match — let readline silently auto-complete
|
|
913
|
-
if (matches.length === 1)
|
|
914
|
-
_clearMenu();
|
|
915
|
-
return callback(null, [[matches[0].cmd + ' '], line]);
|
|
916
|
-
}
|
|
961
|
+
// Single exact match — let readline silently auto-complete
|
|
962
|
+
if (matches.length === 1) return callback(null, [[matches[0].cmd + ' '], line]);
|
|
917
963
|
|
|
918
|
-
// Multiple
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
process.stdout.write(
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
964
|
+
// Multiple (or bare /) — open interactive palette
|
|
965
|
+
// Schedule after this tick so readline finishes its Tab handling first
|
|
966
|
+
setImmediate(async () => {
|
|
967
|
+
// Clear the typed fragment from the prompt line before opening palette
|
|
968
|
+
process.stdout.write('\r\x1b[2K');
|
|
969
|
+
const selected = await openPalette(filter);
|
|
970
|
+
if (selected) {
|
|
971
|
+
await executeInput(selected);
|
|
972
|
+
} else {
|
|
973
|
+
rl.prompt();
|
|
974
|
+
}
|
|
975
|
+
});
|
|
929
976
|
return callback(null, [[], line]);
|
|
930
977
|
},
|
|
931
978
|
});
|
|
932
979
|
|
|
933
|
-
//
|
|
980
|
+
// Trigger palette when user types exactly '/' and presses Tab or Enter isn't needed —
|
|
981
|
+
// the completer above handles Tab. For bare '/' + Enter, handled in rl.on('line') below.
|
|
934
982
|
emitKeypressEvents(process.stdin, rl);
|
|
935
|
-
process.stdin.on('keypress', (_char, key) => {
|
|
936
|
-
if (key?.name === 'return' || key?.name === 'enter') {
|
|
937
|
-
_clearMenu(); // clear before readline processes the line
|
|
938
|
-
return;
|
|
939
|
-
}
|
|
940
|
-
setImmediate(() => {
|
|
941
|
-
const line = rl.line ?? '';
|
|
942
|
-
if (line.startsWith('/') && !pendingResolve) {
|
|
943
|
-
_drawMenu(line.slice(1));
|
|
944
|
-
} else {
|
|
945
|
-
_clearMenu();
|
|
946
|
-
}
|
|
947
|
-
});
|
|
948
|
-
});
|
|
949
983
|
|
|
950
984
|
printHeader();
|
|
951
985
|
printInsights();
|
|
@@ -1253,19 +1287,21 @@ async function drainQueue() {
|
|
|
1253
1287
|
}
|
|
1254
1288
|
|
|
1255
1289
|
rl.on('line', async (input) => {
|
|
1256
|
-
_clearMenu(); // always clear menu when a line is submitted
|
|
1257
1290
|
const line = input.trim();
|
|
1258
1291
|
if (!line) { rl.prompt(); return; }
|
|
1259
1292
|
|
|
1260
|
-
// Bare `/` →
|
|
1261
|
-
if (line
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1293
|
+
// Bare `/` or partial `/cmd` with no session running → open interactive palette
|
|
1294
|
+
if (line.startsWith('/') && !pendingResolve) {
|
|
1295
|
+
const filter = line === '/' ? '' : line.slice(1);
|
|
1296
|
+
const exact = SLASH_COMMANDS.find(c => c.cmd === line);
|
|
1297
|
+
if (!exact) {
|
|
1298
|
+
// Clear the echoed input line before opening palette
|
|
1299
|
+
process.stdout.write('\x1b[1A\r\x1b[2K');
|
|
1300
|
+
const selected = await openPalette(filter);
|
|
1301
|
+
if (selected) await executeInput(selected);
|
|
1302
|
+
else rl.prompt();
|
|
1303
|
+
return;
|
|
1265
1304
|
}
|
|
1266
|
-
console.log('');
|
|
1267
|
-
rl.prompt();
|
|
1268
|
-
return;
|
|
1269
1305
|
}
|
|
1270
1306
|
|
|
1271
1307
|
// If a session is already running, queue the message.
|
package/dist/daemon.mjs
CHANGED
|
@@ -2493,8 +2493,9 @@ var init_MemoryCapability = __esm({
|
|
|
2493
2493
|
"use strict";
|
|
2494
2494
|
init_src();
|
|
2495
2495
|
MemoryCapability = class {
|
|
2496
|
-
constructor(graph) {
|
|
2496
|
+
constructor(graph, onWrite) {
|
|
2497
2497
|
this.graph = graph;
|
|
2498
|
+
this.onWrite = onWrite;
|
|
2498
2499
|
}
|
|
2499
2500
|
name = "memory_write";
|
|
2500
2501
|
description = "Persist a discovered fact to long-term memory so it survives across sessions.";
|
|
@@ -2537,11 +2538,13 @@ var init_MemoryCapability = __esm({
|
|
|
2537
2538
|
});
|
|
2538
2539
|
this.graph.addNode(node);
|
|
2539
2540
|
}
|
|
2540
|
-
|
|
2541
|
+
const result = {
|
|
2541
2542
|
success: true,
|
|
2542
2543
|
output: `Remembered: "${label}" = ${content.slice(0, 120)}${content.length > 120 ? "\u2026" : ""}`,
|
|
2543
2544
|
duration_ms: Date.now() - start
|
|
2544
2545
|
};
|
|
2546
|
+
this.onWrite?.();
|
|
2547
|
+
return result;
|
|
2545
2548
|
} catch (err) {
|
|
2546
2549
|
return {
|
|
2547
2550
|
success: false,
|
|
@@ -2651,7 +2654,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
2651
2654
|
* task_type: browser_task). The main agent does NOT have direct access
|
|
2652
2655
|
* to browser_open without going through a subagent spawn.
|
|
2653
2656
|
*/
|
|
2654
|
-
constructor(codespaceManager, graph) {
|
|
2657
|
+
constructor(codespaceManager, graph, onMemoryWrite) {
|
|
2655
2658
|
this.register(new WebSearchCapability());
|
|
2656
2659
|
if (codespaceManager) {
|
|
2657
2660
|
try {
|
|
@@ -2667,7 +2670,7 @@ var init_CapabilityRegistry = __esm({
|
|
|
2667
2670
|
this.register(new ShellCapability());
|
|
2668
2671
|
this.register(new FileCapability());
|
|
2669
2672
|
if (graph) {
|
|
2670
|
-
this.register(new MemoryCapability(graph));
|
|
2673
|
+
this.register(new MemoryCapability(graph, onMemoryWrite));
|
|
2671
2674
|
}
|
|
2672
2675
|
}
|
|
2673
2676
|
register(cap) {
|
|
@@ -2734,7 +2737,7 @@ var init_AgentExecutor = __esm({
|
|
|
2734
2737
|
this.maxIterations = config.max_iterations ?? 20;
|
|
2735
2738
|
this.maxCommandMs = config.max_command_ms ?? 3e4;
|
|
2736
2739
|
this.agentRoot = config.agent_root;
|
|
2737
|
-
this.registry = new CapabilityRegistry(void 0, config.graph);
|
|
2740
|
+
this.registry = new CapabilityRegistry(void 0, config.graph, config.onMemoryWrite);
|
|
2738
2741
|
}
|
|
2739
2742
|
cwd;
|
|
2740
2743
|
maxIterations;
|
|
@@ -4735,7 +4738,7 @@ var SessionManager = class {
|
|
|
4735
4738
|
if (activeLLM?.isConfigured) {
|
|
4736
4739
|
const executor = new AgentExecutor(
|
|
4737
4740
|
activeLLM,
|
|
4738
|
-
{ cwd: this.cwd, agent_root: this.agentRoot, graph: this.graph },
|
|
4741
|
+
{ cwd: this.cwd, agent_root: this.agentRoot, graph: this.graph, onMemoryWrite: this.onMemoryWritten },
|
|
4739
4742
|
// step callback → emit session.step events
|
|
4740
4743
|
(step) => this.addStep(sessionId, step),
|
|
4741
4744
|
// token callback → emit session.token events
|
|
@@ -4901,33 +4904,48 @@ Current task:`;
|
|
|
4901
4904
|
*/
|
|
4902
4905
|
async _extractAndPersistFacts(task, output, llm) {
|
|
4903
4906
|
if (!this.graph || !llm.isConfigured) return;
|
|
4907
|
+
const combined = `${task} ${output}`;
|
|
4908
|
+
if (combined.trim().length < 20) return;
|
|
4904
4909
|
const prompt = `Extract factual entities from this conversation that should be remembered long-term.
|
|
4905
|
-
Return ONLY a JSON array, no
|
|
4910
|
+
Return ONLY a valid JSON array (no markdown, no explanation), max 12 items.
|
|
4911
|
+
If nothing worth remembering, return [].
|
|
4906
4912
|
|
|
4907
4913
|
Types: identity (name/role), project (apps/products), tech (stack/tools), preference, url, path, config, outcome
|
|
4908
4914
|
|
|
4909
|
-
Format: [{"label":"snake_case_key","content":"value
|
|
4915
|
+
Format: [{"label":"snake_case_key","content":"value","type":"type"}]
|
|
4910
4916
|
|
|
4911
4917
|
Examples:
|
|
4912
|
-
-
|
|
4913
|
-
-
|
|
4914
|
-
-
|
|
4918
|
+
- "my name is Sahil" \u2192 {"label":"user_name","content":"Sahil","type":"identity"}
|
|
4919
|
+
- "we have a telegram bot" \u2192 {"label":"project_telegram_bot","content":"user has a Telegram bot","type":"project"}
|
|
4920
|
+
- "I use React and Next.js" \u2192 {"label":"tech_stack","content":"React, Next.js","type":"tech"}
|
|
4921
|
+
- ngrok URL found \u2192 {"label":"ngrok_url","content":"https://abc.ngrok.io","type":"url"}
|
|
4915
4922
|
|
|
4916
4923
|
Conversation:
|
|
4917
4924
|
User: ${task.slice(0, 600)}
|
|
4918
|
-
Agent: ${output.slice(0,
|
|
4925
|
+
Agent: ${output.slice(0, 500)}`;
|
|
4919
4926
|
try {
|
|
4920
4927
|
const resp = await llm.complete(
|
|
4921
4928
|
[{ role: "user", content: prompt }],
|
|
4922
|
-
"You are a
|
|
4929
|
+
"You are a memory extraction system. Be concise. Extract only factual, durable information. Return valid JSON only."
|
|
4923
4930
|
);
|
|
4924
|
-
|
|
4925
|
-
|
|
4926
|
-
|
|
4931
|
+
let entities = [];
|
|
4932
|
+
const raw = resp.content.trim();
|
|
4933
|
+
try {
|
|
4934
|
+
const parsed = JSON.parse(raw);
|
|
4935
|
+
if (Array.isArray(parsed)) entities = parsed;
|
|
4936
|
+
} catch {
|
|
4937
|
+
const match = raw.match(/\[\s*\{[\s\S]*?\}\s*\]/);
|
|
4938
|
+
if (match) {
|
|
4939
|
+
try {
|
|
4940
|
+
entities = JSON.parse(match[0]);
|
|
4941
|
+
} catch {
|
|
4942
|
+
}
|
|
4943
|
+
}
|
|
4944
|
+
}
|
|
4927
4945
|
if (!Array.isArray(entities) || entities.length === 0) return;
|
|
4928
4946
|
let wrote = 0;
|
|
4929
4947
|
for (const e of entities.slice(0, 12)) {
|
|
4930
|
-
if (!e
|
|
4948
|
+
if (!e?.label?.trim() || !e?.content?.trim()) continue;
|
|
4931
4949
|
const nodeId = `memory:${e.label.toLowerCase().replace(/[^a-z0-9_]/g, "_")}`;
|
|
4932
4950
|
try {
|
|
4933
4951
|
const existing = this.graph.getNode(nodeId);
|
|
@@ -4946,14 +4964,16 @@ Agent: ${output.slice(0, 400)}`;
|
|
|
4946
4964
|
}));
|
|
4947
4965
|
}
|
|
4948
4966
|
wrote++;
|
|
4949
|
-
} catch {
|
|
4967
|
+
} catch (err) {
|
|
4968
|
+
console.warn(`[0agent] Memory write failed for "${e.label}":`, err instanceof Error ? err.message : err);
|
|
4950
4969
|
}
|
|
4951
4970
|
}
|
|
4952
4971
|
if (wrote > 0) {
|
|
4953
|
-
console.log(`[0agent] Memory:
|
|
4972
|
+
console.log(`[0agent] Memory: persisted ${wrote} facts \u2192 graph`);
|
|
4954
4973
|
this.onMemoryWritten?.();
|
|
4955
4974
|
}
|
|
4956
|
-
} catch {
|
|
4975
|
+
} catch (err) {
|
|
4976
|
+
console.warn("[0agent] Memory extraction failed:", err instanceof Error ? err.message : String(err));
|
|
4957
4977
|
}
|
|
4958
4978
|
}
|
|
4959
4979
|
/**
|
|
@@ -7303,20 +7323,24 @@ var ZeroAgentDaemon = class {
|
|
|
7303
7323
|
adapter: this.adapter,
|
|
7304
7324
|
agentRoot,
|
|
7305
7325
|
// agent source path — self-improvement tasks read the right files
|
|
7306
|
-
//
|
|
7326
|
+
// Push to GitHub immediately when facts are extracted from a session
|
|
7307
7327
|
onMemoryWritten: () => {
|
|
7308
7328
|
this.githubMemorySync?.markDirty();
|
|
7329
|
+
if (this.githubMemorySync) {
|
|
7330
|
+
this.githubMemorySync.push("sync: new facts learned").then((r) => {
|
|
7331
|
+
if (r.pushed) console.log(`[0agent] Memory pushed: ${r.nodes_synced} nodes \u2192 github`);
|
|
7332
|
+
}).catch(() => {
|
|
7333
|
+
});
|
|
7334
|
+
}
|
|
7309
7335
|
}
|
|
7310
7336
|
});
|
|
7311
7337
|
const teamSync = identity && teams.length > 0 ? new TeamSync(teamManager, this.adapter, identity.entity_node_id) : null;
|
|
7312
7338
|
if (this.githubMemorySync) {
|
|
7313
7339
|
const memSync = this.githubMemorySync;
|
|
7314
7340
|
this.memorySyncTimer = setInterval(async () => {
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
console.log(`[0agent] Memory auto-synced: ${result.nodes_synced} nodes`);
|
|
7319
|
-
}
|
|
7341
|
+
const result = await memSync.push().catch(() => null);
|
|
7342
|
+
if (result?.pushed && result.nodes_synced > 0) {
|
|
7343
|
+
console.log(`[0agent] Memory synced: ${result.nodes_synced} nodes \u2192 github`);
|
|
7320
7344
|
}
|
|
7321
7345
|
}, 2 * 60 * 1e3);
|
|
7322
7346
|
if (typeof this.memorySyncTimer === "object") this.memorySyncTimer.unref?.();
|