@jigyasudham/veto 3.4.0 → 3.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 (67) hide show
  1. package/README.md +36 -0
  2. package/dist/agents/llm-runner.d.ts.map +1 -1
  3. package/dist/agents/llm-runner.js +61 -4
  4. package/dist/agents/llm-runner.js.map +1 -1
  5. package/dist/cli/lessons.d.ts +17 -0
  6. package/dist/cli/lessons.d.ts.map +1 -0
  7. package/dist/cli/lessons.js +448 -0
  8. package/dist/cli/lessons.js.map +1 -0
  9. package/dist/cli/statusline-hosts.js +1 -1
  10. package/dist/cli/statusline-hosts.js.map +1 -1
  11. package/dist/cli/statusline.d.ts +1 -0
  12. package/dist/cli/statusline.d.ts.map +1 -1
  13. package/dist/cli/statusline.js +18 -1
  14. package/dist/cli/statusline.js.map +1 -1
  15. package/dist/cli.js +10 -0
  16. package/dist/cli.js.map +1 -1
  17. package/dist/lessons/classify.d.ts +3 -0
  18. package/dist/lessons/classify.d.ts.map +1 -1
  19. package/dist/lessons/classify.js +12 -5
  20. package/dist/lessons/classify.js.map +1 -1
  21. package/dist/lessons/consent.d.ts +30 -0
  22. package/dist/lessons/consent.d.ts.map +1 -0
  23. package/dist/lessons/consent.js +86 -0
  24. package/dist/lessons/consent.js.map +1 -0
  25. package/dist/lessons/discover.d.ts +7 -0
  26. package/dist/lessons/discover.d.ts.map +1 -1
  27. package/dist/lessons/discover.js +5 -5
  28. package/dist/lessons/discover.js.map +1 -1
  29. package/dist/lessons/harvest.d.ts +40 -4
  30. package/dist/lessons/harvest.d.ts.map +1 -1
  31. package/dist/lessons/harvest.js +192 -17
  32. package/dist/lessons/harvest.js.map +1 -1
  33. package/dist/lessons/manage.d.ts +163 -0
  34. package/dist/lessons/manage.d.ts.map +1 -0
  35. package/dist/lessons/manage.js +337 -0
  36. package/dist/lessons/manage.js.map +1 -0
  37. package/dist/lessons/on-save.d.ts +18 -0
  38. package/dist/lessons/on-save.d.ts.map +1 -0
  39. package/dist/lessons/on-save.js +53 -0
  40. package/dist/lessons/on-save.js.map +1 -0
  41. package/dist/lessons/select.d.ts +1 -1
  42. package/dist/lessons/select.d.ts.map +1 -1
  43. package/dist/lessons/select.js +8 -6
  44. package/dist/lessons/select.js.map +1 -1
  45. package/dist/lessons/store.d.ts +80 -1
  46. package/dist/lessons/store.d.ts.map +1 -1
  47. package/dist/lessons/store.js +147 -10
  48. package/dist/lessons/store.js.map +1 -1
  49. package/dist/memory/config.d.ts +13 -0
  50. package/dist/memory/config.d.ts.map +1 -1
  51. package/dist/memory/config.js +24 -0
  52. package/dist/memory/config.js.map +1 -1
  53. package/dist/memory/local.d.ts.map +1 -1
  54. package/dist/memory/local.js +6 -3
  55. package/dist/memory/local.js.map +1 -1
  56. package/dist/memory/schema.d.ts +1 -1
  57. package/dist/memory/schema.d.ts.map +1 -1
  58. package/dist/memory/schema.js +50 -0
  59. package/dist/memory/schema.js.map +1 -1
  60. package/dist/server/handlers/council.d.ts.map +1 -1
  61. package/dist/server/handlers/council.js +36 -3
  62. package/dist/server/handlers/council.js.map +1 -1
  63. package/dist/server/handlers/session.d.ts.map +1 -1
  64. package/dist/server/handlers/session.js +10 -0
  65. package/dist/server/handlers/session.js.map +1 -1
  66. package/package.json +1 -1
  67. package/server.json +2 -2
