claude-code-session-manager 0.92.1 → 0.94.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/assets/DataModel-B0LDnnSL.js +1 -0
  2. package/dist/assets/{History-BOR_fJNr.js → History-C685Kytt.js} +2 -2
  3. package/dist/assets/{Hooks-BtD436iL.js → Hooks-CwLnp0Z_.js} +3 -3
  4. package/dist/assets/HostBilko-CwEHEKYk.js +1 -0
  5. package/dist/assets/{Library-DeKdY-tD.js → Library-BU9np05E.js} +25 -25
  6. package/dist/assets/{MarkdownEditor-CbzC-Oqf.js → MarkdownEditor-B74ogK2f.js} +1 -1
  7. package/dist/assets/McpServers-BLM3ftzP.js +2 -0
  8. package/dist/assets/{Memory-B68AJjg2.js → Memory-DAP1t8bA.js} +6 -6
  9. package/dist/assets/{Permissions-Cr0RRvNy.js → Permissions-3pGLJmxQ.js} +3 -3
  10. package/dist/assets/Plugins-Q1KoLEzn.js +2 -0
  11. package/dist/assets/ProvenanceBadge-BnjBQiV9.js +1 -0
  12. package/dist/assets/{SaveBar-87ZJfscX.js → SaveBar-Zq2NfcPf.js} +1 -1
  13. package/dist/assets/Scheduler-D488ebKm.js +16 -0
  14. package/dist/assets/{ScopeSwitcher-CKzpqjJ_.js → ScopeSwitcher-Cnojv22D.js} +1 -1
  15. package/dist/assets/Settings-D5Lhj7ga.js +3 -0
  16. package/dist/assets/{SkillReferenceGraph-D_-wg4r9.js → SkillReferenceGraph-BRDiE6Hv.js} +2 -2
  17. package/dist/assets/Skills-BQDqp-EN.js +3 -0
  18. package/dist/assets/SystemPrompt-tFr19Od6.js +1 -0
  19. package/dist/assets/TagLibrary-BjyrnaRE.js +1 -0
  20. package/dist/assets/{TiptapBody-DYXIp8aG.js → TiptapBody-DGzSg3BO.js} +1 -1
  21. package/dist/assets/{Toggle-CDXR5l3m.js → Toggle-DzoROibf.js} +1 -1
  22. package/dist/assets/index-CCl4tz-u.js +3079 -0
  23. package/dist/assets/{index-D3P_jldk.css → index-CKH5Uxik.css} +1 -1
  24. package/dist/assets/settingsSchema-BYKVe1WM.js +1 -0
  25. package/dist/index.html +2 -2
  26. package/package.json +6 -3
  27. package/scripts/README.md +3 -0
  28. package/src/main/__tests__/agentEffortResolve.test.cjs +117 -0
  29. package/src/main/__tests__/agentLibrary.test.cjs +21 -0
  30. package/src/main/__tests__/agentModelResolve.test.cjs +16 -0
  31. package/src/main/__tests__/agentOverlayWrite.test.cjs +95 -0
  32. package/src/main/__tests__/chat-cancel-terminal.test.cjs +5 -2
  33. package/src/main/__tests__/chat-exit-close-race.test.cjs +8 -2
  34. package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +5 -2
  35. package/src/main/__tests__/chatRunner-session-flag-retry.test.cjs +44 -0
  36. package/src/main/__tests__/intradayRefresh.test.cjs +39 -0
  37. package/src/main/__tests__/openExternalApp-spawn-error.test.cjs +25 -0
  38. package/src/main/__tests__/opsErrorLog.test.cjs +22 -0
  39. package/src/main/__tests__/personaMerge.test.cjs +169 -0
  40. package/src/main/__tests__/prdCreatePlanId.test.cjs +132 -0
  41. package/src/main/__tests__/promptSessionsCreateEpicHandler.test.cjs +13 -0
  42. package/src/main/__tests__/pty-epic-worktree-spawn-cwd.test.cjs +21 -0
  43. package/src/main/__tests__/rateLimitPollerStreak.test.cjs +14 -0
  44. package/src/main/__tests__/runVerify-atomic-verdicts.test.cjs +26 -0
  45. package/src/main/__tests__/runVerify-landed-commit-outranks.test.cjs +11 -1
  46. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +11 -1
  47. package/src/main/__tests__/runVerify.test.cjs +12 -1
  48. package/src/main/__tests__/scheduler-prd-persona-spawn.test.cjs +47 -2
  49. package/src/main/__tests__/transcriptsUsageFor.test.cjs +112 -1
  50. package/src/main/agentLibrary.cjs +86 -40
  51. package/src/main/build-info.json +4 -4
  52. package/src/main/chatRunner.cjs +50 -10
  53. package/src/main/git.cjs +9 -2
  54. package/src/main/historyAggregator.cjs +2 -19
  55. package/src/main/index.cjs +17 -8
  56. package/src/main/ipcSchemas.cjs +25 -3
  57. package/src/main/lib/__tests__/childWithLog.test.cjs +180 -0
  58. package/src/main/lib/__tests__/delegationReadiness.test.cjs +19 -0
  59. package/src/main/lib/__tests__/effectiveModelInfo.test.cjs +10 -5
  60. package/src/main/lib/__tests__/gitCacheBound.test.cjs +69 -0
  61. package/src/main/lib/__tests__/modelCatalog.test.cjs +202 -0
  62. package/src/main/lib/agentEffortResolve.cjs +74 -0
  63. package/src/main/lib/agentModelResolve.cjs +52 -59
  64. package/src/main/lib/agentPersonaSchema.cjs +5 -0
  65. package/src/main/lib/childWithLog.cjs +92 -51
  66. package/src/main/lib/delegationReadiness.cjs +3 -1
  67. package/src/main/lib/effectiveModelInfo.cjs +38 -20
  68. package/src/main/lib/epicMint.cjs +5 -2
  69. package/src/main/lib/epicSpawnPlan.cjs +27 -8
  70. package/src/main/lib/epicTranscriptPath.cjs +5 -1
  71. package/src/main/lib/epicWorktreeMerge.cjs +7 -6
  72. package/src/main/lib/headTailBuffer.cjs +43 -0
  73. package/src/main/lib/intradayRefresh.cjs +33 -0
  74. package/src/main/lib/lruCache.cjs +39 -0
  75. package/src/main/lib/modelCatalog.cjs +243 -0
  76. package/src/main/lib/openExternalApp.cjs +27 -9
  77. package/src/main/lib/opsErrorLog.cjs +22 -0
  78. package/src/main/lib/personaMerge.cjs +166 -0
  79. package/src/main/lib/prdCreate.cjs +23 -5
  80. package/src/main/lib/prdDisposition.cjs +46 -0
  81. package/src/main/lib/prdFrontmatter.cjs +6 -2
  82. package/src/main/lib/promptSessionSchema.cjs +5 -0
  83. package/src/main/lib/promptSessionsCreateEpic.cjs +5 -3
  84. package/src/main/lib/rendererRecovery.cjs +141 -0
  85. package/src/main/lib/scheduleJobSchema.cjs +3 -0
  86. package/src/main/runVerify.cjs +3 -1
  87. package/src/main/scheduler/prdParser.cjs +2 -0
  88. package/src/main/scheduler.cjs +381 -262
  89. package/src/main/templates/PRD_AUTHORING.md +4 -0
  90. package/src/main/transcripts.cjs +54 -11
  91. package/src/preload/api.d.ts +56 -3
  92. package/src/preload/index.cjs +4 -0
  93. package/dist/assets/AgentLibrary-Ci6u03bq.js +0 -3
  94. package/dist/assets/DataModel-BIqcjsv2.js +0 -1
  95. package/dist/assets/HostBilko-CMe5cH3H.js +0 -1
  96. package/dist/assets/ListDetail-CtVjrHKB.js +0 -1
  97. package/dist/assets/McpServers-DKN75Shn.js +0 -2
  98. package/dist/assets/Panel-C2YTW-qP.js +0 -1
  99. package/dist/assets/Plugins-DbZkJs5l.js +0 -2
  100. package/dist/assets/ProvenanceBadge-kJ__BOQv.js +0 -1
  101. package/dist/assets/Scheduler-DdEaxku8.js +0 -16
  102. package/dist/assets/Settings-CjywVsNB.js +0 -3
  103. package/dist/assets/Skills-Cd_yPO_l.js +0 -3
  104. package/dist/assets/SystemPrompt-2z6eGPSF.js +0 -1
  105. package/dist/assets/TagLibrary-DcaWQSp4.js +0 -1
  106. package/dist/assets/index-D5H_H5wC.js +0 -3074
  107. package/dist/assets/settingsSchema-JRDfjgHb.js +0 -3
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /**
2
3
  * Canonical runtime schema for a PromptSession (Epic) record, mirroring the
3
4
  * TS `PromptSession` interface at src/renderer/state/promptSessions.ts:29-76
@@ -72,6 +73,9 @@ const PromptSessionSchema = z.object({
72
73
  openingPrompt: z.string().nullable().optional(),
73
74
  source: EpicSourceSchema.optional(),
74
75
  agentType: z.string().optional(),
76
+ // Per-Epic runtime overrides chosen on the New Session card; absent = the persona's own value.
77
+ model: z.string().optional(),
78
+ effort: z.string().optional(),
75
79
  // Absent on Epics minted before this field existed — those fall back to
76
80
  // rendering the flat `openingPrompt` as a single block (see
77
81
  // ChatTranscriptTurn.tsx's EpicIntakeCard).
@@ -93,6 +97,7 @@ const PromptSessionSchema = z.object({
93
97
  * doesn't match PromptSessionSchema. Callers that want fail-closed
94
98
  * enforcement before persisting a session object call this immediately
95
99
  * before their write.
100
+ * @param {unknown} session
96
101
  */
