pi-observational-memory-extension 0.1.1 → 0.1.2
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/extensions/index.ts +96 -15
- package/package.json +1 -1
package/extensions/index.ts
CHANGED
|
@@ -380,7 +380,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
380
380
|
runtime.currentOperation?.abort();
|
|
381
381
|
if (runtime.state) {
|
|
382
382
|
try {
|
|
383
|
-
|
|
383
|
+
let isStale = false;
|
|
384
|
+
try {
|
|
385
|
+
const _ = ctx.cwd;
|
|
386
|
+
} catch {
|
|
387
|
+
isStale = true;
|
|
388
|
+
}
|
|
389
|
+
if (!isStale && runtime.state.enabled && runtime.state.pendingMessageTokens > 0) {
|
|
384
390
|
await observeNow(ctx, { force: true, reason: "session_shutdown" });
|
|
385
391
|
}
|
|
386
392
|
} catch (error) {
|
|
@@ -388,8 +394,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
388
394
|
}
|
|
389
395
|
await saveState(runtime.state);
|
|
390
396
|
}
|
|
391
|
-
|
|
392
|
-
|
|
397
|
+
try {
|
|
398
|
+
ctx.ui.setStatus("om", undefined);
|
|
399
|
+
ctx.ui.setWidget("om", undefined);
|
|
400
|
+
} catch (e) {
|
|
401
|
+
// Ignore if UI or context is already stale
|
|
402
|
+
}
|
|
393
403
|
});
|
|
394
404
|
|
|
395
405
|
pi.on("model_select", async (event, ctx) => {
|
|
@@ -568,6 +578,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
568
578
|
}
|
|
569
579
|
|
|
570
580
|
async function safeBoundary(ctx: any, boundary: string): Promise<void> {
|
|
581
|
+
let isStale = false;
|
|
582
|
+
try {
|
|
583
|
+
const _ = ctx.cwd;
|
|
584
|
+
} catch {
|
|
585
|
+
isStale = true;
|
|
586
|
+
}
|
|
587
|
+
if (isStale) return;
|
|
588
|
+
|
|
571
589
|
const state = await ensureState(ctx);
|
|
572
590
|
if (!state.enabled || state.status === "failed") return;
|
|
573
591
|
await refreshCounts(ctx);
|
|
@@ -586,17 +604,40 @@ async function safeBoundary(ctx: any, boundary: string): Promise<void> {
|
|
|
586
604
|
}
|
|
587
605
|
if (shouldBuffer(state)) {
|
|
588
606
|
void bufferObservation(ctx, `${boundary}:buffer`).catch(async (error) => {
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
607
|
+
try {
|
|
608
|
+
let innerStale = false;
|
|
609
|
+
try {
|
|
610
|
+
const _ = ctx.cwd;
|
|
611
|
+
} catch {
|
|
612
|
+
innerStale = true;
|
|
613
|
+
}
|
|
614
|
+
if (innerStale) return;
|
|
615
|
+
|
|
616
|
+
const s = await ensureState(ctx);
|
|
617
|
+
s.status = "failed";
|
|
618
|
+
s.lastError = `OM buffer failed: ${errorMessage(error)}`;
|
|
619
|
+
await saveState(s);
|
|
620
|
+
updateStatus(ctx);
|
|
621
|
+
ctx.ui.notify(s.lastError, "error");
|
|
622
|
+
} catch (innerError) {
|
|
623
|
+
// Prevent any uncaught exceptions from background handler
|
|
624
|
+
}
|
|
595
625
|
});
|
|
596
626
|
}
|
|
597
627
|
}
|
|
598
628
|
|
|
599
629
|
async function ensureState(ctx: any): Promise<PiOMRecord> {
|
|
630
|
+
let isStale = false;
|
|
631
|
+
try {
|
|
632
|
+
const _ = ctx.cwd;
|
|
633
|
+
} catch {
|
|
634
|
+
isStale = true;
|
|
635
|
+
}
|
|
636
|
+
if (isStale) {
|
|
637
|
+
if (runtime.state) return runtime.state;
|
|
638
|
+
throw new Error("Cannot ensure state because the extension context is stale.");
|
|
639
|
+
}
|
|
640
|
+
|
|
600
641
|
if (runtime.state && runtime.state.cwd === ctx.cwd && runtime.state.sessionFile === ctx.sessionManager.getSessionFile()) return runtime.state;
|
|
601
642
|
const sessionFile = ctx.sessionManager.getSessionFile?.();
|
|
602
643
|
const sessionId = ctx.sessionManager.getSessionId?.() || (sessionFile ? basename(sessionFile, ".jsonl") : "in-memory");
|
|
@@ -651,6 +692,14 @@ async function saveState(state: PiOMRecord): Promise<void> {
|
|
|
651
692
|
}
|
|
652
693
|
|
|
653
694
|
async function refreshCounts(ctx: any): Promise<void> {
|
|
695
|
+
let isStale = false;
|
|
696
|
+
try {
|
|
697
|
+
const _ = ctx.cwd;
|
|
698
|
+
} catch {
|
|
699
|
+
isStale = true;
|
|
700
|
+
}
|
|
701
|
+
if (isStale) return;
|
|
702
|
+
|
|
654
703
|
const state = await ensureState(ctx);
|
|
655
704
|
const branch = ctx.sessionManager.getBranch() as SessionEntry[];
|
|
656
705
|
const pending = entriesAfter(branch, state.lastObservedEntryId).filter(isMessageLikeEntry);
|
|
@@ -662,11 +711,23 @@ async function refreshCounts(ctx: any): Promise<void> {
|
|
|
662
711
|
function updateStatus(ctx: any): void {
|
|
663
712
|
const state = runtime.state;
|
|
664
713
|
if (!state) return;
|
|
665
|
-
|
|
714
|
+
try {
|
|
715
|
+
ctx.ui.setStatus("om", formatShortStatusColored(state));
|
|
716
|
+
} catch (e) {
|
|
717
|
+
// Context might be stale or UI disposed
|
|
718
|
+
}
|
|
666
719
|
runtime.overlayHandle?.requestRender();
|
|
667
720
|
}
|
|
668
721
|
|
|
669
722
|
async function observeNow(ctx: any, opts: { force: boolean; reason: string; signal?: AbortSignal; manualText?: string }): Promise<void> {
|
|
723
|
+
let isStale = false;
|
|
724
|
+
try {
|
|
725
|
+
const _ = ctx.cwd;
|
|
726
|
+
} catch {
|
|
727
|
+
isStale = true;
|
|
728
|
+
}
|
|
729
|
+
if (isStale) return;
|
|
730
|
+
|
|
670
731
|
const state = await ensureState(ctx);
|
|
671
732
|
assertNoOperation(state);
|
|
672
733
|
const branch = ctx.sessionManager.getBranch() as SessionEntry[];
|
|
@@ -758,6 +819,14 @@ async function bufferObservation(ctx: any, reason: string): Promise<void> {
|
|
|
758
819
|
}
|
|
759
820
|
|
|
760
821
|
async function activateBuffered(ctx: any, reason: string): Promise<void> {
|
|
822
|
+
let isStale = false;
|
|
823
|
+
try {
|
|
824
|
+
const _ = ctx.cwd;
|
|
825
|
+
} catch {
|
|
826
|
+
isStale = true;
|
|
827
|
+
}
|
|
828
|
+
if (isStale) return;
|
|
829
|
+
|
|
761
830
|
const state = await ensureState(ctx);
|
|
762
831
|
if (state.buffered.observations.length === 0) return;
|
|
763
832
|
assertNoOperation(state);
|
|
@@ -794,6 +863,14 @@ async function activateBuffered(ctx: any, reason: string): Promise<void> {
|
|
|
794
863
|
}
|
|
795
864
|
|
|
796
865
|
async function reflectNow(ctx: any, opts: { reason: string; signal?: AbortSignal; manualPrompt?: string }): Promise<void> {
|
|
866
|
+
let isStale = false;
|
|
867
|
+
try {
|
|
868
|
+
const _ = ctx.cwd;
|
|
869
|
+
} catch {
|
|
870
|
+
isStale = true;
|
|
871
|
+
}
|
|
872
|
+
if (isStale) return;
|
|
873
|
+
|
|
797
874
|
const state = await ensureState(ctx);
|
|
798
875
|
assertNoOperation(state);
|
|
799
876
|
if (!state.observations.trim()) throw new Error("No observations to reflect");
|
|
@@ -1320,11 +1397,15 @@ function formatTokens(tokens: number): string {
|
|
|
1320
1397
|
}
|
|
1321
1398
|
|
|
1322
1399
|
async function writeDebug(ctx: any, name: string, payload: unknown): Promise<void> {
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1400
|
+
try {
|
|
1401
|
+
const state = await ensureState(ctx);
|
|
1402
|
+
const dir = runtime.debugDir || join(ctx.cwd, CONFIG_DIR_NAME, "om", "debug");
|
|
1403
|
+
await mkdir(dir, { recursive: true });
|
|
1404
|
+
const file = join(dir, `${new Date().toISOString().replace(/[:.]/g, "-")}-${sanitizeFileName(name)}.json`);
|
|
1405
|
+
await writeFile(file, JSON.stringify({ extension: EXTENSION_ID, sessionId: state.sessionId, ...payload as any }, null, 2) + "\n", "utf8");
|
|
1406
|
+
} catch (error) {
|
|
1407
|
+
// Ignore debug write errors if context is stale
|
|
1408
|
+
}
|
|
1328
1409
|
}
|
|
1329
1410
|
|
|
1330
1411
|
function sanitizeFileName(name: string): string {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-observational-memory-extension",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Mastra-style Observational Memory extension for Pi compaction and runtime context.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Nikita Nosov <20nik.nosov21@gmail.com>",
|