feat(analytics): instrument official templates with PostHog - #60
Conversation
Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
* feat: add Insight Flow Agent chat template * refactor: move agent config behind chat UI * refactor: simplify agent key storage * fix: harden streaming agent template * fix: normalize agent endpoint hosts * fix: surface function errors in the template --------- Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
* feat: classify and gate publishable templates * fix: keep agent chat template on npm --------- Co-authored-by: bruinxz <bruinxz@bruinxzdeMacBook-Pro.local>
|
Thanks for the PR, @xiongxz! A quick note on our workflow: we ask contributors to open an issue first, get it assigned, then submit a PR that links it (e.g. "Closes #123"). This PR isn't linked to any issue. It'll still be reviewed, but please open an issue and claim it (comment that you'd like it assigned to you) so the work is tracked. |
There was a problem hiding this comment.
40 issues found across 181 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="chatbot/lib/analytics.ts">
<violation number="1" location="chatbot/lib/analytics.ts:30">
P2: When a caller passes camelCase PII keys or custom URL properties, this guard forwards them to PostHog; only snake_case keys and four built-in URL keys are sanitized. Normalize key matching and sanitize or reject URL-valued custom properties before capture.</violation>
<violation number="2" location="chatbot/lib/analytics.ts:77">
P1: In sampled chatbot replays, rendered user prompts, assistant responses, and attachment names remain readable because the UI has no `data-private` region. Mark the transcript and attachments private, or disable replay for this template.</violation>
<violation number="3" location="chatbot/lib/analytics.ts:110">
P2: These helpers are never called in the chatbot, so successful sign-ins, sign-ups, and other conversion actions emit no semantic events. Wire them into the relevant success and interaction paths, or remove the unused contract from this template.</violation>
</file>
<file name="e-commerce/lib/analytics.ts">
<violation number="1" location="e-commerce/lib/analytics.ts:8">
P1: CamelCase PII keys bypass the final privacy guard and can send names or phone numbers to PostHog. Normalize property keys before matching, or restrict `AnalyticsProperties` to an approved non-PII schema.</violation>
<violation number="2" location="e-commerce/lib/analytics.ts:41">
P1: Custom URL properties bypass query and fragment stripping, so event callers can send sensitive URL parameters to PostHog. Sanitize every URL-valued property, not only PostHog’s four automatic URL keys.</violation>
<violation number="3" location="e-commerce/lib/analytics.ts:77">
P1: Sampled replay captures customer email, address, recipient, and phone text in this template because no rendered regions use `data-private`. Mark every customer-content region before enabling replay, or configure replay to mask text by default.</violation>
</file>
<file name="insight-flow-agent-chat/src/lib/analytics.ts">
<violation number="1" location="insight-flow-agent-chat/src/lib/analytics.ts:79">
P1: When a session is sampled for replay, customer prompts, agent replies, and the displayed account email are recorded in plain text because this template marks none of them `data-private`. Mask these rendered-content selectors or disable replay for this template.</violation>
<violation number="2" location="insight-flow-agent-chat/src/lib/analytics.ts:111">
P2: The semantic analytics API is never used in this template, so login, form, and conversion events never reach PostHog. Wire the helpers into the successful auth and domain flows.</violation>
</file>
<file name="admin-dashboard/src/lib/analytics.ts">
<violation number="1" location="admin-dashboard/src/lib/analytics.ts:39">
P1: When a custom event contains a URL under any key other than the four PostHog URL keys, this sanitizer leaves query parameters and fragments intact. Sanitize every absolute URL-valued property here so custom analytics cannot send tokenized or otherwise sensitive URLs.</violation>
<violation number="2" location="admin-dashboard/src/lib/analytics.ts:73">
P1: The dashboard enables sampled replay without marking its rendered customer content as private, so replay can record emails, task details, and chat messages even though inputs are masked. Add `data-private` to every customer-content region before enabling replay, or disable replay until that coverage exists.</violation>
<violation number="3" location="admin-dashboard/src/lib/analytics.ts:112">
P2: This dashboard initializes PostHog but never calls the exported semantic helpers, so production records automatic events without login, signup, form, or purchase conversions. Wire these helpers into the corresponding successful flows before claiming semantic conversion instrumentation.</violation>
</file>
<file name="web-research-agent/src/main.tsx">
<violation number="1" location="web-research-agent/src/main.tsx:88">
P2: When a connected workspace is loading or its initial query fails, the UI shows the public showcase data as if it were the user's research. Initialize empty workspace state when `connected` and reserve `demo*` state for showcase mode.</violation>
<violation number="2" location="web-research-agent/src/main.tsx:171">
P1: Sampled session replay records the workspace's customer research content unmasked because it is rendered outside form controls. Mark this workspace root `data-private` before rendering user data.</violation>
</file>
<file name="website-change-monitor/src/lib/analytics.ts">
<violation number="1" location="website-change-monitor/src/lib/analytics.ts:39">
P1: When an event includes a URL under any property other than the four PostHog defaults, its query and hash are sent unchanged. Sanitize every HTTP(S) URL-valued property, not only the built-in URL keys.</violation>
<violation number="2" location="website-change-monitor/src/lib/analytics.ts:70">
P1: Sampled replay records the authenticated email and customer content in this template because no rendered sensitive regions use `data-private`. Mark those regions private before enabling replay, or disable replay for this template.</violation>
</file>
<file name="web-research-agent/functions/research-run.ts">
<violation number="1" location="web-research-agent/functions/research-run.ts:88">
P1: When the AI returns a missing, non-numeric, or out-of-range source, claim construction either fails the whole run or stores a citation label that disagrees with its source. Normalize the source index and derive `citation_label` from the bounded index before inserting claims.</violation>
<violation number="2" location="web-research-agent/functions/research-run.ts:89">
P1: If inserting claims fails, this line ignores the error, then the function marks the project ready and returns 200 with claims that were not persisted. Check the insert result and throw into the existing failure path.</violation>
<violation number="3" location="web-research-agent/functions/research-run.ts:90">
P2: If the final project update fails, the function still returns success and leaves the project `running`, so the UI can show “Collecting sources” indefinitely. Check this update error and route it through the failure response.</violation>
</file>
<file name="scripts/validate-registry.mjs">
<violation number="1" location="scripts/validate-registry.mjs:79">
P1: Prefixed provider key variables bypass this AI contract check because `_` is a word character before `OPENAI` (and the other provider names). Match provider-key tokens anywhere within uppercase environment variable names so templates cannot evade the platform-managed gateway check with names such as `SERVER_OPENAI_API_KEY`.</violation>
</file>
<file name="booking/lib/analytics.ts">
<violation number="1" location="booking/lib/analytics.ts:39">
P1: When event authors use camelCase PII keys or custom URL properties, this helper sends them unchanged. Normalize property names and sanitize or drop URL-like values before calling PostHog.</violation>
<violation number="2" location="booking/lib/analytics.ts:110">
P2: Booking never calls the semantic helpers defined here, so successful sign-ups, logins, and bookings emit no conversion events. Import `analytics` at the success paths and call the corresponding helper after each operation succeeds.</violation>
</file>
<file name="insight-flow-agent-chat/src/App.tsx">
<violation number="1" location="insight-flow-agent-chat/src/App.tsx:24">
P1: When `getCurrentUser()` rejects, `authLoading` never becomes false, so the app stays on the login-recovery spinner indefinitely. Handle the rejection and clear the loading state in a `finally` block.</violation>
</file>
<file name="crm/lib/analytics.ts">
<violation number="1" location="crm/lib/analytics.ts:8">
P1: The privacy guard misses common camelCase PII keys such as `firstName` and `accessToken`. Normalize property keys before applying `PII_KEY`, or extend the boundary matching to cover camelCase names before sending them to PostHog.</violation>
</file>
<file name="web-research-agent/src/lib/analytics.ts">
<violation number="1" location="web-research-agent/src/lib/analytics.ts:73">
P1: When a production session is selected for replay, this config masks inputs but leaves ordinary DOM text unmasked. The template renders `user?.email` and customer content without `data-private`, so mark those regions private before enabling replay.</violation>
</file>
<file name="todo/src/lib/analytics.ts">
<violation number="1" location="todo/src/lib/analytics.ts:8">
P1: Properties using camelCase or other common naming (`firstName`, `phoneNumber`, `accessToken`) bypass this guard because it only recognizes underscore-delimited tokens. Normalize keys or extend the matcher to cover camelCase before sending properties.</violation>
<violation number="2" location="todo/src/lib/analytics.ts:41">
P1: Custom URL properties retain query strings because this sanitizer only processes PostHog's four reserved URL keys. Sanitize every URL-valued property before capture so custom properties cannot send tokens or query-string PII.</violation>
<violation number="3" location="todo/src/lib/analytics.ts:77">
P1: Sampled recordings can capture users' todo text because this configuration masks only explicitly marked elements, while the todo template marks none. Mark the rendered customer-content region with `data-private`, or disable replay for this template.</violation>
</file>
<file name="scripts/verify-publishable-template.mjs">
<violation number="1" location="scripts/verify-publishable-template.mjs:41">
P2: When `dist` or `out` itself is a symlink, this guard never rejects it because `statSync` follows the link, and `visit` scans its target. Use `lstatSync` and reject the root before traversing it.</violation>
</file>
<file name="web-research-agent/migrations/db_init.sql">
<violation number="1" location="web-research-agent/migrations/db_init.sql:57">
P2: When an authenticated user knows another project’s UUID, these child-table policies allow them to insert a source or claim owned by themselves while attaching it to that project, and claims can point at unrelated sources. Require `project_id` to belong to `auth.uid()` and require each claim’s source to belong to that same project and user in both `using` and `with check`.</violation>
</file>
<file name="react/src/lib/analytics.ts">
<violation number="1" location="react/src/lib/analytics.ts:37">
P2: When a custom event property contains a URL outside PostHog's four reserved URL keys, this helper sends its query and hash unchanged. Sanitize every parseable URL-valued property before capture so `track` cannot emit query-bearing URLs.</violation>
</file>
<file name="insight-flow-agent-chat/src/components/ChatPage.tsx">
<violation number="1" location="insight-flow-agent-chat/src/components/ChatPage.tsx:117">
P2: When a user starts a new chat while a reply is streaming, the aborted request can clear the newer request's streaming state in `finally`. This enables overlapping sends and can corrupt the active chat flow; only clear `isStreaming` when this controller is still current.</violation>
</file>
<file name="website-change-monitor/src/main.tsx">
<violation number="1" location="website-change-monitor/src/main.tsx:64">
P2: When a connected workspace load is pending or fails, the authenticated UI continues showing hard-coded demo targets and changes. Users can click actions such as `Check now` for fake IDs instead of their workspace; use empty initial state when `connected` and clear or replace it on load failure.</violation>
<violation number="2" location="website-change-monitor/src/main.tsx:73">
P2: When `getCurrentUser()` rejects, the app remains stuck on the loading spinner because no handler clears `authLoading`. Catch the request failure and set `authLoading` to `false` so the user can sign in or see an error.</violation>
</file>
<file name="nextjs/src/lib/analytics.ts">
<violation number="1" location="nextjs/src/lib/analytics.ts:97">
P2: When a custom event includes a URL-valued property, `track` can send its query parameters unchanged because the URL sanitizer only handles PostHog's reserved URL keys. Sanitize or reject URL-valued custom properties before capture so query parameters cannot leak.</violation>
</file>
<file name="ai-pdf-chatbot/lib/analytics.ts">
<violation number="1" location="ai-pdf-chatbot/lib/analytics.ts:8">
P2: When an event uses a camelCase PII key, both privacy filters forward the value because `PII_KEY` only recognizes underscore-delimited words. Normalize keys or include camelCase boundaries in this matcher before sending properties.</violation>
</file>
<file name="website-change-monitor/functions/monitor-check.ts">
<violation number="1" location="website-change-monitor/functions/monitor-check.ts:56">
P2: When two checks for the same target overlap, both can read the same previous snapshot before either inserts. Each then records a change from that snapshot, producing duplicate notifications; atomically claim or serialize the target, or make snapshot/change recording idempotent.</violation>
<violation number="2" location="website-change-monitor/functions/monitor-check.ts:73">
P2: When `monitor_changes` insertion fails, this handler still returns 200 with `changed: true`, losing the change record. Check the insert error and fail the run instead of reporting a successful check.</violation>
<violation number="3" location="website-change-monitor/functions/monitor-check.ts:77">
P2: When the final target update fails, the function still returns success while the target can remain `checking`, which excludes it from scheduled checks. Check this update error and return a failed check when the state cannot be persisted.</violation>
</file>
<file name="insight-flow-agent-chat/src/lib/insforge.ts">
<violation number="1" location="insight-flow-agent-chat/src/lib/insforge.ts:3">
P2: Vitest now fails before `config-errors.test.ts` runs because importing `config.ts` evaluates this module in Node, where `window` is undefined. Guard the browser access (or read the value from `globalThis`) so shared config utilities remain importable in tests and non-browser contexts.</violation>
</file>
<file name="insight-flow-agent-chat/functions/insight-flow-config.ts">
<violation number="1" location="insight-flow-agent-chat/functions/insight-flow-config.ts:174">
P2: Valid JSON with a null or incorrectly typed body causes this handler to throw before returning a 4xx response. Parse into `unknown`, validate the object and field types in both PUT and POST branches, then return a validation error before accessing them.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| disable_session_recording: Math.random() >= replaySampleRate, | ||
| session_recording: { | ||
| maskAllInputs: true, | ||
| maskTextSelector: '[data-private]', |
There was a problem hiding this comment.
P1: In sampled chatbot replays, rendered user prompts, assistant responses, and attachment names remain readable because the UI has no data-private region. Mark the transcript and attachments private, or disable replay for this template.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At chatbot/lib/analytics.ts, line 77:
<comment>In sampled chatbot replays, rendered user prompts, assistant responses, and attachment names remain readable because the UI has no `data-private` region. Mark the transcript and attachments private, or disable replay for this template.</comment>
<file context>
@@ -0,0 +1,121 @@
+ disable_session_recording: Math.random() >= replaySampleRate,
+ session_recording: {
+ maskAllInputs: true,
+ maskTextSelector: '[data-private]',
+ blockSelector: '[data-private]',
+ },
</file context>
| disable_session_recording: Math.random() >= replaySampleRate, | ||
| session_recording: { | ||
| maskAllInputs: true, | ||
| maskTextSelector: '[data-private]', |
There was a problem hiding this comment.
P1: Sampled replay captures customer email, address, recipient, and phone text in this template because no rendered regions use data-private. Mark every customer-content region before enabling replay, or configure replay to mask text by default.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At e-commerce/lib/analytics.ts, line 77:
<comment>Sampled replay captures customer email, address, recipient, and phone text in this template because no rendered regions use `data-private`. Mark every customer-content region before enabling replay, or configure replay to mask text by default.</comment>
<file context>
@@ -0,0 +1,121 @@
+ disable_session_recording: Math.random() >= replaySampleRate,
+ session_recording: {
+ maskAllInputs: true,
+ maskTextSelector: '[data-private]',
+ blockSelector: '[data-private]',
+ },
</file context>
| disable_session_recording: Math.random() >= replaySampleRate, | ||
| session_recording: { | ||
| maskAllInputs: true, | ||
| maskTextSelector: '[data-private]', |
There was a problem hiding this comment.
P1: When a session is sampled for replay, customer prompts, agent replies, and the displayed account email are recorded in plain text because this template marks none of them data-private. Mask these rendered-content selectors or disable replay for this template.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At insight-flow-agent-chat/src/lib/analytics.ts, line 79:
<comment>When a session is sampled for replay, customer prompts, agent replies, and the displayed account email are recorded in plain text because this template marks none of them `data-private`. Mask these rendered-content selectors or disable replay for this template.</comment>
<file context>
@@ -0,0 +1,123 @@
+ disable_session_recording: Math.random() >= replaySampleRate,
+ session_recording: {
+ maskAllInputs: true,
+ maskTextSelector: '[data-private]',
+ blockSelector: '[data-private]',
+ },
</file context>
| disable_session_recording: Math.random() >= replaySampleRate, | ||
| session_recording: { | ||
| maskAllInputs: true, | ||
| maskTextSelector: '[data-private]', |
There was a problem hiding this comment.
P1: The dashboard enables sampled replay without marking its rendered customer content as private, so replay can record emails, task details, and chat messages even though inputs are masked. Add data-private to every customer-content region before enabling replay, or disable replay until that coverage exists.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At admin-dashboard/src/lib/analytics.ts, line 73:
<comment>The dashboard enables sampled replay without marking its rendered customer content as private, so replay can record emails, task details, and chat messages even though inputs are masked. Add `data-private` to every customer-content region before enabling replay, or disable replay until that coverage exists.</comment>
<file context>
@@ -0,0 +1,115 @@
+ disable_session_recording: Math.random() >= replaySampleRate,
+ session_recording: {
+ maskAllInputs: true,
+ maskTextSelector: '[data-private]',
+ blockSelector: '[data-private]',
+ },
</file context>
| </div> | ||
| </header> | ||
|
|
||
| <main className="grid min-h-[calc(100vh-4rem)] min-w-0 lg:grid-cols-[270px_minmax(0,1fr)_330px]"> |
There was a problem hiding this comment.
P1: Sampled session replay records the workspace's customer research content unmasked because it is rendered outside form controls. Mark this workspace root data-private before rendering user data.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web-research-agent/src/main.tsx, line 171:
<comment>Sampled session replay records the workspace's customer research content unmasked because it is rendered outside form controls. Mark this workspace root `data-private` before rendering user data.</comment>
<file context>
@@ -0,0 +1,271 @@
+ </div>
+ </header>
+
+ <main className="grid min-h-[calc(100vh-4rem)] min-w-0 lg:grid-cols-[270px_minmax(0,1fr)_330px]">
+ <aside className="min-w-0 overflow-hidden border-b border-[#cfd7e5] bg-[#e9eef7] p-4 lg:border-b-0 lg:border-r lg:p-5">
+ <button className="focus-ring flex w-full items-center justify-center gap-2 bg-[#1f5eff] px-4 py-3 text-sm font-semibold text-white hover:bg-[#164bd3]" onClick={() => setComposerOpen(true)}><Plus size={17} />New research</button>
</file context>
| <main className="grid min-h-[calc(100vh-4rem)] min-w-0 lg:grid-cols-[270px_minmax(0,1fr)_330px]"> | |
| <main data-private className="grid min-h-[calc(100vh-4rem)] min-w-0 lg:grid-cols-[270px_minmax(0,1fr)_330px]"> |
| } | ||
| const now = new Date(); | ||
| const next = new Date(now.getTime() + Number(target.interval_minutes) * 60000); | ||
| await client.database.from("monitor_targets").update({ status: changed ? "changed" : "quiet", last_hash: hash, last_checked_at: now.toISOString(), next_check_at: next.toISOString(), error_message: "" }).eq("id", target.id); |
There was a problem hiding this comment.
P2: When the final target update fails, the function still returns success while the target can remain checking, which excludes it from scheduled checks. Check this update error and return a failed check when the state cannot be persisted.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website-change-monitor/functions/monitor-check.ts, line 77:
<comment>When the final target update fails, the function still returns success while the target can remain `checking`, which excludes it from scheduled checks. Check this update error and return a failed check when the state cannot be persisted.</comment>
<file context>
@@ -0,0 +1,84 @@
+ }
+ const now = new Date();
+ const next = new Date(now.getTime() + Number(target.interval_minutes) * 60000);
+ await client.database.from("monitor_targets").update({ status: changed ? "changed" : "quiet", last_hash: hash, last_checked_at: now.toISOString(), next_check_at: next.toISOString(), error_message: "" }).eq("id", target.id);
+ return json(200, { targetId: target.id, changed, snapshotId: snapshots[0].id, fetchMode: page.mode });
+ } catch (error) {
</file context>
| await client.database.from("monitor_targets").update({ status: changed ? "changed" : "quiet", last_hash: hash, last_checked_at: now.toISOString(), next_check_at: next.toISOString(), error_message: "" }).eq("id", target.id); | |
| const { error: targetUpdateError } = await client.database.from("monitor_targets").update({ status: changed ? "changed" : "quiet", last_hash: hash, last_checked_at: now.toISOString(), next_check_at: next.toISOString(), error_message: "" }).eq("id", target.id); | |
| if (targetUpdateError) throw new Error(targetUpdateError.message || "Target state was not saved"); |
| changeType = String(parsed.changeType || changeType).slice(0, 80); | ||
| significance = ["low", "medium", "high"].includes(parsed.significance) ? parsed.significance : significance; | ||
| } catch { /* A deterministic change record is still created. */ } | ||
| await client.database.from("monitor_changes").insert([{ target_id: target.id, previous_snapshot_id: previous.id, current_snapshot_id: snapshots[0].id, summary, change_type: changeType, significance, before_excerpt: previous.content_excerpt, after_excerpt: excerpt }]); |
There was a problem hiding this comment.
P2: When monitor_changes insertion fails, this handler still returns 200 with changed: true, losing the change record. Check the insert error and fail the run instead of reporting a successful check.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At website-change-monitor/functions/monitor-check.ts, line 73:
<comment>When `monitor_changes` insertion fails, this handler still returns 200 with `changed: true`, losing the change record. Check the insert error and fail the run instead of reporting a successful check.</comment>
<file context>
@@ -0,0 +1,84 @@
+ changeType = String(parsed.changeType || changeType).slice(0, 80);
+ significance = ["low", "medium", "high"].includes(parsed.significance) ? parsed.significance : significance;
+ } catch { /* A deterministic change record is still created. */ }
+ await client.database.from("monitor_changes").insert([{ target_id: target.id, previous_snapshot_id: previous.id, current_snapshot_id: snapshots[0].id, summary, change_type: changeType, significance, before_excerpt: previous.content_excerpt, after_excerpt: excerpt }]);
+ }
+ const now = new Date();
</file context>
| await client.database.from("monitor_changes").insert([{ target_id: target.id, previous_snapshot_id: previous.id, current_snapshot_id: snapshots[0].id, summary, change_type: changeType, significance, before_excerpt: previous.content_excerpt, after_excerpt: excerpt }]); | |
| const { error: changeError } = await client.database.from("monitor_changes").insert([{ target_id: target.id, previous_snapshot_id: previous.id, current_snapshot_id: snapshots[0].id, summary, change_type: changeType, significance, before_excerpt: previous.content_excerpt, after_excerpt: excerpt }]); | |
| if (changeError) throw new Error(changeError.message || "Change was not saved"); |
| @@ -0,0 +1,9 @@ | |||
| import { createClient } from '@insforge/sdk'; | |||
|
|
|||
| const runtime = (window as Window & { __INSFORGE_RUNTIME_CONFIG__?: { apiBaseURL?: string; anonKey?: string } }).__INSFORGE_RUNTIME_CONFIG__; | |||
There was a problem hiding this comment.
P2: Vitest now fails before config-errors.test.ts runs because importing config.ts evaluates this module in Node, where window is undefined. Guard the browser access (or read the value from globalThis) so shared config utilities remain importable in tests and non-browser contexts.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At insight-flow-agent-chat/src/lib/insforge.ts, line 3:
<comment>Vitest now fails before `config-errors.test.ts` runs because importing `config.ts` evaluates this module in Node, where `window` is undefined. Guard the browser access (or read the value from `globalThis`) so shared config utilities remain importable in tests and non-browser contexts.</comment>
<file context>
@@ -0,0 +1,9 @@
+import { createClient } from '@insforge/sdk';
+
+const runtime = (window as Window & { __INSFORGE_RUNTIME_CONFIG__?: { apiBaseURL?: string; anonKey?: string } }).__INSFORGE_RUNTIME_CONFIG__;
+const baseUrl = (runtime?.apiBaseURL ?? import.meta.env.VITE_INSFORGE_URL)?.trim().replace(/\/$/, '');
+const anonKey = (runtime?.anonKey ?? import.meta.env.VITE_INSFORGE_ANON_KEY)?.trim();
</file context>
|
|
||
| let input: ConfigInput; | ||
| try { | ||
| input = (await req.json()) as ConfigInput; |
There was a problem hiding this comment.
P2: Valid JSON with a null or incorrectly typed body causes this handler to throw before returning a 4xx response. Parse into unknown, validate the object and field types in both PUT and POST branches, then return a validation error before accessing them.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At insight-flow-agent-chat/functions/insight-flow-config.ts, line 174:
<comment>Valid JSON with a null or incorrectly typed body causes this handler to throw before returning a 4xx response. Parse into `unknown`, validate the object and field types in both PUT and POST branches, then return a validation error before accessing them.</comment>
<file context>
@@ -0,0 +1,222 @@
+
+ let input: ConfigInput;
+ try {
+ input = (await req.json()) as ConfigInput;
+ } catch {
+ return json(400, { error: 'invalid_json' });
</file context>
| function App() { | ||
| const [user, setUser] = useState<{ id: string; email?: string } | null>(null); | ||
| const [authLoading, setAuthLoading] = useState(connected); | ||
| const [projects, setProjects] = useState<Project[]>(demoProjects); |
There was a problem hiding this comment.
P2: When a connected workspace is loading or its initial query fails, the UI shows the public showcase data as if it were the user's research. Initialize empty workspace state when connected and reserve demo* state for showcase mode.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At web-research-agent/src/main.tsx, line 88:
<comment>When a connected workspace is loading or its initial query fails, the UI shows the public showcase data as if it were the user's research. Initialize empty workspace state when `connected` and reserve `demo*` state for showcase mode.</comment>
<file context>
@@ -0,0 +1,271 @@
+function App() {
+ const [user, setUser] = useState<{ id: string; email?: string } | null>(null);
+ const [authLoading, setAuthLoading] = useState(connected);
+ const [projects, setProjects] = useState<Project[]>(demoProjects);
+ const [sources, setSources] = useState<Source[]>(demoSources);
+ const [claims, setClaims] = useState<Claim[]>(demoClaims);
</file context>
Review (round 1)Reviewed together with the two companion PRs (lexmount/insforge-platform#81 and lexmount/lex-insforge#4). Local checks: Scope: 6 of the 8 commits are not about analytics
The six Contract check against the platform sideVerified against insforge-platform#81: the global ( P2-1 Session replay is configured here but the provisioned PostHog projects have it switched offNew PostHog projects default to P2-2 URL sanitization misses
|
Summary
Validation
git diff --checkCompanion PR
Summary by cubic
Instruments all 14 official templates with PostHog analytics driven by the platform's runtime config, adds the Insight Flow Agent Chat template, and moves the chatbot template onto the Insight Flow runtime.
Analytics
/.well-known/insforge-runtime-config.jsand fall back toNEXT_PUBLIC_*orVITE_*env vars for standalone deployments.Templates
insight-flow-agent-chat, a Vite chat UI that proxies to an Insight Flow Agent through two new Edge Functions with per-user, RLS-protected config storage.Written for commit f6c2a77. Summary will update on new commits.