claude-code-session-manager 0.67.0 → 0.69.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 (42) hide show
  1. package/dist/assets/{AgentLibrary-CiqimwWl.js → AgentLibrary-DngWZmn2.js} +1 -1
  2. package/dist/assets/{History-DbzjII2Z.js → History-CiqbZNCW.js} +1 -1
  3. package/dist/assets/{Hooks-C0PTplI_.js → Hooks-1LHEEKhK.js} +1 -1
  4. package/dist/assets/{HostBilko-vGc1FYLD.js → HostBilko-DruBVVx_.js} +1 -1
  5. package/dist/assets/{Library-4tU4rg2h.js → Library-BXt58QDN.js} +1 -1
  6. package/dist/assets/{ListDetail-BGIvkCwZ.js → ListDetail-V5JV-SVN.js} +1 -1
  7. package/dist/assets/{MarkdownEditor-ChFdpCam.js → MarkdownEditor-BYUdiq8p.js} +1 -1
  8. package/dist/assets/{McpServers-DsRWWpU3.js → McpServers-C3b18FY5.js} +1 -1
  9. package/dist/assets/{Memory-DmxoxxxY.js → Memory-MyToD3ri.js} +1 -1
  10. package/dist/assets/{Panel-KC-rv3jS.js → Panel-D1z0ZvX_.js} +1 -1
  11. package/dist/assets/{Permissions-DrDjCRFl.js → Permissions-BDRMk8Uu.js} +1 -1
  12. package/dist/assets/{Plugins-KexSQwk0.js → Plugins-Bd1wIgVW.js} +2 -2
  13. package/dist/assets/{ProvenanceBadge-Rg-jL94y.js → ProvenanceBadge-BIX0JW7m.js} +1 -1
  14. package/dist/assets/{SaveBar-BbEo8U2e.js → SaveBar-DOJnzj3n.js} +1 -1
  15. package/dist/assets/{Scheduler-C4Ti9TCc.js → Scheduler-LdawPR1e.js} +1 -1
  16. package/dist/assets/{ScopeSwitcher-Di9BmYze.js → ScopeSwitcher-Cl3g7YOM.js} +1 -1
  17. package/dist/assets/{Settings-CxyhPuYQ.js → Settings-7jHzkgfK.js} +1 -1
  18. package/dist/assets/{SkillReferenceGraph-0q26RHrn.js → SkillReferenceGraph-B_KqYC6R.js} +1 -1
  19. package/dist/assets/{Skills-BXkVyCo5.js → Skills-H8pCR_6D.js} +1 -1
  20. package/dist/assets/{SystemPrompt-Bwe3NTbi.js → SystemPrompt-fCD2rD2R.js} +1 -1
  21. package/dist/assets/{TagLibrary-CnwEf6EI.js → TagLibrary-erEiP4bj.js} +1 -1
  22. package/dist/assets/{TiptapBody-faCtLj9L.js → TiptapBody-A93HQtTH.js} +1 -1
  23. package/dist/assets/{Toggle-Bw7G_-RR.js → Toggle-CoFtvmoX.js} +1 -1
  24. package/dist/assets/{index-CEnMgeQU.css → index-B77KHr7n.css} +1 -1
  25. package/dist/assets/{index-B6dJ2CsU.js → index-C-iwyX_s.js} +356 -355
  26. package/dist/assets/{settingsSchema-B5C9hZoS.js → settingsSchema-DHDH8NJR.js} +1 -1
  27. package/dist/index.html +2 -2
  28. package/package.json +1 -1
  29. package/plugins/session-manager-dev/.claude-plugin/plugin.json +1 -1
  30. package/plugins/session-manager-dev/skills/send-feedback/SKILL.md +149 -0
  31. package/src/main/__tests__/crossProjectFeedback.test.cjs +327 -0
  32. package/src/main/__tests__/crossProjectFeedbackRoutes.test.cjs +154 -0
  33. package/src/main/__tests__/health-per-project-stall.test.cjs +4 -2
  34. package/src/main/__tests__/scheduler-job-overrun.test.cjs +117 -0
  35. package/src/main/__tests__/scheduler-stall-per-project.test.cjs +6 -2
  36. package/src/main/index.cjs +5 -0
  37. package/src/main/lib/buildTarget.cjs +15 -0
  38. package/src/main/lib/crossProjectFeedback.cjs +545 -0
  39. package/src/main/lib/epicMint.cjs +38 -7
  40. package/src/main/lib/promptSessionSchema.cjs +7 -1
  41. package/src/main/lib/schedulerConfig.cjs +15 -0
  42. package/src/main/scheduler.cjs +73 -1
@@ -76,10 +76,18 @@ const {
76
76
  MAX_JOB_DURATION_MS,
77
77
  BROADCAST_COALESCE_MS,
78
78
  QUARANTINE_ESCALATE_MS: QUARANTINE_ESCALATE_MS_DEFAULT,
79
+ JOB_OVERRUN_FACTOR: JOB_OVERRUN_FACTOR_DEFAULT,
80
+ JOB_OVERRUN_FLOOR_MS: JOB_OVERRUN_FLOOR_MS_DEFAULT,
79
81
  } = require('./lib/schedulerConfig.cjs');
