@kanadego/dsh-heartbeat 1.5.0

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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +192 -0
  3. package/assets/frontwin.ps1 +44 -0
  4. package/assets/idle.ps1 +27 -0
  5. package/assets/notify.ps1 +69 -0
  6. package/assets/presets/heartbeat/agent.cordis.yml +66 -0
  7. package/assets/presets/heartbeat/preset.yml +2 -0
  8. package/assets/screenpulse.ps1 +168 -0
  9. package/assets/vault.ps1 +52 -0
  10. package/client.js +428 -0
  11. package/config/busy-rules.json +63 -0
  12. package/config/interests.json +30 -0
  13. package/config/policy.json +57 -0
  14. package/config/profile-schema.json +68 -0
  15. package/config/watchlist.json +9 -0
  16. package/cordis.patch.yml +14 -0
  17. package/dist/bindings-XPPSKILN.js +19 -0
  18. package/dist/bindings-XPPSKILN.js.map +1 -0
  19. package/dist/chunk-2M35HRL6.js +1207 -0
  20. package/dist/chunk-2M35HRL6.js.map +1 -0
  21. package/dist/chunk-4UE74TUB.js +98 -0
  22. package/dist/chunk-4UE74TUB.js.map +1 -0
  23. package/dist/chunk-AISZRA4C.js +235 -0
  24. package/dist/chunk-AISZRA4C.js.map +1 -0
  25. package/dist/chunk-J6ZTRFFW.js +64 -0
  26. package/dist/chunk-J6ZTRFFW.js.map +1 -0
  27. package/dist/chunk-LLD7LUNN.js +202 -0
  28. package/dist/chunk-LLD7LUNN.js.map +1 -0
  29. package/dist/chunk-S7PTR42P.js +19 -0
  30. package/dist/chunk-S7PTR42P.js.map +1 -0
  31. package/dist/cli/index.js +589 -0
  32. package/dist/cli/index.js.map +1 -0
  33. package/dist/inbox-MMLHISQV.js +22 -0
  34. package/dist/inbox-MMLHISQV.js.map +1 -0
  35. package/dist/index.js +2745 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/lib-FJP7J4T6.js +2281 -0
  38. package/dist/lib-FJP7J4T6.js.map +1 -0
  39. package/dist/runtime-J5NOPRBA.js +11 -0
  40. package/dist/runtime-J5NOPRBA.js.map +1 -0
  41. package/package.json +61 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/path-guard.ts","../src/core/audit-log.ts","../src/core/atomic-fs.ts","../src/config/schema.ts","../src/config/load.ts","../src/seeds/pool.ts","../src/seeds/types.ts","../src/ledger/ledger.ts","../src/browse/browse.ts","../src/profile/store.ts","../src/profile/types.ts","../src/profile/schema.ts","../src/notify/notify.ts","../src/core/preset-install.ts"],"sourcesContent":["// Path whitelist guard (requirement 8 / design doc §10.2).\n//\n// Every fs write/delete must have its target canonicalized first, then be\n// checked against the canonical workspace prefix with a separator boundary.\n// A raw startsWith check is bypassable via \"..\", symlinks/junctions, and\n// case variants; canonical realpath closes all of those on Windows.\n\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nexport class PathOutsideWorkspaceError extends Error {\n constructor(target: string, workspace: string) {\n super(`path outside workspace: \"${target}\" (workspace: \"${workspace}\")`);\n this.name = 'PathOutsideWorkspaceError';\n }\n}\n\n/**\n * Resolve to an absolute canonical path. For targets that do not exist yet,\n * realpath the deepest existing ancestor and re-append the virtual tail, so\n * planned files under the workspace validate while `..` escapes still resolve\n * through the real filesystem.\n */\nexport function canonicalize(target: string): string {\n const abs = path.resolve(target);\n try {\n return fs.realpathSync(abs);\n } catch {\n // Walk up from the missing leaf, recording each missing component, until\n // an existing ancestor is found; re-append the missing tail afterwards.\n const tail: string[] = [];\n let dir = abs;\n for (;;) {\n const base = path.basename(dir);\n const parent = path.dirname(dir);\n if (parent === dir) {\n throw new Error(`cannot canonicalize \"${target}\": no existing ancestor`);\n }\n tail.push(base);\n dir = parent;\n try {\n const realDir = fs.realpathSync(dir);\n return path.join(realDir, ...tail.reverse());\n } catch {\n continue;\n }\n }\n }\n}\n\n/**\n * True when `targetCanon` equals the workspace dir or lies under it.\n * Comparison is case-insensitive (Windows filesystems) and requires a\n * separator boundary so `D:\\ws-data-evil` does not match workspace `D:\\ws-data`.\n */\nexport function isInsideWorkspace(workspaceCanon: string, targetCanon: string): boolean {\n const norm = (p: string) => {\n let n = path.normalize(p).toLowerCase();\n if (!n.endsWith(path.sep)) n += path.sep;\n return n;\n };\n const w = norm(workspaceCanon);\n const t = norm(targetCanon);\n return t === w || t.startsWith(w);\n}\n\nexport interface PathGuard {\n /** Canonical workspace boundary. */\n readonly workspace: string;\n /** Canonicalize then validate; returns the canonical path or throws. */\n assert(target: string): string;\n /** Canonicalize then validate; returns null instead of throwing. */\n check(target: string): string | null;\n}\n\nexport function createPathGuard(workspaceDir: string): PathGuard {\n const workspace = canonicalize(workspaceDir);\n const guard: PathGuard = {\n workspace,\n check(target: string): string | null {\n const canon = canonicalize(target);\n return isInsideWorkspace(workspace, canon) ? canon : null;\n },\n assert(target: string): string {\n const canon = guard.check(target);\n if (canon === null) throw new PathOutsideWorkspaceError(target, workspace);\n return canon;\n },\n };\n return guard;\n}\n","// Append-only JSONL audit log with age-based retention pruning.\n// Audit files are plaintext by charter (transparency), and must never contain\n// sensitive raw observations (window titles, conversation text).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { atomicWriteFileSync } from './atomic-fs.js';\n\nexport interface AuditEvent {\n ts: string;\n [key: string]: unknown;\n}\n\nexport function appendAuditLine(file: string, event: Omit<AuditEvent, 'ts'> & { ts?: string }): void {\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const line = JSON.stringify({ ts: event.ts ?? new Date().toISOString(), ...event });\n fs.appendFileSync(file, line + '\\n', 'utf8');\n}\n\nexport function readAuditLines<T = AuditEvent>(file: string): T[] {\n if (!fs.existsSync(file)) return [];\n const out: T[] = [];\n const raw = fs.readFileSync(file, 'utf8');\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n out.push(JSON.parse(trimmed) as T);\n } catch {\n // Skip corrupt lines; the log is diagnostic, not authoritative data.\n out.push({ ts: '', corrupt: true, raw: trimmed.slice(0, 200) } as unknown as T);\n }\n }\n return out;\n}\n\n/**\n * Drop entries older than maxAgeMs. Returns the number of removed lines.\n * Rewrites the file atomically; on rewrite failure the original is untouched.\n */\nexport function pruneAuditFile(file: string, maxAgeMs: number, now = Date.now()): number {\n if (!fs.existsSync(file)) return 0;\n const lines = readAuditLines(file);\n const kept = lines.filter((e) => {\n const ev = e as unknown as AuditEvent;\n const ts = Date.parse(ev.ts ?? '');\n if (!Number.isFinite(ts)) return true; // keep unparseable lines, never lose audit data silently\n return now - ts <= maxAgeMs;\n });\n const removed = lines.length - kept.length;\n if (removed === 0) return 0;\n const body = kept.map((e) => JSON.stringify(e)).join('\\n');\n atomicWriteFileSync(file, body ? body + '\\n' : '');\n return removed;\n}\n","// Crash-safe file replacement: write to a random-suffix temp file in the SAME\n// directory as the target, then rename over it. Same-directory rename stays\n// on one volume (atomic) and Node's rename replaces existing files on Windows.\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID, randomFillSync } from 'node:crypto';\n\nexport function tmpSibling(target: string, tag = 'w'): string {\n return path.join(\n path.dirname(target),\n `.${path.basename(target)}.${tag}-${randomUUID().slice(0, 8)}.tmp`,\n );\n}\n\nexport function atomicWriteFileSync(target: string, data: string | Uint8Array): void {\n const tmp = tmpSibling(target);\n try {\n fs.writeFileSync(tmp, data);\n fs.renameSync(tmp, target);\n } finally {\n fs.rmSync(tmp, { force: true });\n }\n}\n\nexport function atomicWriteJsonSync(target: string, value: unknown): void {\n atomicWriteFileSync(target, JSON.stringify(value, null, 2));\n}\n\n/** Overwrite a file's bytes with random data before unlinking. */\nexport function shredFileSync(target: string, passes = 3): void {\n const stat = fs.statSync(target);\n if (!stat.isFile()) throw new Error(`shred: not a file: ${target}`);\n const buf = Buffer.alloc(Math.max(stat.size, 1));\n for (let i = 0; i < passes; i++) {\n randomFillSync(buf);\n fs.writeFileSync(target, buf);\n }\n fs.rmSync(target, { force: true });\n}\n","// Policy shape + runtime validation. Factory defaults live in config/policy.json\n// (read-only); the user layer in data/settings/policy.json overrides via deep\n// merge (design doc §13, D3).\n\nexport interface QuietHours {\n start: string; // \"HH:MM\"\n end: string; // \"HH:MM\"\n}\n\nexport interface BrowseWindow {\n start: string;\n end: string;\n}\n\nexport interface Policy {\n heartbeat: { intervalMin: number };\n gate: {\n maxDailySend: number;\n cooldownMinutes: number;\n quietHours: QuietHours;\n };\n browse: {\n windows: BrowseWindow[];\n minIntervalHours: number;\n maxSeedsPerVisit: number;\n };\n seeds: {\n maxActive: number;\n ttlDays: { news: number; fandom: number; scene: number; promise: number };\n coldBenchDays: number;\n retireAfterUsed: number;\n scoreWeights: { freshness: number; unused: number; confidence: number };\n };\n profile: {\n consolidation: { minIntervalHours: number; inboxBacklog: number };\n partitionCap: number;\n maxOpsPerRun: number;\n confidenceCap: { chat: number; screen: number; browse: number };\n volatileDays: number;\n stableLowActivityDays: number;\n psyEnabled: boolean;\n };\n retention: { envPulseHours: number; decisionLogDays: number };\n}\n\nconst HHMM = /^([01]\\d|2[0-3]):[0-5]\\d$/;\n\nfunction isPlainObject(v: unknown): v is Record<string, unknown> {\n return typeof v === 'object' && v !== null && !Array.isArray(v);\n}\n\nfunction fail(msg: string): never {\n throw new Error(`policy: ${msg}`);\n}\n\nexport function assertPolicy(input: unknown): asserts input is Policy {\n if (!isPlainObject(input)) fail('root must be an object');\n const p = input;\n const hb = p.heartbeat;\n if (!isPlainObject(hb)) fail('heartbeat missing');\n if (typeof hb.intervalMin !== 'number' || hb.intervalMin < 1 || hb.intervalMin > 1440) {\n fail('heartbeat.intervalMin must be a number in [1, 1440]');\n }\n const g = p.gate;\n if (!isPlainObject(g)) fail('gate missing');\n if (typeof g.maxDailySend !== 'number' || g.maxDailySend < 0) fail('gate.maxDailySend must be >= 0');\n if (typeof g.cooldownMinutes !== 'number' || g.cooldownMinutes < 0) fail('gate.cooldownMinutes must be >= 0');\n const qh = g.quietHours;\n if (!isPlainObject(qh)) fail('gate.quietHours missing');\n if (typeof qh.start !== 'string' || !HHMM.test(qh.start) || typeof qh.end !== 'string' || !HHMM.test(qh.end)) {\n fail('gate.quietHours must be {start:\"HH:MM\", end:\"HH:MM\"}');\n }\n const b = p.browse;\n if (!isPlainObject(b)) fail('browse missing');\n if (!Array.isArray(b.windows) || b.windows.length === 0) fail('browse.windows must be a non-empty array');\n for (const w of b.windows) {\n if (!isPlainObject(w)) fail('browse.windows entries must be objects');\n if (typeof w.start !== 'string' || !HHMM.test(w.start) || typeof w.end !== 'string' || !HHMM.test(w.end)) {\n fail('browse.windows entries must be {start:\"HH:MM\", end:\"HH:MM\"}');\n }\n }\n if (typeof b.minIntervalHours !== 'number' || b.minIntervalHours <= 0) fail('browse.minIntervalHours must be > 0');\n if (typeof b.maxSeedsPerVisit !== 'number' || b.maxSeedsPerVisit < 1) fail('browse.maxSeedsPerVisit must be >= 1');\n const s = p.seeds;\n if (!isPlainObject(s)) fail('seeds missing');\n if (typeof s.maxActive !== 'number' || s.maxActive < 1) fail('seeds.maxActive must be >= 1');\n if (!isPlainObject(s.ttlDays)) fail('seeds.ttlDays missing');\n for (const k of ['news', 'fandom', 'scene', 'promise'] as const) {\n if (typeof s.ttlDays[k] !== 'number') fail(`seeds.ttlDays.${k} missing`);\n }\n if (typeof s.coldBenchDays !== 'number') fail('seeds.coldBenchDays missing');\n if (typeof s.retireAfterUsed !== 'number' || s.retireAfterUsed < 1) fail('seeds.retireAfterUsed must be >= 1');\n if (!isPlainObject(s.scoreWeights)) fail('seeds.scoreWeights missing');\n const pr = p.profile;\n if (!isPlainObject(pr)) fail('profile missing');\n const c = pr.consolidation;\n if (!isPlainObject(c)) fail('profile.consolidation missing');\n if (typeof c.minIntervalHours !== 'number' || typeof c.inboxBacklog !== 'number') fail('profile.consolidation fields missing');\n if (typeof pr.partitionCap !== 'number' || pr.partitionCap < 1) fail('profile.partitionCap must be >= 1');\n if (typeof pr.maxOpsPerRun !== 'number' || pr.maxOpsPerRun < 1) fail('profile.maxOpsPerRun must be >= 1');\n const cc = pr.confidenceCap;\n if (!isPlainObject(cc)) fail('profile.confidenceCap missing');\n if (typeof cc.chat !== 'number' || typeof cc.screen !== 'number' || typeof cc.browse !== 'number') {\n fail('profile.confidenceCap fields missing');\n }\n if (typeof pr.volatileDays !== 'number' || pr.volatileDays < 1) fail('profile.volatileDays must be >= 1');\n if (typeof pr.stableLowActivityDays !== 'number' || pr.stableLowActivityDays < 1) fail('profile.stableLowActivityDays must be >= 1');\n if (typeof pr.psyEnabled !== 'boolean') fail('profile.psyEnabled must be boolean');\n const r = p.retention;\n if (!isPlainObject(r)) fail('retention missing');\n if (typeof r.envPulseHours !== 'number' || typeof r.decisionLogDays !== 'number') fail('retention fields missing');\n}\n\n/** Recursive merge: user values win; objects merge, arrays and scalars replace. */\nexport function deepMerge<T>(base: T, override: unknown): T {\n if (!isPlainObject(base) || !isPlainObject(override)) {\n return (override === undefined ? base : (override as T));\n }\n const out: Record<string, unknown> = { ...base };\n for (const [k, v] of Object.entries(override)) {\n out[k] = v === undefined ? (base as Record<string, unknown>)[k] : deepMerge((base as Record<string, unknown>)[k], v);\n }\n return out as T;\n}\n","// Two-layer policy loading (D3): factory defaults (config/policy.json, read-only)\n// + user layer (data/settings/policy.json). Missing user layer is normal; an\n// invalid factory file or user layer is fail-closed (throw at startup).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { assertPolicy, deepMerge, type Policy } from './schema.js';\n\nexport const USER_POLICY_FILE = 'policy.json';\n\nexport function loadPolicy(guard: PathGuard, configDir: string, settingsDir: string): Policy {\n const factoryPath = path.join(configDir, 'policy.json');\n let factoryRaw: unknown;\n try {\n factoryRaw = JSON.parse(fs.readFileSync(factoryPath, 'utf8'));\n } catch (e) {\n throw new Error(`factory policy unreadable at ${factoryPath}: ${String(e)}`);\n }\n assertPolicy(factoryRaw);\n\n const userPath = guard.assert(path.join(settingsDir, USER_POLICY_FILE));\n let merged: Policy = factoryRaw;\n if (fs.existsSync(userPath)) {\n try {\n const userRaw: unknown = JSON.parse(fs.readFileSync(userPath, 'utf8'));\n merged = deepMerge(factoryRaw, userRaw);\n } catch (e) {\n throw new Error(`user policy layer unparseable at ${userPath}: ${String(e)}`);\n }\n }\n assertPolicy(merged);\n return merged;\n}\n","// Material pool engine (design doc §4). All four eviction rules are\n// deterministic code - no LLM involvement (§4.2). Storage: data/seeds.jsonl,\n// one seed per JSON line, DPAPI-encrypted at rest (D14).\n\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { loadEncryptedText, saveEncryptedText } from '../vault/vault.js';\nimport type { Policy } from '../config/schema.js';\nimport {\n SEED_SOURCE_DEFAULT_CONFIDENCE,\n emptySeedDb,\n type Seed,\n type SeedDb,\n type SeedRetireReason,\n type SeedSource,\n type SeedTag,\n} from './types.js';\n\nconst DAY_MS = 86_400_000;\n\nexport interface AddSeedInput {\n text: string;\n topic?: string;\n tag?: SeedTag;\n source?: SeedSource;\n confidence?: number;\n}\n\nexport interface AddSeedResult {\n kind: 'added' | 'merged' | 'duplicate';\n seed: Seed;\n evicted?: Seed;\n}\n\nexport interface GcReport {\n consumed: number;\n expired: number;\n coldBench: number;\n activeAfter: number;\n}\n\nexport const TTL_KEYS: readonly SeedTag[] = ['news', 'fandom', 'scene', 'promise'];\n\nexport function normalizeTag(tag: string | undefined): SeedTag {\n if (tag && (TTL_KEYS as readonly string[]).includes(tag)) return tag as SeedTag;\n return 'scene';\n}\n\nfunction parseIso(v: string): number {\n const t = Date.parse(v);\n return Number.isFinite(t) ? t : 0;\n}\n\n// ── storage ─────────────────────────────────────────────────────────────\n\nexport function seedsFilePath(dataDir: string): string {\n return path.join(dataDir, 'seeds.jsonl');\n}\n\nexport function loadPool(guard: PathGuard, file: string): SeedDb {\n const raw = loadEncryptedText(guard, file);\n if (raw === null) return emptySeedDb();\n const db = emptySeedDb();\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const obj = JSON.parse(trimmed) as Seed;\n if (typeof obj.id === 'string' && obj.id.startsWith('s')) {\n db.seeds.push(obj);\n const n = Number(obj.id.slice(1));\n if (Number.isFinite(n) && n > db.seq) db.seq = n;\n }\n } catch {\n // corrupt line: skip (pool is a cache; journal-level audit lives elsewhere)\n }\n }\n return db;\n}\n\nexport function savePool(guard: PathGuard, file: string, db: SeedDb): void {\n const body = db.seeds.map((s) => JSON.stringify(s)).join('\\n');\n saveEncryptedText(guard, file, body ? body + '\\n' : '');\n}\n\n// ── queries ─────────────────────────────────────────────────────────────\n\nexport const activeSeeds = (db: SeedDb): Seed[] => db.seeds.filter((s) => s.status === 'active');\nexport const archivedSeeds = (db: SeedDb): Seed[] => db.seeds.filter((s) => s.status === 'archived');\n\n// ── eviction scoring (rule 4) ───────────────────────────────────────────\n\n/**\n * Comprehensive score (§4.2): freshness x0.4 + unused x0.3 + confidence x0.3.\n * Higher = keep. Lowest-scoring active is evicted when the pool is full.\n */\nexport function evictionScore(seed: Seed, policy: Policy, now: number): number {\n const ttlDays = policy.seeds.ttlDays[seed.tag] || 14;\n const daysStale = Math.max(0, (now - parseIso(seed.lastEvidenceAt)) / DAY_MS);\n const freshness = Math.max(0, 1 - daysStale / ttlDays);\n const unused = seed.used === 0 ? 1 : 1 / seed.used;\n const w = policy.seeds.scoreWeights;\n return freshness * w.freshness + unused * w.unused + seed.confidence * w.confidence;\n}\n\n/** Pick the eviction victim: unprotected actives first (§4.2 加权保护). */\nexport function pickEvictionVictim(db: SeedDb, policy: Policy, now: number): Seed | null {\n const actives = activeSeeds(db);\n if (actives.length === 0) return null;\n const unprotected = actives.filter((s) => !s.protected);\n const candidates = unprotected.length > 0 ? unprotected : actives;\n let worst: Seed | null = null;\n let worstScore = Number.POSITIVE_INFINITY;\n for (const s of candidates) {\n const score = evictionScore(s, policy, now);\n if (score < worstScore) {\n worstScore = score;\n worst = s;\n }\n }\n return worst;\n}\n\nfunction archiveSeed(seed: Seed, reason: SeedRetireReason, now: number): void {\n seed.status = 'archived';\n seed.retireReason = reason;\n seed.retiredAt = new Date(now).toISOString();\n}\n\n// ── operations ──────────────────────────────────────────────────────────\n\nexport function addSeed(\n guard: PathGuard,\n file: string,\n policy: Policy,\n input: AddSeedInput,\n now = Date.now(),\n): AddSeedResult {\n const db = loadPool(guard, file);\n const text = input.text.trim();\n if (!text) throw new Error('seed text must not be empty');\n const tag = normalizeTag(input.tag);\n const source: SeedSource = input.source ?? 'chat';\n const confidence = input.confidence ?? SEED_SOURCE_DEFAULT_CONFIDENCE[source];\n\n // Exact-text dedup against actives.\n const dup = activeSeeds(db).find((s) => s.text === text);\n if (dup) return { kind: 'duplicate', seed: dup };\n\n const nowIso = new Date(now).toISOString();\n const ttlMs = (policy.seeds.ttlDays[tag] || 14) * DAY_MS;\n\n // Same-topic merge (§4.3): collapse all same-topic actives + the new item.\n const sameTopic = activeSeeds(db).filter((s) => s.topic === (input.topic ?? text.slice(0, 24)));\n let seed: Seed;\n if (sameTopic.length > 0) {\n const kept = sameTopic[0]!;\n kept.text = text; // brief takes the latest\n kept.tag = tag;\n kept.source = source;\n kept.confidence = Math.max(kept.confidence, confidence);\n kept.used = sameTopic.reduce((acc, s) => acc + s.used, 0); // used accumulates\n kept.lastEvidenceAt = [kept.lastEvidenceAt, nowIso, ...sameTopic.map((s) => s.lastEvidenceAt)]\n .reduce((a, b) => (parseIso(b) > parseIso(a) ? b : a));\n kept.expiresAt = new Date(Math.max(parseIso(kept.expiresAt), now + ttlMs)).toISOString();\n kept.protected = kept.protected || source === 'hand' || (source === 'profile' && confidence >= 0.7);\n // remove the other same-topic actives (merged into kept)\n for (const extra of sameTopic.slice(1)) {\n extra.status = 'archived';\n extra.retireReason = 'completed';\n extra.retiredAt = nowIso;\n }\n seed = kept;\n // capacity still applies after growth check below if kept is somehow over cap\n if (activeSeeds(db).length > policy.seeds.maxActive) {\n const victim = pickEvictionVictim(db, policy, now);\n if (victim && victim.id !== kept.id) {\n archiveSeed(victim, 'pool_cap', now);\n savePool(guard, file, db);\n return { kind: 'merged', seed: kept, evicted: victim };\n }\n }\n savePool(guard, file, db);\n return { kind: 'merged', seed: kept };\n }\n\n // Capacity first (rule 4): evict before inserting when full.\n let evicted: Seed | undefined;\n if (activeSeeds(db).length >= policy.seeds.maxActive) {\n const victim = pickEvictionVictim(db, policy, now);\n if (victim) {\n archiveSeed(victim, 'pool_cap', now);\n evicted = victim;\n }\n }\n\n db.seq += 1;\n seed = {\n id: `s${db.seq}`,\n text,\n topic: input.topic ?? text.slice(0, 24),\n tag,\n source,\n confidence,\n protected: source === 'hand' || (source === 'profile' && confidence >= 0.7),\n used: 0,\n bornAt: nowIso,\n expiresAt: new Date(now + ttlMs).toISOString(),\n lastUsedAt: null,\n lastEvidenceAt: nowIso,\n status: 'active',\n };\n db.seeds.push(seed);\n savePool(guard, file, db);\n return { kind: 'added', seed, evicted };\n}\n\n/**\n * Deterministic gc (rules 1-3 of §4.2). Rule 4 (pool cap) fires on add only.\n */\nexport function gcPool(guard: PathGuard, file: string, policy: Policy, now = Date.now()): GcReport {\n const db = loadPool(guard, file);\n const report: GcReport = { consumed: 0, expired: 0, coldBench: 0, activeAfter: 0 };\n for (const s of activeSeeds(db)) {\n const ageMs = now - parseIso(s.bornAt);\n const sinceEvidence = parseIso(s.lastEvidenceAt);\n const sinceUsed = s.lastUsedAt ? parseIso(s.lastUsedAt) : 0;\n if (s.used >= policy.seeds.retireAfterUsed && sinceEvidence <= sinceUsed) {\n archiveSeed(s, 'consumed', now);\n report.consumed += 1;\n } else if (now > parseIso(s.expiresAt)) {\n archiveSeed(s, 'expired', now);\n report.expired += 1;\n } else if (s.used === 0 && ageMs >= policy.seeds.coldBenchDays * DAY_MS) {\n archiveSeed(s, 'cold_bench', now);\n report.coldBench += 1;\n }\n }\n report.activeAfter = activeSeeds(db).length;\n savePool(guard, file, db);\n return report;\n}\n\n/** The seed surfaced in a real expression: count + maybe retire (rule 1). */\nexport function surfaceSeed(\n guard: PathGuard,\n file: string,\n policy: Policy,\n id: string,\n now = Date.now(),\n): Seed | null {\n const db = loadPool(guard, file);\n const s = db.seeds.find((x) => x.id === id && x.status === 'active');\n if (!s) return null;\n s.used += 1;\n s.lastUsedAt = new Date(now).toISOString();\n if (s.used >= policy.seeds.retireAfterUsed && parseIso(s.lastEvidenceAt) <= parseIso(s.lastUsedAt)) {\n archiveSeed(s, 'consumed', now);\n }\n savePool(guard, file, db);\n return s;\n}\n\n/** Deliberate retirement (item completed / no longer relevant). */\nexport function archiveSeedById(\n guard: PathGuard,\n file: string,\n id: string,\n reason: SeedRetireReason = 'completed',\n now = Date.now(),\n): Seed | null {\n const db = loadPool(guard, file);\n const s = db.seeds.find((x) => x.id === id && x.status === 'active');\n if (!s) return null;\n archiveSeed(s, reason, now);\n savePool(guard, file, db);\n return s;\n}\n\n/** Restore an archived seed to the active pool (UI operation; resets TTL). */\nexport function restoreSeed(\n guard: PathGuard,\n file: string,\n policy: Policy,\n id: string,\n now = Date.now(),\n): { ok: true; seed: Seed } | { ok: false; reason: string } {\n const db = loadPool(guard, file);\n const s = db.seeds.find((x) => x.id === id && x.status === 'archived');\n if (!s) return { ok: false, reason: 'archived seed not found' };\n if (activeSeeds(db).length >= policy.seeds.maxActive) {\n return { ok: false, reason: `pool full (${policy.seeds.maxActive}); archive something first` };\n }\n s.status = 'active';\n s.retireReason = undefined;\n s.retiredAt = undefined;\n s.expiresAt = new Date(now + (policy.seeds.ttlDays[s.tag] || 14) * DAY_MS).toISOString();\n s.lastEvidenceAt = new Date(now).toISOString();\n savePool(guard, file, db);\n return { ok: true, seed: s };\n}\n\n/** Hard delete (UI explicit action with confirm; audit lives in the CLI/log). */\nexport function deleteSeed(guard: PathGuard, file: string, id: string): boolean {\n const db = loadPool(guard, file);\n const before = db.seeds.length;\n db.seeds = db.seeds.filter((x) => x.id !== id);\n if (db.seeds.length === before) return false;\n savePool(guard, file, db);\n return true;\n}\n","// Material pool (\"素材池\") types. Charter (axiom 3): this pool is a CACHE,\n// not memory - volatile, evictable, burnable; long-term memory only absorbs\n// conversation-validated content. Everything here may be destroyed.\n\nexport type SeedTag = 'news' | 'fandom' | 'scene' | 'promise';\nexport type SeedSource = 'chat' | 'screen' | 'browse' | 'hand' | 'profile';\nexport type SeedRetireReason = 'consumed' | 'expired' | 'cold_bench' | 'pool_cap' | 'completed';\n\nexport interface Seed {\n id: string;\n /** Short brief shown to the reflection prompt. */\n text: string;\n /** Merge key (§4.3): same-topic actives collapse into one. */\n topic: string;\n tag: SeedTag;\n source: SeedSource;\n /** 0..1 trust level; handwriting and high-confidence profile sources gain eviction protection. */\n confidence: number;\n /** Eviction protection (§4.2 pool-cap rule): handwritten / high-confidence profile sources. */\n protected: boolean;\n /** Times this seed surfaced in an actual expression. */\n used: number;\n bornAt: string;\n /** bornAt + ttlDays[tag]; archived when passed (rule 2). */\n expiresAt: string;\n lastUsedAt: string | null;\n /** Fresh-evidence watermark: rule 1 spares seeds with evidence newer than last use. */\n lastEvidenceAt: string;\n status: 'active' | 'archived';\n retireReason?: SeedRetireReason;\n retiredAt?: string;\n}\n\nexport interface SeedDb {\n seq: number;\n seeds: Seed[];\n}\n\nexport function emptySeedDb(): SeedDb {\n return { seq: 0, seeds: [] };\n}\n\nexport const SEED_SOURCE_DEFAULT_CONFIDENCE: Record<SeedSource, number> = {\n hand: 1.0,\n profile: 0.7,\n chat: 0.6,\n screen: 0.4,\n browse: 0.4,\n};\n","// Ledger (\"账本\") - the ONE shared ledger (design doc §5, requirement 5).\n// Human-readable Markdown by charter; the user may edit it by hand, so the\n// parser is tolerant: unknown lines are preserved verbatim on rewrite.\n\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { readText, writeText } from '../vault/vault.js';\n\nconst DAY_MS = 86_400_000;\n\nexport interface LedgerEntry {\n id: string;\n date: string; // \"YYYY-MM-DD\"\n time: string; // \"HH:MM\"\n status: 'open' | 'done';\n text: string;\n}\n\nexport function ledgerFilePath(dataDir: string): string {\n return path.join(dataDir, 'ledger.md');\n}\n\nconst LINE_RE = /^- \\[(\\d{4}-\\d{2}-\\d{2}) (\\d{2}:\\d{2})\\]\\[(open|done)\\]\\[#([0-9a-f]{6})\\] (.*)$/;\n\nfunction renderEntry(e: LedgerEntry): string {\n return `- [${e.date} ${e.time}][${e.status}][#${e.id}] ${e.text}`;\n}\n\nexport function readLedger(guard: PathGuard, file: string): { header: string; entries: LedgerEntry[]; rawLines: string[] } {\n const raw = readText(guard, file, '# 账本\\n');\n const lines = raw.split('\\n');\n const entries: LedgerEntry[] = [];\n const rawLines: string[] = [];\n for (const line of lines) {\n const m = LINE_RE.exec(line);\n if (m) {\n entries.push({ date: m[1]!, time: m[2]!, status: m[3] as 'open' | 'done', id: m[4]!, text: m[5]! });\n }\n rawLines.push(line);\n }\n return { header: lines[0] ?? '# 账本', entries, rawLines };\n}\n\nexport function appendEntry(guard: PathGuard, file: string, text: string, now = Date.now()): LedgerEntry {\n const textTrimmed = text.trim();\n if (!textTrimmed) throw new Error('ledger entry must not be empty');\n const d = new Date(now);\n const pad = (n: number) => String(n).padStart(2, '0');\n const entry: LedgerEntry = {\n id: randomUUID().slice(0, 6),\n date: `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`,\n time: `${pad(d.getHours())}:${pad(d.getMinutes())}`,\n status: 'open',\n text: textTrimmed.replace(/\\r?\\n/g, ' '),\n };\n const { rawLines } = readLedger(guard, file);\n rawLines.push(renderEntry(entry));\n writeText(guard, file, rawLines.join('\\n').replace(/\\n*$/, '\\n'));\n return entry;\n}\n\n/** Mark an entry done by id (preferred) or unique text substring. */\nexport function markDone(guard: PathGuard, file: string, key: string, now = Date.now()): LedgerEntry | null {\n const { rawLines, entries } = readLedger(guard, file);\n const target = entries.find((e) => e.status === 'open' && (e.id === key || e.text.includes(key)));\n if (!target) return null;\n const d = new Date(now);\n const pad = (n: number) => String(n).padStart(2, '0');\n const out = rawLines.map((line) => {\n if (line.includes(`#${target.id}] `)) {\n return `- [${target.date} ${pad(d.getHours())}:${pad(d.getMinutes())}][done][#${target.id}] ${target.text}`;\n }\n return line;\n });\n writeText(guard, file, out.join('\\n').replace(/\\n*$/, '\\n'));\n return target;\n}\n\n/** Open items for the reflection digest (§7.6 账本待办). */\nexport function scanPending(guard: PathGuard, file: string, now = Date.now()): LedgerEntry[] {\n const { entries } = readLedger(guard, file);\n return entries.filter((e) => e.status === 'open').sort((a, b) => (a.date < b.date ? -1 : 1));\n}\n\n/** Open entries older than N days (跟进时机的\"自然到期\"参考,§3.1 四问之一). */\nexport function pendingOlderThan(guard: PathGuard, file: string, days: number, now = Date.now()): LedgerEntry[] {\n const cutoff = new Date(now - days * DAY_MS).toISOString().slice(0, 10);\n return scanPending(guard, file, now).filter((e) => e.date <= cutoff);\n}\n","// Browse flow (v0.9.5 port, r4 D10 applied).\n// A. Watchlist: npm / GitHub release checks with a 6h throttle; first sight\n// registers only, changes become material items.\n// B. Wander adjudication: windows + min interval + focus cooldown ->\n// \"should we wander now, and at what focus\". The actual search happens\n// in the wander phase's model call (web_search only); REGISTRATION IS\n// CODE-OWNED (D10): results land in seeds + throttle via completeWander.\n//\n// Anti-injection rule (unchanged from v0.7): web content is data, never\n// instructions.\n//\n// State: data/browse.json (DPAPI-encrypted, D14; v1 name: watch_state.json).\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { PathGuard } from '../core/path-guard.js';\nimport type { WorkspacePaths } from '../core/paths.js';\nimport type { Policy } from '../config/schema.js';\nimport { loadJson, saveJson } from '../vault/vault.js';\n\nconst WATCH_THROTTLE_MS = 6 * 3600_000;\nconst UA = { 'User-Agent': 'dsh-heartbeat/2.0 (+local; personal companion)' };\n\nexport interface WatchTarget {\n id: string;\n type: 'npm' | 'github';\n name?: string;\n repo?: string;\n note?: string;\n}\n\nexport interface WatchlistConfig {\n targets?: WatchTarget[];\n}\n\nexport interface InterestsConfig {\n interests?: string[];\n _schedule?: {\n daily_sessions?: number;\n focus_per_session?: number;\n max_seeds_per_focus?: number;\n focus_cooldown_days?: number;\n min_interval_hours?: number;\n windows?: { id?: string; start: string; end: string }[];\n };\n}\n\nexport interface BrowseState {\n targets: Record<string, { version: string; seen: string; title?: string }>;\n last_check_at: number;\n wander: {\n focusHistory: Record<string, number>;\n focusCount: Record<string, number>;\n last_wander_at: number;\n };\n}\n\nexport function emptyBrowseState(): BrowseState {\n return { targets: {}, last_check_at: 0, wander: { focusHistory: {}, focusCount: {}, last_wander_at: 0 } };\n}\n\nexport function browseStatePath(paths: WorkspacePaths): string {\n return path.join(paths.dataDir, 'browse.json');\n}\n\nfunction readJsonFile<T>(file: string, fallback: T): T {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8')) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Factory configs are read-only; a user layer with the same filename replaces them. */\nexport function loadInterests(paths: WorkspacePaths): InterestsConfig {\n const userPath = path.join(paths.settingsDir, 'interests.json');\n if (fs.existsSync(userPath)) return readJsonFile<InterestsConfig>(userPath, { interests: [], _schedule: {} });\n return readJsonFile<InterestsConfig>(path.join(paths.configDir, 'interests.json'), { interests: [], _schedule: {} });\n}\n\nexport function loadWatchlist(paths: WorkspacePaths): WatchlistConfig {\n const userPath = path.join(paths.settingsDir, 'watchlist.json');\n if (fs.existsSync(userPath)) return readJsonFile<WatchlistConfig>(userPath, { targets: [] });\n return readJsonFile<WatchlistConfig>(path.join(paths.configDir, 'watchlist.json'), { targets: [] });\n}\n\nfunction loadState(guard: PathGuard, paths: WorkspacePaths): BrowseState {\n return loadJson<BrowseState>(guard, browseStatePath(paths)) ?? emptyBrowseState();\n}\n\n// ── A. watchlist checks (fetcher injectable for tests) ──────────────────\n\nexport type FetchLike = (url: string, init?: { headers?: Record<string, string> }) => Promise<{ ok: boolean; status: number; json(): Promise<unknown> }>;\n\nasync function checkNpm(fetcher: FetchLike, name: string): Promise<{ version: string; seen: string; title?: string } | null> {\n const r = await fetcher(`https://registry.npmjs.org/${name}/latest`, { headers: UA });\n if (!r.ok) throw new Error(`npm ${r.status}`);\n const j = await r.json() as { version?: string };\n if (!j.version) throw new Error('npm: no version');\n return { version: j.version, seen: `npm:${j.version}` };\n}\n\nasync function checkGithub(fetcher: FetchLike, repo: string): Promise<{ version: string; seen: string; title?: string } | null> {\n const r = await fetcher(`https://api.github.com/repos/${repo}/releases/latest`, {\n headers: { ...UA, Accept: 'application/vnd.github+json' },\n });\n if (r.status === 404) return null; // repo has no releases yet\n if (!r.ok) throw new Error(`gh ${r.status}`);\n const j = await r.json() as { tag_name?: string; name?: string };\n if (!j.tag_name) throw new Error('gh: no tag');\n return { version: j.tag_name, seen: `gh:${j.tag_name}`, title: j.name || '' };\n}\n\nexport interface WatchReport {\n /** Material texts for the pool (one per changed target). */\n items: { text: string; topic: string; tag: 'news'; source: 'browse'; confidence: number }[];\n errors: string[];\n checked: number;\n}\n\nexport async function checkWatchlist(\n guard: PathGuard,\n paths: WorkspacePaths,\n opts: { fetcher?: FetchLike; throttleOk?: boolean } = {},\n now = Date.now(),\n): Promise<WatchReport> {\n const fetcher = opts.fetcher ?? (globalThis.fetch as unknown as FetchLike);\n const state = loadState(guard, paths);\n if (opts.throttleOk !== true && now - state.last_check_at < WATCH_THROTTLE_MS) {\n return { items: [], errors: [], checked: 0 };\n }\n const watchlist = loadWatchlist(paths);\n const report: WatchReport = { items: [], errors: [], checked: 0 };\n for (const t of watchlist.targets ?? []) {\n report.checked += 1;\n try {\n const info = t.type === 'npm' && t.name ? await checkNpm(fetcher, t.name)\n : t.type === 'github' && t.repo ? await checkGithub(fetcher, t.repo)\n : null;\n if (!info) continue;\n const prev = state.targets[t.id];\n if (prev && prev.seen !== info.seen) {\n const title = info.title ? `(${info.title.slice(0, 60)})` : '';\n report.items.push({\n text: `${t.note || t.id} 有更新:${prev.version} -> ${info.version}${title}`,\n topic: `watch:${t.id}`,\n tag: 'news',\n source: 'browse',\n confidence: 0.4,\n });\n }\n state.targets[t.id] = info; // first sight registers silently (首见不产素材)\n } catch (e) {\n report.errors.push(`${t.id}: ${String(e)}`);\n }\n }\n state.last_check_at = now;\n saveJson(guard, browseStatePath(paths), state);\n return report;\n}\n\n// ── B. wander adjudication (pure-ish, state injected) ───────────────────\n\nexport interface WanderAdvice {\n focus: string | null;\n query: string | null;\n skipped: string | null;\n}\n\nexport function inWanderWindow(now: Date, windows: { start: string; end: string }[]): string | null {\n const hm = now.getHours() * 60 + now.getMinutes();\n for (const w of windows) {\n const [sh, sm] = w.start.split(':').map(Number);\n const [eh, em] = w.end.split(':').map(Number);\n if (hm >= sh! * 60 + sm! && hm <= eh! * 60 + em!) return `${w.start}-${w.end}`;\n }\n return null;\n}\n\nfunction onCooldown(state: BrowseState, focus: string, cooldownDays: number, now: number): boolean {\n const last = state.wander.focusHistory[focus] ?? 0;\n return last > now - cooldownDays * 86_400_000;\n}\n\n/** Round-robin: least-recently-used non-cooling focus wins. */\nexport function pickFocus(state: BrowseState, interests: InterestsConfig, now: number): string | null {\n const sc = interests._schedule ?? {};\n const cooldown = sc.focus_cooldown_days ?? 3;\n const pool = (interests.interests ?? []).filter((t) => !onCooldown(state, t, cooldown, now));\n if (pool.length === 0) return null;\n pool.sort((a, b) => (state.wander.focusHistory[a] ?? 0) - (state.wander.focusHistory[b] ?? 0));\n return pool[0]!;\n}\n\n/** Adjudicate: windows + min interval + focus cooldown -> advice | skipped. */\nexport function adviseWander(\n guard: PathGuard,\n paths: WorkspacePaths,\n policy: Policy,\n now = new Date(),\n): WanderAdvice {\n const state = loadState(guard, paths);\n const interests = loadInterests(paths);\n const windows = interests._schedule?.windows?.length\n ? interests._schedule.windows\n : (policy.browse.windows as { start: string; end: string }[]);\n const win = inWanderWindow(now, windows);\n if (!win) {\n const hh = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;\n return { focus: null, query: null, skipped: `window(now=${hh})` };\n }\n const minGap = policy.browse.minIntervalHours * 3600_000;\n if (now.getTime() - state.wander.last_wander_at < minGap) {\n return { focus: null, query: null, skipped: 'min-interval' };\n }\n const focus = pickFocus(state, interests, now.getTime());\n if (!focus) return { focus: null, query: null, skipped: 'no-focus' };\n return { focus, query: `${focus} 2026 最新`, skipped: null };\n}\n\n/**\n * Registration is CODE-OWNED (D10): called by the orchestrator after the\n * wander phase's model call returned selections. Records cooldown/count/\n * throttle timestamps for the focus.\n */\nexport function completeWander(\n guard: PathGuard,\n paths: WorkspacePaths,\n focus: string,\n now = Date.now(),\n): { focus: string; count: number } {\n const state = loadState(guard, paths);\n state.wander.focusHistory[focus] = now;\n state.wander.focusCount[focus] = (state.wander.focusCount[focus] ?? 0) + 1;\n state.wander.last_wander_at = now;\n saveJson(guard, browseStatePath(paths), state);\n return { focus, count: state.wander.focusCount[focus]! };\n}\n\nexport function browseStatus(guard: PathGuard, paths: WorkspacePaths): BrowseState {\n return loadState(guard, paths);\n}\n","// Profile store: materialized view + journal (the journal is the ONLY\n// authority; profile.json is a pure projection - r2 §14 verify/rebuild).\n// Every LLM-proposed op passes deterministic guards here (LLM nominates,\n// code decides - §3.3).\n\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { loadJson, saveJson, readText, writeText } from '../vault/vault.js';\nimport { atomicWriteFileSync } from '../core/atomic-fs.js';\nimport type { Policy } from '../config/schema.js';\nimport {\n CONFIDENCE_CAP,\n emptyProfile,\n PARTITIONS,\n type ApplyReport,\n type Evidence,\n type EvidenceKind,\n type InboxItem,\n type JournalRecord,\n type Partition,\n type ProfileDoc,\n type ProfileEntry,\n type ProfileOp,\n} from './types.js';\nimport { checkAddAgainstSchema, type ProfileSchema } from './schema.js';\n\nconst DAY_MS = 86_400_000;\n\nexport function profileFilePath(dataDir: string): string {\n return path.join(dataDir, 'profile.json');\n}\n\nexport function journalFilePath(dataDir: string): string {\n return path.join(dataDir, 'profile_journal.jsonl');\n}\n\nexport function loadProfile(guard: PathGuard, file: string): ProfileDoc {\n const doc = loadJson<ProfileDoc>(guard, file);\n if (!doc || !doc.partitions) return emptyProfile();\n // ensure all partitions exist\n for (const p of PARTITIONS) {\n if (!doc.partitions[p]) doc.partitions[p] = { entries: [] };\n }\n return doc;\n}\n\nfunction parseIso(v: string): number {\n const t = Date.parse(v);\n return Number.isFinite(t) ? t : 0;\n}\n\n/** ref format \"<datafile>#<locator>\": the referenced file must exist under data/. */\nfunction refExists(guard: PathGuard, dataDir: string, ref: string): boolean {\n const base = ref.split('#')[0] ?? '';\n if (!base) return false;\n const target = path.join(dataDir, base);\n try {\n return fs.existsSync(guard.assert(target));\n } catch {\n return false;\n }\n}\n\nfunction capForKinds(kinds: EvidenceKind[]): number {\n if (kinds.length === 0) return 0.4;\n return Math.min(...kinds.map((k) => CONFIDENCE_CAP[k] ?? 0.4));\n}\n\nfunction findActive(doc: ProfileDoc, id: string): ProfileEntry | undefined {\n for (const p of PARTITIONS) {\n const hit = doc.partitions[p]!.entries.find((e) => e.id === id && e.validTo === null);\n if (hit) return hit;\n }\n return undefined;\n}\n\n/**\n * Apply validated ops to the doc (mutates). Returns applied/rejected.\n * Guards (§3.3): whitelist, evidence gate, per-run ops cap is enforced by the\n * caller, confidence caps by evidence kind, psy gating, INVALIDATE ownership:\n * volatile expiry is code-driven (LLM may not), stable invalidation needs a\n * contradicting observation attached.\n */\nexport function applyOpsToDoc(\n guard: PathGuard,\n dataDir: string,\n doc: ProfileDoc,\n ops: ProfileOp[],\n schema: ProfileSchema,\n policy: Policy,\n now: number,\n): ApplyReport {\n const applied: ProfileOp[] = [];\n const rejected: { op: ProfileOp; reason: string }[] = [];\n const nowIso = new Date(now).toISOString();\n\n for (const op of ops) {\n if (op.op === 'NOOP') {\n applied.push(op);\n continue;\n }\n if (op.op === 'ADD') {\n if (op.partition === 'psy' && !policy.profile.psyEnabled) {\n rejected.push({ op, reason: 'psy partition is disabled' });\n continue;\n }\n const check = checkAddAgainstSchema(schema, op.partition, op.topic, op.subTopic, op.temporal);\n if (!check.ok) {\n rejected.push({ op, reason: check.reason! });\n continue;\n }\n if (!op.evidence || op.evidence.length === 0) {\n rejected.push({ op, reason: 'ADD without evidence (no provenance, axiom 1)' });\n continue;\n }\n const badRef = op.evidence.find((e) => !refExists(guard, dataDir, e.ref));\n if (badRef) {\n rejected.push({ op, reason: `evidence ref does not resolve: ${badRef.ref}` });\n continue;\n }\n const cap = capForKinds(op.evidence.map((e) => e.kind));\n const active = doc.partitions[op.partition]!.entries.filter((e) => e.validTo === null);\n if (active.length >= policy.profile.partitionCap) {\n rejected.push({ op, reason: `partition ${op.partition} at cap (${policy.profile.partitionCap}); converge first` });\n continue;\n }\n dbSeq += 1;\n const entry: ProfileEntry = {\n id: `p${dbSeq.toString(36)}${randomUUID().slice(0, 4)}`,\n partition: op.partition,\n topic: op.topic,\n subTopic: op.subTopic,\n content: op.content.trim(),\n confidence: Math.min(op.confidence ?? cap, cap),\n temporal: check.temporal,\n validFrom: nowIso,\n validTo: null,\n supersededBy: null,\n evidence: op.evidence,\n createdAt: nowIso,\n updatedAt: nowIso,\n updateCount: 0,\n };\n op.assignedId = entry.id; // journal replay must reproduce this id\n doc.partitions[op.partition]!.entries.push(entry);\n applied.push(op);\n continue;\n }\n if (op.op === 'UPDATE') {\n const entry = findActive(doc, op.id);\n if (!entry) {\n rejected.push({ op, reason: `unknown or inactive entry: ${op.id}` });\n continue;\n }\n if (op.changes.content !== undefined) entry.content = op.changes.content.trim();\n if (op.changes.confidence !== undefined) {\n const cap = capForKinds(entry.evidence.map((e) => e.kind));\n // confidence upgrades need a second confirming observation (§3.3):\n // only allowed up to cap, and only when the entry already has 2+ evidence\n if (op.changes.confidence > entry.confidence && entry.evidence.length < 2) {\n rejected.push({ op, reason: 'confidence upgrade requires a second confirming observation' });\n continue;\n }\n entry.confidence = Math.min(op.changes.confidence, cap);\n }\n entry.updatedAt = nowIso;\n entry.updateCount += 1;\n applied.push(op);\n continue;\n }\n if (op.op === 'INVALIDATE') {\n const entry = findActive(doc, op.id);\n if (!entry) {\n rejected.push({ op, reason: `unknown or inactive entry: ${op.id}` });\n continue;\n }\n if (entry.temporal === 'volatile') {\n rejected.push({ op, reason: 'volatile expiry is code-owned (time-driven), not LLM-nominated' });\n continue;\n }\n // stable: rebuttal-driven; requires a contradicting observation attached\n const hasNewObservation = (op.evidence ?? []).length > 0\n && (op.evidence ?? []).some((e) => parseIso(e.at) > parseIso(entry.evidence[entry.evidence.length - 1]?.at ?? ''));\n if (!hasNewObservation) {\n rejected.push({ op, reason: 'stable INVALIDATE requires a newer contradicting observation' });\n continue;\n }\n entry.validTo = nowIso;\n entry.supersededBy = null;\n entry.updatedAt = nowIso;\n entry.updateCount += 1;\n if (op.evidence) entry.evidence.push(...op.evidence);\n applied.push(op);\n continue;\n }\n }\n return { applied, rejected };\n}\n\nlet dbSeq = 0;\n\n/** Deterministic aging (D9): volatile expiry + stable low-activity marking. */\nexport function runDeterministicAging(\n doc: ProfileDoc,\n policy: Policy,\n now: number,\n): { volatileExpired: number; lowActivityMarked: number } {\n const nowIso = new Date(now).toISOString();\n let volatileExpired = 0;\n let lowActivityMarked = 0;\n for (const p of PARTITIONS) {\n for (const e of doc.partitions[p]!.entries) {\n if (e.validTo !== null) continue;\n const lastEvidence = Math.max(...e.evidence.map((x) => parseIso(x.at)), parseIso(e.updatedAt));\n if (e.temporal === 'volatile') {\n if (now - lastEvidence > policy.profile.volatileDays * DAY_MS) {\n e.validTo = nowIso;\n e.updatedAt = nowIso;\n e.updateCount += 1;\n volatileExpired += 1;\n }\n } else if (!e.lowActivity && now - lastEvidence > policy.profile.stableLowActivityDays * DAY_MS) {\n e.lowActivity = true;\n lowActivityMarked += 1;\n }\n }\n }\n return { volatileExpired, lowActivityMarked };\n}\n\n/** Persist the materialized view atomically + append the journal record. */\nexport function persistWithJournal(\n guard: PathGuard,\n dataDir: string,\n doc: ProfileDoc,\n record: Omit<JournalRecord, 'ts'>,\n): void {\n saveJson(guard, profileFilePath(dataDir), doc);\n const line = JSON.stringify({ ts: new Date().toISOString(), ...record });\n const journal = journalFilePath(dataDir);\n try {\n fs.appendFileSync(guard.assert(journal), line + '\\n', 'utf8');\n } catch {\n fs.mkdirSync(dataDir, { recursive: true });\n fs.appendFileSync(guard.assert(journal), line + '\\n', 'utf8');\n }\n}\n\n// ── journal replay (verify / rebuild, r2 §14) ───────────────────────────\n\nfunction applyOpPermissive(doc: ProfileDoc, op: ProfileOp, ts: string): void {\n // Journal is trusted history: replay applies without revalidation.\n if (op.op === 'ADD') {\n dbSeq += 1;\n doc.partitions[op.partition]!.entries.push({\n id: op.assignedId ?? `r${dbSeq.toString(36)}${randomUUID().slice(0, 4)}`,\n partition: op.partition,\n topic: op.topic,\n subTopic: op.subTopic,\n content: op.content,\n confidence: op.confidence ?? 0.5,\n temporal: op.temporal ?? 'stable',\n validFrom: ts,\n validTo: null,\n supersededBy: null,\n evidence: op.evidence,\n createdAt: ts,\n updatedAt: ts,\n updateCount: 0,\n });\n return;\n }\n if (op.op === 'UPDATE') {\n const e = [...PARTITIONS].flatMap((p) => doc.partitions[p]!.entries).find((x) => x.id === op.id);\n if (e) {\n if (op.changes.content !== undefined) e.content = op.changes.content;\n if (op.changes.confidence !== undefined) e.confidence = op.changes.confidence;\n e.updatedAt = ts;\n e.updateCount += 1;\n }\n return;\n }\n if (op.op === 'INVALIDATE') {\n const e = [...PARTITIONS].flatMap((p) => doc.partitions[p]!.entries).find((x) => x.id === op.id);\n if (e) {\n e.validTo = ts;\n e.updatedAt = ts;\n e.updateCount += 1;\n }\n }\n // NOOP: nothing\n}\n\nexport interface ReplayResult {\n doc: ProfileDoc;\n truncatedTail: number;\n records: number;\n}\n\n/** Full replay from an empty view. Tolerates a torn tail (explicitly). */\nexport function replayJournal(guard: PathGuard, dataDir: string): ReplayResult {\n const journal = journalFilePath(dataDir);\n const raw = readText(guard, journal, '');\n const doc = emptyProfile();\n let records = 0;\n let truncatedTail = 0;\n const lines = raw.split('\\n');\n for (let i = 0; i < lines.length; i++) {\n const trimmed = lines[i]!.trim();\n if (!trimmed) continue;\n try {\n const rec = JSON.parse(trimmed) as JournalRecord;\n for (const op of rec.applied ?? []) applyOpPermissive(doc, op, rec.ts);\n records += 1;\n } catch {\n const isLast = lines.slice(i + 1).every((l) => !l.trim());\n if (isLast) {\n truncatedTail = lines.length - i; // torn tail from a mid-write crash\n break;\n }\n // mid-file corrupt record: skip (journal stays append-only/immutable)\n }\n }\n return { doc, truncatedTail, records };\n}\n\nexport interface VerifyReport {\n ok: boolean;\n firstDivergence?: { id: string; expected: string; actual: string };\n truncatedTail: number;\n records: number;\n}\n\n/** profile verify: replay vs disk, report first divergence, never fix. */\nexport function verifyProfile(guard: PathGuard, dataDir: string): VerifyReport {\n const replayed = replayJournal(guard, dataDir);\n const onDisk = loadProfile(guard, profileFilePath(dataDir));\n // Timestamps legitimately differ (journal record ts >= op ts); identity\n // fields are also normalized. Compare semantic content only.\n const strip = (doc: ProfileDoc): string =>\n JSON.stringify(doc.partitions, (k, v) => (['id', 'supersededBy', 'validFrom', 'createdAt', 'updatedAt', 'retiredAt'].includes(k) ? '<norm>' : v));\n const ok = strip(replayed.doc) === strip(onDisk);\n if (ok) return { ok: true, truncatedTail: replayed.truncatedTail, records: replayed.records };\n // first divergence: first entry id present in only one view\n const diskIds = new Set([...PARTITIONS].flatMap((p) => onDisk.partitions[p]!.entries.map((e) => e.content))); const replayIds = new Set([...PARTITIONS].flatMap((p) => replayed.doc.partitions[p]!.entries.map((e) => e.content)));\n const onlyDisk = [...diskIds].find((c) => !replayIds.has(c));\n const onlyReplay = [...replayIds].find((c) => !diskIds.has(c));\n return {\n ok: false,\n firstDivergence: {\n id: onlyDisk ?? onlyReplay ?? '(content)',\n expected: onlyReplay ? 'absent in journal replay' : 'present in journal replay',\n actual: onlyDisk ? 'present on disk' : 'absent on disk',\n },\n truncatedTail: replayed.truncatedTail,\n records: replayed.records,\n };\n}\n\nexport interface RebuildReport {\n ok: boolean;\n truncatedTail: number;\n records: number;\n wrote: boolean;\n}\n\n/** profile rebuild: journal is authoritative; atomic replace; torn tail reported. */\nexport function rebuildProfile(\n guard: PathGuard,\n dataDir: string,\n opts: { check?: boolean } = {},\n): RebuildReport & { diffSummary?: string } {\n const replayed = replayJournal(guard, dataDir);\n const target = profileFilePath(dataDir);\n const tmp = path.join(dataDir, `.profile.rebuild.${Date.now()}.tmp`);\n atomicWriteFileSync(tmp, JSON.stringify(replayed.doc, null, 2));\n if (opts.check) {\n const onDisk = loadProfile(guard, target);\n const same = JSON.stringify(onDisk) === JSON.stringify(replayed.doc);\n fs.rmSync(tmp, { force: true });\n return {\n ok: same,\n truncatedTail: replayed.truncatedTail,\n records: replayed.records,\n wrote: false,\n diffSummary: same ? 'no diff' : 'materialized view differs from journal replay',\n };\n }\n fs.renameSync(tmp, target);\n if (replayed.truncatedTail > 0) {\n // explicit audit: never silently rebuild a view that lost its tail\n writeText(\n guard,\n path.join(dataDir, 'logs', 'rebuild-report.txt'),\n `rebuild truncated ${replayed.truncatedTail} torn line(s) at journal tail; ${replayed.records} records applied\\n`,\n );\n }\n return { ok: true, truncatedTail: replayed.truncatedTail, records: replayed.records, wrote: true };\n}\n","// User profile types (design doc §3). Fourth store: warm, structured,\n// slow-evolving model of the user. Not the material pool (hot cache), not\n// DSH long-term memory (conversation-validated layer).\n\nexport type Partition = 'interest' | 'projects' | 'comm' | 'psy';\nexport type Temporal = 'volatile' | 'stable';\nexport type EvidenceKind = 'chat' | 'screen' | 'browse' | 'hand' | 'ledger';\n\nexport interface Evidence {\n kind: EvidenceKind;\n at: string;\n /** Pointer to an existing audit/log location, e.g. \"heartbeat.jsonl#2026-09-06T12:00:00Z\". */\n ref: string;\n /** At most ONE short quote (<=1 sentence). Never raw conversation/screen text. */\n quote?: string;\n}\n\nexport interface ProfileEntry {\n id: string;\n partition: Partition;\n topic: string;\n subTopic: string;\n content: string;\n /** 0..1; capped per source kind (chat .6 / screen .4 / browse .4). */\n confidence: number;\n temporal: Temporal;\n validFrom: string;\n validTo: string | null;\n supersededBy: string | null;\n evidence: Evidence[];\n createdAt: string;\n updatedAt: string;\n updateCount: number;\n /** stable-tier audit flag (180d without observation): digest deprioritizes. */\n lowActivity?: boolean;\n}\n\nexport interface ProfileDoc {\n version: number;\n partitions: Record<Partition, { entries: ProfileEntry[] }>;\n}\n\nexport interface InboxItem {\n id?: string;\n kind: EvidenceKind;\n at: string;\n ref: string;\n /** <= 1 short sentence; never raw conversation/screen text (truncated by writer). */\n note: string;\n}\n\nexport type ProfileOp =\n | {\n op: 'ADD';\n partition: Partition;\n topic: string;\n subTopic: string;\n content: string;\n temporal?: Temporal;\n confidence?: number;\n why: string;\n evidence: Evidence[];\n /** Assigned by the store at apply time so journal replay preserves ids. */\n assignedId?: string;\n }\n | { op: 'UPDATE'; id: string; changes: { content?: string; confidence?: number }; why: string }\n | { op: 'INVALIDATE'; id: string; why: string; evidence?: Evidence[] }\n | { op: 'NOOP'; why: string };\n\nexport interface JournalRecord {\n ts: string;\n runId: string;\n applied: ProfileOp[];\n rejected: { op: ProfileOp; reason: string }[];\n}\n\nexport interface ApplyReport {\n applied: ProfileOp[];\n rejected: { op: ProfileOp; reason: string }[];\n}\n\nexport const PARTITIONS: readonly Partition[] = ['interest', 'projects', 'comm', 'psy'];\n\nexport const CONFIDENCE_CAP: Record<EvidenceKind, number> = {\n chat: 0.6,\n screen: 0.4,\n browse: 0.4,\n hand: 1.0,\n ledger: 0.6,\n};\n\nexport function emptyProfile(): ProfileDoc {\n return {\n version: 1,\n partitions: { interest: { entries: [] }, projects: { entries: [] }, comm: { entries: [] }, psy: { entries: [] } },\n };\n}\n","// profile-schema.json loading + ADD validation (r4 B10: temporal tier is an\n// ENTRY-level attribute; the schema declares, per sub_topic, the ALLOWED tier\n// set and the DEFAULT. Unlisted defaults to 'stable'.)\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { Partition, Temporal } from './types.js';\n\nexport interface SubTopicDecl {\n allowed?: Temporal[];\n default?: Temporal;\n}\n\nexport interface ProfileSchema {\n version: number;\n partitions: Partial<Record<Partition, {\n topics: Record<string, { subtopics: Record<string, SubTopicDecl> }>;\n }>>;\n}\n\nexport function loadProfileSchema(paths: { configDir: string; settingsDir: string }): ProfileSchema {\n const userPath = path.join(paths.settingsDir, 'profile-schema.json');\n const file = fs.existsSync(userPath) ? userPath : path.join(paths.configDir, 'profile-schema.json');\n try {\n const raw = JSON.parse(fs.readFileSync(file, 'utf8')) as ProfileSchema;\n if (!raw.partitions) throw new Error('partitions missing');\n return raw;\n } catch (e) {\n throw new Error(`profile-schema unreadable at ${file}: ${String(e)}`);\n }\n}\n\nexport interface SchemaCheck {\n ok: boolean;\n reason?: string;\n temporal: Temporal;\n}\n\n/**\n * Validate an ADD against the whitelist and resolve the entry's temporal tier:\n * LLM nominates within the sub_topic's allowed set; missing nomination falls\n * back to the declared default; fully unlisted -> reject (charter boundary).\n */\nexport function checkAddAgainstSchema(\n schema: ProfileSchema,\n partition: Partition,\n topic: string,\n subTopic: string,\n nominated?: Temporal,\n): SchemaCheck {\n const p = schema.partitions[partition];\n if (!p) return { ok: false, reason: `partition not in schema: ${partition}`, temporal: 'stable' };\n const t = p.topics[topic];\n if (!t) return { ok: false, reason: `topic not in schema: ${partition}/${topic}`, temporal: 'stable' };\n const st = t.subtopics[subTopic];\n if (!st) return { ok: false, reason: `sub_topic not in schema: ${partition}/${topic}/${subTopic}`, temporal: 'stable' };\n const allowed: Temporal[] = st.allowed && st.allowed.length > 0 ? st.allowed : ['stable'];\n const def: Temporal = st.default && allowed.includes(st.default) ? st.default : allowed[0]!;\n if (!nominated) return { ok: true, temporal: def };\n if (!allowed.includes(nominated)) {\n return {\n ok: false,\n reason: `temporal \"${nominated}\" not allowed for ${partition}/${topic}/${subTopic} (allowed: ${allowed.join('|')})`,\n temporal: def,\n };\n }\n return { ok: true, temporal: nominated };\n}\n","// Windows toast channel (D12): attention hint ONLY - never carries the\n// expression body. Wraps assets/notify.ps1 (self-registering AUMID).\n\nimport { spawnSync } from 'node:child_process';\nimport path from 'node:path';\nimport type { WorkspacePaths } from '../core/paths.js';\n\nfunction runNotify(paths: WorkspacePaths, args: string[]): { status: number; out: string } {\n const r = spawnSync('powershell.exe',\n ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', path.join(paths.assetsDir, 'notify.ps1'), ...args],\n { timeout: 20_000, encoding: 'utf8' });\n return { status: r.status ?? -1, out: `${r.stdout ?? ''}${r.stderr ?? ''}`.trim() };\n}\n\n/** Register the toast identity if missing (idempotent, per machine/user). */\nexport function ensureRegistered(paths: WorkspacePaths): boolean {\n const check = runNotify(paths, ['-Check']);\n if (/REGISTERED: yes/.test(check.out)) return true;\n const reg = runNotify(paths, ['-RegisterOnly']);\n return reg.status === 0;\n}\n\n/**\n * Fire the \"new message\" hint. Fixed neutral text per D12 - the actual\n * expression lives in the dedicated heartbeat session, never in the toast.\n */\nexport function sendNewMessageHint(paths: WorkspacePaths): boolean {\n const r = runNotify(paths, ['-Title', 'Heartbeat', '-Message', '有新消息']);\n return r.status === 0 && /TOAST_SENT/.test(r.out);\n}\n","// Bundled agent-preset installation (contract C13).\n//\n// Why this exists: the heartbeat agent is created by the host `agents` service,\n// which does NOT go through the session-start preset picker. A bare agent joins\n// no preset, and dsh-agent-presets states the consequence verbatim — \"its tools,\n// prompt sections, and skill catalog resolve against the empty global layer\" —\n// so it cannot even see `web_search`. The preset is therefore not optional, and\n// asking the operator to hand-copy two YAML files was the most error-prone step\n// of setup. The plugin now materialises its own bundled template in the roster's\n// USER root on first run.\n//\n// Two properties keep that safe:\n// 1. An existing preset is NEVER overwritten — the composition file belongs to\n// whoever edited it. Only a missing (or ghost) directory is filled in.\n// 2. The target comes from the roster's OWN roots (`agentPresets.roots`,\n// trust === \"user\") rather than a guessed `~/.dsh`, so `$DSH_HOME` and a\n// configured home are honoured without re-deriving them here.\n//\n// Timing: dsh-agent-presets re-scans the filesystem on every read\n// (`list()` -> `discoverPresets` -> `scanRoot`, no cache), so a directory\n// created here is visible to the very next `mount()` — no restart in between.\n\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport const COMPOSITION_FILE = 'agent.cordis.yml';\nexport const METADATA_FILE = 'preset.yml';\n/** The template this package ships, in `assets/presets/<id>/`. */\nexport const BUNDLED_PRESET_ID = 'heartbeat';\n\n/** One entry of the roster's root list (`AgentPresets.roots`). */\nexport interface PresetRootLike {\n path?: string;\n trust?: string;\n}\n\nexport type PresetInstallAction =\n | 'exists'\n | 'created'\n | 'repaired'\n | 'restored'\n | 'skipped-disabled'\n | 'skipped-custom-id'\n | 'skipped-no-root'\n | 'error';\n\nexport interface PresetInstallResult {\n action: PresetInstallAction;\n id: string;\n /** Where the preset lives (or would live). */\n dir?: string;\n /** Where the bundled template was read from. */\n bundledDir?: string;\n detail?: string;\n}\n\n/**\n * Locate the bundled template directory by walking up from a module URL.\n * Works from both entry points (`dist/index.js` and `dist/cli/index.js`) and\n * from the pnpm copy of an installed package.\n */\nexport function bundledPresetDir(\n moduleUrl: string,\n id: string = BUNDLED_PRESET_ID,\n): string | undefined {\n let dir: string;\n try {\n dir = path.dirname(fileURLToPath(moduleUrl));\n } catch {\n return undefined;\n }\n for (let depth = 0; depth < 5; depth += 1) {\n const candidate = path.join(dir, 'assets', 'presets', id);\n if (fs.existsSync(path.join(candidate, COMPOSITION_FILE))) return candidate;\n const parent = path.dirname(dir);\n if (parent === dir) break;\n dir = parent;\n }\n return undefined;\n}\n\n/** The roster's user-trust root, which is where locally authored presets live. */\nexport function userPresetRoot(roots?: readonly PresetRootLike[]): string | undefined {\n const found = roots?.find(\n (root) => root?.trust === 'user' && typeof root.path === 'string' && root.path.length > 0,\n );\n return found?.path === undefined ? undefined : path.resolve(found.path);\n}\n\n/**\n * `<dshHome>/.agent-presets` derived from the documented precedence\n * (`$DSH_HOME`, then `~/.dsh`). Only used where no roster is available — the\n * plugin itself prefers {@link userPresetRoot}.\n */\nexport function conventionalUserPresetRoot(\n env: NodeJS.ProcessEnv = process.env,\n home: string = os.homedir(),\n): string {\n const override = env.DSH_HOME?.trim();\n const root = override && override.length > 0 ? override : path.join(home, '.dsh');\n return path.join(root, '.agent-presets');\n}\n\nexport interface InstallPresetOptions {\n /** `import.meta.url` of the calling entry point. */\n moduleUrl: string;\n /** Preset id the heartbeat agent will join (config `agentPreset`). */\n id?: string;\n /** User-trust root reported by the roster. */\n root?: string;\n /** True when the roster answered — then an absent user root is a fact, not a guess. */\n rosterKnown?: boolean;\n /** `false` disables the install (config `installPreset`). */\n enabled?: boolean;\n /** Overwrite an existing composition file with the bundled one. */\n force?: boolean;\n}\n\n/**\n * Materialise the bundled preset in the roster's user root when it is absent.\n * Never destructive: an existing composition file is kept unless `force`.\n */\nexport function installBundledPreset(options: InstallPresetOptions): PresetInstallResult {\n const id = options.id && options.id.length > 0 ? options.id : BUNDLED_PRESET_ID;\n if (options.enabled === false) {\n return { action: 'skipped-disabled', id, detail: 'installPreset=false' };\n }\n const bundledDir = bundledPresetDir(options.moduleUrl, BUNDLED_PRESET_ID);\n if (id !== BUNDLED_PRESET_ID) {\n return {\n action: 'skipped-custom-id',\n id,\n ...(bundledDir === undefined ? {} : { bundledDir }),\n detail: `only \"${BUNDLED_PRESET_ID}\" ships with the plugin; \"${id}\" is yours to provide`,\n };\n }\n if (bundledDir === undefined) {\n return {\n action: 'error',\n id,\n detail: 'bundled template not found next to the plugin (assets/presets/heartbeat)',\n };\n }\n const root =\n options.root ?? (options.rosterKnown ? undefined : conventionalUserPresetRoot());\n if (root === undefined) {\n return {\n action: 'skipped-no-root',\n id,\n bundledDir,\n detail: 'the roster mounts no user preset root (includeUserRoot=false)',\n };\n }\n const dir = path.join(root, id);\n const composition = path.join(dir, COMPOSITION_FILE);\n try {\n if (fs.existsSync(composition)) {\n if (options.force !== true) {\n const drifted = !sameBytes(composition, path.join(bundledDir, COMPOSITION_FILE));\n return {\n action: 'exists',\n id,\n dir,\n bundledDir,\n detail: drifted ? 'kept as-is (differs from the bundled template)' : 'kept as-is',\n };\n }\n fs.copyFileSync(path.join(bundledDir, COMPOSITION_FILE), composition);\n return {\n action: 'restored',\n id,\n dir,\n bundledDir,\n detail: 'composition replaced from the bundled template',\n };\n }\n const existed = fs.existsSync(dir);\n fs.mkdirSync(dir, { recursive: true });\n fs.copyFileSync(path.join(bundledDir, COMPOSITION_FILE), composition);\n const metadata = path.join(dir, METADATA_FILE);\n // A directory with a composition but no metadata is legal; only fill a void.\n if (!fs.existsSync(metadata)) fs.copyFileSync(path.join(bundledDir, METADATA_FILE), metadata);\n return {\n action: existed ? 'repaired' : 'created',\n id,\n dir,\n bundledDir,\n detail: existed\n ? 'directory existed without a composition file (it occupied the id as a broken row)'\n : undefined,\n };\n } catch (error) {\n return { action: 'error', id, dir, bundledDir, detail: String(error).slice(0, 200) };\n }\n}\n\n/** One line suitable for the audit log and `ctx.logger`. */\nexport function describeInstall(result: PresetInstallResult): string {\n const where = result.dir === undefined ? '' : ` (${result.dir})`;\n const why = result.detail === undefined ? '' : ` — ${result.detail}`;\n return `preset ${result.id} ${result.action}${where}${why}`;\n}\n\nexport interface PresetStatus {\n id: string;\n dir: string;\n bundledDir?: string;\n installed: boolean;\n compositionMatches: boolean;\n metadataMatches: boolean;\n}\n\n/** Read-only inspection for the CLI (`preset status`). */\nexport function presetStatus(\n moduleUrl: string,\n id: string = BUNDLED_PRESET_ID,\n root: string = conventionalUserPresetRoot(),\n): PresetStatus {\n const dir = path.join(root, id);\n const bundledDir = bundledPresetDir(moduleUrl, id);\n const installed = fs.existsSync(path.join(dir, COMPOSITION_FILE));\n return {\n id,\n dir,\n ...(bundledDir === undefined ? {} : { bundledDir }),\n installed,\n compositionMatches:\n installed && bundledDir !== undefined && sameBytes(path.join(dir, COMPOSITION_FILE), path.join(bundledDir, COMPOSITION_FILE)),\n metadataMatches:\n bundledDir !== undefined &&\n fs.existsSync(path.join(dir, METADATA_FILE)) &&\n sameBytes(path.join(dir, METADATA_FILE), path.join(bundledDir, METADATA_FILE)),\n };\n}\n\nfunction sameBytes(left: string, right: string): boolean {\n try {\n return fs.readFileSync(left).equals(fs.readFileSync(right));\n } catch {\n return false;\n }\n}\n"],"mappings":"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","names":["fs","path","fs","path","fs","path","fs","path","path","fs","path","path","path","randomUUID","DAY_MS","path","randomUUID","fs","path","path","fs","path","randomUUID","fs","fs","path","DAY_MS","path","parseIso","fs","randomUUID","path","fs","path"]}
