Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions pi-extension/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,16 +136,20 @@ Empty array if nothing worth remembering. Only output JSON.`;

export default function hexus(pi: ExtensionAPI) {
// Bootstrap config asynchronously — initConfig() runs once and caches.
// getConfig() returns defaults until the promise resolves, then returns real config.
initConfig().catch((err) => console.warn("hexus: config load failed:", err));
const config = getConfig();
// getConfig() returns defaults until the promise resolves, so capture the
// resolved config once it's available instead of freezing the defaults.
let config = getConfig();
initConfig()
.then((cfg) => { config = cfg; })
.catch((err) => console.warn("hexus: config load failed:", err));
const reflConfig = getReflectionConfig();

// State
let sessionId: string | undefined;
let turnsSinceReflection = 0;
let lastReflectionTurn = 0;
let idleTimer: ReturnType<typeof setTimeout> | null = null;
let healthRetryTimer: ReturnType<typeof setTimeout> | null = null;
let isReflecting = false;
let recallInFlight = false; // Dedupe concurrent recall calls

Expand Down Expand Up @@ -244,6 +248,25 @@ export default function hexus(pi: ExtensionAPI) {
}, reflConfig.idleSeconds * 1000);
}

// Re-check /api/health every few seconds while offline so the status
// recovers automatically once the server is reachable again.
function scheduleHealthRetry(ctx: ExtensionAPI): void {
if (healthRetryTimer) clearTimeout(healthRetryTimer);
healthRetryTimer = setTimeout(async () => {
healthRetryTimer = null;
const client = getClient();
const health = await client.health(true).catch(() => null);
if (health?.status === "ok") {
ctx.ui.notify(`hexus connected (${health.row_counts.memory_entries} memories)`, "info");
ctx.ui.setStatus("hexus", `hexus: ${health.row_counts.memory_entries} memories`);
if (reflConfig.enabled) scheduleReflection(ctx);
} else {
ctx.ui.setStatus("hexus", "hexus: offline");
scheduleHealthRetry(ctx);
}
}, 10_000);
}

// -------------------------------------------------------------------------
// Events
// -------------------------------------------------------------------------
Expand All @@ -261,6 +284,9 @@ export default function hexus(pi: ExtensionAPI) {
} else {
ctx.ui.notify("hexus: connection failed", "warning");
ctx.ui.setStatus("hexus", "hexus: offline");
// Keep polling so the status recovers without waiting for the next turn
// (or a user prompt) once the server is reachable again.
scheduleHealthRetry(ctx);
}
});

Expand Down Expand Up @@ -426,6 +452,7 @@ export default function hexus(pi: ExtensionAPI) {

pi.on("session_shutdown", () => {
if (idleTimer) { clearTimeout(idleTimer); idleTimer = null; }
if (healthRetryTimer) { clearTimeout(healthRetryTimer); healthRetryTimer = null; }
// Reset in-flight state so a new session starts clean
isReflecting = false;
recallInFlight = false;
Expand Down
Loading