claude-code-session-manager 0.106.0 → 0.108.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.
- package/dist/assets/{AgentLibrary-CKOZzc2W.js → AgentLibrary-Co54GThg.js} +1 -1
- package/dist/assets/{Button-yuqFpQsK.js → Button-DC6VzovQ.js} +1 -1
- package/dist/assets/{DataModel-Bwdc_gaJ.js → DataModel-DZ0vyJau.js} +1 -1
- package/dist/assets/{History-D1Z0lydo.js → History-Df3YwpPe.js} +1 -1
- package/dist/assets/{Hooks-CyD6l4Is.js → Hooks-DplG6roH.js} +1 -1
- package/dist/assets/{Library-fnod5cPe.js → Library-DLmnlfQt.js} +1 -1
- package/dist/assets/{ListDetail-BKO68SJ4.js → ListDetail-YylbvUj_.js} +1 -1
- package/dist/assets/{MarkdownEditor-DrYMnMBy.js → MarkdownEditor-DMPKWHTF.js} +1 -1
- package/dist/assets/{McpServers-CgfItWhF.js → McpServers-Da-Ty5nc.js} +1 -1
- package/dist/assets/{Memory-4M3gO3_r.js → Memory-C3rUQ_cg.js} +1 -1
- package/dist/assets/{Panel-CKbXo-LT.js → Panel-CC3JTnOI.js} +1 -1
- package/dist/assets/{Permissions-Bc9EpjXL.js → Permissions-XvBiMj1-.js} +1 -1
- package/dist/assets/{Plugins-B_-PlEIx.js → Plugins-CzHY1mgS.js} +2 -2
- package/dist/assets/{ProvenanceBadge-Cxev7XSA.js → ProvenanceBadge-DaJIWVcg.js} +1 -1
- package/dist/assets/{SaveBar-DiqscG6q.js → SaveBar-C06rFT34.js} +1 -1
- package/dist/assets/Scheduler-BehadU-0.js +16 -0
- package/dist/assets/{ScopeSwitcher-BsM4-jHQ.js → ScopeSwitcher-Cp5mxblN.js} +1 -1
- package/dist/assets/Settings-Cg9LIni0.js +4 -0
- package/dist/assets/{SkillReferenceGraph-B9YlOuPo.js → SkillReferenceGraph-DuWRmT_f.js} +1 -1
- package/dist/assets/{Skills-Dtf-z_zN.js → Skills-CMmCZfIv.js} +1 -1
- package/dist/assets/{SystemPrompt-HJDn0E8C.js → SystemPrompt-D_-MV025.js} +1 -1
- package/dist/assets/{TagLibrary-DLGOfauU.js → TagLibrary-D9Tv3yOJ.js} +1 -1
- package/dist/assets/{TiptapBody-Cdmv1CuM.js → TiptapBody-1r4ODLJ7.js} +1 -1
- package/dist/assets/{Toggle-CQFhOo1F.js → Toggle-BGT68XHP.js} +1 -1
- package/dist/assets/{ViewTabs-CxjDCHjH.js → ViewTabs-T0NBc8uv.js} +1 -1
- package/dist/assets/{index-Blo38vfX.js → index-3_AtCAyd.js} +3 -3
- package/dist/assets/{index-DkJv4l6L.css → index-tr1w3hHB.css} +1 -1
- package/dist/assets/{settingsSchema-B0uoL_1R.js → settingsSchema-CvoNk3Ye.js} +1 -1
- package/dist/assets/useAgentsDirWatch-1C2CBBya.js +1 -0
- package/dist/assets/{useConfigDirWatch-Dxv7JbCU.js → useConfigDirWatch-Cd5PnTkB.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +2 -1
- package/scripts/scheduler-mcp-server.cjs +52 -7
- package/src/main/build-info.json +4 -4
- package/src/main/index.cjs +8 -0
- package/src/main/ipcSchemas.cjs +93 -0
- package/src/main/lib/builtinMacros.cjs +2 -2
- package/src/main/lib/e2eStateMachine.cjs +39 -0
- package/src/main/lib/mcpToolCatalog.cjs +8 -6
- package/src/main/lib/shippedPersonaSeeds.cjs +11 -0
- package/src/main/lib/usageCircuit.cjs +15 -0
- package/src/main/scheduler.cjs +57 -11
- package/src/main/usage.cjs +10 -2
- package/src/main/webRemote.cjs +1364 -0
- package/src/preload/api.d.ts +64 -1
- package/src/preload/index.cjs +39 -0
- package/src/seed/agents/demo-video-builder.md +69 -48
- package/dist/assets/Scheduler-CCcJzuu8.js +0 -16
- package/dist/assets/Settings-C3-VL-T8.js +0 -3
- package/dist/assets/useAgentsDirWatch-e3TJUFPP.js +0 -1
package/src/preload/api.d.ts
CHANGED
|
@@ -758,7 +758,7 @@ export interface ScheduleJobStatusHistoryEntry {
|
|
|
758
758
|
at: string;
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
-
export type SchedulePauseReason = 'rate_limit' | 'auth' | 'network' | 'manual' | 'reset_failure';
|
|
761
|
+
export type SchedulePauseReason = 'rate_limit' | 'auth' | 'network' | 'manual' | 'agent' | 'reset_failure';
|
|
762
762
|
|
|
763
763
|
export interface SchedulePauseInfo {
|
|
764
764
|
reason: SchedulePauseReason;
|
|
@@ -767,6 +767,8 @@ export interface SchedulePauseInfo {
|
|
|
767
767
|
/** ISO timestamp at which auto-resume will fire (typically next 5h reset).
|
|
768
768
|
* null means "indefinite — wait for manual Run now". */
|
|
769
769
|
resumeAt: string | null;
|
|
770
|
+
/** Present when reason is 'agent': who paused and why. */
|
|
771
|
+
by?: { originClaudeSessionId: string | null; cwd: string | null; reason: string | null } | null;
|
|
770
772
|
}
|
|
771
773
|
|
|
772
774
|
export interface ScheduleHealthSnapshot {
|
|
@@ -1582,6 +1584,46 @@ export type CreateProjectFolderResult =
|
|
|
1582
1584
|
| { ok: true; path: string }
|
|
1583
1585
|
| { ok: false; code: 'invalid-name' | 'invalid-parent' | 'exists' | 'io'; error: string; path?: string };
|
|
1584
1586
|
|
|
1587
|
+
// ────────────────────────────────────────────── Web Remote (PRD 08)
|
|
1588
|
+
|
|
1589
|
+
export interface WebRemoteDevice {
|
|
1590
|
+
deviceId: string;
|
|
1591
|
+
deviceName: string;
|
|
1592
|
+
issuedAt: string;
|
|
1593
|
+
lastConnectedAt: string | null;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
export interface WebRemoteStatus {
|
|
1597
|
+
enabled: boolean;
|
|
1598
|
+
remoteControlEnabled: boolean;
|
|
1599
|
+
connected: boolean;
|
|
1600
|
+
e2eActive: boolean;
|
|
1601
|
+
/** True once the user has confirmed the SAS on the desktop. */
|
|
1602
|
+
e2eAuthenticated: boolean;
|
|
1603
|
+
/** Explicit E2E session state for UI rendering. */
|
|
1604
|
+
e2eState: 'idle' | 'pending_sas' | 'authenticated' | 'failed';
|
|
1605
|
+
/** 6-digit Short Authentication String pending user confirmation, or null. */
|
|
1606
|
+
pendingSas: string | null;
|
|
1607
|
+
devices: WebRemoteDevice[];
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
export interface WebRemotePairResult {
|
|
1611
|
+
ok: boolean;
|
|
1612
|
+
deviceId?: string;
|
|
1613
|
+
error?: string;
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
export interface WebRemoteMutationResult {
|
|
1617
|
+
ok: boolean;
|
|
1618
|
+
error?: string;
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
export interface WebRemoteAuditTailResult {
|
|
1622
|
+
ok: boolean;
|
|
1623
|
+
lines?: string[];
|
|
1624
|
+
error?: string;
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1585
1627
|
export interface SessionManagerAPI {
|
|
1586
1628
|
app: {
|
|
1587
1629
|
version: () => Promise<string>;
|
|
@@ -2069,6 +2111,27 @@ export interface SessionManagerAPI {
|
|
|
2069
2111
|
* renderer needs per-row badges without a separate git call per file. */
|
|
2070
2112
|
fileStatus: (cwd: string) => Promise<GitFileStatusMap>;
|
|
2071
2113
|
};
|
|
2114
|
+
webRemote: {
|
|
2115
|
+
getStatus: () => Promise<WebRemoteStatus>;
|
|
2116
|
+
enable: () => Promise<WebRemoteMutationResult>;
|
|
2117
|
+
disable: () => Promise<WebRemoteMutationResult>;
|
|
2118
|
+
/** Allow MUTATE-tier commands (pty spawn/write, scheduler writes). Default off. */
|
|
2119
|
+
enableControl: () => Promise<WebRemoteMutationResult>;
|
|
2120
|
+
/** Block MUTATE-tier commands — mobile becomes read-only mirror. */
|
|
2121
|
+
disableControl: () => Promise<WebRemoteMutationResult>;
|
|
2122
|
+
pair: (otp: string) => Promise<WebRemotePairResult>;
|
|
2123
|
+
revokeDevice: (deviceId: string) => Promise<WebRemoteMutationResult>;
|
|
2124
|
+
auditTail: (lines?: number) => Promise<WebRemoteAuditTailResult>;
|
|
2125
|
+
onStatus: (handler: (status: WebRemoteStatus) => void) => () => void;
|
|
2126
|
+
onTokenRevoked: (handler: (ev: { deviceId: string }) => void) => () => void;
|
|
2127
|
+
/** Revoke ALL paired devices and tear down every session immediately (panic). */
|
|
2128
|
+
revokeAll: () => Promise<WebRemoteMutationResult>;
|
|
2129
|
+
/** Subscribe to the completion event broadcast after revokeAll. */
|
|
2130
|
+
onRevokedAll: (handler: (ev: { revokedCount: number }) => void) => () => void;
|
|
2131
|
+
/** Confirm that the SAS shown on the desktop matches the browser — marks the
|
|
2132
|
+
* E2E session as authenticated and unblocks MUTATE-tier commands. */
|
|
2133
|
+
confirmSas: () => Promise<WebRemoteMutationResult>;
|
|
2134
|
+
};
|
|
2072
2135
|
chat: {
|
|
2073
2136
|
/** Spawn a headless `claude -p` run for a tab. Results arrive via the on* listeners. */
|
|
2074
2137
|
run: (payload: ChatRunPayload) => Promise<{ ok: boolean }>;
|
package/src/preload/index.cjs
CHANGED
|
@@ -392,6 +392,45 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
392
392
|
status: (cwd) => ipcRenderer.invoke('git:status', { cwd }),
|
|
393
393
|
fileStatus: (cwd) => ipcRenderer.invoke('git:file-status', { cwd }),
|
|
394
394
|
},
|
|
395
|
+
webRemote: {
|
|
396
|
+
/** Current connection state (no token values). */
|
|
397
|
+
getStatus: () => ipcRenderer.invoke('webRemote:get-status'),
|
|
398
|
+
/** Turn remote control on. Initiates relay connection if a device is paired. */
|
|
399
|
+
enable: () => ipcRenderer.invoke('webRemote:enable'),
|
|
400
|
+
/** Turn remote control off. Immediately drops relay connection. */
|
|
401
|
+
disable: () => ipcRenderer.invoke('webRemote:disable'),
|
|
402
|
+
/** Allow MUTATE-tier commands (pty spawn/write, scheduler writes). Default off. */
|
|
403
|
+
enableControl: () => ipcRenderer.invoke('webRemote:enable-control'),
|
|
404
|
+
/** Block MUTATE-tier commands — mobile becomes read-only mirror. */
|
|
405
|
+
disableControl: () => ipcRenderer.invoke('webRemote:disable-control'),
|
|
406
|
+
/** Pair a new device using the 8-character OTP shown in the web UI. */
|
|
407
|
+
pair: (otp) => ipcRenderer.invoke('webRemote:pair', { otp }),
|
|
408
|
+
/** Revoke a paired device by its deviceId. */
|
|
409
|
+
revokeDevice: (deviceId) => ipcRenderer.invoke('webRemote:revoke-device', { deviceId }),
|
|
410
|
+
/** Read the last N lines of today's audit log (default 50). */
|
|
411
|
+
auditTail: (lines) => ipcRenderer.invoke('webRemote:audit-tail', lines ? { lines } : {}),
|
|
412
|
+
/** Push event from main when connection status changes. */
|
|
413
|
+
onStatus: (handler) => {
|
|
414
|
+
const listener = (_e, payload) => handler(payload);
|
|
415
|
+
ipcRenderer.on('webRemote:status', listener);
|
|
416
|
+
return () => ipcRenderer.removeListener('webRemote:status', listener);
|
|
417
|
+
},
|
|
418
|
+
/** Push event when the relay revokes this device's token. */
|
|
419
|
+
onTokenRevoked: (handler) => {
|
|
420
|
+
const listener = (_e, payload) => handler(payload);
|
|
421
|
+
ipcRenderer.on('webRemote:token-revoked', listener);
|
|
422
|
+
return () => ipcRenderer.removeListener('webRemote:token-revoked', listener);
|
|
423
|
+
},
|
|
424
|
+
/** Revoke ALL paired devices and tear down every session immediately. */
|
|
425
|
+
revokeAll: () => ipcRenderer.invoke('webRemote:revoke-all'),
|
|
426
|
+
confirmSas: () => ipcRenderer.invoke('webRemote:confirm-sas'),
|
|
427
|
+
/** Push event when revokeAll completes (main broadcasts webRemote:revoked-all). */
|
|
428
|
+
onRevokedAll: (handler) => {
|
|
429
|
+
const listener = (_e, payload) => handler(payload);
|
|
430
|
+
ipcRenderer.on('webRemote:revoked-all', listener);
|
|
431
|
+
return () => ipcRenderer.removeListener('webRemote:revoked-all', listener);
|
|
432
|
+
},
|
|
433
|
+
},
|
|
395
434
|
chat: {
|
|
396
435
|
/** Spawn a headless claude -p job. Results arrive via the on* listeners. */
|
|
397
436
|
run: (payload) => ipcRenderer.invoke('chat:run', payload),
|
|
@@ -5,7 +5,7 @@ tools: Read, Grep, Glob, Bash, Write, Edit
|
|
|
5
5
|
title: 'Project Home — Demo Video'
|
|
6
6
|
model: sonnet
|
|
7
7
|
effort: medium
|
|
8
|
-
seedVersion:
|
|
8
|
+
seedVersion: 3
|
|
9
9
|
---
|
|
10
10
|
|
|
11
11
|
You are the demo-video-builder. You make a 30-second animated demo that shows someone what this
|
|
@@ -52,54 +52,73 @@ Produce exactly one complete HTML document:
|
|
|
52
52
|
- Expose `window.smDemo = { duration: 30, seek(t), play(), pause() }`, where `seek(t)` renders
|
|
53
53
|
the exact frame for time `t` in seconds, independent of whatever played before it.
|
|
54
54
|
- Autoplays once on load; shows play/pause, restart, and a progress bar.
|
|
55
|
-
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
- A light/dark neutral palette with sufficient contrast in both.
|
|
62
|
-
</output_contract>
|
|
55
|
+
- Narration audio built per `<audio_policy>
|
|
56
|
+
Narration is the default deliverable: every video ships with a natural-sounding on-device voice
|
|
57
|
+
track reading the storyboard captions. Generate it at build time, never at playback time; the
|
|
58
|
+
shipped document never calls out to a network TTS service. A silent video is allowed ONLY when
|
|
59
|
+
the install or model download below genuinely fails — then your final report must state the exact
|
|
60
|
+
failing command and its error. Generating a track and then saving the HTML without it is a failed run.
|
|
63
61
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
- **TTS: Kokoro-82M via `kokoro-onnx`** (Apache-2.0, ONNX runtime, no PyTorch). Install with
|
|
63
|
+
`python3 -m pip install --user --break-system-packages kokoro-onnx soundfile`. If
|
|
64
|
+
`python3 -m pip` is missing, bootstrap it first:
|
|
65
|
+
`curl -sS https://bootstrap.pypa.io/get-pip.py | python3 - --user --break-system-packages`.
|
|
66
|
+
`ffmpeg` must be on PATH.
|
|
67
|
+
- **Shared model cache:** `MODEL_DIR=~/.cache/kokoro-onnx`, reused across projects. Only if
|
|
68
|
+
`kokoro-v1.0.onnx` (~325 MB) or `voices-v1.0.bin` (~28 MB) is missing there, download it with
|
|
69
|
+
`curl -L --max-time 600 -o "$MODEL_DIR/<file>" https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0/<file>`.
|
|
70
|
+
Never download into /tmp or the project tree.
|
|
71
|
+
- **Voices** (all `lang='en-us'`, speed 1.0; raise speed only to fit a line into its scene, max 1.15):
|
|
72
|
+
female narrator `af_heart` (Kokoro's top-graded voice; alt `af_bella`) is the default; male
|
|
73
|
+
narrator `am_michael` (alt `am_fenrir`) when the human asks for it or the project's audience or
|
|
74
|
+
brand suggests it. A two-voice video may alternate `af_heart` / `am_michael` per scene. Never use
|
|
75
|
+
Piper `-medium` or espeak (robotic). Piper `en_US-lessac-high` is the fallback only when Kokoro
|
|
76
|
+
cannot be installed.
|
|
77
|
+
- **Build recipe.** Work in a fresh `mktemp -d` directory outside the project. Save this as
|
|
78
|
+
`build.py` there, fill in `SCENES` and `TEMPLATE`, and run it:
|
|
68
79
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
80
|
+
```python
|
|
81
|
+
import base64, os, subprocess
|
|
82
|
+
import numpy as np, soundfile
|
|
83
|
+
from kokoro_onnx import Kokoro
|
|
84
|
+
|
|
85
|
+
MODEL_DIR = os.path.expanduser('~/.cache/kokoro-onnx')
|
|
86
|
+
SCENES = [ # (start_s, end_s, line, voice) — one per scene, from the storyboard captions
|
|
87
|
+
(0.0, 4.0, 'Project name. One-line purpose.', 'af_heart'),
|
|
88
|
+
]
|
|
89
|
+
k = Kokoro(f'{MODEL_DIR}/kokoro-v1.0.onnx', f'{MODEL_DIR}/voices-v1.0.bin')
|
|
90
|
+
buf, sr = None, None
|
|
91
|
+
for start, end, line, voice in SCENES:
|
|
92
|
+
samples, sr = k.create(line, voice=voice, speed=1.0, lang='en-us')
|
|
93
|
+
room = end - start - 0.3
|
|
94
|
+
if len(samples) / sr > room: # overruns its scene: speed up, never past 1.15
|
|
95
|
+
speed = min(1.15, len(samples) / sr / room)
|
|
96
|
+
samples, sr = k.create(line, voice=voice, speed=speed, lang='en-us')
|
|
97
|
+
if buf is None:
|
|
98
|
+
buf = np.zeros(30 * sr, dtype=np.float32)
|
|
99
|
+
at = int((start + 0.15) * sr)
|
|
100
|
+
clip = samples[: len(buf) - at]
|
|
101
|
+
buf[at : at + len(clip)] += clip
|
|
102
|
+
soundfile.write('narration.wav', buf, sr)
|
|
103
|
+
subprocess.run(['ffmpeg', '-y', '-i', 'narration.wav', '-ac', '1', '-ar', '24000',
|
|
104
|
+
'-c:a', 'libmp3lame', '-b:a', '64k', 'narration.mp3'], check=True)
|
|
105
|
+
b64 = base64.b64encode(open('narration.mp3', 'rb').read()).decode()
|
|
106
|
+
html = open('template.html').read() # your finished HTML with the placeholder below
|
|
107
|
+
html = html.replace('AUDIO_B64_PLACEHOLDER', b64) # <audio src="data:audio/mpeg;base64,AUDIO_B64_PLACEHOLDER">
|
|
108
|
+
open('demo.html', 'w').write(html)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Keep the mono track at the model's native 24000 Hz and ~64 kbps — downsampling or dropping below
|
|
112
|
+
~48 kbps makes speech sound crushed (a 30 s track is only ~235 KB). Keep the whole document
|
|
113
|
+
under the 2 MB cap.
|
|
93
114
|
- Wire `play()`/`pause()`/`seek(t)` to the `<audio>` element too (`audio.currentTime = t` on
|
|
94
|
-
seek) so
|
|
95
|
-
|
|
96
|
-
button instead of failing silently forever.
|
|
115
|
+
seek) so narration never drifts from the visual clock; call `.play()` on load and, on rejection
|
|
116
|
+
(autoplay-blocked), show a small "tap for sound" button instead of failing silently.
|
|
97
117
|
- Never write the literal pattern `function (` or `function(` in the inline `<script>` —
|
|
98
|
-
`project_demo_video_write`'s safety scanner blocks anything matching `/\bFunction\s*\(/i
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
silent video rather than failing the whole run — audio is a nice-to-have, never a blocker.
|
|
118
|
+
`project_demo_video_write`'s safety scanner blocks anything matching `/\bFunction\s*\(/i`.
|
|
119
|
+
Use arrow functions (`() => { ... }`) throughout.
|
|
120
|
+
- Save the built file by path (see `<process>` step 6). Never retype the base64 audio into a tool
|
|
121
|
+
argument — a ~300 KB payload retyped by the model is exactly how a run once shipped silent.
|
|
103
122
|
</audio_policy>
|
|
104
123
|
|
|
105
124
|
<process>
|
|
@@ -109,11 +128,13 @@ never calls out to a network TTS service.
|
|
|
109
128
|
2. Write the claims list: for every goal or feature you plan to show, note the source you read
|
|
110
129
|
it from. Drop anything without a source.
|
|
111
130
|
3. Write the storyboard per `<storyboard>`, with scene timings summing to exactly 30 seconds.
|
|
112
|
-
4. Build the HTML per `<output_contract
|
|
131
|
+
4. Build the HTML per `<output_contract>` and the narration track per `<audio_policy>`, ending
|
|
132
|
+
with the final document written to a file (`demo.html`) that contains the `<audio>` tag.
|
|
113
133
|
5. Self-check before saving: scene durations sum to 30, every caption traces to a claim in your
|
|
114
134
|
list, and the document contains none of the forbidden network APIs.
|
|
115
|
-
6. Call `project_demo_video_write` exactly once with `{
|
|
116
|
-
|
|
135
|
+
6. Call `project_demo_video_write` exactly once with `{ htmlPath }` — the absolute path of the
|
|
136
|
+
built file. Never pass the document in `html` (retyping base64 is how narration got lost). It
|
|
137
|
+
validates the document and writes it as the project's Demo Video.
|
|
117
138
|
7. Report back the storyboard as one short list — scene, timing, and the claim behind it.
|
|
118
139
|
</process>
|
|
119
140
|
|