@@ -0,0 +1,98 @@
1
+ import {
2
+ loadEncryptedText,
3
+ saveEncryptedText
4
+ } from "./chunk-LLD7LUNN.js";
5
+
6
+ // src/profile/inbox.ts
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import { randomUUID } from "crypto";
10
+ var MAX_NOTE_CHARS = 120;
11
+ function inboxFilePath(dataDir) {
12
+ return path.join(dataDir, "profile_inbox.jsonl");
13
+ }
14
+ function truncateNote(note) {
15
+ const oneLine = note.replace(/\r?\n/g, " ").trim();
16
+ return oneLine.length > MAX_NOTE_CHARS ? oneLine.slice(0, MAX_NOTE_CHARS) + "\u2026" : oneLine;
17
+ }
18
+ function inboxAppend(guard, file, item) {
19
+ const full = {
20
+ id: item.id ?? randomUUID().slice(0, 8),
21
+ kind: item.kind,
22
+ at: item.at,
23
+ ref: item.ref,
24
+ note: truncateNote(item.note)
25
+ };
26
+ const prev = loadEncryptedText(guard, file) ?? "";
27
+ saveEncryptedText(guard, file, prev + JSON.stringify(full) + "\n");
28
+ return full;
29
+ }
30
+ function inboxCount(guard, file) {
31
+ const raw = loadEncryptedText(guard, file);
32
+ if (!raw) return 0;
33
+ return raw.split("\n").filter((l) => l.trim()).length;
34
+ }
35
+ function inboxDrain(guard, file) {
36
+ const raw = loadEncryptedText(guard, file);
37
+ if (!raw) return [];
38
+ const items = [];
39
+ for (const line of raw.split("\n")) {
40
+ const trimmed = line.trim();
41
+ if (!trimmed) continue;
42
+ try {
43
+ items.push(JSON.parse(trimmed));
44
+ } catch {
45
+ }
46
+ }
47
+ return items;
48
+ }
49
+ function inboxClear(guard, file) {
50
+ saveEncryptedText(guard, file, "");
51
+ }
52
+ function dedupeItems(items) {
53
+ const seen = /* @__PURE__ */ new Set();
54
+ const out = [];
55
+ for (const it of items) {
56
+ const key = `${it.kind}#${it.ref}`;
57
+ if (seen.has(key)) continue;
58
+ seen.add(key);
59
+ out.push(it);
60
+ }
61
+ return out;
62
+ }
63
+ function inboxHealthCheck(guard, file) {
64
+ const raw = loadEncryptedText(guard, file) ?? "";
65
+ let total = 0;
66
+ let corrupt = 0;
67
+ const keys = /* @__PURE__ */ new Set();
68
+ let duplicates = 0;
69
+ for (const line of raw.split("\n")) {
70
+ const trimmed = line.trim();
71
+ if (!trimmed) continue;
72
+ try {
73
+ const it = JSON.parse(trimmed);
74
+ total += 1;
75
+ const key = `${it.kind}#${it.ref}`;
76
+ if (keys.has(key)) duplicates += 1;
77
+ keys.add(key);
78
+ } catch {
79
+ corrupt += 1;
80
+ }
81
+ }
82
+ return { total, corrupt, duplicates };
83
+ }
84
+ function inboxFileExists(guard, file) {
85
+ return fs.existsSync(guard.assert(file));
86
+ }
87
+
88
+ export {
89
+ inboxFilePath,
90
+ inboxAppend,
91
+ inboxCount,
92
+ inboxDrain,
93
+ inboxClear,
94
+ dedupeItems,
95
+ inboxHealthCheck,
96
+ inboxFileExists
97
+ };
98
+ //# sourceMappingURL=chunk-4UE74TUB.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/profile/inbox.ts"],"sourcesContent":["// Observation inbox (design doc §3.2): append-only queue of pending\n// observations; drained by the consolidation run. DPAPI-encrypted. Items hold\n// pointers + at most one short sentence - never raw conversation/screen text.\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\nimport type { PathGuard } from '../core/path-guard.js';\nimport { loadEncryptedText, saveEncryptedText } from '../vault/vault.js';\nimport type { InboxItem } from './types.js';\n\nconst MAX_NOTE_CHARS = 120;\n\nexport function inboxFilePath(dataDir: string): string {\n return path.join(dataDir, 'profile_inbox.jsonl');\n}\n\nfunction truncateNote(note: string): string {\n const oneLine = note.replace(/\\r?\\n/g, ' ').trim();\n return oneLine.length > MAX_NOTE_CHARS ? oneLine.slice(0, MAX_NOTE_CHARS) + '…' : oneLine;\n}\n\nexport function inboxAppend(\n guard: PathGuard,\n file: string,\n item: Omit<InboxItem, 'id'> & { id?: string },\n): InboxItem {\n const full: InboxItem = {\n id: item.id ?? randomUUID().slice(0, 8),\n kind: item.kind,\n at: item.at,\n ref: item.ref,\n note: truncateNote(item.note),\n };\n const prev = loadEncryptedText(guard, file) ?? '';\n saveEncryptedText(guard, file, prev + JSON.stringify(full) + '\\n');\n return full;\n}\n\nexport function inboxCount(guard: PathGuard, file: string): number {\n const raw = loadEncryptedText(guard, file);\n if (!raw) return 0;\n return raw.split('\\n').filter((l) => l.trim()).length;\n}\n\n/**\n * Drain all items. `commit` controls crash semantics: on consolidation failure\n * the inbox must SURVIVE (§3.7), so the caller drains with commit=false first,\n * and only rewrites an empty inbox after the run succeeded.\n */\nexport function inboxDrain(guard: PathGuard, file: string): InboxItem[] {\n const raw = loadEncryptedText(guard, file);\n if (!raw) return [];\n const items: InboxItem[] = [];\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n items.push(JSON.parse(trimmed) as InboxItem);\n } catch {\n // corrupt line: drop (queue is re-derivable from sources)\n }\n }\n return items;\n}\n\nexport function inboxClear(guard: PathGuard, file: string): void {\n saveEncryptedText(guard, file, '');\n}\n\n/** Dedupe key (§3.7): same source kind + same ref must not flood the queue. */\nexport function dedupeItems(items: InboxItem[]): InboxItem[] {\n const seen = new Set<string>();\n const out: InboxItem[] = [];\n for (const it of items) {\n const key = `${it.kind}#${it.ref}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(it);\n }\n return out;\n}\n\nexport function inboxHealthCheck(guard: PathGuard, file: string): { total: number; corrupt: number; duplicates: number } {\n const raw = loadEncryptedText(guard, file) ?? '';\n let total = 0;\n let corrupt = 0;\n const keys = new Set<string>();\n let duplicates = 0;\n for (const line of raw.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const it = JSON.parse(trimmed) as InboxItem;\n total += 1;\n const key = `${it.kind}#${it.ref}`;\n if (keys.has(key)) duplicates += 1;\n keys.add(key);\n } catch {\n corrupt += 1;\n }\n }\n return { total, corrupt, duplicates };\n}\n\nexport function inboxFileExists(guard: PathGuard, file: string): boolean {\n return fs.existsSync(guard.assert(file));\n}\n"],"mappings":";;;;;;AAIA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,kBAAkB;AAK3B,IAAM,iBAAiB;AAEhB,SAAS,cAAc,SAAyB;AACrD,SAAO,KAAK,KAAK,SAAS,qBAAqB;AACjD;AAEA,SAAS,aAAa,MAAsB;AAC1C,QAAM,UAAU,KAAK,QAAQ,UAAU,GAAG,EAAE,KAAK;AACjD,SAAO,QAAQ,SAAS,iBAAiB,QAAQ,MAAM,GAAG,cAAc,IAAI,WAAM;AACpF;AAEO,SAAS,YACd,OACA,MACA,MACW;AACX,QAAM,OAAkB;AAAA,IACtB,IAAI,KAAK,MAAM,WAAW,EAAE,MAAM,GAAG,CAAC;AAAA,IACtC,MAAM,KAAK;AAAA,IACX,IAAI,KAAK;AAAA,IACT,KAAK,KAAK;AAAA,IACV,MAAM,aAAa,KAAK,IAAI;AAAA,EAC9B;AACA,QAAM,OAAO,kBAAkB,OAAO,IAAI,KAAK;AAC/C,oBAAkB,OAAO,MAAM,OAAO,KAAK,UAAU,IAAI,IAAI,IAAI;AACjE,SAAO;AACT;AAEO,SAAS,WAAW,OAAkB,MAAsB;AACjE,QAAM,MAAM,kBAAkB,OAAO,IAAI;AACzC,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,IAAI,MAAM,IAAI,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE;AACjD;AAOO,SAAS,WAAW,OAAkB,MAA2B;AACtE,QAAM,MAAM,kBAAkB,OAAO,IAAI;AACzC,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,QAAqB,CAAC;AAC5B,aAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,KAAK,KAAK,MAAM,OAAO,CAAc;AAAA,IAC7C,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,WAAW,OAAkB,MAAoB;AAC/D,oBAAkB,OAAO,MAAM,EAAE;AACnC;AAGO,SAAS,YAAY,OAAiC;AAC3D,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAmB,CAAC;AAC1B,aAAW,MAAM,OAAO;AACtB,UAAM,MAAM,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG;AAChC,QAAI,KAAK,IAAI,GAAG,EAAG;AACnB,SAAK,IAAI,GAAG;AACZ,QAAI,KAAK,EAAE;AAAA,EACb;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,OAAkB,MAAsE;AACvH,QAAM,MAAM,kBAAkB,OAAO,IAAI,KAAK;AAC9C,MAAI,QAAQ;AACZ,MAAI,UAAU;AACd,QAAM,OAAO,oBAAI,IAAY;AAC7B,MAAI,aAAa;AACjB,aAAW,QAAQ,IAAI,MAAM,IAAI,GAAG;AAClC,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AACd,QAAI;AACF,YAAM,KAAK,KAAK,MAAM,OAAO;AAC7B,eAAS;AACT,YAAM,MAAM,GAAG,GAAG,IAAI,IAAI,GAAG,GAAG;AAChC,UAAI,KAAK,IAAI,GAAG,EAAG,eAAc;AACjC,WAAK,IAAI,GAAG;AAAA,IACd,QAAQ;AACN,iBAAW;AAAA,IACb;AAAA,EACF;AACA,SAAO,EAAE,OAAO,SAAS,WAAW;AACtC;AAEO,SAAS,gBAAgB,OAAkB,MAAuB;AACvE,SAAO,GAAG,WAAW,MAAM,OAAO,IAAI,CAAC;AACzC;","names":[]}
@@ -0,0 +1,235 @@
1
+ // node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.3/node_modules/@deepseek-ai/cosmokit/lib/index.js
2
+ function isNullable(value) {
3
+ return value === null || value === void 0;
4
+ }
5
+ function isPlainObject(data) {
6
+ return data && typeof data === "object" && !Array.isArray(data);
7
+ }
8
+ function filterKeys(object, filter) {
9
+ return Object.fromEntries(Object.entries(object).filter(([key, value]) => filter(key, value)));
10
+ }
11
+ function mapValues(object, transform) {
12
+ return Object.fromEntries(Object.entries(object).map(([key, value]) => [key, transform(value, key)]));
13
+ }
14
+ function pick(source, keys, forced) {
15
+ if (!keys) return { ...source };
16
+ const result = {};
17
+ for (const key of keys) if (forced || source[key] !== void 0) result[key] = source[key];
18
+ return result;
19
+ }
20
+ function defineProperty(object, key, value) {
21
+ return Object.defineProperty(object, key, {
22
+ writable: true,
23
+ value,
24
+ enumerable: false
25
+ });
26
+ }
27
+ function is(type, value) {
28
+ if (arguments.length === 1) return (value2) => is(type, value2);
29
+ return type in globalThis && value instanceof globalThis[type] || Object.prototype.toString.call(value).slice(8, -1) === type;
30
+ }
31
+ function isArrayBufferLike(value) {
32
+ return is("ArrayBuffer", value) || is("SharedArrayBuffer", value);
33
+ }
34
+ function isArrayBufferSource(value) {
35
+ return isArrayBufferLike(value) || ArrayBuffer.isView(value);
36
+ }
37
+ var Binary;
38
+ (function(Binary2) {
39
+ Binary2.is = isArrayBufferLike;
40
+ Binary2.isSource = isArrayBufferSource;
41
+ function fromSource(source) {
42
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
43
+ else return source;
44
+ }
45
+ Binary2.fromSource = fromSource;
46
+ function toBase64(source) {
47
+ source = fromSource(source);
48
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("base64");
49
+ let binary = "";
50
+ const bytes = new Uint8Array(source);
51
+ for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
52
+ return btoa(binary);
53
+ }
54
+ Binary2.toBase64 = toBase64;
55
+ function fromBase64(source) {
56
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "base64"));
57
+ return Uint8Array.from(atob(source), (c) => c.charCodeAt(0));
58
+ }
59
+ Binary2.fromBase64 = fromBase64;
60
+ function toHex(source) {
61
+ source = fromSource(source);
62
+ if (typeof Buffer !== "undefined") return Buffer.from(source).toString("hex");
63
+ return Array.from(new Uint8Array(source), (byte) => byte.toString(16).padStart(2, "0")).join("");
64
+ }
65
+ Binary2.toHex = toHex;
66
+ function fromHex(source) {
67
+ if (typeof Buffer !== "undefined") return fromSource(Buffer.from(source, "hex"));
68
+ const hex = source.length % 2 === 0 ? source : source.slice(0, source.length - 1);
69
+ const buffer = [];
70
+ for (let i = 0; i < hex.length; i += 2) buffer.push(parseInt(`${hex[i]}${hex[i + 1]}`, 16));
71
+ return Uint8Array.from(buffer).buffer;
72
+ }
73
+ Binary2.fromHex = fromHex;
74
+ })(Binary || (Binary = {}));
75
+ var base64ToArrayBuffer = Binary.fromBase64;
76
+ var arrayBufferToBase64 = Binary.toBase64;
77
+ var hexToArrayBuffer = Binary.fromHex;
78
+ var arrayBufferToHex = Binary.toHex;
79
+ function clone(source, refs = /* @__PURE__ */ new Map()) {
80
+ if (!source || typeof source !== "object") return source;
81
+ if (is("Date", source)) return new Date(source.valueOf());
82
+ if (is("RegExp", source)) return new RegExp(source.source, source.flags);
83
+ if (isArrayBufferLike(source)) return source.slice(0);
84
+ if (ArrayBuffer.isView(source)) return source.buffer.slice(source.byteOffset, source.byteOffset + source.byteLength);
85
+ const cached = refs.get(source);
86
+ if (cached) return cached;
87
+ if (Array.isArray(source)) {
88
+ const result2 = [];
89
+ refs.set(source, result2);
90
+ source.forEach((value, index) => {
91
+ result2[index] = Reflect.apply(clone, null, [value, refs]);
92
+ });
93
+ return result2;
94
+ }
95
+ const result = Object.create(Object.getPrototypeOf(source));
96
+ refs.set(source, result);
97
+ for (const key of Reflect.ownKeys(source)) {
98
+ const descriptor = { ...Reflect.getOwnPropertyDescriptor(source, key) };
99
+ if ("value" in descriptor) descriptor.value = Reflect.apply(clone, null, [descriptor.value, refs]);
100
+ Reflect.defineProperty(result, key, descriptor);
101
+ }
102
+ return result;
103
+ }
104
+ function deepEqual(a, b, strict) {
105
+ if (a === b) return true;
106
+ if (!strict && isNullable(a) && isNullable(b)) return true;
107
+ if (typeof a !== typeof b) return false;
108
+ if (typeof a !== "object") return false;
109
+ if (!a || !b) return false;
110
+ function check(test, then) {
111
+ return test(a) ? test(b) ? then(a, b) : false : test(b) ? false : void 0;
112
+ }
113
+ return check(Array.isArray, (a2, b2) => a2.length === b2.length && a2.every((item, index) => deepEqual(item, b2[index]))) ?? check(is("Date"), (a2, b2) => a2.valueOf() === b2.valueOf()) ?? check(is("RegExp"), (a2, b2) => a2.source === b2.source && a2.flags === b2.flags) ?? check(isArrayBufferLike, (a2, b2) => {
114
+ if (a2.byteLength !== b2.byteLength) return false;
115
+ const viewA = new Uint8Array(a2);
116
+ const viewB = new Uint8Array(b2);
117
+ for (let i = 0; i < viewA.length; i++) if (viewA[i] !== viewB[i]) return false;
118
+ return true;
119
+ }) ?? Object.keys({
120
+ ...a,
121
+ ...b
122
+ }).every((key) => deepEqual(a[key], b[key], strict));
123
+ }
124
+ function tokenize(source, delimiters, delimiter) {
125
+ const output = [];
126
+ let state = 0;
127
+ for (let i = 0; i < source.length; i++) {
128
+ const code = source.charCodeAt(i);
129
+ if (code >= 65 && code <= 90) {
130
+ if (state === 1) {
131
+ const next = source.charCodeAt(i + 1);
132
+ if (next >= 97 && next <= 122) output.push(delimiter);
133
+ output.push(code + 32);
134
+ } else {
135
+ if (state !== 0) output.push(delimiter);
136
+ output.push(code + 32);
137
+ }
138
+ state = 1;
139
+ } else if (code >= 97 && code <= 122) {
140
+ output.push(code);
141
+ state = 2;
142
+ } else if (delimiters.includes(code)) {
143
+ if (state !== 0) output.push(delimiter);
144
+ state = 0;
145
+ } else output.push(code);
146
+ }
147
+ return String.fromCharCode(...output);
148
+ }
149
+ function paramCase(source) {
150
+ return tokenize(source, [45, 95], 45);
151
+ }
152
+ var hyphenate = paramCase;
153
+ var Time;
154
+ (function(Time2) {
155
+ Time2.millisecond = 1;
156
+ Time2.second = 1e3;
157
+ Time2.minute = Time2.second * 60;
158
+ Time2.hour = Time2.minute * 60;
159
+ Time2.day = Time2.hour * 24;
160
+ Time2.week = Time2.day * 7;
161
+ let timezoneOffset = (/* @__PURE__ */ new Date()).getTimezoneOffset();
162
+ function setTimezoneOffset(offset) {
163
+ timezoneOffset = offset;
164
+ }
165
+ Time2.setTimezoneOffset = setTimezoneOffset;
166
+ function getTimezoneOffset() {
167
+ return timezoneOffset;
168
+ }
169
+ Time2.getTimezoneOffset = getTimezoneOffset;
170
+ function getDateNumber(date = /* @__PURE__ */ new Date(), offset) {
171
+ if (typeof date === "number") date = new Date(date);
172
+ if (offset === void 0) offset = timezoneOffset;
173
+ return Math.floor((date.valueOf() / Time2.minute - offset) / 1440);
174
+ }
175
+ Time2.getDateNumber = getDateNumber;
176
+ function fromDateNumber(value, offset) {
177
+ const date = new Date(value * Time2.day);
178
+ if (offset === void 0) offset = timezoneOffset;
179
+ return new Date(+date + offset * Time2.minute);
180
+ }
181
+ Time2.fromDateNumber = fromDateNumber;
182
+ const numeric = /\d+(?:\.\d+)?/.source;
183
+ const timeRegExp = new RegExp(`^${[
184
+ "w(?:eek(?:s)?)?",
185
+ "d(?:ay(?:s)?)?",
186
+ "h(?:our(?:s)?)?",
187
+ "m(?:in(?:ute)?(?:s)?)?",
188
+ "s(?:ec(?:ond)?(?:s)?)?"
189
+ ].map((unit) => `(${numeric}${unit})?`).join("")}$`);
190
+ function parseTime(source) {
191
+ const capture = timeRegExp.exec(source);
192
+ if (!capture) return 0;
193
+ return (parseFloat(capture[1]) * Time2.week || 0) + (parseFloat(capture[2]) * Time2.day || 0) + (parseFloat(capture[3]) * Time2.hour || 0) + (parseFloat(capture[4]) * Time2.minute || 0) + (parseFloat(capture[5]) * Time2.second || 0);
194
+ }
195
+ Time2.parseTime = parseTime;
196
+ function parseDate(date) {
197
+ const parsed = parseTime(date);
198
+ if (parsed) date = Date.now() + parsed;
199
+ else if (/^\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).toLocaleDateString()}-${date}`;
200
+ else if (/^\d{1,2}-\d{1,2}-\d{1,2}(:\d{1,2}){1,2}$/.test(date)) date = `${(/* @__PURE__ */ new Date()).getFullYear()}-${date}`;
201
+ return date ? new Date(date) : /* @__PURE__ */ new Date();
202
+ }
203
+ Time2.parseDate = parseDate;
204
+ function format(ms) {
205
+ const abs = Math.abs(ms);
206
+ if (abs >= Time2.day - Time2.hour / 2) return Math.round(ms / Time2.day) + "d";
207
+ else if (abs >= Time2.hour - Time2.minute / 2) return Math.round(ms / Time2.hour) + "h";
208
+ else if (abs >= Time2.minute - Time2.second / 2) return Math.round(ms / Time2.minute) + "m";
209
+ else if (abs >= Time2.second) return Math.round(ms / Time2.second) + "s";
210
+ return ms + "ms";
211
+ }
212
+ Time2.format = format;
213
+ function toDigits(source, length = 2) {
214
+ return source.toString().padStart(length, "0");
215
+ }
216
+ Time2.toDigits = toDigits;
217
+ function template(template2, time = /* @__PURE__ */ new Date()) {
218
+ return template2.replace("yyyy", time.getFullYear().toString()).replace("yy", time.getFullYear().toString().slice(2)).replace("MM", toDigits(time.getMonth() + 1)).replace("dd", toDigits(time.getDate())).replace("hh", toDigits(time.getHours())).replace("mm", toDigits(time.getMinutes())).replace("ss", toDigits(time.getSeconds())).replace("SSS", toDigits(time.getMilliseconds(), 3));
219
+ }
220
+ Time2.template = template;
221
+ })(Time || (Time = {}));
222
+
223
+ export {
224
+ isNullable,
225
+ isPlainObject,
226
+ filterKeys,
227
+ mapValues,
228
+ pick,
229
+ defineProperty,
230
+ Binary,
231
+ clone,
232
+ deepEqual,
233
+ hyphenate
234
+ };
235
+ //# sourceMappingURL=chunk-AISZRA4C.js.map