@scotthamilton77/sidekick 0.1.25 → 0.1.26

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.
package/dist/daemon.js CHANGED
@@ -62,6 +62,7 @@ var require_hook_events = __commonJS({
62
62
  exports2.isPostToolUseEvent = isPostToolUseEvent;
63
63
  exports2.isStopEvent = isStopEvent;
64
64
  exports2.isPreCompactEvent = isPreCompactEvent;
65
+ exports2.isPostCompactEvent = isPostCompactEvent;
65
66
  exports2.isSubagentStartEvent = isSubagentStartEvent;
66
67
  exports2.isSubagentStopEvent = isSubagentStopEvent;
67
68
  exports2.HOOK_NAMES = [
@@ -72,6 +73,7 @@ var require_hook_events = __commonJS({
72
73
  "PostToolUse",
73
74
  "Stop",
74
75
  "PreCompact",
76
+ "PostCompact",
75
77
  "SubagentStart",
76
78
  "SubagentStop"
77
79
  ];
@@ -96,6 +98,9 @@ var require_hook_events = __commonJS({
96
98
  function isPreCompactEvent(event) {
97
99
  return event.hook === "PreCompact";
98
100
  }
101
+ function isPostCompactEvent(event) {
102
+ return event.hook === "PostCompact";
103
+ }
99
104
  function isSubagentStartEvent(event) {
100
105
  return event.hook === "SubagentStart";
101
106
  }
@@ -16951,7 +16956,8 @@ var require_tasks = __commonJS({
16951
16956
  });
16952
16957
  exports2.CleanupPayloadSchema = zod_1.z.object({
16953
16958
  maxAgeMs: zod_1.z.number().optional(),
16954
- dryRun: zod_1.z.boolean().optional()
16959
+ dryRun: zod_1.z.boolean().optional(),
16960
+ logMaxAgeMs: zod_1.z.number().optional()
16955
16961
  });
16956
16962
  exports2.MetricsPersistPayloadSchema = zod_1.z.object({
16957
16963
  sessionId: zod_1.z.string(),
@@ -16976,7 +16982,7 @@ var require_hook_input = __commonJS({
16976
16982
  "../types/dist/hook-input.js"(exports2) {
16977
16983
  "use strict";
16978
16984
  Object.defineProperty(exports2, "__esModule", { value: true });
16979
- exports2.HookInputSchema = exports2.StatuslineInputSchema = exports2.StatuslineWorkspaceSchema = exports2.StatuslineCostSchema = exports2.StatuslineContextWindowSchema = exports2.StatuslineModelSchema = exports2.SubagentStopInputSchema = exports2.SubagentStartInputSchema = exports2.NotificationInputSchema = exports2.PreCompactInputSchema = exports2.SessionEndInputSchema = exports2.SessionStartInputSchema = exports2.StopInputSchema = exports2.PostToolUseInputSchema = exports2.PreToolUseInputSchema = exports2.UserPromptSubmitInputSchema = exports2.HookInputBaseSchema = void 0;
16985
+ exports2.HookInputSchema = exports2.StatuslineInputSchema = exports2.StatuslineWorkspaceSchema = exports2.StatuslineCostSchema = exports2.StatuslineContextWindowSchema = exports2.StatuslineModelSchema = exports2.SubagentStopInputSchema = exports2.SubagentStartInputSchema = exports2.NotificationInputSchema = exports2.PostCompactInputSchema = exports2.PreCompactInputSchema = exports2.SessionEndInputSchema = exports2.SessionStartInputSchema = exports2.StopInputSchema = exports2.PostToolUseInputSchema = exports2.PreToolUseInputSchema = exports2.UserPromptSubmitInputSchema = exports2.HookInputBaseSchema = void 0;
16980
16986
  var zod_1 = require_zod();
16981
16987
  exports2.HookInputBaseSchema = zod_1.z.object({
16982
16988
  /** Unique identifier for the current Claude session */
@@ -17034,6 +17040,10 @@ var require_hook_input = __commonJS({
17034
17040
  /** Custom instructions from /compact command (empty for auto) */
17035
17041
  custom_instructions: zod_1.z.string().optional()
17036
17042
  });
17043
+ exports2.PostCompactInputSchema = exports2.HookInputBaseSchema.extend({
17044
+ /** 'manual' (user ran /compact) or 'auto' (context window threshold) */
17045
+ compaction_trigger: zod_1.z.enum(["manual", "auto"]).optional()
17046
+ });
17037
17047
  exports2.NotificationInputSchema = exports2.HookInputBaseSchema.extend({
17038
17048
  /** Notification message text */
17039
17049
  message: zod_1.z.string(),
@@ -17122,6 +17132,7 @@ var require_hook_input = __commonJS({
17122
17132
  exports2.PostToolUseInputSchema,
17123
17133
  exports2.StopInputSchema,
17124
17134
  exports2.PreCompactInputSchema,
17135
+ exports2.PostCompactInputSchema,
17125
17136
  exports2.SubagentStartInputSchema,
17126
17137
  exports2.SubagentStopInputSchema,
17127
17138
  exports2.NotificationInputSchema,
@@ -26397,6 +26408,32 @@ var require_claude_paths = __commonJS({
26397
26408
  }
26398
26409
  });
26399
26410
 
26411
+ // ../sidekick-core/dist/sidekick-paths.js
26412
+ var require_sidekick_paths = __commonJS({
26413
+ "../sidekick-core/dist/sidekick-paths.js"(exports2) {
26414
+ "use strict";
26415
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
26416
+ return mod && mod.__esModule ? mod : { "default": mod };
26417
+ };
26418
+ Object.defineProperty(exports2, "__esModule", { value: true });
26419
+ exports2.resolveHome = resolveHome;
26420
+ exports2.userSidekickRoot = userSidekickRoot;
26421
+ exports2.projectStateDir = projectStateDir;
26422
+ var node_os_1 = __importDefault(require("node:os"));
26423
+ var node_path_1 = require("node:path");
26424
+ var claude_paths_js_1 = require_claude_paths();
26425
+ function resolveHome(home) {
26426
+ return home && home.trim() ? home : node_os_1.default.homedir();
26427
+ }
26428
+ function userSidekickRoot(home) {
26429
+ return (0, node_path_1.join)(resolveHome(home), ".sidekick");
26430
+ }
26431
+ function projectStateDir(projectRoot, home) {
26432
+ return (0, node_path_1.join)(userSidekickRoot(home), "projects", (0, claude_paths_js_1.encodeProjectPath)(projectRoot));
26433
+ }
26434
+ }
26435
+ });
26436
+
26400
26437
  // ../sidekick-core/dist/persona-loader.js
26401
26438
  var require_persona_loader = __commonJS({
26402
26439
  "../sidekick-core/dist/persona-loader.js"(exports2) {
@@ -27065,10 +27102,19 @@ var require_config2 = __commonJS({
27065
27102
  var ProjectsSchema = v4_1.z.object({
27066
27103
  retentionDays: v4_1.z.number().min(1)
27067
27104
  }).strict();
27105
+ var CleanupSchema = v4_1.z.object({
27106
+ sessionMaxAgeDays: v4_1.z.number().min(1).default(7),
27107
+ logMaxAgeDays: v4_1.z.number().min(1).default(30),
27108
+ intervalHours: v4_1.z.number().min(1).default(4)
27109
+ }).strict();
27068
27110
  var DaemonSchema = v4_1.z.object({
27069
27111
  idleTimeoutMs: v4_1.z.number().min(0),
27070
27112
  shutdownTimeoutMs: v4_1.z.number().min(0),
27071
- projects: ProjectsSchema.default({ retentionDays: 30 })
27113
+ projects: ProjectsSchema.default({ retentionDays: 30 }),
27114
+ // Per-field defaults (see CleanupSchema) make any subset of keys valid —
27115
+ // partial overrides are filled at parse time. The object-level default
27116
+ // covers the case where `cleanup` is omitted entirely.
27117
+ cleanup: CleanupSchema.default({ sessionMaxAgeDays: 7, logMaxAgeDays: 30, intervalHours: 4 })
27072
27118
  }).strict();
27073
27119
  var IpcSchema = v4_1.z.object({
27074
27120
  connectTimeoutMs: v4_1.z.number().min(0),
@@ -28145,6 +28191,7 @@ var require_transport = __commonJS({
28145
28191
  var crypto_1 = __importDefault(require("crypto"));
28146
28192
  var os_1 = __importDefault(require("os"));
28147
28193
  var path_1 = __importDefault(require("path"));
28194
+ var sidekick_paths_js_1 = require_sidekick_paths();
28148
28195
  function getProjectHash(projectDir2) {
28149
28196
  return crypto_1.default.createHash("sha256").update(projectDir2).digest("hex").substring(0, 16);
28150
28197
  }
@@ -28170,14 +28217,14 @@ var require_transport = __commonJS({
28170
28217
  throw new Error(`Socket path exceeds Unix limit of ${exports2.UNIX_PATH_MAX} characters (got ${socketPath.length}): ${socketPath}`);
28171
28218
  }
28172
28219
  }
28173
- function getTokenPath(projectDir2) {
28174
- return path_1.default.join(projectDir2, ".sidekick", "sidekickd.token");
28220
+ function getTokenPath(projectDir2, home) {
28221
+ return path_1.default.join((0, sidekick_paths_js_1.projectStateDir)(projectDir2, home), "sidekickd.token");
28175
28222
  }
28176
- function getPidPath(projectDir2) {
28177
- return path_1.default.join(projectDir2, ".sidekick", "sidekickd.pid");
28223
+ function getPidPath(projectDir2, home) {
28224
+ return path_1.default.join((0, sidekick_paths_js_1.projectStateDir)(projectDir2, home), "sidekickd.pid");
28178
28225
  }
28179
- function getLockPath(projectDir2) {
28180
- return path_1.default.join(projectDir2, ".sidekick", "sidekickd.lock");
28226
+ function getLockPath(projectDir2, home) {
28227
+ return path_1.default.join((0, sidekick_paths_js_1.projectStateDir)(projectDir2, home), "sidekickd.lock");
28181
28228
  }
28182
28229
  function getUserDaemonsDir() {
28183
28230
  return path_1.default.join(os_1.default.homedir(), ".sidekick", "daemons");
@@ -33000,6 +33047,46 @@ var require_log_events = __commonJS({
33000
33047
  }
33001
33048
  };
33002
33049
  },
33050
+ /** Create a CleanupTimerStarted event (logged when the cleanup timer starts). */
33051
+ cleanupTimerStarted(metadata) {
33052
+ return {
33053
+ type: "cleanup:timer-started",
33054
+ time: Date.now(),
33055
+ source: "daemon",
33056
+ context: EMPTY_CONTEXT,
33057
+ payload: { intervalMs: metadata.intervalMs }
33058
+ };
33059
+ },
33060
+ /** Create a CleanupTaskEnqueued event (logged when a cleanup task is enqueued). */
33061
+ cleanupTaskEnqueued(metadata) {
33062
+ return {
33063
+ type: "cleanup:task-enqueued",
33064
+ time: Date.now(),
33065
+ source: "daemon",
33066
+ context: EMPTY_CONTEXT,
33067
+ payload: { reason: metadata.reason }
33068
+ };
33069
+ },
33070
+ /** Create a CleanupSkippedRecent event (logged when startup cleanup is skipped). */
33071
+ cleanupSkippedRecent(metadata) {
33072
+ return {
33073
+ type: "cleanup:skipped-recent",
33074
+ time: Date.now(),
33075
+ source: "daemon",
33076
+ context: EMPTY_CONTEXT,
33077
+ payload: { lastCleanupAt: metadata.lastCleanupAt, intervalMs: metadata.intervalMs }
33078
+ };
33079
+ },
33080
+ /** Create a CleanupLogCompleted event (logged when log pruning finishes). */
33081
+ cleanupLogCompleted(metadata) {
33082
+ return {
33083
+ type: "cleanup:log-completed",
33084
+ time: Date.now(),
33085
+ source: "daemon",
33086
+ context: EMPTY_CONTEXT,
33087
+ payload: { cleaned: metadata.cleaned, skipped: metadata.skipped, dryRun: metadata.dryRun }
33088
+ };
33089
+ },
33003
33090
  // --- Statusline Events ---
33004
33091
  /**
33005
33092
  * Create a StatuslineRendered event (logged when statusline renders successfully).
@@ -45604,57 +45691,76 @@ var require_sandbox = __commonJS({
45604
45691
  }
45605
45692
  });
45606
45693
 
45607
- // ../../package.json
45608
- var require_package3 = __commonJS({
45609
- "../../package.json"(exports2, module2) {
45610
- module2.exports = {
45611
- name: "claude-code-sidekick",
45612
- private: true,
45613
- version: "0.1.0",
45614
- type: "module",
45615
- packageManager: "pnpm@9.12.2",
45616
- scripts: {
45617
- build: "pnpm -r build",
45618
- clean: "rm -rf packages/*/dist packages/*/*.tsbuildinfo",
45619
- typecheck: "tsc -p tsconfig.lint.json --noEmit",
45620
- test: "pnpm -r test",
45621
- "test:coverage": "vitest run --coverage",
45622
- "test:coverage:full": "INTEGRATION_TESTS=1 vitest run --coverage",
45623
- lint: "NODE_OPTIONS='--max-old-space-size=4096' eslint packages",
45624
- "lint:fix": "NODE_OPTIONS='--max-old-space-size=4096' eslint packages --fix",
45625
- format: 'prettier --write "packages/**/*.ts"',
45626
- sidekick: "node packages/sidekick-cli/dist/bin.js",
45627
- "test:dist:setup": "./scripts/test-dist.sh setup",
45628
- "test:dist:teardown": "./scripts/test-dist.sh teardown",
45629
- "test:dist:rebuild": "./scripts/test-dist.sh rebuild",
45630
- "publish:dist": "./scripts/publish-dist.sh"
45631
- },
45632
- pnpm: {
45633
- overrides: {
45634
- ajv: ">=6.14.0 <7",
45635
- minimatch: ">=10.2.1",
45636
- "vite@^7.0.0": "^7.3.2",
45637
- "picomatch@^4.0.0": "^4.0.4",
45638
- "picomatch@^2.0.0": "^2.3.2",
45639
- "flatted@^3.0.0": "^3.4.2",
45640
- "postcss@^8.0.0": "^8.5.10"
45694
+ // ../sidekick-core/dist/build-identity.js
45695
+ var require_build_identity = __commonJS({
45696
+ "../sidekick-core/dist/build-identity.js"(exports2) {
45697
+ "use strict";
45698
+ var __importDefault = exports2 && exports2.__importDefault || function(mod) {
45699
+ return mod && mod.__esModule ? mod : { "default": mod };
45700
+ };
45701
+ Object.defineProperty(exports2, "__esModule", { value: true });
45702
+ exports2.computeBuildIdentity = computeBuildIdentity;
45703
+ exports2.computeDevFingerprint = computeDevFingerprint;
45704
+ exports2.discoverDistDirs = discoverDistDirs;
45705
+ exports2.fingerprintDistDirs = fingerprintDistDirs;
45706
+ var fs_1 = require("fs");
45707
+ var path_1 = __importDefault(require("path"));
45708
+ var DEV_PREFIX = "dev:";
45709
+ var NO_DIST_SENTINEL = "nodist";
45710
+ function computeBuildIdentity(injectedVersion = true ? "0.1.26" : void 0, devFingerprint = computeDevFingerprint) {
45711
+ return injectedVersion !== void 0 ? injectedVersion : DEV_PREFIX + devFingerprint();
45712
+ }
45713
+ function computeDevFingerprint() {
45714
+ const repoRoot = path_1.default.resolve(__dirname, "../../..");
45715
+ return fingerprintDistDirs(discoverDistDirs(repoRoot));
45716
+ }
45717
+ function discoverDistDirs(repoRoot) {
45718
+ const packagesDir = path_1.default.join(repoRoot, "packages");
45719
+ let entries;
45720
+ try {
45721
+ entries = (0, fs_1.readdirSync)(packagesDir);
45722
+ } catch {
45723
+ return [];
45724
+ }
45725
+ const dirs = [];
45726
+ for (const entry of entries) {
45727
+ const distDir = path_1.default.join(packagesDir, entry, "dist");
45728
+ try {
45729
+ if ((0, fs_1.statSync)(distDir).isDirectory())
45730
+ dirs.push(distDir);
45731
+ } catch {
45641
45732
  }
45642
- },
45643
- devDependencies: {
45644
- "@eslint/js": "^10.0.1",
45645
- "@types/node": "^22.19.17",
45646
- "@typescript-eslint/eslint-plugin": "^8.59.1",
45647
- "@typescript-eslint/parser": "^8.59.1",
45648
- "@vitest/coverage-v8": "^4.1.5",
45649
- eslint: "^10.3.0",
45650
- "eslint-config-prettier": "^9.1.2",
45651
- "eslint-plugin-prettier": "^5.5.5",
45652
- prettier: "^3.8.3",
45653
- typescript: "^5.9.3",
45654
- "typescript-eslint": "^8.59.1",
45655
- vitest: "^4.1.5"
45656
45733
  }
45657
- };
45734
+ return dirs;
45735
+ }
45736
+ function fingerprintDistDirs(distDirs) {
45737
+ let maxMtimeMs = 0;
45738
+ for (const dir of distDirs) {
45739
+ maxMtimeMs = Math.max(maxMtimeMs, maxMtimeInDir(dir));
45740
+ }
45741
+ return maxMtimeMs === 0 ? NO_DIST_SENTINEL : Math.floor(maxMtimeMs).toString(36);
45742
+ }
45743
+ function maxMtimeInDir(dir) {
45744
+ let max = 0;
45745
+ let entries;
45746
+ try {
45747
+ entries = (0, fs_1.readdirSync)(dir, { withFileTypes: true });
45748
+ } catch {
45749
+ return 0;
45750
+ }
45751
+ for (const entry of entries) {
45752
+ const full = path_1.default.join(dir, entry.name);
45753
+ try {
45754
+ if (entry.isDirectory()) {
45755
+ max = Math.max(max, maxMtimeInDir(full));
45756
+ } else if (entry.isFile()) {
45757
+ max = Math.max(max, (0, fs_1.statSync)(full).mtimeMs);
45758
+ }
45759
+ } catch {
45760
+ }
45761
+ }
45762
+ return max;
45763
+ }
45658
45764
  }
45659
45765
  });
45660
45766
 
@@ -45678,10 +45784,11 @@ var require_daemon_client2 = __commonJS({
45678
45784
  var transport_js_1 = require_transport();
45679
45785
  var sandbox_js_1 = require_sandbox();
45680
45786
  var error_utils_js_1 = require_error_utils();
45787
+ var build_identity_js_1 = require_build_identity();
45681
45788
  var LOCK_TIMEOUT_MS = 1e4;
45682
45789
  var LOCK_RETRY_INTERVAL_MS = 100;
45683
45790
  var LOCK_STALE_THRESHOLD_MS = 3e4;
45684
- var CLIENT_VERSION = require_package3().version;
45791
+ var CLIENT_VERSION = (0, build_identity_js_1.computeBuildIdentity)();
45685
45792
  var DaemonClient = class {
45686
45793
  projectDir;
45687
45794
  logger;
@@ -46213,16 +46320,14 @@ var require_gitignore = __commonJS({
46213
46320
  exports2.SIDEKICK_SECTION_START = "# >>> sidekick";
46214
46321
  exports2.SIDEKICK_SECTION_END = "# <<< sidekick";
46215
46322
  exports2.SIDEKICK_GITIGNORE_HEADER = "# Sidekick \u2014 managed file, do not edit manually";
46216
- exports2.GITIGNORE_ENTRIES = [
46323
+ exports2.GITIGNORE_ENTRIES = [".env", ".env.local", "*.local.yaml"];
46324
+ var STALE_GITIGNORE_ENTRIES = [
46217
46325
  "logs/",
46218
46326
  "sessions/",
46219
46327
  "state/",
46220
46328
  "setup-status.json",
46221
- ".env",
46222
- ".env.local",
46223
46329
  "sidekick*.pid",
46224
- "sidekick*.token",
46225
- "*.local.yaml"
46330
+ "sidekick*.token"
46226
46331
  ];
46227
46332
  async function installGitignoreSection(projectDir2) {
46228
46333
  let status;
@@ -46253,7 +46358,8 @@ var require_gitignore = __commonJS({
46253
46358
  try {
46254
46359
  const content = await fs.readFile(sidekickGitignorePath, "utf-8");
46255
46360
  const missingEntries = exports2.GITIGNORE_ENTRIES.filter((entry) => !content.includes(entry));
46256
- return missingEntries.length === 0 ? "installed" : "incomplete";
46361
+ const staleEntries = STALE_GITIGNORE_ENTRIES.filter((entry) => content.includes(entry));
46362
+ return missingEntries.length === 0 && staleEntries.length === 0 ? "installed" : "incomplete";
46257
46363
  } catch (err) {
46258
46364
  if (err.code !== "ENOENT") {
46259
46365
  throw err;
@@ -57309,9 +57415,9 @@ var require_setup_status_service = __commonJS({
57309
57415
  exports2.createSetupStatusService = createSetupStatusService;
57310
57416
  var fs = __importStar(require("node:fs/promises"));
57311
57417
  var path = __importStar(require("node:path"));
57312
- var os = __importStar(require("node:os"));
57313
57418
  var types_1 = require_dist();
57314
57419
  var gitignore_js_1 = require_gitignore();
57420
+ var sidekick_paths_js_1 = require_sidekick_paths();
57315
57421
  var api_key_detector_js_1 = require_api_key_detector();
57316
57422
  var plugin_detector_js_1 = require_plugin_detector();
57317
57423
  var doctor_engine_js_1 = require_doctor_engine();
@@ -57323,7 +57429,7 @@ var require_setup_status_service = __commonJS({
57323
57429
  logger;
57324
57430
  constructor(projectDir2, options) {
57325
57431
  this.projectDir = projectDir2;
57326
- this.homeDir = options?.homeDir ?? os.homedir();
57432
+ this.homeDir = (0, sidekick_paths_js_1.resolveHome)(options?.homeDir);
57327
57433
  this.logger = options?.logger;
57328
57434
  }
57329
57435
  // === Paths ===
@@ -57331,7 +57437,7 @@ var require_setup_status_service = __commonJS({
57331
57437
  return path.join(this.homeDir, ".sidekick", exports2.USER_STATUS_FILENAME);
57332
57438
  }
57333
57439
  get projectStatusPath() {
57334
- return path.join(this.projectDir, ".sidekick", exports2.PROJECT_STATUS_FILENAME);
57440
+ return path.join((0, sidekick_paths_js_1.projectStateDir)(this.projectDir, this.homeDir), exports2.PROJECT_STATUS_FILENAME);
57335
57441
  }
57336
57442
  // === Feature config ===
57337
57443
  /**
@@ -58593,10 +58699,24 @@ var require_path_resolver = __commonJS({
58593
58699
  Object.defineProperty(exports2, "__esModule", { value: true });
58594
58700
  exports2.PathResolver = void 0;
58595
58701
  var node_path_1 = require("node:path");
58702
+ var sidekick_paths_js_1 = require_sidekick_paths();
58596
58703
  var PathResolver = class {
58597
58704
  stateBase;
58598
- constructor(projectRoot, stateDir = ".sidekick") {
58599
- this.stateBase = (0, node_path_1.join)(projectRoot, stateDir);
58705
+ /**
58706
+ * @param projectRoot Project root (project scope) or an absolute state root (user scope).
58707
+ * @param stateDir Scope selector, NOT a path segment. Only two values are
58708
+ * meaningful: `''` selects user scope (use `projectRoot` as the absolute base,
58709
+ * as-is); any other value (default `'.sidekick'`) selects project scope, where
58710
+ * runtime state is redirected to `~/.sidekick/projects/<encoded-id>/` and the
58711
+ * string content is ignored.
58712
+ * @param home Optional override for the user home that anchors the redirected
58713
+ * project state dir. Only consulted in project scope; in user scope the base
58714
+ * is already absolute. Defaults to `os.homedir()` when omitted. Thread this
58715
+ * from an injected home (production: `$HOME` via env; tests: a programmatic
58716
+ * override) so state, logs, and sessions all resolve under the same root.
58717
+ */
58718
+ constructor(projectRoot, stateDir = ".sidekick", home) {
58719
+ this.stateBase = stateDir === "" ? projectRoot : (0, sidekick_paths_js_1.projectStateDir)(projectRoot, home);
58600
58720
  }
58601
58721
  // === Directories ===
58602
58722
  /** Root state directory (.sidekick or user equivalent) */
@@ -58712,7 +58832,7 @@ var require_state_service = __commonJS({
58712
58832
  cache;
58713
58833
  configGetter;
58714
58834
  constructor(projectRoot, options) {
58715
- this.paths = new path_resolver_js_1.PathResolver(projectRoot, options?.stateDir);
58835
+ this.paths = new path_resolver_js_1.PathResolver(projectRoot, options?.stateDir, options?.homeDir);
58716
58836
  this.staleThresholdMs = options?.staleThresholdMs ?? 6e4;
58717
58837
  this.logger = options?.logger;
58718
58838
  this.cache = options?.cache ? /* @__PURE__ */ new Map() : null;
@@ -59736,7 +59856,6 @@ var require_transcript_metrics_engine = __commonJS({
59736
59856
  exports2.extractTokenUsage = extractTokenUsage;
59737
59857
  exports2.processNestedToolUses = processNestedToolUses;
59738
59858
  exports2.processNestedToolResults = processNestedToolResults;
59739
- exports2.handleCompactBoundary = handleCompactBoundary;
59740
59859
  exports2.isToolResultOnlyMessage = isToolResultOnlyMessage;
59741
59860
  exports2.isLocalCommandStdoutMessage = isLocalCommandStdoutMessage;
59742
59861
  exports2.isExcludedBuiltinCommandInvocation = isExcludedBuiltinCommandInvocation;
@@ -59771,13 +59890,8 @@ var require_transcript_metrics_engine = __commonJS({
59771
59890
  await emitEvent("AssistantMessage", entry, lineNumber);
59772
59891
  await processNestedToolUses(entry, lineNumber, metrics, toolUseIdToName, emitEvent);
59773
59892
  break;
59774
- case "system": {
59775
- const subtype = entry.subtype;
59776
- if (subtype === "compact_boundary") {
59777
- await handleCompactBoundary(entry, lineNumber, metrics, emitEvent);
59778
- }
59893
+ case "system":
59779
59894
  break;
59780
- }
59781
59895
  }
59782
59896
  }
59783
59897
  function extractTokenUsage(entry, metrics) {
@@ -59855,11 +59969,6 @@ var require_transcript_metrics_engine = __commonJS({
59855
59969
  }
59856
59970
  }
59857
59971
  }
59858
- async function handleCompactBoundary(entry, lineNumber, metrics, emitEvent) {
59859
- metrics.currentContextTokens = null;
59860
- metrics.isPostCompactIndeterminate = true;
59861
- await emitEvent("Compact", entry, lineNumber);
59862
- }
59863
59972
  function isToolResultOnlyMessage(entry) {
59864
59973
  const message = entry.message;
59865
59974
  if (!message?.content)
@@ -62240,6 +62349,13 @@ var require_transcript_service = __commonJS({
62240
62349
  // ============================================================================
62241
62350
  // Compaction Management
62242
62351
  // ============================================================================
62352
+ async signalCompaction() {
62353
+ this.metrics.currentContextTokens = null;
62354
+ this.metrics.isPostCompactIndeterminate = true;
62355
+ await this.emitEvent("Compact", {}, this.metrics.lastProcessedLine);
62356
+ this.notifyMetricsChange();
62357
+ this.schedulePersistence();
62358
+ }
62243
62359
  async capturePreCompactState(snapshotPath) {
62244
62360
  if (!this.transcriptPath) {
62245
62361
  throw new Error("TranscriptService not initialized");
@@ -62448,6 +62564,18 @@ var require_service_factory2 = __commonJS({
62448
62564
  }
62449
62565
  return service;
62450
62566
  }
62567
+ /**
62568
+ * Get the cached TranscriptService for a session if it exists.
62569
+ * Returns undefined if no service has been prepared for this session yet.
62570
+ * Touches the session access time if the service exists.
62571
+ */
62572
+ getTranscriptService(sessionId) {
62573
+ const service = this.transcriptServices.get(sessionId);
62574
+ if (service) {
62575
+ this.touchSession(sessionId);
62576
+ }
62577
+ return service;
62578
+ }
62451
62579
  /**
62452
62580
  * Shutdown a session's services (called on SessionEnd).
62453
62581
  */
@@ -63030,28 +63158,69 @@ var require_project_registry = __commonJS({
63030
63158
  return mod && mod.__esModule ? mod : { "default": mod };
63031
63159
  };
63032
63160
  Object.defineProperty(exports2, "__esModule", { value: true });
63033
- exports2.ProjectRegistryService = void 0;
63034
- exports2.encodeProjectDir = encodeProjectDir;
63161
+ exports2.ProjectRegistryService = exports2.encodeProjectDir = void 0;
63035
63162
  var node_fs_1 = require("node:fs");
63036
63163
  var promises_1 = __importDefault(require("node:fs/promises"));
63037
63164
  var node_crypto_1 = require("node:crypto");
63038
63165
  var node_path_1 = require("node:path");
63039
63166
  var types_1 = require_dist();
63040
- function encodeProjectDir(absPath) {
63041
- return absPath.replace(/\//g, "-");
63042
- }
63167
+ var claude_paths_js_1 = require_claude_paths();
63168
+ var error_utils_js_1 = require_error_utils();
63169
+ exports2.encodeProjectDir = claude_paths_js_1.encodeProjectPath;
63043
63170
  var REGISTRY_FILE = "registry.json";
63044
63171
  var ProjectRegistryService = class {
63045
63172
  registryRoot;
63046
- constructor(registryRoot) {
63173
+ logger;
63174
+ constructor(registryRoot, logger) {
63047
63175
  this.registryRoot = registryRoot;
63176
+ this.logger = logger;
63177
+ }
63178
+ /**
63179
+ * Read and validate a single registry entry, returning null when it cannot
63180
+ * be used.
63181
+ *
63182
+ * Two unreadable cases are deliberately distinguished:
63183
+ * - **Missing** `registry.json` (ENOENT) is expected: a CLI hook can create
63184
+ * the project dir with live runtime state (sessions/, state/, logs/) before
63185
+ * the daemon writes registry.json. Logged at debug only — routine, not a
63186
+ * fault.
63187
+ * - **Corrupt** (unreadable/unparseable/schema-invalid) is unexpected and
63188
+ * warned: a persistently-bad registry.json silently excludes the dir from
63189
+ * both listing and pruning, so it must leave a trace.
63190
+ *
63191
+ * Callers treat null as "skip this dir" — never as "safe to delete".
63192
+ */
63193
+ async readEntry(entryFile) {
63194
+ let raw;
63195
+ try {
63196
+ raw = await promises_1.default.readFile(entryFile, "utf-8");
63197
+ } catch (err) {
63198
+ if (err.code === "ENOENT") {
63199
+ this.logger?.debug("Project registry dir has no registry.json; skipping", { entryFile });
63200
+ } else {
63201
+ this.logger?.warn("Failed to read project registry entry; skipping", {
63202
+ entryFile,
63203
+ error: (0, error_utils_js_1.toErrorMessage)(err)
63204
+ });
63205
+ }
63206
+ return null;
63207
+ }
63208
+ try {
63209
+ return types_1.ProjectRegistryEntrySchema.parse(JSON.parse(raw));
63210
+ } catch (err) {
63211
+ this.logger?.warn("Project registry entry is corrupt; skipping", {
63212
+ entryFile,
63213
+ error: (0, error_utils_js_1.toErrorMessage)(err)
63214
+ });
63215
+ return null;
63216
+ }
63048
63217
  }
63049
63218
  /**
63050
63219
  * Register or update a project in the registry.
63051
63220
  * Creates the directory and writes registry.json with current timestamp.
63052
63221
  */
63053
63222
  async register(projectDir2) {
63054
- const encoded = encodeProjectDir(projectDir2);
63223
+ const encoded = (0, exports2.encodeProjectDir)(projectDir2);
63055
63224
  const entryDir = (0, node_path_1.join)(this.registryRoot, encoded);
63056
63225
  const entryFile = (0, node_path_1.join)(entryDir, REGISTRY_FILE);
63057
63226
  await promises_1.default.mkdir(entryDir, { recursive: true });
@@ -63084,12 +63253,9 @@ var require_project_registry = __commonJS({
63084
63253
  if (!dirent.isDirectory())
63085
63254
  continue;
63086
63255
  const entryFile = (0, node_path_1.join)(this.registryRoot, dirent.name, REGISTRY_FILE);
63087
- try {
63088
- const raw = await promises_1.default.readFile(entryFile, "utf-8");
63089
- const parsed = types_1.ProjectRegistryEntrySchema.parse(JSON.parse(raw));
63090
- entries.push(parsed);
63091
- } catch {
63092
- }
63256
+ const entry = await this.readEntry(entryFile);
63257
+ if (entry)
63258
+ entries.push(entry);
63093
63259
  }
63094
63260
  return entries;
63095
63261
  }
@@ -63110,14 +63276,9 @@ var require_project_registry = __commonJS({
63110
63276
  continue;
63111
63277
  const entryDir = (0, node_path_1.join)(this.registryRoot, dirent.name);
63112
63278
  const entryFile = (0, node_path_1.join)(entryDir, REGISTRY_FILE);
63113
- let entry;
63114
- try {
63115
- const raw = await promises_1.default.readFile(entryFile, "utf-8");
63116
- entry = types_1.ProjectRegistryEntrySchema.parse(JSON.parse(raw));
63117
- } catch {
63118
- await promises_1.default.rm(entryDir, { recursive: true, force: true });
63279
+ const entry = await this.readEntry(entryFile);
63280
+ if (!entry)
63119
63281
  continue;
63120
- }
63121
63282
  let reason = null;
63122
63283
  if (!(0, node_fs_1.existsSync)(entry.path)) {
63123
63284
  reason = "path-missing";
@@ -63184,8 +63345,9 @@ var require_daemon_health = __commonJS({
63184
63345
  var node_path_1 = require("node:path");
63185
63346
  var types_1 = require_dist();
63186
63347
  var error_utils_js_1 = require_error_utils();
63187
- function healthFilePath(projectDir2) {
63188
- return (0, node_path_1.join)(projectDir2, ".sidekick", "state", "daemon-health.json");
63348
+ var sidekick_paths_js_1 = require_sidekick_paths();
63349
+ function healthFilePath(projectDir2, home) {
63350
+ return (0, node_path_1.join)((0, sidekick_paths_js_1.projectStateDir)(projectDir2, home), "state", "daemon-health.json");
63189
63351
  }
63190
63352
  function defaultHealth() {
63191
63353
  return {
@@ -63193,8 +63355,8 @@ var require_daemon_health = __commonJS({
63193
63355
  lastCheckedAt: (/* @__PURE__ */ new Date(0)).toISOString()
63194
63356
  };
63195
63357
  }
63196
- async function readDaemonHealth(projectDir2) {
63197
- const path = healthFilePath(projectDir2);
63358
+ async function readDaemonHealth(projectDir2, home) {
63359
+ const path = healthFilePath(projectDir2, home);
63198
63360
  try {
63199
63361
  const content = await fs.readFile(path, "utf-8");
63200
63362
  const json = JSON.parse(content);
@@ -63207,8 +63369,8 @@ var require_daemon_health = __commonJS({
63207
63369
  return defaultHealth();
63208
63370
  }
63209
63371
  }
63210
- async function writeDaemonHealth(projectDir2, health) {
63211
- const path = healthFilePath(projectDir2);
63372
+ async function writeDaemonHealth(projectDir2, health, home) {
63373
+ const path = healthFilePath(projectDir2, home);
63212
63374
  const dir = (0, node_path_1.dirname)(path);
63213
63375
  await fs.mkdir(dir, { recursive: true });
63214
63376
  const tmpPath = `${path}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}.tmp`;
@@ -63224,8 +63386,8 @@ var require_daemon_health = __commonJS({
63224
63386
  throw err;
63225
63387
  }
63226
63388
  }
63227
- async function updateDaemonHealth(projectDir2, newStatus, logger, error) {
63228
- const current = await readDaemonHealth(projectDir2);
63389
+ async function updateDaemonHealth(projectDir2, newStatus, logger, error, home) {
63390
+ const current = await readDaemonHealth(projectDir2, home);
63229
63391
  if (current.status === newStatus) {
63230
63392
  return false;
63231
63393
  }
@@ -63237,7 +63399,7 @@ var require_daemon_health = __commonJS({
63237
63399
  ...error !== void 0 && { error }
63238
63400
  };
63239
63401
  try {
63240
- await writeDaemonHealth(projectDir2, health);
63402
+ await writeDaemonHealth(projectDir2, health, home);
63241
63403
  } catch (err) {
63242
63404
  logger.warn("Failed to write daemon health", {
63243
63405
  from,
@@ -63420,7 +63582,7 @@ var require_dist4 = __commonJS({
63420
63582
  Object.defineProperty(exports2, "__esModule", { value: true });
63421
63583
  exports2.PROJECT_STATUS_FILENAME = exports2.USER_STATUS_FILENAME = exports2.createSetupStatusService = exports2.SetupStatusService = exports2.DaemonClient = exports2.findZombieDaemons = exports2.killZombieDaemons = exports2.killAllDaemons = exports2.SessionLogWriter = exports2.setSessionLogWriter = exports2.logEvent = exports2.LogEvents = exports2.DEFAULT_MAX_FILES = exports2.DEFAULT_ROTATE_SIZE_BYTES = exports2.getComponentLogLevel = exports2.setupGlobalErrorHandlers = exports2.createLoggerFacade = exports2.createLogManager = exports2.createConsoleLogger = exports2.getUserDaemonsDir = exports2.getUserPidPath = exports2.getTokenPath = exports2.getSocketPath = exports2.getProjectHash = exports2.getPidPath = exports2.getLockPath = exports2.IpcService = exports2.IpcServer = exports2.loadPersonaFile = exports2.getDefaultPersonasDir = exports2.discoverPersonas = exports2.createPersonaLoader = exports2.reconstructTranscriptPath = exports2.encodeProjectPath = exports2.isSubagentStopEvent = exports2.isSubagentStartEvent = exports2.isPreCompactEvent = exports2.isStopEvent = exports2.isPostToolUseEvent = exports2.isPreToolUseEvent = exports2.isUserPromptSubmitEvent = exports2.isSessionEndEvent = exports2.isSessionStartEvent = exports2.isTranscriptEvent = exports2.isHookEvent = exports2.MetricsPersistPayloadSchema = exports2.CleanupPayloadSchema = exports2.ResumeGenerationPayloadSchema = exports2.SessionSummaryPayloadSchema = exports2.TaskTypes = void 0;
63422
63584
  exports2.createDefaultTokenUsage = exports2.createDefaultMetrics = exports2.copyWithTimestampSync = exports2.renameWithTimestampSync = exports2.renameWithTimestamp = exports2.copyWithTimestamp = exports2.getTimestampedPath = exports2.extractToolResultPreview = exports2.extractToolCallPreview = exports2.extractTextFromContent = exports2.extractContentPreview = exports2.HandlerRegistryImpl = exports2.extractConsumedTimestamp = exports2.createConsumedFilePattern = exports2.CONSUMED_FILE_PATTERN = exports2.filterActiveReminderFiles = exports2.validatePathSegment = exports2.isValidPathSegment = exports2.getReminderPath = exports2.getHookDir = exports2.getStagingRoot = exports2.SessionScopedStagingService = exports2.StagingServiceCore = exports2.GITIGNORE_ENTRIES = exports2.SIDEKICK_GITIGNORE_HEADER = exports2.SIDEKICK_SECTION_END = exports2.SIDEKICK_SECTION_START = exports2.removeLegacyGitignoreSection = exports2.detectLegacyGitignoreSection = exports2.detectGitignoreStatus = exports2.removeGitignoreSection = exports2.installGitignoreSection = exports2.validateOpenAIKey = exports2.validateOpenRouterKey = exports2.runDoctorCheck = exports2.detectPluginLiveness = exports2.detectPluginInstallation = exports2.detectActualStatusline = exports2.spawnWithTimeout = exports2.getDoctorTimeout = exports2.DOCTOR_TIMEOUTS = exports2.projectApiKeyStatusFromHealth = exports2.userApiKeyStatusFromHealth = exports2.buildProjectApiKeyStatus = exports2.buildUserApiKeyStatus = exports2.detectAllApiKeys = exports2.detectActualApiKey = exports2.readKeyFromEnvFile = exports2.determineOverallStatus = exports2.toScopeStatus = void 0;
63423
- exports2.CoalescingGuard = exports2.loadUserProfile = exports2.parseGitStatusOutput = exports2.getGitFileStatus = exports2.toErrorMessage = exports2.isInSandbox = exports2.updateDaemonHealth = exports2.readDaemonHealth = exports2.ProjectRegistryService = exports2.encodeProjectDir = exports2.DaemonGlobalLogMetricsDescriptor = exports2.CliLogMetricsDescriptor = exports2.DaemonLogMetricsDescriptor = exports2.CompactionHistoryDescriptor = exports2.TranscriptMetricsDescriptor = exports2.GlobalStateAccessor = exports2.SessionStateAccessor = exports2.globalState = exports2.sessionState = exports2.StateCorruptError = exports2.StateNotFoundError = exports2.StateService = exports2.createHookableLogger = exports2.InstrumentedProfileProviderFactory = exports2.InstrumentedLLMProvider = exports2.ServiceFactoryImpl = exports2.TranscriptServiceImpl = void 0;
63585
+ exports2.computeBuildIdentity = exports2.CoalescingGuard = exports2.loadUserProfile = exports2.parseGitStatusOutput = exports2.getGitFileStatus = exports2.toErrorMessage = exports2.isInSandbox = exports2.updateDaemonHealth = exports2.readDaemonHealth = exports2.ProjectRegistryService = exports2.encodeProjectDir = exports2.DaemonGlobalLogMetricsDescriptor = exports2.CliLogMetricsDescriptor = exports2.DaemonLogMetricsDescriptor = exports2.CompactionHistoryDescriptor = exports2.TranscriptMetricsDescriptor = exports2.GlobalStateAccessor = exports2.SessionStateAccessor = exports2.globalState = exports2.sessionState = exports2.StateCorruptError = exports2.StateNotFoundError = exports2.StateService = exports2.createHookableLogger = exports2.InstrumentedProfileProviderFactory = exports2.InstrumentedLLMProvider = exports2.ServiceFactoryImpl = exports2.TranscriptServiceImpl = void 0;
63424
63586
  var types_1 = require_dist();
63425
63587
  Object.defineProperty(exports2, "TaskTypes", { enumerable: true, get: function() {
63426
63588
  return types_1.TaskTypes;
@@ -63479,6 +63641,7 @@ var require_dist4 = __commonJS({
63479
63641
  Object.defineProperty(exports2, "reconstructTranscriptPath", { enumerable: true, get: function() {
63480
63642
  return claude_paths_1.reconstructTranscriptPath;
63481
63643
  } });
63644
+ __exportStar(require_sidekick_paths(), exports2);
63482
63645
  var persona_loader_1 = require_persona_loader();
63483
63646
  Object.defineProperty(exports2, "createPersonaLoader", { enumerable: true, get: function() {
63484
63647
  return persona_loader_1.createPersonaLoader;
@@ -63845,6 +64008,10 @@ var require_dist4 = __commonJS({
63845
64008
  Object.defineProperty(exports2, "CoalescingGuard", { enumerable: true, get: function() {
63846
64009
  return coalescing_guard_1.CoalescingGuard;
63847
64010
  } });
64011
+ var build_identity_1 = require_build_identity();
64012
+ Object.defineProperty(exports2, "computeBuildIdentity", { enumerable: true, get: function() {
64013
+ return build_identity_1.computeBuildIdentity;
64014
+ } });
63848
64015
  }
63849
64016
  });
63850
64017
 
@@ -73962,6 +74129,7 @@ var require_stage_persona_reminders = __commonJS({
73962
74129
  Object.defineProperty(exports2, "__esModule", { value: true });
73963
74130
  exports2.restagePersonaRemindersForActiveSessions = restagePersonaRemindersForActiveSessions;
73964
74131
  exports2.stagePersonaRemindersForSession = stagePersonaRemindersForSession;
74132
+ exports2.resolvePersonaContextForSnapshot = resolvePersonaContextForSnapshot;
73965
74133
  exports2.registerStagePersonaReminders = registerStagePersonaReminders;
73966
74134
  var core_1 = require_dist4();
73967
74135
  var events_js_1 = require_events2();
@@ -74125,6 +74293,20 @@ var require_stage_persona_reminders = __commonJS({
74125
74293
  includeChanged: options?.includeChangedReminder ?? false
74126
74294
  });
74127
74295
  }
74296
+ async function resolvePersonaContextForSnapshot(ctx, sessionId) {
74297
+ if (!isPersonaInjectionEnabled(ctx))
74298
+ return void 0;
74299
+ const persona = await loadPersonaForSession(ctx, sessionId);
74300
+ if (!persona)
74301
+ return void 0;
74302
+ const templateContext = buildPersonaTemplateContext(persona);
74303
+ const reminder = (0, reminder_utils_js_1.resolveReminder)(types_js_1.ReminderIds.REMEMBER_YOUR_PERSONA, {
74304
+ context: templateContext,
74305
+ assets: ctx.assets,
74306
+ logger: ctx.logger
74307
+ });
74308
+ return reminder?.additionalContext ?? void 0;
74309
+ }
74128
74310
  function registerStagePersonaReminders(context) {
74129
74311
  if (!(0, types_1.isDaemonContext)(context))
74130
74312
  return;
@@ -74153,6 +74335,7 @@ var require_stage_user_profile_reminders = __commonJS({
74153
74335
  "use strict";
74154
74336
  Object.defineProperty(exports2, "__esModule", { value: true });
74155
74337
  exports2.stageUserProfileRemindersForSession = stageUserProfileRemindersForSession;
74338
+ exports2.resolveUserProfileContextForSnapshot = resolveUserProfileContextForSnapshot;
74156
74339
  exports2.registerStageUserProfileReminders = registerStageUserProfileReminders;
74157
74340
  var core_1 = require_dist4();
74158
74341
  var events_js_1 = require_events2();
@@ -74192,6 +74375,21 @@ var require_stage_user_profile_reminders = __commonJS({
74192
74375
  ctx.logger.warn("Failed to resolve user-profile reminder", { sessionId });
74193
74376
  }
74194
74377
  }
74378
+ function resolveUserProfileContextForSnapshot(ctx) {
74379
+ const profile = (0, core_1.loadUserProfile)({ logger: ctx.logger });
74380
+ if (!profile)
74381
+ return void 0;
74382
+ const templateContext = {
74383
+ user_name: profile.name,
74384
+ user_role: profile.role,
74385
+ user_interests: profile.interests.join(", ")
74386
+ };
74387
+ const reminder = (0, reminder_utils_js_1.resolveReminder)(types_js_1.ReminderIds.USER_PROFILE, {
74388
+ context: templateContext,
74389
+ assets: ctx.assets
74390
+ });
74391
+ return reminder?.additionalContext ?? void 0;
74392
+ }
74195
74393
  function registerStageUserProfileReminders(context) {
74196
74394
  if (!(0, types_1.isDaemonContext)(context))
74197
74395
  return;
@@ -74287,10 +74485,10 @@ var require_cli_staging_reader = __commonJS({
74287
74485
  stateDir;
74288
74486
  sessionId;
74289
74487
  constructor(options) {
74290
- if (!options.paths.projectConfigDir) {
74291
- throw new Error("CLIStagingReader requires project scope (projectConfigDir must be defined)");
74488
+ if (!options.paths.projectStateDir) {
74489
+ throw new Error("CLIStagingReader requires project scope (projectStateDir must be defined)");
74292
74490
  }
74293
- this.stateDir = options.paths.projectConfigDir;
74491
+ this.stateDir = options.paths.projectStateDir;
74294
74492
  this.sessionId = options.sessionId;
74295
74493
  }
74296
74494
  /**
@@ -74654,6 +74852,22 @@ var require_inject_session_start = __commonJS({
74654
74852
  }
74655
74853
  });
74656
74854
 
74855
+ // ../feature-reminders/dist/handlers/consumption/inject-post-compact.js
74856
+ var require_inject_post_compact = __commonJS({
74857
+ "../feature-reminders/dist/handlers/consumption/inject-post-compact.js"(exports2) {
74858
+ "use strict";
74859
+ Object.defineProperty(exports2, "__esModule", { value: true });
74860
+ exports2.registerInjectPostCompact = registerInjectPostCompact;
74861
+ var consumption_handler_factory_js_1 = require_consumption_handler_factory();
74862
+ function registerInjectPostCompact(context) {
74863
+ (0, consumption_handler_factory_js_1.createConsumptionHandler)(context, {
74864
+ id: "reminders:inject-post-compact",
74865
+ hook: "PostCompact"
74866
+ });
74867
+ }
74868
+ }
74869
+ });
74870
+
74657
74871
  // ../feature-reminders/dist/handlers/consumption/index.js
74658
74872
  var require_consumption = __commonJS({
74659
74873
  "../feature-reminders/dist/handlers/consumption/index.js"(exports2) {
@@ -74665,12 +74879,14 @@ var require_consumption = __commonJS({
74665
74879
  var inject_post_tool_use_1 = require_inject_post_tool_use();
74666
74880
  var inject_stop_1 = require_inject_stop();
74667
74881
  var inject_session_start_1 = require_inject_session_start();
74882
+ var inject_post_compact_js_1 = require_inject_post_compact();
74668
74883
  function registerConsumptionHandlers(context) {
74669
74884
  (0, inject_user_prompt_submit_1.registerInjectUserPromptSubmit)(context);
74670
74885
  (0, inject_pre_tool_use_1.registerInjectPreToolUse)(context);
74671
74886
  (0, inject_post_tool_use_1.registerInjectPostToolUse)(context);
74672
74887
  (0, inject_stop_1.registerInjectStop)(context);
74673
74888
  (0, inject_session_start_1.registerInjectSessionStart)(context);
74889
+ (0, inject_post_compact_js_1.registerInjectPostCompact)(context);
74674
74890
  }
74675
74891
  }
74676
74892
  });
@@ -75115,7 +75331,7 @@ var require_dist5 = __commonJS({
75115
75331
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
75116
75332
  };
75117
75333
  Object.defineProperty(exports2, "__esModule", { value: true });
75118
- exports2.createRemindersState = exports2.ReminderOrchestrator = exports2.ReminderEvents = exports2.handleVCUnverifiedClear = exports2.handleVCUnverifiedSet = exports2.handleReminderConsumed = exports2.classifyCompletion = exports2.restagePersonaRemindersForActiveSessions = exports2.stagePersonaRemindersForSession = exports2.registerStagingHandlers = exports2.registerConsumptionHandlers = exports2.manifest = void 0;
75334
+ exports2.createRemindersState = exports2.ReminderOrchestrator = exports2.ReminderEvents = exports2.handleVCUnverifiedClear = exports2.handleVCUnverifiedSet = exports2.handleReminderConsumed = exports2.classifyCompletion = exports2.resolveUserProfileContextForSnapshot = exports2.resolvePersonaContextForSnapshot = exports2.restagePersonaRemindersForActiveSessions = exports2.stagePersonaRemindersForSession = exports2.registerStagingHandlers = exports2.registerConsumptionHandlers = exports2.manifest = void 0;
75119
75335
  exports2.register = register;
75120
75336
  var staging_1 = require_staging2();
75121
75337
  var consumption_1 = require_consumption();
@@ -75149,6 +75365,13 @@ var require_dist5 = __commonJS({
75149
75365
  Object.defineProperty(exports2, "restagePersonaRemindersForActiveSessions", { enumerable: true, get: function() {
75150
75366
  return stage_persona_reminders_1.restagePersonaRemindersForActiveSessions;
75151
75367
  } });
75368
+ Object.defineProperty(exports2, "resolvePersonaContextForSnapshot", { enumerable: true, get: function() {
75369
+ return stage_persona_reminders_1.resolvePersonaContextForSnapshot;
75370
+ } });
75371
+ var stage_user_profile_reminders_1 = require_stage_user_profile_reminders();
75372
+ Object.defineProperty(exports2, "resolveUserProfileContextForSnapshot", { enumerable: true, get: function() {
75373
+ return stage_user_profile_reminders_1.resolveUserProfileContextForSnapshot;
75374
+ } });
75152
75375
  var completion_classifier_1 = require_completion_classifier();
75153
75376
  Object.defineProperty(exports2, "classifyCompletion", { enumerable: true, get: function() {
75154
75377
  return completion_classifier_1.classifyCompletion;
@@ -77502,6 +77725,7 @@ var require_cleanup_handler = __commonJS({
77502
77725
  var promises_1 = __importDefault(require("fs/promises"));
77503
77726
  var path_1 = __importDefault(require("path"));
77504
77727
  var DEFAULT_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
77728
+ var DEFAULT_LOG_MAX_AGE_MS = 30 * 24 * 60 * 60 * 1e3;
77505
77729
  function createCleanupHandler(deps) {
77506
77730
  return async (payload, ctx) => {
77507
77731
  const result = core_1.CleanupPayloadSchema.safeParse(payload);
@@ -77522,6 +77746,55 @@ var require_cleanup_handler = __commonJS({
77522
77746
  const now = Date.now();
77523
77747
  let cleaned = 0;
77524
77748
  let skipped = 0;
77749
+ const pruneLogs = async () => {
77750
+ const logMaxAgeMs = p.logMaxAgeMs ?? DEFAULT_LOG_MAX_AGE_MS;
77751
+ const logsDir = deps.stateService.logsDir();
77752
+ let logsCleaned = 0;
77753
+ let logsSkipped = 0;
77754
+ try {
77755
+ const logEntries = await promises_1.default.readdir(logsDir, { withFileTypes: true });
77756
+ for (const entry of logEntries) {
77757
+ if (ctx.signal.aborted) {
77758
+ ctx.logger.info("Log cleanup cancelled mid-execution");
77759
+ return;
77760
+ }
77761
+ if (!entry.isFile())
77762
+ continue;
77763
+ const filePath = path_1.default.join(logsDir, entry.name);
77764
+ try {
77765
+ const stats = await promises_1.default.stat(filePath);
77766
+ const age = now - stats.mtimeMs;
77767
+ if (age > logMaxAgeMs) {
77768
+ if (dryRun) {
77769
+ ctx.logger.debug("Would clean log file (dry-run)", { file: entry.name, ageMs: age });
77770
+ } else {
77771
+ await promises_1.default.rm(filePath, { force: true });
77772
+ ctx.logger.debug("Cleaned log file", { file: entry.name, ageMs: age });
77773
+ }
77774
+ logsCleaned++;
77775
+ } else {
77776
+ logsSkipped++;
77777
+ }
77778
+ } catch (statErr) {
77779
+ ctx.logger.warn("Failed to stat log file", {
77780
+ file: entry.name,
77781
+ error: (0, core_1.toErrorMessage)(statErr)
77782
+ });
77783
+ }
77784
+ }
77785
+ if (logsCleaned === 0 && logsSkipped === 0) {
77786
+ ctx.logger.debug("No log files eligible for cleanup");
77787
+ }
77788
+ ctx.logger.info("Log cleanup completed", { cleaned: logsCleaned, skipped: logsSkipped, dryRun });
77789
+ (0, core_1.logEvent)(ctx.logger, core_1.LogEvents.cleanupLogCompleted({ cleaned: logsCleaned, skipped: logsSkipped, dryRun }));
77790
+ } catch (logErr) {
77791
+ if (logErr.code === "ENOENT") {
77792
+ ctx.logger.debug("Logs directory does not exist, nothing to clean");
77793
+ } else {
77794
+ ctx.logger.warn("Log cleanup phase failed", { error: (0, core_1.toErrorMessage)(logErr) });
77795
+ }
77796
+ }
77797
+ };
77525
77798
  try {
77526
77799
  const entries = await promises_1.default.readdir(sessionsDir, { withFileTypes: true });
77527
77800
  for (const entry of entries) {
@@ -77560,11 +77833,22 @@ var require_cleanup_handler = __commonJS({
77560
77833
  ctx.logger.info("Cleanup task cancelled mid-execution, not updating lastCleanup");
77561
77834
  return;
77562
77835
  }
77836
+ await pruneLogs();
77837
+ if (ctx.signal.aborted) {
77838
+ ctx.logger.info("Cleanup task cancelled mid-execution, not updating lastCleanup");
77839
+ return;
77840
+ }
77563
77841
  await deps.taskRegistry.updateLastCleanup();
77564
77842
  ctx.logger.info("Cleanup task completed", { cleaned, skipped, dryRun });
77565
77843
  } catch (err) {
77566
77844
  if (err.code === "ENOENT") {
77567
77845
  ctx.logger.debug("Sessions directory does not exist, nothing to clean");
77846
+ await pruneLogs();
77847
+ if (ctx.signal.aborted) {
77848
+ ctx.logger.info("Cleanup task cancelled mid-execution, not updating lastCleanup");
77849
+ return;
77850
+ }
77851
+ await deps.taskRegistry.updateLastCleanup();
77568
77852
  return;
77569
77853
  }
77570
77854
  ctx.logger.error("Cleanup task failed", {
@@ -78006,9 +78290,10 @@ var require_daemon_helpers = __commonJS({
78006
78290
  exports2.REGISTRY_HEARTBEAT_INTERVAL_MS = exports2.EVICTION_INTERVAL_MS = exports2.HEARTBEAT_INTERVAL_MS = exports2.IDLE_CHECK_INTERVAL_MS = exports2.VERSION = void 0;
78007
78291
  exports2.diffConfigs = diffConfigs;
78008
78292
  exports2.resolveTranscriptPath = resolveTranscriptPath;
78293
+ exports2.shouldRunStartupCleanup = shouldRunStartupCleanup;
78009
78294
  exports2.getPersonaInjectionEnabled = getPersonaInjectionEnabled;
78010
78295
  var core_1 = require_dist4();
78011
- exports2.VERSION = require_package3().version;
78296
+ exports2.VERSION = (0, core_1.computeBuildIdentity)();
78012
78297
  exports2.IDLE_CHECK_INTERVAL_MS = 30 * 1e3;
78013
78298
  exports2.HEARTBEAT_INTERVAL_MS = 5 * 1e3;
78014
78299
  exports2.EVICTION_INTERVAL_MS = 5 * 60 * 1e3;
@@ -78040,6 +78325,11 @@ var require_daemon_helpers = __commonJS({
78040
78325
  }
78041
78326
  return transcriptPath;
78042
78327
  }
78328
+ function shouldRunStartupCleanup(lastCleanupAt, intervalMs, now) {
78329
+ if (lastCleanupAt === void 0)
78330
+ return true;
78331
+ return now - lastCleanupAt >= intervalMs;
78332
+ }
78043
78333
  function getPersonaInjectionEnabled(config) {
78044
78334
  return config.getFeature("session-summary").settings?.personas?.injectPersonaIntoClaude ?? true;
78045
78335
  }
@@ -78135,6 +78425,7 @@ var require_daemon_timer_manager = __commonJS({
78135
78425
  heartbeatInterval = null;
78136
78426
  evictionTimer = null;
78137
78427
  registryHeartbeatInterval = null;
78428
+ cleanupTimer = null;
78138
78429
  constructor(deps) {
78139
78430
  this.deps = deps;
78140
78431
  this.startTime = deps.startTime;
@@ -78186,6 +78477,19 @@ var require_daemon_timer_manager = __commonJS({
78186
78477
  stopEvictionTimer() {
78187
78478
  this.clearTimer("evictionTimer");
78188
78479
  }
78480
+ /** Start periodic cleanup timer (runs every config.core.daemon.cleanup.intervalHours hours). */
78481
+ startCleanupTimer() {
78482
+ const intervalMs = this.deps.configService.core.daemon.cleanup.intervalHours * 60 * 60 * 1e3;
78483
+ this.cleanupTimer = setInterval(() => {
78484
+ void this.deps.onCleanup();
78485
+ }, intervalMs);
78486
+ this.cleanupTimer.unref();
78487
+ this.deps.logger.info("Cleanup timer started", { intervalMs });
78488
+ (0, core_1.logEvent)(this.deps.logger, core_1.LogEvents.cleanupTimerStarted({ intervalMs }));
78489
+ }
78490
+ stopCleanupTimer() {
78491
+ this.clearTimer("cleanupTimer");
78492
+ }
78189
78493
  /** Register this project in the user-level registry for UI discovery. */
78190
78494
  async registerProject() {
78191
78495
  try {
@@ -78211,6 +78515,7 @@ var require_daemon_timer_manager = __commonJS({
78211
78515
  this.startIdleCheck();
78212
78516
  this.startHeartbeat();
78213
78517
  this.startEvictionTimer();
78518
+ this.startCleanupTimer();
78214
78519
  await this.registerProject();
78215
78520
  this.startRegistryHeartbeat();
78216
78521
  }
@@ -78218,6 +78523,7 @@ var require_daemon_timer_manager = __commonJS({
78218
78523
  this.stopIdleCheck();
78219
78524
  this.stopHeartbeat();
78220
78525
  this.stopEvictionTimer();
78526
+ this.stopCleanupTimer();
78221
78527
  this.stopRegistryHeartbeat();
78222
78528
  }
78223
78529
  clearTimer(field) {
@@ -78619,6 +78925,8 @@ var require_daemon = __commonJS({
78619
78925
  registryService;
78620
78926
  timerManager;
78621
78927
  sessionLogWriter;
78928
+ /** In-memory compaction snapshots keyed by sessionId. Populated by PreCompact, consumed by PostCompact. */
78929
+ compactionSnapshots = /* @__PURE__ */ new Map();
78622
78930
  /** Cache persona for handoff on clear. */
78623
78931
  cachePersonaForClear(personaId) {
78624
78932
  this.lastClearedPersona = { personaId, timestamp: Date.now() };
@@ -78642,6 +78950,14 @@ var require_daemon = __commonJS({
78642
78950
  this.logger.debug("Consumed persona from clear handoff", { personaId, age });
78643
78951
  return personaId;
78644
78952
  }
78953
+ buildRuntimePaths() {
78954
+ return {
78955
+ projectDir: this.projectDir,
78956
+ userConfigDir: this.userStateService.rootDir(),
78957
+ projectConfigDir: path_1.default.join(this.projectDir, ".sidekick"),
78958
+ projectStateDir: this.stateService.rootDir()
78959
+ };
78960
+ }
78645
78961
  constructor(projectDir2) {
78646
78962
  const startTime = Date.now();
78647
78963
  this.projectDir = projectDir2;
@@ -78653,7 +78969,7 @@ var require_daemon = __commonJS({
78653
78969
  projectRoot: projectDir2,
78654
78970
  assets: this.assetResolver
78655
78971
  });
78656
- const logDir = path_1.default.join(projectDir2, ".sidekick", "logs");
78972
+ const logDir = path_1.default.join((0, core_1.projectStateDir)(projectDir2), "logs");
78657
78973
  this.logManager = (0, core_1.createLogManager)({
78658
78974
  name: "sidekickd",
78659
78975
  level: this.configService.core.logging.level,
@@ -78675,7 +78991,7 @@ var require_daemon = __commonJS({
78675
78991
  getStartTime: () => this.timerManager.startTime
78676
78992
  });
78677
78993
  this.logger = this.logMetrics.createCountingLogger();
78678
- const sessionsDir = path_1.default.join(projectDir2, ".sidekick", "sessions");
78994
+ const sessionsDir = path_1.default.join((0, core_1.projectStateDir)(projectDir2), "sessions");
78679
78995
  this.sessionLogWriter = new core_1.SessionLogWriter({
78680
78996
  sessionsDir,
78681
78997
  maxHandles: 10,
@@ -78695,7 +79011,7 @@ var require_daemon = __commonJS({
78695
79011
  config: () => this.configService.getAll()
78696
79012
  });
78697
79013
  const registryRoot = path_1.default.join((0, os_1.homedir)(), ".sidekick", "projects");
78698
- this.registryService = new core_1.ProjectRegistryService(registryRoot);
79014
+ this.registryService = new core_1.ProjectRegistryService(registryRoot, this.logger);
78699
79015
  this.stagingStateService = new core_1.StateService(projectDir2, {
78700
79016
  cache: false,
78701
79017
  logger: this.logger,
@@ -78704,7 +79020,7 @@ var require_daemon = __commonJS({
78704
79020
  this.taskEngine = new task_engine_js_1.TaskEngine(this.logger, this.getContextForTask.bind(this));
78705
79021
  this.taskRegistry = new task_registry_js_1.TaskRegistry(this.stateService, this.logger);
78706
79022
  this.configWatcher = new config_watcher_js_1.ConfigWatcher({
78707
- projectDir: this.stateService.rootDir(),
79023
+ projectDir: path_1.default.join(this.projectDir, ".sidekick"),
78708
79024
  devAssetsDir: this.configService.core.development.enabled ? (0, core_1.getDefaultAssetsDir)() : void 0
78709
79025
  }, this.logger, this.handleConfigChange.bind(this));
78710
79026
  this.personaWatcher = new session_persona_watcher_js_1.SessionPersonaWatcher({ sidekickDir: this.stateService.rootDir() }, this.logger, this.handlePersonaChange.bind(this));
@@ -78730,7 +79046,8 @@ var require_daemon = __commonJS({
78730
79046
  projectDir: this.projectDir,
78731
79047
  startTime,
78732
79048
  onIdle: () => this.stop(),
78733
- onHeartbeat: () => this.logMetrics.writeHeartbeat()
79049
+ onHeartbeat: () => this.logMetrics.writeHeartbeat(),
79050
+ onCleanup: () => this.enqueueCleanup("timer")
78734
79051
  });
78735
79052
  this.contextMetricsService = (0, index_js_1.createContextMetricsService)({
78736
79053
  projectDir: projectDir2,
@@ -78771,12 +79088,25 @@ var require_daemon = __commonJS({
78771
79088
  (0, core_1.logEvent)(this.logger, core_1.LogEvents.ipcServerStarted({ socketPath: (0, core_1.getSocketPath)(this.projectDir) }));
78772
79089
  this.configWatcher.start();
78773
79090
  const watchedFiles = [
78774
- this.stateService.rootDir(),
79091
+ path_1.default.join(this.projectDir, ".sidekick"),
78775
79092
  path_1.default.join((0, os_1.homedir)(), ".sidekick"),
78776
79093
  ...this.configService.core.development.enabled ? [(0, core_1.getDefaultAssetsDir)()] : []
78777
79094
  ];
78778
79095
  (0, core_1.logEvent)(this.logger, core_1.LogEvents.configWatcherStarted({ projectDir: this.projectDir, watchedFiles }));
78779
79096
  this.personaWatcher.start();
79097
+ const cleanupCfg = this.configService.core.daemon.cleanup;
79098
+ const cleanupIntervalMs = cleanupCfg.intervalHours * 60 * 60 * 1e3;
79099
+ const registryState = await this.taskRegistry.getState();
79100
+ const lastCleanupAt = registryState.lastCleanupAt;
79101
+ if ((0, daemon_helpers_js_1.shouldRunStartupCleanup)(lastCleanupAt, cleanupIntervalMs, Date.now())) {
79102
+ await this.enqueueCleanup("startup");
79103
+ } else if (lastCleanupAt !== void 0) {
79104
+ this.logger.info("Cleanup skipped \u2014 ran recently", {
79105
+ lastCleanupAt,
79106
+ intervalMs: cleanupIntervalMs
79107
+ });
79108
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.cleanupSkippedRecent({ lastCleanupAt, intervalMs: cleanupIntervalMs }));
79109
+ }
78780
79110
  await this.timerManager.startAll();
78781
79111
  this.logger.info("Daemon started successfully");
78782
79112
  (0, core_1.logEvent)(this.logger, core_1.LogEvents.daemonStarted({ startupDurationMs: Date.now() - this.timerManager.startTime }));
@@ -79025,13 +79355,120 @@ var require_daemon = __commonJS({
79025
79355
  if (hook === "UserPromptSubmit") {
79026
79356
  await this.handleUserPromptSubmitCleanup(event, { logger: requestLogger });
79027
79357
  }
79358
+ if (hook === "PreCompact") {
79359
+ await this.handlePreCompact(event, { logger: requestLogger });
79360
+ }
79361
+ let postCompactResponse;
79362
+ if (hook === "PostCompact") {
79363
+ postCompactResponse = await this.handlePostCompact(event, { logger: requestLogger });
79364
+ }
79028
79365
  if (sessionId && !this.logMetrics.hasSession(sessionId)) {
79029
79366
  await this.logMetrics.initSessionCounters(sessionId, false);
79030
79367
  requestLogger.debug("Log counters initialized from file for hook", { hook });
79031
79368
  }
79032
79369
  const response = await this.handlerRegistry.invokeHook(hook, event, { logger: requestLogger });
79370
+ if (postCompactResponse?.additionalContext) {
79371
+ const registryContext = response.additionalContext;
79372
+ response.additionalContext = registryContext ? `${registryContext}
79373
+
79374
+ ${postCompactResponse.additionalContext}` : postCompactResponse.additionalContext;
79375
+ }
79033
79376
  return response;
79034
79377
  }
79378
+ /**
79379
+ * Handle PostCompact: signal compaction to the session's TranscriptService (UC1)
79380
+ * and return the pre-compact session context snapshot for re-injection (UC2).
79381
+ */
79382
+ async handlePostCompact(event, options) {
79383
+ const log = options?.logger ?? this.logger;
79384
+ const sessionId = event.context?.sessionId;
79385
+ if (!sessionId) {
79386
+ log.warn("PostCompact event missing sessionId");
79387
+ return {};
79388
+ }
79389
+ const transcriptService = this.serviceFactory.getTranscriptService(sessionId);
79390
+ if (transcriptService) {
79391
+ await transcriptService.signalCompaction();
79392
+ log.info("PostCompact: compaction signaled to transcript service", { sessionId });
79393
+ } else {
79394
+ log.warn("PostCompact: no transcript service for session", { sessionId });
79395
+ }
79396
+ const snapshot = this.compactionSnapshots.get(sessionId);
79397
+ if (snapshot) {
79398
+ this.compactionSnapshots.delete(sessionId);
79399
+ log.info("PostCompact: re-injecting compaction snapshot", {
79400
+ sessionId,
79401
+ snapshotLength: snapshot.length
79402
+ });
79403
+ return { additionalContext: snapshot };
79404
+ }
79405
+ return {};
79406
+ }
79407
+ /**
79408
+ * Handle PreCompact: capture staged SessionStart reminders as a compaction snapshot.
79409
+ * The snapshot is returned by the next PostCompact call for context re-injection.
79410
+ */
79411
+ async handlePreCompact(event, options) {
79412
+ const log = options?.logger ?? this.logger;
79413
+ const sessionId = event.context?.sessionId;
79414
+ if (!sessionId)
79415
+ return;
79416
+ try {
79417
+ const minimalCtx = {
79418
+ role: "daemon",
79419
+ config: {
79420
+ core: {
79421
+ logging: {
79422
+ level: this.configService.core.logging.level,
79423
+ components: this.configService.core.logging.components
79424
+ },
79425
+ development: { enabled: this.configService.core.development.enabled }
79426
+ },
79427
+ llm: {
79428
+ defaultProfile: this.configService.llm.defaultProfile,
79429
+ defaultFallbackProfileId: this.configService.llm.defaultFallbackProfileId,
79430
+ profiles: this.configService.llm.profiles,
79431
+ fallbackProfiles: this.configService.llm.fallbackProfiles
79432
+ },
79433
+ getAll: () => this.configService.getAll(),
79434
+ getFeature: (name) => this.configService.getFeature(name)
79435
+ },
79436
+ logger: log,
79437
+ assets: this.assetResolver,
79438
+ paths: this.buildRuntimePaths(),
79439
+ stateService: this.stateService,
79440
+ // Remaining DaemonContext fields are not needed by the resolve functions
79441
+ handlers: this.handlerRegistry,
79442
+ staging: this.serviceFactory.getStagingService(sessionId),
79443
+ transcript: void 0,
79444
+ llm: void 0,
79445
+ profileFactory: void 0,
79446
+ orchestrator: this.orchestrator,
79447
+ personaClearCache: { consume: () => this.consumeCachedPersona() }
79448
+ };
79449
+ const [personaContext, userProfileContext] = await Promise.all([
79450
+ (0, feature_reminders_1.resolvePersonaContextForSnapshot)(minimalCtx, sessionId),
79451
+ Promise.resolve((0, feature_reminders_1.resolveUserProfileContextForSnapshot)(minimalCtx))
79452
+ ]);
79453
+ const contextParts = [personaContext, userProfileContext].filter((c) => typeof c === "string" && c.trim().length > 0);
79454
+ if (contextParts.length === 0) {
79455
+ log.debug("PreCompact: no context to snapshot", { sessionId });
79456
+ return;
79457
+ }
79458
+ const snapshot = ["Context was compacted. Re-establishing session context:", "", ...contextParts].join("\n");
79459
+ this.compactionSnapshots.set(sessionId, snapshot);
79460
+ log.info("PreCompact: compaction snapshot captured", {
79461
+ sessionId,
79462
+ reminderCount: contextParts.length,
79463
+ snapshotLength: snapshot.length
79464
+ });
79465
+ } catch (err) {
79466
+ log.warn("PreCompact: failed to capture snapshot", {
79467
+ sessionId,
79468
+ error: (0, core_1.toErrorMessage)(err)
79469
+ });
79470
+ }
79471
+ }
79035
79472
  /**
79036
79473
  * Handle SessionStart-specific logic: clear staging on startup/clear.
79037
79474
  *
@@ -79115,6 +79552,7 @@ var require_daemon = __commonJS({
79115
79552
  }
79116
79553
  await this.llmManager.shutdownSessionProvider(sessionId, log);
79117
79554
  this.logMetrics.deleteSessionCounters(sessionId);
79555
+ this.compactionSnapshots.delete(sessionId);
79118
79556
  await this.serviceFactory.shutdownSession(sessionId);
79119
79557
  await this.sessionLogWriter.closeSession(sessionId);
79120
79558
  log.info("Session ended");
@@ -79226,11 +79664,7 @@ var require_daemon = __commonJS({
79226
79664
  this.logger.info("Completion classification requested", { sessionId });
79227
79665
  const resolvedTranscriptPath = transcriptPath ?? (0, core_1.reconstructTranscriptPath)(this.projectDir, sessionId);
79228
79666
  this.logger.debug("Resolved transcript path for classification", { transcriptPath: resolvedTranscriptPath });
79229
- const paths = {
79230
- projectDir: this.projectDir,
79231
- userConfigDir: this.userStateService.rootDir(),
79232
- projectConfigDir: this.stateService.rootDir()
79233
- };
79667
+ const paths = this.buildRuntimePaths();
79234
79668
  const sessionDir = this.stateService.sessionRootDir(sessionId);
79235
79669
  const instrumentedProfileFactory = this.llmManager.createInstrumentedProfileFactory(sessionId, sessionDir);
79236
79670
  const stagingService = this.serviceFactory.getStagingService(sessionId);
@@ -79298,11 +79732,7 @@ var require_daemon = __commonJS({
79298
79732
  * @param sessionId - Optional session ID for session-specific context
79299
79733
  */
79300
79734
  async getContextForTask(sessionId) {
79301
- const paths = {
79302
- projectDir: this.projectDir,
79303
- userConfigDir: this.userStateService.rootDir(),
79304
- projectConfigDir: this.stateService.rootDir()
79305
- };
79735
+ const paths = this.buildRuntimePaths();
79306
79736
  let llmProvider = this.llmManager.getBaseProvider();
79307
79737
  let profileFactory = this.llmManager.getProfileFactory();
79308
79738
  if (sessionId) {
@@ -79368,6 +79798,8 @@ var require_daemon = __commonJS({
79368
79798
  },
79369
79799
  onThreshold: () => () => {
79370
79800
  },
79801
+ signalCompaction: async () => {
79802
+ },
79371
79803
  capturePreCompactState: async () => {
79372
79804
  },
79373
79805
  getCompactionHistory: () => []
@@ -79422,11 +79854,7 @@ var require_daemon = __commonJS({
79422
79854
  if (!(this.handlerRegistry instanceof core_1.HandlerRegistryImpl)) {
79423
79855
  return;
79424
79856
  }
79425
- const paths = {
79426
- projectDir: this.projectDir,
79427
- userConfigDir: this.userStateService.rootDir(),
79428
- projectConfigDir: this.stateService.rootDir()
79429
- };
79857
+ const paths = this.buildRuntimePaths();
79430
79858
  const sessionDir = this.stateService.sessionRootDir(sessionId);
79431
79859
  const instrumentedProvider = await this.llmManager.getOrCreateInstrumentedProvider(sessionId, sessionDir, log);
79432
79860
  const instrumentedProfileFactory = this.llmManager.createInstrumentedProfileFactory(sessionId, sessionDir);
@@ -79495,6 +79923,19 @@ var require_daemon = __commonJS({
79495
79923
  log.debug("Cleared staged reminders on UserPromptSubmit", { hooks: hooksToClear });
79496
79924
  await this.orchestrator.onUserPromptSubmit(sessionId);
79497
79925
  }
79926
+ /** Build the cleanup payload from config and enqueue a cleanup task. */
79927
+ enqueueCleanup(reason) {
79928
+ const cleanup = this.configService.core.daemon.cleanup;
79929
+ const payload = {
79930
+ maxAgeMs: cleanup.sessionMaxAgeDays * 24 * 60 * 60 * 1e3,
79931
+ logMaxAgeMs: cleanup.logMaxAgeDays * 24 * 60 * 60 * 1e3,
79932
+ dryRun: false
79933
+ };
79934
+ this.taskEngine.enqueue(core_1.TaskTypes.CLEANUP, payload);
79935
+ this.logger.info("Cleanup task enqueued", { reason });
79936
+ (0, core_1.logEvent)(this.logger, core_1.LogEvents.cleanupTaskEnqueued({ reason }));
79937
+ return Promise.resolve();
79938
+ }
79498
79939
  // ── Delegation shims — existing tests type-cast to call these ─────────
79499
79940
  startEvictionTimer() {
79500
79941
  this.timerManager.startEvictionTimer();
@@ -79536,11 +79977,7 @@ var require_daemon = __commonJS({
79536
79977
  * @see docs/design/FEATURE-REMINDERS.md §3.1 Staging Handlers
79537
79978
  */
79538
79979
  registerStagingHandlers() {
79539
- const paths = {
79540
- projectDir: this.projectDir,
79541
- userConfigDir: this.userStateService.rootDir(),
79542
- projectConfigDir: this.stateService.rootDir()
79543
- };
79980
+ const paths = this.buildRuntimePaths();
79544
79981
  const registrationContext = {
79545
79982
  role: "daemon",
79546
79983
  config: {