claude-code-session-manager 0.35.3 → 0.35.4
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-DmCekkUQ.js → TiptapBody-CVFG9ufh.js} +1 -1
- package/dist/assets/index-DASEOqMP.js +3558 -0
- package/dist/assets/index-DZgKJkf3.css +32 -0
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/plugins/session-manager-dev/skills/develop/standards.md +1 -0
- package/plugins/session-manager-dev/skills/explain-to-me/SKILL.md +48 -35
- package/plugins/session-manager-dev/skills/my-feedback/SKILL.md +20 -16
- package/plugins/session-manager-dev/skills/optimize-kpi/SKILL.md +4 -4
- package/plugins/session-manager-dev/skills/process-feedback/SKILL.md +32 -16
- package/src/main/__tests__/chat-mcp-consent-notice.test.cjs +137 -0
- package/src/main/__tests__/mcpStatus.test.cjs +61 -0
- package/src/main/__tests__/scheduler-autofix-select.test.cjs +50 -2
- package/src/main/__tests__/scheduler-autopromote.test.cjs +19 -1
- package/src/main/chatRunner.cjs +60 -0
- package/src/main/index.cjs +5 -0
- package/src/main/mcpStatus.cjs +93 -0
- package/src/main/scheduler.cjs +153 -30
- package/src/preload/api.d.ts +25 -0
- package/src/preload/index.cjs +8 -0
- package/dist/assets/index-CHKMzzCM.js +0 -3558
- package/dist/assets/index-DVqmrWP3.css +0 -32
package/src/main/scheduler.cjs
CHANGED
|
@@ -1151,6 +1151,19 @@ function isPromotableOriginal(status) {
|
|
|
1151
1151
|
return status === 'failed' || status === 'needs_review';
|
|
1152
1152
|
}
|
|
1153
1153
|
|
|
1154
|
+
/**
|
|
1155
|
+
* Pure helper: given a completed fix-plan slug (e.g. '451-fix-foo'), find the
|
|
1156
|
+
* SPECIFIC promotable original job it heals. Matches on the full
|
|
1157
|
+
* numeric-prefixed slug ('451-foo'), not just the base ('foo') — two
|
|
1158
|
+
* needs_review jobs can share a base slug with different NN prefixes
|
|
1159
|
+
* (e.g. '451-foo' and '453-foo'), and a fix plan must only heal the
|
|
1160
|
+
* lineage whose NN it was authored against. Exported for tests.
|
|
1161
|
+
*/
|
|
1162
|
+
function healTargetForFix(fixSlug, jobs) {
|
|
1163
|
+
const originalSlug = fixSlug.replace(/^(\d+)-fix-/, '$1-');
|
|
1164
|
+
return jobs.find((x) => x.slug === originalSlug && isPromotableOriginal(x.status)) || null;
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1154
1167
|
/**
|
|
1155
1168
|
* Spawn an Opus investigation session for a failed job. The investigator's job
|
|
1156
1169
|
* is to read the failure log + original PRD, identify the root cause, and write
|
|
@@ -1329,6 +1342,13 @@ DO NOT attempt the fix. ONLY write the file. When the file exists, exit immediat
|
|
|
1329
1342
|
console.log(`[scheduler] investigation produced fix plan: ${fixSlug}`);
|
|
1330
1343
|
} else {
|
|
1331
1344
|
console.log(`[scheduler] investigation finished WITHOUT producing fix plan (slug=${failedJob.slug}, code=${exitCode})`);
|
|
1345
|
+
// Record the no-plan outcome so selectAutoFixTargets can offer one
|
|
1346
|
+
// bounded retry instead of permanently dead-ending behind the
|
|
1347
|
+
// 1-attempt cap (autoFixAttempted stays true).
|
|
1348
|
+
mutate((s) => {
|
|
1349
|
+
const j = s.jobs.find((x) => x.slug === failedJob.slug);
|
|
1350
|
+
if (j) j.autoFixOutcome = 'no-plan';
|
|
1351
|
+
}).catch(() => {});
|
|
1332
1352
|
}
|
|
1333
1353
|
// Trigger a tick so the new fix plan is reconciled into the queue and fired.
|
|
1334
1354
|
tickQueue().catch(() => {});
|
|
@@ -1525,17 +1545,16 @@ async function spawnJob(job, runId, runDir, defaultCwd) {
|
|
|
1525
1545
|
// this, the queue stalls indefinitely behind a stale failure even
|
|
1526
1546
|
// though the auto-recovery did its job.
|
|
1527
1547
|
if (effectiveStatus === 'completed' && isFixPlanSlug(job.slug)) {
|
|
1528
|
-
const
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
s.jobs[orig].completedBy = job.slug;
|
|
1548
|
+
const orig = healTargetForFix(job.slug, s.jobs);
|
|
1549
|
+
if (orig) {
|
|
1550
|
+
const priorStatus = orig.status;
|
|
1551
|
+
console.log(`[scheduler] auto-promote: ${orig.slug} (${priorStatus}) → completed because ${job.slug} succeeded`);
|
|
1552
|
+
orig.status = 'completed';
|
|
1553
|
+
orig.exitCode = 0;
|
|
1554
|
+
orig.error = null;
|
|
1555
|
+
orig.completedBy = job.slug;
|
|
1537
1556
|
if (priorStatus === 'needs_review') {
|
|
1538
|
-
delete
|
|
1557
|
+
delete orig.verifierVerdict;
|
|
1539
1558
|
}
|
|
1540
1559
|
}
|
|
1541
1560
|
}
|
|
@@ -1881,16 +1900,57 @@ function selectHistoryJobs(jobs, limit) {
|
|
|
1881
1900
|
// heal a genuinely-unfinished job.
|
|
1882
1901
|
const RESCANNABLE_VERDICTS = new Set(['transcript_errors', 'verify_unavailable']);
|
|
1883
1902
|
|
|
1903
|
+
/**
|
|
1904
|
+
* Backfill a job's missing runId by scanning RUNS_DIR for a run directory
|
|
1905
|
+
* whose contents reference this job's slug (a '<slug>.log' file inside it).
|
|
1906
|
+
* A needs_review job can lose its runId (e.g. an old queue-schema gap) and
|
|
1907
|
+
* become invisible to every auto-resolution path that requires job.runId
|
|
1908
|
+
* truthy. O(number of run dirs) — a single readdir + per-dir existsSync
|
|
1909
|
+
* check, no nested loop over user-scaled data. Dir names are ISO timestamps,
|
|
1910
|
+
* so lexical-descending sort picks the newest match. Exported for tests.
|
|
1911
|
+
*/
|
|
1912
|
+
function resolveRunId(job, { runsDir = RUNS_DIR } = {}) {
|
|
1913
|
+
if (!job || job.runId) return job?.runId || null;
|
|
1914
|
+
if (!job.slug) return null;
|
|
1915
|
+
let dirs;
|
|
1916
|
+
try {
|
|
1917
|
+
dirs = fs.readdirSync(runsDir);
|
|
1918
|
+
} catch {
|
|
1919
|
+
return null;
|
|
1920
|
+
}
|
|
1921
|
+
const matches = dirs.filter((d) => {
|
|
1922
|
+
try {
|
|
1923
|
+
return fs.existsSync(path.join(runsDir, d, `${job.slug}.log`));
|
|
1924
|
+
} catch {
|
|
1925
|
+
return false;
|
|
1926
|
+
}
|
|
1927
|
+
});
|
|
1928
|
+
if (!matches.length) return null;
|
|
1929
|
+
matches.sort().reverse();
|
|
1930
|
+
return matches[0];
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
/**
|
|
1934
|
+
* Pure predicate: a needs_review job with no runId and no backfillable run
|
|
1935
|
+
* directory can never self-heal or get an auto-fix investigation — it must
|
|
1936
|
+
* be surfaced for a human rather than silently dead-ended. Exported for
|
|
1937
|
+
* tests.
|
|
1938
|
+
*/
|
|
1939
|
+
function isUnresolvableNeedsReview(job, { hasRunDir }) {
|
|
1940
|
+
return !!job && job.status === 'needs_review' && !job.runId && !hasRunDir;
|
|
1941
|
+
}
|
|
1942
|
+
|
|
1884
1943
|
/**
|
|
1885
1944
|
* Pure predicate: is this job eligible for the boot re-verify self-heal? Only
|
|
1886
|
-
* needs_review jobs with a run log
|
|
1887
|
-
* EXCLUDES 'uncommitted_changes' (git
|
|
1888
|
-
* so re-scanning it would falsely
|
|
1945
|
+
* needs_review jobs with a run log (own or backfilled via resolveRunId) AND a
|
|
1946
|
+
* transcript-scan verdict. Crucially EXCLUDES 'uncommitted_changes' (git
|
|
1947
|
+
* commit-guard) — verifyRun can't see git, so re-scanning it would falsely
|
|
1948
|
+
* heal an unfinished job. Exported for tests.
|
|
1889
1949
|
*/
|
|
1890
1950
|
function isRescanCandidate(job) {
|
|
1891
1951
|
return !!job
|
|
1892
1952
|
&& job.status === 'needs_review'
|
|
1893
|
-
&& !!job.runId
|
|
1953
|
+
&& !!(job.runId || resolveRunId(job))
|
|
1894
1954
|
&& RESCANNABLE_VERDICTS.has(job.verifierVerdict);
|
|
1895
1955
|
}
|
|
1896
1956
|
|
|
@@ -1906,23 +1966,32 @@ function isRescanCandidate(job) {
|
|
|
1906
1966
|
* @returns {Promise<{rescanned:number, healed:string[]}>}
|
|
1907
1967
|
*/
|
|
1908
1968
|
/**
|
|
1909
|
-
* Pure helper — no I/O
|
|
1910
|
-
*
|
|
1969
|
+
* Pure helper — no I/O by default (resolveJobRunId is injectable; production
|
|
1970
|
+
* caller passes resolveRunId, which does touch disk). Returns the subset of
|
|
1971
|
+
* jobs eligible for automatic fix-plan authoring after a reverify pass
|
|
1972
|
+
* leaves them still in needs_review.
|
|
1911
1973
|
*
|
|
1912
1974
|
* Exclusion rules (all must pass):
|
|
1913
1975
|
* - status === 'needs_review'
|
|
1914
|
-
* -
|
|
1915
|
-
* - autoFixAttempted !== true (1-attempt cap)
|
|
1976
|
+
* - a runId, own or backfilled via resolveJobRunId (need a run log to investigate)
|
|
1916
1977
|
* - not itself a fix-plan slug (avoids infinite recursion)
|
|
1978
|
+
* - autoFixAttempted cap: a job with no prior attempt is eligible; a job
|
|
1979
|
+
* whose prior attempt outcome was 'no-plan' gets ONE bounded retry
|
|
1980
|
+
* (autoFixRetries < 1); any other attempted job (or an exhausted
|
|
1981
|
+
* no-plan retry) is excluded
|
|
1917
1982
|
* - no fix sibling on disk (fixSlugExists) or already in the queue
|
|
1918
1983
|
*/
|
|
1919
|
-
function selectAutoFixTargets(jobs, { fixSlugExists }) {
|
|
1984
|
+
function selectAutoFixTargets(jobs, { fixSlugExists, resolveJobRunId = resolveRunId }) {
|
|
1920
1985
|
const slugsInQueue = new Set(jobs.map((j) => j.slug));
|
|
1921
1986
|
return jobs.filter((job) => {
|
|
1922
1987
|
if (job.status !== 'needs_review') return false;
|
|
1923
|
-
|
|
1924
|
-
if (
|
|
1988
|
+
const runId = job.runId || resolveJobRunId(job);
|
|
1989
|
+
if (!runId) return false;
|
|
1925
1990
|
if (isFixPlanSlug(job.slug)) return false;
|
|
1991
|
+
if (job.autoFixAttempted) {
|
|
1992
|
+
if (job.autoFixOutcome !== 'no-plan') return false;
|
|
1993
|
+
if ((job.autoFixRetries ?? 0) >= 1) return false;
|
|
1994
|
+
}
|
|
1926
1995
|
const fixSlug = `${String(job.parallelGroup ?? 99).padStart(2, '0')}-fix-${job.slug.replace(/^\d+-/, '')}`;
|
|
1927
1996
|
if (fixSlugExists(fixSlug)) return false;
|
|
1928
1997
|
if (slugsInQueue.has(fixSlug)) return false;
|
|
@@ -1936,7 +2005,7 @@ async function reverifyNeedsReview() {
|
|
|
1936
2005
|
const healed = [];
|
|
1937
2006
|
const leftForReview = [];
|
|
1938
2007
|
for (const job of candidates) {
|
|
1939
|
-
const runDir = path.join(RUNS_DIR, job.runId);
|
|
2008
|
+
const runDir = path.join(RUNS_DIR, job.runId || resolveRunId(job));
|
|
1940
2009
|
const prdPath = path.join(PRDS_DIR, `${job.slug}.md`);
|
|
1941
2010
|
// Derive committedDuringRun from the recorded run window. The live
|
|
1942
2011
|
// commit-guard uses gitHead() (before/after HEAD diff); here the run is
|
|
@@ -1978,24 +2047,72 @@ async function reverifyNeedsReview() {
|
|
|
1978
2047
|
console.log(`[scheduler] boot reverify: left for review: ${detail}`);
|
|
1979
2048
|
}
|
|
1980
2049
|
|
|
2050
|
+
// Surface needs_review jobs that can never self-heal or get an auto-fix
|
|
2051
|
+
// investigation (no runId, no backfillable run dir) instead of leaving
|
|
2052
|
+
// them silently stranded. Also surface no-plan auto-fix jobs whose one
|
|
2053
|
+
// bounded retry is already exhausted. Both are annotated, never looped on.
|
|
2054
|
+
const afterHealForAnnotate = await readQueue();
|
|
2055
|
+
const unresolvable = afterHealForAnnotate.jobs.filter(
|
|
2056
|
+
(j) => isUnresolvableNeedsReview(j, { hasRunDir: !!resolveRunId(j) }) && j.verifierVerdict !== 'no_run_artifacts',
|
|
2057
|
+
);
|
|
2058
|
+
const exhaustedNoPlan = afterHealForAnnotate.jobs.filter(
|
|
2059
|
+
(j) => j.status === 'needs_review'
|
|
2060
|
+
&& j.autoFixAttempted && j.autoFixOutcome === 'no-plan' && (j.autoFixRetries ?? 0) >= 1
|
|
2061
|
+
&& j.verifierVerdict !== 'autofix_no_plan',
|
|
2062
|
+
);
|
|
2063
|
+
if (unresolvable.length || exhaustedNoPlan.length) {
|
|
2064
|
+
const unresolvableSet = new Set(unresolvable.map((j) => j.slug));
|
|
2065
|
+
const exhaustedSet = new Set(exhaustedNoPlan.map((j) => j.slug));
|
|
2066
|
+
await mutate((s) => {
|
|
2067
|
+
for (const j of s.jobs) {
|
|
2068
|
+
if (unresolvableSet.has(j.slug)) {
|
|
2069
|
+
j.verifierVerdict = 'no_run_artifacts';
|
|
2070
|
+
j.error = 'no run artifacts — manual review';
|
|
2071
|
+
} else if (exhaustedSet.has(j.slug)) {
|
|
2072
|
+
j.verifierVerdict = 'autofix_no_plan';
|
|
2073
|
+
j.error = 'auto-fix investigation produced no fix plan after retry — manual review';
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
});
|
|
2077
|
+
if (unresolvable.length) {
|
|
2078
|
+
console.log(`[scheduler] boot reverify: no run artifacts for ${unresolvable.map((j) => j.slug).join(', ')} — flagged for manual review`);
|
|
2079
|
+
}
|
|
2080
|
+
if (exhaustedNoPlan.length) {
|
|
2081
|
+
console.log(`[scheduler] boot reverify: auto-fix retry exhausted for ${exhaustedNoPlan.map((j) => j.slug).join(', ')} — flagged for manual review`);
|
|
2082
|
+
}
|
|
2083
|
+
await broadcast();
|
|
2084
|
+
}
|
|
2085
|
+
|
|
1981
2086
|
// Auto-fix: spawn a fix-plan investigation for each job still in
|
|
1982
2087
|
// needs_review after the heal pass (kill-switch: SM_AUTOFIX_DISABLE=1).
|
|
2088
|
+
// spawnInvestigation early-returns once investigationsInFlight reaches
|
|
2089
|
+
// MAX_CONCURRENT_INVESTIGATIONS (queues the rest for retry), so this loop
|
|
2090
|
+
// cannot fan out past the cap regardless of how many targets are selected.
|
|
1983
2091
|
if (process.env.SM_AUTOFIX_DISABLE !== '1') {
|
|
1984
2092
|
const afterHeal = await readQueue();
|
|
1985
2093
|
const targets = selectAutoFixTargets(afterHeal.jobs, {
|
|
1986
2094
|
fixSlugExists: (s) => fs.existsSync(path.join(PRDS_DIR, `${s}.md`)),
|
|
1987
2095
|
});
|
|
1988
2096
|
for (const job of targets) {
|
|
1989
|
-
const
|
|
2097
|
+
const runId = job.runId || resolveRunId(job);
|
|
2098
|
+
const runDir = path.join(RUNS_DIR, runId);
|
|
2099
|
+
const isNoPlanRetry = job.autoFixAttempted && job.autoFixOutcome === 'no-plan';
|
|
1990
2100
|
// Persist the attempt BEFORE spawning — a crash mid-investigation still
|
|
1991
2101
|
// counts it (mirrors orphanRetries). Safe even when the slot is busy: the
|
|
1992
2102
|
// investigation is queued and drained as slots free, so it is genuinely
|
|
1993
2103
|
// attempted rather than silently dropped.
|
|
1994
2104
|
await mutate((s) => {
|
|
1995
2105
|
const j = s.jobs.find((x) => x.slug === job.slug);
|
|
1996
|
-
if (j)
|
|
2106
|
+
if (j) {
|
|
2107
|
+
j.autoFixAttempted = true;
|
|
2108
|
+
if (!j.runId && runId) j.runId = runId;
|
|
2109
|
+
if (isNoPlanRetry) {
|
|
2110
|
+
j.autoFixRetries = (j.autoFixRetries ?? 0) + 1;
|
|
2111
|
+
delete j.autoFixOutcome;
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
1997
2114
|
});
|
|
1998
|
-
console.log(`[scheduler] auto-fix: needs_review ${job.slug} → authoring fix-plan (1/1)`);
|
|
2115
|
+
console.log(`[scheduler] auto-fix: needs_review ${job.slug} → authoring fix-plan (${isNoPlanRetry ? 'retry' : '1/1'})`);
|
|
1999
2116
|
spawnInvestigation(job, runDir).catch((e) => {
|
|
2000
2117
|
console.error('[scheduler] auto-fix spawnInvestigation error', job.slug, e);
|
|
2001
2118
|
});
|
|
@@ -2335,10 +2452,16 @@ async function init() {
|
|
|
2335
2452
|
// Periodic self-heal: re-run the verifier over stale needs_review jobs so a
|
|
2336
2453
|
// job whose work actually landed (committed in-window, no FAIL sentinel)
|
|
2337
2454
|
// auto-clears WITHOUT waiting for the next app restart. Cheap-guarded — the
|
|
2338
|
-
// log scan only runs when something is actually flagged.
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2455
|
+
// log scan only runs when something is actually flagged. Kill-switch:
|
|
2456
|
+
// SM_REVERIFY_PERIODIC_DISABLE=1 (boot reverify above stays always-on).
|
|
2457
|
+
// reverifyNeedsReview's auto-fix loop is capped downstream by
|
|
2458
|
+
// MAX_CONCURRENT_INVESTIGATIONS (spawnInvestigation queues/early-returns
|
|
2459
|
+
// past it), so this interval firing cannot fan out investigations.
|
|
2460
|
+
if (process.env.SM_REVERIFY_PERIODIC_DISABLE !== '1') {
|
|
2461
|
+
const s = readQueueSync();
|
|
2462
|
+
if (s.jobs.some((j) => j.status === 'needs_review')) {
|
|
2463
|
+
reverifyNeedsReview().catch(() => {});
|
|
2464
|
+
}
|
|
2342
2465
|
}
|
|
2343
2466
|
}, 10 * 60_000);
|
|
2344
2467
|
|
|
@@ -2510,4 +2633,4 @@ const remote = {
|
|
|
2510
2633
|
},
|
|
2511
2634
|
};
|
|
2512
2635
|
|
|
2513
|
-
module.exports = { registerScheduleHandlers, attachWindow, init, ROOT, PRDS_DIR, selectHistoryJobs, parsePorcelain, FINISH_PROTOCOL, remote, pickNextBatch, pickForProject, reapDeadRunningJobs, pollRecoveryClearSource, memoryLimitedBatchSize, availableForJobs, reverifyNeedsReview, isRescanCandidate, isPromotableOriginal, selectAutoFixTargets };
|
|
2636
|
+
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 };
|
package/src/preload/api.d.ts
CHANGED
|
@@ -198,6 +198,21 @@ export type BillingFetchResult =
|
|
|
198
198
|
| { kind: 'transient'; message: string; httpStatus: number | null }
|
|
199
199
|
| { kind: 'config'; message: string };
|
|
200
200
|
|
|
201
|
+
// ── MCP server live connection probe (`claude mcp list`)
|
|
202
|
+
export interface McpServerStatus {
|
|
203
|
+
name: string;
|
|
204
|
+
target: string;
|
|
205
|
+
transport: 'stdio' | 'http' | 'sse' | 'ws' | 'unknown';
|
|
206
|
+
status: 'connected' | 'failed' | 'needs-auth' | 'pending' | 'unknown';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export interface McpStatusResult {
|
|
210
|
+
ok: boolean;
|
|
211
|
+
servers: McpServerStatus[];
|
|
212
|
+
error?: string;
|
|
213
|
+
checkedAt: number;
|
|
214
|
+
}
|
|
215
|
+
|
|
201
216
|
// ── Usage matrix (AgOps dashboard, main-side aggregator)
|
|
202
217
|
export type UsageMatrixState = 'idle' | 'executing' | 'plan' | 'background';
|
|
203
218
|
export type UsageMatrixIntensity = 'idle' | 'low' | 'medium' | 'critical';
|
|
@@ -978,6 +993,12 @@ export interface ChatRunErrorEvent {
|
|
|
978
993
|
message: string;
|
|
979
994
|
}
|
|
980
995
|
|
|
996
|
+
export interface ChatRunNoticeEvent {
|
|
997
|
+
tabId: string;
|
|
998
|
+
sessionId: string;
|
|
999
|
+
message: string;
|
|
1000
|
+
}
|
|
1001
|
+
|
|
981
1002
|
export interface SessionManagerAPI {
|
|
982
1003
|
app: {
|
|
983
1004
|
version: () => Promise<string>;
|
|
@@ -1076,6 +1097,9 @@ export interface SessionManagerAPI {
|
|
|
1076
1097
|
billing: {
|
|
1077
1098
|
fetch: () => Promise<BillingFetchResult>;
|
|
1078
1099
|
};
|
|
1100
|
+
mcp: {
|
|
1101
|
+
status: () => Promise<McpStatusResult>;
|
|
1102
|
+
};
|
|
1079
1103
|
usageMatrix: {
|
|
1080
1104
|
snapshot: () => Promise<UsageMatrixSnapshot>;
|
|
1081
1105
|
onTick: (handler: (snap: UsageMatrixSnapshot) => void) => () => void;
|
|
@@ -1311,6 +1335,7 @@ export interface SessionManagerAPI {
|
|
|
1311
1335
|
onNeedsInput: (handler: (e: ChatRunNeedsInputEvent) => void) => () => void;
|
|
1312
1336
|
onComplete: (handler: (e: ChatRunCompleteEvent) => void) => () => void;
|
|
1313
1337
|
onError: (handler: (e: ChatRunErrorEvent) => void) => () => void;
|
|
1338
|
+
onNotice: (handler: (e: ChatRunNoticeEvent) => void) => () => void;
|
|
1314
1339
|
};
|
|
1315
1340
|
exchanges: {
|
|
1316
1341
|
/** Durable per-exchange log entries for a project, newest-first (max 100 by default). */
|
package/src/preload/index.cjs
CHANGED
|
@@ -129,6 +129,9 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
129
129
|
billing: {
|
|
130
130
|
fetch: () => ipcRenderer.invoke('billing:fetch'),
|
|
131
131
|
},
|
|
132
|
+
mcp: {
|
|
133
|
+
status: () => ipcRenderer.invoke('mcp:status'),
|
|
134
|
+
},
|
|
132
135
|
usageMatrix: {
|
|
133
136
|
snapshot: () => ipcRenderer.invoke('usage:matrix:snapshot'),
|
|
134
137
|
onTick: (handler) => {
|
|
@@ -406,6 +409,11 @@ contextBridge.exposeInMainWorld('api', {
|
|
|
406
409
|
ipcRenderer.on('chat:run:error', listener);
|
|
407
410
|
return () => ipcRenderer.removeListener('chat:run:error', listener);
|
|
408
411
|
},
|
|
412
|
+
onNotice: (handler) => {
|
|
413
|
+
const listener = (_e, payload) => handler(payload);
|
|
414
|
+
ipcRenderer.on('chat:run:notice', listener);
|
|
415
|
+
return () => ipcRenderer.removeListener('chat:run:notice', listener);
|
|
416
|
+
},
|
|
409
417
|
},
|
|
410
418
|
exchanges: {
|
|
411
419
|
/** List exchanges for a project (durable chat-run log), newest-first.
|