claude-code-session-manager 0.35.7 → 0.35.9
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/assets/{TiptapBody-C4BgkGy0.js → TiptapBody-Wc0rOkr4.js} +1 -1
- package/dist/assets/{index-DgFpE4dn.js → index-B4PkQh71.js} +286 -285
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/develop/standards.md +3 -1
- package/src/main/__tests__/dod-drain-hook.test.cjs +105 -0
- package/src/main/__tests__/runVerify.test.cjs +33 -9
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +11 -1
- package/src/main/__tests__/scheduler-committed-in-window.test.cjs +62 -0
- package/src/main/browserView.cjs +46 -1
- package/src/main/index.cjs +29 -0
- package/src/main/ipcSchemas.cjs +15 -0
- package/src/main/lib/definitionOfDone.cjs +38 -0
- package/src/main/lib/dodDrainHook.cjs +23 -1
- package/src/main/runVerify.cjs +16 -0
- package/src/main/scheduler.cjs +15 -8
- package/src/preload/api.d.ts +20 -1
- package/src/preload/browserViewPreload.cjs +52 -1
- package/src/preload/index.cjs +2 -0
package/src/main/scheduler.cjs
CHANGED
|
@@ -193,10 +193,12 @@ function gitHead(cwd) {
|
|
|
193
193
|
});
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
// Returns true if ≥1 commit landed
|
|
197
|
-
// (with 60s slack)
|
|
198
|
-
//
|
|
199
|
-
//
|
|
196
|
+
// Returns true if ≥1 commit landed on any ref (branch, remote-tracking branch,
|
|
197
|
+
// or tag) in cwd between startedAt and finishedAt (with 60s slack) — not just
|
|
198
|
+
// the currently checked-out branch. Used by the self-heal pass to derive
|
|
199
|
+
// committedDuringRun from the recorded run window — the live commit-guard uses
|
|
200
|
+
// gitHead() instead. Never throws; git-unavailable → false (no override, job
|
|
201
|
+
// stays as-is).
|
|
200
202
|
function committedInWindow(cwd, startedAt, finishedAt) {
|
|
201
203
|
return new Promise((resolve) => {
|
|
202
204
|
if (!cwd || !startedAt) { resolve(false); return; }
|
|
@@ -205,7 +207,7 @@ function committedInWindow(cwd, startedAt, finishedAt) {
|
|
|
205
207
|
: new Date().toISOString();
|
|
206
208
|
execFile(
|
|
207
209
|
'git',
|
|
208
|
-
['-C', cwd, 'log', '--format=%H', `--since=${startedAt}`, `--until=${until}`],
|
|
210
|
+
['-C', cwd, 'log', '--all', '--format=%H', `--since=${startedAt}`, `--until=${until}`],
|
|
209
211
|
{ timeout: 10_000, windowsHide: true },
|
|
210
212
|
(err, stdout) => { resolve(!err && String(stdout || '').trim().length > 0); },
|
|
211
213
|
);
|
|
@@ -1922,8 +1924,13 @@ function selectHistoryJobs(jobs, limit) {
|
|
|
1922
1924
|
// Transcript-scan verdicts that re-running verifyRun can re-evaluate. NOT
|
|
1923
1925
|
// 'uncommitted_changes' — that comes from the git commit-guard, which verifyRun
|
|
1924
1926
|
// does not inspect, so re-scanning it would always return 'clean' and wrongly
|
|
1925
|
-
// heal a genuinely-unfinished job.
|
|
1926
|
-
|
|
1927
|
+
// heal a genuinely-unfinished job. 'no_verdict_sentinel' is included because
|
|
1928
|
+
// its raising condition (sentinel === null && !committedDuringRun) depends on
|
|
1929
|
+
// commit-detection, which committedInWindow() can fix retroactively (e.g. the
|
|
1930
|
+
// git-log --all scan added for missed non-HEAD commits) — rescanning lets a
|
|
1931
|
+
// job whose commit is now correctly detected clear on its own instead of
|
|
1932
|
+
// staying stuck in needs_review forever.
|
|
1933
|
+
const RESCANNABLE_VERDICTS = new Set(['transcript_errors', 'verify_unavailable', 'no_verdict_sentinel']);
|
|
1927
1934
|
|
|
1928
1935
|
/**
|
|
1929
1936
|
* Backfill a job's missing runId by scanning RUNS_DIR for a run directory
|
|
@@ -2658,4 +2665,4 @@ const remote = {
|
|
|
2658
2665
|
},
|
|
2659
2666
|
};
|
|
2660
2667
|
|
|
2661
|
-
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt };
|
|
2668
|
+
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets, resolveRunId, isUnresolvableNeedsReview, healTargetForFix, buildInvestigationPrompt, committedInWindow };
|
package/src/preload/api.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface RecordStep {
|
|
|
29
29
|
n: number;
|
|
30
30
|
/** `select` is accepted by replay/export (PRD 410) for forward-compat;
|
|
31
31
|
* the live engine does not emit it yet. */
|
|
32
|
-
verb: 'navigate' | 'click' | 'type' | 'select' | 'wait-for';
|
|
32
|
+
verb: 'navigate' | 'click' | 'type' | 'select' | 'wait-for' | 'drag';
|
|
33
33
|
target: string;
|
|
34
34
|
kind?: 'nav' | 'assert';
|
|
35
35
|
/** True for `type` steps — the actual typed value is never captured. */
|
|
@@ -40,6 +40,16 @@ export interface RecordStep {
|
|
|
40
40
|
variable?: string | null;
|
|
41
41
|
/** `select` steps only — the option value to choose on replay/export. */
|
|
42
42
|
value?: string;
|
|
43
|
+
/** Click position, or drag-start position for `drag` steps. */
|
|
44
|
+
x?: number;
|
|
45
|
+
/** Click position, or drag-start position for `drag` steps. */
|
|
46
|
+
y?: number;
|
|
47
|
+
/** `drag` steps only — drag-end target selector. */
|
|
48
|
+
endTarget?: string;
|
|
49
|
+
/** `drag` steps only — drag-end x position. */
|
|
50
|
+
endX?: number;
|
|
51
|
+
/** `drag` steps only — drag-end y position. */
|
|
52
|
+
endY?: number;
|
|
43
53
|
}
|
|
44
54
|
|
|
45
55
|
/** Per-step replay outcome (PRD 410), streamed as `browser:replay-step:<viewId>`. */
|
|
@@ -1056,6 +1066,13 @@ export interface SessionManagerAPI {
|
|
|
1056
1066
|
| { ok: false; error: string }
|
|
1057
1067
|
>;
|
|
1058
1068
|
saveBinary: (path: string, base64: string) => Promise<{ ok: boolean; error?: string }>;
|
|
1069
|
+
/** Opens a native "Save As" dialog and writes `text` to the chosen path directly
|
|
1070
|
+
* (bypasses config.cjs's write-boundary check — the path is user-chosen via OS dialog). */
|
|
1071
|
+
saveRecording: (payload: { defaultName: string; text: string }) => Promise<
|
|
1072
|
+
| { ok: true; path: string }
|
|
1073
|
+
| { ok: false; canceled: true }
|
|
1074
|
+
| { ok: false; error: string }
|
|
1075
|
+
>;
|
|
1059
1076
|
replay: (payload: {
|
|
1060
1077
|
viewId: string;
|
|
1061
1078
|
steps: RecordStep[];
|
|
@@ -1261,6 +1278,8 @@ export interface SessionManagerAPI {
|
|
|
1261
1278
|
>;
|
|
1262
1279
|
/** Write side — copies a Capture-panel screenshot data URL to the OS clipboard. */
|
|
1263
1280
|
copyImage: (dataUrl: string) => Promise<{ ok: boolean; error?: string }>;
|
|
1281
|
+
/** Write side — copies arbitrary text (e.g. a recorded flow export) to the OS clipboard. */
|
|
1282
|
+
writeText: (text: string) => Promise<{ ok: boolean; error?: string }>;
|
|
1264
1283
|
};
|
|
1265
1284
|
memory: {
|
|
1266
1285
|
/** List markdown memory entries for the given workspace (defaults to 'default'). */
|
|
@@ -16,6 +16,9 @@ const TOKEN_PREFIX = '--sm-record-token=';
|
|
|
16
16
|
const expectedToken = (process.argv.find((a) => a.startsWith(TOKEN_PREFIX)) || '').slice(TOKEN_PREFIX.length) || null;
|
|
17
17
|
|
|
18
18
|
let capturing = false;
|
|
19
|
+
let dragStart = null;
|
|
20
|
+
|
|
21
|
+
const DRAG_THRESHOLD_PX = 6;
|
|
19
22
|
|
|
20
23
|
function selectorFor(el) {
|
|
21
24
|
if (!el || el.nodeType !== 1) return '';
|
|
@@ -39,7 +42,54 @@ document.addEventListener(
|
|
|
39
42
|
'click',
|
|
40
43
|
(e) => {
|
|
41
44
|
if (!capturing) return;
|
|
42
|
-
ipcRenderer.send('browser:record-event', {
|
|
45
|
+
ipcRenderer.send('browser:record-event', {
|
|
46
|
+
verb: 'click',
|
|
47
|
+
target: selectorFor(e.target),
|
|
48
|
+
x: e.clientX,
|
|
49
|
+
y: e.clientY,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
true,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
document.addEventListener(
|
|
56
|
+
'mousedown',
|
|
57
|
+
(e) => {
|
|
58
|
+
if (!capturing) return;
|
|
59
|
+
if (e.button !== 0) return;
|
|
60
|
+
dragStart = { target: e.target, x: e.clientX, y: e.clientY };
|
|
61
|
+
},
|
|
62
|
+
true,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
document.addEventListener(
|
|
66
|
+
'mousemove',
|
|
67
|
+
() => {
|
|
68
|
+
if (!capturing) return;
|
|
69
|
+
// No per-event emit — the coalesced step is emitted on mouseup.
|
|
70
|
+
},
|
|
71
|
+
true,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
document.addEventListener(
|
|
75
|
+
'mouseup',
|
|
76
|
+
(e) => {
|
|
77
|
+
if (!capturing) return;
|
|
78
|
+
if (!dragStart) return;
|
|
79
|
+
const dx = e.clientX - dragStart.x;
|
|
80
|
+
const dy = e.clientY - dragStart.y;
|
|
81
|
+
if (Math.abs(dx) > DRAG_THRESHOLD_PX || Math.abs(dy) > DRAG_THRESHOLD_PX) {
|
|
82
|
+
ipcRenderer.send('browser:record-event', {
|
|
83
|
+
verb: 'drag',
|
|
84
|
+
target: selectorFor(dragStart.target),
|
|
85
|
+
x: dragStart.x,
|
|
86
|
+
y: dragStart.y,
|
|
87
|
+
endTarget: selectorFor(e.target),
|
|
88
|
+
endX: e.clientX,
|
|
89
|
+
endY: e.clientY,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
dragStart = null;
|
|
43
93
|
},
|
|
44
94
|
true,
|
|
45
95
|
);
|
|
@@ -63,5 +113,6 @@ contextBridge.exposeInMainWorld('__smRecorder', {
|
|
|
63
113
|
setRecording: (v, token) => {
|
|
64
114
|
if (!expectedToken || token !== expectedToken) return;
|
|
65
115
|
capturing = !!v;
|
|
116
|
+
dragStart = null;
|
|
66
117
|
},
|
|
67
118
|
});
|
package/src/preload/index.cjs
CHANGED
|
@@ -82,6 +82,7 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
82
82
|
captureDom: (payload) => ipcRenderer.invoke('browser:capture-dom', payload),
|
|
83
83
|
captureShot: (viewId) => ipcRenderer.invoke('browser:capture-shot', { viewId }),
|
|
84
84
|
saveBinary: (path, base64) => ipcRenderer.invoke('browser:save-binary', { path, base64 }),
|
|
85
|
+
saveRecording: (payload) => ipcRenderer.invoke('browser:save-recording', payload),
|
|
85
86
|
replay: (payload) => ipcRenderer.invoke('browser:replay', payload),
|
|
86
87
|
onReplayStep: (viewId, handler) => {
|
|
87
88
|
const channel = `browser:replay-step:${viewId}`;
|
|
@@ -289,6 +290,7 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
289
290
|
pasteImage: () => ipcRenderer.invoke('clipboard:paste-image'),
|
|
290
291
|
pasteText: () => ipcRenderer.invoke('clipboard:paste-text'),
|
|
291
292
|
copyImage: (dataUrl) => ipcRenderer.invoke('browser:copy-image', { dataUrl }),
|
|
293
|
+
writeText: (text) => ipcRenderer.invoke('clipboard:write-text', { text }),
|
|
292
294
|
},
|
|
293
295
|
memory: {
|
|
294
296
|
list: (workspace) => ipcRenderer.invoke('memory:list', workspace ? { workspace } : {}),
|