litmus-cli 1.4.11 → 1.4.13

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 (46) hide show
  1. package/dist/commands/doctor.d.ts.map +1 -1
  2. package/dist/commands/doctor.js +10 -8
  3. package/dist/commands/doctor.js.map +1 -1
  4. package/dist/commands/init.d.ts +5 -2
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +49 -25
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/status.d.ts.map +1 -1
  9. package/dist/commands/status.js +15 -3
  10. package/dist/commands/status.js.map +1 -1
  11. package/dist/commands/submit.js +2 -1
  12. package/dist/commands/submit.js.map +1 -1
  13. package/dist/lib/ai-tracking.d.ts +32 -2
  14. package/dist/lib/ai-tracking.d.ts.map +1 -1
  15. package/dist/lib/ai-tracking.js +153 -3
  16. package/dist/lib/ai-tracking.js.map +1 -1
  17. package/dist/lib/backfill-tools.d.ts +42 -0
  18. package/dist/lib/backfill-tools.d.ts.map +1 -1
  19. package/dist/lib/backfill-tools.js +266 -1
  20. package/dist/lib/backfill-tools.js.map +1 -1
  21. package/dist/lib/backfill.d.ts +2 -2
  22. package/dist/lib/backfill.d.ts.map +1 -1
  23. package/dist/lib/backfill.js.map +1 -1
  24. package/dist/lib/capture-nudge.d.ts +118 -0
  25. package/dist/lib/capture-nudge.d.ts.map +1 -0
  26. package/dist/lib/capture-nudge.js +342 -0
  27. package/dist/lib/capture-nudge.js.map +1 -0
  28. package/dist/lib/hook-logger.cjs +28 -4
  29. package/dist/lib/notice-ownership.d.ts +13 -0
  30. package/dist/lib/notice-ownership.d.ts.map +1 -0
  31. package/dist/lib/notice-ownership.js +123 -0
  32. package/dist/lib/notice-ownership.js.map +1 -0
  33. package/dist/lib/notify.d.ts +25 -0
  34. package/dist/lib/notify.d.ts.map +1 -0
  35. package/dist/lib/notify.js +68 -0
  36. package/dist/lib/notify.js.map +1 -0
  37. package/dist/lib/shadow.d.ts +9 -0
  38. package/dist/lib/shadow.d.ts.map +1 -1
  39. package/dist/lib/shadow.js +26 -3
  40. package/dist/lib/shadow.js.map +1 -1
  41. package/dist/lib/watcher.js +96 -3
  42. package/dist/lib/watcher.js.map +1 -1
  43. package/dist/lib/zip.d.ts.map +1 -1
  44. package/dist/lib/zip.js +22 -1
  45. package/dist/lib/zip.js.map +1 -1
  46. package/package.json +1 -1
