@rubytech/create-maxy-code 0.1.373 → 0.1.374

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 (21) hide show
  1. package/dist/__tests__/cgroup-memory-controller-install-flow.test.js +41 -0
  2. package/dist/__tests__/cgroup-memory-controller.test.js +32 -0
  3. package/dist/cgroup-memory-controller.js +33 -0
  4. package/dist/index.js +54 -0
  5. package/package.json +1 -1
  6. package/payload/platform/scripts/rss-sampler.sh +18 -2
  7. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  8. package/payload/platform/services/claude-session-manager/dist/http-server.js +13 -6
  9. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  10. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts +19 -0
  11. package/payload/platform/services/claude-session-manager/dist/jsonl-path.d.ts.map +1 -1
  12. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js +42 -0
  13. package/payload/platform/services/claude-session-manager/dist/jsonl-path.js.map +1 -1
  14. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
  15. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  16. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +24 -0
  17. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  18. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts +75 -4
  19. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.d.ts.map +1 -1
  20. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js +138 -15
  21. package/payload/platform/services/claude-session-manager/dist/session-cap-audit.js.map +1 -1
@@ -0,0 +1,41 @@
1
+ // Static-grep contract for configureCgroupMemoryController()'s placement in
2
+ // the install flow — same precedent as watchdog-deferred-arming.test.ts for
3
+ // an installer side-effecting step that shells out with sudo and can't be
4
+ // safely executed in a unit test. The pure decision rule itself is covered
5
+ // by cgroup-memory-controller.test.ts.
6
+ import test from "node:test";
7
+ import assert from "node:assert/strict";
8
+ import { readFileSync } from "node:fs";
9
+ import { fileURLToPath } from "node:url";
10
+ import { dirname, resolve } from "node:path";
11
+ const here = dirname(fileURLToPath(import.meta.url));
12
+ const INDEX_TS = resolve(here, "../../src/index.ts");
13
+ const SRC = readFileSync(INDEX_TS, "utf-8");
14
+ function fnBody(name) {
15
+ const start = SRC.indexOf(`function ${name}`);
16
+ assert.ok(start >= 0, `could not locate ${name} in index.ts`);
17
+ const nextFn = SRC.indexOf("\nfunction ", start + 1);
18
+ assert.ok(nextFn > start, `could not bound ${name} body`);
19
+ return SRC.slice(start, nextFn);
20
+ }
21
+ test("configureCgroupMemoryController is called inside installService()'s Linux branch", () => {
22
+ const installServiceStart = SRC.indexOf("function installService(");
23
+ assert.ok(installServiceStart >= 0, "installService() not found");
24
+ const darwinReturn = SRC.indexOf("installServiceDarwin();\n return;", installServiceStart);
25
+ assert.ok(darwinReturn > installServiceStart, "expected the darwin early-return inside installService()");
26
+ const callSite = SRC.indexOf("configureCgroupMemoryController();", darwinReturn);
27
+ assert.ok(callSite > darwinReturn, "configureCgroupMemoryController() must be called after the darwin early-return (Linux-only)");
28
+ });
29
+ test("configureCgroupMemoryController never throws on a patch failure (non-fatal, matches the sysctl.d precedent)", () => {
30
+ const body = fnBody("configureCgroupMemoryController");
31
+ assert.ok(body.includes("try {"), "expected a try/catch around the sudo cp — a read-only /boot must not abort install");
32
+ assert.ok(body.includes("catch"), "expected a catch branch");
33
+ });
34
+ test("configureCgroupMemoryController always logs a resolved op=cgroup-memory-status line", () => {
35
+ const body = fnBody("configureCgroupMemoryController");
36
+ const occurrences = body.match(/op=cgroup-memory-status/g) ?? [];
37
+ assert.ok(occurrences.length >= 4, `expected at least 4 branches to log op=cgroup-memory-status, found ${occurrences.length}`);
38
+ });
39
+ test("configureCgroupMemoryController imports the pure decision rule from cgroup-memory-controller.js", () => {
40
+ assert.ok(SRC.includes('from "./cgroup-memory-controller.js"'), "index.ts must import memoryControllerMissing/cmdlineAlreadyPatched/patchCmdline rather than re-implementing the parsing");
41
+ });
@@ -0,0 +1,32 @@
1
+ // Pure decision rule for Task 1297: does this host's kernel have the memory
2
+ // cgroup controller enabled, and if not, does its boot cmdline already carry
3
+ // the fix. The wrapper in index.ts owns the fs reads and the sudo cp; this
4
+ // suite exercises only the pure functions — inputs in, decision out.
5
+ import test from "node:test";
6
+ import assert from "node:assert/strict";
7
+ import { CGROUP_MEMORY_CMDLINE_FLAGS, memoryControllerMissing, cmdlineAlreadyPatched, patchCmdline, } from "../cgroup-memory-controller.js";
8
+ test("memoryControllerMissing: false when memory is in the controller list", () => {
9
+ assert.equal(memoryControllerMissing("cpuset cpu io memory pids\n"), false);
10
+ });
11
+ test("memoryControllerMissing: true when memory is absent (the Pi default)", () => {
12
+ assert.equal(memoryControllerMissing("cpuset cpu io pids\n"), true);
13
+ });
14
+ test("memoryControllerMissing: true on an empty controller list", () => {
15
+ assert.equal(memoryControllerMissing(""), true);
16
+ });
17
+ test("cmdlineAlreadyPatched: true when the flags are present", () => {
18
+ const cmdline = "console=tty1 root=PARTUUID=abc rootwait cgroup_enable=memory cgroup_memory=1\n";
19
+ assert.equal(cmdlineAlreadyPatched(cmdline), true);
20
+ });
21
+ test("cmdlineAlreadyPatched: false on an unpatched line", () => {
22
+ const cmdline = "console=serial0,115200 console=tty1 root=PARTUUID=e9d78c4e-02 rootwait quiet splash\n";
23
+ assert.equal(cmdlineAlreadyPatched(cmdline), false);
24
+ });
25
+ test("patchCmdline: appends the flags with a single trailing newline", () => {
26
+ const cmdline = "console=tty1 root=PARTUUID=abc rootwait\n";
27
+ assert.equal(patchCmdline(cmdline), `console=tty1 root=PARTUUID=abc rootwait ${CGROUP_MEMORY_CMDLINE_FLAGS}\n`);
28
+ });
29
+ test("patchCmdline: works when the source has no trailing newline", () => {
30
+ const cmdline = "console=tty1 root=PARTUUID=abc rootwait";
31
+ assert.equal(patchCmdline(cmdline), `console=tty1 root=PARTUUID=abc rootwait ${CGROUP_MEMORY_CMDLINE_FLAGS}\n`);
32
+ });
@@ -0,0 +1,33 @@
1
+ // Pure logic for Task 1297: the Pi kernel ships with the memory cgroup
2
+ // controller disabled by default (/sys/fs/cgroup/cgroup.controllers has no
3
+ // `memory`), which makes every MemoryHigh=/MemoryMax= this platform
4
+ // configures (Task 1176, Task 1222) a silent no-op — confirmed fleet-wide
5
+ // across all 5 Pis via live inspection. The wrapper in index.ts owns the fs
6
+ // reads, locating the boot cmdline file, and the sudo cp. This module owns
7
+ // only the decision + the patched-content computation — inputs in, decision
8
+ // out. No reboot logic here or in the wrapper; a boot-param change only takes
9
+ // effect on the operator's next reboot.
10
+ export const CGROUP_MEMORY_CMDLINE_FLAGS = "cgroup_enable=memory cgroup_memory=1";
11
+ /** True when the cgroup-v2 root controller list (the raw content of
12
+ * /sys/fs/cgroup/cgroup.controllers) lacks `memory`. Inline rather than
13
+ * imported from platform/services/claude-session-manager because the
14
+ * installer is its own npm package and doesn't bundle platform services.
15
+ * Mirrors (negated) memoryControllerInList() in
16
+ * platform/services/claude-session-manager/src/rc-daemon.ts; future
17
+ * divergence between the two should be caught by the test suite. */
18
+ export function memoryControllerMissing(controllers) {
19
+ return !controllers.split(/\s+/).filter(Boolean).includes("memory");
20
+ }
21
+ /** True when the boot cmdline file already carries the enable flags —
22
+ * patching again would duplicate them. */
23
+ export function cmdlineAlreadyPatched(cmdline) {
24
+ return cmdline.includes("cgroup_enable=memory");
25
+ }
26
+ /** Append the enable flags to the single-line boot cmdline file. Callers
27
+ * must gate on cmdlineAlreadyPatched() first — this always appends, so
28
+ * calling it twice on the same content would duplicate the flags. Trims
29
+ * any trailing whitespace before appending, then restores exactly one
30
+ * trailing newline (cmdline.txt convention: one line, trailing newline). */
31
+ export function patchCmdline(cmdline) {
32
+ return `${cmdline.replace(/\s+$/, "")} ${CGROUP_MEMORY_CMDLINE_FLAGS}\n`;
33
+ }
package/dist/index.js CHANGED
@@ -19,6 +19,7 @@ import { installAllBrewPackages } from "./brew-install.js";
19
19
  import { parseSwVers, isSupportedMacosVersion } from "./macos-version.js";
