claude-code-session-manager 0.33.0 → 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-DHzFmRr2.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/usage.cjs +58 -11
- 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-AKeGl-VM.css +0 -32
- package/dist/assets/index-Bwwbc2mS.js +0 -3535
- 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/usage.cjs
CHANGED
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* }
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
+
const fs = require('node:fs');
|
|
19
20
|
const fsp = require('node:fs/promises');
|
|
20
21
|
const path = require('node:path');
|
|
21
22
|
const os = require('node:os');
|
|
@@ -30,28 +31,74 @@ function envEnabled(v) {
|
|
|
30
31
|
return v != null && v !== '' && v !== '0' && String(v).toLowerCase() !== 'false';
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
* Reads enterprise-auth signals from Claude Code's own settings files — the
|
|
36
|
+
* `env` block and `apiKeyHelper`. This is where corporate gateways are usually
|
|
37
|
+
* configured (a managed/enterprise policy or the user's settings.json), NOT as
|
|
38
|
+
* exported shell vars. It matters most on macOS, where a GUI-launched app does
|
|
39
|
+
* not inherit the shell's environment, so `process.env` looks like a clean
|
|
40
|
+
* consumer install even when `claude` itself is talking to a gateway.
|
|
41
|
+
*
|
|
42
|
+
* Precedence mirrors Claude Code: managed (enterprise) settings, then user
|
|
43
|
+
* settings, then user-local settings. Missing/unreadable/invalid files are
|
|
44
|
+
* skipped. Returns `{ env, apiKeyHelper }`.
|
|
45
|
+
*/
|
|
46
|
+
function readClaudeSettingsAuth() {
|
|
47
|
+
const merged = {};
|
|
48
|
+
let apiKeyHelper = false;
|
|
49
|
+
const managed = process.platform === 'darwin'
|
|
50
|
+
? '/Library/Application Support/ClaudeCode/managed-settings.json'
|
|
51
|
+
: '/etc/claude-code/managed-settings.json';
|
|
52
|
+
const files = [
|
|
53
|
+
managed,
|
|
54
|
+
path.join(os.homedir(), '.claude', 'settings.json'),
|
|
55
|
+
path.join(os.homedir(), '.claude', 'settings.local.json'),
|
|
56
|
+
];
|
|
57
|
+
for (const f of files) {
|
|
58
|
+
try {
|
|
59
|
+
const data = JSON.parse(fs.readFileSync(f, 'utf8'));
|
|
60
|
+
if (data && typeof data.env === 'object' && data.env) Object.assign(merged, data.env);
|
|
61
|
+
if (data && data.apiKeyHelper) apiKeyHelper = true;
|
|
62
|
+
} catch { /* missing / unreadable / invalid JSON → skip */ }
|
|
63
|
+
}
|
|
64
|
+
return { env: merged, apiKeyHelper };
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
/**
|
|
34
68
|
* Is the consumer 5-hour usage meter (/api/oauth/usage) even applicable here?
|
|
35
69
|
*
|
|
36
70
|
* That endpoint only exists for OAuth/subscription auth against
|
|
37
71
|
* api.anthropic.com. Enterprise auth modes have no such meter, so polling it
|
|
38
|
-
* just 404s/times-out
|
|
39
|
-
* Detected modes: Amazon Bedrock, Google
|
|
40
|
-
*
|
|
72
|
+
* just 404s/times-out (or 401s with no credentials file) — and the scheduler
|
|
73
|
+
* must NOT gate on (or pause for) it. Detected modes: Amazon Bedrock, Google
|
|
74
|
+
* Vertex, raw API-key, a custom auth token, a non-Anthropic base URL (corporate
|
|
75
|
+
* gateway/proxy), or an `apiKeyHelper` script.
|
|
76
|
+
*
|
|
77
|
+
* Signals are read from BOTH process.env and Claude Code's settings files, so a
|
|
78
|
+
* gateway configured purely in settings.json (the common enterprise case) is
|
|
79
|
+
* detected even when the GUI process inherited a clean environment.
|
|
41
80
|
*
|
|
42
81
|
* Returns false → caller should treat usage as unavailable-by-design and fire
|
|
43
82
|
* work on its own (pending + memory) instead of waiting on a meter.
|
|
44
83
|
*/
|
|
45
|
-
function usageMeterApplicable(env = process.env) {
|
|
46
|
-
if
|
|
47
|
-
|
|
48
|
-
if (env.
|
|
49
|
-
|
|
50
|
-
if (
|
|
84
|
+
function usageMeterApplicable(env = process.env, settings = readClaudeSettingsAuth()) {
|
|
85
|
+
// Manual escape hatch: if detection misses an unusual gateway setup, the user
|
|
86
|
+
// can force "no consumer meter" so the scheduler stops pausing on 'auth'.
|
|
87
|
+
if (envEnabled(env.SM_NO_USAGE_METER)) return false;
|
|
88
|
+
// An apiKeyHelper means `claude` mints its own key → non-OAuth, no meter.
|
|
89
|
+
if (settings && settings.apiKeyHelper) return false;
|
|
90
|
+
// Effective env: settings.json provides values the GUI process didn't
|
|
91
|
+
// inherit; a real process.env var wins when both define the same key.
|
|
92
|
+
const eff = { ...(settings && settings.env), ...env };
|
|
93
|
+
if (envEnabled(eff.CLAUDE_CODE_USE_BEDROCK)) return false;
|
|
94
|
+
if (envEnabled(eff.CLAUDE_CODE_USE_VERTEX)) return false;
|
|
95
|
+
if (eff.ANTHROPIC_API_KEY) return false;
|
|
96
|
+
if (eff.ANTHROPIC_AUTH_TOKEN) return false;
|
|
97
|
+
if (eff.ANTHROPIC_BASE_URL) {
|
|
51
98
|
// Parse the host rather than substring-match, so a deceptive gateway like
|
|
52
99
|
// https://anthropic.com.attacker.example is correctly treated as enterprise.
|
|
53
100
|
let host;
|
|
54
|
-
try { host = new URL(
|
|
101
|
+
try { host = new URL(eff.ANTHROPIC_BASE_URL).hostname.toLowerCase(); }
|
|
55
102
|
catch { return false; } // unparseable custom URL → treat as a gateway
|
|
56
103
|
if (host !== 'anthropic.com' && !host.endsWith('.anthropic.com')) return false;
|
|
57
104
|
}
|
|
@@ -197,4 +244,4 @@ function registerBillingHandlers() {
|
|
|
197
244
|
});
|
|
198
245
|
}
|
|
199
246
|
|
|
200
|
-
module.exports = { registerBillingHandlers, fetchUsage, classifyUsageResponse, usageMeterApplicable };
|
|
247
|
+
module.exports = { registerBillingHandlers, fetchUsage, classifyUsageResponse, usageMeterApplicable, readClaudeSettingsAuth };
|
package/src/main/voiceHotkey.cjs
CHANGED
|
@@ -313,8 +313,6 @@ function registerHotkeyHandlers() {
|
|
|
313
313
|
return { ok: true, config: currentConfig };
|
|
314
314
|
}));
|
|
315
315
|
|
|
316
|
-
ipcMain.handle('voice:get-hotkey-config-path', () => voiceSettings.storePath());
|
|
317
|
-
|
|
318
316
|
// F5 — device picker preference (additive subtree on voice.json).
|
|
319
317
|
ipcMain.handle('voice:get-device-pref', async () => {
|
|
320
318
|
return await voiceSettings.loadDevice();
|
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 {
|