80
82
  const QUARANTINE_ESCALATE_MS = process.env.SM_QUARANTINE_ESCALATE_HOURS
81
83
  ? Number(process.env.SM_QUARANTINE_ESCALATE_HOURS) * 60 * 60_000
82
84
  : QUARANTINE_ESCALATE_MS_DEFAULT;
85
+ const JOB_OVERRUN_FACTOR = process.env.SM_JOB_OVERRUN_FACTOR
86
+ ? Number(process.env.SM_JOB_OVERRUN_FACTOR)
87
+ : JOB_OVERRUN_FACTOR_DEFAULT;
88
+ const JOB_OVERRUN_FLOOR_MS = process.env.SM_JOB_OVERRUN_FLOOR_MINUTES
89
+ ? Number(process.env.SM_JOB_OVERRUN_FLOOR_MINUTES) * 60_000
90
+ : JOB_OVERRUN_FLOOR_MS_DEFAULT;
83
91
  const { pickForProject, pickNextBatch, DEFAULT_PROJECT_CWD } = require('./lib/schedulerBatch.cjs');
84
92
  const { runDefinitionOfDoneOnDrain } = require('./lib/dodDrainHook.cjs');
85
93
  const { writeRcaReport, extractRcaBlock } = require('./lib/rcaReport.cjs');
@@ -1043,6 +1051,52 @@ function findStaleQuarantinedJobs(jobs, now, thresholdMs) {
1043
1051
  return stale;
1044
1052
  }
1045
1053
 
1054
+ /**
1055
+ * findOverrunningJobs(jobs, now, { factor, floorMs }) → [{ slug, cwd, estimateMinutes, ranMs, ratio }]
1056
+ *
1057
+ * Pure, no IO. A RUNNING job whose elapsed time exceeds
1058
+ * `max(estimateMinutes * factor, floorMs)` is overrunning its own PRD's
1059
+ * declared estimate.
1060
+ *
1061
+ * This closes the gap between the two existing kill paths, which a
1062
+ * long-running-but-chatty job slips straight through:
1063
+ * - MAX_JOB_DURATION_MS (4h) is a deadman — a 20-minute PRD at 3h is 9x
1064
+ * over estimate and still an hour away from it.
1065
+ * - IDLE_OUTPUT_KILL_MS (20m) only fires when the log mtime STALLS; an
1066
+ * agent stuck in a productive-looking loop keeps writing and never trips it.
1067
+ * `estimateMinutes` was parsed, stored and displayed but never once compared
1068
+ * against actual runtime, so the only thing standing between a runaway job
1069
+ * and the 4h ceiling was a human noticing. (2026-08-08: a PRD ran 3h+ and was
1070
+ * caught only because the operator opened a second session to look.)
1071
+ *
1072
+ * Escalation only — see JOB_OVERRUN_FACTOR's note on why this must not kill.
1073
+ * A job with no usable estimate is skipped rather than guessed at.
1074
+ */
1075
+ function findOverrunningJobs(jobs, now, { factor, floorMs } = {}) {
1076
+ const f = typeof factor === 'number' && factor > 0 ? factor : JOB_OVERRUN_FACTOR;
1077
+ const floor = typeof floorMs === 'number' && floorMs >= 0 ? floorMs : JOB_OVERRUN_FLOOR_MS;
1078
+ const out = [];
1079
+ for (const j of jobs ?? []) {
1080
+ if (j.status !== 'running') continue;
1081
+ const est = Number(j.estimateMinutes);
1082
+ if (!Number.isFinite(est) || est <= 0) continue; // no estimate to overrun
1083
+ const startedAt = Date.parse(j.startedAt ?? '');
1084
+ if (Number.isNaN(startedAt)) continue;
1085
+ const ranMs = now - startedAt;
1086
+ if (ranMs <= 0) continue;
1087
+ const thresholdMs = Math.max(est * 60_000 * f, floor);
1088
+ if (ranMs < thresholdMs) continue;
1089
+ out.push({
1090
+ slug: j.slug,
1091
+ cwd: j.cwd ?? null,
1092
+ estimateMinutes: est,
1093
+ ranMs,
1094
+ ratio: ranMs / (est * 60_000),
1095
+ });
1096
+ }
1097
+ return out;
1098
+ }
1099
+
1046
1100
  // An empty queue and an unreadable queue are NOT the same thing, and
1047
1101
  // conflating them is destructive: reconcile() treats every PRD .md with no
1048
1102
  // matching jobs[] row as a brand-new goal and re-mints it as 'pending', so a
@@ -5042,6 +5096,24 @@ async function init() {
5042
5096
  );
5043
5097
  appendAuditEvent('prd_quarantine_stale', { slug: stale.slug, cwd: stale.cwd, ageMs: stale.ageMs });
5044
5098
  }
