Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions apps/electron/src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,30 @@ ipcMain.handle('export:markdown', async (_event, input: unknown) =>
toIpcResult(() => agentKernelHost.exportMarkdown(input))
);

ipcMain.handle('background:listJobs', async () => toIpcResult(() => agentKernelHost.backgroundListJobs()));
ipcMain.handle('background:saveJob', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.backgroundSaveJob(input)));
ipcMain.handle('background:setEnabled', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.backgroundSetEnabled(input)));
ipcMain.handle('background:removeJob', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.backgroundRemoveJob(input)));
ipcMain.handle('background:listRuns', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.backgroundListRuns(input)));
ipcMain.handle('background:listNotifications', async () => toIpcResult(() => agentKernelHost.backgroundListNotifications()));

ipcMain.handle('export:html', async (_event, input: unknown) =>
toIpcResult(() => agentKernelHost.exportHtml(input))
);

ipcMain.handle('export:json', async (_event, input: unknown) =>
toIpcResult(() => agentKernelHost.exportJson(input))
);

ipcMain.handle('export:shareCard', async (_event, input: unknown) =>
toIpcResult(() => agentKernelHost.exportShareCard(input))
);

ipcMain.handle('context:list', async () => toIpcResult(() => agentKernelHost.contextList()));
ipcMain.handle('context:saveWatchlist', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.contextSaveWatchlist(input)));
ipcMain.handle('context:savePortfolio', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.contextSavePortfolio(input)));
ipcMain.handle('context:getRun', async (_event, input: unknown) => toIpcResult(() => agentKernelHost.contextGetRun(input)));

