failproofai 1.0.4-beta.2 → 1.0.4-beta.6
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/build-manifest.json +3 -3
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/required-server-files.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js +2 -2
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +6 -6
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +7 -7
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +2 -2
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/api/audit/invite/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/audit/run/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/audit/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-request/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/login-verify/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/logout/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/auth/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/download/[project]/[session]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page/server-reference-manifest.json +5 -5
- package/.next/standalone/.next/server/app/audit/page.js +3 -3
- package/.next/standalone/.next/server/app/audit/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/audit/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +13 -13
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +14 -14
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/page.js +2 -2
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/policies/page/server-reference-manifest.json +14 -14
- package/.next/standalone/.next/server/app/policies/page.js +4 -4
- package/.next/standalone/.next/server/app/policies/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/policies/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page.js +3 -3
- package/.next/standalone/.next/server/app/project/[name]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/react-loadable-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page/server-reference-manifest.json +2 -2
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js +4 -4
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/project/[name]/session/[sessionId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/projects/page/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/server/app/projects/page.js +3 -3
- package/.next/standalone/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page/server-reference-manifest.json +4 -4
- package/.next/standalone/.next/server/app/settings/page.js +2 -2
- package/.next/standalone/.next/server/app/settings/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1lh7m5d._.js → [externals]__0v1hz15._.js} +1 -1
- package/.next/standalone/.next/server/chunks/{[externals]__1rqkg_y._.js → [externals]__1nb206a._.js} +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0o07qi9._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0rv7m0k._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__17g9wh7._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1legmza._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1p-qi2t._.js +1 -1
- package/.next/standalone/.next/server/chunks/{_08w6xzm._.js → _0otft92._.js} +2 -2
- package/.next/standalone/.next/server/chunks/_0tovk6q._.js +1 -1
- package/.next/standalone/.next/server/chunks/_0trp3yc._.js +1 -1
- package/.next/standalone/.next/server/chunks/_1ek68ln._.js +4 -4
- package/.next/standalone/.next/server/chunks/_1vslkoe._.js +1 -1
- package/.next/standalone/.next/server/chunks/package_json_[json]_cjs_1nxcc4v._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0p-5p8u._.js → [root-of-the-server]__0-4ja6q._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1p2otjt._.js → [root-of-the-server]__00cr3xu._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__013jr2b._.js → [root-of-the-server]__04px77e._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__190won8._.js → [root-of-the-server]__0dxf928._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0s740oi._.js → [root-of-the-server]__0egr5s1._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__01wy8d-._.js → [root-of-the-server]__0i0l1di._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0n_lxhg._.js → [root-of-the-server]__0kokc8z._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__1ctpynv._.js → [root-of-the-server]__0n2txhk._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0oa1lav._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__02npjtd._.js → [root-of-the-server]__0r3c1_0._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0ftmoxc._.js → [root-of-the-server]__17xnxlx._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__0da85px._.js → [root-of-the-server]__1ckpmkk._.js} +3 -3
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__00-s7h8._.js → [root-of-the-server]__1fvfw59._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__1ui5ur8._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/_02b3i98._.js +23 -0
- package/.next/standalone/.next/server/chunks/ssr/{_0oq1dh6._.js → _04ng_6x._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{_1u8-lu2._.js → _07t3jol._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/_08x1r5t._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/{_0h_douw._.js → _0s-77v5._.js} +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1_ozgif._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1ddpc5g._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/_1es2j7i._.js +4 -1
- package/.next/standalone/.next/server/chunks/ssr/_next-internal_server_app_policies_page_actions_1sp2-yo.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/app_audit__components_audit-dashboard_tsx_0p9ud47._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_global-error_tsx_1kp6l3x._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_policies_hooks-client_tsx_19dqvpc._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/app_settings_settings-client_tsx_20lq-mq._.js +1 -1
- package/.next/standalone/.next/server/chunks/ssr/src_hooks_fp-home_ts_0je3xkv._.js +1 -1
- package/.next/standalone/.next/server/middleware-build-manifest.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +22 -22
- package/.next/standalone/.next/static/chunks/{1pb1oztsbwcss.js → 07c6l1lq5_69b.js} +1 -1
- package/.next/standalone/.next/static/chunks/{0o-hh5_turzlz.css → 0jpd8dv2vk930.css} +1 -1
- package/.next/standalone/.next/static/chunks/{32spub4wqjem-.js → 12bsgz8czvcnn.js} +1 -1
- package/.next/standalone/.next/static/chunks/{3m4upvybtrexd.js → 1gn08w2pgqh09.js} +1 -1
- package/.next/standalone/.next/static/chunks/{010bv1w6j171t.js → 1xgq22tri86an.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2aquitk72k2op.js → 20xkiqzvfkuvm.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2bi_1y0a_smt7.js → 2a84ug32tzik2.js} +1 -1
- package/.next/standalone/.next/static/chunks/{2ej3b8gk5ittu.js → 32m2ryfankf_2.js} +1 -1
- package/.next/standalone/.next/static/chunks/3ioub_lpen-sq.js +1 -0
- package/.next/standalone/.next/static/chunks/{0wz8yftk18ts2.js → 3jxd133cw2vm3.js} +1 -1
- package/.next/standalone/.next/static/chunks/44ncu88eq6z71.js +1 -0
- package/.next/standalone/app/actions/get-leaks.ts +5 -1
- package/.next/standalone/app/actions/update-scheduled-audit.ts +4 -56
- package/.next/standalone/app/api/auth/status/route.ts +3 -2
- package/.next/standalone/app/audit/_components/audit-dashboard.tsx +29 -2
- package/.next/standalone/app/audit/_components/leak-section.tsx +15 -2
- package/.next/standalone/app/audit/audit-styles.css +18 -0
- package/.next/standalone/app/settings/page.tsx +1 -1
- package/.next/standalone/app/settings/settings-client.tsx +13 -39
- package/.next/standalone/lib/auth/api-server-client.ts +6 -6
- package/.next/standalone/lib/codex-sessions.ts +20 -4
- package/.next/standalone/lib/copilot-sessions.ts +32 -1
- package/.next/standalone/lib/cursor-sessions.ts +62 -5
- package/.next/standalone/lib/sqlite-reader.ts +78 -1
- package/.next/standalone/package.json +10 -10
- package/.next/standalone/server.js +1 -1
- package/bin/failproofai.mjs +1 -1
- package/dist/cli.mjs +1191 -1130
- package/dist/worker.mjs +198 -359
- package/lib/auth/api-server-client.ts +6 -6
- package/lib/codex-sessions.ts +20 -4
- package/lib/copilot-sessions.ts +32 -1
- package/lib/cursor-sessions.ts +62 -5
- package/lib/sqlite-reader.ts +78 -1
- package/package.json +10 -10
- package/pi-extension/index.ts +11 -4
- package/src/audit/cache.ts +6 -0
- package/src/audit/cli-adapters/claude.ts +13 -0
- package/src/audit/cli-login.ts +4 -1
- package/src/audit/cli.ts +67 -51
- package/src/audit/harm-report.ts +12 -6
- package/src/audit/index.ts +151 -8
- package/src/audit/leak-fingerprint.ts +2 -4
- package/src/audit/leak-record.ts +13 -9
- package/src/audit/leak-scan.ts +20 -44
- package/src/audit/leak-store.ts +5 -2
- package/src/audit/machine-store.ts +8 -9
- package/src/audit/macos-notifier.ts +2 -2
- package/src/audit/redact-example.ts +62 -5
- package/src/audit/report-harm.ts +31 -46
- package/src/audit/schedule-cli.ts +17 -33
- package/src/audit/types.ts +11 -0
- package/src/hooks/configure-wizard.ts +28 -0
- package/src/hooks/fp-config.ts +29 -54
- package/src/hooks/handler.ts +1 -59
- package/src/hooks/integrations.ts +4 -1
- package/src/hooks/notice.ts +23 -154
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__0rgu2r3._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/_0-oij9d._.js +0 -23
- package/.next/standalone/.next/static/chunks/04r6ch8uf_n8m.js +0 -1
- package/.next/standalone/.next/static/chunks/0zebh1n9jkfbt.js +0 -1
- package/src/audit/leak-notice.ts +0 -161
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_clientMiddlewareManifest.js +0 -0
- /package/.next/standalone/.next/static/{aKNv4Kmv98Xpwns0Xa-C- → aeM365dNTtUZOZvNkX8ba}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,44649,e=>{"use strict";var t=e.i(95187);let a=(0,t.createServerReference)("6009779268cbebb950fffbef0f7dab73fe58ddbf3b",t.callServer,void 0,t.findSourceMapURL,"searchHookActivityAction");e.s(["searchHookActivityAction",0,a])},73520,e=>{"use strict";var t=e.i(43476),a=e.i(24071),r=e.i(67927);e.s(["default",0,function({currentPage:e,totalPages:s,onPageChange:i}){if(s<=1)return null;let l=e=>`px-3 py-2 text-sm rounded-md transition-colors flex items-center gap-1 bg-muted text-muted-foreground ${e?"opacity-50 cursor-not-allowed":"hover:bg-muted/80"}`;return(0,t.jsxs)("div",{className:"flex items-center justify-center gap-2 py-4",children:[(0,t.jsxs)("button",{onClick:()=>i(e-1),disabled:1===e,className:l(1===e),"aria-label":"Previous page",children:[(0,t.jsx)(a.ChevronLeft,{className:"w-4 h-4"}),(0,t.jsx)("span",{className:"hidden sm:inline",children:"Previous"})]}),(0,t.jsx)("div",{className:"flex items-center gap-1",children:(()=>{if(s<=7)return Array.from({length:s},(e,t)=>t+1);let t=[1],a=Math.max(2,e-1),r=Math.min(s-1,e+1);e<=3&&(r=Math.min(5,s-1)),e>=s-2&&(a=Math.max(2,s-4)),a>2&&t.push("ellipsis-start");for(let e=a;e<=r;e++)t.push(e);return r<s-1&&t.push("ellipsis-end"),t.push(s),t})().map(a=>"string"==typeof a?(0,t.jsx)("span",{className:"px-2 text-muted-foreground",children:"..."},a):(0,t.jsx)("button",{onClick:()=>i(a),className:`min-w-[2.5rem] px-3 py-2 text-sm rounded-md transition-colors ${e===a?"bg-primary text-primary-foreground":"bg-muted text-muted-foreground hover:bg-muted/80"}`,"aria-label":`Page ${a}`,"aria-current":e===a?"page":void 0,children:a},a))}),(0,t.jsxs)("button",{onClick:()=>i(e+1),disabled:e===s,className:l(e===s),"aria-label":"Next page",children:[(0,t.jsx)("span",{className:"hidden sm:inline",children:"Next"}),(0,t.jsx)(r.ChevronRight,{className:"w-4 h-4"})]})]})}])},34713,e=>{"use strict";e.s(["formatDuration",0,function(e){if(e<1e3)return`${e}ms`;let t=Math.round(e/100);if(t<600)return`${(t/10).toFixed(1)}s`;let a=Math.round(e/1e3);if(a<3600){let e=Math.floor(a/60);return`${e}m ${a%60}s`}let r=Math.round(e/6e4),s=Math.floor(r/60);return`${s}h ${r%60}m`},"formatRelativeTime",0,function(e){let t=Date.now()-e;return t<6e4?`${Math.max(1,Math.floor(t/1e3))}s ago`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}])},24071,e=>{"use strict";let t=(0,e.i(56420).default)("chevron-left",[["path",{d:"m15 18-6-6 6-6",key:"1wnfg3"}]]);e.s(["ChevronLeft",0,t],24071)},67927,e=>{"use strict";let t=(0,e.i(56420).default)("chevron-right",[["path",{d:"m9 18 6-6-6-6",key:"mthhwq"}]]);e.s(["ChevronRight",0,t],67927)},16306,e=>{"use strict";let t=(0,e.i(56420).default)("shield-alert",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"M12 8v4",key:"1got3b"}],["path",{d:"M12 16h.01",key:"1drbdi"}]]);e.s(["ShieldAlert",0,t],16306)},84026,29407,e=>{"use strict";var t=e.i(56420);let a=(0,t.default)("shield-check",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m9 12 2 2 4-4",key:"dzmm74"}]]);e.s(["ShieldCheck",0,a],84026);let r=(0,t.default)("shield-x",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}],["path",{d:"m14.5 9.5-5 5",key:"17q4r4"}],["path",{d:"m9.5 9.5 5 5",key:"18nt4w"}]]);e.s(["ShieldX",0,r],29407)},82954,e=>{"use strict";let t=(0,e.i(56420).default)("shield",[["path",{d:"M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z",key:"oel41y"}]]);e.s(["Shield",0,t],82954)}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,24358,e=>{"use strict";var s=e.i(43476),a=e.i(71645),n=e.i(95187);let t=(0,n.createServerReference)("009ad545620d7158fc4c18ddc9854cfa5473399e64",n.callServer,void 0,n.findSourceMapURL,"getAuditResultAction");var r=e.i(12344);function i({cachedAt:e,fixed:a=!1}){return(0,s.jsxs)("footer",{className:a?"report-footer report-footer--fixed":"report-footer",children:[(0,s.jsx)("img",{src:"/logo.svg",alt:"failproof_ai",style:{height:18,display:"inline-block",verticalAlign:"middle"}}),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"audit v1.0",(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),"generated ",function(e){if(!e)return"—";let s=new Date(e);if(Number.isNaN(s.getTime()))return"—";let a=s.getUTCDate().toString().padStart(2,"0"),n=["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"][s.getUTCMonth()],t=s.getUTCFullYear(),r=s.getUTCHours().toString().padStart(2,"0"),i=s.getUTCMinutes().toString().padStart(2,"0");return`${a} ${n} ${t}, ${r}:${i} utc`}(e),(0,s.jsx)("span",{style:{margin:"0 12px",color:"var(--line-2)"},children:"·"}),(0,s.jsx)("span",{style:{color:"var(--ink-2)"},children:"auto-healing for your agents."})]})}let l=(0,n.createServerReference)("40627fe0bb35d1aea2f59f4ed5f39912b524a8cc1c",n.callServer,void 0,n.findSourceMapURL,"dismissLeakAction"),c=(0,n.createServerReference)("004c0a92f30b3875d89d14068f7fbdb7ecce772dc3",n.callServer,void 0,n.findSourceMapURL,"getLeaksAction"),d=(0,n.createServerReference)("40dbb536ad33353d440386d51c0242e56cd808cfbc",n.callServer,void 0,n.findSourceMapURL,"setLeakNotifyAction");function o(e){let s=Date.parse(e);if(!Number.isFinite(s))return"unknown";let a=Math.floor((Date.now()-s)/864e5);return a<=0?"today":1===a?"yesterday":a<30?`${a}d ago`:new Date(s).toLocaleDateString(void 0,{month:"short",day:"numeric"})}function u(){let[e,n]=(0,a.useState)(null),[t,r]=(0,a.useState)(!0),[i,u]=(0,a.useState)(null),h=(0,a.useCallback)(async()=>{let e=await c();n(e.rows),r(e.notify)},[]);(0,a.useEffect)(()=>{h()},[h]);let p=(0,a.useCallback)(async e=>{u(e);try{await l(e)&&n(s=>s?.filter(s=>s.id!==e)??null)}finally{u(null)}},[]),m=(0,a.useCallback)(async()=>{let e=!t;r(e),await d(e)||r(!e)},[t]);return null===e?null:(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"02 Leaks",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"02"}),"// credentials"]}),(0,s.jsx)("button",{type:"button",className:"cbb-toggle leak-notify","data-on":t,onClick:m,"aria-label":t?"turn desktop notifications off":"turn desktop notifications on",title:"desktop notification when a new credential is found",children:(0,s.jsx)("span",{className:"cbb-toggle-knob"})})]}),0===e.length?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("h2",{className:"audit-sec-title",children:"no credentials found"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// nothing in this machine's transcripts matched a known key format",(0,s.jsx)("br",{}),"// or a secret-named assignment."]})]}):(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("h2",{className:"audit-sec-title",children:[e.length," credential",1===e.length?"":"s"," in your transcripts"]}),(0,s.jsx)("div",{className:"audit-sec-sub",children:"// one row per distinct key — an exposure count is not a key count."}),(0,s.jsx)("ul",{className:"leak-list",children:e.map(e=>(0,s.jsxs)("li",{className:"leak-row","data-attributed":e.attributed,children:[(0,s.jsxs)("div",{className:"leak-row-top",children:[(0,s.jsx)("code",{className:"leak-key",children:e.display}),(0,s.jsx)("span",{className:"leak-label",children:e.label}),e.name?(0,s.jsx)("span",{className:"leak-name",children:e.name}):null,(0,s.jsx)("button",{type:"button",className:"leak-dismiss",onClick:()=>void p(e.id),disabled:i===e.id,children:"not a secret"})]}),(0,s.jsxs)("div",{className:"leak-how",children:[(0,s.jsx)("span",{className:"leak-verb",children:e.mechanism})," in ",(0,s.jsx)("span",{className:"leak-where",children:e.project})," via ",(0,s.jsx)("span",{className:"leak-who",children:e.cli})," · leaked ",(0,s.jsx)("span",{className:"leak-when",children:o(e.firstSeen)}),o(e.firstSeen)!==o(e.lastSeen)?(0,s.jsxs)(s.Fragment,{children:[" · last seen ",(0,s.jsx)("span",{className:"leak-when",children:o(e.lastSeen)})]}):null]}),(0,s.jsxs)("div",{className:"leak-foot",children:[(0,s.jsxs)("span",{className:"leak-meta",children:[e.occurrences," exposure",1===e.occurrences?"":"s",e.sessions>1?` \xb7 ${e.sessions} sessions`:""," · ",e.length," chars"]}),(0,s.jsx)("span",{className:"leak-dir",children:"input"===e.direction?"sent by the agent — blockable at PreToolUse":"returned to the agent — already in the transcript"}),(0,s.jsx)("span",{className:"leak-fix",children:e.attributed?`rotate it in the ${e.label.replace(/ (API )?(key|token).*$/i,"")} console`:e.name?`find what reads ${e.name}, then rotate it there`:"trace it to its owner, then rotate it"})]})]},e.id))})]})]})}var h=e.i(86616);function p({mode:e,running:a,onStarted:n,onCompleted:t}){let{capture:i}=(0,r.usePostHog)(),l=async()=>{i("audit_first_run_clicked",{source:"empty_state",mode:e}),n();try{await (0,h.triggerRun)({cli:[],since:"all"})}catch(e){console.error("audit first run failed:",e)}finally{await t()}};return"no-cache"===e?(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," first run"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--dim)"},children:"○"})," no cache yet"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"scan and see."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["1","5","8","10","13","17","20","23","25","30","32"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no audit data yet"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"run your first audit."}),(0,s.jsxs)("p",{className:"empty-sub",children:["we'll walk every transcript across your installed CLIs — Claude Code, Codex, Copilot, Cursor, OpenCode, Pi — and count every wasteful","or risky action. you'll get your agent's archetype and a punch-list."]}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("button",{type:"button",className:"btn btn-primary btn-press empty-cta",onClick:l,disabled:a,children:a?"[ scanning… ]":"[ run audit ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"scans all sessions · all installed CLIs · may take a while"})]})]})]}):(0,s.jsxs)("section",{className:"section empty-section","data-screen-label":"00 Empty",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," zero transcripts"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--amber)"},children:"●"})," hooks not installed"]})]}),(0,s.jsx)("h2",{className:"section-h",children:"nothing to read."}),(0,s.jsxs)("div",{className:"panel empty-panel",children:[(0,s.jsxs)("div",{className:"empty-glyph","aria-hidden":"true",children:[(0,s.jsx)("div",{className:"empty-glyph-grid",children:Array.from({length:36}).map((e,a)=>{let n=["2","4","9","11","16","18","23","25","27","30","33"].includes(String(a));return(0,s.jsx)("span",{className:"px"+(n?" on":"")},a)})}),(0,s.jsx)("div",{className:"empty-glyph-label",children:"▮▮ no sessions found"})]}),(0,s.jsx)("h3",{className:"empty-headline",children:"install hooks first."}),(0,s.jsx)("p",{className:"empty-sub",children:"failproofai couldn't find any transcripts to scan on this machine. install the hooks for at least one CLI and come back."}),(0,s.jsxs)("div",{className:"empty-actions",children:[(0,s.jsx)("a",{href:"https://docs.befailproof.ai/getting-started",target:"_blank",rel:"noopener noreferrer",className:"btn btn-primary btn-press empty-cta",onClick:()=>{i("audit_install_guide_clicked",{source:"empty_state",mode:e})},children:"[ install guide → ]"}),(0,s.jsx)("span",{className:"empty-meta",children:"takes about 30 seconds · one command per CLI"})]})]})]})}let m=[{label:"discovering transcripts",detail:"walking ~/.claude, ~/.codex, ~/.cursor, …"},{label:"parsing session logs",detail:"reading JSONL + sqlite session stores"},{label:"running policy checks",detail:"replaying every policy against each tool call"},{label:"aggregating results",detail:"counting hits, ranking by frequency"}];function g(){let[e,n]=(0,a.useState)(0),[t,r]=(0,a.useState)(0);(0,a.useEffect)(()=>{let e=setInterval(()=>{n(e=>Math.min(e+1,m.length-1))},4e3),s=setInterval(()=>r(e=>(e+1)%4),350);return()=>{clearInterval(e),clearInterval(s)}},[]);let i=".".repeat(t+1),l=e===m.length-1,c=Math.round(100*Math.min(.9,(e+1)/m.length*.9));return(0,s.jsxs)("section",{className:"section running-section","data-screen-label":"00 Running",children:[(0,s.jsxs)("div",{className:"section-mast",children:[(0,s.jsxs)("div",{className:"section-label",children:[(0,s.jsx)("span",{className:"glyph",children:"━━"})," audit"," ",(0,s.jsx)("span",{className:"sep",children:"·"})," in progress"]}),(0,s.jsxs)("div",{className:"section-meta",children:[(0,s.jsx)("span",{style:{color:"var(--accent-pink)"},children:"●"})," scanning"]})]}),(0,s.jsxs)("h2",{className:"section-h",children:["scanning sessions",i]}),(0,s.jsxs)("div",{className:"panel running-panel",children:[(0,s.jsxs)("div",{className:"running-header",children:[(0,s.jsx)("span",{className:"running-prompt",children:"$"}),(0,s.jsx)("span",{className:"running-cmd",children:"failproofai audit"}),(0,s.jsx)("span",{className:"running-cursor","aria-hidden":"true",children:"▮"})]}),(0,s.jsx)("ul",{className:"running-stages",children:m.map((a,n)=>{let r=n<e,i=n===e;return(0,s.jsxs)("li",{className:"running-stage"+(r?" done":i?" active":" queued"),children:[(0,s.jsx)("span",{className:"running-marker","aria-hidden":"true",children:r?"✓":i?"▮▮":"○"}),(0,s.jsxs)("div",{className:"running-stage-body",children:[(0,s.jsx)("div",{className:"running-stage-label",children:a.label}),i&&(0,s.jsx)("div",{className:"running-stage-detail",children:l?"finishing up…":a.detail})]}),i&&(0,s.jsx)("span",{className:"running-stage-spin","aria-hidden":"true",children:["⠋","⠙","⠹","⠸"][t]})]},n)})}),(0,s.jsxs)("div",{className:"running-bar-label",children:[(0,s.jsx)("span",{children:"progress"}),(0,s.jsxs)("span",{style:{color:"var(--dim)"},children:[c,"%"]})]}),(0,s.jsx)("div",{className:"running-bar-track",children:(0,s.jsx)("div",{className:"running-bar-fill",style:{width:`${c}%`}})}),(0,s.jsx)("p",{className:"running-foot",children:"this can take a while depending on how much session history you have."})]})]})}let x={timeout:"audit took too long — try again",network:"network hiccup — check your connection",post_failed:"audit failed — try again"};function f({status:e,onDismiss:n}){let[t,r]=(0,a.useState)(()=>Date.now());if((0,a.useEffect)(()=>{if("running"!==e.kind)return;let s=setInterval(()=>r(Date.now()),1e3);return()=>clearInterval(s)},[e.kind]),"idle"===e.kind)return null;if("running"===e.kind){let a=Math.max(0,t-e.startedAt);return(0,s.jsxs)("div",{className:"audit-progress-strip",role:"status","aria-live":"polite",children:[(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__spinner","aria-hidden":"true"}),"re-auditing your sessions"]}),(0,s.jsx)("span",{className:"audit-progress-strip__elapsed",children:function(e){if(e<0)return"00:00";let s=Math.floor(e/1e3),a=Math.floor(s/60).toString().padStart(2,"0"),n=(s%60).toString().padStart(2,"0");return`${a}:${n}`}(a)})]}),(0,s.jsx)("div",{className:"audit-progress-strip__pulse","aria-hidden":"true"})]})}return(0,s.jsx)("div",{className:"audit-progress-strip audit-progress-strip--failed",role:"alert",children:(0,s.jsxs)("div",{className:"audit-progress-strip__inner",children:[(0,s.jsxs)("span",{className:"audit-progress-strip__label",children:[(0,s.jsx)("span",{className:"audit-progress-strip__x","aria-hidden":"true",children:"×"}),x[e.reason]]}),(0,s.jsx)("button",{type:"button",className:"audit-progress-strip__dismiss",onClick:n,"aria-label":"dismiss",children:"[ dismiss ]"})]})})}function j({result:e,cachedAt:n,projectFromUrl:t,isRunning:l,rerunStatus:c,onRerun:d,onDismissRerun:o}){let{capture:h}=(0,r.usePostHog)(),{detectorsTriggered:p,missing:m}=(0,a.useMemo)(()=>{let s=0,a=0;for(let n of e.results)n.hits>0&&s++,"builtin"===n.source&&!n.enabledInConfig&&n.hits>0&&a++;return{detectorsTriggered:s,missing:a}},[e]),g=(0,a.useRef)(!1);return(0,a.useEffect)(()=>{g.current||(g.current=!0,h("audit_dashboard_viewed",{missing:m,transcripts_scanned:e.transcripts.scanned,results_count:e.results.length,detectors_triggered:p}))},[h,m,e.transcripts.scanned,e.results.length,p]),(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:c,onDismiss:o}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsxs)("div",{className:"report",children:[(0,s.jsx)(y,{transcripts:e.transcripts.scanned,events:e.eventsScanned??0,projects:e.projectsScanned.length,isRunning:l||"running"===c.kind,onRerun:()=>d("scan_header")}),(0,s.jsx)(u,{})]}),(0,s.jsx)(i,{cachedAt:n})]})]})}function y({transcripts:e,events:a,projects:n,isRunning:t,onRerun:r}){return(0,s.jsxs)("section",{className:"audit-sec","data-screen-label":"01 Scan",children:[(0,s.jsxs)("div",{className:"audit-sec-head",children:[(0,s.jsxs)("span",{className:"audit-sec-eyebrow",children:[(0,s.jsx)("span",{className:"ix",children:"01"}),"// scan"]}),(0,s.jsx)("button",{type:"button",className:"scan-rerun",onClick:r,disabled:t,children:t?"scanning…":"run again"})]}),(0,s.jsx)("h2",{className:"audit-sec-title",children:"scan complete"}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:[a.toLocaleString()," tool call",1===a?"":"s"," across ",e.toLocaleString()," ","transcript",1===e?"":"s",n>0?` \xb7 ${n.toLocaleString()} project${1===n?"":"s"}`:""]}),(0,s.jsxs)("div",{className:"audit-sec-sub",children:["// the report is being rebuilt around leak detection. the scan, the",(0,s.jsx)("br",{}),"// 12-CLI transcript reader and the emailed digest are unaffected."]})]})}function N({running:e,mode:a="no-cache",rerunStatus:n,onDismissRerun:t,onStarted:r,onCompleted:l}){return(0,s.jsxs)("div",{className:"app",children:[(0,s.jsx)(f,{status:n,onDismiss:t}),(0,s.jsxs)("div",{className:"app-shell",children:[(0,s.jsx)("div",{className:"report",children:e?(0,s.jsx)(g,{}):(0,s.jsx)(p,{mode:a,running:e,onStarted:r,onCompleted:l})}),(0,s.jsx)(i,{cachedAt:null,fixed:!0})]})]})}e.s(["AuditDashboard",0,function({initial:e,projectFromUrl:n,totalCatalogSize:i}){let[l,c]=(0,a.useState)(e),[d,o]=(0,a.useState)(!1),[u,p]=(0,a.useState)({kind:"idle"}),{capture:m}=(0,r.usePostHog)(),g=(0,a.useRef)(null),x=(0,a.useRef)(!1),f="cached"===l.status?l.result.transcripts.scanned:0,y="cached"===l.status?l.result.results.length:0,b=(0,a.useCallback)(async()=>{c(await t())},[]),v=(0,a.useCallback)(async e=>{if(d)return;m("audit_rerun_clicked",{source:e,since:"all"}),o(!0);let s=Date.now();p({kind:"running",startedAt:s});try{await (0,h.triggerRun)({cli:[],since:"all",noCache:!0}),await b(),m("audit_rerun_succeeded",{source:e,since:"all",duration_ms:Date.now()-s}),p({kind:"idle"})}catch(n){let a=n instanceof h.RerunError?n.kind:"network";m("audit_rerun_failed",{kind:a,source:e,since:"all",cli_filter:"all",duration_ms:Date.now()-s}),p({kind:"failed",reason:a,failedAt:Date.now()})}finally{o(!1)}},[m,b,d]),k=(0,a.useCallback)(()=>{p({kind:"idle"})},[]);if((0,a.useEffect)(()=>(document.body.classList.add("audit-body"),()=>document.body.classList.remove("audit-body")),[]),(0,a.useEffect)(()=>{let e=d?"running":"empty"===l.status?"empty":0===f?"zero_sessions":"report";g.current!==e&&(g.current=e,m("audit_page_viewed",{state:e,has_cache:"cached"===l.status}))},[l.status,m,d,f]),(0,a.useEffect)(()=>{x.current=!1},[l.status,d,f]),(0,a.useEffect)(()=>{if(d||"cached"!==l.status||0===f)return;let e=0,s=()=>{if(e=0,x.current)return;let s=document.documentElement.scrollHeight-window.innerHeight;s<=0||window.scrollY>=s-24&&(x.current=!0,m("audit_page_scrolled_to_end",{results_count:y,transcripts_scanned:f}))},a=()=>{0===e&&(e=requestAnimationFrame(s))};return s(),window.addEventListener("scroll",a,{passive:!0}),()=>{window.removeEventListener("scroll",a),0!==e&&cancelAnimationFrame(e)}},[l.status,m,y,d,f]),"empty"===l.status&&!d)return(0,s.jsx)(N,{running:!1,rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}});if("empty"===l.status&&d)return(0,s.jsx)(N,{running:!0,rerunStatus:u,onDismissRerun:k,onStarted:()=>{},onCompleted:async()=>{o(!1),await b()}});let w="cached"===l.status?l.result:null;if(!w)return null;let S="cached"===l.status?l.cachedAt:null;return 0===w.transcripts.scanned?(0,s.jsx)(N,{running:d,mode:"zero-sessions",rerunStatus:u,onDismissRerun:k,onStarted:()=>o(!0),onCompleted:async()=>{o(!1),await b()}}):(0,s.jsx)(j,{result:w,cachedAt:S,projectFromUrl:n,isRunning:d,rerunStatus:u,onRerun:e=>v(e),onDismissRerun:k})}],24358)},86616,29054,e=>{"use strict";function s(e,a={},n=15e3){let t=AbortSignal.timeout(n),r=a.signal?AbortSignal.any([a.signal,t]):t;return fetch(e,{...a,signal:r})}function a(e){if(null===e||"object"!=typeof e)return!1;let s=e.name;return"AbortError"===s||"TimeoutError"===s}e.s(["fetchWithTimeout",0,s,"isAbortError",0,a],29054);class n extends Error{kind;constructor(e,s){super(s),this.kind=e,this.name="RerunError"}}async function t(e){try{let a=await s("/api/audit/run",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({cli:e.cli.length>0?e.cli:void 0,since:"all"===e.since?void 0:e.since,noCache:!!e.noCache||void 0})});if(!a.ok&&409!==a.status){let e=await a.text().catch(()=>"");throw console.error("audit run failed:",a.status,e),new n("post_failed",`audit run failed (${a.status})`)}}catch(e){if(e instanceof n)throw e;throw console.error("audit run request failed:",e),new n(a(e)?"timeout":"network","audit run request failed")}let t=0;for(;;){await new Promise(e=>setTimeout(e,1e3));let e=null;try{let a=await s("/api/audit/status",{cache:"no-store"});a.ok&&(e=await a.json())}catch{}if(null===e){if(++t>=10)throw new n("network","audit status poll lost the server");continue}if(t=0,!e.running){if(e.error)throw new n("post_failed",e.error);return}}}e.s(["RerunError",0,n,"triggerRun",0,t],86616)}]);
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
"use server";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
activeFindings,
|
|
5
|
+
dismissFinding,
|
|
6
|
+
readLeakRecord,
|
|
7
|
+
} from "@/src/audit/leak-store";
|
|
4
8
|
import { readConfig, updateConfig } from "@/src/hooks/fp-config";
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { readConfig, updateConfig } from "@/src/hooks/fp-config";
|
|
21
|
-
import { readAuth, whoAmI } from "@/lib/auth/auth-store";
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
* The outcome of trying to turn scheduling on.
|
|
@@ -34,70 +33,19 @@ import { readAuth, whoAmI } from "@/lib/auth/auth-store";
|
|
|
34
33
|
* A returned discriminant survives the boundary, so the caller can open the
|
|
35
34
|
* sign-in dialog for the one failure that has an obvious next step.
|
|
36
35
|
*/
|
|
37
|
-
export type SetAutoAuditResult =
|
|
38
|
-
| { ok: true; auto: boolean }
|
|
39
|
-
| { ok: false; reason: "signed-out" }
|
|
40
|
-
/**
|
|
41
|
-
* The session is intact locally and the api-server could not be reached.
|
|
42
|
-
*
|
|
43
|
-
* Separated from `signed-out` because `whoAmI()` collapses them: it returns
|
|
44
|
-
* null for a 401 AND for every transport failure, so an offline machine, a
|
|
45
|
-
* proxy that blocks the host, and a genuinely expired token were one answer.
|
|
46
|
-
* The user was told "that sign-in expired" and handed a code prompt that
|
|
47
|
-
* cannot succeed either — the failure it names is not the failure they have,
|
|
48
|
-
* and following its advice costs them a real, working session.
|
|
49
|
-
*/
|
|
50
|
-
| { ok: false; reason: "unreachable" };
|
|
36
|
+
export type SetAutoAuditResult = { ok: true; auto: boolean };
|
|
51
37
|
|
|
52
38
|
/**
|
|
53
39
|
* Turn the scheduled scan on or off.
|
|
54
40
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* so a machine with the timer set and nobody to tell is a switch that reads as
|
|
58
|
-
* on and produces nothing, discoverable only by noticing that no digest ever
|
|
59
|
-
* arrives. The caller signs the user in first and retries.
|
|
60
|
-
*
|
|
61
|
-
* `whoAmI()` asks the SERVER, so this refuses in a case the page cannot see: a
|
|
62
|
-
* session file that exists locally but whose refresh token the api-server has
|
|
63
|
-
* rejected. The local file is what the page reads to show "reports go to …", so
|
|
64
|
-
* the two disagree exactly when a session has expired or was minted against a
|
|
65
|
-
* different server — and that disagreement is the common case, not an edge one.
|
|
66
|
-
*
|
|
67
|
-
* Turning it OFF never checks. An expired session must not be able to trap
|
|
68
|
-
* somebody into keeping a feature they are trying to disable.
|
|
69
|
-
*
|
|
70
|
-
* Note this gates SETTING UP the timer, not the machine's ongoing work: a
|
|
71
|
-
* session that later expires leaves the timer running and the local scan
|
|
72
|
-
* working, and only the digest stops. See `report-harm.ts`.
|
|
41
|
+
* Scheduling is local and independent of authentication. A signed-in identity
|
|
42
|
+
* adds email delivery; without one the scan and desktop notification continue.
|
|
73
43
|
*
|
|
74
44
|
* Returns the value actually stored (re-read), so an optimistic UI can confirm
|
|
75
45
|
* against the source of truth rather than assume its own guess landed.
|
|
76
46
|
*/
|
|
77
47
|
export async function setAutoAuditAction(enabled: boolean): Promise<SetAutoAuditResult> {
|
|
78
|
-
|
|
79
|
-
const who = await whoAmI();
|
|
80
|
-
if (!who) {
|
|
81
|
-
// `whoAmI()` deletes the session on a 401 and leaves it alone on a
|
|
82
|
-
// transport failure, so what is left ON DISK is the one thing that tells
|
|
83
|
-
// the two apart — no extra request, and nothing new that can fail.
|
|
84
|
-
const local = readAuth();
|
|
85
|
-
const stillWithinWindow = local && local.refresh_expires_at * 1000 > Date.now();
|
|
86
|
-
return { ok: false, reason: stillWithinWindow ? "unreachable" : "signed-out" };
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// Enabling stamps consent in the same write, because the `whoAmI()` above is
|
|
90
|
-
// exactly what makes this a consent record rather than a guess: a person was
|
|
91
|
-
// present, signed in, and looking at the panel that enumerates what gets
|
|
92
|
-
// sent. `reportHarm` gates sending on the stamp, not on `auto`, so a machine
|
|
93
|
-
// that inherited `auto` from a release where it meant "scan locally on a
|
|
94
|
-
// timer" mails nothing until somebody passes through here or the CLI.
|
|
95
|
-
//
|
|
96
|
-
// Disabling leaves the stamp alone. It is a record of something that did
|
|
97
|
-
// happen, and it grants nothing on its own — sending needs `auto` too.
|
|
98
|
-
const next = updateConfig({
|
|
99
|
-
audit: enabled ? { auto: true, reportsConsentedAt: Date.now() } : { auto: false },
|
|
100
|
-
});
|
|
48
|
+
const next = updateConfig({ audit: { auto: enabled } });
|
|
101
49
|
return { ok: true, auto: next.audit.auto };
|
|
102
50
|
}
|
|
103
51
|
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
* This keeps the dashboard UI and the CLI consistent regardless of whether
|
|
8
8
|
* the api-server is reachable.
|
|
9
9
|
*
|
|
10
|
-
* Reminders are gone: the machine now audits itself on a timer and
|
|
11
|
-
*
|
|
10
|
+
* Reminders are gone: the machine now audits itself on a timer and can mail an
|
|
11
|
+
* alert when it finds a credential leak, so there is nothing to nudge anyone
|
|
12
|
+
* about. The
|
|
12
13
|
* scheduled-scan state lives in `getScheduledAuditAction`, which reads it from
|
|
13
14
|
* the config and the daemon rather than from here.
|
|
14
15
|
*/
|
|
@@ -59,7 +59,9 @@ type Initial =
|
|
|
59
59
|
/** Tag passed to the shared `startRerun()` handler so PostHog can tell
|
|
60
60
|
* whether the click came from the bottom return-section button or the
|
|
61
61
|
* empty-state CTA. */
|
|
62
|
-
|
|
62
|
+
// `scan_header` is the button in section 01 — the only re-scan control the
|
|
63
|
+
// page has now that the old report's sections are switched off.
|
|
64
|
+
export type RerunSource = "return_section" | "empty_state" | "scan_header";
|
|
63
65
|
|
|
64
66
|
interface Props {
|
|
65
67
|
initial: Initial;
|
|
@@ -365,6 +367,8 @@ function MainReport({
|
|
|
365
367
|
transcripts={result.transcripts.scanned}
|
|
366
368
|
events={result.eventsScanned ?? 0}
|
|
367
369
|
projects={result.projectsScanned.length}
|
|
370
|
+
isRunning={isRunning || rerunStatus.kind === "running"}
|
|
371
|
+
onRerun={() => onRerun("scan_header")}
|
|
368
372
|
/>
|
|
369
373
|
{/* The rebuilt report. It reads the leak record rather than this
|
|
370
374
|
scan's result, deliberately: a credential found last month and
|
|
@@ -417,7 +421,19 @@ function MainReport({
|
|
|
417
421
|
* under construction" and "this product is broken".
|
|
418
422
|
*/
|
|
419
423
|
function AuditReportPlaceholder(
|
|
420
|
-
{
|
|
424
|
+
{
|
|
425
|
+
transcripts,
|
|
426
|
+
events,
|
|
427
|
+
projects,
|
|
428
|
+
isRunning,
|
|
429
|
+
onRerun,
|
|
430
|
+
}: {
|
|
431
|
+
transcripts: number;
|
|
432
|
+
events: number;
|
|
433
|
+
projects: number;
|
|
434
|
+
isRunning: boolean;
|
|
435
|
+
onRerun: () => void;
|
|
436
|
+
},
|
|
421
437
|
) {
|
|
422
438
|
const n = (x: number) => x.toLocaleString();
|
|
423
439
|
return (
|
|
@@ -426,6 +442,17 @@ function AuditReportPlaceholder(
|
|
|
426
442
|
<span className="audit-sec-eyebrow">
|
|
427
443
|
<span className="ix">01</span>{"// scan"}
|
|
428
444
|
</span>
|
|
445
|
+
{/* The re-scan control. It lived in the old report's sections and went
|
|
446
|
+
dormant when they were commented out, which left the page with no way
|
|
447
|
+
to run an audit again at all — you had to go back to the terminal. */}
|
|
448
|
+
<button
|
|
449
|
+
type="button"
|
|
450
|
+
className="scan-rerun"
|
|
451
|
+
onClick={onRerun}
|
|
452
|
+
disabled={isRunning}
|
|
453
|
+
>
|
|
454
|
+
{isRunning ? "scanning…" : "run again"}
|
|
455
|
+
</button>
|
|
429
456
|
</div>
|
|
430
457
|
<h2 className="audit-sec-title">scan complete</h2>
|
|
431
458
|
<div className="audit-sec-sub">
|
|
@@ -140,14 +140,27 @@ export function LeakSection() {
|
|
|
140
140
|
{/* The 5W1H, as one sentence rather than a grid of labelled
|
|
141
141
|
cells — a person reading a leak wants the story, and the
|
|
142
142
|
grid version made every row look like a form to fill in. */}
|
|
143
|
+
{/* The date is FIRST seen, not last. "When did this key get
|
|
144
|
+
into a transcript" is the question a leak raises; "when did
|
|
145
|
+
I last run a scan that noticed" is not. They also looked
|
|
146
|
+
identical in practice — the record keeps the 500 most
|
|
147
|
+
recently seen findings, so everything surviving the cap
|
|
148
|
+
reported the same recent `lastSeen` and every row read
|
|
149
|
+
"today", which is true and useless. */}
|
|
143
150
|
<div className="leak-how">
|
|
144
151
|
<span className="leak-verb">{row.mechanism}</span>
|
|
145
152
|
{" in "}
|
|
146
153
|
<span className="leak-where">{row.project}</span>
|
|
147
154
|
{" via "}
|
|
148
155
|
<span className="leak-who">{row.cli}</span>
|
|
149
|
-
{" · "}
|
|
150
|
-
<span className="leak-when">{when(row.
|
|
156
|
+
{" · leaked "}
|
|
157
|
+
<span className="leak-when">{when(row.firstSeen)}</span>
|
|
158
|
+
{when(row.firstSeen) !== when(row.lastSeen) ? (
|
|
159
|
+
<>
|
|
160
|
+
{" · last seen "}
|
|
161
|
+
<span className="leak-when">{when(row.lastSeen)}</span>
|
|
162
|
+
</>
|
|
163
|
+
) : null}
|
|
151
164
|
</div>
|
|
152
165
|
|
|
153
166
|
<div className="leak-foot">
|
|
@@ -1270,3 +1270,21 @@
|
|
|
1270
1270
|
.leak-dismiss { margin-left: 0; }
|
|
1271
1271
|
.leak-foot { gap: 6px; flex-direction: column; }
|
|
1272
1272
|
}
|
|
1273
|
+
|
|
1274
|
+
/* The re-scan control in the scan header. Sits at the end of the eyebrow row so
|
|
1275
|
+
it reads as an action on that section rather than a page-level nav item. */
|
|
1276
|
+
.scan-rerun {
|
|
1277
|
+
margin-left: auto;
|
|
1278
|
+
font-family: var(--font-mono);
|
|
1279
|
+
font-size: 10.5px;
|
|
1280
|
+
letter-spacing: 0.1em;
|
|
1281
|
+
text-transform: lowercase;
|
|
1282
|
+
color: var(--ink-2);
|
|
1283
|
+
background: none;
|
|
1284
|
+
border: 1px solid var(--line-2);
|
|
1285
|
+
padding: 4px 10px;
|
|
1286
|
+
cursor: pointer;
|
|
1287
|
+
}
|
|
1288
|
+
.scan-rerun:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-pink); }
|
|
1289
|
+
.scan-rerun:disabled { opacity: 0.5; cursor: not-allowed; }
|
|
1290
|
+
.scan-rerun:focus-visible { outline: 2px solid var(--accent-pink); outline-offset: 2px; }
|
|
@@ -30,7 +30,7 @@ export const dynamic = "force-dynamic";
|
|
|
30
30
|
export default async function SettingsPage() {
|
|
31
31
|
// Same gate the audit, policies and projects pages carry. It was dropped in
|
|
32
32
|
// the rewrite, and this is the page that least deserves to lose it: it shows
|
|
33
|
-
// the address
|
|
33
|
+
// the address credential alerts go to and can sign the machine out, on a dashboard an
|
|
34
34
|
// operator may deliberately be exposing beyond localhost.
|
|
35
35
|
const disabled = (process.env.FAILPROOFAI_DISABLE_PAGES ?? "")
|
|
36
36
|
.split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -187,12 +187,9 @@ function Toggle({
|
|
|
187
187
|
* What the scan does, as three labelled lines. This is the old footer paragraph
|
|
188
188
|
* restructured — same claims, scannable instead of a wall.
|
|
189
189
|
*
|
|
190
|
-
* "sends"
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* owner's. A list a person can check beats a stronger claim they cannot, and
|
|
194
|
-
* this panel is the one place they would come to check. The digest email states
|
|
195
|
-
* the same three, in the same order.
|
|
190
|
+
* "sends" names the exact remote payload. The scheduled scan itself stays
|
|
191
|
+
* local, and no report is attempted unless a credential leak exists and the
|
|
192
|
+
* machine has a verified email identity.
|
|
196
193
|
*/
|
|
197
194
|
const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
|
|
198
195
|
{
|
|
@@ -202,7 +199,7 @@ const HOW_IT_WORKS: ReadonlyArray<{ label: string; body: string }> = [
|
|
|
202
199
|
{ label: "runs", body: "entirely on this machine. the transcripts never leave it." },
|
|
203
200
|
{
|
|
204
201
|
label: "sends",
|
|
205
|
-
body: "
|
|
202
|
+
body: "only the newest masked credential exposure, its CLI, time, location, and this machine's name — only when email is configured.",
|
|
206
203
|
},
|
|
207
204
|
];
|
|
208
205
|
|
|
@@ -312,26 +309,6 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
312
309
|
setBusy(true);
|
|
313
310
|
try {
|
|
314
311
|
const res = await setAutoAuditAction(true);
|
|
315
|
-
if (!res.ok) {
|
|
316
|
-
setAuto(false);
|
|
317
|
-
if (res.reason === "unreachable") {
|
|
318
|
-
// The session is fine and the network is not. Offering a code prompt
|
|
319
|
-
// here would name a failure the user does not have and hand them a
|
|
320
|
-
// flow that cannot succeed either — and abandoning it mid-way is how
|
|
321
|
-
// a working session gets replaced with none at all.
|
|
322
|
-
toast("could not reach the server. check your connection and try again.");
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
// The server rejected the session this page had been showing an address
|
|
326
|
-
// for — expired, or minted against a different api-server. The local
|
|
327
|
-
// file is the only thing that said "signed in", and `whoAmI` has since
|
|
328
|
-
// cleared it, so re-read before opening the dialog: otherwise the page
|
|
329
|
-
// asks for an email while still displaying one.
|
|
330
|
-
await reload();
|
|
331
|
-
setAuthOpen(true);
|
|
332
|
-
toast("that sign-in expired. one more code and it's on.");
|
|
333
|
-
return;
|
|
334
|
-
}
|
|
335
312
|
setAuto(res.auto);
|
|
336
313
|
toast("scheduled audits on.");
|
|
337
314
|
await reload();
|
|
@@ -360,13 +337,8 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
360
337
|
}
|
|
361
338
|
return;
|
|
362
339
|
}
|
|
363
|
-
// Turning it on needs somewhere to send the digest.
|
|
364
|
-
if (!signedIn) {
|
|
365
|
-
setAuthOpen(true);
|
|
366
|
-
return;
|
|
367
|
-
}
|
|
368
340
|
await enable();
|
|
369
|
-
}, [auto, enable, reload
|
|
341
|
+
}, [auto, enable, reload]);
|
|
370
342
|
|
|
371
343
|
const commitInterval = useCallback(
|
|
372
344
|
async (raw: number) => {
|
|
@@ -403,7 +375,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
403
375
|
setBusy(true);
|
|
404
376
|
try {
|
|
405
377
|
await fetch("/api/auth/logout", { method: "POST" });
|
|
406
|
-
toast("signed out. scans continue;
|
|
378
|
+
toast("signed out. scans and desktop notifications continue; email alerts pause.");
|
|
407
379
|
await reload();
|
|
408
380
|
} catch {
|
|
409
381
|
toast("could not sign out.");
|
|
@@ -534,7 +506,9 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
534
506
|
</span>
|
|
535
507
|
<span className="set-dim">
|
|
536
508
|
{auto
|
|
537
|
-
?
|
|
509
|
+
? signedIn
|
|
510
|
+
? "system notification on leaks; the latest masked leak is also emailed."
|
|
511
|
+
: "system notification on leaks. add an email below for masked alerts."
|
|
538
512
|
: "off. nothing runs and nothing is sent."}
|
|
539
513
|
</span>
|
|
540
514
|
</div>
|
|
@@ -593,7 +567,7 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
593
567
|
// from it at all.
|
|
594
568
|
<>
|
|
595
569
|
<span className="set-warn">
|
|
596
|
-
signed out — scans continue
|
|
570
|
+
signed out — scans and desktop notifications continue; email alerts are paused.
|
|
597
571
|
</span>
|
|
598
572
|
<button
|
|
599
573
|
type="button"
|
|
@@ -601,13 +575,13 @@ export default function SettingsClient({ initial }: { initial: ScheduledAuditVie
|
|
|
601
575
|
disabled={busy}
|
|
602
576
|
onClick={() => setAuthOpen(true)}
|
|
603
577
|
>
|
|
604
|
-
|
|
578
|
+
add email alerts
|
|
605
579
|
</button>
|
|
606
580
|
</>
|
|
607
581
|
) : (
|
|
608
582
|
<span className="set-dim">
|
|
609
|
-
|
|
610
|
-
|
|
583
|
+
scans and desktop notifications work without an account.
|
|
584
|
+
add email later if you want leak alerts in your inbox.
|
|
611
585
|
</span>
|
|
612
586
|
)}
|
|
613
587
|
</div>
|
|
@@ -257,7 +257,7 @@ export interface AuditReportBody {
|
|
|
257
257
|
* Credentials this machine found in its own transcripts.
|
|
258
258
|
*
|
|
259
259
|
* Optional on the wire, so a CLI newer than the api-server sends a field the
|
|
260
|
-
* server ignores rather than failing the whole
|
|
260
|
+
* server ignores rather than failing the whole report — the harmful counts
|
|
261
261
|
* are the older half of the contract and must keep arriving either way.
|
|
262
262
|
*
|
|
263
263
|
* Every field here is a mask, a label, a length or a location. There is no
|
|
@@ -287,15 +287,15 @@ export interface AuditReportResult {
|
|
|
287
287
|
report_id: string;
|
|
288
288
|
/** Whether this report produced an email. */
|
|
289
289
|
emailed: boolean;
|
|
290
|
-
/** `
|
|
290
|
+
/** `no_leak`, `cooldown`, `send_failed`, or null when mail went out. */
|
|
291
291
|
reason: string | null;
|
|
292
292
|
/**
|
|
293
293
|
* Where the next window starts, per the SERVER.
|
|
294
294
|
*
|
|
295
295
|
* Persisted verbatim rather than computed locally. The server anchors it on
|
|
296
|
-
* the last DELIVERED
|
|
296
|
+
* the last DELIVERED alert, so a report held by the cooldown — or one whose
|
|
297
297
|
* send failed — correctly leaves the watermark where it was, and its findings
|
|
298
|
-
* turn up in the next
|
|
298
|
+
* turn up in the next alert instead of falling into a gap. A machine that
|
|
299
299
|
* lost `machine.json` also resyncs here rather than re-reporting from the
|
|
300
300
|
* beginning of time.
|
|
301
301
|
*/
|
|
@@ -303,11 +303,11 @@ export interface AuditReportResult {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
|
-
* Submit one scheduled scan's harmful findings.
|
|
306
|
+
* Submit one scheduled scan's harmful findings and newest masked credential.
|
|
307
307
|
*
|
|
308
308
|
* Called only by the audit child, and only on `--scheduled`. The destination
|
|
309
309
|
* address is never sent: the api-server takes it from the access-token claims,
|
|
310
|
-
* so a report cannot name where its
|
|
310
|
+
* so a report cannot name where its email alert goes.
|
|
311
311
|
*/
|
|
312
312
|
export async function submitAuditReport(
|
|
313
313
|
accessToken: string,
|
|
@@ -158,6 +158,21 @@ function joinTexts(blocks: CodexContentBlock[] | undefined, wantedType: "input_t
|
|
|
158
158
|
.join("\n");
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
/** Text carried by tool outputs. Newer Codex chronicles use an array of
|
|
162
|
+
* `input_text` blocks for `custom_tool_call_output`, while older releases use
|
|
163
|
+
* one string for `function_call_output`. */
|
|
164
|
+
function toolOutputText(output: unknown): string {
|
|
165
|
+
if (typeof output === "string") return output;
|
|
166
|
+
if (Array.isArray(output)) {
|
|
167
|
+
return output
|
|
168
|
+
.filter((block): block is CodexContentBlock => !!block && typeof block === "object")
|
|
169
|
+
.map((block) => typeof block.text === "string" ? block.text : "")
|
|
170
|
+
.filter(Boolean)
|
|
171
|
+
.join("\n");
|
|
172
|
+
}
|
|
173
|
+
return output === undefined ? "" : JSON.stringify(output);
|
|
174
|
+
}
|
|
175
|
+
|
|
161
176
|
/**
|
|
162
177
|
* Parse a Codex JSONL transcript into `LogEntry[]` plus the raw lines.
|
|
163
178
|
* Yields to the event loop every 200 lines so big transcripts don't block.
|
|
@@ -250,10 +265,11 @@ export async function parseCodexLog(
|
|
|
250
265
|
continue;
|
|
251
266
|
}
|
|
252
267
|
|
|
253
|
-
if (subType === "function_call") {
|
|
268
|
+
if (subType === "function_call" || subType === "custom_tool_call") {
|
|
254
269
|
const callId = payload.call_id as string | undefined;
|
|
255
270
|
const name = (payload.name as string | undefined) ?? "function_call";
|
|
256
|
-
const
|
|
271
|
+
const encodedInput = (payload.arguments ?? payload.input) as string | undefined;
|
|
272
|
+
const input = safeJsonParse(encodedInput);
|
|
257
273
|
const toolUse: ToolUseBlock = {
|
|
258
274
|
type: "tool_use",
|
|
259
275
|
id: callId ?? `${timestamp}-${name}`,
|
|
@@ -273,7 +289,7 @@ export async function parseCodexLog(
|
|
|
273
289
|
continue;
|
|
274
290
|
}
|
|
275
291
|
|
|
276
|
-
if (subType === "function_call_output") {
|
|
292
|
+
if (subType === "function_call_output" || subType === "custom_tool_call_output") {
|
|
277
293
|
const callId = payload.call_id as string | undefined;
|
|
278
294
|
const block = callId ? toolUseById.get(callId) : undefined;
|
|
279
295
|
if (block) {
|
|
@@ -282,7 +298,7 @@ export async function parseCodexLog(
|
|
|
282
298
|
block.result = {
|
|
283
299
|
timestamp,
|
|
284
300
|
timestampFormatted: formatTimestamp(date),
|
|
285
|
-
content:
|
|
301
|
+
content: toolOutputText(payload.output),
|
|
286
302
|
durationMs: duration,
|
|
287
303
|
durationFormatted: formatDuration(duration),
|
|
288
304
|
};
|
|
@@ -151,6 +151,11 @@ interface CopilotToolResult {
|
|
|
151
151
|
detailedContent?: string;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
interface CopilotHookToolResult {
|
|
155
|
+
textResultForLlm?: string;
|
|
156
|
+
sessionLog?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
154
159
|
interface CopilotToolTelemetry {
|
|
155
160
|
metrics?: { commandTimeMs?: number; durationMs?: number };
|
|
156
161
|
properties?: Record<string, unknown>;
|
|
@@ -171,6 +176,8 @@ export async function parseCopilotLog(
|
|
|
171
176
|
// toolCallId → tool_use block, so we can attach tool.execution_complete back.
|
|
172
177
|
const toolUseById = new Map<string, ToolUseBlock>();
|
|
173
178
|
const toolUseStartMs = new Map<string, number>();
|
|
179
|
+
let mostRecentToolUse: ToolUseBlock | undefined;
|
|
180
|
+
let mostRecentToolUseStartMs = 0;
|
|
174
181
|
let cwd: string | undefined;
|
|
175
182
|
let seenSessionStart = false;
|
|
176
183
|
|
|
@@ -273,9 +280,32 @@ export async function parseCopilotLog(
|
|
|
273
280
|
toolUseById.set(callId, toolUse);
|
|
274
281
|
toolUseStartMs.set(callId, date.getTime());
|
|
275
282
|
}
|
|
283
|
+
mostRecentToolUse = toolUse;
|
|
284
|
+
mostRecentToolUseStartMs = date.getTime();
|
|
276
285
|
continue;
|
|
277
286
|
}
|
|
278
287
|
|
|
288
|
+
// Copilot CLI 1.0.39 can omit the command's real stdout from
|
|
289
|
+
// `tool.execution_complete` and expose it only to the PostToolUse hook as
|
|
290
|
+
// `input.toolResult.textResultForLlm`. That is the value the model saw and
|
|
291
|
+
// the value failproofai must audit. Keep it on the active tool block; the
|
|
292
|
+
// later completion record may still contribute timing, but must not replace
|
|
293
|
+
// this output with its shorter status-only summary.
|
|
294
|
+
if (recType === "hook.start" && data.hookType === "postToolUse" && mostRecentToolUse) {
|
|
295
|
+
const input = (data.input as Record<string, unknown> | undefined) ?? {};
|
|
296
|
+
const hookResult = (input.toolResult as CopilotHookToolResult | undefined) ?? {};
|
|
297
|
+
const hookContent = hookResult.textResultForLlm ?? hookResult.sessionLog;
|
|
298
|
+
if (typeof hookContent === "string" && hookContent.length > 0) {
|
|
299
|
+
mostRecentToolUse.result = {
|
|
300
|
+
timestamp,
|
|
301
|
+
timestampFormatted: formatTimestamp(date),
|
|
302
|
+
content: hookContent,
|
|
303
|
+
durationMs: Math.max(0, date.getTime() - mostRecentToolUseStartMs),
|
|
304
|
+
durationFormatted: formatDuration(Math.max(0, date.getTime() - mostRecentToolUseStartMs)),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
279
309
|
if (recType === "tool.execution_complete") {
|
|
280
310
|
const callId = data.toolCallId as string | undefined;
|
|
281
311
|
const block = callId ? toolUseById.get(callId) : undefined;
|
|
@@ -289,7 +319,8 @@ export async function parseCopilotLog(
|
|
|
289
319
|
typeof reportedMs === "number" && reportedMs >= 0
|
|
290
320
|
? reportedMs
|
|
291
321
|
: Math.max(0, date.getTime() - startMs);
|
|
292
|
-
const
|
|
322
|
+
const completionContent = result.detailedContent ?? result.content ?? "";
|
|
323
|
+
const content = block.result?.content || completionContent;
|
|
293
324
|
block.result = {
|
|
294
325
|
timestamp,
|
|
295
326
|
timestampFormatted: formatTimestamp(date),
|