opencode-self-improvement 0.9.0 → 0.9.1
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/package.json +1 -1
- package/src/index.ts +40 -24
- package/src/inject.ts +10 -0
- package/src/status.ts +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-self-improvement",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "Durable memory for opencode: remember facts, decisions, and commands across sessions, plus a one-command per-repo setup (/repo-init).",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"exports": {
|
package/src/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
compactionAnchor,
|
|
37
37
|
contextualPull,
|
|
38
38
|
formatMemory,
|
|
39
|
+
injectedRows,
|
|
39
40
|
selectRelevant,
|
|
40
41
|
storeVersion,
|
|
41
42
|
systemDigest,
|
|
@@ -741,9 +742,10 @@ const impl = {
|
|
|
741
742
|
if (cached?.version === version) return;
|
|
742
743
|
const sel = await selectRelevant(latestPrompt.get(sid) ?? "", 5, mems); // widen candidate pool
|
|
743
744
|
const block = sel.block ? capToBudget(sel.block, budget) : "";
|
|
744
|
-
// Refs the injected rows assert (paths + commands), the join key
|
|
745
|
-
// attribution. Derived
|
|
746
|
-
|
|
745
|
+
// Refs the ACTUALLY-injected rows assert (paths + commands), the join key
|
|
746
|
+
// for attribution. Derived from the budget-surviving rows, not the wider
|
|
747
|
+
// candidate pool — crediting paths the model never saw would overclaim.
|
|
748
|
+
const refs = deriveMemoryRefs(injectedRows(block, sel.selected));
|
|
747
749
|
const at = new Date().toISOString();
|
|
748
750
|
const reason: "first" | "store_changed" = cached ? "store_changed" : "first";
|
|
749
751
|
epoch.set(sid, { version, block, at, reason, refs });
|
|
@@ -877,15 +879,16 @@ const impl = {
|
|
|
877
879
|
}),
|
|
878
880
|
);
|
|
879
881
|
|
|
880
|
-
// ---- measured prompt-cache health (
|
|
881
|
-
// Zero-model by design:
|
|
882
|
-
//
|
|
883
|
-
//
|
|
884
|
-
//
|
|
885
|
-
//
|
|
886
|
-
// the
|
|
887
|
-
//
|
|
888
|
-
//
|
|
882
|
+
// ---- measured prompt-cache health (usage events) -----------------------
|
|
883
|
+
// Zero-model by design: usage arrives on `session.usage.updated` (v2 hosts)
|
|
884
|
+
// or `message.updated` assistant info (older hosts), carrying tokens
|
|
885
|
+
// {input, output, reasoning, cache.read/write}; the session-wide
|
|
886
|
+
// read/(read+input+write) share is the honest receipt behind the
|
|
887
|
+
// byte-stability proxy. Streaming updates arrive repeatedly — overwriting
|
|
888
|
+
// with the latest is correct because the final update carries the complete
|
|
889
|
+
// usage. Flushes the per-session status file so the TUI badge refreshes
|
|
890
|
+
// once per turn instead of per step. Degrades to "not measured" when the
|
|
891
|
+
// host exposes no event stream.
|
|
889
892
|
const measuredController = new AbortController();
|
|
890
893
|
if (ctx.event?.subscribe) {
|
|
891
894
|
const eventApi = ctx.event;
|
|
@@ -912,20 +915,33 @@ const impl = {
|
|
|
912
915
|
else if (typeof sid === "string") void guarded("session bind (api)", () => bindSessionFromApi(sid));
|
|
913
916
|
continue;
|
|
914
917
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
918
|
+
// Token usage: v2 hosts (>=2.0.x) emit `session.usage.updated` with
|
|
919
|
+
// data.{sessionID, tokens}; older hosts carried the same TokenUsage
|
|
920
|
+
// on message.updated's assistant info. Accept both so `measured`
|
|
921
|
+
// populates regardless of host version.
|
|
922
|
+
let usid: unknown;
|
|
923
|
+
let ut: { input?: number; cache?: { read?: number; write?: number } } | undefined;
|
|
924
|
+
if (ev?.type === "session.usage.updated") {
|
|
925
|
+
usid = ev.data?.sessionID;
|
|
926
|
+
ut = ev.data?.tokens;
|
|
927
|
+
} else if (ev?.type === "message.updated") {
|
|
928
|
+
const info = ev.properties?.info ?? ev.info ?? ev.data?.info;
|
|
929
|
+
if (info && info.role === "assistant") {
|
|
930
|
+
usid = info.sessionID;
|
|
931
|
+
ut = info.tokens;
|
|
932
|
+
}
|
|
933
|
+
} else {
|
|
934
|
+
continue;
|
|
935
|
+
}
|
|
936
|
+
if (typeof usid !== "string" || !ut) continue;
|
|
937
|
+
sessions.touch(usid);
|
|
938
|
+
measuredTurns.set(usid, {
|
|
923
939
|
at: new Date().toISOString(),
|
|
924
|
-
input: Number(
|
|
925
|
-
read: Number(
|
|
926
|
-
write: Number(
|
|
940
|
+
input: Number(ut.input) || 0,
|
|
941
|
+
read: Number(ut.cache?.read) || 0,
|
|
942
|
+
write: Number(ut.cache?.write) || 0,
|
|
927
943
|
});
|
|
928
|
-
await guarded("status flush (measured)", () => flushStatus(
|
|
944
|
+
await guarded("status flush (measured)", () => flushStatus(usid));
|
|
929
945
|
}
|
|
930
946
|
} catch {
|
|
931
947
|
// the subscription ended (abort or host error); nothing to do
|
package/src/inject.ts
CHANGED
|
@@ -264,6 +264,16 @@ export async function relevantKnowledge(message: string, limit = 3, rows?: Memor
|
|
|
264
264
|
return (await selectRelevant(message, limit, rows)).block;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
// The candidate rows that ACTUALLY survived capToBudget into `block`, in order.
|
|
268
|
+
// capToBudget drops whole lines, so attribution must key off these — not the
|
|
269
|
+
// pre-budget candidates — or it would credit the model with paths and commands
|
|
270
|
+
// it was never shown. capToBudget keeps whole lines verbatim, so a candidate is
|
|
271
|
+
// present iff its rendered line is a substring of the block.
|
|
272
|
+
export function injectedRows(block: string, candidates: Memory[]): Memory[] {
|
|
273
|
+
if (!block) return [];
|
|
274
|
+
return candidates.filter((m) => block.includes(formatMemory(m)));
|
|
275
|
+
}
|
|
276
|
+
|
|
267
277
|
// Deterministic fingerprint of the rows that can affect injection. Hash the
|
|
268
278
|
// exact rendered row plus its full id, in load order: content edits, blocklist
|
|
269
279
|
// changes and supersession all invalidate an epoch, while evidence, hidden
|
package/src/status.ts
CHANGED
|
@@ -55,10 +55,10 @@ export function emptyStats(): SessionStats {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
* Measured prompt-cache usage from the last
|
|
59
|
-
* (message.updated
|
|
60
|
-
* SESSION-WIDE prompt cache — the
|
|
61
|
-
* never billed as our block's own rate.
|
|
58
|
+
* Measured prompt-cache usage from the last usage event — `session.usage.updated`
|
|
59
|
+
* (v2 hosts) or `message.updated` assistant info (older hosts) — carrying tokens
|
|
60
|
+
* {input, cache.read, cache.write}. This is the SESSION-WIDE prompt cache — the
|
|
61
|
+
* honest receipt for "are we busting cache", never billed as our block's own rate.
|
|
62
62
|
*/
|
|
63
63
|
export interface Measured {
|
|
64
64
|
at: string;
|