20
20
  import { decideChromiumAction, isSnapConfinedPath } from "./snap-chromium.js";
21
21
  import { classifyPortHolder } from "./preflight-port-classifier.js";
22
+ import { memoryControllerMissing, cmdlineAlreadyPatched, patchCmdline } from "./cgroup-memory-controller.js";
22
23
  import { parsePluginList, computeInstallActions, parseExternalPlugins, findUnregisteredResyncs, } from "./lib/plugin-install.js";
23
24
  import { findPremiumMcpDirs } from "./lib/premium-mcp-discover.js";
24
25
  import { pickBindDecision, mergeSmbConf, formatSambaMarker, SAMBA_ENABLE_UNITS, } from "./samba-provision.js";
@@ -3243,6 +3244,56 @@ function installServiceDarwin() {
3243
3244
  console.log(` Server may still be starting. Check http://localhost:${PORT} in a moment.`);
3244
3245
  }
3245
3246
  }
3247
+ /** Task 1297 — the Pi kernel ships with the memory cgroup controller
3248
+ * disabled by default (/sys/fs/cgroup/cgroup.controllers has no `memory`),
3249
+ * which makes every MemoryHigh=/MemoryMax= this platform configures (Task
3250
+ * 1176, Task 1222) a silent no-op — confirmed fleet-wide, not theorized.
3251
+ * Patches the boot cmdline so the NEXT reboot activates it; never triggers
3252
+ * the reboot itself. Non-fatal: a read-only /boot partition, an absent
3253
+ * cmdline file (x86 hosts don't have one and don't need this), or a host
3254
+ * where the controller is already enabled (laptop, hart) all resolve to a
3255
+ * logged status line and a clean return — matching the sysctl.d QUIC
3256
+ * drop-in step's failure handling immediately below this function's call
3257
+ * site. */
3258
+ function configureCgroupMemoryController() {
3259
+ let controllers;
3260
+ try {
3261
+ controllers = readFileSync("/sys/fs/cgroup/cgroup.controllers", "utf-8");
3262
+ }
3263
+ catch {
3264
+ logFile("[install] op=cgroup-memory-status enabled=unknown patched=unknown reason=no-cgroup-v2-root");
3265
+ return;
3266
+ }
3267
+ if (!memoryControllerMissing(controllers)) {
3268
+ logFile("[install] op=cgroup-memory-status enabled=true patched=false");
3269
+ return;
3270
+ }
3271
+ const cmdlinePath = existsSync("/boot/firmware/cmdline.txt")
3272
+ ? "/boot/firmware/cmdline.txt"
3273
+ : existsSync("/boot/cmdline.txt")
3274
+ ? "/boot/cmdline.txt"
3275
+ : null;
3276
+ if (!cmdlinePath) {
3277
+ logFile("[install] op=cgroup-memory-status enabled=false patched=false reason=no-cmdline-file");
3278
+ return;
3279
+ }
3280
+ const cmdline = readFileSync(cmdlinePath, "utf-8");
3281
+ if (cmdlineAlreadyPatched(cmdline)) {
3282
+ logFile("[install] op=cgroup-memory-status enabled=false patched=true");
3283
+ return;
3284
+ }
3285
+ try {
3286
+ const tmpPath = "/tmp/cmdline.txt.maxy-cgroup-patch";
3287
+ writeFileSync(tmpPath, patchCmdline(cmdline));
3288
+ console.log(" [privileged] cp");
3289
+ shell("cp", [tmpPath, cmdlinePath], { sudo: true });
3290
+ spawnSync("rm", ["-f", tmpPath]);
3291
+ logFile("[install] op=cgroup-memory-status enabled=false patched=true");
3292
+ }
3293
+ catch {
3294
+ logFile("[install] op=cgroup-memory-status enabled=false patched=false reason=patch-failed");
3295
+ }
3296
+ }
3246
3297
  function installService() {
3247
3298
  log("11", TOTAL, `Starting ${BRAND.productName}...`);
3248
3299
  // Linux falls through to the systemd-user body below; darwin renders +
@@ -3265,6 +3316,9 @@ function installService() {
3265
3316
  spawnSync("sudo", ["sysctl", "--system"], { stdio: "ignore", timeout: 10_000 });
3266
3317
  }
3267
3318
  catch { /* non-critical — values applied on next reboot */ }
3319
+ // Task 1297 — activate the fleet-wide memory ceiling (Task 1176/1222) by
3320
+ // enabling the kernel controller it has silently depended on since it shipped.
3321
+ configureCgroupMemoryController();
3268
3322
  const serviceDir = resolve(process.env.HOME ?? "/root", ".config/systemd/user");
3269
3323
  mkdirSync(serviceDir, { recursive: true });
3270
3324
  // Create systemd user service
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.373",
3
+ "version": "0.1.374",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -56,14 +56,30 @@ while true; do
56
56
  # interface, not CLI prose); skip the line on any host where systemctl is
57
57
  # absent, the slice is unknown, or MemoryHigh is unset/infinity.
58
58
  if command -v systemctl >/dev/null 2>&1; then
59
- slice_props=$(systemctl --user show claude-ptys.slice -p MemoryCurrent -p MemoryHigh 2>/dev/null || true)
59
+ slice_props=$(systemctl --user show claude-ptys.slice -p MemoryCurrent -p MemoryHigh -p ControlGroup 2>/dev/null || true)
60
60
  slice_current=$(printf '%s\n' "$slice_props" | sed -n 's/^MemoryCurrent=//p')
61
61
  slice_high=$(printf '%s\n' "$slice_props" | sed -n 's/^MemoryHigh=//p')
62
+ slice_cgroup=$(printf '%s\n' "$slice_props" | sed -n 's/^ControlGroup=//p')
62
63
  if [[ "$slice_current" =~ ^[0-9]+$ && "$slice_high" =~ ^[0-9]+$ && "$slice_high" -gt 0 ]]; then
63
64
  slice_current_mb=$(( slice_current / 1048576 ))
64
65
  slice_high_mb=$(( slice_high / 1048576 ))
65
66
  slice_ratio=$(( slice_current * 100 / slice_high ))
66
- echo "[rss] op=slice-pressure slice=claude-ptys current=${slice_current_mb} high=${slice_high_mb} ratio=${slice_ratio}" >> "$LOG_PATH"
67
+ # Task 1297 memory.events `high` is the cgroup's own count of times
68
+ # this slice crossed MemoryHigh (a throttle event). The concern this
69
+ # answers: a MemoryHigh throttle could itself slow the brand server
70
+ # enough to miss its systemd watchdog heartbeat — this makes that
71
+ # correlation checkable from logs instead of guessed.
72
+ high_events=0
73
+ if [[ -n "$slice_cgroup" && -r "/sys/fs/cgroup${slice_cgroup}/memory.events" ]]; then
74
+ high_events=$(sed -n 's/^high //p' "/sys/fs/cgroup${slice_cgroup}/memory.events" 2>/dev/null || echo 0)
75
+ [[ "$high_events" =~ ^[0-9]+$ ]] || high_events=0
76
+ fi
77
+ echo "[rss] op=slice-pressure slice=claude-ptys current=${slice_current_mb} high=${slice_high_mb} ratio=${slice_ratio} highEvents=${high_events}" >> "$LOG_PATH"
78
+ # Task 1297 — the exact standing-sampler line the task names: real
79
+ # cgroup-accounted cohort total (not the claude.exe-only pgrep sum used
80
+ # by the plain per-cycle line above) against the real ceiling, so
81
+ # sizing can be tuned from evidence instead of guessed.
82
+ echo "[realagent-mem-census] rss_total=${slice_current} ceiling=${slice_high} sessions_live=${claude_count}" >> "$LOG_PATH"
67
83
  fi
68
84
  fi
69
85
 
@@ -1 +1 @@
1
- {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAe3B,OAAO,EAkBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AAyBzB,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAA;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAgFlE,eAAO,MAAM,kBAAkB,QAA2B,CAAA;AAS1D,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAIlD,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7E;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,SAAS,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,WAAW,CAAA;IAC/B,eAAe,EAAE,aAAa,CAAA;IAC9B;kFAC8E;IAC9E,cAAc,EAAE,cAAc,CAAA;IAC9B;;gFAE4E;IAC5E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;6BAGyB;IACzB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;;sCAGkC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C;;gFAE4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAsFD;;;;;;mEAMmE;AACnE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAiC9D;AAMD;;;;;;iBAMiB;AACjB,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAmCvF;AAID;;;;;;yBAMyB;AACzB,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAiCzD;AAkCD,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEpG;AAgBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAUD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMvE;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAER;AAkCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5F;AAwJD;;;kEAGkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAC3B,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD;;;+CAG2C;IAC3C,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD,CAAA;AA8CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAynFpD"}
1
+ {"version":3,"file":"http-server.d.ts","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAe3B,OAAO,EAkBL,KAAK,SAAS,EAEf,MAAM,kBAAkB,CAAA;AA4BzB,OAAO,KAAK,EAAE,SAAS,EAAyB,MAAM,iBAAiB,CAAA;AAEvE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,EAAqB,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAA;AAgFlE,eAAO,MAAM,kBAAkB,QAA2B,CAAA;AAS1D,eAAO,MAAM,4BAA4B,QAAS,CAAA;AAIlD,MAAM,WAAW,QAAS,SAAQ,IAAI,CAAC,SAAS,EAAE,gBAAgB,GAAG,SAAS,CAAC;IAC7E;;qEAEiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,SAAS,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,iBAAiB,EAAE,MAAM,CAAA;IACzB,kBAAkB,EAAE,WAAW,CAAA;IAC/B,eAAe,EAAE,aAAa,CAAA;IAC9B;kFAC8E;IAC9E,cAAc,EAAE,cAAc,CAAA;IAC9B;;gFAE4E;IAC5E,cAAc,EAAE,cAAc,CAAA;IAC9B;;;6BAGyB;IACzB,mBAAmB,CAAC,EAAE,mBAAmB,CAAA;IACzC;;;sCAGkC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAA;IAC3C;;gFAE4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAsFD;;;;;;mEAMmE;AACnE,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CAiC9D;AAMD;;;;;;iBAMiB;AACjB,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAmCvF;AAID;;;;;;yBAMyB;AACzB,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAiCzD;AAkCD,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CAEpG;AAgBD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE1D;AAUD,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAMvE;AAMD,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAER;AAkCD,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5F;AAwJD;;;kEAGkE;AAClE,MAAM,MAAM,OAAO,GAAG,IAAI,GAAG;IAC3B,2BAA2B,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACpD;;;+CAG2C;IAC3C,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CAChD,CAAA;AA8CD,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAgoFpD"}
@@ -44,7 +44,7 @@ import { createRcChildLog } from './rc-child-log.js';
44
44
  // --append-system-prompt for channel-bound admin sessions.
45
45
  import { extractVoiceSection } from './system-prompt.js';
46
46
  import { readJsonlSession } from './jsonl-enumerator.js';
47
- import { claudeStateRoot, projectSlugForCwd, jsonlPathForSessionId, sidecarPathForSessionId, permissionModeLogPathForSessionId, findExistingJsonlForSessionId, detectRemoteControlSlug, classifyBindBlocker, } from './jsonl-path.js';
47
+ import { claudeStateRoot, projectSlugForCwd, jsonlPathForSessionId, sidecarPathForSessionId, permissionModeLogPathForSessionId, findExistingJsonlForSessionId, slugForExistingJsonl, jsonlPathForSlug, sidecarPathForSlug, detectRemoteControlSlug, classifyBindBlocker, } from './jsonl-path.js';
48
48
  import { basename, dirname, join } from 'node:path';
49
49
  import { validateUserTitle } from './user-title-store.js';
50
50
  import { credentialsPathForConfigDir, snapshotAuthForFailureReason } from './auth-snapshot.js';
@@ -1669,17 +1669,24 @@ export function buildHttpApp(deps) {
1669
1669
  }
1670
1670
  // Task 260 — purge from BOTH locations: a row may have its JSONL
1671
1671
  // at the top level (live or just-ended) or under archive/ (archived).
1672
- const topJsonl = jsonlPathForSessionId(deps.spawnCwd, sessionId);
1673
- const archivedJsonl = jsonlPathForSessionId(deps.spawnCwd, sessionId, { archived: true });
1674
- const topSidecar = sidecarPathForSessionId(deps.spawnCwd, sessionId);
1675
- const archivedSidecar = sidecarPathForSessionId(deps.spawnCwd, sessionId, { archived: true });
1672
+ // Task 1298 resolve the slug that actually holds the JSONL first;
1673
+ // the list (sidebar-sessions.ts) walks every slug, so delete must too.
1674
+ // Fall back to spawnCwd's slug only when nothing is found anywhere —
1675
+ // hasTop/hasArchived will both be false regardless, so the fallback
1676
+ // only affects which (nonexistent) paths appear in the 404 case.
1677
+ const resolvedSlug = slugForExistingJsonl(deps.claudeConfigDir, sessionId) ?? projectSlugForCwd(deps.spawnCwd);
1678
+ const topJsonl = jsonlPathForSlug(deps.claudeConfigDir, resolvedSlug, sessionId);
1679
+ const archivedJsonl = jsonlPathForSlug(deps.claudeConfigDir, resolvedSlug, sessionId, { archived: true });
1680
+ const topSidecar = sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, sessionId);
1681
+ const archivedSidecar = sidecarPathForSlug(deps.claudeConfigDir, resolvedSlug, sessionId, { archived: true });
1676
1682
  const hasTop = existsSync(topJsonl);
1677
1683
  const hasArchived = existsSync(archivedJsonl);
1678
1684
  if (!row || (!hasTop && !hasArchived)) {
1685
+ deps.logger(`delete-not-found sessionId=${sessionId.slice(0, 8)} reason=not-found-anywhere`);
1679
1686
  timed(deps.logger, 'DELETE', `/${id}`, 404, Date.now() - start);
1680
1687
  return c.json({ error: 'session-not-found' }, 404);
1681
1688
  }
1682
- const subdir = join(claudeStateRoot(), 'projects', projectSlugForCwd(deps.spawnCwd), sessionId);
1689
+ const subdir = join(claudeStateRoot(), 'projects', resolvedSlug, sessionId);
1683
1690
  let bytes = 0;
1684
1691
  try {
1685
1692
  bytes = hasTop ? statSync(topJsonl).size : hasArchived ? statSync(archivedJsonl).size : 0;