claude-code-session-manager 0.33.1 → 0.34.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/README.md +5 -1
- package/dist/assets/{TiptapBody-BqQFXHkk.js → TiptapBody-DWeWI8gw.js} +51 -51
- package/dist/assets/index-C2m4dco8.css +32 -0
- package/dist/assets/index-CFT773vM.js +3491 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/discover-features/SKILL.md +95 -0
- package/plugins/session-manager-dev/skills/optimize-kpi/SKILL.md +9 -9
- package/plugins/session-manager-dev/skills/process-feedback/SKILL.md +77 -62
- package/plugins/session-manager-dev/skills/project-status/SKILL.md +13 -0
- package/src/main/__tests__/chat-queue.test.cjs +65 -0
- package/src/main/__tests__/chat-stop-signal.test.cjs +52 -0
- package/src/main/__tests__/exchanges.test.cjs +177 -0
- package/src/main/__tests__/files-reject-credentials.test.cjs +40 -0
- package/src/main/__tests__/kg-augment.test.cjs +195 -0
- package/src/main/__tests__/memoryAggregate.test.cjs +70 -0
- package/src/main/agentMemory.cjs +0 -21
- package/src/main/chatRunner.cjs +393 -0
- package/src/main/exchanges.cjs +125 -0
- package/src/main/files.cjs +20 -8
- package/src/main/historyAggregator.cjs +0 -162
- package/src/main/index.cjs +76 -57
- package/src/main/ipcSchemas.cjs +70 -30
- package/src/main/lib/kgExchangePairing.cjs +75 -0
- package/src/main/lib/reaperHelpers.cjs +7 -1
- package/src/main/lib/summarize.cjs +114 -0
- package/src/main/memoryAggregate.cjs +250 -0
- package/src/main/pluginInstall.cjs +4 -2
- package/src/main/scheduler.cjs +66 -20
- package/src/main/supervisor.cjs +16 -0
- package/src/main/transcripts.cjs +22 -2
- package/src/main/voiceHotkey.cjs +0 -2
- package/src/main/webRemote.cjs +11 -78
- package/src/preload/api.d.ts +131 -125
- package/src/preload/index.cjs +45 -29
- package/dist/assets/index-1PpZBVUr.js +0 -3535
- package/dist/assets/index-AKeGl-VM.css +0 -32
- package/src/main/kg.cjs +0 -792
- package/src/main/lib/kgLite.cjs +0 -195
- package/src/main/lib/kgPrune.cjs +0 -87
package/src/main/webRemote.cjs
CHANGED
|
@@ -28,6 +28,7 @@ const { writeTextAtomic, validatePath } = require('./config.cjs');
|
|
|
28
28
|
const logs = require('./logs.cjs');
|
|
29
29
|
const { sendIfAlive } = require('./lib/sendToRenderer.cjs');
|
|
30
30
|
const { schemas } = require('./ipcSchemas.cjs');
|
|
31
|
+
const { summarize: _sharedSummarize } = require('./lib/summarize.cjs');
|
|
31
32
|
const { makeState, confirmSas: confirmSasLogic } = require('./lib/e2eStateMachine.cjs');
|
|
32
33
|
|
|
33
34
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
@@ -740,70 +741,9 @@ function startSessionListPush() {
|
|
|
740
741
|
}
|
|
741
742
|
|
|
742
743
|
// ─── SM-V2-03: mobile summary via Claude Haiku 4.5 ───────────────────────────
|
|
744
|
+
// Summarization logic lives in lib/summarize.cjs (shared with exchanges.cjs).
|
|
743
745
|
|
|
744
|
-
const SUMMARY_MIN_CHARS = 280;
|
|
745
|
-
const SUMMARY_MODEL = 'claude-haiku-4-5';
|
|
746
|
-
const SUMMARY_MAX_INPUT_CHARS = 24_000; // cap the turn text sent to Haiku (~6k tokens)
|
|
747
|
-
const SUMMARY_SYSTEM =
|
|
748
|
-
'Summarize this Claude Code assistant turn for a phone screen in 2 sentences max, ' +
|
|
749
|
-
'followed by an optional list of up to 3 short action items. Plain text only — no ' +
|
|
750
|
-
'markdown headers, no code blocks. Lead with what was done or decided.';
|
|
751
|
-
|
|
752
|
-
let _anthropicKeyCache = null; // memoized found key only (string); null = re-resolve
|
|
753
|
-
|
|
754
|
-
/** Resolve the Anthropic API key: env → web-remote.json → null (degrade to raw).
|
|
755
|
-
* Only a FOUND key is cached — if absent we re-resolve each call (cheap, loadConfig
|
|
756
|
-
* is TTL-cached) so adding the key to web-remote.json later takes effect without a restart. */
|
|
757
|
-
async function resolveAnthropicKey() {
|
|
758
|
-
if (_anthropicKeyCache) return _anthropicKeyCache;
|
|
759
|
-
const fromEnv = process.env.ANTHROPIC_API_KEY;
|
|
760
|
-
if (fromEnv && fromEnv.trim()) { _anthropicKeyCache = fromEnv.trim(); return _anthropicKeyCache; }
|
|
761
|
-
try {
|
|
762
|
-
const cfg = await loadConfig();
|
|
763
|
-
const k = cfg.anthropicApiKey;
|
|
764
|
-
if (typeof k === 'string' && k.trim()) { _anthropicKeyCache = k.trim(); return _anthropicKeyCache; }
|
|
765
|
-
} catch { /* fall through to null → re-resolve next time */ }
|
|
766
|
-
return null;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
/** POST to the Anthropic Messages API. Returns the first text block, or throws. */
|
|
770
|
-
function anthropicSummarize(apiKey, text) {
|
|
771
|
-
const body = JSON.stringify({
|
|
772
|
-
model: SUMMARY_MODEL,
|
|
773
|
-
max_tokens: 320,
|
|
774
|
-
system: SUMMARY_SYSTEM,
|
|
775
|
-
messages: [{ role: 'user', content: text.slice(0, SUMMARY_MAX_INPUT_CHARS) }],
|
|
776
|
-
});
|
|
777
|
-
return new Promise((resolve, reject) => {
|
|
778
|
-
const req = https.request('https://api.anthropic.com/v1/messages', {
|
|
779
|
-
method: 'POST',
|
|
780
|
-
headers: {
|
|
781
|
-
'content-type': 'application/json',
|
|
782
|
-
'x-api-key': apiKey,
|
|
783
|
-
'anthropic-version': '2023-06-01',
|
|
784
|
-
'content-length': Buffer.byteLength(body),
|
|
785
|
-
},
|
|
786
|
-
timeout: 20_000,
|
|
787
|
-
}, (res) => {
|
|
788
|
-
let data = '';
|
|
789
|
-
res.on('data', (c) => { data += c; });
|
|
790
|
-
res.on('end', () => {
|
|
791
|
-
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
792
|
-
return reject(new Error(`anthropic HTTP ${res.statusCode}`));
|
|
793
|
-
}
|
|
794
|
-
try {
|
|
795
|
-
const json = JSON.parse(data);
|
|
796
|
-
const block = Array.isArray(json.content) ? json.content.find((b) => b.type === 'text') : null;
|
|
797
|
-
if (!block?.text) return reject(new Error('no text in response'));
|
|
798
|
-
resolve(block.text.trim());
|
|
799
|
-
} catch (e) { reject(e); }
|
|
800
|
-
});
|
|
801
|
-
});
|
|
802
|
-
req.on('error', reject);
|
|
803
|
-
req.on('timeout', () => req.destroy(new Error('anthropic request timed out')));
|
|
804
|
-
req.end(body);
|
|
805
|
-
});
|
|
806
|
-
}
|
|
746
|
+
const SUMMARY_MIN_CHARS = 280; // below this, push raw — not worth an API call
|
|
807
747
|
|
|
808
748
|
/**
|
|
809
749
|
* Produce a mobile summary of the watcher's last completed assistant turn and push it.
|
|
@@ -823,22 +763,15 @@ async function maybeSummarize(w) {
|
|
|
823
763
|
return;
|
|
824
764
|
}
|
|
825
765
|
|
|
826
|
-
const
|
|
827
|
-
if (
|
|
828
|
-
|
|
829
|
-
pushEvent('event:session:summary', {
|
|
830
|
-
tabId: w.tabId, summary: text.slice(0, 600), ofMessageId, model: 'raw', degraded: 'no_api_key', ts: Date.now(),
|
|
831
|
-
});
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
try {
|
|
836
|
-
const summary = await anthropicSummarize(apiKey, text);
|
|
837
|
-
pushEvent('event:session:summary', { tabId: w.tabId, summary, ofMessageId, model: SUMMARY_MODEL, ts: Date.now() });
|
|
838
|
-
} catch (e) {
|
|
839
|
-
logs.writeLine({ scope: 'webRemote', level: 'warn', message: 'summary failed; pushing raw', meta: { error: e?.message } });
|
|
840
|
-
pushEvent('event:session:summary', { tabId: w.tabId, summary: text.slice(0, 600), ofMessageId, model: 'raw', degraded: 'api_error', ts: Date.now() });
|
|
766
|
+
const { summary, model, degraded } = await _sharedSummarize(text);
|
|
767
|
+
if (degraded) {
|
|
768
|
+
logs.writeLine({ scope: 'webRemote', level: 'warn', message: `summary degraded: ${degraded}`, meta: {} });
|
|
841
769
|
}
|
|
770
|
+
pushEvent('event:session:summary', {
|
|
771
|
+
tabId: w.tabId, summary, ofMessageId, model,
|
|
772
|
+
...(degraded ? { degraded } : {}),
|
|
773
|
+
ts: Date.now(),
|
|
774
|
+
});
|
|
842
775
|
}
|
|
843
776
|
|
|
844
777
|
// ─── Message handling & command dispatch ─────────────────────────────────────
|
package/src/preload/api.d.ts
CHANGED
|
@@ -443,24 +443,6 @@ export interface HistoryAggregateResult {
|
|
|
443
443
|
scannedMs: number;
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
export interface ConversationSummary {
|
|
447
|
-
/** ISO 8601 timestamp of the first event in the conversation (or file mtime fallback). */
|
|
448
|
-
timestamp: string;
|
|
449
|
-
/** Decoded project cwd, e.g. /home/user/Projects/foo. */
|
|
450
|
-
projectFolder: string;
|
|
451
|
-
stats: {
|
|
452
|
-
/** Wall-clock duration in ms (first event ts → last event ts). Omitted when unknown. */
|
|
453
|
-
duration?: number;
|
|
454
|
-
/** Sum of input + output tokens across the file. 0 when no usage blocks present. */
|
|
455
|
-
estimatedTokens: number;
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
export interface ListConversationsResult {
|
|
460
|
-
conversations: ConversationSummary[];
|
|
461
|
-
scannedMs: number;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
446
|
export interface SessionScanEntry {
|
|
465
447
|
sessionId: string;
|
|
466
448
|
projectEncoded: string;
|
|
@@ -490,7 +472,6 @@ export interface FilesWriteResult { ok: boolean; error: string | null }
|
|
|
490
472
|
export interface FilesCreateResult { ok: boolean; path?: string; error: string | null }
|
|
491
473
|
export interface FilesRenameResult { ok: boolean; newPath?: string; error: string | null }
|
|
492
474
|
export interface FilesDeleteResult { ok: boolean; error: string | null }
|
|
493
|
-
export interface FilesShellResult { ok: boolean; error?: string }
|
|
494
475
|
|
|
495
476
|
export interface SearchFileEntry {
|
|
496
477
|
name: string;
|
|
@@ -681,6 +662,29 @@ export interface MemoryMutationResult {
|
|
|
681
662
|
error: string | null;
|
|
682
663
|
}
|
|
683
664
|
|
|
665
|
+
export interface MemoryClusterLink {
|
|
666
|
+
from: string;
|
|
667
|
+
to: string;
|
|
668
|
+
label?: string;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export interface MemoryCluster {
|
|
672
|
+
id: string;
|
|
673
|
+
name: string;
|
|
674
|
+
summary: string;
|
|
675
|
+
memberSlugs: string[];
|
|
676
|
+
links: MemoryClusterLink[];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export interface MemoryAggregateResult {
|
|
680
|
+
workspace: string;
|
|
681
|
+
generatedAt: number | null;
|
|
682
|
+
clusters: MemoryCluster[];
|
|
683
|
+
orphans: string[];
|
|
684
|
+
cached: boolean;
|
|
685
|
+
error?: string;
|
|
686
|
+
}
|
|
687
|
+
|
|
684
688
|
// ────────────────────────────────────────────── Per-subagent memory
|
|
685
689
|
// Stored at ~/.claude/session-manager/agent-memory/<agentId>.json. Keyed by
|
|
686
690
|
// agent name (the .md filename in ~/.claude/agents/), not by workspace cwd.
|
|
@@ -712,17 +716,6 @@ export interface AgentMemoryMutationResult {
|
|
|
712
716
|
error: string | null;
|
|
713
717
|
}
|
|
714
718
|
|
|
715
|
-
export interface AgentMemoryAgentSummary {
|
|
716
|
-
agentId: string;
|
|
717
|
-
bytes: number;
|
|
718
|
-
mtimeMs: number;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
export interface AgentMemoryListAgentsResult {
|
|
722
|
-
agents: AgentMemoryAgentSummary[];
|
|
723
|
-
error: string | null;
|
|
724
|
-
}
|
|
725
|
-
|
|
726
719
|
// ────────────────────────────────────────────── Git status (richer than app:git-branch)
|
|
727
720
|
|
|
728
721
|
/** Mirrors the status returned by src/main/git.cjs mapStatus(). */
|
|
@@ -845,6 +838,59 @@ export interface WebRemoteAuditTailResult {
|
|
|
845
838
|
error?: string;
|
|
846
839
|
}
|
|
847
840
|
|
|
841
|
+
// ────────────────────────────────────────────── Chat runner (PRD 319)
|
|
842
|
+
|
|
843
|
+
export interface ChatRunPayload {
|
|
844
|
+
tabId: string;
|
|
845
|
+
/** UUID for the claude session — created by the caller; stays the same across
|
|
846
|
+
* resumes so transcript context carries forward. */
|
|
847
|
+
sessionId: string;
|
|
848
|
+
prompt: string;
|
|
849
|
+
cwd: string;
|
|
850
|
+
/** When true, use --resume <sessionId> instead of --session-id. */
|
|
851
|
+
resume?: boolean;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
export interface ChatRunQueuedEvent {
|
|
855
|
+
tabId: string;
|
|
856
|
+
sessionId: string;
|
|
857
|
+
/** 1-based position in the FIFO queue while waiting behind a busy lane. */
|
|
858
|
+
position: number;
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
export interface ChatRunStartedEvent {
|
|
862
|
+
tabId: string;
|
|
863
|
+
sessionId: string;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
export interface ChatRunOutputEvent {
|
|
867
|
+
tabId: string;
|
|
868
|
+
/** Incremental text delta from one assistant content block. */
|
|
869
|
+
delta: string;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
export interface ChatRunNeedsInputEvent {
|
|
873
|
+
tabId: string;
|
|
874
|
+
sessionId: string;
|
|
875
|
+
/** Questions the agent is blocked on. */
|
|
876
|
+
questions: string[];
|
|
877
|
+
/** Full raw assistant text including the stop-signal sentinel. */
|
|
878
|
+
raw: string;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export interface ChatRunCompleteEvent {
|
|
882
|
+
tabId: string;
|
|
883
|
+
sessionId: string;
|
|
884
|
+
/** The run's own final assistant message verbatim. */
|
|
885
|
+
finalMessage: string;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
export interface ChatRunErrorEvent {
|
|
889
|
+
tabId: string;
|
|
890
|
+
sessionId: string;
|
|
891
|
+
message: string;
|
|
892
|
+
}
|
|
893
|
+
|
|
848
894
|
export interface SessionManagerAPI {
|
|
849
895
|
app: {
|
|
850
896
|
version: () => Promise<string>;
|
|
@@ -867,16 +913,6 @@ export interface SessionManagerAPI {
|
|
|
867
913
|
homeSelfCheck: () => Promise<{ ok: boolean; error?: string; realCwd?: string }>;
|
|
868
914
|
onNewSession: (handler: () => void) => () => void;
|
|
869
915
|
onRebootSession: (handler: () => void) => () => void;
|
|
870
|
-
openInEditor: (cwd: string, editor?: string | null) => Promise<{ ok: boolean; opener?: string; error?: string }>;
|
|
871
|
-
/** Open an http/https URL in the OS default browser. file://, javascript:,
|
|
872
|
-
* and other schemes are rejected with `ok:false` to prevent abuse. */
|
|
873
|
-
openExternal: (url: string) => Promise<{ ok: boolean; error?: string }>;
|
|
874
|
-
/** Open a specific file at line:col in the user's editor. Editors with
|
|
875
|
-
* goto-line support (code/cursor/subl) get the `-g file:line:col` form;
|
|
876
|
-
* others open the file alone. Image files are routed to the OS default viewer. */
|
|
877
|
-
openFileInEditor: (filePath: string, line?: number, col?: number, editor?: string | null) => Promise<{ ok: boolean; opener?: string; error?: string }>;
|
|
878
|
-
openInFinder: (cwd: string) => Promise<{ ok: boolean; opener?: string; error?: string }>;
|
|
879
|
-
openInTerminal: (cwd: string) => Promise<{ ok: boolean; opener?: string; error?: string }>;
|
|
880
916
|
archiveProject: (encoded: string) => Promise<{ ok: boolean; error?: string }>;
|
|
881
917
|
};
|
|
882
918
|
pty: {
|
|
@@ -929,7 +965,6 @@ export interface SessionManagerAPI {
|
|
|
929
965
|
onHotkeyConfigChanged: (handler: (cfg: VoiceHotkeyConfig) => void) => () => void;
|
|
930
966
|
getHotkeyConfig: () => Promise<VoiceHotkeyConfig>;
|
|
931
967
|
setHotkeyConfig: (cfg: VoiceHotkeyConfig) => Promise<VoiceSetHotkeyResult>;
|
|
932
|
-
getHotkeyConfigPath: () => Promise<string>;
|
|
933
968
|
setRecording: (recording: boolean) => void;
|
|
934
969
|
/** F5: read persisted audio-input device preference. */
|
|
935
970
|
getDevicePref: () => Promise<VoiceDevicePref>;
|
|
@@ -965,8 +1000,21 @@ export interface SessionManagerAPI {
|
|
|
965
1000
|
create: (parentPath: string, name: string, kind: 'file' | 'folder') => Promise<FilesCreateResult>;
|
|
966
1001
|
rename: (path: string, newName: string) => Promise<FilesRenameResult>;
|
|
967
1002
|
delete: (path: string) => Promise<FilesDeleteResult>;
|
|
968
|
-
|
|
969
|
-
|
|
1003
|
+
};
|
|
1004
|
+
/** Consolidated shell open/reveal. One method, discriminated on `as`, replaces
|
|
1005
|
+
* the former app.openIn* / app.openExternal / files.openExternal / files.showInFinder.
|
|
1006
|
+
* Each variant's boundary guard (home-scope / http(s)-only) runs in the main handler. */
|
|
1007
|
+
shell: {
|
|
1008
|
+
open: (
|
|
1009
|
+
opts:
|
|
1010
|
+
| { as: 'editor'; cwd: string; editor?: string | null }
|
|
1011
|
+
| { as: 'fileInEditor'; path: string; line?: number; col?: number; editor?: string | null }
|
|
1012
|
+
| { as: 'finder'; cwd: string }
|
|
1013
|
+
| { as: 'terminal'; cwd: string }
|
|
1014
|
+
| { as: 'external'; url: string }
|
|
1015
|
+
| { as: 'openPath'; path: string }
|
|
1016
|
+
| { as: 'revealPath'; path: string }
|
|
1017
|
+
) => Promise<{ ok: boolean; opener?: string; error?: string }>;
|
|
970
1018
|
};
|
|
971
1019
|
search: {
|
|
972
1020
|
files: (cwd: string, query?: string, opts?: { limit?: number }) => Promise<SearchFilesResult>;
|
|
@@ -983,7 +1031,6 @@ export interface SessionManagerAPI {
|
|
|
983
1031
|
};
|
|
984
1032
|
history: {
|
|
985
1033
|
aggregate: (req?: HistoryAggregateRequest) => Promise<HistoryAggregateResult>;
|
|
986
|
-
listConversations: () => Promise<ListConversationsResult>;
|
|
987
1034
|
scanProjects: () => Promise<SessionScanResult>;
|
|
988
1035
|
};
|
|
989
1036
|
schedule: {
|
|
@@ -993,7 +1040,6 @@ export interface SessionManagerAPI {
|
|
|
993
1040
|
runNow: () => Promise<{ ok: boolean }>;
|
|
994
1041
|
forceTick: () => Promise<{ ok: boolean }>;
|
|
995
1042
|
resume: () => Promise<{ ok: boolean }>;
|
|
996
|
-
refreshReset: () => Promise<{ ok: boolean; nextReset: string | null }>;
|
|
997
1043
|
/** Re-scan prds/ and merge into queue.json; broadcasts updated state. */
|
|
998
1044
|
rescan: () => Promise<{ ok: boolean }>;
|
|
999
1045
|
/** Move all pending+failed PRDs to prds-archived/<ISO>/ and drop their
|
|
@@ -1057,6 +1103,8 @@ export interface SessionManagerAPI {
|
|
|
1057
1103
|
delete: (name: string, workspace?: string) => Promise<MemoryMutationResult>;
|
|
1058
1104
|
/** Create a new memory entry with starter frontmatter + body. */
|
|
1059
1105
|
create: (name: string, description?: string, workspace?: string) => Promise<MemoryMutationResult>;
|
|
1106
|
+
/** Aggregate workspace memories into semantic clusters. `refresh:true` fires a cost-gated `claude -p` pass; otherwise returns the cache only. */
|
|
1107
|
+
aggregate: (workspace: string, refresh?: boolean) => Promise<MemoryAggregateResult>;
|
|
1060
1108
|
};
|
|
1061
1109
|
agentMemory: {
|
|
1062
1110
|
/** List all memory entries for one subagent. Sorted newest first. */
|
|
@@ -1072,8 +1120,6 @@ export interface SessionManagerAPI {
|
|
|
1072
1120
|
) => Promise<AgentMemoryMutationResult>;
|
|
1073
1121
|
/** Delete one entry. Removes the file outright when last entry is removed. */
|
|
1074
1122
|
delete: (agentId: string, entryId: string) => Promise<AgentMemoryMutationResult>;
|
|
1075
|
-
/** List all agents that currently have a memory file on disk. */
|
|
1076
|
-
listAgents: () => Promise<AgentMemoryListAgentsResult>;
|
|
1077
1123
|
};
|
|
1078
1124
|
docEditor: {
|
|
1079
1125
|
pickFile: (payload?: { lastDir?: string }) => Promise<{ path: string | null; error?: string }>;
|
|
@@ -1121,88 +1167,48 @@ export interface SessionManagerAPI {
|
|
|
1121
1167
|
* E2E session as authenticated and unblocks MUTATE-tier commands. */
|
|
1122
1168
|
confirmSas: () => Promise<WebRemoteMutationResult>;
|
|
1123
1169
|
};
|
|
1124
|
-
|
|
1125
|
-
/**
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1170
|
+
chat: {
|
|
1171
|
+
/** Spawn a headless `claude -p` run for a tab. Results arrive via the on* listeners. */
|
|
1172
|
+
run: (payload: ChatRunPayload) => Promise<{ ok: boolean }>;
|
|
1173
|
+
/** Cancel the in-flight run for a tab (SIGTERM→SIGKILL). No-op when idle. */
|
|
1174
|
+
cancel: (tabId: string) => void;
|
|
1175
|
+
onQueued: (handler: (e: ChatRunQueuedEvent) => void) => () => void;
|
|
1176
|
+
onRunStarted: (handler: (e: ChatRunStartedEvent) => void) => () => void;
|
|
1177
|
+
onOutput: (handler: (e: ChatRunOutputEvent) => void) => () => void;
|
|
1178
|
+
onNeedsInput: (handler: (e: ChatRunNeedsInputEvent) => void) => () => void;
|
|
1179
|
+
onComplete: (handler: (e: ChatRunCompleteEvent) => void) => () => void;
|
|
1180
|
+
onError: (handler: (e: ChatRunErrorEvent) => void) => () => void;
|
|
1181
|
+
};
|
|
1182
|
+
exchanges: {
|
|
1183
|
+
/** Durable per-exchange log entries for a project, newest-first (max 100 by default). */
|
|
1184
|
+
list: (payload: {
|
|
1185
|
+
cwd: string;
|
|
1186
|
+
sessionId?: string;
|
|
1187
|
+
limit?: number;
|
|
1188
|
+
offset?: number;
|
|
1189
|
+
}) => Promise<Exchange[]>;
|
|
1141
1190
|
};
|
|
1142
1191
|
}
|
|
1143
1192
|
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
/** total - processed. */
|
|
1153
|
-
pending: number;
|
|
1154
|
-
nodes: number;
|
|
1155
|
-
edges: number;
|
|
1156
|
-
lastIngest: string | null;
|
|
1157
|
-
}
|
|
1158
|
-
|
|
1159
|
-
export interface KgNode {
|
|
1160
|
-
id: string;
|
|
1161
|
-
key: string;
|
|
1162
|
-
name: string;
|
|
1163
|
-
type: string;
|
|
1164
|
-
description: string;
|
|
1165
|
-
count: number;
|
|
1166
|
-
firstTs: string | null;
|
|
1167
|
-
lastTs: string | null;
|
|
1168
|
-
}
|
|
1169
|
-
export interface KgEdge {
|
|
1170
|
-
src: string;
|
|
1171
|
-
dst: string;
|
|
1172
|
-
relation: string;
|
|
1173
|
-
weight: number;
|
|
1174
|
-
lastTs: string | null;
|
|
1175
|
-
}
|
|
1176
|
-
export interface KgState {
|
|
1177
|
-
/** The project this graph belongs to. */
|
|
1193
|
+
// ────────────────────────────────────────────── Exchanges (PRD 324 read path)
|
|
1194
|
+
|
|
1195
|
+
export interface Exchange {
|
|
1196
|
+
/** ISO 8601 timestamp when the exchange was recorded. */
|
|
1197
|
+
ts: string;
|
|
1198
|
+
/** Claude session UUID. */
|
|
1199
|
+
sessionId: string;
|
|
1200
|
+
/** Project cwd. */
|
|
1178
1201
|
cwd: string;
|
|
1179
|
-
/**
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
logPath: string;
|
|
1190
|
-
extractionEnabled: boolean;
|
|
1191
|
-
/** Active capture mode: 'llm' | 'lite' | 'off'. */
|
|
1192
|
-
captureMode: 'llm' | 'lite' | 'off';
|
|
1193
|
-
/** Node cap from kg-config.json; 0 = disabled; default 300. */
|
|
1194
|
-
maxGraphNodes: number;
|
|
1195
|
-
};
|
|
1196
|
-
}
|
|
1197
|
-
export interface KgIngestProgress {
|
|
1198
|
-
phase: 'start' | 'extract' | 'batch' | 'done' | 'error';
|
|
1199
|
-
ingesting: boolean;
|
|
1200
|
-
batch?: number;
|
|
1201
|
-
totalBatches?: number;
|
|
1202
|
-
added?: number;
|
|
1203
|
-
/** Set on `batch`: the project cwd whose graph just committed. */
|
|
1204
|
-
cwd?: string;
|
|
1205
|
-
error?: string;
|
|
1202
|
+
/** The user prompt that started the exchange. */
|
|
1203
|
+
prompt: string;
|
|
1204
|
+
/** The verbatim assistant result. */
|
|
1205
|
+
result: string;
|
|
1206
|
+
/** Haiku-generated one-sentence summary. */
|
|
1207
|
+
summary: string;
|
|
1208
|
+
/** Haiku model used for the summary (informational). */
|
|
1209
|
+
model?: string;
|
|
1210
|
+
/** Set when summarization failed — `result` is available but `summary` may be empty. */
|
|
1211
|
+
degraded?: boolean;
|
|
1206
1212
|
}
|
|
1207
1213
|
|
|
1208
1214
|
declare global {
|
package/src/preload/index.cjs
CHANGED
|
@@ -9,11 +9,6 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
9
9
|
pickDirectory: () => ipcRenderer.invoke('app:pick-directory'),
|
|
10
10
|
gitBranch: (cwd) => ipcRenderer.invoke('app:git-branch', { cwd }),
|
|
11
11
|
rebootApp: () => ipcRenderer.send('app:reboot-app'),
|
|
12
|
-
openInEditor: (cwd, editor) => ipcRenderer.invoke('app:open-in-editor', { cwd, editor }),
|
|
13
|
-
openExternal: (url) => ipcRenderer.invoke('app:open-external', { url }),
|
|
14
|
-
openFileInEditor: (filePath, line, col, editor) => ipcRenderer.invoke('app:open-file-in-editor', { path: filePath, line, col, editor }),
|
|
15
|
-
openInFinder: (cwd) => ipcRenderer.invoke('app:open-in-finder', { cwd }),
|
|
16
|
-
openInTerminal: (cwd) => ipcRenderer.invoke('app:open-in-terminal', { cwd }),
|
|
17
12
|
archiveProject: (encoded) => ipcRenderer.invoke('app:archive-project', { encoded }),
|
|
18
13
|
testFireHook: (args) => ipcRenderer.invoke('app:test-fire-hook', args),
|
|
19
14
|
// F7: lets the renderer suppress the wizard auto-trigger under SM_E2E=1.
|
|
@@ -121,7 +116,6 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
121
116
|
},
|
|
122
117
|
getHotkeyConfig: () => ipcRenderer.invoke('voice:get-hotkey-config'),
|
|
123
118
|
setHotkeyConfig: (cfg) => ipcRenderer.invoke('voice:set-hotkey', cfg),
|
|
124
|
-
getHotkeyConfigPath: () => ipcRenderer.invoke('voice:get-hotkey-config-path'),
|
|
125
119
|
setRecording: (recording) => ipcRenderer.send('voice:set-recording', !!recording),
|
|
126
120
|
// F5 device picker prefs (~/.config/session-manager/voice.json `device` key).
|
|
127
121
|
getDevicePref: () => ipcRenderer.invoke('voice:get-device-pref'),
|
|
@@ -158,7 +152,6 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
158
152
|
},
|
|
159
153
|
history: {
|
|
160
154
|
aggregate: (req) => ipcRenderer.invoke('history:aggregate', req),
|
|
161
|
-
listConversations: () => ipcRenderer.invoke('history:list-conversations'),
|
|
162
155
|
scanProjects: () => ipcRenderer.invoke('history:scan-projects'),
|
|
163
156
|
},
|
|
164
157
|
files: {
|
|
@@ -168,8 +161,11 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
168
161
|
create: (parentPath, name, kind) => ipcRenderer.invoke('files:create', { parentPath, name, kind }),
|
|
169
162
|
rename: (path, newName) => ipcRenderer.invoke('files:rename', { path, newName }),
|
|
170
163
|
delete: (path) => ipcRenderer.invoke('files:delete', { path }),
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
},
|
|
165
|
+
// Consolidated shell open/reveal — see shell:open in index.cjs.
|
|
166
|
+
// as: 'editor' | 'fileInEditor' | 'finder' | 'terminal' | 'external' | 'openPath' | 'revealPath'
|
|
167
|
+
shell: {
|
|
168
|
+
open: (opts) => ipcRenderer.invoke('shell:open', opts),
|
|
173
169
|
},
|
|
174
170
|
search: {
|
|
175
171
|
files: (cwd, query, opts) => ipcRenderer.invoke('search:files', { cwd, query, opts }),
|
|
@@ -191,7 +187,6 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
191
187
|
runNow: () => ipcRenderer.invoke('schedule:run-now'),
|
|
192
188
|
forceTick: () => ipcRenderer.invoke('schedule:force-tick'),
|
|
193
189
|
resume: () => ipcRenderer.invoke('schedule:resume'),
|
|
194
|
-
refreshReset: () => ipcRenderer.invoke('schedule:refresh-reset'),
|
|
195
190
|
rescan: () => ipcRenderer.invoke('schedule:rescan'),
|
|
196
191
|
clearQueue: () => ipcRenderer.invoke('schedule:clear-queue'),
|
|
197
192
|
openFolder: () => ipcRenderer.invoke('schedule:open-folder'),
|
|
@@ -244,6 +239,8 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
244
239
|
if (workspace) payload.workspace = workspace;
|
|
245
240
|
return ipcRenderer.invoke('memory:create', payload);
|
|
246
241
|
},
|
|
242
|
+
aggregate: (workspace, refresh) =>
|
|
243
|
+
ipcRenderer.invoke('memory:aggregate', refresh ? { workspace, refresh: true } : { workspace }),
|
|
247
244
|
},
|
|
248
245
|
agentMemory: {
|
|
249
246
|
list: (agentId) => ipcRenderer.invoke('agent-memory:list', { agentId }),
|
|
@@ -254,7 +251,6 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
254
251
|
return ipcRenderer.invoke('agent-memory:set', payload);
|
|
255
252
|
},
|
|
256
253
|
delete: (agentId, entryId) => ipcRenderer.invoke('agent-memory:delete', { agentId, entryId }),
|
|
257
|
-
listAgents: () => ipcRenderer.invoke('agent-memory:list-agents'),
|
|
258
254
|
},
|
|
259
255
|
docEditor: {
|
|
260
256
|
pickFile: (payload) => ipcRenderer.invoke('doc-editor:pick-file', payload),
|
|
@@ -320,25 +316,45 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
320
316
|
return () => ipcRenderer.removeListener('webRemote:revoked-all', listener);
|
|
321
317
|
},
|
|
322
318
|
},
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
319
|
+
chat: {
|
|
320
|
+
/** Spawn a headless claude -p job. Results arrive via the on* listeners. */
|
|
321
|
+
run: (payload) => ipcRenderer.invoke('chat:run', payload),
|
|
322
|
+
/** Cancel an in-flight run for the given tabId. No-op if none running. */
|
|
323
|
+
cancel: (tabId) => ipcRenderer.send('chat:cancel', { tabId }),
|
|
324
|
+
onQueued: (handler) => {
|
|
325
|
+
const listener = (_e, payload) => handler(payload);
|
|
326
|
+
ipcRenderer.on('chat:run:queued', listener);
|
|
327
|
+
return () => ipcRenderer.removeListener('chat:run:queued', listener);
|
|
328
|
+
},
|
|
329
|
+
onRunStarted: (handler) => {
|
|
330
|
+
const listener = (_e, payload) => handler(payload);
|
|
331
|
+
ipcRenderer.on('chat:run:started', listener);
|
|
332
|
+
return () => ipcRenderer.removeListener('chat:run:started', listener);
|
|
333
|
+
},
|
|
334
|
+
onOutput: (handler) => {
|
|
335
|
+
const listener = (_e, payload) => handler(payload);
|
|
336
|
+
ipcRenderer.on('chat:run:output', listener);
|
|
337
|
+
return () => ipcRenderer.removeListener('chat:run:output', listener);
|
|
338
|
+
},
|
|
339
|
+
onNeedsInput: (handler) => {
|
|
340
|
+
const listener = (_e, payload) => handler(payload);
|
|
341
|
+
ipcRenderer.on('chat:run:needs-input', listener);
|
|
342
|
+
return () => ipcRenderer.removeListener('chat:run:needs-input', listener);
|
|
343
|
+
},
|
|
344
|
+
onComplete: (handler) => {
|
|
339
345
|
const listener = (_e, payload) => handler(payload);
|
|
340
|
-
ipcRenderer.on('
|
|
341
|
-
return () => ipcRenderer.removeListener('
|
|
346
|
+
ipcRenderer.on('chat:run:complete', listener);
|
|
347
|
+
return () => ipcRenderer.removeListener('chat:run:complete', listener);
|
|
342
348
|
},
|
|
349
|
+
onError: (handler) => {
|
|
350
|
+
const listener = (_e, payload) => handler(payload);
|
|
351
|
+
ipcRenderer.on('chat:run:error', listener);
|
|
352
|
+
return () => ipcRenderer.removeListener('chat:run:error', listener);
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
exchanges: {
|
|
356
|
+
/** List exchanges for a project (durable chat-run log), newest-first.
|
|
357
|
+
* `sessionId` filters to one session; `limit`/`offset` for pagination. */
|
|
358
|
+
list: (payload) => ipcRenderer.invoke('exchanges:list', payload),
|
|
343
359
|
},
|
|
344
360
|
});
|