omp-vcc 0.1.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 +106 -0
  3. package/commands/omp-vcc.md +19 -0
  4. package/commands/vcc-recall.md +21 -0
  5. package/extensions/main.ts +319 -0
  6. package/extensions/vcc-core/commands/vcc-recall.ts +2 -0
  7. package/extensions/vcc-core/core/brief.ts +404 -0
  8. package/extensions/vcc-core/core/build-sections.ts +77 -0
  9. package/extensions/vcc-core/core/compact-args.ts +46 -0
  10. package/extensions/vcc-core/core/content.ts +157 -0
  11. package/extensions/vcc-core/core/drill-down.ts +299 -0
  12. package/extensions/vcc-core/core/filter-noise.ts +42 -0
  13. package/extensions/vcc-core/core/format-recall.ts +101 -0
  14. package/extensions/vcc-core/core/format.ts +82 -0
  15. package/extensions/vcc-core/core/lineage.ts +27 -0
  16. package/extensions/vcc-core/core/load-messages.ts +44 -0
  17. package/extensions/vcc-core/core/normalize.ts +66 -0
  18. package/extensions/vcc-core/core/rank.ts +284 -0
  19. package/extensions/vcc-core/core/recall-scope.ts +31 -0
  20. package/extensions/vcc-core/core/render-entries.ts +55 -0
  21. package/extensions/vcc-core/core/report.ts +233 -0
  22. package/extensions/vcc-core/core/sanitize.ts +6 -0
  23. package/extensions/vcc-core/core/search-entries.ts +576 -0
  24. package/extensions/vcc-core/core/settings.ts +151 -0
  25. package/extensions/vcc-core/core/skill-collapse.ts +36 -0
  26. package/extensions/vcc-core/core/summarize.ts +208 -0
  27. package/extensions/vcc-core/core/token-estimate.ts +101 -0
  28. package/extensions/vcc-core/core/tool-args.ts +17 -0
  29. package/extensions/vcc-core/details.ts +12 -0
  30. package/extensions/vcc-core/extract/commits.ts +70 -0
  31. package/extensions/vcc-core/extract/files.ts +88 -0
  32. package/extensions/vcc-core/extract/goals.ts +80 -0
  33. package/extensions/vcc-core/extract/preferences.ts +56 -0
  34. package/extensions/vcc-core/hook.ts +1017 -0
  35. package/extensions/vcc-core/sections.ts +9 -0
  36. package/extensions/vcc-core/types.ts +17 -0
  37. package/package.json +104 -0
  38. package/scripts/smoke.ts +116 -0
  39. package/scripts/uninstall-reset.js +73 -0
  40. package/skills/omp-vcc/SKILL.md +35 -0
  41. package/types.d.ts +114 -0