@@ -0,0 +1,30 @@
1
+ import { type LessonSyncReport } from './harvest.js';
2
+ /**
3
+ * What each AI CLI sets in the environment of the commands it runs.
4
+ * CLAUDECODE and AI_AGENT are set by Claude Code (checked on this machine);
5
+ * GEMINI_CLI by Gemini CLI's shell tool; the CODEX_ ones by Codex's sandboxed
6
+ * and npm-launched runs. A real terminal is required as well, so a marker a
7
+ * host does not set is not the only line of defence.
8
+ */
9
+ export declare const AI_SESSION_MARKERS: readonly ["CLAUDECODE", "AI_AGENT", "GEMINI_CLI", "CODEX_SANDBOX", "CODEX_SANDBOX_NETWORK_DISABLED", "CODEX_MANAGED_BY_NPM"];
10
+ /** The marker showing an AI is running this command, or null in the user's own terminal. */
11
+ export declare function detectAiSession(env?: NodeJS.ProcessEnv): string | null;
12
+ /**
13
+ * The disclosure shown before the user accepts. Changing what it promises
14
+ * means bumping LESSONS_CONSENT_VERSION, and so does starting delivery: this
15
+ * text promises to ask again first.
16
+ */
17
+ export declare function lessonsDisclosure(): string;
18
+ export type ConsentSummary = {
19
+ report: LessonSyncReport;
20
+ notes: number;
21
+ projects: number;
22
+ anyProject: number;
23
+ ownProject: number;
24
+ unlinked: number;
25
+ byHost: Record<'claude' | 'codex' | 'gemini', number>;
26
+ disabledHosts: string[];
27
+ };
28
+ /** Record consent, read every AI's memory once, and summarise what was found. */
29
+ export declare function acceptLessonsConsent(home?: string): ConsentSummary;
30
+ //# sourceMappingURL=consent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.d.ts","sourceRoot":"","sources":["../../src/lessons/consent.ts"],"names":[],"mappings":"AAOA,OAAO,EAAqB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGxE;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,8HAA+H,CAAC;AAE/J,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,IAAI,CAEnF;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,MAAM,CA0C1C;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,MAAM,EAAE,gBAAgB,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,QAAQ,GAAG,OAAO,GAAG,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtD,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,oBAAoB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,cAAc,CAelE"}
@@ -0,0 +1,86 @@
1
+ // Consent v2 for Harvest-and-Share (council 320c40dc, legal conditions): it
2
+ // names both flows, across projects and between AI vendors, is OFF by default,
3
+ // and no upgrade can switch it on. Only the user can accept it, typing "yes"
4
+ // in a terminal of their own: a command an AI runs is refused, so no AI can
5
+ // turn sharing on for them (owner decision, 2026-09-19).
6
+ import { enableLessonsSharing } from '../memory/config.js';
7
+ import { syncLessonSources } from './harvest.js';
8
+ import { lessonFlows, lessonsStatus, unresolvedFolders } from './manage.js';
9
+ /**
10
+ * What each AI CLI sets in the environment of the commands it runs.
11
+ * CLAUDECODE and AI_AGENT are set by Claude Code (checked on this machine);
12
+ * GEMINI_CLI by Gemini CLI's shell tool; the CODEX_ ones by Codex's sandboxed
13
+ * and npm-launched runs. A real terminal is required as well, so a marker a
14
+ * host does not set is not the only line of defence.
15
+ */
16
+ export const AI_SESSION_MARKERS = ['CLAUDECODE', 'AI_AGENT', 'GEMINI_CLI', 'CODEX_SANDBOX', 'CODEX_SANDBOX_NETWORK_DISABLED', 'CODEX_MANAGED_BY_NPM'];
17
+ /** The marker showing an AI is running this command, or null in the user's own terminal. */
18
+ export function detectAiSession(env = process.env) {
19
+ return AI_SESSION_MARKERS.find(name => (env[name] ?? '') !== '') ?? null;
20
+ }
21
+ /**
22
+ * The disclosure shown before the user accepts. Changing what it promises
23
+ * means bumping LESSONS_CONSENT_VERSION, and so does starting delivery: this
24
+ * text promises to ask again first.
25
+ */
26
+ export function lessonsDisclosure() {
27
+ return [
28
+ 'What you are turning on: sharing between your AIs\' notes',
29
+ '',
30
+ 'Claude, Codex and Gemini each keep notes in their own memory (Claude\'s',
31
+ 'memory folders, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md). With sharing on,',
32
+ 'Veto passes those notes on where they help:',
33
+ '',
34
+ ' • ACROSS YOUR PROJECTS: a note about you or this computer, written while',
35
+ ' you worked on one project, can be shared into your other projects.',
36
+ ' • BETWEEN AIs: a note one AI wrote can be shared with the others, so a',
37
+ ' note Claude wrote can reach Codex and Gemini, and the other way round.',
38
+ ' A note given to an AI goes to that AI\'s company as part of your',
39
+ ' conversation, like anything else you send it.',
40
+ '',
41
+ 'What Veto does, and does not do:',
42
+ ' • It only reads those memory files. It never changes or deletes them.',
43
+ ' • It reads them again each time you save a session, so notes you make',
44
+ ' today are picked up without you running anything. `veto lessons off`',
45
+ ' stops it, and so does turning harvest_on_save off in Veto\'s config.',
46
+ ' • It keeps a copy of each note in its own database on this computer,',
47
+ ' with email addresses, your home folder and anything that looks like a',
48
+ ' password or key removed first. Veto itself uploads nothing.',
49
+ ' • A note stays in its own project if it is about that project, or if it',
50
+ ' contains a command, a web address, a credential or an instruction to',
51
+ ' fetch, send or run something. Notes about secrets or personal details',
52
+ ' never leave their project.',
53
+ ' • A shared note is marked as information from another AI session, never',
54
+ ' as an instruction to follow.',
55
+ '',
56
+ 'For now this is a trial: Veto reads the notes and keeps its own masked',
57
+ 'copy of them on this computer, but gives none of them to any AI yet, and',
58
+ 'works out nothing about which ones it would give. Before it starts giving',
59
+ 'notes to your AIs, Veto will ask you again.',
60
+ '',
61
+ 'You stay in control:',
62
+ ' veto lessons list every note Veto has read',
63
+ ' veto lessons why <id> one note, where it came from, where it may go',
64
+ ' veto lessons forget <id> stop one note for good',
65
+ ' veto lessons exclude keep the current project out entirely',
66
+ ' veto lessons off turn sharing off and delete everything Veto copied',
67
+ ].join('\n');
68
+ }
69
+ /** Record consent, read every AI's memory once, and summarise what was found. */
70
+ export function acceptLessonsConsent(home) {
71
+ enableLessonsSharing();
72
+ const report = syncLessonSources(home);
73
+ const status = lessonsStatus();
74
+ const flows = lessonFlows();
75
+ return {
76
+ report,
77
+ notes: status.notes,
78
+ projects: new Set(flows.filter(f => !f.projectIdentity.startsWith('global:')).map(f => f.projectIdentity)).size,
79
+ anyProject: status.anyProject,
80
+ ownProject: flows.reduce((sum, f) => sum + f.ownProject + f.nowhere, 0),
81
+ unlinked: unresolvedFolders().length,
82
+ byHost: status.byHost,
83
+ disabledHosts: [...status.disabledHosts.keys()],
84
+ };
85
+ }
86
+ //# sourceMappingURL=consent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/lessons/consent.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,4EAA4E;AAC5E,yDAAyD;AAEzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAyB,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAE5E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,eAAe,EAAE,gCAAgC,EAAE,sBAAsB,CAAU,CAAC;AAE/J,4FAA4F;AAC5F,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,OAAO,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC,IAAI,IAAI,CAAC;AAC3E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,2DAA2D;QAC3D,EAAE;QACF,yEAAyE;QACzE,4EAA4E;QAC5E,6CAA6C;QAC7C,EAAE;QACF,4EAA4E;QAC5E,wEAAwE;QACxE,0EAA0E;QAC1E,4EAA4E;QAC5E,sEAAsE;QACtE,mDAAmD;QACnD,EAAE;QACF,kCAAkC;QAClC,yEAAyE;QACzE,yEAAyE;QACzE,0EAA0E;QAC1E,0EAA0E;QAC1E,wEAAwE;QACxE,2EAA2E;QAC3E,iEAAiE;QACjE,2EAA2E;QAC3E,0EAA0E;QAC1E,2EAA2E;QAC3E,gCAAgC;QAChC,2EAA2E;QAC3E,kCAAkC;QAClC,EAAE;QACF,wEAAwE;QACxE,0EAA0E;QAC1E,2EAA2E;QAC3E,6CAA6C;QAC7C,EAAE;QACF,sBAAsB;QACtB,uDAAuD;QACvD,4EAA4E;QAC5E,qDAAqD;QACrD,oEAAoE;QACpE,iFAAiF;KAClF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAaD,iFAAiF;AACjF,MAAM,UAAU,oBAAoB,CAAC,IAAa;IAChD,oBAAoB,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,MAAM,GAAG,aAAa,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,WAAW,EAAE,CAAC;IAC5B,OAAO;QACL,MAAM;QACN,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI;QAC/G,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;QACvE,QAAQ,EAAE,iBAAiB,EAAE,CAAC,MAAM;QACpC,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,aAAa,EAAE,CAAC,GAAG,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC;KAChD,CAAC;AACJ,CAAC"}
@@ -6,6 +6,13 @@ export type NativeMemorySource = {
6
6
  mtimeMs: number;
7
7
  /** A host-wide file every session of that host loads, not tied to a project. */
8
8
  global: boolean;
9
+ /**
10
+ * The host's own directory this file was found under. A memory file is only
11
+ * followed while it resolves inside it, so a link dropped into a memory
12
+ * folder cannot make the harvester read somewhere else. Not the user's home:
13
+ * CODEX_HOME and a relocated Claude directory both live wherever they like.
14
+ */
15
+ root: string;
9
16
  /** Claude only: the ~/.claude/projects/<slug> folder the entry lives in. */
10
17
  claudeFolder?: string;
11
18
  claudeSlug?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/lessons/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA8BF;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,SAAY,GAAG,kBAAkB,EAAE,CAUlF"}
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../src/lessons/discover.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AA8BF;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,SAAY,GAAG,kBAAkB,EAAE,CAUlF"}
@@ -1,10 +1,10 @@
1
1
  import { existsSync, readdirSync, statSync } from 'node:fs';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, join } from 'node:path';
