Skip to content
Closed
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
36 changes: 30 additions & 6 deletions LifeOS/install/LIFEOS/PULSE/Observability/observability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,14 @@ function readJsonlByteTail(filePath: string, maxBytes: number): any[] {
const size = statSync(filePath).size
const start = Math.max(0, size - maxBytes)
const fd = openSync(filePath, "r")
const buf = Buffer.alloc(size - start)
readSync(fd, buf, 0, buf.length, start)
closeSync(fd)
let text = buf.toString("utf-8")
let text: string
try {
const buf = Buffer.alloc(size - start)
readSync(fd, buf, 0, buf.length, start)
text = buf.toString("utf-8")
} finally {
closeSync(fd)
}
if (start > 0) {
const nl = text.indexOf("\n")
text = nl === -1 ? "" : text.slice(nl + 1)
Expand Down Expand Up @@ -281,6 +285,21 @@ function foldClimbLine(map: Map<string, ClimbPoint[]>, line: string): void {
map.set(ev.slug, points)
}

// Key-count eviction. The per-key series are ring-buffered (200 points / 600
// ticks), but the number of KEYS was unbounded: every slug and every session_id
// the daemon ever folded stayed resident for its lifetime. Nothing downstream
// reads a series whose newest point is older than the stalest run window
// (RUN_ACTIVITY.nativeStaleMs, 4h), so a day of retention is generous. Called
// on the fold path only — an unchanged log returns early and grows nothing.
const CACHE_RETENTION_MS = 24 * 60 * 60 * 1000

function evictStaleSeries<T extends { ts: number }>(map: Map<string, T[]>, now: number): void {
for (const [key, series] of map) {
const last = series[series.length - 1]
if (!last || now - last.ts > CACHE_RETENTION_MS) map.delete(key)
}
}

export function getClimbData(): Map<string, ClimbPoint[]> {
try {
if (!existsSync(WORK_EVENTS_PATH)) return new Map()
Expand Down Expand Up @@ -310,6 +329,7 @@ export function getClimbData(): Map<string, ClimbPoint[]> {
climbCache.offset += Buffer.byteLength(complete, "utf-8") + 1

for (const line of complete.split("\n")) foldClimbLine(climbCache.data, line)
evictStaleSeries(climbCache.data, Date.now())
return climbCache.data
} catch {
return climbCache.data
Expand Down Expand Up @@ -440,6 +460,7 @@ export function getActivityData(): Map<string, ToolTick[]> {
const remainder = chunk.slice(lastNewline + 1)
activityCache.offset = st.size - Buffer.byteLength(remainder, "utf-8")
for (const line of chunk.slice(0, lastNewline).split("\n")) foldActivityLine(line)
evictStaleSeries(activityCache.bySession, Date.now())
return activityCache.bySession
} catch {
return activityCache.bySession
Expand Down Expand Up @@ -3938,7 +3959,7 @@ function buildTeamFromUserFiles(appIds: string[]): OverviewTeam[] | null {
if (principal) {
team.push({
id: "T0", name: principal, role: "Principal", kind: "human", owns: [],
avatar: principal.charAt(0), note: "Sets direction. Everything here serves his TELOS.",
avatar: principal.charAt(0), note: "Sets direction. Everything here serves the principal's TELOS.",
})
}
const da = nameFrom(readMd(join(USER_DIR, "DIGITAL_ASSISTANT", "DA_IDENTITY.md")))
Expand Down Expand Up @@ -4213,7 +4234,10 @@ function handleLifeCardApi(): Response {
// arm on a fresh install: the "run /interview" onboarding banner never rendered,
// and Pulse presented the fabricated sample mission and goals to the new user as
// if they were their own. Placeholder entries do not count as personalization.
const SAMPLE_ENTRY_RE = /^\**\s*\(sample\)/i
// \b not literal ")": the scaffold also writes "(sample — …)" variants (e.g.
// MISSION.md M2), which a literal match let through. Same fix as the sibling
// filter in LIFEOS/TOOLS/GenerateTelosSummary.ts.
const SAMPLE_ENTRY_RE = /^\**\s*\(sample\b/i

function telosPersonalized(): boolean {
try {
Expand Down