// Controlled external open (spec §10): http/https only, never a shell.
ipcMain.handle('openExternal', async (_event, url: unknown) =>
toIpcResult(async () => {
Expand Down
30 changes: 30 additions & 0 deletions apps/electron/src/main/kernelHost.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,31 @@ mock.module('@finagent/shared', () => ({
THESIS_REVIEW_HOUR: 9,
WEEKDAYS: [1, 2, 3, 4, 5],
reportToMarkdown: () => '',
reportToHtml: () => '<html></html>',
reportToJson: () => '{}',
reportToShareCard: () => ({ svg: '', text: '' }),
redactForShare: (report: unknown) => report,
PortfolioContextRepository: class {
listWatchlists = async () => [];
listPortfolios = async () => [];
saveWatchlist = async (input: unknown) => input;
savePortfolio = async (input: unknown) => input;
snapshot = async () => ({ id: 'ctx-1', kind: 'watchlist', sourceId: 'wl', sourceVersion: 1, createdAt: 1, document: {} });
bindSnapshots = async () => ({ runId: 'r1', sessionId: 's1', branchId: 'main', snapshotIds: [], boundAt: 1 });
getRunContext = async () => undefined;
get = async () => undefined;
},
BackgroundJobRepository: class {
listJobs = async () => [];
listRuns = async () => [];
listNotifications = async () => [];
saveJob = async (input: unknown) => input;
setEnabled = async () => undefined;
removeJob = async () => undefined;
},
BackgroundJobScheduler: class {
tick = async () => undefined;
},
computeSkillCalibrations: () => [],
computeStrategyCalibrations: () => [],
// V7 evaluation/observability (kernelHost constructor wiring; spec §15).
Expand Down Expand Up @@ -457,4 +480,11 @@ describe('AgentKernelHost', () => {
});
host.dispose();
});

it('validates context and background job payloads at the IPC boundary', async () => {
const host = new AgentKernelHost();
await expect(host.contextSaveWatchlist({ id: 'wl', name: 'Bad', instruments: [{ instrumentId: 'AAPL' }] })).rejects.toMatchObject({ code: 'INVALID_ARGUMENT' });
await expect(host.backgroundSaveJob({ id: 'job', type: 'research' })).rejects.toMatchObject({ code: 'INVALID_ARGUMENT' });
host.dispose();
});
});
243 changes: 240 additions & 3 deletions apps/electron/src/main/kernelHost.ts

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions apps/electron/src/preload/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,29 @@ var electronAPI = {
},
export: {
markdown: (input) => import_electron.ipcRenderer.invoke("export:markdown", input),
html: (input) => import_electron.ipcRenderer.invoke("export:html", input),
json: (input) => import_electron.ipcRenderer.invoke("export:json", input),
shareCard: (input) => import_electron.ipcRenderer.invoke("export:shareCard", input)
},
background: {
listJobs: () => import_electron.ipcRenderer.invoke("background:listJobs"),
saveJob: (input) => import_electron.ipcRenderer.invoke("background:saveJob", input),
setEnabled: (input) => import_electron.ipcRenderer.invoke("background:setEnabled", input),
removeJob: (input) => import_electron.ipcRenderer.invoke("background:removeJob", input),
listRuns: (input) => import_electron.ipcRenderer.invoke("background:listRuns", input),
listNotifications: () => import_electron.ipcRenderer.invoke("background:listNotifications"),
onOpen: (callback) => {
const listener = (_event, deepLink) => callback(deepLink);
import_electron.ipcRenderer.on("notification:open", listener);
return () => import_electron.ipcRenderer.removeListener("notification:open", listener);
}
},
context: {
list: () => import_electron.ipcRenderer.invoke("context:list"),
saveWatchlist: (input) => import_electron.ipcRenderer.invoke("context:saveWatchlist", input),
savePortfolio: (input) => import_electron.ipcRenderer.invoke("context:savePortfolio", input),
getRun: (input) => import_electron.ipcRenderer.invoke("context:getRun", input)
},
evaluation: {
getSettings: () => import_electron.ipcRenderer.invoke("evaluation:getSettings"),
setSettings: (input) => import_electron.ipcRenderer.invoke("evaluation:setSettings", input),
Expand Down
38 changes: 38 additions & 0 deletions apps/electron/src/preload/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,25 @@ export interface ElectronAPI {
};
export: {
markdown: (input: { reportId: string }) => Promise<unknown>;
html: (input: { reportId: string }) => Promise<unknown>;
json: (input: { reportId: string }) => Promise<unknown>;
shareCard: (input: { reportId: string }) => Promise<unknown>;
};
background: {
listJobs: () => Promise<unknown>;
saveJob: (input: unknown) => Promise<unknown>;
setEnabled: (input: { jobId: string; enabled: boolean }) => Promise<unknown>;
removeJob: (input: { jobId: string }) => Promise<unknown>;
listRuns: (input: { jobId?: string }) => Promise<unknown>;
listNotifications: () => Promise<unknown>;
onOpen: (callback: (deepLink: string) => void) => () => void;
};
context: {
list: () => Promise<unknown>;
saveWatchlist: (input: unknown) => Promise<unknown>;
savePortfolio: (input: unknown) => Promise<unknown>;
getRun: (input: { runId: string; sessionId: string; branchId: string }) => Promise<unknown>;
};
evaluation: {
getSettings: () => Promise<unknown>;
setSettings: (input: unknown) => Promise<unknown>;
Expand Down Expand Up @@ -380,8 +397,29 @@ const electronAPI: ElectronAPI = {
},
export: {
markdown: (input: { reportId: string }) => ipcRenderer.invoke('export:markdown', input),
html: (input: { reportId: string }) => ipcRenderer.invoke('export:html', input),
json: (input: { reportId: string }) => ipcRenderer.invoke('export:json', input),
shareCard: (input: { reportId: string }) => ipcRenderer.invoke('export:shareCard', input),
},
background: {
listJobs: () => ipcRenderer.invoke('background:listJobs'),
saveJob: (input: unknown) => ipcRenderer.invoke('background:saveJob', input),
setEnabled: (input: { jobId: string; enabled: boolean }) => ipcRenderer.invoke('background:setEnabled', input),
removeJob: (input: { jobId: string }) => ipcRenderer.invoke('background:removeJob', input),
listRuns: (input: { jobId?: string }) => ipcRenderer.invoke('background:listRuns', input),
listNotifications: () => ipcRenderer.invoke('background:listNotifications'),
onOpen: (callback: (deepLink: string) => void) => {
const listener = (_event: unknown, deepLink: string) => callback(deepLink);
ipcRenderer.on('notification:open', listener);
return () => ipcRenderer.removeListener('notification:open', listener);
},
},
context: {
list: () => ipcRenderer.invoke('context:list'),
saveWatchlist: (input: unknown) => ipcRenderer.invoke('context:saveWatchlist', input),
savePortfolio: (input: unknown) => ipcRenderer.invoke('context:savePortfolio', input),
getRun: (input: { runId: string; sessionId: string; branchId: string }) => ipcRenderer.invoke('context:getRun', input),
},
evaluation: {
getSettings: () => ipcRenderer.invoke('evaluation:getSettings'),
setSettings: (input) => ipcRenderer.invoke('evaluation:setSettings', input),
Expand Down
204 changes: 204 additions & 0 deletions artifacts/issues-36-38-e2e/e2e-evidence.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
{
"generatedAt": 1789113452103,
"liveAvailability": [
{
"url": "https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_source=folio",
"status": 200
},
{
"url": "https://www.nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_campaign=earnings#financial-highlights",
"status": "unreachable"
},
{
"url": "https://data.sec.gov/submissions/CIK0001045810.json",
"status": 200
},
{
"url": "https://www.reuters.com/technology/artificial-intelligence/nvidia-forecasts-first-quarter-revenue-above-estimates-2025-02-26/",
"status": "unreachable"
},
{
"url": "https://finance.example.test/nvidia-results-copy-a",
"status": "unreachable"
},
{
"url": "https://markets.example.test/nvidia-results-copy-b",
"status": "unreachable"
}
],
"sources": [
{
"id": "nvidia-ir",
"url": "https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_source=folio",
"title": "NVIDIA Announces Financial Results for Fourth Quarter and Fiscal 2025",
"summary": "Quarterly revenue was $39.3 billion, up 12% from Q3 and up 78% from a year ago.",
"publisher": "NVIDIA Newsroom",
"publishedAt": 1740603600000,
"available": true
},
{
"id": "nvidia-ir-tracked",
"url": "https://www.nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_campaign=earnings#financial-highlights",
"title": "NVIDIA Announces Financial Results for Fourth Quarter and Fiscal 2025",
"summary": "Quarterly revenue was $39.3 billion, up 12% from Q3 and up 78% from a year ago.",
"publisher": "NVIDIA Newsroom mirror",
"publishedAt": 1740603600000,
"available": false
},
{
"id": "sec-submissions",
"url": "https://data.sec.gov/submissions/CIK0001045810.json",
"title": "NVIDIA Corporation SEC submissions",
"summary": "Official filing history and accession metadata for NVIDIA Corporation.",
"publisher": "U.S. SEC",
"publishedAt": 1740607200000,
"available": true
},
{
"id": "reuters-original",
"url": "https://www.reuters.com/technology/artificial-intelligence/nvidia-forecasts-first-quarter-revenue-above-estimates-2025-02-26/",
"title": "Nvidia forecasts first-quarter revenue above estimates",
"summary": "Nvidia forecast quarterly revenue above Wall Street estimates as demand for AI chips remained strong.",
"publisher": "Reuters",
"publishedAt": 1740607800000,
"available": false
},
{
"id": "wire-copy-a",
"url": "https://finance.example.test/nvidia-results-copy-a",
"title": "Nvidia forecasts first quarter revenue above estimates",
"summary": "Nvidia forecast quarterly revenue above Wall Street estimates as demand for AI chips remained strong.",
"publisher": "Syndication A",
"publishedAt": 1740608100000,
"available": false
},
{
"id": "wire-copy-b",
"url": "https://markets.example.test/nvidia-results-copy-b",
"title": "Nvidia forecasts Q1 revenue above estimates",
"summary": "Nvidia forecast quarterly revenue above Wall Street estimates as demand for AI chips remained strong.",
"publisher": "Syndication B",
"publishedAt": 1740608400000,
"available": false
}
],
"context": {
"instrumentCount": 5,
"snapshotIds": [
"ctx-watchlist-ai-watchlist-v1-1789113452103-df109f6b-1c5a-4841-abfb-18014b993e7f",
"ctx-portfolio-core-portfolio-v1-1789113452103-039eaef8-32be-4e69-b61e-9909cda12a80"
],
"historicalSnapshotVersion": 1,
"currentPortfolioVersion": 2
},
"background": {
"jobs": [
{
"id": "failure-check",
"type": "filing-check",
"enabled": true,
"schedule": {
"intervalMs": 86400000
},
"input": {
"symbol": "NVDA.US"
},
"targetContext": {
"kind": "watchlist",
"id": "ai-watchlist"
},
"createdAt": 1789113451103,
"nextRunAt": 1789199852103,
"status": "failed",
"retryPolicy": {
"maxAttempts": 2,
"initialBackoffMs": 1,
"maxBackoffMs": 2
},
"missedRunPolicy": "catch-up",
"notificationPolicy": {
"onSuccess": true,
"onFailure": true,
"sensitivePreview": false
},
"lastRunAt": 1789113452103,
"lastRunId": "6726935d-f906-4953-84f5-1f2df94ebc29"
},
{
"id": "nvda-filing-check",
"type": "filing-check",
"enabled": true,
"schedule": {
"intervalMs": 86400000
},
"input": {
"symbol": "NVDA.US"
},
"targetContext": {
"kind": "watchlist",
"id": "ai-watchlist"
},
"createdAt": 1789113451103,
"nextRunAt": 1789199852103,
"status": "succeeded",
"retryPolicy": {
"maxAttempts": 2,
"initialBackoffMs": 1,
"maxBackoffMs": 2
},
"missedRunPolicy": "catch-up",
"notificationPolicy": {
"onSuccess": true,
"onFailure": true,
"sensitivePreview": false
},
"lastRunAt": 1789113452103,
"lastRunId": "9fa476a9-c243-4e2c-bb50-9308f39f5c40"
}
],
"runs": [
{
"id": "6726935d-f906-4953-84f5-1f2df94ebc29",
"jobId": "failure-check",
"status": "failed",
"scheduledFor": 1789113452102,
"startedAt": 1789113452103,
"finishedAt": 1789113452103,
"attempts": 2,
"error": "simulated provider failure"
},
{
"id": "9fa476a9-c243-4e2c-bb50-9308f39f5c40",
"jobId": "nvda-filing-check",
"status": "succeeded",
"scheduledFor": 1789113452102,
"startedAt": 1789113452103,
"finishedAt": 1789113452103,
"attempts": 1,
"productionRunId": "report-nvda-fy2025-e2e"
}
],
"notifications": [
{
"id": "notification-6726935d-f906-4953-84f5-1f2df94ebc29",
"jobId": "failure-check",
"runId": "6726935d-f906-4953-84f5-1f2df94ebc29",
"kind": "failed",
"title": "Research needs attention",
"message": "Open Folio for details.",
"createdAt": 1789113452103,
"deepLink": "/jobs/failure-check"
},
{
"id": "notification-9fa476a9-c243-4e2c-bb50-9308f39f5c40",
"jobId": "nvda-filing-check",
"runId": "9fa476a9-c243-4e2c-bb50-9308f39f5c40",
"kind": "filing-found",
"title": "New filing found",
"message": "Open Folio to view the result.",
"createdAt": 1789113452103,
"deepLink": "/runs/report-nvda-fy2025-e2e"
}
]
}
}
1 change: 1 addition & 0 deletions artifacts/issues-36-38-e2e/nvda-fy2025-report.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!doctype html><html lang="en-US"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>NVDA.US Research Report</title><style>body{font:16px/1.55 system-ui,sans-serif;max-width:820px;margin:48px auto;padding:0 28px;color:#18202a}h1{font-size:2.25rem}h2{margin-top:2rem;border-bottom:1px solid #d7dde5;padding-bottom:.35rem}.meta,.verdict,footer{color:#586474}.citation{font-size:.8em;text-decoration:none}li{margin:.8rem 0}table{border-collapse:collapse;width:100%;margin:1rem 0}th,td{border:1px solid #ccd3dc;padding:.45rem;text-align:left}th{background:#f5f7fa}@media print{body{margin:0;max-width:none}.citation{color:inherit}a{color:inherit;text-decoration:none}section{break-inside:avoid}}</style></head><body><header><h1>NVDA.US Research Report</h1><p class="meta">Generated 2026-09-11T07:57:32.103Z · bullish · 84%</p><p>NVIDIA reported fiscal Q4 2025 revenue of $39.3B, up 78% year over year. The report keeps the live Web and financial evidence references, including source availability and structured period/currency/unit metadata.</p></header><main><section><h2>Earnings event</h2><p class="verdict">positive</p><table><thead><tr><th>Metric</th><th>Value</th><th>As of</th></tr></thead><tbody><tr><td>Revenue</td><td>$39.3B</td><td>FY2025 Q4</td></tr><tr><td>YoY growth</td><td>78%</td><td>FY2025 Q4</td></tr></tbody></table><a class="citation" href="#citation-1">[1]</a> <a class="citation" href="#citation-2">[2]</a> <a class="citation" href="#citation-3">[3]</a> <a class="citation" href="#citation-4">[4]</a></section><section><h2>Financial evidence</h2><p class="verdict">positive</p><p>Quarterly revenue and growth were checked as structured values with explicit unit and period.</p><a class="citation" href="#citation-5">[5]</a></section><section><h2>Evidence</h2><ol><li id="citation-1"><strong>[1]</strong> NVIDIA Announces Financial Results for Fourth Quarter and Fiscal 2025<br><span><a href="https://nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_source=folio">NVIDIA Newsroom</a> · run live-search-e2e · 2026-09-11T07:57:32.103Z</span></li><li id="citation-2"><strong>[2]</strong> NVIDIA Announces Financial Results for Fourth Quarter and Fiscal 2025<br><span><a href="https://www.nvidianews.nvidia.com/news/nvidia-announces-financial-results-for-fourth-quarter-and-fiscal-2025?utm_campaign=earnings#financial-highlights">NVIDIA Newsroom mirror</a> · run live-search-e2e · 2026-09-11T07:57:32.103Z</span></li><li id="citation-3"><strong>[3]</strong> NVIDIA Corporation SEC submissions<br><span><a href="https://data.sec.gov/submissions/CIK0001045810.json">U.S. SEC</a> · run live-search-e2e · 2026-09-11T07:57:32.103Z</span></li><li id="citation-4"><strong>[4]</strong> Nvidia forecasts first-quarter revenue above estimates<br><span><a href="https://www.reuters.com/technology/artificial-intelligence/nvidia-forecasts-first-quarter-revenue-above-estimates-2025-02-26/">Reuters</a> · run live-search-e2e · 2026-09-11T07:57:32.103Z</span></li><li id="citation-5"><strong>[5]</strong> Fiscal Q4 revenue was $39.3B and grew 78% YoY.<br><span>NVDA.US · quarterly_revenue · USD · billions · run financial-e2e · 2026-09-11T07:57:32.103Z</span></li></ol></section></main><footer>Folio export · schema folio-report-export-v1 · run e2e-copilot-run</footer></body></html>
Loading