5099
+
5100
+ // Estimate-relative overrun escalation. Sits in the blind spot between
5101
+ // the 4h deadman and the 20-minute idle-output watchdog: a job that keeps
5102
+ // producing output while looping trips neither, so nothing noticed a PRD
5103
+ // running 9x its own estimate until a human went looking. Escalate loudly;
5104
+ // never kill on an estimate (see JOB_OVERRUN_FACTOR).
5105
+ for (const over of findOverrunningJobs(s.jobs, Date.now())) {
5106
+ console.warn(
5107
+ `[scheduler] JOB OVERRUNNING ESTIMATE: project=${over.cwd ?? '(unknown)'} slug=${over.slug} `
5108
+ + `ran=${Math.round(over.ranMs / 60_000)}m vs estimate=${over.estimateMinutes}m `
5109
+ + `(${over.ratio.toFixed(1)}x, threshold ${JOB_OVERRUN_FACTOR}x floor ${Math.round(JOB_OVERRUN_FLOOR_MS / 60_000)}m) — `
5110
+ + `still running; the ${Math.round(MAX_JOB_DURATION_MS / 3_600_000)}h deadman has NOT fired yet. `
5111
+ + `Check the run log, then let it finish or cancel it via scheduler_cancel_job`,
5112
+ );
5113
+ appendAuditEvent('job_overrunning_estimate', {
5114
+ slug: over.slug, cwd: over.cwd, estimateMinutes: over.estimateMinutes, ranMs: over.ranMs, ratio: over.ratio,
5115
+ });
5116
+ }
5045
5117
  }, 10 * 60_000);
5046
5118
 
5047
5119
  // Self-rescheduling poll loop with exponential backoff. Replaces the
@@ -5626,4 +5698,4 @@ function registerAdminRoutes(adminHttp, remoteObj = remote) {
5626
5698
  });
5627
5699
  }
5628
5700
 
5629
- module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, healRefusalReason, writeQueue, reconcile, reconcileSourcePromptId, allocateParallelGroup, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, isGitRepoSync, committedInWindow, computeCommittedDuringRun, classifySigtermWithCommit, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH, forceTickOutcome, applyPauseCleared, detectNetworkErrorInLog, detectRateLimitInLog, classifyFailureOutcome, commitGuardVerdict, TRANSIENT_RETRY_CAP, buildScheduleStatePayload, partitionBootOrphans, applyOrphanOutcome, BOOT_ORPHAN_KILL_GRACE_MS, registerAdminRoutes, notifyOriginatingTab, notifyNeedsReview, isNotifiableTerminalStatus, extractResultTextFromLog, candidatePrdsDirs, candidateArchivedPrdsDirs, resolveArchivedPrdStatus, prdDirForCwd, prdPathForJob, archivedPrdPathForJob, archivedTwinExists, findPrdDir, resolveVerifyPrdPath, resolveNotifyPrd, runPrdMigration, consolidateAllFlatPrds, shouldSkipInvestigationForCleanRun, archiveCompletedPrd, retireCompletedSlugs, SCHEDULER_BOOTED_AT, SCHEDULER_CODE_SHA, resetJobFields, executeJob, prdArchivedSkipResult, spawnJob, listPrdsInternal, computeStallSummary, findStaleQuarantinedJobs, QUARANTINE_ESCALATE_MS };
5701
+ module.exports = { findOverrunningJobs, JOB_OVERRUN_FACTOR, JOB_OVERRUN_FLOOR_MS, registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, healRefusalReason, writeQueue, reconcile, reconcileSourcePromptId, allocateParallelGroup, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, isEligibleForImmediateAutoFix, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, isGitRepoSync, committedInWindow, computeCommittedDuringRun, classifySigtermWithCommit, isFixPlanSlug, isFixPlanBeyondDepthCap, MAX_INVESTIGATION_DEPTH, forceTickOutcome, applyPauseCleared, detectNetworkErrorInLog, detectRateLimitInLog, classifyFailureOutcome, commitGuardVerdict, TRANSIENT_RETRY_CAP, buildScheduleStatePayload, partitionBootOrphans, applyOrphanOutcome, BOOT_ORPHAN_KILL_GRACE_MS, registerAdminRoutes, notifyOriginatingTab, notifyNeedsReview, isNotifiableTerminalStatus, extractResultTextFromLog, candidatePrdsDirs, candidateArchivedPrdsDirs, resolveArchivedPrdStatus, prdDirForCwd, prdPathForJob, archivedPrdPathForJob, archivedTwinExists, findPrdDir, resolveVerifyPrdPath, resolveNotifyPrd, runPrdMigration, consolidateAllFlatPrds, shouldSkipInvestigationForCleanRun, archiveCompletedPrd, retireCompletedSlugs, SCHEDULER_BOOTED_AT, SCHEDULER_CODE_SHA, resetJobFields, executeJob, prdArchivedSkipResult, spawnJob, listPrdsInternal, computeStallSummary, findStaleQuarantinedJobs, QUARANTINE_ESCALATE_MS };