@@ -0,0 +1,123 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+ import { createHash } from "crypto";
5
+ // ── Ownership registry for candidate-facing notice files (ENG-1857) ──
6
+ // The notice files (LITMUS-AI-NOTICE.md, LITMUS-REPAIR.md) sit world-readable
7
+ // at the assessment root, so nothing IN a file can prove this CLI wrote it —
8
+ // any sentinel or marker line can be copied into a look-alike, and a
9
+ // candidate's edits to a generated notice would still carry it. Ownership
10
+ // therefore lives OUTSIDE the file: a registry records the sha256 of exactly
11
+ // the content we wrote, and only a byte-identical file is ever treated as
12
+ // ours. The moment a candidate edits our notice — or plants their own — the
13
+ // hash stops matching and the file is theirs: its change events flow, it
14
+ // ships in the submission zip, and no cleanup path may overwrite or remove it.
15
+ //
16
+ // The registry lives in HOME scope (`~/.litmus/notice-owners.json`, keyed by
17
+ // project realpath), not in the project's own `.litmus/`, so it is outside
18
+ // everything that operates on the assessment tree — the candidate's editor
19
+ // and agents, `git clean`, the zip, the watcher. To be explicit about the
20
+ // boundary this does NOT move: everything on a candidate-owned machine is
21
+ // ultimately candidate-writable (activity.jsonl included), so no client-side
22
+ // state can be adversarially trustworthy. This registry's job is preventing
23
+ // ACCIDENTAL destruction or suppression of candidate work; a candidate who
24
+ // deliberately forges an entry can only self-suppress a root file with one of
25
+ // these two fixed names — which gains them nothing over simply keeping that
26
+ // file outside the assessment directory, where it was never evidence at all.
27
+ const OWNERS_FILE_NAME = "notice-owners.json";
28
+ // Same ceiling as the notices themselves (~1KB each): anything larger at one
29
+ // of these paths is candidate work regardless of what the registry says.
30
+ const MAX_NOTICE_BYTES = 64 * 1024;
31
+ // Resolved per call (not at module load): tests fake HOME, and vitest caches
32
+ // modules — a load-time constant would lock the first test's home in place.
33
+ function ownersPath() {
34
+ return path.join(os.homedir(), ".litmus", OWNERS_FILE_NAME);
35
+ }
36
+ /** Registry keys are project realpaths: `process.cwd()` is realpath-resolved
37
+ * but callers may hold raw paths, and on macOS `/var` is a symlink to
38
+ * `/private/var` — raw-string keys would silently never match. */
39
+ function projectKey(projectDir) {
40
+ try {
41
+ return fs.realpathSync(projectDir);
42
+ }
43
+ catch {
44
+ return projectDir;
45
+ }
46
+ }
47
+ function contentHash(content) {
48
+ return createHash("sha256").update(content, "utf8").digest("hex");
49
+ }
50
+ /** Tolerant load: unreadable or malformed means an empty registry — doubt is
51
+ * always resolved toward "the candidate's file". */
52
+ function loadOwners() {
53
+ try {
54
+ const raw = JSON.parse(fs.readFileSync(ownersPath(), "utf8"));
55
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw))
56
+ return {};
57
+ const registry = {};
58
+ for (const [project, entries] of Object.entries(raw)) {
59
+ if (typeof entries !== "object" || entries === null || Array.isArray(entries))
60
+ continue;
61
+ const owned = {};
62
+ for (const [name, hash] of Object.entries(entries)) {
63
+ if (typeof hash === "string" && /^[0-9a-f]{64}$/.test(hash))
64
+ owned[name] = hash;
65
+ }
66
+ if (Object.keys(owned).length)
67
+ registry[project] = owned;
68
+ }
69
+ return registry;
70
+ }
71
+ catch {
72
+ return {};
73
+ }
74
+ }
75
+ function saveOwners(registry) {
76
+ try {
77
+ fs.mkdirSync(path.dirname(ownersPath()), { recursive: true });
78
+ fs.writeFileSync(ownersPath(), JSON.stringify(registry), "utf8");
79
+ }
80
+ catch { /* best effort — worst case a notice we wrote is treated as the candidate's */ }
81
+ }
82
+ /** Record that this CLI just wrote `content` to the root file `fileName`.
83
+ * Call immediately after every write of a notice file. */
84
+ export function recordOwnedNotice(projectDir, fileName, content) {
85
+ const registry = loadOwners();
86
+ const key = projectKey(projectDir);
87
+ registry[key] = { ...registry[key], [fileName]: contentHash(content) };
88
+ saveOwners(registry);
89
+ }
90
+ /** Forget a notice we removed (or gave up on). */
91
+ export function clearOwnedNotice(projectDir, fileName) {
92
+ const registry = loadOwners();
93
+ const key = projectKey(projectDir);
94
+ const owned = registry[key];
95
+ if (!owned || !(fileName in owned))
96
+ return;
97
+ delete owned[fileName];
98
+ if (!Object.keys(owned).length)
99
+ delete registry[key];
100
+ saveOwners(registry);
101
+ }
102
+ /**
103
+ * True only when the root file `fileName` exists and is byte-identical to
104
+ * content this CLI recorded writing for this project. Never throws; a missing
105
+ * file, missing record, unreadable content, oversize file, or hash mismatch
106
+ * are all "not ours".
107
+ */
108
+ export function isOwnedNoticeFile(projectDir, fileName) {
109
+ const recorded = loadOwners()[projectKey(projectDir)]?.[fileName];
110
+ if (!recorded)
111
+ return false;
112
+ try {
113
+ const abs = path.join(projectDir, fileName);
114
+ const stat = fs.statSync(abs);
115
+ if (!stat.isFile() || stat.size > MAX_NOTICE_BYTES)
116
+ return false;
117
+ return contentHash(fs.readFileSync(abs, "utf8")) === recorded;
118
+ }
119
+ catch {
120
+ return false;
121
+ }
122
+ }
123
+ //# sourceMappingURL=notice-ownership.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notice-ownership.js","sourceRoot":"","sources":["../../src/lib/notice-ownership.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Best-effort desktop notification (ENG-1857) — zero dependencies, silent
3
+ * failure. Used by the watcher to reach a candidate whose terminal (and
4
+ * tracker.log) they are not looking at. It must never throw and never block:
5
+ * every path is a fire-and-forget execFile with a 5s timeout, and a missing
6
+ * binary (no notify-send, locked-down PowerShell) is simply a no-op.
7
+ */
8
+ import { execFile } from "child_process";
9
+ /**
10
+ * The command for this platform, or null when there is nothing sensible to
11
+ * run. Pure — split out so tests can check construction (quote escaping)
12
+ * without spawning anything.
13
+ */
14
+ export declare function buildNotifyCommand(platform: NodeJS.Platform, title: string, message: string): {
15
+ cmd: string;
16
+ args: string[];
17
+ } | null;
18
+ /**
19
+ * Show a desktop notification. Never throws, never blocks the caller;
20
+ * failure of any kind (missing binary, DE without a notification daemon,
21
+ * headless session) is silently ignored — the notice file is the durable
22
+ * fallback surface.
23
+ */
24
+ export declare function desktopNotify(title: string, message: string, exec?: typeof execFile): void;
25
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAYxC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,IAAI,CA4BxC;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,OAAO,QAAmB,GAC/B,IAAI,CAaN"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Best-effort desktop notification (ENG-1857) — zero dependencies, silent
3
+ * failure. Used by the watcher to reach a candidate whose terminal (and
4
+ * tracker.log) they are not looking at. It must never throw and never block:
5
+ * every path is a fire-and-forget execFile with a 5s timeout, and a missing
6
+ * binary (no notify-send, locked-down PowerShell) is simply a no-op.
7
+ */
8
+ import { execFile } from "child_process";
9
+ /** AppleScript string literal: backslashes and double quotes escaped. */
10
+ function appleScriptString(s) {
11
+ return `"${s.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
12
+ }
13
+ /** PowerShell single-quoted string literal: single quotes doubled. */
14
+ function powerShellString(s) {
15
+ return `'${s.replace(/'/g, "''")}'`;
16
+ }
17
+ /**
18
+ * The command for this platform, or null when there is nothing sensible to
19
+ * run. Pure — split out so tests can check construction (quote escaping)
20
+ * without spawning anything.
21
+ */
22
+ export function buildNotifyCommand(platform, title, message) {
23
+ if (platform === "darwin") {
24
+ return {
25
+ cmd: "osascript",
26
+ args: ["-e", `display notification ${appleScriptString(message)} with title ${appleScriptString(title)}`],
27
+ };
28
+ }
29
+ if (platform === "win32") {
30
+ // WinRT toast via PowerShell — present on every supported Windows, no
31
+ // module install needed. Text is injected as single-quoted PS literals
32
+ // and lands in XML text nodes (CreateTextNode), so no XML escaping is
33
+ // needed on top.
34
+ const script = [
35
+ "[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null",
36
+ "$template = [Windows.UI.Notifications.ToastNotificationManager]::GetTemplateContent([Windows.UI.Notifications.ToastTemplateType]::ToastText02)",
37
+ "$texts = $template.GetElementsByTagName('text')",
38
+ `$texts.Item(0).AppendChild($template.CreateTextNode(${powerShellString(title)})) | Out-Null`,
39
+ `$texts.Item(1).AppendChild($template.CreateTextNode(${powerShellString(message)})) | Out-Null`,
40
+ "$toast = [Windows.UI.Notifications.ToastNotification]::new($template)",
41
+ `[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier(${powerShellString("Litmus")}).Show($toast)`,
42
+ ].join("; ");
43
+ return { cmd: "powershell", args: ["-NoProfile", "-NonInteractive", "-Command", script] };
44
+ }
45
+ if (platform === "linux") {
46
+ // notify-send if present; execFile's ENOENT is swallowed below when not.
47
+ return { cmd: "notify-send", args: [title, message] };
48
+ }
49
+ return null;
50
+ }
51
+ /**
52
+ * Show a desktop notification. Never throws, never blocks the caller;
53
+ * failure of any kind (missing binary, DE without a notification daemon,
54
+ * headless session) is silently ignored — the notice file is the durable
55
+ * fallback surface.
56
+ */
57
+ export function desktopNotify(title, message, exec = execFile) {
58
+ try {
59
+ const command = buildNotifyCommand(process.platform, title, message);
60
+ if (!command)
61
+ return;
62
+ const child = exec(command.cmd, command.args, { timeout: 5000, windowsHide: true }, () => { });
63
+ // Detach from the event loop so a slow notifier can't hold the watcher up.
64
+ child.unref?.();
65
+ }
66
+ catch { /* never let a notification failure surface */ }
67
+ }
68
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/lib/notify.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC,yEAAyE;AACzE,SAAS,iBAAiB,CAAC,CAAS;IAClC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,GAAG,CAAA;AAC7D,CAAC;AAED,sEAAsE;AACtE,SAAS,gBAAgB,CAAC,CAAS;IACjC,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAA;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAChC,QAAyB,EACzB,KAAa,EACb,OAAe;IAEf,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAC1B,OAAO;YACL,GAAG,EAAE,WAAW;YAChB,IAAI,EAAE,CAAC,IAAI,EAAE,wBAAwB,iBAAiB,CAAC,OAAO,CAAC,eAAe,iBAAiB,CAAC,KAAK,CAAC,EAAE,CAAC;SAC1G,CAAA;IACH,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,sEAAsE;QACtE,uEAAuE;QACvE,sEAAsE;QACtE,iBAAiB;QACjB,MAAM,MAAM,GAAG;YACb,wHAAwH;YACxH,gJAAgJ;YAChJ,iDAAiD;YACjD,uDAAuD,gBAAgB,CAAC,KAAK,CAAC,eAAe;YAC7F,uDAAuD,gBAAgB,CAAC,OAAO,CAAC,eAAe;YAC/F,uEAAuE;YACvE,4EAA4E,gBAAgB,CAAC,QAAQ,CAAC,gBAAgB;SACvH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACZ,OAAO,EAAE,GAAG,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,CAAA;IAC3F,CAAC;IACD,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;QACzB,yEAAyE;QACzE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAA;IACvD,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAC3B,KAAa,EACb,OAAe,EACf,OAAwB,QAAQ;IAEhC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,kBAAkB,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,CAAA;QACpE,IAAI,CAAC,OAAO;YAAE,OAAM;QACpB,MAAM,KAAK,GAAG,IAAI,CAChB,OAAO,CAAC,GAAG,EACX,OAAO,CAAC,IAAI,EACZ,EAAE,OAAO,EAAE,IAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EACrC,GAAG,EAAE,GAAoD,CAAC,CAC3D,CAAA;QACD,2EAA2E;QAC3E,KAAK,CAAC,KAAK,EAAE,EAAE,CAAA;IACjB,CAAC;IAAC,MAAM,CAAC,CAAC,8CAA8C,CAAC,CAAC;AAC5D,CAAC"}
@@ -77,6 +77,15 @@ export declare function deleteShadowForProject(projectDir: string): void;
77
77
  */