97
102
  function assertValidPromptSession(session) {
98
103
  const result = PromptSessionSchema.safeParse(session);
@@ -41,12 +41,14 @@ const { validatePath } = require('../config.cjs');
41
41
  * IPC (config:read-json/write-json, lib/activeIndexMerge.cjs's own merge) is
42
42
  * already required to pass through.
43
43
  */
44
- async function createEpicViaIpc(cwd, { goalText, tag, agentType, source, openingPrompt, sections } = {}) {
44
+ async function createEpicViaIpc(cwd, { goalText, tag, agentType, model, effort, source, openingPrompt, sections } = {}) {
45
45
  validatePath(cwd);
46
46
  const { epicId } = await ensureEpic(cwd, {
47
47
  goalText,
48
48
  tag,
49
49
  agentType,
50
+ model,
51
+ effort,
50
52
  source,
51
53
  openingPrompt,
52
54
  sections,
@@ -62,8 +64,8 @@ function registerPromptSessionsCreateEpicHandlers() {
62
64
  const { schemas: s, validated: v } = require('../ipcSchemas.cjs');
63
65
  ipcMain.handle(
64
66
  'promptSessions:create-epic',
65
- v(s.promptSessionsCreateEpic, ({ cwd, goalText, tag, agentType, source, openingPrompt, sections }) =>
66
- createEpicViaIpc(cwd, { goalText, tag, agentType, source, openingPrompt, sections })),
67
+ v(s.promptSessionsCreateEpic, ({ cwd, goalText, tag, agentType, model, effort, source, openingPrompt, sections }) =>
68
+ createEpicViaIpc(cwd, { goalText, tag, agentType, model, effort, source, openingPrompt, sections })),
67
69
  );
68
70
  }
69
71
 
@@ -0,0 +1,141 @@
1
+ 'use strict';
2
+
3
+ // Bounded automatic recovery for a dead / hung / failed-to-load renderer.
4
+ // Without it an OOM-killed renderer leaves a permanently white window while
5
+ // scheduler jobs keep running headless with no UI to pause them.
6
+ //
7
+ // `createReloadPolicy` is the pure cap/timing unit (injected clock);
8
+ // `attachRendererRecovery` wires it to one webContents (= one window).
9
+
10
+ const MAX_RELOADS = 3;
11
+ const WINDOW_MS = 10 * 60 * 1000;
12
+ const UNRESPONSIVE_GRACE_MS = 30 * 1000;
13
+ const LOAD_RETRY_DELAY_MS = 2000;
14
+ const ERR_ABORTED = -3;
15
+
16
+ /** Sliding-window cap: at most `max` reloads per `windowMs`. O(max) per call. */
17
+ function createReloadPolicy({ now = Date.now, max = MAX_RELOADS, windowMs = WINDOW_MS } = {}) {
18
+ let stamps = [];
19
+ return {
20
+ /** Record a reload if the cap allows it. Returns true when allowed. */
21
+ tryReload() {
22
+ const t = now();
23
+ stamps = stamps.filter((s) => t - s < windowMs);
24
+ if (stamps.length >= max) return false;
25
+ stamps.push(t);
26
+ return true;
27
+ },
28
+ /** Would tryReload() succeed right now? Pure — consumes no slot. */
29
+ canReload() {
30
+ const t = now();
31
+ return stamps.filter((s) => t - s < windowMs).length < max;
32
+ },
33
+ /** Should a render-process-gone reason trigger recovery? */
34
+ shouldRecoverFromGone(reason) {
35
+ return reason !== 'clean-exit';
36
+ },
37
+ /** Should a did-fail-load trigger a retry? Main frame only, not ERR_ABORTED. */
38
+ shouldRetryLoad(errorCode, isMainFrame) {
39
+ return isMainFrame !== false && errorCode !== ERR_ABORTED;
40
+ },
41
+ };
42
+ }
43
+
44
+ function attachRendererRecovery(wc, {
45
+ logs,
46
+ policy = createReloadPolicy(),
47
+ setTimer = setTimeout,
48
+ clearTimer = clearTimeout,
49
+ } = {}) {
50
+ let unresponsiveTimer = null;
51
+ let loadRetryTimer = null;
52
+ let capLogged = false;
53
+
54
+ const log = (level, message, meta) => {
55
+ try { logs?.writeLine({ scope: 'crash-diag', level, message, meta }); } catch { /* logging must never break recovery */ }
56
+ };
57
+ const alive = () => !wc.isDestroyed();
58
+
59
+ function logCapReached(reason) {
60
+ if (capLogged) return;
61
+ capLogged = true;
62
+ log('error', 'renderer auto-reload cap reached — giving up', { reason, max: MAX_RELOADS, windowMs: WINDOW_MS });
63
+ }
64
+
65
+ function reload(reason) {
66
+ if (!alive()) return;
67
+ if (!policy.tryReload()) {
68
+ logCapReached(reason);
69
+ return;
70
+ }
71
+ capLogged = false;
72
+ log('warn', 'renderer auto-reload', { reason });
73
+ try { wc.reload(); } catch (e) { log('error', 'renderer reload threw', { reason, error: e?.message }); }
74
+ }
75
+
76
+ // A hung main thread cannot commit a same-process reload, so kill the renderer
77
+ // and let the render-process-gone handler do the single cap-counted reload
78
+ // (in a new process). No slot is consumed here.
79
+ function recoverFromHang() {
80
+ if (!alive()) return;
81
+ if (!policy.canReload()) {
82
+ // A hung-but-visible window beats a dead white one.
83
+ logCapReached('unresponsive');
84
+ return;
85
+ }
86
+ try {
87
+ if (typeof wc.forcefullyCrashRenderer !== 'function') throw new Error('forcefullyCrashRenderer unavailable');
88
+ log('warn', 'renderer unresponsive — forcefully crashing for recovery');
89
+ wc.forcefullyCrashRenderer();
90
+ } catch (e) {
91
+ log('warn', 'forcefullyCrashRenderer failed — falling back to reload', { error: e?.message });
92
+ reload('unresponsive');
93
+ }
94
+ }
95
+
96
+ wc.on('render-process-gone', (_e, details) => {
97
+ const reason = details?.reason;
98
+ if (!policy.shouldRecoverFromGone(reason)) return;
99
+ reload(`render-process-gone:${reason}`);
100
+ });
101
+
102
+ wc.on('unresponsive', () => {
103
+ log('warn', 'renderer unresponsive');
104
+ if (unresponsiveTimer) return;
105
+ unresponsiveTimer = setTimer(() => {
106
+ unresponsiveTimer = null;
107
+ recoverFromHang();
108
+ }, UNRESPONSIVE_GRACE_MS);
109
+ });
110
+
111
+ wc.on('responsive', () => {
112
+ if (unresponsiveTimer) { clearTimer(unresponsiveTimer); unresponsiveTimer = null; }
113
+ log('info', 'renderer responsive again');
114
+ });
115
+
116
+ wc.on('did-fail-load', (_e, errorCode, errorDescription, _url, isMainFrame) => {
117
+ if (!policy.shouldRetryLoad(errorCode, isMainFrame)) return;
118
+ log('warn', 'main-frame did-fail-load', { errorCode, errorDescription });
119
+ if (loadRetryTimer) return;
120
+ loadRetryTimer = setTimer(() => {
121
+ loadRetryTimer = null;
122
+ reload(`did-fail-load:${errorCode}`);
123
+ }, LOAD_RETRY_DELAY_MS);
124
+ });
125
+
126
+ wc.once('destroyed', () => {
127
+ if (unresponsiveTimer) clearTimer(unresponsiveTimer);
128
+ if (loadRetryTimer) clearTimer(loadRetryTimer);
129
+ unresponsiveTimer = loadRetryTimer = null;
130
+ });
131
+ }
132
+
133
+ module.exports = {
134
+ createReloadPolicy,
135
+ attachRendererRecovery,
136
+ MAX_RELOADS,
137
+ WINDOW_MS,
138
+ UNRESPONSIVE_GRACE_MS,
139
+ LOAD_RETRY_DELAY_MS,
140
+ ERR_ABORTED,
141
+ };
@@ -1,3 +1,4 @@
1
+ // @ts-check
1
2
  /**
2
3
  * Canonical runtime schema for a ScheduleJob record, mirroring the TS
3
4
  * `ScheduleJob` interface (`ScheduleJob` in src/preload/api.d.ts) field-for-field.
@@ -24,6 +25,7 @@
24
25
 
25
26
  const { z } = require('zod');
26
27
 
28
+ /** @type {[string, ...string[]]} */
27
29
  const JOB_STATUSES = ['pending', 'running', 'investigating', 'completed', 'skipped', 'failed', 'needs_review', 'quarantined'];
28
30
 
29
31
  const ScheduleJobStatusSchema = z.enum(JOB_STATUSES);
@@ -138,6 +140,7 @@ const ScheduleJobSchema = z
138
140
  /**
139
141
  * Throws a clear, descriptive error (not a raw ZodError dump) when `job`
140
142
  * doesn't match ScheduleJobSchema. Mirrors assertValidPromptSession's shape.
143
+ * @param {unknown} job
141
144
  */
142
145
  function assertValidScheduleJob(job) {
143
146
  const result = ScheduleJobSchema.safeParse(job);
@@ -898,7 +898,9 @@ async function verifyRun({
898
898
  scannedAt: new Date().toISOString(),
899
899
  ...(extras ?? {}),
900
900
  };
901
- try { fs.writeFileSync(verdictsPath, JSON.stringify(record, null, 2)); } catch { /* best-effort */ }
901
+ // Atomic (tmp+rename) — scheduler.cjs reads this sidecar to decide completed-equivalence,
902
+ // so a torn write must never be observable. Lazy require keeps this module electron-free at load.
903
+ try { require('./config.cjs').writeJsonSync(verdictsPath, record); } catch { /* best-effort */ }
902
904
  return { verdict, reason, downgradeTo, ...(extras ?? {}) };
903
905
  }
904
906
 
@@ -126,6 +126,8 @@ async function parsePrdRaw(filePath) {
126
126
  // the decision (first-ever PRD in its Epic, or one with its own explicit
127
127
  // dependsOn).
128
128
  disposition: (fm.disposition === 'append' || fm.disposition === 'new-head') ? fm.disposition : null,
129
+ // Durable wave identity (createPrd-stamped); malformed values read as absent.
130
+ planId: (typeof fm.planId === 'string' && /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/.test(fm.planId)) ? fm.planId : null,
129
131
  // Opt-in exclusive-lease flag (PRD 1107, quietMachineLease.cjs +
130
132
  // schedulerBatch.cjs's pickNextBatch). Only a literal `true` opts in —
131
133
  // matches prdFrontmatter.cjs's applyKey semantics for this field.