4
- function candidate(source, sourcePath, extra = {}) {
4
+ function candidate(source, sourcePath, root, extra = {}) {
5
5
  try {
6
6
  const stat = statSync(sourcePath);
7
- return stat.isFile() ? { source, sourcePath, fileName: basename(sourcePath), mtimeMs: stat.mtimeMs, global: false, ...extra } : null;
7
+ return stat.isFile() ? { source, sourcePath, fileName: basename(sourcePath), mtimeMs: stat.mtimeMs, global: false, root, ...extra } : null;
8
8
  }
9
9
  catch {
10
10
  return null;
@@ -37,7 +37,7 @@ function claudeMemoryFiles(claudeHome) {
37
37
  for (const file of files) {
38
38
  if (!file.isFile() || !file.name.toLowerCase().endsWith('.md'))
39
39
  continue;
40
- const found = candidate('claude', join(memory, file.name), { claudeFolder, claudeSlug: folder.name });
40
+ const found = candidate('claude', join(memory, file.name), claudeHome, { claudeFolder, claudeSlug: folder.name });
41
41
  if (found)
42
42
  out.push(found);
43
43
  }
@@ -53,11 +53,11 @@ export function discoverNativeMemorySources(home = homedir()) {
53
53
  const codexHome = home === homedir() && process.env.CODEX_HOME ? process.env.CODEX_HOME : join(home, '.codex');
54
54
  const out = claudeMemoryFiles(join(home, '.claude'));
55
55
  for (const path of [join(codexHome, 'AGENTS.md'), join(codexHome, 'AGENTS.override.md')]) {
56
- const found = candidate('codex', path, { global: true });
56
+ const found = candidate('codex', path, codexHome, { global: true });
57
57
  if (found)
58
58
  out.push(found);
59
59
  }
60
- const gemini = candidate('gemini', join(home, '.gemini', 'GEMINI.md'), { global: true });
60
+ const gemini = candidate('gemini', join(home, '.gemini', 'GEMINI.md'), join(home, '.gemini'), { global: true });
61
61
  if (gemini)
62
62
  out.push(gemini);
63
63
  return out.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/lessons/discover.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../src/lessons/discover.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,19 @@
1
1
  import { type LessonSource } from './adapters/index.js';
2
+ /**
3
+ * Bump when anything that decides what a file yields changes: this harvester,
4
+ * the adapters, mask.ts or classify.ts. Every remembered file fingerprint is
5
+ * invalidated, so the next pass re-reads and re-classifies everything.
6
+ * `tests/lessons-harvester-version.test.ts` fails if those files change
7
+ * without this being bumped, so the reminder is not a comment alone.
8
+ */
9
+ export declare const HARVESTER_VERSION = 1;
2
10
  export type HarvestOutcome = {
3
11
  status: 'harvested';
4
12
  inserted: number;
5
13
  updated: number;
6
14
  removed: number;
15
+ } | {
16
+ status: 'unchanged';
7
17
  } | {
8
18
  status: 'skipped';
9
19
  reason: 'empty' | 'index' | 'veto-authored';
@@ -26,23 +36,49 @@ export declare function harvestNativeMemory(input: {
26
36
  /** The project's own names; a note naming its project stays project scope. */
27
37
  projectNames?: string[];
28
38
  global?: boolean;
39
+ /** The host directory this file was found under; it may not resolve outside it. */
40
+ root?: string;
41
+ /** Ignore what this file looked like last time and read it again regardless. */
42
+ forced?: boolean;
29
43
  }): HarvestOutcome;
30
44
  export type LessonSyncReport = {
31
45
  consent: boolean;
32
46
  sources: number;
33
47
  harvested: number;
48
+ /** Files whose bytes and project are what they were last time; nothing was parsed. */
49
+ unchanged: number;
34
50
  skipped: number;
35
51
  disabled: number;
36
52
  unavailable: number;
53
+ /** Sources belonging to a project kept out of sharing; nothing of theirs is stored. */
54
+ excluded: number;
37
55
  inserted: number;
38
56
  updated: number;
39
57
  removed: number;
40
58
  unresolvedProjects: number;
59
+ /** Files the pass never reached because it ran out of its time budget. */
60
+ notReached: number;
61
+ elapsedMs: number;
62
+ };
63
+ export type SyncOptions = {
64
+ home?: string;
65
+ /** Re-read every file even if it looks untouched. Policy changes need this. */
66
+ forced?: boolean;
67
+ /**
68
+ * Stop once this many milliseconds have gone, checked before every file.
69
+ * What is left waits for the next pass; the work is idempotent, so nothing
70
+ * is lost by stopping. Only the save path sets it.
71
+ */
72
+ budgetMs?: number;
41
73
  };
42
74
  /**
43
- * One full pass: discover every known native-memory file, harvest it under its
44
- * project's identity, and delete rows whose file has gone. Nothing is read
45
- * until the user has accepted consent v2.
75
+ * One pass: discover every known native-memory file, harvest what changed
76
+ * under its project's identity, and delete rows whose file has gone. Nothing
77
+ * is read until the user has accepted consent v2, and an excluded project's
78
+ * files are never read at all.
79
+ *
80
+ * A pass given a budget stops cleanly when it runs out and reports what it did
81
+ * not reach. Every step is idempotent, so the next pass simply carries on.
46
82
  */
47
- export declare function syncLessonSources(home?: string): LessonSyncReport;
83
+ export declare function syncLessonSources(options?: string | SyncOptions): LessonSyncReport;
48
84
  //# sourceMappingURL=harvest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../src/lessons/harvest.ts"],"names":[],"mappings":"AAGA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAQ3E,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,MAAM,EAAE,UAAU,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,cAAc,CA6BjB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAgBF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAsBjE"}
1
+ {"version":3,"file":"harvest.d.ts","sourceRoot":"","sources":["../../src/lessons/harvest.ts"],"names":[],"mappings":"AAIA,OAAO,EAAqB,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAY3E;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,IAAI,CAAC;AAgBnC,MAAM,MAAM,cAAc,GACtB;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3E;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,GACvB;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,eAAe,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACnF;IAAE,MAAM,EAAE,UAAU,GAAG,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAsC3D;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,MAAM,EAAE,YAAY,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,cAAc,CA4EjB;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,sFAAsF;IACtF,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+EAA+E;IAC/E,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAyCF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,MAAM,GAAG,WAAgB,GAAG,gBAAgB,CAsDtF"}
@@ -1,5 +1,6 @@
1
- import { readFileSync, statSync } from 'node:fs';
2
- import { basename } from 'node:path';
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, realpathSync, statSync } from 'node:fs';
3
+ import { basename, dirname, relative, isAbsolute } from 'node:path';
3
4
  import { isLessonsSharingEnabled } from '../memory/config.js';
4
5
  import { parseNativeMemory } from './adapters/index.js';
5
6
  import { classifyLesson } from './classify.js';
@@ -7,7 +8,64 @@ import { discoverNativeMemorySources } from './discover.js';
7
8
  import { projectNames, resolveProjectIdentity } from './identity.js';
8
9
  import { maskLessonText } from './mask.js';
9
10
  import { resolveClaudeProjectFolder } from './source-project.js';
10
- import { disableLessonSource, isLessonSourceEnabled, purgeVanishedLessonSources, syncLessons } from './store.js';
11
+ import { deleteLessonSource, disableLessonSource, isLessonSourceEnabled, isProjectExcluded, lessonFileState, purgeVanishedLessonFileState, purgeVanishedLessonSources, recordLessonFileState, recordLessonFileUnavailable, syncLessons, unresolvedProjectCount, } from './store.js';
12
+ /**
13
+ * Bump when anything that decides what a file yields changes: this harvester,
14
+ * the adapters, mask.ts or classify.ts. Every remembered file fingerprint is
15
+ * invalidated, so the next pass re-reads and re-classifies everything.
16
+ * `tests/lessons-harvester-version.test.ts` fails if those files change
17
+ * without this being bumped, so the reminder is not a comment alone.
18
+ */
19
+ export const HARVESTER_VERSION = 1;
20
+ /**
21
+ * A memory file is a hand-written note; anything this large is not one. The
22
+ * cap stops an unbounded read on the save path (council 534e2bd5, security).
23
+ */
24
+ const MAX_SOURCE_BYTES = 2 * 1024 * 1024;
25
+ /**
26
+ * After this many passes in a row where a file could not be read, a save stops
27
+ * probing it. Reading it is what costs seconds when the drive holding it is no
28
+ * longer plugged in, and a save must not wait for that. A command still tries
29
+ * every time, so plugging the drive back in and running `veto lessons` works.
30
+ */
31
+ const UNAVAILABLE_BACKOFF_AFTER = 3;
32
+ /**
33
+ * A memory file is followed only while it resolves inside the host directory
34
+ * it was discovered under: a link dropped into a memory folder must not turn
35
+ * the harvester into a reader of arbitrary files elsewhere on the machine.
36
+ *
37
+ * The boundary is that host directory and NOT the user's home, because neither
38
+ * is reliably inside the other — CODEX_HOME and a relocated Claude directory
39
+ * both live wherever the user puts them, and a home-based rule would refuse
40
+ * every note those users have.
41
+ *
42
+ * The whole path is resolved rather than just its last part, so a symlinked
43
+ * folder above the file is caught as well.
44
+ */
45
+ function resolvesOutsideRoot(sourcePath, root) {
46
+ try {
47
+ const rel = relative(realpathSync(root), realpathSync(sourcePath));
48
+ return rel.startsWith('..') || isAbsolute(rel);
49
+ }
50
+ catch {
51
+ return true;
52
+ }
53
+ }
54
+ /**
55
+ * Everything that decides what this file yields: its bytes, and the project
56
+ * identity and names that classification reads. Identity is part of it because
57
+ * an alias can move a file to another project without touching its contents —
58
+ * skipping on content alone would silently ignore `veto lessons alias`.
59
+ */
60
+ function fingerprint(raw, input) {
61
+ return createHash('sha256')
62
+ .update(raw).update('\u0000')
63
+ .update(input.projectIdentity).update('\u0000')
64
+ .update(input.projectLabel ?? '').update('\u0000')
65
+ .update((input.projectNames ?? []).join('\u0000')).update('\u0000')
66
+ .update(input.global === true ? '1' : '0')
67
+ .digest('hex');
68
+ }
11
69
  /**
12
70
  * Read and harvest a single host-native memory file. The source is only ever
13
71
  * opened for reading. An unrecognised shape disables that host's harvester
@@ -17,15 +75,56 @@ import { disableLessonSource, isLessonSourceEnabled, purgeVanishedLessonSources,
17
75
  export function harvestNativeMemory(input) {
18
76
  if (!isLessonSourceEnabled(input.source))
19
77
  return { status: 'disabled', reason: 'source disabled after format drift' };
20
- let raw;
78
+ // Defaults to the file's own folder when a caller names no root, which is
79
+ // the strictest sensible reading of "where this file is allowed to live".
80
+ if (resolvesOutsideRoot(input.sourcePath, input.root ?? dirname(input.sourcePath))) {
81
+ return { status: 'unavailable', reason: "memory file resolves outside its host's own folder and was not read" };
82
+ }
83
+ // Stat first: an unchanged file costs this and nothing more. No transaction
84
+ // is opened on this path, so a quiet pass takes no write lock at all.
85
+ let size;
21
86
  let mtime;
87
+ try {
88
+ const stat = statSync(input.sourcePath);
89
+ size = stat.size;
90
+ mtime = stat.mtime.toISOString();
91
+ }
92
+ catch {
93
+ recordLessonFileUnavailable(input.source, input.sourcePath);
94
+ return { status: 'unavailable', reason: 'native memory file could not be read' };
95
+ }
96
+ if (size > MAX_SOURCE_BYTES) {
97
+ // Left alone rather than emptied: this is a file we declined to read, not
98
+ // a file we read and found nothing in.
99
+ return { status: 'unavailable', reason: `memory file is larger than ${MAX_SOURCE_BYTES / (1024 * 1024)} MB and was not read` };
100
+ }
101
+ const known = input.forced ? null : lessonFileState(input.source, input.sourcePath);
102
+ // Identity is part of the comparison because an alias, or a drive coming
103
+ // back, moves a file to another project without touching a byte of it. It
104
+ // costs nothing here: the caller has already worked out which project this
105
+ // file belongs to.
106
+ const current = known?.harvester_version === HARVESTER_VERSION && known?.project_identity === input.projectIdentity;
107
+ if (current && known?.mtime === mtime && known?.size === size)
108
+ return { status: 'unchanged' };
109
+ let raw;
22
110
  try {
23
111
  raw = readFileSync(input.sourcePath, 'utf8');
24
- mtime = statSync(input.sourcePath).mtime.toISOString();
25
112
  }
26
113
  catch {
114
+ recordLessonFileUnavailable(input.source, input.sourcePath);
27
115
  return { status: 'unavailable', reason: 'native memory file could not be read' };
28
116
  }
117
+ // mtime moved but the content did not — a touch, a sync tool, a restore.
118
+ // Remember the new mtime so the cheap path hits next time, and parse nothing.
119
+ const digest = fingerprint(raw, input);
120
+ const remember = () => recordLessonFileState({
121
+ source: input.source, sourcePath: input.sourcePath, mtime, size, contentHash: digest,
122
+ projectIdentity: input.projectIdentity, harvesterVersion: HARVESTER_VERSION,
123
+ });
124
+ if (current && known?.content_hash === digest) {
125
+ remember();
126
+ return { status: 'unchanged' };
127
+ }
29
128
  const fileName = basename(input.sourcePath);
30
129
  const parsed = parseNativeMemory(input.source, raw, fileName);
31
130
  if (!parsed.ok) {
@@ -34,7 +133,9 @@ export function harvestNativeMemory(input) {
34
133
  }
35
134
  const base = { sourceCli: input.source, sourcePath: input.sourcePath, projectIdentity: input.projectIdentity, projectLabel: input.projectLabel ?? null, sourceMtime: mtime };
36
135
  if ('skip' in parsed) {
37
- return { status: 'skipped', reason: parsed.skip, removed: syncLessons({ ...base, sections: [] }).removed };
136
+ const removed = syncLessons({ ...base, sections: [] }).removed;
137
+ remember();
138
+ return { status: 'skipped', reason: parsed.skip, removed };
38
139
  }
39
140
  const sections = parsed.sections.map(section => {
40
141
  const masked = maskLessonText(section.text);
@@ -44,7 +145,9 @@ export function harvestNativeMemory(input) {
44
145
  });
45
146
  return { anchor: section.anchor, textMasked: masked.text, ...classified };
46
147
  });
47
- return { status: 'harvested', ...syncLessons({ ...base, sections }) };
148
+ const synced = syncLessons({ ...base, sections });
149
+ remember();
150
+ return { status: 'harvested', ...synced };
48
151
  }
49
152
  function sourceProject(source, folders) {
50
153
  if (source.global || !source.claudeFolder || !source.claudeSlug)
@@ -60,25 +163,91 @@ function sourceProject(source, folders) {
60
163
  return resolved;
61
164
  }
62
165
  /**
63
- * One full pass: discover every known native-memory file, harvest it under its
64
- * project's identity, and delete rows whose file has gone. Nothing is read
65
- * until the user has accepted consent v2.
166
+ * Whether a file can be passed over without reading it or resolving which
167
+ * project it belongs to. Deliberately cheap: one stat and one indexed read.
168
+ * Project resolution is the expensive part of a pass — it can walk a drive
169
+ * root — so a quiet pass must never get that far.
66
170
  */
67
- export function syncLessonSources(home) {
171
+ function untouchedSince(source, recheckUnresolved) {
172
+ const known = lessonFileState(source.source, source.sourcePath);
173
+ if (!known)
174
+ return false;
175
+ // A file that has failed to be read this many times running is treated as
176
+ // gone until a command looks for it again; even the stat is skipped, because
177
+ // the stat is the part that hangs.
178
+ if (!recheckUnresolved && known.unavailable_streak >= UNAVAILABLE_BACKOFF_AFTER)
179
+ return 'gone';
180
+ if (known.harvester_version !== HARVESTER_VERSION)
181
+ return false;
182
+ // A folder never traced to a directory on this machine may become traceable
183
+ // when a drive is plugged back in, and only resolving it again would notice.
184
+ // A pass with time to spare looks; a save does not, because that lookup is
185
+ // the one that walks a drive root.
186
+ if (recheckUnresolved && known.project_identity?.startsWith('claude-slug:'))
187
+ return false;
188
+ try {
189
+ const stat = statSync(source.sourcePath);
190
+ return known.mtime === stat.mtime.toISOString() && known.size === stat.size;
191
+ }
192
+ catch {
193
+ return false;
194
+ }
195
+ }
196
+ /**
197
+ * One pass: discover every known native-memory file, harvest what changed
198
+ * under its project's identity, and delete rows whose file has gone. Nothing
199
+ * is read until the user has accepted consent v2, and an excluded project's
200
+ * files are never read at all.
201
+ *
202
+ * A pass given a budget stops cleanly when it runs out and reports what it did
203
+ * not reach. Every step is idempotent, so the next pass simply carries on.
204
+ */
205
+ export function syncLessonSources(options = {}) {
206
+ const opts = typeof options === 'string' ? { home: options } : options;
207
+ const started = Date.now();
68
208
  const report = {
69
- consent: isLessonsSharingEnabled(), sources: 0, harvested: 0, skipped: 0, disabled: 0, unavailable: 0,
70
- inserted: 0, updated: 0, removed: 0, unresolvedProjects: 0,
209
+ consent: isLessonsSharingEnabled(), sources: 0, harvested: 0, unchanged: 0, skipped: 0, disabled: 0, unavailable: 0, excluded: 0,
210
+ inserted: 0, updated: 0, removed: 0, unresolvedProjects: 0, notReached: 0, elapsedMs: 0,
71
211
  };
72
212
  if (!report.consent)
73
213
  return report;
74
- const sources = discoverNativeMemorySources(home);
214
+ const sources = discoverNativeMemorySources(opts.home);
75
215
  report.sources = sources.length;
76
216
  const folders = new Map();
217
+ // Only a pass that is not racing a save can afford to look for folders that
218
+ // were unresolved last time.
219
+ const recheckUnresolved = opts.budgetMs === undefined;
220
+ let index = 0;
77
221
  for (const source of sources) {
222
+ // Checked before every file, not once at the start: a single slow stat on
223
+ // a drive that is no longer plugged in must not carry the whole pass past
224
+ // its bound (council 534e2bd5, devil).
225
+ if (opts.budgetMs !== undefined && Date.now() - started >= opts.budgetMs) {
226
+ report.notReached = sources.length - index;
227
+ break;
228
+ }
229
+ index++;
230
+ if (!opts.forced) {
231
+ const state = untouchedSince(source, recheckUnresolved);
232
+ if (state === 'gone') {
233
+ report.unavailable++;
234
+ continue;
235
+ }
236
+ if (state) {
237
+ report.unchanged++;
238
+ continue;
239
+ }
240
+ }
78
241
  const project = sourceProject(source, folders);
242
+ if (isProjectExcluded(project.identity)) {
243
+ // Not even read: an excluded project's notes never reach the store.
244
+ report.excluded++;
245
+ report.removed += deleteLessonSource(source.source, source.sourcePath);
246
+ continue;
247
+ }
79
248
  const outcome = harvestNativeMemory({
80
249
  source: source.source, sourcePath: source.sourcePath, projectIdentity: project.identity,
81
- projectLabel: project.label, projectNames: project.names, global: source.global,
250
+ projectLabel: project.label, projectNames: project.names, global: source.global, root: source.root, forced: opts.forced,
82
251
  });
83
252
  report[outcome.status]++;
84
253
  if (outcome.status === 'harvested') {
@@ -89,8 +258,14 @@ export function syncLessonSources(home) {
89
258
  if (outcome.status === 'skipped')
90
259
  report.removed += outcome.removed;
91
260
  }
92
- report.unresolvedProjects = [...folders.values()].filter(f => f.identity.startsWith('claude-slug:')).length;
93
- report.removed += purgeVanishedLessonSources(sources);
261
+ report.unresolvedProjects = unresolvedProjectCount();
262
+ // A pass cut short has not seen every source, so it cannot conclude that a
263
+ // missing one is gone for good.
264
+ if (report.notReached === 0) {
265
+ report.removed += purgeVanishedLessonSources(sources);
266
+ purgeVanishedLessonFileState(sources);
267
+ }
268
+ report.elapsedMs = Date.now() - started;
94
269
  return report;
95
270
  }
96
271
  //# sourceMappingURL=harvest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"harvest.js","sourceRoot":"","sources":["../../src/lessons/harvest.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"harvest.js","sourceRoot":"","sources":["../../src/lessons/harvest.ts"],"names":[],"mappings":"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"}