78
78
  export declare function pruneShadows(registryPaths: string[]): number;
79
79
  export declare const REPAIR_FILE_NAME = "LITMUS-REPAIR.md";
80
+ /** The repair file's title line. A LABEL, not an ownership proof — the file
81
+ * is world-readable, so any content can be copied into a look-alike.
82
+ * Ownership is the recorded content hash (notice-ownership.ts): write and
83
+ * remove below, and the watcher/zip via isCliNoticeArtifact, only ever touch
84
+ * a file byte-identical to what this CLI recorded writing (ENG-1857). A
85
+ * repair file left by an older CLI (no recorded hash) is treated as the
86
+ * candidate's: never removed, and it ships in the zip — the pre-ENG-1857
87
+ * status quo, and the safe direction when in doubt. */
88
+ export declare const REPAIR_FILE_MARKER = "# Litmus needs a one-minute repair";
80
89
  /** Written by the watcher only when self-heal could not act (server
81
90
  * unreachable, no usable shadow, AND the in-memory restore failed to write).
82
91
  * Rewritten every heartbeat while the outage lasts — a cleanup pass that
@@ -1 +1 @@
1
- {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;wEACoE;IACpE,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CAClB;AAMD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAOD,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AA6BD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAcnF;AAiBD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQ9E;AAED,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAO9E;AAcD;;+DAE+D;AAC/D,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAS5F;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAU9E;AAUD,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9D,IAAI,CASN;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAYlF;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAOzH;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQhH;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQjH;AAID,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAQpE;AAED;+DAC+D;AAC/D,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAe5D;AAID,eAAO,MAAM,gBAAgB,qBAAqB,CAAA;AAElD;;;;;;;;;6EAS6E;AAC7E,wBAAgB,iBAAiB,IAAI,MAAM,CAoB1C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAEzD"}
1
+ {"version":3,"file":"shadow.d.ts","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,MAAM,WAAW,YAAa,SAAQ,YAAY;IAChD;wEACoE;IACpE,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;CAClB;AAMD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAOD,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGhE;AAED,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGjE;AA6BD,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAcnF;AAiBD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAQ9E;AAED,qEAAqE;AACrE,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAO9E;AAcD;;+DAE+D;AAC/D,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAS5F;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAU9E;AAUD,eAAO,MAAM,gBAAgB,gBAAgB,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,oEAAoE;AACpE,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC9D,IAAI,CASN;AAED,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAYlF;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAOzH;AAED,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQhH;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,eAAe,GAAG,MAAM,GAAG,IAAI,CAQjH;AAID,wBAAgB,yBAAyB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAQpE;AAED;+DAC+D;AAC/D,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAG/D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAe5D;AAID,eAAO,MAAM,gBAAgB,qBAAqB,CAAA;AAElD;;;;;;;wDAOwD;AACxD,eAAO,MAAM,kBAAkB,uCAAuC,CAAA;AAEtE;;;;;;;;;6EAS6E;AAC7E,wBAAgB,iBAAiB,IAAI,MAAM,CAoB1C;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAUxD;AAED,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CASzD"}
@@ -1,6 +1,7 @@
1
1
  import fs from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
+ import { clearOwnedNotice, isOwnedNoticeFile, recordOwnedNotice } from "./notice-ownership.js";
4
5
  function litmusHome() {
5
6
  return path.join(os.homedir(), ".litmus");
6
7
  }
@@ -255,6 +256,15 @@ export function pruneShadows(registryPaths) {
255
256
  }
256
257
  // ── Candidate-facing repair file (last resort) ────────────────────
257
258
  export const REPAIR_FILE_NAME = "LITMUS-REPAIR.md";
259
+ /** The repair file's title line. A LABEL, not an ownership proof — the file
260
+ * is world-readable, so any content can be copied into a look-alike.
261
+ * Ownership is the recorded content hash (notice-ownership.ts): write and
262
+ * remove below, and the watcher/zip via isCliNoticeArtifact, only ever touch
263
+ * a file byte-identical to what this CLI recorded writing (ENG-1857). A
264
+ * repair file left by an older CLI (no recorded hash) is treated as the
265
+ * candidate's: never removed, and it ships in the zip — the pre-ENG-1857
266
+ * status quo, and the safe direction when in doubt. */
267
+ export const REPAIR_FILE_MARKER = "# Litmus needs a one-minute repair";
258
268
  /** Written by the watcher only when self-heal could not act (server
259
269
  * unreachable, no usable shadow, AND the in-memory restore failed to write).
260
270
  * Rewritten every heartbeat while the outage lasts — a cleanup pass that
@@ -267,7 +277,7 @@ export const REPAIR_FILE_NAME = "LITMUS-REPAIR.md";
267
277
  * `litmus doctor` run bare prompts for the token pointing at that panel. */