@@ -0,0 +1,9 @@
1
+ // @ts-nocheck
2
+ export interface SectionData {
3
+ sessionGoal: string[];
4
+ outstandingContext: string[];
5
+ filesAndChanges: string[];
6
+ commits: string[];
7
+ userPreferences: string[];
8
+ briefTranscript: string;
9
+ }
@@ -0,0 +1,17 @@
1
+ // @ts-nocheck
2
+ import type { Message } from "@oh-my-pi/pi-ai";
3
+
4
+ export type CompactionReason = "manual" | "threshold" | "overflow";
5
+
6
+ export interface FileOps {
7
+ readFiles?: string[];
8
+ modifiedFiles?: string[];
9
+ createdFiles?: string[];
10
+ }
11
+
12
+ export type NormalizedBlock =
13
+ | { kind: "user"; text: string; sourceIndex?: number }
14
+ | { kind: "assistant"; text: string; sourceIndex?: number }
15
+ | { kind: "tool_call"; name: string; args: Record<string, unknown>; sourceIndex?: number }
16
+ | { kind: "tool_result"; name: string; text: string; sourceIndex?: number }
17
+ | { kind: "bash"; command: string; output: string; exitCode: number | undefined; sourceIndex?: number };
package/package.json ADDED
@@ -0,0 +1,104 @@
1
+ {
2
+ "name": "omp-vcc",
3
+ "version": "0.1.0",
4
+ "description": "Algorithmic VCC compaction for omp - fast lossless no-LLM",
5
+ "type": "module",
6
+ "keywords": [
7
+ "oh-my-pi",
8
+ "omp",
9
+ "plugin"
10
+ ],
11
+ "license": "MIT",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/zhulinchng/omp-vcc.git"
15
+ },
16
+ "files": [
17
+ "extensions",
18
+ "skills",
19
+ "commands",
20
+ "scripts",
21
+ "types.d.ts"
22
+ ],
23
+ "omp": {
24
+ "description": "Algorithmic VCC compaction for omp - fast lossless no-LLM",
25
+ "extensions": [
26
+ "./extensions/main.ts"
27
+ ],
28
+ "commands": [
29
+ "./commands/omp-vcc.md",
30
+ "./commands/vcc-recall.md"
31
+ ],
32
+ "settings": {
33
+ "vccEnabled": {
34
+ "type": "boolean",
35
+ "default": true,
36
+ "description": "Enable VCC compaction interception (master switch)"
37
+ },
38
+ "overrideDefaultCompaction": {
39
+ "type": "boolean",
40
+ "default": true,
41
+ "description": "When true, omp-vcc handles all compactions (threshold/overflow/manual); when false, only /omp-vcc is handled"
42
+ },
43
+ "smartKeepTail": {
44
+ "type": "boolean",
45
+ "default": true,
46
+ "description": "Boost default keep:1 tail when small (5k→25k token window)"
47
+ },
48
+ "continueAfterThresholdCompact": {
49
+ "type": "boolean",
50
+ "default": true,
51
+ "description": "Auto-continue after threshold/overflow compaction"
52
+ },
53
+ "debug": {
54
+ "type": "boolean",
55
+ "default": false,
56
+ "description": "Write debug snapshot to /tmp/omp-vcc-debug.json"
57
+ }
58
+ }
59
+ },
60
+ "pi": {
61
+ "description": "Algorithmic VCC compaction for omp - fast lossless no-LLM",
62
+ "extensions": [
63
+ "./extensions/main.ts"
64
+ ],
65
+ "commands": [
66
+ "./commands/omp-vcc.md",
67
+ "./commands/vcc-recall.md"
68
+ ],
69
+ "settings": {
70
+ "vccEnabled": {
71
+ "type": "boolean",
72
+ "default": true,
73
+ "description": "Enable VCC compaction interception (master switch)"
74
+ },
75
+ "overrideDefaultCompaction": {
76
+ "type": "boolean",
77
+ "default": true,
78
+ "description": "When true, omp-vcc handles all compactions (threshold/overflow/manual); when false, only /omp-vcc is handled"
79
+ },
80
+ "smartKeepTail": {
81
+ "type": "boolean",
82
+ "default": true,
83
+ "description": "Boost default keep:1 tail when small (5k→25k token window)"
84
+ },
85
+ "continueAfterThresholdCompact": {
86
+ "type": "boolean",
87
+ "default": true,
88
+ "description": "Auto-continue after threshold/overflow compaction"
89
+ },
90
+ "debug": {
91
+ "type": "boolean",
92
+ "default": false,
93
+ "description": "Write debug snapshot to /tmp/omp-vcc-debug.json"
94
+ }
95
+ }
96
+ },
97
+ "scripts": {
98
+ "typecheck": "bunx tsc --noEmit",
99
+ "test": "bun test",
100
+ "smoke": "bun run scripts/smoke.ts",
101
+ "postuninstall": "node scripts/uninstall-reset.js || true",
102
+ "prepublishOnly": "npm run typecheck"
103
+ }
104
+ }
@@ -0,0 +1,116 @@
1
+ // Smoke checks for @zhulinchng/omp-vcc — host-free, zero deps
2
+ import extension from "../extensions/main.ts";
3
+ import { buildOwnCut } from "../extensions/vcc-core/hook.ts";
4
+ import { calibrateCharsPerToken } from "../extensions/vcc-core/core/token-estimate.ts";
5
+
6
+ let failures = 0;
7
+ function check(name: string, condition: boolean, detail = "") {
8
+ if (!condition) {
9
+ failures++;
10
+ console.error(`FAIL ${name}${detail ? ": " + detail : ""}`);
11
+ } else {
12
+ console.log(`ok ${name}`);
13
+ }
14
+ }
15
+
16
+ console.log("1. extension loads and registers");
17
+ try {
18
+ const handlers = new Map<string, unknown>();
19
+ const tools: any[] = [];
20
+ const commands: any[] = [];
21
+ const chain: any = {
22
+ describe: () => chain,
23
+ optional: () => chain,
24
+ };
25
+ const mockZod: any = {
26
+ object: (s: any) => s,
27
+ string: () => chain,
28
+ number: () => chain,
29
+ boolean: () => chain,
30
+ enum: () => chain,
31
+ array: () => chain,
32
+ };
33
+ const mockPi: any = {
34
+ on: (event: string, handler: unknown) => handlers.set(event, handler),
35
+ registerTool: (tool: any) => tools.push(tool),
36
+ registerCommand: (name: string, opts: any) => commands.push({ name, opts }),
37
+ zod: mockZod,
38
+ logger: {
39
+ info: () => {},
40
+ warn: () => {},
41
+ error: () => {},
42
+ debug: () => {},
43
+ },
44
+ };
45
+
46
+ await (extension as any)(mockPi);
47
+
48
+ check(
49
+ "session_before_compact hooked",
50
+ handlers.has("session_before_compact"),
51
+ );
52
+ check("context hooked", handlers.has("context"));
53
+ check("session_compact hooked", handlers.has("session_compact"));
54
+ check(
55
+ "vcc_recall registered",
56
+ tools.some((t) => t.name === "vcc_recall"),
57
+ );
58
+ check(
59
+ "omp-vcc command registered",
60
+ commands.some((c) => c.name === "omp-vcc"),
61
+ );
62
+ check(
63
+ "vcc-recall command registered",
64
+ commands.some((c) => c.name === "vcc-recall"),
65
+ );
66
+ check(
67
+ "pi-vcc alias registered",
68
+ commands.some((c) => c.name === "pi-vcc"),
69
+ );
70
+ } catch (e) {
71
+ check("extension loads", false, String(e));
72
+ }
73
+
74
+ console.log("2. vcc-core pipeline smoke");
75
+ try {
76
+ const result = buildOwnCut(
77
+ [
78
+ {
79
+ id: "m1",
80
+ type: "message",
81
+ message: { role: "user", content: "hello" },
82
+ },
83
+ {
84
+ id: "m2",
85
+ type: "message",
86
+ message: { role: "assistant", content: [{ type: "text", text: "hi" }] },
87
+ },
88
+ {
89
+ id: "m3",
90
+ type: "message",
91
+ message: { role: "user", content: "world" },
92
+ },
93
+ {
94
+ id: "m4",
95
+ type: "message",
96
+ message: {
97
+ role: "assistant",
98
+ content: [{ type: "text", text: "reply" }],
99
+ },
100
+ },
101
+ ] as any,
102
+ 1,
103
+ );
104
+ check("buildOwnCut ok", (result as any).ok === true);
105
+ const cal = calibrateCharsPerToken(1000, 250);
106
+ check("calibrateCharsPerToken", cal.charsPerToken === 4);
107
+ } catch (e) {
108
+ check("vcc-core pipeline", false, String(e));
109
+ }
110
+
111
+ console.log(
112
+ failures === 0
113
+ ? "\nAll smoke checks passed."
114
+ : `\n${failures} smoke check(s) FAILED.`,
115
+ );
116
+ process.exitCode = failures === 0 ? 0 : 1;
@@ -0,0 +1,73 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * postuninstall hook: reset ownership marker if this plugin owned global state.
4
+ * Generic postuninstall reset for ownership marker
5
+ * Marker: ~/.config/@zhulinchng/omp-vcc/.ownership.json with { state: "owned", previous: boolean }
6
+ */
7
+ import { readFileSync, rmSync, writeFileSync } from "node:fs";
8
+ import { homedir } from "node:os";
9
+ import { join } from "node:path";
10
+ import { pathToFileURL } from "node:url";
11
+
12
+ export function resetOwnedQuiet(home) {
13
+ const markerPath = join(
14
+ home,
15
+ ".config",
16
+ "@zhulinchng/omp-vcc",
17
+ ".ownership.json",
18
+ );
19
+ const configPath = join(home, ".omp", "agent", "config.yml");
20
+ let marker;
21
+ try {
22
+ marker = JSON.parse(readFileSync(markerPath, "utf8"));
23
+ } catch {
24
+ return "no-marker";
25
+ }
26
+ const clearMarker = () => {
27
+ try {
28
+ rmSync(markerPath, { force: true });
29
+ } catch {}
30
+ };
31
+ if (marker?.state !== "owned") {
32
+ clearMarker();
33
+ return "not-owned";
34
+ }
35
+ let content;
36
+ try {
37
+ content = readFileSync(configPath, "utf8");
38
+ } catch {
39
+ clearMarker();
40
+ return "config-missing";
41
+ }
42
+ const lines = content.split("\n");
43
+ let inStartup = false;
44
+ let changed = false;
45
+ for (let i = 0; i < lines.length; i++) {
46
+ const line = lines[i] ?? "";
47
+ if (/^startup:\s*$/.test(line)) inStartup = true;
48
+ else if (inStartup && /^[^ \t]/.test(line) && line.trim() !== "")
49
+ inStartup = false;
50
+ if (inStartup && /quiet:\s*true/.test(line)) {
51
+ lines[i] = line.replace("true", "false");
52
+ changed = true;
53
+ break;
54
+ }
55
+ }
56
+ if (changed) {
57
+ try {
58
+ writeFileSync(configPath, lines.join("\n"), "utf8");
59
+ } catch {
60
+ return "write-failed";
61
+ }
62
+ }
63
+ clearMarker();
64
+ return changed ? "restored" : "already-default";
65
+ }
66
+
67
+ if (
68
+ process.argv[1] &&
69
+ import.meta.url === pathToFileURL(process.argv[1]).href
70
+ ) {
71
+ const result = resetOwnedQuiet(homedir());
72
+ console.log(`uninstall-reset: ${result}`);
73
+ }
@@ -0,0 +1,35 @@
1
+ # omp-vcc Skill — VCC-Inspired Algorithmic Compaction
2
+
3
+ > Lossless, transcript-preserving structured summaries — no LLM calls. Based on `sting8k/pi-vcc` (TypeScript) and `lllyasviel/VCC` (View-oriented Conversation Compiler, arXiv 2603.29678).
4
+
5
+ ## Philosophy
6
+
7
+ - **Agent trace as structured document** (`user`, `assistant`, `thinking`, `tool_call/result`, `subagent`, compaction boundaries, harness directives). Like VCC's `V_full` identity view defines a line-number coordinate system; `V_ui` gives a one-line tool summary (`* Read "src/pets.py" (file.txt:18-20)`); `V_adapt(b, ρ)` projects via relevance predicate `ρ` (regex / BM25 / embedding / LLM) preserving turn headers, role tags, and `(f:s-e)` pointers.
8
+ - **Fast, deterministic, lossless** — pure extraction, not LLM summarization. 30–470 ms, 35–99% context reduction, pointer invariant `V_ui → V_full[s:e]` holds structurally (SSA-like).
9
+ - **Progressive disclosure** — brief transcript → ranked recall via `vcc_recall` (`ρ = regex→OR`) → drill-down `#N:path` resolves `(session.jsonl:s-e)` into the full view.
10
+
11
+ ## Pipeline
12
+
13
+ Calibrated `charsPerToken` from `preparation.tokensBefore` (heuristic fallback 4) → Smart keep-tail (5 k → 25 k) → Build own cut (`firstKeptEntryId`, orphan recovery) → Token-budget tail rescue (`no_anchor` / `oversized_tail` ×2.5) → Normalize (IR lex: escaped JSON→`|` block, `digits→` strip, `<system-reminder>` filter, `TodoWrite`/`ToolSearch`/ANSI strip, `same message.id` merge, `queue-operation` discard, base64 image extract) → Filter noise → Build 5 sections (Session Goal, Files And Changes, Commits, Outstanding Context, User Preferences) → Ranked brief transcript (TF-IDF, `RANKED_BRIEF_BUDGET_TOKENS=1100` ceiling 2000, ~15 tok/block) → Format bracketed sections + separator `---` → Bounded merge (sticky dedup, volatile replace, transcript roll, 120-line cap).
14
+
15
+ ## Usage
16
+
17
+ - **Auto**: threshold/overflow compaction intercepts `session_before_compact` when `overrideDefaultCompaction=true` (default). No LLM summary; token-budgeted.
18
+ - **Manual**: `/omp-vcc [keep:N] [focus]` — e.g. `/omp-vcc keep:2 fix auth` keeps last 2 user turns. Also `/pi-vcc` alias.
19
+ - **Recall**: `vcc_recall({query:"redis cache", scope:"all", page:1})` or `/vcc-recall hook|inject scope:all page:2`. Regex first, then TF-IDF OR fallback. `mode:'touched'` lists files, `#12:src/auth.ts` drills. 5 per page.
20
+
21
+ ## Configuration
22
+
23
+ File `~/.omp/omp-vcc/config.json` (XDG-aware via `$OMP_DIR`/`$PI_CODING_AGENT_DIR`/`$OMP_VCC_CONFIG_PATH`, migrates legacy `~/.pi/agent/pi-vcc-config.json`). Manifest `omp.settings` also exposed: `vccEnabled`, `overrideDefaultCompaction`, `smartKeepTail`, `continueAfterThresholdCompact`, `debug` (`/tmp/omp-vcc-debug.json`).
24
+
25
+ Optional native strategy: add `vcc` to `COMPACTION_METHOD_CHOICES` in `oh-my-pi` (see `docs/configuration.md#native-strategy`).
26
+
27
+ ## Verification
28
+
29
+ `bunx tsc --noEmit` → `bun test` → `bun run smoke` → `omp plugin link` → `/omp-vcc keep:1` shows `[Session Goal]` toast `omp-vcc: kept 1/5 turns, ~2.1k tok`.
30
+
31
+ ## Related
32
+
33
+ - VCC paper `arxiv:2603.29678` — three views, AppWorld evaluation (+1.1–4.2 pts, ½–⅔ tokens)
34
+ - `sting8k/pi-vcc` `@0.7.0` — ported core `extensions/vcc-core/*` verbatim, imports adapted to `@oh-my-pi/*`
35
+ - `lllyasviel/VCC` `VCC.py` — adaptive `SEP`, `match_lines`, transposed modalities
package/types.d.ts ADDED
@@ -0,0 +1,114 @@
1
+ // Ambient types for dual omp/pi compatibility — zero-build TS loading
2
+ // Import specifier is rewritten by host (legacy-pi-compat.ts) for Pi.
3
+ // This file enables tsc --noEmit without a build step and satisfies imports
4
+ // from generated extensions/tools/hooks without installing the host package.
5
+ declare module "@oh-my-pi/pi-coding-agent" {
6
+ export interface ExtensionContext {
7
+ logger: { info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void; debug(...args: unknown[]): void };
8
+ ui: { notify(msg: string, level?: string): void; setWidget?: unknown; setHeader?: unknown; [key: string]: unknown };
9
+ cwd: string;
10
+ mode?: string;
11
+ hasUI?: boolean;
12
+ sessionManager: {
13
+ getSessionFile(): string | undefined;
14
+ getBranch(fromId?: string): any[];
15
+ getEntries(): any[];
16
+ };
17
+ compact(instructionsOrOptions?: string | any): Promise<void>;
18
+ sendMessage?: any;
19
+ sendUserMessage?: any;
20
+ [key: string]: unknown;
21
+ }
22
+ export interface ExtensionCommandContext extends ExtensionContext {
23
+ compact(instructionsOrOptions?: string | any): Promise<void>;
24
+ }
25
+ export interface ExtensionAPI {
26
+ registerTool(tool: unknown): void;
27
+ registerCommand(name: string, opts: unknown): void;
28
+ on(event: string, handler: (event: unknown, ctx: ExtensionContext) => unknown): void;
29
+ zod: {
30
+ object(shape: Record<string, unknown>): any;
31
+ string(): any;
32
+ number(): any;
33
+ boolean(): any;
34
+ enum(values: string[]): any;
35
+ array(item: unknown): any;
36
+ optional(item: unknown): any;
37
+ };
38
+ arktype: unknown;
39
+ typebox: unknown;
40
+ ui?: unknown;
41
+ logger: { info(...args: unknown[]): void; warn(...args: unknown[]): void; error(...args: unknown[]): void; debug(...args: unknown[]): void };
42
+ cwd: string;
43
+ hasUI: boolean;
44
+ getFlag(name: string): unknown;
45
+ sendMessage?(message: unknown, options?: unknown): void;
46
+ sendUserMessage?(content: unknown, options?: unknown): void | Promise<void>;
47
+ [key: string]: unknown;
48
+ }
49
+ export type HookFactory = (pi: ExtensionAPI) => void | Promise<void>;
50
+ export type CustomToolFactory = (pi: { zod: ExtensionAPI["zod"] }) => unknown;
51
+ export const zod: ExtensionAPI["zod"];
52
+ export function convertToLlm(messages: any[]): any[];
53
+ }
54
+
55
+ declare module "@oh-my-pi/pi-coding-agent/session/messages" {
56
+ export function convertToLlm(messages: any[]): any[];
57
+ export const USER_INTERRUPT_LABEL: string;
58
+ export const SILENT_ABORT_MARKER: string;
59
+ export type CustomMessage<T = unknown> = any;
60
+ }
61
+
62
+ declare module "@oh-my-pi/pi-ai" {
63
+ export type Message = any;
64
+ export type TextContent = any;
65
+ export type ImageContent = any;
66
+ export type ToolCallContent = any;
67
+ }
68
+
69
+ declare module "@earendil-works/pi-coding-agent" {
70
+ export * from "@oh-my-pi/pi-coding-agent";
71
+ }
72
+ declare module "@earendil-works/pi-coding-agent/session/messages" {
73
+ export * from "@oh-my-pi/pi-coding-agent/session/messages";
74
+ }
75
+ declare module "@earendil-works/pi-ai" {
76
+ export * from "@oh-my-pi/pi-ai";
77
+ }
78
+ declare module "@mariozechner/pi-coding-agent" {
79
+ export * from "@oh-my-pi/pi-coding-agent";
80
+ }
81
+ declare module "@mariozechner/pi-ai" {
82
+ export * from "@oh-my-pi/pi-ai";
83
+ }
84
+
85
+ // Node shims for smoke/tests without @types/node
86
+ declare const process: { exitCode?: number; argv: string[]; exit(code?: number): never; env: Record<string, string | undefined> };
87
+ declare module "node:test" {
88
+ export function describe(name: string, fn: () => void): void;
89
+ export function it(name: string, fn: () => unknown): void;
90
+ export const assert: { ok(value: unknown, msg?: string): void; equal(a: unknown, b: unknown, msg?: string): void; deepEqual(a: unknown, b: unknown, msg?: string): void };
91
+ }
92
+ declare module "node:fs" {
93
+ export function readFileSync(path: string, encoding: string): string;
94
+ export function writeFileSync(path: string, data: string, encoding?: string): void;
95
+ export function rmSync(path: string, opts?: unknown): void;
96
+ export function mkdtempSync(prefix: string): string;
97
+ export function mkdirSync(path: string, opts?: unknown): void;
98
+ export function existsSync(path: string): boolean;
99
+ }
100
+ declare module "node:os" {
101
+ export function homedir(): string;
102
+ export function tmpdir(): string;
103
+ }
104
+ declare module "node:path" {
105
+ export function join(...parts: string[]): string;
106
+ export function resolve(...parts: string[]): string;
107
+ export function dirname(path: string): string;
108
+ }
109
+ declare module "node:url" {
110
+ export function pathToFileURL(path: string): { href: string };
111
+ }
112
+ declare module "node:module" {
113
+ export function createRequire(filename: string): (id: string) => any;
114
+ }