268
278
  export function repairFileContent() {
269
279
  return [
270
- "# Litmus needs a one-minute repair",
280
+ REPAIR_FILE_MARKER,
271
281
  "",
272
282
  "Your assessment's tracking settings went missing, so your activity is not",
273
283
  "being recorded and `litmus submit` will not work until they are restored.",
@@ -288,13 +298,26 @@ export function repairFileContent() {
288
298
  }
289
299
  export function writeRepairFile(projectDir) {
290
300
  try {
291
- fs.writeFileSync(path.join(projectDir, REPAIR_FILE_NAME), repairFileContent(), "utf8");
301
+ const target = path.join(projectDir, REPAIR_FILE_NAME);
302
+ // Never clobber a file we didn't verifiably write ourselves (ENG-1857) —
303
+ // losing candidate work would be worse than a missing repair prompt.
304
+ if (fs.existsSync(target) && !isOwnedNoticeFile(projectDir, REPAIR_FILE_NAME))
305
+ return;
306
+ const content = repairFileContent();
307
+ fs.writeFileSync(target, content, "utf8");
308
+ recordOwnedNotice(projectDir, REPAIR_FILE_NAME, content);
292
309
  }
293
310
  catch { /* best effort */ }
294
311
  }
295
312
  export function removeRepairFile(projectDir) {
296
313
  try {
297
- fs.rmSync(path.join(projectDir, REPAIR_FILE_NAME), { force: true });
314
+ const target = path.join(projectDir, REPAIR_FILE_NAME);
315
+ // Unlink only the byte-identical file WE recorded writing (ENG-1857): a
316
+ // candidate-owned or candidate-edited file must survive every cleanup path.
317
+ if (!isOwnedNoticeFile(projectDir, REPAIR_FILE_NAME))
318
+ return;
319
+ fs.rmSync(target, { force: true });
320
+ clearOwnedNotice(projectDir, REPAIR_FILE_NAME);
298
321
  }
299
322
  catch { /* best effort */ }
300
323
  }
@@ -1 +1 @@
1
- {"version":3,"file":"shadow.js","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shadow.js","sourceRoot":"","sources":["../../src/lib/shadow.ts"],"names":[],"mappings":"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"}
@@ -30,10 +30,12 @@ import { fetchInitMetadata, ServerError } from "./api.js";
30
30
  import { ServerTimesSchema } from "./schemas.js";
31
31
  import { detectInstalledHooks } from "./ai-tracking.js";
32
32
  import { setupAiTracking } from "../commands/init.js";
33
- import { writeShadowChainCursor, readShadowChainCursor, writeShadowTrackerFile, writeShadowReviveInfo, readShadowConfigById, findShadowConfigForDir, deleteShadowForAssessment, writeRepairFile, removeRepairFile, } from "./shadow.js";
33
+ import { writeShadowChainCursor, readShadowChainCursor, writeShadowTrackerFile, writeShadowReviveInfo, readShadowConfigById, findShadowConfigForDir, deleteShadowForAssessment, writeRepairFile, removeRepairFile, REPAIR_FILE_NAME, } from "./shadow.js";
34
34
  import { createKeepAliveGuard } from "./keepalive.js";
35
35
  import { getProcessList, getConnections, extractConnection, resolvePidNames } from "./platform.js";
36
36
  import { classifyClaudeProcesses, refineClaudeTool } from "./detection.js";
37
+ import { decideNudge, loadNudgeState, saveNudgeState, initialPromptCursor, scanPromptCount, writeAiNoticeFile, removeAiNoticeFile, nudgeNotificationCopy, isCliNoticeArtifact, AI_NOTICE_FILE_NAME, } from "./capture-nudge.js";
38
+ import { desktopNotify } from "./notify.js";
37
39
  // ENG-1491: an uncaught throw no longer always kills the watcher — on a
38
40
  // multi-day assessment one bad callback used to silently end tracking until
39
41
  // something respawned it. The guard keeps the two exits that are still
@@ -339,6 +341,7 @@ function uploadEvents() {
339
341
  if (litmusConfig)
340
342
  deleteShadowForAssessment(litmusConfig.assessmentId);
341
343
  removeRepairFile(projectDir);
344
+ removeOwnedAiNotice(); // ENG-1857: session over — the nudge goes with it
342
345
  shutdown();
343
346
  }
344
347
  }
@@ -411,8 +414,25 @@ const IGNORED = [
411
414
  /(?:^|[/\\])venv[/\\]/,
412
415
  /(?:^|[/\\])\.venv[/\\]/,
413
416
  ];
417
+ // Candidate-facing notice files this CLI writes at the assessment root
418
+ // (ENG-1857). They are guidance to the candidate, not candidate work, so
419
+ // they must never become judge-visible signals: not as `change` events from
420
+ // our own write (IGNORED via the check below), and not in the interview-
421
+ // agent snapshot (SNAPSHOT_IGNORE via the same check). LITMUS-REPAIR.md
422
+ // predates this feature and was missing from both lists — fixed alongside.
423
+ //
424
+ // The suppression is root-only and content-gated (see isCliNoticeArtifact):
425
+ // a nested file with the same basename — or a root file the candidate wrote
426
+ // themselves — is candidate work, and its events flow normally. The cache
427
+ // remembers the last ownership verdict per filename so the deletion of OUR
428
+ // notice (content no longer readable) is still classified as ours; it is
429
+ // seeded at boot for the respawn-then-remove case where this watcher never
430
+ // saw the file change.
431
+ const noticeOwnership = new Map();
432
+ isCliNoticeArtifact(projectDir, AI_NOTICE_FILE_NAME, noticeOwnership);
433
+ isCliNoticeArtifact(projectDir, REPAIR_FILE_NAME, noticeOwnership);
414
434
  function shouldIgnore(relPath) {
415
- return IGNORED.some((re) => re.test(relPath));
435
+ return isCliNoticeArtifact(projectDir, relPath, noticeOwnership) || IGNORED.some((re) => re.test(relPath));
416
436
  }
417
437
  // ── Hash chain for tamper evidence ──────────────────────────────
418
438
  // Resume from the last chained event already in the log rather than restarting
@@ -973,6 +993,7 @@ const AI_TOOL_PATTERNS = {
973
993
  codeium: /\bcodeium\b/,
974
994
  codex: /\bcodex\b/,
975
995
  copilot: /\bcopilot\b/,
996
+ gemini: /\bgemini\b/,
976
997
  windsurf: /\bwindsurf\b/,
977
998
  ollama: /\bollama\b/,
978
999
  tabnine: /\btabnine\b/,
@@ -984,6 +1005,68 @@ const AI_TOOL_PATTERNS = {
984
1005
  pearai: /\bpearai\b/,
985
1006
  ghostwriter: /\bghostwriter\b/,
986
1007
  };
1008
+ // ── Capture nudge (ENG-1857) ─────────────────────────────────────
1009
+ // A hook-capturable AI tool running with ZERO captured prompts is the silent
1010
+ // Codex/Copilot trust-gate signature: the candidate's AI work is invisibly
1011
+ // not being recorded, and they'd only find out from their report. Warn THEM,
1012
+ // live. Strictly candidate-facing: a desktop notification, a notice file at
1013
+ // the assessment root, and stderr (-> .litmus/tracker.log, excluded from all
1014
+ // grader signals via substrate.py's _is_capture_artifact). It must emit NO
1015
+ // activity event — the grader renders every non-response activity event
1016
+ // verbatim in the judge-visible timeline and merges the in-zip
1017
+ // activity.jsonl, so an event here would leak straight to reviewers.
1018
+ //
1019
+ // State persists in .litmus/nudge-state.json so the routine watcher respawns
1020
+ // (litmus status/doctor revive watchers) don't re-nag from scratch.
1021
+ let nudgeState = loadNudgeState(projectDir);
1022
+ // Incremental ai_prompt/ai_response counter over activity.jsonl: byte-offset
1023
+ // cursor, only appended bytes are read each cycle. A watcher restart rescans
1024
+ // from 0 — the same cost status.ts already pays on every run.
1025
+ let promptCursor = initialPromptCursor();
1026
+ /**
1027
+ * Remove OUR notice, if that is what sits at the path. The ownership cache
1028
+ * is refreshed first (while the content is still readable) so the unlink's
1029
+ * own file event is classified as ours and suppressed; a candidate-owned
1030
+ * file is left alone by removeAiNoticeFile's sentinel gate.
1031
+ */
1032
+ function removeOwnedAiNotice() {
1033
+ isCliNoticeArtifact(projectDir, AI_NOTICE_FILE_NAME, noticeOwnership);
1034
+ removeAiNoticeFile(projectDir);
1035
+ }
1036
+ function runCaptureNudge(aiTools) {
1037
+ try {
1038
+ // Once anything was captured the latch is permanent; stop reading the log.
1039
+ if (promptCursor.count === 0 && !nudgeState.latched) {
1040
+ promptCursor = scanPromptCount(activityLogPath, promptCursor);
1041
+ }
1042
+ const deadlineMs = litmusConfig ? getEffectiveDeadline(litmusConfig) : null;
1043
+ const msRemaining = deadlineMs === null ? null : deadlineMs - Date.now();
1044
+ const decision = decideNudge(nudgeState, aiTools, promptCursor.count, Date.now(), msRemaining);
1045
+ nudgeState = decision.state;
1046
+ saveNudgeState(projectDir, nudgeState);
1047
+ if (decision.action === "nudge") {
1048
+ // Write first: when a candidate-owned file occupies the path we never
1049
+ // clobber it, and the notification then drops its "Details:" pointer
1050
+ // (which would name a file carrying THEIR content).
1051
+ const noticeWritten = writeAiNoticeFile(projectDir);
1052
+ if (noticeWritten) {
1053
+ isCliNoticeArtifact(projectDir, AI_NOTICE_FILE_NAME, noticeOwnership); // refresh ownership
1054
+ }
1055
+ else {
1056
+ process.stderr.write(`[watcher] ${AI_NOTICE_FILE_NAME} exists and is not ours; notification only\n`);
1057
+ }
1058
+ const copy = nudgeNotificationCopy(decision.tool, noticeWritten);
1059
+ desktopNotify(copy.title, copy.message);
1060
+ // stderr only — this lands in tracker.log, never in activity.jsonl.
1061
+ process.stderr.write(`[watcher] capture nudge fired (${decision.tool}, nudge ${nudgeState.nudgeCount}): AI tool present, zero prompts captured\n`);
1062
+ }
1063
+ else if (decision.action === "remove_notice") {
1064
+ removeOwnedAiNotice();
1065
+ process.stderr.write("[watcher] AI capture confirmed; nudge latched off\n");
1066
+ }
1067
+ }
1068
+ catch { /* the nudge must never break the watcher */ }
1069
+ }
987
1070
  function detectEnvironment() {
988
1071
  try {
989
1072
  const ps = getProcessList();
@@ -1011,6 +1094,11 @@ function detectEnvironment() {
1011
1094
  event.aiTools = aiTools;
1012
1095
  emit(event);
1013
1096
  }
1097
+ // ENG-1857: warn the candidate when a hook-capturable tool is running but
1098
+ // nothing is being captured. Runs on EVERY scan (an empty aiTools is what
1099
+ // resets the consecutive-presence counters). Candidate-facing only — see
1100
+ // runCaptureNudge.
1101
+ runCaptureNudge(aiTools);
1014
1102
  }
1015
1103
  catch { /* process listing may fail, skip */ }
1016
1104
  }
@@ -1071,6 +1159,9 @@ const AI_NETWORK_HOSTS = {
1071
1159
  "claude.ai": "claude_web",
1072
1160
  "generativelanguage.googleapis.com": "gemini_api",
1073
1161
  "gemini.google.com": "gemini_web",
1162
+ // The Gemini CLI's default Code Assist endpoint (OAuth login path) — it only
1163
+ // hits generativelanguage.googleapis.com when the user brings their own key.
1164
+ "cloudcode-pa.googleapis.com": "gemini_cli",
1074
1165
  "copilot-proxy.githubusercontent.com": "copilot",
1075
1166
  "githubcopilot.com": "copilot",
1076
1167
  "api2.cursor.sh": "cursor",
@@ -1219,7 +1310,9 @@ const SNAPSHOT_IGNORE = [
1219
1310
  /id_ed25519(\.pub)?$/i,
1220
1311
  ];
1221
1312
  function shouldIgnoreForSnapshot(relPath) {
1222
- return SNAPSHOT_IGNORE.some((re) => re.test(relPath));
1313
+ // OUR root notice files (LITMUS-AI-NOTICE.md, LITMUS-REPAIR.md) never ride
1314
+ // the snapshot; candidate files by those names do — see noticeOwnership.
1315
+ return isCliNoticeArtifact(projectDir, relPath, noticeOwnership) || SNAPSHOT_IGNORE.some((re) => re.test(relPath));
1223
1316
  }
1224
1317
  function collectProjectFiles() {
1225
1318
  const files = {};